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.
274 lines
15 KiB
JavaScript
274 lines
15 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* #4264/#4265/#4266 (epic #4272 Phase 2) — TDD_APPLICABLE is USED (as a
|
|
* `${TDD_APPLICABLE ? ... : ...}` ternary, per #3990) in both executor
|
|
* dispatch backends but was never ASSIGNED anywhere: `phase.tdd-applicable`
|
|
* (Phase 1, #4273) computed the predicate, but no workflow ever called it.
|
|
* That is a silent "absence resolves to false" defect (ADR-3473 §8.4) — an
|
|
* un-substituted `${TDD_APPLICABLE ...}` marker is JS-truthy as literal text,
|
|
* so a real TDD plan would have silently dropped its RED/GREEN/REFACTOR
|
|
* procedure while never producing an error.
|
|
*
|
|
* These are shape assertions on the raw workflow-markdown TEXT: the deployed
|
|
* text IS the runtime-loaded product (an LLM orchestrator reads it top to
|
|
* bottom and composes/executes it) — same rationale as
|
|
* tdd-single-statement.test.cjs's header comment. `readFileSync` here targets
|
|
* `.md` files only, never a `.cjs`/`.js`/`.ts` source path, so
|
|
* `local/no-source-grep` does not apply and no `allow-test-rule` marker is
|
|
* needed (confirmed against tdd-single-statement.test.cjs, which reads the
|
|
* same two files with no such marker).
|
|
*/
|
|
|
|
const { test, describe } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
|
|
const ROOT = path.join(__dirname, '..');
|
|
const read = (p) => fs.readFileSync(path.join(ROOT, p), 'utf8');
|
|
|
|
const HARNESS_PATH = 'msd-core/workflows/execute-phase.md';
|
|
const WORKTREE_PATH = 'msd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md';
|
|
const TDD_STEP_FRAGMENT_PATH = 'msd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md';
|
|
|
|
// #4272-phase-6 byte-ceiling extraction (two commits ago): the harness
|
|
// backend's TDD_APPLICABLE assignment + phase.tdd-applicable call moved OUT
|
|
// of execute-phase.md and into the tdd-applicability-resolution.md step
|
|
// fragment; execute-phase.md itself now carries only a one-line REFERENCE to
|
|
// that fragment. The worktree backend was not affected — it still carries
|
|
// everything inline in executor-isolation-dispatch.md. `referenceFile` is
|
|
// where the reader/orchestrator encounters the backend (and, for the
|
|
// harness, where the one-line pointer + the ${TDD_APPLICABLE...} USE live);
|
|
// `assignmentFile` is where TDD_APPLICABLE is actually ASSIGNED.
|
|
const BACKENDS = {
|
|
'execute-phase.md': { referenceFile: HARNESS_PATH, assignmentFile: TDD_STEP_FRAGMENT_PATH },
|
|
'executor-isolation-dispatch.md': { referenceFile: WORKTREE_PATH, assignmentFile: WORKTREE_PATH },
|
|
};
|
|
|
|
// #4268: the two backends' `msd_run query phase.tdd-applicable` calls differ
|
|
// today ONLY in their variable-name prefix (`TDD_APPLICABLE_RAW=$(...)` vs
|
|
// `_TDD_APPLICABLE_RAW=$(...)`), and nothing asserts the command-substitution
|
|
// CONTENT itself stays byte-identical — a one-word divergence (e.g. dropping
|
|
// `2>/dev/null` or changing `--pick applicable` in only one backend) ships
|
|
// green. This extracts the call starting at `msd_run query
|
|
// phase.tdd-applicable` through the end of the line, stripping only the
|
|
// trailing `)` that closes the `$( ... )` command substitution — i.e.
|
|
// everything up to and including the variable-name-and-`=$(` prefix is
|
|
// discarded, exactly per #4268.
|
|
function extractQueryCall(line) {
|
|
const start = line.indexOf('msd_run query phase.tdd-applicable');
|
|
if (start === -1) return null;
|
|
let call = line.slice(start);
|
|
if (call.endsWith(')')) call = call.slice(0, -1);
|
|
return call;
|
|
}
|
|
|
|
describe('#4266 — TDD_APPLICABLE is actually computed in both backends', () => {
|
|
// A backend may assign TDD_APPLICABLE directly from the `msd_run query
|
|
// phase.tdd-applicable` call (harness: `TDD_APPLICABLE=$(msd_run query
|
|
// phase.tdd-applicable ...)`) or via an intermediate raw/rc pair mirroring
|
|
// the existing ISOLATION resolution pattern (worktree: `_TDD_APPLICABLE_RAW
|
|
// =$(msd_run query phase.tdd-applicable ...)` ... `TDD_APPLICABLE=
|
|
// "$_TDD_APPLICABLE_RAW"`) — either shape is fine as long as the assignment
|
|
// exists AND is fed by a real phase.tdd-applicable call within a small
|
|
// preceding window, not a bare/silent default.
|
|
function assertTddApplicableIsComputed(name, filePath) {
|
|
const text = read(filePath);
|
|
const lines = text.split('\n');
|
|
const assignIdx = lines.findIndex((l) => /^\s*TDD_APPLICABLE=/.test(l));
|
|
assert.ok(assignIdx !== -1, `${name} must assign TDD_APPLICABLE, not just reference it`);
|
|
const windowStart = Math.max(0, assignIdx - 8);
|
|
const window = lines.slice(windowStart, assignIdx + 1).join('\n');
|
|
assert.ok(
|
|
window.includes('phase.tdd-applicable'),
|
|
`${name}'s TDD_APPLICABLE assignment must be fed by a phase.tdd-applicable call within a few lines above it, got window: ${window}`,
|
|
);
|
|
}
|
|
|
|
test('harness backend (execute-phase.md) assigns TDD_APPLICABLE via phase.tdd-applicable', () => {
|
|
// Assignment now lives in the extracted tdd-applicability-resolution.md
|
|
// step fragment, not in execute-phase.md itself (#4272-phase-6).
|
|
assertTddApplicableIsComputed('execute-phase.md', BACKENDS['execute-phase.md'].assignmentFile);
|
|
});
|
|
|
|
test('worktree backend (executor-isolation-dispatch.md) assigns TDD_APPLICABLE via phase.tdd-applicable', () => {
|
|
assertTddApplicableIsComputed('executor-isolation-dispatch.md', BACKENDS['executor-isolation-dispatch.md'].assignmentFile);
|
|
});
|
|
|
|
for (const [name, cfg] of Object.entries(BACKENDS)) {
|
|
test(`${name}'s phase.tdd-applicable call is plan-scoped, not phase-scoped`, () => {
|
|
const text = read(cfg.assignmentFile);
|
|
const callLine = text.split('\n').find((l) => l.includes('phase.tdd-applicable') && l.includes('msd_run query'));
|
|
assert.ok(callLine, `${name} must carry a msd_run query phase.tdd-applicable call`);
|
|
assert.ok(
|
|
callLine.includes('{phase_dir}') && callLine.includes('{plan_file}'),
|
|
`${name}'s phase.tdd-applicable call must pass the per-plan {phase_dir}/{plan_file} path, got: ${callLine.trim()}`,
|
|
);
|
|
});
|
|
}
|
|
|
|
test('both backends assign TDD_APPLICABLE before every ${TDD_APPLICABLE...} use (document order)', () => {
|
|
// Worktree backend: assignment and every ${TDD_APPLICABLE...} use live
|
|
// in the same file — same-file ordering check.
|
|
{
|
|
const text = read(BACKENDS['executor-isolation-dispatch.md'].assignmentFile);
|
|
const lines = text.split('\n');
|
|
const assignIdx = lines.findIndex((l) => /^\s*TDD_APPLICABLE=/.test(l));
|
|
assert.ok(assignIdx !== -1, 'executor-isolation-dispatch.md has no TDD_APPLICABLE assignment');
|
|
const useIndices = [];
|
|
lines.forEach((l, i) => {
|
|
if (/\$\{TDD_APPLICABLE\b/.test(l)) useIndices.push(i);
|
|
});
|
|
assert.ok(useIndices.length > 0, 'executor-isolation-dispatch.md has no ${TDD_APPLICABLE...} use to check ordering against');
|
|
for (const useIdx of useIndices) {
|
|
assert.ok(
|
|
assignIdx < useIdx,
|
|
`executor-isolation-dispatch.md: TDD_APPLICABLE is used at line ${useIdx + 1} before it is assigned at line ${assignIdx + 1}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Harness backend: the assignment was extracted into
|
|
// tdd-applicability-resolution.md (#4272-phase-6); execute-phase.md no
|
|
// longer contains the assignment at all, only a one-line REFERENCE to
|
|
// where it's resolved followed (later) by the ${TDD_APPLICABLE...} USE.
|
|
// Confirm the fragment does assign it, and that in execute-phase.md the
|
|
// reference precedes every use.
|
|
{
|
|
const fragmentText = read(BACKENDS['execute-phase.md'].assignmentFile);
|
|
assert.ok(
|
|
/^\s*TDD_APPLICABLE=/m.test(fragmentText),
|
|
'tdd-applicability-resolution.md has no TDD_APPLICABLE assignment',
|
|
);
|
|
|
|
const text = read(BACKENDS['execute-phase.md'].referenceFile);
|
|
const lines = text.split('\n');
|
|
const refIdx = lines.findIndex(
|
|
(l) => l.includes('tdd-applicability-resolution.md') && l.includes('TDD-applicability resolution'),
|
|
);
|
|
assert.ok(refIdx !== -1, 'execute-phase.md must carry a one-line reference to tdd-applicability-resolution.md');
|
|
const useIndices = [];
|
|
lines.forEach((l, i) => {
|
|
if (/\$\{TDD_APPLICABLE\b/.test(l)) useIndices.push(i);
|
|
});
|
|
assert.ok(useIndices.length > 0, 'execute-phase.md has no ${TDD_APPLICABLE...} use to check ordering against');
|
|
for (const useIdx of useIndices) {
|
|
assert.ok(
|
|
refIdx < useIdx,
|
|
`execute-phase.md: TDD_APPLICABLE is used at line ${useIdx + 1} before the reference to its resolution at line ${refIdx + 1}`,
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('both tdd.md embed-line comments describe the same three real sources (#4265)', () => {
|
|
for (const [name, cfg] of Object.entries(BACKENDS)) {
|
|
const text = read(cfg.referenceFile);
|
|
const line = text.split('\n').find((l) => /tdd\.md/.test(l) && /TDD_APPLICABLE \?/.test(l));
|
|
assert.ok(line, `${name} still lists tdd.md as a conditional embed entry`);
|
|
assert.ok(line.includes('type: tdd'), `${name}'s tdd.md comment must mention plan type: tdd, got: ${line.trim()}`);
|
|
assert.ok(line.includes('tdd="true"'), `${name}'s tdd.md comment must mention the tdd="true" task attribute (#4265), got: ${line.trim()}`);
|
|
assert.ok(
|
|
line.includes('workflow.tdd_mode') || line.includes('config'),
|
|
`${name}'s tdd.md comment must mention the workflow.tdd_mode config default, got: ${line.trim()}`,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('worktree backend fail-closes on an un-substituted ${TDD_APPLICABLE marker, matching the ${AGENT_SKILLS} check shape', () => {
|
|
const text = read(WORKTREE_PATH);
|
|
const lines = text.split('\n');
|
|
const skillsIdx = lines.findIndex((l) => l.includes("grep -q '\\${AGENT_SKILLS}'"));
|
|
assert.ok(skillsIdx !== -1, 'worktree backend must still carry the existing ${AGENT_SKILLS} fail-closed check');
|
|
const tddCheckIdx = lines.findIndex((l) => l.includes("grep -q '\\${TDD_APPLICABLE"));
|
|
assert.ok(tddCheckIdx !== -1, 'worktree backend must carry a fail-closed grep check for an unresolved ${TDD_APPLICABLE marker');
|
|
// Same shape: FATAL message + exit 1 within the following couple of lines.
|
|
const block = lines.slice(tddCheckIdx, tddCheckIdx + 4).join('\n');
|
|
assert.ok(/FATAL:/.test(block), 'the ${TDD_APPLICABLE check must emit a FATAL message like the ${AGENT_SKILLS} check');
|
|
assert.ok(/exit 1/.test(block), 'the ${TDD_APPLICABLE check must exit 1 like the ${AGENT_SKILLS} check');
|
|
});
|
|
|
|
test('harness backend references the tdd-applicability-resolution step fragment before Agent() is called, and that fragment states a MANDATORY pre-dispatch halt covering TDD_APPLICABLE, CONTEXT_WINDOW, and AGENT_SKILLS', () => {
|
|
// #4272-phase-6 byte-ceiling extraction: the "MANDATORY pre-dispatch check"
|
|
// paragraph moved out of execute-phase.md into
|
|
// tdd-applicability-resolution.md (mirroring per-plan-executor-routing.md);
|
|
// the host file now carries only a one-line reference to it.
|
|
const text = read(HARNESS_PATH);
|
|
const lines = text.split('\n');
|
|
// The literal executor dispatch call sits alone on its own line
|
|
// (` Agent(`), inside the per-plan spawn section (step 3) — distinct
|
|
// from the many inline `` `Agent(...)` `` prose mentions elsewhere in
|
|
// this file (e.g. the runtime-compatibility table at line ~22).
|
|
const agentCallIdx = lines.findIndex((l) => /^\s*Agent\($/.test(l));
|
|
assert.ok(agentCallIdx !== -1, 'execute-phase.md must contain the literal Agent( dispatch-call line');
|
|
const before = lines.slice(0, agentCallIdx).join('\n');
|
|
assert.ok(
|
|
before.includes('execute-phase/steps/tdd-applicability-resolution.md'),
|
|
'execute-phase.md must reference the tdd-applicability-resolution.md step fragment before the Agent() call',
|
|
);
|
|
|
|
const fragment = read(TDD_STEP_FRAGMENT_PATH);
|
|
const checkIdx = fragment.indexOf('MANDATORY');
|
|
assert.ok(checkIdx !== -1, 'tdd-applicability-resolution.md must state a MANDATORY pre-dispatch check');
|
|
const checkText = fragment.slice(checkIdx);
|
|
for (const marker of ['TDD_APPLICABLE', 'CONTEXT_WINDOW', 'AGENT_SKILLS']) {
|
|
assert.ok(checkText.includes(marker), `pre-dispatch check must name ${marker}`);
|
|
}
|
|
assert.ok(/HALT/i.test(checkText), 'pre-dispatch check must instruct an explicit halt, not a soft warning');
|
|
});
|
|
|
|
test('regression: tdd-single-statement.test.cjs assertions still hold by inspection', () => {
|
|
for (const [name, cfg] of Object.entries(BACKENDS)) {
|
|
const text = read(cfg.referenceFile);
|
|
const line = text.split('\n').find((l) => /tdd\.md/.test(l) && /TDD_APPLICABLE/.test(l));
|
|
assert.ok(line, `${name} still lists tdd.md as a conditional embed entry`);
|
|
assert.ok(/TDD_APPLICABLE \?/.test(line), `${name}'s tdd.md entry must stay conditional on TDD_APPLICABLE (#3990)`);
|
|
}
|
|
});
|
|
|
|
test('RED-first: the predicate-equality comparison actually has teeth on a one-token divergence', () => {
|
|
// Prove the comparison catches a divergence BEFORE trusting it against
|
|
// the real files — a deliberately mutated in-memory pair, no file edits.
|
|
const original = 'msd_run query phase.tdd-applicable "{phase_dir}/{plan_file}" --pick applicable 2>/dev/null';
|
|
const mutated = original.replace('--pick applicable', '--pick other');
|
|
assert.notEqual(mutated, original, 'sanity: the mutated fixture must actually differ from the original');
|
|
assert.throws(
|
|
() => assert.equal(mutated, original),
|
|
(err) => err instanceof assert.AssertionError,
|
|
'assert.equal must fail on a one-token divergence between two backends\' predicate calls — otherwise the real-file check below has no teeth',
|
|
);
|
|
});
|
|
|
|
test('both backends\' msd_run query phase.tdd-applicable command-substitution calls are byte-identical', () => {
|
|
// #4268 Standards review: both real backend files also carry this exact
|
|
// substring inside an unrelated FATAL echo message a few lines after the
|
|
// real assignment line (`echo "FATAL: ... 'msd_run query
|
|
// phase.tdd-applicable' failed. ..."`). A bare `.includes()` match
|
|
// happened to work only because `.find()` hits the assignment line
|
|
// first in document order. Anchor on `=$(` immediately before the call —
|
|
// only the real `..._RAW=$(msd_run query phase.tdd-applicable ...)`
|
|
// assignment line has that shape; the FATAL message's `'msd_run query
|
|
// phase.tdd-applicable' failed` is preceded by a quote, not `=$(`.
|
|
const harnessLine = read(BACKENDS['execute-phase.md'].assignmentFile)
|
|
.split('\n')
|
|
.find((l) => l.includes('=$(msd_run query phase.tdd-applicable'));
|
|
const worktreeLine = read(BACKENDS['executor-isolation-dispatch.md'].assignmentFile)
|
|
.split('\n')
|
|
.find((l) => l.includes('=$(msd_run query phase.tdd-applicable'));
|
|
assert.ok(harnessLine, 'execute-phase.md backend must carry a msd_run query phase.tdd-applicable call');
|
|
assert.ok(worktreeLine, 'executor-isolation-dispatch.md backend must carry a msd_run query phase.tdd-applicable call');
|
|
|
|
const harnessCall = extractQueryCall(harnessLine);
|
|
const worktreeCall = extractQueryCall(worktreeLine);
|
|
assert.equal(
|
|
harnessCall,
|
|
worktreeCall,
|
|
'#4268: the harness and worktree backends must issue byte-identical phase.tdd-applicable command-substitution calls ' +
|
|
'(only the variable-name-and-`=$(` prefix may differ), got:\n' +
|
|
` execute-phase.md (harness): ${harnessCall}\n` +
|
|
` executor-isolation-dispatch.md (worktree): ${worktreeCall}`,
|
|
);
|
|
});
|
|
});
|