From 645601a10db20875ee772e3ca536de6201760681 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Mon, 15 Jun 2026 17:03:22 -0400 Subject: [PATCH] =?UTF-8?q?refactor(#1286):=20T4=20=E2=80=94=20migrate=205?= =?UTF-8?q?=20large=20destructure=20callers=20off=20the=20core=20spine=20(?= =?UTF-8?q?batch=203)=20(#1287)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate the entire core surface of commands (~23 symbols), phase (~17), roadmap, state, template to the leaf modules directly (behaviour-identical — leaves are the objects core re-exports by reference). All 5 now import zero core symbols and are removed from the allowlist (9 -> 4). Dropped a dead `void replaceInCurrentMilestone` from phase.cts; stale core.cjs docstrings fixed. core.cts re-exports untouched (serve the remaining 4 idiom-hard files); teardown is T-final. No behaviour change. Closes #1286 Co-authored-by: Claude Opus 4.8 --- .../lint-core-spine-imports.allowlist.json | 7 +-- src/commands.cts | 46 ++++++++----------- src/phase.cts | 46 ++++++++----------- src/roadmap.cts | 13 ++++-- src/state.cts | 13 +++++- src/template.cts | 13 +++++- 6 files changed, 70 insertions(+), 68 deletions(-) diff --git a/scripts/lint-core-spine-imports.allowlist.json b/scripts/lint-core-spine-imports.allowlist.json index b16d4b4e5..14ca24a72 100644 --- a/scripts/lint-core-spine-imports.allowlist.json +++ b/scripts/lint-core-spine-imports.allowlist.json @@ -4,11 +4,6 @@ "gsd-core/bin/gsd-tools.cjs", "src/audit-command-router.cts", "src/check-command-router.cts", - "src/commands.cts", - "src/intel-command-router.cts", - "src/phase.cts", - "src/roadmap.cts", - "src/state.cts", - "src/template.cts" + "src/intel-command-router.cts" ] } diff --git a/src/commands.cts b/src/commands.cts index 6560c0f55..5414e50cc 100644 --- a/src/commands.cts +++ b/src/commands.cts @@ -10,32 +10,26 @@ import fs from 'node:fs'; import path from 'node:path'; import { execGit, platformWriteSync, platformReadSync, platformEnsureDir } from './shell-command-projection.cjs'; // eslint-disable-next-line @typescript-eslint/no-require-imports -import core = require('./core.cjs'); -const { - loadConfig, - isGitIgnored, - normalizePhaseName, - comparePhaseNum, - getArchivedPhaseDirs, - generateSlugInternal, - getMilestoneInfo, - getMilestonePhaseFilter, - resolveModelInternal, - resolveEffortInternal, - resolveFastModeInternal, - resolveEffortForTier, - stripShippedMilestones: _stripShippedMilestones, - extractCurrentMilestone, - toPosixPath, - output, - error, - findPhaseInternal, - extractOneLinerFromBody, - getRoadmapPhaseInternal, - extractPhaseToken, - resolveGranularityInternal, - assertValidGranularityOverride, -} = core; +import ioMod = require('./io.cjs'); +const { output, error } = ioMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import configLoaderMod = require('./config-loader.cjs'); +const { loadConfig, isGitIgnored } = configLoaderMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import coreUtilsMod = require('./core-utils.cjs'); +const { toPosixPath, generateSlugInternal, extractOneLinerFromBody } = coreUtilsMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import phaseIdMod = require('./phase-id.cjs'); +const { normalizePhaseName, comparePhaseNum, extractPhaseToken } = phaseIdMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import phaseLocatorMod = require('./phase-locator.cjs'); +const { getArchivedPhaseDirs, findPhaseInternal } = phaseLocatorMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import roadmapParserMod = require('./roadmap-parser.cjs'); +const { extractCurrentMilestone, stripShippedMilestones: _stripShippedMilestones, getMilestoneInfo, getMilestonePhaseFilter, getRoadmapPhaseInternal } = roadmapParserMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import modelResolverMod = require('./model-resolver.cjs'); +const { resolveModelInternal, resolveEffortInternal, resolveFastModeInternal, resolveEffortForTier, resolveGranularityInternal, assertValidGranularityOverride } = modelResolverMod; import { renderEffortForRuntime, RUNTIMES_WITH_FAST_MODE } from './model-catalog.cjs'; // eslint-disable-next-line @typescript-eslint/no-require-imports import planningWorkspace = require('./planning-workspace.cjs'); diff --git a/src/phase.cts b/src/phase.cts index 122afcbf0..576c1ccc6 100644 --- a/src/phase.cts +++ b/src/phase.cts @@ -18,8 +18,24 @@ import fs from 'node:fs'; import path from 'node:path'; -// eslint-disable-next-line @typescript-eslint/no-require-imports -- core.cjs is an export= CommonJS module -import core = require('./core.cjs'); +// eslint-disable-next-line @typescript-eslint/no-require-imports -- io.cjs is an export= CommonJS module +import ioMod = require('./io.cjs'); +const { output, error, ERROR_REASON } = ioMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports -- config-loader.cjs is an export= CommonJS module +import configLoaderMod = require('./config-loader.cjs'); +const { loadConfig } = configLoaderMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports -- core-utils.cjs is an export= CommonJS module +import coreUtilsMod = require('./core-utils.cjs'); +const { toPosixPath, generateSlugInternal, readSubdirectories } = coreUtilsMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-id.cjs is an export= CommonJS module +import phaseIdMod = require('./phase-id.cjs'); +const { escapeRegex, normalizePhaseName, phaseMarkdownRegexSource, comparePhaseNum, phaseTokenMatches } = phaseIdMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-locator.cjs is an export= CommonJS module +import phaseLocatorMod = require('./phase-locator.cjs'); +const { findPhaseInternal, getArchivedPhaseDirs } = phaseLocatorMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports -- roadmap-parser.cjs is an export= CommonJS module +import roadmapParserMod = require('./roadmap-parser.cjs'); +const { stripShippedMilestones, extractCurrentMilestone, getMilestonePhaseFilter } = roadmapParserMod; // eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module import planningWorkspace = require('./planning-workspace.cjs'); // eslint-disable-next-line @typescript-eslint/no-require-imports -- frontmatter.cjs is an export= CommonJS module @@ -34,27 +50,6 @@ import { realClock } from './clock.cjs'; import uatPredicate = require('./uat-predicate.cjs'); const { evaluateUatPassed } = uatPredicate; -const { - escapeRegex, - loadConfig, - normalizePhaseName, - phaseMarkdownRegexSource, - comparePhaseNum, - findPhaseInternal, - getArchivedPhaseDirs, - generateSlugInternal, - getMilestonePhaseFilter, - stripShippedMilestones, - extractCurrentMilestone, - replaceInCurrentMilestone, - toPosixPath, - output, - error, - readSubdirectories, - phaseTokenMatches, - ERROR_REASON, -} = core; - const { planningDir, withPlanningLock } = planningWorkspace; const { extractFrontmatter } = frontmatterMod; const { @@ -67,11 +62,6 @@ const { updatePerformanceMetricsSection, } = stateMod; -// Unused import silences TS — keep for structural parity with .cjs (stripShippedMilestones, -// replaceInCurrentMilestone are exported from core but only used in phase.cjs as-is). -void stripShippedMilestones; -void replaceInCurrentMilestone; - // #2893 — strict canonical filter: `{padded_phase}-{NN}-PLAN.md` or `PLAN.md`. const isCanonicalPlanFile = (f: string): boolean => f.endsWith('-PLAN.md') || f === 'PLAN.md'; diff --git a/src/roadmap.cts b/src/roadmap.cts index 5d208df4f..ab96cb63b 100644 --- a/src/roadmap.cts +++ b/src/roadmap.cts @@ -10,8 +10,14 @@ import fs from 'node:fs'; import path from 'node:path'; import { realClock } from './clock.cjs'; // eslint-disable-next-line @typescript-eslint/no-require-imports -import core = require('./core.cjs'); -const { escapeRegex, normalizePhaseName, phaseMarkdownRegexSource, phaseMarkdownRegexSourceExact, output, error, findPhaseInternal, phaseTokenMatches } = core; +import ioMod = require('./io.cjs'); +const { output, error } = ioMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import phaseIdMod = require('./phase-id.cjs'); +const { escapeRegex, normalizePhaseName, phaseMarkdownRegexSource, phaseMarkdownRegexSourceExact, phaseTokenMatches } = phaseIdMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import phaseLocatorMod = require('./phase-locator.cjs'); +const { findPhaseInternal } = phaseLocatorMod; // eslint-disable-next-line @typescript-eslint/no-require-imports import roadmapParserModule = require('./roadmap-parser.cjs'); const { stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone } = roadmapParserModule; @@ -98,8 +104,7 @@ function countPhasePlansAndSummaries(phaseDir: string): PhasePlansAndSummaries { }; } -// `phaseMarkdownRegexSource` moved to core.cjs (#3537) so phase.cjs and -// core.cjs itself can consume it without circular deps. Imported above. +// `phaseMarkdownRegexSource` lives in phase-id.cjs (#3537) and is imported above. // ─── searchPhaseInContent ───────────────────────────────────────────────────── diff --git a/src/state.cts b/src/state.cts index c1f54a902..4026af13d 100644 --- a/src/state.cts +++ b/src/state.cts @@ -9,8 +9,17 @@ import fs from 'node:fs'; import path from 'node:path'; // eslint-disable-next-line @typescript-eslint/no-require-imports -import core = require('./core.cjs'); -const { escapeRegex, loadConfig, getMilestoneInfo, getMilestonePhaseFilter, extractCurrentMilestone, output, error } = core; +import ioMod = require('./io.cjs'); +const { output, error } = ioMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import configLoaderMod = require('./config-loader.cjs'); +const { loadConfig } = configLoaderMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import phaseIdMod = require('./phase-id.cjs'); +const { escapeRegex } = phaseIdMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import roadmapParserMod = require('./roadmap-parser.cjs'); +const { getMilestoneInfo, getMilestonePhaseFilter, extractCurrentMilestone } = roadmapParserMod; import { platformWriteSync, platformReadSync, platformEnsureDir } from './shell-command-projection.cjs'; // eslint-disable-next-line @typescript-eslint/no-require-imports import planningWorkspace = require('./planning-workspace.cjs'); diff --git a/src/template.cts b/src/template.cts index d600dc4d2..d696b02a8 100644 --- a/src/template.cts +++ b/src/template.cts @@ -9,8 +9,17 @@ import fs from 'node:fs'; import path from 'node:path'; // eslint-disable-next-line @typescript-eslint/no-require-imports -import core = require('./core.cjs'); -const { normalizePhaseName, findPhaseInternal, generateSlugInternal, toPosixPath, output, error } = core; +import ioMod = require('./io.cjs'); +const { output, error } = ioMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import coreUtilsMod = require('./core-utils.cjs'); +const { toPosixPath, generateSlugInternal } = coreUtilsMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import phaseIdMod = require('./phase-id.cjs'); +const { normalizePhaseName } = phaseIdMod; +// eslint-disable-next-line @typescript-eslint/no-require-imports +import phaseLocatorMod = require('./phase-locator.cjs'); +const { findPhaseInternal } = phaseLocatorMod; import { platformWriteSync } from './shell-command-projection.cjs'; // eslint-disable-next-line @typescript-eslint/no-require-imports import planningWorkspace = require('./planning-workspace.cjs');