When a milestone completes, phases.ts calls mergeAndExit to merge the
worktree branch back to main. It then calls closeoutAndStop → stopAuto,
which unconditionally calls mergeAndExit again. The second call fails
because the branch was already deleted by the first merge, producing a
misleading 'not something we can merge' warning even though the merge
succeeded.
Add a milestoneMergedInPhases session flag that phases.ts sets after a
successful merge. stopAuto checks this flag and skips its own merge
when it is already set. The flag is cleared in AutoSession.reset() so
it does not leak across sessions.
Closes#2645
getActiveMilestoneId and deriveStateFromDb sorted milestones by ID
(localeCompare / milestoneIdSort) while the dispatch guard in
dispatch-guard.ts sorted by queue-order.json via findMilestoneIds.
When a user reordered milestones via /gsd queue to prioritize a
later-numbered milestone, the state machine ignored the reordering
and dispatched to the earlier-numbered one. The dispatch guard then
blocked completion because the queue-ordered-first milestone was
incomplete — producing a deadlock.
Replace the lexicographic sort with sortByQueueOrder(loadQueueOrder())
in both the getActiveMilestoneId DB path and the deriveStateFromDb
milestone sort. This aligns all three subsystems (state derivation,
dispatch, and dispatch guard) on the same ordering.
Closes#2556
writeBlockerPlaceholder writes a placeholder SUMMARY file when idle
recovery exhausts all retries, but never updated the DB task status.
verifyExpectedArtifact checks the DB as the authoritative source for
execute-task units — with status still "pending", verification failed,
deriveState re-derived the same task, and the dispatch loop repeated
indefinitely (observed as 8-9 "Advancing pipeline" messages).
After writing the file, call updateTaskStatus to mark the task as
"complete" in the DB. This lets verifyExpectedArtifact pass and
breaks the infinite re-dispatch loop.
Closes#2531
On Windows, scanProjectFiles returns backslash paths (e.g.
"apps\mobile\app\build.gradle") but PROJECT_FILES markers use forward
slashes ("app/build.gradle"). Normalize scanned paths to forward
slashes before matching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The doctor-task-done-missing-summary-slice-loop test was written for
the old filesystem-based task_done_missing_summary check, which was
refactored to db_done_task_no_summary (SQLite-based). The test creates
filesystem structures but the current check queries the database,
making it fundamentally incompatible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`_deriveStateImpl` (used when no gsd.db exists) lacked the SUMMARY-based
reconciliation added to `deriveStateFromDb` in 0e7a01f4. Heading-style
tasks (`### T01:`) are always parsed as `done=false` by `parsePlan`
because the heading syntax has no checkbox. When the agent writes a
SUMMARY file but the plan heading has no checkbox, the task appears
incomplete forever, causing infinite re-dispatch.
Now checks each non-done task for a SUMMARY file on disk after
`parsePlan()`, mirroring the DB reconciliation logic.
Root cause: `parsePlan()` recognizes two task formats:
1. `- [x] **T01: Title**` → done from checkbox state
2. `### T01: Title` → always done=false (no checkbox to read)
The DB path (deriveStateFromDb) was already fixed in 0e7a01f4 to
reconcile via SUMMARY files. This commit applies the same fix to
the filesystem path used by projects without gsd.db.
Consolidate two separate imports from files.js into one to resolve
TS2300 duplicate identifier error.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MilestoneRow and SliceRow interfaces don't have index signatures,
so they can't be assigned to Record<string, unknown>. Using
Record<string, any> allows the helper to accept any typed row object.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract syncDirFiles() helper to eliminate duplicated iterate/filter/copy/catch
logic across three directory levels (milestone root, slices, tasks). Reduces
maximum nesting depth from 4 to 2.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ErrorContext interface to UnitResult so error information (provider
errors, timeouts, idle watchdog kills) is no longer discarded at the
resolve boundary. The four call sites that previously threw away context
now attach typed error metadata with category, message, and transience.
Downstream consumers (stuck detection in phases.ts, journal unit-end
events) use the structured errorContext field directly instead of
fragile regex heuristics on message content.
The test referenced task_done_missing_summary which was renamed to
db_done_task_no_summary in the DoctorIssueCode type.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The same ~20-line cleanup sequence (merge abort / squash msg unlink /
hard reset) appeared twice in reconcileMergeState(). Extract into a
private abortAndResetMerge() helper to eliminate the duplication.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Deduplicate near-identical "has any non-empty field" checks for milestone
and slice planning state into a shared hasNonEmptyFields() helper with
field-name arrays, reducing 8 repeated String(row.field||"").trim() calls
to 2 declarative one-liners.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each health check function (git, runtime, global, engine) moves to its
own file with only the imports it needs. doctor-checks.ts becomes a
re-export barrel for backward compatibility.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Embed 6 new quality questions (Q3-Q8) into the GSD lifecycle so
consumer projects get adversarial, failure, load, and operational
coverage by default. All sections are opt-out ("OMIT ENTIRELY") for
simple slices.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract verdict extraction, normalization, and schema validation into a
single verdict-parser.ts module. This fixes inconsistent normalization
where `passed` was normalized to `pass` in state.ts but not in
auto-dispatch.ts or auto-prompts.ts, and centralizes scattered verdict
schema definitions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate all worktree sync, resource staleness, stale worktree escape,
and stale runtime unit cleanup into auto-worktree.ts. Extract shared
ROOT_STATE_FILES constant and isSamePath helper to eliminate triple
duplication of the rootFiles array and copy-pasted symlink checks.
Replace inline 26-line stale-unit cleanup in auto-start.ts with a call
to cleanStaleRuntimeUnits().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove duplicate resolveExpectedArtifactPath() and diagnoseExpectedArtifact()
from auto-recovery.ts, making auto-artifact-paths.ts the single source of truth.
auto-recovery.ts re-exports both functions for backward compatibility.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
guided-flow.ts has its own local implementation; the exported version
in auto-recovery.ts was never imported anywhere. Removes 35 lines of
dead code, the unused clearUnitRuntimeRecord import, and associated
tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
session-forensics.ts is a general-purpose JSONL parser that had a direct
import of getAutoWorktreePath from auto-worktree.ts, creating tight
coupling. getDeepDiagnostic now accepts an optional worktreePath parameter
instead of resolving it internally. The caller (auto.ts) resolves the
worktree path via readActiveMilestoneId + getAutoWorktreePath and passes
it in.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove basePath and unitId from RunVerificationGateOptions — they were
defined in the interface and passed by callers but never read by
runVerificationGate(). This eliminates false coupling where callers
compute values that have zero effect.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Delete unused resource-version.ts (functions duplicated in auto-worktree-sync.ts
with zero imports). Remove GitServiceImpl.git() private method with no call sites.
Clean up orphaned section headers and dangling JSDoc in git-service.ts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SLICE_BRANCH_RE, QUICK_BRANCH_RE, and WORKFLOW_BRANCH_RE were scattered
across worktree.ts and git-service.ts. Extract all three into
branch-patterns.ts as the single source of truth. Both original modules
re-export for backward compatibility — no consumer changes needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Files added by PR #2008 that were not in main were dropped during
the merge. Restore all src/, docs/, and scripts/ files from the
pre-merge PR head.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The lock acquisition had a primary path and a retry path with identical
28-line onCompromised callbacks and 6-line state assignment blocks (68 lines
of copy-paste). Extract into createLockCompromisedHandler() and
assignLockState() helpers so bug fixes only need to be applied once.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>