taskplane 0.22.10 → 0.22.12

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.
@@ -98,6 +98,26 @@ export interface ParsedTask {
98
98
  resolvedRepoId?: string;
99
99
  /** Optional explicit segment DAG metadata from `## Segment DAG`. */
100
100
  explicitSegmentDag?: PromptSegmentDagMetadata;
101
+ /**
102
+ * Repo ID that owns task packet files (v4, TP-081).
103
+ * Populated by execution engine in workspace mode. Undefined in repo mode.
104
+ */
105
+ packetRepoId?: string;
106
+ /**
107
+ * Absolute path to task folder in the packet repo worktree (v4, TP-081).
108
+ * Populated by execution engine. Undefined if not yet resolved.
109
+ */
110
+ packetTaskPath?: string;
111
+ /**
112
+ * Segment IDs for this task (v4, TP-081).
113
+ * Populated from TaskSegmentPlan during execution.
114
+ */
115
+ segmentIds?: string[];
116
+ /**
117
+ * Currently active segment ID (v4, TP-081).
118
+ * Null when no segment is active.
119
+ */
120
+ activeSegmentId?: string | null;
101
121
  }
102
122
 
103
123
  /** Build a stable segment ID from task + repo identity (`<taskId>::<repoId>`). */
@@ -205,7 +225,7 @@ export interface TaskArea {
205
225
  export interface TaskRunnerConfig {
206
226
  task_areas: Record<string, TaskArea>;
207
227
  reference_docs: Record<string, string>;
208
- /** Named testing/verification commands (e.g., { test: "npx vitest run" }). Used for baseline fingerprinting (TP-032). */
228
+ /** Named testing/verification commands (e.g., { test: "node --test tests/*.test.ts" }). Used for baseline fingerprinting (TP-032). */
209
229
  testing_commands?: Record<string, string>;
210
230
  /**
211
231
  * Model fallback behavior when a configured model becomes unavailable mid-batch.
@@ -1015,6 +1035,12 @@ export interface OrchBatchRuntimeState {
1015
1035
  * Populated from persisted state on resume; defaults used for new batches.
1016
1036
  */
1017
1037
  diagnostics?: BatchDiagnostics;
1038
+ /**
1039
+ * v4 segment records carried forward across resume cycles (TP-081).
1040
+ * Populated from persisted state on resume; empty for new batches
1041
+ * and repo-mode batches.
1042
+ */
1043
+ segments?: PersistedSegmentRecord[];
1018
1044
  /**
1019
1045
  * Unknown top-level fields from loaded persisted state.
1020
1046
  * Carried forward so they survive serialization roundtrips.
@@ -2304,15 +2330,22 @@ export function defaultBatchDiagnostics(): BatchDiagnostics {
2304
2330
  * exit summaries, batch cost). Task records gain optional
2305
2331
  * `exitDiagnostic` alongside legacy `exitReason`.
2306
2332
  * Both new sections are optional for v1/v2 migration paths.
2333
+ * v4 — Segment execution (TP-081). Adds optional `segments` array
2334
+ * for persisting per-segment runtime state. Task records gain
2335
+ * optional `packetRepoId`, `packetTaskPath`, `segmentIds`, and
2336
+ * `activeSegmentId` fields. All v4-specific fields are optional
2337
+ * for backward compatibility with v1/v2/v3 migration paths.
2338
+ * When migrating from v3, `segments` defaults to `[]` and
2339
+ * task-level segment fields default to `undefined`.
2307
2340
  *
2308
2341
  * Compatibility policy:
2309
- * - loadBatchState() accepts v1, v2, and v3 files. v1 and v2 are
2310
- * auto-upconverted to v3 in memory (chained: v1→v2→v3).
2342
+ * - loadBatchState() accepts v1, v2, v3, and v4 files. v1v2→v3→v4
2343
+ * auto-upconverted in memory (chained).
2311
2344
  * The on-disk file is NOT rewritten during load.
2312
- * - saveBatchState() always writes v3.
2313
- * - Schema versions > 3 are rejected with STATE_SCHEMA_INVALID.
2345
+ * - saveBatchState() always writes v4.
2346
+ * - Schema versions > 4 are rejected with STATE_SCHEMA_INVALID.
2314
2347
  */
2315
- export const BATCH_STATE_SCHEMA_VERSION = 3;
2348
+ export const BATCH_STATE_SCHEMA_VERSION = 4;
2316
2349
 
2317
2350
  /**
2318
2351
  * Canonical file path for persisted batch state.
@@ -2431,6 +2464,99 @@ export interface PersistedTaskRecord {
2431
2464
  * falling back to `exitReason` for display.
2432
2465
  */
2433
2466
  exitDiagnostic?: TaskExitDiagnostic;
2467
+ /**
2468
+ * Repo ID that owns task packet files (PROMPT.md/STATUS.md/.DONE) (v4, TP-081).
2469
+ *
2470
+ * In workspace mode, this is the `taskPacketRepo` from routing config.
2471
+ * Undefined in repo mode or for pre-v4 state files.
2472
+ */
2473
+ packetRepoId?: string;
2474
+ /**
2475
+ * Absolute path to the task folder in the packet repo worktree (v4, TP-081).
2476
+ *
2477
+ * Used by resume to locate packet files without re-running discovery.
2478
+ * Undefined in repo mode or for pre-v4 state files.
2479
+ */
2480
+ packetTaskPath?: string;
2481
+ /**
2482
+ * Segment IDs belonging to this task (v4, TP-081).
2483
+ *
2484
+ * Array of segment ID strings (`<taskId>::<repoId>`).
2485
+ * Empty array for repo-mode tasks or single-repo tasks.
2486
+ * Undefined for pre-v4 state files.
2487
+ */
2488
+ segmentIds?: string[];
2489
+ /**
2490
+ * Currently executing segment ID (v4, TP-081).
2491
+ *
2492
+ * Null when no segment is active (all completed or not started).
2493
+ * Undefined for pre-v4 state files.
2494
+ */
2495
+ activeSegmentId?: string | null;
2496
+ }
2497
+
2498
+ // ── Segment-Level Persisted State (v4, TP-081) ──────────────────────
2499
+
2500
+ /**
2501
+ * Segment execution status within a batch.
2502
+ *
2503
+ * State machine mirrors `LaneTaskStatus` but applies at segment granularity:
2504
+ * pending → running → succeeded
2505
+ * → failed
2506
+ * → stalled
2507
+ * pending → skipped (prior segment failed, or task skipped)
2508
+ *
2509
+ * @since v4 (TP-081)
2510
+ */
2511
+ export type PersistedSegmentStatus = "pending" | "running" | "succeeded" | "failed" | "stalled" | "skipped";
2512
+
2513
+ /**
2514
+ * Persisted record of a single segment's execution state.
2515
+ *
2516
+ * A segment is a repo-scoped execution unit within a task. Each task
2517
+ * may have one or more segments (one per repo the task touches).
2518
+ *
2519
+ * Contains everything `/orch-resume` needs to reconstruct segment-level
2520
+ * progress without re-running discovery.
2521
+ *
2522
+ * @since v4 (TP-081)
2523
+ */
2524
+ export interface PersistedSegmentRecord {
2525
+ /** Stable segment identifier (`<taskId>::<repoId>`, e.g., "TP-002::api") */
2526
+ segmentId: string;
2527
+ /** Parent task identifier */
2528
+ taskId: string;
2529
+ /** Repo ID this segment targets */
2530
+ repoId: string;
2531
+ /** Segment execution status */
2532
+ status: PersistedSegmentStatus;
2533
+ /** Lane ID the segment executed on (e.g., "lane-1"), empty if not yet assigned */
2534
+ laneId: string;
2535
+ /** TMUX session name used for this segment */
2536
+ sessionName: string;
2537
+ /** Absolute path to the worktree used for this segment */
2538
+ worktreePath: string;
2539
+ /** Git branch name checked out for this segment */
2540
+ branch: string;
2541
+ /** Epoch ms when segment execution started (null if not yet started) */
2542
+ startedAt: number | null;
2543
+ /** Epoch ms when segment execution ended (null if still pending/running) */
2544
+ endedAt: number | null;
2545
+ /** Number of retry attempts for this segment */
2546
+ retries: number;
2547
+ /**
2548
+ * Segment IDs this segment depends on (intra-task DAG edges).
2549
+ * Empty array for the first segment in a task or for tasks with no intra-task deps.
2550
+ */
2551
+ dependsOnSegmentIds: string[];
2552
+ /**
2553
+ * Structured exit diagnostic for this segment.
2554
+ * Optional: absent for segments that haven't exited yet.
2555
+ * Uses the same `TaskExitDiagnostic` shape from diagnostics.ts.
2556
+ */
2557
+ exitDiagnostic?: TaskExitDiagnostic;
2558
+ /** Human-readable exit reason (legacy compat, same as task-level) */
2559
+ exitReason: string;
2434
2560
  }
2435
2561
 
2436
2562
  /**
@@ -2545,9 +2671,17 @@ export interface PersistedRepoMergeOutcome {
2545
2671
  * data alongside legacy `exitReason` string).
2546
2672
  * - Both sections are required in v3. Migration from v1/v2 fills
2547
2673
  * conservative defaults (see `defaultResilienceState()` / `defaultBatchDiagnostics()`).
2674
+ *
2675
+ * v4 additions (TP-081):
2676
+ * - `segments` array (required): per-segment execution records for multi-repo
2677
+ * task execution. Empty array in repo mode or for pre-v4 migration.
2678
+ * - Task records gain optional `packetRepoId`, `packetTaskPath`, `segmentIds`,
2679
+ * and `activeSegmentId` for segment-level tracking.
2680
+ * - Migration from v3 fills `segments` as `[]` and leaves task-level segment
2681
+ * fields as `undefined`.
2548
2682
  */
2549
2683
  export interface PersistedBatchState {
2550
- /** Schema version — must equal BATCH_STATE_SCHEMA_VERSION (currently 3) */
2684
+ /** Schema version — must equal BATCH_STATE_SCHEMA_VERSION (currently 4) */
2551
2685
  schemaVersion: number;
2552
2686
  /** Current batch execution phase */
2553
2687
  phase: OrchBatchPhase;
@@ -2596,14 +2730,24 @@ export interface PersistedBatchState {
2596
2730
  errors: string[];
2597
2731
  /**
2598
2732
  * Resilience state for retry/recovery tracking (v3, TP-030).
2599
- * Required in v3. Migration from v1/v2 fills conservative defaults.
2733
+ * Required in v3+. Migration from v1/v2 fills conservative defaults.
2600
2734
  */
2601
2735
  resilience: ResilienceState;
2602
2736
  /**
2603
2737
  * Batch-level diagnostics for cost tracking and exit summaries (v3, TP-030).
2604
- * Required in v3. Migration from v1/v2 fills conservative defaults.
2738
+ * Required in v3+. Migration from v1/v2 fills conservative defaults.
2605
2739
  */
2606
2740
  diagnostics: BatchDiagnostics;
2741
+ /**
2742
+ * Per-segment execution records for multi-repo task execution (v4, TP-081).
2743
+ *
2744
+ * Each entry represents one repo-scoped segment of a task. In repo mode
2745
+ * or for single-repo tasks, this array is empty (segment tracking is
2746
+ * implicit via task records).
2747
+ *
2748
+ * Required in v4. Migration from v1/v2/v3 fills empty array.
2749
+ */
2750
+ segments: PersistedSegmentRecord[];
2607
2751
  /**
2608
2752
  * Unknown top-level fields captured during deserialization.
2609
2753
  * Preserved on roundtrip to avoid data loss from future schema extensions
@@ -3213,3 +3357,97 @@ export function createRepoModeContext(
3213
3357
  };
3214
3358
  }
3215
3359
 
3360
+
3361
+ // ── Agent Mailbox Types (TP-089) ─────────────────────────────────────
3362
+
3363
+ /**
3364
+ * Mailbox directory name under .pi/.
3365
+ * @since TP-089
3366
+ */
3367
+ export const MAILBOX_DIR_NAME = "mailbox";
3368
+
3369
+ /**
3370
+ * Maximum content size in UTF-8 bytes.
3371
+ * Steering messages should be concise directives; larger context should be
3372
+ * written to a separate file and referenced by path.
3373
+ * @since TP-089
3374
+ */
3375
+ export const MAILBOX_MAX_CONTENT_BYTES = 4096;
3376
+
3377
+ /**
3378
+ * Message types for the agent mailbox system.
3379
+ *
3380
+ * | Type | Direction | Purpose |
3381
+ * |------------|---------------------|--------------------------------------------|
3382
+ * | `steer` | supervisor → agent | Course correction. Agent must follow. |
3383
+ * | `query` | supervisor → agent | Request for status/info. Agent replies. |
3384
+ * | `abort` | supervisor → agent | Graceful stop. Agent wraps up and exits. |
3385
+ * | `info` | supervisor → agent | FYI context. No action required. |
3386
+ * | `reply` | agent → supervisor | Response to query or steer acknowledgment. |
3387
+ * | `escalate` | agent → supervisor | Agent-initiated: blocked or needs guidance. |
3388
+ *
3389
+ * @since TP-089
3390
+ */
3391
+ export type MailboxMessageType = "steer" | "query" | "abort" | "info" | "reply" | "escalate";
3392
+
3393
+ /**
3394
+ * Set of valid mailbox message types for runtime validation.
3395
+ * @since TP-089
3396
+ */
3397
+ export const MAILBOX_MESSAGE_TYPES: ReadonlySet<string> = new Set<MailboxMessageType>([
3398
+ "steer", "query", "abort", "info", "reply", "escalate",
3399
+ ]);
3400
+
3401
+ /**
3402
+ * Message format for the file-based agent mailbox.
3403
+ *
3404
+ * Messages are written as JSON files in batch-scoped, session-scoped
3405
+ * directories. The rpc-wrapper checks the inbox on every `message_end`
3406
+ * event and injects pending messages into the agent's LLM context via
3407
+ * pi's `steer` RPC command.
3408
+ *
3409
+ * @see docs/specifications/taskplane/agent-mailbox-steering.md
3410
+ * @since TP-089
3411
+ */
3412
+ export interface MailboxMessage {
3413
+ /** Unique message ID: `{timestamp}-{5char-hex-nonce}` */
3414
+ id: string;
3415
+ /** Batch ID — must match current batch for validation */
3416
+ batchId: string;
3417
+ /** Sender identifier: `"supervisor"` or session name */
3418
+ from: string;
3419
+ /** Target session name or `"_broadcast"` */
3420
+ to: string;
3421
+ /** Epoch milliseconds (Date.now()) */
3422
+ timestamp: number;
3423
+ /** Message type */
3424
+ type: MailboxMessageType;
3425
+ /** Message body (max 4KB UTF-8 bytes) */
3426
+ content: string;
3427
+ /** Whether the sender expects a reply (default: false) */
3428
+ expectsReply?: boolean;
3429
+ /** Reference to a previous message ID for threading (default: null) */
3430
+ replyTo?: string | null;
3431
+ }
3432
+
3433
+ /**
3434
+ * Input options for writeMailboxMessage.
3435
+ *
3436
+ * The caller provides these fields; the utility generates `id`, `batchId`,
3437
+ * `to`, and `timestamp` from its own arguments.
3438
+ *
3439
+ * @since TP-089
3440
+ */
3441
+ export interface WriteMailboxMessageOpts {
3442
+ /** Sender identifier: `"supervisor"` or session name */
3443
+ from: string;
3444
+ /** Message type */
3445
+ type: MailboxMessageType;
3446
+ /** Message body (max 4KB UTF-8 bytes) */
3447
+ content: string;
3448
+ /** Whether the sender expects a reply (default: false) */
3449
+ expectsReply?: boolean;
3450
+ /** Reference to a previous message ID for threading (default: null) */
3451
+ replyTo?: string | null;
3452
+ }
3453
+
@@ -27,10 +27,15 @@
27
27
  * 6. Truncate to 512 chars (bound fingerprint size)
28
28
  *
29
29
  * **Fallback for non-JSON output:**
30
- * If vitest JSON parsing fails (truncated, missing, non-JSON), produce a
31
- * single fingerprint with kind: "command_error" and the first 512 chars
30
+ * If legacy Vitest JSON parsing fails (truncated, missing, non-JSON), produce
31
+ * a single fingerprint with kind: "command_error" and the first 512 chars
32
32
  * of stderr (or stdout) as messageNorm.
33
33
  *
34
+ * **Compatibility note:**
35
+ * Taskplane's default tests use Node.js native `node:test`. The Vitest parser
36
+ * in this module is retained only for backward compatibility when projects
37
+ * provide custom `testing.commands` that still emit Vitest JSON.
38
+ *
34
39
  * @module orch/verification
35
40
  */
36
41
  import { spawnSync } from "child_process";
@@ -300,20 +305,20 @@ function classifyFailureKind(message: string): TestFingerprint["kind"] {
300
305
  }
301
306
 
302
307
  /**
303
- * Parse vitest JSON reporter output into test fingerprints.
308
+ * Parse legacy Vitest JSON reporter output into test fingerprints.
304
309
  *
305
- * Expects the stdout to contain a JSON object matching vitest's JSON reporter format.
310
+ * Expects stdout to contain a JSON object matching Vitest's JSON reporter format.
306
311
  * Only failed tests produce fingerprints (passed tests are irrelevant for baseline diffing).
307
312
  *
308
313
  * If JSON parsing fails or the structure is unexpected, returns null to signal
309
314
  * that the caller should use fallback fingerprinting.
310
315
  *
311
316
  * @param commandId - The command that produced this output
312
- * @param stdout - Raw stdout from the vitest command
317
+ * @param stdout - Raw stdout from the Vitest command (legacy compatibility path)
313
318
  * @returns Array of fingerprints for failed tests, or null if parsing fails
314
319
  */
315
320
  export function parseVitestOutput(commandId: string, stdout: string): TestFingerprint[] | null {
316
- // Try to extract JSON from stdout (vitest may prepend/append non-JSON lines)
321
+ // Try to extract JSON from stdout (Vitest may prepend/append non-JSON lines)
317
322
  let json: VitestJsonResult;
318
323
  try {
319
324
  // First attempt: parse the whole stdout as JSON
@@ -363,7 +368,7 @@ export function parseVitestOutput(commandId: string, stdout: string): TestFinger
363
368
  }
364
369
 
365
370
  // Suite-level failures: testResults[].status === "failed" with no assertion-level details.
366
- // This covers setup/import/runtime-at-file-load errors where vitest marks the file as
371
+ // This covers setup/import/runtime-at-file-load errors where Vitest marks the file as
367
372
  // failed but produces no assertionResults (or only non-failed ones).
368
373
  if (testFile.status === "failed") {
369
374
  const hasFailedAssertions = hasAssertions && assertions!.some(a => a.status === "failed");
@@ -388,7 +393,7 @@ export function parseVitestOutput(commandId: string, stdout: string): TestFinger
388
393
  * Parse test output into normalized fingerprints.
389
394
  *
390
395
  * Strategy:
391
- * 1. Try vitest JSON adapter
396
+ * 1. Try legacy Vitest JSON adapter
392
397
  * 2. If parsing fails: produce a fallback command_error fingerprint
393
398
  *
394
399
  * The adapter pattern is extensible — future parsers for jest, pytest, etc.
@@ -416,7 +421,7 @@ export function parseTestOutput(commandResult: CommandResult): TestFingerprint[]
416
421
  return [];
417
422
  }
418
423
 
419
- // Try vitest JSON adapter
424
+ // Try legacy Vitest JSON adapter
420
425
  const vitestFingerprints = parseVitestOutput(commandId, stdout);
421
426
  if (vitestFingerprints !== null && vitestFingerprints.length > 0) {
422
427
  return vitestFingerprints;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.22.10",
3
+ "version": "0.22.12",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -68,7 +68,7 @@ Every action you take falls into one of three categories:
68
68
  ### Diagnostic (always allowed — no confirmation needed)
69
69
  - Reading batch-state.json, STATUS.md, events.jsonl, merge results
70
70
  - Running `git status`, `git log`, `git diff`
71
- - Running test suites (`npx vitest run`, etc.)
71
+ - Running test suites (`node --experimental-strip-types --experimental-test-module-mocks --no-warnings --import ./tests/loader.mjs --test ...`, etc.)
72
72
  - Listing tmux sessions (`tmux list-sessions`)
73
73
  - Checking worktree health (`git worktree list`)
74
74
  - Reading any file for diagnostics
@@ -201,8 +201,29 @@ a reviewer agent. The tool takes two parameters: `step` (number) and `type`
201
201
  documentation/delivery). These are low-risk steps where review overhead exceeds
202
202
  value.
203
203
 
204
+ ### ⚠️ CRITICAL: Plan review happens BEFORE implementation
205
+
206
+ **The plan review MUST happen BEFORE you write any code for that step.**
207
+ The entire purpose of plan review is to catch design issues, missing cases, and
208
+ wrong approaches BEFORE you spend tokens implementing them. If you implement
209
+ first and then request plan review, the reviewer's feedback is wasted — the
210
+ code is already written.
211
+
212
+ **Correct sequence:**
213
+ 1. Hydrate step checkboxes (expand the plan)
214
+ 2. Commit the hydrated STATUS.md
215
+ 3. **Call `review_step(step=N, type="plan")` — BEFORE writing any code**
216
+ 4. Handle verdict (APPROVE → implement; REVISE → fix plan, re-review)
217
+ 5. Implement the step (write code, check off items)
218
+ 6. Commit implementation
219
+ 7. Call `review_step(step=N, type="code")` — AFTER implementation
220
+
221
+ **WRONG sequence (violates the protocol):**
222
+ 1. ~~Hydrate, implement, check off, commit, THEN call plan review~~ ❌
223
+ This makes plan review pointless — the work is already done.
224
+
204
225
  **Handling verdicts:**
205
- - **APPROVE** → proceed to next step
226
+ - **APPROVE** → proceed (to implementation after plan review; to next step after code review)
206
227
  - **RETHINK** → reconsider your plan approach, adjust, then implement
207
228
  - **REVISE** → read the review file in `.reviews/` for detailed feedback,
208
229
  address the issues, commit fixes, then **call `review_step` again** for re-review.
@@ -211,14 +232,16 @@ value.
211
232
 
212
233
  **Example flow for a Review Level 2 task, Step 3:**
213
234
  1. Read Step 3 requirements
214
- 2. Call `review_step(step=3, type="plan")` → get plan feedback
215
- 3. Capture baseline: run `git rev-parse HEAD` and save the SHA
216
- 4. Implement Step 3
217
- 5. Commit changes
218
- 6. Call `review_step(step=3, type="code", baseline="<saved SHA>")` → get code feedback
219
- 7. If REVISE: fix issues, commit, call `review_step(step=3, type="code")` again
220
- 8. Repeat 7 until APPROVE (max 2 code review cycles per step)
221
- 9. Move to Step 4
235
+ 2. Hydrate Step 3 checkboxes, commit STATUS.md
236
+ 3. Call `review_step(step=3, type="plan")` get plan feedback (**NO CODE YET**)
237
+ 4. If REVISE: adjust plan, re-request plan review
238
+ 5. If APPROVE: capture baseline SHA (`git rev-parse HEAD`)
239
+ 6. Implement Step 3 (write code, check off items)
240
+ 7. Commit changes
241
+ 8. Call `review_step(step=3, type="code", baseline="<saved SHA>")` get code feedback
242
+ 9. If REVISE: fix issues, commit, call `review_step(step=3, type="code")` again
243
+ 10. Repeat 9 until APPROVE (max 2 code review cycles per step)
244
+ 11. Move to Step 4
222
245
 
223
246
  If the `review_step` tool is not available (e.g., non-orchestrated mode), skip
224
247
  this protocol entirely — the task-runner handles reviews externally.
@@ -252,30 +275,24 @@ Run tests at two different scopes depending on where you are in the task:
252
275
 
253
276
  ### During implementation steps (targeted tests)
254
277
 
255
- After implementing each step, run **targeted tests** for fast feedback:
278
+ After implementing each step, run **targeted tests** for fast feedback.
279
+ Use file-targeted runs for the test files that cover your changes:
256
280
 
257
281
  ```bash
258
- cd extensions && npx vitest run --changed
282
+ cd extensions && node --experimental-strip-types --experimental-test-module-mocks --no-warnings --import ./tests/loader.mjs --test tests/some-specific.test.ts
259
283
  ```
260
284
 
261
- - Vitest's `--changed` flag uses git to find modified files since the last commit
262
- and runs only tests related to those files.
263
- - Workers commit at step boundaries, so between commits the changed set is
264
- exactly "what this step modified" this naturally targets the right tests.
265
- - Alternatively, run specific test files that cover the code you modified:
266
- `npx vitest run tests/some-specific.test.ts`
267
- - **If `--changed` returns no tests:** That's fine — it means your changes don't
268
- have directly related test files. The full suite in the Testing step will catch
269
- any indirect regressions.
270
- - **If targeted tests fail:** Fix the failure before proceeding. Don't accumulate
271
- failures across steps.
285
+ - Node's native runner does not provide a reliable project-level `--changed`
286
+ equivalent; select targeted files explicitly.
287
+ - If multiple files are relevant, pass multiple `--test` paths.
288
+ - **If targeted tests fail:** fix them before proceeding. Don't accumulate failures.
272
289
 
273
290
  ### During the Testing & Verification step (full suite)
274
291
 
275
292
  Run the **full test suite** as a quality gate:
276
293
 
277
294
  ```bash
278
- cd extensions && npx vitest run
295
+ cd extensions && node --experimental-strip-types --experimental-test-module-mocks --no-warnings --import ./tests/loader.mjs --test tests/*.test.ts
279
296
  ```
280
297
 
281
298
  - ALL tests must pass — zero failures allowed.