The Failure That Was Actually Common
Coop's architecture opens its problem statement with two sentences worth memorizing. Workers occasionally start assigned work without taking its record. They more often finish long work after losing the reminder to close it. The second failure is the expensive one. The code shipped, the push went through, the session said so in a cheerful paragraph, and the ledger still shows the todo as taken, three days later, until Dad notices and repairs it by hand.
You cannot fix that with a better reminder. A reminder is a sentence in a context window, and the long work that made the session forget is exactly what pushed it out. So the architecture asks the engine to hold two facts independently of any model's memory: which exact session accepted which exact items, and which accepted items still need an outcome. The first fact is the binding you met in Track 3. The second is the subject of this whole track: the obligation.
The Debt Is Written With the Take
When a worker accepts an invitation, Engine.accept does several things in one transaction: it takes the item in the domain, inserts a binding, and inserts a row into engine_obligations with kind outcome and a due time of now plus OUTCOME_REVIEW_SECONDS, which is two hours. There is no moment where the work is taken and nothing is owed. If the store refuses the transaction, neither exists; if it commits, both do.
The command-line door gets the same treatment with a longer backstop. A todo take typed inside a session Coop can identify becomes a binding with an outcome obligation due in LEGACY_REVIEW_SECONDS, three hours. A take with no session reference at all is still a take: instead of refusing it (the refusal only pushed sessions to the untracked fast path), the engine writes a legacy_binding obligation, also due in three hours, whose reason reads "Taken without a session reference; the chief reviews its outcome".
Due Is Not Done
The due column is the most misread field in the table, so be precise about it. It is the time the obligation becomes eligible for review. Nothing happens to the work when it passes. The architecture says it plainly: a normal turn ending without a report makes the obligation eligible for review, not closure. Idle never means finished.
Only a disposition discharges an outcome obligation. A worker's own report of done, released or abandoned goes through _record_outcome, which writes the report, resolves the binding and sets the obligation to resolved with the report's id as its disposition, all in one transaction. A compatibility close from the CLI takes the same path. The chief's close, release or abandon takes it too. A report of blocked is the instructive exception: the engine stores it, sets the obligation's due to now and clears parked, and the work stays bound. A blocker is evidence that someone needs to look, not an ending.
The Lifetime Obligation and One Review
Here is the distinction that keeps this machinery honest. The obligation lives as long as the binding does. A review is one look at it. The architecture separates them on purpose: a persisted, version-checked decision to keep waiting can resolve the current review, but it must keep the outcome obligation and atomically arm the next due time. A wake receipt, a final sentence or an idle chief acknowledges neither.
| Decision on one review | The obligation | Next due |
|---|---|---|
| defer | stays pending | now + the requested delay (30 s to 30 days, default 300 s) |
| escalate | stays pending; Dad is told | now + the requested delay |
| park | stays pending, parked = 1; Dad is told | now + 30 days (PARK_SECONDS) |
| close, release, abandon | resolved | none |
A parked obligation waits for Dad or the worker alone. Observations of the worker's session no longer pull it forward; a fresh blocked report from the worker unparks it, and Dad's own disposition resolves it. If neither comes, the thirty days run out and it is owed again, because nothing in this table ever forgets a debt on its own.
One case got a tighter rule. Dad's ruling of 2026-10-08 reads "A blocked report is never parked for good", and the engine carries it as BLOCKED_SWEEP_SECONDS, six hours. When the worker's latest report says blocked, a defer or escalate is capped at six hours, a park comes due in six hours instead of thirty days, and a parked blocked item the chief skipped is still pulled back within six. The reason is practical: blockers clear. A push goes through later, Dad installs the build, a proposal gets answered, and the work should be closed without Dad having to ask.
Invariant Five
The architecture's fifth invariant is the sentence this lesson exists to earn: unresolved decisions survive worker, chief and engine context loss. The worker compacts and forgets its task: the obligation is a row, and the adapter restores the envelope from the store. The chief's turn crashes: its run is left uncertain, the obligation is still pending, and a failed run pushes the obligation's due fifteen minutes out instead of dropping it. The engine restarts: persisted due times drive the timers, and the architecture forbids the other design in one line, "an in-memory timer is never the obligation."