Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD across contents and paths, upstream package/repo coordinates -> @golem15/msd-core and golem15com/msd-core. Deep links into upstream history, sibling upstream packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is. Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line, package/plugin identity, regenerated lockfile, install-tree fixtures, derived registries and benchmark baseline; migration checksum baseline re-locked (MSD keeps its own install state, so no install had applied the old sums); sort-order and regex-escaped expectations in tests adjusted.
650 lines
27 KiB
JavaScript
650 lines
27 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Tests for the STATE.md field-extraction fallback-chain drift guard
|
|
* (epic #3180, issue #3187 Phase 5, ADR-3180 §7.7) —
|
|
* `scripts/lint-state-field-drift.cjs`.
|
|
*
|
|
* Per ADR-3180 Decision 4(b) the guard AND this paired test are both
|
|
* required; neither alone is sufficient. Every test here drives the
|
|
* guard's exported PURE functions (`findStateFieldDrift`, `buildFunctionInfo`,
|
|
* `scanRepo`) with string fixtures — no temp tree is needed for anything but
|
|
* `scanRepo`'s own tree-walk contract (D1), which runs against the real repo.
|
|
*
|
|
* Covers .msd/phase/refactor-3187-state-field-extraction-single-owner/
|
|
* 50-test-matrix.md section D, rows D1-D9 (D3b/D8b/D8c included), plus the
|
|
* multi-line-signature attribution regression the guard's own module header
|
|
* documents (`buildFunctionInfo`'s `pendingDeclName` deferral).
|
|
*
|
|
* Fixtures use array `.join('\n')`, never an indented template literal —
|
|
* indentation bleed would shift every asserted line number.
|
|
*/
|
|
|
|
const { test, describe } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const path = require('node:path');
|
|
const fs = require('node:fs');
|
|
|
|
const drift = require('../scripts/lint-state-field-drift.cjs');
|
|
const { sanitizeForReport } = require('../scripts/lib/drift-scan.cjs');
|
|
|
|
const REPO_ROOT = path.join(__dirname, '..');
|
|
const OWNER_RELPATH = drift.OWNER_FILE;
|
|
const OTHER_RELPATH = path.join('src', 'unrelated.cts');
|
|
|
|
// ─── D1: the real repo tree reports zero re-derivations ───────────────────
|
|
|
|
describe('D1 — scanRepo against the real repo tree', () => {
|
|
test('guard reports zero on the consolidated tree', () => {
|
|
// This is the guard's actual contract, exercised through its own
|
|
// scanRepo tree-walk (not a synthetic fixture) — the test that fails
|
|
// the day an eighth copy of the fallback chain lands anywhere in src/.
|
|
const violations = drift.scanRepo(REPO_ROOT);
|
|
assert.deepStrictEqual(violations, []);
|
|
});
|
|
});
|
|
|
|
// ─── D2: the guard MUST be able to fail ────────────────────────────────────
|
|
|
|
describe('D2 — the guard must be able to fail', () => {
|
|
test('guard catches a reintroduced ladder', () => {
|
|
const text = [
|
|
'function fmScalarLadder(fm, body) {',
|
|
' const v = fm.x;',
|
|
" if (typeof v === 'number' || typeof v === 'boolean') {",
|
|
' return String(v);',
|
|
' }',
|
|
" return stateExtractField(body, 'x');",
|
|
'}',
|
|
].join('\n');
|
|
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
// A guard that cannot fail is worse than no guard: this fixture is a
|
|
// synthetic re-derivation (coercion ladder + fallback call, same
|
|
// function), and it MUST produce exactly one violation at the exact
|
|
// line of the fallback call — not merely "an array".
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 6);
|
|
assert.match(out[0].found, /stateExtractField/);
|
|
});
|
|
});
|
|
|
|
// ─── D3 / D3b: body-only case-variant chain, with and without a ladder ────
|
|
|
|
describe('D3 — body-only case-variant chain without a ladder is not drift', () => {
|
|
test('body-only case-variant chain without a ladder is not drift', () => {
|
|
const text = [
|
|
'function noLadderFunction(body) {',
|
|
" const a = stateExtractField(body, 'Last Activity') ?? stateExtractField(body, 'Last activity');",
|
|
' return a;',
|
|
'}',
|
|
].join('\n');
|
|
|
|
// No ladder anywhere in this function — a bare case-variant fallback
|
|
// chain over the body alone is not the derivation this guard owns.
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.deepStrictEqual(out, []);
|
|
});
|
|
});
|
|
|
|
describe('D3b — case-variant chain beside a ladder is drift', () => {
|
|
test('case-variant chain beside a ladder is drift', () => {
|
|
const text = [
|
|
'function withLadderFunction(fm, body) {',
|
|
' const v = fm.status;',
|
|
" if (typeof v === 'number' || typeof v === 'boolean') {",
|
|
' // handled elsewhere in this same function',
|
|
' }',
|
|
" const a = stateExtractField(body, 'Last Activity') ?? stateExtractField(body, 'Last activity');",
|
|
' return a;',
|
|
'}',
|
|
].join('\n');
|
|
|
|
// The SAME case-variant chain, now inside a function that also bears
|
|
// the coercion ladder — the function must route through the owner
|
|
// instead of hand-rolling its own body-side fallback.
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 6);
|
|
});
|
|
});
|
|
|
|
// ─── D3c / D3d / D3e: evasion shapes found by isolated adversarial review ──
|
|
// A previous version of LADDER_RE required a bare-identifier `\1`
|
|
// backreference and hardcoded 'number' before 'boolean' on a single line.
|
|
// All three fixtures below were verified to produce ZERO violations against
|
|
// that version despite being real re-derivation shapes (ladder +
|
|
// `stateExtractField(` in the same function).
|
|
|
|
describe('D3c — member-expression / computed operand ladder is drift', () => {
|
|
test('a dotted member-expression operand (fm.key) is caught', () => {
|
|
const text = [
|
|
'function memberOperand(fm, body) {',
|
|
" if (typeof fm.key === 'number' || typeof fm.key === 'boolean') {",
|
|
' return String(fm.key);',
|
|
' }',
|
|
" return stateExtractField(body, 'x');",
|
|
'}',
|
|
].join('\n');
|
|
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 5);
|
|
});
|
|
|
|
test('a computed-access operand (fm[key]) is caught', () => {
|
|
const text = [
|
|
'function computedOperand(fm, body, key) {',
|
|
" if (typeof fm[key] === 'number' || typeof fm[key] === 'boolean') {",
|
|
' return String(fm[key]);',
|
|
' }',
|
|
" return stateExtractField(body, 'x');",
|
|
'}',
|
|
].join('\n');
|
|
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 5);
|
|
});
|
|
|
|
test('a control case: two DIFFERENT member operands never counts as one ladder', () => {
|
|
// fm.a and fm.b are different operands — this must never be mistaken
|
|
// for a coercion ladder over a single value.
|
|
const text = [
|
|
'function unrelatedOperands(fm, body) {',
|
|
" if (typeof fm.a === 'number' || typeof fm.b === 'boolean') {",
|
|
' return null;',
|
|
' }',
|
|
" return stateExtractField(body, 'x');",
|
|
'}',
|
|
].join('\n');
|
|
|
|
assert.deepStrictEqual(drift.findStateFieldDrift(text, OTHER_RELPATH), []);
|
|
});
|
|
});
|
|
|
|
describe('D3d — either tier order is drift (order swap)', () => {
|
|
test("'boolean' before 'number' (the opposite of the historical example) is caught", () => {
|
|
const text = [
|
|
'function orderSwap(fm, body) {',
|
|
' const v = fm.x;',
|
|
" if (typeof v === 'boolean' || typeof v === 'number') {",
|
|
' return String(v);',
|
|
' }',
|
|
" return stateExtractField(body, 'x');",
|
|
'}',
|
|
].join('\n');
|
|
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 6);
|
|
});
|
|
});
|
|
|
|
describe('D3e — a ladder split across lines, or as two separate `if` statements, is drift', () => {
|
|
test('the same `||` expression wrapped across two lines is caught', () => {
|
|
const text = [
|
|
'function splitAcrossLines(fm, body) {',
|
|
' const v = fm.x;',
|
|
" if (typeof v === 'number' ||",
|
|
" typeof v === 'boolean') {",
|
|
' return String(v);',
|
|
' }',
|
|
" return stateExtractField(body, 'x');",
|
|
'}',
|
|
].join('\n');
|
|
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 7);
|
|
});
|
|
|
|
test('two separate `if` statements (no `||` at all) are caught', () => {
|
|
const text = [
|
|
'function separateIfStatements(fm, body) {',
|
|
' const v = fm.x;',
|
|
" if (typeof v === 'number') { return String(v); }",
|
|
" if (typeof v === 'boolean') { return String(v); }",
|
|
" return stateExtractField(body, 'x');",
|
|
'}',
|
|
].join('\n');
|
|
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 5);
|
|
});
|
|
|
|
test('two separate `if` statements FURTHER apart than LADDER_WINDOW_LINES are NOT caught (documented bound)', () => {
|
|
// Proves LADDER_WINDOW_LINES is a real, enforced bound, not decorative —
|
|
// mirrors D8's "distance is irrelevant" proof for the OTHER (unbounded)
|
|
// pairing in this same guard, but for the ladder-clause pairing this is
|
|
// intentionally the opposite: bounded, and the bound is real.
|
|
const filler = [];
|
|
for (let i = 0; i < drift.LADDER_WINDOW_LINES + 5; i++) filler.push(` // filler line ${i}`);
|
|
const text = [
|
|
'function tooFarApart(fm, body) {',
|
|
' const v = fm.x;',
|
|
" if (typeof v === 'number') { return String(v); }",
|
|
...filler,
|
|
" if (typeof v === 'boolean') { return String(v); }",
|
|
" return stateExtractField(body, 'x');",
|
|
'}',
|
|
].join('\n');
|
|
|
|
assert.deepStrictEqual(drift.findStateFieldDrift(text, OTHER_RELPATH), []);
|
|
});
|
|
});
|
|
|
|
// ─── D4: nested frontmatter object read is not drift ───────────────────────
|
|
|
|
describe('D4 — nested frontmatter read is not drift', () => {
|
|
test('nested frontmatter read is not drift', () => {
|
|
const text = [
|
|
'function fmScalarKey(fm) {',
|
|
' const v = fm.nested ? fm.nested.key : undefined;',
|
|
" if (typeof v === 'number' || typeof v === 'boolean') {",
|
|
' return String(v);',
|
|
' }',
|
|
' return null;',
|
|
'}',
|
|
].join('\n');
|
|
|
|
// Ladder-bearing (the coercion ladder is present) but call-free — this
|
|
// is the live smart-entry.cts `fmScalarKey` control case proving the
|
|
// guard distinguishes "has a ladder" from "re-derives the fallback
|
|
// chain": a ladder alone with no stateExtractField( call in the same
|
|
// function is a different question and must stay silent.
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.deepStrictEqual(out, []);
|
|
});
|
|
});
|
|
|
|
// ─── D5: comment-awareness ──────────────────────────────────────────────────
|
|
|
|
describe('D5 — documented ladder in prose is not drift', () => {
|
|
test('documented ladder in prose is not drift', () => {
|
|
const text = [
|
|
'function commented(fm, body) {',
|
|
" // typeof v === 'number' || typeof v === 'boolean' then stateExtractField(body, 'x')",
|
|
' /*',
|
|
" * typeof v === 'number' || typeof v === 'boolean'",
|
|
" * stateExtractField(body, 'x')",
|
|
' */',
|
|
' return null;',
|
|
'}',
|
|
].join('\n');
|
|
|
|
// Both a `//` line comment and a `/* */` block comment carry the exact
|
|
// ladder + call text. ADR-3180 Amendment 3: a guard that reports prose
|
|
// as drift trains readers to reflexively exempt documentation, so
|
|
// scanCode must blank both before either detection regex runs.
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.deepStrictEqual(out, []);
|
|
});
|
|
});
|
|
|
|
// ─── D6 / D7: owner-file exemption is function-scoped, never file-scoped ──
|
|
|
|
describe('D6 — owner file is scanned; only named functions exempt', () => {
|
|
test('owner file is scanned; only named functions exempt', () => {
|
|
const text = [
|
|
'function stateFieldValue(fm, body, fmKey, bodyField) {',
|
|
' const v = fm[fmKey];',
|
|
" if (typeof v === 'number' || typeof v === 'boolean') {",
|
|
' return String(v);',
|
|
' }',
|
|
' return stateExtractField(body, bodyField);',
|
|
'}',
|
|
].join('\n');
|
|
|
|
// The canonical owner function itself IS the derivation by
|
|
// construction — it must stay silent when scanned under the real
|
|
// owner relPath.
|
|
const out = drift.findStateFieldDrift(text, OWNER_RELPATH);
|
|
assert.deepStrictEqual(out, []);
|
|
});
|
|
});
|
|
|
|
describe('D7 — a second copy inside the owner file is still caught', () => {
|
|
test('a second copy inside the owner file is still caught', () => {
|
|
// The exact Amendment-4 blind spot: a whole-file exemption on the
|
|
// owner is precisely how getMilestoneInfo stayed invisible to an
|
|
// earlier guard. FUNCTION_SCOPED_EXEMPTIONS must never let a SECOND,
|
|
// unrelated re-derivation elsewhere in the SAME owner file go unseen.
|
|
const text = [
|
|
'function stateFieldValue(fm, body, fmKey, bodyField) {',
|
|
' const v = fm[fmKey];',
|
|
" if (typeof v === 'number' || typeof v === 'boolean') {",
|
|
' return String(v);',
|
|
' }',
|
|
' return stateExtractField(body, bodyField);',
|
|
'}',
|
|
'',
|
|
'function stateFieldValueDuplicate(fm, body) {',
|
|
' const w = fm.other;',
|
|
" if (typeof w === 'number' || typeof w === 'boolean') {",
|
|
' return String(w);',
|
|
' }',
|
|
" return stateExtractField(body, 'other');",
|
|
'}',
|
|
].join('\n');
|
|
|
|
const out = drift.findStateFieldDrift(text, OWNER_RELPATH);
|
|
// The canonical stateFieldValue (lines 1-7) stays silent; the
|
|
// unrelated second copy (stateFieldValueDuplicate, lines 9-15) is
|
|
// flagged — proving the exemption is keyed on FUNCTION NAME, not on
|
|
// the whole file.
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 14);
|
|
});
|
|
});
|
|
|
|
describe('D6/D7 combined — the same line in a non-owner file is always reported', () => {
|
|
test('the same canonical-shaped line outside the owner file is reported (no exemption applies)', () => {
|
|
const text = [
|
|
'function stateFieldValue(fm, body, fmKey, bodyField) {',
|
|
' const v = fm[fmKey];',
|
|
" if (typeof v === 'number' || typeof v === 'boolean') {",
|
|
' return String(v);',
|
|
' }',
|
|
' return stateExtractField(body, bodyField);',
|
|
'}',
|
|
].join('\n');
|
|
|
|
// FUNCTION_SCOPED_EXEMPTIONS is keyed on relPath === OWNER_RELPATH; a
|
|
// function of the SAME name in a DIFFERENT file gets no exemption at
|
|
// all.
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 6);
|
|
});
|
|
});
|
|
|
|
// ─── D8 / D8b: distance and attribution are per-function, not a window ────
|
|
|
|
describe('D8 — distance within a function is irrelevant', () => {
|
|
test('distance within a function is irrelevant', () => {
|
|
const filler = [];
|
|
for (let i = 0; i < 40; i++) filler.push(` // filler line ${i} — no ladder or call text here`);
|
|
|
|
const text = [
|
|
'function farApart(fm, body) {',
|
|
' const v = fm.x;',
|
|
" if (typeof v === 'number' || typeof v === 'boolean') {",
|
|
' return String(v);',
|
|
' }',
|
|
...filler,
|
|
" return stateExtractField(body, 'x');",
|
|
'}',
|
|
].join('\n');
|
|
|
|
// No fixed line-distance window anywhere in this detection — an
|
|
// earlier draft used one and was rejected (ADR-3180 Decision 4(a)):
|
|
// it produced "a zero it did not earn" on live copies further from
|
|
// their ladder than any defensible line count. 40 filler lines here
|
|
// proves the current shape has no such ceiling.
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 5 + filler.length + 1);
|
|
});
|
|
});
|
|
|
|
describe('D8b — a neighbouring function is not implicated', () => {
|
|
test('a neighbouring function is not implicated', () => {
|
|
const text = [
|
|
'function ladderOwner(fm) {',
|
|
' const v = fm.x;',
|
|
" if (typeof v === 'number' || typeof v === 'boolean') {",
|
|
' return String(v);',
|
|
' }',
|
|
' return null;',
|
|
'}',
|
|
'',
|
|
'function neighbour(body) {',
|
|
" return stateExtractField(body, 'x');",
|
|
'}',
|
|
].join('\n');
|
|
|
|
// The file, taken as a whole, carries BOTH a ladder and a
|
|
// stateExtractField( call — but they sit in two DIFFERENT named
|
|
// functions with no shared enclosing scope. Attribution is
|
|
// per-function, not per-file, so neither function alone qualifies.
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.deepStrictEqual(out, []);
|
|
});
|
|
});
|
|
|
|
// ─── D8c: closure-scoped ladders (both real-world shapes) ─────────────────
|
|
|
|
describe('D8c — closure-scoped ladder is attributed correctly', () => {
|
|
test('an arrow-function closure (const fmScalar = (...) => {...}) is attributed to its enclosing function', () => {
|
|
const text = [
|
|
'function cmdStateSnapshotLike(fm, body) {',
|
|
' const fmScalar = (key) => {',
|
|
' const v = fm[key];',
|
|
" if (typeof v === 'number' || typeof v === 'boolean') return String(v);",
|
|
' return null;',
|
|
' };',
|
|
" const status = fmScalar('status') ?? stateExtractField(body, 'Status');",
|
|
' return status;',
|
|
'}',
|
|
].join('\n');
|
|
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 7);
|
|
|
|
// Attribution proof: the call line's innermost open frame is the
|
|
// OUTER named function, not the closure (which has already closed by
|
|
// that line) — and the ladder marks the outer frame transitively,
|
|
// because the closure's body is lexically part of the outer
|
|
// function's own body.
|
|
const lines = text.split('\n');
|
|
const { ladderBearing, innermostAt } = drift.buildFunctionInfo(lines);
|
|
assert.strictEqual(innermostAt[6], 'cmdStateSnapshotLike'); // 0-indexed line 7
|
|
assert.ok(ladderBearing.has('cmdStateSnapshotLike'));
|
|
});
|
|
|
|
test('a nested function-declaration closure (function fmScalar(...) {...}) is attributed to its enclosing function', () => {
|
|
const text = [
|
|
'function outerWithNestedFn(fm, body) {',
|
|
' function fmScalarNested(key) {',
|
|
' const v = fm[key];',
|
|
" if (typeof v === 'number' || typeof v === 'boolean') return String(v);",
|
|
' return null;',
|
|
' }',
|
|
" const status = fmScalarNested('status') ?? stateExtractField(body, 'Status');",
|
|
' return status;',
|
|
'}',
|
|
].join('\n');
|
|
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 7);
|
|
|
|
const lines = text.split('\n');
|
|
const { ladderBearing, innermostAt } = drift.buildFunctionInfo(lines);
|
|
assert.strictEqual(innermostAt[6], 'outerWithNestedFn'); // 0-indexed line 7
|
|
assert.ok(ladderBearing.has('outerWithNestedFn'));
|
|
});
|
|
});
|
|
|
|
// ─── Regression: multi-line function signature must not strand its frame ──
|
|
// buildFunctionInfo's module-header documents a real bug: FUNCTION_DECL_RE
|
|
// can match a line with no `{` at all (a signature spilling across several
|
|
// lines); pushing that frame immediately recorded `openDepth` at the
|
|
// ENCLOSING scope's depth rather than the function's own, so the frame could
|
|
// NEVER pop and silently misattributed every later top-level line to it.
|
|
// `pendingDeclName` defers the push to the line whose brace count actually
|
|
// increases. 229 functions across 76 files share this multi-line-signature
|
|
// shape, so this proof is load-bearing, not decorative.
|
|
|
|
describe('regression — a multi-line function signature does not strand its frame', () => {
|
|
test('a ladder+call inside a function whose signature spans several lines is flagged and attributed there; a later top-level call is not', () => {
|
|
const text = [
|
|
'function preferNewerLike(',
|
|
' fm,',
|
|
' body,',
|
|
' fmKey',
|
|
')',
|
|
'{',
|
|
' const v = fm[fmKey];',
|
|
" if (typeof v === 'number' || typeof v === 'boolean') {",
|
|
' return String(v);',
|
|
' }',
|
|
' return stateExtractField(body, fmKey);',
|
|
'}',
|
|
'',
|
|
"stateExtractField(body, 'decoy');",
|
|
].join('\n');
|
|
|
|
const out = drift.findStateFieldDrift(text, OTHER_RELPATH);
|
|
// Only the in-function call (line 11) is flagged. If the frame had
|
|
// been stranded open (the pre-fix bug), the decoy top-level call on
|
|
// line 14 would ALSO be flagged — this assertion is the whole proof
|
|
// the fix holds.
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 11);
|
|
|
|
const lines = text.split('\n');
|
|
const { innermostAt } = drift.buildFunctionInfo(lines);
|
|
assert.strictEqual(innermostAt[10], 'preferNewerLike'); // 0-indexed line 11
|
|
assert.strictEqual(innermostAt[13], null); // 0-indexed line 14 — module scope, unattributed
|
|
});
|
|
});
|
|
|
|
// ─── D9: report-output sanitization ────────────────────────────────────────
|
|
// `main()` sanitizes both a violation's file path and its matched fragment
|
|
// through `sanitizeForReport` (scripts/lib/drift-scan.cjs) before writing
|
|
// either to stderr — the exact boundary a fork-authored filename or source
|
|
// line reaches a CI log through. Direct coverage of the sanitizer itself,
|
|
// mirroring tests/plan-count-single-owner.test.cjs's `sanitizeForReport`
|
|
// describe block.
|
|
|
|
describe('D9 — report output is sanitized', () => {
|
|
test('C0 control bytes are escaped', () => {
|
|
assert.strictEqual(sanitizeForReport(String.fromCharCode(0x1b)), '\\x1b');
|
|
});
|
|
|
|
test('DEL (0x7f) is escaped', () => {
|
|
assert.strictEqual(sanitizeForReport(String.fromCharCode(0x7f)), '\\x7f');
|
|
});
|
|
|
|
test('C1 control bytes are escaped', () => {
|
|
assert.strictEqual(sanitizeForReport(String.fromCharCode(0x9b)), '\\x9b');
|
|
});
|
|
|
|
test('zero-width space (U+200B) is escaped', () => {
|
|
assert.strictEqual(sanitizeForReport(String.fromCharCode(0x200b)), '\\u200b');
|
|
});
|
|
|
|
test('Unicode LINE SEPARATOR (U+2028) is escaped', () => {
|
|
assert.strictEqual(sanitizeForReport(String.fromCharCode(0x2028)), '\\u2028');
|
|
});
|
|
|
|
test('Unicode PARAGRAPH SEPARATOR (U+2029) is escaped', () => {
|
|
assert.strictEqual(sanitizeForReport(String.fromCharCode(0x2029)), '\\u2029');
|
|
});
|
|
|
|
test('a bidi RIGHT-TO-LEFT OVERRIDE (U+202E) is escaped', () => {
|
|
assert.strictEqual(sanitizeForReport(String.fromCharCode(0x202e)), '\\u202e');
|
|
});
|
|
|
|
test('ordinary ASCII filename/fragment text passes through unchanged', () => {
|
|
const text = "src/state-document.cts:296 stateExtractField(body, 'x')";
|
|
assert.strictEqual(sanitizeForReport(text), text);
|
|
});
|
|
});
|
|
|
|
// ─── D10: prompt-layer prose detection (ADR-3180 Decision 4(d)) ───────────
|
|
// Widening the scan surface from `src/` alone to also cover the prompt-layer
|
|
// markdown (`msd-core/workflows`, `commands`, `agents`, `skills`) is unproven
|
|
// unless (a) the surface is genuinely declared, and (b) a NEW prose
|
|
// re-derivation added there is genuinely caught — not merely that the guard
|
|
// happens to report zero because nothing was ever exercised against it.
|
|
|
|
describe('D10a — the prompt layer is in the scan surface', () => {
|
|
test('SCAN_DIRS covers msd-core/workflows, commands, agents, skills (in addition to src)', () => {
|
|
assert.ok(drift.SCAN_DIRS.includes('src'));
|
|
assert.ok(drift.SCAN_DIRS.includes('msd-core/workflows'));
|
|
assert.ok(drift.SCAN_DIRS.includes('commands'));
|
|
assert.ok(drift.SCAN_DIRS.includes('agents'));
|
|
assert.ok(drift.SCAN_DIRS.includes('skills'));
|
|
});
|
|
|
|
test('SCAN_EXT covers markdown', () => {
|
|
assert.ok(drift.SCAN_EXT.has('.md'));
|
|
});
|
|
});
|
|
|
|
describe('D10b — a NEW prose re-derivation in a workflow file is flagged', () => {
|
|
const OTHER_WORKFLOW_RELPATH = path.join('msd-core', 'workflows', 'unrelated-workflow.md');
|
|
|
|
test('a synthetic workflow line carrying the fm-key/body-bold tokens plus both words is caught', () => {
|
|
const text = [
|
|
'# Some Workflow',
|
|
'',
|
|
'1. Read STATE.md. Extract: `owner` (frontmatter `owner:` or body `**Owner:**`), and proceed.',
|
|
'',
|
|
].join('\n');
|
|
|
|
const out = drift.findPromptFieldDrift(text, OTHER_WORKFLOW_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
assert.strictEqual(out[0].line, 3);
|
|
assert.match(out[0].found, /frontmatter/);
|
|
});
|
|
|
|
test('control case: the backtick tokens alone, with no bold body token, are not flagged', () => {
|
|
const text = '1. Read STATE.md. Extract: `owner` (frontmatter `owner:` or plain body Owner field), and proceed.';
|
|
const out = drift.findPromptFieldDrift(text, OTHER_WORKFLOW_RELPATH);
|
|
assert.deepStrictEqual(out, []);
|
|
});
|
|
|
|
test('control case: both tokens present but the word "frontmatter" absent is not flagged', () => {
|
|
const text = '1. Extract: `owner` (config `owner:` or body `**Owner:**`).';
|
|
const out = drift.findPromptFieldDrift(text, OTHER_WORKFLOW_RELPATH);
|
|
assert.deepStrictEqual(out, []);
|
|
});
|
|
|
|
test('control case: both tokens and "frontmatter" present but the word "body" absent is not flagged', () => {
|
|
const text = '1. Extract: `owner` (frontmatter `owner:` or fallback `**Owner:**`).';
|
|
const out = drift.findPromptFieldDrift(text, OTHER_WORKFLOW_RELPATH);
|
|
assert.deepStrictEqual(out, []);
|
|
});
|
|
});
|
|
|
|
describe('D10c — the smart-entry.md msd-tools-down fallback is a written, permanent exemption', () => {
|
|
const SMART_ENTRY_RELPATH = path.join('msd-core', 'workflows', 'smart-entry.md');
|
|
const LIVE_LINE =
|
|
"- Read `.planning/STATE.md` (frontmatter + body) with the Read tool. Extract: `status` (frontmatter `status:` or body `**Status:**`), `Phase:` from the body, `total_phases`/`percent` from a nested `progress:` frontmatter object if present, and any `## Blockers` items.";
|
|
|
|
test('the real live line is caught when NOT under the exempted relPath (proves detection genuinely fires on this exact text — it is not silent by construction)', () => {
|
|
const out = drift.findPromptFieldDrift(LIVE_LINE, path.join('msd-core', 'workflows', 'unrelated-workflow.md'));
|
|
assert.strictEqual(out.length, 1);
|
|
});
|
|
|
|
test('the SAME line under the real smart-entry.md relPath is exempted (silent), per the written PROMPT_LAYER_EXEMPTIONS reason', () => {
|
|
const out = drift.findPromptFieldDrift(LIVE_LINE, SMART_ENTRY_RELPATH);
|
|
assert.deepStrictEqual(out, []);
|
|
// The exemption carries a written reason (Decision 4(d)) — not a bare
|
|
// suppression — and is NOT a Decision 4(e) ratchet: this site cannot be
|
|
// migrated onto the owner by construction, so there is no removal issue
|
|
// to key it against.
|
|
const reasons = drift.PROMPT_LAYER_EXEMPTIONS.get(SMART_ENTRY_RELPATH);
|
|
assert.ok(reasons.has(LIVE_LINE.trim()));
|
|
assert.match(reasons.get(LIVE_LINE.trim()), /msd-tools|cannot run|by construction/i);
|
|
});
|
|
|
|
test('the exemption is keyed on exact trimmed text, not merely the file — a DIFFERENT re-derivation in the SAME file is still caught', () => {
|
|
const text = '- Extract: `owner` (frontmatter `owner:` or body `**Owner:**`).';
|
|
const out = drift.findPromptFieldDrift(text, SMART_ENTRY_RELPATH);
|
|
assert.strictEqual(out.length, 1);
|
|
});
|
|
|
|
test('the real smart-entry.md file on disk scans clean end-to-end (the actual contract, not just the extracted fixture)', () => {
|
|
const content = fs.readFileSync(path.join(REPO_ROOT, 'msd-core', 'workflows', 'smart-entry.md'), 'utf8');
|
|
const out = drift.findPromptFieldDrift(content, SMART_ENTRY_RELPATH);
|
|
assert.deepStrictEqual(out, []);
|
|
});
|
|
});
|