Commit graph

1378 commits

Author SHA1 Message Date
Jordan Gaytan
558b1f2081 fix(parallel): three bugs preventing reliable parallel worker execution (#2801)
Bug 1 — Workers exit immediately (#2792):
spawnWorker() used `--print "/gsd auto"` which calls session.prompt()
that returns immediately when ctx.newSession() resets the session inside
the auto-loop. Changed to `headless --json auto` which uses an RPC
client that keeps the process alive until auto-mode completes.

Bug 2 — Dispatch guard blocks parallel workers (#2797):
getPriorSliceCompletionBlocker() checked ALL milestones in queue order,
blocking M012 when M011 had incomplete slices. When GSD_MILESTONE_LOCK
is set, the guard now only checks intra-milestone slice dependencies.
Added test covering cross-milestone bypass + intra-milestone preservation.

Bug 3 — Orphaned RPC children on stop (#2798):
stopParallel() gave only 750ms for SIGTERM before SIGKILL. The headless
parent needs ~1500ms to cascade shutdown to its RPC child via
client.stop(). Increased to 3000ms to prevent orphaned processes holding
auto.lock.

Updated tests:
- dispatch-guard.test.ts: new test for GSD_MILESTONE_LOCK bypass
- parallel-worker-monitoring.test.ts: updated spawn args assertion
2026-03-27 18:10:02 -06:00
mastertyko
6918fb76c6 fix(gsd): parse raw YAML under preference headings (#2794)
Accept raw YAML blocks beneath markdown preference headings while preserving legacy heading-list parsing.

Closes #2787
2026-03-27 18:09:48 -06:00
mastertyko
c88a9d2d4f fix(gsd): persist verification classes in milestone validation (#2820) 2026-03-27 18:09:39 -06:00
drkthng
6dd02bb3ce fix(gsd): guard reconcileWorktreeDb against same-file ATTACH corruption (#2825)
When worktrees use shared-WAL mode (R012), the worktree DB path resolves
to the same physical file as the project root DB via symlink. Calling
reconcileWorktreeDb() ATTACHes this WAL-mode file to itself, corrupting
the database with 'database disk image is malformed'.

Fix 1 — auto-worktree.ts mergeMilestoneToMain(): skip reconciliation
when isSamePath() confirms both DB paths resolve to the same file.

Fix 2 — gsd-db.ts reconcileWorktreeDb(): defence-in-depth realpathSync
guard inside the function itself, before the ATTACH statement.

Fix 3 — auto/infra-errors.ts: classify 'database disk image is
malformed' as SQLITE_CORRUPT infrastructure error so the auto-loop
stops immediately instead of burning 3 retries on a guaranteed failure.

Regression tests verify:
1. Same-file via symlink returns zero (no ATTACH)
2. Identical string paths return zero
3. Genuinely different DBs still reconcile normally
4. Malformed DB message classified as infra error
5. Transient SQLITE_BUSY is not falsely classified

Closes #2823
2026-03-27 18:08:13 -06:00
TÂCHES
44dd06fe66 Merge pull request #2868 from jeremymcs/fix/model-override-unittype-mapping
fix(model-routing): use honest unitTypes for discuss dispatches
2026-03-27 17:40:08 -06:00
TÂCHES
8a35b2308f Merge pull request #2887 from mastertyko/fix/subagent-project-agents-dir
fix(gsd): discover project subagents in .gsd
2026-03-27 17:39:36 -06:00
TÂCHES
f9dd134933 Merge pull request #2904 from mastertyko/fix/auto-resume-cold-db-bootstrap
fix(gsd): resume cold auto bootstrap from db
2026-03-27 17:39:07 -06:00
TÂCHES
ac85f28822 Merge pull request #2915 from gsd-build/fix/headless-completed-status
fix(headless): match "completed" status from RPC v2
2026-03-27 17:16:00 -06:00
Lex Christopherson
8870d84012 fix(headless): match "completed" status from RPC v2 in exit code mapper
mapStatusToExitCode only handled "complete" but RPC v2 emits "completed",
causing all headless sessions to falsely timeout and restart.

Also emits milestone-ready notification in checkAutoStartAfterDiscuss so
headless parent can detect and chain into auto-mode.

Closes #2914

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 17:04:31 -06:00
mastertyko
09de02adee fix(gsd): resume cold auto bootstrap from db
Open the project database before the first auto bootstrap derive so cold-start resume uses DB-backed slice state instead of stale markdown fallback state.

Also recognize glyph completion markers in roadmap tables and lock the new bootstrap ordering with regression coverage.

Closes #2841
2026-03-27 22:58:16 +01:00
mastertyko
6843d39971 fix(gsd): preserve first auto unit model after session reset
Auto-mode selected the correct unit model in runUnitPhase, but a fresh session could drop that selection before the first prompt was sent.

Persist the applied unit model on AutoSession, restore it immediately after newSession(), and cover the seam with a regression test that proves the model is re-applied before dispatch.

Closes #2853
2026-03-27 22:58:16 +01:00
mastertyko
142da7823a fix(gsd): prefer PREFERENCES.md in worktrees (#2796)
Keep auto-worktree sync and initial seeding aligned with the repo's canonical preferences filename while retaining the lowercase legacy fallback for older repos and case-sensitive filesystems.
2026-03-27 14:52:30 -06:00
Iouri Goussev
b6e105b058 perf(test): compile unit tests with esbuild, reclassify integration tests, fix node_modules symlink (#2809)
* fix(test): wire src/resources/extensions/shared/tests/ into test:unit runner

The test:unit glob excluded src/resources/extensions/shared/tests/ entirely,
leaving format-utils.test.ts (and any future tests there) silently unfired.

- Add shared/tests/*.test.ts to the test:unit glob in package.json
- Export newestSrcMtime from ensure-workspace-builds.cjs (require.main guard
  prevents side-effects on require) so the staleness logic can be tested
- Add src/tests/ensure-workspace-builds.test.ts covering newestSrcMtime:
  non-existent dir, no .ts files, single file, max of multiple, recursion,
  node_modules skip

Closes #2808

* perf(test): compile unit tests with esbuild and fix dist-test/node_modules

Replace per-file --experimental-strip-types with a single esbuild compilation
step (scripts/compile-tests.mjs) that compiles all src/ TypeScript to dist-test/
in ~3s, then runs the pre-compiled JS. Eliminates ~1.7s Node startup overhead
per test file.

- scripts/compile-tests.mjs: esbuild compilation, asset copy, .ts→.js rewrite,
  stale file cleanup; creates dist-test/node_modules symlink so resource-loader.ts
  resolves gsdNodeModules to a real path (fixes node-modules-symlink test failure)
- scripts/dist-test-resolve.mjs: ESM loader hook for @gsd/* bare specifiers and
  .ts→.js fallback rewriting at runtime
- .gitignore: exclude dist-test/ from version control
- package.json: add test:compile script; update test:unit to compile-then-run;
  update test:integration globs to cover new integration/ subdirectories
- worker-registry.ts: unref() cleanup timer so it does not keep the Node process
  alive after tests complete

Closes #2858

* fix(test): update relative imports in tests/integration/ after directory move

When tests were moved from tests/ to tests/integration/ in the previous
commit, relative imports weren't updated. ../foo now resolves one level
too shallow.

Fix all 117 import paths across 43 test files:
- ../foo → ../../foo (source files at gsd/ level)
- ../../get-secrets-from-user.ts → ../../../ (at extensions/ level)
- ../../subagent/worker-registry.ts → ../../../ (at extensions/ level)
- ./marketplace-test-fixtures.js → ../marketplace-test-fixtures.ts
- ./test-helpers.ts → ../test-helpers.ts

typecheck:extensions now passes with zero errors.

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

* test(integration): set 10-minute timeout for integration test runner

build job takes ~7min on main. Without a global timeout, hanging tests
block the suite indefinitely. --test-timeout=600000 caps each test at 10min.

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

* Revert "test(integration): set 10-minute timeout for integration test runner"

This reverts commit be77ead77d369ad8569292ae6b69ba56435f5433.

* fix(test): correct formatDuration(0) edge case and docker test root path

- formatDuration(0) now returns '0s' instead of '0ms' by guarding the
  sub-second branch with ms > 0
- docker-template.test.ts root path goes ../../.. from dist-test/src/tests/
  to reach project root instead of landing in dist-test/
- replace require() calls in skill-health.ts and visualizer-overlay.ts
  with proper ES module imports

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

* fix(test): correct relative import paths in integration tests

All affected tests were one directory level off — importing from ../web/
and ../resources/ when the correct paths are ../../web/ and ../../resources/.
Tests live at src/tests/integration/, not src/tests/.

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

* fix(test): add esbuild to root devDeps and wire dist-test-resolve hook

P1: esbuild was only in web/package.json — compile-tests.mjs requires it
at the root node_modules path, so CI failed on clean installs.

P2: dist-test-resolve.mjs existed but was never loaded; @gsd/* imports in
compiled tests resolved to installed workspace packages instead of freshly
compiled dist-test output. Add --import to test:unit.

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

* fix(deps): align esbuild version with lock file (0.25.12)

^0.27.4 didn't satisfy the existing lock file entry. Use the version
already present so npm ci passes without regenerating the lock file.

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

* fix(test): correct all relative import depths in src/tests/integration/

Tests in src/tests/integration/ need 3 levels up (../../..) to reach
project-root dirs (web/, packages/) and 2 levels up (../..) to reach
src-level dirs (src/web/, src/cli-web-branch.ts).

Fixes:
- ../../web/lib/ → ../../../web/lib/   (Next.js app, not src/web/)
- ../../web/app/ → ../../../web/app/
- ../../packages/ → ../../../packages/
- ../cli-web-branch.ts → ../../cli-web-branch.ts
- ../web-mode.ts → ../../web-mode.ts
- ../resources/extensions/ → ../../resources/extensions/
- ci_monitor ROOT path: 2 levels up → 3 levels up
- web-responsive WEB_ROOT: 2 levels up → 3 levels up

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

* chore(test): use dot reporter for test:unit to reduce noise

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

* chore(test): switch test:unit reporter to tap

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

* chore(test): compact test reporter — silent on pass, failures + summary only

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

* chore(test): include shared/tests in test:coverage

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

* fix(test): correct path depths in tests moved to integration/

Tests moved from tests/ to tests/integration/ need one extra ../
to reach the same source files. Also fix web component paths — those
files live at web/ not src/web/.

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

* fix(test): fix web component paths in web-session-parity-contract

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

* fix(test): use process.cwd() for project root in docker-template test

Resolving relative to __dirname breaks under test:coverage which runs
source files directly from src/tests/ — needs ../.. not ../../..
(the extra level only exists in the compiled dist-test/ output).

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

* ci: retrigger CI

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 14:51:49 -06:00
Jordan Gaytan
48279ae5a4 feat(parallel): /gsd parallel watch — native TUI overlay for worker monitoring (#2806)
* feat(parallel): add /gsd parallel watch command and Ctrl+Alt+P overlay

Integrates the parallel worker monitor as a native pi-tui overlay that
renders inside the GSD session, matching the existing dashboard overlay
pattern (GSDDashboardOverlay / Ctrl+Alt+G).

Three integration points:
- /gsd parallel watch — opens the live monitor overlay
- Ctrl+Alt+P — keyboard shortcut (same pattern as Ctrl+Alt+G for status)
- Tab completion: 'watch' added to parallel subcommand completions

The overlay (ParallelMonitorOverlay) provides:
- Per-worker panels: health dot, phase label, slice/task progress bars
- Event feed: recent task completions from worktree SQLite DBs
- Cost tracking: status.json with NDJSON fallback for respawned workers
- Heartbeat: orchestrator timestamp or file mtime proxy
- Scrollable: arrow keys / j/k, ESC/q to close
- 5s auto-refresh via setInterval

Reuses all data-reading logic from the standalone scripts/parallel-monitor.mjs
(merged in #2799) but renders through the pi-tui theme system instead of
raw ANSI codes. Follows the same overlay registration pattern as the
GSD dashboard (register-shortcuts.ts + handlers/core.ts).

* fix(parallel): align overlay with Component interface, add tests

- Add invalidate() method required by Component interface
- Fix handleInput signature: void return, not boolean
- Fix Key usage: Key.escape/Key.down/Key.up (constants, not functions)
- Fix render signature: single width arg, not (width, height)
- Add resize listener cleanup in dispose()
- Add parallel-monitor-overlay.test.ts (satisfies require-tests CI gate)

* fix(parallel): use spawnSync for cross-platform path safety

Replace execSync template literals with spawnSync array args for sqlite3
calls. Paths with spaces or special chars broke on Windows because
execSync interpolates into a shell string. spawnSync passes args directly
to the process, bypassing shell interpretation.

Fixes cross-platform-filesystem-safety.test.ts assertion.
2026-03-27 14:51:18 -06:00
mastertyko
447a57ae0f fix(gsd): resume auto-mode after transient provider pause (#2822)
Transient provider recovery previously sent a hidden continue message after the backoff timer elapsed, but the auto loop had already exited. Resume the paused session through startAuto() instead so the timer actually restarts auto-mode, and cover the resumed, duplicate-resume, and missing-base-path cases with regression tests.

Closes #2813
2026-03-27 14:50:40 -06:00
Jeremy McSpadden
b8d4f03747 fix(parallel): resolve session lock contention and 3 related parallel-mode bugs (#2184) (#2800)
Per-milestone lock isolation prevents workers from contending on shared
.gsd/auto.lock. Budget ceiling scoped to current session for parallel
workers. Symlink sync skip prevents ERR_FS_CP_EINVAL. Planning artifacts
copied to worktree so workers can find their roadmap.

Closes #2184
2026-03-27 14:48:35 -06:00
mastertyko
a0b9a85a20 fix(gsd): preserve auto start model through discuss (#2837) 2026-03-27 14:47:14 -06:00
mastertyko
2c0f2b3893 fix(gsd): discover project subagents in .gsd
Prefer the documented .gsd/agents location for project-local subagents while keeping a legacy fallback to .pi/agents so existing workarounds continue to function. Add a regression test covering both paths.

Closes #2864
2026-03-27 21:41:45 +01:00
TÂCHES
1d5590c19a feat: headless text mode observability + skip UAT pause (#2867)
* feat: headless text mode shows tool calls + skip UAT pause in headless

Text mode observability:
- Tool calls always visible with summarized args (path, command, pattern)
- Tool errors surfaced even in non-verbose mode
- Cost updates shown periodically
- Empty [status] lines suppressed (setStatus/setWidget are TUI-only)
- Empty notify messages suppressed

UAT pause skip:
- Set GSD_HEADLESS=1 env var when spawning RPC child process
- auto-dispatch checks GSD_HEADLESS and skips pauseAfterDispatch for UAT
- Headless runs no longer stall waiting for human UAT verification

* test: add formatProgress unit tests for headless text mode

16 tests covering tool call display, arg summarization, cost formatting,
empty status suppression, and notify filtering.

* ci: retrigger
2026-03-27 12:13:17 -06:00
Jeremy
c622eec0e1 fix(model-routing): use honest unitTypes for discuss dispatches and map all auto-dispatch phases
Discuss dispatches in guided-flow.ts were aliased to "plan-milestone"/"plan-slice"
unitTypes, causing the planning model preference to silently override the user's
active model. This was discovered when a user configured Codex as their model but
got switched to Opus during discuss phases because models.planning was set.

Changes:
- Add "discuss" and "validation" keys to GSDModelConfig/GSDModelConfigV2
- Map discuss-milestone/discuss-slice to models.discuss (falls back to planning)
- Map reassess-roadmap/rewrite-docs/gate-evaluate/validate-milestone to
  models.validation (falls back to planning)
- Map reactive-execute to models.execution, complete-milestone to models.completion
- Fix 15 dispatchWorkflow calls in guided-flow.ts to use honest unitTypes
- Add discuss-slice to LIFECYCLE_ONLY_UNITS, artifact paths, metrics phase
  classification, complexity tiers, and all dashboard/overlay label functions

Closes #2865
2026-03-27 12:48:11 -05:00
mastertyko
36930694e4 fix(gsd): use project root for prior-slice dispatch guard (#2863)
Resolve the prior-slice completion guard against originalBasePath when auto-mode is running in a worktree. This keeps completed upstream milestones from blocking new dispatches because their SUMMARY state lives at the project root, not the stale worktree snapshot.

Closes #2838

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-03-27 11:29:03 -06:00
mastertyko
27f66100e5 fix(gsd): include queue context in milestone planning prompts (#2846)
* test(integration): suppress npm pack buffer overflows

* fix(gsd): include queue context in milestone planning prompts
2026-03-27 09:55:19 -06:00
mastertyko
2bc92afa6b fix(bg-shell): recover from deleted cwd in timers (#2850)
* test(integration): suppress npm pack buffer overflows

* fix(bg-shell): recover from deleted cwd in timers
2026-03-27 09:54:31 -06:00
mastertyko
905ee092ce fix(gsd): enable dynamic routing without models section (#2851)
* test(integration): suppress npm pack buffer overflows

* fix(gsd): enable dynamic routing without models section
2026-03-27 09:53:51 -06:00
mastertyko
9ce6e02bd8 fix: hydrate collected secrets for current session (#2788)
secure_env_collect previously persisted secrets to their destination but left the running Node process unchanged. Extensions like Context7 read process.env directly, so newly collected keys did not work until restart.

Hydrate process.env as soon as a secret is successfully applied, and cover the regression through collectSecretsFromManifest so the current session can use the key immediately.

Closes #2685
2026-03-26 20:33:20 -06:00
TÂCHES
9823fd2d2d fix: resolve stash pop conflicts and stop swallowing merge errors (#2780)
* fix: resolve stash pop conflicts and stop swallowing merge errors

After a squash merge, `git stash pop` can conflict on `.gsd/` state files,
leaving them in UU state that permanently blocks all subsequent milestone
merges. The post-commit stash pop catch block now detects `.gsd/` conflicts,
auto-resolves them by accepting the HEAD version (matching the existing
merge-time policy), and drops the stash when safe.

In phases.ts, three catch blocks only handled MergeConflictError and silently
continued on any other error, allowing auto-mode to advance to the next
milestone with unmerged work. All three now stop auto-mode and return a
"merge-failed" break result for non-conflict errors.

Closes #2766

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add regression tests for stash pop conflict and error handling

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use correct LogComponent type in stash pop handler

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: join file array for logWarning in stash pop handler

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:16:42 -06:00
TÂCHES
f4736f47ae fix: treat any extracted verdict as terminal in isValidationTerminal (#2774)
* fix: treat any extracted verdict as terminal in isValidationTerminal

If the LLM writes a VALIDATION file with an unrecognized verdict like
`fail`, the allowlist in isValidationTerminal() returned false, keeping
the state machine in validating-milestone phase and re-dispatching
validate-milestone indefinitely (14+ times observed).

Any non-null verdict from extractVerdict() means validation completed.
Only return false when no verdict could be parsed.

Closes #2769

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add regression tests for isValidationTerminal with fail verdict

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: update existing test to match new any-verdict-is-terminal behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:09:00 -06:00
TÂCHES
d80927f50d fix: guard activeMilestone.id access in discuss and headless paths (#2776)
* fix: guard activeMilestone.id access in discuss and headless paths

When upstream state corruption (#2772, #2770) produces an activeMilestone
object with undefined id, the existing `!state.activeMilestone` guard
passes (truthy object), and the undefined id propagates to SQLite where
better-sqlite3 throws "Missing named parameter 'mid'".

Strengthen guards at three call sites to check `!state.activeMilestone?.id`
so corrupted state falls through to the no-milestone recovery path.

Closes #2773

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add regression tests for activeMilestone.id guard

Covers the #2773 fix where a malformed activeMilestone object with
id: undefined bypassed the old truthiness check and caused a crash
in discuss and headless paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:05:19 -06:00
TÂCHES
6af72210d1 fix: clean up zombie parallel workers stuck in error state (#2782)
* fix: clean up zombie parallel workers stuck in error state

refreshWorkerStatuses() set worker.state = "error" for dead workers but
never removed them or deactivated the orchestrator, leaving zombies in
memory forever. restoreRuntimeState() short-circuited on state?.active
without verifying any workers were actually alive.

Two fixes:
1. refreshWorkerStatuses() now checks if all workers are terminal after
   the status sweep — if so, deactivates the orchestrator and removes
   the persisted state file.
2. restoreRuntimeState() now verifies at least one worker is in a
   non-terminal state before returning true. If all workers are dead,
   it clears the stale cached state and falls through to restoreState()
   which handles proper cleanup.

Closes #2736

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add regression tests for zombie worker cleanup

Covers #2736: verifies refreshWorkerStatuses() deactivates orchestrator
when all workers reach terminal states, and restoreRuntimeState() clears
stale cached state instead of returning true with only dead workers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:04:46 -06:00
TÂCHES
be7466d99f fix: relax milestone validation gate to accept prose evidence (#2779)
* fix: relax milestone validation gate to accept prose evidence

The completion gate at auto-dispatch.ts required exact "MET"/"N/A"
substrings that renderValidationMarkdown() never emits, causing a
deadlock where no validation output could satisfy the gate. The gate
now accepts either the structured template format (MET/N/A table) or
prose evidence patterns (e.g., "Operational: verified", "Operational
checks confirmed") that the validation agent naturally produces.

Closes #2739

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add tests for relaxed validation gate patterns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:04:43 -06:00
TÂCHES
ce23da6718 fix: write milestone reports to project root instead of worktree (#2778)
* fix: write milestone reports to project root instead of worktree

During worktree isolation, s.basePath points to the temporary worktree
directory. Reports written there are silently lost when the worktree is
cleaned up. Use s.originalBasePath (falling back to s.basePath when not
in a worktree) so reports persist in the actual project directory.

Closes #2751

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add regression test for milestone report path resolution

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:04:40 -06:00
TÂCHES
f96a26b3c9 fix: auto-resolve build artifact conflicts in milestone merge (#2777)
* fix: auto-resolve build artifact conflicts in milestone merge

The binary conflict classification in mergeMilestoneToMain only
auto-resolved .gsd/ prefixed files. Machine-generated build artifacts
like .tsbuildinfo, .pyc, __pycache__/, .DS_Store, and .map files were
treated as real code conflicts, blocking auto-merge unnecessarily.

Extract an isSafeToAutoResolve helper that checks both the .gsd/ prefix
and a SAFE_AUTO_RESOLVE_PATTERNS regex list. Matched files are resolved
with --theirs, same as .gsd/ state files.

Closes #2761

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add tests for build artifact auto-resolve patterns

Extract isSafeToAutoResolve and SAFE_AUTO_RESOLVE_PATTERNS to module-level
exports for testability. Add unit tests covering .gsd/ state files, build
artifacts (.tsbuildinfo, .pyc, __pycache__, .DS_Store, .map), and rejection
of real source files (.ts, .js, .py, .json, .md).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:03:37 -06:00
TÂCHES
46016adf9a fix: let rate-limit errors attempt model fallback before pausing (#2775)
* fix: let rate-limit errors attempt model fallback before pausing

Rate-limit errors were early-returning with pauseTransientWithBackoff()
before reaching model fallback logic. Since rate limits are frequently
per-model (not provider-wide), this caused 20+ minute stuck-loops when
fallback models were available. Now rate-limit errors enter the same
fallback path as other transient errors, only pausing if no fallback
model is available.

Closes #2770

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add regression test for rate-limit model fallback

Verifies that rate-limit errors enter the model fallback path before
pausing (#2770), and that the old early-return bypass no longer exists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:03:28 -06:00
mastertyko
d7755e596c test(gsd): harden suite-level stability for RTK, worktree, and git bootstrap (#2786)
* test: harden web runtime auth token and lock retry tests

Teach the packaged web runtime harness to recover the auth token from the launcher stderr when the browser-open stub log is absent. Also widen the transient session-lock retry tests so they stay stable under full-suite CPU contention.

* test: harden suite-level RTK and worktree stability

Stabilize the RTK seam tests under full-suite load by using a faster fake RTK binary on Unix and allowing the tests to raise the rewrite timeout without changing the production default. Also widen the transient session-lock retry budget and give the heavy auto-worktree milestone merge suite an explicit timeout so it can complete under CI-level contention.

* test: harden git-service repo bootstrap under suite load

Switch repo bootstrap steps in git-service.test.ts to runGit(...) where the setup only needs direct git invocations.

This removes avoidable shell wrappers from the highest-churn repo setup paths, which makes the full unit suite less prone to child-process flake under load while keeping the test behavior unchanged.
2026-03-26 20:02:41 -06:00
TÂCHES
4fe4dbf456 fix: prevent gsd next from self-killing via stale crash lock (#2784)
* fix: prevent gsd next from self-killing via stale crash lock

cleanupAfterLoopExit() did not clear the crash lock or session lock.
On the next `/gsd next`, checkRemoteAutoSession() read the stale lock
with the current PID, reported it as a "remote" session, and
stopAutoRemote() sent SIGTERM to the current process.

Three fixes:
1. cleanupAfterLoopExit() now clears crash lock and releases session lock
2. checkRemoteAutoSession() returns { running: false } for own PID
3. stopAutoRemote() guards against self-kill for own PID

Closes #2730

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add regression tests for stale lock self-kill prevention

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:59:32 -06:00
Jeremy McSpadden
ff2c2605f3 feat(gsd): enable safety mechanisms by default (snapshots, pre-merge checks) (#2678)
Flip two safety mechanisms from opt-in to opt-out so all users benefit
from rollback protection and merge regression checks without manual
configuration.

- git.snapshots: false → true (creates recovery refs before destructive ops)
- git.pre_merge_check: false → "auto" in solo mode (auto-detects test runner)

Both remain configurable; users can explicitly disable with snapshots: false
or pre_merge_check: false.

Closes #2677
2026-03-26 18:15:31 -06:00
Iouri Goussev
de600c1db0 refactor(gsd): extract duplicated status guards and validation helpers (#2767)
* fix: rebuild stale workspace packages after git pull

ensure-workspace-builds.cjs only triggered a build when dist/index.js
was missing entirely. After `git pull` updates package sources, the old
dist/ stayed in place causing TypeScript type errors (bash_transform,
authMode, malformedArguments missing from compiled .d.ts files).

Now compares newest .ts mtime under src/ against dist/index.js mtime
and rebuilds any package whose sources are newer than its dist.

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

* fix(rtk): trust explicit binaryPath without existsSync check; add options object to shared rewriteCommandWithRtk

resolveRtkBinaryPath was calling existsSync on options.binaryPath, making
it impossible to inject a non-existent test binary — tests expected the
options-object API to bypass filesystem checks.

Also brings src/resources/extensions/shared/rtk.ts rewriteCommandWithRtk
in line with the same options-object signature already in src/rtk.ts.

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

* refactor(gsd): extract duplicated status guards and validation helpers

isClosedStatus(), isNonEmptyString(), and validateStringArray() were
each copy-pasted across 5-10 tool handler files with no shared module.
Extract them into status-guards.ts and validation.ts, replace all 26
inline status checks and 8 duplicated validation functions with imports.

Standardizes "inside a closed" -> "in a closed" in two reopen error
messages as a side effect of the normalization pass.

Closes #2727

* refactor(gsd): migrate state.ts isStatusDone to isClosedStatus; fix blank lines and import order

- state.ts had a private isStatusDone() identical to isClosedStatus() —
  replace with import from status-guards.ts
- Remove double blank lines left behind in plan-{milestone,slice,task}.ts
  and replan-slice.ts after local function extraction
- Fix import ordering in reassess-roadmap.ts (node built-ins first,
  status-guards/validation before gsd-db block)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:14:43 -06:00
TÂCHES
4f1ff1fe28 fix: auto-mode stops after provider errors (#2762) (#2764)
* feat: Registered 6 MCP tools (gsd_execute, gsd_status, gsd_result, gsd_…

- "packages/mcp-server/src/server.ts"
- "packages/mcp-server/src/cli.ts"
- "packages/mcp-server/src/index.ts"
- "packages/rpc-client/dist/index.d.ts"

GSD-Task: S05/T02

* docs: Added 31 integration tests, build pipeline, and consumer README f…

- "packages/mcp-server/src/mcp-server.test.ts"
- "packages/mcp-server/README.md"
- "packages/mcp-server/dist/"

GSD-Task: S05/T03

* fix: prevent auto-mode hard stop on provider errors and suppress duplicate async_job_result follow-ups (#2762)

Two compounding bugs caused auto-mode to silently die after unit completion:

1. async_job_result follow-ups fired after unit completion because deliverResult
   ran synchronously in the job promise .then() chain, racing with await_job's
   .then() that sets job.awaited=true. Deferring delivery by one microtask via
   queueMicrotask ensures await_job marks the job first.

2. Provider error pause converted to hard stop because pauseAuto resolved the
   unit promise with {status:"cancelled"} but no ErrorContext, so runUnitPhase
   treated it identically to a session-creation timeout and called stopAuto.
   Now pauseAuto accepts and forwards ErrorContext, and runUnitPhase checks for
   category:"provider" to break without hard-stopping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: update source-scan assertion for new pauseAuto signature

The structural test checked for `resolveAgentEndCancelled()` with empty
parens. Now that pauseAuto passes _errorContext, match the call prefix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:55:20 -06:00
TÂCHES
e296d8b9d9 Merge pull request #2681 from splichy/fix/provider-scoped-stream-routing
fix: exempt interactive tools from idle watchdog stall detection
2026-03-26 17:20:30 -06:00
TÂCHES
55524d4ffb Merge pull request #2687 from mastertyko/fix/copy-preferences-to-worktree
fix(gsd): include preferences.md in worktree sync and initial seed
2026-03-26 17:19:20 -06:00
Lex Christopherson
e6a01a265a Merge remote-tracking branch 'origin/main' into claude/audit-sqlite-gsd-VLoLV 2026-03-26 17:13:27 -06:00
TÂCHES
473583d349 Merge pull request #2759 from igouss/fix/tool-handlers-bypass-db-port-2726
refactor(gsd): wire tool handlers through DB port layer, remove _getAdapter from all tools
2026-03-26 17:12:24 -06:00
mastertyko
5ab375b773 fix: make transaction() re-entrant and add slice_dependencies to initSchema
Two bugs fixed:

1. transaction() now tracks nesting depth. When deleteTask/deleteSlice
   (which wrap in transaction()) are called from within an outer
   transaction() in reassess-roadmap.ts or replan-slice.ts, the inner
   call skips BEGIN/COMMIT since SQLite doesn't support nested
   transactions. This fixes:
   - reassess-handler.test.ts: 3 failing tests
   - replan-handler.test.ts: 4 failing tests
   All errors were: 'cannot start a transaction within a transaction'

2. slice_dependencies table and v13/v14 indexes were only created in
   migrateSchema (for upgrades from older versions) but missing from
   initSchema (for fresh databases). New databases started at schema
   version 14 but never created the table, causing 'no such table:
   slice_dependencies' when deleteSlice was called.
2026-03-26 23:59:46 +01:00
mastertyko
523e910f21 fix: remove preferences.md from ROOT_STATE_FILES to prevent back-sync overwrite
preferences.md was in ROOT_STATE_FILES which caused syncWorktreeStateBack()
to overwrite the project root's authoritative copy with the worktree's
stale copy. The forward-sync (main → worktree) is already handled
separately in syncGsdStateToWorktree() as additive-only.

Fixes the failing CI test:
  worktree-preferences-sync.test.ts:107
  '#2684: syncWorktreeStateBack does NOT overwrite project root preferences.md'

Also updates preferences-worktree-sync.test.ts to assert preferences.md
is NOT in ROOT_STATE_FILES (it must be handled separately).
2026-03-26 23:57:17 +01:00
Iouri Goussev
02e813c0a3 fix: wire tool handlers through DB port layer, remove _getAdapter from all tools
Fixes #2726. Tool handlers were bypassing the DB port layer by calling
_getAdapter() directly for raw SQL. Replace all such callsites with
proper exported DB functions.

- Add setTaskSummaryMd(), setSliceSummaryMd() to gsd-db.ts
- Extend updateMilestoneStatus() to accept optional completedAt param
- Add deleteVerificationEvidence(), deleteAssessmentByScope() to gsd-db.ts
- complete-task.ts: use updateTaskStatus, setTaskSummaryMd, deleteVerificationEvidence
- complete-slice.ts: use updateSliceStatus, setSliceSummaryMd
- complete-milestone.ts: use updateMilestoneStatus for both complete and rollback
- validate-milestone.ts: use insertAssessment, deleteAssessmentByScope
- plan-slice.ts, plan-milestone.ts: remove dead _getAdapter import
2026-03-26 18:56:06 -04:00
Lex Christopherson
f28aca2ee1 Merge branch 'main' into fix/unified-error-classifier
Resolve conflicts: keep unified classifyError (PR intent), remove old
classifyProviderError. Port stream_exhausted pattern from main into
unified CONNECTION_RE and add corresponding test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:47:02 -06:00
Lex Christopherson
7d0130fa0f Merge branch 'main' into fix/unified-error-classifier
Resolve conflicts in provider-error-pause.ts and provider-errors.test.ts.
Add stream_exhausted(_without_result) pattern to unified CONNECTION_RE
(ported from main's classifyProviderError addition).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:43:26 -06:00
mastertyko
032088e409 fix(gsd): move state machine guards inside transaction in 5 tool handlers (#2752)
plan-task, plan-slice, plan-milestone, reassess-roadmap, and
replan-slice all ran state machine guards (getSlice, getMilestone,
getTask) outside the transaction() callback, then performed writes
in a separate transaction. This created a TOCTOU race: two agents
could both pass the guard simultaneously and both write successfully.

Fix: move all guard checks into the transaction() callback using
the guardError pattern already used by complete-task, complete-slice,
reopen-task, and reopen-slice. The SQLite write lock now covers both
the guard reads and the subsequent writes atomically.

Closes #2723
2026-03-26 16:39:19 -06:00
Jeremy McSpadden
f8814f5a15 refactor(pi-ai): replace model-ID pattern matching with capability metadata (#2548)
* refactor(pi-ai): replace model-ID pattern matching with capability metadata

Add ModelCapabilities to Model<TApi> and a CAPABILITY_PATCHES mechanism
so call sites read model.capabilities fields instead of parsing model IDs
or hardcoding provider names.

- types.ts: add ModelCapabilities interface (supportsXhigh, requiresToolCallId,
  supportsServiceTier, charsPerToken) and capabilities?: ModelCapabilities to
  Model<TApi>
- models.ts: add CAPABILITY_PATCHES table applied at registry init; patches
  declare GPT-5.x and Opus 4.6 capabilities once instead of repeating ID
  checks at every call site; supportsXhigh() now reads capabilities only
- service-tier.ts: extract SERVICE_TIER_MODEL_PREFIXES constant so the gating
  list has a single named home; add path comment pointing to issue #2546 for
  the full capability-driven follow-up

No behaviour change. New models and providers can declare capabilities in
their model definitions without touching function logic.

Closes #2546

* fix(pi-ai): apply capability patches to custom/discovered/extension models

Models constructed outside the static pi-ai registry (custom models
from models.json, extension-registered models, discovered models)
bypassed CAPABILITY_PATCHES — causing supportsXhigh() to silently
return false for GPT-5.x or Opus 4.6 variants registered through
those paths.

Export applyCapabilityPatches() from pi-ai and call it in ModelRegistry
after model assembly in all three construction paths: loadModels(),
applyProviderConfig(), and discoverModels().

Add regression tests covering patching, precedence, idempotency,
and synthetic models that mimic the custom/extension path.

Closes #2546
2026-03-26 16:38:29 -06:00
Claude
2023291dca perf(gsd-db): comprehensive SQLite audit fixes — indexes, caching, safety, reconciliation
13 improvements from a full audit of the SQLite DB system powering /gsd auto:

Performance:
- Add 5 missing indexes for hot-path dispatch queries (schema v13)
- Add PRAGMA synchronous=NORMAL, cache_size, mmap_size, temp_store tuning
- Implement prepared statement caching in DbAdapter (eliminates re-parse)
- Replace getActiveSliceFromDb N+1 query with single json_each() query
- Add lightweight query variants (getActiveMilestoneIdFromDb, getSliceTaskCounts, etc.)
- Batch enforceMemoryCap into single UPDATE (was N individual updates)

Safety:
- Wrap deleteSlice/deleteTask in transactions (prevents orphaned rows on crash)
- Harden reconcileWorktreeDb path sanitization (reject quotes, semicolons, nulls)
- Fix memory ID race condition (insert-then-derive from AUTOINCREMENT seq)

Completeness:
- Extend worktree reconciliation to merge all 7 tables (was only 3)
- Add slice_dependencies junction table for indexed dep queries (schema v14)
- Add DB vacuuming (incremental on close, full VACUUM export for milestones)
- Update dead-code sequence column comments to "ordering hint"

All 22 DB-related tests pass (gsd-db, memory-store, worktree-db).

https://claude.ai/code/session_019h5VhLuSYNnQEd6kz9otwk
2026-03-26 22:38:23 +00:00