substrate-ai 0.21.2 → 0.21.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ import { AdapterRegistry } from "./dist-CpnLJuVw.js";
2
+ import "./adapter-registry-DXLMTmfD.js";
3
+
4
+ export { AdapterRegistry };
package/dist/cli/index.js CHANGED
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { FileKvStore, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createDatabaseAdapter, createDoltOperatorReader, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion } from "../health-DvgJ7jj_.js";
2
+ import { FileKvStore, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createDatabaseAdapter, createDoltOperatorReader, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion } from "../health-BUl3y64N.js";
3
3
  import { createLogger } from "../logger-KeHncl-f.js";
4
4
  import { createEventBus } from "../helpers-CElYrONe.js";
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, createGitWorktreeManager, createPipelineRun, createStderrLogger, getActiveDecisions, getAllCostEntriesFiltered, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initWorkGraphSchema, initializeDolt, listRunMetrics, loadParentRunDecisions, supersedeDecision, swallowDebug, tagRunAsBaseline, updatePipelineRun } from "../dist-BkhYgJWo.js";
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, createGitWorktreeManager, createPipelineRun, createStderrLogger, getActiveDecisions, getAllCostEntriesFiltered, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initWorkGraphSchema, initializeDolt, listRunMetrics, loadParentRunDecisions, supersedeDecision, swallowDebug, tagRunAsBaseline, updatePipelineRun } from "../dist-CpnLJuVw.js";
6
6
  import "../adapter-registry-DXLMTmfD.js";
7
- import { JOURNEY_REGISTRY_PATH, RunManifest, SupervisorLock, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, loadJourneyRegistryFromFile, loadJourneyRegistryFromTrustedTree, parseRuntimeProbes, readCurrentRunId, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics, runAcTraceabilityCheck } from "../manifest-read-DyyFPrJH.js";
8
- 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-CAmBrDky.js";
9
- import "../errors-C7pY6_yQ.js";
7
+ import { ACCEPTANCE_CONTRACT_PROFILE_PATH, JOURNEY_DEFERRALS_PATH, JOURNEY_REGISTRY_PATH, RunManifest, SupervisorLock, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, loadAcceptanceContractFromTrustedTree, loadJourneyRegistryFromFile, loadJourneyRegistryFromTrustedTree, parseAcceptanceContract, parseJourneyDeferrals, parseRuntimeProbes, readCurrentRunId, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics, runAcTraceabilityCheck } from "../manifest-read-DZDlSq_s.js";
8
+ 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-nSb1TD3o.js";
9
+ import "../errors-8tVEwG6v.js";
10
10
  import "../routing-DFxoKHDt.js";
11
11
  import { WorkGraphRepository } from "../work-graph-repository-DZyJv5pV.js";
12
12
  import "../decisions-CzSIEeGP.js";
13
13
  import "../decision-router-BAPpON_C.js";
14
- import "../interactive-prompt-CPF2GTvV.js";
14
+ import "../interactive-prompt-C4Is1QDB.js";
15
15
  import "../recovery-engine-BKGBeBnW.js";
16
16
  import "../version-manager-impl-qFBiO4Eh.js";
17
- import { registerUpgradeCommand } from "../upgrade-S1oUMH5P.js";
17
+ import { registerUpgradeCommand } from "../upgrade-BUSzXgnd.js";
18
18
  import { Command } from "commander";
19
19
  import { fileURLToPath } from "url";
20
20
  import { dirname, join, resolve } from "path";
@@ -25,11 +25,11 @@ import * as actualFS from "node:fs";
25
25
  import { existsSync, promises, readFileSync, writeFileSync } from "node:fs";
26
26
  import { execFile, spawnSync } from "node:child_process";
27
27
  import * as path$1 from "node:path";
28
- import { basename as basename$1, join as join$1, posix, relative, resolve as resolve$1, win32 } from "node:path";
28
+ import { basename as basename$1, dirname as dirname$1, join as join$1, posix, relative, resolve as resolve$1, win32 } from "node:path";
29
29
  import { randomUUID } from "node:crypto";
30
30
  import { z } from "zod";
31
31
  import * as fs from "node:fs/promises";
32
- import { lstat, readFile as readFile$1, readdir as readdir$1, readlink, realpath } from "node:fs/promises";
32
+ import { lstat, mkdir as mkdir$1, readFile as readFile$1, readdir as readdir$1, readlink, realpath, writeFile as writeFile$1 } from "node:fs/promises";
33
33
  import { chmodSync, cpSync, 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, rmSync as rmSync$1, statSync as statSync$1, unlinkSync as unlinkSync$1, writeFileSync as writeFileSync$1 } from "fs";
34
34
  import { homedir } from "os";
35
35
  import { createRequire } from "node:module";
@@ -7130,7 +7130,7 @@ async function runStatusAction(options) {
7130
7130
  logger$15.debug({ err }, "Work graph query failed, continuing without work graph data");
7131
7131
  }
7132
7132
  if (run === void 0) {
7133
- const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-DDsjslMy.js");
7133
+ const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-DklP4Se9.js");
7134
7134
  const substrateDirPath = join(projectRoot, ".substrate");
7135
7135
  const processInfo = inspectProcessTree$1({
7136
7136
  projectRoot,
@@ -8078,7 +8078,7 @@ function defaultSupervisorDeps() {
8078
8078
  if (cached === null) {
8079
8079
  const { AdapterRegistry: AR } = await import(
8080
8080
  /* @vite-ignore */
8081
- "../adapter-registry-CZn__637.js"
8081
+ "../adapter-registry-BSig1zpx.js"
8082
8082
  );
8083
8083
  cached = new AR();
8084
8084
  await cached.discoverAndRegister();
@@ -8645,11 +8645,11 @@ async function runSupervisorAction(options, deps = {}) {
8645
8645
  try {
8646
8646
  const { createExperimenter } = await import(
8647
8647
  /* @vite-ignore */
8648
- "../experimenter-BYqALeaE.js"
8648
+ "../experimenter-C7swe92R.js"
8649
8649
  );
8650
8650
  const { getLatestRun: getLatest } = await import(
8651
8651
  /* @vite-ignore */
8652
- "../decisions-BPwrDCTx.js"
8652
+ "../decisions-lxU14pNh.js"
8653
8653
  );
8654
8654
  const expAdapter = createDatabaseAdapter({
8655
8655
  backend: "auto",
@@ -8659,7 +8659,7 @@ async function runSupervisorAction(options, deps = {}) {
8659
8659
  await initSchema(expAdapter);
8660
8660
  const { runRunAction: runPipeline } = await import(
8661
8661
  /* @vite-ignore */
8662
- "../run-CF0qLJAV.js"
8662
+ "../run-CMXmzqoy.js"
8663
8663
  );
8664
8664
  const runStoryFn = async (opts) => {
8665
8665
  const exitCode = await runPipeline({
@@ -9184,7 +9184,7 @@ async function runMetricsAction(options) {
9184
9184
  const routingConfigPath = join(dbDir, "routing.yml");
9185
9185
  let routingConfig = null;
9186
9186
  if (existsSync$1(routingConfigPath)) try {
9187
- const { loadModelRoutingConfig } = await import("../routing-BS-Q8_2L.js");
9187
+ const { loadModelRoutingConfig } = await import("../routing-CGO2UZyA.js");
9188
9188
  routingConfig = loadModelRoutingConfig(routingConfigPath);
9189
9189
  } catch {}
9190
9190
  if (routingConfig === null) routingConfig = {
@@ -13474,15 +13474,76 @@ function registerAcceptanceCommand(program, version) {
13474
13474
  const projectRoot = process.cwd();
13475
13475
  const source = opts.ref !== void 0 ? `committed @ ${opts.ref}` : "working tree";
13476
13476
  const result = opts.ref !== void 0 ? await loadJourneyRegistryFromTrustedTree(projectRoot, opts.ref) : await loadJourneyRegistryFromFile(projectRoot);
13477
+ let contract;
13478
+ if (opts.ref !== void 0) contract = await loadAcceptanceContractFromTrustedTree(projectRoot, opts.ref);
13479
+ else try {
13480
+ const profileContent = await readFile$1(join$1(projectRoot, ACCEPTANCE_CONTRACT_PROFILE_PATH), "utf-8");
13481
+ contract = parseAcceptanceContract(profileContent);
13482
+ } catch {
13483
+ contract = { status: "absent" };
13484
+ }
13477
13485
  if (opts.outputFormat === "json") {
13478
13486
  const output = buildJsonOutput("substrate acceptance validate", {
13479
13487
  source,
13480
- ...result
13488
+ ...result,
13489
+ contract: { status: contract.status }
13481
13490
  }, version);
13482
13491
  process.stdout.write(JSON.stringify(output, null, 2) + "\n");
13483
13492
  process.exit(result.status === "ok" ? ACCEPTANCE_EXIT_SUCCESS : ACCEPTANCE_EXIT_ERROR);
13484
13493
  }
13485
- process.exit(renderHuman(result, source));
13494
+ const exitCode = renderHuman(result, source);
13495
+ if (contract.status === "ok") {
13496
+ const surfaces = Object.keys(contract.contract.surfaces).join(", ");
13497
+ process.stdout.write(`contract: OK — surfaces declared: ${surfaces}\n`);
13498
+ } else if (contract.status === "invalid") {
13499
+ process.stdout.write(`contract: INVALID (${ACCEPTANCE_CONTRACT_PROFILE_PATH} acceptance: block):\n`);
13500
+ for (const issue of contract.issues) process.stdout.write(` ${issue.path}: ${issue.message}\n`);
13501
+ } else if (contract.status === "absent") process.stdout.write(`contract: ABSENT — no acceptance: block in ${ACCEPTANCE_CONTRACT_PROFILE_PATH}. Claimed journeys can never be walked (acceptance-unrunnable in blocking mode).
13502
+ `);
13503
+ process.exit(exitCode);
13504
+ });
13505
+ acceptanceCmd.command("defer <journeyId>").description("Record an operator deferral for a journey — the coverage audit reports it `deferred` instead of unclaimed/unwalked. Writes to the working tree; COMMIT the file for the audit (trusted tree) to honor it.").requiredOption("--reason <text>", "why this journey is deferred (the operator ack is the point)").action(async (journeyId, opts) => {
13506
+ const projectRoot = process.cwd();
13507
+ const registry = await loadJourneyRegistryFromFile(projectRoot);
13508
+ if (registry.status !== "ok") {
13509
+ process.stdout.write(`cannot defer: journey registry is ${registry.status} at ${JOURNEY_REGISTRY_PATH} — fix it first (substrate acceptance validate)\n`);
13510
+ process.exit(ACCEPTANCE_EXIT_ERROR);
13511
+ }
13512
+ if (!registry.registry.journeys.some((j) => j.id === journeyId)) {
13513
+ process.stdout.write(`cannot defer: journey "${journeyId}" is not in the registry. Known ids: ${registry.registry.journeys.map((j) => j.id).join(", ")}\n`);
13514
+ process.exit(ACCEPTANCE_EXIT_ERROR);
13515
+ }
13516
+ const deferralsPath = join$1(projectRoot, JOURNEY_DEFERRALS_PATH);
13517
+ let existingContent = "";
13518
+ try {
13519
+ existingContent = await readFile$1(deferralsPath, "utf-8");
13520
+ } catch {}
13521
+ const existing = parseJourneyDeferrals(existingContent);
13522
+ if (!existing.ok) {
13523
+ process.stdout.write(`cannot defer: ${JOURNEY_DEFERRALS_PATH} is invalid:\n`);
13524
+ for (const issue of existing.issues) process.stdout.write(` ${issue.path}: ${issue.message}\n`);
13525
+ process.exit(ACCEPTANCE_EXIT_ERROR);
13526
+ }
13527
+ if (existing.deferrals.some((d) => d.journey === journeyId)) {
13528
+ process.stdout.write(`journey ${journeyId} is already deferred — no change\n`);
13529
+ process.exit(ACCEPTANCE_EXIT_SUCCESS);
13530
+ }
13531
+ const deferrals = [...existing.deferrals, {
13532
+ journey: journeyId,
13533
+ reason: opts.reason,
13534
+ deferred_at: new Date().toISOString()
13535
+ }];
13536
+ const rendered = "deferrals:\n" + deferrals.map((d) => ` - journey: ${d.journey}\n reason: ${JSON.stringify(d.reason)}\n` + (d.deferred_at !== void 0 ? ` deferred_at: ${JSON.stringify(d.deferred_at)}\n` : "")).join("");
13537
+ try {
13538
+ await mkdir$1(dirname$1(deferralsPath), { recursive: true });
13539
+ await writeFile$1(deferralsPath, rendered, "utf-8");
13540
+ } catch (err) {
13541
+ process.stdout.write(`failed to write ${JOURNEY_DEFERRALS_PATH}: ${String(err)}\n`);
13542
+ process.exit(ACCEPTANCE_EXIT_ERROR);
13543
+ }
13544
+ process.stdout.write(`deferred ${journeyId} ("${opts.reason}") → ${JOURNEY_DEFERRALS_PATH}\nCOMMIT this file — the coverage audit reads the trusted (committed) tree.
13545
+ `);
13546
+ process.exit(ACCEPTANCE_EXIT_SUCCESS);
13486
13547
  });
13487
13548
  }
13488
13549
 
@@ -13560,8 +13621,8 @@ async function createProgram() {
13560
13621
  /** Fire-and-forget startup version check (story 8.3, AC3/AC5) */
13561
13622
  function checkForUpdatesInBackground(currentVersion) {
13562
13623
  if (process.env.SUBSTRATE_NO_UPDATE_CHECK === "1") return;
13563
- import("../upgrade-BOWLlLbD.js").then(async () => {
13564
- const { createVersionManager } = await import("../version-manager-impl-BVcsgUtN.js");
13624
+ import("../upgrade-M0UkDCeg.js").then(async () => {
13625
+ const { createVersionManager } = await import("../version-manager-impl-DeEPhQlA.js");
13565
13626
  const vm = createVersionManager();
13566
13627
  const result = await vm.checkForUpdates();
13567
13628
  if (result.updateAvailable) {
@@ -93,6 +93,12 @@ Each dispatched story runs in an isolated git worktree on its own branch (`subst
93
93
 
94
94
  Lifecycle events on the NDJSON stream: `story:committed` → (`story:merged`) → `story:finalized {mode, branch, sha, pr_url?}`. `substrate report` lists unmerged deliverable branches under "Finalization". Optional `finalization.epic_gate_command` runs before the LAST story of an epic integrates (non-zero exit → `epic-gate-failed`, branch preserved).
95
95
 
96
+ ### Acceptance Gate — journey coverage (optional)
97
+
98
+ If the project declares a journey registry at `.substrate/acceptance/journeys.yaml` (PRD user journeys with concrete end-states — COMMIT the file; substrate reads the committed copy, never an agent-writable worktree copy), the pipeline audits journey coverage at each epic close and at run end: every registered journey lands in exactly one state — `walked-pass`, `walked-fail`, `deferred`, `unclaimed` (NO story claims it — the never-wired-journey class), or `unwalked`. Results stream as `acceptance:coverage` events and land on the run manifest.
99
+
100
+ `acceptance.mode` in `.substrate/config.yaml`: `advisory` (default — warns, never blocks), `blocking` (an unclaimed/unwalked journey escalates `journey-unclaimed`/`journey-unwalked` on the LAST story of the epic before it integrates), or `off`. Create-story tags stories with the journeys they deliver (`journeys:` frontmatter). To explicitly skip a journey: `substrate acceptance defer <id> --reason "<why>"` and commit the deferral file. Lint the registry with `substrate acceptance validate`.
101
+
96
102
  ### Key Commands Reference
97
103
 
98
104
  | Command | Purpose |
@@ -1,4 +1,4 @@
1
- import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, listRequirements, registerArtifact, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./dist-BkhYgJWo.js";
1
+ import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, listRequirements, registerArtifact, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./dist-CpnLJuVw.js";
2
2
  import "./decisions-CzSIEeGP.js";
3
3
 
4
4
  export { getLatestRun };
@@ -6341,7 +6341,12 @@ const SubstrateConfigSchema = z.object({
6341
6341
  ]).optional(),
6342
6342
  merge_strategy: z.enum(["ff-only", "three-way"]).optional(),
6343
6343
  epic_gate_command: z.string().optional()
6344
- }).strict().optional()
6344
+ }).strict().optional(),
6345
+ acceptance: z.object({ mode: z.enum([
6346
+ "off",
6347
+ "advisory",
6348
+ "blocking"
6349
+ ]).optional() }).strict().optional()
6345
6350
  }).passthrough();
6346
6351
  const PartialProviderConfigSchema = ProviderConfigSchema.partial();
6347
6352
  const PartialGlobalSettingsSchema = GlobalSettingsSchema.partial();
@@ -6368,7 +6373,12 @@ const PartialSubstrateConfigSchema = z.object({
6368
6373
  ]).optional(),
6369
6374
  merge_strategy: z.enum(["ff-only", "three-way"]).optional(),
6370
6375
  epic_gate_command: z.string().optional()
6371
- }).strict().optional()
6376
+ }).strict().optional(),
6377
+ acceptance: z.object({ mode: z.enum([
6378
+ "off",
6379
+ "advisory",
6380
+ "blocking"
6381
+ ]).optional() }).strict().optional()
6372
6382
  }).passthrough();
6373
6383
 
6374
6384
  //#endregion
@@ -12628,4 +12638,4 @@ async function callLLM(params) {
12628
12638
 
12629
12639
  //#endregion
12630
12640
  export { ADVISORY_NOTES, AdapterRegistry, AdtError, BRANCH_PREFIX, BudgetConfigSchema, CLAUDE_AUTH_FAILURE_HINT, CODEX_SANDBOX_BLOCK_HINT, CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, Categorizer, ClaudeCodeAdapter, CodexCLIAdapter, ConfigError, ConfigIncompatibleFormatError, ConsumerAnalyzer, CostTrackerConfigSchema, DEFAULT_CONFIG, DEFAULT_GLOBAL_SETTINGS, DispatcherImpl, DoltClient, DoltNotInstalled, DoltQueryError, ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, EfficiencyScorer, GeminiCLIAdapter, GlobalSettingsSchema, InMemoryDatabaseAdapter, IngestionServer, LEARNING_FINDING, LogTurnAnalyzer, ModelRoutingConfigSchema, MonitorDatabaseImpl, OPERATIONAL_FINDING, PartialGlobalSettingsSchema, PartialProviderConfigSchema, ProviderPolicySchema, ProvidersSchema, Recommender, RoutingConfigError, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, STORY_METRICS, STORY_OUTCOME, SubstrateConfigSchema, TASK_TYPE_PHASE_MAP, TEST_EXPANSION_FINDING, TEST_PLAN, TelemetryConfigSchema, TelemetryNormalizer, TelemetryPipeline, TurnAnalyzer, VersionManagerImpl, addTokenUsage, aggregateTokenUsageForRun, aggregateTokenUsageForStory, buildAuditLogEntry, buildBranchName, buildModificationDirective, buildPRBody, buildWorktreePath, callLLM, checkDoltInstalled, classifyVersionGap, compareRunMetrics, createAmendmentRun, createConfigSystem, createDatabaseAdapter as createDatabaseAdapter$1, createDecision, createExperimenter, createGitWorktreeManager, createPipelineRun, createRequirement, createStderrLogger, createVersionManager, detectClaudeAuthFailure, detectCodexSandboxBlock, detectInterfaceChanges, determineVerdict, getActiveDecisions, getAllCostEntriesFiltered, getArtifactByTypeForRun, getArtifactsByRun, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getModelTier, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initWorkGraphSchema, initializeDolt, listRequirements, listRunMetrics, loadModelRoutingConfig, loadParentRunDecisions, registerArtifact, resolvePromptFile, supersedeDecision, swallowDebug, tagRunAsBaseline, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision, writeRunMetrics, writeStoryMetrics };
12631
- //# sourceMappingURL=dist-BkhYgJWo.js.map
12641
+ //# sourceMappingURL=dist-CpnLJuVw.js.map
@@ -1,4 +1,4 @@
1
- import { AdtError } from "./dist-BkhYgJWo.js";
1
+ import { AdtError } from "./dist-CpnLJuVw.js";
2
2
 
3
3
  //#region src/core/errors.ts
4
4
  /** Error thrown when task configuration is invalid */
@@ -71,4 +71,4 @@ var TaskGraphIncompatibleFormatError = class extends AdtError {
71
71
 
72
72
  //#endregion
73
73
  export { BudgetExceededError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError };
74
- //# sourceMappingURL=errors-C7pY6_yQ.js.map
74
+ //# sourceMappingURL=errors-8tVEwG6v.js.map
@@ -1,3 +1,3 @@
1
- import { buildAuditLogEntry, buildBranchName, buildModificationDirective, buildPRBody, buildWorktreePath, createExperimenter, determineVerdict, resolvePromptFile } from "./dist-BkhYgJWo.js";
1
+ import { buildAuditLogEntry, buildBranchName, buildModificationDirective, buildPRBody, buildWorktreePath, createExperimenter, determineVerdict, resolvePromptFile } from "./dist-CpnLJuVw.js";
2
2
 
3
3
  export { createExperimenter };
@@ -1,6 +1,6 @@
1
1
  import { createLogger } from "./logger-KeHncl-f.js";
2
- import { DoltClient, DoltQueryError, createDatabaseAdapter$1 as createDatabaseAdapter, getLatestRun, getPipelineRunById, initSchema } from "./dist-BkhYgJWo.js";
3
- import { resolveMainRepoRoot, resolveRunManifest } from "./manifest-read-DyyFPrJH.js";
2
+ import { DoltClient, DoltQueryError, createDatabaseAdapter$1 as createDatabaseAdapter, getLatestRun, getPipelineRunById, initSchema } from "./dist-CpnLJuVw.js";
3
+ import { resolveMainRepoRoot, resolveRunManifest } from "./manifest-read-DZDlSq_s.js";
4
4
  import { createRequire } from "module";
5
5
  import { dirname, join } from "path";
6
6
  import { existsSync, readFileSync } from "node:fs";
@@ -1000,4 +1000,4 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
1000
1000
 
1001
1001
  //#endregion
1002
1002
  export { BMAD_BASELINE_TOKENS_FULL, DEFAULT_STALL_THRESHOLD_SECONDS, FileKvStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter$1 as createDatabaseAdapter, createDoltOperatorReader, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, isOrchestratorProcessLine, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, runHealthAction, validateStoryKey };
1003
- //# sourceMappingURL=health-DvgJ7jj_.js.map
1003
+ //# sourceMappingURL=health-BUl3y64N.js.map
@@ -1,7 +1,7 @@
1
- import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-DvgJ7jj_.js";
1
+ import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-BUl3y64N.js";
2
2
  import "./logger-KeHncl-f.js";
3
- import "./dist-BkhYgJWo.js";
4
- import "./manifest-read-DyyFPrJH.js";
3
+ import "./dist-CpnLJuVw.js";
4
+ import "./manifest-read-DZDlSq_s.js";
5
5
  import "./work-graph-repository-DZyJv5pV.js";
6
6
  import "./decisions-CzSIEeGP.js";
7
7
 
@@ -314,6 +314,13 @@ declare const SubstrateConfigSchema: z.ZodObject<{
314
314
  }>>;
315
315
  epic_gate_command: z.ZodOptional<z.ZodString>;
316
316
  }, z.core.$strict>>;
317
+ acceptance: z.ZodOptional<z.ZodObject<{
318
+ mode: z.ZodOptional<z.ZodEnum<{
319
+ off: "off";
320
+ advisory: "advisory";
321
+ blocking: "blocking";
322
+ }>>;
323
+ }, z.core.$strict>>;
317
324
  }, z.core.$loose>;
318
325
  type SubstrateConfig = z.infer<typeof SubstrateConfigSchema>; //#endregion
319
326
  //#region packages/core/dist/events/core-events.d.ts
@@ -1622,4 +1629,4 @@ interface Recommendation {
1622
1629
 
1623
1630
  //#endregion
1624
1631
  export { AdapterDiscoveryResult, AdapterRegistry as AdapterRegistry$1, AdtError as AdtError$1, ClaudeCodeAdapter as ClaudeCodeAdapter$1, CodexCLIAdapter as CodexCLIAdapter$1, ConfigError as ConfigError$1, ConfigIncompatibleFormatError as ConfigIncompatibleFormatError$1, CoreEvents, DatabaseAdapter, DiscoveryReport, GeminiCLIAdapter as GeminiCLIAdapter$1, Recommendation, TypedEventBus };
1625
- //# sourceMappingURL=index-CnV5PcSR.d.ts.map
1632
+ //# sourceMappingURL=index-C31XtSMn.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AdapterDiscoveryResult, AdapterRegistry$1 as AdapterRegistry, AdtError$1 as AdtError, ClaudeCodeAdapter$1 as ClaudeCodeAdapter, CodexCLIAdapter$1 as CodexCLIAdapter, ConfigError$1 as ConfigError, ConfigIncompatibleFormatError$1 as ConfigIncompatibleFormatError, DiscoveryReport, GeminiCLIAdapter$1 as GeminiCLIAdapter, Recommendation, TypedEventBus } from "./index-CnV5PcSR.js";
1
+ import { AdapterDiscoveryResult, AdapterRegistry$1 as AdapterRegistry, AdtError$1 as AdtError, ClaudeCodeAdapter$1 as ClaudeCodeAdapter, CodexCLIAdapter$1 as CodexCLIAdapter, ConfigError$1 as ConfigError, ConfigIncompatibleFormatError$1 as ConfigIncompatibleFormatError, DiscoveryReport, GeminiCLIAdapter$1 as GeminiCLIAdapter, Recommendation, TypedEventBus } from "./index-C31XtSMn.js";
2
2
  import pino from "pino";
3
3
  import { z } from "zod";
4
4
  import { Readable, Writable } from "node:stream";
@@ -203,6 +203,32 @@ interface StoryFinalizedEvent {
203
203
  /** PR URL (pr mode, only when gh pr create succeeded) */
204
204
  pr_url?: string;
205
205
  }
206
+ /**
207
+ * A0.3 (acceptance-gate): journey coverage audit result, emitted at each
208
+ * epic close and at run end (`scope: "final"`). Every registered journey in
209
+ * scope lands in exactly one state; `unclaimed` is the never-wired-journey
210
+ * class (UJ-2) caught structurally. In `blocking` mode an unclaimed/unwalked
211
+ * journey escalates the LAST story of the epic before it integrates; in
212
+ * `advisory` mode (the default) this event and a warning are the signal.
213
+ */
214
+ interface AcceptanceCoverageEvent {
215
+ type: 'acceptance:coverage';
216
+ /** ISO-8601 timestamp generated at emit time */
217
+ ts: string;
218
+ /** Audited boundary: `epic-<n>` or `final` */
219
+ scope: string;
220
+ /** Audit posture the run is under */
221
+ mode: 'advisory' | 'blocking';
222
+ /** Per-journey coverage states */
223
+ entries: {
224
+ journeyId: string;
225
+ criticality: 'critical' | 'standard';
226
+ state: 'walked-pass' | 'walked-fail' | 'deferred' | 'unclaimed' | 'unwalked';
227
+ ownerStories: string[];
228
+ }[];
229
+ /** State counts across `entries` */
230
+ summary: Record<string, number>;
231
+ }
206
232
  /**
207
233
  * Emitted when the orchestrator auto-approves a story after exhausting
208
234
  * review cycles with only minor issues remaining. Provides transparency
@@ -836,7 +862,7 @@ interface PipelineMergeConflictDetectedEvent {
836
862
  * }
837
863
  * ```
838
864
  */
839
- type PipelineEvent = PipelineStartEvent | PipelineCompleteEvent | PipelinePreFlightFailureEvent | PipelineProfileStaleEvent | PipelineContractMismatchEvent | PipelineContractVerificationSummaryEvent | StoryPhaseEvent | StoryDoneEvent | StoryCommittedEvent | StoryMergedEvent | StoryFinalizedEvent | 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 | PipelineMergeConflictDetectedEvent; //#endregion
865
+ type PipelineEvent = PipelineStartEvent | PipelineCompleteEvent | PipelinePreFlightFailureEvent | PipelineProfileStaleEvent | PipelineContractMismatchEvent | PipelineContractVerificationSummaryEvent | StoryPhaseEvent | StoryDoneEvent | StoryCommittedEvent | StoryMergedEvent | StoryFinalizedEvent | AcceptanceCoverageEvent | 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 | PipelineMergeConflictDetectedEvent; //#endregion
840
866
  //#region src/core/errors.d.ts
841
867
 
842
868
  /**
@@ -1497,6 +1523,23 @@ interface OrchestratorEvents {
1497
1523
  sha: string;
1498
1524
  prUrl?: string;
1499
1525
  };
1526
+ /**
1527
+ * A0.3 (acceptance-gate): journey coverage audit result — emitted at epic
1528
+ * close and at run end. Mapped to the NDJSON `acceptance:coverage` event.
1529
+ */
1530
+ 'orchestrator:acceptance-coverage': {
1531
+ scope: string;
1532
+ mode: 'advisory' | 'blocking';
1533
+ entries: {
1534
+ journeyId: string;
1535
+ title: string;
1536
+ criticality: 'critical' | 'standard';
1537
+ epic?: number;
1538
+ state: 'walked-pass' | 'walked-fail' | 'deferred' | 'unclaimed' | 'unwalked';
1539
+ ownerStories: string[];
1540
+ }[];
1541
+ summary: Record<string, number>;
1542
+ };
1500
1543
  /** A story has been escalated after exceeding max review cycles */
1501
1544
  'orchestrator:story-escalated': {
1502
1545
  storyKey: string;
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { childLogger, createLogger, logger } from "./logger-KeHncl-f.js";
2
2
  import { assertDefined, createEventBus, createTuiApp, deepClone, formatDuration, generateId, isPlainObject, isTuiCapable, printNonTtyWarning, sleep, withRetry } from "./helpers-CElYrONe.js";
3
- import { AdapterRegistry, AdtError, ClaudeCodeAdapter, CodexCLIAdapter, ConfigError, ConfigIncompatibleFormatError, GeminiCLIAdapter } from "./dist-BkhYgJWo.js";
3
+ import { AdapterRegistry, AdtError, ClaudeCodeAdapter, CodexCLIAdapter, ConfigError, ConfigIncompatibleFormatError, GeminiCLIAdapter } from "./dist-CpnLJuVw.js";
4
4
  import "./adapter-registry-DXLMTmfD.js";
5
- import { BudgetExceededError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError } from "./errors-C7pY6_yQ.js";
5
+ import { BudgetExceededError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError } from "./errors-8tVEwG6v.js";
6
6
 
7
7
  //#region src/core/di.ts
8
8
  /**
@@ -1,5 +1,5 @@
1
1
  import { createLogger } from "./logger-KeHncl-f.js";
2
- import { readCurrentRunId, resolveMainRepoRoot } from "./manifest-read-DyyFPrJH.js";
2
+ import { readCurrentRunId, resolveMainRepoRoot } from "./manifest-read-DZDlSq_s.js";
3
3
  import { join } from "node:path";
4
4
  import { mkdir, readFile, writeFile } from "node:fs/promises";
5
5
  import * as readline from "node:readline";
@@ -180,4 +180,4 @@ async function runInteractivePrompt(decisionContext) {
180
180
 
181
181
  //#endregion
182
182
  export { runInteractivePrompt };
183
- //# sourceMappingURL=interactive-prompt-CPF2GTvV.js.map
183
+ //# sourceMappingURL=interactive-prompt-C4Is1QDB.js.map
@@ -1,5 +1,5 @@
1
1
  import { createLogger } from "./logger-KeHncl-f.js";
2
- import { LEARNING_FINDING, createDecision, getDecisionsByCategory } from "./dist-BkhYgJWo.js";
2
+ import { LEARNING_FINDING, createDecision, getDecisionsByCategory } from "./dist-CpnLJuVw.js";
3
3
  import * as path$1 from "path";
4
4
  import { join } from "path";
5
5
  import { readFile } from "fs/promises";
@@ -8,9 +8,10 @@ import { YAMLException, load } from "js-yaml";
8
8
  import { existsSync, promises, readFileSync, readdirSync, statSync } from "node:fs";
9
9
  import { execSync, spawn } from "node:child_process";
10
10
  import * as path$2 from "node:path";
11
- import { basename as basename$1, delimiter, dirname as dirname$1, join as join$1, resolve as resolve$1 } from "node:path";
11
+ import { basename as basename$1, delimiter, dirname as dirname$1, join as join$1, relative, resolve as resolve$1 } from "node:path";
12
+ import { createHash } from "node:crypto";
12
13
  import { z } from "zod";
13
- import { mkdir as mkdir$1, open, readFile as readFile$1, unlink as unlink$1, writeFile as writeFile$1 } from "node:fs/promises";
14
+ import { mkdir as mkdir$1, open, readFile as readFile$1, readdir as readdir$1, unlink as unlink$1, writeFile as writeFile$1 } from "node:fs/promises";
14
15
  import * as fs from "fs";
15
16
  import { createRequire } from "node:module";
16
17
  import { fileURLToPath } from "node:url";
@@ -5080,6 +5081,162 @@ function parseJourneyRegistry(yamlContent) {
5080
5081
  };
5081
5082
  }
5082
5083
 
5084
+ //#endregion
5085
+ //#region packages/sdlc/dist/acceptance/coverage.js
5086
+ /**
5087
+ * Compute the coverage state of every journey audited at this scope.
5088
+ *
5089
+ * - `{epic: n}` audits journeys declaring `epic: n`.
5090
+ * - `{final: true}` audits the FULL registry (epicless journeys have no
5091
+ * earlier audit point; epic-scoped ones are re-audited — idempotent).
5092
+ *
5093
+ * State precedence: deferred > walked-fail > walked-pass > unwalked/unclaimed.
5094
+ * An operator deferral wins over everything (explicit scope decision); a fail
5095
+ * verdict wins over a pass (any failing end-state fails the journey).
5096
+ */
5097
+ function computeJourneyCoverage(input) {
5098
+ const { registry, claims, verdicts, deferredJourneyIds, scope } = input;
5099
+ const audited = registry.journeys.filter((j) => "final" in scope ? true : j.epic === scope.epic);
5100
+ const claimsByJourney = new Map();
5101
+ for (const claim of claims) {
5102
+ const owners = claimsByJourney.get(claim.journeyId) ?? [];
5103
+ if (!owners.includes(claim.storyKey)) owners.push(claim.storyKey);
5104
+ claimsByJourney.set(claim.journeyId, owners);
5105
+ }
5106
+ const deferred = new Set(deferredJourneyIds);
5107
+ const verdictByJourney = new Map();
5108
+ for (const v of verdicts) {
5109
+ const existing = verdictByJourney.get(v.journeyId);
5110
+ verdictByJourney.set(v.journeyId, existing === "fail" ? "fail" : v.verdict);
5111
+ }
5112
+ return audited.map((journey) => {
5113
+ const ownerStories = claimsByJourney.get(journey.id) ?? [];
5114
+ let state;
5115
+ if (deferred.has(journey.id)) state = "deferred";
5116
+ else {
5117
+ const verdict = verdictByJourney.get(journey.id);
5118
+ if (verdict === "fail") state = "walked-fail";
5119
+ else if (verdict === "pass") state = "walked-pass";
5120
+ else state = ownerStories.length > 0 ? "unwalked" : "unclaimed";
5121
+ }
5122
+ return {
5123
+ journeyId: journey.id,
5124
+ title: journey.title,
5125
+ criticality: journey.criticality,
5126
+ ...journey.epic !== void 0 ? { epic: journey.epic } : {},
5127
+ state,
5128
+ ownerStories
5129
+ };
5130
+ });
5131
+ }
5132
+ /** Aggregate counts for the `acceptance:coverage` event payload. */
5133
+ function summarizeCoverage(entries) {
5134
+ const summary = {
5135
+ "walked-pass": 0,
5136
+ "walked-fail": 0,
5137
+ deferred: 0,
5138
+ unclaimed: 0,
5139
+ unwalked: 0
5140
+ };
5141
+ for (const entry of entries) summary[entry.state] += 1;
5142
+ return summary;
5143
+ }
5144
+ /** Repo-relative path of the operator deferral file. */
5145
+ const JOURNEY_DEFERRALS_PATH = ".substrate/acceptance/deferrals.yaml";
5146
+ const JourneyDeferralSchema = z.object({
5147
+ journey: z.string().min(1),
5148
+ reason: z.string().min(1, "a deferral must carry an operator reason"),
5149
+ deferred_at: z.string().optional()
5150
+ });
5151
+ const JourneyDeferralsFileSchema = z.object({ deferrals: z.array(JourneyDeferralSchema) });
5152
+ /** Parse deferrals YAML. Never throws; malformed content comes back as issues. */
5153
+ function parseJourneyDeferrals(yamlContent) {
5154
+ let doc;
5155
+ try {
5156
+ doc = load(yamlContent);
5157
+ } catch (err) {
5158
+ const message = err instanceof YAMLException ? err.message : String(err);
5159
+ return {
5160
+ ok: false,
5161
+ issues: [{
5162
+ path: "(root)",
5163
+ message: `malformed YAML: ${message}`
5164
+ }]
5165
+ };
5166
+ }
5167
+ if (doc === null || doc === void 0) return {
5168
+ ok: true,
5169
+ deferrals: []
5170
+ };
5171
+ const result = JourneyDeferralsFileSchema.safeParse(doc);
5172
+ if (!result.success) return {
5173
+ ok: false,
5174
+ issues: result.error.issues.map((issue) => ({
5175
+ path: issue.path.length > 0 ? issue.path.map(String).join(".") : "(root)",
5176
+ message: issue.message
5177
+ }))
5178
+ };
5179
+ return {
5180
+ ok: true,
5181
+ deferrals: result.data.deferrals
5182
+ };
5183
+ }
5184
+
5185
+ //#endregion
5186
+ //#region packages/sdlc/dist/acceptance/contract.js
5187
+ /** The acceptance contract lives inside the project profile. */
5188
+ const ACCEPTANCE_CONTRACT_PROFILE_PATH = ".substrate/project-profile.yaml";
5189
+ const RenderSurfaceSchema = z.object({ render: z.string().min(1) }).strict();
5190
+ const WebSurfaceSchema = z.object({
5191
+ serve: z.string().min(1),
5192
+ ready: z.string().optional()
5193
+ }).strict();
5194
+ const AcceptanceContractSchema = z.object({
5195
+ fixtures: z.string().optional(),
5196
+ surfaces: z.object({
5197
+ email: RenderSurfaceSchema.optional(),
5198
+ cli: RenderSurfaceSchema.optional(),
5199
+ file: RenderSurfaceSchema.optional(),
5200
+ web: WebSurfaceSchema.optional()
5201
+ }).strict()
5202
+ }).strict();
5203
+ /**
5204
+ * Extract + validate the `acceptance:` block from project-profile YAML
5205
+ * content. `absent` when the profile has no block (legal — the coverage
5206
+ * audit escalates registry-present+contract-absent as acceptance-unrunnable
5207
+ * in blocking mode; pure accounting still works without one).
5208
+ */
5209
+ function parseAcceptanceContract(profileYamlContent) {
5210
+ let doc;
5211
+ try {
5212
+ doc = load(profileYamlContent);
5213
+ } catch (err) {
5214
+ const message = err instanceof YAMLException ? err.message : String(err);
5215
+ return {
5216
+ status: "invalid",
5217
+ issues: [{
5218
+ path: "(root)",
5219
+ message: `malformed profile YAML: ${message}`
5220
+ }]
5221
+ };
5222
+ }
5223
+ if (doc === null || doc === void 0 || typeof doc !== "object") return { status: "absent" };
5224
+ const block = doc["acceptance"];
5225
+ if (block === void 0 || block === null) return { status: "absent" };
5226
+ const result = AcceptanceContractSchema.safeParse(block);
5227
+ if (!result.success) return {
5228
+ status: "invalid",
5229
+ issues: result.error.issues.map((issue) => ({
5230
+ path: issue.path.length > 0 ? `acceptance.${issue.path.map(String).join(".")}` : "acceptance",
5231
+ message: issue.message
5232
+ }))
5233
+ };
5234
+ return {
5235
+ status: "ok",
5236
+ contract: result.data
5237
+ };
5238
+ }
5239
+
5083
5240
  //#endregion
5084
5241
  //#region packages/sdlc/dist/acceptance/loader.js
5085
5242
  /** stderr shapes that mean "the path is not in the tree at this ref" (absent, not an error). */
@@ -5154,6 +5311,57 @@ async function loadJourneyRegistryFromTrustedTree(repoRoot, ref = "HEAD") {
5154
5311
  };
5155
5312
  }
5156
5313
  /**
5314
+ * Load operator journey deferrals from the trusted tree at `ref`.
5315
+ * A0.3 note: `HEAD` at run end can include story-merged edits — full
5316
+ * tamper-hardening of this input is A1.3 scope (spec-tamper tripwire).
5317
+ */
5318
+ async function loadJourneyDeferralsFromTrustedTree(repoRoot, ref = "HEAD") {
5319
+ const result = await runGitShow(repoRoot, ref, JOURNEY_DEFERRALS_PATH);
5320
+ if (result.spawnError !== void 0) return {
5321
+ status: "error",
5322
+ message: `git show could not be spawned: ${result.spawnError}`
5323
+ };
5324
+ if (result.code !== 0) {
5325
+ if (GIT_SHOW_ABSENT_PATTERNS.some((p) => p.test(result.stderr))) return {
5326
+ status: "ok",
5327
+ deferrals: []
5328
+ };
5329
+ return {
5330
+ status: "error",
5331
+ message: `git show ${ref}:${JOURNEY_DEFERRALS_PATH} failed (exit ${String(result.code)}): ${result.stderr.trim()}`
5332
+ };
5333
+ }
5334
+ const parsed = parseJourneyDeferrals(result.stdout);
5335
+ if (!parsed.ok) return {
5336
+ status: "invalid",
5337
+ issues: parsed.issues
5338
+ };
5339
+ return {
5340
+ status: "ok",
5341
+ deferrals: parsed.deferrals
5342
+ };
5343
+ }
5344
+ /**
5345
+ * Load the acceptance contract (the `acceptance:` block of the project
5346
+ * profile) from the trusted tree at `ref` (A1.1). Same H7 posture as the
5347
+ * registry: the agent-writable worktree copy of the profile is never read.
5348
+ */
5349
+ async function loadAcceptanceContractFromTrustedTree(repoRoot, ref = "HEAD") {
5350
+ const result = await runGitShow(repoRoot, ref, ACCEPTANCE_CONTRACT_PROFILE_PATH);
5351
+ if (result.spawnError !== void 0) return {
5352
+ status: "error",
5353
+ message: `git show could not be spawned: ${result.spawnError}`
5354
+ };
5355
+ if (result.code !== 0) {
5356
+ if (GIT_SHOW_ABSENT_PATTERNS.some((p) => p.test(result.stderr))) return { status: "absent" };
5357
+ return {
5358
+ status: "error",
5359
+ message: `git show ${ref}:${ACCEPTANCE_CONTRACT_PROFILE_PATH} failed (exit ${String(result.code)}): ${result.stderr.trim()}`
5360
+ };
5361
+ }
5362
+ return parseAcceptanceContract(result.stdout);
5363
+ }
5364
+ /**
5157
5365
  * Filesystem read for OPERATOR LINT ONLY (`substrate acceptance validate`).
5158
5366
  * Gate/judge code paths must use `loadJourneyRegistryFromTrustedTree`.
5159
5367
  */
@@ -5385,6 +5593,7 @@ const PerStoryStateSchema = z.object({
5385
5593
  escalation_detail: z.string().optional(),
5386
5594
  commit_sha: z.string().optional(),
5387
5595
  baseline_sha: z.string().optional(),
5596
+ journeys: z.array(z.string()).optional(),
5388
5597
  checkpoint_sha: z.string().optional(),
5389
5598
  finalization: z.object({
5390
5599
  mode: z.union([
@@ -5519,6 +5728,20 @@ const RunManifestSchema = z.object({
5519
5728
  orchestrator_start_branch: z.string().optional(),
5520
5729
  stopped_reason: z.string().optional(),
5521
5730
  stopped_at: z.string().optional(),
5731
+ journeys: z.array(z.object({
5732
+ journeyId: z.string(),
5733
+ title: z.string(),
5734
+ criticality: z.enum(["critical", "standard"]),
5735
+ epic: z.number().optional(),
5736
+ state: z.enum([
5737
+ "walked-pass",
5738
+ "walked-fail",
5739
+ "deferred",
5740
+ "unclaimed",
5741
+ "unwalked"
5742
+ ]),
5743
+ ownerStories: z.array(z.string())
5744
+ })).optional(),
5522
5745
  generation: z.number().int().nonnegative(),
5523
5746
  created_at: z.string(),
5524
5747
  updated_at: z.string()
@@ -6599,5 +6822,5 @@ async function resolveRunManifest(dbRoot, runId) {
6599
6822
  }
6600
6823
 
6601
6824
  //#endregion
6602
- export { FindingsInjector, JOURNEY_REGISTRY_PATH, RunManifest, RuntimeProbeListSchema, SupervisorLock, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, applyConfigToGraph, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, loadJourneyRegistryFromFile, loadJourneyRegistryFromTrustedTree, parseRuntimeProbes, parseStoryFrontmatter, readCurrentRunId, renderFindings, resolveGraphPath, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics, runAcTraceabilityCheck, runStaleVerificationRecovery };
6603
- //# sourceMappingURL=manifest-read-DyyFPrJH.js.map
6825
+ export { ACCEPTANCE_CONTRACT_PROFILE_PATH, FindingsInjector, JOURNEY_DEFERRALS_PATH, JOURNEY_REGISTRY_PATH, RunManifest, RuntimeProbeListSchema, SupervisorLock, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, applyConfigToGraph, computeJourneyCoverage, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, loadAcceptanceContractFromTrustedTree, loadJourneyDeferralsFromTrustedTree, loadJourneyRegistryFromFile, loadJourneyRegistryFromTrustedTree, parseAcceptanceContract, parseJourneyDeferrals, parseRuntimeProbes, parseStoryFrontmatter, readCurrentRunId, renderFindings, resolveGraphPath, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics, runAcTraceabilityCheck, runStaleVerificationRecovery, summarizeCoverage };
6826
+ //# sourceMappingURL=manifest-read-DZDlSq_s.js.map
@@ -1,6 +1,6 @@
1
1
  import "../../logger-KeHncl-f.js";
2
- import "../../dist-BkhYgJWo.js";
3
- import "../../manifest-read-DyyFPrJH.js";
4
- import { runInteractivePrompt } from "../../interactive-prompt-CPF2GTvV.js";
2
+ import "../../dist-CpnLJuVw.js";
3
+ import "../../manifest-read-DZDlSq_s.js";
4
+ import { runInteractivePrompt } from "../../interactive-prompt-C4Is1QDB.js";
5
5
 
6
6
  export { runInteractivePrompt };
@@ -1,4 +1,4 @@
1
- import { ModelRoutingConfigSchema, ProviderPolicySchema, RoutingConfigError, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, TASK_TYPE_PHASE_MAP, getModelTier, loadModelRoutingConfig } from "./dist-BkhYgJWo.js";
1
+ import { ModelRoutingConfigSchema, ProviderPolicySchema, RoutingConfigError, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, TASK_TYPE_PHASE_MAP, getModelTier, loadModelRoutingConfig } from "./dist-CpnLJuVw.js";
2
2
  import "./routing-DFxoKHDt.js";
3
3
 
4
4
  export { loadModelRoutingConfig };
@@ -1,14 +1,14 @@
1
- import "./health-DvgJ7jj_.js";
1
+ import "./health-BUl3y64N.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./helpers-CElYrONe.js";
4
- import "./dist-BkhYgJWo.js";
5
- import "./manifest-read-DyyFPrJH.js";
6
- import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-CAmBrDky.js";
4
+ import "./dist-CpnLJuVw.js";
5
+ import "./manifest-read-DZDlSq_s.js";
6
+ import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-nSb1TD3o.js";
7
7
  import "./routing-DFxoKHDt.js";
8
8
  import "./work-graph-repository-DZyJv5pV.js";
9
9
  import "./decisions-CzSIEeGP.js";
10
10
  import "./decision-router-BAPpON_C.js";
11
- import "./interactive-prompt-CPF2GTvV.js";
11
+ import "./interactive-prompt-C4Is1QDB.js";
12
12
  import "./recovery-engine-BKGBeBnW.js";
13
13
 
14
14
  export { runRunAction };
@@ -1,11 +1,11 @@
1
- import { BMAD_BASELINE_TOKENS_FULL, FileKvStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, validateStoryKey } from "./health-DvgJ7jj_.js";
1
+ import { BMAD_BASELINE_TOKENS_FULL, FileKvStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, validateStoryKey } from "./health-BUl3y64N.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, BRANCH_PREFIX, CLAUDE_AUTH_FAILURE_HINT, CODEX_SANDBOX_BLOCK_HINT, 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, classifyVersionGap, createConfigSystem, createDatabaseAdapter$1, createDecision, createGitWorktreeManager, createPipelineRun, createRequirement, detectClaudeAuthFailure, detectCodexSandboxBlock, detectInterfaceChanges, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunMetrics, getRunningPipelineRuns, getStoryMetricsForRun, getTokenUsageSummary, initSchema, listRequirements, loadModelRoutingConfig, registerArtifact, swallowDebug, updatePipelineRun, updatePipelineRunConfig, upsertDecision, writeRunMetrics, writeStoryMetrics } from "./dist-BkhYgJWo.js";
5
- import { FindingsInjector, RunManifest, RuntimeProbeListSchema, applyConfigToGraph, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, loadJourneyRegistryFromTrustedTree, parseRuntimeProbes, parseStoryFrontmatter, renderFindings, resolveGraphPath, resolveMainRepoRoot, runAcTraceabilityCheck, runStaleVerificationRecovery } from "./manifest-read-DyyFPrJH.js";
4
+ import { ADVISORY_NOTES, BRANCH_PREFIX, CLAUDE_AUTH_FAILURE_HINT, CODEX_SANDBOX_BLOCK_HINT, 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, classifyVersionGap, createConfigSystem, createDatabaseAdapter$1, createDecision, createGitWorktreeManager, createPipelineRun, createRequirement, detectClaudeAuthFailure, detectCodexSandboxBlock, detectInterfaceChanges, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunMetrics, getRunningPipelineRuns, getStoryMetricsForRun, getTokenUsageSummary, initSchema, listRequirements, loadModelRoutingConfig, registerArtifact, swallowDebug, updatePipelineRun, updatePipelineRunConfig, upsertDecision, writeRunMetrics, writeStoryMetrics } from "./dist-CpnLJuVw.js";
5
+ import { ACCEPTANCE_CONTRACT_PROFILE_PATH, FindingsInjector, JOURNEY_DEFERRALS_PATH, RunManifest, RuntimeProbeListSchema, applyConfigToGraph, computeJourneyCoverage, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, loadAcceptanceContractFromTrustedTree, loadJourneyDeferralsFromTrustedTree, loadJourneyRegistryFromTrustedTree, parseRuntimeProbes, parseStoryFrontmatter, renderFindings, resolveGraphPath, resolveMainRepoRoot, runAcTraceabilityCheck, runStaleVerificationRecovery, summarizeCoverage } from "./manifest-read-DZDlSq_s.js";
6
6
  import { WorkGraphRepository, detectCycles } from "./work-graph-repository-DZyJv5pV.js";
7
7
  import { deriveExitCode, routeDecision } from "./decision-router-BAPpON_C.js";
8
- import { runInteractivePrompt } from "./interactive-prompt-CPF2GTvV.js";
8
+ import { runInteractivePrompt } from "./interactive-prompt-C4Is1QDB.js";
9
9
  import { runRecoveryEngine } from "./recovery-engine-BKGBeBnW.js";
10
10
  import { basename, dirname, extname, join } from "path";
11
11
  import { access, readFile, readdir, stat } from "fs/promises";
@@ -933,6 +933,38 @@ const PIPELINE_EVENT_METADATA = [
933
933
  }
934
934
  ]
935
935
  },
936
+ {
937
+ type: "acceptance:coverage",
938
+ description: "Journey coverage audit result (acceptance gate, A0.3). Every registered journey in scope lands in exactly one state: walked-pass, walked-fail, deferred, unclaimed (NO story claims it — the never-wired-journey class), or unwalked (claimed but never walked). Advisory mode warns; blocking mode escalates the last story of the epic.",
939
+ when: "At each epic close and once at run end (scope: \"final\").",
940
+ fields: [
941
+ {
942
+ name: "ts",
943
+ type: "string",
944
+ description: "Timestamp."
945
+ },
946
+ {
947
+ name: "scope",
948
+ type: "string",
949
+ description: "Audited boundary: epic-<n> or final."
950
+ },
951
+ {
952
+ name: "mode",
953
+ type: "advisory|blocking",
954
+ description: "Audit posture (acceptance.mode config)."
955
+ },
956
+ {
957
+ name: "entries",
958
+ type: "array",
959
+ description: "Per-journey {journeyId, criticality, state, ownerStories}."
960
+ },
961
+ {
962
+ name: "summary",
963
+ type: "object",
964
+ description: "State counts across entries."
965
+ }
966
+ ]
967
+ },
936
968
  {
937
969
  type: "story:escalation",
938
970
  description: "Story escalated — either max review cycles exceeded or a precondition for SHIP_IT was not met.",
@@ -951,7 +983,7 @@ const PIPELINE_EVENT_METADATA = [
951
983
  {
952
984
  name: "reason",
953
985
  type: "string",
954
- description: "Escalation reason. Common values: review-cycles-exhausted (default review-loop max hit); create-story-no-file (create-story phase did not write a story artifact); dev-story-no-commit (substrate auto-commit found no committable changes — agent produced nothing or all changes were outside the worktree); dev-story-commit-failed (substrate auto-commit failed — typically a pre-commit hook rejected the commit; stderr captured in issues); merge-to-main-error (unexpected error in the merge-to-main phase); merge-conflict-detected (story branch could not be merged due to conflicts); parent-tree-dirtied-by-run (uncommitted parent changes intersect the story diff — merge refused, H3.3); ff-only-merge-not-possible (base moved and merge_strategy is ff-only — set finalization.merge_strategy: three-way or integrate manually, H3.3); epic-gate-failed (finalization.epic_gate_command exited non-zero before the last story of an epic integrated, H3.4); story-file-missing (story artifact gone from working tree AND branch HEAD — check wip/feat commits on the story branch, H5.5); undisclosed-files-in-merge (a committed implementation file was never in the dev agent's files_modified, so no review cycle inspected it — inspect the branch, then re-dispatch with accurate files_modified or merge manually, H7). v0.20.87+."
986
+ description: "Escalation reason. Common values: review-cycles-exhausted (default review-loop max hit); create-story-no-file (create-story phase did not write a story artifact); dev-story-no-commit (substrate auto-commit found no committable changes — agent produced nothing or all changes were outside the worktree); dev-story-commit-failed (substrate auto-commit failed — typically a pre-commit hook rejected the commit; stderr captured in issues); merge-to-main-error (unexpected error in the merge-to-main phase); merge-conflict-detected (story branch could not be merged due to conflicts); parent-tree-dirtied-by-run (uncommitted parent changes intersect the story diff — merge refused, H3.3); ff-only-merge-not-possible (base moved and merge_strategy is ff-only — set finalization.merge_strategy: three-way or integrate manually, H3.3); epic-gate-failed (finalization.epic_gate_command exited non-zero before the last story of an epic integrated, H3.4); story-file-missing (story artifact gone from working tree AND branch HEAD — check wip/feat commits on the story branch, H5.5); undisclosed-files-in-merge (a committed implementation file was never in the dev agent's files_modified, so no review cycle inspected it — inspect the branch, then re-dispatch with accurate files_modified or merge manually, H7); journey-unclaimed (acceptance.mode: blocking — a registered journey has NO story claiming it at epic close; wire it or defer via `substrate acceptance defer`, A0.3); journey-unwalked (acceptance.mode: blocking — a claimed journey was never walked by the acceptance gate at epic close, A0.3); acceptance-unrunnable (acceptance.mode: blocking — a journey registry exists but the committed acceptance contract/registry is absent or invalid, so claimed journeys can never be walked; declare an acceptance: block in the project profile, A1.1). v0.20.87+."
955
987
  },
956
988
  {
957
989
  name: "cycles",
@@ -6319,6 +6351,7 @@ async function runCreateStory(deps, params) {
6319
6351
  };
6320
6352
  }
6321
6353
  const parsed = parseResult.data;
6354
+ let validatedJourneys;
6322
6355
  if (journeyRegistry !== void 0 && parsed.story_file !== void 0) {
6323
6356
  let artifactContent;
6324
6357
  try {
@@ -6344,11 +6377,14 @@ async function runCreateStory(deps, params) {
6344
6377
  tokenUsage
6345
6378
  };
6346
6379
  }
6347
- if (taggedJourneys.length > 0) logger$21.info({
6348
- epicId,
6349
- storyKey,
6350
- journeys: taggedJourneys
6351
- }, "A0.2: story tagged with journeys");
6380
+ if (taggedJourneys.length > 0) {
6381
+ logger$21.info({
6382
+ epicId,
6383
+ storyKey,
6384
+ journeys: taggedJourneys
6385
+ }, "A0.2: story tagged with journeys");
6386
+ validatedJourneys = taggedJourneys;
6387
+ }
6352
6388
  }
6353
6389
  }
6354
6390
  logger$21.info({
@@ -6363,6 +6399,7 @@ async function runCreateStory(deps, params) {
6363
6399
  story_key: parsed.story_key,
6364
6400
  story_title: parsed.story_title,
6365
6401
  tokenUsage,
6402
+ ...validatedJourneys !== void 0 ? { journeys: validatedJourneys } : {},
6366
6403
  ...dispatchResult.model !== void 0 ? { model: dispatchResult.model } : {}
6367
6404
  };
6368
6405
  }
@@ -13915,6 +13952,53 @@ function createImplementationOrchestrator(deps) {
13915
13952
  _lastProgressTs = Date.now();
13916
13953
  _stalledStories.clear();
13917
13954
  }
13955
+ async function auditJourneyCoverage(scope, opts) {
13956
+ const mode = config.acceptanceMode ?? "advisory";
13957
+ if (mode === "off" || projectRoot === void 0) return void 0;
13958
+ const registryLoad = await loadJourneyRegistryFromTrustedTree(projectRoot);
13959
+ if (registryLoad.status === "absent") return void 0;
13960
+ if (registryLoad.status === "invalid") return {
13961
+ entries: [],
13962
+ unrunnable: "journey registry is INVALID: " + registryLoad.issues.map((i) => `${i.path}: ${i.message}`).join("; ") + " — fix it (substrate acceptance validate) or the audit cannot run"
13963
+ };
13964
+ if (registryLoad.status !== "ok") {
13965
+ logger$27.warn({ detail: registryLoad.message }, "A0.3: journey registry read failed — coverage audit skipped (environmental)");
13966
+ return void 0;
13967
+ }
13968
+ let unrunnable;
13969
+ const contractLoad = await loadAcceptanceContractFromTrustedTree(projectRoot);
13970
+ if (contractLoad.status === "absent") unrunnable = `journey registry exists but the committed project profile has no acceptance: contract block (${ACCEPTANCE_CONTRACT_PROFILE_PATH}) — claimed journeys can never be walked. Declare render commands per surface.`;
13971
+ else if (contractLoad.status === "invalid") unrunnable = "acceptance: contract block is INVALID: " + contractLoad.issues.map((i) => `${i.path}: ${i.message}`).join("; ");
13972
+ else if (contractLoad.status === "error") logger$27.warn({ detail: contractLoad.message }, "A1.1: acceptance contract read failed (environmental) — treating as absent for this audit");
13973
+ const deferralsLoad = await loadJourneyDeferralsFromTrustedTree(projectRoot);
13974
+ if (deferralsLoad.status !== "ok") logger$27.warn({ detail: deferralsLoad.status === "invalid" ? deferralsLoad.issues : deferralsLoad.message }, `A0.3: ${JOURNEY_DEFERRALS_PATH} unreadable/invalid — treating as no deferrals`);
13975
+ const deferredJourneyIds = deferralsLoad.status === "ok" ? deferralsLoad.deferrals.map((d) => d.journey) : [];
13976
+ const claims = [];
13977
+ for (const [key, st] of _stories.entries()) for (const journeyId of st.journeys ?? []) claims.push({
13978
+ journeyId,
13979
+ storyKey: key
13980
+ });
13981
+ const entries = computeJourneyCoverage({
13982
+ registry: registryLoad.registry,
13983
+ claims,
13984
+ verdicts: [],
13985
+ deferredJourneyIds,
13986
+ scope
13987
+ });
13988
+ const scopeLabel = "final" in scope ? "final" : `epic-${String(scope.epic)}`;
13989
+ eventBus.emit("orchestrator:acceptance-coverage", {
13990
+ scope: scopeLabel,
13991
+ mode,
13992
+ entries,
13993
+ summary: summarizeCoverage(entries)
13994
+ });
13995
+ if (unrunnable !== void 0) logger$27.warn({ scope: scopeLabel }, `A1.1: acceptance gate unrunnable — ${unrunnable}`);
13996
+ if (opts?.persist === true && runManifest !== null) await runManifest.update({ journeys: entries }).catch((err) => logger$27.warn({ err }, "A0.3: manifest journeys ledger write failed — pipeline continues"));
13997
+ return unrunnable !== void 0 ? {
13998
+ entries,
13999
+ unrunnable
14000
+ } : { entries };
14001
+ }
13918
14002
  function getStallThresholdMs(phase) {
13919
14003
  return phase === "IN_DEV" ? DEV_STORY_STALL_THRESHOLD_MS : DEFAULT_STALL_THRESHOLD_MS;
13920
14004
  }
@@ -14141,6 +14225,11 @@ function createImplementationOrchestrator(deps) {
14141
14225
  storyKey,
14142
14226
  storyFilePath
14143
14227
  }, "Found existing story file — skipping create-story");
14228
+ try {
14229
+ const reusedContent = await readFile$1(candidatePath, "utf-8");
14230
+ const reusedJourneys = parseStoryFrontmatter(reusedContent).journeys;
14231
+ if (reusedJourneys.length > 0) updateStory(storyKey, { journeys: reusedJourneys });
14232
+ } catch {}
14144
14233
  endPhase(storyKey, "create-story");
14145
14234
  eventBus.emit("orchestrator:story-phase-complete", {
14146
14235
  storyKey,
@@ -14435,6 +14524,13 @@ function createImplementationOrchestrator(deps) {
14435
14524
  }
14436
14525
  }
14437
14526
  storyFilePath = createResult.story_file;
14527
+ if (createResult.journeys !== void 0 && createResult.journeys.length > 0) {
14528
+ updateStory(storyKey, { journeys: createResult.journeys });
14529
+ if (runManifest !== null) runManifest.patchStoryState(storyKey, { journeys: createResult.journeys }).catch((err) => logger$27.warn({
14530
+ err,
14531
+ storyKey
14532
+ }, "A0.3: patchStoryState(journeys) failed — pipeline continues"));
14533
+ }
14438
14534
  _capturedStoryTitle = sanitizeStoryTitle(createResult.story_title);
14439
14535
  if (createResult.story_title) try {
14440
14536
  const epicId = storyKey.split("-")[0] ?? storyKey;
@@ -16287,6 +16383,55 @@ function createImplementationOrchestrator(deps) {
16287
16383
  }
16288
16384
  }
16289
16385
  const finalizationMode = config.finalizationMode ?? "merge";
16386
+ {
16387
+ const acceptanceModeCfg = config.acceptanceMode ?? "advisory";
16388
+ if (acceptanceModeCfg !== "off") {
16389
+ const epicOfA = (key) => key.includes("-") ? key.slice(0, key.lastIndexOf("-")) : key;
16390
+ const epicIdA = epicOfA(storyKey);
16391
+ const TERMINAL_A = ["COMPLETE", "ESCALATED"];
16392
+ const isLastOfEpicA = [..._stories.entries()].filter(([key]) => key !== storyKey && epicOfA(key) === epicIdA).every(([, st]) => TERMINAL_A.includes(st.phase));
16393
+ const epicNum = Number(epicIdA);
16394
+ if (isLastOfEpicA && Number.isInteger(epicNum)) {
16395
+ const audit = await auditJourneyCoverage({ epic: epicNum });
16396
+ const violations = (audit?.entries ?? []).filter((e) => e.state === "unclaimed" || e.state === "unwalked");
16397
+ if (violations.length > 0 || audit?.unrunnable !== void 0) {
16398
+ const issueLines = violations.map((v) => `journey ${v.journeyId} [${v.criticality}] "${v.title}" is ${v.state}` + (v.ownerStories.length > 0 ? ` (claimed by ${v.ownerStories.join(", ")} but never walked)` : " — NO story claims it"));
16399
+ if (audit?.unrunnable !== void 0) issueLines.push(audit.unrunnable);
16400
+ if (acceptanceModeCfg === "blocking") {
16401
+ const verdict = violations.some((v) => v.state === "unclaimed") ? "journey-unclaimed" : audit?.unrunnable !== void 0 ? "acceptance-unrunnable" : "journey-unwalked";
16402
+ logger$27.error({
16403
+ storyKey,
16404
+ epicId: epicIdA,
16405
+ verdict,
16406
+ journeys: violations.map((v) => v.journeyId)
16407
+ }, "A0.3/A1.1: journey coverage violation at epic close — blocking finalization; branch preserved");
16408
+ updateStory(storyKey, {
16409
+ phase: "ESCALATED",
16410
+ error: verdict,
16411
+ completedAt: new Date().toISOString()
16412
+ });
16413
+ await emitEscalation({
16414
+ storyKey,
16415
+ lastVerdict: verdict,
16416
+ reviewCycles: completedReviewCycles,
16417
+ issues: [
16418
+ `epic ${epicIdA} closes with ${String(violations.length)} journey coverage violation(s) — the never-wired-journey class:`,
16419
+ ...issueLines,
16420
+ "Wire the journey in a story tagged with its id, or defer it explicitly: `substrate acceptance defer <id> --reason \"<why>\"` (commit the deferral file)."
16421
+ ]
16422
+ });
16423
+ await persistState();
16424
+ return "terminal";
16425
+ }
16426
+ logger$27.warn({
16427
+ storyKey,
16428
+ epicId: epicIdA,
16429
+ violations: issueLines
16430
+ }, "A0.3 (advisory): journey coverage violations at epic close — not blocking (acceptance.mode: advisory)");
16431
+ }
16432
+ }
16433
+ }
16434
+ }
16290
16435
  const epicGateCommand = config.epicGateCommand;
16291
16436
  if (epicGateCommand !== void 0 && epicGateCommand.trim() !== "" && finalizationMode !== "branch") {
16292
16437
  const epicOf = (key) => key.includes("-") ? key.slice(0, key.lastIndexOf("-")) : key;
@@ -18064,6 +18209,11 @@ function createImplementationOrchestrator(deps) {
18064
18209
  } catch (err) {
18065
18210
  logger$27.debug({ err }, "Profile staleness check failed (best-effort)");
18066
18211
  }
18212
+ try {
18213
+ await auditJourneyCoverage({ final: true }, { persist: true });
18214
+ } catch (err) {
18215
+ logger$27.warn({ err }, "A0.3: run-end coverage sweep failed (best-effort)");
18216
+ }
18067
18217
  let completed = 0;
18068
18218
  let escalated = 0;
18069
18219
  let failed = 0;
@@ -46299,6 +46449,21 @@ function wireNdjsonEmitter(eventBus, ndjsonEmitter) {
46299
46449
  ...payload.prUrl !== void 0 ? { pr_url: payload.prUrl } : {}
46300
46450
  });
46301
46451
  });
46452
+ eventBus.on("orchestrator:acceptance-coverage", (payload) => {
46453
+ ndjsonEmitter.emit({
46454
+ type: "acceptance:coverage",
46455
+ ts: new Date().toISOString(),
46456
+ scope: payload.scope,
46457
+ mode: payload.mode,
46458
+ entries: payload.entries.map((e) => ({
46459
+ journeyId: e.journeyId,
46460
+ criticality: e.criticality,
46461
+ state: e.state,
46462
+ ownerStories: e.ownerStories
46463
+ })),
46464
+ summary: payload.summary
46465
+ });
46466
+ });
46302
46467
  eventBus.on("orchestrator:story-escalated", (payload) => {
46303
46468
  const rawIssues = Array.isArray(payload.issues) ? payload.issues : [];
46304
46469
  const issues = rawIssues.map((issue) => {
@@ -46631,6 +46796,7 @@ async function runRunAction(options) {
46631
46796
  let configFinalizationMode;
46632
46797
  let configMergeStrategy;
46633
46798
  let configEpicGateCommand;
46799
+ let configAcceptanceMode;
46634
46800
  let configPermissionProfile;
46635
46801
  let configWorktreeCopyFiles;
46636
46802
  try {
@@ -46661,6 +46827,7 @@ async function runRunAction(options) {
46661
46827
  if (cfg.finalization?.mode !== void 0) configFinalizationMode = cfg.finalization.mode;
46662
46828
  if (cfg.finalization?.merge_strategy !== void 0) configMergeStrategy = cfg.finalization.merge_strategy;
46663
46829
  if (cfg.finalization?.epic_gate_command !== void 0) configEpicGateCommand = cfg.finalization.epic_gate_command;
46830
+ if (cfg.acceptance?.mode !== void 0) configAcceptanceMode = cfg.acceptance.mode;
46664
46831
  if (cfg.dispatch?.permission_profile !== void 0) configPermissionProfile = cfg.dispatch.permission_profile;
46665
46832
  if (Array.isArray(cfg.worktree?.copy_files) && cfg.worktree.copy_files.length > 0) {
46666
46833
  configWorktreeCopyFiles = [...cfg.worktree.copy_files];
@@ -46764,6 +46931,7 @@ async function runRunAction(options) {
46764
46931
  ...finalization !== void 0 ? { finalizationMode: finalization } : configFinalizationMode !== void 0 ? { finalizationMode: configFinalizationMode } : {},
46765
46932
  ...configMergeStrategy !== void 0 ? { mergeStrategy: configMergeStrategy } : {},
46766
46933
  ...configEpicGateCommand !== void 0 ? { epicGateCommand: configEpicGateCommand } : {},
46934
+ ...configAcceptanceMode !== void 0 ? { acceptanceMode: configAcceptanceMode } : {},
46767
46935
  ...configPermissionProfile !== void 0 ? { permissionProfile: configPermissionProfile } : {},
46768
46936
  ...configWorktreeCopyFiles !== void 0 ? { worktreeCopyFiles: configWorktreeCopyFiles } : {}
46769
46937
  });
@@ -47332,6 +47500,7 @@ async function runRunAction(options) {
47332
47500
  ...finalization !== void 0 ? { finalizationMode: finalization } : configFinalizationMode !== void 0 ? { finalizationMode: configFinalizationMode } : {},
47333
47501
  ...configMergeStrategy !== void 0 ? { mergeStrategy: configMergeStrategy } : {},
47334
47502
  ...configEpicGateCommand !== void 0 ? { epicGateCommand: configEpicGateCommand } : {},
47503
+ ...configAcceptanceMode !== void 0 ? { acceptanceMode: configAcceptanceMode } : {},
47335
47504
  ...configWorktreeCopyFiles !== void 0 ? { worktreeCopyFiles: configWorktreeCopyFiles } : {}
47336
47505
  },
47337
47506
  projectRoot,
@@ -47555,7 +47724,7 @@ async function runRunAction(options) {
47555
47724
  }
47556
47725
  }
47557
47726
  async function runFullPipeline(options) {
47558
- const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, projectRoot, events: eventsFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, skipVerification, maxReviewCycles = 2, retryBudget, registry: injectedRegistry, tokenCeilings, stories: explicitStories, telemetryEnabled: fullTelemetryEnabled, telemetryPort: fullTelemetryPort, agentId, meshUrl: fpMeshUrl, meshProjectId: fpMeshProjectId, engineType: fpEngineType, probeAuthor, probeAuthorStateIntegrating: fpProbeAuthorStateIntegrating, noWorktree, worktreeCopyFiles: fpWorktreeCopyFiles, finalizationMode: fpFinalizationMode, mergeStrategy: fpMergeStrategy, epicGateCommand: fpEpicGateCommand, permissionProfile: fpPermissionProfile } = options;
47727
+ const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, projectRoot, events: eventsFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, skipVerification, maxReviewCycles = 2, retryBudget, registry: injectedRegistry, tokenCeilings, stories: explicitStories, telemetryEnabled: fullTelemetryEnabled, telemetryPort: fullTelemetryPort, agentId, meshUrl: fpMeshUrl, meshProjectId: fpMeshProjectId, engineType: fpEngineType, probeAuthor, probeAuthorStateIntegrating: fpProbeAuthorStateIntegrating, noWorktree, worktreeCopyFiles: fpWorktreeCopyFiles, finalizationMode: fpFinalizationMode, mergeStrategy: fpMergeStrategy, epicGateCommand: fpEpicGateCommand, acceptanceMode: fpAcceptanceMode, permissionProfile: fpPermissionProfile } = options;
47559
47728
  if (!existsSync$1(dbDir)) mkdirSync$1(dbDir, { recursive: true });
47560
47729
  let doltServerFull = null;
47561
47730
  try {
@@ -47871,6 +48040,7 @@ async function runFullPipeline(options) {
47871
48040
  ...fpFinalizationMode !== void 0 ? { finalizationMode: fpFinalizationMode } : {},
47872
48041
  ...fpMergeStrategy !== void 0 ? { mergeStrategy: fpMergeStrategy } : {},
47873
48042
  ...fpEpicGateCommand !== void 0 ? { epicGateCommand: fpEpicGateCommand } : {},
48043
+ ...fpAcceptanceMode !== void 0 ? { acceptanceMode: fpAcceptanceMode } : {},
47874
48044
  ...fpWorktreeCopyFiles !== void 0 ? { worktreeCopyFiles: fpWorktreeCopyFiles } : {}
47875
48045
  },
47876
48046
  projectRoot,
@@ -48041,7 +48211,7 @@ async function runFullPipeline(options) {
48041
48211
  */
48042
48212
  async function emitPreDispatchVersionAdvisory(currentVersion) {
48043
48213
  if (process.env["SUBSTRATE_NO_UPDATE_CHECK"] === "1") return;
48044
- const { createVersionManager } = await import("./version-manager-impl-BVcsgUtN.js");
48214
+ const { createVersionManager } = await import("./version-manager-impl-DeEPhQlA.js");
48045
48215
  const vm = createVersionManager();
48046
48216
  const result = await vm.checkForUpdates(true);
48047
48217
  const gap = classifyVersionGap(currentVersion, result.latestVersion);
@@ -48148,4 +48318,4 @@ function registerRunCommand(program, version = "0.0.0", projectRoot = process.cw
48148
48318
 
48149
48319
  //#endregion
48150
48320
  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 };
48151
- //# sourceMappingURL=run-CAmBrDky.js.map
48321
+ //# sourceMappingURL=run-nSb1TD3o.js.map
@@ -1,4 +1,4 @@
1
- import { CoreEvents, DatabaseAdapter, TypedEventBus } from "../../../index-CnV5PcSR.js";
1
+ import { CoreEvents, DatabaseAdapter, TypedEventBus } from "../../../index-C31XtSMn.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region packages/sdlc/dist/verification/findings.d.ts
@@ -582,6 +582,7 @@ declare const PerStoryStateSchema: z.ZodObject<{
582
582
  escalation_detail: z.ZodOptional<z.ZodString>;
583
583
  commit_sha: z.ZodOptional<z.ZodString>;
584
584
  baseline_sha: z.ZodOptional<z.ZodString>;
585
+ journeys: z.ZodOptional<z.ZodArray<z.ZodString>>;
585
586
  checkpoint_sha: z.ZodOptional<z.ZodString>;
586
587
  finalization: z.ZodOptional<z.ZodObject<{
587
588
  mode: z.ZodUnion<readonly [z.ZodLiteral<"merge">, z.ZodLiteral<"branch">, z.ZodLiteral<"pr">]>;
@@ -717,6 +718,19 @@ interface RunManifestData {
717
718
  * Optional for backward-compatibility with pre-75-2 manifests.
718
719
  */
719
720
  orchestrator_start_branch?: string;
721
+ /**
722
+ * A0.3 (acceptance-gate): journey coverage ledger from the most recent
723
+ * audit (epic-close or run-end). Absent on registry-less projects and
724
+ * pre-A0.3 manifests.
725
+ */
726
+ journeys?: {
727
+ journeyId: string;
728
+ title: string;
729
+ criticality: 'critical' | 'standard';
730
+ epic?: number;
731
+ state: 'walked-pass' | 'walked-fail' | 'deferred' | 'unclaimed' | 'unwalked';
732
+ ownerStories: string[];
733
+ }[];
720
734
  /**
721
735
  * Human-readable reason the run was stopped (e.g. 'killed_by_user').
722
736
  * Set by the SIGTERM/SIGINT handler (Story 58-7). Absent on pre-58-7 manifests.
@@ -751,8 +765,8 @@ declare const CliFlagsSchema: z.ZodObject<{
751
765
  stories: z.ZodOptional<z.ZodArray<z.ZodString>>;
752
766
  halt_on: z.ZodOptional<z.ZodEnum<{
753
767
  none: "none";
754
- all: "all";
755
768
  critical: "critical";
769
+ all: "all";
756
770
  }>>;
757
771
  cost_ceiling: z.ZodOptional<z.ZodNumber>;
758
772
  agent: z.ZodOptional<z.ZodString>;
@@ -1,4 +1,4 @@
1
- import { createVersionManager } from "./dist-BkhYgJWo.js";
1
+ import { createVersionManager } from "./dist-CpnLJuVw.js";
2
2
  import { execSync, spawn } from "child_process";
3
3
  import * as readline from "readline";
4
4
 
@@ -123,4 +123,4 @@ function registerUpgradeCommand(program) {
123
123
 
124
124
  //#endregion
125
125
  export { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand };
126
- //# sourceMappingURL=upgrade-S1oUMH5P.js.map
126
+ //# sourceMappingURL=upgrade-BUSzXgnd.js.map
@@ -1,5 +1,5 @@
1
- import "./dist-BkhYgJWo.js";
1
+ import "./dist-CpnLJuVw.js";
2
2
  import "./version-manager-impl-qFBiO4Eh.js";
3
- import { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand } from "./upgrade-S1oUMH5P.js";
3
+ import { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand } from "./upgrade-BUSzXgnd.js";
4
4
 
5
5
  export { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand };
@@ -1,4 +1,4 @@
1
- import { VersionManagerImpl, createVersionManager } from "./dist-BkhYgJWo.js";
1
+ import { VersionManagerImpl, createVersionManager } from "./dist-CpnLJuVw.js";
2
2
  import "./version-manager-impl-qFBiO4Eh.js";
3
3
 
4
4
  export { createVersionManager };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.21.2",
3
+ "version": "0.21.4",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,4 +0,0 @@
1
- import { AdapterRegistry } from "./dist-BkhYgJWo.js";
2
- import "./adapter-registry-DXLMTmfD.js";
3
-
4
- export { AdapterRegistry };