From 8485ea1d368d18e1f7a45581a85f20971a1ad839 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Fri, 19 Jun 2026 22:26:02 +0200 Subject: [PATCH] =?UTF-8?q?docs(voyage):=20S28=20=E2=80=94=20locate=20plan?= =?UTF-8?q?=5Fversion=20in=20plan=20header=20line,=20not=20frontmatter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HANDOVER-CONTRACTS.md was the lone source that called plan_version a frontmatter field. The plan template (templates/plan-template.md:21) emits it ONLY in the prose "Generated by" header line; brief-generated plans carry no frontmatter at all (the sole frontmatter block, source_findings, is trekreview-only and additive). planning-orchestrator.md:228 ("metadata line below the title"), the parser (lib/parsers/plan-schema.mjs, S26), and the plan-schema.test.mjs comment all already say prose. The contract was the outlier — the deferred doc-side closure the S26 commit named as scope to avoid then ("a 3-file doc reconciliation"). Operator-chosen direction (Option A + A1): correct the contract to the artifact, doc-only — NOT move the field to frontmatter (which would alter every plan's shape and fight the trekreview-only frontmatter design). A1 keeps the standardized "Frontmatter schema" heading, corrects the row, and notes the real (optional, trekreview-only) source_findings frontmatter. Three surgical spots in HANDOVER-CONTRACTS.md: - versioning table: `plan_version` (frontmatter) → (plan header line) - Handover 4 "Frontmatter schema": no-required-frontmatter note + Location column locating plan_version in the "Generated by" header line + parser ref - Handover 5 progress.json: "Mirrors plan's frontmatter" → "Mirrors the plan header's plan_version" TDD (red first): new doc-consistency pin asserts the contract no longer labels plan_version a (frontmatter) field, drops "Mirrors plan's frontmatter", and positively locates it in the plan header line. Red on the old wording (L15 + L264), green after the edits. Scope: only HANDOVER-CONTRACTS.md + the one pin. Template/orchestrator/parser/ examples untouched (already correct). Example-01's bare-line emission drift is noted out-of-scope for a possible later item. Tests: 729 (727 pass / 2 skip / 0 fail; live `node --test`, = 728 baseline +1). plugin validate passes (1 accepted CLAUDE.md-at-root warning). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01LqBYc8Ltrk7LipyJmGxXiB --- docs/HANDOVER-CONTRACTS.md | 19 ++++++++++++++----- tests/lib/doc-consistency.test.mjs | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/docs/HANDOVER-CONTRACTS.md b/docs/HANDOVER-CONTRACTS.md index f15642d..89d01c9 100644 --- a/docs/HANDOVER-CONTRACTS.md +++ b/docs/HANDOVER-CONTRACTS.md @@ -12,7 +12,7 @@ Each artifact carries an explicit version field. Schema bumps are coordinated: |---|---|---| | `brief.md` | `brief_version` (frontmatter) | `2.2` | | `research/*.md` | (implicit; tracked via `type: trekresearch-brief`) | unversioned | -| `plan.md` | `plan_version` (frontmatter) | `1.7` | +| `plan.md` | `plan_version` (plan header line) | `1.7` | | `progress.json` | `schema_version` (top-level) | `"1"` | | `review.md` | `review_version` (frontmatter) | `1.0` | | `.session-state.local.json` | `schema_version` (top-level) | `1` (number) | @@ -203,9 +203,18 @@ The validator (`lib/validators/architecture-discovery.mjs`) is intentionally min **Frontmatter schema:** -| Field | Type | Required | Allowed | -|---|---|---|---| -| `plan_version` | string | yes | `"1.7"` (current) | +`plan.md` has **no required frontmatter**. The only frontmatter block, +`source_findings` (optional), appears for `type: trekreview` plans — see +Handover 6. Brief-generated plans carry no frontmatter at all. + +| Field | Type | Required | Location | Allowed | +|---|---|---|---|---| +| `plan_version` | string | yes | plan header line, not frontmatter | `"1.7"` (current) | + +`plan_version` is emitted in the plan's "Generated by" header metadata line +(`> Generated by … — `plan_version: 1.7``), parsed by `extractPlanVersion` +(`lib/parsers/plan-schema.mjs`) from either line-start or the backtick-wrapped +prose form (relaxed in S26 so the template's prose emission parses). **Body invariants (strict, v1.7):** @@ -261,7 +270,7 @@ The strongest validation in the entire pipeline. Phase 5.5 (planning-orchestrato | `schema_version` | string | yes | `"1"` (current) | | `plan` | string | yes | Path to the plan being executed | | `plan_type` | string | optional | `plan \| session-spec` | -| `plan_version` | string | yes | Mirrors plan's frontmatter | +| `plan_version` | string | yes | Mirrors the plan header's plan_version | | `started_at` | ISO string | yes | | | `updated_at` | ISO string | yes | Bumped on every write | | `completed_at` | ISO string | optional | Set when status flips to completed | diff --git a/tests/lib/doc-consistency.test.mjs b/tests/lib/doc-consistency.test.mjs index bbfd555..4e1fcff 100644 --- a/tests/lib/doc-consistency.test.mjs +++ b/tests/lib/doc-consistency.test.mjs @@ -143,6 +143,20 @@ test('templates/plan-template.md declares plan_version: 1.7', () => { assert.match(tpl, /plan_version:\s*['"]?1\.7['"]?/); }); +test('HANDOVER-CONTRACTS.md locates plan_version in the plan header line, not frontmatter (S28)', () => { + // The plan template emits plan_version only in the prose "Generated by" + // header line; brief-generated plans carry no frontmatter at all (the sole + // frontmatter block, `source_findings`, is trekreview-only and additive). + // The contract must therefore not label plan_version a frontmatter field. + const t = read('docs/HANDOVER-CONTRACTS.md'); + assert.doesNotMatch(t, /`plan_version`\s*\(frontmatter\)/, + 'HANDOVER-CONTRACTS.md must not call plan_version a (frontmatter) field — it lives in the plan header line'); + assert.doesNotMatch(t, /Mirrors plan's frontmatter/, + "progress.json's plan_version mirrors the plan header line, not frontmatter"); + assert.match(t, /`plan_version`\s*\(plan header line\)/, + 'HANDOVER-CONTRACTS.md versioning table must locate plan_version in the plan header line'); +}); + // S19 prune (audit §Top changes #8): removed a tautological prose-pin — // "commands/trekexecute.md still parses v1.7 plan schema" was an OR-chain // (`includes('plan_version') || includes('Step N:') || includes('### Step ')`)