pi-crew 0.11.40 → 0.11.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,81 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.11.42] — fix wave 14: event-log drain API + zombie-scanner seam (2026-10-25)
6
+
7
+ Wave 12d papered over the recurring `ENOTEMPTY` teardown race with `removeDirWithRetry`; the root cause stayed:
8
+ production fires **66 event writes across 23 files** without awaiting them (`void appendEventAsync` ×23,
9
+ `appendEventFireAndForget` ×43), and each queued write re-creates the run dir before appending — so teardown that
10
+ `rmSync`s the state dir mid-flight races the async writer and loses. There was no way for a test to await one
11
+ run's writers: the queue drain was private and `beforeExit`-only (SPEC `fixwave14-drain-api-zombie-seam-2026-10-25`).
12
+
13
+ **Added — `drainEventLogWriters(eventsPath?)`.** Public per-run drain API for the event-log async writer queues
14
+ (allSettled tail await + per-path buffer flush, never throws). Lets tests await in-flight writers before removing
15
+ a run state dir instead of retry-racing them — the race repro (drain → plain `rmSync`, no retry) is now a pinned
16
+ test.
17
+
18
+ **Added — `cleanupStateRoot(dir, eventsPath?)` test helper.** Best-effort drain then `removeDirWithRetry(settleMs: 0)`.
19
+
20
+ **Added — `ZombieScanOptions` seam on `scanZombieSubagents`** (`platform`/`getuid`/`readProcFile`/`listProcPids`/
21
+ `pidAlive`/`getParentPid`, defaults byte-exact). **7 previously linux-only tests now run on every OS** via
22
+ synthetic `/proc` fixtures (+2 new seam tests; 2 real-`/proc` default-path tests stay linux-gated).
23
+
24
+ **Fixed — teardown races by adopting drain-then-remove in pilot tests.** `team-tool-steer-clamp` (retry-only →
25
+ drain), `dwf-setresult` (12 bare `rmSync`), `event-log-batch`/`event-log-async`, plus dedup of two local
26
+ `removeDirWithRetry` variants (`subagent-steer-live`, `subagent-tools-integration`).
27
+
28
+ **Battery (PASS).** targeted ×3 (drain 4/4, zombie 26/26 skip=0, 6 pilot files), `test:critical` 124/124, full
29
+ unit 10,075 pass / 1 ambient-adjudicated / 4 skipped, integration 144/0/4, static gates 0, dist MATCH.
30
+
31
+ **Docs.** Battery report `docs/real-test/reports/real-test-2026-10-25-wave14-drain-zombie-battery.md`; closed
32
+ investigation (no code change): the "hung" alert for an already-cancelled run was delivery timing — `cancelled`
33
+ is already terminal in both the emit and delivery gates.
34
+
35
+ ## [0.11.41] — fix wave 13: watchdog liveness — false "appears hung" alerts on healthy direct runs (2026-10-25)
36
+
37
+ Five false alarms landed in one working session (2026-10-24/25), all the same shape: healthy direct single-agent
38
+ runs executing ONE long tool call (a ~20-min CI poll loop; a ~20-min full test suite). Every run completed
39
+ normally — the watchdog's *evidence* was frozen, not the run (SPEC `fixwave13-watchdog-liveness-2026-10-25`).
40
+ Unlike the T1/T4 incidents already covered by WD-DELIVER-GUARD / WD-STALE-RECORD, these verdicts were wrong at
41
+ emit time: the run genuinely was still running.
42
+
43
+ **Fixed — root cause: evidence frozen during long tool calls (GAP-0).** The hung verdict in
44
+ `src/runtime/foreground-watchdog.ts` reached through `isLikelyOrphanedActiveRun` used only the manifest
45
+ `updatedAt` and agent `progress.lastActivityAt` — and both freeze by construction while a tool runs:
46
+ `persistLiveProgress` was purely event-driven (no pi events fire during a 20-min bash call) and `updateRunStatus`
47
+ writes `updatedAt` only on status transitions. The 5-min activity shield expires, the next cycle reads a stale
48
+ manifest ⇒ false hung.
49
+
50
+ **Added — watchdog liveness short-circuit (F1).** Before a hung verdict is emitted, the watchdog now consults the
51
+ two liveness signals other consumers already trust: a fresh **active-run registry claim** (registered at dispatch)
52
+ ⇒ the alert is suppressed for up to `CLAIMED_RUN_HUNG_MS` = 45 min, OR a fresh **task heartbeat**
53
+ (`heartbeat.lastSeenAt` on a running/waiting task, `alive !== false`) ⇒ no alert. The hung ladder for unclaimed
54
+ runs is unchanged (`ORPHANED_ACTIVE_RUN_MS` untouched — dashboard semantics preserved).
55
+
56
+ **Added — live-executor heartbeat timer (GAP-1).** `src/runtime/task-runner/live-executor.ts` now runs an unref'd
57
+ `LIVE_TOOL_HEARTBEAT_INTERVAL_MS` = 30 s interval, active only while a tool executes, throttled persist of live
58
+ progress, cleared in `finally`; no event-log writes. The state store no longer freezes mid-tool for ANY consumer
59
+ (watchdog, dashboard, doctor).
60
+
61
+ **Added — evidence timestamp (GAP-2).** The watchdog alert detail now carries `as of <updatedAt>` so a fresh alert
62
+ is distinguishable from a stale or queued one.
63
+
64
+ **Fixed — `steer-live` test fixture orphan leak (GAP-3).** 13 orphan background-runner processes from the
65
+ `steer-live` test were found alive and killed manually — 5 born AFTER wave 12 shipped (a different fixture from
66
+ the `steer-clamp` test wave 12 fixed). `test/unit/extension/core/subagent-steer-live.test.ts` spawns a REAL
67
+ background runner through the SUT (setsid + detached + SIGTERM-swallowing) and held no handle on it. Teardown now
68
+ resolves the runner pid from `manifest.async.pid` → `async.pid` → state scan, reaps via `reapGroup` when
69
+ `pgid === pid` (with `/proc/<pid>/cwd` scoping + self-pid guard), falls back to a pid-SIGKILL, and runs in
70
+ `finally` + `after()`.
71
+
72
+ **Battery (PASS).** targeted ×3 (`foreground-watchdog` 29/29, `live-executor-heartbeat` 5/5, `subagent-steer-live`
73
+ 4/4), `test:critical` 124/124, full unit 10,061 pass / 0 fail / 4 skipped, integration 144/0/4, static gates 0,
74
+ dist MATCH. **Live repro on the real dist** (`PI_CREW_WATCHDOG_INTERVAL_MS=20000` + a 400 s tool): 5 hung verdicts
75
+ suppressed by the new liveness short-circuit, 0 alerts delivered — while the same box's older 0.11.40 verifier
76
+ session emitted 2 false alerts during the battery (the exact pre-fix behavior).
77
+
78
+ **Docs.** battery report `docs/real-test/reports/real-test-2026-10-25-wave13-watchdog-liveness-battery.md`.
79
+
5
80
  ## [0.11.40] — fix wave 12: fixture teardown — test-harness orphan leaks (2026-10-24)
6
81
 
7
82
  Closes the leak class wave 11 exposed but did not own: the reaping wave made production descendants provable, and