Files
msd-core/docs/research/2026-05-12-skill-surface-budget.md
Tom Boucher 463cffd894 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>
2026-06-02 18:35:29 -04:00

18 KiB
Raw Blame History

Skill surface budget — research memo

Date: 2026-05-12 Author: triage analysis for issue #3408 Status: Research input for ADR-0010 Reading time: ~15 min


1. Problem statement

Claude Code and other runtimes that surface skills enumerate every installed skill's name + description into the system prompt on every turn, inside an <available_skills> block. This block is capped by skillListingBudgetFraction — default 1% of the model's context window, ~2,000 tokens at 200k. When the combined descriptions exceed the cap, the harness silently truncates the tail. The reporter of #3408 has 135 installed skills across multiple plugins and observed dropped skills.

GSD's audited footprint:

Metric Value
Installed skills 66
Installed sub-agents 33 (not listed in <available_skills> — invoked via Task)
Total skill description chars 4,787
Estimated description tokens (÷4) ~1,196
Mean description length 72.5 chars
Description ceiling (enforced by scripts/lint-descriptions.cjs) 100 chars
GSD share of a 200k 1% budget ~60%

GSD on its own consumes roughly 60% of the default skill-listing budget. When the user stacks any other plugin of comparable size (the reporter's /doctor shows several), the budget is breached and skills get dropped. This is not a GSD-only problem, but GSD is the single biggest contributor in the typical install, so we are the natural place for ecosystem-wide remediation to start.

2. What's already in place

GSD has done one consolidation pass and shipped one install-time lever:

  • --minimal / --core-only install flag (bin/install.js:123, gsd-core/bin/lib/install-profiles.cjs). Stages a filtered copy of commands/gsd/ into a temp dir before each runtime-specific copy step. Reduces ~12k tokens of cold-start overhead to ~700.
  • MINIMAL_SKILL_ALLOWLIST — 6 skills: new-project, discuss-phase, plan-phase, execute-phase, help, update. Zero sub-agents in minimal.
  • Hard 100-char description budget, enforced in CI by scripts/lint-descriptions.cjs and npm run lint:descriptions.
  • gsd update (without --minimal) as the documented upgrade path from minimal → full.

The 100-char cap means shrinking descriptions further is not a viable lever — average is already 72.5 chars and the rare 99-char outliers exist because they earn their length (e.g. gsd:progress, gsd:inbox). Any future budget relief has to come from emitting fewer skills, not shorter ones.

3. Audit findings

3.1 Dependency topology (66 skills)

Hot nodes (counted by other-skill body references):

Rank Skill Callers Role
1 phase 38 Dispatcher to all phase-typed workflows
2 review 11 Review-output convergence
3 config 7 Project config display/edit
4 progress 5 Active-phase progress tracker
5 update 5 Upgrade path
6 discuss-phase 2 Main-loop step 1
6 execute-phase 2 Main-loop step 3
6 new-project 2 Bootstrap
6 plan-phase 2 Main-loop step 2

phase and review are the two skills whose absence would silently break dozens of others. phase is referenced by 38 other skills but is not in the current minimal allowlist — a latent gap worth raising. Confirm with a --minimal install + a /gsd:audit-fix invocation whether it still works.

3.2 Functional clusters

Greedy clustering by name + prose intent:

Cluster Skills Desc chars Desc tokens
core_loop 6 346 ~87
phase_variants (includes core_loop) 10 734 ~184
audit_review 11 772 ~193
milestone 4 292 ~73
research_ideate (sketch, spike, forensics, explore, graphify, ns-ideate) 6 482 ~121
workspace_state (pause, resume, workspace, workstreams, thread, capture, inbox) 7 496 ~124
docs 2 160 ~40
ui 2 122 ~31
ai_eval 2 179 ~45
ns_meta 6 315 ~79
utility (incl. health, stats, settings, cleanup, pr-branch, ship, undo, fast, quick, …) 23 1,714 ~429

The utility bucket is the heaviest single cluster (36% of description tokens) and is also the most heterogeneous — half of these likely run only once per project, not once per session. They're the prime candidates for an "advanced" or "opt-in" tier.

3.3 Consolidation ceiling

A second consolidation pass (collapsing all 10 *-phase skills into a single dispatcher) would save ~150 tokens at most, at the cost of making the user-facing slash commands less discoverable in the UI and complicating argument parsing. Pure consolidation has diminishing returns below the current 66-skill count; the next big saves come from changing what gets surfaced rather than how it's written.

4. Options

Each option is graded against five dimensions:

  • User UX: how surprising / how much new mental model
  • Implementation cost: relative dev effort
  • Dependency safety: risk of breaking cross-skill calls
  • Token savings: vs. current ~1,196 desc tokens
  • Anthropic dependency: whether platform changes are required

Option A — Expand install-time profiles (named feature flags)

Layer named profiles on top of the existing minimal/full binary:

gsd install --profile=core            # current --minimal (6 skills, 0 agents)
gsd install --profile=standard        # +phase, +review, +config, +progress (~14 skills)
gsd install --profile=full            # current default (66 skills, 33 agents)
gsd install --profile=core,audit,ui   # composable feature tags
Dimension Assessment
User UX Familiar pattern (Cargo features, Helm --set, Ansible tags). Picker prompt on interactive install.
Implementation cost Low. install-profiles.cjs already does the staging. Extend MINIMAL_SKILL_ALLOWLIST into a map of profile → set, add a --profile arg parser, add interactive AskUserQuestion.
Dependency safety Need a manifest declaring each skill's required-skills set, so a profile can't ship an orphan caller. Add as CI lint.
Token savings High — standard cuts ~70% of descriptions; named clusters give users granular control.
Anthropic dependency None.

Pros: ships unilaterally, leverages existing seam, low blast radius. Cons: install-time only — users on a "full" install can't shrink without reinstall.

Option B — Runtime enable/disable command

A /gsd:surface (or gsd surface CLI) command that toggles which skills are visible to the runtime without touching installed files:

/gsd:surface list                # show enabled/disabled
/gsd:surface disable ui audit    # hide a cluster
/gsd:surface profile standard    # apply a named profile

Implementation: write enable/disable state to ~/.claude/skills/<name>/SKILL.md.disabled (rename) or maintain a gsd-surface.json manifest the installer reads on every update.

Dimension Assessment
User UX Discoverable through /gsd:help. Lower commit than reinstall. Mirrors VS Code's enable/disable extension UX.
Implementation cost Medium. Need persistent state separate from install files, plus a re-apply loop on gsd update.
Dependency safety Same manifest requirement as Option A — disabling phase should warn that 38 skills depend on it.
Token savings High — user-driven; can match Option A's savings.
Anthropic dependency None for the rename approach. Cleaner if Anthropic supports a SKILL.disabled convention natively.

Pros: in-session adjustable, no reinstall friction. Cons: state lives outside the installer's idempotent model, so gsd update migrations get more complex.

Option C — Further skill consolidation

Collapse semantically related skills into a single dispatcher with sub-modes:

gsd-phase           → keeps existing
gsd-milestone {new|complete|summary|audit}        # was 4 skills (hypothetical)
gsd-research  {sketch|spike|forensics|explore}    # was 4 skills (hypothetical)
gsd-workspace {pause|resume|capture|inbox|thread} # was 5 skills (hypothetical)

(The hypothetical dispatchers above are written without the slash prefix to signal they are not shipped commands — Option C is a sketch, not a recommendation.)

Dimension Assessment
User UX Breaking change for muscle-memorized slash commands; needs aliases for ≥1 release cycle.
Implementation cost Medium-high. Argument-parsing inside each dispatcher; migration of cross-references in 30+ skill bodies; aliases; CHANGELOG entries.
Dependency safety High — every cross-reference in existing skill bodies needs rewriting. The audit graph (3.1) is the migration spec.
Token savings Moderate — ~200-300 tokens by collapsing the 13-15 named skills above into 3 dispatchers.
Anthropic dependency None.

Pros: also improves IA — the slash-command surface becomes more discoverable. Cons: breaks user habits, doesn't compose with A/B (you still want profiles after consolidating).

Option D — Lazy / on-demand descriptions (Anthropic ask)

Skills ship a 1-line teaser in the system prompt and the full description loads only when the model expresses interest (analogous to ToolSearch for deferred tools). Cuts per-skill listing cost to ~10 chars.

Dimension Assessment
User UX Invisible to users.
Implementation cost Low for GSD — add a teaser: frontmatter field. High for Anthropic — harness changes.
Dependency safety N/A (purely about listing).
Token savings ~85% of all skill-listing budget across the ecosystem.
Anthropic dependency Yes — platform feature.

This is the architecturally correct long-term answer. GSD can't ship it alone.

Option E — Per-plugin budget allocation (Anthropic ask)

Instead of one shared skillListingBudgetFraction, give each plugin a dedicated quota (e.g. proportional to declared skills.count × ceiling). Eliminates one greedy plugin starving others.

Dimension Assessment
User UX Invisible.
Implementation cost Low for GSD — declare quota in package.json / plugin manifest. Medium for Anthropic — quota arithmetic and tie-breaking in the harness.
Token savings Doesn't reduce total, but eliminates the silent-drop failure mode.
Anthropic dependency Yes.

Option F — Sub-plugins / split distribution

Publish GSD as multiple npm packages: get-shit-done-redux-core, get-shit-done-redux-milestones, get-shit-done-redux-research, etc. Users install only what they need.

Dimension Assessment
User UX Reasonable for advanced users; confusing for first-time installers. Needs a meta-package (get-shit-done-redux) that depends on the slim ones — analogous to VS Code extension packs.
Implementation cost High. Multi-package build pipeline, version sync across packages, changelog routing, install-script forking.
Dependency safety npm semver carries the contract; cross-package refs become real require() calls.
Token savings Same as Option A in practice — token savings come from choosing not to install, not from the package boundary.
Anthropic dependency None.

Pros: clean separation, follows npm-ecosystem norms. Cons: very high lift for the same token savings Option A delivers.

5. Recommendation

Adopt Option A (named install profiles) as ADR-0010, with Option B (runtime surface toggle) as a Phase-2 amendment. File Options D and E to Anthropic as platform asks.

Why this ordering:

  1. A reuses an existing seam. install-profiles.cjs is already the staging point; this is the lowest-risk way to ship meaningful relief in the next release.
  2. A is composable. Naming clusters as profiles is a forcing function for the dependency manifest, which we want anyway for the lint described in 3.1.
  3. B follows A naturally. Once profiles exist, the /gsd:surface command is "apply a profile to a live install plus persist deltas." Without A, B has no profiles to apply.
  4. C is independent and orthogonal. It can happen in parallel as IA cleanup; it should not block A.
  5. D and E are platform-level. GSD ships A regardless; D/E are documented as cooperative asks so Anthropic sees them in context.

6. Anthropic platform asks

Drafted for filing at https://docs.claude.com/feedback or similar channel; copy unchanged into the issue or feedback form.

Ask 1 — Native lazy skill descriptions

Skills currently emit name + description into every system prompt. For large plugin ecosystems (135+ skills on power-user installs) this overruns skillListingBudgetFraction and silently drops skills. Proposal: add a frontmatter teaser: field (≤40 chars) that ships in the listing, with the full description: loaded only when the model requests it (analogous to ToolSearch for deferred tools). Backwards-compatible: skills without teaser: keep current behavior.

Ask 2 — Per-plugin budget allocation

A single skillListingBudgetFraction shared across all installed plugins causes silent truncation when one plugin's skill set is large. Proposal: each plugin declares a soft quota in its manifest; the harness arbitrates fairness when total demand exceeds budget (e.g. proportional shrink, with a documented order — most-recently-installed last to be cut). Surface drops in /doctor output today; do not drop silently.

Ask 3 — Dependency-aware skill listing

Skills can call other skills (in GSD, the phase skill is referenced by 38 others). When the harness drops a skill from the listing, it has no way to know whether anything else relies on it. Proposal: optional frontmatter requires: [other-skill] so the harness keeps the closure of dependencies in the listing, or warns the user at install time that a dropped skill is reachable from a kept one.

Ask 4 — Disable/enable without uninstall

Today the only way to remove a skill from the listing is to delete its SKILL.md. Proposal: a .disabled suffix (e.g. SKILL.md.disabled) or a per-skill enabled: false frontmatter is treated as "not surfaced" by the harness. This lets plugins ship surface-toggle UIs (like our proposed /gsd:surface disable) without touching install state.

7. Implementation sketch (for the ADR)

Phase 1 — profiles (ships with ADR-0010):

  1. In gsd-core/bin/lib/install-profiles.cjs, replace the single MINIMAL_SKILL_ALLOWLIST constant with a PROFILES map. Each profile is the transitive closure over a base set, so standard includes core automatically.
  2. Add a requires: frontmatter field to every skill that calls another skill in its body. Add a lint check in scripts/lint-descriptions.cjs (or a sibling lint-skill-deps.cjs) that fails CI if a skill body references another skill that isn't in its requires list, and that fails if any profile would ship a skill whose requires aren't satisfied.
  3. Extend the bin/install.js argument parser: --profile=<name> (mutually exclusive with --minimal), --profile=core,audit for composition. Keep --minimal as an alias for --profile=core.
  4. Interactive install: if no --profile is given and no runtime/location is forced, present an AskUserQuestion-style picker. (Cowork analog already in the install flow.)
  5. gsd update re-applies the recorded profile from a small marker file (~/.claude/skills/.gsd-profile).

Phase 2 — runtime surface command (follow-up ADR or amendment):

  1. /gsd:surface command writes to the profile marker and re-runs the staging step for the active runtime.
  2. Once Anthropic ships Ask 4, switch from file-deletion to .disabled-suffix toggling.

8. Follow-ups outside this scope

  • Stale comment in install-profiles.cjs. The module-level header cites "86 skills + 33 agents" producing ~12k tokens. The audited count is 66 + 33 — a previous consolidation pass already happened. Update the comment in a parallel cleanup commit when ADR-0010 lands.
  • Audit JSON refresh. docs/research/data/2026-05-12-skill-audit.json is a one-shot snapshot. If we want it to stay current, wire the extraction script into scripts/ and run it on lint:skill-deps. Not a blocker.

9. Risks and unknowns

  • The phase dispatcher gap in the existing minimal allowlist. Confirm whether a fresh --minimal install + the documented main loop actually works end-to-end. If discuss-phase/plan-phase/execute-phase silently fall back to /gsd:phase, the minimal allowlist is currently broken. Track as a separate bug if confirmed.
  • Profile naming bikeshed. core / standard / full vs. minimal / recommended / everything vs. functional names (planning, audit, research). Settle in the ADR's Open Questions.
  • Discoverability of disabled skills. If gsd:audit-fix isn't surfaced, a user asking "audit my project" won't get it suggested. /gsd:help should list installed-but-not-surfaced skills with a one-line upgrade hint.
  • Telemetry blind spot. GSD doesn't currently know which skills users invoke, so "drop the long tail" is theoretical. Survey or self-reporting may be needed before drawing the standard profile line.

10. References

  • Issue: #3408
  • Existing seam: gsd-core/bin/lib/install-profiles.cjs
  • Description lint: scripts/lint-descriptions.cjs
  • Install dispatcher: bin/install.js:123 (mode parsing), bin/install.js:8167-8207 (minimal staging)
  • Audit data: docs/research/data/2026-05-12-skill-audit.json (per-skill dep graph, description sizes, and cluster mapping — reproducible from commands/gsd/ and agents/)
  • Prior ADRs: 0008 (Installer Migration Module) and 0009 (Shell Command Projection Module) — both touch the same install pipeline this proposal extends.
  • Ecosystem precedents: Cargo [features], npm optionalDependencies, VS Code extension packs, Homebrew taps, Helm chart values, Ansible role tags, Linux kernel make menuconfig tristate, systemd target activation.