pi-crew 0.6.0 → 0.6.3
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 +225 -0
- package/README.md +70 -31
- package/docs/issue-29-analysis.md +189 -0
- package/docs/superpowers/plans/2026-06-09-fallow-patterns-adoption.md +1268 -0
- package/package.json +2 -2
- package/src/agents/agent-config.ts +2 -1
- package/src/benchmark/feedback-loop.ts +4 -2
- package/src/config/config.ts +106 -15
- package/src/errors.ts +107 -0
- package/src/extension/async-notifier.ts +6 -2
- package/src/extension/crew-cleanup.ts +8 -5
- package/src/extension/cross-extension-rpc.ts +48 -0
- package/src/extension/management.ts +464 -109
- package/src/extension/register.ts +194 -34
- package/src/extension/registration/commands.ts +4 -3
- package/src/extension/registration/subagent-helpers.ts +2 -2
- package/src/extension/registration/subagent-tools.ts +3 -1
- package/src/extension/registration/team-tool.ts +3 -1
- package/src/extension/registration/viewers.ts +3 -2
- package/src/extension/run-export.ts +16 -1
- package/src/extension/run-import.ts +16 -0
- package/src/extension/team-tool/anchor.ts +5 -1
- package/src/extension/team-tool/api.ts +12 -7
- package/src/extension/team-tool/cancel.ts +3 -3
- package/src/extension/team-tool/config-patch.ts +15 -1
- package/src/extension/team-tool/explain.ts +1 -1
- package/src/extension/team-tool/handle-schedule.ts +40 -0
- package/src/extension/team-tool/inspect.ts +3 -3
- package/src/extension/team-tool/lifecycle-actions.ts +4 -4
- package/src/extension/team-tool/respond.ts +2 -2
- package/src/extension/team-tool/run.ts +60 -11
- package/src/extension/team-tool/status.ts +1 -1
- package/src/extension/team-tool.ts +175 -47
- package/src/hooks/registry.ts +84 -12
- package/src/hooks/types.ts +12 -3
- package/src/i18n.ts +15 -2
- package/src/observability/exporters/otlp-exporter.ts +73 -0
- package/src/plugins/plugin-define.ts +6 -0
- package/src/plugins/plugin-registry.ts +32 -0
- package/src/plugins/plugins/index.ts +3 -0
- package/src/plugins/plugins/nextjs.ts +19 -0
- package/src/plugins/plugins/vite.ts +14 -0
- package/src/plugins/plugins/vitest.ts +14 -0
- package/src/runtime/adaptive-plan.ts +24 -0
- package/src/runtime/agent-control.ts +6 -3
- package/src/runtime/async-runner.ts +86 -3
- package/src/runtime/background-runner.ts +529 -144
- package/src/runtime/chain-parser.ts +5 -1
- package/src/runtime/chain-runner.ts +67 -3
- package/src/runtime/checkpoint.ts +262 -180
- package/src/runtime/child-pi.ts +165 -38
- package/src/runtime/crash-recovery.ts +25 -14
- package/src/runtime/crew-agent-records.ts +6 -3
- package/src/runtime/cross-extension-rpc.ts +34 -8
- package/src/runtime/diagnostic-export.ts +4 -5
- package/src/runtime/dynamic-script-runner.ts +9 -6
- package/src/runtime/foreground-watchdog.ts +3 -3
- package/src/runtime/heartbeat-watcher.ts +19 -2
- package/src/runtime/intercom-bridge.ts +7 -0
- package/src/runtime/iteration-hooks.ts +1 -1
- package/src/runtime/live-agent-manager.ts +6 -3
- package/src/runtime/live-irc.ts +4 -2
- package/src/runtime/manifest-cache.ts +4 -0
- package/src/runtime/orphan-worker-registry.ts +444 -0
- package/src/runtime/parallel-utils.ts +2 -1
- package/src/runtime/parent-guard.ts +70 -16
- package/src/runtime/pi-args.ts +437 -14
- package/src/runtime/pi-spawn.ts +1 -0
- package/src/runtime/post-checks.ts +11 -4
- package/src/runtime/retry-runner.ts +9 -3
- package/src/runtime/{drift-detectors.ts → run-drift.ts} +1 -1
- package/src/runtime/run-tracker.ts +38 -10
- package/src/runtime/sandbox.ts +42 -21
- package/src/runtime/scheduler.ts +25 -2
- package/src/runtime/semaphore.ts +2 -1
- package/src/runtime/settings-store.ts +14 -2
- package/src/runtime/skill-effectiveness.ts +109 -64
- package/src/runtime/skill-instructions.ts +114 -33
- package/src/runtime/stale-reconciler.ts +310 -69
- package/src/runtime/subagent-manager.ts +265 -53
- package/src/runtime/subprocess-tool-registry.ts +2 -2
- package/src/runtime/task-health.ts +76 -0
- package/src/runtime/task-id.ts +20 -0
- package/src/runtime/task-packet.ts +13 -1
- package/src/runtime/task-runner/live-executor.ts +1 -1
- package/src/runtime/task-runner/progress.ts +1 -1
- package/src/runtime/task-runner/state-helpers.ts +110 -6
- package/src/runtime/task-runner.ts +98 -67
- package/src/runtime/team-runner.ts +186 -20
- package/src/runtime/usage-tracker.ts +4 -2
- package/src/runtime/verification-gates.ts +36 -9
- package/src/schema/team-tool-schema.ts +27 -9
- package/src/skills/discover-skills.ts +9 -3
- package/src/state/active-run-registry.ts +170 -38
- package/src/state/artifact-store.ts +25 -13
- package/src/state/atomic-write-v2.ts +86 -0
- package/src/state/atomic-write.ts +346 -55
- package/src/state/blob-store.ts +178 -10
- package/src/state/contracts.ts +2 -1
- package/src/state/crew-init.ts +161 -28
- package/src/state/decision-ledger.ts +172 -111
- package/src/state/event-log-rotation.ts +82 -52
- package/src/state/event-log.ts +270 -75
- package/src/state/health-store.ts +71 -0
- package/src/state/hook-instinct-bridge.ts +2 -1
- package/src/state/locks.ts +109 -20
- package/src/state/mailbox.ts +45 -7
- package/src/state/observation-store.ts +4 -1
- package/src/state/run-graph.ts +141 -130
- package/src/state/run-metrics.ts +24 -8
- package/src/state/state-store.ts +333 -44
- package/src/state/task-claims.ts +9 -2
- package/src/tools/safe-bash.ts +69 -20
- package/src/types/new-api-types.ts +10 -5
- package/src/ui/keybinding-map.ts +2 -1
- package/src/ui/live-run-sidebar.ts +1 -1
- package/src/ui/loaders.ts +4 -0
- package/src/ui/overlays/agent-picker-overlay.ts +1 -1
- package/src/ui/overlays/mailbox-detail-overlay.ts +1 -1
- package/src/ui/run-action-dispatcher.ts +4 -3
- package/src/ui/run-snapshot-cache.ts +1 -1
- package/src/ui/status-colors.ts +2 -1
- package/src/ui/syntax-highlight.ts +2 -1
- package/src/ui/tool-render.ts +13 -3
- package/src/utils/env-filter.ts +66 -0
- package/src/utils/file-coalescer.ts +9 -1
- package/src/utils/fs-watch.ts +4 -2
- package/src/utils/gh-protocol.ts +2 -1
- package/src/utils/paths.ts +80 -5
- package/src/utils/redaction.ts +6 -1
- package/src/utils/safe-paths.ts +287 -10
- package/src/worktree/cleanup.ts +117 -26
- package/src/worktree/worktree-manager.ts +128 -15
- package/test-bugs-all.mjs +10 -6
- package/test-integration-check.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,230 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.3] — Cross-Platform CI, 87 Test Fixes, Worktree Validation, Heartbeat & Crash Fixes (2026-06-12)
|
|
4
|
+
|
|
5
|
+
### Highlights
|
|
6
|
+
- **Cross-platform CI green** — 0 failures across Ubuntu, macOS, and Windows (4,725 unit + 113 integration tests)
|
|
7
|
+
- **87 pre-existing test failures resolved** — 0 failures across 4,792 tests in 506 test files
|
|
8
|
+
- **Heartbeat false-positive dead detection fixed** — `message_start` added to progress flush events; PID liveness gate uses `task.checkpoint.childPid` fallback
|
|
9
|
+
- **ENOENT crash on prune/forget race fixed** — 4-layer defense in `persistSingleTaskUpdate`, `persistHeartbeat`, `saveRunTasks`, and `upsertCrewAgent`
|
|
10
|
+
- **Scheduled job lifecycle completed** — spawned runs tracked via `spawnedRunIds[]`, auto-cancelled on job removal, manifests stamped with `schedulerJobId` for traceability
|
|
11
|
+
- **Worktree precondition validation** — friendly error messages instead of crashes when cwd is not a git repo or repo has uncommitted changes
|
|
12
|
+
- **Cross-platform path handling** — `canonicalizePath` with `realpathSync.native` for Windows short-name/long-name aliasing; macOS `/var` → `/private/var` symlink resolution
|
|
13
|
+
- **Pipe buffer deadlock fix** — `spawnSync` with `stdio: 'pipe'` caused deadlock when OS pipe buffer (~64KB) filled at ~227 tests; switched to `stdio: 'inherit'`
|
|
14
|
+
- **Stale lock recovery** — removed `readLockToken` guard so stale locks without 'token' field are properly deleted
|
|
15
|
+
- **Full-feature smoke test** — 58 integration tests covering all pi-crew actions
|
|
16
|
+
- **Pre-push review**: 56 unpushed commits reviewed (116 files, +9,599/−980 lines), 1 release blocker found and fixed
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
- **`89ed975`** — Heartbeat watcher: added `message_start` to `shouldFlushProgressEvent()` so LLM stream start updates `lastActivityAt`. Previously, an 8m53s LLM response (365 file reads, no tool calls) triggered false `heartbeat_dead` at 300s threshold.
|
|
20
|
+
- **`9c1bf1f`** — Heartbeat watcher: PID liveness gate uses `task.heartbeat?.pid ?? task.checkpoint?.childPid` fallback. Review team discovered the gate was dead code because `createWorkerHeartbeat(taskId)` never receives PID.
|
|
21
|
+
- **`2bbbb99`** — ENOENT crash: `persistSingleTaskUpdate` recheck stat wrapped in try/catch; `persistHeartbeat` catches ENOENT; `saveRunTasks` guards with `statSync(stateRoot)`; `upsertCrewAgent` skips if stateRoot gone.
|
|
22
|
+
- **`08df7ce`** — Release blocker: `src/errors.ts` enum→const object, `src/state/health-store.ts` parameter property — both incompatible with Node 22 `--experimental-strip-types`.
|
|
23
|
+
- **`3e0b957`** — Sandbox constructor escape detection strengthened.
|
|
24
|
+
- **`dd279bc`** — EBADF (missing O_WRONLY flag), re-entrant sync locks, worktree list parsing, env-filter provider keys.
|
|
25
|
+
- **`38b8f5a`** — Create `transcripts/` directory before child-pi appends.
|
|
26
|
+
- **`d893434`** — Child-pi: remove API key allowlist; child Pi uses same config as parent.
|
|
27
|
+
- **`5cb9122`** — Cross-platform: `canonicalizePath` in `paths.ts` uses `realpathSync.native` for consistent long-name paths on Windows; all test temp dirs canonicalized through `.native`.
|
|
28
|
+
- **`3b46556`** — Cross-platform: `resolvedWorktreeRoot` uses `.native` for git worktree compatibility on Windows; worktree list comparison normalizes through `.native`.
|
|
29
|
+
- **`b2b7068`** — Worktree: precondition validation in `team-tool/run.ts` checks git repo existence and clean leader status before creating run manifest, returning friendly error messages instead of crashing.
|
|
30
|
+
- **`8090fe2`** — Worktree: respect `requireCleanWorktreeLeader` config setting in precondition check.
|
|
31
|
+
- **`2014739`** — Pipe buffer deadlock: `spawnSync` with `stdio: 'pipe'` caused deadlock when OS pipe buffer (~64KB) filled at ~227 tests; switched to `stdio: 'inherit'`.
|
|
32
|
+
- **`d6920bf`** — Stale lock recovery: removed `readLockToken` guard so stale locks without 'token' field are properly deleted.
|
|
33
|
+
- **`3897c1d`** — Mailbox: return paths as-is from `safeMailboxDir`, `safeMailboxFile`, `safeMailboxTasksRoot`, `taskMailboxDir` instead of re-resolving through `resolveRealContainedPath` which changed path forms on Windows.
|
|
34
|
+
- **`f867d4d`** — macOS: `realpathSync(os.tmpdir())` in 3 test files to handle `/var` → `/private/var` symlink.
|
|
35
|
+
- **`2fd0c1e`** — TypeScript: fix Property 'text' and Property 'taskId'→'id' errors for strict compiler checks.
|
|
36
|
+
|
|
37
|
+
### Test Fixes (87 total)
|
|
38
|
+
- **`1ab7926`** — 33 failures: state-store mtime CAS, locks race, discovery, atomic-write, config-schema, blob-store, env-filter, sandbox, security-hardening, worktree
|
|
39
|
+
- **`bba0bed`** — 3 failures: blob dedup, auto-recovery cap, transcript append
|
|
40
|
+
- **`03dd9b3`** — 14 failures: team-runner, retry-runner, hooks, stale-reconciler, resume-checkpoint, dynamic-script-runner, adaptive-implementation
|
|
41
|
+
- **`a91c316`** — 5 failures: re-entrant sync locks (`withRunLockSync`), `registerWorker()` optional `registeredAt`, `phase8-smoke` PI_TEAMS_HOME isolation, `test-integration-check` PI_CREW_ALLOW_MOCK, `test-bugs-all.mjs` graceful skip
|
|
42
|
+
- **`952c14d`** — 58 full-feature smoke tests added
|
|
43
|
+
|
|
44
|
+
### Features
|
|
45
|
+
- **`14269f0`** — Scheduler tracks spawned runs: `ScheduledJob.spawnedRunIds[]`, `CrewScheduler.recordSpawnedRun()`, `remove()` calls `runCancelFn` per spawned run, manifests stamped with `schedulerJobId`/`schedulerName`.
|
|
46
|
+
- **`e499570`** — Plugin registry system for framework context injection (Next.js, Vite, Vitest).
|
|
47
|
+
- **`84170c3`** — Team runner integrates plugin registry for framework-aware task context.
|
|
48
|
+
- **`ee466a8`** — Health score system with penalty-based scoring and time-series snapshots.
|
|
49
|
+
- **`daa53ab`** — Atomic write v2 with fsync + rename pattern for crash-safe state persistence.
|
|
50
|
+
- **`6c01f2c`** — CrewError taxonomy: E001–E006 structured error codes.
|
|
51
|
+
- **`2ce143f`** — State-store uses CrewError for structured errors.
|
|
52
|
+
- **`0cd4853`** — Stable task IDs via `stableIdFromContent` for cross-run consistency.
|
|
53
|
+
- **`ff3da92`** — Health snapshot saved on run completion.
|
|
54
|
+
|
|
55
|
+
### Stats
|
|
56
|
+
- 137 commits since v0.6.1
|
|
57
|
+
- 200 files changed (+16,955 / −2,057 lines)
|
|
58
|
+
- 366 source files, ~70K lines TypeScript
|
|
59
|
+
- 506 test files, ~66K lines TypeScript
|
|
60
|
+
- 4,792 tests, 0 failures
|
|
61
|
+
- CI: Ubuntu ✅ macOS ✅ Windows ✅ (0 failures each)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## [0.6.3] — Post-Release Hardening: Cleanup, Safe-Paths, State-Store Race (2026-06-08)
|
|
65
|
+
|
|
66
|
+
### Highlights
|
|
67
|
+
- **State-store manifest/tasks mtime race fixed** (commits `04fe0be`, `f15ee98`) — `loadRunManifestById` no longer throws on benign mtime skew between `manifest.json` and `tasks.json`. A previous user review (run `team_20260608082852_*`) hit a 4812-second hang because of this throw; the fix prevents the same hang from recurring.
|
|
68
|
+
- **Orphan worker + temp dir cleanup hardened** (8 commits) — 4-layer defense (in-memory Set, per-session temp dir, user-root temp dir, legacy `/tmp` cleanup) with symlink guards, `O_NOFOLLOW` opens, and bounded batch sizes.
|
|
69
|
+
- **`PI_CREW_PARENT_PID` restored to child env allow-list** (commit `e1f7dfe`) — silent regression from a previous round fixed; parent-guard now works again for orphan-worker detection.
|
|
70
|
+
- **`safe-paths.resolveRealContainedPath` extended** (commits `ba0ce54`, `aa457a5`) — now supports creating new files (target does not have to exist) while keeping full symlink-ancestor protection.
|
|
71
|
+
- **`blob-store` metadata race fixed** (commit `5819b18`) — per-hash in-memory lock + atomic write of content-then-metadata prevents concurrent writers from corrupting metadata.
|
|
72
|
+
- **Behavior change: `parent-guard` no longer `.unref()`s its interval** — the guard timer now keeps the event loop alive by design so workers do not exit while their parent is still alive but the worker has no other pending work. See "Behavior Changes" below.
|
|
73
|
+
|
|
74
|
+
### Security Fixes
|
|
75
|
+
- **`e1f7dfe`** — Restored `PI_CREW_PARENT_PID` in `child-pi.ts` env allow-list. Previous round (`dbf7a48`) replaced `PI_CREW_*`/`PI_TEAMS_*` wildcards with an explicit list but omitted `PI_CREW_PARENT_PID`, silently breaking the parent-guard mechanism.
|
|
76
|
+
- **`ba0ce54` / `aa457a5`** — `safe-paths.resolveRealContainedPath` now allows new-file creation while keeping symlink-ancestor protection. Documented the asymmetric ancestor policy in the function JSDoc.
|
|
77
|
+
- **`e1f7dfe`** (sibling) — `worktree/cleanup.ts` no longer uses `PI_*` / `PI_CREW_*` wildcards in `GIT_SAFE_ENV` (could match secret vars like `PI_PASSWORD`).
|
|
78
|
+
- **`2b8f27a` / `1bf67eb`** — Child env allow-list switched from dangerous wildcards (`*_API_KEY`, `*_TOKEN`, `*_SECRET`, `LC_*`, `XDG_*`, `NPM_*`) to an explicit list of 6 API keys + 12 essential env vars. Eliminates accidental secret leakage via matching name patterns.
|
|
79
|
+
|
|
80
|
+
### Cleanup Hardening (8 commits)
|
|
81
|
+
- **`5edcb18`** — Track temp dirs globally via in-memory `Set<string>`, cap `reconcileOrphanedTempWorkspaces` scan size, fix cleanup stub.
|
|
82
|
+
- **`8ba270d`** — Move temp dirs from `/tmp` to `~/.pi/agent/pi-crew/tmp/` (uses `userPiRoot()` so `PI_TEAMS_HOME` / `PI_CODING_AGENT_DIR` are respected). Eliminates `/tmp` pollution and unifies state layout.
|
|
83
|
+
- **`ceb1cb1`** — Layer 4 periodic cleanup for orphan prompt/task temp dirs older than 24h.
|
|
84
|
+
- **`a76932d`** — Skip symlinks and in-use dirs during cleanup, plus a one-shot legacy `/tmp` sweep to clean up directories left behind by pre-`8ba270d` installations.
|
|
85
|
+
- **`c9eb430`** — Kill orphan background workers and trigger temp cleanup on `session_start`.
|
|
86
|
+
- **`a192509`** — 4 critical hardening fixes: never `rmSync` a symlink, double-check immediately before delete, skip dirs currently in use, and tear down the global tracker on process exit.
|
|
87
|
+
- **`992231d`** — 24 new unit tests in `test/unit/cleanup-orphan-temp.test.ts` covering each cleanup layer and failure mode.
|
|
88
|
+
- **`dbf7a48`** — UI: replace `console.log` cleanup messages with `notifyOperator` for proper user notification.
|
|
89
|
+
|
|
90
|
+
### Bug Fixes
|
|
91
|
+
- **`5819b18`** — `blob-store.writeBlob` race condition: per-hash lock + atomic write of content-then-metadata (previously metadata first, leaving orphans on blob write failure).
|
|
92
|
+
- **`04fe0be`** — `state-store.loadRunManifestById` returned `undefined` (was: threw) on manifest/tasks mtime mismatch — the throw caused background runners to crash within 1s of startup.
|
|
93
|
+
- **`f15ee98`** — `state-store.loadRunManifestById` removed the false-positive mtime check entirely. The `saveManifestAndTasksAtomicSync` writer intentionally writes manifest before tasks, so a manifest with newer mtime than tasks is a NORMAL post-write state, not corruption.
|
|
94
|
+
- **`cd7ef89` / `a0c2ba3` / `098c8a9` / `b782424` / `e1ea7d4` / `de3f550` / `2b8f27a` / `1bf67eb`** — 8 deep-review auto-fix commits addressing 78+77+29+28+24+24+24+24 verified issues across the cleanup, state, runtime, and utils modules.
|
|
95
|
+
- **`e1f7dfe`** — `parent-guard.ts` unref'd timers were silently causing worker exit while parent was still alive; restoring the allow-list entry brought the guard back into effect.
|
|
96
|
+
|
|
97
|
+
### Behavior Changes
|
|
98
|
+
- **`parent-guard.ts` no longer `.unref()`s the guard interval** (revert/restore series `0aed8b5` / `152ac80` / `ee0ddb4` / `81b9608`). The watchdog timer now keeps the event loop alive by design. If a worker has no other pending work (no I/O, no timers, no child processes), the guard interval is the only thing keeping the worker alive until either the parent dies or the worker is explicitly stopped. The previous revert-then-restore pattern ("to test if they cause pi hang") never conclusively identified a root cause; the current state was reached after manual testing. **Mitigation recommended**: add a max-worker-lifetime safety net in a future release.
|
|
99
|
+
|
|
100
|
+
### New / Heavily Expanded Source Files
|
|
101
|
+
- **`src/runtime/orphan-worker-registry.ts`** (NEW, 307 lines) — PID+start-time+parent-PID verification before SIGKILL; file-locked registry at `<userPiRoot>/state/orphan-workers.json`; honest about residual userspace TOCTOU window between start-time re-check and actual `process.kill`.
|
|
102
|
+
- **`src/runtime/pi-args.ts`** (heavily expanded, 342 lines) — `createSafeTempDir` walks the full ancestor chain rejecting symlinks; `buildPiWorkerArgs` builds the child argv safely (no shell); `cleanupTempDir` / `cleanupAllTrackedTempDirs` / `cleanupOrphanTempDirs` / `cleanupLegacyOrphanTempDirs` provide 4 layers of defense with bounded work.
|
|
103
|
+
|
|
104
|
+
### Test Coverage
|
|
105
|
+
- **`test/unit/orphan-worker-registry.test.ts`** (NEW, 279 lines) — `registerWorker` / `unregisterWorker` / `cleanupOrphanWorkers` with `__test_setRegistryPath` for isolation; covers invalid PIDs, dedup, parent-PID tolerance, current-session protection, dead-PID pruning.
|
|
106
|
+
- **`test/unit/cleanup-orphan-temp.test.ts`** (NEW, 242 lines) — covers `cleanupTempDir` / `cleanupAllTrackedTempDirs` / `cleanupOrphanTempDirs` / `cleanupLegacyOrphanTempDirs` with `utimesSync` to simulate aged dirs; tests symlink skip, in-use skip, and `/tmp` legacy cleanup.
|
|
107
|
+
- **`test/integration/cleanup-full-flow.test.ts`** (NEW, 241 lines) — end-to-end integration of all cleanup layers, simulating a crashed session.
|
|
108
|
+
|
|
109
|
+
### Documentation
|
|
110
|
+
- **`src/runtime/parent-guard.ts`** — added a "Trust model" JSDoc section explaining why `PI_CREW_PARENT_PID` is safe to pass in env (PID is not a secret), what residual risks remain (child can spoof before guard starts), and why the guard is a self-termination signal, not a security boundary.
|
|
111
|
+
- **`src/utils/safe-paths.ts:resolveRealContainedPath`** — added a "Security model — asymmetric ancestor handling" JSDoc section explaining why `baseDir` ancestors must exist (cannot validate otherwise) while target ancestors may be non-existent (for new-file creation).
|
|
112
|
+
|
|
113
|
+
### Stats
|
|
114
|
+
- 23 commits since v0.6.2: `0aed8b5`, `152ac80`, `ee0ddb4`, `81b9608`, `5edcb18`, `8ba270d`, `ceb1cb1`, `a76932d`, `c9eb430`, `a192509`, `992231d`, `dbf7a48`, `e1f7dfe`, `1bf67eb`, `2b8f27a`, `de3f550`, `e1ea7d4`, `5819b18`, `cd7ef89`, `b782424`, `a0c2ba3`, `098c8a9`, `ba0ce54`, `aa457a5`, `04fe0be`, `f15ee98`
|
|
115
|
+
- 79 files changed (+3567 / -712)
|
|
116
|
+
- 1 new state fix: manifest/tasks mtime false positive
|
|
117
|
+
- 1 new file: `orphan-worker-registry.ts` (307 lines)
|
|
118
|
+
- 3 new test files: 762 lines
|
|
119
|
+
|
|
120
|
+
## [0.6.2] — Issue #28 + #29 Fixes + Post-Review Hardening (2026-06-05)
|
|
121
|
+
|
|
122
|
+
### Highlights
|
|
123
|
+
- **Issue #28 fixed**: `crew-init.ts` jiti namespace race — inline `parseRoot`/`safeJoin`/`safeDirname`/`safeResolve` helpers; jiti upgraded 2.6.1 → 2.7.0
|
|
124
|
+
- **Issue #29 fixed**: 11 hardcoded `.crew/state/runs/...` sites now use `projectCrewRoot()` for `.pi/teams/` fallback
|
|
125
|
+
- **Subagent defense-in-depth**: `record.promise` rejection can no longer crash pi via `unhandledRejection`
|
|
126
|
+
- **2 new MEDIUM-severity path-traversal vulnerabilities fixed** in `decision-ledger.ts` (7 functions) and `run-graph.ts` (3 functions)
|
|
127
|
+
- **F-8 safeJoin edge case** fixed (trailing separator handling)
|
|
128
|
+
- **~100 new tests** across 7 files (5 e2e scripts + 18 regression tests for the new fixes)
|
|
129
|
+
|
|
130
|
+
### Security Fixes (MEDIUM)
|
|
131
|
+
- `decision-ledger.ts`: `initLedger`, `appendEntry`, `getLedger`, `getLatestDecision`, `summarizeLedger`, `promoteCandidate`, `decayCandidate` now call `assertSafePathId("runId", runId)` to prevent path-traversal
|
|
132
|
+
- `run-graph.ts`: `saveRunGraph`, `loadRunGraph`, `listRunGraphs` use `projectCrewRoot()` and `assertSafePathId("runId", runId)` (same bug class as issue #29)
|
|
133
|
+
|
|
134
|
+
### Bug Fixes
|
|
135
|
+
- **F-1** (post-issue-#29 review): removed duplicate "Defense in depth" comment in `subagent-manager.ts`
|
|
136
|
+
- **F-3** (post-issue-#29 review): added F-6 documentation in `test/unit/crew-init.test.ts` header
|
|
137
|
+
- **F-8** (post-issue-#29 review): `safeJoin("/", "foo")` no longer produces `"//foo"`; trailing separator in parts is stripped (UNC paths preserved)
|
|
138
|
+
- **`crew-init.ts safeJoin`**: collapse internal runs of separator while preserving leading UNC `\\\\` and POSIX `/`
|
|
139
|
+
|
|
140
|
+
### Test Coverage
|
|
141
|
+
- 9 new regression tests in `test/unit/issue-29-pi-paths.test.ts` (resolver precedence, `waitForRun` error message in both layouts, checkpoint/skill-effectiveness round-trip in `.pi/`-only project, decision-ledger path, defense-in-depth via real child process)
|
|
142
|
+
- 13 new tests in `test/unit/crew-init.test.ts` (F-1 UNC preservation, F-2 jiti race via `path` Proxy, F-3 `safeResolve` graceful degradation, F-4 `__test__internals` export convention, F-5 stale docstring, F-6 async-runner rationale, F-8 trailing-separator)
|
|
143
|
+
- 4 new tests in `test/unit/crew-init.test.ts` for `safeJoin` (F-8 regression coverage)
|
|
144
|
+
- 7 new tests in `test/unit/decision-ledger.test.ts` (path-traversal rejection)
|
|
145
|
+
- 3 new tests in `test/unit/run-graph.test.ts` (path-traversal rejection)
|
|
146
|
+
- 14 new tests in `test/unit/skill-effectiveness.test.ts` (cwd parameter migration)
|
|
147
|
+
- 5 new E2E scripts in `scripts/test-issue-29-*.ts`:
|
|
148
|
+
- `test-issue-29-e2e.ts` — unit-level integration
|
|
149
|
+
- `test-issue-29-crash.ts` — focused crash reproduction
|
|
150
|
+
- `test-issue-29-team-tool.ts` — slow-path early-exit error message
|
|
151
|
+
- `test-issue-29-real-tasks.ts` — full `executeTeamRun` pipeline in `.pi/`-only project (25 assertions)
|
|
152
|
+
- **`test-issue-29-real-runtime.ts`** — spawns REAL detached `background-runner.ts` process (most realistic)
|
|
153
|
+
|
|
154
|
+
### Test Quality Improvements
|
|
155
|
+
- **F-4** (post-issue-#29 review): `crew-init.ts findProjectRoot` now accepts optional `path` dep; test passes the `stubPath` Proxy directly to source code (not a copy)
|
|
156
|
+
- **F-5** (post-issue-#29 review): defense-in-depth crash test now spawns a real child process so the host's `unhandledRejection` detector can actually fire (was previously a comment-only test)
|
|
157
|
+
|
|
158
|
+
### Stats
|
|
159
|
+
- 7 source files changed (3 fixes + 1 new path-resolver usage)
|
|
160
|
+
- 7 test files changed (4 unit + 5 e2e scripts)
|
|
161
|
+
- 9 commits since v0.6.1: `e95e055`, `cd8c3b8`, `a80fe6c`, `362789c`, `0c78307`, `083afaf`, `d33b86c`, `03c0a20`, `1bedd24`, `0ce3d5a`, `b17fb6b`, `f8731e6`, `105d31d`, `00c66a5`, `2d49910`, `6cbbafa`
|
|
162
|
+
|
|
163
|
+
## [0.6.1] — Post-v0.6.0 Security Hardening + Test Coverage (2026-06-04)
|
|
164
|
+
|
|
165
|
+
### Highlights
|
|
166
|
+
- **42+ security issues fixed** — 7 CRITICAL, 10 HIGH, 11 MEDIUM, 14 post-restart review findings
|
|
167
|
+
- **~1,900 new tests** across 113+ test files — total suite now ~4,600 tests
|
|
168
|
+
- **38 dead exports cleaned** across 19 modules
|
|
169
|
+
- **12 `any` types replaced** with proper TypeScript types
|
|
170
|
+
- **Full battle-testing** — 2 Pi restart cycles, all team types, management operations verified
|
|
171
|
+
|
|
172
|
+
### Security Fixes (CRITICAL)
|
|
173
|
+
- `async-runner.ts`: Environment variable leak in child process — sanitized with `sanitizeEnvSecrets()`
|
|
174
|
+
- `verification-gates.ts`: Shell injection via user-controlled strings — switched to `execFileSync`
|
|
175
|
+
- `sandbox.ts`: `String.fromCharCode` bypass — added `constructor` to `FORBIDDEN_PATTERNS`
|
|
176
|
+
- `locks.ts`: Timing-unsafe comparison on lock tokens — replaced with constant-time compare
|
|
177
|
+
- `event-log.ts`: Request IDs logged in plaintext — now hashed before logging
|
|
178
|
+
- `team-runner.ts`: Missing heartbeat for long-running tasks — added 30s heartbeat writer
|
|
179
|
+
- `worktree-manager.ts`: Environment secrets leaked to git subprocesses — `sanitizeEnvSecrets()`
|
|
180
|
+
|
|
181
|
+
### Security Fixes (HIGH)
|
|
182
|
+
- `preStepScript` symlink traversal — `fs.realpathSync` before path containment check
|
|
183
|
+
- `childEnvAllowList` wildcard patterns (`LC_*`, `XDG_*`) could leak secrets
|
|
184
|
+
- Event log sync/async race condition — route sync `appendEvent` through async queue
|
|
185
|
+
- Subagent record validation — `sanitizePersistedRecord()` with allow-listed fields
|
|
186
|
+
- Verification gate redirect — allow single `>` for `2>&1`, block `>>` and `<[^&]`
|
|
187
|
+
- `allowPatterns` validation — reject patterns matching empty strings
|
|
188
|
+
|
|
189
|
+
### Security Fixes (MEDIUM)
|
|
190
|
+
- `logInternalError` import paths normalized across all modules
|
|
191
|
+
- `Object.freeze()` narrowing fix — use `Readonly<{...}>` explicit types
|
|
192
|
+
- NTFS mtime granularity — write-first, `utimes`-after for cache invalidation
|
|
193
|
+
- Windows path separators — platform-agnostic assertions in tests
|
|
194
|
+
- `executeUnchecked` visibility — `__test_executeUnchecked` export pattern
|
|
195
|
+
- `seedPaths` containment — `normalizeSeedPaths()` validates paths stay within `repoRoot`
|
|
196
|
+
|
|
197
|
+
### Code Quality
|
|
198
|
+
- 38 dead/unused exports removed across 19 source modules
|
|
199
|
+
- 12 `any` types replaced with proper interfaces
|
|
200
|
+
- `enforceLabelCap` MRU correctness — `delete`-then-`set` to maintain Map insertion order
|
|
201
|
+
- `readIfSmall` bounded reads — `Buffer.alloc` + `fs.readSync` instead of `readFileSync`
|
|
202
|
+
|
|
203
|
+
### Test Coverage
|
|
204
|
+
- 113 new test files, ~1,900 new test cases
|
|
205
|
+
- Modules now covered: config, extension, workflow, subagent, observability, runtime, graph,
|
|
206
|
+
heartbeat, permissions, state, locks, event-log, safe-bash, sandbox, verification-gates,
|
|
207
|
+
async-runner, team-runner, background-runner, worktree, fingerprint, BM25 search, and more
|
|
208
|
+
- Windows CI verified: path separators, `npx.cmd` resolution, NTFS mtime all pass
|
|
209
|
+
- Test runner wrapper (`scripts/test-runner.mjs`) ensures non-zero exit on failures
|
|
210
|
+
|
|
211
|
+
### Stats
|
|
212
|
+
- Test suite: ~4,600 pass, 0 fail
|
|
213
|
+
- TypeScript: 0 errors
|
|
214
|
+
- Lines added since v0.6.0: 22,520 (742 src + 21,777 test)
|
|
215
|
+
- Files changed: 204
|
|
216
|
+
- Security issues fixed: 42+
|
|
217
|
+
- Audit rounds: 42 (including post-v0.6.0 battle-testing)
|
|
218
|
+
|
|
219
|
+
## [0.6.0] — Source Tour Patterns + 15 New Modules (2026-06-03)
|
|
220
|
+
|
|
221
|
+
### Highlights
|
|
222
|
+
- **15 upstream patterns implemented** from 63-repository source tour
|
|
223
|
+
- **10 new source modules** (2,267 LOC): chain-parser, run-drift, intercom-bridge,
|
|
224
|
+
plan-templates, task-id, context-retrieval, intermediate-store, fingerprint,
|
|
225
|
+
memory-store, observation-store
|
|
226
|
+
- **37 skills reviewed** with origin fields, all passing validation
|
|
227
|
+
|
|
3
228
|
## [0.5.22] — Remaining Issues from Ultimate Sweep (2026-06-03)
|
|
4
229
|
|
|
5
230
|
### Highlights
|
package/README.md
CHANGED
|
@@ -9,24 +9,28 @@ npm: pi-crew
|
|
|
9
9
|
repo: https://github.com/baphuongna/pi-crew
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
**v0.
|
|
13
|
-
|
|
14
|
-
###
|
|
15
|
-
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
|
|
29
|
-
|
|
12
|
+
**v0.6.3**: See [CHANGELOG.md](CHANGELOG.md).
|
|
13
|
+
|
|
14
|
+
### Highlights (v0.6.2 → v0.6.3)
|
|
15
|
+
|
|
16
|
+
- **137 commits** since v0.6.1 — 200 files changed (+16,955 / −2,057 lines)
|
|
17
|
+
- **4,792 tests**, 506 test files — **0 failures** across the entire suite
|
|
18
|
+
- **Cross-platform CI green** — 0 failures on Ubuntu, macOS, and Windows
|
|
19
|
+
- **366 source files**, ~70K lines of TypeScript
|
|
20
|
+
- **Worktree precondition validation** — friendly errors instead of crashes when cwd is not a git repo or repo is dirty
|
|
21
|
+
- **Cross-platform path handling** — `canonicalizePath` with `realpathSync.native` for Windows short-name/long-name aliasing; macOS symlink resolution
|
|
22
|
+
- **Scheduled job lifecycle** — spawned runs are tracked, cancelling a job kills its runs
|
|
23
|
+
- **Heartbeat false-positive fix** — PID liveness gate prevents dead detection during long LLM responses
|
|
24
|
+
- **ENOENT crash fix** — prune/forget race no longer crashes pi when persisting to deleted runs
|
|
25
|
+
- **Pipe buffer deadlock fix** — test runner no longer deadlocks when OS pipe buffer fills
|
|
26
|
+
- **Plugin registry** — extensible framework context injection for Next.js, Vite, Vitest
|
|
27
|
+
- **Health score system** — penalty-based scoring with time-series snapshots
|
|
28
|
+
- **CrewError taxonomy** — E001–E006 structured error codes replacing raw throws
|
|
29
|
+
- **Atomic write v2** — fsync + rename pattern for crash-safe state persistence
|
|
30
|
+
- **Pre-push review**: 56 unpushed commits reviewed, 1 release blocker found and fixed
|
|
31
|
+
- **Security**: sandbox constructor escape strengthened; env-filter provider key handling fixed
|
|
32
|
+
- **State-store race fix** — manifest/tasks mtime false positive eliminated
|
|
33
|
+
- **Orphan worker/temp cleanup** — 4-layer defense with session-scoped tracking
|
|
30
34
|
|
|
31
35
|
---
|
|
32
36
|
|
|
@@ -48,6 +52,9 @@ See [SECURITY-ISSUES.md](SECURITY-ISSUES.md) for the full list (SEC-001 – SEC-
|
|
|
48
52
|
- **Adaptive plan fanout** — single `assess` step lets a planner pick the smallest effective crew
|
|
49
53
|
- **Adaptive workflows** — `implementation`, `review`, `parallel-research`, `research` workflows ship in `workflows/`
|
|
50
54
|
- **Hardened secrets** — linear-time detection covers PEM keys, Authorization headers, Bearer tokens, and `key=value` patterns
|
|
55
|
+
- **Scheduled runs** — `schedule`/`scheduled` actions with cron, interval, and one-shot support; spawned runs tracked and auto-cancelled on job removal
|
|
56
|
+
- **Plugin system** — framework-aware context injection (Next.js, Vite, Vitest) via plugin registry
|
|
57
|
+
- **Health scoring** — penalty-based run health with time-series snapshots
|
|
51
58
|
|
|
52
59
|
---
|
|
53
60
|
|
|
@@ -118,13 +125,13 @@ When unsure which team/workflow fits:
|
|
|
118
125
|
|
|
119
126
|
## Builtin Teams
|
|
120
127
|
|
|
121
|
-
| Team | Workflow |
|
|
128
|
+
| Team | Workflow | Purpose |
|
|
122
129
|
|------|----------|----------|
|
|
123
|
-
| `default` | explore → plan → execute → verify |
|
|
124
|
-
| `fast-fix` | explore → execute → verify |
|
|
125
|
-
| `implementation` | Adaptive planner
|
|
130
|
+
| `default` | explore → plan → execute → verify | Balanced, general-purpose |
|
|
131
|
+
| `fast-fix` | explore → execute → verify | Quick bug fixes |
|
|
132
|
+
| `implementation` | Adaptive planner decides fanout | Multi-file implementation |
|
|
126
133
|
| `review` | explore → code-review → security-review → verify | Code review + security audit |
|
|
127
|
-
| `research` | explore → analyze → write |
|
|
134
|
+
| `research` | explore → analyze → write | Research and documentation |
|
|
128
135
|
| `parallel-research` | Parallel shards → synthesize → write | Multi-source research |
|
|
129
136
|
|
|
130
137
|
## Builtin Agents
|
|
@@ -178,7 +185,7 @@ Worktree mode creates an **isolated git worktree per task** — safe for paralle
|
|
|
178
185
|
"action": "run",
|
|
179
186
|
"team": "implementation",
|
|
180
187
|
"goal": "Refactor auth",
|
|
181
|
-
"
|
|
188
|
+
"workspaceMode": "worktree"
|
|
182
189
|
}
|
|
183
190
|
```
|
|
184
191
|
|
|
@@ -187,10 +194,13 @@ Worktree mode creates an **isolated git worktree per task** — safe for paralle
|
|
|
187
194
|
```
|
|
188
195
|
|
|
189
196
|
Requirements:
|
|
190
|
-
- Git repository
|
|
191
|
-
- Clean working tree (no uncommitted changes in the
|
|
197
|
+
- Git repository (cwd must be inside a git repo)
|
|
198
|
+
- Clean working tree (no uncommitted changes in the leader worktree)
|
|
199
|
+
- Can be disabled via config: `requireCleanWorktreeLeader: false`
|
|
192
200
|
- Worktrees auto-cleanup on run completion/cancel
|
|
193
201
|
|
|
202
|
+
If preconditions are not met, a friendly error message is returned instead of crashing.
|
|
203
|
+
|
|
194
204
|
---
|
|
195
205
|
|
|
196
206
|
## Configuration
|
|
@@ -253,11 +263,18 @@ Requirements:
|
|
|
253
263
|
{ "action": "artifacts", "runId": "team_..." }
|
|
254
264
|
{ "action": "cancel", "runId": "team_..." }
|
|
255
265
|
{ "action": "resume", "runId": "team_..." }
|
|
266
|
+
{ "action": "retry", "runId": "team_..." }
|
|
267
|
+
{ "action": "steer", "runId": "team_...", "taskId": "01_explore", "message": "Focus on src/ only" }
|
|
268
|
+
{ "action": "respond", "runId": "team_...", "message": "Answer" }
|
|
269
|
+
{ "action": "wait", "runId": "team_..." }
|
|
256
270
|
|
|
257
271
|
// Discovery
|
|
258
272
|
{ "action": "list" }
|
|
259
|
-
{ "action": "get", "resource": "team", "
|
|
273
|
+
{ "action": "get", "resource": "team", "team": "default" }
|
|
274
|
+
{ "action": "get", "resource": "agent", "agent": "explorer" }
|
|
275
|
+
{ "action": "get", "resource": "workflow", "workflow": "review" }
|
|
260
276
|
{ "action": "recommend", "goal": "Refactor auth flow" }
|
|
277
|
+
{ "action": "search", "goal": "heartbeat detection" }
|
|
261
278
|
|
|
262
279
|
// Resource management
|
|
263
280
|
{ "action": "create", "resource": "agent", "config": { "name": "api-reviewer", ... } }
|
|
@@ -279,12 +296,34 @@ Requirements:
|
|
|
279
296
|
{ "action": "autonomy", "profile": "assisted" }
|
|
280
297
|
|
|
281
298
|
// Advanced
|
|
282
|
-
{ "action": "api", "runId": "team_...", "operation": "read-manifest" }
|
|
299
|
+
{ "action": "api", "runId": "team_...", "config": { "operation": "read-manifest" } }
|
|
283
300
|
{ "action": "plan", "team": "default", "goal": "..." }
|
|
301
|
+
{ "action": "orchestrate", "planPath": "plan.md", "team": "implementation", "goal": "..." }
|
|
302
|
+
{ "action": "parallel", "config": { "tasks": [{"goal": "...", "agent": "explorer"}] } }
|
|
284
303
|
{ "action": "worktrees", "runId": "team_..." }
|
|
304
|
+
{ "action": "graph", "runId": "team_..." }
|
|
305
|
+
{ "action": "explain", "runId": "team_..." }
|
|
306
|
+
{ "action": "health" }
|
|
307
|
+
{ "action": "doctor" }
|
|
308
|
+
{ "action": "cache" }
|
|
309
|
+
{ "action": "invalidate", "runId": "team_..." }
|
|
310
|
+
|
|
311
|
+
// Scheduled runs
|
|
312
|
+
{ "action": "schedule", "team": "fast-fix", "goal": "Run tests", "cron": "0 9 * * MON" }
|
|
313
|
+
{ "action": "schedule", "team": "default", "goal": "...", "interval": 3600000 }
|
|
314
|
+
{ "action": "schedule", "team": "research", "goal": "...", "once": "+10m" }
|
|
315
|
+
{ "action": "scheduled" }
|
|
316
|
+
|
|
317
|
+
// Diagnostics & settings
|
|
318
|
+
{ "action": "config" }
|
|
319
|
+
{ "action": "settings" }
|
|
320
|
+
{ "action": "autonomy" }
|
|
321
|
+
{ "action": "anchor" }
|
|
322
|
+
{ "action": "onboard" }
|
|
323
|
+
{ "action": "auto-summarize" }
|
|
285
324
|
```
|
|
286
325
|
|
|
287
|
-
📖 Full actions reference (
|
|
326
|
+
📖 Full actions reference (40+ actions): [docs/actions-reference.md](docs/actions-reference.md)
|
|
288
327
|
|
|
289
328
|
---
|
|
290
329
|
|
|
@@ -379,13 +418,13 @@ Your system prompt here.
|
|
|
379
418
|
```bash
|
|
380
419
|
cd pi-crew
|
|
381
420
|
npm install # dependencies
|
|
382
|
-
npm test # unit + integration tests
|
|
421
|
+
npm test # unit + integration tests (~4,800 tests)
|
|
383
422
|
npm run typecheck # tsc --noEmit
|
|
384
423
|
npm run ci # full CI-equivalent check
|
|
385
424
|
npm pack --dry-run # package verification
|
|
386
425
|
```
|
|
387
426
|
|
|
388
|
-
|
|
427
|
+
Stats: **366 source files** (70K lines) · **506 test files** (66K lines) · **4,792 tests, 0 failures** · **CI: Ubuntu ✅ macOS ✅ Windows ✅**
|
|
389
428
|
|
|
390
429
|
---
|
|
391
430
|
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Issue #29 Analysis: Hardcoded `.crew/state/runs` Path Crashes pi
|
|
2
|
+
|
|
3
|
+
> **Status**: ANALYZED — no code changes applied (per user request).
|
|
4
|
+
> **Reporter**: sethmorton (external, opened 2026-06-05)
|
|
5
|
+
> **Affects**: pi-crew 0.6.0 / 0.6.1 (issue present in v0.6.1 too — not fixed in any post-v0.6.1 commit as of `e95e055`).
|
|
6
|
+
> **Severity**: **CRITICAL** — full-harness uncaughtException, can crash pi.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Reporter's Claims — Verification Matrix
|
|
11
|
+
|
|
12
|
+
| Claim | Verified? | Evidence |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `waitForRun()` hardcodes `.crew/state/runs/<runId>` at `run-tracker.ts:82` | ✅ YES | `src/runtime/run-tracker.ts:82` — `const runDir = path.join(cwd, ".crew", "state", "runs", runId);` |
|
|
15
|
+
| `projectCrewRoot()` correctly resolves to `.pi/teams/` for `.pi/`-only projects | ✅ YES | `src/utils/paths.ts:88-97` |
|
|
16
|
+
| The throw escapes as an unhandled promise rejection | ✅ PARTIAL | Both direct call sites (`team-tool.ts:817`, `team-tool/run.ts:211,344`) wrap in `try/catch`. **However**, the issue is real when `waitForRun` is called indirectly through `subagent-manager.ts`. |
|
|
17
|
+
| `subagent-manager.ts:250` re-throws via `record.promise` IIFE | ⚠️ LINE-DRIFT | Issue says `~line 250` — in current code (commit `e95e055`), `record.promise` IIFE starts at line 258 and the `throw error;` is at line 281. The mechanism is correct; line numbers drifted from prior code. |
|
|
18
|
+
|
|
19
|
+
## 2. Root Cause Analysis
|
|
20
|
+
|
|
21
|
+
### Why it crashes (in `.pi/`-only projects)
|
|
22
|
+
|
|
23
|
+
`projectCrewRoot(cwd)` (in `src/utils/paths.ts:88-97`) returns:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
1. .crew/ (if exists)
|
|
27
|
+
2. .pi/teams/ (if .pi/ exists and .crew/ doesn't)
|
|
28
|
+
3. .crew/ (default, would be created by ensureCrewDirectory)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
In a `.pi/`-based project **without** `.crew/`, run state lives at:
|
|
32
|
+
```
|
|
33
|
+
.pi/teams/state/runs/<runId>/
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
But `waitForRun()` (line 82) hardcodes:
|
|
37
|
+
```
|
|
38
|
+
.crew/state/runs/<runId>/
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The early-exit check `fs.existsSync(runDir)` returns `false`, throwing:
|
|
42
|
+
```
|
|
43
|
+
Error: Run <runId> not found. No run directory at <cwd>/.crew/state/runs/<runId>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Why this becomes a `uncaughtException`
|
|
47
|
+
|
|
48
|
+
The throw in `waitForRun` propagates differently depending on caller:
|
|
49
|
+
|
|
50
|
+
| Caller | Wrapped in try/catch? | Result |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `team-tool.ts:817` (wait action) | ✅ YES | Returns error to user — safe |
|
|
53
|
+
| `team-tool/run.ts:211` (async.run) | ✅ YES | Returns error to user — safe |
|
|
54
|
+
| `team-tool/run.ts:344` (foreground) | ✅ YES | Returns error to user — safe |
|
|
55
|
+
| **`subagent-manager.ts:270` (pollRunToTerminal)** | ⚠️ **NO** | `record.promise` rejects → line 281 `throw error` re-throws → if no caller awaits → **unhandled rejection** |
|
|
56
|
+
|
|
57
|
+
The `subagent-manager.ts` path is the actual crash site. Direct `waitForRun` calls in `team-tool.ts` are safe.
|
|
58
|
+
|
|
59
|
+
### Why the run "exists" in `.pi/teams/...`
|
|
60
|
+
|
|
61
|
+
The background worker process (`background-runner.ts`) correctly **reads/writes** to the right path **once it uses `projectCrewRoot()`** — but it does NOT. The same hardcoding is present in `background-runner.ts:139, 172`. So the **background worker itself is writing to `.crew/state/runs/...` (wrong path)**, while the **manifest claims it lives at `.pi/teams/state/runs/...`** (correct path).
|
|
62
|
+
|
|
63
|
+
Wait — let me re-verify this. The manifest is created by which process?
|
|
64
|
+
|
|
65
|
+
Actually, the **run manifest is created at the orchestrator** (using `projectCrewRoot`), and the **background worker reads/writes log/exit-code files using the same wrong path**. The manifest's `stateRoot` field is correct (`.pi/teams/...`), but the background worker writes its log to `<cwd>/.crew/state/runs/.../background.log`, which is the wrong path.
|
|
66
|
+
|
|
67
|
+
Result: the manifest correctly points to `.pi/teams/...` but the background worker logs/exit-codes go to `.crew/.../background.log`. The manifest loader (`loadRunManifestById` → `scopeBaseRoot` → `projectCrewRoot`) reads from the correct path. So if the manifest is created and persists, polling SHOULD work. The early-exit `fs.existsSync` check fires only on the very first poll iteration before any manifest has been written.
|
|
68
|
+
|
|
69
|
+
## 3. Affected Sites — Complete Inventory
|
|
70
|
+
|
|
71
|
+
Hardcoded `.crew/state/runs/...` paths that ignore `projectCrewRoot()`:
|
|
72
|
+
|
|
73
|
+
| File | Line | Code | Used for |
|
|
74
|
+
|---|---|---|---|
|
|
75
|
+
| `src/runtime/run-tracker.ts` | 82 | `path.join(cwd, ".crew", "state", "runs", runId)` | **CRASH SITE**: `waitForRun()` early-exit |
|
|
76
|
+
| `src/runtime/background-runner.ts` | 139 | `path.join(_cwd, ".crew/state/runs", _runId, "background.log")` | Background worker log redirect |
|
|
77
|
+
| `src/runtime/background-runner.ts` | 172 | `path.join(cwd, ".crew", "state", "runs", runId, "exit-code.txt")` | Exit code persistence |
|
|
78
|
+
| `src/runtime/skill-effectiveness.ts` | 115 | `join(process.cwd(), ".crew/state/runs/${runId}/skill-metrics.jsonl")` | Skill metrics storage |
|
|
79
|
+
| `src/runtime/skill-effectiveness.ts` | 125 | `join(process.cwd(), ".crew/state/runs/${runId}/skill-activations.jsonl")` | Skill activation storage |
|
|
80
|
+
| `src/runtime/checkpoint.ts` | 166 | `path.join(process.cwd(), ".crew/state/runs", runId)` | `saveCheckpoint` |
|
|
81
|
+
| `src/runtime/checkpoint.ts` | 177 | `path.join(process.cwd(), ".crew/state/runs", runId)` | `loadCheckpoint` |
|
|
82
|
+
| `src/runtime/checkpoint.ts` | 188 | `path.join(process.cwd(), ".crew/state/runs", runId)` | `clearCheckpoint` |
|
|
83
|
+
| `src/runtime/checkpoint.ts` | 199 | `path.join(process.cwd(), ".crew/state/runs", runId)` | `hasCheckpoint` |
|
|
84
|
+
| `src/runtime/checkpoint.ts` | 209 | `path.join(process.cwd(), ".crew/state/runs", runId)` | `listCheckpoints` |
|
|
85
|
+
| `src/state/decision-ledger.ts` | 29 | `stateRoot ?? \`.crew/state/runs/${runId}\`` | `getLedgerPath` fallback (always hit when called without `stateRoot`) |
|
|
86
|
+
|
|
87
|
+
**11 sites across 5 files** confirmed affected.
|
|
88
|
+
|
|
89
|
+
### Already correct (for reference)
|
|
90
|
+
|
|
91
|
+
| File | Line | Why correct |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `src/state/crew-init.ts` | 209 | `safeJoin(crewRoot, "state", "runs")` — uses `crewRoot` parameter |
|
|
94
|
+
| `src/extension/team-tool.ts` | 1210 | `path.join(projectCrewRoot(ctx.cwd), "state", "runs", params.runId)` ✓ |
|
|
95
|
+
| `src/extension/team-onboard.ts` | 28 | `path.join(crewRoot, "state", "runs")` — uses parameter |
|
|
96
|
+
| `src/runtime/stale-reconciler.ts` | 389 | Inside `tmpDir` workspace scan — these are pi-crew's own zombie workspaces, not user `.pi/` projects, so this is acceptable (but inconsistent) |
|
|
97
|
+
|
|
98
|
+
## 4. Why the Bug Survives (it should have been caught)
|
|
99
|
+
|
|
100
|
+
- ✅ `loadRunManifestById` and `loadRunManifestByIdAsync` in `state-store.ts` correctly use `projectCrewRoot()` via `scopeBaseRoot` → `useProjectState`.
|
|
101
|
+
- ❌ `waitForRun()` and the 10 other sites above bypass the resolver and hardcode the path.
|
|
102
|
+
- The hardcoded path is **inconsistent** with the resolver — clearly a copy-paste/regression.
|
|
103
|
+
- The issue is **only triggered in `.pi/`-only projects** (no `.crew/`). Standard `.crew/` projects (the original pi-crew use case) never hit it.
|
|
104
|
+
|
|
105
|
+
## 5. Recommended Fix (NOT APPLIED per user request)
|
|
106
|
+
|
|
107
|
+
### Phase 1 — Use the resolver everywhere
|
|
108
|
+
|
|
109
|
+
Replace each hardcoded path with:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { projectCrewRoot } from "../utils/paths.ts";
|
|
113
|
+
// ...
|
|
114
|
+
const stateRoot = path.join(projectCrewRoot(cwd), "state", "runs", runId);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
For sites that take `process.cwd()`, switch to taking a `cwd` parameter (callers pass the real cwd). This is a small refactor for `skill-effectiveness.ts` and `checkpoint.ts` since their current API uses `process.cwd()`.
|
|
118
|
+
|
|
119
|
+
### Phase 2 — Defense in depth (the reporter's second suggestion)
|
|
120
|
+
|
|
121
|
+
The reporter's point about `subagent-manager.ts` is also valid: **a single run failure should never crash the harness**. The current pattern at line 281 `throw error; // H4: Propagate rejection` deliberately rejects the promise, but if no one awaits it, it becomes unhandled.
|
|
122
|
+
|
|
123
|
+
Two options:
|
|
124
|
+
|
|
125
|
+
**A) Add a safety net in `subagent-manager.ts` (recommended)**:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
private start(record: SubagentRecord, ...) {
|
|
129
|
+
// ...
|
|
130
|
+
record.promise = (async () => { ... })();
|
|
131
|
+
// Defense in depth: a subagent failure should never crash pi.
|
|
132
|
+
// Attach a no-op catch so that even if no caller awaits, the rejection
|
|
133
|
+
// doesn't propagate to unhandledRejection → uncaughtException.
|
|
134
|
+
record.promise.catch((error) => {
|
|
135
|
+
logInternalError("subagent-manager.start.unhandled", error, `id=${record.id}`);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
This protects the harness against **any** future rejection path, not just the one fixed in Phase 1.
|
|
141
|
+
|
|
142
|
+
**B) Remove the `throw error;` at line 281** — the record is already marked `error` status, the caller can read it. This is a deeper change to the API contract.
|
|
143
|
+
|
|
144
|
+
Option (A) is the smaller, safer change.
|
|
145
|
+
|
|
146
|
+
## 6. Test Coverage
|
|
147
|
+
|
|
148
|
+
Tests exist for `checkpoint` (`checkpoint.test.ts`, `checkpoint-cov.test.ts`) and `skill-effectiveness` (`skill-effectiveness.test.ts`) but they likely use a `.crew/`-based temp project, so they wouldn't catch this bug. A regression test should:
|
|
149
|
+
|
|
150
|
+
1. Create a temp project with `.pi/` (no `.crew/`)
|
|
151
|
+
2. Call each affected function (`waitForRun`, `saveCheckpoint`, `getSkillMetricsPath`, etc.)
|
|
152
|
+
3. Assert the result path is `.pi/teams/...` (not `.crew/...`)
|
|
153
|
+
|
|
154
|
+
For the `subagent-manager.ts` defense-in-depth: spawn a subagent whose `runner` rejects, assert pi does not exit and the record is marked `error`.
|
|
155
|
+
|
|
156
|
+
## 7. Impact Assessment
|
|
157
|
+
|
|
158
|
+
- **Blast radius**: All pi-crew users with `.pi/`-only projects.
|
|
159
|
+
- **Trigger frequency**: Low — requires the background worker to fail/die AND `waitForRun` to be called indirectly via `subagent-manager.ts`. The `team-tool` direct paths are safe.
|
|
160
|
+
- **Severity**: CRITICAL — full-harness crash with misleading "Run not found" error pointing at a path that, by design, should never exist.
|
|
161
|
+
|
|
162
|
+
## 8. Recommended PR Description (draft)
|
|
163
|
+
|
|
164
|
+
> **Title**: fix(issue #29): use projectCrewRoot() for run-state paths in all 11 affected sites
|
|
165
|
+
>
|
|
166
|
+
> **Body**:
|
|
167
|
+
>
|
|
168
|
+
> The `.pi/`-based project layout (`.pi/teams/`) is not handled by the 11 hardcoded `.crew/state/runs/...` sites listed in the analysis. The most severe site is `waitForRun()` in `src/runtime/run-tracker.ts:82`, which throws on a non-existent path and (via `subagent-manager.ts:281`) can escape as an unhandled rejection that crashes pi.
|
|
169
|
+
>
|
|
170
|
+
> Fix:
|
|
171
|
+
> 1. Replace all 11 hardcoded paths with `path.join(projectCrewRoot(cwd), "state", "runs", runId)`.
|
|
172
|
+
> 2. For sites that use `process.cwd()` (`skill-effectiveness.ts`, `checkpoint.ts`), switch to taking a `cwd` parameter.
|
|
173
|
+
> 3. Add `record.promise.catch(...)` in `subagent-manager.ts:start()` as defense in depth — a subagent failure should never crash the harness.
|
|
174
|
+
> 4. Add regression tests using a `.pi/`-only temp project.
|
|
175
|
+
>
|
|
176
|
+
> Closes #29.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Status
|
|
181
|
+
|
|
182
|
+
- ✅ Issue reproduced in code (all 11 sites verified)
|
|
183
|
+
- ✅ Root cause identified
|
|
184
|
+
- ✅ Fix plan drafted
|
|
185
|
+
- ❌ **Code changes NOT applied** (per user request: "chưa thực hiện sửa gì cả")
|
|
186
|
+
- ❌ Tests NOT added
|
|
187
|
+
- ❌ v0.6.2 NOT released
|
|
188
|
+
|
|
189
|
+
Sẵn sàng apply khi user yêu cầu.
|