diff --git a/docs/plans/2026-09-26_lead-decisions.md b/docs/plans/2026-09-26_lead-decisions.md index 870a68da..8dee9843 100644 --- a/docs/plans/2026-09-26_lead-decisions.md +++ b/docs/plans/2026-09-26_lead-decisions.md @@ -1617,3 +1617,34 @@ which stay with him. Each item names who decided it and what happened. - S6's gate uses a scratch business with no tracker, so it doesn't wait on the tasks.mosaicstack.dev vs tasks.woltje.com ruling. Dogfooding against the live mosaic-stack business does. +79. **CHAT-01 gets an `engine-exit` stop mode; the EOF proof moves to row + 52 (2026-10-10).** Dewey stopped row 51 (#1537) at rev 292. Recording + `stopped` at engine exit needs a CHAT-01 rule changed: + `cohortProof.stop` must name a stop, no stop mode means "the engine + exited", and `confirm-stopped` (`check.mjs` line 337) accepts only a + confirmed `force-stop`. Ruling: amend CHAT-01, and don't narrow the + brief to evidence-only. + - Why: every conversation that ends on its own otherwise keeps a + scope and an idle shim until someone force-stops it. The force-stop + confirmation guards a destructive act, and at EOF the controller + kills nothing; it reads the cohort and releases only if it is empty. + The proof stays the cohort and effects observation, so "EOF does not + prove death" holds. + - Authority: CHAT-01 is a reviewed draft. Jason approved the drafting + (#1507), and Q21 covers publishing reviewed, green refactor work. + An additive stop mode with its own contract review is a design call + like item 30's C-5 move, so it's mine. It authorizes no deployment, + and it changes no role, policy or credential. + - Shape: one new row 52, owner Dewey, reviewers Darkwing (CHAT-01's + author) and Filbert, after row 51 because the two share + `cohort.mjs` and `controller.mjs`. The CHAT-01 commit comes before + the conversation code, and each approval names all four CHAT-01 + hashes. Brief: `docs/plans/2026-10-10_cohort-release-follow-ups.md`, + section "CHAT-01 engine-exit stop and release at engine exit". + - Row 51 keeps the crash window and retry, the close PID fix, and the + relok, closeany, noprooffkind and nopush tests. Its brief section + drops the engine-exit item and its two tests, re-pinned by + `queue set 51 brief`. + - Dewey's rejected workarounds stay rejected: labelling the EOF stop + `force-stop` without a confirmation, and `stopped` on the claim while + the binding stays `uncertain`. diff --git a/docs/plans/2026-10-10_cohort-release-follow-ups.md b/docs/plans/2026-10-10_cohort-release-follow-ups.md index 4c425886..42a963a7 100644 --- a/docs/plans/2026-10-10_cohort-release-follow-ups.md +++ b/docs/plans/2026-10-10_cohort-release-follow-ups.md @@ -1,7 +1,10 @@ # Conversation cohort: release follow-ups after row 50 (2026-10-10) Status: written by Sage, lead. It follows `docs/plans/BRIEF-TEMPLATE.md` -and amends no section of the slice 1 brief. One row. +and amends no section of the slice 1 brief. Two rows. Row 51 was briefed +with the engine-exit proof in it. Dewey found that the proof needs a +CHAT-01 rule changed, so lead decision 79 (2026-10-10) moved it to row 52, +which carries the CHAT-01 amendment and its own contract review. ## Cohort release follow-ups: engine exit, crash window and close @@ -45,14 +48,8 @@ Owner: Dewey. Reviewers: Darkwing and Filbert. ### What ships -- Engine exit. On EOF the controller runs the same cohort proof the force - stop uses. If the cohort reads empty, it records `stopped` with that - `cohortProof` and releases the scope. Nothing is killed, so no client - confirmation is needed. If the cohort isn't empty, the binding stays - `uncertain` as today. Before writing code, the packet names every - claim-protocol rule this touches (K6 included). If one of them needs a - change to a rule written in the slice 1 brief, stop and report to Sage - first. +- Engine exit isn't in this row. It moved to row 52 (decision 79), and + this row leaves the EOF path as row 50 left it. - Crash window and retry. The start and recover paths release a claim recorded `stopped` with a `cohortProof` whose scope is still listed. A release that ended `unavailable` or `still listed` is retried there, @@ -61,9 +58,6 @@ Owner: Dewey. Reviewers: Darkwing and Filbert. signal the recorded PID, or it checks that the PID is still the same engine before it does. The packet says which and why. - Tests: - - an engine that exits on its own leaves no unit and records `stopped`; - - an engine that exits while another member still runs stays - `uncertain`, and nothing is released; - a restart after a crash between `stopped` and the release removes the unit; - close after a proven stop doesn't signal a PID it can't show is the @@ -72,11 +66,96 @@ Owner: Dewey. Reviewers: Darkwing and Filbert. ### Out of scope -The force-stop phases, the pgroup fallback, and the release polling cost -(Darkwing note 2). +The engine-exit proof (row 52), the force-stop phases, the pgroup +fallback, and the release polling cost (Darkwing note 2). ### Gate Darkwing and Filbert approve on the row's issue. The conversation and webui node suites and every `scripts/test-*.sh` green on Sage's gate rerun. No `mosaic-chat-*` scope left after the suites. + +## CHAT-01 engine-exit stop and release at engine exit + +### Problem + +A normal engine exit leaves the scope and an idle shim, one per +conversation that ends on its own (row 50 deviation, comment 27054). The +fix is the cohort proof at EOF, but CHAT-01 has no way to record it +(Dewey, row 51, rev 292): + +- `cohortProof.stop` must name a stop record, and a stop's `mode` is + `interrupt`, `force-stop` or `revocation`. None of them means the engine + exited. +- `confirm-stopped` in `docs/plans/chat-01/check.mjs` refuses any stop + whose mode isn't `force-stop`, and a force stop needs exact client + confirmation (K6). So every route to `stopped` today runs through a + confirmed force stop. + +The confirmation guards a destructive act. At EOF the controller kills +nothing: it reads the cohort, and it releases only if the cohort is empty. +Labelling that a force stop without a confirmation, or recording `stopped` +on the claim while the binding stays `uncertain`, would break a rule +instead of changing it. This row changes it in the contract first. + +### Owner and reviewer + +Owner: Dewey. Reviewers: Darkwing (CHAT-01's author) and Filbert. As the +CHAT-01 README requires, each approval names all four current hashes of +`docs/plans/chat-01/`. + +### Files owned + +- `docs/plans/chat-01/contracts.schema.json`, `check.mjs`, `fixtures.json` + and `README.md` +- `packages/conversation/src/cohort.mjs`, `packages/conversation/src/controller.mjs` +- `packages/conversation/src/shim.mjs`, only if the EOF proof needs a shim op +- `packages/conversation/tests/` and `packages/conversation/README.md` + +### What ships + +- CHAT-01, in its own commit before any conversation code: + - Stop mode `engine-exit`. The server starts it, as it does a + revocation (`request: null`). It needs no confirmation, since nothing + is signalled. + - Starting one closes admission and takes the one escalation slot + (H10). A force-stop confirmation issued before it goes stale (H17). A + request already dispatched to the engine ends with an `uncertain` + receipt, never an invented outcome. + - `confirm-stopped` accepts `engine-exit` under the same `stopped(w, s)` + check as `force-stop`: complete membership, the epoch and verified + effects. + - Recover after an `engine-exit` stop still needs its own exact + confirmation, bound to that stop's ID (K6, K17, K18). + - The README rule "SIGTERM, EOF, abort acknowledgment or idle does not + prove death" stays. EOF starts the observation, and only the cohort + and effects observations prove. K15 (an unreadable `engine` cgroup is + absent, never empty) and K2 (pgroup never proves) are unchanged. + - Fixtures: an empty cohort reaches `stopped`. A non-empty or unreadable + cohort stays `uncertain`. A pending force-stop confirmation goes stale, + and recover without a confirmation is refused. +- Conversation: on EOF the controller runs the same cohort proof the force + stop uses, with `hello`, `events` and `members` only, and no freeze or + kill. If the cohort reads empty, it records the `engine-exit` stop + `stopped` with that `cohortProof` and releases the scope through + `#releaseScope`. If the cohort isn't empty, the binding stays + `uncertain`, as it does today, and nothing is released. +- Tests: + - an engine that exits on its own leaves no unit and records `stopped`; + - an engine that exits while another member still runs stays + `uncertain`, and nothing is released; + - an EOF proof racing a client force stop takes one escalation slot, + not two. + +### Out of scope + +Any other change to CHAT-01, CHAT-01C or the slice 1 brief. If the work +needs one, stop and report to Sage. + +### Gate + +Darkwing and Filbert approve on the row's issue, each naming the four +CHAT-01 hashes. `node docs/plans/chat-01/check.mjs` passes. The +conversation and webui node suites and every `scripts/test-*.sh` are +green on Sage's gate rerun, and no `mosaic-chat-*` scope is left after the +suites.