substrate-ai 0.20.149 → 0.20.151

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.
Files changed (28) hide show
  1. package/dist/adapter-registry-BsBP3j5i.js +4 -0
  2. package/dist/cli/index.js +66 -16
  3. package/dist/cli/templates/__tests__/docs-match-behavior.test.ts +69 -0
  4. package/dist/cli/templates/claude-commands/substrate-metrics.md +12 -0
  5. package/dist/cli/templates/claude-commands/substrate-run.md +21 -0
  6. package/dist/cli/templates/claude-commands/substrate-supervisor.md +15 -0
  7. package/dist/cli/templates/claude-md-substrate-section.md +14 -1
  8. package/dist/{decisions-De43bvYq.js → decisions-DH12X_94.js} +1 -1
  9. package/dist/{dist-C2CAZDep.js → dist-90byyGPp.js} +79 -10
  10. package/dist/{errors-Db8EIM9w.js → errors-ST3E8qWc.js} +2 -2
  11. package/dist/{experimenter-Dm5jUPO6.js → experimenter-CyA9K5u6.js} +1 -1
  12. package/dist/{health-ByQadq3J.js → health-BERxzeG-.js} +3 -3
  13. package/dist/{health-WGcvcIYK.js → health-CoJ3nBWL.js} +3 -3
  14. package/dist/{index-DxeIRBBf.d.ts → index-CnV5PcSR.d.ts} +23 -1
  15. package/dist/index.d.ts +23 -1
  16. package/dist/index.js +2 -2
  17. package/dist/{interactive-prompt-CMBlj6UF.js → interactive-prompt-UWLccCqL.js} +2 -2
  18. package/dist/{manifest-read-knD2uWkz.js → manifest-read-BOvY53EG.js} +7 -3
  19. package/dist/modules/interactive-prompt/index.js +3 -3
  20. package/dist/{routing-B3sgH5MZ.js → routing-pCkRxj09.js} +1 -1
  21. package/dist/{run-CtIc7_rK.js → run-Csg27VHs.js} +5 -5
  22. package/dist/{run-BAUlPom3.js → run-Dgqfz9HY.js} +90 -31
  23. package/dist/src/modules/recovery-engine/index.d.ts +1 -1
  24. package/dist/{upgrade-DOmKCyOY.js → upgrade-Dw6jKkH3.js} +2 -2
  25. package/dist/{upgrade-B3iamwjO.js → upgrade-unZndH-S.js} +2 -2
  26. package/dist/{version-manager-impl-DizCvYy8.js → version-manager-impl--XXnwjMd.js} +1 -1
  27. package/package.json +1 -1
  28. package/dist/adapter-registry-CauYNA1U.js +0 -4
@@ -0,0 +1,4 @@
1
+ import { AdapterRegistry } from "./dist-90byyGPp.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-ByQadq3J.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-BERxzeG-.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-C2CAZDep.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-90byyGPp.js";
6
6
  import "../adapter-registry-DXLMTmfD.js";
7
- import { RunManifest, SupervisorLock, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, parseRuntimeProbes, readCurrentRunId, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics, runAcTraceabilityCheck } from "../manifest-read-knD2uWkz.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-BAUlPom3.js";
9
- import "../errors-Db8EIM9w.js";
7
+ import { RunManifest, SupervisorLock, ZERO_FINDINGS_BY_AUTHOR, ZERO_FINDING_COUNTS, ZERO_PROBE_AUTHOR_METRICS, aggregateProbeAuthorMetrics, parseRuntimeProbes, readCurrentRunId, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics, runAcTraceabilityCheck } from "../manifest-read-BOvY53EG.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-Dgqfz9HY.js";
9
+ import "../errors-ST3E8qWc.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-CMBlj6UF.js";
14
+ import "../interactive-prompt-UWLccCqL.js";
15
15
  import "../recovery-engine-BKGBeBnW.js";
16
16
  import "../version-manager-impl-qFBiO4Eh.js";
17
- import { registerUpgradeCommand } from "../upgrade-DOmKCyOY.js";
17
+ import { registerUpgradeCommand } from "../upgrade-Dw6jKkH3.js";
18
18
  import { Command } from "commander";
19
19
  import { fileURLToPath } from "url";
20
20
  import { dirname, join, resolve } from "path";
@@ -624,6 +624,7 @@ const SubstrateConfigSchema = z.object({
624
624
  supervisor_poll_interval_seconds: z.number().int().positive().optional(),
625
625
  retry_budget: z.number().int().positive().optional(),
626
626
  epics_path: z.string().optional(),
627
+ dispatch: z.object({ permission_profile: z.enum(["skip", "scoped"]).optional() }).strict().optional(),
627
628
  finalization: z.object({
628
629
  mode: z.enum([
629
630
  "merge",
@@ -656,6 +657,7 @@ const PartialSubstrateConfigSchema = z.object({
656
657
  supervisor_poll_interval_seconds: z.number().int().positive().optional(),
657
658
  retry_budget: z.number().int().positive().optional(),
658
659
  epics_path: z.string().optional(),
660
+ dispatch: z.object({ permission_profile: z.enum(["skip", "scoped"]).optional() }).strict().optional(),
659
661
  finalization: z.object({
660
662
  mode: z.enum([
661
663
  "merge",
@@ -1523,6 +1525,51 @@ async function scaffoldClaudeSettings(projectRoot) {
1523
1525
  await writeFile(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
1524
1526
  logger$18.info({ settingsPath }, "Wrote substrate settings to .claude/settings.json");
1525
1527
  }
1528
+ /**
1529
+ * H5.2 (field finding #2): write substrate's own slash commands
1530
+ * (/substrate-run, /substrate-supervisor, /substrate-metrics) into
1531
+ * `.claude/commands/`. Pre-fix, the init summary ADVERTISED these commands
1532
+ * but nothing ever wrote them — the bmad generator populates `.claude/skills/`
1533
+ * only, so operators found an empty (or user-files-only) commands dir.
1534
+ *
1535
+ * Only files named `substrate-*.md` are owned (overwritten); user files are
1536
+ * never touched. Works with pre-existing / gitignored `.claude/` dirs —
1537
+ * mkdirSync recursive + per-file writes make no assumptions about state.
1538
+ */
1539
+ function scaffoldSubstrateSlashCommands(projectRoot) {
1540
+ try {
1541
+ const pkgRoot = findPackageRoot(__dirname);
1542
+ let templatesDir = join(pkgRoot, "dist", "cli", "templates", "claude-commands");
1543
+ if (!existsSync$1(templatesDir)) templatesDir = join(pkgRoot, "src", "cli", "templates", "claude-commands");
1544
+ if (!existsSync$1(templatesDir)) {
1545
+ logger$18.warn({ templatesDir }, "substrate slash-command templates not found; skipping");
1546
+ return 0;
1547
+ }
1548
+ const commandsDir = join(projectRoot, ".claude", "commands");
1549
+ mkdirSync$1(commandsDir, { recursive: true });
1550
+ let written = 0;
1551
+ for (const file of readdirSync$1(templatesDir)) {
1552
+ if (!file.startsWith("substrate-") || !file.endsWith(".md")) continue;
1553
+ try {
1554
+ writeFileSync$1(join(commandsDir, file), readFileSync$1(join(templatesDir, file), "utf8"));
1555
+ written += 1;
1556
+ } catch (err) {
1557
+ logger$18.warn({
1558
+ file,
1559
+ err
1560
+ }, "failed to write substrate slash command (init continues)");
1561
+ }
1562
+ }
1563
+ logger$18.info({
1564
+ written,
1565
+ commandsDir
1566
+ }, "Scaffolded substrate slash commands");
1567
+ return written;
1568
+ } catch (err) {
1569
+ logger$18.warn({ err }, "scaffoldSubstrateSlashCommands failed; init continues");
1570
+ return 0;
1571
+ }
1572
+ }
1526
1573
  function resolveBmadMethodInstallerLibPath(fromDir = __dirname) {
1527
1574
  try {
1528
1575
  const _require = createRequire(join(fromDir, "synthetic.js"));
@@ -2276,6 +2323,7 @@ async function runInitAction(options) {
2276
2323
  await scaffoldStatuslineScript(projectRoot);
2277
2324
  await scaffoldClaudeSettings(projectRoot);
2278
2325
  await scaffoldClaudeCommands(projectRoot, outputFormat);
2326
+ scaffoldSubstrateSlashCommands(projectRoot);
2279
2327
  scaffoldCodexProject(projectRoot, outputFormat);
2280
2328
  if (options.installUserScope) {
2281
2329
  const homeDir = process.env["HOME"] ?? process.env["USERPROFILE"];
@@ -7078,7 +7126,7 @@ async function runStatusAction(options) {
7078
7126
  logger$15.debug({ err }, "Work graph query failed, continuing without work graph data");
7079
7127
  }
7080
7128
  if (run === void 0) {
7081
- const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-WGcvcIYK.js");
7129
+ const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-CoJ3nBWL.js");
7082
7130
  const substrateDirPath = join(projectRoot, ".substrate");
7083
7131
  const processInfo = inspectProcessTree$1({
7084
7132
  projectRoot,
@@ -8026,7 +8074,7 @@ function defaultSupervisorDeps() {
8026
8074
  if (cached === null) {
8027
8075
  const { AdapterRegistry: AR } = await import(
8028
8076
  /* @vite-ignore */
8029
- "../adapter-registry-CauYNA1U.js"
8077
+ "../adapter-registry-BsBP3j5i.js"
8030
8078
  );
8031
8079
  cached = new AR();
8032
8080
  await cached.discoverAndRegister();
@@ -8593,11 +8641,11 @@ async function runSupervisorAction(options, deps = {}) {
8593
8641
  try {
8594
8642
  const { createExperimenter } = await import(
8595
8643
  /* @vite-ignore */
8596
- "../experimenter-Dm5jUPO6.js"
8644
+ "../experimenter-CyA9K5u6.js"
8597
8645
  );
8598
8646
  const { getLatestRun: getLatest } = await import(
8599
8647
  /* @vite-ignore */
8600
- "../decisions-De43bvYq.js"
8648
+ "../decisions-DH12X_94.js"
8601
8649
  );
8602
8650
  const expAdapter = createDatabaseAdapter({
8603
8651
  backend: "auto",
@@ -8607,7 +8655,7 @@ async function runSupervisorAction(options, deps = {}) {
8607
8655
  await initSchema(expAdapter);
8608
8656
  const { runRunAction: runPipeline } = await import(
8609
8657
  /* @vite-ignore */
8610
- "../run-CtIc7_rK.js"
8658
+ "../run-Csg27VHs.js"
8611
8659
  );
8612
8660
  const runStoryFn = async (opts) => {
8613
8661
  const exitCode = await runPipeline({
@@ -9132,7 +9180,7 @@ async function runMetricsAction(options) {
9132
9180
  const routingConfigPath = join(dbDir, "routing.yml");
9133
9181
  let routingConfig = null;
9134
9182
  if (existsSync$1(routingConfigPath)) try {
9135
- const { loadModelRoutingConfig } = await import("../routing-B3sgH5MZ.js");
9183
+ const { loadModelRoutingConfig } = await import("../routing-pCkRxj09.js");
9136
9184
  routingConfig = loadModelRoutingConfig(routingConfigPath);
9137
9185
  } catch {}
9138
9186
  if (routingConfig === null) routingConfig = {
@@ -13104,7 +13152,9 @@ function assembleReport(runId, manifest, halts, storyOutputTokens) {
13104
13152
  completed_at: completedAt,
13105
13153
  wall_clock_ms: totalWallMs
13106
13154
  };
13107
- const spent = manifest.cost_accumulation?.run_total ?? 0;
13155
+ const runTotal = manifest.cost_accumulation?.run_total ?? 0;
13156
+ const perStoryCostSum = Object.values(perStoryState).reduce((sum, st) => sum + (typeof st?.cost_usd === "number" ? st.cost_usd : 0), 0);
13157
+ const spent = runTotal > 0 ? runTotal : perStoryCostSum;
13108
13158
  const ceiling = manifest.cost_accumulation != null ? manifest.cli_flags?.cost_ceiling : void 0;
13109
13159
  const utilization = ceiling != null && ceiling > 0 ? `${(spent / ceiling * 100).toFixed(1)}%` : void 0;
13110
13160
  const overCeiling = ceiling != null ? spent > ceiling : false;
@@ -13458,8 +13508,8 @@ async function createProgram() {
13458
13508
  /** Fire-and-forget startup version check (story 8.3, AC3/AC5) */
13459
13509
  function checkForUpdatesInBackground(currentVersion) {
13460
13510
  if (process.env.SUBSTRATE_NO_UPDATE_CHECK === "1") return;
13461
- import("../upgrade-B3iamwjO.js").then(async () => {
13462
- const { createVersionManager } = await import("../version-manager-impl-DizCvYy8.js");
13511
+ import("../upgrade-unZndH-S.js").then(async () => {
13512
+ const { createVersionManager } = await import("../version-manager-impl--XXnwjMd.js");
13463
13513
  const vm = createVersionManager();
13464
13514
  const result = await vm.checkForUpdates();
13465
13515
  if (result.updateAvailable) {
@@ -0,0 +1,69 @@
1
+ /**
2
+ * H5.3: docs-match-behavior gate for the consumer CLAUDE.md template.
3
+ *
4
+ * The template (`claude-md-substrate-section.md`) is what every consumer
5
+ * project's Claude session reads before driving substrate — when it drifts
6
+ * from the code, agents act on behavior that no longer exists (the
7
+ * income-sources run repeatedly hit this class). This suite pins the
8
+ * load-bearing claims to their code-side sources of truth:
9
+ *
10
+ * - every finalization mode in the config schema is documented
11
+ * - the ff-only merge default and the three-way opt-in are stated
12
+ * - the external worktree base default (H4.2) is stated
13
+ * - every `story:*` / `pipeline:*` event the template names exists in
14
+ * EVENT_TYPE_NAMES (no phantom events)
15
+ *
16
+ * Ship rule (execution-plan H5.3 AC1): a change to finalizeStory /
17
+ * merge-to-main / verification-check registration that alters documented
18
+ * behavior must update the template in the same commit — this suite is the
19
+ * enforcement for the pinned claims; reviewers enforce the rest.
20
+ */
21
+
22
+ import { describe, it, expect } from 'vitest'
23
+ import { readFileSync } from 'node:fs'
24
+ import { resolve, dirname } from 'node:path'
25
+ import { fileURLToPath } from 'node:url'
26
+ import { EVENT_TYPE_NAMES } from '../../../modules/implementation-orchestrator/event-types.js'
27
+
28
+ const HERE = dirname(fileURLToPath(import.meta.url))
29
+ const TEMPLATE = readFileSync(resolve(HERE, '..', 'claude-md-substrate-section.md'), 'utf-8')
30
+ const CONFIG_SCHEMA = readFileSync(
31
+ resolve(HERE, '..', '..', '..', 'modules', 'config', 'config-schema.ts'),
32
+ 'utf-8',
33
+ )
34
+
35
+ describe('consumer CLAUDE.md template ↔ behavior parity (H5.3)', () => {
36
+ it('documents every finalization mode the config schema accepts', () => {
37
+ const enumMatch = /finalization:[\s\S]*?mode: z\.enum\(\[([^\]]+)\]\)/.exec(CONFIG_SCHEMA)
38
+ expect(enumMatch).not.toBeNull()
39
+ const modes = [...enumMatch![1]!.matchAll(/'([a-z]+)'/g)].map((m) => m[1]!)
40
+ expect(modes.length).toBeGreaterThanOrEqual(3)
41
+ for (const mode of modes) {
42
+ expect(TEMPLATE, `finalization mode "${mode}" missing from template`).toContain(`\`${mode}\``)
43
+ }
44
+ })
45
+
46
+ it('states the ff-only merge default and the three-way opt-in (H3.3)', () => {
47
+ expect(TEMPLATE).toContain('fast-forward-only')
48
+ expect(TEMPLATE).toContain('merge_strategy: three-way')
49
+ expect(TEMPLATE).toContain('ff-only-merge-not-possible')
50
+ })
51
+
52
+ it('states the external worktree base default (H4.2)', () => {
53
+ expect(TEMPLATE).toContain('~/.substrate/worktrees/')
54
+ expect(TEMPLATE).toContain('worktree.base: in-repo')
55
+ })
56
+
57
+ it('every event name the template cites exists in EVENT_TYPE_NAMES', () => {
58
+ const cited = [...TEMPLATE.matchAll(/`((?:story|pipeline):[a-z-]+)`/g)].map((m) => m[1]!)
59
+ expect(cited.length).toBeGreaterThan(0)
60
+ const known = new Set<string>(EVENT_TYPE_NAMES)
61
+ const phantom = cited.filter((e) => !known.has(e))
62
+ expect(phantom, `template cites events that do not exist: ${phantom.join(', ')}`).toEqual([])
63
+ })
64
+
65
+ it('documents the commit-first discipline (H0.1) — the branch is the durable copy', () => {
66
+ expect(TEMPLATE).toContain('commit-first')
67
+ expect(TEMPLATE).toContain('wip(story-')
68
+ })
69
+ })
@@ -0,0 +1,12 @@
1
+ ---
2
+ description: Show substrate run metrics and the latest completion report
3
+ ---
4
+
5
+ Report on substrate pipeline outcomes for this project:
6
+
7
+ 1. `substrate report --run latest` — per-story outcomes, cost, escalation diagnostics, halt notifications. Add `--verify-ac` for the AC-to-test traceability matrix.
8
+ 2. `substrate metrics --output-format json` — historical run metrics.
9
+
10
+ Summarize conversationally: how many stories verified/recovered/escalated/failed, total cost, and anything the operator must act on (escalations, halts, unmerged deliverable branches listed under "Finalization").
11
+
12
+ $ARGUMENTS
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: Run the substrate implementation pipeline (guided)
3
+ ---
4
+
5
+ Run the substrate pipeline for this project.
6
+
7
+ **Before dispatching, confirm scope with the user if they did not name stories.** Without `--stories`, substrate auto-discovers ALL pending stories and may dispatch 30+ at once.
8
+
9
+ Steps:
10
+
11
+ 1. If the user named stories (e.g. "1-1,1-2"), run:
12
+ `substrate run --events --stories <keys>`
13
+ Otherwise ask which stories to run, or confirm a full auto-discovery run is intended.
14
+ 2. Pipeline runs take 5–40 minutes. Use a background invocation or a timeout of at least 10 minutes — the default 2-minute tool timeout WILL kill the pipeline. NEVER pipe substrate output through `head`/`tail`/`grep` (EPIPE stalls).
15
+ 3. Poll `substrate status --output-format json` every 60–90s while it runs; `substrate health --output-format json` if it goes quiet for 15+ minutes.
16
+ 4. When it completes, summarize per-story outcomes and run `substrate report --run latest`.
17
+
18
+ Autonomy: default halts on critical+fatal. For fully autonomous runs use:
19
+ `substrate run --halt-on none --non-interactive --events --output-format json`
20
+
21
+ $ARGUMENTS
@@ -0,0 +1,15 @@
1
+ ---
2
+ description: Attach the substrate supervisor to an active pipeline run
3
+ ---
4
+
5
+ Attach the substrate supervisor to the pipeline run that is active in this project:
6
+
7
+ `substrate supervisor --output-format json`
8
+
9
+ Notes:
10
+ - The supervisor monitors an active run (stall detection, kill-and-restart recovery, post-run analysis) — it does NOT start one. Start the run first (see /substrate-run).
11
+ - Only attach to runs started in this session; attaching to another session's run risks killing healthy dispatches.
12
+ - Long-running: use a background invocation or a generous timeout.
13
+ - After the run, read the analysis with `substrate metrics --analysis <run_id> --output-format json`.
14
+
15
+ $ARGUMENTS
@@ -81,7 +81,17 @@ The Recovery Engine runs a 3-tier auto-fix ladder before any halt — Tier A ret
81
81
 
82
82
  ### Per-Story Worktree Behavior
83
83
 
84
- Each dispatched story runs in `.substrate-worktrees/story-<key>` on its own branch (`substrate/story-<key>`). The agent's auto-commit (e.g., `feat(story-N-M): ...`) lands on the branch, not main. Merge to main happens after verification SHIP_IT; the worktree is then removed. After verification failure, the worktree and branch are preserved for `substrate reconcile-from-disk` inspection. Use `--no-worktree` if your project doesn't support worktrees (submodules, bare repos).
84
+ Each dispatched story runs in an isolated git worktree on its own branch (`substrate/story-<key>`). By default the worktree lives OUTSIDE the repo at `~/.substrate/worktrees/<projectname>-<hash8>/<key>/`; set `worktree.base: in-repo` in `.substrate/config.yaml` to restore the legacy `.substrate-worktrees/<key>/` location. Substrate auto-commits the story's work to the branch (`feat(story-N-M): ...`) at dev-story completion — commit-first, before review so the branch is always the durable copy; failure paths add `wip(story-<key>)` checkpoints. After verification failure, the worktree and branch are preserved for `substrate reconcile-from-disk` inspection. Use `--no-worktree` if your project doesn't support worktrees (submodules, bare repos).
85
+
86
+ ### Finalization — how verified work integrates
87
+
88
+ `finalization.mode` in `.substrate/config.yaml` (or `--finalization <mode>` per run):
89
+
90
+ - **`merge`** (default): local merge into the branch the run started from. The merge is **fast-forward-only by default** — if the base branch moved during the run, the story escalates `ff-only-merge-not-possible` instead of synthesizing a merge commit. Set `finalization.merge_strategy: three-way` to allow merge commits — **required for concurrent multi-story runs** (later stories cannot fast-forward past earlier merges). A dirty parent tree whose changes intersect the story's diff escalates `parent-tree-dirtied-by-run` naming the files.
91
+ - **`branch`**: verified work stays on `substrate/story-<key>` — nothing self-merges; the branch is the deliverable. The safe brownfield mode.
92
+ - **`pr`**: branch + `git push` + `gh pr create` (one PR per story). Degrades to `branch` with a warning when push/gh fail — never blocks the story.
93
+
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).
85
95
 
86
96
  ### Key Commands Reference
87
97
 
@@ -92,6 +102,7 @@ Each dispatched story runs in `.substrate-worktrees/story-<key>` on its own bran
92
102
  | `substrate run --non-interactive` | Suppress stdin prompts; combine with `--halt-on none` for fully autonomous |
93
103
  | `substrate run --verify-ac` | On-demand AC-to-Test traceability matrix |
94
104
  | `substrate run --no-worktree` | Disable per-story git worktrees (use for submodules or bare repos) |
105
+ | `substrate run --finalization <mode>` | Integration mode: `merge` (default) / `branch` (never self-merge) / `pr` |
95
106
  | `substrate report [--run <id\|latest>]` | Per-run completion report — outcomes, cost, escalation diagnostics, halt notifications |
96
107
  | `substrate report --verify-ac` | Append AC-to-Test traceability matrix to the report |
97
108
  | `substrate reconcile-from-disk [--dry-run] [--yes]` | Path A reconciliation when pipeline reports failed but tree is coherent |
@@ -120,6 +131,8 @@ Substrate writes per-project state under `.substrate/` in a few flavors:
120
131
 
121
132
  The defensible default for most projects is to ignore everything under `.substrate/` except the operator config. Local telemetry stays on each developer's machine — operators see their own corpus locally via `substrate metrics`; cross-machine sharing of routing telemetry is a future feature, not currently supported.
122
133
 
134
+ Besides `.substrate/`, `substrate init` also scaffolds project-root artifacts that ARE intentional and worth tracking in git: `CLAUDE.md`, `AGENTS.md`, `GEMINI.md` (pipeline instructions per agent CLI), `packs/<pack-name>/` (the methodology pack's prompts — the pipeline reads these at dispatch time), `.claude/commands/substrate-*.md` + `.claude/skills/`, and `.codex/` mirrors. Track them; they are inputs to every run, not state. If your team prefers not to commit the non-Claude agent files (`AGENTS.md`, `GEMINI.md`, `.codex/`), gitignore them explicitly — substrate regenerates them on `substrate init`.
135
+
123
136
  ```gitignore
124
137
  # Substrate state — track only the operator config; everything else is
125
138
  # per-process, per-run, or local-machine accumulation
@@ -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-C2CAZDep.js";
1
+ import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, listRequirements, registerArtifact, updateDecision, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./dist-90byyGPp.js";
2
2
  import "./decisions-CzSIEeGP.js";
3
3
 
4
4
  export { getLatestRun };
@@ -4,6 +4,7 @@ import { EventEmitter } from "node:events";
4
4
  import yaml, { dump, load } from "js-yaml";
5
5
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
6
  import { exec, execFile, execSync, spawn, spawnSync } from "node:child_process";
7
+ import * as path$3 from "node:path";
7
8
  import * as path$2 from "node:path";
8
9
  import * as path$1 from "node:path";
9
10
  import path, { dirname as dirname$1, join as join$1, resolve as resolve$1 } from "node:path";
@@ -1302,7 +1303,8 @@ var DispatcherImpl = class {
1302
1303
  ...storyKey !== void 0 ? { storyKey } : {},
1303
1304
  ...optimizationDirectives !== void 0 ? { optimizationDirectives } : {},
1304
1305
  taskType,
1305
- dispatchId: id
1306
+ dispatchId: id,
1307
+ ...this._config.permissionProfile !== void 0 ? { permissionProfile: this._config.permissionProfile } : {}
1306
1308
  });
1307
1309
  const baseTimeoutMs = timeout ?? this._config.defaultTimeouts[taskType] ?? DEFAULT_TIMEOUTS[taskType] ?? 3e5;
1308
1310
  const timeoutMultiplier = capabilities.timeoutMultiplier ?? 1;
@@ -1314,6 +1316,29 @@ var DispatcherImpl = class {
1314
1316
  env["GIT_CEILING_DIRECTORIES"] = dirname$1(worktreePath);
1315
1317
  if (cmd.env !== void 0) Object.assign(env, cmd.env);
1316
1318
  if (cmd.unsetEnvKeys !== void 0) for (const key of cmd.unsetEnvKeys) delete env[key];
1319
+ if (cmd.executionMode !== void 0 && cmd.executionMode !== "spawn") {
1320
+ this._logger.error({
1321
+ id,
1322
+ agent,
1323
+ executionMode: cmd.executionMode
1324
+ }, "dispatch rejected: execution mode not implemented");
1325
+ this._running.delete(id);
1326
+ this._drainQueue();
1327
+ resolve$2({
1328
+ id,
1329
+ status: "failed",
1330
+ exitCode: -1,
1331
+ output: "",
1332
+ parsed: null,
1333
+ parseError: `executionMode "${cmd.executionMode}" is not implemented — only 'spawn' is available (H4.4 seam)`,
1334
+ durationMs: 0,
1335
+ tokenEstimate: {
1336
+ input: Math.ceil(prompt.length / CHARS_PER_TOKEN$3),
1337
+ output: 0
1338
+ }
1339
+ });
1340
+ return;
1341
+ }
1317
1342
  const proc = spawn(cmd.binary, cmd.args, {
1318
1343
  cwd: cmd.cwd,
1319
1344
  env,
@@ -3198,10 +3223,12 @@ async function initTelemetrySchema(adapter) {
3198
3223
  cache_hit_sub_score DOUBLE NOT NULL DEFAULT 0,
3199
3224
  io_ratio_sub_score DOUBLE NOT NULL DEFAULT 0,
3200
3225
  context_management_sub_score DOUBLE NOT NULL DEFAULT 0,
3226
+ token_density_sub_score DOUBLE NOT NULL DEFAULT 0,
3201
3227
  avg_cache_hit_rate DOUBLE NOT NULL DEFAULT 0,
3202
3228
  avg_io_ratio DOUBLE NOT NULL DEFAULT 0,
3203
3229
  context_spike_count INTEGER NOT NULL DEFAULT 0,
3204
3230
  total_turns INTEGER NOT NULL DEFAULT 0,
3231
+ cold_start_turns_excluded INTEGER NOT NULL DEFAULT 0,
3205
3232
  per_model_json TEXT NOT NULL DEFAULT '[]',
3206
3233
  per_source_json TEXT NOT NULL DEFAULT '[]',
3207
3234
  dispatch_id TEXT,
@@ -3218,6 +3245,9 @@ async function initTelemetrySchema(adapter) {
3218
3245
  ]) try {
3219
3246
  await adapter.exec(`ALTER TABLE efficiency_scores ADD COLUMN ${col} TEXT`);
3220
3247
  } catch {}
3248
+ for (const [col, ddl] of [["token_density_sub_score", "DOUBLE NOT NULL DEFAULT 0"], ["cold_start_turns_excluded", "INTEGER NOT NULL DEFAULT 0"]]) try {
3249
+ await adapter.exec(`ALTER TABLE efficiency_scores ADD COLUMN ${col} ${ddl}`);
3250
+ } catch {}
3221
3251
  await adapter.exec(`
3222
3252
  CREATE TABLE IF NOT EXISTS recommendations (
3223
3253
  id VARCHAR(16) NOT NULL,
@@ -6302,6 +6332,7 @@ const SubstrateConfigSchema = z.object({
6302
6332
  telemetry: TelemetryConfigSchema.optional(),
6303
6333
  worktree: WorktreeConfigSchema.optional(),
6304
6334
  trivialOutputThreshold: z.number().int().nonnegative().optional(),
6335
+ dispatch: z.object({ permission_profile: z.enum(["skip", "scoped"]).optional() }).strict().optional(),
6305
6336
  finalization: z.object({
6306
6337
  mode: z.enum([
6307
6338
  "merge",
@@ -6328,6 +6359,7 @@ const PartialSubstrateConfigSchema = z.object({
6328
6359
  telemetry: TelemetryConfigSchema.partial().optional(),
6329
6360
  worktree: WorktreeConfigSchema.partial().optional(),
6330
6361
  trivialOutputThreshold: z.number().int().nonnegative().optional(),
6362
+ dispatch: z.object({ permission_profile: z.enum(["skip", "scoped"]).optional() }).strict().optional(),
6331
6363
  finalization: z.object({
6332
6364
  mode: z.enum([
6333
6365
  "merge",
@@ -6668,8 +6700,8 @@ function readEnvOverrides(logger) {
6668
6700
  /**
6669
6701
  * Get a value from a nested object using dot-notation key.
6670
6702
  */
6671
- function getByPath(obj, path$3) {
6672
- const parts = path$3.split(".");
6703
+ function getByPath(obj, path$4) {
6704
+ const parts = path$4.split(".");
6673
6705
  let cursor = obj;
6674
6706
  for (const part of parts) {
6675
6707
  if (cursor === null || cursor === void 0 || typeof cursor !== "object") return void 0;
@@ -6681,8 +6713,8 @@ function getByPath(obj, path$3) {
6681
6713
  * Return a deep clone of `obj` with `path` set to `value`.
6682
6714
  * Creates intermediate objects as needed.
6683
6715
  */
6684
- function setByPath(obj, path$3, value) {
6685
- const parts = path$3.split(".");
6716
+ function setByPath(obj, path$4, value) {
6717
+ const parts = path$4.split(".");
6686
6718
  const result = { ...obj };
6687
6719
  let cursor = result;
6688
6720
  for (let i = 0; i < parts.length - 1; i++) {
@@ -9528,6 +9560,40 @@ function checkAdapterVersionCompat(adapterName, actualVersion, tested) {
9528
9560
  //#endregion
9529
9561
  //#region packages/core/dist/adapters/claude-adapter.js
9530
9562
  const execAsync$2 = promisify(exec);
9563
+ /**
9564
+ * H4.3: generate the per-worktree Claude Code settings file for the
9565
+ * 'scoped' permission profile. Written NEXT TO the worktree (its parent
9566
+ * directory — the worktree base, outside any repo after H4.2) so it can
9567
+ * never be committed by commit-first.
9568
+ *
9569
+ * Rules: reads/search/bash allowed everywhere (verification needs the
9570
+ * suite + git); file MUTATION allowed only under the worktree — anything
9571
+ * else falls to "ask", which headless -p mode denies visibly (there is no
9572
+ * interactive prompt to stall on).
9573
+ */
9574
+ function writeScopedPermissionSettings(worktreePath) {
9575
+ const wt = path$3.resolve(worktreePath);
9576
+ const settings = { permissions: {
9577
+ allow: [
9578
+ "Read",
9579
+ "Glob",
9580
+ "Grep",
9581
+ "Bash",
9582
+ "WebFetch",
9583
+ "WebSearch",
9584
+ "TodoWrite",
9585
+ "Task",
9586
+ `Edit(${wt}/**)`,
9587
+ `Write(${wt}/**)`,
9588
+ `NotebookEdit(${wt}/**)`
9589
+ ],
9590
+ deny: []
9591
+ } };
9592
+ const settingsPath = path$3.join(path$3.dirname(wt), `.agent-settings-${path$3.basename(wt)}.json`);
9593
+ mkdirSync(path$3.dirname(settingsPath), { recursive: true });
9594
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
9595
+ return settingsPath;
9596
+ }
9531
9597
  /** Default model used when none is specified */
9532
9598
  const DEFAULT_MODEL$1 = "claude-sonnet-4-6";
9533
9599
  /**
@@ -9650,9 +9716,12 @@ var ClaudeCodeAdapter = class ClaudeCodeAdapter {
9650
9716
  const args = [
9651
9717
  "-p",
9652
9718
  "--model",
9653
- model,
9654
- "--dangerously-skip-permissions"
9719
+ model
9655
9720
  ];
9721
+ if (options.permissionProfile === "scoped") {
9722
+ const settingsPath = writeScopedPermissionSettings(options.worktreePath);
9723
+ args.push("--permission-mode", "acceptEdits", "--settings", settingsPath);
9724
+ } else args.push("--dangerously-skip-permissions");
9656
9725
  args.push("--output-format", "stream-json", "--verbose");
9657
9726
  if (options.additionalFlags && options.additionalFlags.length > 0) args.push(...options.additionalFlags);
9658
9727
  const systemPromptParts = [BASE_SYSTEM_PROMPT];
@@ -11974,9 +12043,9 @@ var VersionManagerImpl = class {
11974
12043
  try {
11975
12044
  const _require = createRequire(import.meta.url);
11976
12045
  const fs = _require("fs");
11977
- const path$3 = _require("path");
12046
+ const path$4 = _require("path");
11978
12047
  const yaml$1 = _require("js-yaml");
11979
- const configPath = path$3.join(process.cwd(), ".substrate", "config.yaml");
12048
+ const configPath = path$4.join(process.cwd(), ".substrate", "config.yaml");
11980
12049
  try {
11981
12050
  const raw = fs.readFileSync(configPath, "utf-8");
11982
12051
  configObj = yaml$1.load(raw) ?? {};
@@ -12531,4 +12600,4 @@ async function callLLM(params) {
12531
12600
 
12532
12601
  //#endregion
12533
12602
  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 };
12534
- //# sourceMappingURL=dist-C2CAZDep.js.map
12603
+ //# sourceMappingURL=dist-90byyGPp.js.map
@@ -1,4 +1,4 @@
1
- import { AdtError } from "./dist-C2CAZDep.js";
1
+ import { AdtError } from "./dist-90byyGPp.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-Db8EIM9w.js.map
74
+ //# sourceMappingURL=errors-ST3E8qWc.js.map
@@ -1,3 +1,3 @@
1
- import { buildAuditLogEntry, buildBranchName, buildModificationDirective, buildPRBody, buildWorktreePath, createExperimenter, determineVerdict, resolvePromptFile } from "./dist-C2CAZDep.js";
1
+ import { buildAuditLogEntry, buildBranchName, buildModificationDirective, buildPRBody, buildWorktreePath, createExperimenter, determineVerdict, resolvePromptFile } from "./dist-90byyGPp.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-C2CAZDep.js";
3
- import { resolveMainRepoRoot, resolveRunManifest } from "./manifest-read-knD2uWkz.js";
2
+ import { DoltClient, DoltQueryError, createDatabaseAdapter$1 as createDatabaseAdapter, getLatestRun, getPipelineRunById, initSchema } from "./dist-90byyGPp.js";
3
+ import { resolveMainRepoRoot, resolveRunManifest } from "./manifest-read-BOvY53EG.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-ByQadq3J.js.map
1003
+ //# sourceMappingURL=health-BERxzeG-.js.map
@@ -1,7 +1,7 @@
1
- import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-ByQadq3J.js";
1
+ import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-BERxzeG-.js";
2
2
  import "./logger-KeHncl-f.js";
3
- import "./dist-C2CAZDep.js";
4
- import "./manifest-read-knD2uWkz.js";
3
+ import "./dist-90byyGPp.js";
4
+ import "./manifest-read-BOvY53EG.js";
5
5
  import "./work-graph-repository-DZyJv5pV.js";
6
6
  import "./decisions-CzSIEeGP.js";
7
7
 
@@ -296,6 +296,12 @@ declare const SubstrateConfigSchema: z.ZodObject<{
296
296
  }>>;
297
297
  }, z.core.$strict>>;
298
298
  trivialOutputThreshold: z.ZodOptional<z.ZodNumber>;
299
+ dispatch: z.ZodOptional<z.ZodObject<{
300
+ permission_profile: z.ZodOptional<z.ZodEnum<{
301
+ skip: "skip";
302
+ scoped: "scoped";
303
+ }>>;
304
+ }, z.core.$strict>>;
299
305
  finalization: z.ZodOptional<z.ZodObject<{
300
306
  mode: z.ZodOptional<z.ZodEnum<{
301
307
  merge: "merge";
@@ -860,6 +866,15 @@ interface SpawnCommand {
860
866
  stdin?: string;
861
867
  /** Optional timeout in milliseconds */
862
868
  timeoutMs?: number;
869
+ /**
870
+ * H4.4 (container-ready seam): how this command is executed.
871
+ * 'spawn' (default, and the only implemented mode) = child_process.spawn on
872
+ * the host. 'container' is RESERVED for a future backend that runs the
873
+ * agent in a container with the worktree bind-mounted at the same path —
874
+ * the dispatcher MUST reject it until that backend exists. Mirrors the
875
+ * direct-API adapter design's `executionMode: 'spawn' | 'direct'` seam.
876
+ */
877
+ executionMode?: 'spawn' | 'container';
863
878
  }
864
879
  /**
865
880
  * Options passed to adapter methods for each invocation.
@@ -909,6 +924,13 @@ interface AdapterOptions {
909
924
  taskType?: string;
910
925
  /** Dispatch context: unique dispatch ID for per-dispatch telemetry correlation */
911
926
  dispatchId?: string;
927
+ /**
928
+ * H4.3: permission profile for the spawned agent. 'skip' (default) uses
929
+ * --dangerously-skip-permissions; 'scoped' generates a per-worktree
930
+ * settings file (mutation allowed only under the worktree) and runs with
931
+ * --permission-mode acceptEdits. Config: `dispatch.permission_profile`.
932
+ */
933
+ permissionProfile?: 'skip' | 'scoped';
912
934
  }
913
935
  /**
914
936
  * Capabilities reported by an adapter for this CLI agent.
@@ -1600,4 +1622,4 @@ interface Recommendation {
1600
1622
 
1601
1623
  //#endregion
1602
1624
  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 };
1603
- //# sourceMappingURL=index-DxeIRBBf.d.ts.map
1625
+ //# sourceMappingURL=index-CnV5PcSR.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-DxeIRBBf.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-CnV5PcSR.js";
2
2
  import pino from "pino";
3
3
  import { z } from "zod";
4
4
  import { Readable, Writable } from "node:stream";
@@ -1068,6 +1068,12 @@ declare const SubstrateConfigSchema: z.ZodObject<{
1068
1068
  supervisor_poll_interval_seconds: z.ZodOptional<z.ZodNumber>;
1069
1069
  retry_budget: z.ZodOptional<z.ZodNumber>;
1070
1070
  epics_path: z.ZodOptional<z.ZodString>;
1071
+ dispatch: z.ZodOptional<z.ZodObject<{
1072
+ permission_profile: z.ZodOptional<z.ZodEnum<{
1073
+ skip: "skip";
1074
+ scoped: "scoped";
1075
+ }>>;
1076
+ }, z.core.$strict>>;
1071
1077
  finalization: z.ZodOptional<z.ZodObject<{
1072
1078
  mode: z.ZodOptional<z.ZodEnum<{
1073
1079
  merge: "merge";
@@ -2165,6 +2171,15 @@ interface SpawnCommand {
2165
2171
  stdin?: string;
2166
2172
  /** Optional timeout in milliseconds */
2167
2173
  timeoutMs?: number;
2174
+ /**
2175
+ * H4.4 (container-ready seam): how this command is executed.
2176
+ * 'spawn' (default, and the only implemented mode) = child_process.spawn on
2177
+ * the host. 'container' is RESERVED for a future backend that runs the
2178
+ * agent in a container with the worktree bind-mounted at the same path —
2179
+ * the dispatcher MUST reject it until that backend exists. Mirrors the
2180
+ * direct-API adapter design's `executionMode: 'spawn' | 'direct'` seam.
2181
+ */
2182
+ executionMode?: 'spawn' | 'container';
2168
2183
  }
2169
2184
  /**
2170
2185
  * Options passed to adapter methods for each invocation.
@@ -2214,6 +2229,13 @@ interface AdapterOptions {
2214
2229
  taskType?: string;
2215
2230
  /** Dispatch context: unique dispatch ID for per-dispatch telemetry correlation */
2216
2231
  dispatchId?: string;
2232
+ /**
2233
+ * H4.3: permission profile for the spawned agent. 'skip' (default) uses
2234
+ * --dangerously-skip-permissions; 'scoped' generates a per-worktree
2235
+ * settings file (mutation allowed only under the worktree) and runs with
2236
+ * --permission-mode acceptEdits. Config: `dispatch.permission_profile`.
2237
+ */
2238
+ permissionProfile?: 'skip' | 'scoped';
2217
2239
  }
2218
2240
  /**
2219
2241
  * Capabilities reported by an adapter for this CLI agent.
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-C2CAZDep.js";
3
+ import { AdapterRegistry, AdtError, ClaudeCodeAdapter, CodexCLIAdapter, ConfigError, ConfigIncompatibleFormatError, GeminiCLIAdapter } from "./dist-90byyGPp.js";
4
4
  import "./adapter-registry-DXLMTmfD.js";
5
- import { BudgetExceededError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError } from "./errors-Db8EIM9w.js";
5
+ import { BudgetExceededError, GitError, RecoveryError, TaskConfigError, TaskGraphCycleError, TaskGraphError, TaskGraphIncompatibleFormatError, WorkerError, WorkerNotFoundError } from "./errors-ST3E8qWc.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-knD2uWkz.js";
2
+ import { readCurrentRunId, resolveMainRepoRoot } from "./manifest-read-BOvY53EG.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-CMBlj6UF.js.map
183
+ //# sourceMappingURL=interactive-prompt-UWLccCqL.js.map
@@ -1,5 +1,5 @@
1
1
  import { createLogger } from "./logger-KeHncl-f.js";
2
- import { LEARNING_FINDING, createDecision, getDecisionsByCategory } from "./dist-C2CAZDep.js";
2
+ import { LEARNING_FINDING, createDecision, getDecisionsByCategory } from "./dist-90byyGPp.js";
3
3
  import * as path$1 from "path";
4
4
  import { join } from "path";
5
5
  import { readFile } from "fs/promises";
@@ -3449,9 +3449,13 @@ var RuntimeProbeCheck = class {
3449
3449
  });
3450
3450
  }
3451
3451
  const status = findings.some((f) => f.severity === "error") ? "fail" : findings.some((f) => f.severity === "warn") ? "warn" : "pass";
3452
+ const deferredCount = findings.filter((f) => f.category === CATEGORY_DEFERRED).length;
3453
+ const failedCount = findings.filter((f) => f.severity === "error").length;
3454
+ const ranCount = parsed.probes.filter((probe) => probe.sandbox !== "twin").length;
3455
+ const summaryLine = `runtime-probes: ${ranCount} ran (${ranCount - failedCount} passed, ${failedCount} failed)` + (deferredCount > 0 ? `, ${deferredCount} deferred (sandbox=twin, Phase 3)` : "");
3452
3456
  return {
3453
3457
  status,
3454
- details: findings.length > 0 ? renderFindings(findings) : `runtime-probes: ${parsed.probes.length} probe(s) passed`,
3458
+ details: findings.length > 0 ? `${summaryLine}\n${renderFindings(findings)}` : summaryLine,
3455
3459
  duration_ms: Date.now() - start,
3456
3460
  findings
3457
3461
  };
@@ -6300,4 +6304,4 @@ async function resolveRunManifest(dbRoot, runId) {
6300
6304
 
6301
6305
  //#endregion
6302
6306
  export { FindingsInjector, 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, parseRuntimeProbes, readCurrentRunId, renderFindings, resolveGraphPath, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, rollupFindingsByAuthor, rollupProbeAuthorByClass, rollupProbeAuthorMetrics, runAcTraceabilityCheck, runStaleVerificationRecovery };
6303
- //# sourceMappingURL=manifest-read-knD2uWkz.js.map
6307
+ //# sourceMappingURL=manifest-read-BOvY53EG.js.map
@@ -1,6 +1,6 @@
1
1
  import "../../logger-KeHncl-f.js";
2
- import "../../dist-C2CAZDep.js";
3
- import "../../manifest-read-knD2uWkz.js";
4
- import { runInteractivePrompt } from "../../interactive-prompt-CMBlj6UF.js";
2
+ import "../../dist-90byyGPp.js";
3
+ import "../../manifest-read-BOvY53EG.js";
4
+ import { runInteractivePrompt } from "../../interactive-prompt-UWLccCqL.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-C2CAZDep.js";
1
+ import { ModelRoutingConfigSchema, ProviderPolicySchema, RoutingConfigError, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, TASK_TYPE_PHASE_MAP, getModelTier, loadModelRoutingConfig } from "./dist-90byyGPp.js";
2
2
  import "./routing-DFxoKHDt.js";
3
3
 
4
4
  export { loadModelRoutingConfig };
@@ -1,14 +1,14 @@
1
- import "./health-ByQadq3J.js";
1
+ import "./health-BERxzeG-.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./helpers-CElYrONe.js";
4
- import "./dist-C2CAZDep.js";
5
- import "./manifest-read-knD2uWkz.js";
6
- import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-BAUlPom3.js";
4
+ import "./dist-90byyGPp.js";
5
+ import "./manifest-read-BOvY53EG.js";
6
+ import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-Dgqfz9HY.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-CMBlj6UF.js";
11
+ import "./interactive-prompt-UWLccCqL.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-ByQadq3J.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-BERxzeG-.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-C2CAZDep.js";
5
- import { FindingsInjector, RunManifest, RuntimeProbeListSchema, applyConfigToGraph, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, parseRuntimeProbes, renderFindings, resolveGraphPath, resolveMainRepoRoot, runAcTraceabilityCheck, runStaleVerificationRecovery } from "./manifest-read-knD2uWkz.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-90byyGPp.js";
5
+ import { FindingsInjector, RunManifest, RuntimeProbeListSchema, applyConfigToGraph, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, parseRuntimeProbes, renderFindings, resolveGraphPath, resolveMainRepoRoot, runAcTraceabilityCheck, runStaleVerificationRecovery } from "./manifest-read-BOvY53EG.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-CMBlj6UF.js";
8
+ import { runInteractivePrompt } from "./interactive-prompt-UWLccCqL.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";
@@ -951,7 +951,7 @@ const PIPELINE_EVENT_METADATA = [
951
951
  {
952
952
  name: "reason",
953
953
  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). v0.20.87+."
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). v0.20.87+."
955
955
  },
956
956
  {
957
957
  name: "cycles",
@@ -7304,6 +7304,41 @@ async function stageIntentToAdd(files, workingDirectory) {
7304
7304
  * Run a git command in the specified directory and return its stdout output.
7305
7305
  * Returns '' on any error and logs a warning.
7306
7306
  */
7307
+ /**
7308
+ * H5.5 (story-artifact resilience): recover a story file from the branch
7309
+ * HEAD when the working-tree copy has gone missing (live-smoke incident
7310
+ * 2026-07-06: a fix-phase agent DELETED the committed story artifact; the
7311
+ * re-review hit ENOENT and was misclassified `consecutive-review-timeouts`
7312
+ * while the file sat safely in the H0.1 feat commit the whole time).
7313
+ *
7314
+ * Reads `git show HEAD:<relpath>` in the worktree and, on success, WRITES
7315
+ * the content back to the working tree so downstream phases find it too.
7316
+ * Returns the content, or undefined when the file isn't in HEAD either.
7317
+ */
7318
+ async function recoverStoryFileFromBranch(storyFilePath, workingDirectory) {
7319
+ const rel = relative(workingDirectory, resolve$1(workingDirectory, storyFilePath));
7320
+ if (rel.startsWith("..")) {
7321
+ logger$20.warn({
7322
+ storyFilePath,
7323
+ workingDirectory
7324
+ }, "recoverStoryFileFromBranch: path outside the worktree — not recoverable");
7325
+ return void 0;
7326
+ }
7327
+ const content = await runGitCommand(["show", `HEAD:${rel}`], workingDirectory, "recover-story-file");
7328
+ if (content.trim().length === 0) return void 0;
7329
+ try {
7330
+ const abs = resolve$1(workingDirectory, rel);
7331
+ await mkdir$1(dirname$1(abs), { recursive: true });
7332
+ await writeFile$1(abs, content, "utf-8");
7333
+ logger$20.warn({ storyFilePath: abs }, "H5.5: story file restored to the working tree from branch HEAD");
7334
+ } catch (writeErr) {
7335
+ logger$20.warn({
7336
+ storyFilePath,
7337
+ err: writeErr
7338
+ }, "H5.5: recovered story content but could not restore the file (continuing with in-memory content)");
7339
+ }
7340
+ return content;
7341
+ }
7307
7342
  async function runGitCommand(args, cwd, logLabel) {
7308
7343
  return new Promise((resolve$6) => {
7309
7344
  let stdout = "";
@@ -7746,20 +7781,28 @@ async function runDevStory(deps, params) {
7746
7781
  try {
7747
7782
  storyContent = await readFile$1(storyFilePath, "utf-8");
7748
7783
  } catch (err) {
7749
- if (err.code === "ENOENT") {
7784
+ const recovered = await recoverStoryFileFromBranch(storyFilePath, deps.projectRoot ?? process.cwd());
7785
+ if (recovered !== void 0) {
7786
+ logger$18.warn({
7787
+ storyKey,
7788
+ storyFilePath
7789
+ }, "story file missing from working tree — recovered from branch HEAD (H5.5)");
7790
+ storyContent = recovered;
7791
+ } else if (err.code === "ENOENT") {
7750
7792
  logger$18.error({
7751
7793
  storyKey,
7752
7794
  storyFilePath
7753
7795
  }, "Story file not found");
7754
7796
  return makeFailureResult("story_file_not_found");
7797
+ } else {
7798
+ const error = err instanceof Error ? err.message : String(err);
7799
+ logger$18.error({
7800
+ storyKey,
7801
+ storyFilePath,
7802
+ error
7803
+ }, "Failed to read story file");
7804
+ return makeFailureResult(`story_file_read_error: ${error}`);
7755
7805
  }
7756
- const error = err instanceof Error ? err.message : String(err);
7757
- logger$18.error({
7758
- storyKey,
7759
- storyFilePath,
7760
- error
7761
- }, "Failed to read story file");
7762
- return makeFailureResult(`story_file_read_error: ${error}`);
7763
7806
  }
7764
7807
  if (storyContent.trim().length === 0) {
7765
7808
  logger$18.error({
@@ -8525,14 +8568,20 @@ async function runCodeReview(deps, params) {
8525
8568
  storyContent = await readFile$1(storyFilePath, "utf-8");
8526
8569
  } catch (err) {
8527
8570
  const error = err instanceof Error ? err.message : String(err);
8528
- logger$17.error({
8529
- storyFilePath,
8530
- error
8531
- }, "Failed to read story file");
8532
- return defaultFailResult(`Failed to read story file: ${error}`, {
8533
- input: 0,
8534
- output: 0
8535
- });
8571
+ const recovered = await recoverStoryFileFromBranch(storyFilePath, cwd);
8572
+ if (recovered !== void 0) {
8573
+ logger$17.warn({ storyFilePath }, "story file missing from working tree — recovered from branch HEAD (H5.5)");
8574
+ storyContent = recovered;
8575
+ } else {
8576
+ logger$17.error({
8577
+ storyFilePath,
8578
+ error
8579
+ }, "Failed to read story file (absent from working tree AND branch HEAD)");
8580
+ return defaultFailResult(`story-file-missing: ${storyFilePath} absent from working tree and branch HEAD (${error})`, {
8581
+ input: 0,
8582
+ output: 0
8583
+ });
8584
+ }
8536
8585
  }
8537
8586
  const archConstraintsContent = await getArchConstraints$2(deps);
8538
8587
  const templateTokens = countTokens(template);
@@ -16449,23 +16498,26 @@ function createImplementationOrchestrator(deps) {
16449
16498
  continue;
16450
16499
  }
16451
16500
  if (isPhantomReview && timeoutRetried) {
16501
+ const isStoryFileMissing = (reviewResult.error ?? "").includes("story-file-missing");
16502
+ const escalationReason = isStoryFileMissing ? "story-file-missing" : "consecutive-review-timeouts";
16452
16503
  logger$27.warn({
16453
16504
  storyKey,
16454
16505
  reviewCycles,
16455
- error: reviewResult.error
16456
- }, "Consecutive review timeouts detected (original + retry both failed) — escalating immediately");
16506
+ error: reviewResult.error,
16507
+ reason: escalationReason
16508
+ }, "Consecutive review failures (original + retry) — escalating immediately");
16457
16509
  endPhase(storyKey, "code-review");
16458
16510
  updateStory(storyKey, {
16459
16511
  phase: "ESCALATED",
16460
- error: "consecutive-review-timeouts",
16512
+ error: escalationReason,
16461
16513
  completedAt: new Date().toISOString()
16462
16514
  });
16463
16515
  await writeStoryMetricsBestEffort(storyKey, "escalated", reviewCycles + 1);
16464
16516
  await emitEscalation({
16465
16517
  storyKey,
16466
- lastVerdict: "consecutive-review-timeouts",
16518
+ lastVerdict: escalationReason,
16467
16519
  reviewCycles: reviewCycles + 1,
16468
- issues: ["Review dispatch failed twice consecutively (original + phantom-retry). Likely resource-constrained or diff too large for reviewer."]
16520
+ issues: [isStoryFileMissing ? `Story artifact is missing from BOTH the working tree and the branch HEAD (${reviewResult.error ?? ""}). An agent deleted it and it was never committed — check the wip/feat commits on the story branch for the last good copy.` : "Review dispatch failed twice consecutively (original + phantom-retry). Likely resource-constrained or diff too large for reviewer."]
16469
16521
  });
16470
16522
  await persistState();
16471
16523
  return;
@@ -46317,6 +46369,7 @@ async function runRunAction(options) {
46317
46369
  let configFinalizationMode;
46318
46370
  let configMergeStrategy;
46319
46371
  let configEpicGateCommand;
46372
+ let configPermissionProfile;
46320
46373
  let configWorktreeCopyFiles;
46321
46374
  try {
46322
46375
  const configSystem = createConfigSystem({ projectConfigDir: dbDir });
@@ -46346,6 +46399,7 @@ async function runRunAction(options) {
46346
46399
  if (cfg.finalization?.mode !== void 0) configFinalizationMode = cfg.finalization.mode;
46347
46400
  if (cfg.finalization?.merge_strategy !== void 0) configMergeStrategy = cfg.finalization.merge_strategy;
46348
46401
  if (cfg.finalization?.epic_gate_command !== void 0) configEpicGateCommand = cfg.finalization.epic_gate_command;
46402
+ if (cfg.dispatch?.permission_profile !== void 0) configPermissionProfile = cfg.dispatch.permission_profile;
46349
46403
  if (Array.isArray(cfg.worktree?.copy_files) && cfg.worktree.copy_files.length > 0) {
46350
46404
  configWorktreeCopyFiles = [...cfg.worktree.copy_files];
46351
46405
  logger.info({ copyFiles: configWorktreeCopyFiles }, "Loaded worktree.copy_files from config");
@@ -46448,6 +46502,7 @@ async function runRunAction(options) {
46448
46502
  ...finalization !== void 0 ? { finalizationMode: finalization } : configFinalizationMode !== void 0 ? { finalizationMode: configFinalizationMode } : {},
46449
46503
  ...configMergeStrategy !== void 0 ? { mergeStrategy: configMergeStrategy } : {},
46450
46504
  ...configEpicGateCommand !== void 0 ? { epicGateCommand: configEpicGateCommand } : {},
46505
+ ...configPermissionProfile !== void 0 ? { permissionProfile: configPermissionProfile } : {},
46451
46506
  ...configWorktreeCopyFiles !== void 0 ? { worktreeCopyFiles: configWorktreeCopyFiles } : {}
46452
46507
  });
46453
46508
  let storyKeys = [...parsedStoryKeys];
@@ -46696,7 +46751,8 @@ async function runRunAction(options) {
46696
46751
  adapterRegistry: injectedRegistry,
46697
46752
  config: {
46698
46753
  routingResolver,
46699
- ...dispatchTimeouts ? { defaultTimeouts: dispatchTimeouts } : {}
46754
+ ...dispatchTimeouts ? { defaultTimeouts: dispatchTimeouts } : {},
46755
+ ...configPermissionProfile !== void 0 ? { permissionProfile: configPermissionProfile } : {}
46700
46756
  }
46701
46757
  });
46702
46758
  eventBus.on("orchestrator:story-phase-complete", (payload) => {
@@ -47237,7 +47293,7 @@ async function runRunAction(options) {
47237
47293
  }
47238
47294
  }
47239
47295
  async function runFullPipeline(options) {
47240
- 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 } = options;
47296
+ 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;
47241
47297
  if (!existsSync$1(dbDir)) mkdirSync$1(dbDir, { recursive: true });
47242
47298
  let doltServerFull = null;
47243
47299
  try {
@@ -47278,7 +47334,10 @@ async function runFullPipeline(options) {
47278
47334
  const dispatcher = createDispatcher({
47279
47335
  eventBus,
47280
47336
  adapterRegistry: injectedRegistry,
47281
- config: { routingResolver }
47337
+ config: {
47338
+ routingResolver,
47339
+ ...fpPermissionProfile !== void 0 ? { permissionProfile: fpPermissionProfile } : {}
47340
+ }
47282
47341
  });
47283
47342
  const phaseDeps = {
47284
47343
  db: adapter,
@@ -47720,7 +47779,7 @@ async function runFullPipeline(options) {
47720
47779
  */
47721
47780
  async function emitPreDispatchVersionAdvisory(currentVersion) {
47722
47781
  if (process.env["SUBSTRATE_NO_UPDATE_CHECK"] === "1") return;
47723
- const { createVersionManager } = await import("./version-manager-impl-DizCvYy8.js");
47782
+ const { createVersionManager } = await import("./version-manager-impl--XXnwjMd.js");
47724
47783
  const vm = createVersionManager();
47725
47784
  const result = await vm.checkForUpdates(true);
47726
47785
  const gap = classifyVersionGap(currentVersion, result.latestVersion);
@@ -47827,4 +47886,4 @@ function registerRunCommand(program, version = "0.0.0", projectRoot = process.cw
47827
47886
 
47828
47887
  //#endregion
47829
47888
  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 };
47830
- //# sourceMappingURL=run-BAUlPom3.js.map
47889
+ //# sourceMappingURL=run-Dgqfz9HY.js.map
@@ -1,4 +1,4 @@
1
- import { CoreEvents, DatabaseAdapter, TypedEventBus } from "../../../index-DxeIRBBf.js";
1
+ import { CoreEvents, DatabaseAdapter, TypedEventBus } from "../../../index-CnV5PcSR.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region packages/sdlc/dist/verification/findings.d.ts
@@ -1,4 +1,4 @@
1
- import { createVersionManager } from "./dist-C2CAZDep.js";
1
+ import { createVersionManager } from "./dist-90byyGPp.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-DOmKCyOY.js.map
126
+ //# sourceMappingURL=upgrade-Dw6jKkH3.js.map
@@ -1,5 +1,5 @@
1
- import "./dist-C2CAZDep.js";
1
+ import "./dist-90byyGPp.js";
2
2
  import "./version-manager-impl-qFBiO4Eh.js";
3
- import { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand } from "./upgrade-DOmKCyOY.js";
3
+ import { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand } from "./upgrade-Dw6jKkH3.js";
4
4
 
5
5
  export { isGlobalInstall, registerUpgradeCommand, runUpgradeCommand };
@@ -1,4 +1,4 @@
1
- import { VersionManagerImpl, createVersionManager } from "./dist-C2CAZDep.js";
1
+ import { VersionManagerImpl, createVersionManager } from "./dist-90byyGPp.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.20.149",
3
+ "version": "0.20.151",
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-C2CAZDep.js";
2
- import "./adapter-registry-DXLMTmfD.js";
3
-
4
- export { AdapterRegistry };