* 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>
11 KiB
Multi-agent orchestration in GSD Core
Explanation — This document describes why GSD Core is designed around multi-agent orchestration and how the pieces fit together. It is not a step-by-step guide. For configuration, see Configure model profiles and the Configuration reference. For the full agent roster, see Inventory.
The problem this design solves
AI coding agents degrade. Not because the model gets worse, but because the context window fills up. As a conversation grows, earlier decisions and code get pushed out or diluted by the noise of intermediate steps. By the time an agent writes the fifth file in a complex task, it may have already forgotten the constraint stated in the first message. This is sometimes called context rot.
GSD Core's multi-agent design is a direct response to that problem. Instead of
one long-running agent carrying the whole session, a thin orchestrator spawns
short-lived specialised agents, each with a fresh 200 K-token context window
and only the artifacts it needs to do its specific job. The orchestrator
never does heavy lifting itself; it loads context, spawns the right agent,
collects the result, and updates shared state in .planning/.
The orchestrator → agent pattern
Every workflow in gsd-core/workflows/ follows the same shape:
Orchestrator (workflow .md file)
│
├── Load context
│ gsd-tools.cjs init <workflow> <phase>
│ → JSON: project info, config, state, phase details
│
├── Resolve model
│ gsd-tools.cjs resolve-model <agent-name>
│ → opus | sonnet | haiku | inherit
│
├── Spawn specialised agent (Task/SubAgent call)
│ ├── Agent definition (agents/*.md)
│ ├── Context payload (init JSON)
│ ├── Model assignment
│ └── Tool permissions
│
├── Collect result
│
└── Update state
gsd-tools.cjs state update / state patch / state advance-plan
The orchestrator is deliberately thin. It does not reason about the domain, does not write code, and does not interpret results beyond routing them to the next step. That boundary keeps each layer's responsibility clear and prevents the orchestrator's context from accumulating domain noise.
The agent roster
GSD Core's agents fall into functional categories that map onto the research → plan → execute → verify pipeline:
| Category | Agents | Typical parallelism |
|---|---|---|
| Researchers | gsd-project-researcher, gsd-phase-researcher, gsd-ui-researcher, gsd-advisor-researcher |
4 parallel (stack, features, architecture, pitfalls) |
| Synthesisers | gsd-research-synthesizer |
Sequential, after researchers complete |
| Planners | gsd-planner, gsd-roadmapper |
Sequential |
| Checkers | gsd-plan-checker, gsd-integration-checker, gsd-ui-checker, gsd-nyquist-auditor |
Sequential, up to 3 revision iterations |
| Executors | gsd-executor |
Parallel within a wave, sequential across waves |
| Verifiers | gsd-verifier |
Sequential, after all executors complete |
| Mappers | gsd-codebase-mapper |
4 parallel sub-probes |
| Auditors | gsd-ui-auditor, gsd-security-auditor |
Sequential |
Each agent definition (in agents/*.md) declares its allowed tool access,
purpose, and colour for terminal output. An agent that only needs to read files
and write a single output document gets exactly those permissions — no Bash
execution, no access to broader state. That constraint is intentional: it
keeps the blast radius small if an agent behaves unexpectedly.
For the complete 31-agent roster, see Inventory.
Wave-based parallel execution
The most visible expression of multi-agent design is how /gsd-execute-phase
handles a set of plans that may depend on one another.
Before spawning any executor, the orchestrator performs a wave analysis:
it reads the dependency declarations in each PLAN.md file and groups plans
into waves. Plans with no declared dependencies form Wave 1 and run in
parallel. Plans that depend on Wave 1 form Wave 2, and so on.
Plan 01 (no deps) ─┐
Plan 02 (no deps) ─┤─── Wave 1 (parallel)
Plan 03 (depends: 01) ─┤─── Wave 2 (waits for Wave 1)
Plan 04 (depends: 02) ─┘
Plan 05 (depends: 03, 04) ─── Wave 3 (waits for Wave 2)
Each executor within a wave:
- receives a fresh context window (200 K tokens, or up to 1 M on capable models)
- receives the specific
PLAN.mdit is responsible for - receives project context (
PROJECT.md,STATE.md) - receives phase context (
CONTEXT.md,RESEARCH.mdif available) - produces atomic git commits on completion
- writes a
SUMMARY.mddescribing what was built
After all executors in a wave finish, the orchestrator runs the pre-commit
hook once for the wave as a whole. Executors commit with --no-verify to
prevent build-lock contention (for example, Cargo lock fights in Rust
projects) when multiple agents commit in parallel. The hook therefore runs
once per wave rather than once per commit.
Parallel commit safety
Two mechanisms prevent write conflicts when multiple executors run simultaneously:
-
Atomic lock on
STATE.md— Every write toSTATE.mduses a lockfile (STATE.md.lock) withO_EXCLatomic creation. This prevents the read-modify-write race where two agents each read the file, modify different fields, and the later writer overwrites the earlier one's changes. Stale locks (older than 10 seconds) are automatically cleared. -
Per-wave hook run — Rather than each executor running pre-commit hooks independently (which can cause file-level contention on shared build artefacts), the orchestrator runs
git hook run pre-commitonce after every wave completes.
Adaptive context enrichment for large-window models
Standard 200 K context windows are enough for an executor to implement a
single focused plan. When the configured context_window is 500 K tokens or
larger (for example, when using Opus 4.6 or Sonnet 4.6 in 1 M-class mode),
the orchestrator automatically enriches subagent prompts with additional
context that would not fit in a standard window:
- Executor agents receive prior-wave
SUMMARY.mdfiles and the phaseCONTEXT.md/RESEARCH.md, giving them cross-plan awareness within the phase - Verifier agents receive all
PLAN.md,SUMMARY.md, andCONTEXT.mdfiles plusREQUIREMENTS.md, enabling history-aware verification
This enrichment is conditional on the context_window value in
config.json. On standard-window configurations, prompts use truncated
versions with cache-friendly ordering to maximise token efficiency.
Why this design — the connection to context engineering
The orchestrator → agent pattern only makes sense as part of a broader approach to context engineering: the idea that what an AI agent gets in its context window matters as much as the model tier or prompt quality. See Context engineering for the full treatment.
Multi-agent orchestration operationalises context engineering in two ways:
Context isolation. Each agent receives only what it needs. A researcher gets the project description and domain questions; it does not get the full planning history. A verifier gets every plan and summary; it does not get the raw research. Isolation keeps each agent's context dense with signal rather than diluted by noise from other pipeline stages.
Context hygiene across sessions. Because all state lives in
.planning/ as human-readable Markdown and JSON (not in any agent's context
window), GSD workflows survive context resets (/clear), tab switches, and
multi-day breaks. The next agent always starts from persisted, verified
artifacts rather than from a reconstructed memory of a long conversation.
Trade-offs
Multi-agent orchestration is not free.
Coordination overhead. Each agent spawn is a round-trip: the orchestrator
must format a prompt, hand off context, wait for the subagent to complete
(typically 1–5 minutes), and then parse the result. A single capable agent
working in one context would finish faster for simple tasks. GSD mitigates
this by making parallelism the default wherever dependencies permit — the
four researchers in a plan-phase run simultaneously, not sequentially.
Opacity during execution. While a subagent is running, its work is invisible to the parent session. There is no live progress stream. This is a deliberate consequence of the fresh-context design: the subagent is operating in its own context window. The orchestrator shows a liveness note on the spawn line ("runs in a subagent — no output until it returns") to set expectations.
Context stitching cost. Packaging the right artifacts for each agent
requires the orchestrator to spend tokens assembling and transmitting context
payloads. This is the cost of isolation. The gsd-tools.cjs init handler
produces a JSON payload that balances completeness with token budget, applying
cache-friendly ordering so that the stable parts of the payload (project
definition, config) hit the cache on repeat invocations.
Model cost amplification. Running five agents in parallel at Opus tier
costs more than running one. The model profile system (model_profiles.md,
resolved per agent by model-profiles.cjs) lets you assign cheaper tiers to
less critical agents. The dynamic_routing feature further reduces cost by
starting every agent on a cheaper tier and escalating only on a soft failure.
See Configuration for the full options.
In return for these costs, the design buys consistent quality across large phases. An executor writing the tenth file in a 400-line plan does not degrade because its context is fresh. A verifier checking twenty requirements does not forget the first ten because it received all of them as structured input rather than conversation history.
Related
- Context engineering — the upstream principle that motivates this design
- Configure model profiles — how to assign model tiers per agent
- Configuration reference — full
config.jsonschema includingmodels,model_overrides,dynamic_routing, andcontext_window - Inventory — authoritative agent roster and workflow list
- Architecture — implementation-level detail on the orchestrator → agent pattern and wave execution model
- Docs index