design(sdlc): ship cannot terminate honestly when investigation finds no change is owed
Problem
archon-ship has no honest terminal for a correct conclusion of "the cause is rooted, and no change is owed in this repository". A run that reaches that conclusion is reported as failed.
The route markers in archon-ship.yaml gate on route and on $inv.output.rooted == true. An investigation that roots a cause owned upstream still sets rooted: true, so gate-rooted fires and the run enters the delivery tail. deliver__impl__assert-changed then refuses the result:
implement produced neither a commit nor a working-tree change outside
.archon/, and the branch carries no verified work ahead of the base (green=false, red_cause=unknown).
That guard is right about what it checks. The gap is that the graph offers no other way out: once investigation succeeds, delivery is the only continuation, and delivery is defined as a diff.
Why it matters
The failure is indistinguishable from a run that broke. Status failed, outcome: null, no PR — the same shape as a genuine execution fault. An operator reading run state alone learns nothing about the fact that the run did its job and the answer was "nothing to build here". The evidence survives only inside artifacts nobody is directed to.
It also makes the pack punish the most valuable outcome it can produce. Concluding that a reported bug is owned by a dependency, with a reproduction and rejected alternatives behind it, is a better result than a speculative local patch, and the pipeline scores it as a failure.
AGENTS.md already separates these facts for delivery — "an honest 'no work is owed' is a successful run that shipped nothing, and lifecycle status and work outcome are separate facts" is why outcome exists on ship at all. That reasoning stops at delivered: false for the no-action route and does not reach the rooted-but-nothing-to-build case.
Why now
Hit on the first real run of this shape. archon-ship on #3273 (run a8d1354ac61e4d0c54709ffec4143208) investigated a Pi credential bug, built a deliberate reproduction, proved the defect is upstream in Pi's refreshModelCatalogs, proved Archon's own paths already isolate providers, and correctly wrote no code. It failed at assert-changed.
That issue's own acceptance anticipated the branch — "If the owner is Pi, the finding is reported upstream and the issue records whether Archon tolerates, works around, or waits" — so the input was explicit that a no-code outcome was legitimate, and the pipeline still could not express it.
Desired outcome
A run that roots a cause and establishes that no change is owed in this repository reaches a terminal state that says so: the run completes, its outcome distinguishes "nothing to build" from "delivered", and the report explaining why is reachable from run state rather than only from the artifact tree.
The two remaining deliverables in that case are forge writes the implement node is explicitly forbidden to make (filing upstream, recording the decision on the issue). Whether those get an owner, or are handed back to the operator as declared output, is part of this decision.
Invariants
assert-changedkeeps refusing an empty delivery. This is not a request to weaken the spend guard; a run that was supposed to write code and did not must still fail.- The distinction must be established before the delivery tail, by the node that knows it — investigation — not by relaxing a downstream check.
- "Nothing to build" must not become reachable by a model simply declining work. It needs the same evidence bar as any other investigation verdict.
- No silent success: a run that fails for an ordinary reason must keep failing, and the new terminal must be distinguishable from both a delivery and a fault.
- Triage's existing vocabulary is not re-invented.
NO_ACTIONthere means the item was never actionable; this is a different fact, established after investigation.
Acceptance
- Seen red first: re-running
archon-shipon an issue whose cause is upstream-owned fails atdeliver__impl__assert-changedondev. - After: the same run completes, with an outcome that distinguishes it from a delivered PR and from a failure, and the report naming the owner is reachable from run state.
- A fixture covers the new path, and the existing red path — implement produced nothing when a change was owed — is seen red and still fails.
- Whether the remaining forge writes have an owner is decided and recorded on this issue.
Evidence
Run a8d1354ac61e4d0c54709ffec4143208 on #3273; its investigation.md, implementation.md and repro/ are in that run's artifacts. Anchors: .archon/workflows/sdlc/ship/archon-ship.yaml (the gate-rooted marker and the deliver join), and the deliver pack's assert-changed script.
Source: coleam00/Archon