substrate-ai 0.2.26 → 0.2.28

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.
@@ -539,7 +539,7 @@ const migration010RunMetrics = {
539
539
 
540
540
  //#endregion
541
541
  //#region src/persistence/migrations/index.ts
542
- const logger$19 = createLogger("persistence:migrations");
542
+ const logger$21 = 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$19.info("Starting migration runner");
560
+ logger$21.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$19.info("No pending migrations");
571
+ logger$21.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$19.info({
576
+ logger$21.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$19.info({ version: migration.version }, "Migration applied successfully");
590
+ logger$21.info({ version: migration.version }, "Migration applied successfully");
591
591
  }
592
- logger$19.info({ count: pending.length }, "All pending migrations applied");
592
+ logger$21.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$18 = createLogger("persistence:database");
597
+ const logger$20 = 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$18.info({ path: this._path }, "Opening SQLite database");
614
+ logger$20.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$18.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$20.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$18.info({ path: this._path }, "SQLite database opened with WAL mode");
621
+ logger$20.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$18.info({ path: this._path }, "SQLite database closed");
630
+ logger$20.info({ path: this._path }, "SQLite database closed");
631
631
  }
632
632
  /**
633
633
  * Return the raw BetterSqlite3 instance.
@@ -2277,6 +2277,75 @@ const PIPELINE_EVENT_METADATA = [
2277
2277
  type: "string",
2278
2278
  description: "Story key."
2279
2279
  }]
2280
+ },
2281
+ {
2282
+ type: "story:interface-change-warning",
2283
+ description: "Non-blocking warning: modified files export shared TypeScript interfaces that may be referenced by test files outside the same module (potential stale mock risk). Story proceeds to code-review.",
2284
+ when: "After build verification passes, before code-review, when exported interfaces in modified .ts files are referenced by cross-module test files.",
2285
+ fields: [
2286
+ {
2287
+ name: "ts",
2288
+ type: "string",
2289
+ description: "Timestamp."
2290
+ },
2291
+ {
2292
+ name: "storyKey",
2293
+ type: "string",
2294
+ description: "Story key."
2295
+ },
2296
+ {
2297
+ name: "modifiedInterfaces",
2298
+ type: "string[]",
2299
+ description: "Exported interface/type names found in modified files."
2300
+ },
2301
+ {
2302
+ name: "potentiallyAffectedTests",
2303
+ type: "string[]",
2304
+ description: "Test file paths (relative to project root) that reference the modified interface names."
2305
+ }
2306
+ ]
2307
+ },
2308
+ {
2309
+ type: "story:metrics",
2310
+ description: "Per-story metrics on terminal state.",
2311
+ when: "After terminal state (success/escalation/failure).",
2312
+ fields: [
2313
+ {
2314
+ name: "ts",
2315
+ type: "string",
2316
+ description: "Timestamp."
2317
+ },
2318
+ {
2319
+ name: "storyKey",
2320
+ type: "string",
2321
+ description: "Story key."
2322
+ },
2323
+ {
2324
+ name: "wallClockMs",
2325
+ type: "number",
2326
+ description: "Wall-clock ms."
2327
+ },
2328
+ {
2329
+ name: "phaseBreakdown",
2330
+ type: "Record<string,number>",
2331
+ description: "Phase→ms durations."
2332
+ },
2333
+ {
2334
+ name: "tokens",
2335
+ type: "{input:number;output:number}",
2336
+ description: "Token counts."
2337
+ },
2338
+ {
2339
+ name: "reviewCycles",
2340
+ type: "number",
2341
+ description: "Review cycle count."
2342
+ },
2343
+ {
2344
+ name: "dispatches",
2345
+ type: "number",
2346
+ description: "Dispatch count."
2347
+ }
2348
+ ]
2280
2349
  }
2281
2350
  ];
2282
2351
  /**
@@ -2606,7 +2675,7 @@ function truncateToTokens(text, maxTokens) {
2606
2675
 
2607
2676
  //#endregion
2608
2677
  //#region src/modules/context-compiler/context-compiler-impl.ts
2609
- const logger$17 = createLogger("context-compiler");
2678
+ const logger$19 = createLogger("context-compiler");
2610
2679
  /**
2611
2680
  * Fraction of the original token budget that must remain (after required +
2612
2681
  * important sections) before an optional section is included.
@@ -2698,7 +2767,7 @@ var ContextCompilerImpl = class {
2698
2767
  includedParts.push(truncated);
2699
2768
  remainingBudget -= truncatedTokens;
2700
2769
  anyTruncated = true;
2701
- logger$17.warn({
2770
+ logger$19.warn({
2702
2771
  section: section.name,
2703
2772
  originalTokens: tokens,
2704
2773
  budgetTokens: truncatedTokens
@@ -2712,7 +2781,7 @@ var ContextCompilerImpl = class {
2712
2781
  });
2713
2782
  } else {
2714
2783
  anyTruncated = true;
2715
- logger$17.warn({
2784
+ logger$19.warn({
2716
2785
  section: section.name,
2717
2786
  tokens
2718
2787
  }, "Context compiler: omitted \"important\" section — no budget remaining");
@@ -2739,7 +2808,7 @@ var ContextCompilerImpl = class {
2739
2808
  } else {
2740
2809
  if (tokens > 0) {
2741
2810
  anyTruncated = true;
2742
- logger$17.warn({
2811
+ logger$19.warn({
2743
2812
  section: section.name,
2744
2813
  tokens,
2745
2814
  budgetFractionRemaining: budgetFractionRemaining.toFixed(2)
@@ -3024,7 +3093,7 @@ function parseYamlResult(yamlText, schema) {
3024
3093
 
3025
3094
  //#endregion
3026
3095
  //#region src/modules/agent-dispatch/dispatcher-impl.ts
3027
- const logger$16 = createLogger("agent-dispatch");
3096
+ const logger$18 = createLogger("agent-dispatch");
3028
3097
  const SHUTDOWN_GRACE_MS = 1e4;
3029
3098
  const SHUTDOWN_MAX_WAIT_MS = 3e4;
3030
3099
  const CHARS_PER_TOKEN = 4;
@@ -3069,7 +3138,7 @@ function getAvailableMemory() {
3069
3138
  }).trim(), 10);
3070
3139
  _lastKnownPressureLevel = pressureLevel;
3071
3140
  if (pressureLevel >= 4) {
3072
- logger$16.warn({ pressureLevel }, "macOS kernel reports critical memory pressure");
3141
+ logger$18.warn({ pressureLevel }, "macOS kernel reports critical memory pressure");
3073
3142
  return 0;
3074
3143
  }
3075
3144
  } catch {}
@@ -3084,7 +3153,7 @@ function getAvailableMemory() {
3084
3153
  const speculative = parseInt(vmstat.match(/Pages speculative:\s+(\d+)/)?.[1] ?? "0", 10);
3085
3154
  const available = (free + purgeable + speculative) * pageSize;
3086
3155
  if (pressureLevel >= 2) {
3087
- logger$16.warn({
3156
+ logger$18.warn({
3088
3157
  pressureLevel,
3089
3158
  availableBeforeDiscount: available
3090
3159
  }, "macOS kernel reports memory pressure — discounting estimate");
@@ -3164,7 +3233,7 @@ var DispatcherImpl = class {
3164
3233
  resolve: typedResolve,
3165
3234
  reject
3166
3235
  });
3167
- logger$16.debug({
3236
+ logger$18.debug({
3168
3237
  id,
3169
3238
  queueLength: this._queue.length
3170
3239
  }, "Dispatch queued");
@@ -3195,7 +3264,7 @@ var DispatcherImpl = class {
3195
3264
  async shutdown() {
3196
3265
  this._shuttingDown = true;
3197
3266
  this._stopMemoryPressureTimer();
3198
- logger$16.info({
3267
+ logger$18.info({
3199
3268
  running: this._running.size,
3200
3269
  queued: this._queue.length
3201
3270
  }, "Dispatcher shutting down");
@@ -3228,13 +3297,13 @@ var DispatcherImpl = class {
3228
3297
  }
3229
3298
  }, 50);
3230
3299
  });
3231
- logger$16.info("Dispatcher shutdown complete");
3300
+ logger$18.info("Dispatcher shutdown complete");
3232
3301
  }
3233
3302
  async _startDispatch(id, request, resolve$2) {
3234
3303
  const { prompt, agent, taskType, timeout, outputSchema, workingDirectory, model, maxTurns } = request;
3235
3304
  const adapter = this._adapterRegistry.get(agent);
3236
3305
  if (adapter === void 0) {
3237
- logger$16.warn({
3306
+ logger$18.warn({
3238
3307
  id,
3239
3308
  agent
3240
3309
  }, "No adapter found for agent");
@@ -3280,7 +3349,7 @@ var DispatcherImpl = class {
3280
3349
  });
3281
3350
  const startedAt = Date.now();
3282
3351
  proc.on("error", (err) => {
3283
- logger$16.error({
3352
+ logger$18.error({
3284
3353
  id,
3285
3354
  binary: cmd.binary,
3286
3355
  error: err.message
@@ -3288,7 +3357,7 @@ var DispatcherImpl = class {
3288
3357
  });
3289
3358
  if (proc.stdin !== null) {
3290
3359
  proc.stdin.on("error", (err) => {
3291
- if (err.code !== "EPIPE") logger$16.warn({
3360
+ if (err.code !== "EPIPE") logger$18.warn({
3292
3361
  id,
3293
3362
  error: err.message
3294
3363
  }, "stdin write error");
@@ -3330,7 +3399,7 @@ var DispatcherImpl = class {
3330
3399
  agent,
3331
3400
  taskType
3332
3401
  });
3333
- logger$16.debug({
3402
+ logger$18.debug({
3334
3403
  id,
3335
3404
  agent,
3336
3405
  taskType,
@@ -3347,7 +3416,7 @@ var DispatcherImpl = class {
3347
3416
  dispatchId: id,
3348
3417
  timeoutMs
3349
3418
  });
3350
- logger$16.warn({
3419
+ logger$18.warn({
3351
3420
  id,
3352
3421
  agent,
3353
3422
  taskType,
@@ -3401,7 +3470,7 @@ var DispatcherImpl = class {
3401
3470
  exitCode: code,
3402
3471
  output: stdout
3403
3472
  });
3404
- logger$16.debug({
3473
+ logger$18.debug({
3405
3474
  id,
3406
3475
  agent,
3407
3476
  taskType,
@@ -3427,7 +3496,7 @@ var DispatcherImpl = class {
3427
3496
  error: stderr || `Process exited with code ${String(code)}`,
3428
3497
  exitCode: code
3429
3498
  });
3430
- logger$16.debug({
3499
+ logger$18.debug({
3431
3500
  id,
3432
3501
  agent,
3433
3502
  taskType,
@@ -3486,7 +3555,7 @@ var DispatcherImpl = class {
3486
3555
  const next = this._queue.shift();
3487
3556
  if (next === void 0) return;
3488
3557
  next.handle.status = "running";
3489
- logger$16.debug({
3558
+ logger$18.debug({
3490
3559
  id: next.id,
3491
3560
  queueLength: this._queue.length
3492
3561
  }, "Dequeued dispatch");
@@ -3499,7 +3568,7 @@ var DispatcherImpl = class {
3499
3568
  _isMemoryPressured() {
3500
3569
  const free = getAvailableMemory();
3501
3570
  if (free < MIN_FREE_MEMORY_BYTES) {
3502
- logger$16.warn({
3571
+ logger$18.warn({
3503
3572
  freeMB: Math.round(free / 1024 / 1024),
3504
3573
  thresholdMB: Math.round(MIN_FREE_MEMORY_BYTES / 1024 / 1024),
3505
3574
  pressureLevel: _lastKnownPressureLevel
@@ -3539,6 +3608,54 @@ var DispatcherImpl = class {
3539
3608
  };
3540
3609
  /** Default command for the build verification gate */
3541
3610
  const DEFAULT_VERIFY_COMMAND = "npm run build";
3611
+ /**
3612
+ * Detect the package manager used in a project by checking for lockfiles.
3613
+ *
3614
+ * Priority order (checked in sequence):
3615
+ * pnpm-lock.yaml → pnpm run build
3616
+ * yarn.lock → yarn run build
3617
+ * bun.lockb → bun run build
3618
+ * package-lock.json → npm run build
3619
+ * (none found) → npm run build (fallback, matches DEFAULT_VERIFY_COMMAND)
3620
+ *
3621
+ * Lockfile presence is used rather than the `packageManager` field in
3622
+ * package.json because lockfiles are more reliable and always present in
3623
+ * real projects.
3624
+ */
3625
+ function detectPackageManager(projectRoot) {
3626
+ const candidates = [
3627
+ {
3628
+ file: "pnpm-lock.yaml",
3629
+ packageManager: "pnpm",
3630
+ command: "pnpm run build"
3631
+ },
3632
+ {
3633
+ file: "yarn.lock",
3634
+ packageManager: "yarn",
3635
+ command: "yarn run build"
3636
+ },
3637
+ {
3638
+ file: "bun.lockb",
3639
+ packageManager: "bun",
3640
+ command: "bun run build"
3641
+ },
3642
+ {
3643
+ file: "package-lock.json",
3644
+ packageManager: "npm",
3645
+ command: "npm run build"
3646
+ }
3647
+ ];
3648
+ for (const candidate of candidates) if (existsSync$1(join$1(projectRoot, candidate.file))) return {
3649
+ packageManager: candidate.packageManager,
3650
+ lockfile: candidate.file,
3651
+ command: candidate.command
3652
+ };
3653
+ return {
3654
+ packageManager: "npm",
3655
+ lockfile: null,
3656
+ command: DEFAULT_VERIFY_COMMAND
3657
+ };
3658
+ }
3542
3659
  /** Default timeout in milliseconds for the build verification gate */
3543
3660
  const DEFAULT_VERIFY_TIMEOUT_MS = 6e4;
3544
3661
  /**
@@ -3555,7 +3672,16 @@ const DEFAULT_VERIFY_TIMEOUT_MS = 6e4;
3555
3672
  */
3556
3673
  function runBuildVerification(options) {
3557
3674
  const { verifyCommand, verifyTimeoutMs, projectRoot } = options;
3558
- const cmd = verifyCommand === void 0 ? DEFAULT_VERIFY_COMMAND : verifyCommand;
3675
+ let cmd;
3676
+ if (verifyCommand === void 0) {
3677
+ const detection = detectPackageManager(projectRoot);
3678
+ logger$18.info({
3679
+ packageManager: detection.packageManager,
3680
+ lockfile: detection.lockfile,
3681
+ resolvedCommand: detection.command
3682
+ }, "Build verification: resolved command via package manager detection");
3683
+ cmd = detection.command;
3684
+ } else cmd = verifyCommand;
3559
3685
  if (!cmd) return { status: "skipped" };
3560
3686
  const timeoutMs = verifyTimeoutMs ?? DEFAULT_VERIFY_TIMEOUT_MS;
3561
3687
  try {
@@ -3748,7 +3874,7 @@ function pickRecommendation(distribution, profile, totalIssues, reviewCycles, la
3748
3874
 
3749
3875
  //#endregion
3750
3876
  //#region src/modules/compiled-workflows/prompt-assembler.ts
3751
- const logger$15 = createLogger("compiled-workflows:prompt-assembler");
3877
+ const logger$17 = createLogger("compiled-workflows:prompt-assembler");
3752
3878
  /**
3753
3879
  * Assemble a final prompt from a template and sections map.
3754
3880
  *
@@ -3773,7 +3899,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
3773
3899
  tokenCount,
3774
3900
  truncated: false
3775
3901
  };
3776
- logger$15.warn({
3902
+ logger$17.warn({
3777
3903
  tokenCount,
3778
3904
  ceiling: tokenCeiling
3779
3905
  }, "Prompt exceeds token ceiling — truncating optional sections");
@@ -3789,10 +3915,10 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
3789
3915
  const targetSectionTokens = Math.max(0, currentSectionTokens - overBy);
3790
3916
  if (targetSectionTokens === 0) {
3791
3917
  contentMap[section.name] = "";
3792
- logger$15.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
3918
+ logger$17.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
3793
3919
  } else {
3794
3920
  contentMap[section.name] = truncateToTokens(section.content, targetSectionTokens);
3795
- logger$15.warn({
3921
+ logger$17.warn({
3796
3922
  sectionName: section.name,
3797
3923
  targetSectionTokens
3798
3924
  }, "Section truncated to fit token budget");
@@ -3803,7 +3929,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
3803
3929
  }
3804
3930
  if (tokenCount <= tokenCeiling) break;
3805
3931
  }
3806
- if (tokenCount > tokenCeiling) logger$15.warn({
3932
+ if (tokenCount > tokenCeiling) logger$17.warn({
3807
3933
  tokenCount,
3808
3934
  ceiling: tokenCeiling
3809
3935
  }, "Required sections alone exceed token ceiling — returning over-budget prompt");
@@ -3890,6 +4016,20 @@ const CodeReviewIssueSchema = z.object({
3890
4016
  line: coerceOptionalNumber
3891
4017
  });
3892
4018
  /**
4019
+ * Schema for a single entry in the AC verification checklist emitted by
4020
+ * the code-review sub-agent. Each entry maps an acceptance criterion to a
4021
+ * status determination backed by concrete evidence from the diff.
4022
+ */
4023
+ const AcChecklistEntrySchema = z.object({
4024
+ ac_id: z.string(),
4025
+ status: z.enum([
4026
+ "met",
4027
+ "not_met",
4028
+ "partial"
4029
+ ]),
4030
+ evidence: z.string()
4031
+ });
4032
+ /**
3893
4033
  * Compute the verdict from the issue list using deterministic rules.
3894
4034
  *
3895
4035
  * The agent reports issues with severities; the pipeline computes the
@@ -3911,7 +4051,8 @@ function computeVerdict(issueList) {
3911
4051
  /**
3912
4052
  * Schema for the YAML output contract of the code-review sub-agent.
3913
4053
  *
3914
- * The agent must emit YAML with verdict, issues count, and issue_list.
4054
+ * The agent must emit YAML with verdict, issues count, issue_list, and
4055
+ * optionally an ac_checklist mapping each AC to its implementation status.
3915
4056
  * Example:
3916
4057
  * verdict: NEEDS_MINOR_FIXES
3917
4058
  * issues: 3
@@ -3920,11 +4061,20 @@ function computeVerdict(issueList) {
3920
4061
  * description: "Missing error handling in createFoo()"
3921
4062
  * file: "src/modules/foo/foo.ts"
3922
4063
  * line: 42
4064
+ * ac_checklist:
4065
+ * - ac_id: AC1
4066
+ * status: met
4067
+ * evidence: "Implemented in src/modules/foo/foo.ts:createFoo()"
4068
+ * - ac_id: AC2
4069
+ * status: not_met
4070
+ * evidence: "No implementation found for getConstraints()"
3923
4071
  * notes: "Generally clean implementation."
3924
4072
  *
3925
- * The transform auto-corrects the issues count and recomputes the verdict
3926
- * from the issue list. The agent's original verdict is preserved as
3927
- * `agentVerdict` for logging and debugging.
4073
+ * The transform:
4074
+ * 1. Auto-corrects the issues count from issue_list length.
4075
+ * 2. Auto-injects a major issue for each not_met AC not already flagged.
4076
+ * 3. Recomputes the verdict from the (possibly augmented) issue list.
4077
+ * The agent's original verdict is preserved as `agentVerdict` for logging.
3928
4078
  */
3929
4079
  const CodeReviewResultSchema = z.object({
3930
4080
  verdict: z.enum([
@@ -3934,13 +4084,27 @@ const CodeReviewResultSchema = z.object({
3934
4084
  ]),
3935
4085
  issues: coerceNumber,
3936
4086
  issue_list: z.array(CodeReviewIssueSchema),
4087
+ ac_checklist: z.array(AcChecklistEntrySchema).default([]),
3937
4088
  notes: z.string().optional()
3938
- }).transform((data) => ({
3939
- ...data,
3940
- issues: data.issue_list.length,
3941
- agentVerdict: data.verdict,
3942
- verdict: computeVerdict(data.issue_list)
3943
- }));
4089
+ }).transform((data) => {
4090
+ const augmentedIssueList = [...data.issue_list];
4091
+ for (const entry of data.ac_checklist) if (entry.status === "not_met") {
4092
+ const acIdPattern = new RegExp(`\\b${entry.ac_id}\\b`);
4093
+ const alreadyFlagged = augmentedIssueList.some((issue) => (issue.severity === "major" || issue.severity === "blocker") && acIdPattern.test(issue.description));
4094
+ if (!alreadyFlagged) augmentedIssueList.push({
4095
+ severity: "major",
4096
+ description: `${entry.ac_id} not implemented: ${entry.evidence}`,
4097
+ file: ""
4098
+ });
4099
+ }
4100
+ return {
4101
+ ...data,
4102
+ issue_list: augmentedIssueList,
4103
+ issues: augmentedIssueList.length,
4104
+ agentVerdict: data.verdict,
4105
+ verdict: computeVerdict(augmentedIssueList)
4106
+ };
4107
+ });
3944
4108
  /**
3945
4109
  * Schema for the YAML output contract of the test-plan sub-agent.
3946
4110
  *
@@ -4018,12 +4182,46 @@ const TestExpansionResultSchema = z.object({
4018
4182
  });
4019
4183
 
4020
4184
  //#endregion
4021
- //#region src/modules/compiled-workflows/create-story.ts
4022
- const logger$14 = createLogger("compiled-workflows:create-story");
4185
+ //#region src/modules/compiled-workflows/token-ceiling.ts
4186
+ /**
4187
+ * Default token ceilings for each compiled workflow.
4188
+ * These match the hardcoded constants previously defined inline in each workflow.
4189
+ */
4190
+ const TOKEN_CEILING_DEFAULTS = {
4191
+ "create-story": 3e3,
4192
+ "dev-story": 24e3,
4193
+ "code-review": 1e5,
4194
+ "test-plan": 8e3,
4195
+ "test-expansion": 2e4
4196
+ };
4023
4197
  /**
4024
- * Hard ceiling for the assembled create-story prompt.
4198
+ * Resolve the effective token ceiling for a workflow type.
4199
+ *
4200
+ * Returns the ceiling from `tokenCeilings` config if present and valid,
4201
+ * otherwise falls back to the hardcoded default.
4202
+ *
4203
+ * @param workflowType - One of: 'create-story', 'dev-story', 'code-review', 'test-plan', 'test-expansion'
4204
+ * @param tokenCeilings - Optional per-workflow overrides from parsed config
4205
+ * @returns `{ ceiling: number, source: 'config' | 'default' }`
4025
4206
  */
4026
- const TOKEN_CEILING$4 = 3e3;
4207
+ function getTokenCeiling(workflowType, tokenCeilings) {
4208
+ if (tokenCeilings !== void 0) {
4209
+ const configured = tokenCeilings[workflowType];
4210
+ if (configured !== void 0) return {
4211
+ ceiling: configured,
4212
+ source: "config"
4213
+ };
4214
+ }
4215
+ const defaultValue = TOKEN_CEILING_DEFAULTS[workflowType] ?? 0;
4216
+ return {
4217
+ ceiling: defaultValue,
4218
+ source: "default"
4219
+ };
4220
+ }
4221
+
4222
+ //#endregion
4223
+ //#region src/modules/compiled-workflows/create-story.ts
4224
+ const logger$16 = createLogger("compiled-workflows:create-story");
4027
4225
  /**
4028
4226
  * Execute the compiled create-story workflow.
4029
4227
  *
@@ -4043,17 +4241,23 @@ const TOKEN_CEILING$4 = 3e3;
4043
4241
  */
4044
4242
  async function runCreateStory(deps, params) {
4045
4243
  const { epicId, storyKey, pipelineRunId } = params;
4046
- logger$14.debug({
4244
+ logger$16.debug({
4047
4245
  epicId,
4048
4246
  storyKey,
4049
4247
  pipelineRunId
4050
4248
  }, "Starting create-story workflow");
4249
+ const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("create-story", deps.tokenCeilings);
4250
+ logger$16.info({
4251
+ workflow: "create-story",
4252
+ ceiling: TOKEN_CEILING,
4253
+ source: tokenCeilingSource
4254
+ }, "Token ceiling resolved");
4051
4255
  let template;
4052
4256
  try {
4053
4257
  template = await deps.pack.getPrompt("create-story");
4054
4258
  } catch (err) {
4055
4259
  const error = err instanceof Error ? err.message : String(err);
4056
- logger$14.error({ error }, "Failed to retrieve create-story prompt template");
4260
+ logger$16.error({ error }, "Failed to retrieve create-story prompt template");
4057
4261
  return {
4058
4262
  result: "failed",
4059
4263
  error: `Failed to retrieve prompt template: ${error}`,
@@ -4094,11 +4298,11 @@ async function runCreateStory(deps, params) {
4094
4298
  content: storyTemplateContent,
4095
4299
  priority: "important"
4096
4300
  }
4097
- ], TOKEN_CEILING$4);
4098
- logger$14.debug({
4301
+ ], TOKEN_CEILING);
4302
+ logger$16.debug({
4099
4303
  tokenCount,
4100
4304
  truncated,
4101
- tokenCeiling: TOKEN_CEILING$4
4305
+ tokenCeiling: TOKEN_CEILING
4102
4306
  }, "Prompt assembled for create-story");
4103
4307
  const handle = deps.dispatcher.dispatch({
4104
4308
  prompt,
@@ -4112,7 +4316,7 @@ async function runCreateStory(deps, params) {
4112
4316
  dispatchResult = await handle.result;
4113
4317
  } catch (err) {
4114
4318
  const error = err instanceof Error ? err.message : String(err);
4115
- logger$14.error({
4319
+ logger$16.error({
4116
4320
  epicId,
4117
4321
  storyKey,
4118
4322
  error
@@ -4133,7 +4337,7 @@ async function runCreateStory(deps, params) {
4133
4337
  if (dispatchResult.status === "failed") {
4134
4338
  const errorMsg = dispatchResult.parseError ?? `Dispatch failed with exit code ${dispatchResult.exitCode}`;
4135
4339
  const stderrDetail = dispatchResult.output ? ` Output: ${dispatchResult.output}` : "";
4136
- logger$14.warn({
4340
+ logger$16.warn({
4137
4341
  epicId,
4138
4342
  storyKey,
4139
4343
  exitCode: dispatchResult.exitCode
@@ -4145,7 +4349,7 @@ async function runCreateStory(deps, params) {
4145
4349
  };
4146
4350
  }
4147
4351
  if (dispatchResult.status === "timeout") {
4148
- logger$14.warn({
4352
+ logger$16.warn({
4149
4353
  epicId,
4150
4354
  storyKey
4151
4355
  }, "Create-story dispatch timed out");
@@ -4158,7 +4362,7 @@ async function runCreateStory(deps, params) {
4158
4362
  if (dispatchResult.parsed === null) {
4159
4363
  const details = dispatchResult.parseError ?? "No YAML block found in output";
4160
4364
  const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
4161
- logger$14.warn({
4365
+ logger$16.warn({
4162
4366
  epicId,
4163
4367
  storyKey,
4164
4368
  details,
@@ -4174,7 +4378,7 @@ async function runCreateStory(deps, params) {
4174
4378
  const parseResult = CreateStoryResultSchema.safeParse(dispatchResult.parsed);
4175
4379
  if (!parseResult.success) {
4176
4380
  const details = parseResult.error.message;
4177
- logger$14.warn({
4381
+ logger$16.warn({
4178
4382
  epicId,
4179
4383
  storyKey,
4180
4384
  details
@@ -4187,7 +4391,7 @@ async function runCreateStory(deps, params) {
4187
4391
  };
4188
4392
  }
4189
4393
  const parsed = parseResult.data;
4190
- logger$14.info({
4394
+ logger$16.info({
4191
4395
  epicId,
4192
4396
  storyKey,
4193
4397
  storyFile: parsed.story_file,
@@ -4209,7 +4413,7 @@ function getImplementationDecisions(deps) {
4209
4413
  try {
4210
4414
  return getDecisionsByPhase(deps.db, "implementation");
4211
4415
  } catch (err) {
4212
- logger$14.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
4416
+ logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
4213
4417
  return [];
4214
4418
  }
4215
4419
  }
@@ -4252,13 +4456,13 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
4252
4456
  if (storyKey) {
4253
4457
  const storySection = extractStorySection(shardContent, storyKey);
4254
4458
  if (storySection) {
4255
- logger$14.debug({
4459
+ logger$16.debug({
4256
4460
  epicId,
4257
4461
  storyKey
4258
4462
  }, "Extracted per-story section from epic shard");
4259
4463
  return storySection;
4260
4464
  }
4261
- logger$14.debug({
4465
+ logger$16.debug({
4262
4466
  epicId,
4263
4467
  storyKey
4264
4468
  }, "No matching story section found — using full epic shard");
@@ -4268,11 +4472,11 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
4268
4472
  if (projectRoot) {
4269
4473
  const fallback = readEpicShardFromFile(projectRoot, epicId);
4270
4474
  if (fallback) {
4271
- logger$14.info({ epicId }, "Using file-based fallback for epic shard (decisions table empty)");
4475
+ logger$16.info({ epicId }, "Using file-based fallback for epic shard (decisions table empty)");
4272
4476
  if (storyKey) {
4273
4477
  const storySection = extractStorySection(fallback, storyKey);
4274
4478
  if (storySection) {
4275
- logger$14.debug({
4479
+ logger$16.debug({
4276
4480
  epicId,
4277
4481
  storyKey
4278
4482
  }, "Extracted per-story section from file-based epic shard");
@@ -4284,7 +4488,7 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
4284
4488
  }
4285
4489
  return "";
4286
4490
  } catch (err) {
4287
- logger$14.warn({
4491
+ logger$16.warn({
4288
4492
  epicId,
4289
4493
  error: err instanceof Error ? err.message : String(err)
4290
4494
  }, "Failed to retrieve epic shard");
@@ -4301,7 +4505,7 @@ function getPrevDevNotes(decisions, epicId) {
4301
4505
  if (devNotes.length === 0) return "";
4302
4506
  return devNotes[devNotes.length - 1].value;
4303
4507
  } catch (err) {
4304
- logger$14.warn({
4508
+ logger$16.warn({
4305
4509
  epicId,
4306
4510
  error: err instanceof Error ? err.message : String(err)
4307
4511
  }, "Failed to retrieve prev dev notes");
@@ -4321,13 +4525,13 @@ function getArchConstraints$2(deps) {
4321
4525
  if (deps.projectRoot) {
4322
4526
  const fallback = readArchConstraintsFromFile(deps.projectRoot);
4323
4527
  if (fallback) {
4324
- logger$14.info("Using file-based fallback for architecture constraints (decisions table empty)");
4528
+ logger$16.info("Using file-based fallback for architecture constraints (decisions table empty)");
4325
4529
  return fallback;
4326
4530
  }
4327
4531
  }
4328
4532
  return "";
4329
4533
  } catch (err) {
4330
- logger$14.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
4534
+ logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
4331
4535
  return "";
4332
4536
  }
4333
4537
  }
@@ -4347,7 +4551,7 @@ function readEpicShardFromFile(projectRoot, epicId) {
4347
4551
  const match = pattern.exec(content);
4348
4552
  return match ? match[0].trim() : "";
4349
4553
  } catch (err) {
4350
- logger$14.warn({
4554
+ logger$16.warn({
4351
4555
  epicId,
4352
4556
  error: err instanceof Error ? err.message : String(err)
4353
4557
  }, "File-based epic shard fallback failed");
@@ -4370,7 +4574,7 @@ function readArchConstraintsFromFile(projectRoot) {
4370
4574
  const content = readFileSync$1(archPath, "utf-8");
4371
4575
  return content.slice(0, 1500);
4372
4576
  } catch (err) {
4373
- logger$14.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
4577
+ logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
4374
4578
  return "";
4375
4579
  }
4376
4580
  }
@@ -4383,7 +4587,7 @@ async function getStoryTemplate(deps) {
4383
4587
  try {
4384
4588
  return await deps.pack.getTemplate("story");
4385
4589
  } catch (err) {
4386
- logger$14.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
4590
+ logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
4387
4591
  return "";
4388
4592
  }
4389
4593
  }
@@ -4420,7 +4624,7 @@ async function isValidStoryFile(filePath) {
4420
4624
 
4421
4625
  //#endregion
4422
4626
  //#region src/modules/compiled-workflows/git-helpers.ts
4423
- const logger$13 = createLogger("compiled-workflows:git-helpers");
4627
+ const logger$15 = createLogger("compiled-workflows:git-helpers");
4424
4628
  /**
4425
4629
  * Capture the full git diff for HEAD (working tree vs current commit).
4426
4630
  *
@@ -4516,7 +4720,7 @@ async function stageIntentToAdd(files, workingDirectory) {
4516
4720
  if (files.length === 0) return;
4517
4721
  const existing = files.filter((f) => {
4518
4722
  const exists = existsSync$1(f);
4519
- if (!exists) logger$13.debug({ file: f }, "Skipping nonexistent file in stageIntentToAdd");
4723
+ if (!exists) logger$15.debug({ file: f }, "Skipping nonexistent file in stageIntentToAdd");
4520
4724
  return exists;
4521
4725
  });
4522
4726
  if (existing.length === 0) return;
@@ -4550,7 +4754,7 @@ async function runGitCommand(args, cwd, logLabel) {
4550
4754
  stderr += chunk.toString("utf-8");
4551
4755
  });
4552
4756
  proc.on("error", (err) => {
4553
- logger$13.warn({
4757
+ logger$15.warn({
4554
4758
  label: logLabel,
4555
4759
  cwd,
4556
4760
  error: err.message
@@ -4559,7 +4763,7 @@ async function runGitCommand(args, cwd, logLabel) {
4559
4763
  });
4560
4764
  proc.on("close", (code) => {
4561
4765
  if (code !== 0) {
4562
- logger$13.warn({
4766
+ logger$15.warn({
4563
4767
  label: logLabel,
4564
4768
  cwd,
4565
4769
  code,
@@ -4575,7 +4779,7 @@ async function runGitCommand(args, cwd, logLabel) {
4575
4779
 
4576
4780
  //#endregion
4577
4781
  //#region src/modules/implementation-orchestrator/project-findings.ts
4578
- const logger$12 = createLogger("project-findings");
4782
+ const logger$14 = createLogger("project-findings");
4579
4783
  /** Maximum character length for the findings summary */
4580
4784
  const MAX_CHARS = 2e3;
4581
4785
  /**
@@ -4630,7 +4834,7 @@ function getProjectFindings(db) {
4630
4834
  if (summary.length > MAX_CHARS) summary = summary.slice(0, MAX_CHARS - 3) + "...";
4631
4835
  return summary;
4632
4836
  } catch (err) {
4633
- logger$12.warn({ err }, "Failed to query project findings (graceful fallback)");
4837
+ logger$14.warn({ err }, "Failed to query project findings (graceful fallback)");
4634
4838
  return "";
4635
4839
  }
4636
4840
  }
@@ -4651,11 +4855,119 @@ function extractRecurringPatterns(outcomes) {
4651
4855
  return [...patternCounts.entries()].filter(([, count]) => count >= 2).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => `${pattern} (${count} occurrences)`);
4652
4856
  }
4653
4857
 
4858
+ //#endregion
4859
+ //#region src/modules/compiled-workflows/story-complexity.ts
4860
+ const logger$13 = createLogger("compiled-workflows:story-complexity");
4861
+ /**
4862
+ * Compute a complexity score from story markdown content.
4863
+ *
4864
+ * Returns { taskCount, subtaskCount, fileCount, complexityScore }.
4865
+ * All counts default to 0 when the corresponding sections are absent.
4866
+ *
4867
+ * @param storyContent - Raw markdown content of the story file
4868
+ */
4869
+ function computeStoryComplexity(storyContent) {
4870
+ const taskCount = countTopLevelTasks(storyContent);
4871
+ const subtaskCount = countSubtasks(storyContent);
4872
+ const fileCount = countFilesInLayout(storyContent);
4873
+ const complexityScore = Math.round(taskCount + subtaskCount * .5 + fileCount * .5);
4874
+ return {
4875
+ taskCount,
4876
+ subtaskCount,
4877
+ fileCount,
4878
+ complexityScore
4879
+ };
4880
+ }
4881
+ /**
4882
+ * Compute the resolved maxTurns for a dev-story dispatch.
4883
+ *
4884
+ * base 75 turns for score <= 10, +10 turns per additional complexity point, capped at 200.
4885
+ *
4886
+ * @param complexityScore - Score returned by computeStoryComplexity
4887
+ */
4888
+ function resolveDevStoryMaxTurns(complexityScore) {
4889
+ return Math.min(200, 75 + Math.max(0, complexityScore - 10) * 10);
4890
+ }
4891
+ /**
4892
+ * Compute the resolved maxTurns for a fix-story (major-rework) dispatch.
4893
+ *
4894
+ * base 50 turns for score <= 10, +10 turns per additional complexity point, capped at 150.
4895
+ *
4896
+ * @param complexityScore - Score returned by computeStoryComplexity
4897
+ */
4898
+ function resolveFixStoryMaxTurns(complexityScore) {
4899
+ return Math.min(150, 50 + Math.max(0, complexityScore - 10) * 10);
4900
+ }
4901
+ /**
4902
+ * Log the complexity result at info level.
4903
+ *
4904
+ * Emits a structured log with storyKey, taskCount, subtaskCount, fileCount,
4905
+ * complexityScore, and resolvedMaxTurns so operators can observe turn-limit scaling.
4906
+ *
4907
+ * @param storyKey - Story identifier (e.g. "24-6")
4908
+ * @param complexity - Result from computeStoryComplexity
4909
+ * @param resolvedMaxTurns - Turn limit resolved for this dispatch
4910
+ */
4911
+ function logComplexityResult(storyKey, complexity, resolvedMaxTurns) {
4912
+ logger$13.info({
4913
+ storyKey,
4914
+ taskCount: complexity.taskCount,
4915
+ subtaskCount: complexity.subtaskCount,
4916
+ fileCount: complexity.fileCount,
4917
+ complexityScore: complexity.complexityScore,
4918
+ resolvedMaxTurns
4919
+ }, "Story complexity computed");
4920
+ }
4921
+ /**
4922
+ * Count top-level task lines matching `- [ ] Task N:` (with literal "Task" keyword).
4923
+ */
4924
+ function countTopLevelTasks(content) {
4925
+ const taskPattern = /^- \[ \] Task \d+:/gm;
4926
+ return (content.match(taskPattern) ?? []).length;
4927
+ }
4928
+ /**
4929
+ * Count nested subtask lines — `- [ ]` lines that begin with one or more spaces or tabs.
4930
+ *
4931
+ * IMPORTANT: Use `[ \t]+` (space/tab only), NOT `\s+`, because `\s` includes `\n`.
4932
+ * With multiline `/gm`, using `\s+` causes false matches when an empty line (`\n`)
4933
+ * precedes a top-level `- [ ]` line — the `\n` gets consumed as "whitespace".
4934
+ */
4935
+ function countSubtasks(content) {
4936
+ const subtaskPattern = /^[ \t]+- \[ \]/gm;
4937
+ return (content.match(subtaskPattern) ?? []).length;
4938
+ }
4939
+ /**
4940
+ * Count file entries inside a "File Layout" fenced code block.
4941
+ *
4942
+ * Uses a split-based section extraction to avoid regex lazy-matching pitfalls.
4943
+ * Finds a heading containing "File Layout", extracts section content up to the
4944
+ * next heading (or end of string), then finds fenced code blocks within it.
4945
+ *
4946
+ * Returns 0 when no File Layout section or fenced code block is found.
4947
+ */
4948
+ function countFilesInLayout(content) {
4949
+ const headingMatch = content.match(/^#{2,4}\s+File\s+Layout\s*$/im);
4950
+ if (!headingMatch || headingMatch.index === void 0) return 0;
4951
+ const afterHeading = content.slice(headingMatch.index + headingMatch[0].length);
4952
+ const nextHeadingMatch = afterHeading.match(/^#{2,4}\s+/m);
4953
+ const sectionContent = nextHeadingMatch?.index !== void 0 ? afterHeading.slice(0, nextHeadingMatch.index) : afterHeading;
4954
+ const codeBlocks = sectionContent.match(/```[\s\S]*?```/g);
4955
+ if (!codeBlocks) return 0;
4956
+ const fileExtPattern = /\.(ts|js|json|sql|yaml|yml|md)\b/;
4957
+ let count = 0;
4958
+ for (const block of codeBlocks) {
4959
+ const lines = block.split("\n");
4960
+ for (const line of lines) {
4961
+ if (line.trimStart().startsWith("```")) continue;
4962
+ if (fileExtPattern.test(line)) count++;
4963
+ }
4964
+ }
4965
+ return count;
4966
+ }
4967
+
4654
4968
  //#endregion
4655
4969
  //#region src/modules/compiled-workflows/dev-story.ts
4656
- const logger$11 = createLogger("compiled-workflows:dev-story");
4657
- /** Hard token ceiling for the assembled dev-story prompt */
4658
- const TOKEN_CEILING$3 = 24e3;
4970
+ const logger$12 = createLogger("compiled-workflows:dev-story");
4659
4971
  /** Default timeout for dev-story dispatches in milliseconds (30 min) */
4660
4972
  const DEFAULT_TIMEOUT_MS$1 = 18e5;
4661
4973
  /** Default Vitest test patterns injected when no test-pattern decisions exist */
@@ -4678,10 +4990,16 @@ const DEFAULT_VITEST_PATTERNS = `## Test Patterns (defaults)
4678
4990
  */
4679
4991
  async function runDevStory(deps, params) {
4680
4992
  const { storyKey, storyFilePath, taskScope, priorFiles } = params;
4681
- logger$11.info({
4993
+ logger$12.info({
4682
4994
  storyKey,
4683
4995
  storyFilePath
4684
4996
  }, "Starting compiled dev-story workflow");
4997
+ const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("dev-story", deps.tokenCeilings);
4998
+ logger$12.info({
4999
+ workflow: "dev-story",
5000
+ ceiling: TOKEN_CEILING,
5001
+ source: tokenCeilingSource
5002
+ }, "Token ceiling resolved");
4685
5003
  const devStoryContextTemplate = {
4686
5004
  taskType: "dev-story",
4687
5005
  sections: [{
@@ -4720,10 +5038,10 @@ async function runDevStory(deps, params) {
4720
5038
  let template;
4721
5039
  try {
4722
5040
  template = await deps.pack.getPrompt("dev-story");
4723
- logger$11.debug({ storyKey }, "Retrieved dev-story prompt template from pack");
5041
+ logger$12.debug({ storyKey }, "Retrieved dev-story prompt template from pack");
4724
5042
  } catch (err) {
4725
5043
  const error = err instanceof Error ? err.message : String(err);
4726
- logger$11.error({
5044
+ logger$12.error({
4727
5045
  storyKey,
4728
5046
  error
4729
5047
  }, "Failed to retrieve dev-story prompt template");
@@ -4734,14 +5052,14 @@ async function runDevStory(deps, params) {
4734
5052
  storyContent = await readFile$1(storyFilePath, "utf-8");
4735
5053
  } catch (err) {
4736
5054
  if (err.code === "ENOENT") {
4737
- logger$11.error({
5055
+ logger$12.error({
4738
5056
  storyKey,
4739
5057
  storyFilePath
4740
5058
  }, "Story file not found");
4741
5059
  return makeFailureResult("story_file_not_found");
4742
5060
  }
4743
5061
  const error = err instanceof Error ? err.message : String(err);
4744
- logger$11.error({
5062
+ logger$12.error({
4745
5063
  storyKey,
4746
5064
  storyFilePath,
4747
5065
  error
@@ -4749,29 +5067,32 @@ async function runDevStory(deps, params) {
4749
5067
  return makeFailureResult(`story_file_read_error: ${error}`);
4750
5068
  }
4751
5069
  if (storyContent.trim().length === 0) {
4752
- logger$11.error({
5070
+ logger$12.error({
4753
5071
  storyKey,
4754
5072
  storyFilePath
4755
5073
  }, "Story file is empty");
4756
5074
  return makeFailureResult("story_file_empty");
4757
5075
  }
5076
+ const complexity = computeStoryComplexity(storyContent);
5077
+ const resolvedMaxTurns = resolveDevStoryMaxTurns(complexity.complexityScore);
5078
+ logComplexityResult(storyKey, complexity, resolvedMaxTurns);
4758
5079
  let testPatternsContent = "";
4759
5080
  try {
4760
5081
  const solutioningDecisions = getDecisionsByPhase(deps.db, "solutioning");
4761
5082
  const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
4762
5083
  if (testPatternDecisions.length > 0) {
4763
5084
  testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
4764
- logger$11.debug({
5085
+ logger$12.debug({
4765
5086
  storyKey,
4766
5087
  count: testPatternDecisions.length
4767
5088
  }, "Loaded test patterns from decision store");
4768
5089
  } else {
4769
5090
  testPatternsContent = DEFAULT_VITEST_PATTERNS;
4770
- logger$11.debug({ storyKey }, "No test-pattern decisions found — using default Vitest patterns");
5091
+ logger$12.debug({ storyKey }, "No test-pattern decisions found — using default Vitest patterns");
4771
5092
  }
4772
5093
  } catch (err) {
4773
5094
  const error = err instanceof Error ? err.message : String(err);
4774
- logger$11.warn({
5095
+ logger$12.warn({
4775
5096
  storyKey,
4776
5097
  error
4777
5098
  }, "Failed to load test patterns — using defaults");
@@ -4786,7 +5107,7 @@ async function runDevStory(deps, params) {
4786
5107
  const findings = getProjectFindings(deps.db);
4787
5108
  if (findings.length > 0) {
4788
5109
  priorFindingsContent = "Previous pipeline runs encountered these issues — avoid repeating them:\n\n" + findings;
4789
- logger$11.debug({
5110
+ logger$12.debug({
4790
5111
  storyKey,
4791
5112
  findingsLen: findings.length
4792
5113
  }, "Injecting prior findings into dev-story prompt");
@@ -4806,7 +5127,7 @@ async function runDevStory(deps, params) {
4806
5127
  if (plan.test_categories && plan.test_categories.length > 0) parts.push(`\n### Categories: ${plan.test_categories.join(", ")}`);
4807
5128
  if (plan.coverage_notes) parts.push(`\n### Coverage Notes\n${plan.coverage_notes}`);
4808
5129
  testPlanContent = parts.join("\n");
4809
- logger$11.debug({ storyKey }, "Injecting test plan into dev-story prompt");
5130
+ logger$12.debug({ storyKey }, "Injecting test plan into dev-story prompt");
4810
5131
  }
4811
5132
  } catch {}
4812
5133
  const sections = [
@@ -4851,11 +5172,11 @@ async function runDevStory(deps, params) {
4851
5172
  priority: "optional"
4852
5173
  }
4853
5174
  ];
4854
- const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING$3);
4855
- logger$11.info({
5175
+ const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING);
5176
+ logger$12.info({
4856
5177
  storyKey,
4857
5178
  tokenCount,
4858
- ceiling: TOKEN_CEILING$3,
5179
+ ceiling: TOKEN_CEILING,
4859
5180
  truncated
4860
5181
  }, "Assembled dev-story prompt");
4861
5182
  let dispatchResult;
@@ -4866,12 +5187,13 @@ async function runDevStory(deps, params) {
4866
5187
  taskType: "dev-story",
4867
5188
  timeout: DEFAULT_TIMEOUT_MS$1,
4868
5189
  outputSchema: DevStoryResultSchema,
5190
+ maxTurns: resolvedMaxTurns,
4869
5191
  ...deps.projectRoot !== void 0 ? { workingDirectory: deps.projectRoot } : {}
4870
5192
  });
4871
5193
  dispatchResult = await handle.result;
4872
5194
  } catch (err) {
4873
5195
  const error = err instanceof Error ? err.message : String(err);
4874
- logger$11.error({
5196
+ logger$12.error({
4875
5197
  storyKey,
4876
5198
  error
4877
5199
  }, "Dispatch threw an unexpected error");
@@ -4882,11 +5204,11 @@ async function runDevStory(deps, params) {
4882
5204
  output: dispatchResult.tokenEstimate.output
4883
5205
  };
4884
5206
  if (dispatchResult.status === "timeout") {
4885
- logger$11.error({
5207
+ logger$12.error({
4886
5208
  storyKey,
4887
5209
  durationMs: dispatchResult.durationMs
4888
5210
  }, "Dev-story dispatch timed out");
4889
- if (dispatchResult.output.length > 0) logger$11.info({
5211
+ if (dispatchResult.output.length > 0) logger$12.info({
4890
5212
  storyKey,
4891
5213
  partialOutput: dispatchResult.output.slice(0, 500)
4892
5214
  }, "Partial output before timeout");
@@ -4896,12 +5218,12 @@ async function runDevStory(deps, params) {
4896
5218
  };
4897
5219
  }
4898
5220
  if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
4899
- logger$11.error({
5221
+ logger$12.error({
4900
5222
  storyKey,
4901
5223
  exitCode: dispatchResult.exitCode,
4902
5224
  status: dispatchResult.status
4903
5225
  }, "Dev-story dispatch failed");
4904
- if (dispatchResult.output.length > 0) logger$11.info({
5226
+ if (dispatchResult.output.length > 0) logger$12.info({
4905
5227
  storyKey,
4906
5228
  partialOutput: dispatchResult.output.slice(0, 500)
4907
5229
  }, "Partial output from failed dispatch");
@@ -4913,7 +5235,7 @@ async function runDevStory(deps, params) {
4913
5235
  if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
4914
5236
  const details = dispatchResult.parseError ?? "parsed result was null";
4915
5237
  const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
4916
- logger$11.error({
5238
+ logger$12.error({
4917
5239
  storyKey,
4918
5240
  parseError: details,
4919
5241
  rawOutputSnippet: rawSnippet
@@ -4921,12 +5243,12 @@ async function runDevStory(deps, params) {
4921
5243
  let filesModified = [];
4922
5244
  try {
4923
5245
  filesModified = await getGitChangedFiles(deps.projectRoot ?? process.cwd());
4924
- if (filesModified.length > 0) logger$11.info({
5246
+ if (filesModified.length > 0) logger$12.info({
4925
5247
  storyKey,
4926
5248
  fileCount: filesModified.length
4927
5249
  }, "Recovered files_modified from git status (YAML fallback)");
4928
5250
  } catch (err) {
4929
- logger$11.warn({
5251
+ logger$12.warn({
4930
5252
  storyKey,
4931
5253
  error: err instanceof Error ? err.message : String(err)
4932
5254
  }, "Failed to recover files_modified from git");
@@ -4943,7 +5265,7 @@ async function runDevStory(deps, params) {
4943
5265
  };
4944
5266
  }
4945
5267
  const parsed = dispatchResult.parsed;
4946
- logger$11.info({
5268
+ logger$12.info({
4947
5269
  storyKey,
4948
5270
  result: parsed.result,
4949
5271
  acMet: parsed.ac_met.length
@@ -5082,13 +5404,7 @@ function extractFilesInScope(storyContent) {
5082
5404
 
5083
5405
  //#endregion
5084
5406
  //#region src/modules/compiled-workflows/code-review.ts
5085
- const logger$10 = createLogger("compiled-workflows:code-review");
5086
- /**
5087
- * Hard token ceiling for the assembled code-review prompt (50,000 tokens).
5088
- * Quality reviews require seeing actual code diffs, not just file names.
5089
- * // TODO: consider externalizing to pack config when multiple packs exist
5090
- */
5091
- const TOKEN_CEILING$2 = 1e5;
5407
+ const logger$11 = createLogger("compiled-workflows:code-review");
5092
5408
  /**
5093
5409
  * Default fallback result when dispatch fails or times out.
5094
5410
  * Uses NEEDS_MINOR_FIXES (not NEEDS_MAJOR_REWORK) so a parse/schema failure
@@ -5126,18 +5442,24 @@ function defaultFailResult(error, tokenUsage) {
5126
5442
  async function runCodeReview(deps, params) {
5127
5443
  const { storyKey, storyFilePath, workingDirectory, pipelineRunId, filesModified, previousIssues } = params;
5128
5444
  const cwd = workingDirectory ?? process.cwd();
5129
- logger$10.debug({
5445
+ logger$11.debug({
5130
5446
  storyKey,
5131
5447
  storyFilePath,
5132
5448
  cwd,
5133
5449
  pipelineRunId
5134
5450
  }, "Starting code-review workflow");
5451
+ const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("code-review", deps.tokenCeilings);
5452
+ logger$11.info({
5453
+ workflow: "code-review",
5454
+ ceiling: TOKEN_CEILING,
5455
+ source: tokenCeilingSource
5456
+ }, "Token ceiling resolved");
5135
5457
  let template;
5136
5458
  try {
5137
5459
  template = await deps.pack.getPrompt("code-review");
5138
5460
  } catch (err) {
5139
5461
  const error = err instanceof Error ? err.message : String(err);
5140
- logger$10.error({ error }, "Failed to retrieve code-review prompt template");
5462
+ logger$11.error({ error }, "Failed to retrieve code-review prompt template");
5141
5463
  return defaultFailResult(`Failed to retrieve prompt template: ${error}`, {
5142
5464
  input: 0,
5143
5465
  output: 0
@@ -5148,7 +5470,7 @@ async function runCodeReview(deps, params) {
5148
5470
  storyContent = await readFile$1(storyFilePath, "utf-8");
5149
5471
  } catch (err) {
5150
5472
  const error = err instanceof Error ? err.message : String(err);
5151
- logger$10.error({
5473
+ logger$11.error({
5152
5474
  storyFilePath,
5153
5475
  error
5154
5476
  }, "Failed to read story file");
@@ -5166,16 +5488,16 @@ async function runCodeReview(deps, params) {
5166
5488
  if (filesModified && filesModified.length > 0) {
5167
5489
  const scopedDiff = await getGitDiffForFiles(filesModified, cwd);
5168
5490
  const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
5169
- if (scopedTotal <= TOKEN_CEILING$2) {
5491
+ if (scopedTotal <= TOKEN_CEILING) {
5170
5492
  gitDiffContent = scopedDiff;
5171
- logger$10.debug({
5493
+ logger$11.debug({
5172
5494
  fileCount: filesModified.length,
5173
5495
  tokenCount: scopedTotal
5174
5496
  }, "Using scoped file diff");
5175
5497
  } else {
5176
- logger$10.warn({
5498
+ logger$11.warn({
5177
5499
  estimatedTotal: scopedTotal,
5178
- ceiling: TOKEN_CEILING$2,
5500
+ ceiling: TOKEN_CEILING,
5179
5501
  fileCount: filesModified.length
5180
5502
  }, "Scoped diff exceeds token ceiling — falling back to stat-only summary");
5181
5503
  gitDiffContent = await getGitDiffStatSummary(cwd);
@@ -5185,17 +5507,17 @@ async function runCodeReview(deps, params) {
5185
5507
  await stageIntentToAdd(changedFiles, cwd);
5186
5508
  const fullDiff = await getGitDiffSummary(cwd);
5187
5509
  const fullTotal = nonDiffTokens + countTokens(fullDiff);
5188
- if (fullTotal <= TOKEN_CEILING$2) gitDiffContent = fullDiff;
5510
+ if (fullTotal <= TOKEN_CEILING) gitDiffContent = fullDiff;
5189
5511
  else {
5190
- logger$10.warn({
5512
+ logger$11.warn({
5191
5513
  estimatedTotal: fullTotal,
5192
- ceiling: TOKEN_CEILING$2
5514
+ ceiling: TOKEN_CEILING
5193
5515
  }, "Full git diff would exceed token ceiling — using stat-only summary");
5194
5516
  gitDiffContent = await getGitDiffStatSummary(cwd);
5195
5517
  }
5196
5518
  }
5197
5519
  if (gitDiffContent.trim().length === 0) {
5198
- logger$10.info({ storyKey }, "Empty git diff — skipping review with SHIP_IT");
5520
+ logger$11.info({ storyKey }, "Empty git diff — skipping review with SHIP_IT");
5199
5521
  return {
5200
5522
  verdict: "SHIP_IT",
5201
5523
  issues: 0,
@@ -5220,7 +5542,7 @@ async function runCodeReview(deps, params) {
5220
5542
  const findings = getProjectFindings(deps.db);
5221
5543
  if (findings.length > 0) {
5222
5544
  priorFindingsContent = "Previous reviews found these recurring patterns — pay special attention:\n\n" + findings;
5223
- logger$10.debug({
5545
+ logger$11.debug({
5224
5546
  storyKey,
5225
5547
  findingsLen: findings.length
5226
5548
  }, "Injecting prior findings into code-review prompt");
@@ -5253,12 +5575,12 @@ async function runCodeReview(deps, params) {
5253
5575
  priority: "optional"
5254
5576
  }
5255
5577
  ];
5256
- const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING$2);
5257
- if (assembleResult.truncated) logger$10.warn({
5578
+ const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
5579
+ if (assembleResult.truncated) logger$11.warn({
5258
5580
  storyKey,
5259
5581
  tokenCount: assembleResult.tokenCount
5260
5582
  }, "Code-review prompt truncated to fit token ceiling");
5261
- logger$10.debug({
5583
+ logger$11.debug({
5262
5584
  storyKey,
5263
5585
  tokenCount: assembleResult.tokenCount,
5264
5586
  truncated: assembleResult.truncated
@@ -5276,7 +5598,7 @@ async function runCodeReview(deps, params) {
5276
5598
  dispatchResult = await handle.result;
5277
5599
  } catch (err) {
5278
5600
  const error = err instanceof Error ? err.message : String(err);
5279
- logger$10.error({
5601
+ logger$11.error({
5280
5602
  storyKey,
5281
5603
  error
5282
5604
  }, "Code-review dispatch threw unexpected error");
@@ -5292,7 +5614,7 @@ async function runCodeReview(deps, params) {
5292
5614
  const rawOutput = dispatchResult.output ?? void 0;
5293
5615
  if (dispatchResult.status === "failed") {
5294
5616
  const errorMsg = `Dispatch status: failed. Exit code: ${dispatchResult.exitCode}. ${dispatchResult.parseError ?? ""} ${dispatchResult.output ? `Stderr: ${dispatchResult.output}` : ""}`.trim();
5295
- logger$10.warn({
5617
+ logger$11.warn({
5296
5618
  storyKey,
5297
5619
  exitCode: dispatchResult.exitCode
5298
5620
  }, "Code-review dispatch failed");
@@ -5302,7 +5624,7 @@ async function runCodeReview(deps, params) {
5302
5624
  };
5303
5625
  }
5304
5626
  if (dispatchResult.status === "timeout") {
5305
- logger$10.warn({ storyKey }, "Code-review dispatch timed out");
5627
+ logger$11.warn({ storyKey }, "Code-review dispatch timed out");
5306
5628
  return {
5307
5629
  ...defaultFailResult("Dispatch status: timeout. The agent did not complete within the allowed time.", tokenUsage),
5308
5630
  rawOutput
@@ -5310,7 +5632,7 @@ async function runCodeReview(deps, params) {
5310
5632
  }
5311
5633
  if (dispatchResult.parsed === null) {
5312
5634
  const details = dispatchResult.parseError ?? "No YAML block found in output";
5313
- logger$10.warn({
5635
+ logger$11.warn({
5314
5636
  storyKey,
5315
5637
  details
5316
5638
  }, "Code-review output schema validation failed");
@@ -5327,7 +5649,7 @@ async function runCodeReview(deps, params) {
5327
5649
  const parseResult = CodeReviewResultSchema.safeParse(dispatchResult.parsed);
5328
5650
  if (!parseResult.success) {
5329
5651
  const details = parseResult.error.message;
5330
- logger$10.warn({
5652
+ logger$11.warn({
5331
5653
  storyKey,
5332
5654
  details
5333
5655
  }, "Code-review output failed schema validation");
@@ -5342,13 +5664,13 @@ async function runCodeReview(deps, params) {
5342
5664
  };
5343
5665
  }
5344
5666
  const parsed = parseResult.data;
5345
- if (parsed.agentVerdict !== parsed.verdict) logger$10.info({
5667
+ if (parsed.agentVerdict !== parsed.verdict) logger$11.info({
5346
5668
  storyKey,
5347
5669
  agentVerdict: parsed.agentVerdict,
5348
5670
  pipelineVerdict: parsed.verdict,
5349
5671
  issues: parsed.issues
5350
5672
  }, "Pipeline overrode agent verdict based on issue severities");
5351
- logger$10.info({
5673
+ logger$11.info({
5352
5674
  storyKey,
5353
5675
  verdict: parsed.verdict,
5354
5676
  issues: parsed.issues
@@ -5373,16 +5695,14 @@ function getArchConstraints$1(deps) {
5373
5695
  if (constraints.length === 0) return "";
5374
5696
  return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
5375
5697
  } catch (err) {
5376
- logger$10.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
5698
+ logger$11.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
5377
5699
  return "";
5378
5700
  }
5379
5701
  }
5380
5702
 
5381
5703
  //#endregion
5382
5704
  //#region src/modules/compiled-workflows/test-plan.ts
5383
- const logger$9 = createLogger("compiled-workflows:test-plan");
5384
- /** Hard token ceiling for the assembled test-plan prompt */
5385
- const TOKEN_CEILING$1 = 8e3;
5705
+ const logger$10 = createLogger("compiled-workflows:test-plan");
5386
5706
  /** Default timeout for test-plan dispatches in milliseconds (5 min — lightweight call) */
5387
5707
  const DEFAULT_TIMEOUT_MS = 3e5;
5388
5708
  /**
@@ -5394,17 +5714,23 @@ const DEFAULT_TIMEOUT_MS = 3e5;
5394
5714
  */
5395
5715
  async function runTestPlan(deps, params) {
5396
5716
  const { storyKey, storyFilePath, pipelineRunId } = params;
5397
- logger$9.info({
5717
+ logger$10.info({
5398
5718
  storyKey,
5399
5719
  storyFilePath
5400
5720
  }, "Starting compiled test-plan workflow");
5721
+ const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("test-plan", deps.tokenCeilings);
5722
+ logger$10.info({
5723
+ workflow: "test-plan",
5724
+ ceiling: TOKEN_CEILING,
5725
+ source: tokenCeilingSource
5726
+ }, "Token ceiling resolved");
5401
5727
  let template;
5402
5728
  try {
5403
5729
  template = await deps.pack.getPrompt("test-plan");
5404
- logger$9.debug({ storyKey }, "Retrieved test-plan prompt template from pack");
5730
+ logger$10.debug({ storyKey }, "Retrieved test-plan prompt template from pack");
5405
5731
  } catch (err) {
5406
5732
  const error = err instanceof Error ? err.message : String(err);
5407
- logger$9.warn({
5733
+ logger$10.warn({
5408
5734
  storyKey,
5409
5735
  error
5410
5736
  }, "Failed to retrieve test-plan prompt template");
@@ -5415,14 +5741,14 @@ async function runTestPlan(deps, params) {
5415
5741
  storyContent = await readFile$1(storyFilePath, "utf-8");
5416
5742
  } catch (err) {
5417
5743
  if (err.code === "ENOENT") {
5418
- logger$9.warn({
5744
+ logger$10.warn({
5419
5745
  storyKey,
5420
5746
  storyFilePath
5421
5747
  }, "Story file not found for test planning");
5422
5748
  return makeTestPlanFailureResult("story_file_not_found");
5423
5749
  }
5424
5750
  const error = err instanceof Error ? err.message : String(err);
5425
- logger$9.warn({
5751
+ logger$10.warn({
5426
5752
  storyKey,
5427
5753
  storyFilePath,
5428
5754
  error
@@ -5433,11 +5759,11 @@ async function runTestPlan(deps, params) {
5433
5759
  name: "story_content",
5434
5760
  content: storyContent,
5435
5761
  priority: "required"
5436
- }], TOKEN_CEILING$1);
5437
- logger$9.info({
5762
+ }], TOKEN_CEILING);
5763
+ logger$10.info({
5438
5764
  storyKey,
5439
5765
  tokenCount,
5440
- ceiling: TOKEN_CEILING$1,
5766
+ ceiling: TOKEN_CEILING,
5441
5767
  truncated
5442
5768
  }, "Assembled test-plan prompt");
5443
5769
  let dispatchResult;
@@ -5453,7 +5779,7 @@ async function runTestPlan(deps, params) {
5453
5779
  dispatchResult = await handle.result;
5454
5780
  } catch (err) {
5455
5781
  const error = err instanceof Error ? err.message : String(err);
5456
- logger$9.warn({
5782
+ logger$10.warn({
5457
5783
  storyKey,
5458
5784
  error
5459
5785
  }, "Test-plan dispatch threw an unexpected error");
@@ -5464,7 +5790,7 @@ async function runTestPlan(deps, params) {
5464
5790
  output: dispatchResult.tokenEstimate.output
5465
5791
  };
5466
5792
  if (dispatchResult.status === "timeout") {
5467
- logger$9.warn({
5793
+ logger$10.warn({
5468
5794
  storyKey,
5469
5795
  durationMs: dispatchResult.durationMs
5470
5796
  }, "Test-plan dispatch timed out");
@@ -5474,7 +5800,7 @@ async function runTestPlan(deps, params) {
5474
5800
  };
5475
5801
  }
5476
5802
  if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
5477
- logger$9.warn({
5803
+ logger$10.warn({
5478
5804
  storyKey,
5479
5805
  exitCode: dispatchResult.exitCode,
5480
5806
  status: dispatchResult.status
@@ -5486,7 +5812,7 @@ async function runTestPlan(deps, params) {
5486
5812
  }
5487
5813
  if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
5488
5814
  const details = dispatchResult.parseError ?? "parsed result was null";
5489
- logger$9.warn({
5815
+ logger$10.warn({
5490
5816
  storyKey,
5491
5817
  parseError: details
5492
5818
  }, "Test-plan YAML schema validation failed");
@@ -5509,19 +5835,19 @@ async function runTestPlan(deps, params) {
5509
5835
  }),
5510
5836
  rationale: `Test plan for ${storyKey}: ${parsed.test_files.length} test files, categories: ${parsed.test_categories.join(", ")}`
5511
5837
  });
5512
- logger$9.info({
5838
+ logger$10.info({
5513
5839
  storyKey,
5514
5840
  fileCount: parsed.test_files.length,
5515
5841
  categories: parsed.test_categories
5516
5842
  }, "Test plan stored in decision store");
5517
5843
  } catch (err) {
5518
5844
  const error = err instanceof Error ? err.message : String(err);
5519
- logger$9.warn({
5845
+ logger$10.warn({
5520
5846
  storyKey,
5521
5847
  error
5522
5848
  }, "Failed to store test plan in decision store — proceeding anyway");
5523
5849
  }
5524
- logger$9.info({
5850
+ logger$10.info({
5525
5851
  storyKey,
5526
5852
  result: parsed.result
5527
5853
  }, "Test-plan workflow completed");
@@ -5552,11 +5878,7 @@ function makeTestPlanFailureResult(error) {
5552
5878
 
5553
5879
  //#endregion
5554
5880
  //#region src/modules/compiled-workflows/test-expansion.ts
5555
- const logger$8 = createLogger("compiled-workflows:test-expansion");
5556
- /**
5557
- * Hard token ceiling for the assembled test-expansion prompt (20,000 tokens).
5558
- */
5559
- const TOKEN_CEILING = 2e4;
5881
+ const logger$9 = createLogger("compiled-workflows:test-expansion");
5560
5882
  function defaultFallbackResult(error, tokenUsage) {
5561
5883
  return {
5562
5884
  expansion_priority: "low",
@@ -5586,18 +5908,24 @@ function defaultFallbackResult(error, tokenUsage) {
5586
5908
  async function runTestExpansion(deps, params) {
5587
5909
  const { storyKey, storyFilePath, pipelineRunId, filesModified, workingDirectory } = params;
5588
5910
  const cwd = workingDirectory ?? process.cwd();
5589
- logger$8.debug({
5911
+ logger$9.debug({
5590
5912
  storyKey,
5591
5913
  storyFilePath,
5592
5914
  cwd,
5593
5915
  pipelineRunId
5594
5916
  }, "Starting test-expansion workflow");
5917
+ const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("test-expansion", deps.tokenCeilings);
5918
+ logger$9.info({
5919
+ workflow: "test-expansion",
5920
+ ceiling: TOKEN_CEILING,
5921
+ source: tokenCeilingSource
5922
+ }, "Token ceiling resolved");
5595
5923
  let template;
5596
5924
  try {
5597
5925
  template = await deps.pack.getPrompt("test-expansion");
5598
5926
  } catch (err) {
5599
5927
  const error = err instanceof Error ? err.message : String(err);
5600
- logger$8.warn({ error }, "Failed to retrieve test-expansion prompt template");
5928
+ logger$9.warn({ error }, "Failed to retrieve test-expansion prompt template");
5601
5929
  return defaultFallbackResult(`Failed to retrieve prompt template: ${error}`, {
5602
5930
  input: 0,
5603
5931
  output: 0
@@ -5608,7 +5936,7 @@ async function runTestExpansion(deps, params) {
5608
5936
  storyContent = await readFile$1(storyFilePath, "utf-8");
5609
5937
  } catch (err) {
5610
5938
  const error = err instanceof Error ? err.message : String(err);
5611
- logger$8.warn({
5939
+ logger$9.warn({
5612
5940
  storyFilePath,
5613
5941
  error
5614
5942
  }, "Failed to read story file");
@@ -5628,12 +5956,12 @@ async function runTestExpansion(deps, params) {
5628
5956
  const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
5629
5957
  if (scopedTotal <= TOKEN_CEILING) {
5630
5958
  gitDiffContent = scopedDiff;
5631
- logger$8.debug({
5959
+ logger$9.debug({
5632
5960
  fileCount: filesModified.length,
5633
5961
  tokenCount: scopedTotal
5634
5962
  }, "Using scoped file diff");
5635
5963
  } else {
5636
- logger$8.warn({
5964
+ logger$9.warn({
5637
5965
  estimatedTotal: scopedTotal,
5638
5966
  ceiling: TOKEN_CEILING,
5639
5967
  fileCount: filesModified.length
@@ -5641,7 +5969,7 @@ async function runTestExpansion(deps, params) {
5641
5969
  gitDiffContent = await getGitDiffStatSummary(cwd);
5642
5970
  }
5643
5971
  } catch (err) {
5644
- logger$8.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to get git diff — proceeding with empty diff");
5972
+ logger$9.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to get git diff — proceeding with empty diff");
5645
5973
  }
5646
5974
  const sections = [
5647
5975
  {
@@ -5661,11 +5989,11 @@ async function runTestExpansion(deps, params) {
5661
5989
  }
5662
5990
  ];
5663
5991
  const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
5664
- if (assembleResult.truncated) logger$8.warn({
5992
+ if (assembleResult.truncated) logger$9.warn({
5665
5993
  storyKey,
5666
5994
  tokenCount: assembleResult.tokenCount
5667
5995
  }, "Test-expansion prompt truncated to fit token ceiling");
5668
- logger$8.debug({
5996
+ logger$9.debug({
5669
5997
  storyKey,
5670
5998
  tokenCount: assembleResult.tokenCount,
5671
5999
  truncated: assembleResult.truncated
@@ -5683,7 +6011,7 @@ async function runTestExpansion(deps, params) {
5683
6011
  dispatchResult = await handle.result;
5684
6012
  } catch (err) {
5685
6013
  const error = err instanceof Error ? err.message : String(err);
5686
- logger$8.warn({
6014
+ logger$9.warn({
5687
6015
  storyKey,
5688
6016
  error
5689
6017
  }, "Test-expansion dispatch threw unexpected error");
@@ -5698,19 +6026,19 @@ async function runTestExpansion(deps, params) {
5698
6026
  };
5699
6027
  if (dispatchResult.status === "failed") {
5700
6028
  const errorMsg = `Dispatch status: failed. Exit code: ${dispatchResult.exitCode}. ${dispatchResult.parseError ?? ""}`.trim();
5701
- logger$8.warn({
6029
+ logger$9.warn({
5702
6030
  storyKey,
5703
6031
  exitCode: dispatchResult.exitCode
5704
6032
  }, "Test-expansion dispatch failed");
5705
6033
  return defaultFallbackResult(errorMsg, tokenUsage);
5706
6034
  }
5707
6035
  if (dispatchResult.status === "timeout") {
5708
- logger$8.warn({ storyKey }, "Test-expansion dispatch timed out");
6036
+ logger$9.warn({ storyKey }, "Test-expansion dispatch timed out");
5709
6037
  return defaultFallbackResult("Dispatch status: timeout. The agent did not complete within the allowed time.", tokenUsage);
5710
6038
  }
5711
6039
  if (dispatchResult.parsed === null) {
5712
6040
  const details = dispatchResult.parseError ?? "No YAML block found in output";
5713
- logger$8.warn({
6041
+ logger$9.warn({
5714
6042
  storyKey,
5715
6043
  details
5716
6044
  }, "Test-expansion output has no parseable YAML");
@@ -5719,14 +6047,14 @@ async function runTestExpansion(deps, params) {
5719
6047
  const parseResult = TestExpansionResultSchema.safeParse(dispatchResult.parsed);
5720
6048
  if (!parseResult.success) {
5721
6049
  const details = parseResult.error.message;
5722
- logger$8.warn({
6050
+ logger$9.warn({
5723
6051
  storyKey,
5724
6052
  details
5725
6053
  }, "Test-expansion output failed schema validation");
5726
6054
  return defaultFallbackResult(`schema_validation_failed: ${details}`, tokenUsage);
5727
6055
  }
5728
6056
  const parsed = parseResult.data;
5729
- logger$8.info({
6057
+ logger$9.info({
5730
6058
  storyKey,
5731
6059
  expansion_priority: parsed.expansion_priority,
5732
6060
  coverage_gaps: parsed.coverage_gaps.length,
@@ -5751,7 +6079,7 @@ function getArchConstraints(deps) {
5751
6079
  if (constraints.length === 0) return "";
5752
6080
  return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
5753
6081
  } catch (err) {
5754
- logger$8.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
6082
+ logger$9.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
5755
6083
  return "";
5756
6084
  }
5757
6085
  }
@@ -6064,7 +6392,7 @@ function detectConflictGroups(storyKeys, config) {
6064
6392
 
6065
6393
  //#endregion
6066
6394
  //#region src/cli/commands/health.ts
6067
- const logger$7 = createLogger("health-cmd");
6395
+ const logger$8 = createLogger("health-cmd");
6068
6396
  /** Default stall threshold in seconds — also used by supervisor default */
6069
6397
  const DEFAULT_STALL_THRESHOLD_SECONDS = 600;
6070
6398
  /**
@@ -6329,7 +6657,7 @@ async function runHealthAction(options) {
6329
6657
  const msg = err instanceof Error ? err.message : String(err);
6330
6658
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
6331
6659
  else process.stderr.write(`Error: ${msg}\n`);
6332
- logger$7.error({ err }, "health action failed");
6660
+ logger$8.error({ err }, "health action failed");
6333
6661
  return 1;
6334
6662
  }
6335
6663
  }
@@ -6347,7 +6675,7 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
6347
6675
 
6348
6676
  //#endregion
6349
6677
  //#region src/modules/implementation-orchestrator/seed-methodology-context.ts
6350
- const logger$6 = createLogger("implementation-orchestrator:seed");
6678
+ const logger$7 = createLogger("implementation-orchestrator:seed");
6351
6679
  /** Max chars for the architecture summary seeded into decisions */
6352
6680
  const MAX_ARCH_CHARS = 6e3;
6353
6681
  /** Max chars per epic shard (fallback when per-story extraction returns null) */
@@ -6381,12 +6709,12 @@ function seedMethodologyContext(db, projectRoot) {
6381
6709
  const testCount = seedTestPatterns(db, projectRoot);
6382
6710
  if (testCount === -1) result.skippedCategories.push("test-patterns");
6383
6711
  else result.decisionsCreated += testCount;
6384
- logger$6.info({
6712
+ logger$7.info({
6385
6713
  decisionsCreated: result.decisionsCreated,
6386
6714
  skippedCategories: result.skippedCategories
6387
6715
  }, "Methodology context seeding complete");
6388
6716
  } catch (err) {
6389
- logger$6.warn({ error: err instanceof Error ? err.message : String(err) }, "Methodology context seeding failed (non-fatal)");
6717
+ logger$7.warn({ error: err instanceof Error ? err.message : String(err) }, "Methodology context seeding failed (non-fatal)");
6390
6718
  }
6391
6719
  return result;
6392
6720
  }
@@ -6430,7 +6758,7 @@ function seedArchitecture(db, projectRoot) {
6430
6758
  });
6431
6759
  count = 1;
6432
6760
  }
6433
- logger$6.debug({ count }, "Seeded architecture decisions");
6761
+ logger$7.debug({ count }, "Seeded architecture decisions");
6434
6762
  return count;
6435
6763
  }
6436
6764
  /**
@@ -6454,11 +6782,11 @@ function seedEpicShards(db, projectRoot) {
6454
6782
  const storedHashDecision = implementationDecisions.find((d) => d.category === "epic-shard-hash" && d.key === "epics-file");
6455
6783
  const storedHash = storedHashDecision?.value;
6456
6784
  if (storedHash === currentHash) {
6457
- logger$6.debug({ hash: currentHash }, "Epic shards up-to-date (hash unchanged) — skipping re-seed");
6785
+ logger$7.debug({ hash: currentHash }, "Epic shards up-to-date (hash unchanged) — skipping re-seed");
6458
6786
  return -1;
6459
6787
  }
6460
6788
  if (implementationDecisions.some((d) => d.category === "epic-shard")) {
6461
- logger$6.debug({
6789
+ logger$7.debug({
6462
6790
  storedHash,
6463
6791
  currentHash
6464
6792
  }, "Epics file changed — deleting stale epic-shard decisions");
@@ -6486,7 +6814,7 @@ function seedEpicShards(db, projectRoot) {
6486
6814
  value: currentHash,
6487
6815
  rationale: "SHA-256 hash of epics file content for change detection"
6488
6816
  });
6489
- logger$6.debug({
6817
+ logger$7.debug({
6490
6818
  count,
6491
6819
  hash: currentHash
6492
6820
  }, "Seeded epic shard decisions");
@@ -6510,7 +6838,7 @@ function seedTestPatterns(db, projectRoot) {
6510
6838
  value: patterns.slice(0, MAX_TEST_PATTERNS_CHARS),
6511
6839
  rationale: "Detected from project configuration at orchestrator startup"
6512
6840
  });
6513
- logger$6.debug("Seeded test patterns decision");
6841
+ logger$7.debug("Seeded test patterns decision");
6514
6842
  return 1;
6515
6843
  }
6516
6844
  /**
@@ -6681,6 +7009,107 @@ function findArtifact(projectRoot, candidates) {
6681
7009
  return void 0;
6682
7010
  }
6683
7011
 
7012
+ //#endregion
7013
+ //#region src/modules/agent-dispatch/interface-change-detector.ts
7014
+ const logger$6 = createLogger("interface-change-detector");
7015
+ /**
7016
+ * Extract exported interface and type names from TypeScript source content.
7017
+ *
7018
+ * Matches:
7019
+ * export interface Foo { ... } → 'Foo'
7020
+ * export type Bar = ... → 'Bar'
7021
+ *
7022
+ * Uses simple regex (not AST) for performance (<500ms target).
7023
+ * Does NOT match: re-exports (`export { Foo }`), default exports, internal declarations.
7024
+ */
7025
+ function extractExportedNames(content) {
7026
+ const names = [];
7027
+ const pattern = /^export\s+(?:interface|type)\s+(\w+)/gm;
7028
+ let match;
7029
+ while ((match = pattern.exec(content)) !== null) if (match[1] !== void 0) names.push(match[1]);
7030
+ return names;
7031
+ }
7032
+ /**
7033
+ * Detect whether modified .ts files export interfaces/types that are
7034
+ * referenced by test files outside the same module.
7035
+ *
7036
+ * Non-blocking: any errors during detection are caught, logged, and an empty
7037
+ * result is returned rather than throwing. Detection failure never blocks pipeline.
7038
+ *
7039
+ * @param options.filesModified - List of file paths modified by the dev-story (relative to projectRoot)
7040
+ * @param options.projectRoot - Absolute path to the project root
7041
+ * @param options.storyKey - Story key for logging context
7042
+ */
7043
+ function detectInterfaceChanges(options) {
7044
+ try {
7045
+ const { filesModified, projectRoot, storyKey } = options;
7046
+ const tsSourceFiles = filesModified.filter((f) => f.endsWith(".ts") && !f.endsWith(".test.ts") && !f.endsWith(".spec.ts"));
7047
+ if (tsSourceFiles.length === 0) return {
7048
+ modifiedInterfaces: [],
7049
+ potentiallyAffectedTests: []
7050
+ };
7051
+ const allNames = new Set();
7052
+ const sourceDirs = [];
7053
+ for (const relPath of tsSourceFiles) {
7054
+ const absPath = join$1(projectRoot, relPath);
7055
+ try {
7056
+ const content = readFileSync$1(absPath, "utf-8");
7057
+ const names = extractExportedNames(content);
7058
+ for (const name of names) allNames.add(name);
7059
+ sourceDirs.push(dirname$1(relPath));
7060
+ } catch {
7061
+ logger$6.debug({
7062
+ absPath,
7063
+ storyKey
7064
+ }, "Could not read modified file for interface extraction");
7065
+ }
7066
+ }
7067
+ if (allNames.size === 0) return {
7068
+ modifiedInterfaces: [],
7069
+ potentiallyAffectedTests: []
7070
+ };
7071
+ const affectedTests = new Set();
7072
+ for (const name of allNames) {
7073
+ let grepOutput = "";
7074
+ try {
7075
+ grepOutput = execSync(`grep -r --include="*.test.ts" --include="*.spec.ts" -l "${name}" .`, {
7076
+ cwd: projectRoot,
7077
+ encoding: "utf-8",
7078
+ timeout: 1e4,
7079
+ stdio: [
7080
+ "ignore",
7081
+ "pipe",
7082
+ "pipe"
7083
+ ]
7084
+ });
7085
+ } catch (grepErr) {
7086
+ const e = grepErr;
7087
+ if (typeof e.stdout === "string" && e.stdout.trim().length > 0) grepOutput = e.stdout;
7088
+ else continue;
7089
+ }
7090
+ const testFiles = grepOutput.split("\n").map((l) => l.trim().replace(/^\.\//, "")).filter((l) => l.length > 0);
7091
+ for (const tf of testFiles) {
7092
+ const tfDir = dirname$1(tf);
7093
+ const isSameModule = sourceDirs.some((srcDir) => tfDir === srcDir || tfDir.startsWith(srcDir + "/"));
7094
+ if (!isSameModule) affectedTests.add(tf);
7095
+ }
7096
+ }
7097
+ return {
7098
+ modifiedInterfaces: Array.from(allNames),
7099
+ potentiallyAffectedTests: Array.from(affectedTests)
7100
+ };
7101
+ } catch (err) {
7102
+ logger$6.warn({
7103
+ err,
7104
+ storyKey: options.storyKey
7105
+ }, "Interface change detection failed — skipping");
7106
+ return {
7107
+ modifiedInterfaces: [],
7108
+ potentiallyAffectedTests: []
7109
+ };
7110
+ }
7111
+ }
7112
+
6684
7113
  //#endregion
6685
7114
  //#region src/modules/implementation-orchestrator/orchestrator-impl.ts
6686
7115
  function createPauseGate() {
@@ -6701,8 +7130,8 @@ function createPauseGate() {
6701
7130
  * @returns A fully-configured ImplementationOrchestrator ready to call run()
6702
7131
  */
6703
7132
  function createImplementationOrchestrator(deps) {
6704
- const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot } = deps;
6705
- const logger$20 = createLogger("implementation-orchestrator");
7133
+ const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot, tokenCeilings } = deps;
7134
+ const logger$22 = createLogger("implementation-orchestrator");
6706
7135
  let _state = "IDLE";
6707
7136
  let _startedAt;
6708
7137
  let _completedAt;
@@ -6745,7 +7174,7 @@ function createImplementationOrchestrator(deps) {
6745
7174
  const nowMs = Date.now();
6746
7175
  for (const [phase, startMs] of starts) {
6747
7176
  const endMs = ends?.get(phase);
6748
- if (endMs === void 0) logger$20.warn({
7177
+ if (endMs === void 0) logger$22.warn({
6749
7178
  storyKey,
6750
7179
  phase
6751
7180
  }, "Phase has no end time — story may have errored mid-phase. Duration capped to now() and may be inflated.");
@@ -6760,12 +7189,14 @@ function createImplementationOrchestrator(deps) {
6760
7189
  const startedAt = storyState?.startedAt;
6761
7190
  const completedAt = storyState?.completedAt ?? new Date().toISOString();
6762
7191
  const wallClockSeconds = startedAt ? Math.round((new Date(completedAt).getTime() - new Date(startedAt).getTime()) / 1e3) : 0;
7192
+ const wallClockMs = startedAt ? new Date(completedAt).getTime() - new Date(startedAt).getTime() : 0;
6763
7193
  const tokenAgg = aggregateTokenUsageForStory(db, config.pipelineRunId, storyKey);
7194
+ const phaseDurationsJson = buildPhaseDurationsJson(storyKey);
6764
7195
  writeStoryMetrics(db, {
6765
7196
  run_id: config.pipelineRunId,
6766
7197
  story_key: storyKey,
6767
7198
  result,
6768
- phase_durations_json: buildPhaseDurationsJson(storyKey),
7199
+ phase_durations_json: phaseDurationsJson,
6769
7200
  started_at: startedAt,
6770
7201
  completed_at: completedAt,
6771
7202
  wall_clock_seconds: wallClockSeconds,
@@ -6792,13 +7223,41 @@ function createImplementationOrchestrator(deps) {
6792
7223
  rationale: `Story ${storyKey} completed with result=${result} in ${wallClockSeconds}s. Tokens: ${tokenAgg.input}+${tokenAgg.output}. Review cycles: ${reviewCycles}.`
6793
7224
  });
6794
7225
  } catch (decisionErr) {
6795
- logger$20.warn({
7226
+ logger$22.warn({
6796
7227
  err: decisionErr,
6797
7228
  storyKey
6798
7229
  }, "Failed to write story-metrics decision (best-effort)");
6799
7230
  }
7231
+ try {
7232
+ const phaseBreakdown = {};
7233
+ const starts = _phaseStartMs.get(storyKey);
7234
+ const ends = _phaseEndMs.get(storyKey);
7235
+ if (starts) {
7236
+ const nowMs = Date.now();
7237
+ for (const [phase, startMs] of starts) {
7238
+ const endMs = ends?.get(phase);
7239
+ phaseBreakdown[phase] = endMs !== void 0 ? endMs - startMs : nowMs - startMs;
7240
+ }
7241
+ }
7242
+ eventBus.emit("story:metrics", {
7243
+ storyKey,
7244
+ wallClockMs,
7245
+ phaseBreakdown,
7246
+ tokens: {
7247
+ input: tokenAgg.input,
7248
+ output: tokenAgg.output
7249
+ },
7250
+ reviewCycles,
7251
+ dispatches: _storyDispatches.get(storyKey) ?? 0
7252
+ });
7253
+ } catch (emitErr) {
7254
+ logger$22.warn({
7255
+ err: emitErr,
7256
+ storyKey
7257
+ }, "Failed to emit story:metrics event (best-effort)");
7258
+ }
6800
7259
  } catch (err) {
6801
- logger$20.warn({
7260
+ logger$22.warn({
6802
7261
  err,
6803
7262
  storyKey
6804
7263
  }, "Failed to write story metrics (best-effort)");
@@ -6827,7 +7286,7 @@ function createImplementationOrchestrator(deps) {
6827
7286
  rationale: `Story ${storyKey} ${outcome} after ${reviewCycles} review cycle(s).`
6828
7287
  });
6829
7288
  } catch (err) {
6830
- logger$20.warn({
7289
+ logger$22.warn({
6831
7290
  err,
6832
7291
  storyKey
6833
7292
  }, "Failed to write story-outcome decision (best-effort)");
@@ -6853,7 +7312,7 @@ function createImplementationOrchestrator(deps) {
6853
7312
  rationale: `Escalation diagnosis for ${payload.storyKey}: ${diagnosis.recommendedAction} — ${diagnosis.rationale}`
6854
7313
  });
6855
7314
  } catch (err) {
6856
- logger$20.warn({
7315
+ logger$22.warn({
6857
7316
  err,
6858
7317
  storyKey: payload.storyKey
6859
7318
  }, "Failed to persist escalation diagnosis (best-effort)");
@@ -6903,7 +7362,7 @@ function createImplementationOrchestrator(deps) {
6903
7362
  token_usage_json: serialized
6904
7363
  });
6905
7364
  } catch (err) {
6906
- logger$20.warn("Failed to persist orchestrator state", { err });
7365
+ logger$22.warn("Failed to persist orchestrator state", { err });
6907
7366
  }
6908
7367
  }
6909
7368
  function recordProgress() {
@@ -6950,7 +7409,7 @@ function createImplementationOrchestrator(deps) {
6950
7409
  }
6951
7410
  if (childActive) {
6952
7411
  _lastProgressTs = Date.now();
6953
- logger$20.debug({
7412
+ logger$22.debug({
6954
7413
  storyKey: key,
6955
7414
  phase: s.phase,
6956
7415
  childPids
@@ -6959,7 +7418,7 @@ function createImplementationOrchestrator(deps) {
6959
7418
  }
6960
7419
  _stalledStories.add(key);
6961
7420
  _storiesWithStall.add(key);
6962
- logger$20.warn({
7421
+ logger$22.warn({
6963
7422
  storyKey: key,
6964
7423
  phase: s.phase,
6965
7424
  elapsedMs: elapsed,
@@ -7004,7 +7463,7 @@ function createImplementationOrchestrator(deps) {
7004
7463
  for (let attempt = 0; attempt < MEMORY_PRESSURE_BACKOFF_MS.length; attempt++) {
7005
7464
  const memState = dispatcher.getMemoryState();
7006
7465
  if (!memState.isPressured) return true;
7007
- logger$20.warn({
7466
+ logger$22.warn({
7008
7467
  storyKey,
7009
7468
  freeMB: memState.freeMB,
7010
7469
  thresholdMB: memState.thresholdMB,
@@ -7024,11 +7483,11 @@ function createImplementationOrchestrator(deps) {
7024
7483
  * exhausted retries the story is ESCALATED.
7025
7484
  */
7026
7485
  async function processStory(storyKey) {
7027
- logger$20.info("Processing story", { storyKey });
7486
+ logger$22.info("Processing story", { storyKey });
7028
7487
  {
7029
7488
  const memoryOk = await checkMemoryPressure(storyKey);
7030
7489
  if (!memoryOk) {
7031
- logger$20.warn({ storyKey }, "Memory pressure exhausted — escalating story without dispatch");
7490
+ logger$22.warn({ storyKey }, "Memory pressure exhausted — escalating story without dispatch");
7032
7491
  _stories.set(storyKey, {
7033
7492
  phase: "ESCALATED",
7034
7493
  reviewCycles: 0,
@@ -7062,14 +7521,14 @@ function createImplementationOrchestrator(deps) {
7062
7521
  if (match) {
7063
7522
  const candidatePath = join$1(artifactsDir, match);
7064
7523
  const validation = await isValidStoryFile(candidatePath);
7065
- if (!validation.valid) logger$20.warn({
7524
+ if (!validation.valid) logger$22.warn({
7066
7525
  storyKey,
7067
7526
  storyFilePath: candidatePath,
7068
7527
  reason: validation.reason
7069
7528
  }, `Existing story file for ${storyKey} is invalid (${validation.reason}) — re-creating`);
7070
7529
  else {
7071
7530
  storyFilePath = candidatePath;
7072
- logger$20.info({
7531
+ logger$22.info({
7073
7532
  storyKey,
7074
7533
  storyFilePath
7075
7534
  }, "Found existing story file — skipping create-story");
@@ -7094,7 +7553,8 @@ function createImplementationOrchestrator(deps) {
7094
7553
  pack,
7095
7554
  contextCompiler,
7096
7555
  dispatcher,
7097
- projectRoot
7556
+ projectRoot,
7557
+ tokenCeilings
7098
7558
  }, {
7099
7559
  epicId: storyKey.split("-")[0] ?? storyKey,
7100
7560
  storyKey,
@@ -7172,17 +7632,18 @@ function createImplementationOrchestrator(deps) {
7172
7632
  pack,
7173
7633
  contextCompiler,
7174
7634
  dispatcher,
7175
- projectRoot
7635
+ projectRoot,
7636
+ tokenCeilings
7176
7637
  }, {
7177
7638
  storyKey,
7178
7639
  storyFilePath: storyFilePath ?? "",
7179
7640
  pipelineRunId: config.pipelineRunId
7180
7641
  });
7181
7642
  testPlanPhaseResult = testPlanResult.result;
7182
- if (testPlanResult.result === "success") logger$20.info({ storyKey }, "Test plan generated successfully");
7183
- else logger$20.warn({ storyKey }, "Test planning returned failed result — proceeding to dev-story without test plan");
7643
+ if (testPlanResult.result === "success") logger$22.info({ storyKey }, "Test plan generated successfully");
7644
+ else logger$22.warn({ storyKey }, "Test planning returned failed result — proceeding to dev-story without test plan");
7184
7645
  } catch (err) {
7185
- logger$20.warn({
7646
+ logger$22.warn({
7186
7647
  storyKey,
7187
7648
  err
7188
7649
  }, "Test planning failed — proceeding to dev-story without test plan");
@@ -7206,7 +7667,7 @@ function createImplementationOrchestrator(deps) {
7206
7667
  try {
7207
7668
  storyContentForAnalysis = await readFile$1(storyFilePath ?? "", "utf-8");
7208
7669
  } catch (err) {
7209
- logger$20.error({
7670
+ logger$22.error({
7210
7671
  storyKey,
7211
7672
  storyFilePath,
7212
7673
  error: err instanceof Error ? err.message : String(err)
@@ -7214,7 +7675,7 @@ function createImplementationOrchestrator(deps) {
7214
7675
  }
7215
7676
  const analysis = analyzeStoryComplexity(storyContentForAnalysis);
7216
7677
  const batches = planTaskBatches(analysis);
7217
- logger$20.info({
7678
+ logger$22.info({
7218
7679
  storyKey,
7219
7680
  estimatedScope: analysis.estimatedScope,
7220
7681
  batchCount: batches.length,
@@ -7232,7 +7693,7 @@ function createImplementationOrchestrator(deps) {
7232
7693
  if (_state !== "RUNNING") break;
7233
7694
  const taskScope = batch.taskIds.map((id, i) => `T${id}: ${batch.taskTitles[i] ?? ""}`).join("\n");
7234
7695
  const priorFiles = allFilesModified.size > 0 ? Array.from(allFilesModified) : void 0;
7235
- logger$20.info({
7696
+ logger$22.info({
7236
7697
  storyKey,
7237
7698
  batchIndex: batch.batchIndex,
7238
7699
  taskCount: batch.taskIds.length
@@ -7246,7 +7707,8 @@ function createImplementationOrchestrator(deps) {
7246
7707
  pack,
7247
7708
  contextCompiler,
7248
7709
  dispatcher,
7249
- projectRoot
7710
+ projectRoot,
7711
+ tokenCeilings
7250
7712
  }, {
7251
7713
  storyKey,
7252
7714
  storyFilePath: storyFilePath ?? "",
@@ -7256,7 +7718,7 @@ function createImplementationOrchestrator(deps) {
7256
7718
  });
7257
7719
  } catch (batchErr) {
7258
7720
  const errMsg = batchErr instanceof Error ? batchErr.message : String(batchErr);
7259
- logger$20.warn({
7721
+ logger$22.warn({
7260
7722
  storyKey,
7261
7723
  batchIndex: batch.batchIndex,
7262
7724
  error: errMsg
@@ -7276,7 +7738,7 @@ function createImplementationOrchestrator(deps) {
7276
7738
  filesModified: batchFilesModified,
7277
7739
  result: batchResult.result === "success" ? "success" : "failed"
7278
7740
  };
7279
- logger$20.info(batchMetrics, "Batch dev-story metrics");
7741
+ logger$22.info(batchMetrics, "Batch dev-story metrics");
7280
7742
  for (const f of batchFilesModified) allFilesModified.add(f);
7281
7743
  if (batchFilesModified.length > 0) batchFileGroups.push({
7282
7744
  batchIndex: batch.batchIndex,
@@ -7298,13 +7760,13 @@ function createImplementationOrchestrator(deps) {
7298
7760
  })
7299
7761
  });
7300
7762
  } catch (tokenErr) {
7301
- logger$20.warn({
7763
+ logger$22.warn({
7302
7764
  storyKey,
7303
7765
  batchIndex: batch.batchIndex,
7304
7766
  err: tokenErr
7305
7767
  }, "Failed to record batch token usage");
7306
7768
  }
7307
- if (batchResult.result === "failed") logger$20.warn({
7769
+ if (batchResult.result === "failed") logger$22.warn({
7308
7770
  storyKey,
7309
7771
  batchIndex: batch.batchIndex,
7310
7772
  error: batchResult.error
@@ -7325,7 +7787,8 @@ function createImplementationOrchestrator(deps) {
7325
7787
  pack,
7326
7788
  contextCompiler,
7327
7789
  dispatcher,
7328
- projectRoot
7790
+ projectRoot,
7791
+ tokenCeilings
7329
7792
  }, {
7330
7793
  storyKey,
7331
7794
  storyFilePath: storyFilePath ?? "",
@@ -7339,7 +7802,7 @@ function createImplementationOrchestrator(deps) {
7339
7802
  });
7340
7803
  persistState();
7341
7804
  if (devResult.result === "success") devStoryWasSuccess = true;
7342
- else logger$20.warn("Dev-story reported failure, proceeding to code review", {
7805
+ else logger$22.warn("Dev-story reported failure, proceeding to code review", {
7343
7806
  storyKey,
7344
7807
  error: devResult.error,
7345
7808
  filesModified: devFilesModified.length
@@ -7363,10 +7826,11 @@ function createImplementationOrchestrator(deps) {
7363
7826
  persistState();
7364
7827
  return;
7365
7828
  }
7829
+ let gitDiffFiles;
7366
7830
  if (devStoryWasSuccess) {
7367
- const changedFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
7368
- if (changedFiles.length === 0) {
7369
- logger$20.warn({ storyKey }, "Zero-diff detected after COMPLETE dev-story — no file changes in git working tree");
7831
+ gitDiffFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
7832
+ if (gitDiffFiles.length === 0) {
7833
+ logger$22.warn({ storyKey }, "Zero-diff detected after COMPLETE dev-story — no file changes in git working tree");
7370
7834
  eventBus.emit("orchestrator:zero-diff-escalation", {
7371
7835
  storyKey,
7372
7836
  reason: "zero-diff-on-complete"
@@ -7397,7 +7861,7 @@ function createImplementationOrchestrator(deps) {
7397
7861
  });
7398
7862
  if (buildVerifyResult.status === "passed") {
7399
7863
  eventBus.emit("story:build-verification-passed", { storyKey });
7400
- logger$20.info({ storyKey }, "Build verification passed");
7864
+ logger$22.info({ storyKey }, "Build verification passed");
7401
7865
  } else if (buildVerifyResult.status === "failed" || buildVerifyResult.status === "timeout") {
7402
7866
  const truncatedOutput = (buildVerifyResult.output ?? "").slice(0, 2e3);
7403
7867
  const reason = buildVerifyResult.reason ?? "build-verification-failed";
@@ -7406,7 +7870,7 @@ function createImplementationOrchestrator(deps) {
7406
7870
  exitCode: buildVerifyResult.exitCode ?? 1,
7407
7871
  output: truncatedOutput
7408
7872
  });
7409
- logger$20.warn({
7873
+ logger$22.warn({
7410
7874
  storyKey,
7411
7875
  reason,
7412
7876
  exitCode: buildVerifyResult.exitCode
@@ -7427,6 +7891,28 @@ function createImplementationOrchestrator(deps) {
7427
7891
  return;
7428
7892
  }
7429
7893
  }
7894
+ try {
7895
+ const filesModified = gitDiffFiles ?? devFilesModified;
7896
+ if (filesModified.length > 0) {
7897
+ const icResult = detectInterfaceChanges({
7898
+ filesModified,
7899
+ projectRoot: projectRoot ?? process.cwd(),
7900
+ storyKey
7901
+ });
7902
+ if (icResult.potentiallyAffectedTests.length > 0) {
7903
+ logger$22.warn({
7904
+ storyKey,
7905
+ modifiedInterfaces: icResult.modifiedInterfaces,
7906
+ potentiallyAffectedTests: icResult.potentiallyAffectedTests
7907
+ }, "Interface change warning: modified exports may affect cross-module test mocks");
7908
+ eventBus.emit("story:interface-change-warning", {
7909
+ storyKey,
7910
+ modifiedInterfaces: icResult.modifiedInterfaces,
7911
+ potentiallyAffectedTests: icResult.potentiallyAffectedTests
7912
+ });
7913
+ }
7914
+ }
7915
+ } catch {}
7430
7916
  let reviewCycles = 0;
7431
7917
  let keepReviewing = true;
7432
7918
  let timeoutRetried = false;
@@ -7459,7 +7945,7 @@ function createImplementationOrchestrator(deps) {
7459
7945
  "NEEDS_MAJOR_REWORK": 2
7460
7946
  };
7461
7947
  for (const group of batchFileGroups) {
7462
- logger$20.info({
7948
+ logger$22.info({
7463
7949
  storyKey,
7464
7950
  batchIndex: group.batchIndex,
7465
7951
  fileCount: group.files.length
@@ -7470,7 +7956,8 @@ function createImplementationOrchestrator(deps) {
7470
7956
  pack,
7471
7957
  contextCompiler,
7472
7958
  dispatcher,
7473
- projectRoot
7959
+ projectRoot,
7960
+ tokenCeilings
7474
7961
  }, {
7475
7962
  storyKey,
7476
7963
  storyFilePath: storyFilePath ?? "",
@@ -7496,7 +7983,7 @@ function createImplementationOrchestrator(deps) {
7496
7983
  rawOutput: lastRawOutput,
7497
7984
  tokenUsage: aggregateTokens
7498
7985
  };
7499
- logger$20.info({
7986
+ logger$22.info({
7500
7987
  storyKey,
7501
7988
  batchCount: batchFileGroups.length,
7502
7989
  verdict: worstVerdict,
@@ -7509,7 +7996,8 @@ function createImplementationOrchestrator(deps) {
7509
7996
  pack,
7510
7997
  contextCompiler,
7511
7998
  dispatcher,
7512
- projectRoot
7999
+ projectRoot,
8000
+ tokenCeilings
7513
8001
  }, {
7514
8002
  storyKey,
7515
8003
  storyFilePath: storyFilePath ?? "",
@@ -7522,7 +8010,7 @@ function createImplementationOrchestrator(deps) {
7522
8010
  const isPhantomReview = reviewResult.dispatchFailed === true || reviewResult.verdict !== "SHIP_IT" && (reviewResult.issue_list === void 0 || reviewResult.issue_list.length === 0) && reviewResult.error !== void 0;
7523
8011
  if (isPhantomReview && !timeoutRetried) {
7524
8012
  timeoutRetried = true;
7525
- logger$20.warn({
8013
+ logger$22.warn({
7526
8014
  storyKey,
7527
8015
  reviewCycles,
7528
8016
  error: reviewResult.error
@@ -7532,7 +8020,7 @@ function createImplementationOrchestrator(deps) {
7532
8020
  verdict = reviewResult.verdict;
7533
8021
  issueList = reviewResult.issue_list ?? [];
7534
8022
  if (verdict === "NEEDS_MAJOR_REWORK" && reviewCycles > 0 && previousIssueList.length > 0 && issueList.length < previousIssueList.length) {
7535
- logger$20.info({
8023
+ logger$22.info({
7536
8024
  storyKey,
7537
8025
  originalVerdict: verdict,
7538
8026
  issuesBefore: previousIssueList.length,
@@ -7568,7 +8056,7 @@ function createImplementationOrchestrator(deps) {
7568
8056
  if (_decomposition !== void 0) parts.push(`decomposed: ${_decomposition.batchCount} batches`);
7569
8057
  parts.push(`${fileCount} files`);
7570
8058
  parts.push(`${totalTokensK} tokens`);
7571
- logger$20.info({
8059
+ logger$22.info({
7572
8060
  storyKey,
7573
8061
  verdict,
7574
8062
  agentVerdict: reviewResult.agentVerdict
@@ -7611,7 +8099,8 @@ function createImplementationOrchestrator(deps) {
7611
8099
  pack,
7612
8100
  contextCompiler,
7613
8101
  dispatcher,
7614
- projectRoot
8102
+ projectRoot,
8103
+ tokenCeilings
7615
8104
  }, {
7616
8105
  storyKey,
7617
8106
  storyFilePath: storyFilePath ?? "",
@@ -7619,7 +8108,7 @@ function createImplementationOrchestrator(deps) {
7619
8108
  filesModified: devFilesModified,
7620
8109
  workingDirectory: projectRoot
7621
8110
  });
7622
- logger$20.debug({
8111
+ logger$22.debug({
7623
8112
  storyKey,
7624
8113
  expansion_priority: expansionResult.expansion_priority,
7625
8114
  coverage_gaps: expansionResult.coverage_gaps.length
@@ -7632,7 +8121,7 @@ function createImplementationOrchestrator(deps) {
7632
8121
  value: JSON.stringify(expansionResult)
7633
8122
  });
7634
8123
  } catch (expansionErr) {
7635
- logger$20.warn({
8124
+ logger$22.warn({
7636
8125
  storyKey,
7637
8126
  error: expansionErr instanceof Error ? expansionErr.message : String(expansionErr)
7638
8127
  }, "Test expansion failed — story verdict unchanged");
@@ -7659,7 +8148,7 @@ function createImplementationOrchestrator(deps) {
7659
8148
  persistState();
7660
8149
  return;
7661
8150
  }
7662
- logger$20.info({
8151
+ logger$22.info({
7663
8152
  storyKey,
7664
8153
  reviewCycles: finalReviewCycles,
7665
8154
  issueCount: issueList.length
@@ -7709,7 +8198,7 @@ function createImplementationOrchestrator(deps) {
7709
8198
  fixPrompt = assembled.prompt;
7710
8199
  } catch {
7711
8200
  fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, minor fixes needed`;
7712
- logger$20.warn("Failed to assemble auto-approve fix prompt, using fallback", { storyKey });
8201
+ logger$22.warn("Failed to assemble auto-approve fix prompt, using fallback", { storyKey });
7713
8202
  }
7714
8203
  const handle = dispatcher.dispatch({
7715
8204
  prompt: fixPrompt,
@@ -7726,9 +8215,9 @@ function createImplementationOrchestrator(deps) {
7726
8215
  output: fixResult.tokenEstimate.output
7727
8216
  } : void 0 }
7728
8217
  });
7729
- if (fixResult.status === "timeout") logger$20.warn("Auto-approve fix timed out — approving anyway (issues were minor)", { storyKey });
8218
+ if (fixResult.status === "timeout") logger$22.warn("Auto-approve fix timed out — approving anyway (issues were minor)", { storyKey });
7730
8219
  } catch (err) {
7731
- logger$20.warn("Auto-approve fix dispatch failed — approving anyway (issues were minor)", {
8220
+ logger$22.warn("Auto-approve fix dispatch failed — approving anyway (issues were minor)", {
7732
8221
  storyKey,
7733
8222
  err
7734
8223
  });
@@ -7758,9 +8247,15 @@ function createImplementationOrchestrator(deps) {
7758
8247
  let fixPrompt;
7759
8248
  const isMajorRework = taskType === "major-rework";
7760
8249
  const templateName = isMajorRework ? "rework-story" : "fix-story";
8250
+ let fixMaxTurns;
7761
8251
  try {
7762
8252
  const fixTemplate = await pack.getPrompt(templateName);
7763
8253
  const storyContent = await readFile$1(storyFilePath ?? "", "utf-8");
8254
+ if (isMajorRework) {
8255
+ const complexity = computeStoryComplexity(storyContent);
8256
+ fixMaxTurns = resolveFixStoryMaxTurns(complexity.complexityScore);
8257
+ logComplexityResult(storyKey, complexity, fixMaxTurns);
8258
+ }
7764
8259
  let reviewFeedback;
7765
8260
  if (issueList.length === 0) reviewFeedback = isMajorRework ? [
7766
8261
  `Verdict: ${verdict}`,
@@ -7831,7 +8326,7 @@ function createImplementationOrchestrator(deps) {
7831
8326
  fixPrompt = assembled.prompt;
7832
8327
  } catch {
7833
8328
  fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, taskType=${taskType}`;
7834
- logger$20.warn("Failed to assemble fix prompt, using fallback", {
8329
+ logger$22.warn("Failed to assemble fix prompt, using fallback", {
7835
8330
  storyKey,
7836
8331
  taskType
7837
8332
  });
@@ -7843,6 +8338,7 @@ function createImplementationOrchestrator(deps) {
7843
8338
  taskType,
7844
8339
  ...fixModel !== void 0 ? { model: fixModel } : {},
7845
8340
  outputSchema: DevStoryResultSchema,
8341
+ ...fixMaxTurns !== void 0 ? { maxTurns: fixMaxTurns } : {},
7846
8342
  ...projectRoot !== void 0 ? { workingDirectory: projectRoot } : {}
7847
8343
  }) : dispatcher.dispatch({
7848
8344
  prompt: fixPrompt,
@@ -7861,7 +8357,7 @@ function createImplementationOrchestrator(deps) {
7861
8357
  } : void 0 }
7862
8358
  });
7863
8359
  if (fixResult.status === "timeout") {
7864
- logger$20.warn("Fix dispatch timed out — escalating story", {
8360
+ logger$22.warn("Fix dispatch timed out — escalating story", {
7865
8361
  storyKey,
7866
8362
  taskType
7867
8363
  });
@@ -7883,7 +8379,7 @@ function createImplementationOrchestrator(deps) {
7883
8379
  }
7884
8380
  if (fixResult.status === "failed") {
7885
8381
  if (isMajorRework) {
7886
- logger$20.warn("Major rework dispatch failed — escalating story", {
8382
+ logger$22.warn("Major rework dispatch failed — escalating story", {
7887
8383
  storyKey,
7888
8384
  exitCode: fixResult.exitCode
7889
8385
  });
@@ -7903,14 +8399,14 @@ function createImplementationOrchestrator(deps) {
7903
8399
  persistState();
7904
8400
  return;
7905
8401
  }
7906
- logger$20.warn("Fix dispatch failed", {
8402
+ logger$22.warn("Fix dispatch failed", {
7907
8403
  storyKey,
7908
8404
  taskType,
7909
8405
  exitCode: fixResult.exitCode
7910
8406
  });
7911
8407
  }
7912
8408
  } catch (err) {
7913
- logger$20.warn("Fix dispatch failed, continuing to next review", {
8409
+ logger$22.warn("Fix dispatch failed, continuing to next review", {
7914
8410
  storyKey,
7915
8411
  taskType,
7916
8412
  err
@@ -7973,11 +8469,11 @@ function createImplementationOrchestrator(deps) {
7973
8469
  }
7974
8470
  async function run(storyKeys) {
7975
8471
  if (_state === "RUNNING" || _state === "PAUSED") {
7976
- logger$20.warn("run() called while orchestrator is already running or paused — ignoring", { state: _state });
8472
+ logger$22.warn("run() called while orchestrator is already running or paused — ignoring", { state: _state });
7977
8473
  return getStatus();
7978
8474
  }
7979
8475
  if (_state === "COMPLETE") {
7980
- logger$20.warn("run() called on a COMPLETE orchestrator — ignoring", { state: _state });
8476
+ logger$22.warn("run() called on a COMPLETE orchestrator — ignoring", { state: _state });
7981
8477
  return getStatus();
7982
8478
  }
7983
8479
  _state = "RUNNING";
@@ -7995,13 +8491,13 @@ function createImplementationOrchestrator(deps) {
7995
8491
  if (config.enableHeartbeat) startHeartbeat();
7996
8492
  if (projectRoot !== void 0) {
7997
8493
  const seedResult = seedMethodologyContext(db, projectRoot);
7998
- if (seedResult.decisionsCreated > 0) logger$20.info({
8494
+ if (seedResult.decisionsCreated > 0) logger$22.info({
7999
8495
  decisionsCreated: seedResult.decisionsCreated,
8000
8496
  skippedCategories: seedResult.skippedCategories
8001
8497
  }, "Methodology context seeded from planning artifacts");
8002
8498
  }
8003
8499
  const groups = detectConflictGroups(storyKeys, { moduleMap: pack.manifest.conflictGroups });
8004
- logger$20.info("Orchestrator starting", {
8500
+ logger$22.info("Orchestrator starting", {
8005
8501
  storyCount: storyKeys.length,
8006
8502
  groupCount: groups.length,
8007
8503
  maxConcurrency: config.maxConcurrency
@@ -8013,7 +8509,7 @@ function createImplementationOrchestrator(deps) {
8013
8509
  _state = "FAILED";
8014
8510
  _completedAt = new Date().toISOString();
8015
8511
  persistState();
8016
- logger$20.error("Orchestrator failed with unhandled error", { err });
8512
+ logger$22.error("Orchestrator failed with unhandled error", { err });
8017
8513
  return getStatus();
8018
8514
  }
8019
8515
  stopHeartbeat();
@@ -8040,7 +8536,7 @@ function createImplementationOrchestrator(deps) {
8040
8536
  _pauseGate = createPauseGate();
8041
8537
  _state = "PAUSED";
8042
8538
  eventBus.emit("orchestrator:paused", {});
8043
- logger$20.info("Orchestrator paused");
8539
+ logger$22.info("Orchestrator paused");
8044
8540
  }
8045
8541
  function resume() {
8046
8542
  if (_state !== "PAUSED") return;
@@ -8051,7 +8547,7 @@ function createImplementationOrchestrator(deps) {
8051
8547
  }
8052
8548
  _state = "RUNNING";
8053
8549
  eventBus.emit("orchestrator:resumed", {});
8054
- logger$20.info("Orchestrator resumed");
8550
+ logger$22.info("Orchestrator resumed");
8055
8551
  }
8056
8552
  return {
8057
8553
  run,
@@ -12530,6 +13026,27 @@ async function runRunAction(options) {
12530
13026
  output: payload.output
12531
13027
  });
12532
13028
  });
13029
+ eventBus.on("story:interface-change-warning", (payload) => {
13030
+ ndjsonEmitter.emit({
13031
+ type: "story:interface-change-warning",
13032
+ ts: new Date().toISOString(),
13033
+ storyKey: payload.storyKey,
13034
+ modifiedInterfaces: payload.modifiedInterfaces,
13035
+ potentiallyAffectedTests: payload.potentiallyAffectedTests
13036
+ });
13037
+ });
13038
+ eventBus.on("story:metrics", (payload) => {
13039
+ ndjsonEmitter.emit({
13040
+ type: "story:metrics",
13041
+ ts: new Date().toISOString(),
13042
+ storyKey: payload.storyKey,
13043
+ wallClockMs: payload.wallClockMs,
13044
+ phaseBreakdown: payload.phaseBreakdown,
13045
+ tokens: payload.tokens,
13046
+ reviewCycles: payload.reviewCycles,
13047
+ dispatches: payload.dispatches
13048
+ });
13049
+ });
12533
13050
  }
12534
13051
  const orchestrator = createImplementationOrchestrator({
12535
13052
  db,
@@ -12983,5 +13500,5 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
12983
13500
  }
12984
13501
 
12985
13502
  //#endregion
12986
- 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 };
12987
- //# sourceMappingURL=run-CEtHPG4I.js.map
13503
+ export { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, parseDbTimestampAsUtc, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
13504
+ //# sourceMappingURL=run-BCyrbL3w.js.map