chore(#604): rename get-shit-done/ runtime directory to gsd-core/ (#615)

* chore(#604): rename get-shit-done/ runtime directory to gsd-core/

Renames the installed runtime directory `get-shit-done/` to `gsd-core/` so the
on-disk name matches the package (`@opengsd/gsd-core`), repo, and binary
(`gsd-tools`). The npm package name and binary are unchanged; npx/npm consumers
are unaffected.

Mechanical (bulk, ~90% of the diff):
- `git mv get-shit-done gsd-core`
- Swept path/identifier references across the repo via
  `perl -pe 's/get-shit-done(?!-\w)/gsd-core/g'`. The negative lookahead
  preserves the five legitimate slug variants that are NOT the directory:
  get-shit-done-{OLD,cc,classic,cli,redux} (old package/repo names).
- Build/manifest wiring: package.json (bin, files, coverage globs),
  tsconfig.build.json (outDir), ~86 .gitignore build-output entries,
  stryker.config.mjs, scan-ignore files, install.js path strings.
- Frozen (not rewritten): CHANGELOG.md history; translated docs
  (README.<locale>.md and docs/{ja-JP,ko-KR,pt-BR,zh-CN}/).

New logic (review here):
- src/installer-migrations/003-rename-get-shit-done-to-gsd-core.cts: a proper
  ADR-0008 installer migration. On upgrade it walks the legacy
  `~/.claude/get-shit-done/` tree, classifies each file via the prior install
  manifest, and emits remove-managed / backup-and-remove for managed files
  while PRESERVING unknown user-added files. Symlink-safe (skips a symlinked
  root and symlinked entries; bounds-checks every path under configDir). The
  framework rolls back on install failure. Emptied dirs may remain (framework
  has no recursive dir-removal primitive) — documented.
- scripts/lint-legacy-dir-name.cjs: CI regression guard forbidding the bare
  `get-shit-done` directory token (split token to avoid self-match; case-
  insensitive; `(?!-\w)` lookahead allows the slug variants; allowlists
  CHANGELOG, translated docs, and `gsd-allow-legacy-name` marker lines).
  Wired into the lint-tests CI job.
- Restored scripts/lint-package-identity-drift.cjs detection regexes (the
  mechanical sweep had wrongly rewritten the old-name patterns it exists to
  detect) and marked them as intentional legacy references.
- TDD tests for the migration and the guard; do.md slash-command guard regex
  tightened so a `/gsd-core/bin` path segment is not mistaken for a command;
  changeset + docs/installer-migrations.md row added.

Breaking: the installed runtime path moves `~/.claude/get-shit-done/` ->
`~/.claude/gsd-core/`. Migration 003 removes the stale legacy dir's managed
files (preserving user files) on upgrade. Users with custom hooks/configs
hardcoding the old path must update them.

Closes #604

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

* fix(#604): unsweep pending changesets + allowlist injection-example docs

CI fixes for the rename PR:
- Do not sweep pending .changeset/*.md (ephemeral release-note fragments,
  like CHANGELOG); reverted those body edits so 5 pre-existing malformed
  fragments (missing type/pr) no longer enter the PR diff and trip docs-lint.
  Allowlisted .changeset/ in the legacy-name guard accordingly.
- Allowlisted TEST-EXAMPLES.md and docs/explanation/security-model.md in
  prompt-injection-scan.sh: they contain intentional injection examples /
  security-model prose; the path-reference rewrites are kept.

CodeQL alerts on this PR are pre-existing (alert lines unchanged by this PR;
none in the new migration/guard) and are out of scope for the rename.

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

* fix(#604): resolve CodeQL alerts surfaced on this PR

The rename diff touched files carrying pre-existing CodeQL findings; per the
no-pre-existing-dismissal rule, fixing every surfaced alert rather than waving
them off. All behavior-preserving:

- scripts/ci-test-scope.cjs: build the config-path match from string
  .includes() instead of a RegExp over an arg-derived value (js/regex-injection).
- src/profile-output.cts: escape backslashes before pipe-escaping desc/safeName
  so the table-cell escape is complete (js/incomplete-sanitization).
- tests/{bug-2643,bug-2808,docs-parity-live-registry}: two-pass HTML-comment
  strip so a bare/unclosed `<!--` cannot survive (js/incomplete-multi-character-sanitization).
- tests/inline-plan-threshold: drop the no-op `\s`->`\s` identity replace,
  keep the meaningful POSIX-class conversion (js/identity-replacement).

Verified: build:lib green; the touched test files + ci-test-scope + profile-output
suites pass; lint:legacy-name clean.

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

* fix(#604): correctly resolve remaining CodeQL alerts (regex-injection + sanitization)

The prior commit's fixes for two alerts were ineffective:
- ci-test-scope.cjs js/regex-injection: the alert is the CLI-arg-derived `file`
  reaching static regex `.test(file)` calls (not the config rule). Removed ALL
  regex over file/t — startsWith/includes/=== string checks + an isWindowsHint
  helper — so there is no regex sink for the tainted value.
- js/incomplete-multi-character-sanitization (3 test files): a single
  `.replace(/<!--...-->/g,'')` can let `<!--` re-form. Replaced with a fixpoint
  loop (replace until stable) plus a final bare-opener strip.

Verified: no regex over file/t remains; ci-test-scope + the 3 test suites pass;
lint:legacy-name clean.

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

* fix(#604): make ci-test-scope + comment-strippers regex-free to clear CodeQL

CodeQL flags the regex PATTERNS syntactically (regex-injection on the
--files arg split; incomplete-multi-character-sanitization on the <!--...-->
replace), so loop fixes do not satisfy it. Made these paths regex-free:
- ci-test-scope.cjs splitFiles: char-by-char separator tokenizer (no /[,\\s]+/).
- 3 test files: indexOf/slice HTML-comment stripper (no .replace(/<!--/)).
Behavior preserved; ci-test-scope + the 3 suites pass; guard clean.

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

* fix(#604): unblock security base64 scan on the large rename diff

The security job hit its 10m timeout: base64-scan.sh choked on the binary
test fixture tests/feat-3594-parser-property-style.test.cjs (embedded NUL/
non-UTF8 bytes -> thousands of bogus blobs + "ignored null byte" warnings),
and the ~800-file rename diff is slow to scan regardless.

- scripts/base64-scan.sh: skip binary-by-content files (grep -Iq .) — they
  can't carry base64-obfuscated *text* and feeding NUL bytes through the
  per-line scanner is pathologically slow. collect_files already filtered
  binary *extensions*; this catches binary *content* in text extensions.
- .github/workflows/security-scan.yml: raise the security job timeout 10m->30m
  to accommodate very large diffs (the scan itself is unchanged).

Verified locally: scan skips the fixture, 0 "ignored null byte" warnings,
0 findings, exit 0.

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

* fix(#604): sweep get-shit-done refs introduced by merging next

The branch was updated with next (#614/#384/#618 etc.), which reference the
get-shit-done/ dir (still named that on next). Swept the stale references in
the merged files to gsd-core so the rename stays consistent and lint:legacy-name
passes:
- commands/gsd/discuss-phase.md (runtime-launcher shim paths)
- src/core.cts (getAgentsDir layout comments)
- tests/bug-384-agents-runtime-aware.test.cjs (require path to runtime lib)

Verified: guard 0 violations; build green.

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

* fix(#604): exclude gsd-core/ path segments from bug-3683 command cross-ref invariant

The #614 runtime-launcher shim added to discuss-phase.md references
`${_GSD_RUNTIME_ROOT}/gsd-core/bin/...`. bug-3683's REF_PATTERN excluded path-y
refs only via lookbehind, but `}` precedes `/gsd-core/` in the shim, so it
mis-read the directory path as a dangling `/gsd-core` command ref (same class as
the #604 bug-2954 fix). Added a trailing `(?![\w-]*\/)` so `/gsd-<x>/...` path
segments are not treated as slash-command references.

Verified locally on BOTH platforms before pushing:
- mac (node 26) full suite: 0 failures
- gsd-test-runner (linux, node22 image) full suite: 0 failures
- bug-3683 + bug-2954 pass.

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

* fix(#604): lazily resolve findProjectRoot in gsd-tools (harden flaky CI)

CI intermittently failed state.test's gsd-tools subprocess with
"findProjectRoot is not a function" (flip-flopping across legs; not reproducible
on mac full suite, gsd-test linux full suite, test:unit, or state.test x8).
findProjectRoot is a re-export from core.cjs (sourced from project-root.cjs);
binding it via destructure at module-load can be undefined under a load-ordering
edge. Resolve it lazily at call time via a small wrapper so the lookup happens
after core.cjs is fully initialized.

Verified green on BOTH platforms before pushing:
- mac (node 26) full suite: 0 failures
- gsd-test-runner (linux, node22) full suite: 0 failures
- state.test.cjs: 106/106; gsd-tools loads cleanly.

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

* fix(#604): allowlist verification-patterns.md placeholder examples in secret scan

The rename git-mv'd references/verification-patterns.md into gsd-core/, pulling
it into the secret-scan diff. It documents stub/placeholder RED-FLAG env-var
examples (illustrative Stripe test-key / database-URL / API-key placeholders) —
not real credentials. Added it to .secretscanignore with the strict annotation,
mirroring the existing gsd-core/workflows/plan-phase.md exception.

Verified locally: secret-scan-lint --strict OK; secret-scan --diff origin/next
exits 0 with 0 findings.

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-02 18:35:29 -04:00
committed by GitHub
parent b177c1704f
commit 463cffd894
845 changed files with 3316 additions and 2373 deletions

View File

@@ -9,9 +9,9 @@ We decided to centralize model-selection data in one Model Catalog Module so the
Before this ADR there were four drifting sources:
1. `get-shit-done/bin/lib/model-profiles.cjs` — agent → profile alias map, phase-type map, dynamic-routing default tiers
1. `gsd-core/bin/lib/model-profiles.cjs` — agent → profile alias map, phase-type map, dynamic-routing default tiers
2. `sdk/src/query/config-query.ts` — stale 18-agent copy of `MODEL_PROFILES`
3. `get-shit-done/workflows/settings-advanced.md` — runtime → built-in model-id table
3. `gsd-core/workflows/settings-advanced.md` — runtime → built-in model-id table
4. `sdk/src/session-runner.ts` — hardcoded Claude-only profile → model-id map
This caused issue #3229: the SDK knew only 18 agents while 33 agent files existed on disk, so ~15 agents silently fell back to Sonnet with `unknown_agent: true`.

View File

@@ -7,7 +7,7 @@ We propose introducing a Shell Command Projection Module that owns projection fr
## Decision
- Add a **Shell Command Projection Module** under `get-shit-done/bin/lib/` as the single owner for runtime-aware command-text rendering.
- Add a **Shell Command Projection Module** under `gsd-core/bin/lib/` as the single owner for runtime-aware command-text rendering.
- Feed the module typed inputs (`platform`, `shell`, `runtime`, executable token, args, path policy) instead of prebuilt shell strings.
- Keep callers as thin Adapters that request projected text for:
- managed hook commands in `settings.json`
@@ -125,7 +125,7 @@ The seam grew beyond the original "rendering only" scope. The "does not become a
**Open question resolutions:**
- Q4 (installer-only vs shared seam): **resolved — shared.** The seam lives in `get-shit-done/bin/lib/`, consumed by installer, planning workflow, and every fs/subprocess call site across the tool.
- Q4 (installer-only vs shared seam): **resolved — shared.** The seam lives in `gsd-core/bin/lib/`, consumed by installer, planning workflow, and every fs/subprocess call site across the tool.
- Q1, Q2, Q3 (`hooks.shell_preference`, Windows Git Bash modeling, shim/script builder migration timing): unresolved, carried forward as projection-design concerns independent of the I/O expansion.
See CONTEXT.md "Shell Command Projection Module" entry for the canonical current-state description.

View File

@@ -8,13 +8,13 @@
---
We propose introducing a File Operation Engine Module that owns policy for managed file reads, writes, deletes, locks, backups, and rollbacks across installer, migration, and planning surfaces. Today, file mutation behavior is duplicated across `bin/install.js`, `get-shit-done/bin/lib/installer-migrations.cjs`, and multiple planning modules, with drift in atomic-write guarantees, path safety checks, and ownership classification.
We propose introducing a File Operation Engine Module that owns policy for managed file reads, writes, deletes, locks, backups, and rollbacks across installer, migration, and planning surfaces. Today, file mutation behavior is duplicated across `bin/install.js`, `gsd-core/bin/lib/installer-migrations.cjs`, and multiple planning modules, with drift in atomic-write guarantees, path safety checks, and ownership classification.
This ADR also captures where Shell Command Projection Module policy should be consumed or expanded for hook-command-specific file mutations, so shell command drift and file mutation drift do not evolve as separate bug classes.
## Decision
- Add a **File Operation Engine Module** under `get-shit-done/bin/lib/` as the single seam for file mutation safety policy.
- Add a **File Operation Engine Module** under `gsd-core/bin/lib/` as the single seam for file mutation safety policy.
- Keep command-text projection in the Shell Command Projection Module (ADR-0009), but route projection-adjacent hook file mutations through shared managed-hook ownership policy.
- Move file operation adapters to the new seam in two tracks:
- **Track A (projection-adjacent):** runtime config hook-command detection/rewrite/delete paths consume shared managed-hook policy from the projection seam.
@@ -36,9 +36,9 @@ This ADR also captures where Shell Command Projection Module policy should be co
- hook cleanup command detection (`isGsdHookCommand`)
- stale Codex hook strip basenames (`STALE_HOOK_BASENAMES`)
- settings/config hook entry prune/rewrite paths
- `get-shit-done/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs`
- `gsd-core/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs`
- `isManagedCodexHookCommand` regex/path detection duplicated from installer-owned hook policy
- `get-shit-done/bin/lib/shell-command-projection.cjs`
- `gsd-core/bin/lib/shell-command-projection.cjs`
- `isManagedHookBasename` already owns part of this policy and should become the canonical owner
### Solution-wide file operation drift (Track B)
@@ -46,13 +46,13 @@ This ADR also captures where Shell Command Projection Module policy should be co
- `bin/install.js`
- local `atomicWriteFileSync` and temp cleanup registry
- large inlined read/modify/write + backup/rollback logic for runtime config and hooks
- `get-shit-done/bin/lib/core.cjs`
- `gsd-core/bin/lib/core.cjs`
- `atomicWriteFileSync` helper diverges in fallback behavior from installer/migration variants
- `get-shit-done/bin/lib/installer-migrations.cjs`
- `gsd-core/bin/lib/installer-migrations.cjs`
- separate `writeFileAtomicSync`, rollback journaling, lock handling, and containment checks
- `get-shit-done/bin/lib/planning-workspace.cjs` and `get-shit-done/bin/lib/state.cjs`
- `gsd-core/bin/lib/planning-workspace.cjs` and `gsd-core/bin/lib/state.cjs`
- duplicated lock-file create/release/remove patterns and best-effort cleanup semantics
- `get-shit-done/bin/lib/roadmap.cjs`, `phase.cjs`, `milestone.cjs`, `frontmatter.cjs`, `drift.cjs`
- `gsd-core/bin/lib/roadmap.cjs`, `phase.cjs`, `milestone.cjs`, `frontmatter.cjs`, `drift.cjs`
- direct read/modify/write flows with inconsistent atomicity and normalization policy application
## Interface sketch

View File

@@ -3,11 +3,11 @@
- **Status:** Proposed
- **Date:** 2026-05-12
We propose extending the existing install profile seam (`get-shit-done/bin/lib/install-profiles.cjs`) into a **Skill Surface Budget Module** that owns which subset of GSD's 66 skills is written to the runtime config dirs, and that owns the per-skill `requires:` dependency manifest used to keep that subset closed under cross-skill references. GSD currently ships a binary `--minimal` / full toggle; runtimes that enumerate skills (Claude Code, OpenCode, etc.) cap the `<available_skills>` system-prompt block at `skillListingBudgetFraction` of the context window (default 1% = ~2k tokens at 200k), and GSD alone consumes ~60% of that cap (#3408). Further description shrinkage is unavailable — `scripts/lint-descriptions.cjs` already enforces a hard 100-char ceiling and the mean is 72.5 chars. The remaining lever is surfacing fewer skills, which requires a typed profile model plus a dependency manifest, not more ad-hoc allowlists.
We propose extending the existing install profile seam (`gsd-core/bin/lib/install-profiles.cjs`) into a **Skill Surface Budget Module** that owns which subset of GSD's 66 skills is written to the runtime config dirs, and that owns the per-skill `requires:` dependency manifest used to keep that subset closed under cross-skill references. GSD currently ships a binary `--minimal` / full toggle; runtimes that enumerate skills (Claude Code, OpenCode, etc.) cap the `<available_skills>` system-prompt block at `skillListingBudgetFraction` of the context window (default 1% = ~2k tokens at 200k), and GSD alone consumes ~60% of that cap (#3408). Further description shrinkage is unavailable — `scripts/lint-descriptions.cjs` already enforces a hard 100-char ceiling and the mean is 72.5 chars. The remaining lever is surfacing fewer skills, which requires a typed profile model plus a dependency manifest, not more ad-hoc allowlists.
## Decision
- Add a **Skill Surface Budget Module** by extending `get-shit-done/bin/lib/install-profiles.cjs` as the single owner for which `commands/gsd/*.md` and `agents/gsd-*.md` files are staged into the per-runtime copy pipeline.
- Add a **Skill Surface Budget Module** by extending `gsd-core/bin/lib/install-profiles.cjs` as the single owner for which `commands/gsd/*.md` and `agents/gsd-*.md` files are staged into the per-runtime copy pipeline.
- Replace the single `MINIMAL_SKILL_ALLOWLIST` constant with a typed `PROFILES` map keyed by profile name. Each profile is a *base set* of skills; the module computes the **transitive closure** over each skill's declared `requires:` set before staging.
- Add a `requires:` frontmatter field to every skill whose body references another GSD skill. The dependency graph in the research memo (`docs/research/2026-05-12-skill-surface-budget.md` §3.1) is the migration spec for this pass.
- Extend `bin/install.js` argument parsing to accept `--profile=<name>` and `--profile=<name1>,<name2>` (composable). Preserve `--minimal` / `--core-only` as aliases for `--profile=core`. Default install (no flag) remains `full` for back-compat.
@@ -35,7 +35,7 @@ It should **not** in the first pass:
## Migration Inventory
### `get-shit-done/bin/lib/install-profiles.cjs`
### `gsd-core/bin/lib/install-profiles.cjs`
- Replace `MINIMAL_SKILL_ALLOWLIST` Object.freeze constant with `PROFILES` Object.freeze map of profile-name → base skill set.
- Replace `isMinimalMode(mode)` with `resolveProfile(mode)` returning a typed `{name, skills: Set, agents: Set}` after transitive-closure computation.
@@ -141,7 +141,7 @@ requires: [phase, discuss-phase] # GSD skills only; not Claude Code primitives
- Feature issue: `#3408`
- Research input: `docs/research/2026-05-12-skill-surface-budget.md`
- Existing seam being extended: `get-shit-done/bin/lib/install-profiles.cjs`
- Existing seam being extended: `gsd-core/bin/lib/install-profiles.cjs`
- Description budget enforcement: `scripts/lint-descriptions.cjs`
- Installer dispatch site: `bin/install.js:123-124`, `:8167-8207`
- See `0008-installer-migration-module.md` (the migration that records the profile marker lives here)

View File

@@ -11,7 +11,7 @@ The root problem is an absence of a profile/surface seam: the installer wrote ev
## Decision
- Add a **Skill Surface Budget Module** under `get-shit-done/bin/lib/install-profiles.cjs` as the single owner for which skills and agents are written to runtime config directories.
- Add a **Skill Surface Budget Module** under `gsd-core/bin/lib/install-profiles.cjs` as the single owner for which skills and agents are written to runtime config directories.
- Define three named profiles: `core` (six skills covering the main loop), `standard` (core + phase management and workspace skills), and `full` (all skills — the previous default).
- Compute each profile's effective skill set as the transitive closure over the `requires:` dependency graph extracted from skill frontmatter, so partial installs never break cross-skill dependencies.
- Persist the chosen profile in a `.gsd-profile` marker file in each runtime config directory; `gsd update` reads the marker to honor the profile on re-install.
@@ -33,9 +33,9 @@ The Phase 2 decision, previously listed as an open question, is recorded here as
- `disable <cluster>` — mark a cluster disabled; re-stage to remove its skills from the runtime config dir
- `enable <cluster>` — mark a cluster enabled; re-stage to add its skills back
- `reset` — clear surface state and re-apply the active profile from `.gsd-profile`
- Implement the runtime surface engine in `get-shit-done/bin/lib/surface.cjs`, consuming `stageSkillsForProfile` and `stageAgentsForProfile` from the Phase 1 module without duplicating staging logic.
- Implement the runtime surface engine in `gsd-core/bin/lib/surface.cjs`, consuming `stageSkillsForProfile` and `stageAgentsForProfile` from the Phase 1 module without duplicating staging logic.
- Persist per-runtime surface state in `<runtimeConfigDir>/.gsd-surface.json`, independent from `.gsd-profile`. The profile marker owns install-time identity; the surface JSON owns session-scope cluster toggles.
- Source cluster taxonomy from the research memo §3.2 (2026-05-12-skill-surface-budget.md). Define clusters in `get-shit-done/bin/lib/clusters.cjs` — a separate module so the surface engine and future SDK callers can import cluster definitions without loading the full profile module.
- Source cluster taxonomy from the research memo §3.2 (2026-05-12-skill-surface-budget.md). Define clusters in `gsd-core/bin/lib/clusters.cjs` — a separate module so the surface engine and future SDK callers can import cluster definitions without loading the full profile module.
- Cluster taxonomy: `core_loop`, `audit_review`, `milestone`, `research_ideate`, `workspace_state`, `docs`, `ui`, `ai_eval`, `ns_meta`, `utility`. Membership may overlap; every installed skill stem must appear in at least one cluster (enforced by `tests/surface-clusters.test.cjs`).
- Relationship to Anthropic platform asks: Asks D (native per-skill toggle API) and E (budget-fraction negotiation) remain filed separately. The `/gsd:surface` command is a unilateral GSD-side workaround that does not depend on those platform changes.
@@ -43,7 +43,7 @@ The Phase 2 decision, previously listed as an open question, is recorded here as
Phase 1 artifacts landed on `feat/3408-skills-description-dropped-due-to-size`:
- `get-shit-done/bin/lib/install-profiles.cjs` — `PROFILES` map, `resolveProfile`, `loadSkillsManifest`, `stageSkillsForProfile`, `stageAgentsForProfile`, `readActiveProfile`, `writeActiveProfile`, `mostRestrictiveProfile`, `resolveEffectiveProfile`
- `gsd-core/bin/lib/install-profiles.cjs` — `PROFILES` map, `resolveProfile`, `loadSkillsManifest`, `stageSkillsForProfile`, `stageAgentsForProfile`, `readActiveProfile`, `writeActiveProfile`, `mostRestrictiveProfile`, `resolveEffectiveProfile`
- `requires:` frontmatter added to 64 skills in `commands/gsd/*.md`
- `scripts/lint-skill-deps.cjs` — CI gate for `requires:` integrity, wired into `pretest`
- `bin/install.js` — `--profile=<name>` flag (composable); `--minimal`/`--core-only` as aliases; `.gsd-profile` marker write on install; `gsd update` re-reads marker
@@ -52,8 +52,8 @@ Phase 1 artifacts landed on `feat/3408-skills-description-dropped-due-to-size`:
Phase 2 shipped on the same branch:
- `commands/gsd/surface.md` — `/gsd:surface` slash command runbook (sub-commands: `list`, `status`, `profile <name>`, `disable <cluster>`, `enable <cluster>`, `reset`)
- `get-shit-done/bin/lib/surface.cjs` — runtime engine (`readSurface`, `writeSurface`, `resolveSurface`, `applySurface`, `listSurface`); reuses `stageSkillsForProfile` / `stageAgentsForProfile` from Phase 1
- `get-shit-done/bin/lib/clusters.cjs` — 10-cluster taxonomy covering all installed skill stems
- `gsd-core/bin/lib/surface.cjs` — runtime engine (`readSurface`, `writeSurface`, `resolveSurface`, `applySurface`, `listSurface`); reuses `stageSkillsForProfile` / `stageAgentsForProfile` from Phase 1
- `gsd-core/bin/lib/clusters.cjs` — 10-cluster taxonomy covering all installed skill stems
- Tests: `tests/surface-state.test.cjs`, `tests/surface-clusters.test.cjs`, `tests/surface-resolve.test.cjs`, `tests/surface-apply.test.cjs`, `tests/surface-list.test.cjs`
- Persistent surface state: `<runtimeConfigDir>/.gsd-surface.json` (independent from `.gsd-profile`)

View File

@@ -9,7 +9,7 @@ Seven `*-command-router.cjs` files (`phase`, `phases`, `roadmap`, `state`, `veri
## Decision
Introduce `CommandRoutingHub` (`get-shit-done/bin/lib/command-routing-hub.cjs`) as the single dispatch seam for all CJS command family routers. The hub contract:
Introduce `CommandRoutingHub` (`gsd-core/bin/lib/command-routing-hub.cjs`) as the single dispatch seam for all CJS command family routers. The hub contract:
```
createHub({ mode: 'sdk' | 'cjs', sdkLoader, cjsRegistry, manifest }) -> hub

View File

@@ -164,12 +164,12 @@ Root architectural gap: orchestration flows lack a plan strategy seam (`local` v
- Issue: #15
- `commands/gsd/progress.md`
- `get-shit-done/workflows/progress.md`
- `get-shit-done/workflows/next.md`
- `gsd-core/workflows/progress.md`
- `gsd-core/workflows/next.md`
- `commands/gsd/autonomous.md`
- `get-shit-done/workflows/autonomous.md`
- `gsd-core/workflows/autonomous.md`
- `commands/gsd/plan-review-convergence.md`
- `get-shit-done/workflows/plan-review-convergence.md`
- `gsd-core/workflows/plan-review-convergence.md`
- `commands/gsd/review.md`
- `docs/COMMANDS.md` (`/gsd-plan-review-convergence`, `/gsd-review`)
- `docs/CONFIGURATION.md` (`workflow.plan_review_convergence`, `review.default_reviewers`, `review.models.*`)

View File

@@ -7,9 +7,9 @@
- **Extends:** ADR-0005 (seam map) — adds the **Shared-Module Source Policy** to the seam family
- **Defers to:** ADR-0001 (Dispatch Policy Module), ADR-0003 (Model Catalog Module), ADR-0004 (Planning Workspace Module), ADR-0006 (Planning Path Projection Module), ADR-0009 (Shell Command Projection Module — post-Phase 3–4, also subsuming superseded ADR-0010)
We decided to harden the boundary between the CJS tooling layer (`get-shit-done/bin/lib/*.cjs`) and the SDK (`sdk/src/**/*.ts`) by making every Module that is conceptually shared between the two runtimes have exactly one hand-authored source of truth and at most one generated artifact per runtime. The trigger is the recurring drift bug class — #1535, #1542, #2047/#2052, #2638/#2655, #2653/#2670, #2687/#2706, #2798/#2816, #3055/#3116, #3523 — each of which was a fix landing on one side without the other.
We decided to harden the boundary between the CJS tooling layer (`gsd-core/bin/lib/*.cjs`) and the SDK (`sdk/src/**/*.ts`) by making every Module that is conceptually shared between the two runtimes have exactly one hand-authored source of truth and at most one generated artifact per runtime. The trigger is the recurring drift bug class — #1535, #1542, #2047/#2052, #2638/#2655, #2653/#2670, #2687/#2706, #2798/#2816, #3055/#3116, #3523 — each of which was a fix landing on one side without the other.
The precedent shape is already in the repo. `sdk/scripts/gen-command-aliases.ts` emits `sdk/src/query/command-aliases.generated.ts` **and** `get-shit-done/bin/lib/command-aliases.generated.cjs` from one TypeScript source. `sdk/scripts/check-command-aliases-fresh.mjs` is the CI freshness gate. The two consuming sides are pure Adapters over the generated artifact. This ADR generalizes that pattern to the other Shared Modules and forbids the hand-synced-pair anti-pattern that produced #3523.
The precedent shape is already in the repo. `sdk/scripts/gen-command-aliases.ts` emits `sdk/src/query/command-aliases.generated.ts` **and** `gsd-core/bin/lib/command-aliases.generated.cjs` from one TypeScript source. `sdk/scripts/check-command-aliases-fresh.mjs` is the CI freshness gate. The two consuming sides are pure Adapters over the generated artifact. This ADR generalizes that pattern to the other Shared Modules and forbids the hand-synced-pair anti-pattern that produced #3523.
## Decision
@@ -20,10 +20,10 @@ A **Shared Module** is any Module whose Interface is consumed identically by bot
For every Shared Module:
1. **Exactly one hand-authored source of truth.** Lives at `sdk/src/<module-name>/` as TypeScript when the Module has behavior, or `sdk/shared/<module-name>.manifest.json` when the Module is pure data.
2. **Generated artifacts only.** The CJS-side file is `get-shit-done/bin/lib/<module-name>.generated.cjs` and is emitted mechanically. It is never hand-edited.
2. **Generated artifacts only.** The CJS-side file is `gsd-core/bin/lib/<module-name>.generated.cjs` and is emitted mechanically. It is never hand-edited.
3. **Per-Module freshness check.** A CI script `sdk/scripts/check-<module>-fresh.mjs` re-runs the generator and fails if the emitted artifact differs from the committed one. Precedent: `check-command-aliases-fresh.mjs`.
4. **Per-Module drift lint** (when the source is data, not a generator output). Precedent: `scripts/lint-shell-command-projection-drift.cjs`. The lint asserts the canonical-owner invariants that aren't captured by file-equality.
5. **Hand-synced pairs are forbidden.** A pre-merge `lint-shared-module-handsync.cjs` greps `get-shit-done/bin/lib/` for non-`.generated.*` files whose basename matches a `sdk/src/query/<same-name>.ts` source and fails the build unless the pair is explicitly allow-listed.
5. **Hand-synced pairs are forbidden.** A pre-merge `lint-shared-module-handsync.cjs` greps `gsd-core/bin/lib/` for non-`.generated.*` files whose basename matches a `sdk/src/query/<same-name>.ts` source and fails the build unless the pair is explicitly allow-listed.
### 2. Module-indexed canonical-owner table
@@ -31,13 +31,13 @@ The table below indexes by Module, not by physical layer. Each row names the sou
| Module | Status | Source of truth | Generated artifacts | Adapters |
|---|---|---|---|---|
| **STATE.md Document Module** | New under this ADR (Phase 1) — see CONTEXT.md "STATE.md Document Module" | `sdk/src/state/index.ts` (promoted from `sdk/src/query/state-document.ts`) | `sdk/src/query/state-document.generated.ts`, `get-shit-done/bin/lib/state-document.generated.cjs` | `bin/lib/state.cjs` and `sdk/src/query/state*.ts` import the generated form |
| **Configuration Module** | New under this ADR (Phase 2) — definition added to CONTEXT.md as part of Phase 2 | `sdk/src/config/index.ts` plus data manifests `sdk/shared/config-schema.manifest.json` and `sdk/shared/config-defaults.manifest.json` | `sdk/src/query/config-schema.generated.ts`, `get-shit-done/bin/lib/config-schema.generated.cjs`, `get-shit-done/bin/lib/configuration.generated.cjs` | `bin/lib/config.cjs`, `bin/lib/core.cjs:loadConfig`, `sdk/src/config.ts` |
| **Workstream Inventory Module** (Builder) | Amended under this ADR (Phase 3) — Builder split documented in CONTEXT.md update | `sdk/src/workstream/builder.ts` (pure projection from directory entries + STATE.md text + plan scan results → typed inventory) | `sdk/src/query/workstream-inventory-builder.generated.ts`, `get-shit-done/bin/lib/workstream-inventory-builder.generated.cjs` | Per-side fs Readers (`workstream-inventory.cjs` sync, `workstream-inventory.ts` async) call the Builder. Readers stay hand-authored because the fs idiom legitimately differs. |
| **Project-Root Resolution Module** | New under this ADR (Phase 4) — short CONTEXT.md entry, behavior already de-facto shared | `sdk/src/project-root/index.ts` | `get-shit-done/bin/lib/project-root.generated.cjs` | `bin/lib/core.cjs` (`findProjectRoot`, `findEffectiveRoot`), `sdk/src/helpers.ts` |
| **Frontmatter Module** | Conditional (Phase 3, only if drift catalogue confirms pair duplication) | `sdk/src/frontmatter/index.ts` | `get-shit-done/bin/lib/frontmatter.generated.cjs` | Existing handler call sites |
| **Plan Scan Module** | Conditional (Phase 3 or later) | `sdk/src/plan-scan/index.ts` | `get-shit-done/bin/lib/plan-scan.generated.cjs` | Phase/roadmap routers |
| **CJS Command Router Adapter Module** | Amended under this ADR (Phase 5). Existing Module (per CONTEXT.md) is extended so the per-family `handlers` map delegates to the SDK runtime bridge in-process instead of to parallel CJS handler implementations. | `sdk/src/query-runtime-bridge.ts` (already exists) + per-family delegate emitter | `get-shit-done/bin/lib/cjs-command-router-adapter.cjs` (existing, ~40 lines) plus per-family `handlers` maps that `require('../../sdk/dist/query-runtime-bridge.cjs')` and call `QueryRuntimeBridge.execute()` | `bin/gsd-tools.cjs` and the seven `bin/lib/*-command-router.cjs` files are the consumers. Per-family CJS handler files (`state.cjs`, `verify.cjs`, `init.cjs`, etc.) shrink to delegates or are deleted once the SDK handler is the only implementation. |
| **STATE.md Document Module** | New under this ADR (Phase 1) — see CONTEXT.md "STATE.md Document Module" | `sdk/src/state/index.ts` (promoted from `sdk/src/query/state-document.ts`) | `sdk/src/query/state-document.generated.ts`, `gsd-core/bin/lib/state-document.generated.cjs` | `bin/lib/state.cjs` and `sdk/src/query/state*.ts` import the generated form |
| **Configuration Module** | New under this ADR (Phase 2) — definition added to CONTEXT.md as part of Phase 2 | `sdk/src/config/index.ts` plus data manifests `sdk/shared/config-schema.manifest.json` and `sdk/shared/config-defaults.manifest.json` | `sdk/src/query/config-schema.generated.ts`, `gsd-core/bin/lib/config-schema.generated.cjs`, `gsd-core/bin/lib/configuration.generated.cjs` | `bin/lib/config.cjs`, `bin/lib/core.cjs:loadConfig`, `sdk/src/config.ts` |
| **Workstream Inventory Module** (Builder) | Amended under this ADR (Phase 3) — Builder split documented in CONTEXT.md update | `sdk/src/workstream/builder.ts` (pure projection from directory entries + STATE.md text + plan scan results → typed inventory) | `sdk/src/query/workstream-inventory-builder.generated.ts`, `gsd-core/bin/lib/workstream-inventory-builder.generated.cjs` | Per-side fs Readers (`workstream-inventory.cjs` sync, `workstream-inventory.ts` async) call the Builder. Readers stay hand-authored because the fs idiom legitimately differs. |
| **Project-Root Resolution Module** | New under this ADR (Phase 4) — short CONTEXT.md entry, behavior already de-facto shared | `sdk/src/project-root/index.ts` | `gsd-core/bin/lib/project-root.generated.cjs` | `bin/lib/core.cjs` (`findProjectRoot`, `findEffectiveRoot`), `sdk/src/helpers.ts` |
| **Frontmatter Module** | Conditional (Phase 3, only if drift catalogue confirms pair duplication) | `sdk/src/frontmatter/index.ts` | `gsd-core/bin/lib/frontmatter.generated.cjs` | Existing handler call sites |
| **Plan Scan Module** | Conditional (Phase 3 or later) | `sdk/src/plan-scan/index.ts` | `gsd-core/bin/lib/plan-scan.generated.cjs` | Phase/roadmap routers |
| **CJS Command Router Adapter Module** | Amended under this ADR (Phase 5). Existing Module (per CONTEXT.md) is extended so the per-family `handlers` map delegates to the SDK runtime bridge in-process instead of to parallel CJS handler implementations. | `sdk/src/query-runtime-bridge.ts` (already exists) + per-family delegate emitter | `gsd-core/bin/lib/cjs-command-router-adapter.cjs` (existing, ~40 lines) plus per-family `handlers` maps that `require('../../sdk/dist/query-runtime-bridge.cjs')` and call `QueryRuntimeBridge.execute()` | `bin/gsd-tools.cjs` and the seven `bin/lib/*-command-router.cjs` files are the consumers. Per-family CJS handler files (`state.cjs`, `verify.cjs`, `init.cjs`, etc.) shrink to delegates or are deleted once the SDK handler is the only implementation. |
| Command-Alias Module | **Already sealed** by this pattern's precedent — `sdk/scripts/gen-command-aliases.ts` + `check-command-aliases-fresh.mjs` | No change | No change | No change |
| Dispatch Policy Module | **Defer — see ADR-0001** (and its 2026-05-05 SDK Runtime Bridge amendment) | n/a | n/a | n/a |
| Model Catalog Module | **Defer — see ADR-0003**; the `sdk/shared/model-catalog.json` manifest already follows the source-of-truth policy | n/a | n/a | n/a |
@@ -68,7 +68,7 @@ Drift is blocked at three layers, each modeled on an existing in-repo script:
1. **Per-Module freshness check** — `sdk/scripts/check-<module>-fresh.mjs`, one per Shared Module in the table. Precedent: `check-command-aliases-fresh.mjs`.
2. **Per-Module drift lint** (when invariants are not pure file-equality) — `scripts/lint-<module>-drift.cjs`, one per data-manifest-backed Module. Precedent: `lint-shell-command-projection-drift.cjs`.
3. **Hand-sync pair lint** — `scripts/lint-shared-module-handsync.cjs` rejects any pair of files at `get-shit-done/bin/lib/<name>.cjs` and `sdk/src/query/<name>.ts` (or `sdk/src/<name>.ts`) that are neither generated artifacts nor on an explicit allow-list. This blocks the #3523 anti-pattern at PR time.
3. **Hand-sync pair lint** — `scripts/lint-shared-module-handsync.cjs` rejects any pair of files at `gsd-core/bin/lib/<name>.cjs` and `sdk/src/query/<name>.ts` (or `sdk/src/<name>.ts`) that are neither generated artifacts nor on an explicit allow-list. This blocks the #3523 anti-pattern at PR time.
CODEOWNERS extends to `sdk/src/<module>/` for each Shared Module. Architecture-team review is required for changes to a source of truth.
@@ -105,11 +105,11 @@ _(Append-only. Use a dated header when the decision evolves.)_
### 2026-05-23 — validate.ts → verify.cjs generator pattern (issue #6)
Three pure helpers from `sdk/src/query/validate.ts` Check 8 are now generated into
`get-shit-done/bin/lib/validate.generated.cjs` via `sdk/scripts/gen-validate.mjs`,
`gsd-core/bin/lib/validate.generated.cjs` via `sdk/scripts/gen-validate.mjs`,
following the same I/O adapter pattern established by PR #154 (issue #4):
**Generator:** `sdk/scripts/gen-validate.mjs`
**Artifact:** `get-shit-done/bin/lib/validate.generated.cjs`
**Artifact:** `gsd-core/bin/lib/validate.generated.cjs`
**Freshness check:** `sdk/scripts/check-validate-fresh.mjs`
**CI:** `.github/workflows/test.yml` — "SDK generated validate artifact drift check"
@@ -150,7 +150,7 @@ is still a full implementation; only Check 8 helpers are generated).
#### Extension — issue #26: W005/W006-archived/I001 generator migration
PR #3479 fixed three false-positive classes in `sdk/src/query/validate.ts`. PR #3806 hand-ported
the three fixes to `get-shit-done/bin/lib/verify.cjs` but did not route them through the generator
the three fixes to `gsd-core/bin/lib/verify.cjs` but did not route them through the generator
— meaning they could drift again. Issue #26 closes this gap by extending `gen-validate.mjs`
(introduced in this amendment above) to also extract and export the W005/W006-archived/I001 items.
@@ -211,11 +211,11 @@ verify.cjs migration scope for generator-pattern coverage.
**Decision:** Apply the I/O adapter pattern (Section 4) to the pure-computation kernel inside `phase-lifecycle.ts`:
1. **Three new generator scripts** extract pure helpers from the phase family:
- `sdk/scripts/gen-phase.mjs` → `get-shit-done/bin/lib/phase.generated.cjs`
- `sdk/scripts/gen-phase.mjs` → `gsd-core/bin/lib/phase.generated.cjs`
(pure helpers: `isCanonicalPlanFile`, `describeNonCanonicalPlans`)
- `sdk/scripts/gen-phase-lifecycle.mjs` → `get-shit-done/bin/lib/phase-lifecycle.generated.cjs`
- `sdk/scripts/gen-phase-lifecycle.mjs` → `gsd-core/bin/lib/phase-lifecycle.generated.cjs`
(pure helpers: `deriveProgressFromRoadmap`, `clampPercent`)
- `sdk/scripts/gen-phase-lifecycle-policy.mjs` → `get-shit-done/bin/lib/phase-lifecycle-policy.generated.cjs`
- `sdk/scripts/gen-phase-lifecycle-policy.mjs` → `gsd-core/bin/lib/phase-lifecycle-policy.generated.cjs`
(14 pure policy helpers: `generatePhaseSlug`, `computePhaseDirectory`, `buildPhaseRoadmapEntry`, etc.)
2. **`phase.cjs:cmdPhaseComplete`** is migrated to use `deriveProgressFromRoadmap` + `clampPercent` from the generated artifact. It reads the freshly-updated ROADMAP synchronously, derives the completed-phase count from Complete-row matching (idempotent), and passes it through `clampPercent` to prevent >100% Progress.

View File

@@ -5,13 +5,13 @@
- **Issue:** #3660
- **Implementation:** #3663 (Phase 1), feat/3663-runtime-artifact-layout-module-phase-1-m
The **Runtime Surface Module** (`get-shit-done/bin/lib/surface.cjs`, introduced by ADR-0011 Phase 2) re-materializes a resolved Skill Surface profile to disk via `applySurface`. It currently hardcodes two artifact kinds (`commands`, `agents`) and re-derives their source directories via `_findInstallSource` / `_findAgentsSource` walk-up heuristics. The install and uninstall pipelines in `bin/install.js` each encode the same per-runtime artifact layout independently across ~14 install sites and ~6 uninstall sites. Bug #3659 surfaced the resulting drift: `applySurface` omits the `skills` kind for runtimes whose canonical layout is `skills/gsd-<stem>/SKILL.md`, so `gsd-surface profile <name>` leaves ~67 skill directories on disk under the install-time profile's footprint when the resolved profile should have pruned them — roughly 2.7k tokens per session on a measured workstation.
The **Runtime Surface Module** (`gsd-core/bin/lib/surface.cjs`, introduced by ADR-0011 Phase 2) re-materializes a resolved Skill Surface profile to disk via `applySurface`. It currently hardcodes two artifact kinds (`commands`, `agents`) and re-derives their source directories via `_findInstallSource` / `_findAgentsSource` walk-up heuristics. The install and uninstall pipelines in `bin/install.js` each encode the same per-runtime artifact layout independently across ~14 install sites and ~6 uninstall sites. Bug #3659 surfaced the resulting drift: `applySurface` omits the `skills` kind for runtimes whose canonical layout is `skills/gsd-<stem>/SKILL.md`, so `gsd-surface profile <name>` leaves ~67 skill directories on disk under the install-time profile's footprint when the resolved profile should have pruned them — roughly 2.7k tokens per session on a measured workstation.
The root problem is the absence of a typed seam for "where does runtime R put artifact kind K." Three lifecycle sites (install, uninstall, surface) each independently encode this knowledge and drift independently.
## Decision
- Add a **Runtime Artifact Layout Module** at `get-shit-done/bin/lib/runtime-artifact-layout.cjs` as the single owner of the per-runtime artifact-placement table.
- Add a **Runtime Artifact Layout Module** at `gsd-core/bin/lib/runtime-artifact-layout.cjs` as the single owner of the per-runtime artifact-placement table.
- The module requires `runtime-homes.cjs` for the canonical runtime enum and global config-dir resolution. It adds the artifact-kind axis on top.
- Expose `resolveRuntimeArtifactLayout(runtime, configDir) → Layout`. The returned `Layout` is a plain typed object — `{ runtime, configDir, kinds: ArtifactKind[] }` — with no I/O on resolution.
- Each `ArtifactKind` is `{ kind: 'commands'|'agents'|'skills', destSubpath, prefix, stage }`. `stage` is a function `(resolvedProfile) → stagedDir` that closes over the per-runtime converter where one is needed (e.g. `convertClaudeCommandToClaudeSkill` for the `skills` kind on Claude global).
@@ -29,7 +29,7 @@ The root problem is the absence of a typed seam for "where does runtime R put ar
Phase 1 should land the module and one consumer (the bug-#3659 fix):
1. New `get-shit-done/bin/lib/runtime-artifact-layout.cjs` — `resolveRuntimeArtifactLayout`, the typed `Layout`/`ArtifactKind` shapes, and the runtime table covering every runtime currently enumerated in `runtime-homes.cjs`.
1. New `gsd-core/bin/lib/runtime-artifact-layout.cjs` — `resolveRuntimeArtifactLayout`, the typed `Layout`/`ArtifactKind` shapes, and the runtime table covering every runtime currently enumerated in `runtime-homes.cjs`.
2. `surface.cjs:applySurface` migrates to layout-driven iteration. `_findInstallSource` and `_findAgentsSource` deleted. The `skills` kind is now iterated alongside `commands` and `agents` — bug #3659 closed.
3. `commands/gsd/surface.md` and `tests/surface-apply.test.cjs` updated to construct + pass `Layout` values.
4. New `tests/runtime-artifact-layout-*.test.cjs` covering:
@@ -47,10 +47,10 @@ Phase 1 should **not**:
## Migration Inventory
### New file
- `get-shit-done/bin/lib/runtime-artifact-layout.cjs` — module body + runtime layout table.
- `gsd-core/bin/lib/runtime-artifact-layout.cjs` — module body + runtime layout table.
### Files modified (Phase 1)
- `get-shit-done/bin/lib/surface.cjs` — `applySurface` signature change; `_findInstallSource` + `_findAgentsSource` removal.
- `gsd-core/bin/lib/surface.cjs` — `applySurface` signature change; `_findInstallSource` + `_findAgentsSource` removal.
- `commands/gsd/surface.md` — runbook updates the 3 sites that call `applySurface` to first call `resolveRuntimeArtifactLayout`.
- `tests/surface-apply.test.cjs` — 5 call sites pass `layout` instead of `commandsDir, agentsDir`.
@@ -132,17 +132,17 @@ function applySurface(runtimeConfigDir, layout, manifest, clusterMap) {
- See `0011-skill-surface-budget-module.md` — the Runtime Surface Module this seam serves
- See `0008-installer-migration-module.md` — legacy-layout migrations stay there
- See `0005-sdk-architecture-seam-map.md` — the seam map this module joins
- Existing canonical sibling: `get-shit-done/bin/lib/runtime-homes.cjs`
- Existing canonical sibling: `gsd-core/bin/lib/runtime-homes.cjs`
- Per-runtime skill converters this module references: `bin/install.js:1622` (Copilot), `:1681` (Claude), `:1792` (Antigravity), `:2534` (Codex)
- Hermes nested-skills layout rationale: `#2841`
## Implementation status
Phase 1 implementation landed on `feat/3663-runtime-artifact-layout-module-phase-1-m`:
- `get-shit-done/bin/lib/runtime-artifact-layout.cjs` — 15-runtime layout table (grok intentionally excluded), `resolveRuntimeArtifactLayout(runtime, configDir, scope) → Layout`, walk-up `findInstallSourceRoot` helper.
- `gsd-core/bin/lib/runtime-artifact-layout.cjs` — 15-runtime layout table (grok intentionally excluded), `resolveRuntimeArtifactLayout(runtime, configDir, scope) → Layout`, walk-up `findInstallSourceRoot` helper.
- Clarification: in this Phase 1 implementation, **Cline resolves to zero kinds** (`kinds: []`), so it carries no `commands` kind in the layout table.
- `get-shit-done/bin/lib/install-profiles.cjs` — new `stageSkillsForRuntimeAsSkills(srcCommandsDir, resolvedProfile, converter, prefix) → stagedDir` helper.
- `get-shit-done/bin/lib/surface.cjs` — `applySurface(runtimeConfigDir, layout, manifest, clusterMap)` signature migration; `_findInstallSource` + `_findAgentsSource` deleted; `_syncGsdDir` extended to handle the `skills` kind via directory iteration.
- `gsd-core/bin/lib/install-profiles.cjs` — new `stageSkillsForRuntimeAsSkills(srcCommandsDir, resolvedProfile, converter, prefix) → stagedDir` helper.
- `gsd-core/bin/lib/surface.cjs` — `applySurface(runtimeConfigDir, layout, manifest, clusterMap)` signature migration; `_findInstallSource` + `_findAgentsSource` deleted; `_syncGsdDir` extended to handle the `skills` kind via directory iteration.
- Tests: `runtime-artifact-layout-resolve.test.cjs` (16), `runtime-artifact-layout-edge-cases.test.cjs` (10), `runtime-artifact-layout-stage.test.cjs` (5), `install-profiles-stage.test.cjs` (+7 new), `surface-apply.test.cjs` (updated 5 call sites + new skills-kind test).
Phase 2 (separate issue #3664 — `bin/install.js` install/uninstall pipeline migration) is blocked on Phase 1 merge.

View File

@@ -8,11 +8,11 @@
### The `$GSD_SDK` → `gsd_run` rename (#373/#379)
PRs #373 and #379 renamed the runtime resolver from the unquoted `$GSD_SDK` shell variable to a single-line, space-safe `gsd_run` launcher. The launcher is defined in `get-shit-done/workflows/_runtime-launcher.snippet.sh`, propagated to all workflow `.md` files by `scripts/sync-runtime-launcher.cjs`, and enforced by `tests/runtime-launcher-parity.test.cjs` (which forbids any `$GSD_SDK` token in workflow markdown).
PRs #373 and #379 renamed the runtime resolver from the unquoted `$GSD_SDK` shell variable to a single-line, space-safe `gsd_run` launcher. The launcher is defined in `gsd-core/workflows/_runtime-launcher.snippet.sh`, propagated to all workflow `.md` files by `scripts/sync-runtime-launcher.cjs`, and enforced by `tests/runtime-launcher-parity.test.cjs` (which forbids any `$GSD_SDK` token in workflow markdown).
### The silent regression (#406)
During a multi-PR merge sweep, PR #406 (`fix(#160)`) — branched **before** #379 — re-introduced 5 `$GSD_SDK` occurrences into `get-shit-done/workflows/next.md`. Because it edited a **different** region of the file than #379, the merge produced no textual conflict and Git accepted it silently.
During a multi-PR merge sweep, PR #406 (`fix(#160)`) — branched **before** #379 — re-introduced 5 `$GSD_SDK` occurrences into `gsd-core/workflows/next.md`. Because it edited a **different** region of the file than #379, the merge produced no textual conflict and Git accepted it silently.
#406's own CI was green because its base predated the parity test, and nothing re-checked the merge result against current `next`. #406 also carried a stale companion assertion (`tests/policy-160-route0-resume.test.cjs`) that **required** `$GSD_SDK` to be present.
@@ -64,5 +64,5 @@ A green PR on a stale base can still regress the integration branch via a **sema
- Rename PRs: #373, #379 (`gsd_run`)
- Propagator: `scripts/sync-runtime-launcher.cjs`
- Parity test: `tests/runtime-launcher-parity.test.cjs`
- Launcher snippet: `get-shit-done/workflows/_runtime-launcher.snippet.sh`
- Launcher snippet: `gsd-core/workflows/_runtime-launcher.snippet.sh`
- Tracking issue: #415

View File

@@ -23,7 +23,7 @@ This codebase adopts ESLint flat config (eslint ≥ 9) with `typescript-eslint`,
### Generated vs hand-written split
Approximately 59 hand-written and 13 generated `.cjs` files currently coexist in `get-shit-done/bin/lib/`. The hand-written files are not checked by `typescript-eslint` type-aware rules because `tsconfig.lint.json` is not wired into an ESLint project. ADR 457 (`457-generated-cjs-single-source.md`) proposes collapsing this split; the present ADR is a prerequisite: the ESLint harness must exist before the collapse can surface type errors.
Approximately 59 hand-written and 13 generated `.cjs` files currently coexist in `gsd-core/bin/lib/`. The hand-written files are not checked by `typescript-eslint` type-aware rules because `tsconfig.lint.json` is not wired into an ESLint project. ADR 457 (`457-generated-cjs-single-source.md`) proposes collapsing this split; the present ADR is a prerequisite: the ESLint harness must exist before the collapse can surface type errors.
## Decision

View File

@@ -9,7 +9,7 @@
>
> **Provenance note.** An earlier draft of this ADR (and issue #457) was authored
> by an agent and asserted a codebase state that did not exist — "~13 files
> generated from `.ts` via `tsc`", `get-shit-done/src/` / `sdk/src/` source trees,
> generated from `.ts` via `tsc`", `gsd-core/src/` / `sdk/src/` source trees,
> and a `tests/cjs-ts-parity.test.cjs`. None of those existed. This rewrite
> grounds the decision in verified ground truth. Do not restore the earlier
> "natural completion of the 13 generated files" framing; it was fiction.
@@ -18,12 +18,12 @@
### What actually exists today (verified 2026-05-31)
- `get-shit-done/bin/lib/` holds **84** `.cjs` files. **Exactly one** carries a
- `gsd-core/bin/lib/` holds **84** `.cjs` files. **Exactly one** carries a
`// @generated` header: `package-identity.cjs`.
- That one generated file is **not** `tsc` output. It is produced by
`scripts/generate-package-identity.cjs` — a plain Node script that reads
`package.json` and **bakes literal coordinate values** into a CJS module.
- There is **no** `get-shit-done/src/` or `sdk/src/` TypeScript tree. There is
- There is **no** `gsd-core/src/` or `sdk/src/` TypeScript tree. There is
**no** TS→CJS transpilation pipeline. There is **no**
`tests/cjs-ts-parity.test.cjs`. The only parity test is
`tests/issue-498-package-identity.test.cjs`, scoped to the one baked file: it
@@ -88,7 +88,7 @@ build artifact?** Three models:
2. **Build at publish (recommended).** `bin/lib/*.cjs` becomes a gitignored
build artifact emitted from a TS `src/` tree by `tsc`; npm publishes the
built output. **Feasible today:** `package.json` already ships `get-shit-done`
built output. **Feasible today:** `package.json` already ships `gsd-core`
and `scripts` via its `files` array, and already runs a pre-publish build
step (`"prepublishOnly": "npm run build:hooks"`) — the `.cjs` emit hooks into
the same step, and `npm pack` includes on-disk artifacts regardless of