pi-crew 0.11.6 → 0.11.8
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/.githooks/pre-commit +22 -0
- package/.githooks/pre-push +56 -0
- package/CHANGELOG.md +216 -1
- package/dist/index.mjs +531 -508
- package/docs/publishing.md +29 -0
- package/index.ts +3 -3
- package/package.json +9 -6
- package/scripts/install-hooks.mjs +32 -0
- package/skills/real-test-pi-crew/SKILL.md +31 -18
- package/src/config/config-validation.ts +35 -0
- package/src/config/env-vars.ts +38 -0
- package/src/config/types.ts +71 -0
- package/src/extension/registration/foreground-run-controller.ts +19 -10
- package/src/extension/registration/lifecycle-handlers.ts +6 -491
- package/src/extension/registration/lifecycle.ts +7 -90
- package/src/extension/registration/render-loop.ts +502 -0
- package/src/extension/team-tool/doctor.ts +6 -0
- package/src/prompt/prompt-runtime.ts +33 -4
- package/src/runtime/child-pi/child-pi-spawn.ts +14 -0
- package/src/runtime/child-pi/child-pi.ts +130 -6
- package/src/runtime/child-pi/pi-version.ts +187 -0
- package/src/runtime/child-pi/session-recovery.ts +334 -0
- package/src/runtime/classifier/classifier-service.ts +298 -0
- package/src/runtime/dispatch-batch.ts +4 -0
- package/src/runtime/live-session/live-session-runtime.ts +31 -0
- package/src/runtime/model/model-scope.ts +141 -21
- package/src/runtime/model/pi-args.ts +166 -6
- package/src/runtime/peer-dep.ts +24 -0
- package/src/runtime/process/zombie-scanner.ts +168 -7
- package/src/runtime/rpc/README.md +139 -0
- package/src/runtime/rpc/frame-client.ts +328 -0
- package/src/runtime/rpc/rpc-worker.ts +271 -0
- package/src/runtime/rpc/ui-request-policy.ts +112 -0
- package/src/runtime/run-worker.ts +91 -3
- package/src/runtime/scheduling/run-coalesced-task-group.ts +3 -0
- package/src/runtime/skill-instructions.ts +66 -1
- package/src/runtime/surface/resolve-surface.ts +34 -19
- package/src/runtime/surface/surface-spawn.ts +8 -1
- package/src/runtime/task-output-context.ts +191 -32
- package/src/runtime/task-packet.ts +3 -0
- package/src/runtime/task-runner/child-executor.ts +172 -2
- package/src/runtime/task-runner/post-execution.ts +5 -3
- package/src/runtime/task-runner/pre-execution.ts +32 -1
- package/src/runtime/task-runner/prompt-builder.ts +10 -2
- package/src/runtime/task-runner.ts +6 -0
- package/src/schema/config-schema.ts +37 -0
- package/src/schema/team-tool-schema.ts +14 -2
- package/src/state/contracts.ts +5 -0
- package/src/ui/format-helpers.ts +11 -0
- package/src/ui/overlays/mailbox-compose-overlay.ts +15 -1
- package/src/ui/pi-ui-compat.ts +18 -9
- package/src/ui/rail.ts +14 -1
- package/src/ui/settings-overlay.ts +11 -2
- package/src/ui/widget/index.ts +0 -1
- package/src/ui/widget/task-list.ts +10 -3
- package/src/ui/widget/widget-renderer.ts +53 -4
- package/src/utils/relative-time.ts +11 -3
- package/src/utils/visual.ts +32 -2
package/docs/publishing.md
CHANGED
|
@@ -76,6 +76,35 @@ Users can install the published package with:
|
|
|
76
76
|
pi install npm:pi-crew
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
### Pre-push hook (local release gate, D3)
|
|
80
|
+
|
|
81
|
+
The repo ships committed git hooks under `.githooks/`. A plain
|
|
82
|
+
`npm install` activates them via the `prepare` script
|
|
83
|
+
(`scripts/install-hooks.mjs` sets `git config core.hooksPath .githooks` —
|
|
84
|
+
idempotent, and a no-op when git or a repo is absent). Manual activation
|
|
85
|
+
in an existing clone:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
git config core.hooksPath .githooks
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Scope of `pre-push` (the release-gate broadening; `pre-commit` handles
|
|
92
|
+
lockfile drift):
|
|
93
|
+
|
|
94
|
+
- **push to `refs/heads/main`** — `check:conflict-markers` + `typecheck`,
|
|
95
|
+
then the **FULL unit suite** (`npm run test:unit`, the same gate CI
|
|
96
|
+
runs). Any failure blocks the push. This closes the 2026-10 gap where
|
|
97
|
+
local pushes ran only `test:critical` (~120 tests) while CI ran the full
|
|
98
|
+
suite — three waves shipped red that way.
|
|
99
|
+
- **push to any other ref** — fast gates only (`check:conflict-markers` +
|
|
100
|
+
`typecheck`).
|
|
101
|
+
|
|
102
|
+
Emergency bypass (CI still gates `main`):
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
git push --no-verify
|
|
106
|
+
```
|
|
107
|
+
|
|
79
108
|
### Postinstall
|
|
80
109
|
|
|
81
110
|
`npm install` / `pi install` triggers `scripts/postinstall.mjs`, which:
|
package/index.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Resolution order:
|
|
5
5
|
* 1. dist/index.mjs (bundle) when present AND not explicitly disabled.
|
|
6
|
-
* Default since v0.9.17 close-out —
|
|
7
|
-
* ~
|
|
8
|
-
*
|
|
6
|
+
* Default since v0.9.17 close-out — re-measured 2026-10-01 (deep review
|
|
7
|
+
* §10/V7): ~36% faster cold-start on a cold Node cache; the gap narrows
|
|
8
|
+
* with a warm NODE_COMPILE_CACHE. The bundle's primary
|
|
9
9
|
* value is correctness (vendored typebox) and deterministic startup,
|
|
10
10
|
* not raw speed.
|
|
11
11
|
* See `scripts/bench-cold-start.mjs` for reproducible numbers.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-crew",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.8",
|
|
4
4
|
"description": "Pi extension for coordinated AI teams, workflows, worktrees, and async task orchestration",
|
|
5
5
|
"author": "baphuongna",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
10
|
"engines": {
|
|
11
|
-
"node": ">=22.
|
|
11
|
+
"node": ">=22.19.0"
|
|
12
12
|
},
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
"assets/crew-vibes.ttf",
|
|
46
46
|
"scripts/clean-strip-types.mjs",
|
|
47
47
|
"scripts/postinstall.mjs",
|
|
48
|
+
"scripts/install-hooks.mjs",
|
|
49
|
+
".githooks/",
|
|
48
50
|
"scripts/install-crew-vibes-font.mjs",
|
|
49
51
|
"scripts/build-crew-vibes-font.py",
|
|
50
52
|
"scripts/pty_probe.py",
|
|
@@ -101,6 +103,7 @@
|
|
|
101
103
|
"build:bundle:once": "node scripts/watch-bundle.mjs --once",
|
|
102
104
|
"dev": "node scripts/dev-runner.mjs",
|
|
103
105
|
"postinstall": "node scripts/postinstall.mjs",
|
|
106
|
+
"prepare": "node scripts/install-hooks.mjs",
|
|
104
107
|
"install:crew-font": "node scripts/install-crew-vibes-font.mjs",
|
|
105
108
|
"build:crew-font": "python scripts/build-crew-vibes-font.py",
|
|
106
109
|
"bench": "node scripts/run-bench.mjs",
|
|
@@ -147,10 +150,10 @@
|
|
|
147
150
|
},
|
|
148
151
|
"devDependencies": {
|
|
149
152
|
"@biomejs/biome": "^2.5.14",
|
|
150
|
-
"@earendil-works/pi-agent-core": "^1.0.
|
|
151
|
-
"@earendil-works/pi-ai": "^1.0.
|
|
152
|
-
"@earendil-works/pi-coding-agent": "^1.0.
|
|
153
|
-
"@earendil-works/pi-tui": "^1.0.
|
|
153
|
+
"@earendil-works/pi-agent-core": "^1.0.4",
|
|
154
|
+
"@earendil-works/pi-ai": "^1.0.4",
|
|
155
|
+
"@earendil-works/pi-coding-agent": "^1.0.4",
|
|
156
|
+
"@earendil-works/pi-tui": "^1.0.4",
|
|
154
157
|
"@sinclair/typebox": "^0.34.52",
|
|
155
158
|
"@types/node": "^25.9.8",
|
|
156
159
|
"tsx": "^4.23.15",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// install-hooks.mjs — D3: point git at the committed .githooks directory.
|
|
3
|
+
//
|
|
4
|
+
// Idempotent + environment-tolerant: exits 0 when git is absent, when the
|
|
5
|
+
// cwd is not a git repo, or when `git config` fails for any reason — a
|
|
6
|
+
// developer convenience must never break `npm install` (the same reason
|
|
7
|
+
// postinstall.mjs is best-effort). Wired as the package.json `prepare`
|
|
8
|
+
// script so a plain `npm install` activates BOTH committed hooks:
|
|
9
|
+
// .githooks/pre-commit — lockfile-drift gate (check-lockfile-sync)
|
|
10
|
+
// .githooks/pre-push — release gate (conflict markers + typecheck,
|
|
11
|
+
// + FULL test:unit when pushing to refs/heads/main)
|
|
12
|
+
import { spawnSync } from "node:child_process";
|
|
13
|
+
|
|
14
|
+
function git(args) {
|
|
15
|
+
return spawnSync("git", args, { encoding: "utf8" });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function main() {
|
|
19
|
+
const probe = git(["rev-parse", "--is-inside-work-tree"]);
|
|
20
|
+
if (probe.error || probe.status !== 0) {
|
|
21
|
+
console.log("[install-hooks] no usable git here — skipping core.hooksPath setup");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const set = git(["config", "core.hooksPath", ".githooks"]);
|
|
25
|
+
if (set.error || set.status !== 0) {
|
|
26
|
+
console.warn("[install-hooks] could not set core.hooksPath (non-fatal):", set.stderr?.trim() ?? set.error?.message);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
console.log("[install-hooks] git core.hooksPath → .githooks (pre-commit + pre-push active)");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
main();
|
|
@@ -3,7 +3,7 @@ name: real-test-pi-crew
|
|
|
3
3
|
description: >
|
|
4
4
|
LIVE end-to-end battery for pi-crew changes — unit-green ≠ works in a real Pi session; this skill proves the latter.
|
|
5
5
|
USE WHEN: you changed src/runtime/broker|child-pi|surface|prompt|state, src/ui, src/config, src/schema/team-tool-schema.ts, agents/*.md, workflows, CI, or dist/ — before committing; a worker hung/died/"no output for 600000ms"; a run went green but panes/UI/ask/steer look wrong; someone says "verify it really works".
|
|
6
|
-
TIERS AT A GLANCE: T1 critical (
|
|
6
|
+
TIERS AT A GLANCE: T1 critical (~19s) · T2 kill-switch · T3 bundle+md5 · T4/T8 live-session sync · T5/T6 TUI probes · T7 smoke run · T9 team-tool feature battery · T10 surface panes (tmux + herdr) · T11 regression battery (a–j) · T12 agent-frontmatter contracts · T13 real-run UI render. A decision table at the TOP of the file maps changed-path → required tiers.
|
|
7
7
|
When NOT to use: unit tests for isolated modules (use the test runner directly); pure test execution.
|
|
8
8
|
|
|
9
9
|
origin: pi-crew
|
|
@@ -86,7 +86,7 @@ End-to-end verification discipline for pi-crew changes. Distilled from the broke
|
|
|
86
86
|
|
|
87
87
|
| Tier | Chứng minh gì | Chi phí | Section |
|
|
88
88
|
|---|---|---|---|
|
|
89
|
-
| 1 | critical suites xanh (broker/UI) |
|
|
89
|
+
| 1 | critical suites xanh (broker/UI) | ~19s | Tier 1 |
|
|
90
90
|
| 2 | kill-switch tắt đúng qua 3 đường | 75s | Tier 2 |
|
|
91
91
|
| 3 | typecheck + rebuild bundle + md5 sync | 25s | Tier 3 |
|
|
92
92
|
| 4 | session sống nhận được bundle mới | <1s + restart | Tier 4 |
|
|
@@ -123,6 +123,11 @@ End-to-end verification discipline for pi-crew changes. Distilled from the broke
|
|
|
123
123
|
| `scripts/test-runner.mjs` (the runner itself) | T1 (exercises it) + `test/unit/scripts/test-runner-exit.test.ts` (21 tests: exit codes, cancelled-guard red-green, missing-file pre-check, batch shard empirical) + ONE batch-mode run (`PI_CREW_TEST_BATCH_SIZE=2 node scripts/test-runner.mjs --shard=0/1 <3 small files>`). Dev script — NOT bundled, no T3/T4 needed |
|
|
124
124
|
| `scripts/check-lazy-imports.mjs` | Run the gate on a clean tree (`node scripts/check-lazy-imports.mjs` exit 0) + re-probe the pattern battery vs the KNOWN GAPS comment (round-3 lesson: a skip-rule fix can OPEN a new false-skip hole — probe both directions). NOT bundled |
|
|
125
125
|
| `dist/` (bundle) | T3 → T4 → T8 → T11j |
|
|
126
|
+
| `src/runtime/rpc/**` (worker transport seam — RPC vẫn EXPERIMENTAL) | Pinned suites `test/unit/runtime/rpc/rpc-worker.test.ts` (10) + `test/unit/runtime/run-worker-rpc-seam.test.ts` (8, gồm DR1/DR5 exit-race) + **T7 smoke với `PI_CREW_WORKER_TRANSPORT=rpc`** khi đụng transport wiring; stdio fallback phải giữ structured warn |
|
|
127
|
+
| `src/runtime/classifier/**` + config ceremony (`classifierEnabled` / `classifierModel`) | `classifier-service.test.ts` (14) + `child-executor-retry-triage.test.ts` (6) + `npm run check:env-vars` — unit-only, KHÔNG live tier (seam DORMANT default off; chỉ live khi `PI_CREW_CLASSIFIER_ENABLED=1`) |
|
|
128
|
+
| `src/runtime/child-pi/pi-version.ts` / version-floor wiring (U9) | `test/unit/runtime/child-pi/pi-version.test.ts` (11) + `test/unit/runtime/model/pi-args-version-floor.test.ts` (7) + `test/unit/runtime/child-pi/pi-version-floor-wiring.test.ts` (2) |
|
|
129
|
+
| `.githooks/` + `scripts/install-hooks.mjs` | `bash -n .githooks/pre-push` + MỘT push thật quan sát hook (main → full `test:unit` + fast gates; ref khác → fast gates; kỳ vọng dòng `[pre-push] gates green.`) |
|
|
130
|
+
| `src/config/**` (env-vars keys MỚI) | `test/unit/config/config-schema-sync.test.ts` + `npm run check:env-vars` (mọi `PI_CREW_*` mới phải registered + routed qua `getCrewEnv`) — không cần live tier |
|
|
126
131
|
| bất kỳ đường dẫn trên, trước khi commit | gates đầy đủ: critical + unit + tsc + biome + md5 |
|
|
127
132
|
|
|
128
133
|
**When to use**: after any change to `src/runtime/broker/*.ts` (broker + tokens + issuer), `src/ui/`, `src/config/` (incl. `src/config/migration-validator.ts`), `src/extension/registration/lifecycle-handlers.ts`, `src/runtime/child-pi/*.ts` (worker spawn/kill/steering), `src/runtime/mcp-proxy.ts` + `src/runtime/model/pi-args.ts` (worker argv build + MCP role-gate — G2/G3, SDD-2), `src/runtime/async-runner.ts` + `src/runtime/background-runner.ts` (detached spawn + allowlist + entry-guarded `executeBackgroundRun` core — async dispatch, xem quy tắc tier ở decision table), `src/runtime/heartbeat/*` + `src/ui/heartbeat-aggregator.ts` (ambient heartbeat — guest `gc-*` skip), `src/runtime/surface/*.ts` (MuxSurface providers, degrade, launch script), `src/prompt/*.ts` (worker-side tools: ask / message / delegate / surface-worker recorder), `src/runtime/goal-workflow/plan-templates.ts`, `src/runtime/team-runner.ts` or `src/runtime/task-runner/**` (scheduler / execution — Tier 7 smoke), `src/state/**` (durable state — Tier 7 + 9a events/status + **Tier 11a read-your-writes**), `src/utils/paths.ts` + `src/utils/project-markers.ts` + `src/runtime/stale-reconciler.ts` + `src/extension/team-tool/health-monitor.ts` (run-state layout + symlink defense — pinned suites listed in the 2026-09-20 update), `src/runtime/live-session/**` + `src/runtime/custom-tools/*` (live-session mode + worker custom tools), `src/schema/team-tool-schema.ts` (or any `Type.Unsafe({...})` schema definition), `src/extension/registration/team-tool.ts`, `workflows/*.workflow.md`, `.github/workflows/*.yml` (CI env — Tier 11e), `scripts/wc-gate.mjs` (Tier 11b), or before any commit touching these paths. Schema changes additionally require Tier 9 (feature battery) because the team tool's TypeBox schema is validated by pi-ai BEFORE the handler runs — a too-strict or malformed schema breaks every action silently. Surface changes additionally require Tier 10 (surface-mode battery) because surface is fail-closed: every failure degrades to headless and the run still goes green — only pane-level evidence proves the panes engaged. Resource `.md` changes (agent bodies/frontmatter, skill metadata, discovery, frontmatter parsing) additionally require **Tier 12** (resource-contract battery) because the agent/team/workflow frontmatter parser is line-based, not YAML — a folded scalar parses as `">"` for every consumer while all other tiers stay green.
|
|
@@ -140,6 +145,7 @@ End-to-end verification discipline for pi-crew changes. Distilled from the broke
|
|
|
140
145
|
> **2026-09-29 update (RR-021 optimization wave + 4 review rounds, baseline `79a758d2`..`4393858c`, 37 commits; story `docs/stories/RR-021.md`; audit `pi-crew-optimization-audit.md`)**: (1) **Correctness/hang-risks** — `classifyReviewOutcome` word-boundary fix (the `\baccept\b` string-literal bug + negation guard), git/rg probe timeouts, `withRunLockSync`→`withRunLock` rerouting (lock family v0.9.26 intact), `reconcileAllStaleRuns` concurrent (`mapConcurrent` bound 4, per-run error isolation), sleeps→waitFor. (2) **Bundle hygiene** — esbuild/acorn/diff load via lazy sync-createRequire shims, ajv external, dead code −767 lines: bundle 3360 KB → **1575.2 KB**; NEW gates `check:lazy-imports` (ci.yml:142; src-side `// LAZY:` marker enforcement + dist-side no-hoisted-heavy-import assertion) and `check:bundle-size` (ci.yml:169). (3) **Test-gate integrity (review rounds 2–4)** — the runner now fails LOUDLY on `# cancelled N>0` (dual reporter: stdout UX unchanged, full TAP to a temp file, scraped in BOTH single-run and batch paths — `parseCancelledCount` + 21-test `test-runner-exit.test.ts`), on explicitly-listed files that do not exist (node:test v22 silently skipped them → exit 0; round-3 MAJOR B1), and `test:bundle` routes through the guarded runner (was the only CI gate bypassing it). Incremental tsc: typecheck ~20s → **~3.5s warm**. Suite anchors @ 2026-09-29: unit **8204 tests / 8201 pass / 0 fail / 3 skipped** (909 files), integration **136 / 132 pass / 4 skipped** (35 files incl. 3 slow), `test:critical` **116** (~18s), bundle **1575.2 KB** md5 `35ca335a90da01ac631493f220aaf121` (`--committed-hash` OK). Line refs moved: `PI_CREW_TEST_RUNNER_TIMEOUT_MS` now `scripts/test-runner.mjs:419`; crash-recovery design comment `:228`.
|
|
141
146
|
|
|
142
147
|
> **2026-10-01 update (SDD-2 W-B — G2 MCP role-gate + G3 spill-always + Phase D bump 0.99.1, commits `66f37875`→`f0b343ed`, execution record §13 `pi-crew-sdd-2026-09-30-buoi1.md`)**: (1) **G2 flips full-loadout sanity semantics** — MCP presence in workers is now ROLE- and PATH-dependent: the live-session SDK path strips MCP (`DefaultResourceLoader({ extensionsOverride: stripMcpExtensions })`) cho read-only roles (`mcpPermittedForRole` — explorer/reviewer/security-reviewer/analyst/critic/planner + unknown default-deny); a read-only worker WITHOUT MCP is CORRECT, a write-role worker without MCP is a regression; nếu strip không arm được (SDK hết export `DefaultResourceLoader`…) → fail-open + warning event `task.mcp_enforcement_degraded` (registry 168 → 169, `check:event-types --enforce` clean). CLI child-process path CHƯA cover (WI-4 defer: `~/.pi/agent/mcp.json` trống + `-ne` giết provider `oc-go` — read-only roles vẫn discover adapter MCP trên đường đó). (2) **G3 spill-always**: MỌI task text qua `task.md` 0600 — argv worker KHÔNG bao giờ chứa task text; probe đọc argv phải expect `@<temp>/task.md`, task ngắn mất prefix `Task: `; `TASK_ARG_LIMIT` đã xóa. (3) Phase D bump `@earendil-works/*` → 0.99.x: SDK bỏ option `enableMCP`, tool seam cần `ExtensionToolContext` (export mới `src/extension/pi-api.ts`). Decision table thêm row `mcp-proxy.ts`/`model/pi-args.ts`; D5 loadout line refs dời `:283-330` → `:280-336`.
|
|
148
|
+
> **2026-10-07 update (0.11.6 → 0.11.7/0.11.8-pre stack, GH#61/#62 + pi 1.0.4 adoption + RPC hardening, HEAD `11e717f5`)**: (1) **GH#61 (0.11.6)** — directory `pi.extensions` entries được normalize sang entry-file identity (`resolveExtensionDirEntry`): child worker không còn double-load directory extension (`Tool "…" conflicts … exit 1` — CHANGELOG 0.11.6 block, `CHANGELOG.md:207`). (2) **pi 1.0.4 adoption** — devDeps `@earendil-works/*` ^1.0.4 (`package.json:153-156`; peerDeps `:136-139` giữ `*`), host pi 1.0.4. (3) **U2-lite MCP parity cut** — parity spawns fold `mcp__*` vào disallow list trên MỌI host (`pi-args.ts:414`) + `--no-mcp` khi host ≥ `PI_NO_MCP_FLOOR="1.0.4"` (`pi-version.ts:76`, gate `pi-args.ts:430`); hermetic spawns MCP-clean qua `--no-extensions` — probe loadout: worker KHÔNG thấy `mcp__*` là ĐÚNG trên cả parity lẫn hermetic path (verdict `docs/reviews/pi-1.0.4-upgrade-notes-2026-10-06.md`). (4) **RPC transport wired (W7)** — `PI_CREW_WORKER_TRANSPORT=rpc` chọn RPC transport tại run-worker seam (`runWorker`, `src/runtime/run-worker.ts:138`; env names `src/runtime/rpc/rpc-worker.ts:43-49`), precedence **env > `runtime.workerTransport` config > stdio**, invalid → stdio (`config-schema.ts:157`); probe recipe = T7 smoke với `PI_CREW_WORKER_TRANSPORT=rpc`; DR1/DR5 exit-race hardening + `PI_CREW_RPC_TURN_TIMEOUT_MS` (default 600_000; dialog policy `PI_CREW_RPC_DIALOG_ANSWER` default `cancel`) — suite `run-worker-rpc-seam.test.ts` (8) có 3 scenario MỚI: DR1 exit-race `:223`, DR5 started-then-fail KHÔNG fallback `:266`, DR1 turn-timeout `:298` (+ truth table `:328`); pin kèm `test/unit/runtime/rpc/rpc-worker.test.ts` (10). RPC vẫn EXPERIMENTAL — fail trước agent output → stdio fallback (structured warn). (5) **Classifier seam DORMANT (P2-1)** — default OFF (`config-schema.ts:95`; `resolveClassifierEnabled`: env `PI_CREW_CLASSIFIER_ENABLED` > config > FALSE, `classifier-service.ts:158`); battery KHÔNG dính trừ khi set `PI_CREW_CLASSIFIER_ENABLED=1` (+ `PI_CREW_CLASSIFIER_MODEL`, default `opencode/jev-1.13-free`) — unit-only (`classifier-service.test.ts` 14 + `child-executor-retry-triage.test.ts` 6), KHÔNG live tier. (6) **Pre-push hook (D3)** — `.githooks/pre-push` qua `git config core.hooksPath .githooks` (wired `package.json` `prepare` → `scripts/install-hooks.mjs`): MỌI push chạy `check:conflict-markers` + typecheck; push `refs/heads/main` TỰ chạy FULL `test:unit` (~13-14 phút) rồi mới in `[pre-push] gates green.`; bypass `git push --no-verify` CHỈ khẩn cấp (CI vẫn gate main). (7) **GH#62 foreground completion** (`62c9ed0d`→`51adb1e1`→`3ed0067e`→`c956b2d4`→`af462d7b`) — foreground-run completion path sống sót qua spread-copy contexts; suite `test/unit/extension/registration/foreground-run-completion.test.ts` (4 tests). (8) **hermeticWorkers DEFAULT ON** (`config-schema.ts:89`; env `PI_CREW_HERMETIC_WORKERS`; `resolveHermeticWorkers` `pi-args.ts:149` — `explicit ?? true`). (9) **U9 version-floor wiring** — suites `test/unit/runtime/child-pi/pi-version.test.ts` (11) + `test/unit/runtime/model/pi-args-version-floor.test.ts` (7) + `test/unit/runtime/child-pi/pi-version-floor-wiring.test.ts` (2). (10) **RT-11 lesson (`48cc088d`)** — static spawn-site scan (`child-pi-spawn-registration.test.ts:33`) đếm CẢ prose trong comment (`spawn (`); khi dash vì comment → **reword comment, KHÔNG relax test**. Suite anchors @ 2026-10-07: unit **8711 tests / 8707 pass / 1 flaky-under-load / 3 skipped** (968 files, ~13 min; flaky = `team-tool-resume-liveness` F1 liveness race — 6/6 pass isolated ×3, KHÔNG phải regression), integration **139 / 135 pass / 4 skipped** (exit 0 — CHỈ khi scrub ĐỦ `PI_CREW_*`/`PI_TEAMS_*`, xem Failure-symptoms row MAX_DEPTH), `test:critical` **120** (~19-20s), bundle **1620.1 KB** (1,658,985 B) md5 `20544cf9ea436c69b0b434462def91b9` (`--committed-hash` OK), event registry **176 registered / 141 emitted** (`check:event-types --enforce` clean), devDeps `@earendil-works/*` ^1.0.4 / host pi **1.0.4**.
|
|
143
149
|
> **2026-09-21 update (F-L2 — steer boot-window loss, found by the live feature battery)**: a steer written to `<artifactsRoot>/steering/<taskId>.jsonl` while the worker extension is still LOADING was silently lost forever — `registerPiTeamsPromptRuntime` starts the file poll at load time, pi's loader gates action methods until bind (`sendMessage: notInitialized` THROWS), and the old poll advanced `lastOffset` to `stat.size` BEFORE the line loop while its per-line catch swallowed the throw as "malformed line" (run `team_20260921035840`, root-caused by watcher-driven bisection probes R7-R9: +0.65s post-boot = lost, +2.5s+ = delivered). Fix (`src/prompt/prompt-runtime.ts`): walk the buffer by byte offset, advance only past terminal verdicts (delivered/non-steer/rejected/malformed), rewind to the failing line on a DELIVERY failure, `createSeenSteerIdSet.unmark()` releases the dedup id so the retry is not skipped. Regression suite `test/unit/prompt/prompt-runtime-steer-boot-window.test.ts` (4 tests) — mutation-checked both layers (offset-revert → 4 fail; unmark-removal → exactly the id-bearing test fails). Note: `dist/index.mjs` is UNCHANGED by this fix — child workers load prompt-runtime from the SOURCE path (`pi-args.ts PROMPT_RUNTIME_EXTENSION_PATH`), so the fix is live without a bundle bump.
|
|
144
150
|
>
|
|
145
151
|
> **F-L2 live re-verification (post-restart, 2026-09-21 afternoon)** — two independent proofs: (a) **lab**: pre-write the steer, then `PI_CREW_STEERING_FILE=<file> pi --extension <repo>/src/prompt/prompt-runtime.ts -p '<long task>'` — stderr shows `[pi-crew:prompt-runtime.steer-delivery-failed] Extension runtime not initialized…` ×2 (pre-bind ticks now LOGGED instead of swallowed) then the post-bind tick delivers and the model complies; (b) **real team run**: watcher writes at **+0.51s** after the steering dir appears (the previously-lost window) → worker session log gains the `custom_message` and the probe token lands in `results/<taskId>.txt`. Suite anchors @ 2026-09-21 (post-fix): unit **8006 tests / 8003 pass / 0 fail / 3 skipped** (875 files; 1088s under load), integration **134 / 130 pass / 4 skipped**, `test:critical` **116**, bundle **3360.1 KB** md5 `2f45df91ed0580ee5bb5b9b57a8a8c3f` (UNCHANGED — see the source-path note above).
|
|
@@ -202,11 +208,11 @@ The skill maps to existing CI gates as follows:
|
|
|
202
208
|
| Bundle-staleness check (incl. **ARCH-7 path-leak scan** since `7d18508b` — line-scans `dist/index.mjs` + structural sourcemap check for tracked-source leaks) | Tier 3 last step | `scripts/check-bundle-staleness.mjs`; `--committed-hash` mode = Tier 11j release gate |
|
|
203
209
|
| `npm run test:bundle` (bundle import smoke, 2 tests) | Tier 3 post-build sanity | `test/unit/bundle-load.test.ts` — routed through the guarded `scripts/test-runner.mjs` since RR-021 round-3 (was a bare `node --test` bypass, the only CI gate without the wrappers) |
|
|
204
210
|
| `node scripts/release-smoke.mjs` (manual, release cut) | Tier 3/11j companion | ARCH-6: installs pi-* peer deps, `import()`s the tarball-installed bundle (`:77`), shape-checks exports |
|
|
205
|
-
| Full `npm test` (= unit
|
|
211
|
+
| Full `npm test` (= unit 968 files + integration 32 + 3 slow @ 2026-10-07) | n/a — too slow for in-loop | CI only; slow tier (3 files) is a SEPARATE glob `test:integration:slow` — only `npm run test:full` includes it |
|
|
206
212
|
| `PI_CREW_SMOKE=1` env | Tier 11e | set ONLY in `weekly-smoke.yml` (auth-gated); nightly.yml deliberately does NOT (comment at `:24`) |
|
|
207
213
|
| `npm run check:decision-drift` | Tier 11 (doc↔code) | **build-blocking in `.github/workflows/ci.yml` since 2026-09-17** (review-remediation baseline): every `PI_CREW_*` token cited in `docs/decisions/*.md` must exist in `src/` |
|
|
208
214
|
| `npm run check:env-vars` | Tier 11 | **build-blocking in `ci.yml` since 2026-09-17**: every `PI_CREW_*` read must be routed via `getCrewEnv` + registered |
|
|
209
|
-
| `npm run check:event-types -- --enforce` | Tier 9a/11 | **build-blocking in `ci.yml` since 2026-09-17**: event registry (`src/state/contracts.ts`) must cover every emitted type — 168 registered / 0 drift @ 2026-09-17 (was 89, drift silent); **169 @ 2026-10-01** sau SDD-2 (+`task.mcp_enforcement_degraded`) |
|
|
215
|
+
| `npm run check:event-types -- --enforce` | Tier 9a/11 | **build-blocking in `ci.yml` since 2026-09-17**: event registry (`src/state/contracts.ts`) must cover every emitted type — 168 registered / 0 drift @ 2026-09-17 (was 89, drift silent); **169 @ 2026-10-01** sau SDD-2 (+`task.mcp_enforcement_degraded`); **176 registered / 141 emitted @ 2026-10-07** — đếm bằng chính enforcement script (trong `TEAM_EVENT_TYPES` có 1 token trùng: 177 tokens / 176 unique) |
|
|
210
216
|
| `npm run check:lazy-imports` | Tier 3 companion (RR-021 WI-2.3 + review rounds 2-4) | `scripts/check-lazy-imports.mjs` — src-side: every runtime `import()` position needs a `// LAZY:` marker (known gaps: ternary-else / object-property / case-label / runtime member-access after same-line colon — documented in-script); dist-side: no module-scope hoisted heavy import (esbuild/acorn/diff/jiti/cli-highlight/yaml/ajv). Build-blocking at `ci.yml:142` |
|
|
211
217
|
| `npm run check:bundle-size` | Tier 3 companion | `scripts/check-bundle-size.mjs` — committed bundle ≤ budget (Sprint 6 gate, `ci.yml:169`; budget re-anchored when RR-021 shrank the bundle 3360→1575 KB) |
|
|
212
218
|
| `PI_CREW_TEST_RUNNER_TIMEOUT_MS` env | Tier 1 companion | overrides the test-runner spawn deadline (`scripts/test-runner.mjs:419` since RR-021; was :206); default **1_500_000 ms** since 2026-09-17 (was 900_000 — sized for a 5800-test suite; the full suite outgrew it and the ETIMEDOUT became a false green pre-F05). Since RR-021 rounds 2-3 the runner ALSO fail-closes on `# cancelled N>0` (TAP dual-reporter scrape) and on explicitly-listed missing files — see `test/unit/scripts/test-runner-exit.test.ts` (21 tests) |
|
|
@@ -235,11 +241,11 @@ To add Tier 1 to CI as a fast-feedback gate (under 30s):
|
|
|
235
241
|
|
|
236
242
|
---
|
|
237
243
|
|
|
238
|
-
## Tier 1 — Critical unit tests (~
|
|
244
|
+
## Tier 1 — Critical unit tests (~19s, 120 tests, the only suite you need for broker/UI changes)
|
|
239
245
|
|
|
240
|
-
**What**: run the curated
|
|
246
|
+
**What**: run the curated 15-file fast subset.
|
|
241
247
|
|
|
242
|
-
**Why this exists**: full `npm run test:unit` runs
|
|
248
|
+
**Why this exists**: full `npm run test:unit` runs 968 files (was 642 at skill-writing time — it keeps growing), several minutes. Verifier worker response timeout would kill the worker mid-run → run = "hang". The fix (introduced in commit `1cb2dca`) splits out a `test:critical` subset covering exactly what changed in the broker/UI work.
|
|
243
249
|
|
|
244
250
|
**How**:
|
|
245
251
|
|
|
@@ -247,16 +253,16 @@ To add Tier 1 to CI as a fast-feedback gate (under 30s):
|
|
|
247
253
|
time npm run test:critical
|
|
248
254
|
```
|
|
249
255
|
|
|
250
|
-
Expected output: `# tests
|
|
256
|
+
Expected output: `# tests 120 # pass 120 # fail 0 # cancelled 0 # duration_ms ~19965`. (Count was 97 at v0.9.46, 101 at v0.9.66, 102 at the waitMethodsEnabled flip, 116 since 2026-09-20, **120 @ 2026-10-07** (+`child-executor-heartbeat-pulse.test.ts` → 15 files) — verify with the actual run; the skill's hard-coded numbers drift between releases.) **Since RR-021 round-2 the runner itself fails loudly on `# cancelled N>0`** (dual-reporter TAP scrape — the round-1 unref incident shipped green with 2 cancelled tests), and **round-3 added the missing-file pre-check**: an explicitly-listed file that does not exist now exits 1 naming the path instead of being silently skipped by node:test.
|
|
251
257
|
|
|
252
258
|
**References**:
|
|
253
259
|
|
|
254
260
|
| What | Where |
|
|
255
261
|
|---|---|
|
|
256
|
-
| Script definition | `package.json:
|
|
262
|
+
| Script definition | `package.json:91` — list of 15 files passed to `node scripts/test-runner.mjs` |
|
|
257
263
|
| Introduced in commit | `1cb2dca fix(verifier): use test:critical instead of test:unit to avoid worker timeout` |
|
|
258
264
|
| Runner wrapper | `scripts/test-runner.mjs` — injects `--test-force-exit`, forwards to `tsx --test` |
|
|
259
|
-
| The
|
|
265
|
+
| The 15 files | broker: `test/unit/runtime/broker/crew-broker-{handshake,stale-socket,feature-flag,server-gate,client-fallback,mailbox-observer,close-during-reconnect,steer-dedup,symlink-steering}.test.ts`; UI: `test/unit/ui/keybinding-map.parity.test.ts`, `test/unit/ui/pi-tui-dispatch-probe.test.ts`; utils: `test/unit/utils/session-utils-extract.test.ts`; config: `test/unit/config/config-schema-sync.test.ts`; spawn env: `test/unit/runtime/child-pi/child-pi-env-spread.test.ts`; heartbeat: `test/unit/runtime/task-runner/child-executor-heartbeat-pulse.test.ts` (15th file @ 2026-10-07) |
|
|
260
266
|
| Failure mode that motivates it | Worker timeout in `src/runtime/child-pi/child-pi-constants.ts:23` (`RESPONSE_TIMEOUT_MS = DEFAULT_CHILD_PI.responseTimeoutMs` — 300000 at the time, now 600000); verifier LLM ran `npm test` and got killed with exit 143 (SIGTERM) |
|
|
261
267
|
|
|
262
268
|
**Run after**: any edit to `src/runtime/broker/*.ts`, `src/ui/`, `src/config/`, `src/extension/registration/lifecycle-handlers.ts`, or `src/runtime/child-pi/*.ts`.
|
|
@@ -287,7 +293,7 @@ PI_CREW_BROKER=0 npm run test:critical
|
|
|
287
293
|
PI_CREW_BROKER=1 npm run test:critical
|
|
288
294
|
```
|
|
289
295
|
|
|
290
|
-
All three must show `# pass
|
|
296
|
+
All three must show `# pass 120 # fail 0`. Measured times in this session (2026-08-11): ~26s for default, ~26s for `PI_CREW_BROKER=0`, ~26s for `PI_CREW_BROKER=1` (varies ±1-2s run-to-run).
|
|
291
297
|
|
|
292
298
|
**References**:
|
|
293
299
|
|
|
@@ -328,7 +334,7 @@ Compare the printed md5 against what the user's Pi session loaded. If they diffe
|
|
|
328
334
|
| Bundle builder | `scripts/build-bundle.mjs` (esbuild-based, bundles `index.bundle.ts` → `dist/index.mjs`) |
|
|
329
335
|
| Bundle resolution rule | `index.ts:1-25` (entrypoint docstring); also `scripts/build-bundle.mjs:14-20` (entrypoint preference); **symlink is live for source files but the bundled `dist/index.mjs` is loaded** |
|
|
330
336
|
| Postinstall hook | `scripts/postinstall.mjs:43` — best-effort bundle rebuild; falls back to strip-types if esbuild missing |
|
|
331
|
-
| Bundle md5 anchors | `1cc4d55e18add7b9a036c569143320b6` (Phase-4 flip, ~2.78 MB) → `16e29d053bd370e24f40df147dadcb79` (v0.9.66, 2026-08-11) → `9b557ac106b82e1ee33d39dd0d6c7dd7` (post-MuxSurface-A1 main, 2026-08-27) → `945720b1ad25673d86e263cdd834532f` (post-Batch-10 branch tip `aa899a1e`, 2026-09-11, ~3.30 MB) → `25ffc88e9a13611f61871b4c4a5b17ab` (review-remediation F01–F20 wave, 2026-09-17, 3357.3 KB) → `3860c0264940e7df91511da493fa375e` (2026-09-26, 1677.8 KB) → **`35ca335a90da01ac631493f220aaf121`** (RR-021 wave, 2026-09-29, **1575.2 KB** — lazy esbuild/acorn/diff shims + ajv external + −767 dead lines). **Always check current**: `md5sum dist/index.mjs` |
|
|
337
|
+
| Bundle md5 anchors | `1cc4d55e18add7b9a036c569143320b6` (Phase-4 flip, ~2.78 MB) → `16e29d053bd370e24f40df147dadcb79` (v0.9.66, 2026-08-11) → `9b557ac106b82e1ee33d39dd0d6c7dd7` (post-MuxSurface-A1 main, 2026-08-27) → `945720b1ad25673d86e263cdd834532f` (post-Batch-10 branch tip `aa899a1e`, 2026-09-11, ~3.30 MB) → `25ffc88e9a13611f61871b4c4a5b17ab` (review-remediation F01–F20 wave, 2026-09-17, 3357.3 KB) → `3860c0264940e7df91511da493fa375e` (2026-09-26, 1677.8 KB) → **`35ca335a90da01ac631493f220aaf121`** (RR-021 wave, 2026-09-29, **1575.2 KB** — lazy esbuild/acorn/diff shims + ajv external + −767 dead lines) → **`20544cf9ea436c69b0b434462def91b9`** (2026-10-07, **1620.1 KB** / 1,658,985 B — pi 1.0.4 adoption + RPC transport wiring + U2-lite MCP cut). **Always check current**: `md5sum dist/index.mjs` |
|
|
332
338
|
|
|
333
339
|
---
|
|
334
340
|
|
|
@@ -474,7 +480,7 @@ else:
|
|
|
474
480
|
|
|
475
481
|
**What**: prove the verifier worker completes within `RESPONSE_TIMEOUT_MS` (**600s since the stuck-worker hardening — was 300s when this skill was distilled; `DEFAULT_CHILD_PI.responseTimeoutMs = 10 * 60_000`**).
|
|
476
482
|
|
|
477
|
-
**Why this is its own tier**: `test:critical` covers unit-level invariants, but the verifier LLM is a separate failure mode — it reads the verifier prompt from `src/runtime/goal-workflow/plan-templates.ts:144, 147` (taskTemplate strings) or from `workflows/*.workflow.md` (workflow verifier sections), then decides which bash command to run. If the prompt says "Run tests" without specifying which, the LLM runs `npm test` (
|
|
483
|
+
**Why this is its own tier**: `test:critical` covers unit-level invariants, but the verifier LLM is a separate failure mode — it reads the verifier prompt from `src/runtime/goal-workflow/plan-templates.ts:144, 147` (taskTemplate strings) or from `workflows/*.workflow.md` (workflow verifier sections), then decides which bash command to run. If the prompt says "Run tests" without specifying which, the LLM runs `npm test` (968 files) and the worker gets killed by the response timeout with exit 143.
|
|
478
484
|
|
|
479
485
|
**How** (from parent Pi session — `team` is a tool, not a shell command):
|
|
480
486
|
|
|
@@ -788,7 +794,7 @@ grep -n "Log the event first" src/runtime/recovery/crash-recovery.ts # design
|
|
|
788
794
|
# 3. buffered-site census — snapshot & audit:
|
|
789
795
|
grep -rln "appendEventBuffered" src/ | wc -l # 15 files @ 2026-09-29 (16 at v0.10.5; RR-021 WI-4.2 dead-code sweep removed one); audited live conversions = 43; EVERY new site needs the reader-audit
|
|
790
796
|
# 4. the full gate — test:critical has NO stores/dwf/recovery coverage:
|
|
791
|
-
npm run test:unit #
|
|
797
|
+
npm run test:unit # 968 files, ~8711 tests @ 2026-10-07, ~13 min (785s node measured 2026-10-07; 1088s under load 2026-09-21) — MANDATORY after any delayed-write conversion program
|
|
792
798
|
```
|
|
793
799
|
|
|
794
800
|
### 11b. wc-gate enforcement (M4 done-gate)
|
|
@@ -1088,7 +1094,7 @@ PY
|
|
|
1088
1094
|
| Test using real `loadConfig()` to mock config | Flaky when env / disk config changes | `612e18b` | `test/unit/runtime/broker/crew-broker-server-gate.test.ts:78` (use `brokerEnv: "0"` instead of `flagOn: false`) |
|
|
1089
1095
|
| Source edit seen immediately | No, requires bundle rebuild + reload | n/a (permanent) | `index.ts:1-25` — bundle resolution rules |
|
|
1090
1096
|
| Skip disabled-path proof | `effectiveEnabled()` regression slips through | n/a (permanent) | Tier 2 above |
|
|
1091
|
-
| `npm run test:unit` against the full suite (
|
|
1097
|
+
| `npm run test:unit` against the full suite (968 files now, 642 then) | several minutes; mis-judges verifier runtime | n/a (permanent) | Tier 1 above |
|
|
1092
1098
|
| Skip typecheck | TS errors slip past `test:critical` (which uses `--test-timeout=30000`) | n/a (permanent) | Tier 3 above |
|
|
1093
1099
|
| Run `pi` from a stale bundle | Session shows old behavior despite src/ edits | n/a (permanent) | `scripts/check-bundle-staleness.mjs` — CI gate |
|
|
1094
1100
|
| Test by reading code | Proves nothing about runtime | n/a (permanent) | All tiers above |
|
|
@@ -1124,6 +1130,11 @@ PY
|
|
|
1124
1130
|
| **Declaring a red gate "pre-existing" (or "my regression") from reasoning alone** (2026-09-16): `check:env-vars` was red; the file was untouched, but that is an argument, not evidence. | n/a (process) | Prove it in a clean-HEAD worktree: `git worktree add -q /tmp/pc-head HEAD && (cd /tmp/pc-head && node scripts/check-env-vars.mjs); git worktree remove --force /tmp/pc-head` | Same recipe as the broker-flake proof (Tier 1 / Failure symptoms) |
|
|
1125
1131
|
| **Shipping a catalog PNG with tofu boxes** (2026-09-16): the catalog rendered the braille spinner + `⟳` + `⎿` as `□` — invisible in the `.txt` captures, obvious the moment the image was opened. | `docs/ui-samples/render_png.py` glyph self-check | Make the renderer FAIL on any character it cannot paint (compare against the `.notdef` box), then map the offender | Tier 13 "Catalog" + `render_png.py` (`_has_glyph`, `BRAILLE_TO`) |
|
|
1126
1132
|
| **Treating `wait-request-broker.test.ts` load-timeout as a product bug**: the test runner's per-file 180s timeout is below this file's full-suite runtime under parallel load — fails only with the whole suite, passes in isolation. Pre-existing flake, NOT a regression from your change. | n/a (test infra) | Re-run the single file before fixing anything: `node scripts/test-runner.mjs test/unit/runtime/broker/wait-request-broker.test.ts`. Green in isolation = infra flake; move on. |
|
|
1133
|
+
| **Pipe nuốt exit code của gate** (GH#62 wave, `51adb1e1`): `npm run typecheck \| tail -1` trả về exit của `tail` (0) — một type error từng được ship vì gate chạy qua pipe. | n/a (process) | KHÔNG bao giờ pipe một gate qua `tail`/`head`; chạy thẳng, hoặc `set -o pipefail` khi bắt buộc. Commit `51adb1e1` "pipe-swallowed gates in the previous commit" |
|
|
1134
|
+
| **Fake cache shape sai** (GH#62 CI-shard catch, `3ed0067e`): fake `new Map()` thiếu `.list()` → crash ở manifestCache path khi test chạm code thật. | `3ed0067e` | Dùng factory thật `createManifestCache(tmpdir)` (`src/runtime/manifest-cache.ts:218`, `.list()` :306) thay vì hand-rolled Map giả |
|
|
1135
|
+
| **URL win32 không đối xứng**: path Windows (`C:\…`) không phải URL hợp lệ khi truyền qua argv/env giữa process | n/a (permanent) | Luôn `pathToFileURL()` phía gửi + `fileURLToPath()` phía nhận, đối xứng với nguồn — tham chiếu cặp đang dùng: `src/runtime/peer-dep.ts:40,90,211` |
|
|
1136
|
+
| **RT-11 scan đếm cả comment prose** (`48cc088d`): static spawn-site scan regex `spawn\s*\(` match CẢ text trong comment — một comment "spawn (and…" thành call-site thứ 4 trong khi chỉ có 3 call thật + register | `48cc088d` | **Reword COMMENT, KHÔNG relax test** — enforcer là `test/unit/runtime/child-pi/child-pi-spawn-registration.test.ts:33` (regex cố ý lỏng) |
|
|
1137
|
+
| **Stale-test-after-refactor class — 3 vòng CI đỏ liền 2026-10-04/05**: test pin layout/text cũ không theo kịp refactor lành tính (literal-count 4→5 subsections sau khi chia section — `test/unit/runtime/core/task-handoff-template.test.ts`, commit `d7923d38`; OSC-8 dock-link regex — `test/unit/ui/widget-osc8-link.test.ts`, commit `db70cc82`; T-6b storm tick — `test/unit/runtime/task-runner/state-helpers-cas-contention.test.ts`, commit `57f1bbf8`). Hook header gọi đúng: "three waves in a row shipped red" | n/a (process) | Full suite trước push — giờ `.githooks/pre-push` enforce trên push main (update 2026-10-07); refactor layout/text → cập nhật pin test CÙNG commit |
|
|
1127
1138
|
|
|
1128
1139
|
---
|
|
1129
1140
|
|
|
@@ -1158,6 +1169,8 @@ When a tier fails, the recovery is usually quick. Match the symptom to the cause
|
|
|
1158
1169
|
| Ambient "N dead worker(s)" notification for a run that already reached a terminal status | Stale health marker re-firing on a terminal run — no live process behind it | Verify before acting: `ps -p <pid>` (dead), `manifest.status` (failed/done), `exit-code.txt`. Nothing to recover; marker clears via `/team-dashboard → health → K` (kill stale). Never kill a pid on a stale heartbeat alone — check pid + log first |
|
|
1159
1170
|
| Ambient "heartbeat dead"/"missing heartbeat" cho task guest `gc-*` (depth-2 delegate) trên run ĐANG CHẠY | Guest tasks không có kênh heartbeat (lifecycle do `delegate.requested/admitted/completed` sở hữu) — fixed 2026-09-26: watcher `73790eaa` + aggregator `b7ab5ef2` skip `agent==="delegate"` ở CẢ HAI lớp | Nếu tái xuất hiện trên build ≥ `b7ab5ef2`: check skip marker còn nguyên ở cả 2 nơi (watcher task-loop + `summarizeHeartbeats`). KHÔNG bao giờ kết luận worker thật chết chỉ từ notification của guest task — verify pid/manifest như row trên |
|
|
1160
1171
|
| Battery async run "xanh" nhưng event `async.spawned` có `data.inline:true`, hoặc không có process runner detached nào trong `ps` | **Seam leak**: shell có `PI_CREW_TEST_ASYNC_INLINE=1` (thường + `PI_CREW_ALLOW_MOCK=1`) export từ phiên test trước — async dispatch chạy in-process, battery đang chứng minh đường SAI (seam là unit-test tool) | Preflight battery: `env | grep PI_CREW_TEST_ASYNC_INLINE` phải RỖNG; sau mỗi async run trong T7/T9b: assert `async.spawned` KHÔNG có `data.inline`. Nếu dính: `unset` cả 2 biến rồi chạy lại tier |
|
|
1172
|
+
| Integration đỏ macOS/Node-24 `phase6` "argv must not contain task text" với task NGẮN (empty task vẫn xanh) — code giống hệt run xanh trước | **Random-path collision flake** (CI 2026-10-07): `@<mkdtemp-path>` argv arg chứa suffix ngẫu nhiên 6 ký tự [a-zA-Z0-9]; task 3 ký tự có P ≈ 1/15k bị substring-collision với suffix → assert `arg.includes(task)` tưởng là leak | Scan argv LOẠI arg `@<taskPath>` chính nó (fix `f67cf62d`) — path là random mkdtemp, không phải nội dung task; never "fix" bằng đổi task text dài hơn để trốn collision |
|
|
1173
|
+
| `phase6-runtime-hardening.test.ts` "depth guard blocks child workers at max depth before mock execution" fails 2/2 khi chạy từ worker shell dù xanh trong CI | **Env leak, không phải regression**: worker shell export sẵn `PI_CREW_MAX_DEPTH=4`/`PI_TEAMS_MAX_DEPTH=4` và `resolveCrewMaxDepth` ưu tiên ENV hơn `inputMaxDepth` tường minh (`src/runtime/model/pi-args.ts:128` — `env.PI_CREW_MAX_DEPTH ?? env.PI_TEAMS_MAX_DEPTH` check TRƯỚC input) → test set `maxDepth:2` vẫn thấy 4, `blocked:false` | Scrub ĐỦ biến (danh sách `-u` tĩnh KHÔNG đủ — biến thể của gotcha 2026-08-15): `SCRUB=$(env \| grep -oE '^(PI_CREW\|PI_TEAMS)_[A-Z_]+' \| sed 's/^/-u /' \| tr '\n' ' '); env $SCRUB npm run test:integration` |
|
|
1161
1174
|
| Steer written during worker BOOT never reaches the model (no error, no log; later steers work) | F-L2 (fixed 2026-09-21): poll advanced its offset past an entry whose `pi.sendMessage` threw pre-bind ("not initialized") and the per-line catch ate the throw — one-shot silent loss | Fixed in `prompt-runtime.ts` (offset rewind + dedup unmark); pinned by `prompt-runtime-steer-boot-window.test.ts`. On older builds: re-send the steer after ~5s — a steer that lands post-bind delivers fine |
|
|
1162
1175
|
| Steer "lost" but the file you wrote is FULL while a 0-byte `<taskId>.jsonl` sits beside it | **Probe bug, not a product bug** — the steering filename depends on run kind: team runs poll `<taskId>.jsonl`, crew_agent/subagent runs use `<slot>-agent.jsonl`; you wrote the wrong channel (no component reads it) | Re-run writing every `*.jsonl` in the steering dir (Tier 9g), or derive the name from the run kind. Cross-check `results/<taskId>.txt` + the worker session's `custom_message` before calling it a loss |
|
|
1163
1176
|
| Fix verified in unit tests but a live probe still fails | Live path ≠ unit path: wrong filename (above), wrong role/tool for the probe instruction, or the session did not cold-start the changed module | Confirm the loaded artifact (`--extension <path>` resolution for workers is the SOURCE file, so a restart is enough; parent-side needs bundle+restart), then re-run Tier 9g with text-only compliance |
|
|
@@ -1352,7 +1365,7 @@ The skill mentions specific commits, line numbers, and version pins. As the code
|
|
|
1352
1365
|
| Verify line refs after each `src/` commit | Every commit touching the cited file | `git log -p -- src/extension/registration/lifecycle-handlers.ts \| grep effectiveEnabled` — if line moved, update the skill |
|
|
1353
1366
|
| Verify commit hashes still exist | Quarterly or before major edits | `git log --oneline -1 <hash>` — if gone, find the equivalent newer commit |
|
|
1354
1367
|
| Verify version pins (v0.9.46, etc.) | Each release | `git log --oneline -- src/ui/run-dashboard.ts \| head -5` — confirm diag removal history (e3ee6fe2) still accurate |
|
|
1355
|
-
| Verify `test:critical` still has
|
|
1368
|
+
| Verify `test:critical` still has 15 files | Each `src/runtime/broker/*.ts` edit | `grep test:critical package.json` — adjust the file list |
|
|
1356
1369
|
| Verify Tier 7 verifier prompts still say `test:critical` | Each workflow file edit | `grep "Run FAST checks" workflows/*.workflow.md` |
|
|
1357
1370
|
| Verify Tier 10 surface refs | Each `src/runtime/surface/**` edit | `ls test/system/surface-*.e2e.test.ts` + grep `MAX_SURFACE_WORKERS` in resolve-surface.ts — cap/config shape may drift between A1 → A2 |
|
|
1358
1371
|
| Verify herdr wire details | Each herdr release bump | `herdr api schema --json` vs `src/runtime/surface/herdr-provider.ts` (envelope/pane.read source/1-conn-per-request were verified on herdr 0.8.2) |
|
|
@@ -1373,7 +1386,7 @@ The skill does NOT need to be updated for every commit — only when the cited l
|
|
|
1373
1386
|
## Quick reference — exact commands
|
|
1374
1387
|
|
|
1375
1388
|
```bash
|
|
1376
|
-
# Tier 1 (critical unit, ~
|
|
1389
|
+
# Tier 1 (critical unit, ~19s, 120 tests)
|
|
1377
1390
|
npm run test:critical
|
|
1378
1391
|
# Tier 2 (3-path proof, broker changes only)
|
|
1379
1392
|
PI_CREW_BROKER=0 npm run test:critical
|
|
@@ -1457,7 +1470,7 @@ PY
|
|
|
1457
1470
|
|
|
1458
1471
|
Before claiming "tested":
|
|
1459
1472
|
|
|
1460
|
-
- [ ] Tier 1: `test:critical` fresh-run, all pass (<25s). Count varies by release — was 97 at v0.9.46, 101 at v0.9.66, 102 at the waitMethodsEnabled flip,
|
|
1473
|
+
- [ ] Tier 1: `test:critical` fresh-run, all pass (<25s). Count varies by release — was 97 at v0.9.46, 101 at v0.9.66, 102 at the waitMethodsEnabled flip, 116 @ 2026-09-20, **120 @ 2026-10-07**; record the actual count in the report.
|
|
1461
1474
|
- [ ] Tier 2: 3-path proof all pass — **required if you touched `src/config/defaults.ts` or `src/extension/registration/lifecycle-handlers.ts`**
|
|
1462
1475
|
- [ ] Tier 3: `npm run typecheck` exit 0, `npm run build:bundle` exit 0
|
|
1463
1476
|
- [ ] Tier 4: bundle md5 matches what the session loaded (or user has `/quit`-ed + reopened)
|
|
@@ -77,6 +77,11 @@ const LIMIT_CEILINGS = {
|
|
|
77
77
|
// (10_000 turns), which capped the effective timeout at 10s and silently disabled
|
|
78
78
|
// any larger value (e.g. 300_000 = 5min) via parsePositiveInteger returning undefined.
|
|
79
79
|
runtimeTaskTimeoutMs: 24 * 60 * 60 * 1000,
|
|
80
|
+
// Handoff budget (est tokens, chars/4) for the dynamic.dependencyContext
|
|
81
|
+
// prompt layer. Above the ceiling the budget is treated as the OFF-switch
|
|
82
|
+
// (untrimmed render), mirroring the render-site semantics in
|
|
83
|
+
// task-output-context.ts.
|
|
84
|
+
runtimeHandoffBudgetTokens: 1_000_000,
|
|
80
85
|
nestingMaxSlots: 64,
|
|
81
86
|
nestingMaxDepth: 10,
|
|
82
87
|
} as const;
|
|
@@ -325,6 +330,21 @@ function parseModelFallbackConfig(value: unknown): CrewRuntimeConfig["modelFallb
|
|
|
325
330
|
return Object.values(modelFallback).some((entry) => entry !== undefined) ? modelFallback : undefined;
|
|
326
331
|
}
|
|
327
332
|
|
|
333
|
+
/**
|
|
334
|
+
* Handoff budget for the dynamic.dependencyContext prompt layer (est tokens,
|
|
335
|
+
* chars/4). Deliberately NOT parsePositiveInteger: out-of-range values are
|
|
336
|
+
* dropped silently (undefined → render site falls back to default 1800)
|
|
337
|
+
* rather than rejecting the whole runtime block. The ≤0 / >1_000_000
|
|
338
|
+
* OFF-switch is expressible only via env PI_CREW_HANDOFF_BUDGET_TOKENS
|
|
339
|
+
* (env bypasses this parse and resolves first); the config schema documents
|
|
340
|
+
* 1..1_000_000 as the valid range.
|
|
341
|
+
*/
|
|
342
|
+
function parseHandoffBudgetTokens(value: unknown): number | undefined {
|
|
343
|
+
if (typeof value !== "number" || !Number.isInteger(value)) return undefined;
|
|
344
|
+
if (value < 1 || value > LIMIT_CEILINGS.runtimeHandoffBudgetTokens) return undefined;
|
|
345
|
+
return value;
|
|
346
|
+
}
|
|
347
|
+
|
|
328
348
|
function parseRuntimeConfig(value: unknown): CrewRuntimeConfig | undefined {
|
|
329
349
|
const obj = asRecord(value);
|
|
330
350
|
if (!obj) return { inheritContext: true } as CrewRuntimeConfig;
|
|
@@ -362,10 +382,25 @@ function parseRuntimeConfig(value: unknown): CrewRuntimeConfig | undefined {
|
|
|
362
382
|
return Object.values(parsed).some((v) => v !== undefined) ? parsed : undefined;
|
|
363
383
|
})(),
|
|
364
384
|
excludeContextBash: parseWithSchema(Type.Boolean(), obj.excludeContextBash),
|
|
385
|
+
handoffBudgetTokens: parseHandoffBudgetTokens(obj.handoffBudgetTokens),
|
|
365
386
|
agentExtensions: parseStringList(obj.agentExtensions),
|
|
366
387
|
isolationPolicy: parseIsolationPolicy(obj.isolationPolicy),
|
|
367
388
|
surface: parseSurfacePolicy(obj.surface),
|
|
368
389
|
modelFallback: parseModelFallbackConfig(obj.modelFallback),
|
|
390
|
+
// F19-1 discipline: every key declared in types.ts + schema MUST be
|
|
391
|
+
// emitted here or the config key is dead (runtime.sessionRecovery —
|
|
392
|
+
// W2/P1-1; runtime.workerTransport — W7/P2-3, experimental;
|
|
393
|
+
// runtime.hermeticWorkers — R3-19/D5 reversal, hermetic spawns;
|
|
394
|
+
// runtime.classifierEnabled/classifierModel — P2-1 dormant classifier
|
|
395
|
+
// seam, consumed at child-executor's retry triage).
|
|
396
|
+
// Necessary but NOT sufficient: emission still needs a CONSUMER at the
|
|
397
|
+
// seam — workerTransport was parse+emit-only dead until D1/DR2 threaded
|
|
398
|
+
// it to resolveWorkerTransport (run-worker seam).
|
|
399
|
+
sessionRecovery: parseWithSchema(Type.Boolean(), obj.sessionRecovery),
|
|
400
|
+
hermeticWorkers: parseWithSchema(Type.Boolean(), obj.hermeticWorkers),
|
|
401
|
+
workerTransport: parseWithSchema(Type.Union([Type.Literal("stdio"), Type.Literal("rpc")]), obj.workerTransport),
|
|
402
|
+
classifierEnabled: parseWithSchema(Type.Boolean(), obj.classifierEnabled),
|
|
403
|
+
classifierModel: parseWithSchema(Type.String({ minLength: 1 }), obj.classifierModel),
|
|
369
404
|
};
|
|
370
405
|
return Object.values(runtime).some((entry) => entry !== undefined) ? runtime : undefined;
|
|
371
406
|
}
|
package/src/config/env-vars.ts
CHANGED
|
@@ -317,6 +317,11 @@ export const CREW_ENV_VARS: Record<string, CrewEnvVarSpec> = {
|
|
|
317
317
|
name: "PI_CREW_DISABLE_RESULT_READ_CACHE",
|
|
318
318
|
doc: "'1' bypasses the per-run result-artifact read cache (R10-1 bypass control; task-output-context.ts createResultArtifactReadCache)",
|
|
319
319
|
},
|
|
320
|
+
PI_CREW_HANDOFF_BUDGET_TOKENS: {
|
|
321
|
+
name: "PI_CREW_HANDOFF_BUDGET_TOKENS",
|
|
322
|
+
parser: "int",
|
|
323
|
+
doc: "est-token (chars/4) budget for the dynamic.dependencyContext prompt layer; beats runtime.handoffBudgetTokens, default 1800; resolved ≤0 or >1_000_000 disables the trim (task-output-context.ts renderDependencyOutputContext)",
|
|
324
|
+
},
|
|
320
325
|
PI_CREW_MOCK_LIVE_SESSION: {
|
|
321
326
|
name: "PI_CREW_MOCK_LIVE_SESSION",
|
|
322
327
|
doc: "'success' mocks a successful live-session (live-session-runtime.ts:558, runtime-resolver.ts:38/116)",
|
|
@@ -440,6 +445,39 @@ export const CREW_ENV_VARS: Record<string, CrewEnvVarSpec> = {
|
|
|
440
445
|
name: "PI_CREW_AGENT_EVENTS_PATH",
|
|
441
446
|
doc: "per-agent events log (<stateRoot>/agents/<taskId>/events.jsonl) written by the surface worker recorder (surface-worker.ts; derived by prepareSurfaceSpawn)",
|
|
442
447
|
},
|
|
448
|
+
PI_CREW_SESSION_RECOVERY: {
|
|
449
|
+
name: "PI_CREW_SESSION_RECOVERY",
|
|
450
|
+
parser: "boolean",
|
|
451
|
+
doc: "W2 session-file recovery override for killed workers (exitCode null / killed): '0' disables, '1' forces on — beats ChildPiRunInput.sessionRecovery / runtime.sessionRecovery; default ON (session-recovery.ts:resolveSessionRecoveryEnabled)",
|
|
452
|
+
},
|
|
453
|
+
PI_CREW_HERMETIC_WORKERS: {
|
|
454
|
+
name: "PI_CREW_HERMETIC_WORKERS",
|
|
455
|
+
parser: "boolean",
|
|
456
|
+
doc: "R3-19/D5 hermetic worker spawn override: '1' forces --no-extensions on worker argv, '0' restores the old ambient-stack parity (discovered/configured/built-in extensions load); beats BuildPiWorkerArgsInput.hermeticWorkers / runtime.hermeticWorkers; default ON (pi-args.ts:resolveHermeticWorkers). Explicit -e prompt-runtime + agent extensions + --skill flags are unaffected (cli.md:186-191)",
|
|
457
|
+
},
|
|
458
|
+
PI_CREW_WORKER_TRANSPORT: {
|
|
459
|
+
name: "PI_CREW_WORKER_TRANSPORT",
|
|
460
|
+
doc: "W7 EXPERIMENTAL worker transport override: 'rpc' selects the prototype RPC frame-client (src/runtime/rpc/) at the run-worker seam (wired e2728f68; D1/DR2 also plumbs config runtime.workerTransport to the same seam — env WINS over config when set, invalid env fails safe to stdio); anything else/unset → config value, default stdio child-pi path (rpc-worker.ts resolveWorkerTransport)",
|
|
461
|
+
},
|
|
462
|
+
PI_CREW_CLASSIFIER_ENABLED: {
|
|
463
|
+
name: "PI_CREW_CLASSIFIER_ENABLED",
|
|
464
|
+
parser: "boolean",
|
|
465
|
+
doc: "P2-1 (pi 1.0.4 adoption) host-process retry-triage classifier override: '1' enables transient-vs-permanent classification before a queued re-attempt in the child-executor retry loop, '0' forces it off — beats runtime.classifierEnabled; default OFF / dormant (classifier-service.ts:resolveClassifierEnabled). Fallback decision is used on every classifier failure mode, never an error",
|
|
466
|
+
},
|
|
467
|
+
PI_CREW_CLASSIFIER_MODEL: {
|
|
468
|
+
name: "PI_CREW_CLASSIFIER_MODEL",
|
|
469
|
+
doc: "P2-1 classifier model id override ('provider/id', catalog ids only — availability depends on provider credentials at runtime): beats runtime.classifierModel; unset/blank → config value, default 'opencode/jev-1.13-free' (classifier-service.ts:resolveClassifierModel)",
|
|
470
|
+
},
|
|
471
|
+
PI_CREW_RPC_DIALOG_ANSWER: {
|
|
472
|
+
name: "PI_CREW_RPC_DIALOG_ANSWER",
|
|
473
|
+
doc: "W7 EXPERIMENTAL dialog auto-answer policy for RPC-mode extension_ui_request dialogs (select/confirm/input/editor): 'cancel' (default) answers {cancelled:true} — universal safe answer; 'block' leaves the server promise pending (debug). Auto-confirm deliberately NOT implemented (security); invalid → 'cancel' (rpc-worker.ts resolveDialogAnswerPolicy)",
|
|
474
|
+
},
|
|
475
|
+
PI_CREW_RPC_TURN_TIMEOUT_MS: {
|
|
476
|
+
name: "PI_CREW_RPC_TURN_TIMEOUT_MS",
|
|
477
|
+
parser: "int",
|
|
478
|
+
default: "600000",
|
|
479
|
+
doc: "W7/DR1 belt-and-suspenders turn timeout for the EXPERIMENTAL RPC worker transport: max ms a prompt-accepted turn may run without agent_settled before the transport fails with the early-failure shape (DR5 guard still blocks the stdio fallback once a session event was seen). Env-only while RPC stays experimental. Invalid/≤0 → 600000 (rpc-worker.ts resolveRpcTurnTimeoutMs)",
|
|
480
|
+
},
|
|
443
481
|
PI_CREW_AUTO_EXIT: {
|
|
444
482
|
name: "PI_CREW_AUTO_EXIT",
|
|
445
483
|
doc: "'1' → the worker shuts its session down after the final settled turn — spec §5.2 D7 (written by prepareSurfaceSpawn, read by surface-worker.ts)",
|
package/src/config/types.ts
CHANGED
|
@@ -65,6 +65,53 @@ export interface CrewRuntimeConfig {
|
|
|
65
65
|
*/
|
|
66
66
|
taskTimeoutMs?: number;
|
|
67
67
|
inheritContext?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* W2 (session-file recovery): when a worker dies without a final assistant
|
|
70
|
+
* event (exitCode null / killed), tail-replay its session JSONL (written by
|
|
71
|
+
* the worker under `<artifactsRoot>/sessions/<taskId>/`) and attach the last
|
|
72
|
+
* COMPLETE assistant record to the task result (`recoveredFromSession`).
|
|
73
|
+
* Default: enabled. The env var PI_CREW_SESSION_RECOVERY overrides this
|
|
74
|
+
* flag in either direction (see session-recovery.ts:resolveSessionRecoveryEnabled).
|
|
75
|
+
* Manifest polling is NOT replaced — this only augments the settle-time result.
|
|
76
|
+
*/
|
|
77
|
+
sessionRecovery?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* R3-19/D5 reversal (2026-10-04): hermetic worker spawns — pass
|
|
80
|
+
* `--no-extensions` on worker argv. Explicit `-e prompt-runtime` and agent
|
|
81
|
+
* `extensions:` still load (cli.md:186-187: "Disables discovered, configured,
|
|
82
|
+
* and built-in extensions. Explicit -e paths still load"), and `--skill`
|
|
83
|
+
* flags are independent — what gets cut is the ~1.37s/spawn ambient package
|
|
84
|
+
* stack (pi-crew host ext + configured packages; probe C §R3b.6: 431ms vs
|
|
85
|
+
* 1806ms to first stdout record) plus the ambient tool surface (team/
|
|
86
|
+
* crew_agent/Agent host tools + 8 direct-exposure MCP tools).
|
|
87
|
+
* Default: true. Env PI_CREW_HERMETIC_WORKERS overrides in either
|
|
88
|
+
* direction (pi-args.ts resolveHermeticWorkers). Surface TUI spawns strip
|
|
89
|
+
* the flag — panes keep the full interactive session.
|
|
90
|
+
*/
|
|
91
|
+
hermeticWorkers?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* P2-1 (pi 1.0.4 adoption, 2026-10-07): host-process retry-triage
|
|
94
|
+
* classifier seam (src/runtime/classifier/classifier-service.ts —
|
|
95
|
+
* wrapper around ctx.modelRegistry.classify(), host process only).
|
|
96
|
+
* DORMANT by design: default false, and the consumer (child-executor
|
|
97
|
+
* retry loop) falls back to the pre-existing retry behavior on every
|
|
98
|
+
* classifier failure mode (unavailable / threw / stopReason!=='stop').
|
|
99
|
+
* Enabling opts INTO transient-vs-permanent classification before a
|
|
100
|
+
* queued re-attempt burns a full worker spawn. Env
|
|
101
|
+
* PI_CREW_CLASSIFIER_ENABLED overrides in either direction
|
|
102
|
+
* (resolveClassifierEnabled). Behavior pick, not authority — no
|
|
103
|
+
* sensitive mark.
|
|
104
|
+
*/
|
|
105
|
+
classifierEnabled?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* P2-1: classifier model id ("provider/id" form) for the seam above.
|
|
108
|
+
* Availability is credential-dependent at runtime (getAvailableOfType);
|
|
109
|
+
* a configured-but-uncredentialed id degrades to the fallback decision,
|
|
110
|
+
* never to an error. Default "opencode/jev-1.13-free"
|
|
111
|
+
* (DEFAULT_CLASSIFIER_MODEL); env PI_CREW_CLASSIFIER_MODEL overrides
|
|
112
|
+
* (resolveClassifierModel).
|
|
113
|
+
*/
|
|
114
|
+
classifierModel?: string;
|
|
68
115
|
promptMode?: "replace" | "append";
|
|
69
116
|
groupJoin?: "off" | "group" | "smart";
|
|
70
117
|
groupJoinAckTimeoutMs?: number;
|
|
@@ -85,6 +132,30 @@ export interface CrewRuntimeConfig {
|
|
|
85
132
|
};
|
|
86
133
|
/** Mark certain bash commands as excludeFromContext to reduce context tokens. Default: false */
|
|
87
134
|
excludeContextBash?: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* Est-token budget for the dynamic.dependencyContext prompt layer
|
|
137
|
+
* (handoff budget). When the rendered dependency output exceeds this many
|
|
138
|
+
* estimated tokens (chars/4 — the same heuristic the prompt breakdown
|
|
139
|
+
* uses), dependencies are trimmed IN DECLARATION ORDER: earlier deps keep
|
|
140
|
+
* their full output, later ones are downgraded to taskId/role/status + a
|
|
141
|
+
* ≤240-char summary head + an artifact pointer
|
|
142
|
+
* (`artifacts/<runId>/results/<taskId>.txt`) the worker can `read` back.
|
|
143
|
+
* Long sharedRead bodies are dropped the same way. Valid range 1..1_000_000;
|
|
144
|
+
* a RESOLVED value ≤0 or >1_000_000 disables the trim entirely
|
|
145
|
+
* (off-switch). Env override PI_CREW_HANDOFF_BUDGET_TOKENS beats this
|
|
146
|
+
* config. Default: 1800.
|
|
147
|
+
*/
|
|
148
|
+
handoffBudgetTokens?: number;
|
|
149
|
+
/**
|
|
150
|
+
* W7 (P2-3) EXPERIMENTAL: transport used to spawn child-pi workers.
|
|
151
|
+
* "stdio" (default) — the production print-mode path, unchanged.
|
|
152
|
+
* "rpc" — prototype RPC frame-client (src/runtime/rpc/), wired at the
|
|
153
|
+
* run-worker seam since e2728f68 (D1/DR2 plumbing, 2026-10-06): callers
|
|
154
|
+
* carrying runtimeConfig thread it to the seam. Env
|
|
155
|
+
* PI_CREW_WORKER_TRANSPORT=rpc overrides this config (env wins —
|
|
156
|
+
* established precedence; invalid env fails safe to stdio).
|
|
157
|
+
*/
|
|
158
|
+
workerTransport?: "stdio" | "rpc";
|
|
88
159
|
/**
|
|
89
160
|
* Mux-surface policy (mux-surface spec v0.7 §8.1): WHERE worker processes
|
|
90
161
|
* live — a pane in tmux/herdr or headless child processes. Surface only
|
|
@@ -92,6 +92,15 @@ function startForegroundRunImpl(
|
|
|
92
92
|
runId?: string,
|
|
93
93
|
): void {
|
|
94
94
|
const ownerGeneration = ctx.captureSessionGeneration();
|
|
95
|
+
// Issue #62: tool-dispatched runs receive a SPREAD COPY of the session
|
|
96
|
+
// context (withSessionId() always returns { ...ctx }), so the object-
|
|
97
|
+
// identity check (isContextCurrent) was false from the very first moment
|
|
98
|
+
// and the whole completion block below was silently skipped for every
|
|
99
|
+
// foreground run started via the team/subagent tools — stale working
|
|
100
|
+
// message, no crew:run-completed entry, no crew.run.* metrics. Liveness
|
|
101
|
+
// for the reporting half is therefore session-ID based; the UI clear is
|
|
102
|
+
// unconditional (try/catch guards a disposed ctx).
|
|
103
|
+
const ownerSessionId = extensionCtx.sessionManager?.getSessionId?.();
|
|
95
104
|
const controller = new AbortController();
|
|
96
105
|
const key = runId ?? Symbol();
|
|
97
106
|
ctx.foregroundTeamRunControllers.set(key, controller);
|
|
@@ -130,7 +139,7 @@ function startForegroundRunImpl(
|
|
|
130
139
|
logInternalError("register.foreground-run-failure", statusError, `runId=${runId}`);
|
|
131
140
|
}
|
|
132
141
|
}
|
|
133
|
-
if (ctx.
|
|
142
|
+
if (ctx.isOwnerSessionCurrent(ownerGeneration, ownerSessionId)) {
|
|
134
143
|
extensionCtx.ui.notify(`pi-crew foreground run failed: ${message}`, "error");
|
|
135
144
|
} else {
|
|
136
145
|
logInternalError("register.foreground-run-failure", error, `runId=${runId} context disposed`);
|
|
@@ -145,18 +154,18 @@ function startForegroundRunImpl(
|
|
|
145
154
|
})
|
|
146
155
|
.catch((error) => logInternalError("register.foreground-watchdog", error, `runId=${runId}`));
|
|
147
156
|
}
|
|
148
|
-
const ownerCurrent = ctx.
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
|
|
157
|
+
const ownerCurrent = ctx.isOwnerSessionCurrent(ownerGeneration, ownerSessionId);
|
|
158
|
+
// Issue #62: clear the working message UNCONDITIONALLY. The `hasUI`
|
|
159
|
+
// getter THROWS on a disposed ctx, so touch it inside try/catch —
|
|
160
|
+
// a stale ctx just means the host already reset its UI; a live one
|
|
161
|
+
// must never keep spinning a finished run's label.
|
|
162
|
+
try {
|
|
163
|
+
if (extensionCtx.hasUI) {
|
|
155
164
|
setWorkingIndicator(extensionCtx);
|
|
156
165
|
extensionCtx.ui.setWorkingMessage();
|
|
157
|
-
} catch {
|
|
158
|
-
/* ignore */
|
|
159
166
|
}
|
|
167
|
+
} catch {
|
|
168
|
+
/* disposed ctx — host-side UI reset already happened */
|
|
160
169
|
}
|
|
161
170
|
if (ownerCurrent && runId) {
|
|
162
171
|
const loaded = loadRunManifestById(extensionCtx.cwd, runId);
|