Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Routes, joins and reach

Part of the decision log: joins and barriers, Hops at load time, scoped routes and what validation checks.

Why fan-in is a join on the receiving node rather than a pipeline definition. Two edges into one agent would otherwise fire it twice, on the first arrival rather than the last — duplicate side effects, silently. A pipeline DSL would fix that by dictating sequence, but it would also turn the route map from a permission graph into an execution graph and take routing decisions away from agents. Declaring the barrier on the receiver keeps senders free and the mesh emergent: nobody is told what to do next, a joined agent simply cannot be woken by one input alone. It also removed the need for blocking request_response, because the Tower parks flights instead of parking processes.

Why a barrier guards its upstreams rather than its agent. A join could plausibly mean "this agent may not run until these inputs arrive". It does not; it means "these inputs reach this agent together". The difference only shows up when a joined agent is also reachable another way, and then it decides whether the design works at all: an entry agent that collects results from the helpers it dispatches would, under the stricter reading, park its own human trigger while waiting for agents that cannot run until it has been triggered. Deadlock on the first flight. Scoping the barrier to its declared upstreams also removes the need for an intermediary gate agent in a review loop — verdicts rendezvous directly on the agent that produced the work, which then decides for itself whether to loop or move on. The cost is that a run may wake holding less than everything in flight for it, which is why sender identity is mandatory.

Why optionality lives in prompts rather than in the route map. join = "all" waits for every declared upstream, so an agent that consults a specialist only when the work calls for one would strand its own rendezvous — the barrier waits for an agent that was never asked, and the itinerary stalls on the happy path. Making the barrier wait only for upstreams actually dispatched is the real fix, and it is deferred rather than dismissed: it requires the Tower to observe dispatch, and it races, because a fast upstream could release the barrier before its sibling is dispatched at all. Until then the rule is to dispatch everyone every time and let an idle specialist answer "nothing to add" — a cheap read-only run in exchange for a barrier that can always be satisfied.

Why Hops is checked at load time but only weakly. An agent further from an entry point than max_hops can carry is configured, appears live in the route map, and never runs — worth catching before the first flight. Plain reachability is not enough on its own, though: a join = "all" target looks close when any upstream is close, while it actually waits for the last, so a second check requires every upstream to be able to afford the flight into the barrier. Both measure shortest paths, and the budget is really consumed by loops: a two-agent review cycle costs two hops per turn, so a route map three flights deep can need twenty to be useful. Nothing static can know how many times a loop will turn, so the checks deliberately prove only the negative. The reference scenario carries the arithmetic instead, and a regression test pins it, because the default max_hops = 8 permits that factory's happy path and not one round of rework.

Why routes can be scoped to pipelines, and why that keeps the mesh a mesh. One mesh for the whole factory meant every chain could use every route, and a real factory's workflows share agents. A production factory's review sweep spawns a reviewer per pull request; the build workflow lets the same reviewer hand work to the only agent that pushes code, for its self-review. The sweep's reviewer reads untrusted pull request text, and the route map permitted it that edge — only a prompt said not to. A second cost was quieter: the per-workflow map drew agents a workflow never uses, and a pipeline had to declare every flag of every prompt the shared mesh let it reach. A scope says who may reach whom within a workflow, so every question the mesh answered is still answered by an edge, and nothing about order is introduced — agents still decide where work goes. The scope sits on the route, not on the pipeline, so that [[routes]] stays the one place that says who may reach whom and pipelines stay thin: an entry agent, a trigger, some booleans.

Why an unscoped route stays global. Every route written before scopes existed has to mean what it meant, and the common case — a factory with one workflow, or routes every workflow shares — should not have to name anything. Narrowing is opt-in and additive: a route gains a scope, never loses one. pipelines = [] is refused rather than read either way, because "no pipelines" is a route nothing may use and "global" is the widest permission there is; a typo in either direction would be silent.

What was rejected for scoping. A route table per pipeline would have been a second route map to keep agreeing with the first, and a copy of every shared route per workflow. A deny-list (except = [...]) fails open: a pipeline added later would silently gain every route nobody thought to exclude it from. Scoping by agent groups or tags would have added a fourth concept to configure for what pipelines already name. And sequencing — "in this workflow, a then b" — was never on the table: it is the pipeline engine the route map deliberately is not.

Why validation takes the best case across a pipeline's ways in. The reach, hop and join checks have always proved only the negative: an agent no way in can wake, a depth no way in can afford. Scoped, each way in is walked over its own graph and the findings merged by that same best case, so a factory that scopes nothing gets exactly the findings it always had. The one new check that looks for something dead — a scoped route no chain of its pipelines can wake — treats every agent any chain can wake as a possible start for a resuming pipeline, because resumed work goes back to whoever booked it and any agent may book; a warning that is not true is one people stop reading.

Why workflow membership crosses spawn edges when reachability does not. The two ask different questions. reachable_from asks what could still deliver into this itinerary, and a spawned chain never can — that is what stops a dead barrier being kept alive by an agent that could not possibly satisfy it. Workflow membership asks what a way in sets in motion, and a reviewer spawned by a sweep is unarguably part of the sweep. An agent belonging to two workflows appears in both, which is the honest answer rather than the tidy one.