Files
msd-core/tests/feat-3881-yaml-parser-consequences.test.cjs
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
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.
2026-10-06 01:47:40 +02:00

884 lines
40 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ADR-3473 §8.1 (#3881) — consequence and boundary coverage for the js-yaml migration.
// See .msd/phase/feat-3881-one-yaml-parser/50-test-matrix.md sections A and F. Each row
// pins a consequence of swapping the hand-rolled line scanner for the vendored js-yaml
// (§40-design.md §0.2) that is otherwise invisible to the existing suite.
'use strict';
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const {
extractFrontmatter,
reconstructFrontmatter,
spliceFrontmatter,
UNTERMINATED_KEY_THRESHOLD,
FRONTMATTER_UNPARSEABLE,
} = require('../msd-core/bin/lib/frontmatter.cjs');
const { transitionCore } = require('../msd-core/bin/lib/state-transition.cjs');
const {
_resetUnusableInputWarningsForTests,
_unusableInputEmissionCountForTests,
} = require('../msd-core/bin/lib/unusable-input.cjs');
const { createTempDir, cleanup, runMsdTools, createTempProject } = require('./helpers.cjs');
const { runNode } = require('./helpers/process-seam.cjs');
const { throwIfFailed } = require('./helpers/git-fixture.cjs');
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
const TOOLS_PATH = path.join(__dirname, '..', 'msd-core', 'bin', 'msd-tools.cjs');
const fixedClock = Object.freeze({
today: () => '2026-06-27',
localToday: () => '2026-06-27',
nowIso: () => '2026-06-27T12:00:00.000Z',
});
function withTempDir(fn) {
const dir = createTempDir('feat-3881-consequences-');
try {
return fn(dir);
} finally {
cleanup(dir);
}
}
// ─── A. Consequences ────────────────────────────────────────────────────────
describe('A1 emptyValuedKeySurvivesAWrite', () => {
test('a key with no value round-trips through parse -> reconstruct -> re-parse with the key still present', () => {
const doc = '---\nphase: 3\nprogress:\n---\n\nbody\n';
const parsed = extractFrontmatter(doc);
assert.ok(
Object.prototype.hasOwnProperty.call(parsed, 'progress'),
'an empty-valued key must survive the initial parse'
);
// reconstructFrontmatter omits null-valued keys (frontmatter.cjs: `if (value === null ...) continue`),
// so the empty-value contract only survives a write if extractFrontmatter never hands one back —
// this is what pins that guarantee rather than reconstructFrontmatter's own omission logic.
const reconstructed = reconstructFrontmatter(parsed);
const rewritten = `---\n${reconstructed}\n---\n\nbody\n`;
const reparsed = extractFrontmatter(rewritten);
assert.ok(
Object.prototype.hasOwnProperty.call(reparsed, 'progress'),
`progress must survive a write; reconstructed frontmatter was ${JSON.stringify(reconstructed)}`
);
});
});
describe('A2 unparseableDocumentKeepsItsFrontmatterBlock', () => {
test('a STATE.md with a git merge-conflict marker in its frontmatter keeps the block through beginPhase', () => {
const fmBlock = [
'---',
'<<<<<<< HEAD',
'status: foo',
'=======',
'status: bar',
'>>>>>>> feature',
'---',
'',
].join('\n');
const body = [
'# Project State',
'',
'**Status:** Planning',
'',
'## Current Position',
'',
'Phase: 2 — DONE',
'Plan: —',
'Status: Planning',
'',
].join('\n');
const content = fmBlock + body;
// Verify reachability first: the conflicted region parses to zero keys with the
// unparseable marker set, exercising the exact branch beginPhaseCore relies on.
const fm = extractFrontmatter(content);
assert.equal(Object.keys(fm).length, 0);
assert.equal(fm[FRONTMATTER_UNPARSEABLE], true);
const result = transitionCore(
content,
{ kind: 'beginPhase', phaseNumber: 3, phaseName: 'Test Phase', planCount: 5 },
{ clock: fixedClock }
);
assert.ok(
result.content.includes('<<<<<<< HEAD') &&
result.content.includes('=======') &&
result.content.includes('>>>>>>> feature'),
`frontmatter conflict markers must survive the write; got ${JSON.stringify(result.content)}`
);
});
// Post-#3881-review, finding 7: this describe block exercised only ONE of the 8 call sites
// that route through `beginFrontmatterReassembly` (frontmatter.cts's docblock names all 8:
// 7 `*Core` functions in state-transition.cts, dispatched by `transitionCore`, plus 1 more
// hand-verified separately in `state.cts`'s `cmdStateCompletePhase`). Table-driven over the
// remaining 6 `transitionCore` kinds that share the same preservation contract.
const OTHER_TRANSITION_KINDS = [
['advancePlan', { kind: 'advancePlan' }],
['completePhase', { kind: 'completePhase', phaseNum: '2', nextPhaseNum: '3', nextPhaseName: 'Next Phase', isLastPhase: false, planCount: 1, summaryCount: 1 }],
['plannedPhase', { kind: 'plannedPhase', phaseNumber: 3, phaseName: 'Test Phase', planCount: 5 }],
['milestoneComplete', { kind: 'milestoneComplete', version: 'v1.0', nextMilestoneCommand: '/msd:new-milestone' }],
['patch', { kind: 'patch', patches: { Status: 'Paused' } }],
['update', { kind: 'update', field: 'Status', value: 'Paused' }],
];
const fmBlock = [
'---',
'<<<<<<< HEAD',
'status: foo',
'=======',
'status: bar',
'>>>>>>> feature',
'---',
'',
].join('\n');
const body = [
'# Project State',
'',
'**Status:** Planning',
'',
'## Current Position',
'',
'Phase: 2 — DONE',
'Plan: —',
'Status: Planning',
'',
].join('\n');
const content = fmBlock + body;
for (const [label, intent] of OTHER_TRANSITION_KINDS) {
test(`a git merge-conflict marker in the frontmatter keeps the block through ${label}`, () => {
const result = transitionCore(content, intent, { clock: fixedClock, roadmapProvider: () => null });
assert.ok(
result.content.includes('<<<<<<< HEAD')
&& result.content.includes('=======')
&& result.content.includes('>>>>>>> feature'),
`${label}: frontmatter conflict markers must survive the write; got ${JSON.stringify(result.content)}`
);
});
}
});
// ─── A2b. The 8th reassemble site — the real CLI path, not just the pure transform ─────────
//
// Post-#3881-review, second round: the 7 `transitionCore` kinds above preserve an unparseable
// block at the PURE-TRANSFORM layer, but `state.cts`'s CLI adapters wrap every transform in
// `readModifyWriteStateMd` -> `syncAndPreserveStateMd`, which reruns `extractFrontmatter` on
// the (already-preserved) result and — before this fix — unconditionally re-derived a FRESH
// frontmatter block, discarding the raw one a second time. Confirmed by execution: BEFORE the
// fix, `state complete-phase` on a conflict-marker STATE.md returned success with the markers
// GONE, replaced by a freshly-derived well-formed block (re-derivation, not fence deletion —
// case (b), not (a)). Table-driven over the CLI verbs found to share the same
// `readModifyWriteStateMd` path, plus a control proving the ADR-3408 §8.3 CLOSED-list "body
// wins" contract (`state sync`) is untouched.
describe('A2b unparseableFrontmatterSurvivesTheRealCliPath', () => {
const CONFLICT_FM_BLOCK = [
'---',
'<<<<<<< HEAD',
'status: foo',
'=======',
'status: bar',
'>>>>>>> feature',
'---',
'',
].join('\n');
const CONFLICT_BODY = [
'# Project State',
'',
'## Current Position',
'',
'Phase: 1 — Foundation',
'Plan: 1 of 1',
'Status: Executing Phase 1',
'Last activity: 2026-07-01 — mid-flight',
'',
].join('\n');
function writeConflictFixture(tmpDir) {
const planningDir = path.join(tmpDir, '.planning');
fs.mkdirSync(path.join(planningDir, 'phases', '01-foundation'), { recursive: true });
fs.writeFileSync(
path.join(planningDir, 'ROADMAP.md'),
['# Roadmap', '', '### Phase 1: Foundation', '**Goal:** Setup', ''].join('\n'),
);
const statePath = path.join(planningDir, 'STATE.md');
fs.writeFileSync(statePath, CONFLICT_FM_BLOCK + CONFLICT_BODY);
return statePath;
}
const NON_SANCTIONED_VERBS = [
['state complete-phase', ['state', 'complete-phase']],
['state update', ['state', 'update', 'Last Activity', '2026-08-26']],
['query state.patch', ['query', 'state.patch', JSON.stringify({ Status: 'Paused for review' })]],
['state begin-phase', ['state', 'begin-phase', '--phase', '2', '--name', 'Next Phase']],
];
for (const [label, args] of NON_SANCTIONED_VERBS) {
test(`${label}: a git merge-conflict-marked frontmatter block survives the real CLI write`, () => {
const tmpDir = createTempProject();
try {
const statePath = writeConflictFixture(tmpDir);
const result = runMsdTools(args, tmpDir);
assert.ok(result.success, `${label} failed: ${result.error}`);
const after = fs.readFileSync(statePath, 'utf-8');
assert.ok(
after.includes('<<<<<<< HEAD') && after.includes('=======') && after.includes('>>>>>>> feature'),
`${label}: conflict markers must survive; got:\n${after}`,
);
} finally {
cleanup(tmpDir);
}
});
}
test('control: state sync (ADR-3408 §8.3 CLOSED list — body wins) still overwrites unparseable frontmatter, unchanged', () => {
// The one command that MUST keep clobbering it — a regression here would mean the fix
// widened the closed list, which the review explicitly forbids.
const tmpDir = createTempProject();
try {
const statePath = writeConflictFixture(tmpDir);
const result = runMsdTools(['state', 'sync'], tmpDir);
assert.ok(result.success, `state sync failed: ${result.error}`);
const after = fs.readFileSync(statePath, 'utf-8');
assert.ok(
!after.includes('<<<<<<< HEAD'),
'state sync must still re-derive frontmatter from the body (its documented contract) — conflict markers must NOT survive',
);
assert.ok(/^---\r?\n/.test(after), 'state sync must still produce a well-formed frontmatter block');
} finally {
cleanup(tmpDir);
}
});
});
// #3881 ADR-3473 §8.5: `state sync`'s "body wins" regeneration over an unparseable
// frontmatter block (control test above, A2b) is correct and must not change — but it was
// SILENT: `synced: true`, exit 0, no signal that the existing block (including any
// merge-conflict markers) was unreadable and destroyed. §8.5: "a derived conclusion may not
// be reported as authoritative when the derivation dropped input it could not resolve."
// Table-driven per the dispatch brief's instruction to check sibling verbs on the same
// ADR-3408 §8.3 sanctioned-regenerate list: `REGENERATE_STATE` (`/msd-health --repair`) is
// on that list too, but is DESTRUCTIVE-risk and unconditionally REFUSED by `applyRepairs`'s
// dispatcher (src/health-diagnostic.cts) before `runRepairAction` is ever invoked — so
// `state sync` is the only LIVE verb on the sanctioned path today. No table needed; a single
// verb, driven through the real CLI, is the whole live surface.
describe('A2c stateSyncWarnsOnUnparseableFrontmatterRegeneration', () => {
const CONFLICT_FM_BLOCK = [
'---',
'<<<<<<< HEAD',
'status: foo',
'=======',
'status: bar',
'>>>>>>> feature',
'---',
'',
].join('\n');
const CONFLICT_BODY = [
'# Project State',
'',
'## Current Position',
'',
'Phase: 1 — Foundation',
'Plan: 1 of 1',
'Status: Executing Phase 1',
'Last activity: 2026-07-01 — mid-flight',
'',
].join('\n');
function seedPhaseDirs(tmpDir) {
const planningDir = path.join(tmpDir, '.planning');
fs.mkdirSync(path.join(planningDir, 'phases', '01-foundation'), { recursive: true });
fs.mkdirSync(path.join(planningDir, 'phases', '02-next-phase'), { recursive: true });
fs.writeFileSync(
path.join(planningDir, 'ROADMAP.md'),
['# Roadmap', '', '### Phase 1: Foundation', '**Goal:** Setup', '', '### Phase 2: Next', '**Goal:** More', ''].join('\n'),
);
}
function writeConflictState(tmpDir) {
const statePath = path.join(tmpDir, '.planning', 'STATE.md');
fs.writeFileSync(statePath, CONFLICT_FM_BLOCK + CONFLICT_BODY);
return statePath;
}
function writeValidState(tmpDir) {
const statePath = path.join(tmpDir, '.planning', 'STATE.md');
const validFm = [
'---',
'msd_state_version: \'1.0\'',
'status: executing',
'current_phase: 1',
'---',
'',
].join('\n');
fs.writeFileSync(statePath, validFm + CONFLICT_BODY);
return statePath;
}
test('RED (pre-fix) proof: unparseable frontmatter — stderr carries the msd: warning line and the JSON result surfaces it in `changes`', () => {
const tmpDir = createTempProject();
try {
seedPhaseDirs(tmpDir);
const statePath = writeConflictState(tmpDir);
const r = runNode([TOOLS_PATH, 'state', 'sync', '--raw'], { cwd: tmpDir, timeoutMs: PROBE_TIMEOUT_MS });
throwIfFailed(r, 'msd-tools state sync --raw');
// Regeneration still happened (unchanged contract — the control test above pins this
// for the general case; re-asserted here on the same fixture this warning covers).
const after = fs.readFileSync(statePath, 'utf-8');
assert.ok(!after.includes('<<<<<<< HEAD'), 'state sync must still regenerate over the unparseable block');
// Human channel: matches the existing `msd: warning — ... (#NNNN)` precedent (#3573).
assert.match(
r.stderr,
/msd: warning — .*frontmatter.*could not be parsed.*regenerated.*\(#3881\)/s,
`expected a msd: warning on stderr naming the unparseable frontmatter; got stderr:\n${r.stderr}\nstdout:\n${r.stdout}`,
);
// Machine channel: the JSON result's existing `changes` array (the mechanism this
// codebase already uses to surface sync-time signals — see the "Progress: skipped —
// ..." entries in src/state.cts) must carry the same disclosure.
const parsed = JSON.parse(r.stdout);
assert.ok(Array.isArray(parsed.changes), `expected a changes array in JSON result; got ${r.stdout}`);
assert.ok(
parsed.changes.some((c) => typeof c === 'string' && c.includes('could not be parsed') && c.includes('#3881')),
`expected 'changes' to include the unparseable-frontmatter warning; got ${JSON.stringify(parsed.changes)}`,
);
assert.strictEqual(parsed.synced, true, 'exit-0/synced:true stays correct — sync did what its contract says');
} finally {
cleanup(tmpDir);
}
});
test('control (cannot pass vacuously): valid, parseable frontmatter emits NO such warning', () => {
const tmpDir = createTempProject();
try {
seedPhaseDirs(tmpDir);
const statePath = writeValidState(tmpDir);
const r = runNode([TOOLS_PATH, 'state', 'sync', '--raw'], { cwd: tmpDir, timeoutMs: PROBE_TIMEOUT_MS });
throwIfFailed(r, 'msd-tools state sync --raw');
assert.doesNotMatch(
r.stderr,
/#3881/,
`valid frontmatter must not trigger the unparseable-frontmatter warning; got stderr:\n${r.stderr}`,
);
const parsed = JSON.parse(r.stdout);
assert.ok(
!parsed.changes.some((c) => typeof c === 'string' && c.includes('#3881')),
`expected no #3881 warning in changes for valid frontmatter; got ${JSON.stringify(parsed.changes)}`,
);
void statePath;
} finally {
cleanup(tmpDir);
}
});
});
describe('A3 unparseableIsDistinguishableFromEmpty', () => {
test('both an empty and an unparseable block yield zero keys, but only the unparseable one carries the marker', () => {
const empty = extractFrontmatter('---\n---\n\nbody\n');
const unparseable = extractFrontmatter('---\nfoo: [unclosed\n---\n\nbody\n');
assert.equal(Object.keys(empty).length, 0);
assert.equal(Object.keys(unparseable).length, 0);
assert.notEqual(
empty[FRONTMATTER_UNPARSEABLE],
true,
'a genuinely empty frontmatter block must not carry the unparseable marker'
);
assert.equal(
unparseable[FRONTMATTER_UNPARSEABLE],
true,
'a malformed frontmatter block must carry the unparseable marker'
);
});
});
describe('A4 nonScalarValuesCanonicalize', () => {
test('the four spellings of an object-list scalar canonicalize to one value', () => {
const spellings = [
'- test: "a b"',
'- test: a b',
"- test: 'a b'",
'- {test: a b}',
];
const CANONICAL = ['test: a b'];
for (const spelling of spellings) {
const doc = `---\nkey:\n${spelling}\n---\n\nbody\n`;
const parsed = extractFrontmatter(doc);
assert.deepEqual(
parsed.key,
CANONICAL,
`spelling ${JSON.stringify(spelling)} must canonicalize to ${JSON.stringify(CANONICAL)}; got ${JSON.stringify(parsed.key)}`
);
}
});
});
describe('A5 truncationProbeStillFiresOnAnOpenFence', () => {
test('fires on the dominant real truncation shape: opening fence, well-formed keys, then nothing', () => {
_resetUnusableInputWarningsForTests();
const truncated = '---\nphase: 3\nplan: 2\n';
extractFrontmatter(truncated);
assert.equal(
_unusableInputEmissionCountForTests(),
1,
'the #1882 probe must fire on a well-formed-but-unterminated frontmatter region'
);
});
test('does NOT fire on the documented false-positive shape: a rule followed by ordinary prose', () => {
_resetUnusableInputWarningsForTests();
const rule = '---\nNote: this is a paragraph.\n\nJust ordinary prose after a thematic break.\n';
extractFrontmatter(rule);
assert.equal(
_unusableInputEmissionCountForTests(),
0,
'a document that merely opens with a thematic break above prose must not be flagged as truncated'
);
});
// Post-#3881-review, finding 5: the trivially-parseable dominant shape above was the ONLY
// shape this row exercised — vacuous for the risk it names, since it never touched
// `countKeysBeforeTruncation`'s failure/recovery path at all (that whole-region text is
// valid YAML; the probe fires purely from a successful parse). Table-driven over every real
// truncation shape confirmed regressed by execution during review: an unquoted colon inside
// a value, an open (unterminated) flow collection, a mis-indented sibling key, and an
// anchor/alias whose refusal throws a mark-less exception. Each must still fire the #1882
// diagnostic exactly once.
const REGRESSED_TRUNCATION_SHAPES = [
['unquoted colon in a value', '---\nphase: 3\ntitle: a: b\n'],
['open (unterminated) flow collection', '---\nphase: 3\nlist: [a, b\n'],
['mis-indented sibling key', '---\nphase: 3\n plan: 2\n'],
['anchor/alias — refusal throws a mark-less exception', '---\nphase: 3\nfoo: &a bar\n'],
];
for (const [label, doc] of REGRESSED_TRUNCATION_SHAPES) {
test(`fires on a real truncation shape the mark-based recovery regressed on: ${label}`, () => {
_resetUnusableInputWarningsForTests();
extractFrontmatter(doc);
assert.equal(
_unusableInputEmissionCountForTests(),
1,
`the #1882 probe must fire on an unterminated region shaped like: ${label}; doc=${JSON.stringify(doc)}`
);
});
}
});
describe('A6 commentsStayOnTheirOwnKey', () => {
test('a column-0 comment above a Unicode key attaches to that key and survives a round-trip', () => {
const doc = '---\nfoo: bar\n# note\n相: baz\n---\n\nbody\n';
const parsed = extractFrontmatter(doc);
assert.deepEqual(Object.keys(parsed), ['foo', '相']);
assert.equal(parsed['相'], 'baz');
const reconstructed = reconstructFrontmatter(parsed);
const commentLine = reconstructed.split('\n').find((l) => l.startsWith('#'));
const keyLine = reconstructed.split('\n').find((l) => l.startsWith('相:'));
assert.ok(commentLine, `reconstructed frontmatter must carry the comment; got ${JSON.stringify(reconstructed)}`);
const commentIdx = reconstructed.split('\n').indexOf(commentLine);
const keyIdx = reconstructed.split('\n').indexOf(keyLine);
assert.equal(keyIdx, commentIdx + 1, 'the comment must sit immediately above the 相 key, not the following one');
// Round-trip: reparsing the reconstructed block and reconstructing again is byte-identical.
const rewritten = `---\n${reconstructed}\n---\n\nbody\n`;
const reparsed = extractFrontmatter(rewritten);
assert.equal(reconstructFrontmatter(reparsed), reconstructed);
});
});
describe('A7 anchorsAndAliasesAreRefused', () => {
// #3881 review, finding 1: the original refusal was a raw-line regex matching only the
// bare-key spelling (`key: &x`). A quoted key, a flow mapping and a flow sequence all
// define/use the SAME anchor mechanics while never matching that line shape — table-driven
// over every spelling that was confirmed bypassable, plus the original passing case, so a
// future regression in any one spelling fails loudly rather than hiding behind the others.
const SPELLINGS = [
['plain', '---\nfoo: &a bar\nbaz: *a\n---\n\nbody\n'],
['quoted key', '---\n"foo": &a bar\n"baz": *a\n---\n\nbody\n'],
['flow mapping', '---\na: {b: &a 1, c: *a}\n---\n\nbody\n'],
['flow sequence', '---\na: [&a "q", *a]\n---\n\nbody\n'],
['merge key (<<:) with an alias', '---\nbase: &b\n x: "1"\nfoo:\n <<: *b\n y: "2"\n---\n\nbody\n'],
];
for (const [label, doc] of SPELLINGS) {
test(`${label}: refused rather than expanded`, () => {
const parsed = extractFrontmatter(doc);
assert.equal(Object.keys(parsed).length, 0, `${label} must parse to zero keys`);
assert.equal(parsed[FRONTMATTER_UNPARSEABLE], true, `${label} must carry the unparseable marker`);
});
}
test('a bare merge key with NO alias is not itself refused (no anchor, no expansion risk)', () => {
// Under FAILSAFE_SCHEMA (no !!merge type resolution) this never actually merges — it
// parses as an ordinary, non-expanding literal "<<" string key. Documented behavior
// change from the pre-review regex (which refused every `<<:`-shaped line regardless of
// whether an alias was present) — see frontmatter.cts refuseAnchorsAndAliases docblock.
const doc = '---\na:\n <<: {b: 1}\n c: 2\n---\n\nbody\n';
const parsed = extractFrontmatter(doc);
assert.notEqual(parsed[FRONTMATTER_UNPARSEABLE], true);
assert.deepEqual(parsed.a, { '<<': { b: '1' }, c: '2' });
});
});
describe('A8 aliasExpansionCannotExhaustMemory', () => {
test('a billion-laughs frontmatter is refused, bounded on the RESULT, never on elapsed time', () => {
const bomb = [
'a: &a ["lol","lol","lol","lol","lol","lol","lol","lol","lol"]',
'b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]',
'c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]',
'd: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]',
'e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d]',
'f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e]',
'g: [*f,*f,*f,*f,*f,*f,*f,*f,*f]',
].join('\n');
const doc = `---\n${bomb}\n---\n\nbody\n`;
const parsed = extractFrontmatter(doc);
// Assertions are on the RESULT SHAPE (zero keys, bounded serialized size), never on
// wall-clock elapsed time — this repo forbids elapsed-time assertions in tests.
assert.equal(Object.keys(parsed).length, 0);
assert.equal(parsed[FRONTMATTER_UNPARSEABLE], true);
const serializedSize = Buffer.byteLength(JSON.stringify(parsed), 'utf8');
assert.ok(
serializedSize < 1024,
`a refused parse must stay tiny (would be ~22.8MB if expanded); got ${serializedSize} bytes`
);
});
test('the same billion-laughs bomb, quoted-key-spelled, is ALSO refused (#3881 review, finding 1)', () => {
// The exact bypass the review found: the pre-fix raw-text regex matched only bare
// (unquoted) keys, so this 303-byte quoted-key spelling of the identical bomb went
// straight through unrefused and expanded to ~35.8MB. Pinned here on the RESULT shape.
const bomb = [
'"a": &a ["lol","lol","lol","lol","lol","lol","lol","lol","lol"]',
'"b": &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]',
'"c": &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]',
'"d": &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]',
'"e": &e [*d,*d,*d,*d,*d,*d,*d,*d,*d]',
'"f": &f [*e,*e,*e,*e,*e,*e,*e,*e,*e]',
'"g": [*f,*f,*f,*f,*f,*f,*f,*f,*f]',
].join('\n');
const doc = `---\n${bomb}\n---\n\nbody\n`;
const parsed = extractFrontmatter(doc);
assert.equal(Object.keys(parsed).length, 0);
assert.equal(parsed[FRONTMATTER_UNPARSEABLE], true);
const serializedSize = Buffer.byteLength(JSON.stringify(parsed), 'utf8');
assert.ok(
serializedSize < 1024,
`a refused parse must stay tiny (would be ~35.8MB if expanded); got ${serializedSize} bytes`
);
});
});
// A9: fix #3881/#3881-followup-2 (regression pinned by tests/smart-entry.unit.test.cjs:867,
// tests/smart-entry.property.test.cjs). `repairAmbiguousColonValues`'s only real dependent is
// hand-edited STATE.md content that never lives in this repo's own tracked `*.md` files — a
// tracked-document sweep will always show zero dependents for this function, which is exactly
// the wrong signal to delete it on (see `loadWithAmbiguousColonRepair`'s docblock in
// src/frontmatter.cts). This row pins the dependency at the frontmatter layer itself, so the
// next document sweep sees it here too, not only three modules away in smart-entry.
describe('A9 ambiguousColonRepairSurvivesHandEditedStateMd (#2571/#2570)', () => {
test('a colon-separated date+description value parses to the full string after the first colon', () => {
const doc = '---\nlast_activity: 2026-06-08: reviewed the PR queue\n---\n\nbody\n';
const parsed = extractFrontmatter(doc);
assert.equal(
parsed.last_activity,
'2026-06-08: reviewed the PR queue',
'the ambiguous colon must be repaired rather than the whole region going unparseable'
);
});
});
describe('finding 3: null-byte sentinel round-trip is injective', () => {
const E000 = String.fromCharCode(0xE000);
test('a real NUL is preserved exactly when no pre-existing U+E000 is present', () => {
const doc = '---\nfoo: "hasnull"\n---\n\nbody\n';
const parsed = extractFrontmatter(doc);
assert.equal(parsed.foo, 'hasnull');
});
test('a document containing a literal U+E000 (the sentinel itself) is refused, not silently corrupted', () => {
// Before the fix, restoreNullBytesDeep rewrote EVERY U+E000 in the parsed tree back to
// U+0000 unconditionally — including one the document author legitimately wrote — so this
// document's own U+E000 silently became a NUL. It must now be refused instead.
const doc = `---\nfoo: "pre${E000}existing"\n---\n\nbody\n`;
const parsed = extractFrontmatter(doc);
assert.equal(Object.keys(parsed).length, 0);
assert.equal(parsed[FRONTMATTER_UNPARSEABLE], true);
});
test('a literal U+E000 alongside a real NUL is refused rather than merging the two into one byte', () => {
// The exact corruption case from the review: escaping the real NUL to U+E000 makes it
// indistinguishable from the pre-existing U+E000, and restoring converts BOTH back to NUL.
const doc = `---\nfoo: "hasnull"\nbar: "pre${E000}existing"\n---\n\nbody\n`;
const parsed = extractFrontmatter(doc);
assert.equal(Object.keys(parsed).length, 0);
assert.equal(parsed[FRONTMATTER_UNPARSEABLE], true);
});
});
// ─── F. Boundaries ──────────────────────────────────────────────────────────
describe('F1 UNTERMINATED_KEY_THRESHOLD boundary', () => {
function unterminatedRegionWithKeys(n) {
const lines = [];
for (let i = 0; i < n; i++) lines.push(`k${i}: v${i}`);
return `---\n${lines.join('\n')}\n`;
}
test('threshold-1 keys: no diagnostic', () => {
_resetUnusableInputWarningsForTests();
extractFrontmatter(unterminatedRegionWithKeys(UNTERMINATED_KEY_THRESHOLD - 1));
assert.equal(_unusableInputEmissionCountForTests(), 0);
});
test('threshold keys: fires', () => {
_resetUnusableInputWarningsForTests();
extractFrontmatter(unterminatedRegionWithKeys(UNTERMINATED_KEY_THRESHOLD));
assert.equal(_unusableInputEmissionCountForTests(), 1);
});
test('threshold+1 keys: fires', () => {
_resetUnusableInputWarningsForTests();
extractFrontmatter(unterminatedRegionWithKeys(UNTERMINATED_KEY_THRESHOLD + 1));
assert.equal(_unusableInputEmissionCountForTests(), 1);
});
});
describe('F2 alias/nesting refusal bound', () => {
// refuseAnchorsAndAliases (frontmatter.cjs) is a raw-text pre-scan that refuses on ANY
// line carrying an anchor/alias/merge-key marker — there is no numeric count threshold
// in this implementation. The real boundary it exercises is therefore an occurrence
// COUNT: 0 (below the refusal trigger) parses; 1 (the trigger) is refused; 2 (over) stays
// refused, proving the refusal is not a first-occurrence artifact that a second alias
// could slip past.
test('0 anchor/alias lines: parses normally', () => {
const doc = '---\nfoo: bar\nbaz: qux\n---\n\nbody\n';
const parsed = extractFrontmatter(doc);
assert.deepEqual(parsed, { foo: 'bar', baz: 'qux' });
});
test('1 anchor/alias line: refused', () => {
const doc = '---\nfoo: &a bar\nbaz: qux\n---\n\nbody\n';
const parsed = extractFrontmatter(doc);
assert.equal(Object.keys(parsed).length, 0);
assert.equal(parsed[FRONTMATTER_UNPARSEABLE], true);
});
test('2 anchor/alias lines: still refused', () => {
const doc = '---\nfoo: &a bar\nbaz: *a\n---\n\nbody\n';
const parsed = extractFrontmatter(doc);
assert.equal(Object.keys(parsed).length, 0);
assert.equal(parsed[FRONTMATTER_UNPARSEABLE], true);
});
});
describe('F3 frontmatter size boundary', () => {
const FIXTURE_PATH = path.join(__dirname, 'fixtures', 'adversarial', 'frontmatter', 'huge-bounded.md');
test('~30KB fixture (huge-bounded.md) completes with a typed result', () => {
const content = fs.readFileSync(FIXTURE_PATH, 'utf8');
const parsed = extractFrontmatter(content, FIXTURE_PATH);
assert.equal(typeof parsed, 'object');
assert.ok(Array.isArray(parsed.plans));
assert.ok(parsed.plans.length > 0);
});
test('a larger (~640KB) frontmatter block also completes with a typed result', () => {
withTempDir((dir) => {
const lines = ['---', 'phase: "06"', 'plans:'];
// ~640KB of array items — an order of magnitude above the committed ~30KB fixture,
// isolated in a per-test temp file rather than a new committed fixture.
for (let i = 0; i < 40000; i++) {
lines.push(` - item-${String(i).padStart(5, '0')}`);
}
lines.push('---', '', 'Body.', '');
const content = lines.join('\n');
assert.ok(Buffer.byteLength(content, 'utf8') > 500 * 1024, 'fixture must exceed the committed one by an order of magnitude');
const filePath = path.join(dir, 'huge-bounded-larger.md');
fs.writeFileSync(filePath, content, 'utf8');
const readBack = fs.readFileSync(filePath, 'utf8');
const parsed = extractFrontmatter(readBack, filePath);
assert.equal(typeof parsed, 'object');
assert.ok(Array.isArray(parsed.plans));
assert.equal(parsed.plans.length, 40000);
assert.equal(parsed.plans[0], 'item-00000');
assert.equal(parsed.plans[39999], 'item-39999');
});
});
});
// Relocated from tests/frontmatter.test.cjs (mutation-matrix piece 1, #3881 follow-up): the
// mutation shard dropped tests/frontmatter.test.cjs (2932 lines, 3132ms of the shard's 4800ms
// per-run cost, ~96 minutes of the frontmatter shard's 180-minute budget for that one file) to
// stay inside CI's time budget, but that file was the ONLY place two assertion classes lived —
// the anchor-alias-bomb refusal (ADR-3473 §8.1 consequence 6, row A8) and the B1/B2 block-scalar
// assertions. Both are relocated here verbatim (not re-derived) so the mutants they kill stay
// killed after frontmatter.test.cjs leaves the shard's `tests` list. frontmatter.test.cjs itself
// keeps these exact assertions too (not deleted there) — it still runs in the normal (non-mutation)
// suite, so this is a second, mutation-scoped copy, not a move.
describe('anchor-alias-bomb refusal (relocated from tests/frontmatter.test.cjs for mutation-matrix piece 1)', () => {
const FIXTURE_DIR = path.join(__dirname, 'fixtures', 'adversarial', 'frontmatter');
function readFixture(name) {
return fs.readFileSync(path.join(FIXTURE_DIR, name), 'utf8');
}
test('anchor-alias-bomb.md: refused rather than expanded (ADR-3473 §8.1 consequence 6, row A8)', () => {
const parsed = extractFrontmatter(readFixture('anchor-alias-bomb.md'), 'anchor-alias-bomb.md');
assert.equal(Object.keys(parsed).length, 0);
assert.equal(parsed[FRONTMATTER_UNPARSEABLE], true);
assert.ok(Buffer.byteLength(JSON.stringify(parsed), 'utf8') < 1024);
});
test('anchor-alias-bomb-quoted.md: refused identically, even quoted-key-spelled (#3881 review, finding 1)', () => {
const parsed = extractFrontmatter(readFixture('anchor-alias-bomb-quoted.md'), 'anchor-alias-bomb-quoted.md');
assert.equal(Object.keys(parsed).length, 0);
assert.equal(parsed[FRONTMATTER_UNPARSEABLE], true);
assert.ok(Buffer.byteLength(JSON.stringify(parsed), 'utf8') < 1024);
});
});
describe('B1/B2 block-scalar assertions (relocated from tests/frontmatter.test.cjs for mutation-matrix piece 1)', () => {
const ADD_TESTS_PATH = path.join(__dirname, '..', 'commands', 'msd', 'add-tests.md');
test('B1 blockScalarValueIsNotTheBlockIndicator: argument-instructions is the instruction text, not "|"', () => {
const content = fs.readFileSync(ADD_TESTS_PATH, 'utf8');
const parsed = extractFrontmatter(content, ADD_TESTS_PATH);
const value = parsed['argument-instructions'];
assert.equal(typeof value, 'string');
assert.notEqual(value, '|');
assert.ok(value.length > 1, 'block scalar value must be the multi-line instruction body');
assert.ok(value.includes('Parse the argument as a phase number'), 'block scalar value must retain the source instruction text');
});
test('B2 blockScalarDoesNotInventATopLevelKey: parsing add-tests.md produces no phantom "Example" key', () => {
const content = fs.readFileSync(ADD_TESTS_PATH, 'utf8');
const parsed = extractFrontmatter(content, ADD_TESTS_PATH);
assert.ok(!Object.prototype.hasOwnProperty.call(parsed, 'Example'), 'parser must not scrape a top-level "Example" key out of the block scalar body');
});
});
// Frontmatter mutation-gap closure (#3881 follow-up, CI run 33012034388): the frontmatter
// shard measured 63.03% against its 65 floor. These tests close the gap by constraining real
// behavior in the four highest-value survivor clusters — canonical flattening's no-op guard
// (frontmatterDeepEqual, gates spliceFrontmatter's whole-document identity check), the writer's
// double-quoting decision (scalarNeedsDoubleQuoting), the reader's ambiguous-colon repair
// (repairAmbiguousColonValues), and the null-byte round-trip (escapeNullBytesForParse). Each
// case below is paired with a documented near-miss so a mutant that weakens the real condition
// (not just a syntactically different one) is observably wrong, not merely re-typed.
describe('frontmatterDeepEqual (via spliceFrontmatter no-op detection)', () => {
test('key order is insignificant for objects: a same-value, reordered-keys write is a byte-exact no-op', () => {
const doc = '---\na: 1\nb: 2\n---\n\nbody\n';
assert.equal(spliceFrontmatter(doc, { b: '2', a: '1' }), doc);
});
test('array order IS significant: same elements in a different order is NOT a no-op', () => {
// Near-miss for the .every -> .some mutant: index 0 matches ("x"==="x") but index 1 does
// not ("y" !== "z"). The real .every-based comparison must see this as unequal (regenerate);
// a .some-based mutant would short-circuit true on the matching index-0 element alone and
// wrongly report a no-op.
const doc = '---\ntags:\n - x\n - y\n---\n\nbody\n';
const result = spliceFrontmatter(doc, { tags: ['x', 'z'] });
assert.notEqual(result, doc, 'a value-changed array must not be treated as a no-op write');
});
test('a new array value that differs only in length is NOT a no-op', () => {
const doc = '---\ntags:\n - x\n---\n\nbody\n';
assert.notEqual(spliceFrontmatter(doc, { tags: ['x', 'y'] }), doc);
});
test('two empty arrays of matching (zero) length ARE a no-op', () => {
const doc = '---\ntags: []\n---\n\nbody\n';
assert.equal(spliceFrontmatter(doc, { tags: [] }), doc);
});
test('an array-valued field replaced with a same-text scalar is NOT a no-op (array vs non-array must never compare equal)', () => {
const doc = '---\ntags:\n - x\n---\n\nbody\n';
assert.notEqual(spliceFrontmatter(doc, { tags: 'x' }), doc);
});
test('nested-object key order is insignificant: a same-value, reordered-keys nested object is a no-op', () => {
const doc = '---\nmeta:\n a: "1"\n b: "2"\n---\n\nbody\n';
assert.equal(spliceFrontmatter(doc, { meta: { b: '2', a: '1' } }), doc);
});
test('a nested object with a genuinely different key set is NOT a no-op', () => {
const doc = '---\nmeta:\n a: "1"\n b: "2"\n---\n\nbody\n';
assert.notEqual(spliceFrontmatter(doc, { meta: { a: '1', c: '2' } }), doc);
});
});
describe('scalarNeedsDoubleQuoting (via reconstructFrontmatter double-quoting decisions)', () => {
test('a value with internal (non-leading, non-trailing) whitespace is NOT quoted', () => {
// Near-miss for the /^\s|\s$/ -> /^\s|\s/ mutant (dropped end-anchor): a mutant that tests
// for whitespace ANYWHERE rather than only leading/trailing would wrongly quote this.
assert.equal(reconstructFrontmatter({ key: 'mid dle' }), 'key: mid dle');
});
test('trailing whitespace alone (no leading whitespace) IS quoted', () => {
assert.equal(reconstructFrontmatter({ key: 'trailing ' }), 'key: "trailing "');
});
test('a leading dash followed by a space IS quoted (reads as a YAML list indicator)', () => {
assert.equal(reconstructFrontmatter({ key: '- item' }), 'key: "- item"');
});
test('a leading dash with NO following space is NOT quoted (near-miss control for the above)', () => {
assert.equal(reconstructFrontmatter({ key: '-item' }), 'key: -item');
});
test('a lone UTF-16 surrogate is quoted (bare emission is invalid YAML and would not re-parse)', () => {
const reconstructed = reconstructFrontmatter({ key: '\uD800' });
assert.equal(reconstructed, 'key: "\\uD800"');
});
});
// `repairAmbiguousColonValues` (and its sibling `repairMalformedInlineArrays` +
// `splitLegacyInlineArrayItems`) was deleted (#3881 follow-up): a sweep of every tracked
// `*.md` file with a frontmatter fence (910 files) found ZERO documents whose parse result
// changed with the repair disabled — it was hand-rolled YAML leniency kept alive on a fallback
// path, the exact thing ADR-3473 §8.1 exists to remove. `extractFrontmatter` now surfaces
// `unparseableResult()` (via `FRONTMATTER_UNPARSEABLE`) for the ambiguous-colon shapes this
// block used to pin instead of silently repairing them.
describe('escapeNullBytesForParse (null-byte round-trip through the sentinel swap)', () => {
test('a NUL byte inside a key survives extractFrontmatter byte-for-byte, including at region offset 1', () => {
// Region offset 1 specifically distinguishes the `indexOf(...) === -1` -> `=== +1` mutant:
// for THIS input the mutant's condition is true (index really is 1), so it takes the
// "no substitution needed" branch and hands js-yaml a raw, unescaped NUL — which js-yaml
// rejects outright under every schema, collapsing the whole parse to {}. The same input
// also kills the sentinel StringLiteral "" mutant (deletes the byte instead of escaping it):
// that mutant would parse successfully but produce key "x" instead of "x".
const NUL = '';
const doc = `---\nx${NUL}: y\n---\n\nbody\n`;
const parsed = extractFrontmatter(doc);
assert.ok(
Object.prototype.hasOwnProperty.call(parsed, `x${NUL}`),
`NUL byte must survive as part of the key, not be dropped or crash the parse; got keys ${JSON.stringify(Object.keys(parsed))}`
);
assert.equal(parsed[`x${NUL}`], 'y');
});
});