* chore(#604): rename get-shit-done/ runtime directory to gsd-core/ Renames the installed runtime directory `get-shit-done/` to `gsd-core/` so the on-disk name matches the package (`@opengsd/gsd-core`), repo, and binary (`gsd-tools`). The npm package name and binary are unchanged; npx/npm consumers are unaffected. Mechanical (bulk, ~90% of the diff): - `git mv get-shit-done gsd-core` - Swept path/identifier references across the repo via `perl -pe 's/get-shit-done(?!-\w)/gsd-core/g'`. The negative lookahead preserves the five legitimate slug variants that are NOT the directory: get-shit-done-{OLD,cc,classic,cli,redux} (old package/repo names). - Build/manifest wiring: package.json (bin, files, coverage globs), tsconfig.build.json (outDir), ~86 .gitignore build-output entries, stryker.config.mjs, scan-ignore files, install.js path strings. - Frozen (not rewritten): CHANGELOG.md history; translated docs (README.<locale>.md and docs/{ja-JP,ko-KR,pt-BR,zh-CN}/). New logic (review here): - src/installer-migrations/003-rename-get-shit-done-to-gsd-core.cts: a proper ADR-0008 installer migration. On upgrade it walks the legacy `~/.claude/get-shit-done/` tree, classifies each file via the prior install manifest, and emits remove-managed / backup-and-remove for managed files while PRESERVING unknown user-added files. Symlink-safe (skips a symlinked root and symlinked entries; bounds-checks every path under configDir). The framework rolls back on install failure. Emptied dirs may remain (framework has no recursive dir-removal primitive) — documented. - scripts/lint-legacy-dir-name.cjs: CI regression guard forbidding the bare `get-shit-done` directory token (split token to avoid self-match; case- insensitive; `(?!-\w)` lookahead allows the slug variants; allowlists CHANGELOG, translated docs, and `gsd-allow-legacy-name` marker lines). Wired into the lint-tests CI job. - Restored scripts/lint-package-identity-drift.cjs detection regexes (the mechanical sweep had wrongly rewritten the old-name patterns it exists to detect) and marked them as intentional legacy references. - TDD tests for the migration and the guard; do.md slash-command guard regex tightened so a `/gsd-core/bin` path segment is not mistaken for a command; changeset + docs/installer-migrations.md row added. Breaking: the installed runtime path moves `~/.claude/get-shit-done/` -> `~/.claude/gsd-core/`. Migration 003 removes the stale legacy dir's managed files (preserving user files) on upgrade. Users with custom hooks/configs hardcoding the old path must update them. Closes #604 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): unsweep pending changesets + allowlist injection-example docs CI fixes for the rename PR: - Do not sweep pending .changeset/*.md (ephemeral release-note fragments, like CHANGELOG); reverted those body edits so 5 pre-existing malformed fragments (missing type/pr) no longer enter the PR diff and trip docs-lint. Allowlisted .changeset/ in the legacy-name guard accordingly. - Allowlisted TEST-EXAMPLES.md and docs/explanation/security-model.md in prompt-injection-scan.sh: they contain intentional injection examples / security-model prose; the path-reference rewrites are kept. CodeQL alerts on this PR are pre-existing (alert lines unchanged by this PR; none in the new migration/guard) and are out of scope for the rename. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): resolve CodeQL alerts surfaced on this PR The rename diff touched files carrying pre-existing CodeQL findings; per the no-pre-existing-dismissal rule, fixing every surfaced alert rather than waving them off. All behavior-preserving: - scripts/ci-test-scope.cjs: build the config-path match from string .includes() instead of a RegExp over an arg-derived value (js/regex-injection). - src/profile-output.cts: escape backslashes before pipe-escaping desc/safeName so the table-cell escape is complete (js/incomplete-sanitization). - tests/{bug-2643,bug-2808,docs-parity-live-registry}: two-pass HTML-comment strip so a bare/unclosed `<!--` cannot survive (js/incomplete-multi-character-sanitization). - tests/inline-plan-threshold: drop the no-op `\s`->`\s` identity replace, keep the meaningful POSIX-class conversion (js/identity-replacement). Verified: build:lib green; the touched test files + ci-test-scope + profile-output suites pass; lint:legacy-name clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): correctly resolve remaining CodeQL alerts (regex-injection + sanitization) The prior commit's fixes for two alerts were ineffective: - ci-test-scope.cjs js/regex-injection: the alert is the CLI-arg-derived `file` reaching static regex `.test(file)` calls (not the config rule). Removed ALL regex over file/t — startsWith/includes/=== string checks + an isWindowsHint helper — so there is no regex sink for the tainted value. - js/incomplete-multi-character-sanitization (3 test files): a single `.replace(/<!--...-->/g,'')` can let `<!--` re-form. Replaced with a fixpoint loop (replace until stable) plus a final bare-opener strip. Verified: no regex over file/t remains; ci-test-scope + the 3 test suites pass; lint:legacy-name clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): make ci-test-scope + comment-strippers regex-free to clear CodeQL CodeQL flags the regex PATTERNS syntactically (regex-injection on the --files arg split; incomplete-multi-character-sanitization on the <!--...--> replace), so loop fixes do not satisfy it. Made these paths regex-free: - ci-test-scope.cjs splitFiles: char-by-char separator tokenizer (no /[,\\s]+/). - 3 test files: indexOf/slice HTML-comment stripper (no .replace(/<!--/)). Behavior preserved; ci-test-scope + the 3 suites pass; guard clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): unblock security base64 scan on the large rename diff The security job hit its 10m timeout: base64-scan.sh choked on the binary test fixture tests/feat-3594-parser-property-style.test.cjs (embedded NUL/ non-UTF8 bytes -> thousands of bogus blobs + "ignored null byte" warnings), and the ~800-file rename diff is slow to scan regardless. - scripts/base64-scan.sh: skip binary-by-content files (grep -Iq .) — they can't carry base64-obfuscated *text* and feeding NUL bytes through the per-line scanner is pathologically slow. collect_files already filtered binary *extensions*; this catches binary *content* in text extensions. - .github/workflows/security-scan.yml: raise the security job timeout 10m->30m to accommodate very large diffs (the scan itself is unchanged). Verified locally: scan skips the fixture, 0 "ignored null byte" warnings, 0 findings, exit 0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): sweep get-shit-done refs introduced by merging next The branch was updated with next (#614/#384/#618 etc.), which reference the get-shit-done/ dir (still named that on next). Swept the stale references in the merged files to gsd-core so the rename stays consistent and lint:legacy-name passes: - commands/gsd/discuss-phase.md (runtime-launcher shim paths) - src/core.cts (getAgentsDir layout comments) - tests/bug-384-agents-runtime-aware.test.cjs (require path to runtime lib) Verified: guard 0 violations; build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): exclude gsd-core/ path segments from bug-3683 command cross-ref invariant The #614 runtime-launcher shim added to discuss-phase.md references `${_GSD_RUNTIME_ROOT}/gsd-core/bin/...`. bug-3683's REF_PATTERN excluded path-y refs only via lookbehind, but `}` precedes `/gsd-core/` in the shim, so it mis-read the directory path as a dangling `/gsd-core` command ref (same class as the #604 bug-2954 fix). Added a trailing `(?![\w-]*\/)` so `/gsd-<x>/...` path segments are not treated as slash-command references. Verified locally on BOTH platforms before pushing: - mac (node 26) full suite: 0 failures - gsd-test-runner (linux, node22 image) full suite: 0 failures - bug-3683 + bug-2954 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): lazily resolve findProjectRoot in gsd-tools (harden flaky CI) CI intermittently failed state.test's gsd-tools subprocess with "findProjectRoot is not a function" (flip-flopping across legs; not reproducible on mac full suite, gsd-test linux full suite, test:unit, or state.test x8). findProjectRoot is a re-export from core.cjs (sourced from project-root.cjs); binding it via destructure at module-load can be undefined under a load-ordering edge. Resolve it lazily at call time via a small wrapper so the lookup happens after core.cjs is fully initialized. Verified green on BOTH platforms before pushing: - mac (node 26) full suite: 0 failures - gsd-test-runner (linux, node22) full suite: 0 failures - state.test.cjs: 106/106; gsd-tools loads cleanly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): allowlist verification-patterns.md placeholder examples in secret scan The rename git-mv'd references/verification-patterns.md into gsd-core/, pulling it into the secret-scan diff. It documents stub/placeholder RED-FLAG env-var examples (illustrative Stripe test-key / database-URL / API-key placeholders) — not real credentials. Added it to .secretscanignore with the strict annotation, mirroring the existing gsd-core/workflows/plan-phase.md exception. Verified locally: secret-scan-lint --strict OK; secret-scan --diff origin/next exits 0 with 0 findings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
20 KiB
Supports two modes:
- Idea mode (default) — user describes an idea to spike
- Frontier mode — no argument or "frontier" / "what should I spike?" — analyzes existing spike landscape and proposes integration and frontier spikes
<required_reading> Read all files referenced by the invoking prompt's execution_context before starting. </required_reading>
``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ GSD ► SPIKING ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ```Parse $ARGUMENTS for:
--quickflag → setQUICK_MODE=true--textflag → setTEXT_MODE=truefrontieror empty → setFRONTIER_MODE=true- Remaining text → the idea to spike
Text mode: If TEXT_MODE is enabled, replace AskUserQuestion calls with plain-text numbered lists.
## Routing- FRONTIER_MODE is true → Jump to
frontier_mode - Otherwise → Continue to
setup_directory
Load the Spike Landscape
If no .planning/spikes/ directory exists, tell the user there's nothing to analyze and offer to start fresh with an idea instead.
Otherwise, load in this order:
a. MANIFEST.md — the overall idea, requirements, and spike table with verdicts.
b. Findings skills — glob ./.claude/skills/spike-findings-*/SKILL.md and read any that exist, plus their references/*.md. These contain curated knowledge from prior wrap-ups.
c. CONVENTIONS.md — read .planning/spikes/CONVENTIONS.md if it exists. Established stack and patterns.
d. All spike READMEs — read .planning/spikes/*/README.md for verdicts, results, investigation trails, and tags.
Analyze for Integration Spikes
Review every pair and cluster of VALIDATED spikes. Look for:
- Shared resources: Two spikes that both touch the same API, database, state, or data format but were tested independently.
- Data handoffs: Spike A produces output that Spike B consumes. The formats were assumed compatible but never proven.
- Timing/ordering: Spikes that work in isolation but have sequencing dependencies in the real flow.
- Resource contention: Spikes that individually work but may compete for connections, memory, rate limits, or tokens when combined.
If integration risks exist, present them as concrete proposed spikes with names and Given/When/Then validation questions. If no meaningful integration risks exist, say so and skip this category.
Analyze for Frontier Spikes
Think laterally about the overall idea from MANIFEST.md and what's been proven so far. Consider:
- Gaps in the vision: Capabilities assumed but unproven.
- Discovered dependencies: Findings that reveal new questions.
- Alternative approaches: Different angles for PARTIAL or INVALIDATED spikes.
- Adjacent capabilities: Things that would meaningfully improve the idea if feasible.
- Comparison opportunities: Approaches that worked but felt heavy.
Present frontier spikes as concrete proposals numbered from the highest existing spike number with Given/When/Then and risk ordering.
Get Alignment and Execute
Present all integration and frontier candidates, then ask which to run. When the user picks spikes, write definitions into .planning/spikes/MANIFEST.md (appending to existing table) and proceed directly to building them starting at research.
mkdir -p .planning/spikes
Check for existing spikes to determine numbering:
ls -d .planning/spikes/[0-9][0-9][0-9]-* 2>/dev/null | sort | tail -1
Check commit_docs config:
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi
COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
Check conventions first. If .planning/spikes/CONVENTIONS.md exists, follow its stack and patterns — these represent validated choices the user expects to see continued.
Then check the project stack:
ls package.json pyproject.toml Cargo.toml go.mod 2>/dev/null
Use the project's language/framework by default. For greenfield projects with no conventions and no existing stack, pick whatever gets to a runnable result fastest.
Avoid unless the spike specifically requires it:
- Complex package management beyond
npm installorpip install - Build tools, bundlers, or transpilers
- Docker, containers, or infrastructure
- Env files or config systems — hardcode everything
a. Conventions: Read .planning/spikes/CONVENTIONS.md if it exists.
b. Findings skills: Glob for ./.claude/skills/spike-findings-*/SKILL.md and read any that exist, plus their references/*.md files.
c. Manifest: Read .planning/spikes/MANIFEST.md for the index of all spikes.
d. Related READMEs: Based on the new idea, identify which prior spikes are related by matching tags, names, technologies, or domain overlap. Read only those .planning/spikes/*/README.md files. Skip unrelated ones.
Cross-reference against this full body of prior work:
- Skip already-validated questions. Note the prior spike number and move on.
- Build on prior findings. Don't repeat failed approaches. Use their Research and Results sections.
- Reuse prior research. Carry findings forward rather than re-researching.
- Follow established conventions. Mention any deviation.
- Call out relevant prior art when presenting the decomposition.
If no .planning/spikes/ exists, skip this step.
Break the idea into 2-5 independent questions. Frame each as Given/When/Then. Present as a table:
| # | Spike | Type | Validates (Given/When/Then) | Risk |
|---|-------|------|-----------------------------|------|
| 001 | websocket-streaming | standard | Given a WS connection, when LLM streams tokens, then client receives chunks < 100ms | **High** |
| 002a | pdf-parse-pdfjs | comparison | Given a multi-page PDF, when parsed with pdfjs, then structured text is extractable | Medium |
| 002b | pdf-parse-camelot | comparison | Given a multi-page PDF, when parsed with camelot, then structured text is extractable | Medium |
Spike types:
- standard — one approach answering one question
- comparison — same question, different approaches. Shared number with letter suffix.
Good spikes: specific feasibility questions with observable output. Bad spikes: too broad, no observable output, or just reading/planning.
Order by risk — most likely to kill the idea runs first.
**If `QUICK_MODE` is true:** Skip.╔══════════════════════════════════════════════════════════════╗ ║ CHECKPOINT: Decision Required ║ ╚══════════════════════════════════════════════════════════════╝
{spike table from decompose step}
────────────────────────────────────────────────────────────── → Build all in this order, or adjust the list? ──────────────────────────────────────────────────────────────
## Research and Briefing Before Each SpikeThis step runs before each individual spike, not once at the start.
a. Present a spike briefing:
Spike NNN: Descriptive Name [2-3 sentences: what this spike is, why it matters, key risk or unknown.]
b. Research the current state of the art. Use context7 (resolve-library-id → query-docs) for libraries/frameworks. Use web search for APIs/services without a context7 entry. Read actual documentation.
c. Surface competing approaches as a table:
| Approach | Tool/Library | Pros | Cons | Status |
|---|---|---|---|---|
| ... | ... | ... | ... | ... |
Chosen approach: [which one and why]
If 2+ credible approaches exist, plan to build quick variants within the spike and compare them.
d. Capture research findings in a ## Research section in the README.
Skip when unnecessary for pure logic with no external dependencies.
Create or update `.planning/spikes/MANIFEST.md`:# Spike Manifest
## Idea
[One paragraph describing the overall idea being explored]
## Requirements
[Design decisions that emerged from the user's choices during spiking. Non-negotiable for the real build. Updated as spikes progress.]
- [e.g., "Must use streaming JSON output, not single-response"]
- [e.g., "Must support reconnection on network failure"]
## Spikes
| # | Name | Type | Validates | Verdict | Tags |
|---|------|------|-----------|---------|------|
Track requirements as they emerge. When the user expresses a preference during spiking, add it to the Requirements section immediately.
## Re-Ground Before Each SpikeBefore starting each spike (not just the first), re-read .planning/spikes/MANIFEST.md and .planning/spikes/CONVENTIONS.md to prevent drift within long sessions. Check the Requirements section — make sure the spike doesn't contradict any established requirements.
Depth over speed. The goal is genuine understanding, not a quick verdict. Never declare VALIDATED after a single happy-path test. Follow surprising findings. Test edge cases. Document the investigation trail, not just the conclusion.
Comparison spikes use shared number with letter suffix: NNN-a-name / NNN-b-name. Build back-to-back, then head-to-head comparison.
For Each Spike:
a. Create .planning/spikes/NNN-descriptive-name/
b. Default to giving the user something they can experience. The bias should be toward building a simple UI or interactive demo, not toward stdout that only Claude reads. The user wants to feel the spike working, not just be told it works.
The default is: build something the user can interact with. This could be:
- A simple HTML page that shows the result visually
- A web UI with a button that triggers the action and shows the response
- A page that displays data flowing through a pipeline
- A minimal interface where the user can try different inputs and see outputs
Only fall back to stdout/CLI verification when the spike is genuinely about a fact, not a feeling:
- Pure data transformation where the answer is "yes it parses correctly"
- Binary yes/no questions (does this API authenticate? does this library exist?)
- Benchmark numbers (how fast is X? how much memory does Y use?)
When in doubt, build the UI. It takes a few extra minutes but produces a spike the user can actually demo and feel confident about.
If the spike needs runtime observability, build a forensic log layer:
- Event log array with ISO timestamps and category tags
- Export mechanism (server: GET endpoint, CLI: JSON file, browser: Export button)
- Log summary (event counts, duration, errors, metadata)
- Analysis helpers if volume warrants it
c. Build the code. Start with simplest version, then deepen.
d. Iterate when findings warrant it:
- Surprising surface? Write a follow-up test that isolates and explores it.
- Answer feels shallow? Probe edge cases — large inputs, concurrent requests, malformed data, network failures.
- Assumption wrong? Adjust. Note the pivot in the README.
Multiple files per spike are expected for complex questions (e.g., test-basic.js, test-edge-cases.js, benchmark.js).
e. Write README.md with YAML frontmatter:
---
spike: NNN
name: descriptive-name
type: standard
validates: "Given [precondition], when [action], then [expected outcome]"
verdict: PENDING
related: []
tags: [tag1, tag2]
---
# Spike NNN: Descriptive Name
## What This Validates
[Given/When/Then]
## Research
[Docs checked, approach comparison table, chosen approach, gotchas. Omit if no external deps.]
## How to Run
[Command(s)]
## What to Expect
[Concrete observable outcomes]
## Observability
[If forensic log layer exists. Omit otherwise.]
## Investigation Trail
[Updated as spike progresses. Document each iteration: what tried, what revealed, what tried next.]
## Results
[Verdict, evidence, surprises, log analysis findings.]
f. Auto-link related spikes silently.
g. Run and verify:
- Self-verifiable: run, iterate if findings warrant deeper investigation, update verdict
- Needs human judgment: present checkpoint box:
╔══════════════════════════════════════════════════════════════╗ ║ CHECKPOINT: Verification Required ║ ╚══════════════════════════════════════════════════════════════╝
Spike {NNN}: {name} How to run: {command} What to expect: {concrete outcomes}
────────────────────────────────────────────────────────────── → Does this match what you expected? Describe what you see. ──────────────────────────────────────────────────────────────
h. Update .planning/spikes/MANIFEST.md with the spike's row.
i. Commit (if COMMIT_DOCS is true):
gsd_run query commit "docs(spike-NNN): [VERDICT] — [key finding]" --files .planning/spikes/NNN-descriptive-name/ .planning/spikes/MANIFEST.md
j. Report:
◆ Spike NNN: {name}
Verdict: {VALIDATED ✓ / INVALIDATED ✗ / PARTIAL ⚠}
Key findings: {not just verdict — investigation trail, surprises, edge cases explored}
Impact: {effect on remaining spikes}
Do not rush to a verdict. A spike that says "VALIDATED — it works" with no nuance is almost always incomplete.
k. If core assumption invalidated:
╔══════════════════════════════════════════════════════════════╗ ║ CHECKPOINT: Decision Required ║ ╚══════════════════════════════════════════════════════════════╝
Core assumption invalidated by Spike {NNN}. {what was invalidated and why}
────────────────────────────────────────────────────────────── → Continue with remaining spikes / Pivot approach / Abandon ──────────────────────────────────────────────────────────────
## Update ConventionsAfter all spikes in this session are built, update .planning/spikes/CONVENTIONS.md with patterns that emerged or solidified.
# Spike Conventions
Patterns and stack choices established across spike sessions. New spikes follow these unless the question requires otherwise.
## Stack
[What we use for frontend, backend, scripts, and why]
## Structure
[Common file layouts, port assignments, naming patterns]
## Patterns
[Recurring approaches: how we handle auth, how we style, how we serve]
## Tools & Libraries
[Preferred packages with versions that worked, and any to avoid]
Only include patterns that repeated across 2+ spikes or were explicitly chosen by the user. If CONVENTIONS.md already exists, update sections with new patterns from this session.
Commit (if COMMIT_DOCS is true):
gsd_run query commit "docs(spikes): update conventions" --files .planning/spikes/CONVENTIONS.md
Verdicts
| # | Name | Type | Verdict |
|---|---|---|---|
| 001 | {name} | standard | ✓ VALIDATED |
| 002a | {name} | comparison | ✓ WINNER |
Key Discoveries
{surprises, gotchas, investigation trail highlights}
Feasibility Assessment
{overall viability}
Signal for the Build
{what to use, avoid, watch out for}
───────────────────────────────────────────────────────────────
## ▶ Next Up
**Package findings** — wrap spike knowledge into an implementation blueprint
`/gsd:spike --wrap-up`
───────────────────────────────────────────────────────────────
**Also available:**
- `/gsd:spike` — spike more ideas (or run with no argument for frontier mode)
- `/gsd:plan-phase` — start planning the real implementation
- `/gsd:explore` — continue exploring the idea
───────────────────────────────────────────────────────────────
</step>
</process>
<success_criteria>
- [ ] `.planning/spikes/` created (auto-creates if needed, no project init required)
- [ ] Prior spikes and findings skills consulted before building
- [ ] Conventions followed (or deviation documented)
- [ ] Research grounded each spike in current docs before coding
- [ ] Depth over speed — edge cases tested, surprising findings followed, investigation trail documented
- [ ] Comparison spikes built back-to-back with head-to-head verdict
- [ ] Spikes needing human interaction have forensic log layer
- [ ] Requirements tracked in MANIFEST.md as they emerge from user choices
- [ ] CONVENTIONS.md created or updated with patterns that emerged
- [ ] Each spike README has complete frontmatter, Investigation Trail, and Results
- [ ] MANIFEST.md is current (with Type column and Requirements section)
- [ ] Commits use `docs(spike-NNN): [VERDICT]` format
- [ ] Consolidated report presented with next-step routing
</success_criteria>