fix(plan): a 1.9 Test first names its test only on a line that opens with File:
The step check read File: anywhere on the Test first lines, so a 1.9 plan whose only File: stood mid-line validated while the executor, K6 and the red proof read no test file. From 1.9 checkSteps reads the test the way testFirstFiles does; below 1.9 it reads as before (decision 16). Found by the step 5 controller in /trekexecute --session 2; fixed on the operator's decision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
64e6771c8b
commit
3009c5eddb
3 changed files with 61 additions and 8 deletions
|
|
@ -55,7 +55,7 @@ import {
|
|||
parseStepFields, findSection, splitSections, findModuleCycle, parseMermaid, fencedBlocks,
|
||||
versionAtLeast, PLAN_VERSION_CURRENT, PLAN_VERSIONS_SUPPORTED, CEREMONY_VALUES,
|
||||
ON_FAILURE_ACTIONS, onFailureAction, stepTestsOnlyFromFields, entryPaths, untickedPaths,
|
||||
testFirstFileEntries,
|
||||
testFirstFileEntries, testFirstFiles,
|
||||
} from '../parsers/plan-schema.mjs';
|
||||
import { validateAllManifests } from '../parsers/manifest-yaml.mjs';
|
||||
import { parseDocument } from '../util/frontmatter.mjs';
|
||||
|
|
@ -70,18 +70,29 @@ export const DESIGN_SUBSECTIONS = Object.freeze([
|
|||
]);
|
||||
const BACKTICKED = /`[^`\n]+`/;
|
||||
|
||||
function checkSteps(sections) {
|
||||
// From 1.9 the step names its test the way the executor reads it
|
||||
// (testFirstFiles): only through a line that opens with `File:` and its
|
||||
// backticked path, so a `File:` mid-line names no test. Below 1.9 the
|
||||
// unanchored test stands (decision 16: older plans are read as before).
|
||||
function checkSteps(sections, planVersion) {
|
||||
const out = [];
|
||||
let endToEnd = 0;
|
||||
const grammar = versionAtLeast(planVersion, '1.9');
|
||||
for (const s of sections) {
|
||||
const f = parseStepFields(s.body);
|
||||
const tf = f.get('Test first');
|
||||
const names = tf && /\bFile:\**[ \t]*`[^`\n]+`/i.test(tf.text) && /\bVerifies:\**[ \t]*\S/i.test(tf.text);
|
||||
const file = grammar
|
||||
? testFirstFiles(f, planVersion).length > 0
|
||||
: /\bFile:\**[ \t]*`[^`\n]+`/i.test(tf?.text ?? '');
|
||||
const names = tf && file && /\bVerifies:\**[ \t]*\S/i.test(tf.text);
|
||||
if (!names) {
|
||||
out.push(issue(
|
||||
'PLAN_STEP_NO_TEST_FIRST',
|
||||
`Step ${s.n}: ${tf ? '`Test first:` does not name a File (backticked path) and what it Verifies' : 'no `Test first:` field'}`,
|
||||
'plan_version 1.8 requires `Test first:` on every step, with `File:` and `Verifies:` sub-bullets.',
|
||||
grammar
|
||||
? 'plan_version 1.9 requires `Test first:` on every step, with a line that opens with `File:` and its ' +
|
||||
'backticked path, and a `Verifies:` line.'
|
||||
: 'plan_version 1.8 requires `Test first:` on every step, with `File:` and `Verifies:` sub-bullets.',
|
||||
));
|
||||
}
|
||||
for (const name of OPTIONAL_STEP_FIELDS) {
|
||||
|
|
@ -386,7 +397,7 @@ export function validatePlanContent(rawText, opts = {}) {
|
|||
if (stepsFrom !== null) {
|
||||
const appended = sections.filter((s) => Number(s.n) >= stepsFrom);
|
||||
const stepIssues = versionAtLeast(planVersion, '1.8') ? [] : [
|
||||
...checkSteps(appended).filter((i) => i.code !== 'PLAN_NO_END_TO_END'),
|
||||
...checkSteps(appended, planVersion).filter((i) => i.code !== 'PLAN_NO_END_TO_END'),
|
||||
...checkRunnable(text, appended, { cwd: opts.cwd, stepsOnly: true }),
|
||||
];
|
||||
for (const s of appended) {
|
||||
|
|
@ -401,7 +412,7 @@ export function validatePlanContent(rawText, opts = {}) {
|
|||
|
||||
const ceremony = extractPlanCeremony(text);
|
||||
if (versionAtLeast(planVersion, '1.8')) {
|
||||
const v18 = [...checkSteps(sections), ...checkRunnable(text, sections, { cwd: opts.cwd })];
|
||||
const v18 = [...checkSteps(sections, planVersion), ...checkRunnable(text, sections, { cwd: opts.cwd })];
|
||||
if (!ceremony.valid) {
|
||||
v18.push(issue('PLAN_CEREMONY_INVALID', `ceremony=${ceremony.raw}`,
|
||||
`Allowed: ${CEREMONY_VALUES.join(' | ')} (absent = architectural).`));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue