fb92d1e596b26695ecce9a2841465efb9fcb2b75
196 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fb92d1e596 |
fix(#2983): classifier exit-code discipline, base-tag staging, drop vestigial merge-back (#2984)
* fix(#2983): classifier exit-code discipline, base-tag staging, drop vestigial merge-back Three issues surfaced by CodeRabbit's post-merge review of #2981 plus a production failure on the v1.39.1 release run. (1) Overloaded classifier exit code scripts/diff-touches-shipped-paths.cjs reused exit 1 for both the legitimate "no shipped paths" result and Node's default exit on uncaught throw, so any classifier failure (corrupt package.json, EPERM, etc.) was indistinguishable from a normal skip — the workflow's `if ! ... ; then skip` idiom would silently drop the commit. Distinct exit codes now: 0 shipped — at least one path is in the npm `files` whitelist 1 not shipped — CI / test / docs / planning only 2 classifier error — workflow MUST fail-fast uncaughtException + unhandledRejection + try/catch around fs/JSON parsing all route to exit 2 with stderr context. (2) Classifier missing at the base tag (CRITICAL) `Prepare hotfix branch` runs `git checkout -b "$BRANCH" "$BASE_TAG"` BEFORE the cherry-pick loop, replacing the working tree with the base tag's contents. Base tags predating #2980 (notably v1.39.0, the most likely next hotfix base) don't have scripts/diff-touches-shipped-paths.cjs at all — `node <missing>` exits non-zero — `if !` skips every commit — empty hotfix branch published. Strictly worse than the original #2980 push-rejection, which at least failed loudly. Stage the classifier from the dispatched ref's working tree into $RUNNER_TEMP at the top of the run script (before any working-tree- mutating git command). The cherry-pick loop now references $CLASSIFIER (staged) instead of the in-tree path. Sanity guards: refuse to start if scripts/diff-touches-shipped-paths.cjs is missing in the dispatched ref, refuse to proceed if cp didn't materialize $CLASSIFIER. The cherry-pick loop captures node's exit via ${PIPESTATUS[1]} and dispatches via explicit case: 0 proceed with cherry-pick 1 skip into NON_SHIPPED_SKIPPED * emit ::error:: + exit "$CLASSIFIER_RC" (3) Drop the merge-back PR step Auto-cherry-pick only picks commits already on main (`git cherry HEAD origin/main` outputs the unmerged ones; we filter fix:/chore: from main). By construction every code commit on the hotfix branch is already on main. The only hotfix-branch-only commit is `chore: bump version to X.Y.Z for hotfix`, which either no-ops against main or rewinds main's in-progress version. The merge-back PR was vestigial. It also failed in production on run 25232968975 with `GitHub Actions is not permitted to create or approve pull requests (createPullRequest)` — org policy blocks PR creation from the workflow's GH_TOKEN. Even without that block, the PR would have nothing useful to merge. Step removed. The `pull-requests: write` permission granted solely for the merge-back step has been dropped from the release job (least-privilege). Regression coverage tests/bug-2983-classifier-exit-codes-and-base-tag-staging.test.cjs adds 12 assertions across two describe blocks: - 5 classifier behavioral: exit 0/1 preserved, exit 2 on missing package.json, exit 2 on malformed JSON, exit-code constants exported. - 7 workflow contract: classifier staged before checkout, target is $RUNNER_TEMP, missing-source guard, missing-staged guard, PIPESTATUS-based dispatch, error branch fails workflow, loop uses staged path (not in-tree). tests/bug-2980-hotfix-only-picks-shipping-changes.test.cjs updated where it asserted the pre-#2983 `if ! ... ; then` shape: now accepts the post-#2983 case-dispatch form. The test still proves the classifier participates; bug-2983 enforces the specific shape. Run summary references for the curious reviewer: - Run 25232010071 — original #2980 trigger (workflow-file push rejection) - Run 25232968975 — failed merge-back step that prompted the "is this even useful?" question that drove the removal Closes #2983 * fix(#2983): address CodeRabbit findings on PR #2984 Two findings, both real, both fixed. (1) [Critical] PIPESTATUS capture clobbered by `|| true` Pre-fix shape: git diff-tree ... | node "$CLASSIFIER" || true CLASSIFIER_RC="${PIPESTATUS[1]}" When the classifier exits 1 ("not shipped" — common case) or 2 (error), `|| true` triggers the right-hand side. `true` is a one-command "pipeline" that overwrites PIPESTATUS to (0). ${PIPESTATUS[1]} on the next line is therefore unset (or stale under set -u). The case dispatch then matched the empty string — falling into `*)` and failing the workflow on every non-shipped commit, OR matching `0)` after some shells default-init unset to 0 and silently picking commits that don't ship. Local repro confirms the issue: $ bash -c 'set -euo pipefail; false | sh -c "exit 7" || true; \ echo "PIPESTATUS: ${PIPESTATUS[*]}"; \ echo "[1]: ${PIPESTATUS[1]:-<unset>}"' PIPESTATUS: 0 [1]: <unset> Fix: bracket the pipeline in `set +e`/`set -e`, snapshot PIPESTATUS into a local array on the very next line, then dispatch on the snapshot: set +e git diff-tree ... | node "$CLASSIFIER" PIPE_RC=("${PIPESTATUS[@]}") set -e DIFFTREE_RC="${PIPE_RC[0]}" CLASSIFIER_RC="${PIPE_RC[1]}" The snapshot must happen on the first line after the pipeline; any intervening simple command resets PIPESTATUS. The array form is invariant against that. Bonus from the new shape: $DIFFTREE_RC is now also captured. git diff-tree is unlikely to fail on a known-good $SHA, but if it does, we no longer feed partial/empty input to the classifier and call it "not shipped." A non-zero DIFFTREE_RC emits ::error::git diff-tree failed and exits. (2) [Minor] Stale "Merge-back PR opened against main" summary line The hotfix run summary still printed: echo "- Merge-back PR opened against main" But the merge-back step itself was removed in the previous commit on this branch. Operators reading the summary would expect a PR that doesn't exist. Replaced with explicit non-action text: echo "- No merge-back PR (auto-picked commits are already on main)" Test coverage bug-2983 test file gains 3 assertions: - PIPE_RC array-snapshot pattern is required (regex matches the exact `PIPE_RC=("${PIPESTATUS[@]}")` form). - The `pipeline || true; ${PIPESTATUS[1]}` antipattern is explicitly forbidden via assert.doesNotMatch. - DIFFTREE_RC is captured from PIPE_RC[0] and a non-zero value triggers ::error::git diff-tree failed. - Run summary forbids `Merge-back PR opened against main` and requires the new non-action sentence. bug-2964 test's loop-anchor window bumped 6 KB → 8 KB to accommodate the additional pre-pick scaffolding (the test's own comment had already anticipated this kind of growth, citing prior precedents from #2970 and #2980). Mark CodeRabbit comments resolved post-commit. Refs CR finding ids 3175253571, 3175253578 on PR #2984. |
||
|
|
7424271aa0 |
fix(#2980): hotfix cherry-pick only picks commits that change what ships (#2981)
* fix(#2980): pre-skip workflow-file cherry-picks in release-sdk hotfix loop The default GITHUB_TOKEN issued to the release-sdk run lacks the `workflow` scope, so the prepare job's `git push origin "$BRANCH"` is rejected by GitHub when any cherry-picked commit modifies a file under `.github/workflows/`: ! [remote rejected] hotfix/X.YY.Z -> hotfix/X.YY.Z (refusing to allow a GitHub App to create or update workflow ... without `workflows` permission) Pre-#2980 behavior: the auto_cherry_pick loop happily picked workflow-file commits, then the trailing push exploded with no clear signal which commit was the culprit. v1.39.1 hit this on PR #2977 (run 25232010071) — earlier release-sdk fixes (#2965, #2967, #2970) had been skipped on conflict so their workflow-file changes never reached the push step, masking the bug; #2977 was the first workflow-file commit to apply cleanly and the push immediately exploded. Fix: pre-pick guard in the cherry-pick loop. Inspect each candidate commit's file list via `git diff-tree --no-commit-id --name-only -r` BEFORE attempting the pick. If any path matches `^\.github/workflows/`, skip the commit, emit a `::warning::` annotation naming the dropped commit, and append to a new `WORKFLOW_SKIPPED` bucket. The run summary surfaces this bucket in its own section, distinct from `CONFLICT_SKIPPED` (real merge conflicts) and `POLICY_SKIPPED` (feat/refactor exclusions), so operators reviewing the run never confuse the remediation paths. The loud-warning piece is non-negotiable: silent drops were explicitly rejected as a failure mode during the option-1/2/3 tradeoff discussion. If a workflow-file fix genuinely needs to ship in a hotfix, the operator applies it manually on the hotfix branch using a token with `workflow` scope, or lands it on main and re-cuts the release. Regression covered by tests/bug-2980-skip-workflow-file-cherrypicks.test.cjs (5 assertions: pre-pick guard exists, uses `git diff-tree`, emits `::warning::`, lands in dedicated bucket, surfaces in summary). The bug-2964 test's 4 KB window after the cherry-pick-loop anchor was nudged to 6 KB to accommodate the new pre-pick scaffolding — the test's own comment had already anticipated this kind of growth (citing #2970's merge-commit pre-skip as prior precedent). Closes #2980 * refactor(#2980): replace workflow-file pre-skip with shipped-paths filter The previous commit on this branch caught only the .github/workflows/* subset of the bug, treating the symptom (push rejection on workflow-file changes) rather than the root cause (the fix:/chore: filter is too broad — it picks any commit with that conventional-commit type even when the diff cannot affect the published npm package). CI-only fixes (release-sdk.yml itself, hotfix tooling, test-only commits) shouldn't flow through hotfix runs at all — they cannot change what `npm install get-shit-done-cc@X.YY.Z` produces. The .github/workflows/* push rejection is just the loudest of these "shouldn't have been picked" cases; tests/, docs/, .planning/ commits get picked silently with the same lack of effect on consumers. Replace the workflow-file pre-skip with a shipped-paths filter: - New scripts/diff-touches-shipped-paths.cjs reads package.json `files`, plus package.json itself (always-shipped per `npm pack` semantics), and exits 0 iff any input path is in the shipped set. Lockfile is not shipped (npm pack excludes it unless explicitly in `files`). - Workflow loop now pipes `git diff-tree --no-commit-id --name-only -r` through the classifier; on exit 1 the commit is skipped and appended to a new NON_SHIPPED_SKIPPED bucket (replaces WORKFLOW_SKIPPED). - Run summary surfaces NON_SHIPPED_SKIPPED as informational — no ::warning:: annotation. A non-shipping commit cannot affect the package, so a yellow alert would imply remediation is possible and would mislead operators. The classifier in a separate .cjs file (rather than inline bash heredoc) is so its rules — directory-prefix vs exact-match, package.json-always-shipped, lockfile-not-shipped — are unit-testable in tests/bug-2980-hotfix-only-picks-shipping-changes.test.cjs (11 new assertions: 4 static workflow + 6 classifier behavioral + 1 mixed- diff edge case). Why this dissolves the original push-rejection bug: workflow files aren't in `files`, so workflow-only commits are skipped pre-pick. The push step never sees them. If a workflow-file fix genuinely needs to ship in a hotfix release (extremely rare — the hotfix workflow is read from main's ref, not the hotfix branch's), the operator applies it manually using a token with `workflow` scope. The pre-skip puts that requirement in the run summary explicitly. Closes #2980 |
||
|
|
7a416b10d4 |
fix(#2976): allow same-version bump in release-sdk hotfix release job (#2977)
The release job's "Bump in-tree version (not committed)" step ran `npm version "$VERSION" --no-git-tag-version` without --allow-same-version, so on real hotfix runs it failed with `npm error Version not changed` — because the prepare job had already committed the bump on the hotfix branch (the release job checks out BRANCH on real runs vs BASE_TAG on dry-runs, which is why dry-run never caught it). Pass --allow-same-version to both bumps, matching release.yml:326. Closes #2976 |
||
|
|
ef43f5161f |
fix(#2969): deterministic Step 5 verification gate for /gsd-reapply-patches (#2972)
* fix(#2969): deterministic Step 5 verification gate for /gsd-reapply-patches The prior Step 5 "Hunk Verification Gate" was prescribed correctly in the workflow text — but executed laxly by the LLM, which filled in `verified: yes` without actually checking content presence. The reporter observed three distinct files (skills/gsd-discuss-phase/SKILL.md, skills/gsd-autonomous/ SKILL.md, get-shit-done/workflows/new-project.md) where archives contained substantive user-added blocks that did not survive into the merged result, yet the gate reported clean. Move verification from LLM-driven prose into a deterministic Node script the workflow calls. The script can't be shortcut. Changes: - scripts/verify-reapply-patches.cjs (new): pure Node, no external deps. For each file in the patches dir, computes user-added significant lines as the line-set diff between backup and pristine baseline (when available; falls back to "every significant backup line" when no pristine — over-broad but the safe direction for this bug class). Asserts each line appears literally in the merged installed file via String.prototype.includes. Filters trivial lines (length < 12 chars, pure punctuation, decorative comments) so harmless drift doesn't trigger false failures. Exits 0 on pass, 1 on any miss with per-file diagnostic, 2 on usage error. Supports --json for workflow consumption. - get-shit-done/workflows/reapply-patches.md: rewrite Step 5 to call the script and parse its JSON output. The Step 4 Hunk Verification Table remains as advisory Claude-readable summary, but the gate is now the script's exit code. - tests/bug-2969-verify-reapply-patches.test.cjs (new): 6 tests covering (a) pass when every line survives, (b) fail when a line is missing, (c) fail when the merged file is deleted entirely, (d) --json structured report shape, (e) backup-meta.json is correctly skipped as metadata, (f) no-pristine-dir fallback exercises the safe over-broad path. All pass. Out of scope: the manifest-baseline tightening described in #2969 Failure 1 (saveLocalPatches comparing against the wrong baseline so prior silent wipes poison subsequent updates). That's a separate, bigger architectural change involving pristine-content infrastructure; this PR addresses the gate fidelity half so users at least see the diagnostic when content goes missing. Closes #2969 (partial — Failure 2 only) * fix(#2969): preserve #1999 Hunk Verification Table assertions alongside new script gate CI failure on PR #2972 surfaced that tests/reapply-patches.test.cjs (the #1999 contract) asserts Step 5 references: - "Hunk Verification Table" - `verified: no` failure condition - explicit STOP/halt/abort directive - "table absent / missing" halt path My initial Step 5 rewrite for #2969 substituted the deterministic script for the table-based gate entirely, stripping those references. The script is the strictly stronger gate, but the existing #1999 test enforces the table-based safety net as a defense-in-depth contract. Restore both gates as a layered Step 5: - 5a (binding): deterministic verifier script — script gate, exits non-zero on any miss, cannot be shortcut by the LLM - 5b (advisory): Hunk Verification Table review — preserved as redundant safety net for the case where the script has a bug or the pristine baseline is unavailable Both gates must pass. Verified: tests/reapply-patches.test.cjs (5 tests in the #1999 suite) and tests/bug-2969-verify-reapply-patches.test.cjs (6 tests in the #2969 suite) all pass — 21/21 total in this fixture. * fix(#2969): address CodeRabbit findings on workflow + script Five CR findings on PR #2972, all valid; addressed in this commit: 1. (Major) Stderr was merged into VERIFY_OUTPUT via `2>&1`, so any Node warning, deprecation notice, or stack trace would corrupt the JSON parse downstream. Capture stdout only; stderr remains on the controlling terminal for operator visibility. 2. (Major) verifyFile() crashed with EISDIR/EACCES instead of producing a structured diagnostic when the installed path was a directory or unreadable. Wrap statSync/readFileSync in try/catch and emit a per-file fail row; the whole-run gate continues with structured output. Added test case asserting the directory-at-installed-path case fails with `not a regular file` diagnostic instead of crashing. 3. (Minor) PRISTINE_FLAG built as a single string + unquoted expansion would split paths with spaces. Switched to a bash array (VERIFY_ARGS) that preserves whitespace through expansion. 4. (Minor) Fenced code block missing language tag (markdownlint MD040). Added `text` tag to the error message block. 5. (Minor) Usage comment said pristine fallback was "backup-meta lookup" but the actual code path falls back to significant-line checks from backup content. Corrected the comment to match implementation. Verified all 21 tests in tests/reapply-patches.test.cjs (#1999 contract) + tests/bug-2969-verify-reapply-patches.test.cjs (now 7 tests with the new directory case) pass. * test(#2969): structured JSON assertions, no substring matching on script output Replace every assert.match(r.stdout, /pattern/) call with structured assertions on the parsed JSON report from the script's own --json mode. The script's --json contract IS the structured shape we test against — the test author should never depend on the human-readable formatter output, just as no test should depend on substring presence in source. Changes: - All 7 tests now run the verifier with --json (via a runVerifier() helper) and parse the resulting JSON document into { status, report, stderr }. Diagnostic stderr is preserved as a separate channel for debug output but is not used for assertions. - Each previously substring-matched diagnostic ("Failures: 1", "not a regular file", "installed file missing after merge", file path, dropped line) is now a deepEqual / equal / Array.includes against typed report fields: report.failures, report.results[i].status, report.results[i].reason, report.results[i].file, report.results[i].missing[]. - Added an explicit "documented shape" test asserting the JSON output has exactly the keys { file, missing, reason, status } per result — locks the public contract of the --json mode. - DRY'd up fixture reset into a resetFixture() helper since every test starts with a fresh patches/installed/pristine triple. Linter: scripts/lint-no-source-grep.cjs reports 0 violations across 348 test files. Combined run of bug-2969-...test.cjs (7 tests) + reapply-patches.test.cjs (5 tests in the #1999 suite) all pass — 22/22 in the relevant fixture. * fix(#2969): typed REASON enum + raw-text-matching rule shipped repo-wide This commit closes the loop on the no-source-grep discipline: 1. scripts/verify-reapply-patches.cjs: - Frozen REASON enum exposes the diagnostic surface as stable codes: OK_NO_USER_LINES_VS_PRISTINE, OK_NO_SIGNIFICANT_BACKUP_LINES, FAIL_INSTALLED_MISSING, FAIL_INSTALLED_NOT_REGULAR_FILE, FAIL_READ_ERROR, FAIL_USER_LINES_MISSING. - Each result.reason is now a code from this enum, not free text. Tests assert via REASON.X equality, not regex on prose. - REASON exported from module.exports. 2. tests/bug-2969-verify-reapply-patches.test.cjs: - Full rewrite. Every assertion on typed structured fields: report.results[0].status === 'fail', report.results[0].reason === REASON.FAIL_INSTALLED_NOT_REGULAR_FILE, report.results[0].missing.includes(droppedLine) (Array set membership, not String substring). - Locks the REASON enum surface via Object.keys(REASON).sort() deepEqual. - Locks the JSON report shape via Object.keys(report).sort() deepEqual. - Zero regex, zero String#includes, zero startsWith/endsWith on text. 3. CONTRIBUTING.md: - New section "Prohibited: Raw Text Matching on Test Outputs" with concrete BAD/GOOD examples (substring on file content; assert.match on stdout; "structured parser" hiding string ops; regex on free-form reason fields). - The rule statement: "Tests assert on typed structured values. If the code under test produces text, the code under test must also expose a structured intermediate representation, and the test must assert on that IR — never on the rendered text." - Required structured-surface table: file IR, --json mode, frozen enum, fs facts. - "Hiding grep behind a function is still grep" callout — the parser-wrapper anti-pattern. - New `pre-existing-text-matching` exemption category for the 8 grandfathered files. Marked Transitional; new tests cannot use it. 4. scripts/lint-no-source-grep.cjs: - Three new patterns enforced (in addition to the existing .cjs-source readFileSync rule): - assert.match/doesNotMatch on .stdout/.stderr - .stdout/.stderr.<includes|startsWith|endsWith>( - readFileSync(...).<includes|startsWith|endsWith>( - Aggregated violations per file (multiple findings now report together). - Updated diagnostic message references both CONTRIBUTING.md sections. 5. 8 pre-existing tests annotated with `// allow-test-rule: pre-existing-text-matching` so the lint passes on this commit; each carries the prose "Tracked for migration to typed-IR assertions; do not copy this pattern." Files: bug-2649, bug-2687, bug-2796, bug-2838, bug-2943, graphify, hooks-opt-in, security-scan. Verification: lint 0 violations across 348 test files; full suite passes. * fix(#2969): rename exemption category to pending-migration-to-typed-ir + cite tracking issue Per maintainer feedback: 1. "Grandfathered" / "legacy" framing is wrong — both terms imply permanent or condoned exemption. The 8 files are tracked for correction, not exempted. 2. Each annotated file must cite the tracking issue so the migration work is auditable. Changes: - CONTRIBUTING.md: rename exemption category from `pre-existing-text-matching` to `pending-migration-to-typed-ir`. Update prose to "Tracked for correction, not exempted" and require each annotation to cite the open migration issue (e.g. `// allow-test-rule: pending-migration-to-typed-ir [#NNNN]`). - 8 test files: update annotation to cite #2974 (the tracking issue opened for migrating these files to typed-IR assertions). |
||
|
|
e9a66da1e7 |
fix(#2962): write npm-style gsd-sdk shim on Windows under --sdk install (#2971)
* fix(#2962): write npm-style gsd-sdk shim on Windows under --sdk install trySelfLinkGsdSdk previously contained `if (process.platform === 'win32') return null;` — a missed gap from #2775's POSIX self-link rather than an intentional design choice. As a result, `npx get-shit-done-cc@latest --claude --global --sdk` on Windows left `gsd-sdk` off PATH despite the installer reporting success, and the obvious recovery (`npm i -g @gsd-build/sdk`) lands the stale 0.1.0 publication that lacks the `query` subcommand the agents call ~40 times. This PR addresses the shim half. The npm-publish half (publishing @gsd-build/sdk at parity with the get-shit-done-cc version) requires maintainer credentials and is left for separate action. Changes: - bin/install.js: replace the unconditional Windows return-null with dispatch to a new trySelfLinkGsdSdkWindows() that: * resolves npm's global bin via `execFileSync('npm', ['prefix', '-g'])` (no shell interpolation; npm is the only PATH-resolved binary) * verifies write access with a probe before producing partial state * writes the standard npm shim triple to npm's global bin: - gsd-sdk.cmd (cmd.exe; CRLF line endings) - gsd-sdk.ps1 (PowerShell) - gsd-sdk (Bash wrapper for Cygwin/MSYS/Git-Bash) * each shim invokes `node "<absolute path to bin/gsd-sdk.js>"` with the passed args, decoupling shim location from SDK location — same logical structure as the POSIX wrapper-via-require() fallback above * unlinks any stale shims before writing so prior installs don't pin callers to a now-absent path * returns the .cmd path on success (handle the existing onPath check looks for) or null on any failure, falling through to the existing "gsd-sdk is not on your PATH" warning at line 8704 - tests/bug-2962-windows-sdk-shim.test.cjs (new): 5 tests exercising trySelfLinkGsdSdkWindows directly with cp.execFileSync mocked to redirect npm prefix to a temp dir. Asserts shim contents reference the absolute path, .cmd uses CRLF, stale shims are replaced not appended, and null is returned when `npm prefix -g` fails. - tests/no-unconditional-win32-skip.test.cjs (new): regression guard that fails CI if any future commit re-introduces `if (process.platform === 'win32') return null;` (or similar skip-only branches) in bin/install.js. Negative test verified by transiently re-introducing the bad pattern → guard fired → restored → passes. Out of scope: publishing @gsd-build/sdk@<current> to npm so the natural `npm i -g @gsd-build/sdk` recovery also lands a usable SDK. That requires maintainer credentials and is the second half of the issue. Closes #2962 * fix(#2962): address CodeRabbit findings — execSync for npm.cmd, behavior-based regression guard CR finding 1 (🟠 Major): Node's child_process docs explicitly call out that .cmd/.bat files cannot be spawned via execFile/execFileSync without a shell ("Spawning .bat and .cmd files on Windows" section). Since `npm` on Windows is `npm.cmd`, my use of execFileSync('npm', ['prefix', '-g'], { shell: false }) would have failed on the very platform this PR is meant to fix. Switched to cp.execSync('npm prefix -g', ...) — matching the existing convention at line ~8718 which makes the same lookup. Args are static literals so shell interpolation is not an injection vector. CR finding 2 (🟠 Major): the source-grep regression test in tests/no-unconditional-win32-skip.test.cjs violated the repo's no-source-grep testing standard (CONTRIBUTING.md). Replaced with a behavior-based test that: - overrides process.platform to 'win32' via Object.defineProperty - mocks cp.execSync to return a temp-dir as npm prefix - calls trySelfLinkGsdSdk(shimSrc) and asserts it returns non-null AND materializes gsd-sdk.cmd on disk The behavior guard is strictly stronger than the regex version: it would catch any equivalent skip pattern (e.g. os.platform() === 'win32', a typeof-based guard, etc.), not just literal `if (process.platform === 'win32')` text. Negative-tested by re-introducing the `return null` skip → test fails with maintainer-quoted diagnostic "trySelfLinkGsdSdk must not silently return null on Windows; a no-op skip is a missed-parity regression"; restored → passes. Test for Windows shim materialization (bug-2962-windows-sdk-shim.test.cjs) also updated to mock cp.execSync (matching the new production code path) instead of cp.execFileSync. Full suite: 6480/6480 pass. * test(#2962): make Windows shim tests self-contained per CR Each test now invokes trySelfLinkGsdSdkWindows() itself before reading the shim files, so they don't implicitly depend on the earlier test's side effects. Addresses CR's order-dependence finding. * test(#2962): structured shim parsing — eliminate substring source-grep CR found that even after the prior refactor, three tests in the suite still used .includes()/.startsWith() against shim file content (cmdContent.includes(\`@node ${jsonQuoted} %*\`) etc.). Substring matching on file text is the same anti-pattern the no-source-grep standard forbids — even when the file is one this test wrote — because it asserts a literal exists rather than that the structured shape is correct. Replace with three small parsers (parseCmdShim, parsePs1Invocation, parseBashInvocation) that split each shim into header + invocation tokens and assert via deepEqual on structured records. The assertions now check that the .cmd has @ECHO OFF / @SETLOCAL / @node <abs> %* in that order with exactly 3 meaningful lines, and that the .ps1 and bash wrappers split into the expected (call, nodeCmd, target, argToken) tuples. The stale-shim replacement test was hardened the same way: instead of proving the absence of a sentinel substring, it now proves the parsed target equals the new shimSrc and != the old path. Verified: scripts/lint-no-source-grep.cjs reports 0 violations across 348 test files. The 6-test windows-sdk-shim + win32-skip-guard suite all pass. * fix(#2962): expose pure shim IR + tests assert on typed fields, not rendered text Earlier "structured parser" approach (parseCmdShim / parsePs1Invocation / parseBashInvocation) was still raw-text manipulation behind a function wrapper — split('\\r\\n'), trim().split(/\\s+/), content.includes('\\r\\n'). Maintainer was right: hiding grep behind a parser is still grep. Real fix: refactor production code to expose the structured intermediate representation, and have tests assert on the IR fields directly. Production: - New buildWindowsShimTriple(shimSrc) — pure function, no fs/spawn. Returns { invocation: { interpreter, target }, eol: { cmd, ps1, sh }, fileNames: { cmd, ps1, sh }, render: { cmd: () => string, ... } }. The IR is the contract; rendered text is an implementation detail of the renderers. - trySelfLinkGsdSdkWindows now calls buildWindowsShimTriple, looks up filenames from triple.fileNames, and writes triple.render[kind]() to each target. Same observable behavior, structurally separated. - buildWindowsShimTriple added to test-mode exports. Tests (full rewrite — no shim file content is read at any point): - Layer 1: pure-IR tests assert on triple.invocation.target, triple.eol === { cmd: '\\r\\n', ps1: '\\n', sh: '\\n' }, triple.fileNames === { cmd: 'gsd-sdk.cmd', ... }, and the documented IR shape via Object.keys().sort() deepEqual. - Layer 2: fs/spawn driver tests assert filesystem FACTS: - return value equals expected path - all three target files exist as regular non-empty files - rendered file byte length === Buffer.byteLength of triple.render(kind) output (proves the writer writes what the renderer produces, no mutation, no truncation, no double-write — without comparing content) - mtime advances on rewrite (proves stale-replace behavior) - returns null when npm prefix -g throws No more split, .includes, .startsWith, .endsWith, or substring matching anywhere in the test suite. Lint clean. 10/10 tests pass. |
||
|
|
0d6abb87ac | fix(#2954): align help.md with post-#2824 skill consolidation (#2959) | ||
|
|
c5dfdbe42e |
fix(#2957): claude+global post-install instructs restart and skill fallback (#2960)
* fix(#2957): claude+global post-install instructs restart and skill fallback `npx get-shit-done-cc --claude --global` writes skills to `~/.claude/skills/gsd-*/SKILL.md` (CC 2.1.88+ format) and removes the legacy `~/.claude/commands/gsd/`. The post-install message still told users to type `/gsd-new-project` without mentioning the required Claude Code restart or the skill-name fallback. On configurations where CC does not auto-surface skills in the slash menu, users hit "no commands appear" and assumed the install failed. Split the post-install message: the existing single-line instruction stays for every non-Claude runtime and for `--claude --local`. For `--claude --global` it now reads: Restart Claude Code, then in any directory either type /gsd-new-project or ask Claude to run the gsd-new-project skill. This covers both invocation paths and surfaces the restart requirement. Add tests/bug-2957-claude-global-postinstall-message.test.cjs as a regression guard: captures the printed message for claude+global, claude+local, and opencode+global; asserts content for each. Verified the test fails on main (pre-fix) and passes after the fix. Closes #2957 * test(#2957): assert legacy generic instruction is replaced not extended CodeRabbit flagged that the test would still pass if the new restart/ fallback copy were printed *alongside* the old 'open a blank directory' instruction. Adding a doesNotMatch assertion proves the claude+global branch replaces the legacy line rather than appending to it. |
||
|
|
9d0d085a17 |
fix(query/agent-skills): emit raw <agent_skills> block instead of JSON-wrapped string (#2917)
* fix(query/agent-skills): emit raw <agent_skills> block instead of JSON-wrapped string The CLI dispatcher (`cli.ts`) JSON-stringifies all query handler results via `console.log(JSON.stringify(result.data, null, 2))`. For the `agent-skills` handler this produced a JSON-quoted string literal — e.g. `"<agent_skills>\n…</agent_skills>"` — which workflows embedded verbatim via `$(gsd-sdk query agent-skills gsd-planner)`, breaking all `<agent_skills>` injection into spawned subagent prompts. Fix: add an optional `format: 'json' | 'text'` field to `QueryResult`. When a handler returns `format: 'text'` and `--pick` is not active, the CLI writes the string directly via `process.stdout.write` instead of JSON-stringifying it. `agentSkills` sets `format: 'text'` for non-empty blocks. Regression guard: two new CLI integration tests in `skills.test.ts` spawn the CLI as a child process and assert that (a) a mapped agent type receives the raw XML block on stdout and (b) an unmapped agent type produces the existing JSON empty-string output. Fixes #2914. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(changelog): add #2917 entry under Unreleased Fixed --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
53cda93a01 |
Add automated cherry-pick + SDK-bundle parity to hotfix flow (#2956)
* feat(workflows): hotfix auto-cherry-pick + SDK-bundle parity (#2955) hotfix.yml: - create: auto-cherry-picks fix:/chore: commits from origin/main since BASE_TAG, oldest-first. Patch-equivalents skipped via git cherry. feat:/refactor: never auto-included. Conflicts halt with offending SHA. - finalize: install-smoke gate, sdk-bundle/gsd-sdk.tgz parity with release-sdk.yml, tightened next dist-tag re-point, --latest on gh release create. SDK package.json bumped in lockstep. release-sdk.yml: - New action input (publish | hotfix) and auto_cherry_pick boolean. - New prepare job branches hotfix/X.YY.Z from highest vX.YY.* tag, cherry-picks same logic as hotfix.yml, outputs effective ref. - install-smoke and release consume prepare.outputs.ref. - Hotfix mode forces tag=latest, opens merge-back PR. Idempotent if branch already exists. VERSIONING.md: documents the cumulative-tag invariant (vX.YY.Z anchors vX.YY.{Z+1}) and both workflow paths. Closes #2955 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(code-review): wire --fix dispatch and update stale command references (#2947) * fix(#2893): surface non-canonical plan filenames instead of silently returning zero plans Reporter saw `plan_count: 0` from `/gsd:execute-phase` even though five plan files existed on disk. Investigation showed the planner had written files like `01-PLAN-01-foundation.md`, while `phase-plan-index`'s strict filter (`f.endsWith('-PLAN.md') || f === 'PLAN.md'`) rejected them silently — collapsing two distinct states into the same `plans: []` return: - directory truly has no plans (legit empty) - directory has plans but the filter rejected them (user/agent error) The canonical contract is documented in three places: - `agents/gsd-planner.md` write_phase_prompt step (lines 1063-1080) - `commands/gsd/plan-phase.md` - `references/universal-anti-patterns.md` (rule 26) It mandates `{padded_phase}-{NN}-PLAN.md` and explicitly forbids `PLAN-NN.md` / `01-PLAN-01.md` / `plan-NN.md` etc. The strict filter is correct per that contract. The bug is that the executor never tells the user when the contract was violated — they just see `plan_count: 0` with no signal. Fix: add a diagnostic helper `describeNonCanonicalPlans()` that scans the phase directory for files matching `*PLAN*.md` (the diagnostic net) that the canonical filter rejected, excluding legit derivatives like `*-PLAN-OUTLINE.md` and `*-PLAN.pre-bounce.md`. When offenders exist, return a `warning` field naming each one and citing the canonical pattern so the user knows what to rename to. Wired into the three filter sites: - `phase-plan-index` (the executor's main entry point) - `phases list --type plans` - `find-phase` The strict filter itself is unchanged — existing canonical plans behave identically. This is purely a diagnostic that converts silent-empty into loud-with-actionable-error. Tests: - `phase-plan-index returns warning for reporter's exact filename pattern (`01-PLAN-01-foundation.md`)` - `truly empty dir does not emit a warning` - `canonical plans + outline + pre-bounce files do not emit a warning` Closes #2893 * test(#2893): add parity tests for find-phase and phases list --type plans warnings CodeRabbit's only finding on the prior commit: I wired the warning into three filter sites (`phase-plan-index`, `find-phase`, `phases list --type plans`) but only `phase-plan-index` had test coverage for the warning shape. The other two paths could silently diverge during future refactors — exactly the silent-drift class of bug this fix exists to prevent. Add four parity tests mirroring the existing two: - find-phase: non-canonical filenames produce a warning naming each offender + citing the canonical pattern. - find-phase: canonical plan + derivative files (PLAN-OUTLINE, pre-bounce) produce no warning. - phases list --type plans: same non-canonical case, but assert the warning is prefixed with `${dir}: ` (this path aggregates across phase directories so each offender is tagged with its dir). - phases list --type plans: canonical case, no warning. `node --test tests/phase.test.cjs`: 98/98 pass (was 94, +4 new). * docs(changelog): hotfix flow auto-cherry-pick + SDK bundle parity (#2955) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(workflows): address CodeRabbit findings on hotfix flow (#2955) 5 findings, all real: 1. BASE_TAG selection used lexicographic awk compare, breaking on multi-digit patches (v1.27.10 wrongly < v1.27.2). Fixed in both hotfix.yml and release-sdk.yml: append TARGET_TAG to candidate list, sort -V, take preceding entry. Semver-correct. 2,4. Cherry-pick conflict aborted locally with no remote branch to resolve from. Now the skeleton branch is pushed up-front (real runs); on conflict we abort, push the partial-pick state with --force-with-lease, and emit operator instructions in the run summary. 3. release-sdk.yml dry_run exited before cherry-pick, defeating the purpose. Now dry_run still applies cherry-picks locally (catches conflicts), just skips push. Downstream install-smoke runs against BASE_TAG; the cherry-pick verification itself is the dry-run signal. 5. release-sdk.yml release job missing pull-requests: write — gh pr create for the merge-back PR would have failed under restricted token defaults. Permission added. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(workflows): CR round 2 — dry-run signal + post-publish reconciliation (#2955) 3 findings, all real: 6. hotfix.yml create dry_run skipped every step (branch creation, cherry-pick, version bump) — a green dry-run gave no signal at all. Now the local checkout/cherry-pick/bump always runs; only the git push calls are gated on dry_run. Conflicts surface in dry-run too. 7,8. "Refuse if version already on npm" preflight hard-failed reruns, so a transient failure between npm publish and a later step (tag push, GH release, merge-back PR, dist-tag re-point) left the release half-shipped with no path to reconcile. Replaced with a prior_publish detect step that warns and sets skip_publish=true; the publish step is gated on that flag, but tag/release/PR/dist-tag continue. GitHub Release create is now idempotent (edit --latest if already exists). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(workflows): CR round 3 — preserve dry-run cherry-pick history in conflict guidance (#2955) Dry-run conflict path discarded successful picks with the runner, but the message told operators to rerun with auto_cherry_pick=false — which recreates the branch from BASE_TAG and silently loses every pick that had succeeded before the conflict. Updated both hotfix.yml and release-sdk.yml: dry-run conflict summary now lists the lost SHAs and recommends re-running with auto_cherry_pick=true (real, not dry-run) to materialize the partial branch on origin. Real-run guidance unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
ec07861228 |
fix(#2948): wire spike --wrap-up flag dispatch (#2951)
* fix(#2948): wire spike --wrap-up flag dispatch Add dispatch block to commands/gsd/spike.md so that /gsd-spike --wrap-up routes to the spike-wrap-up workflow instead of silently no-oping. Also add spike-wrap-up.md to execution_context so the runtime can load it, and update both companion references in workflows/spike.md from the deleted /gsd-spike-wrap-up entry-point to /gsd-spike --wrap-up. Fixes #2948 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(#2948): rewrite dispatch test using parseFrontmatter + section extraction Replace raw fs.readFileSync + text.includes() / regex assertions with structural parsing: parseFrontmatter extracts the YAML frontmatter fields and _body, extractSection pulls named XML blocks, and parseExecutionContextRefs resolves the @-prefixed workflow references. Assertions now target the argument-hint frontmatter field, the execution_context @-ref list, and the routing text within <context>/<process> sections — not arbitrary substrings in the raw file. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(#2948): tighten dispatch assertion to line-level rule check Replace the co-occurrence check (dispatchText.includes('--wrap-up') && dispatchText.includes('spike-wrap-up')) with line-level assertions that parse the <process> section's rules array, find the exact '- If it is `--wrap-up`:' line, verify it includes 'strip the flag' and 'spike-wrap-up', and assert the '- Otherwise:' fallback still routes to the spike workflow. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(#2948): anchor parseFrontmatter to line 0 to avoid mid-file --- delimiters parseFrontmatter was scanning the whole file for the first two '---' lines, which can match a mid-document horizontal rule as the opening delimiter. Now requires lines[0].trim() === '---'; returns { _body: content } for files with no frontmatter, and searches for the closing '---' from line 1 onward. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
3ba17e872e |
fix(#2950): update stale deleted-command references in workflow files (#2952)
* fix(#2950): update stale deleted-command references in workflow files Eight workflow files (help.md, do.md, settings.md, discuss-phase.md, new-project.md, plan-phase.md, spike.md, sketch.md) referenced command names removed in #2790. Updated all occurrences to canonical new forms: /gsd-phase (--insert / --remove), /gsd-capture, /gsd-config (--profile / --integrations / --advanced), /gsd-spike --wrap-up, /gsd-sketch --wrap-up, /gsd-code-review --fix. Adds regression test (124 assertions) in tests/bug-2950-stale-command-refs.test.cjs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(#2950): update pre-existing assertions to accept new consolidated command forms gsd-settings-advanced.test.cjs and settings-integrations.test.cjs were checking settings.md for the old micro-skill names (/gsd-settings-advanced, /gsd-settings-integrations). Now that #2950 updates settings.md to use the consolidated equivalents, broaden the assertions to accept both old and new forms. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(#2950): require canonical command forms and forbid legacy variants The broadened OR assertions added to unblock CI were too permissive — they could pass with legacy names still present. Now assert the canonical form is present (gsd-config --advanced / gsd-config --integrations) AND the legacy forms are absent (gsd-settings-advanced, gsd:settings-advanced, /gsd-settings-integrations). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
4d628b306a |
fix(#2949): wire sketch --wrap-up flag dispatch (#2953)
* fix(#2949): wire sketch --wrap-up flag dispatch Add dispatch logic to commands/gsd/sketch.md so --wrap-up routes to the sketch-wrap-up workflow instead of silently falling through to the normal sketch workflow. Also adds sketch-wrap-up.md to execution_context and updates companion references in workflows/sketch.md from the deleted /gsd-sketch-wrap-up command to /gsd-sketch --wrap-up. Fixes #2949 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2949): use exact-match "If it is" instead of "If it contains" for --wrap-up dispatch Aligns with the established pattern across all consolidated commands (workspace.md, update.md, progress.md) where the first-token check uses "If it is `--flag`" for exact equality, not substring matching. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
2cc8796265 |
fix(config-get): return schema default for context_window when absent (#2944)
* fix(config-get): return schema default for context_window when absent (#2943) cmdConfigGet in bin/lib/config.cjs now consults a SCHEMA_DEFAULTS map before emitting "Key not found", so context_window (and any future schema-defaulted keys) return their default value (exit 0) when not set in config.json. Also updates the stale subagent-timeout.test.cjs assertion that expected the old broken behavior (exit 1 / "Key not found") to match the corrected behavior. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: use distinct sentinel to prove --default wins over schema default (#2943) * docs: update CHANGELOG.md for #2943 fix --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
6dce1de4a7 |
fix: gap-analysis parses mixed requirement prefixes and skips table headers (#2902)
* fix: parse non-REQ IDs in gap-analysis and ignore table headers * fix: parse requirement IDs from first traceability column only --------- Co-authored-by: Tom Boucher <thomas.boucher@sas.com> |
||
|
|
abb2cb63f6 |
refactor: extract planning-workspace seam from core.cjs (#2901)
* refactor: extract planning workspace seam from core * docs: document planning-workspace module and inventory updates * fix: harden planning lock timeout and preserve workstream set contract --------- Co-authored-by: Tom Boucher <thomas.boucher@sas.com> |
||
|
|
ca88429bf8 |
docs(#2888): release notes for 1.40.0-rc.1 (#2889)
Add docs/RELEASE-v1.40.0-rc.1.md following the rc.7 format. Cover the 11 commits on main since v1.39.0-rc.7's release notes landed: - #2790 — skill surface consolidated 86 → 59 - #2792 — namespace meta-skills + keyword-tag descriptions + context guard - #2833 — phase-lifecycle status-line read-side - #2876 — yamlQuote SKILL.md description (Copilot/Antigravity/Trae/CodeBuddy) - #2768 — Gemini slash command namespace - #2858 — gsd slash namespace drift cleanup - #2851 — bare gsd-tools → absolute path - #2866 — Codex installer trailing-newline preservation - #2868 — canary publish moved from main to dev - #2872 — auto-close PRs without issue link Update CHANGELOG.md [Unreleased] with the same 1.40.0-rc.1 entries. Closes #2888 |
||
|
|
87917131f2 |
refactor(#2790): consolidate 86 gsd-* skills to 59 — fold flags, delete dead skills (#2824)
* feat(#2790): consolidate 86 gsd-* skills to 59 — zero functional loss Closes #2790 - `capture.md` — absorbs add-todo (default), note (--note), add-backlog (--backlog), plant-seed (--seed), check-todos (--list) - `phase.md` — absorbs add-phase (default), insert-phase (--insert), remove-phase (--remove), edit-phase (--edit) - `config.md` — absorbs settings-advanced (--advanced), settings-integrations (--integrations), set-profile (--profile); settings.md retained as-is - `workspace.md` — absorbs new-workspace (--new), list-workspaces (--list), remove-workspace (--remove) - `update.md` — adds --sync (absorbs sync-skills) and --reapply (absorbs reapply-patches) - `sketch.md` — adds --wrap-up (absorbs sketch-wrap-up) - `spike.md` — adds --wrap-up (absorbs spike-wrap-up) - `map-codebase.md` — adds --fast (absorbs scan) and --query (absorbs intel) - `code-review.md` — adds --fix (absorbs code-review-fix) - `progress.md` — adds --next (absorbs next) and --do (absorbs do) join-discord, research-phase, session-report, from-gsd2, analyze-dependencies, list-phase-assumptions, plan-milestone-gaps autonomous.md: updated Skill(skill="gsd:code-review-fix") → Skill(skill="gsd:code-review", args="--fix --auto") to match the consolidated skill name - New: tests/enh-2790-skill-consolidation.test.cjs (48 tests) - Updated: 14 existing test files redirected from deleted command paths to their consolidated equivalents - docs/INVENTORY.md: Commands count 86→59, ghost rows removed, new consolidated rows added - docs/INVENTORY-MANIFEST.json: regenerated to match filesystem Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(#2790): add CHANGELOG entry for skill consolidation * docs(#2790): update COMMANDS.md for 86→59 skill consolidation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2790): address CodeRabbit review findings - CHANGELOG.md: add --next alongside --do in progress flag list - config.md: remove trailing space from --profile code span (MD038) - COMMANDS.md: add required descriptions to /gsd-phase examples; /gsd-phase without args errors, not interactive - COMMANDS.md: add --next and --do to /gsd-progress flags table + examples - test: convert content.includes('--reapply') to structural frontmatter parse; add allow-test-rule comment for workflow content assertions - test: replace redundant existsSync duplicate with assertion that verifies the full consolidated flag surface (--sync | --reapply) in argument-hint Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2790): restore reapply-patches workflow and strengthen test assertions - Create get-shit-done/workflows/reapply-patches.md: the #2790 consolidation deleted the 14K combined command+workflow file (reapply-patches.md) but update.md already referenced the workflow via execution_context_extended. Restoring it fixes a silent behavioral gap where --reapply had no workflow to load. Includes full three-way merge logic, hunk verification table (Step 4), and the Hunk Verification Gate (Step 5) that blocks cleanup until all user-added hunks are confirmed present in the merged output. - Fix update.md: /gsd-reapply-patches → /gsd-update --reapply (stale ref) - Fix reapply-verify-hunks.test.cjs: was checking existsSync(update.md) 8×; now points to the workflow file and asserts real behavioral content (Post-merge verification, Hunk presence check, Line-count check, backup reference, per-file tracking, structural ordering) - Fix reapply-patches.test.cjs: replace content.includes() stubs with frontmatter-parsed argument-hint assertions; replace 4 existsSync(update.md) no-ops with real assertions against the workflow content - Fix edit-phase.test.cjs: /gsd-edit-phase → /gsd-phase (COMMANDS.md now documents the consolidated command with --edit flag) - Fix next-safety-gates.test.cjs: split OR predicates into independent assertions — --next in progress.md and --force in next.md workflow - Fix workspace.test.cjs: add allow-test-rule comment for routing content checks (command routing text IS the deployed behavioral contract) - Fix bug-2439 test: strengthen pre-flight assertion to verify gsd-sdk is referenced (not just --profile) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address CodeRabbit review findings (CR round 2) - INVENTORY.md: update sync-skills.md row to reference /gsd-update --sync instead of stale /gsd-sync-skills (absorbed in #2790) - enh-2380-sync-skills.test.cjs: align INVENTORY.md assertion with the corrected reference; was asserting the old /gsd-sync-skills name while the manifest test correctly asserted /gsd-update, creating conflicting expectations in the same suite - reapply-verify-hunks.test.cjs: add explicit notEqual(-1) assertions for all three anchors before the ordering check so a missing anchor produces a clear failure instead of a false positive (writeIdx=-1 < verifyIdx=5 is true) - bug-2439-set-profile-gsd-sdk-preflight.test.cjs: defer fs.readFileSync until after the existence assertion; eager describe-level read caused the suite to crash before the existence test could run, making it effectively dead code Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2790): address CR — INVENTORY routing + reapply test contract wording Two unresolved CodeRabbit findings (Major): - docs/INVENTORY.md: workflow-file table still pointed at obsolete /gsd-do, /gsd-next, /gsd-note, /gsd-add-todo, /gsd-add-backlog, /gsd-check-todos, /gsd-plant-seed slash commands. Re-route to the consolidated /gsd-progress (--next, --do) and /gsd-capture (--note, --backlog, --seed, --list) so the inventory is internally consistent. - tests/reapply-verify-hunks.test.cjs: 'verification tracks per-file status' asserted on phrasing that doesn't appear in reapply-patches.md (the 'per-file' substring only matched accidentally via 'sequential integer per file'). Switch to the actual contract text — Hunk Verification Table, one row per hunk per file, verified column. * test(#2790): update CR-INTEGRATION tests for consolidated --fix invocation After the merge of main (which carries #2843's hyphen-form fix), the consolidation in this branch absorbs gsd-code-review-fix into gsd-code-review as the --fix flag. Update the two CR-INTEGRATION tests that previously asserted on the standalone gsd-code-review-fix skill name to instead assert on a gsd-code-review invocation carrying --fix in its arg tokens. Tests still parse Skill() invocations structurally; only the asserted skill-name + arg-token shape changed. * test(#2790): scope success_criteria check to the <success_criteria> block CodeRabbit nitpick: 'success criteria includes verification' did a whole-file substring check, which can false-pass if the phrase appears elsewhere in the document. Extract the <success_criteria>...</success_criteria> block first via extractTagBlock() and assert against that scope only. * fix(#2790): post-rebase reconciliation with main - INVENTORY.md/JSON: add reapply-patches workflow row + bump count to 85 - autonomous.md: switch consolidated --fix invocation to hyphen Skill name - analyze-dependencies test: assert COMMANDS.md does NOT document the consolidated-away /gsd-analyze-dependencies entry (was: bare .includes()) * fix(#2790): address remaining CR findings — strengthen contract tests Doc-fixes: - INVENTORY.md: route transition.md & edit-phase.md rows to consolidated /gsd-progress --next and /gsd-phase --edit (was: deleted /gsd-next, /gsd-edit-phase) - config.md --profile branch: document #2439 pre-flight `command -v gsd-sdk` guard + install hint BEFORE the gsd-sdk invocation (closes opaque "command not found: gsd-sdk" regression path) Test discipline (no-source-grep contract): - bug-2439: replace bare `content.includes('gsd-sdk')` with structured parse of <context> block + --profile branch; assert pre-flight token, install hint, #2439 citation, and ordering vs gsd-sdk invocation - edit-phase: parse INVENTORY.md edit-phase.md row's "Invoked by" column and assert `/gsd-phase --edit` (not the deleted /gsd-edit-phase) - next-safety-gates: tighten `--next` documentation contract — require --next AND --force AND completeness routing (was OR-based, passed when only --next present) - reapply-patches: parse argument-hint flag list structurally; scan ALL <execution_context*> blocks for the @-include of reapply-patches.md; parse Hunk Verification Table header columns directly; locate Step 5 via heading parsing then assert (i) table reference, (ii) verified=no gate, (iii) STOP/halt directive, (iv) explicit absent-table halt path - workspace: parse frontmatter, tokenize argument-hint across multiple bracketed segments, parse @-include targets from <execution_context> rather than substring-matching the file body --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
4d394a249d |
fix(commands): normalize gsd slash namespace drift (#2858)
* fix(commands): normalize gsd slash namespace drift * fix(#2855): address CodeRabbit findings on namespace drift PR Three CR findings, all valid: 1. autonomous.md line 783 still had `gsd:discuss-phase` (the PR's own normalization missed this line). Switched to `gsd-discuss-phase` and updated the matching test in autonomous-interactive.test.cjs that was asserting the now-retired colon form. 2. tests/bug-2543-gsd-slash-namespace.test.cjs source-grepped the fix-slash-commands.cjs script with .includes() rather than driving its transform behaviour. Refactored fix-slash-commands.cjs to export a pure transformContent(src, cmdNames) function, kept the CLI behaviour unchanged via require.main, and replaced the source-grep block with five behavioural cases: rewrite, multi-occurrence, idempotence on canonical input, no-op on gsd-sdk/gsd-tools, and word-boundary safety. 3. tests/bug-2808-skill-hyphen-name.test.cjs matched `name:` anywhere in SKILL.md; a stray name: in the body could satisfy the assertion. Scoped the lookup to the YAML frontmatter block via the suggested diff (parse the leading --- ... --- region first, then find name: inside it). Full suite: 5854/5854 passing. * fix(#2855): address remaining CodeRabbit findings on PR #2858 Three structural concerns flagged on the namespace-drift fix PR: 1. scripts/fix-slash-commands.cjs:24 — `buildPattern([])` compiled `/gsd:()(?=[^a-zA-Z0-9_-]|$)/g`. The empty capture group still matches any `/gsd:` token followed by a non-word boundary (whitespace, EOL, punctuation), rewriting it to a stray `/gsd-`. Verified live: `transformContent("/gsd:", [])` → `"/gsd-"`. Added a guard returning null from `buildPattern` on empty input and updated `transformContent` and `processDir` to no-op when the pattern is null. 2. tests/autonomous-interactive.test.cjs:44-47 — assertion was `content.includes('gsd-discuss-phase') && content.includes('INTERACTIVE')`, which would false-pass on any unrelated co-occurrence (e.g. `INTERACTIVE=""` initialization plus a stray `gsd-discuss-phase` prose mention). Replaced with a structural extraction: locate the `**If \`INTERACTIVE\` is set:**` branch, bound it by the next `**If` / `<step>` boundary, and assert the `Skill(skill="gsd-discuss-phase", ...)` invocation lives inside that region. Tolerates whitespace around `(`, `skill`, and `=`. 3. tests/bug-2808-skill-hyphen-name.test.cjs:104 — colon-call regex was `Skill\(skill=...` and missed valid formatting like `Skill(skill = "gsd:cmd")` or `Skill( skill = ...)`. Loosened to `Skill\(\s*skill\s*=\s*...` so reformatting drift can't slip past the namespace guard. Verification: 5854/5854 pass on `npm test` from the rebased branch. * fix(#2855): drop pre-validation filter that hid namespace drift CR finding on tests/bug-2808-skill-hyphen-name.test.cjs:128: the test collected generated skill directories with `.filter(entry => entry.isDirectory() && entry.name.startsWith('gsd-'))`, then validated namespace invariants over that filtered list. Anything that violated the prefix invariant — `gsd:extract-learnings` (colon form), `extract_learnings` without prefix, `Gsd-foo` mis-cased — would silently disappear from the iteration and the test would falsely pass. Drop the `startsWith('gsd-')` filter so every generated directory shows up. Add explicit assertions before the existing per-skill loop: - directory list is non-empty (catches a broken converter that produces nothing) - every directory begins with `gsd-` - every directory contains no `:` - every directory contains no `_` Re-audited the full PR diff for the same anti-pattern: only this one site filtered before validating the namespace; bug-2643 and commands-doc-parity also use `readdirSync().filter()` but only by file extension, which is correct. 5854/5854 on `npm test`. * fix(#2855): address remaining CR findings (1 active + 2 nitpicks) Three findings on PR #2858, all the same root cause: input narrowing before validation lets drift slip past the guards. 1. tests/bug-2808-...:104 (active) — `colonCallRe` captured local names with `[a-z0-9-]+`, which excluded the underscore. A drift like `Skill(skill="gsd:extract_learnings")` (deprecated colon syntax with the old underscore filename) silently slid through. Broadened the capture to `[^'"\s)]+` so any malformed local name is surfaced; surrounding pattern (whitespace tolerance, escape support, flags) unchanged. 2. tests/bug-2643-...:43 (nitpick) — `extractSkillNamesHyphen` and `extractSkillNamesColon` had the same over-strict capture plus relied on a single regex over raw bytes, which the project test- rigor memory bans (`feedback_no_source_grep_tests.md`). Replaced with `extractSkillCalls(content)` — a small structural extractor that walks `Skill(` openers, locates each call's matching `)`, parses the body's `skill = "..."` keyword argument with permissive whitespace + quoting + escape handling, and returns `{ name, raw }` records. The two namespace-form helpers become thin filters over the structured output. Tightened the body class to `[^'"\\]+` so a trailing escape `\` before the closing quote (as in `Skill(skill=\"gsd-foo\", …)` written inside another string context) doesn't get included in the captured name. 3. tests/bug-2543-...:44 (nitpick) — `DOC_SEARCH_FILES` was a hand- curated 7-entry array. Every doc added in the future would silently weaken drift detection until someone remembered to extend the list. Replaced with `discoverDocSearchFiles(ROOT)`: globs every `.md` under `docs/` and adds `README.md` if present. New docs are picked up automatically. Re-audited the diff surface for similar narrowings; no other sites filter or constrain before validating namespace invariants. 5854/5854 on `npm test`. * fix(#2855): recurse docs/ tree so localized translations are scanned too CR finding: discoverDocSearchFiles() stopped at docs/*.md, leaving localized translation trees (docs/ja-JP/, docs/zh-CN/, docs/ko-KR/, docs/pt-BR/) and other nested doc collections (docs/skills/, docs/superpowers/) invisible to the namespace-drift invariant. Verified the gap: docs/ has 6 nested directories with ~30 .md files that the previous top-level-only scan was skipping. None contain /gsd: references today, but a future translation update or new doc subdir could leak drift. Switch to an iterative stack walk so every .md under docs/ is scanned regardless of depth. Stack form (rather than recursion) avoids the risk of running into the call-stack limit on deep doc trees. 5854/5854 on `npm test`. --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com> |
||
|
|
f2ada8500c |
chore(#2868): switch canary publish from main to dev branch (#2871)
* chore(#2868): switch canary publish from main to dev branch Swaps the four `if:` guards in `.github/workflows/canary.yml` from `refs/heads/main` to `refs/heads/dev` so the canary stream is owned by the new long-lived integration branch. Adds a policy comment at the top of the workflow documenting the branch->dist-tag mapping (dev=@canary, main=@next/@latest, no overlap). Closes #2868 * fix(#2868): summary block matches publish-step gate CodeRabbit caught: the Summary step keyed off DRY_RUN only, so a non-dry-run on main would falsely report "Published"/"Tagged" even though all four publish steps were skipped by the new dev-only gate. Add PUBLISH_ELIGIBLE env mirroring the publish-step `if:` expression and a VALIDATION ONLY branch in the summary so non-dev runs report honestly. |
||
|
|
2498f5649d |
docs(release): backfill CHANGELOG with 17 RC-train entries before v1.39.0 final cut (#2854)
Adds [Unreleased] entries for PRs that landed between v1.39.0-rc.4 and v1.39.0-rc.6 but were missing from CHANGELOG.md. One bullet per PR, grouped Added (#2828) and Fixed (16 entries: #2788, #2791, #2794, #2796, #2798, #2801, #2803, #2805, #2808, #2829, #2831, #2832, #2835, #2836, #2838, #2839). Closes #2853 |
||
|
|
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> |
||
|
|
eeaf9c556f |
fix(#2787): track fenced code blocks in extractCurrentMilestone (#2812)
* fix(#2787): track fenced code blocks in extractCurrentMilestone The milestone-end search used a multiline regex against the raw restContent string. Lines inside fenced code blocks (``` or ~~~) that matched the milestone-heading pattern (e.g. `# note v1.0`) prematurely set sectionEnd, hiding all phases after the block from roadmap analyze, roadmap get-phase, and every downstream command. Replace the regex match with a line-by-line scan that tracks fence state. Lines inside an open fence are skipped regardless of content. Adds three regression tests covering backtick fences, tilde fences, and the roadmap get-phase code path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2787): track fence delimiter instead of toggling bare boolean Replace the inFence boolean with fenceChar/fenceLen tracking so that indented fences (up to 3 leading spaces) and mixed-delimiter content (~~~ inside a backtick fence) are parsed correctly. A closing fence is only recognised when it uses the same character as the opening delimiter and has at least the same run length, matching the CommonMark spec. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2787): require fence-only closing line — reject info-string lines as closers A closing fence delimiter must contain only optional trailing whitespace. A line like \`\`\`js inside an open fence has an info string and must not close it. The previous regex /^\s{0,3}([`~]{3,})/ matched the opening of any such line, so the closing check could toggle fenceChar off on an info-string line and expose subsequent heading-like content to the milestone-end detector. Fix: capture the trailing portion of every fence-candidate line and only clear fenceChar when trailing matches /^\s*$/ (per CommonMark §4.5). Adds a regression test covering the ```text / ```js nesting scenario. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
d46efb4790 |
fix(#2784): clear shared ~/.cache/gsd/ update-check cache in update workflow (#2813)
* fix(#2784): clear shared ~/.cache/gsd/ cache in update workflow The SessionStart hook (hooks/gsd-check-update.js) writes update-check results to $HOME/.cache/gsd/gsd-update-check.json (shared, tool-agnostic). The update.md run_update step only cleared per-runtime paths like ~/.claude/cache/gsd-update-check.json, so the statusline kept showing the stale upgrade indicator after a successful update. Fix: add rm -f "$HOME/.cache/gsd/gsd-update-check.json" to the cache-clear block in the run_update step. Regression test: tests/bug-2784-update-cache-clear-path.test.cjs * fix(#2784): address CodeRabbit review — four edge-cases count, bash fence, structured test assertions |
||
|
|
06de427b09 |
docs: add CHANGELOG entry and rc.5 release notes for #2809 Codex hooks migrator fixes (#2811)
Covers the five correctness findings addressed in the round-5 CR of PR #2809: parseHooksBody key parser (hyphenated/quoted keys), buildNestedBlock empty-handler guard, legacyMapSections segment-count filter, quoted-dot regression test, and strengthened command path assertion. Closes #2810 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
c0730fffde |
docs(changelog): expand [Unreleased] with all 1.39.0-rc.4 changes since v1.38.5 (#2799)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
b44482cf03 |
fix(#2760): defensive Codex install — strip legacy agents blocks, default hooks to AoT, validate post-write schema (#2785)
* fix(#2760): defensive Codex install — strip legacy agents blocks, default hooks to AoT, validate post-write schema Three defects, three defensive fixes shipped together. Issue reporter never returned with the requested diagnostic backup, but four additional users have since confirmed the same Codex breakage and ZakAnun confirmed manual cleanup is the only working workaround — defensive triple ships without the original backup grep, justified by the corroborating reports. Fix 1 (defect 3 — confirmed real). The Codex hooks emit path always appended a top-level `[[hooks]]` AoT block, which collides with users who already use the namespaced AoT form `[[hooks.SessionStart]]`. New helper `hasUserNamespacedAotHooks()` detects the user's preferred shape on parse and the install emits the GSD-managed hook in that same shape when present. Default for fresh configs stays at top-level `[[hooks]]` so status-quo behavior is preserved. Fix 2 (defects 1+2 — defensive). `stripLeakedGsdCodexSections()` (the install-time stripper) now always purges bare `[agents]` single-bracket tables and `[[agents]]` sequence tables regardless of GSD marker presence — both forms are invalid in current Codex schema and produce "invalid type: ..., expected struct AgentsToml". Previously gated on GSD-name lookup which missed marker-stripped configs and third-party authored entries. The uninstall-time stripper (`stripCodexGsdAgentSections`) keeps its old conservative behavior so user-authored entries survive uninstall. Fix 3 (defensive). Post-write schema validation parses the bytes about to be committed and asserts no bare `[agents]`, no `[[agents]]`, and no bare `[hooks.<Event>]` tables remain. On failure the install restores the pre-install backup of config.toml and aborts loudly so the user is never left with a Codex CLI that refuses to load. Pre-install snapshot is captured before installCodexConfig runs (not after) so restore returns the file to its true pre-GSD state. Tests added (10 new, 1 updated): - bug-2760-codex-install-defensive.test.cjs (10 new tests across 4 describes: hooks AoT preservation, strip robustness for both [agents] and [[agents]] without marker, schema validator behavior, abort+restore via test seam) - codex-config.test.cjs "case 2 ..." updated to reflect new defensive bare-[agents] purge Full suite: 5747 pass / 0 fail. Closes #2760 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(#2760): normalize Codex hooks emit field name across migration and managed paths The migrateCodexHooksMapFormat path emitted `type = "<TYPE>"` for legacy [hooks.TYPE] sections, while the GSD-managed Codex install emitted `event = "SessionStart"` — same target [[hooks]] schema, two different field names. Codex currently tolerates both via permissive parsing, but the moment one path tightens this becomes a silent #2760-class regression. Normalize both call sites on `event` (the existing GSD-managed convention). Update migration emit, docstring, and existing migration assertions to match. Add a parity regression test that drives both code paths and asserts the [[hooks]] field key is identical. * test(#2153): fix test isolation by building hooks/dist on demand The "Codex install copies hook file (#2153)" regression depends on hooks/dist/ being populated, but that directory is gitignored and only built by `npm run build:hooks`. The npm pretest chain runs `build:sdk` but not `build:hooks`, so when this file is run in isolation (`node --test tests/codex-config.test.cjs`) the hook copy step skips silently and the regression test fails on a stale-environment artifact rather than a real bug. Add a top-level before() hook that runs scripts/build-hooks.js when hooks/dist/ is missing or empty. Matches the pattern already used by bug-1834-sh-hooks-installed and other install integration tests, so the suite passes regardless of runner ordering or which tests are targeted. * fix(#2760): structural TOML validation, atomic writes, and behavioral test rewrites Addresses CodeRabbit review on PR #2785 plus source-grep violations the maintainer flagged in the regression test. Fix 1 (CR 3149606220) — validateCodexConfigSchema now parses the TOML into a structured object first via the new parseTomlToObject helper, then runs schema-shape checks against both the parsed structure and the table section headers. Malformed TOML with valid-looking headers no longer slips past validation. Fix 2 (CR 3149606224) — Replaced the four source-grep assertions in tests/bug-2760-codex-install-defensive.test.cjs (lines 109, 125, 169, 201) with structural assertions against the parsed TOML object via the exported parseTomlToObject helper. Tests now verify behavior (the file parses and contains the expected structure) instead of literal byte patterns. Robust to formatting changes — exactly what the regex-loosening suggestion was reaching for, done correctly. Confirmed clean by `npm run lint:tests` (0 violations). Fix 3 (CR 3149606234) — The describe block that mutates installModule.__codexSchemaValidator now runs with concurrency: false so the test seam mutation cannot leak into sibling suites that also call runCodexInstall. Fix 4 (CR outside-diff) — Approach (b): atomic temp-file + renameSync. Added atomicWriteFileSync helper used by mergeCodexConfig and the final hooks-write. A mid-write failure leaves the .tmp-<pid>-<n> sibling behind (cleaned up immediately) and never truncates the original config.toml. Paired with try/catch wrapping around the entire post-snapshot mutation sequence so any unexpected throw also triggers restoreCodexSnapshot. Two layers of defense: atomic write prevents the corruption window, snapshot restore handles non-atomic write paths. Added behavioral test for fix 4: stubs fs.renameSync to throw on the configPath rename, asserts the on-disk bytes match the pre-install snapshot byte-for-byte, asserts the parsed structure is still the user's [model] section (no half-written GSD agents block), and asserts no stray .tmp-* files remain. Marked concurrency: false because it monkey-patches a global. Test results: 5749/5749 pass, 0 fail. lint:tests clean. * test(#2760): TOML-parse based assertions for bare-agents purge and hook-field parity (CodeRabbit follow-up) * fix(#2760): treat write failures as fatal, strip legacy hooks before guard, tighten TOML parser (CR4) CR4 finding 1 (MAJOR) — Write failures silently succeeded. The inner catch around atomicWriteFileSync restored the snapshot then re-threw, but the outer catch only matched 'post-write Codex schema validation failed' and downgraded everything else to a warn-and-continue. Install finished with "Done!" while Codex had no GSD agents configured. Fix: wrap writeErr with a `post-write Codex install failed:` prefix and broaden the outer guard to `.startsWith( 'post-write')` so both schema-validation and write failures abort install. CR4 finding 2 (MAJOR) — Legacy flat [[hooks]] block prevented namespaced AoT upgrade. The `!configContent.includes('gsd-check-update')` guard short- circuited the new namespaced emit when an existing install had the legacy flat [[hooks]] block, leaving users stuck in the mixed layout this fix is designed to eliminate. Fix: strip ALL existing managed gsd-check-update hook blocks (top-level [[hooks]] AND namespaced [[hooks.SessionStart]]) BEFORE evaluating the includes guard, so every install converges on the right shape regardless of prior state. CR4 finding 3 (MAJOR) — Homegrown TOML parser silently accepted malformed input. parseTomlValue happily consumed the `0` prefix of `timeout = 0.5` and parseTomlToObject did not verify the full RHS was consumed, so `key = "x" junk` and date/time literals slipped through. Per CONTRIBUTING ("No external dependencies in core"), option (b) was chosen over adding @iarna/toml: (a) parseTomlValue rejects any integer immediately followed by `.`, `e`, `E`, `:`, `-`, `T`, or `Z` (floats / dates / times); (b) parseTomlToObject scans from parsed.end to the next newline and throws `trailing bytes after value` if anything other than whitespace + optional `# comment` is present. * test(#2760): add CR4 regression tests + scope GSD_TEST_MODE + rename rename-fault test CR4 finding 5 (NIT) — GSD_TEST_MODE leak. Saved previous value, set '1' for the require, then restored (delete if undefined). No more test-only env var leaking to siblings in the same node process. CR4 finding 4 (NIT) — Renamed the existing fix-4 test from 'fs.writeFileSync' to 'fs.renameSync' (the only call actually faulted) and added a sibling test that stubs fs.writeFileSync to throw on the .tmp- target — exercising the pre-rename branch of atomicWriteFileSync that was previously untested. Both serialize via concurrency: false on the existing describe block. CR4 finding 1 (MAJOR test) — New behavioral test asserts install throws with a `post-write Codex install failed` message AND never prints "Done!" when the hook-block atomic rename fails. Captures stdout via console.log stub, asserts byte equality of restored snapshot. Faults only the rename whose temp source contains gsd-check-update so earlier mergeCodexConfig writes are not collateral damage. CR4 finding 2 (MAJOR test) — New TOML-parsed behavioral test for the legacy-hook upgrade path: pre-install has [[hooks.SessionStart]] (user) + legacy flat [[hooks]] managed gsd-check-update entry; post-install must have hooks.SessionStart as Array-of-tables with both user hook and GSD entry, and no top-level [[hooks]] AoT remaining. Also asserts exactly one gsd-check-update entry (no duplicates). CR4 finding 3 (MAJOR test) — parseTomlToObject regression suite: rejects floats (timeout = 0.5), dates (created = 1979-05-27), trailing garbage (key = "x" junk), and accepts trailing whitespace + # comment. * fix(#2760): CR5 — pre-write fatal, TOML duplicate-key/header rejection, namespaced AoT migration Address all five CodeRabbit round-5 findings on PR #2785: Finding 1 (MAJOR) — Pre-write failures in the Codex hook configuration catch (around bin/install.js:7002) used to fall through to console.warn even though restoreCodexSnapshot() had already run. This produced "Done!" output with no Codex hooks configured. Now wraps the original error with a "(pre-write)" prefix and rethrows so install aborts loudly. Same defect class as CR4 finding 1, different layer. Finding 2 (MAJOR) — parseTomlToObject silently reused existing tables and overwrote duplicate keys. Real TOML 1.0 rejects: - duplicate scalar key in same table ([a]\nx=1\nx=2) - re-declared [a] header (two [a] sections) - [[arr]] then [arr] for same path (shape mismatch) Tracks pathShape, declaredHeaders, and per-table-instance key sets; throws "duplicate or shape-mismatched table header at <path>" or "duplicate key <name> in <path>". Finding 3 (MAJOR) — migrateCodexHooksMapFormat used to emit flat [[hooks]]\nevent="<TYPE>", which produced mixed flat+namespaced layouts when the user already had [[hooks.<OTHER>]] entries. Now emits [[hooks.<TYPE>]] directly (the namespace IS the event); managed-emit detector hasUserNamespacedAotHooks fires correctly so the install converges on a single namespaced layout regardless of pre-existing state. Finding 4 (NIT) — tests/bug-2760-codex-install-defensive.test.cjs rename-failure test tightened from "throw OR warn acceptable" to assert.equal(threw, true), locking the contract Finding 1 establishes. Finding 5 (NIT) — bug-2760 test suite snapshots and restores fs.renameSync defensively in beforeEach/afterEach (symmetric with fs.writeFileSync), removing the fragile per-test try/finally. Second test in the same suite cleaned up to drop its try/finally. Updates tests/codex-config.test.cjs to assert the new namespaced AoT migration shape via parseTomlToObject (no source-grep). Existing field- parity test reframed as shape-parity since both paths now emit namespaced. Tests: 5764 pass (+8 new). lint:tests: 0 violations. * docs(#2760): add CHANGELOG entry for Codex install defensive triple Adds the [Unreleased] Fixed entry for the Codex install fix landed in this PR — defensive strip of legacy [agents]/[[agents]] blocks, namespaced AoT hook detection across all events, atomic write + rollback, strict TOML validation rejecting duplicate keys/repeated headers/trailing bytes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
9472f343db |
feat(#2762): --minimal install profile (≥94% cold-start token reduction) (#2764)
* feat(#2762): add --minimal install profile to cut cold-start token cost Eager system-prompt load from 86 gsd-* skill descriptions plus 33 subagent descriptions costs ~12k tokens per turn even in directories with no .planning/. Frontier models (Sonnet 4.6 / Opus 4.7) with 200K-1M context don't feel it; local LLMs with 32K-128K do. --minimal (alias --core-only) installs only the main GSD loop: new-project, discuss-phase, plan-phase, execute-phase, plus help/update. Zero gsd-* subagents are written. Re-running gsd update without --minimal expands to the full surface. Default install behavior is unchanged. DRY: a single stageSkillsForMode() helper filters the source dir; all 13 runtime-specific copy fns are unchanged because they recurse the staged dir. Allowlist + helpers live in get-shit-done/bin/lib/install- profiles.cjs as the single source of truth. Manifest now records mode: 'minimal' | 'full' so future commands can detect install profile. Tested end-to-end: --minimal yields 6 skill folders + 0 agents; default yields 86 + 33 (unchanged). * docs(#2762): document --minimal install in README Adds a collapsible 'Minimal Install' section under Getting Started covering: who it's for (local LLMs, token-billed APIs), what you get (6 skills, 0 subagents, ~700 token floor vs ~12k), and the critical caveat that re-installing without --minimal restores the full surface and erases the savings. Includes a comparison table, the manifest inspection one-liner, and the use-case decision matrix. * fix(#2762): address CodeRabbit review + CI failures CodeRabbit findings: 1. Temp dir leak (Minor): stageSkillsForMode created tmp dirs that were never cleaned up. Added a module-level Set tracking every staged dir plus a process.on('exit') handler that rm -rf's them. Also wrap the copy loop in try/catch to remove a partially-populated tmp dir on mid-flight failure. Verified end-to-end: 0 leaked dirs in /tmp after a real install. 2. Codex full -> minimal stale state (Major): a previous full Codex install left agents/gsd-*.toml files plus [agents.gsd-*] sections in config.toml. The original cleanup only removed .md files, so a switch to --minimal would leave Codex still advertising the full agent surface. Cleanup now also handles .toml under isCodex, and minimal mode strips GSD sections from config.toml via the existing stripGsdFromCodexConfig helper (same path used by --uninstall). 3. Nitpick — Codex downgrade regression test: added a spawnSync-based end-to-end test that fakes a previous full install (stale gsd-*.md + gsd-*.toml + GSD-marked config.toml + a user-owned agent/setting), runs install.js --codex --minimal, and asserts stale GSD files + sections are gone while user content is preserved. CI failures (inventory parity): - docs/INVENTORY.md CLI Modules table now lists install-profiles.cjs with the correct headline count (30 -> 31). - docs/INVENTORY-MANIFEST.json regenerated via gen-inventory-manifest.cjs. Test count: 149 pass (was 116 in last commit; +14 new install-minimal + all previously-failing inventory tests now green). * test(#2762): expand install-minimal test coverage for future-proofing Each new test pins a specific guarantee that closes off a future regression class — turning every CodeRabbit finding (including the nitpicky one) into a permanent guard. cleanupStagedSkills suite (+3 tests): - 'full mode does not register a staged dir' — catches a future regression where someone forgets the early-return in stageSkillsForMode and starts polluting STAGED_DIRS in default installs. - 'exit handler registers exactly once across many calls' — catches removal of the exitHandlerRegistered guard. install.js has 13 dispatch sites, so a missing guard would attach 13 listeners. - 'mid-copy failure removes partial staged dir and re-throws' — intercepts fs.copyFileSync to throw mid-loop and asserts the staged dir count in /tmp is unchanged after the throw. Pins the exact CodeRabbit-flagged leak. Claude full -> minimal downgrade (+1 test): - Mirrors the Codex downgrade test for the .md-only path that the other 12 runtimes share. Asserts user-owned agents are preserved. Manifest mode round-trip (+3 tests): - Default install -> mode: 'full' with >6 skills and >0 agents - --minimal -> mode: 'minimal' with exactly 6 skills and 0 agents - --core-only alias produces identical manifest to --minimal Allowlist scope guards (+3 tests): - Every main-loop command IS in allowlist (positive) - Off-loop commands (autonomous, ship, do, progress, next, fast, quick, debug, code-review, verify-work) are NOT (guards against silent scope creep — future contributor adds 'autonomous' to core and the floor erodes) - Unknown mode strings fall through to full behavior — pre-emptive guard for future 'compact'/'tier2' modes that might forget to update the predicate. Total: 25 tests in this file (was 15), 159/159 passing across the install + inventory suites. * fix(#2762): clean up staged tmp dirs on SIGINT/SIGTERM/SIGHUP CodeRabbit follow-up review on c727bf5f flagged that process.on('exit') does not fire on signal-driven termination. An installer is exactly the kind of process users abort mid-run with Ctrl+C, so without explicit signal handlers the staged tmp dirs in STAGED_DIRS would be left behind until the OS reaps tmpdir. Fix: ensureExitCleanup now also registers process.once handlers for SIGINT, SIGTERM, SIGHUP. Each handler runs cleanupStagedSkills then re-raises the same signal via process.kill(pid, sig) so the OS-default handler takes over and the parent shell sees the correct exit code (130 for SIGINT, etc.) — CI scripts and interactive users see the abort the way they expect. Test: spawns a child that stages a tmp dir then blocks; parent captures the staged path from stdout, sends SIGINT, asserts (a) the staged dir is gone after child exit, (b) child exits via the signal not via code 0. Skipped on Windows (signal semantics differ; the natural-exit cleanup test covers the Windows CI matrix). Total: 26 tests in install-minimal.test.cjs (was 25). |
||
|
|
22b73f548d | docs: update changelog for v1.38.5 | ||
|
|
a5a2d44121 | 1.38.4 | ||
|
|
efab0545c7 | docs: update changelog for v1.38.3 | ||
|
|
1068223439 |
feat(#2500): enrich gsd-codebase-mapper arch-focus ARCHITECTURE.md with ASCII diagrams, data flow traces, and constraints (#2715)
* feat(#2500): enrich gsd-codebase-mapper arch-focus ARCHITECTURE.md template The codebase mapper's arch-focus template was a sparse structural inventory. After major refactors, the research/ARCHITECTURE.md (created at /gsd-new-project and never refreshable) went stale while the refreshable codebase version lacked the visual richness that makes architecture docs useful for planning. Add to the ARCHITECTURE.md template: - <!-- refreshed: {date} --> marker at the top (maintainer request) - ASCII system overview diagram with component boxes and flow arrows - Component responsibility table (Component / Responsibility / File) - Primary request path traces with numbered steps and code references - Architectural constraints section (threading, global state, circular imports) - Anti-patterns section with codebase-specific patterns and correct alternatives All existing sections (Pattern Overview, Layers, Key Abstractions, Entry Points, Error Handling, Cross-Cutting Concerns) are preserved. 7 new tests in tests/enh-2500-codebase-mapper-arch-rich-format.test.cjs verify each required section is present in the deployed template. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(#2500): resolve CodeRabbit review findings - Add 'text' language tag to bare ASCII diagram fenced block (markdownlint MD040) - Tighten data flow test: require '### Primary Request Path' heading, 3+ numbered steps, and file:line reference pattern — prevents loose-match false positives - Tighten constraints test: require '## Architectural Constraints' heading AND Threading / Global state / Circular imports tokens — prevents broad keyword matches masking regressions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
b1a670e662 |
fix(#2697): replace retired /gsd: prefix with /gsd- in all user-facing text (#2699)
All workflow, command, reference, template, and tool-output files that surfaced /gsd:<cmd> as a user-typed slash command have been updated to use /gsd-<cmd>, matching the Claude Code skill directory name. Closes #2697 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
eba0c99698 |
fix(#2623): resolve parent .planning root for sub_repos workspaces in SDK query dispatch (#2629)
* fix(#2623): resolve parent .planning root for sub_repos workspaces in SDK query dispatch When `gsd-sdk query` is invoked from inside a `sub_repos`-listed child repo, `projectDir` defaulted to `process.cwd()` which pointed at the child repo, not the parent workspace that owns `.planning/`. Handlers then directly checked `${projectDir}/.planning` and reported `project_exists: false`. The legacy `gsd-tools.cjs` CLI does not have this gap — it calls `findProjectRoot(cwd)` from `bin/lib/core.cjs`, which walks up from the starting directory checking each ancestor's `.planning/config.json` for a `sub_repos` entry that lists the starting directory's top-level segment. This change ports that walk-up as a new `findProjectRoot` helper in `sdk/src/query/helpers.ts` and applies it once in `cli.ts:main()` before dispatching `query`, `run`, `init`, or `auto`. Resolution is idempotent: if `projectDir` already owns `.planning/` (including an explicit `--project-dir` pointing at the workspace root), the helper returns it unchanged. The walk is capped at 10 parent levels and never crosses `$HOME`. All filesystem errors are swallowed. Regression coverage: - `helpers.test.ts` — 8 unit tests covering own-`.planning` guard (#1362), sub_repos match, nested-path match, `planning.sub_repos` shape, heuristic fallback, unparseable config, legacy `multiRepo: true`. - `sub-repos-root.integration.test.ts` — end-to-end baseline (reproduces the bug without the walk-up) and fixed behavior (walk-up + dispatch of `init.new-milestone` reports `project_exists: true` with the parent workspace as `project_root`). sdk vitest: 1511 pass / 24 fail (all 24 failures pre-existing on main, baseline is 26 failing — `comm -23` against baseline produces zero new failures). CJS: 5410 pass / 0 fail. Closes #2623 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(#2623): remove stray .planing typo from integration test setup Address CodeRabbit nitpick: the mkdir('.planing') call on line 23 was dead code from a typo, with errors silently swallowed via .catch(() => {}). The test already creates '.planning' correctly on the next line. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
b35fdd51f3 |
Revert "feat(#2473): ship refuses to open PR when HANDOFF.json declares in-pr…" (#2596)
This reverts commit
|
||
|
|
7212cfd4de |
feat(#2473): ship refuses to open PR when HANDOFF.json declares in-progress work (#2553)
* feat(#2473): ship refuses to open PR when HANDOFF.json declares in-progress work Add a preflight step to /gsd-ship that parses .planning/HANDOFF.json and refuses to run git push + gh pr create when any remaining_tasks[].status is not in the terminal set {done, cancelled, deferred_to_backend, wont_fix}. Refusal names each blocking task and lists four resolutions (finish, mark terminal, delete stale file, --force). Missing HANDOFF.json is a no-op so projects that do not use /gsd-pause-work see no behavior change. Also documents the terminal-statuses contract in references/artifact-types.md and adds tests/ship-handoff-preflight.test.cjs to lock in the contract. Closes #2473 * fix(#2473): capture node exit from $() so malformed HANDOFF.json hard-stops Command substitution BLOCKING=$(node -e "...") discards the inner process exit code, so a corrupted HANDOFF.json that fails JSON.parse would yield empty BLOCKING and fall through silently to push_branch — the opposite of what preflight is supposed to do. Capture node's exit into HANDOFF_EXIT via $? immediately after the assignment and branch on it. A non-zero exit is now a hard refusal with the parser error printed on the preceding stderr line. --force does not bypass this branch: if the file exists and can't be parsed, something is wrong and the user should fix it (option 3 in the refusal message — "Delete HANDOFF.json if it's stale" — still applies). Verified with a tmp-dir simulation: captured exit 2, hard-stop fires correctly on malformed JSON. Added a test case asserting the capture ($?) + branch (-ne 0) + parser exit (process.exit(2)) are all present, so a future refactor can't silently reintroduce the bug. Reported by @coderabbitai on PR #2553. |
||
|
|
86fb9c85c3 |
docs(sdk): registry docs and gsd-sdk query call sites (#2302 Track B) (#2340)
* feat(sdk): golden parity harness and query handler CJS alignment (#2302 Track A) Golden/read-only parity tests and registry alignment, query handler fixes (check-completion, state-mutation, commit, validate, summary, etc.), and WAITING.json dual-write for .gsd/.planning readers. Refs gsd-build/get-shit-done#2341 * fix(sdk): getMilestoneInfo matches GSD ROADMAP (🟡, last bold, STATE fallback) - Recognize in-flight 🟡 milestone bullets like 🚧. - Derive from last **vX.Y Title** before ## Phases when emoji absent. - Fall back to STATE.md milestone when ROADMAP is missing; use last bare vX.Y in cleaned text instead of first (avoids v1.0 from shipped list). - Fixes init.execute-phase milestone_version and buildStateFrontmatter after state.begin-phase (syncStateFrontmatter). * feat(sdk): phase list, plan task structure, requirements extract handlers - Register phase.list-plans, phase.list-artifacts, plan.task-structure, requirements.extract-from-plans (SDK-only; golden-policy exceptions). - Add unit tests; document in QUERY-HANDLERS.md. - writeProfile: honor --output, render dimensions, return profile_path and dimensions_scored. * feat(sdk): centralize getGsdAgentsDir in query helpers Extract agent directory resolution to helpers (GSD_AGENTS_DIR, primary ~/.claude/agents, legacy path). Use from init and docs-init init bundles. docs(15): add 15-CONTEXT for autonomous phase-15 run. * feat(sdk): query CLI CJS fallback and session correlation - createRegistry(eventStream, sessionId) threads correlation into mutation events - gsd-sdk query falls back to gsd-tools.cjs when no native handler matches (disable with GSD_QUERY_FALLBACK=off); stderr bridge warnings - Export createRegistry from @gsd-build/sdk; add sdk/README.md - Update QUERY-HANDLERS.md and registry module docs for fallback + sessionId - Agents: prefer node dist/cli.js query over cat/grep for STATE and plans * fix(sdk): init phase_found parity, docs-init agents path, state field extract - Normalize findPhase not-found to null before roadmap fallback (matches findPhaseInternal) - docs-init: use detectRuntime + resolveAgentsDir for checkAgentsInstalled - state.cjs stateExtractField: horizontal whitespace only after colon (YAML progress guard) - Tests: commit_docs default true; config-get golden uses temp config; golden integration green Refs: #2302 * refactor(sdk): share SessionJsonlRecord in profile-extract-messages CodeRabbit nit: dedupe JSONL record shape for isGenuineUserMessage and streamExtractMessages. * fix(sdk): address CodeRabbit major threads (paths, gates, audit, verify) - Resolve @file: and CLI JSON indirection relative to projectDir; guard empty normalized query command - plan.task-structure + intel extract/patch-meta: resolvePathUnderProject containment - check.config-gates: safe string booleans; plan_checker alias precedence over plan_check default - state.validate/sync: phaseTokenMatches + comparePhaseNum ordering - verify.schema-drift: token match phase dirs; files_modified from parsed frontmatter - audit-open: has_scan_errors, unreadable rows, human report when scans fail - requirements PLANNED key PLAN for root PLAN.md; gsd-tools timeout note - ingest-docs: repo-root path containment; classifier output slug-hash Golden parity test strips has_scan_errors until CJS adds field. * fix: Resolve CodeRabbit security and quality findings - Secure intel.ts and cli.ts against path traversal - Catch and validate git add status in commit.ts - Expand roadmap milestone marker extraction - Fix parsing array-of-objects in frontmatter YAML - Fix unhandled config evaluations - Improve coverage test parity mapping * docs(sdk): registry docs and gsd-sdk query call sites (#2302 Track B) Update CHANGELOG, architecture and user guides, workflow call sites, and read-guard tests for gsd-sdk query; sync ARCHITECTURE.md command/workflow counts and directory-tree totals with the repo (80 commands, 77 workflows). Address CodeRabbit: fix markdown tables and emphasis; align CLI-TOOLS GSDTools and state.read docs with implementation; correct roadmap handler name in universal-anti-patterns; resolve settings workflow config path without relying on config_path from state.load. Refs gsd-build/get-shit-done#2340 * test: raise planner character extraction limit to 48K * fix(sdk): resolve build TS error and doc conflict markers |
||
|
|
fbf30792f3 |
docs: authoritative shipped-surface inventory with filesystem-backed parity tests (#2390)
* docs: finish trust-bug fixes in user guide and commands Correct load-bearing defects in the v1.36.0 docs corpus so readers stop acting on wrong defaults and stale exhaustiveness claims. - README.md: drop "Complete feature"/"Every command"/"All 18 agents" exhaustiveness claims; replace version-pinned "What's new in v1.32" bullet with a CHANGELOG pointer. - CONFIGURATION.md: fix `claude_md_path` default (null/none -> `./CLAUDE.md`) in both Full Schema and core settings table; correct `workflow.tdd_mode` provenance from "Added in v1.37" to "Added in v1.36". - USER-GUIDE.md: fix `workflow.discuss_mode` default (`standard` -> `discuss`) in the workflow-toggles table AND in the abbreviated Full Schema JSON block above it; align the Options cell with the shipped enum. - COMMANDS.md: drop "Complete command syntax" subtitle overclaim to match the README posture. - AGENTS.md: weaken "All 21 specialized agents" header to reflect that the `agents/` filesystem is authoritative (shipped roster is 31). Part 1 of a stacked docs refresh series (PR 1/4). * docs: refresh shipped surface coverage for v1.36 Close the v1.36.0 shipped-surface gaps in the docs corpus. - COMMANDS.md: add /gsd-graphify section (build/query/status/diff) and its config gate; expand /gsd-quick with --validate flag and list/ status/resume subcommands; expand /gsd-thread with list --open, list --resolved, close <slug>, status <slug>. - CLI-TOOLS.md: replace the hardcoded "15 domain modules" count with a pointer to the Module Architecture table; add a graphify verb-family section (build/query/status/diff/snapshot); add Graphify and Learnings rows to the Module Architecture table. - FEATURES.md: add TOC entries for #116 TDD Pipeline Mode and #117 Knowledge Graph Integration; add the #117 body with REQ-GRAPH-01..05. - CONFIGURATION.md: move security_enforcement / security_asvs_level / security_block_on from root into `workflow.*` in Full Schema to match templates/config.json and the gsd-sdk runtime reads; update Security Settings table to use the workflow.* prefix; add planning.sub_repos to Full Schema and description table; add a Graphify Settings section documenting graphify.enabled and graphify.build_timeout. Note: VALID_CONFIG_KEYS in bin/lib/config.cjs does not yet include workflow.security_* or planning.sub_repos, so config-set currently rejects them. That is a pre-existing validator gap that this PR does not attempt to fix; the docs now correctly describe where these keys live per the shipped template and runtime reads. Part 2 of a stacked docs refresh series (PR 2/5), based on PR 1. * docs: make inventory authoritative and reconcile architecture Upgrade docs/INVENTORY.md from "complete for agents, selective for others" to authoritative across all six shipped-surface families, and reconcile docs/ARCHITECTURE.md against the new inventory so the PR that introduces INVENTORY does not also introduce an INVENTORY/ARCHITECTURE contradiction. - docs/AGENTS.md: weaken "21 specialized agents" header to 21 primary + 10 advanced (31 shipped); add new "Advanced and Specialized Agents" section with concise role cards for the 10 previously-omitted shipped agents (pattern-mapper, debug-session-manager, code-reviewer, code-fixer, ai-researcher, domain-researcher, eval-planner, eval-auditor, framework-selector, intel-updater); footnote the Agent Tool Permissions Summary as primary-agents-only so it no longer misleads. - docs/INVENTORY.md (rewritten to be authoritative): * Full 31-agent roster with one-line role + spawner + primary-doc status per agent (unchanged from prior partial work). * Commands: full 75-row enumeration grouped by Core Workflow, Phase & Milestone Management, Session & Navigation, Codebase Intelligence, Review/Debug/Recovery, and Docs/Profile/Utilities — each row carries a one-line role derived from the command's frontmatter and a link to the source file. * Workflows: full 72-row enumeration covering every get-shit-done/workflows/*.md, with a one-line role per workflow and a column naming the user-facing command (or internal orchestrator) that invokes it. * References: full 41-row enumeration grouped by Core, Workflow, Thinking-Model clusters, and the Modular Planner decomposition, matching the groupings docs/ARCHITECTURE.md already uses; notes the few-shot-examples subdirectory separately. * CLI Modules and Hooks: unchanged — already full rosters. * Maintenance section rewritten to describe the drift-guard test suite that will land in PR4 (inventory-counts, commands-doc-parity, agents-doc-parity, cli-modules-doc-parity, hooks-doc-parity). - docs/ARCHITECTURE.md reconciled against INVENTORY: * References block: drop the stale "(35 total)" count; point at INVENTORY.md#references-41-shipped for the authoritative count. * CLI Tools block: drop the stale "19 domain modules" count; point at INVENTORY.md#cli-modules-24-shipped for the authoritative roster. * Agent Spawn Categories: relabel as "Primary Agent Spawn Categories" and add a footer naming the 10 advanced agents and pointing at INVENTORY.md#agents-31-shipped for the full 31-agent roster. - docs/CONFIGURATION.md: preserve the six model-profile rows added in the prior partial work, and tighten the fallback note so it names the 13 shipped agents without an explicit profile row, documents model_overrides as the escape hatch, and points at INVENTORY.md for the authoritative 31-agent roster. Part 3 of a stacked docs refresh series (PR 3/4). Remaining consistency work (USER-GUIDE config-section delete-and-link, FEATURES.md TOC reorder, ARCHITECTURE.md Hook-table expansion + installation-layout collapse, CLI-TOOLS.md module-row additions, workflow-discuss-mode invocation normalization, and the five doc-parity tests) lands in PR4. * test(docs): add consistency guards and remove duplicate refs Consolidates USER-GUIDE.md's command/config duplicates into pointers to COMMANDS.md and CONFIGURATION.md (kills a ghost `resolve_model_ids` key and a stale `discuss_mode: standard` default); reorders FEATURES.md TOC chronologically so v1.32 precedes v1.34/1.35/1.36; expands ARCHITECTURE.md's Hook table to the 11 shipped hooks (gsd-read-injection-scanner, gsd-check-update-worker) and collapses the installation-layout hook enumeration to the *.js/*.sh pattern form; adds audit/gsd2-import/intel rows and state signal-*, audit-open, from-gsd2 verbs to CLI-TOOLS.md; normalizes workflow-discuss-mode.md invocations to `node gsd-tools.cjs config-set`. Adds five drift guards anchored on docs/INVENTORY.md as the authoritative roster: inventory-counts (all six families), commands/agents/cli-modules/hooks parity checks that every shipped surface has a row somewhere. * fix(convergence): thread --ws to review agent; add stall and max-cycles behavioral tests - Thread GSD_WS through to review agent spawn in plan-review-convergence workflow (step 5a) so --ws scoping is symmetric with planning step - Add behavioral stall detection test: asserts workflow compares HIGH_COUNT >= prev_high_count and emits a stall warning - Add behavioral --max-cycles 1 test: asserts workflow reaches escalation gate when cycle >= MAX_CYCLES with HIGH > 0 after a single cycle - Include original PR files (commands, workflow, tests) as the branch predated the PR commits Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(docs,config): PR #2390 review — security_* config keys and REQ-GRAPH-02 scope Addresses trek-e's review items that don't require rebase: - config.cjs: add workflow.security_enforcement, workflow.security_asvs_level, workflow.security_block_on to VALID_CONFIG_KEYS so gsd-sdk config-set accepts them (closed the gap where docs/CONFIGURATION.md listed keys the validator rejected). - core.cjs: add matching CONFIG_DEFAULTS entries (true / 1 / 'high') so the canonical defaults table matches the documented values. - config.cjs: wire the three keys into the new-project workflow defaults so fresh configs inherit them. - planning-config.md: document the three keys in the Workflow Fields table, keeping the CONFIG_DEFAULTS ↔ doc parity test happy. - config-field-docs.test.cjs: extend NAMESPACE_MAP so the flat keys in CONFIG_DEFAULTS resolve to their workflow.* doc rows. - FEATURES.md REQ-GRAPH-02: split the slash-command surface (build|query| status|diff) from the CLI surface which additionally exposes `snapshot` (invoked automatically at the tail of `graphify build`). The prior text overstated the slash-command surface. * docs(inventory): refresh rosters and counts for post-rebase drift origin/main accumulated surfaces since this PR was authored: - Agents: 31 → 33 (+ gsd-doc-classifier, gsd-doc-synthesizer) - Commands: 76 → 82 (+ ingest-docs, ultraplan-phase, spike, spike-wrap-up, sketch, sketch-wrap-up) - Workflows: 73 → 79 (same 6 names) - References: 41 → 49 (+ debugger-philosophy, doc-conflict-engine, mandatory-initial-read, project-skills-discovery, sketch-interactivity, sketch-theme-system, sketch-tooling, sketch-variant-patterns) Adds rows in the existing sub-groupings, introduces a Sketch References subsection, and bumps all four headline counts. Roles are pulled from source frontmatter / purpose blocks for each file. All 5 parity tests (inventory-counts, agents-doc-parity, commands-doc-parity, cli-modules-doc-parity, hooks-doc-parity) pass against this state — 156 assertions, 0 failures. Also updates the 'Coverage note' advanced-agent count 10 → 12 and the few-shot-examples footnote "41 top-level references" → "49" to keep the file internally consistent. * docs(agents): add advanced stubs for gsd-doc-classifier and gsd-doc-synthesizer Both agents ship on main (spawned by /gsd-ingest-docs) but had no coverage in docs/AGENTS.md. Adds the "advanced stub" entries (Role, property table, Key behaviors) following the template used by the other 10 advanced/specialized agents in the same section. Also updates the Agent Tool Permissions Summary scope note from "10 advanced/specialized agents" to 12 to reflect the two new stubs. * docs(commands): add entries for ingest-docs, ultraplan-phase, plan-review-convergence These three commands ship on main (plan-review-convergence via trek-e's 4b452d29 commit on this branch) but had no user-facing section in docs/COMMANDS.md — they lived only in INVENTORY.md. The commands-doc-parity test already passes via INVENTORY, but the user-facing doc was missing canonical explanations, argument tables, and examples. - /gsd-plan-review-convergence → Core Workflow (after /gsd-plan-phase) - /gsd-ultraplan-phase → Core Workflow (after plan-review-convergence) - /gsd-ingest-docs → Brownfield (after /gsd-import, since both consume the references/doc-conflict-engine.md contract) Content pulled from each command's frontmatter and workflow purpose block. * test: remove redundant ARCHITECTURE.md count tests tests/architecture-counts.test.cjs and tests/command-count-sync.test.cjs were added when docs/ARCHITECTURE.md carried hardcoded counts for commands/ workflows/agents. With the PR #2390 cleanup, ARCHITECTURE.md no longer owns those numbers — docs/INVENTORY.md does, enforced by tests/inventory-counts.test.cjs (scans the same filesystem directories with the same readdirSync filter). Keeping these ARCHITECTURE-specific tests would re-introduce the hardcoded counts they guard, defeating trek-e's review point. The single-source-of- truth parity tests already catch the same drift scenarios. Related: #2257 (the regression this replaced). --------- Co-authored-by: Tom Boucher <trekkie@nomorestars.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
3856b53098 |
Merge remote-tracking branch 'origin/main' into fix/2406-ship-read-injection-scanner
# Conflicts: # CHANGELOG.md |
||
|
|
381c138534 | feat(sdk): make checkAgentsInstalled runtime-aware (#2402) (#2413) | ||
|
|
8ac02084be | fix(sdk): point checkAgentsInstalled at ~/.claude/agents (#2401) | ||
|
|
e208e9757c | refactor(agents): consolidate emphasis-marker density in top 4 agents (#2368) (#2412) | ||
|
|
13a96ee994 |
fix(build): include gsd-read-injection-scanner in hooks/dist (#2406)
The scanner was added in #2201 but never added to the HOOKS_TO_COPY allowlist in scripts/build-hooks.js, so it never landed in hooks/dist/. install.js reads from hooks/dist/, so every install on 1.37.0/1.37.1 emitted "Skipped read injection scanner hook — not found at target" and the read-time prompt-injection scanner was silently disabled. - Add gsd-read-injection-scanner.js to HOOKS_TO_COPY - Add it to EXPECTED_ALL_HOOKS regression test in install-hooks-copy Fixes #2406 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d5f849955b |
Merge remote-tracking branch 'origin/main' into feat/2387-ingest-docs-clean
# Conflicts: # CHANGELOG.md |
||
|
|
b96255cf0c |
test(ingest-docs): add structural tests and CHANGELOG entry
- tests/ingest-docs.test.cjs — 40 structural assertions guarding the contract: command/workflow/agent/reference files exist; frontmatter shape; --mode/--manifest/--resolve/path parsing; path traversal guard; 50-doc cap; auto mode-detect via planning_exists; directory conventions for ADR/PRD/SPEC; parallel classifier + synthesizer spawns; BLOCKER/WARNING/INFO severity and the no-write safety gate; gsd-roadmapper routing; --resolve interactive reserved-for-future; INGEST-CONFLICTS.md writing. Classifier covers 5 types, JSON schema, Accepted-only locking. Synthesizer covers precedence ordering, LOCKED-vs-LOCKED block in both modes, three-bucket report, cycle detection, variant preservation, SYNTHESIS.md entry point. Plus a regression guard that /gsd-import still consumes the shared doc-conflict-engine reference (refactor drift check). - CHANGELOG.md — Unreleased "Added" entry for /gsd-ingest-docs (#2387). Full suite: 4151/4151 passing. Refs #2387 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
b2fcacda1b |
fix(install): auto-install @gsd-build/sdk so gsd-sdk is on PATH (#2385)
Every /gsd-* command shells out to `gsd-sdk query …`, but the SDK was never installed by bin/install.js — the `--sdk` flag documented in README was never implemented. Users upgrading to 1.36+ hit "command not found: gsd-sdk" on every command. - Implement SDK install in finishInstall's finalize path - Default on; --no-sdk to skip; --sdk to force when already present - Idempotent probe via `which gsd-sdk` before reinstalling - Failures are warnings, not fatal — install hint printed Closes #2385 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d32e5bd461 |
docs: update changelog for v1.37.1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
bbcec632b6 |
docs: update changelog for v1.37.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
c5e77c8809 |
feat(agents): enforce size budget + extract duplicated boilerplate (#2361) (#2362)
Adds tiered agent-size-budget test to prevent unbounded growth in agent definitions, which are loaded verbatim into context on every subagent dispatch. Extracts two duplicated blocks (mandatory-initial-read, project-skills-discovery) to shared references under get-shit-done/references/ and migrates the 5 top agents (planner, executor, debugger, verifier, phase-researcher) to @file includes. Also fixes two broken relative @planner-source-audit.md references in gsd-planner.md that silently disabled the planner's source audit discipline. Closes #2361 Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
4a912e2e45 |
feat(debugger): extract philosophy block to shared reference (#2363) (#2364)
The gsd-debugger philosophy block contains 76 lines of evergreen debugging disciplines (user-as-reporter, meta-debugging, cognitive biases, restart protocol) that are not debugger-specific workflow and are paid in context on every debugger dispatch. Extracts to get-shit-done/references/debugger-philosophy.md, replaces the inline block with a single @file include. Behavior-preserving. Closes #2363 Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |