* docs(3524): propose CJS↔SDK hard-seam ADR + phased PRD
Adds docs/adr/3524-cjs-sdk-hard-seam.md (Proposed) and
docs/prd/3524-cjs-sdk-hard-seam.md (Reference) tracking #3524.
Updates the ADR and PRD index READMEs.
The ADR defines one canonical owner per responsibility across the
CJS (bin/lib/*.cjs) and SDK (sdk/src/**/*.ts) sides, eliminating the
recurring drift bug class (#1535, #1542, #2047, #2638, #2653, #2687,
#2798, #3055, #3523). Three layers: shared data (sdk/shared/*.json),
shared core logic (sdk/src/core/ → dual CJS+ESM build), thin adapters.
Enforcement is layered: build-time grep, type-level contract test,
mutation parity test, CODEOWNERS gate, in-file banner.
The PRD phases the migration in five independently shippable steps —
shared data first (closes constant drift), then config consolidation
(closes#3523 class), then project-root + path projection, then state
and verify handlers, then enforcement hardening + retrospective.
* docs(3524): revise seam ADR + PRD after architecture review
Architecture-review pass (via /improve-codebase-architecture) found
seven deepening opportunities; this commit applies all of them.
1. Re-anchor on the existing generator precedent. The repo already
has sdk/scripts/gen-command-aliases.ts emitting both
.generated.ts and .generated.cjs from one TS source, with
sdk/scripts/check-command-aliases-fresh.mjs as the CI freshness
gate. The ADR's invented dual CJS+ESM bundler pipeline is
dropped. Each Shared Module gets one generator script and one
freshness check, modeled on that precedent.
2. Drop the generic sdk/src/core/ container. The canonical-owner
table is now indexed by Module, using the CONTEXT.md domain
vocabulary (STATE.md Document Module, Configuration Module,
etc.) rather than file-path-based pseudo-modules.
3. Split the coarse "State management" row into three: the pure
STATE.md Document Module (already a character-identical
hand-synced pair — perfect Phase 1 target), the Planning
Workspace Module (defer to ADR-0004), and per-side state I/O
Adapters (legitimately differ sync vs async).
4. Defer to existing ADRs. Planning Path Projection (ADR-0006),
Model Catalog (ADR-0003), Planning Workspace (ADR-0004),
Dispatch Policy (ADR-0001), Shell Command Projection (ADR-0009
post-Phase 3-4 expansion which absorbed superseded ADR-0010).
The stale ADR-0010 reference is fixed.
5. Define a Configuration Module entry in CONTEXT.md as a Phase 2
deliverable, with explicit Interface contract for loadConfig,
normalizeLegacyKeys, mergeDefaults, migrateOnDisk.
6. Split the Workstream Inventory Module into a pure Builder
(generated, shared) and per-side Reader Adapters (hand-authored,
sync vs async). Same pattern generalizes to other paired Modules.
7. Match enforcement to existing scripts. Per-Module freshness
checks (precedent: check-command-aliases-fresh.mjs), per-Module
drift lints (precedent: lint-shell-command-projection-drift.cjs),
and one hand-sync pair lint that blocks the #3523 anti-pattern
at PR time.
PRD phases reordered: STATE.md Document Module ships first as a
proof of pattern (two identical files become one source plus one
generated artifact). Configuration Module ships second, closing
the #3523 class. Workstream Inventory Builder split third.
Project-Root Resolution fourth. Enforcement and retrospective
fifth.
* docs(3524): expand scope — CJS router delegates to SDK runtime bridge
User flagged that the original "Out of scope" list was my unilateral
scoping call, not theirs. After review, the CJS router consolidation
(formerly out-of-scope item #1) is brought into scope.
ADR additions:
- CJS Command Router Adapter Module row added to canonical-owner
table. Existing Module (per CONTEXT.md) is amended so the
per-family `handlers` map delegates to `QueryRuntimeBridge.execute()`
in-process. Per-side CJS handler files for canonical families
(state.cjs, verify.cjs, init.cjs, phase.cjs, etc.) shrink to
delegates or are deleted.
- Per-side I/O Adapter consequence updated to clarify the bridge
preserves the in-process model. No subprocess hop is added.
- "Out of scope" stripped of router item; CJS-only seam migration
and verify-Module-first work remain out of scope.
PRD additions:
- New Phase 5: CJS Command Router Adapter delegates to SDK runtime
bridge, family-by-family, with golden parity matrix per family
gating each PR.
- Old Phase 5 (enforcement) renumbered to Phase 6, expanded to cover
Phase 5's parity matrix and runtime-bridge CODEOWNERS.
- Open question #4 added for the synchronous-bridging mechanism
(`deasync` vs `Atomics.wait` vs sync-handler refactor) — resolved
in the Phase 5 spike before any family migration begins.
- Open question #5 added for family migration order (recommended:
smallest read-only family first).
- Risks table expanded with three Phase 5 rows: bridging-mechanism
uncertainty, observable-output regression, startup-time impact.
- Done-when updated for six phases and five enforcement layers.
Non-goals updated: CJS-only Module migration and verify-Module
deepening remain out of scope. CJS CLI removal explicitly stays
off the table — the external `gsd-tools` contract is preserved.
* docs(3524): address CodeRabbit review
* docs(3524): fix PRD issue reference markdown
Closes#3522. When --respect-staged is passed the git add loop is skipped
entirely so per-hunk staging from git add -p is preserved. Default behavior
(full re-stage) is unchanged. The #3061 pathspec invariant holds under both
modes. Nothing-staged within scope returns { committed: false, reason:
'nothing staged' } without error.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three-part fix for the false "unknown config key(s)" warning fired for
top-level `branching_strategy` in .planning/config.json:
1. On-disk migration (option 3, mirroring multiRepo → planning.sub_repos):
When loadConfig reads a config.json with top-level `branching_strategy`
set and `git.branching_strategy` unset, it grafts the value into
`git.branching_strategy` and deletes the top-level key, then persists.
If `git.branching_strategy` is already set, the nested value wins
(matches SDK mergeDefaults precedence, PR #3116).
2. KNOWN_TOP_LEVEL safety net: 'branching_strategy' added to the deprecated-
keys bucket so the warning never fires even on the first read of a root
config that feeds a workstream merge (where `parsed` may still carry it).
3. Double-emission guard: a module-level `_warnedUnknownConfigKeys` Set
deduplicates the unknown-key warning across multiple loadConfig calls
within a single CLI invocation (init phase-op N called it twice).
Closes#3523
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Six failing tests covering all Done-when criteria from #3523:
1. No warning emitted for top-level branching_strategy
2. Value still surfaced via git.branching_strategy after loadConfig
3. Double-emission capped to single-emission per process
4-5. On-disk migration (option 3): write-back + no-clobber guard
6. CJS↔SDK contract: both agree on legacy-shape fixture
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The post-merge worktree-cleanup loop in quick.md issued bare `git diff`,
`git merge`, and related commands relying on CWD = project root. An LLM
orchestrator that reformats bash across separate tool calls can leak CWD
into a worktree, causing the merge to silently no-op.
At the top of each iteration body, resolve PROJECT_ROOT via
`git -C "$WT" rev-parse --git-common-dir` and `cd "$PROJECT_ROOT"`.
If the root cannot be resolved or reached, log a skip message and
continue to the next manifest entry. All existing guards (pre-merge
deletion guard #1756, STATE.md/ROADMAP.md backup/restore, resurrection
guard #2501/#3195) remain intact after the CWD pin.
Closes#3521 (bug 1 — CWD safety only; bug 2 / resurrection guard was
already fixed in a6beac40 / PR #3201 and is pending reporter retest).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Simulates orchestrator-leaked CWD in the post-merge cleanup loop and
asserts that PROJECT_ROOT is resolved via `git -C "$WT" rev-parse
--git-common-dir` before any bare git command, that a missing root
causes a logged skip/continue, and that the existing pre-merge deletion
guard (#1756) and STATE.md/ROADMAP.md backup/restore remain in-place
after the CWD pin.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The grep -v alternation in the git-enhanced two-way merge step was missing
the 'gsd-update' arm after the slash-command rename from /gsd:update to
/gsd-update. Commits authored by the current update flow fell through the
filter and were misclassified as user customizations, causing spurious merge
prompts during /gsd-update --reapply.
Adds 'gsd-update' between 'gsd:update' and 'GSD update'. The legacy
'gsd:update' arm is preserved for back-compat; 'GSD update' and 'gsd-install'
exclusions are unchanged.
Adds bug-3516-reapply-patches-gsd-update-filter.test.cjs — 7 tests that
assert all four exclusion patterns (gsd:update, gsd-update, GSD update,
gsd-install) are present in the git-enhanced two-way merge filter inside
get-shit-done/workflows/reapply-patches.md.
Two tests fail before the fix: 'filter excludes renamed gsd-update commits'
and 'all four expected exclusion patterns are present in the filter'.
Fixes two root causes behind bug #3517:
1. Idempotency: completed_phases was blindly incremented (parseInt + 1),
causing phase.complete N run twice to double-count (4 → 5 → 6).
Now derives from ROADMAP progress table Complete-row count, making
the operation idempotent.
2. Field coverage: eight STATE.md fields were left stale after phase
completion. Now updates in the same atomic lock section:
- frontmatter: stopped_at, last_updated, total_plans, completed_plans
- body: Current focus, Status line, By Phase table row
completed_plans = count of *-SUMMARY.md files across all phase dirs
total_plans = sum of M/N plan counts from ROADMAP progress table
percent = recomputed from fresh derived counts
Closes#3517
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extract composeStatusline() helper from duplicated inline template logic in
runStatusline() and renderStatusline(). Both call sites now route through the
helper, which accepts a position param ('end' | 'front', default 'end').
- 'end' (default) preserves byte-identical output to v1.38.x and earlier
- 'front' renders ctx immediately after model name, before the first │
- Invalid values silently coerce to 'end' at runtime (belt-and-suspenders;
config-set rejects invalid values upfront via enum validator)
Adds statusline.context_position to VALID_CONFIG_KEYS in both CJS and TS
schemas, enum validator in config.cjs, docs row in CONFIGURATION.md,
and a changeset. Closes#2937.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On machines where os.tmpdir() returns a path with a space (e.g.
/Volumes/Mini Me/tmp), runGsdTools() string args were whitespace-split by
the helper tokeniser, truncating paths at the first space. Switch all
calls that embed a dynamic path into the argument list to the array form
of runGsdTools() so execFileSync receives each path as a single argv slot.
Fixes#3509
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds boolean config key `git.create_tag` (default: true, fully backcompat)
so projects with their own release flow can disable GSD's automatic
`git tag -a v[X.Y]` on milestone completion. Also adds tag-collision
pre-check to prevent silent failure on re-run. Closes#3086
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>