Commit graph

1229 commits

Author SHA1 Message Date
Lex Christopherson
811680f5b6 fix: make workflow event hash platform-deterministic
The hash included `ts` in the input despite the docstring promising
it was "independent of ts/actor/session". On Windows, millisecond
timer resolution caused two calls within the same tick to get
different timestamps, producing different hashes for identical
cmd+params.

Remove `ts` from the hash input to match documented behavior.
Revert continue-on-error on windows-portability now that the
root cause is fixed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:20:35 -06:00
Lex Christopherson
ae0029b49f fix(gsd): create empty DB for fresh projects with empty .gsd/ (#2510)
ensureDbOpen() and the auto-start DB lifecycle block both gated DB
creation on the presence of Markdown files (DECISIONS.md, REQUIREMENTS.md,
milestones/). In a brand new project, .gsd/ exists but contains no
Markdown yet, so gsd_decision_save returned db_unavailable and the
agent derailed.

Create an empty DB whenever .gsd/ exists, regardless of Markdown content.
Migration runs only when Markdown files are present.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 10:29:53 -06:00
TÂCHES
9490f2c45c Merge pull request #2439 from jeremymcs/fix/remote-token-hydration-on-startup
fix(remote-questions): hydrate remote channel tokens from auth.json on startup
2026-03-25 10:10:00 -06:00
TÂCHES
57fe2e08cf Merge pull request #2507 from jeremymcs/fix/prompt-single-writer-tool-api-alignment
fix(gsd): align prompts with single-writer tool API
2026-03-25 09:36:45 -06:00
Jeremy McSpadden
2fada22c63 fix(gsd): align prompts with single-writer tool API
Replace direct file writes and manual DECISIONS.md/REQUIREMENTS.md
mutations in GSD prompts with the correct gsd_* tool calls:

- `gsd_summary_save` for RESEARCH, CONTEXT, and SUMMARY artifacts
- `gsd_requirement_update` instead of direct REQUIREMENTS.md edits
- `gsd_decision_save` instead of append-to-DECISIONS.md
- `gsd_plan_slice` instead of manual plan file writes in guided-plan-slice

Also document intentional exceptions: quick-task (no milestone context,
outside auto-mode lifecycle) and rethink park/unpark/reorder/discard
(no tool API exists for these milestone-lifecycle operations yet).

Adds "never edited manually" clarification to system.md checkbox docs.
2026-03-25 10:34:34 -05:00
Lex Christopherson
5c34ab2e60 merge: resolve conflict in complete-milestone.ts — keep verificationPassed + TOCTOU guards
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:31:50 -06:00
Lex Christopherson
1d44a89634 fix(gsd): integration-proof — check DB state not roadmap projection after reset
After slice completion + reset, the roadmap projection may not be re-rendered
in the new table format. DB state is authoritative — assert on DB status
instead of parsing projection files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:27:28 -06:00
Jeremy McSpadden
ddbf3105a3 fix(gsd): block milestone completion when verification fails (#2500)
Closes #2499
2026-03-25 09:25:54 -06:00
Lex Christopherson
887d940a2c fix(gsd): relax integration-proof cross-validation for table-format roadmap
DB state is authoritative (single-writer engine). The filesystem parser
doesn't parse the new table-format roadmap projections, so cross-validation
is relaxed to check DB correctness only. Undo/reset roadmap check accepts
either checkbox or emoji format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:23:15 -06:00
Lex Christopherson
5a24f1df80 fix(gsd): update integration-proof tests for table-format roadmap projections
Roadmap now uses emoji table (/) instead of markdown checkboxes ([x]/[ ]).
Plan checkbox format changed from **T01:** to **T01: title**.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:17:05 -06:00
Lex Christopherson
a23c19e77e fix(gsd): update test assertions for schema v11, prompt changes, and removed completedUnits
- Schema version assertions: 10→11 in gsd-db, md-importer, memory-store tests
- Prompt contract tests: update for gsd_complete_task/gsd_complete_slice tool names
- Milestone transition test: update for archive-then-clear pattern
- Plan-milestone test: update for table-format roadmap projection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:12:15 -06:00
Lex Christopherson
8119e12ce9 fix(gsd): update test files for removed completedUnits, writeLock signature, and type changes
- Remove completedUnits from WorkerInfo/SessionLockData test object literals
- Remove verifyExpectedArtifact/writeUnitRuntimeRecord from LoopDeps mocks
- Fix writeLock call signatures (remove numeric completedUnits arg)
- Fix idle-recovery imports (moved to auto-recovery.ts)
- Add full_plan_md to TaskRow test objects
- Fix WorkflowEvent type in test (exclude session_id from Omit)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 08:54:29 -06:00
Lex Christopherson
63dea156c3 fix(gsd): remove stale completedUnits refs, fix writeLock callers, add missing imports
- Remove completedUnits from dashboard, context, parallel, guided-flow, merge
- Fix writeLock callers to match new (basePath, unitType, unitId, sessionFile?) signature
- Add gsdRoot, atomicWriteSync, verifyExpectedArtifact, writeUnitRuntimeRecord imports to phases.ts
- Add full_plan_md to workflow-manifest snapshot mapping

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 08:54:29 -06:00
Lex Christopherson
3a12089355 fix(gsd): harden single-writer engine — close TOCTOU, intercept bypasses, status inconsistencies
- Write intercept: block edit + bash tools (not just write), case-insensitive
  patterns for macOS, resolve ".." path segments, use BLOCKED_WRITE_ERROR constant
- TOCTOU: move all guard reads inside transaction callbacks across all 5 handlers
  (complete-task, complete-slice, complete-milestone, reopen-task, reopen-slice)
- Wrap reopen-task in a transaction (was bare updateTaskStatus call)
- Fix "done" vs "complete" status inconsistency: complete-slice task filter,
  projection SUMMARY rendering, and regenerateIfMissing all accept both statuses
- Workflow reconcile: sync-lock for concurrent access, stable timestamp sort,
  write event log before DB replay, wrap replayEvents in transaction, include ts
  in event hash, add session_id to parsed conflict events, replay non-conflicting
  events after last conflict resolution
- Manifest: wrap snapshotState queries in deferred transaction for consistent
  snapshot, validate manifest structure on read
- Projections: fix regenerateIfMissing SUMMARY to check individual files not just
  directory, return false for async STATE regeneration, use logWarning consistently
- Logger: hasWarnings() checks for actual warnings (not just buffer.length > 0),
  stderr output on audit write failures

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 08:53:53 -06:00
Jeremy McSpadden
6ed5b01507 test(gsd): add tests for v3 reopen tools, unit ownership, and projection regression
37 new tests across 4 files covering v3 features that had no test
coverage, plus regression tests for the projection bug fixes:

- reopen-task.test.ts (8): success path (reset to pending, no side
  effects on other tasks) + 6 failure paths (empty ID, missing
  milestone/slice/task, closed parents, already pending)
- reopen-slice.test.ts (7): success path (reset slice + all tasks,
  single task variant) + 5 failure paths (empty ID, missing entities,
  closed milestone, already in_progress)
- unit-ownership.test.ts (14): key builders, claim/get/release CRUD,
  overwrite semantics, multi-unit independence, checkOwnership
  (opt-in when no actorName, null when unclaimed, pass when owner
  matches, error when mismatch)
- projection-regression.test.ts (8): renderPlanContent checkbox for
  "complete"/"done"/"pending" status + mixed, parsePlan-compatible
  bold format, renderRoadmapContent status icons

All 37 tests pass. Zero regressions.
2026-03-25 08:53:36 -06:00
Jeremy McSpadden
a1592c984b feat(gsd): single-writer engine v3 — state machine guards, actor identity, reversibility
Three work streams bundled into one phase to close the behavioral control
gaps identified in the v2 handler audit:

Stream 1 — State machine guards on all 8 tool handlers:
- Entity existence checks before mutations (milestone, slice, task)
- Valid status transition enforcement (can't double-complete, can't re-plan
  closed work, can't complete inside a closed parent)
- depends_on validation for plan-milestone (deps must exist + be complete)
- blockerTaskId verification in replan-slice (must exist + be complete)
- Deep task check in complete-milestone (all tasks, not just slice status)

Stream 2 — Actor identity + persistent audit log:
- WorkflowEvent extended with actor_name, trigger_reason, session_id
- Engine-generated UUID session_id stable per process lifetime
- All 8 handlers accept optional actorName/triggerReason and pass through
- workflow-logger now flushes to .gsd/audit-log.jsonl (survives context resets)
- New setLogBasePath() and readAuditLog() API

Stream 3 — Reversibility + unit ownership:
- New gsd_task_reopen handler (reset task to pending with full guards)
- New gsd_slice_reopen handler (reset slice + all tasks with transaction)
- Opt-in unit ownership via .gsd/unit-claims.json (claim/release/check)
- Ownership enforced in complete-task and complete-slice when claims exist
- insertReplanHistory converted to upsert via schema v11 unique index

Bug fixes (pre-existing):
- renderPlanContent checkbox: checked "done" but tasks are "complete"
- renderRoadmapContent: same "done" vs "complete" mismatch
- renderPlanContent format: **T01:** title didn't match parsePlan regex
- Tests updated to seed DB entities and match projection output format
2026-03-25 08:53:36 -06:00
Jeremy McSpadden
5130b04d5a fix(write-intercept): close bare-relative-path bypass in STATE.md regex
The previous regex `/[/\\]\.gsd[/\\]STATE\.md$/` required a path
separator *before* `.gsd`, so a bare relative path like `.gsd/STATE.md`
(no leading directory component) was not blocked. If the file doesn't
exist yet, `realpathSync` throws and the bare path slipped through
undetected.

Fix: change both patterns to `(^|[/\\])` so paths starting with `.gsd/`
are caught regardless of whether a separator precedes them.

Caught during e2e team verification (write-intercept-e2e agent).
Updated test to assert the bare path is now blocked.
2026-03-25 08:53:02 -06:00
Jeremy McSpadden
eab3851a56 test(gsd): gap-fill tests for single-writer engine v2 modules
62 new tests across 6 files covering the modules introduced in the v2
single-writer discipline layer that had no test coverage:

- write-intercept.test.ts (15): isBlockedStateFile path matching for
  STATE.md (blocked) vs other .gsd/ files (allowed), BLOCKED_WRITE_ERROR
- sync-lock.test.ts (7): acquireSyncLock/releaseSyncLock including
  lock file creation, round-trip, and stale lock override
- workflow-events.test.ts (15): appendEvent (creates dir, valid JSONL,
  deterministic hash), readEvents (empty, parse, skip corrupted),
  findForkPoint (edge cases), compactMilestoneEvents (archive/truncate)
- workflow-manifest.test.ts (8): snapshotState, writeManifest,
  readManifest (null/parse/version guard), bootstrapFromManifest
  round-trip restore
- workflow-projections.test.ts (17): renderPlanContent pure function —
  H1/Goal/Demo/Tasks structure, [x]/[ ] checkboxes, Estimate/Files/
  Verify/Duration sublines, task ordering
- post-mutation-hook.test.ts (5): regression — verifies that after
  handleCompleteTask, event-log.jsonl and state-manifest.json are
  both written by the post-mutation hook; also confirms hook failures
  are non-fatal (handler still returns success)

All 62 tests pass. Zero regressions introduced.
2026-03-25 08:53:02 -06:00
Jeremy McSpadden
1c0cca4f76 feat(gsd): single-writer state engine v2 — discipline layer on DB architecture
Ports the single-writer state architecture from PRs #2288–#2293 onto the
current upstream codebase (schema v10, polymorphic engine). Original PRs
were based on a pre-v5 schema with incompatible column names and predated
the WorkflowEngine interface refactor.

New files:
- workflow-events.ts: append-only event log (.gsd/event-log.jsonl)
- workflow-manifest.ts: full DB snapshot after every mutation (crash recovery)
- workflow-projections.ts: renders PLAN/ROADMAP/SUMMARY/STATE.md from DB
- workflow-migration.ts: migrates legacy markdown projects into DB
- workflow-reconcile.ts: event log replay for diverged worktrees
- workflow-logger.ts: structured error/warning accumulation
- sync-lock.ts: advisory lock for concurrent worktree syncs
- write-intercept.ts: blocks direct writes to STATE.md
- auto-artifact-paths.ts: central artifact path registry

Modified:
- All 8 tool handlers (complete-task, complete-slice, plan-slice, etc.)
  now wrap mutations in atomic transactions + emit event log + write
  manifest + regenerate markdown projections after every command
- state.ts: telemetry counters for DB vs filesystem derivation paths
- register-hooks.ts: write-intercept wired into tool_call hook
- doctor.ts/doctor-checks.ts/doctor-types.ts: engine health checks,
  fixable:false on completion-state issues, removed placeholder stubs
- auto.ts + supporting files: removed completedUnits tracking globally,
  removed unit-runtime record reads/writes, removed inline doctor runs
- auto-post-unit.ts: detectRogueFileWrites (6 unit types), removed
  doctor health tracking block, added regenerateIfMissing on retry
- 3 prompts updated to use gsd_* tool API instead of direct file edits

ADR-004: GSD had multiple writers racing to edit the same markdown files
concurrently, causing race conditions, stale reads, and corrupt state.
The single-writer discipline layer makes markdown files derived artifacts
(generated from DB after every command) rather than authoritative sources.

Supersedes closed PRs: #2288, #2289, #2290, #2291, #2292, #2293

AI assistance: implemented with Claude Code (GSD/Claude).
2026-03-25 08:53:02 -06:00
Tom Boucher
9574c5796d fix(voice): fix misleading portaudio error on PEP 668 Linux systems (#2403) (#2407)
Two bugs in ensureLinuxReady():

1. Branch ordering: "ModuleNotFoundError: No module named 'sounddevice'"
   contains the word "sounddevice", so the portaudio branch matched first,
   producing the misleading "install libportaudio2" message even when
   libportaudio2 was already installed.

2. No venv auto-creation: On PEP 668 systems (Ubuntu 23.10+), system pip
   is blocked. The code trusted speech-recognizer.py to self-install deps,
   but its pip install also fails. Now ensureLinuxReady() auto-creates
   ~/.gsd/voice-venv when the sounddevice module is missing.

Fixes:
- Extract diagnoseSounddeviceError() with correct branch ordering
  (check "No module"/"ModuleNotFoundError" BEFORE "sounddevice")
- Add ensureVoiceVenv() to auto-create venv with sounddevice+requests
- Refactor into linux-ready.ts for testability
- Add 20 unit tests covering all error diagnosis paths and venv creation

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 08:45:39 -06:00
Jay The Reaper
68902466ac fix(core): address PR review feedback for non-apikey provider support (#2452)
- Strip apiKey from options at streamSimple registration boundary for
  externalCli/none providers — enforced structurally, not by convention
- Add registration-time validation: externalCli/none requires streamSimple,
  rejects contradictory apiKey, improved error messages mentioning authMode
- Cache legacy hook module imports to prevent side-effect double-execution
- Add isReady() trust boundary documentation
- Add inline comments on compaction-orchestrator apiKey flow
- Refactor package-commands.test.ts to use t.after() cleanup
- Add lifecycle-hooks.test.ts with 24 unit tests for readManifestRuntimeDeps,
  collectRuntimeDependencies, verifyRuntimeDependencies, resolveLocalSourcePath
- Expand model-registry-auth-mode.test.ts with streamSimple apiKey boundary
  tests and registration validation tests (80 total tests across all files)
- Add afterRemove deleted-directory edge case test
- Fix help-text.ts wording: "lifecycle hooks" → "post-install validation"
- Fix event.message null check documentation (intentional tightening)
2026-03-25 08:45:20 -06:00
Jeremy McSpadden
7b162fe4ce fix(gsd): change default isolation mode from worktree to none (#2481)
When no preferences.md exists, getIsolationMode() and
shouldUseWorktreeIsolation() defaulted to "worktree", which requires
git branch infrastructure (milestone/<MID> branches) that isn't
automatically set up. This caused milestone-complete to fail with
"branch doesn't exist" when users worked directly on main without
configuring preferences.

Change the default to "none" (work on current branch) across all five
locations: getIsolationMode(), shouldUseWorktreeIsolation(),
MODE_DEFAULTS for solo/team, doctor.ts, and doctor-checks.ts.
Worktree isolation is now explicit opt-in via preferences.md.

Closes #2480
2026-03-25 08:44:08 -06:00
Jeremy McSpadden
bf54012d1f fix(loader): add startup checks for Node version and git availability (#2463)
Closes #2461
2026-03-25 08:43:54 -06:00
Jeremy McSpadden
43aca75b98 fix(gsd): add worktree lifecycle events to journal (#2486)
* fix(gsd): add worktree lifecycle events to journal

* fix(gsd): widen source scan window in merge-conflict test

The journal event additions in _mergeWorktreeMode pushed the
MergeConflictError re-throw past the 5000-char scan window used
by merge-conflict-stops-loop.test.ts. Increase to 6000 to
accommodate the added emitJournalEvent calls.

* fix(gsd): restore cwd before temp dir cleanup in journal test

On Windows, rmSync fails with EPERM when the process cwd is inside
the directory being deleted. Save and restore the original cwd in
afterEach before cleanup.
2026-03-25 08:43:39 -06:00
Jeremy McSpadden
bc7669bf0f feat(gsd): add workflow-logger and wire into engine, tool, manifest, reconcile paths (#2494)
* feat(gsd): add workflow-logger for structured operational error/warning accumulation

Adds workflow-logger.ts — a centralized in-memory accumulator for operational
warnings and errors across the GSD engine pipeline.

Key additions vs the standalone/workflow-logger branch:
- Fix hasWarnings() to filter severity === "warn" (was returning _buffer.length > 0,
  incorrectly returning true for error-only buffers)
- Add hasAnyIssues() for callers that want to check for either severity
- Add drainAndSummarize() atomic helper to prevent the drain-before-summarize footgun
- Document singleton safety requirement: callers must _resetLogs() per unit
- Document always-on stderr policy (intentional, unlike debug-logger opt-in)
- Move test from engine/ to tests/ to match project test discovery glob
- Expand test suite from 15 to 32 cases: stderr output, context handling,
  hasWarnings with errors-only buffer, drainAndSummarize, double-drain,
  warnings-only summarize, formatForNotification context exclusion,
  buffer limit robustness, ISO timestamp validation

* feat(gsd): wire workflow-logger into engine, tool, manifest, and reconcile paths

Routes 34 previously silent/raw-stderr error and warning sites through the
structured workflow-logger so the auto-loop can drain and surface root causes.

Changes by component:

  tool (12 sites) — bootstrap/db-tools.ts
    All 12 gsd_* tool handler catch blocks replaced from process.stderr.write
    to logError("tool", ...) with { tool, error } context.

  engine (9 sites) — auto/phases.ts (7), auto/run-unit.ts (2)
    7 silent catches in phases.ts annotated with logWarning("engine", ...):
    health gate, milestone merge, completed-units archive, STATE.md rebuild,
    baseline char count, prompt reorder failure, disk flush.
    2 silent catches in run-unit.ts: chdir and clearQueue failures.

  manifest (8 sites) — db-writer.ts
    nextDecisionId, saveDecisionToDb, updateRequirementInDb, saveArtifactToDb
    error paths replaced with logError("manifest", ...).
    Shrinkage guard replaced with logWarning("manifest", ...).

  reconcile (5 sites) — auto-worktree.ts (2), worktree-manager.ts (3)
    Post-create hook failure, teardown directory persistence, stale worktree
    removal, submodule stash, stash failure — all replaced with
    logWarning("reconcile", ...) with { worktree } context.

No control flow changed. TypeScript clean. 32/32 tests pass.

* fix(gsd): use info.name instead of global name in auto-worktree logWarning call
2026-03-25 08:43:20 -06:00
Jeremy McSpadden
64e2604782 fix(remote-questions): hydrate remote channel tokens from auth.json on startup
Token saved via `/gsd remote discord` (or `/gsd keys add discord_bot`) is
persisted to auth.json but was not being restored to process.env on the next
launch. resolveRemoteConfig() and getRemoteConfigStatus() both read only from
process.env, so the token appeared missing on every fresh session, triggering
the 'DISCORD_BOT_TOKEN not set — remote questions disabled' warning.

Fix: add hydrateRemoteTokensFromAuth() that reads discord_bot, slack_bot, and
telegram_bot API keys from auth.json and populates the corresponding env vars
(DISCORD_BOT_TOKEN, SLACK_BOT_TOKEN, TELEGRAM_BOT_TOKEN) before the env check,
but only when the vars are not already set. Called at the top of both public
functions so hydration fires regardless of which codepath triggers config
resolution.

- Silently no-ops if auth.json is absent or AuthStorage is unavailable
- Does not overwrite env vars already set (env takes precedence)
- Uses require() so AuthStorage failures don't crash the extension

Tests: 5 new source-level and behavioral assertions covering hydration call
ordering, provider map coverage, skip-when-set guard, and null-config path.
2026-03-25 09:41:59 -05:00
TÂCHES
e2eb5cecf2 fix(gsd): handle retentionDays=0 on Windows + run windows-portability on PRs (#2460)
Two changes:

1. pruneActivityLogs: when retentionDays is 0, skip mtime comparison and
   unconditionally remove all files except highest-seq. On Windows, NTFS
   timestamp resolution meant freshly-created files could have mtime >=
   Date.now() at cutoff calculation, so none were pruned.

2. CI: remove the push-to-main gate on windows-portability so it runs on
   PRs too — catches Windows failures before merge instead of after.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:17:22 -06:00
Tom Boucher
5b0c24a92c feat(web): make web UI mobile responsive (#2354)
* feat(web): make web UI mobile responsive

Fixes #2274

Add mobile-first responsive design to the GSD web UI:
- Viewport meta tag via Next.js Viewport export
- Collapsible sidebar as slide-out drawer on mobile with hamburger menu
- Milestone explorer as right-side drawer on mobile with bottom bar toggle
- Responsive header: hide project label, scope badge, beta badge on small screens
- Dashboard: responsive grid (1col mobile -> 2col sm -> 4col xl), responsive padding
- Status bar: hide secondary info on small screens, responsive text sizing
- Touch-friendly 44px minimum tap targets on mobile nav items
- Mobile CSS utilities in globals.css (overlay, drawer transitions)
- 19 structural tests verifying responsive classes exist in key components

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

* ci: retrigger after stale check

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:07:39 -06:00
Tom Boucher
3522b54618 fix(gsd): isInheritedRepo conflates ~/.gsd with project .gsd when git root is $HOME (#2398)
When the user's home directory is a git repo (e.g. dotfile managers like
yadm), isInheritedRepo() found ~/.gsd and concluded that subdirectories
were part of an existing GSD project — loading the wrong project state.

Extract isProjectGsd() to distinguish a project .gsd (symlink to external
state, or legacy directory) from the global ~/.gsd state directory by
comparing against the resolved GSD_HOME path.

Fixes #2393

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:07:22 -06:00
Tom Boucher
be4037be90 fix: reconcile disk milestones missing from DB in deriveStateFromDb (#2416) (#2422)
After migration to DB-backed state, milestones on disk that were never
imported into the DB became invisible. deriveStateFromDb now scans the
milestones directory and injects synthetic entries for any disk-only
milestones, then re-sorts to maintain canonical order.

Fixes #2416

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:06:47 -06:00
Tom Boucher
aa3ac89bf8 fix(auto): reset recoveryAttempts on unit re-dispatch (#2322) (#2424)
The dispatch-time writeUnitRuntimeRecord call in runUnitPhase did not
reset recoveryAttempts, so the counter from a prior execution's timeout
carried over to subsequent dispatches. This caused re-dispatched units
to be instantly skipped (recoveryAttempts >= maxRecoveryAttempts) with
no steering message or second chance.

Add `recoveryAttempts: 0` to the dispatch-time runtime record write so
each execution starts with its full recovery budget.

Fixes #2322

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:06:23 -06:00
Tom Boucher
e115909fd0 fix: detect and preserve submodule state during worktree teardown (#2337) (#2425)
Worktree teardown with --force destroyed uncommitted changes in
submodule directories. Now detects .gitmodules, checks submodule
status for uncommitted changes, and stashes them before removal.
When submodules have dirty state, attempts non-force removal first.

Fixes #2337

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:06:04 -06:00
Tom Boucher
fa8e5500ac fix(auto-start): handle survivor branch recovery in phase=complete (#2358) (#2427)
When bootstrapAutoSession finds a survivor milestone branch and the
derived state phase is "complete", recovery was skipped entirely because
the survivor branch detection only triggered for phase === "pre-planning".
This left the milestone worktree/branch alive and routed bootstrap into
showSmartEntry instead of running finalization (merge, cleanup).

Changes:
- Broaden survivor branch detection to also check phase === "complete"
- Add explicit finalization path: when hasSurvivorBranch && phase ===
  "complete", call resolver.mergeAndExit() to run the pending merge and
  worktree cleanup, then re-derive state so the normal flow continues
- After finalization, clear hasSurvivorBranch so the "all milestones
  complete" or "next milestone" path runs correctly

Fixes #2358

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:05:39 -06:00
TÂCHES
ea8976d16e feat(gsd): add /gsd rethink command for conversational project reorganization (#2459)
Collects a snapshot of all milestones (status, dependencies, slice progress,
queue order) and dispatches a prompt that turns Claude into a reorganization
assistant. Supports reordering, parking, unparking, discarding, adding
milestones, and updating dependencies through conversation.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:04:24 -06:00
TÂCHES
109f8e4461 fix(gsd): widen test search window for CRLF portability on Windows (#2458)
The completed-units-metrics-sync source-scanning test used a 700-char
window that was too small when Windows CRLF line endings inflated byte
offsets, causing the archive keyword check to miss by ~2 chars.
Widens the window to 1200 chars and lowercases the comparison so
"Archive" and "cpSync" match regardless of case or line ending style.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:55:36 -06:00
TÂCHES
c77148632b fix(gsd): preserve rich task plans on DB roundtrip (#2450) (#2453)
Add `full_plan_md` TEXT column to the tasks table, following the
established `full_summary_md` pattern. When populated,
`renderTaskPlanFromDb()` writes the stored markdown directly instead
of regenerating a minimal version from individual DB fields.

- DB schema: add `full_plan_md` column (migration v11)
- `TaskPlanningRecord` / `upsertTaskPlanning`: accept and persist `fullPlanMd`
- `renderTaskPlanFromDb`: prefer `full_plan_md` when non-empty
- plan-task, plan-slice, replan-slice tools: pass `fullPlanMd` through

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:40:56 -06:00
mastertyko
98f5daeda8 feat(gsd): add renderCall/renderResult previews to DB tools (#2273)
Add inline rendering to gsd_decision_save, gsd_requirement_update,
gsd_summary_save, and gsd_milestone_generate_id so the TUI shows
meaningful context during and after tool execution instead of generic
static labels.

Before: ' Save Decision' (no context)
After:  ' decision_save [architecture] Use SQLite — better-sqlite3'
        '✓ Decision D042 saved → DECISIONS.md'

Follows the established pattern from context7 and search-the-web:
{toolTitle bold name} {accent primary arg} {muted/dim metadata}

Closes #2236
2026-03-24 23:31:56 -06:00
Tom Boucher
58631bba2b fix: merge worktree back to main when stopAuto is called after milestone completion (#2317) (#2430)
stopAuto Step 4 previously always called exitMilestone(preserveBranch: true),
which preserved the worktree branch but never merged it back. When auto-mode
stopped after complete-milestone, the code stayed stranded on the worktree branch.

Now checks if the milestone has a SUMMARY file (completion signal) and calls
mergeAndExit instead, so completed milestone code reaches main.

Fixes #2317

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:21:00 -06:00
TÂCHES
61fcc0fbee Merge pull request #2451 from gsd-build/fix/doctor-pending-slice-false-errors
fix(gsd): skip doctor directory checks for pending slices
2026-03-24 23:18:41 -06:00
Tom Boucher
515fe0295b feat(gsd): add /gsd mcp command for MCP server status and connectivity (#2362)
Adds a new `/gsd mcp` slash command that shows configured MCP servers,
their connection status, and available tools. Supports two subcommands:
- `/gsd mcp status` (default) — overview of all servers
- `/gsd mcp check <server>` — detailed info for a specific server

Exports a `getConnectionStatus()` helper from the mcp-client extension
so the command can query live connection state.

Fixes #1489

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:18:31 -06:00
Lex Christopherson
ed95e70534 fix(gsd): skip doctor directory checks for pending slices (#2446)
Doctor flagged missing_slice_dir and missing_tasks_dir as ERROR for
slices with status "pending" — slices that plan-milestone inserted but
haven't been dispatched yet. These directories are created lazily by
ensurePreconditions() at dispatch time, so their absence is expected.

Preserve the DB status field in the slice mapping and skip directory
checks entirely for pending slices.

Closes #2446

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:14:47 -06:00
TÂCHES
b9ff5d5052 fix(gsd): migrate completion/validation prompts to DB-backed tools (#2449)
* fix(gsd): migrate completion/validation prompts to DB-backed tools and fix pipeline inconsistencies (#2444)

- Create gsd_validate_milestone tool (handler + DB registration) using assessments table
- Update complete-milestone.md to use gsd_complete_milestone instead of manual file writes
- Update validate-milestone.md to use gsd_validate_milestone + gsd_reassess_roadmap for remediation
- Add buildSkillActivationBlock() to 4 missing prompt builders (complete-milestone, validate-milestone, run-uat, reassess-roadmap)
- Remove dead completedSliceSummaryPath variable from reassess-roadmap builder
- Remove dead "degraded fallback" sections from replan-slice.md and reassess-roadmap.md
- Fix plan-slice.md double-tool instruction (gsd_plan_slice already persists tasks)
- Fix inconsistent commit/write instructions in complete-milestone.md

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

* fix: update tests for new tool registration and prompt changes

- Add gsd_validate_milestone to tool-naming RENAME_MAP (24→26 tools)
- Update prompt-contracts assertions for removed fallback text and singular DB tool phrasing
- Restore {{roadmapPath}}, {{assessmentPath}}, {{planPath}}, {{replanPath}} template vars in prompts for context

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

* fix: restore {{milestoneSummaryPath}} template var in complete-milestone prompt

Test expects the milestone summary path reference in the prompt content.

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-24 23:08:27 -06:00
TÂCHES
9a6a341b57 fix(gsd): prevent saveArtifactToDb from overwriting larger files with truncated content (#2442) (#2447)
When a file already exists on disk and the new content is <50% of the
existing file size, skip the disk write and store the existing file
content in the DB instead. This prevents data loss when research prompts
write full content via `write` then `gsd_summary_save` is called with
an abbreviated summary.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:36:19 -06:00
Tom Boucher
cf0fe6c571 fix: stop auto loop on real code merge conflicts (#2330) (#2428)
MergeConflictError from squash merge was caught silently in
worktree-resolver's mergeAndExit, so the auto loop retried the
merge forever. Now:

1. worktree-resolver re-throws MergeConflictError after cleanup
2. auto/phases.ts catches it at all 3 mergeAndExit call sites
3. On conflict, stops the loop with a clear error message

Fixes #2330

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:36:06 -06:00
Tom Boucher
df269b3b00 feat: complete offline mode support (#2429)
* feat: complete offline mode support for local-only model setups

- Add isLocalModel() to detect localhost/127.0.0.1/0.0.0.0/::1/unix sockets
- Add isAllLocalChain() to verify all registry models are local
- Validate --offline flag rejects remote models with clear error
- Auto-enable PI_OFFLINE when all configured models are local
- Return dummy API key for local models to skip auth validation
- Filter web search results in offline mode (chat-controller + tool-execution)
- Add ECONNREFUSED/ENOTFOUND/ENETUNREACH to INFRA_ERROR_CODES for immediate
  failure (no retry) when network is intentionally unavailable
- Add comprehensive test suite (17 tests)

Fixes #2341

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

* fix(test): update infra-error test for new offline-mode error codes

The offline mode feature added ECONNREFUSED, ENOTFOUND, and ENETUNREACH
to INFRA_ERROR_CODES but the test still asserted size === 6. Update the
count to 9 and add detection tests for the three new codes.

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-24 22:35:45 -06:00
Tom Boucher
17ce3085f9 fix: classify terminated/connection errors as transient in provider error handler (#2309) (#2432)
classifyProviderError now recognizes terminated, connection reset, connection
refused, fetch failed, and other network errors as transient. These get a 15s
backoff delay and auto-resume instead of being treated as permanent failures
requiring manual intervention.

Fixes #2309

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:35:19 -06:00
Tom Boucher
5d0c6311f1 fix: archive completed-units.json on milestone transition and sync metrics.json (#2313) (#2431)
Two bugs fixed:
1. completed-units.json was wiped to [] on milestone transition, losing all
   tracking data. Now archived to completed-units-{MID}.json before reset.
2. metrics.json was never synced between worktree and project root. Added to
   syncStateToProjectRoot, syncWorktreeStateBack, and syncGsdStateToWorktree
   file lists.

Fixes #2313

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:35:01 -06:00
Tom Boucher
81de9f60c5 fix: supervision timeouts now respect task est: annotations (#2243) (#2434)
Added parseEstimateMinutes() to parse estimate strings like "30m", "2h",
"1h30m" into minutes. startUnitSupervision now looks up the task estimate
from the DB and scales soft/hard timeouts accordingly. A 30m task gets 3x
the default timeout, a 2h task gets 12x. Idle timeout is not scaled
because idle is idle regardless of task size.

Also added taskEstimate field to SupervisionContext interface for explicit
estimate passing from callers.

Fixes #2243

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:34:45 -06:00
Tom Boucher
2ddb790141 fix: auto_pr: true now actually creates PRs — fix 3 interacting bugs (#2302) (#2433)
Three bugs prevented auto_pr from ever creating a PR:

1. auto_pr was gated on `pushed` flag which requires auto_push to also be
   true. Changed condition to `!nothingToCommit` so auto_pr works independently.

2. phases.ts called createDraftPR AFTER mergeAndExit (when we're back on main
   and the milestone branch may not exist on remote). Removed duplicate PR
   creation from phases.ts — it's already handled inside mergeMilestoneToMain.

3. createDraftPR in git-service.ts lacked --head and --base parameters, so
   gh would create a PR from whatever branch was current. Added optional
   opts parameter with head/base support.

Fixes #2302

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:34:14 -06:00
TÂCHES
e39dc7976c fix(gsd): insert DB row when generating milestone ID (#2416)
gsd_milestone_generate_id creates a minimal DB row (status: 'queued')
via INSERT OR IGNORE when generating an ID. This ensures milestones
created via /gsd queue or multi-milestone discuss are visible to the
state machine from the moment they get an ID, rather than relying on
the safety-net reconciliation in deriveStateFromDb().

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:26:39 -06:00