substrate-ai 0.20.63 → 0.20.65
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/adapter-registry-BbVWH3Yv.js +4 -0
- package/dist/cli/index.js +93 -23
- package/dist/decision-router-DblHY8se.js +97 -0
- package/dist/{decisions-4F91LrVD.js → decisions-DilHo99V.js} +2 -2
- package/dist/{dist-W2emvN3F.js → dist-K_RRWnBX.js} +2 -2
- package/dist/{errors-CKFu8YI9.js → errors-pSiZbn6e.js} +2 -2
- package/dist/{experimenter-DxxwicpK.js → experimenter-DT9v2Pto.js} +1 -1
- package/dist/health-DC3y-sR6.js +1715 -0
- package/dist/health-qhtWYh49.js +8 -0
- package/dist/index-c924O9mj.d.ts +1432 -0
- package/dist/index.d.ts +132 -736
- package/dist/index.js +2 -2
- package/dist/interactive-prompt-C7wpE4z4.js +183 -0
- package/dist/{health-C-KOZrFJ.js → manifest-read-DDkXC3L_.js} +130 -2013
- package/dist/modules/interactive-prompt/index.d.ts +86 -0
- package/dist/modules/interactive-prompt/index.js +6 -0
- package/dist/recovery-engine-BKGBeBnW.js +281 -0
- package/dist/{routing-0ykvBl_4.js → routing-CzF0p6lI.js} +2 -2
- package/dist/run-DX95j4_D.js +14 -0
- package/dist/{run-CHUFlRbH.js → run-DzB4rgkj.js} +391 -31
- package/dist/src/modules/decision-router/index.d.ts +88 -0
- package/dist/src/modules/decision-router/index.js +3 -0
- package/dist/src/modules/recovery-engine/index.d.ts +1101 -0
- package/dist/src/modules/recovery-engine/index.js +5 -0
- package/dist/{upgrade-C8LAnB6W.js → upgrade-DxzQ1nss.js} +3 -3
- package/dist/{upgrade-CAqLkNUP.js → upgrade-MP9XzrI6.js} +2 -2
- package/dist/version-manager-impl-GZDUBt0Q.js +4 -0
- package/dist/work-graph-repository-DZyJv5pV.js +265 -0
- package/package.json +1 -1
- package/dist/adapter-registry-k7ZX3Bz6.js +0 -4
- package/dist/health-CJqd1FzY.js +0 -6
- package/dist/run-Z_-caE_i.js +0 -9
- package/dist/version-manager-impl-DaA_ALYK.js +0 -4
- /package/dist/{decisions-C0pz9Clx.js → decisions-CzSIEeGP.js} +0 -0
- /package/dist/{routing-CcBOCuC9.js → routing-DFxoKHDt.js} +0 -0
- /package/dist/{version-manager-impl-BmOWu8ml.js → version-manager-impl-qFBiO4Eh.js} +0 -0
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore,
|
|
1
|
+
import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, validateStoryKey } from "./health-DC3y-sR6.js";
|
|
2
2
|
import { createLogger } from "./logger-KeHncl-f.js";
|
|
3
3
|
import { TypedEventBusImpl, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning, sleep } from "./helpers-CElYrONe.js";
|
|
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-
|
|
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-K_RRWnBX.js";
|
|
5
|
+
import { FindingsInjector, RunManifest, RuntimeProbeListSchema, applyConfigToGraph, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, renderFindings, resolveGraphPath, resolveMainRepoRoot, runStaleVerificationRecovery } from "./manifest-read-DDkXC3L_.js";
|
|
6
|
+
import { WorkGraphRepository, detectCycles } from "./work-graph-repository-DZyJv5pV.js";
|
|
7
|
+
import { deriveExitCode, routeDecision } from "./decision-router-DblHY8se.js";
|
|
8
|
+
import { runInteractivePrompt } from "./interactive-prompt-C7wpE4z4.js";
|
|
9
|
+
import { runRecoveryEngine } from "./recovery-engine-BKGBeBnW.js";
|
|
5
10
|
import { basename, dirname, extname, join } from "path";
|
|
6
11
|
import { access, readFile, readdir, stat } from "fs/promises";
|
|
7
12
|
import { EventEmitter } from "node:events";
|
|
@@ -14,7 +19,7 @@ import path, { basename as basename$1, dirname as dirname$1, extname as extname$
|
|
|
14
19
|
import { tmpdir } from "node:os";
|
|
15
20
|
import { createHash, randomUUID } from "node:crypto";
|
|
16
21
|
import { z } from "zod";
|
|
17
|
-
import { access as access$1, lstat, mkdir as mkdir$1, readFile as readFile$1, readdir as readdir$1, readlink, realpath, rename, stat as stat$1, unlink, writeFile as writeFile$1 } from "node:fs/promises";
|
|
22
|
+
import { access as access$1, lstat, mkdir as mkdir$1, readFile as readFile$1, readdir as readdir$1, readlink, realpath, rename, stat as stat$1, unlink as unlink$1, writeFile as writeFile$1 } from "node:fs/promises";
|
|
18
23
|
import { existsSync as existsSync$1, lstatSync, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdir as readdir$2, readdirSync as readdirSync$1, readlinkSync, realpathSync as realpathSync$1, unlinkSync as unlinkSync$1, writeFileSync as writeFileSync$1 } from "fs";
|
|
19
24
|
import { promisify } from "node:util";
|
|
20
25
|
import { fileURLToPath } from "node:url";
|
|
@@ -1764,7 +1769,7 @@ const PIPELINE_EVENT_METADATA = [
|
|
|
1764
1769
|
{
|
|
1765
1770
|
name: "action",
|
|
1766
1771
|
type: "string",
|
|
1767
|
-
description: "Action taken
|
|
1772
|
+
description: "Action taken — stopped when policy requires halt, or the defaultAction when proceeding autonomously."
|
|
1768
1773
|
},
|
|
1769
1774
|
{
|
|
1770
1775
|
name: "skipped_stories",
|
|
@@ -1774,10 +1779,47 @@ const PIPELINE_EVENT_METADATA = [
|
|
|
1774
1779
|
{
|
|
1775
1780
|
name: "severity",
|
|
1776
1781
|
type: "string",
|
|
1777
|
-
description: "
|
|
1782
|
+
description: "Severity from routeDecision (critical for cost-ceiling-exhausted).",
|
|
1778
1783
|
optional: true
|
|
1779
1784
|
}
|
|
1780
1785
|
]
|
|
1786
|
+
},
|
|
1787
|
+
{
|
|
1788
|
+
type: "decision:halt-skipped-non-interactive",
|
|
1789
|
+
description: "A critical halt decision was skipped under --non-interactive mode; default action was applied autonomously.",
|
|
1790
|
+
when: "Emitted when --non-interactive suppresses an operator prompt and applies the default action. Story 72-2.",
|
|
1791
|
+
fields: [
|
|
1792
|
+
{
|
|
1793
|
+
name: "ts",
|
|
1794
|
+
type: "string",
|
|
1795
|
+
description: "Timestamp."
|
|
1796
|
+
},
|
|
1797
|
+
{
|
|
1798
|
+
name: "run_id",
|
|
1799
|
+
type: "string",
|
|
1800
|
+
description: "Pipeline run ID."
|
|
1801
|
+
},
|
|
1802
|
+
{
|
|
1803
|
+
name: "decision_type",
|
|
1804
|
+
type: "string",
|
|
1805
|
+
description: "Halt decision type that was skipped (e.g., halt:escalation)."
|
|
1806
|
+
},
|
|
1807
|
+
{
|
|
1808
|
+
name: "severity",
|
|
1809
|
+
type: "string",
|
|
1810
|
+
description: "Severity of the skipped halt (e.g., critical)."
|
|
1811
|
+
},
|
|
1812
|
+
{
|
|
1813
|
+
name: "default_action",
|
|
1814
|
+
type: "string",
|
|
1815
|
+
description: "Action applied in place of the operator prompt."
|
|
1816
|
+
},
|
|
1817
|
+
{
|
|
1818
|
+
name: "reason",
|
|
1819
|
+
type: "string",
|
|
1820
|
+
description: "Human-readable reason for skipping."
|
|
1821
|
+
}
|
|
1822
|
+
]
|
|
1781
1823
|
}
|
|
1782
1824
|
];
|
|
1783
1825
|
/**
|
|
@@ -14284,6 +14326,49 @@ function createImplementationOrchestrator(deps) {
|
|
|
14284
14326
|
}, "Build-fix dispatch failed — escalating");
|
|
14285
14327
|
}
|
|
14286
14328
|
if (!buildFixPassed) {
|
|
14329
|
+
let buildHaltPolicy = "critical";
|
|
14330
|
+
if (runManifest !== null && runManifest !== void 0) try {
|
|
14331
|
+
const manifestData = await runManifest.read();
|
|
14332
|
+
buildHaltPolicy = manifestData.cli_flags.halt_on ?? "critical";
|
|
14333
|
+
} catch {}
|
|
14334
|
+
const buildRouteResult = routeDecision("build-verification-failure", buildHaltPolicy);
|
|
14335
|
+
const buildRunId = config.pipelineRunId ?? "unknown";
|
|
14336
|
+
const buildReason = `build verification failed for story ${storyKey}`;
|
|
14337
|
+
if (buildRouteResult.halt) {
|
|
14338
|
+
eventBus.emit("decision:halt", {
|
|
14339
|
+
runId: buildRunId,
|
|
14340
|
+
decisionType: "build-verification-failure",
|
|
14341
|
+
severity: buildRouteResult.severity,
|
|
14342
|
+
reason: buildReason
|
|
14343
|
+
});
|
|
14344
|
+
await runInteractivePrompt({
|
|
14345
|
+
runId: buildRunId,
|
|
14346
|
+
decisionType: "build-verification-failure",
|
|
14347
|
+
severity: buildRouteResult.severity,
|
|
14348
|
+
summary: buildReason,
|
|
14349
|
+
defaultAction: buildRouteResult.defaultAction,
|
|
14350
|
+
choices: [
|
|
14351
|
+
"escalate-without-halt",
|
|
14352
|
+
"retry-with-custom-context",
|
|
14353
|
+
"propose-re-scope",
|
|
14354
|
+
"abort-run"
|
|
14355
|
+
],
|
|
14356
|
+
onHaltSkipped: (payload) => {
|
|
14357
|
+
eventBus.emit("decision:halt-skipped-non-interactive", payload);
|
|
14358
|
+
}
|
|
14359
|
+
}).catch((err) => {
|
|
14360
|
+
logger$26.warn({
|
|
14361
|
+
err,
|
|
14362
|
+
storyKey
|
|
14363
|
+
}, "interactive prompt failed — continuing with default action");
|
|
14364
|
+
});
|
|
14365
|
+
} else eventBus.emit("decision:autonomous", {
|
|
14366
|
+
runId: buildRunId,
|
|
14367
|
+
decisionType: "build-verification-failure",
|
|
14368
|
+
severity: buildRouteResult.severity,
|
|
14369
|
+
defaultAction: buildRouteResult.defaultAction,
|
|
14370
|
+
reason: buildReason
|
|
14371
|
+
});
|
|
14287
14372
|
eventBus.emit("story:build-verification-failed", {
|
|
14288
14373
|
storyKey,
|
|
14289
14374
|
exitCode: buildVerifyResult.exitCode ?? 1,
|
|
@@ -14292,7 +14377,9 @@ function createImplementationOrchestrator(deps) {
|
|
|
14292
14377
|
logger$26.warn({
|
|
14293
14378
|
storyKey,
|
|
14294
14379
|
reason,
|
|
14295
|
-
exitCode: buildVerifyResult.exitCode
|
|
14380
|
+
exitCode: buildVerifyResult.exitCode,
|
|
14381
|
+
routedHalt: buildRouteResult.halt,
|
|
14382
|
+
defaultAction: buildRouteResult.defaultAction
|
|
14296
14383
|
}, "Build verification failed — escalating story");
|
|
14297
14384
|
updateStory(storyKey, {
|
|
14298
14385
|
phase: "ESCALATED",
|
|
@@ -14381,17 +14468,165 @@ function createImplementationOrchestrator(deps) {
|
|
|
14381
14468
|
verificationStore.set(storyKey, verifSummary);
|
|
14382
14469
|
await persistVerificationResult(storyKey, verifSummary, runManifest);
|
|
14383
14470
|
if (verifSummary.status === "fail") {
|
|
14384
|
-
|
|
14385
|
-
|
|
14386
|
-
|
|
14387
|
-
|
|
14388
|
-
|
|
14389
|
-
|
|
14390
|
-
|
|
14391
|
-
|
|
14392
|
-
|
|
14393
|
-
|
|
14394
|
-
|
|
14471
|
+
let shouldFallThroughToComplete = false;
|
|
14472
|
+
if (runManifest != null) {
|
|
14473
|
+
const failFindings = (verifSummary.checks ?? []).flatMap((c) => c.findings ?? []);
|
|
14474
|
+
const hasBuildFail = (verifSummary.checks ?? []).some((c) => (c.checkName === "build" || c.checkName === "typecheck") && c.status === "fail");
|
|
14475
|
+
const recoveryRootCause = hasBuildFail ? "build-failure" : "ac-missing-evidence";
|
|
14476
|
+
const recoveryBudget = {
|
|
14477
|
+
max: config.maxReviewCycles,
|
|
14478
|
+
remaining: Math.max(0, config.maxReviewCycles - finalReviewCycles)
|
|
14479
|
+
};
|
|
14480
|
+
const recoveryResult = await runRecoveryEngine({
|
|
14481
|
+
runId: config.pipelineRunId ?? storyKey,
|
|
14482
|
+
storyKey,
|
|
14483
|
+
failure: {
|
|
14484
|
+
rootCause: recoveryRootCause,
|
|
14485
|
+
findings: failFindings
|
|
14486
|
+
},
|
|
14487
|
+
budget: recoveryBudget,
|
|
14488
|
+
bus: toSdlcEventBus(eventBus),
|
|
14489
|
+
manifest: runManifest,
|
|
14490
|
+
adapter: db,
|
|
14491
|
+
engine: "linear"
|
|
14492
|
+
}).catch((recoveryErr) => {
|
|
14493
|
+
logger$26.warn({
|
|
14494
|
+
storyKey,
|
|
14495
|
+
err: recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr)
|
|
14496
|
+
}, "Recovery Engine invocation failed — falling through to VERIFICATION_FAILED (best-effort)");
|
|
14497
|
+
return null;
|
|
14498
|
+
});
|
|
14499
|
+
if (recoveryResult?.action === "halt-entire-run") {
|
|
14500
|
+
logger$26.error({
|
|
14501
|
+
storyKey,
|
|
14502
|
+
pendingProposalsCount: recoveryResult.pendingProposalsCount
|
|
14503
|
+
}, "Recovery Engine safety valve: halting entire run due to >= 5 pending proposals");
|
|
14504
|
+
_budgetExhausted = true;
|
|
14505
|
+
} else if (recoveryResult?.action === "retry") {
|
|
14506
|
+
logger$26.info({
|
|
14507
|
+
storyKey,
|
|
14508
|
+
attempt: recoveryResult.attempt,
|
|
14509
|
+
retryBudgetRemaining: recoveryResult.retryBudgetRemaining
|
|
14510
|
+
}, "Recovery Engine Tier A: re-dispatching dev-story with enriched prompt");
|
|
14511
|
+
try {
|
|
14512
|
+
incrementDispatches(storyKey);
|
|
14513
|
+
const retryDevResult = await runDevStory({
|
|
14514
|
+
db,
|
|
14515
|
+
pack,
|
|
14516
|
+
contextCompiler,
|
|
14517
|
+
dispatcher,
|
|
14518
|
+
projectRoot,
|
|
14519
|
+
tokenCeilings,
|
|
14520
|
+
otlpEndpoint: _otlpEndpoint,
|
|
14521
|
+
repoMapInjector,
|
|
14522
|
+
maxRepoMapTokens,
|
|
14523
|
+
agentId
|
|
14524
|
+
}, {
|
|
14525
|
+
storyKey,
|
|
14526
|
+
storyFilePath: storyFilePath ?? "",
|
|
14527
|
+
pipelineRunId: config.pipelineRunId,
|
|
14528
|
+
findingsPrompt: recoveryResult.enrichedPrompt
|
|
14529
|
+
});
|
|
14530
|
+
replaceDevStorySignals(retryDevResult);
|
|
14531
|
+
await persistDevStorySignals(storyKey, devStorySignals, runManifest);
|
|
14532
|
+
const retryVerifContext = assembleVerificationContext({
|
|
14533
|
+
storyKey,
|
|
14534
|
+
workingDir: projectRoot ?? process.cwd(),
|
|
14535
|
+
reviewResult: latestReviewSignals,
|
|
14536
|
+
storyContent: storyContentForVerification,
|
|
14537
|
+
devStoryResult: devStorySignals,
|
|
14538
|
+
outputTokenCount: devOutputTokenCount,
|
|
14539
|
+
sourceEpicContent
|
|
14540
|
+
});
|
|
14541
|
+
const retryVerifSummary = await verificationPipeline.run(retryVerifContext, "A");
|
|
14542
|
+
verificationStore.set(storyKey, retryVerifSummary);
|
|
14543
|
+
await persistVerificationResult(storyKey, retryVerifSummary, runManifest);
|
|
14544
|
+
if (retryVerifSummary.status !== "fail") {
|
|
14545
|
+
logger$26.info({ storyKey }, "Recovery Engine Tier A retry succeeded — story proceeding to COMPLETE");
|
|
14546
|
+
shouldFallThroughToComplete = true;
|
|
14547
|
+
} else logger$26.warn({ storyKey }, "Recovery Engine Tier A retry still failed — falling through to VERIFICATION_FAILED");
|
|
14548
|
+
} catch (retryErr) {
|
|
14549
|
+
logger$26.warn({
|
|
14550
|
+
storyKey,
|
|
14551
|
+
err: retryErr instanceof Error ? retryErr.message : String(retryErr)
|
|
14552
|
+
}, "Recovery Engine Tier A re-dispatch threw — falling through to VERIFICATION_FAILED");
|
|
14553
|
+
}
|
|
14554
|
+
} else if (recoveryResult?.action === "propose") {
|
|
14555
|
+
logger$26.info({ storyKey }, "Recovery Engine Tier B: proposal appended — marking story ESCALATED for operator re-scope");
|
|
14556
|
+
updateStory(storyKey, {
|
|
14557
|
+
phase: "ESCALATED",
|
|
14558
|
+
completedAt: new Date().toISOString(),
|
|
14559
|
+
error: "recovery-engine-propose"
|
|
14560
|
+
});
|
|
14561
|
+
persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
|
|
14562
|
+
err,
|
|
14563
|
+
storyKey
|
|
14564
|
+
}, "StateStore write failed after recovery-propose"));
|
|
14565
|
+
await emitEscalation({
|
|
14566
|
+
storyKey,
|
|
14567
|
+
lastVerdict: "recovery-propose",
|
|
14568
|
+
reviewCycles: finalReviewCycles,
|
|
14569
|
+
issues: failFindings
|
|
14570
|
+
});
|
|
14571
|
+
await writeStoryMetricsBestEffort(storyKey, "escalated", finalReviewCycles);
|
|
14572
|
+
await persistState();
|
|
14573
|
+
return "verification-failed";
|
|
14574
|
+
} else if (recoveryResult?.action === "halt") {
|
|
14575
|
+
logger$26.warn({ storyKey }, "Recovery Engine Tier C: halt — invoking interactive prompt for operator decision");
|
|
14576
|
+
const haltRunId = config.pipelineRunId ?? storyKey;
|
|
14577
|
+
await runInteractivePrompt({
|
|
14578
|
+
runId: haltRunId,
|
|
14579
|
+
decisionType: "verification-failure",
|
|
14580
|
+
severity: "critical",
|
|
14581
|
+
summary: `Verification failed (Tier C halt) on story ${storyKey}: ${recoveryRootCause}`,
|
|
14582
|
+
defaultAction: "escalate",
|
|
14583
|
+
choices: [
|
|
14584
|
+
"escalate-without-halt",
|
|
14585
|
+
"propose-re-scope",
|
|
14586
|
+
"abort-run"
|
|
14587
|
+
],
|
|
14588
|
+
onHaltSkipped: (haltPayload) => {
|
|
14589
|
+
eventBus.emit("decision:halt-skipped-non-interactive", haltPayload);
|
|
14590
|
+
}
|
|
14591
|
+
}).catch((err) => {
|
|
14592
|
+
logger$26.warn({
|
|
14593
|
+
err,
|
|
14594
|
+
storyKey
|
|
14595
|
+
}, "Recovery Engine Tier C: interactive prompt failed — escalating anyway");
|
|
14596
|
+
});
|
|
14597
|
+
updateStory(storyKey, {
|
|
14598
|
+
phase: "ESCALATED",
|
|
14599
|
+
completedAt: new Date().toISOString(),
|
|
14600
|
+
error: "recovery-engine-halt"
|
|
14601
|
+
});
|
|
14602
|
+
persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
|
|
14603
|
+
err,
|
|
14604
|
+
storyKey
|
|
14605
|
+
}, "StateStore write failed after recovery-halt"));
|
|
14606
|
+
await emitEscalation({
|
|
14607
|
+
storyKey,
|
|
14608
|
+
lastVerdict: "recovery-halt",
|
|
14609
|
+
reviewCycles: finalReviewCycles,
|
|
14610
|
+
issues: failFindings
|
|
14611
|
+
});
|
|
14612
|
+
await writeStoryMetricsBestEffort(storyKey, "escalated", finalReviewCycles);
|
|
14613
|
+
await persistState();
|
|
14614
|
+
return "verification-failed";
|
|
14615
|
+
}
|
|
14616
|
+
}
|
|
14617
|
+
if (!shouldFallThroughToComplete) {
|
|
14618
|
+
updateStory(storyKey, {
|
|
14619
|
+
phase: "VERIFICATION_FAILED",
|
|
14620
|
+
completedAt: new Date().toISOString()
|
|
14621
|
+
});
|
|
14622
|
+
persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
|
|
14623
|
+
err,
|
|
14624
|
+
storyKey
|
|
14625
|
+
}, "StateStore write failed after verification-failed"));
|
|
14626
|
+
await writeStoryMetricsBestEffort(storyKey, "verification-failed", finalReviewCycles);
|
|
14627
|
+
await persistState();
|
|
14628
|
+
return "verification-failed";
|
|
14629
|
+
}
|
|
14395
14630
|
}
|
|
14396
14631
|
}
|
|
14397
14632
|
if (autoApprove !== void 0) eventBus.emit("story:auto-approved", {
|
|
@@ -15234,7 +15469,42 @@ function createImplementationOrchestrator(deps) {
|
|
|
15234
15469
|
* @param manifest - The current run manifest data
|
|
15235
15470
|
*/
|
|
15236
15471
|
async function handleCeilingExceeded(triggeredStoryKey, remainingInGroup, result, manifest) {
|
|
15237
|
-
const
|
|
15472
|
+
const haltPolicy = manifest.cli_flags.halt_on ?? "critical";
|
|
15473
|
+
const routeResult = routeDecision("cost-ceiling-exhausted", haltPolicy);
|
|
15474
|
+
const runId = config.pipelineRunId ?? "unknown";
|
|
15475
|
+
const reason = `cost ceiling exceeded: ${result.cumulative.toFixed(4)} USD >= ${result.ceiling} USD`;
|
|
15476
|
+
if (routeResult.halt) {
|
|
15477
|
+
eventBus.emit("decision:halt", {
|
|
15478
|
+
runId,
|
|
15479
|
+
decisionType: "cost-ceiling-exhausted",
|
|
15480
|
+
severity: routeResult.severity,
|
|
15481
|
+
reason
|
|
15482
|
+
});
|
|
15483
|
+
await runInteractivePrompt({
|
|
15484
|
+
runId,
|
|
15485
|
+
decisionType: "cost-ceiling-exhausted",
|
|
15486
|
+
severity: routeResult.severity,
|
|
15487
|
+
summary: reason,
|
|
15488
|
+
defaultAction: routeResult.defaultAction,
|
|
15489
|
+
choices: [
|
|
15490
|
+
"skip-remaining",
|
|
15491
|
+
"retry-with-custom-context",
|
|
15492
|
+
"propose-re-scope",
|
|
15493
|
+
"abort-run"
|
|
15494
|
+
],
|
|
15495
|
+
onHaltSkipped: (payload) => {
|
|
15496
|
+
eventBus.emit("decision:halt-skipped-non-interactive", payload);
|
|
15497
|
+
}
|
|
15498
|
+
}).catch((err) => {
|
|
15499
|
+
logger$26.warn({ err }, "interactive prompt failed during cost-ceiling halt — continuing with default action");
|
|
15500
|
+
});
|
|
15501
|
+
} else eventBus.emit("decision:autonomous", {
|
|
15502
|
+
runId,
|
|
15503
|
+
decisionType: "cost-ceiling-exhausted",
|
|
15504
|
+
severity: routeResult.severity,
|
|
15505
|
+
defaultAction: routeResult.defaultAction,
|
|
15506
|
+
reason
|
|
15507
|
+
});
|
|
15238
15508
|
const allSkipped = [triggeredStoryKey, ...remainingInGroup];
|
|
15239
15509
|
for (const [key, state] of _stories) if (state.phase === "PENDING" && !allSkipped.includes(key)) allSkipped.push(key);
|
|
15240
15510
|
for (const key of allSkipped) {
|
|
@@ -15248,16 +15518,18 @@ function createImplementationOrchestrator(deps) {
|
|
|
15248
15518
|
eventBus.emit("cost:ceiling-reached", {
|
|
15249
15519
|
cumulative_cost: result.cumulative,
|
|
15250
15520
|
ceiling: result.ceiling,
|
|
15251
|
-
halt_on:
|
|
15252
|
-
action: "stopped",
|
|
15521
|
+
halt_on: haltPolicy,
|
|
15522
|
+
action: routeResult.halt ? "stopped" : routeResult.defaultAction,
|
|
15253
15523
|
skipped_stories: allSkipped,
|
|
15254
|
-
|
|
15524
|
+
severity: routeResult.severity
|
|
15255
15525
|
});
|
|
15256
15526
|
_budgetExhausted = true;
|
|
15257
15527
|
logger$26.warn({
|
|
15258
15528
|
skipped: allSkipped.length,
|
|
15259
15529
|
cumulative: result.cumulative,
|
|
15260
|
-
ceiling: result.ceiling
|
|
15530
|
+
ceiling: result.ceiling,
|
|
15531
|
+
routedHalt: routeResult.halt,
|
|
15532
|
+
defaultAction: routeResult.defaultAction
|
|
15261
15533
|
}, "Cost ceiling reached — stopping dispatch");
|
|
15262
15534
|
}
|
|
15263
15535
|
/**
|
|
@@ -43246,7 +43518,7 @@ async function writeRunState(projectDir, state) {
|
|
|
43246
43518
|
async function clearRunState(projectDir) {
|
|
43247
43519
|
const filePath = runStatePath(projectDir);
|
|
43248
43520
|
try {
|
|
43249
|
-
await unlink(filePath);
|
|
43521
|
+
await unlink$1(filePath);
|
|
43250
43522
|
} catch (err) {
|
|
43251
43523
|
if (err.code !== "ENOENT") throw err;
|
|
43252
43524
|
}
|
|
@@ -44204,6 +44476,17 @@ function wireNdjsonEmitter(eventBus, ndjsonEmitter) {
|
|
|
44204
44476
|
...payload.severity !== void 0 ? { severity: payload.severity } : {}
|
|
44205
44477
|
});
|
|
44206
44478
|
});
|
|
44479
|
+
eventBus.on("decision:halt-skipped-non-interactive", (payload) => {
|
|
44480
|
+
ndjsonEmitter.emit({
|
|
44481
|
+
type: "decision:halt-skipped-non-interactive",
|
|
44482
|
+
ts: new Date().toISOString(),
|
|
44483
|
+
run_id: payload.runId,
|
|
44484
|
+
decision_type: payload.decisionType,
|
|
44485
|
+
severity: payload.severity,
|
|
44486
|
+
default_action: payload.defaultAction,
|
|
44487
|
+
reason: payload.reason
|
|
44488
|
+
});
|
|
44489
|
+
});
|
|
44207
44490
|
}
|
|
44208
44491
|
/**
|
|
44209
44492
|
* Resolve the `probeAuthorStateIntegrating` boolean from CLI flag and env var.
|
|
@@ -44220,8 +44503,20 @@ function resolveProbeAuthorStateIntegrating(cliFlag) {
|
|
|
44220
44503
|
if (envVal !== void 0) return envVal === "on";
|
|
44221
44504
|
return true;
|
|
44222
44505
|
}
|
|
44506
|
+
/**
|
|
44507
|
+
* Story 72-2: --non-interactive + Machine-Readable Exit Codes.
|
|
44508
|
+
*
|
|
44509
|
+
* Phase D Story 54-6 (2026-04-05): original headless CI/CD spec.
|
|
44510
|
+
* Story 72-1: Decision Router providing routeDecision defaultAction authority.
|
|
44511
|
+
* Story 72-2 (this code): --non-interactive flag that suppresses stdin reads,
|
|
44512
|
+
* auto-applies default actions via routeDecision, and returns exit codes 0/1/2.
|
|
44513
|
+
*
|
|
44514
|
+
* Enables strata + agent-mesh cross-project CI/CD invocation:
|
|
44515
|
+
* substrate run --non-interactive --halt-on none --events --output-format json
|
|
44516
|
+
*/
|
|
44223
44517
|
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;
|
|
44518
|
+
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;
|
|
44519
|
+
const haltOn = haltOnOpt;
|
|
44225
44520
|
const VALID_PROBE_AUTHOR_MODES = [
|
|
44226
44521
|
"enabled",
|
|
44227
44522
|
"disabled",
|
|
@@ -44534,11 +44829,12 @@ async function runRunAction(options) {
|
|
|
44534
44829
|
const runsDir = join(dbDir, "runs");
|
|
44535
44830
|
const cliFlags = {
|
|
44536
44831
|
...parsedStoryKeys.length > 0 ? { stories: parsedStoryKeys } : {},
|
|
44537
|
-
halt_on: haltOn ?? "
|
|
44832
|
+
halt_on: haltOn ?? "critical",
|
|
44538
44833
|
...costCeiling !== void 0 ? { cost_ceiling: costCeiling } : {},
|
|
44539
44834
|
...agentId !== void 0 ? { agent: agentId } : {},
|
|
44540
44835
|
...skipVerification === true ? { skip_verification: true } : {},
|
|
44541
|
-
...eventsFlag === true ? { events: true } : {}
|
|
44836
|
+
...eventsFlag === true ? { events: true } : {},
|
|
44837
|
+
...nonInteractive === true ? { non_interactive: true } : {}
|
|
44542
44838
|
};
|
|
44543
44839
|
const manifest = RunManifest.open(pipelineRun.id, runsDir);
|
|
44544
44840
|
await manifest.patchCLIFlags(cliFlags);
|
|
@@ -44565,6 +44861,14 @@ async function runRunAction(options) {
|
|
|
44565
44861
|
});
|
|
44566
44862
|
} catch {}
|
|
44567
44863
|
const eventBus = createEventBus();
|
|
44864
|
+
let _costCeilingExhausted = false;
|
|
44865
|
+
let _fatalHaltReached = false;
|
|
44866
|
+
eventBus.on("cost:ceiling-reached", () => {
|
|
44867
|
+
_costCeilingExhausted = true;
|
|
44868
|
+
});
|
|
44869
|
+
eventBus.on("decision:halt", (payload) => {
|
|
44870
|
+
if (payload.severity === "fatal") _fatalHaltReached = true;
|
|
44871
|
+
});
|
|
44568
44872
|
const contextCompiler = createContextCompiler({ db: adapter });
|
|
44569
44873
|
if (!injectedRegistry) throw new Error("AdapterRegistry is required — must be initialized at CLI startup");
|
|
44570
44874
|
const routingConfigPath = join(projectRoot, "substrate.routing.yml");
|
|
@@ -44705,7 +45009,7 @@ async function runRunAction(options) {
|
|
|
44705
45009
|
if (tuiFlag === true && !isTuiCapable()) printNonTtyWarning();
|
|
44706
45010
|
if (verboseFlag !== true && eventsFlag !== true) process.env.LOG_LEVEL = "silent";
|
|
44707
45011
|
let tuiApp;
|
|
44708
|
-
if (tuiFlag === true && isTuiCapable() && eventsFlag !== true && outputFormat === "human") {
|
|
45012
|
+
if (tuiFlag === true && nonInteractive !== true && isTuiCapable() && eventsFlag !== true && outputFormat === "human") {
|
|
44709
45013
|
tuiApp = createTuiApp(process.stdout, process.stdin);
|
|
44710
45014
|
tuiApp.handleEvent({
|
|
44711
45015
|
type: "pipeline:start",
|
|
@@ -45098,13 +45402,52 @@ async function runRunAction(options) {
|
|
|
45098
45402
|
engineType: resolvedEngine,
|
|
45099
45403
|
concurrency
|
|
45100
45404
|
});
|
|
45405
|
+
if (nonInteractive === true) {
|
|
45406
|
+
if (escalatedKeys.length > 0 || _costCeilingExhausted || _fatalHaltReached) {
|
|
45407
|
+
const haltPolicy = haltOn ?? "critical";
|
|
45408
|
+
const routeResult = routeDecision("pipeline-escalation", haltPolicy);
|
|
45409
|
+
eventBus.emit("decision:halt-skipped-non-interactive", {
|
|
45410
|
+
runId: pipelineRun.id,
|
|
45411
|
+
decisionType: "pipeline-escalation",
|
|
45412
|
+
severity: routeResult.severity,
|
|
45413
|
+
defaultAction: routeResult.defaultAction,
|
|
45414
|
+
reason: "non-interactive: stdin prompt suppressed"
|
|
45415
|
+
});
|
|
45416
|
+
try {
|
|
45417
|
+
const runsDir = join(dbDir, "runs");
|
|
45418
|
+
const runManifestForHalt = RunManifest.open(pipelineRun.id, runsDir);
|
|
45419
|
+
await runManifestForHalt.update({ cli_flags: {
|
|
45420
|
+
halt_on: haltPolicy,
|
|
45421
|
+
halt_skipped: true,
|
|
45422
|
+
halt_skipped_decisions: [{
|
|
45423
|
+
decisionType: "pipeline-escalation",
|
|
45424
|
+
severity: routeResult.severity,
|
|
45425
|
+
defaultAction: routeResult.defaultAction,
|
|
45426
|
+
reason: "non-interactive: stdin prompt suppressed",
|
|
45427
|
+
skippedAt: new Date().toISOString()
|
|
45428
|
+
}]
|
|
45429
|
+
} });
|
|
45430
|
+
} catch (haltManifestErr) {
|
|
45431
|
+
logger.warn({ err: haltManifestErr }, "Failed to write halt-skipped to manifest (best-effort)");
|
|
45432
|
+
}
|
|
45433
|
+
}
|
|
45434
|
+
const derivedCode = deriveExitCode({
|
|
45435
|
+
succeeded: succeededKeys,
|
|
45436
|
+
escalated: escalatedKeys,
|
|
45437
|
+
failed: failedKeys,
|
|
45438
|
+
total: storyKeys.length,
|
|
45439
|
+
costCeilingExhausted: _costCeilingExhausted,
|
|
45440
|
+
fatalHaltReached: _fatalHaltReached
|
|
45441
|
+
});
|
|
45442
|
+
return derivedCode;
|
|
45443
|
+
}
|
|
45101
45444
|
return 0;
|
|
45102
45445
|
} catch (err) {
|
|
45103
45446
|
const msg = err instanceof Error ? err.message : String(err);
|
|
45104
45447
|
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
45105
45448
|
else process.stderr.write(`Error: ${msg}\n`);
|
|
45106
45449
|
logger.error({ err }, "run failed");
|
|
45107
|
-
return 1;
|
|
45450
|
+
return nonInteractive === true ? 2 : 1;
|
|
45108
45451
|
} finally {
|
|
45109
45452
|
try {
|
|
45110
45453
|
await adapter.close();
|
|
@@ -45578,7 +45921,22 @@ async function runFullPipeline(options) {
|
|
|
45578
45921
|
}
|
|
45579
45922
|
}
|
|
45580
45923
|
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:
|
|
45924
|
+
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", [
|
|
45925
|
+
"Run without operator prompts (CI/CD mode). Suppresses all stdin reads,",
|
|
45926
|
+
"auto-applies default actions for halt decisions, and returns machine-readable",
|
|
45927
|
+
"exit codes: 0=all stories succeeded, 1=some escalated, 2=run-level failure.",
|
|
45928
|
+
"",
|
|
45929
|
+
"Canonical CI/CD invocation:",
|
|
45930
|
+
" substrate run --non-interactive --halt-on none --events --output-format json",
|
|
45931
|
+
"",
|
|
45932
|
+
"Exit code semantics:",
|
|
45933
|
+
" 0 All stories succeeded (or recovered cleanly)",
|
|
45934
|
+
" 1 Some stories escalated; run completed",
|
|
45935
|
+
" 2 Run-level failure (cost ceiling exhausted, fatal halt, orchestrator died)",
|
|
45936
|
+
"",
|
|
45937
|
+
"--halt-on defaults to critical. Skipped halt decisions are recorded as",
|
|
45938
|
+
"decision:halt-skipped-non-interactive events in --non-interactive mode."
|
|
45939
|
+
].join("\n ")).action(async (opts) => {
|
|
45582
45940
|
if (opts.helpAgent) {
|
|
45583
45941
|
process.exitCode = await runHelpAgent();
|
|
45584
45942
|
return;
|
|
@@ -45622,12 +45980,14 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
45622
45980
|
haltOn: opts.haltOn,
|
|
45623
45981
|
costCeiling: opts.costCeiling,
|
|
45624
45982
|
probeAuthor: opts.probeAuthor,
|
|
45625
|
-
probeAuthorStateIntegrating: opts.probeAuthorStateIntegrating
|
|
45983
|
+
probeAuthorStateIntegrating: opts.probeAuthorStateIntegrating,
|
|
45984
|
+
nonInteractive: opts.nonInteractive
|
|
45626
45985
|
});
|
|
45986
|
+
if (opts.nonInteractive === true) process.exit(exitCode);
|
|
45627
45987
|
process.exitCode = exitCode;
|
|
45628
45988
|
});
|
|
45629
45989
|
}
|
|
45630
45990
|
|
|
45631
45991
|
//#endregion
|
|
45632
45992
|
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-
|
|
45993
|
+
//# sourceMappingURL=run-DzB4rgkj.js.map
|
|
@@ -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
|