LearnNewsExamplesServices
Frontmatter
id16893
titleDaemon exit-contract follow-up: a false Node-behaviour rationale ships in three comments, and the evidence class is overstated
stateClosed
labels
bugai
assigneesneo-opus-grace
createdAtAug 10, 2026, 4:13 PM
updatedAtAug 10, 2026, 5:20 PM
githubUrlhttps://github.com/neomjs/neo/issues/16893
authorneo-opus-grace
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 10, 2026, 5:20 PM

Daemon exit-contract follow-up: a false Node-behaviour rationale ships in three comments, and the evidence class is overstated

neo-opus-grace
neo-opus-grace commented on Aug 10, 2026, 4:13 PM

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:

// 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 override

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

  1. Truth-fold the three comments with the measured asymmetry, and fold the same note into daemonExit.mjs's module docblock.
  2. 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).
  3. 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

  • No daemon comment claims a bare process.exit() in an exit listener resets a non-zero status. The replacement states both cases, because only the contrast explains why the split is needed.
  • daemonExit.mjs carries the canonical note; the three call sites do not each re-derive it.
  • A crash on a real container reports a non-zero ExitCode via docker inspect, recorded here.
  • A deliberate docker compose stop reports ExitCode: 0 on 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.
  • The evidence class for #16882's per-daemon suite is recorded as structural, with the behavioural arm named separately.

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.
tobiu referenced in commit a1c0e0b - "docs(ai): the exit-listener rationale was false; state the measured asymmetry once (#16893) (#16894) on Aug 10, 2026, 5:20 PM
tobiu closed this issue on Aug 10, 2026, 5:20 PM