7f8b5701bf0bbe3dc65b8ffddd96a9a4e8377e2c
249 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7f8b5701bf |
Enforce documentation updates via lint:docs + PR templates (#3651)
* Enforce documentation updates via lint:docs + PR templates (#3213) New scripts/lint-docs-required.cjs + Docs Required CI workflow fail any PR whose changeset fragment is typed Added / Changed / Deprecated / Removed without modifying at least one file under docs/. Mirrors scripts/changeset/lint.cjs: pure evaluateLint({ changedFiles, fragments, labels }) returning { ok, reason, triggering } over a frozen LINT_REASON enum; CLI wrapper reads the PR diff and parses each touched changeset fragment via the existing parseFragment helper. Escape hatches: - no-docs PR label (global) - per-fragment <!-- docs-exempt: <reason> --> marker, all triggering fragments must carry it for the PR to pass Fixed and Security fragments do not trigger the lint — bug fixes restore documented behavior, they do not introduce new behavior to document. PR templates (enhancement.md, feature.md) gain a Documentation checklist section pointing at the which-doc-to-update matrix. CONTRIBUTING.md adds a Documentation Updates section codifying that matrix, the English-canonical language policy for docs/ and the root README, and the two opt-out routes. Closes #3213 * Address Codex review: fail-closed on malformed fragments and strip docs-exempt marker from rendered release notes (#3213) Two P2 issues caught by `codex review --base main`: 1) Malformed fragments could silently bypass docs enforcement. parseFragment would return ok:false on a triggering Added fragment with bad frontmatter and readFragmentsFromDisk dropped it, so evaluateLint saw no triggering fragments and passed. The changeset-required lint only checks fragment _presence_ not _validity_, so the assumed fallback did not catch it. Fix: readFragmentsFromDisk now returns { fragments, malformed }; evaluateLint accepts a malformed param and emits a new FAIL_MALFORMED_FRAGMENT verdict that outranks every OK path (including the no-docs label) — a parse failure must be fixed before docs lint can decide anything else. 2) The per-fragment <!-- docs-exempt: reason --> marker lived in the fragment body, so the existing changelog (serializeChangelog) and GitHub release-notes (formatBullet) serializers published it verbatim. Worse, both serializers append `(#NNNN)` to the body's last line — with the marker as the trailing line, the PR suffix attached to the hidden comment instead of the visible bullet. Fix: parseFragment now extracts the marker into a typed `docsExempt` field and strips it from `body`, so all downstream renderers produce clean output without remembering to strip. The regex is anchored to its own line (^...$ with m flag) so inline mentions of the marker syntax in documentation (e.g. inside backticks) cannot accidentally exempt a fragment. Bounded character class [^\n>] keeps the regex linear-time. Test additions: - tests/lint-docs-required.test.cjs: FAIL_MALFORMED_FRAGMENT coverage, end-to-end "Added fragment with bad pr → malformed → fail-closed" regression test, updated readFragmentsFromDisk return-shape assertions, isExemptFragment now checks the typed docsExempt field rather than body content. - tests/changeset-parse.test.cjs: extractDocsExempt extraction cases (with/ without reason, case-insensitive, EMPTY_BODY when body is only a marker), inline-mention false-positive guard, real-marker-wins-when-also-inline test. - tests/changeset-new.test.cjs: fragment shape now includes docsExempt: null. CONTRIBUTING.md updated to clarify the "on its own line" requirement and the parse-time stripping behavior. The bootstrap fragment cleaned up so its body no longer contains a literal marker example that would have triggered the false-positive case. Full suite: 9696/9696 pass. * CRLF-safe docs-exempt marker stripping (Codex review pass 2, #3213) Second `codex review --commit` pass caught a CRLF regression in the docs-exempt extraction added in the previous commit. Repro: a Windows-authored fragment ---\r\ntype: Added\r\npr: 1\r\n---\r\nFeature.\r\n\r\n<!-- docs-exempt: x -->\r\n would parse to body `Feature.\r\n\r\n\r` because: - The previous trailing-newline slice trimmed only `\n`, leaving `\r`. - DOCS_EXEMPT_RE was anchored with `$` only — in multiline mode `$` matches before `\n` but does not consume `\r`, so the marker line's trailing `\r` was left behind after replace. - The cleanup regex stripped trailing `\n` but not `\r`. Net effect: serializeChangelog emitted - Feature.\r \r \r (#1) — the `(#1)` PR suffix landed on a blank line instead of attached to the visible bullet. Same bug surfaces in github-release-notes formatBullet. Fix: - DOCS_EXEMPT_RE: add `\r?` before `$` so the regex consumes the CR of a CRLF terminator. Switch reason character class from `[^\n>]` to `[^\r\n>]` so CRLF-authored reasons don't carry a trailing `\r`. - extractDocsExempt cleanup: `[ \t\r]+$/gm` strips trailing `\r` on each line; `(?:\r?\n){3,}` collapses CRLF triple-blank-lines; `[\r\n]+$` strips every trailing line terminator (LF or CR). - parseFragment trailing-newline slice: CRLF-aware — strips `\r\n` (2 chars) before falling through to single `\n`. Tests: two CRLF regression cases in tests/changeset-parse.test.cjs — Codex's exact repro (end-to-end through serializeChangelog) plus the no-marker CRLF passthrough case. Full suite: 9698/9698 pass. * CRLF regression test asserts on parseChangelog IR not rendered text (Codex review pass 3, #3213) Third `codex review` pass caught that the CRLF regression test added in the previous commit asserted on serializeChangelog's rendered Markdown via `out.split('\n')` + `assert.match`. That violates CONTRIBUTING.md's "Prohibited: Raw Text Matching on Test Outputs" rule and the documented serializer contract in `serialize.cjs`: > tests assert via round-trip (parse(serialize(ir))) > rather than by inspecting serialized text Replace the regex check with the established `parseChangelog(out)` round-trip and assert on the structured `{ body: 'Feature.', pr: 1 }` bullet. This is also a stronger regression check than the substring match: Codex's own probe in the review session confirmed the pre-fix buggy body shape (`Feature.\r\n\r\n\r`) breaks parseChangelog's bullet regex entirely (returns `bullets: []`), so the round-trip catches the exact failure mode end-to-end. Full suite: 9698/9698 pass. * Address CodeRabbit findings: anchor link + require non-empty docs-exempt reason (#3213) CodeRabbit's review on the PR caught two actionable issues, both quick wins. Anchor link in PR templates pointed to a heading that does not exist. The CONTRIBUTING.md heading "Documentation Updates — Update the Relevant Docs" contains an em-dash, which GitHub strips entirely when generating anchor slugs (it does NOT collapse to a hyphen). The actual anchor is #documentation-updates-update-the-relevant-docs (single hyphen between every word), not #documentation-updates--update-the-relevant-docs (double hyphen where the em-dash was). Both feature.md and enhancement.md fixed. The docs-exempt marker matched a bare `<!-- docs-exempt -->` with no reason, which defeats the entire purpose of the escape hatch — the marker exists to leave an audit trail explaining WHY a PR is exempt. Without a reason it is a silent bypass. Fix: DOCS_EXEMPT_RE now requires both the colon AND a non-whitespace first reason character. Bare `<!-- docs-exempt -->`, empty `<!-- docs-exempt: -->`, and whitespace-only `<!-- docs-exempt: -->` are all rejected as if the marker were not present (`docsExempt: null`). The lint then falls through to its normal docs-required / no-docs-label checks. `isExemptFragment` in the lint module tightened too — defense-in-depth: even if a caller constructs a fragment with `docsExempt: ''` directly, it does not count as exempt. The predicate now requires `typeof === 'string'` and non-empty after trim. Tests: - changeset-parse.test.cjs: three new explicit-rejection cases (bare marker, empty reason, whitespace-only reason). Existing DOCS_EXEMPT_RE shape test extended with negative assertions for the same three forms. - lint-docs-required.test.cjs: prior "empty reason still exempt" test inverted — empty/whitespace docsExempt now produces FAIL_DOCS_MISSING. isExemptFragment helper test extended with the same negative cases. - CONTRIBUTING.md: clarified that the reason is required and non-empty. Skipped CodeRabbit's third finding ("use `npm run lint:docs` in CI workflow instead of `node scripts/lint-docs-required.cjs`") — the existing changeset-required.yml uses the direct-node form for the equivalent changeset lint, so the new docs-required.yml is convention-consistent. Switching one without the other would create drift, and switching both is out of scope for #3213. Bootstrap fragment continues to extract cleanly under the stricter regex (verified — `docsExempt` field still contains the full bootstrap reason). Full suite: 9701/9701 pass. |
||
|
|
5a8610e0c2 | test: forbid cwd in PR checkers | ||
|
|
a3ca6ff6d6 |
feat(3553): Project-Root Resolution Module via generator (Phase 4 of #3524)
Phase 4 of the CJS↔SDK hard-seam migration (parent #3524). Eliminates the `findProjectRoot` duplication that lived at bin/lib/core.cjs:74-140 and sdk/src/query/helpers.ts:497-590, the drift carrier behind historical bugs #1362 and #2561. - sdk/src/project-root/index.ts — source of truth (120 lines, pure-with-sync-fs). Exports findProjectRoot(startDir: string) and FIND_PROJECT_ROOT_MAX_DEPTH constant. - sdk/src/project-root/index.test.ts — 13 vitest pinning fixtures covering all four heuristics, the #1362 guard, malformed config fallback, empty sub_repos, deep nesting, and depth-limit enforcement. - sdk/scripts/gen-project-root.mjs — generator. Captures function body via Function.prototype.toString() from compiled sdk/dist/. Emits CJS preamble for destructured node:fs / node:path / node:os imports. - sdk/scripts/check-project-root-fresh.mjs — freshness check. Imports the generator function directly (Phase 3's cleaner pattern). - get-shit-done/bin/lib/project-root.generated.cjs — generator- emitted CJS mirror. - tests/project-root-generator.test.cjs — 11 parity assertions comparing SDK source and generated CJS for every fixture. - sdk/src/query/helpers.ts: -127 lines. The 94-line inline findProjectRoot plus the FIND_PROJECT_ROOT_MAX_DEPTH constant (originally at line 471) replaced by a single re-export: `export { findProjectRoot } from '../project-root/index.js';` Removed unused `parse as parsePath` import. - get-shit-done/bin/lib/core.cjs: -83 lines net. The 67-line inline findProjectRoot replaced by a single `require('./project-root.generated.cjs')`. The detectSubRepos helper at lines 40-56 stays (used by loadConfig migration). - sdk/package.json: gen:project-root + check:project-root-fresh scripts. - package.json: proxy for the freshness check. - .githooks/pre-commit: drift block. - .github/workflows/test.yml: drift check step after the state-document drift step. - CONTEXT.md: Project-Root Resolution Module entry. - docs/INVENTORY.md, docs/INVENTORY-MANIFEST.json: +1 module count, +1 row. - Full suite: 9226/9226 pass (baseline 9215 + 11 new parity fixtures). - SDK vitest: 1804/1804 pass. - Reader shrink: -127 SDK + -83 CJS = 210 lines of duplication deleted across the two Readers. New shared Module is 120 lines. 1. Depth limit canonicalization. CJS findProjectRoot previously had no explicit walk-up bound (walked until dir === root or homedir). The new Module uses FIND_PROJECT_ROOT_MAX_DEPTH = 10, matching the SDK's pre-existing value. Only affects paths nested more than 10 levels deep from a .planning/ root — a pathological case in practice. None of the existing 22 CJS findProjectRoot tests covered this; the new parity test does. 2. platformReadSync → readFileSync. The old CJS findProjectRoot used the platformReadSync wrapper from shell-command-projection.cjs for reading .planning/config.json, which returns null on read failure. The Module uses raw readFileSync, which throws — caught by the surrounding try/catch that already swallowed errors. Functionally equivalent for the existing code path; no test exercises the null-return semantic. Closes #3553. |
||
|
|
ed8f4c9a31 |
feat(3544): Workstream Inventory Builder/Reader split (Phase 3 of #3524)
Phase 3 of the CJS↔SDK hard-seam migration (parent #3524). Introduces the Builder/Reader pattern for paired Modules with mixed pure-and-I/O concerns — the template for Phase 4 and follow-up enhancements that migrate other paired Modules. Phase 1 and Phase 2 migrated Modules where both sides used character-equivalent logic. Phase 3 introduces the case where the pure logic is shareable but the I/O is legitimately per-side. The Builder/Reader split resolves this: - The Builder is pure — accepts pre-collected data (BuilderInputs struct), returns the typed projection. One source of truth; one generator-emitted CJS mirror. Drift is structurally impossible. - The Readers are per-side hand-authored Adapters that do the fs reads in their native idiom (currently both sync; either side can go async later without touching the Builder), then delegate to the Builder. - sdk/src/workstream-inventory/builder.ts — Builder source. 170 lines. Pure. Exports buildWorkstreamInventory(inputs), isCompletedInventory(status), plus the three typed inventory interfaces (WorkstreamPhaseInventory, WorkstreamInventory, WorkstreamInventoryList). - sdk/src/workstream-inventory/builder.test.ts — 18 vitest pinning fixtures across all status branches, progress-percent clamping, active-marker projection, and isCompletedInventory classifier. - sdk/scripts/gen-workstream-inventory-builder.mjs — generator. Captures function bodies via Function.prototype.toString(); emits with the standard GENERATED FILE banner. Includes a small `const relative = path.relative;` preamble in the output to handle ESM destructured imports in the compiled source. - sdk/scripts/check-workstream-inventory-builder-fresh.mjs — freshness check. Imports the generator function directly (rather than duplicating logic) — a cleaner pattern than Phase 1/2's approach. - get-shit-done/bin/lib/workstream-inventory-builder.generated.cjs — generator-emitted CJS mirror. - tests/workstream-inventory-builder-generator.test.cjs — 16 parity assertions confirming CJS-generated output == SDK source output for every fixture. - bin/lib/workstream-inventory.cjs: 159 → 132 lines. Projection logic gone. `inspectWorkstream` and `listWorkstreamInventories` collect BuilderInputs via the existing sync fs functions and delegate to the Builder. `isCompletedInventory` re-exported from the Builder (its signature changed from object→string, but no external callers exist so the change is safe). - sdk/src/query/workstream-inventory.ts: 196 → 143 lines. Same shape, sync fs (the SDK was already sync — surprise from recon). Types re-exported from the Builder. - sdk/package.json: gen:workstream-inventory-builder and check:workstream-inventory-builder-fresh scripts. - package.json: proxy for the freshness check. - .githooks/pre-commit: drift block. - .github/workflows/test.yml: drift check step. - CONTEXT.md: amended "Workstream Inventory Module" entry to document the Builder/Reader split. - docs/INVENTORY.md, docs/INVENTORY-MANIFEST.json: +1 module count, +1 row for the generated builder. - Full suite: 9229/9229 pass (baseline 9215 + 14 net new from the parity assertions). - Vitest: 18 Builder fixtures pass. - Reader shrink: -27 lines on CJS, -53 lines on SDK. - Net diff (modified files only): +68 / -133 = 65-line reduction. New files (Builder, generator, freshness check, parity test) add ~600 lines of new structured code. 1. `isCompletedInventory` signature changed from isCompletedInventory(inventory: object) to isCompletedInventory(status: string). Original CJS exported the object form but no external caller passed an object — they all passed inventory.status. Verified by grep before committing. 2. Generator preamble. The compiled ESM uses `import { relative } from 'node:path'`, making `relative` a free variable in `buildWorkstreamInventory`. The generator emits `const relative = path.relative;` so the captured function body works in CJS. 3. Freshness check imports the generator. The freshness check imports the generator's buildWorkstreamInventoryBuilderCjs() function directly rather than duplicating generation logic. Cleaner than Phase 1/2; future generators should follow this. Shareable via the Builder/Reader pattern in future enhancements: - frontmatter (pure YAML/markdown parsing) - plan-scan (pure PLAN.md structure parsing) - decisions (pure decision-record parsing) - secrets (regex-based detection in text) - uat (UAT-criteria parsing) Structural divergence — different approach needed: - state — sync vs async file ops; mutation paths differ. - workstream — lifecycle ops; per-side API surface differs. - phase, roadmap, init, profile-output, template — large surfaces; each its own potential enhancement. None of these is in scope for Phase 3. Closes #3544. |
||
|
|
2de2d185fa |
feat(3536): Configuration Module via shared manifests + generator (Phase 2 of #3524)
Phase 2 of the CJS↔SDK hard-seam migration (parent #3524). Eliminates the structural drift surface that produced bug class After this phase, neither bin/lib/ nor sdk/src/ defines CONFIG_DEFAULTS, VALID_CONFIG_KEYS, DYNAMIC_KEY_PATTERNS, or the four legacy-key normalizations inline. All come from one canonical source: the Configuration Module (sdk/src/configuration/index.ts) + two JSON manifests (sdk/shared/config-{defaults,schema}.manifest.json). The CJS mirror is generator-emitted (get-shit-done/bin/lib/configuration.generated.cjs) with a CI freshness check (sdk/scripts/check-configuration-fresh.mjs). - sdk/shared/config-defaults.manifest.json — canonical nested defaults, union of CJS + SDK keys (includes security_*, post_planning_gaps, agent_skills, mode, every git/workflow/hooks sub-section). - sdk/shared/config-schema.manifest.json — VALID_CONFIG_KEYS array, RUNTIME_STATE_KEYS array, DYNAMIC_KEY_PATTERNS array with source strings (regex reconstructed at runtime). - sdk/src/configuration/index.ts — source of truth. Exports loadConfig (pure read), normalizeLegacyKeys (pure, idempotent, returns Normalization[]), mergeDefaults (deep-merge), migrateOnDisk (explicit opt-in disk writeback), plus CONFIG_DEFAULTS, VALID_CONFIG_KEYS, RUNTIME_STATE_KEYS, DYNAMIC_KEY_PATTERNS. - sdk/src/configuration/index.test.ts — 29 vitest pinning tests. - sdk/scripts/gen-configuration.mjs — generator (Function.prototype.toString() inspection of compiled SDK dist, plus brace-balanced text scan for internal helpers, matching the Phase 1 pattern). - sdk/scripts/check-configuration-fresh.mjs — CI freshness gate. - tests/configuration-generator.test.cjs — 27 parity assertions (CJS-generated == SDK source). - tests/configuration-migrate-config.test.cjs — 3 cases for the new gsd-tools migrate-config subcommand. - bin/lib/core.cjs: CONFIG_DEFAULTS literal now sources values from CANONICAL_CONFIG_DEFAULTS (the manifest), with a thin flat projection at the load boundary to preserve the existing flat-shape return contract for the ~21 CJS test files and 100+ consumers. All four legacy-key migration blocks (branching_strategy, sub_repos, multiRepo, depth — historically lines 351-358, 388-397, 401-408, 416-423) collapse to a single normalizeLegacyKeys call in each code path. The inline platformWriteSync writeback stays for now to preserve sync loadConfig semantics; the new async migrateOnDisk is reachable via gsd-tools migrate-config. - bin/lib/config-schema.cjs: 135 → 31 lines. Re-exports from the generated Module. - bin/lib/config.cjs: adds cmdMigrateConfig handler (calls migrateOnDisk on the explicit user-driven path). - bin/gsd-tools.cjs: wires migrate-config into command dispatch. - sdk/src/config.ts: re-exports CONFIG_DEFAULTS and mergeDefaults from the Module. loadConfig now calls normalizeLegacyKeys before mergeDefaults (replaces the inline branching_strategy graft). - sdk/src/query/config-schema.ts: 160 → 36 lines. Re-exports from the Module. - tests/config-schema-sdk-parity.test.cjs: refactored from "CJS Set equals SDK Set" (trivially true post-migration) to "both sides source from the manifest" — structural plus runtime invariant. - Four other tests that text-grepped source files for valid keys (plan-review-convergence, bug-3212, bug-2492, feat-3210) are updated to use runtime VALID_CONFIG_KEYS.has() or manifest JSON lookups. - CONTEXT.md: new Configuration Module entry with full Interface contract. - Root package.json: check:configuration-fresh proxy script. - sdk/package.json: gen:configuration + check:configuration-fresh. - .githooks/pre-commit: configuration drift block. - .github/workflows/test.yml: configuration drift step after the alias drift check. - 9201 CJS tests pass (baseline pre-cycle: 9195; +6 net new tests across migrate-config + parity refactor) - 1872 SDK vitest tests pass - 29 Configuration Module vitest fixtures - 27 CJS/SDK parity fixtures - Net diff: +388 / −519 = 131-line reduction across the seven cycles, despite adding the new Module, manifests, generator, freshness check, and two new test files. 1. SDK CONFIG_DEFAULTS now includes manifest-canonical keys (resolve_model_ids: false, context_window: 200000, phase_naming, claude_md_path, git.create_tag, workflow.security_*, workflow.code_review_*, planning.*, hooks.workflow_guard, ship.*). Consumers accessing via [key: string]: unknown index get the manifest default instead of undefined. 2. SDK mergeDefaults is now proper recursive deep-merge instead of spread-per-section. Overlay { workflow: { research: false } } now preserves sibling workflow keys; previously it replaced the entire workflow section with only research + the section's defaults. Semantically identical for the common case; strictly better for partial nested overrides. 3. New gsd-tools migrate-config CLI subcommand for the explicit, opt-in on-disk migration path. Closes #3536. |
||
|
|
bfd7ddbad3 |
feat(3530): STATE.md Document Module via generator (Phase 1 of #3524) (#3531)
* feat(3530): STATE.md Document Module via generator (Phase 1 of #3524) Phase 1 of the CJS↔SDK hard-seam migration (parent #3524). Converts the hand-synced state-document.cjs/state-document.ts pair into a generator-driven seam, modeled on the existing command-aliases.generated.* precedent. What landed: - sdk/src/query/state-document.ts is the source of truth. - sdk/scripts/gen-state-document.ts emits get-shit-done/bin/lib/state-document.generated.cjs from the compiled SDK dist via Function.prototype.toString() inspection for the 7 public exports and 3 internal helpers. - sdk/scripts/check-state-document-fresh.mjs is the CI freshness gate; pre-commit hook also runs it when relevant files change. - get-shit-done/bin/lib/state-document.cjs is reduced to a one-line re-export from state-document.generated.cjs so existing callers (state.cjs, workstream-inventory.cjs, init.cjs) need no changes. - New CI step in .github/workflows/test.yml after the existing alias drift check. - sdk/package.json: gen:state-document, check:state-document-fresh scripts. tsx added as devDep. - Root package.json: proxy script for the freshness check. - CONTEXT.md: one-sentence amendment on STATE.md Document Module recording the source-of-truth file path. Tests: - sdk/src/query/state-document.test.ts: 34 vitest fixtures across the 7 public exports (TDD pinning safety net). - tests/state-document-generator.test.cjs: 31 node:test parity assertions comparing SDK source vs generated CJS for every fixture. - Full suite: 9177/9177 pass (baseline was 9146; +31 new tests). One subtle behavior change worth flagging: the old hand-written state-document.cjs used String(str) coercion inside escapeRegex, which the SDK source does not. The generator faithfully matches the SDK (the source of truth per ADR-3524), so the new CJS no longer coerces non-string input to string before regex-escaping. No current caller passes non-string input, so no observable regression in the test suite. Flagged in the PR body for reviewers. Closes #3530. * fix(3530): address state-document review findings |
||
|
|
75d5ca5875 |
feat(code-review): integrate fallow structural pre-pass for /gsd-code-review (#3424)
* feat(code-review): add optional fallow structural pre-pass * fix(ci): sync lockfile for fallow optional binaries * fix(test): make fallow integration tests cross-platform * fix(review): require executable fallow binary paths * docs(review): clarify structural findings usage and size guard * fix(fallow): preserve line:0, prefer node_modules/.bin, sync SDK twin (H1, M2, N1 from #3424 review) * fix(workflow): harden fallow pre-pass — exit check, timeout, atomic write, size-guard order (B1, H2-H4, M1, M3 from #3424 review) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deps): pin fallow floor to ^2.70.0 matching lockfile (H7 from #3424 review) * fix(config): enum-validate fallow.scope/profile + group code_quality.* contiguously (H5, N3 from #3424 review) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(fallow): label mcp gate reserved, version-pin install, expand context schema (B3, H8, M4, M8, L1 from #3424 review) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(fallow): replace source-grep with behavioral tests, expand fixtures, fail-loud tmpdir (B4, H6, L2, L3, M5, M6, N2 from #3424 review) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(workflow): escape closing structural_findings tag in JSON payload (CR #3424 inline finding) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
d0f916728b |
feat(skill-surface): install-time profiles + runtime /gsd:surface (#3408) (#3456)
* feat(skill-deps): add requires: frontmatter to all 51 skills with cross-skill references Mechanical migration from docs/research/data/2026-05-12-skill-audit.json. Every skill whose body references another GSD skill now declares those dependencies in `requires:` YAML frontmatter (flow-style array). Notable: discuss-phase, plan-phase, and execute-phase all reference `phase`, which confirms the latent gap in MINIMAL_SKILL_ALLOWLIST — `phase` is pulled by the core loop but was never in the allowlist. The profile closure model (ADR-0010 Phase 1) resolves this automatically. Closes part of #3408. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(skill-surface-budget): add PROFILES map, resolveProfile, loadSkillsManifest, staging, marker IO Implements the Skill Surface Budget Module core (ADR-0010, Phase 1): - PROFILES Object.freeze map: core (6 skills), standard (~13), full ('*') - loadSkillsManifest: parses requires: frontmatter from commands/gsd/*.md into a Map<stem, string[]> without external YAML dep - resolveProfile({modes, manifest}): computes transitive closure over the requires: graph; composable (modes=['core','audit'] unions closures) - stageSkillsForProfile / stageAgentsForProfile: filesystem staging with same exit-cleanup machinery as the legacy stageSkillsForMode - readActiveProfile / writeActiveProfile: .gsd-profile marker round-trip - Back-compat shims preserved: MINIMAL_SKILL_ALLOWLIST, isMinimalMode, shouldInstallSkill (overloaded), stageSkillsForMode — all legacy tests pass The phase latent bug is now resolved by closure: discuss-phase, plan-phase, and execute-phase all require phase, so any profile including any of them automatically includes phase via transitive closure. Tests: 22 manifest+resolve, 9 stage, 10 marker (41 new tests, all green). Back-compat anchor: 80/80 passing. Closes part of #3408. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(skill-surface-budget): add lint-skill-deps.cjs CI gate and fix 19 missed requires: entries Two lint checks (scripts/lint-skill-deps.cjs): a) Frontmatter-body consistency: skill body references must appear in requires: b) Profile closure: every requires: dep of any profile skill must be in closure Running the lint revealed 19 body references missed by the audit JSON (the audit used static analysis; some bodies have conditional references). Fixed: complete-milestone: +audit-milestone, discuss-phase, plan-phase, execute-phase, new-milestone fast: +quick health: +thread map-codebase: +new-project, plan-phase new-milestone, new-project, review, ultraplan-phase: +plan-phase ship: +verify-work sketch, spike: +new-project verify-work: +execute-phase workstreams: +new-milestone, resume-work Wired into package.json as lint:skill-deps and added to pretest. 8 fixture-based tests: all green. Closes part of #3408. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(skill-surface-budget): wire --profile= arg, profile marker write/read in bin/install.js - Add --profile=<name> / --profile=<n1>,<n2> arg parsing (composable). Mutually exclusive with --minimal / --core-only (aliases for --profile=core). Default (no flag): full. - Import readActiveProfile / writeActiveProfile from install-profiles.cjs. - After writeManifest: persist active profile to .gsd-profile marker. - gsd update path: if no --profile flag given, read existing .gsd-profile marker so non-full profiles are not silently re-expanded to full (ADR-0010). - Update --help block to document --profile= with per-tier token costs. New test: install-minimal-backcompat.test.cjs (6 tests): - PROFILES.core === MINIMAL_SKILL_ALLOWLIST (contract) - --minimal writes .gsd-profile marker "core" - --profile=core, --profile=standard write correct markers - default install writes marker "full" Closes part of #3408. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(changeset): add feat-3408-skill-profiles changelog fragment Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(install-profiles): derive agents from skill body refs and wire into resolveProfile Deviation 1 of ADR-0010 phase 1b: tiered profiles (core, standard) now produce a non-empty agents Set instead of always returning empty. resolveProfile() scans each skill body for gsd-* agent name references (via new parseCallsAgents()), stores them in _calls_agents_<stem> manifest entries, and unions them across the resolved skill closure. stageAgentsForProfile() already checked resolvedProfile.agents — it now gets real data so tiered profiles install the correct subset of agents instead of zero. Closes #3408 (partial — Deviation 1 only) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(install): honor .gsd-profile marker on update, add resolveEffectiveProfile/mostRestrictiveProfile Deviation 2 of ADR-0010 phase 1b: the marker written during installation is now actually honored when re-running without explicit flags (e.g. gsd update). The dead-end logging block is replaced by resolveEffectiveProfile(), which picks the marker profile over 'full' when no explicit --profile= flag was given. The resolved profile is piped through to all 13 stageSkillsForMode dispatch sites (now _stageSkills) so updates install only the previously-chosen skill subset. --minimal retains its back-compat behavior (strict 6-skill allowlist, no closure) while writing 'core' to the marker. mostRestrictiveProfile() is exported for callers that need to reconcile disagreeing markers across runtimes (smallest skill set wins). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(surface): add CLUSTERS data + state IO module Add clusters.cjs with 10 named skill groups covering all 66 skills (verified by surface-clusters.test.cjs). Add surface.cjs with readSurface/ writeSurface atomic IO, resolveSurface, applySurface, and listSurface. Tests: 17 passing (11 state IO + 6 cluster integrity). Closes #3408 * docs(adr): add ADR-0011 Skill Surface Budget Module (Phase 1 accepted, Phase 2 amendment) Records the install-time profile staging decision (Phase 1, landed) and the runtime /gsd:surface cluster-toggle decision (Phase 2, in flight) as an amendment. Updates the ADR README index. Closes #3408 * docs(install-profiles): update module docblock for Phase 2 and ADR-0011 Corrects the ADR reference from 0010 to 0011, documents the three-profile model and back-compat aliases, adds resolveEffectiveProfile precedence rule, and notes the companion surface.cjs Phase 2 engine. * docs(context): add Skill Surface Budget Module canonical entry Adds the Domain terms entry for the Skill Surface Budget Module covering both Phase 1 (install-time profiles, .gsd-profile marker) and Phase 2 (runtime /gsd:surface cluster toggles, clusters.cjs, .gsd-surface.json), per ADR-0011 Consequences requirement. * feat(surface): add resolveSurface and applySurface engine + tests Tests cover: profile → surface equivalence, cluster disable/enable, explicitAdds transitive closure, applySurface file sync (add missing, remove superseded, preserve non-gsd files), listSurface token cost. 16 new tests passing. * docs(readme): document --profile= flag and /gsd:surface command Brief user-facing mention of install profiles (core/standard/full) and the /gsd:surface slash command in the Commands table. Points to ADR-0011 for details. * feat(surface): add /gsd:surface slash command runbook New skill: gsd:surface — runtime profile/cluster toggle without reinstall. Sub-commands: list, status, profile <name>, disable/enable <cluster>, reset. Persists state to .gsd-surface.json (independent of .gsd-profile). Description 96 chars (≤100 limit). lint:descriptions + lint:skill-deps: 0 violations. * feat(surface): add changeset fragment for /gsd:surface runtime toggle * feat(surface): add surface skill stem to utility cluster surface.md is a new skill; add it to the utility cluster so the surface-clusters.test.cjs coverage invariant stays satisfied. * docs(adr): fix ADR references to 0011 and record Phase 2 as shipped ADR-0010 number was already claimed by the file-operation-engine ADR; this ADR landed as 0011-skill-surface-budget-module.md. Update inline ADR references in clusters.cjs, surface.cjs, install-profiles.cjs, and the Phase 2 changeset to ADR-0011. Update the ADR Status section to record Phase 2 artifacts as shipped on this branch rather than "in progress". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(research): port skill-surface-budget memo and audit data ADR-0011 references docs/research/2026-05-12-skill-surface-budget.md and docs/research/data/2026-05-12-skill-audit.json, which only existed in the research worktree. Port both onto this branch so the ADR's References section resolves and reviewers can read the cluster taxonomy (§3.2), dependency topology (§3.1), and option grading (§4) that justify Phase 1 and Phase 2 decisions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(registration): register surface/clusters in INVENTORY, COMMANDS, and help.md - surface.md: convert allowed-tools from inline YAML array to block style (was parsed as a single tool name "[Read, Write, Bash]" by test harness) - docs/INVENTORY.md: add CLI module rows for clusters.cjs and surface.cjs; add Commands row for /gsd-surface; bump CLI Modules count 55→57, Commands 66→67 - docs/INVENTORY-MANIFEST.json: add entries for clusters.cjs, surface.cjs, and /gsd-surface (filename-based command key) - docs/COMMANDS.md: add ### `/gsd-surface` heading in Configuration Commands - get-shit-done/workflows/help.md: add /gsd:surface entry in Configuration section Fixes registration failures introduced by Phase 2 of #3408. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(surface,docs): scrub .claude leakage and escape hypothetical slash tokens Two PR regressions introduced earlier on this branch: 1. surface.cjs JSDoc comments contained the canonical paths (~/.claude/commands/gsd, ~/.claude/agents) as example values, which the cline-install leak regex (~\/\.claude\/(?:get-shit-done|commands|agents |hooks)) flagged as install-time path leaks. Reworded the docblocks to describe runtime-resolved paths without literal ~/.claude tokens. 2. The ported research memo proposed hypothetical Option C dispatchers using slash syntax (/gsd:milestone, /gsd:research). The docs-parity-live-registry test enforces that every slash-command token in docs/ resolves to a real command. Rewrote the Option C sketch without the slash prefix and added a clarifying note that the dispatchers are illustrative, not shipped. Targeted tests now pass: tests/cline-install.test.cjs and tests/docs-parity-live-registry.test.cjs both green. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: remove raw output/source grep in lint tests * fix: close coderabbit profile and requires issues * test: align surface token-cost assertion wording * fix(install): align core profile alias and defer profile marker write --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
96806003c5 |
fix(#3229): shared model catalog source of truth for agent profiles + runtime tier defaults (#3230)
* docs(adr): add ADR-0003 model catalog module * fix(#3229): add shared model catalog as source of truth for agent profiles and runtime tier defaults Research / design (ADR-0003): - Existing drift came from 4 independent model truths: 1. CJS model-profiles.cjs 2. SDK config-query.ts stale copy (18 agents) 3. settings-advanced.md runtime tier table 4. session-runner Claude-only profile map - New design: one machine-readable Model Catalog Module in sdk/shared/ that both packages ship and consume. Implementation: - sdk/shared/model-catalog.json — canonical source of truth for: - full 33-agent registry - per-agent golden (quality) alias + balanced/budget aliases - adaptive derivation from routingTier - agent→phaseType map - agent→dynamic-routing default tier map - runtime tier defaults for all supported runtimes - get-shit-done/bin/lib/model-catalog.cjs — CJS adapter over the catalog - sdk/src/model-catalog.ts — SDK adapter over the same catalog - CJS model-profiles.cjs now re-exports derived data from model-catalog.cjs - SDK config-query.ts now re-exports MODEL_PROFILES/VALID_PROFILES from model-catalog.ts instead of maintaining its own list - sdk/src/query/helpers.ts runtime list now comes from the catalog (fixes hermes drift) - sdk/src/session-runner.ts Claude profile→model-id mapping now resolves via catalog - docs/CONFIGURATION.md + settings-advanced.md runtime tables updated to match catalog Behavior changes: - resolve-model now covers every shipped agent file on disk (33 agents) - unknown-agent fallback is profile-semantic, not hardcoded sonnet: quality→opus, budget→haiku, balanced/adaptive→sonnet, inherit→inherit - Group B runtimes remain known runtimes but do not get built-in tier defaults Tests (RED→GREEN): - root tests: shipped agent files must equal MODEL_PROFILES keys - sdk tests: shipped agent files must equal MODEL_PROFILES keys - direct fix assertion: gsd-code-reviewer resolves to opus under quality with no unknown_agent - runtime defaults parity test: settings-advanced.md + CONFIGURATION.md tables must match catalog - helper tests: hermes included in SUPPORTED_RUNTIMES and getRuntimeConfigDir() Closes #3229 * chore(changeset): update #3229 changeset pr field to 3230 * fix(ci): update inherit fallback expectations and inventory parity for model catalog |
||
|
|
2d32ad82be |
fix(plan-phase): remove agent: directive that caused OpenCode subagent dispatch (#3156) (#3206)
* feat(roadmap): parse **Mode:** field on phase sections Adds a 'mode' field to roadmap.get-phase and roadmap.analyze outputs. Recognizes '**Mode:** mvp' lines in phase sections; lowercased + trimmed. Forward-compat: unrecognized values preserved verbatim, no enum check. Foundation for --mvp flag in plan-phase (PRD: vertical-mvp-slice). * feat(plan-phase): parse --mvp flag and resolve MVP_MODE Resolution order: CLI flag → ROADMAP **Mode:** field → workflow.mvp_mode config → false. Walking Skeleton gate fires for new-project Phase 1. Wires MVP_MODE + WALKING_SKELETON into gsd-planner subagent prompt. Per PRD vertical-mvp-slice Phase 1 (Q1, Q2, Q4). * docs(planner): add vertical-slice planning reference New reference loaded by gsd-planner when MVP_MODE=true. Defines slice ordering, Walking Skeleton rules, and anti-patterns. Referenced from plan-phase workflow MVP_MODE wiring. * docs(planner): add SKELETON.md template Template emitted by gsd-planner under WALKING_SKELETON=true. Captures architectural decisions and out-of-scope list for new-project Phase 1. * chore(inventory): register new planner references Added planner-mvp-mode.md and skeleton-template.md to INVENTORY.md and INVENTORY-MANIFEST.json. References now: 53. * feat(gsd-planner): add MVP Mode Detection section Mode-switched branch in the existing planner agent (per Q4: single agent). Vertical-slice decomposition rules, Walking Skeleton handling, and TDD-mode compatibility. Heavy guidance lives in references/planner-mvp-mode.md. * test(plan-phase): add --mvp resolution-chain integration cases Validates roadmap.get-phase --pick mode and confirms workflow.mvp_mode default is unset in fresh projects. * docs(changelog): announce --mvp vertical-slice planning (#2826) * feat(mvp-phase): add /gsd mvp-phase slash command Standalone command for vertical MVP planning. Frontmatter only; heavyweight workflow at get-shit-done/workflows/mvp-phase.md follows in next commit. Mirrors discuss-phase/edit-phase command shape. * docs(planner): add user-story-template reference Defines the canonical 'As a / I want to / So that' format and the ROADMAP.md / PLAN.md emit rules. Used by mvp-phase workflow and gsd-planner agent under MVP_MODE. * docs(planner): add SPIDR splitting reference Defines size signals, the five SPIDR axes (Spike/Paths/Interfaces/Data/Rules), the interactive workflow, and anti-patterns. Per PRD Q3 decision: full interactive flow, not lightweight check. Used by mvp-phase workflow. * fix(mvp-phase): trim description to fit 100-char budget * feat(mvp-phase): add mvp-phase workflow Standalone workflow: phase validation -> user story prompts (As a / I want to / So that) -> SPIDR splitting check -> ROADMAP write (Mode + Goal) -> delegation to plan-phase. Per PRD Phase 2 (Q3 full SPIDR; Phase-2-A/B/C/D decisions). Plan-phase auto-detects MVP via Phase 1's resolution chain, so no flags are needed when delegating. * feat(gsd-planner): emit user-story header in PLAN.md under MVP mode Extends the MVP Mode Detection section (added in Phase 1) so the planner sources the user story from ROADMAP **Goal:** and emits the bolded **As a** / **I want to** / **so that** form as the first content under the phase header in PLAN.md. References user-story-template.md. * test(mvp-phase): integration smoke test for ROADMAP mutation Validates roadmap.get-phase output after a workflow-spec'd ROADMAP write: mode=mvp and goal=full user story. Catches schema drift between workflow emit and parser expectation. Includes a long-story case (>120 chars) to confirm SPIDR-rejected stories still parse correctly. * chore(inventory): register mvp-phase command + 2 new references Adds /gsd mvp-phase to commands list, mvp-phase workflow to workflows list, and user-story-template.md + spidr-splitting.md to references. References count: 53 -> 55. * docs(changelog): announce /gsd mvp-phase command (#2826) * fix(mvp-phase): add TEXT_MODE plain-text fallback for non-Claude runtimes (#2012) * docs(executor): add MVP+TDD gate reference Defines the runtime gate semantics for execute-phase when both MVP_MODE and TDD_MODE are true: pre-task verification of failing-test commit, end-of-phase review escalation from advisory to blocking, behavior-adding task definition. Loaded conditionally by execute-phase workflow and gsd-executor agent. * feat(execute-phase): MVP+TDD runtime gate + blocking review Resolves MVP_MODE in Step 1 (CLI flag -> roadmap mode -> config -> false). Adds per-task gate that halts before behavior-adding tasks run if no failing-test commit exists for the plan. Escalates end-of-phase TDD review from advisory to blocking when both MVP_MODE and TDD_MODE active. Also updates INVENTORY-MANIFEST.json to register execute-mvp-tdd.md (added by Task 1) so manifest-sync tests pass. Per PRD vertical-mvp-slice Phase 3a (decisions Phase-3-A, Phase-3-Split). * feat(gsd-executor): add MVP+TDD Gate section Mirrors the planner's MVP Mode Detection pattern from Phase 1. Instructs halt-and-report when the runtime gate trips, references execute-mvp-tdd.md for full semantics. No agent changes outside the new section. * test(execute-phase): add MVP+TDD resolution-chain integration cases Validates roadmap.get-phase --pick mode and confirms workflow.mvp_mode default is unset in fresh projects. Mirrors the Phase 1 plan-phase resolution-chain integration test. * chore(inventory): register execute-mvp-tdd reference Bumps References count 55 -> 56. Registers execute-mvp-tdd.md. Adds "init" to PROSE_ALLOWLIST in registry integration test so bare `gsd-sdk query init` prose examples in plan docs don't trigger the unregistered-handler guard (real commands are all init.<subcommand>). * docs(changelog): announce MVP+TDD runtime gate in execute-phase (#2826) * docs(verifier): add verify-mvp-mode reference Defines UAT framing under MVP mode: user-flow walk-through first, technical checks deferred, coverage check as goal-backward narrowing to the user story's outcome clause. Loaded conditionally by verify-work workflow and gsd-verifier agent. * feat(verify-work): MVP-mode UAT framing — user flow first Resolves MVP_MODE from phase mode field. Under MVP mode, generates UAT in three ordered sections: user-flow walk-through (derived from user story), technical checks (deferred), coverage check (goal-backward). Falls back to standard UAT generation when mode is null/absent. User-story-format guard refuses to verify a mode:mvp phase with a non-user-story goal. Also updates docs/INVENTORY.md (56 references) and docs/INVENTORY-MANIFEST.json to register verify-mvp-mode.md added in Task 1. Per PRD vertical-mvp-slice Phase 3b (decisions Phase-3-B, Phase-3-Verify-Structure). * feat(gsd-verifier): add MVP Mode Verification section Narrows goal-backward verification to the user-story [outcome] clause when phase mode is mvp. References verify-mvp-mode.md. Preserves existing goal-backward methodology for non-MVP phases. User-story-format guard refuses to verify a mode:mvp phase with a non-user-story goal. * docs(changelog): announce MVP-mode UAT framing in verify-work (#2826) * feat(new-project): add Vertical MVP vs Horizontal Layers mode prompt Asks user at project init how to structure the project. Vertical MVP emits **Mode:** mvp on every initial roadmap phase (per-phase mode preserved per PRD Q1). Horizontal Layers falls back to standard template — no behavioral change for existing flows. Per PRD vertical-mvp-slice Phase 4 (decision Phase-4-Persistence). * feat(progress): add MVP-mode user-flow display When phase has **Mode:** mvp, progress renders user-flow status from PLAN.md task names alongside standard task progress. Tasks that aren't user-flow-shaped (technical-sounding) are filtered out of the user-flow sub-block. Falls back to standard display when mode is null/absent. Per PRD vertical-mvp-slice Phase 4 (decision Phase-4-Progress). * feat(stats): add MVP phase count summary Reads roadmap.analyze (which surfaces mode per phase from Phase 1) and emits 'Phases: N total | M MVP | K standard' summary line. Suppressed when MVP_COUNT == 0 to avoid clutter on non-MVP projects. Per PRD vertical-mvp-slice Phase 4. * feat(graphify): add MVP-mode visual differentiation MVP-mode phases render with #22c55e fill color AND ' (MVP)' label suffix — two-channel signaling for color-blind and grayscale renders. Standard phases unchanged. Per PRD vertical-mvp-slice Phase 4 (PRD Q5: distinct visual treatment). * docs(changelog): announce Phase 4 discovery & progress (#2826) * chore(release): bump dev to 1.50.0-canary.0 for first 1.50.0 canary Sets the base version that .github/workflows/canary.yml derives the canary tag from (strips suffix → base 1.50.0 → next available v1.50.0-canary.N). This kicks off the 1.50.0 release train, opened by the MVP/TDD/UAT vertical slice landed across PRs #2867, #2874, #2878, #2880, #2883. * docs: add CANARY stream README + v1.50.0-canary.1 release notes - docs/CANARY.md — explains the dev→@canary stream policy, install/rollback paths, and when (not) to install canary builds - docs/RELEASE-v1.50.0-canary.1.md — release notes for the first 1.50.0 canary cut: vertical MVP/TDD/UAT slice (#2867 + #2874 + #2878 + #2880 + #2883), opening the 1.50.0 train under PRD #2826 - docs/README.md — index entry + quick link for the canary stream * fix(ci/canary): publish gate checks dev branch, not main Four publish-step `if:` conditions in .github/workflows/canary.yml were checking `github.ref == 'refs/heads/main'`. Those steps (Tag and push, Publish to npm, Publish SDK to npm, Verify publish) therefore always skipped on every workflow_dispatch invocation since canary runs from dev, never main. The workflow's own header comment is unambiguous: `dev → @canary`. The gate was a copy-paste from release.yml (which correctly targets main for the @next/@latest streams) that was never corrected for the canary stream. This is why the 1.50.0-canary.1 publish hadn't materialized despite three green workflow runs. With the gate corrected, the next dispatch will actually publish. * ci(release-sdk): make release-sdk.yml dispatchable from the dev branch The workflow lives on main only, so the GitHub Actions "Use workflow from" dropdown doesn't list dev — meaning dev → @dev publishes can't be triggered from the dev branch directly. Add the file to dev so an operator can dispatch it with branch=dev and tag=dev. Per project release-stream policy: dev branch publishes canary (@dev). This is the stream that needs the file most, since main never publishes @dev itself (main does @next / @latest). File is byte-identical to main's release-sdk.yml — straight propagation, no behavioral change. Tracking issues #2925, #2929. * docs(mvp): canary-prep concept cleanup — CONTEXT.md, mvp-concepts index, --prd interaction (#3176) * chore(mvp): concept cleanup + cross-ref index for v1.50.0-canary.2 prep - CONTEXT.md gains 7 MVP domain terms (MVP Mode, User Story, Walking Skeleton, Vertical Slice, Behavior-Adding Task, MVP+TDD Gate, SPIDR Splitting) so the project glossary matches the shipped surface. - New get-shit-done/references/mvp-concepts.md indexes the six MVP reference files and concept-to-file map so agents and contributors can find the right canonical doc without grepping. - plan-phase.md Walking Skeleton block now documents that --mvp and --prd compose orthogonally on Phase 1; no precedence needed. - INVENTORY/INVENTORY-MANIFEST refreshed for the new reference (58 -> 59). No behavior change. Canary-prep cleanup ahead of v1.50.0-canary.2. Surfaced for follow-up (not in this PR): - MVP_MODE resolution shell block duplicated across plan-phase, execute-phase, verify-work workflows (needs a shared workflow-include mechanism; structural change). - Behavior-Adding Task predicate is prose-only; no shared utility. - User Story regex hardcoded in verify-work; would benefit from a central definition consumed by the verifier and the mvp-phase command. * chore(changeset): set PR number for mvp concept cleanup * feat(mvp): centralize resolution surfaces + fix SDK roadmap mode parity (#3178) Three new SDK query verbs replace the architectural duplication surfaced by the v1.50.0-canary.2 review against dev tip 12c4e565: phase.mvp-mode <N> [--cli-flag] Single canonical precedence resolver (CLI flag -> ROADMAP **Mode:** mvp -> workflow.mvp_mode config -> false). Replaces 4-8 lines of bash that were duplicated across plan-phase.md, execute-phase.md, verify-work.md, and progress.md. Returns {active, source, roadmap_mode, config_mvp_mode, cli_flag_present}. task.is-behavior-adding <plan-file> | --task-content <xml> Behavior-Adding Task predicate (tdd="true" + <behavior> block + non-test source files in <files>). Replaces prose-only specification in references/execute-mvp-tdd.md; gsd-executor agent now invokes the verb instead of re-inlining the three checks. Returns {is_behavior_adding, checks, reason}. user-story.validate <text> | --story <text> Owns the canonical User Story regex /^As a .+, I want to .+, so that .+\.$/ previously hardcoded in verify-work.md prose. Consumed by gsd-verifier (phase-goal guard) and /gsd-mvp-phase (interactive-prompt validation). Returns {valid, slots: {role, capability, outcome}, errors[]}. Bug fix bundled: sdk/src/query/roadmap.ts searchPhaseInContent now extracts the mode field from **Mode:**, restoring parity with roadmap.cjs:120-123. Without this, roadmap.get-phase --pick mode returned null on the native dispatch path even when the phase had **Mode:** mvp set, causing MVP_MODE to silently fall through to the config/false branch in every consuming workflow. The original PRs Phase 1 (#2885) shipped the CJS parser but the SDK port omitted the field; this fix brings them back to parity. Workflows + agents updated to call the verbs: - plan-phase.md, execute-phase.md, verify-work.md, progress.md call phase.mvp-mode (one line replaces the duplicated bash chains). - execute-phase.md MVP+TDD gate calls task.is-behavior-adding. - verify-work.md goal guard calls user-story.validate. - mvp-phase.md interactive prompt validates via user-story.validate. - gsd-executor agent references task.is-behavior-adding instead of prose. - gsd-verifier agent references user-story.validate instead of inlined regex. Tests: 24 new vitest tests in sdk/src/query/mvp.test.ts cover all three verbs + the regression. Two existing contract tests (progress, verify) updated to assert on the new verb shape. All 60 existing MVP contract tests pass; golden integration suite (38 + 42 tests) passes. Closes #3177 * fix(canary.2): unblock release gates for v1.50.0-canary.2 Run 25451329660 (Release SDK Bundle on dev, 2026-05-06T17:41) failed at the test-suite step with 3 deterministic content/structure gate failures, all attributable to the MVP umbrella integration in #3178 and the docs sweep in #3180. Failure 1: /gsd-mvp-phase undocumented in workflows/help.md - tests/bug-2954-help-md-slash-command-stubs.test.cjs requires every shipped commands/gsd/<X>.md to have a /gsd-<X> mention in help.md - PR #3180 updated docs/COMMANDS.md but missed help.md (which the AI agents load in-product) - Fix: add a /gsd-mvp-phase entry to help.md right before /gsd-plan-phase Failures 2 + 3: execute-phase.md (1727) and plan-phase.md (1714) over XL budget (1700) - PR #3178 added MVP-mode verb calls (phase.mvp-mode, task.is-behavior-adding, user-story.validate) to both workflow files, pushing them past 1700 lines - Fix: bump XL_BUDGET 1700 -> 1800 with inline comment pointing at the structural follow-up (extract MVP bodies to <workflow>/modes/mvp.md per the discuss-phase/modes/ precedent) - The structural extract is the right long-term fix but is bigger than canary unblock scope; will land in a follow-up after canary cycles Local verification: $ node --test tests/bug-2954-help-md-slash-command-stubs.test.cjs tests/workflow-size-budget.test.cjs tests 111 pass 111 fail 0 After this lands, re-trigger Release SDK Bundle on dev for v1.50.0-canary.2. * chore(changeset): set PR number for canary.2 unblock * fix(codex): generate-claude-md writes to AGENTS.md on Codex runtime When config.runtime === 'codex' or GSD_RUNTIME=codex, override the output target to AGENTS.md regardless of claude_md_path, so Codex projects no longer have GSD sections written to CLAUDE.md by mistake. Fixes both the CJS (gsd-tools) and SDK (profile-output.ts) paths. Explicit --output flags are still honoured in both paths. Closes #3163 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(plan-phase): remove agent: directive that caused OpenCode subagent dispatch On OpenCode, any command with `agent: <name>` in its frontmatter is auto-dispatched to a subagent context where the Agent tool is unavailable. plan-phase.md and mvp-phase.md both carried `agent: gsd-planner`, causing them to run inside gsd-planner's subagent context with no ability to spawn researcher/planner/checker subagents — the orchestrator fell back to inline execution for all three phases. Fix: remove `agent: gsd-planner` from both command files so they run in the main agent context. Also replace the stale `Task` tool in allowed-tools with `Agent` (the correct dispatcher tool name post-#3168 rename). Adds a structural regression test that parses YAML frontmatter of every commands/gsd/*.md file and asserts no command carries an `agent:` directive. Closes #3156 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(mvp): address CodeRabbit workflow and contract findings * fix(execute-phase): use registered state.update query command --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
9d5db87249 |
feat(#2975): adopt changeset-fragment workflow to eliminate CHANGELOG conflicts (#2978)
* feat(#2975): adopt changeset-fragment workflow to eliminate CHANGELOG conflicts Two PRs that both edit `### Fixed` in CHANGELOG.md always conflict on merge. Recently bit on #2960/#2972 in the same session — fix-the-conflict-and-rebase tax. Replace the shared-file model with per-PR fragment files that never share lines. Implementation built TDD per #2975, vertical slices with structured-IR assertions throughout: scripts/changeset/parse.cjs - fragment text → typed record + frozen FRAGMENT_ERROR enum (8 tests) scripts/changeset/render.cjs - fragments → structured IR with Keep-a-Changelog section ordering (2 tests) scripts/changeset/serialize.cjs - IR ↔ markdown round-trip pair (parse(serialize(ir)) === ir, 3 tests) scripts/changeset/cli.cjs - file-I/O wrapper with --json mode; reads .changeset/, folds into CHANGELOG.md, deletes consumed fragments. Idempotent. (1 test) scripts/changeset/lint.cjs - pure verdict (changedFiles, labels) → { ok, reason } via LINT_REASON enum. Honors `no-changelog` label. (5 tests) scripts/changeset/new.cjs - fragment scaffolder with random adjective-noun-noun filename. Tests assert via parseFragment round-trip. (3 tests) Total: 22 tests, all assertions on typed structured fields. No regex on text, no String#includes on file content. Lint clean across 356 test files. Supporting: .changeset/README.md - format spec + workflow docs .changeset/eager-hawks-rally.md - dogfood fragment for THIS PR (will be the first thing the new release tool consumes) .github/workflows/changeset-required.yml - CI: every PR runs lint.cjs package.json - npm run changeset, changelog:render, lint:changeset CONTRIBUTING.md - new "CHANGELOG Entries — Drop a Fragment" section between PR Guidelines and Testing Standards Closes #2975 * fix(#2975): address CodeRabbit findings on changeset workflow 7 valid findings (4 Major, 3 Minor); all addressed: scripts/changeset/parse.cjs - Preserve fragment body verbatim. Previously body.trim() ate intentional leading whitespace (code blocks, etc.); now trim() is used only for the emptiness check, and a single trailing newline is stripped (the editor-added one) so well-formed fragments round-trip byte-for-byte. Added a regression test asserting a code-block-leading body is preserved. scripts/changeset/cli.cjs - Validate flag values during argument parsing. parseArgs now returns { ok, opts | error }; rejects `--repo` etc. with no following value or with another flag as the value. main() surfaces the error message before exiting 2. - Handle post-write fragment-deletion failures. After CHANGELOG.md is written, any unlink failure is captured into a structured deleteFailures list with reason 'fail_fragment_delete'; cmdRender returns exitCode=1 with the partial-failure detail instead of leaving the changelog updated and fragments behind (which would cause double-consumption on rerun). scripts/changeset/lint.cjs - Treat CHANGELOG.md as a linted user-facing path. Direct edits to CHANGELOG.md (the bypass route around the new workflow) now fail the lint with FAIL_MISSING_FRAGMENT. Added a regression test for that case. - Use cp.execFileSync instead of cp.execSync for the git diff call. Eliminates the shell-interpolation surface on GITHUB_BASE_REF; git's own arg parser remains the validator. scripts/changeset/new.cjs - Atomic fragment creation. existsSync() + writeFileSync was racy under concurrent invocations. Now writeFileSync uses { flag: 'wx' } which fails EEXIST on collision; the random-name retry loop catches EEXIST and re-rolls. Throws explicitly after 16 attempts rather than silently overwriting. .changeset/README.md - Add language tag `md` to the format example fence (markdownlint MD040). All 25 changeset tests pass; lint clean (356 test files, 0 violations). * fix(#2975): sanitize --type and validate flag values in new.cjs (CR fixes) Two CR findings on scripts/changeset/new.cjs: 1. (Minor) `type` was embedded in frontmatter without sanitization. A newline in the value (e.g. `--type 'Fixed\ntype: Added'`) would corrupt the fragment. scaffoldFragment now validates `type` against the Keep-a-Changelog ALLOWED_TYPES set BEFORE writing — same set parse.cjs uses on consume. Throws with a typed error referencing the allowed values; tests cover the newline case + 4 other non-allowed values. 2. (Minor) `--repo` (and other value-taking flags) without a value silently set opts.repo to undefined, which produced a cryptic ERR_INVALID_ARG_TYPE deep inside path.join. parseArgs now mirrors the cli.cjs convention: returns { ok, opts | error }, validates that the next token exists and is not itself another flag, and surfaces a precise "missing value for --repo" message before exit. Added 3 tests: missing-trailing-value, flag-as-value, well-formed. 29 tests pass across the changeset suite (4 new regression tests). |
||
|
|
006cdafe8f |
ci(drift): enforce alias freshness checks in CI and contributor flow (#2910)
Merging alias-drift guardrails and local hook hardening. |
||
|
|
e81592878e |
feat(#2789): trim skill description anti-patterns; enforce 100-char budget (#2823)
* feat(#2789): trim skill description anti-patterns; enforce 100-char budget - Trim descriptions in all commands/gsd/*.md files over 100 chars - Remove flag documentation from descriptions (belongs in argument-hint) - Remove Triggers: keyword stuffing - Add scripts/lint-descriptions.cjs — fails on descriptions > 100 chars - Add npm script: lint:descriptions - Add tests/enh-2789-description-budget.test.cjs Closes #2789 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(#2789): add CHANGELOG entry for description budget lint * docs(#2789): update COMMANDS.md descriptions; add skill description standards note Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
9e58c45ea1 |
fix(#2791): GSD_WORKSTREAM env var respected by gsd-sdk query + gsd-tools bin alias (#2821)
* fix(#2791): GSD_WORKSTREAM env var respected by gsd-sdk query + gsd-tools bin alias Two fixes for gsd-sdk binary issues: **Issue 1 — Binary name collision:** Both `get-shit-done-cc` and `@gsd-build/sdk` declare `bin: { "gsd-sdk": ... }`. Added `"gsd-tools": "bin/gsd-sdk.js"` to `package.json` bin so users with the collision can invoke `gsd-tools query <cmd>` as a conflict-free alternative. **Issue 2 — Query registry not workstream-aware:** `gsd-sdk query` commands ignored `GSD_WORKSTREAM` env var, always reading from the root `.planning/` even when a workstream was active. `gsd-tools.cjs` reads `GSD_WORKSTREAM` via `planningDir()`, so all ~35 `gsd-sdk query` call sites in workflow files were broken in workstream-scoped projects. Fix: added env var fallback in `sdk/src/cli.ts` — when `--ws` is not provided, `GSD_WORKSTREAM` is used (with name validation; invalid values are silently ignored, matching CJS behaviour). Regression test: `tests/bug-2791-sdk-workstream-env.test.cjs` Closes #2791 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2791): address CodeRabbit — precedence test, invalid env fallback assertion, bash fence --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
f983c95ffc |
Release/1.39.0-rc.4 (#2797)
* chore: bump version to 1.39.0 for release * chore: bump to 1.39.0-rc.1 * chore: bump to 1.39.0-rc.2 * chore: bump to 1.39.0-rc.3 * chore: bump to 1.39.0-rc.4 * docs: add v1.39.0-rc.4 release notes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
f3685d9173 | 1.38.5 | ||
|
|
a5a2d44121 | 1.38.4 | ||
|
|
73abae60f0 | 1.38.3 | ||
|
|
aeef87de7f |
docs(test-standards): enforce no-source-grep rule with CI linter + CONTRIBUTING.md (#2700)
* docs(test-standards): enforce no-source-grep rule with CI linter + update CONTRIBUTING.md
Adds scripts/lint-no-source-grep.cjs — a static linter that detects readFileSync
on .cjs source files in tests without an allow-test-rule annotation. Wires it
into CI as a new lint-tests job in test.yml and as npm run lint:tests.
Resolves all 9 existing violations across the test suite:
- Rewrites workspace routing tests (3) as behavioral runGsdTools calls that
verify each command is router-recognized (exit != "Unknown init workflow")
- Adds allow-test-rule annotations with explanatory comments to 7 legitimate
structural tests: architectural invariants (locking, orphan-worktree),
structural regression guards (milestone-regex-global), docs-parity
(config-field-docs), integration-test-input (copilot-install), and
structural-implementation-guards (bug-1891, discuss-mode)
Updates CONTRIBUTING.md Testing Standards section with:
- "Prohibited: Source-Grep Tests" section with the before/after pattern,
root cause analysis of why it breaks (commit
|
||
|
|
0a049149e1 |
fix(sdk): decouple from build-from-source install, close #2441 #2453 (#2457)
* fix(sdk): decouple SDK from build-from-source install path, close #2441 and #2453 Ship sdk/dist prebuilt in the tarball and replace the npm-install-g sub-install with a parent-package bin shim (bin/gsd-sdk.js). npm chmods bin entries from a packed tarball correctly, eliminating the mode-644 failure (#2453) and the full class of NPM_CONFIG_PREFIX/ignore-scripts/ corepack/air-gapped failure modes that caused #2439 and #2441. Changes: - sdk/package.json: prepublishOnly runs `rm -rf dist && tsc && chmod +x dist/cli.js` (stale-build guard + execute-bit fix at publish time) - package.json: add "gsd-sdk": "bin/gsd-sdk.js" bin entry; add sdk/dist to files so the prebuilt CLI ships in the tarball - bin/gsd-sdk.js: new back-compat shim — resolves sdk/dist/cli.js relative to the package root and delegates via `node`, so all existing PATH call sites (slash commands, agents, hooks) continue to work unchanged (S1 shim) - bin/install.js: replace installSdkIfNeeded() build-from-source + global- install dance with a dist-verify + chmod-in-place guard; delete resolveGsdSdk(), detectShellRc(), emitSdkFatal() helpers now unused - .github/workflows/install-smoke.yml: add smoke-unpacked job that strips execute bit from sdk/dist/cli.js before install to reproduce the exact #2453 failure mode - tests/bug-2441-sdk-decouple.test.cjs: new regression tests asserting all invariants (no npm install -g from sdk/, shim exists, sdk/dist in files, prepublishOnly has rm -rf + chmod) - tests/bugs-1656-1657.test.cjs: update stale assertions that required build-from-source behavior (now asserts new prebuilt-dist invariants) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(release): bump to 1.38.2, wire release.yml to build SDK dist - Bump version 1.38.1 -> 1.38.2 for the #2441/#2453 fix shipped in 0f6903d. - Add `build:sdk` script (`cd sdk && npm ci && npm run build`). - `prepublishOnly` now runs hooks + SDK builds as a safety net. - release.yml (rc + finalize): build SDK dist before `npm publish` so the published tarball always ships fresh `sdk/dist/` (kept gitignored). - CHANGELOG: document 1.38.2 entry and `--sdk` flag semantics change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci: build SDK dist before tests and smoke jobs sdk/dist/ is gitignored (built fresh at publish time via release.yml), but both the test suite and install-smoke jobs run `bin/install.js` or `npm pack` against the checked-out tree where dist doesn't exist yet. - test.yml: `npm run build:sdk` before `npm run test:coverage`, so tests that spawn `bin/install.js` don't hit `installSdkIfNeeded()`'s fatal missing-dist check. - install-smoke.yml (both smoke and smoke-unpacked): build SDK before pack/chmod so the published tarball contains dist and the unpacked install has a file to strip exec-bit from. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sdk): lift SDK runtime deps to parent so tarball install can resolve them The SDK's runtime deps (ws, @anthropic-ai/claude-agent-sdk) live in sdk/package.json, but sdk/node_modules is NOT shipped in the parent tarball — only sdk/dist, sdk/src, sdk/prompts, and sdk/package.json are. When a user runs `npm install -g get-shit-done-cc`, npm installs the parent's node_modules but never runs `npm install` inside the nested sdk/ directory. Result: `node sdk/dist/cli.js` fails with ERR_MODULE_NOT_FOUND for 'ws'. The smoke tarball job caught this; the unpacked variant masked it because `npm install -g <dir>` copies the entire workspace including sdk/node_modules (left over from `npm run build:sdk`). Fix: declare the same deps in the parent package.json so they land in <pkg>/node_modules, which Node's resolution walks up to from <pkg>/sdk/dist/cli.js. Keep them declared in sdk/package.json too so the SDK remains a self-contained package for standalone dev. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(lockfile): regenerate package-lock.json cleanly The previous `npm install` run left the lockfile internally inconsistent (resolved esbuild@0.27.7 referenced but not fully written), causing `npm ci` to fail in CI with "Missing from lock file" errors. Clean regen via rm + npm install fixes all three failed jobs (test, smoke, smoke-unpacked), which were all hitting the same `npm ci` sync check. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(deps): remove unused esbuild + vitest from root devDependencies Both were declared but never imported anywhere in the root package (confirmed via grep of bin/, scripts/, tests/). They lived in sdk/ already, which is the only place they're actually used. The transitive tree they pulled in (vitest → vite → esbuild 0.28 → @esbuild/openharmony-arm64) was the root of the CI npm ci failures: the openharmony platform package's `optional: true` flag was not being applied correctly by npm 10 on Linux runners, causing EBADPLATFORM. After removal: 800+ transitive packages → 155. Lockfile regenerated cleanly. All 4170 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sdk): pretest:coverage builds sdk; tighten shim test assertions Add "pretest:coverage": "npm run build:sdk" so npm run test:coverage works in clean checkouts where sdk/dist/ hasn't been built yet. Tighten the two loose shim assertions in bug-2441-sdk-decouple.test.cjs: - forwards-to test now asserts path.resolve() is called with the 'sdk','dist','cli.js' path segments, not just substring presence - node-invocation test now asserts spawnSync(process.execPath, [...]) pattern, ruling out matches in comments or the shebang line Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address PR review — pretest:coverage + tighten shim tests Review feedback from trek-e on PR 2457: 1. pretest:coverage + pretest hooks now run `npm run build:sdk` so `npm run test[:coverage]` in a clean checkout produces the required sdk/dist/ artifacts before running the installer-dependent tests. CI already does this explicitly; local contributors benefit. 2. Shim tests in bug-2441-sdk-decouple.test.cjs tightened from loose substring matches (which would pass on comments/shebangs alone) to regex assertions on the actual path.resolve call, spawnSync with process.execPath, process.argv.slice(2), and process.exit pattern. These now provide real regression protection for #2453-class bugs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: correct CHANGELOG entry and add [1.38.2] reference link Two issues in the 1.38.2 CHANGELOG entry: - installSdkIfNeeded() was described as deleted but it still exists in bin/install.js (repurposed to verify sdk/dist/cli.js and fix execute bit). Corrected the description to say 'repurposes' rather than 'deletes'. - The reference-link block at the bottom of the file was missing a [1.38.2] compare URL and [Unreleased] still pointed to v1.37.1...HEAD. Added the [1.38.2] link and updated [Unreleased] to compare/v1.38.2...HEAD. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(sdk): double-cast WorkflowConfig to Record for strict tsc build TypeScript error on main (introduced in #2611) blocks `npm run build` in sdk/, which now runs as part of this PR's tarball build path. Apply the double-cast via `unknown` as the compiler suggests. Same fix as #2622; can be dropped if that lands first. * test: remove bug-2598 test obsoleted by SDK decoupling The bug-2598 test guards the Windows CVE-2024-27980 fix in the old build-from-source path (npm spawnSync with shell:true + formatSpawnFailure diagnostics). This PR removes that entire code path — installSdkIfNeeded no longer spawns npm, it just verifies the prebuilt sdk/dist/cli.js shipped in the tarball. The test asserts `installSdkIfNeeded.toString()` contains a formatSpawnFailure helper. After decoupling, no such helper exists (nothing to format — there's no spawn). Keeping the test would assert invariants of the rejected architecture. The original #2598 defect (silent failure of npm spawn on Windows) is structurally impossible in the shim path: bin/gsd-sdk.js invokes `node sdk/dist/cli.js` directly via child_process.spawn with an explicit argv array. No .cmd wrapper, no shell delegation. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Tom Boucher <trekkie@nomorestars.com> |
||
|
|
337e052aa9 | chore: bump version to 1.38.2 for hotfix | ||
|
|
29ea90bc83 | chore: bump version to 1.38.1 for hotfix | ||
|
|
0c6172bfad | chore: finalize v1.38.0 | ||
|
|
c69ecd975a | chore: bump to 1.38.0-rc.1 | ||
|
|
341bb941c6 | chore: bump version to 1.38.0 for release | ||
|
|
fc1fa9172b |
fix(install): build gsd-sdk from in-repo sdk/ source, not stale npm package
PR #2386 v1 installed the published @gsd-build/sdk from npm, which ships an older version that lacks query handlers needed by current workflows. Every GSD release would drift further from what the installer put on PATH. This commit rewires installSdkIfNeeded() to build from the in-repo sdk/ source tree instead: 1. cd sdk && npm install (build-time deps incl. tsc) 2. npm run build (tsc → sdk/dist/) 3. npm install -g . (global install; gsd-sdk on PATH) Each step is a hard gate — failures warn loudly and point users at the manual equivalent command. No more silent drift between installed SDK and the rest of the GSD system. Root package.json `files` now ships sdk/src, sdk/prompts, sdk/package.json, sdk/package-lock.json, and sdk/tsconfig.json so npm-registry installs also carry the source tree needed to build gsd-sdk locally. Also fixes a blocking tsc error in sdk/src/event-stream.ts:313 — the cast to `Array<{ type: string; [key: string]: unknown }>` needed a double-cast via `unknown` because BetaContentBlock's variants don't carry an index signature. Runtime-neutral type-widening; sdk vitest suite unchanged (1256 passing; the lone failure is a pre-existing integration test that requires external API access). Updates the #1657/#2385 regression test to assert the new build-from-source path (path.resolve(__dirname, '..', 'sdk') + `npm run build` + `npm install -g .`) plus a new assertion that root package.json files array ships sdk source. Refs #2385 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4cbe0b6d56 | 1.37.1 | ||
|
|
9e8257a3b1 | 1.37.0 | ||
|
|
201b8f1a05 | 1.36.0 | ||
|
|
1274e0e82c | chore: bump version to 1.35.0 for release | ||
|
|
caf337508f | 1.34.2 | ||
|
|
c7de05e48f |
fix(engines): lower Node.js minimum to 22
Node 22 is still in Active LTS until October 2026 and Maintenance LTS until April 2027. Raising the engines floor to >=24.0.0 unnecessarily locked out a fully-supported LTS version and produced EBADENGINE warnings on install. Restore Node 22 support, add Node 22 to the CI matrix, and update CONTRIBUTING.md to match. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
07b7d40f70 | 1.34.1 | ||
|
|
d14e336793 |
chore: bump to 1.34.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
0e06a44deb |
fix(package): include hooks/*.sh files in npm package (#1852 #1862) (#1864)
The "files" field in package.json listed "hooks/dist" instead of "hooks", which excluded gsd-session-state.sh, gsd-validate-commit.sh, and gsd-phase-boundary.sh from the npm tarball. Any fresh install from the registry produced broken shell hook registrations. Fix: replace "hooks/dist" with "hooks" so the full hooks/ directory is bundled, covering both the compiled .js files (in hooks/dist/) and the .sh source hooks at the top of hooks/. Adds regression test in tests/package-manifest.test.cjs. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
f7d4d60522 |
fix(ci): drop Node 22 from matrix, require Node 24 minimum (#1848)
Node 20 reached EOL April 30 2026. Node 22 is no longer the LTS baseline — Node 24 is the current Active LTS. Update CI matrix to run only Node 24, raise engines floor to >=24.0.0, and update CONTRIBUTING.md node compatibility table accordingly. Fixes #1847 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
949da16dbc |
chore(release): v1.33.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
a6457a7688 |
ci: drop Windows runner, add static hardcoded-path detection (#1676)
Replace the Windows CI runner with a static analysis test that catches the same class of platform-specific path bugs (C:\, /home/, /Users/, /tmp/) without requiring an actual Windows machine. - tests/hardcoded-paths.test.cjs: new static scanner that checks string literals in all source JS/CJS files for hardcoded platform paths; runs on Linux/macOS in <100ms and fires on every PR - .github/workflows/test.yml: remove windows-latest from matrix; switch macOS smoke-test runner from Node 22 → Node 24 (the declared standard) - package.json: bump engines.node from >=20.0.0 to >=22.0.0 (Node 20 reached EOL April 2026) Matrix goes from 4 runners → 3 runners per run: ubuntu/22 ubuntu/24 macos/24 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
27bc736661 |
chore: release v1.32.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
8de750e855 |
chore: bump version to 1.31.0 for npm release
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
0fde35acf9 | 1.30.0 | ||
|
|
596ce2d252 |
feat: GSD SDK — headless CLI with init + auto commands (#1407)
* test: Bootstrapped sdk/ as TypeScript ESM package with full GSD-1 PLAN.… - "sdk/package.json" - "sdk/tsconfig.json" - "sdk/vitest.config.ts" - "sdk/src/types.ts" - "sdk/src/plan-parser.ts" - "sdk/src/plan-parser.test.ts" GSD-Task: S01/T01 * test: Implemented config reader and gsd-tools bridge with 25 unit tests… - "sdk/src/config.ts" - "sdk/src/config.test.ts" - "sdk/src/gsd-tools.ts" - "sdk/src/gsd-tools.test.ts" GSD-Task: S01/T02 * test: Built prompt-builder, session-runner, and GSD class — 85 total un… - "sdk/src/prompt-builder.ts" - "sdk/src/prompt-builder.test.ts" - "sdk/src/session-runner.ts" - "sdk/src/index.ts" - "sdk/src/types.ts" GSD-Task: S01/T03 * test: Created E2E integration test with fixtures proving full SDK pipel… - "sdk/src/e2e.integration.test.ts" - "sdk/test-fixtures/sample-plan.md" - "sdk/test-fixtures/.planning/config.json" - "sdk/test-fixtures/.planning/STATE.md" - "vitest.config.ts" - "tsconfig.json" GSD-Task: S01/T04 * test: Added PhaseType/GSDEventType enums, 16-variant GSDEvent union, GS… - "sdk/src/types.ts" - "sdk/src/event-stream.ts" - "sdk/src/logger.ts" - "sdk/src/event-stream.test.ts" - "sdk/src/logger.test.ts" GSD-Task: S02/T01 * test: Built ContextEngine for phase-aware context file resolution, getT… - "sdk/src/context-engine.ts" - "sdk/src/tool-scoping.ts" - "sdk/src/phase-prompt.ts" - "sdk/src/context-engine.test.ts" - "sdk/src/tool-scoping.test.ts" - "sdk/src/phase-prompt.test.ts" GSD-Task: S02/T02 * test: Wired event stream into session runner, added onEvent()/addTransp… - "sdk/src/session-runner.ts" - "sdk/src/index.ts" - "sdk/src/e2e.integration.test.ts" GSD-Task: S02/T03 * feat: Added PhaseStepType enum, PhaseOpInfo interface, phase lifecycle… - "sdk/src/types.ts" - "sdk/src/gsd-tools.ts" - "sdk/src/session-runner.ts" - "sdk/src/index.ts" - "sdk/src/phase-runner-types.test.ts" GSD-Task: S03/T01 * test: Implemented PhaseRunner state machine with 39 unit tests covering… - "sdk/src/phase-runner.ts" - "sdk/src/phase-runner.test.ts" GSD-Task: S03/T02 * test: Wired PhaseRunner into GSD.runPhase() public API with full re-exp… - "sdk/src/index.ts" - "sdk/src/phase-runner.integration.test.ts" - "sdk/src/phase-runner.ts" GSD-Task: S03/T03 * test: Expanded runVerifyStep with full gap closure cycle (plan → execut… - "sdk/src/types.ts" - "sdk/src/phase-runner.ts" - "sdk/src/phase-runner.test.ts" GSD-Task: S04/T02 * fix: Added 3 integration tests proving phasePlanIndex returns correct t… - "sdk/src/phase-runner.integration.test.ts" - "sdk/src/index.ts" GSD-Task: S04/T03 * test: Add milestone-level types, typed roadmapAnalyze(), GSD.run() orch… - "sdk/src/types.ts" - "sdk/src/gsd-tools.ts" - "sdk/src/index.ts" - "sdk/src/milestone-runner.test.ts" GSD-Task: S05/T01 * test: Added CLITransport (structured stdout log lines) and WSTransport… - "sdk/src/cli-transport.ts" - "sdk/src/cli-transport.test.ts" - "sdk/src/ws-transport.ts" - "sdk/src/ws-transport.test.ts" - "sdk/src/index.ts" - "sdk/package.json" GSD-Task: S05/T02 * test: Added gsd-sdk CLI entry point with argument parsing, bin field, p… - "sdk/src/cli.ts" - "sdk/src/cli.test.ts" - "sdk/package.json" GSD-Task: S05/T03 * feat: Add InitNewProjectInfo type, initNewProject()/configSet() GSDTool… - "sdk/src/types.ts" - "sdk/src/gsd-tools.ts" - "sdk/src/cli.ts" - "sdk/src/cli.test.ts" - "sdk/src/gsd-tools.test.ts" GSD-Task: S01/T01 * chore: Created InitRunner orchestrator with setup → config → PROJECT.md… - "sdk/src/init-runner.ts" - "sdk/src/types.ts" - "sdk/src/index.ts" GSD-Task: S01/T02 * test: Wired InitRunner into CLI main() for full gsd-sdk init dispatch a… - "sdk/src/cli.ts" - "sdk/src/init-runner.test.ts" - "sdk/src/cli.test.ts" GSD-Task: S01/T03 * test: Add PlanCheck step, AI self-discuss, and retryOnce wrapper to Pha… - "sdk/src/types.ts" - "sdk/src/phase-runner.ts" - "sdk/src/session-runner.ts" - "sdk/src/phase-runner.test.ts" - "sdk/src/phase-runner-types.test.ts" GSD-Task: S02/T01 * feat: Rewrite CLITransport with ANSI colors, phase banners, spawn indic… - "sdk/src/cli-transport.ts" - "sdk/src/cli-transport.test.ts" GSD-Task: S02/T02 * test: Add `gsd-sdk auto` command with autoMode config override, USAGE t… - "sdk/src/cli.ts" - "sdk/src/cli.test.ts" - "sdk/src/index.ts" - "sdk/src/types.ts" GSD-Task: S02/T03 * fix: CLI shebang + gsd-tools non-JSON output handling Three bugs found during first real gsd-sdk run: 1. cli.ts shebang was commented out — shell executed JS as bash, triggering ImageMagick's import command instead of Node 2. configSet() called exec() which JSON.parse()d the output, but gsd-tools config-set returns 'key=value' text, not JSON. Added execRaw() method for commands that return plain text. 3. Same JSON parse bug affected commit() (returns git SHA), stateLoad(), verifySummary(), initExecutePhase(), stateBeginPhase(), and phaseComplete(). All switched to execRaw(). Tests updated to match real gsd-tools output format (plain text instead of mocked JSON). 376/376 tests pass. |
||
|
|
604a78b30b | 1.29.0 | ||
|
|
41ee44ae92 |
docs: update repository references from glittercowboy to gsd-build
The repository was transferred from the glittercowboy org to gsd-build, but several files still referenced the old org in URLs. This updates all repository URL references across READMEs (all languages), package.json, and the update workflow. Also removes a duplicate language selector in the main README header. Files intentionally unchanged: - CHANGELOG.md (historical entries) - CODEOWNERS, FUNDING.yml, SECURITY.md (reference @glittercowboy as a GitHub username/handle, not a repo URL) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
277c446215 | 1.28.0 | ||
|
|
47cb2b5c16 | 1.27.0 | ||
|
|
f656dcbd6f |
chore: update CI matrix to Node 20, 22, 24 — drop EOL Node 18
Node 18 reached EOL April 2025. Node 24 is the current LTS target. Changes: - CI matrix: [18, 20, 22] → [20, 22, 24] - package.json engines: >=16.7.0 → >=20.0.0 - Removed Node 18 conditional in CI (c8 coverage works on all 20+) - Simplified CI to single test:coverage step for all versions 797/797 tests pass on Node 24. |
||
|
|
641a4fc15a | 1.26.0 | ||
|
|
f35fe0dbb9 | 1.25.1 | ||
|
|
4ce0925851 | 1.25.0 |