Commit graph

1151 commits

Author SHA1 Message Date
mastertyko
ca0be14f32 fix: preserve doctor missing-dir checks for active legacy slices
Doctor's DB-backed slice normalization already marks pending slices, but the
legacy roadmap fallback only returned done/not-done. That made future unstarted
slices look active during milestone-scoped doctor runs, producing false
missing_slice_dir errors.

Infer a doctor-local pending state for legacy slices by treating every undone
slice except the current active slice as unstarted. This keeps active-slice
missing directory checks intact while skipping false positives for future
slices, and adds a regression test for the legacy fallback path.

Closes #2518
2026-03-26 00:10:38 +01:00
copilot-swe-agent[bot]
a36e6abaa8 fix: clarify regex alternation in test assertion
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/5a619137-0710-4934-949f-bae63945bf70
2026-03-25 22:35:37 +00:00
copilot-swe-agent[bot]
f2283c9a30 fix: verdict gate accepts PARTIAL for mixed/human-experience/live-runtime UATs
The verdict gate in auto-dispatch.ts now reads the UAT file to determine
the UAT type. For mixed, human-experience, and live-runtime modes,
PARTIAL is accepted as a valid verdict (all automatable checks passed,
human-only checks documented as NEEDS-HUMAN).

The run-uat prompt is updated so that PASS is the correct verdict when
all automatable checks succeed, even if human-only checks remain. PARTIAL
is reserved for when automatable checks themselves are inconclusive.

Fixes gsd-build/gsd-2#1400

Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/5a619137-0710-4934-949f-bae63945bf70
2026-03-25 22:34:30 +00:00
TÂCHES
b44da0b0d6 Merge pull request #2537 from mastertyko/fix/clear-stale-milestone-id-reservations
fix(gsd): clear stale milestone ID reservations at session start
2026-03-25 16:07:06 -06:00
TÂCHES
9cc993f21c Merge pull request #2547 from gsd-build/copilot/update-gsd-forensics-logs-and-journal
Enhance /gsd forensics with journal and activity log awareness
2026-03-25 16:05:01 -06:00
copilot-swe-agent[bot]
aee8973d81 fix: make journal scanning intelligent — limit parsed files, line-count older ones
scanJournalForForensics() previously called queryJournal() which loaded
ALL journal entries from ALL daily files into memory. For long-running
projects this could be thousands of entries and megabytes of data.

Now:
- Only the last 3 daily files are fully JSON-parsed (event counts, flows)
- Older files are line-counted only (no JSON parsing) for totals
- Recent events use a rolling window of 20 (shift, not accumulate)
- Constants MAX_JOURNAL_RECENT_FILES and MAX_JOURNAL_RECENT_EVENTS
  make limits explicit and tunable

Activity log scanning was already intelligent:
- nativeParseJsonlTail with 10MB byte cap
- Only last 5 files scanned
- extractTrace() distills raw JSONL into compact ExecutionTrace structs
- formatReportForPrompt has 30KB hard cap on total output

Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/7e7f71ec-0d56-409b-930e-5dff1305ff2a
2026-03-25 21:53:37 +00:00
TÂCHES
9a16119235 Merge pull request #2535 from mastertyko/fix/preflight-context-draft-completed-milestones
fix(auto): skip CONTEXT-DRAFT warning for completed/parked milestones
2026-03-25 15:47:25 -06:00
TÂCHES
bdbad70625 Merge pull request #2538 from jeremymcs/feat/discuss-queued-milestones
feat(discuss): allow /gsd discuss to target queued milestones
2026-03-25 15:47:01 -06:00
mastertyko
c64d3ba65d fix(forensics): filter benign bash exit-code-1 and user skips from error traces
extractTrace() indiscriminately counts all isError tool results as
errors, including grep/rg/find returning exit code 1 (no matches)
and user-interrupt skips. This produces false-positive error-trace
anomalies in forensics reports — in a healthy 10-unit run, 3 units
were flagged with 8 spurious 'errors'.

Add two filters before pushing to the errors array:
- Bash commands with '(no output)' + exit code 1 (normal POSIX grep)
- 'Skipped due to queued user message' (intentional user interrupt)

Real errors (non-zero exit with actual error output, non-bash tool
failures) are still counted as before.

Closes #2539
2026-03-25 22:13:25 +01:00
Jeremy McSpadden
f21537d725 feat(discuss): allow /gsd discuss to target queued milestones
Closes #2307

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 16:05:06 -05:00
mastertyko
aee09a53ec fix(gsd): clear stale milestone ID reservations at session start
The module-level reservedMilestoneIds Set persists across /gsd
invocations within the same Node process. Each cancelled session
reserves an ID that is never claimed, permanently inflating the
next milestone number. Starting /gsd 3 times without completing
produces M011 instead of M009.

Call clearReservedMilestoneIds() at the top of showSmartEntry()
and showHeadlessMilestoneCreation() so stale reservations from
previous cancelled sessions are discarded before generating new IDs.
The function already existed but was never called outside tests.

Closes #2488
2026-03-25 21:47:18 +01:00
mastertyko
e5330ee082 fix(auto): skip CONTEXT-DRAFT warning for completed/parked milestones
The pre-flight milestone queue check in auto-start warns about every
CONTEXT-DRAFT.md it finds, regardless of milestone status. A completed
milestone with a leftover CONTEXT-DRAFT.md triggers a spurious warning
on every session start — noise with no actionable meaning.

Add a status guard that skips completed and parked milestones before
checking for CONTEXT-DRAFT files. When the DB is unavailable, fall back
to the existing warn-on-all behavior (safe default).

Closes #2473
2026-03-25 21:24:44 +01:00
Lex Christopherson
bbea8460b5 fix(claude-code-cli): render tool calls above text response
- Filter toolcall_start/delta/end events from streaming to prevent
  out-of-order rendering in the TUI's accumulated message content
- Collect tool calls from intermediate SDK turns and include them
  BEFORE text content in the final AssistantMessage
- The agent loop's externalToolExecution path emits proper
  tool_execution_start/end events for each intermediate tool call
- Result: tool activity renders above the text response, not below

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:57:47 -06:00
Lex Christopherson
a0ee03d331 feat(agent-core): add externalToolExecution mode for external providers
Adds `externalToolExecution` flag to AgentLoopConfig. When true, the
agent loop emits tool_execution_start/end events for TUI rendering but
skips local tool dispatch. Used by providers that handle tool execution
internally (e.g., Claude Code CLI via Agent SDK).

The flag is dynamically evaluated per-loop via a callback on
AgentOptions, so model switches mid-session are handled correctly.
Providers with authMode "externalCli" automatically use this mode.

Also updates the Claude Code CLI stream adapter to preserve tool call
blocks in the final message instead of stripping them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:57:47 -06:00
TÂCHES
0ded615826 Merge pull request #2528 from gsd-build/copilot/fix-inherited-repo-check
fix: isInheritedRepo() false negative when parent dir has stale .gsd
2026-03-25 12:48:57 -06:00
copilot-swe-agent[bot]
cc7a0cd7c4 fix: isInheritedRepo false negative when parent has stale .gsd; defense-in-depth local .git check in bootstrap
Fix 1 (auto-start.ts): Replace nativeIsRepo(base) with existsSync(join(base, ".git"))
so bootstrap always creates .git locally even when parent repo makes git rev-parse succeed.

Fix 2 (repo-identity.ts): Start walk-up loop at dirname(normalizedBase) instead of
normalizedBase — finding .gsd at basePath itself is irrelevant to inheritance detection.

Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/99fdcddc-7e44-4a64-a1ec-a536806216f6
2026-03-25 18:42:27 +00:00
TÂCHES
8ac7bebded Merge pull request #2515 from ahwlsqja/fix/reconcile-stale-task-units
fix: reconcile stale task DB status from disk artifacts (#2514)
2026-03-25 12:39:37 -06:00
TÂCHES
92fbe26239 Merge pull request #2523 from gsd-build/feat/claude-code-cli-provider
feat(provider): Claude Code CLI provider via Agent SDK
2026-03-25 12:26:43 -06:00
Lex Christopherson
e8a7881307 fix(claude-code-cli): resolve SDK executable path and update model IDs
- Add pathToClaudeCodeExecutable to SDK query options, resolving the
  system `claude` binary via `which claude`. Without this, the SDK
  looks for a bundled cli.js that doesn't exist when installed as a
  library dependency.
- Remove env option that was replacing the subprocess environment and
  stripping auth credentials, causing "Not logged in" errors.
- Update model IDs to current versions: claude-opus-4-6 (1M ctx),
  claude-sonnet-4-6 (1M ctx), claude-haiku-4-5 (200K ctx).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:22:40 -06:00
TÂCHES
4170c639a4 Merge pull request #2525 from gsd-build/fix/planning-doctrine-demoable-definition
fix: make planning doctrine demoable definition audience-appropriate
2026-03-25 12:21:34 -06:00
copilot-swe-agent[bot]
ce4720bad8 refactor: address review - extract RAPID_ITERATION_THRESHOLD_MS, simplify data access
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/d648480a-42f4-4c41-81c7-85038609c717
2026-03-25 18:20:29 +00:00
copilot-swe-agent[bot]
1737b169b5 feat: enhance /gsd forensics with journal and activity log awareness
- Add journalSummary to ForensicReport: flow count, event type
  distribution, recent events timeline, date range
- Add activityLogMeta to ForensicReport: file count, total size,
  oldest/newest files
- Add journal-based anomaly detectors: stuck-detected, guard-block,
  rapid-iterations, worktree-failure events
- Update formatReportForPrompt and saveForensicReport to include
  journal timeline and activity log metadata
- Update forensics prompt template with journal format docs,
  investigation guidance for cross-referencing activity+journal
- Update web types (diagnostics-types.ts) and forensics-service.ts
  for new fields
- Add forensics-journal.test.ts with 11 contract tests

Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/d648480a-42f4-4c41-81c7-85038609c717
2026-03-25 18:19:08 +00:00
Lex Christopherson
524e9dd258 fix: make planning doctrine demoable definition audience-appropriate
The old "demoable" definition was biased toward GUI/SaaS products —
it explicitly penalized terminal commands and curl as demo surfaces.
For developer tools (CLIs, APIs, frameworks), the terminal IS the
product interface and curl IS a legitimate demo.

Redefines "demoable" as audience-appropriate: the intended user
exercising the capability through its real interface. Adds a carve-out
for infrastructure-as-product slices (protocols, extension APIs,
provider interfaces) to the foundation-only rule.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:17:00 -06:00
copilot-swe-agent[bot]
8a0e1dea00 fix(prompts): migrate remaining 4 prompts to use DB-backed tool API instead of direct write
- research-milestone.md: replace direct write with gsd_summary_save (artifact_type: RESEARCH)
- plan-slice.md: update contradictory footer to reference gsd_plan_slice tool
- run-uat.md: replace direct write with gsd_summary_save (artifact_type: ASSESSMENT)
- complete-slice.md: update footer to reference gsd_complete_slice tool

Closes #2513

Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/gsd-build/gsd-2/sessions/350eb36b-b2d7-4e1a-bd2f-debd7b9b13a3
2026-03-25 18:08:33 +00:00
Lex Christopherson
c55d409991 feat(provider): add Claude Code CLI provider extension
Implements Phase 1 of the Claude Code subscription-as-provider integration
(issue #2509). Users with a Claude Code subscription (Pro/Max/Team) can
use subsidized inference through GSD's UI via the official Agent SDK.

The extension registers a provider with authMode: "externalCli" that
delegates to the user's locally-installed claude CLI. The SDK runs the
full agentic loop (multi-turn, tool execution) in one streamSimple call.
Tool calls stream in real-time for TUI visibility but are stripped from
the final AssistantMessage so the agent loop ends cleanly without local
tool dispatch.

Zero core changes — pure extension-based implementation.

Closes #2509

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:07:08 -06:00
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
ahwlsqja
0e7a01f49c fix: reconcile stale task DB status from disk artifacts (#2514)
When a session disconnects after the agent writes SUMMARY + VERIFY
files but before postUnitPostVerification updates the DB, tasks
remain 'pending' in the DB despite being complete on disk.

deriveStateFromDb now checks each non-done task for a SUMMARY file
on disk before selecting the active task. If found, it updates the
DB to 'complete' and logs to stderr for observability.

Fixes #2514
2026-03-26 02:01:57 +09: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
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