substrate-ai 0.2.19 → 0.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-mFmS2pw6.js";
2
+ import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-CcWb6Kb-.js";
3
3
  import { createLogger, deepMask } from "../logger-D2fS2ccL.js";
4
4
  import { AdapterRegistry, createEventBus } from "../event-bus-BMxhfxfT.js";
5
5
  import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator } from "../version-manager-impl-CZ6KF1Ds.js";
6
6
  import { ConfigError, ConfigIncompatibleFormatError } from "../errors-BPqtzQ4U.js";
7
7
  import { addTokenUsage, createDecision, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-Dq4cAA2L.js";
8
- import { EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-CobuCGbM.js";
8
+ import { EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-Dq4IfJzE.js";
9
9
  import { abortMerge, createWorktree, getConflictingFiles, getMergedFiles, getOrphanedWorktrees, performMerge, removeBranch, removeWorktree, simulateMerge, verifyGitVersion } from "../git-utils-CtmrZrHS.js";
10
10
  import { registerUpgradeCommand } from "../upgrade-CjjAx5kD.js";
11
11
  import { Command } from "commander";
@@ -2828,7 +2828,7 @@ async function runSupervisorAction(options, deps = {}) {
2828
2828
  try {
2829
2829
  const { createExperimenter } = await import(
2830
2830
  /* @vite-ignore */
2831
- "../experimenter-CHRVkV3d.js"
2831
+ "../experimenter-prkFLFPw.js"
2832
2832
  );
2833
2833
  const { getLatestRun: getLatest } = await import(
2834
2834
  /* @vite-ignore */
@@ -2842,7 +2842,7 @@ async function runSupervisorAction(options, deps = {}) {
2842
2842
  const expDb = expDbWrapper.db;
2843
2843
  const { runRunAction: runPipeline } = await import(
2844
2844
  /* @vite-ignore */
2845
- "../run-CUGB4FQx.js"
2845
+ "../run-C5zfaWYN.js"
2846
2846
  );
2847
2847
  const runStoryFn = async (opts) => {
2848
2848
  const exitCode = await runPipeline({
@@ -1,6 +1,6 @@
1
1
  import "./logger-D2fS2ccL.js";
2
2
  import { createDecision } from "./decisions-Dq4cAA2L.js";
3
- import { EXPERIMENT_RESULT, getRunMetrics, getStoryMetricsForRun } from "./operational-CobuCGbM.js";
3
+ import { EXPERIMENT_RESULT, getRunMetrics, getStoryMetricsForRun } from "./operational-Dq4IfJzE.js";
4
4
  import { spawnGit } from "./git-utils-CtmrZrHS.js";
5
5
  import { spawn } from "node:child_process";
6
6
  import { join } from "node:path";
@@ -500,4 +500,4 @@ function createExperimenter(config, deps) {
500
500
 
501
501
  //#endregion
502
502
  export { createExperimenter };
503
- //# sourceMappingURL=experimenter-CHRVkV3d.js.map
503
+ //# sourceMappingURL=experimenter-prkFLFPw.js.map
package/dist/index.d.ts CHANGED
@@ -995,6 +995,19 @@ interface OrchestratorEvents {
995
995
  lastVerdict: string;
996
996
  reviewCycles: number;
997
997
  issues: unknown[];
998
+ /** Structured diagnosis with classification and recommended action (Story 22-3) */
999
+ diagnosis?: {
1000
+ issueDistribution: 'concentrated' | 'widespread';
1001
+ severityProfile: 'blocker-present' | 'major-only' | 'minor-only' | 'no-structured-issues';
1002
+ totalIssues: number;
1003
+ blockerCount: number;
1004
+ majorCount: number;
1005
+ minorCount: number;
1006
+ affectedFiles: string[];
1007
+ reviewCycles: number;
1008
+ recommendedAction: 'retry-targeted' | 'split-story' | 'human-intervention';
1009
+ rationale: string;
1010
+ };
998
1011
  };
999
1012
  /** A non-fatal warning occurred during story processing */
1000
1013
  'orchestrator:story-warn': {
@@ -255,7 +255,46 @@ const EXPERIMENT_RESULT = "experiment-result";
255
255
  * ```
256
256
  */
257
257
  const STORY_METRICS = "story-metrics";
258
+ /**
259
+ * Category for structured escalation diagnoses.
260
+ *
261
+ * Key schema: "{storyKey}:{runId}"
262
+ *
263
+ * Value shape:
264
+ * ```json
265
+ * {
266
+ * "issueDistribution": "concentrated",
267
+ * "severityProfile": "major-only",
268
+ * "totalIssues": 3,
269
+ * "blockerCount": 0,
270
+ * "majorCount": 3,
271
+ * "minorCount": 0,
272
+ * "affectedFiles": ["src/foo.ts"],
273
+ * "reviewCycles": 3,
274
+ * "recommendedAction": "retry-targeted",
275
+ * "rationale": "3 major issues concentrated in few files."
276
+ * }
277
+ * ```
278
+ */
279
+ const ESCALATION_DIAGNOSIS = "escalation-diagnosis";
280
+ /**
281
+ * Category for per-story outcome findings (learning loop).
282
+ *
283
+ * Key schema: "{storyKey}:{runId}"
284
+ *
285
+ * Value shape:
286
+ * ```json
287
+ * {
288
+ * "storyKey": "22-1",
289
+ * "outcome": "complete",
290
+ * "reviewCycles": 2,
291
+ * "verdictHistory": ["NEEDS_MINOR_FIXES", "SHIP_IT"],
292
+ * "recurringPatterns": ["missing error handling"]
293
+ * }
294
+ * ```
295
+ */
296
+ const STORY_OUTCOME = "story-outcome";
258
297
 
259
298
  //#endregion
260
- export { EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, aggregateTokenUsageForStory, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline, writeRunMetrics, writeStoryMetrics };
261
- //# sourceMappingURL=operational-CobuCGbM.js.map
299
+ export { ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, STORY_OUTCOME, aggregateTokenUsageForRun, aggregateTokenUsageForStory, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline, writeRunMetrics, writeStoryMetrics };
300
+ //# sourceMappingURL=operational-Dq4IfJzE.js.map
@@ -0,0 +1,7 @@
1
+ import { registerRunCommand, runRunAction } from "./run-CcWb6Kb-.js";
2
+ import "./logger-D2fS2ccL.js";
3
+ import "./event-bus-BMxhfxfT.js";
4
+ import "./decisions-Dq4cAA2L.js";
5
+ import "./operational-Dq4IfJzE.js";
6
+
7
+ export { runRunAction };
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "./logger-D2fS2ccL.js";
2
2
  import { AdapterRegistry, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning } from "./event-bus-BMxhfxfT.js";
3
- import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./decisions-Dq4cAA2L.js";
4
- import { STORY_METRICS, aggregateTokenUsageForRun, aggregateTokenUsageForStory, getStoryMetricsForRun, writeRunMetrics, writeStoryMetrics } from "./operational-CobuCGbM.js";
3
+ import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./decisions-Dq4cAA2L.js";
4
+ import { ESCALATION_DIAGNOSIS, OPERATIONAL_FINDING, STORY_METRICS, STORY_OUTCOME, aggregateTokenUsageForRun, aggregateTokenUsageForStory, getStoryMetricsForRun, writeRunMetrics, writeStoryMetrics } from "./operational-Dq4IfJzE.js";
5
5
  import { createRequire } from "module";
6
6
  import { dirname, join } from "path";
7
7
  import { access, readFile, readdir, stat } from "fs/promises";
@@ -539,7 +539,7 @@ const migration010RunMetrics = {
539
539
 
540
540
  //#endregion
541
541
  //#region src/persistence/migrations/index.ts
542
- const logger$16 = createLogger("persistence:migrations");
542
+ const logger$17 = createLogger("persistence:migrations");
543
543
  const MIGRATIONS = [
544
544
  initialSchemaMigration,
545
545
  costTrackerSchemaMigration,
@@ -557,7 +557,7 @@ const MIGRATIONS = [
557
557
  * Safe to call multiple times — already-applied migrations are skipped.
558
558
  */
559
559
  function runMigrations(db) {
560
- logger$16.info("Starting migration runner");
560
+ logger$17.info("Starting migration runner");
561
561
  db.exec(`
562
562
  CREATE TABLE IF NOT EXISTS schema_migrations (
563
563
  version INTEGER PRIMARY KEY,
@@ -568,12 +568,12 @@ function runMigrations(db) {
568
568
  const appliedVersions = new Set(db.prepare("SELECT version FROM schema_migrations").all().map((row) => row.version));
569
569
  const pending = MIGRATIONS.filter((m) => !appliedVersions.has(m.version)).sort((a, b) => a.version - b.version);
570
570
  if (pending.length === 0) {
571
- logger$16.info("No pending migrations");
571
+ logger$17.info("No pending migrations");
572
572
  return;
573
573
  }
574
574
  const insertMigration = db.prepare("INSERT INTO schema_migrations (version, name) VALUES (?, ?)");
575
575
  for (const migration of pending) {
576
- logger$16.info({
576
+ logger$17.info({
577
577
  version: migration.version,
578
578
  name: migration.name
579
579
  }, "Applying migration");
@@ -587,14 +587,14 @@ function runMigrations(db) {
587
587
  });
588
588
  applyMigration();
589
589
  }
590
- logger$16.info({ version: migration.version }, "Migration applied successfully");
590
+ logger$17.info({ version: migration.version }, "Migration applied successfully");
591
591
  }
592
- logger$16.info({ count: pending.length }, "All pending migrations applied");
592
+ logger$17.info({ count: pending.length }, "All pending migrations applied");
593
593
  }
594
594
 
595
595
  //#endregion
596
596
  //#region src/persistence/database.ts
597
- const logger$15 = createLogger("persistence:database");
597
+ const logger$16 = createLogger("persistence:database");
598
598
  /**
599
599
  * Thin wrapper that opens a SQLite database, applies required PRAGMAs,
600
600
  * and exposes the raw BetterSqlite3 instance.
@@ -611,14 +611,14 @@ var DatabaseWrapper = class {
611
611
  */
612
612
  open() {
613
613
  if (this._db !== null) return;
614
- logger$15.info({ path: this._path }, "Opening SQLite database");
614
+ logger$16.info({ path: this._path }, "Opening SQLite database");
615
615
  this._db = new BetterSqlite3(this._path);
616
616
  const walResult = this._db.pragma("journal_mode = WAL");
617
- if (walResult?.[0]?.journal_mode !== "wal") logger$15.warn({ result: walResult?.[0]?.journal_mode }, "WAL pragma did not return expected \"wal\" — journal_mode may be \"memory\" or unsupported");
617
+ if (walResult?.[0]?.journal_mode !== "wal") logger$16.warn({ result: walResult?.[0]?.journal_mode }, "WAL pragma did not return expected \"wal\" — journal_mode may be \"memory\" or unsupported");
618
618
  this._db.pragma("busy_timeout = 5000");
619
619
  this._db.pragma("synchronous = NORMAL");
620
620
  this._db.pragma("foreign_keys = ON");
621
- logger$15.info({ path: this._path }, "SQLite database opened with WAL mode");
621
+ logger$16.info({ path: this._path }, "SQLite database opened with WAL mode");
622
622
  }
623
623
  /**
624
624
  * Close the database. Idempotent — calling close() when already closed is a no-op.
@@ -627,7 +627,7 @@ var DatabaseWrapper = class {
627
627
  if (this._db === null) return;
628
628
  this._db.close();
629
629
  this._db = null;
630
- logger$15.info({ path: this._path }, "SQLite database closed");
630
+ logger$16.info({ path: this._path }, "SQLite database closed");
631
631
  }
632
632
  /**
633
633
  * Return the raw BetterSqlite3 instance.
@@ -2496,7 +2496,7 @@ function truncateToTokens(text, maxTokens) {
2496
2496
 
2497
2497
  //#endregion
2498
2498
  //#region src/modules/context-compiler/context-compiler-impl.ts
2499
- const logger$14 = createLogger("context-compiler");
2499
+ const logger$15 = createLogger("context-compiler");
2500
2500
  /**
2501
2501
  * Fraction of the original token budget that must remain (after required +
2502
2502
  * important sections) before an optional section is included.
@@ -2588,7 +2588,7 @@ var ContextCompilerImpl = class {
2588
2588
  includedParts.push(truncated);
2589
2589
  remainingBudget -= truncatedTokens;
2590
2590
  anyTruncated = true;
2591
- logger$14.warn({
2591
+ logger$15.warn({
2592
2592
  section: section.name,
2593
2593
  originalTokens: tokens,
2594
2594
  budgetTokens: truncatedTokens
@@ -2602,7 +2602,7 @@ var ContextCompilerImpl = class {
2602
2602
  });
2603
2603
  } else {
2604
2604
  anyTruncated = true;
2605
- logger$14.warn({
2605
+ logger$15.warn({
2606
2606
  section: section.name,
2607
2607
  tokens
2608
2608
  }, "Context compiler: omitted \"important\" section — no budget remaining");
@@ -2629,7 +2629,7 @@ var ContextCompilerImpl = class {
2629
2629
  } else {
2630
2630
  if (tokens > 0) {
2631
2631
  anyTruncated = true;
2632
- logger$14.warn({
2632
+ logger$15.warn({
2633
2633
  section: section.name,
2634
2634
  tokens,
2635
2635
  budgetFractionRemaining: budgetFractionRemaining.toFixed(2)
@@ -2914,7 +2914,7 @@ function parseYamlResult(yamlText, schema) {
2914
2914
 
2915
2915
  //#endregion
2916
2916
  //#region src/modules/agent-dispatch/dispatcher-impl.ts
2917
- const logger$13 = createLogger("agent-dispatch");
2917
+ const logger$14 = createLogger("agent-dispatch");
2918
2918
  const SHUTDOWN_GRACE_MS = 1e4;
2919
2919
  const SHUTDOWN_MAX_WAIT_MS = 3e4;
2920
2920
  const CHARS_PER_TOKEN = 4;
@@ -2957,7 +2957,7 @@ function getAvailableMemory() {
2957
2957
  encoding: "utf-8"
2958
2958
  }).trim(), 10);
2959
2959
  if (pressureLevel >= 4) {
2960
- logger$13.warn({ pressureLevel }, "macOS kernel reports critical memory pressure");
2960
+ logger$14.warn({ pressureLevel }, "macOS kernel reports critical memory pressure");
2961
2961
  return 0;
2962
2962
  }
2963
2963
  } catch {}
@@ -2972,7 +2972,7 @@ function getAvailableMemory() {
2972
2972
  const speculative = parseInt(vmstat.match(/Pages speculative:\s+(\d+)/)?.[1] ?? "0", 10);
2973
2973
  const available = (free + purgeable + speculative) * pageSize;
2974
2974
  if (pressureLevel >= 2) {
2975
- logger$13.warn({
2975
+ logger$14.warn({
2976
2976
  pressureLevel,
2977
2977
  availableBeforeDiscount: available
2978
2978
  }, "macOS kernel reports memory pressure — discounting estimate");
@@ -3051,7 +3051,7 @@ var DispatcherImpl = class {
3051
3051
  resolve: typedResolve,
3052
3052
  reject
3053
3053
  });
3054
- logger$13.debug({
3054
+ logger$14.debug({
3055
3055
  id,
3056
3056
  queueLength: this._queue.length
3057
3057
  }, "Dispatch queued");
@@ -3082,7 +3082,7 @@ var DispatcherImpl = class {
3082
3082
  async shutdown() {
3083
3083
  this._shuttingDown = true;
3084
3084
  this._stopMemoryPressureTimer();
3085
- logger$13.info({
3085
+ logger$14.info({
3086
3086
  running: this._running.size,
3087
3087
  queued: this._queue.length
3088
3088
  }, "Dispatcher shutting down");
@@ -3115,13 +3115,13 @@ var DispatcherImpl = class {
3115
3115
  }
3116
3116
  }, 50);
3117
3117
  });
3118
- logger$13.info("Dispatcher shutdown complete");
3118
+ logger$14.info("Dispatcher shutdown complete");
3119
3119
  }
3120
3120
  async _startDispatch(id, request, resolve$2) {
3121
3121
  const { prompt, agent, taskType, timeout, outputSchema, workingDirectory, model, maxTurns } = request;
3122
3122
  const adapter = this._adapterRegistry.get(agent);
3123
3123
  if (adapter === void 0) {
3124
- logger$13.warn({
3124
+ logger$14.warn({
3125
3125
  id,
3126
3126
  agent
3127
3127
  }, "No adapter found for agent");
@@ -3167,7 +3167,7 @@ var DispatcherImpl = class {
3167
3167
  });
3168
3168
  const startedAt = Date.now();
3169
3169
  proc.on("error", (err) => {
3170
- logger$13.error({
3170
+ logger$14.error({
3171
3171
  id,
3172
3172
  binary: cmd.binary,
3173
3173
  error: err.message
@@ -3175,7 +3175,7 @@ var DispatcherImpl = class {
3175
3175
  });
3176
3176
  if (proc.stdin !== null) {
3177
3177
  proc.stdin.on("error", (err) => {
3178
- if (err.code !== "EPIPE") logger$13.warn({
3178
+ if (err.code !== "EPIPE") logger$14.warn({
3179
3179
  id,
3180
3180
  error: err.message
3181
3181
  }, "stdin write error");
@@ -3217,7 +3217,7 @@ var DispatcherImpl = class {
3217
3217
  agent,
3218
3218
  taskType
3219
3219
  });
3220
- logger$13.debug({
3220
+ logger$14.debug({
3221
3221
  id,
3222
3222
  agent,
3223
3223
  taskType,
@@ -3234,7 +3234,7 @@ var DispatcherImpl = class {
3234
3234
  dispatchId: id,
3235
3235
  timeoutMs
3236
3236
  });
3237
- logger$13.warn({
3237
+ logger$14.warn({
3238
3238
  id,
3239
3239
  agent,
3240
3240
  taskType,
@@ -3288,7 +3288,7 @@ var DispatcherImpl = class {
3288
3288
  exitCode: code,
3289
3289
  output: stdout
3290
3290
  });
3291
- logger$13.debug({
3291
+ logger$14.debug({
3292
3292
  id,
3293
3293
  agent,
3294
3294
  taskType,
@@ -3314,7 +3314,7 @@ var DispatcherImpl = class {
3314
3314
  error: stderr || `Process exited with code ${String(code)}`,
3315
3315
  exitCode: code
3316
3316
  });
3317
- logger$13.debug({
3317
+ logger$14.debug({
3318
3318
  id,
3319
3319
  agent,
3320
3320
  taskType,
@@ -3373,7 +3373,7 @@ var DispatcherImpl = class {
3373
3373
  const next = this._queue.shift();
3374
3374
  if (next === void 0) return;
3375
3375
  next.handle.status = "running";
3376
- logger$13.debug({
3376
+ logger$14.debug({
3377
3377
  id: next.id,
3378
3378
  queueLength: this._queue.length
3379
3379
  }, "Dequeued dispatch");
@@ -3386,7 +3386,7 @@ var DispatcherImpl = class {
3386
3386
  _isMemoryPressured() {
3387
3387
  const free = getAvailableMemory();
3388
3388
  if (free < MIN_FREE_MEMORY_BYTES) {
3389
- logger$13.warn({
3389
+ logger$14.warn({
3390
3390
  freeMB: Math.round(free / 1024 / 1024),
3391
3391
  thresholdMB: Math.round(MIN_FREE_MEMORY_BYTES / 1024 / 1024)
3392
3392
  }, "Memory pressure detected — holding dispatch queue");
@@ -3424,9 +3424,94 @@ function createDispatcher(options) {
3424
3424
  return new DispatcherImpl(options.eventBus, options.adapterRegistry, config);
3425
3425
  }
3426
3426
 
3427
+ //#endregion
3428
+ //#region src/modules/implementation-orchestrator/escalation-diagnosis.ts
3429
+ /**
3430
+ * Generate a structured diagnosis from escalation data.
3431
+ *
3432
+ * Handles both structured issue lists (from code-review) and plain string
3433
+ * arrays (from create-story/dev-story failures).
3434
+ */
3435
+ function generateEscalationDiagnosis(issues, reviewCycles, lastVerdict) {
3436
+ const structured = issues.map((issue) => {
3437
+ if (typeof issue === "string") return {
3438
+ severity: "major",
3439
+ description: issue
3440
+ };
3441
+ const iss = issue;
3442
+ return {
3443
+ severity: iss.severity ?? "unknown",
3444
+ description: iss.description ?? "",
3445
+ file: iss.file,
3446
+ line: iss.line
3447
+ };
3448
+ });
3449
+ const blockerCount = structured.filter((i) => i.severity === "blocker").length;
3450
+ const majorCount = structured.filter((i) => i.severity === "major").length;
3451
+ const minorCount = structured.filter((i) => i.severity === "minor").length;
3452
+ const totalIssues = structured.length;
3453
+ const fileCounts = new Map();
3454
+ for (const issue of structured) if (issue.file) fileCounts.set(issue.file, (fileCounts.get(issue.file) ?? 0) + 1);
3455
+ const sortedFiles = [...fileCounts.entries()].sort((a, b) => b[1] - a[1]).map(([file]) => file);
3456
+ const issuesWithFiles = structured.filter((i) => i.file).length;
3457
+ let issueDistribution = "widespread";
3458
+ if (issuesWithFiles > 0 && sortedFiles.length > 0) {
3459
+ const topTwoCount = sortedFiles.slice(0, 2).reduce((sum, file) => sum + (fileCounts.get(file) ?? 0), 0);
3460
+ if (topTwoCount > issuesWithFiles * .5) issueDistribution = "concentrated";
3461
+ }
3462
+ let severityProfile;
3463
+ if (totalIssues === 0) severityProfile = "no-structured-issues";
3464
+ else if (blockerCount > 0) severityProfile = "blocker-present";
3465
+ else if (majorCount > 0) severityProfile = "major-only";
3466
+ else severityProfile = "minor-only";
3467
+ const { action, rationale } = pickRecommendation(issueDistribution, severityProfile, totalIssues, reviewCycles, lastVerdict);
3468
+ return {
3469
+ issueDistribution,
3470
+ severityProfile,
3471
+ totalIssues,
3472
+ blockerCount,
3473
+ majorCount,
3474
+ minorCount,
3475
+ affectedFiles: sortedFiles.slice(0, 5),
3476
+ reviewCycles,
3477
+ recommendedAction: action,
3478
+ rationale
3479
+ };
3480
+ }
3481
+ function pickRecommendation(distribution, profile, totalIssues, reviewCycles, lastVerdict) {
3482
+ if (lastVerdict.startsWith("create-story") || lastVerdict.startsWith("dev-story")) return {
3483
+ action: "human-intervention",
3484
+ rationale: `Pipeline failed during ${lastVerdict.replace(/-/g, " ")} before code review. Manual investigation needed.`
3485
+ };
3486
+ if (lastVerdict === "fix-dispatch-timeout") return {
3487
+ action: "retry-targeted",
3488
+ rationale: "Fix dispatch timed out. Retry with a targeted prompt focusing on the remaining issues."
3489
+ };
3490
+ if (profile === "no-structured-issues") return {
3491
+ action: "retry-targeted",
3492
+ rationale: "Review produced no structured issues — likely a schema parse failure. Retry may resolve."
3493
+ };
3494
+ if (profile === "blocker-present") return {
3495
+ action: "human-intervention",
3496
+ rationale: `${totalIssues} issues including blockers after ${reviewCycles} review cycles. Fundamental problems remain — human review recommended.`
3497
+ };
3498
+ if (distribution === "concentrated" && profile === "major-only" && totalIssues <= 5) return {
3499
+ action: "retry-targeted",
3500
+ rationale: `${totalIssues} major issues concentrated in few files. A targeted retry prompt could resolve them.`
3501
+ };
3502
+ if (distribution === "widespread" && totalIssues > 3) return {
3503
+ action: "split-story",
3504
+ rationale: `${totalIssues} issues spread across many files after ${reviewCycles} cycles. Consider splitting into smaller stories.`
3505
+ };
3506
+ return {
3507
+ action: "retry-targeted",
3508
+ rationale: `${totalIssues} issues (${profile}) after ${reviewCycles} cycles. Retry with focused prompt.`
3509
+ };
3510
+ }
3511
+
3427
3512
  //#endregion
3428
3513
  //#region src/modules/compiled-workflows/prompt-assembler.ts
3429
- const logger$12 = createLogger("compiled-workflows:prompt-assembler");
3514
+ const logger$13 = createLogger("compiled-workflows:prompt-assembler");
3430
3515
  /**
3431
3516
  * Assemble a final prompt from a template and sections map.
3432
3517
  *
@@ -3451,7 +3536,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
3451
3536
  tokenCount,
3452
3537
  truncated: false
3453
3538
  };
3454
- logger$12.warn({
3539
+ logger$13.warn({
3455
3540
  tokenCount,
3456
3541
  ceiling: tokenCeiling
3457
3542
  }, "Prompt exceeds token ceiling — truncating optional sections");
@@ -3467,10 +3552,10 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
3467
3552
  const targetSectionTokens = Math.max(0, currentSectionTokens - overBy);
3468
3553
  if (targetSectionTokens === 0) {
3469
3554
  contentMap[section.name] = "";
3470
- logger$12.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
3555
+ logger$13.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
3471
3556
  } else {
3472
3557
  contentMap[section.name] = truncateToTokens(section.content, targetSectionTokens);
3473
- logger$12.warn({
3558
+ logger$13.warn({
3474
3559
  sectionName: section.name,
3475
3560
  targetSectionTokens
3476
3561
  }, "Section truncated to fit token budget");
@@ -3481,7 +3566,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
3481
3566
  }
3482
3567
  if (tokenCount <= tokenCeiling) break;
3483
3568
  }
3484
- if (tokenCount > tokenCeiling) logger$12.warn({
3569
+ if (tokenCount > tokenCeiling) logger$13.warn({
3485
3570
  tokenCount,
3486
3571
  ceiling: tokenCeiling
3487
3572
  }, "Required sections alone exceed token ceiling — returning over-budget prompt");
@@ -3622,7 +3707,7 @@ const CodeReviewResultSchema = z.object({
3622
3707
 
3623
3708
  //#endregion
3624
3709
  //#region src/modules/compiled-workflows/create-story.ts
3625
- const logger$11 = createLogger("compiled-workflows:create-story");
3710
+ const logger$12 = createLogger("compiled-workflows:create-story");
3626
3711
  /**
3627
3712
  * Hard ceiling for the assembled create-story prompt.
3628
3713
  */
@@ -3646,7 +3731,7 @@ const TOKEN_CEILING$2 = 3e3;
3646
3731
  */
3647
3732
  async function runCreateStory(deps, params) {
3648
3733
  const { epicId, storyKey, pipelineRunId } = params;
3649
- logger$11.debug({
3734
+ logger$12.debug({
3650
3735
  epicId,
3651
3736
  storyKey,
3652
3737
  pipelineRunId
@@ -3656,7 +3741,7 @@ async function runCreateStory(deps, params) {
3656
3741
  template = await deps.pack.getPrompt("create-story");
3657
3742
  } catch (err) {
3658
3743
  const error = err instanceof Error ? err.message : String(err);
3659
- logger$11.error({ error }, "Failed to retrieve create-story prompt template");
3744
+ logger$12.error({ error }, "Failed to retrieve create-story prompt template");
3660
3745
  return {
3661
3746
  result: "failed",
3662
3747
  error: `Failed to retrieve prompt template: ${error}`,
@@ -3698,7 +3783,7 @@ async function runCreateStory(deps, params) {
3698
3783
  priority: "important"
3699
3784
  }
3700
3785
  ], TOKEN_CEILING$2);
3701
- logger$11.debug({
3786
+ logger$12.debug({
3702
3787
  tokenCount,
3703
3788
  truncated,
3704
3789
  tokenCeiling: TOKEN_CEILING$2
@@ -3715,7 +3800,7 @@ async function runCreateStory(deps, params) {
3715
3800
  dispatchResult = await handle.result;
3716
3801
  } catch (err) {
3717
3802
  const error = err instanceof Error ? err.message : String(err);
3718
- logger$11.error({
3803
+ logger$12.error({
3719
3804
  epicId,
3720
3805
  storyKey,
3721
3806
  error
@@ -3736,7 +3821,7 @@ async function runCreateStory(deps, params) {
3736
3821
  if (dispatchResult.status === "failed") {
3737
3822
  const errorMsg = dispatchResult.parseError ?? `Dispatch failed with exit code ${dispatchResult.exitCode}`;
3738
3823
  const stderrDetail = dispatchResult.output ? ` Output: ${dispatchResult.output}` : "";
3739
- logger$11.warn({
3824
+ logger$12.warn({
3740
3825
  epicId,
3741
3826
  storyKey,
3742
3827
  exitCode: dispatchResult.exitCode
@@ -3748,7 +3833,7 @@ async function runCreateStory(deps, params) {
3748
3833
  };
3749
3834
  }
3750
3835
  if (dispatchResult.status === "timeout") {
3751
- logger$11.warn({
3836
+ logger$12.warn({
3752
3837
  epicId,
3753
3838
  storyKey
3754
3839
  }, "Create-story dispatch timed out");
@@ -3761,7 +3846,7 @@ async function runCreateStory(deps, params) {
3761
3846
  if (dispatchResult.parsed === null) {
3762
3847
  const details = dispatchResult.parseError ?? "No YAML block found in output";
3763
3848
  const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
3764
- logger$11.warn({
3849
+ logger$12.warn({
3765
3850
  epicId,
3766
3851
  storyKey,
3767
3852
  details,
@@ -3777,7 +3862,7 @@ async function runCreateStory(deps, params) {
3777
3862
  const parseResult = CreateStoryResultSchema.safeParse(dispatchResult.parsed);
3778
3863
  if (!parseResult.success) {
3779
3864
  const details = parseResult.error.message;
3780
- logger$11.warn({
3865
+ logger$12.warn({
3781
3866
  epicId,
3782
3867
  storyKey,
3783
3868
  details
@@ -3790,7 +3875,7 @@ async function runCreateStory(deps, params) {
3790
3875
  };
3791
3876
  }
3792
3877
  const parsed = parseResult.data;
3793
- logger$11.info({
3878
+ logger$12.info({
3794
3879
  epicId,
3795
3880
  storyKey,
3796
3881
  storyFile: parsed.story_file,
@@ -3812,7 +3897,7 @@ function getImplementationDecisions(deps) {
3812
3897
  try {
3813
3898
  return getDecisionsByPhase(deps.db, "implementation");
3814
3899
  } catch (err) {
3815
- logger$11.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
3900
+ logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
3816
3901
  return [];
3817
3902
  }
3818
3903
  }
@@ -3828,13 +3913,13 @@ function getEpicShard(decisions, epicId, projectRoot) {
3828
3913
  if (projectRoot) {
3829
3914
  const fallback = readEpicShardFromFile(projectRoot, epicId);
3830
3915
  if (fallback) {
3831
- logger$11.info({ epicId }, "Using file-based fallback for epic shard (decisions table empty)");
3916
+ logger$12.info({ epicId }, "Using file-based fallback for epic shard (decisions table empty)");
3832
3917
  return fallback;
3833
3918
  }
3834
3919
  }
3835
3920
  return "";
3836
3921
  } catch (err) {
3837
- logger$11.warn({
3922
+ logger$12.warn({
3838
3923
  epicId,
3839
3924
  error: err instanceof Error ? err.message : String(err)
3840
3925
  }, "Failed to retrieve epic shard");
@@ -3851,7 +3936,7 @@ function getPrevDevNotes(decisions, epicId) {
3851
3936
  if (devNotes.length === 0) return "";
3852
3937
  return devNotes[devNotes.length - 1].value;
3853
3938
  } catch (err) {
3854
- logger$11.warn({
3939
+ logger$12.warn({
3855
3940
  epicId,
3856
3941
  error: err instanceof Error ? err.message : String(err)
3857
3942
  }, "Failed to retrieve prev dev notes");
@@ -3871,13 +3956,13 @@ function getArchConstraints$1(deps) {
3871
3956
  if (deps.projectRoot) {
3872
3957
  const fallback = readArchConstraintsFromFile(deps.projectRoot);
3873
3958
  if (fallback) {
3874
- logger$11.info("Using file-based fallback for architecture constraints (decisions table empty)");
3959
+ logger$12.info("Using file-based fallback for architecture constraints (decisions table empty)");
3875
3960
  return fallback;
3876
3961
  }
3877
3962
  }
3878
3963
  return "";
3879
3964
  } catch (err) {
3880
- logger$11.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
3965
+ logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
3881
3966
  return "";
3882
3967
  }
3883
3968
  }
@@ -3897,7 +3982,7 @@ function readEpicShardFromFile(projectRoot, epicId) {
3897
3982
  const match = pattern.exec(content);
3898
3983
  return match ? match[0].trim() : "";
3899
3984
  } catch (err) {
3900
- logger$11.warn({
3985
+ logger$12.warn({
3901
3986
  epicId,
3902
3987
  error: err instanceof Error ? err.message : String(err)
3903
3988
  }, "File-based epic shard fallback failed");
@@ -3920,7 +4005,7 @@ function readArchConstraintsFromFile(projectRoot) {
3920
4005
  const content = readFileSync$1(archPath, "utf-8");
3921
4006
  return content.slice(0, 1500);
3922
4007
  } catch (err) {
3923
- logger$11.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
4008
+ logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
3924
4009
  return "";
3925
4010
  }
3926
4011
  }
@@ -3933,14 +4018,14 @@ async function getStoryTemplate(deps) {
3933
4018
  try {
3934
4019
  return await deps.pack.getTemplate("story");
3935
4020
  } catch (err) {
3936
- logger$11.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
4021
+ logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
3937
4022
  return "";
3938
4023
  }
3939
4024
  }
3940
4025
 
3941
4026
  //#endregion
3942
4027
  //#region src/modules/compiled-workflows/git-helpers.ts
3943
- const logger$10 = createLogger("compiled-workflows:git-helpers");
4028
+ const logger$11 = createLogger("compiled-workflows:git-helpers");
3944
4029
  /**
3945
4030
  * Capture the full git diff for HEAD (working tree vs current commit).
3946
4031
  *
@@ -4064,7 +4149,7 @@ async function runGitCommand(args, cwd, logLabel) {
4064
4149
  stderr += chunk.toString("utf-8");
4065
4150
  });
4066
4151
  proc.on("error", (err) => {
4067
- logger$10.warn({
4152
+ logger$11.warn({
4068
4153
  label: logLabel,
4069
4154
  cwd,
4070
4155
  error: err.message
@@ -4073,7 +4158,7 @@ async function runGitCommand(args, cwd, logLabel) {
4073
4158
  });
4074
4159
  proc.on("close", (code) => {
4075
4160
  if (code !== 0) {
4076
- logger$10.warn({
4161
+ logger$11.warn({
4077
4162
  label: logLabel,
4078
4163
  cwd,
4079
4164
  code,
@@ -4087,6 +4172,84 @@ async function runGitCommand(args, cwd, logLabel) {
4087
4172
  });
4088
4173
  }
4089
4174
 
4175
+ //#endregion
4176
+ //#region src/modules/implementation-orchestrator/project-findings.ts
4177
+ const logger$10 = createLogger("project-findings");
4178
+ /** Maximum character length for the findings summary */
4179
+ const MAX_CHARS = 2e3;
4180
+ /**
4181
+ * Query the decision store for prior project findings and return a formatted
4182
+ * markdown summary suitable for prompt injection.
4183
+ *
4184
+ * Returns an empty string if no findings exist (AC5: graceful fallback).
4185
+ */
4186
+ function getProjectFindings(db) {
4187
+ try {
4188
+ const outcomes = getDecisionsByCategory(db, STORY_OUTCOME);
4189
+ const operational = getDecisionsByCategory(db, OPERATIONAL_FINDING);
4190
+ const metrics = getDecisionsByCategory(db, STORY_METRICS);
4191
+ const diagnoses = getDecisionsByCategory(db, ESCALATION_DIAGNOSIS);
4192
+ if (outcomes.length === 0 && operational.length === 0 && metrics.length === 0 && diagnoses.length === 0) return "";
4193
+ const sections = [];
4194
+ if (outcomes.length > 0) {
4195
+ const patterns = extractRecurringPatterns(outcomes);
4196
+ if (patterns.length > 0) {
4197
+ sections.push("**Recurring patterns from prior runs:**");
4198
+ for (const p of patterns) sections.push(`- ${p}`);
4199
+ }
4200
+ }
4201
+ if (diagnoses.length > 0) {
4202
+ sections.push("**Prior escalations:**");
4203
+ for (const d of diagnoses.slice(-3)) try {
4204
+ const val = JSON.parse(d.value);
4205
+ sections.push(`- ${d.key.split(":")[0]}: ${val.recommendedAction} — ${val.rationale}`);
4206
+ } catch {
4207
+ sections.push(`- ${d.key}: escalated`);
4208
+ }
4209
+ }
4210
+ const highCycleStories = metrics.filter((m) => {
4211
+ try {
4212
+ const val = JSON.parse(m.value);
4213
+ return val.review_cycles >= 2;
4214
+ } catch {
4215
+ return false;
4216
+ }
4217
+ }).slice(-5);
4218
+ if (highCycleStories.length > 0) {
4219
+ sections.push("**Stories with high review cycles:**");
4220
+ for (const m of highCycleStories) try {
4221
+ const val = JSON.parse(m.value);
4222
+ sections.push(`- ${m.key.split(":")[0]}: ${val.review_cycles} cycles`);
4223
+ } catch {}
4224
+ }
4225
+ const stalls = operational.filter((o) => o.key.startsWith("stall:"));
4226
+ if (stalls.length > 0) sections.push(`**Prior stalls:** ${stalls.length} stall event(s) recorded`);
4227
+ if (sections.length === 0) return "";
4228
+ let summary = sections.join("\n");
4229
+ if (summary.length > MAX_CHARS) summary = summary.slice(0, MAX_CHARS - 3) + "...";
4230
+ return summary;
4231
+ } catch (err) {
4232
+ logger$10.warn({ err }, "Failed to query project findings (graceful fallback)");
4233
+ return "";
4234
+ }
4235
+ }
4236
+ /**
4237
+ * Extract recurring patterns from story-outcome decisions.
4238
+ *
4239
+ * Looks for patterns that appear across multiple story outcomes
4240
+ * (e.g., "missing error handling" flagged in 3/5 stories).
4241
+ */
4242
+ function extractRecurringPatterns(outcomes) {
4243
+ const patternCounts = new Map();
4244
+ for (const o of outcomes) try {
4245
+ const val = JSON.parse(o.value);
4246
+ if (Array.isArray(val.recurringPatterns)) {
4247
+ for (const pattern of val.recurringPatterns) if (typeof pattern === "string") patternCounts.set(pattern, (patternCounts.get(pattern) ?? 0) + 1);
4248
+ }
4249
+ } catch {}
4250
+ return [...patternCounts.entries()].filter(([, count]) => count >= 2).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => `${pattern} (${count} occurrences)`);
4251
+ }
4252
+
4090
4253
  //#endregion
4091
4254
  //#region src/modules/compiled-workflows/dev-story.ts
4092
4255
  const logger$9 = createLogger("compiled-workflows:dev-story");
@@ -4217,6 +4380,17 @@ async function runDevStory(deps, params) {
4217
4380
  const priorFilesContent = priorFiles !== void 0 && priorFiles.length > 0 ? `## Files Modified by Previous Batches\n\nThe following files were created or modified by prior batch dispatches. Review them for context before implementing:\n\n${priorFiles.map((f) => `- ${f}`).join("\n")}` : "";
4218
4381
  const filesInScopeContent = extractFilesInScope(storyContent);
4219
4382
  const projectContextContent = deps.projectRoot ? await buildProjectContext(storyContent, deps.projectRoot) : "";
4383
+ let priorFindingsContent = "";
4384
+ try {
4385
+ const findings = getProjectFindings(deps.db);
4386
+ if (findings.length > 0) {
4387
+ priorFindingsContent = "Previous pipeline runs encountered these issues — avoid repeating them:\n\n" + findings;
4388
+ logger$9.debug({
4389
+ storyKey,
4390
+ findingsLen: findings.length
4391
+ }, "Injecting prior findings into dev-story prompt");
4392
+ }
4393
+ } catch {}
4220
4394
  const sections = [
4221
4395
  {
4222
4396
  name: "story_content",
@@ -4247,6 +4421,11 @@ async function runDevStory(deps, params) {
4247
4421
  name: "test_patterns",
4248
4422
  content: testPatternsContent,
4249
4423
  priority: "optional"
4424
+ },
4425
+ {
4426
+ name: "prior_findings",
4427
+ content: priorFindingsContent,
4428
+ priority: "optional"
4250
4429
  }
4251
4430
  ];
4252
4431
  const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING$1);
@@ -4599,6 +4778,17 @@ async function runCodeReview(deps, params) {
4599
4778
  "",
4600
4779
  ...previousIssues.map((iss, i) => ` ${i + 1}. [${iss.severity ?? "unknown"}] ${iss.description ?? "no description"}${iss.file ? ` (${iss.file}${iss.line ? `:${iss.line}` : ""})` : ""}`)
4601
4780
  ].join("\n");
4781
+ let priorFindingsContent = "";
4782
+ try {
4783
+ const findings = getProjectFindings(deps.db);
4784
+ if (findings.length > 0) {
4785
+ priorFindingsContent = "Previous reviews found these recurring patterns — pay special attention:\n\n" + findings;
4786
+ logger$8.debug({
4787
+ storyKey,
4788
+ findingsLen: findings.length
4789
+ }, "Injecting prior findings into code-review prompt");
4790
+ }
4791
+ } catch {}
4602
4792
  const sections = [
4603
4793
  {
4604
4794
  name: "story_content",
@@ -4619,6 +4809,11 @@ async function runCodeReview(deps, params) {
4619
4809
  name: "arch_constraints",
4620
4810
  content: archConstraintsContent,
4621
4811
  priority: "optional"
4812
+ },
4813
+ {
4814
+ name: "prior_findings",
4815
+ content: priorFindingsContent,
4816
+ priority: "optional"
4622
4817
  }
4623
4818
  ];
4624
4819
  const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
@@ -5397,7 +5592,7 @@ function createPauseGate() {
5397
5592
  */
5398
5593
  function createImplementationOrchestrator(deps) {
5399
5594
  const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot } = deps;
5400
- const logger$17 = createLogger("implementation-orchestrator");
5595
+ const logger$18 = createLogger("implementation-orchestrator");
5401
5596
  let _state = "IDLE";
5402
5597
  let _startedAt;
5403
5598
  let _completedAt;
@@ -5434,7 +5629,7 @@ function createImplementationOrchestrator(deps) {
5434
5629
  const nowMs = Date.now();
5435
5630
  for (const [phase, startMs] of starts) {
5436
5631
  const endMs = ends?.get(phase);
5437
- if (endMs === void 0) logger$17.warn({
5632
+ if (endMs === void 0) logger$18.warn({
5438
5633
  storyKey,
5439
5634
  phase
5440
5635
  }, "Phase has no end time — story may have errored mid-phase. Duration capped to now() and may be inflated.");
@@ -5481,18 +5676,87 @@ function createImplementationOrchestrator(deps) {
5481
5676
  rationale: `Story ${storyKey} completed with result=${result} in ${wallClockSeconds}s. Tokens: ${tokenAgg.input}+${tokenAgg.output}. Review cycles: ${reviewCycles}.`
5482
5677
  });
5483
5678
  } catch (decisionErr) {
5484
- logger$17.warn({
5679
+ logger$18.warn({
5485
5680
  err: decisionErr,
5486
5681
  storyKey
5487
5682
  }, "Failed to write story-metrics decision (best-effort)");
5488
5683
  }
5489
5684
  } catch (err) {
5490
- logger$17.warn({
5685
+ logger$18.warn({
5491
5686
  err,
5492
5687
  storyKey
5493
5688
  }, "Failed to write story metrics (best-effort)");
5494
5689
  }
5495
5690
  }
5691
+ /**
5692
+ * Persist a story outcome finding to the decision store (Story 22-1, AC4).
5693
+ *
5694
+ * Records outcome, review cycles, and any recurring issue patterns for
5695
+ * future prompt injection via the learning loop.
5696
+ */
5697
+ function writeStoryOutcomeBestEffort(storyKey, outcome, reviewCycles, issuePatterns) {
5698
+ if (config.pipelineRunId === void 0) return;
5699
+ try {
5700
+ createDecision(db, {
5701
+ pipeline_run_id: config.pipelineRunId,
5702
+ phase: "implementation",
5703
+ category: STORY_OUTCOME,
5704
+ key: `${storyKey}:${config.pipelineRunId}`,
5705
+ value: JSON.stringify({
5706
+ storyKey,
5707
+ outcome,
5708
+ reviewCycles,
5709
+ recurringPatterns: issuePatterns ?? []
5710
+ }),
5711
+ rationale: `Story ${storyKey} ${outcome} after ${reviewCycles} review cycle(s).`
5712
+ });
5713
+ } catch (err) {
5714
+ logger$18.warn({
5715
+ err,
5716
+ storyKey
5717
+ }, "Failed to write story-outcome decision (best-effort)");
5718
+ }
5719
+ }
5720
+ /**
5721
+ * Emit an escalation event with structured diagnosis and persist the
5722
+ * diagnosis to the decision store (Story 22-3).
5723
+ */
5724
+ function emitEscalation(payload) {
5725
+ const diagnosis = generateEscalationDiagnosis(payload.issues, payload.reviewCycles, payload.lastVerdict);
5726
+ eventBus.emit("orchestrator:story-escalated", {
5727
+ ...payload,
5728
+ diagnosis
5729
+ });
5730
+ if (config.pipelineRunId !== void 0) try {
5731
+ createDecision(db, {
5732
+ pipeline_run_id: config.pipelineRunId,
5733
+ phase: "implementation",
5734
+ category: ESCALATION_DIAGNOSIS,
5735
+ key: `${payload.storyKey}:${config.pipelineRunId}`,
5736
+ value: JSON.stringify(diagnosis),
5737
+ rationale: `Escalation diagnosis for ${payload.storyKey}: ${diagnosis.recommendedAction} — ${diagnosis.rationale}`
5738
+ });
5739
+ } catch (err) {
5740
+ logger$18.warn({
5741
+ err,
5742
+ storyKey: payload.storyKey
5743
+ }, "Failed to persist escalation diagnosis (best-effort)");
5744
+ }
5745
+ const issuePatterns = extractIssuePatterns(payload.issues);
5746
+ writeStoryOutcomeBestEffort(payload.storyKey, "escalated", payload.reviewCycles, issuePatterns);
5747
+ }
5748
+ /**
5749
+ * Extract short pattern descriptions from an issue list for recurring pattern tracking.
5750
+ */
5751
+ function extractIssuePatterns(issues) {
5752
+ const patterns = [];
5753
+ for (const issue of issues) if (typeof issue === "string") patterns.push(issue.slice(0, 100));
5754
+ else {
5755
+ const iss = issue;
5756
+ if (iss.description && (iss.severity === "blocker" || iss.severity === "major")) patterns.push(iss.description.slice(0, 100));
5757
+ }
5758
+ return patterns.slice(0, 10);
5759
+ }
5496
5760
  function getStatus() {
5497
5761
  const stories = {};
5498
5762
  for (const [key, s] of _stories) stories[key] = { ...s };
@@ -5523,7 +5787,7 @@ function createImplementationOrchestrator(deps) {
5523
5787
  token_usage_json: serialized
5524
5788
  });
5525
5789
  } catch (err) {
5526
- logger$17.warn("Failed to persist orchestrator state", { err });
5790
+ logger$18.warn("Failed to persist orchestrator state", { err });
5527
5791
  }
5528
5792
  }
5529
5793
  function recordProgress() {
@@ -5553,7 +5817,7 @@ function createImplementationOrchestrator(deps) {
5553
5817
  if (_stalledStories.has(key)) continue;
5554
5818
  _stalledStories.add(key);
5555
5819
  _storiesWithStall.add(key);
5556
- logger$17.warn({
5820
+ logger$18.warn({
5557
5821
  storyKey: key,
5558
5822
  phase: s.phase,
5559
5823
  elapsedMs: elapsed
@@ -5590,7 +5854,7 @@ function createImplementationOrchestrator(deps) {
5590
5854
  * exhausted retries the story is ESCALATED.
5591
5855
  */
5592
5856
  async function processStory(storyKey) {
5593
- logger$17.info("Processing story", { storyKey });
5857
+ logger$18.info("Processing story", { storyKey });
5594
5858
  await waitIfPaused();
5595
5859
  if (_state !== "RUNNING") return;
5596
5860
  startPhase(storyKey, "create-story");
@@ -5605,7 +5869,7 @@ function createImplementationOrchestrator(deps) {
5605
5869
  const match = files.find((f) => f.startsWith(`${storyKey}-`) && f.endsWith(".md"));
5606
5870
  if (match) {
5607
5871
  storyFilePath = join$1(artifactsDir, match);
5608
- logger$17.info({
5872
+ logger$18.info({
5609
5873
  storyKey,
5610
5874
  storyFilePath
5611
5875
  }, "Found existing story file — skipping create-story");
@@ -5650,7 +5914,7 @@ function createImplementationOrchestrator(deps) {
5650
5914
  completedAt: new Date().toISOString()
5651
5915
  });
5652
5916
  writeStoryMetricsBestEffort(storyKey, "failed", 0);
5653
- eventBus.emit("orchestrator:story-escalated", {
5917
+ emitEscalation({
5654
5918
  storyKey,
5655
5919
  lastVerdict: "create-story-failed",
5656
5920
  reviewCycles: 0,
@@ -5667,7 +5931,7 @@ function createImplementationOrchestrator(deps) {
5667
5931
  completedAt: new Date().toISOString()
5668
5932
  });
5669
5933
  writeStoryMetricsBestEffort(storyKey, "failed", 0);
5670
- eventBus.emit("orchestrator:story-escalated", {
5934
+ emitEscalation({
5671
5935
  storyKey,
5672
5936
  lastVerdict: "create-story-no-file",
5673
5937
  reviewCycles: 0,
@@ -5686,7 +5950,7 @@ function createImplementationOrchestrator(deps) {
5686
5950
  completedAt: new Date().toISOString()
5687
5951
  });
5688
5952
  writeStoryMetricsBestEffort(storyKey, "failed", 0);
5689
- eventBus.emit("orchestrator:story-escalated", {
5953
+ emitEscalation({
5690
5954
  storyKey,
5691
5955
  lastVerdict: "create-story-exception",
5692
5956
  reviewCycles: 0,
@@ -5707,7 +5971,7 @@ function createImplementationOrchestrator(deps) {
5707
5971
  try {
5708
5972
  storyContentForAnalysis = await readFile$1(storyFilePath ?? "", "utf-8");
5709
5973
  } catch (err) {
5710
- logger$17.error({
5974
+ logger$18.error({
5711
5975
  storyKey,
5712
5976
  storyFilePath,
5713
5977
  error: err instanceof Error ? err.message : String(err)
@@ -5715,7 +5979,7 @@ function createImplementationOrchestrator(deps) {
5715
5979
  }
5716
5980
  const analysis = analyzeStoryComplexity(storyContentForAnalysis);
5717
5981
  const batches = planTaskBatches(analysis);
5718
- logger$17.info({
5982
+ logger$18.info({
5719
5983
  storyKey,
5720
5984
  estimatedScope: analysis.estimatedScope,
5721
5985
  batchCount: batches.length,
@@ -5733,7 +5997,7 @@ function createImplementationOrchestrator(deps) {
5733
5997
  if (_state !== "RUNNING") break;
5734
5998
  const taskScope = batch.taskIds.map((id, i) => `T${id}: ${batch.taskTitles[i] ?? ""}`).join("\n");
5735
5999
  const priorFiles = allFilesModified.size > 0 ? Array.from(allFilesModified) : void 0;
5736
- logger$17.info({
6000
+ logger$18.info({
5737
6001
  storyKey,
5738
6002
  batchIndex: batch.batchIndex,
5739
6003
  taskCount: batch.taskIds.length
@@ -5757,7 +6021,7 @@ function createImplementationOrchestrator(deps) {
5757
6021
  });
5758
6022
  } catch (batchErr) {
5759
6023
  const errMsg = batchErr instanceof Error ? batchErr.message : String(batchErr);
5760
- logger$17.warn({
6024
+ logger$18.warn({
5761
6025
  storyKey,
5762
6026
  batchIndex: batch.batchIndex,
5763
6027
  error: errMsg
@@ -5777,7 +6041,7 @@ function createImplementationOrchestrator(deps) {
5777
6041
  filesModified: batchFilesModified,
5778
6042
  result: batchResult.result === "success" ? "success" : "failed"
5779
6043
  };
5780
- logger$17.info(batchMetrics, "Batch dev-story metrics");
6044
+ logger$18.info(batchMetrics, "Batch dev-story metrics");
5781
6045
  for (const f of batchFilesModified) allFilesModified.add(f);
5782
6046
  if (batchFilesModified.length > 0) batchFileGroups.push({
5783
6047
  batchIndex: batch.batchIndex,
@@ -5799,13 +6063,13 @@ function createImplementationOrchestrator(deps) {
5799
6063
  })
5800
6064
  });
5801
6065
  } catch (tokenErr) {
5802
- logger$17.warn({
6066
+ logger$18.warn({
5803
6067
  storyKey,
5804
6068
  batchIndex: batch.batchIndex,
5805
6069
  err: tokenErr
5806
6070
  }, "Failed to record batch token usage");
5807
6071
  }
5808
- if (batchResult.result === "failed") logger$17.warn({
6072
+ if (batchResult.result === "failed") logger$18.warn({
5809
6073
  storyKey,
5810
6074
  batchIndex: batch.batchIndex,
5811
6075
  error: batchResult.error
@@ -5838,7 +6102,7 @@ function createImplementationOrchestrator(deps) {
5838
6102
  result: devResult
5839
6103
  });
5840
6104
  persistState();
5841
- if (devResult.result === "failed") logger$17.warn("Dev-story reported failure, proceeding to code review", {
6105
+ if (devResult.result === "failed") logger$18.warn("Dev-story reported failure, proceeding to code review", {
5842
6106
  storyKey,
5843
6107
  error: devResult.error,
5844
6108
  filesModified: devFilesModified.length
@@ -5853,7 +6117,7 @@ function createImplementationOrchestrator(deps) {
5853
6117
  completedAt: new Date().toISOString()
5854
6118
  });
5855
6119
  writeStoryMetricsBestEffort(storyKey, "failed", 0);
5856
- eventBus.emit("orchestrator:story-escalated", {
6120
+ emitEscalation({
5857
6121
  storyKey,
5858
6122
  lastVerdict: "dev-story-exception",
5859
6123
  reviewCycles: 0,
@@ -5895,7 +6159,7 @@ function createImplementationOrchestrator(deps) {
5895
6159
  "NEEDS_MAJOR_REWORK": 2
5896
6160
  };
5897
6161
  for (const group of batchFileGroups) {
5898
- logger$17.info({
6162
+ logger$18.info({
5899
6163
  storyKey,
5900
6164
  batchIndex: group.batchIndex,
5901
6165
  fileCount: group.files.length
@@ -5932,7 +6196,7 @@ function createImplementationOrchestrator(deps) {
5932
6196
  rawOutput: lastRawOutput,
5933
6197
  tokenUsage: aggregateTokens
5934
6198
  };
5935
- logger$17.info({
6199
+ logger$18.info({
5936
6200
  storyKey,
5937
6201
  batchCount: batchFileGroups.length,
5938
6202
  verdict: worstVerdict,
@@ -5958,7 +6222,7 @@ function createImplementationOrchestrator(deps) {
5958
6222
  const isPhantomReview = reviewResult.verdict !== "SHIP_IT" && (reviewResult.issue_list === void 0 || reviewResult.issue_list.length === 0) && reviewResult.error !== void 0;
5959
6223
  if (isPhantomReview && !timeoutRetried) {
5960
6224
  timeoutRetried = true;
5961
- logger$17.warn({
6225
+ logger$18.warn({
5962
6226
  storyKey,
5963
6227
  reviewCycles,
5964
6228
  error: reviewResult.error
@@ -5968,7 +6232,7 @@ function createImplementationOrchestrator(deps) {
5968
6232
  verdict = reviewResult.verdict;
5969
6233
  issueList = reviewResult.issue_list ?? [];
5970
6234
  if (verdict === "NEEDS_MAJOR_REWORK" && reviewCycles > 0 && previousIssueList.length > 0 && issueList.length < previousIssueList.length) {
5971
- logger$17.info({
6235
+ logger$18.info({
5972
6236
  storyKey,
5973
6237
  originalVerdict: verdict,
5974
6238
  issuesBefore: previousIssueList.length,
@@ -6004,7 +6268,7 @@ function createImplementationOrchestrator(deps) {
6004
6268
  if (_decomposition !== void 0) parts.push(`decomposed: ${_decomposition.batchCount} batches`);
6005
6269
  parts.push(`${fileCount} files`);
6006
6270
  parts.push(`${totalTokensK} tokens`);
6007
- logger$17.info({
6271
+ logger$18.info({
6008
6272
  storyKey,
6009
6273
  verdict,
6010
6274
  agentVerdict: reviewResult.agentVerdict
@@ -6019,7 +6283,7 @@ function createImplementationOrchestrator(deps) {
6019
6283
  completedAt: new Date().toISOString()
6020
6284
  });
6021
6285
  writeStoryMetricsBestEffort(storyKey, "failed", reviewCycles);
6022
- eventBus.emit("orchestrator:story-escalated", {
6286
+ emitEscalation({
6023
6287
  storyKey,
6024
6288
  lastVerdict: "code-review-exception",
6025
6289
  reviewCycles,
@@ -6035,6 +6299,7 @@ function createImplementationOrchestrator(deps) {
6035
6299
  completedAt: new Date().toISOString()
6036
6300
  });
6037
6301
  writeStoryMetricsBestEffort(storyKey, "success", reviewCycles + 1);
6302
+ writeStoryOutcomeBestEffort(storyKey, "complete", reviewCycles + 1);
6038
6303
  eventBus.emit("orchestrator:story-complete", {
6039
6304
  storyKey,
6040
6305
  reviewCycles
@@ -6053,7 +6318,7 @@ function createImplementationOrchestrator(deps) {
6053
6318
  completedAt: new Date().toISOString()
6054
6319
  });
6055
6320
  writeStoryMetricsBestEffort(storyKey, "escalated", finalReviewCycles);
6056
- eventBus.emit("orchestrator:story-escalated", {
6321
+ emitEscalation({
6057
6322
  storyKey,
6058
6323
  lastVerdict: verdict,
6059
6324
  reviewCycles: finalReviewCycles,
@@ -6062,7 +6327,7 @@ function createImplementationOrchestrator(deps) {
6062
6327
  persistState();
6063
6328
  return;
6064
6329
  }
6065
- logger$17.info({
6330
+ logger$18.info({
6066
6331
  storyKey,
6067
6332
  reviewCycles: finalReviewCycles,
6068
6333
  issueCount: issueList.length
@@ -6112,7 +6377,7 @@ function createImplementationOrchestrator(deps) {
6112
6377
  fixPrompt = assembled.prompt;
6113
6378
  } catch {
6114
6379
  fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, minor fixes needed`;
6115
- logger$17.warn("Failed to assemble auto-approve fix prompt, using fallback", { storyKey });
6380
+ logger$18.warn("Failed to assemble auto-approve fix prompt, using fallback", { storyKey });
6116
6381
  }
6117
6382
  const handle = dispatcher.dispatch({
6118
6383
  prompt: fixPrompt,
@@ -6129,9 +6394,9 @@ function createImplementationOrchestrator(deps) {
6129
6394
  output: fixResult.tokenEstimate.output
6130
6395
  } : void 0 }
6131
6396
  });
6132
- if (fixResult.status === "timeout") logger$17.warn("Auto-approve fix timed out — approving anyway (issues were minor)", { storyKey });
6397
+ if (fixResult.status === "timeout") logger$18.warn("Auto-approve fix timed out — approving anyway (issues were minor)", { storyKey });
6133
6398
  } catch (err) {
6134
- logger$17.warn("Auto-approve fix dispatch failed — approving anyway (issues were minor)", {
6399
+ logger$18.warn("Auto-approve fix dispatch failed — approving anyway (issues were minor)", {
6135
6400
  storyKey,
6136
6401
  err
6137
6402
  });
@@ -6143,6 +6408,7 @@ function createImplementationOrchestrator(deps) {
6143
6408
  completedAt: new Date().toISOString()
6144
6409
  });
6145
6410
  writeStoryMetricsBestEffort(storyKey, "success", finalReviewCycles);
6411
+ writeStoryOutcomeBestEffort(storyKey, "complete", finalReviewCycles);
6146
6412
  eventBus.emit("orchestrator:story-complete", {
6147
6413
  storyKey,
6148
6414
  reviewCycles: finalReviewCycles
@@ -6203,7 +6469,7 @@ function createImplementationOrchestrator(deps) {
6203
6469
  fixPrompt = assembled.prompt;
6204
6470
  } catch {
6205
6471
  fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, taskType=${taskType}`;
6206
- logger$17.warn("Failed to assemble fix prompt, using fallback", {
6472
+ logger$18.warn("Failed to assemble fix prompt, using fallback", {
6207
6473
  storyKey,
6208
6474
  taskType
6209
6475
  });
@@ -6226,7 +6492,7 @@ function createImplementationOrchestrator(deps) {
6226
6492
  } : void 0 }
6227
6493
  });
6228
6494
  if (fixResult.status === "timeout") {
6229
- logger$17.warn("Fix dispatch timed out — escalating story", {
6495
+ logger$18.warn("Fix dispatch timed out — escalating story", {
6230
6496
  storyKey,
6231
6497
  taskType
6232
6498
  });
@@ -6237,7 +6503,7 @@ function createImplementationOrchestrator(deps) {
6237
6503
  completedAt: new Date().toISOString()
6238
6504
  });
6239
6505
  writeStoryMetricsBestEffort(storyKey, "escalated", reviewCycles + 1);
6240
- eventBus.emit("orchestrator:story-escalated", {
6506
+ emitEscalation({
6241
6507
  storyKey,
6242
6508
  lastVerdict: verdict,
6243
6509
  reviewCycles: reviewCycles + 1,
@@ -6246,13 +6512,13 @@ function createImplementationOrchestrator(deps) {
6246
6512
  persistState();
6247
6513
  return;
6248
6514
  }
6249
- if (fixResult.status === "failed") logger$17.warn("Fix dispatch failed", {
6515
+ if (fixResult.status === "failed") logger$18.warn("Fix dispatch failed", {
6250
6516
  storyKey,
6251
6517
  taskType,
6252
6518
  exitCode: fixResult.exitCode
6253
6519
  });
6254
6520
  } catch (err) {
6255
- logger$17.warn("Fix dispatch failed, continuing to next review", {
6521
+ logger$18.warn("Fix dispatch failed, continuing to next review", {
6256
6522
  storyKey,
6257
6523
  taskType,
6258
6524
  err
@@ -6306,11 +6572,11 @@ function createImplementationOrchestrator(deps) {
6306
6572
  }
6307
6573
  async function run(storyKeys) {
6308
6574
  if (_state === "RUNNING" || _state === "PAUSED") {
6309
- logger$17.warn("run() called while orchestrator is already running or paused — ignoring", { state: _state });
6575
+ logger$18.warn("run() called while orchestrator is already running or paused — ignoring", { state: _state });
6310
6576
  return getStatus();
6311
6577
  }
6312
6578
  if (_state === "COMPLETE") {
6313
- logger$17.warn("run() called on a COMPLETE orchestrator — ignoring", { state: _state });
6579
+ logger$18.warn("run() called on a COMPLETE orchestrator — ignoring", { state: _state });
6314
6580
  return getStatus();
6315
6581
  }
6316
6582
  _state = "RUNNING";
@@ -6328,13 +6594,13 @@ function createImplementationOrchestrator(deps) {
6328
6594
  if (config.enableHeartbeat) startHeartbeat();
6329
6595
  if (projectRoot !== void 0) {
6330
6596
  const seedResult = seedMethodologyContext(db, projectRoot);
6331
- if (seedResult.decisionsCreated > 0) logger$17.info({
6597
+ if (seedResult.decisionsCreated > 0) logger$18.info({
6332
6598
  decisionsCreated: seedResult.decisionsCreated,
6333
6599
  skippedCategories: seedResult.skippedCategories
6334
6600
  }, "Methodology context seeded from planning artifacts");
6335
6601
  }
6336
6602
  const groups = detectConflictGroups(storyKeys);
6337
- logger$17.info("Orchestrator starting", {
6603
+ logger$18.info("Orchestrator starting", {
6338
6604
  storyCount: storyKeys.length,
6339
6605
  groupCount: groups.length,
6340
6606
  maxConcurrency: config.maxConcurrency
@@ -6346,7 +6612,7 @@ function createImplementationOrchestrator(deps) {
6346
6612
  _state = "FAILED";
6347
6613
  _completedAt = new Date().toISOString();
6348
6614
  persistState();
6349
- logger$17.error("Orchestrator failed with unhandled error", { err });
6615
+ logger$18.error("Orchestrator failed with unhandled error", { err });
6350
6616
  return getStatus();
6351
6617
  }
6352
6618
  stopHeartbeat();
@@ -6373,7 +6639,7 @@ function createImplementationOrchestrator(deps) {
6373
6639
  _pauseGate = createPauseGate();
6374
6640
  _state = "PAUSED";
6375
6641
  eventBus.emit("orchestrator:paused", {});
6376
- logger$17.info("Orchestrator paused");
6642
+ logger$18.info("Orchestrator paused");
6377
6643
  }
6378
6644
  function resume() {
6379
6645
  if (_state !== "PAUSED") return;
@@ -6384,7 +6650,7 @@ function createImplementationOrchestrator(deps) {
6384
6650
  }
6385
6651
  _state = "RUNNING";
6386
6652
  eventBus.emit("orchestrator:resumed", {});
6387
- logger$17.info("Orchestrator resumed");
6653
+ logger$18.info("Orchestrator resumed");
6388
6654
  }
6389
6655
  return {
6390
6656
  run,
@@ -11022,7 +11288,8 @@ async function runRunAction(options) {
11022
11288
  key: payload.storyKey,
11023
11289
  reason: payload.lastVerdict ?? "escalated",
11024
11290
  cycles: payload.reviewCycles ?? 0,
11025
- issues
11291
+ issues,
11292
+ ...payload.diagnosis !== void 0 ? { diagnosis: payload.diagnosis } : {}
11026
11293
  });
11027
11294
  });
11028
11295
  eventBus.on("orchestrator:story-warn", (payload) => {
@@ -11508,4 +11775,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
11508
11775
 
11509
11776
  //#endregion
11510
11777
  export { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
11511
- //# sourceMappingURL=run-mFmS2pw6.js.map
11778
+ //# sourceMappingURL=run-CcWb6Kb-.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.2.19",
3
+ "version": "0.2.20",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -14,6 +14,9 @@
14
14
  ### Architecture Constraints
15
15
  {{arch_constraints}}
16
16
 
17
+ ### Prior Run Findings
18
+ {{prior_findings}}
19
+
17
20
  ---
18
21
 
19
22
  ## Mission
@@ -32,8 +35,9 @@ Adversarial code review. Find what's wrong. Validate story claims against actual
32
35
  - Whether each AC is actually implemented
33
36
  - Whether each `[x]` task is actually done
34
37
 
35
- 3. **Execute adversarial review** across 4 dimensions:
38
+ 3. **Execute adversarial review** across 5 dimensions:
36
39
  - **AC Validation** — Is each acceptance criterion implemented?
40
+ - **AC-to-Test Traceability** — For each AC, identify the specific test file and test function that validates it. If an AC has no corresponding test evidence, flag it as a major issue: "AC{N} has no test evidence". A test "covers" an AC if it directly exercises the behavior described in the criterion — tangential tests do not count.
37
41
  - **Task Audit** — Tasks marked `[x]` that aren't done are BLOCKER issues
38
42
  - **Code Quality** — Security, error handling, edge cases, maintainability
39
43
  - **Test Quality** — Real assertions, not placeholders or skipped tests
@@ -16,6 +16,9 @@
16
16
  ### Test Patterns
17
17
  {{test_patterns}}
18
18
 
19
+ ### Prior Run Findings
20
+ {{prior_findings}}
21
+
19
22
  ---
20
23
 
21
24
  ## Mission
@@ -1,7 +0,0 @@
1
- import { registerRunCommand, runRunAction } from "./run-mFmS2pw6.js";
2
- import "./logger-D2fS2ccL.js";
3
- import "./event-bus-BMxhfxfT.js";
4
- import "./decisions-Dq4cAA2L.js";
5
- import "./operational-CobuCGbM.js";
6
-
7
- export { runRunAction };