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.
112 lines
4.1 KiB
JavaScript
112 lines
4.1 KiB
JavaScript
// allow-test-rule: source-text-is-the-product (see #3427)
|
|
// Reads msd-core/templates/phase-prompt.md — the template's deployed text IS
|
|
// what the planner loads, so testing its worked `depends_on` examples tests the
|
|
// contract the wave DAG depends on.
|
|
|
|
/**
|
|
* Regression guard for #3427 / #3473 criterion B8.
|
|
*
|
|
* `resolveDependencyId` (src/phase.cts) resolves a `depends_on` token only
|
|
* when it is a FULL plan id (`planMap` exact match) or a canonical id
|
|
* (`canonicalToId` prefix match). A bare short-form suffix (`"01"`) resolves
|
|
* to nothing and every caller silently drops the edge, collapsing the phase
|
|
* into wave 1 (#3427; the resolver half is rewritten by #3473 B4/B5).
|
|
*
|
|
* Until short-form resolution exists — and regardless, per the template's own
|
|
* documented convention (`depends_on: [] # e.g., ["01-01"]`) — the planner
|
|
* template must only ever TEACH full-form ids in its worked `depends_on`
|
|
* examples. This suite pins that: every non-empty `depends_on:` example in
|
|
* templates/phase-prompt.md lists `NN-NN` full-form plan ids only.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const { describe, test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const TEMPLATE = path.join(__dirname, '..', 'msd-core', 'templates', 'phase-prompt.md');
|
|
|
|
/** Full-form plan id: two-digit phase + two-digit plan, e.g. "03-01". */
|
|
const FULL_FORM_ID = /^\d{2}-\d{2}$/;
|
|
|
|
/**
|
|
* Extract every `depends_on:` example line from a template body.
|
|
* Returns [{ line, tokens }] where tokens is the parsed string array
|
|
* ([] for `depends_on: []`, null for a non-literal/unparsable value — the
|
|
* caller treats null as a failure so an example can never opt out by
|
|
* obfuscating its array).
|
|
*/
|
|
function extractDependsOnExamples(body) {
|
|
const out = [];
|
|
const lines = body.split('\n');
|
|
lines.forEach((line, i) => {
|
|
const match = line.match(/^\s*depends_on:\s*(\[[^\]]*\])\s*(?:#.*)?$/);
|
|
if (!match) return;
|
|
const raw = match[1].trim();
|
|
if (raw === '[]') {
|
|
out.push({ line: i + 1, text: line, tokens: [] });
|
|
return;
|
|
}
|
|
const inner = raw.slice(1, -1).trim();
|
|
const tokens = inner
|
|
.split(',')
|
|
.map((piece) => piece.trim().replace(/^["']|["']$/g, ''));
|
|
out.push({ line: i + 1, text: line, tokens });
|
|
});
|
|
return out;
|
|
}
|
|
|
|
/** Every token in every non-empty example is a full-form plan id. */
|
|
function shortFormTokens(examples) {
|
|
const bad = [];
|
|
for (const example of examples) {
|
|
for (const token of example.tokens) {
|
|
if (!FULL_FORM_ID.test(token)) bad.push({ ...example, token });
|
|
}
|
|
}
|
|
return bad;
|
|
}
|
|
|
|
describe('phase-prompt template teaches only full-form depends_on ids (#3427 / #3473 B8)', () => {
|
|
const body = fs.readFileSync(TEMPLATE, 'utf8');
|
|
const examples = extractDependsOnExamples(body);
|
|
|
|
test('template carries depends_on examples to check (non-vacuous)', () => {
|
|
assert.ok(examples.length >= 8, `expected >= 8 depends_on examples, found ${examples.length}`);
|
|
assert.ok(
|
|
examples.some((e) => e.tokens.length > 0),
|
|
'expected at least one non-empty depends_on example'
|
|
);
|
|
});
|
|
|
|
test('every non-empty depends_on example lists full-form plan ids only', () => {
|
|
const bad = shortFormTokens(examples);
|
|
assert.deepEqual(
|
|
bad.map((b) => `line ${b.line}: token "${b.token}" in ${b.text.trim()}`),
|
|
[],
|
|
'depends_on examples must use full-form plan ids (e.g. ["03-01"]), never ' +
|
|
'bare short-form suffixes like ["01"] — the resolver drops those edges ' +
|
|
'silently (#3427)'
|
|
);
|
|
});
|
|
|
|
test('checker: known good/bad samples classify correctly', () => {
|
|
const good = extractDependsOnExamples(
|
|
[
|
|
'depends_on: []',
|
|
'depends_on: ["03-01"]',
|
|
'depends_on: ["03-01", "03-02"] # trailing comment',
|
|
'depends_on: ["01-01"]',
|
|
].join('\n')
|
|
);
|
|
assert.deepEqual(shortFormTokens(good), []);
|
|
|
|
const bad = extractDependsOnExamples(
|
|
['depends_on: ["01"]', 'depends_on: ["01", "02"]'].join('\n')
|
|
);
|
|
assert.equal(shortFormTokens(bad).length, 3); // both tokens of line 2 + line 1
|
|
});
|
|
});
|