pi-crew 0.11.51 → 0.11.52

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
@@ -1,5 +1,44 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.52] — live-catch backlog: cross-session cancel reaches inline runs + heartbeat-watcher false-positive class (2026-10-30)
4
+
5
+ **Fixed (runtime, live-caught run `team_20261030044630`).** Two gaps filed as backlog after the
6
+ 0.11.51 real-test battery, both root-caused from the incident artifacts (not hypotheses):
7
+
8
+ 1. **Cross-session cancel never reached INLINE async runs.** The foreground-control interrupt guard
9
+ lived ONLY in background-runner's detached `main()`. Runs executed inline in the owner session
10
+ process (`executeBackgroundRun` core — also the `PI_CREW_TEST_ASYNC_INLINE` seam) never polled
11
+ `foreground-control.json`: the incident run had `manifest.async === undefined`, zero `async.*`
12
+ events, and TWO interrupt requests both stuck `acknowledged:false` while worker 403288 ran ≥75s
13
+ post-cancel with fresh heartbeats. Fix: guard core extracted as `startRunInterruptGuard` (hosted in
14
+ `foreground-control.ts` with the producer — keeps the lazy-imports ratchet at 542); detached
15
+ `startInterruptGuard` is now a thin process-wide wrapper (behavior byte-identical: sweep +
16
+ stopParentGuard + exitCode 130, pinned by regression tests); `executeBackgroundRun` with no host
17
+ signal mounts a RUN-SCOPED guard — ack + event + `runController.abort()` ONLY, never
18
+ `terminateActiveChildPiProcesses()` (a global sweep would murder sibling runs' children in the same
19
+ host process; pinned by the SCOPED-KILL test with a real registered child). The team-run input now
20
+ receives the RESOLVED signal — inline runs previously had NO cancellation source at all. Guard
21
+ stops in a `finally`; ownership deduped by host-signal presence (no double guard).
22
+ 2. **heartbeat-watcher false-dead class.** `crew.task.heartbeat_dead` fired 3× for a live worker
23
+ (heartbeat silent >5min during long tool work, `task.heartbeat.pid` undefined on every task, so the
24
+ PID-liveness downgrade gate had nothing to read), and `handleRetry` re-queued tasks WITHOUT
25
+ stripping liveness fields (attempt-2 inherited attempt-1's heartbeat timestamps). Fix: (a) watcher
26
+ attempt-clamp — heartbeat/`agentProgress` evidence older than the current attempt's `startedAt` is
27
+ inadmissible; no usable evidence ⇒ elapsed from `startedAt` (a task younger than `deadMs` can never
28
+ be dead); (b) `handleRetry` strips `heartbeat` + `agentProgress` on re-queue (checkpoint untouched);
29
+ (c) child-executor pins the attempt's worker pid into `task.heartbeat` (per-attempt reset, guarded
30
+ spread) so the PID-gate actually functions.
31
+
32
+ Also: an already-ACKED interrupt no longer warns "Ignoring unknown foreground control request" every
33
+ poll tick until guard stop (verifier-flagged cosmetic, carried over from the pre-extraction guard).
34
+
35
+ Tests: 5 new `interrupt-guard.test.ts` (inline wiring, post-stop non-consumption, SCOPED-KILL,
36
+ detached regression, acked-silence), `async-runner-inline-interrupt.test.ts` (e2e via the real inline
37
+ seam: mid-run request → ack ≤3s + event + terminal run + no leaked interval), 4 watcher attempt-clamp
38
+ pins (incl. genuinely-stale-still-dead regression + no-startedAt legacy), 2 heartbeat-pid pins, +1
39
+ lifecycle retry-strip. Leader gates: full unit+integration, typecheck, biome, format:check,
40
+ lazy-imports 542, slop 41 — all green.
41
+
3
42
  ## [0.11.51] — SEC-2026-11: security audit remediation (30 confirmed findings + 4 leads, 13 waves) (2026-10-30)
4
43
 
5
44
  **Fixed (security).** Full remediation of the 2026-10-29 independent security audit (skill security-audit, full