* test: cover Windows hook shell drift for Claude (#3413)
* fix: scope Windows hook syntax to Gemini runtime (#3413)
* docs: add changeset for #3413
* docs: set changeset pr for #3413
* fix: route Windows hook formatting through runtime-aware projection seam
* test: cover runtime projection edge cases for Windows hooks
* fix(docs): add shell-command-projection to inventory parity
* docs: align CLI module shipped count after rebase
* fix(phase): parse remove --force regardless of position
* chore(changeset): add fragment for phase-remove flag fix
* fix(phase): fail when phase.remove target is missing
* test(3309): red — workflow.human_verify_mode contract
New behavioral test file covers:
- workflow.human_verify_mode is a recognized config key (VALID_CONFIG_KEYS)
- defaults to 'mid-flight' (preserves current behavior)
- config-set / config-get round-trips for both values
- persists in config.json as string
- planner agent file references the flag with canonical wording, couples
end-of-phase mode with the rule that checkpoint:human-verify is not
emitted, and documents the <verify><human-check> deferred-item shape
- verifier agent file references harvesting <verify><human-check> blocks
- references/checkpoints.md documents the cost-control alternative
Source-text assertions on agent .md files are exempted via
allow-test-rule: source-text-is-the-product — those files ARE the
runtime contract loaded by AI runtimes, so asserting their wording is
the only way to verify the agents will respect the flag.
Fails 10/11 against current source. Will pass after the fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(3309): add workflow.human_verify_mode = end-of-phase opt-out
Each mid-flight checkpoint:human-verify halt costs a full executor
cold-start (CLAUDE.md, MEMORY.md, STATE.md, plan re-read on every
respawn) because subagent context is discarded across the pause. A plan
with N human-verify checkpoints pays the cold-start cost N+1 times. The
reporter (rentanything-nb) measured this at "tens of thousands of tokens"
per round-trip and "hundreds of thousands per week."
This adds workflow.human_verify_mode (default 'mid-flight') with an
'end-of-phase' value that:
- instructs gsd-planner to NOT emit <task type="checkpoint:human-verify">
tasks; verification details go into a <verify><human-check> sub-block
on the relevant auto task instead
- instructs gsd-verifier (Step 8) to harvest those <verify><human-check>
blocks at end-of-phase and merge them into its own human-verification
list
- the existing human_needed → HUMAN-UAT.md flow in execute-phase.md is
the single sink — no new file/writer is created
checkpoint:decision and checkpoint:human-action are unaffected — those
gate the work itself, not post-hoc verification.
Surfaces touched:
- bin/lib/config-schema.cjs, bin/lib/config.cjs — register key + default
- sdk/src/config.ts, sdk/src/query/config-schema.ts — SDK parity
- agents/gsd-planner.md — slim Detection section + reference link
- agents/gsd-verifier.md — Step 8 harvest instruction
- get-shit-done/references/planner-human-verify-mode.md — full rules,
loaded conditionally to keep planner.md under its size budget
- get-shit-done/references/checkpoints.md — surface the alternative
- docs/CONFIGURATION.md — config table row
- docs/INVENTORY.md, docs/INVENTORY-MANIFEST.json — track new reference
Tag name <human-check> chosen instead of <human> to avoid the
prompt-injection scan pattern that flags <system|assistant|human> tags.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(3309): align changeset pr: to actual PR number
The pr: field was authored as 3319 (a guess at the next number) before
the PR was opened. Actual PR is #3325.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(3309): flip workflow.human_verify_mode default to end-of-phase
Per maintainer direction on PR #3325, end-of-phase is the new project
default. Mid-flight checkpoint:human-verify halts cost a full executor
cold-start (CLAUDE.md, MEMORY.md, STATE.md, plan re-read on respawn) per
round-trip — reported at "tens of thousands of tokens" per round-trip,
"hundreds of thousands per week" on real projects. The cost-control
mode is what new projects should get out of the box.
mid-flight remains a one-line opt-back-in via:
gsd config-set workflow.human_verify_mode mid-flight
Behavior change for existing projects: the new default takes effect
when .planning/config.json is rewritten (config-set, fresh project).
Existing in-flight PLAN.md files with checkpoint:human-verify tasks
continue to work in either mode — the flag only changes what the
planner emits next time it runs.
Surfaces updated:
- bin/lib/config.cjs, sdk/src/config.ts — default flipped
- sdk/src/config.ts docstring — describes new default + opt-back-in
- agents/gsd-planner.md — Detection section explains new default
- references/planner-human-verify-mode.md — reordered modes; added
guidance on when to opt back into mid-flight
- references/checkpoints.md — surface the default flip and the why
- docs/CONFIGURATION.md — table row reflects new default + reason
- tests/feat-3309-human-verify-mode.test.cjs — default test asserts
end-of-phase
- .changeset/fierce-geese-march.md — describes the default flip and
the migration semantics
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: address human verify mode review
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: reconcile planner action contract
The deep_work_rules workflow block was stronger than the planner agent contract: it required verbatim context copies and self-sufficient action text, so planners were incentivized to inline implementation code. Bound action content to directive prose with concrete identifiers, allow behavior/test acceptance criteria, and pin the cross-file contract with a regression test. Closes#3320.
* chore: add changeset for planner contract fix
* refactor: tighten sdk-first architecture seams
Refs #3312
* refactor: finish state document seam cleanup
Refs #3312
* test: harden minimal install cleanup assertion
* ci: support sdk-scoped package lock
* fix(3316): restore root package-lock.json and align changeset pr ref
Reverts dec57a83 ("ci: support sdk-scoped package lock") and restores
the root package-lock.json that c249d34d deleted. The deletion was the
wrong direction:
- The root package.json declares its own runtime and dev deps
(@anthropic-ai/claude-agent-sdk, ws, c8). Without a root lockfile,
`npm install --no-package-lock` resolves whatever satisfies semver at
install time — CI today and CI in six months can install different
transitive trees, defeating reproducibility.
- The lockfile has been part of every release on this repo (long
history on main); removing it loses the npm audit / Dependabot
target without compensating benefit.
- The CI workaround pattern (cache-dependency-path: sdk/package-lock.json
+ `npm install --no-package-lock`) papered over the symptom rather
than fix the cause.
Also fix the changeset pr: from 3312 (issue) to 3316 (PR). CONTEXT.md
flags this exact failure mode as a recurring CodeRabbit finding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: address coderabbit review findings
* fix: close remaining coderabbit threads
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes#3310
Wires `ERROR_REASON.SDK_UNKNOWN_COMMAND` and `ERROR_REASON.USAGE` into the remaining untyped error paths in `gsd-tools.cjs` (template, frontmatter, requirements, milestone, uat, todo, workstream, graphify, learnings subcommand routers and `--cwd`/missing-required-arg paths). Tests assert via `JSON.parse(stderr).reason` rather than substring matching on prose. Closure regression guard locks the canonical `{ok, reason, message}` shape for every newly-typed path.
Also moves `--json-errors` activation up to the top of `main()` so `--cwd` validation paths emit JSON rather than plain text when `GSD_JSON_ERRORS=1` is set.
* test(phase-dir): add red test for k015 prefix-drift in plan-milestone-gaps and import workflows (#3298)
Asserts that:
- plan-milestone-gaps.md step 8 does not use bare {NN}-{name} mkdir pattern
- plan-milestone-gaps.md step 8 uses phase.add or expected_phase_dir
- import.md plan_convert does not use bare {NN}-{slug} mkdir pattern
- import.md plan_convert uses expected_phase_dir from init.phase-op
These 5 tests are RED until the fix lands.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(phase-dir): add projectCode prefix to phase-dir construction in plan-milestone-gaps and import workflows (#3298)
Both plan-milestone-gaps.md step 8 and import.md plan_convert step were
constructing phase directories using raw {NN}-{name}/{NN}-{slug} template
patterns, bypassing the project_code prefix from .planning/config.json.
Fix: both steps now call `gsd-sdk query init.phase-op <N>` and consume the
`expected_phase_dir` field (which includes the `<CODE>-<NN>-<slug>` prefix
when project_code is set), matching the pattern already established by
PR #3292 for /gsd-discuss-phase and /gsd-plan-phase.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(phase-dir): apply project_code prefix to backlog phase dir in add-backlog workflow (k015 sibling, #3298)
Sibling k015 audit found a third drift site: add-backlog.md step 4 was
constructing the 999.x backlog phase directory using raw ${NEXT}-${SLUG}
without applying the project_code prefix from .planning/config.json.
Fix: read project_code via `gsd-sdk query config-get project_code --raw`
and prepend `${CODE}-` when set, matching the pattern used by phase.insert
(which already applies project_code to decimal phases in phase.cjs line 736).
Also extends bug-3298 regression test to cover this third site.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(changeset): add changeset for #3298 phase-dir prefix drift fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(changeset): set pr: 3306 in changeset for #3298; resolve stash conflict in live-command-registry.cjs
The conflict was cosmetic (string concat → template literals) introduced by
accidental git stash during test verification. Taking the newer template-literal
form throughout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(3251): red — assert 14 missing commands in command-aliases.generated.cjs
Parametrized test covering all 14 commands from issue #3251. Requires the
CJS manifest and asserts structurally (never greps source). Currently fails
because NON_FAMILY_COMMAND_ALIASES is not exported and all 14 are missing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(3251): add 14 missing commands to command-aliases.generated.cjs
Adds NON_FAMILY_COMMAND_ALIASES export to command-aliases.generated.cjs and
extends sdk/src/query/command-manifest.non-family.ts with the 10 commands that
were registered in static catalogs but absent from the manifest source-of-truth:
- check.decision-coverage-plan / check.decision-coverage-verify
- frontmatter.get
- phase.mvp-mode
- progress.bar
- stats.json
- task.is-behavior-adding
- todo.match-phase
- uat.render-checkpoint
- workstream.list
The other 4 (frontmatter.set, learnings.copy, milestone.complete,
requirements.mark-complete) were already in non-family.ts but unexported.
Generator (sdk/scripts/gen-command-aliases.ts) now produces both the TS and CJS
artifacts including the non-family section, sorted by canonical for determinism.
Freshness check (sdk/scripts/check-command-aliases-fresh.mjs) verifies TS and CJS
non-family parity against the manifest source-of-truth.
Fixes#3251
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(3251): add changeset and CHANGELOG entry for PR #3305
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(gen-command-aliases): preserve TS type interfaces and annotations on regen (#3251)
- Add FamilyCommandAlias and NonFamilyCommandAlias interface declarations to tsBody so regen never strips them
- Replace JSON.stringify with single-line compact serialisers for TS output (matches committed file format)
- Apply typed annotations (readonly FamilyCommandAlias[] / readonly NonFamilyCommandAlias[]) to all exported TS constants
- CJS path unchanged: remains untyped pure-JS with JSON.stringify multi-line format
- Regenerate sdk/src/query/command-aliases.generated.ts to sync with updated generator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: drop redundant CHANGELOG.md edit (use .changeset/ fragment per CONTRIBUTING.md)
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(3255): add red/green tests for --json-errors structured error mode
Ten tests covering the --json-errors mode contract:
- Unknown command → sdk_unknown_command
- Dotted unknown command → sdk_unknown_command
- Missing --pick value → usage
- Config key not found → config_key_not_found
- Unknown subcommand → sdk_unknown_command
- GSD_JSON_ERRORS=1 env var activation
- Successful command unaffected
- Stable error shape ({ok, reason, message})
- Single error line per invocation
- Unknown flag → usage
All assertions use JSON.parse on stderr captures, never .includes() on
text (#2974 / CONTRIBUTING.md "Prohibited: Raw Text Matching" rule).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(3255): add typed ERROR_REASON codes and GSD_JSON_ERRORS env var support
- Destructure ERROR_REASON from core in gsd-tools.cjs
- Add GSD_JSON_ERRORS=1 env var as alternative to --json-errors CLI flag
- Pass ERROR_REASON.SDK_UNKNOWN_COMMAND to unknown top-level command default path
- Pass ERROR_REASON.SDK_UNKNOWN_COMMAND to unknown intel subcommand path
- Pass ERROR_REASON.USAGE to --pick missing value error path
- Pass ERROR_REASON.USAGE to --version flag rejection path
All ten tests in feat-3255-json-errors-mode.test.cjs pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(3255): add json-errors taxonomy doc, changeset, and CHANGELOG entry
- docs/json-errors.md: full error code taxonomy, wire format spec, and
test-authoring guidelines for the --json-errors mode
- .changeset/gentle-tigers-roar.md: changeset fragment (pr will be updated
after PR is opened)
- CHANGELOG.md: Unreleased → Added entry for the new structured error mode
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: update changeset PR number to 3304
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(gsd-tools): document --json-errors in usage/help text (#3255)
Add [--json-errors] to the TOP_LEVEL_USAGE synopsis line and introduce a
"Global flags:" section describing all four global flags (--raw, --pick,
--cwd, --ws) plus --json-errors with its GSD_JSON_ERRORS=1 env-var
alternative, so operators can discover the flag via `gsd-tools --help`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: drop redundant CHANGELOG.md edit (use .changeset/ fragment per CONTRIBUTING.md)
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(adr): add docs/adr/README.md index and structural ADR test (#3271)
- Add docs/adr/README.md as an indexed entry point linking all 7 ADRs
- Add tests/enh-3271-sdk-adr-structure.test.cjs: structural assertions that
ADR 0005 and 0006 exist, have required headings and Status/Date metadata,
and that README links every ADR file by filename
- Update CHANGELOG.md with Enhancement entry
- Add .changeset/3271-sdk-adr-structure.md
ADRs 0005 (SDK architecture seam-map) and 0006 (planning-path projection
module) already landed on main. This PR completes issue #3271 by adding the
README index and the structural test gate that enforces ADR completeness
going forward.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: set changeset pr: 3302
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(test): exclude self-reference from ADR 0005 cross-ref count (#3271)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: drop redundant CHANGELOG.md edit (use .changeset/ fragment per CONTRIBUTING.md)
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(phase-plans): red — shared scanPhasePlans contract + parity across call sites (#3262)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(phase-plans): extract shared scanPhasePlans helper (k014) (#3262)
Eliminates four divergent copies of the plan-scan algorithm:
- roadmap.cjs:countPhasePlansAndSummaries (root call site)
- state.cjs:buildStateFrontmatter (1 of 3)
- state.cjs:cmdStateValidate (2 of 3)
- state.cjs:cmdStateSync (3 of 3)
- init.cjs:listPhasePlanFiles / listPhaseSummaryFiles
New bin/lib/plan-scan.cjs exports scanPhasePlans(phaseDir) → {
planCount, summaryCount, completed, hasNestedPlans,
planFiles, summaryFiles
}
Divergences resolved:
- roadmap.cjs used a broad isPlanFile (any .md containing PLAN in name,
matching the extended layout 5-PLAN-01-setup.md); canonical helper
adopts this wider pattern as the reference implementation.
- state.cjs used a strict endsWith(-PLAN.md) filter, missing extended-
layout root files; now unified with roadmap.cjs semantics.
- init.cjs listPhasePlanFiles used ^PLAN-\d+ for nested, missing
the -PLAN-\d+ variant state.cjs also matched; helper includes both.
- pre-bounce exclusion broadened to /.pre-bounce.md$/i (any pre-bounce
file), not just -PLAN.*\.pre-bounce\.md (roadmap form) or flat
.pre-bounce.md (state form).
- OUTLINE exclusion broadened to /-OUTLINE\.md$/i to catch both
flat (-PLAN-OUTLINE.md) and nested (PLAN-01-OUTLINE.md) forms.
Sibling audit: no 5th call site found. phase.cjs:looksLikePlanFile is
a diagnostic probe for non-canonical naming (not a counter) — left
in place per its distinct purpose.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(changelog): add entry for #3262 scanPhasePlans extraction
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(changeset): add changeset fragment for #3262
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(inventory): add plan-scan.cjs row to INVENTORY.md CLI Modules table (#3262)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(3262): update bug-3128 test + INVENTORY counts for plan-scan.cjs
- Update tests/bug-3128-roadmap-plan-count-slug-layout.test.cjs to verify
that roadmap.cjs delegates to plan-scan.cjs (require check) and that the
extended filter lives in plan-scan.cjs as isRootPlanFile with /PLAN/i
- Bump docs/INVENTORY.md CLI Modules headline from 46 to 47 (plan-scan.cjs)
- Regenerate docs/INVENTORY-MANIFEST.json to include cli_modules/plan-scan.cjs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(3262): migrate two missed call sites to scanPhasePlans (k014)
- init.cjs cmdInitExecutePhase: replace inline /-PLAN\.md$/i filter
with listPhasePlanFiles(path) to honour nested, extended-layout, OUTLINE
and pre-bounce exclusions (CR finding)
- state.cjs cmdStateUpdateProgress: replace dual /-PLAN\.md$/i and
/-SUMMARY\.md$/i filters with scanPhasePlans() so the progress-bar
body field uses the same counts as buildStateFrontmatter frontmatter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(3262): correct INVENTORY-MANIFEST.json to tracked files only
Remove 3 untracked local entries from cli_modules so the manifest matches
what CI sees (47 tracked .cjs files, not 50 local). Previous regeneration
ran against the local filesystem which included cjs-command-router-adapter.cjs,
state-document.cjs, and workstream-inventory.cjs (all untracked on this branch).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(3232): add contributor-standards.md (CONTEXT.md + ADR + AI-agent pillars)
Codifies contributor expectations around the three pillars called out in
issue #3232: CONTEXT.md format and governance, ADR naming/status/amendment
conventions, and AI-agent-assisted work requirements (worktree isolation,
TDD discipline, adversarial review, CR-loop). Updates CONTRIBUTING.md to
link the new doc and adds an AI-agent bullet to the architecture-standards
summary. Structural test asserts all three pillars and the CONTRIBUTING.md
cross-link exist.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: add changeset for PR #3301 (contributor-standards)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: fix worktree example to use generic branch name placeholder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(docs): add lang tags to fenced code blocks (MD040) (#3232)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: gsd-intel-updater layout-detection block must be gated or removed (#3290 RED)
Group A asserts the bare `ls -d .kilo ... || echo unknown` detection invocation
is absent or wrapped in a framework-repo gate (fails RED: currently unconditional).
Group B confirms zero downstream consumers of the verdict (passes GREEN: none exist).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(intel): gate layout-detection block on framework-repo check (#3290)
The "Runtime layout detection" bash block in gsd-intel-updater ran
unconditionally on every project analysed, emitting a noisy:
Layout detection returned "unknown" — this project is not a GSD-system
installation (no `.claude/get-shit-done/` or `.kilo/` runtime root).
for every ordinary (non-GSD-framework) user project. Group B audit confirmed
zero downstream consumers of the verdict outside the file itself.
Fix (option A): wrap the detection bash block in a positive framework-repo
gate — `jq -r '.name' package.json == "get-shit-done-cc"` — so it runs
only when analysing the GSD framework's own repo. The layout table (.kilo/*
paths) is retained for kilo-layout coverage (required by bug #2351 regression
test).
Dead-code vintage: byte-identical from v1.21.0 through v1.41.1 per reporter.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* changeset: pr=3299 for #3290
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>