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.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Tests for src/core-utils.cts (compiled to gsd-core/bin/lib/core-utils.cjs).
|
||||
* Tests for src/core-utils.cts (compiled to msd-core/bin/lib/core-utils.cjs).
|
||||
*
|
||||
* Verifies behavioural contracts of the utilities extracted from core.cjs
|
||||
* per ADR-857 rollout phase 2c (#877):
|
||||
@@ -27,41 +27,41 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const os = require('node:os');
|
||||
|
||||
const coreUtils = require('../gsd-core/bin/lib/core-utils.cjs');
|
||||
const { SCOPE } = require('../gsd-core/bin/lib/planning-scope.cjs');
|
||||
const coreUtils = require('../msd-core/bin/lib/core-utils.cjs');
|
||||
const { SCOPE } = require('../msd-core/bin/lib/planning-scope.cjs');
|
||||
const {
|
||||
cleanup, runGsdTools, scrubConfigLocationEnv,
|
||||
cleanup, runMsdTools, scrubConfigLocationEnv,
|
||||
saveSessionEnv, restoreSessionEnv, clearSessionEnv, TEST_HOME_SANDBOX_MARKER,
|
||||
captureFdSync,
|
||||
} = require('./helpers.cjs');
|
||||
const phaseLocator = require('../gsd-core/bin/lib/phase-locator.cjs');
|
||||
const phaseId = require('../gsd-core/bin/lib/phase-id.cjs');
|
||||
const workstreamNamePolicy = require('../gsd-core/bin/lib/workstream-name-policy.cjs');
|
||||
const activeWorkstreamStore = require('../gsd-core/bin/lib/active-workstream-store.cjs');
|
||||
const gsd2Import = require('../gsd-core/bin/lib/gsd2-import.cjs');
|
||||
const commandsMod = require('../gsd-core/bin/lib/commands.cjs');
|
||||
const initMod = require('../gsd-core/bin/lib/init.cjs');
|
||||
const phaseLocator = require('../msd-core/bin/lib/phase-locator.cjs');
|
||||
const phaseId = require('../msd-core/bin/lib/phase-id.cjs');
|
||||
const workstreamNamePolicy = require('../msd-core/bin/lib/workstream-name-policy.cjs');
|
||||
const activeWorkstreamStore = require('../msd-core/bin/lib/active-workstream-store.cjs');
|
||||
const gsd2Import = require('../msd-core/bin/lib/gsd2-import.cjs');
|
||||
const commandsMod = require('../msd-core/bin/lib/commands.cjs');
|
||||
const initMod = require('../msd-core/bin/lib/init.cjs');
|
||||
|
||||
// #3883/Stryker shard budget (scripts/mutation-matrix.cjs `core-utils` entry,
|
||||
// `tests/state-contract.test.cjs`'s header documents the same mechanism):
|
||||
// Stryker's command-runner bills one `node --test <file>` invocation as a
|
||||
// single unit costing whatever the file's slowest run costs, re-run once per
|
||||
// mutant. A3/A5 originally drove every CLI-reachable slug site through
|
||||
// `runGsdTools` (a real child-process spawn per call, ~85-170ms each across
|
||||
// `runMsdTools` (a real child-process spawn per call, ~85-170ms each across
|
||||
// ~70 calls) — ~7.5s of the file's ~7.9s wall time, which blew the 15-minute
|
||||
// shard cap. `cmdGenerateSlug` / `cmdInitExecutePhase` / `cmdInitPhaseOp` /
|
||||
// `cmdInitProgress` are plain functions reachable in-process from the built
|
||||
// `gsd-core/bin/lib/*.cjs` — calling them directly removes the spawn
|
||||
// `msd-core/bin/lib/*.cjs` — calling them directly removes the spawn
|
||||
// entirely instead of moving the cost to a sibling file. `captureFd1Sync`
|
||||
// intercepts the fd-level write these commands make via io.cjs's
|
||||
// `writeAllSync` → `fs.writeSync(1, ...)` (bug #1008's pattern, already
|
||||
// established in tests/io.test.cjs and tests/init.test.cjs's
|
||||
// `captureInitVerifyWork` — NOT `process.stdout.write`, which silently
|
||||
// captures nothing here). `withHermeticInProcessEnv` reproduces the isolation
|
||||
// `runGsdTools(..., { HOME: tmpDir })` + `testEnvBase()` gave the child
|
||||
// `runMsdTools(..., { HOME: tmpDir })` + `testEnvBase()` gave the child
|
||||
// process (HOME/USERPROFILE sandbox + config-location env scrub + session-
|
||||
// identity env clear) so an in-process call can't read the developer's real
|
||||
// `~/.gsd` config or leak real session-identity env into the slug output.
|
||||
// `~/.msd` config or leak real session-identity env into the slug output.
|
||||
|
||||
function captureFd1Sync(fn) {
|
||||
return captureFdSync(1, fn);
|
||||
@@ -146,7 +146,7 @@ describe('detectSubRepos', () => {
|
||||
afterEach(() => { if (tmpDir) { cleanup(tmpDir); tmpDir = null; } });
|
||||
|
||||
test('returns empty array for directory with no sub-repos', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
assert.deepEqual(coreUtils.detectSubRepos(tmpDir), []);
|
||||
});
|
||||
|
||||
@@ -155,7 +155,7 @@ describe('detectSubRepos', () => {
|
||||
});
|
||||
|
||||
test('detects directory with .git as sub-repo', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
const subDir = path.join(tmpDir, 'myrepo');
|
||||
fs.mkdirSync(subDir);
|
||||
fs.mkdirSync(path.join(subDir, '.git'));
|
||||
@@ -168,7 +168,7 @@ describe('detectSubRepos', () => {
|
||||
// the actual #4458 defect was new-project.md's own `find -exec test -d
|
||||
// "{}/.git"` predicate never calling this helper at all.
|
||||
test('detects directory with .git as a FILE (linked worktree) as sub-repo', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
const subDir = path.join(tmpDir, 'myworktree');
|
||||
fs.mkdirSync(subDir);
|
||||
fs.writeFileSync(path.join(subDir, '.git'), 'gitdir: /some/main/repo/.git/worktrees/myworktree\n');
|
||||
@@ -176,7 +176,7 @@ describe('detectSubRepos', () => {
|
||||
});
|
||||
|
||||
test('excludes hidden directories', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
const hiddenDir = path.join(tmpDir, '.hidden');
|
||||
fs.mkdirSync(hiddenDir);
|
||||
fs.mkdirSync(path.join(hiddenDir, '.git'));
|
||||
@@ -184,7 +184,7 @@ describe('detectSubRepos', () => {
|
||||
});
|
||||
|
||||
test('excludes node_modules', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
const nmDir = path.join(tmpDir, 'node_modules');
|
||||
fs.mkdirSync(nmDir);
|
||||
fs.mkdirSync(path.join(nmDir, '.git'));
|
||||
@@ -192,7 +192,7 @@ describe('detectSubRepos', () => {
|
||||
});
|
||||
|
||||
test('returns sorted results for multiple sub-repos', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
for (const name of ['z-repo', 'a-repo', 'm-repo']) {
|
||||
const subDir = path.join(tmpDir, name);
|
||||
fs.mkdirSync(subDir);
|
||||
@@ -202,7 +202,7 @@ describe('detectSubRepos', () => {
|
||||
});
|
||||
|
||||
test('adversarial: directory name with path-traversal-like characters', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
// Create a subdirectory that doesn't start with '.' and isn't node_modules
|
||||
const subDir = path.join(tmpDir, 'normal-dir');
|
||||
fs.mkdirSync(subDir);
|
||||
@@ -218,31 +218,31 @@ describe('pathExistsInternal', () => {
|
||||
afterEach(() => { if (tmpDir) { cleanup(tmpDir); tmpDir = null; } });
|
||||
|
||||
test('returns true for an existing file', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
const fp = path.join(tmpDir, 'file.txt');
|
||||
fs.writeFileSync(fp, 'hello');
|
||||
assert.strictEqual(coreUtils.pathExistsInternal(tmpDir, 'file.txt'), true);
|
||||
});
|
||||
|
||||
test('returns true for an existing directory', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
const subDir = path.join(tmpDir, 'subdir');
|
||||
fs.mkdirSync(subDir);
|
||||
assert.strictEqual(coreUtils.pathExistsInternal(tmpDir, 'subdir'), true);
|
||||
});
|
||||
|
||||
test('returns false for a non-existent path', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
assert.strictEqual(coreUtils.pathExistsInternal(tmpDir, 'nope.txt'), false);
|
||||
});
|
||||
|
||||
test('handles absolute targetPath', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
assert.strictEqual(coreUtils.pathExistsInternal(tmpDir, tmpDir), true);
|
||||
});
|
||||
|
||||
test('adversarial: path traversal attempt returns false (no such file)', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
// Traversal resolves via path.join — no crash, just correct false/true
|
||||
const result = coreUtils.pathExistsInternal(tmpDir, '../nonexistent');
|
||||
assert.strictEqual(typeof result, 'boolean');
|
||||
@@ -425,12 +425,12 @@ describe('readSubdirectories', () => {
|
||||
});
|
||||
|
||||
test('returns [] for empty directory', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
assert.deepEqual(coreUtils.readSubdirectories(tmpDir), []);
|
||||
});
|
||||
|
||||
test('returns only directory names, not files', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.mkdirSync(path.join(tmpDir, 'subdir'));
|
||||
fs.writeFileSync(path.join(tmpDir, 'file.txt'), '');
|
||||
const result = coreUtils.readSubdirectories(tmpDir);
|
||||
@@ -438,7 +438,7 @@ describe('readSubdirectories', () => {
|
||||
});
|
||||
|
||||
test('sort=false returns dirs in filesystem order', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.mkdirSync(path.join(tmpDir, '02-phase'));
|
||||
fs.mkdirSync(path.join(tmpDir, '01-phase'));
|
||||
const result = coreUtils.readSubdirectories(tmpDir, false);
|
||||
@@ -446,7 +446,7 @@ describe('readSubdirectories', () => {
|
||||
});
|
||||
|
||||
test('sort=true orders by comparePhaseNum', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
for (const name of ['10-phase', '02-phase', '01-phase']) {
|
||||
fs.mkdirSync(path.join(tmpDir, name));
|
||||
}
|
||||
@@ -455,7 +455,7 @@ describe('readSubdirectories', () => {
|
||||
});
|
||||
|
||||
test('sort=true handles decimal phase ids correctly', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
for (const name of ['01.2-phase', '01.10-phase', '01.1-phase']) {
|
||||
fs.mkdirSync(path.join(tmpDir, name));
|
||||
}
|
||||
@@ -472,7 +472,7 @@ describe('getPhaseFileStats', () => {
|
||||
afterEach(() => { if (tmpDir) { cleanup(tmpDir); tmpDir = null; } });
|
||||
|
||||
test('returns empty arrays and false flags for empty directory', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
const stats = coreUtils.getPhaseFileStats(tmpDir);
|
||||
assert.deepEqual(stats.plans, []);
|
||||
assert.deepEqual(stats.summaries, []);
|
||||
@@ -483,7 +483,7 @@ describe('getPhaseFileStats', () => {
|
||||
});
|
||||
|
||||
test('detects PLAN.md files', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.writeFileSync(path.join(tmpDir, 'PLAN.md'), '');
|
||||
fs.writeFileSync(path.join(tmpDir, '01-PLAN.md'), '');
|
||||
const stats = coreUtils.getPhaseFileStats(tmpDir);
|
||||
@@ -491,42 +491,42 @@ describe('getPhaseFileStats', () => {
|
||||
});
|
||||
|
||||
test('detects SUMMARY.md files', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.writeFileSync(path.join(tmpDir, 'SUMMARY.md'), '');
|
||||
const stats = coreUtils.getPhaseFileStats(tmpDir);
|
||||
assert.deepEqual(stats.summaries, ['SUMMARY.md']);
|
||||
});
|
||||
|
||||
test('detects RESEARCH.md', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.writeFileSync(path.join(tmpDir, 'RESEARCH.md'), '');
|
||||
const stats = coreUtils.getPhaseFileStats(tmpDir);
|
||||
assert.strictEqual(stats.hasResearch, true);
|
||||
});
|
||||
|
||||
test('detects *-RESEARCH.md', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.writeFileSync(path.join(tmpDir, 'feature-RESEARCH.md'), '');
|
||||
const stats = coreUtils.getPhaseFileStats(tmpDir);
|
||||
assert.strictEqual(stats.hasResearch, true);
|
||||
});
|
||||
|
||||
test('detects VERIFICATION.md', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.writeFileSync(path.join(tmpDir, 'VERIFICATION.md'), '');
|
||||
const stats = coreUtils.getPhaseFileStats(tmpDir);
|
||||
assert.strictEqual(stats.hasVerification, true);
|
||||
});
|
||||
|
||||
test('detects REVIEWS.md', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.writeFileSync(path.join(tmpDir, 'REVIEWS.md'), '');
|
||||
const stats = coreUtils.getPhaseFileStats(tmpDir);
|
||||
assert.strictEqual(stats.hasReviews, true);
|
||||
});
|
||||
|
||||
test('detects CONTEXT.md via findContextMdIn', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.writeFileSync(path.join(tmpDir, 'CONTEXT.md'), '');
|
||||
const stats = coreUtils.getPhaseFileStats(tmpDir);
|
||||
assert.strictEqual(stats.hasContext, true);
|
||||
@@ -535,13 +535,13 @@ describe('getPhaseFileStats', () => {
|
||||
// ─── #3183 (ADR-3180 Decision 2): scope field + degrade-not-throw ─────────
|
||||
|
||||
test('#3183 row 17: scope is COMPLETE for a readable, empty phase dir', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
const stats = coreUtils.getPhaseFileStats(tmpDir);
|
||||
assert.strictEqual(stats.scope, SCOPE.COMPLETE);
|
||||
});
|
||||
|
||||
test('#3183 row 15: scope is UNREADABLE and getPhaseFileStats does not throw for a nonexistent dir', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
const missing = path.join(tmpDir, 'does-not-exist');
|
||||
assert.doesNotThrow(() => coreUtils.getPhaseFileStats(missing));
|
||||
const stats = coreUtils.getPhaseFileStats(missing);
|
||||
@@ -567,7 +567,7 @@ describe('getPhaseFileStats', () => {
|
||||
// named in the issue. No chmod 0o000 — root bypasses mode bits (silent
|
||||
// zero coverage in root CI); restored via t.mock's auto-restore.
|
||||
test('#4014 matrix row 10: own readdirSync failure is not masked by an already-successful scan.scope', (t) => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.writeFileSync(path.join(tmpDir, '01-PLAN.md'), '# Plan\n');
|
||||
const target = path.resolve(tmpDir);
|
||||
const origReaddirSync = fs.readdirSync.bind(fs);
|
||||
@@ -594,7 +594,7 @@ describe('getPhaseFileStats', () => {
|
||||
});
|
||||
|
||||
test('#4014 matrix row 11: both reads succeed — scope complete, flags computed as before (must not regress)', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.writeFileSync(path.join(tmpDir, '01-PLAN.md'), '# Plan\n');
|
||||
fs.writeFileSync(path.join(tmpDir, 'CONTEXT.md'), '# context\n');
|
||||
const stats = coreUtils.getPhaseFileStats(tmpDir);
|
||||
@@ -604,7 +604,7 @@ describe('getPhaseFileStats', () => {
|
||||
});
|
||||
|
||||
test('#3183 row 7 regression: nested plans/PLAN-01.md ONLY is reported (used to report 0)', () => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
fs.mkdirSync(path.join(tmpDir, 'plans'));
|
||||
fs.writeFileSync(path.join(tmpDir, 'plans', 'PLAN-01.md'), '# Plan\n');
|
||||
const stats = coreUtils.getPhaseFileStats(tmpDir);
|
||||
@@ -616,7 +616,7 @@ describe('getPhaseFileStats', () => {
|
||||
// VERIFICATION.md present belongs to phase 04. scopeToPhase must exclude
|
||||
// it, so hasVerification reads false — an unscoped implementation would
|
||||
// wrongly report phase 03 as verified off phase 04's report.
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-cu-test-'));
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-cu-test-'));
|
||||
const phaseDir = path.join(tmpDir, '03-test');
|
||||
fs.mkdirSync(phaseDir);
|
||||
fs.writeFileSync(path.join(phaseDir, '03-01-PLAN.md'), '# Plan\n');
|
||||
@@ -985,7 +985,7 @@ describe('summaryCandidates / findUnsummarizedPlans / findOrphanSummaries — #4
|
||||
// src/ (file:line evidence in the PR description). This block drives each
|
||||
// reachable site the way production reaches it and proves today's code
|
||||
// disagrees with the canonical — failing-first, per the phase 6 test matrix
|
||||
// (.gsd/phase/feat-3883-one-impl-per-rule/50-test-matrix.md section A/B).
|
||||
// (.msd/phase/feat-3883-one-impl-per-rule/50-test-matrix.md section A/B).
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('#3883 one-impl-per-rule: slug re-implementation divergence', () => {
|
||||
@@ -1092,7 +1092,7 @@ describe('#3883 one-impl-per-rule: slug re-implementation divergence', () => {
|
||||
name: 'init.cts:176 slugifyPhaseName (CLI init execute-phase, ROADMAP-only phase)',
|
||||
cap: null,
|
||||
call(text) {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-3883-ep-'));
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-3883-ep-'));
|
||||
try {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning'), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
@@ -1113,7 +1113,7 @@ describe('#3883 one-impl-per-rule: slug re-implementation divergence', () => {
|
||||
name: 'init.cts:1957 cmdInitPhaseOp !phaseInfo fallback (CLI init phase-op, no directory)',
|
||||
cap: null,
|
||||
call(text) {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-3883-po-fb-'));
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-3883-po-fb-'));
|
||||
try {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning'), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
@@ -1134,7 +1134,7 @@ describe('#3883 one-impl-per-rule: slug re-implementation divergence', () => {
|
||||
name: 'init.cts:1935 cmdInitPhaseOp archived branch (CLI init phase-op, archived dir + current ROADMAP)',
|
||||
cap: null,
|
||||
call(text) {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-3883-po-ar-'));
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-3883-po-ar-'));
|
||||
try {
|
||||
const archiveDir = path.join(tmpDir, '.planning', 'milestones', 'v1.0-phases', '01-old');
|
||||
fs.mkdirSync(archiveDir, { recursive: true });
|
||||
@@ -1157,7 +1157,7 @@ describe('#3883 one-impl-per-rule: slug re-implementation divergence', () => {
|
||||
name: 'init.cts:3109 cmdInitProgress unstarted ROADMAP phase (CLI init progress)',
|
||||
cap: null,
|
||||
call(text) {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-3883-prog-'));
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-3883-prog-'));
|
||||
try {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning'), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
@@ -1189,8 +1189,8 @@ describe('#3883 one-impl-per-rule: slug re-implementation divergence', () => {
|
||||
cap: null,
|
||||
call(text) {
|
||||
try {
|
||||
const dir = phaseId.toDir({ project: 'GSD', milestone: '01', phase: '01' }, text);
|
||||
return dir.replace(/^GSD\.01-01-?/, '');
|
||||
const dir = phaseId.toDir({ project: 'MSD', milestone: '01', phase: '01' }, text);
|
||||
return dir.replace(/^MSD\.01-01-?/, '');
|
||||
} catch (e) {
|
||||
// #3883 declared difference (axis: empty-sanitize-guard,
|
||||
// phase-id.cts:380 toDir docstring, "toDir: slug sanitizes to
|
||||
@@ -1225,7 +1225,7 @@ describe('#3883 one-impl-per-rule: slug re-implementation divergence', () => {
|
||||
name: 'phase-locator.cts:269 findPhaseInternal phase_slug (direct require, exported)',
|
||||
cap: null,
|
||||
call(text) {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-3883-pl-'));
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-3883-pl-'));
|
||||
try {
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', `01-${text}`);
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
@@ -1267,7 +1267,7 @@ describe('#3883 one-impl-per-rule: slug re-implementation divergence', () => {
|
||||
reason:
|
||||
'The slugified text is never caller-supplied: it is always one of a '
|
||||
+ 'fixed ASCII whitelist of environment-variable KEY NAMES '
|
||||
+ '(WORKSTREAM_SESSION_ENV_KEYS, all 13 entries: GSD_SESSION_KEY, '
|
||||
+ '(WORKSTREAM_SESSION_ENV_KEYS, all 13 entries: MSD_SESSION_KEY, '
|
||||
+ 'CODEX_THREAD_ID, CLAUDE_SESSION_ID, CLAUDE_CODE_SESSION_ID, '
|
||||
+ 'CLAUDE_CODE_SSE_PORT, OPENCODE_SESSION_ID, GEMINI_SESSION_ID, '
|
||||
+ 'CURSOR_SESSION_ID, WINDSURF_SESSION_ID, TERM_SESSION_ID, WT_SESSION, '
|
||||
@@ -1408,7 +1408,7 @@ describe('#3883 one-impl-per-rule: slug re-implementation divergence', () => {
|
||||
// its own real, restricted input domain (declared, A4), not the shared corpus.
|
||||
describe('active-workstream-store.cts:97 getWorkstreamSessionKey (real input domain only)', () => {
|
||||
const REAL_ENV_KEYS = [
|
||||
'GSD_SESSION_KEY', 'CODEX_THREAD_ID', 'CLAUDE_SESSION_ID', 'CLAUDE_CODE_SESSION_ID',
|
||||
'MSD_SESSION_KEY', 'CODEX_THREAD_ID', 'CLAUDE_SESSION_ID', 'CLAUDE_CODE_SESSION_ID',
|
||||
'CLAUDE_CODE_SSE_PORT', 'OPENCODE_SESSION_ID', 'GEMINI_SESSION_ID', 'CURSOR_SESSION_ID',
|
||||
'WINDSURF_SESSION_ID', 'TERM_SESSION_ID', 'WT_SESSION', 'TMUX_PANE', 'ZELLIJ_SESSION_NAME',
|
||||
];
|
||||
@@ -1454,7 +1454,7 @@ describe('#3883 one-impl-per-rule: slug re-implementation divergence', () => {
|
||||
assert.strictEqual(coreUtils.generateSlugInternal(' '), '');
|
||||
});
|
||||
test('cmdGenerateSlug rejects empty input with an explicit error (not a silent empty slug)', () => {
|
||||
const result = runGsdTools(['generate-slug', ''], process.cwd(), { HOME: os.tmpdir() });
|
||||
const result = runMsdTools(['generate-slug', ''], process.cwd(), { HOME: os.tmpdir() });
|
||||
assert.ok(!result.success, 'generate-slug must fail on empty text');
|
||||
assert.ok(result.error.includes('text required'), `expected "text required" error, got: ${result.error}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user