substrate-ai 0.20.62 → 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 +10 -9
- package/dist/decision-router-BA__VYIp.js +97 -0
- package/dist/{health-FZVOBYND.js → health-CLNmnZiw.js} +1 -1
- package/dist/{health-BoXxsFSF.js → health-DudlnqXd.js} +237 -5
- package/dist/index.d.ts +104 -1
- package/dist/{run-CRz08RrU.js → run-CCxsv-9M.js} +213 -17
- package/dist/{run-BxfeSz6G.js → run-ChxsPICN.js} +3 -2
- package/dist/src/modules/decision-router/index.d.ts +88 -0
- package/dist/src/modules/decision-router/index.js +3 -0
- package/dist/{upgrade-C8LAnB6W.js → upgrade-OFeC_NIx.js} +2 -2
- package/dist/{upgrade-CAqLkNUP.js → upgrade-aW7GYL2F.js} +1 -1
- package/dist/{version-manager-impl-DaA_ALYK.js → version-manager-impl-BCSf5E3j.js} +1 -1
- package/package.json +1 -1
- /package/dist/{experimenter-DxxwicpK.js → experimenter-BgpUcUaW.js} +0 -0
- /package/dist/{version-manager-impl-BmOWu8ml.js → version-manager-impl-FH4TTnXm.js} +0 -0
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-
|
|
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-
|
|
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 "../
|
|
12
|
-
import
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
14039
|
-
const { createVersionManager } = await import("../version-manager-impl-
|
|
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-
|
|
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";
|
|
@@ -7,7 +7,7 @@ import { readFile } from "fs/promises";
|
|
|
7
7
|
import { EventEmitter } from "node:events";
|
|
8
8
|
import { YAMLException, load } from "js-yaml";
|
|
9
9
|
import { existsSync, promises, readFileSync, readdirSync, statSync } from "node:fs";
|
|
10
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
10
|
+
import { execSync, spawn, spawnSync } from "node:child_process";
|
|
11
11
|
import * as path$2 from "node:path";
|
|
12
12
|
import { basename as basename$1, dirname as dirname$1, join as join$1, resolve as resolve$1 } from "node:path";
|
|
13
13
|
import { z } from "zod";
|
|
@@ -5548,6 +5548,227 @@ function createDefaultVerificationPipeline(bus, config) {
|
|
|
5548
5548
|
return new VerificationPipeline(bus, checks);
|
|
5549
5549
|
}
|
|
5550
5550
|
|
|
5551
|
+
//#endregion
|
|
5552
|
+
//#region packages/sdlc/dist/verification/cross-story-race-recovery.js
|
|
5553
|
+
/**
|
|
5554
|
+
* Detect stories whose verification results were recorded before concurrent
|
|
5555
|
+
* story commits landed.
|
|
5556
|
+
*
|
|
5557
|
+
* Stale-verification boundary condition:
|
|
5558
|
+
* t.committedAt > s.verifiedAt
|
|
5559
|
+
* AND
|
|
5560
|
+
* t.modifiedFiles ∩ (s.modifiedFiles ∪ s.testFiles) ≠ ∅
|
|
5561
|
+
*
|
|
5562
|
+
* Where:
|
|
5563
|
+
* s = the story we're checking for staleness
|
|
5564
|
+
* t = another story in the batch that may have committed after s verified
|
|
5565
|
+
*
|
|
5566
|
+
* Pure function — all data must be pre-resolved in the `batch` entries.
|
|
5567
|
+
* Returns empty array when no stale stories are detected (idempotent, no-op).
|
|
5568
|
+
*
|
|
5569
|
+
* @param batch Pre-resolved story data for all stories in the concurrent batch.
|
|
5570
|
+
* @param _manifest Manifest instance (accepted for API symmetry; not used in pure logic).
|
|
5571
|
+
* @returns Array of story keys that have stale verification results.
|
|
5572
|
+
*/
|
|
5573
|
+
function detectStaleVerifications(batch, _manifest) {
|
|
5574
|
+
if (batch.length < 2) return [];
|
|
5575
|
+
const staleKeys = [];
|
|
5576
|
+
for (const s of batch) {
|
|
5577
|
+
const sVerifiedAt = s.verifiedAt;
|
|
5578
|
+
if (sVerifiedAt === void 0) continue;
|
|
5579
|
+
const sPrimaryFiles = s.modifiedFiles ?? s.verificationResultFiles ?? [];
|
|
5580
|
+
const sTestFiles = s.testFiles ?? [];
|
|
5581
|
+
const sAllFiles = new Set([...sPrimaryFiles, ...sTestFiles]);
|
|
5582
|
+
if (sAllFiles.size === 0) continue;
|
|
5583
|
+
for (const t of batch) {
|
|
5584
|
+
if (t.storyKey === s.storyKey) continue;
|
|
5585
|
+
const tCommittedAt = t.committedAt;
|
|
5586
|
+
if (tCommittedAt === void 0) continue;
|
|
5587
|
+
let tTime, sTime;
|
|
5588
|
+
try {
|
|
5589
|
+
tTime = new Date(tCommittedAt).getTime();
|
|
5590
|
+
sTime = new Date(sVerifiedAt).getTime();
|
|
5591
|
+
} catch {
|
|
5592
|
+
continue;
|
|
5593
|
+
}
|
|
5594
|
+
if (isNaN(tTime) || isNaN(sTime)) continue;
|
|
5595
|
+
if (tTime <= sTime) continue;
|
|
5596
|
+
const tFiles = t.modifiedFiles ?? [];
|
|
5597
|
+
const hasOverlap = tFiles.some((f) => sAllFiles.has(f));
|
|
5598
|
+
if (hasOverlap) {
|
|
5599
|
+
staleKeys.push(s.storyKey);
|
|
5600
|
+
break;
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
return staleKeys;
|
|
5605
|
+
}
|
|
5606
|
+
/**
|
|
5607
|
+
* Resolves the commit timestamp for a story's implementation commit.
|
|
5608
|
+
*
|
|
5609
|
+
* Uses the canonical `feat(story-<storyKey>):` commit message pattern to
|
|
5610
|
+
* locate the story's auto-commit in git history.
|
|
5611
|
+
*
|
|
5612
|
+
* @param storyKey Story key (e.g. "70-1")
|
|
5613
|
+
* @param workingDir Absolute path to the git repo root
|
|
5614
|
+
* @returns ISO-8601 commit timestamp string, or `undefined` if not found
|
|
5615
|
+
*/
|
|
5616
|
+
function CommittedAtResolver(storyKey, workingDir) {
|
|
5617
|
+
try {
|
|
5618
|
+
const result = execSync(`git log --format=%cI --grep="feat(story-${storyKey}):" -1`, {
|
|
5619
|
+
cwd: workingDir,
|
|
5620
|
+
encoding: "utf-8",
|
|
5621
|
+
stdio: [
|
|
5622
|
+
"ignore",
|
|
5623
|
+
"pipe",
|
|
5624
|
+
"pipe"
|
|
5625
|
+
]
|
|
5626
|
+
}).trim();
|
|
5627
|
+
return result.length > 0 ? result : void 0;
|
|
5628
|
+
} catch {
|
|
5629
|
+
return void 0;
|
|
5630
|
+
}
|
|
5631
|
+
}
|
|
5632
|
+
/**
|
|
5633
|
+
* Cross-story race recovery action handler.
|
|
5634
|
+
*
|
|
5635
|
+
* Orchestrates the full recovery arc for stories with stale verification
|
|
5636
|
+
* results caused by concurrent story commits landing after verification ran:
|
|
5637
|
+
*
|
|
5638
|
+
* 1. Resolves `committedAt` for each batch story via git log.
|
|
5639
|
+
* 2. Enriches BatchEntry[] from manifest per_story_state when fields absent.
|
|
5640
|
+
* 3. Calls `detectStaleVerifications` to identify stale story keys.
|
|
5641
|
+
* 4. Returns `{ noStale: true }` immediately when none are stale (idempotent).
|
|
5642
|
+
* 5. For each stale story:
|
|
5643
|
+
* a. Transitions status to `verification-stale` in manifest.
|
|
5644
|
+
* b. Re-runs `createDefaultVerificationPipeline` against current tree.
|
|
5645
|
+
* c. On pass: transitions to `complete`, emits `pipeline:cross-story-race-recovered`.
|
|
5646
|
+
* d. On fail: transitions to `failed` with `verification_re_run: true`,
|
|
5647
|
+
* emits `pipeline:cross-story-race-still-failed`.
|
|
5648
|
+
*
|
|
5649
|
+
* Implements AC3 canonical-helper discipline: all state reads use RunManifest,
|
|
5650
|
+
* all persistence uses the injected adapter. No direct file reads of manifest.json.
|
|
5651
|
+
*
|
|
5652
|
+
* @param input Recovery input (see StaleVerificationRecoveryInput)
|
|
5653
|
+
* @returns Recovery summary
|
|
5654
|
+
*/
|
|
5655
|
+
async function runStaleVerificationRecovery(input) {
|
|
5656
|
+
const { runId, batch, workingDir, bus, manifest } = input;
|
|
5657
|
+
const enrichedBatch = [];
|
|
5658
|
+
let manifestData = null;
|
|
5659
|
+
try {
|
|
5660
|
+
manifestData = await manifest.read();
|
|
5661
|
+
} catch {}
|
|
5662
|
+
for (const entry of batch) {
|
|
5663
|
+
const committedAt = CommittedAtResolver(entry.storyKey, workingDir);
|
|
5664
|
+
let verifiedAt = entry.verifiedAt;
|
|
5665
|
+
let modifiedFiles = entry.modifiedFiles;
|
|
5666
|
+
let testFiles = entry.testFiles;
|
|
5667
|
+
let verificationResultFiles = entry.verificationResultFiles;
|
|
5668
|
+
if (manifestData !== null) {
|
|
5669
|
+
const perStory = manifestData.per_story_state[entry.storyKey];
|
|
5670
|
+
if (perStory !== void 0) {
|
|
5671
|
+
if (verifiedAt === void 0) verifiedAt = perStory.completed_at;
|
|
5672
|
+
if (modifiedFiles === void 0 && perStory.dev_story_signals?.files_modified !== void 0) modifiedFiles = perStory.dev_story_signals.files_modified;
|
|
5673
|
+
if (verificationResultFiles === void 0 && perStory.dev_story_signals?.files_modified !== void 0) verificationResultFiles = perStory.dev_story_signals.files_modified;
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5676
|
+
const enriched = { storyKey: entry.storyKey };
|
|
5677
|
+
if (committedAt !== void 0) enriched.committedAt = committedAt;
|
|
5678
|
+
if (verifiedAt !== void 0) enriched.verifiedAt = verifiedAt;
|
|
5679
|
+
if (modifiedFiles !== void 0) enriched.modifiedFiles = modifiedFiles;
|
|
5680
|
+
if (testFiles !== void 0) enriched.testFiles = testFiles;
|
|
5681
|
+
if (verificationResultFiles !== void 0) enriched.verificationResultFiles = verificationResultFiles;
|
|
5682
|
+
enrichedBatch.push(enriched);
|
|
5683
|
+
}
|
|
5684
|
+
const staleKeys = detectStaleVerifications(enrichedBatch, manifest);
|
|
5685
|
+
if (staleKeys.length === 0) return {
|
|
5686
|
+
recovered: [],
|
|
5687
|
+
stillFailed: [],
|
|
5688
|
+
noStale: true
|
|
5689
|
+
};
|
|
5690
|
+
const recovered = [];
|
|
5691
|
+
const stillFailed = [];
|
|
5692
|
+
const verificationPipeline = createDefaultVerificationPipeline(bus);
|
|
5693
|
+
for (const storyKey of staleKeys) {
|
|
5694
|
+
const recoveryStart = Date.now();
|
|
5695
|
+
let originalFindings = [];
|
|
5696
|
+
if (manifestData !== null) {
|
|
5697
|
+
const perStory = manifestData.per_story_state[storyKey];
|
|
5698
|
+
if (perStory?.verification_result !== void 0) originalFindings = (perStory.verification_result.checks ?? []).flatMap((c) => c.findings ?? []);
|
|
5699
|
+
}
|
|
5700
|
+
await manifest.patchStoryState(storyKey, { status: "verification-stale" }).catch(() => {});
|
|
5701
|
+
let commitSha;
|
|
5702
|
+
try {
|
|
5703
|
+
commitSha = execSync("git rev-parse HEAD", {
|
|
5704
|
+
cwd: workingDir,
|
|
5705
|
+
encoding: "utf-8",
|
|
5706
|
+
stdio: [
|
|
5707
|
+
"ignore",
|
|
5708
|
+
"pipe",
|
|
5709
|
+
"pipe"
|
|
5710
|
+
]
|
|
5711
|
+
}).trim();
|
|
5712
|
+
} catch {
|
|
5713
|
+
commitSha = "unknown";
|
|
5714
|
+
}
|
|
5715
|
+
let storyContent;
|
|
5716
|
+
try {
|
|
5717
|
+
const artifactsDir = `${workingDir}/_bmad-output/implementation-artifacts`;
|
|
5718
|
+
if (existsSync(artifactsDir)) {
|
|
5719
|
+
const STALE_SUFFIX = /\.stale-\d+\.md$/;
|
|
5720
|
+
const files = readdirSync(artifactsDir);
|
|
5721
|
+
const match = files.find((f) => f.startsWith(`${storyKey}-`) && f.endsWith(".md") && !STALE_SUFFIX.test(f));
|
|
5722
|
+
if (match !== void 0) storyContent = readFileSync(`${artifactsDir}/${match}`, "utf-8");
|
|
5723
|
+
}
|
|
5724
|
+
} catch {}
|
|
5725
|
+
const verifContext = {
|
|
5726
|
+
storyKey,
|
|
5727
|
+
workingDir,
|
|
5728
|
+
commitSha,
|
|
5729
|
+
timeout: 6e4,
|
|
5730
|
+
...storyContent !== void 0 ? { storyContent } : {}
|
|
5731
|
+
};
|
|
5732
|
+
const freshSummary = await verificationPipeline.run(verifContext, "A");
|
|
5733
|
+
const freshFindings = (freshSummary.checks ?? []).flatMap((c) => c.findings ?? []);
|
|
5734
|
+
const recoveryDurationMs = Date.now() - recoveryStart;
|
|
5735
|
+
if (freshSummary.status === "pass" || freshSummary.status === "warn") {
|
|
5736
|
+
await manifest.patchStoryState(storyKey, {
|
|
5737
|
+
status: "complete",
|
|
5738
|
+
verification_result: freshSummary,
|
|
5739
|
+
completed_at: new Date().toISOString()
|
|
5740
|
+
}).catch(() => {});
|
|
5741
|
+
bus.emit("pipeline:cross-story-race-recovered", {
|
|
5742
|
+
runId,
|
|
5743
|
+
storyKey,
|
|
5744
|
+
originalFindings,
|
|
5745
|
+
freshFindings,
|
|
5746
|
+
recoveryDurationMs
|
|
5747
|
+
});
|
|
5748
|
+
recovered.push(storyKey);
|
|
5749
|
+
} else {
|
|
5750
|
+
await manifest.patchStoryState(storyKey, {
|
|
5751
|
+
status: "failed",
|
|
5752
|
+
verification_result: freshSummary,
|
|
5753
|
+
verification_re_run: true,
|
|
5754
|
+
completed_at: new Date().toISOString()
|
|
5755
|
+
}).catch(() => {});
|
|
5756
|
+
bus.emit("pipeline:cross-story-race-still-failed", {
|
|
5757
|
+
runId,
|
|
5758
|
+
storyKey,
|
|
5759
|
+
freshFindings,
|
|
5760
|
+
recoveryDurationMs
|
|
5761
|
+
});
|
|
5762
|
+
stillFailed.push(storyKey);
|
|
5763
|
+
}
|
|
5764
|
+
}
|
|
5765
|
+
return {
|
|
5766
|
+
recovered,
|
|
5767
|
+
stillFailed,
|
|
5768
|
+
noStale: false
|
|
5769
|
+
};
|
|
5770
|
+
}
|
|
5771
|
+
|
|
5551
5772
|
//#endregion
|
|
5552
5773
|
//#region packages/sdlc/dist/run-model/cli-flags.js
|
|
5553
5774
|
/**
|
|
@@ -5566,7 +5787,16 @@ const CliFlagsSchema = z.object({
|
|
|
5566
5787
|
cost_ceiling: z.number().positive().optional(),
|
|
5567
5788
|
agent: z.string().optional(),
|
|
5568
5789
|
skip_verification: z.boolean().optional(),
|
|
5569
|
-
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()
|
|
5570
5800
|
});
|
|
5571
5801
|
|
|
5572
5802
|
//#endregion
|
|
@@ -5704,6 +5934,7 @@ const PerStoryStatusSchema = z.union([
|
|
|
5704
5934
|
z.literal("verification-failed"),
|
|
5705
5935
|
z.literal("gated"),
|
|
5706
5936
|
z.literal("skipped"),
|
|
5937
|
+
z.literal("verification-stale"),
|
|
5707
5938
|
z.string()
|
|
5708
5939
|
]);
|
|
5709
5940
|
/**
|
|
@@ -5735,7 +5966,8 @@ const PerStoryStateSchema = z.object({
|
|
|
5735
5966
|
z.literal("state-integrating"),
|
|
5736
5967
|
z.literal("both"),
|
|
5737
5968
|
z.string()
|
|
5738
|
-
]).optional()
|
|
5969
|
+
]).optional(),
|
|
5970
|
+
verification_re_run: z.boolean().optional()
|
|
5739
5971
|
});
|
|
5740
5972
|
|
|
5741
5973
|
//#endregion
|
|
@@ -7340,5 +7572,5 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
7340
7572
|
}
|
|
7341
7573
|
|
|
7342
7574
|
//#endregion
|
|
7343
|
-
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, validateStoryKey };
|
|
7344
|
-
//# sourceMappingURL=health-
|
|
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 };
|
|
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
|
/**
|
|
@@ -2479,6 +2501,87 @@ interface OrchestratorEvents {
|
|
|
2479
2501
|
/** Hash computed from the current source epic's AC section */
|
|
2480
2502
|
currentHash: string;
|
|
2481
2503
|
};
|
|
2504
|
+
/**
|
|
2505
|
+
* Cross-story race recovery succeeded: fresh verification passed for a story
|
|
2506
|
+
* whose original result was stale due to a concurrent story committing after
|
|
2507
|
+
* the original verification ran.
|
|
2508
|
+
*
|
|
2509
|
+
* Story 70-1. Motivating incidents: Epic 66 (run a832487a), Epic 67 (run a59e4c96).
|
|
2510
|
+
* Mirror of CoreEvents['pipeline:cross-story-race-recovered']; both must stay in sync.
|
|
2511
|
+
*/
|
|
2512
|
+
'pipeline:cross-story-race-recovered': {
|
|
2513
|
+
runId: string;
|
|
2514
|
+
storyKey: string;
|
|
2515
|
+
originalFindings: unknown[];
|
|
2516
|
+
freshFindings: unknown[];
|
|
2517
|
+
recoveryDurationMs: number;
|
|
2518
|
+
};
|
|
2519
|
+
/**
|
|
2520
|
+
* Cross-story race recovery completed but fresh verification still failed:
|
|
2521
|
+
* the story genuinely has issues that are not attributable to the race condition.
|
|
2522
|
+
*
|
|
2523
|
+
* Story 70-1. Motivating incidents: Epic 66 (run a832487a), Epic 67 (run a59e4c96).
|
|
2524
|
+
* Mirror of CoreEvents['pipeline:cross-story-race-still-failed']; both must stay in sync.
|
|
2525
|
+
*/
|
|
2526
|
+
'pipeline:cross-story-race-still-failed': {
|
|
2527
|
+
runId: string;
|
|
2528
|
+
storyKey: string;
|
|
2529
|
+
freshFindings: unknown[];
|
|
2530
|
+
recoveryDurationMs: number;
|
|
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
|
+
};
|
|
2482
2585
|
}
|
|
2483
2586
|
|
|
2484
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, validateStoryKey } from "./health-
|
|
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
|
|
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: "
|
|
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
|
|
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:
|
|
15252
|
-
action: "stopped",
|
|
15328
|
+
halt_on: haltPolicy,
|
|
15329
|
+
action: routeResult.halt ? "stopped" : routeResult.defaultAction,
|
|
15253
15330
|
skipped_stories: allSkipped,
|
|
15254
|
-
|
|
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
|
/**
|
|
@@ -15745,7 +15824,36 @@ function createImplementationOrchestrator(deps) {
|
|
|
15745
15824
|
try {
|
|
15746
15825
|
for (const rawBatchGroups of batches) {
|
|
15747
15826
|
const batchGroups = detectAndSerializeConcurrentFileCollisions(rawBatchGroups);
|
|
15827
|
+
let collisionFired = false;
|
|
15828
|
+
const collisionListener = (_evt) => {
|
|
15829
|
+
collisionFired = true;
|
|
15830
|
+
};
|
|
15831
|
+
eventBus.on("dispatch:cross-story-file-collision", collisionListener);
|
|
15748
15832
|
await runWithConcurrency(batchGroups, config.maxConcurrency);
|
|
15833
|
+
eventBus.off("dispatch:cross-story-file-collision", collisionListener);
|
|
15834
|
+
if (collisionFired && runManifest !== null && runManifest !== void 0) {
|
|
15835
|
+
const batchStoryKeys = batchGroups.flat();
|
|
15836
|
+
const batchEntries = batchStoryKeys.map((key) => ({ storyKey: key }));
|
|
15837
|
+
const runId = config.pipelineRunId ?? "unknown";
|
|
15838
|
+
try {
|
|
15839
|
+
const recoveryResult = await runStaleVerificationRecovery({
|
|
15840
|
+
runId,
|
|
15841
|
+
batch: batchEntries,
|
|
15842
|
+
workingDir: projectRoot ?? process.cwd(),
|
|
15843
|
+
bus: toSdlcEventBus(eventBus),
|
|
15844
|
+
manifest: runManifest,
|
|
15845
|
+
adapter: db
|
|
15846
|
+
});
|
|
15847
|
+
if (!recoveryResult.noStale) logger$26.info({
|
|
15848
|
+
recovered: recoveryResult.recovered,
|
|
15849
|
+
stillFailed: recoveryResult.stillFailed,
|
|
15850
|
+
recoveredCount: recoveryResult.recovered.length,
|
|
15851
|
+
stillFailedCount: recoveryResult.stillFailed.length
|
|
15852
|
+
}, "Cross-story race recovery complete");
|
|
15853
|
+
} catch (recoveryErr) {
|
|
15854
|
+
logger$26.warn({ err: recoveryErr }, "Cross-story race recovery failed (non-fatal) — pipeline continues");
|
|
15855
|
+
}
|
|
15856
|
+
}
|
|
15749
15857
|
}
|
|
15750
15858
|
} catch (err) {
|
|
15751
15859
|
stopHeartbeat();
|
|
@@ -44175,6 +44283,17 @@ function wireNdjsonEmitter(eventBus, ndjsonEmitter) {
|
|
|
44175
44283
|
...payload.severity !== void 0 ? { severity: payload.severity } : {}
|
|
44176
44284
|
});
|
|
44177
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
|
+
});
|
|
44178
44297
|
}
|
|
44179
44298
|
/**
|
|
44180
44299
|
* Resolve the `probeAuthorStateIntegrating` boolean from CLI flag and env var.
|
|
@@ -44191,8 +44310,20 @@ function resolveProbeAuthorStateIntegrating(cliFlag) {
|
|
|
44191
44310
|
if (envVal !== void 0) return envVal === "on";
|
|
44192
44311
|
return true;
|
|
44193
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
|
+
*/
|
|
44194
44324
|
async function runRunAction(options) {
|
|
44195
|
-
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;
|
|
44196
44327
|
const VALID_PROBE_AUTHOR_MODES = [
|
|
44197
44328
|
"enabled",
|
|
44198
44329
|
"disabled",
|
|
@@ -44505,11 +44636,12 @@ async function runRunAction(options) {
|
|
|
44505
44636
|
const runsDir = join(dbDir, "runs");
|
|
44506
44637
|
const cliFlags = {
|
|
44507
44638
|
...parsedStoryKeys.length > 0 ? { stories: parsedStoryKeys } : {},
|
|
44508
|
-
halt_on: haltOn ?? "
|
|
44639
|
+
halt_on: haltOn ?? "critical",
|
|
44509
44640
|
...costCeiling !== void 0 ? { cost_ceiling: costCeiling } : {},
|
|
44510
44641
|
...agentId !== void 0 ? { agent: agentId } : {},
|
|
44511
44642
|
...skipVerification === true ? { skip_verification: true } : {},
|
|
44512
|
-
...eventsFlag === true ? { events: true } : {}
|
|
44643
|
+
...eventsFlag === true ? { events: true } : {},
|
|
44644
|
+
...nonInteractive === true ? { non_interactive: true } : {}
|
|
44513
44645
|
};
|
|
44514
44646
|
const manifest = RunManifest.open(pipelineRun.id, runsDir);
|
|
44515
44647
|
await manifest.patchCLIFlags(cliFlags);
|
|
@@ -44536,6 +44668,14 @@ async function runRunAction(options) {
|
|
|
44536
44668
|
});
|
|
44537
44669
|
} catch {}
|
|
44538
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
|
+
});
|
|
44539
44679
|
const contextCompiler = createContextCompiler({ db: adapter });
|
|
44540
44680
|
if (!injectedRegistry) throw new Error("AdapterRegistry is required — must be initialized at CLI startup");
|
|
44541
44681
|
const routingConfigPath = join(projectRoot, "substrate.routing.yml");
|
|
@@ -44676,7 +44816,7 @@ async function runRunAction(options) {
|
|
|
44676
44816
|
if (tuiFlag === true && !isTuiCapable()) printNonTtyWarning();
|
|
44677
44817
|
if (verboseFlag !== true && eventsFlag !== true) process.env.LOG_LEVEL = "silent";
|
|
44678
44818
|
let tuiApp;
|
|
44679
|
-
if (tuiFlag === true && isTuiCapable() && eventsFlag !== true && outputFormat === "human") {
|
|
44819
|
+
if (tuiFlag === true && nonInteractive !== true && isTuiCapable() && eventsFlag !== true && outputFormat === "human") {
|
|
44680
44820
|
tuiApp = createTuiApp(process.stdout, process.stdin);
|
|
44681
44821
|
tuiApp.handleEvent({
|
|
44682
44822
|
type: "pipeline:start",
|
|
@@ -45069,13 +45209,52 @@ async function runRunAction(options) {
|
|
|
45069
45209
|
engineType: resolvedEngine,
|
|
45070
45210
|
concurrency
|
|
45071
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
|
+
}
|
|
45072
45251
|
return 0;
|
|
45073
45252
|
} catch (err) {
|
|
45074
45253
|
const msg = err instanceof Error ? err.message : String(err);
|
|
45075
45254
|
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
45076
45255
|
else process.stderr.write(`Error: ${msg}\n`);
|
|
45077
45256
|
logger.error({ err }, "run failed");
|
|
45078
|
-
return 1;
|
|
45257
|
+
return nonInteractive === true ? 2 : 1;
|
|
45079
45258
|
} finally {
|
|
45080
45259
|
try {
|
|
45081
45260
|
await adapter.close();
|
|
@@ -45549,7 +45728,22 @@ async function runFullPipeline(options) {
|
|
|
45549
45728
|
}
|
|
45550
45729
|
}
|
|
45551
45730
|
function registerRunCommand(program, _version = "0.0.0", projectRoot = process.cwd(), registry) {
|
|
45552
|
-
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:
|
|
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) => {
|
|
45553
45747
|
if (opts.helpAgent) {
|
|
45554
45748
|
process.exitCode = await runHelpAgent();
|
|
45555
45749
|
return;
|
|
@@ -45593,12 +45787,14 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
45593
45787
|
haltOn: opts.haltOn,
|
|
45594
45788
|
costCeiling: opts.costCeiling,
|
|
45595
45789
|
probeAuthor: opts.probeAuthor,
|
|
45596
|
-
probeAuthorStateIntegrating: opts.probeAuthorStateIntegrating
|
|
45790
|
+
probeAuthorStateIntegrating: opts.probeAuthorStateIntegrating,
|
|
45791
|
+
nonInteractive: opts.nonInteractive
|
|
45597
45792
|
});
|
|
45793
|
+
if (opts.nonInteractive === true) process.exit(exitCode);
|
|
45598
45794
|
process.exitCode = exitCode;
|
|
45599
45795
|
});
|
|
45600
45796
|
}
|
|
45601
45797
|
|
|
45602
45798
|
//#endregion
|
|
45603
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 };
|
|
45604
|
-
//# sourceMappingURL=run-
|
|
45800
|
+
//# sourceMappingURL=run-CCxsv-9M.js.map
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import "./health-
|
|
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-
|
|
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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "./dist-W2emvN3F.js";
|
|
2
|
-
import "./version-manager-impl-
|
|
3
|
-
import { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand } from "./upgrade-
|
|
2
|
+
import "./version-manager-impl-FH4TTnXm.js";
|
|
3
|
+
import { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand } from "./upgrade-aW7GYL2F.js";
|
|
4
4
|
|
|
5
5
|
export { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand };
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|