refactor(plan): K6 keeps its own normaliser and LIST_ITEM is a literal again

Operator decision 27 (13.10); review finding 7961a700.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-14 10:19:56 +02:00
commit 1786df999e
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
3 changed files with 16 additions and 8 deletions

View file

@ -373,10 +373,7 @@ const BUILD_FILE = /^(?!Profile$)(?:[A-Z][a-z]+|GNUmake)file$|^(?:make|rake|just
// Where an entry's description starts, and the words that start a list item.
const DESCRIPTION_DASH = /^(?:\u2014|\u2013|-|--)$/;
const LIST_JOINER = /^(?:and|or|\+|&)$/i;
// The list markers a plan line may open with; LIST_ITEM and the 1.9 `File:`
// line are both built from it.
const LIST_MARKER = String.raw`(?:[-*+]|\d+[.)])`;
const LIST_ITEM = new RegExp(String.raw`^\s*${LIST_MARKER}\s+`);
const LIST_ITEM = /^\s*(?:[-*+]|\d+[.)])\s+/;
const lastSegment = (p) => p.replace(/\/$/, '').split('/').pop();
/** A plan path as the readers hold it: `/` separators, no leading `./`. */
export const normPlanPath = (p) => p.replace(/\\/g, '/').replace(/^\.\//, '');
@ -629,8 +626,8 @@ export function stepTestsOnly(body, version) {
// selector); global, so read it with matchAll only: one match per `File:`.
const TEST_FIRST_FILE_LINE = /\bFile:\**[ \t]*(?=`)/gi;
// From plan_version 1.9: a line that opens with `File:` (after an optional
// list marker (LIST_MARKER) and `**`), backticked or not.
const TEST_FIRST_FILE_LINE_19 = new RegExp(String.raw`^\s*(?:${LIST_MARKER}\s+)?(?:\*\*)?File:(?:\*\*)?`, 'i');
// list marker of the set LIST_ITEM accepts, and `**`), backticked or not.
const TEST_FIRST_FILE_LINE_19 = /^\s*(?:(?:[-*+]|\d+[.)])\s+)?(?:\*\*)?File:(?:\*\*)?/i;
/**
* plan_version 1.9: the rest of each `Test first:` `File:` line (after `File:`

View file

@ -38,7 +38,7 @@
import { readFileSync, existsSync, realpathSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { resolve, relative, dirname, isAbsolute, sep } from 'node:path';
import { sliceSteps, parseStepFields, entryPaths, testFirstFiles, extractPlanVersion, usesFilesGrammar, normPlanPath as norm } from '../parsers/plan-schema.mjs';
import { sliceSteps, parseStepFields, entryPaths, testFirstFiles, extractPlanVersion, usesFilesGrammar } from '../parsers/plan-schema.mjs';
import { parseManifest } from '../parsers/manifest-yaml.mjs';
import { isMain } from '../util/is-main.mjs';
@ -60,6 +60,8 @@ function pathsIn(text) {
return out;
}
const norm = p => p.replace(/\\/g, '/').replace(/^\.\//, '');
/** Every path the plan names: Files, Test first File, manifest expected_paths. */
export function plannedPaths(planText) {
const out = new Set();

View file

@ -15,7 +15,7 @@ import { buildPlan } from '../helpers/plan-v18-builder.mjs';
import { plannedPaths } from '../../lib/verification/plan-scope-sweep.mjs';
import { childEnv } from '../helpers/child-env.mjs';
import { spawnSync } from 'node:child_process';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, readFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
@ -200,6 +200,15 @@ test('below 1.9: K6 plans what it planned before - the same input plans both pat
assert.deepEqual(plannedPaths(tf), ['src/mod2.mjs', 'src/parse.mjs', 'tests/a.test.mjs', 'tests/mod2.test.mjs']);
});
test('decision 27 — K6 keeps its own norm and LIST_ITEM is a literal', () => {
const sweep = readFileSync(join(ROOT, 'lib', 'verification', 'plan-scope-sweep.mjs'), 'utf8');
assert.doesNotMatch(sweep, /\bnormPlanPath\b/, 'K6 does not import the parser\'s normaliser');
assert.ok(sweep.includes("const norm = p => p.replace(/\\\\/g, '/').replace(/^\\.\\//, '');"), 'K6 keeps its own norm line');
const parser = readFileSync(join(ROOT, 'lib', 'parsers', 'plan-schema.mjs'), 'utf8');
assert.doesNotMatch(parser, /\bLIST_MARKER\b/, 'LIST_ITEM is not built from LIST_MARKER');
assert.ok(parser.includes('const LIST_ITEM = /^\\s*(?:[-*+]|\\d+[.)])\\s+/;'), 'LIST_ITEM is the literal');
});
test('1.9: the red proof\'s tests_only reads Files by the grammar (CLI, temp git repo); 1.8 reads it as before', () => {
const repo = mkdtempSync(join(tmpdir(), 'plan19-redproof-'));
const git = (...args) => spawnSync('git', args, { cwd: repo, encoding: 'utf8', env: childEnv() });