Two rewrites, one strategy: the Python design doc that became a Go binary¶
Here is a sentence that sounds like a mistake and is actually the most useful thing I learned this year: the most valuable rewrite of this project's orchestrator is one that never shipped a single line of code.
The orchestration layer -- the machinery that turns a ticket into a reviewed, merged pull request -- has been rewritten twice. Once on paper, in Python, in a design doc that was explicitly never scheduled and never executed. Once for real, in Go, as a command that builds, tests, and ships. The two rewrites were in different languages, by different efforts, months apart. And the punchline is that the one that shipped followed the strategy the one that never shipped had worked out. The paper rewrite did the thinking; the Go rewrite cashed it in.
That is an awkward, instructive shape, and I want to be honest about it rather than tidy it up. If you are learning to build software with AI, you are going to write a lot of design docs and throw most of them away, and you are going to wonder whether the thrown-away ones were wasted. This is the post where I argue that one of them very much was not.
The first rewrite was a design, not a schedule¶
Open the overview docs and the very first words tell you its status: "Status: design, not scheduled." It was drafted as a would-be "Phase 750," then a later ROADMAP rewrite retired numbered orchestrator phases on the grounds that orchestrator work does not fit a phase-with-gate model. So the design lost its home in the build plan and the doc became its own home -- a complete, serious rewrite plan with no slot to run in. As the doc itself puts it: "The design itself remains valid; this doc is its home until somebody decides to act on it."
Nobody acted on it. No workflow_py/ directory was ever born. The shim that would have swapped the bash orchestrator for a Python one was never written. By every measure that a project tracks -- commits, merged tickets, run records -- the Python rewrite did not happen.
But read past the status line and you find something that did not need code to be real: a worked-out strategy for how you safely replace a system that is still running every day. That strategy is the thing that survived.
The strategy it worked out¶
Four ideas carry the Python design doc, and none of them is about Python. They are about how you port a live orchestrator without breaking the work flowing through it.
- Parity-first. Ticket #1 is not a feature -- it is a parity test harness. The doc is blunt about why: without it, "every subsequent ticket's review degrades to 'the agent says it works, do I believe it'; with it, every port has a machine-checkable acceptance gate from day one." The harness dual-runs the old and the new orchestrator against the same synthetic backlog and diffs their outputs. You do not port a verb until you can prove the port behaves like the original.
- A frozen verb surface. The prime invariant is "zero behavioral change at the verb surface." A user running the lifecycle should not be able to tell which orchestrator is underneath by behavior alone. The draft-to-ticket-to-review-to-ship lifecycle and the sibling
.run.mdrun records are frozen for the duration -- you are changing the mechanics, not the shape. (That lifecycle is the subject of its own post, The orchestration lifecycle: from phase to ticket to retro; the rewrite's whole job was to preserve it exactly.) - Lowest-risk-first sequencing. After the harness, the doc orders the port from safest to scariest: read-only verbs (
status,todos) first to settle the state schema, then single-agent verbs (ticket,phase,confirm), then thegh-touching verbs (review,ship), and the parallel-batch and merge-queue paths last. The reasoning has a nice recursive twist: parallel-batch is the verb being used to drive the rewrite, so it cannot be replaced until everything else already is -- "a satisfying final dogfood." - Vendored fakes as a language-agnostic contract. The Phase 700 test fakes -- the workflow test harness,
fake-gh.sh,fake-ntfy.sh-- are activated byPATHoverride and environment-variable indirection, which means they do not care what language calls them. The doc reuses them deliberately: "a contract that's language-agnostic." A fake invoked throughPATHis exactly as usable from Go as from bash.
Hold onto that last point, because it is the hinge of the whole story. A test contract that does not care about language is a piece of strategy you can hand to a rewrite in any material. The Python doc did not know it was writing the test plan for a Go program. But it was.
The second rewrite executed that strategy in a different material¶
The orchestrator that actually got ported is tempo, a Go command. Its the Tempo design memo describes it as "the future Go command for ticket-driven workflow orchestration," built as a separate command name on a pinned Go toolchain with its own tempo-owned Python venv -- deliberately walled off from the surrounding repo's runtime so the port could not accidentally lean on it.
What matters for this post is that the Go port carried the Python doc's strategy across, almost beat for beat:
- Parity-first carried. the Tempo implementation runs "the frozen the orchestrator dispatcher reference and tempo over identical fixture inputs, and compares normalized
backlog-listandstatusoutput." That is the Python doc's ticket #1, rebuilt in Go: the old bash orchestrator is the frozen reference, the new one is checked against it. The very first tempo ticket in the batch istempo-orchestrator-parity-harness. - The verb behavior stayed frozen -- with one deliberate divergence worth naming. The Python plan would have kept the literal command name (
dev.sh) and swapped the implementation under a shim. The Go port instead chose a new command name,tempo, because "the current workflow has project and orchestrator commands with overlapping names, which can make AI sessions ambiguous." So the command changed; the verbs and their behavior did not. The lifecycle, the verbs, and the run-record shape are preserved and parity-checked. Frozen surface, new doorknob. - Lowest-risk-first carried. The tempo batch built the toolchain scaffold and dispatcher skeleton first, then the state store and config models, then the lighter read and draft verbs, then the heavier stages --
tempo-orchestrator-stage-ticket-implement, thentempo-orchestrator-stage-review-preflight, thentempo-orchestrator-stage-ship-merge, withtempo-orchestrator-batch-and-group-runamong the last. Same safest-to-scariest ordering the Python doc prescribed. - The vendored fakes carried for free -- which is the payoff of that language-agnostic contract. Because the fakes are driven through
PATH, the Go tests reuse the same fixture discipline the bash suite did, with no rewrite of the fakes themselves.
Same plan. Different language. The strategy was portable in a way that code never is.
The dogfood: the old orchestrator shipped its own successor¶
This is the beat I find genuinely strange, in the good way.
The tempo port was not built by hand on the side. Its tickets were authored and run through the existing bash orchestrator -- the very system tempo is meant to replace. The old orchestrator scheduled the tempo tickets, spawned the worker clones, ran the review gates, and merged the pull requests that built its own replacement. Its final act was shipping its successor.
And it finished the job. The retrospective at the ticket archive audited the whole batch and found that "all discovered tempo-* ticket run records are archived and report outcome: merged," with "no tempo ticket without a .run.md sibling." That is roughly three dozen tickets -- the retro enumerates every one -- each carried end to end by the system being decommissioned, each leaving the same greppable run-record memory every other ticket in this project leaves behind.
If you have read The cross-game compounding flywheel, this will rhyme: the same retro machinery that folds one game's lesson back into the catalog is what audited the port and confirmed nothing fell through. The orchestrator did not just build tempo -- it remembered building tempo, in the same durable format it uses for everything else. The dogfood is not a stunt; it is the system's normal loop pointed at itself.
Reconciling the drift: Tempo is the realized direction¶
There is a tidy-up to do here, and it is a documentation problem, not a code one. When you change where a system is headed, the old maps of where it was headed do not erase themselves. Two of them are still in the repo, and a reader who finds them first could reasonably conclude the orchestrator's future is still open.
- the overview docs describes a Python rewrite as the next move. It predates the Go port.
- the overview docs carries a "Durable-execution engine for orchestrator scheduling" note that weighs Temporal as "the closest conceptual fit if the system ever grows beyond the custom scheduler's comfort zone," and explicitly flags that Temporal "has a first-class Go SDK, which matters if the orchestration layer ever moves out of shell/Python." That note also predates -- and is partly overtaken by -- the Go work.
I am not going to rewrite either doc here, and neither should you reach for the find-and-replace. They are honest records of thinking that was real when it was written, and the project keeps its road-not-taken notes on purpose. The reconciliation a reader needs is just this: Tempo is the realized direction. The orchestration layer did move toward Go -- not via Temporal's durable-execution engine, and not via a Python rewrite, but via a hand-built Go command that kept the project's markdown-transparency model intact. The Python doc and the IDEAS Temporal note are the two futures that were considered; the Go port is the one that happened. Read them as alternatives that lost, not as plans still pending.
(For control-plane specifics, the current surface is the /dev-* slash commands over the orchestration control plane orchestrator, not any retired tooling -- if you see references to older control planes in old notes, they are historical.)
The honest edge¶
Here is the uncomfortable, useful point, and I would be selling you something if I softened it.
A design doc that was never scheduled still taught the strategy that shipped. The value of the Python rewrite was not code -- none landed, and none ever will. Its value was the parity-first, frozen-surface, lowest-risk-first, language-agnostic-fakes thinking, which a later effort in a different language reused wholesale. If you measure that doc by its output, it is a zero. If you measure it by its influence on the thing that actually shipped, it is one of the highest-leverage artifacts in the repo. Those two measurements disagree completely, and the second one is the true one.
There is a real cost hiding in that, and I will not bury it. A strategy document that is never executed is also never tested, and you cannot always tell the difference between "evergreen strategy" and "plausible-sounding plan that would have fallen apart on contact with code" until something actually runs it. The Python doc looks vindicated because the Go port succeeded -- but the Go port is the experiment that validated it, after the fact. Until tempo ran, the design was a bet, not a proven plan.
And the validation is still partial, honestly. The parity harness today "compares normalized backlog-list and status output" and its own report "lists uncovered lifecycle surfaces, including ticket/review/ship/merge execution, so a future cutover can expand coverage deliberately instead of mistaking a read-surface check for complete orchestrator parity." In plain terms: the read surfaces are parity-checked; the heavier lifecycle surfaces are ported but not yet under the same direct comparison. The strategy carried. Proving it carried all the way is work that is still open.
So the lesson is not "design docs are always worth it" -- plenty are not. The lesson is narrower and more durable: the strategic thinking is separable from the implementation, and it outlives the implementation's language. When you write a plan for replacing a running system -- parity gate first, surface frozen, riskiest part last, tests that do not care what calls them -- you are writing something that can be cashed in by an effort you have not imagined yet, in a material you did not choose. The Python rewrite proved that the hard part of a rewrite was never the language. It was the strategy. And the strategy shipped, even though the doc never did.
That is the shape of two rewrites and one strategy: a Python design that was never scheduled, a Go port that ran the same plan in a different material, an old bash orchestrator that shipped its own successor through its own loop, and a tidy-up that names Tempo as the realized direction while leaving the roads not taken on the map. If you want the loop the whole thing was built to preserve, that is the lifecycle deep-dive; if you want the argument for why a system that learns about itself this way compounds, that is the compounding-flywheel post. This one is the case for the design doc you are about to throw away: keep the strategy. It is worth more than the code.
Or: subscribe for the rest of the track on how the orchestrator evolves.