Understand one turn of the Pi loop
You’re authenticated, sitting at a live prompt inside stash, and the feed-date bug is right there. Hold off for two minutes anyway. If you’ve spent any time with Claude Code or Codex, you already have a mental model of “the agent loop” - read, propose a plan, wait for you to say yes, then apply. Pi’s loop looks almost the same from a distance, and is meaningfully different up close, in a way you want to see once before it matters.
Make your first turn a question
Section titled “Make your first turn a question”stash is new to Pi too - it knows nothing about the codebase it hasn’t read yet. A read-only question costs you nothing and shows you exactly how the loop behaves before you ever ask it to change something:
> what does this service do, and where do saved articles get their dates from?
bash: rg -l "date" app/ | head -20 → app/models.py app/worker/fetch.py app/worker/extract.py app/feeds/parse.py
read: app/worker/extract.py
stash is a self-hosted read-it-later service - you save a URL, a background worker fetches it, strips the page down to readable article text, and indexes it for full-text search. Saved-at and published dates both get set in the worker: extract.py parses whatever date string it finds in the page metadata, with no fallback if the format doesn't match what it expects. That's almost certainly where your feed-date bug lives.Watch what one turn actually bought you. Pi didn’t guess from the prompt - it ran a real shell command to find files that mention dates, read the one that looked most relevant, and came back with an answer and a lead on your bug. That’s the whole loop, once: gather context, then answer. Nothing was written, nothing ran that could touch your data - you just watched the model think out loud using the same two tools it’ll use for the real fix.
The loop, stripped to its mechanism
Section titled “The loop, stripped to its mechanism”That’s genuinely the entire loop, described in the plainest terms its own author uses: the model responds, any tool calls it made get executed, the results go back into the conversation, and the model responds again - repeating for as long as it keeps calling tools, and stopping the moment a response comes back with none. There’s no step counter, no built-in ceiling on how many times that can repeat. A five-line fix might close in one round trip; a gnarlier bug might take a dozen tool calls before the model has enough to work with.
Compare that to the four-beat shape you may already know - read, propose, approve, apply. Pi’s loop has three of those beats, not four:
- Gather - the model calls a tool (
bash,read) to see what it needs to see. - Act - it calls a tool that changes something (
write,edit, or abashcommand that mutates state). - Verify - often another tool call, checking the result of the last act - and if verification fails, straight back to gather, not a dead end.
The missing beat is approve. Nothing in that list stops and waits for your yes. By default, Pi has no permission gate at all - every tool call it decides to make, including ones that write files or run arbitrary shell commands, executes immediately, without asking. Here’s that same shape traced on a different small fix, one step at a time, so you can see gather → act → verify → (loop back on a failed verify) → done, without spending it on stash yet:
You’ve now watched the loop close once, cleanly, with nothing at stake. You know its shape - gather, act, verify, repeat until it’s done - and you know the one beat it doesn’t have. That’s exactly the two things you need before you hand it something real. Next: ship your first fix.