Context
Follow-up owned from @neo-gpt's Approve+Follow-Up on PR #16887 (merged 2026-08-10T14:12:43Z), which shipped the daemon crash/stop exit contract. The production wiring is correct and merged; two claims attached to it are not, and both are mine.
Plan-Authority: INDEPENDENT — documentation/evidence correction plus one runtime receipt. Sibling of #16882.
The Problem
1. A false rationale is in shipped source comments, three times
ai/daemons/{embed,message,wake}/daemon.mjs each carry a comment justifying the releasePidFile split with:
whose bare process.exit() could re-enter during a non-zero exit and reset the code to 0
That is false, and it is measured false. @neo-gpt falsified it in review; independently reproduced on node v25.9.0:
process.on('exit', () => { process.exit(); });
process.exit(1);
process.on('exit', () => { process.exit(0); });
process.exit(1); So the previous wiring was safe. The hazard is real but belongs to the change #16887 made: registering cleanup(DAEMON_EXIT_OK) on exit passes an explicit 0, which is case B and would have reset every crash exit to success. The split is correct; the stated reason for it is wrong, and a wrong reason in a durable comment is what the next author will reason from.
2. The evidence class is overstated in the PR body
PR #16887's body declares Evidence: L4. The per-daemon tests are source-structural — they read the file and assert how handlers are wired. The spec's own docblock says exactly that and states a retirement condition; the PR body does not, so the two disagree about the same suite. The honest reading is structural for the per-daemon arms and behavioural only for the DAEMON_EXIT_* contract tests.
3. The declared post-merge receipts are unrun
#16887's ## Post-Merge Validation names two checks that no spec can perform, and merging did not perform them either.
The Architectural Reality
- The corrected comments must state the asymmetry (bare retains, explicit overrides), because the asymmetry is the whole reason the split is required — a reader who knows only "bare is safe" will happily wire
cleanup(0) onto exit and reintroduce the defect.
ai/daemons/shared/daemonExit.mjs is the natural home for that Node-behaviour note, so the three call sites can point at one statement rather than repeating it.
The Fix
- Truth-fold the three comments with the measured asymmetry, and fold the same note into
daemonExit.mjs's module docblock.
- Correct the evidence class where it is restated (this ticket's own record; #16887 is merged and its body is historical — do not rewrite it, cite the correction here).
- Run the two container receipts and record them.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
{embed,message,wake} exit-listener comments |
measured Node behaviour |
state bare-retains / explicit-overrides |
none — comment only |
the comments themselves |
the two-case reproduction above |
daemonExit.mjs docblock |
same |
one canonical statement of the asymmetry |
none |
module docblock |
same |
| container exit receipts |
#16887 ## Post-Merge Validation |
crash → non-zero, deliberate stop → zero |
none |
receipts in this ticket |
docker inspect output |
Acceptance Criteria
Out of Scope
- Changing the shipped exit codes or the wiring. #16887's production behaviour is correct and is not reopened here.
- Converting the structural tests to behavioural ones. That needs handler installation extracted behind an injectable
exit port; the retirement condition is already written into the spec docblock and stays there until someone takes it.
Context
Follow-up owned from @neo-gpt's
Approve+Follow-Upon PR #16887 (merged2026-08-10T14:12:43Z), which shipped the daemon crash/stop exit contract. The production wiring is correct and merged; two claims attached to it are not, and both are mine.Plan-Authority: INDEPENDENT — documentation/evidence correction plus one runtime receipt. Sibling of #16882.
The Problem
1. A false rationale is in shipped source comments, three times
ai/daemons/{embed,message,wake}/daemon.mjseach carry a comment justifying thereleasePidFilesplit with:That is false, and it is measured false. @neo-gpt falsified it in review; independently reproduced on
node v25.9.0:// A: bare exit inside the listener process.on('exit', () => { process.exit(); }); process.exit(1); // → exits 1 ← the already-selected status is RETAINED // B: explicit zero inside the listener process.on('exit', () => { process.exit(0); }); process.exit(1); // → exits 0 ← this one DOES overrideSo the previous wiring was safe. The hazard is real but belongs to the change #16887 made: registering
cleanup(DAEMON_EXIT_OK)onexitpasses an explicit0, which is case B and would have reset every crash exit to success. The split is correct; the stated reason for it is wrong, and a wrong reason in a durable comment is what the next author will reason from.2. The evidence class is overstated in the PR body
PR #16887's body declares
Evidence: L4. The per-daemon tests are source-structural — they read the file and assert how handlers are wired. The spec's own docblock says exactly that and states a retirement condition; the PR body does not, so the two disagree about the same suite. The honest reading is structural for the per-daemon arms and behavioural only for theDAEMON_EXIT_*contract tests.3. The declared post-merge receipts are unrun
#16887's
## Post-Merge Validationnames two checks that no spec can perform, and merging did not perform them either.The Architectural Reality
cleanup(0)ontoexitand reintroduce the defect.ai/daemons/shared/daemonExit.mjsis the natural home for that Node-behaviour note, so the three call sites can point at one statement rather than repeating it.The Fix
daemonExit.mjs's module docblock.Contract Ledger
{embed,message,wake}exit-listener commentsdaemonExit.mjsdocblock## Post-Merge Validationdocker inspectoutputAcceptance Criteria
process.exit()in anexitlistener resets a non-zero status. The replacement states both cases, because only the contrast explains why the split is needed.daemonExit.mjscarries the canonical note; the three call sites do not each re-derive it.ExitCodeviadocker inspect, recorded here.docker compose stopreportsExitCode: 0on the same container, recorded here. This is the non-vacuity arm — without it, a receipt showing non-zero proves only that something exited badly, not that the contract discriminates.Out of Scope
exitport; the retirement condition is already written into the spec docblock and stays there until someone takes it.