pi-crew 0.8.6 → 0.8.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-crew",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "Pi extension for coordinated AI teams, workflows, worktrees, and async task orchestration",
5
5
  "author": "baphuongna",
6
6
  "license": "MIT",
@@ -9,6 +9,7 @@ import { projectCrewRoot, userCrewRoot } from "../../utils/paths.ts";
9
9
  import { DEFAULT_PATHS } from "../../config/defaults.ts";
10
10
  import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
11
11
  import { getPiSpawnCommand } from "../../runtime/pi-spawn.ts";
12
+ import { getRuntimeWarmupStatus } from "../../runtime/runtime-warmup.ts";
12
13
  import { validateResources } from "../validate-resources.ts";
13
14
  import { detectDrift, formatDriftReport, type DriftReport } from "../../config/drift-detector.ts";
14
15
  import { TeamToolParams } from "../../schema/team-tool-schema.ts";
@@ -188,6 +189,37 @@ export function buildTeamDoctorReport(input: TeamDoctorReportInput): TeamDoctorR
188
189
  { label: "leader repository", ok: true, detail: input.cwd },
189
190
  { label: "cleanup policy", ok: true, detail: "dirty worktrees preserved unless force is set" },
190
191
  ]),
192
+ section("Runtime warmup (cold-start fix v0.8.6)", () => {
193
+ // Surface whether the general cold-start-race fix is active + how long
194
+ // the graph warmup took, so a session can confirm the fix loaded
195
+ // (post-restart) and isn't pathologically slow. An UNWARMED graph is
196
+ // the documented cause of `Cannot read properties of undefined
197
+ // (reading '<binding>')` under concurrent subagent spawn.
198
+ //
199
+ // "Not started" is NOT a doctor error: it is the normal state in unit
200
+ // tests and in any caller that invokes buildTeamDoctorReport directly
201
+ // without going through registerPiTeams. Only a STARTED-but-FAILED
202
+ // warmup is an error (something genuinely went wrong during pre-warm).
203
+ const status = getRuntimeWarmupStatus();
204
+ const checks: DoctorCheck[] = [
205
+ {
206
+ label: "warmup started",
207
+ ok: true, // informational — "not started" is not a failure
208
+ detail: status.started ? "module graph pre-warmed at registration" : "not started in this process (normal for direct unit-test calls; in a live Pi session, started at extension load)",
209
+ },
210
+ ];
211
+ if (status.started) {
212
+ checks.push({
213
+ label: "warmup completed",
214
+ ok: status.completed,
215
+ detail: status.completed ? (status.durationMs !== undefined ? `graph warm in ${status.durationMs}ms` : "completed") : "in progress",
216
+ });
217
+ if (status.error) {
218
+ checks.push({ label: "warmup error", ok: false, detail: status.error });
219
+ }
220
+ }
221
+ return checks;
222
+ }),
191
223
  ];
192
224
  if (input.smokeChildPi) {
193
225
  sections.push([`Child check`, `- ${input.smokeChildPi.ok ? "OK" : "FAIL"} child Pi smoke: ${input.smokeChildPi.detail}`]);
@@ -61,6 +61,9 @@ const HOT_PEER_DEPS = ["@earendil-works/pi-coding-agent"] as const;
61
61
 
62
62
  let warmupPromise: Promise<void> | undefined;
63
63
  let warmupStarted = false;
64
+ let warmupCompleted = false;
65
+ let warmupDurationMs: number | undefined;
66
+ let warmupError: string | undefined;
64
67
 
65
68
  /**
66
69
  * Start the runtime warmup (idempotent). Fires eager `import()` of the hot
@@ -74,6 +77,7 @@ let warmupStarted = false;
74
77
  export function startRuntimeWarmup(): void {
75
78
  if (warmupStarted) return;
76
79
  warmupStarted = true;
80
+ const startedAt = Date.now();
77
81
  warmupPromise = (async (): Promise<void> => {
78
82
  const imports: Array<Promise<unknown>> = [];
79
83
  for (const spec of HOT_MODULE_SPECIFIERS) {
@@ -91,9 +95,15 @@ export function startRuntimeWarmup(): void {
91
95
  );
92
96
  }
93
97
  await Promise.all(imports);
94
- })().catch(() => {
95
- // final safety net — warmup must never reject
96
- });
98
+ })()
99
+ .then(() => {
100
+ warmupCompleted = true;
101
+ warmupDurationMs = Date.now() - startedAt;
102
+ })
103
+ .catch((err: unknown) => {
104
+ // final safety net — warmup must never reject. Record for diagnostics.
105
+ warmupError = err instanceof Error ? err.message : String(err ?? "unknown");
106
+ });
97
107
  }
98
108
 
99
109
  /**
@@ -113,9 +123,34 @@ export async function awaitRuntimeWarmup(): Promise<void> {
113
123
  export function resetRuntimeWarmupForTest(): void {
114
124
  warmupPromise = undefined;
115
125
  warmupStarted = false;
126
+ warmupCompleted = false;
127
+ warmupDurationMs = undefined;
128
+ warmupError = undefined;
116
129
  }
117
130
 
118
131
  /** Test seam: has startRuntimeWarmup() been called? */
119
132
  export function isRuntimeWarmupStarted(): boolean {
120
133
  return warmupStarted;
121
134
  }
135
+
136
+ /**
137
+ * Diagnostic snapshot of warmup state for `team doctor`. Surfaces whether the
138
+ * v0.8.6 cold-start fix is active and how long the graph warmup took, so a
139
+ * session can confirm the fix loaded (post-restart) and isn't pathologically
140
+ * slow.
141
+ */
142
+ export interface RuntimeWarmupStatus {
143
+ started: boolean;
144
+ completed: boolean;
145
+ durationMs: number | undefined;
146
+ error: string | undefined;
147
+ }
148
+
149
+ export function getRuntimeWarmupStatus(): RuntimeWarmupStatus {
150
+ return {
151
+ started: warmupStarted,
152
+ completed: warmupCompleted,
153
+ durationMs: warmupDurationMs,
154
+ error: warmupError,
155
+ };
156
+ }