Skip to content

Course · Pi · Teams

Peer-to-peer coms - and the honest 'is this worth it?' test

Every fix so far in this module has traded one coordination cost for another - leads traded seven messages for three plus their own overhead, expertise traded a rediscovery cost for a small file. This chapter’s fix is the last trade, and it’s the one where the spoiler is the whole point: the lang-field handshake below will pass every count of the honest test that closes this chapter. Watch how it passes - that’s what tells you what a peer channel that shouldn’t get built looks like.

Here’s the fact from the last lesson’s expertise file, working itself out in real time: the new extractor starts returning a lang field on every row - something the old one never produced. The extraction lead learns this the moment its first worker comes back. The index lead needs to know it before it reindexes a single row, or it reindexes against a shape that’s about to change again underneath it.

Guess first: route that fact through the hierarchy you already built, and count the hops between the extraction lead learning it and the index lead acting on it. Extraction lead sends its finding up to you - hop one: send, then you await the message and read it. You relay it down to the index lead - hop two: another send, another await on the index lead’s end. Two hops, two sends, two reads, and you personally did the relaying at both ends even though you made no decision at either one. You’re not coordinating. You’re a wire.

Now the peer path. Extraction lead calls list, sees the index lead is live, and sends the fact directly. Index lead awaits, gets it, sends back an ack. One hop. You read neither message unless you go looking for them.

Same as the jump from spawn to teams, this isn’t a new capability bolted onto Pi’s core; it’s the same idiom, applied bidirectionally. Coms exposes the same four verbs you just traced with:

  • list - discover which peers are live right now, and their rough context usage.
  • send - fire a prompt at a named peer, get an acknowledgment back once it’s queued.
  • get - check for a reply without blocking.
  • await - block until a reply arrives or a timeout passes.

The custom coms extension this lesson sketches would use a local transport: a small socket per agent (a Unix socket, or a named pipe on Windows), discovered through a per-project registry of small files the extension itself writes and prunes. Pi core does not ship these four verbs, and this course does not provide the complete extension; treat the transport description as an implementation plan. Leads on separate machines or in separate sandboxes are a further extension of the same idiom, with a dedicated hub process to run and secure and distinct tool names. That’s real infrastructure, not a free upgrade. Don’t reach for it until you actually have leads that can’t share a filesystem.

The delivery idiom underneath is one you’ve already used: an inbound prompt arrives as a follow-up message that forces a new turn - the same pi.sendMessage(..., {deliverAs: "followUp", triggerTurn: true}) call that delivered a background subagent’s result back in the last chapter. And there’s no separate “reply” tool to remember to call - when the receiving agent’s turn ends, the extension reads its own last assistant message straight off its session history and ships that back as the reply automatically, so “the agent forgot to answer” isn’t a failure mode you have to guard against.

[extraction-lead → index-lead]
row shape changed: every row now carries `lang`. reindex against the
new shape, not the old one - don't start until you've confirmed this.
[index-lead → extraction-lead]
ack. holding the reindex until the extractor migration finishes on the
last three sites, then running against `lang`-inclusive rows only.

One send, one ack, done. Notice what’s missing compared to the hierarchical path: nobody above these two leads made a decision, because nobody above them needed to.

The one thing you must build in on purpose: an end condition

Section titled “The one thing you must build in on purpose: an end condition”

A hierarchy has a natural end-state built in - a worker returns, the call is over. A flat peer channel doesn’t; two agents messaging each other have no structural reason to ever stop, and a sloppy prompt on either end can turn one useful handshake into an endless back-and-forth. The concrete guard is a hop counter on every message - each envelope carries a count, and the transport refuses to forward a message past a fixed limit. It’s a blunt instrument, but it’s the difference between “one lead tells another a fact” and a runaway loop burning tokens on both sides until someone notices.

What a hop counter doesn’t fix: leads that disagree

Section titled “What a hop counter doesn’t fix: leads that disagree”

Everything traced above assumes the clean case - one fact, one direction, one ack. Real domains interact messier than that. Say the index lead had already started planning its reindex around the old, lang-less shape by the time the extraction lead’s message arrives - now the two leads don’t just need to exchange a fact, they need to reconcile two decisions that were made independently, on a shared boundary neither one owns alone. The four verbs above move a message; they don’t arbitrate a disagreement. Nothing in list/send/get/await decides whose plan wins when two leads’ domains touch the same row shape and land on different conclusions about it. That’s still your job, routed back up the hierarchy you supposedly bypassed - which means peer coms removes hops for facts that only inform, and quietly hands you back the ones that need a decision. Know which kind you’re building for before you wire the channel.

Is this actually worth it? The honest test

Section titled “Is this actually worth it? The honest test”

Everything in this lesson is easy to over-apply - a peer channel is a fun thing to build, and “my agents talk to each other” sounds like progress whether or not it is. Before you wire one up for real, ask honestly:

  1. Does the fact actually change what the other side does? The lang field genuinely changes the index lead’s plan - it passes. If a lead just wants to share something interesting with no bearing on the other’s next move, that’s a status update, not a reason for a peer channel.
  2. Could the orchestrator relay it just as well? For one message, once, routing through you costs you two hops of attention and nothing else. That’s not expensive enough to justify standing up and securing a whole comms layer over. The channel earns its keep when the relay would happen often enough, or urgently enough, that your attention is the actual bottleneck.
  3. Can you name the end condition before you send the first message? “Index lead acks once, done” is a real end condition. If you can’t state one in a sentence, you don’t have a peer exchange - you have the start of a loop.
  4. Does the cost scale with you in mind? Cost here runs roughly with agent count times how many times a message bounces back and forth - cheap for two leads trading one fact, expensive fast for a five-agent debate that keeps going. Know which one you’re building before you commit to it.
  5. Is the infrastructure proportionate to how often this actually happens? A running hub process, tokens to secure it, peers to register - that’s real ongoing cost. For a single once-per-migration handshake between two leads on one machine, the lightweight local version is proportionate. A standing, always-on hub is worth it only if you’re actually running a persistent multi-agent setup often enough to amortize it.

Run stash’s lang-field handshake through that list honestly and it passes on every count: it changes a real plan, it’s a single exchange with a clean end, and a local socket-based call is cheap enough that building it costs less than the two hops of your attention it would otherwise take. That’s the bar. A three-agent standing debate channel you build because the pattern exists and it looked good in a demo does not automatically clear it - and most of the time, it doesn’t need to.

The verdict on the whole team, not just the channel

Section titled “The verdict on the whole team, not just the channel”

This chapter’s test was written for one peer handshake. Point it at everything this module built, because that’s the verdict this module actually owes you: the orchestrator, the three leads, the seven workers, the expertise files, and the peer channel, together, on a migration whose total reading material would have fit in one context window with room to spare.

The trace says the answer isn’t a flat yes or a flat no. The lead layer earns its keep for this migration specifically because the domains are both multiple and individually wide enough to need more than one worker each - drop to one or two workers per domain and the honest answer in the last chapter was that a flatter, two-tier version would have done the same job for less ceremony. The expertise files earn their keep cleanly and don’t scale down well either way: fourteen wasted worker-spawns against one written line is not a close call, for a lead that will see its domain again. The peer channel earns its keep in exactly the form the test above passed - one handshake, one local socket, one hop counter - and would flip to a net cost the moment it grew into a standing hub nobody’s using often enough to amortize. Here’s that last part as its own ledger row, on purpose, because this module is the one that’s allowed to show a line where the cost outruns the buy:

LayerWhat it costsWhat it buysWho decided
Lead (tool-surface locked to dispatch_agent only)one more agent’s own overhead, per domain, on top of every worker beneath ita report count that stays flat as workers scale - 3 reports whether each lead runs 2 workers or 8you
Expertise file, one per leada small markdown file the lead re-reads every spawn; nothing if nobody curates itzero repeated worker-spawns rediscovering a fact the lead already paid to learn oncethe lead, curated by hand - never auto-logged
Peer channel (coms), light local form, one handshakea hop counter, a small local transport, one exchangetwo relay hops removed for a fact that actually changes the other lead’s planyou, per exchange - gated by the honest test above
Peer channel, standing hub, always-ona running process, credentials to secure it, peers to register and keep currentreadiness for exchanges that, on a migration this size, mostly never comenobody - this is the row where the cost outruns the buy

Same roster, same close look:

LeadDomainExpertise fileSkills
Extraction leadsrc/stash/extract/, app/worker.py.pi/agents/expertise/extraction-lead.mdonboard-content-source
Index leadfull-text search rebuild.pi/agents/expertise/index-lead.md-
Interface leadweb UI + CLI rendering.pi/agents/expertise/interface-lead.md-

The right amount of team, for most of what you’ll actually do to stash, is smaller than it’s tempting to build - but for this specific migration, the trace says the team you built earns almost all of what it cost. Build the fourth row above and it wouldn’t have.

Rules, config, extensions, gates, skills, subagents, and now a team - orchestrator, leads that never touch a file, workers underneath them, expertise files that make the leads better over time, and a peer channel you only reach for when the test above actually passes. You have every layer this course has handed you. Nobody has added up the bill. Next: pull all of it into one coherent harness, and judge honestly where Pi belongs in your day.