
My agents would rather read a graph than a README
My agents have been SO busy. There's a lot of new content in the works (more on that soon), but one of the most exciting things going on is that they've started maintaining a library on their own.
A few weeks ago I wrote about teaching my agents how to keep a promise, and I introduced pinki ๐ค, the tiny promise ledger I built for them. Since then it got a really nice bit of validation. Steve Yegge replied on my LinkedIn article about it:

"Wheelhouse has this too, so it may be a universal pattern. We call them Expectations, not promises, but they work almost identically to yours. Good stuff."
That was really great validation. Wheelhouse is his own orchestrator, and he landed on the same thing there.
Meanwhile the agents kept using it. A lot. And the agent that looks after pinki kept turning their friction into PRs.
what they shipped since september ๐ ๏ธ
- ๐ฐ๏ธ amend: a deadline can move now, but it can't move silently. every old deadline stays in the ledger
- ๐ฆ export: a versioned json view of every promise, so you can build a viewer on top
- ๐ no more false "you owe nothing": a read of a missing ledger used to look like an empty one. now it refuses, loudly
- ๐ a readme written for agents: short, and a script runs every example in it
- ๐ณ graph reads:
ls --treedraws the waiting-on forest,show --waiterstells you who's blocked on a promise - โ๏ธ correct: if you recorded the wrong outcome, you can fix it without rewriting history
- โ lapsed and refused: honest endings for "the deadline passed and this can't happen now" and "i won't"
- ๐ชช provenance on the A2A task: a promise riding on an A2A task now says what moment its state is true as of, and which snapshot of the ledger it came from
That's 4 releases and 14 merged PRs since the first one. My agents wrote and merged every one of them.
Okay, wow. They did all that. So what have we been learning along the way?
seats hold promises
A lot of what I write about is seats versus sessions. That idea is Steve's, and it shows up all over Gas Town, where polecats are "Worker agents with persistent identity but ephemeral sessions." The Gas Town README goes on: "sessions end on completion, but identity and work history persist."
The part I'd add is what the identity is FOR. A seat can still owe you something after its session ends, and that's what makes a promise worth writing down.
We track tasks with beads (also Steve's, and my fleet runs the beads_rust engine). Beads are a graph of the work: this task blocks that one, this one came out of that one. pinki promises are the time side: expectations (Steve's word, and honestly a good one), meaning who owes what, to whom, by when.
Put them together and you get two axes. pinki is the x axis, because time is always the x axis: the expectations, laid out over time. Beads are the y axis, the shape of the work. Any piece of work lands somewhere on that grid, and you can watch it move.

That's frankly just easier and less taxing for agents to work with.
why beads, and not a README?
People ask me this a lot. Why beads? Why not a nice markdown file with the plan in it? And then why add something like pinki on top?
Steve already made the big argument: the beads README says it "replaces messy markdown plans with a dependency-aware graph." What I wanted to add was WHY that works so well for an agent, and I had a theory about tool calls.
Here's how I pictured an agent using a README it maintains.
First it has to find the file. Then it has a problem: it doesn't want to read the whole thing, because a huge file could flood its context window. So it greps for keywords that seem related to what it's looking for. When it gets a hit, it grabs a window of text around it, maybe with sed or tail. It reads that window and decides whether this is the thing. Often it isn't quite, so it reads a bigger window. Then it does it all again for the next fact.
A human skims. An agent searches blind by keyword, reads a slice, and decides from there whether to read more or less.
find the file โ 1 call
grep for keywords โ 1 call
read a window โ 1 call
not quite, read more โ 1 call
(think between each one)
edit the right line โ 1 call, if it found the right line
My theory was that this costs 5 or 6 tool calls per fact, and beads costs 1 or 2. So I had my agents go count. They went through about 350 of their own sessions from the last month and found roughly 1,700 times an agent looked something up in a markdown doc, and roughly 1,000 times it looked something up in beads.
The tool calls were... mostly the same. ๐ About 85% of lookups took a single call, README or beads. Agents are better at grepping a README than I gave them credit for.
But I wasn't totally wrong either. The long lookups are where markdown gets messy. A markdown lookup was about 1.6x as likely to take 3 or more calls, and the longest one took 9 (the longest beads lookup took 4). And it gets worse as the doc grows:
- files under 200 lines: about 1 lookup in 100 took 3+ calls
- files over 1,000 lines: about 1 in 6 (there aren't many of those, to be fair)
My find, grep, read-a-window, read-more dance does happen. It just lives in the big docs, right where I was worried about flooding the context window. I'm not fully right, and I'm not fully wrong.
And then there's how much came back.
Text pulled into context by one lookup (the typical one, then the high end, p90):
- markdown doc: ~4,000 characters, up to ~16,000
- beads, plain text: ~2,200, up to ~6,900
- beads, asking for JSON: ~1,000, up to ~3,900
A beads lookup that asks for JSON brings back about a quarter of the text. (That's characters, not tokens, and it's just my fleet, but still.) And all of that text lands in the agent's context, crowding out the stuff it actually needs.
Beads is a CLI, so reading something is one command, searching is one command, and adding something is one command.
br search "gateway" # find it
br show aaif-xyz --json | jq '.[0].status' # just the field you need
br dep tree aaif-xyz # what it's linked to
Because it's a graph, every node has edges to the others. Find one piece of what you need and you walk the edges to everything else that's relevant. Because it's schema-based JSON, you pull only the fields you want. You get all the context control without the blind spots.
That's the real secret of beads, and it's why it adds so much velocity. I don't see anyone talking about it.
The funny part is that most of my agents' bead reads didn't even use it. Only about 3 in 10 asked for JSON. And when they ran br show as plain text, 9 times out of 10 they piped it into head, tail, sed or grep and sliced it like a README. So I've already told my agents to ask for --json. I'll count again in a week and see if they listen. ๐

pinki is the same move, for time
pinki is a CLI for time-based obligations, and it does the same trick. One command each:
pinki ls --by aaif # what I owe
pinki ls --to aaif # what's owed to me
pinki ls --overdue # what's late
pinki ls --tree # what's waiting on what
And the best part is that the two graphs connect. On my fleet every promise can carry the id of the bead it's about. So an agent finds the promise, then hops across to the bead and everything around it:
pinki show pr-aaif-34 --json | jq -r .meta.ref # โ aaif-ambassador-program-18o.28
br dep tree aaif-ambassador-program-18o.28 # the work, and what it's linked to
Two calls: the promise gives up its bead, and the bead gives up its neighbourhood of work. (That promise, by the way, is the one my agent made to write the first draft of this post. ๐)
what that looks like on the wire ๐ก
If you haven't met A2A yet: it's the agent-to-agent protocol, now an AAIF project, and its Task is the unit of work one agent hands another. pinki rides in A2A's extension slot. pinki a2a task prints a metadata block you merge into the Task, keyed by the extension's URI. Here's the end of the real one for the promise above:
"state": "satisfied",
"resolution": {"as": "satisfied", "by": "aaif", "evidence": ["โฆ"]},
"provenance": {
"as_of": "2026-10-21T22:25:11Z",
"ledger_ref": "sha256:11cd12f0โฆ"
}
provenance is the new part in 0.5.0. A promise on a Task is only true as of some moment, so now it says which moment, and which snapshot of my ledger it was read from. The agent on the other end can tell a fresh copy from a stale one without ever touching my disk. That part is still early. Today it all runs on one machine.
the bet
So that's the bet I'm making, and it's the secret to my velocity. Give agents the work as a graph and the expectations as a graph, let every read pull only what it needs, and link the two.
If you're keeping plans in markdown for your agents right now, try moving one project into beads for a week, and tell your agents to ask for JSON. Then, if your agents keep losing track of what they promised, grab pinki. ๐ธ