pi-crew 0.11.47 → 0.11.49

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.
@@ -51,6 +51,21 @@ if [ "$pushes_main" = "1" ]; then
51
51
  echo "[pre-push] Fix the failures above, or bypass with 'git push --no-verify' (CI still gates)."
52
52
  exit 1
53
53
  fi
54
+
55
+ # Pre-publish battery (2026-10-28, born from the v0.11.46/0.11.47
56
+ # silent-worker incident): the LIVE real-Pi gate. One command, tiers
57
+ # path-triggered from the diff (T1/T3/T11j always; T7 spawns a real
58
+ # pi -p fast-fix smoke when spawn-affecting paths changed). Evidence lands
59
+ # in .crew/audit/battery.jsonl. There is NO tier selection here on purpose —
60
+ # a gate the agent may choose to skip is a gate that will be skipped.
61
+ # No pi binary (CI-like hosts): PI_CREW_BATTERY_SKIP=1 records the skip in
62
+ # the audit trail; a silent skip fails.
63
+ echo "[pre-push] push to main: running the pre-publish battery (live real-Pi gate)…"
64
+ if ! node scripts/pre-publish-battery.mjs; then
65
+ echo "[pre-push] ✋ push blocked: pre-publish battery RED (see .crew/audit/battery.jsonl)."
66
+ echo "[pre-push] Gate KHÔNG CHẠY = KHÔNG PUBLISH — fix, or bypass with 'git push --no-verify' (CI still gates) and record WHY."
67
+ exit 1
68
+ fi
54
69
  fi
55
70
 
56
71
  echo "[pre-push] gates green."
package/CHANGELOG.md CHANGED
@@ -1,5 +1,97 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.49] — 0.11.46/47 incident ROOT-CAUSED & CLOSED: fd0 handshake race → fd3 dedicated pipe; T7 nonce binding; review fix wave (2026-10-28)
4
+
5
+ **Root cause (dynamically confirmed 5/5 + on-disk leak proof).** pi's `readPipedStdin` (main.js:52-65) consumes ALL
6
+ piped stdin as PROMPT CONTENT in every mode except `--mode rpc`. The v0.11.46 stdin handshake rode fd0 and raced it
7
+ three ways: (1) token JSON merged into the worker prompt — **secret leak, proven in a live transcript** (the model
8
+ reasoned about the credential line); (2) stream-ended creds-less silence; (3) our reader's `pause()` before EOF
9
+ drain deadlocking pi's boot await → **self-initiated exit 0** (not parent SIGTERM as first modeled) with
10
+ `takeOverStdout`-buffered ~0-byte stdout — the exact incident signature (~1.2s, 0 stdout, ×3 retry).
11
+
12
+ **Fix:** brokered child-pi workers now get a DEDICATED pipe as stdio entry 4 (fd3); the payload is written there
13
+ and read by `readFirstLineFromFd`. fd0 returns to `"ignore"` (pre-0.11.46 shape), killing every race ordering. The
14
+ detached background RUNNER keeps fd0 (plain node process, no race). Gate stays OPT-IN (`PI_CREW_BROKER_STDIN=1`);
15
+ default route bit-for-bit unchanged. **Evidence:** pre-fix gate-ON matrix on pi@1.0.4: 5/5 silent death; post-fix
16
+ 2/2 alive (106-110 KB stdout, ~3.2 s, zero leak) — full inversion. Live-worker integration 2/2 on pi 1.4.0 with
17
+ REPO-SRC extension proven via argv capture; security review 9/9 invariants.
18
+
19
+ **Also:** `maxTasksPerRun` is now enforced at manifest admission (pre-dispatch) instead of only at finalize —
20
+ oversized manifests were fully dispatched and >~2.5k-task handshake lines silently lose broker creds (writer now
21
+ warns >60 KiB). Worker-side fallback gate reads the RESOLVED env (`options.env ?? process.env` via `getCrewEnv`)
22
+ — closing the test seam that masked the earlier dead-wire. `check:env-vars` registry compliance for all 8 broker
23
+ identity reads; fd-mechanics tests skip on win32 (reader short-circuits there by design; brokered spawn is
24
+ auto-disabled on Windows).
25
+
26
+ ### Also in this release (earlier 2026-10-28) — cold-verified review fix wave (2026-10-28)
27
+
28
+ **Fixed (MAJOR) — the `PI_CREW_BROKER_STDIN=1` opt-in route was dead-wired end-to-end (review + 3-agent cold-verification, dynamically proven).**
29
+ Two compounding defects: (1) the parent never re-wrote the flag into the child's `built.env` (registry kind `host-only` strips
30
+ the ambient value), so the worker-side mirror gate (`resolveBrokerStdinFallbackEnabled`, which reads the child's OWN
31
+ `process.env`) could never activate — the stdin payload was written and never read, leaving every opt-in worker creds-less
32
+ (`NOOP_HANDLE`: no ask/message/mailbox/steer, no broker audit). (2) the token was omitted from `built.env` for ALL
33
+ branches, but only the headless-JSON branch delivers the payload — RPC (`child-pi-rpc.ts`) and surface-pane spawns compose
34
+ without it, so their children got neither token nor payload. Unit tests masked both: they flip the gate by mutating
35
+ `process.env` in-process, bypassing the spawn-env plumbing (structural seam: `startChildBrokerClientAsync` honors
36
+ `options.env` for identity vars but the gate reads ambient `process.env`).
37
+
38
+ Repair: `prepareSpawnContext` now writes `PI_CREW_BROKER_STDIN="1"` into `built.env` when the host gate is ON (registry
39
+ kind → `child-write`) and puts the token in `built.env` for every brokered spawn; only the headless compose (payload
40
+ present) strips it from the FINAL env (`buildFinalChildPiSpawnOptions`), so RPC/surface keep the env route — a token in
41
+ their env also disarms their stdin fallback (their stdin is the JSONL protocol / a TTY). Gate OFF: bit-for-bit legacy.
42
+ New composed-path tests assert the flag/token/payload matrix across all three call sites (no live-reader assertions — the
43
+ 0.11.46 reader exit-0 death is still open, default stays OPT-IN). Misleading docblocks + the factually-wrong registry
44
+ rationale ("parent decision fully determines the route") corrected. `argvIndicatesRpcMode` now also matches the
45
+ `--mode=rpc` equals-form.
46
+
47
+ **Fixed (MAJOR) — T7 live-smoke judged the WRONG run (newest-mtime race → false PASS).** The battery's T7 tier picked
48
+ `candidates[0]` (newest run dir ≥ start−60s) with zero binding to its own dispatch — a concurrent team run created in the
49
+ window got judged instead (two real run dirs on this host measured 25 ms apart), and the `pi -p` dispatch stdout/stderr
50
+ was piped with zero listeners, so a silent smoke death left no forensics. Now the dispatched goal carries a per-invocation
51
+ nonce (`[t7-nonce:<uuid>]`); only run dirs whose `events.jsonl` contain that nonce are judged, zero nonce dirs = FAIL, a
52
+ non-terminal run is a FAIL checked before the killed/hung diagnosis, and a capped tail (64 KB) of the dispatch's own
53
+ stdout/stderr is recorded in the failure reason, audit entry, and log output.
54
+
55
+ **Fixed (minor) — battery gate fail-open trigger paths.** `changedPaths()` now filters diff-base tags to release semver
56
+ (`^v\d+\.\d+\.\d+$` — a near-HEAD non-release tag previously shrank the diff window and silently un-triggered T7) and
57
+ parses `git status --porcelain` renames (`R old -> new`) into BOTH paths instead of one mangled string.
58
+ `prepublish-ci.mjs` now preserves `PI_CREW_BATTERY_SKIP` through the npm `prepublishOnly` path (the documented skip
59
+ hatch was dead there — the battery's own failure message recommended a var its caller stripped).
60
+
61
+ **Changed — T7 trigger coverage gaps closed** (`src/state|utils|agents|teams|workflows|hooks|observability`, `scripts/`):
62
+ cold-verification found the workflow-engine src subtrees and the gate's own scripts/ were invisible to the live gate; gate
63
+ changes now live-test the gate (this commit deliberately triggers T7 on its own pre-push).
64
+
65
+ **Changed — `before_agent_start` `systemPrompt` hardening (defense-in-depth).** Cold-verification refuted reachability
66
+ (`systemPrompt` is required in every host type def and synthesized by a runner getter, unlike `prompt` which arrives via
67
+ caller args — the GH #64 root cause), but the asymmetry was real: `autonomous-policy.ts` and `knowledge-injection.ts`
68
+ now coerce non-strings to `""` (append-only output, safe); `prompt-runtime.ts` SKIPS the rewrite instead (coercing `""`
69
+ there would blank a real system prompt — it strips sections). No behavior change on any current host.
70
+
71
+ **Added — LOUD oversize diagnostic in `buildBrokerStdinLine`.** The reader aborts past 64 KiB without a newline; a
72
+ ~2.5k-task manifest (~131 KiB measured live — pipe chunks make the effective Linux boundary ~2× the coded cap)
73
+ silently stripped EVERY task's broker creds. The writer now warns at 60 KiB via `logInternalError` (not PI_CREW_DEBUG-
74
+ gated); the line is still written, never blocked.
75
+
76
+ ## [0.11.48] — HOTFIX: stdin broker-credential gate flipped OPT-IN — fixes silent worker death (2026-10-28)
77
+
78
+ **Fixed — v0.11.46/0.11.47 regression: every headless JSON worker on the default route exited 0 with zero stdout.**
79
+ The LOW-1 stdin handshake shipped in 0.11.46 with the gate default-ON. On real Pi hosts, the moment the worker-side
80
+ stdin reader activated (`startChildBrokerClientAsync` → `readStdinFirstLine`: env broker identity present, env token
81
+ absent, gate on — the default shape of every team-run worker), the whole `pi --mode json -p` process exited 0 silently
82
+ (~1.2s, 0 stdout bytes, retry ×3, run failed). CI never caught it: unit tests inject the `readStdinLine` seam and never
83
+ run `process.stdin` inside a real pi process; the live battery (real-test-pi-crew T7) caught it — which is also why this
84
+ release ships with that battery re-run green (3/3 tasks + `message` round-trip through the legacy env route).
85
+
86
+ `PI_CREW_BROKER_STDIN` is now **OPT-IN** (`"1"` enables the stdin route; unset/`0`/anything else keeps the legacy env
87
+ route on BOTH sides, bit-for-bit pre-0.11.46 behavior). The LOW-1 closure code (writer, reader, payload v2) stays for
88
+ re-enabling after the reader path is root-caused.
89
+
90
+ Also adds `test/integration/broker-stdin-reader-liveworker.test.ts` — a LIVE regression test that spawns a real `pi`
91
+ worker with the exact env shape that died and asserts non-zero stdout (skips where no `pi` binary exists, e.g. CI
92
+ containers that run the fake-pi fixtures), plus incidental biome fixes (`useImportType` ×5, `noUselessTernary`) in
93
+ crew-broker-delegate-request.ts / surface-worker.ts.
94
+
3
95
  ## [0.11.47] — live-session split: model/prompt clusters out of live-session-runtime.ts (2026-10-27)
4
96
 
5
97
  **Changed — `live-session-runtime.ts` (1971 → 1634 lines).** The file sat 29 lines under the 2000-line wc-gate LIMIT,