substrate-ai 0.21.1 → 0.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -3
- package/dist/cli/index.js +60 -8
- package/dist/cli/templates/agents-md-substrate-section.md +12 -1
- package/dist/cli/templates/gemini-md-substrate-section.md +12 -1
- package/dist/{health-Coh3BG8W.js → health-DDsjslMy.js} +2 -2
- package/dist/{health-Cfkv6XoQ.js → health-DvgJ7jj_.js} +2 -2
- package/dist/{interactive-prompt-BBfPSxyJ.js → interactive-prompt-CPF2GTvV.js} +2 -2
- package/dist/{manifest-read-BhNRIiqm.js → manifest-read-DyyFPrJH.js} +221 -3
- package/dist/modules/interactive-prompt/index.js +2 -2
- package/dist/{run-JrEnvaB0.js → run-CAmBrDky.js} +61 -5
- package/dist/{run-CKM-5T34.js → run-CF0qLJAV.js} +4 -4
- package/package.json +1 -1
- package/packs/bmad/prompts/create-story.md +22 -0
package/README.md
CHANGED
|
@@ -157,11 +157,21 @@ Stories run in parallel across your available agents, each in its own git worktr
|
|
|
157
157
|
|
|
158
158
|
#### Per-Story Worktree Lifecycle
|
|
159
159
|
|
|
160
|
-
Each dispatched story runs in a dedicated git worktree
|
|
160
|
+
Each dispatched story runs in a dedicated git worktree on branch `substrate/story-<key>`. **Since v0.20.149 the worktree base is external by default** — `~/.substrate/worktrees/<projectname>-<hash8>/<key>/`, outside your repo; set `worktree.base: in-repo` in `.substrate/config.yaml` to restore the legacy `.substrate-worktrees/<key>/` path. **Commit-first (v0.20.139+):** substrate auto-commits the agent's output to the story branch (`feat(story-N-M): <title>`) at **dev-story completion — before review** — so the branch is always the durable copy of the work; failure paths add `wip(story-<key>)` checkpoints. Pre-commit hooks run on the substrate commit. If the commit fails (a hook rejects) or the run produced no committable changes, the story escalates `dev-story-commit-failed` / `dev-story-no-commit`. How the verified branch integrates is controlled by [finalization mode](#finalization). After a verification failure, the worktree and branch are preserved for inspection via `substrate reconcile-from-disk`. Use `--no-worktree` for projects that don't support worktrees (submodules, bare repos).
|
|
161
|
+
|
|
162
|
+
#### Finalization
|
|
163
|
+
|
|
164
|
+
`finalization.mode` in `.substrate/config.yaml` (or `--finalization <mode>` per run) decides how a verified story's branch integrates:
|
|
165
|
+
|
|
166
|
+
- **`merge`** (default) — local merge into the run's start branch, **fast-forward-only by default** (a base that moved during the run escalates `ff-only-merge-not-possible` rather than fabricating a merge commit). `finalization.merge_strategy: three-way` enables merge commits and is **required for concurrent multi-story runs**.
|
|
167
|
+
- **`branch`** — nothing self-merges; the `substrate/story-<key>` branch is the deliverable (safe for brownfield repos).
|
|
168
|
+
- **`pr`** — branch + push + `gh pr create` (one PR per story); degrades to `branch` on push/gh failure, never blocks.
|
|
169
|
+
|
|
170
|
+
NDJSON lifecycle events `story:committed` → (`story:merged`) → `story:finalized` report the outcome, and `substrate report` lists any unmerged deliverable branches.
|
|
161
171
|
|
|
162
172
|
### Verification Pipeline
|
|
163
173
|
|
|
164
|
-
|
|
174
|
+
A Tier-A gate sequence runs after code review. Each gate can pass, warn, or fail; failures block SHIP_IT.
|
|
165
175
|
|
|
166
176
|
| Gate | What it catches |
|
|
167
177
|
|---|---|
|
|
@@ -169,6 +179,10 @@ Six gates run after code review. Each can pass, warn, or fail; failures block SH
|
|
|
169
179
|
| **trivial-output** | Output token count below threshold — likely no real work done |
|
|
170
180
|
| **acceptance-criteria-evidence** | Each AC has demonstrable evidence in dev-story signals (files modified, tests added) |
|
|
171
181
|
| **build** | Project build succeeds against the dev's worktree |
|
|
182
|
+
| **test-suite** | Runs the project's real test command (from the trusted main-tree profile) and fails on a red suite; rejects exit-code-laundering wrappers (`… \|\| true`) |
|
|
183
|
+
| **scope-contamination** | Foreign-language sources / foreign toolchain manifests / build-output droppings vs the project's declared languages |
|
|
184
|
+
| **test-mutation** | Warns when a story modifies or deletes pre-existing tracked test files (reward-hack tripwire), including committed and renamed test files |
|
|
185
|
+
| **net-new-implementation** | A "success" story whose diff is artifact-only, an empty stub, or a whitespace-only edit escalates `no-implementation` |
|
|
172
186
|
| **runtime-probes** | Each declared `## Runtime Probes` section probe runs successfully against real or sandboxed state. Includes auto-detection for error-shape envelopes (`{"isError": true}`, `{"status": "error"}`) and production-trigger requirements for event-driven ACs. Frontmatter `external_state_dependencies` declarations hard-gate when probes section is missing. |
|
|
173
187
|
| **source-ac-fidelity** | AC text in source epic appears verbatim in story artifact (paths, MUST clauses, hard contracts). Includes 4 context-aware heuristics: negation (paths the AC says NOT to deliver), dependency-context (peer packages the implementation imports), operational-path (system install destinations like `.git/hooks/`), and alternative-option groups. |
|
|
174
188
|
|
|
@@ -501,6 +515,20 @@ token_ceilings:
|
|
|
501
515
|
# Optional: dispatch timeout overrides (ms)
|
|
502
516
|
dispatch_timeouts:
|
|
503
517
|
dev-story: 1800000 # 30 min
|
|
518
|
+
|
|
519
|
+
# Optional: how verified stories integrate (v0.20.147+)
|
|
520
|
+
finalization:
|
|
521
|
+
mode: merge # merge (default) | branch | pr
|
|
522
|
+
merge_strategy: ff-only # ff-only (default) | three-way (needed for concurrent multi-story)
|
|
523
|
+
# epic_gate_command: "uv run pytest" # must pass before the last story of an epic integrates
|
|
524
|
+
|
|
525
|
+
# Optional: per-story worktree location (v0.20.149+)
|
|
526
|
+
worktree:
|
|
527
|
+
base: external # external (default: ~/.substrate/worktrees/...) | in-repo
|
|
528
|
+
|
|
529
|
+
# Optional: spawned-agent permission profile (v0.20.152+)
|
|
530
|
+
dispatch:
|
|
531
|
+
permission_profile: skip # skip (default) | scoped (per-worktree settings; accident-mitigation, not a security boundary)
|
|
504
532
|
```
|
|
505
533
|
|
|
506
534
|
### Configuration Files
|
|
@@ -527,7 +555,7 @@ substrate init
|
|
|
527
555
|
|
|
528
556
|
Without Dolt, all functionality works except for: `substrate diff`, `substrate history`, persistent OTEL observability tables, and context engineering repo-map storage.
|
|
529
557
|
|
|
530
|
-
**On-disk operator surface:** Each pipeline run creates per-story
|
|
558
|
+
**On-disk operator surface:** Each pipeline run creates a per-story worktree — by default under `~/.substrate/worktrees/<project>-<hash>/<key>/` (external base; `worktree.base: in-repo` restores `.substrate-worktrees/<key>/`). Verified stories integrate per the [finalization mode](#finalization) and their worktrees are removed; failed or escalated stories preserve their worktree and branch for operator inspection via `substrate reconcile-from-disk`.
|
|
531
559
|
|
|
532
560
|
## CLI Command Reference
|
|
533
561
|
|
|
@@ -551,6 +579,7 @@ These commands are typically invoked by your AI assistant during pipeline operat
|
|
|
551
579
|
| `substrate run --max-review-cycles <n>` | Cycles per story (default 2; use 3 for migrations / interface extraction) |
|
|
552
580
|
| `substrate run --skip-verification` | Skip post-dispatch verification (use sparingly) |
|
|
553
581
|
| `substrate run --no-worktree` | Disable per-story git worktrees (use for submodule repos or bare repos that don't support worktrees) |
|
|
582
|
+
| `substrate run --finalization <mode>` | How verified work integrates: `merge` (default) / `branch` / `pr` — see [Finalization](#finalization) |
|
|
554
583
|
| `substrate run --help-agent` | Print agent instruction prompt fragment |
|
|
555
584
|
| `substrate resume` | Resume an interrupted run |
|
|
556
585
|
| `substrate cancel` | Cancel a running pipeline |
|
package/dist/cli/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
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-
|
|
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";
|
|
3
3
|
import { createLogger } from "../logger-KeHncl-f.js";
|
|
4
4
|
import { createEventBus } from "../helpers-CElYrONe.js";
|
|
5
5
|
import { AdapterRegistry, BudgetConfigSchema, CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, ConfigError, CostTrackerConfigSchema, DEFAULT_CONFIG, DoltClient, DoltNotInstalled, GlobalSettingsSchema, InMemoryDatabaseAdapter, IngestionServer, MonitorDatabaseImpl, OPERATIONAL_FINDING, PartialGlobalSettingsSchema, PartialProviderConfigSchema, ProvidersSchema, RoutingRecommender, STORY_METRICS, TelemetryConfigSchema, addTokenUsage, aggregateTokenUsageForRun, checkDoltInstalled, compareRunMetrics, createAmendmentRun, createConfigSystem, createDecision, 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";
|
|
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-
|
|
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-
|
|
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
9
|
import "../errors-C7pY6_yQ.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-
|
|
14
|
+
import "../interactive-prompt-CPF2GTvV.js";
|
|
15
15
|
import "../recovery-engine-BKGBeBnW.js";
|
|
16
16
|
import "../version-manager-impl-qFBiO4Eh.js";
|
|
17
17
|
import { registerUpgradeCommand } from "../upgrade-S1oUMH5P.js";
|
|
@@ -699,6 +699,7 @@ const WHOLESALE_SUBSTRATE_IGNORES = new Set([
|
|
|
699
699
|
const STAR = ".substrate/*";
|
|
700
700
|
const CONFIG_NEGATION = "!.substrate/config.yaml";
|
|
701
701
|
const PROFILE_NEGATION = "!.substrate/project-profile.yaml";
|
|
702
|
+
const ACCEPTANCE_NEGATION = "!.substrate/acceptance/";
|
|
702
703
|
const CODEX_PROMPTS = ".codex/prompts/";
|
|
703
704
|
const CODEX_SKILLS = ".codex/skills/";
|
|
704
705
|
/**
|
|
@@ -717,6 +718,9 @@ function computeSubstrateGitignore(existing) {
|
|
|
717
718
|
const profileNegIdx = trimmed.lastIndexOf(PROFILE_NEGATION);
|
|
718
719
|
const profileNegationEffective = profileNegIdx !== -1 && profileNegIdx > starIdx;
|
|
719
720
|
if (!profileNegationEffective) append.push(PROFILE_NEGATION);
|
|
721
|
+
const acceptanceNegIdx = trimmed.lastIndexOf(ACCEPTANCE_NEGATION);
|
|
722
|
+
const acceptanceNegationEffective = acceptanceNegIdx !== -1 && acceptanceNegIdx > starIdx;
|
|
723
|
+
if (!acceptanceNegationEffective) append.push(ACCEPTANCE_NEGATION);
|
|
720
724
|
if (!trimmed.includes(CODEX_PROMPTS)) append.push(CODEX_PROMPTS);
|
|
721
725
|
if (!trimmed.includes(CODEX_SKILLS)) append.push(CODEX_SKILLS);
|
|
722
726
|
let content = lines.join("\n");
|
|
@@ -7126,7 +7130,7 @@ async function runStatusAction(options) {
|
|
|
7126
7130
|
logger$15.debug({ err }, "Work graph query failed, continuing without work graph data");
|
|
7127
7131
|
}
|
|
7128
7132
|
if (run === void 0) {
|
|
7129
|
-
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-
|
|
7133
|
+
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-DDsjslMy.js");
|
|
7130
7134
|
const substrateDirPath = join(projectRoot, ".substrate");
|
|
7131
7135
|
const processInfo = inspectProcessTree$1({
|
|
7132
7136
|
projectRoot,
|
|
@@ -8655,7 +8659,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
8655
8659
|
await initSchema(expAdapter);
|
|
8656
8660
|
const { runRunAction: runPipeline } = await import(
|
|
8657
8661
|
/* @vite-ignore */
|
|
8658
|
-
"../run-
|
|
8662
|
+
"../run-CF0qLJAV.js"
|
|
8659
8663
|
);
|
|
8660
8664
|
const runStoryFn = async (opts) => {
|
|
8661
8665
|
const exitCode = await runPipeline({
|
|
@@ -12988,7 +12992,7 @@ function computeReportVerdict(summary, runStatus) {
|
|
|
12988
12992
|
if (summary.total === 0) return "NO STORIES RUN";
|
|
12989
12993
|
return "ALL PASSED";
|
|
12990
12994
|
}
|
|
12991
|
-
function renderHuman(output, manifest) {
|
|
12995
|
+
function renderHuman$1(output, manifest) {
|
|
12992
12996
|
const lines = [];
|
|
12993
12997
|
const { runId, summary, stories, escalations, cost, duration, halts } = output;
|
|
12994
12998
|
const durationStr = duration.wall_clock_ms != null ? formatDurationMs(duration.wall_clock_ms) : "unknown";
|
|
@@ -13411,7 +13415,7 @@ async function runReportAction(options) {
|
|
|
13411
13415
|
output.ac_traceability = acTraceability;
|
|
13412
13416
|
}
|
|
13413
13417
|
if (outputFormat === "json") process.stdout.write(renderJson(output) + "\n");
|
|
13414
|
-
else process.stdout.write(renderHuman(output, manifest));
|
|
13418
|
+
else process.stdout.write(renderHuman$1(output, manifest));
|
|
13415
13419
|
return 0;
|
|
13416
13420
|
}
|
|
13417
13421
|
/**
|
|
@@ -13435,6 +13439,53 @@ function registerReportCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
13435
13439
|
});
|
|
13436
13440
|
}
|
|
13437
13441
|
|
|
13442
|
+
//#endregion
|
|
13443
|
+
//#region src/cli/commands/acceptance.ts
|
|
13444
|
+
const ACCEPTANCE_EXIT_SUCCESS = 0;
|
|
13445
|
+
const ACCEPTANCE_EXIT_ERROR = 1;
|
|
13446
|
+
function summarizeRegistry(registry) {
|
|
13447
|
+
const journeyCount = registry.journeys.length;
|
|
13448
|
+
const endStateCount = registry.journeys.reduce((n, j) => n + j.end_states.length, 0);
|
|
13449
|
+
const criticalCount = registry.journeys.filter((j) => j.criticality === "critical").length;
|
|
13450
|
+
return `journey registry v${String(registry.version)}: ${String(journeyCount)} journeys (${String(criticalCount)} critical), ${String(endStateCount)} end-states`;
|
|
13451
|
+
}
|
|
13452
|
+
/** Render a load result for humans. Returns the exit code. */
|
|
13453
|
+
function renderHuman(result, source) {
|
|
13454
|
+
switch (result.status) {
|
|
13455
|
+
case "ok":
|
|
13456
|
+
process.stdout.write(`OK — ${summarizeRegistry(result.registry)} (${source})\n`);
|
|
13457
|
+
for (const journey of result.registry.journeys) process.stdout.write(` ${journey.id} [${journey.criticality}] ${journey.title} — ${String(journey.end_states.length)} end-state(s), surfaces: ${journey.surfaces.join(", ")}\n`);
|
|
13458
|
+
return ACCEPTANCE_EXIT_SUCCESS;
|
|
13459
|
+
case "absent":
|
|
13460
|
+
process.stdout.write(`NO REGISTRY — ${JOURNEY_REGISTRY_PATH} not found (${source}).\nAuthor it at planning time; see the acceptance-gate design brief for the schema.\n`);
|
|
13461
|
+
return ACCEPTANCE_EXIT_ERROR;
|
|
13462
|
+
case "invalid":
|
|
13463
|
+
process.stdout.write(`INVALID — ${JOURNEY_REGISTRY_PATH} failed validation (${source}):\n`);
|
|
13464
|
+
for (const issue of result.issues) process.stdout.write(` ${issue.path}: ${issue.message}\n`);
|
|
13465
|
+
return ACCEPTANCE_EXIT_ERROR;
|
|
13466
|
+
case "error":
|
|
13467
|
+
process.stdout.write(`ERROR — could not read the registry (${source}): ${result.message}\n`);
|
|
13468
|
+
return ACCEPTANCE_EXIT_ERROR;
|
|
13469
|
+
}
|
|
13470
|
+
}
|
|
13471
|
+
function registerAcceptanceCommand(program, version) {
|
|
13472
|
+
const acceptanceCmd = program.command("acceptance").description("Acceptance Gate — journey registry tools (the missing sprint demo)");
|
|
13473
|
+
acceptanceCmd.command("validate").description(`Lint ${JOURNEY_REGISTRY_PATH} with actionable, pathed errors`).option("--ref <ref>", "validate the COMMITTED registry at a git ref (trusted-tree read) instead of the working tree").option("--output-format <format>", "Output format: text (default) or json", "text").action(async (opts) => {
|
|
13474
|
+
const projectRoot = process.cwd();
|
|
13475
|
+
const source = opts.ref !== void 0 ? `committed @ ${opts.ref}` : "working tree";
|
|
13476
|
+
const result = opts.ref !== void 0 ? await loadJourneyRegistryFromTrustedTree(projectRoot, opts.ref) : await loadJourneyRegistryFromFile(projectRoot);
|
|
13477
|
+
if (opts.outputFormat === "json") {
|
|
13478
|
+
const output = buildJsonOutput("substrate acceptance validate", {
|
|
13479
|
+
source,
|
|
13480
|
+
...result
|
|
13481
|
+
}, version);
|
|
13482
|
+
process.stdout.write(JSON.stringify(output, null, 2) + "\n");
|
|
13483
|
+
process.exit(result.status === "ok" ? ACCEPTANCE_EXIT_SUCCESS : ACCEPTANCE_EXIT_ERROR);
|
|
13484
|
+
}
|
|
13485
|
+
process.exit(renderHuman(result, source));
|
|
13486
|
+
});
|
|
13487
|
+
}
|
|
13488
|
+
|
|
13438
13489
|
//#endregion
|
|
13439
13490
|
//#region src/cli/index.ts
|
|
13440
13491
|
process.setMaxListeners(20);
|
|
@@ -13494,6 +13545,7 @@ async function createProgram() {
|
|
|
13494
13545
|
registerMonitorCommand(program, version);
|
|
13495
13546
|
registerMergeCommand(program);
|
|
13496
13547
|
registerWorktreesCommand(program, version);
|
|
13548
|
+
registerAcceptanceCommand(program, version);
|
|
13497
13549
|
registerBrainstormCommand(program, version);
|
|
13498
13550
|
registerExportCommand(program, version);
|
|
13499
13551
|
registerIngestEpicCommand(program);
|
|
@@ -69,7 +69,17 @@ The Recovery Engine runs a 3-tier auto-fix ladder before any halt — Tier A ret
|
|
|
69
69
|
|
|
70
70
|
### Per-Story Worktree Behavior
|
|
71
71
|
|
|
72
|
-
Each dispatched story runs in
|
|
72
|
+
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).
|
|
73
|
+
|
|
74
|
+
### Finalization — how verified work integrates
|
|
75
|
+
|
|
76
|
+
`finalization.mode` in `.substrate/config.yaml` (or `--finalization <mode>` per run):
|
|
77
|
+
|
|
78
|
+
- **`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.
|
|
79
|
+
- **`branch`**: verified work stays on `substrate/story-<key>` — nothing self-merges; the branch is the deliverable. The safe brownfield mode.
|
|
80
|
+
- **`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.
|
|
81
|
+
|
|
82
|
+
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).
|
|
73
83
|
|
|
74
84
|
### Key Commands
|
|
75
85
|
|
|
@@ -79,6 +89,7 @@ Each dispatched story runs in `.substrate-worktrees/story-<key>` on its own bran
|
|
|
79
89
|
| `substrate run --halt-on <severity>` | Halt policy: `all` / `critical` (default) / `none` |
|
|
80
90
|
| `substrate run --non-interactive` | Suppress stdin prompts |
|
|
81
91
|
| `substrate run --no-worktree` | Disable per-story git worktrees (use for submodules or bare repos) |
|
|
92
|
+
| `substrate run --finalization <mode>` | Integration mode: `merge` (default) / `branch` (never self-merge) / `pr` |
|
|
82
93
|
| `substrate report [--run <id\|latest>]` | Per-run completion report |
|
|
83
94
|
| `substrate report --verify-ac` | Append AC-to-Test traceability matrix |
|
|
84
95
|
| `substrate reconcile-from-disk [--dry-run] [--yes]` | Path A reconciliation when tree is coherent |
|
|
@@ -69,7 +69,17 @@ The Recovery Engine runs a 3-tier auto-fix ladder before any halt — Tier A ret
|
|
|
69
69
|
|
|
70
70
|
### Per-Story Worktree Behavior
|
|
71
71
|
|
|
72
|
-
Each dispatched story runs in
|
|
72
|
+
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).
|
|
73
|
+
|
|
74
|
+
### Finalization — how verified work integrates
|
|
75
|
+
|
|
76
|
+
`finalization.mode` in `.substrate/config.yaml` (or `--finalization <mode>` per run):
|
|
77
|
+
|
|
78
|
+
- **`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.
|
|
79
|
+
- **`branch`**: verified work stays on `substrate/story-<key>` — nothing self-merges; the branch is the deliverable. The safe brownfield mode.
|
|
80
|
+
- **`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.
|
|
81
|
+
|
|
82
|
+
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).
|
|
73
83
|
|
|
74
84
|
### Key Commands
|
|
75
85
|
|
|
@@ -79,6 +89,7 @@ Each dispatched story runs in `.substrate-worktrees/story-<key>` on its own bran
|
|
|
79
89
|
| `substrate run --halt-on <severity>` | Halt policy: `all` / `critical` (default) / `none` |
|
|
80
90
|
| `substrate run --non-interactive` | Suppress stdin prompts |
|
|
81
91
|
| `substrate run --no-worktree` | Disable per-story git worktrees (use for submodules or bare repos) |
|
|
92
|
+
| `substrate run --finalization <mode>` | Integration mode: `merge` (default) / `branch` (never self-merge) / `pr` |
|
|
82
93
|
| `substrate report [--run <id\|latest>]` | Per-run completion report |
|
|
83
94
|
| `substrate report --verify-ac` | Append AC-to-Test traceability matrix |
|
|
84
95
|
| `substrate reconcile-from-disk [--dry-run] [--yes]` | Path A reconciliation when tree is coherent |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-
|
|
1
|
+
import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-DvgJ7jj_.js";
|
|
2
2
|
import "./logger-KeHncl-f.js";
|
|
3
3
|
import "./dist-BkhYgJWo.js";
|
|
4
|
-
import "./manifest-read-
|
|
4
|
+
import "./manifest-read-DyyFPrJH.js";
|
|
5
5
|
import "./work-graph-repository-DZyJv5pV.js";
|
|
6
6
|
import "./decisions-CzSIEeGP.js";
|
|
7
7
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createLogger } from "./logger-KeHncl-f.js";
|
|
2
2
|
import { DoltClient, DoltQueryError, createDatabaseAdapter$1 as createDatabaseAdapter, getLatestRun, getPipelineRunById, initSchema } from "./dist-BkhYgJWo.js";
|
|
3
|
-
import { resolveMainRepoRoot, resolveRunManifest } from "./manifest-read-
|
|
3
|
+
import { resolveMainRepoRoot, resolveRunManifest } from "./manifest-read-DyyFPrJH.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-
|
|
1003
|
+
//# sourceMappingURL=health-DvgJ7jj_.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createLogger } from "./logger-KeHncl-f.js";
|
|
2
|
-
import { readCurrentRunId, resolveMainRepoRoot } from "./manifest-read-
|
|
2
|
+
import { readCurrentRunId, resolveMainRepoRoot } from "./manifest-read-DyyFPrJH.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-
|
|
183
|
+
//# sourceMappingURL=interactive-prompt-CPF2GTvV.js.map
|
|
@@ -3087,7 +3087,21 @@ function executeProbeOnHost(probe, options = {}) {
|
|
|
3087
3087
|
* registry, os. Open string so novel names don't cause silent skips.
|
|
3088
3088
|
*/
|
|
3089
3089
|
const ExternalStateDependencySchema = z.string();
|
|
3090
|
-
|
|
3090
|
+
/**
|
|
3091
|
+
* Journey tags (acceptance-gate story A0.2): `journeys: [UJ-x]` declares which
|
|
3092
|
+
* registry journeys this story claims to deliver. Tolerant by design — a bare
|
|
3093
|
+
* string coerces to a one-element list, and any non-coercible shape falls back
|
|
3094
|
+
* to `[]` via `.catch` WITHOUT failing the whole frontmatter block (a malformed
|
|
3095
|
+
* journey tag must never silently drop `external_state_dependencies`).
|
|
3096
|
+
* Untagged is legal: the epic-close coverage invariant (A0.3) is the backstop;
|
|
3097
|
+
* tags only buy earlier detection. Unknown-id validation happens against the
|
|
3098
|
+
* trusted-tree registry in the create-story workflow, not here.
|
|
3099
|
+
*/
|
|
3100
|
+
const JourneyTagsSchema = z.union([z.string().transform((s) => [s]), z.array(z.string())]).catch([]);
|
|
3101
|
+
const StoryFrontmatterSchema = z.object({
|
|
3102
|
+
external_state_dependencies: z.array(ExternalStateDependencySchema).optional().default([]),
|
|
3103
|
+
journeys: JourneyTagsSchema.optional().default([])
|
|
3104
|
+
});
|
|
3091
3105
|
/**
|
|
3092
3106
|
* Parse the optional YAML frontmatter block from a story artifact.
|
|
3093
3107
|
*
|
|
@@ -4963,6 +4977,210 @@ async function runAcTraceabilityCheck(input) {
|
|
|
4963
4977
|
};
|
|
4964
4978
|
}
|
|
4965
4979
|
|
|
4980
|
+
//#endregion
|
|
4981
|
+
//#region packages/sdlc/dist/acceptance/registry.js
|
|
4982
|
+
/** Repo-relative path of the registry — the single canonical location. */
|
|
4983
|
+
const JOURNEY_REGISTRY_PATH = ".substrate/acceptance/journeys.yaml";
|
|
4984
|
+
const JourneySurfaceSchema = z.enum([
|
|
4985
|
+
"email",
|
|
4986
|
+
"cli",
|
|
4987
|
+
"file",
|
|
4988
|
+
"web"
|
|
4989
|
+
]);
|
|
4990
|
+
const JourneyEndStateSchema = z.object({
|
|
4991
|
+
id: z.string().min(1, "end-state id must be a non-empty string"),
|
|
4992
|
+
given: z.string().min(1, "given must be a non-empty string"),
|
|
4993
|
+
walk: z.string().min(1, "walk must be a non-empty string"),
|
|
4994
|
+
then: z.string().min(1, "then must be a non-empty string")
|
|
4995
|
+
});
|
|
4996
|
+
const JourneySchema = z.object({
|
|
4997
|
+
id: z.string().min(1, "journey id must be a non-empty string"),
|
|
4998
|
+
title: z.string().min(1, "title must be a non-empty string"),
|
|
4999
|
+
criticality: z.enum(["critical", "standard"]),
|
|
5000
|
+
surfaces: z.array(JourneySurfaceSchema).min(1, "a journey must declare at least one surface"),
|
|
5001
|
+
epic: z.number().int().positive().optional(),
|
|
5002
|
+
end_states: z.array(JourneyEndStateSchema).min(1, "a journey must declare at least one end_state — a journey with none is unjudgeable")
|
|
5003
|
+
});
|
|
5004
|
+
const JourneyRegistrySchema = z.object({
|
|
5005
|
+
version: z.number().int().positive("version must be a positive integer"),
|
|
5006
|
+
journeys: z.array(JourneySchema)
|
|
5007
|
+
}).superRefine((registry, ctx) => {
|
|
5008
|
+
const seenJourneyIds = new Map();
|
|
5009
|
+
registry.journeys.forEach((journey, i) => {
|
|
5010
|
+
const firstIndex = seenJourneyIds.get(journey.id);
|
|
5011
|
+
if (firstIndex !== void 0) ctx.addIssue({
|
|
5012
|
+
code: "custom",
|
|
5013
|
+
path: [
|
|
5014
|
+
"journeys",
|
|
5015
|
+
i,
|
|
5016
|
+
"id"
|
|
5017
|
+
],
|
|
5018
|
+
message: `duplicate journey id "${journey.id}" (first declared at journeys[${firstIndex}])`
|
|
5019
|
+
});
|
|
5020
|
+
else seenJourneyIds.set(journey.id, i);
|
|
5021
|
+
const seenEndStateIds = new Map();
|
|
5022
|
+
journey.end_states.forEach((endState, j) => {
|
|
5023
|
+
const firstEs = seenEndStateIds.get(endState.id);
|
|
5024
|
+
if (firstEs !== void 0) ctx.addIssue({
|
|
5025
|
+
code: "custom",
|
|
5026
|
+
path: [
|
|
5027
|
+
"journeys",
|
|
5028
|
+
i,
|
|
5029
|
+
"end_states",
|
|
5030
|
+
j,
|
|
5031
|
+
"id"
|
|
5032
|
+
],
|
|
5033
|
+
message: `duplicate end-state id "${endState.id}" in journey "${journey.id}" (first declared at end_states[${firstEs}])`
|
|
5034
|
+
});
|
|
5035
|
+
else seenEndStateIds.set(endState.id, j);
|
|
5036
|
+
});
|
|
5037
|
+
});
|
|
5038
|
+
});
|
|
5039
|
+
function zodErrorToValidationIssues(error) {
|
|
5040
|
+
return error.issues.map((issue) => ({
|
|
5041
|
+
path: issue.path.length > 0 ? issue.path.map(String).join(".") : "(root)",
|
|
5042
|
+
message: issue.message
|
|
5043
|
+
}));
|
|
5044
|
+
}
|
|
5045
|
+
/**
|
|
5046
|
+
* Parse and validate registry YAML content.
|
|
5047
|
+
*
|
|
5048
|
+
* Never throws: malformed YAML, non-object documents, and schema violations
|
|
5049
|
+
* all come back as named, pathed issues.
|
|
5050
|
+
*/
|
|
5051
|
+
function parseJourneyRegistry(yamlContent) {
|
|
5052
|
+
let doc;
|
|
5053
|
+
try {
|
|
5054
|
+
doc = load(yamlContent);
|
|
5055
|
+
} catch (err) {
|
|
5056
|
+
const message = err instanceof YAMLException ? err.message : String(err);
|
|
5057
|
+
return {
|
|
5058
|
+
ok: false,
|
|
5059
|
+
issues: [{
|
|
5060
|
+
path: "(root)",
|
|
5061
|
+
message: `malformed YAML: ${message}`
|
|
5062
|
+
}]
|
|
5063
|
+
};
|
|
5064
|
+
}
|
|
5065
|
+
if (doc === null || doc === void 0) return {
|
|
5066
|
+
ok: false,
|
|
5067
|
+
issues: [{
|
|
5068
|
+
path: "(root)",
|
|
5069
|
+
message: "registry file is empty"
|
|
5070
|
+
}]
|
|
5071
|
+
};
|
|
5072
|
+
const result = JourneyRegistrySchema.safeParse(doc);
|
|
5073
|
+
if (!result.success) return {
|
|
5074
|
+
ok: false,
|
|
5075
|
+
issues: zodErrorToValidationIssues(result.error)
|
|
5076
|
+
};
|
|
5077
|
+
return {
|
|
5078
|
+
ok: true,
|
|
5079
|
+
registry: result.data
|
|
5080
|
+
};
|
|
5081
|
+
}
|
|
5082
|
+
|
|
5083
|
+
//#endregion
|
|
5084
|
+
//#region packages/sdlc/dist/acceptance/loader.js
|
|
5085
|
+
/** stderr shapes that mean "the path is not in the tree at this ref" (absent, not an error). */
|
|
5086
|
+
const GIT_SHOW_ABSENT_PATTERNS = [
|
|
5087
|
+
/does not exist in/i,
|
|
5088
|
+
/exists on disk, but not in/i,
|
|
5089
|
+
/path .* does not exist/i
|
|
5090
|
+
];
|
|
5091
|
+
/** Array-argv spawn — no shell, no interpolation, injection-safe by construction. */
|
|
5092
|
+
function runGitShow(repoRoot, ref, relPath) {
|
|
5093
|
+
return new Promise((resolve$2) => {
|
|
5094
|
+
let stdout = "";
|
|
5095
|
+
let stderr = "";
|
|
5096
|
+
const proc = spawn("git", ["show", `${ref}:${relPath}`], {
|
|
5097
|
+
cwd: repoRoot,
|
|
5098
|
+
stdio: [
|
|
5099
|
+
"ignore",
|
|
5100
|
+
"pipe",
|
|
5101
|
+
"pipe"
|
|
5102
|
+
]
|
|
5103
|
+
});
|
|
5104
|
+
proc.stdout?.on("data", (chunk) => {
|
|
5105
|
+
stdout += chunk.toString("utf-8");
|
|
5106
|
+
});
|
|
5107
|
+
proc.stderr?.on("data", (chunk) => {
|
|
5108
|
+
stderr += chunk.toString("utf-8");
|
|
5109
|
+
});
|
|
5110
|
+
proc.on("error", (err) => {
|
|
5111
|
+
resolve$2({
|
|
5112
|
+
code: null,
|
|
5113
|
+
stdout,
|
|
5114
|
+
stderr,
|
|
5115
|
+
spawnError: err.message
|
|
5116
|
+
});
|
|
5117
|
+
});
|
|
5118
|
+
proc.on("close", (code) => {
|
|
5119
|
+
resolve$2({
|
|
5120
|
+
code,
|
|
5121
|
+
stdout,
|
|
5122
|
+
stderr
|
|
5123
|
+
});
|
|
5124
|
+
});
|
|
5125
|
+
});
|
|
5126
|
+
}
|
|
5127
|
+
/**
|
|
5128
|
+
* Load the journey registry from the trusted main tree at `ref`.
|
|
5129
|
+
*
|
|
5130
|
+
* @param repoRoot absolute path of the TRUSTED repo root (the main tree, not a worktree)
|
|
5131
|
+
* @param ref git ref to read at — the dispatch snapshot ref (defaults to HEAD)
|
|
5132
|
+
*/
|
|
5133
|
+
async function loadJourneyRegistryFromTrustedTree(repoRoot, ref = "HEAD") {
|
|
5134
|
+
const result = await runGitShow(repoRoot, ref, JOURNEY_REGISTRY_PATH);
|
|
5135
|
+
if (result.spawnError !== void 0) return {
|
|
5136
|
+
status: "error",
|
|
5137
|
+
message: `git show could not be spawned: ${result.spawnError}`
|
|
5138
|
+
};
|
|
5139
|
+
if (result.code !== 0) {
|
|
5140
|
+
if (GIT_SHOW_ABSENT_PATTERNS.some((p) => p.test(result.stderr))) return { status: "absent" };
|
|
5141
|
+
return {
|
|
5142
|
+
status: "error",
|
|
5143
|
+
message: `git show ${ref}:${JOURNEY_REGISTRY_PATH} failed (exit ${String(result.code)}): ${result.stderr.trim()}`
|
|
5144
|
+
};
|
|
5145
|
+
}
|
|
5146
|
+
const parsed = parseJourneyRegistry(result.stdout);
|
|
5147
|
+
if (!parsed.ok) return {
|
|
5148
|
+
status: "invalid",
|
|
5149
|
+
issues: parsed.issues
|
|
5150
|
+
};
|
|
5151
|
+
return {
|
|
5152
|
+
status: "ok",
|
|
5153
|
+
registry: parsed.registry
|
|
5154
|
+
};
|
|
5155
|
+
}
|
|
5156
|
+
/**
|
|
5157
|
+
* Filesystem read for OPERATOR LINT ONLY (`substrate acceptance validate`).
|
|
5158
|
+
* Gate/judge code paths must use `loadJourneyRegistryFromTrustedTree`.
|
|
5159
|
+
*/
|
|
5160
|
+
async function loadJourneyRegistryFromFile(projectRoot) {
|
|
5161
|
+
const filePath = join$1(projectRoot, JOURNEY_REGISTRY_PATH);
|
|
5162
|
+
let content;
|
|
5163
|
+
try {
|
|
5164
|
+
content = await readFile$1(filePath, "utf-8");
|
|
5165
|
+
} catch (err) {
|
|
5166
|
+
const code = err.code;
|
|
5167
|
+
if (code === "ENOENT" || code === "ENOTDIR") return { status: "absent" };
|
|
5168
|
+
return {
|
|
5169
|
+
status: "error",
|
|
5170
|
+
message: `could not read ${filePath}: ${String(err)}`
|
|
5171
|
+
};
|
|
5172
|
+
}
|
|
5173
|
+
const parsed = parseJourneyRegistry(content);
|
|
5174
|
+
if (!parsed.ok) return {
|
|
5175
|
+
status: "invalid",
|
|
5176
|
+
issues: parsed.issues
|
|
5177
|
+
};
|
|
5178
|
+
return {
|
|
5179
|
+
status: "ok",
|
|
5180
|
+
registry: parsed.registry
|
|
5181
|
+
};
|
|
5182
|
+
}
|
|
5183
|
+
|
|
4966
5184
|
//#endregion
|
|
4967
5185
|
//#region packages/sdlc/dist/run-model/cli-flags.js
|
|
4968
5186
|
/**
|
|
@@ -6381,5 +6599,5 @@ async function resolveRunManifest(dbRoot, runId) {
|
|
|
6381
6599
|
}
|
|
6382
6600
|
|
|
6383
6601
|
//#endregion
|
|
6384
|
-
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 };
|
|
6385
|
-
//# sourceMappingURL=manifest-read-
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../../logger-KeHncl-f.js";
|
|
2
2
|
import "../../dist-BkhYgJWo.js";
|
|
3
|
-
import "../../manifest-read-
|
|
4
|
-
import { runInteractivePrompt } from "../../interactive-prompt-
|
|
3
|
+
import "../../manifest-read-DyyFPrJH.js";
|
|
4
|
+
import { runInteractivePrompt } from "../../interactive-prompt-CPF2GTvV.js";
|
|
5
5
|
|
|
6
6
|
export { runInteractivePrompt };
|
|
@@ -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-
|
|
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";
|
|
2
2
|
import { createLogger } from "./logger-KeHncl-f.js";
|
|
3
3
|
import { TypedEventBusImpl, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning, sleep } from "./helpers-CElYrONe.js";
|
|
4
4
|
import { ADVISORY_NOTES, 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, parseRuntimeProbes, renderFindings, resolveGraphPath, resolveMainRepoRoot, runAcTraceabilityCheck, runStaleVerificationRecovery } from "./manifest-read-
|
|
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";
|
|
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-
|
|
8
|
+
import { runInteractivePrompt } from "./interactive-prompt-CPF2GTvV.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); 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+."
|
|
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+."
|
|
955
955
|
},
|
|
956
956
|
{
|
|
957
957
|
name: "cycles",
|
|
@@ -6138,6 +6138,21 @@ async function runCreateStory(deps, params) {
|
|
|
6138
6138
|
} catch {}
|
|
6139
6139
|
const archConstraintsContent = await getArchConstraints$3(deps);
|
|
6140
6140
|
const storyTemplateContent = await getStoryTemplate(deps);
|
|
6141
|
+
const trustedRoot = deps.parentProjectRoot ?? deps.projectRoot;
|
|
6142
|
+
let journeyRegistry;
|
|
6143
|
+
if (trustedRoot !== void 0) {
|
|
6144
|
+
const registryLoad = await loadJourneyRegistryFromTrustedTree(trustedRoot);
|
|
6145
|
+
if (registryLoad.status === "ok" && registryLoad.registry.journeys.length > 0) journeyRegistry = registryLoad.registry;
|
|
6146
|
+
else if (registryLoad.status === "invalid") logger$21.warn({
|
|
6147
|
+
storyKey,
|
|
6148
|
+
issues: registryLoad.issues
|
|
6149
|
+
}, "A0.2: journey registry present but invalid — stories will be untagged (registry validity is escalated at epic close)");
|
|
6150
|
+
else if (registryLoad.status === "error") logger$21.warn({
|
|
6151
|
+
storyKey,
|
|
6152
|
+
error: registryLoad.message
|
|
6153
|
+
}, "A0.2: journey registry read failed — stories will be untagged");
|
|
6154
|
+
}
|
|
6155
|
+
const journeyRegistryContent = journeyRegistry !== void 0 ? journeyRegistry.journeys.map((j$1) => `- ${j$1.id} [${j$1.criticality}]: ${j$1.title}`).join("\n") : "";
|
|
6141
6156
|
const { prompt, tokenCount, truncated } = assemblePrompt(template, [
|
|
6142
6157
|
{
|
|
6143
6158
|
name: "story_key",
|
|
@@ -6182,6 +6197,15 @@ async function runCreateStory(deps, params) {
|
|
|
6182
6197
|
name: "prior_drift_feedback",
|
|
6183
6198
|
content: "",
|
|
6184
6199
|
priority: "optional"
|
|
6200
|
+
}],
|
|
6201
|
+
...journeyRegistryContent.length > 0 ? [{
|
|
6202
|
+
name: "journey_registry",
|
|
6203
|
+
content: journeyRegistryContent,
|
|
6204
|
+
priority: "required"
|
|
6205
|
+
}] : [{
|
|
6206
|
+
name: "journey_registry",
|
|
6207
|
+
content: "",
|
|
6208
|
+
priority: "optional"
|
|
6185
6209
|
}]
|
|
6186
6210
|
], TOKEN_CEILING);
|
|
6187
6211
|
logger$21.debug({
|
|
@@ -6295,6 +6319,38 @@ async function runCreateStory(deps, params) {
|
|
|
6295
6319
|
};
|
|
6296
6320
|
}
|
|
6297
6321
|
const parsed = parseResult.data;
|
|
6322
|
+
if (journeyRegistry !== void 0 && parsed.story_file !== void 0) {
|
|
6323
|
+
let artifactContent;
|
|
6324
|
+
try {
|
|
6325
|
+
artifactContent = await readFile$1(parsed.story_file, "utf-8");
|
|
6326
|
+
} catch {
|
|
6327
|
+
artifactContent = void 0;
|
|
6328
|
+
}
|
|
6329
|
+
if (artifactContent !== void 0) {
|
|
6330
|
+
const taggedJourneys = parseStoryFrontmatter(artifactContent).journeys;
|
|
6331
|
+
const knownIds = new Set(journeyRegistry.journeys.map((j$1) => j$1.id));
|
|
6332
|
+
const unknown = taggedJourneys.filter((id) => !knownIds.has(id));
|
|
6333
|
+
if (unknown.length > 0) {
|
|
6334
|
+
const details = `story artifact declares unknown journey id(s): ${unknown.join(", ")} — registry v${String(journeyRegistry.version)} defines: ${[...knownIds].join(", ")}`;
|
|
6335
|
+
logger$21.warn({
|
|
6336
|
+
epicId,
|
|
6337
|
+
storyKey,
|
|
6338
|
+
unknown
|
|
6339
|
+
}, "A0.2: journey tag validation failed");
|
|
6340
|
+
return {
|
|
6341
|
+
result: "failed",
|
|
6342
|
+
error: "schema_validation_failed",
|
|
6343
|
+
details,
|
|
6344
|
+
tokenUsage
|
|
6345
|
+
};
|
|
6346
|
+
}
|
|
6347
|
+
if (taggedJourneys.length > 0) logger$21.info({
|
|
6348
|
+
epicId,
|
|
6349
|
+
storyKey,
|
|
6350
|
+
journeys: taggedJourneys
|
|
6351
|
+
}, "A0.2: story tagged with journeys");
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6298
6354
|
logger$21.info({
|
|
6299
6355
|
epicId,
|
|
6300
6356
|
storyKey,
|
|
@@ -48092,4 +48148,4 @@ function registerRunCommand(program, version = "0.0.0", projectRoot = process.cw
|
|
|
48092
48148
|
|
|
48093
48149
|
//#endregion
|
|
48094
48150
|
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 };
|
|
48095
|
-
//# sourceMappingURL=run-
|
|
48151
|
+
//# sourceMappingURL=run-CAmBrDky.js.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import "./health-
|
|
1
|
+
import "./health-DvgJ7jj_.js";
|
|
2
2
|
import "./logger-KeHncl-f.js";
|
|
3
3
|
import "./helpers-CElYrONe.js";
|
|
4
4
|
import "./dist-BkhYgJWo.js";
|
|
5
|
-
import "./manifest-read-
|
|
6
|
-
import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-
|
|
5
|
+
import "./manifest-read-DyyFPrJH.js";
|
|
6
|
+
import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-CAmBrDky.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-
|
|
11
|
+
import "./interactive-prompt-CPF2GTvV.js";
|
|
12
12
|
import "./recovery-engine-BKGBeBnW.js";
|
|
13
13
|
|
|
14
14
|
export { runRunAction };
|
package/package.json
CHANGED
|
@@ -191,6 +191,28 @@ Suggested values (use as many as apply):
|
|
|
191
191
|
|
|
192
192
|
**Omit the field (or leave it empty) only for purely-algorithmic modules** where you also omit `## Runtime Probes` — parse/format/sort/score/calculate transforms, type-only refactors, documentation edits, build-config changes, and unit-test-only stories.
|
|
193
193
|
|
|
194
|
+
### Journey tags (acceptance gate)
|
|
195
|
+
|
|
196
|
+
This project's journey registry (empty when the project has none):
|
|
197
|
+
|
|
198
|
+
{{journey_registry}}
|
|
199
|
+
|
|
200
|
+
If the registry above is non-empty, decide which journeys this story's acceptance criteria deliver or materially advance, and declare them in the SAME YAML frontmatter block at the top of the story file:
|
|
201
|
+
|
|
202
|
+
```text
|
|
203
|
+
---
|
|
204
|
+
external_state_dependencies:
|
|
205
|
+
- subprocess
|
|
206
|
+
journeys:
|
|
207
|
+
- UJ-2
|
|
208
|
+
---
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Rules:
|
|
212
|
+
- Use ONLY journey ids that appear in the registry above — an unknown id fails the story artifact's schema validation.
|
|
213
|
+
- Tag a journey when the story wires, extends, or completes any part of that journey's user-facing path — not merely because it touches nearby code.
|
|
214
|
+
- If no registered journey applies (or the registry above is empty), OMIT the `journeys:` field entirely. Untagged stories are legal; unclaimed journeys are audited separately at epic close.
|
|
215
|
+
|
|
194
216
|
### Probe YAML shape
|
|
195
217
|
|
|
196
218
|
Declare probes as a YAML list inside a single fenced `yaml` block directly under the `## Runtime Probes` heading. Each entry has this shape:
|