src/hooks/src/subagent-stop/watchdog.ts read every entry in
.claude/logs/subagent-spawns.jsonl (last 20), so spawns from
prior sessions (terminal closed, /clear, OOM) accumulated forever and fired
CRITICAL warnings about agents that hadn't existed for days. This PR adds a
24h age cap.
CRITICAL: Agent "agents-view-editor" running for 6823min — may be hung. CRITICAL: Agent "manifest-reconciler" running for 6822min — may be hung. CRITICAL: Agent "opus-sweeper" running for 6822min — may be hung. CRITICAL: Agent "agents-view-editor" running for 6821min — may be hung. CRITICAL: Agent "agents-view-editor" running for 6818min — may be hung. CRITICAL: Agent "manifest-reconciler" running for 6818min — may be hung. CRITICAL: Agent "opus-sweeper" running for 6818min — may be hung. CRITICAL: Agent "Explore" running for 5493min — may be hung. CRITICAL: Agent "Explore" running for 5493min — may be hung. CRITICAL: Agent "ork:code-quality-reviewer" running for 2849min — may be hung. CRITICAL: Agent "ork:security-auditor" running for 2849min — may be hung. CRITICAL: Agent "general-purpose" running for 2367min — may be hung. CRITICAL: Agent "general-purpose" running for 2367min — may be hung. CRITICAL: Agent "general-purpose" running for 2367min — may be hung. CRITICAL: Agent "general-purpose" running for 1764min — may be hung. CRITICAL: Agent "general-purpose" running for 1744min — may be hung. CRITICAL: Agent "general-purpose" running for 1741min — may be hung. CRITICAL: Agent "Explore" running for 1254min — may be hung. CRITICAL: Agent "ork:backend-system-architect" running for 1253min — may be hung. CRITICAL: Agent "ork:security-auditor" running for 1224min — may be hung. 20 entries, all 1224–6823 minutes (20h–4.7d) old. Every entry is a zombie from a prior session.
for (const spawn of spawns) {
if (spawn.agent_id === completedAgentId) continue;
if (!spawn.timestamp) continue;
const elapsed = now - new Date(spawn.timestamp).getTime();
if (elapsed >= CRITICAL_MS) {
warnings.push(`CRITICAL: ... ${min}min — may be hung.`);
} else if (elapsed >= WARNING_MS) {
warnings.push(`WARNING: ... ${min}min.`);
}
}
Every spawn ever, last 20 lines wide. Multi-session zombies stay forever.
const MAX_SPAWN_AGE_MS = 24 * 60 * 60 * 1000;
for (const spawn of spawns) {
if (spawn.agent_id === completedAgentId) continue;
if (!spawn.timestamp) continue;
const elapsed = now - new Date(spawn.timestamp).getTime();
if (elapsed > MAX_SPAWN_AGE_MS) continue;
if (elapsed >= CRITICAL_MS) {
warnings.push(`CRITICAL: ... ${min}min — may be hung.`);
} else if (elapsed >= WARNING_MS) {
warnings.push(`WARNING: ... ${min}min.`);
}
}
Anything older than 24h is silently reaped. Real recent hangs surface.
CRITICAL: Agent "agents-view-editor" running for 6823min CRITICAL: Agent "manifest-reconciler" running for 6822min CRITICAL: Agent "opus-sweeper" running for 6822min ... 17 more zombies, all silently dropped ... (silent — no system message) Or, if a real recent hang exists alongside the zombies: CRITICAL: Agent "backend-system-architect" running for 11min — may be hung.
describe('agentWatchdog', () => {
// 8 existing tests preserved
...
// New (#1882):
it('silently drops zombie spawn older than 24h cap', ...);
it('still warns for an agent JUST under the 24h cap', ...);
it('only legitimate recent spawn surfaces when mixed with zombies', ...);
});
// 11/11 passing — verified locally with npx vitest