Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: The ship pipeline dissolves into the orchestrator — the unit machine is the deterministic atom, judgement composes units, and the pipeline graph retires
status: proposed
status: accepted
date: 2026-09-21
pattern: Agentic orchestration over deterministic unit workflows — coding → review → fix → checks → merge remains one leased, single-owner state machine; an LLM loop reads each durable ending and chooses the next bounded act, while DAG planning, recovery directives and person-managed workflow columns retire
---
Expand Down Expand Up @@ -174,3 +174,42 @@ Before this proposal stands, an author-side adversarial pass tried to falsify th
8. **Objection: this proposal treats record 0072's accepted authority as shipped implementation.** Valid. The record now distinguishes acceptance from implementation everywhere 0072 is load-bearing and makes its field or an equivalent durable projection a dependency, never a present implementation fact.

After those changes, the remaining risk is not an unanswered structural contradiction but whether a reader with no session context can recover the boundary and the reconcile rules from the document alone. That is the pending gate: the maintainer gives only this record to an independent reader on the pull request and appends the reader's quoted restatement, hardest part and already-answered first objection. Until that receipt lands, `status: proposed` is the truthful status.

## Amended 2026-09-21 — the independent cold-reader gate passes

The acceptance receipt was posted as [comment 5766497724](https://api.github.com/repositories/1337796103/issues/comments/5766497724) on pull request 2156 at 2026-09-21 19:47Z. A fresh agent session was handed only this record at `e43f3b7c`, with no repository, other files or other context. Its answer, verbatim:

> **1. Restate the bet.** The record decides that the smallest deterministic thing in the system is one unit's machine — coding → review → fix → checks → merge, with its leases, single owner, admission, reviewed-head and fast-gate guards, rebase-before-push and merge door — and that everything above that atom becomes judgement: an LLM orchestrator reads each unit's named ending plus fresh repository and plane facts, then picks the next bounded act. What it replaces is the second orchestration system, the plan runner: the DAG walk with depends-on columns, the sequential cursor, the `plan:merge` versus `merge: person` column, attempts numbered by re-issue, the typed contract's stop rules, and the person-driven recovery vocabulary (`agent:ship`, `agent:coding`, `pulls rebase`). Plans survive as evidence the orchestrator reads and must cite when it departs from them, not as a program anyone executes. Authority is explicitly not part of the trade: where the effective grant says a person merges, a person still merges.
>
> **2. The hardest part.** The parent cardinality invariant — that a live parent declares exactly one of a live child, a bounded wait, or an ending — is the part I judge hardest to get right, and the record agrees by ranking it first in the difficulty map. It is hard because the writers of those three facts can sit on different generations of the process, so the replacement has to be durable, atomic and idempotent across a roll that can kill the writer mid-transition; anything less and the orchestrator merely observes the same owner gap that #2134 produced instead of closing it. It is also the one rule that cannot be delegated to the model, since the model can roll too, so the plane must own the zero-or-many detection and the repair trigger in deterministic code. And its correctness currently rests on a dependency that does not exist yet: record 0072's state field is still `proposed`, which the document concedes blocks the claim that the rule shipped.
>
> **3. First objection.** My first objection was that replacing a DAG with an LLM's choice makes scheduling unreviewable — if plan order is only "context", the orchestrator can quietly run a consumer before the schema change that feeds it and nobody can audit why. The document answers it in two places. Difficulty map item 7 states the requirement directly: "The orchestrator must cite why it departed from a written order, or 'judgement' becomes unreviewable scheduling by vibe." The "Why not keep depends-on as a hard guard?" section and amendment 6 add the safety half — the guards that must never soften live inside the unit machine and the merge door, while plan dependencies stay reasons the orchestrator should normally honor and must justify leaving, so a departure lands as cited fresh facts in the thread.
>
> Verdict: passes

## Amended 2026-09-21 — correctness review closes totality, generation and fallback holes

*Gate.* An adversarial pass reviewed this record against `main` at `1073acc1`, then rechecked the moved base at `c3f15544` after the mandatory pre-push rebase. The later base already carries `cf64c31a`'s ended-thread continuation: an ended non-merged pipeline re-issues its original task on the same plan and branch, while a merged unit routes fresh. That behavior is a preserved reconcile fixture, not work this record may replace. The pass tried to disprove four claims: total reconciliation over today's pipeline outcomes, cardinality across two bot generations, lossless flag changes in either direction, and preservation of every unit-machine and merge-door guard. Cross-model review was not used in this unattended acceptance run. It found three rollout-blocking omissions and one wording boundary; the corrections below are part of the accepted decision.

**Every current outcome has one next-act rule.** The six original reconciliation rows were not total over the facts `src/core/ship/coordinator.ts`, `src/core/runEvents.ts` and `src/core/pipelineStanding.ts` produce. In particular, `checks_failed` is a round outcome rather than a `UnitEnding`, and “approved but not merge-ready” is a standing assembled from approval plus fresh pull-request facts. The reconcile input is therefore one closed, exhaustively tested projection over both current unions, not a switch over ending names alone. A new source variant fails that totality test. Its required acts are:

| Reconcile fact | Only legal next acts |
| --- | --- |
| `merge_ready` | Re-read pull request, approved head, checks, queue and effective merge grant. Pass the same head through the merge door, or wait for the person/queue; never open a coding round. |
| approved but not merge-ready | Pending checks or queue state becomes a bounded wait. A dirty head enters the bounded pre-push resolver. Fix-up commits, a moved head or lost approval return through the machine's fix/review states. It cannot be relabelled merge-ready. |
| `held` | Persist one pending-question key and one bounded wait. The first accepted thread or pull-request answer clears it and resumes this unit; duplicates start nothing. |
| `checks_failed` | Preserve the exact pushed and reviewed head, turn the failed checks into machine-owned check findings and enter a bounded fix/review round. A human-gated finding parks a question. No orchestrator word can claim green. |
| died | Re-read the ledger ending, branch tip, open pull request and salvage receipt. Retry only under a remaining lease from the retained head; otherwise write a named ending with that head. A dead process is never evidence that work vanished. |
| `interrupted` | Adopt the checkpoint, branch tip or open pull request and resume the earliest safe machine state. Never start from an empty branch or number a new attempt. |
| budget | Apply the existing lease and renewal decision. Continue, park or end only as that deterministic result permits, always retaining the pushed head and pull request. The orchestrator cannot mint time or convert a spent bound to success. |
| `stopped` | Run mandatory work preservation before sealing the named stop. It stays ended until a later explicit reply resumes the same unit by adopting its durable head; no automatic retry defeats the person's stop. |

`merged` and `already_landed` remain done. `refused` and `merge_refused` remain guard outcomes, not prompts the orchestrator may reinterpret. The adapter also covers the source union's more specific cap, pending-review, no-verdict, transient, aborted, idle and step-threw forms by one of the rows above while retaining their exact cause; it never erases the source ending.

**Cardinality is one fenced durable transition.** Record 0072 is accepted at this head but its live-state field is not implemented, so this record's rollout depends on that field landing; “an equivalent projection” is no longer sufficient. The hosted parent's record-0072 state transition and a tagged relation — exactly one of `{child}`, `{wait}`, `{ending}` — are committed in one durable mutation under the row's expected sequence, composer epoch and owner generation. Reclaim fences the earlier generation before the new one may write. A stale generation, repeated event or losing compare-and-swap is a refusal, not a second relation. Readers consume the tagged relation; they do not reconstruct cardinality by joining three independently written fields. A legacy zero-cardinality row is repaired by re-reading child, ledger and repository facts and compare-and-swapping one relation; ambiguity parks a bounded reconciliation fault rather than guessing. Thus a roll may delay the transition but cannot expose two accepted owners or let an old writer overwrite the new generation.

**The flag chooses a desired composer, never two active composers.** The rollout flag is `plane.shipReconcile: off | shadow | on`: `off` chooses the plan runner, `shadow` leaves the plan runner authoritative while the orchestrator computes a side-effect-free decision over the same normalized ask and facts, and `on` chooses the orchestrator. A durable unit row carries the active composer and epoch. A mode change during coding, review, fix, checks or merge does not cancel or duplicate that machine act. It records the desired composer; after the act returns one durable ending, wait or question, a compare-and-swap advances the epoch, fences the old composer and lets the target adopt the same thread, branch, pull request, reviewed head, check head and pending-question key. A roll uses the same hand-off. A shadow decision cannot spawn, write, consume an answer or merge. Rollback therefore preserves a mid-round unit and its head in either direction instead of restarting it or allowing both paths to walk it.

**The atom and the door keep every guard.** “Retire the typed contract's stop rules” means retire graph-level prose that turns a recoverable orchestration condition into a terminal attempt. It does not retire the contract's first-instruction rebase, rebase-before-push, changed-set fast gates on the rebased tree, truthful validation, handoff, preservation of dirty or unpushed work, reviewed-head equality, authorization, confirmation or any merge-door check. Those remain executable preconditions inside the unit machine or door. The orchestrator selects a typed act and cites its facts; it cannot write approval, checks, reviewed head, gate receipts, grants or merge facts. Retiring the `merge: person` plan column likewise changes no effective grant.

The earlier rollout sentence that held acceptance for implementation gates is superseded: implementation proof belongs to the three-unit plan and does not masquerade as architectural acceptance. The independent cold-reader receipt and this tree-backed adversarial pass are the record's two acceptance gates. With their omissions corrected above, no unresolved contradiction remains, so the record is accepted; nothing in this status claims the implementation has shipped.
2 changes: 1 addition & 1 deletion docs/explanation/design-decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Statuses: **proposed** (written, not yet agreed), **accepted** (agreed, being bu
| 0070 | [The control plane is where the maintainer works — the /plane page pins a chat column beside the panels, and an orchestrator thread the person keeps talking to answers questions about the fleet and acts on it](../decisions/0070-the-control-plane-is-where-the-maintainer-works-the-plane-page-pins-a-chat-column-beside-the-panels-and-an-orchestrator-thread-answers-about-the-fleet-and-acts-on-it.md) | ChatOps over one command registry — the chat is a surface, never a second write path; one long-lived conversation per person as the working context; every read answered from one projection (the plane's tables), every write through the one fenced door; two deciders split by input kind — rules to the machine, a person's words to the thread | accepted | 2026-09-19 |
| 0071 | [A ship unit owns its pull request until the pull request is merged — merge-ready waits on facts, a DIRTY head buys a rebase round, and anyone's merge ends the unit](../decisions/0071-a-ship-unit-owns-its-pull-request-until-it-is-merged-merge-ready-waits-on-facts-and-a-dirty-head-buys-a-rebase-round.md) | Three mechanisms in place of one always-on loop — every push is preceded by a rebase onto the fetched base; a sweep command a person runs rebases the pipeline's open pull requests with a two-rung resolver (git alone with the repository's own merge drivers and rerere, then one bounded model round reading the repository's AGENTS.md); and watch-until-merge is a per-repository setting, off by default, reacting to push-to-base webhooks under a rebase-in-flight cap and a spend limit | accepted | 2026-09-20 |
| 0072 | [A run has one live state, owned by the server — a closed set from admitted to ended, one event the moment it changes, one projection and one wording function every surface reads](../decisions/0072-a-run-has-one-live-state-owned-by-the-server-a-closed-set-one-event-one-wording-function-and-every-surface-reads-the-one-field.md) | State machine as the one source of truth — the server assigns each run's live state from a closed union, appends a state event to the run's log when it changes, projects {state, since, bound, detail} onto the run summary, and one wording function beside the ending words renders it everywhere; the seven rival derivations are deleted | accepted | 2026-09-20 |
| 0073 | [The ship pipeline dissolves into the orchestrator — the unit machine is the deterministic atom, judgement composes units, and the pipeline graph retires](../decisions/0073-the-ship-pipeline-dissolves-into-the-orchestrator-the-unit-machine-is-the-deterministic-atom-and-judgement-composes-units.md) | Agentic orchestration over deterministic unit workflows — coding → review → fix → checks → merge remains one leased, single-owner state machine; an LLM loop reads each durable ending and chooses the next bounded act, while DAG planning, recovery directives and person-managed workflow columns retire | proposed | 2026-09-21 |
| 0073 | [The ship pipeline dissolves into the orchestrator — the unit machine is the deterministic atom, judgement composes units, and the pipeline graph retires](../decisions/0073-the-ship-pipeline-dissolves-into-the-orchestrator-the-unit-machine-is-the-deterministic-atom-and-judgement-composes-units.md) | Agentic orchestration over deterministic unit workflows — coding → review → fix → checks → merge remains one leased, single-owner state machine; an LLM loop reads each durable ending and chooses the next bounded act, while DAG planning, recovery directives and person-managed workflow columns retire | accepted | 2026-09-21 |

<!-- /generated:decision-records -->

Expand Down
Loading