substrate-ai 0.5.2 → 0.5.4

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.
@@ -0,0 +1,4 @@
1
+ import "./logger-D2fS2ccL.js";
2
+ import { AdapterRegistry } from "./adapter-registry-CDNPbixE.js";
3
+
4
+ export { AdapterRegistry };
@@ -1,8 +1,10 @@
1
+ import { createLogger } from "./logger-D2fS2ccL.js";
1
2
  import { exec } from "child_process";
2
3
  import { promisify } from "util";
3
4
 
4
5
  //#region src/adapters/claude-adapter.ts
5
6
  const execAsync$2 = promisify(exec);
7
+ const logger = createLogger("claude-adapter");
6
8
  /** Default model used when none is specified */
7
9
  const DEFAULT_MODEL$1 = "claude-sonnet-4-6";
8
10
  /** Approximate characters per token for estimation */
@@ -62,15 +64,21 @@ var ClaudeCodeAdapter = class {
62
64
  buildCommand(prompt, options) {
63
65
  const model = options.model ?? DEFAULT_MODEL$1;
64
66
  const systemPrompt = "You are an autonomous coding agent executing a single pipeline task. Ignore all session startup context, memory notes, and \"Next Up\" indicators. Follow the instructions in the user message exactly. Emit ONLY the YAML output specified in the Output Contract — no other text.";
67
+ const effectiveSystemPrompt = options.optimizationDirectives !== void 0 && options.optimizationDirectives.length > 0 ? `${systemPrompt}\n\n## Optimization Directives\n${options.optimizationDirectives}` : systemPrompt;
68
+ if (options.optimizationDirectives !== void 0 && options.optimizationDirectives.length > 0) logger.debug({
69
+ storyKey: options.storyKey,
70
+ directiveChars: options.optimizationDirectives.length
71
+ }, "Injecting optimization directives into system prompt");
65
72
  const args = [
66
73
  "-p",
67
74
  "--model",
68
75
  model,
69
76
  "--dangerously-skip-permissions",
70
77
  "--system-prompt",
71
- systemPrompt
78
+ effectiveSystemPrompt
72
79
  ];
73
80
  if (options.maxTurns !== void 0) args.push("--max-turns", String(options.maxTurns));
81
+ if (options.maxContextTokens !== void 0) args.push("--max-context-tokens", String(options.maxContextTokens));
74
82
  if (options.additionalFlags && options.additionalFlags.length > 0) args.push(...options.additionalFlags);
75
83
  const envEntries = {};
76
84
  const unsetKeys = ["CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT"];
@@ -825,4 +833,4 @@ var AdapterRegistry = class {
825
833
 
826
834
  //#endregion
827
835
  export { AdapterRegistry, ClaudeCodeAdapter, CodexCLIAdapter, GeminiCLIAdapter };
828
- //# sourceMappingURL=adapter-registry-BkUvZSKJ.js.map
836
+ //# sourceMappingURL=adapter-registry-CDNPbixE.js.map
package/dist/cli/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { AdapterTelemetryPersistence, AppError, DEFAULT_CONFIG, DEFAULT_ROUTING_POLICY, DoltClient, DoltNotInstalled, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, FileStateStore, GitClient, GrammarLoader, IngestionServer, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SUBSTRATE_OWNED_SETTINGS_KEYS, SymbolParser, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, checkDoltInstalled, createConfigSystem, createContextCompiler, createDatabaseAdapter, createDispatcher, createDoltClient, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStateStore, createStopAfterGate, detectCycles, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, initSchema, initializeDolt, isSyncAdapter, parseDbTimestampAsUtc, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-CxoTrYdA.js";
2
+ import { AdapterTelemetryPersistence, AppError, DEFAULT_CONFIG, DEFAULT_ROUTING_POLICY, DoltClient, DoltNotInstalled, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, FileStateStore, GitClient, GrammarLoader, IngestionServer, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SUBSTRATE_OWNED_SETTINGS_KEYS, SymbolParser, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, checkDoltInstalled, createConfigSystem, createContextCompiler, createDatabaseAdapter, createDispatcher, createDoltClient, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStateStore, createStopAfterGate, createTelemetryAdvisor, detectCycles, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, initSchema, initializeDolt, isSyncAdapter, parseDbTimestampAsUtc, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-BVqGAkUO.js";
3
3
  import { createLogger } from "../logger-D2fS2ccL.js";
4
- import { AdapterRegistry } from "../adapter-registry-BkUvZSKJ.js";
4
+ import { AdapterRegistry } from "../adapter-registry-CDNPbixE.js";
5
5
  import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema } from "../config-migrator-DtZW1maj.js";
6
6
  import { ConfigError, createEventBus } from "../helpers-BihqWgVe.js";
7
7
  import { RoutingRecommender } from "../routing-BUE9pIxW.js";
8
8
  import { addTokenUsage, createDecision, createPipelineRun, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-C6MF2Cax.js";
9
- import { ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-CidppHy-.js";
9
+ import { ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-BRpT8MYF.js";
10
10
  import { abortMerge, createWorktree, getConflictingFiles, getMergedFiles, getOrphanedWorktrees, performMerge, removeBranch, removeWorktree, simulateMerge, verifyGitVersion } from "../git-utils-C-fdrHF_.js";
11
11
  import "../version-manager-impl-DTlmGvHb.js";
12
12
  import { registerUpgradeCommand } from "../upgrade-C8_VcI8B.js";
@@ -1122,7 +1122,8 @@ async function runResumeAction(options) {
1122
1122
  events: eventsFlag,
1123
1123
  existingRunId: runId,
1124
1124
  projectRoot,
1125
- registry
1125
+ registry,
1126
+ stories: options.stories
1126
1127
  });
1127
1128
  } catch (err) {
1128
1129
  const msg = err instanceof Error ? err.message : String(err);
@@ -1137,7 +1138,7 @@ async function runResumeAction(options) {
1137
1138
  }
1138
1139
  }
1139
1140
  async function runFullPipelineFromPhase(options) {
1140
- const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, events: eventsFlag, existingRunId, projectRoot, registry: injectedRegistry } = options;
1141
+ const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, events: eventsFlag, existingRunId, projectRoot, registry: injectedRegistry, stories: explicitStories } = options;
1141
1142
  if (!existsSync(dbDir)) mkdirSync(dbDir, { recursive: true });
1142
1143
  const adapter = createDatabaseAdapter({
1143
1144
  backend: "auto",
@@ -1283,7 +1284,10 @@ async function runFullPipelineFromPhase(options) {
1283
1284
  ...telemetryPersistence !== void 0 ? { telemetryPersistence } : {}
1284
1285
  });
1285
1286
  if (ndjsonEmitter !== void 0) {
1286
- const resolvedKeys = await resolveStoryKeys(adapter, projectRoot, { pipelineRunId: runId });
1287
+ const resolvedKeys = await resolveStoryKeys(adapter, projectRoot, {
1288
+ explicit: explicitStories,
1289
+ pipelineRunId: runId
1290
+ });
1287
1291
  ndjsonEmitter.emit({
1288
1292
  type: "pipeline:start",
1289
1293
  ts: new Date().toISOString(),
@@ -1376,7 +1380,10 @@ async function runFullPipelineFromPhase(options) {
1376
1380
  logger$16.warn({ err }, "Failed to record token usage");
1377
1381
  }
1378
1382
  });
1379
- const storyKeys = await resolveStoryKeys(adapter, projectRoot, { pipelineRunId: runId });
1383
+ const storyKeys = await resolveStoryKeys(adapter, projectRoot, {
1384
+ explicit: explicitStories,
1385
+ pipelineRunId: runId
1386
+ });
1380
1387
  if (storyKeys.length === 0 && outputFormat === "human") process.stdout.write("[IMPLEMENTATION] No stories found for this run. Check solutioning phase output.\n");
1381
1388
  await orchestrator.run(storyKeys);
1382
1389
  if (ndjsonEmitter !== void 0) ndjsonEmitter.emit({
@@ -2518,7 +2525,7 @@ function defaultSupervisorDeps() {
2518
2525
  if (cached === null) {
2519
2526
  const { AdapterRegistry: AR } = await import(
2520
2527
  /* @vite-ignore */
2521
- "../adapter-registry-BRQXdPnB.js"
2528
+ "../adapter-registry-B0XmM7pb.js"
2522
2529
  );
2523
2530
  cached = new AR();
2524
2531
  await cached.discoverAndRegister();
@@ -2626,6 +2633,31 @@ function defaultSupervisorDeps() {
2626
2633
  await raAdapter.close();
2627
2634
  } catch {}
2628
2635
  }
2636
+ },
2637
+ getRunConfig: async (runId, projectRoot) => {
2638
+ try {
2639
+ const dbRoot = await resolveMainRepoRoot(projectRoot);
2640
+ const rcAdapter = createDatabaseAdapter({
2641
+ backend: "auto",
2642
+ basePath: dbRoot
2643
+ });
2644
+ try {
2645
+ await initSchema(rcAdapter);
2646
+ const rows = await rcAdapter.query("SELECT config_json FROM pipeline_runs WHERE id = ?", [runId]);
2647
+ if (rows.length === 0 || rows[0].config_json === null) return null;
2648
+ const config = JSON.parse(rows[0].config_json);
2649
+ return {
2650
+ explicitStories: config.explicitStories,
2651
+ epic: config.epic
2652
+ };
2653
+ } finally {
2654
+ try {
2655
+ await rcAdapter.close();
2656
+ } catch {}
2657
+ }
2658
+ } catch {
2659
+ return null;
2660
+ }
2629
2661
  }
2630
2662
  };
2631
2663
  }
@@ -2681,7 +2713,11 @@ async function handleStallRecovery(health, state, config, deps, io) {
2681
2713
  const { killPid, resumePipeline, sleep, incrementRestarts, getAllDescendants, writeStallFindings, getRegistry } = deps;
2682
2714
  const { emitEvent, log } = io;
2683
2715
  const { projectRoot } = state;
2684
- if (health.staleness_seconds < stallThreshold) return null;
2716
+ const REVIEW_PHASES = new Set(["IN_REVIEW", "code-review"]);
2717
+ const activePhases = Object.values(health.stories.details ?? {}).map((s) => s.phase);
2718
+ const inReviewPhase = activePhases.some((p) => REVIEW_PHASES.has(p));
2719
+ const effectiveThreshold = inReviewPhase ? stallThreshold * 2 : stallThreshold;
2720
+ if (health.staleness_seconds < effectiveThreshold) return null;
2685
2721
  const directPids = [...health.process.orchestrator_pid !== null ? [health.process.orchestrator_pid] : [], ...health.process.child_pids];
2686
2722
  const descendantPids = getAllDescendants(directPids);
2687
2723
  const directPidSet = new Set(directPids);
@@ -2747,6 +2783,15 @@ async function handleStallRecovery(health, state, config, deps, io) {
2747
2783
  });
2748
2784
  log(`Supervisor: Restarting pipeline (attempt ${newRestartCount}/${maxRestarts})`);
2749
2785
  try {
2786
+ let scopedStories;
2787
+ if (deps.getRunConfig !== void 0 && health.run_id !== null) try {
2788
+ const runConfig = await deps.getRunConfig(health.run_id, projectRoot);
2789
+ if (runConfig?.explicitStories !== void 0 && runConfig.explicitStories.length > 0) scopedStories = runConfig.explicitStories;
2790
+ } catch {}
2791
+ if (scopedStories === void 0) {
2792
+ const healthKeys = Object.keys(health.stories.details ?? {});
2793
+ if (healthKeys.length > 0) scopedStories = healthKeys;
2794
+ }
2750
2795
  const registry = await getRegistry();
2751
2796
  await resumePipeline({
2752
2797
  runId: health.run_id ?? void 0,
@@ -2754,7 +2799,8 @@ async function handleStallRecovery(health, state, config, deps, io) {
2754
2799
  projectRoot,
2755
2800
  concurrency: 3,
2756
2801
  pack,
2757
- registry
2802
+ registry,
2803
+ ...scopedStories !== void 0 ? { stories: scopedStories } : {}
2758
2804
  });
2759
2805
  if (writeStallFindings) await writeStallFindings({
2760
2806
  runId: health.run_id,
@@ -2909,7 +2955,7 @@ async function runSupervisorAction(options, deps = {}) {
2909
2955
  try {
2910
2956
  const { createExperimenter } = await import(
2911
2957
  /* @vite-ignore */
2912
- "../experimenter-CoR0k66d.js"
2958
+ "../experimenter-CjfzjmwY.js"
2913
2959
  );
2914
2960
  const { getLatestRun: getLatest } = await import(
2915
2961
  /* @vite-ignore */
@@ -2923,7 +2969,7 @@ async function runSupervisorAction(options, deps = {}) {
2923
2969
  await initSchema(expAdapter);
2924
2970
  const { runRunAction: runPipeline } = await import(
2925
2971
  /* @vite-ignore */
2926
- "../run-BSs4Dn0j.js"
2972
+ "../run-WMR5BAhL.js"
2927
2973
  );
2928
2974
  const runStoryFn = async (opts) => {
2929
2975
  const exitCode = await runPipeline({
@@ -3001,7 +3047,8 @@ async function runSupervisorAction(options, deps = {}) {
3001
3047
  incrementRestarts: resolvedDeps.incrementRestarts,
3002
3048
  getAllDescendants: resolvedDeps.getAllDescendants,
3003
3049
  writeStallFindings: resolvedDeps.writeStallFindings,
3004
- getRegistry: resolvedDeps.getRegistry
3050
+ getRegistry: resolvedDeps.getRegistry,
3051
+ getRunConfig: resolvedDeps.getRunConfig
3005
3052
  }, {
3006
3053
  emitEvent,
3007
3054
  log
@@ -3189,7 +3236,7 @@ async function openTelemetryAdapter(basePath) {
3189
3236
  function rowsToEfficiencyScore(rows) {
3190
3237
  return rows;
3191
3238
  }
3192
- function printEfficiencyTable(scores) {
3239
+ function printEfficiencyTable(scores, dispatchScoresByStory = new Map()) {
3193
3240
  process.stdout.write(`\nEfficiency Scores (${scores.length} records)\n`);
3194
3241
  process.stdout.write("─".repeat(80) + "\n");
3195
3242
  process.stdout.write(` ${"Story Key".padEnd(14)} ${"Score".padStart(6)} ${"Cache Hit%".padStart(11)} ${"I/O Ratio".padStart(10)} ${"Ctx Mgmt".padStart(9)} Model\n`);
@@ -3200,6 +3247,16 @@ function printEfficiencyTable(scores) {
3200
3247
  const ctxMgmt = String(Math.round(s.contextManagementSubScore));
3201
3248
  const model = s.perModelBreakdown.length > 0 ? s.perModelBreakdown[0]?.model ?? "unknown" : "unknown";
3202
3249
  process.stdout.write(` ${s.storyKey.padEnd(14)} ${String(s.compositeScore).padStart(6)} ${cacheHitPct.padStart(11)} ${ioRatio.padStart(10)} ${ctxMgmt.padStart(9)} ${model}\n`);
3250
+ const dispatchScores = dispatchScoresByStory.get(s.storyKey);
3251
+ if (dispatchScores !== void 0 && dispatchScores.length > 0) {
3252
+ const rows = dispatchScores.slice(0, 5);
3253
+ for (const ds of rows) {
3254
+ const taskType = ds.taskType ?? "unknown";
3255
+ const phase = ds.phase ?? "unknown";
3256
+ const dsCacheHitPct = ds.totalTurns > 0 ? `${(ds.avgCacheHitRate * 100).toFixed(1)}%` : "0.0%";
3257
+ process.stdout.write(` ↳ ${taskType}/${phase} score=${ds.compositeScore} cache=${dsCacheHitPct} turns=${ds.totalTurns}\n`);
3258
+ }
3259
+ }
3203
3260
  }
3204
3261
  }
3205
3262
  function printRecommendationTable(recs) {
@@ -3215,13 +3272,15 @@ function printRecommendationTable(recs) {
3215
3272
  }
3216
3273
  function printTurnTable(turns, storyKey) {
3217
3274
  process.stdout.write(`\nTurn Analysis: ${storyKey} (${turns.length} turns)\n`);
3218
- process.stdout.write("─".repeat(80) + "\n");
3219
- process.stdout.write(` ${"#".padStart(4)} ${"Tokens In".padStart(10)} ${"Tok Out".padStart(8)} ${"Cache Hit%".padStart(11)} ${"Ctx Size".padStart(9)} Spike\n`);
3220
- process.stdout.write(" " + "─".repeat(60) + "\n");
3275
+ process.stdout.write("─".repeat(100) + "\n");
3276
+ process.stdout.write(` ${"#".padStart(4)} ${"Tokens In".padStart(10)} ${"Tok Out".padStart(8)} ${"Cache Hit%".padStart(11)} ${"Ctx Size".padStart(9)} ${"Task Type".padEnd(16)} ${"Phase".padEnd(16)} Spike\n`);
3277
+ process.stdout.write(" " + "─".repeat(86) + "\n");
3221
3278
  for (const t of turns) {
3222
3279
  const cacheHitPct = t.inputTokens > 0 ? `${(t.cacheHitRate * 100).toFixed(1)}%` : "0.0%";
3223
3280
  const spike = t.isContextSpike ? " ⚠" : "";
3224
- process.stdout.write(` ${String(t.turnNumber).padStart(4)} ${t.inputTokens.toLocaleString().padStart(10)} ${t.outputTokens.toLocaleString().padStart(8)} ${cacheHitPct.padStart(11)} ${t.contextSize.toLocaleString().padStart(9)}${spike}\n`);
3281
+ const taskType = (t.taskType ?? "-").padEnd(16);
3282
+ const phase = (t.phase ?? "-").padEnd(16);
3283
+ process.stdout.write(` ${String(t.turnNumber).padStart(4)} ${t.inputTokens.toLocaleString().padStart(10)} ${t.outputTokens.toLocaleString().padStart(8)} ${cacheHitPct.padStart(11)} ${t.contextSize.toLocaleString().padStart(9)} ${taskType} ${phase}${spike}\n`);
3225
3284
  }
3226
3285
  }
3227
3286
  function printConsumerTable(consumers, storyKey) {
@@ -3295,8 +3354,19 @@ async function runMetricsAction(options) {
3295
3354
  try {
3296
3355
  if (efficiency === true) {
3297
3356
  const scores = await telemetryPersistence.getEfficiencyScores(20);
3298
- if (outputFormat === "json") process.stdout.write(formatOutput({ efficiency: rowsToEfficiencyScore(scores) }, "json", true) + "\n");
3299
- else printEfficiencyTable(scores);
3357
+ const storyKeys = [...new Set(scores.map((s) => s.storyKey))];
3358
+ const dispatchScoresByStory = new Map();
3359
+ await Promise.all(storyKeys.map(async (sk) => {
3360
+ const ds = await telemetryPersistence.getDispatchEfficiencyScores(sk);
3361
+ if (ds.length > 0) dispatchScoresByStory.set(sk, ds);
3362
+ }));
3363
+ if (outputFormat === "json") {
3364
+ const efficiencyWithDispatch = scores.map((s) => ({
3365
+ ...rowsToEfficiencyScore([s])[0],
3366
+ dispatchScores: dispatchScoresByStory.get(s.storyKey) ?? []
3367
+ }));
3368
+ process.stdout.write(formatOutput({ efficiency: efficiencyWithDispatch }, "json", true) + "\n");
3369
+ } else printEfficiencyTable(scores, dispatchScoresByStory);
3300
3370
  return 0;
3301
3371
  }
3302
3372
  if (recommendations === true) {
@@ -7279,7 +7349,7 @@ async function getRetryableEscalations(adapter, runId) {
7279
7349
  //#region src/cli/commands/retry-escalated.ts
7280
7350
  const logger$3 = createLogger("retry-escalated-cmd");
7281
7351
  async function runRetryEscalatedAction(options) {
7282
- const { runId, dryRun, outputFormat, projectRoot, concurrency, pack: packName, registry: injectedRegistry } = options;
7352
+ const { runId, dryRun, force, outputFormat, projectRoot, concurrency, pack: packName, registry: injectedRegistry } = options;
7283
7353
  const dbRoot = await resolveMainRepoRoot(projectRoot);
7284
7354
  const dbPath = join(dbRoot, ".substrate", "substrate.db");
7285
7355
  const doltDir = join(dbRoot, ".substrate", "state", ".dolt");
@@ -7304,6 +7374,26 @@ async function runRetryEscalatedAction(options) {
7304
7374
  else process.stdout.write("No retry-targeted escalations found.\n");
7305
7375
  return 0;
7306
7376
  }
7377
+ const perStoryContextCeilings = {};
7378
+ if (!dryRun && !force) {
7379
+ const advisor = createTelemetryAdvisor({ db: adapter });
7380
+ const CONTEXT_SPIKE_THRESHOLD = 1e5;
7381
+ const contextCeiling = Math.round(CONTEXT_SPIKE_THRESHOLD * .8);
7382
+ for (const storyKey of retryable) try {
7383
+ const profile = await advisor.getEfficiencyProfile(storyKey);
7384
+ if (profile === null) continue;
7385
+ if (profile.compositeScore < 50) process.stdout.write(`[WARN] ${storyKey}: Previous run had low efficiency (score: ${profile.compositeScore}). Retry may encounter the same issues.\n`);
7386
+ if (profile.contextManagementSubScore < 50) {
7387
+ perStoryContextCeilings[storyKey] = contextCeiling;
7388
+ process.stdout.write(`[INFO] ${storyKey}: Context ceiling set to ${contextCeiling} tokens due to prior context spike pattern.\n`);
7389
+ }
7390
+ } catch (err) {
7391
+ logger$3.warn({
7392
+ err,
7393
+ storyKey
7394
+ }, "Failed to read efficiency profile — skipping gate");
7395
+ }
7396
+ }
7307
7397
  if (dryRun) {
7308
7398
  if (outputFormat === "json") process.stdout.write(formatOutput({
7309
7399
  retryKeys: retryable,
@@ -7339,7 +7429,8 @@ async function runRetryEscalatedAction(options) {
7339
7429
  config_json: JSON.stringify({
7340
7430
  storyKeys: retryable,
7341
7431
  concurrency,
7342
- retryRun: true
7432
+ retryRun: true,
7433
+ explicitStories: retryable
7343
7434
  })
7344
7435
  });
7345
7436
  const eventBus = createEventBus();
@@ -7358,7 +7449,8 @@ async function runRetryEscalatedAction(options) {
7358
7449
  config: {
7359
7450
  maxConcurrency: concurrency,
7360
7451
  maxReviewCycles: 2,
7361
- pipelineRunId: pipelineRun.id
7452
+ pipelineRunId: pipelineRun.id,
7453
+ ...Object.keys(perStoryContextCeilings).length > 0 ? { perStoryContextCeilings } : {}
7362
7454
  },
7363
7455
  projectRoot
7364
7456
  });
@@ -7410,7 +7502,7 @@ async function runRetryEscalatedAction(options) {
7410
7502
  }
7411
7503
  }
7412
7504
  function registerRetryEscalatedCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
7413
- program.command("retry-escalated").description("Retry escalated stories flagged as retry-targeted by escalation diagnosis").option("--run-id <id>", "Scope to a specific pipeline run ID (defaults to latest run with escalations)").option("--dry-run", "Print retryable and skipped stories without invoking the orchestrator").option("--concurrency <n>", "Maximum parallel story executions", (v) => {
7505
+ program.command("retry-escalated").description("Retry escalated stories flagged as retry-targeted by escalation diagnosis").option("--run-id <id>", "Scope to a specific pipeline run ID (defaults to latest run with escalations)").option("--dry-run", "Print retryable and skipped stories without invoking the orchestrator").option("--force", "Bypass efficiency-gate checks (warning and context ceiling)", false).option("--concurrency <n>", "Maximum parallel story executions", (v) => {
7414
7506
  const n = parseInt(v, 10);
7415
7507
  if (isNaN(n) || n < 1) throw new Error(`--concurrency must be a positive integer, got: ${v}`);
7416
7508
  return n;
@@ -7419,6 +7511,7 @@ function registerRetryEscalatedCommand(program, _version = "0.0.0", projectRoot
7419
7511
  const exitCode = await runRetryEscalatedAction({
7420
7512
  runId: opts.runId,
7421
7513
  dryRun: opts.dryRun === true,
7514
+ force: opts.force === true,
7422
7515
  outputFormat,
7423
7516
  projectRoot: opts.projectRoot,
7424
7517
  concurrency: opts.concurrency,
@@ -7880,23 +7973,19 @@ function registerRoutingCommand(program) {
7880
7973
  /**
7881
7974
  * Work-graph schema DDL constants.
7882
7975
  *
7883
- * Story 31-1 placeholder defines the `stories`, `story_dependencies`, and
7884
- * `ready_stories` DDL used by the EpicIngester and downstream consumers.
7885
- *
7886
- * NOTE: This file is a minimal placeholder created by story 31-2 because story
7887
- * 31-1 (schema creation) had not yet run. If story 31-1 produces a richer
7888
- * schema, merge carefully and remove this note.
7976
+ * Aligned with the authoritative schema in src/modules/state/schema.sql.
7977
+ * Table names use `wg_stories` and `story_dependencies`.
7889
7978
  */
7890
7979
  const CREATE_STORIES_TABLE = `
7891
- CREATE TABLE IF NOT EXISTS stories (
7892
- story_key VARCHAR(50) NOT NULL,
7893
- epic_num INT NOT NULL,
7894
- story_num INT NOT NULL,
7895
- title VARCHAR(500) NOT NULL,
7896
- priority VARCHAR(10) NOT NULL,
7897
- size VARCHAR(50) NOT NULL,
7898
- sprint INT NOT NULL,
7899
- status VARCHAR(50) NOT NULL DEFAULT 'planned',
7980
+ CREATE TABLE IF NOT EXISTS wg_stories (
7981
+ story_key VARCHAR(20) NOT NULL,
7982
+ epic VARCHAR(20) NOT NULL,
7983
+ title VARCHAR(255),
7984
+ status VARCHAR(30) NOT NULL DEFAULT 'planned',
7985
+ spec_path VARCHAR(500),
7986
+ created_at DATETIME,
7987
+ updated_at DATETIME,
7988
+ completed_at DATETIME,
7900
7989
  PRIMARY KEY (story_key)
7901
7990
  )
7902
7991
  `.trim();
@@ -7906,31 +7995,33 @@ CREATE TABLE IF NOT EXISTS story_dependencies (
7906
7995
  depends_on VARCHAR(50) NOT NULL,
7907
7996
  dependency_type VARCHAR(50) NOT NULL DEFAULT 'blocks',
7908
7997
  source VARCHAR(50) NOT NULL DEFAULT 'explicit',
7998
+ created_at DATETIME,
7909
7999
  PRIMARY KEY (story_key, depends_on)
7910
8000
  )
7911
8001
  `.trim();
7912
8002
  const CREATE_READY_STORIES_VIEW = `
7913
8003
  CREATE VIEW IF NOT EXISTS ready_stories AS
7914
8004
  SELECT s.*
7915
- FROM stories s
7916
- WHERE s.status = 'planned'
8005
+ FROM wg_stories s
8006
+ WHERE s.status IN ('planned', 'ready')
7917
8007
  AND NOT EXISTS (
7918
8008
  SELECT 1 FROM story_dependencies sd
7919
- JOIN stories blocking ON sd.depends_on = blocking.story_key
8009
+ JOIN wg_stories blocking ON sd.depends_on = blocking.story_key
7920
8010
  WHERE sd.story_key = s.story_key
7921
- AND blocking.status != 'done'
8011
+ AND sd.dependency_type = 'blocks'
8012
+ AND blocking.status <> 'complete'
7922
8013
  )
7923
8014
  `.trim();
7924
8015
 
7925
8016
  //#endregion
7926
8017
  //#region src/modules/work-graph/epic-parser.ts
7927
- /** Regex for sprint header lines: `**Sprint 1 —` (em dash or hyphen) */
7928
- const SPRINT_HEADER_RE = /^\*\*Sprint\s+(\d+)\s*[—–-]/i;
8018
+ /** Regex for sprint header lines: `**Sprint 1 —` or `Sprint 1 —` (with or without bold markers) */
8019
+ const SPRINT_HEADER_RE = /^(?:\*\*)?Sprint\s+(\d+)\s*[—–-]/i;
7929
8020
  /**
7930
8021
  * Regex for story lines: `- 31-2: Epic doc ingestion (P0, Medium)`
7931
8022
  * Captures: epicNum, storyNum, title, priority, size
7932
8023
  */
7933
- const STORY_LINE_RE = /^-\s+(\d+)-(\d+):\s+(.+?)\s+\((P\d+),\s+([\w-]+)\)\s*$/;
8024
+ const STORY_LINE_RE = /^(?:-\s+)?(?:Story\s+)?(\d+)-(\d+):\s+(.+?)\s+\((P\d+),\s+([\w-]+)\)\s*$/;
7934
8025
  /** Regex to find the story map section heading */
7935
8026
  const STORY_MAP_HEADING_RE = /^#{1,6}\s+.*Story\s+Map/im;
7936
8027
  /** Regex to find the dependency chain line */
@@ -7953,6 +8044,7 @@ var EpicParser = class {
7953
8044
  let currentSprint = 0;
7954
8045
  for (const rawLine of afterHeading.split("\n")) {
7955
8046
  const line = rawLine.trim();
8047
+ if (line.startsWith("```")) continue;
7956
8048
  const sprintMatch = SPRINT_HEADER_RE.exec(line);
7957
8049
  if (sprintMatch) {
7958
8050
  currentSprint = parseInt(sprintMatch[1], 10);
@@ -8043,24 +8135,21 @@ var EpicIngester = class {
8043
8135
  return this.adapter.transaction(async (tx) => {
8044
8136
  let storiesUpserted = 0;
8045
8137
  for (const story of stories) {
8046
- const existing = await tx.query("SELECT status FROM stories WHERE story_key = ?", [story.story_key]);
8047
- if (existing.length > 0) await tx.query("UPDATE stories SET title = ?, priority = ?, size = ?, sprint = ? WHERE story_key = ?", [
8138
+ const existing = await tx.query("SELECT status FROM wg_stories WHERE story_key = ?", [story.story_key]);
8139
+ if (existing.length > 0) await tx.query("UPDATE wg_stories SET title = ?, updated_at = ? WHERE story_key = ?", [
8048
8140
  story.title,
8049
- story.priority,
8050
- story.size,
8051
- story.sprint,
8141
+ new Date().toISOString(),
8052
8142
  story.story_key
8053
8143
  ]);
8054
8144
  else {
8055
- await tx.query("INSERT INTO stories (story_key, epic_num, story_num, title, priority, size, sprint, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [
8145
+ const now = new Date().toISOString();
8146
+ await tx.query("INSERT INTO wg_stories (story_key, epic, title, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)", [
8056
8147
  story.story_key,
8057
- story.epic_num,
8058
- story.story_num,
8148
+ String(story.epic_num),
8059
8149
  story.title,
8060
- story.priority,
8061
- story.size,
8062
- story.sprint,
8063
- "planned"
8150
+ "planned",
8151
+ now,
8152
+ now
8064
8153
  ]);
8065
8154
  storiesUpserted++;
8066
8155
  }
@@ -1,6 +1,6 @@
1
1
  import "./logger-D2fS2ccL.js";
2
2
  import { createDecision } from "./decisions-C6MF2Cax.js";
3
- import { EXPERIMENT_RESULT, getRunMetrics, getStoryMetricsForRun } from "./operational-CidppHy-.js";
3
+ import { EXPERIMENT_RESULT, getRunMetrics, getStoryMetricsForRun } from "./operational-BRpT8MYF.js";
4
4
  import { spawnGit } from "./git-utils-C-fdrHF_.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-CoR0k66d.js.map
503
+ //# sourceMappingURL=experimenter-CjfzjmwY.js.map
package/dist/index.d.ts CHANGED
@@ -1514,6 +1514,19 @@ interface AdapterOptions {
1514
1514
  * so the telemetry pipeline can group spans/events per story.
1515
1515
  */
1516
1516
  storyKey?: string;
1517
+ /**
1518
+ * Optional maximum context tokens (passed as --max-context-tokens to Claude CLI).
1519
+ * When set, constrains the context window to prevent runaway token usage.
1520
+ * Used by efficiency-gated retry logic (Story 30-8) to cap context for stories
1521
+ * that previously exhibited context spike patterns.
1522
+ */
1523
+ maxContextTokens?: number;
1524
+ /**
1525
+ * Optional optimization directives derived from prior stories' telemetry (Story 30-6).
1526
+ * When set, appended to the system prompt to guide the sub-agent toward efficient patterns.
1527
+ * Generated by TelemetryAdvisor.formatOptimizationDirectives().
1528
+ */
1529
+ optimizationDirectives?: string;
1517
1530
  }
1518
1531
  /**
1519
1532
  * Capabilities reported by an adapter for this CLI agent.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { childLogger, createLogger, logger } from "./logger-D2fS2ccL.js";
2
- import { AdapterRegistry, ClaudeCodeAdapter, CodexCLIAdapter, GeminiCLIAdapter } from "./adapter-registry-BkUvZSKJ.js";
2
+ import { AdapterRegistry, ClaudeCodeAdapter, CodexCLIAdapter, GeminiCLIAdapter } from "./adapter-registry-CDNPbixE.js";
3
3
  import { AdtError, BudgetExceededError, ConfigError, ConfigIncompatibleFormatError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError, assertDefined, createEventBus, createTuiApp, deepClone, formatDuration, generateId, isPlainObject, isTuiCapable, printNonTtyWarning, sleep, withRetry } from "./helpers-BihqWgVe.js";
4
4
 
5
5
  //#region src/core/di.ts
@@ -186,7 +186,7 @@ async function aggregateTokenUsageForStory(adapter, runId, storyKey) {
186
186
  FROM token_usage
187
187
  WHERE pipeline_run_id = ?
188
188
  AND metadata IS NOT NULL
189
- AND json_extract(metadata, '$.storyKey') = ?`, [runId, storyKey]);
189
+ AND metadata LIKE ?`, [runId, `%"storyKey":"${storyKey}"%`]);
190
190
  return rows[0] ?? {
191
191
  input: 0,
192
192
  output: 0,
@@ -371,4 +371,4 @@ const ADVISORY_NOTES = "advisory-notes";
371
371
 
372
372
  //#endregion
373
373
  export { ADVISORY_NOTES, ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, STORY_OUTCOME, TEST_EXPANSION_FINDING, TEST_PLAN, aggregateTokenUsageForRun, aggregateTokenUsageForStory, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline, writeRunMetrics, writeStoryMetrics };
374
- //# sourceMappingURL=operational-CidppHy-.js.map
374
+ //# sourceMappingURL=operational-BRpT8MYF.js.map