Choreographic Prompting
Narrative cohesion as a projection problem, a retrospective on a five-lens agent fan-out
Table of Contents
This note is a retrospective. It preserves, as written on 2026-06-12, a speculative fan-out across four lenses on choreography for multi-agent LLM systems, plus the same-day L7 review that rejected its flagship proposal. Where the review asked for a concrete change, the text now records what has since shipped. The speculation itself is kept under its honest label.
The fan-out grounds Jason Walsh's unpublished choreographic prompting in choreographic programming (2024 event note) (klor / endpoint projection), the Loopcraft "stack of loops" idea, and the wal.sh agent-team tooling.
Sources the agents consulted: choreographic programming (Wikipedia; Montesi), klor (lovrosdu/klor, arXiv:2505.20845), higher-order choreographies (arXiv:2111.03701), Loopcraft (latent.space, "the art of stacking loops"), and the wal.sh corpus (reversible transforms, the Workflow grind, the verification ledger).
1. The unifying idea
A multi-agent LLM system is a distributed protocol, and its dominant failure mode is not hallucination but silent desync – sender expectations and receiver contracts drift because every agent's prompt is hand-authored separately (the "Alice and Bob" anti-pattern: you write Alice's "ask Bob for X" in one file and hope Bob's prompt, in another file, says "you will receive X"). Mismatches do not crash; they silently desync (Bob hallucinates the missing field; both agents wait for the other to go first).
Choreographic programming already solved this for processes: write one global program of who-says-what-to-whom, then project it to per-endpoint code, deadlock-free by construction. Lift the projection target from a process to a prompt and every lens collapses to the same move:
- Choreographic prompting projects a global choreography to each agent's system prompt + I/O schema.
- Layered loops (Loopcraft) are choreographies with back-edges – a loop boundary is a role boundary.
- The wal.sh fan-out (the validate-annotations run) is a choreography reconstructed after the fact by the verification ledger.
One claim, restated four ways: the ledger is an unwritten choreography's execution trace, and the prompts are unprojected endpoint code.
1.1. The corrected failure mode: narrative cohesion, not deadlock
The L7 review (below) is right that this codebase exhibits no mechanical desync: the validate-annotations run is an advisory map-reduce (a fan-out to independent validators, a fan-in to the ledger) with no inter-agent edges, so nothing waits, nothing hangs, and integrity is reconciled post-hoc on purpose. The borrowed word "deadlock" does not apply.
But the hypothesis survives once the failure mode is named correctly. The synchronization a multi-agent system actually lacks is not control-flow rendezvous – it is narrative cohesion. When N agents each hold a private slice of context and no global protocol says what the shared story is, their outputs drift apart at the seams: one validator's "corrected" silently contradicts another's framing, a downstream summary inherits an assumption no upstream agent licensed, the composite reads like six people describing different elephants. That drift is real, it is common, and it is invisible exactly the way the draft's "silent desync" claimed – it just shows up as an incoherent document, not a hung process. The value of a choreography is then not deadlock-freedom; it is cohesion by construction: a single global account of the narrative that every local prompt is projected from, so the seams cannot drift because there is one source for what the story is.
This reframe is what makes the idea worth keeping – and what chor-lint
(below), not chor-validate, was the honest first step toward.
2. The four lenses
| Lens | Thesis (compressed) | Boldest claim |
|---|---|---|
| Endpoint projection for prompts | Write the multi-agent system ONCE as a global choreography; project each agent's prompt + I/O contract from it. Sender/receiver mismatch becomes a projection-time type error, not a runtime hang. | The dominant failure mode of multi-agent LLM systems is unprojected choreography. In two years, shipping one will mean writing one choreography and projecting the prompts – as nobody hand-writes per-CPU message passing anymore. |
| Higher-order skeletons + prompt optimization | A choreographic prompt skeleton is a higher-order, role-parameterized program (a klor defchor whose roles are agents). Prompt optimization then optimizes the PROTOCOL, not the string. |
Multi-agent prompt engineering is a compiler problem, not a prompting problem; the deadlock-freedom theorem transfers to give a static no-desync guarantee on the optimized workflow. |
| Choreographic templating as a loop DSL | Loopcraft's "stack of loops" is the choreography problem one level up: each loop (refine/review/plan) is written from its own local endpoint and silently desyncs. Choreography is the composition DSL. | The "design loops, don't prompt agents" era will produce its own concurrency-bug epidemic – layered loops desyncing and starving – and the cure is already written. |
| Grounding in wal.sh | The multi-agent layer is orchestration pretending to be coordination: a central script fans out to N agents who don't know each other exists; integrity is reconstructed after the fact by the ledger. | The verification ledger is not an audit log – it is an under-specified choreography's execution trace, recorded because the choreography was never written down. |
A fifth lens (adversarial choreography / temporal re-entry, on the agent-worm
paper arXiv:2605.02812) dropped on a transient StructuredOutput error and was
never re-run; nothing below depends on it, because the L7 review's reframe and
the chor-lint outcome rest entirely on the wal.sh grounding lens and the
ledger evidence, not on the adversarial angle.
3. The buildable direction: chor-validate
The single most promising experiment (grounded entirely in code already in the tree): a klor-projected replacement for the validate-annotations fan-out.
src/wal_sh/chor/validate.clj– a klordefchorover roles[Allocator Skeptic Citation Date Style Ledger]. Reads(o/needs-review docs)atAllocator, routes each candidate heading (by:CUSTOM_ID:) to the role that owns its verdict dimension via klor's(A=>B value), gathers verdicts atLedger, which appends to.verify/chain.jsonl.src/wal_sh/chor/project.clj– the EPP step, but the projection target is a prompt, not a JVM process: each role projects to{:role :Skeptic :context <heading+drawer+upstream-msgs> :ask "..." :emit-schema ...}– a StructuredOutput contract, exactly the shape the Workflow agents already run under.scripts/chor-validate– a bb driver mirroringscripts/audit-annotations+scripts/verify-chain: runs the projected prompts (prompt-caching the shared choreography prefix), collects per-role StructuredOutput, feeds each intoverify-chain add --verifier <role> --verdict <v> --subject <custom-id>.gmake chor-validatenext toaudit-annotations.
The payoff, in one line: today an agent can emit :VERDICT: corrected on a
heading no other agent looked at, and o/invalid-verdicts only catches
off-convention values, not missing cross-checks. With projection, the
Citation role cannot run without the heading Skeptic flagged being routed to
it – the dependency is syntactic. Communication mismatch becomes impossible to
write, which is klor's exact selling point, lifted to prompts.
Why klor, not a Python framework: projection must be a real compiler with a
real type checker. klor is library-level choreography in Clojure via
tools.analyzer at macroexpand; defchor over named roles, (R1=>R2 v)
message-passing, EPP to ordinary per-role Clojure. The only change for prompting
is the projection backend: emit a prompt-context map + JSON-schema instead of a
core.async process. The repo is already CLJ/CLJC/CLJS with bb drivers reading
wal-sh.site.org – klor drops onto deps.edn with no paradigm shift. Its
simulator (projections over core.async with a fake-LLM stub) tests the
choreography's wiring deterministically before spending a token.
4. Review and revised plan (L7, 2026-06-12)
An L7 review checked the draft against the live annotation layer
(src/wal_sh/site/org.clj, the ledger, audit-annotations). Verdict: strong
essay, but chor-validate inverts the design intent of the system it claims to
fix.
Resolving the ledger citations. The review cites blocks of the verification
ledger .verify/chain.jsonl by index. That file has since been reset and is now
gitignored, so every citation below names the commit that still holds the
historical chain, ea860c33. Block indices are 0-based (i=43 is line 44 of the
file at that commit). The 26 pass blocks the review counts are reproducible:
git show ea860c33:.verify/chain.jsonl | grep -c '"verdict":"pass"'
(A bare grep -c pass reports 30; the four extra lines carry the word in
notes or a verifier name, not in verdict.)
What the review got right (folded in here, not buried):
- The fan-out is map-reduce, not a choreography. The six
claim-validator-teamblocks (ledger blocks i=41–46 atea860c33) are independent, single-subject, advisory verdicts. No validator consumes another's output; there is noA=>B value. A fan-in/fan-out with no inter-validator edges is map-reduce. The falsification test: try to write thedefchoron paper – the only edges areAllocator → {validators}andvalidators → Ledger. If you cannot write a non-trivial choreography, there isn't one. - Coupling would suppress the value. Ledger block i=43 at
ea860c33– a validator overturning a priorcorrectonorg-noter– exists precisely because the validators do not depend on each other. Routing "the heading Skeptic flagged" into Citation would have removed the redundancy that produced the one real dispute. - Over-claims contradict the draft's own open question. "Syntactically
impossible to express" / the ledger "becomes a tautology" are false for a
prompt target: a stochastic model can ignore its projected role. Projection
buys a contract enforced at runtime, not a compile-time guarantee. And making
verify-chainOK a tautology would gut the ledger's stated purpose (a tamper-evident trail records nothing falsifiable if OK is guaranteed).
The one real synchronization defect it found (the narrative-cohesion failure,
made concrete): the ledger at ea860c33 carries verdict "pass" ×26 (the
metadesc batches), while the drawer vocabulary o/verdict-values was, at the
time of the review, the 5-value set
{correct corrected disputed verified needs-citation}. Ledger-batch verdicts and
drawer-heading verdicts had drifted into two vocabularies, and CLAUDE.md
claimed they were "the same value." That is a schema desync – fixable with a
shared enum, not a six-role choreography.
What has shipped since: o/verdict-values is now the 8-value set
{correct corrected disputed verified needs-citation reproduced attributed speculative}
(src/wal_sh/site/org.clj line 35), and wal-sh.site.validate/verdict-whitelist
(src/wal_sh/site/validate.clj line 42) redeclares the same 8 values as the
single source of truth for the verdict-convention check (line 327), which
flags any drawer verdict off the whitelist. The ledger-side half did not ship as
a check: scripts/verify-chain add still accepts any --verdict string. The
pass drift was instead retired by the ledger reset; the fresh chain carries
zero pass blocks.
Revised plan – chor-lint, not chor-validate:
- Falsify on paper first. Write the validate-annotations
defchoras data (roles, messages, knowledge-of-choice) with no klor dependency. The thesis is true iff a non-trivial protocol exists. One afternoon; likely kills the flagship and clarifies what's left. - Ship the contract, not the routing. This shipped, without klor and without
the name
chor-lint: the shared 8-value verdict enum lives insrc/wal_sh/site/validate.cljline 42 (verdict-whitelist, the declared single source of truth), mirrored byorg.cljline 35, and theverdict-conventioncheck enforces it over every drawer. The companion check that every ledgerverdictis in-vocabulary was not built; the reset chain made thepassdrift moot rather than caught. - Shelve klor until a run genuinely needs validator→validator message passing
(e.g. Citation must consume Skeptic's flagged spans). Then prototype in klor's
fake-LLM simulator first (the one part of
chor-validateworth keeping).
The narrative-cohesion reframe and the shipped verdict enum are what make this
worth keeping; the chor-validate compiler stays premature until a real
data-dependency between agents exists.
5. Sharpest open question
EPP for processes is sound because a projected process executes deterministically; a projected prompt is run by a stochastic model that can ignore its routing. Does the ledger-as-trace actually catch a role that "received" a message but never conditioned on it – or must projection enforce routing structurally (a separate API call per message, a per-role context firewall) rather than textually (one prompt narrating the protocol)? And does prompt-caching the shared choreography prefix (the cited speedup) directly undermine that firewall by leaking the global script to every role? The static guarantee survives only if the answer is structural; otherwise choreographic prompting buys a contract to enforce at runtime, not a guarantee.
6. Other open questions worth keeping
- Role granularity: fixed agent vs ephemeral subagent spawn vs context-window region. klor roles are static; agent swarms are dynamic – does fan-out over an unknown N need parametric/higher-order choreographies (cf. Pirouette)?
- Knowledge-of-choice assumes enumerable branches; LLM control flow is
open-ended. Force a bounded set of
selectlabels – and is that a healthy constraint on autonomy? - Affine/resource annotations: can "this inner loop runs at most k iterations per outer turn" be a projection-time type error (budget starvation as a type)?
- Ledger compaction: Merkle-fold an inner-loop episode into one block the outer loop signs – tamper-evidence at human-auditable scale.
- Inferred choreography: mine
.verify/chain.jsonl+ session transcripts to synthesize thedefchorthe agents were already implicitly following, then project it back as enforcement (theagent-contractsskill was already reverse-engineered this way).
7. The pitch
Your Agents Aren't Hallucinating, They're Losing the Plot: Narrative Cohesion as a Projection Problem.
When N agents each hold a private slice of context and no global account says what the shared story is, their outputs drift at the seams – silently, the way a distributed protocol with no source of truth drifts. The drift is invisible until the composite document reads like six people describing different elephants. The bet: write the shared narrative account once and project the per-role prompts from it, so cohesion is sourced from one place instead of reconstructed after the fact. This is not a compile-time guarantee against a stochastic model – it is a runtime contract that there is a single story every prompt is a view of. The honest first step was one shared verdict vocabulary, and that part has shipped; the choreography compiler waits for a real data-dependency between agents to appear.