🪦 #1882 — watchdog zombie reap

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.

Captured noise (real session output, 2026-05-19→20)

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.

Before (watchdog.ts)

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.

After (this PR)

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.

After (same scenario)

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.

Test coverage added

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