0013 — Answered is not executed, and the sweep chases both

The failure

Measured 2026-09-07: six decisions Gera had answered sat between one and six days with nobody acting on them. Each row carried his verbatim choice — "Merge all three in order: #6915, then #370, then #371", "Turn it on — I set the switch, restart Smith". Every raiser's task was still awaiting-human.

Two writers, each correct alone:

  1. The decisions page closes the row when he clicks. Five of the six carry via: agents-web. That behaviour answers his 2026-08-05 report — "I click answers and they are NOT BEING RECORDED" — because the Decisions tab renders the ledger, so a row left open reads as still waiting on him.
  2. The driver's whole design is that the row stays open until the raiser has EXECUTED. Its own words: "this driver does NOT close the row the instant an answer appears — that would write 'executed' over work nobody did." It wakes, it nags, and the happy path is the raiser closing its own row.

Together the first defeats the second. SmithDecisionBlockWorkflow._apply reads RESOLVED on its next poll and returns _closed_by_someone, which reports closure.DONE"the happy path" — and terminates. The raiser is never woken. The sweep will not re-arm it, because open_block_records returns state == "open" only. The answer is recorded, durable, correct, and unexecuted.

This is the 2026-08-10 failure the driver was built to delete ("40 answers landed, an hour later 28 still read as unresolved, because nothing was polling"), reappearing one layer up: now something polls, and the poll is switched off by the click.

The decision

answered is not a terminal state, and the sweep drives it.

  1. open_block_records also returns rows that are resolved, whose resolution reason is answered, that carry no execution record, and whose raiser owns a non-terminal task. They are stamped so the driver knows what it is driving. Rows are still skipped for waiting_on, unreadable, id-less.
  2. The driver, for such a row, does not treat RESOLVED as somebody else's close. It goes to the wake-and-nag path it already has, quoting the recorded choice, and ends on closure.PARTIAL with the remainder named — recorded, not executed — never DONE, which it cannot know.
  3. The bound is liveness, not a counter. An unexecuted row is driven only while liveness says its raiser is ALIVE. Waking a dead session does nothing, and a lane whose raiser is gone needs a person or a new operator, not a nag — so it stays on blocked outstanding's worklist instead. This reuses the fleet's one three-valued primitive and refuses on UNKNOWN, the same gate _close_it_ourselves already carries.

Why not the alternatives

Stop the page closing the row. Restores the original design exactly, and reintroduces 2026-08-05 unless the Decisions tab first grows a third rendered state. It changes the surface Gera uses daily to fix a defect behind it; the sweep change does not.

Full separation (open → answered → executed → closed) with the page rendering all three. The cleanest end state, and the right eventual shape. It touches the page, the driver and the ledger contract at once, and none of that is needed to stop answers being stranded today. Left open deliberately; this ADR does not foreclose it.

What this does not fix, stated so nobody assumes it

Consequences

Amended by ADR-0022 (2026-09-10)

Two things this ADR named and did not do are now done, and one thing it said about the catalog turned out to be the reverse of what happened.