pi-crew 0.11.48 → 0.11.50

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,90 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.50] — LOW-1: PI_CREW_BROKER_STDIN fd3 handshake DEFAULT-ON ('0' opts out) (2026-10-28)
4
+
5
+ **Changed (LOW-1 gate flip).** The broker-credential fd3 handshake route is now **DEFAULT-ON**: both gates
6
+ (`child-pi-spawn.resolveBrokerStdinHandshakeEnabled`, `crew-broker-child.resolveBrokerStdinFallbackEnabled`) flipped
7
+ from `=== "1"` (opt-in, v0.11.48–0.11.49) to `!== "0"` — only the exact value `0` opts out (rollback to the
8
+ bit-for-bit legacy env route, unchanged contract). Safe now because the v0.11.49 fd3 dedicated-pipe transport removed
9
+ the fd0/readPipedStdin race that caused the 0.11.46/47 silent-worker-death incident. The FIX-1 spawner write of
10
+ `"1"` stays (pins the child route deterministically + keeps a default-ON parent compatible with pre-flip workers
11
+ whose mirror gate still reads `=== "1"`); an opted-out parent keeps the token in the child env, which independently
12
+ disarms the worker reader. New live-worker integration pin: DEFAULT (gate unset) + fd3 payload → alive + no leak
13
+ (unit seams are structurally blind to this failure class — run the real-test battery T7 before publishing).
14
+
15
+ ## [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)
16
+
17
+ **Root cause (dynamically confirmed 5/5 + on-disk leak proof).** pi's `readPipedStdin` (main.js:52-65) consumes ALL
18
+ piped stdin as PROMPT CONTENT in every mode except `--mode rpc`. The v0.11.46 stdin handshake rode fd0 and raced it
19
+ three ways: (1) token JSON merged into the worker prompt — **secret leak, proven in a live transcript** (the model
20
+ reasoned about the credential line); (2) stream-ended creds-less silence; (3) our reader's `pause()` before EOF
21
+ drain deadlocking pi's boot await → **self-initiated exit 0** (not parent SIGTERM as first modeled) with
22
+ `takeOverStdout`-buffered ~0-byte stdout — the exact incident signature (~1.2s, 0 stdout, ×3 retry).
23
+
24
+ **Fix:** brokered child-pi workers now get a DEDICATED pipe as stdio entry 4 (fd3); the payload is written there
25
+ and read by `readFirstLineFromFd`. fd0 returns to `"ignore"` (pre-0.11.46 shape), killing every race ordering. The
26
+ detached background RUNNER keeps fd0 (plain node process, no race). Gate stays OPT-IN (`PI_CREW_BROKER_STDIN=1`);
27
+ default route bit-for-bit unchanged. **Evidence:** pre-fix gate-ON matrix on pi@1.0.4: 5/5 silent death; post-fix
28
+ 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
29
+ REPO-SRC extension proven via argv capture; security review 9/9 invariants.
30
+
31
+ **Also:** `maxTasksPerRun` is now enforced at manifest admission (pre-dispatch) instead of only at finalize —
32
+ oversized manifests were fully dispatched and >~2.5k-task handshake lines silently lose broker creds (writer now
33
+ warns >60 KiB). Worker-side fallback gate reads the RESOLVED env (`options.env ?? process.env` via `getCrewEnv`)
34
+ — closing the test seam that masked the earlier dead-wire. `check:env-vars` registry compliance for all 8 broker
35
+ identity reads; fd-mechanics tests skip on win32 (reader short-circuits there by design; brokered spawn is
36
+ auto-disabled on Windows).
37
+
38
+ ### Also in this release (earlier 2026-10-28) — cold-verified review fix wave (2026-10-28)
39
+
40
+ **Fixed (MAJOR) — the `PI_CREW_BROKER_STDIN=1` opt-in route was dead-wired end-to-end (review + 3-agent cold-verification, dynamically proven).**
41
+ Two compounding defects: (1) the parent never re-wrote the flag into the child's `built.env` (registry kind `host-only` strips
42
+ the ambient value), so the worker-side mirror gate (`resolveBrokerStdinFallbackEnabled`, which reads the child's OWN
43
+ `process.env`) could never activate — the stdin payload was written and never read, leaving every opt-in worker creds-less
44
+ (`NOOP_HANDLE`: no ask/message/mailbox/steer, no broker audit). (2) the token was omitted from `built.env` for ALL
45
+ branches, but only the headless-JSON branch delivers the payload — RPC (`child-pi-rpc.ts`) and surface-pane spawns compose
46
+ without it, so their children got neither token nor payload. Unit tests masked both: they flip the gate by mutating
47
+ `process.env` in-process, bypassing the spawn-env plumbing (structural seam: `startChildBrokerClientAsync` honors
48
+ `options.env` for identity vars but the gate reads ambient `process.env`).
49
+
50
+ Repair: `prepareSpawnContext` now writes `PI_CREW_BROKER_STDIN="1"` into `built.env` when the host gate is ON (registry
51
+ kind → `child-write`) and puts the token in `built.env` for every brokered spawn; only the headless compose (payload
52
+ present) strips it from the FINAL env (`buildFinalChildPiSpawnOptions`), so RPC/surface keep the env route — a token in
53
+ their env also disarms their stdin fallback (their stdin is the JSONL protocol / a TTY). Gate OFF: bit-for-bit legacy.
54
+ New composed-path tests assert the flag/token/payload matrix across all three call sites (no live-reader assertions — the
55
+ 0.11.46 reader exit-0 death is still open, default stays OPT-IN). Misleading docblocks + the factually-wrong registry
56
+ rationale ("parent decision fully determines the route") corrected. `argvIndicatesRpcMode` now also matches the
57
+ `--mode=rpc` equals-form.
58
+
59
+ **Fixed (MAJOR) — T7 live-smoke judged the WRONG run (newest-mtime race → false PASS).** The battery's T7 tier picked
60
+ `candidates[0]` (newest run dir ≥ start−60s) with zero binding to its own dispatch — a concurrent team run created in the
61
+ window got judged instead (two real run dirs on this host measured 25 ms apart), and the `pi -p` dispatch stdout/stderr
62
+ was piped with zero listeners, so a silent smoke death left no forensics. Now the dispatched goal carries a per-invocation
63
+ nonce (`[t7-nonce:<uuid>]`); only run dirs whose `events.jsonl` contain that nonce are judged, zero nonce dirs = FAIL, a
64
+ non-terminal run is a FAIL checked before the killed/hung diagnosis, and a capped tail (64 KB) of the dispatch's own
65
+ stdout/stderr is recorded in the failure reason, audit entry, and log output.
66
+
67
+ **Fixed (minor) — battery gate fail-open trigger paths.** `changedPaths()` now filters diff-base tags to release semver
68
+ (`^v\d+\.\d+\.\d+$` — a near-HEAD non-release tag previously shrank the diff window and silently un-triggered T7) and
69
+ parses `git status --porcelain` renames (`R old -> new`) into BOTH paths instead of one mangled string.
70
+ `prepublish-ci.mjs` now preserves `PI_CREW_BATTERY_SKIP` through the npm `prepublishOnly` path (the documented skip
71
+ hatch was dead there — the battery's own failure message recommended a var its caller stripped).
72
+
73
+ **Changed — T7 trigger coverage gaps closed** (`src/state|utils|agents|teams|workflows|hooks|observability`, `scripts/`):
74
+ cold-verification found the workflow-engine src subtrees and the gate's own scripts/ were invisible to the live gate; gate
75
+ changes now live-test the gate (this commit deliberately triggers T7 on its own pre-push).
76
+
77
+ **Changed — `before_agent_start` `systemPrompt` hardening (defense-in-depth).** Cold-verification refuted reachability
78
+ (`systemPrompt` is required in every host type def and synthesized by a runner getter, unlike `prompt` which arrives via
79
+ caller args — the GH #64 root cause), but the asymmetry was real: `autonomous-policy.ts` and `knowledge-injection.ts`
80
+ now coerce non-strings to `""` (append-only output, safe); `prompt-runtime.ts` SKIPS the rewrite instead (coercing `""`
81
+ there would blank a real system prompt — it strips sections). No behavior change on any current host.
82
+
83
+ **Added — LOUD oversize diagnostic in `buildBrokerStdinLine`.** The reader aborts past 64 KiB without a newline; a
84
+ ~2.5k-task manifest (~131 KiB measured live — pipe chunks make the effective Linux boundary ~2× the coded cap)
85
+ silently stripped EVERY task's broker creds. The writer now warns at 60 KiB via `logInternalError` (not PI_CREW_DEBUG-
86
+ gated); the line is still written, never blocked.
87
+
3
88
  ## [0.11.48] — HOTFIX: stdin broker-credential gate flipped OPT-IN — fixes silent worker death (2026-10-28)
4
89
 
5
90
  **Fixed — v0.11.46/0.11.47 regression: every headless JSON worker on the default route exited 0 with zero stdout.**