From 1da4f3fe30339f02558a6c250b17ee3c074976f3 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Mon, 4 May 2026 17:48:37 +0200 Subject: [PATCH] =?UTF-8?q?docs(ultraplan-local):=20Handover=207=20=C2=A7?= =?UTF-8?q?=20Lifecycle=20(SC-5=20stale-file=20principle)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 11 of v3.4.1 plan. Adds the lifecycle subsection to Handover 7 documenting: - Producer/consumer arbeidsdeling (executor + helper write; ultracontinue reads; pre-compact-flush refreshes only) - Stale-file principle: status==='completed' state files SHOULD be removed via /ultracontinue-local --cleanup --confirm (operator-invoked, no auto-cleanup, no force flag) - Frontmatter contract for NEXT-SESSION-PROMPT.local.md: producers MUST write produced_by + produced_at (ISO-8601); files without frontmatter are tolerated (warning, not error) for backwards compatibility - Idempotency: --cleanup --confirm is safe to re-run; partial state reported but never auto-recovered Adds 3 doc-consistency pins: - next-session-prompt-validator CLI shim - Handover 7 § Lifecycle subsection present - Handover 7 § Lifecycle names --cleanup + produced_by contract 358 -> 361 tests, all green. Co-Authored-By: Claude Opus 4.7 --- .../docs/HANDOVER-CONTRACTS.md | 24 ++++++++++++ .../tests/lib/doc-consistency.test.mjs | 37 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/plugins/ultraplan-local/docs/HANDOVER-CONTRACTS.md b/plugins/ultraplan-local/docs/HANDOVER-CONTRACTS.md index b3654ad..7e8cae7 100644 --- a/plugins/ultraplan-local/docs/HANDOVER-CONTRACTS.md +++ b/plugins/ultraplan-local/docs/HANDOVER-CONTRACTS.md @@ -412,6 +412,30 @@ The validator (`lib/validators/session-state-validator.mjs`) exposes the standar - `SESSION_STATE_NOT_RESUMABLE` → warning; `/ultracontinue` exits cleanly with "no further sessions to resume; project complete" - Validator failures during writer Phase 8 emit a stderr warning but DO NOT block the session-end report. `progress.json` remains the authoritative record of what was attempted. +### § Lifecycle + +The state file follows a producer/consumer separation that keeps responsibilities narrow and the contract observable. + +**Producer/consumer arbeidsdeling:** + +| Role | Owners | Phase / location | +|---|---|---| +| Producer (writes the state file) | `/ultraexecute-local` | Phase 8 (canonical), Phase 2.55 (dirty-tree pre-flight stop), Phase 4 (entry-condition stop) | +| Producer (informal multi-session helper) | `/ultraplan-end-session-local` | Phase 3 — writes the same schema for ad-hoc handovers that don't run through executor | +| Refresher (touch only) | `hooks/scripts/pre-compact-flush.mjs` | Updates `updated_at` only; never creates the file; never changes `status` or any owned field; only acts when `status` is `in_progress` or `partial` | +| Consumer | `/ultracontinue-local` | Phase 2 — reads, validates, narrates a 3-line summary, then begins executing the next session | + +**Stale-file principle (SC-5):** When `status === 'completed'`, the state file and its sibling `NEXT-SESSION-PROMPT.local.md` represent finished work and SHOULD be removed. Removal is **operator-invoked** via `/ultracontinue-local --cleanup --confirm `; the plugin does NOT auto-cleanup. Stale state is actively harmful — it can mislead a fresh `/ultracontinue` into resuming a project that's already shipped. The `--cleanup` gate refuses to act unless `validateSessionState({...}).valid === true && parsed.status === 'completed'`. There is no force flag. + +**Frontmatter contract for `NEXT-SESSION-PROMPT.local.md`:** Producers MUST write a YAML frontmatter block on the prompt file with at minimum: + +- `produced_by:` — string identifying the producer (e.g. `ultraplan-local-A4-session`, `ultraexecute-local-phase-8`, `ultraplan-end-session-local`) +- `produced_at:` — ISO-8601 timestamp of when the file was written + +The `next-session-prompt-validator` (`lib/validators/next-session-prompt-validator.mjs`) cross-checks `produced_at` against the sibling state file's `updated_at` and emits a `NEXT_SESSION_PROMPT_INCONSISTENT` error when the prompt is older than the state — that means the prompt has not been refreshed for the current session and is stale. Files **without** any frontmatter are tolerated (warning, not error) for backwards compatibility with v3.3.x and earlier hand-rolled prompt files; this is consistent with Handover 3's drift-WARN posture. + +**Idempotency:** `--cleanup --confirm` is safe to re-run. If only one of the two files (state file, prompt file) was previously deleted, the second run reports the partial state ("state file: not found, prompt file: removed") but does not auto-recover or re-create. There is no rollback. Operators choosing to re-create a project after `--cleanup` should re-run `/ultrabrief-local` from scratch. + --- ## Stability summary diff --git a/plugins/ultraplan-local/tests/lib/doc-consistency.test.mjs b/plugins/ultraplan-local/tests/lib/doc-consistency.test.mjs index 9051543..c075b89 100644 --- a/plugins/ultraplan-local/tests/lib/doc-consistency.test.mjs +++ b/plugins/ultraplan-local/tests/lib/doc-consistency.test.mjs @@ -140,6 +140,43 @@ test('session-state-validator has CLI shim', () => { ); }); +test('next-session-prompt-validator has CLI shim', () => { + const text = read('lib/validators/next-session-prompt-validator.mjs'); + assert.ok( + text.includes('import.meta.url === '), + 'lib/validators/next-session-prompt-validator.mjs should expose the standard CLI shim ' + + '(if (import.meta.url === `file://${process.argv[1]}`)) so /ultracontinue Phase 1.5 can call it from Bash', + ); +}); + +test('HANDOVER-CONTRACTS.md Handover 7 documents § Lifecycle subsection', () => { + const text = read('docs/HANDOVER-CONTRACTS.md'); + const h7Start = text.indexOf('## Handover 7'); + assert.ok(h7Start >= 0, 'Handover 7 heading missing'); + const h7End = text.indexOf('## Stability summary', h7Start); + assert.ok(h7End > h7Start, 'Stability summary heading missing — could not bound Handover 7'); + const h7 = text.slice(h7Start, h7End); + assert.ok( + h7.includes('Lifecycle'), + 'Handover 7 section should include a § Lifecycle subsection (SC-5 stale-file principle)', + ); +}); + +test('HANDOVER-CONTRACTS.md Handover 7 § Lifecycle names --cleanup and produced_by contract', () => { + const text = read('docs/HANDOVER-CONTRACTS.md'); + const h7Start = text.indexOf('## Handover 7'); + const h7End = text.indexOf('## Stability summary', h7Start); + const h7 = text.slice(h7Start, h7End); + assert.ok( + h7.includes('--cleanup'), + 'Handover 7 § Lifecycle should mention --cleanup as the operator-invoked stale-file remover', + ); + assert.ok( + h7.includes('produced_by'), + 'Handover 7 § Lifecycle should document the produced_by frontmatter contract for NEXT-SESSION-PROMPT.local.md', + ); +}); + test('CLAUDE.md mentions /ultracontinue-local command', () => { const md = read('CLAUDE.md'); assert.ok(