Every ticket as the root of its provenance graph — goal, patch, approval, runs and (as the ticket-class build lands) mandate, manifestations, guarantees, journey, artifacts and children. Rendered through the <<<TicketGraphList>>> tag.
Redo #279 pair_loop no-verdict-crash fix + lazy re-ask registration — WITHOUT the assign-before-ACTIVE regression; selftest must cover the live governance path
goal
Re-do ticket #279's pair_loop.py work, which was HARVESTED then REVERTED on 2026-07-15 because it
shipped a dispatch-killing regression its selftest did not catch.
WHAT #279 WAS FOR (still wanted):
* Fix the harness no-verdict crash: a worker that returns nothing readable (or done=false) must
FAIL THE ROUND gracefully — not crash the pair. The worktree persists with whatever was
written and a fresh worker runs the next round.
* Lazy per-spawn registration so a RE-ASKED agent can exist at all: its id and birth prompt are
not known until its predecessor is rejected, so agent.register + ticket.assign must happen at
spawn time, immediately before each agent runs.
THE REGRESSION IT INTRODUCED (must NOT recur):
* By moving registration to lazy-at-spawn, #279 left `pair()` calling
`rest("process_status", {"id": tid, "status": "ACTIVE"})` BEFORE any agent was assigned. The
governance DB enforces an integrity constraint: "a ticket authorizes an agent: assign one
(ticket.assign) before it can go ACTIVE" (SQLSTATE 23000). So EVERY new dispatch 500'd at
"pair (round 1)" — the pipeline's core function was dead. Only already-ACTIVE pairs survived.
HARD REQUIREMENTS FOR THE REDO:
1. process_status -> ACTIVE MUST run AFTER at least one ticket.assign on the ticket (e.g. assign
the worker, or the scribe, first; only then flip ACTIVE — ACTIVE need only be set once, on
round 1).
2. Keep the no-verdict-crash fix and the lazy re-ask registration.
3. The --selftest MUST exercise the LIVE governance ordering (a real process_status->ACTIVE after
an assign, against the actual constraint), not only the throwaway in-memory DB. #279 passed
129/129 precisely because its selftest never hit the assign-before-ACTIVE rule. A regression
that takes down all dispatch must be catchable by the selftest.
DELIVERABLE: checkouts/current/congruency/tools/pair_loop.py (behavior-preserving except the two
fixes above). ACCEPTANCE: `pair_loop.py --selftest` passes AND a real `--ticket N` dispatch on a
REQUESTED forge unit reaches ACTIVE and spawns its worker (no 500), verified live.
patch
none
approval
unapproved
runs
no runs recorded
mandate (clauses)
not yet recorded — lands with the ticket-class build
manifestations
not yet recorded — lands with the ticket-class build
guarantees
not yet recorded — lands with the ticket-class build
journey (blunders & successes)
not yet recorded — lands with the ticket-class build
artifacts (forge)
not yet recorded — lands with the ticket-class build