Adds a pre-write guard in reconcileWorktreeLogs: re-reads the event
log before overwriting and retries if it grew since the initial read.
Prevents appendEvent calls between read and rewrite from being silently
dropped by the atomic overwrite.
1. plan_task and plan_slice replay now use strict INSERT OR IGNORE
instead of calling insertTask/insertSlice which use ON CONFLICT
DO UPDATE. Prevents replay of older plan events from downgrading
progressed task/slice status back to pending.
2. Type guard on cmd normalization: non-string cmd values are skipped
with a warning instead of throwing.
3. Type guard on extractEntityKey for consistency.
1. Type guard on cmd normalization: non-string cmd values are now
skipped with a warning instead of throwing, preventing replay
from crashing on malformed event lines.
2. complete_milestone replay now validates all slices are closed
before marking milestone complete. Prevents a reordered/partial
event stream from closing a milestone with incomplete work.
3. Type guard on extractEntityKey cmd normalization for consistency.
Adds tests for plan event entity key extraction and unknown cmd handling.
Fixes empty catch blocks in auto-recovery.ts appendEvent calls that failed
the "no empty catch blocks" CI lint.
Covers event log cmd format normalization (hyphens + underscores),
extractEntityKey for complete-milestone, and isClosedStatus
including skipped status.
Five fixes for event log integrity and worktree reconciliation:
1. writeBlockerPlaceholder now appends event log entries after DB writes
so recovery-path completions are visible to worktree reconciliation.
2. appendEvent failure is no longer silently swallowed in completion tools.
Each post-mutation step (projections, manifest, event log) now has its
own try/catch so a projection failure cannot prevent the event log entry.
Event log failures use logError (persisted to audit-log.jsonl) instead
of logWarning.
3. verification_evidence dedup confirmed already in place — INSERT OR IGNORE
with unique index on (task_id, slice_id, milestone_id, command, verdict).
4. New entity replay handlers added to replayEvents: plan_milestone (creates
milestone via INSERT OR IGNORE), plan_slice (creates slice), plan_task
(creates task), replan_slice (informational no-op). Also added to
extractEntityKey for conflict detection.
5. Post-reconcile cache invalidation added — targeted invalidation
(invalidateStateCache + clearPathCache + clearParseCache) at the end of
reconcileWorktreeLogs so deriveState() sees post-reconcile DB state.
Four critical fixes for the GSD state machine:
1. Event log cmd format mismatch — completion tools write hyphenated cmds
("complete-task") but replayEvents handled only underscored ("complete_task").
Worktree reconciliation replay was completely broken for modern completions.
Fix: normalize cmd via replace(/-/g, "_") in both replayEvents and
extractEntityKey. Also adds complete_milestone replay handler and warns
on unknown commands instead of silently skipping.
2. Dead if-block at state.ts:434-440 — empty block with misleading comments
wasted getMilestoneSlices() + every() computation. Removed and replaced
with clear comment explaining why all-slices-done milestones without
SUMMARY are intentionally not added to completeMilestoneIds.
3. getActiveMilestoneId missing "skipped" status — checked complete/done/parked
but not skipped. isStatusDone() includes skipped, creating divergence where
a skipped milestone could become permanently "active". Fix: use
isClosedStatus() || parked check.
4. executeReplan disk-file fallback — triage-resolution.ts writes replan
trigger to disk and DB (best-effort). If DB write fails, deriveStateFromDb
only checked the DB column, making the trigger invisible. Fix: fall back
to checking the disk REPLAN-TRIGGER file when DB column is null.
Four critical fixes for the GSD state machine:
1. Event log cmd format mismatch — completion tools write hyphenated cmds
("complete-task") but replayEvents handled only underscored ("complete_task").
Worktree reconciliation replay was completely broken for modern completions.
Fix: normalize cmd via replace(/-/g, "_") in both replayEvents and
extractEntityKey. Also adds complete_milestone replay handler and warns
on unknown commands instead of silently skipping.
2. Dead if-block at state.ts:434-440 — empty block with misleading comments
wasted getMilestoneSlices() + every() computation. Removed and replaced
with clear comment explaining why all-slices-done milestones without
SUMMARY are intentionally not added to completeMilestoneIds.
3. getActiveMilestoneId missing "skipped" status — checked complete/done/parked
but not skipped. isStatusDone() includes skipped, creating divergence where
a skipped milestone could become permanently "active". Fix: use
isClosedStatus() || parked check.
4. executeReplan disk-file fallback — triage-resolution.ts writes replan
trigger to disk and DB (best-effort). If DB write fails, deriveStateFromDb
only checked the DB column, making the trigger invisible. Fix: fall back
to checking the disk REPLAN-TRIGGER file when DB column is null.
Ecosystem research: executeSearchQuery() was a stub returning empty
results. Research now happens during the discussion (between Layer 1
and Layer 2) using whatever web search tools are available — native
Anthropic web search for Claude, search-the-web for other providers.
Preparation phase focuses on mechanical work only.
Adversarial review fixes:
- Clear lastPreparationResult on every discuss entry to prevent
cross-session/project state leaks
- Replace invalid JS regex anchor \z with indexOf-based section
extraction in prompt-validation.ts
- Document consecutive error counter finding as upstream behavior
(agent-loop.ts is part of pi-agent-core, not gsd extension)
The Model [phase] [tier] notification fired on every unit dispatch during
auto-mode, cluttering the notification widget. The dashboard header already
displays the active model, making this redundant. Gate behind verbose flag
consistent with all other model routing notifications in the same function.
task.files ("files likely touched") is a planning hint that includes files
a task will create, not a dependency contract. Including it in ordering
checks caused false "sequence violation" blocking errors when a task listed
files it would create. Only task.inputs (machine-parsed prerequisites)
should trigger ordering violations, matching checkFilePathConsistency (#3626).
Closes#3677
- Changed checkTaskOrdering to check [...task.inputs] instead of
[...task.files, ...task.inputs]
- Updated 4 existing tests to use inputs (were testing buggy behavior)
- Added 8 regression tests: 5 ordering false-positive cases,
3 consistency edge cases
The ghost milestone check (#3645) was eliminating queued shell
milestones before the deferred-shell logic (#3470) could handle them,
causing queued milestones to vanish from the registry entirely.
Fixes workflow-logger coverage test failures: empty catch blocks in
reopen-slice/reopen-task and raw process.stderr in reopen-milestone
now use logWarning from workflow-logger.
Fixes identified by comprehensive state machine validation:
- M12: reopen-task/slice now deletes SUMMARY.md from disk, preventing
the DB-filesystem reconciler from auto-correcting tasks back to
"complete" — reopen was previously a no-op when artifacts existed
- H4: add 30s hard timeout to unitPromise via Promise.race — prevents
permanent hang if supervision fails to resolve agent_end
- H5: add handleReopenMilestone — milestone completion was irrevocable
- H6: pass ID as title when auto-creating phantom parent entities
- H7: guard loadRegistry() against missing/corrupt registry.json
- M4: report_blocker replay now sets blocker_discovered flag via
new setTaskBlockerDiscovered() DB function
- M5: insertVerificationEvidence uses INSERT OR IGNORE with unique
index on (task_id, slice_id, milestone_id, command, verdict)
- M11: complete-slice rollback preserves original status instead of
hardcoding "pending"
- M14: deriveWorkflowAction shows contextual labels for blocked,
paused, validating-milestone, completing-milestone, needs-discussion,
and replanning-slice phases instead of generic "Continue"
Includes 86 regression tests (49 unit + 37 integration) validating
every phase transition, completion guard, and edge case.
Closes#3161