Closes#48. Makes the canonical worktree_branch_check fragment verify-only/fail-closed (exit 42, no git reset self-recovery), adds an orchestrator fail-closed collection rule and a cwd-drift guard at execute_waves entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extracts the fail-closed worktree branch-check guard into a single canonical fragment (get-shit-done/references/worktree-branch-check.md) and repoints all five sites at it; orchestrator embeds the runnable block at dispatch. All safety invariants preserved; adversarially reviewed; full matrix green. Closes#588.
* feat(#41): extract per-commit gate_status into ship PR body TDD Audit
/gsd:ship's generate_pr_body now reconstructs the TDD gate trail that a
squash-merge would otherwise discard. A new TDD Audit section walks the
merge-base..HEAD commit range (merges excluded), reads each commit's
gate_status: trailer via Git's native trailer machinery, pairs each
test: commit with its following feat:/fix: implementation commit, and
counts commits lacking a recognized trailer as missing. A single
aggregate `gate_status: skill=N, fallback=N, exempt=N, missing=N`
trailer is emitted as the final line of the PR body so a GitHub
squash-merge carries the audit footprint into the base branch.
Hardening (per adversarial review): impl pairing is restricted to
feat:/fix: (refactor/docs/chore are skipped, never mistaken for GREEN);
the gate_status cell is normalized to a known token and never rendered
raw; commits with multiple gate_status trailers are treated as missing;
every table cell escapes pipes and strips CR/LF; records guard against
delimiter-injection from adversarial commit messages.
Scoped additively: no changes to commands, agents, templates, or SDK.
Closes#41
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#41): add changeset for ship TDD Audit enhancement
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Local-install managed .sh hooks under Claude Code on Windows were wrapped with the absolute Git Bash path; Claude runs the hook string inside Git Bash, so bash tried to exec bash (cannot execute binary file). Centralizes the win32+claude+.sh guard (shellHookOmitsBashRunner) and adds an exported, testable buildLocalShellHookCommand so the local path matches the global path. Closes the #166/#377 regression in the local-install branch. Adds a Windows-covered regression test.
Fixes#580
* fix(#581): add Edit to six writer agents' tools so Edit-only discipline is enforceable
Six writer agents (gsd-eval-planner, gsd-ai-researcher, gsd-domain-researcher,
gsd-phase-researcher, gsd-ui-researcher, gsd-debug-session-manager) shipped with
Write but no Edit in their tools: frontmatter. Their spawn prompts instruct
surgical in-place section edits on existing/shared files (notably the AI-SPEC.md
trio writing disjoint sections of the same file), but with no Edit tool they
fall back to whole-file Write — silently clobbering sibling sections
(last-writer-wins) while still reporting success.
Same bug class as #571, fixed for gsd-doc-writer in #575. This adds Edit
alongside the existing Write for all six (Edit placed adjacent to Write, mirroring
the gsd-doc-writer fix). Write is retained; no prompt-body changes; no other agents
touched.
Adds a regression test (tests/agent-frontmatter.test.cjs) asserting each of the
six section-writer agents carries both Write and Edit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(#581): add changeset fragment for writer-agent Edit fix
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(#581): regenerate changeset via npm run changeset
Replace hand-authored fragment with one generated by the official
scripts/changeset/new.cjs script (correct <adjective>-<noun>-<noun>
filename convention).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes#260
Moves the step-0b absolute-path guard from prose instructions to a harness-enforced PreToolUse hook (gsd-worktree-path-guard.js). Hard-blocks Edit/Write/MultiEdit calls whose absolute path resolves outside the active worktree root.
Squashed from claude/fervent-booth-fb7b1f. Hardens resolveModelPolicy against prototype pollution and fixes resolveModelForTier to check model_policy before dynamic_routing. 199 tests green.
Steps 2 and 4 of resolveEffortInternal now include an else branch that
consults CANONICAL_CONFIG_DEFAULTS.effort when effortCfg is null, mirroring
the existing Step 3 manifest-fallback pattern for routing_tier_defaults.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#488): add gsd-tools effort sync command to re-apply effort config to installed agents
Effort frontmatter is injected at install time, but there was no way to propagate
config changes (agent_overrides, routing_tier_defaults, default) without a full reinstall.
- Adds `cmdEffortSync` to commands.cjs: scans `<configDir>/agents/gsd-*.md`, resolves
the current effort per agent via `resolveEffortInternal` + `renderEffortForRuntime`,
and rewrites (or injects) the `effort:` frontmatter idempotently.
- Dry-run mode (default) reports pending changes without writing; `--apply` writes.
- Accepts `--config-dir` and `--runtime` overrides; gracefully no-ops on non-claude runtimes.
- Wires the `effort sync` subcommand into `gsd-tools.cjs` and adds it to the help list.
- Five regression tests cover dry-run, apply, no-op, inject-missing, and non-claude runtime.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#488): add gsd-tools effort sync command to re-apply effort config to installed agents
Effort frontmatter is injected at install time, but there was no way to propagate
config changes (agent_overrides, routing_tier_defaults, default) without a full reinstall.
- Adds `cmdEffortSync` to commands.cjs: scans `<configDir>/agents/gsd-*.md`, resolves
the current effort per agent via `resolveInstallTimeEffort` + `renderEffortForRuntime`,
and rewrites (or injects) the `effort:` frontmatter idempotently.
- Uses install-time resolvers (readGsdEffectiveEffortConfig from bin/install.js) rather
than the runtime resolver (loadConfig), so home-level effort changes in ~/.gsd/defaults.json
are correctly picked up even when a project .planning/config.json exists.
- Skips symlinks in agents dir to avoid clobbering symlink targets.
- Dry-run mode (default) reports pending changes without writing; --apply writes.
- Accepts --config-dir and --runtime overrides; gracefully no-ops on non-claude runtimes.
- Rejects unexpected positional arguments in the CLI parser.
- Wires the effort sync subcommand into gsd-tools.cjs and adds it to the help list.
- Eight regression tests: dry-run, apply, noop, inject-missing, non-claude runtime,
home-config gap scenario, CLI positional-arg rejection, and CLI dispatch integration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#570): scope Codex leak scanner to manifest, replace bare ~/.claude refs
Two root causes:
- scanForLeakedPaths walked entire ~/.codex tree, flagging pre-existing
unrelated files; now reads gsd-file-manifest.json to scope scan to
GSD-owned artifacts only
- convertClaudeToCodexMarkdown replaced ~/\.claude/ (slash form) but not
bare ~/\.claude\b; gsd-debugger.toml and gsd-surface/SKILL.md examples
slipped through; bare word-boundary replacement now added
- writeManifest tracked agents/gsd-*.md but Codex installs .toml files;
manifest now also records .toml agent files so the scoped scanner covers them
Closes#570
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: add changeset fragment for #570
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#571): forbid Write in doc-writer fix mode; add workflow truncation guard
gsd-doc-writer in fix mode only had Write in its tools list, so when
correcting a specific failing claim it would re-emit the whole file with
only the lines it had in context — truncating untracked docs with no git
recovery path.
Fix 1 (root cause): add Edit to the agent tools frontmatter and rewrite
fix_mode instructions to mandate Edit for surgical corrections and
explicitly forbid Write on existing files. Also reinforced in
critical_rules.
Fix 2 (safety net): add a post-fix line-count guard in the fix_loop step
of docs-update.md. If the file shrank by >90% after a fix agent runs,
the orchestrator restores the file from the existing_content it captured
before dispatch and logs a WARNING. This makes the previously
unrecoverable case recoverable.
Regression test: tests/bug-571-doc-writer-fix-mode-edit-only.test.cjs
covers both the agent contract and the workflow guard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: add changeset for fix#571
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#571): address codex adversarial review findings
- Quote {doc_path} in shell snippets to handle paths with spaces (#SECURITY)
- Clarify corrupted doc re-verification vs re-fix distinction (#CORRECTNESS)
- Strengthen regression tests with structural ordering assertions (#REGRESSION)
- Move docs-update.md from global ALLOWLIST to SIZE_ONLY_WORKFLOWS so
injection scanning still runs while only the 50K size finding is exempt (#SECURITY)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(#49): provider-neutral model policy presets
Adds model_policy config surface with known-provider presets (openai/anthropic/google/qwen) and generic provider escape hatch. model_policy.runtime_tiers resolves before legacy model_profile_overrides. reasoning_effort is stripped for unsupported runtimes.
Closes#49
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#49): replace unregistered /gsd-settings-advanced token in docs
docs-parity-live-registry enforces every /token in docs/*.md maps to
a live command. /gsd-settings-advanced is a workflow filename, not a
registered command — use /gsd:settings instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#49): update INVENTORY.md count and manifest for config-types.cjs
inventory-counts and inventory-manifest-sync tests require the headline
count and INVENTORY-MANIFEST.json to reflect every file in bin/lib/.
config-types.cjs (new module added by feat(#49)) was missing from both.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* enhancement(#558): add liveness hints to all GSD spawn announcements
Append '(runs in a subagent — no output until it returns, ~1–5 min; expected,
not a freeze)' inline to every ◆ Spawning… banner and subagent dispatch
instruction across 26 workflows. Silent subagents look identical to frozen
sessions — this note sets the expectation so users wait instead of killing
healthy in-progress work.
Changes:
- references/ui-brand.md: document liveness convention under Spawning Indicators
- 10 banner workflows: append liveness note to ◆ Spawning… lines in-place
- 18 subagent-only workflows: add print instruction with liveness phrase
- tests/spawn-liveness-banner.test.cjs: new test; fails if any workflow with
subagent_type omits 'runs in a subagent'
- docs/USER-GUIDE.md: troubleshooting entry for frozen-looking spawns
- .changeset/558-spawn-liveness-banner.md: changeset fragment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#558): add missing pr field to changeset fragment
The changeset lint requires pr: <NNN> in frontmatter; the fragment was
written without it, causing parse.cjs to reject it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#558): address codex review — missed spawns and tighten test
- plan-phase.md: add liveness note to chunked outline planner and
per-plan chunked planner banners (two missed ◆ Spawning… lines)
- quick.md: add liveness note to research banner and add missing
display line before planner spawn in Step 5
- plan-review-convergence.md: add liveness note to initial planning
and review-agent spawn Display lines
- docs-update.md: add Print instructions with liveness note before
gsd-doc-verifier spawns in Phase 1 and Phase 2
- autonomous.md: add Print instruction with liveness note before
background plan-phase agent dispatch in step 3b
- tests/spawn-liveness-banner.test.cjs: replace single file-level
check with two assertions:
(1) every ◆ Spawning… banner line carries the phrase on that line
(2) every file with subagent_type contains the phrase somewhere
The tighter test would have caught all five missed spawns.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#558): tighten spawn-liveness test regex to catch spawn-word-anywhere variants
Previous SPAWN_BANNER_RE only matched ◆ immediately followed by Spawning|spawning.
Replace with /◆[^\n]*\bspawning?\b/i which matches the spawn word anywhere on the
◆ line — catching "◆ Chunked mode: spawning outline planner..." and similar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#558): rename changeset to PR number 566 and correct pr field
Changeset was filed as 558-spawn-liveness-banner.md (issue#) but the
convention is the PR number. Renamed to 566-spawn-liveness-banner.md
and updated pr: 558 → pr: 566 so release notes link to the right PR.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* enhancement(#40): integrate branch pruning into /gsd-cleanup archival workflow
Adds a prune_local_branches step to cleanup.md (between archive_phases and
commit) that force-deletes local branches whose upstream is gone — keeping
local clones symmetric with delete_branch_on_merge on GitHub.
Key design choices vs. PR #562 (the local-model draft):
- dry-run step shows stale branches using cached tracking refs only; git
fetch --prune is deferred to the execution step so the dry-run is
non-side-effecting
- awk uses { if ($1 != "*") print $1 } form to explicitly exclude the
currently checked-out branch (the * prefix in git branch -vv output),
not a prose note that lets xargs receive literal * as an argument
- git fetch --prune runs exactly once, in prune_local_branches, eliminating
the TOCTOU window between a preview fetch and an execution fetch
- two new negative-contract tests: identify_completed_milestones must not
run git branch commands; show_dry_run must not run git fetch --prune
Closes#40
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: regenerate changeset using repo script (correct format)
Replaces hand-written fragment (used `/** ... */` comment syntax)
with one generated by `npm run changeset -- --type Changed --pr 562`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address codex review blockers — protect main/next/trunk, align dry-run with execution
Codex adversarial review (pre-PR gate) flagged two blockers:
1. awk filter only excluded '*' (current branch) but not protected names.
main/next/trunk/develop could be force-deleted if their upstream was
gone. Fix: use !~ /^\*$|^main$|^next$|^trunk$|^develop$/ regex match.
2. Dry-run enumerated from cached tracking refs; execution re-ran
git fetch --prune, creating a TOCTOU window between what the user
confirmed and what got deleted. Fix: move git fetch --prune into
show_dry_run (prefetch for display accuracy); prune_local_branches
now enumerates from the already-fetched state with no second fetch.
Updated 14 structural tests to match new design (added protected-name
exclusion test; inverted show_dry_run fetch assertion).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Import `extractCurrentMilestone` from `./core.cjs` into `state.cjs`
- Replace `getMilestonePhaseFilter.phaseCount` usage in `buildStateFrontmatter`
with a direct ROADMAP parse using the same digit-anchored pattern as
`roadmap.analyze` — single source of truth for `total_phases` (#549)
- Apply the same replacement in `cmdStateSync` for consistency
- Add regression test: bug-549-total-phases-overcounts-with-phase-section-heading.test.cjs
- Add changeset fragment: .changeset/549-total-phases-decimal-overcounting.md
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#557): Milestone marked complete while ROADMAP lists unstarted phases
- Fix 1 — Broaden extractCurrentMilestone() (core.cjs):
(a) Extend sectionPattern to also match <summary> tag content: when a
milestone version appears only inside a <summary> tag, locate the
enclosing <details> block and return its content directly instead of
falling through to stripShippedMilestones().
(b) Extend activeMarkerPattern to include 🔄: change
/\b(?:STARTED|ACTIVE|WIP)\b|in\s+progress|🚧/i to also match 🔄.
(c) Extend the Step 2 fallback regex from /🚧\s*\*\*v(\d+\.\d+)\s/ to
/(?:🚧|🔄)\s*\*\*v(\d+\.\d+)\s/ so the emoji-only fallback also
catches 🔄.
- Fix 2 — Add completion guard (milestone.cjs):
Before writing "milestone complete" to STATE.md, check whether any phase
in the current milestone has no directory on disk (disk_status:
no_directory). When STATE.md milestone version matches the version being
completed and unstarted phases are found, emit an error. Re-run with
--force to override.
- Fix 3 — Add W021 health check (verify.cjs):
Check 14 (W021): if STATE.md status contains "milestone complete" or
"archived", scan the current milestone section of ROADMAP.md; if any
phase has no directory on disk, emit W021 warning: "STATE says milestone
complete but ROADMAP lists N unstarted phase(s)".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#557): replace hand-written changeset with generated fragment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#557): address Codex review findings
- core.cjs: add (?!Phase\s+\S) to sectionPattern so phase headings that
mention the milestone version (e.g. ### Phase 1: v1.3 migration) cannot
bypass the <summary> fallback path
- milestone.cjs: replace loose numeric-prefix matching with phaseTokenMatches
+ normalizePhaseName so decimal (2.1) and letter-suffix (12A) phase IDs
are handled correctly in the completion guard
- verify.cjs: same correction in W021 check; also add phaseTokenMatches to
core.cjs import
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#557): fix getMilestonePhaseFilter version-heading false match
getMilestonePhaseFilter's sectionPattern also lacked the (?!Phase\s+\S)
exclusion that extractCurrentMilestone received in the previous commit.
A phase title like "### Phase 4: v1.3 migration" could match as the
milestone section start, mis-scoping the phase set used for completion
stats and archive.
Also handle the case where the version lives only in a <summary> tag:
when there is no heading match but a <summary> match exists, skip
setting missingExplicitVersion so milestone.complete does not incorrectly
error with "no phases found".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes#38
Removes the `"approved" → continue` ack-and-advance shortcut from the `human_needed` verification path in execute-phase. The phase now stays pending until `/gsd:verify-work` completes the UAT and triggers its auto-transition — enforcing the invariant that ROADMAP advances only after a completed verification record.
Also fixes: UAT file format mismatch (`status: testing` + correct `## Current Test` key shape), filename alignment (`{phase_num}-UAT.md`), explicit ack handler covering legacy `approved` keyword, stale `HUMAN-UAT.md` references in agent/reference files.
* docs(#457): rewrite ADR-457 to ground truth and accept build-at-publish
The prior draft asserted a codebase state that never existed (13 tsc-generated
files, src/ trees, a tests/cjs-ts-parity.test.cjs). Corrected to verified ground
truth (84 bin/lib .cjs, 1 value-baked package-identity.cjs, no tsc pipeline),
distinguished value-baking from transpilation so package-identity stops being
miscited as precedent, made check-in-the-artifact vs build-at-publish the central
decision, and flipped status to Accepted (build-at-publish).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* build(#537): pilot TS build-at-publish for bin/lib (semver-compare)
First hand-written module collapsed to a TypeScript source of truth per ADR-457.
src/semver-compare.cts compiles (tsc, strict, noEmitOnError) to a gitignored
get-shit-done/bin/lib/semver-compare.cjs. build:lib is wired into build, pretest,
pretest:coverage, and prepublishOnly so the artifact is built before test and
shipped on publish. Type-aware ESLint on src/**/*.cts immediately caught the
params were over-typed as `unknown` (no-base-to-string); narrowed to a honest
VersionInput domain type. Behavior preserved: semver-compare.test.cjs (14) and
bug-10 (4) pass against the generated output; runtime consumer changeset/cli.cjs
unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#537): make build-at-publish robust across all CI paths (codex review)
Adversarial review found the pilot's generated artifact would be missing on
clean CI checkouts. `pretest`/`pretest:coverage` only fire for `npm test`, but
CI runs `test:unit`/`test:integration`/`test:install` and `node run-tests.cjs`
directly — none of which built the artifact, so any suite requiring
semver-compare.cjs would hit module-not-found on a clean checkout, and
install-smoke's `npm pack` could ship without it.
- Add a `prepare` script (`npm run build:lib`). `npm ci` runs it automatically,
so every CI test job and install-smoke's pack emit the artifact before use.
This is the idiomatic npm mechanism for compiled-output-not-in-git and fixes
both the test and pack paths in one place.
- Add `src/` + `tsconfig.build.json` to ci-test-scope and the install-smoke /
mutation path filters, so a source-only edit to a migrated module still
triggers its tests and mutation coverage (prevents silent CI skips).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#537): map src/*.cts to built artifact in mutation changed-files detection
Follow-up to the codex re-review. The prior commit added src/**/*.cts to the
mutation workflow's path trigger but left its "compute changed core lib files"
step diffing only get-shit-done/bin/lib/**/*.cjs — which are now gitignored and
never appear in a diff. A source-only edit would trigger the workflow then
early-exit ("no core lib files changed"), silently skipping mutation testing.
Map each changed src/*.cts to its built get-shit-done/bin/lib/*.cjs path (the
on-disk artifact Stryker mutates after prepare/build:lib), merge with the
hand-written .cjs diff, and apply the test/excluded-module filters once.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#537): use 'src/' pathspec in mutation diff (git glob doesn't match top-level)
Codex review caught that `git diff -- 'src/**/*.cts'` returns empty for a
top-level file like src/semver-compare.cts — git's default pathspec glob does
not match `**` across zero directories (verified on git 2.50.1). The prior
commit's src-detection therefore never fired, so source-only changes still
skipped mutation. Switch to the dir-scoped pathspec 'src/' + a `.cts` grep
(robust for flat and nested layouts), and broaden the workflow path trigger to
'src/**' to match install-smoke. Verified end-to-end: a change to
src/semver-compare.cts now resolves to get-shit-done/bin/lib/semver-compare.cjs
in the --mutate list.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#537): add changeset fragment for build-at-publish pilot
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#537): replace prepare with prepack + build-if-missing; defer mutation wiring
CI surfaced three real issues the local run and codex review missed:
1. lockfile-sync failed on every platform. Root cause: `npm ci --dry-run` (the
repo's lockfile health check) RUNS the `prepare` script, but in dry-run the
devDependencies aren't installed, so `tsc` is not found (exit 127) and the
check reports a misleading "out of sync". `prepare` is the wrong hook for a
build needing a devDep. Replace it with `prepack` (runs only on pack/publish,
when node_modules exists) for the tarball path, and build the artifact inside
scripts/run-tests.cjs (build-if-missing) for the test path — the universal
chokepoint every CI test invocation funnels through, including the direct
`node run-tests.cjs --files-from` step that bypasses npm lifecycle hooks. The
guard is a no-op once built, so the run-tests harness test is unaffected.
2. The Stryker mutation gate ran only 1 test against semver-compare (~0% score,
71/71 mutants surviving) — a Stryker test-selection problem orthogonal to the
build migration, and raising the score needs property tests (ADR-456). Revert
the mutation.yml src wiring; mutation coverage for src-authored modules is a
separate follow-up tracked in #537. (The deletion of the gitignored top-level
.cjs does not match the workflow's `bin/lib/**/*.cjs` git pathspec, so the
gate skips cleanly.)
Verified: clean-room `npm ci --dry-run` exits 0; deleting the artifact then
running a suite rebuilds it; run-tests harness 22/22 green; `npm pack` includes
the built artifact via prepack.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#448): resolve UI safety gate helper against the GSD install dir
The §5.6 UI Design Contract Gate (and autonomous §3a.5) resolved
ui-safety-gate.cjs via `git rev-parse --show-toplevel`, i.e. the
consuming project's git root — which has no bin/lib. The node call
failed, its exit code was conflated with "no UI", and the gate
silently no-opped so frontend phases skipped the UI-SPEC prompt.
Resolve the helper against the GSD install dir via RUNTIME_DIR (the
same idiom §1 uses for gsd-tools), with git-toplevel and $HOME/.claude
fallbacks. When the helper genuinely can't be found, fail OPEN with a
stderr warning (assume UI present) rather than silently skipping.
Tests: bug-3706 structural guard now requires RUNTIME_DIR resolution
and forbids the consuming-project GSD_REPO_ROOT anchor; a new
behavioral test resolves and runs the helper from a temp consuming
project (no bin/lib) with RUNTIME_DIR set. autonomous-ui-steps updated
to match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#448): add changeset fragment for PR #539
* fix(#448): add get-shit-done/bin/lib/ to UI gate probe and deploy helper there
The installer copies get-shit-done/ to the target but not root bin/lib/, so
the helper was never found for installed users. Placing ui-safety-gate.cjs in
get-shit-done/bin/lib/ ensures the installer deploys it, and probing that path
first makes the gate work correctly in installed runtimes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: update changeset to cover get-shit-done/bin/lib/ deployment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: update inventory for ui-safety-gate.cjs in get-shit-done/bin/lib/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#447): scope post-planning gap analysis to phase_req_ids
§13e gap-analysis diffed the entire REQUIREMENTS.md against a phase's
plans even when the phase mapped no REQ-IDs, so a phase mapping nothing
reported every unrelated project requirement as "not covered".
Teach the gap-analysis CLI a --phase-req-ids option (null/TBD skips the
requirements comparison, an ID list scopes to it, absent = unchanged
back-compat) and have §13e pass the phase's mapped IDs — mirroring the
§13 Requirements Coverage Gate's null/TBD skip. CONTEXT.md decisions stay
in scope regardless.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#447): source phase_req_ids from init.plan-phase, not roadmap.get-phase
Adversarial-review follow-up. roadmap.get-phase returns the raw phase
markdown (not JSON), so `--pick phase_req_ids` yielded nothing and §13e
would have skipped the requirements comparison for EVERY phase — a
silent regression. phase_req_ids is exposed by init.plan-phase; switch
§13e to it. Adds an integration test asserting the query exposes the
IDs and that gap-analysis scopes to them (and skips when unmapped).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#447): add changeset fragment for PR #538
* fix(#447): surface mapped REQ-IDs absent from REQUIREMENTS.md as explicit missing rows
Previously, a phase_req_ids entry not found in REQUIREMENTS.md was silently
dropped from the gap report, allowing the analysis to falsely report full
coverage when the requirement document had drifted.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: update changeset to cover missing-REQ-ID detection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#191): migrate gsd-sdk query call sites to gsd-tools query
Retiring the gsd-sdk shim. gsd-tools.cjs already accepts `query` as a
meta-prefix (gsd-tools query <command>), so this is a behavior-preserving 1:1
swap across the runtime reference prompts, the graphify hook's commit-detection
gate, and two bin/lib comment/message references.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#191): remove vestigial gsd-sdk shim code from installer + projection
The gsd-sdk shim was already not wired up (no gsd-sdk bin in package.json;
buildWindowsShimTriple had zero call sites). Remove the dead code:
- shell-command-projection.cjs: buildWindowsShimTriple + formatSdkPathDiagnostic
(+ their now-unused PACKAGE_NAME import) and exports
- install.js: the re-export wrappers + imports, the #3406 stale-standalone-sdk
detection (detectStaleStandaloneSdk/formatStaleStandaloneSdkWarning + its
global-install call site), and the exports
Preserved (retained, not gsd-sdk): buildCodexHookWindowsShimIR (#3426) — only
its comments referenced the gsd-sdk pattern; reworded. Also kept the
homePathCoveredByRc 'reopen your shell' branch in maybeSuggestPathExport — its
logic is bin-dir-agnostic, only the message mentioned gsd-sdk; reworded to use
the actual bin dir.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(#191): update tests for retired gsd-sdk shim
- bug-3441/bug-3442: drop the formatSdkPathDiagnostic / buildWindowsShimTriple
assertions (functions removed); retained PATH-action + drift-guard tests stay
- bug-505: remove the 'still exported' assertions for detectStaleStandaloneSdk /
formatStaleStandaloneSdkWarning / the shim contract surface (#505 kept them;
#191 removes them)
- graphify-auto-update: migrate the hook-dispatch inputs gsd-sdk query commit ->
gsd-tools query commit to match the migrated commit hook
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#191): point active docs at gsd-tools query (gsd-sdk shim retired)
Update the user/agent-facing docs (AGENTS, COMMANDS, CONFIGURATION, USER-GUIDE,
ship-pr-body-sections) that presented gsd-sdk query as a current command to
gsd-tools query. Historical docs (ADRs, PRDs, release notes) left untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#191): correct state.load vs state.json description for gsd-tools query
Adversarial-review (codex) finding: the migrated USER-GUIDE line claimed both
'gsd-tools query state.json' and 'state.load' resolve to the frontmatter-rebuild
handler. Verified they don't — state.load returns the CJS load shape
(config + state_raw + flags), state.json returns the frontmatter shape. Both are
available via gsd-tools query; corrected the text to say so.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#191): add changeset for gsd-sdk shim retirement
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* chore: rename npm package + bin to @opengsd/gsd-core (functional)
- package.json: name @opengsd/get-shit-done-redux → @opengsd/gsd-core,
bin key get-shit-done-redux → gsd-core, repository/homepage/bugs URLs
- package-lock.json: regenerated (npm install --package-lock-only)
- tests/**, scripts/**, bin/**, .github/**, agents/**, commands/**,
get-shit-done/bin/**, get-shit-done/workflows/**:
applied the 4-rule replacement (scoped npm ref, GitHub repo path,
bin/clone invocations) per #505 single-source refactor
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: sweep live references to @opengsd/gsd-core
Update all live documentation (README.md + translations, docs/**,
CONTRIBUTING.md, VERSIONING.md, SECURITY.md, CONTEXT.md,
docs/CANARY.md) to reflect the renamed package and repository.
Rules applied:
- @opengsd/get-shit-done-redux → @opengsd/gsd-core (scoped npm name)
- open-gsd/get-shit-done-redux → open-gsd/gsd-core (GitHub repo)
- GSD-redux/get-shit-done-redux → open-gsd/gsd-core (stale badge org)
- bare bin/clone refs → gsd-core
CHANGELOG.md, docs/adr/**, docs/RELEASE-*.md, docs/research/**,
and .changeset/** are preserved byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: add negative lookbehind to slash-command regex in bug-2954 test
The extractSlashReferences regex matched /gsd-core inside npm package
URLs (@opengsd/gsd-core), producing a false /gsd:core command reference.
Adding a negative lookbehind (?<![a-z]) excludes matches preceded by a
letter, so only standalone /gsd-<cmd> and /gsd:<cmd> tokens are found.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#518): add changeset for package rename
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(#518): update package-identity expectations to the renamed coordinates
The rebase regenerated the seam to @opengsd/gsd-core (bin gsd-core, repo
open-gsd/gsd-core). The #498 seam tests assert deriveIdentity against the REAL
package.json, so their expected literals must follow the rename. The drift-lint
unit test is left as-is — its SEAM is a self-consistent fixture and its
stale-literal detection cases would shift if altered; the live-repo scan in it
already passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#22): add plan_review.source_grounding + _authority config keys
Two additive opt-out keys for the drift guard: source_grounding (bool,
default true) gates the source-grounded reviewer pass; _authority (enum
grep|intel|treesitter|lsp|scip, default grep) selects the resolver rung.
No existing default changed.
Refs #22
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(#22): add intel api-surface renderer + CLI subcommand
Renders .planning/intel/api-map.json into a human-readable API-SURFACE.md
for planner injection. Empty/missing map still writes a surface that
announces itself incomplete (absence = unknown, not 'does not exist').
Gated on intel.enabled like all intel functions.
Refs #22
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(#22): add source-grounding pass to plan-review-convergence
Default-on reviewer pass (plan_review.source_grounding) that enumerates
every symbol a plan cites, excludes declared new artifacts, resolves each
against source via the configured authority adapter, and records
three-valued verdicts. rung-0/1 MISSING is needs-acknowledgement, not a
hard block; UNCHECKABLE is logged in a REVIEWS.md coverage section.
Refs #22
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(#22): inject API-SURFACE.md into planner + require Artifacts section
When intel.enabled, plan-phase regenerates API-SURFACE.md and injects it
as a HINT (prefer, may be incomplete, absence = unknown), never a hard
rule. Every plan must now emit an 'Artifacts this phase produces' section
so the source-grounding reviewer can separate new symbols from references
to existing code.
Refs #22
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(#22): surface drift-guard in setup + settings, add docs
/gsd:new-project asks to enable plan_review.source_grounding (default Y);
/gsd:settings exposes the toggle and authority knob. Documents both config
keys in CONFIGURATION.md, the intel api-surface command in COMMANDS.md,
the drift guard in USER-GUIDE.md, and links ADR 22 from ARCHITECTURE.md.
Refs #22
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(#22): respect AskUserQuestion 4-option cap and plan-phase XL line budget
settings drift-guard toggle moved to its own 2-option question; #22
plan-phase additions condensed to bring the file back under the 1810-line
XL budget without dropping the intel gate, the incomplete-surface hint, or
the Artifacts-section requirement.
Refs #22
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(#22): use live slash-command forms in drift-guard docs
Doc-parity gate requires every slash-command token in docs/*.md to resolve
to a registered command. Corrected the command form(s) referenced in the
#22 drift-guard / api-surface documentation.
The unresolved token was /gsd-core, matched from the GitHub repo reference
"open-gsd/gsd-core#22" in docs/adr/22-plan-drift-guard.md. This is the
same pattern as the existing 'test-runner' exemption (open-gsd/gsd-test-runner).
Added 'core' to INTERNAL_COMPONENT_SLUGS with a matching explanatory comment.
Refs #22
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(#22): add changeset fragment for drift guard (PR #487)
Refs #22
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: CI Rebase Check <ci@gsd-redux>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor: remove stale sdk/src generated-file banners from bin/lib/*.cjs (#506)
Drop the GENERATED FILE / Source: sdk/src / Regenerate: cd sdk banners from
13 hand-maintained CJS modules and delete the orphaned
generator-freshness-contract script + test. Post-ADR-0174 cleanup; the
referenced sdk/ generator pipeline (dir, gen:* scripts, *.generated.cjs) no
longer exists. No runtime behavior change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: add changeset for #510 (sdk/src banner cleanup)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#498): generated package-identity seam derived from package.json
Introduce a single source for GSD's published-package coordinates:
scripts/generate-package-identity.cjs (pure deriveIdentity + formatManualInstall
+ render) emits the generated get-shit-done/bin/lib/package-identity.cjs with
values baked from package.json at build time. Baking is required because the
installed tree carries only a synthetic {"type":"commonjs"} package.json, so a
runtime require('package.json').name resolves to undefined (#378). Reconciles
Wired into npm run build; a parity test fails CI if the committed file drifts
from package.json.
Refs #498
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#498): repoint update worker + check-latest-version at the seam
- check-latest-version.cjs sources PACKAGE_NAME from the package-identity seam
instead of a re-typed literal (single source; #2992's constant guarantee is
preserved since the seam bakes from package.json).
- gsd-check-update-worker.js no longer does require('../package.json').name
(resolved to undefined in the installed tree → background update check
silently broken, #378). It now delegates the latest-version lookup to
checkLatestVersion(), collapsing the duplicated npm-view call onto the single
deterministic adapter and inheriting its typed {ok,version,reason} surface.
- Move the PR #3102 Windows shell-gate contract test onto execNpm (where the
spawn now lives) and assert the worker no longer spawns npm directly.
- Rewrite the #378 contract: worker must NOT use require(package.json).name and
must delegate; check-latest-version PACKAGE_NAME is single-sourced from the seam.
Fixes #378-class runtime breakage. Refs #498
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#498): changeset for package-identity seam + update-check fix
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#498): drift-guard lint — value-check GSD coordinate literals against the seam
scripts/lint-package-identity-drift.cjs scans the runtime/code surface
(bin/, hooks/, scripts/, get-shit-done/) and asserts every GSD package name
and GitHub repo slug literal equals the Package Identity seam's current value.
Passes today; fails the moment a repoint isn't propagated (rename package.json,
regenerate the seam, and stale literals are reported until updated). This is
the second adapter that makes the seam real and a repoint mechanically safe.
Enforced via tests/issue-498-identity-drift-lint.test.cjs (scanRepo === [])
under npm test; also exposed as `npm run check:identity-drift`.
Refs #498
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#498): update-context projection — port update.md resolution to a tested seam
Add get-shit-done/bin/lib/update-context.cjs: a pure, injected-fs port of
update.md's ~280-line get_installed_version bash. resolveUpdateContext()
reproduces the full precedence cascade (preferred fast-path -> local probe ->
global probe via env overrides then $HOME -> LOCAL-if-distinct -> scope
cascade -> UNKNOWN) and returns the 4-field contract { installedVersion,
scope, runtime, gsdDir }. The fs is injected so every branch is finally
testable without a live multi-runtime install.
Expose it as `gsd-tools update-context [--config-dir <d>] [--runtime <r>] --json`.
Purely additive — update.md is unchanged in this commit; the workflow swap
follows separately.
Refs #498
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(#498): swap update.md resolution to the update-context projection
Replace ~280 lines of inline runtime/scope/config-dir bash in update.md's
get_installed_version step with a call to `gsd-tools update-context --json`
(60 lines: derive PREFERRED_* from execution_context, resolve gsd-tools.cjs,
parse the 4-field JSON). Behavior is unchanged — the projection reproduces the
same cascade — but the logic is now tested in update-context.cjs instead of
untestable bash-in-markdown.
Relocate the #3608 antigravity-first-class contract onto the projection
(RUNTIME_DIRS order, inferPreferredRuntime, envRuntimeDirs) plus a behavioral
test; keep the execution_context path-classification assertion on update.md.
Re-point install.test's custom-config-dir assertion (kilo.jsonc/KILO_CONFIG)
to update-context.cjs where that detection now lives.
Full root suite: 2022 pass / 0 fail.
Refs #498
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(#498): record Update Context Module in CONTEXT.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#498): CI — avoid bare gsd-tools in update.md; register new CLI modules
- update.md update-context invocation: resolve the PATH gsd-tools shim into a
variable and call "$GSD_TOOLS" (never a bare `gsd-tools` command) — satisfies
the #2851 workflow-bare-gsd-tools guard.
- Register package-identity.cjs and update-context.cjs in docs/INVENTORY.md
(CLI Modules 76 -> 78 + rows) and regenerate docs/INVENTORY-MANIFEST.json,
fixing inventory-counts and inventory-manifest-sync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(#498): make update-context + parity tests OS-agnostic (Windows CI)
Two Windows-only test failures, both test-portability (production code is fine —
the real-fs CLI integration test passed on Windows):
- update-context resolver tests + bug-3608 behavioral test used POSIX path-string
keys in their fake fs, but the resolver builds lookups via path.join/resolve
(backslash + drive letter on Windows) → keys never matched → everything
resolved to UNKNOWN/claude. Normalize fake-fs keys and gsdDir comparisons
through path.resolve so they match on both platforms.
- package-identity parity test compared render() (LF) to the committed file,
which Windows git checks out as CRLF (no .gitattributes eol rule). Normalize
line endings before comparing, matching the repo convention
(autonomous-decomposition, bug-3707).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#498): update.md backup must use GSD_DIR (adversarial-review finding)
The get_installed_version rewrite emits GSD_DIR but dropped the probe-loop
variables LOCAL_DIR/GLOBAL_DIR. The backup_custom_files step still read those,
so RUNTIME_DIR went empty for every LOCAL/GLOBAL install and detect-custom-files
was skipped — and since the update then runs a clean install that wipes managed
dirs (commands/gsd, get-shit-done), user-added files could be deleted without
the intended backup.
Set RUNTIME_DIR="$GSD_DIR" directly (the resolved config dir; empty for
UNKNOWN scope, which still skips the backup). Add a structural regression
(tests/issue-498-update-backup-runtime-dir.test.cjs).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(#503): re-point Antigravity .agent detection at the #498 projection
#499 moves the runtime/scope detection cascade out of update.md inline bash
into get-shit-done/bin/lib/update-context.cjs. The #503 regression test asserted
on the inline RUNTIME_DIRS array, which no longer exists, so it would fail
against the projected update.md even though the .agent guarantee is preserved.
Rewrite it to verify the surviving surfaces:
- behavioral: resolveUpdateContext resolves a LOCAL ./.agent install to the
antigravity runtime (the original root cause, now covered by adding
['antigravity', '.agent'] to the projection RUNTIME_DIRS table)
- update.md prose classifier still maps /.agent/ -> antigravity
- the post-update cache-clear for-dir loop still includes .agent
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#498): finish de-hardcoding consumers + close adversarial-review parity gaps
Restore the consumer de-hardcoding that is the point of the seam, and close the
parity gaps an adversarial review (codex) found in the update-context projection.
De-hardcode the repo slug + install command in the changeset tooling — #516
only single-sourced the package NAME, leaving 'open-gsd/get-shit-done-redux'
hardcoded in scripts/changeset/cli.cjs and github-release-notes.cjs. Route both
through the seam's repoSlug/packageName so a rename is a regenerate, not a hand
edit. The drift-lint real scan now reports zero divergent coordinate literals.
Projection parity vs the old inline bash, as ONE consistent rule
(trustedVersionAt) applied on every path:
- expand a leading ~/ in preferredConfigDir before the fast path (the bash ran
expand_home first; a custom --config-dir ~/foo otherwise fell to UNKNOWN)
- trust a version only when BOTH VERSION and the update.md marker exist — fast
path AND LOCAL/GLOBAL cascade; a partial dir falls to 0.0.0 keeping scope
- apply the same same-path dedup to the 0.0.0 fallback so a partial install
probed from cwd===home is not misdetected as LOCAL
Adds regression tests for tilde expansion, VERSION-only (cascade + fast path),
and the cwd===home partial-install dedup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#505): remove dead SDK-shim verification subsystem from bin/install.js
Post-ADR-0174 the @opengsd/gsd-sdk package was retired; sdk/ no longer ships.
installSdkIfNeeded had no callers in the live install flow and its entire
transitive call graph (classifySdkInstall, buildSdkFailFastReport,
renderSdkFailFastReport, buildGsdSdkVersionMismatchReport, readGsdSdkVersion,
parseGsdSdkVersion, findGsdSdkOnPath, isGsdSdkOnPath, isLegacyGsdSdkShim,
filterNpxFromPath, getUserShellPath, getUserShellWindowsPersistentPath,
trySelfLinkGsdSdk, trySelfLinkGsdSdkWindows, buildWindowsShimTriple,
formatSdkPathDiagnostic, renderGsdSdkVersionMismatchReport) was dead code.
Also removed two now-empty test files (no-unconditional-win32-skip.test.cjs,
bug-3020-install-shell-path-probe.test.cjs) that exercised the removed
functions, and added a regression guard (bug-505-remove-dead-sdk-verification.test.cjs).
detectStaleStandaloneSdk and formatStaleStandaloneSdkWarning are deliberately
KEPT — they handle a real leftover-global-SDK condition (#3406).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore: add changeset for #505 dead SDK-shim removal
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#505): restore buildWindowsShimTriple/formatSdkPathDiagnostic projection surfaces
The dead-code removal also deleted buildWindowsShimTriple and
formatSdkPathDiagnostic (plus their imports/exports). These have no
production caller, but they are the install.js side of a projection-contract
drift guard: tests/bug-3441 and tests/bug-3442 assert install.js delegates to
shell-command-projection.cjs rather than hand-rolling the projection. Removing
them broke those tests (TypeError: ... is not a function) — surfaced by the
full/coverage CI matrix, which runs suites the local scoped run skipped.
Restore the two thin wrappers, their `*FromProjection` import aliases, and
their exports. Update the bug-505 guard test to assert they remain exported as
contract surfaces (moved out of the dead-symbol list).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#500): stop state planned-phase corrupting milestone progress.* counters
Two independent defects combined to corrupt STATE.md progress.* on a
plan-phase run:
RC1 — cmdStatePlannedPhase wrote via writeStateMd, which unconditionally
runs syncStateFrontmatter and rebuilds progress.* (total/completed
plans+phases) from a half-planned disk snapshot, trampling curated
counters. It now routes through readModifyWriteStateMd(..., { resync:false }),
the same body-only-write guard state.update uses — per-phase body fields
are updated, milestone progress.* is preserved.
RC2 — isRootPlanFile's loose /PLAN/i fallback matched legacy
`<N>-PLAN-<NN>-SUMMARY.md` names (they contain "PLAN"), double-counting
summaries as plans (a 4-plan/4-summary phase scanned as planCount:8,
completed:false). isRootPlanFile now rejects isRootSummaryFile before the
fallback, so summaries are never counted as plans.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#500): add changeset for planned-phase progress fix
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#501): stop flat "## Phase Details" leaking phases into active milestone
extractCurrentMilestone returned `preamble + currentSection`, where the
preamble (everything before the first milestone heading, only <details>
stripped) could carry a flat "## Phase Details" section listing `### Phase N:`
entries for ALL milestones. Those leaked into the active-milestone scope, so
getMilestonePhaseFilter / buildStateFrontmatter counted the whole project
(e.g. total_phases: 18) instead of the active milestone (14-18).
Fix (maintainer direction: code fix, count + validate-aware):
1. core.cjs extractCurrentMilestone — strip flat phase-detail blocks
(`### Phase N:` heading + body, and a "## Phase Details" heading) from the
preamble. The active milestone's own phases live in currentSection, so this
is safe. Fixes the count with no ROADMAP edits.
2. verify.cjs cmdValidateConsistency + cmdValidateHealth — the "phases on disk
but not in ROADMAP" / W007 checks now compare disk dirs against the FULL
roadmap (every milestone), not the active-milestone scope. Without this,
narrowing the scope would flag every shipped phase dir as a spurious orphan
(the documented side effect of the <details> workaround).
Tests reproduce the real layout (flat Phase Details before milestones) and
assert: state json total_phases counts only active phases; validate
consistency and validate health (W007) do not flag shipped phase dirs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#501): add changeset for flat Phase Details milestone leak fix
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#503): detect local Antigravity (.agent) installs in /gsd:update
The installer places local Antigravity installs in ./.agent/
(bin/install.js getDirName('antigravity') === '.agent'), but the
/gsd:update detection cascade in update.md only knew the global
Antigravity layout (.gemini/antigravity{,-ide,-cli}). A local .agent
install fell through to the `Otherwise -> claude` default, so the
update refreshed Claude artifacts instead of the Antigravity install.
Add `.agent` -> antigravity to all four runtime-dir surfaces in
update.md: the execution_context path classifier, the RUNTIME_DIRS
candidate array, the local-scope discovery loop, and the post-update
cache-clear loop (the last otherwise left a stale update indicator on
local Antigravity installs).
Note: the issue also cited update-context.cjs, which does not exist on
`next` (it is introduced by the still-open PR #499). The same .agent
fix should be carried into update-context.cjs when #499 lands.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(#503): add changeset for Antigravity .agent detection fix
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The gsd_run resolver preamble now probes
<repo-root>/.claude/get-shit-done/bin/${_GSD_SHIM_NAME} as the second
check — immediately after the existing get-shit-done/bin/ check and
before command -v / $HOME/.claude fallbacks. This covers the install
layout produced by npx @opengsd/get-shit-done-redux@latest --claude --local.
A _GSD_RUNTIME_ROOT variable is introduced to bind the repo-root
expression once and reuse it for both checks without repeating the
git rev-parse subshell.
76 workflow files regenerated via node scripts/sync-runtime-launcher.cjs.
All parity, size-budget, and new regression tests pass.
Co-authored-by: CI Rebase Check <ci@gsd-redux>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract a pure `parseConfigDirFromArgs(argsArray)` seam from the
closure-based `parseConfigDirArg()` and fix the equals-form parser to
use `slice(indexOf('=') + 1)` instead of `split('=')[1]`, so that
paths like `/tmp/gsd=a` or `/tmp/a=b=c` are preserved in full.
Both `--config-dir=<path>` and `-c=<path>` are fixed. The pure seam
is exported via `module.exports` so the 12-case unit test can assert
on typed return values without spawning a child process.
Co-authored-by: CI Rebase Check <ci@gsd-redux>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* enh: bump opus-tier model IDs to current GA (Opus 4.8 / codex gpt-5.5)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(#466): changeset for opus-tier model-ID refresh
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: CI Rebase Check <ci@gsd-redux>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(#455): implement typed surfaces to retire grep tests
Production surfaces added:
- hooks/managed-hooks-registry.cjs: new CJS module exporting MANAGED_HOOKS
as a typed array; gsd-check-update-worker.js now requires it instead of
declaring an inline array
- bin/install.js: elevate inline gsdHooks to module-level GSD_UNINSTALL_HOOKS,
export it alongside runtimeMap/allRuntimes (already exported)
- scripts/build-hooks.js: export HOOKS_TO_COPY; guard build() behind
require.main===module so tests can require the file without triggering a build
- get-shit-done/bin/lib/init.cjs: add --json mode to agent-skills command,
emitting typed IR { agent_type, block, skills_count } for test assertions
- get-shit-done/bin/gsd-tools.cjs: wire --json flag for agent-skills dispatch
Category-B source-grep migrations:
- tests/managed-hooks.test.cjs: require MANAGED_HOOKS from registry, drop fs.readFileSync+regex
- tests/orphaned-hooks.test.cjs: require MANAGED_HOOKS+HOOKS_TO_COPY as typed exports
- tests/hooks-opt-in.test.cjs: replace gsdHooks regex-parse with GSD_UNINSTALL_HOOKS import
- tests/install-minimal-hooks.test.cjs: replace gsdHooks regex-parse with GSD_UNINSTALL_HOOKS
- tests/copilot-install.test.cjs: replace src.includes() checks with typed
assertions on runtimeMap, allRuntimes, parseRuntimeInput, buildRuntimePromptText
- tests/agent-skills.test.cjs: migrate to --json typed IR assertions
pending-migration-to-typed-ir token cleared (87 of 87 files):
- 78 files already had source-text-is-the-product; removed duplicate token
- 5 files already used typed assertions; reclassified or annotated
- 4 files required individual reclassification to source-text-is-the-product
or architectural-invariant
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(#455): update workflow-guard test to typed GSD_UNINSTALL_HOOKS import; isolate HOME in runtime-launcher (D) test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(#455): guard install.js main() behind require.main===module so the typed export is require-safe
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(#455): document --json typed surfaces for agent-skills, progress, validate context
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(#455): add changeset fragment for new --json surfaces
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(#455): complete grep migration for files flagged by lint-tests
The branch commit 4e630d99 stripped `allow-test-rule: pending-migration-to-typed-ir`
from ~80 test files without replacing their assertions or adding the correct
exemption annotation. The files were NOT source-grep tests — they read .md
workflow/agent/command/reference files (source-text-is-the-product) or hook
source files for structural invariants (structural-regression-guard). No
assertion logic was changed; only the correct allow-test-rule annotation was
added to each file per CONTRIBUTING.md exception matrix.
73 files: `source-text-is-the-product` — workflow/agent/command/reference .md
7 files: `structural-regression-guard` — hook .js / bin/install.js structural checks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: CI Rebase Check <ci@gsd-redux>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(#443): RED unified effort + fast_mode + resolve-execution
All 68 tests failing as expected — no implementation yet.
Covers: effort cascade (tier defaults, overrides, invalid fallthrough),
fast_mode cascade (boolean-only, tier defaults), resolveEffortForTier
escalation, renderEffortForRuntime clamping, resolve-execution CLI,
config schema new keys, QA hostile-input matrix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(#443): unified cross-provider effort + fast_mode knobs and resolve-execution query
Adds config-driven effort control (universal ladder: minimal<low<medium<high<xhigh<max)
and fast_mode propagation knobs, with per-runtime rendering that clamps the unique
tail values (max=Anthropic-only clamps to xhigh on Codex; minimal=Codex-only clamps
to low on Claude).
Key changes:
- config-schema.manifest.json: add effort.default, fast_mode.enabled as validKeys;
add 4 dynamicKeyPatterns for effort.routing_tier_defaults, effort.agent_overrides,
fast_mode.routing_tier_defaults, fast_mode.agent_overrides; fix stale _comment
- config-defaults.manifest.json: add effort and fast_mode blocks with tier defaults
- model-catalog.cjs: add EFFORT_RENDERING map, renderEffortForRuntime(), RUNTIMES_WITH_FAST_MODE
- model-profiles.cjs: re-export new catalog exports
- core.cjs: add resolveEffortInternal, resolveFastModeInternal, resolveEffortForTier,
VALID_EFFORTS, EFFORT_SET, nextEffort; pass effort/fast_mode through loadConfig
- commands.cjs: replace reasoning_effort in cmdResolveModel with unified effort;
add cmdResolveExecution (superset command with effort_rendered, effort_param,
effort_propagation, fast_mode, fast_mode_supported)
- gsd-tools.cjs: add resolve-execution case with --effort/--fast-mode/--attempt flags
- tests/feat-443: 69 tests covering cascade, rendering, escalation, CLI, schema, QA matrix
- tests/commands.test.cjs: convert 3 reasoning_effort assertions to unified effort
- docs/CONFIGURATION.md: document effort + fast_mode + resolve-execution sections
- settings-advanced.md: list new effort/fast_mode keys in confirmation table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(#443): remove dead catalog effort lane; unify codex effort through renderEffortForRuntime
- Remove resolveReasoningEffortInternal (catalog-driven effort function) from
core.cjs and its export; remove from commands.cjs destructure import
- Convert tests/issue-2517-runtime-aware-profiles.test.cjs: all 11 effort
assertions now use resolveEffortInternal + renderEffortForRuntime; Claude
effort is first-class (output_config.effort); unknown runtimes assert param===null
- Convert tests/feat-3023-model-phase-types.test.cjs: replace the entire
resolveReasoningEffortInternal describe with unified effort assertions;
effort derives from AGENT_DEFAULT_TIERS routing tier, not phase-type tier
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(#443): ADR for unified cross-provider effort + fast-mode routing
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(#443): architecture-level QA invariants + test-strategy doc
Add 48-test integration suite (feat-443-effort-fast-mode.integration.test.cjs)
covering 8 architectural invariants: cross-provider validity (never emit a value
the real API would 400 on), param/channel contract stability, resolve-execution
JSON contract (all 8 keys + correct types), totality across the full 33-agent
registry, fast-mode honesty (claude always fast_mode_supported=false), precedence
first-valid-wins matrix for both effort and fast_mode cascades, dynamic-routing
composition (effort escalation independent of model tier), and config-set round-trip
for all new effort/* and fast_mode/* key namespaces. Append test-strategy section
with invariant rationale and E2E gap documentation to docs/TESTING-SUITES.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(#443): add failing install-wiring tests for effort per-runtime injection (RED)
TDD RED: 10 failing tests covering:
- Claude .md gets effort: injected per tier (planner=xhigh, mapper=low, executor=high)
- Gemini .md does NOT get effort: (already passing — Gemini-safe)
- Codex .toml gets model_reasoning_effort via unified resolver
- Config-driven: effort.agent_overrides drives both Claude .md and Codex .toml
- Source purity: agents/*.md have no effort: key (already passing)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(#443): wire effort per-runtime at install (Claude .md frontmatter + Codex .toml unified)
- Import AGENT_DEFAULT_TIERS and renderEffortForRuntime from model-catalog.cjs
- Add readGsdEffectiveEffortConfig(targetDir): reads merged effort config from
.planning/config.json (per-project wins) + ~/.gsd/defaults.json (global fallback),
same probe pattern as readGsdRuntimeProfileResolver
- Add resolveInstallTimeEffort(effortCfg, agentName): pure function matching
resolveEffortInternal() precedence (agent_overrides > routing_tier_defaults > default > 'high')
without loadConfig side-effects (no sub-repo detection, no migration writes)
- Claude agent copy loop: inject `effort: <value>` into frontmatter ONLY for
runtime === 'claude'; all other .md runtimes (Gemini, Qwen, Hermes, etc.) stay
effort-free (Gemini-safe source contract preserved in agents/*.md)
- generateCodexAgentToml: add effortCfg param; emit model_reasoning_effort from
unified resolver (replaces old catalog entry.reasoning_effort); Codex clamps
max → xhigh via renderEffortForRuntime('codex', ...)
- installCodexConfig: pass readGsdEffectiveEffortConfig(targetDir) to
generateCodexAgentToml so per-project config wins for Codex .toml too
- Update failing tests to GREEN: 12/12 pass; all 17 install tests pass;
2847/2848 unit tests pass (1 pre-existing failure: policy-shell-pinning)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(#443): source install effort defaults from manifest (kill drift) + guard test
Replace hardcoded _GSD_EFFORT_MANIFEST_TIER_DEFAULTS and the 'high' fallback in
resolveInstallTimeEffort with values read from config-defaults.manifest.json at
module init, using the same __dirname-relative path install.js already uses for
all shared manifests. Add feat-443-effort-defaults-drift.test.cjs to assert
equality between install.js's runtime constants and the manifest on every CI run.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#443): reconcile Codex TOML tests with unified effort design
The #443 unified effort resolver makes generateCodexAgentToml always emit
model_reasoning_effort (driven by resolveInstallTimeEffort, not model_profile_overrides).
The test 'generated TOML omits reasoning_effort when runtime has none' had an
obsolete premise — model_profile_overrides.reasoning_effort:'' no longer suppresses
unified effort. Convert it to assert the new invariant: Codex TOML always carries a
valid model_reasoning_effort from the agent's routing tier (xhigh for gsd-planner,
a heavy-tier agent), while model_profile_overrides model override is still respected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#443): make install.js effort resolution lazy (no load-time side effects breaking launcher-parity)
Replace module-load-time IIFE + hard throw (config-defaults.manifest.json read)
and top-level require of model-catalog.cjs with a lazy _getGsdEffortCatalog()
getter that initialises on first call from resolveInstallTimeEffort /
generateCodexAgentToml / Claude .md effort injection. Requiring install.js in
unrelated test contexts (e.g. runtime-launcher-parity) no longer triggers
manifest IO or throws, eliminating the load-time side effect that changed
subprocess exit codes / stderr on the bench.
Drift-guard exports (_GSD_EFFORT_MANIFEST_TIER_DEFAULTS / _GSD_EFFORT_MANIFEST_DEFAULT)
preserved as lazy getter properties on module.exports so feat-443-effort-defaults-drift
still validates them without forcing eager load.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#443): isolate install-wiring test HOME to stop \$HOME/.claude pollution breaking launcher-parity
runGlobalInstall() now redirects HOME to a per-call isolated tmpdir in addition
to the existing runtime-specific env-var redirects (CLAUDE_CONFIG_DIR,
GEMINI_CONFIG_DIR, CODEX_HOME). This ensures install.js code that uses
os.homedir() directly — including the ~/.cache/gsd update-check deletion,
~/.gsd/defaults.json reads, and any HOME-relative npm subprocess writes —
never touches the real \$HOME during the test.
Without the HOME isolation the install test (which is new to this branch and
is now picked up by Docker's raw \`tests/*.test.cjs\` glob) could write or
delete files under the real \$HOME, causing runtime-launcher-parity test (D)
to fail: (D) asserts a loud non-zero exit when \$RUNTIME_DIR/gsd-tools.cjs is
absent and gsd-tools is not on PATH, but the launcher's \$HOME/.claude fallback
arm succeeds if \$HOME/.claude/get-shit-done/bin/gsd-tools.cjs exists.
Also sets GSD_SKIP_STALE_SDK_CHECK=1 to suppress the \`npm ls -g\` subprocess
that the global installer spawns — irrelevant to effort-wiring assertions,
slow, and potentially writes to ~/.npm cache.
All 12 feat-443 install-wiring assertions preserved. Drift-guard 5/5. Unit
suite 2848/2850 (pre-existing policy-shell-pinning.test.cjs failure on next).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(#443): add changeset fragment for effort + fast-mode routing
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(#443): set GSD_TEST_MODE before requiring install.js in drift-guard test to prevent HOME leak
Without GSD_TEST_MODE=1, require('bin/install.js') runs the module's main
install block (guarded by !GSD_TEST_MODE), performing a real global Claude
install into $HOME/.claude/. On CI ubuntu where node is on standard PATH,
the launcher's $HOME/.claude fallback arm then finds gsd-tools.cjs, causing
runtime-launcher-parity test (D) to exit zero when it must exit non-zero.
Root cause: feat-443-effort-defaults-drift.test.cjs (unit suite) runs
alphabetically before runtime-launcher-parity.test.cjs in the same node
--test invocation. Each runs in a separate worker process but shares the
same HOME. The drift test's install leaks gsd-tools.cjs into that HOME,
then the launcher test's bash subprocess finds it via the $HOME/.claude arm.
Fix: add process.env.GSD_TEST_MODE = '1' at the top of the drift-guard
test, before the require(installPath) call. This matches the pattern used
by feat-443-effort-fast-mode.test.cjs and feat-443-effort-install-wiring
.install.test.cjs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#443): deterministic resolve-execution arg parsing + validate install-time effort (Codex adversarial findings)
Finding 1: resolve-execution --effort low gsd-planner misrouted 'low' as the agent.
Replace find(non-dash) with a proper flag-consuming loop that collects a single
positional; validate missing/extra positionals and malformed --attempt values.
Finding 2: resolveInstallTimeEffort returned unvalidated effort strings (e.g. "ultra")
verbatim. Each precedence layer now checks GSD_EFFORT_SET (imported once from
core.cjs) before accepting a value, mirroring resolveEffortInternal exactly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#443): newline-agnostic effort frontmatter injection (Windows CRLF) + CRLF-safe assertions
Extracts injectEffortFrontmatter(content, effortValue) pure helper that detects
EOL (LF vs CRLF) from the opening '---' line and inserts 'effort: <value>'
before the closing '---' delimiter using the same EOL as the surrounding
frontmatter. Regex now uses /^---\r?\n([\s\S]*?)^---\r?$/m instead of the
LF-only /^(---\n[\s\S]*?)(---)(\n|$)/ that silently skipped CRLF files on
Windows (git core.autocrlf=true checkout).
Also adds 7 unit tests covering LF, CRLF, idempotency, no-frontmatter, and
complex frontmatter cases. Exports injectEffortFrontmatter from module.exports.
Fixes 6 CI failures in tests/feat-443-effort-install-wiring.install.test.cjs
on windows-latest runners (lines 138, 145, 152, 261, 345, 356).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: CI Rebase Check <ci@gsd-redux>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends the canonical runtime-launcher snippet with a third resolution arm
that probes $HOME/.claude/get-shit-done/bin/${_GSD_SHIM_NAME} between the
PATH check and the hard-error exit. Global Claude-Code installs (--claude
without --local) with no PATH wiring and no RUNTIME_DIR no longer hit the
hard-error path.
Resolution order: local/RUNTIME_DIR -> PATH -> ~/.claude/... -> hard error.
Propagated to 76 workflow .md files via sync-runtime-launcher.cjs. Parity
test (G) and bug-211 regression test (4 assertions) added.
* fix(#376): rewrite /gsd: → /gsd- in Claude-installed hook .js files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#376): preserve .sh branch + {{GSD_VERSION}} stamp in restructured hook-copy loop
Trim the .js branch comment/whitespace so the `else {` and
`entry.endsWith('.sh')` fall within the 1500/2000-char assertion windows
anchored on `configDirReplacement` in the regression tests for #1834 and
#2136. The .sh read+substitute+chmod path is intact; the new #376 hyphen-
namespace rewrite for .js/.cjs files is also preserved.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces KNOWN_TEMPLATE_DEFAULTS and KNOWN_STATUS_PATTERNS in state-document.cjs
to enumerate every string a GSD handler writes. stateReplaceFieldIfTemplate
consults this table and only replaces the field when the existing value is a known
template default (or absent) — executor-authored values are left untouched.
Wire-in:
- record-session: Resume File now only overwritten when caller passes --resume-file
OR existing value is 'None'. Router no longer defaults resume_file to 'None'
before calling the handler.
- advance-plan (both branches): Status and Last Activity guarded via
stateReplaceFieldIfTemplate.
- updateCurrentPositionFields: Status and Last activity in the Current Position
section guarded; bare ISO date shape is the trigger for replacement, prose
narrative is preserved.
- planned-phase: Status and Last Activity guarded the same way.
Regression tests (7 cases) in tests/bug-397-state-preserve-executor-authored.test.cjs
cover each data-loss shape; all 106 existing state.test.cjs tests still pass.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove `DEFAULT_SMOKE_TESTS` and `WINDOWS_SMOKE_TESTS` constants (now dead after the unconditional injection block is dropped)
- Drop the `addAll(targeted, DEFAULT_SMOKE_TESTS)` / `addAll(windows, WINDOWS_SMOKE_TESTS)` block from the `codeChanged` branch
- When `codeChanged && targetedTests.length === 0`, push `'unit'` as the fallback suite token
- Two new regression tests in `tests/ci-test-scope.test.cjs` covering the no-injection and unit-fallback contracts (bug #408)
* fix(#416): return null when active milestone has no archive (no fall-through to prior milestone's archive)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(#416): handle bold-formatted Milestone: field in archive dir resolver
The STATE.md regex in getActiveMilestoneArchiveDir failed to extract the
version from **Milestone:** vX.Y format (bold wraps the label+colon).
The old pattern captured '**' instead of the version, causing the
milestone→archive lookup to produce a false candidate path, then return
null (post-fix behavior) instead of falling through to the version-sort
fallback — breaking the #3164 consistency scanner tests.
Fix: extend the regex to skip optional trailing '**' after the colon so
both 'milestone: vX.Y' and '**Milestone:** vX.Y' parse correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
cmdWebsearch called fetch() with no timeout and no retry, so a hung
connection blocked indefinitely and transient 429/5xx/network failures
were not recovered. Add AbortSignal.timeout (configurable via
GSD_WEBSEARCH_TIMEOUT_MS, default 10s) and a bounded retry loop
(max 2 retries, exponential backoff + jitter) for 429/5xx/network
errors, honoring Retry-After on 429 (capped at 60s). Non-429 4xx fail
immediately (no wasted retries). Transient-exhausted failures report an
`attempts` count. Worst-case time is bounded by timeout*(1+retries)+backoff.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The update worker queried the unscoped 'get-shit-done-redux' via
`npm view`, which returns E404 — so `latest` stayed null and
`update_available` could never become true. Now derives the name from
package.json (`require('../package.json').name`) so it always matches
the actual published scoped name (@opengsd/get-shit-done-redux).
Fixes#378.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
config-get calls lacked --default, causing stderr noise ("Key not found") and a fragile empty-variable fallback when workflow.nyquist_validation was absent; added --default true (matching schema default). Fixes#138.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>