@opengsd/gsd-core 1.7.0-rc.3 → 1.7.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.ja-JP.md +4 -3
  4. package/README.ko-KR.md +4 -3
  5. package/README.md +4 -3
  6. package/README.pt-BR.md +4 -3
  7. package/README.zh-CN.md +4 -3
  8. package/bin/install.js +33 -15
  9. package/commands/gsd/map-codebase.md +4 -4
  10. package/commands/gsd/ns-project.md +2 -1
  11. package/commands/gsd/onboard.md +46 -0
  12. package/gsd-core/bin/gsd-tools.cjs +47 -2
  13. package/gsd-core/bin/lib/capability-registry.cjs +467 -49
  14. package/gsd-core/bin/lib/capability-state.cjs +117 -18
  15. package/gsd-core/bin/lib/capability-writer.cjs +11 -1
  16. package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +136 -0
  17. package/gsd-core/bin/lib/claude-orchestration.cjs +404 -0
  18. package/gsd-core/bin/lib/clusters.cjs +1 -0
  19. package/gsd-core/bin/lib/command-aliases.cjs +8 -0
  20. package/gsd-core/bin/lib/init-command-router.cjs +4 -0
  21. package/gsd-core/bin/lib/init.cjs +25 -69
  22. package/gsd-core/bin/lib/install-engine.cjs +8 -5
  23. package/gsd-core/bin/lib/install-profiles.cjs +2 -0
  24. package/gsd-core/bin/lib/loop-resolver.cjs +7 -1
  25. package/gsd-core/bin/lib/model-resolver.cjs +69 -4
  26. package/gsd-core/bin/lib/onboard-projection.cjs +309 -0
  27. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  28. package/gsd-core/bin/lib/surface.cjs +51 -30
  29. package/gsd-core/bin/shared/model-catalog.json +5 -0
  30. package/gsd-core/references/gsd-run-resolver.md +8 -0
  31. package/gsd-core/templates/project.md +1 -1
  32. package/gsd-core/workflows/do.md +4 -3
  33. package/gsd-core/workflows/help/modes/brief.md +2 -1
  34. package/gsd-core/workflows/help/modes/default.md +2 -1
  35. package/gsd-core/workflows/help/modes/full.md +11 -1
  36. package/gsd-core/workflows/help/modes/topic.md +1 -1
  37. package/gsd-core/workflows/onboard.md +277 -0
  38. package/gsd-core/workflows/quick.md +20 -0
  39. package/package.json +1 -1
  40. package/scripts/gen-golden-install-parity-zcode.cjs +77 -0
  41. package/scripts/run-tests.cjs +5 -3
  42. package/scripts/sync-runtime-launcher.cjs +20 -2
  43. package/skills/gsd-map-codebase/SKILL.md +3 -3
  44. package/skills/gsd-ns-project/SKILL.md +1 -0
  45. package/skills/gsd-onboard/SKILL.md +46 -0
@@ -7,7 +7,8 @@ One-liner refresher for returning users. Output ONLY the `<reference>` content b
7
7
 
8
8
  ```text
9
9
  /gsd:new-project Initialize a project (greenfield)
10
- /gsd:map-codebase Map an existing codebase (brownfield)
10
+ /gsd:onboard Onboard an existing codebase (brownfield)
11
+ /gsd:map-codebase Refresh/map codebase intelligence
11
12
  /gsd:plan-phase <N> Create a phase plan
12
13
  /gsd:execute-phase <N> Execute a phase
13
14
  /gsd:progress Where am I, what's next
@@ -11,11 +11,12 @@ Plan-driven development for solo agentic work with Claude Code. GSD Core turns a
11
11
 
12
12
  ```text
13
13
  /gsd:new-project # Greenfield: questioning → research → requirements → roadmap
14
+ /gsd:onboard # Existing codebase: map → ingest docs → initialize planning
14
15
  /gsd:plan-phase 1 # Create a detailed plan for phase 1
15
16
  /gsd:execute-phase 1 # Execute all plans in the phase
16
17
  ```
17
18
 
18
- Existing codebase? Run `/gsd:map-codebase` first to ground GSD in your code.
19
+ Existing codebase? Run `/gsd:onboard` to map the repo, ingest existing docs, and initialize planning safely.
19
20
 
20
21
  ## Common commands
21
22
 
@@ -62,6 +62,16 @@ Creates all `.planning/` artifacts:
62
62
 
63
63
  Usage: `/gsd:new-project`
64
64
 
65
+ **`/gsd:onboard [--fast] [--text]`**
66
+ Guide first-time onboarding for an existing codebase.
67
+
68
+ - Detects brownfield code, existing planning docs, and partial `.planning/` state
69
+ - Routes through `/gsd:map-codebase`, `/gsd:ingest-docs`, and `/gsd:new-project` in the safe order
70
+ - Creates `.planning/onboarding/SUMMARY.md` after project setup
71
+ - Idempotent: confirms existing artifacts and does not overwrite planning silently
72
+
73
+ Usage: `/gsd:onboard`
74
+
65
75
  **`/gsd:map-codebase [--fast] [--focus <area>] [--query <term>]`**
66
76
  Map an existing codebase for brownfield projects.
67
77
 
@@ -72,7 +82,7 @@ Map an existing codebase for brownfield projects.
72
82
  - Analyzes codebase with parallel Explore agents
73
83
  - Creates `.planning/codebase/` with 7 focused documents
74
84
  - Covers stack, architecture, structure, conventions, testing, integrations, concerns
75
- - Use before `/gsd:new-project` on existing codebases
85
+ - Usually reached through `/gsd:onboard` for first-time existing-codebase setup; run directly to refresh or focus a map
76
86
 
77
87
  Usage: `/gsd:map-codebase`
78
88
 
@@ -9,7 +9,7 @@ Emit a section from the full reference for the topic in `$ARGUMENTS`. Read `work
9
9
  |---|---|
10
10
  | `next`, `smart-entry` | `### Smart Entry` |
11
11
  | `workflow`, `core`, `core-workflow` | `## Core Workflow` (entire section through end of `### Quick Mode`) |
12
- | `init`, `new-project` | `### Project Initialization` |
12
+ | `init`, `new-project`, `onboard`, `onboarding`, `brownfield` | `### Project Initialization` |
13
13
  | `map`, `map-codebase` | The `/gsd:map-codebase` block under `### Project Initialization` |
14
14
  | `discuss`, `discuss-phase` | The `/gsd:discuss-phase` block under `### Phase Planning` |
15
15
  | `plan`, `planning`, `plan-phase` | `### Phase Planning` |
@@ -0,0 +1,277 @@
1
+ # /gsd:onboard Workflow
2
+
3
+ One-command onboarding for an existing or unknown repo. This workflow is a thin
4
+ renderer around `init onboard`; deterministic routing lives in the CLI projection.
5
+
6
+ @~/.claude/gsd-core/references/gsd-run-resolver.md
7
+
8
+ ## 1. Render the Onboarding Projection
9
+
10
+ Parse `$ARGUMENTS`:
11
+ - `--fast` passes `--fast` to `init onboard`. Fast mode accepts the fast map for lightweight onboarding only; `next_action` still decides whether complete map work is required before project setup.
12
+ - `--text` forces text-mode choices for runtimes without `AskUserQuestion`.
13
+
14
+ Run the standard `gsd_run` resolver from the reference above, then run the projection from the runtime root:
15
+
16
+ ```bash
17
+ # If --fast was parsed from $ARGUMENTS:
18
+ INIT=$(gsd_run --cwd "$_GSD_RUNTIME_ROOT" init onboard --fast --raw)
19
+ # Otherwise:
20
+ INIT=$(gsd_run --cwd "$_GSD_RUNTIME_ROOT" init onboard --raw)
21
+ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
22
+ ```
23
+
24
+ Parse JSON fields from `INIT`:
25
+
26
+ - `next_action.kind`, `next_action.command`, `next_action.reason`, `next_action.missing`, `next_action.summary_path`
27
+ - `handoff_commands.ingest_docs`, `handoff_commands.manager`, `handoff_commands.new_project`, `handoff_commands.onboard`
28
+ - `map_readiness`, `codebase_map_summary_status`, `codebase_map_final_status`
29
+ - `planning_exists`, `project_exists`, `requirements_exists`, `roadmap_exists`, `state_exists`
30
+ - `is_brownfield`, `fast_mode`, `has_codebase_map`, `has_fast_codebase_map`
31
+ - `missing_codebase_map_files`, `missing_fast_codebase_map_files`
32
+ - `has_docs_candidates`, `doc_candidate_count`, `onboarding_summary_exists`
33
+ - `commit_docs`, `text_mode`, `has_git`, `git_worktree_root`, `in_nested_subdir`
34
+
35
+ Set:
36
+ - `TEXT_MODE=true` if `--text` is present or `text_mode` is true. When `TEXT_MODE` is active, replace every `AskUserQuestion` call below with a plain-text numbered list and ask the user to type their choice number — required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
37
+ - `ONBOARDING_ROOT={git_worktree_root || _GSD_RUNTIME_ROOT}`.
38
+
39
+ If `has_git` and `in_nested_subdir` are true, warn that onboarding artifacts belong to the outer worktree at `git_worktree_root`. Do not run `git init`.
40
+
41
+ ## 2. Execute `next_action`
42
+
43
+ ### `map-codebase`
44
+
45
+ If `next_action.kind == "map-codebase"`:
46
+
47
+ - If `TEXT_MODE=true`, print:
48
+
49
+ ```text
50
+ {next_action.reason}
51
+ Missing map files: {fast_mode ? missing_fast_codebase_map_files : missing_codebase_map_files}
52
+
53
+ 1. Map codebase first — run {next_action.command} from worktree root {ONBOARDING_ROOT} (Recommended)
54
+ 2. Skip mapping — continue with weaker onboarding context
55
+
56
+ Enter number:
57
+ ```
58
+
59
+ - Otherwise use AskUserQuestion:
60
+ - header: "Codebase"
61
+ - question: "{next_action.reason} Map it first?"
62
+ - options:
63
+ - "Map codebase first" — Run `{next_action.command}` from worktree root `{ONBOARDING_ROOT}` (Recommended)
64
+ - "Skip mapping" — Continue with weaker onboarding context
65
+
66
+ If the user chooses mapping, do not nest the interactive workflow. Print:
67
+
68
+ ```text
69
+ Run from worktree root {ONBOARDING_ROOT}:
70
+
71
+ {next_action.command}
72
+
73
+ Then rerun {handoff_commands.onboard} from the same worktree root.
74
+ ```
75
+
76
+ Exit. If the user skips mapping:
77
+
78
+ - If `(project_exists || requirements_exists || roadmap_exists || state_exists) && (!project_exists || !requirements_exists || !roadmap_exists || !state_exists)`, route the skip to the partial planning guard instead:
79
+
80
+ ```text
81
+ Skipping codebase mapping may give downstream steps weaker context, but project planning exists and is incomplete.
82
+
83
+ PROJECT.md: {project_exists ? "present" : "missing"}
84
+ REQUIREMENTS.md: {requirements_exists ? "present" : "missing"}
85
+ ROADMAP.md: {roadmap_exists ? "present" : "missing"}
86
+ STATE.md: {state_exists ? "present" : "missing"}
87
+
88
+ Run the appropriate lower-level command to fill the missing planning artifact(s), then rerun {handoff_commands.onboard}.
89
+ ```
90
+
91
+ Exit.
92
+
93
+ - If `has_docs_candidates && !project_exists`, route the skip to docs ingest instead:
94
+
95
+ ```text
96
+ Skipping codebase mapping may give downstream steps weaker context, but existing ADR/PRD/SPEC/RFC documents should still be ingested before {handoff_commands.new_project}.
97
+
98
+ Run from worktree root {ONBOARDING_ROOT}:
99
+
100
+ {handoff_commands.ingest_docs}
101
+
102
+ Then rerun {handoff_commands.onboard} from the same worktree root.
103
+ ```
104
+
105
+ Exit.
106
+
107
+ - Otherwise print:
108
+
109
+ ```text
110
+ Skipping codebase mapping may give {handoff_commands.new_project} weaker context.
111
+
112
+ Run from worktree root {ONBOARDING_ROOT}:
113
+
114
+ {handoff_commands.new_project}
115
+
116
+ Then rerun {handoff_commands.onboard} from the same worktree root.
117
+ ```
118
+
119
+ Exit.
120
+
121
+ ### `ingest-docs`
122
+
123
+ If `next_action.kind == "ingest-docs"`:
124
+
125
+ - If `TEXT_MODE=true`, print:
126
+
127
+ ```text
128
+ {next_action.reason}
129
+ Detected {doc_candidate_count} possible ADR/PRD/SPEC/RFC document(s).
130
+
131
+ 1. Ingest docs first — run {next_action.command} from worktree root {ONBOARDING_ROOT} (Recommended)
132
+ 2. Skip docs ingest — continue to {handoff_commands.new_project}
133
+
134
+ Enter number:
135
+ ```
136
+
137
+ - Otherwise use AskUserQuestion:
138
+ - header: "Docs"
139
+ - question: "Detected {doc_candidate_count} possible ADR/PRD/SPEC/RFC document(s). Ingest them first?"
140
+ - options:
141
+ - "Ingest docs first" — Run `{next_action.command}` from worktree root `{ONBOARDING_ROOT}` (Recommended)
142
+ - "Skip docs ingest" — Continue to `{handoff_commands.new_project}`
143
+
144
+ If the user chooses ingest, print:
145
+
146
+ ```text
147
+ Run from worktree root {ONBOARDING_ROOT}:
148
+
149
+ {next_action.command}
150
+
151
+ Then rerun {handoff_commands.onboard} from the same worktree root.
152
+ ```
153
+
154
+ Exit. If the user skips docs ingest, print:
155
+
156
+ ```text
157
+ Skipping docs ingest may omit existing ADR/PRD/SPEC/RFC context from {handoff_commands.new_project}.
158
+
159
+ Run from worktree root {ONBOARDING_ROOT}:
160
+
161
+ {handoff_commands.new_project}
162
+
163
+ Then rerun {handoff_commands.onboard} from the same worktree root.
164
+ ```
165
+
166
+ Exit.
167
+
168
+ ### `complete-map-before-new-project`
169
+
170
+ If `next_action.kind == "complete-map-before-new-project"`, print:
171
+
172
+ ```text
173
+ {next_action.reason}
174
+
175
+ Run from worktree root {ONBOARDING_ROOT}:
176
+
177
+ {next_action.command}
178
+
179
+ Then rerun {handoff_commands.onboard} from the same worktree root.
180
+ ```
181
+
182
+ Exit.
183
+
184
+ ### `new-project`
185
+
186
+ If `next_action.kind == "new-project"`, print:
187
+
188
+ ```text
189
+ {next_action.reason}
190
+
191
+ Run from worktree root {ONBOARDING_ROOT}:
192
+
193
+ {next_action.command}
194
+
195
+ Then rerun {handoff_commands.onboard} from the same worktree root.
196
+ ```
197
+
198
+ Exit.
199
+
200
+ ### `partial-planning`
201
+
202
+ If `next_action.kind == "partial-planning"`, print:
203
+
204
+ ```text
205
+ Project planning exists but is incomplete.
206
+
207
+ Missing files: {next_action.missing}
208
+ REQUIREMENTS.md: {requirements_exists ? "present" : "missing"}
209
+ ROADMAP.md: {roadmap_exists ? "present" : "missing"}
210
+ STATE.md: {state_exists ? "present" : "missing"}
211
+
212
+ Run the appropriate lower-level command to fill the missing planning artifact(s), then rerun {handoff_commands.onboard}.
213
+ ```
214
+
215
+ Exit.
216
+
217
+ ### `ready`
218
+
219
+ If `next_action.kind == "ready"`, print the final status section and exit.
220
+
221
+ ### `write-summary`
222
+
223
+ If `next_action.kind == "write-summary"`, continue to summary creation.
224
+
225
+ ## 3. Create Onboarding Summary
226
+
227
+ Create `{ONBOARDING_ROOT}/{next_action.summary_path}`. Do not overwrite an existing summary; the projection should only route here when the summary is missing.
228
+
229
+ Summary template:
230
+
231
+ ```markdown
232
+ # Onboarding Summary
233
+
234
+ ## Project State
235
+ - PROJECT.md: {project_exists ? "present" : "missing"}
236
+ - REQUIREMENTS.md: {requirements_exists ? "present" : "missing"}
237
+ - ROADMAP.md: {roadmap_exists ? "present" : "missing"}
238
+ - STATE.md: {state_exists ? "present" : "missing"}
239
+
240
+ ## Codebase Context
241
+ - Brownfield repo: {is_brownfield ? "yes" : "no"}
242
+ - Map readiness: {map_readiness}
243
+ - Codebase map: {codebase_map_summary_status}
244
+ - Fast map available: {has_fast_codebase_map ? "yes" : "no"}
245
+
246
+ ## Docs Context
247
+ - Existing ADR/PRD/SPEC/RFC candidates: {has_docs_candidates ? doc_candidate_count : 0}
248
+
249
+ ## Recommended Next Step
250
+ - {handoff_commands.manager}
251
+ ```
252
+
253
+ If `commit_docs` is true, commit only the summary path from the onboarding root:
254
+
255
+ ```bash
256
+ gsd_run --cwd "$ONBOARDING_ROOT" query commit "docs: create onboarding summary" --files .planning/onboarding/SUMMARY.md
257
+ ```
258
+
259
+ Continue to final status.
260
+
261
+ ## 4. Final Status
262
+
263
+ Print:
264
+
265
+ ```text
266
+ Onboarding status:
267
+ - PROJECT.md: {project_exists ? "present" : "missing"}
268
+ - REQUIREMENTS.md: {requirements_exists ? "present" : "missing"}
269
+ - ROADMAP.md: {roadmap_exists ? "present" : "missing"}
270
+ - STATE.md: {state_exists ? "present" : "missing"}
271
+ - Codebase map: {codebase_map_final_status}
272
+ - Onboarding summary: present
273
+
274
+ Next recommended command: {handoff_commands.manager}
275
+ ```
276
+
277
+ Do not run implementation execution or shipping from onboarding.
@@ -671,6 +671,26 @@ fi
671
671
 
672
672
  **Step 6: Spawn executor**
673
673
 
674
+ Auto-degrade to sequential if HEAD has diverged from the worktree fork base (#1941, mirrors
675
+ execute-phase's #683/#1369 guard). Claude Code's `isolation="worktree"` forks new worktrees from
676
+ `origin/HEAD`, not the live local HEAD. If a prior quick task in this session (or the Step 5.6
677
+ pre-dispatch plan commit above) advanced local HEAD without an intervening `git push`,
678
+ `origin/HEAD` stays pinned to a stale ancestor and the executor's `worktree_branch_check` guard
679
+ halts with a base-mismatch fatal — potentially many commits behind, not just one. Run this check
680
+ immediately before capturing `EXPECTED_BASE` so it reflects the most current local state.
681
+
682
+ ```bash
683
+ if [ "$RUNTIME" = "claude" ] && [ "${USE_WORKTREES:-true}" != "false" ]; then
684
+ _QUICK_SHOULD_DEGRADE=$(gsd_run query worktree.base-check --pick shouldDegrade 2>/dev/null || true)
685
+ if [ "$_QUICK_SHOULD_DEGRADE" = "true" ]; then
686
+ _QUICK_DEGRADE_MSG=$(gsd_run query worktree.base-check --pick message 2>/dev/null || true)
687
+ [ -n "$_QUICK_DEGRADE_MSG" ] && printf '%s\n' "$_QUICK_DEGRADE_MSG" >&2
688
+ echo "⚠ [#1941] Worktree fork base diverged from orchestrator HEAD — auto-degrading to sequential mode for this quick task to avoid a base-mismatch halt." >&2
689
+ USE_WORKTREES=false
690
+ fi
691
+ fi
692
+ ```
693
+
674
694
  Capture current HEAD before spawning (used for worktree branch check):
675
695
  ```bash
676
696
  EXPECTED_BASE=$(git rev-parse HEAD)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengsd/gsd-core",
3
- "version": "1.7.0-rc.3",
3
+ "version": "1.7.0-rc.4",
4
4
  "description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
5
5
  "main": ".opencode/plugins/gsd-core.js",
6
6
  "bin": {
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * Standalone golden-fixture generator for tests/golden-install-parity.
5
+ *
6
+ * This is a BUILD-TIME generation script — NOT a test run. It replicates the
7
+ * buildParityManifest logic from tests/golden-install-parity.test.cjs and
8
+ * captures the zcode fixture so the parity test (which the gsd-test gate runs)
9
+ * has a committed artifact to compare against. The authoritative test gate
10
+ * remains `gsd-test run`, never a local `node --test`.
11
+ *
12
+ * Usage: node scripts/gen-golden-install-parity-zcode.cjs
13
+ */
14
+ const fs = require('node:fs');
15
+ const path = require('node:path');
16
+ const crypto = require('node:crypto');
17
+
18
+ const ROOT = path.resolve(__dirname, '..');
19
+ const { walk, runMinimalInstall, RUNTIME_META } = require(path.join(ROOT, 'tests', 'helpers', 'install-shared.cjs'));
20
+ const PKG_VERSION = require(path.join(ROOT, 'package.json')).version;
21
+ const FIXTURE_DIR = path.join(ROOT, 'tests', 'fixtures', 'golden-install-parity');
22
+
23
+ const VOLATILE_FILES = new Set([
24
+ 'gsd-file-manifest.json',
25
+ 'gsd-install-state.json',
26
+ '.gsd-source',
27
+ 'gsd-core/CHANGELOG.md',
28
+ ]);
29
+ const HOOK_CONFIG_FILES = new Set(['settings.json', 'hooks.json']);
30
+ const EXCLUDED_PREFIXES = ['gsd-core/bin/lib/'];
31
+
32
+ function buildParityManifest(configDir, root) {
33
+ const allFiles = walk(configDir);
34
+ const unsorted = {};
35
+ for (const full of allFiles) {
36
+ const rel = path.relative(configDir, full).split(path.sep).join('/');
37
+ if (VOLATILE_FILES.has(rel)) continue;
38
+ if (HOOK_CONFIG_FILES.has(path.basename(rel))) continue;
39
+ if (EXCLUDED_PREFIXES.some((p) => rel.startsWith(p))) continue;
40
+ const content = fs.readFileSync(full);
41
+ const normalized = content.toString('utf8').split(root).join('<HOME>').split(PKG_VERSION).join('<VERSION>');
42
+ const hash = crypto.createHash('sha256').update(normalized).digest('hex').slice(0, 16);
43
+ unsorted[rel] = hash;
44
+ }
45
+ const sorted = {};
46
+ for (const key of Object.keys(unsorted).sort()) sorted[key] = unsorted[key];
47
+ return sorted;
48
+ }
49
+
50
+ function cleanup(root) {
51
+ try { fs.rmSync(root, { recursive: true, force: true }); } catch { /* best effort */ }
52
+ }
53
+
54
+ // Regenerate the fixture for every runtime in RUNTIME_META. Needed when a
55
+ // SHARED gsd-core payload file (e.g. model-catalog.json, capability-registry)
56
+ // changes content — its hash appears in every runtime's manifest, so all
57
+ // fixtures must be recaptured together. Usage:
58
+ // node scripts/gen-golden-install-parity-zcode.cjs [runtime ...]
59
+ // With no args, regenerates ALL runtimes. With args, only the named runtimes.
60
+ const targets = process.argv.slice(2).length > 0 ? process.argv.slice(2) : Object.keys(RUNTIME_META);
61
+ fs.mkdirSync(FIXTURE_DIR, { recursive: true });
62
+ for (const runtime of targets) {
63
+ if (!Object.prototype.hasOwnProperty.call(RUNTIME_META, runtime)) {
64
+ process.stderr.write(`[gen] unknown runtime '${runtime}' (not in RUNTIME_META) — skipping\n`);
65
+ continue;
66
+ }
67
+ const { configDir, root } = runMinimalInstall({ runtime, scope: 'global' });
68
+ let actual;
69
+ try {
70
+ actual = buildParityManifest(configDir, root);
71
+ } finally {
72
+ cleanup(root);
73
+ }
74
+ const fixturePath = path.join(FIXTURE_DIR, `${runtime}.json`);
75
+ fs.writeFileSync(fixturePath, JSON.stringify(actual, null, 2) + '\n', 'utf8');
76
+ process.stdout.write(`[gen] ${runtime}: wrote ${Object.keys(actual).length} file hashes -> ${fixturePath}\n`);
77
+ }
@@ -570,11 +570,13 @@ function main() {
570
570
  // progress (verified: no leaked handle / hang; --test-force-exit exits leaks
571
571
  // cleanly, so the timeout was pure slowness, NOT the leak the kill message guesses).
572
572
  // The per-chunk timeout is sized for a "healthy chunk (~4-5 min)"; keep chunks at
573
- // roughly half a shard so each gets its own fresh 600s budget and a fresh node
574
- // process (also relieving per-process memory pressure from 170+ files at once).
573
+ // roughly a third of a shard so each gets its own fresh 600s budget and a fresh
574
+ // node process (also relieving per-process memory pressure from 170+ files at once).
575
+ // Lowered from 90 to 60 after #1575 — macOS Node 22 shard 2/3 chunk 2 (~80 files
576
+ // including state.test.cjs, perf-*, worktree-cleanup) exceeded 600s with 90.
575
577
  const MAX_FILES_PER_CHUNK = process.env.RUN_TESTS_MAX_FILES_PER_CHUNK
576
578
  ? Number(process.env.RUN_TESTS_MAX_FILES_PER_CHUNK)
577
- : 90;
579
+ : 60;
578
580
 
579
581
  // node:test does not exit until the event loop drains. A unit test that leaks
580
582
  // an open handle (un-terminated Worker, un-killed child_process, ref'd timer)
@@ -343,9 +343,12 @@ function transformFile(content, preamble) {
343
343
  }
344
344
  }
345
345
 
346
- // Insert preamble into the first gsd_run block only
346
+ // Insert preamble into the first gsd_run block only — UNLESS this file
347
+ // delegates to the shared resolver reference (@-include). Delegating files keep
348
+ // the stripped blocks (any inline preamble removed) but never get one inserted.
349
+ const delegates = delegatesToResolverReference(content);
347
350
  const finalBlocks = strippedBlocks.map((stripped, bi) => {
348
- if (bi === firstGsdRunBlockIdx) {
351
+ if (!delegates && bi === firstGsdRunBlockIdx) {
349
352
  return insertPreamble(stripped, preamble);
350
353
  }
351
354
  return stripped;
@@ -374,6 +377,21 @@ function escapeRegExp(str) {
374
377
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
375
378
  }
376
379
 
380
+ /**
381
+ * A file "delegates to the shared resolver" when it pulls the canonical gsd_run
382
+ * preamble in from gsd-core/references/gsd-run-resolver.md via an @-include
383
+ * instead of inlining the snippet (see onboard.md / issue #1990).
384
+ *
385
+ * These files must NOT carry an inline preamble: the resolver reference ships the
386
+ * one canonical copy, and onboard-command.test.cjs asserts the inline form is
387
+ * absent. transformFile still STRIPS any inline preamble from them (so a stray
388
+ * copy is removed) but never re-inserts one — mirroring the exemption in
389
+ * runtime-launcher-parity.test.cjs (subtest B / B2).
390
+ */
391
+ function delegatesToResolverReference(content) {
392
+ return content.includes('references/gsd-run-resolver.md');
393
+ }
394
+
377
395
  // Main
378
396
  function main() {
379
397
  const preamble = loadPreamble();
@@ -42,7 +42,7 @@ Parse the first token of $ARGUMENTS:
42
42
  Check for .planning/STATE.md - loads context if project already initialized
43
43
 
44
44
  **This command can run:**
45
- - Before /gsd-new-project (brownfield codebases) - creates codebase map first
45
+ - Via /gsd-onboard for first-time brownfield setup - creates codebase map first
46
46
  - After /gsd-new-project (greenfield codebases) - updates codebase map as code evolves
47
47
  - Anytime to refresh codebase understanding
48
48
  </context>
@@ -51,7 +51,7 @@ Check for .planning/STATE.md - loads context if project already initialized
51
51
  **Use map-codebase for:**
52
52
  - Brownfield projects before initialization (understand existing code first)
53
53
  - Refreshing codebase map after significant changes
54
- - Onboarding to an unfamiliar codebase
54
+ - Refreshing or deepening an onboarded codebase map
55
55
  - Before major refactoring (understand current state)
56
56
  - When STATE.md references outdated codebase info
57
57
 
@@ -71,7 +71,7 @@ Check for .planning/STATE.md - loads context if project already initialized
71
71
  4. Wait for agents to complete, collect confirmations (NOT document contents)
72
72
  5. Verify all 7 documents exist with line counts
73
73
  6. Commit codebase map
74
- 7. Offer next steps (typically: /gsd-new-project or /gsd-plan-phase)
74
+ 7. Offer next steps (typically: /gsd-onboard, /gsd-new-project, or /gsd-plan-phase)
75
75
  </process>
76
76
 
77
77
  <success_criteria>
@@ -14,6 +14,7 @@ inline as part of `gsd-audit-milestone`'s output.
14
14
  | User wants | Invoke |
15
15
  |---|---|
16
16
  | Start a new project | gsd-new-project |
17
+ | Onboard an existing codebase | gsd-onboard |
17
18
  | Create a new milestone | gsd-new-milestone |
18
19
  | Complete the current milestone | gsd-complete-milestone |
19
20
  | Audit a milestone for issues | gsd-audit-milestone |
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: gsd-onboard
3
+ description: "Guide existing codebase onboarding through mapping, doc ingest, and planning setup"
4
+ argument-hint: "[--fast] [--text]"
5
+ allowed-tools:
6
+ - Read
7
+ - Bash
8
+ - Write
9
+ - Glob
10
+ - Grep
11
+ - Agent
12
+ - AskUserQuestion
13
+ ---
14
+
15
+ <runtime_note>
16
+ **Copilot (VS Code):** Use `vscode_askquestions` wherever this workflow calls `AskUserQuestion`. They are equivalent — `vscode_askquestions` is the VS Code Copilot implementation of the same interactive question API.
17
+ </runtime_note>
18
+
19
+ <objective>
20
+ Guide brownfield onboarding for an existing codebase by routing through the existing GSD primitives in the safe order: codebase map → docs ingest → project initialization → onboarding summary.
21
+
22
+ **Creates or confirms:**
23
+ - `.planning/codebase/` — evidence-backed codebase map from `/gsd-map-codebase`
24
+ - `.planning/PROJECT.md`, `REQUIREMENTS.md`, `ROADMAP.md`, `STATE.md` — project setup from `/gsd-new-project` or `/gsd-ingest-docs`
25
+ - `.planning/onboarding/SUMMARY.md` — lightweight index of what was learned and the next command
26
+
27
+ **Non-goals:** This command does not execute phases, ship work, or overwrite existing planning artifacts without an explicit gate.
28
+ </objective>
29
+
30
+ <execution_context>
31
+ @~/.claude/gsd-core/workflows/onboard.md
32
+ @~/.claude/gsd-core/references/ui-brand.md
33
+ @~/.claude/gsd-core/references/gate-prompts.md
34
+ </execution_context>
35
+
36
+ <context>
37
+ Arguments: $ARGUMENTS
38
+
39
+ Flags:
40
+ - `--fast` — prefer `/gsd-map-codebase --fast` for the mapping handoff; the complete map is still required before `/gsd-new-project`.
41
+ - `--text` — use plain-text numbered lists instead of TUI menus.
42
+ </context>
43
+
44
+ <process>
45
+ Execute the onboard workflow end-to-end. Preserve all safety gates, text-mode fallbacks, idempotency checks, and top-level handoff rules for nested interactive commands.
46
+ </process>