* enhance(#4406): the lazily-read remainder and the artifact templates ADR-4139 Decision 3, Phase 6 of the #4139 Compact Content epic. Covers stream 1b (gsd-core/workflows/<name>/{modes,steps,templates}/*.md) and stream 4 (gsd-core/templates/**) with a variant-swap mechanism, confirmed with the user: two independent, complete files per covered path (canonical + .compact.md sibling), with the gate picking which one gets Read at the call site. This is a different shape from Phase 5's spine+detail partition, and is safe here specifically because these files are already reached only by a runtime Read — a missed Read already means zero overlay content today, with or without workflow.compact_content, so selecting between two independently-complete files introduces no new failure mode (documented in gsd-core/references/compact-content-gate.md's new "Streams 1b and 4" section). Disposition, after inspecting every candidate rather than trusting a byte-size threshold (same rigor Phase 5 applied to review.md): - Stream 1b: 1 of 78 files compacted (help/modes/full.md, a user-facing reference doc emitted verbatim, not orchestrator instruction). The other 9 size-threshold candidates are dominated by fail-closed guards, exact CLI invocations, or output-format contracts (AskUserQuestion blocks) — recorded not-worth-compacting, same reasoning as Phase 5's review.md. - Stream 4: a ground-truth reachability audit replaced the initial size-only candidate list. Two files (summary.md, user-setup.md) got compact variants; a third (spec.md) was drafted, then dropped after discovering its only two call sites are eager @-includes, not a runtime Read — stream-1 material hiding under gsd-core/templates/, not stream-4's actual mechanism. summary.md itself has 3 eager call sites and only 1 genuine runtime-Read call site (execute-plan.md); only that one was wired, so the compact variant's savings apply to the sequential single-plan execution path only. - Discovered while auditing reachability: 12 gsd-core/templates/** files with zero references anywhere in workflow/agent/command prose, compiled source, or tests — dead scaffolding predating this phase. Deleted in this same PR per this repo's no-defer policy, after re-verifying against a computed path.join(...) pattern (not just a plain-string search) that nearly caused two genuinely load-bearing templates (user-profile.md, dev-preferences.md) to be misclassified as dead. New checker (tests/helpers/compact-content-variant.cjs): registration, reachability, protected-content-preserved, size-smaller — replacing Phase 3/5's disjointness/completeness checks, which assume a partition rather than two deliberately-overlapping documents. The reachability check's own "unprefixed match" guard had a real bug (rejected the repo's own `~/.claude/gsd-core/...` convention), caught by running it against the already-wired help/modes/full.compact.md pair rather than only synthetic fixtures — fixed to anchor on the nearest `gsd-core` path segment instead. Template consumer parity (tests/compact-content-template-variant-parity.test.cjs): proves each compact variant's `## File Template` fenced block — the actual output-format contract a generated SUMMARY.md/USER-SETUP.md is parsed against — is byte-identical to the canonical file, then runs the one real deterministic consumer (gsd-core/bin/lib/coverage.cjs's classifyContent, backing `gsd-tools uat classify-coverage`) against content built from that shared contract. Added a sibling benchmark script (scripts/benchmark-compact-content-variants.cjs) rather than extending the existing spine/detail one — different data shape, and the existing script's own contract deliberately isolates it from a test-only helper's shape changing. Emitted-drift acknowledgement: not needed. Every changed/added path in this diff is hand-authored and present in the diff itself, so diffEmitted's attribution loop resolves `via` to the path's own source before reaching the ack-lookup branch (same reasoning Phase 5 verified for its own diff). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * enhance(#4406): address code-review findings on the variant-swap gate - docs/CONFIGURATION.md and gsd-core/references/planning-config.md's workflow.compact_content entries described only the spine+detail mechanism (Phase 5) and were missing this phase's variant-swap mechanism and its benchmark:compact-content-variants script entirely — required since this PR's changeset is type Added (CLAUDE.md's "Missing Docs for Changesets" rule). Both now describe both mechanisms and which call sites are wired. - Added the missing RED^-1/no-op fixture for checkProtectedContentPreserved: a canonical file with zero <!-- gsd:protected --> blocks must be a no-op, not a violation — the only branch of that function the existing fixtures didn't exercise. - Collapsed findCompactFiles/findMarkdownFiles in tests/helpers/compact-content-variant.cjs into one findFilesWithSuffix helper — the two were identical recursive walks differing only in the extension predicate (minor Duplicated-Code finding). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(#4406): restore copilot-instructions.md, a false-positive dead-template classification gsd-test caught this, not static analysis: 10 real failures in tests/copilot-install.test.cjs, tests/installer-migration-install.integration.test.cjs, and tests/repo-layout.test.cjs — all downstream of bin/install.js's Copilot install path, which does fs.readFileSync(path.join(targetDir, 'gsd-core', 'templates', 'copilot-instructions.md')) after copying gsd-core/templates/** into the target project, then merges it into both .github/copilot-instructions.md and (local installs) AGENTS.md. The reachability audit that flagged this file as dead checked src/*.cts and gsd-core/bin/*.cjs but never the repo-root bin/install.js — a separately maintained installer bundle outside the src/-to-gsd-core/bin/lib/ compiled-output convention. The fs.existsSync guard around that read degrades to a silent skip rather than a crash when the template is missing, which is why this surfaced only once the real E2E install test ran, not from any static check. Re-verified the remaining 11 deleted filenames against bin/install.js specifically (plain substring and quoted-filename search) before trusting that list — all 11 have zero hits there, confirmed dead by the same standard this one file failed. Regenerated the installer emitted-tree goldens (tests/fixtures/install-tree/*.json) to reflect the restored file, and corrected the "Removed" changeset (jolly-lynx-sprint.md) and the phase design doc from 12 to 11 deleted files. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Emitted-Drift-Ack-Growth: execute-plan.md — call-site wiring for the summary.md and user-setup.md .compact.md variants Emitted-Drift-Ack-Growth: help.md — call-site wiring for full.compact.md, same variant-resolution rule * docs(#4406): backfill changeset PR numbers Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(#4406): resolve removed-but-needed lint findings on the dead-template deletion CI's own full-test matrix (not gsd-test's matrix, which does not run this check) caught 4 more false-positive dead-template classifications via tests/removed-but-needed-lint.test.cjs / scripts/lint-removed-but-needed.cjs — a literal, word-boundary basename check across .github/workflows/, gsd-core/, and docs/ (excluding docs/adr/** and docs/research/**) for every file a PR deletes. It has no semantic awareness, so a deleted template's basename colliding with something else entirely still fires: - claude-md.md: gsd-core/templates/README.md had a stale table row claiming /gsd-profile reads this template to generate CLAUDE.md. Verified false (no code reads it anywhere, same search that already covered bin/install.js) — fixed the row to *(inline)*, matching every other command-generated artifact in that table. File stays deleted. - codebase/testing.md: collided with docs/guides/testing.md, an illustrative example row in docs-update.md's sample output table (an unrelated real generated-docs path). Swapped the example topic to "contributing" — the row is illustrative, any topic works. File stays deleted. - codebase/architecture.md, codebase/stack.md: collided with docs/reference/ planning-artifacts.md's directory listing of a user's own generated .planning/codebase/architecture.md and stack.md output — the same semantic mismatch already investigated and dismissed as unrelated earlier in this phase's audit, now caught by a gate instead of judgment. That listing repeats across 5 locale copies of the doc. - continue-here.md: collided with the real .continue-here.md pause-work artifact, referenced across 15+ locale and workflow files. For the last two, the lint's own error message offers "restore the file or update every consumer in the same commit." Rewording 15+ files across languages I cannot verify translation quality for, to shave 2 already-tiny templates that were merely presumed dead, is disproportionate to this PR's actual scope — restored codebase/architecture.md, codebase/stack.md, and continue-here.md instead, and corrected docs/ARCHITECTURE.md's Templates section accordingly. Final confirmed-dead set: claude-md.md, codebase/concerns.md, codebase/conventions.md, codebase/integrations.md, codebase/structure.md, codebase/testing.md, debug-subagent-prompt.md, discovery.md — 8 files, down from the original 12. Verified locally: GSD_REMOVED_BUT_NEEDED_BASE=next node scripts/lint-removed-but-needed.cjs now passes clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Emitted-Drift-Ack-Growth: docs-update.md — swapped an illustrative example-table topic (testing -> contributing) to avoid a removed-but-needed basename collision with the deleted codebase/testing.md template; net +10 bytes * fix(#4406): split codex-config.test.cjs to fix a genuine Windows CI timeout Root cause of the `full test (windows-latest, 24, shard 2/3)` failure the user asked to be actually fixed, not just re-run past: PR #4497 (landed 2026-09-07, one day before this PR's CI run) isolated tests/codex-config.test.cjs into its own dedicated chunk because its measured weight (17.87, ~45% of the post-cut Windows budget) made it unsafe to share a chunk with any other file. That isolation was necessary but not sufficient — even alone, with zero companion-file contention, the file's real Windows execution time sits right at the 600s per-chunk ceiling. Two independent CI runs on two unrelated PRs (this one and #4154) were both killed within ~1.4s of the identical 600000ms mark — not random contention, a deterministic near-miss the isolation fix couldn't address because it never reduced the file's own cost, only removed the risk of a companion file's cost stacking on top of it (which the PR #4497 comment explicitly anticipated: "if a future profiling pass genuinely speeds up codex-config.test.cjs itself, this isolation can be revisited"). The file itself explains why it's this heavy: 11,262 lines / 433 tests / 79 describe blocks, accumulated over dozens of bug-fix PRs (#2695, #2760, #3245, #3285, #3346, #3426, #3427, #3562, #3566, #3582, #3808, and more), several of which are explicitly documented as "folded" in from separate files that were never actually split back out ("Verified non-duplicate against both the pre-existing target and the other three folded sources"). Split into 4 files by top-level AST statement boundaries (never a naive column-0 regex — an early attempt at that overcounted 79 apparent "describe(" matches when only 21 are genuinely top-level; the rest are nested inside a handful of large folded-in blocks, which a regex can't tell apart from real top-level statements). Verified lossless twice: the split script asserts byte-for-byte reconstruction of every source character, and independently, total test()/describe() call counts match exactly between the original file and the sum across all 4 new files (433/79 both sides). Each new file carries the complete original shared header (imports/helpers) for safety; per-file unused-import warnings from that duplication are resolved via ESLint-precise alias renames (`{ foo: _foo }`, the standard form for an intentionally-unused destructured binding — never a bare `{ _foo }`, which would destructure a different, nonexistent property). No change needed to scripts/run-tests.cjs's ISOLATED_HEAVY_FILES or its pinned test in tests/run-tests-harness.test.cjs: the file that keeps the original name (tests/codex-config.test.cjs) is now only ~28% of the original's size and safely isolated in its own chunk as before; the other three new files re-enter normal weight-balanced packing, none individually close to disproportionate. Confirmed no other file hardcodes the hardcoded filename anywhere that would silently stop these tests from running (the CI test-selection scripts determine scope algorithmically, not by literal filename). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: sim <sim@local> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
5
.changeset/jolly-lynx-sprint.md
Normal file
5
.changeset/jolly-lynx-sprint.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
type: Removed
|
||||||
|
pr: 4540
|
||||||
|
---
|
||||||
|
**Removed 8 unreferenced planning-artifact scaffolding templates under `gsd-core/templates/`** (`claude-md.md`, four of the seven `codebase/` brownfield-mapping templates — `concerns.md`, `conventions.md`, `integrations.md`, `structure.md` — plus `debug-subagent-prompt.md` and `discovery.md`) — confirmed, file by file, to have zero references anywhere in workflow prose, agent/command definitions, compiled source, or tests, and (for the deleted set specifically) no surviving basename reference anywhere in the tree either. `codebase/architecture.md`, `codebase/stack.md`, and `continue-here.md` were kept: their basenames collide with unrelated, genuinely live concepts documented across many files (a user's generated `.planning/codebase/*.md` output, and the real `.continue-here.md` pause-work artifact), so deleting them would have required rewording numerous translated docs to describe something else entirely.
|
||||||
5
.changeset/patient-moles-click.md
Normal file
5
.changeset/patient-moles-click.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
type: Added
|
||||||
|
pr: 4540
|
||||||
|
---
|
||||||
|
**`workflow.compact_content` now also covers lazily-read workflow fragments and planning-artifact templates.** With the key on, `help --full`'s reference doc and generated `SUMMARY.md`/`USER-SETUP.md` templates resolve to a terser `.compact.md` sibling at the point of their existing `Read` — two independent, complete files, picked per the same shared gate Phase 5 introduced (`gsd-core/references/compact-content-gate.md`). With the key off (default), nothing changes.
|
||||||
@@ -277,7 +277,7 @@ Markdown templates for all planning artifacts. Used by `gsd-tools.cjs template f
|
|||||||
- `DEBUG.md` — Debug session tracking template
|
- `DEBUG.md` — Debug session tracking template
|
||||||
- `UI-SPEC.md`, `UAT.md`, `VALIDATION.md` — Specialized verification templates
|
- `UI-SPEC.md`, `UAT.md`, `VALIDATION.md` — Specialized verification templates
|
||||||
- `discussion-log.md` — Discussion audit trail template
|
- `discussion-log.md` — Discussion audit trail template
|
||||||
- `codebase/` — Brownfield mapping templates (stack, architecture, conventions, concerns, structure, testing, integrations)
|
- `codebase/` — Brownfield mapping templates (architecture, stack)
|
||||||
- `research-project/` — Research output templates (SUMMARY, STACK, FEATURES, ARCHITECTURE, PITFALLS)
|
- `research-project/` — Research output templates (SUMMARY, STACK, FEATURES, ARCHITECTURE, PITFALLS)
|
||||||
|
|
||||||
### Hooks (`hooks/`)
|
### Hooks (`hooks/`)
|
||||||
|
|||||||
@@ -517,7 +517,7 @@ All workflow toggles follow the **absent = enabled** pattern. If a key is missin
|
|||||||
| `workflow.text_mode` | boolean | `false` | Replaces AskUserQuestion TUI menus with plain-text numbered lists. Required for Claude Code remote sessions (`/rc` mode) where TUI menus don't render. Can also be set per-session with `--text` flag on discuss-phase. Added in v1.28 |
|
| `workflow.text_mode` | boolean | `false` | Replaces AskUserQuestion TUI menus with plain-text numbered lists. Required for Claude Code remote sessions (`/rc` mode) where TUI menus don't render. Can also be set per-session with `--text` flag on discuss-phase. Added in v1.28 |
|
||||||
| `workflow.use_worktrees` | boolean | `true` | When `false`, disables git worktree isolation for parallel execution. Users who prefer sequential execution or whose environment does not support worktrees can disable this. Added in v1.31. **Branch-divergence note:** when your branch has diverged from `origin/HEAD`, GSD auto-degrades to sequential and prints a warning. See [`worktree.baseRef`](#worktree-settings) to restore parallel execution on a diverged branch. **Per-runtime note:** whether this key can be honored depends on the runtime's declared `dispatch.isolation` capability, not on its name (#2584). Runtimes whose own harness isolates each executor (**Claude Code**, **Cursor**) run parallel worktrees natively; runtimes exposing a headless exec with an explicit working directory (**Codex**, **OpenCode**, **Kimi**, **Kimi Code**) get worktrees GSD itself creates and merges — where a dispatch site can only drive the harness model, those hosts degrade to sequential with a warning rather than aborting. Every other runtime declares no isolation primitive, and forcing `use_worktrees: true` there still fails closed before any executor dispatch. `/gsd-health` reports such a value as warning `W025` (#2486). **Default on a non-Claude install:** if a worktree-capable non-Claude host is not isolating as described above, check whether the install stamped this key's default to `false` and set an explicit `use_worktrees: true`. See [Executor isolation per runtime](#executor-isolation-per-runtime). |
|
| `workflow.use_worktrees` | boolean | `true` | When `false`, disables git worktree isolation for parallel execution. Users who prefer sequential execution or whose environment does not support worktrees can disable this. Added in v1.31. **Branch-divergence note:** when your branch has diverged from `origin/HEAD`, GSD auto-degrades to sequential and prints a warning. See [`worktree.baseRef`](#worktree-settings) to restore parallel execution on a diverged branch. **Per-runtime note:** whether this key can be honored depends on the runtime's declared `dispatch.isolation` capability, not on its name (#2584). Runtimes whose own harness isolates each executor (**Claude Code**, **Cursor**) run parallel worktrees natively; runtimes exposing a headless exec with an explicit working directory (**Codex**, **OpenCode**, **Kimi**, **Kimi Code**) get worktrees GSD itself creates and merges — where a dispatch site can only drive the harness model, those hosts degrade to sequential with a warning rather than aborting. Every other runtime declares no isolation primitive, and forcing `use_worktrees: true` there still fails closed before any executor dispatch. `/gsd-health` reports such a value as warning `W025` (#2486). **Default on a non-Claude install:** if a worktree-capable non-Claude host is not isolating as described above, check whether the install stamped this key's default to `false` and set an explicit `use_worktrees: true`. See [Executor isolation per runtime](#executor-isolation-per-runtime). |
|
||||||
| `workflow.agent_hint_routing` | boolean | `true` | Per-plan specialist executor routing (#1689). When `true`, a plan whose `agent_hint:` frontmatter names a subagent that resolves on the active runtime is dispatched to that specialist instead of `gsd-executor`. Default `true` — a no-op for plans without `agent_hint:`, so existing dispatch is unchanged. Set `false` to disable. See [PLAN.md `agent_hint`](reference/plan-md.md#per-plan-executor-routing). |
|
| `workflow.agent_hint_routing` | boolean | `true` | Per-plan specialist executor routing (#1689). When `true`, a plan whose `agent_hint:` frontmatter names a subagent that resolves on the active runtime is dispatched to that specialist instead of `gsd-executor`. Default `true` — a no-op for plans without `agent_hint:`, so existing dispatch is unchanged. Set `false` to disable. See [PLAN.md `agent_hint`](reference/plan-md.md#per-plan-executor-routing). |
|
||||||
| `workflow.compact_content` | boolean | `false` | Compact content mode (#4139, [ADR-4139](adr/4139-compact-content-seam.md)). Per-project boolean selecting the terser form of GSD's own shipped prompt content (workflows, templates, agent-skill payloads). Six workflows branch on it today — `plan-phase` (#4402, the pilot), `execute-phase`, `docs-update`, `new-project`, `verify-work`, and `complete-milestone` (#4405) — each split into a spine plus a deferred `<workflow>/detail/*.md` elaboration: with the key off, the spine reads its own elaboration back in before continuing (byte-identical instruction set to before); with it on, that read is skipped. The remaining eagerly-`@`-included workflows were reviewed and recorded as not worth splitting (see `docs/PARTITION-RULES.md` § "Deciding whether a file is worth splitting") — either their size comes from safety-critical orchestration logic rather than deferrable narrative (`review.md`), or they're small enough that a split's fixed structural overhead would exceed the savings. The eager-window token reduction each split actually achieves is measured, not asserted: `npm run benchmark:compact-content` reports per-split and aggregate on/off token counts (a proxy-tokenizer delta — Anthropic publishes no tokenizer for Claude 3+, so the comparison is exact under a pinned tokenizer even though the absolute counts are not Claude's real ones) against a committed baseline (`tests/fixtures/compact-content-benchmark-baseline.json`, #4404). Reporting-only — it never fails CI. |
|
| `workflow.compact_content` | boolean | `false` | Compact content mode (#4139, [ADR-4139](adr/4139-compact-content-seam.md)). Per-project boolean selecting the terser form of GSD's own shipped prompt content (workflows, templates, agent-skill payloads). Two mechanisms exist, chosen per stream. **Spine + detail** (top-level, eagerly-`@`-included workflows): six workflows branch on it today — `plan-phase` (#4402, the pilot), `execute-phase`, `docs-update`, `new-project`, `verify-work`, and `complete-milestone` (#4405) — each split into a spine plus a deferred `<workflow>/detail/*.md` elaboration: with the key off, the spine reads its own elaboration back in before continuing (byte-identical instruction set to before); with it on, that read is skipped. The remaining eagerly-`@`-included workflows were reviewed and recorded as not worth splitting (see `docs/PARTITION-RULES.md` § "Deciding whether a file is worth splitting") — either their size comes from safety-critical orchestration logic rather than deferrable narrative (`review.md`), or they're small enough that a split's fixed structural overhead would exceed the savings. **Variant swap** (#4406 — lazily-`Read` workflow subdirectory files and `gsd-core/templates/**` planning-artifact templates, which have no eager window to shrink): a `.compact.md` sibling next to the canonical file, resolved at the point of the existing `Read` per `gsd-core/references/compact-content-gate.md` § "Streams 1b and 4". Three call sites are wired today — `help --full`'s reference doc (`gsd-core/workflows/help/modes/full.md`) and the sequential-execution `SUMMARY.md`/`USER-SETUP.md` template reads in `execute-plan.md` — after a per-candidate reachability audit found most other size-based candidates were either genuinely unreferenced (deleted), reached only through an eager `@`-include or orchestrator build-time embed (left unconverted, same reasoning as the eagerly-included workflows above), or consumed only by a test fixture or a parser's documented grammar rather than a runtime `Read`. The token reduction each mechanism actually achieves is measured, not asserted: `npm run benchmark:compact-content` (spine/detail) and `npm run benchmark:compact-content-variants` (variant-swap) each report per-item and aggregate on/off token counts (a proxy-tokenizer delta — Anthropic publishes no tokenizer for Claude 3+, so the comparison is exact under a pinned tokenizer even though the absolute counts are not Claude's real ones) against their own committed baseline (`tests/fixtures/compact-content-benchmark-baseline.json`, #4404; `tests/fixtures/compact-content-variant-benchmark-baseline.json`, #4406). Both are reporting-only — neither ever fails CI. |
|
||||||
| `workflow.worktree_skip_hooks` | boolean | `false` | When `true`, executor agents in worktree mode pass `--no-verify` (skipping pre-commit hooks) and post-wave hook validation runs against the merged result instead. Opt-in escape hatch for projects whose hooks cannot run in agent worktrees. Default `false` runs hooks on every commit (#2924). |
|
| `workflow.worktree_skip_hooks` | boolean | `false` | When `true`, executor agents in worktree mode pass `--no-verify` (skipping pre-commit hooks) and post-wave hook validation runs against the merged result instead. Opt-in escape hatch for projects whose hooks cannot run in agent worktrees. Default `false` runs hooks on every commit (#2924). |
|
||||||
| `workflow.code_review` | boolean | `true` | Enable `/gsd-code-review` and `/gsd-code-review --fix` commands. When `false`, the commands exit with a configuration gate message. Added in v1.34 |
|
| `workflow.code_review` | boolean | `true` | Enable `/gsd-code-review` and `/gsd-code-review --fix` commands. When `false`, the commands exit with a configuration gate message. Added in v1.34 |
|
||||||
| `workflow.code_review_point` | string | `execute:post` | Loop point at which the code-review capability's step registers: `execute:post` reviews once, after every wave in a phase has landed (default — unchanged behavior); `execute:wave:post` reviews once per completed wave instead, scoped to what changed since the phase's prior review (the whole phase's diff on the first wave, each subsequent wave's own diff thereafter). Manual `/gsd-code-review <phase>` invocation is unaffected by this key — it is gated by `workflow.code_review` alone and runs regardless of which point is configured. `/gsd-autonomous` and `/gsd-quick` have no wave granularity of their own, so setting this to `execute:wave:post` means code review does not run automatically inside those two flows (consistent with how every other `execute:wave:post`-only capability already behaves for them). Added in #3661 |
|
| `workflow.code_review_point` | string | `execute:post` | Loop point at which the code-review capability's step registers: `execute:post` reviews once, after every wave in a phase has landed (default — unchanged behavior); `execute:wave:post` reviews once per completed wave instead, scoped to what changed since the phase's prior review (the whole phase's diff on the first wave, each subsequent wave's own diff thereafter). Manual `/gsd-code-review <phase>` invocation is unaffected by this key — it is gated by `workflow.code_review` alone and runs regardless of which point is configured. `/gsd-autonomous` and `/gsd-quick` have no wave granularity of their own, so setting this to `execute:wave:post` means code review does not run automatically inside those two flows (consistent with how every other `execute:wave:post`-only capability already behaves for them). Added in #3661 |
|
||||||
|
|||||||
@@ -603,6 +603,7 @@
|
|||||||
"discuss-phase/modes/text.md",
|
"discuss-phase/modes/text.md",
|
||||||
"help/modes/brief.md",
|
"help/modes/brief.md",
|
||||||
"help/modes/default.md",
|
"help/modes/default.md",
|
||||||
|
"help/modes/full.compact.md",
|
||||||
"help/modes/full.md",
|
"help/modes/full.md",
|
||||||
"help/modes/topic.md"
|
"help/modes/topic.md"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
# Compact Content Gate
|
# Compact Content Gate
|
||||||
|
|
||||||
Shared by every workflow spine split under ADR-4139. States the config check and the resolution rule once — a spine references this file; it never restates the check inline.
|
Shared by every workflow spine split under ADR-4139, and by every lazily-read fragment or
|
||||||
|
planning-artifact template given a compact variant under Phase 6 (#4406). States the config check
|
||||||
|
and both resolution rules once — a spine or fragment references this file; it never restates
|
||||||
|
either check inline.
|
||||||
|
|
||||||
## The check
|
## The check
|
||||||
|
|
||||||
@@ -8,11 +11,40 @@ Shared by every workflow spine split under ADR-4139. States the config check and
|
|||||||
COMPACT_CONTENT=$(gsd_run query config-get workflow.compact_content --raw 2>/dev/null || echo "false")
|
COMPACT_CONTENT=$(gsd_run query config-get workflow.compact_content --raw 2>/dev/null || echo "false")
|
||||||
```
|
```
|
||||||
|
|
||||||
## The resolution rule
|
## Stream 1 — spine + detail (top-level, eagerly `@`-included workflows)
|
||||||
|
|
||||||
- **`COMPACT_CONTENT` is `"false"` (default):** Read every part under this workflow's own `detail/` directory (a sibling of this spine, e.g. `gsd-core/workflows/<name>/detail/*.md`) now, in full, before continuing past this point. Their content elaborates on the spine you are reading — treat everything they say as part of this document from here on.
|
- **`COMPACT_CONTENT` is `"false"` (default):** Read every part under this workflow's own `detail/` directory (a sibling of this spine, e.g. `gsd-core/workflows/<name>/detail/*.md`) now, in full, before continuing past this point. Their content elaborates on the spine you are reading — treat everything they say as part of this document from here on.
|
||||||
- **`COMPACT_CONTENT` is `"true"`:** Do not read the detail file. Continue directly with the spine's own content — per ADR-4139 Decision 3, it is complete enough to run this workflow correctly on its own.
|
- **`COMPACT_CONTENT` is `"true"`:** Do not read the detail file. Continue directly with the spine's own content — per ADR-4139 Decision 3, it is complete enough to run this workflow correctly on its own.
|
||||||
|
|
||||||
## The fail-safe this exists to hold (ADR-4139 Decision 4)
|
**The fail-safe this holds (ADR-4139 Decision 4):** A `Read` that does not fire for any reason (tool error, a skipped step, a misread condition) leaves you running on the spine alone. That is the same, correct, terser state an opted-in project runs in on purpose — never a state with no instructions. The spine's own completeness is what makes this safe; this gate is only ever additive.
|
||||||
|
|
||||||
A `Read` that does not fire for any reason (tool error, a skipped step, a misread condition) leaves you running on the spine alone. That is the same, correct, terser state an opted-in project runs in on purpose — never a state with no instructions. The spine's own completeness is what makes this safe; this gate is only ever additive.
|
## Streams 1b and 4 — variant resolution (lazily-read fragments and planning-artifact templates)
|
||||||
|
|
||||||
|
For a `workflows/<name>/{modes,steps,templates}/*.md` fragment or a `gsd-core/templates/**`
|
||||||
|
planning-artifact template that has a registered `.compact.md` sibling (same directory, same stem,
|
||||||
|
`.compact.md` suffix):
|
||||||
|
|
||||||
|
- **`COMPACT_CONTENT` is `"false"` (default), or the file has no registered `.compact.md` sibling:** Read the canonical path exactly as named — unchanged from today.
|
||||||
|
- **`COMPACT_CONTENT` is `"true"` and a `.compact.md` sibling is registered:** Read the `.compact.md` sibling instead of the canonical path.
|
||||||
|
|
||||||
|
Both files are complete, independently — Read exactly one, never both, and never read the compact
|
||||||
|
sibling's content as an addendum to the canonical file.
|
||||||
|
|
||||||
|
**The fail-safe this holds:** unlike stream 1, a call site this rule actually applies to is already
|
||||||
|
reached only by a runtime `Read` — a missed `Read` already means zero overlay content, with or
|
||||||
|
without `workflow.compact_content`. Selecting between two independently-complete files at that
|
||||||
|
call site does not introduce a new way to end up with nothing; the worst case is identical to
|
||||||
|
today's. This is why stream 1b/4 can use variant-swap (two independent files) where stream 1 could
|
||||||
|
not: the degradation-direction argument that ruled out converting stream 1's `@`-includes (ADR-4139
|
||||||
|
Decision 4) does not apply at a genuine runtime-`Read` call site, because there is no
|
||||||
|
host-guaranteed baseline being traded away there.
|
||||||
|
|
||||||
|
**This rule is scoped per call site, not per file.** A `gsd-core/templates/**` file can have both
|
||||||
|
kinds of reference in the corpus at once — some places name it inside an eager `@`-include or an
|
||||||
|
orchestrator build-time embed (the same mechanism as stream 1, just reaching a template path
|
||||||
|
instead of a workflow path), others name it in prose instructing a runtime `Read`. Only the latter
|
||||||
|
gets rewritten to point at this rule; an eager reference to the canonical file is left exactly as
|
||||||
|
it is, for the same reason stream 1's `@`-includes were left alone — converting it would trade a
|
||||||
|
host-guaranteed load for a conditional one. Before wiring any call site, confirm by inspection
|
||||||
|
which kind it is; do not assume every mention of a `gsd-core/templates/**` path is a runtime `Read`
|
||||||
|
just because the directory's typical case is.
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ Set via `workflow.*` namespace in config.json (e.g., `"workflow": { "research":
|
|||||||
| `workflow.ui_phase` | boolean | `true` | `true`, `false` | Generate UI-SPEC.md for frontend phases |
|
| `workflow.ui_phase` | boolean | `true` | `true`, `false` | Generate UI-SPEC.md for frontend phases |
|
||||||
| `workflow.ui_safety_gate` | boolean | `true` | `true`, `false` | Require safety gate approval for UI changes |
|
| `workflow.ui_safety_gate` | boolean | `true` | `true`, `false` | Require safety gate approval for UI changes |
|
||||||
| `workflow.text_mode` | boolean | `false` | `true`, `false` | Use plain-text numbered lists instead of AskUserQuestion menus |
|
| `workflow.text_mode` | boolean | `false` | `true`, `false` | Use plain-text numbered lists instead of AskUserQuestion menus |
|
||||||
| `workflow.compact_content` | boolean | `false` | `true`, `false` | Compact content mode (#4139, ADR-4139) — per-project boolean selecting terser payloads. Six workflows branch on it: `plan-phase` (#4402, pilot), `execute-phase`, `docs-update`, `new-project`, `verify-work`, `complete-milestone` (#4405). The rest of the eager-window corpus was reviewed and recorded as not worth splitting (`docs/PARTITION-RULES.md`) |
|
| `workflow.compact_content` | boolean | `false` | `true`, `false` | Compact content mode (#4139, ADR-4139) — per-project boolean selecting terser payloads. Six workflows branch on it via spine+detail: `plan-phase` (#4402, pilot), `execute-phase`, `docs-update`, `new-project`, `verify-work`, `complete-milestone` (#4405). The rest of the eager-window corpus was reviewed and recorded as not worth splitting (`docs/PARTITION-RULES.md`). Lazily-`Read` workflow fragments and `gsd-core/templates/**` templates use a `.compact.md` sibling instead (#4406, `gsd-core/references/compact-content-gate.md` § "Streams 1b and 4") — wired today for `help --full` and the sequential-execution `SUMMARY.md`/`USER-SETUP.md` reads |
|
||||||
| `workflow.research_before_questions` | boolean | `false` | `true`, `false` | Run research before interactive questions in discuss phase (also honored on the `/gsd:quick` path, #3894). _Alias:_ `research_before_questions` is the flat-key form used in `CONFIG_DEFAULTS`; `workflow.research_before_questions` is the canonical namespaced form. |
|
| `workflow.research_before_questions` | boolean | `false` | `true`, `false` | Run research before interactive questions in discuss phase (also honored on the `/gsd:quick` path, #3894). _Alias:_ `research_before_questions` is the flat-key form used in `CONFIG_DEFAULTS`; `workflow.research_before_questions` is the canonical namespaced form. |
|
||||||
| `workflow.discuss_mode` | string | `"discuss"` | `"discuss"`, `"assumptions"` | Default mode for discuss-phase: `"discuss"` runs interactive questioning; `"assumptions"` analyzes codebase and surfaces assumptions instead |
|
| `workflow.discuss_mode` | string | `"discuss"` | `"discuss"`, `"assumptions"` | Default mode for discuss-phase: `"discuss"` runs interactive questioning; `"assumptions"` analyzes codebase and surfaces assumptions instead |
|
||||||
| `workflow.skip_discuss` | boolean | `false` | `true`, `false` | Skip discuss phase entirely |
|
| `workflow.skip_discuss` | boolean | `false` | `true`, `false` | Skip discuss phase entirely |
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ These files live directly at `.planning/` — not inside phase subdirectories.
|
|||||||
| `LEARNINGS.md` | *(inline)* | `/gsd:extract-learnings`, `/gsd:execute-phase` (gated: `features.global_learnings`) | Phase retrospective learnings for future plans |
|
| `LEARNINGS.md` | *(inline)* | `/gsd:extract-learnings`, `/gsd:execute-phase` (gated: `features.global_learnings`) | Phase retrospective learnings for future plans |
|
||||||
| `THREADS.md` | *(inline)* | `/gsd:thread` | Persistent discussion threads |
|
| `THREADS.md` | *(inline)* | `/gsd:thread` | Persistent discussion threads |
|
||||||
| `config.json` | `config.json` | `/gsd:new-project`, `/gsd:health --repair` | Project-specific GSD configuration |
|
| `config.json` | `config.json` | `/gsd:new-project`, `/gsd:health --repair` | Project-specific GSD configuration |
|
||||||
| `CLAUDE.md` | `claude-md.md` | `/gsd-profile` | Auto-assembled Claude Code context file |
|
| `CLAUDE.md` | *(inline)* | `/gsd-profile` | Auto-assembled Claude Code context file |
|
||||||
| `RETROSPECTIVE.md` | *(inline)* | `/gsd:complete-milestone` | Living milestone retrospective updated at each milestone close |
|
| `RETROSPECTIVE.md` | *(inline)* | `/gsd:complete-milestone` | Living milestone retrospective updated at each milestone close |
|
||||||
|
|
||||||
### Version-stamped artifacts (pattern: `vX.Y-*.md`)
|
### Version-stamped artifacts (pattern: `vX.Y-*.md`)
|
||||||
|
|||||||
@@ -1,145 +0,0 @@
|
|||||||
# CLAUDE.md Template
|
|
||||||
|
|
||||||
Template for project-root `CLAUDE.md` — auto-generated by `gsd-tools generate-claude-md`.
|
|
||||||
|
|
||||||
Contains 7 marker-bounded sections. Each section is independently updatable.
|
|
||||||
The `generate-claude-md` subcommand manages 6 sections (project, stack, conventions, architecture, skills, workflow enforcement).
|
|
||||||
The profile section is managed exclusively by `generate-claude-profile`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Section Templates
|
|
||||||
|
|
||||||
### Project Section
|
|
||||||
```
|
|
||||||
<!-- GSD:project-start source:PROJECT.md -->
|
|
||||||
## Project
|
|
||||||
|
|
||||||
{{project_content}}
|
|
||||||
<!-- GSD:project-end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
**Fallback text:**
|
|
||||||
```
|
|
||||||
Project not yet initialized. Run /gsd:new-project to set up.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stack Section
|
|
||||||
```
|
|
||||||
<!-- GSD:stack-start source:STACK.md -->
|
|
||||||
## Technology Stack
|
|
||||||
|
|
||||||
{{stack_content}}
|
|
||||||
<!-- GSD:stack-end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
**Fallback text:**
|
|
||||||
```
|
|
||||||
Technology stack not yet documented. Will populate after codebase mapping or first phase.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Conventions Section
|
|
||||||
```
|
|
||||||
<!-- GSD:conventions-start source:CONVENTIONS.md -->
|
|
||||||
## Conventions
|
|
||||||
|
|
||||||
{{conventions_content}}
|
|
||||||
<!-- GSD:conventions-end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
**Fallback text:**
|
|
||||||
```
|
|
||||||
Conventions not yet established. Will populate as patterns emerge during development.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Architecture Section
|
|
||||||
```
|
|
||||||
<!-- GSD:architecture-start source:ARCHITECTURE.md -->
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
{{architecture_content}}
|
|
||||||
<!-- GSD:architecture-end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
**Fallback text:**
|
|
||||||
```
|
|
||||||
Architecture not yet mapped. Follow existing patterns found in the codebase.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Skills Section
|
|
||||||
```
|
|
||||||
<!-- GSD:skills-start source:skills/ -->
|
|
||||||
## Project Skills
|
|
||||||
|
|
||||||
| Skill | Description | Path |
|
|
||||||
| -------------- | --------------------- | ------------------------- |
|
|
||||||
| {{skill_name}} | {{skill_description}} | `{{skill_path}}/SKILL.md` |
|
|
||||||
<!-- GSD:skills-end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
**Fallback text:**
|
|
||||||
```
|
|
||||||
No project skills found. Add skills to any of: `.claude/skills/`, `.agents/skills/`, `.cursor/skills/`, or `.github/skills/` with a `SKILL.md` index file.
|
|
||||||
```
|
|
||||||
|
|
||||||
**Discovery behavior:**
|
|
||||||
- Scans `.claude/skills/`, `.agents/skills/`, `.cursor/skills/`, `.github/skills/` for subdirectories containing `SKILL.md`
|
|
||||||
- Extracts `name` and `description` from YAML frontmatter (supports multi-line descriptions)
|
|
||||||
- Skips GSD's own installed skills (directories starting with `gsd-`)
|
|
||||||
- Deduplicates by skill name across directories
|
|
||||||
|
|
||||||
### Workflow Enforcement Section
|
|
||||||
```
|
|
||||||
<!-- GSD:workflow-start source:GSD defaults -->
|
|
||||||
## GSD Workflow Enforcement
|
|
||||||
|
|
||||||
Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.
|
|
||||||
|
|
||||||
Use these entry points:
|
|
||||||
- `/gsd:quick` for small fixes, doc updates, and ad-hoc tasks
|
|
||||||
- `/gsd:debug` for investigation and bug fixing
|
|
||||||
- `/gsd:execute-phase` for planned phase work
|
|
||||||
|
|
||||||
Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it.
|
|
||||||
<!-- GSD:workflow-end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
### Profile Section (Placeholder Only)
|
|
||||||
```
|
|
||||||
<!-- GSD:profile-start -->
|
|
||||||
## Developer Profile
|
|
||||||
|
|
||||||
> Profile not yet configured. Run `/gsd:profile-user` to generate your developer profile.
|
|
||||||
> This section is managed by `generate-claude-profile` — do not edit manually.
|
|
||||||
<!-- GSD:profile-end -->
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** This section is NOT managed by `generate-claude-md`. It is managed exclusively
|
|
||||||
by `generate-claude-profile`. The placeholder above is only used when creating a new
|
|
||||||
CLAUDE.md file and no profile section exists yet.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Section Ordering
|
|
||||||
|
|
||||||
1. **Project** — Identity and purpose (what this project is)
|
|
||||||
2. **Stack** — Technology choices (what tools are used)
|
|
||||||
3. **Conventions** — Code patterns and rules (how code is written)
|
|
||||||
4. **Architecture** — System structure (how components fit together)
|
|
||||||
5. **Skills** — Discovered project skills with name and description (what domain knowledge is available)
|
|
||||||
6. **Workflow Enforcement** — Default GSD entry points for file-changing work
|
|
||||||
7. **Profile** — Developer behavioral preferences (how to interact)
|
|
||||||
|
|
||||||
## Marker Format
|
|
||||||
|
|
||||||
- Start: `<!-- GSD:{name}-start source:{file} -->`
|
|
||||||
- End: `<!-- GSD:{name}-end -->`
|
|
||||||
- Source attribute enables targeted updates when source files change
|
|
||||||
- Partial match on start marker (without closing `-->`) for detection
|
|
||||||
|
|
||||||
## Fallback Behavior
|
|
||||||
|
|
||||||
When a source file is missing, fallback text provides Claude-actionable guidance:
|
|
||||||
- Guides Claude's behavior in the absence of data
|
|
||||||
- Not placeholder ads or "missing" notices
|
|
||||||
- Each fallback tells Claude what to do, not just what's absent
|
|
||||||
@@ -1,310 +0,0 @@
|
|||||||
# Codebase Concerns Template
|
|
||||||
|
|
||||||
Template for `.planning/codebase/CONCERNS.md` - captures known issues and areas requiring care.
|
|
||||||
|
|
||||||
**Purpose:** Surface actionable warnings about the codebase. Focused on "what to watch out for when making changes."
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Template
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# Codebase Concerns
|
|
||||||
|
|
||||||
**Analysis Date:** [YYYY-MM-DD]
|
|
||||||
|
|
||||||
## Tech Debt
|
|
||||||
|
|
||||||
**[Area/Component]:**
|
|
||||||
- Issue: [What's the shortcut/workaround]
|
|
||||||
- Why: [Why it was done this way]
|
|
||||||
- Impact: [What breaks or degrades because of it]
|
|
||||||
- Fix approach: [How to properly address it]
|
|
||||||
|
|
||||||
**[Area/Component]:**
|
|
||||||
- Issue: [What's the shortcut/workaround]
|
|
||||||
- Why: [Why it was done this way]
|
|
||||||
- Impact: [What breaks or degrades because of it]
|
|
||||||
- Fix approach: [How to properly address it]
|
|
||||||
|
|
||||||
## Known Bugs
|
|
||||||
|
|
||||||
**[Bug description]:**
|
|
||||||
- Symptoms: [What happens]
|
|
||||||
- Trigger: [How to reproduce]
|
|
||||||
- Workaround: [Temporary mitigation if any]
|
|
||||||
- Root cause: [If known]
|
|
||||||
- Blocked by: [If waiting on something]
|
|
||||||
|
|
||||||
**[Bug description]:**
|
|
||||||
- Symptoms: [What happens]
|
|
||||||
- Trigger: [How to reproduce]
|
|
||||||
- Workaround: [Temporary mitigation if any]
|
|
||||||
- Root cause: [If known]
|
|
||||||
|
|
||||||
## Security Considerations
|
|
||||||
|
|
||||||
**[Area requiring security care]:**
|
|
||||||
- Risk: [What could go wrong]
|
|
||||||
- Current mitigation: [What's in place now]
|
|
||||||
- Recommendations: [What should be added]
|
|
||||||
|
|
||||||
**[Area requiring security care]:**
|
|
||||||
- Risk: [What could go wrong]
|
|
||||||
- Current mitigation: [What's in place now]
|
|
||||||
- Recommendations: [What should be added]
|
|
||||||
|
|
||||||
## Performance Bottlenecks
|
|
||||||
|
|
||||||
**[Slow operation/endpoint]:**
|
|
||||||
- Problem: [What's slow]
|
|
||||||
- Measurement: [Actual numbers: "500ms p95", "2s load time"]
|
|
||||||
- Cause: [Why it's slow]
|
|
||||||
- Improvement path: [How to speed it up]
|
|
||||||
|
|
||||||
**[Slow operation/endpoint]:**
|
|
||||||
- Problem: [What's slow]
|
|
||||||
- Measurement: [Actual numbers]
|
|
||||||
- Cause: [Why it's slow]
|
|
||||||
- Improvement path: [How to speed it up]
|
|
||||||
|
|
||||||
## Fragile Areas
|
|
||||||
|
|
||||||
**[Component/Module]:**
|
|
||||||
- Why fragile: [What makes it break easily]
|
|
||||||
- Common failures: [What typically goes wrong]
|
|
||||||
- Safe modification: [How to change it without breaking]
|
|
||||||
- Test coverage: [Is it tested? Gaps?]
|
|
||||||
|
|
||||||
**[Component/Module]:**
|
|
||||||
- Why fragile: [What makes it break easily]
|
|
||||||
- Common failures: [What typically goes wrong]
|
|
||||||
- Safe modification: [How to change it without breaking]
|
|
||||||
- Test coverage: [Is it tested? Gaps?]
|
|
||||||
|
|
||||||
## Scaling Limits
|
|
||||||
|
|
||||||
**[Resource/System]:**
|
|
||||||
- Current capacity: [Numbers: "100 req/sec", "10k users"]
|
|
||||||
- Limit: [Where it breaks]
|
|
||||||
- Symptoms at limit: [What happens]
|
|
||||||
- Scaling path: [How to increase capacity]
|
|
||||||
|
|
||||||
## Dependencies at Risk
|
|
||||||
|
|
||||||
**[Package/Service]:**
|
|
||||||
- Risk: [e.g., "deprecated", "unmaintained", "breaking changes coming"]
|
|
||||||
- Impact: [What breaks if it fails]
|
|
||||||
- Migration plan: [Alternative or upgrade path]
|
|
||||||
|
|
||||||
## Missing Critical Features
|
|
||||||
|
|
||||||
**[Feature gap]:**
|
|
||||||
- Problem: [What's missing]
|
|
||||||
- Current workaround: [How users cope]
|
|
||||||
- Blocks: [What can't be done without it]
|
|
||||||
- Implementation complexity: [Rough effort estimate]
|
|
||||||
|
|
||||||
## Test Coverage Gaps
|
|
||||||
|
|
||||||
**[Untested area]:**
|
|
||||||
- What's not tested: [Specific functionality]
|
|
||||||
- Risk: [What could break unnoticed]
|
|
||||||
- Priority: [High/Medium/Low]
|
|
||||||
- Difficulty to test: [Why it's not tested yet]
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Concerns audit: [date]*
|
|
||||||
*Update as issues are fixed or new ones discovered*
|
|
||||||
```
|
|
||||||
|
|
||||||
<good_examples>
|
|
||||||
```markdown
|
|
||||||
# Codebase Concerns
|
|
||||||
|
|
||||||
**Analysis Date:** 2025-01-20
|
|
||||||
|
|
||||||
## Tech Debt
|
|
||||||
|
|
||||||
**Database queries in React components:**
|
|
||||||
- Issue: Direct Supabase queries in 15+ page components instead of server actions
|
|
||||||
- Files: `app/dashboard/page.tsx`, `app/profile/page.tsx`, `app/courses/[id]/page.tsx`, `app/settings/page.tsx` (and 11 more in `app/`)
|
|
||||||
- Why: Rapid prototyping during MVP phase
|
|
||||||
- Impact: Can't implement RLS properly, exposes DB structure to client
|
|
||||||
- Fix approach: Move all queries to server actions in `app/actions/`, add proper RLS policies
|
|
||||||
|
|
||||||
**Manual webhook signature validation:**
|
|
||||||
- Issue: Copy-pasted Stripe webhook verification code in 3 different endpoints
|
|
||||||
- Files: `app/api/webhooks/stripe/route.ts`, `app/api/webhooks/checkout/route.ts`, `app/api/webhooks/subscription/route.ts`
|
|
||||||
- Why: Each webhook added ad-hoc without abstraction
|
|
||||||
- Impact: Easy to miss verification in new webhooks (security risk)
|
|
||||||
- Fix approach: Create shared `lib/stripe/validate-webhook.ts` middleware
|
|
||||||
|
|
||||||
## Known Bugs
|
|
||||||
|
|
||||||
**Race condition in subscription updates:**
|
|
||||||
- Symptoms: User shows as "free" tier for 5-10 seconds after successful payment
|
|
||||||
- Trigger: Fast navigation after Stripe checkout redirect, before webhook processes
|
|
||||||
- Files: `app/checkout/success/page.tsx` (redirect handler), `app/api/webhooks/stripe/route.ts` (webhook)
|
|
||||||
- Workaround: Stripe webhook eventually updates status (self-heals)
|
|
||||||
- Root cause: Webhook processing slower than user navigation, no optimistic UI update
|
|
||||||
- Fix: Add polling in `app/checkout/success/page.tsx` after redirect
|
|
||||||
|
|
||||||
**Inconsistent session state after logout:**
|
|
||||||
- Symptoms: User redirected to /dashboard after logout instead of /login
|
|
||||||
- Trigger: Logout via button in mobile nav (desktop works fine)
|
|
||||||
- File: `components/MobileNav.tsx` (line ~45, logout handler)
|
|
||||||
- Workaround: Manual URL navigation to /login works
|
|
||||||
- Root cause: Mobile nav component not awaiting supabase.auth.signOut()
|
|
||||||
- Fix: Add await to logout handler in `components/MobileNav.tsx`
|
|
||||||
|
|
||||||
## Security Considerations
|
|
||||||
|
|
||||||
**Admin role check client-side only:**
|
|
||||||
- Risk: Admin dashboard pages check isAdmin from Supabase client, no server verification
|
|
||||||
- Files: `app/admin/page.tsx`, `app/admin/users/page.tsx`, `components/AdminGuard.tsx`
|
|
||||||
- Current mitigation: None (relying on UI hiding)
|
|
||||||
- Recommendations: Add middleware to admin routes in `middleware.ts`, verify role server-side
|
|
||||||
|
|
||||||
**Unvalidated file uploads:**
|
|
||||||
- Risk: Users can upload any file type to avatar bucket (no size/type validation)
|
|
||||||
- File: `components/AvatarUpload.tsx` (upload handler)
|
|
||||||
- Current mitigation: Supabase bucket limits to 2MB (configured in dashboard)
|
|
||||||
- Recommendations: Add file type validation (image/* only) in `lib/storage/validate.ts`
|
|
||||||
|
|
||||||
## Performance Bottlenecks
|
|
||||||
|
|
||||||
**/api/courses endpoint:**
|
|
||||||
- Problem: Fetching all courses with nested lessons and authors
|
|
||||||
- File: `app/api/courses/route.ts`
|
|
||||||
- Measurement: 1.2s p95 response time with 50+ courses
|
|
||||||
- Cause: N+1 query pattern (separate query per course for lessons)
|
|
||||||
- Improvement path: Use Prisma include to eager-load lessons in `lib/db/courses.ts`, add Redis caching
|
|
||||||
|
|
||||||
**Dashboard initial load:**
|
|
||||||
- Problem: Waterfall of 5 serial API calls on mount
|
|
||||||
- File: `app/dashboard/page.tsx`
|
|
||||||
- Measurement: 3.5s until interactive on slow 3G
|
|
||||||
- Cause: Each component fetches own data independently
|
|
||||||
- Improvement path: Convert to Server Component with single parallel fetch
|
|
||||||
|
|
||||||
## Fragile Areas
|
|
||||||
|
|
||||||
**Authentication middleware chain:**
|
|
||||||
- File: `middleware.ts`
|
|
||||||
- Why fragile: 4 different middleware functions run in specific order (auth -> role -> subscription -> logging)
|
|
||||||
- Common failures: Middleware order change breaks everything, hard to debug
|
|
||||||
- Safe modification: Add tests before changing order, document dependencies in comments
|
|
||||||
- Test coverage: No integration tests for middleware chain (only unit tests)
|
|
||||||
|
|
||||||
**Stripe webhook event handling:**
|
|
||||||
- File: `app/api/webhooks/stripe/route.ts`
|
|
||||||
- Why fragile: Giant switch statement with 12 event types, shared transaction logic
|
|
||||||
- Common failures: New event type added without handling, partial DB updates on error
|
|
||||||
- Safe modification: Extract each event handler to `lib/stripe/handlers/*.ts`
|
|
||||||
- Test coverage: Only 3 of 12 event types have tests
|
|
||||||
|
|
||||||
## Scaling Limits
|
|
||||||
|
|
||||||
**Supabase Free Tier:**
|
|
||||||
- Current capacity: 500MB database, 1GB file storage, 2GB bandwidth/month
|
|
||||||
- Limit: ~5000 users estimated before hitting limits
|
|
||||||
- Symptoms at limit: 429 rate limit errors, DB writes fail
|
|
||||||
- Scaling path: Upgrade to Pro ($25/mo) extends to 8GB DB, 100GB storage
|
|
||||||
|
|
||||||
**Server-side render blocking:**
|
|
||||||
- Current capacity: ~50 concurrent users before slowdown
|
|
||||||
- Limit: Vercel Hobby plan (10s function timeout, 100GB-hrs/mo)
|
|
||||||
- Symptoms at limit: 504 gateway timeouts on course pages
|
|
||||||
- Scaling path: Upgrade to Vercel Pro ($20/mo), add edge caching
|
|
||||||
|
|
||||||
## Dependencies at Risk
|
|
||||||
|
|
||||||
**react-hot-toast:**
|
|
||||||
- Risk: Unmaintained (last update 18 months ago), React 19 compatibility unknown
|
|
||||||
- Impact: Toast notifications break, no graceful degradation
|
|
||||||
- Migration plan: Switch to sonner (actively maintained, similar API)
|
|
||||||
|
|
||||||
## Missing Critical Features
|
|
||||||
|
|
||||||
**Payment failure handling:**
|
|
||||||
- Problem: No retry mechanism or user notification when subscription payment fails
|
|
||||||
- Current workaround: Users manually re-enter payment info (if they notice)
|
|
||||||
- Blocks: Can't retain users with expired cards, no dunning process
|
|
||||||
- Implementation complexity: Medium (Stripe webhooks + email flow + UI)
|
|
||||||
|
|
||||||
**Course progress tracking:**
|
|
||||||
- Problem: No persistent state for which lessons completed
|
|
||||||
- Current workaround: Users manually track progress
|
|
||||||
- Blocks: Can't show completion percentage, can't recommend next lesson
|
|
||||||
- Implementation complexity: Low (add completed_lessons junction table)
|
|
||||||
|
|
||||||
## Test Coverage Gaps
|
|
||||||
|
|
||||||
**Payment flow end-to-end:**
|
|
||||||
- What's not tested: Full Stripe checkout -> webhook -> subscription activation flow
|
|
||||||
- Risk: Payment processing could break silently (has happened twice)
|
|
||||||
- Priority: High
|
|
||||||
- Difficulty to test: Need Stripe test fixtures and webhook simulation setup
|
|
||||||
|
|
||||||
**Error boundary behavior:**
|
|
||||||
- What's not tested: How app behaves when components throw errors
|
|
||||||
- Risk: White screen of death for users, no error reporting
|
|
||||||
- Priority: Medium
|
|
||||||
- Difficulty to test: Need to intentionally trigger errors in test environment
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Concerns audit: 2025-01-20*
|
|
||||||
*Update as issues are fixed or new ones discovered*
|
|
||||||
```
|
|
||||||
</good_examples>
|
|
||||||
|
|
||||||
<guidelines>
|
|
||||||
**What belongs in CONCERNS.md:**
|
|
||||||
- Tech debt with clear impact and fix approach
|
|
||||||
- Known bugs with reproduction steps
|
|
||||||
- Security gaps and mitigation recommendations
|
|
||||||
- Performance bottlenecks with measurements
|
|
||||||
- Fragile code that breaks easily
|
|
||||||
- Scaling limits with numbers
|
|
||||||
- Dependencies that need attention
|
|
||||||
- Missing features that block workflows
|
|
||||||
- Test coverage gaps
|
|
||||||
|
|
||||||
**What does NOT belong here:**
|
|
||||||
- Opinions without evidence ("code is messy")
|
|
||||||
- Complaints without solutions ("auth sucks")
|
|
||||||
- Future feature ideas (that's for product planning)
|
|
||||||
- Normal TODOs (those live in code comments)
|
|
||||||
- Architectural decisions that are working fine
|
|
||||||
- Minor code style issues
|
|
||||||
|
|
||||||
**When filling this template:**
|
|
||||||
- **Always include file paths** - Concerns without locations are not actionable. Use backticks: `src/file.ts`
|
|
||||||
- Be specific with measurements ("500ms p95" not "slow")
|
|
||||||
- Include reproduction steps for bugs
|
|
||||||
- Suggest fix approaches, not just problems
|
|
||||||
- Focus on actionable items
|
|
||||||
- Prioritize by risk/impact
|
|
||||||
- Update as issues get resolved
|
|
||||||
- Add new concerns as discovered
|
|
||||||
|
|
||||||
**Tone guidelines:**
|
|
||||||
- Professional, not emotional ("N+1 query pattern" not "terrible queries")
|
|
||||||
- Solution-oriented ("Fix: add index" not "needs fixing")
|
|
||||||
- Risk-focused ("Could expose user data" not "security is bad")
|
|
||||||
- Factual ("3.5s load time" not "really slow")
|
|
||||||
|
|
||||||
**Useful for phase planning when:**
|
|
||||||
- Deciding what to work on next
|
|
||||||
- Estimating risk of changes
|
|
||||||
- Understanding where to be careful
|
|
||||||
- Prioritizing improvements
|
|
||||||
- Onboarding new Claude contexts
|
|
||||||
- Planning refactoring work
|
|
||||||
|
|
||||||
**How this gets populated:**
|
|
||||||
Explore agents detect these during codebase mapping. Manual additions welcome for human-discovered issues. This is living documentation, not a complaint list.
|
|
||||||
</guidelines>
|
|
||||||
@@ -1,307 +0,0 @@
|
|||||||
# Coding Conventions Template
|
|
||||||
|
|
||||||
Template for `.planning/codebase/CONVENTIONS.md` - captures coding style and patterns.
|
|
||||||
|
|
||||||
**Purpose:** Document how code is written in this codebase. Prescriptive guide for Claude to match existing style.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Template
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# Coding Conventions
|
|
||||||
|
|
||||||
**Analysis Date:** [YYYY-MM-DD]
|
|
||||||
|
|
||||||
## Naming Patterns
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- [Pattern: e.g., "kebab-case for all files"]
|
|
||||||
- [Test files: e.g., "*.test.ts alongside source"]
|
|
||||||
- [Components: e.g., "PascalCase.tsx for React components"]
|
|
||||||
|
|
||||||
**Functions:**
|
|
||||||
- [Pattern: e.g., "camelCase for all functions"]
|
|
||||||
- [Async: e.g., "no special prefix for async functions"]
|
|
||||||
- [Handlers: e.g., "handleEventName for event handlers"]
|
|
||||||
|
|
||||||
**Variables:**
|
|
||||||
- [Pattern: e.g., "camelCase for variables"]
|
|
||||||
- [Constants: e.g., "UPPER_SNAKE_CASE for constants"]
|
|
||||||
- [Private: e.g., "_prefix for private members" or "no prefix"]
|
|
||||||
|
|
||||||
**Types:**
|
|
||||||
- [Interfaces: e.g., "PascalCase, no I prefix"]
|
|
||||||
- [Types: e.g., "PascalCase for type aliases"]
|
|
||||||
- [Enums: e.g., "PascalCase for enum name, UPPER_CASE for values"]
|
|
||||||
|
|
||||||
## Code Style
|
|
||||||
|
|
||||||
**Formatting:**
|
|
||||||
- [Tool: e.g., "Prettier with config in .prettierrc"]
|
|
||||||
- [Line length: e.g., "100 characters max"]
|
|
||||||
- [Quotes: e.g., "single quotes for strings"]
|
|
||||||
- [Semicolons: e.g., "required" or "omitted"]
|
|
||||||
|
|
||||||
**Linting:**
|
|
||||||
- [Tool: e.g., "ESLint with eslint.config.js"]
|
|
||||||
- [Rules: e.g., "extends airbnb-base, no console in production"]
|
|
||||||
- [Run: e.g., "npm run lint"]
|
|
||||||
|
|
||||||
## Import Organization
|
|
||||||
|
|
||||||
**Order:**
|
|
||||||
1. [e.g., "External packages (react, express, etc.)"]
|
|
||||||
2. [e.g., "Internal modules (@/lib, @/components)"]
|
|
||||||
3. [e.g., "Relative imports (., ..)"]
|
|
||||||
4. [e.g., "Type imports (import type {})"]
|
|
||||||
|
|
||||||
**Grouping:**
|
|
||||||
- [Blank lines: e.g., "blank line between groups"]
|
|
||||||
- [Sorting: e.g., "alphabetical within each group"]
|
|
||||||
|
|
||||||
**Path Aliases:**
|
|
||||||
- [Aliases used: e.g., "@/ for src/, @components/ for src/components/"]
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
**Patterns:**
|
|
||||||
- [Strategy: e.g., "throw errors, catch at boundaries"]
|
|
||||||
- [Custom errors: e.g., "extend Error class, named *Error"]
|
|
||||||
- [Async: e.g., "use try/catch, no .catch() chains"]
|
|
||||||
|
|
||||||
**Error Types:**
|
|
||||||
- [When to throw: e.g., "invalid input, missing dependencies"]
|
|
||||||
- [When to return: e.g., "expected failures return Result<T, E>"]
|
|
||||||
- [Logging: e.g., "log error with context before throwing"]
|
|
||||||
|
|
||||||
## Logging
|
|
||||||
|
|
||||||
**Framework:**
|
|
||||||
- [Tool: e.g., "console.log, pino, winston"]
|
|
||||||
- [Levels: e.g., "debug, info, warn, error"]
|
|
||||||
|
|
||||||
**Patterns:**
|
|
||||||
- [Format: e.g., "structured logging with context object"]
|
|
||||||
- [When: e.g., "log state transitions, external calls"]
|
|
||||||
- [Where: e.g., "log at service boundaries, not in utils"]
|
|
||||||
|
|
||||||
## Comments
|
|
||||||
|
|
||||||
**When to Comment:**
|
|
||||||
- [e.g., "explain why, not what"]
|
|
||||||
- [e.g., "document business logic, algorithms, edge cases"]
|
|
||||||
- [e.g., "avoid obvious comments like // increment counter"]
|
|
||||||
|
|
||||||
**JSDoc/TSDoc:**
|
|
||||||
- [Usage: e.g., "required for public APIs, optional for internal"]
|
|
||||||
- [Format: e.g., "use @param, @returns, @throws tags"]
|
|
||||||
|
|
||||||
**TODO Comments:**
|
|
||||||
- [Pattern: e.g., "// TODO(username): description"]
|
|
||||||
- [Tracking: e.g., "link to issue number if available"]
|
|
||||||
|
|
||||||
## Function Design
|
|
||||||
|
|
||||||
**Size:**
|
|
||||||
- [e.g., "keep under 50 lines, extract helpers"]
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- [e.g., "max 3 parameters, use object for more"]
|
|
||||||
- [e.g., "destructure objects in parameter list"]
|
|
||||||
|
|
||||||
**Return Values:**
|
|
||||||
- [e.g., "explicit returns, no implicit undefined"]
|
|
||||||
- [e.g., "return early for guard clauses"]
|
|
||||||
|
|
||||||
## Module Design
|
|
||||||
|
|
||||||
**Exports:**
|
|
||||||
- [e.g., "named exports preferred, default exports for React components"]
|
|
||||||
- [e.g., "export from index.ts for public API"]
|
|
||||||
|
|
||||||
**Barrel Files:**
|
|
||||||
- [e.g., "use index.ts to re-export public API"]
|
|
||||||
- [e.g., "avoid circular dependencies"]
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Convention analysis: [date]*
|
|
||||||
*Update when patterns change*
|
|
||||||
```
|
|
||||||
|
|
||||||
<good_examples>
|
|
||||||
```markdown
|
|
||||||
# Coding Conventions
|
|
||||||
|
|
||||||
**Analysis Date:** 2025-01-20
|
|
||||||
|
|
||||||
## Naming Patterns
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- kebab-case for all files (command-handler.ts, user-service.ts)
|
|
||||||
- *.test.ts alongside source files
|
|
||||||
- index.ts for barrel exports
|
|
||||||
|
|
||||||
**Functions:**
|
|
||||||
- camelCase for all functions
|
|
||||||
- No special prefix for async functions
|
|
||||||
- handleEventName for event handlers (handleClick, handleSubmit)
|
|
||||||
|
|
||||||
**Variables:**
|
|
||||||
- camelCase for variables
|
|
||||||
- UPPER_SNAKE_CASE for constants (MAX_RETRIES, API_BASE_URL)
|
|
||||||
- No underscore prefix (no private marker in TS)
|
|
||||||
|
|
||||||
**Types:**
|
|
||||||
- PascalCase for interfaces, no I prefix (User, not IUser)
|
|
||||||
- PascalCase for type aliases (UserConfig, ResponseData)
|
|
||||||
- PascalCase for enum names, UPPER_CASE for values (Status.PENDING)
|
|
||||||
|
|
||||||
## Code Style
|
|
||||||
|
|
||||||
**Formatting:**
|
|
||||||
- Prettier with .prettierrc
|
|
||||||
- 100 character line length
|
|
||||||
- Single quotes for strings
|
|
||||||
- Semicolons required
|
|
||||||
- 2 space indentation
|
|
||||||
|
|
||||||
**Linting:**
|
|
||||||
- ESLint with eslint.config.js
|
|
||||||
- Extends @typescript-eslint/recommended
|
|
||||||
- No console.log in production code (use logger)
|
|
||||||
- Run: npm run lint
|
|
||||||
|
|
||||||
## Import Organization
|
|
||||||
|
|
||||||
**Order:**
|
|
||||||
1. External packages (react, express, commander)
|
|
||||||
2. Internal modules (@/lib, @/services)
|
|
||||||
3. Relative imports (./utils, ../types)
|
|
||||||
4. Type imports (import type { User })
|
|
||||||
|
|
||||||
**Grouping:**
|
|
||||||
- Blank line between groups
|
|
||||||
- Alphabetical within each group
|
|
||||||
- Type imports last within each group
|
|
||||||
|
|
||||||
**Path Aliases:**
|
|
||||||
- @/ maps to src/
|
|
||||||
- No other aliases defined
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
**Patterns:**
|
|
||||||
- Throw errors, catch at boundaries (route handlers, main functions)
|
|
||||||
- Extend Error class for custom errors (ValidationError, NotFoundError)
|
|
||||||
- Async functions use try/catch, no .catch() chains
|
|
||||||
|
|
||||||
**Error Types:**
|
|
||||||
- Throw on invalid input, missing dependencies, invariant violations
|
|
||||||
- Log error with context before throwing: logger.error({ err, userId }, 'Failed to process')
|
|
||||||
- Include cause in error message: new Error('Failed to X', { cause: originalError })
|
|
||||||
|
|
||||||
## Logging
|
|
||||||
|
|
||||||
**Framework:**
|
|
||||||
- pino logger instance exported from lib/logger.ts
|
|
||||||
- Levels: debug, info, warn, error (no trace)
|
|
||||||
|
|
||||||
**Patterns:**
|
|
||||||
- Structured logging with context: logger.info({ userId, action }, 'User action')
|
|
||||||
- Log at service boundaries, not in utility functions
|
|
||||||
- Log state transitions, external API calls, errors
|
|
||||||
- No console.log in committed code
|
|
||||||
|
|
||||||
## Comments
|
|
||||||
|
|
||||||
**When to Comment:**
|
|
||||||
- Explain why, not what: // Retry 3 times because API has transient failures
|
|
||||||
- Document business rules: // Users must verify email within 24 hours
|
|
||||||
- Explain non-obvious algorithms or workarounds
|
|
||||||
- Avoid obvious comments: // set count to 0
|
|
||||||
|
|
||||||
**JSDoc/TSDoc:**
|
|
||||||
- Required for public API functions
|
|
||||||
- Optional for internal functions if signature is self-explanatory
|
|
||||||
- Use @param, @returns, @throws tags
|
|
||||||
|
|
||||||
**TODO Comments:**
|
|
||||||
- Format: // TODO: description (no username, using git blame)
|
|
||||||
- Link to issue if exists: // TODO: Fix race condition (issue #123)
|
|
||||||
|
|
||||||
## Function Design
|
|
||||||
|
|
||||||
**Size:**
|
|
||||||
- Keep under 50 lines
|
|
||||||
- Extract helpers for complex logic
|
|
||||||
- One level of abstraction per function
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- Max 3 parameters
|
|
||||||
- Use options object for 4+ parameters: function create(options: CreateOptions)
|
|
||||||
- Destructure in parameter list: function process({ id, name }: ProcessParams)
|
|
||||||
|
|
||||||
**Return Values:**
|
|
||||||
- Explicit return statements
|
|
||||||
- Return early for guard clauses
|
|
||||||
- Use Result<T, E> type for expected failures
|
|
||||||
|
|
||||||
## Module Design
|
|
||||||
|
|
||||||
**Exports:**
|
|
||||||
- Named exports preferred
|
|
||||||
- Default exports only for React components
|
|
||||||
- Export public API from index.ts barrel files
|
|
||||||
|
|
||||||
**Barrel Files:**
|
|
||||||
- index.ts re-exports public API
|
|
||||||
- Keep internal helpers private (don't export from index)
|
|
||||||
- Avoid circular dependencies (import from specific files if needed)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Convention analysis: 2025-01-20*
|
|
||||||
*Update when patterns change*
|
|
||||||
```
|
|
||||||
</good_examples>
|
|
||||||
|
|
||||||
<guidelines>
|
|
||||||
**What belongs in CONVENTIONS.md:**
|
|
||||||
- Naming patterns observed in the codebase
|
|
||||||
- Formatting rules (Prettier config, linting rules)
|
|
||||||
- Import organization patterns
|
|
||||||
- Error handling strategy
|
|
||||||
- Logging approach
|
|
||||||
- Comment conventions
|
|
||||||
- Function and module design patterns
|
|
||||||
|
|
||||||
**What does NOT belong here:**
|
|
||||||
- Architecture decisions (that's ARCHITECTURE.md)
|
|
||||||
- Technology choices (that's STACK.md)
|
|
||||||
- Test patterns (that's TESTING.md)
|
|
||||||
- File organization (that's STRUCTURE.md)
|
|
||||||
|
|
||||||
**When filling this template:**
|
|
||||||
- Check .prettierrc, .eslintrc, or similar config files
|
|
||||||
- Examine 5-10 representative source files for patterns
|
|
||||||
- Look for consistency: if 80%+ follows a pattern, document it
|
|
||||||
- Be prescriptive: "Use X" not "Sometimes Y is used"
|
|
||||||
- Note deviations: "Legacy code uses Y, new code should use X"
|
|
||||||
- Keep under ~150 lines total
|
|
||||||
|
|
||||||
**Useful for phase planning when:**
|
|
||||||
- Writing new code (match existing style)
|
|
||||||
- Adding features (follow naming patterns)
|
|
||||||
- Refactoring (apply consistent conventions)
|
|
||||||
- Code review (check against documented patterns)
|
|
||||||
- Onboarding (understand style expectations)
|
|
||||||
|
|
||||||
**Analysis approach:**
|
|
||||||
- Scan src/ directory for file naming patterns
|
|
||||||
- Check package.json scripts for lint/format commands
|
|
||||||
- Read 5-10 files to identify function naming, error handling
|
|
||||||
- Look for config files (.prettierrc, eslint.config.js)
|
|
||||||
- Note patterns in imports, comments, function signatures
|
|
||||||
</guidelines>
|
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
# External Integrations Template
|
|
||||||
|
|
||||||
Template for `.planning/codebase/INTEGRATIONS.md` - captures external service dependencies.
|
|
||||||
|
|
||||||
**Purpose:** Document what external systems this codebase communicates with. Focused on "what lives outside our code that we depend on."
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Template
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# External Integrations
|
|
||||||
|
|
||||||
**Analysis Date:** [YYYY-MM-DD]
|
|
||||||
|
|
||||||
## APIs & External Services
|
|
||||||
|
|
||||||
**Payment Processing:**
|
|
||||||
- [Service] - [What it's used for: e.g., "subscription billing, one-time payments"]
|
|
||||||
- SDK/Client: [e.g., "stripe npm package v14.x"]
|
|
||||||
- Auth: [e.g., "API key in STRIPE_SECRET_KEY env var"]
|
|
||||||
- Endpoints used: [e.g., "checkout sessions, webhooks"]
|
|
||||||
|
|
||||||
**Email/SMS:**
|
|
||||||
- [Service] - [What it's used for: e.g., "transactional emails"]
|
|
||||||
- SDK/Client: [e.g., "sendgrid/mail v8.x"]
|
|
||||||
- Auth: [e.g., "API key in SENDGRID_API_KEY env var"]
|
|
||||||
- Templates: [e.g., "managed in SendGrid dashboard"]
|
|
||||||
|
|
||||||
**External APIs:**
|
|
||||||
- [Service] - [What it's used for]
|
|
||||||
- Integration method: [e.g., "REST API via fetch", "GraphQL client"]
|
|
||||||
- Auth: [e.g., "OAuth2 token in AUTH_TOKEN env var"]
|
|
||||||
- Rate limits: [if applicable]
|
|
||||||
|
|
||||||
## Data Storage
|
|
||||||
|
|
||||||
**Databases:**
|
|
||||||
- [Type/Provider] - [e.g., "PostgreSQL on Supabase"]
|
|
||||||
- Connection: [e.g., "via DATABASE_URL env var"]
|
|
||||||
- Client: [e.g., "Prisma ORM v5.x"]
|
|
||||||
- Migrations: [e.g., "prisma migrate in migrations/"]
|
|
||||||
|
|
||||||
**File Storage:**
|
|
||||||
- [Service] - [e.g., "AWS S3 for user uploads"]
|
|
||||||
- SDK/Client: [e.g., "@aws-sdk/client-s3"]
|
|
||||||
- Auth: [e.g., "IAM credentials in AWS_* env vars"]
|
|
||||||
- Buckets: [e.g., "prod-uploads, dev-uploads"]
|
|
||||||
|
|
||||||
**Caching:**
|
|
||||||
- [Service] - [e.g., "Redis for session storage"]
|
|
||||||
- Connection: [e.g., "REDIS_URL env var"]
|
|
||||||
- Client: [e.g., "ioredis v5.x"]
|
|
||||||
|
|
||||||
## Authentication & Identity
|
|
||||||
|
|
||||||
**Auth Provider:**
|
|
||||||
- [Service] - [e.g., "Supabase Auth", "Auth0", "custom JWT"]
|
|
||||||
- Implementation: [e.g., "Supabase client SDK"]
|
|
||||||
- Token storage: [e.g., "httpOnly cookies", "localStorage"]
|
|
||||||
- Session management: [e.g., "JWT refresh tokens"]
|
|
||||||
|
|
||||||
**OAuth Integrations:**
|
|
||||||
- [Provider] - [e.g., "Google OAuth for sign-in"]
|
|
||||||
- Credentials: [e.g., "GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET"]
|
|
||||||
- Scopes: [e.g., "email, profile"]
|
|
||||||
|
|
||||||
## Monitoring & Observability
|
|
||||||
|
|
||||||
**Error Tracking:**
|
|
||||||
- [Service] - [e.g., "Sentry"]
|
|
||||||
- DSN: [e.g., "SENTRY_DSN env var"]
|
|
||||||
- Release tracking: [e.g., "via SENTRY_RELEASE"]
|
|
||||||
|
|
||||||
**Analytics:**
|
|
||||||
- [Service] - [e.g., "Mixpanel for product analytics"]
|
|
||||||
- Token: [e.g., "MIXPANEL_TOKEN env var"]
|
|
||||||
- Events tracked: [e.g., "user actions, page views"]
|
|
||||||
|
|
||||||
**Logs:**
|
|
||||||
- [Service] - [e.g., "CloudWatch", "Datadog", "none (stdout only)"]
|
|
||||||
- Integration: [e.g., "AWS Lambda built-in"]
|
|
||||||
|
|
||||||
## CI/CD & Deployment
|
|
||||||
|
|
||||||
**Hosting:**
|
|
||||||
- [Platform] - [e.g., "Vercel", "AWS Lambda", "Docker on ECS"]
|
|
||||||
- Deployment: [e.g., "automatic on main branch push"]
|
|
||||||
- Environment vars: [e.g., "configured in Vercel dashboard"]
|
|
||||||
|
|
||||||
**CI Pipeline:**
|
|
||||||
- [Service] - [e.g., "GitHub Actions"]
|
|
||||||
- Workflows: [e.g., "test.yml, deploy.yml"]
|
|
||||||
- Secrets: [e.g., "stored in GitHub repo secrets"]
|
|
||||||
|
|
||||||
## Environment Configuration
|
|
||||||
|
|
||||||
**Development:**
|
|
||||||
- Required env vars: [List critical vars]
|
|
||||||
- Secrets location: [e.g., ".env.local (gitignored)", "1Password vault"]
|
|
||||||
- Mock/stub services: [e.g., "Stripe test mode", "local PostgreSQL"]
|
|
||||||
|
|
||||||
**Staging:**
|
|
||||||
- Environment-specific differences: [e.g., "uses staging Stripe account"]
|
|
||||||
- Data: [e.g., "separate staging database"]
|
|
||||||
|
|
||||||
**Production:**
|
|
||||||
- Secrets management: [e.g., "Vercel environment variables"]
|
|
||||||
- Failover/redundancy: [e.g., "multi-region DB replication"]
|
|
||||||
|
|
||||||
## Webhooks & Callbacks
|
|
||||||
|
|
||||||
**Incoming:**
|
|
||||||
- [Service] - [Endpoint: e.g., "/api/webhooks/stripe"]
|
|
||||||
- Verification: [e.g., "signature validation via stripe.webhooks.constructEvent"]
|
|
||||||
- Events: [e.g., "payment_intent.succeeded, customer.subscription.updated"]
|
|
||||||
|
|
||||||
**Outgoing:**
|
|
||||||
- [Service] - [What triggers it]
|
|
||||||
- Endpoint: [e.g., "external CRM webhook on user signup"]
|
|
||||||
- Retry logic: [if applicable]
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Integration audit: [date]*
|
|
||||||
*Update when adding/removing external services*
|
|
||||||
```
|
|
||||||
|
|
||||||
<good_examples>
|
|
||||||
```markdown
|
|
||||||
# External Integrations
|
|
||||||
|
|
||||||
**Analysis Date:** 2025-01-20
|
|
||||||
|
|
||||||
## APIs & External Services
|
|
||||||
|
|
||||||
**Payment Processing:**
|
|
||||||
- Stripe - Subscription billing and one-time course payments
|
|
||||||
- SDK/Client: stripe npm package v14.8
|
|
||||||
- Auth: API key in STRIPE_SECRET_KEY env var
|
|
||||||
- Endpoints used: checkout sessions, customer portal, webhooks
|
|
||||||
|
|
||||||
**Email/SMS:**
|
|
||||||
- SendGrid - Transactional emails (receipts, password resets)
|
|
||||||
- SDK/Client: @sendgrid/mail v8.1
|
|
||||||
- Auth: API key in SENDGRID_API_KEY env var
|
|
||||||
- Templates: Managed in SendGrid dashboard (template IDs in code)
|
|
||||||
|
|
||||||
**External APIs:**
|
|
||||||
- OpenAI API - Course content generation
|
|
||||||
- Integration method: REST API via openai npm package v4.x
|
|
||||||
- Auth: Bearer token in OPENAI_API_KEY env var
|
|
||||||
- Rate limits: 3500 requests/min (tier 3)
|
|
||||||
|
|
||||||
## Data Storage
|
|
||||||
|
|
||||||
**Databases:**
|
|
||||||
- PostgreSQL on Supabase - Primary data store
|
|
||||||
- Connection: via DATABASE_URL env var
|
|
||||||
- Client: Prisma ORM v5.8
|
|
||||||
- Migrations: prisma migrate in prisma/migrations/
|
|
||||||
|
|
||||||
**File Storage:**
|
|
||||||
- Supabase Storage - User uploads (profile images, course materials)
|
|
||||||
- SDK/Client: @supabase/supabase-js v2.x
|
|
||||||
- Auth: Service role key in SUPABASE_SERVICE_ROLE_KEY
|
|
||||||
- Buckets: avatars (public), course-materials (private)
|
|
||||||
|
|
||||||
**Caching:**
|
|
||||||
- None currently (all database queries, no Redis)
|
|
||||||
|
|
||||||
## Authentication & Identity
|
|
||||||
|
|
||||||
**Auth Provider:**
|
|
||||||
- Supabase Auth - Email/password + OAuth
|
|
||||||
- Implementation: Supabase client SDK with server-side session management
|
|
||||||
- Token storage: httpOnly cookies via @supabase/ssr
|
|
||||||
- Session management: JWT refresh tokens handled by Supabase
|
|
||||||
|
|
||||||
**OAuth Integrations:**
|
|
||||||
- Google OAuth - Social sign-in
|
|
||||||
- Credentials: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET (Supabase dashboard)
|
|
||||||
- Scopes: email, profile
|
|
||||||
|
|
||||||
## Monitoring & Observability
|
|
||||||
|
|
||||||
**Error Tracking:**
|
|
||||||
- Sentry - Server and client errors
|
|
||||||
- DSN: SENTRY_DSN env var
|
|
||||||
- Release tracking: Git commit SHA via SENTRY_RELEASE
|
|
||||||
|
|
||||||
**Analytics:**
|
|
||||||
- None (planned: Mixpanel)
|
|
||||||
|
|
||||||
**Logs:**
|
|
||||||
- Vercel logs - stdout/stderr only
|
|
||||||
- Retention: 7 days on Pro plan
|
|
||||||
|
|
||||||
## CI/CD & Deployment
|
|
||||||
|
|
||||||
**Hosting:**
|
|
||||||
- Vercel - Next.js app hosting
|
|
||||||
- Deployment: Automatic on main branch push
|
|
||||||
- Environment vars: Configured in Vercel dashboard (synced to .env.example)
|
|
||||||
|
|
||||||
**CI Pipeline:**
|
|
||||||
- GitHub Actions - Tests and type checking
|
|
||||||
- Workflows: .github/workflows/ci.yml
|
|
||||||
- Secrets: None needed (public repo tests only)
|
|
||||||
|
|
||||||
## Environment Configuration
|
|
||||||
|
|
||||||
**Development:**
|
|
||||||
- Required env vars: DATABASE_URL, NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY
|
|
||||||
- Secrets location: .env.local (gitignored), team shared via 1Password vault
|
|
||||||
- Mock/stub services: Stripe test mode, Supabase local dev project
|
|
||||||
|
|
||||||
**Staging:**
|
|
||||||
- Uses separate Supabase staging project
|
|
||||||
- Stripe test mode
|
|
||||||
- Same Vercel account, different environment
|
|
||||||
|
|
||||||
**Production:**
|
|
||||||
- Secrets management: Vercel environment variables
|
|
||||||
- Database: Supabase production project with daily backups
|
|
||||||
|
|
||||||
## Webhooks & Callbacks
|
|
||||||
|
|
||||||
**Incoming:**
|
|
||||||
- Stripe - /api/webhooks/stripe
|
|
||||||
- Verification: Signature validation via stripe.webhooks.constructEvent
|
|
||||||
- Events: payment_intent.succeeded, customer.subscription.updated, customer.subscription.deleted
|
|
||||||
|
|
||||||
**Outgoing:**
|
|
||||||
- None
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Integration audit: 2025-01-20*
|
|
||||||
*Update when adding/removing external services*
|
|
||||||
```
|
|
||||||
</good_examples>
|
|
||||||
|
|
||||||
<guidelines>
|
|
||||||
**What belongs in INTEGRATIONS.md:**
|
|
||||||
- External services the code communicates with
|
|
||||||
- Authentication patterns (where secrets live, not the secrets themselves)
|
|
||||||
- SDKs and client libraries used
|
|
||||||
- Environment variable names (not values)
|
|
||||||
- Webhook endpoints and verification methods
|
|
||||||
- Database connection patterns
|
|
||||||
- File storage locations
|
|
||||||
- Monitoring and logging services
|
|
||||||
|
|
||||||
**What does NOT belong here:**
|
|
||||||
- Actual API keys or secrets (NEVER write these)
|
|
||||||
- Internal architecture (that's ARCHITECTURE.md)
|
|
||||||
- Code patterns (that's PATTERNS.md)
|
|
||||||
- Technology choices (that's STACK.md)
|
|
||||||
- Performance issues (that's CONCERNS.md)
|
|
||||||
|
|
||||||
**When filling this template:**
|
|
||||||
- Check .env.example or .env.template for required env vars
|
|
||||||
- Look for SDK imports (stripe, @sendgrid/mail, etc.)
|
|
||||||
- Check for webhook handlers in routes/endpoints
|
|
||||||
- Note where secrets are managed (not the secrets)
|
|
||||||
- Document environment-specific differences (dev/staging/prod)
|
|
||||||
- Include auth patterns for each service
|
|
||||||
|
|
||||||
**Useful for phase planning when:**
|
|
||||||
- Adding new external service integrations
|
|
||||||
- Debugging authentication issues
|
|
||||||
- Understanding data flow outside the application
|
|
||||||
- Setting up new environments
|
|
||||||
- Auditing third-party dependencies
|
|
||||||
- Planning for service outages or migrations
|
|
||||||
|
|
||||||
**Security note:**
|
|
||||||
Document WHERE secrets live (env vars, Vercel dashboard, 1Password), never WHAT the secrets are.
|
|
||||||
</guidelines>
|
|
||||||
@@ -1,285 +0,0 @@
|
|||||||
# Structure Template
|
|
||||||
|
|
||||||
Template for `.planning/codebase/STRUCTURE.md` - captures physical file organization.
|
|
||||||
|
|
||||||
**Purpose:** Document where things physically live in the codebase. Answers "where do I put X?"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Template
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# Codebase Structure
|
|
||||||
|
|
||||||
**Analysis Date:** [YYYY-MM-DD]
|
|
||||||
|
|
||||||
## Directory Layout
|
|
||||||
|
|
||||||
[ASCII box-drawing tree of top-level directories with purpose - use ├── └── │ characters for tree structure only]
|
|
||||||
|
|
||||||
```
|
|
||||||
[project-root]/
|
|
||||||
├── [dir]/ # [Purpose]
|
|
||||||
├── [dir]/ # [Purpose]
|
|
||||||
├── [dir]/ # [Purpose]
|
|
||||||
└── [file] # [Purpose]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Directory Purposes
|
|
||||||
|
|
||||||
**[Directory Name]:**
|
|
||||||
- Purpose: [What lives here]
|
|
||||||
- Contains: [Types of files: e.g., "*.ts source files", "component directories"]
|
|
||||||
- Key files: [Important files in this directory]
|
|
||||||
- Subdirectories: [If nested, describe structure]
|
|
||||||
|
|
||||||
**[Directory Name]:**
|
|
||||||
- Purpose: [What lives here]
|
|
||||||
- Contains: [Types of files]
|
|
||||||
- Key files: [Important files]
|
|
||||||
- Subdirectories: [Structure]
|
|
||||||
|
|
||||||
## Key File Locations
|
|
||||||
|
|
||||||
**Entry Points:**
|
|
||||||
- [Path]: [Purpose: e.g., "CLI entry point"]
|
|
||||||
- [Path]: [Purpose: e.g., "Server startup"]
|
|
||||||
|
|
||||||
**Configuration:**
|
|
||||||
- [Path]: [Purpose: e.g., "TypeScript config"]
|
|
||||||
- [Path]: [Purpose: e.g., "Build configuration"]
|
|
||||||
- [Path]: [Purpose: e.g., "Environment variables"]
|
|
||||||
|
|
||||||
**Core Logic:**
|
|
||||||
- [Path]: [Purpose: e.g., "Business services"]
|
|
||||||
- [Path]: [Purpose: e.g., "Database models"]
|
|
||||||
- [Path]: [Purpose: e.g., "API routes"]
|
|
||||||
|
|
||||||
**Testing:**
|
|
||||||
- [Path]: [Purpose: e.g., "Unit tests"]
|
|
||||||
- [Path]: [Purpose: e.g., "Test fixtures"]
|
|
||||||
|
|
||||||
**Documentation:**
|
|
||||||
- [Path]: [Purpose: e.g., "User-facing docs"]
|
|
||||||
- [Path]: [Purpose: e.g., "Developer guide"]
|
|
||||||
|
|
||||||
## Naming Conventions
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- [Pattern]: [Example: e.g., "kebab-case.ts for modules"]
|
|
||||||
- [Pattern]: [Example: e.g., "PascalCase.tsx for React components"]
|
|
||||||
- [Pattern]: [Example: e.g., "*.test.ts for test files"]
|
|
||||||
|
|
||||||
**Directories:**
|
|
||||||
- [Pattern]: [Example: e.g., "kebab-case for feature directories"]
|
|
||||||
- [Pattern]: [Example: e.g., "plural names for collections"]
|
|
||||||
|
|
||||||
**Special Patterns:**
|
|
||||||
- [Pattern]: [Example: e.g., "index.ts for directory exports"]
|
|
||||||
- [Pattern]: [Example: e.g., "__tests__ for test directories"]
|
|
||||||
|
|
||||||
## Where to Add New Code
|
|
||||||
|
|
||||||
**New Feature:**
|
|
||||||
- Primary code: [Directory path]
|
|
||||||
- Tests: [Directory path]
|
|
||||||
- Config if needed: [Directory path]
|
|
||||||
|
|
||||||
**New Component/Module:**
|
|
||||||
- Implementation: [Directory path]
|
|
||||||
- Types: [Directory path]
|
|
||||||
- Tests: [Directory path]
|
|
||||||
|
|
||||||
**New Route/Command:**
|
|
||||||
- Definition: [Directory path]
|
|
||||||
- Handler: [Directory path]
|
|
||||||
- Tests: [Directory path]
|
|
||||||
|
|
||||||
**Utilities:**
|
|
||||||
- Shared helpers: [Directory path]
|
|
||||||
- Type definitions: [Directory path]
|
|
||||||
|
|
||||||
## Special Directories
|
|
||||||
|
|
||||||
[Any directories with special meaning or generation]
|
|
||||||
|
|
||||||
**[Directory]:**
|
|
||||||
- Purpose: [e.g., "Generated code", "Build output"]
|
|
||||||
- Source: [e.g., "Auto-generated by X", "Build artifacts"]
|
|
||||||
- Committed: [Yes/No - in .gitignore?]
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Structure analysis: [date]*
|
|
||||||
*Update when directory structure changes*
|
|
||||||
```
|
|
||||||
|
|
||||||
<good_examples>
|
|
||||||
```markdown
|
|
||||||
# Codebase Structure
|
|
||||||
|
|
||||||
**Analysis Date:** 2025-01-20
|
|
||||||
|
|
||||||
## Directory Layout
|
|
||||||
|
|
||||||
```
|
|
||||||
gsd-core/
|
|
||||||
├── bin/ # Executable entry points
|
|
||||||
├── commands/ # Slash command definitions
|
|
||||||
│ └── gsd/ # GSD-specific commands
|
|
||||||
├── gsd-core/ # Skill resources
|
|
||||||
│ ├── references/ # Principle documents
|
|
||||||
│ ├── templates/ # File templates
|
|
||||||
│ └── workflows/ # Multi-step procedures
|
|
||||||
├── src/ # Source code (if applicable)
|
|
||||||
├── tests/ # Test files
|
|
||||||
├── package.json # Project manifest
|
|
||||||
└── README.md # User documentation
|
|
||||||
```
|
|
||||||
|
|
||||||
## Directory Purposes
|
|
||||||
|
|
||||||
**bin/**
|
|
||||||
- Purpose: CLI entry points
|
|
||||||
- Contains: install.js (installer script)
|
|
||||||
- Key files: install.js - handles npx installation
|
|
||||||
- Subdirectories: None
|
|
||||||
|
|
||||||
**commands/gsd/**
|
|
||||||
- Purpose: Slash command definitions for Claude Code
|
|
||||||
- Contains: *.md files (one per command)
|
|
||||||
- Key files: new-project.md, plan-phase.md, execute-plan.md
|
|
||||||
- Subdirectories: None (flat structure)
|
|
||||||
|
|
||||||
**gsd-core/references/**
|
|
||||||
- Purpose: Core philosophy and guidance documents
|
|
||||||
- Contains: principles.md, questioning.md, plan-format.md
|
|
||||||
- Key files: principles.md - system philosophy
|
|
||||||
- Subdirectories: None
|
|
||||||
|
|
||||||
**gsd-core/templates/**
|
|
||||||
- Purpose: Document templates for .planning/ files
|
|
||||||
- Contains: Template definitions with frontmatter
|
|
||||||
- Key files: project.md, roadmap.md, plan.md, summary.md
|
|
||||||
- Subdirectories: codebase/ (new - for stack/architecture/structure templates)
|
|
||||||
|
|
||||||
**gsd-core/workflows/**
|
|
||||||
- Purpose: Reusable multi-step procedures
|
|
||||||
- Contains: Workflow definitions called by commands
|
|
||||||
- Key files: execute-plan.md, research-phase.md
|
|
||||||
- Subdirectories: None
|
|
||||||
|
|
||||||
## Key File Locations
|
|
||||||
|
|
||||||
**Entry Points:**
|
|
||||||
- `bin/install.js` - Installation script (npx entry)
|
|
||||||
|
|
||||||
**Configuration:**
|
|
||||||
- `package.json` - Project metadata, dependencies, bin entry
|
|
||||||
- `.gitignore` - Excluded files
|
|
||||||
|
|
||||||
**Core Logic:**
|
|
||||||
- `bin/install.js` - All installation logic (file copying, path replacement)
|
|
||||||
|
|
||||||
**Testing:**
|
|
||||||
- `tests/` - Test files (if present)
|
|
||||||
|
|
||||||
**Documentation:**
|
|
||||||
- `README.md` - User-facing installation and usage guide
|
|
||||||
- `CLAUDE.md` - Instructions for Claude Code when working in this repo
|
|
||||||
|
|
||||||
## Naming Conventions
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- kebab-case.md: Markdown documents
|
|
||||||
- kebab-case.js: JavaScript source files
|
|
||||||
- UPPERCASE.md: Important project files (README, CLAUDE, CHANGELOG)
|
|
||||||
|
|
||||||
**Directories:**
|
|
||||||
- kebab-case: All directories
|
|
||||||
- Plural for collections: templates/, commands/, workflows/
|
|
||||||
|
|
||||||
**Special Patterns:**
|
|
||||||
- {command-name}.md: Slash command definition
|
|
||||||
- *-template.md: Could be used but templates/ directory preferred
|
|
||||||
|
|
||||||
## Where to Add New Code
|
|
||||||
|
|
||||||
**New Slash Command:**
|
|
||||||
- Primary code: `commands/gsd/{command-name}.md`
|
|
||||||
- Tests: `tests/commands/{command-name}.test.js` (if testing implemented)
|
|
||||||
- Documentation: Update `README.md` with new command
|
|
||||||
|
|
||||||
**New Template:**
|
|
||||||
- Implementation: `gsd-core/templates/{name}.md`
|
|
||||||
- Documentation: Template is self-documenting (includes guidelines)
|
|
||||||
|
|
||||||
**New Workflow:**
|
|
||||||
- Implementation: `gsd-core/workflows/{name}.md`
|
|
||||||
- Usage: Reference from command with `@~/.claude/gsd-core/workflows/{name}.md`
|
|
||||||
|
|
||||||
**New Reference Document:**
|
|
||||||
- Implementation: `gsd-core/references/{name}.md`
|
|
||||||
- Usage: Reference from commands/workflows as needed
|
|
||||||
|
|
||||||
**Utilities:**
|
|
||||||
- No utilities yet (`install.js` is monolithic)
|
|
||||||
- If extracted: `src/utils/`
|
|
||||||
|
|
||||||
## Special Directories
|
|
||||||
|
|
||||||
**gsd-core/**
|
|
||||||
- Purpose: Resources installed to ~/.claude/
|
|
||||||
- Source: Copied by bin/install.js during installation
|
|
||||||
- Committed: Yes (source of truth)
|
|
||||||
|
|
||||||
**commands/**
|
|
||||||
- Purpose: Slash commands installed to ~/.claude/commands/
|
|
||||||
- Source: Copied by bin/install.js during installation
|
|
||||||
- Committed: Yes (source of truth)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Structure analysis: 2025-01-20*
|
|
||||||
*Update when directory structure changes*
|
|
||||||
```
|
|
||||||
</good_examples>
|
|
||||||
|
|
||||||
<guidelines>
|
|
||||||
**What belongs in STRUCTURE.md:**
|
|
||||||
- Directory layout (ASCII box-drawing tree for structure visualization)
|
|
||||||
- Purpose of each directory
|
|
||||||
- Key file locations (entry points, configs, core logic)
|
|
||||||
- Naming conventions
|
|
||||||
- Where to add new code (by type)
|
|
||||||
- Special/generated directories
|
|
||||||
|
|
||||||
**What does NOT belong here:**
|
|
||||||
- Conceptual architecture (that's ARCHITECTURE.md)
|
|
||||||
- Technology stack (that's STACK.md)
|
|
||||||
- Code implementation details (defer to code reading)
|
|
||||||
- Every single file (focus on directories and key files)
|
|
||||||
|
|
||||||
**When filling this template:**
|
|
||||||
- Use `tree -L 2` or similar to visualize structure
|
|
||||||
- Identify top-level directories and their purposes
|
|
||||||
- Note naming patterns by observing existing files
|
|
||||||
- Locate entry points, configs, and main logic areas
|
|
||||||
- Keep directory tree concise (max 2-3 levels)
|
|
||||||
|
|
||||||
**Tree format (ASCII box-drawing characters for structure only):**
|
|
||||||
```
|
|
||||||
root/
|
|
||||||
├── dir1/ # Purpose
|
|
||||||
│ ├── subdir/ # Purpose
|
|
||||||
│ └── file.ts # Purpose
|
|
||||||
├── dir2/ # Purpose
|
|
||||||
└── file.ts # Purpose
|
|
||||||
```
|
|
||||||
|
|
||||||
**Useful for phase planning when:**
|
|
||||||
- Adding new features (where should files go?)
|
|
||||||
- Understanding project organization
|
|
||||||
- Finding where specific logic lives
|
|
||||||
- Following existing conventions
|
|
||||||
</guidelines>
|
|
||||||
@@ -1,480 +0,0 @@
|
|||||||
# Testing Patterns Template
|
|
||||||
|
|
||||||
Template for `.planning/codebase/TESTING.md` - captures test framework and patterns.
|
|
||||||
|
|
||||||
**Purpose:** Document how tests are written and run. Guide for adding tests that match existing patterns.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Template
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# Testing Patterns
|
|
||||||
|
|
||||||
**Analysis Date:** [YYYY-MM-DD]
|
|
||||||
|
|
||||||
## Test Framework
|
|
||||||
|
|
||||||
**Runner:**
|
|
||||||
- [Framework: e.g., "Jest 29.x", "Vitest 1.x"]
|
|
||||||
- [Config: e.g., "jest.config.js in project root"]
|
|
||||||
|
|
||||||
**Assertion Library:**
|
|
||||||
- [Library: e.g., "built-in expect", "chai"]
|
|
||||||
- [Matchers: e.g., "toBe, toEqual, toThrow"]
|
|
||||||
|
|
||||||
**Run Commands:**
|
|
||||||
```bash
|
|
||||||
[e.g., "npm test" or "npm run test"] # Run all tests
|
|
||||||
[e.g., "npm test -- --watch"] # Watch mode
|
|
||||||
[e.g., "npm test -- path/to/file.test.ts"] # Single file
|
|
||||||
[e.g., "npm run test:coverage"] # Coverage report
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test File Organization
|
|
||||||
|
|
||||||
**Location:**
|
|
||||||
- [Pattern: e.g., "*.test.ts alongside source files"]
|
|
||||||
- [Alternative: e.g., "__tests__/ directory" or "separate tests/ tree"]
|
|
||||||
|
|
||||||
**Naming:**
|
|
||||||
- [Unit tests: e.g., "module-name.test.ts"]
|
|
||||||
- [Integration: e.g., "feature-name.integration.test.ts"]
|
|
||||||
- [E2E: e.g., "user-flow.e2e.test.ts"]
|
|
||||||
|
|
||||||
**Structure:**
|
|
||||||
```
|
|
||||||
[Show actual directory pattern, e.g.:
|
|
||||||
src/
|
|
||||||
lib/
|
|
||||||
utils.ts
|
|
||||||
utils.test.ts
|
|
||||||
services/
|
|
||||||
user-service.ts
|
|
||||||
user-service.test.ts
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Structure
|
|
||||||
|
|
||||||
**Suite Organization:**
|
|
||||||
```typescript
|
|
||||||
[Show actual pattern used, e.g.:
|
|
||||||
|
|
||||||
describe('ModuleName', () => {
|
|
||||||
describe('functionName', () => {
|
|
||||||
it('should handle success case', () => {
|
|
||||||
// arrange
|
|
||||||
// act
|
|
||||||
// assert
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle error case', () => {
|
|
||||||
// test code
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Patterns:**
|
|
||||||
- [Setup: e.g., "beforeEach for shared setup, avoid beforeAll"]
|
|
||||||
- [Teardown: e.g., "afterEach to clean up, restore mocks"]
|
|
||||||
- [Structure: e.g., "arrange/act/assert pattern required"]
|
|
||||||
|
|
||||||
## Mocking
|
|
||||||
|
|
||||||
**Framework:**
|
|
||||||
- [Tool: e.g., "Jest built-in mocking", "Vitest vi", "Sinon"]
|
|
||||||
- [Import mocking: e.g., "vi.mock() at top of file"]
|
|
||||||
|
|
||||||
**Patterns:**
|
|
||||||
```typescript
|
|
||||||
[Show actual mocking pattern, e.g.:
|
|
||||||
|
|
||||||
// Mock external dependency
|
|
||||||
vi.mock('./external-service', () => ({
|
|
||||||
fetchData: vi.fn()
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock in test
|
|
||||||
const mockFetch = vi.mocked(fetchData);
|
|
||||||
mockFetch.mockResolvedValue({ data: 'test' });
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
**What to Mock:**
|
|
||||||
- [e.g., "External APIs, file system, database"]
|
|
||||||
- [e.g., "Time/dates (use vi.useFakeTimers)"]
|
|
||||||
- [e.g., "Network calls (use mock fetch)"]
|
|
||||||
|
|
||||||
**What NOT to Mock:**
|
|
||||||
- [e.g., "Pure functions, utilities"]
|
|
||||||
- [e.g., "Internal business logic"]
|
|
||||||
|
|
||||||
## Fixtures and Factories
|
|
||||||
|
|
||||||
**Test Data:**
|
|
||||||
```typescript
|
|
||||||
[Show pattern for creating test data, e.g.:
|
|
||||||
|
|
||||||
// Factory pattern
|
|
||||||
function createTestUser(overrides?: Partial<User>): User {
|
|
||||||
return {
|
|
||||||
id: 'test-id',
|
|
||||||
name: 'Test User',
|
|
||||||
email: 'test@example.com',
|
|
||||||
...overrides
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fixture file
|
|
||||||
// tests/fixtures/users.ts
|
|
||||||
export const mockUsers = [/* ... */];
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Location:**
|
|
||||||
- [e.g., "tests/fixtures/ for shared fixtures"]
|
|
||||||
- [e.g., "factory functions in test file or tests/factories/"]
|
|
||||||
|
|
||||||
## Coverage
|
|
||||||
|
|
||||||
**Requirements:**
|
|
||||||
- [Target: e.g., "80% line coverage", "no specific target"]
|
|
||||||
- [Enforcement: e.g., "CI blocks <80%", "coverage for awareness only"]
|
|
||||||
|
|
||||||
**Configuration:**
|
|
||||||
- [Tool: e.g., "built-in coverage via --coverage flag"]
|
|
||||||
- [Exclusions: e.g., "exclude *.test.ts, config files"]
|
|
||||||
|
|
||||||
**View Coverage:**
|
|
||||||
```bash
|
|
||||||
[e.g., "npm run test:coverage"]
|
|
||||||
[e.g., "open coverage/index.html"]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Types
|
|
||||||
|
|
||||||
**Unit Tests:**
|
|
||||||
- [Scope: e.g., "test single function/class in isolation"]
|
|
||||||
- [Mocking: e.g., "mock all external dependencies"]
|
|
||||||
- [Speed: e.g., "must run in <1s per test"]
|
|
||||||
|
|
||||||
**Integration Tests:**
|
|
||||||
- [Scope: e.g., "test multiple modules together"]
|
|
||||||
- [Mocking: e.g., "mock external services, use real internal modules"]
|
|
||||||
- [Setup: e.g., "use test database, seed data"]
|
|
||||||
|
|
||||||
**E2E Tests:**
|
|
||||||
- [Framework: e.g., "Playwright for E2E"]
|
|
||||||
- [Scope: e.g., "test full user flows"]
|
|
||||||
- [Location: e.g., "e2e/ directory separate from unit tests"]
|
|
||||||
|
|
||||||
## Common Patterns
|
|
||||||
|
|
||||||
**Async Testing:**
|
|
||||||
```typescript
|
|
||||||
[Show pattern, e.g.:
|
|
||||||
|
|
||||||
it('should handle async operation', async () => {
|
|
||||||
const result = await asyncFunction();
|
|
||||||
expect(result).toBe('expected');
|
|
||||||
});
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Error Testing:**
|
|
||||||
```typescript
|
|
||||||
[Show pattern, e.g.:
|
|
||||||
|
|
||||||
it('should throw on invalid input', () => {
|
|
||||||
expect(() => functionCall()).toThrow('error message');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Async error
|
|
||||||
it('should reject on failure', async () => {
|
|
||||||
await expect(asyncCall()).rejects.toThrow('error message');
|
|
||||||
});
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Snapshot Testing:**
|
|
||||||
- [Usage: e.g., "for React components only" or "not used"]
|
|
||||||
- [Location: e.g., "__snapshots__/ directory"]
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Testing analysis: [date]*
|
|
||||||
*Update when test patterns change*
|
|
||||||
```
|
|
||||||
|
|
||||||
<good_examples>
|
|
||||||
```markdown
|
|
||||||
# Testing Patterns
|
|
||||||
|
|
||||||
**Analysis Date:** 2025-01-20
|
|
||||||
|
|
||||||
## Test Framework
|
|
||||||
|
|
||||||
**Runner:**
|
|
||||||
- Vitest 1.0.4
|
|
||||||
- Config: vitest.config.ts in project root
|
|
||||||
|
|
||||||
**Assertion Library:**
|
|
||||||
- Vitest built-in expect
|
|
||||||
- Matchers: toBe, toEqual, toThrow, toMatchObject
|
|
||||||
|
|
||||||
**Run Commands:**
|
|
||||||
```bash
|
|
||||||
npm test # Run all tests
|
|
||||||
npm test -- --watch # Watch mode
|
|
||||||
npm test -- path/to/file.test.ts # Single file
|
|
||||||
npm run test:coverage # Coverage report
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test File Organization
|
|
||||||
|
|
||||||
**Location:**
|
|
||||||
- *.test.ts alongside source files
|
|
||||||
- No separate tests/ directory
|
|
||||||
|
|
||||||
**Naming:**
|
|
||||||
- unit-name.test.ts for all tests
|
|
||||||
- No distinction between unit/integration in filename
|
|
||||||
|
|
||||||
**Structure:**
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
lib/
|
|
||||||
parser.ts
|
|
||||||
parser.test.ts
|
|
||||||
services/
|
|
||||||
install-service.ts
|
|
||||||
install-service.test.ts
|
|
||||||
bin/
|
|
||||||
install.ts
|
|
||||||
(no test - integration tested via CLI)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Structure
|
|
||||||
|
|
||||||
**Suite Organization:**
|
|
||||||
```typescript
|
|
||||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
||||||
|
|
||||||
describe('ModuleName', () => {
|
|
||||||
describe('functionName', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
// reset state
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle valid input', () => {
|
|
||||||
// arrange
|
|
||||||
const input = createTestInput();
|
|
||||||
|
|
||||||
// act
|
|
||||||
const result = functionName(input);
|
|
||||||
|
|
||||||
// assert
|
|
||||||
expect(result).toEqual(expectedOutput);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw on invalid input', () => {
|
|
||||||
expect(() => functionName(null)).toThrow('Invalid input');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**Patterns:**
|
|
||||||
- Use beforeEach for per-test setup, avoid beforeAll
|
|
||||||
- Use afterEach to restore mocks: vi.restoreAllMocks()
|
|
||||||
- Explicit arrange/act/assert comments in complex tests
|
|
||||||
- One assertion focus per test (but multiple expects OK)
|
|
||||||
|
|
||||||
## Mocking
|
|
||||||
|
|
||||||
**Framework:**
|
|
||||||
- Vitest built-in mocking (vi)
|
|
||||||
- Module mocking via vi.mock() at top of test file
|
|
||||||
|
|
||||||
**Patterns:**
|
|
||||||
```typescript
|
|
||||||
import { vi } from 'vitest';
|
|
||||||
import { externalFunction } from './external';
|
|
||||||
|
|
||||||
// Mock module
|
|
||||||
vi.mock('./external', () => ({
|
|
||||||
externalFunction: vi.fn()
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('test suite', () => {
|
|
||||||
it('mocks function', () => {
|
|
||||||
const mockFn = vi.mocked(externalFunction);
|
|
||||||
mockFn.mockReturnValue('mocked result');
|
|
||||||
|
|
||||||
// test code using mocked function
|
|
||||||
|
|
||||||
expect(mockFn).toHaveBeenCalledWith('expected arg');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**What to Mock:**
|
|
||||||
- File system operations (fs-extra)
|
|
||||||
- Child process execution (child_process.exec)
|
|
||||||
- External API calls
|
|
||||||
- Environment variables (process.env)
|
|
||||||
|
|
||||||
**What NOT to Mock:**
|
|
||||||
- Internal pure functions
|
|
||||||
- Simple utilities (string manipulation, array helpers)
|
|
||||||
- TypeScript types
|
|
||||||
|
|
||||||
## Fixtures and Factories
|
|
||||||
|
|
||||||
**Test Data:**
|
|
||||||
```typescript
|
|
||||||
// Factory functions in test file
|
|
||||||
function createTestConfig(overrides?: Partial<Config>): Config {
|
|
||||||
return {
|
|
||||||
targetDir: '/tmp/test',
|
|
||||||
global: false,
|
|
||||||
...overrides
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shared fixtures in tests/fixtures/
|
|
||||||
// tests/fixtures/sample-command.md
|
|
||||||
export const sampleCommand = `---
|
|
||||||
description: Test command
|
|
||||||
---
|
|
||||||
Content here`;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Location:**
|
|
||||||
- Factory functions: define in test file near usage
|
|
||||||
- Shared fixtures: tests/fixtures/ (for multi-file test data)
|
|
||||||
- Mock data: inline in test when simple, factory when complex
|
|
||||||
|
|
||||||
## Coverage
|
|
||||||
|
|
||||||
**Requirements:**
|
|
||||||
- No enforced coverage target
|
|
||||||
- Coverage tracked for awareness
|
|
||||||
- Focus on critical paths (parsers, service logic)
|
|
||||||
|
|
||||||
**Configuration:**
|
|
||||||
- Vitest coverage via c8 (built-in)
|
|
||||||
- Excludes: *.test.ts, bin/install.ts, config files
|
|
||||||
|
|
||||||
**View Coverage:**
|
|
||||||
```bash
|
|
||||||
npm run test:coverage
|
|
||||||
open coverage/index.html
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Types
|
|
||||||
|
|
||||||
**Unit Tests:**
|
|
||||||
- Test single function in isolation
|
|
||||||
- Mock all external dependencies (fs, child_process)
|
|
||||||
- Fast: each test <100ms
|
|
||||||
- Examples: parser.test.ts, validator.test.ts
|
|
||||||
|
|
||||||
**Integration Tests:**
|
|
||||||
- Test multiple modules together
|
|
||||||
- Mock only external boundaries (file system, process)
|
|
||||||
- Examples: install-service.test.ts (tests service + parser)
|
|
||||||
|
|
||||||
**E2E Tests:**
|
|
||||||
- Not currently used
|
|
||||||
- CLI integration tested manually
|
|
||||||
|
|
||||||
## Common Patterns
|
|
||||||
|
|
||||||
**Async Testing:**
|
|
||||||
```typescript
|
|
||||||
it('should handle async operation', async () => {
|
|
||||||
const result = await asyncFunction();
|
|
||||||
expect(result).toBe('expected');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**Error Testing:**
|
|
||||||
```typescript
|
|
||||||
it('should throw on invalid input', () => {
|
|
||||||
expect(() => parse(null)).toThrow('Cannot parse null');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Async error
|
|
||||||
it('should reject on file not found', async () => {
|
|
||||||
await expect(readConfig('invalid.txt')).rejects.toThrow('ENOENT');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**File System Mocking:**
|
|
||||||
```typescript
|
|
||||||
import { vi } from 'vitest';
|
|
||||||
import * as fs from 'fs-extra';
|
|
||||||
|
|
||||||
vi.mock('fs-extra');
|
|
||||||
|
|
||||||
it('mocks file system', () => {
|
|
||||||
vi.mocked(fs.readFile).mockResolvedValue('file content');
|
|
||||||
// test code
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**Snapshot Testing:**
|
|
||||||
- Not used in this codebase
|
|
||||||
- Prefer explicit assertions for clarity
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Testing analysis: 2025-01-20*
|
|
||||||
*Update when test patterns change*
|
|
||||||
```
|
|
||||||
</good_examples>
|
|
||||||
|
|
||||||
<guidelines>
|
|
||||||
**What belongs in TESTING.md:**
|
|
||||||
- Test framework and runner configuration
|
|
||||||
- Test file location and naming patterns
|
|
||||||
- Test structure (describe/it, beforeEach patterns)
|
|
||||||
- Mocking approach and examples
|
|
||||||
- Fixture/factory patterns
|
|
||||||
- Coverage requirements
|
|
||||||
- How to run tests (commands)
|
|
||||||
- Common testing patterns in actual code
|
|
||||||
|
|
||||||
**What does NOT belong here:**
|
|
||||||
- Specific test cases (defer to actual test files)
|
|
||||||
- Technology choices (that's STACK.md)
|
|
||||||
- CI/CD setup (that's deployment docs)
|
|
||||||
|
|
||||||
**When filling this template:**
|
|
||||||
- Check package.json scripts for test commands
|
|
||||||
- Find test config file (jest.config.js, vitest.config.ts)
|
|
||||||
- Read 3-5 existing test files to identify patterns
|
|
||||||
- Look for test utilities in tests/ or test-utils/
|
|
||||||
- Check for coverage configuration
|
|
||||||
- Document actual patterns used, not ideal patterns
|
|
||||||
|
|
||||||
**Useful for phase planning when:**
|
|
||||||
- Adding new features (write matching tests)
|
|
||||||
- Refactoring (maintain test patterns)
|
|
||||||
- Fixing bugs (add regression tests)
|
|
||||||
- Understanding verification approach
|
|
||||||
- Setting up test infrastructure
|
|
||||||
|
|
||||||
**Analysis approach:**
|
|
||||||
- Check package.json for test framework and scripts
|
|
||||||
- Read test config file for coverage, setup
|
|
||||||
- Examine test file organization (collocated vs separate)
|
|
||||||
- Review 5 test files for patterns (mocking, structure, assertions)
|
|
||||||
- Look for test utilities, fixtures, factories
|
|
||||||
- Note any test types (unit, integration, e2e)
|
|
||||||
- Document commands for running tests
|
|
||||||
</guidelines>
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
# Debug Subagent Prompt Template
|
|
||||||
|
|
||||||
Template for spawning gsd-debugger agent. The agent contains all debugging expertise - this template provides problem context only.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Template
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
<objective>
|
|
||||||
Investigate issue: {issue_id}
|
|
||||||
|
|
||||||
**Summary:** {issue_summary}
|
|
||||||
</objective>
|
|
||||||
|
|
||||||
<symptoms>
|
|
||||||
expected: {expected}
|
|
||||||
actual: {actual}
|
|
||||||
errors: {errors}
|
|
||||||
reproduction: {reproduction}
|
|
||||||
timeline: {timeline}
|
|
||||||
</symptoms>
|
|
||||||
|
|
||||||
<mode>
|
|
||||||
symptoms_prefilled: {true_or_false}
|
|
||||||
goal: {find_root_cause_only | find_and_fix}
|
|
||||||
</mode>
|
|
||||||
|
|
||||||
<debug_file>
|
|
||||||
Create: .planning/debug/{slug}.md
|
|
||||||
</debug_file>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Placeholders
|
|
||||||
|
|
||||||
| Placeholder | Source | Example |
|
|
||||||
|-------------|--------|---------|
|
|
||||||
| `{issue_id}` | Orchestrator-assigned | `auth-screen-dark` |
|
|
||||||
| `{issue_summary}` | User description | `Auth screen is too dark` |
|
|
||||||
| `{expected}` | From symptoms | `See logo clearly` |
|
|
||||||
| `{actual}` | From symptoms | `Screen is dark` |
|
|
||||||
| `{errors}` | From symptoms | `None in console` |
|
|
||||||
| `{reproduction}` | From symptoms | `Open /auth page` |
|
|
||||||
| `{timeline}` | From symptoms | `After recent deploy` |
|
|
||||||
| `{goal}` | Orchestrator sets | `find_and_fix` |
|
|
||||||
| `{slug}` | Generated | `auth-screen-dark` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
**From /gsd:debug:**
|
|
||||||
```python
|
|
||||||
Task(
|
|
||||||
prompt=filled_template,
|
|
||||||
subagent_type="gsd-debugger",
|
|
||||||
description="Debug {slug}"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
**From diagnose-issues (UAT):**
|
|
||||||
```python
|
|
||||||
Task(prompt=template, subagent_type="gsd-debugger", description="Debug UAT-001")
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Continuation
|
|
||||||
|
|
||||||
For checkpoints, spawn fresh agent with:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
<objective>
|
|
||||||
Continue debugging {slug}. Evidence is in the debug file.
|
|
||||||
</objective>
|
|
||||||
|
|
||||||
<prior_state>
|
|
||||||
Debug file: @.planning/debug/{slug}.md
|
|
||||||
</prior_state>
|
|
||||||
|
|
||||||
<checkpoint_response>
|
|
||||||
**Type:** {checkpoint_type}
|
|
||||||
**Response:** {user_response}
|
|
||||||
</checkpoint_response>
|
|
||||||
|
|
||||||
<mode>
|
|
||||||
goal: {goal}
|
|
||||||
</mode>
|
|
||||||
```
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
# Discovery Template
|
|
||||||
|
|
||||||
Template for `.planning/phases/XX-name/DISCOVERY.md` - shallow research for library/option decisions.
|
|
||||||
|
|
||||||
**Purpose:** Answer "which library/option should we use" questions during mandatory discovery in plan-phase.
|
|
||||||
|
|
||||||
For deep ecosystem research ("how do experts build this"), use `/gsd:plan-phase --research-phase` which produces RESEARCH.md.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Template
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
---
|
|
||||||
phase: XX-name
|
|
||||||
type: discovery
|
|
||||||
topic: [discovery-topic]
|
|
||||||
---
|
|
||||||
|
|
||||||
<session_initialization>
|
|
||||||
Before beginning discovery, verify today's date:
|
|
||||||
!`date +%Y-%m-%d`
|
|
||||||
|
|
||||||
Use this date when searching for "current" or "latest" information.
|
|
||||||
Example: If today is 2025-11-22, search for "2025" not "2024".
|
|
||||||
</session_initialization>
|
|
||||||
|
|
||||||
<discovery_objective>
|
|
||||||
Discover [topic] to inform [phase name] implementation.
|
|
||||||
|
|
||||||
Purpose: [What decision/implementation this enables]
|
|
||||||
Scope: [Boundaries]
|
|
||||||
Output: DISCOVERY.md with recommendation
|
|
||||||
</discovery_objective>
|
|
||||||
|
|
||||||
<discovery_scope>
|
|
||||||
<include>
|
|
||||||
- [Question to answer]
|
|
||||||
- [Area to investigate]
|
|
||||||
- [Specific comparison if needed]
|
|
||||||
</include>
|
|
||||||
|
|
||||||
<exclude>
|
|
||||||
- [Out of scope for this discovery]
|
|
||||||
- [Defer to implementation phase]
|
|
||||||
</exclude>
|
|
||||||
</discovery_scope>
|
|
||||||
|
|
||||||
<discovery_protocol>
|
|
||||||
|
|
||||||
**Source Priority:**
|
|
||||||
1. **Context7 MCP** - For library/framework documentation (current, authoritative)
|
|
||||||
2. **Official Docs** - For platform-specific or non-indexed libraries
|
|
||||||
3. **WebSearch** - For comparisons, trends, community patterns (verify all findings)
|
|
||||||
|
|
||||||
**Quality Checklist:**
|
|
||||||
Before completing discovery, verify:
|
|
||||||
- [ ] All claims have authoritative sources (Context7 or official docs)
|
|
||||||
- [ ] Negative claims ("X is not possible") verified with official documentation
|
|
||||||
- [ ] API syntax/configuration from Context7 or official docs (never WebSearch alone)
|
|
||||||
- [ ] WebSearch findings cross-checked with authoritative sources
|
|
||||||
- [ ] Recent updates/changelogs checked for breaking changes
|
|
||||||
- [ ] Alternative approaches considered (not just first solution found)
|
|
||||||
|
|
||||||
**Confidence Levels:**
|
|
||||||
- HIGH: Context7 or official docs confirm
|
|
||||||
- MEDIUM: WebSearch + Context7/official docs confirm
|
|
||||||
- LOW: WebSearch only or training knowledge only (mark for validation)
|
|
||||||
|
|
||||||
</discovery_protocol>
|
|
||||||
|
|
||||||
|
|
||||||
<output_structure>
|
|
||||||
Create `.planning/phases/XX-name/DISCOVERY.md`:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# [Topic] Discovery
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
[2-3 paragraph executive summary - what was researched, what was found, what's recommended]
|
|
||||||
|
|
||||||
## Primary Recommendation
|
|
||||||
[What to do and why - be specific and actionable]
|
|
||||||
|
|
||||||
## Alternatives Considered
|
|
||||||
[What else was evaluated and why not chosen]
|
|
||||||
|
|
||||||
## Key Findings
|
|
||||||
|
|
||||||
### [Category 1]
|
|
||||||
- [Finding with source URL and relevance to our case]
|
|
||||||
|
|
||||||
### [Category 2]
|
|
||||||
- [Finding with source URL and relevance]
|
|
||||||
|
|
||||||
## Code Examples
|
|
||||||
[Relevant implementation patterns, if applicable]
|
|
||||||
|
|
||||||
## Metadata
|
|
||||||
|
|
||||||
<metadata>
|
|
||||||
<confidence level="high|medium|low">
|
|
||||||
[Why this confidence level - based on source quality and verification]
|
|
||||||
</confidence>
|
|
||||||
|
|
||||||
<sources>
|
|
||||||
- [Primary authoritative sources used]
|
|
||||||
</sources>
|
|
||||||
|
|
||||||
<open_questions>
|
|
||||||
[What couldn't be determined or needs validation during implementation]
|
|
||||||
</open_questions>
|
|
||||||
|
|
||||||
<validation_checkpoints>
|
|
||||||
[If confidence is LOW or MEDIUM, list specific things to verify during implementation]
|
|
||||||
</validation_checkpoints>
|
|
||||||
</metadata>
|
|
||||||
```
|
|
||||||
</output_structure>
|
|
||||||
|
|
||||||
<success_criteria>
|
|
||||||
- All scope questions answered with authoritative sources
|
|
||||||
- Quality checklist items completed
|
|
||||||
- Clear primary recommendation
|
|
||||||
- Low-confidence findings marked with validation checkpoints
|
|
||||||
- Ready to inform PLAN.md creation
|
|
||||||
</success_criteria>
|
|
||||||
|
|
||||||
<guidelines>
|
|
||||||
**When to use discovery:**
|
|
||||||
- Technology choice unclear (library A vs B)
|
|
||||||
- Best practices needed for unfamiliar integration
|
|
||||||
- API/library investigation required
|
|
||||||
- Single decision pending
|
|
||||||
|
|
||||||
**When NOT to use:**
|
|
||||||
- Established patterns (CRUD, auth with known library)
|
|
||||||
- Implementation details (defer to execution)
|
|
||||||
- Questions answerable from existing project context
|
|
||||||
|
|
||||||
**When to use RESEARCH.md instead:**
|
|
||||||
- Niche/complex domains (3D, games, audio, shaders)
|
|
||||||
- Need ecosystem knowledge, not just library choice
|
|
||||||
- "How do experts build this" questions
|
|
||||||
- Use `/gsd:plan-phase --research-phase` for these
|
|
||||||
</guidelines>
|
|
||||||
212
gsd-core/templates/summary.compact.md
Normal file
212
gsd-core/templates/summary.compact.md
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
# Summary Template
|
||||||
|
|
||||||
|
Template for `.planning/phases/XX-name/{phase}-{plan}-SUMMARY.md` - phase completion documentation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Template
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
phase: XX-name
|
||||||
|
plan: YY
|
||||||
|
subsystem: [primary category: auth, payments, ui, api, database, infra, testing, etc.]
|
||||||
|
tags: [searchable tech: jwt, stripe, react, postgres, prisma]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: [prior phase this depends on]
|
||||||
|
provides: [what that phase built that this uses]
|
||||||
|
provides:
|
||||||
|
- [bullet list of what this phase built/delivered]
|
||||||
|
affects: [list of phase names or keywords that will need this context]
|
||||||
|
|
||||||
|
# Actuals (#2632) — pairs with the plan's `estimate` to calibrate future estimates.
|
||||||
|
# Same estimateTokens scale (chars/4 over the realized diff), never a harness token count.
|
||||||
|
actuals:
|
||||||
|
tokens: [chars/4 over files actually changed]
|
||||||
|
tasks: [tasks completed]
|
||||||
|
commits: [commits made]
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: [libraries/tools added in this phase]
|
||||||
|
patterns: [architectural/code patterns established]
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created: [important files created]
|
||||||
|
modified: [important files modified]
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Decision 1"
|
||||||
|
- "Decision 2"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Pattern 1: description"
|
||||||
|
- "Pattern 2: description"
|
||||||
|
|
||||||
|
requirements-completed: [] # REQUIRED — Copy ALL requirement IDs from this plan's `requirements` frontmatter field.
|
||||||
|
|
||||||
|
# Coverage metadata (#1602) — one entry per shipped deliverable. Drives DETERMINISTIC UAT routing in verify-work.
|
||||||
|
# OMIT this whole block for legacy/prose-only SUMMARYs — verify-work then falls back to the ## Accomplishments bullets
|
||||||
|
# (byte-identical behavior for un-migrated phases). See <coverage_guidance> below for the contract.
|
||||||
|
coverage:
|
||||||
|
- id: D1
|
||||||
|
description: "[deliverable in human-readable form — what would have been a prose ## Accomplishments bullet]"
|
||||||
|
requirement: "[REQ-ID from this plan's `requirements`, or omit if none]"
|
||||||
|
verification:
|
||||||
|
- kind: unit # unit | integration | e2e | automated_ui | manual_procedural | other
|
||||||
|
ref: "[tests/path.test.ts#test name | playwright:shot.png | command invocation]"
|
||||||
|
status: pass # pass | fail | unknown — from the latest run
|
||||||
|
human_judgment: false # REQUIRED boolean. false => may auto-pass IF every verification status is `pass`.
|
||||||
|
- id: D2
|
||||||
|
description: "[a deliverable that needs a human to sign off]"
|
||||||
|
verification: []
|
||||||
|
human_judgment: true
|
||||||
|
rationale: "[REQUIRED when human_judgment: true — why automation is insufficient]"
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: Xmin
|
||||||
|
completed: YYYY-MM-DD
|
||||||
|
status: complete
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase [X]: [Name] Summary
|
||||||
|
|
||||||
|
**[Substantive one-liner describing outcome - NOT "phase complete" or "implementation finished"]**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** [time] (e.g., 23 min, 1h 15m)
|
||||||
|
- **Started:** [ISO timestamp]
|
||||||
|
- **Completed:** [ISO timestamp]
|
||||||
|
- **Tasks:** [count completed]
|
||||||
|
- **Files modified:** [count]
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
- [Most important outcome]
|
||||||
|
- [Second key accomplishment]
|
||||||
|
- [Third if applicable]
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
Each task was committed atomically:
|
||||||
|
|
||||||
|
1. **Task 1: [task name]** - `abc123f` (feat/fix/test/refactor)
|
||||||
|
2. **Task 2: [task name]** - `def456g` (feat/fix/test/refactor)
|
||||||
|
3. **Task 3: [task name]** - `hij789k` (feat/fix/test/refactor)
|
||||||
|
|
||||||
|
**Plan metadata:** `lmn012o` (docs: complete plan)
|
||||||
|
|
||||||
|
_Note: TDD tasks may have multiple commits (test → feat → refactor)_
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
- `path/to/file.ts` - What it does
|
||||||
|
- `path/to/another.ts` - What it does
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
[Key decisions with brief rationale, or "None - followed plan as specified"]
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
[If no deviations: "None - plan executed exactly as written"]
|
||||||
|
|
||||||
|
[If deviations occurred:]
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Rule X - Category] Brief description**
|
||||||
|
- **Found during:** Task [N] ([task name])
|
||||||
|
- **Issue:** [What was wrong]
|
||||||
|
- **Fix:** [What was done]
|
||||||
|
- **Files modified:** [file paths]
|
||||||
|
- **Verification:** [How it was verified]
|
||||||
|
- **Committed in:** [hash] (part of task commit)
|
||||||
|
|
||||||
|
[... repeat for each auto-fix ...]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Total deviations:** [N] auto-fixed ([breakdown by rule])
|
||||||
|
**Impact on plan:** [Brief assessment - e.g., "All auto-fixes necessary for correctness/security. No scope creep."]
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
[Problems and how they were resolved, or "None"]
|
||||||
|
|
||||||
|
[Note: "Deviations from Plan" documents unplanned work that was handled automatically via deviation rules. "Issues Encountered" documents problems during planned work that required problem-solving.]
|
||||||
|
|
||||||
|
## User Setup Required
|
||||||
|
|
||||||
|
[If USER-SETUP.md was generated:]
|
||||||
|
**External services require manual configuration.** See [{phase}-USER-SETUP.md](./{phase}-USER-SETUP.md) for:
|
||||||
|
- Environment variables to add
|
||||||
|
- Dashboard configuration steps
|
||||||
|
- Verification commands
|
||||||
|
|
||||||
|
[If no USER-SETUP.md:]
|
||||||
|
None - no external service configuration required.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
[What's ready for next phase]
|
||||||
|
[Any blockers or concerns]
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: XX-name*
|
||||||
|
*Completed: [date]*
|
||||||
|
```
|
||||||
|
|
||||||
|
<frontmatter_guidance>
|
||||||
|
**Purpose:** Enable automatic context assembly via dependency graph. Frontmatter makes summary metadata machine-readable so plan-phase can scan all summaries quickly and select relevant ones based on dependencies (`requires`/`provides`/`affects` create the explicit links; transitive closure follows from them).
|
||||||
|
|
||||||
|
**Subsystem/Tags:** Primary categorization + searchable technical keywords, for detecting related phases and tech-stack awareness. **Key-files:** important files for @context references in PLAN.md. **Patterns:** established conventions future phases should maintain.
|
||||||
|
|
||||||
|
**Population:** Frontmatter is populated during summary creation in execute-plan.md. See `<step name="create_summary">` for field-by-field guidance.
|
||||||
|
|
||||||
|
**Status (#2830):** `status: complete` is the default — the plan finished. Use `status: halted` instead when the plan reached a designed stop (a gate failure, a spike concluding without expanding into the full build, or any other intentional non-completion) and intentionally left tasks unfinished. `halted` is machine-read: any plan whose `depends_on` (directly or transitively) names a halted plan is reported as blocked, not offered to the executor, until the halt is resolved and re-summarized as `complete`.
|
||||||
|
</frontmatter_guidance>
|
||||||
|
|
||||||
|
<coverage_guidance>
|
||||||
|
**Purpose (#1602):** The `coverage:` block is a per-deliverable Requirements Traceability Matrix. It lets `verify-work`'s `extract_tests` step route deliverables DETERMINISTICALLY — auto-passing those proven by passing tests and reserving human UAT for genuine judgment — instead of re-deriving coverage from prose. Consumed via `gsd-tools uat classify-coverage --summary <SUMMARY>`.
|
||||||
|
|
||||||
|
**Field semantics:**
|
||||||
|
|
||||||
|
| Field | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `id` | Stable identifier (`D1`, `D2`…) for cross-referencing from UAT.md and audit reports. Must be unique within the SUMMARY. |
|
||||||
|
| `description` | The deliverable in human-readable form — what would have been a prose bullet. |
|
||||||
|
| `requirement` | Links back to a REQUIREMENTS.md REQ-ID (joins `requirements-completed`). Optional. |
|
||||||
|
| `verification[].kind` | Enum: `unit \| integration \| e2e \| automated_ui \| manual_procedural \| other`. |
|
||||||
|
| `verification[].ref` | Test path + descriptor (`file#test name`), Playwright screenshot ref, or command invocation. Required per entry. |
|
||||||
|
| `verification[].status` | `pass \| fail \| unknown` — populated from the latest test run. |
|
||||||
|
| `human_judgment` | Explicit boolean; REQUIRED. `true` always routes to a human. |
|
||||||
|
| `rationale` | REQUIRED when `human_judgment: true`. The audit trail for why automation is insufficient. |
|
||||||
|
|
||||||
|
**Deterministic contract (what the classifier does):**
|
||||||
|
- A deliverable auto-passes (no human prompt) **only** when `human_judgment: false` AND `verification` is non-empty AND every `verification[].status` is `pass`. This is the narrow, fully-proven case.
|
||||||
|
- **Everything else is presented to a human** — `human_judgment: true`, an empty `verification:`, any non-`pass`/`unknown` status, or any schema error. A false-negative is a redundant prompt (the status quo); a false-positive ships a bug UAT existed to catch.
|
||||||
|
- **Fail-safe default:** if you cannot determine coverage for a deliverable, you MUST set `human_judgment: true` with `rationale: "Coverage not determined at authoring time — verifier must classify"`. Never leave a deliverable's `human_judgment` empty, and never set it `false` just to skip the prompt — auto-pass additionally requires a passing `verification` entry, so the flag alone cannot skip the human.
|
||||||
|
- `coverage: []` means "no deliverables to classify" (the single-confirmation path). OMITTING the block entirely means "legacy" — `verify-work` falls back to prose `## Accomplishments` extraction unchanged.
|
||||||
|
</coverage_guidance>
|
||||||
|
|
||||||
|
<one_liner_rules>
|
||||||
|
The one-liner MUST be substantive:
|
||||||
|
|
||||||
|
**Good:** "JWT auth with refresh rotation using jose library" · "Prisma schema with User, Session, and Product models" · "Dashboard with real-time metrics via Server-Sent Events"
|
||||||
|
|
||||||
|
**Bad:** "Phase complete" · "Authentication implemented" · "Foundation finished" · "All tasks done"
|
||||||
|
|
||||||
|
The one-liner should tell someone what actually shipped.
|
||||||
|
</one_liner_rules>
|
||||||
|
|
||||||
|
<guidelines>
|
||||||
|
**Frontmatter:** MANDATORY - complete all fields. Enables automatic context assembly for future planning.
|
||||||
|
|
||||||
|
**One-liner:** Must be substantive. "JWT auth with refresh rotation using jose library" not "Authentication implemented".
|
||||||
|
|
||||||
|
**Decisions section:**
|
||||||
|
- Key decisions made during execution with rationale
|
||||||
|
- Extracted to STATE.md accumulated context
|
||||||
|
- Use "None - followed plan as specified" if no deviations
|
||||||
|
|
||||||
|
**After creation:** STATE.md updated with position, decisions, issues.
|
||||||
|
</guidelines>
|
||||||
199
gsd-core/templates/user-setup.compact.md
Normal file
199
gsd-core/templates/user-setup.compact.md
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
# User Setup Template
|
||||||
|
|
||||||
|
Template for `.planning/phases/XX-name/{phase}-USER-SETUP.md` - human-required configuration that Claude cannot automate.
|
||||||
|
|
||||||
|
**Purpose:** Document setup tasks that literally require human action - account creation, dashboard configuration, secret retrieval. Claude automates everything possible; this file captures only what remains.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Template
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Phase {X}: User Setup Required
|
||||||
|
|
||||||
|
**Generated:** [YYYY-MM-DD]
|
||||||
|
**Phase:** {phase-name}
|
||||||
|
**Status:** Incomplete
|
||||||
|
|
||||||
|
Complete these items for the integration to function. Claude automated everything possible; these items require human access to external dashboards/accounts.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
| Status | Variable | Source | Add to |
|
||||||
|
|--------|----------|--------|--------|
|
||||||
|
| [ ] | `ENV_VAR_NAME` | [Service Dashboard → Path → To → Value] | `.env.local` |
|
||||||
|
| [ ] | `ANOTHER_VAR` | [Service Dashboard → Path → To → Value] | `.env.local` |
|
||||||
|
|
||||||
|
## Account Setup
|
||||||
|
|
||||||
|
[Only if new account creation is required]
|
||||||
|
|
||||||
|
- [ ] **Create [Service] account**
|
||||||
|
- URL: [signup URL]
|
||||||
|
- Skip if: Already have account
|
||||||
|
|
||||||
|
## Dashboard Configuration
|
||||||
|
|
||||||
|
[Only if dashboard configuration is required]
|
||||||
|
|
||||||
|
- [ ] **[Configuration task]**
|
||||||
|
- Location: [Service Dashboard → Path → To → Setting]
|
||||||
|
- Set to: [Required value or configuration]
|
||||||
|
- Notes: [Any important details]
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
After completing setup, verify with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# [Verification commands]
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected results:
|
||||||
|
- [What success looks like]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Once all items complete:** Mark status as "Complete" at top of file.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When to Generate
|
||||||
|
|
||||||
|
Generate `{phase}-USER-SETUP.md` when plan frontmatter contains `user_setup` field.
|
||||||
|
|
||||||
|
**Trigger:** `user_setup` exists in PLAN.md frontmatter and has items.
|
||||||
|
|
||||||
|
**Location:** Same directory as PLAN.md and SUMMARY.md.
|
||||||
|
|
||||||
|
**Timing:** Generated during execute-plan.md after tasks complete, before SUMMARY.md creation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Frontmatter Schema
|
||||||
|
|
||||||
|
In PLAN.md, `user_setup` declares human-required configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
user_setup:
|
||||||
|
- service: stripe
|
||||||
|
why: "Payment processing requires API keys"
|
||||||
|
env_vars:
|
||||||
|
- name: STRIPE_SECRET_KEY
|
||||||
|
source: "Stripe Dashboard → Developers → API keys → Secret key"
|
||||||
|
- name: STRIPE_WEBHOOK_SECRET
|
||||||
|
source: "Stripe Dashboard → Developers → Webhooks → Signing secret"
|
||||||
|
dashboard_config:
|
||||||
|
- task: "Create webhook endpoint"
|
||||||
|
location: "Stripe Dashboard → Developers → Webhooks → Add endpoint"
|
||||||
|
details: "URL: https://[your-domain]/api/webhooks/stripe, Events: checkout.session.completed, customer.subscription.*"
|
||||||
|
local_dev:
|
||||||
|
- "Run: stripe listen --forward-to localhost:3000/api/webhooks/stripe"
|
||||||
|
- "Use the webhook secret from CLI output for local testing"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Automation-First Rule
|
||||||
|
|
||||||
|
**USER-SETUP.md contains ONLY what Claude literally cannot do.**
|
||||||
|
|
||||||
|
| Claude CAN Do (not in USER-SETUP) | Claude CANNOT Do (→ USER-SETUP) |
|
||||||
|
|-----------------------------------|--------------------------------|
|
||||||
|
| `npm install stripe` | Create Stripe account |
|
||||||
|
| Write webhook handler code | Get API keys from dashboard |
|
||||||
|
| Create `.env.local` file structure | Copy actual secret values |
|
||||||
|
| Run `stripe listen` | Authenticate Stripe CLI (browser OAuth) |
|
||||||
|
| Configure package.json | Access external service dashboards |
|
||||||
|
| Write any code | Retrieve secrets from third-party systems |
|
||||||
|
|
||||||
|
**The test:** "Does this require a human in a browser, accessing an account Claude doesn't have credentials for?"
|
||||||
|
- Yes → USER-SETUP.md
|
||||||
|
- No → Claude does it automatically
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Service-Specific Example
|
||||||
|
|
||||||
|
<stripe_example>
|
||||||
|
```markdown
|
||||||
|
# Phase 10: User Setup Required
|
||||||
|
|
||||||
|
**Generated:** 2025-01-14
|
||||||
|
**Phase:** 10-monetization
|
||||||
|
**Status:** Incomplete
|
||||||
|
|
||||||
|
Complete these items for Stripe integration to function.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
| Status | Variable | Source | Add to |
|
||||||
|
|--------|----------|--------|--------|
|
||||||
|
| [ ] | `STRIPE_SECRET_KEY` | Stripe Dashboard → Developers → API keys → Secret key | `.env.local` |
|
||||||
|
| [ ] | `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | Stripe Dashboard → Developers → API keys → Publishable key | `.env.local` |
|
||||||
|
| [ ] | `STRIPE_WEBHOOK_SECRET` | Stripe Dashboard → Developers → Webhooks → [endpoint] → Signing secret | `.env.local` |
|
||||||
|
|
||||||
|
## Account Setup
|
||||||
|
|
||||||
|
- [ ] **Create Stripe account** (if needed)
|
||||||
|
- URL: https://dashboard.stripe.com/register
|
||||||
|
- Skip if: Already have Stripe account
|
||||||
|
|
||||||
|
## Dashboard Configuration
|
||||||
|
|
||||||
|
- [ ] **Create webhook endpoint**
|
||||||
|
- Location: Stripe Dashboard → Developers → Webhooks → Add endpoint
|
||||||
|
- Endpoint URL: `https://[your-domain]/api/webhooks/stripe`
|
||||||
|
- Events to send:
|
||||||
|
- `checkout.session.completed`
|
||||||
|
- `customer.subscription.created`
|
||||||
|
- `customer.subscription.updated`
|
||||||
|
- `customer.subscription.deleted`
|
||||||
|
|
||||||
|
- [ ] **Create products and prices** (if using subscription tiers)
|
||||||
|
- Location: Stripe Dashboard → Products → Add product
|
||||||
|
- Create each subscription tier
|
||||||
|
- Copy Price IDs to:
|
||||||
|
- `STRIPE_STARTER_PRICE_ID`
|
||||||
|
- `STRIPE_PRO_PRICE_ID`
|
||||||
|
|
||||||
|
## Local Development
|
||||||
|
|
||||||
|
For local webhook testing:
|
||||||
|
```bash
|
||||||
|
stripe listen --forward-to localhost:3000/api/webhooks/stripe
|
||||||
|
```
|
||||||
|
Use the webhook signing secret from CLI output (starts with `whsec_`).
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
After completing setup:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify build passes
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Test webhook endpoint (should return 400 bad signature, not 500 crash)
|
||||||
|
curl -X POST http://localhost:3000/api/webhooks/stripe \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: Build passes, webhook returns 400 (signature validation working).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Once all items complete:** Mark status as "Complete" at top of file.
|
||||||
|
```
|
||||||
|
</stripe_example>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Guidelines
|
||||||
|
|
||||||
|
**Never include:** Actual secret values. Steps Claude can automate (package installs, code changes).
|
||||||
|
|
||||||
|
**Naming:** `{phase}-USER-SETUP.md` matches the phase number pattern.
|
||||||
|
**Status tracking:** User marks checkboxes and updates status line when complete.
|
||||||
|
**Searchability:** `grep -r "USER-SETUP" .planning/` finds all phases with user requirements.
|
||||||
@@ -288,7 +288,7 @@ Mode resolution:
|
|||||||
| architecture | docs/architecture/overview.md | create | new directory |
|
| architecture | docs/architecture/overview.md | create | new directory |
|
||||||
| getting_started | docs/guides/getting-started.md | update | found, hand-written |
|
| getting_started | docs/guides/getting-started.md | update | found, hand-written |
|
||||||
| development | docs/guides/development.md | create | matched docs/guides/ |
|
| development | docs/guides/development.md | create | matched docs/guides/ |
|
||||||
| testing | docs/guides/testing.md | create | matched docs/guides/ |
|
| contributing | docs/guides/contributing.md | create | matched docs/guides/ |
|
||||||
| configuration | docs/guides/configuration.md | create | matched docs/guides/ |
|
| configuration | docs/guides/configuration.md | create | matched docs/guides/ |
|
||||||
| api | docs/api/reference.md | create | new directory |
|
| api | docs/api/reference.md | create | new directory |
|
||||||
| deployment | docs/guides/deployment.md | update | found, hand-written |
|
| deployment | docs/guides/deployment.md | update | found, hand-written |
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ fi
|
|||||||
grep -A 50 "^user_setup:" .planning/phases/XX-name/{phase}-{plan}-PLAN.md | head -50
|
grep -A 50 "^user_setup:" .planning/phases/XX-name/{phase}-{plan}-PLAN.md | head -50
|
||||||
```
|
```
|
||||||
|
|
||||||
If user_setup exists: create `{phase}-USER-SETUP.md` using template `~/.claude/gsd-core/templates/user-setup.md`. Per service: env vars table, account setup checklist, dashboard config, local dev notes, verification commands. Status "Incomplete". Set `USER_SETUP_CREATED=true`. If empty/missing: skip.
|
If user_setup exists: create `{phase}-USER-SETUP.md` using the template at `~/.claude/gsd-core/templates/user-setup.md` (or its `~/.claude/gsd-core/templates/user-setup.compact.md` variant — resolve per `~/.claude/gsd-core/references/compact-content-gate.md` §"Streams 1b and 4"). Per service: env vars table, account setup checklist, dashboard config, local dev notes, verification commands. Status "Incomplete". Set `USER_SETUP_CREATED=true`. If empty/missing: skip.
|
||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="create_summary">
|
<step name="create_summary">
|
||||||
@@ -409,7 +409,7 @@ emit narrative output between the Write tool call and the commit tool call.
|
|||||||
Truncation at this boundary is a known failure mode (see #2070 rescue logic in
|
Truncation at this boundary is a known failure mode (see #2070 rescue logic in
|
||||||
execute-phase.md step 5.5).
|
execute-phase.md step 5.5).
|
||||||
|
|
||||||
Create `{phase}-{plan}-SUMMARY.md` at `.planning/phases/XX-name/`. Use `~/.claude/gsd-core/templates/summary.md`.
|
Create `{phase}-{plan}-SUMMARY.md` at `.planning/phases/XX-name/`. Use the template at `~/.claude/gsd-core/templates/summary.md` (or its `~/.claude/gsd-core/templates/summary.compact.md` variant — resolve per `~/.claude/gsd-core/references/compact-content-gate.md` §"Streams 1b and 4").
|
||||||
|
|
||||||
**Frontmatter:** phase, plan, subsystem, tags | requires/provides/affects | tech-stack.added/patterns | key-files.created/modified | key-decisions | requirements-completed (**MUST** copy `requirements` array from PLAN.md frontmatter verbatim) | duration ($DURATION), completed ($PLAN_END_TIME date).
|
**Frontmatter:** phase, plan, subsystem, tags | requires/provides/affects | tech-stack.added/patterns | key-files.created/modified | key-decisions | requirements-completed (**MUST** copy `requirements` array from PLAN.md frontmatter verbatim) | duration ($DURATION), completed ($PLAN_END_TIME date).
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Display GSD command help at the tier the user asked for. Output ONLY the referen
|
|||||||
| When `$ARGUMENTS` is | Read |
|
| When `$ARGUMENTS` is | Read |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `--brief` (or `-b`) alone | `workflows/help/modes/brief.md` |
|
| `--brief` (or `-b`) alone | `workflows/help/modes/brief.md` |
|
||||||
| `--full` (or `-f`, `--all`) alone | `workflows/help/modes/full.md` |
|
| `--full` (or `-f`, `--all`) alone | `workflows/help/modes/full.md` (or its `workflows/help/modes/full.compact.md` variant per `gsd-core/references/compact-content-gate.md` §"Streams 1b and 4 — variant resolution") |
|
||||||
| empty / unset | `workflows/help/modes/default.md` |
|
| empty / unset | `workflows/help/modes/default.md` |
|
||||||
| `--brief <topic>` (or `-b <topic>`) | `workflows/help/modes/topic.md` in compact scope (signature + one-line summary of the matched section) |
|
| `--brief <topic>` (or `-b <topic>`) | `workflows/help/modes/topic.md` in compact scope (signature + one-line summary of the matched section) |
|
||||||
| anything else — bare topic, `--full <topic>`, or topic with leading `--` | `workflows/help/modes/topic.md` in full scope (entire matched section) |
|
| anything else — bare topic, `--full <topic>`, or topic with leading `--` | `workflows/help/modes/topic.md` in full scope (entire matched section) |
|
||||||
|
|||||||
398
gsd-core/workflows/help/modes/full.compact.md
Normal file
398
gsd-core/workflows/help/modes/full.compact.md
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
Apply response_language to all user-facing prose — narration between tool calls, status updates, progress notes, and findings included; preserve code, paths, and identifiers.
|
||||||
|
|
||||||
|
<purpose>
|
||||||
|
Display the complete GSD Core command reference. Output ONLY the reference content. Do NOT add project-specific analysis, git status, next-step suggestions, or any commentary beyond the reference.
|
||||||
|
</purpose>
|
||||||
|
|
||||||
|
<reference>
|
||||||
|
# GSD Core Command Reference
|
||||||
|
|
||||||
|
**GSD Core** (Git. Ship. Done.) creates hierarchical project plans optimized for solo agentic development with Claude Code.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. `/gsd:new-project` — Initialize project (research, requirements, roadmap)
|
||||||
|
2. `/gsd:plan-phase 1` — Create detailed plan for first phase
|
||||||
|
3. `/gsd:execute-phase 1` — Execute the phase
|
||||||
|
|
||||||
|
Not sure where to start? `/gsd:next` reads your project state and routes you to the right next action.
|
||||||
|
|
||||||
|
### Smart Entry
|
||||||
|
|
||||||
|
**`/gsd:next`** — State-aware front door. Detects your situation via `gsd-tools smart-entry` (no-project, paused, blocked, planning, executing, needs-verify, idle, complete, …) and shows a menu with one recommended action. Launcher only; falls back to `/gsd:progress`.
|
||||||
|
|
||||||
|
Usage: `/gsd:next`
|
||||||
|
|
||||||
|
## Staying Updated
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @opengsd/gsd-core@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Core Workflow
|
||||||
|
|
||||||
|
```text
|
||||||
|
/gsd:new-project → /gsd:plan-phase → /gsd:execute-phase → repeat
|
||||||
|
```
|
||||||
|
|
||||||
|
### Project Initialization
|
||||||
|
|
||||||
|
**`/gsd:new-project`** — Unified flow from idea to ready-for-planning: deep questioning, optional domain research (4 parallel researchers), requirements with v1/v2/out-of-scope scoping, roadmap with phase breakdown. Creates `.planning/`: `PROJECT.md`, `config.json`, `research/`, `REQUIREMENTS.md`, `ROADMAP.md`, `STATE.md`.
|
||||||
|
|
||||||
|
Usage: `/gsd:new-project`
|
||||||
|
|
||||||
|
**`/gsd:onboard [--fast] [--text]`** — Guides first-time onboarding for an existing codebase: detects brownfield state, routes through `/gsd:map-codebase` → `/gsd:ingest-docs` → `/gsd:new-project` in safe order, idempotent.
|
||||||
|
|
||||||
|
Usage: `/gsd:onboard`
|
||||||
|
|
||||||
|
**`/gsd:map-codebase [--fast] [--focus <area>] [--query <term>]`** — Maps an existing codebase with parallel Explore agents into `.planning/codebase/` (stack, architecture, structure, conventions, testing, integrations, concerns). `--fast` for rapid assessment, `--query` to search the intel index.
|
||||||
|
|
||||||
|
Usage: `/gsd:map-codebase`
|
||||||
|
|
||||||
|
### Phase Planning
|
||||||
|
|
||||||
|
**`/gsd:discuss-phase <number> [--chain | --analyze | --power | --assumptions] [--batch[=N]]`** — Articulate your vision for a phase before planning; creates CONTEXT.md. `--chain` chained flow, `--analyze` assumption analysis, `--power` extended questions, `--assumptions` surfaces implementation assumptions non-interactively, `--batch` groups 2-5 questions per turn.
|
||||||
|
|
||||||
|
Usage: `/gsd:discuss-phase 2`
|
||||||
|
Usage: `/gsd:discuss-phase 2 --batch=3`
|
||||||
|
|
||||||
|
**`/gsd:plan-phase <number> [--research] [--skip-research] [--research-phase <N>] [--view] [--gaps] [--skip-verify] [--skip-ui] [--prd <file>] [--ingest <path-or-glob>] [--ingest-format <auto|nygard|madr|narrative>] [--reviews] [--text] [--bounce] [--skip-bounce] [--chunked] [--tdd] [--mvp] [--granularity <coarse|standard|fine>] [--no-tracer] [--no-reversibility-gates]`** — Creates `.planning/phases/XX-phase-name/XX-YY-PLAN.md` with concrete tasks, verification criteria, and success measures (multiple plans per phase supported).
|
||||||
|
|
||||||
|
Key flags: `--research-phase <N>` runs research only and writes `RESEARCH.md` then exits (replaces the deleted `gsd-research-phase`; `--research` forces refresh, `--view` prints existing without spawning). `--gaps` closes gaps from a prior plan-check. `--ingest`/`--ingest-format` pre-ingest external ADRs/PRDs/SPECs (see PRD Express Path). `--bounce`/`--skip-bounce` toggle the optional external refinement pass (`workflow.plan_bounce`). `--chunked` splits planning into short, individually-committed passes for crash resilience (`workflow.plan_chunked`), resumable. `--tdd` tests-before-code order. `--mvp` adds user story + Walking Skeleton (see `/gsd:mvp-phase`). `--granularity` overrides resolved plan granularity. `--no-tracer` opts out of tracer-first ordering. `--no-reversibility-gates` suppresses the one-way-door checkpoint for unattended runs.
|
||||||
|
|
||||||
|
Usage: `/gsd:plan-phase 1`
|
||||||
|
Result: Creates `.planning/phases/01-foundation/01-01-PLAN.md`
|
||||||
|
|
||||||
|
**PRD Express Path:** Pass `--prd path/to/requirements.md` to skip discuss-phase — your PRD becomes locked decisions in CONTEXT.md.
|
||||||
|
|
||||||
|
### Execution
|
||||||
|
|
||||||
|
**`/gsd:execute-phase <phase-number> [--wave N] [--gaps-only] [--tdd]`** — Groups plans by wave (frontmatter), executes sequentially with parallel plans per wave via Task tool, verifies phase goal, updates REQUIREMENTS/ROADMAP/STATE. `--wave N` runs only wave N; `--gaps-only` re-runs verifier-flagged plans; `--tdd` enforces test-driven order.
|
||||||
|
|
||||||
|
Usage: `/gsd:execute-phase 5`
|
||||||
|
Usage: `/gsd:execute-phase 5 --wave 2`
|
||||||
|
|
||||||
|
### Smart Router
|
||||||
|
|
||||||
|
**`/gsd:progress --do "<description>"`** — Routes freeform text to the best-matching GSD command; asks you to pick between top matches on ambiguity. Never does the work itself.
|
||||||
|
|
||||||
|
Usage: `/gsd:progress --do "fix the login button"`
|
||||||
|
|
||||||
|
### Quick Mode
|
||||||
|
|
||||||
|
**`/gsd:quick [--full] [--validate] [--discuss] [--research]`** — Small ad-hoc tasks in `.planning/quick/` (updates STATE.md, not ROADMAP.md); spawns planner+executor only by default. `--full` = discuss+research+plan-check+verify; `--validate` = plan-check + post-execution verify; `--discuss`/`--research` add one step each; flags compose.
|
||||||
|
|
||||||
|
Usage: `/gsd:quick`
|
||||||
|
Result: Creates `.planning/quick/NNN-slug/PLAN.md`, `.planning/quick/NNN-slug/NNN-slug-SUMMARY.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**`/gsd:quick-batch [--file <path>] [--jobs auto|N] [--validate] [--research] [--resume <batch-id>] [task list]`** — Batches several quick-shaped tasks (inline or `--file`); one coordinator plans/dispatches/merges. `--jobs` caps concurrency, `--resume` dispatches only eligible items; `--discuss`/`--full` are rejected.
|
||||||
|
|
||||||
|
Usage: `/gsd:quick-batch --jobs 3 --validate`
|
||||||
|
Result: Per-item artifacts under `.planning/quick/`; batch state in `.planning/quick-batches/<batch-id>/BATCH.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**`/gsd:fast [description]`** — Trivial task inline, no subagent, no planning files: typo fixes, config changes, ≤3 file edits (redirects to `/gsd:quick` above that). Atomic commit, logs to STATE.md.
|
||||||
|
|
||||||
|
Usage: `/gsd:fast "fix the typo in README"`
|
||||||
|
|
||||||
|
### Roadmap Management
|
||||||
|
|
||||||
|
**`/gsd:phase <description>`** — Appends a new phase (next sequential number) to ROADMAP.md.
|
||||||
|
|
||||||
|
Usage: `/gsd:phase "Add admin dashboard"`
|
||||||
|
|
||||||
|
**`/gsd:phase --insert <after> <description>`** — Inserts a decimal phase (e.g. 7.1) between existing phases for discovered mid-milestone work.
|
||||||
|
|
||||||
|
Usage: `/gsd:phase --insert 7 "Fix critical auth bug"`
|
||||||
|
Result: Creates Phase 7.1
|
||||||
|
|
||||||
|
**`/gsd:phase --remove <number>`** — Deletes a future (unstarted) phase and renumbers subsequent phases; git commit preserves history.
|
||||||
|
|
||||||
|
Usage: `/gsd:phase --remove 17`
|
||||||
|
Result: Phase 17 deleted, phases 18-20 become 17-19
|
||||||
|
|
||||||
|
**`/gsd:phase --edit <number> [--force]`** — Edits title/description/requirements/dependencies in place; `--force` allows editing already-started phases.
|
||||||
|
|
||||||
|
### Milestone Management
|
||||||
|
|
||||||
|
**`/gsd:new-milestone <name>`** — Mirrors `/gsd:new-project`'s flow for brownfield (existing PROJECT.md): questioning, optional research, requirements, roadmap. `--reset-phase-numbers` restarts at Phase 1 (archives old dirs first); `--ws <name>` scopes to a workstream, skipping the shared PROJECT.md write.
|
||||||
|
|
||||||
|
Usage: `/gsd:new-milestone "v2.0 Features"`
|
||||||
|
|
||||||
|
**`/gsd:complete-milestone <version>`** — Archives to MILESTONES.md + milestones/ dir, tags the release, preps workspace for next version.
|
||||||
|
|
||||||
|
Usage: `/gsd:complete-milestone 1.0.0`
|
||||||
|
|
||||||
|
### Progress Tracking
|
||||||
|
|
||||||
|
**`/gsd:progress [--next | --forensic | --do "<description>"]`** — Progress bar, SUMMARY recap, current position, key decisions, offers to execute/create next plan, detects 100% completion.
|
||||||
|
|
||||||
|
Modes: default (report+routing) · `--next` (auto-advance; `--force` bypasses safety gates) · `--next --auto` (chains steps until milestone completion or a blocking decision) · `--next --converge` (routes planning through `/gsd:plan-review-convergence`, requires `workflow.plan_review_convergence`; reviewer flags and `--max-cycles` forward) · `--forensic` (appends a 6-check integrity audit) · `--do "<text>"` (smart router, see above).
|
||||||
|
|
||||||
|
Usage: `/gsd:progress`
|
||||||
|
Usage: `/gsd:progress --next --auto`
|
||||||
|
|
||||||
|
### Session Management
|
||||||
|
|
||||||
|
**`/gsd:resume-work`** — Reads STATE.md, shows position and recent progress, offers next actions.
|
||||||
|
|
||||||
|
Usage: `/gsd:resume-work`
|
||||||
|
|
||||||
|
**`/gsd:pause-work [--report]`** — Creates a `.continue-here` handoff, updates STATE.md's session-continuity section. `--report` also writes a post-session summary to `.planning/reports/`.
|
||||||
|
|
||||||
|
Usage: `/gsd:pause-work`
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
**`/gsd:debug [issue description] [--diagnose]`** — Adaptive-question symptom gathering, `.planning/debug/[slug].md` tracking, scientific-method investigation, survives `/clear` (resume with no args), archives resolved issues. `--diagnose` runs a one-shot pass without a persistent session.
|
||||||
|
|
||||||
|
Usage: `/gsd:debug "login button doesn't work"`
|
||||||
|
|
||||||
|
### Spiking & Sketching
|
||||||
|
|
||||||
|
**`/gsd:spike [idea] [--quick]`** — Decomposes into 2-5 risk-ordered Given/When/Then experiments, builds minimum code, captures VALIDATED/INVALIDATED/PARTIAL, saves to `.planning/spikes/` with MANIFEST.md. Works in any repo, no `/gsd:new-project` needed. `--quick` skips decomposition.
|
||||||
|
|
||||||
|
Usage: `/gsd:spike "can we stream LLM output over WebSockets?"`
|
||||||
|
|
||||||
|
**`/gsd:sketch [idea] [--quick]`** — Conversational mood intake, 2-3 tabbed HTML variants per sketch, shared CSS theme system, saves to `.planning/sketches/` with MANIFEST.md. `--quick` skips mood intake.
|
||||||
|
|
||||||
|
Usage: `/gsd:sketch "dashboard layout for the admin panel"`
|
||||||
|
|
||||||
|
**`/gsd:spike --wrap-up`** — Curates spikes one-at-a-time (include/exclude/partial/UAT), generates a project skill under `./.claude/skills/spike-findings-[project]/`, writes `.planning/spikes/WRAP-UP-SUMMARY.md`, adds a CLAUDE.md auto-load line.
|
||||||
|
|
||||||
|
Usage: `/gsd:spike --wrap-up`
|
||||||
|
|
||||||
|
**`/gsd:sketch --wrap-up`** — Same curation flow for sketches, generating `./.claude/skills/sketch-findings-[project]/` with design decisions/CSS/HTML structures.
|
||||||
|
|
||||||
|
Usage: `/gsd:sketch --wrap-up`
|
||||||
|
|
||||||
|
### Capturing Ideas, Notes, and Todos
|
||||||
|
|
||||||
|
**`/gsd:capture [description]`** — Extracts context from conversation (or uses the given text), creates a todo in `.planning/todos/pending/`, infers area, checks duplicates, updates STATE.md count.
|
||||||
|
|
||||||
|
Usage: `/gsd:capture Add auth token refresh`
|
||||||
|
|
||||||
|
**`/gsd:capture --note <text>`** — Zero-friction timestamped note to `.planning/notes/` (or `~/.claude/notes/` globally). Subcommands: append (default), list, promote (note → todo). Works without a project.
|
||||||
|
|
||||||
|
Usage: `/gsd:capture --note refactor the hook system`
|
||||||
|
Usage: `/gsd:capture --note promote 3`
|
||||||
|
|
||||||
|
**`/gsd:capture --list [area]`** — Lists pending todos (optional area filter), loads full context for the one you pick, routes to work-now/add-to-phase/brainstorm, moves it to completed/ on start.
|
||||||
|
|
||||||
|
Usage: `/gsd:capture --list api`
|
||||||
|
|
||||||
|
**`/gsd:capture --list-seeds [status]`** — Read-only listing of captured seeds (ID, status, scope, trigger, title); optional status filter. Enrich via `/gsd:capture --seed --enrich SEED-NNN`.
|
||||||
|
|
||||||
|
Usage: `/gsd:capture --list-seeds dormant`
|
||||||
|
|
||||||
|
### User Acceptance Testing
|
||||||
|
|
||||||
|
**`/gsd:verify-work [phase]`** — Extracts testable deliverables from SUMMARY.md, presents tests one at a time (yes/no), auto-diagnoses failures into fix plans, ready for re-execution.
|
||||||
|
|
||||||
|
Usage: `/gsd:verify-work 3`
|
||||||
|
|
||||||
|
### Ship Work
|
||||||
|
|
||||||
|
**`/gsd:ship [phase]`** — Pushes branch, opens a PR with a body from SUMMARY/VERIFICATION/REQUIREMENTS, optionally requests review, updates STATE.md. Requires a verified phase and authenticated `gh`.
|
||||||
|
|
||||||
|
Usage: `/gsd:ship 4` or `/gsd:ship 4 --draft`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**`/gsd:review --phase N [--gemini] [--claude] [--codex] [--coderabbit] [--opencode] [--qwen] [--cursor] [--agy] [--all]`** — Detects available external AI CLIs, each independently reviews the phase's plans with the same structured prompt (CodeRabbit reviews the live diff, up to ~5 min), produces REVIEWS.md with consensus. Feed back via `/gsd:plan-phase N --reviews`.
|
||||||
|
|
||||||
|
Usage: `/gsd:review --phase 3 --all`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**`/gsd:pr-branch [target]`** — Classifies commits (code-only/planning-only/mixed), cherry-picks code onto a clean branch so reviewers see no `.planning/` artifacts.
|
||||||
|
|
||||||
|
Usage: `/gsd:pr-branch` or `/gsd:pr-branch main`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**`/gsd:capture --seed [idea]`** — Captures a forward-looking idea with WHY/WHEN-to-surface trigger conditions; auto-surfaces during `/gsd:new-milestone` when triggers match.
|
||||||
|
|
||||||
|
Usage: `/gsd:capture --seed "add real-time notifications when we build the events system"`
|
||||||
|
|
||||||
|
**`/gsd:capture --backlog [description]`** — Adds an idea to the 999.x backlog without committing to the current milestone; promote later via `/gsd:review-backlog`.
|
||||||
|
|
||||||
|
Usage: `/gsd:capture --backlog "real-time notifications when events ship"`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**`/gsd:audit-uat`** — Cross-phase audit of all outstanding UAT/verification items (pending, skipped, blocked, human_needed), cross-references the codebase for stale docs, produces a prioritized test plan. Run before a new milestone.
|
||||||
|
|
||||||
|
Usage: `/gsd:audit-uat`
|
||||||
|
|
||||||
|
### Milestone Auditing
|
||||||
|
|
||||||
|
**`/gsd:audit-milestone [version]`** — Reads all phase VERIFICATION.md files, checks requirements coverage, spawns an integration checker for cross-phase wiring, creates MILESTONE-AUDIT.md.
|
||||||
|
|
||||||
|
Usage: `/gsd:audit-milestone`
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
**`/gsd:settings`** — Interactively toggles researcher/plan-checker/verifier agents and the model profile (quality/balanced/budget/inherit); updates `.planning/config.json`.
|
||||||
|
|
||||||
|
Usage: `/gsd:settings`
|
||||||
|
|
||||||
|
**`/gsd:config [--profile <profile> | --advanced | --integrations]`** — `--profile` quick-switches model profile (`quality` = Opus everywhere but verification, `balanced` = Opus planning/Sonnet execution (default), `budget` = Sonnet writing/Haiku research-verification, `inherit` = current session model). `--advanced` = plan bounce, timeouts, branch templates, cross-AI execution. `--integrations` = third-party API keys, code-review CLI routing, agent-skill injection.
|
||||||
|
|
||||||
|
Usage: `/gsd:config --profile budget`
|
||||||
|
|
||||||
|
**`/gsd:surface [list|status|profile <name>|disable <cluster>|enable <cluster>|reset]`** — Toggles which skills are surfaced without reinstalling: `list`/`status` show enabled/disabled + token cost, `profile <name>` switches base profile (`core`/`standard`/`full`), `disable`/`enable` a cluster, `reset` returns to install-time profile.
|
||||||
|
|
||||||
|
Usage: `/gsd:surface profile standard`
|
||||||
|
|
||||||
|
### Utility Commands
|
||||||
|
|
||||||
|
**`/gsd:cleanup`** — Dry-run then moves completed-milestone phase dirs from `.planning/phases/` to `.planning/milestones/v{X.Y}-phases/`.
|
||||||
|
|
||||||
|
Usage: `/gsd:cleanup`
|
||||||
|
|
||||||
|
**`/gsd:help [--brief | --full | <topic> | --brief <topic>]`** — `--brief` = ~10-line refresher; no flag = one-page newcomer tour; `--full` = this complete reference; `<topic>` = matching section only (e.g. `/gsd:help debug`); `--brief <topic>` = compact scoped lookup. Every topic output starts with a `**Topic:** \`<alias>\` → \`<heading>\` *(scope: full | compact)*` preamble. See `gsd-core/workflows/help/modes/topic.md` for the alias table.
|
||||||
|
|
||||||
|
Usage: `/gsd:help debug`
|
||||||
|
Usage: `/gsd:help --brief debug`
|
||||||
|
|
||||||
|
**`/gsd:update [--sync] [--reapply] [--next | --rc]`** — Shows installed-vs-latest, changelog since your version, breaking changes, confirms before installing. `--sync` syncs managed skills across runtime roots; `--reapply` reapplies local modifications post-update; `--next`/`--rc` installs from the `@next` RC dist-tag (ADR #660) instead of `@latest`.
|
||||||
|
|
||||||
|
Usage: `/gsd:update`
|
||||||
|
|
||||||
|
## Additional Commands
|
||||||
|
|
||||||
|
Every command below is also a live `/gsd-*` slash command, grouped by purpose.
|
||||||
|
|
||||||
|
### Discovery & Specification
|
||||||
|
|
||||||
|
- **`/gsd:explore`** — Socratic ideation and idea routing before committing to plans.
|
||||||
|
- **`/gsd:spec-phase <phase> [--auto] [--text]`** — Clarify WHAT a phase delivers with ambiguity scoring; produces SPEC.md before discuss-phase.
|
||||||
|
- **`/gsd:ai-integration-phase [phase]`** — Generate an AI-SPEC.md design contract for phases building AI systems.
|
||||||
|
- **`/gsd:ui-phase [phase]`** — Generate UI design contract (UI-SPEC.md) for frontend phases.
|
||||||
|
- **`/gsd:import --from <filepath> | --from-gsd2`** — Ingest external plans with conflict detection, or reverse-migrate a GSD-2 project to v1 format.
|
||||||
|
- **`/gsd:ingest-docs [path] [--mode new|merge] [--manifest <file>] [--resolve auto|interactive]`** — Bootstrap or merge `.planning/` from existing ADRs/PRDs/SPECs/docs.
|
||||||
|
|
||||||
|
### Planning & Execution
|
||||||
|
|
||||||
|
- **`/gsd:mvp-phase <phase-number>`** — Plans a phase as a vertical MVP slice (user story + SPIDR splitting) before handoff to plan-phase; same end-state as `/gsd:plan-phase --mvp` with a guided intro.
|
||||||
|
- **`/gsd:ultraplan-phase [phase]`** — [BETA] Offload plan phase to Claude Code's ultraplan cloud; review in browser, import back.
|
||||||
|
- **`/gsd:plan-review-convergence <phase> [--gemini] [--claude] [--codex] [--coderabbit] [--opencode] [--qwen] [--cursor] [--agy/--antigravity] [--ollama] [--lm-studio] [--llama-cpp] [--kimi-code] [--all] [--text] [--ws <name>] [--max-cycles N]`** — Cross-AI convergence loop: replan with review feedback until no HIGH concerns remain (cloud and local-model reviewers).
|
||||||
|
- **`/gsd:autonomous [--from N] [--to N] [--only N] [--interactive] [--converge]`** — Runs all remaining phases unattended: discuss → plan → execute per phase; `--converge`/`--cross-ai` routes planning through convergence.
|
||||||
|
|
||||||
|
### Quality, Review & Verification
|
||||||
|
|
||||||
|
- **`/gsd:code-review <phase> [--depth=quick|standard|deep] [--files file1,file2,...] [--fix [--all] [--auto]]`** — Reviews phase-changed source for bugs, security, quality.
|
||||||
|
- **`/gsd:secure-phase [phase]`** — Retroactively verifies threat mitigations for a completed phase.
|
||||||
|
- **`/gsd:validate-phase [phase]`** — Retroactively audits and fills Nyquist validation gaps.
|
||||||
|
- **`/gsd:ui-review [phase]`** — Retroactive 6-pillar visual audit of implemented frontend code.
|
||||||
|
- **`/gsd:eval-review [phase]`** — Audits an executed AI phase's evaluation coverage; produces EVAL-REVIEW.md.
|
||||||
|
- **`/gsd:audit-fix --source <audit-uat> [--severity medium|high|all] [--max N] [--dry-run]`** — Autonomous audit-to-fix: find, classify, fix, test, commit.
|
||||||
|
- **`/gsd:add-tests <phase> [additional instructions]`** — Generates tests for a completed phase from UAT criteria and implementation.
|
||||||
|
|
||||||
|
### Diagnostics & Maintenance
|
||||||
|
|
||||||
|
- **`/gsd:health [--repair] [--context]`** — Diagnoses planning-directory health, optionally repairs.
|
||||||
|
- **`/gsd:forensics [problem description]`** — Post-mortem investigation for failed GSD workflows.
|
||||||
|
- **`/gsd:undo --last N | --phase NN | --plan NN-MM`** — Safe git revert using the phase manifest with dependency checks.
|
||||||
|
- **`/gsd:docs-update [--force] [--verify-only]`** — Generates/updates docs verified against the codebase.
|
||||||
|
- **`/gsd:extract-learnings <phase>`** — Extracts decisions, lessons, patterns, surprises from phase artifacts.
|
||||||
|
|
||||||
|
### Knowledge & Context
|
||||||
|
|
||||||
|
- **`/gsd:graphify [build|query <term>|status|diff]`** — Builds/queries/inspects the project knowledge graph in `.planning/graphs/`.
|
||||||
|
- **`/gsd:mempalace-recall`** — Recalls prior decisions/patterns/surprises from MemPalace before planning.
|
||||||
|
- **`/gsd:mempalace-capture [artifact-type]`** — Files a phase artifact into MemPalace, mirrors decisions into its temporal KG.
|
||||||
|
- **`/gsd:thread [list [--open|--resolved] | close <slug> | status <slug> | name | description]`** — Manages persistent context threads across sessions.
|
||||||
|
- **`/gsd:profile-user [--questionnaire] [--refresh]`** — Generates a developer behavioral profile + Claude-discoverable artifacts.
|
||||||
|
- **`/gsd:stats`** — Project statistics: phases, plans, requirements, git metrics, timeline.
|
||||||
|
|
||||||
|
### Workflow & Orchestration
|
||||||
|
|
||||||
|
- **`/gsd:manager [--analyze-deps]`** — Interactive command center for multiple phases from one terminal; `--analyze-deps` scans dependency relationships before parallel execution.
|
||||||
|
- **`/gsd:workspace [--new | --list | --remove] [name]`** — Creates/lists/removes isolated GSD workspace environments.
|
||||||
|
- **`/gsd:workstreams`** — List, create, switch, status, progress, complete, and resume parallel workstreams.
|
||||||
|
- **`/gsd:review-backlog`** — Reviews and promotes backlog items to the active milestone.
|
||||||
|
- **`/gsd:milestone-summary [version]`** — Comprehensive project summary from milestone artifacts, for onboarding/review.
|
||||||
|
|
||||||
|
### Repository Integration
|
||||||
|
|
||||||
|
- **`/gsd:inbox [--issues] [--prs] [--label] [--close-incomplete] [--repo owner/repo]`** — Triages open GitHub issues/PRs against project templates and contribution guidelines.
|
||||||
|
|
||||||
|
### Namespace Routers (model-facing meta-skills)
|
||||||
|
|
||||||
|
Six skills for two-stage hierarchical routing across 60+ skills; invoke directly to browse a category interactively:
|
||||||
|
|
||||||
|
- **`/gsd-context`** — Codebase intelligence (map, graphify, docs, learnings, mempalace).
|
||||||
|
- **`/gsd-ideate`** — Exploration/capture (explore, sketch, spike, spec, capture).
|
||||||
|
- **`/gsd-manage`** — Configuration/workspace (workstreams, thread, update, ship, inbox).
|
||||||
|
- **`/gsd-project`** — Project-lifecycle (milestones, audits, summary).
|
||||||
|
- **`/gsd-quality`** — Quality gates (code review, debug, audit, security, eval, ui).
|
||||||
|
- **`/gsd-workflow`** — Phase pipeline (discuss, plan, execute, verify, phase, progress).
|
||||||
|
|
||||||
|
## Files & Structure
|
||||||
|
|
||||||
|
```text
|
||||||
|
.planning/
|
||||||
|
├── PROJECT.md # Project vision
|
||||||
|
├── ROADMAP.md # Current phase breakdown
|
||||||
|
├── STATE.md # Project memory & context
|
||||||
|
├── RETROSPECTIVE.md # Living retrospective (updated per milestone)
|
||||||
|
├── config.json # Workflow mode & gates
|
||||||
|
├── todos/ # Captured ideas and tasks (pending/, completed/)
|
||||||
|
├── spikes/ # Spike experiments — MANIFEST.md + NNN-name/ dirs
|
||||||
|
├── sketches/ # Design sketches — MANIFEST.md, themes/, NNN-name/ dirs
|
||||||
|
├── debug/ # Active debug sessions (resolved/ archive)
|
||||||
|
├── milestones/ # Archived roadmap/requirements snapshots + v{X.Y}-phases/
|
||||||
|
├── codebase/ # Codebase map (brownfield): STACK/ARCHITECTURE/STRUCTURE/
|
||||||
|
│ # CONVENTIONS/TESTING/INTEGRATIONS/CONCERNS.md
|
||||||
|
└── phases/ # 01-foundation/01-01-PLAN.md + -SUMMARY.md, etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow Modes
|
||||||
|
|
||||||
|
Set during `/gsd:new-project`, changeable anytime in `.planning/config.json`:
|
||||||
|
|
||||||
|
- **Interactive** — confirms each major decision, pauses at checkpoints, more guidance.
|
||||||
|
- **YOLO** — auto-approves most decisions, executes without confirmation, stops only for critical checkpoints.
|
||||||
|
|
||||||
|
## Planning Configuration
|
||||||
|
|
||||||
|
`.planning/config.json`:
|
||||||
|
|
||||||
|
- **`planning.commit_docs`** (default `true`) — `false` keeps planning artifacts local-only (add `.planning/` to `.gitignore`); useful for OSS/client projects wanting private planning.
|
||||||
|
- **`planning.search_gitignored`** (default `false`) — `true` adds `--no-ignore` to broad ripgrep searches when `.planning/` is gitignored.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"planning": {
|
||||||
|
"commit_docs": false,
|
||||||
|
"search_gitignored": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Workflows
|
||||||
|
|
||||||
|
**New project:** `/gsd:new-project` → `/clear` → `/gsd:plan-phase 1` → `/clear` → `/gsd:execute-phase 1`
|
||||||
|
|
||||||
|
**Resuming:** `/gsd:progress`
|
||||||
|
|
||||||
|
**Urgent mid-milestone work:** `/gsd:phase --insert 5 "Critical security fix"` → `/gsd:plan-phase 5.1` → `/gsd:execute-phase 5.1`
|
||||||
|
|
||||||
|
**Completing a milestone:** `/gsd:complete-milestone 1.0.0` → `/clear` → `/gsd:new-milestone`
|
||||||
|
|
||||||
|
**Capturing ideas:** `/gsd:capture` (from context) · `/gsd:capture --note ...` (quick note) · `/gsd:capture --seed "..."` (forward-looking) · `/gsd:capture --list` (review)
|
||||||
|
|
||||||
|
**Debugging:** `/gsd:debug "symptom"` → (investigate, context fills) → `/clear` → `/gsd:debug` (resumes)
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
- Read `.planning/PROJECT.md` for project vision
|
||||||
|
- Read `.planning/STATE.md` for current context
|
||||||
|
- Check `.planning/ROADMAP.md` for phase status
|
||||||
|
- Run `/gsd:progress` to check where you're up to
|
||||||
|
</reference>
|
||||||
@@ -140,6 +140,7 @@
|
|||||||
"lint:hooks-runtime-build-seam": "node scripts/lint-hooks-runtime-build-seam.cjs",
|
"lint:hooks-runtime-build-seam": "node scripts/lint-hooks-runtime-build-seam.cjs",
|
||||||
"ci:test-scope": "node scripts/ci-test-scope.cjs",
|
"ci:test-scope": "node scripts/ci-test-scope.cjs",
|
||||||
"benchmark:compact-content": "node scripts/benchmark-compact-content.cjs --check",
|
"benchmark:compact-content": "node scripts/benchmark-compact-content.cjs --check",
|
||||||
|
"benchmark:compact-content-variants": "node scripts/benchmark-compact-content-variants.cjs --check",
|
||||||
"changeset": "node scripts/changeset/new.cjs",
|
"changeset": "node scripts/changeset/new.cjs",
|
||||||
"changelog:render": "node scripts/changeset/cli.cjs render",
|
"changelog:render": "node scripts/changeset/cli.cjs render",
|
||||||
"test": "node scripts/run-tests.cjs",
|
"test": "node scripts/run-tests.cjs",
|
||||||
|
|||||||
291
scripts/benchmark-compact-content-variants.cjs
Normal file
291
scripts/benchmark-compact-content-variants.cjs
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Benchmarks the token-count effect of every registered variant-swap pair
|
||||||
|
* (ADR-4139, Phase 6 #4406 — `gsd-core/workflows/<name>/{modes,steps,templates}/*.compact.md`
|
||||||
|
* and `gsd-core/templates/**\/*.compact.md`). Sibling to
|
||||||
|
* `scripts/benchmark-compact-content.cjs` (Phase 3's spine/detail benchmark) rather than an
|
||||||
|
* extension of it — the two are different data shapes (a variant pair is two independent,
|
||||||
|
* deliberately-overlapping complete files; a spine/detail split is one document partitioned in
|
||||||
|
* two disjoint halves), and mixing them into one report would conflate an "off" total that means
|
||||||
|
* something different in each case.
|
||||||
|
*
|
||||||
|
* For every registered pair it reports the "off" token count (the canonical file — what
|
||||||
|
* `workflow.compact_content=false`, the default, pays at that call site) against the "on" token
|
||||||
|
* count (the `.compact.md` sibling — what `workflow.compact_content=true` pays once the gate
|
||||||
|
* resolves to it). See `gsd-core/references/compact-content-gate.md` §"Streams 1b and 4" for the
|
||||||
|
* resolution rule this measures.
|
||||||
|
*
|
||||||
|
* PROXY-TOKENIZER CAVEAT: same as the sibling script — `gpt-tokenizer` is a stand-in tokenizer;
|
||||||
|
* Anthropic publishes none for Claude 3+. The on/off comparison is exact under this one pinned
|
||||||
|
* tokenizer applied identically to both sides; the absolute counts are not Claude's real counts.
|
||||||
|
*
|
||||||
|
* Discovery is REIMPLEMENTED here rather than imported from
|
||||||
|
* `tests/helpers/compact-content-variant.cjs`, for the same reason
|
||||||
|
* `benchmark-compact-content.cjs` reimplements spine/detail discovery instead of importing it: a
|
||||||
|
* `scripts/` reporting tool depending on a test-only helper module inverts this repo's normal
|
||||||
|
* layering, and a test-only module changing shape should never be able to break a benchmark.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node scripts/benchmark-compact-content-variants.cjs # print JSON to stdout
|
||||||
|
* node scripts/benchmark-compact-content-variants.cjs --write # write the committed baseline
|
||||||
|
* node scripts/benchmark-compact-content-variants.cjs --check # recompute, diff vs committed baseline
|
||||||
|
* node scripts/benchmark-compact-content-variants.cjs --check --baseline-path=<path>
|
||||||
|
*
|
||||||
|
* Same CRITICAL contract as the sibling script: this — and `--check` especially — MUST NEVER
|
||||||
|
* exit non-zero because a baseline is drifted, stale, or missing. Only a genuine I/O error
|
||||||
|
* reading a SOURCE file the benchmark measures may throw. This is a reporting instrument, never
|
||||||
|
* a gate.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
const { countTokens } = require('gpt-tokenizer');
|
||||||
|
const { runMain } = require('./lib/cli-exit.cjs');
|
||||||
|
|
||||||
|
const ROOT = path.resolve(__dirname, '..');
|
||||||
|
const VARIANT_ROOTS = [path.join(ROOT, 'gsd-core', 'workflows'), path.join(ROOT, 'gsd-core', 'templates')];
|
||||||
|
const BASELINE_PATH = path.join(ROOT, 'tests', 'fixtures', 'compact-content-variant-benchmark-baseline.json');
|
||||||
|
const COMPACT_SUFFIX = '.compact.md';
|
||||||
|
|
||||||
|
function getTokenizerVersion() {
|
||||||
|
const pkgPath = require.resolve('gpt-tokenizer/package.json');
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
||||||
|
return pkg.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover every registered variant pair under `roots`: a `.compact.md` file
|
||||||
|
* with a same-directory, same-stem canonical `.md` sibling. Mirrors
|
||||||
|
* `tests/helpers/compact-content-variant.cjs`'s `discoverRegisteredVariants`
|
||||||
|
* in shape but is a from-scratch, self-contained implementation (see module
|
||||||
|
* header for why this is not a shared import). Skips an orphaned compact
|
||||||
|
* file with no canonical sibling — that is the guard's problem, not this
|
||||||
|
* benchmark's; a pair with no canonical baseline has no "off" number to
|
||||||
|
* report against.
|
||||||
|
*
|
||||||
|
* @param {string[]} [roots]
|
||||||
|
* @returns {Array<{name: string, canonicalPath: string, compactPath: string}>}
|
||||||
|
*/
|
||||||
|
function discoverRegisteredVariants(roots = VARIANT_ROOTS) {
|
||||||
|
const pairs = [];
|
||||||
|
|
||||||
|
function walk(dir) {
|
||||||
|
let entries;
|
||||||
|
try {
|
||||||
|
entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const entry of entries) {
|
||||||
|
const full = path.join(dir, entry.name);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
walk(full);
|
||||||
|
} else if (entry.isFile() && entry.name.endsWith(COMPACT_SUFFIX)) {
|
||||||
|
const stem = entry.name.slice(0, -COMPACT_SUFFIX.length);
|
||||||
|
const canonicalPath = path.join(dir, `${stem}.md`);
|
||||||
|
if (fs.existsSync(canonicalPath)) {
|
||||||
|
const name = path.relative(ROOT, canonicalPath).split(path.sep).join('/');
|
||||||
|
pairs.push({ name, canonicalPath, compactPath: full });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const root of roots) walk(root);
|
||||||
|
return pairs.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the off/on/reduction numbers for ONE registered variant pair.
|
||||||
|
* Throws on a genuine read failure of a source file (the one thing allowed
|
||||||
|
* to throw, per the module-header CRITICAL note).
|
||||||
|
*
|
||||||
|
* @param {{canonicalPath: string, compactPath: string}} pair
|
||||||
|
* @returns {{offTokens: number, onTokens: number, reductionPct: number}}
|
||||||
|
*/
|
||||||
|
function computePairTokens(pair) {
|
||||||
|
const offTokens = countTokens(fs.readFileSync(pair.canonicalPath, 'utf8'));
|
||||||
|
const onTokens = countTokens(fs.readFileSync(pair.compactPath, 'utf8'));
|
||||||
|
const reductionPct = offTokens === 0 ? 0 : round2(((offTokens - onTokens) / offTokens) * 100);
|
||||||
|
return { offTokens, onTokens, reductionPct };
|
||||||
|
}
|
||||||
|
|
||||||
|
function round2(n) {
|
||||||
|
return Math.round(n * 100) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aggregate per-pair numbers from SUMMED off/on totals, never averaged
|
||||||
|
* per-pair percentages. Reports `0` (not `NaN`) for zero registered pairs.
|
||||||
|
* @param {Record<string, {offTokens: number, onTokens: number}>} pairResults
|
||||||
|
*/
|
||||||
|
function computeAggregate(pairResults) {
|
||||||
|
let offTokens = 0;
|
||||||
|
let onTokens = 0;
|
||||||
|
for (const key of Object.keys(pairResults)) {
|
||||||
|
offTokens += pairResults[key].offTokens;
|
||||||
|
onTokens += pairResults[key].onTokens;
|
||||||
|
}
|
||||||
|
const reductionPct = offTokens === 0 ? 0 : round2(((offTokens - onTokens) / offTokens) * 100);
|
||||||
|
return { offTokens, onTokens, reductionPct };
|
||||||
|
}
|
||||||
|
|
||||||
|
const LABEL =
|
||||||
|
"PROXY-TOKENIZER DELTA — gpt-tokenizer is a stand-in; Anthropic publishes no tokenizer for Claude 3+. " +
|
||||||
|
"The on/off COMPARISON is exact under this pinned tokenizer; absolute counts are not Claude's real token counts.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string[]} [roots]
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
function buildReport(roots = VARIANT_ROOTS) {
|
||||||
|
const pairs = discoverRegisteredVariants(roots);
|
||||||
|
const pairReports = {};
|
||||||
|
for (const pair of pairs) {
|
||||||
|
pairReports[pair.name] = computePairTokens(pair);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
schema_version: 1,
|
||||||
|
generated_by: 'scripts/benchmark-compact-content-variants.cjs',
|
||||||
|
tokenizer: { name: 'gpt-tokenizer', version: getTokenizerVersion() },
|
||||||
|
label: LABEL,
|
||||||
|
pairs: pairReports,
|
||||||
|
aggregate: computeAggregate(pairReports),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a human-readable drift summary between a (possibly missing/invalid)
|
||||||
|
* committed baseline and a freshly-computed live report. Never throws.
|
||||||
|
* @param {string} baselinePath
|
||||||
|
* @param {object} live
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function formatDriftReport(baselinePath, live) {
|
||||||
|
const lines = [];
|
||||||
|
let baseline = null;
|
||||||
|
let baselineReadError = null;
|
||||||
|
try {
|
||||||
|
const raw = fs.readFileSync(baselinePath, 'utf8');
|
||||||
|
try {
|
||||||
|
baseline = JSON.parse(raw);
|
||||||
|
} catch (parseErr) {
|
||||||
|
baselineReadError = `baseline at ${baselinePath} could not be parsed as JSON: ${parseErr.message}`;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
baselineReadError = `no baseline found at ${baselinePath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baselineReadError) {
|
||||||
|
lines.push(`DRIFT: ${baselineReadError} — treating as fully drifted (this is reported, not an error).`);
|
||||||
|
lines.push('Live pairs:');
|
||||||
|
for (const name of Object.keys(live.pairs).sort()) {
|
||||||
|
const p = live.pairs[name];
|
||||||
|
lines.push(` + ${name}: off=${p.offTokens} on=${p.onTokens} reduction=${p.reductionPct}%`);
|
||||||
|
}
|
||||||
|
lines.push(
|
||||||
|
`Live aggregate: off=${live.aggregate.offTokens} on=${live.aggregate.onTokens} ` +
|
||||||
|
`reduction=${live.aggregate.reductionPct}%`,
|
||||||
|
);
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
const baselinePairs = (baseline && typeof baseline === 'object' && baseline.pairs) || {};
|
||||||
|
const livePairs = live.pairs;
|
||||||
|
const allNames = new Set([...Object.keys(baselinePairs), ...Object.keys(livePairs)]);
|
||||||
|
let anyDrift = false;
|
||||||
|
|
||||||
|
if (!baseline || typeof baseline.label !== 'string' || !baseline.label.includes('PROXY-TOKENIZER')) {
|
||||||
|
anyDrift = true;
|
||||||
|
lines.push('DRIFT: committed baseline is missing the required "PROXY-TOKENIZER" label.');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const name of [...allNames].sort()) {
|
||||||
|
const b = baselinePairs[name];
|
||||||
|
const l = livePairs[name];
|
||||||
|
if (!b) {
|
||||||
|
anyDrift = true;
|
||||||
|
lines.push(`DRIFT: pair "${name}" is new (not in committed baseline) — live off=${l.offTokens} on=${l.onTokens} reduction=${l.reductionPct}%`);
|
||||||
|
} else if (!l) {
|
||||||
|
anyDrift = true;
|
||||||
|
lines.push(`DRIFT: pair "${name}" was removed (present in committed baseline, not found live) — baseline off=${b.offTokens} on=${b.onTokens} reduction=${b.reductionPct}%`);
|
||||||
|
} else if (b.offTokens !== l.offTokens || b.onTokens !== l.onTokens || b.reductionPct !== l.reductionPct) {
|
||||||
|
anyDrift = true;
|
||||||
|
lines.push(
|
||||||
|
`DRIFT: pair "${name}": off ${b.offTokens} -> ${l.offTokens} (${l.offTokens - b.offTokens >= 0 ? '+' : ''}${l.offTokens - b.offTokens}), ` +
|
||||||
|
`on ${b.onTokens} -> ${l.onTokens} (${l.onTokens - b.onTokens >= 0 ? '+' : ''}${l.onTokens - b.onTokens}), ` +
|
||||||
|
`reduction ${b.reductionPct}% -> ${l.reductionPct}% (${round2(l.reductionPct - b.reductionPct) >= 0 ? '+' : ''}${round2(l.reductionPct - b.reductionPct)}pp)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ba = (baseline && baseline.aggregate) || {};
|
||||||
|
const la = live.aggregate;
|
||||||
|
if (ba.offTokens !== la.offTokens || ba.onTokens !== la.onTokens || ba.reductionPct !== la.reductionPct) {
|
||||||
|
anyDrift = true;
|
||||||
|
lines.push(
|
||||||
|
`DRIFT: aggregate: off ${ba.offTokens} -> ${la.offTokens}, on ${ba.onTokens} -> ${la.onTokens}, ` +
|
||||||
|
`reduction ${ba.reductionPct}% -> ${la.reductionPct}%`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anyDrift) {
|
||||||
|
lines.push(`Baseline at ${baselinePath} is up to date with the live recompute.`);
|
||||||
|
} else {
|
||||||
|
lines.push('');
|
||||||
|
lines.push('Run `node scripts/benchmark-compact-content-variants.cjs --write` to refresh the committed baseline.');
|
||||||
|
lines.push('(This is a REPORT, not a gate — exiting 0 regardless of drift, per this script\'s own contract.)');
|
||||||
|
}
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(argv) {
|
||||||
|
const opts = { write: false, check: false, baselinePath: BASELINE_PATH };
|
||||||
|
for (const arg of argv) {
|
||||||
|
if (arg === '--write') opts.write = true;
|
||||||
|
else if (arg === '--check') opts.check = true;
|
||||||
|
else if (arg.startsWith('--baseline-path=')) opts.baselinePath = arg.slice('--baseline-path='.length);
|
||||||
|
}
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const opts = parseArgs(process.argv.slice(2));
|
||||||
|
|
||||||
|
if (opts.write) {
|
||||||
|
const report = buildReport();
|
||||||
|
fs.mkdirSync(path.dirname(BASELINE_PATH), { recursive: true });
|
||||||
|
fs.writeFileSync(BASELINE_PATH, JSON.stringify(report, null, 2) + '\n');
|
||||||
|
process.stdout.write(`Wrote ${BASELINE_PATH}\n`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.check) {
|
||||||
|
const live = buildReport();
|
||||||
|
process.stdout.write(formatDriftReport(opts.baselinePath, live) + '\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(JSON.stringify(buildReport(), null, 2) + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* c8 ignore next 3 -- CLI entry guard; this repo measures coverage with c8, which does not honor istanbul pragmas */
|
||||||
|
if (require.main === module) {
|
||||||
|
runMain(main);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
discoverRegisteredVariants,
|
||||||
|
computePairTokens,
|
||||||
|
computeAggregate,
|
||||||
|
buildReport,
|
||||||
|
formatDriftReport,
|
||||||
|
getTokenizerVersion,
|
||||||
|
parseArgs,
|
||||||
|
LABEL,
|
||||||
|
BASELINE_PATH,
|
||||||
|
VARIANT_ROOTS,
|
||||||
|
};
|
||||||
2350
tests/codex-config-agents.test.cjs
Normal file
2350
tests/codex-config-agents.test.cjs
Normal file
File diff suppressed because it is too large
Load Diff
3629
tests/codex-config-hooks.test.cjs
Normal file
3629
tests/codex-config-hooks.test.cjs
Normal file
File diff suppressed because it is too large
Load Diff
2894
tests/codex-config-install.test.cjs
Normal file
2894
tests/codex-config-install.test.cjs
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
142
tests/compact-content-template-variant-parity.test.cjs
Normal file
142
tests/compact-content-template-variant-parity.test.cjs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tests/compact-content-template-variant-parity.test.cjs — ADR-4139, epic #4139, Phase 6 (#4406).
|
||||||
|
*
|
||||||
|
* Check 5 of `.gsd/phase/enhance-4406-lazy-remainder/40-design.md`'s variant-pair checklist:
|
||||||
|
* template consumer parity. Only two `gsd-core/templates/**` files got a `.compact.md` variant
|
||||||
|
* wired to a genuine runtime `Read` this phase — `summary.md` and `user-setup.md` (see
|
||||||
|
* `40-design.md`'s "a size-only candidate list was also the wrong test here" for why `spec.md`
|
||||||
|
* was dropped and why `summary.md`'s wiring is scoped to one call site only).
|
||||||
|
*
|
||||||
|
* The actual guarantee that makes compacting these templates safe: downstream consumers parse
|
||||||
|
* the GENERATED artifact (a real SUMMARY.md / USER-SETUP.md an agent wrote), never the template
|
||||||
|
* file itself. So the only way a compact variant could silently break a real consumer is if it
|
||||||
|
* changed the artifact's OUTPUT-FORMAT CONTRACT — the `## File Template` fenced block — relative
|
||||||
|
* to the canonical file. Both compact variants were authored to leave that block byte-identical
|
||||||
|
* and compact only the surrounding illustrative material (examples, guidelines prose). This test
|
||||||
|
* proves that invariant directly, rather than assuming it from the authoring process, and then
|
||||||
|
* proves the shared contract really is what the one real deterministic consumer
|
||||||
|
* (`gsd-core/bin/lib/coverage.cjs`'s `classifyContent`, backing `gsd-tools uat classify-coverage`)
|
||||||
|
* accepts and classifies correctly.
|
||||||
|
*
|
||||||
|
* `user-setup.md` has no deterministic content parser anywhere in this repo (confirmed by
|
||||||
|
* repo-wide search — the only "consumption" beyond an agent `Read` is a human `grep -r
|
||||||
|
* "USER-SETUP" .planning/` search over filenames, insensitive to internal structure). Its parity
|
||||||
|
* check is therefore limited to the File Template identity assertion; there is no real parser
|
||||||
|
* round trip to run against it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { describe, test } = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
const ROOT = path.join(__dirname, '..');
|
||||||
|
const { classifyContent } = require(path.join(ROOT, 'gsd-core', 'bin', 'lib', 'coverage.cjs'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the fenced code block immediately following a `## File Template` heading.
|
||||||
|
* Returns the block's inner content (without the opening/closing fences), or null if
|
||||||
|
* the heading or a following fence isn't found.
|
||||||
|
* @param {string} content
|
||||||
|
* @returns {string | null}
|
||||||
|
*/
|
||||||
|
function extractFileTemplateBlock(content) {
|
||||||
|
const headingIdx = content.indexOf('## File Template');
|
||||||
|
if (headingIdx === -1) return null;
|
||||||
|
const fenceStart = content.indexOf('```', headingIdx);
|
||||||
|
if (fenceStart === -1) return null;
|
||||||
|
const afterOpenFence = content.indexOf('\n', fenceStart) + 1;
|
||||||
|
const fenceEnd = content.indexOf('\n```', afterOpenFence);
|
||||||
|
if (fenceEnd === -1) return null;
|
||||||
|
return content.slice(afterOpenFence, fenceEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('template consumer parity — File Template contract identity', () => {
|
||||||
|
test('summary.md and summary.compact.md share a byte-identical File Template block', () => {
|
||||||
|
const canonical = fs.readFileSync(path.join(ROOT, 'gsd-core', 'templates', 'summary.md'), 'utf8');
|
||||||
|
const compact = fs.readFileSync(path.join(ROOT, 'gsd-core', 'templates', 'summary.compact.md'), 'utf8');
|
||||||
|
const canonicalBlock = extractFileTemplateBlock(canonical);
|
||||||
|
const compactBlock = extractFileTemplateBlock(compact);
|
||||||
|
assert.ok(canonicalBlock, 'canonical summary.md must have an extractable File Template block');
|
||||||
|
assert.ok(compactBlock, 'summary.compact.md must have an extractable File Template block');
|
||||||
|
assert.strictEqual(
|
||||||
|
compactBlock,
|
||||||
|
canonicalBlock,
|
||||||
|
'summary.compact.md must not alter the output-format contract any downstream consumer parses',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('user-setup.md and user-setup.compact.md share a byte-identical File Template block', () => {
|
||||||
|
const canonical = fs.readFileSync(path.join(ROOT, 'gsd-core', 'templates', 'user-setup.md'), 'utf8');
|
||||||
|
const compact = fs.readFileSync(path.join(ROOT, 'gsd-core', 'templates', 'user-setup.compact.md'), 'utf8');
|
||||||
|
const canonicalBlock = extractFileTemplateBlock(canonical);
|
||||||
|
const compactBlock = extractFileTemplateBlock(compact);
|
||||||
|
assert.ok(canonicalBlock, 'canonical user-setup.md must have an extractable File Template block');
|
||||||
|
assert.ok(compactBlock, 'user-setup.compact.md must have an extractable File Template block');
|
||||||
|
assert.strictEqual(
|
||||||
|
compactBlock,
|
||||||
|
canonicalBlock,
|
||||||
|
'user-setup.compact.md must not alter the output-format contract',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('template consumer parity — real classify-coverage round trip (summary.md)', () => {
|
||||||
|
const REALISTIC_SUMMARY = `---
|
||||||
|
phase: 07-example
|
||||||
|
plan: 01
|
||||||
|
subsystem: testing
|
||||||
|
tags: [example]
|
||||||
|
requires: []
|
||||||
|
provides: []
|
||||||
|
affects: []
|
||||||
|
coverage:
|
||||||
|
- id: D1
|
||||||
|
description: "Deterministic deliverable"
|
||||||
|
verification:
|
||||||
|
- kind: unit
|
||||||
|
ref: "tests/example.test.cjs#does the thing"
|
||||||
|
status: pass
|
||||||
|
human_judgment: false
|
||||||
|
- id: D2
|
||||||
|
description: "Deliverable needing a human"
|
||||||
|
verification: []
|
||||||
|
human_judgment: true
|
||||||
|
rationale: "UI screenshot review"
|
||||||
|
duration: 5min
|
||||||
|
completed: 2026-01-01
|
||||||
|
status: complete
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 7: Example Summary
|
||||||
|
|
||||||
|
**A deterministic deliverable and a human-judgment deliverable.**
|
||||||
|
`;
|
||||||
|
|
||||||
|
test('a SUMMARY.md built from the shared File Template coverage schema classifies correctly', () => {
|
||||||
|
const result = classifyContent(REALISTIC_SUMMARY, '07-example-01-SUMMARY.md');
|
||||||
|
assert.strictEqual(result.mode, 'coverage');
|
||||||
|
assert.strictEqual(result.errors.length, 0, `expected no validation errors: ${JSON.stringify(result.errors)}`);
|
||||||
|
assert.strictEqual(result.auto_passed.length, 1, 'D1 (human_judgment:false, all verification pass) must auto-pass');
|
||||||
|
assert.strictEqual(result.present.length, 1, 'D2 (human_judgment:true) must route to a human');
|
||||||
|
assert.strictEqual(result.all_auto_covered, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('boundary: a legacy SUMMARY.md with no coverage block still classifies (byte-identical fallback)', () => {
|
||||||
|
const legacy = REALISTIC_SUMMARY.replace(/coverage:[\s\S]*?rationale: "UI screenshot review"\n/, '');
|
||||||
|
const result = classifyContent(legacy, '07-example-01-SUMMARY.md');
|
||||||
|
assert.strictEqual(result.mode, 'legacy');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('negative: a coverage block that fails validation is presented to a human, never silently auto-passed', () => {
|
||||||
|
const malformed = REALISTIC_SUMMARY.replace('human_judgment: false', '');
|
||||||
|
const result = classifyContent(malformed, '07-example-01-SUMMARY.md');
|
||||||
|
assert.strictEqual(result.mode, 'coverage');
|
||||||
|
assert.ok(
|
||||||
|
result.present.some((p) => p.id === 'D1'),
|
||||||
|
'D1 missing human_judgment must route to present, never auto-pass',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
293
tests/compact-content-variant-guard.test.cjs
Normal file
293
tests/compact-content-variant-guard.test.cjs
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tests/compact-content-variant-guard.test.cjs — ADR-4139, epic #4139, Phase 6 (#4406).
|
||||||
|
*
|
||||||
|
* Implements the four mechanical checks `.gsd/phase/enhance-4406-lazy-remainder/40-design.md`
|
||||||
|
* describes for the variant-swap shape (two independent, complete files; the gate picks which
|
||||||
|
* one gets `Read`) covering `gsd-core/workflows/<name>/{modes,steps,templates}/*.compact.md`
|
||||||
|
* and `gsd-core/templates/**\/*.compact.md`. This is a DIFFERENT shape from
|
||||||
|
* `tests/compact-content-partition-guard.test.cjs` (stream 1's spine+detail partition) — see
|
||||||
|
* `tests/helpers/compact-content-variant.cjs` for why disjointness/completeness do not apply
|
||||||
|
* here. Template consumer parity (the fifth check) lives in its own file,
|
||||||
|
* `tests/compact-content-template-variant-parity.test.cjs`, because it needs a real
|
||||||
|
* artifact-generation + real-parser round trip per template rather than a generic file-shape
|
||||||
|
* check.
|
||||||
|
*
|
||||||
|
* Each check gets a RED (deliberately broken) and GREEN (fixed) fixture pair, built against
|
||||||
|
* synthetic temp files — never against this repo's own real variants — per this repo's rule
|
||||||
|
* that a guard nobody has seen go red is not yet a guard.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { describe, test } = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const os = require('node:os');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
const { cleanup } = require('./helpers.cjs');
|
||||||
|
const {
|
||||||
|
discoverRegisteredVariants,
|
||||||
|
checkRegistration,
|
||||||
|
checkReachability,
|
||||||
|
checkProtectedContentPreserved,
|
||||||
|
checkSizeSmaller,
|
||||||
|
} = require('./helpers/compact-content-variant.cjs');
|
||||||
|
|
||||||
|
describe('compact-content variant guard — real repo state (ADR-4139, Phase 6 #4406)', () => {
|
||||||
|
test('check 1 (registration): every .compact.md file has a canonical sibling', () => {
|
||||||
|
const pairs = discoverRegisteredVariants();
|
||||||
|
const violations = checkRegistration(pairs);
|
||||||
|
assert.deepStrictEqual(violations, [], `registration violations: ${JSON.stringify(violations, null, 2)}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('check 2 (reachability): every registered compact variant is named by at least one spine', () => {
|
||||||
|
const pairs = discoverRegisteredVariants();
|
||||||
|
const violations = checkReachability(pairs);
|
||||||
|
assert.deepStrictEqual(violations, [], `reachability violations: ${JSON.stringify(violations, null, 2)}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('check 3 (protected content preserved): every protected block in a canonical file survives in its compact sibling', () => {
|
||||||
|
const pairs = discoverRegisteredVariants();
|
||||||
|
const violations = checkProtectedContentPreserved(pairs);
|
||||||
|
assert.deepStrictEqual(violations, [], `protected-content violations: ${JSON.stringify(violations, null, 2)}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('check 4 (size smaller): every compact file is strictly smaller than its canonical sibling', () => {
|
||||||
|
const pairs = discoverRegisteredVariants();
|
||||||
|
const violations = checkSizeSmaller(pairs);
|
||||||
|
assert.deepStrictEqual(violations, [], `size violations: ${JSON.stringify(violations, null, 2)}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('non-vacuity: this repo actually has registered variant pairs to check', () => {
|
||||||
|
const pairs = discoverRegisteredVariants();
|
||||||
|
assert.ok(pairs.length > 0, 'expected at least one registered .compact.md pair — an empty result proves nothing');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('failing-first fixture: check 1 (registration)', () => {
|
||||||
|
test('RED — a .compact.md file exists with no canonical sibling (orphan)', () => {
|
||||||
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-variant-orphan-'));
|
||||||
|
try {
|
||||||
|
const workflowsDir = path.join(tmpRoot, 'gsd-core', 'workflows', 'foo', 'steps');
|
||||||
|
fs.mkdirSync(workflowsDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(workflowsDir, 'bar.compact.md'), 'Compact content with no canonical pair.\n');
|
||||||
|
|
||||||
|
const pairs = discoverRegisteredVariants([path.join(tmpRoot, 'gsd-core', 'workflows')]);
|
||||||
|
const violations = checkRegistration(pairs);
|
||||||
|
assert.ok(violations.length > 0, 'expected at least one registration violation');
|
||||||
|
assert.ok(
|
||||||
|
violations.some((v) => v.kind === 'orphan_compact_file' && v.compactPath.endsWith('bar.compact.md')),
|
||||||
|
`expected the orphan file to be named: ${JSON.stringify(violations)}`,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
cleanup(tmpRoot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GREEN — the canonical sibling is added', () => {
|
||||||
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-variant-orphan-ok-'));
|
||||||
|
try {
|
||||||
|
const workflowsDir = path.join(tmpRoot, 'gsd-core', 'workflows', 'foo', 'steps');
|
||||||
|
fs.mkdirSync(workflowsDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(workflowsDir, 'bar.compact.md'), 'Terser.\n');
|
||||||
|
fs.writeFileSync(path.join(workflowsDir, 'bar.md'), 'Canonical, longer content.\n');
|
||||||
|
|
||||||
|
const pairs = discoverRegisteredVariants([path.join(tmpRoot, 'gsd-core', 'workflows')]);
|
||||||
|
const violations = checkRegistration(pairs);
|
||||||
|
assert.deepStrictEqual(violations, []);
|
||||||
|
} finally {
|
||||||
|
cleanup(tmpRoot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('failing-first fixture: check 2 (reachability)', () => {
|
||||||
|
test('RED — a registered pair whose compact path is never named by any spine', () => {
|
||||||
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-variant-unreached-'));
|
||||||
|
try {
|
||||||
|
const workflowsRoot = path.join(tmpRoot, 'gsd-core', 'workflows');
|
||||||
|
const stepsDir = path.join(workflowsRoot, 'foo', 'steps');
|
||||||
|
fs.mkdirSync(stepsDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(stepsDir, 'bar.md'), 'Canonical, longer content.\n');
|
||||||
|
fs.writeFileSync(path.join(stepsDir, 'bar.compact.md'), 'Terser.\n');
|
||||||
|
fs.writeFileSync(path.join(workflowsRoot, 'foo.md'), 'Spine with entirely unrelated content and no path reference of any kind.\n');
|
||||||
|
|
||||||
|
const pairs = discoverRegisteredVariants([workflowsRoot]);
|
||||||
|
const violations = checkReachability(pairs, [workflowsRoot]);
|
||||||
|
assert.ok(violations.length > 0, 'expected at least one reachability violation');
|
||||||
|
assert.ok(
|
||||||
|
violations.some((v) => v.kind === 'unreachable_compact_file' && v.compactPath.endsWith('bar.compact.md')),
|
||||||
|
`expected the unreached file to be named: ${JSON.stringify(violations)}`,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
cleanup(tmpRoot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GREEN — the spine names the compact path via the variant-resolution rule', () => {
|
||||||
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-variant-unreached-ok-'));
|
||||||
|
try {
|
||||||
|
const workflowsRoot = path.join(tmpRoot, 'gsd-core', 'workflows');
|
||||||
|
const stepsDir = path.join(workflowsRoot, 'foo', 'steps');
|
||||||
|
fs.mkdirSync(stepsDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(stepsDir, 'bar.md'), 'Canonical, longer content.\n');
|
||||||
|
fs.writeFileSync(path.join(stepsDir, 'bar.compact.md'), 'Terser.\n');
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(workflowsRoot, 'foo.md'),
|
||||||
|
'Read and execute `gsd-core/workflows/foo/steps/bar.md` (or its `gsd-core/workflows/foo/steps/bar.compact.md` variant per the shared gate).\n',
|
||||||
|
);
|
||||||
|
|
||||||
|
const pairs = discoverRegisteredVariants([workflowsRoot]);
|
||||||
|
const violations = checkReachability(pairs, [workflowsRoot]);
|
||||||
|
assert.deepStrictEqual(violations, []);
|
||||||
|
} finally {
|
||||||
|
cleanup(tmpRoot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a vendored path ending in the same filename does not grant false reachability', () => {
|
||||||
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-variant-unreached-prefix-'));
|
||||||
|
try {
|
||||||
|
const workflowsRoot = path.join(tmpRoot, 'gsd-core', 'workflows');
|
||||||
|
const stepsDir = path.join(workflowsRoot, 'foo', 'steps');
|
||||||
|
fs.mkdirSync(stepsDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(stepsDir, 'bar.md'), 'Canonical, longer content.\n');
|
||||||
|
fs.writeFileSync(path.join(stepsDir, 'bar.compact.md'), 'Terser.\n');
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(workflowsRoot, 'foo.md'),
|
||||||
|
'This mentions vendor/foo/steps/bar.compact.md, a different tree entirely.\n',
|
||||||
|
);
|
||||||
|
|
||||||
|
const pairs = discoverRegisteredVariants([workflowsRoot]);
|
||||||
|
const violations = checkReachability(pairs, [workflowsRoot]);
|
||||||
|
assert.ok(
|
||||||
|
violations.some((v) => v.kind === 'unreachable_compact_file'),
|
||||||
|
'a prefixed match must not count as reachability',
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
cleanup(tmpRoot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('failing-first fixture: check 3 (protected content preserved)', () => {
|
||||||
|
test('RED — a protected block in the canonical file is absent from the compact sibling', () => {
|
||||||
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-variant-protected-'));
|
||||||
|
try {
|
||||||
|
const workflowsRoot = path.join(tmpRoot, 'gsd-core', 'workflows', 'foo', 'steps');
|
||||||
|
fs.mkdirSync(workflowsRoot, { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(workflowsRoot, 'bar.md'),
|
||||||
|
'Intro.\n\n<!-- gsd:protected -->\nNever weaken this exact guardrail sentence.\n\nMore filler text that is safe to shorten elsewhere in this file to pad it out longer than the compact sibling.\n',
|
||||||
|
);
|
||||||
|
fs.writeFileSync(path.join(workflowsRoot, 'bar.compact.md'), 'Terser intro with the guardrail dropped.\n');
|
||||||
|
|
||||||
|
const pairs = discoverRegisteredVariants([path.join(tmpRoot, 'gsd-core', 'workflows')]);
|
||||||
|
const violations = checkProtectedContentPreserved(pairs);
|
||||||
|
assert.ok(violations.length > 0, 'expected at least one protected-content violation');
|
||||||
|
assert.ok(
|
||||||
|
violations.some((v) => v.missing.includes('Never weaken this exact guardrail sentence.')),
|
||||||
|
`expected the dropped sentence to be named: ${JSON.stringify(violations)}`,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
cleanup(tmpRoot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GREEN — the guardrail sentence is preserved verbatim in the compact sibling', () => {
|
||||||
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-variant-protected-ok-'));
|
||||||
|
try {
|
||||||
|
const workflowsRoot = path.join(tmpRoot, 'gsd-core', 'workflows', 'foo', 'steps');
|
||||||
|
fs.mkdirSync(workflowsRoot, { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(workflowsRoot, 'bar.md'),
|
||||||
|
'Intro.\n\n<!-- gsd:protected -->\nNever weaken this exact guardrail sentence.\n\nMore filler text that is safe to shorten elsewhere in this file to pad it out longer than the compact sibling.\n',
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(workflowsRoot, 'bar.compact.md'),
|
||||||
|
'Terser intro.\n\nNever weaken this exact guardrail sentence.\n',
|
||||||
|
);
|
||||||
|
|
||||||
|
const pairs = discoverRegisteredVariants([path.join(tmpRoot, 'gsd-core', 'workflows')]);
|
||||||
|
const violations = checkProtectedContentPreserved(pairs);
|
||||||
|
assert.deepStrictEqual(violations, []);
|
||||||
|
} finally {
|
||||||
|
cleanup(tmpRoot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a canonical file with no protected blocks is a correct no-op (nothing to preserve)', () => {
|
||||||
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-variant-protected-none-'));
|
||||||
|
try {
|
||||||
|
const workflowsRoot = path.join(tmpRoot, 'gsd-core', 'workflows', 'foo', 'steps');
|
||||||
|
fs.mkdirSync(workflowsRoot, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(workflowsRoot, 'bar.md'), 'Plain canonical content with no protected sentinel at all.\n');
|
||||||
|
fs.writeFileSync(path.join(workflowsRoot, 'bar.compact.md'), 'Terser.\n');
|
||||||
|
|
||||||
|
const pairs = discoverRegisteredVariants([path.join(tmpRoot, 'gsd-core', 'workflows')]);
|
||||||
|
const violations = checkProtectedContentPreserved(pairs);
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
violations,
|
||||||
|
[],
|
||||||
|
'a canonical file with zero <!-- gsd:protected --> blocks has nothing to check — never a violation',
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
cleanup(tmpRoot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('failing-first fixture: check 4 (size smaller)', () => {
|
||||||
|
test('RED — a "compact" file the same size as its canonical sibling', () => {
|
||||||
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-variant-size-'));
|
||||||
|
try {
|
||||||
|
const workflowsRoot = path.join(tmpRoot, 'gsd-core', 'workflows', 'foo', 'steps');
|
||||||
|
fs.mkdirSync(workflowsRoot, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(workflowsRoot, 'bar.md'), 'x'.repeat(100));
|
||||||
|
fs.writeFileSync(path.join(workflowsRoot, 'bar.compact.md'), 'y'.repeat(100));
|
||||||
|
|
||||||
|
const pairs = discoverRegisteredVariants([path.join(tmpRoot, 'gsd-core', 'workflows')]);
|
||||||
|
const violations = checkSizeSmaller(pairs);
|
||||||
|
assert.ok(violations.length > 0, 'expected at least one size violation');
|
||||||
|
assert.ok(
|
||||||
|
violations.some((v) => v.kind === 'compact_not_smaller' && v.canonicalSize === 100 && v.compactSize === 100),
|
||||||
|
`expected the equal-size pair to be named: ${JSON.stringify(violations)}`,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
cleanup(tmpRoot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('RED — a "compact" file one byte LARGER than its canonical sibling (boundary point)', () => {
|
||||||
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-variant-size-over-'));
|
||||||
|
try {
|
||||||
|
const workflowsRoot = path.join(tmpRoot, 'gsd-core', 'workflows', 'foo', 'steps');
|
||||||
|
fs.mkdirSync(workflowsRoot, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(workflowsRoot, 'bar.md'), 'x'.repeat(100));
|
||||||
|
fs.writeFileSync(path.join(workflowsRoot, 'bar.compact.md'), 'y'.repeat(101));
|
||||||
|
|
||||||
|
const pairs = discoverRegisteredVariants([path.join(tmpRoot, 'gsd-core', 'workflows')]);
|
||||||
|
const violations = checkSizeSmaller(pairs);
|
||||||
|
assert.ok(violations.length > 0, 'expected at least one size violation');
|
||||||
|
} finally {
|
||||||
|
cleanup(tmpRoot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GREEN — a compact file exactly one byte smaller (boundary point)', () => {
|
||||||
|
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-variant-size-ok-'));
|
||||||
|
try {
|
||||||
|
const workflowsRoot = path.join(tmpRoot, 'gsd-core', 'workflows', 'foo', 'steps');
|
||||||
|
fs.mkdirSync(workflowsRoot, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(workflowsRoot, 'bar.md'), 'x'.repeat(100));
|
||||||
|
fs.writeFileSync(path.join(workflowsRoot, 'bar.compact.md'), 'y'.repeat(99));
|
||||||
|
|
||||||
|
const pairs = discoverRegisteredVariants([path.join(tmpRoot, 'gsd-core', 'workflows')]);
|
||||||
|
const violations = checkSizeSmaller(pairs);
|
||||||
|
assert.deepStrictEqual(violations, []);
|
||||||
|
} finally {
|
||||||
|
cleanup(tmpRoot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -13,8 +13,8 @@
|
|||||||
"reductionPct": 36.67
|
"reductionPct": 36.67
|
||||||
},
|
},
|
||||||
"docs-update": {
|
"docs-update": {
|
||||||
"offTokens": 14231,
|
"offTokens": 14232,
|
||||||
"onTokens": 11881,
|
"onTokens": 11882,
|
||||||
"reductionPct": 16.51
|
"reductionPct": 16.51
|
||||||
},
|
},
|
||||||
"execute-phase": {
|
"execute-phase": {
|
||||||
@@ -39,8 +39,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"aggregate": {
|
"aggregate": {
|
||||||
"offTokens": 106922,
|
"offTokens": 106923,
|
||||||
"onTokens": 90274,
|
"onTokens": 90275,
|
||||||
"reductionPct": 15.57
|
"reductionPct": 15.57
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
tests/fixtures/compact-content-variant-benchmark-baseline.json
vendored
Normal file
31
tests/fixtures/compact-content-variant-benchmark-baseline.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"generated_by": "scripts/benchmark-compact-content-variants.cjs",
|
||||||
|
"tokenizer": {
|
||||||
|
"name": "gpt-tokenizer",
|
||||||
|
"version": "4.0.0"
|
||||||
|
},
|
||||||
|
"label": "PROXY-TOKENIZER DELTA — gpt-tokenizer is a stand-in; Anthropic publishes no tokenizer for Claude 3+. The on/off COMPARISON is exact under this pinned tokenizer; absolute counts are not Claude's real token counts.",
|
||||||
|
"pairs": {
|
||||||
|
"gsd-core/templates/summary.md": {
|
||||||
|
"offTokens": 2938,
|
||||||
|
"onTokens": 2299,
|
||||||
|
"reductionPct": 21.75
|
||||||
|
},
|
||||||
|
"gsd-core/templates/user-setup.md": {
|
||||||
|
"offTokens": 2061,
|
||||||
|
"onTokens": 1363,
|
||||||
|
"reductionPct": 33.87
|
||||||
|
},
|
||||||
|
"gsd-core/workflows/help/modes/full.md": {
|
||||||
|
"offTokens": 9950,
|
||||||
|
"onTokens": 6579,
|
||||||
|
"reductionPct": 33.88
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aggregate": {
|
||||||
|
"offTokens": 14949,
|
||||||
|
"onTokens": 10241,
|
||||||
|
"reductionPct": 31.49
|
||||||
|
}
|
||||||
|
}
|
||||||
11
tests/fixtures/install-tree/antigravity.json
vendored
11
tests/fixtures/install-tree/antigravity.json
vendored
@@ -296,21 +296,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -331,8 +323,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -412,6 +406,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/augment.json
vendored
11
tests/fixtures/install-tree/augment.json
vendored
@@ -368,21 +368,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -403,8 +395,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -484,6 +478,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/claude-local.json
vendored
11
tests/fixtures/install-tree/claude-local.json
vendored
@@ -261,21 +261,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -296,8 +288,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -377,6 +371,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/claude.json
vendored
11
tests/fixtures/install-tree/claude.json
vendored
@@ -296,21 +296,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -331,8 +323,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -412,6 +406,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/cline.json
vendored
11
tests/fixtures/install-tree/cline.json
vendored
@@ -298,21 +298,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -333,8 +325,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -414,6 +408,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/codebuddy.json
vendored
11
tests/fixtures/install-tree/codebuddy.json
vendored
@@ -368,21 +368,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -403,8 +395,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -484,6 +478,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/codex.json
vendored
11
tests/fixtures/install-tree/codex.json
vendored
@@ -332,21 +332,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -367,8 +359,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -448,6 +442,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/copilot.json
vendored
11
tests/fixtures/install-tree/copilot.json
vendored
@@ -297,21 +297,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -332,8 +324,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -413,6 +407,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/cursor.json
vendored
11
tests/fixtures/install-tree/cursor.json
vendored
@@ -296,21 +296,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -331,8 +323,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -412,6 +406,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/hermes.json
vendored
11
tests/fixtures/install-tree/hermes.json
vendored
@@ -296,21 +296,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -331,8 +323,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -412,6 +406,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/kilo.json
vendored
11
tests/fixtures/install-tree/kilo.json
vendored
@@ -368,21 +368,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -403,8 +395,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -484,6 +478,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/kimi-code.json
vendored
11
tests/fixtures/install-tree/kimi-code.json
vendored
@@ -297,21 +297,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -332,8 +324,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -413,6 +407,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/kimi.json
vendored
11
tests/fixtures/install-tree/kimi.json
vendored
@@ -333,21 +333,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -368,8 +360,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -449,6 +443,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/opencode.json
vendored
11
tests/fixtures/install-tree/opencode.json
vendored
@@ -368,21 +368,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -403,8 +395,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -484,6 +478,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/pi.json
vendored
11
tests/fixtures/install-tree/pi.json
vendored
@@ -156,21 +156,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -191,8 +183,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -272,6 +266,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/qwen.json
vendored
11
tests/fixtures/install-tree/qwen.json
vendored
@@ -296,21 +296,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -331,8 +323,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -412,6 +406,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/trae.json
vendored
11
tests/fixtures/install-tree/trae.json
vendored
@@ -296,21 +296,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -331,8 +323,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -412,6 +406,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/windsurf.json
vendored
11
tests/fixtures/install-tree/windsurf.json
vendored
@@ -224,21 +224,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -259,8 +251,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -340,6 +334,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
11
tests/fixtures/install-tree/zcode.json
vendored
11
tests/fixtures/install-tree/zcode.json
vendored
@@ -368,21 +368,13 @@
|
|||||||
"gsd-core/templates/UAT.md",
|
"gsd-core/templates/UAT.md",
|
||||||
"gsd-core/templates/UI-SPEC.md",
|
"gsd-core/templates/UI-SPEC.md",
|
||||||
"gsd-core/templates/VALIDATION.md",
|
"gsd-core/templates/VALIDATION.md",
|
||||||
"gsd-core/templates/claude-md.md",
|
|
||||||
"gsd-core/templates/codebase/architecture.md",
|
"gsd-core/templates/codebase/architecture.md",
|
||||||
"gsd-core/templates/codebase/concerns.md",
|
|
||||||
"gsd-core/templates/codebase/conventions.md",
|
|
||||||
"gsd-core/templates/codebase/integrations.md",
|
|
||||||
"gsd-core/templates/codebase/stack.md",
|
"gsd-core/templates/codebase/stack.md",
|
||||||
"gsd-core/templates/codebase/structure.md",
|
|
||||||
"gsd-core/templates/codebase/testing.md",
|
|
||||||
"gsd-core/templates/config.json",
|
"gsd-core/templates/config.json",
|
||||||
"gsd-core/templates/context.md",
|
"gsd-core/templates/context.md",
|
||||||
"gsd-core/templates/continue-here.md",
|
"gsd-core/templates/continue-here.md",
|
||||||
"gsd-core/templates/copilot-instructions.md",
|
"gsd-core/templates/copilot-instructions.md",
|
||||||
"gsd-core/templates/debug-subagent-prompt.md",
|
|
||||||
"gsd-core/templates/dev-preferences.md",
|
"gsd-core/templates/dev-preferences.md",
|
||||||
"gsd-core/templates/discovery.md",
|
|
||||||
"gsd-core/templates/discussion-log.md",
|
"gsd-core/templates/discussion-log.md",
|
||||||
"gsd-core/templates/milestone-archive.md",
|
"gsd-core/templates/milestone-archive.md",
|
||||||
"gsd-core/templates/milestone.md",
|
"gsd-core/templates/milestone.md",
|
||||||
@@ -403,8 +395,10 @@
|
|||||||
"gsd-core/templates/summary-complex.md",
|
"gsd-core/templates/summary-complex.md",
|
||||||
"gsd-core/templates/summary-minimal.md",
|
"gsd-core/templates/summary-minimal.md",
|
||||||
"gsd-core/templates/summary-standard.md",
|
"gsd-core/templates/summary-standard.md",
|
||||||
|
"gsd-core/templates/summary.compact.md",
|
||||||
"gsd-core/templates/summary.md",
|
"gsd-core/templates/summary.md",
|
||||||
"gsd-core/templates/user-profile.md",
|
"gsd-core/templates/user-profile.md",
|
||||||
|
"gsd-core/templates/user-setup.compact.md",
|
||||||
"gsd-core/templates/user-setup.md",
|
"gsd-core/templates/user-setup.md",
|
||||||
"gsd-core/templates/verification-report.md",
|
"gsd-core/templates/verification-report.md",
|
||||||
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
"gsd-core/workflows/_runtime-launcher.snippet.sh",
|
||||||
@@ -484,6 +478,7 @@
|
|||||||
"gsd-core/workflows/help.md",
|
"gsd-core/workflows/help.md",
|
||||||
"gsd-core/workflows/help/modes/brief.md",
|
"gsd-core/workflows/help/modes/brief.md",
|
||||||
"gsd-core/workflows/help/modes/default.md",
|
"gsd-core/workflows/help/modes/default.md",
|
||||||
|
"gsd-core/workflows/help/modes/full.compact.md",
|
||||||
"gsd-core/workflows/help/modes/full.md",
|
"gsd-core/workflows/help/modes/full.md",
|
||||||
"gsd-core/workflows/help/modes/topic.md",
|
"gsd-core/workflows/help/modes/topic.md",
|
||||||
"gsd-core/workflows/import.md",
|
"gsd-core/workflows/import.md",
|
||||||
|
|||||||
293
tests/helpers/compact-content-variant.cjs
Normal file
293
tests/helpers/compact-content-variant.cjs
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared library for the compact-content VARIANT guard (ADR-4139, epic #4139,
|
||||||
|
* Phase 6 #4406). See `gsd-core/references/compact-content-gate.md` §"Streams
|
||||||
|
* 1b and 4 — variant resolution" for the operational rule this module checks;
|
||||||
|
* this file is the mechanics, not the source of truth for behavior.
|
||||||
|
*
|
||||||
|
* This is a DIFFERENT shape from `compact-content-split.cjs` (Phase 3, stream
|
||||||
|
* 1's spine+detail partition). A partition is one document split into two
|
||||||
|
* halves that must never overlap (disjointness) and whose union must equal
|
||||||
|
* the original (completeness). A variant pair is two INDEPENDENT, complete
|
||||||
|
* documents that are EXPECTED to overlap heavily — the compact file is a
|
||||||
|
* hand-terser rewrite of the same content, not an extracted remainder. So
|
||||||
|
* this module has no disjointness check and no completeness-at-split-time
|
||||||
|
* check; it has the five checks `40-design.md` (Phase 6) describes instead:
|
||||||
|
*
|
||||||
|
* 1. Registration — `discoverRegisteredVariants` (a `.compact.md`
|
||||||
|
* file with no canonical sibling is not a registered pair; the guard
|
||||||
|
* test reports it as an orphan).
|
||||||
|
* 2. Reachability — `checkReachability` (a registered pair whose
|
||||||
|
* compact path is never named by any spine's "Read ... variant
|
||||||
|
* resolution" call site is unwired dead weight).
|
||||||
|
* 3. Protected content preserved — `checkProtectedContentPreserved` (a
|
||||||
|
* `<!-- gsd:protected -->` block's lines must appear verbatim in BOTH
|
||||||
|
* files, since nothing is "moved" in a variant pair — it is duplicated).
|
||||||
|
* 4. Size smaller — `checkSizeSmaller`.
|
||||||
|
* 5. Template consumer parity — NOT implemented here; it needs a real
|
||||||
|
* artifact-generation + real-parser round trip per template, which is
|
||||||
|
* the domain of `tests/compact-content-template-variant-parity.test.cjs`
|
||||||
|
* directly, not a generic file-shape check.
|
||||||
|
*
|
||||||
|
* This module only reads (filesystem + a search of markdown source for
|
||||||
|
* literal path substrings). No writes, no network, no git.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('node:fs');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
const { extractProtectedBlocks, normalizeNonTrivialLines } = require('./compact-content-split.cjs');
|
||||||
|
|
||||||
|
/** Default scan roots: everywhere a `.compact.md` sibling can legally live. */
|
||||||
|
const DEFAULT_VARIANT_ROOTS = [
|
||||||
|
path.join(__dirname, '..', '..', 'gsd-core', 'workflows'),
|
||||||
|
path.join(__dirname, '..', '..', 'gsd-core', 'templates'),
|
||||||
|
];
|
||||||
|
|
||||||
|
/** Every markdown-source root a spine/fragment might name a variant path from. */
|
||||||
|
const DEFAULT_SEARCH_ROOTS = [
|
||||||
|
path.join(__dirname, '..', '..', 'gsd-core', 'workflows'),
|
||||||
|
];
|
||||||
|
|
||||||
|
const COMPACT_SUFFIX = '.compact.md';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively list every file under `dir` whose name ends with `suffix`.
|
||||||
|
* Shared by both file-discovery needs this module has — `.compact.md` files
|
||||||
|
* (`findCompactFiles`) and general `.md` files to search for reachability
|
||||||
|
* (`findMarkdownFiles`) — which otherwise duplicated the same walk with only
|
||||||
|
* the extension predicate differing.
|
||||||
|
* @param {string} dir
|
||||||
|
* @param {string} suffix
|
||||||
|
* @returns {string[]} absolute paths
|
||||||
|
*/
|
||||||
|
function findFilesWithSuffix(dir, suffix) {
|
||||||
|
const results = [];
|
||||||
|
let entries;
|
||||||
|
try {
|
||||||
|
entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
|
} catch {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
for (const entry of entries) {
|
||||||
|
const full = path.join(dir, entry.name);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
results.push(...findFilesWithSuffix(full, suffix));
|
||||||
|
} else if (entry.isFile() && entry.name.endsWith(suffix)) {
|
||||||
|
results.push(full);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively list every `*.compact.md` file under `dir`.
|
||||||
|
* @param {string} dir
|
||||||
|
* @returns {string[]} absolute paths
|
||||||
|
*/
|
||||||
|
function findCompactFiles(dir) {
|
||||||
|
return findFilesWithSuffix(dir, COMPACT_SUFFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover every registered compact/canonical variant pair under `roots`.
|
||||||
|
*
|
||||||
|
* A pair is registered by a `<dir>/<stem>.compact.md` file existing on disk —
|
||||||
|
* there is no separate registry. Its canonical sibling is `<dir>/<stem>.md`
|
||||||
|
* in the SAME directory. A `.compact.md` file with no canonical sibling is
|
||||||
|
* still returned (with `canonicalExists: false`) so the registration check
|
||||||
|
* can report it as an orphan by name, rather than silently skipping it.
|
||||||
|
*
|
||||||
|
* @param {string[]} roots
|
||||||
|
* @returns {{compactPath: string, canonicalPath: string, canonicalExists: boolean}[]}
|
||||||
|
*/
|
||||||
|
function discoverRegisteredVariants(roots = DEFAULT_VARIANT_ROOTS) {
|
||||||
|
const pairs = [];
|
||||||
|
for (const root of roots) {
|
||||||
|
for (const compactPath of findCompactFiles(root)) {
|
||||||
|
const dir = path.dirname(compactPath);
|
||||||
|
const stem = path.basename(compactPath, COMPACT_SUFFIX);
|
||||||
|
const canonicalPath = path.join(dir, `${stem}.md`);
|
||||||
|
pairs.push({
|
||||||
|
compactPath,
|
||||||
|
canonicalPath,
|
||||||
|
canonicalExists: fs.existsSync(canonicalPath),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pairs.sort((a, b) => a.compactPath.localeCompare(b.compactPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check 1 — registration. A `.compact.md` file must have a canonical sibling.
|
||||||
|
* @param {ReturnType<typeof discoverRegisteredVariants>} pairs
|
||||||
|
*/
|
||||||
|
function checkRegistration(pairs) {
|
||||||
|
const violations = [];
|
||||||
|
for (const pair of pairs) {
|
||||||
|
if (!pair.canonicalExists) {
|
||||||
|
violations.push({ kind: 'orphan_compact_file', compactPath: pair.compactPath });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return violations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check 2 — reachability. A registered pair's compact path must be named by
|
||||||
|
* at least one markdown file under `searchRoots` (a spine's "Read ... variant
|
||||||
|
* resolution" call site). Three needle forms, matched differently, because
|
||||||
|
* this corpus has two live conventions for naming these paths (found by
|
||||||
|
* walking up from the compact file itself to its nearest `gsd-core` ancestor,
|
||||||
|
* so this works the same way against the real repo and against a fixture
|
||||||
|
* that builds its own `<tmp>/gsd-core/...` tree):
|
||||||
|
*
|
||||||
|
* - The `gsd-core/<rest>` form (e.g. `gsd-core/workflows/autonomous/steps/
|
||||||
|
* converge-fail-fast.md`'s own convention) is unambiguous on its own — a
|
||||||
|
* different, longer path coincidentally ending in this exact multi-segment
|
||||||
|
* suffix is not a realistic false positive, so a plain substring match is
|
||||||
|
* sufficient without the "unprefixed" guard below.
|
||||||
|
* - The `<rest>` form without the leading `gsd-core/` (e.g. `workflows/help/
|
||||||
|
* modes/full.compact.md`, `help.md`'s own dispatch-table convention) is
|
||||||
|
* equally unambiguous for the same reason.
|
||||||
|
* - The bare `<stem>.compact.md` form has no such guarantee — a same-named
|
||||||
|
* file under an unrelated nested directory (the exact class of bug already
|
||||||
|
* hit once this phase: `discuss-phase/templates/context.md` vs. the root
|
||||||
|
* `templates/context.md`) could grant it a false reachability. This form
|
||||||
|
* keeps the `isUnprefixedMatch` guard from `namesFragmentAsEntryPoint`
|
||||||
|
* (`scripts/lint-response-language-coverage.cjs`): a path character
|
||||||
|
* immediately before the match means this is the tail of some longer,
|
||||||
|
* different path, not the fragment itself.
|
||||||
|
*
|
||||||
|
* @param {ReturnType<typeof discoverRegisteredVariants>} pairs
|
||||||
|
* @param {string[]} searchRoots
|
||||||
|
*/
|
||||||
|
function checkReachability(pairs, searchRoots = DEFAULT_SEARCH_ROOTS) {
|
||||||
|
const violations = [];
|
||||||
|
const haystacks = [];
|
||||||
|
for (const root of searchRoots) {
|
||||||
|
for (const file of findMarkdownFiles(root)) {
|
||||||
|
haystacks.push(fs.readFileSync(file, 'utf8'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const pair of pairs) {
|
||||||
|
if (!pair.canonicalExists) continue; // already reported by checkRegistration
|
||||||
|
const gsdCoreRelative = relativeToNearestGsdCore(pair.compactPath);
|
||||||
|
const stem = path.basename(pair.compactPath, COMPACT_SUFFIX);
|
||||||
|
const bareNeedle = `${stem}${COMPACT_SUFFIX}`;
|
||||||
|
const reached = haystacks.some((text) => {
|
||||||
|
if (gsdCoreRelative && (text.includes(`gsd-core/${gsdCoreRelative}`) || text.includes(gsdCoreRelative))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return isUnprefixedMatch(text, bareNeedle);
|
||||||
|
});
|
||||||
|
if (!reached) {
|
||||||
|
violations.push({ kind: 'unreachable_compact_file', compactPath: pair.compactPath });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return violations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walk up from `filePath` to the nearest ancestor directory literally named
|
||||||
|
* `gsd-core`, and return the path from there to `filePath` (POSIX-separated).
|
||||||
|
* Returns `null` if no such ancestor exists. Anchoring on the literal
|
||||||
|
* `gsd-core` segment — rather than a hardcoded repo-root constant — is what
|
||||||
|
* lets this match both the real repo and a fixture built under its own
|
||||||
|
* `<tmp>/gsd-core/...` tree the same way.
|
||||||
|
* @param {string} filePath
|
||||||
|
* @returns {string | null}
|
||||||
|
*/
|
||||||
|
function relativeToNearestGsdCore(filePath) {
|
||||||
|
const segments = filePath.split(path.sep);
|
||||||
|
const idx = segments.lastIndexOf('gsd-core');
|
||||||
|
if (idx === -1) return null;
|
||||||
|
return segments.slice(idx + 1).join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Is `needle` present in `text` with no path character immediately before it (any line)? */
|
||||||
|
function isUnprefixedMatch(text, needle) {
|
||||||
|
return text.split(/\r?\n/).some((line) => {
|
||||||
|
const at = line.indexOf(needle);
|
||||||
|
if (at === -1) return false;
|
||||||
|
const before = at > 0 ? line[at - 1] : '';
|
||||||
|
return !/[A-Za-z0-9_\-./]/.test(before);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function findMarkdownFiles(dir) {
|
||||||
|
return findFilesWithSuffix(dir, '.md');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check 3 — protected content preserved. Every protected block's non-trivial
|
||||||
|
* lines in the canonical file must also appear (verbatim, after the same
|
||||||
|
* normalization the partition guard uses) somewhere in the compact sibling.
|
||||||
|
* Unlike the partition guard, this is NOT a sentinel-presence check on the
|
||||||
|
* compact file itself — the compact file need not carry `<!-- gsd:protected -->`
|
||||||
|
* markers of its own, since it is not itself audited for content it might
|
||||||
|
* shed later; it only must not have DROPPED the protected wording.
|
||||||
|
*
|
||||||
|
* @param {ReturnType<typeof discoverRegisteredVariants>} pairs
|
||||||
|
*/
|
||||||
|
function checkProtectedContentPreserved(pairs) {
|
||||||
|
const violations = [];
|
||||||
|
for (const pair of pairs) {
|
||||||
|
if (!pair.canonicalExists) continue;
|
||||||
|
const canonical = fs.readFileSync(pair.canonicalPath, 'utf8');
|
||||||
|
const compact = fs.readFileSync(pair.compactPath, 'utf8');
|
||||||
|
const { blocks } = extractProtectedBlocks(canonical);
|
||||||
|
if (blocks.length === 0) continue;
|
||||||
|
const compactLines = new Set(normalizeNonTrivialLines(compact));
|
||||||
|
for (const block of blocks) {
|
||||||
|
const missing = block.lines
|
||||||
|
.map((l) => l.trim())
|
||||||
|
.filter((l) => l.length > 0)
|
||||||
|
.filter((l) => !compactLines.has(l));
|
||||||
|
if (missing.length > 0) {
|
||||||
|
violations.push({
|
||||||
|
kind: 'protected_content_dropped',
|
||||||
|
canonicalPath: pair.canonicalPath,
|
||||||
|
compactPath: pair.compactPath,
|
||||||
|
missing,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return violations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check 4 — size smaller. The compact file must be strictly smaller than its
|
||||||
|
* canonical sibling; a same-size-or-larger "compact" file is not one.
|
||||||
|
* @param {ReturnType<typeof discoverRegisteredVariants>} pairs
|
||||||
|
*/
|
||||||
|
function checkSizeSmaller(pairs) {
|
||||||
|
const violations = [];
|
||||||
|
for (const pair of pairs) {
|
||||||
|
if (!pair.canonicalExists) continue;
|
||||||
|
const canonicalSize = fs.statSync(pair.canonicalPath).size;
|
||||||
|
const compactSize = fs.statSync(pair.compactPath).size;
|
||||||
|
if (!(compactSize < canonicalSize)) {
|
||||||
|
violations.push({
|
||||||
|
kind: 'compact_not_smaller',
|
||||||
|
canonicalPath: pair.canonicalPath,
|
||||||
|
compactPath: pair.compactPath,
|
||||||
|
canonicalSize,
|
||||||
|
compactSize,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return violations;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
DEFAULT_VARIANT_ROOTS,
|
||||||
|
DEFAULT_SEARCH_ROOTS,
|
||||||
|
COMPACT_SUFFIX,
|
||||||
|
discoverRegisteredVariants,
|
||||||
|
checkRegistration,
|
||||||
|
checkReachability,
|
||||||
|
checkProtectedContentPreserved,
|
||||||
|
checkSizeSmaller,
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user