substrate-ai 0.20.29 → 0.20.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-Dx9hm9x1.js";
1
+ import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, RuntimeProbeListSchema, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, detectsEventDrivenAC, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-DJ4z2uWN.js";
2
2
  import { createLogger } from "./logger-KeHncl-f.js";
3
3
  import { TypedEventBusImpl, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning, sleep } from "./helpers-CElYrONe.js";
4
4
  import { ADVISORY_NOTES, Categorizer, ConsumerAnalyzer, DEFAULT_GLOBAL_SETTINGS, DispatcherImpl, DoltClient, ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, EfficiencyScorer, IngestionServer, LogTurnAnalyzer, OPERATIONAL_FINDING, Recommender, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, STORY_METRICS, STORY_OUTCOME, SubstrateConfigSchema, TEST_EXPANSION_FINDING, TEST_PLAN, TelemetryNormalizer, TelemetryPipeline, TurnAnalyzer, addTokenUsage, aggregateTokenUsageForRun, aggregateTokenUsageForStory, callLLM, createConfigSystem, createDatabaseAdapter$1, createDecision, createPipelineRun, createRequirement, detectInterfaceChanges, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunMetrics, getRunningPipelineRuns, getStoryMetricsForRun, getTokenUsageSummary, initSchema, listRequirements, loadModelRoutingConfig, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision, writeRunMetrics, writeStoryMetrics } from "./dist-CqtWS9wF.js";
@@ -9,11 +9,12 @@ import yaml from "js-yaml";
9
9
  import * as actualFS from "node:fs";
10
10
  import { accessSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
11
11
  import { exec, execFile, execFileSync, execSync, spawn } from "node:child_process";
12
+ import * as path$2 from "node:path";
12
13
  import path, { basename as basename$1, dirname as dirname$1, extname as extname$1, isAbsolute, join as join$1, posix, resolve as resolve$1, win32 } from "node:path";
13
14
  import { tmpdir } from "node:os";
14
15
  import { createHash, randomUUID } from "node:crypto";
15
16
  import { z } from "zod";
16
- import { access as access$1, lstat, mkdir as mkdir$1, readFile as readFile$1, readdir as readdir$1, readlink, realpath, stat as stat$1, unlink, writeFile as writeFile$1 } from "node:fs/promises";
17
+ import { access as access$1, lstat, mkdir as mkdir$1, readFile as readFile$1, readdir as readdir$1, readlink, realpath, rename, stat as stat$1, unlink, writeFile as writeFile$1 } from "node:fs/promises";
17
18
  import { existsSync as existsSync$1, lstatSync, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdir as readdir$2, readdirSync as readdirSync$1, readlinkSync, realpathSync as realpathSync$1, unlinkSync as unlinkSync$1, writeFileSync as writeFileSync$1 } from "fs";
18
19
  import { promisify } from "node:util";
19
20
  import { fileURLToPath } from "node:url";
@@ -2122,7 +2123,7 @@ async function runHelpAgent() {
2122
2123
 
2123
2124
  //#endregion
2124
2125
  //#region src/persistence/dolt-server.ts
2125
- const logger$23 = createLogger("dolt-server");
2126
+ const logger$25 = createLogger("dolt-server");
2126
2127
  /**
2127
2128
  * Start a dolt sql-server for the given project, if Dolt is available and no
2128
2129
  * server is already running (socket already exists).
@@ -2139,7 +2140,7 @@ async function startDoltServer(projectRoot) {
2139
2140
  if (!existsSync(join$1(stateDir, ".dolt"))) return null;
2140
2141
  try {
2141
2142
  await access$1(socketPath);
2142
- logger$23.debug("Dolt socket already exists at %s — using existing server", socketPath);
2143
+ logger$25.debug("Dolt socket already exists at %s — using existing server", socketPath);
2143
2144
  return null;
2144
2145
  } catch {}
2145
2146
  try {
@@ -2148,10 +2149,10 @@ async function startDoltServer(projectRoot) {
2148
2149
  stdio: "pipe"
2149
2150
  });
2150
2151
  } catch {
2151
- logger$23.debug("dolt binary not on PATH — cannot start server");
2152
+ logger$25.debug("dolt binary not on PATH — cannot start server");
2152
2153
  return null;
2153
2154
  }
2154
- logger$23.debug("Starting dolt sql-server at %s", socketPath);
2155
+ logger$25.debug("Starting dolt sql-server at %s", socketPath);
2155
2156
  let proc$1;
2156
2157
  try {
2157
2158
  proc$1 = spawn("dolt", [
@@ -2172,26 +2173,26 @@ async function startDoltServer(projectRoot) {
2172
2173
  detached: false
2173
2174
  });
2174
2175
  } catch (err) {
2175
- logger$23.debug("Failed to spawn dolt sql-server: %s", err instanceof Error ? err.message : String(err));
2176
+ logger$25.debug("Failed to spawn dolt sql-server: %s", err instanceof Error ? err.message : String(err));
2176
2177
  return null;
2177
2178
  }
2178
2179
  let failed = false;
2179
2180
  proc$1.on("error", (err) => {
2180
- logger$23.debug("dolt sql-server error: %s", err.message);
2181
+ logger$25.debug("dolt sql-server error: %s", err.message);
2181
2182
  failed = true;
2182
2183
  });
2183
2184
  proc$1.on("exit", (code) => {
2184
- if (code !== null && code !== 0) logger$23.debug("dolt sql-server exited with code %d", code);
2185
+ if (code !== null && code !== 0) logger$25.debug("dolt sql-server exited with code %d", code);
2185
2186
  });
2186
2187
  proc$1.stderr?.on("data", (chunk) => {
2187
2188
  const line = chunk.toString().trim();
2188
- if (line) logger$23.debug("dolt-server: %s", line);
2189
+ if (line) logger$25.debug("dolt-server: %s", line);
2189
2190
  });
2190
2191
  const deadline = Date.now() + 5e3;
2191
2192
  while (Date.now() < deadline && !failed) try {
2192
2193
  await access$1(socketPath);
2193
2194
  const pid = proc$1.pid ?? 0;
2194
- logger$23.info("Auto-started dolt sql-server (pid=%d, socket=%s)", pid, socketPath);
2195
+ logger$25.info("Auto-started dolt sql-server (pid=%d, socket=%s)", pid, socketPath);
2195
2196
  let stopped = false;
2196
2197
  return {
2197
2198
  pid,
@@ -2199,14 +2200,14 @@ async function startDoltServer(projectRoot) {
2199
2200
  stop: () => {
2200
2201
  if (stopped) return;
2201
2202
  stopped = true;
2202
- logger$23.debug("Stopping dolt sql-server (pid=%d)", pid);
2203
+ logger$25.debug("Stopping dolt sql-server (pid=%d)", pid);
2203
2204
  proc$1.kill("SIGTERM");
2204
2205
  }
2205
2206
  };
2206
2207
  } catch {
2207
2208
  await new Promise((resolve$6) => setTimeout(resolve$6, 100));
2208
2209
  }
2209
- logger$23.debug("dolt sql-server did not start within 5s — killing");
2210
+ logger$25.debug("dolt sql-server did not start within 5s — killing");
2210
2211
  proc$1.kill("SIGTERM");
2211
2212
  return null;
2212
2213
  }
@@ -2276,7 +2277,7 @@ function truncateToTokens(text, maxTokens) {
2276
2277
 
2277
2278
  //#endregion
2278
2279
  //#region src/modules/context-compiler/context-compiler-impl.ts
2279
- const logger$22 = createLogger("context-compiler");
2280
+ const logger$24 = createLogger("context-compiler");
2280
2281
  /**
2281
2282
  * Fraction of the original token budget that must remain (after required +
2282
2283
  * important sections) before an optional section is included.
@@ -2337,7 +2338,7 @@ var ContextCompilerImpl = class {
2337
2338
  }
2338
2339
  _applyExclusionFilter(text, sectionName) {
2339
2340
  for (const excludedPath of this._excludedPaths) if (text.includes(excludedPath)) {
2340
- logger$22.warn({
2341
+ logger$24.warn({
2341
2342
  sectionName,
2342
2343
  excludedPath
2343
2344
  }, "ContextCompiler: section excluded — contains path from exclusion list");
@@ -2395,7 +2396,7 @@ var ContextCompilerImpl = class {
2395
2396
  includedParts.push(truncated);
2396
2397
  remainingBudget -= truncatedTokens;
2397
2398
  anyTruncated = true;
2398
- logger$22.warn({
2399
+ logger$24.warn({
2399
2400
  section: section.name,
2400
2401
  originalTokens: tokens,
2401
2402
  budgetTokens: truncatedTokens
@@ -2409,7 +2410,7 @@ var ContextCompilerImpl = class {
2409
2410
  });
2410
2411
  } else {
2411
2412
  anyTruncated = true;
2412
- logger$22.warn({
2413
+ logger$24.warn({
2413
2414
  section: section.name,
2414
2415
  tokens
2415
2416
  }, "Context compiler: omitted \"important\" section — no budget remaining");
@@ -2436,7 +2437,7 @@ var ContextCompilerImpl = class {
2436
2437
  } else {
2437
2438
  if (tokens > 0) {
2438
2439
  anyTruncated = true;
2439
- logger$22.warn({
2440
+ logger$24.warn({
2440
2441
  section: section.name,
2441
2442
  tokens,
2442
2443
  budgetFractionRemaining: budgetFractionRemaining.toFixed(2)
@@ -2530,8 +2531,8 @@ var GrammarLoader = class {
2530
2531
  _extensionMap;
2531
2532
  _cache = new Map();
2532
2533
  _unavailable = false;
2533
- constructor(logger$24) {
2534
- this._logger = logger$24;
2534
+ constructor(logger$26) {
2535
+ this._logger = logger$26;
2535
2536
  this._extensionMap = new Map([
2536
2537
  [".ts", "tree-sitter-typescript/typescript"],
2537
2538
  [".tsx", "tree-sitter-typescript/tsx"],
@@ -2568,8 +2569,8 @@ var GrammarLoader = class {
2568
2569
  throw err;
2569
2570
  }
2570
2571
  }
2571
- _loadModule(path$3) {
2572
- return __require(path$3);
2572
+ _loadModule(path$4) {
2573
+ return __require(path$4);
2573
2574
  }
2574
2575
  };
2575
2576
 
@@ -2617,9 +2618,9 @@ const ERR_REPO_MAP_GIT_FAILED = "ERR_REPO_MAP_GIT_FAILED";
2617
2618
  var SymbolParser = class {
2618
2619
  _grammarLoader;
2619
2620
  _logger;
2620
- constructor(grammarLoader, logger$24) {
2621
+ constructor(grammarLoader, logger$26) {
2621
2622
  this._grammarLoader = grammarLoader;
2622
- this._logger = logger$24;
2623
+ this._logger = logger$26;
2623
2624
  }
2624
2625
  async parseFile(filePath) {
2625
2626
  const ext$2 = extname$1(filePath);
@@ -2764,9 +2765,9 @@ async function computeFileHash(filePath) {
2764
2765
  var DoltSymbolRepository = class {
2765
2766
  _client;
2766
2767
  _logger;
2767
- constructor(client, logger$24) {
2768
+ constructor(client, logger$26) {
2768
2769
  this._client = client;
2769
- this._logger = logger$24;
2770
+ this._logger = logger$26;
2770
2771
  }
2771
2772
  /**
2772
2773
  * Atomically replace all symbols for filePath.
@@ -2972,11 +2973,11 @@ var RepoMapStorage = class {
2972
2973
  _metaRepo;
2973
2974
  _gitClient;
2974
2975
  _logger;
2975
- constructor(symbolRepo, metaRepo, gitClient, logger$24) {
2976
+ constructor(symbolRepo, metaRepo, gitClient, logger$26) {
2976
2977
  this._symbolRepo = symbolRepo;
2977
2978
  this._metaRepo = metaRepo;
2978
2979
  this._gitClient = gitClient;
2979
- this._logger = logger$24;
2980
+ this._logger = logger$26;
2980
2981
  }
2981
2982
  /**
2982
2983
  * Returns true if the file's current content hash differs from the stored hash.
@@ -3093,8 +3094,8 @@ function runGit(args, cwd) {
3093
3094
  */
3094
3095
  var GitClient = class {
3095
3096
  _logger;
3096
- constructor(logger$24) {
3097
- this._logger = logger$24;
3097
+ constructor(logger$26) {
3098
+ this._logger = logger$26;
3098
3099
  }
3099
3100
  /**
3100
3101
  * Returns the current HEAD commit SHA.
@@ -3926,12 +3927,12 @@ const qmarksTestNoExtDot$1 = ([$0]) => {
3926
3927
  };
3927
3928
  /* c8 ignore start */
3928
3929
  const defaultPlatform$3 = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
3929
- const path$2 = {
3930
+ const path$3 = {
3930
3931
  win32: { sep: "\\" },
3931
3932
  posix: { sep: "/" }
3932
3933
  };
3933
3934
  /* c8 ignore stop */
3934
- const sep$1 = defaultPlatform$3 === "win32" ? path$2.win32.sep : path$2.posix.sep;
3935
+ const sep$1 = defaultPlatform$3 === "win32" ? path$3.win32.sep : path$3.posix.sep;
3935
3936
  minimatch$1.sep = sep$1;
3936
3937
  const GLOBSTAR$1 = Symbol("globstar **");
3937
3938
  minimatch$1.GLOBSTAR = GLOBSTAR$1;
@@ -4450,9 +4451,9 @@ var RepoMapQueryEngine = class {
4450
4451
  repo;
4451
4452
  logger;
4452
4453
  telemetry;
4453
- constructor(repo, logger$24, telemetry) {
4454
+ constructor(repo, logger$26, telemetry) {
4454
4455
  this.repo = repo;
4455
- this.logger = logger$24;
4456
+ this.logger = logger$26;
4456
4457
  this.telemetry = telemetry;
4457
4458
  }
4458
4459
  async query(q) {
@@ -4672,9 +4673,9 @@ var RepoMapFormatter = class {
4672
4673
  var RepoMapTelemetry = class {
4673
4674
  _telemetry;
4674
4675
  _logger;
4675
- constructor(telemetry, logger$24) {
4676
+ constructor(telemetry, logger$26) {
4676
4677
  this._telemetry = telemetry;
4677
- this._logger = logger$24;
4678
+ this._logger = logger$26;
4678
4679
  }
4679
4680
  /**
4680
4681
  * Emit a `repo_map.query` span.
@@ -4699,9 +4700,9 @@ var RepoMapTelemetry = class {
4699
4700
  var RepoMapModule = class {
4700
4701
  _metaRepo;
4701
4702
  _logger;
4702
- constructor(metaRepo, logger$24) {
4703
+ constructor(metaRepo, logger$26) {
4703
4704
  this._metaRepo = metaRepo;
4704
- this._logger = logger$24;
4705
+ this._logger = logger$26;
4705
4706
  }
4706
4707
  /**
4707
4708
  * Check whether the stored repo-map is stale relative to the current HEAD commit.
@@ -4745,9 +4746,9 @@ var RepoMapModule = class {
4745
4746
  var RepoMapInjector = class {
4746
4747
  _queryEngine;
4747
4748
  _logger;
4748
- constructor(queryEngine, logger$24) {
4749
+ constructor(queryEngine, logger$26) {
4749
4750
  this._queryEngine = queryEngine;
4750
- this._logger = logger$24;
4751
+ this._logger = logger$26;
4751
4752
  }
4752
4753
  /**
4753
4754
  * Build repo-map context by extracting file references from the story content,
@@ -4823,12 +4824,13 @@ const DEFAULT_TIMEOUTS = {
4823
4824
  "arch-decisions": 24e4,
4824
4825
  "arch-patterns": 24e4,
4825
4826
  "story-epics": 24e4,
4826
- "story-stories": 6e5
4827
+ "story-stories": 6e5,
4828
+ "probe-author": 3e5
4827
4829
  };
4828
4830
 
4829
4831
  //#endregion
4830
4832
  //#region src/modules/agent-dispatch/dispatcher-impl.ts
4831
- const logger$21 = createLogger("agent-dispatch");
4833
+ const logger$23 = createLogger("agent-dispatch");
4832
4834
  /**
4833
4835
  * Create a new Dispatcher instance.
4834
4836
  *
@@ -4972,7 +4974,7 @@ function runBuildVerification(options) {
4972
4974
  let cmd;
4973
4975
  if (verifyCommand === void 0) {
4974
4976
  const detection = detectPackageManager(projectRoot);
4975
- logger$21.info({
4977
+ logger$23.info({
4976
4978
  packageManager: detection.packageManager,
4977
4979
  lockfile: detection.lockfile,
4978
4980
  resolvedCommand: detection.command
@@ -4984,7 +4986,7 @@ function runBuildVerification(options) {
4984
4986
  const filters = deriveTurboFilters(changedFiles, projectRoot);
4985
4987
  if (filters.length > 0) {
4986
4988
  cmd = `${cmd} ${filters.join(" ")}`;
4987
- logger$21.info({
4989
+ logger$23.info({
4988
4990
  filters,
4989
4991
  originalCmd: options.verifyCommand ?? "(auto-detected)"
4990
4992
  }, "Build verification: scoped turbo build to affected packages");
@@ -5027,7 +5029,7 @@ function runBuildVerification(options) {
5027
5029
  };
5028
5030
  const missingScriptPattern = /Missing script[:\s]|No script found|Command "build" not found/i;
5029
5031
  if (missingScriptPattern.test(combinedOutput)) {
5030
- logger$21.warn("Build script not found — skipping pre-flight (greenfield repo)");
5032
+ logger$23.warn("Build script not found — skipping pre-flight (greenfield repo)");
5031
5033
  return {
5032
5034
  status: "skipped",
5033
5035
  exitCode,
@@ -5037,7 +5039,7 @@ function runBuildVerification(options) {
5037
5039
  }
5038
5040
  const pep668Pattern = /externally-managed-environment|This environment is externally managed/i;
5039
5041
  if (pep668Pattern.test(combinedOutput)) {
5040
- logger$21.warn("PEP 668: pip blocked by externally-managed-environment — skipping pre-flight. Create a .venv to resolve.");
5042
+ logger$23.warn("PEP 668: pip blocked by externally-managed-environment — skipping pre-flight. Create a .venv to resolve.");
5041
5043
  return {
5042
5044
  status: "skipped",
5043
5045
  exitCode,
@@ -5221,7 +5223,7 @@ function pickRecommendation(distribution, profile, totalIssues, reviewCycles, la
5221
5223
 
5222
5224
  //#endregion
5223
5225
  //#region src/modules/implementation-orchestrator/project-findings.ts
5224
- const logger$20 = createLogger("project-findings");
5226
+ const logger$22 = createLogger("project-findings");
5225
5227
  /** Maximum character length for the findings summary */
5226
5228
  const MAX_CHARS = 2e3;
5227
5229
  /**
@@ -5294,7 +5296,7 @@ async function getProjectFindings(db) {
5294
5296
  if (summary.length > MAX_CHARS) summary = summary.slice(0, MAX_CHARS - 3) + "...";
5295
5297
  return summary;
5296
5298
  } catch (err) {
5297
- logger$20.warn({ err }, "Failed to query project findings (graceful fallback)");
5299
+ logger$22.warn({ err }, "Failed to query project findings (graceful fallback)");
5298
5300
  return "";
5299
5301
  }
5300
5302
  }
@@ -5317,7 +5319,7 @@ function extractRecurringPatterns(outcomes) {
5317
5319
 
5318
5320
  //#endregion
5319
5321
  //#region src/modules/compiled-workflows/prompt-assembler.ts
5320
- const logger$19 = createLogger("compiled-workflows:prompt-assembler");
5322
+ const logger$21 = createLogger("compiled-workflows:prompt-assembler");
5321
5323
  /**
5322
5324
  * Assemble a final prompt from a template and sections map.
5323
5325
  *
@@ -5342,7 +5344,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
5342
5344
  tokenCount,
5343
5345
  truncated: false
5344
5346
  };
5345
- logger$19.warn({
5347
+ logger$21.warn({
5346
5348
  tokenCount,
5347
5349
  ceiling: tokenCeiling
5348
5350
  }, "Prompt exceeds token ceiling — truncating optional sections");
@@ -5358,10 +5360,10 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
5358
5360
  const targetSectionTokens = Math.max(0, currentSectionTokens - overBy);
5359
5361
  if (targetSectionTokens === 0) {
5360
5362
  contentMap[section.name] = "";
5361
- logger$19.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
5363
+ logger$21.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
5362
5364
  } else {
5363
5365
  contentMap[section.name] = truncateToTokens(section.content, targetSectionTokens);
5364
- logger$19.warn({
5366
+ logger$21.warn({
5365
5367
  sectionName: section.name,
5366
5368
  targetSectionTokens
5367
5369
  }, "Section truncated to fit token budget");
@@ -5372,7 +5374,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
5372
5374
  }
5373
5375
  if (tokenCount <= tokenCeiling) break;
5374
5376
  }
5375
- if (tokenCount > tokenCeiling) logger$19.warn({
5377
+ if (tokenCount > tokenCeiling) logger$21.warn({
5376
5378
  tokenCount,
5377
5379
  ceiling: tokenCeiling
5378
5380
  }, "Required sections alone exceed token ceiling — returning over-budget prompt");
@@ -5633,6 +5635,43 @@ const TestExpansionResultSchema = z.object({
5633
5635
  suggested_tests: z.array(SuggestedTestSchema).default([]),
5634
5636
  notes: z.string().optional()
5635
5637
  });
5638
+ /**
5639
+ * Inline mirror of RuntimeProbeSchema from @substrate-ai/sdlc.
5640
+ *
5641
+ * Inlined here instead of imported to avoid triggering Vitest's mock validation
5642
+ * error in orchestrator tests that mock @substrate-ai/sdlc without including
5643
+ * RuntimeProbeListSchema. The canonical definition lives in
5644
+ * packages/sdlc/src/verification/probes/types.ts — keep in sync when that
5645
+ * schema changes.
5646
+ */
5647
+ const InlineRuntimeProbeSchema = z.object({
5648
+ name: z.string().min(1, "probe name is required"),
5649
+ sandbox: z.enum(["host", "twin"]),
5650
+ command: z.string().min(1, "probe command is required"),
5651
+ timeout_ms: z.number().int().positive().optional(),
5652
+ description: z.string().optional(),
5653
+ expect_stdout_no_regex: z.array(z.string().min(1)).optional(),
5654
+ expect_stdout_regex: z.array(z.string().min(1)).optional()
5655
+ });
5656
+ const InlineRuntimeProbeListSchema = z.array(InlineRuntimeProbeSchema);
5657
+ /**
5658
+ * Schema for the YAML output contract of the probe-author sub-agent.
5659
+ *
5660
+ * The agent emits a yaml block containing result and probes list.
5661
+ * The probes field is validated against the RuntimeProbe shape from @substrate-ai/sdlc
5662
+ * (inlined to avoid module mock conflicts in orchestrator unit tests).
5663
+ *
5664
+ * Example:
5665
+ * result: success
5666
+ * probes:
5667
+ * - name: my-probe
5668
+ * sandbox: host
5669
+ * command: echo "hello"
5670
+ */
5671
+ const ProbeAuthorResultSchema = z.object({
5672
+ result: z.preprocess((val) => val === "failure" ? "failed" : val, z.enum(["success", "failed"])),
5673
+ probes: InlineRuntimeProbeListSchema
5674
+ });
5636
5675
 
5637
5676
  //#endregion
5638
5677
  //#region src/modules/compiled-workflows/token-ceiling.ts
@@ -5645,7 +5684,8 @@ const TOKEN_CEILING_DEFAULTS = {
5645
5684
  "dev-story": 4e5,
5646
5685
  "code-review": 5e5,
5647
5686
  "test-plan": 1e5,
5648
- "test-expansion": 2e5
5687
+ "test-expansion": 2e5,
5688
+ "probe-author": 5e4
5649
5689
  };
5650
5690
  /**
5651
5691
  * Resolve the effective token ceiling for a workflow type.
@@ -5674,7 +5714,7 @@ function getTokenCeiling(workflowType, tokenCeilings) {
5674
5714
 
5675
5715
  //#endregion
5676
5716
  //#region src/modules/compiled-workflows/create-story.ts
5677
- const logger$18 = createLogger("compiled-workflows:create-story");
5717
+ const logger$20 = createLogger("compiled-workflows:create-story");
5678
5718
  /**
5679
5719
  * Compute a hex SHA-256 of the normalized source AC section text.
5680
5720
  *
@@ -5709,13 +5749,13 @@ function hashSourceAcSection(section) {
5709
5749
  */
5710
5750
  async function runCreateStory(deps, params) {
5711
5751
  const { epicId, storyKey, pipelineRunId, source_ac_hash, priorDriftFeedback } = params;
5712
- logger$18.debug({
5752
+ logger$20.debug({
5713
5753
  epicId,
5714
5754
  storyKey,
5715
5755
  pipelineRunId
5716
5756
  }, "Starting create-story workflow");
5717
5757
  const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("create-story", deps.tokenCeilings);
5718
- logger$18.info({
5758
+ logger$20.info({
5719
5759
  workflow: "create-story",
5720
5760
  ceiling: TOKEN_CEILING,
5721
5761
  source: tokenCeilingSource
@@ -5725,7 +5765,7 @@ async function runCreateStory(deps, params) {
5725
5765
  template = await deps.pack.getPrompt("create-story");
5726
5766
  } catch (err) {
5727
5767
  const error = err instanceof Error ? err.message : String(err);
5728
- logger$18.error({ error }, "Failed to retrieve create-story prompt template");
5768
+ logger$20.error({ error }, "Failed to retrieve create-story prompt template");
5729
5769
  return {
5730
5770
  result: "failed",
5731
5771
  error: `Failed to retrieve prompt template: ${error}`,
@@ -5742,7 +5782,7 @@ async function runCreateStory(deps, params) {
5742
5782
  const storySection = extractStorySection(epicShardContent, storyKey);
5743
5783
  if (storySection !== null) {
5744
5784
  const computedHash = hashSourceAcSection(storySection);
5745
- if (source_ac_hash !== void 0 && source_ac_hash !== computedHash) logger$18.debug({
5785
+ if (source_ac_hash !== void 0 && source_ac_hash !== computedHash) logger$20.debug({
5746
5786
  storyKey,
5747
5787
  suppliedHash: source_ac_hash,
5748
5788
  computedHash
@@ -5757,7 +5797,7 @@ async function runCreateStory(deps, params) {
5757
5797
  const storyDef = storyDecisions.find((d) => d.category === "stories" && d.key === storyKey);
5758
5798
  if (storyDef) {
5759
5799
  storyDefinitionContent = storyDef.value;
5760
- logger$18.debug({ storyKey }, "Injected story definition from solutioning decisions");
5800
+ logger$20.debug({ storyKey }, "Injected story definition from solutioning decisions");
5761
5801
  }
5762
5802
  } catch {}
5763
5803
  const archConstraintsContent = await getArchConstraints$3(deps);
@@ -5808,7 +5848,7 @@ async function runCreateStory(deps, params) {
5808
5848
  priority: "optional"
5809
5849
  }]
5810
5850
  ], TOKEN_CEILING);
5811
- logger$18.debug({
5851
+ logger$20.debug({
5812
5852
  tokenCount,
5813
5853
  truncated,
5814
5854
  tokenCeiling: TOKEN_CEILING
@@ -5828,7 +5868,7 @@ async function runCreateStory(deps, params) {
5828
5868
  dispatchResult = await handle.result;
5829
5869
  } catch (err) {
5830
5870
  const error = err instanceof Error ? err.message : String(err);
5831
- logger$18.error({
5871
+ logger$20.error({
5832
5872
  epicId,
5833
5873
  storyKey,
5834
5874
  error
@@ -5849,7 +5889,7 @@ async function runCreateStory(deps, params) {
5849
5889
  if (dispatchResult.status === "failed") {
5850
5890
  const errorMsg = dispatchResult.parseError ?? `Dispatch failed with exit code ${dispatchResult.exitCode}`;
5851
5891
  const stderrDetail = dispatchResult.output ? ` Output: ${dispatchResult.output}` : "";
5852
- logger$18.warn({
5892
+ logger$20.warn({
5853
5893
  epicId,
5854
5894
  storyKey,
5855
5895
  exitCode: dispatchResult.exitCode,
@@ -5862,7 +5902,7 @@ async function runCreateStory(deps, params) {
5862
5902
  };
5863
5903
  }
5864
5904
  if (dispatchResult.status === "timeout") {
5865
- logger$18.warn({
5905
+ logger$20.warn({
5866
5906
  epicId,
5867
5907
  storyKey
5868
5908
  }, "Create-story dispatch timed out");
@@ -5875,7 +5915,7 @@ async function runCreateStory(deps, params) {
5875
5915
  if (dispatchResult.parsed === null) {
5876
5916
  const details = dispatchResult.parseError ?? "No YAML block found in output";
5877
5917
  const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
5878
- logger$18.warn({
5918
+ logger$20.warn({
5879
5919
  epicId,
5880
5920
  storyKey,
5881
5921
  details,
@@ -5891,7 +5931,7 @@ async function runCreateStory(deps, params) {
5891
5931
  const parseResult = CreateStoryResultSchema.safeParse(dispatchResult.parsed);
5892
5932
  if (!parseResult.success) {
5893
5933
  const details = parseResult.error.message;
5894
- logger$18.warn({
5934
+ logger$20.warn({
5895
5935
  epicId,
5896
5936
  storyKey,
5897
5937
  details
@@ -5904,7 +5944,7 @@ async function runCreateStory(deps, params) {
5904
5944
  };
5905
5945
  }
5906
5946
  const parsed = parseResult.data;
5907
- logger$18.info({
5947
+ logger$20.info({
5908
5948
  epicId,
5909
5949
  storyKey,
5910
5950
  storyFile: parsed.story_file,
@@ -5932,7 +5972,7 @@ async function getImplementationDecisions(deps, pipelineRunId) {
5932
5972
  }
5933
5973
  return await getDecisionsByPhase(deps.db, "implementation");
5934
5974
  } catch (err) {
5935
- logger$18.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
5975
+ logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
5936
5976
  return [];
5937
5977
  }
5938
5978
  }
@@ -6019,10 +6059,10 @@ function computeStoryFileFidelity(storyFileContent, namedPaths) {
6019
6059
  };
6020
6060
  const missing = [];
6021
6061
  const present = [];
6022
- for (const path$3 of namedPaths) {
6023
- const basename$2 = path$3.includes("/") ? path$3.slice(path$3.lastIndexOf("/") + 1) : path$3;
6024
- if (storyFileContent.includes(path$3) || storyFileContent.includes(basename$2)) present.push(path$3);
6025
- else missing.push(path$3);
6062
+ for (const path$4 of namedPaths) {
6063
+ const basename$2 = path$4.includes("/") ? path$4.slice(path$4.lastIndexOf("/") + 1) : path$4;
6064
+ if (storyFileContent.includes(path$4) || storyFileContent.includes(basename$2)) present.push(path$4);
6065
+ else missing.push(path$4);
6026
6066
  }
6027
6067
  return {
6028
6068
  missing,
@@ -6134,7 +6174,7 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
6134
6174
  if (storyKey) {
6135
6175
  const perStoryShard = decisions.find((d) => d.category === "epic-shard" && d.key === storyKey);
6136
6176
  if (perStoryShard?.value) {
6137
- logger$18.debug({
6177
+ logger$20.debug({
6138
6178
  epicId,
6139
6179
  storyKey
6140
6180
  }, "Found per-story epic shard (direct lookup)");
@@ -6146,13 +6186,13 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
6146
6186
  if (shardContent && storyKey) {
6147
6187
  const storySection = extractStorySection(shardContent, storyKey);
6148
6188
  if (storySection) {
6149
- logger$18.debug({
6189
+ logger$20.debug({
6150
6190
  epicId,
6151
6191
  storyKey
6152
6192
  }, "Extracted per-story section from epic shard (pre-37-0 fallback)");
6153
6193
  return storySection;
6154
6194
  }
6155
- logger$18.info({
6195
+ logger$20.info({
6156
6196
  epicId,
6157
6197
  storyKey
6158
6198
  }, "Story section absent in decisions-store shard — attempting file-based fallback before returning stale shard");
@@ -6160,11 +6200,11 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
6160
6200
  if (projectRoot) {
6161
6201
  const fallback = readEpicShardFromFile(projectRoot, epicId);
6162
6202
  if (fallback) {
6163
- logger$18.info({ epicId }, "Using file-based fallback for epic shard");
6203
+ logger$20.info({ epicId }, "Using file-based fallback for epic shard");
6164
6204
  if (storyKey) {
6165
6205
  const storySection = extractStorySection(fallback, storyKey);
6166
6206
  if (storySection) {
6167
- logger$18.debug({
6207
+ logger$20.debug({
6168
6208
  epicId,
6169
6209
  storyKey
6170
6210
  }, "Extracted per-story section from file-based epic shard");
@@ -6177,7 +6217,7 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
6177
6217
  if (shardContent) return shardContent;
6178
6218
  return "";
6179
6219
  } catch (err) {
6180
- logger$18.warn({
6220
+ logger$20.warn({
6181
6221
  epicId,
6182
6222
  error: err instanceof Error ? err.message : String(err)
6183
6223
  }, "Failed to retrieve epic shard");
@@ -6194,7 +6234,7 @@ function getPrevDevNotes(decisions, epicId) {
6194
6234
  if (devNotes.length === 0) return "";
6195
6235
  return devNotes[devNotes.length - 1].value;
6196
6236
  } catch (err) {
6197
- logger$18.warn({
6237
+ logger$20.warn({
6198
6238
  epicId,
6199
6239
  error: err instanceof Error ? err.message : String(err)
6200
6240
  }, "Failed to retrieve prev dev notes");
@@ -6227,7 +6267,7 @@ async function getArchConstraints$3(deps) {
6227
6267
  const truncatedBody = body.length > 300 ? body.slice(0, 297) + "..." : body;
6228
6268
  return `${header}\n${truncatedBody}`;
6229
6269
  }).join("\n\n");
6230
- logger$18.info({
6270
+ logger$20.info({
6231
6271
  fullLength: full.length,
6232
6272
  summarizedLength: summarized.length,
6233
6273
  decisions: constraints.length
@@ -6237,13 +6277,13 @@ async function getArchConstraints$3(deps) {
6237
6277
  if (deps.projectRoot) {
6238
6278
  const fallback = readArchConstraintsFromFile(deps.projectRoot);
6239
6279
  if (fallback) {
6240
- logger$18.info("Using file-based fallback for architecture constraints (decisions table empty)");
6280
+ logger$20.info("Using file-based fallback for architecture constraints (decisions table empty)");
6241
6281
  return fallback.length > ARCH_CONSTRAINT_MAX_CHARS ? fallback.slice(0, ARCH_CONSTRAINT_MAX_CHARS) + "\n\n[truncated for token budget]" : fallback;
6242
6282
  }
6243
6283
  }
6244
6284
  return "";
6245
6285
  } catch (err) {
6246
- logger$18.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
6286
+ logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
6247
6287
  return "";
6248
6288
  }
6249
6289
  }
@@ -6296,7 +6336,7 @@ function readEpicShardFromFile(projectRoot, epicId) {
6296
6336
  } catch {}
6297
6337
  return "";
6298
6338
  } catch (err) {
6299
- logger$18.warn({
6339
+ logger$20.warn({
6300
6340
  epicId,
6301
6341
  error: err instanceof Error ? err.message : String(err)
6302
6342
  }, "File-based epic shard fallback failed");
@@ -6319,7 +6359,7 @@ function readArchConstraintsFromFile(projectRoot) {
6319
6359
  const content = readFileSync(archPath, "utf-8");
6320
6360
  return content.slice(0, 1500);
6321
6361
  } catch (err) {
6322
- logger$18.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
6362
+ logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
6323
6363
  return "";
6324
6364
  }
6325
6365
  }
@@ -6332,7 +6372,7 @@ async function getStoryTemplate(deps) {
6332
6372
  try {
6333
6373
  return await deps.pack.getTemplate("story");
6334
6374
  } catch (err) {
6335
- logger$18.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
6375
+ logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
6336
6376
  return "";
6337
6377
  }
6338
6378
  }
@@ -6369,7 +6409,7 @@ async function isValidStoryFile(filePath) {
6369
6409
 
6370
6410
  //#endregion
6371
6411
  //#region src/modules/compiled-workflows/git-helpers.ts
6372
- const logger$17 = createLogger("compiled-workflows:git-helpers");
6412
+ const logger$19 = createLogger("compiled-workflows:git-helpers");
6373
6413
  /**
6374
6414
  * Check whether the repo at `cwd` has at least one commit (HEAD resolves).
6375
6415
  * Returns false for fresh repos with no commits, avoiding `fatal: bad revision 'HEAD'`.
@@ -6406,7 +6446,7 @@ function hasCommits(cwd) {
6406
6446
  */
6407
6447
  async function getGitDiffSummary(workingDirectory = process.cwd()) {
6408
6448
  if (!hasCommits(workingDirectory)) {
6409
- logger$17.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff");
6449
+ logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff");
6410
6450
  return "";
6411
6451
  }
6412
6452
  return runGitCommand(["diff", "HEAD"], workingDirectory, "git-diff-summary");
@@ -6423,7 +6463,7 @@ async function getGitDiffSummary(workingDirectory = process.cwd()) {
6423
6463
  */
6424
6464
  async function getGitDiffStatSummary(workingDirectory = process.cwd()) {
6425
6465
  if (!hasCommits(workingDirectory)) {
6426
- logger$17.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat");
6466
+ logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat");
6427
6467
  return "";
6428
6468
  }
6429
6469
  return runGitCommand([
@@ -6472,7 +6512,7 @@ async function getGitDiffStatBetweenCommits(baseCommit, endCommit = "HEAD", work
6472
6512
  async function getGitDiffForFiles(files, workingDirectory = process.cwd()) {
6473
6513
  if (files.length === 0) return "";
6474
6514
  if (!hasCommits(workingDirectory)) {
6475
- logger$17.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff for files");
6515
+ logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff for files");
6476
6516
  return "";
6477
6517
  }
6478
6518
  await stageIntentToAdd(files, workingDirectory);
@@ -6499,7 +6539,7 @@ async function getGitDiffForFiles(files, workingDirectory = process.cwd()) {
6499
6539
  async function getGitDiffStatForFiles(files, workingDirectory = process.cwd()) {
6500
6540
  if (files.length === 0) return "";
6501
6541
  if (!hasCommits(workingDirectory)) {
6502
- logger$17.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat for files");
6542
+ logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat for files");
6503
6543
  return "";
6504
6544
  }
6505
6545
  return runGitCommand([
@@ -6548,7 +6588,7 @@ async function stageIntentToAdd(files, workingDirectory) {
6548
6588
  if (files.length === 0) return;
6549
6589
  const existing = files.filter((f$1) => {
6550
6590
  const exists = existsSync(f$1);
6551
- if (!exists) logger$17.debug({ file: f$1 }, "Skipping nonexistent file in stageIntentToAdd");
6591
+ if (!exists) logger$19.debug({ file: f$1 }, "Skipping nonexistent file in stageIntentToAdd");
6552
6592
  return exists;
6553
6593
  });
6554
6594
  if (existing.length === 0) return;
@@ -6582,7 +6622,7 @@ async function runGitCommand(args, cwd, logLabel) {
6582
6622
  stderr += chunk.toString("utf-8");
6583
6623
  });
6584
6624
  proc$1.on("error", (err) => {
6585
- logger$17.warn({
6625
+ logger$19.warn({
6586
6626
  label: logLabel,
6587
6627
  cwd,
6588
6628
  error: err.message
@@ -6591,7 +6631,7 @@ async function runGitCommand(args, cwd, logLabel) {
6591
6631
  });
6592
6632
  proc$1.on("close", (code) => {
6593
6633
  if (code !== 0) {
6594
- logger$17.warn({
6634
+ logger$19.warn({
6595
6635
  label: logLabel,
6596
6636
  cwd,
6597
6637
  code,
@@ -6607,7 +6647,7 @@ async function runGitCommand(args, cwd, logLabel) {
6607
6647
 
6608
6648
  //#endregion
6609
6649
  //#region src/modules/compiled-workflows/story-complexity.ts
6610
- const logger$16 = createLogger("compiled-workflows:story-complexity");
6650
+ const logger$18 = createLogger("compiled-workflows:story-complexity");
6611
6651
  /**
6612
6652
  * Compute a complexity score from story markdown content.
6613
6653
  *
@@ -6659,7 +6699,7 @@ function resolveFixStoryMaxTurns(complexityScore) {
6659
6699
  * @param resolvedMaxTurns - Turn limit resolved for this dispatch
6660
6700
  */
6661
6701
  function logComplexityResult(storyKey, complexity, resolvedMaxTurns) {
6662
- logger$16.info({
6702
+ logger$18.info({
6663
6703
  storyKey,
6664
6704
  taskCount: complexity.taskCount,
6665
6705
  subtaskCount: complexity.subtaskCount,
@@ -6915,9 +6955,9 @@ function resolveInstallCommand(projectRoot) {
6915
6955
 
6916
6956
  //#endregion
6917
6957
  //#region src/modules/compiled-workflows/dev-story.ts
6918
- const logger$15 = createLogger("compiled-workflows:dev-story");
6958
+ const logger$17 = createLogger("compiled-workflows:dev-story");
6919
6959
  /** Default timeout for dev-story dispatches in milliseconds (30 min) */
6920
- const DEFAULT_TIMEOUT_MS$1 = 18e5;
6960
+ const DEFAULT_TIMEOUT_MS$2 = 18e5;
6921
6961
  /**
6922
6962
  * Execute the compiled dev-story workflow.
6923
6963
  *
@@ -6927,12 +6967,12 @@ const DEFAULT_TIMEOUT_MS$1 = 18e5;
6927
6967
  */
6928
6968
  async function runDevStory(deps, params) {
6929
6969
  const { storyKey, storyFilePath, taskScope, priorFiles, findingsPrompt: handlerFindingsPrompt } = params;
6930
- logger$15.info({
6970
+ logger$17.info({
6931
6971
  storyKey,
6932
6972
  storyFilePath
6933
6973
  }, "Starting compiled dev-story workflow");
6934
6974
  const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("dev-story", deps.tokenCeilings);
6935
- logger$15.info({
6975
+ logger$17.info({
6936
6976
  workflow: "dev-story",
6937
6977
  ceiling: TOKEN_CEILING,
6938
6978
  source: tokenCeilingSource
@@ -6975,10 +7015,10 @@ async function runDevStory(deps, params) {
6975
7015
  let template;
6976
7016
  try {
6977
7017
  template = await deps.pack.getPrompt("dev-story");
6978
- logger$15.debug({ storyKey }, "Retrieved dev-story prompt template from pack");
7018
+ logger$17.debug({ storyKey }, "Retrieved dev-story prompt template from pack");
6979
7019
  } catch (err) {
6980
7020
  const error = err instanceof Error ? err.message : String(err);
6981
- logger$15.error({
7021
+ logger$17.error({
6982
7022
  storyKey,
6983
7023
  error
6984
7024
  }, "Failed to retrieve dev-story prompt template");
@@ -6989,14 +7029,14 @@ async function runDevStory(deps, params) {
6989
7029
  storyContent = await readFile$1(storyFilePath, "utf-8");
6990
7030
  } catch (err) {
6991
7031
  if (err.code === "ENOENT") {
6992
- logger$15.error({
7032
+ logger$17.error({
6993
7033
  storyKey,
6994
7034
  storyFilePath
6995
7035
  }, "Story file not found");
6996
7036
  return makeFailureResult("story_file_not_found");
6997
7037
  }
6998
7038
  const error = err instanceof Error ? err.message : String(err);
6999
- logger$15.error({
7039
+ logger$17.error({
7000
7040
  storyKey,
7001
7041
  storyFilePath,
7002
7042
  error
@@ -7004,7 +7044,7 @@ async function runDevStory(deps, params) {
7004
7044
  return makeFailureResult(`story_file_read_error: ${error}`);
7005
7045
  }
7006
7046
  if (storyContent.trim().length === 0) {
7007
- logger$15.error({
7047
+ logger$17.error({
7008
7048
  storyKey,
7009
7049
  storyFilePath
7010
7050
  }, "Story file is empty");
@@ -7012,7 +7052,7 @@ async function runDevStory(deps, params) {
7012
7052
  }
7013
7053
  const staleStatus = detectDeprecatedStatusField(storyContent);
7014
7054
  if (staleStatus !== null) {
7015
- logger$15.warn({
7055
+ logger$17.warn({
7016
7056
  storyFilePath,
7017
7057
  staleStatus
7018
7058
  }, "Story spec contains deprecated Status field — stripped before dispatch (status is managed by Dolt work graph)");
@@ -7027,17 +7067,17 @@ async function runDevStory(deps, params) {
7027
7067
  const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
7028
7068
  if (testPatternDecisions.length > 0) {
7029
7069
  testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
7030
- logger$15.debug({
7070
+ logger$17.debug({
7031
7071
  storyKey,
7032
7072
  count: testPatternDecisions.length
7033
7073
  }, "Loaded test patterns from decision store");
7034
7074
  } else {
7035
7075
  testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
7036
- logger$15.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
7076
+ logger$17.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
7037
7077
  }
7038
7078
  } catch (err) {
7039
7079
  const error = err instanceof Error ? err.message : String(err);
7040
- logger$15.warn({
7080
+ logger$17.warn({
7041
7081
  storyKey,
7042
7082
  error
7043
7083
  }, "Failed to load test patterns — using defaults");
@@ -7051,7 +7091,7 @@ async function runDevStory(deps, params) {
7051
7091
  if (deps.repoMapInjector !== void 0) {
7052
7092
  const injection = await deps.repoMapInjector.buildContext(storyContent, deps.maxRepoMapTokens ?? 2e3);
7053
7093
  repoContextContent = injection.text;
7054
- logger$15.info({
7094
+ logger$17.info({
7055
7095
  storyKey,
7056
7096
  repoMapTokens: Math.ceil(injection.text.length / 4),
7057
7097
  symbolCount: injection.symbolCount,
@@ -7061,7 +7101,7 @@ async function runDevStory(deps, params) {
7061
7101
  let priorFindingsContent = "";
7062
7102
  if (handlerFindingsPrompt !== void 0 && handlerFindingsPrompt.length > 0) {
7063
7103
  priorFindingsContent = handlerFindingsPrompt;
7064
- logger$15.debug({
7104
+ logger$17.debug({
7065
7105
  storyKey,
7066
7106
  findingsLen: handlerFindingsPrompt.length
7067
7107
  }, "Using pre-computed findings from handler (Story 53-8 AC2)");
@@ -7073,7 +7113,7 @@ async function runDevStory(deps, params) {
7073
7113
  });
7074
7114
  if (findings.length > 0) {
7075
7115
  priorFindingsContent = findings;
7076
- logger$15.debug({
7116
+ logger$17.debug({
7077
7117
  storyKey,
7078
7118
  findingsLen: findings.length
7079
7119
  }, "Injecting relevance-scored findings into dev-story prompt");
@@ -7093,7 +7133,7 @@ async function runDevStory(deps, params) {
7093
7133
  if (plan.test_categories && plan.test_categories.length > 0) parts.push(`\n### Categories: ${plan.test_categories.join(", ")}`);
7094
7134
  if (plan.coverage_notes) parts.push(`\n### Coverage Notes\n${plan.coverage_notes}`);
7095
7135
  testPlanContent = parts.join("\n");
7096
- logger$15.debug({ storyKey }, "Injecting test plan into dev-story prompt");
7136
+ logger$17.debug({ storyKey }, "Injecting test plan into dev-story prompt");
7097
7137
  }
7098
7138
  } catch {}
7099
7139
  const sections = [
@@ -7154,7 +7194,7 @@ async function runDevStory(deps, params) {
7154
7194
  }
7155
7195
  ];
7156
7196
  const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING);
7157
- logger$15.info({
7197
+ logger$17.info({
7158
7198
  storyKey,
7159
7199
  tokenCount,
7160
7200
  ceiling: TOKEN_CEILING,
@@ -7166,7 +7206,7 @@ async function runDevStory(deps, params) {
7166
7206
  prompt,
7167
7207
  agent: deps.agentId ?? "claude-code",
7168
7208
  taskType: "dev-story",
7169
- timeout: DEFAULT_TIMEOUT_MS$1,
7209
+ timeout: DEFAULT_TIMEOUT_MS$2,
7170
7210
  outputSchema: DevStoryResultSchema,
7171
7211
  maxTurns: resolvedMaxTurns,
7172
7212
  ...deps.projectRoot !== void 0 ? { workingDirectory: deps.projectRoot } : {},
@@ -7178,7 +7218,7 @@ async function runDevStory(deps, params) {
7178
7218
  dispatchResult = await handle.result;
7179
7219
  } catch (err) {
7180
7220
  const error = err instanceof Error ? err.message : String(err);
7181
- logger$15.error({
7221
+ logger$17.error({
7182
7222
  storyKey,
7183
7223
  error
7184
7224
  }, "Dispatch threw an unexpected error");
@@ -7189,11 +7229,11 @@ async function runDevStory(deps, params) {
7189
7229
  output: dispatchResult.tokenEstimate.output
7190
7230
  };
7191
7231
  if (dispatchResult.status === "timeout") {
7192
- logger$15.error({
7232
+ logger$17.error({
7193
7233
  storyKey,
7194
7234
  durationMs: dispatchResult.durationMs
7195
7235
  }, "Dev-story dispatch timed out");
7196
- if (dispatchResult.output.length > 0) logger$15.info({
7236
+ if (dispatchResult.output.length > 0) logger$17.info({
7197
7237
  storyKey,
7198
7238
  partialOutput: dispatchResult.output.slice(0, 500)
7199
7239
  }, "Partial output before timeout");
@@ -7203,12 +7243,12 @@ async function runDevStory(deps, params) {
7203
7243
  };
7204
7244
  }
7205
7245
  if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
7206
- logger$15.error({
7246
+ logger$17.error({
7207
7247
  storyKey,
7208
7248
  exitCode: dispatchResult.exitCode,
7209
7249
  status: dispatchResult.status
7210
7250
  }, "Dev-story dispatch failed");
7211
- if (dispatchResult.output.length > 0) logger$15.info({
7251
+ if (dispatchResult.output.length > 0) logger$17.info({
7212
7252
  storyKey,
7213
7253
  partialOutput: dispatchResult.output.slice(0, 500)
7214
7254
  }, "Partial output from failed dispatch");
@@ -7220,7 +7260,7 @@ async function runDevStory(deps, params) {
7220
7260
  if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
7221
7261
  const details = dispatchResult.parseError ?? "parsed result was null";
7222
7262
  const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
7223
- logger$15.error({
7263
+ logger$17.error({
7224
7264
  storyKey,
7225
7265
  parseError: details,
7226
7266
  rawOutputSnippet: rawSnippet
@@ -7228,12 +7268,12 @@ async function runDevStory(deps, params) {
7228
7268
  let filesModified = [];
7229
7269
  try {
7230
7270
  filesModified = await getGitChangedFiles(deps.projectRoot ?? process.cwd());
7231
- if (filesModified.length > 0) logger$15.info({
7271
+ if (filesModified.length > 0) logger$17.info({
7232
7272
  storyKey,
7233
7273
  fileCount: filesModified.length
7234
7274
  }, "Recovered files_modified from git status (YAML fallback)");
7235
7275
  } catch (err) {
7236
- logger$15.warn({
7276
+ logger$17.warn({
7237
7277
  storyKey,
7238
7278
  error: err instanceof Error ? err.message : String(err)
7239
7279
  }, "Failed to recover files_modified from git");
@@ -7250,7 +7290,7 @@ async function runDevStory(deps, params) {
7250
7290
  };
7251
7291
  }
7252
7292
  const parsed = dispatchResult.parsed;
7253
- logger$15.info({
7293
+ logger$17.info({
7254
7294
  storyKey,
7255
7295
  result: parsed.result,
7256
7296
  acMet: parsed.ac_met.length
@@ -7366,7 +7406,7 @@ function extractReferencedFiles(storyContent) {
7366
7406
  const matches = storyContent.match(filePathRegex) ?? [];
7367
7407
  const freq = new Map();
7368
7408
  for (const m of matches) freq.set(m, (freq.get(m) ?? 0) + 1);
7369
- const sorted = [...freq.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([path$3]) => path$3);
7409
+ const sorted = [...freq.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([path$4]) => path$4);
7370
7410
  return sorted.filter((p) => !p.includes(".test."));
7371
7411
  }
7372
7412
  function extractFilesInScope(storyContent) {
@@ -7390,19 +7430,6 @@ function extractFilesInScope(storyContent) {
7390
7430
  //#endregion
7391
7431
  //#region src/modules/compiled-workflows/scope-guardrail.ts
7392
7432
  /**
7393
- * ScopeGuardrail — utility for detecting out-of-scope file modifications in code review.
7394
- *
7395
- * Parses the expected file set from a story spec and compares it against the
7396
- * actual files modified by the dev agent. Returns a pre-computed scope analysis
7397
- * markdown string that can be injected into the code-review prompt so the LLM
7398
- * reviewer does not need to re-parse the spec sections manually.
7399
- *
7400
- * Architecture constraints:
7401
- * - Pure utility: no imports from packages/sdlc/, packages/core/, or persistence layer
7402
- * - Takes plain strings in, returns plain strings out
7403
- * - Test-file exemption patterns must match countTestMetrics() in code-review.ts
7404
- */
7405
- /**
7406
7433
  * File extensions that qualify a token as a "path-like" string in task bullets.
7407
7434
  * Any token containing `/` and one of these extensions is treated as a file path.
7408
7435
  */
@@ -7495,13 +7522,17 @@ var ScopeGuardrail = class ScopeGuardrail {
7495
7522
  *
7496
7523
  * @param storyContent - Raw story spec markdown text
7497
7524
  * @param filesModified - List of file paths from the git diff
7525
+ * @param fileDiffs - Optional per-file diff map. When provided, transitive
7526
+ * re-exports (Story 61-5) whose `from` source IS in
7527
+ * expectedFiles are excluded from the out-of-scope set.
7498
7528
  * @returns Markdown string listing expected/actual/delta file sets,
7499
7529
  * or empty string if no violations found
7500
7530
  */
7501
- static buildAnalysis(storyContent, filesModified) {
7531
+ static buildAnalysis(storyContent, filesModified, fileDiffs) {
7502
7532
  const expectedFiles = ScopeGuardrail.parseExpectedFiles(storyContent);
7503
7533
  const nonTestFiles = filesModified.filter((f$1) => !isTestFile(f$1));
7504
- const outOfScope = nonTestFiles.filter((f$1) => !expectedFiles.has(f$1));
7534
+ let outOfScope = nonTestFiles.filter((f$1) => !expectedFiles.has(f$1));
7535
+ if (fileDiffs !== void 0) outOfScope = outOfScope.filter((filePath) => !isPureTransitiveReExport(filePath, fileDiffs.get(filePath), expectedFiles));
7505
7536
  if (outOfScope.length === 0) return "";
7506
7537
  const expectedList = expectedFiles.size > 0 ? Array.from(expectedFiles).map((f$1) => ` - ${f$1}`).join("\n") : " (none specified)";
7507
7538
  const actualList = nonTestFiles.length > 0 ? nonTestFiles.map((f$1) => ` - ${f$1}`).join("\n") : " (none)";
@@ -7571,10 +7602,108 @@ function isFilePath(candidate) {
7571
7602
  if (/\s/.test(candidate)) return false;
7572
7603
  return RECOGNIZED_EXTENSIONS.some((ext$2) => candidate.endsWith(ext$2));
7573
7604
  }
7605
+ /**
7606
+ * Split a combined `git diff` output into per-file diff sections, keyed by
7607
+ * the post-image (`b/`) path. Returns an empty map if the input is empty.
7608
+ *
7609
+ * Used by callers that need to feed per-file diffs into
7610
+ * `ScopeGuardrail.buildAnalysis` for transitive re-export detection.
7611
+ */
7612
+ function parseDiffByFile(combinedDiff) {
7613
+ const result = new Map();
7614
+ if (combinedDiff.trim() === "") return result;
7615
+ let currentPath = null;
7616
+ let currentLines = [];
7617
+ for (const line of combinedDiff.split("\n")) {
7618
+ const headerMatch = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
7619
+ if (headerMatch !== null) {
7620
+ if (currentPath !== null) result.set(currentPath, currentLines.join("\n"));
7621
+ currentPath = headerMatch[2] ?? null;
7622
+ currentLines = [line];
7623
+ continue;
7624
+ }
7625
+ if (currentPath !== null) currentLines.push(line);
7626
+ }
7627
+ if (currentPath !== null) result.set(currentPath, currentLines.join("\n"));
7628
+ return result;
7629
+ }
7630
+ /**
7631
+ * Single-line re-export pattern. Matches:
7632
+ * export { Foo } from './bar.js'
7633
+ * export { Foo, Bar as Baz } from './bar'
7634
+ * export type { Foo } from './bar.js'
7635
+ * export { type Foo, Bar } from './bar.js'
7636
+ *
7637
+ * Captures the relative `from` path (group 1). Path must start with `./`
7638
+ * (downward re-exports only — the canonical pattern is package barrel files
7639
+ * re-exporting submodule symbols). `../` paths are not tolerated; those
7640
+ * suggest cross-package coupling that warrants real review.
7641
+ */
7642
+ const REEXPORT_LINE_RE = /^\s*export\s+(?:type\s+)?\{[^}]+\}\s+from\s+['"](\.\/[^'"]+)['"]\s*;?\s*$/;
7643
+ /**
7644
+ * Returns true if `diffContent` represents a pure transitive re-export
7645
+ * change for `modifiedFilePath` — meaning every added/removed line is a
7646
+ * re-export whose `from` source resolves to a file in `expectedFiles`.
7647
+ *
7648
+ * Story 61-5: surfaced by 60-13 dispatch where dev added a 2-line re-export
7649
+ * of `detectsEventDrivenAC` to `verification/index.ts` (an `index.ts`
7650
+ * re-export hop required for cross-package access via `@substrate-ai/sdlc`).
7651
+ * scope-guardrail flagged it as out-of-scope, reviewer admitted "the change
7652
+ * is clearly required" but flagged anyway, story timed out → escalated.
7653
+ *
7654
+ * Returns false when:
7655
+ * - diff content unavailable
7656
+ * - any non-re-export, non-comment, non-blank change exists
7657
+ * - any re-export's resolved `from` source is NOT in expectedFiles
7658
+ * - diff is empty (no changes — preserve existing behavior)
7659
+ */
7660
+ function isPureTransitiveReExport(modifiedFilePath, diffContent, expectedFiles) {
7661
+ if (diffContent === void 0 || diffContent.trim() === "") return false;
7662
+ let sawChange = false;
7663
+ for (const line of diffContent.split("\n")) {
7664
+ if (line.startsWith("+++") || line.startsWith("---") || line.startsWith("@@") || line.startsWith("diff ") || line.startsWith("index ") || line.startsWith("similarity ") || line.startsWith("rename ") || line.startsWith("new file ") || line.startsWith("deleted file ") || line.startsWith("Binary ") || line.startsWith("\\ No newline")) continue;
7665
+ if (!line.startsWith("+") && !line.startsWith("-")) continue;
7666
+ const content = line.slice(1);
7667
+ const trimmed = content.trim();
7668
+ if (trimmed === "") continue;
7669
+ if (trimmed.startsWith("//")) continue;
7670
+ sawChange = true;
7671
+ const match$2 = trimmed.match(REEXPORT_LINE_RE);
7672
+ if (match$2 === null) return false;
7673
+ const fromPath = match$2[1] ?? "";
7674
+ if (!resolvesIntoExpected(modifiedFilePath, fromPath, expectedFiles)) return false;
7675
+ }
7676
+ return sawChange;
7677
+ }
7678
+ /**
7679
+ * Resolve a `from './x.js'` path relative to `modifiedFilePath`'s directory
7680
+ * and check if the resolved path (or its `.ts` sibling) is in `expectedFiles`.
7681
+ *
7682
+ * ESM convention: source files are `.ts`, but imports use `.js` extensions.
7683
+ * We accept either extension when matching against expectedFiles.
7684
+ */
7685
+ function resolvesIntoExpected(modifiedFilePath, relativePath, expectedFiles) {
7686
+ const dir = path$2.posix.dirname(modifiedFilePath);
7687
+ const resolved = path$2.posix.normalize(path$2.posix.join(dir, relativePath));
7688
+ const candidates = new Set([resolved]);
7689
+ if (resolved.endsWith(".js")) {
7690
+ candidates.add(resolved.slice(0, -3) + ".ts");
7691
+ candidates.add(resolved.slice(0, -3) + ".tsx");
7692
+ }
7693
+ if (resolved.endsWith(".ts")) candidates.add(resolved.slice(0, -3) + ".js");
7694
+ if (!/\.(t|j)sx?$/.test(resolved)) {
7695
+ candidates.add(resolved + ".ts");
7696
+ candidates.add(resolved + ".tsx");
7697
+ candidates.add(resolved + ".js");
7698
+ candidates.add(resolved + "/index.ts");
7699
+ }
7700
+ for (const candidate of candidates) if (expectedFiles.has(candidate)) return true;
7701
+ return false;
7702
+ }
7574
7703
 
7575
7704
  //#endregion
7576
7705
  //#region src/modules/compiled-workflows/code-review.ts
7577
- const logger$14 = createLogger("compiled-workflows:code-review");
7706
+ const logger$16 = createLogger("compiled-workflows:code-review");
7578
7707
  /**
7579
7708
  * Default fallback result when dispatch fails or times out.
7580
7709
  * Uses NEEDS_MINOR_FIXES (not NEEDS_MAJOR_REWORK) so a parse/schema failure
@@ -7649,14 +7778,14 @@ async function countTestMetrics(filesModified, cwd) {
7649
7778
  async function runCodeReview(deps, params) {
7650
7779
  const { storyKey, storyFilePath, workingDirectory, pipelineRunId, filesModified, previousIssues, buildPassed, baselineCommit } = params;
7651
7780
  const cwd = workingDirectory ?? process.cwd();
7652
- logger$14.debug({
7781
+ logger$16.debug({
7653
7782
  storyKey,
7654
7783
  storyFilePath,
7655
7784
  cwd,
7656
7785
  pipelineRunId
7657
7786
  }, "Starting code-review workflow");
7658
7787
  const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("code-review", deps.tokenCeilings);
7659
- logger$14.info({
7788
+ logger$16.info({
7660
7789
  workflow: "code-review",
7661
7790
  ceiling: TOKEN_CEILING,
7662
7791
  source: tokenCeilingSource
@@ -7666,7 +7795,7 @@ async function runCodeReview(deps, params) {
7666
7795
  template = await deps.pack.getPrompt("code-review");
7667
7796
  } catch (err) {
7668
7797
  const error = err instanceof Error ? err.message : String(err);
7669
- logger$14.error({ error }, "Failed to retrieve code-review prompt template");
7798
+ logger$16.error({ error }, "Failed to retrieve code-review prompt template");
7670
7799
  return defaultFailResult(`Failed to retrieve prompt template: ${error}`, {
7671
7800
  input: 0,
7672
7801
  output: 0
@@ -7677,7 +7806,7 @@ async function runCodeReview(deps, params) {
7677
7806
  storyContent = await readFile$1(storyFilePath, "utf-8");
7678
7807
  } catch (err) {
7679
7808
  const error = err instanceof Error ? err.message : String(err);
7680
- logger$14.error({
7809
+ logger$16.error({
7681
7810
  storyFilePath,
7682
7811
  error
7683
7812
  }, "Failed to read story file");
@@ -7697,12 +7826,12 @@ async function runCodeReview(deps, params) {
7697
7826
  const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
7698
7827
  if (scopedTotal <= TOKEN_CEILING) {
7699
7828
  gitDiffContent = scopedDiff;
7700
- logger$14.debug({
7829
+ logger$16.debug({
7701
7830
  fileCount: filesModified.length,
7702
7831
  tokenCount: scopedTotal
7703
7832
  }, "Using scoped file diff");
7704
7833
  } else {
7705
- logger$14.warn({
7834
+ logger$16.warn({
7706
7835
  estimatedTotal: scopedTotal,
7707
7836
  ceiling: TOKEN_CEILING,
7708
7837
  fileCount: filesModified.length
@@ -7716,7 +7845,7 @@ async function runCodeReview(deps, params) {
7716
7845
  const fullTotal = nonDiffTokens + countTokens(fullDiff);
7717
7846
  if (fullTotal <= TOKEN_CEILING) gitDiffContent = fullDiff;
7718
7847
  else {
7719
- logger$14.warn({
7848
+ logger$16.warn({
7720
7849
  estimatedTotal: fullTotal,
7721
7850
  ceiling: TOKEN_CEILING
7722
7851
  }, "Full git diff would exceed token ceiling — using stat-only summary");
@@ -7724,7 +7853,7 @@ async function runCodeReview(deps, params) {
7724
7853
  }
7725
7854
  }
7726
7855
  if (gitDiffContent.trim().length === 0 && baselineCommit) {
7727
- logger$14.info({
7856
+ logger$16.info({
7728
7857
  storyKey,
7729
7858
  baselineCommit
7730
7859
  }, "Working tree diff empty — diffing against baseline commit");
@@ -7732,7 +7861,7 @@ async function runCodeReview(deps, params) {
7732
7861
  const commitTotal = nonDiffTokens + countTokens(commitDiff);
7733
7862
  if (commitDiff.trim().length > 0) if (commitTotal <= TOKEN_CEILING) gitDiffContent = commitDiff;
7734
7863
  else {
7735
- logger$14.warn({
7864
+ logger$16.warn({
7736
7865
  estimatedTotal: commitTotal,
7737
7866
  ceiling: TOKEN_CEILING
7738
7867
  }, "Baseline..HEAD diff exceeds token ceiling — using stat-only summary");
@@ -7740,7 +7869,7 @@ async function runCodeReview(deps, params) {
7740
7869
  }
7741
7870
  }
7742
7871
  if (gitDiffContent.trim().length === 0) {
7743
- logger$14.info({ storyKey }, "Empty git diff — skipping review with SHIP_IT");
7872
+ logger$16.info({ storyKey }, "Empty git diff — skipping review with SHIP_IT");
7744
7873
  return {
7745
7874
  verdict: "SHIP_IT",
7746
7875
  issues: 0,
@@ -7756,7 +7885,7 @@ async function runCodeReview(deps, params) {
7756
7885
  if (deps.repoMapInjector !== void 0) {
7757
7886
  const injection = await deps.repoMapInjector.buildContext(storyContent, deps.maxRepoMapTokens ?? 2e3);
7758
7887
  repoContextContent = injection.text;
7759
- logger$14.info({
7888
+ logger$16.info({
7760
7889
  storyKey,
7761
7890
  repoMapTokens: Math.ceil(injection.text.length / 4),
7762
7891
  symbolCount: injection.symbolCount,
@@ -7776,16 +7905,17 @@ async function runCodeReview(deps, params) {
7776
7905
  const findings = await getProjectFindings(deps.db);
7777
7906
  if (findings.length > 0) {
7778
7907
  priorFindingsContent = "Previous reviews found these recurring patterns — pay special attention:\n\n" + findings;
7779
- logger$14.debug({
7908
+ logger$16.debug({
7780
7909
  storyKey,
7781
7910
  findingsLen: findings.length
7782
7911
  }, "Injecting prior findings into code-review prompt");
7783
7912
  }
7784
7913
  } catch {}
7785
7914
  const testMetricsContent = await countTestMetrics(filesModified, cwd);
7786
- if (testMetricsContent) logger$14.debug({ storyKey }, "Injecting verified test-count metrics into code-review context");
7787
- const scopeAnalysisContent = storyContent && filesModified ? ScopeGuardrail.buildAnalysis(storyContent, filesModified) : "";
7788
- if (scopeAnalysisContent) logger$14.debug({ storyKey }, "Scope analysis detected out-of-scope files");
7915
+ if (testMetricsContent) logger$16.debug({ storyKey }, "Injecting verified test-count metrics into code-review context");
7916
+ const fileDiffs = gitDiffContent ? parseDiffByFile(gitDiffContent) : void 0;
7917
+ const scopeAnalysisContent = storyContent && filesModified ? ScopeGuardrail.buildAnalysis(storyContent, filesModified, fileDiffs) : "";
7918
+ if (scopeAnalysisContent) logger$16.debug({ storyKey }, "Scope analysis detected out-of-scope files");
7789
7919
  const buildStatusPrefix = buildPassed === true ? "BUILD STATUS: PASSED — code compiles and passes build verification. Focus on logic correctness, style, and acceptance criteria rather than compilation errors.\n\n" : "";
7790
7920
  const sections = [
7791
7921
  {
@@ -7830,11 +7960,11 @@ async function runCodeReview(deps, params) {
7830
7960
  }
7831
7961
  ];
7832
7962
  const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
7833
- if (assembleResult.truncated) logger$14.warn({
7963
+ if (assembleResult.truncated) logger$16.warn({
7834
7964
  storyKey,
7835
7965
  tokenCount: assembleResult.tokenCount
7836
7966
  }, "Code-review prompt truncated to fit token ceiling");
7837
- logger$14.debug({
7967
+ logger$16.debug({
7838
7968
  storyKey,
7839
7969
  tokenCount: assembleResult.tokenCount,
7840
7970
  truncated: assembleResult.truncated
@@ -7855,7 +7985,7 @@ async function runCodeReview(deps, params) {
7855
7985
  dispatchResult = await handle.result;
7856
7986
  } catch (err) {
7857
7987
  const error = err instanceof Error ? err.message : String(err);
7858
- logger$14.error({
7988
+ logger$16.error({
7859
7989
  storyKey,
7860
7990
  error
7861
7991
  }, "Code-review dispatch threw unexpected error");
@@ -7871,7 +8001,7 @@ async function runCodeReview(deps, params) {
7871
8001
  const rawOutput = dispatchResult.output ?? void 0;
7872
8002
  if (dispatchResult.status === "failed") {
7873
8003
  const errorMsg = `Dispatch status: failed. Exit code: ${dispatchResult.exitCode}. ${dispatchResult.parseError ?? ""} ${dispatchResult.output ? `Stderr: ${dispatchResult.output}` : ""}`.trim();
7874
- logger$14.warn({
8004
+ logger$16.warn({
7875
8005
  storyKey,
7876
8006
  exitCode: dispatchResult.exitCode
7877
8007
  }, "Code-review dispatch failed");
@@ -7881,7 +8011,7 @@ async function runCodeReview(deps, params) {
7881
8011
  };
7882
8012
  }
7883
8013
  if (dispatchResult.status === "timeout") {
7884
- logger$14.warn({ storyKey }, "Code-review dispatch timed out");
8014
+ logger$16.warn({ storyKey }, "Code-review dispatch timed out");
7885
8015
  return {
7886
8016
  ...defaultFailResult("Dispatch status: timeout. The agent did not complete within the allowed time.", tokenUsage),
7887
8017
  rawOutput
@@ -7889,7 +8019,7 @@ async function runCodeReview(deps, params) {
7889
8019
  }
7890
8020
  if (dispatchResult.parsed === null) {
7891
8021
  const details = dispatchResult.parseError ?? "No YAML block found in output";
7892
- logger$14.warn({
8022
+ logger$16.warn({
7893
8023
  storyKey,
7894
8024
  details
7895
8025
  }, "Code-review output schema validation failed");
@@ -7907,7 +8037,7 @@ async function runCodeReview(deps, params) {
7907
8037
  const parseResult = CodeReviewResultSchema.safeParse(dispatchResult.parsed);
7908
8038
  if (!parseResult.success) {
7909
8039
  const details = parseResult.error.message;
7910
- logger$14.warn({
8040
+ logger$16.warn({
7911
8041
  storyKey,
7912
8042
  details
7913
8043
  }, "Code-review output failed schema validation");
@@ -7923,13 +8053,13 @@ async function runCodeReview(deps, params) {
7923
8053
  };
7924
8054
  }
7925
8055
  const parsed = parseResult.data;
7926
- if (parsed.agentVerdict !== parsed.verdict) logger$14.info({
8056
+ if (parsed.agentVerdict !== parsed.verdict) logger$16.info({
7927
8057
  storyKey,
7928
8058
  agentVerdict: parsed.agentVerdict,
7929
8059
  pipelineVerdict: parsed.verdict,
7930
8060
  issues: parsed.issues
7931
8061
  }, "Pipeline overrode agent verdict based on issue severities");
7932
- logger$14.info({
8062
+ logger$16.info({
7933
8063
  storyKey,
7934
8064
  verdict: parsed.verdict,
7935
8065
  issues: parsed.issues
@@ -7954,16 +8084,16 @@ async function getArchConstraints$2(deps) {
7954
8084
  if (constraints.length === 0) return "";
7955
8085
  return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
7956
8086
  } catch (err) {
7957
- logger$14.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
8087
+ logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
7958
8088
  return "";
7959
8089
  }
7960
8090
  }
7961
8091
 
7962
8092
  //#endregion
7963
8093
  //#region src/modules/compiled-workflows/test-plan.ts
7964
- const logger$13 = createLogger("compiled-workflows:test-plan");
8094
+ const logger$15 = createLogger("compiled-workflows:test-plan");
7965
8095
  /** Default timeout for test-plan dispatches in milliseconds (5 min — lightweight call) */
7966
- const DEFAULT_TIMEOUT_MS = 3e5;
8096
+ const DEFAULT_TIMEOUT_MS$1 = 3e5;
7967
8097
  /**
7968
8098
  * Execute the compiled test-plan workflow.
7969
8099
  *
@@ -7973,12 +8103,12 @@ const DEFAULT_TIMEOUT_MS = 3e5;
7973
8103
  */
7974
8104
  async function runTestPlan(deps, params) {
7975
8105
  const { storyKey, storyFilePath, pipelineRunId } = params;
7976
- logger$13.info({
8106
+ logger$15.info({
7977
8107
  storyKey,
7978
8108
  storyFilePath
7979
8109
  }, "Starting compiled test-plan workflow");
7980
8110
  const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("test-plan", deps.tokenCeilings);
7981
- logger$13.info({
8111
+ logger$15.info({
7982
8112
  workflow: "test-plan",
7983
8113
  ceiling: TOKEN_CEILING,
7984
8114
  source: tokenCeilingSource
@@ -7986,10 +8116,10 @@ async function runTestPlan(deps, params) {
7986
8116
  let template;
7987
8117
  try {
7988
8118
  template = await deps.pack.getPrompt("test-plan");
7989
- logger$13.debug({ storyKey }, "Retrieved test-plan prompt template from pack");
8119
+ logger$15.debug({ storyKey }, "Retrieved test-plan prompt template from pack");
7990
8120
  } catch (err) {
7991
8121
  const error = err instanceof Error ? err.message : String(err);
7992
- logger$13.warn({
8122
+ logger$15.warn({
7993
8123
  storyKey,
7994
8124
  error
7995
8125
  }, "Failed to retrieve test-plan prompt template");
@@ -8000,14 +8130,14 @@ async function runTestPlan(deps, params) {
8000
8130
  storyContent = await readFile$1(storyFilePath, "utf-8");
8001
8131
  } catch (err) {
8002
8132
  if (err.code === "ENOENT") {
8003
- logger$13.warn({
8133
+ logger$15.warn({
8004
8134
  storyKey,
8005
8135
  storyFilePath
8006
8136
  }, "Story file not found for test planning");
8007
8137
  return makeTestPlanFailureResult("story_file_not_found");
8008
8138
  }
8009
8139
  const error = err instanceof Error ? err.message : String(err);
8010
- logger$13.warn({
8140
+ logger$15.warn({
8011
8141
  storyKey,
8012
8142
  storyFilePath,
8013
8143
  error
@@ -8021,13 +8151,13 @@ async function runTestPlan(deps, params) {
8021
8151
  const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
8022
8152
  if (testPatternDecisions.length > 0) {
8023
8153
  testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
8024
- logger$13.debug({
8154
+ logger$15.debug({
8025
8155
  storyKey,
8026
8156
  count: testPatternDecisions.length
8027
8157
  }, "Loaded test patterns from decision store");
8028
8158
  } else {
8029
8159
  testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
8030
- logger$13.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
8160
+ logger$15.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
8031
8161
  }
8032
8162
  } catch {
8033
8163
  testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
@@ -8049,7 +8179,7 @@ async function runTestPlan(deps, params) {
8049
8179
  priority: "optional"
8050
8180
  }
8051
8181
  ], TOKEN_CEILING);
8052
- logger$13.info({
8182
+ logger$15.info({
8053
8183
  storyKey,
8054
8184
  tokenCount,
8055
8185
  ceiling: TOKEN_CEILING,
@@ -8061,7 +8191,7 @@ async function runTestPlan(deps, params) {
8061
8191
  prompt,
8062
8192
  agent: deps.agentId ?? "claude-code",
8063
8193
  taskType: "test-plan",
8064
- timeout: DEFAULT_TIMEOUT_MS,
8194
+ timeout: DEFAULT_TIMEOUT_MS$1,
8065
8195
  outputSchema: TestPlanResultSchema,
8066
8196
  ...deps.projectRoot !== void 0 ? { workingDirectory: deps.projectRoot } : {},
8067
8197
  ...deps.otlpEndpoint !== void 0 ? { otlpEndpoint: deps.otlpEndpoint } : {},
@@ -8070,7 +8200,7 @@ async function runTestPlan(deps, params) {
8070
8200
  dispatchResult = await handle.result;
8071
8201
  } catch (err) {
8072
8202
  const error = err instanceof Error ? err.message : String(err);
8073
- logger$13.warn({
8203
+ logger$15.warn({
8074
8204
  storyKey,
8075
8205
  error
8076
8206
  }, "Test-plan dispatch threw an unexpected error");
@@ -8081,7 +8211,7 @@ async function runTestPlan(deps, params) {
8081
8211
  output: dispatchResult.tokenEstimate.output
8082
8212
  };
8083
8213
  if (dispatchResult.status === "timeout") {
8084
- logger$13.warn({
8214
+ logger$15.warn({
8085
8215
  storyKey,
8086
8216
  durationMs: dispatchResult.durationMs
8087
8217
  }, "Test-plan dispatch timed out");
@@ -8091,7 +8221,7 @@ async function runTestPlan(deps, params) {
8091
8221
  };
8092
8222
  }
8093
8223
  if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
8094
- logger$13.warn({
8224
+ logger$15.warn({
8095
8225
  storyKey,
8096
8226
  exitCode: dispatchResult.exitCode,
8097
8227
  status: dispatchResult.status
@@ -8103,7 +8233,7 @@ async function runTestPlan(deps, params) {
8103
8233
  }
8104
8234
  if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
8105
8235
  const details = dispatchResult.parseError ?? "parsed result was null";
8106
- logger$13.warn({
8236
+ logger$15.warn({
8107
8237
  storyKey,
8108
8238
  parseError: details
8109
8239
  }, "Test-plan YAML schema validation failed");
@@ -8126,19 +8256,19 @@ async function runTestPlan(deps, params) {
8126
8256
  }),
8127
8257
  rationale: `Test plan for ${storyKey}: ${parsed.test_files.length} test files, categories: ${parsed.test_categories.join(", ")}`
8128
8258
  });
8129
- logger$13.info({
8259
+ logger$15.info({
8130
8260
  storyKey,
8131
8261
  fileCount: parsed.test_files.length,
8132
8262
  categories: parsed.test_categories
8133
8263
  }, "Test plan stored in decision store");
8134
8264
  } catch (err) {
8135
8265
  const error = err instanceof Error ? err.message : String(err);
8136
- logger$13.warn({
8266
+ logger$15.warn({
8137
8267
  storyKey,
8138
8268
  error
8139
8269
  }, "Failed to store test plan in decision store — proceeding anyway");
8140
8270
  }
8141
- logger$13.info({
8271
+ logger$15.info({
8142
8272
  storyKey,
8143
8273
  result: parsed.result
8144
8274
  }, "Test-plan workflow completed");
@@ -8178,14 +8308,312 @@ async function getArchConstraints$1(deps) {
8178
8308
  if (constraints.length === 0) return "";
8179
8309
  return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
8180
8310
  } catch (err) {
8181
- logger$13.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints for test-plan — proceeding without them");
8311
+ logger$15.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints for test-plan — proceeding without them");
8182
8312
  return "";
8183
8313
  }
8184
8314
  }
8185
8315
 
8316
+ //#endregion
8317
+ //#region src/modules/implementation-orchestrator/probe-author-integration.ts
8318
+ const logger$14 = createLogger("implementation-orchestrator:probe-author");
8319
+ /** Default timeout for probe-author dispatches (5 min) */
8320
+ const DEFAULT_TIMEOUT_MS = 3e5;
8321
+ /** Timeout multiplier for the single retry after a timeout failure */
8322
+ const TIMEOUT_RETRY_MULTIPLIER = 1.5;
8323
+ /**
8324
+ * Execute the probe-author integration phase.
8325
+ *
8326
+ * Gate 1 — Event-driven AC check: calls `detectsEventDrivenAC(epicContent)`.
8327
+ * Skip if the source AC does not describe a hook, timer, signal, or webhook.
8328
+ *
8329
+ * Gate 2 — Idempotency check: reads storyFilePath and checks for an existing
8330
+ * `## Runtime Probes` section. Skip if present (probes already authored).
8331
+ *
8332
+ * Dispatch: assembles the probe-author prompt and dispatches via WorkflowDeps.
8333
+ * Uses ProbeAuthorResultSchema (result + probes) as outputSchema so the
8334
+ * existing YAML-parser anchor-key detection works correctly.
8335
+ *
8336
+ * Retry policy:
8337
+ * - Timeout → single retry at TIMEOUT_RETRY_MULTIPLIER × DEFAULT_TIMEOUT_MS.
8338
+ * Fall through if second attempt also times out.
8339
+ * - Invalid YAML → single retry with augmented prompt that includes the parse
8340
+ * error and the first 500 chars of bad output. Fall through if retry fails.
8341
+ * - Dispatch error (process crash, network failure) → fall through immediately.
8342
+ * - Empty probes list → emit `probe-author:no-probes-authored`, fall through.
8343
+ *
8344
+ * All failure paths are non-fatal — returns result: 'failed' instead of
8345
+ * throwing so the caller can unconditionally fall through to dev-story.
8346
+ */
8347
+ async function runProbeAuthor(deps, params) {
8348
+ const start = Date.now();
8349
+ const { storyKey, storyFilePath, pipelineRunId, sourceAcContent, epicContent, emitEvent } = params;
8350
+ const tokenUsage = {
8351
+ input: 0,
8352
+ output: 0
8353
+ };
8354
+ if (!detectsEventDrivenAC(epicContent)) {
8355
+ logger$14.debug({ storyKey }, "probe-author: source AC not event-driven — skipping");
8356
+ return makeSkippedResult(tokenUsage, start);
8357
+ }
8358
+ let storyContent;
8359
+ try {
8360
+ storyContent = await readFile$1(storyFilePath, "utf-8");
8361
+ if (/^## Runtime Probes/m.test(storyContent)) {
8362
+ logger$14.info({ storyKey }, "probe-author: story artifact already has ## Runtime Probes — skipping");
8363
+ return makeSkippedResult(tokenUsage, start);
8364
+ }
8365
+ } catch (err) {
8366
+ const error = err instanceof Error ? err.message : String(err);
8367
+ logger$14.warn({
8368
+ storyKey,
8369
+ error
8370
+ }, "probe-author: failed to read story file — falling through");
8371
+ emitEvent?.("probe-author:dispatch-error", {
8372
+ storyKey,
8373
+ runId: pipelineRunId,
8374
+ error
8375
+ });
8376
+ return makeFailedResult(`story_file_read_error: ${error}`, tokenUsage, start);
8377
+ }
8378
+ const { ceiling: TOKEN_CEILING } = getTokenCeiling("probe-author", deps.tokenCeilings);
8379
+ let template;
8380
+ try {
8381
+ template = await deps.pack.getPrompt("probe-author");
8382
+ logger$14.debug({ storyKey }, "probe-author: retrieved prompt template");
8383
+ } catch (err) {
8384
+ const error = err instanceof Error ? err.message : String(err);
8385
+ logger$14.warn({
8386
+ storyKey,
8387
+ error
8388
+ }, "probe-author: failed to get prompt template — falling through");
8389
+ emitEvent?.("probe-author:dispatch-error", {
8390
+ storyKey,
8391
+ runId: pipelineRunId,
8392
+ error
8393
+ });
8394
+ return makeFailedResult(`template_load_failed: ${error}`, tokenUsage, start);
8395
+ }
8396
+ const { prompt: basePrompt } = assemblePrompt(template, [{
8397
+ name: "rendered_ac_section",
8398
+ content: storyContent,
8399
+ priority: "required"
8400
+ }, {
8401
+ name: "source_epic_ac_section",
8402
+ content: sourceAcContent,
8403
+ priority: "required"
8404
+ }], TOKEN_CEILING);
8405
+ const doDispatch = async (promptText, timeoutMs) => {
8406
+ const handle = deps.dispatcher.dispatch({
8407
+ prompt: promptText,
8408
+ agent: deps.agentId ?? "claude-code",
8409
+ taskType: "probe-author",
8410
+ timeout: timeoutMs,
8411
+ outputSchema: ProbeAuthorResultSchema,
8412
+ ...deps.projectRoot !== void 0 ? { workingDirectory: deps.projectRoot } : {},
8413
+ ...deps.otlpEndpoint !== void 0 ? { otlpEndpoint: deps.otlpEndpoint } : {},
8414
+ storyKey
8415
+ });
8416
+ return await handle.result;
8417
+ };
8418
+ let dispatchResult;
8419
+ try {
8420
+ logger$14.info({ storyKey }, "probe-author: dispatching probe-author agent");
8421
+ dispatchResult = await doDispatch(basePrompt, DEFAULT_TIMEOUT_MS);
8422
+ tokenUsage.input += dispatchResult.tokenEstimate.input;
8423
+ tokenUsage.output += dispatchResult.tokenEstimate.output;
8424
+ } catch (err) {
8425
+ const error = err instanceof Error ? err.message : String(err);
8426
+ logger$14.warn({
8427
+ storyKey,
8428
+ error
8429
+ }, "probe-author: dispatch error — falling through to dev-story");
8430
+ emitEvent?.("probe-author:dispatch-error", {
8431
+ storyKey,
8432
+ runId: pipelineRunId,
8433
+ error
8434
+ });
8435
+ return makeFailedResult(`dispatch_error: ${error}`, tokenUsage, start);
8436
+ }
8437
+ if (dispatchResult.status === "timeout") {
8438
+ const elapsedMs = Date.now() - start;
8439
+ logger$14.warn({
8440
+ storyKey,
8441
+ elapsedMs
8442
+ }, "probe-author: dispatch timed out — retrying with 1.5× timeout");
8443
+ emitEvent?.("probe-author:timeout", {
8444
+ storyKey,
8445
+ runId: pipelineRunId,
8446
+ elapsedMs
8447
+ });
8448
+ try {
8449
+ const retryResult = await doDispatch(basePrompt, Math.round(DEFAULT_TIMEOUT_MS * TIMEOUT_RETRY_MULTIPLIER));
8450
+ tokenUsage.input += retryResult.tokenEstimate.input;
8451
+ tokenUsage.output += retryResult.tokenEstimate.output;
8452
+ if (retryResult.status === "timeout") {
8453
+ logger$14.warn({ storyKey }, "probe-author: retry also timed out — falling through to dev-story");
8454
+ return makeFailedResult("dispatch_timeout", tokenUsage, start);
8455
+ }
8456
+ dispatchResult = retryResult;
8457
+ } catch (retryErr) {
8458
+ const error = retryErr instanceof Error ? retryErr.message : String(retryErr);
8459
+ logger$14.warn({
8460
+ storyKey,
8461
+ error
8462
+ }, "probe-author: retry dispatch error — falling through to dev-story");
8463
+ return makeFailedResult(`retry_dispatch_error: ${error}`, tokenUsage, start);
8464
+ }
8465
+ }
8466
+ if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
8467
+ const error = `dispatch_failed with exit_code=${dispatchResult.exitCode}`;
8468
+ logger$14.warn({ storyKey }, `probe-author: ${error} — falling through to dev-story`);
8469
+ emitEvent?.("probe-author:dispatch-error", {
8470
+ storyKey,
8471
+ runId: pipelineRunId,
8472
+ error
8473
+ });
8474
+ return makeFailedResult(error, tokenUsage, start);
8475
+ }
8476
+ if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
8477
+ const parseError = dispatchResult.parseError ?? "parsed result was null";
8478
+ const rawOutputSnippet = dispatchResult.output.slice(0, 500);
8479
+ logger$14.warn({
8480
+ storyKey,
8481
+ parseError,
8482
+ rawOutputSnippet
8483
+ }, "probe-author: YAML parse failure — retrying with augmented prompt");
8484
+ emitEvent?.("probe-author:invalid-output", {
8485
+ storyKey,
8486
+ runId: pipelineRunId,
8487
+ parseError,
8488
+ rawOutputSnippet
8489
+ });
8490
+ const augmentedPrompt = `${basePrompt}\n\n---\n\nPrevious output failed parsing with: ${parseError}; produce a single yaml block conforming to RuntimeProbeListSchema`;
8491
+ try {
8492
+ const retryResult = await doDispatch(augmentedPrompt, DEFAULT_TIMEOUT_MS);
8493
+ tokenUsage.input += retryResult.tokenEstimate.input;
8494
+ tokenUsage.output += retryResult.tokenEstimate.output;
8495
+ if (retryResult.parseError !== null || retryResult.parsed === null) {
8496
+ logger$14.warn({ storyKey }, "probe-author: retry still produced invalid YAML — falling through");
8497
+ return makeFailedResult("invalid_yaml_after_retry", tokenUsage, start);
8498
+ }
8499
+ dispatchResult = retryResult;
8500
+ } catch (retryErr) {
8501
+ const error = retryErr instanceof Error ? retryErr.message : String(retryErr);
8502
+ logger$14.warn({
8503
+ storyKey,
8504
+ error
8505
+ }, "probe-author: retry error after invalid YAML — falling through");
8506
+ return makeFailedResult(`retry_error_after_invalid_yaml: ${error}`, tokenUsage, start);
8507
+ }
8508
+ }
8509
+ const parsedOutput = dispatchResult.parsed;
8510
+ const probeValidation = RuntimeProbeListSchema.safeParse(parsedOutput.probes);
8511
+ if (!probeValidation.success) {
8512
+ const validationError = probeValidation.error.message;
8513
+ logger$14.warn({
8514
+ storyKey,
8515
+ validationError
8516
+ }, "probe-author: probes failed RuntimeProbeListSchema — falling through");
8517
+ return makeFailedResult(`schema_validation_failed: ${validationError}`, tokenUsage, start);
8518
+ }
8519
+ const probes = probeValidation.data;
8520
+ if (probes.length === 0) {
8521
+ logger$14.info({ storyKey }, "probe-author: authored empty probes list — no probes needed");
8522
+ emitEvent?.("probe-author:no-probes-authored", {
8523
+ storyKey,
8524
+ runId: pipelineRunId
8525
+ });
8526
+ return {
8527
+ result: "success",
8528
+ probesAuthoredCount: 0,
8529
+ tokenUsage,
8530
+ durationMs: Date.now() - start
8531
+ };
8532
+ }
8533
+ try {
8534
+ const refreshedContent = await readFile$1(storyFilePath, "utf-8");
8535
+ if (/^## Runtime Probes/m.test(refreshedContent)) {
8536
+ logger$14.info({ storyKey }, "probe-author: ## Runtime Probes section appeared after dispatch — skipping append (idempotent)");
8537
+ const dispatchDurationMs$1 = Date.now() - start;
8538
+ emitEvent?.("probe-author:dispatched", {
8539
+ storyKey,
8540
+ runId: pipelineRunId,
8541
+ probesAuthoredCount: 0,
8542
+ dispatchDurationMs: dispatchDurationMs$1,
8543
+ costUsd: estimateDispatchCost$1(tokenUsage.input, tokenUsage.output)
8544
+ });
8545
+ return makeSkippedResult(tokenUsage, start);
8546
+ }
8547
+ const probesYaml = yaml.dump(probes, { lineWidth: 120 }).trimEnd();
8548
+ const probesSection = `\n## Runtime Probes\n\n\`\`\`yaml\n${probesYaml}\n\`\`\`\n`;
8549
+ const newContent = refreshedContent + probesSection;
8550
+ const targetDir = dirname$1(storyFilePath);
8551
+ const tmpPath = join$1(targetDir, `.probe-author-${Date.now()}.tmp.md`);
8552
+ await writeFile$1(tmpPath, newContent, "utf-8");
8553
+ await rename(tmpPath, storyFilePath);
8554
+ logger$14.info({
8555
+ storyKey,
8556
+ probesCount: probes.length
8557
+ }, "probe-author: appended ## Runtime Probes section");
8558
+ } catch (err) {
8559
+ const error = err instanceof Error ? err.message : String(err);
8560
+ logger$14.warn({
8561
+ storyKey,
8562
+ error
8563
+ }, "probe-author: failed to append probes — falling through to dev-story");
8564
+ return {
8565
+ result: "failed",
8566
+ probesAuthoredCount: probes.length,
8567
+ error: `append_error: ${error}`,
8568
+ tokenUsage,
8569
+ durationMs: Date.now() - start
8570
+ };
8571
+ }
8572
+ const dispatchDurationMs = Date.now() - start;
8573
+ const costUsd = estimateDispatchCost$1(tokenUsage.input, tokenUsage.output);
8574
+ emitEvent?.("probe-author:dispatched", {
8575
+ storyKey,
8576
+ runId: pipelineRunId,
8577
+ probesAuthoredCount: probes.length,
8578
+ dispatchDurationMs,
8579
+ costUsd
8580
+ });
8581
+ logger$14.info({
8582
+ storyKey,
8583
+ probesAuthoredCount: probes.length
8584
+ }, "probe-author: phase complete");
8585
+ return {
8586
+ result: "success",
8587
+ probesAuthoredCount: probes.length,
8588
+ tokenUsage,
8589
+ durationMs: Date.now() - start
8590
+ };
8591
+ }
8592
+ function makeSkippedResult(tokenUsage, start) {
8593
+ return {
8594
+ result: "skipped",
8595
+ probesAuthoredCount: 0,
8596
+ tokenUsage,
8597
+ durationMs: Date.now() - start
8598
+ };
8599
+ }
8600
+ function makeFailedResult(error, tokenUsage, start) {
8601
+ return {
8602
+ result: "failed",
8603
+ probesAuthoredCount: 0,
8604
+ error,
8605
+ tokenUsage,
8606
+ durationMs: Date.now() - start
8607
+ };
8608
+ }
8609
+ /** Claude pricing: $3/1M input, $15/1M output */
8610
+ function estimateDispatchCost$1(input, output) {
8611
+ return (input * 3 + output * 15) / 1e6;
8612
+ }
8613
+
8186
8614
  //#endregion
8187
8615
  //#region src/modules/compiled-workflows/test-expansion.ts
8188
- const logger$12 = createLogger("compiled-workflows:test-expansion");
8616
+ const logger$13 = createLogger("compiled-workflows:test-expansion");
8189
8617
  function defaultFallbackResult(error, tokenUsage) {
8190
8618
  return {
8191
8619
  expansion_priority: "low",
@@ -8215,14 +8643,14 @@ function defaultFallbackResult(error, tokenUsage) {
8215
8643
  async function runTestExpansion(deps, params) {
8216
8644
  const { storyKey, storyFilePath, pipelineRunId, filesModified, workingDirectory } = params;
8217
8645
  const cwd = workingDirectory ?? process.cwd();
8218
- logger$12.debug({
8646
+ logger$13.debug({
8219
8647
  storyKey,
8220
8648
  storyFilePath,
8221
8649
  cwd,
8222
8650
  pipelineRunId
8223
8651
  }, "Starting test-expansion workflow");
8224
8652
  const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("test-expansion", deps.tokenCeilings);
8225
- logger$12.info({
8653
+ logger$13.info({
8226
8654
  workflow: "test-expansion",
8227
8655
  ceiling: TOKEN_CEILING,
8228
8656
  source: tokenCeilingSource
@@ -8232,7 +8660,7 @@ async function runTestExpansion(deps, params) {
8232
8660
  template = await deps.pack.getPrompt("test-expansion");
8233
8661
  } catch (err) {
8234
8662
  const error = err instanceof Error ? err.message : String(err);
8235
- logger$12.warn({ error }, "Failed to retrieve test-expansion prompt template");
8663
+ logger$13.warn({ error }, "Failed to retrieve test-expansion prompt template");
8236
8664
  return defaultFallbackResult(`Failed to retrieve prompt template: ${error}`, {
8237
8665
  input: 0,
8238
8666
  output: 0
@@ -8243,7 +8671,7 @@ async function runTestExpansion(deps, params) {
8243
8671
  storyContent = await readFile$1(storyFilePath, "utf-8");
8244
8672
  } catch (err) {
8245
8673
  const error = err instanceof Error ? err.message : String(err);
8246
- logger$12.warn({
8674
+ logger$13.warn({
8247
8675
  storyFilePath,
8248
8676
  error
8249
8677
  }, "Failed to read story file");
@@ -8259,13 +8687,13 @@ async function runTestExpansion(deps, params) {
8259
8687
  const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
8260
8688
  if (testPatternDecisions.length > 0) {
8261
8689
  testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
8262
- logger$12.debug({
8690
+ logger$13.debug({
8263
8691
  storyKey,
8264
8692
  count: testPatternDecisions.length
8265
8693
  }, "Loaded test patterns from decision store");
8266
8694
  } else {
8267
8695
  testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
8268
- logger$12.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
8696
+ logger$13.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
8269
8697
  }
8270
8698
  } catch {
8271
8699
  testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
@@ -8280,12 +8708,12 @@ async function runTestExpansion(deps, params) {
8280
8708
  const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
8281
8709
  if (scopedTotal <= TOKEN_CEILING) {
8282
8710
  gitDiffContent = scopedDiff;
8283
- logger$12.debug({
8711
+ logger$13.debug({
8284
8712
  fileCount: filesModified.length,
8285
8713
  tokenCount: scopedTotal
8286
8714
  }, "Using scoped file diff");
8287
8715
  } else {
8288
- logger$12.warn({
8716
+ logger$13.warn({
8289
8717
  estimatedTotal: scopedTotal,
8290
8718
  ceiling: TOKEN_CEILING,
8291
8719
  fileCount: filesModified.length
@@ -8293,7 +8721,7 @@ async function runTestExpansion(deps, params) {
8293
8721
  gitDiffContent = await getGitDiffStatForFiles(filesModified, cwd);
8294
8722
  }
8295
8723
  } catch (err) {
8296
- logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to get git diff — proceeding with empty diff");
8724
+ logger$13.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to get git diff — proceeding with empty diff");
8297
8725
  }
8298
8726
  const sections = [
8299
8727
  {
@@ -8318,11 +8746,11 @@ async function runTestExpansion(deps, params) {
8318
8746
  }
8319
8747
  ];
8320
8748
  const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
8321
- if (assembleResult.truncated) logger$12.warn({
8749
+ if (assembleResult.truncated) logger$13.warn({
8322
8750
  storyKey,
8323
8751
  tokenCount: assembleResult.tokenCount
8324
8752
  }, "Test-expansion prompt truncated to fit token ceiling");
8325
- logger$12.debug({
8753
+ logger$13.debug({
8326
8754
  storyKey,
8327
8755
  tokenCount: assembleResult.tokenCount,
8328
8756
  truncated: assembleResult.truncated
@@ -8342,7 +8770,7 @@ async function runTestExpansion(deps, params) {
8342
8770
  dispatchResult = await handle.result;
8343
8771
  } catch (err) {
8344
8772
  const error = err instanceof Error ? err.message : String(err);
8345
- logger$12.warn({
8773
+ logger$13.warn({
8346
8774
  storyKey,
8347
8775
  error
8348
8776
  }, "Test-expansion dispatch threw unexpected error");
@@ -8357,19 +8785,19 @@ async function runTestExpansion(deps, params) {
8357
8785
  };
8358
8786
  if (dispatchResult.status === "failed") {
8359
8787
  const errorMsg = `Dispatch status: failed. Exit code: ${dispatchResult.exitCode}. ${dispatchResult.parseError ?? ""}`.trim();
8360
- logger$12.warn({
8788
+ logger$13.warn({
8361
8789
  storyKey,
8362
8790
  exitCode: dispatchResult.exitCode
8363
8791
  }, "Test-expansion dispatch failed");
8364
8792
  return defaultFallbackResult(errorMsg, tokenUsage);
8365
8793
  }
8366
8794
  if (dispatchResult.status === "timeout") {
8367
- logger$12.warn({ storyKey }, "Test-expansion dispatch timed out");
8795
+ logger$13.warn({ storyKey }, "Test-expansion dispatch timed out");
8368
8796
  return defaultFallbackResult("Dispatch status: timeout. The agent did not complete within the allowed time.", tokenUsage);
8369
8797
  }
8370
8798
  if (dispatchResult.parsed === null) {
8371
8799
  const details = dispatchResult.parseError ?? "No YAML block found in output";
8372
- logger$12.warn({
8800
+ logger$13.warn({
8373
8801
  storyKey,
8374
8802
  details
8375
8803
  }, "Test-expansion output has no parseable YAML");
@@ -8378,14 +8806,14 @@ async function runTestExpansion(deps, params) {
8378
8806
  const parseResult = TestExpansionResultSchema.safeParse(dispatchResult.parsed);
8379
8807
  if (!parseResult.success) {
8380
8808
  const details = parseResult.error.message;
8381
- logger$12.warn({
8809
+ logger$13.warn({
8382
8810
  storyKey,
8383
8811
  details
8384
8812
  }, "Test-expansion output failed schema validation");
8385
8813
  return defaultFallbackResult(`schema_validation_failed: ${details}`, tokenUsage);
8386
8814
  }
8387
8815
  const parsed = parseResult.data;
8388
- logger$12.info({
8816
+ logger$13.info({
8389
8817
  storyKey,
8390
8818
  expansion_priority: parsed.expansion_priority,
8391
8819
  coverage_gaps: parsed.coverage_gaps.length,
@@ -8410,11 +8838,15 @@ async function getArchConstraints(deps) {
8410
8838
  if (constraints.length === 0) return "";
8411
8839
  return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
8412
8840
  } catch (err) {
8413
- logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
8841
+ logger$13.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
8414
8842
  return "";
8415
8843
  }
8416
8844
  }
8417
8845
 
8846
+ //#endregion
8847
+ //#region src/modules/compiled-workflows/probe-author.ts
8848
+ const logger$12 = createLogger("compiled-workflows:probe-author");
8849
+
8418
8850
  //#endregion
8419
8851
  //#region src/modules/compiled-workflows/story-analyzer.ts
8420
8852
  /**
@@ -11103,6 +11535,76 @@ function findEpicFiles(projectRoot) {
11103
11535
  }
11104
11536
  }
11105
11537
  /**
11538
+ * Story 61-3: find the epic file relevant to a specific story.
11539
+ *
11540
+ * Sibling to `findEpicsFile` for the verification path
11541
+ * (`assembleVerificationContext` populates `sourceEpicContent` from this).
11542
+ * `findEpicsFile` only checks the consolidated convention (`epics.md`)
11543
+ * → returns undefined for projects using per-epic files (substrate's own
11544
+ * planning artifacts), causing `SourceAcFidelityCheck` to silently skip
11545
+ * with a `source-ac-source-unavailable` warn — exactly what happened on
11546
+ * the 60-12 redispatch (run 4700c6e8, 2026-04-27).
11547
+ *
11548
+ * Story 61-3 v2 (post-round-3): the v1 implementation honored
11549
+ * findEpicsFile's returned path without verifying it contained the
11550
+ * requested story. substrate's own findEpicsFile glob-matches
11551
+ * `epics-and-stories-*.md` files, so for projects with stale consolidated
11552
+ * files (substrate has `epics-and-stories-software-factory.md` for old
11553
+ * epics 40-50) the function returned that path → caller's
11554
+ * extractStorySection found nothing for new stories → sourceEpicContent
11555
+ * stayed undefined. This rev verifies file contains the story (via the
11556
+ * SAME `### Story X:` heading match the caller uses) before returning,
11557
+ * and falls through to per-epic search if not.
11558
+ *
11559
+ * Lookup order:
11560
+ * 1. Consolidated epics.md (existing findEpicsFile path) — return ONLY
11561
+ * if file content contains a `### Story <storyKey>:` heading.
11562
+ * 2. Per-epic file `epic-<epicNum>-*.md` derived from storyKey's first
11563
+ * numeric segment (e.g. storyKey '60-12' → epicNum '60' →
11564
+ * `epic-60-*.md`). Per-epic files contain the entire epic so a
11565
+ * filename match is sufficient (no content verification needed —
11566
+ * mirrors readEpicShardFromFile in create-story.ts).
11567
+ *
11568
+ * Returns the matched path, or undefined if no file contains the story.
11569
+ */
11570
+ function findEpicFileForStory(projectRoot, storyKey) {
11571
+ const consolidated = findEpicsFile(projectRoot);
11572
+ if (consolidated !== void 0) {
11573
+ if (fileContainsStory(consolidated, storyKey)) return consolidated;
11574
+ }
11575
+ const epicNumMatch = /^(\d+)/.exec(storyKey);
11576
+ if (!epicNumMatch) return void 0;
11577
+ const epicNum = epicNumMatch[1];
11578
+ const planningDir = join$1(projectRoot, "_bmad-output", "planning-artifacts");
11579
+ if (!existsSync(planningDir)) return void 0;
11580
+ try {
11581
+ const entries = readdirSync(planningDir, { encoding: "utf-8" });
11582
+ const perEpicPattern = new RegExp(`^epic-${epicNum}-.*\\.md$`);
11583
+ const matches = entries.filter((e) => perEpicPattern.test(e)).sort();
11584
+ if (matches.length > 0) return join$1(planningDir, matches[0]);
11585
+ } catch {}
11586
+ return void 0;
11587
+ }
11588
+ /**
11589
+ * Story 61-3 v2: check whether a file's content contains a story heading
11590
+ * matching the storyKey, with the same separator tolerance as
11591
+ * `extractStorySection` (Story 60-6) so both call sites agree.
11592
+ *
11593
+ * Cheap to call (one synchronous read, one regex test); gracefully
11594
+ * returns false on any I/O error.
11595
+ */
11596
+ function fileContainsStory(filePath, storyKey) {
11597
+ try {
11598
+ const content = readFileSync(filePath, "utf-8");
11599
+ const parts = storyKey.split(/[-._ ]/);
11600
+ const normalized = parts.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("[-._ ]");
11601
+ const headingPattern = new RegExp(`^###\\s+Story\\s+${normalized}[:\\s]`, "m");
11602
+ return headingPattern.test(content);
11603
+ } catch {
11604
+ return false;
11605
+ }
11606
+ }
11607
+ /**
11106
11608
  * Collect story keys that already have implementation artifact files.
11107
11609
  * Scans _bmad-output/implementation-artifacts/ for files matching N-M-*.md.
11108
11610
  */
@@ -11637,7 +12139,7 @@ function checkProfileStaleness(projectRoot) {
11637
12139
  */
11638
12140
  function createImplementationOrchestrator(deps) {
11639
12141
  const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot, tokenCeilings, stateStore, telemetryPersistence, ingestionServer, repoMapInjector, maxRepoMapTokens, agentId, runManifest = null } = deps;
11640
- const logger$24 = createLogger("implementation-orchestrator");
12142
+ const logger$26 = createLogger("implementation-orchestrator");
11641
12143
  const telemetryAdvisor = db !== void 0 ? createTelemetryAdvisor({ db }) : void 0;
11642
12144
  const wgRepo = new WorkGraphRepository(db);
11643
12145
  const _wgInProgressWritten = new Set();
@@ -11720,7 +12222,7 @@ function createImplementationOrchestrator(deps) {
11720
12222
  const existingCount = storyState?.retry_count ?? 0;
11721
12223
  _storyRetryCount.set(storyKey, existingCount);
11722
12224
  } catch (err) {
11723
- logger$24.warn({
12225
+ logger$26.warn({
11724
12226
  err,
11725
12227
  storyKey
11726
12228
  }, "initRetryCount: failed to read manifest — starting at 0");
@@ -11734,7 +12236,7 @@ function createImplementationOrchestrator(deps) {
11734
12236
  const current = _storyRetryCount.get(storyKey) ?? 0;
11735
12237
  const next = current + 1;
11736
12238
  _storyRetryCount.set(storyKey, next);
11737
- if (runManifest !== null && runManifest !== void 0) runManifest.patchStoryState(storyKey, { retry_count: next }).catch((err) => logger$24.warn({
12239
+ if (runManifest !== null && runManifest !== void 0) runManifest.patchStoryState(storyKey, { retry_count: next }).catch((err) => logger$26.warn({
11738
12240
  err,
11739
12241
  storyKey
11740
12242
  }, "patchStoryState(retry_count) failed — pipeline continues"));
@@ -11747,7 +12249,7 @@ function createImplementationOrchestrator(deps) {
11747
12249
  const nowMs = Date.now();
11748
12250
  for (const [phase, startMs] of starts) {
11749
12251
  const endMs = ends?.get(phase);
11750
- if (endMs === void 0) logger$24.warn({
12252
+ if (endMs === void 0) logger$26.warn({
11751
12253
  storyKey,
11752
12254
  phase
11753
12255
  }, "Phase has no end time — story may have errored mid-phase. Duration capped to now() and may be inflated.");
@@ -11764,7 +12266,7 @@ function createImplementationOrchestrator(deps) {
11764
12266
  const wallClockSeconds = startedAt ? Math.round((new Date(completedAt).getTime() - new Date(startedAt).getTime()) / 1e3) : 0;
11765
12267
  const wallClockMs = startedAt ? new Date(completedAt).getTime() - new Date(startedAt).getTime() : 0;
11766
12268
  const tokenAgg = await aggregateTokenUsageForStory(db, config.pipelineRunId, storyKey);
11767
- if (runManifest !== null) runManifest.patchStoryState(storyKey, { cost_usd: tokenAgg.cost }).catch((err) => logger$24.warn({
12269
+ if (runManifest !== null) runManifest.patchStoryState(storyKey, { cost_usd: tokenAgg.cost }).catch((err) => logger$26.warn({
11768
12270
  err,
11769
12271
  storyKey
11770
12272
  }, "patchStoryState(cost_usd) failed — pipeline continues"));
@@ -11800,7 +12302,7 @@ function createImplementationOrchestrator(deps) {
11800
12302
  recordedAt: completedAt,
11801
12303
  timestamp: completedAt
11802
12304
  }).catch((storeErr) => {
11803
- logger$24.warn({
12305
+ logger$26.warn({
11804
12306
  err: storeErr,
11805
12307
  storyKey
11806
12308
  }, "Failed to record metric to StateStore (best-effort)");
@@ -11822,7 +12324,7 @@ function createImplementationOrchestrator(deps) {
11822
12324
  rationale: `Story ${storyKey} completed with result=${result} in ${wallClockSeconds}s. Tokens: ${tokenAgg.input}+${tokenAgg.output}. Review cycles: ${reviewCycles}.`
11823
12325
  });
11824
12326
  } catch (decisionErr) {
11825
- logger$24.warn({
12327
+ logger$26.warn({
11826
12328
  err: decisionErr,
11827
12329
  storyKey
11828
12330
  }, "Failed to write story-metrics decision (best-effort)");
@@ -11901,7 +12403,7 @@ function createImplementationOrchestrator(deps) {
11901
12403
  const LOW_OUTPUT_TOKEN_THRESHOLD = 100;
11902
12404
  const unverified = tokenAgg.output < LOW_OUTPUT_TOKEN_THRESHOLD;
11903
12405
  if (unverified) {
11904
- logger$24.warn({
12406
+ logger$26.warn({
11905
12407
  storyKey,
11906
12408
  outputTokens: tokenAgg.output,
11907
12409
  threshold: LOW_OUTPUT_TOKEN_THRESHOLD
@@ -11925,13 +12427,13 @@ function createImplementationOrchestrator(deps) {
11925
12427
  ...unverified ? { unverified: true } : {}
11926
12428
  });
11927
12429
  } catch (emitErr) {
11928
- logger$24.warn({
12430
+ logger$26.warn({
11929
12431
  err: emitErr,
11930
12432
  storyKey
11931
12433
  }, "Failed to emit story:metrics event (best-effort)");
11932
12434
  }
11933
12435
  } catch (err) {
11934
- logger$24.warn({
12436
+ logger$26.warn({
11935
12437
  err,
11936
12438
  storyKey
11937
12439
  }, "Failed to write story metrics (best-effort)");
@@ -11960,7 +12462,7 @@ function createImplementationOrchestrator(deps) {
11960
12462
  rationale: `Story ${storyKey} ${outcome} after ${reviewCycles} review cycle(s).`
11961
12463
  });
11962
12464
  } catch (err) {
11963
- logger$24.warn({
12465
+ logger$26.warn({
11964
12466
  err,
11965
12467
  storyKey
11966
12468
  }, "Failed to write story-outcome decision (best-effort)");
@@ -11998,7 +12500,7 @@ function createImplementationOrchestrator(deps) {
11998
12500
  rationale: `Escalation diagnosis for ${payload.storyKey}: ${diagnosis.recommendedAction} — ${diagnosis.rationale}`
11999
12501
  });
12000
12502
  } catch (err) {
12001
- logger$24.warn({
12503
+ logger$26.warn({
12002
12504
  err,
12003
12505
  storyKey: payload.storyKey
12004
12506
  }, "Failed to persist escalation diagnosis (best-effort)");
@@ -12048,7 +12550,7 @@ function createImplementationOrchestrator(deps) {
12048
12550
  const existing = _stories.get(storyKey);
12049
12551
  if (existing !== void 0) {
12050
12552
  Object.assign(existing, updates);
12051
- persistStoryState(storyKey, existing).catch((err) => logger$24.warn({
12553
+ persistStoryState(storyKey, existing).catch((err) => logger$26.warn({
12052
12554
  err,
12053
12555
  storyKey
12054
12556
  }, "StateStore write failed after updateStory"));
@@ -12057,12 +12559,12 @@ function createImplementationOrchestrator(deps) {
12057
12559
  storyKey,
12058
12560
  conflict: err
12059
12561
  });
12060
- else logger$24.warn({
12562
+ else logger$26.warn({
12061
12563
  err,
12062
12564
  storyKey
12063
12565
  }, "mergeStory failed");
12064
12566
  });
12065
- else if (updates.phase === "ESCALATED" || updates.phase === "VERIFICATION_FAILED") stateStore?.rollbackStory(storyKey).catch((err) => logger$24.warn({
12567
+ else if (updates.phase === "ESCALATED" || updates.phase === "VERIFICATION_FAILED") stateStore?.rollbackStory(storyKey).catch((err) => logger$26.warn({
12066
12568
  err,
12067
12569
  storyKey
12068
12570
  }, "rollbackStory failed — branch may persist"));
@@ -12074,7 +12576,7 @@ function createImplementationOrchestrator(deps) {
12074
12576
  ...updates
12075
12577
  };
12076
12578
  const opts = targetStatus === "complete" || targetStatus === "escalated" ? { completedAt: fullUpdated.completedAt } : void 0;
12077
- wgRepo.updateStoryStatus(storyKey, targetStatus, opts).catch((err) => logger$24.warn({
12579
+ wgRepo.updateStoryStatus(storyKey, targetStatus, opts).catch((err) => logger$26.warn({
12078
12580
  err,
12079
12581
  storyKey
12080
12582
  }, "wg_stories status update failed (best-effort)"));
@@ -12090,7 +12592,7 @@ function createImplementationOrchestrator(deps) {
12090
12592
  status: "dispatched",
12091
12593
  phase: String(updates.phase),
12092
12594
  started_at: fullUpdated.startedAt ?? new Date().toISOString()
12093
- }).catch((err) => logger$24.warn({
12595
+ }).catch((err) => logger$26.warn({
12094
12596
  err,
12095
12597
  storyKey
12096
12598
  }, "patchStoryState(dispatched) failed — pipeline continues"));
@@ -12102,7 +12604,7 @@ function createImplementationOrchestrator(deps) {
12102
12604
  completed_at: fullUpdated.completedAt ?? new Date().toISOString(),
12103
12605
  review_cycles: fullUpdated.reviewCycles ?? 0,
12104
12606
  dispatches: _storyDispatches.get(storyKey) ?? 0
12105
- }).catch((err) => logger$24.warn({
12607
+ }).catch((err) => logger$26.warn({
12106
12608
  err,
12107
12609
  storyKey
12108
12610
  }, `patchStoryState(${manifestStatus}) failed — pipeline continues`));
@@ -12132,7 +12634,7 @@ function createImplementationOrchestrator(deps) {
12132
12634
  };
12133
12635
  await stateStore.setStoryState(storyKey, record);
12134
12636
  } catch (err) {
12135
- logger$24.warn({
12637
+ logger$26.warn({
12136
12638
  err,
12137
12639
  storyKey
12138
12640
  }, "StateStore.setStoryState failed (best-effort)");
@@ -12148,7 +12650,7 @@ function createImplementationOrchestrator(deps) {
12148
12650
  token_usage_json: serialized
12149
12651
  });
12150
12652
  } catch (err) {
12151
- logger$24.warn({ err }, "Failed to persist orchestrator state");
12653
+ logger$26.warn({ err }, "Failed to persist orchestrator state");
12152
12654
  }
12153
12655
  }
12154
12656
  function recordProgress() {
@@ -12174,7 +12676,7 @@ function createImplementationOrchestrator(deps) {
12174
12676
  queuedDispatches: queued
12175
12677
  });
12176
12678
  if (config.pipelineRunId !== void 0) updatePipelineRun(db, config.pipelineRunId, { current_phase: "implementation" }).catch((err) => {
12177
- logger$24.debug({ err }, "Heartbeat: failed to touch updated_at (non-fatal)");
12679
+ logger$26.debug({ err }, "Heartbeat: failed to touch updated_at (non-fatal)");
12178
12680
  });
12179
12681
  const elapsed = Date.now() - _lastProgressTs;
12180
12682
  let childPids = [];
@@ -12196,7 +12698,7 @@ function createImplementationOrchestrator(deps) {
12196
12698
  }
12197
12699
  if (childActive) {
12198
12700
  _lastProgressTs = Date.now();
12199
- logger$24.debug({
12701
+ logger$26.debug({
12200
12702
  storyKey: key,
12201
12703
  phase: s$1.phase,
12202
12704
  childPids
@@ -12205,7 +12707,7 @@ function createImplementationOrchestrator(deps) {
12205
12707
  }
12206
12708
  _stalledStories.add(key);
12207
12709
  _storiesWithStall.add(key);
12208
- logger$24.warn({
12710
+ logger$26.warn({
12209
12711
  storyKey: key,
12210
12712
  phase: s$1.phase,
12211
12713
  elapsedMs: elapsed,
@@ -12250,7 +12752,7 @@ function createImplementationOrchestrator(deps) {
12250
12752
  for (let attempt = 0; attempt < MEMORY_PRESSURE_BACKOFF_MS.length; attempt++) {
12251
12753
  const memState = dispatcher.getMemoryState();
12252
12754
  if (!memState.isPressured) return true;
12253
- logger$24.warn({
12755
+ logger$26.warn({
12254
12756
  storyKey,
12255
12757
  freeMB: memState.freeMB,
12256
12758
  thresholdMB: memState.thresholdMB,
@@ -12270,12 +12772,12 @@ function createImplementationOrchestrator(deps) {
12270
12772
  * exhausted retries the story is ESCALATED.
12271
12773
  */
12272
12774
  async function processStory(storyKey, storyOptions) {
12273
- logger$24.info({ storyKey }, "Processing story");
12775
+ logger$26.info({ storyKey }, "Processing story");
12274
12776
  await initRetryCount(storyKey);
12275
12777
  {
12276
12778
  const memoryOk = await checkMemoryPressure(storyKey);
12277
12779
  if (!memoryOk) {
12278
- logger$24.warn({ storyKey }, "Memory pressure exhausted — escalating story without dispatch");
12780
+ logger$26.warn({ storyKey }, "Memory pressure exhausted — escalating story without dispatch");
12279
12781
  const memPressureState = {
12280
12782
  phase: "ESCALATED",
12281
12783
  reviewCycles: 0,
@@ -12284,7 +12786,7 @@ function createImplementationOrchestrator(deps) {
12284
12786
  completedAt: new Date().toISOString()
12285
12787
  };
12286
12788
  _stories.set(storyKey, memPressureState);
12287
- persistStoryState(storyKey, memPressureState).catch((err) => logger$24.warn({
12789
+ persistStoryState(storyKey, memPressureState).catch((err) => logger$26.warn({
12288
12790
  err,
12289
12791
  storyKey
12290
12792
  }, "StateStore write failed after memory-pressure escalation"));
@@ -12301,7 +12803,7 @@ function createImplementationOrchestrator(deps) {
12301
12803
  }
12302
12804
  await waitIfPaused();
12303
12805
  if (_state !== "RUNNING") return;
12304
- stateStore?.branchForStory(storyKey).catch((err) => logger$24.warn({
12806
+ stateStore?.branchForStory(storyKey).catch((err) => logger$26.warn({
12305
12807
  err,
12306
12808
  storyKey
12307
12809
  }, "branchForStory failed — continuing without branch isolation"));
@@ -12320,7 +12822,7 @@ function createImplementationOrchestrator(deps) {
12320
12822
  if (match$2) {
12321
12823
  const candidatePath = join$1(artifactsDir, match$2);
12322
12824
  const validation = await isValidStoryFile(candidatePath);
12323
- if (!validation.valid) logger$24.warn({
12825
+ if (!validation.valid) logger$26.warn({
12324
12826
  storyKey,
12325
12827
  storyFilePath: candidatePath,
12326
12828
  reason: validation.reason
@@ -12345,7 +12847,7 @@ function createImplementationOrchestrator(deps) {
12345
12847
  storedHash,
12346
12848
  currentHash
12347
12849
  });
12348
- logger$24.info({
12850
+ logger$26.info({
12349
12851
  storyKey,
12350
12852
  storedHash,
12351
12853
  currentHash
@@ -12356,7 +12858,7 @@ function createImplementationOrchestrator(deps) {
12356
12858
  } catch {}
12357
12859
  if (!isDrift) {
12358
12860
  storyFilePath = candidatePath;
12359
- logger$24.info({
12861
+ logger$26.info({
12360
12862
  storyKey,
12361
12863
  storyFilePath
12362
12864
  }, "Found existing story file — skipping create-story");
@@ -12376,12 +12878,12 @@ function createImplementationOrchestrator(deps) {
12376
12878
  const staleName = match$2.replace(/\.md$/, `.stale-${ts}.md`);
12377
12879
  const stalePath = join$1(artifactsDir, staleName);
12378
12880
  renameSync(candidatePath, stalePath);
12379
- logger$24.info({
12881
+ logger$26.info({
12380
12882
  storyKey,
12381
12883
  staleName
12382
12884
  }, `[orchestrator] story ${storyKey}: renamed drifted artifact to ${staleName} before re-dispatch`);
12383
12885
  } catch (renameErr) {
12384
- logger$24.warn({
12886
+ logger$26.warn({
12385
12887
  storyKey,
12386
12888
  err: renameErr
12387
12889
  }, "Failed to rename stale artifact before create-story re-dispatch; relying on 58-9d fraud-guard");
@@ -12390,7 +12892,7 @@ function createImplementationOrchestrator(deps) {
12390
12892
  }
12391
12893
  } catch {}
12392
12894
  if (storyFilePath === void 0 && projectRoot && isImplicitlyCovered(storyKey, projectRoot)) {
12393
- logger$24.info({ storyKey }, `Story ${storyKey} appears implicitly covered — all expected new files already exist. Skipping create-story.`);
12895
+ logger$26.info({ storyKey }, `Story ${storyKey} appears implicitly covered — all expected new files already exist. Skipping create-story.`);
12394
12896
  endPhase(storyKey, "create-story");
12395
12897
  eventBus.emit("orchestrator:story-phase-complete", {
12396
12898
  storyKey,
@@ -12443,7 +12945,7 @@ function createImplementationOrchestrator(deps) {
12443
12945
  output_tokens: createResult.tokenUsage.output,
12444
12946
  cost_usd: estimateDispatchCost(createResult.tokenUsage.input, createResult.tokenUsage.output),
12445
12947
  metadata: JSON.stringify({ storyKey })
12446
- })).catch((tokenErr) => logger$24.warn({
12948
+ })).catch((tokenErr) => logger$26.warn({
12447
12949
  storyKey,
12448
12950
  err: tokenErr
12449
12951
  }, "Failed to record create-story token usage"));
@@ -12451,7 +12953,7 @@ function createImplementationOrchestrator(deps) {
12451
12953
  if (createResult.result === "failed") {
12452
12954
  const errMsg = createResult.error ?? "create-story failed";
12453
12955
  const stderrSnippet = errMsg.includes("--- stderr ---") ? errMsg.slice(errMsg.indexOf("--- stderr ---") + 15, errMsg.indexOf("--- stderr ---") + 515) : errMsg.slice(0, 500);
12454
- logger$24.error({
12956
+ logger$26.error({
12455
12957
  storyKey,
12456
12958
  stderrSnippet
12457
12959
  }, `Create-story failed: ${stderrSnippet.split("\n")[0]}`);
@@ -12493,7 +12995,7 @@ function createImplementationOrchestrator(deps) {
12493
12995
  let claimedPath = createResult.story_file;
12494
12996
  if (claimedPath.startsWith(escapedExpectedDir)) {
12495
12997
  claimedPath = claimedPath.replace("/\\_bmad-output/", "/_bmad-output/");
12496
- logger$24.warn({
12998
+ logger$26.warn({
12497
12999
  storyKey,
12498
13000
  originalClaim: createResult.story_file,
12499
13001
  normalizedClaim: claimedPath
@@ -12511,7 +13013,7 @@ function createImplementationOrchestrator(deps) {
12511
13013
  if (escapedVariant !== claimedPath && existsSync(escapedVariant)) try {
12512
13014
  renameSync(escapedVariant, claimedPath);
12513
13015
  actualPath = claimedPath;
12514
- logger$24.warn({
13016
+ logger$26.warn({
12515
13017
  storyKey,
12516
13018
  escapedVariant,
12517
13019
  canonicalPath: claimedPath
@@ -12522,7 +13024,7 @@ function createImplementationOrchestrator(deps) {
12522
13024
  });
12523
13025
  } catch (renameErr) {
12524
13026
  actualPath = escapedVariant;
12525
- logger$24.warn({
13027
+ logger$26.warn({
12526
13028
  storyKey,
12527
13029
  escapedVariant,
12528
13030
  canonicalPath: claimedPath,
@@ -12537,7 +13039,7 @@ function createImplementationOrchestrator(deps) {
12537
13039
  if (actualPath === null) {
12538
13040
  const outputTokens = createResult.tokenUsage?.output ?? 0;
12539
13041
  const errMsg = `create-story claimed success (story_file: ${createResult.story_file}) but the file does not exist on disk (output tokens: ${outputTokens})`;
12540
- logger$24.error({
13042
+ logger$26.error({
12541
13043
  storyKey,
12542
13044
  claimedPath: createResult.story_file,
12543
13045
  outputTokens
@@ -12564,7 +13066,7 @@ function createImplementationOrchestrator(deps) {
12564
13066
  const mtimeISO = new Date(claimedStat.mtimeMs).toISOString();
12565
13067
  const dispatchStartISO = new Date(dispatchStartMs).toISOString();
12566
13068
  const errMsg = `create-story claimed success but did not rewrite ${actualPath} during this dispatch (file mtime ${mtimeISO} predates dispatch start ${dispatchStartISO}; output tokens: ${outputTokens})`;
12567
- logger$24.error({
13069
+ logger$26.error({
12568
13070
  storyKey,
12569
13071
  claimedPath: actualPath,
12570
13072
  mtimeISO,
@@ -12587,7 +13089,7 @@ function createImplementationOrchestrator(deps) {
12587
13089
  return;
12588
13090
  }
12589
13091
  } catch (verifyErr) {
12590
- logger$24.warn({
13092
+ logger$26.warn({
12591
13093
  storyKey,
12592
13094
  err: verifyErr
12593
13095
  }, "create-story post-dispatch file verification threw; proceeding with claimed path");
@@ -12610,7 +13112,7 @@ function createImplementationOrchestrator(deps) {
12610
13112
  const overlap = computeTitleOverlap(expectedTitle, createResult.story_title);
12611
13113
  if (overlap < TITLE_OVERLAP_WARNING_THRESHOLD) {
12612
13114
  const msg = `Story title mismatch: expected "${expectedTitle}" but got "${createResult.story_title}" (word overlap: ${Math.round(overlap * 100)}%). This may indicate the create-story agent received truncated context.`;
12613
- logger$24.warn({
13115
+ logger$26.warn({
12614
13116
  storyKey,
12615
13117
  expectedTitle,
12616
13118
  generatedTitle: createResult.story_title,
@@ -12620,7 +13122,7 @@ function createImplementationOrchestrator(deps) {
12620
13122
  storyKey,
12621
13123
  msg
12622
13124
  });
12623
- } else logger$24.debug({
13125
+ } else logger$26.debug({
12624
13126
  storyKey,
12625
13127
  expectedTitle,
12626
13128
  generatedTitle: createResult.story_title,
@@ -12629,7 +13131,7 @@ function createImplementationOrchestrator(deps) {
12629
13131
  }
12630
13132
  }
12631
13133
  } catch (titleValidationErr) {
12632
- logger$24.debug({
13134
+ logger$26.debug({
12633
13135
  storyKey,
12634
13136
  err: titleValidationErr
12635
13137
  }, "Story title validation skipped due to error");
@@ -12670,7 +13172,7 @@ function createImplementationOrchestrator(deps) {
12670
13172
  const pathDrift = pathFidelity?.drift ?? 0;
12671
13173
  const clauseDrift = clauseFidelity.drift;
12672
13174
  const overallDrift = Math.max(pathDrift, clauseDrift);
12673
- logger$24.debug({
13175
+ logger$26.debug({
12674
13176
  storyKey,
12675
13177
  pathDrift,
12676
13178
  clauseDrift,
@@ -12692,7 +13194,7 @@ function createImplementationOrchestrator(deps) {
12692
13194
  if (pathMissing.length > 0) reasons.push(`${pathMissing.length} named path(s) missing`);
12693
13195
  if (numericMismatches.length > 0) reasons.push(`${numericMismatches.length} numeric quantifier mismatch(es) (e.g., "${numericMismatches[0].noun}" source=${numericMismatches[0].sourceCount} rendered=${numericMismatches[0].renderedCount})`);
12694
13196
  if (clauseFidelity.clauseRatio < .7) reasons.push(`clause shortfall (rendered ${clauseFidelity.renderedClauseCount}/${clauseFidelity.sourceClauseCount} = ${Math.round(clauseFidelity.clauseRatio * 100)}%)`);
12695
- logger$24.warn({
13197
+ logger$26.warn({
12696
13198
  storyKey,
12697
13199
  pathDrift,
12698
13200
  clauseDrift,
@@ -12735,7 +13237,7 @@ function createImplementationOrchestrator(deps) {
12735
13237
  storyFilePath = void 0;
12736
13238
  continue;
12737
13239
  } catch (renameErr) {
12738
- logger$24.warn({
13240
+ logger$26.warn({
12739
13241
  storyKey,
12740
13242
  err: renameErr,
12741
13243
  stalePath
@@ -12749,7 +13251,7 @@ function createImplementationOrchestrator(deps) {
12749
13251
  if (numericMismatches.length > 0) reasons.push(`numeric mismatches: ${numericMismatches.map((m) => `${m.noun} (source=${m.sourceCount}, rendered=${m.renderedCount})`).join("; ")}`);
12750
13252
  if (clauseFidelity.clauseRatio < .7) reasons.push(`clause shortfall: source=${clauseFidelity.sourceClauseCount}, rendered=${clauseFidelity.renderedClauseCount}`);
12751
13253
  const errMsg = `create-story output drifted from source AC after ${MAX_FIDELITY_RETRIES} retries; ` + reasons.join("; ");
12752
- logger$24.error({
13254
+ logger$26.error({
12753
13255
  storyKey,
12754
13256
  pathDrift,
12755
13257
  clauseDrift,
@@ -12776,7 +13278,7 @@ function createImplementationOrchestrator(deps) {
12776
13278
  }
12777
13279
  }
12778
13280
  } catch (fidelityErr) {
12779
- logger$24.warn({
13281
+ logger$26.warn({
12780
13282
  storyKey,
12781
13283
  err: fidelityErr
12782
13284
  }, "fidelity gate threw; proceeding without retry");
@@ -12807,18 +13309,79 @@ function createImplementationOrchestrator(deps) {
12807
13309
  ...contract.transport !== void 0 ? { transport: contract.transport } : {}
12808
13310
  })
12809
13311
  });
12810
- logger$24.info({
13312
+ logger$26.info({
12811
13313
  storyKey,
12812
13314
  contractCount: contracts.length,
12813
13315
  contracts
12814
13316
  }, "Stored interface contract declarations");
12815
13317
  }
12816
13318
  } catch (err) {
12817
- logger$24.warn({
13319
+ logger$26.warn({
12818
13320
  storyKey,
12819
13321
  error: err instanceof Error ? err.message : String(err)
12820
13322
  }, "Failed to parse interface contracts — continuing without contract declarations");
12821
13323
  }
13324
+ if (storyFilePath) try {
13325
+ let probeAuthorEpicContent = "";
13326
+ const probeAuthorEpicsPath = findEpicFileForStory(projectRoot ?? process.cwd(), storyKey);
13327
+ if (probeAuthorEpicsPath) try {
13328
+ const epicFull = readFileSync(probeAuthorEpicsPath, "utf-8");
13329
+ const section = extractStorySection(epicFull, storyKey);
13330
+ probeAuthorEpicContent = section ?? epicFull;
13331
+ } catch {}
13332
+ if (detectsEventDrivenAC(probeAuthorEpicContent)) {
13333
+ let artifactHasProbes = false;
13334
+ try {
13335
+ const artifactContent = readFileSync(storyFilePath, "utf-8");
13336
+ artifactHasProbes = /^## Runtime Probes/m.test(artifactContent);
13337
+ } catch {}
13338
+ if (!artifactHasProbes) {
13339
+ const probeAuthorResult = await runProbeAuthor({
13340
+ db,
13341
+ pack,
13342
+ contextCompiler,
13343
+ dispatcher,
13344
+ projectRoot,
13345
+ tokenCeilings,
13346
+ otlpEndpoint: _otlpEndpoint,
13347
+ agentId
13348
+ }, {
13349
+ storyKey,
13350
+ storyFilePath,
13351
+ pipelineRunId: config.pipelineRunId ?? "",
13352
+ sourceAcContent: probeAuthorEpicContent,
13353
+ epicContent: probeAuthorEpicContent,
13354
+ emitEvent: (name, payload) => {
13355
+ eventBus.emit("orchestrator:story-warn", {
13356
+ storyKey,
13357
+ msg: `probe-author:${name.replace("probe-author:", "")} ${JSON.stringify(payload)}`
13358
+ });
13359
+ }
13360
+ });
13361
+ logger$26.info({
13362
+ storyKey,
13363
+ result: probeAuthorResult.result,
13364
+ probesAuthoredCount: probeAuthorResult.probesAuthoredCount
13365
+ }, "probe-author phase complete");
13366
+ if (config.pipelineRunId !== void 0 && probeAuthorResult.tokenUsage.input + probeAuthorResult.tokenUsage.output > 0) Promise.resolve().then(() => addTokenUsage(db, config.pipelineRunId, {
13367
+ phase: "probe-author",
13368
+ agent: "probe-author",
13369
+ input_tokens: probeAuthorResult.tokenUsage.input,
13370
+ output_tokens: probeAuthorResult.tokenUsage.output,
13371
+ cost_usd: estimateDispatchCost(probeAuthorResult.tokenUsage.input, probeAuthorResult.tokenUsage.output),
13372
+ metadata: JSON.stringify({ storyKey })
13373
+ })).catch((tokenErr) => logger$26.warn({
13374
+ storyKey,
13375
+ err: tokenErr
13376
+ }, "Failed to record probe-author token usage"));
13377
+ } else logger$26.debug({ storyKey }, "probe-author: story artifact already has ## Runtime Probes — skipping gate");
13378
+ } else logger$26.debug({ storyKey }, "probe-author: source AC not event-driven — skipping gate");
13379
+ } catch (probeAuthorErr) {
13380
+ logger$26.warn({
13381
+ storyKey,
13382
+ err: probeAuthorErr
13383
+ }, "probe-author gate threw unexpectedly; proceeding to test-plan without authored probes");
13384
+ }
12822
13385
  await waitIfPaused();
12823
13386
  if (_state !== "RUNNING") return;
12824
13387
  startPhase(storyKey, "test-plan");
@@ -12843,10 +13406,10 @@ function createImplementationOrchestrator(deps) {
12843
13406
  });
12844
13407
  testPlanPhaseResult = testPlanResult.result;
12845
13408
  testPlanTokenUsage = testPlanResult.tokenUsage;
12846
- if (testPlanResult.result === "success") logger$24.info({ storyKey }, "Test plan generated successfully");
12847
- else logger$24.warn({ storyKey }, "Test planning returned failed result — proceeding to dev-story without test plan");
13409
+ if (testPlanResult.result === "success") logger$26.info({ storyKey }, "Test plan generated successfully");
13410
+ else logger$26.warn({ storyKey }, "Test planning returned failed result — proceeding to dev-story without test plan");
12848
13411
  } catch (err) {
12849
- logger$24.warn({
13412
+ logger$26.warn({
12850
13413
  storyKey,
12851
13414
  err
12852
13415
  }, "Test planning failed — proceeding to dev-story without test plan");
@@ -12859,7 +13422,7 @@ function createImplementationOrchestrator(deps) {
12859
13422
  output_tokens: testPlanTokenUsage.output,
12860
13423
  cost_usd: estimateDispatchCost(testPlanTokenUsage.input, testPlanTokenUsage.output),
12861
13424
  metadata: JSON.stringify({ storyKey })
12862
- })).catch((tokenErr) => logger$24.warn({
13425
+ })).catch((tokenErr) => logger$26.warn({
12863
13426
  storyKey,
12864
13427
  err: tokenErr
12865
13428
  }, "Failed to record test-plan token usage"));
@@ -12927,7 +13490,7 @@ function createImplementationOrchestrator(deps) {
12927
13490
  storyContentForAnalysis = await readFile$1(storyFilePath ?? "", "utf-8");
12928
13491
  storyContentForVerification = storyContentForAnalysis;
12929
13492
  } catch (err) {
12930
- logger$24.error({
13493
+ logger$26.error({
12931
13494
  storyKey,
12932
13495
  storyFilePath,
12933
13496
  error: err instanceof Error ? err.message : String(err)
@@ -12935,7 +13498,7 @@ function createImplementationOrchestrator(deps) {
12935
13498
  }
12936
13499
  const analysis = analyzeStoryComplexity(storyContentForAnalysis);
12937
13500
  const batches = planTaskBatches(analysis);
12938
- logger$24.info({
13501
+ logger$26.info({
12939
13502
  storyKey,
12940
13503
  estimatedScope: analysis.estimatedScope,
12941
13504
  batchCount: batches.length,
@@ -12953,7 +13516,7 @@ function createImplementationOrchestrator(deps) {
12953
13516
  if (_state !== "RUNNING") break;
12954
13517
  const taskScope = batch.taskIds.map((id, i) => `T${id}: ${batch.taskTitles[i] ?? ""}`).join("\n");
12955
13518
  const priorFiles = allFilesModified.size > 0 ? Array.from(allFilesModified) : void 0;
12956
- logger$24.info({
13519
+ logger$26.info({
12957
13520
  storyKey,
12958
13521
  batchIndex: batch.batchIndex,
12959
13522
  taskCount: batch.taskIds.length
@@ -12984,7 +13547,7 @@ function createImplementationOrchestrator(deps) {
12984
13547
  });
12985
13548
  } catch (batchErr) {
12986
13549
  const errMsg = batchErr instanceof Error ? batchErr.message : String(batchErr);
12987
- logger$24.warn({
13550
+ logger$26.warn({
12988
13551
  storyKey,
12989
13552
  batchIndex: batch.batchIndex,
12990
13553
  error: errMsg
@@ -13005,7 +13568,7 @@ function createImplementationOrchestrator(deps) {
13005
13568
  filesModified: batchFilesModified,
13006
13569
  result: batchResult.result === "success" ? "success" : "failed"
13007
13570
  };
13008
- logger$24.info(batchMetrics, "Batch dev-story metrics");
13571
+ logger$26.info(batchMetrics, "Batch dev-story metrics");
13009
13572
  for (const f$1 of batchFilesModified) allFilesModified.add(f$1);
13010
13573
  if (batchFilesModified.length > 0) batchFileGroups.push({
13011
13574
  batchIndex: batch.batchIndex,
@@ -13024,13 +13587,13 @@ function createImplementationOrchestrator(deps) {
13024
13587
  durationMs: batchDurationMs,
13025
13588
  result: batchMetrics.result
13026
13589
  })
13027
- })).catch((tokenErr) => logger$24.warn({
13590
+ })).catch((tokenErr) => logger$26.warn({
13028
13591
  storyKey,
13029
13592
  batchIndex: batch.batchIndex,
13030
13593
  err: tokenErr
13031
13594
  }, "Failed to record batch token usage"));
13032
13595
  if (batchResult.tokenUsage?.output !== void 0) devOutputTokenCount = (devOutputTokenCount ?? 0) + batchResult.tokenUsage.output;
13033
- if (batchResult.result === "failed") logger$24.warn({
13596
+ if (batchResult.result === "failed") logger$26.warn({
13034
13597
  storyKey,
13035
13598
  batchIndex: batch.batchIndex,
13036
13599
  error: batchResult.error
@@ -13073,7 +13636,7 @@ function createImplementationOrchestrator(deps) {
13073
13636
  output_tokens: devResult.tokenUsage.output,
13074
13637
  cost_usd: estimateDispatchCost(devResult.tokenUsage.input, devResult.tokenUsage.output),
13075
13638
  metadata: JSON.stringify({ storyKey })
13076
- })).catch((tokenErr) => logger$24.warn({
13639
+ })).catch((tokenErr) => logger$26.warn({
13077
13640
  storyKey,
13078
13641
  err: tokenErr
13079
13642
  }, "Failed to record dev-story token usage"));
@@ -13088,7 +13651,7 @@ function createImplementationOrchestrator(deps) {
13088
13651
  endPhase(storyKey, "dev-story");
13089
13652
  const timeoutFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
13090
13653
  if (timeoutFiles.length === 0) {
13091
- logger$24.warn({ storyKey }, "Dev-story timeout with zero modified files — escalating immediately (no checkpoint)");
13654
+ logger$26.warn({ storyKey }, "Dev-story timeout with zero modified files — escalating immediately (no checkpoint)");
13092
13655
  updateStory(storyKey, {
13093
13656
  phase: "ESCALATED",
13094
13657
  error: "timeout-no-files",
@@ -13104,7 +13667,7 @@ function createImplementationOrchestrator(deps) {
13104
13667
  await persistState();
13105
13668
  return;
13106
13669
  }
13107
- logger$24.info({
13670
+ logger$26.info({
13108
13671
  storyKey,
13109
13672
  filesCount: timeoutFiles.length
13110
13673
  }, "Dev-story timeout with partial files — capturing checkpoint");
@@ -13121,7 +13684,7 @@ function createImplementationOrchestrator(deps) {
13121
13684
  ]
13122
13685
  }).trim();
13123
13686
  } catch (diffErr) {
13124
- logger$24.warn({
13687
+ logger$26.warn({
13125
13688
  storyKey,
13126
13689
  error: diffErr instanceof Error ? diffErr.message : String(diffErr)
13127
13690
  }, "Failed to capture git diff for checkpoint — proceeding with empty diff");
@@ -13148,7 +13711,7 @@ function createImplementationOrchestrator(deps) {
13148
13711
  recordedAt: new Date().toISOString(),
13149
13712
  sprint: config.sprint
13150
13713
  }).catch((storeErr) => {
13151
- logger$24.warn({
13714
+ logger$26.warn({
13152
13715
  err: storeErr,
13153
13716
  storyKey
13154
13717
  }, "Failed to record timeout metric to StateStore (best-effort)");
@@ -13207,9 +13770,9 @@ function createImplementationOrchestrator(deps) {
13207
13770
  checkpointRetryPrompt = assembled.prompt;
13208
13771
  } catch {
13209
13772
  checkpointRetryPrompt = `Continue story ${storyKey} from checkpoint. Your prior attempt timed out. Do not redo completed work.`;
13210
- logger$24.warn({ storyKey }, "Failed to assemble checkpoint retry prompt — using fallback");
13773
+ logger$26.warn({ storyKey }, "Failed to assemble checkpoint retry prompt — using fallback");
13211
13774
  }
13212
- logger$24.info({
13775
+ logger$26.info({
13213
13776
  storyKey,
13214
13777
  filesCount: checkpointData.filesModified.length
13215
13778
  }, "Dispatching checkpoint retry for timed-out story");
@@ -13238,7 +13801,7 @@ function createImplementationOrchestrator(deps) {
13238
13801
  } : void 0 }
13239
13802
  });
13240
13803
  if (checkpointRetryResult.status === "timeout") {
13241
- logger$24.warn({ storyKey }, "Checkpoint retry dispatch timed out — escalating story");
13804
+ logger$26.warn({ storyKey }, "Checkpoint retry dispatch timed out — escalating story");
13242
13805
  updateStory(storyKey, {
13243
13806
  phase: "ESCALATED",
13244
13807
  error: "checkpoint-retry-timeout",
@@ -13258,7 +13821,7 @@ function createImplementationOrchestrator(deps) {
13258
13821
  replaceDevStorySignals(retryParsed);
13259
13822
  devFilesModified = retryParsed?.files_modified ?? checkGitDiffFiles(projectRoot ?? process.cwd());
13260
13823
  if (checkpointRetryResult.status === "completed" && retryParsed?.result === "success") devStoryWasSuccess = true;
13261
- else logger$24.warn({
13824
+ else logger$26.warn({
13262
13825
  storyKey,
13263
13826
  status: checkpointRetryResult.status
13264
13827
  }, "Checkpoint retry completed with failure — proceeding to code review");
@@ -13268,13 +13831,13 @@ function createImplementationOrchestrator(deps) {
13268
13831
  replaceDevStorySignals(devResult);
13269
13832
  if (devResult.result === "success") devStoryWasSuccess = true;
13270
13833
  else {
13271
- logger$24.warn({
13834
+ logger$26.warn({
13272
13835
  storyKey,
13273
13836
  error: devResult.error,
13274
13837
  filesModified: devFilesModified.length
13275
13838
  }, "Dev-story reported failure, proceeding to code review");
13276
13839
  if (!devResult.error?.startsWith("dispatch_timeout")) {
13277
- logger$24.warn({
13840
+ logger$26.warn({
13278
13841
  storyKey,
13279
13842
  error: devResult.error
13280
13843
  }, "Agent process failure (non-timeout) — story will proceed to code review with partial work");
@@ -13336,13 +13899,13 @@ function createImplementationOrchestrator(deps) {
13336
13899
  }).trim();
13337
13900
  if (committedFiles.length > 0) gitDiffFiles = committedFiles.split("\n").filter(Boolean);
13338
13901
  } catch {}
13339
- logger$24.info({
13902
+ logger$26.info({
13340
13903
  storyKey,
13341
13904
  baselineHeadSha,
13342
13905
  committedFileCount: gitDiffFiles?.length ?? 0
13343
13906
  }, "Working tree clean but new commits detected since dispatch — skipping zero-diff escalation");
13344
13907
  } else {
13345
- logger$24.warn({ storyKey }, "Zero-diff detected after COMPLETE dev-story — no file changes and no new commits");
13908
+ logger$26.warn({ storyKey }, "Zero-diff detected after COMPLETE dev-story — no file changes and no new commits");
13346
13909
  eventBus.emit("orchestrator:zero-diff-escalation", {
13347
13910
  storyKey,
13348
13911
  reason: "zero-diff-on-complete"
@@ -13392,10 +13955,10 @@ function createImplementationOrchestrator(deps) {
13392
13955
  "pipe"
13393
13956
  ]
13394
13957
  });
13395
- logger$24.info({ storyKey }, "Secondary typecheck (tsc --noEmit) passed");
13958
+ logger$26.info({ storyKey }, "Secondary typecheck (tsc --noEmit) passed");
13396
13959
  } catch (tscErr) {
13397
13960
  const tscOutput = tscErr instanceof Error && "stdout" in tscErr ? String(tscErr.stdout ?? "").slice(0, 2e3) : "";
13398
- logger$24.warn({
13961
+ logger$26.warn({
13399
13962
  storyKey,
13400
13963
  tscOutput
13401
13964
  }, "Secondary typecheck (tsc --noEmit) failed — treating as build failure");
@@ -13410,7 +13973,7 @@ function createImplementationOrchestrator(deps) {
13410
13973
  if (buildVerifyResult.status === "passed") {
13411
13974
  _buildPassed = true;
13412
13975
  eventBus.emit("story:build-verification-passed", { storyKey });
13413
- logger$24.info({ storyKey }, "Build verification passed");
13976
+ logger$26.info({ storyKey }, "Build verification passed");
13414
13977
  } else if (buildVerifyResult.status === "failed" || buildVerifyResult.status === "timeout") {
13415
13978
  const truncatedOutput = (buildVerifyResult.output ?? "").slice(0, 2e3);
13416
13979
  const reason = buildVerifyResult.reason ?? "build-verification-failed";
@@ -13419,7 +13982,7 @@ function createImplementationOrchestrator(deps) {
13419
13982
  const resolvedRoot = projectRoot ?? process.cwd();
13420
13983
  const hasChanges = detectPackageChanges(_packageSnapshot, resolvedRoot);
13421
13984
  if (hasChanges) {
13422
- logger$24.warn({ storyKey }, "Package files changed since snapshot — restoring to prevent cascade");
13985
+ logger$26.warn({ storyKey }, "Package files changed since snapshot — restoring to prevent cascade");
13423
13986
  const restoreResult = restorePackageSnapshot(_packageSnapshot, { projectRoot: resolvedRoot });
13424
13987
  if (restoreResult.restored) {
13425
13988
  const retryAfterRestore = runBuildVerification({
@@ -13432,11 +13995,11 @@ function createImplementationOrchestrator(deps) {
13432
13995
  retryPassed = true;
13433
13996
  _buildPassed = true;
13434
13997
  eventBus.emit("story:build-verification-passed", { storyKey });
13435
- logger$24.warn({
13998
+ logger$26.warn({
13436
13999
  storyKey,
13437
14000
  filesRestored: restoreResult.filesRestored
13438
14001
  }, "Build passed after package snapshot restore — cross-story pollution detected and cleaned");
13439
- } else logger$24.warn({
14002
+ } else logger$26.warn({
13440
14003
  storyKey,
13441
14004
  filesRestored: restoreResult.filesRestored
13442
14005
  }, "Build still fails after snapshot restore — story has its own build errors");
@@ -13448,7 +14011,7 @@ function createImplementationOrchestrator(deps) {
13448
14011
  if (missingPkgMatch && buildVerifyResult.status !== "timeout") {
13449
14012
  const missingPkg = missingPkgMatch[1].replace(/^(@[^/]+\/[^/]+)\/.*$/, "$1").replace(/^([^@][^/]*)\/.*$/, "$1");
13450
14013
  const resolvedRoot = projectRoot ?? process.cwd();
13451
- logger$24.warn({
14014
+ logger$26.warn({
13452
14015
  storyKey,
13453
14016
  missingPkg
13454
14017
  }, "Build-fix retry: detected missing npm package — attempting npm install");
@@ -13459,7 +14022,7 @@ function createImplementationOrchestrator(deps) {
13459
14022
  encoding: "utf-8",
13460
14023
  stdio: "pipe"
13461
14024
  });
13462
- logger$24.warn({
14025
+ logger$26.warn({
13463
14026
  storyKey,
13464
14027
  missingPkg
13465
14028
  }, "Build-fix retry: npm install succeeded — retrying build verification");
@@ -13473,18 +14036,18 @@ function createImplementationOrchestrator(deps) {
13473
14036
  retryPassed = true;
13474
14037
  _buildPassed = true;
13475
14038
  eventBus.emit("story:build-verification-passed", { storyKey });
13476
- logger$24.warn({
14039
+ logger$26.warn({
13477
14040
  storyKey,
13478
14041
  missingPkg
13479
14042
  }, "Build-fix retry: build verification passed after installing missing package");
13480
- } else logger$24.warn({
14043
+ } else logger$26.warn({
13481
14044
  storyKey,
13482
14045
  missingPkg,
13483
14046
  retryStatus: retryResult.status
13484
14047
  }, "Build-fix retry: build still fails after installing missing package — escalating");
13485
14048
  } catch (installErr) {
13486
14049
  const installMsg = installErr instanceof Error ? installErr.message : String(installErr);
13487
- logger$24.warn({
14050
+ logger$26.warn({
13488
14051
  storyKey,
13489
14052
  missingPkg,
13490
14053
  error: installMsg
@@ -13494,7 +14057,7 @@ function createImplementationOrchestrator(deps) {
13494
14057
  if (!retryPassed) {
13495
14058
  let buildFixPassed = false;
13496
14059
  if (buildVerifyResult.status === "failed" && storyFilePath !== void 0) try {
13497
- logger$24.info({ storyKey }, "Dispatching build-fix agent");
14060
+ logger$26.info({ storyKey }, "Dispatching build-fix agent");
13498
14061
  startPhase(storyKey, "build-fix");
13499
14062
  const storyContent = await readFile$1(storyFilePath, "utf-8");
13500
14063
  let buildFixTemplate;
@@ -13531,11 +14094,11 @@ function createImplementationOrchestrator(deps) {
13531
14094
  buildFixPassed = true;
13532
14095
  _buildPassed = true;
13533
14096
  eventBus.emit("story:build-verification-passed", { storyKey });
13534
- logger$24.info({ storyKey }, "Build passed after build-fix dispatch");
13535
- } else logger$24.warn({ storyKey }, "Build still fails after build-fix dispatch — escalating");
14097
+ logger$26.info({ storyKey }, "Build passed after build-fix dispatch");
14098
+ } else logger$26.warn({ storyKey }, "Build still fails after build-fix dispatch — escalating");
13536
14099
  } catch (fixErr) {
13537
14100
  const fixMsg = fixErr instanceof Error ? fixErr.message : String(fixErr);
13538
- logger$24.warn({
14101
+ logger$26.warn({
13539
14102
  storyKey,
13540
14103
  error: fixMsg
13541
14104
  }, "Build-fix dispatch failed — escalating");
@@ -13546,7 +14109,7 @@ function createImplementationOrchestrator(deps) {
13546
14109
  exitCode: buildVerifyResult.exitCode ?? 1,
13547
14110
  output: truncatedOutput
13548
14111
  });
13549
- logger$24.warn({
14112
+ logger$26.warn({
13550
14113
  storyKey,
13551
14114
  reason,
13552
14115
  exitCode: buildVerifyResult.exitCode
@@ -13578,7 +14141,7 @@ function createImplementationOrchestrator(deps) {
13578
14141
  storyKey
13579
14142
  });
13580
14143
  if (icResult.potentiallyAffectedTests.length > 0) {
13581
- logger$24.warn({
14144
+ logger$26.warn({
13582
14145
  storyKey,
13583
14146
  modifiedInterfaces: icResult.modifiedInterfaces,
13584
14147
  potentiallyAffectedTests: icResult.potentiallyAffectedTests
@@ -13659,7 +14222,7 @@ function createImplementationOrchestrator(deps) {
13659
14222
  "NEEDS_MAJOR_REWORK": 2
13660
14223
  };
13661
14224
  for (const group of batchFileGroups) {
13662
- logger$24.info({
14225
+ logger$26.info({
13663
14226
  storyKey,
13664
14227
  batchIndex: group.batchIndex,
13665
14228
  fileCount: group.files.length
@@ -13704,7 +14267,7 @@ function createImplementationOrchestrator(deps) {
13704
14267
  rawOutput: lastRawOutput,
13705
14268
  tokenUsage: aggregateTokens
13706
14269
  };
13707
- logger$24.info({
14270
+ logger$26.info({
13708
14271
  storyKey,
13709
14272
  batchCount: batchFileGroups.length,
13710
14273
  verdict: worstVerdict,
@@ -13747,7 +14310,7 @@ function createImplementationOrchestrator(deps) {
13747
14310
  storyKey,
13748
14311
  reviewCycle: reviewCycles
13749
14312
  })
13750
- })).catch((tokenErr) => logger$24.warn({
14313
+ })).catch((tokenErr) => logger$26.warn({
13751
14314
  storyKey,
13752
14315
  err: tokenErr
13753
14316
  }, "Failed to record code-review token usage"));
@@ -13755,7 +14318,7 @@ function createImplementationOrchestrator(deps) {
13755
14318
  const isPhantomReview = reviewResult.dispatchFailed === true || reviewResult.verdict !== "SHIP_IT" && reviewResult.verdict !== "LGTM_WITH_NOTES" && (reviewResult.issue_list === void 0 || reviewResult.issue_list.length === 0) && reviewResult.error !== void 0;
13756
14319
  if (isPhantomReview && !timeoutRetried) {
13757
14320
  timeoutRetried = true;
13758
- logger$24.warn({
14321
+ logger$26.warn({
13759
14322
  storyKey,
13760
14323
  reviewCycles,
13761
14324
  error: reviewResult.error
@@ -13763,7 +14326,7 @@ function createImplementationOrchestrator(deps) {
13763
14326
  continue;
13764
14327
  }
13765
14328
  if (isPhantomReview && timeoutRetried) {
13766
- logger$24.warn({
14329
+ logger$26.warn({
13767
14330
  storyKey,
13768
14331
  reviewCycles,
13769
14332
  error: reviewResult.error
@@ -13787,7 +14350,7 @@ function createImplementationOrchestrator(deps) {
13787
14350
  verdict = reviewResult.verdict;
13788
14351
  issueList = reviewResult.issue_list ?? [];
13789
14352
  if (verdict === "NEEDS_MAJOR_REWORK" && reviewCycles > 0 && previousIssueList.length > 0 && issueList.length < previousIssueList.length) {
13790
- logger$24.info({
14353
+ logger$26.info({
13791
14354
  storyKey,
13792
14355
  originalVerdict: verdict,
13793
14356
  issuesBefore: previousIssueList.length,
@@ -13823,7 +14386,7 @@ function createImplementationOrchestrator(deps) {
13823
14386
  if (_decomposition !== void 0) parts.push(`decomposed: ${_decomposition.batchCount} batches`);
13824
14387
  parts.push(`${fileCount} files`);
13825
14388
  parts.push(`${totalTokensK} tokens`);
13826
- logger$24.info({
14389
+ logger$26.info({
13827
14390
  storyKey,
13828
14391
  verdict,
13829
14392
  agentVerdict: reviewResult.agentVerdict
@@ -13856,7 +14419,7 @@ function createImplementationOrchestrator(deps) {
13856
14419
  rawOutput: reviewResult.rawOutput
13857
14420
  } : void 0;
13858
14421
  let sourceEpicContent;
13859
- const epicsPath1 = findEpicsFile(projectRoot ?? process.cwd());
14422
+ const epicsPath1 = findEpicFileForStory(projectRoot ?? process.cwd(), storyKey);
13860
14423
  if (epicsPath1) try {
13861
14424
  const epicFull = readFileSync(epicsPath1, "utf-8");
13862
14425
  const section = extractStorySection(epicFull, storyKey);
@@ -13880,7 +14443,7 @@ function createImplementationOrchestrator(deps) {
13880
14443
  phase: "VERIFICATION_FAILED",
13881
14444
  completedAt: new Date().toISOString()
13882
14445
  });
13883
- persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$24.warn({
14446
+ persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
13884
14447
  err,
13885
14448
  storyKey
13886
14449
  }, "StateStore write failed after verification-failed"));
@@ -13894,7 +14457,7 @@ function createImplementationOrchestrator(deps) {
13894
14457
  completedAt: new Date().toISOString()
13895
14458
  });
13896
14459
  if (config.skipVerification !== true && runManifest != null) Promise.resolve().then(() => runManifest.read()).then((manifest) => {
13897
- if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$24.warn({
14460
+ if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$26.warn({
13898
14461
  storyKey,
13899
14462
  category: "verification-result-missing"
13900
14463
  }, "post-COMPLETE invariant: verification_result absent in manifest");
@@ -13919,9 +14482,9 @@ function createImplementationOrchestrator(deps) {
13919
14482
  }),
13920
14483
  rationale: `Advisory notes from LGTM_WITH_NOTES review of ${storyKey}`
13921
14484
  });
13922
- logger$24.info({ storyKey }, "Advisory notes persisted to decision store");
14485
+ logger$26.info({ storyKey }, "Advisory notes persisted to decision store");
13923
14486
  } catch (advisoryErr) {
13924
- logger$24.warn({
14487
+ logger$26.warn({
13925
14488
  storyKey,
13926
14489
  error: advisoryErr instanceof Error ? advisoryErr.message : String(advisoryErr)
13927
14490
  }, "Failed to persist advisory notes (best-effort)");
@@ -13929,27 +14492,27 @@ function createImplementationOrchestrator(deps) {
13929
14492
  if (telemetryPersistence !== void 0) try {
13930
14493
  const turns = await telemetryPersistence.getTurnAnalysis(storyKey);
13931
14494
  if (turns.length > 0) {
13932
- const scorer = new EfficiencyScorer(logger$24);
14495
+ const scorer = new EfficiencyScorer(logger$26);
13933
14496
  const effScore = scorer.score(storyKey, turns);
13934
14497
  await telemetryPersistence.storeEfficiencyScore(effScore);
13935
- logger$24.info({
14498
+ logger$26.info({
13936
14499
  storyKey,
13937
14500
  compositeScore: effScore.compositeScore,
13938
14501
  modelCount: effScore.perModelBreakdown.length
13939
14502
  }, "Efficiency score computed and persisted");
13940
- } else logger$24.debug({ storyKey }, "No turn analysis data available — skipping efficiency scoring");
14503
+ } else logger$26.debug({ storyKey }, "No turn analysis data available — skipping efficiency scoring");
13941
14504
  } catch (effErr) {
13942
- logger$24.warn({
14505
+ logger$26.warn({
13943
14506
  storyKey,
13944
14507
  error: effErr instanceof Error ? effErr.message : String(effErr)
13945
14508
  }, "Efficiency scoring failed — story verdict unchanged");
13946
14509
  }
13947
14510
  if (telemetryPersistence !== void 0) try {
13948
14511
  const turns = await telemetryPersistence.getTurnAnalysis(storyKey);
13949
- if (turns.length === 0) logger$24.debug({ storyKey }, "No turn analysis data for telemetry categorization — skipping");
14512
+ if (turns.length === 0) logger$26.debug({ storyKey }, "No turn analysis data for telemetry categorization — skipping");
13950
14513
  else {
13951
- const categorizer = new Categorizer(logger$24);
13952
- const consumerAnalyzer = new ConsumerAnalyzer(categorizer, logger$24);
14514
+ const categorizer = new Categorizer(logger$26);
14515
+ const consumerAnalyzer = new ConsumerAnalyzer(categorizer, logger$26);
13953
14516
  const categoryStats = categorizer.computeCategoryStatsFromTurns(turns);
13954
14517
  const consumerStats = consumerAnalyzer.analyzeFromTurns(turns);
13955
14518
  await telemetryPersistence.storeCategoryStats(storyKey, categoryStats);
@@ -13957,7 +14520,7 @@ function createImplementationOrchestrator(deps) {
13957
14520
  const growingCount = categoryStats.filter((c) => c.trend === "growing").length;
13958
14521
  const topCategory = categoryStats[0]?.category ?? "none";
13959
14522
  const topConsumer = consumerStats[0]?.consumerKey ?? "none";
13960
- logger$24.info({
14523
+ logger$26.info({
13961
14524
  storyKey,
13962
14525
  topCategory,
13963
14526
  topConsumer,
@@ -13965,7 +14528,7 @@ function createImplementationOrchestrator(deps) {
13965
14528
  }, "Semantic categorization and consumer analysis complete");
13966
14529
  }
13967
14530
  } catch (catErr) {
13968
- logger$24.warn({
14531
+ logger$26.warn({
13969
14532
  storyKey,
13970
14533
  error: catErr instanceof Error ? catErr.message : String(catErr)
13971
14534
  }, "Semantic categorization failed — story verdict unchanged");
@@ -13987,7 +14550,7 @@ function createImplementationOrchestrator(deps) {
13987
14550
  filesModified: devFilesModified,
13988
14551
  workingDirectory: projectRoot
13989
14552
  });
13990
- logger$24.debug({
14553
+ logger$26.debug({
13991
14554
  storyKey,
13992
14555
  expansion_priority: expansionResult.expansion_priority,
13993
14556
  coverage_gaps: expansionResult.coverage_gaps.length
@@ -14000,7 +14563,7 @@ function createImplementationOrchestrator(deps) {
14000
14563
  value: JSON.stringify(expansionResult)
14001
14564
  });
14002
14565
  } catch (expansionErr) {
14003
- logger$24.warn({
14566
+ logger$26.warn({
14004
14567
  storyKey,
14005
14568
  error: expansionErr instanceof Error ? expansionErr.message : String(expansionErr)
14006
14569
  }, "Test expansion failed — story verdict unchanged");
@@ -14027,7 +14590,7 @@ function createImplementationOrchestrator(deps) {
14027
14590
  await persistState();
14028
14591
  return;
14029
14592
  }
14030
- logger$24.info({
14593
+ logger$26.info({
14031
14594
  storyKey,
14032
14595
  reviewCycles: finalReviewCycles,
14033
14596
  issueCount: issueList.length
@@ -14093,7 +14656,7 @@ function createImplementationOrchestrator(deps) {
14093
14656
  fixPrompt = assembled.prompt;
14094
14657
  } catch {
14095
14658
  fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, minor fixes needed`;
14096
- logger$24.warn({ storyKey }, "Failed to assemble auto-approve fix prompt, using fallback");
14659
+ logger$26.warn({ storyKey }, "Failed to assemble auto-approve fix prompt, using fallback");
14097
14660
  }
14098
14661
  const handle = dispatcher.dispatch({
14099
14662
  prompt: fixPrompt,
@@ -14114,9 +14677,9 @@ function createImplementationOrchestrator(deps) {
14114
14677
  output: fixResult.tokenEstimate.output
14115
14678
  } : void 0 }
14116
14679
  });
14117
- if (fixResult.status === "timeout") logger$24.warn({ storyKey }, "Auto-approve fix timed out — approving anyway (issues were minor)");
14680
+ if (fixResult.status === "timeout") logger$26.warn({ storyKey }, "Auto-approve fix timed out — approving anyway (issues were minor)");
14118
14681
  } catch (err) {
14119
- logger$24.warn({
14682
+ logger$26.warn({
14120
14683
  storyKey,
14121
14684
  err
14122
14685
  }, "Auto-approve fix dispatch failed — approving anyway (issues were minor)");
@@ -14129,7 +14692,7 @@ function createImplementationOrchestrator(deps) {
14129
14692
  rawOutput: reviewResult.rawOutput
14130
14693
  } : void 0;
14131
14694
  let sourceEpicContent2;
14132
- const epicsPath2 = findEpicsFile(projectRoot ?? process.cwd());
14695
+ const epicsPath2 = findEpicFileForStory(projectRoot ?? process.cwd(), storyKey);
14133
14696
  if (epicsPath2) try {
14134
14697
  const epicFull2 = readFileSync(epicsPath2, "utf-8");
14135
14698
  const section2 = extractStorySection(epicFull2, storyKey);
@@ -14153,7 +14716,7 @@ function createImplementationOrchestrator(deps) {
14153
14716
  phase: "VERIFICATION_FAILED",
14154
14717
  completedAt: new Date().toISOString()
14155
14718
  });
14156
- persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$24.warn({
14719
+ persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
14157
14720
  err,
14158
14721
  storyKey
14159
14722
  }, "StateStore write failed after verification-failed"));
@@ -14176,7 +14739,7 @@ function createImplementationOrchestrator(deps) {
14176
14739
  completedAt: new Date().toISOString()
14177
14740
  });
14178
14741
  if (config.skipVerification !== true && runManifest != null) Promise.resolve().then(() => runManifest.read()).then((manifest) => {
14179
- if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$24.warn({
14742
+ if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$26.warn({
14180
14743
  storyKey,
14181
14744
  category: "verification-result-missing"
14182
14745
  }, "post-COMPLETE invariant: verification_result absent in manifest");
@@ -14201,7 +14764,7 @@ function createImplementationOrchestrator(deps) {
14201
14764
  outcome: "retried",
14202
14765
  cost_usd: 0,
14203
14766
  timestamp: new Date().toISOString()
14204
- }).catch((err) => logger$24.warn({
14767
+ }).catch((err) => logger$26.warn({
14205
14768
  err,
14206
14769
  storyKey
14207
14770
  }, "appendRecoveryEntry failed — pipeline continues"));
@@ -14341,7 +14904,7 @@ function createImplementationOrchestrator(deps) {
14341
14904
  fixPrompt = assembled.prompt;
14342
14905
  } catch {
14343
14906
  fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, taskType=${taskType}`;
14344
- logger$24.warn({
14907
+ logger$26.warn({
14345
14908
  storyKey,
14346
14909
  taskType
14347
14910
  }, "Failed to assemble fix prompt, using fallback");
@@ -14378,7 +14941,86 @@ function createImplementationOrchestrator(deps) {
14378
14941
  } : void 0 }
14379
14942
  });
14380
14943
  if (fixResult.status === "timeout") {
14381
- logger$24.warn({
14944
+ if (taskType === "minor-fixes") {
14945
+ const finalReviewCycles = reviewCycles + 1;
14946
+ const downgradedVerdict = "LGTM_WITH_NOTES";
14947
+ logger$26.warn({
14948
+ storyKey,
14949
+ reviewCycles: finalReviewCycles,
14950
+ issueCount: issueList.length
14951
+ }, "Minor-fixes dispatch timed out — auto-approving as LGTM_WITH_NOTES (original findings retained as warnings)");
14952
+ endPhase(storyKey, "code-review");
14953
+ if (config.skipVerification !== true) {
14954
+ const latestReviewSignals = reviewResult != null ? {
14955
+ dispatchFailed: reviewResult.dispatchFailed,
14956
+ error: reviewResult.error,
14957
+ rawOutput: reviewResult.rawOutput
14958
+ } : void 0;
14959
+ let sourceEpicContent3;
14960
+ const epicsPath3 = findEpicFileForStory(projectRoot ?? process.cwd(), storyKey);
14961
+ if (epicsPath3) try {
14962
+ const epicFull3 = readFileSync(epicsPath3, "utf-8");
14963
+ const section3 = extractStorySection(epicFull3, storyKey);
14964
+ if (section3) sourceEpicContent3 = section3;
14965
+ } catch {}
14966
+ await persistDevStorySignals(storyKey, devStorySignals, runManifest);
14967
+ const verifContext = assembleVerificationContext({
14968
+ storyKey,
14969
+ workingDir: projectRoot ?? process.cwd(),
14970
+ reviewResult: latestReviewSignals,
14971
+ storyContent: storyContentForVerification,
14972
+ devStoryResult: devStorySignals,
14973
+ outputTokenCount: devOutputTokenCount,
14974
+ sourceEpicContent: sourceEpicContent3
14975
+ });
14976
+ const verifSummary = await verificationPipeline.run(verifContext, "A");
14977
+ verificationStore.set(storyKey, verifSummary);
14978
+ await persistVerificationResult(storyKey, verifSummary, runManifest);
14979
+ if (verifSummary.status === "fail") {
14980
+ updateStory(storyKey, {
14981
+ phase: "VERIFICATION_FAILED",
14982
+ completedAt: new Date().toISOString()
14983
+ });
14984
+ persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
14985
+ err,
14986
+ storyKey
14987
+ }, "StateStore write failed after verification-failed"));
14988
+ await writeStoryMetricsBestEffort(storyKey, "verification-failed", finalReviewCycles);
14989
+ await persistState();
14990
+ return;
14991
+ }
14992
+ }
14993
+ eventBus.emit("story:auto-approved", {
14994
+ storyKey,
14995
+ verdict: downgradedVerdict,
14996
+ reviewCycles: finalReviewCycles,
14997
+ maxReviewCycles: config.maxReviewCycles,
14998
+ issueCount: issueList.length,
14999
+ reason: `Minor-fixes dispatch timed out (cycle ${finalReviewCycles}) — auto-approving as LGTM_WITH_NOTES with original findings retained as warnings`
15000
+ });
15001
+ updateStory(storyKey, {
15002
+ phase: "COMPLETE",
15003
+ reviewCycles: finalReviewCycles,
15004
+ lastVerdict: downgradedVerdict,
15005
+ completedAt: new Date().toISOString()
15006
+ });
15007
+ if (config.skipVerification !== true && runManifest != null) Promise.resolve().then(() => runManifest.read()).then((manifest) => {
15008
+ if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$26.warn({
15009
+ storyKey,
15010
+ category: "verification-result-missing"
15011
+ }, "post-COMPLETE invariant: verification_result absent in manifest");
15012
+ }).catch(() => {});
15013
+ await writeStoryMetricsBestEffort(storyKey, downgradedVerdict, finalReviewCycles);
15014
+ await writeStoryOutcomeBestEffort(storyKey, "complete", finalReviewCycles);
15015
+ eventBus.emit("orchestrator:story-complete", {
15016
+ storyKey,
15017
+ reviewCycles: finalReviewCycles
15018
+ });
15019
+ await persistState();
15020
+ keepReviewing = false;
15021
+ return;
15022
+ }
15023
+ logger$26.warn({
14382
15024
  storyKey,
14383
15025
  taskType
14384
15026
  }, "Fix dispatch timed out — escalating story");
@@ -14400,7 +15042,7 @@ function createImplementationOrchestrator(deps) {
14400
15042
  }
14401
15043
  if (fixResult.status === "failed") {
14402
15044
  if (isMajorRework) {
14403
- logger$24.warn({
15045
+ logger$26.warn({
14404
15046
  storyKey,
14405
15047
  exitCode: fixResult.exitCode
14406
15048
  }, "Major rework dispatch failed — escalating story");
@@ -14420,7 +15062,7 @@ function createImplementationOrchestrator(deps) {
14420
15062
  await persistState();
14421
15063
  return;
14422
15064
  }
14423
- logger$24.warn({
15065
+ logger$26.warn({
14424
15066
  storyKey,
14425
15067
  taskType,
14426
15068
  exitCode: fixResult.exitCode
@@ -14428,7 +15070,7 @@ function createImplementationOrchestrator(deps) {
14428
15070
  }
14429
15071
  if (isMajorRework) replaceDevStorySignals(fixResult.parsed);
14430
15072
  } catch (err) {
14431
- logger$24.warn({
15073
+ logger$26.warn({
14432
15074
  storyKey,
14433
15075
  taskType,
14434
15076
  err
@@ -14479,7 +15121,7 @@ function createImplementationOrchestrator(deps) {
14479
15121
  ...haltOn !== "none" ? { severity: "critical" } : {}
14480
15122
  });
14481
15123
  _budgetExhausted = true;
14482
- logger$24.warn({
15124
+ logger$26.warn({
14483
15125
  skipped: allSkipped.length,
14484
15126
  cumulative: result.cumulative,
14485
15127
  ceiling: result.ceiling
@@ -14496,7 +15138,7 @@ function createImplementationOrchestrator(deps) {
14496
15138
  const completedStoryKeys = [];
14497
15139
  for (const storyKey of group) {
14498
15140
  if (_shutdownRequested) {
14499
- logger$24.info({ storyKey }, "shutdown requested — skipping dispatch");
15141
+ logger$26.info({ storyKey }, "shutdown requested — skipping dispatch");
14500
15142
  return;
14501
15143
  }
14502
15144
  if (runManifest !== null && runManifest !== void 0) try {
@@ -14519,7 +15161,7 @@ function createImplementationOrchestrator(deps) {
14519
15161
  }
14520
15162
  }
14521
15163
  } catch (err) {
14522
- logger$24.debug({ err }, "Cost ceiling check failed — proceeding without enforcement");
15164
+ logger$26.debug({ err }, "Cost ceiling check failed — proceeding without enforcement");
14523
15165
  }
14524
15166
  let optimizationDirectives;
14525
15167
  if (telemetryAdvisor !== void 0 && completedStoryKeys.length > 0) try {
@@ -14527,13 +15169,13 @@ function createImplementationOrchestrator(deps) {
14527
15169
  const directives = telemetryAdvisor.formatOptimizationDirectives(recs);
14528
15170
  if (directives.length > 0) {
14529
15171
  optimizationDirectives = directives;
14530
- logger$24.debug({
15172
+ logger$26.debug({
14531
15173
  storyKey,
14532
15174
  directiveCount: recs.filter((r) => r.severity !== "info").length
14533
15175
  }, "Optimization directives ready for dispatch");
14534
15176
  }
14535
15177
  } catch (err) {
14536
- logger$24.debug({
15178
+ logger$26.debug({
14537
15179
  err,
14538
15180
  storyKey
14539
15181
  }, "Failed to fetch optimization directives — proceeding without");
@@ -14588,7 +15230,7 @@ function createImplementationOrchestrator(deps) {
14588
15230
  async function shutdownGracefully(reason, signal) {
14589
15231
  if (_shutdownRequested) return;
14590
15232
  _shutdownRequested = true;
14591
- logger$24.info({
15233
+ logger$26.info({
14592
15234
  reason,
14593
15235
  signal
14594
15236
  }, "Graceful shutdown initiated — stopping new dispatches");
@@ -14598,8 +15240,8 @@ function createImplementationOrchestrator(deps) {
14598
15240
  run_status: "stopped",
14599
15241
  stopped_reason: reason,
14600
15242
  stopped_at: new Date().toISOString()
14601
- }).catch((err) => logger$24.warn({ err }, "patchRunStatus failed during shutdown (best-effort)"));
14602
- if (config.pipelineRunId !== void 0) await updatePipelineRun(db, config.pipelineRunId, { status: "stopped" }).catch((err) => logger$24.warn({ err }, "updatePipelineRun(stopped) failed during shutdown (best-effort)"));
15243
+ }).catch((err) => logger$26.warn({ err }, "patchRunStatus failed during shutdown (best-effort)"));
15244
+ if (config.pipelineRunId !== void 0) await updatePipelineRun(db, config.pipelineRunId, { status: "stopped" }).catch((err) => logger$26.warn({ err }, "updatePipelineRun(stopped) failed during shutdown (best-effort)"));
14603
15245
  const activePhases = [
14604
15246
  "PENDING",
14605
15247
  "IN_STORY_CREATION",
@@ -14610,7 +15252,7 @@ function createImplementationOrchestrator(deps) {
14610
15252
  "CHECKPOINT"
14611
15253
  ];
14612
15254
  const cancellations = [];
14613
- for (const [storyKey, state] of _stories.entries()) if (activePhases.includes(state.phase)) cancellations.push(wgRepo.updateStoryStatus(storyKey, "cancelled").catch((err) => logger$24.warn({
15255
+ for (const [storyKey, state] of _stories.entries()) if (activePhases.includes(state.phase)) cancellations.push(wgRepo.updateStoryStatus(storyKey, "cancelled").catch((err) => logger$26.warn({
14614
15256
  err,
14615
15257
  storyKey
14616
15258
  }, "wg_stories → cancelled failed during shutdown (best-effort)")));
@@ -14619,11 +15261,11 @@ function createImplementationOrchestrator(deps) {
14619
15261
  }
14620
15262
  async function run(storyKeys) {
14621
15263
  if (_state === "RUNNING" || _state === "PAUSED") {
14622
- logger$24.warn({ state: _state }, "run() called while orchestrator is already running or paused — ignoring");
15264
+ logger$26.warn({ state: _state }, "run() called while orchestrator is already running or paused — ignoring");
14623
15265
  return getStatus();
14624
15266
  }
14625
15267
  if (_state === "COMPLETE") {
14626
- logger$24.warn({ state: _state }, "run() called on a COMPLETE orchestrator — ignoring");
15268
+ logger$26.warn({ state: _state }, "run() called on a COMPLETE orchestrator — ignoring");
14627
15269
  return getStatus();
14628
15270
  }
14629
15271
  _state = "RUNNING";
@@ -14647,7 +15289,7 @@ function createImplementationOrchestrator(deps) {
14647
15289
  const seedStart = Date.now();
14648
15290
  const seedResult = await seedMethodologyContext(db, projectRoot);
14649
15291
  _startupTimings.seedMethodologyMs = Date.now() - seedStart;
14650
- if (seedResult.decisionsCreated > 0) logger$24.info({
15292
+ if (seedResult.decisionsCreated > 0) logger$26.info({
14651
15293
  decisionsCreated: seedResult.decisionsCreated,
14652
15294
  skippedCategories: seedResult.skippedCategories,
14653
15295
  durationMs: _startupTimings.seedMethodologyMs
@@ -14657,12 +15299,12 @@ function createImplementationOrchestrator(deps) {
14657
15299
  const ingestStart = Date.now();
14658
15300
  try {
14659
15301
  const ingestResult = await autoIngestEpicsDependencies(db, projectRoot);
14660
- if (ingestResult.storiesIngested > 0 || ingestResult.dependenciesIngested > 0) logger$24.info({
15302
+ if (ingestResult.storiesIngested > 0 || ingestResult.dependenciesIngested > 0) logger$26.info({
14661
15303
  ...ingestResult,
14662
15304
  durationMs: Date.now() - ingestStart
14663
15305
  }, "Auto-ingested stories and dependencies from epics document");
14664
15306
  } catch (err) {
14665
- logger$24.debug({ err }, "Auto-ingest from epics document skipped — work graph may be unavailable");
15307
+ logger$26.debug({ err }, "Auto-ingest from epics document skipped — work graph may be unavailable");
14666
15308
  }
14667
15309
  }
14668
15310
  const sigtermHandler = () => {
@@ -14680,7 +15322,7 @@ function createImplementationOrchestrator(deps) {
14680
15322
  _startupTimings.stateStoreInitMs = Date.now() - stateStoreInitStart;
14681
15323
  for (const key of storyKeys) {
14682
15324
  const pendingState = _stories.get(key);
14683
- if (pendingState !== void 0) persistStoryState(key, pendingState).catch((err) => logger$24.warn({
15325
+ if (pendingState !== void 0) persistStoryState(key, pendingState).catch((err) => logger$26.warn({
14684
15326
  err,
14685
15327
  storyKey: key
14686
15328
  }, "StateStore write failed during PENDING init"));
@@ -14691,12 +15333,12 @@ function createImplementationOrchestrator(deps) {
14691
15333
  _startupTimings.queryStoriesMs = Date.now() - queryStoriesStart;
14692
15334
  for (const record of existingRecords) _stateStoreCache.set(record.storyKey, record);
14693
15335
  } catch (err) {
14694
- logger$24.warn({ err }, "StateStore.queryStories() failed during init — status merge will be empty (best-effort)");
15336
+ logger$26.warn({ err }, "StateStore.queryStories() failed during init — status merge will be empty (best-effort)");
14695
15337
  }
14696
15338
  }
14697
15339
  if (ingestionServer !== void 0) {
14698
15340
  if (telemetryPersistence !== void 0) try {
14699
- const pipelineLogger = logger$24;
15341
+ const pipelineLogger = logger$26;
14700
15342
  const telemetryPipeline = new TelemetryPipeline({
14701
15343
  normalizer: new TelemetryNormalizer(pipelineLogger),
14702
15344
  turnAnalyzer: new TurnAnalyzer(pipelineLogger),
@@ -14708,14 +15350,14 @@ function createImplementationOrchestrator(deps) {
14708
15350
  persistence: telemetryPersistence
14709
15351
  });
14710
15352
  ingestionServer.setPipeline(telemetryPipeline);
14711
- logger$24.info("TelemetryPipeline wired to IngestionServer");
15353
+ logger$26.info("TelemetryPipeline wired to IngestionServer");
14712
15354
  } catch (pipelineErr) {
14713
- logger$24.warn({ err: pipelineErr }, "Failed to create TelemetryPipeline — continuing without analysis pipeline");
15355
+ logger$26.warn({ err: pipelineErr }, "Failed to create TelemetryPipeline — continuing without analysis pipeline");
14714
15356
  }
14715
- await ingestionServer.start().catch((err) => logger$24.warn({ err }, "IngestionServer.start() failed — continuing without telemetry (best-effort)"));
15357
+ await ingestionServer.start().catch((err) => logger$26.warn({ err }, "IngestionServer.start() failed — continuing without telemetry (best-effort)"));
14716
15358
  try {
14717
15359
  _otlpEndpoint = ingestionServer.getOtlpEnvVars().OTEL_EXPORTER_OTLP_ENDPOINT;
14718
- logger$24.info({ otlpEndpoint: _otlpEndpoint }, "OTLP telemetry ingestion active");
15360
+ logger$26.info({ otlpEndpoint: _otlpEndpoint }, "OTLP telemetry ingestion active");
14719
15361
  } catch {}
14720
15362
  }
14721
15363
  let contractDeclarations = [];
@@ -14755,12 +15397,12 @@ function createImplementationOrchestrator(deps) {
14755
15397
  const conflictDetectStart = Date.now();
14756
15398
  const { batches, edges: contractEdges } = detectConflictGroupsWithContracts(storyKeys, { moduleMap: pack.manifest.conflictGroups }, contractDeclarations);
14757
15399
  _startupTimings.conflictDetectMs = Date.now() - conflictDetectStart;
14758
- if (contractEdges.length > 0) logger$24.info({
15400
+ if (contractEdges.length > 0) logger$26.info({
14759
15401
  contractEdges,
14760
15402
  edgeCount: contractEdges.length
14761
15403
  }, "Contract dependency edges detected — applying contract-aware dispatch ordering");
14762
- wgRepo.addContractDependencies(contractEdges).catch((err) => logger$24.warn({ err }, "contract dep persistence failed (best-effort)"));
14763
- logger$24.info({
15404
+ wgRepo.addContractDependencies(contractEdges).catch((err) => logger$26.warn({ err }, "contract dep persistence failed (best-effort)"));
15405
+ logger$26.info({
14764
15406
  storyCount: storyKeys.length,
14765
15407
  groupCount: batches.reduce((sum, b) => sum + b.length, 0),
14766
15408
  batchCount: batches.length,
@@ -14770,7 +15412,7 @@ function createImplementationOrchestrator(deps) {
14770
15412
  groups: batch.map((g) => g.join(","))
14771
15413
  }))
14772
15414
  }, "Orchestrator starting");
14773
- logger$24.info({
15415
+ logger$26.info({
14774
15416
  storyCount: storyKeys.length,
14775
15417
  conflictGroups: batches.length,
14776
15418
  maxConcurrency: config.maxConcurrency
@@ -14791,7 +15433,7 @@ function createImplementationOrchestrator(deps) {
14791
15433
  exitCode,
14792
15434
  output: truncatedOutput
14793
15435
  });
14794
- logger$24.error({
15436
+ logger$26.error({
14795
15437
  exitCode,
14796
15438
  reason: preFlightResult.reason
14797
15439
  }, "Pre-flight build check failed — aborting pipeline before any story dispatch");
@@ -14800,19 +15442,19 @@ function createImplementationOrchestrator(deps) {
14800
15442
  await persistState();
14801
15443
  return getStatus();
14802
15444
  }
14803
- if (preFlightResult.status !== "skipped") logger$24.info("Pre-flight build check passed");
15445
+ if (preFlightResult.status !== "skipped") logger$26.info("Pre-flight build check passed");
14804
15446
  }
14805
- logger$24.info(_startupTimings, "Orchestrator startup timings (ms)");
15447
+ logger$26.info(_startupTimings, "Orchestrator startup timings (ms)");
14806
15448
  const totalGroups = batches.reduce((sum, b) => sum + b.length, 0);
14807
15449
  const actualConcurrency = Math.min(config.maxConcurrency, totalGroups);
14808
15450
  if (actualConcurrency > 1 && projectRoot !== void 0) try {
14809
15451
  _packageSnapshot = capturePackageSnapshot({ projectRoot });
14810
- logger$24.info({
15452
+ logger$26.info({
14811
15453
  fileCount: _packageSnapshot.files.size,
14812
15454
  installCommand: _packageSnapshot.installCommand
14813
15455
  }, "Package snapshot captured for concurrent story protection");
14814
15456
  } catch (snapErr) {
14815
- logger$24.warn({ err: snapErr }, "Failed to capture package snapshot — continuing without protection");
15457
+ logger$26.warn({ err: snapErr }, "Failed to capture package snapshot — continuing without protection");
14816
15458
  }
14817
15459
  try {
14818
15460
  for (const batchGroups of batches) await runWithConcurrency(batchGroups, config.maxConcurrency);
@@ -14821,7 +15463,7 @@ function createImplementationOrchestrator(deps) {
14821
15463
  _state = "FAILED";
14822
15464
  _completedAt = new Date().toISOString();
14823
15465
  await persistState();
14824
- logger$24.error({ err }, "Orchestrator failed with unhandled error");
15466
+ logger$26.error({ err }, "Orchestrator failed with unhandled error");
14825
15467
  return getStatus();
14826
15468
  }
14827
15469
  stopHeartbeat();
@@ -14831,7 +15473,7 @@ function createImplementationOrchestrator(deps) {
14831
15473
  const totalDeclarations = contractDeclarations.length;
14832
15474
  const currentSprintDeclarations = contractDeclarations.filter((d) => storyKeys.includes(d.storyKey));
14833
15475
  const stalePruned = totalDeclarations - currentSprintDeclarations.length;
14834
- if (stalePruned > 0) logger$24.info({
15476
+ if (stalePruned > 0) logger$26.info({
14835
15477
  stalePruned,
14836
15478
  remaining: currentSprintDeclarations.length
14837
15479
  }, "Pruned stale contract declarations from previous epics");
@@ -14845,11 +15487,11 @@ function createImplementationOrchestrator(deps) {
14845
15487
  contractName: mismatch.contractName,
14846
15488
  mismatchDescription: mismatch.mismatchDescription
14847
15489
  });
14848
- logger$24.warn({
15490
+ logger$26.warn({
14849
15491
  mismatchCount: mismatches.length,
14850
15492
  mismatches
14851
15493
  }, "Post-sprint contract verification found mismatches — manual review required");
14852
- } else if (currentSprintDeclarations.length > 0) logger$24.info("Post-sprint contract verification passed — all declared contracts satisfied");
15494
+ } else if (currentSprintDeclarations.length > 0) logger$26.info("Post-sprint contract verification passed — all declared contracts satisfied");
14853
15495
  eventBus.emit("pipeline:contract-verification-summary", {
14854
15496
  verified: currentSprintDeclarations.length,
14855
15497
  stalePruned,
@@ -14884,12 +15526,12 @@ function createImplementationOrchestrator(deps) {
14884
15526
  });
14885
15527
  await stateStore.setContractVerification(sk, records);
14886
15528
  }
14887
- logger$24.info({ storyCount: contractsByStory.size }, "Contract verification results persisted to StateStore");
15529
+ logger$26.info({ storyCount: contractsByStory.size }, "Contract verification results persisted to StateStore");
14888
15530
  } catch (persistErr) {
14889
- logger$24.warn({ err: persistErr }, "Failed to persist contract verification results to StateStore");
15531
+ logger$26.warn({ err: persistErr }, "Failed to persist contract verification results to StateStore");
14890
15532
  }
14891
15533
  } catch (err) {
14892
- logger$24.error({ err }, "Post-sprint contract verification threw an error — skipping");
15534
+ logger$26.error({ err }, "Post-sprint contract verification threw an error — skipping");
14893
15535
  }
14894
15536
  if (projectRoot !== void 0) try {
14895
15537
  const indicators = checkProfileStaleness(projectRoot);
@@ -14899,10 +15541,10 @@ function createImplementationOrchestrator(deps) {
14899
15541
  message,
14900
15542
  indicators
14901
15543
  });
14902
- logger$24.warn({ indicators }, message);
15544
+ logger$26.warn({ indicators }, message);
14903
15545
  }
14904
15546
  } catch (err) {
14905
- logger$24.debug({ err }, "Profile staleness check failed (best-effort)");
15547
+ logger$26.debug({ err }, "Profile staleness check failed (best-effort)");
14906
15548
  }
14907
15549
  let completed = 0;
14908
15550
  let escalated = 0;
@@ -14921,8 +15563,8 @@ function createImplementationOrchestrator(deps) {
14921
15563
  } finally {
14922
15564
  process.off("SIGTERM", sigtermHandler);
14923
15565
  process.off("SIGINT", sigintHandler);
14924
- if (stateStore !== void 0) await stateStore.close().catch((err) => logger$24.warn({ err }, "StateStore.close() failed (best-effort)"));
14925
- if (ingestionServer !== void 0) await ingestionServer.stop().catch((err) => logger$24.warn({ err }, "IngestionServer.stop() failed (best-effort)"));
15566
+ if (stateStore !== void 0) await stateStore.close().catch((err) => logger$26.warn({ err }, "StateStore.close() failed (best-effort)"));
15567
+ if (ingestionServer !== void 0) await ingestionServer.stop().catch((err) => logger$26.warn({ err }, "IngestionServer.stop() failed (best-effort)"));
14926
15568
  }
14927
15569
  }
14928
15570
  function pause() {
@@ -14931,7 +15573,7 @@ function createImplementationOrchestrator(deps) {
14931
15573
  _pauseGate = createPauseGate();
14932
15574
  _state = "PAUSED";
14933
15575
  eventBus.emit("orchestrator:paused", {});
14934
- logger$24.info("Orchestrator paused");
15576
+ logger$26.info("Orchestrator paused");
14935
15577
  }
14936
15578
  function resume() {
14937
15579
  if (_state !== "PAUSED") return;
@@ -14942,7 +15584,7 @@ function createImplementationOrchestrator(deps) {
14942
15584
  }
14943
15585
  _state = "RUNNING";
14944
15586
  eventBus.emit("orchestrator:resumed", {});
14945
- logger$24.info("Orchestrator resumed");
15587
+ logger$26.info("Orchestrator resumed");
14946
15588
  }
14947
15589
  return {
14948
15590
  run,
@@ -16358,8 +17000,8 @@ async function resolveContext(ref, deps, runId, params, stepOutputs) {
16358
17000
  return params[key] ?? "";
16359
17001
  }
16360
17002
  if (source.startsWith("decision:")) {
16361
- const path$3 = source.slice(9);
16362
- const [phase, category] = path$3.split(".");
17003
+ const path$4 = source.slice(9);
17004
+ const [phase, category] = path$4.split(".");
16363
17005
  if (!phase || !category) return "";
16364
17006
  const decisions = await getDecisionsByPhaseForRun(deps.db, runId, phase);
16365
17007
  const filtered = decisions.filter((d) => d.category === category);
@@ -16430,8 +17072,8 @@ async function runSteps(steps, deps, runId, phase, params) {
16430
17072
  for (const ref of step.context) {
16431
17073
  let value;
16432
17074
  if (ref.source.startsWith("decision:")) {
16433
- const path$3 = ref.source.slice(9);
16434
- const [decPhase, decCategory] = path$3.split(".");
17075
+ const path$4 = ref.source.slice(9);
17076
+ const [decPhase, decCategory] = path$4.split(".");
16435
17077
  if (decPhase && decCategory) {
16436
17078
  const decisions = await getDecisionsByPhaseForRun(deps.db, runId, decPhase);
16437
17079
  const filtered = decisions.filter((d) => d.category === decCategory);
@@ -27478,8 +28120,8 @@ var require_uri_all = __commonJS({ "node_modules/uri-js/dist/es5/uri.all.js"(exp
27478
28120
  wsComponents.secure = void 0;
27479
28121
  }
27480
28122
  if (wsComponents.resourceName) {
27481
- var _wsComponents$resourc = wsComponents.resourceName.split("?"), _wsComponents$resourc2 = slicedToArray(_wsComponents$resourc, 2), path$3 = _wsComponents$resourc2[0], query = _wsComponents$resourc2[1];
27482
- wsComponents.path = path$3 && path$3 !== "/" ? path$3 : void 0;
28123
+ var _wsComponents$resourc = wsComponents.resourceName.split("?"), _wsComponents$resourc2 = slicedToArray(_wsComponents$resourc, 2), path$4 = _wsComponents$resourc2[0], query = _wsComponents$resourc2[1];
28124
+ wsComponents.path = path$4 && path$4 !== "/" ? path$4 : void 0;
27483
28125
  wsComponents.query = query;
27484
28126
  wsComponents.resourceName = void 0;
27485
28127
  }
@@ -27820,12 +28462,12 @@ var require_util = __commonJS({ "node_modules/ajv/lib/compile/util.js"(exports,
27820
28462
  return "'" + escapeQuotes(str) + "'";
27821
28463
  }
27822
28464
  function getPathExpr(currentPath, expr, jsonPointers, isNumber) {
27823
- var path$3 = jsonPointers ? "'/' + " + expr + (isNumber ? "" : ".replace(/~/g, '~0').replace(/\\//g, '~1')") : isNumber ? "'[' + " + expr + " + ']'" : "'[\\'' + " + expr + " + '\\']'";
27824
- return joinPaths(currentPath, path$3);
28465
+ var path$4 = jsonPointers ? "'/' + " + expr + (isNumber ? "" : ".replace(/~/g, '~0').replace(/\\//g, '~1')") : isNumber ? "'[' + " + expr + " + ']'" : "'[\\'' + " + expr + " + '\\']'";
28466
+ return joinPaths(currentPath, path$4);
27825
28467
  }
27826
28468
  function getPath(currentPath, prop, jsonPointers) {
27827
- var path$3 = jsonPointers ? toQuotedString("/" + escapeJsonPointer(prop)) : toQuotedString(getProperty(prop));
27828
- return joinPaths(currentPath, path$3);
28469
+ var path$4 = jsonPointers ? toQuotedString("/" + escapeJsonPointer(prop)) : toQuotedString(getProperty(prop));
28470
+ return joinPaths(currentPath, path$4);
27829
28471
  }
27830
28472
  var JSON_POINTER$1 = /^\/(?:[^~]|~0|~1)*$/;
27831
28473
  var RELATIVE_JSON_POINTER$1 = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
@@ -31993,16 +32635,16 @@ var require_ajv = __commonJS({ "node_modules/ajv/lib/ajv.js"(exports, module) {
31993
32635
  return metaOpts;
31994
32636
  }
31995
32637
  function setLogger(self) {
31996
- var logger$24 = self._opts.logger;
31997
- if (logger$24 === false) self.logger = {
32638
+ var logger$26 = self._opts.logger;
32639
+ if (logger$26 === false) self.logger = {
31998
32640
  log: noop,
31999
32641
  warn: noop,
32000
32642
  error: noop
32001
32643
  };
32002
32644
  else {
32003
- if (logger$24 === void 0) logger$24 = console;
32004
- if (!(typeof logger$24 == "object" && logger$24.log && logger$24.warn && logger$24.error)) throw new Error("logger must implement log, warn and error methods");
32005
- self.logger = logger$24;
32645
+ if (logger$26 === void 0) logger$26 = console;
32646
+ if (!(typeof logger$26 == "object" && logger$26.log && logger$26.warn && logger$26.error)) throw new Error("logger must implement log, warn and error methods");
32647
+ self.logger = logger$26;
32006
32648
  }
32007
32649
  }
32008
32650
  function noop() {}
@@ -32034,8 +32676,8 @@ var ToolRegistry = class {
32034
32676
  if (!valid) {
32035
32677
  const errors = validate$1.errors ?? [];
32036
32678
  const messages = errors.map((e) => {
32037
- const path$3 = e.instancePath ?? e.dataPath ?? "";
32038
- return `${path$3} ${e.message ?? ""}`.trim();
32679
+ const path$4 = e.instancePath ?? e.dataPath ?? "";
32680
+ return `${path$4} ${e.message ?? ""}`.trim();
32039
32681
  }).join(", ");
32040
32682
  return {
32041
32683
  content: `Validation failed for tool '${name}': ${messages}`,
@@ -38639,10 +39281,10 @@ var PathBase = class {
38639
39281
  /**
38640
39282
  * Get the Path object referenced by the string path, resolved from this Path
38641
39283
  */
38642
- resolve(path$3) {
38643
- if (!path$3) return this;
38644
- const rootPath = this.getRootString(path$3);
38645
- const dir = path$3.substring(rootPath.length);
39284
+ resolve(path$4) {
39285
+ if (!path$4) return this;
39286
+ const rootPath = this.getRootString(path$4);
39287
+ const dir = path$4.substring(rootPath.length);
38646
39288
  const dirParts = dir.split(this.splitSep);
38647
39289
  const result = rootPath ? this.getRoot(rootPath).#resolveParts(dirParts) : this.#resolveParts(dirParts);
38648
39290
  return result;
@@ -39297,8 +39939,8 @@ var PathWin32 = class PathWin32 extends PathBase {
39297
39939
  /**
39298
39940
  * @internal
39299
39941
  */
39300
- getRootString(path$3) {
39301
- return win32.parse(path$3).root;
39942
+ getRootString(path$4) {
39943
+ return win32.parse(path$4).root;
39302
39944
  }
39303
39945
  /**
39304
39946
  * @internal
@@ -39343,8 +39985,8 @@ var PathPosix = class PathPosix extends PathBase {
39343
39985
  /**
39344
39986
  * @internal
39345
39987
  */
39346
- getRootString(path$3) {
39347
- return path$3.startsWith("/") ? "/" : "";
39988
+ getRootString(path$4) {
39989
+ return path$4.startsWith("/") ? "/" : "";
39348
39990
  }
39349
39991
  /**
39350
39992
  * @internal
@@ -39437,9 +40079,9 @@ var PathScurryBase = class {
39437
40079
  /**
39438
40080
  * Get the depth of a provided path, string, or the cwd
39439
40081
  */
39440
- depth(path$3 = this.cwd) {
39441
- if (typeof path$3 === "string") path$3 = this.cwd.resolve(path$3);
39442
- return path$3.depth();
40082
+ depth(path$4 = this.cwd) {
40083
+ if (typeof path$4 === "string") path$4 = this.cwd.resolve(path$4);
40084
+ return path$4.depth();
39443
40085
  }
39444
40086
  /**
39445
40087
  * Return the cache of child entries. Exposed so subclasses can create
@@ -39820,9 +40462,9 @@ var PathScurryBase = class {
39820
40462
  process$1();
39821
40463
  return results;
39822
40464
  }
39823
- chdir(path$3 = this.cwd) {
40465
+ chdir(path$4 = this.cwd) {
39824
40466
  const oldCwd = this.cwd;
39825
- this.cwd = typeof path$3 === "string" ? this.cwd.resolve(path$3) : path$3;
40467
+ this.cwd = typeof path$4 === "string" ? this.cwd.resolve(path$4) : path$4;
39826
40468
  this.cwd[setAsCwd](oldCwd);
39827
40469
  }
39828
40470
  };
@@ -40206,8 +40848,8 @@ var MatchRecord = class {
40206
40848
  this.store.set(target, current === void 0 ? n$1 : n$1 & current);
40207
40849
  }
40208
40850
  entries() {
40209
- return [...this.store.entries()].map(([path$3, n$1]) => [
40210
- path$3,
40851
+ return [...this.store.entries()].map(([path$4, n$1]) => [
40852
+ path$4,
40211
40853
  !!(n$1 & 2),
40212
40854
  !!(n$1 & 1)
40213
40855
  ]);
@@ -40383,9 +41025,9 @@ var GlobUtil = class {
40383
41025
  signal;
40384
41026
  maxDepth;
40385
41027
  includeChildMatches;
40386
- constructor(patterns, path$3, opts) {
41028
+ constructor(patterns, path$4, opts) {
40387
41029
  this.patterns = patterns;
40388
- this.path = path$3;
41030
+ this.path = path$4;
40389
41031
  this.opts = opts;
40390
41032
  this.#sep = !opts.posix && opts.platform === "win32" ? "\\" : "/";
40391
41033
  this.includeChildMatches = opts.includeChildMatches !== false;
@@ -40406,11 +41048,11 @@ var GlobUtil = class {
40406
41048
  });
40407
41049
  }
40408
41050
  }
40409
- #ignored(path$3) {
40410
- return this.seen.has(path$3) || !!this.#ignore?.ignored?.(path$3);
41051
+ #ignored(path$4) {
41052
+ return this.seen.has(path$4) || !!this.#ignore?.ignored?.(path$4);
40411
41053
  }
40412
- #childrenIgnored(path$3) {
40413
- return !!this.#ignore?.childrenIgnored?.(path$3);
41054
+ #childrenIgnored(path$4) {
41055
+ return !!this.#ignore?.childrenIgnored?.(path$4);
40414
41056
  }
40415
41057
  pause() {
40416
41058
  this.paused = true;
@@ -40592,8 +41234,8 @@ var GlobUtil = class {
40592
41234
  };
40593
41235
  var GlobWalker = class extends GlobUtil {
40594
41236
  matches = new Set();
40595
- constructor(patterns, path$3, opts) {
40596
- super(patterns, path$3, opts);
41237
+ constructor(patterns, path$4, opts) {
41238
+ super(patterns, path$4, opts);
40597
41239
  }
40598
41240
  matchEmit(e) {
40599
41241
  this.matches.add(e);
@@ -40620,8 +41262,8 @@ var GlobWalker = class extends GlobUtil {
40620
41262
  };
40621
41263
  var GlobStream = class extends GlobUtil {
40622
41264
  results;
40623
- constructor(patterns, path$3, opts) {
40624
- super(patterns, path$3, opts);
41265
+ constructor(patterns, path$4, opts) {
41266
+ super(patterns, path$4, opts);
40625
41267
  this.results = new Minipass({
40626
41268
  signal: this.signal,
40627
41269
  objectMode: true
@@ -44513,4 +45155,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
44513
45155
 
44514
45156
  //#endregion
44515
45157
  export { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, normalizeGraphSummaryToStatus, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveMaxReviewCycles, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict, wireNdjsonEmitter };
44516
- //# sourceMappingURL=run-D4WYiyK8.js.map
45158
+ //# sourceMappingURL=run-DGyWCmcu.js.map