# Pi Is Bad; Or, The Positive Effects of Friction in Automated Development

> Source: <https://blog.djhaskin.com/blog/the-positive-effects-of-friction-in-automated-development-or-pi-is-bad/>
> Published: 2026-08-04 21:55:20+00:00

# Introduction

I have recently tried the AI harness [Pi](https://pi.dev/) . I'm coming from it
from a harness that I first picked up when all this stuff was sort of newer
called [Goose](https://Goose-docs.ai/). I can see what all the hype is about,
but I actually had a bit of a ["better is
worse"](https://dreamsongs.com/WorseIsBetter.html) experience with it. I think
I'll be sticking with Goose for now. I have come to the conclusion that it is
because of the friction Goose introduces into the system makes it better.

In the rest of this blog post, I describe the differences in my experience
running Pi versus Goose using Open Router's
[deepseek-v4-flash](https://openrouter.ai/deepseek/deepseek-v4-flash) model. Goose configuration that I used may be found [here](https://github.com/djha-skin/dotfiles/blob/main/dot-config/goose/config.yaml). Pi global configuration may be found [here](https://github.com/djha-skin/dotfiles/tree/main/dot-pi/agent). Goose project-level wrapper may be found [here](https://github.com/djha-skin/dsolv/blob/main/scripts/goosew). Pi project-level configuration may be found [here](https://github.com/djha-skin/dsolv/tree/main/.pi).

# Experiment Design

I type a short three-to-four sentence prompt into Goose and it just works. Sometimes I would need to provide guidance, but once I get it going, I can often step away from my desk and come back. I often heard anecdotes about how AI could "slip the leash," like cheat on unit tests, implement the wrong thing, do exactly what they were told not to do, that sort of thing. I really didn't understand these complaints. I would just write stuff, give the AI the occasional dont-do-that-do-this, and it would do okay. However, I tried the same short-style prompting on Pi, and it immediately started slipping the leash.

I tested using Pi on [a project I'm porting from Clojure to Common
Lisp](https://github.com/djha-skin/dsolv/). [I wrote a skill that told it how I
like Common Lisp to be
written](https://github.com/djha-skin/dsolv/tree/main/.agents/skills/djha-skin-common-Lisp).
In there I said to write a bunch of
[beads](https://github.com/gastownhall/beads). I initially set up Pi to write
beads via the [bead MCP
server](https://github.com/gastownhall/beads/blob/main/integrations/beads-mcp/README.md).
Then I told the AI to use the beads MCP in the skill I wrote and later it was
also in the AGENTS.md file. I started Pi up, and told it to read AGENTS.md and I
also gave it an initial prompt to use the skill and start porting code.

# Problem 1: Pi Ignores MCP Servers

When I started Pi up, it also immediately ignored what I told it to do in the skill. It didn't write any beads and just started writing code almost immediately. I had to yell at it two or three times before it figured out "oh yeah, I should stop and write beads." I stopped Pi and did some research to determine the underlying cause of this flagrant ignorance on the part of the AI.

The [Pi MCP plugin](https://Pi/packages/pi-mcp-adapter) lazily loads the MCP
servers into the context by default. Mario Zechner [wrote an interesting blog
post](https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/) about
how MCP might not be great because it introduces tokens into the context window
about the MCP tools whether you use them or not. The MCP adapter in Pi follows
this philosophy. It only loads the definitions of the MCP tools into the
context. Because of this, my mention of beads was probably the first occurrence
of beads that the AI saw. Not knowing what to do about it, it probably just blew
past it.

I started it up again with a bead plugin instead of using the bead MCP server.
However, I couldn't take this approach with the Common Lisp
[cl-mcp](https://github.com/cl-ai-project/cl-mcp) MCP server. No one had written
an equivalent plugin. Pi kept deviating from my telling it to use the MCP
server, and this was a big problem. Running Common Lisp code from the CLI often
drops into the Lisp interactive debugger. [Common Lisp doesn't like being vibe
coded](https://blog.djhaskin.com/blog/writing-lisp-is-ai-resistant-and-im-sad/),
and the interactive debugger is one of those reasons, but there are many.
Long-running REPL development, REPL reloading, one-off Lisp evaluations within
the REPL environment, Lisp-form-level editing (rather than line editing), and
parenthesis balancing are all problems with which AIs often struggle. The cl-mcp
server fixes several of these problems, and more besides.

Goose handles the Lisp debugger really poorly when it runs Lisp code through the
CLI. In Goose, the debugger totally hangs the Goose session and Goose must be
restarted. When I restart it, I tell Goose "You used Lisp CLI tools again, don't
do that, use the MCP server." I use Goose's [Top Of Mind
feature](https://goose-docs.ai/docs/mcp/tom-mcp/) to remind Goose to use the MCP
server instead. When Top Of Mind enabled, I have more or less stopped having
this problem with Goose.

Pi handles the debugger and even parenthesis balancing
more gracefully than Goose, but this actually caused problems within the
session. Pi seems to put the Lisp process in its own shell and handles the
interactive prompt gracefully by killing it when it gets to the debugger prompt.
Like with Goose, though, I still wanted to enforce the use of cl-mcp. Pi didn't
have a top-of-mind-style context injection system available in its package
system, when I was running this test, so I made a [top of mind plugin for
Pi](https://www.npmjs.com/package/@djhaskin987/pi-top-of-mind) and used that to
remind Pi of the MCP server. Even with my putting it into the context every 10
tools calls or so though, Pi kept messing up and stopped using the MCP server.
It kept getting big enough rewards from the ad-hoc interactions it had with
Common Lisp through the CLI that it stopped using the proper tool for the job,
specifically designed to help it get better results.

# Problem 2: Pi Is Too Fast

With the same model and API backend, Pi generates text much, much faster than
Goose. It also doesn't allow the user to stop the generation by default. Pi
didn't stop when I wanted to talk to it, just kept barrelling toward ruin. The
speed would make me feel white-knuckled, like "what is it going to screw up this
time." I ended up installing the [pi-stop](https://Pi/packages/pi-stop?page=41) plugin. This
helped immensely because I could now stop Pi. However, it still generated text
so fast that I couldn't process what it was generating. Often, by the time I had
figured out it needed steering, it generated a lot of garbage that sat there
poisoning its context, not to mention the bad code or incorrect beads that it
wrote. Eventually, after any given Pi session, I would have to
switch back to Goose because the AI got confused about what to do next. This
speed was not only a problem for context poisoning, but since it generated a lot
of churn that then needed cleaned up in a very short amount of time, it costed a
lot more money on OpenRouter while yielding poorer results. It simply goes too
fast and too easily makes mistakes without providing enough time for steering to
take place.

# Problem 3: Pi Package Whack-a-Mole

Goose comes with top-of-mind, full-stop steering, maximum turns before stop, and
(though I don't use it) fine-grained permissions system allowing you to fine
tune what tools are exposed to the AI and which are not. Finally, it has a
maximum context token setting (I had to install yet-another-plugin in Pi for
this, [pi-context-cap](https://pi.dev/packages/pi-context-cap?page=46). All of
this comes out of the box with Goose. I had to hunt around for these features
with Pi, and in one case, I had to write my own extension. Worse, the plugins
often had only hundreds of downloads, making me think they were one-off garage
projects (like the top-of-mind plugin I just wrote for Pi was!) that weren't
really maintained by their creators. It made me feel like I was playing
whack-a-mole. "Oops! Pi doesn't have x, guess I have to go shopping." I don't go
shopping around for different seat belts for my car. I just hope the
manufacturer had them built-in. Plug-ins are awesome, and a vibrant package
ecosystem is awesome, but my time in the trenches with Jenkins has taught me
that plugins shouldn't be used *everywhere*.

# The Grass Is Not Always Greener

Goose's heavy use of MCP makes it work better. Goose has
[hooks](https://Goose-docs.ai/docs/guides/context-engineering/hooks/) and
[plugins](https://Goose-docs.ai/docs/guides/context-engineering/plugins/), but
most extensions are simply implemented via MCP. It loads the definitions into
the context window up front, as far as I can tell. It uses the sequential
thinking MCP. It uses the to-do MCP. It uses the beads MCP. It often does this
without me explicitly prompting it to do so because I already sort of did by
loading those things. Because all of
its MCP tool definitions are loaded into it all at once, the AI thinks it needs
to use them. That load was an implied suggestion that these MCP tools
are to be used. In Pi, there are a bunch of plugins I have like [the task
plugin](https://github.com/tintinweb/pi-tasks), but it doesn't use it unless I
tell it do so so. I figured out eventually how to eagerly load the MCP servers
in Pi eagerly into the context, but at first this was a problem. [Goose's
philosophy](https://goose-docs.ai/blog/2025/12/21/code-mode-doesnt-replace-mcp)
is to limit the MCP servers to just the ones you use. Having had this problem of
please-use-my-mcp-no-seriously with cl-mcp, I agree with the sentiment. If you
*do* want the AI to use the tool, you SHOULD have MCP definitions, skills, and
other sundry tools you *want* the thing to use loaded up front.

Code is generated at a much more manageable pace in Goose. Goose is written in Go. Go has excellent asynchronous tools, but the author doesn't seem to have used them. Everything feels more or less single threaded. This is objectively worse, but it's also objectively better. It's easy to stop the AI from doing something wrong. It slows the AI down a little bit, but speed is not the AI's problem. My cognitive load trying to keep up with the AI is the problem. Just like with cars and roads, my AI tools need speed limits to allow for the reaction time of the driver.

With Goose, I set the session up, then spend about 20 minutes watching it. I will often have to give it one-sentence steering messages, but this drops in frequency as the session goes on. It is very often at first, but after a while, it just gets "into a groove" and starts cranking out good, high quality code. After that, I check on it every so often, sometimes stopping it and writing one-sentence steering messages, and then go back to what I was doing. If I forget about the session and come back, it will often have hit its configured maximum token limit or configured maximum number of turns, but it will still have written good stuff.

It all just makes me feel like Goose was written by people who really cared about bringing the AI under control without fuss. Goose just works. It feels boring. Boring is good.

# Conclusion

Now we can see the worse-is-better effect in my experiences. More wasted context is better, since it tells the AI how to think. Slower is better, since it allows me time to think about what the AI is generating. The Lisp debugger breaking Goose but not Pi is better, since it forced me to force the AI to use the MCP server.

It makes me think about Golang versus JavaScript since those are the two languages in which Goose and Pi were written. Typed languages famously made the trade-off of slower development, but the friction they introduced help write more precise code. This is awful if you're writing a front end, because the front end needs to change faster than that, but lower down, things need to change less and be more correct since it's hard to inspect the effects of your code when you're outside the browser. I feel these positive effects of a slower, heavier AI harness when I use Goose.

I use NeoVim as an editor, and it felt like I was switching from Vim to VS Code
when I went from Goose to Pi. I found what I thought I would find: a better
plug-in system, a rich extension ecosystem, a more polished UX, just like VS
Code. However, I also had worse results in coding. The one thing that Goose
needed to be better at, it *was* better at, just like with Vim. There are still
some really awesome things that I like about Pi, just like there are some really
awesome things about VS Code. However, Vim has always been a better fit for my
brain.

I think I'm going to stick with Goose for a little while longer. Form is nice, but in this business, function is non-negotiable.
