docs(changelog): plan_version 1.9 and the trial-4 findings, with door lines

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-12 20:40:33 +02:00
commit acfd2eb9b6
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
2 changed files with 136 additions and 0 deletions

View file

@ -4,6 +4,95 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## Unreleased — plan_version 1.9, the trial-4 findings, session-clean tests
A new plan version, 1.9, in which every path in a step's `Files:` and on its
`Test first:` `File:` line stands first, in backticks, so the reader no longer
guesses paths from prose (decision 16). The same delivery closes the six
findings from the test repo's trial 4 and stops `npm test` from pointing a live
session's stop guard at a temp-repo lock. Every fix began with a test that
failed on the code before it. Suite 2381 (2379 pass, 0 fail, 2 skipped),
measured by `npm test` on Step 13's commit (`acd0e3d`), the commit before this
section.
Nothing here asks anything of users. Older plans warn, they do not fail: a plan
below 1.9 is read as in 6.3.1, apart from the fixes under Fixed, and gets one
warning per step whose `Files:` names a path that is not backticked.
### Doors
- two-way: plan_version 1.9 — a 1.9 plan read by 6.3.1 gets PLAN_VERSION_MISMATCH as a warning and the 6.3.1 reader, so a revert rewrites no plan.
- two-way: Files reader (three 6.3.0 answers restored) — a revert brings back 6.3.1's answers; no file format changes.
- two-way: every test file on the Test first File line — a revert reads the first file only again.
- two-way: brief-conflict mark on review findings — an older verdict gate ignores the field.
- two-way: approval commits require a Co-Authored-By trailer — only Voyage's own commands call the CLI, and they pass it.
- two-way: explorer prompts, stamp order, test env — prose and test-side only.
### Added
- **plan_version 1.9: the file first on every line.** Each entry of `Files:`
and each `Test first:` `File:` line opens with its backticked path or paths;
everything from the first other text on is description and is never read.
A 1.9 plan that breaks this gets `PLAN_STEP_PATH_NOT_FIRST` or
`PLAN_STEP_PATH_NOT_A_PATH` (errors under `--strict`, warnings without). The
parser, the validator, the tests-only check, the plan-file sweep (K6) and red
proof read a 1.9 plan by this grammar. 1.9 is the current target: Handover
4, the plan template, `/trekplan`, the planning orchestrator and CLAUDE.md
say so, and the rules introduced at 1.8 say "1.8 or later".
- **A warning for older plans.** Below 1.9, a path in `Files:` that is not
backticked gets the warning `PLAN_STEP_PATH_UNTICKED`, under `--strict` too.
- **The brief-conflict mark.** A `/code-review` finding at the MAJOR tier whose
recommended action goes against a brief Non-Goal or another stated choice
drops to MINOR, marked `contradicts_brief` and `brief_lowered_from: MAJOR`;
any other engine finding only gets the mark, and a BLOCKER is never lowered.
`/trekreview` passes the brief's choices to the coordinator, which writes the
mark into `review.md`; the opt-in workflow review applies the same rule and
floors an illegal lowering. On an ALLOW or WARN review the verdict gate
answers exit 2 for a finding lowered or marked outside that one shape:
`brief_lowered_from` on any other finding, a mark on a reviewer finding, a
mark or an engine finding below its catalogue tier, or a mark with a missing
or unknown rule key.
### Fixed
- **The suite no longer hands the session id to the lock CLI.** `npm test`
preloads a helper that removes `CLAUDE_CODE_SESSION_ID`, and every test that
starts a child process passes an environment without it. A live session's
stop guard is no longer pointed at a temp-repo lock.
- **The three `Files:` regressions from 6.3.1's Known issues** give 6.3.0's
answer again: a source file after a described test is kept, a path that a
description only mentions is not read as a file, and a backticked bare name
such as `LICENSE` alone on a following line is read as a file.
- **Every test file on the `Test first:` `File:` line** is read, locked and
copied for red proof, not only the first.
- **A 1.9 `Test first:` names its test only on a line that opens with
`File:`**, the way the executor, K6 and red proof read it. Below 1.9 the
check reads as before.
- **Engine provenance in the review.** Ingest takes `engine` from the payload,
never from a field a reviewer wrote on its own finding, and on a severity tie
the reviewer-agent finding is kept over the engine finding, in the
coordinator's Pass 1 and in the workflow dedup. A brief-conflict mark can no
longer lower a reviewer's MAJOR (operator decision 2026-10-12).
- **Explorers and reviewers stay in the repo.** `/trekplan` tells its
brief-reviewer, the eight Phase 5 explorers, the Phase 6 deep-dives, the
plan-critic and the scope-guardian to read local files only inside the repo
under exploration plus the paths their prompt gives, and every explorer gets
the brief.
- **The brief review stamp covers only text the reviewer saw.** For a `small`
brief, the reviewer's gaps are written after the stamp, so the stamp reads
stale and `/trekplan` reviews the brief once more.
- **Approval commits carry the caller's Co-Authored-By trailer.**
`artifact-commit.mjs` takes `--trailer` and refuses (exit 1, before anything
is staged or committed) a missing, multi-line or malformed trailer, an unfilled `{model}`, and
one that names a session link. `/trekbrief`, `/trekplan` and `/trekexecute`
pass it; `/trekexecute` prints the reason and goes on when the approval is
logged but not committed.
### Known issues
- The session split reads a 1.9 plan as an older plan (operator answer 3).
- Marked MINOR findings still feed `recurrence` / project-memory proposals.
## v6.3.1 — 2026-10-11 — Fixes from the release review of 6.3.0, and two verdict-gate holes closed
Rounds 7 to 10 of the release review of 6.3.0. Round 7 is the WARN 6.3.0 was

View file

@ -139,3 +139,50 @@ test('the template\'s own example steps validate as 1.9 with none of the new pat
const soft = validatePlanContent(read('templates/plan-template.md'), { strict: false });
assert.deepEqual([...soft.errors, ...soft.warnings].map((i) => i.code).filter((c) => NEW_CODES.includes(c)), []);
});
// Step 14: the CHANGELOG's top section is the Unreleased 1.9 delivery, with door
// lines the release check accepts. The release check is imported as a module
// namespace, so a missing parseDoors export fails an assertion, not the load.
const releaseCheck = await import('../../scripts/release-check.mjs');
const UNRELEASED = '## Unreleased — plan_version 1.9, the trial-4 findings, session-clean tests';
function firstChangelogSection() {
const lines = read('CHANGELOG.md').split('\n');
const start = lines.findIndex((l) => /^## /.test(l));
assert.ok(start >= 0, 'CHANGELOG.md has no "## " section');
let end = lines.length;
for (let i = start + 1; i < lines.length; i++) {
if (/^## /.test(lines[i])) { end = i; break; }
}
return { heading: lines[start], body: lines.slice(start + 1, end).join('\n') };
}
test('CHANGELOG: the first "## " section is the Unreleased plan_version 1.9 section', () => {
assert.equal(firstChangelogSection().heading, UNRELEASED);
});
test('CHANGELOG: parseDoors reads the Unreleased section\'s door lines, none bad, one naming plan_version 1.9', () => {
assert.equal(typeof releaseCheck.parseDoors, 'function', 'scripts/release-check.mjs exports parseDoors');
const { heading, body } = firstChangelogSection();
assert.equal(heading, UNRELEASED);
const d = releaseCheck.parseDoors(body);
assert.equal(d.found, true, 'the Unreleased section has a "### Doors" heading');
assert.ok(d.entries.length > 0, 'the Doors list has door lines');
assert.deepEqual(d.bad, [], 'every top-level door line reads "- one-way: ..." or "- two-way: ..."');
assert.ok(d.entries.some((e) => e.text.includes('plan_version 1.9')),
`one door line names plan_version 1.9: ${JSON.stringify(d.entries.map((e) => e.text))}`);
});
test('CHANGELOG: the Unreleased section\'s Known issues name the session split', () => {
const { heading, body } = firstChangelogSection();
assert.equal(heading, UNRELEASED);
const lines = body.split('\n');
const start = lines.findIndex((l) => /^### Known issues\s*$/.test(l));
assert.ok(start >= 0, 'the Unreleased section has a "### Known issues" heading');
let end = lines.length;
for (let i = start + 1; i < lines.length; i++) {
if (/^###? /.test(lines[i])) { end = i; break; }
}
const known = lines.slice(start + 1, end).join('\n');
assert.match(known, /session split/, `Known issues name the session split: ${JSON.stringify(known)}`);
});