Auto-mode process kill shows no user notification; stale lock recovery discards crash context
Problem
When the auto-mode process is killed by the OS (SIGKILL, OOM, etc.) during unit execution, the user sees no notification at all — the process simply vanishes. On next startup, the only message shown is the generic "Cleared stale auto-mode worker state." which does not explain what unit was interrupted, why it crashed, or what state was left behind.
This is a recurring user complaint.
Root Cause
Two gaps in the crash notification path:
1. closeOutCrashedUnit has no user notification (auto/loop.js:230-247)
function closeOutCrashedUnit(s, iterData, err) {
const summary = formatDispatchExceptionSummary({ error: err });
try {
emitOpenUnitEndForUnit(s.basePath, iterData.unitType, iterData.unitId, "cancelled", {
message: summary,
category: "unit-exception",
isTransient: false,
});
writeUnitRuntimeRecord(s.basePath, iterData.unitType, iterData.unitId, ...);
}
catch (closeoutErr) {
logWarning("dispatch", `unit crash closeout failed: ...`);
}
}This function records crash metadata to disk and journal but never calls ctx.ui.notify(). When a unit crashes and the error is caught by the blanket catch (line 1099), the outer catch does notify — but closeOutCrashedUnit itself is a missed opportunity for structured crash notification with unit context.
2. Stale lock recovery notification is too generic (auto-start.js:502-504)
if (startupLock && !isLockProcessAlive(startupLock)) {
clearLock(base);
ctx.ui.notify("Cleared stale auto-mode worker state.", "info");
}When auto-mode restarts after a process kill (SIGKILL — no catch block runs), the stale lock contains unitType, unitId, unitStartedAt, and sessionFile. None of this context is surfaced to the user. The notification should include what unit was interrupted and suggest running /gsd auto to resume.
3. SIGKILL is inherently uncatchable
When the OS kills the process (OOM, user kill, system restart), Node.js cannot execute any cleanup handlers. The blanket catch at auto/loop.js:1099 — which does have proper ctx.ui.notify() calls — never executes. The only recovery path is the stale lock detection on next startup (issue #2 above), which currently discards the crash context.
Expected Behavior
closeOutCrashedUnitshould accept a notification callback and call it with crash details (unit type, unit ID, error summary) so the user sees what crashed.Stale lock recovery in
auto-start.jsshould surface the interrupted unit identity in the notification message (unit type, unit ID, PID, timestamp) instead of the generic "Cleared stale auto-mode worker state."Consider persisting a crash note file (similar to
persistCrashNoteused in the blanket catch at lines 1163, 1198, 1227) duringcloseOutCrashedUnitso that even if the process dies before the notification reaches the UI, the next session can read and surface it.
Environment
- GSD version: 3.0.0
- Model: claude-opus-4-6
- Unit: plan-slice (M008/S09)
Reproduction Context
- Milestone M008 was in progress with 8/9 slices complete
- Validation found bugs, created remediation slice S09
research-slice M008/S09completed successfullyplan-slice M008/S09was dispatched and process PID 43520 died during execution- User saw no error message, no notification, no explanation
- On inspection: stale crash lock with dead PID 43520, journal showing
unit-startforplan-slice M008/S09with no correspondingunit-end
Forensic Evidence
- 35 anomalies detected across project lifetime, including 20 stuck-loop errors (many caused by non-executable verify fields, documented separately)
- Crash lock: PID 43520, started 2026-06-03T04:54:44.314Z, was executing
plan-sliceforM008/S09 - Journal timeline shows:
unit-start flow=baf96fb0 unit=M008/S09at 04:54:44.327Z with no subsequent events - Doctor issue:
stale_crash_lock— process is no longer running
Suggested Fix Priority
Gap #2 (stale lock recovery message) is the highest-impact, lowest-effort fix — it covers all SIGKILL scenarios with a one-line change. Gap #1 is a defense-in-depth improvement. Gap #3 (crash note persistence) is the most robust but requires more design work.
Auto-generated by GSD forensics
Source: gsd-build/gsd-2