substrate-ai 0.2.20 → 0.2.21

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.
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "./logger-D2fS2ccL.js";
2
2
  import { AdapterRegistry, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning } from "./event-bus-BMxhfxfT.js";
3
3
  import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./decisions-Dq4cAA2L.js";
4
- import { ESCALATION_DIAGNOSIS, OPERATIONAL_FINDING, STORY_METRICS, STORY_OUTCOME, aggregateTokenUsageForRun, aggregateTokenUsageForStory, getStoryMetricsForRun, writeRunMetrics, writeStoryMetrics } from "./operational-Dq4IfJzE.js";
4
+ import { ESCALATION_DIAGNOSIS, OPERATIONAL_FINDING, STORY_METRICS, STORY_OUTCOME, TEST_EXPANSION_FINDING, TEST_PLAN, aggregateTokenUsageForRun, aggregateTokenUsageForStory, getStoryMetricsForRun, writeRunMetrics, writeStoryMetrics } from "./operational-CnMlvWqc.js";
5
5
  import { createRequire } from "module";
6
6
  import { dirname, join } from "path";
7
7
  import { access, readFile, readdir, stat } from "fs/promises";
@@ -539,7 +539,7 @@ const migration010RunMetrics = {
539
539
 
540
540
  //#endregion
541
541
  //#region src/persistence/migrations/index.ts
542
- const logger$17 = createLogger("persistence:migrations");
542
+ const logger$19 = createLogger("persistence:migrations");
543
543
  const MIGRATIONS = [
544
544
  initialSchemaMigration,
545
545
  costTrackerSchemaMigration,
@@ -557,7 +557,7 @@ const MIGRATIONS = [
557
557
  * Safe to call multiple times — already-applied migrations are skipped.
558
558
  */
559
559
  function runMigrations(db) {
560
- logger$17.info("Starting migration runner");
560
+ logger$19.info("Starting migration runner");
561
561
  db.exec(`
562
562
  CREATE TABLE IF NOT EXISTS schema_migrations (
563
563
  version INTEGER PRIMARY KEY,
@@ -568,12 +568,12 @@ function runMigrations(db) {
568
568
  const appliedVersions = new Set(db.prepare("SELECT version FROM schema_migrations").all().map((row) => row.version));
569
569
  const pending = MIGRATIONS.filter((m) => !appliedVersions.has(m.version)).sort((a, b) => a.version - b.version);
570
570
  if (pending.length === 0) {
571
- logger$17.info("No pending migrations");
571
+ logger$19.info("No pending migrations");
572
572
  return;
573
573
  }
574
574
  const insertMigration = db.prepare("INSERT INTO schema_migrations (version, name) VALUES (?, ?)");
575
575
  for (const migration of pending) {
576
- logger$17.info({
576
+ logger$19.info({
577
577
  version: migration.version,
578
578
  name: migration.name
579
579
  }, "Applying migration");
@@ -587,14 +587,14 @@ function runMigrations(db) {
587
587
  });
588
588
  applyMigration();
589
589
  }
590
- logger$17.info({ version: migration.version }, "Migration applied successfully");
590
+ logger$19.info({ version: migration.version }, "Migration applied successfully");
591
591
  }
592
- logger$17.info({ count: pending.length }, "All pending migrations applied");
592
+ logger$19.info({ count: pending.length }, "All pending migrations applied");
593
593
  }
594
594
 
595
595
  //#endregion
596
596
  //#region src/persistence/database.ts
597
- const logger$16 = createLogger("persistence:database");
597
+ const logger$18 = createLogger("persistence:database");
598
598
  /**
599
599
  * Thin wrapper that opens a SQLite database, applies required PRAGMAs,
600
600
  * and exposes the raw BetterSqlite3 instance.
@@ -611,14 +611,14 @@ var DatabaseWrapper = class {
611
611
  */
612
612
  open() {
613
613
  if (this._db !== null) return;
614
- logger$16.info({ path: this._path }, "Opening SQLite database");
614
+ logger$18.info({ path: this._path }, "Opening SQLite database");
615
615
  this._db = new BetterSqlite3(this._path);
616
616
  const walResult = this._db.pragma("journal_mode = WAL");
617
- if (walResult?.[0]?.journal_mode !== "wal") logger$16.warn({ result: walResult?.[0]?.journal_mode }, "WAL pragma did not return expected \"wal\" — journal_mode may be \"memory\" or unsupported");
617
+ if (walResult?.[0]?.journal_mode !== "wal") logger$18.warn({ result: walResult?.[0]?.journal_mode }, "WAL pragma did not return expected \"wal\" — journal_mode may be \"memory\" or unsupported");
618
618
  this._db.pragma("busy_timeout = 5000");
619
619
  this._db.pragma("synchronous = NORMAL");
620
620
  this._db.pragma("foreign_keys = ON");
621
- logger$16.info({ path: this._path }, "SQLite database opened with WAL mode");
621
+ logger$18.info({ path: this._path }, "SQLite database opened with WAL mode");
622
622
  }
623
623
  /**
624
624
  * Close the database. Idempotent — calling close() when already closed is a no-op.
@@ -627,7 +627,7 @@ var DatabaseWrapper = class {
627
627
  if (this._db === null) return;
628
628
  this._db.close();
629
629
  this._db = null;
630
- logger$16.info({ path: this._path }, "SQLite database closed");
630
+ logger$18.info({ path: this._path }, "SQLite database closed");
631
631
  }
632
632
  /**
633
633
  * Return the raw BetterSqlite3 instance.
@@ -1208,11 +1208,38 @@ function buildPipelineStatusOutput(run, tokenSummary, decisionsCount, storiesCou
1208
1208
  totalCost += row.total_cost_usd;
1209
1209
  }
1210
1210
  let activeDispatches = 0;
1211
+ let storiesSummary;
1211
1212
  try {
1212
1213
  if (run.token_usage_json) {
1213
1214
  const state = JSON.parse(run.token_usage_json);
1214
- if (state.stories) {
1215
- for (const s of Object.values(state.stories)) if (s.phase !== "PENDING" && s.phase !== "COMPLETE" && s.phase !== "ESCALATED") activeDispatches++;
1215
+ if (state.stories && Object.keys(state.stories).length > 0) {
1216
+ const now = Date.now();
1217
+ let completed = 0;
1218
+ let inProgress = 0;
1219
+ let escalated = 0;
1220
+ let pending = 0;
1221
+ const details = {};
1222
+ for (const [key, s] of Object.entries(state.stories)) {
1223
+ const phase = s.phase ?? "PENDING";
1224
+ if (phase !== "PENDING" && phase !== "COMPLETE" && phase !== "ESCALATED") activeDispatches++;
1225
+ if (phase === "COMPLETE") completed++;
1226
+ else if (phase === "ESCALATED") escalated++;
1227
+ else if (phase === "PENDING") pending++;
1228
+ else inProgress++;
1229
+ const elapsed = s.startedAt != null ? Math.max(0, Math.round((now - new Date(s.startedAt).getTime()) / 1e3)) : 0;
1230
+ details[key] = {
1231
+ phase,
1232
+ review_cycles: s.reviewCycles ?? 0,
1233
+ elapsed_seconds: elapsed
1234
+ };
1235
+ }
1236
+ storiesSummary = {
1237
+ completed,
1238
+ in_progress: inProgress,
1239
+ escalated,
1240
+ pending,
1241
+ details
1242
+ };
1216
1243
  }
1217
1244
  }
1218
1245
  } catch {}
@@ -1230,7 +1257,8 @@ function buildPipelineStatusOutput(run, tokenSummary, decisionsCount, storiesCou
1230
1257
  last_activity: run.updated_at,
1231
1258
  staleness_seconds: Math.round((Date.now() - parseDbTimestampAsUtc(run.updated_at).getTime()) / 1e3),
1232
1259
  last_event_ts: run.updated_at,
1233
- active_dispatches: activeDispatches
1260
+ active_dispatches: activeDispatches,
1261
+ ...storiesSummary !== void 0 ? { stories: storiesSummary } : {}
1234
1262
  };
1235
1263
  }
1236
1264
  /**
@@ -1259,6 +1287,19 @@ function formatPipelineStatusHuman(status) {
1259
1287
  lines.push(` Total Cost: $${status.total_tokens.cost_usd.toFixed(4)}`);
1260
1288
  lines.push(` Decisions: ${status.decisions_count}`);
1261
1289
  lines.push(` Stories: ${status.stories_count}`);
1290
+ if (status.stories !== void 0 && Object.keys(status.stories.details).length > 0) {
1291
+ lines.push("");
1292
+ lines.push(" Sprint Progress:");
1293
+ lines.push(" " + "─".repeat(68));
1294
+ lines.push(` ${"STORY".padEnd(10)} ${"PHASE".padEnd(24)} ${"CYCLES".padEnd(8)} ELAPSED`);
1295
+ lines.push(" " + "─".repeat(68));
1296
+ for (const [key, detail] of Object.entries(status.stories.details)) {
1297
+ const elapsed = detail.elapsed_seconds > 0 ? `${detail.elapsed_seconds}s` : "-";
1298
+ lines.push(` ${key.padEnd(10)} ${detail.phase.padEnd(24)} ${String(detail.review_cycles).padEnd(8)} ${elapsed}`);
1299
+ }
1300
+ lines.push(" " + "─".repeat(68));
1301
+ lines.push(` Completed: ${status.stories.completed} In Progress: ${status.stories.in_progress} Escalated: ${status.stories.escalated} Pending: ${status.stories.pending}`);
1302
+ }
1262
1303
  return lines.join("\n");
1263
1304
  }
1264
1305
  /**
@@ -2496,7 +2537,7 @@ function truncateToTokens(text, maxTokens) {
2496
2537
 
2497
2538
  //#endregion
2498
2539
  //#region src/modules/context-compiler/context-compiler-impl.ts
2499
- const logger$15 = createLogger("context-compiler");
2540
+ const logger$17 = createLogger("context-compiler");
2500
2541
  /**
2501
2542
  * Fraction of the original token budget that must remain (after required +
2502
2543
  * important sections) before an optional section is included.
@@ -2588,7 +2629,7 @@ var ContextCompilerImpl = class {
2588
2629
  includedParts.push(truncated);
2589
2630
  remainingBudget -= truncatedTokens;
2590
2631
  anyTruncated = true;
2591
- logger$15.warn({
2632
+ logger$17.warn({
2592
2633
  section: section.name,
2593
2634
  originalTokens: tokens,
2594
2635
  budgetTokens: truncatedTokens
@@ -2602,7 +2643,7 @@ var ContextCompilerImpl = class {
2602
2643
  });
2603
2644
  } else {
2604
2645
  anyTruncated = true;
2605
- logger$15.warn({
2646
+ logger$17.warn({
2606
2647
  section: section.name,
2607
2648
  tokens
2608
2649
  }, "Context compiler: omitted \"important\" section — no budget remaining");
@@ -2629,7 +2670,7 @@ var ContextCompilerImpl = class {
2629
2670
  } else {
2630
2671
  if (tokens > 0) {
2631
2672
  anyTruncated = true;
2632
- logger$15.warn({
2673
+ logger$17.warn({
2633
2674
  section: section.name,
2634
2675
  tokens,
2635
2676
  budgetFractionRemaining: budgetFractionRemaining.toFixed(2)
@@ -2914,7 +2955,7 @@ function parseYamlResult(yamlText, schema) {
2914
2955
 
2915
2956
  //#endregion
2916
2957
  //#region src/modules/agent-dispatch/dispatcher-impl.ts
2917
- const logger$14 = createLogger("agent-dispatch");
2958
+ const logger$16 = createLogger("agent-dispatch");
2918
2959
  const SHUTDOWN_GRACE_MS = 1e4;
2919
2960
  const SHUTDOWN_MAX_WAIT_MS = 3e4;
2920
2961
  const CHARS_PER_TOKEN = 4;
@@ -2957,7 +2998,7 @@ function getAvailableMemory() {
2957
2998
  encoding: "utf-8"
2958
2999
  }).trim(), 10);
2959
3000
  if (pressureLevel >= 4) {
2960
- logger$14.warn({ pressureLevel }, "macOS kernel reports critical memory pressure");
3001
+ logger$16.warn({ pressureLevel }, "macOS kernel reports critical memory pressure");
2961
3002
  return 0;
2962
3003
  }
2963
3004
  } catch {}
@@ -2972,7 +3013,7 @@ function getAvailableMemory() {
2972
3013
  const speculative = parseInt(vmstat.match(/Pages speculative:\s+(\d+)/)?.[1] ?? "0", 10);
2973
3014
  const available = (free + purgeable + speculative) * pageSize;
2974
3015
  if (pressureLevel >= 2) {
2975
- logger$14.warn({
3016
+ logger$16.warn({
2976
3017
  pressureLevel,
2977
3018
  availableBeforeDiscount: available
2978
3019
  }, "macOS kernel reports memory pressure — discounting estimate");
@@ -3051,7 +3092,7 @@ var DispatcherImpl = class {
3051
3092
  resolve: typedResolve,
3052
3093
  reject
3053
3094
  });
3054
- logger$14.debug({
3095
+ logger$16.debug({
3055
3096
  id,
3056
3097
  queueLength: this._queue.length
3057
3098
  }, "Dispatch queued");
@@ -3082,7 +3123,7 @@ var DispatcherImpl = class {
3082
3123
  async shutdown() {
3083
3124
  this._shuttingDown = true;
3084
3125
  this._stopMemoryPressureTimer();
3085
- logger$14.info({
3126
+ logger$16.info({
3086
3127
  running: this._running.size,
3087
3128
  queued: this._queue.length
3088
3129
  }, "Dispatcher shutting down");
@@ -3115,13 +3156,13 @@ var DispatcherImpl = class {
3115
3156
  }
3116
3157
  }, 50);
3117
3158
  });
3118
- logger$14.info("Dispatcher shutdown complete");
3159
+ logger$16.info("Dispatcher shutdown complete");
3119
3160
  }
3120
3161
  async _startDispatch(id, request, resolve$2) {
3121
3162
  const { prompt, agent, taskType, timeout, outputSchema, workingDirectory, model, maxTurns } = request;
3122
3163
  const adapter = this._adapterRegistry.get(agent);
3123
3164
  if (adapter === void 0) {
3124
- logger$14.warn({
3165
+ logger$16.warn({
3125
3166
  id,
3126
3167
  agent
3127
3168
  }, "No adapter found for agent");
@@ -3167,7 +3208,7 @@ var DispatcherImpl = class {
3167
3208
  });
3168
3209
  const startedAt = Date.now();
3169
3210
  proc.on("error", (err) => {
3170
- logger$14.error({
3211
+ logger$16.error({
3171
3212
  id,
3172
3213
  binary: cmd.binary,
3173
3214
  error: err.message
@@ -3175,7 +3216,7 @@ var DispatcherImpl = class {
3175
3216
  });
3176
3217
  if (proc.stdin !== null) {
3177
3218
  proc.stdin.on("error", (err) => {
3178
- if (err.code !== "EPIPE") logger$14.warn({
3219
+ if (err.code !== "EPIPE") logger$16.warn({
3179
3220
  id,
3180
3221
  error: err.message
3181
3222
  }, "stdin write error");
@@ -3217,7 +3258,7 @@ var DispatcherImpl = class {
3217
3258
  agent,
3218
3259
  taskType
3219
3260
  });
3220
- logger$14.debug({
3261
+ logger$16.debug({
3221
3262
  id,
3222
3263
  agent,
3223
3264
  taskType,
@@ -3234,7 +3275,7 @@ var DispatcherImpl = class {
3234
3275
  dispatchId: id,
3235
3276
  timeoutMs
3236
3277
  });
3237
- logger$14.warn({
3278
+ logger$16.warn({
3238
3279
  id,
3239
3280
  agent,
3240
3281
  taskType,
@@ -3288,7 +3329,7 @@ var DispatcherImpl = class {
3288
3329
  exitCode: code,
3289
3330
  output: stdout
3290
3331
  });
3291
- logger$14.debug({
3332
+ logger$16.debug({
3292
3333
  id,
3293
3334
  agent,
3294
3335
  taskType,
@@ -3314,7 +3355,7 @@ var DispatcherImpl = class {
3314
3355
  error: stderr || `Process exited with code ${String(code)}`,
3315
3356
  exitCode: code
3316
3357
  });
3317
- logger$14.debug({
3358
+ logger$16.debug({
3318
3359
  id,
3319
3360
  agent,
3320
3361
  taskType,
@@ -3373,7 +3414,7 @@ var DispatcherImpl = class {
3373
3414
  const next = this._queue.shift();
3374
3415
  if (next === void 0) return;
3375
3416
  next.handle.status = "running";
3376
- logger$14.debug({
3417
+ logger$16.debug({
3377
3418
  id: next.id,
3378
3419
  queueLength: this._queue.length
3379
3420
  }, "Dequeued dispatch");
@@ -3386,7 +3427,7 @@ var DispatcherImpl = class {
3386
3427
  _isMemoryPressured() {
3387
3428
  const free = getAvailableMemory();
3388
3429
  if (free < MIN_FREE_MEMORY_BYTES) {
3389
- logger$14.warn({
3430
+ logger$16.warn({
3390
3431
  freeMB: Math.round(free / 1024 / 1024),
3391
3432
  thresholdMB: Math.round(MIN_FREE_MEMORY_BYTES / 1024 / 1024)
3392
3433
  }, "Memory pressure detected — holding dispatch queue");
@@ -3511,7 +3552,7 @@ function pickRecommendation(distribution, profile, totalIssues, reviewCycles, la
3511
3552
 
3512
3553
  //#endregion
3513
3554
  //#region src/modules/compiled-workflows/prompt-assembler.ts
3514
- const logger$13 = createLogger("compiled-workflows:prompt-assembler");
3555
+ const logger$15 = createLogger("compiled-workflows:prompt-assembler");
3515
3556
  /**
3516
3557
  * Assemble a final prompt from a template and sections map.
3517
3558
  *
@@ -3536,7 +3577,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
3536
3577
  tokenCount,
3537
3578
  truncated: false
3538
3579
  };
3539
- logger$13.warn({
3580
+ logger$15.warn({
3540
3581
  tokenCount,
3541
3582
  ceiling: tokenCeiling
3542
3583
  }, "Prompt exceeds token ceiling — truncating optional sections");
@@ -3552,10 +3593,10 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
3552
3593
  const targetSectionTokens = Math.max(0, currentSectionTokens - overBy);
3553
3594
  if (targetSectionTokens === 0) {
3554
3595
  contentMap[section.name] = "";
3555
- logger$13.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
3596
+ logger$15.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
3556
3597
  } else {
3557
3598
  contentMap[section.name] = truncateToTokens(section.content, targetSectionTokens);
3558
- logger$13.warn({
3599
+ logger$15.warn({
3559
3600
  sectionName: section.name,
3560
3601
  targetSectionTokens
3561
3602
  }, "Section truncated to fit token budget");
@@ -3566,7 +3607,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
3566
3607
  }
3567
3608
  if (tokenCount <= tokenCeiling) break;
3568
3609
  }
3569
- if (tokenCount > tokenCeiling) logger$13.warn({
3610
+ if (tokenCount > tokenCeiling) logger$15.warn({
3570
3611
  tokenCount,
3571
3612
  ceiling: tokenCeiling
3572
3613
  }, "Required sections alone exceed token ceiling — returning over-budget prompt");
@@ -3704,14 +3745,89 @@ const CodeReviewResultSchema = z.object({
3704
3745
  agentVerdict: data.verdict,
3705
3746
  verdict: computeVerdict(data.issue_list)
3706
3747
  }));
3748
+ /**
3749
+ * Schema for the YAML output contract of the test-plan sub-agent.
3750
+ *
3751
+ * The agent must emit YAML with result, test_files, test_categories, and coverage_notes.
3752
+ * Example:
3753
+ * result: success
3754
+ * test_files:
3755
+ * - src/modules/foo/__tests__/foo.test.ts
3756
+ * test_categories:
3757
+ * - unit
3758
+ * - integration
3759
+ * coverage_notes: "AC1 covered by foo.test.ts"
3760
+ */
3761
+ const TestPlanResultSchema = z.object({
3762
+ result: z.preprocess((val) => val === "failure" ? "failed" : val, z.enum(["success", "failed"])),
3763
+ test_files: z.array(z.string()).default([]),
3764
+ test_categories: z.array(z.string()).default([]),
3765
+ coverage_notes: z.string().default("")
3766
+ });
3767
+ /**
3768
+ * Schema for a single coverage gap identified during test expansion analysis.
3769
+ */
3770
+ const CoverageGapSchema = z.object({
3771
+ ac_ref: z.string(),
3772
+ description: z.string(),
3773
+ gap_type: z.enum([
3774
+ "missing-e2e",
3775
+ "missing-integration",
3776
+ "unit-only"
3777
+ ])
3778
+ });
3779
+ /**
3780
+ * Schema for a single suggested test generated during test expansion analysis.
3781
+ */
3782
+ const SuggestedTestSchema = z.object({
3783
+ test_name: z.string(),
3784
+ test_type: z.enum([
3785
+ "e2e",
3786
+ "integration",
3787
+ "unit"
3788
+ ]),
3789
+ description: z.string(),
3790
+ target_ac: z.string().optional()
3791
+ });
3792
+ /**
3793
+ * Schema for the YAML output contract of the test-expansion sub-agent.
3794
+ *
3795
+ * The agent must emit YAML with expansion_priority, coverage_gaps, and suggested_tests.
3796
+ * Example:
3797
+ * expansion_priority: medium
3798
+ * coverage_gaps:
3799
+ * - ac_ref: AC1
3800
+ * description: "Happy path not exercised at module boundary"
3801
+ * gap_type: missing-integration
3802
+ * suggested_tests:
3803
+ * - test_name: "runFoo integration happy path"
3804
+ * test_type: integration
3805
+ * description: "Test runFoo with real DB to verify AC1 end-to-end"
3806
+ * target_ac: AC1
3807
+ * notes: "Unit coverage is solid but integration layer is untested."
3808
+ */
3809
+ const TestExpansionResultSchema = z.object({
3810
+ expansion_priority: z.preprocess((val) => [
3811
+ "low",
3812
+ "medium",
3813
+ "high"
3814
+ ].includes(val) ? val : "low", z.enum([
3815
+ "low",
3816
+ "medium",
3817
+ "high"
3818
+ ])),
3819
+ coverage_gaps: z.array(CoverageGapSchema).default([]),
3820
+ suggested_tests: z.array(SuggestedTestSchema).default([]),
3821
+ notes: z.string().optional()
3822
+ });
3707
3823
 
3708
3824
  //#endregion
3709
3825
  //#region src/modules/compiled-workflows/create-story.ts
3710
- const logger$12 = createLogger("compiled-workflows:create-story");
3826
+ const logger$14 = createLogger("compiled-workflows:create-story");
3711
3827
  /**
3712
3828
  * Hard ceiling for the assembled create-story prompt.
3713
3829
  */
3714
- const TOKEN_CEILING$2 = 3e3;
3830
+ const TOKEN_CEILING$4 = 3e3;
3715
3831
  /**
3716
3832
  * Execute the compiled create-story workflow.
3717
3833
  *
@@ -3731,7 +3847,7 @@ const TOKEN_CEILING$2 = 3e3;
3731
3847
  */
3732
3848
  async function runCreateStory(deps, params) {
3733
3849
  const { epicId, storyKey, pipelineRunId } = params;
3734
- logger$12.debug({
3850
+ logger$14.debug({
3735
3851
  epicId,
3736
3852
  storyKey,
3737
3853
  pipelineRunId
@@ -3741,7 +3857,7 @@ async function runCreateStory(deps, params) {
3741
3857
  template = await deps.pack.getPrompt("create-story");
3742
3858
  } catch (err) {
3743
3859
  const error = err instanceof Error ? err.message : String(err);
3744
- logger$12.error({ error }, "Failed to retrieve create-story prompt template");
3860
+ logger$14.error({ error }, "Failed to retrieve create-story prompt template");
3745
3861
  return {
3746
3862
  result: "failed",
3747
3863
  error: `Failed to retrieve prompt template: ${error}`,
@@ -3754,7 +3870,7 @@ async function runCreateStory(deps, params) {
3754
3870
  const implementationDecisions = getImplementationDecisions(deps);
3755
3871
  const epicShardContent = getEpicShard(implementationDecisions, epicId, deps.projectRoot);
3756
3872
  const prevDevNotesContent = getPrevDevNotes(implementationDecisions, epicId);
3757
- const archConstraintsContent = getArchConstraints$1(deps);
3873
+ const archConstraintsContent = getArchConstraints$2(deps);
3758
3874
  const storyTemplateContent = await getStoryTemplate(deps);
3759
3875
  const { prompt, tokenCount, truncated } = assemblePrompt(template, [
3760
3876
  {
@@ -3782,11 +3898,11 @@ async function runCreateStory(deps, params) {
3782
3898
  content: storyTemplateContent,
3783
3899
  priority: "important"
3784
3900
  }
3785
- ], TOKEN_CEILING$2);
3786
- logger$12.debug({
3901
+ ], TOKEN_CEILING$4);
3902
+ logger$14.debug({
3787
3903
  tokenCount,
3788
3904
  truncated,
3789
- tokenCeiling: TOKEN_CEILING$2
3905
+ tokenCeiling: TOKEN_CEILING$4
3790
3906
  }, "Prompt assembled for create-story");
3791
3907
  const handle = deps.dispatcher.dispatch({
3792
3908
  prompt,
@@ -3800,7 +3916,7 @@ async function runCreateStory(deps, params) {
3800
3916
  dispatchResult = await handle.result;
3801
3917
  } catch (err) {
3802
3918
  const error = err instanceof Error ? err.message : String(err);
3803
- logger$12.error({
3919
+ logger$14.error({
3804
3920
  epicId,
3805
3921
  storyKey,
3806
3922
  error
@@ -3821,7 +3937,7 @@ async function runCreateStory(deps, params) {
3821
3937
  if (dispatchResult.status === "failed") {
3822
3938
  const errorMsg = dispatchResult.parseError ?? `Dispatch failed with exit code ${dispatchResult.exitCode}`;
3823
3939
  const stderrDetail = dispatchResult.output ? ` Output: ${dispatchResult.output}` : "";
3824
- logger$12.warn({
3940
+ logger$14.warn({
3825
3941
  epicId,
3826
3942
  storyKey,
3827
3943
  exitCode: dispatchResult.exitCode
@@ -3833,7 +3949,7 @@ async function runCreateStory(deps, params) {
3833
3949
  };
3834
3950
  }
3835
3951
  if (dispatchResult.status === "timeout") {
3836
- logger$12.warn({
3952
+ logger$14.warn({
3837
3953
  epicId,
3838
3954
  storyKey
3839
3955
  }, "Create-story dispatch timed out");
@@ -3846,7 +3962,7 @@ async function runCreateStory(deps, params) {
3846
3962
  if (dispatchResult.parsed === null) {
3847
3963
  const details = dispatchResult.parseError ?? "No YAML block found in output";
3848
3964
  const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
3849
- logger$12.warn({
3965
+ logger$14.warn({
3850
3966
  epicId,
3851
3967
  storyKey,
3852
3968
  details,
@@ -3862,7 +3978,7 @@ async function runCreateStory(deps, params) {
3862
3978
  const parseResult = CreateStoryResultSchema.safeParse(dispatchResult.parsed);
3863
3979
  if (!parseResult.success) {
3864
3980
  const details = parseResult.error.message;
3865
- logger$12.warn({
3981
+ logger$14.warn({
3866
3982
  epicId,
3867
3983
  storyKey,
3868
3984
  details
@@ -3875,7 +3991,7 @@ async function runCreateStory(deps, params) {
3875
3991
  };
3876
3992
  }
3877
3993
  const parsed = parseResult.data;
3878
- logger$12.info({
3994
+ logger$14.info({
3879
3995
  epicId,
3880
3996
  storyKey,
3881
3997
  storyFile: parsed.story_file,
@@ -3897,7 +4013,7 @@ function getImplementationDecisions(deps) {
3897
4013
  try {
3898
4014
  return getDecisionsByPhase(deps.db, "implementation");
3899
4015
  } catch (err) {
3900
- logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
4016
+ logger$14.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
3901
4017
  return [];
3902
4018
  }
3903
4019
  }
@@ -3913,13 +4029,13 @@ function getEpicShard(decisions, epicId, projectRoot) {
3913
4029
  if (projectRoot) {
3914
4030
  const fallback = readEpicShardFromFile(projectRoot, epicId);
3915
4031
  if (fallback) {
3916
- logger$12.info({ epicId }, "Using file-based fallback for epic shard (decisions table empty)");
4032
+ logger$14.info({ epicId }, "Using file-based fallback for epic shard (decisions table empty)");
3917
4033
  return fallback;
3918
4034
  }
3919
4035
  }
3920
4036
  return "";
3921
4037
  } catch (err) {
3922
- logger$12.warn({
4038
+ logger$14.warn({
3923
4039
  epicId,
3924
4040
  error: err instanceof Error ? err.message : String(err)
3925
4041
  }, "Failed to retrieve epic shard");
@@ -3936,7 +4052,7 @@ function getPrevDevNotes(decisions, epicId) {
3936
4052
  if (devNotes.length === 0) return "";
3937
4053
  return devNotes[devNotes.length - 1].value;
3938
4054
  } catch (err) {
3939
- logger$12.warn({
4055
+ logger$14.warn({
3940
4056
  epicId,
3941
4057
  error: err instanceof Error ? err.message : String(err)
3942
4058
  }, "Failed to retrieve prev dev notes");
@@ -3948,7 +4064,7 @@ function getPrevDevNotes(decisions, epicId) {
3948
4064
  * Looks for decisions with phase='solutioning', category='architecture'.
3949
4065
  * Falls back to reading _bmad-output/architecture/architecture.md on disk if decisions are empty.
3950
4066
  */
3951
- function getArchConstraints$1(deps) {
4067
+ function getArchConstraints$2(deps) {
3952
4068
  try {
3953
4069
  const decisions = getDecisionsByPhase(deps.db, "solutioning");
3954
4070
  const constraints = decisions.filter((d) => d.category === "architecture");
@@ -3956,13 +4072,13 @@ function getArchConstraints$1(deps) {
3956
4072
  if (deps.projectRoot) {
3957
4073
  const fallback = readArchConstraintsFromFile(deps.projectRoot);
3958
4074
  if (fallback) {
3959
- logger$12.info("Using file-based fallback for architecture constraints (decisions table empty)");
4075
+ logger$14.info("Using file-based fallback for architecture constraints (decisions table empty)");
3960
4076
  return fallback;
3961
4077
  }
3962
4078
  }
3963
4079
  return "";
3964
4080
  } catch (err) {
3965
- logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
4081
+ logger$14.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
3966
4082
  return "";
3967
4083
  }
3968
4084
  }
@@ -3982,7 +4098,7 @@ function readEpicShardFromFile(projectRoot, epicId) {
3982
4098
  const match = pattern.exec(content);
3983
4099
  return match ? match[0].trim() : "";
3984
4100
  } catch (err) {
3985
- logger$12.warn({
4101
+ logger$14.warn({
3986
4102
  epicId,
3987
4103
  error: err instanceof Error ? err.message : String(err)
3988
4104
  }, "File-based epic shard fallback failed");
@@ -4005,7 +4121,7 @@ function readArchConstraintsFromFile(projectRoot) {
4005
4121
  const content = readFileSync$1(archPath, "utf-8");
4006
4122
  return content.slice(0, 1500);
4007
4123
  } catch (err) {
4008
- logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
4124
+ logger$14.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
4009
4125
  return "";
4010
4126
  }
4011
4127
  }
@@ -4018,14 +4134,14 @@ async function getStoryTemplate(deps) {
4018
4134
  try {
4019
4135
  return await deps.pack.getTemplate("story");
4020
4136
  } catch (err) {
4021
- logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
4137
+ logger$14.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
4022
4138
  return "";
4023
4139
  }
4024
4140
  }
4025
4141
 
4026
4142
  //#endregion
4027
4143
  //#region src/modules/compiled-workflows/git-helpers.ts
4028
- const logger$11 = createLogger("compiled-workflows:git-helpers");
4144
+ const logger$13 = createLogger("compiled-workflows:git-helpers");
4029
4145
  /**
4030
4146
  * Capture the full git diff for HEAD (working tree vs current commit).
4031
4147
  *
@@ -4149,7 +4265,7 @@ async function runGitCommand(args, cwd, logLabel) {
4149
4265
  stderr += chunk.toString("utf-8");
4150
4266
  });
4151
4267
  proc.on("error", (err) => {
4152
- logger$11.warn({
4268
+ logger$13.warn({
4153
4269
  label: logLabel,
4154
4270
  cwd,
4155
4271
  error: err.message
@@ -4158,7 +4274,7 @@ async function runGitCommand(args, cwd, logLabel) {
4158
4274
  });
4159
4275
  proc.on("close", (code) => {
4160
4276
  if (code !== 0) {
4161
- logger$11.warn({
4277
+ logger$13.warn({
4162
4278
  label: logLabel,
4163
4279
  cwd,
4164
4280
  code,
@@ -4174,7 +4290,7 @@ async function runGitCommand(args, cwd, logLabel) {
4174
4290
 
4175
4291
  //#endregion
4176
4292
  //#region src/modules/implementation-orchestrator/project-findings.ts
4177
- const logger$10 = createLogger("project-findings");
4293
+ const logger$12 = createLogger("project-findings");
4178
4294
  /** Maximum character length for the findings summary */
4179
4295
  const MAX_CHARS = 2e3;
4180
4296
  /**
@@ -4229,7 +4345,7 @@ function getProjectFindings(db) {
4229
4345
  if (summary.length > MAX_CHARS) summary = summary.slice(0, MAX_CHARS - 3) + "...";
4230
4346
  return summary;
4231
4347
  } catch (err) {
4232
- logger$10.warn({ err }, "Failed to query project findings (graceful fallback)");
4348
+ logger$12.warn({ err }, "Failed to query project findings (graceful fallback)");
4233
4349
  return "";
4234
4350
  }
4235
4351
  }
@@ -4252,11 +4368,11 @@ function extractRecurringPatterns(outcomes) {
4252
4368
 
4253
4369
  //#endregion
4254
4370
  //#region src/modules/compiled-workflows/dev-story.ts
4255
- const logger$9 = createLogger("compiled-workflows:dev-story");
4371
+ const logger$11 = createLogger("compiled-workflows:dev-story");
4256
4372
  /** Hard token ceiling for the assembled dev-story prompt */
4257
- const TOKEN_CEILING$1 = 24e3;
4373
+ const TOKEN_CEILING$3 = 24e3;
4258
4374
  /** Default timeout for dev-story dispatches in milliseconds (30 min) */
4259
- const DEFAULT_TIMEOUT_MS = 18e5;
4375
+ const DEFAULT_TIMEOUT_MS$1 = 18e5;
4260
4376
  /** Default Vitest test patterns injected when no test-pattern decisions exist */
4261
4377
  const DEFAULT_VITEST_PATTERNS = `## Test Patterns (defaults)
4262
4378
  - Framework: Vitest (NOT jest — --testPathPattern flag does not work, use -- "pattern")
@@ -4277,7 +4393,7 @@ const DEFAULT_VITEST_PATTERNS = `## Test Patterns (defaults)
4277
4393
  */
4278
4394
  async function runDevStory(deps, params) {
4279
4395
  const { storyKey, storyFilePath, taskScope, priorFiles } = params;
4280
- logger$9.info({
4396
+ logger$11.info({
4281
4397
  storyKey,
4282
4398
  storyFilePath
4283
4399
  }, "Starting compiled dev-story workflow");
@@ -4319,10 +4435,10 @@ async function runDevStory(deps, params) {
4319
4435
  let template;
4320
4436
  try {
4321
4437
  template = await deps.pack.getPrompt("dev-story");
4322
- logger$9.debug({ storyKey }, "Retrieved dev-story prompt template from pack");
4438
+ logger$11.debug({ storyKey }, "Retrieved dev-story prompt template from pack");
4323
4439
  } catch (err) {
4324
4440
  const error = err instanceof Error ? err.message : String(err);
4325
- logger$9.error({
4441
+ logger$11.error({
4326
4442
  storyKey,
4327
4443
  error
4328
4444
  }, "Failed to retrieve dev-story prompt template");
@@ -4333,14 +4449,14 @@ async function runDevStory(deps, params) {
4333
4449
  storyContent = await readFile$1(storyFilePath, "utf-8");
4334
4450
  } catch (err) {
4335
4451
  if (err.code === "ENOENT") {
4336
- logger$9.error({
4452
+ logger$11.error({
4337
4453
  storyKey,
4338
4454
  storyFilePath
4339
4455
  }, "Story file not found");
4340
4456
  return makeFailureResult("story_file_not_found");
4341
4457
  }
4342
4458
  const error = err instanceof Error ? err.message : String(err);
4343
- logger$9.error({
4459
+ logger$11.error({
4344
4460
  storyKey,
4345
4461
  storyFilePath,
4346
4462
  error
@@ -4348,7 +4464,7 @@ async function runDevStory(deps, params) {
4348
4464
  return makeFailureResult(`story_file_read_error: ${error}`);
4349
4465
  }
4350
4466
  if (storyContent.trim().length === 0) {
4351
- logger$9.error({
4467
+ logger$11.error({
4352
4468
  storyKey,
4353
4469
  storyFilePath
4354
4470
  }, "Story file is empty");
@@ -4360,17 +4476,17 @@ async function runDevStory(deps, params) {
4360
4476
  const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
4361
4477
  if (testPatternDecisions.length > 0) {
4362
4478
  testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
4363
- logger$9.debug({
4479
+ logger$11.debug({
4364
4480
  storyKey,
4365
4481
  count: testPatternDecisions.length
4366
4482
  }, "Loaded test patterns from decision store");
4367
4483
  } else {
4368
4484
  testPatternsContent = DEFAULT_VITEST_PATTERNS;
4369
- logger$9.debug({ storyKey }, "No test-pattern decisions found — using default Vitest patterns");
4485
+ logger$11.debug({ storyKey }, "No test-pattern decisions found — using default Vitest patterns");
4370
4486
  }
4371
4487
  } catch (err) {
4372
4488
  const error = err instanceof Error ? err.message : String(err);
4373
- logger$9.warn({
4489
+ logger$11.warn({
4374
4490
  storyKey,
4375
4491
  error
4376
4492
  }, "Failed to load test patterns — using defaults");
@@ -4385,12 +4501,29 @@ async function runDevStory(deps, params) {
4385
4501
  const findings = getProjectFindings(deps.db);
4386
4502
  if (findings.length > 0) {
4387
4503
  priorFindingsContent = "Previous pipeline runs encountered these issues — avoid repeating them:\n\n" + findings;
4388
- logger$9.debug({
4504
+ logger$11.debug({
4389
4505
  storyKey,
4390
4506
  findingsLen: findings.length
4391
4507
  }, "Injecting prior findings into dev-story prompt");
4392
4508
  }
4393
4509
  } catch {}
4510
+ let testPlanContent = "";
4511
+ try {
4512
+ const testPlanDecisions = getDecisionsByCategory(deps.db, "test-plan");
4513
+ const matchingPlan = testPlanDecisions.find((d) => d.key === storyKey);
4514
+ if (matchingPlan) {
4515
+ const plan = JSON.parse(matchingPlan.value);
4516
+ const parts = ["## Test Plan"];
4517
+ if (plan.test_files && plan.test_files.length > 0) {
4518
+ parts.push("\n### Test Files");
4519
+ for (const f of plan.test_files) parts.push(`- ${f}`);
4520
+ }
4521
+ if (plan.test_categories && plan.test_categories.length > 0) parts.push(`\n### Categories: ${plan.test_categories.join(", ")}`);
4522
+ if (plan.coverage_notes) parts.push(`\n### Coverage Notes\n${plan.coverage_notes}`);
4523
+ testPlanContent = parts.join("\n");
4524
+ logger$11.debug({ storyKey }, "Injecting test plan into dev-story prompt");
4525
+ }
4526
+ } catch {}
4394
4527
  const sections = [
4395
4528
  {
4396
4529
  name: "story_content",
@@ -4422,17 +4555,22 @@ async function runDevStory(deps, params) {
4422
4555
  content: testPatternsContent,
4423
4556
  priority: "optional"
4424
4557
  },
4558
+ {
4559
+ name: "test_plan",
4560
+ content: testPlanContent,
4561
+ priority: "optional"
4562
+ },
4425
4563
  {
4426
4564
  name: "prior_findings",
4427
4565
  content: priorFindingsContent,
4428
4566
  priority: "optional"
4429
4567
  }
4430
4568
  ];
4431
- const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING$1);
4432
- logger$9.info({
4569
+ const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING$3);
4570
+ logger$11.info({
4433
4571
  storyKey,
4434
4572
  tokenCount,
4435
- ceiling: TOKEN_CEILING$1,
4573
+ ceiling: TOKEN_CEILING$3,
4436
4574
  truncated
4437
4575
  }, "Assembled dev-story prompt");
4438
4576
  let dispatchResult;
@@ -4441,14 +4579,14 @@ async function runDevStory(deps, params) {
4441
4579
  prompt,
4442
4580
  agent: "claude-code",
4443
4581
  taskType: "dev-story",
4444
- timeout: DEFAULT_TIMEOUT_MS,
4582
+ timeout: DEFAULT_TIMEOUT_MS$1,
4445
4583
  outputSchema: DevStoryResultSchema,
4446
4584
  ...deps.projectRoot !== void 0 ? { workingDirectory: deps.projectRoot } : {}
4447
4585
  });
4448
4586
  dispatchResult = await handle.result;
4449
4587
  } catch (err) {
4450
4588
  const error = err instanceof Error ? err.message : String(err);
4451
- logger$9.error({
4589
+ logger$11.error({
4452
4590
  storyKey,
4453
4591
  error
4454
4592
  }, "Dispatch threw an unexpected error");
@@ -4459,11 +4597,11 @@ async function runDevStory(deps, params) {
4459
4597
  output: dispatchResult.tokenEstimate.output
4460
4598
  };
4461
4599
  if (dispatchResult.status === "timeout") {
4462
- logger$9.error({
4600
+ logger$11.error({
4463
4601
  storyKey,
4464
4602
  durationMs: dispatchResult.durationMs
4465
4603
  }, "Dev-story dispatch timed out");
4466
- if (dispatchResult.output.length > 0) logger$9.info({
4604
+ if (dispatchResult.output.length > 0) logger$11.info({
4467
4605
  storyKey,
4468
4606
  partialOutput: dispatchResult.output.slice(0, 500)
4469
4607
  }, "Partial output before timeout");
@@ -4473,12 +4611,12 @@ async function runDevStory(deps, params) {
4473
4611
  };
4474
4612
  }
4475
4613
  if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
4476
- logger$9.error({
4614
+ logger$11.error({
4477
4615
  storyKey,
4478
4616
  exitCode: dispatchResult.exitCode,
4479
4617
  status: dispatchResult.status
4480
4618
  }, "Dev-story dispatch failed");
4481
- if (dispatchResult.output.length > 0) logger$9.info({
4619
+ if (dispatchResult.output.length > 0) logger$11.info({
4482
4620
  storyKey,
4483
4621
  partialOutput: dispatchResult.output.slice(0, 500)
4484
4622
  }, "Partial output from failed dispatch");
@@ -4490,7 +4628,7 @@ async function runDevStory(deps, params) {
4490
4628
  if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
4491
4629
  const details = dispatchResult.parseError ?? "parsed result was null";
4492
4630
  const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
4493
- logger$9.error({
4631
+ logger$11.error({
4494
4632
  storyKey,
4495
4633
  parseError: details,
4496
4634
  rawOutputSnippet: rawSnippet
@@ -4498,12 +4636,12 @@ async function runDevStory(deps, params) {
4498
4636
  let filesModified = [];
4499
4637
  try {
4500
4638
  filesModified = await getGitChangedFiles(deps.projectRoot ?? process.cwd());
4501
- if (filesModified.length > 0) logger$9.info({
4639
+ if (filesModified.length > 0) logger$11.info({
4502
4640
  storyKey,
4503
4641
  fileCount: filesModified.length
4504
4642
  }, "Recovered files_modified from git status (YAML fallback)");
4505
4643
  } catch (err) {
4506
- logger$9.warn({
4644
+ logger$11.warn({
4507
4645
  storyKey,
4508
4646
  error: err instanceof Error ? err.message : String(err)
4509
4647
  }, "Failed to recover files_modified from git");
@@ -4520,7 +4658,7 @@ async function runDevStory(deps, params) {
4520
4658
  };
4521
4659
  }
4522
4660
  const parsed = dispatchResult.parsed;
4523
- logger$9.info({
4661
+ logger$11.info({
4524
4662
  storyKey,
4525
4663
  result: parsed.result,
4526
4664
  acMet: parsed.ac_met.length
@@ -4659,13 +4797,13 @@ function extractFilesInScope(storyContent) {
4659
4797
 
4660
4798
  //#endregion
4661
4799
  //#region src/modules/compiled-workflows/code-review.ts
4662
- const logger$8 = createLogger("compiled-workflows:code-review");
4800
+ const logger$10 = createLogger("compiled-workflows:code-review");
4663
4801
  /**
4664
4802
  * Hard token ceiling for the assembled code-review prompt (50,000 tokens).
4665
4803
  * Quality reviews require seeing actual code diffs, not just file names.
4666
4804
  * // TODO: consider externalizing to pack config when multiple packs exist
4667
4805
  */
4668
- const TOKEN_CEILING = 1e5;
4806
+ const TOKEN_CEILING$2 = 1e5;
4669
4807
  /**
4670
4808
  * Default fallback result when dispatch fails or times out.
4671
4809
  * Uses NEEDS_MINOR_FIXES (not NEEDS_MAJOR_REWORK) so a parse/schema failure
@@ -4702,7 +4840,7 @@ function defaultFailResult(error, tokenUsage) {
4702
4840
  async function runCodeReview(deps, params) {
4703
4841
  const { storyKey, storyFilePath, workingDirectory, pipelineRunId, filesModified, previousIssues } = params;
4704
4842
  const cwd = workingDirectory ?? process.cwd();
4705
- logger$8.debug({
4843
+ logger$10.debug({
4706
4844
  storyKey,
4707
4845
  storyFilePath,
4708
4846
  cwd,
@@ -4713,7 +4851,7 @@ async function runCodeReview(deps, params) {
4713
4851
  template = await deps.pack.getPrompt("code-review");
4714
4852
  } catch (err) {
4715
4853
  const error = err instanceof Error ? err.message : String(err);
4716
- logger$8.error({ error }, "Failed to retrieve code-review prompt template");
4854
+ logger$10.error({ error }, "Failed to retrieve code-review prompt template");
4717
4855
  return defaultFailResult(`Failed to retrieve prompt template: ${error}`, {
4718
4856
  input: 0,
4719
4857
  output: 0
@@ -4724,7 +4862,7 @@ async function runCodeReview(deps, params) {
4724
4862
  storyContent = await readFile$1(storyFilePath, "utf-8");
4725
4863
  } catch (err) {
4726
4864
  const error = err instanceof Error ? err.message : String(err);
4727
- logger$8.error({
4865
+ logger$10.error({
4728
4866
  storyFilePath,
4729
4867
  error
4730
4868
  }, "Failed to read story file");
@@ -4733,7 +4871,7 @@ async function runCodeReview(deps, params) {
4733
4871
  output: 0
4734
4872
  });
4735
4873
  }
4736
- const archConstraintsContent = getArchConstraints(deps);
4874
+ const archConstraintsContent = getArchConstraints$1(deps);
4737
4875
  const templateTokens = countTokens(template);
4738
4876
  const storyTokens = countTokens(storyContent);
4739
4877
  const constraintTokens = countTokens(archConstraintsContent);
@@ -4742,16 +4880,16 @@ async function runCodeReview(deps, params) {
4742
4880
  if (filesModified && filesModified.length > 0) {
4743
4881
  const scopedDiff = await getGitDiffForFiles(filesModified, cwd);
4744
4882
  const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
4745
- if (scopedTotal <= TOKEN_CEILING) {
4883
+ if (scopedTotal <= TOKEN_CEILING$2) {
4746
4884
  gitDiffContent = scopedDiff;
4747
- logger$8.debug({
4885
+ logger$10.debug({
4748
4886
  fileCount: filesModified.length,
4749
4887
  tokenCount: scopedTotal
4750
4888
  }, "Using scoped file diff");
4751
4889
  } else {
4752
- logger$8.warn({
4890
+ logger$10.warn({
4753
4891
  estimatedTotal: scopedTotal,
4754
- ceiling: TOKEN_CEILING,
4892
+ ceiling: TOKEN_CEILING$2,
4755
4893
  fileCount: filesModified.length
4756
4894
  }, "Scoped diff exceeds token ceiling — falling back to stat-only summary");
4757
4895
  gitDiffContent = await getGitDiffStatSummary(cwd);
@@ -4761,11 +4899,11 @@ async function runCodeReview(deps, params) {
4761
4899
  await stageIntentToAdd(changedFiles, cwd);
4762
4900
  const fullDiff = await getGitDiffSummary(cwd);
4763
4901
  const fullTotal = nonDiffTokens + countTokens(fullDiff);
4764
- if (fullTotal <= TOKEN_CEILING) gitDiffContent = fullDiff;
4902
+ if (fullTotal <= TOKEN_CEILING$2) gitDiffContent = fullDiff;
4765
4903
  else {
4766
- logger$8.warn({
4904
+ logger$10.warn({
4767
4905
  estimatedTotal: fullTotal,
4768
- ceiling: TOKEN_CEILING
4906
+ ceiling: TOKEN_CEILING$2
4769
4907
  }, "Full git diff would exceed token ceiling — using stat-only summary");
4770
4908
  gitDiffContent = await getGitDiffStatSummary(cwd);
4771
4909
  }
@@ -4783,7 +4921,7 @@ async function runCodeReview(deps, params) {
4783
4921
  const findings = getProjectFindings(deps.db);
4784
4922
  if (findings.length > 0) {
4785
4923
  priorFindingsContent = "Previous reviews found these recurring patterns — pay special attention:\n\n" + findings;
4786
- logger$8.debug({
4924
+ logger$10.debug({
4787
4925
  storyKey,
4788
4926
  findingsLen: findings.length
4789
4927
  }, "Injecting prior findings into code-review prompt");
@@ -4816,12 +4954,12 @@ async function runCodeReview(deps, params) {
4816
4954
  priority: "optional"
4817
4955
  }
4818
4956
  ];
4819
- const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
4820
- if (assembleResult.truncated) logger$8.warn({
4957
+ const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING$2);
4958
+ if (assembleResult.truncated) logger$10.warn({
4821
4959
  storyKey,
4822
4960
  tokenCount: assembleResult.tokenCount
4823
4961
  }, "Code-review prompt truncated to fit token ceiling");
4824
- logger$8.debug({
4962
+ logger$10.debug({
4825
4963
  storyKey,
4826
4964
  tokenCount: assembleResult.tokenCount,
4827
4965
  truncated: assembleResult.truncated
@@ -4839,7 +4977,7 @@ async function runCodeReview(deps, params) {
4839
4977
  dispatchResult = await handle.result;
4840
4978
  } catch (err) {
4841
4979
  const error = err instanceof Error ? err.message : String(err);
4842
- logger$8.error({
4980
+ logger$10.error({
4843
4981
  storyKey,
4844
4982
  error
4845
4983
  }, "Code-review dispatch threw unexpected error");
@@ -4855,7 +4993,7 @@ async function runCodeReview(deps, params) {
4855
4993
  const rawOutput = dispatchResult.output ?? void 0;
4856
4994
  if (dispatchResult.status === "failed") {
4857
4995
  const errorMsg = `Dispatch status: failed. Exit code: ${dispatchResult.exitCode}. ${dispatchResult.parseError ?? ""} ${dispatchResult.output ? `Stderr: ${dispatchResult.output}` : ""}`.trim();
4858
- logger$8.warn({
4996
+ logger$10.warn({
4859
4997
  storyKey,
4860
4998
  exitCode: dispatchResult.exitCode
4861
4999
  }, "Code-review dispatch failed");
@@ -4865,7 +5003,7 @@ async function runCodeReview(deps, params) {
4865
5003
  };
4866
5004
  }
4867
5005
  if (dispatchResult.status === "timeout") {
4868
- logger$8.warn({ storyKey }, "Code-review dispatch timed out");
5006
+ logger$10.warn({ storyKey }, "Code-review dispatch timed out");
4869
5007
  return {
4870
5008
  ...defaultFailResult("Dispatch status: timeout. The agent did not complete within the allowed time.", tokenUsage),
4871
5009
  rawOutput
@@ -4873,7 +5011,7 @@ async function runCodeReview(deps, params) {
4873
5011
  }
4874
5012
  if (dispatchResult.parsed === null) {
4875
5013
  const details = dispatchResult.parseError ?? "No YAML block found in output";
4876
- logger$8.warn({
5014
+ logger$10.warn({
4877
5015
  storyKey,
4878
5016
  details
4879
5017
  }, "Code-review output schema validation failed");
@@ -4890,7 +5028,7 @@ async function runCodeReview(deps, params) {
4890
5028
  const parseResult = CodeReviewResultSchema.safeParse(dispatchResult.parsed);
4891
5029
  if (!parseResult.success) {
4892
5030
  const details = parseResult.error.message;
4893
- logger$8.warn({
5031
+ logger$10.warn({
4894
5032
  storyKey,
4895
5033
  details
4896
5034
  }, "Code-review output failed schema validation");
@@ -4905,13 +5043,13 @@ async function runCodeReview(deps, params) {
4905
5043
  };
4906
5044
  }
4907
5045
  const parsed = parseResult.data;
4908
- if (parsed.agentVerdict !== parsed.verdict) logger$8.info({
5046
+ if (parsed.agentVerdict !== parsed.verdict) logger$10.info({
4909
5047
  storyKey,
4910
5048
  agentVerdict: parsed.agentVerdict,
4911
5049
  pipelineVerdict: parsed.verdict,
4912
5050
  issues: parsed.issues
4913
5051
  }, "Pipeline overrode agent verdict based on issue severities");
4914
- logger$8.info({
5052
+ logger$10.info({
4915
5053
  storyKey,
4916
5054
  verdict: parsed.verdict,
4917
5055
  issues: parsed.issues
@@ -4929,6 +5067,384 @@ async function runCodeReview(deps, params) {
4929
5067
  * Retrieve architecture constraints from the decision store.
4930
5068
  * Looks for decisions with phase='solutioning', category='architecture'.
4931
5069
  */
5070
+ function getArchConstraints$1(deps) {
5071
+ try {
5072
+ const decisions = getDecisionsByPhase(deps.db, "solutioning");
5073
+ const constraints = decisions.filter((d) => d.category === "architecture");
5074
+ if (constraints.length === 0) return "";
5075
+ return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
5076
+ } catch (err) {
5077
+ logger$10.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
5078
+ return "";
5079
+ }
5080
+ }
5081
+
5082
+ //#endregion
5083
+ //#region src/modules/compiled-workflows/test-plan.ts
5084
+ const logger$9 = createLogger("compiled-workflows:test-plan");
5085
+ /** Hard token ceiling for the assembled test-plan prompt */
5086
+ const TOKEN_CEILING$1 = 8e3;
5087
+ /** Default timeout for test-plan dispatches in milliseconds (5 min — lightweight call) */
5088
+ const DEFAULT_TIMEOUT_MS = 3e5;
5089
+ /**
5090
+ * Execute the compiled test-plan workflow.
5091
+ *
5092
+ * @param deps - Injected dependencies (db, pack, contextCompiler, dispatcher)
5093
+ * @param params - Parameters (storyKey, storyFilePath, pipelineRunId)
5094
+ * @returns TestPlanResult with result, test_files, test_categories, coverage_notes, tokenUsage
5095
+ */
5096
+ async function runTestPlan(deps, params) {
5097
+ const { storyKey, storyFilePath, pipelineRunId } = params;
5098
+ logger$9.info({
5099
+ storyKey,
5100
+ storyFilePath
5101
+ }, "Starting compiled test-plan workflow");
5102
+ let template;
5103
+ try {
5104
+ template = await deps.pack.getPrompt("test-plan");
5105
+ logger$9.debug({ storyKey }, "Retrieved test-plan prompt template from pack");
5106
+ } catch (err) {
5107
+ const error = err instanceof Error ? err.message : String(err);
5108
+ logger$9.warn({
5109
+ storyKey,
5110
+ error
5111
+ }, "Failed to retrieve test-plan prompt template");
5112
+ return makeTestPlanFailureResult(`template_load_failed: ${error}`);
5113
+ }
5114
+ let storyContent;
5115
+ try {
5116
+ storyContent = await readFile$1(storyFilePath, "utf-8");
5117
+ } catch (err) {
5118
+ if (err.code === "ENOENT") {
5119
+ logger$9.warn({
5120
+ storyKey,
5121
+ storyFilePath
5122
+ }, "Story file not found for test planning");
5123
+ return makeTestPlanFailureResult("story_file_not_found");
5124
+ }
5125
+ const error = err instanceof Error ? err.message : String(err);
5126
+ logger$9.warn({
5127
+ storyKey,
5128
+ storyFilePath,
5129
+ error
5130
+ }, "Failed to read story file for test planning");
5131
+ return makeTestPlanFailureResult(`story_file_read_error: ${error}`);
5132
+ }
5133
+ const { prompt, tokenCount, truncated } = assemblePrompt(template, [{
5134
+ name: "story_content",
5135
+ content: storyContent,
5136
+ priority: "required"
5137
+ }], TOKEN_CEILING$1);
5138
+ logger$9.info({
5139
+ storyKey,
5140
+ tokenCount,
5141
+ ceiling: TOKEN_CEILING$1,
5142
+ truncated
5143
+ }, "Assembled test-plan prompt");
5144
+ let dispatchResult;
5145
+ try {
5146
+ const handle = deps.dispatcher.dispatch({
5147
+ prompt,
5148
+ agent: "claude-code",
5149
+ taskType: "test-plan",
5150
+ timeout: DEFAULT_TIMEOUT_MS,
5151
+ outputSchema: TestPlanResultSchema,
5152
+ ...deps.projectRoot !== void 0 ? { workingDirectory: deps.projectRoot } : {}
5153
+ });
5154
+ dispatchResult = await handle.result;
5155
+ } catch (err) {
5156
+ const error = err instanceof Error ? err.message : String(err);
5157
+ logger$9.warn({
5158
+ storyKey,
5159
+ error
5160
+ }, "Test-plan dispatch threw an unexpected error");
5161
+ return makeTestPlanFailureResult(`dispatch_error: ${error}`);
5162
+ }
5163
+ const tokenUsage = {
5164
+ input: dispatchResult.tokenEstimate.input,
5165
+ output: dispatchResult.tokenEstimate.output
5166
+ };
5167
+ if (dispatchResult.status === "timeout") {
5168
+ logger$9.warn({
5169
+ storyKey,
5170
+ durationMs: dispatchResult.durationMs
5171
+ }, "Test-plan dispatch timed out");
5172
+ return {
5173
+ ...makeTestPlanFailureResult(`dispatch_timeout after ${dispatchResult.durationMs}ms`),
5174
+ tokenUsage
5175
+ };
5176
+ }
5177
+ if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
5178
+ logger$9.warn({
5179
+ storyKey,
5180
+ exitCode: dispatchResult.exitCode,
5181
+ status: dispatchResult.status
5182
+ }, "Test-plan dispatch failed");
5183
+ return {
5184
+ ...makeTestPlanFailureResult(`dispatch_failed with exit_code=${dispatchResult.exitCode}`),
5185
+ tokenUsage
5186
+ };
5187
+ }
5188
+ if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
5189
+ const details = dispatchResult.parseError ?? "parsed result was null";
5190
+ logger$9.warn({
5191
+ storyKey,
5192
+ parseError: details
5193
+ }, "Test-plan YAML schema validation failed");
5194
+ return {
5195
+ ...makeTestPlanFailureResult(`schema_validation_failed: ${details}`),
5196
+ tokenUsage
5197
+ };
5198
+ }
5199
+ const parsed = dispatchResult.parsed;
5200
+ try {
5201
+ createDecision(deps.db, {
5202
+ pipeline_run_id: pipelineRunId,
5203
+ phase: "implementation",
5204
+ category: TEST_PLAN,
5205
+ key: storyKey,
5206
+ value: JSON.stringify({
5207
+ test_files: parsed.test_files,
5208
+ test_categories: parsed.test_categories,
5209
+ coverage_notes: parsed.coverage_notes
5210
+ }),
5211
+ rationale: `Test plan for ${storyKey}: ${parsed.test_files.length} test files, categories: ${parsed.test_categories.join(", ")}`
5212
+ });
5213
+ logger$9.info({
5214
+ storyKey,
5215
+ fileCount: parsed.test_files.length,
5216
+ categories: parsed.test_categories
5217
+ }, "Test plan stored in decision store");
5218
+ } catch (err) {
5219
+ const error = err instanceof Error ? err.message : String(err);
5220
+ logger$9.warn({
5221
+ storyKey,
5222
+ error
5223
+ }, "Failed to store test plan in decision store — proceeding anyway");
5224
+ }
5225
+ logger$9.info({
5226
+ storyKey,
5227
+ result: parsed.result
5228
+ }, "Test-plan workflow completed");
5229
+ return {
5230
+ result: parsed.result,
5231
+ test_files: parsed.test_files,
5232
+ test_categories: parsed.test_categories,
5233
+ coverage_notes: parsed.coverage_notes,
5234
+ tokenUsage
5235
+ };
5236
+ }
5237
+ /**
5238
+ * Build a failure result with sensible defaults.
5239
+ */
5240
+ function makeTestPlanFailureResult(error) {
5241
+ return {
5242
+ result: "failed",
5243
+ test_files: [],
5244
+ test_categories: [],
5245
+ coverage_notes: "",
5246
+ error,
5247
+ tokenUsage: {
5248
+ input: 0,
5249
+ output: 0
5250
+ }
5251
+ };
5252
+ }
5253
+
5254
+ //#endregion
5255
+ //#region src/modules/compiled-workflows/test-expansion.ts
5256
+ const logger$8 = createLogger("compiled-workflows:test-expansion");
5257
+ /**
5258
+ * Hard token ceiling for the assembled test-expansion prompt (20,000 tokens).
5259
+ */
5260
+ const TOKEN_CEILING = 2e4;
5261
+ function defaultFallbackResult(error, tokenUsage) {
5262
+ return {
5263
+ expansion_priority: "low",
5264
+ coverage_gaps: [],
5265
+ suggested_tests: [],
5266
+ error,
5267
+ tokenUsage
5268
+ };
5269
+ }
5270
+ /**
5271
+ * Execute the compiled test-expansion workflow.
5272
+ *
5273
+ * Steps:
5274
+ * 1. Retrieve compiled prompt template via pack.getPrompt('test-expansion')
5275
+ * 2. Read story file contents from storyFilePath
5276
+ * 3. Query decision store for architecture constraints (solutioning, architecture)
5277
+ * 4. Capture scoped git diff for filesModified, with stat-only fallback if oversized
5278
+ * 5. Assemble prompt with 20,000-token ceiling
5279
+ * 6. Dispatch via dispatcher with taskType='test-expansion'
5280
+ * 7. Validate YAML output against TestExpansionResultSchema
5281
+ * 8. Return typed TestExpansionResult (never throws — all errors return graceful fallback)
5282
+ *
5283
+ * @param deps - Injected dependencies (db, pack, contextCompiler, dispatcher)
5284
+ * @param params - Story key, story file path, files modified, working directory, pipeline run ID
5285
+ * @returns Promise resolving to TestExpansionResult (never rejects)
5286
+ */
5287
+ async function runTestExpansion(deps, params) {
5288
+ const { storyKey, storyFilePath, pipelineRunId, filesModified, workingDirectory } = params;
5289
+ const cwd = workingDirectory ?? process.cwd();
5290
+ logger$8.debug({
5291
+ storyKey,
5292
+ storyFilePath,
5293
+ cwd,
5294
+ pipelineRunId
5295
+ }, "Starting test-expansion workflow");
5296
+ let template;
5297
+ try {
5298
+ template = await deps.pack.getPrompt("test-expansion");
5299
+ } catch (err) {
5300
+ const error = err instanceof Error ? err.message : String(err);
5301
+ logger$8.warn({ error }, "Failed to retrieve test-expansion prompt template");
5302
+ return defaultFallbackResult(`Failed to retrieve prompt template: ${error}`, {
5303
+ input: 0,
5304
+ output: 0
5305
+ });
5306
+ }
5307
+ let storyContent;
5308
+ try {
5309
+ storyContent = await readFile$1(storyFilePath, "utf-8");
5310
+ } catch (err) {
5311
+ const error = err instanceof Error ? err.message : String(err);
5312
+ logger$8.warn({
5313
+ storyFilePath,
5314
+ error
5315
+ }, "Failed to read story file");
5316
+ return defaultFallbackResult(`Failed to read story file: ${error}`, {
5317
+ input: 0,
5318
+ output: 0
5319
+ });
5320
+ }
5321
+ const archConstraintsContent = getArchConstraints(deps);
5322
+ let gitDiffContent = "";
5323
+ if (filesModified && filesModified.length > 0) try {
5324
+ const templateTokens = countTokens(template);
5325
+ const storyTokens = countTokens(storyContent);
5326
+ const constraintTokens = countTokens(archConstraintsContent);
5327
+ const nonDiffTokens = templateTokens + storyTokens + constraintTokens;
5328
+ const scopedDiff = await getGitDiffForFiles(filesModified, cwd);
5329
+ const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
5330
+ if (scopedTotal <= TOKEN_CEILING) {
5331
+ gitDiffContent = scopedDiff;
5332
+ logger$8.debug({
5333
+ fileCount: filesModified.length,
5334
+ tokenCount: scopedTotal
5335
+ }, "Using scoped file diff");
5336
+ } else {
5337
+ logger$8.warn({
5338
+ estimatedTotal: scopedTotal,
5339
+ ceiling: TOKEN_CEILING,
5340
+ fileCount: filesModified.length
5341
+ }, "Scoped diff exceeds token ceiling — falling back to stat-only summary");
5342
+ gitDiffContent = await getGitDiffStatSummary(cwd);
5343
+ }
5344
+ } catch (err) {
5345
+ logger$8.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to get git diff — proceeding with empty diff");
5346
+ }
5347
+ const sections = [
5348
+ {
5349
+ name: "story_content",
5350
+ content: storyContent,
5351
+ priority: "required"
5352
+ },
5353
+ {
5354
+ name: "git_diff",
5355
+ content: gitDiffContent,
5356
+ priority: "important"
5357
+ },
5358
+ {
5359
+ name: "arch_constraints",
5360
+ content: archConstraintsContent,
5361
+ priority: "optional"
5362
+ }
5363
+ ];
5364
+ const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
5365
+ if (assembleResult.truncated) logger$8.warn({
5366
+ storyKey,
5367
+ tokenCount: assembleResult.tokenCount
5368
+ }, "Test-expansion prompt truncated to fit token ceiling");
5369
+ logger$8.debug({
5370
+ storyKey,
5371
+ tokenCount: assembleResult.tokenCount,
5372
+ truncated: assembleResult.truncated
5373
+ }, "Prompt assembled for test-expansion");
5374
+ const { prompt } = assembleResult;
5375
+ const handle = deps.dispatcher.dispatch({
5376
+ prompt,
5377
+ agent: "claude-code",
5378
+ taskType: "test-expansion",
5379
+ outputSchema: TestExpansionResultSchema,
5380
+ workingDirectory: deps.projectRoot
5381
+ });
5382
+ let dispatchResult;
5383
+ try {
5384
+ dispatchResult = await handle.result;
5385
+ } catch (err) {
5386
+ const error = err instanceof Error ? err.message : String(err);
5387
+ logger$8.warn({
5388
+ storyKey,
5389
+ error
5390
+ }, "Test-expansion dispatch threw unexpected error");
5391
+ return defaultFallbackResult(`Dispatch error: ${error}`, {
5392
+ input: Math.ceil(prompt.length / 4),
5393
+ output: 0
5394
+ });
5395
+ }
5396
+ const tokenUsage = {
5397
+ input: dispatchResult.tokenEstimate.input,
5398
+ output: dispatchResult.tokenEstimate.output
5399
+ };
5400
+ if (dispatchResult.status === "failed") {
5401
+ const errorMsg = `Dispatch status: failed. Exit code: ${dispatchResult.exitCode}. ${dispatchResult.parseError ?? ""}`.trim();
5402
+ logger$8.warn({
5403
+ storyKey,
5404
+ exitCode: dispatchResult.exitCode
5405
+ }, "Test-expansion dispatch failed");
5406
+ return defaultFallbackResult(errorMsg, tokenUsage);
5407
+ }
5408
+ if (dispatchResult.status === "timeout") {
5409
+ logger$8.warn({ storyKey }, "Test-expansion dispatch timed out");
5410
+ return defaultFallbackResult("Dispatch status: timeout. The agent did not complete within the allowed time.", tokenUsage);
5411
+ }
5412
+ if (dispatchResult.parsed === null) {
5413
+ const details = dispatchResult.parseError ?? "No YAML block found in output";
5414
+ logger$8.warn({
5415
+ storyKey,
5416
+ details
5417
+ }, "Test-expansion output has no parseable YAML");
5418
+ return defaultFallbackResult(`schema_validation_failed: ${details}`, tokenUsage);
5419
+ }
5420
+ const parseResult = TestExpansionResultSchema.safeParse(dispatchResult.parsed);
5421
+ if (!parseResult.success) {
5422
+ const details = parseResult.error.message;
5423
+ logger$8.warn({
5424
+ storyKey,
5425
+ details
5426
+ }, "Test-expansion output failed schema validation");
5427
+ return defaultFallbackResult(`schema_validation_failed: ${details}`, tokenUsage);
5428
+ }
5429
+ const parsed = parseResult.data;
5430
+ logger$8.info({
5431
+ storyKey,
5432
+ expansion_priority: parsed.expansion_priority,
5433
+ coverage_gaps: parsed.coverage_gaps.length,
5434
+ suggested_tests: parsed.suggested_tests.length
5435
+ }, "Test-expansion workflow completed successfully");
5436
+ return {
5437
+ expansion_priority: parsed.expansion_priority,
5438
+ coverage_gaps: parsed.coverage_gaps,
5439
+ suggested_tests: parsed.suggested_tests,
5440
+ notes: parsed.notes,
5441
+ tokenUsage
5442
+ };
5443
+ }
5444
+ /**
5445
+ * Retrieve architecture constraints from the decision store.
5446
+ * Looks for decisions with phase='solutioning', category='architecture'.
5447
+ */
4932
5448
  function getArchConstraints(deps) {
4933
5449
  try {
4934
5450
  const decisions = getDecisionsByPhase(deps.db, "solutioning");
@@ -5592,7 +6108,7 @@ function createPauseGate() {
5592
6108
  */
5593
6109
  function createImplementationOrchestrator(deps) {
5594
6110
  const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot } = deps;
5595
- const logger$18 = createLogger("implementation-orchestrator");
6111
+ const logger$20 = createLogger("implementation-orchestrator");
5596
6112
  let _state = "IDLE";
5597
6113
  let _startedAt;
5598
6114
  let _completedAt;
@@ -5629,7 +6145,7 @@ function createImplementationOrchestrator(deps) {
5629
6145
  const nowMs = Date.now();
5630
6146
  for (const [phase, startMs] of starts) {
5631
6147
  const endMs = ends?.get(phase);
5632
- if (endMs === void 0) logger$18.warn({
6148
+ if (endMs === void 0) logger$20.warn({
5633
6149
  storyKey,
5634
6150
  phase
5635
6151
  }, "Phase has no end time — story may have errored mid-phase. Duration capped to now() and may be inflated.");
@@ -5676,13 +6192,13 @@ function createImplementationOrchestrator(deps) {
5676
6192
  rationale: `Story ${storyKey} completed with result=${result} in ${wallClockSeconds}s. Tokens: ${tokenAgg.input}+${tokenAgg.output}. Review cycles: ${reviewCycles}.`
5677
6193
  });
5678
6194
  } catch (decisionErr) {
5679
- logger$18.warn({
6195
+ logger$20.warn({
5680
6196
  err: decisionErr,
5681
6197
  storyKey
5682
6198
  }, "Failed to write story-metrics decision (best-effort)");
5683
6199
  }
5684
6200
  } catch (err) {
5685
- logger$18.warn({
6201
+ logger$20.warn({
5686
6202
  err,
5687
6203
  storyKey
5688
6204
  }, "Failed to write story metrics (best-effort)");
@@ -5711,7 +6227,7 @@ function createImplementationOrchestrator(deps) {
5711
6227
  rationale: `Story ${storyKey} ${outcome} after ${reviewCycles} review cycle(s).`
5712
6228
  });
5713
6229
  } catch (err) {
5714
- logger$18.warn({
6230
+ logger$20.warn({
5715
6231
  err,
5716
6232
  storyKey
5717
6233
  }, "Failed to write story-outcome decision (best-effort)");
@@ -5737,7 +6253,7 @@ function createImplementationOrchestrator(deps) {
5737
6253
  rationale: `Escalation diagnosis for ${payload.storyKey}: ${diagnosis.recommendedAction} — ${diagnosis.rationale}`
5738
6254
  });
5739
6255
  } catch (err) {
5740
- logger$18.warn({
6256
+ logger$20.warn({
5741
6257
  err,
5742
6258
  storyKey: payload.storyKey
5743
6259
  }, "Failed to persist escalation diagnosis (best-effort)");
@@ -5787,7 +6303,7 @@ function createImplementationOrchestrator(deps) {
5787
6303
  token_usage_json: serialized
5788
6304
  });
5789
6305
  } catch (err) {
5790
- logger$18.warn("Failed to persist orchestrator state", { err });
6306
+ logger$20.warn("Failed to persist orchestrator state", { err });
5791
6307
  }
5792
6308
  }
5793
6309
  function recordProgress() {
@@ -5817,7 +6333,7 @@ function createImplementationOrchestrator(deps) {
5817
6333
  if (_stalledStories.has(key)) continue;
5818
6334
  _stalledStories.add(key);
5819
6335
  _storiesWithStall.add(key);
5820
- logger$18.warn({
6336
+ logger$20.warn({
5821
6337
  storyKey: key,
5822
6338
  phase: s.phase,
5823
6339
  elapsedMs: elapsed
@@ -5854,7 +6370,7 @@ function createImplementationOrchestrator(deps) {
5854
6370
  * exhausted retries the story is ESCALATED.
5855
6371
  */
5856
6372
  async function processStory(storyKey) {
5857
- logger$18.info("Processing story", { storyKey });
6373
+ logger$20.info("Processing story", { storyKey });
5858
6374
  await waitIfPaused();
5859
6375
  if (_state !== "RUNNING") return;
5860
6376
  startPhase(storyKey, "create-story");
@@ -5869,7 +6385,7 @@ function createImplementationOrchestrator(deps) {
5869
6385
  const match = files.find((f) => f.startsWith(`${storyKey}-`) && f.endsWith(".md"));
5870
6386
  if (match) {
5871
6387
  storyFilePath = join$1(artifactsDir, match);
5872
- logger$18.info({
6388
+ logger$20.info({
5873
6389
  storyKey,
5874
6390
  storyFilePath
5875
6391
  }, "Found existing story file — skipping create-story");
@@ -5961,6 +6477,39 @@ function createImplementationOrchestrator(deps) {
5961
6477
  }
5962
6478
  await waitIfPaused();
5963
6479
  if (_state !== "RUNNING") return;
6480
+ startPhase(storyKey, "test-plan");
6481
+ updateStory(storyKey, { phase: "IN_TEST_PLANNING" });
6482
+ persistState();
6483
+ let testPlanPhaseResult = "failed";
6484
+ try {
6485
+ const testPlanResult = await runTestPlan({
6486
+ db,
6487
+ pack,
6488
+ contextCompiler,
6489
+ dispatcher,
6490
+ projectRoot
6491
+ }, {
6492
+ storyKey,
6493
+ storyFilePath: storyFilePath ?? "",
6494
+ pipelineRunId: config.pipelineRunId
6495
+ });
6496
+ testPlanPhaseResult = testPlanResult.result;
6497
+ if (testPlanResult.result === "success") logger$20.info({ storyKey }, "Test plan generated successfully");
6498
+ else logger$20.warn({ storyKey }, "Test planning returned failed result — proceeding to dev-story without test plan");
6499
+ } catch (err) {
6500
+ logger$20.warn({
6501
+ storyKey,
6502
+ err
6503
+ }, "Test planning failed — proceeding to dev-story without test plan");
6504
+ }
6505
+ endPhase(storyKey, "test-plan");
6506
+ eventBus.emit("orchestrator:story-phase-complete", {
6507
+ storyKey,
6508
+ phase: "IN_TEST_PLANNING",
6509
+ result: { result: testPlanPhaseResult }
6510
+ });
6511
+ await waitIfPaused();
6512
+ if (_state !== "RUNNING") return;
5964
6513
  startPhase(storyKey, "dev-story");
5965
6514
  updateStory(storyKey, { phase: "IN_DEV" });
5966
6515
  persistState();
@@ -5971,7 +6520,7 @@ function createImplementationOrchestrator(deps) {
5971
6520
  try {
5972
6521
  storyContentForAnalysis = await readFile$1(storyFilePath ?? "", "utf-8");
5973
6522
  } catch (err) {
5974
- logger$18.error({
6523
+ logger$20.error({
5975
6524
  storyKey,
5976
6525
  storyFilePath,
5977
6526
  error: err instanceof Error ? err.message : String(err)
@@ -5979,7 +6528,7 @@ function createImplementationOrchestrator(deps) {
5979
6528
  }
5980
6529
  const analysis = analyzeStoryComplexity(storyContentForAnalysis);
5981
6530
  const batches = planTaskBatches(analysis);
5982
- logger$18.info({
6531
+ logger$20.info({
5983
6532
  storyKey,
5984
6533
  estimatedScope: analysis.estimatedScope,
5985
6534
  batchCount: batches.length,
@@ -5997,7 +6546,7 @@ function createImplementationOrchestrator(deps) {
5997
6546
  if (_state !== "RUNNING") break;
5998
6547
  const taskScope = batch.taskIds.map((id, i) => `T${id}: ${batch.taskTitles[i] ?? ""}`).join("\n");
5999
6548
  const priorFiles = allFilesModified.size > 0 ? Array.from(allFilesModified) : void 0;
6000
- logger$18.info({
6549
+ logger$20.info({
6001
6550
  storyKey,
6002
6551
  batchIndex: batch.batchIndex,
6003
6552
  taskCount: batch.taskIds.length
@@ -6021,7 +6570,7 @@ function createImplementationOrchestrator(deps) {
6021
6570
  });
6022
6571
  } catch (batchErr) {
6023
6572
  const errMsg = batchErr instanceof Error ? batchErr.message : String(batchErr);
6024
- logger$18.warn({
6573
+ logger$20.warn({
6025
6574
  storyKey,
6026
6575
  batchIndex: batch.batchIndex,
6027
6576
  error: errMsg
@@ -6041,7 +6590,7 @@ function createImplementationOrchestrator(deps) {
6041
6590
  filesModified: batchFilesModified,
6042
6591
  result: batchResult.result === "success" ? "success" : "failed"
6043
6592
  };
6044
- logger$18.info(batchMetrics, "Batch dev-story metrics");
6593
+ logger$20.info(batchMetrics, "Batch dev-story metrics");
6045
6594
  for (const f of batchFilesModified) allFilesModified.add(f);
6046
6595
  if (batchFilesModified.length > 0) batchFileGroups.push({
6047
6596
  batchIndex: batch.batchIndex,
@@ -6063,13 +6612,13 @@ function createImplementationOrchestrator(deps) {
6063
6612
  })
6064
6613
  });
6065
6614
  } catch (tokenErr) {
6066
- logger$18.warn({
6615
+ logger$20.warn({
6067
6616
  storyKey,
6068
6617
  batchIndex: batch.batchIndex,
6069
6618
  err: tokenErr
6070
6619
  }, "Failed to record batch token usage");
6071
6620
  }
6072
- if (batchResult.result === "failed") logger$18.warn({
6621
+ if (batchResult.result === "failed") logger$20.warn({
6073
6622
  storyKey,
6074
6623
  batchIndex: batch.batchIndex,
6075
6624
  error: batchResult.error
@@ -6102,7 +6651,7 @@ function createImplementationOrchestrator(deps) {
6102
6651
  result: devResult
6103
6652
  });
6104
6653
  persistState();
6105
- if (devResult.result === "failed") logger$18.warn("Dev-story reported failure, proceeding to code review", {
6654
+ if (devResult.result === "failed") logger$20.warn("Dev-story reported failure, proceeding to code review", {
6106
6655
  storyKey,
6107
6656
  error: devResult.error,
6108
6657
  filesModified: devFilesModified.length
@@ -6159,7 +6708,7 @@ function createImplementationOrchestrator(deps) {
6159
6708
  "NEEDS_MAJOR_REWORK": 2
6160
6709
  };
6161
6710
  for (const group of batchFileGroups) {
6162
- logger$18.info({
6711
+ logger$20.info({
6163
6712
  storyKey,
6164
6713
  batchIndex: group.batchIndex,
6165
6714
  fileCount: group.files.length
@@ -6196,7 +6745,7 @@ function createImplementationOrchestrator(deps) {
6196
6745
  rawOutput: lastRawOutput,
6197
6746
  tokenUsage: aggregateTokens
6198
6747
  };
6199
- logger$18.info({
6748
+ logger$20.info({
6200
6749
  storyKey,
6201
6750
  batchCount: batchFileGroups.length,
6202
6751
  verdict: worstVerdict,
@@ -6222,7 +6771,7 @@ function createImplementationOrchestrator(deps) {
6222
6771
  const isPhantomReview = reviewResult.verdict !== "SHIP_IT" && (reviewResult.issue_list === void 0 || reviewResult.issue_list.length === 0) && reviewResult.error !== void 0;
6223
6772
  if (isPhantomReview && !timeoutRetried) {
6224
6773
  timeoutRetried = true;
6225
- logger$18.warn({
6774
+ logger$20.warn({
6226
6775
  storyKey,
6227
6776
  reviewCycles,
6228
6777
  error: reviewResult.error
@@ -6232,7 +6781,7 @@ function createImplementationOrchestrator(deps) {
6232
6781
  verdict = reviewResult.verdict;
6233
6782
  issueList = reviewResult.issue_list ?? [];
6234
6783
  if (verdict === "NEEDS_MAJOR_REWORK" && reviewCycles > 0 && previousIssueList.length > 0 && issueList.length < previousIssueList.length) {
6235
- logger$18.info({
6784
+ logger$20.info({
6236
6785
  storyKey,
6237
6786
  originalVerdict: verdict,
6238
6787
  issuesBefore: previousIssueList.length,
@@ -6268,7 +6817,7 @@ function createImplementationOrchestrator(deps) {
6268
6817
  if (_decomposition !== void 0) parts.push(`decomposed: ${_decomposition.batchCount} batches`);
6269
6818
  parts.push(`${fileCount} files`);
6270
6819
  parts.push(`${totalTokensK} tokens`);
6271
- logger$18.info({
6820
+ logger$20.info({
6272
6821
  storyKey,
6273
6822
  verdict,
6274
6823
  agentVerdict: reviewResult.agentVerdict
@@ -6305,6 +6854,38 @@ function createImplementationOrchestrator(deps) {
6305
6854
  reviewCycles
6306
6855
  });
6307
6856
  persistState();
6857
+ try {
6858
+ const expansionResult = await runTestExpansion({
6859
+ db,
6860
+ pack,
6861
+ contextCompiler,
6862
+ dispatcher,
6863
+ projectRoot
6864
+ }, {
6865
+ storyKey,
6866
+ storyFilePath: storyFilePath ?? "",
6867
+ pipelineRunId: config.pipelineRunId,
6868
+ filesModified: devFilesModified,
6869
+ workingDirectory: projectRoot
6870
+ });
6871
+ logger$20.debug({
6872
+ storyKey,
6873
+ expansion_priority: expansionResult.expansion_priority,
6874
+ coverage_gaps: expansionResult.coverage_gaps.length
6875
+ }, "Test expansion analysis complete");
6876
+ createDecision(db, {
6877
+ pipeline_run_id: config.pipelineRunId ?? "unknown",
6878
+ phase: "implementation",
6879
+ category: TEST_EXPANSION_FINDING,
6880
+ key: `${storyKey}:${config.pipelineRunId ?? "unknown"}`,
6881
+ value: JSON.stringify(expansionResult)
6882
+ });
6883
+ } catch (expansionErr) {
6884
+ logger$20.warn({
6885
+ storyKey,
6886
+ error: expansionErr instanceof Error ? expansionErr.message : String(expansionErr)
6887
+ }, "Test expansion failed — story verdict unchanged");
6888
+ }
6308
6889
  keepReviewing = false;
6309
6890
  return;
6310
6891
  }
@@ -6327,7 +6908,7 @@ function createImplementationOrchestrator(deps) {
6327
6908
  persistState();
6328
6909
  return;
6329
6910
  }
6330
- logger$18.info({
6911
+ logger$20.info({
6331
6912
  storyKey,
6332
6913
  reviewCycles: finalReviewCycles,
6333
6914
  issueCount: issueList.length
@@ -6377,7 +6958,7 @@ function createImplementationOrchestrator(deps) {
6377
6958
  fixPrompt = assembled.prompt;
6378
6959
  } catch {
6379
6960
  fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, minor fixes needed`;
6380
- logger$18.warn("Failed to assemble auto-approve fix prompt, using fallback", { storyKey });
6961
+ logger$20.warn("Failed to assemble auto-approve fix prompt, using fallback", { storyKey });
6381
6962
  }
6382
6963
  const handle = dispatcher.dispatch({
6383
6964
  prompt: fixPrompt,
@@ -6394,9 +6975,9 @@ function createImplementationOrchestrator(deps) {
6394
6975
  output: fixResult.tokenEstimate.output
6395
6976
  } : void 0 }
6396
6977
  });
6397
- if (fixResult.status === "timeout") logger$18.warn("Auto-approve fix timed out — approving anyway (issues were minor)", { storyKey });
6978
+ if (fixResult.status === "timeout") logger$20.warn("Auto-approve fix timed out — approving anyway (issues were minor)", { storyKey });
6398
6979
  } catch (err) {
6399
- logger$18.warn("Auto-approve fix dispatch failed — approving anyway (issues were minor)", {
6980
+ logger$20.warn("Auto-approve fix dispatch failed — approving anyway (issues were minor)", {
6400
6981
  storyKey,
6401
6982
  err
6402
6983
  });
@@ -6469,7 +7050,7 @@ function createImplementationOrchestrator(deps) {
6469
7050
  fixPrompt = assembled.prompt;
6470
7051
  } catch {
6471
7052
  fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, taskType=${taskType}`;
6472
- logger$18.warn("Failed to assemble fix prompt, using fallback", {
7053
+ logger$20.warn("Failed to assemble fix prompt, using fallback", {
6473
7054
  storyKey,
6474
7055
  taskType
6475
7056
  });
@@ -6492,7 +7073,7 @@ function createImplementationOrchestrator(deps) {
6492
7073
  } : void 0 }
6493
7074
  });
6494
7075
  if (fixResult.status === "timeout") {
6495
- logger$18.warn("Fix dispatch timed out — escalating story", {
7076
+ logger$20.warn("Fix dispatch timed out — escalating story", {
6496
7077
  storyKey,
6497
7078
  taskType
6498
7079
  });
@@ -6512,13 +7093,13 @@ function createImplementationOrchestrator(deps) {
6512
7093
  persistState();
6513
7094
  return;
6514
7095
  }
6515
- if (fixResult.status === "failed") logger$18.warn("Fix dispatch failed", {
7096
+ if (fixResult.status === "failed") logger$20.warn("Fix dispatch failed", {
6516
7097
  storyKey,
6517
7098
  taskType,
6518
7099
  exitCode: fixResult.exitCode
6519
7100
  });
6520
7101
  } catch (err) {
6521
- logger$18.warn("Fix dispatch failed, continuing to next review", {
7102
+ logger$20.warn("Fix dispatch failed, continuing to next review", {
6522
7103
  storyKey,
6523
7104
  taskType,
6524
7105
  err
@@ -6572,11 +7153,11 @@ function createImplementationOrchestrator(deps) {
6572
7153
  }
6573
7154
  async function run(storyKeys) {
6574
7155
  if (_state === "RUNNING" || _state === "PAUSED") {
6575
- logger$18.warn("run() called while orchestrator is already running or paused — ignoring", { state: _state });
7156
+ logger$20.warn("run() called while orchestrator is already running or paused — ignoring", { state: _state });
6576
7157
  return getStatus();
6577
7158
  }
6578
7159
  if (_state === "COMPLETE") {
6579
- logger$18.warn("run() called on a COMPLETE orchestrator — ignoring", { state: _state });
7160
+ logger$20.warn("run() called on a COMPLETE orchestrator — ignoring", { state: _state });
6580
7161
  return getStatus();
6581
7162
  }
6582
7163
  _state = "RUNNING";
@@ -6594,13 +7175,13 @@ function createImplementationOrchestrator(deps) {
6594
7175
  if (config.enableHeartbeat) startHeartbeat();
6595
7176
  if (projectRoot !== void 0) {
6596
7177
  const seedResult = seedMethodologyContext(db, projectRoot);
6597
- if (seedResult.decisionsCreated > 0) logger$18.info({
7178
+ if (seedResult.decisionsCreated > 0) logger$20.info({
6598
7179
  decisionsCreated: seedResult.decisionsCreated,
6599
7180
  skippedCategories: seedResult.skippedCategories
6600
7181
  }, "Methodology context seeded from planning artifacts");
6601
7182
  }
6602
7183
  const groups = detectConflictGroups(storyKeys);
6603
- logger$18.info("Orchestrator starting", {
7184
+ logger$20.info("Orchestrator starting", {
6604
7185
  storyCount: storyKeys.length,
6605
7186
  groupCount: groups.length,
6606
7187
  maxConcurrency: config.maxConcurrency
@@ -6612,7 +7193,7 @@ function createImplementationOrchestrator(deps) {
6612
7193
  _state = "FAILED";
6613
7194
  _completedAt = new Date().toISOString();
6614
7195
  persistState();
6615
- logger$18.error("Orchestrator failed with unhandled error", { err });
7196
+ logger$20.error("Orchestrator failed with unhandled error", { err });
6616
7197
  return getStatus();
6617
7198
  }
6618
7199
  stopHeartbeat();
@@ -6639,7 +7220,7 @@ function createImplementationOrchestrator(deps) {
6639
7220
  _pauseGate = createPauseGate();
6640
7221
  _state = "PAUSED";
6641
7222
  eventBus.emit("orchestrator:paused", {});
6642
- logger$18.info("Orchestrator paused");
7223
+ logger$20.info("Orchestrator paused");
6643
7224
  }
6644
7225
  function resume() {
6645
7226
  if (_state !== "PAUSED") return;
@@ -6650,7 +7231,7 @@ function createImplementationOrchestrator(deps) {
6650
7231
  }
6651
7232
  _state = "RUNNING";
6652
7233
  eventBus.emit("orchestrator:resumed", {});
6653
- logger$18.info("Orchestrator resumed");
7234
+ logger$20.info("Orchestrator resumed");
6654
7235
  }
6655
7236
  return {
6656
7237
  run,
@@ -8481,6 +9062,10 @@ const AMENDMENT_CONTEXT_HEADER$2 = "\n\n--- AMENDMENT CONTEXT (Parent Run Decisi
8481
9062
  const AMENDMENT_CONTEXT_FOOTER$2 = "\n--- END AMENDMENT CONTEXT ---\n";
8482
9063
  /** Marker appended when amendment context is truncated to fit token budget */
8483
9064
  const TRUNCATED_MARKER$2 = "\n[TRUNCATED]";
9065
+ /** Prior run findings framing block prefix */
9066
+ const PRIOR_FINDINGS_HEADER = "\n\n--- PRIOR RUN FINDINGS ---\n";
9067
+ /** Prior run findings framing block suffix */
9068
+ const PRIOR_FINDINGS_FOOTER = "\n--- END PRIOR RUN FINDINGS ---\n";
8484
9069
  /** Concept placeholder in the prompt template */
8485
9070
  const CONCEPT_PLACEHOLDER = "{{concept}}";
8486
9071
  /** Product brief fields to persist as decisions */
@@ -8521,6 +9106,9 @@ function buildAnalysisSteps() {
8521
9106
  context: [{
8522
9107
  placeholder: "concept",
8523
9108
  source: "param:concept"
9109
+ }, {
9110
+ placeholder: "prior_findings",
9111
+ source: "param:prior_findings"
8524
9112
  }],
8525
9113
  persist: [{
8526
9114
  field: "problem_statement",
@@ -8584,7 +9172,14 @@ async function runAnalysisMultiStep(deps, params) {
8584
9172
  };
8585
9173
  try {
8586
9174
  const steps = buildAnalysisSteps();
8587
- const result = await runSteps(steps, deps, params.runId, "analysis", { concept: params.concept });
9175
+ let priorFindings = "";
9176
+ try {
9177
+ priorFindings = getProjectFindings(deps.db);
9178
+ } catch {}
9179
+ const result = await runSteps(steps, deps, params.runId, "analysis", {
9180
+ concept: params.concept,
9181
+ prior_findings: priorFindings
9182
+ });
8588
9183
  if (!result.success) return {
8589
9184
  result: "failed",
8590
9185
  error: result.error ?? "multi_step_failed",
@@ -8665,6 +9260,18 @@ async function runAnalysisPhase(deps, params) {
8665
9260
  let effectiveConcept = concept;
8666
9261
  if (concept.length > MAX_CONCEPT_CHARS) effectiveConcept = concept.slice(0, MAX_CONCEPT_CHARS) + "...";
8667
9262
  let prompt = template.replace(CONCEPT_PLACEHOLDER, effectiveConcept);
9263
+ try {
9264
+ const priorFindings = getProjectFindings(db);
9265
+ if (priorFindings !== "") {
9266
+ const maxPromptChars = MAX_PROMPT_TOKENS$1 * 4;
9267
+ const framingLen = PRIOR_FINDINGS_HEADER.length + PRIOR_FINDINGS_FOOTER.length;
9268
+ const availableForFindings = maxPromptChars - prompt.length - framingLen - TRUNCATED_MARKER$2.length;
9269
+ if (availableForFindings > 0) {
9270
+ const findingsToInject = priorFindings.length > availableForFindings ? priorFindings.slice(0, availableForFindings) + TRUNCATED_MARKER$2 : priorFindings;
9271
+ prompt += PRIOR_FINDINGS_HEADER + findingsToInject + PRIOR_FINDINGS_FOOTER;
9272
+ }
9273
+ }
9274
+ } catch {}
8668
9275
  if (amendmentContext !== void 0 && amendmentContext !== "") {
8669
9276
  const maxPromptChars = MAX_PROMPT_TOKENS$1 * 4;
8670
9277
  const basePromptLen = prompt.length;
@@ -10919,7 +11526,7 @@ function mapInternalPhaseToEventPhase(internalPhase) {
10919
11526
  }
10920
11527
  }
10921
11528
  async function runRunAction(options) {
10922
- const { pack: packName, from: startPhase, stopAfter, concept: conceptArg, conceptFile, stories: storiesArg, concurrency, outputFormat, projectRoot, events: eventsFlag, verbose: verboseFlag, tui: tuiFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag } = options;
11529
+ const { pack: packName, from: startPhase, stopAfter, concept: conceptArg, conceptFile, stories: storiesArg, concurrency, outputFormat, projectRoot, events: eventsFlag, verbose: verboseFlag, tui: tuiFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, registry: injectedRegistry } = options;
10923
11530
  if (startPhase !== void 0 && !VALID_PHASES.includes(startPhase)) {
10924
11531
  const errorMsg = `Invalid phase '${startPhase}'. Valid phases: ${VALID_PHASES.join(", ")}`;
10925
11532
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
@@ -10977,7 +11584,8 @@ async function runRunAction(options) {
10977
11584
  ...eventsFlag === true ? { events: true } : {},
10978
11585
  ...skipUx === true ? { skipUx: true } : {},
10979
11586
  ...researchFlag === true ? { research: true } : {},
10980
- ...skipResearchFlag === true ? { skipResearch: true } : {}
11587
+ ...skipResearchFlag === true ? { skipResearch: true } : {},
11588
+ ...injectedRegistry !== void 0 ? { registry: injectedRegistry } : {}
10981
11589
  });
10982
11590
  let storyKeys = [];
10983
11591
  if (storiesArg !== void 0 && storiesArg !== "") {
@@ -11066,8 +11674,8 @@ async function runRunAction(options) {
11066
11674
  });
11067
11675
  const eventBus = createEventBus();
11068
11676
  const contextCompiler = createContextCompiler({ db });
11069
- const adapterRegistry = new AdapterRegistry();
11070
- await adapterRegistry.discoverAndRegister();
11677
+ const adapterRegistry = injectedRegistry ?? new AdapterRegistry();
11678
+ if (injectedRegistry === void 0) await adapterRegistry.discoverAndRegister();
11071
11679
  const dispatcher = createDispatcher({
11072
11680
  eventBus,
11073
11681
  adapterRegistry
@@ -11430,7 +12038,7 @@ async function runRunAction(options) {
11430
12038
  }
11431
12039
  }
11432
12040
  async function runFullPipeline(options) {
11433
- const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, projectRoot, events: eventsFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag } = options;
12041
+ const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, projectRoot, events: eventsFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, registry: injectedRegistry } = options;
11434
12042
  if (!existsSync(dbDir)) mkdirSync(dbDir, { recursive: true });
11435
12043
  const dbWrapper = new DatabaseWrapper(dbPath);
11436
12044
  try {
@@ -11458,8 +12066,8 @@ async function runFullPipeline(options) {
11458
12066
  }
11459
12067
  const eventBus = createEventBus();
11460
12068
  const contextCompiler = createContextCompiler({ db });
11461
- const adapterRegistry = new AdapterRegistry();
11462
- await adapterRegistry.discoverAndRegister();
12069
+ const adapterRegistry = injectedRegistry ?? new AdapterRegistry();
12070
+ if (injectedRegistry === void 0) await adapterRegistry.discoverAndRegister();
11463
12071
  const dispatcher = createDispatcher({
11464
12072
  eventBus,
11465
12073
  adapterRegistry
@@ -11734,7 +12342,7 @@ async function runFullPipeline(options) {
11734
12342
  } catch {}
11735
12343
  }
11736
12344
  }
11737
- function registerRunCommand(program, _version = "0.0.0", projectRoot = process.cwd()) {
12345
+ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
11738
12346
  program.command("run").description("Run the autonomous pipeline (use --from to start from a specific phase)").option("--pack <name>", "Methodology pack name", "bmad").option("--from <phase>", "Start from this phase: analysis, planning, solutioning, implementation").option("--stop-after <phase>", "Stop pipeline after this phase completes").option("--concept <text>", "Inline concept text (required when --from analysis)").option("--concept-file <path>", "Path to a file containing the concept text").option("--stories <keys>", "Comma-separated story keys (e.g., 10-1,10-2)").option("--concurrency <n>", "Maximum parallel conflict groups", (v) => parseInt(v, 10), 3).option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").option("--events", "Emit structured NDJSON events on stdout for programmatic consumption").option("--verbose", "Show detailed pino log output").option("--help-agent", "Print a machine-optimized prompt fragment for AI agents and exit").option("--tui", "Show TUI dashboard").option("--skip-ux", "Skip the UX design phase even if enabled in the pack manifest").option("--research", "Enable the research phase even if not set in the pack manifest").option("--skip-research", "Skip the research phase even if enabled in the pack manifest").action(async (opts) => {
11739
12347
  if (opts.helpAgent) {
11740
12348
  process.exitCode = await runHelpAgent();
@@ -11767,7 +12375,8 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
11767
12375
  tui: opts.tui,
11768
12376
  skipUx: opts.skipUx,
11769
12377
  research: opts.research,
11770
- skipResearch: opts.skipResearch
12378
+ skipResearch: opts.skipResearch,
12379
+ registry
11771
12380
  });
11772
12381
  process.exitCode = exitCode;
11773
12382
  });
@@ -11775,4 +12384,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
11775
12384
 
11776
12385
  //#endregion
11777
12386
  export { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
11778
- //# sourceMappingURL=run-CcWb6Kb-.js.map
12387
+ //# sourceMappingURL=run-BLIgARum.js.map