pi-crew 0.11.22 → 0.11.23

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,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ### Fixed (deep review 2026-10-16/17 — Wave 1; report `pi-crew-deep-review-2026-10-16.md`, 128/128 cold-verified)
6
+
7
+ - **fix(recovery): R2-H1 — orphan reconcile persisted the FILTERED zombie subset** (tasks.json data loss, runtime-proven). `/tmp` orphan reconcile wrote `result.repairedTasks` (stale-only subset) via full-overwrite — every sibling record destroyed on a timer tick. Now persists `result.persistTasks ?? repairedTasks` (mirror of `crash-recovery.ts`); caller retention pinning test (3-task fixture: alive + zombie + completed all retained).
8
+ - **fix(scheduling): R3-H1 — coalesced-group writes used the dispatch-time snapshot** (concurrent sibling state loss, runtime-proven ×3). Terminal write (:401) and both pre-dispatch saves (:64/:89) now use the in-file `repairGroupTerminalWrite` fresh-read group-scoped merge (was wired only to the late-heartbeat path); concurrent-sibling pinning test + repair-warn companion updated.
9
+ - **fix(state): R4-A1 — agents.json requeue subset-wipe** (deterministic). Both ask-requeue paths (`dispatch-batch.ts` sweep + `respond.ts`) overwrote the authoritative index with only the requeued subset — RUNNING siblings unresolvable until next full save. Both now full-set merge (mirror `team-runner.ts:525-538`); retention tests on both paths.
10
+ - **fix(broker): R7-H1 — delegate.request params bypassed the server-side validation family** (durable budget/turn-cap sandbox escape, exploit-transcript-proven). `maxTurns`/`budgetTokens` were parsed with `typeof === "number"` only: `budgetTokens:-1e9` durably inflated remaining budget by ~1e9 (reserve decrements `tokensSpent` unclamped; refunds gated `reserved > 0` never reconcile), `maxTurns:1e999` → Infinity killed soft+hard turn caps. New `parseDelegateRequestParams` (in `broker/protocol/request-parsers.ts`, the family home): `Number.isInteger` maxTurns 1..200 + budgetTokens 0..100_000_000 (client `Type.Integer` parity), prompt ≤100k, role enum, timeoutSec finite with §7 range — rejected `bad-params` BEFORE admission and the reserve write; edge-matrix tests (15 cases from the runtime negative control).
11
+ - **fix(process): R7-A2 — zombie-scanner `isPidAlive` treated EPERM as dead** — live-but-protected workers landed in `scan.zombies` and `doctor` SIGHUPed them. Now `code !== ESRCH && code !== ENOENT → alive` (mirror of active-run-registry R7-15); EPERM pinning test.
12
+ - **fix(prompt): R7-P1 — surface workers self-terminated at 5s off-Linux** — `parentAlive`'s default `/proc` reader throws off-Linux and `return false` fired BEFORE the empty-start-time tolerance the host writer implements; guard now armed only when `/proc` is readable. Verified in clone (surface-runtime 38/38).
13
+ - **fix(event-log): FS1 + FS3**. FS1: agent-events `.seq` sidecar trusted with no regression guard — crash-window re-mints duplicate seqs that `> sinceSeq` consumers drop forever; now max(sidecar, tail-scan) like EL-1. FS3: cursor `nextSeq=max-delivered` + file-order slice stranded lower-seq events forever on documented non-monotonic files (field-confirmed in 7 runs); `filtered` is now seq-sorted before the limit slice (2 sites); the 2 file-order test pins updated to seq-order contract.
14
+ - **fix(run): R5-M-B3 — 4 bare `void appendEventAsync` in `run.ts`** (probe-proven host crash via unhandledRejection, exit=7); all now `.catch`-logged (17 other sites already were).
15
+
5
16
  ### Fixed (rounds 4–7 audit waves — DR-02/DR-03/DR-04, reports `docs/reviews/pi-crew-audit-round{4,5,6,7}-*.md`)
6
17
 
7
18
  - **fix(state): DR-03 C1 — disk-terminal status preserved + delegate allocation max-merge** (R6-1 HIGH). `persistSingleTaskUpdate` reloaded disk inside the lock but `updateTask` replaced by id UNCONDITIONALLY — a detached runner's late settle overwrote a crash-recovery `cancelled` task with `completed` and emitted `task.completed` after `crew.run.orphan_cancelled`. New `mergeTaskUpdateWithDisk()` preserves a differing disk-terminal status (result fields still recorded); post-execution derives the persisted status from the persist return (terminal events via `TERMINAL_TASK_EVENT_TYPES` — never `task.completed` over cancelled). R6-4: delegate budget reserve/roll-up now merges `tokensSpent=max(disk,incoming)` — the 1s heartbeat whole-record replace could no longer clobber a reservation (lost update), and refund over-subtraction self-heals.