Commit Graph

3229 Commits

Author SHA1 Message Date
Tom Boucher
ef436aae62 fix(#492): manifest effort.agent_overrides and effort.default now fall back correctly
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>
2026-06-01 09:52:39 -04:00
Tom Boucher
78871a7371 fix(#488): add gsd-tools effort sync command
* 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>
2026-06-01 09:51:50 -04:00
Tom Boucher
28a172d450 fix(#570): scope Codex leak scanner to manifest + replace bare ~/.claude refs
* 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>
2026-06-01 09:46:18 -04:00
Tom Boucher
94130e9968 test(#425): replace source-grep assertions with behavioral coverage (#573)
Remove three source-grep describe blocks from bug-1834 and bug-2136 test
files that anchored on byte-offset windows in install.js source. The same
regressions are already fully covered by the E2E behavioral tests (Section 1
in bug-1834, Part 4 in bug-2136) that invoke the actual installer and inspect
the installed files directly.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 09:45:58 -04:00
Tom Boucher
c2ebb1ba16 fix(#571): forbid Write in doc-writer fix mode; add workflow truncation guard
* 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>
2026-06-01 09:44:58 -04:00
Tom Boucher
2ba6b69d53 feat(#49): provider-neutral model policy presets
* 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>
2026-06-01 08:46:55 -04:00
Tom Boucher
3f5fa99ac5 Merge pull request #567 from open-gsd/claude/dreamy-williams-d12a18 2026-06-01 07:48:05 -04:00
Tom Boucher
d15a44676e chore(#504): remove dead sdk/ references from eslint & stryker config
ADR-0174 retired @opengsd/gsd-sdk; sdk/ no longer exists. Removes the
sdkSrcExists guard + unused existsSync/join imports + sdk/dist/** ignore
from eslint.config.mjs, and drops the stale GENERATED comment + exclusion
for configuration.cjs (hand-authored since SDK removal) from stryker.config.mjs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 23:36:41 -04:00
Tom Boucher
e3bc53f835 enhancement(#558): add liveness hints to all GSD spawn announcements (#566)
* 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>
2026-05-31 23:33:49 -04:00
Tom Boucher
0a12b06381 feat(#39): milestone-prefixed phase IDs (M-NN convention) + migration tool + validation (#565)
* feat(#39): milestone-prefixed phase IDs (M-NN convention) + migration tool + validation

- Add getMilestoneFromPhaseId() / getPhaseDirFromPhaseId() helpers to core.cjs
- Fix isDirInMilestone to match M-NN-style dirs (02-01-setup) against M-NN ROADMAP headings
- Extend heading regex to tolerate [bracket-token] scope prefix on phase headings
- Add W021 validation rule for milestone prefix mismatch
- Add gsd-tools roadmap validate + roadmap upgrade --convention milestone-prefixed
- Add phase_id_convention config field (null default, backwards-compatible)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(#39): address 4 Codex review findings in milestone-prefixed phase ID implementation

- getMilestoneFromPhaseId: tighten regex to require a digit after the hyphen (rejects '1-' and '1-abc')
- isDirInMilestone: use convention-aware regex — only capture M-NN segments when ROADMAP itself uses hyphenated phase IDs, preventing legacy dirs like '01-02-setup' from being misread as phase '1-02'
- checkW021: add UNPREFIXED_PHASE_RE path so unprefixed headings (### Phase 1:) also fire W021 when convention is milestone-prefixed
- roadmap-upgrade: remove isMigratedDirName dir-name check (false-positive for legacy dirs); config + ROADMAP heading checks at lines 194 and 212 are sufficient

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: update changeset pr reference to #565

* fix(#39): restore phaseDirNameRe 2-digit minimum; add roadmap-upgrade to inventory

- validate.cjs: \d{1,} → \d{2,} to keep single-digit prefix rejection per W005 contract
- docs/INVENTORY.md: 79 → 80, add roadmap-upgrade.cjs row
- docs/INVENTORY-MANIFEST.json: regenerated (roadmap-upgrade.cjs entry)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 23:15:55 -04:00
Tom Boucher
48c1827028 enhancement(#40): integrate branch pruning into /gsd-cleanup archival workflow (#564)
* 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>
2026-05-31 22:31:54 -04:00
Tom Boucher
4ee10f875f fix(#549): State progress writer over-counts total_phases by 1 when a decimal (inserted) phase exists (#561)
- 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>
2026-05-31 22:05:59 -04:00
Tom Boucher
03c0663cf5 fix(#557): milestone erased when version is in <summary> tag (#563)
* 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>
2026-05-31 22:05:45 -04:00
Tom Boucher
c976a9c858 fix(#38): replace misleading approved checkpoint in execute-phase human_needed branch (#560)
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.
2026-05-31 21:21:00 -04:00
Tom Boucher
9b5ee37364 chore(#556): retire orphaned CJS↔SDK hand-sync tooling (#559)
The @opengsd/gsd-sdk package boundary was retired (ADR-0174, #191/#192) and
the sdk/ tree is no longer tracked. ADR-0174's Supersedes table explicitly
records that the generator-based Shared-Module hand-sync lint is deleted as
part of that collapse. This removes the now-orphaned machinery it left behind:

- scripts/lint-shared-module-handsync.cjs — paired bin/lib/*.cjs files with
  sdk/src/**/*.ts sources that no longer exist; wired into no CI workflow or
  npm script (dead).
- scripts/shared-module-handsync-allowlist.json — the lint's allowlist; every
  entry pointed at a non-existent sdk/src source / generated artifact /
  freshness check.
- tests/lint-shared-module-handsync.test.cjs — tested the deleted lint.

Docs corrected to match:
- CONTRIBUTING.md — removed the "CJS↔SDK seam" instruction (it linked the
  already-deleted docs/agents/cjs-sdk-seam.md and told contributors to
  maintain the allowlist under a retired generator pattern).
- docs/prd/3524-cjs-sdk-hard-seam.md + docs/prd/README.md — marked the PRD
  Superseded by ADR-0174, matching the already-superseded ADR-3524.

Added tests/no-cjs-sdk-handsync-tooling.test.cjs as a regression guard so the
retired tooling stays removed and is not silently re-wired into package.json.

ADR-3524 is left in place (already Superseded by ADR-0174); runtime modules and
regression tests that cite it in comments keep resolving. No user-facing change.

Closes #556

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 20:28:57 -04:00
Tom Boucher
84e9888d25 chore(#552): lint hand-written bin/lib/*.cjs and remove surfaced dead code (#554)
The GENERATED_CJS_IGNORES array in eslint.config.mjs wrongly listed 12
hand-written bin/lib/*.cjs modules as "generated" and excluded them from
linting. Genuinely-generated artifacts are already covered by the
**/*.generated.cjs glob and the ADR-457 semver-compare.cjs entry, so the
array only created a coverage gap. Remove it so the 12 modules are linted
under the existing get-shit-done/bin/**/*.cjs ruleset.

Linting them surfaces two dormant dead-code findings, both removed here:

- phase-lifecycle.cjs: stale `eslint-disable-next-line no-cond-assign`
  directive — the loop already uses the parenthesized-assignment form the
  rule permits by default, so it suppressed nothing.
- state-document.cjs: vestigial `tempField`/`tempDefaults` locals (and
  their comment) left over from a refactor to an inline `.some(...)` check.

Pure dead-code/lint-config cleanup; no user-facing behavior change.

Closes #552

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 20:28:43 -04:00
Tom Boucher
01f894078d fix(#551): lint hand-written bin/lib/*.cjs mislabeled as generated (#555)
* fix(#551): lint hand-written bin/lib/*.cjs mislabeled as generated

GENERATED_CJS_IGNORES excluded 12 hand-written runtime modules from the
ADR-452 ESLint harness. None carry an @generated header and no generator
emits them — they are hand-written, not generated. Remove the mislabeled
list so they lint like the rest of get-shit-done/bin/**/*.cjs. The genuinely
tsc-generated semver-compare.cjs keeps its own separate ADR-457 ignore.

Also drop the stale "Type-aware via parserOptions.project=tsconfig.lint.json"
comment on the .cjs block: that block sets no parser/project and enables no
@typescript-eslint rules; type-aware linting lives in the src/**/*.cts block.

Lint stays green (0 errors); 2 pre-existing warnings surface (already warn
severity) per the file's warn-first convention, tracked as follow-up cleanup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(#551): guard ESLint coverage of hand-written bin/lib/*.cjs

Asserts via ESLint's isPathIgnored API that every get-shit-done/bin/lib/*.cjs
without an @generated header or a src/<name>.cts|.ts source is linted (not
ignored), and that the genuinely tsc-generated semver-compare.cjs stays
ignored (ADR-457). Fails 13/14 against the pre-fix config; passes on the fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 20:04:03 -04:00
Tom Boucher
de2f73d21a enhancement(#34): add Antigravity CLI (agy) as a peer reviewer in /gsd-review
Closes #34

Squash-merged via admin override — all CI green (28/28 checks), branch protection review gate bypassed with maintainer authorization.
2026-05-31 16:15:41 -04:00
Tom Boucher
5cd52eb151 enhancement(#537): pilot TS build-at-publish for bin/lib (semver-compare) (#541)
* 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>
2026-05-31 14:47:48 -04:00
Tom Boucher
6fc84528e5 fix(#448): resolve UI safety gate helper against the GSD install dir (#539)
* 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>
2026-05-31 14:42:37 -04:00
Tom Boucher
3d39064406 fix(#447): scope post-planning gap analysis to phase_req_ids (#538)
* 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>
2026-05-31 14:42:18 -04:00
Jeremy McSpadden
22a3400c64 Merge pull request #547 from open-gsd/codex/changelog-1.2.0
docs(#546): update changelog for 1.2.0
2026-05-31 10:59:28 -05:00
Jeremy McSpadden
c18edb4225 docs(#546): update changelog for 1.2.0 2026-05-31 10:56:48 -05:00
Jeremy McSpadden
e8ba6d276a Merge pull request #543 from open-gsd/codex/readme-version-alignment
[codex] docs: align README version source of truth
2026-05-31 10:33:32 -05:00
Jeremy McSpadden
c6724ea947 docs(#542): align README version source of truth 2026-05-31 10:31:26 -05:00
Jeremy McSpadden
0c2afb4d19 Merge pull request #529 from open-gsd/codex/tagline-punctuation-next
[codex] Correct GSD Core tagline punctuation
2026-05-31 08:00:50 -05:00
Jeremy McSpadden
42f8c192a8 Merge branch 'next' into codex/tagline-punctuation-next 2026-05-31 07:57:37 -05:00
Jeremy McSpadden
d6f555adec Merge pull request #533 from open-gsd/codex/remove-readme-continuity-section
[codex] Remove README continuity section
2026-05-31 07:50:13 -05:00
Jeremy McSpadden
313e6dace9 Merge pull request #535 from open-gsd/ci/maintainer-pr-policy-skips
ci(#534): skip maintainer PR policy gates
2026-05-31 07:49:37 -05:00
Jeremy McSpadden
1c835e208d ci(#534): skip maintainer PR policy gates 2026-05-31 07:46:56 -05:00
Jeremy McSpadden
7eaa14f48f docs(#532): remove README continuity section 2026-05-31 07:41:39 -05:00
Jeremy McSpadden
a8c025febc Merge pull request #531 from open-gsd/codex/readme-brand-cleanup-next
[codex] Streamline README brand header
2026-05-31 07:39:43 -05:00
Jeremy McSpadden
ec8a08f2d2 docs(#530): streamline README brand header 2026-05-31 07:36:52 -05:00
Jeremy McSpadden
09cb6d79b2 docs(#528): correct GSD Core tagline punctuation 2026-05-31 07:33:01 -05:00
Jeremy McSpadden
66b1a56b69 Merge pull request #527 from open-gsd/codex/include-assets-next
[codex] Include README assets in next package
2026-05-31 07:29:59 -05:00
Jeremy McSpadden
9bee1cab7f fix(#526): include README assets in package 2026-05-31 07:28:12 -05:00
Jeremy McSpadden
062f6dc11a Merge pull request #525 from open-gsd/main
Main
2026-05-31 07:21:41 -05:00
Jeremy McSpadden
45e4111257 Merge pull request #524 from open-gsd/codex/gsd-core-brand-refresh
[codex] Rebrand public docs as GSD Core
2026-05-31 07:19:48 -05:00
Jeremy McSpadden
c81d5fcb2c docs(#523): rebrand public docs as GSD Core 2026-05-31 07:16:19 -05:00
github-actions[bot]
63ad985b6a chore: finalize v1.2.0 2026-05-31 01:24:28 +00:00
github-actions[bot]
9e91489325 chore: bump to 1.2.0-rc.1 2026-05-31 00:02:19 +00:00
github-actions[bot]
ade4ee440b chore: bump version to 1.2.0 for release 2026-05-30 23:30:28 +00:00
Tom Boucher
0fbe1d899e chore(#191): retire the gsd-sdk shim — route everything at gsd-tools (#522)
* 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>
2026-05-30 19:19:14 -04:00
Colin Johnson
2bc295b32d fix(#464): make phase completion planning writes transactional (#465)
* fix(#464): make phase completion planning writes transactional

* fix(#464): add phase completion changeset

* test(#464): avoid source-grep rollback assertion

* fix(#464): address phase completion rollback review

* fix(#191): remove retired sdk tsconfig reference
2026-05-30 17:44:36 -04:00
Tom Boucher
79002a00cb chore(#518): rename npm package + bin to @opengsd/gsd-core (#519)
* 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>
2026-05-30 17:25:02 -04:00
Tom Boucher
05cdec5f47 feat(#22): plan-vs-codebase drift guard (source-grounded reviewer + intel surface) (#487)
* 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>
2026-05-30 17:08:11 -04:00
Tom Boucher
7fd71226b2 chore(#507): retire dead sdk/ references from CONTEXT.md (#508)
ADR-0174 retired the @opengsd/gsd-sdk package; sdk/ no longer exists.
CONTEXT.md still pointed contributors at dead sdk/src/**.ts paths — the
source of the stale references cited in bug reports #500 and #501.

- Glossary: repoint module Source-of-truth/entry-point pointers to the
  live get-shit-done/bin/lib/*.cjs and bin/shared/*.manifest.json homes;
  trim retired SDK-only clauses (milestone runner, SDK native-query
  surfaces, generator pattern). Every replacement path verified on disk.
- Remap live predicates: WORKSTREAM.POINTER.SEAM (-> active-workstream-
  store.cjs), PRED.k320.ci-paths-monitored (-> actual ci-test-scope
  roots), EXEC.CLASSIFY.handler (-> agent-command-router.cjs).
- Remove/generalize obsolete SDK-duality operative rules: SDK-ONLY-VERBS
  (deleted), PORT-DRIFT.cjs-sdk (deleted), SDK-PORT-NAME-COLLISION ->
  NAME-COLLISION (generalized), GENERATIVE exemplar repointed to live
  runtime-launcher-parity.test.cjs, stale-sdk/dist-gen-scripts section
  removed; REMOVED-BUT-NEEDED / SOURCE-GREP / CANARY.detect de-sdk'd.
- Preserve dated SESSION.* log + CANARY.examples audit (history).

Follow-ups for code-side sdk/ cleanup: #504 (lint/stryker config),
#505 (install.js sdk/dist verify), #506 (bin/lib generated banners).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 17:06:27 -04:00
Tom Boucher
0008244245 refactor: remove stale Source: sdk/src generated-file banners from bin/lib/*.cjs (#510)
* 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>
2026-05-30 17:06:23 -04:00
Tom Boucher
6f2520786d feat(#498): single Package Identity seam for /gsd:update + fix runtime undefined-name bug (#499)
* 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>
2026-05-30 16:53:44 -04:00
Tom Boucher
fbd555c2ce fix(#520): register package-identity.cjs in inventory (regression from #516) (#521)
#516 added get-shit-done/bin/lib/package-identity.cjs without regenerating
the inventory, breaking inventory-manifest-sync and inventory-counts on next.
Regenerate docs/INVENTORY-MANIFEST.json and bump docs/INVENTORY.md
CLI-module count 76 -> 77 with the new row.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 14:43:20 -04:00