refactor(#1289): T5 — migrate the final idiom-hard callers off the core spine (#1290)

The last 4 core importers, migrated off non-destructure idioms:
- gsd-tools.cjs: core.{error,ERROR_REASON,setJsonErrorMode,output} -> io.cjs;
  core.findProjectRoot -> project-root.cjs (lazy wrapper preserved); inline
  resolveWorktreeRoot require -> worktree-safety.cjs
- audit-command-router: DI default `_core ?? core` -> `_core ?? io` (seam preserved)
- intel-command-router: DI default -> `{ output: io.output, timeAgo: coreUtils.timeAgo }` (seam preserved)
- check-command-router: io destructure -> io.cjs; dynamic core['planningDir']
  -> planning-workspace, core['findPhaseInternal'] -> phase-locator (typed
  imports, dropped the Record-cast bracket hack)

Allowlist is now EMPTY — NO file imports the core spine. core.cts re-exports
are dead weight; T-final deletes core.cts + remaining shim tests + the lint.
No behaviour change.

Closes #1289

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-15 17:44:25 -04:00
committed by GitHub
parent b108f101b0
commit 76765bc24d
5 changed files with 96 additions and 107 deletions

View File

@@ -25,7 +25,7 @@
*/
// eslint-disable-next-line @typescript-eslint/no-require-imports
import core = require('./core.cjs');
import io = require('./io.cjs');
// ─── Types ────────────────────────────────────────────────────────────────────
@@ -82,15 +82,15 @@ function routeAuditOpen({ args, cwd, raw, error, _audit, _core }: RouteAuditOpen
void error;
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
const a: AuditModule = _audit ?? require('./audit.cjs');
const c: CoreModule = _core ?? core;
const c: CoreModule = _core ?? io;
const wantJson = args.includes('--json');
const result = a.auditOpenArtifacts(cwd);
if (wantJson) {
// core.output JSON-stringifies its first arg; pass the object directly.
// io.output JSON-stringifies its first arg; pass the object directly.
c.output(result, raw);
} else {
// Human-readable report must bypass JSON encoding — use the rawValue
// form (third arg) which core.output emits verbatim.
// form (third arg) which io.output emits verbatim.
c.output(null, true, a.formatAuditReport(result));
}
}

View File

@@ -10,8 +10,14 @@ import fs from 'node:fs';
import path from 'node:path';
import { execFileSync } from 'node:child_process';
// eslint-disable-next-line @typescript-eslint/no-require-imports
import core = require('./core.cjs');
const { output, error, ERROR_REASON } = core;
import io = require('./io.cjs');
const { output, error, ERROR_REASON } = io;
// eslint-disable-next-line @typescript-eslint/no-require-imports
import planningWorkspaceMod = require('./planning-workspace.cjs');
const { planningDir } = planningWorkspaceMod;
// eslint-disable-next-line @typescript-eslint/no-require-imports
import phaseLocatorMod = require('./phase-locator.cjs');
const { findPhaseInternal } = phaseLocatorMod;
import { parseDecisions } from './decisions.cjs';
import type { Decision } from './decisions.cjs';
import { checkUiPresence } from './ui-safety-gate.cjs';
@@ -358,7 +364,7 @@ function cmdDecisionCoverageVerify(projectDir: string, args: string[], raw: bool
* Invocable as: gsd_run check ui-plan-gate <phase>
*
* Uses checkUiPresence from ui-safety-gate.cjs — does NOT reimplement frontend detection.
* Uses getRoadmapPhaseInternal + findPhaseInternal from core.cjs for phase data.
* Uses getRoadmapPhaseWithFallback + findPhaseInternal from leaf modules for phase data.
*/
function findUiSpecInDir(phaseDir: string): string {
if (!phaseDir || !fs.existsSync(phaseDir)) return '';
@@ -384,7 +390,7 @@ function findUiSpecInDir(phaseDir: string): string {
* surface the miss — we do NOT silently degrade to frontend:false if the roadmap
* exists but the phase header is absent.
* (b) Runs checkUiPresence (frontend detection) — no reimplementation.
* (c) Resolves the phase directory via core.findPhaseInternal; checks for *-UI-SPEC.md.
* (c) Resolves the phase directory via findPhaseInternal (phase-locator.cjs); checks for *-UI-SPEC.md.
*
* Returns: { frontend, hasUiSpec, block, uiSpecPath, phaseLookupFailed }
* block = frontend && !hasUiSpec
@@ -407,10 +413,8 @@ function computeUiPlanGate(projectDir: string, phase: string): {
const section = getRoadmapPhaseWithFallback(projectDir, phase);
if (section === null) {
// Distinguish: ROADMAP.md missing (no-roadmap project) vs phase not found in ROADMAP.
// core.planningDir(cwd) resolves the .planning/ root for workstream-aware paths.
const planDir: string = typeof (core as unknown as Record<string, unknown>)['planningDir'] === 'function'
? (core as unknown as Record<string, (cwd: string) => string>)['planningDir'](projectDir)
: path.join(projectDir, '.planning');
// planningDir(cwd) resolves the .planning/ root for workstream-aware paths.
const planDir: string = planningDir(projectDir);
const roadmapPath = path.join(planDir, 'ROADMAP.md');
if (fs.existsSync(roadmapPath)) {
// ROADMAP.md exists but phase was not found → surface the miss
@@ -427,22 +431,18 @@ function computeUiPlanGate(projectDir: string, phase: string): {
const frontend = presenceResult.hasUI;
// (c) Resolve phase directory via findPhaseInternal and check for *-UI-SPEC.md
const coreModule = core as unknown as Record<string, unknown>;
let phaseDir = '';
try {
const findPhase = coreModule['findPhaseInternal'] as ((cwd: string, phase: string) => Record<string, unknown> | string | null) | undefined;
if (typeof findPhase === 'function') {
const result = findPhase(projectDir, phase);
if (result && typeof result === 'object') {
// findPhaseInternal returns { directory: '<relative-posix-path>', ... }
// directory is relative to cwd — resolve it to absolute.
const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
if (relDir) {
phaseDir = path.resolve(projectDir, relDir);
}
} else if (typeof result === 'string') {
phaseDir = result;
const result = findPhaseInternal(projectDir, phase);
if (result && typeof result === 'object') {
// findPhaseInternal returns { directory: '<relative-posix-path>', ... }
// directory is relative to cwd — resolve it to absolute.
const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
if (relDir) {
phaseDir = path.resolve(projectDir, relDir);
}
} else if (typeof result === 'string') {
phaseDir = result;
}
} catch { /* phase dir lookup failure → hasUiSpec=false */ }
@@ -501,7 +501,7 @@ const UI_PATH_PATTERNS_RE = /\/(components|pages|views|screens|layouts|ui|fronte
* same lookup as computeUiPlanGate — to determine if this is a frontend phase.
* (b) Runs checkUiPresence (frontend detection) — no reimplementation.
* (c) Checks git diff HEAD~1..HEAD for UI file changes in the current worktree.
* (d) Resolves the phase directory via core.findPhaseInternal; checks for *-UI-SPEC.md.
* (d) Resolves the phase directory via findPhaseInternal (phase-locator.cjs); checks for *-UI-SPEC.md.
*
* Returns: { frontend, hasUiFiles, hasUiSpec, block, message?, phaseLookupFailed? }
* block = frontend && hasUiFiles && !hasUiSpec
@@ -521,9 +521,7 @@ function computeUiSafetyGate(projectDir: string, phase: string): {
try {
const section = getRoadmapPhaseWithFallback(projectDir, phase);
if (section === null) {
const planDir: string = typeof (core as unknown as Record<string, unknown>)['planningDir'] === 'function'
? (core as unknown as Record<string, (cwd: string) => string>)['planningDir'](projectDir)
: path.join(projectDir, '.planning');
const planDir: string = planningDir(projectDir);
const roadmapPath = path.join(planDir, 'ROADMAP.md');
if (fs.existsSync(roadmapPath)) {
phaseLookupFailed = true;
@@ -554,20 +552,16 @@ function computeUiSafetyGate(projectDir: string, phase: string): {
} catch { /* git unavailable or no prior commit — treat as no UI files changed */ }
// (d) Resolve phase directory and check for *-UI-SPEC.md (same as computeUiPlanGate)
const coreModule = core as unknown as Record<string, unknown>;
let phaseDir = '';
try {
const findPhase = coreModule['findPhaseInternal'] as ((cwd: string, phase: string) => Record<string, unknown> | string | null) | undefined;
if (typeof findPhase === 'function') {
const result = findPhase(projectDir, phase);
if (result && typeof result === 'object') {
const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
if (relDir) {
phaseDir = path.resolve(projectDir, relDir);
}
} else if (typeof result === 'string') {
phaseDir = result;
const result = findPhaseInternal(projectDir, phase);
if (result && typeof result === 'object') {
const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
if (relDir) {
phaseDir = path.resolve(projectDir, relDir);
}
} else if (typeof result === 'string') {
phaseDir = result;
}
} catch { /* phase dir lookup failure → hasUiSpec=false */ }
@@ -639,18 +633,14 @@ function cmdTddReviewCheckpoint(projectDir: string, args: string[], raw: boolean
}
// Resolve phase directory
const coreModule = core as unknown as Record<string, unknown>;
let phaseDir = '';
try {
const findPhase = coreModule['findPhaseInternal'] as ((cwd: string, phase: string) => Record<string, unknown> | string | null) | undefined;
if (typeof findPhase === 'function') {
const result = findPhase(projectDir, phase);
if (result && typeof result === 'object') {
const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
if (relDir) phaseDir = path.resolve(projectDir, relDir);
} else if (typeof result === 'string') {
phaseDir = result;
}
const result = findPhaseInternal(projectDir, phase);
if (result && typeof result === 'object') {
const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
if (relDir) phaseDir = path.resolve(projectDir, relDir);
} else if (typeof result === 'string') {
phaseDir = result;
}
} catch { /* phase dir lookup failure */ }

View File

@@ -38,14 +38,17 @@
* the case block).
*/
// eslint-disable-next-line @typescript-eslint/no-require-imports
import core = require('./core.cjs');
// eslint-disable-next-line @typescript-eslint/no-require-imports
import io = require('./io.cjs');
// eslint-disable-next-line @typescript-eslint/no-require-imports
import coreUtils = require('./core-utils.cjs');
// eslint-disable-next-line @typescript-eslint/no-require-imports
import path = require('path');
const { ERROR_REASON } = io;
// Default CoreModule implementation assembled from leaf modules.
// _core seam overrides this entirely for test injection.
const _defaultCore = { output: io.output, timeAgo: coreUtils.timeAgo };
// ─── Types ────────────────────────────────────────────────────────────────────
@@ -83,7 +86,7 @@ interface RouteIntelCommandOptions {
function routeIntelCommand({ args, cwd, raw, error, _intel, _core }: RouteIntelCommandOptions): void {
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
const intel: IntelModule = _intel ?? require('./intel.cjs');
const c: CoreModule = _core ?? core;
const c: CoreModule = _core ?? _defaultCore;
const subcommand = args[1];
if (subcommand === 'query') {