The convergence lint only scanned src/ + gsd-core/bin, so ~35 test files still imported core.cjs. Repoint all 33 behaviour importers to the leaf modules directly (same symbol->leaf map as the src migration; leaves are the objects core re-exported by reference), delete the now-meaningless shim-identity describe blocks in the 8 leaf tests, and delete tests/core.test.cjs (forwarded-behaviour coverage now lives at the leaves; resolveWorktreeRoot test relocated to worktree-safety in T0) and tests/lint-core-spine-imports.test.cjs (the lint is removed in T-final). Dropped the stale core.test.cjs entries from the allow-test-rule-refs allowlist; eslint-rules RuleTester fixture path pointed at io.cjs. After T6: ZERO test imports core.cjs. core.cts still builds (now fully unused); T-final deletes it. No behaviour change. Closes #1291 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -152,8 +152,6 @@
|
||||
"tests/context-enrichment.test.cjs :: source-text-is-the-product",
|
||||
"tests/contributor-standards.test.cjs :: source-text-is-the-product",
|
||||
"tests/copilot-install.test.cjs :: integration-test-input",
|
||||
"tests/core.test.cjs :: architectural-invariant",
|
||||
"tests/core.test.cjs :: structural-regression-guard",
|
||||
"tests/cursor-hooks.test.cjs :: source-text-is-the-product",
|
||||
"tests/cursor-reviewer.test.cjs :: source-text-is-the-product",
|
||||
"tests/debug-session-management.test.cjs :: source-text-is-the-product",
|
||||
|
||||
@@ -28,7 +28,7 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const { createTempProject, cleanup, captureConsole } = require('./helpers.cjs');
|
||||
const { getMilestonePhaseFilter } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { getMilestonePhaseFilter } = require('../gsd-core/bin/lib/roadmap-parser.cjs');
|
||||
|
||||
// ─── helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const { resolveModelInternal } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { resolveModelInternal } = require('../gsd-core/bin/lib/model-resolver.cjs');
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { createTempProject, cleanup } = require('./helpers.cjs');
|
||||
const { getMilestonePhaseFilter } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { getMilestonePhaseFilter } = require('../gsd-core/bin/lib/roadmap-parser.cjs');
|
||||
|
||||
describe('bug #2554 — getMilestonePhaseFilter decimal phase dirs', () => {
|
||||
let tmpDir;
|
||||
|
||||
@@ -17,7 +17,7 @@ const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
const { createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/config-loader.cjs');
|
||||
|
||||
function makeSubRepo(parent, name) {
|
||||
const dir = path.join(parent, name);
|
||||
|
||||
@@ -29,7 +29,7 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const os = require('node:os');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
const { resolveEffortInternal } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { resolveEffortInternal } = require('../gsd-core/bin/lib/model-resolver.cjs');
|
||||
|
||||
/** Create a bare temp directory with no .planning/ structure */
|
||||
function createBareTmpDir(prefix = 'gsd-test-') {
|
||||
|
||||
@@ -107,9 +107,9 @@ describe('bug #730 — milestone (Phase Details) section scope resolution', () =
|
||||
// directly before any phases/ dir exists
|
||||
// -------------------------------------------------------------------------
|
||||
test('getRoadmapPhaseInternal resolves the current-milestone phase directly before any dir exists', () => {
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { getRoadmapPhaseInternal } = require('../gsd-core/bin/lib/roadmap-parser.cjs');
|
||||
|
||||
const res = core.getRoadmapPhaseInternal(dir, '2');
|
||||
const res = getRoadmapPhaseInternal(dir, '2');
|
||||
assert.ok(res !== null && res !== undefined, `getRoadmapPhaseInternal returned null/undefined for phase 2`);
|
||||
assert.strictEqual(res.found, true, `res.found should be true; got ${JSON.stringify(res)}`);
|
||||
assert.strictEqual(res.phase_name, 'Feature', `res.phase_name should be 'Feature'; got '${res.phase_name}'`);
|
||||
|
||||
@@ -534,7 +534,7 @@ describe('dispatchCapabilityCommand — non-ExitError from router → structured
|
||||
assert.ok(msg.includes('fake.cjs'), 'message must name the module; got: ' + msg);
|
||||
assert.ok(msg.includes('boom'), 'message must include original error message; got: ' + msg);
|
||||
// Reason must be SDK_FAIL_FAST
|
||||
const { ERROR_REASON } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { ERROR_REASON } = require('../gsd-core/bin/lib/io.cjs');
|
||||
assert.strictEqual(reason, ERROR_REASON.SDK_FAIL_FAST, 'reason must be SDK_FAIL_FAST');
|
||||
});
|
||||
|
||||
@@ -625,7 +625,7 @@ describe('dispatchCapabilityCommand — non-ExitError from router → structured
|
||||
// ─── 10. Async router (thenable) → structured error ─────────────────────────
|
||||
|
||||
describe('dispatchCapabilityCommand — async router returns a Promise → structured error', () => {
|
||||
const { ERROR_REASON } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { ERROR_REASON } = require('../gsd-core/bin/lib/io.cjs');
|
||||
|
||||
test('router returns Promise.resolve() → error() called with "must be synchronous" + SDK_FAIL_FAST', () => {
|
||||
const errorCalls = [];
|
||||
|
||||
@@ -27,7 +27,6 @@ const { cleanup } = require('./helpers.cjs');
|
||||
// ─── module under test ────────────────────────────────────────────────────────
|
||||
|
||||
const configLoader = require('../gsd-core/bin/lib/config-loader.cjs');
|
||||
const coreModule = require('../gsd-core/bin/lib/core.cjs');
|
||||
|
||||
const { loadConfig, _resetRuntimeWarningCacheForTests } = configLoader;
|
||||
|
||||
@@ -50,25 +49,6 @@ function writeWorkstreamConfig(tmpDir, wsName, obj) {
|
||||
fs.writeFileSync(path.join(wsDir, 'config.json'), JSON.stringify(obj, null, 2), 'utf-8');
|
||||
}
|
||||
|
||||
// ─── shim identity ────────────────────────────────────────────────────────────
|
||||
|
||||
describe('config-loader shim identity', () => {
|
||||
test('core.loadConfig === configLoader.loadConfig (same function object)', () => {
|
||||
assert.strictEqual(
|
||||
coreModule.loadConfig,
|
||||
configLoader.loadConfig,
|
||||
'core.cjs must re-export the same loadConfig function as config-loader.cjs'
|
||||
);
|
||||
});
|
||||
|
||||
test('core.isGitIgnored === configLoader.isGitIgnored (same function object)', () => {
|
||||
assert.strictEqual(
|
||||
coreModule.isGitIgnored,
|
||||
configLoader.isGitIgnored,
|
||||
'core.cjs must re-export the same isGitIgnored function as config-loader.cjs'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── defaults when no config.json ────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -30,50 +30,8 @@ const path = require('node:path');
|
||||
const os = require('node:os');
|
||||
|
||||
const coreUtils = require('../gsd-core/bin/lib/core-utils.cjs');
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
// ─── Shim-identity assertions ─────────────────────────────────────────────────
|
||||
|
||||
describe('core-utils: shim-identity — core.cjs re-exports same function objects', () => {
|
||||
test('core.toPosixPath === coreUtils.toPosixPath', () => {
|
||||
assert.strictEqual(core.toPosixPath, coreUtils.toPosixPath);
|
||||
});
|
||||
test('core.detectSubRepos === coreUtils.detectSubRepos', () => {
|
||||
assert.strictEqual(core.detectSubRepos, coreUtils.detectSubRepos);
|
||||
});
|
||||
test('core.extractOneLinerFromBody === coreUtils.extractOneLinerFromBody', () => {
|
||||
assert.strictEqual(core.extractOneLinerFromBody, coreUtils.extractOneLinerFromBody);
|
||||
});
|
||||
test('core.pathExistsInternal === coreUtils.pathExistsInternal', () => {
|
||||
assert.strictEqual(core.pathExistsInternal, coreUtils.pathExistsInternal);
|
||||
});
|
||||
test('core.generateSlugInternal === coreUtils.generateSlugInternal', () => {
|
||||
assert.strictEqual(core.generateSlugInternal, coreUtils.generateSlugInternal);
|
||||
});
|
||||
test('core.filterPlanFiles === coreUtils.filterPlanFiles', () => {
|
||||
assert.strictEqual(core.filterPlanFiles, coreUtils.filterPlanFiles);
|
||||
});
|
||||
test('core.filterSummaryFiles === coreUtils.filterSummaryFiles', () => {
|
||||
assert.strictEqual(core.filterSummaryFiles, coreUtils.filterSummaryFiles);
|
||||
});
|
||||
test('core.getPhaseFileStats === coreUtils.getPhaseFileStats', () => {
|
||||
assert.strictEqual(core.getPhaseFileStats, coreUtils.getPhaseFileStats);
|
||||
});
|
||||
test('core.readSubdirectories === coreUtils.readSubdirectories', () => {
|
||||
assert.strictEqual(core.readSubdirectories, coreUtils.readSubdirectories);
|
||||
});
|
||||
test('core.timeAgo === coreUtils.timeAgo', () => {
|
||||
assert.strictEqual(core.timeAgo, coreUtils.timeAgo);
|
||||
});
|
||||
test('extractCanonicalPlanId is NOT re-exported from core', () => {
|
||||
assert.strictEqual(typeof core.extractCanonicalPlanId, 'undefined');
|
||||
});
|
||||
test('extractCanonicalPlanId IS exported from coreUtils', () => {
|
||||
assert.strictEqual(typeof coreUtils.extractCanonicalPlanId, 'function');
|
||||
});
|
||||
});
|
||||
|
||||
// ─── toPosixPath ─────────────────────────────────────────────────────────────
|
||||
|
||||
describe('toPosixPath', () => {
|
||||
|
||||
2092
tests/core.test.cjs
2092
tests/core.test.cjs
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ const path = require('path');
|
||||
const os = require('os');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/config-loader.cjs');
|
||||
|
||||
/** Create a bare temp dir (no .planning/) to simulate pre-project context */
|
||||
function createBareTmpDir() {
|
||||
|
||||
@@ -120,7 +120,7 @@ describe('no-source-grep rule', () => {
|
||||
valid: [
|
||||
{
|
||||
code: `
|
||||
const mod = require('../gsd-core/bin/lib/core.cjs');
|
||||
const mod = require('../gsd-core/bin/lib/io.cjs');
|
||||
mod.someMethod();
|
||||
`,
|
||||
filename: 'tests/foo.test.cjs',
|
||||
|
||||
@@ -27,7 +27,7 @@ const path = require('node:path');
|
||||
|
||||
const {
|
||||
resolveModelInternal,
|
||||
} = require('../gsd-core/bin/lib/core.cjs');
|
||||
} = require('../gsd-core/bin/lib/model-resolver.cjs');
|
||||
const {
|
||||
AGENT_TO_PHASE_TYPE,
|
||||
VALID_PHASE_TYPES,
|
||||
@@ -254,7 +254,7 @@ describe('#3023 resolver: models.<phase_type> overrides profile-based tier', ()
|
||||
|
||||
// ─── #443 Unified effort: resolveEffortInternal + renderEffortForRuntime ────
|
||||
|
||||
const { resolveEffortInternal } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { resolveEffortInternal } = require('../gsd-core/bin/lib/model-resolver.cjs');
|
||||
const { renderEffortForRuntime } = require('../gsd-core/bin/lib/model-catalog.cjs');
|
||||
|
||||
describe('#3023 + #443: unified effort resolver (resolveEffortInternal) for Codex', () => {
|
||||
|
||||
@@ -49,7 +49,7 @@ const path = require('node:path');
|
||||
const {
|
||||
resolveModelInternal,
|
||||
resolveModelForTier,
|
||||
} = require('../gsd-core/bin/lib/core.cjs');
|
||||
} = require('../gsd-core/bin/lib/model-resolver.cjs');
|
||||
const {
|
||||
AGENT_DEFAULT_TIERS,
|
||||
VALID_AGENT_TIERS,
|
||||
|
||||
@@ -52,7 +52,7 @@ const {
|
||||
resolveFastModeInternal,
|
||||
resolveEffortForTier,
|
||||
VALID_EFFORTS,
|
||||
} = require('../gsd-core/bin/lib/core.cjs');
|
||||
} = require('../gsd-core/bin/lib/model-resolver.cjs');
|
||||
|
||||
const {
|
||||
renderEffortForRuntime,
|
||||
|
||||
@@ -25,7 +25,7 @@ const {
|
||||
resolveEffortInternal,
|
||||
resolveFastModeInternal,
|
||||
resolveEffortForTier,
|
||||
} = require('../gsd-core/bin/lib/core.cjs');
|
||||
} = require('../gsd-core/bin/lib/model-resolver.cjs');
|
||||
|
||||
const {
|
||||
renderEffortForRuntime,
|
||||
|
||||
@@ -63,8 +63,10 @@ const {
|
||||
resolveModelInternal,
|
||||
resolveModelPolicy,
|
||||
resolveModelForTier,
|
||||
} = require('../gsd-core/bin/lib/model-resolver.cjs');
|
||||
const {
|
||||
KNOWN_PROVIDERS,
|
||||
} = require('../gsd-core/bin/lib/core.cjs');
|
||||
} = require('../gsd-core/bin/lib/model-catalog.cjs');
|
||||
|
||||
// KNOWN_PROVIDERS must also be exported directly from model-catalog.cjs
|
||||
const modelCatalog = require('../gsd-core/bin/lib/model-catalog.cjs');
|
||||
|
||||
@@ -30,7 +30,7 @@ const path = require('node:path');
|
||||
const {
|
||||
resolveGranularityInternal,
|
||||
VALID_GRANULARITIES,
|
||||
} = require('../gsd-core/bin/lib/core.cjs');
|
||||
} = require('../gsd-core/bin/lib/model-resolver.cjs');
|
||||
const commands = require('../gsd-core/bin/lib/commands.cjs');
|
||||
const {
|
||||
VALID_PHASE_TYPES,
|
||||
|
||||
@@ -19,7 +19,6 @@ const os = require('node:os');
|
||||
const fs = require('node:fs');
|
||||
|
||||
const io = require('../gsd-core/bin/lib/io.cjs');
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
|
||||
// ─── ERROR_REASON constants ───────────────────────────────────────────────────
|
||||
|
||||
@@ -308,37 +307,6 @@ describe('reapStaleTempFiles (via io)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ─── core.cjs re-export shim parity ──────────────────────────────────────────
|
||||
|
||||
describe('core.cjs re-export shims', () => {
|
||||
test('core.output is the same function as io.output', () => {
|
||||
assert.strictEqual(core.output, io.output);
|
||||
});
|
||||
|
||||
test('core.error is the same function as io.error', () => {
|
||||
assert.strictEqual(core.error, io.error);
|
||||
});
|
||||
|
||||
test('core.ERROR_REASON is the same object as io.ERROR_REASON', () => {
|
||||
assert.strictEqual(core.ERROR_REASON, io.ERROR_REASON);
|
||||
});
|
||||
|
||||
test('core.setJsonErrorMode is the same function as io.setJsonErrorMode', () => {
|
||||
assert.strictEqual(core.setJsonErrorMode, io.setJsonErrorMode);
|
||||
});
|
||||
|
||||
test('core.getJsonErrorMode is the same function as io.getJsonErrorMode', () => {
|
||||
assert.strictEqual(core.getJsonErrorMode, io.getJsonErrorMode);
|
||||
});
|
||||
|
||||
test('core.reapStaleTempFiles is the same function as io.reapStaleTempFiles', () => {
|
||||
assert.strictEqual(core.reapStaleTempFiles, io.reapStaleTempFiles);
|
||||
});
|
||||
|
||||
test('core.GSD_TEMP_DIR is the same value as io.GSD_TEMP_DIR', () => {
|
||||
assert.strictEqual(core.GSD_TEMP_DIR, io.GSD_TEMP_DIR);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── bug #1008: output()/error() tolerate a full / slow non-blocking pipe ─────
|
||||
//
|
||||
|
||||
@@ -36,9 +36,11 @@ const {
|
||||
resolveModelInternal,
|
||||
resolveEffortInternal,
|
||||
resolveTierEntry,
|
||||
} = require('../gsd-core/bin/lib/model-resolver.cjs');
|
||||
const {
|
||||
RUNTIME_PROFILE_MAP,
|
||||
KNOWN_RUNTIMES,
|
||||
} = require('../gsd-core/bin/lib/core.cjs');
|
||||
} = require('../gsd-core/bin/lib/model-catalog.cjs');
|
||||
const { renderEffortForRuntime } = require('../gsd-core/bin/lib/model-catalog.cjs');
|
||||
const { isValidConfigKey } = require('../gsd-core/bin/lib/config-schema.cjs');
|
||||
|
||||
@@ -471,7 +473,7 @@ describe('issue #2517: VALID_CONFIG_KEYS schema', () => {
|
||||
|
||||
// ─── loadConfig validation warnings (review findings #10, #13) ──────────────
|
||||
describe('issue #2517: loadConfig warns on unknown runtime/tier (findings #10, #13)', () => {
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/config-loader.cjs');
|
||||
let tmpDir;
|
||||
let origWrite;
|
||||
let captured;
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
'use strict';
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
|
||||
/**
|
||||
* Tests for scripts/lint-core-spine-imports.cjs (issue #1268).
|
||||
*
|
||||
* Behavioural, no source-grep. Asserts on structured return values from the
|
||||
* exported pure functions and on process exit codes from the CLI.
|
||||
*/
|
||||
|
||||
const { test, describe, afterEach } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const SCRIPT_PATH = path.join(ROOT, 'scripts', 'lint-core-spine-imports.cjs');
|
||||
const ALLOWLIST_PATH = path.join(ROOT, 'scripts', 'lint-core-spine-imports.allowlist.json');
|
||||
|
||||
const { scanCoreSpineImports, loadAllowlist } = require(SCRIPT_PATH);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Pure-function tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('lint-core-spine-imports: scanCoreSpineImports (pure)', () => {
|
||||
let tmpDir;
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(tmpDir);
|
||||
tmpDir = undefined;
|
||||
});
|
||||
|
||||
test('happy path: allowlisted importer is not a violation', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
// File that imports the core spine
|
||||
const importerPath = path.join(tmpDir, 'importer.cts');
|
||||
fs.writeFileSync(importerPath, [
|
||||
"import core = require('./core.cjs');",
|
||||
'export = { x: 1 };',
|
||||
].join('\n'), 'utf8');
|
||||
|
||||
// File that does NOT import the core spine
|
||||
const cleanPath = path.join(tmpDir, 'clean.cts');
|
||||
fs.writeFileSync(cleanPath, [
|
||||
"import io = require('./io.cjs');",
|
||||
'export = { y: 2 };',
|
||||
].join('\n'), 'utf8');
|
||||
|
||||
// Allowlist contains the importer's repo-relative path
|
||||
const importerRel = path.relative(ROOT, importerPath).replace(/\\/g, '/');
|
||||
const allowlistSet = new Set([importerRel]);
|
||||
|
||||
const violations = scanCoreSpineImports([tmpDir], allowlistSet);
|
||||
assert.deepEqual(violations, []);
|
||||
});
|
||||
|
||||
test('violation path: non-allowlisted importer is returned with correct file and 1-based line', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
// File with a blank first line, then the core import on line 2
|
||||
const importerPath = path.join(tmpDir, 'new-squatter.cts');
|
||||
fs.writeFileSync(importerPath, [
|
||||
'// a comment',
|
||||
"import core = require('./core.cjs');",
|
||||
'export = {};',
|
||||
].join('\n'), 'utf8');
|
||||
|
||||
// Empty allowlist — importer is NOT allowed
|
||||
const violations = scanCoreSpineImports([tmpDir], new Set());
|
||||
|
||||
assert.equal(violations.length, 1, 'expected exactly one violation');
|
||||
const v = violations[0];
|
||||
// File path is repo-relative POSIX
|
||||
const expectedRel = path.relative(ROOT, importerPath).replace(/\\/g, '/');
|
||||
assert.equal(v.file, expectedRel);
|
||||
// Line 2 (1-based): the second line contains the require
|
||||
assert.equal(v.line, 2);
|
||||
});
|
||||
|
||||
test('require("../lib/core.cjs") form is detected', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
const f = path.join(tmpDir, 'leaf.cjs');
|
||||
fs.writeFileSync(f, 'const core = require("../lib/core.cjs");\n', 'utf8');
|
||||
|
||||
const violations = scanCoreSpineImports([tmpDir], new Set());
|
||||
assert.equal(violations.length, 1);
|
||||
assert.equal(violations[0].line, 1);
|
||||
});
|
||||
|
||||
test('require("./lib/core.cjs") form is detected', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
const f = path.join(tmpDir, 'leaf.cjs');
|
||||
fs.writeFileSync(f, 'const core = require("./lib/core.cjs");\n', 'utf8');
|
||||
|
||||
const violations = scanCoreSpineImports([tmpDir], new Set());
|
||||
assert.equal(violations.length, 1);
|
||||
});
|
||||
|
||||
test('clean file (no spine import) is never a violation', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
const f = path.join(tmpDir, 'pure.cts');
|
||||
fs.writeFileSync(f, [
|
||||
"import io = require('./io.cjs');",
|
||||
"import roadmap = require('./roadmap-parser.cjs');",
|
||||
].join('\n'), 'utf8');
|
||||
|
||||
const violations = scanCoreSpineImports([tmpDir], new Set());
|
||||
assert.deepEqual(violations, []);
|
||||
});
|
||||
|
||||
test('commented-out core require is NOT a violation', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
const f = path.join(tmpDir, 'commented.cts');
|
||||
fs.writeFileSync(f, [
|
||||
"// import core = require('./core.cjs');",
|
||||
"// const core = require('../lib/core.cjs');",
|
||||
"import io = require('./io.cjs');",
|
||||
].join('\n'), 'utf8');
|
||||
|
||||
const violations = scanCoreSpineImports([tmpDir], new Set());
|
||||
assert.deepEqual(violations, [], 'commented-out requires should not count as violations');
|
||||
});
|
||||
|
||||
test('unconventional relative path ../lib/core.cjs IS detected', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
const f = path.join(tmpDir, 'deep-leaf.cjs');
|
||||
fs.writeFileSync(f, "const core = require('../lib/core.cjs');\n", 'utf8');
|
||||
|
||||
const violations = scanCoreSpineImports([tmpDir], new Set());
|
||||
assert.equal(violations.length, 1, 'expected exactly one violation for ../lib/core.cjs');
|
||||
assert.equal(violations[0].line, 1);
|
||||
});
|
||||
|
||||
test('core-utils.cjs and core-schema.cjs are NOT detected as core spine imports', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
const f = path.join(tmpDir, 'sibling.cts');
|
||||
fs.writeFileSync(f, [
|
||||
"import coreUtils = require('./core-utils.cjs');",
|
||||
"import coreSchema = require('./core-schema.cjs');",
|
||||
].join('\n'), 'utf8');
|
||||
|
||||
const violations = scanCoreSpineImports([tmpDir], new Set());
|
||||
assert.deepEqual(violations, [], 'core-utils.cjs and core-schema.cjs should not match');
|
||||
});
|
||||
|
||||
test('dynamic import("./core.cjs") form is detected', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
const f = path.join(tmpDir, 'dyn-importer.cjs');
|
||||
fs.writeFileSync(f, [
|
||||
'// dynamic import form',
|
||||
"const core = await import('./core.cjs');",
|
||||
].join('\n'), 'utf8');
|
||||
|
||||
const violations = scanCoreSpineImports([tmpDir], new Set());
|
||||
assert.equal(violations.length, 1, 'expected exactly one violation for dynamic import');
|
||||
assert.equal(violations[0].line, 2);
|
||||
});
|
||||
|
||||
test('bare side-effect import \'./core.cjs\' form is detected', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
const f = path.join(tmpDir, 'side-effect-importer.cjs');
|
||||
fs.writeFileSync(f, [
|
||||
'// bare side-effect import form',
|
||||
"import './core.cjs';",
|
||||
].join('\n'), 'utf8');
|
||||
|
||||
const violations = scanCoreSpineImports([tmpDir], new Set());
|
||||
assert.equal(violations.length, 1, 'expected exactly one violation for bare side-effect import');
|
||||
assert.equal(violations[0].line, 2);
|
||||
});
|
||||
|
||||
test('TS import x = require("./core.cjs") form is still flagged', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
const f = path.join(tmpDir, 'ts-require.cts');
|
||||
fs.writeFileSync(f, "import core = require('./core.cjs');\n", 'utf8');
|
||||
|
||||
const violations = scanCoreSpineImports([tmpDir], new Set());
|
||||
assert.equal(violations.length, 1, 'TS import = require() should still be flagged');
|
||||
assert.equal(violations[0].line, 1);
|
||||
});
|
||||
|
||||
test('import of core-utils.cjs (sibling) is NOT flagged even with new alternations', () => {
|
||||
tmpDir = createTempDir('lint-spine-test-');
|
||||
|
||||
const f = path.join(tmpDir, 'sibling-check.cjs');
|
||||
fs.writeFileSync(f, [
|
||||
"const u = require('./core-utils.cjs');",
|
||||
"const cu = await import('./core-utils.cjs');",
|
||||
"import './core-utils.cjs';",
|
||||
].join('\n'), 'utf8');
|
||||
|
||||
const violations = scanCoreSpineImports([tmpDir], new Set());
|
||||
assert.deepEqual(violations, [], 'core-utils.cjs imports should not match even with new alternations');
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// loadAllowlist
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('lint-core-spine-imports: loadAllowlist', () => {
|
||||
let tmpDir;
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(tmpDir);
|
||||
tmpDir = undefined;
|
||||
});
|
||||
|
||||
test('returns a Set of the allow entries', () => {
|
||||
tmpDir = createTempDir('lint-spine-al-');
|
||||
const jsonPath = path.join(tmpDir, 'allowlist.json');
|
||||
fs.writeFileSync(jsonPath, JSON.stringify({ allow: ['src/foo.cts', 'src/bar.cts'] }), 'utf8');
|
||||
|
||||
const s = loadAllowlist(jsonPath);
|
||||
assert.ok(s instanceof Set);
|
||||
assert.ok(s.has('src/foo.cts'));
|
||||
assert.ok(s.has('src/bar.cts'));
|
||||
assert.equal(s.size, 2);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// T0-green / allowlist-completeness guard (real repo)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('lint-core-spine-imports: T0 allowlist covers all current importers', () => {
|
||||
test('scanCoreSpineImports against real src/ + gsd-core/bin/ returns [] with the shipped allowlist', () => {
|
||||
const roots = [
|
||||
path.join(ROOT, 'src'),
|
||||
path.join(ROOT, 'gsd-core', 'bin'),
|
||||
];
|
||||
const allowlistSet = loadAllowlist(ALLOWLIST_PATH);
|
||||
const violations = scanCoreSpineImports(roots, allowlistSet);
|
||||
|
||||
assert.deepEqual(
|
||||
violations,
|
||||
[],
|
||||
'T0 allowlist is incomplete — new core-spine importers detected:\n' +
|
||||
violations.map((v) => ` ${v.file}:${v.line}`).join('\n'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CLI contract
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('lint-core-spine-imports: CLI', () => {
|
||||
test('clean repo → exit 0 and stdout contains the structured ok line', () => {
|
||||
const result = spawnSync(process.execPath, [SCRIPT_PATH], {
|
||||
cwd: ROOT,
|
||||
encoding: 'utf8',
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
result.status,
|
||||
0,
|
||||
`expected exit 0 but got ${result.status}.\nstdout: ${result.stdout}\nstderr: ${result.stderr}`,
|
||||
);
|
||||
assert.ok(
|
||||
result.stdout.includes('ok core-spine-imports:'),
|
||||
`stdout should contain "ok core-spine-imports:", got: ${result.stdout}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -8,7 +8,7 @@ const assert = require('node:assert/strict');
|
||||
const {
|
||||
getMilestoneFromPhaseId,
|
||||
getPhaseDirFromPhaseId,
|
||||
} = require('../gsd-core/bin/lib/core.cjs');
|
||||
} = require('../gsd-core/bin/lib/phase-id.cjs');
|
||||
|
||||
// ─── getMilestoneFromPhaseId ────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const { MODEL_ALIAS_MAP } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { MODEL_ALIAS_MAP } = require('../gsd-core/bin/lib/model-catalog.cjs');
|
||||
|
||||
describe('MODEL_ALIAS_MAP (#1690 regression)', () => {
|
||||
test('opus maps to claude-opus-4-8', () => {
|
||||
|
||||
@@ -29,7 +29,6 @@ const { cleanup } = require('./helpers.cjs');
|
||||
// ─── modules under test ───────────────────────────────────────────────────────
|
||||
|
||||
const modelResolver = require('../gsd-core/bin/lib/model-resolver.cjs');
|
||||
const coreModule = require('../gsd-core/bin/lib/core.cjs');
|
||||
|
||||
const {
|
||||
resolveTierEntry,
|
||||
@@ -60,61 +59,6 @@ function writeConfig(tmpDir, obj) {
|
||||
fs.writeFileSync(configPath, JSON.stringify(obj, null, 2), 'utf-8');
|
||||
}
|
||||
|
||||
// ─── shim identity ────────────────────────────────────────────────────────────
|
||||
|
||||
describe('model-resolver shim identity', () => {
|
||||
test('core.resolveTierEntry === modelResolver.resolveTierEntry', () => {
|
||||
assert.strictEqual(coreModule.resolveTierEntry, modelResolver.resolveTierEntry);
|
||||
});
|
||||
|
||||
test('core.resolveModelPolicy === modelResolver.resolveModelPolicy', () => {
|
||||
assert.strictEqual(coreModule.resolveModelPolicy, modelResolver.resolveModelPolicy);
|
||||
});
|
||||
|
||||
test('core.resolveModelInternal === modelResolver.resolveModelInternal', () => {
|
||||
assert.strictEqual(coreModule.resolveModelInternal, modelResolver.resolveModelInternal);
|
||||
});
|
||||
|
||||
test('core.VALID_GRANULARITIES === modelResolver.VALID_GRANULARITIES (same Set object)', () => {
|
||||
assert.strictEqual(coreModule.VALID_GRANULARITIES, modelResolver.VALID_GRANULARITIES);
|
||||
});
|
||||
|
||||
test('core.resolveGranularityInternal === modelResolver.resolveGranularityInternal', () => {
|
||||
assert.strictEqual(coreModule.resolveGranularityInternal, modelResolver.resolveGranularityInternal);
|
||||
});
|
||||
|
||||
test('core.assertValidGranularityOverride === modelResolver.assertValidGranularityOverride', () => {
|
||||
assert.strictEqual(coreModule.assertValidGranularityOverride, modelResolver.assertValidGranularityOverride);
|
||||
});
|
||||
|
||||
test('core.resolveModelForTier === modelResolver.resolveModelForTier', () => {
|
||||
assert.strictEqual(coreModule.resolveModelForTier, modelResolver.resolveModelForTier);
|
||||
});
|
||||
|
||||
test('core.VALID_EFFORTS === modelResolver.VALID_EFFORTS (same Array object)', () => {
|
||||
assert.strictEqual(coreModule.VALID_EFFORTS, modelResolver.VALID_EFFORTS);
|
||||
});
|
||||
|
||||
test('core.EFFORT_SET === modelResolver.EFFORT_SET (same Set object)', () => {
|
||||
assert.strictEqual(coreModule.EFFORT_SET, modelResolver.EFFORT_SET);
|
||||
});
|
||||
|
||||
test('core.nextEffort === modelResolver.nextEffort', () => {
|
||||
assert.strictEqual(coreModule.nextEffort, modelResolver.nextEffort);
|
||||
});
|
||||
|
||||
test('core.resolveEffortInternal === modelResolver.resolveEffortInternal', () => {
|
||||
assert.strictEqual(coreModule.resolveEffortInternal, modelResolver.resolveEffortInternal);
|
||||
});
|
||||
|
||||
test('core.resolveFastModeInternal === modelResolver.resolveFastModeInternal', () => {
|
||||
assert.strictEqual(coreModule.resolveFastModeInternal, modelResolver.resolveFastModeInternal);
|
||||
});
|
||||
|
||||
test('core.resolveEffortForTier === modelResolver.resolveEffortForTier', () => {
|
||||
assert.strictEqual(coreModule.resolveEffortForTier, modelResolver.resolveEffortForTier);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── resolveModelInternal ─────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ const os = require('node:os');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
// Import loadConfig directly (sync, no CLI subprocess needed)
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/config-loader.cjs');
|
||||
|
||||
// ─── helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const phaseId = require('../gsd-core/bin/lib/phase-id.cjs');
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
|
||||
// ─── escapeRegex ─────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -388,42 +387,3 @@ describe('getPhaseDirFromPhaseId', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ─── core.cjs re-export shim identity assertions ──────────────────────────────
|
||||
|
||||
describe('core.cjs re-export shim identity (single instance)', () => {
|
||||
test('core.escapeRegex === phaseId.escapeRegex', () => {
|
||||
assert.strictEqual(core.escapeRegex, phaseId.escapeRegex);
|
||||
});
|
||||
|
||||
test('core.normalizePhaseName === phaseId.normalizePhaseName', () => {
|
||||
assert.strictEqual(core.normalizePhaseName, phaseId.normalizePhaseName);
|
||||
});
|
||||
|
||||
test('core.comparePhaseNum === phaseId.comparePhaseNum', () => {
|
||||
assert.strictEqual(core.comparePhaseNum, phaseId.comparePhaseNum);
|
||||
});
|
||||
|
||||
test('core.extractPhaseToken === phaseId.extractPhaseToken', () => {
|
||||
assert.strictEqual(core.extractPhaseToken, phaseId.extractPhaseToken);
|
||||
});
|
||||
|
||||
test('core.phaseTokenMatches === phaseId.phaseTokenMatches', () => {
|
||||
assert.strictEqual(core.phaseTokenMatches, phaseId.phaseTokenMatches);
|
||||
});
|
||||
|
||||
test('core.phaseMarkdownRegexSource === phaseId.phaseMarkdownRegexSource', () => {
|
||||
assert.strictEqual(core.phaseMarkdownRegexSource, phaseId.phaseMarkdownRegexSource);
|
||||
});
|
||||
|
||||
test('core.phaseMarkdownRegexSourceExact === phaseId.phaseMarkdownRegexSourceExact', () => {
|
||||
assert.strictEqual(core.phaseMarkdownRegexSourceExact, phaseId.phaseMarkdownRegexSourceExact);
|
||||
});
|
||||
|
||||
test('core.getMilestoneFromPhaseId === phaseId.getMilestoneFromPhaseId', () => {
|
||||
assert.strictEqual(core.getMilestoneFromPhaseId, phaseId.getMilestoneFromPhaseId);
|
||||
});
|
||||
|
||||
test('core.getPhaseDirFromPhaseId === phaseId.getPhaseDirFromPhaseId', () => {
|
||||
assert.strictEqual(core.getPhaseDirFromPhaseId, phaseId.getPhaseDirFromPhaseId);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,25 +24,8 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const phaseLocator = require('../gsd-core/bin/lib/phase-locator.cjs');
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
// ─── Shim-identity assertions ─────────────────────────────────────────────────
|
||||
|
||||
describe('phase-locator: shim-identity — core.cjs re-exports same function objects', () => {
|
||||
test('core.findPhaseInternal === phaseLocator.findPhaseInternal', () => {
|
||||
assert.strictEqual(core.findPhaseInternal, phaseLocator.findPhaseInternal);
|
||||
});
|
||||
|
||||
test('core.getArchivedPhaseDirs === phaseLocator.getArchivedPhaseDirs', () => {
|
||||
assert.strictEqual(core.getArchivedPhaseDirs, phaseLocator.getArchivedPhaseDirs);
|
||||
});
|
||||
|
||||
test('core.searchPhaseInDir === phaseLocator.searchPhaseInDir', () => {
|
||||
assert.strictEqual(core.searchPhaseInDir, phaseLocator.searchPhaseInDir);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── findPhaseInternal — basic active-phase lookup ────────────────────────────
|
||||
|
||||
describe('findPhaseInternal: active phase lookup', () => {
|
||||
|
||||
@@ -2848,7 +2848,7 @@ Plans:
|
||||
// comparePhaseNum and normalizePhaseName (imported directly)
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
const { comparePhaseNum, normalizePhaseName } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { comparePhaseNum, normalizePhaseName } = require('../gsd-core/bin/lib/phase-id.cjs');
|
||||
|
||||
describe('comparePhaseNum', () => {
|
||||
test('sorts integer phases numerically', () => {
|
||||
|
||||
@@ -15,7 +15,7 @@ const {
|
||||
setActiveWorkstream,
|
||||
} = require('../gsd-core/bin/lib/planning-workspace.cjs');
|
||||
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
const planningWorkspaceDirect = require('../gsd-core/bin/lib/planning-workspace.cjs');
|
||||
|
||||
describe('planning-workspace: planningDir/planningPaths parity', () => {
|
||||
const cwd = '/fake/repo';
|
||||
@@ -147,7 +147,7 @@ describe('planning-workspace: lock seam', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('core compatibility adapter: planning workspace functions', () => {
|
||||
describe('planning-workspace direct: functions expose matching behavior', () => {
|
||||
let savedSession;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -160,26 +160,26 @@ describe('core compatibility adapter: planning workspace functions', () => {
|
||||
else delete process.env.GSD_SESSION_KEY;
|
||||
});
|
||||
|
||||
test('core and planning-workspace expose matching behavior', () => {
|
||||
test('planning-workspace functions work consistently', () => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-core-compat-'));
|
||||
try {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'workstreams', 'alpha'), { recursive: true });
|
||||
|
||||
core.setActiveWorkstream(tmpDir, 'alpha');
|
||||
assert.strictEqual(core.getActiveWorkstream(tmpDir), 'alpha');
|
||||
planningWorkspaceDirect.setActiveWorkstream(tmpDir, 'alpha');
|
||||
assert.strictEqual(planningWorkspaceDirect.getActiveWorkstream(tmpDir), 'alpha');
|
||||
assert.strictEqual(getActiveWorkstream(tmpDir), 'alpha');
|
||||
|
||||
assert.strictEqual(
|
||||
core.planningDir(tmpDir, 'feature-x', 'my-project'),
|
||||
planningWorkspaceDirect.planningDir(tmpDir, 'feature-x', 'my-project'),
|
||||
planningDir(tmpDir, 'feature-x', 'my-project')
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
core.planningPaths(tmpDir, 'feature-x'),
|
||||
planningWorkspaceDirect.planningPaths(tmpDir, 'feature-x'),
|
||||
planningPaths(tmpDir, 'feature-x')
|
||||
);
|
||||
|
||||
setActiveWorkstream(tmpDir, null);
|
||||
assert.strictEqual(core.getActiveWorkstream(tmpDir), null);
|
||||
assert.strictEqual(planningWorkspaceDirect.getActiveWorkstream(tmpDir), null);
|
||||
} finally {
|
||||
cleanup(tmpDir);
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ describe('workflow.post_planning_gaps config (#2493)', () => {
|
||||
|
||||
test('CONFIG_DEFAULTS contains post_planning_gaps default true', () => {
|
||||
// CONFIG_DEFAULTS is exported from core.cjs
|
||||
const { CONFIG_DEFAULTS } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { CONFIG_DEFAULTS } = require('../gsd-core/bin/lib/config-loader.cjs');
|
||||
assert.strictEqual(CONFIG_DEFAULTS.post_planning_gaps, true);
|
||||
});
|
||||
|
||||
@@ -419,7 +419,7 @@ describe('workflow.post_planning_gaps config (#2493)', () => {
|
||||
// in its return so callers can read config.post_planning_gaps regardless of whether
|
||||
// config.json exists, has the workflow section, or sets the flat key.
|
||||
test('loadConfig() returns post_planning_gaps default true when key absent', () => {
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/config-loader.cjs');
|
||||
runGsdTools('config-ensure-section', tmpDir);
|
||||
// Remove the key to simulate older configs that pre-date the toggle
|
||||
const cfgPath = path.join(tmpDir, '.planning', 'config.json');
|
||||
@@ -431,7 +431,7 @@ describe('workflow.post_planning_gaps config (#2493)', () => {
|
||||
});
|
||||
|
||||
test('loadConfig() returns post_planning_gaps:false when workflow.post_planning_gaps=false', () => {
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/config-loader.cjs');
|
||||
runGsdTools('config-ensure-section', tmpDir);
|
||||
runGsdTools(['config-set', 'workflow.post_planning_gaps', 'false'], tmpDir);
|
||||
const config = loadConfig(tmpDir);
|
||||
@@ -439,7 +439,7 @@ describe('workflow.post_planning_gaps config (#2493)', () => {
|
||||
});
|
||||
|
||||
test('loadConfig() returns post_planning_gaps:true when workflow.post_planning_gaps=true', () => {
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/config-loader.cjs');
|
||||
runGsdTools('config-ensure-section', tmpDir);
|
||||
runGsdTools(['config-set', 'workflow.post_planning_gaps', 'true'], tmpDir);
|
||||
const config = loadConfig(tmpDir);
|
||||
|
||||
@@ -16,7 +16,7 @@ const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
|
||||
// Lazy-loaded so tests can fail clearly when the export doesn't exist yet.
|
||||
function getPruneOrphanedWorktrees() {
|
||||
const { pruneOrphanedWorktrees } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { pruneOrphanedWorktrees } = require('../gsd-core/bin/lib/worktree-safety.cjs');
|
||||
return pruneOrphanedWorktrees;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const roadmapParser = require('../gsd-core/bin/lib/roadmap-parser.cjs');
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const {
|
||||
@@ -46,28 +45,6 @@ function writeState(tmpDir, fields) {
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), lines.join('\n') + '\n');
|
||||
}
|
||||
|
||||
// ─── Shim-identity assertions ─────────────────────────────────────────────────
|
||||
|
||||
describe('roadmap-parser: shim-identity — core.cjs re-exports same function objects', () => {
|
||||
test('core.extractCurrentMilestone === roadmapParser.extractCurrentMilestone', () => {
|
||||
assert.strictEqual(core.extractCurrentMilestone, roadmapParser.extractCurrentMilestone);
|
||||
});
|
||||
test('core.stripShippedMilestones === roadmapParser.stripShippedMilestones', () => {
|
||||
assert.strictEqual(core.stripShippedMilestones, roadmapParser.stripShippedMilestones);
|
||||
});
|
||||
test('core.replaceInCurrentMilestone === roadmapParser.replaceInCurrentMilestone', () => {
|
||||
assert.strictEqual(core.replaceInCurrentMilestone, roadmapParser.replaceInCurrentMilestone);
|
||||
});
|
||||
test('core.getRoadmapPhaseInternal === roadmapParser.getRoadmapPhaseInternal', () => {
|
||||
assert.strictEqual(core.getRoadmapPhaseInternal, roadmapParser.getRoadmapPhaseInternal);
|
||||
});
|
||||
test('core.getMilestoneInfo === roadmapParser.getMilestoneInfo', () => {
|
||||
assert.strictEqual(core.getMilestoneInfo, roadmapParser.getMilestoneInfo);
|
||||
});
|
||||
test('core.getMilestonePhaseFilter === roadmapParser.getMilestonePhaseFilter', () => {
|
||||
assert.strictEqual(core.getMilestonePhaseFilter, roadmapParser.getMilestonePhaseFilter);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── stripShippedMilestones ───────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ describe('roadmap get-phase fallback to full ROADMAP.md (#1634)', () => {
|
||||
// Regression: phase heading like "### Phase 12: v1.0 Tech-Debt Closure"
|
||||
// was incorrectly treated as a milestone boundary because the greedy
|
||||
// `.*v\d+\.\d+` subpattern in nextMilestonePattern matched it.
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
const core = require('../gsd-core/bin/lib/roadmap-parser.cjs');
|
||||
writeState(tmpDir, 'v1.1');
|
||||
const roadmap = `# Roadmap
|
||||
|
||||
@@ -262,7 +262,7 @@ describe('roadmap get-phase fallback to full ROADMAP.md (#1634)', () => {
|
||||
test('extractCurrentMilestone handles PHASE/phase (case-insensitive) containing vX.Y (#2619 follow-up)', () => {
|
||||
// CodeRabbit follow-up: the negative lookahead `(?!Phase\s+\S)` must be
|
||||
// case-insensitive so PHASE/phase variants are also excluded.
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
const core = require('../gsd-core/bin/lib/roadmap-parser.cjs');
|
||||
writeState(tmpDir, 'v1.1');
|
||||
const roadmap = `# Roadmap
|
||||
|
||||
@@ -333,7 +333,7 @@ This phase covers:
|
||||
|
||||
describe('extractCurrentMilestone — closed-sibling heading selection (#145)', () => {
|
||||
let tmpDir;
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
const core = require('../gsd-core/bin/lib/roadmap-parser.cjs');
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = createTempProject();
|
||||
@@ -656,7 +656,7 @@ This is the only milestone.
|
||||
|
||||
describe('extractCurrentMilestone — boundary / active-override hardening (#145 follow-up)', () => {
|
||||
let tmpDir;
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
const core = require('../gsd-core/bin/lib/roadmap-parser.cjs');
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = createTempProject();
|
||||
|
||||
@@ -14,7 +14,7 @@ const os = require('os');
|
||||
|
||||
const {
|
||||
reapStaleTempFiles,
|
||||
} = require('../gsd-core/bin/lib/core.cjs');
|
||||
} = require('../gsd-core/bin/lib/io.cjs');
|
||||
|
||||
const GSD_TEMP_DIR = path.join(os.tmpdir(), 'gsd');
|
||||
|
||||
@@ -121,14 +121,14 @@ describe('dedicated gsd temp subdirectory', () => {
|
||||
|
||||
// The legacy reap function should still clean old-location files
|
||||
// We import it if exported, or verify the main reap handles both
|
||||
const core = require('../gsd-core/bin/lib/core.cjs');
|
||||
if (typeof core.reapStaleTempFilesLegacy === 'function') {
|
||||
core.reapStaleTempFilesLegacy(testPrefix, { maxAgeMs: 5 * 60 * 1000 });
|
||||
const ioModule = require('../gsd-core/bin/lib/io.cjs');
|
||||
if (typeof ioModule.reapStaleTempFilesLegacy === 'function') {
|
||||
ioModule.reapStaleTempFilesLegacy(testPrefix, { maxAgeMs: 5 * 60 * 1000 });
|
||||
assert.ok(!fs.existsSync(oldLocationPath), 'legacy reap should clean old location');
|
||||
} else {
|
||||
// If no separate legacy function, the main output() should do a one-time
|
||||
// migration sweep. We just verify the export shape is correct.
|
||||
assert.ok(typeof core.reapStaleTempFiles === 'function');
|
||||
assert.ok(typeof ioModule.reapStaleTempFiles === 'function');
|
||||
// Clean up manually since we're not testing migration here
|
||||
fs.unlinkSync(oldLocationPath);
|
||||
}
|
||||
|
||||
@@ -833,7 +833,7 @@ describe('path traversal rejection', () => {
|
||||
});
|
||||
|
||||
describe('setActiveWorkstream rejects invalid names directly', () => {
|
||||
const { setActiveWorkstream } = require('../gsd-core/bin/lib/core.cjs');
|
||||
const { setActiveWorkstream } = require('../gsd-core/bin/lib/planning-workspace.cjs');
|
||||
for (const name of maliciousNames) {
|
||||
test(`throws for ${name}`, () => {
|
||||
assert.throws(
|
||||
|
||||
Reference in New Issue
Block a user