substrate-ai 0.20.63 → 0.20.64

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,15 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { FileStateStore, RunManifest, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, parseRuntimeProbes, readCurrentRunId, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics } from "../health-C-KOZrFJ.js";
2
+ import { FileStateStore, RunManifest, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, parseRuntimeProbes, readCurrentRunId, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics } from "../health-DudlnqXd.js";
3
3
  import { createLogger } from "../logger-KeHncl-f.js";
4
4
  import { createEventBus } from "../helpers-CElYrONe.js";
5
5
  import { AdapterRegistry, BudgetConfigSchema, CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, ConfigError, CostTrackerConfigSchema, DEFAULT_CONFIG, DoltClient, DoltNotInstalled, GlobalSettingsSchema, InMemoryDatabaseAdapter, IngestionServer, MonitorDatabaseImpl, OPERATIONAL_FINDING, PartialGlobalSettingsSchema, PartialProviderConfigSchema, ProvidersSchema, RoutingRecommender, STORY_METRICS, TelemetryConfigSchema, addTokenUsage, aggregateTokenUsageForRun, checkDoltInstalled, compareRunMetrics, createAmendmentRun, createConfigSystem, createDecision, createDoltClient, createPipelineRun, getActiveDecisions, getAllCostEntriesFiltered, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initializeDolt, listRunMetrics, loadParentRunDecisions, supersedeDecision, tagRunAsBaseline, updatePipelineRun } from "../dist-W2emvN3F.js";
6
6
  import "../adapter-registry-DXLMTmfD.js";
7
- import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GLOBSTAR, GitClient, GrammarLoader, Minimatch, Minipass, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, escape, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runProbeAuthor, runSolutioningPhase, unescape, validateStopAfterFromConflict } from "../run-CHUFlRbH.js";
7
+ import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GLOBSTAR, GitClient, GrammarLoader, Minimatch, Minipass, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, escape, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runProbeAuthor, runSolutioningPhase, unescape, validateStopAfterFromConflict } from "../run-CCxsv-9M.js";
8
8
  import "../errors-CKFu8YI9.js";
9
9
  import "../routing-CcBOCuC9.js";
10
10
  import "../decisions-C0pz9Clx.js";
11
- import "../version-manager-impl-BmOWu8ml.js";
12
- import { registerUpgradeCommand } from "../upgrade-CAqLkNUP.js";
11
+ import "../decision-router-BA__VYIp.js";
12
+ import "../version-manager-impl-FH4TTnXm.js";
13
+ import { registerUpgradeCommand } from "../upgrade-aW7GYL2F.js";
13
14
  import { Command } from "commander";
14
15
  import { fileURLToPath } from "url";
15
16
  import { dirname, join, resolve } from "path";
@@ -7486,7 +7487,7 @@ async function runStatusAction(options) {
7486
7487
  logger$15.debug({ err }, "Work graph query failed, continuing without work graph data");
7487
7488
  }
7488
7489
  if (run === void 0) {
7489
- const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-CJqd1FzY.js");
7490
+ const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-CLNmnZiw.js");
7490
7491
  const substrateDirPath = join(projectRoot, ".substrate");
7491
7492
  const processInfo = inspectProcessTree$1({
7492
7493
  projectRoot,
@@ -9018,7 +9019,7 @@ async function runSupervisorAction(options, deps = {}) {
9018
9019
  try {
9019
9020
  const { createExperimenter } = await import(
9020
9021
  /* @vite-ignore */
9021
- "../experimenter-DxxwicpK.js"
9022
+ "../experimenter-BgpUcUaW.js"
9022
9023
  );
9023
9024
  const { getLatestRun: getLatest } = await import(
9024
9025
  /* @vite-ignore */
@@ -9032,7 +9033,7 @@ async function runSupervisorAction(options, deps = {}) {
9032
9033
  await initSchema(expAdapter);
9033
9034
  const { runRunAction: runPipeline } = await import(
9034
9035
  /* @vite-ignore */
9035
- "../run-Z_-caE_i.js"
9036
+ "../run-ChxsPICN.js"
9036
9037
  );
9037
9038
  const runStoryFn = async (opts) => {
9038
9039
  const exitCode = await runPipeline({
@@ -14035,8 +14036,8 @@ async function createProgram() {
14035
14036
  /** Fire-and-forget startup version check (story 8.3, AC3/AC5) */
14036
14037
  function checkForUpdatesInBackground(currentVersion) {
14037
14038
  if (process.env.SUBSTRATE_NO_UPDATE_CHECK === "1") return;
14038
- import("../upgrade-C8LAnB6W.js").then(async () => {
14039
- const { createVersionManager } = await import("../version-manager-impl-DaA_ALYK.js");
14039
+ import("../upgrade-OFeC_NIx.js").then(async () => {
14040
+ const { createVersionManager } = await import("../version-manager-impl-BCSf5E3j.js");
14040
14041
  const vm = createVersionManager();
14041
14042
  const result = await vm.checkForUpdates();
14042
14043
  if (result.updateAvailable) {
@@ -0,0 +1,97 @@
1
+ //#region src/modules/decision-router/index.ts
2
+ /**
3
+ * Maps each known decision type to its severity level.
4
+ *
5
+ * Epic 70: cross-story-race-recovered (info — log only, no halt) and
6
+ * cross-story-race-still-failed (critical — recovery exhausted, halt for operator)
7
+ * motivate the registry pattern. Epic 73 (Recovery Engine) will register
8
+ * additional decision types here.
9
+ */
10
+ const DECISION_SEVERITY_MAP = {
11
+ "cost-ceiling-exhausted": "critical",
12
+ "build-verification-failure": "critical",
13
+ "recovery-retry-attempt": "info",
14
+ "re-scope-proposal": "warning",
15
+ "scope-violation": "fatal",
16
+ "cross-story-race-recovered": "info",
17
+ "cross-story-race-still-failed": "critical",
18
+ "pipeline-escalation": "warning"
19
+ };
20
+ /**
21
+ * Default action to apply when a decision does NOT trigger a halt.
22
+ * Caller invokes the returned defaultAction string autonomously.
23
+ */
24
+ const DEFAULT_ACTION_MAP = {
25
+ "cost-ceiling-exhausted": "skip-remaining",
26
+ "build-verification-failure": "escalate-without-halt",
27
+ "recovery-retry-attempt": "continue-autonomous",
28
+ "re-scope-proposal": "escalate-without-halt",
29
+ "scope-violation": "abort-pipeline",
30
+ "cross-story-race-recovered": "continue-autonomous",
31
+ "cross-story-race-still-failed": "escalate-without-halt",
32
+ "pipeline-escalation": "escalate-without-halt"
33
+ };
34
+ /** Fallback default action for unknown decision types. */
35
+ const DEFAULT_DEFAULT_ACTION = "escalate-without-halt";
36
+ /**
37
+ * Route a halt-able decision through the autonomy policy.
38
+ *
39
+ * Pure function — no I/O, no side effects. All orchestrator state interactions
40
+ * remain in orchestrator-impl.ts.
41
+ *
42
+ * Halt policy logic (AC4):
43
+ * - 'all': halts on info | warning | critical | fatal
44
+ * - 'critical': halts on critical | fatal (default)
45
+ * - 'none': halts ONLY on fatal (scope violations bypass the autonomy-gradient
46
+ * policy — they are always halts regardless of the chosen policy)
47
+ *
48
+ * Fatal always halts regardless of policy — hard safety invariant, not configurable.
49
+ *
50
+ * Unknown decision types default to severity 'critical' (safe default, AC9e).
51
+ *
52
+ * @param decision - The decision type string to route
53
+ * @param policy - The autonomy policy from the --halt-on CLI flag
54
+ * @returns { halt: boolean, defaultAction: string, severity: Severity }
55
+ */
56
+ function routeDecision(decision, policy) {
57
+ const severity = DECISION_SEVERITY_MAP[decision] ?? "critical";
58
+ let halt;
59
+ switch (policy) {
60
+ case "all":
61
+ halt = true;
62
+ break;
63
+ case "critical":
64
+ halt = severity === "critical" || severity === "fatal";
65
+ break;
66
+ case "none":
67
+ halt = severity === "fatal";
68
+ break;
69
+ default: halt = severity === "critical" || severity === "fatal";
70
+ }
71
+ if (severity === "fatal") halt = true;
72
+ const defaultAction = DEFAULT_ACTION_MAP[decision] ?? DEFAULT_DEFAULT_ACTION;
73
+ return {
74
+ halt,
75
+ defaultAction,
76
+ severity
77
+ };
78
+ }
79
+ /**
80
+ * Derive the machine-readable exit code from pipeline completion results.
81
+ *
82
+ * - Exit `0` when all stories succeeded (or recovered cleanly)
83
+ * - Exit `1` when some stories escalated; run completed
84
+ * - Exit `2` when run-level failure (cost ceiling, fatal halt, orchestrator died, stories failed)
85
+ *
86
+ * @param outcome - Pipeline completion outcome data
87
+ * @returns 0 (success), 1 (escalated), or 2 (failure)
88
+ */
89
+ function deriveExitCode(outcome) {
90
+ if (outcome.failed.length > 0 || outcome.costCeilingExhausted === true || outcome.fatalHaltReached === true || outcome.orchestratorDied === true) return 2;
91
+ if (outcome.escalated.length > 0 && outcome.failed.length === 0) return 1;
92
+ return 0;
93
+ }
94
+
95
+ //#endregion
96
+ export { DECISION_SEVERITY_MAP, deriveExitCode, routeDecision };
97
+ //# sourceMappingURL=decision-router-BA__VYIp.js.map
@@ -1,4 +1,4 @@
1
- import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-C-KOZrFJ.js";
1
+ import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-DudlnqXd.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./dist-W2emvN3F.js";
4
4
  import "./decisions-C0pz9Clx.js";
@@ -5787,7 +5787,16 @@ const CliFlagsSchema = z.object({
5787
5787
  cost_ceiling: z.number().positive().optional(),
5788
5788
  agent: z.string().optional(),
5789
5789
  skip_verification: z.boolean().optional(),
5790
- events: z.boolean().optional()
5790
+ events: z.boolean().optional(),
5791
+ non_interactive: z.boolean().optional(),
5792
+ halt_skipped: z.boolean().optional(),
5793
+ halt_skipped_decisions: z.array(z.object({
5794
+ decisionType: z.string(),
5795
+ severity: z.string(),
5796
+ defaultAction: z.string(),
5797
+ reason: z.string(),
5798
+ skippedAt: z.string()
5799
+ })).optional()
5791
5800
  });
5792
5801
 
5793
5802
  //#endregion
@@ -7564,4 +7573,4 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
7564
7573
 
7565
7574
  //#endregion
7566
7575
  export { BMAD_BASELINE_TOKENS_FULL, DEFAULT_STALL_THRESHOLD_SECONDS, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, RuntimeProbeListSchema, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN$1 as STORY_KEY_PATTERN, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, __commonJS, __require, __toESM, aggregateProbeAuthorMetrics, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter$1 as createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, createStateStore, detectCycles, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, isOrchestratorProcessLine, parseDbTimestampAsUtc, parseRuntimeProbes, readCurrentRunId, registerHealthCommand, renderFindings, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveGraphPath, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics, runHealthAction, runStaleVerificationRecovery, validateStoryKey };
7567
- //# sourceMappingURL=health-C-KOZrFJ.js.map
7576
+ //# sourceMappingURL=health-DudlnqXd.js.map
package/dist/index.d.ts CHANGED
@@ -732,6 +732,28 @@ interface CostCeilingReachedEvent {
732
732
  /** 'critical' when halt_on is 'all' or 'critical'; absent when 'none' */
733
733
  severity?: string;
734
734
  }
735
+ /**
736
+ * Emitted when --non-interactive mode suppresses a halt decision and applies
737
+ * the default action autonomously instead of prompting the operator.
738
+ *
739
+ * Story 72-2: closes the NDJSON protocol gap — operators can see which halts
740
+ * were auto-skipped and what actions were applied via `substrate report`.
741
+ */
742
+ interface DecisionHaltSkippedNonInteractiveEvent {
743
+ type: 'decision:halt-skipped-non-interactive';
744
+ /** ISO-8601 timestamp generated at emit time */
745
+ ts: string;
746
+ /** Pipeline run ID */
747
+ run_id: string;
748
+ /** Halt decision type that was skipped (e.g., 'halt:escalation', 'cost-ceiling-exhausted') */
749
+ decision_type: string;
750
+ /** Severity of the skipped halt (e.g., 'critical') */
751
+ severity: string;
752
+ /** Action that was applied in place of the operator prompt (e.g., 'continue-autonomous') */
753
+ default_action: string;
754
+ /** Human-readable reason for skipping */
755
+ reason: string;
756
+ }
735
757
  /**
736
758
  * Discriminated union of all pipeline event types.
737
759
  *
@@ -744,7 +766,7 @@ interface CostCeilingReachedEvent {
744
766
  * }
745
767
  * ```
746
768
  */
747
- type PipelineEvent = PipelineStartEvent | PipelineCompleteEvent | PipelinePreFlightFailureEvent | PipelineProfileStaleEvent | PipelineContractMismatchEvent | PipelineContractVerificationSummaryEvent | StoryPhaseEvent | StoryDoneEvent | StoryEscalationEvent | StoryWarnEvent | StoryLogEvent | PipelineHeartbeatEvent | StoryStallEvent | StoryZeroDiffEscalationEvent | StoryBuildVerificationFailedEvent | StoryBuildVerificationPassedEvent | StoryInterfaceChangeWarningEvent | StoryMetricsEvent | SupervisorPollEvent | SupervisorKillEvent | SupervisorRestartEvent | SupervisorAbortEvent | SupervisorSummaryEvent | SupervisorAnalysisCompleteEvent | SupervisorAnalysisErrorEvent | SupervisorExperimentStartEvent | SupervisorExperimentSkipEvent | SupervisorExperimentRecommendationsEvent | SupervisorExperimentCompleteEvent | SupervisorExperimentErrorEvent | RoutingModelSelectedEvent | PipelinePhaseStartEvent | PipelinePhaseCompleteEvent | StoryAutoApprovedEvent | VerificationCheckCompleteEvent | VerificationStoryCompleteEvent | CostWarningEvent | CostCeilingReachedEvent; //#endregion
769
+ type PipelineEvent = PipelineStartEvent | PipelineCompleteEvent | PipelinePreFlightFailureEvent | PipelineProfileStaleEvent | PipelineContractMismatchEvent | PipelineContractVerificationSummaryEvent | StoryPhaseEvent | StoryDoneEvent | StoryEscalationEvent | StoryWarnEvent | StoryLogEvent | PipelineHeartbeatEvent | StoryStallEvent | StoryZeroDiffEscalationEvent | StoryBuildVerificationFailedEvent | StoryBuildVerificationPassedEvent | StoryInterfaceChangeWarningEvent | StoryMetricsEvent | SupervisorPollEvent | SupervisorKillEvent | SupervisorRestartEvent | SupervisorAbortEvent | SupervisorSummaryEvent | SupervisorAnalysisCompleteEvent | SupervisorAnalysisErrorEvent | SupervisorExperimentStartEvent | SupervisorExperimentSkipEvent | SupervisorExperimentRecommendationsEvent | SupervisorExperimentCompleteEvent | SupervisorExperimentErrorEvent | RoutingModelSelectedEvent | PipelinePhaseStartEvent | PipelinePhaseCompleteEvent | StoryAutoApprovedEvent | VerificationCheckCompleteEvent | VerificationStoryCompleteEvent | CostWarningEvent | CostCeilingReachedEvent | DecisionHaltSkippedNonInteractiveEvent; //#endregion
748
770
  //#region packages/core/dist/types.d.ts
749
771
 
750
772
  /**
@@ -2507,6 +2529,59 @@ interface OrchestratorEvents {
2507
2529
  freshFindings: unknown[];
2508
2530
  recoveryDurationMs: number;
2509
2531
  };
2532
+ /**
2533
+ * Story 72-2: A critical halt decision was skipped under --non-interactive mode.
2534
+ *
2535
+ * Emitted when the pipeline would have prompted the operator (interactive stdin
2536
+ * read) but --non-interactive suppressed stdin and auto-applied the default
2537
+ * action instead. Operators reviewing the run via `substrate report` can see
2538
+ * which halts were auto-skipped and what actions were applied.
2539
+ *
2540
+ * Mirror of CoreEvents['decision:halt-skipped-non-interactive']; both must stay
2541
+ * in sync.
2542
+ */
2543
+ 'decision:halt-skipped-non-interactive': {
2544
+ runId: string;
2545
+ /** Halt decision type that was skipped (e.g., 'halt:escalation', 'halt:critical'). */
2546
+ decisionType: string;
2547
+ /** Severity of the skipped halt (e.g., 'critical'). */
2548
+ severity: string;
2549
+ /** Action that was applied in place of the operator prompt (e.g., 'continue'). */
2550
+ defaultAction: string;
2551
+ /** Human-readable reason for skipping (e.g., 'non-interactive: stdin prompt suppressed'). */
2552
+ reason: string;
2553
+ };
2554
+ /**
2555
+ * Story 72-1: A halt-able decision was routed and the policy requires halting.
2556
+ * Emitted when routeDecision() returns halt=true for a given policy.
2557
+ * Mirror of CoreEvents['decision:halt']; both must stay in sync.
2558
+ */
2559
+ 'decision:halt': {
2560
+ runId: string;
2561
+ /** The decision type that triggered the halt (e.g., 'cost-ceiling-exhausted'). */
2562
+ decisionType: string;
2563
+ /** Severity of the decision (e.g., 'critical', 'fatal'). */
2564
+ severity: string;
2565
+ /** Human-readable reason for the halt. */
2566
+ reason: string;
2567
+ };
2568
+ /**
2569
+ * Story 72-1: A halt-able decision was routed and the policy allows autonomous action.
2570
+ * Emitted when routeDecision() returns halt=false for a given policy.
2571
+ * Caller applies defaultAction without operator intervention.
2572
+ * Mirror of CoreEvents['decision:autonomous']; both must stay in sync.
2573
+ */
2574
+ 'decision:autonomous': {
2575
+ runId: string;
2576
+ /** The decision type that was routed autonomously (e.g., 'build-verification-failure'). */
2577
+ decisionType: string;
2578
+ /** Severity of the decision (e.g., 'info', 'warning', 'critical'). */
2579
+ severity: string;
2580
+ /** The action applied autonomously (e.g., 'escalate-without-halt', 'continue-autonomous'). */
2581
+ defaultAction: string;
2582
+ /** Human-readable reason for autonomous action. */
2583
+ reason: string;
2584
+ };
2510
2585
  }
2511
2586
 
2512
2587
  //#endregion
@@ -1,7 +1,8 @@
1
- import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, RuntimeProbeListSchema, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, runStaleVerificationRecovery, validateStoryKey } from "./health-C-KOZrFJ.js";
1
+ import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, RuntimeProbeListSchema, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, runStaleVerificationRecovery, validateStoryKey } from "./health-DudlnqXd.js";
2
2
  import { createLogger } from "./logger-KeHncl-f.js";
3
3
  import { TypedEventBusImpl, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning, sleep } from "./helpers-CElYrONe.js";
4
4
  import { ADVISORY_NOTES, Categorizer, ConsumerAnalyzer, DEFAULT_GLOBAL_SETTINGS, DispatcherImpl, DoltClient, ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, EfficiencyScorer, IngestionServer, LogTurnAnalyzer, OPERATIONAL_FINDING, Recommender, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, STORY_METRICS, STORY_OUTCOME, SubstrateConfigSchema, TEST_EXPANSION_FINDING, TEST_PLAN, TelemetryNormalizer, TelemetryPipeline, TurnAnalyzer, addTokenUsage, aggregateTokenUsageForRun, aggregateTokenUsageForStory, callLLM, createConfigSystem, createDatabaseAdapter$1, createDecision, createPipelineRun, createRequirement, detectInterfaceChanges, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunMetrics, getRunningPipelineRuns, getStoryMetricsForRun, getTokenUsageSummary, initSchema, listRequirements, loadModelRoutingConfig, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision, writeRunMetrics, writeStoryMetrics } from "./dist-W2emvN3F.js";
5
+ import { deriveExitCode, routeDecision } from "./decision-router-BA__VYIp.js";
5
6
  import { basename, dirname, extname, join } from "path";
6
7
  import { access, readFile, readdir, stat } from "fs/promises";
7
8
  import { EventEmitter } from "node:events";
@@ -1764,7 +1765,7 @@ const PIPELINE_EVENT_METADATA = [
1764
1765
  {
1765
1766
  name: "action",
1766
1767
  type: "string",
1767
- description: "Action taken (always stopped in this story; interactive prompt is Epic 54 scope)."
1768
+ description: "Action taken stopped when policy requires halt, or the defaultAction when proceeding autonomously."
1768
1769
  },
1769
1770
  {
1770
1771
  name: "skipped_stories",
@@ -1774,10 +1775,47 @@ const PIPELINE_EVENT_METADATA = [
1774
1775
  {
1775
1776
  name: "severity",
1776
1777
  type: "string",
1777
- description: "critical when halt_on is all or critical; absent when none.",
1778
+ description: "Severity from routeDecision (critical for cost-ceiling-exhausted).",
1778
1779
  optional: true
1779
1780
  }
1780
1781
  ]
1782
+ },
1783
+ {
1784
+ type: "decision:halt-skipped-non-interactive",
1785
+ description: "A critical halt decision was skipped under --non-interactive mode; default action was applied autonomously.",
1786
+ when: "Emitted when --non-interactive suppresses an operator prompt and applies the default action. Story 72-2.",
1787
+ fields: [
1788
+ {
1789
+ name: "ts",
1790
+ type: "string",
1791
+ description: "Timestamp."
1792
+ },
1793
+ {
1794
+ name: "run_id",
1795
+ type: "string",
1796
+ description: "Pipeline run ID."
1797
+ },
1798
+ {
1799
+ name: "decision_type",
1800
+ type: "string",
1801
+ description: "Halt decision type that was skipped (e.g., halt:escalation)."
1802
+ },
1803
+ {
1804
+ name: "severity",
1805
+ type: "string",
1806
+ description: "Severity of the skipped halt (e.g., critical)."
1807
+ },
1808
+ {
1809
+ name: "default_action",
1810
+ type: "string",
1811
+ description: "Action applied in place of the operator prompt."
1812
+ },
1813
+ {
1814
+ name: "reason",
1815
+ type: "string",
1816
+ description: "Human-readable reason for skipping."
1817
+ }
1818
+ ]
1781
1819
  }
1782
1820
  ];
1783
1821
  /**
@@ -14284,6 +14322,27 @@ function createImplementationOrchestrator(deps) {
14284
14322
  }, "Build-fix dispatch failed — escalating");
14285
14323
  }
14286
14324
  if (!buildFixPassed) {
14325
+ let buildHaltPolicy = "critical";
14326
+ if (runManifest !== null && runManifest !== void 0) try {
14327
+ const manifestData = await runManifest.read();
14328
+ buildHaltPolicy = manifestData.cli_flags.halt_on ?? "critical";
14329
+ } catch {}
14330
+ const buildRouteResult = routeDecision("build-verification-failure", buildHaltPolicy);
14331
+ const buildRunId = config.pipelineRunId ?? "unknown";
14332
+ const buildReason = `build verification failed for story ${storyKey}`;
14333
+ if (buildRouteResult.halt) eventBus.emit("decision:halt", {
14334
+ runId: buildRunId,
14335
+ decisionType: "build-verification-failure",
14336
+ severity: buildRouteResult.severity,
14337
+ reason: buildReason
14338
+ });
14339
+ else eventBus.emit("decision:autonomous", {
14340
+ runId: buildRunId,
14341
+ decisionType: "build-verification-failure",
14342
+ severity: buildRouteResult.severity,
14343
+ defaultAction: buildRouteResult.defaultAction,
14344
+ reason: buildReason
14345
+ });
14287
14346
  eventBus.emit("story:build-verification-failed", {
14288
14347
  storyKey,
14289
14348
  exitCode: buildVerifyResult.exitCode ?? 1,
@@ -14292,7 +14351,9 @@ function createImplementationOrchestrator(deps) {
14292
14351
  logger$26.warn({
14293
14352
  storyKey,
14294
14353
  reason,
14295
- exitCode: buildVerifyResult.exitCode
14354
+ exitCode: buildVerifyResult.exitCode,
14355
+ routedHalt: buildRouteResult.halt,
14356
+ defaultAction: buildRouteResult.defaultAction
14296
14357
  }, "Build verification failed — escalating story");
14297
14358
  updateStory(storyKey, {
14298
14359
  phase: "ESCALATED",
@@ -15234,7 +15295,23 @@ function createImplementationOrchestrator(deps) {
15234
15295
  * @param manifest - The current run manifest data
15235
15296
  */
15236
15297
  async function handleCeilingExceeded(triggeredStoryKey, remainingInGroup, result, manifest) {
15237
- const haltOn = manifest.cli_flags.halt_on ?? "none";
15298
+ const haltPolicy = manifest.cli_flags.halt_on ?? "critical";
15299
+ const routeResult = routeDecision("cost-ceiling-exhausted", haltPolicy);
15300
+ const runId = config.pipelineRunId ?? "unknown";
15301
+ const reason = `cost ceiling exceeded: ${result.cumulative.toFixed(4)} USD >= ${result.ceiling} USD`;
15302
+ if (routeResult.halt) eventBus.emit("decision:halt", {
15303
+ runId,
15304
+ decisionType: "cost-ceiling-exhausted",
15305
+ severity: routeResult.severity,
15306
+ reason
15307
+ });
15308
+ else eventBus.emit("decision:autonomous", {
15309
+ runId,
15310
+ decisionType: "cost-ceiling-exhausted",
15311
+ severity: routeResult.severity,
15312
+ defaultAction: routeResult.defaultAction,
15313
+ reason
15314
+ });
15238
15315
  const allSkipped = [triggeredStoryKey, ...remainingInGroup];
15239
15316
  for (const [key, state] of _stories) if (state.phase === "PENDING" && !allSkipped.includes(key)) allSkipped.push(key);
15240
15317
  for (const key of allSkipped) {
@@ -15248,16 +15325,18 @@ function createImplementationOrchestrator(deps) {
15248
15325
  eventBus.emit("cost:ceiling-reached", {
15249
15326
  cumulative_cost: result.cumulative,
15250
15327
  ceiling: result.ceiling,
15251
- halt_on: haltOn,
15252
- action: "stopped",
15328
+ halt_on: haltPolicy,
15329
+ action: routeResult.halt ? "stopped" : routeResult.defaultAction,
15253
15330
  skipped_stories: allSkipped,
15254
- ...haltOn !== "none" ? { severity: "critical" } : {}
15331
+ severity: routeResult.severity
15255
15332
  });
15256
15333
  _budgetExhausted = true;
15257
15334
  logger$26.warn({
15258
15335
  skipped: allSkipped.length,
15259
15336
  cumulative: result.cumulative,
15260
- ceiling: result.ceiling
15337
+ ceiling: result.ceiling,
15338
+ routedHalt: routeResult.halt,
15339
+ defaultAction: routeResult.defaultAction
15261
15340
  }, "Cost ceiling reached — stopping dispatch");
15262
15341
  }
15263
15342
  /**
@@ -44204,6 +44283,17 @@ function wireNdjsonEmitter(eventBus, ndjsonEmitter) {
44204
44283
  ...payload.severity !== void 0 ? { severity: payload.severity } : {}
44205
44284
  });
44206
44285
  });
44286
+ eventBus.on("decision:halt-skipped-non-interactive", (payload) => {
44287
+ ndjsonEmitter.emit({
44288
+ type: "decision:halt-skipped-non-interactive",
44289
+ ts: new Date().toISOString(),
44290
+ run_id: payload.runId,
44291
+ decision_type: payload.decisionType,
44292
+ severity: payload.severity,
44293
+ default_action: payload.defaultAction,
44294
+ reason: payload.reason
44295
+ });
44296
+ });
44207
44297
  }
44208
44298
  /**
44209
44299
  * Resolve the `probeAuthorStateIntegrating` boolean from CLI flag and env var.
@@ -44220,8 +44310,20 @@ function resolveProbeAuthorStateIntegrating(cliFlag) {
44220
44310
  if (envVal !== void 0) return envVal === "on";
44221
44311
  return true;
44222
44312
  }
44313
+ /**
44314
+ * Story 72-2: --non-interactive + Machine-Readable Exit Codes.
44315
+ *
44316
+ * Phase D Story 54-6 (2026-04-05): original headless CI/CD spec.
44317
+ * Story 72-1: Decision Router providing routeDecision defaultAction authority.
44318
+ * Story 72-2 (this code): --non-interactive flag that suppresses stdin reads,
44319
+ * auto-applies default actions via routeDecision, and returns exit codes 0/1/2.
44320
+ *
44321
+ * Enables strata + agent-mesh cross-project CI/CD invocation:
44322
+ * substrate run --non-interactive --halt-on none --events --output-format json
44323
+ */
44223
44324
  async function runRunAction(options) {
44224
- const { pack: packName, from: startPhase, stopAfter, concept: conceptArg, conceptFile, stories: storiesArg, concurrency, outputFormat, projectRoot, events: eventsFlag, verbose: verboseFlag, tui: tuiFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, skipVerification, epic: epicNumber, dryRun, maxReviewCycles = 2, engine, agent: agentId, registry: injectedRegistry, haltOn, costCeiling, probeAuthor, probeAuthorStateIntegrating: probeAuthorStateIntegratingFlag } = options;
44325
+ const { pack: packName, from: startPhase, stopAfter, concept: conceptArg, conceptFile, stories: storiesArg, concurrency, outputFormat, projectRoot, events: eventsFlag, verbose: verboseFlag, tui: tuiFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, skipVerification, epic: epicNumber, dryRun, maxReviewCycles = 2, engine, agent: agentId, registry: injectedRegistry, haltOn: haltOnOpt, costCeiling, probeAuthor, probeAuthorStateIntegrating: probeAuthorStateIntegratingFlag, nonInteractive } = options;
44326
+ const haltOn = haltOnOpt;
44225
44327
  const VALID_PROBE_AUTHOR_MODES = [
44226
44328
  "enabled",
44227
44329
  "disabled",
@@ -44534,11 +44636,12 @@ async function runRunAction(options) {
44534
44636
  const runsDir = join(dbDir, "runs");
44535
44637
  const cliFlags = {
44536
44638
  ...parsedStoryKeys.length > 0 ? { stories: parsedStoryKeys } : {},
44537
- halt_on: haltOn ?? "none",
44639
+ halt_on: haltOn ?? "critical",
44538
44640
  ...costCeiling !== void 0 ? { cost_ceiling: costCeiling } : {},
44539
44641
  ...agentId !== void 0 ? { agent: agentId } : {},
44540
44642
  ...skipVerification === true ? { skip_verification: true } : {},
44541
- ...eventsFlag === true ? { events: true } : {}
44643
+ ...eventsFlag === true ? { events: true } : {},
44644
+ ...nonInteractive === true ? { non_interactive: true } : {}
44542
44645
  };
44543
44646
  const manifest = RunManifest.open(pipelineRun.id, runsDir);
44544
44647
  await manifest.patchCLIFlags(cliFlags);
@@ -44565,6 +44668,14 @@ async function runRunAction(options) {
44565
44668
  });
44566
44669
  } catch {}
44567
44670
  const eventBus = createEventBus();
44671
+ let _costCeilingExhausted = false;
44672
+ let _fatalHaltReached = false;
44673
+ eventBus.on("cost:ceiling-reached", () => {
44674
+ _costCeilingExhausted = true;
44675
+ });
44676
+ eventBus.on("decision:halt", (payload) => {
44677
+ if (payload.severity === "fatal") _fatalHaltReached = true;
44678
+ });
44568
44679
  const contextCompiler = createContextCompiler({ db: adapter });
44569
44680
  if (!injectedRegistry) throw new Error("AdapterRegistry is required — must be initialized at CLI startup");
44570
44681
  const routingConfigPath = join(projectRoot, "substrate.routing.yml");
@@ -44705,7 +44816,7 @@ async function runRunAction(options) {
44705
44816
  if (tuiFlag === true && !isTuiCapable()) printNonTtyWarning();
44706
44817
  if (verboseFlag !== true && eventsFlag !== true) process.env.LOG_LEVEL = "silent";
44707
44818
  let tuiApp;
44708
- if (tuiFlag === true && isTuiCapable() && eventsFlag !== true && outputFormat === "human") {
44819
+ if (tuiFlag === true && nonInteractive !== true && isTuiCapable() && eventsFlag !== true && outputFormat === "human") {
44709
44820
  tuiApp = createTuiApp(process.stdout, process.stdin);
44710
44821
  tuiApp.handleEvent({
44711
44822
  type: "pipeline:start",
@@ -45098,13 +45209,52 @@ async function runRunAction(options) {
45098
45209
  engineType: resolvedEngine,
45099
45210
  concurrency
45100
45211
  });
45212
+ if (nonInteractive === true) {
45213
+ if (escalatedKeys.length > 0 || _costCeilingExhausted || _fatalHaltReached) {
45214
+ const haltPolicy = haltOn ?? "critical";
45215
+ const routeResult = routeDecision("pipeline-escalation", haltPolicy);
45216
+ eventBus.emit("decision:halt-skipped-non-interactive", {
45217
+ runId: pipelineRun.id,
45218
+ decisionType: "pipeline-escalation",
45219
+ severity: routeResult.severity,
45220
+ defaultAction: routeResult.defaultAction,
45221
+ reason: "non-interactive: stdin prompt suppressed"
45222
+ });
45223
+ try {
45224
+ const runsDir = join(dbDir, "runs");
45225
+ const runManifestForHalt = RunManifest.open(pipelineRun.id, runsDir);
45226
+ await runManifestForHalt.update({ cli_flags: {
45227
+ halt_on: haltPolicy,
45228
+ halt_skipped: true,
45229
+ halt_skipped_decisions: [{
45230
+ decisionType: "pipeline-escalation",
45231
+ severity: routeResult.severity,
45232
+ defaultAction: routeResult.defaultAction,
45233
+ reason: "non-interactive: stdin prompt suppressed",
45234
+ skippedAt: new Date().toISOString()
45235
+ }]
45236
+ } });
45237
+ } catch (haltManifestErr) {
45238
+ logger.warn({ err: haltManifestErr }, "Failed to write halt-skipped to manifest (best-effort)");
45239
+ }
45240
+ }
45241
+ const derivedCode = deriveExitCode({
45242
+ succeeded: succeededKeys,
45243
+ escalated: escalatedKeys,
45244
+ failed: failedKeys,
45245
+ total: storyKeys.length,
45246
+ costCeilingExhausted: _costCeilingExhausted,
45247
+ fatalHaltReached: _fatalHaltReached
45248
+ });
45249
+ return derivedCode;
45250
+ }
45101
45251
  return 0;
45102
45252
  } catch (err) {
45103
45253
  const msg = err instanceof Error ? err.message : String(err);
45104
45254
  if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
45105
45255
  else process.stderr.write(`Error: ${msg}\n`);
45106
45256
  logger.error({ err }, "run failed");
45107
- return 1;
45257
+ return nonInteractive === true ? 2 : 1;
45108
45258
  } finally {
45109
45259
  try {
45110
45260
  await adapter.close();
@@ -45578,7 +45728,22 @@ async function runFullPipeline(options) {
45578
45728
  }
45579
45729
  }
45580
45730
  function registerRunCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
45581
- program.command("run").description("Run the autonomous pipeline (use --from to start from a specific phase)").option("--pack <name>", "Methodology pack name", "bmad").option("--from <phase>", "Start from this phase: analysis, planning, solutioning, implementation").option("--stop-after <phase>", "Stop pipeline after this phase completes").option("--concept <text>", "Inline concept text (required when --from analysis)").option("--concept-file <path>", "Path to a file containing the concept text").option("--stories <keys>", "Comma-separated story keys (e.g., 10-1,10-2)").option("--epic <n>", "Scope story discovery to a single epic number (e.g., 27)", (v) => parseInt(v, 10)).option("--concurrency <n>", "Maximum parallel conflict groups", (v) => parseInt(v, 10), 3).option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").option("--events", "Emit structured NDJSON events on stdout for programmatic consumption").option("--verbose", "Show detailed pino log output").option("--help-agent", "Print a machine-optimized prompt fragment for AI agents and exit").option("--tui", "Show TUI dashboard").option("--skip-ux", "Skip the UX design phase even if enabled in the pack manifest").option("--research", "Enable the research phase even if not set in the pack manifest").option("--skip-research", "Skip the research phase even if enabled in the pack manifest").option("--skip-preflight", "Skip the pre-flight build check (escape hatch for known-broken projects)").option("--skip-verification", "Skip the post-dispatch verification pipeline (Story 51-5)").option("--max-review-cycles <n>", "Maximum review cycles per story (default: 2)", (v) => parseInt(v, 10), 2).option("--dry-run", "Preview routing and repo-map injection without dispatching (Story 28-9)").option("--engine <type>", "Execution engine: linear (default) or graph").option("--agent <id>", "Agent backend: claude-code (default), codex, or gemini").option("--halt-on <severity>", "Halt pipeline on escalation severity: all | critical | none (default: none)", "none").option("--cost-ceiling <amount>", "Maximum cost ceiling in USD (positive number); halts pipeline when exceeded", parseFloat).option("--probe-author <mode>", "probe-author phase mode: enabled | disabled | auto (default: auto = SUBSTRATE_PROBE_AUTHOR_ENABLED env, default true) (Story 60-14)", "auto").option("--probe-author-state-integrating <value>", "Disable probe-author dispatch for state-integrating ACs (Phase 3). Use to ramp DOWN if catch rate drops below the GREEN threshold. Values: on | off (default: on)").action(async (opts) => {
45731
+ program.command("run").description("Run the autonomous pipeline (use --from to start from a specific phase)").option("--pack <name>", "Methodology pack name", "bmad").option("--from <phase>", "Start from this phase: analysis, planning, solutioning, implementation").option("--stop-after <phase>", "Stop pipeline after this phase completes").option("--concept <text>", "Inline concept text (required when --from analysis)").option("--concept-file <path>", "Path to a file containing the concept text").option("--stories <keys>", "Comma-separated story keys (e.g., 10-1,10-2)").option("--epic <n>", "Scope story discovery to a single epic number (e.g., 27)", (v) => parseInt(v, 10)).option("--concurrency <n>", "Maximum parallel conflict groups", (v) => parseInt(v, 10), 3).option("--project-root <path>", "Project root directory", projectRoot).option("--output-format <format>", "Output format: human (default) or json", "human").option("--events", "Emit structured NDJSON events on stdout for programmatic consumption").option("--verbose", "Show detailed pino log output").option("--help-agent", "Print a machine-optimized prompt fragment for AI agents and exit").option("--tui", "Show TUI dashboard").option("--skip-ux", "Skip the UX design phase even if enabled in the pack manifest").option("--research", "Enable the research phase even if not set in the pack manifest").option("--skip-research", "Skip the research phase even if enabled in the pack manifest").option("--skip-preflight", "Skip the pre-flight build check (escape hatch for known-broken projects)").option("--skip-verification", "Skip the post-dispatch verification pipeline (Story 51-5)").option("--max-review-cycles <n>", "Maximum review cycles per story (default: 2)", (v) => parseInt(v, 10), 2).option("--dry-run", "Preview routing and repo-map injection without dispatching (Story 28-9)").option("--engine <type>", "Execution engine: linear (default) or graph").option("--agent <id>", "Agent backend: claude-code (default), codex, or gemini").option("--halt-on <severity>", "Halt pipeline on escalation severity: all | critical | none (default: critical)", "critical").option("--cost-ceiling <amount>", "Maximum cost ceiling in USD (positive number); halts pipeline when exceeded", parseFloat).option("--probe-author <mode>", "probe-author phase mode: enabled | disabled | auto (default: auto = SUBSTRATE_PROBE_AUTHOR_ENABLED env, default true) (Story 60-14)", "auto").option("--probe-author-state-integrating <value>", "Disable probe-author dispatch for state-integrating ACs (Phase 3). Use to ramp DOWN if catch rate drops below the GREEN threshold. Values: on | off (default: on)").option("--non-interactive", [
45732
+ "Run without operator prompts (CI/CD mode). Suppresses all stdin reads,",
45733
+ "auto-applies default actions for halt decisions, and returns machine-readable",
45734
+ "exit codes: 0=all stories succeeded, 1=some escalated, 2=run-level failure.",
45735
+ "",
45736
+ "Canonical CI/CD invocation:",
45737
+ " substrate run --non-interactive --halt-on none --events --output-format json",
45738
+ "",
45739
+ "Exit code semantics:",
45740
+ " 0 All stories succeeded (or recovered cleanly)",
45741
+ " 1 Some stories escalated; run completed",
45742
+ " 2 Run-level failure (cost ceiling exhausted, fatal halt, orchestrator died)",
45743
+ "",
45744
+ "--halt-on defaults to critical. Skipped halt decisions are recorded as",
45745
+ "decision:halt-skipped-non-interactive events in --non-interactive mode."
45746
+ ].join("\n ")).action(async (opts) => {
45582
45747
  if (opts.helpAgent) {
45583
45748
  process.exitCode = await runHelpAgent();
45584
45749
  return;
@@ -45622,12 +45787,14 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
45622
45787
  haltOn: opts.haltOn,
45623
45788
  costCeiling: opts.costCeiling,
45624
45789
  probeAuthor: opts.probeAuthor,
45625
- probeAuthorStateIntegrating: opts.probeAuthorStateIntegrating
45790
+ probeAuthorStateIntegrating: opts.probeAuthorStateIntegrating,
45791
+ nonInteractive: opts.nonInteractive
45626
45792
  });
45793
+ if (opts.nonInteractive === true) process.exit(exitCode);
45627
45794
  process.exitCode = exitCode;
45628
45795
  });
45629
45796
  }
45630
45797
 
45631
45798
  //#endregion
45632
45799
  export { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GLOBSTAR$1 as GLOBSTAR, GitClient, GrammarLoader, Minimatch$1 as Minimatch, Minipass, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, escape$1 as escape, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, normalizeGraphSummaryToStatus, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runProbeAuthor, runRunAction, runSolutioningPhase, unescape$1 as unescape, validateStopAfterFromConflict, wireNdjsonEmitter };
45633
- //# sourceMappingURL=run-CHUFlRbH.js.map
45800
+ //# sourceMappingURL=run-CCxsv-9M.js.map
@@ -1,9 +1,10 @@
1
- import "./health-C-KOZrFJ.js";
1
+ import "./health-DudlnqXd.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./helpers-CElYrONe.js";
4
4
  import "./dist-W2emvN3F.js";
5
- import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-CHUFlRbH.js";
5
+ import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-CCxsv-9M.js";
6
6
  import "./routing-CcBOCuC9.js";
7
7
  import "./decisions-C0pz9Clx.js";
8
+ import "./decision-router-BA__VYIp.js";
8
9
 
9
10
  export { runRunAction };
@@ -0,0 +1,88 @@
1
+ //#region src/modules/decision-router/index.d.ts
2
+ /**
3
+ * Decision Router — Phase D Story 54-2 (2026-04-05): original autonomy-gradient spec.
4
+ * Story 72-1: --halt-on flag + Decision Router (this file).
5
+ * Classifies every halt-able decision by severity and enforces the chosen autonomy policy.
6
+ * Exports routeDecision(decision, policy) pure function for orchestrator consumption.
7
+ * Epic 70: cross-story-race decision types (cross-story-race-recovered,
8
+ * cross-story-race-still-failed) motivate the registry pattern — different halt semantics
9
+ * based on severity (info vs critical).
10
+ * Epic 73 (Recovery Engine) will register additional decision types via DECISION_SEVERITY_MAP.
11
+ *
12
+ * Story 72-2: --non-interactive flag consuming routeDecision for stdin suppression.
13
+ * Enables strata + agent-mesh cross-project CI/CD invocation.
14
+ */
15
+ /**
16
+ * Severity of a halt-able decision.
17
+ * Controls which autonomy policies trigger a halt.
18
+ */
19
+ type Severity = 'info' | 'warning' | 'critical' | 'fatal';
20
+ /**
21
+ * All known halt-able decision types (AC1).
22
+ * Epic 73 (Recovery Engine) will extend this union with additional types.
23
+ */
24
+ type DecisionType = 'cost-ceiling-exhausted' | 'build-verification-failure' | 'recovery-retry-attempt' | 're-scope-proposal' | 'scope-violation' | 'cross-story-race-recovered' | 'cross-story-race-still-failed' | 'pipeline-escalation';
25
+ /**
26
+ * Maps each known decision type to its severity level.
27
+ *
28
+ * Epic 70: cross-story-race-recovered (info — log only, no halt) and
29
+ * cross-story-race-still-failed (critical — recovery exhausted, halt for operator)
30
+ * motivate the registry pattern. Epic 73 (Recovery Engine) will register
31
+ * additional decision types here.
32
+ */
33
+ declare const DECISION_SEVERITY_MAP: Record<DecisionType, Severity>;
34
+ /**
35
+ * Route a halt-able decision through the autonomy policy.
36
+ *
37
+ * Pure function — no I/O, no side effects. All orchestrator state interactions
38
+ * remain in orchestrator-impl.ts.
39
+ *
40
+ * Halt policy logic (AC4):
41
+ * - 'all': halts on info | warning | critical | fatal
42
+ * - 'critical': halts on critical | fatal (default)
43
+ * - 'none': halts ONLY on fatal (scope violations bypass the autonomy-gradient
44
+ * policy — they are always halts regardless of the chosen policy)
45
+ *
46
+ * Fatal always halts regardless of policy — hard safety invariant, not configurable.
47
+ *
48
+ * Unknown decision types default to severity 'critical' (safe default, AC9e).
49
+ *
50
+ * @param decision - The decision type string to route
51
+ * @param policy - The autonomy policy from the --halt-on CLI flag
52
+ * @returns { halt: boolean, defaultAction: string, severity: Severity }
53
+ */
54
+ declare function routeDecision(decision: string, policy: 'all' | 'critical' | 'none'): {
55
+ halt: boolean;
56
+ defaultAction: string;
57
+ severity: Severity;
58
+ };
59
+ /**
60
+ * Inputs for exit code derivation from pipeline completion results.
61
+ * Used by the CLI layer to derive the machine-readable exit code (0/1/2)
62
+ * for non-interactive CI/CD invocations.
63
+ */
64
+ interface PipelineOutcome {
65
+ succeeded: string[];
66
+ recovered?: string[];
67
+ escalated: string[];
68
+ failed: string[];
69
+ total: number;
70
+ costCeilingExhausted?: boolean;
71
+ fatalHaltReached?: boolean;
72
+ orchestratorDied?: boolean;
73
+ }
74
+ /**
75
+ * Derive the machine-readable exit code from pipeline completion results.
76
+ *
77
+ * - Exit `0` when all stories succeeded (or recovered cleanly)
78
+ * - Exit `1` when some stories escalated; run completed
79
+ * - Exit `2` when run-level failure (cost ceiling, fatal halt, orchestrator died, stories failed)
80
+ *
81
+ * @param outcome - Pipeline completion outcome data
82
+ * @returns 0 (success), 1 (escalated), or 2 (failure)
83
+ */
84
+ declare function deriveExitCode(outcome: PipelineOutcome): 0 | 1 | 2; //#endregion
85
+
86
+ //# sourceMappingURL=index.d.ts.map
87
+ export { DECISION_SEVERITY_MAP, DecisionType, PipelineOutcome, Severity, deriveExitCode, routeDecision };
88
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ import { DECISION_SEVERITY_MAP, deriveExitCode, routeDecision } from "../../../decision-router-BA__VYIp.js";
2
+
3
+ export { DECISION_SEVERITY_MAP, deriveExitCode, routeDecision };
@@ -1,5 +1,5 @@
1
1
  import "./dist-W2emvN3F.js";
2
- import "./version-manager-impl-BmOWu8ml.js";
3
- import { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand } from "./upgrade-CAqLkNUP.js";
2
+ import "./version-manager-impl-FH4TTnXm.js";
3
+ import { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand } from "./upgrade-aW7GYL2F.js";
4
4
 
5
5
  export { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand };
@@ -123,4 +123,4 @@ function registerUpgradeCommand(program) {
123
123
 
124
124
  //#endregion
125
125
  export { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand };
126
- //# sourceMappingURL=upgrade-CAqLkNUP.js.map
126
+ //# sourceMappingURL=upgrade-aW7GYL2F.js.map
@@ -1,4 +1,4 @@
1
1
  import { VersionManagerImpl, createVersionManager } from "./dist-W2emvN3F.js";
2
- import "./version-manager-impl-BmOWu8ml.js";
2
+ import "./version-manager-impl-FH4TTnXm.js";
3
3
 
4
4
  export { createVersionManager };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.20.63",
3
+ "version": "0.20.64",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",