refactor(#1294): T-final — delete the core.cjs re-export spine (epic #1267 complete) (#1295)

* refactor(#1294): T-final — delete the core.cjs re-export spine (epic #1267 complete)

After T0–T6 nothing imports core, so retire the spine and its scaffolding:
- delete src/core.cts (and the gitignored gsd-core/bin/lib/core.cjs artifact;
  remove its .gitignore + eslint-ignore entries)
- delete scripts/lint-core-spine-imports.cjs + its allowlist; drop it from the
  package.json lint:ci chain
- regenerate docs/INVENTORY-MANIFEST.json (drops the core.cjs surface)
- sweep stale references: CONTEXT.md glossary back-compat clauses (spine retired,
  callers import the leaf directly), planning-config.md CONFIG_DEFAULTS owner,
  and false present-tense core.cjs claims in leaf-module docstrings

The ADR-857 decomposition is complete: the former Core god-module is fully
dissolved into its leaf modules; no re-export spine remains. No behaviour change.

Closes #1294

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(#1294): migrate the computed-path core.cjs importers the literal grep missed

bin/install.js used require(path.join(_gsdLibDir, 'core.cjs')) (a computed
path, and bin/install.js was never in the convergence lint's scan roots), and
~8 test files referenced core.cjs via path.join/readFileSync/existsSync/FILE_ARG
forms the literal-string migration grep missed. Route install.js's symbols to
their leaves (RUNTIME_PROFILE_MAP->model-catalog, resolveTierEntry/EFFORT_SET->
model-resolver) and repoint/adjust the test references to the leaves. Recovers
the 161 'Cannot find module core.cjs' failures from the spine deletion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-15 18:50:46 -04:00
committed by GitHub
parent c76827afbc
commit 8c3d934a90
31 changed files with 62 additions and 501 deletions

1
.gitignore vendored
View File

@@ -138,7 +138,6 @@ build/
/gsd-core/bin/lib/runtime-config-adapter-registry.cjs /gsd-core/bin/lib/runtime-config-adapter-registry.cjs
/gsd-core/bin/lib/runtime-hooks-surface.cjs /gsd-core/bin/lib/runtime-hooks-surface.cjs
/gsd-core/bin/lib/command-routing-hub.cjs /gsd-core/bin/lib/command-routing-hub.cjs
/gsd-core/bin/lib/core.cjs
/gsd-core/bin/lib/core-utils.cjs /gsd-core/bin/lib/core-utils.cjs
/gsd-core/bin/lib/io.cjs /gsd-core/bin/lib/io.cjs
/gsd-core/bin/lib/phase-id.cjs /gsd-core/bin/lib/phase-id.cjs

View File

@@ -15,13 +15,13 @@ Module owning `milestone complete` (archive roadmap/requirements/phases, build M
Module that composes Dispatch Policy Module, Query Execution Policy Module, and per-stage handlers (input-validation, plan, execution, result-builder, formatting, error-mapping, observability) into the end-to-end pipeline that produces a `QueryDispatchResult`. The SDK-era pipeline collapsed onto the Command Routing Hub per ADR-0174; current dispatch seam: `gsd-core/bin/lib/command-routing-hub.cjs` (see Command Routing Hub below). Module that composes Dispatch Policy Module, Query Execution Policy Module, and per-stage handlers (input-validation, plan, execution, result-builder, formatting, error-mapping, observability) into the end-to-end pipeline that produces a `QueryDispatchResult`. The SDK-era pipeline collapsed onto the Command Routing Hub per ADR-0174; current dispatch seam: `gsd-core/bin/lib/command-routing-hub.cjs` (see Command Routing Hub below).
### Phase Id Module ### Phase Id Module
Module owning the pure phase-id parsing and matching helpers: phase-name normalization, phase-token extraction/matching, milestone- and phase-dir id parsing, and phase-markdown regex builders (`escapeRegex`, `normalizePhaseName`, `comparePhaseNum`, `extractPhaseToken`, `phaseTokenMatches`, `phaseMarkdownRegexSource`/`phaseMarkdownRegexSourceExact`, `getMilestoneFromPhaseId`, `getPhaseDirFromPhaseId`). Pure string/regex — no I/O, no config, no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2a (#865) as the cycle-free leaf that unblocks the roadmap-parser and phase-locator extractions; `core.cjs` re-exports the helpers for back-compat. Source of truth: `gsd-core/bin/lib/phase-id.cjs` (generated from `src/phase-id.cts`). Module owning the pure phase-id parsing and matching helpers: phase-name normalization, phase-token extraction/matching, milestone- and phase-dir id parsing, and phase-markdown regex builders (`escapeRegex`, `normalizePhaseName`, `comparePhaseNum`, `extractPhaseToken`, `phaseTokenMatches`, `phaseMarkdownRegexSource`/`phaseMarkdownRegexSourceExact`, `getMilestoneFromPhaseId`, `getPhaseDirFromPhaseId`). Pure string/regex — no I/O, no config, no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2a (#865) as the cycle-free leaf that unblocks the roadmap-parser and phase-locator extractions; the `core.cjs` re-export spine was retired in epic #1267, so callers import this leaf directly. Source of truth: `gsd-core/bin/lib/phase-id.cjs` (generated from `src/phase-id.cts`).
### Phase Lifecycle Module ### Phase Lifecycle Module
Module owning phase create, rename, complete, remove, list, and plan-index operations, plus phase-dir prefix validation, STATE.md staleness detection, and auto-prune behaviour. Entry point: `gsd-core/bin/lib/phase.cjs` (CJS surface). Typed phase events: `GSDPhaseStartEvent`, `GSDPhaseStepStartEvent`, `GSDPhaseStepCompleteEvent`, `GSDPhaseCompleteEvent`. (The SDK native-query surface, the `types.ts` event definitions, `phase-runner.ts`, and `phase-prompt.ts` were retired with the SDK package per ADR-0174.) Module owning phase create, rename, complete, remove, list, and plan-index operations, plus phase-dir prefix validation, STATE.md staleness detection, and auto-prune behaviour. Entry point: `gsd-core/bin/lib/phase.cjs` (CJS surface). Typed phase events: `GSDPhaseStartEvent`, `GSDPhaseStepStartEvent`, `GSDPhaseStepCompleteEvent`, `GSDPhaseCompleteEvent`. (The SDK native-query surface, the `types.ts` event definitions, `phase-runner.ts`, and `phase-prompt.ts` were retired with the SDK package per ADR-0174.)
### Phase Locator Module ### Phase Locator Module
Module owning phase-directory search and location: active-phase discovery against the `.planning/phases/` tree (`searchPhaseInDir`, `findPhaseInternal`) and archived-phase-dir enumeration (`getArchivedPhaseDirs`), matching phase ids/tokens against the filesystem. Depends only on leaf modules (`phase-id` for token/name matching, `core-utils` for fs-scan/path helpers, `planning-workspace` for `planningDir`) — no `loadConfig`, no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2d (#881); `core.cjs` re-exports `searchPhaseInDir`, `findPhaseInternal`, and `getArchivedPhaseDirs` for back-compat. Source of truth: `gsd-core/bin/lib/phase-locator.cjs` (generated from `src/phase-locator.cts`). Module owning phase-directory search and location: active-phase discovery against the `.planning/phases/` tree (`searchPhaseInDir`, `findPhaseInternal`) and archived-phase-dir enumeration (`getArchivedPhaseDirs`), matching phase ids/tokens against the filesystem. Depends only on leaf modules (`phase-id` for token/name matching, `core-utils` for fs-scan/path helpers, `planning-workspace` for `planningDir`) — no `loadConfig`, no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2d (#881); the `core.cjs` re-export spine was retired in epic #1267, so callers import this leaf directly. Source of truth: `gsd-core/bin/lib/phase-locator.cjs` (generated from `src/phase-locator.cts`).
### Dispatch Policy Module ### Dispatch Policy Module
Module owning dispatch error mapping, fallback policy, timeout classification, and CLI exit mapping contract. Module owning dispatch error mapping, fallback policy, timeout classification, and CLI exit mapping contract.
@@ -89,13 +89,13 @@ Module owning `.planning` path resolution, active workstream pointer policy (`se
Module owning workstream directory discovery, per-workstream state projection, phase/plan/summary counting, roadmap-declared phase count, active marker projection, and active-workstream collision inputs. Command handlers render list/status/progress outputs from this inventory instead of rescanning `.planning/workstreams/*` directly. Source of truth for the pure projection is `gsd-core/bin/lib/workstream-inventory-builder.cjs` (a Builder Module); the Reader Adapter `gsd-core/bin/lib/workstream-inventory.cjs` collects filesystem inputs and delegates projection to the Builder. Module owning workstream directory discovery, per-workstream state projection, phase/plan/summary counting, roadmap-declared phase count, active marker projection, and active-workstream collision inputs. Command handlers render list/status/progress outputs from this inventory instead of rescanning `.planning/workstreams/*` directly. Source of truth for the pure projection is `gsd-core/bin/lib/workstream-inventory-builder.cjs` (a Builder Module); the Reader Adapter `gsd-core/bin/lib/workstream-inventory.cjs` collects filesystem inputs and delegates projection to the Builder.
### Project-Root Resolution Module ### Project-Root Resolution Module
Module owning project-root resolution from any starting directory. Walks the ancestor chain (bounded by `FIND_PROJECT_ROOT_MAX_DEPTH = 10`) applying four heuristics in order: (0) own `.planning/` guard (#1362), (1) parent `.planning/config.json` `sub_repos` traversal, (2) legacy `multiRepo: true` boolean + ancestor `.git`, (3) `.git` heuristic with parent `.planning/`. Returns `startDir` when no ancestor qualifies. Sync `node:fs` I/O. Source of truth: `gsd-core/bin/lib/project-root.cjs`; consumed via a thin re-export at `gsd-core/bin/lib/core.cjs`. Module owning project-root resolution from any starting directory. Walks the ancestor chain (bounded by `FIND_PROJECT_ROOT_MAX_DEPTH = 10`) applying four heuristics in order: (0) own `.planning/` guard (#1362), (1) parent `.planning/config.json` `sub_repos` traversal, (2) legacy `multiRepo: true` boolean + ancestor `.git`, (3) `.git` heuristic with parent `.planning/`. Returns `startDir` when no ancestor qualifies. Sync `node:fs` I/O. Source of truth: `gsd-core/bin/lib/project-root.cjs`; the `core.cjs` re-export spine was retired in epic #1267, so callers import this leaf directly.
### Planning Path Projection Module ### Planning Path Projection Module
Module owning projection from project/workstream context to concrete `.planning` paths. Policy precedence is `explicit workstream > env workstream > env project > root`. Invalid workspace context is a validation error at this seam rather than a silent fallback. Module owning projection from project/workstream context to concrete `.planning` paths. Policy precedence is `explicit workstream > env workstream > env project > root`. Invalid workspace context is a validation error at this seam rather than a silent fallback.
### Worktree Safety Policy Module ### Worktree Safety Policy Module
CJS Module owning worktree lifecycle safety policy for the GSD orchestration layer. Interface: `resolveWorktreeContext(cwd, deps) → WorktreeContext` (linked-worktree root mapping), `parseWorktreePorcelain(output) → WorktreeEntry[]` (porcelain parser, skips detached HEAD), `planWorktreePrune(repoRoot, opts, deps) → PrunePlan` (metadata-prune plan, never destructive by default), `executeWorktreePrunePlan(plan, deps) → PruneResult` (executes prune; degrades gracefully on git timeout), `listLinkedWorktreePaths(repoRoot, deps) → LinkedPathsResult`, `inspectWorktreeHealth(repoRoot, opts, deps) → HealthResult` (orphan + stale detection), `snapshotWorktreeInventory(repoRoot, opts, deps) → InventoryResult`, `planWorktreeWaveCleanup(repoRoot, manifest) → CleanupPlan` (manifest-scoped, fail-closed), `executeWorktreeWaveCleanupPlan(plan, deps) → CleanupResult`. Source of truth: `gsd-core/bin/lib/worktree-safety.cjs`. Timeout path: all git subprocess calls are bounded; callers receive `ok:false, reason:'git_timed_out'` rather than a thrown exception. Test anchor: `tests/worktree-safety.test.cjs`. Planned (retiring the `core.cjs` spine, ADR-857): absorbs the two thin compositional wrappers that squatted in Core — `resolveWorktreeRoot(cwd, deps)` (a projection over `resolveWorktreeContext`) and `pruneOrphanedWorktrees(...)` (sequences `planWorktreePrune` + `executeWorktreePrunePlan` with a timeout warning) — so callers reach this single worktree-lifecycle seam directly instead of through Core. `gitWorktreeInfoInternal` does NOT move here — worktree-info detection belongs to the Git Query Module. CJS Module owning worktree lifecycle safety policy for the GSD orchestration layer. Interface: `resolveWorktreeContext(cwd, deps) → WorktreeContext` (linked-worktree root mapping), `parseWorktreePorcelain(output) → WorktreeEntry[]` (porcelain parser, skips detached HEAD), `planWorktreePrune(repoRoot, opts, deps) → PrunePlan` (metadata-prune plan, never destructive by default), `executeWorktreePrunePlan(plan, deps) → PruneResult` (executes prune; degrades gracefully on git timeout), `listLinkedWorktreePaths(repoRoot, deps) → LinkedPathsResult`, `inspectWorktreeHealth(repoRoot, opts, deps) → HealthResult` (orphan + stale detection), `snapshotWorktreeInventory(repoRoot, opts, deps) → InventoryResult`, `planWorktreeWaveCleanup(repoRoot, manifest) → CleanupPlan` (manifest-scoped, fail-closed), `executeWorktreeWaveCleanupPlan(plan, deps) → CleanupResult`. Source of truth: `gsd-core/bin/lib/worktree-safety.cjs`. Timeout path: all git subprocess calls are bounded; callers receive `ok:false, reason:'git_timed_out'` rather than a thrown exception. Test anchor: `tests/worktree-safety.test.cjs`. The `core.cjs` re-export spine was retired in epic #1267: this module absorbed the two thin compositional wrappers that squatted in Core — `resolveWorktreeRoot(cwd, deps)` (a projection over `resolveWorktreeContext`) and `pruneOrphanedWorktrees(...)` (sequences `planWorktreePrune` + `executeWorktreePrunePlan` with a timeout warning) — so callers reach this single worktree-lifecycle seam directly. `gitWorktreeInfoInternal` did NOT move here — worktree-info detection belongs to the Git Query Module.
### Worktree Lifecycle Module ### Worktree Lifecycle Module
Workflow contract seam covering agent worktree lifecycle orchestration rules. The `worktree_branch_check` block lives in one canonical fragment (`gsd-core/references/worktree-branch-check.md`) that `execute-phase.md`, `quick.md`, `diagnose-issues.md`, and `execute-plan.md` embed at dispatch. Key invariants: `worktree_branch_check` is **verify-only and fail-closed** — the orchestrator owns worktree lifecycle and base recovery, so the sub-agent holds no state-correction primitives; HEAD attachment verified via `git symbolic-ref`; positive allow-list `^worktree-agent-*` enforced; `git update-ref` on protected refs is prohibited; on base mismatch the sub-agent halts with `exit 42` and surfaces to the orchestrator (#48); the orchestrator runs a cwd-drift guard at `execute_waves` entry that resolves the worktree root and refuses drift into an agent worktree (#48); cleanup is manifest-scoped (`WAVE_WORKTREE_MANIFEST`) not global-discovery-based; worktree spawning is sequential (one `run_in_background` at a time to avoid `config.lock` contention). Test anchor: `tests/worktree.test.cjs`. Workflow contract seam covering agent worktree lifecycle orchestration rules. The `worktree_branch_check` block lives in one canonical fragment (`gsd-core/references/worktree-branch-check.md`) that `execute-phase.md`, `quick.md`, `diagnose-issues.md`, and `execute-plan.md` embed at dispatch. Key invariants: `worktree_branch_check` is **verify-only and fail-closed** — the orchestrator owns worktree lifecycle and base recovery, so the sub-agent holds no state-correction primitives; HEAD attachment verified via `git symbolic-ref`; positive allow-list `^worktree-agent-*` enforced; `git update-ref` on protected refs is prohibited; on base mismatch the sub-agent halts with `exit 42` and surfaces to the orchestrator (#48); the orchestrator runs a cwd-drift guard at `execute_waves` entry that resolves the worktree root and refuses drift into an agent worktree (#48); cleanup is manifest-scoped (`WAVE_WORKTREE_MANIFEST`) not global-discovery-based; worktree spawning is sequential (one `run_in_background` at a time to avoid `config.lock` contention). Test anchor: `tests/worktree.test.cjs`.
@@ -116,22 +116,22 @@ Module owning validation for Installer Migration Module records and planned acti
Primary installer for all runtimes. Single production file: `bin/install.js` (generated). Exports: `install(isGlobal, runtime[, configDir])` → typed result `{ runtime, configDir, settingsPath, settings, statuslineCommand, updateBannerCommand }`; `uninstall(isGlobal, runtime[, configDir])`; `installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile)`; `uninstallRuntimeArtifacts(runtime, configDir, scope)`; `writeManifest(configDir, runtime)`. Runtime enum: `allRuntimes` (15 values: claude, antigravity, augment, cline, codebuddy, codex, copilot, cursor, gemini, hermes, kilo, opencode, qwen, trae, windsurf). Directory helpers: `getDirName(runtime)` → local dir name; `getConfigDirFromHome(runtime, isGlobal)` → shell-quoted path fragment. Per-runtime global config-dir resolution is delegated to `gsd-core/bin/lib/runtime-homes.cjs:getGlobalConfigDir(runtime[, explicitDir])` — the canonical, env-var–aware projection (`explicitDir` override + opencode/kilo `*_CONFIG` file-path precedence); the legacy in-installer `getGlobalDir`/`getOpencodeGlobalDir`/`getKiloGlobalDir` were retired into it (#56). Runtime-specific helpers: `resolveKiloConfigPath(configDir)`, `configureKiloPermissions(isGlobal[, explicitDir])`. Claude-specific permission helpers: `mergeClaudePermissions(settings)` — non-destructively appends GSD-owned allow/deny entries (see `GSD_CLAUDE_ALLOW_PERMISSIONS`, `GSD_CLAUDE_DENY_PERMISSIONS` constants) to a Claude Code settings object; called from `finishInstall` for `runtime === 'claude'` only; uninstall removes exactly these entries (#768). Layout-driven artifact copy/removal delegates to `gsd-core/bin/lib/runtime-artifact-layout.cjs:resolveRuntimeArtifactLayout` (throws `TypeError` for unknown runtimes). Seven runtimes with non-recursive skill loaders (claude global, cline, qwen, hermes, augment, trae, antigravity) use a nested router layout: 6 `gsd-ns-*` router bundles emitted as top-level skills, with concrete skills nested at `<router>/skills/<name>/SKILL.md` (hermes prefix='': `skills/gsd/ns-*/…`). The remaining skills-runtimes (cursor, codex, copilot, windsurf, codebuddy, opencode, kilo) use the flat `skills/gsd-<stem>/` layout unchanged. See Skill Surface Budget Module and Runtime Artifact Layout Module. Primary installer for all runtimes. Single production file: `bin/install.js` (generated). Exports: `install(isGlobal, runtime[, configDir])` → typed result `{ runtime, configDir, settingsPath, settings, statuslineCommand, updateBannerCommand }`; `uninstall(isGlobal, runtime[, configDir])`; `installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile)`; `uninstallRuntimeArtifacts(runtime, configDir, scope)`; `writeManifest(configDir, runtime)`. Runtime enum: `allRuntimes` (15 values: claude, antigravity, augment, cline, codebuddy, codex, copilot, cursor, gemini, hermes, kilo, opencode, qwen, trae, windsurf). Directory helpers: `getDirName(runtime)` → local dir name; `getConfigDirFromHome(runtime, isGlobal)` → shell-quoted path fragment. Per-runtime global config-dir resolution is delegated to `gsd-core/bin/lib/runtime-homes.cjs:getGlobalConfigDir(runtime[, explicitDir])` — the canonical, env-var–aware projection (`explicitDir` override + opencode/kilo `*_CONFIG` file-path precedence); the legacy in-installer `getGlobalDir`/`getOpencodeGlobalDir`/`getKiloGlobalDir` were retired into it (#56). Runtime-specific helpers: `resolveKiloConfigPath(configDir)`, `configureKiloPermissions(isGlobal[, explicitDir])`. Claude-specific permission helpers: `mergeClaudePermissions(settings)` — non-destructively appends GSD-owned allow/deny entries (see `GSD_CLAUDE_ALLOW_PERMISSIONS`, `GSD_CLAUDE_DENY_PERMISSIONS` constants) to a Claude Code settings object; called from `finishInstall` for `runtime === 'claude'` only; uninstall removes exactly these entries (#768). Layout-driven artifact copy/removal delegates to `gsd-core/bin/lib/runtime-artifact-layout.cjs:resolveRuntimeArtifactLayout` (throws `TypeError` for unknown runtimes). Seven runtimes with non-recursive skill loaders (claude global, cline, qwen, hermes, augment, trae, antigravity) use a nested router layout: 6 `gsd-ns-*` router bundles emitted as top-level skills, with concrete skills nested at `<router>/skills/<name>/SKILL.md` (hermes prefix='': `skills/gsd/ns-*/…`). The remaining skills-runtimes (cursor, codex, copilot, windsurf, codebuddy, opencode, kilo) use the flat `skills/gsd-<stem>/` layout unchanged. See Skill Surface Budget Module and Runtime Artifact Layout Module.
### I/O Module ### I/O Module
Module owning the tool's CLI I/O primitives: `output()` result emission (with large-payload temp-file spillover via `GSD_TEMP_DIR`/`ensureGsdTempDir`/`reapStaleTempFiles`), `error()` stderr emission with exit-code mapping, and the JSON-error-mode toggle (`setJsonErrorMode`/`getJsonErrorMode`, `ERROR_REASON`). Extracted from the Core module per ADR-857 rollout phase 1 (#859) so feature modules (`graphify`, `intel`, `audit`, `profile-pipeline`) depend on a small I/O seam instead of the core god-module; `core.cjs` re-exports the primitives for back-compat. Source of truth: `gsd-core/bin/lib/io.cjs` (generated from `src/io.cts`). Module owning the tool's CLI I/O primitives: `output()` result emission (with large-payload temp-file spillover via `GSD_TEMP_DIR`/`ensureGsdTempDir`/`reapStaleTempFiles`), `error()` stderr emission with exit-code mapping, and the JSON-error-mode toggle (`setJsonErrorMode`/`getJsonErrorMode`, `ERROR_REASON`). Extracted from the Core module per ADR-857 rollout phase 1 (#859) so feature modules (`graphify`, `intel`, `audit`, `profile-pipeline`) depend on a small I/O seam instead of the core god-module; the `core.cjs` re-export spine was retired in epic #1267, so callers import this leaf directly. Source of truth: `gsd-core/bin/lib/io.cjs` (generated from `src/io.cts`).
### Roadmap Parser Module ### Roadmap Parser Module
Module owning ROADMAP.md parsing: shipped-milestone slicing, current-milestone extraction, milestone/phase lookups, and milestone-phase filtering (`stripShippedMilestones`, `extractCurrentMilestone`, `replaceInCurrentMilestone`, `getRoadmapPhaseInternal`, `getMilestoneInfo`, `getMilestonePhaseFilter`). Depends only on leaf modules (`phase-id`, `planning-workspace`, `shell-command-projection`) — no `loadConfig`, no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2b (#870), resolving the ROADMAP.md parse/write straddle so the Roadmap module (`roadmap.cjs`, which owns ROADMAP.md mutation) imports parsing directly instead of through Core; `core.cjs` re-exports the helpers for back-compat. Source of truth: `gsd-core/bin/lib/roadmap-parser.cjs` (generated from `src/roadmap-parser.cts`). Module owning ROADMAP.md parsing: shipped-milestone slicing, current-milestone extraction, milestone/phase lookups, and milestone-phase filtering (`stripShippedMilestones`, `extractCurrentMilestone`, `replaceInCurrentMilestone`, `getRoadmapPhaseInternal`, `getMilestoneInfo`, `getMilestonePhaseFilter`). Depends only on leaf modules (`phase-id`, `planning-workspace`, `shell-command-projection`) — no `loadConfig`, no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2b (#870), resolving the ROADMAP.md parse/write straddle so the Roadmap module (`roadmap.cjs`, which owns ROADMAP.md mutation) imports parsing directly instead of through Core; the `core.cjs` re-export spine was retired in epic #1267, so callers import this leaf directly. Source of truth: `gsd-core/bin/lib/roadmap-parser.cjs` (generated from `src/roadmap-parser.cts`).
### Core Utilities Module ### Core Utilities Module
Module owning the shared low-level utility primitives extracted from Core: POSIX path normalization (`toPosixPath`), filesystem scanning (`detectSubRepos`, `readSubdirectories`, `getPhaseFileStats`, `pathExistsInternal`), and small pure helpers (`generateSlugInternal`, `extractOneLinerFromBody`, `filterPlanFiles`, `filterSummaryFiles`, `extractCanonicalPlanId`, `timeAgo`). Depends only on Node built-ins and already-leafed modules (`phase-id` for `comparePhaseNum`, `planning-workspace` for `findContextMdIn`) — no `loadConfig`, no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2c (#877) as the shared leaf that unblocks the phase-locator fs-search extraction (2d); `core.cjs` re-exports the public helpers for back-compat. Source of truth: `gsd-core/bin/lib/core-utils.cjs` (generated from `src/core-utils.cts`). Module owning the shared low-level utility primitives extracted from Core: POSIX path normalization (`toPosixPath`), filesystem scanning (`detectSubRepos`, `readSubdirectories`, `getPhaseFileStats`, `pathExistsInternal`), and small pure helpers (`generateSlugInternal`, `extractOneLinerFromBody`, `filterPlanFiles`, `filterSummaryFiles`, `extractCanonicalPlanId`, `timeAgo`). Depends only on Node built-ins and already-leafed modules (`phase-id` for `comparePhaseNum`, `planning-workspace` for `findContextMdIn`) — no `loadConfig`, no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2c (#877) as the shared leaf that unblocks the phase-locator fs-search extraction (2d); the `core.cjs` re-export spine was retired in epic #1267, so callers import this leaf directly. Source of truth: `gsd-core/bin/lib/core-utils.cjs` (generated from `src/core-utils.cts`).
### Agent Install Check Module [Planned] ### Agent Install Check Module
Module owning agent-presence resolution and verification, extracted from the Core module as the cleanup step that retires the `core.cjs` re-export spine (the final ADR-857 decomposition). Interface: `getAgentsDir(runtime?, env?)` — env-var-aware, runtime-aware agents-directory resolution (the `claude` runtime resolves `__dirname`-relative); `checkAgentsInstalled(...)` — multi-runtime agent-presence check that validates `gsd-file-manifest.json` completeness and confirms the declared agents exist on disk. Pure read/verify — no install-write side effects (writes remain the Installer Module's). Consumed by the Init Command Module, the verify workflow, and the docs workflow. Source of truth: `gsd-core/bin/lib/agent-install-check.cjs` (generated from `src/agent-install-check.cts`); replaces the two functions that squatted in `core.cts`. See Installer Module and ADR-857. Module owning agent-presence resolution and verification, extracted from the Core module as the cleanup step that retired the `core.cjs` re-export spine (the final ADR-857 decomposition, epic #1267). Interface: `getAgentsDir(runtime?, env?)` — env-var-aware, runtime-aware agents-directory resolution (the `claude` runtime resolves `__dirname`-relative); `checkAgentsInstalled(...)` — multi-runtime agent-presence check that validates `gsd-file-manifest.json` completeness and confirms the declared agents exist on disk. Pure read/verify — no install-write side effects (writes remain the Installer Module's). Consumed by the Init Command Module, the verify workflow, and the docs workflow. Source of truth: `gsd-core/bin/lib/agent-install-check.cjs` (generated from `src/agent-install-check.cts`); replaced the two functions that squatted in `core.cts`. See Installer Module and ADR-857.
### Config Loader Module ### Config Loader Module
Module owning project configuration loading: reads `.planning/config.json`, merges built-in defaults (`CONFIG_DEFAULTS`/`CANONICAL_CONFIG_DEFAULTS`), normalizes legacy keys, applies the active-workstream overlay, validates against the config schema, and warns on unknown keys/profile overrides (`loadConfig` plus its `_deepMergeConfig`/`isGitIgnored`/`_warnUnknownProfileOverrides` helpers). Depends only on leaf modules (`configuration`, `config-schema`, `planning-workspace`, `shell-command-projection`, `core-utils`, `model-catalog`) — no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2e (#885) as the prerequisite for the model-resolver extraction (the resolvers call `loadConfig`); `core.cjs` re-exports `loadConfig` for back-compat. Source of truth: `gsd-core/bin/lib/config-loader.cjs` (generated from `src/config-loader.cts`). Module owning project configuration loading: reads `.planning/config.json`, merges built-in defaults (`CONFIG_DEFAULTS`/`CANONICAL_CONFIG_DEFAULTS`), normalizes legacy keys, applies the active-workstream overlay, validates against the config schema, and warns on unknown keys/profile overrides (`loadConfig` plus its `_deepMergeConfig`/`isGitIgnored`/`_warnUnknownProfileOverrides` helpers). Depends only on leaf modules (`configuration`, `config-schema`, `planning-workspace`, `shell-command-projection`, `core-utils`, `model-catalog`) — no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2e (#885) as the prerequisite for the model-resolver extraction (the resolvers call `loadConfig`); the `core.cjs` re-export spine was retired in epic #1267, so callers import this leaf directly. Source of truth: `gsd-core/bin/lib/config-loader.cjs` (generated from `src/config-loader.cts`).
### Model Resolver Module ### Model Resolver Module
Module owning model and effort resolution policy: resolves the model, runtime tier, planning granularity, reasoning effort, and fast-mode for a given agent by reading project config and resolving against the model profiles and catalog (`resolveModelInternal`, `resolveModelPolicy`, `resolveTierEntry`, `resolveModelForTier`, `resolveGranularityInternal`, `resolveEffortInternal`, `resolveFastModeInternal`, `resolveEffortForTier`, `nextEffort`, `assertValidGranularityOverride`). Depends only on leaf modules (`config-loader` for `loadConfig`, `configuration` for defaults, `model-profiles` and `model-catalog` for the static tables) — no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2f (#888) — the final core.cts decomposition step, leaving Core a thin re-export spine; `core.cjs` re-exports the resolvers for back-compat. Source of truth: `gsd-core/bin/lib/model-resolver.cjs` (generated from `src/model-resolver.cts`). Module owning model and effort resolution policy: resolves the model, runtime tier, planning granularity, reasoning effort, and fast-mode for a given agent by reading project config and resolving against the model profiles and catalog (`resolveModelInternal`, `resolveModelPolicy`, `resolveTierEntry`, `resolveModelForTier`, `resolveGranularityInternal`, `resolveEffortInternal`, `resolveFastModeInternal`, `resolveEffortForTier`, `nextEffort`, `assertValidGranularityOverride`). Depends only on leaf modules (`config-loader` for `loadConfig`, `configuration` for defaults, `model-profiles` and `model-catalog` for the static tables) — no other core dependency. Extracted from the Core module per ADR-857 rollout phase 2f (#888) — the final core.cts decomposition step; the `core.cjs` re-export spine was retired in epic #1267, so callers import this leaf directly. Source of truth: `gsd-core/bin/lib/model-resolver.cjs` (generated from `src/model-resolver.cts`).
### Package Identity Module [Planned] ### Package Identity Module [Planned]
Single seam owning GSD's published-package coordinates so a repoint/rename is a one-line change instead of a tree-wide sweep. Source of truth is `package.json`; values are *derived*, not re-typed: `packageName` (`.name` → `@opengsd/get-shit-done-redux`), `binName` (`Object.keys(.bin)[0]` → `get-shit-done-redux`), `repoSlug` (parsed from `.repository.url` → `open-gsd/get-shit-done-redux`), plus derived `changelogRawUrl` and `manualInstallCommand({ scope, runtime })`. Generated `.cjs` per ADR-457 (generated-single-source); shipped under `gsd-core/bin/lib/`. Three consumer worlds: **Node** consumers `require()` it at runtime (worker, `check-latest-version.cjs`, `bin/install.js`); the **bash launcher** snippet receives the literal injected by `scripts/sync-runtime-launcher.cjs` at sync time; **prose/help** literals (`update.md`, installer help) carry a committed copy. A drift-guard lint (`scripts/lint-package-identity-drift.cjs`, sibling to `check:alias-drift`) fails CI on any raw package/repo literal outside `package.json`, the generated module, and the value-checked materialization sites — this is what keeps the seam real (`two adapters`, not one). Replaces the contradictory pair it consolidates: the runtime-broken `require('../package.json').name` in `hooks/gsd-check-update-worker.js` (#378, resolves to `undefined` post-install) and the hardcoded constant in `check-latest-version.cjs` (#2992). _Avoid_: "package name string", "the npm name" (when you mean the seam). See ADR-457 and Installer Module. Single seam owning GSD's published-package coordinates so a repoint/rename is a one-line change instead of a tree-wide sweep. Source of truth is `package.json`; values are *derived*, not re-typed: `packageName` (`.name` → `@opengsd/get-shit-done-redux`), `binName` (`Object.keys(.bin)[0]` → `get-shit-done-redux`), `repoSlug` (parsed from `.repository.url` → `open-gsd/get-shit-done-redux`), plus derived `changelogRawUrl` and `manualInstallCommand({ scope, runtime })`. Generated `.cjs` per ADR-457 (generated-single-source); shipped under `gsd-core/bin/lib/`. Three consumer worlds: **Node** consumers `require()` it at runtime (worker, `check-latest-version.cjs`, `bin/install.js`); the **bash launcher** snippet receives the literal injected by `scripts/sync-runtime-launcher.cjs` at sync time; **prose/help** literals (`update.md`, installer help) carry a committed copy. A drift-guard lint (`scripts/lint-package-identity-drift.cjs`, sibling to `check:alias-drift`) fails CI on any raw package/repo literal outside `package.json`, the generated module, and the value-checked materialization sites — this is what keeps the seam real (`two adapters`, not one). Replaces the contradictory pair it consolidates: the runtime-broken `require('../package.json').name` in `hooks/gsd-check-update-worker.js` (#378, resolves to `undefined` post-install) and the hardcoded constant in `check-latest-version.cjs` (#2992). _Avoid_: "package name string", "the npm name" (when you mean the seam). See ADR-457 and Installer Module.
@@ -306,7 +306,7 @@ A legal deferred state of an Execute step (`external_job_waiting`): the executor
`RULESET.TESTS.no-source-grep.exemption=// allow-test-rule: <runtime-contract-is-the-product> with one-line justification; reserved for tests where the file content IS the product surface (STATE.md, config.toml, hooks.json, agent .md). Migration to typed-IR parser tracked in #2974.` `RULESET.TESTS.no-source-grep.exemption=// allow-test-rule: <runtime-contract-is-the-product> with one-line justification; reserved for tests where the file content IS the product surface (STATE.md, config.toml, hooks.json, agent .md). Migration to typed-IR parser tracked in #2974.`
`RULESET.TESTS.no-source-grep.tmp-file-traps=reading tmp files written by the SUT in tests still trips lint; round-trip through CLI (e.g. frontmatter get) instead of readFileSync+.includes()` `RULESET.TESTS.no-source-grep.tmp-file-traps=reading tmp files written by the SUT in tests still trips lint; round-trip through CLI (e.g. frontmatter get) instead of readFileSync+.includes()`
`RULESET.TESTS.escape-regex=new RegExp("prefix${var}") must escapeRegex(var); core.cjs already exports escapeRegex; phase IDs like 5.1 contain . which is metacharacter` `RULESET.TESTS.escape-regex=new RegExp("prefix${var}") must escapeRegex(var); phase-id.cjs exports escapeRegex (core.cjs re-export spine retired in epic #1267); phase IDs like 5.1 contain . which is metacharacter`
`RULESET.TESTS.no-dead-regex-in-includes=src.includes("foo.*bar") is always false — .* is regex metacharacter not wildcard; use new RegExp(...).test(src) or delete` `RULESET.TESTS.no-dead-regex-in-includes=src.includes("foo.*bar") is always false — .* is regex metacharacter not wildcard; use new RegExp(...).test(src) or delete`
`RULESET.TESTS.guard-toplevel-readFileSync=module-level const src = readFileSync(...) throws before any test() registers — wrap in try/catch in test() or use lazy load` `RULESET.TESTS.guard-toplevel-readFileSync=module-level const src = readFileSync(...) throws before any test() registers — wrap in try/catch in test() or use lazy load`
`RULESET.TESTS.coderabbit-fix-prefer=behavioral tests (call exported fn, capture JSON, assert typed fields) over source-grep` `RULESET.TESTS.coderabbit-fix-prefer=behavioral tests (call exported fn, capture JSON, assert typed fields) over source-grep`
@@ -379,7 +379,7 @@ A legal deferred state of an Execute step (`external_job_waiting`): the executor
`PR.3267.POSTMORTEM.recovery=[issue#3270 created, label approved-enhancement applied, PR reopened, body includes "Closes #3270", label no-changelog applied]` `PR.3267.POSTMORTEM.recovery=[issue#3270 created, label approved-enhancement applied, PR reopened, body includes "Closes #3270", label no-changelog applied]`
`WORKTREE.SEAM.current=Worktree Safety Policy Module` `WORKTREE.SEAM.current=Worktree Safety Policy Module`
`WORKTREE.SEAM.files=[gsd-core/bin/lib/worktree-safety.cjs, gsd-core/bin/lib/core.cjs]` `WORKTREE.SEAM.files=[gsd-core/bin/lib/worktree-safety.cjs]`
`WORKTREE.SEAM.interface=[resolveWorktreeContext, parseWorktreePorcelain, planWorktreePrune, executeWorktreePrunePlan]` `WORKTREE.SEAM.interface=[resolveWorktreeContext, parseWorktreePorcelain, planWorktreePrune, executeWorktreePrunePlan]`
`WORKTREE.SEAM.default-prune-policy=metadata_prune_only (non-destructive)` `WORKTREE.SEAM.default-prune-policy=metadata_prune_only (non-destructive)`
`WORKTREE.SEAM.decision-1=retain non-destructive default; destructive path only as explicit future opt-in scaffold` `WORKTREE.SEAM.decision-1=retain non-destructive default; destructive path only as explicit future opt-in scaffold`
@@ -683,7 +683,7 @@ Invariants:
- Platform policy owned at the seam: `shell: process.platform === 'win32'` lives only in run-npm; probeTty returns `null` on Windows. - Platform policy owned at the seam: `shell: process.platform === 'win32'` lives only in run-npm; probeTty returns `null` on Windows.
- Normalization policy: platformWriteSync owns full `normalizeMd` for `.md`; CRLF-to-LF + trailing newline for all others; callers must NOT pre-call `normalizeMd`. - Normalization policy: platformWriteSync owns full `normalizeMd` for `.md`; CRLF-to-LF + trailing newline for all others; callers must NOT pre-call `normalizeMd`.
- `_normalizeMd` is re-implemented inline (not imported from `core.cjs`) to avoid circular dep. - `_normalizeMd` is re-implemented inline (not imported from `core.cjs`) to avoid circular dep.
- `atomicWriteFileSync`, `safeReadFile`, `normalizeMd` remain in `core.cjs` exports until Phase 4 (#3468). - `atomicWriteFileSync`, `safeReadFile`, `normalizeMd` were in `core.cjs` exports (retired in epic #1267); callers now import these from their respective leaf modules directly.
Migration plan: Phase 1 (#3465) seam additions complete; Phase 2 (#3466) targets 6 subprocess files; Phase 3 (#3467) targets 15 fs files (215 call sites); Phase 4 (#3468) removes compat exports. Migration plan: Phase 1 (#3465) seam additions complete; Phase 2 (#3466) targets 6 subprocess files; Phase 3 (#3467) targets 15 fs files (215 call sites); Phase 4 (#3468) removes compat exports.

View File

@@ -265,9 +265,11 @@ const _gsdLibDir = path.join(__dirname, '..', 'gsd-core', 'bin', 'lib');
const { MODEL_PROFILES: GSD_MODEL_PROFILES } = require(path.join(_gsdLibDir, 'model-profiles.cjs')); const { MODEL_PROFILES: GSD_MODEL_PROFILES } = require(path.join(_gsdLibDir, 'model-profiles.cjs'));
const { const {
RUNTIME_PROFILE_MAP: GSD_RUNTIME_PROFILE_MAP, RUNTIME_PROFILE_MAP: GSD_RUNTIME_PROFILE_MAP,
} = require(path.join(_gsdLibDir, 'model-catalog.cjs'));
const {
resolveTierEntry: gsdResolveTierEntry, resolveTierEntry: gsdResolveTierEntry,
EFFORT_SET: GSD_EFFORT_SET, EFFORT_SET: GSD_EFFORT_SET,
} = require(path.join(_gsdLibDir, 'core.cjs')); } = require(path.join(_gsdLibDir, 'model-resolver.cjs'));
// #443 — model-catalog and config-defaults.manifest.json exports needed only // #443 — model-catalog and config-defaults.manifest.json exports needed only
// by effort-resolution code paths (resolveInstallTimeEffort / // by effort-resolution code paths (resolveInstallTimeEffort /

View File

@@ -300,7 +300,6 @@
"configuration.cjs", "configuration.cjs",
"context-utilization.cjs", "context-utilization.cjs",
"core-utils.cjs", "core-utils.cjs",
"core.cjs",
"decisions.cjs", "decisions.cjs",
"docs.cjs", "docs.cjs",
"drift.cjs", "drift.cjs",

View File

@@ -106,7 +106,6 @@ export default tseslint.config(
'gsd-core/bin/lib/runtime-config-adapter-registry.cjs', 'gsd-core/bin/lib/runtime-config-adapter-registry.cjs',
'gsd-core/bin/lib/runtime-hooks-surface.cjs', 'gsd-core/bin/lib/runtime-hooks-surface.cjs',
'gsd-core/bin/lib/command-routing-hub.cjs', 'gsd-core/bin/lib/command-routing-hub.cjs',
'gsd-core/bin/lib/core.cjs',
'gsd-core/bin/lib/core-utils.cjs', 'gsd-core/bin/lib/core-utils.cjs',
'gsd-core/bin/lib/io.cjs', 'gsd-core/bin/lib/io.cjs',
'gsd-core/bin/lib/phase-id.cjs', 'gsd-core/bin/lib/phase-id.cjs',

View File

@@ -220,7 +220,7 @@ Squash merge is recommended — keeps main branch history clean while preserving
## Complete Field Reference ## Complete Field Reference
Generated from `CONFIG_DEFAULTS` (core.cjs) and `VALID_CONFIG_KEYS` (config.cjs). Generated from `CONFIG_DEFAULTS` (configuration.cjs) and `VALID_CONFIG_KEYS` (config-schema.cjs).
### Core Fields ### Core Fields

View File

@@ -92,7 +92,7 @@
"pretest:coverage": "npm run build:lib && npm run lint:skill-deps", "pretest:coverage": "npm run build:lib && npm run lint:skill-deps",
"lint": "eslint . --cache --cache-location node_modules/.cache/eslint/", "lint": "eslint . --cache --cache-location node_modules/.cache/eslint/",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
"lint:ci": "npm run lint && npm run lint:skill-deps && node scripts/lint-test-file-count.cjs && node scripts/lint-command-contract.cjs && node scripts/lint-pr-check-project-dir.cjs && npm run lint:legacy-name && node scripts/lint-regression-test-names.cjs && node scripts/lint-windows-test-portability.cjs && node scripts/lint-allow-test-rule-refs.cjs && node scripts/lint-core-spine-imports.cjs", "lint:ci": "npm run lint && npm run lint:skill-deps && node scripts/lint-test-file-count.cjs && node scripts/lint-command-contract.cjs && node scripts/lint-pr-check-project-dir.cjs && npm run lint:legacy-name && node scripts/lint-regression-test-names.cjs && node scripts/lint-windows-test-portability.cjs && node scripts/lint-allow-test-rule-refs.cjs",
"lint:allow-test-rule-refs": "node scripts/lint-allow-test-rule-refs.cjs", "lint:allow-test-rule-refs": "node scripts/lint-allow-test-rule-refs.cjs",
"lint:windows-test-portability": "node scripts/lint-windows-test-portability.cjs", "lint:windows-test-portability": "node scripts/lint-windows-test-portability.cjs",
"lint:regression-names": "node scripts/lint-regression-test-names.cjs", "lint:regression-names": "node scripts/lint-regression-test-names.cjs",

View File

@@ -1,4 +0,0 @@
{
"_comment": "Files allowed to import the core re-export spine during the staged retirement (issue #1268). Entries are REMOVED as each tranche migrates a leaf; the file + this lint are deleted in T-final.",
"allow": []
}

View File

@@ -1,203 +0,0 @@
#!/usr/bin/env node
'use strict';
/**
* Migration-convergence lint for the core re-export spine (issue #1268).
*
* The spine (core.cjs) is being staged for retirement: each tranche migrates a
* set of leaf importers to consume the individual modules directly. This lint
* makes CI RED the moment a NEW file starts importing the spine, preventing
* regressions as the tranche work proceeds.
*
* Allowlisted importers (the "T0 set") are files that already imported the
* spine when this lint was introduced. Entries are REMOVED from the allowlist
* as each tranche migrates a leaf. The allowlist + this script are deleted in
* T-final.
*
* Model: scripts/lint-package-identity-drift.cjs
*/
const fs = require('node:fs');
const path = require('node:path');
// ---------------------------------------------------------------------------
// Core-spine import detection
// ---------------------------------------------------------------------------
/**
* Matches any import form that targets the core spine at any relative depth,
* including the TS `import x = require(...)` form, dynamic imports, and bare
* side-effect imports.
*
* Patterns matched (illustrative):
* require('./core.cjs')
* require('./core')
* require('../core.cjs')
* require('./lib/core.cjs')
* require('../lib/core.cjs')
* require('../../lib/core.cjs')
* from './core.cjs'
* import core = require('../lib/core.cjs')
* import('./core.cjs') dynamic import
* await import('../lib/core.cjs') dynamic import
* import './core.cjs'; bare side-effect import
*
* Patterns NOT matched (boundary after bare `core` is quote or `.cjs`):
* require('./core-utils.cjs')
* require('./core-schema.cjs')
*
* The `import\s+` branch (bare side-effect) will NOT match
* `import x = require(...)` because a non-quote token follows `import `.
*/
const CORE_IMPORT_RE = /(?:require\(\s*|import\s*\(\s*|from\s+|import\s+)['"](?:\.\.?\/)+(?:lib\/)?core(?:\.cjs)?['"]/;
/**
* Return true if `line` is a real (non-commented) core-spine import.
* @param {string} line
* @returns {boolean}
*/
function lineImportsSpine(line) {
if (line.trimStart().startsWith('//')) return false;
return CORE_IMPORT_RE.test(line);
}
// ---------------------------------------------------------------------------
// Directory walking
// ---------------------------------------------------------------------------
/**
* Recursively collect files under `dir` matching `extSet`, skipping
* `node_modules`, `.git`, `.memdb`, and any caller-supplied `skipDirs`.
* (The caller is responsible for scoping roots; this function does NOT itself
* skip `tests/` — that exclusion is achieved by not passing `tests/` as a root.)
*
* @param {string} dir - Absolute directory to walk.
* @param {Set<string>} extSet - File extensions to include (e.g. new Set(['.cts','.cjs'])).
* @param {Set<string>} skipDirs - Absolute paths of directories to skip entirely.
* @param {string[]} acc - Accumulator (modified in place).
* @returns {string[]}
*/
function walk(dir, extSet, skipDirs, acc) {
let entries;
try {
entries = fs.readdirSync(dir, { withFileTypes: true });
} catch {
return acc;
}
for (const entry of entries) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === '.memdb') continue;
if (skipDirs.has(full)) continue;
walk(full, extSet, skipDirs, acc);
} else if (entry.isFile() && extSet.has(path.extname(entry.name))) {
acc.push(full);
}
}
return acc;
}
// ---------------------------------------------------------------------------
// Pure exported API
// ---------------------------------------------------------------------------
/**
* Scan `roots` for source files that import the core spine but are not in
* `allowlistSet`.
*
* @param {string[]} roots - Absolute directories to scan.
* @param {Set<string>} allowlistSet - Repo-relative POSIX paths that are
* allowed to import the spine (the T0 set).
* @returns {{ file: string, line: number }[]}
* Repo-relative POSIX file path and 1-based line number for each violation.
*/
function scanCoreSpineImports(roots, allowlistSet) {
const repoRoot = path.join(__dirname, '..');
// gsd-core/bin/lib is excluded (generated artefacts — double-counts src/).
const skipDirs = new Set([path.join(repoRoot, 'gsd-core', 'bin', 'lib')]);
// src/*.cts → .cts only; gsd-core/bin/**/*.cjs → .cjs only.
// Accept either extension in each root; walk will naturally skip unsupported
// files via extSet filtering.
const extSet = new Set(['.cts', '.cjs']);
const violations = [];
for (const root of roots) {
const files = walk(root, extSet, skipDirs, []);
for (const abs of files) {
// Derive repo-relative POSIX path for allowlist lookup and reporting.
const rel = path.relative(repoRoot, abs).replace(/\\/g, '/');
let text;
try {
text = fs.readFileSync(abs, 'utf8');
} catch {
continue;
}
const lines = text.split('\n');
for (let i = 0; i < lines.length; i++) {
if (lineImportsSpine(lines[i])) {
if (!allowlistSet.has(rel)) {
violations.push({ file: rel, line: i + 1 });
}
// Do NOT break: report every spine-import line so no violation is masked.
}
}
}
}
return violations;
}
/**
* Load the allowlist JSON and return a Set of repo-relative paths.
*
* @param {string} allowlistPath - Absolute path to the JSON file.
* @returns {Set<string>}
*/
function loadAllowlist(allowlistPath) {
const raw = fs.readFileSync(allowlistPath, 'utf8');
const obj = JSON.parse(raw);
return new Set(obj.allow || []);
}
// ---------------------------------------------------------------------------
// CLI entry point
// ---------------------------------------------------------------------------
function main() {
const repoRoot = path.join(__dirname, '..');
const allowlistPath = path.join(__dirname, 'lint-core-spine-imports.allowlist.json');
const allowlistSet = loadAllowlist(allowlistPath);
const roots = [
path.join(repoRoot, 'src'),
path.join(repoRoot, 'gsd-core', 'bin'),
];
const violations = scanCoreSpineImports(roots, allowlistSet);
if (violations.length === 0) {
process.stdout.write(
`ok core-spine-imports: ${allowlistSet.size} allowlisted importer(s), 0 new\n`,
);
return;
}
process.stderr.write('core-spine-imports: new file(s) importing the core re-export spine detected.\n');
process.stderr.write('The spine (core.cjs) is being retired (issue #1268). Import the leaf module\n');
process.stderr.write('directly instead, or add to the allowlist only if genuinely transitional:\n');
for (const v of violations) {
process.stderr.write(` ${v.file}:${v.line}\n`);
}
process.stderr.write(`core-spine-imports: ${violations.length} violation(s)\n`);
process.exitCode = 1;
}
if (require.main === module) main();
module.exports = { scanCoreSpineImports, loadAllowlist };

View File

@@ -5,8 +5,8 @@
* - getAgentsDir(runtime?): string * - getAgentsDir(runtime?): string
* - checkAgentsInstalled(runtime?): AgentsInstalledResult * - checkAgentsInstalled(runtime?): AgentsInstalledResult
* *
* Consumers that previously imported these via core.cjs continue to work: * The core.cjs re-export spine was retired in epic #1267; callers import
* core.cjs re-exports both symbols BY REFERENCE from this module. * these symbols from agent-install-check.cjs directly.
*/ */
import fs from 'node:fs'; import fs from 'node:fs';

View File

@@ -7,11 +7,10 @@
* normalizes legacy keys, applies the active-workstream overlay, validates * normalizes legacy keys, applies the active-workstream overlay, validates
* against the config schema, and warns on unknown keys/profile overrides. * against the config schema, and warns on unknown keys/profile overrides.
* Behaviour is preserved byte-for-behaviour from the prior location; only * Behaviour is preserved byte-for-behaviour from the prior location; only
* the module boundary moved. core.cjs re-exports `loadConfig` for back-compat. * the module boundary moved. The core.cjs re-export spine was retired in
* epic #1267; callers import loadConfig from config-loader.cjs directly.
* *
* New imports should pull loadConfig from config-loader.cjs directly. * Dependencies (leaf modules only):
*
* Dependencies (leaf modules only — no core.cjs):
* - node:fs / node:os / node:path (stdlib) * - node:fs / node:os / node:path (stdlib)
* - ./configuration.cjs (normalizeLegacyKeys, CONFIG_DEFAULTS as CANONICAL_CONFIG_DEFAULTS) * - ./configuration.cjs (normalizeLegacyKeys, CONFIG_DEFAULTS as CANONICAL_CONFIG_DEFAULTS)
* - ./config-schema.cjs (VALID_CONFIG_KEYS, DYNAMIC_KEY_PATTERNS) * - ./config-schema.cjs (VALID_CONFIG_KEYS, DYNAMIC_KEY_PATTERNS)

View File

@@ -5,8 +5,8 @@
* *
* Imported by: * Imported by:
* - config.cjs (isValidConfigKey validator) * - config.cjs (isValidConfigKey validator)
* - core.cjs
* - many tests (config-schema.property.test.cjs, bug-*, feat-*, etc.) * - many tests (config-schema.property.test.cjs, bug-*, feat-*, etc.)
* (core.cjs re-export spine retired in epic #1267)
* *
* See Phase 2 Cycle 5 (#3536) — schema manifest migration. * See Phase 2 Cycle 5 (#3536) — schema manifest migration.
* *

View File

@@ -2,7 +2,8 @@
* TypeScript type definitions for GSD project config — model_policy block. * TypeScript type definitions for GSD project config — model_policy block.
* *
* These types reflect the model_policy config shape consumed by * These types reflect the model_policy config shape consumed by
* resolveModelPolicy in core.cjs and validated by config-schema.cjs. * resolveModelPolicy in model-resolver.cjs and validated by config-schema.cjs.
* (core.cjs re-export spine retired in epic #1267)
* *
* See feat #49 (model_policy presets) and config-schema.manifest.json. * See feat #49 (model_policy presets) and config-schema.manifest.json.
* Added under ADR-457: TS sources in src/ compile to CJS artifacts in * Added under ADR-457: TS sources in src/ compile to CJS artifacts in

View File

@@ -1,219 +0,0 @@
/**
* Core — Shared utilities, constants, and internal helpers
*
* ADR-457 build-at-publish: the hand-written bin/lib/core.cjs collapsed
* to a TypeScript source of truth. Behaviour is preserved byte-for-behaviour
* from the prior hand-written .cjs; only strict types are added.
*/
// (fs, path, execGit removed — last callers relocated to leaf modules during #1268 T0 rehome)
// eslint-disable-next-line @typescript-eslint/no-require-imports
import ioModule = require('./io.cjs');
const { output, error, ERROR_REASON, setJsonErrorMode, getJsonErrorMode, GSD_TEMP_DIR, reapStaleTempFiles } = ioModule;
// eslint-disable-next-line @typescript-eslint/no-require-imports
import phaseIdModule = require('./phase-id.cjs');
const { escapeRegex, normalizePhaseName, getMilestoneFromPhaseId, getPhaseDirFromPhaseId, phaseMarkdownRegexSource, phaseMarkdownRegexSourceExact, comparePhaseNum, extractPhaseToken, phaseTokenMatches } = phaseIdModule;
// eslint-disable-next-line @typescript-eslint/no-require-imports
import roadmapParserModule = require('./roadmap-parser.cjs');
const { stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone, getRoadmapPhaseInternal, getMilestoneInfo, getMilestonePhaseFilter } = roadmapParserModule;
import { RUNTIME_PROFILE_MAP, KNOWN_RUNTIMES, RUNTIMES_WITH_REASONING_EFFORT, RUNTIMES_WITH_FAST_MODE, KNOWN_PROVIDERS, MODEL_ALIAS_MAP } from './model-catalog.cjs';
// eslint-disable-next-line @typescript-eslint/no-require-imports
import worktreeSafety = require('./worktree-safety.cjs');
const {
inspectWorktreeHealth,
} = worktreeSafety;
// eslint-disable-next-line @typescript-eslint/no-require-imports
import planningWorkspace = require('./planning-workspace.cjs');
// Compatibility shim: new imports should use planning-workspace.cjs directly.
const {
planningDir,
planningRoot,
planningPaths,
withPlanningLock,
getActiveWorkstream,
setActiveWorkstream,
} = planningWorkspace;
// eslint-disable-next-line @typescript-eslint/no-require-imports
import coreUtilsModule = require('./core-utils.cjs');
const {
toPosixPath,
detectSubRepos,
extractOneLinerFromBody,
pathExistsInternal,
generateSlugInternal,
filterPlanFiles,
filterSummaryFiles,
getPhaseFileStats,
readSubdirectories,
timeAgo,
} = coreUtilsModule;
// eslint-disable-next-line @typescript-eslint/no-require-imports
import phaseLocatorModule = require('./phase-locator.cjs');
const { searchPhaseInDir, findPhaseInternal, getArchivedPhaseDirs } = phaseLocatorModule;
import { findProjectRoot } from './project-root.cjs';
// ─── Config Loader Module (extracted from core, ADR-857 phase 2e / #885) ─────
// eslint-disable-next-line @typescript-eslint/no-require-imports
import configLoaderModule = require('./config-loader.cjs');
const {
loadConfig,
isGitIgnored,
CONFIG_DEFAULTS,
_warnUnknownProfileOverrides,
RUNTIME_OVERRIDE_TIERS,
} = configLoaderModule;
// ─── Model Resolver Module (extracted from core, ADR-857 phase 2f / #888) ────
// eslint-disable-next-line @typescript-eslint/no-require-imports
import modelResolverModule = require('./model-resolver.cjs');
const {
resolveTierEntry,
resolveModelPolicy,
resolveModelInternal,
VALID_GRANULARITIES,
resolveGranularityInternal,
assertValidGranularityOverride,
resolveModelForTier,
VALID_EFFORTS,
EFFORT_SET,
nextEffort,
resolveEffortInternal,
resolveFastModeInternal,
resolveEffortForTier,
} = modelResolverModule;
// ─── Path helpers ────────────────────────────────────────────────────────────
// toPosixPath and detectSubRepos moved to core-utils.cjs (ADR-857 phase 2c / #877).
// The destructured bindings above (from coreUtilsModule) make them available to
// core-internal callers; core.cjs re-exports toPosixPath and detectSubRepos for back-compat.
// findProjectRoot is now re-exported from the generated CJS module above.
// loadConfig, isGitIgnored, CONFIG_DEFAULTS, and related helpers moved to
// config-loader.cjs (ADR-857 phase 2e / #885). The destructured bindings above
// (from configLoaderModule) make them available to core-internal callers;
// core.cjs re-exports loadConfig and isGitIgnored for back-compat.
// ─── Planning workspace (pathing + active workstream + lock) moved to planning-workspace.cjs ───
// ─── Phase utilities (pure helpers re-exported from phase-id.cjs) ─────────────
// escapeRegex, normalizePhaseName, getMilestoneFromPhaseId, getPhaseDirFromPhaseId,
// phaseMarkdownRegexSource, phaseMarkdownRegexSourceExact, comparePhaseNum,
// extractPhaseToken, phaseTokenMatches
// — all imported via `phaseIdModule` above; internal callers use the destructured bindings.
// extractCanonicalPlanId moved to core-utils.cjs (ADR-857 phase 2c / #877).
// It is consumed exclusively by phase-locator.cjs, which imports it from
// core-utils.cjs directly. It is NOT destructured in core.cts and is NOT
// in core.cjs's public export = block (it was never public).
// searchPhaseInDir, findPhaseInternal, getArchivedPhaseDirs moved to phase-locator.cjs
// (ADR-857 phase 2d / #881). The destructured bindings above (from phaseLocatorModule)
// make them available to core-internal callers; core.cjs re-exports findPhaseInternal,
// getArchivedPhaseDirs, and searchPhaseInDir for back-compat.
// ─── Roadmap milestone scoping (re-exported from roadmap-parser.cjs) ──────────
// stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone,
// getRoadmapPhaseInternal, getMilestoneInfo, getMilestonePhaseFilter
// — all imported via `roadmapParserModule` above; internal callers use the destructured bindings.
// ─── Agent installation validation (#1371) ───────────────────────────────────
// getAgentsDir and checkAgentsInstalled moved to agent-install-check.cjs (T0 #1268).
// Re-exports removed in T1 (#1277); callers import directly from agent-install-check.cjs.
// ─── Model alias resolution ───────────────────────────────────────────────────
// RUNTIME_OVERRIDE_TIERS, _warnedConfigKeys, _warnUnknownProfileOverrides, and
// _resetRuntimeWarningCacheForTests moved to config-loader.cjs (ADR-857 phase 2e / #885)
// and model-resolver.cjs (ADR-857 phase 2f / #888) respectively.
// _resetRuntimeWarningCacheForTests is NO LONGER re-exported from core.cjs; callers
// must import it directly from config-loader.cjs / model-resolver.cjs (or use
// the shared resetRuntimeWarningCaches() helper in tests/helpers.cjs).
// resolveTierEntry, resolveModelPolicy, resolveModelInternal, VALID_GRANULARITIES,
// resolveGranularityInternal, assertValidGranularityOverride, resolveModelForTier,
// VALID_EFFORTS, EFFORT_SET, nextEffort, resolveEffortInternal, resolveFastModeInternal,
// resolveEffortForTier — all moved to model-resolver.cjs (ADR-857 phase 2f / #888).
// The destructured bindings above (from modelResolverModule) make them available to
// core-internal callers; core.cjs re-exports all 13 symbols for back-compat.
// ─── Summary body helpers / Misc utilities / Phase file helpers ───────────────
// extractOneLinerFromBody, pathExistsInternal, generateSlugInternal,
// filterPlanFiles, filterSummaryFiles, getPhaseFileStats, readSubdirectories,
// timeAgo — all moved to core-utils.cjs (ADR-857 phase 2c / #877).
// The destructured bindings above (from coreUtilsModule) make them available
// to core-internal callers; core.cjs re-exports the public ones for back-compat.
// MilestoneInfo, MilestonePhaseFilter, getMilestoneInfo, getMilestonePhaseFilter
// — all re-exported from roadmap-parser.cjs via roadmapParserModule above.
export = {
output,
error,
ERROR_REASON,
setJsonErrorMode,
getJsonErrorMode,
loadConfig,
isGitIgnored,
escapeRegex,
normalizePhaseName,
getMilestoneFromPhaseId,
getPhaseDirFromPhaseId,
phaseMarkdownRegexSource,
phaseMarkdownRegexSourceExact,
comparePhaseNum,
searchPhaseInDir,
extractPhaseToken,
phaseTokenMatches,
findPhaseInternal,
getArchivedPhaseDirs,
getRoadmapPhaseInternal,
resolveModelInternal,
resolveModelForTier,
resolveGranularityInternal,
VALID_GRANULARITIES,
assertValidGranularityOverride,
resolveEffortInternal,
resolveFastModeInternal,
resolveEffortForTier,
VALID_EFFORTS,
EFFORT_SET,
nextEffort,
RUNTIME_PROFILE_MAP,
RUNTIMES_WITH_REASONING_EFFORT,
RUNTIMES_WITH_FAST_MODE,
KNOWN_RUNTIMES,
RUNTIME_OVERRIDE_TIERS,
resolveTierEntry,
resolveModelPolicy,
KNOWN_PROVIDERS,
pathExistsInternal,
generateSlugInternal,
getMilestoneInfo,
getMilestonePhaseFilter,
stripShippedMilestones,
extractCurrentMilestone,
replaceInCurrentMilestone,
toPosixPath,
extractOneLinerFromBody,
resolveWorktreeRoot: worktreeSafety.resolveWorktreeRoot,
// Deprecated re-exports — prefer direct import from planning-workspace.cjs
withPlanningLock,
findProjectRoot,
detectSubRepos,
reapStaleTempFiles,
GSD_TEMP_DIR,
MODEL_ALIAS_MAP,
CONFIG_DEFAULTS,
planningDir,
planningRoot,
planningPaths,
getActiveWorkstream,
setActiveWorkstream,
filterPlanFiles,
filterSummaryFiles,
getPhaseFileStats,
readSubdirectories,
timeAgo,
pruneOrphanedWorktrees: worktreeSafety.pruneOrphanedWorktrees,
inspectWorktreeHealth,
};

View File

@@ -176,7 +176,7 @@ export function resolveBaseBranch(
): string { ): string {
const execGit: ExecGitFn = deps?.execGit ?? execGitSeam; const execGit: ExecGitFn = deps?.execGit ?? execGitSeam;
// Derive .planning dir relative to cwd (mirrors planningDir() in core.cjs) // Derive .planning dir relative to cwd (mirrors planningDir() in planning-workspace.cjs)
const planningDir = path.join(cwd, '.planning'); const planningDir = path.join(cwd, '.planning');
// 1. Config override // 1. Config override

View File

@@ -4,10 +4,8 @@
* *
* Extracted from core.cts (ADR-857 rollout phase 1 / issue #859). * Extracted from core.cts (ADR-857 rollout phase 1 / issue #859).
* The hand-written bodies are preserved byte-for-behaviour; only the module * The hand-written bodies are preserved byte-for-behaviour; only the module
* boundary moved. core.cts re-exports every symbol here under its own * boundary moved. The core.cjs re-export spine was retired in epic #1267;
* `export =` object so existing consumers are unaffected. * callers import I/O primitives from io.cjs directly.
*
* New imports should pull I/O primitives from io.cjs directly.
*/ */
import fs from 'node:fs'; import fs from 'node:fs';

View File

@@ -138,7 +138,7 @@ function cmdMilestoneComplete(cwd: string, version: string, options: MilestoneCo
platformEnsureDir(archiveDir); platformEnsureDir(archiveDir);
// Scope stats and accomplishments to only the phases belonging to the // Scope stats and accomplishments to only the phases belonging to the
// current milestone's ROADMAP. Uses the shared filter from core.cjs // current milestone's ROADMAP. Uses the shared filter from roadmap-parser.cjs
// (same logic used by cmdPhasesList and other callers). // (same logic used by cmdPhasesList and other callers).
const isDirInMilestone = getMilestonePhaseFilter(cwd, version); const isDirInMilestone = getMilestonePhaseFilter(cwd, version);
if (isDirInMilestone.missingExplicitVersion) { if (isDirInMilestone.missingExplicitVersion) {

View File

@@ -6,11 +6,10 @@
* planning granularity, reasoning effort, and fast-mode for a given agent by * planning granularity, reasoning effort, and fast-mode for a given agent by
* reading project config and resolving against the model profiles and catalog. * reading project config and resolving against the model profiles and catalog.
* Behaviour is preserved byte-for-behaviour from the prior location; only * Behaviour is preserved byte-for-behaviour from the prior location; only
* the module boundary moved. core.cjs re-exports the resolvers for back-compat. * the module boundary moved. The core.cjs re-export spine was retired in
* epic #1267; callers import resolvers from model-resolver.cjs directly.
* *
* New imports should pull resolvers from model-resolver.cjs directly. * Dependencies (leaf modules only):
*
* Dependencies (leaf modules only — no core.cjs):
* - node:fs / node:path (stdlib, not currently needed — included for future use) * - node:fs / node:path (stdlib, not currently needed — included for future use)
* - ./config-loader.cjs (loadConfig) * - ./config-loader.cjs (loadConfig)
* - ./configuration.cjs (CONFIG_DEFAULTS as CANONICAL_CONFIG_DEFAULTS) * - ./configuration.cjs (CONFIG_DEFAULTS as CANONICAL_CONFIG_DEFAULTS)

View File

@@ -4,10 +4,8 @@
* *
* Extracted from core.cts (ADR-857 rollout phase 2a / issue #865). * Extracted from core.cts (ADR-857 rollout phase 2a / issue #865).
* The hand-written bodies are preserved byte-for-behaviour; only the module * The hand-written bodies are preserved byte-for-behaviour; only the module
* boundary moved. core.cts re-exports every symbol here under its own * boundary moved. The core.cjs re-export spine was retired in epic #1267;
* `export =` object so existing consumers are unaffected. * callers import phase-id helpers from phase-id.cjs directly.
*
* New imports should pull phase-id helpers from phase-id.cjs directly.
* *
* Dependencies: none (pure string/regex, no Node built-ins required). * Dependencies: none (pure string/regex, no Node built-ins required).
*/ */

View File

@@ -6,13 +6,10 @@
* (`searchPhaseInDir`, `findPhaseInternal`) and archived-phase-dir * (`searchPhaseInDir`, `findPhaseInternal`) and archived-phase-dir
* enumeration (`getArchivedPhaseDirs`), matching phase ids/tokens against * enumeration (`getArchivedPhaseDirs`), matching phase ids/tokens against
* the filesystem. Behaviour is preserved byte-for-behaviour from the prior * the filesystem. Behaviour is preserved byte-for-behaviour from the prior
* location; only the module boundary moved. core.cjs re-exports * location; only the module boundary moved. The core.cjs re-export spine
* `searchPhaseInDir`, `findPhaseInternal`, and `getArchivedPhaseDirs` for back-compat. * was retired in epic #1267; callers import phase-locator helpers directly.
* *
* New imports should pull phase-locator helpers from phase-locator.cjs * Dependencies (leaf modules only — no loadConfig):
* directly.
*
* Dependencies (leaf modules only — no core.cjs, no loadConfig):
* - node:fs / node:path (stdlib) * - node:fs / node:path (stdlib)
* - ./phase-id.cjs (normalizePhaseName, phaseTokenMatches, extractPhaseToken) * - ./phase-id.cjs (normalizePhaseName, phaseTokenMatches, extractPhaseToken)
* - ./core-utils.cjs (readSubdirectories, getPhaseFileStats, extractCanonicalPlanId, toPosixPath) * - ./core-utils.cjs (readSubdirectories, getPhaseFileStats, extractCanonicalPlanId, toPosixPath)

View File

@@ -41,7 +41,7 @@
import fs from 'node:fs'; import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { execFileSync } from 'node:child_process'; import { execFileSync } from 'node:child_process';
// Import the leaf I/O module directly, not the core.cjs re-export spine (being retired, #1268). // Import the leaf I/O module directly (core.cjs re-export spine retired in epic #1267).
// eslint-disable-next-line @typescript-eslint/no-require-imports // eslint-disable-next-line @typescript-eslint/no-require-imports
import io = require('./io.cjs'); import io = require('./io.cjs');
const { output, error, ERROR_REASON } = io; const { output, error, ERROR_REASON } = io;

View File

@@ -5,12 +5,10 @@
* Owns shipped-milestone slicing, current-milestone extraction, * Owns shipped-milestone slicing, current-milestone extraction,
* milestone/phase lookups, and milestone-phase filtering. * milestone/phase lookups, and milestone-phase filtering.
* Behaviour is preserved byte-for-behaviour from the prior location; * Behaviour is preserved byte-for-behaviour from the prior location;
* only the module boundary moved. core.cjs re-exports every symbol here * only the module boundary moved. The core.cjs re-export spine was retired
* under its own `export =` object so existing consumers are unaffected. * in epic #1267; callers import roadmap-parser helpers directly.
* *
* New imports should pull roadmap-parser helpers from roadmap-parser.cjs directly. * Dependencies (leaf modules only — no loadConfig):
*
* Dependencies (leaf modules only — no core.cjs, no loadConfig):
* - node:fs / node:path (stdlib) * - node:fs / node:path (stdlib)
* - ./phase-id.cjs (escapeRegex, phaseMarkdownRegexSource) * - ./phase-id.cjs (escapeRegex, phaseMarkdownRegexSource)
* - ./planning-workspace.cjs (planningDir) * - ./planning-workspace.cjs (planningDir)

View File

@@ -251,8 +251,7 @@ function executeWorktreePrunePlan(plan: WorktreePrunePlan | null, deps: Worktree
const result = execGit(['worktree', 'prune'], { cwd: plan.repoRoot }); const result = execGit(['worktree', 'prune'], { cwd: plan.repoRoot });
if (result.timedOut) { if (result.timedOut) {
// AC4: surface timedOut as a first-class field so callers (e.g. // AC4: surface timedOut as a first-class field so callers can log a structured WARNING rather
// pruneOrphanedWorktrees in core.cjs) can log a structured WARNING rather
// than silently ignoring it (PRED.k302 — error-swallowing-empty-sentinel). // than silently ignoring it (PRED.k302 — error-swallowing-empty-sentinel).
return { return {
ok: false, ok: false,

View File

@@ -13,7 +13,7 @@ const assert = require('node:assert/strict');
const path = require('path'); const path = require('path');
const { extractOneLinerFromBody } = require( const { extractOneLinerFromBody } = require(
path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'core.cjs') path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'core-utils.cjs')
); );
describe('bug #2660: extractOneLinerFromBody', () => { describe('bug #2660: extractOneLinerFromBody', () => {

View File

@@ -26,7 +26,7 @@ const os = require('node:os');
const { execFileSync } = require('node:child_process'); const { execFileSync } = require('node:child_process');
const GSD_TOOLS = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs'); const GSD_TOOLS = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
const { ERROR_REASON } = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'core.cjs')); const { ERROR_REASON } = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'io.cjs'));
const { cleanup } = require('./helpers.cjs'); const { cleanup } = require('./helpers.cjs');
describe('bug-2943: config-get returns schema default for context_window', () => { describe('bug-2943: config-get returns schema default for context_window', () => {

View File

@@ -68,7 +68,7 @@ describe('no-source-grep rule', () => {
code: ` code: `
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const src = fs.readFileSync(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'core.cjs'), 'utf-8'); const src = fs.readFileSync(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'io.cjs'), 'utf-8');
src.includes('someFunction'); src.includes('someFunction');
`, `,
filename: 'tests/foo.test.cjs', filename: 'tests/foo.test.cjs',
@@ -105,7 +105,7 @@ describe('no-source-grep rule', () => {
// allow-test-rule: pending migration // allow-test-rule: pending migration
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const src = fs.readFileSync(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'core.cjs'), 'utf-8'); const src = fs.readFileSync(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'io.cjs'), 'utf-8');
src.includes('someFunction'); src.includes('someFunction');
`, `,
filename: 'tests/foo.test.cjs', filename: 'tests/foo.test.cjs',

View File

@@ -262,7 +262,7 @@ describe('#3023 + #443: unified effort resolver (resolveEffortInternal) for Code
beforeEach(() => { projectDir = makeTmp('effort'); }); beforeEach(() => { projectDir = makeTmp('effort'); });
afterEach(() => { rmr(projectDir); }); afterEach(() => { rmr(projectDir); });
test('resolveEffortInternal exported from core.cjs', () => { test('resolveEffortInternal exported from model-resolver.cjs', () => {
assert.equal(typeof resolveEffortInternal, 'function'); assert.equal(typeof resolveEffortInternal, 'function');
}); });

View File

@@ -719,11 +719,11 @@ describe('#49 isValidConfigKey: model_policy.* keys accepted/rejected', () => {
// ─── KNOWN_PROVIDERS export tests ───────────────────────────────────────────── // ─── KNOWN_PROVIDERS export tests ─────────────────────────────────────────────
describe('#49 KNOWN_PROVIDERS exports from model-catalog.cjs and core.cjs', () => { describe('#49 KNOWN_PROVIDERS exports from model-catalog.cjs', () => {
test('KNOWN_PROVIDERS exported from core.cjs includes all keys from providerPresets in catalog', () => { test('KNOWN_PROVIDERS exported from model-catalog.cjs includes all keys from providerPresets in catalog', () => {
// KNOWN_PROVIDERS must be a Set (or array) exported from core.cjs. // KNOWN_PROVIDERS must be a Set (or array) exported from model-catalog.cjs.
assert.ok(KNOWN_PROVIDERS != null, assert.ok(KNOWN_PROVIDERS != null,
'KNOWN_PROVIDERS must be exported from core.cjs'); 'KNOWN_PROVIDERS must be exported from model-catalog.cjs');
const isIterable = typeof KNOWN_PROVIDERS[Symbol.iterator] === 'function'; const isIterable = typeof KNOWN_PROVIDERS[Symbol.iterator] === 'function';
assert.ok(isIterable, assert.ok(isIterable,
'KNOWN_PROVIDERS must be iterable (Set or array)'); 'KNOWN_PROVIDERS must be iterable (Set or array)');
@@ -741,15 +741,14 @@ describe('#49 KNOWN_PROVIDERS exports from model-catalog.cjs and core.cjs', () =
'KNOWN_PROVIDERS must not include "generic" (it is not a catalog-backed provider)'); 'KNOWN_PROVIDERS must not include "generic" (it is not a catalog-backed provider)');
}); });
test('KNOWN_PROVIDERS exported from model-catalog.cjs matches core.cjs re-export', () => { test('KNOWN_PROVIDERS from model-catalog.cjs is the canonical export', () => {
// model-catalog.cjs must also export KNOWN_PROVIDERS (the canonical source). // model-catalog.cjs is the canonical source of KNOWN_PROVIDERS.
// core.cjs re-exports it. Both must be identical.
assert.ok(modelCatalog.KNOWN_PROVIDERS != null, assert.ok(modelCatalog.KNOWN_PROVIDERS != null,
'KNOWN_PROVIDERS must be exported from model-catalog.cjs'); 'KNOWN_PROVIDERS must be exported from model-catalog.cjs');
const fromCatalog = [...modelCatalog.KNOWN_PROVIDERS].sort(); const fromCatalog = [...modelCatalog.KNOWN_PROVIDERS].sort();
const fromCore = [...KNOWN_PROVIDERS].sort(); const fromImport = [...KNOWN_PROVIDERS].sort();
assert.deepStrictEqual(fromCore, fromCatalog, assert.deepStrictEqual(fromImport, fromCatalog,
'KNOWN_PROVIDERS from core.cjs (re-export) must match model-catalog.cjs canonical export'); 'KNOWN_PROVIDERS imported from model-catalog.cjs must match the module export');
}); });
}); });

View File

@@ -359,7 +359,7 @@ describe('intel router: unit tests via recording mocks', () => {
test('routeIntelCommand extract-exports: calls intelExtractExports(path.resolve(cwd, filePath))', () => { test('routeIntelCommand extract-exports: calls intelExtractExports(path.resolve(cwd, filePath))', () => {
const calls = []; const calls = [];
const c = makeCoreMock(); const c = makeCoreMock();
const FILE_ARG = 'lib/core.cjs'; const FILE_ARG = 'lib/io.cjs';
const EXPECTED = path.resolve(CWD, FILE_ARG); const EXPECTED = path.resolve(CWD, FILE_ARG);
routeIntelCommand({ routeIntelCommand({

View File

@@ -619,14 +619,14 @@ describe('issue #2517: install end-to-end — per-project config reaches Codex T
// resolver-construction time. Catches accidental relative-path drift in CI. // resolver-construction time. Catches accidental relative-path drift in CI.
const installDir = path.dirname(require.resolve('../bin/install.js')); const installDir = path.dirname(require.resolve('../bin/install.js'));
const libDir = path.join(installDir, '..', 'gsd-core', 'bin', 'lib'); const libDir = path.join(installDir, '..', 'gsd-core', 'bin', 'lib');
assert.ok(fs.existsSync(path.join(libDir, 'core.cjs'))); assert.ok(fs.existsSync(path.join(libDir, 'model-catalog.cjs')));
assert.ok(fs.existsSync(path.join(libDir, 'model-profiles.cjs'))); assert.ok(fs.existsSync(path.join(libDir, 'model-profiles.cjs')));
}); });
}); });
// ─── RUNTIME_PROFILE_MAP single source of truth (finding #16) ─────────────── // ─── RUNTIME_PROFILE_MAP single source of truth (finding #16) ───────────────
describe('issue #2517: RUNTIME_PROFILE_MAP single source of truth (finding #16)', () => { describe('issue #2517: RUNTIME_PROFILE_MAP single source of truth (finding #16)', () => {
test('install.js consumes the same map as core.cjs', () => { test('install.js consumes the same map as model-catalog.cjs', () => {
// `bin/install.js` must NOT carry its own duplicate copy of the map. // `bin/install.js` must NOT carry its own duplicate copy of the map.
// The shared resolver imported in install.js exposes `runtime` and the // The shared resolver imported in install.js exposes `runtime` and the
// entries through `resolveTierEntry`, so any future drift between the two // entries through `resolveTierEntry`, so any future drift between the two

View File

@@ -24,7 +24,7 @@ const WORKTREE_SAFETY_PATH = path.join(
__dirname, '..', 'gsd-core', 'bin', 'lib', 'worktree-safety.cjs' __dirname, '..', 'gsd-core', 'bin', 'lib', 'worktree-safety.cjs'
); );
const CORE_PATH = path.join( const CORE_PATH = path.join(
__dirname, '..', 'gsd-core', 'bin', 'lib', 'core.cjs' __dirname, '..', 'gsd-core', 'bin', 'lib', 'worktree-safety.cjs'
); );
const { const {