substrate-ai 0.20.4 → 0.20.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { FileStateStore, RunManifest, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest } from "../health-CB48LF0t.js";
2
+ import { FileStateStore, RunManifest, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest } from "../health-DHLR9Iz1.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, IngestionServer, MonitorDatabaseImpl, OPERATIONAL_FINDING, PartialGlobalSettingsSchema, PartialProviderConfigSchema, ProvidersSchema, RoutingRecommender, STORY_METRICS, TelemetryConfigSchema, addTokenUsage, aggregateTokenUsageForRun, checkDoltInstalled, compareRunMetrics, createAmendmentRun, createConfigSystem, createDecision, createDoltClient, createPipelineRun, getActiveDecisions, getAllCostEntriesFiltered, getBaselineRunMetrics, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestCompletedRun, getLatestRun, getPipelineRunById, getPlanningCostTotal, getRetryableEscalations, getRunMetrics, getRunningPipelineRuns, getSessionCostSummary, getSessionCostSummaryFiltered, getStoryMetricsForRun, getTokenUsageSummary, incrementRunRestarts, initSchema, initializeDolt, listRunMetrics, loadParentRunDecisions, supersedeDecision, tagRunAsBaseline, updatePipelineRun } from "../dist-srr3BfCc.js";
6
6
  import "../adapter-registry-DXLMTmfD.js";
7
- import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-BHKqyFFM.js";
7
+ import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-ofO9AWFc.js";
8
8
  import "../errors-CSTQNabo.js";
9
9
  import "../routing-CcBOCuC9.js";
10
10
  import "../decisions-C0pz9Clx.js";
@@ -3665,7 +3665,7 @@ async function runStatusAction(options) {
3665
3665
  logger$12.debug({ err }, "Work graph query failed, continuing without work graph data");
3666
3666
  }
3667
3667
  if (run === void 0) {
3668
- const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-DIMI36KN.js");
3668
+ const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-CQTK6ltK.js");
3669
3669
  const substrateDirPath = join(projectRoot, ".substrate");
3670
3670
  const processInfo = inspectProcessTree$1({
3671
3671
  projectRoot,
@@ -5191,7 +5191,7 @@ async function runSupervisorAction(options, deps = {}) {
5191
5191
  await initSchema(expAdapter);
5192
5192
  const { runRunAction: runPipeline } = await import(
5193
5193
  /* @vite-ignore */
5194
- "../run-l5fLWKtI.js"
5194
+ "../run-s6bRK0LF.js"
5195
5195
  );
5196
5196
  const runStoryFn = async (opts) => {
5197
5197
  const exitCode = await runPipeline({
@@ -1,4 +1,4 @@
1
- import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-CB48LF0t.js";
1
+ import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-DHLR9Iz1.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./dist-srr3BfCc.js";
4
4
  import "./decisions-C0pz9Clx.js";
@@ -4,6 +4,7 @@ import { createRequire } from "module";
4
4
  import { dirname, join } from "path";
5
5
  import { readFile } from "fs/promises";
6
6
  import { EventEmitter } from "node:events";
7
+ import { YAMLException, load } from "js-yaml";
7
8
  import { existsSync, promises, readFileSync } from "node:fs";
8
9
  import { spawn, spawnSync } from "node:child_process";
9
10
  import { dirname as dirname$1, join as join$1, resolve as resolve$1 } from "node:path";
@@ -3259,7 +3260,7 @@ const MAX_OUTPUT_CHARS = 2e3;
3259
3260
  /** Per-stream tail size cap for structured findings (story 55-1 convention). */
3260
3261
  const TAIL_BYTES = 4 * 1024;
3261
3262
  /** Return the last N bytes of a UTF-8 string, sliced by string length for simplicity. */
3262
- function tail(text, bytes = TAIL_BYTES) {
3263
+ function tail$1(text, bytes = TAIL_BYTES) {
3263
3264
  return text.length <= bytes ? text : text.slice(text.length - bytes);
3264
3265
  }
3265
3266
  /**
@@ -3351,8 +3352,8 @@ var BuildCheck = class {
3351
3352
  severity: "error",
3352
3353
  message: `command exceeded ${BUILD_CHECK_TIMEOUT_MS}ms`,
3353
3354
  command: cmd,
3354
- stdoutTail: tail(stdout),
3355
- stderrTail: tail(stderr),
3355
+ stdoutTail: tail$1(stdout),
3356
+ stderrTail: tail$1(stderr),
3356
3357
  durationMs: duration
3357
3358
  }];
3358
3359
  resolve$2({
@@ -3379,8 +3380,8 @@ var BuildCheck = class {
3379
3380
  message: `build failed (exit ${code}): ${truncated}`,
3380
3381
  command: cmd,
3381
3382
  ...code !== null ? { exitCode: code } : {},
3382
- stdoutTail: tail(stdout),
3383
- stderrTail: tail(stderr),
3383
+ stdoutTail: tail$1(stdout),
3384
+ stderrTail: tail$1(stderr),
3384
3385
  durationMs: duration
3385
3386
  }];
3386
3387
  resolve$2({
@@ -3395,6 +3396,342 @@ var BuildCheck = class {
3395
3396
  }
3396
3397
  };
3397
3398
 
3399
+ //#endregion
3400
+ //#region packages/sdlc/dist/verification/probes/types.js
3401
+ /**
3402
+ * Execution sandbox for a runtime probe.
3403
+ *
3404
+ * - `host`: probe runs directly on the operator's machine. Explicit opt-in.
3405
+ * Cheapest; most dangerous. Authors choosing `host` acknowledge the probe
3406
+ * may touch host state (ports, systemd units, filesystem) and take
3407
+ * responsibility for cleanup.
3408
+ * - `twin`: probe runs inside an ephemeral sandbox brokered by the Digital
3409
+ * Twin subsystem (Epic 47). Twin integration is **deferred to Phase 3** —
3410
+ * probes with `sandbox: twin` currently emit a `probe-deferred` warn
3411
+ * finding rather than executing. Authors can declare twin-scoped probes
3412
+ * today and they will execute transparently once Phase 3 lands.
3413
+ */
3414
+ const RuntimeProbeSandboxSchema = z.enum(["host", "twin"]);
3415
+ /**
3416
+ * Default per-probe timeout in milliseconds. Matches the existing
3417
+ * BuildCheck ceiling (60 s) — deliberate, so probe timeouts are bounded
3418
+ * by the same policy the pipeline already uses for long-running checks.
3419
+ */
3420
+ const DEFAULT_PROBE_TIMEOUT_MS = 6e4;
3421
+ /** Hard upper bound on per-probe stdout/stderr retention (≤ 4 KiB — the
3422
+ * same convention as VerificationFinding.{stdoutTail,stderrTail}). */
3423
+ const PROBE_TAIL_BYTES = 4 * 1024;
3424
+ /**
3425
+ * Zod schema for one runtime probe declared in a story's
3426
+ * `## Runtime Probes` section.
3427
+ *
3428
+ * Required fields (`name`, `sandbox`, `command`) force authors to make
3429
+ * intent explicit — no silent defaults that could mask a miswritten probe.
3430
+ * Optional fields cover operational knobs with sensible fallbacks.
3431
+ */
3432
+ const RuntimeProbeSchema = z.object({
3433
+ name: z.string().min(1, "probe name is required"),
3434
+ sandbox: RuntimeProbeSandboxSchema,
3435
+ command: z.string().min(1, "probe command is required"),
3436
+ timeout_ms: z.number().int().positive().optional(),
3437
+ description: z.string().optional()
3438
+ });
3439
+ /** Zod schema for the full list (wrapping the per-probe schema). */
3440
+ const RuntimeProbeListSchema = z.array(RuntimeProbeSchema);
3441
+
3442
+ //#endregion
3443
+ //#region packages/sdlc/dist/verification/probes/parser.js
3444
+ const SECTION_HEADING = /^##\s+Runtime\s+Probes\s*$/i;
3445
+ /**
3446
+ * Return the raw text of the story's `## Runtime Probes` section (excluding
3447
+ * the heading line itself), or `undefined` if the section is not present.
3448
+ *
3449
+ * The section ends at the next `##` heading or end-of-file. Sub-headings
3450
+ * (`###`, `####`) remain part of the section body.
3451
+ */
3452
+ function extractRuntimeProbesSection(storyContent) {
3453
+ const lines = storyContent.split(/\r?\n/);
3454
+ const start = lines.findIndex((line) => SECTION_HEADING.test(line.trim()));
3455
+ if (start === -1) return void 0;
3456
+ let end = lines.length;
3457
+ for (let i = start + 1; i < lines.length; i += 1) if (/^##\s+\S/.test(lines[i] ?? "")) {
3458
+ end = i;
3459
+ break;
3460
+ }
3461
+ return lines.slice(start + 1, end).join("\n");
3462
+ }
3463
+ /**
3464
+ * Extract the body of the first ```yaml (or ```yml) fenced block in the
3465
+ * given section text. Returns `undefined` if no yaml fence is present.
3466
+ *
3467
+ * The opening fence is recognized case-insensitively and may carry an
3468
+ * arbitrary trailing info string (e.g. ```yaml title=...). The closing
3469
+ * fence is any line whose first non-whitespace run is exactly three
3470
+ * backticks.
3471
+ */
3472
+ function extractYamlFence(section) {
3473
+ const lines = section.split(/\r?\n/);
3474
+ let inside = false;
3475
+ let collected;
3476
+ for (const line of lines) {
3477
+ if (!inside) {
3478
+ if (/^\s*```\s*(yaml|yml)\b/i.test(line)) {
3479
+ inside = true;
3480
+ collected = [];
3481
+ }
3482
+ continue;
3483
+ }
3484
+ if (/^\s*```\s*$/.test(line)) return (collected ?? []).join("\n");
3485
+ collected?.push(line);
3486
+ }
3487
+ return void 0;
3488
+ }
3489
+ /**
3490
+ * Parse the `## Runtime Probes` section of a story's markdown content.
3491
+ *
3492
+ * Outcomes:
3493
+ * - section missing → { kind: 'absent' }
3494
+ * - section present, no yaml fence → { kind: 'invalid' }
3495
+ * - section present, yaml fence malformed → { kind: 'invalid' }
3496
+ * - section present, yaml root is not a list → { kind: 'invalid' }
3497
+ * - section present, entry fails RuntimeProbeSchema → { kind: 'invalid' }
3498
+ * - section present, yaml valid, all entries valid → { kind: 'parsed' }
3499
+ *
3500
+ * Duplicate names within a single story are surfaced as `invalid` so that
3501
+ * finding messages can unambiguously reference a probe by name.
3502
+ */
3503
+ function parseRuntimeProbes(storyContent) {
3504
+ const section = extractRuntimeProbesSection(storyContent);
3505
+ if (section === void 0) return { kind: "absent" };
3506
+ const yamlBody = extractYamlFence(section);
3507
+ if (yamlBody === void 0) return {
3508
+ kind: "invalid",
3509
+ error: "## Runtime Probes section is present but contains no terminated ```yaml fenced block"
3510
+ };
3511
+ let parsed;
3512
+ try {
3513
+ parsed = load(yamlBody) ?? [];
3514
+ } catch (err) {
3515
+ const detail = err instanceof YAMLException ? err.message : String(err);
3516
+ return {
3517
+ kind: "invalid",
3518
+ error: `YAML parse error: ${detail}`
3519
+ };
3520
+ }
3521
+ if (!Array.isArray(parsed)) return {
3522
+ kind: "invalid",
3523
+ error: `probe block root must be a YAML list; got ${typeof parsed}`
3524
+ };
3525
+ const validation = RuntimeProbeListSchema.safeParse(parsed);
3526
+ if (!validation.success) {
3527
+ const first = validation.error.issues[0];
3528
+ const path$1 = first?.path.join(".") ?? "";
3529
+ const message = first?.message ?? "schema validation failed";
3530
+ return {
3531
+ kind: "invalid",
3532
+ error: `probe list is malformed at ${path$1 || "<root>"}: ${message}`
3533
+ };
3534
+ }
3535
+ const probes = validation.data;
3536
+ const seen = new Set();
3537
+ for (const probe of probes) {
3538
+ if (seen.has(probe.name)) return {
3539
+ kind: "invalid",
3540
+ error: `duplicate probe name: ${probe.name}`
3541
+ };
3542
+ seen.add(probe.name);
3543
+ }
3544
+ return {
3545
+ kind: "parsed",
3546
+ probes
3547
+ };
3548
+ }
3549
+
3550
+ //#endregion
3551
+ //#region packages/sdlc/dist/verification/probes/executor.js
3552
+ /** Return the last N bytes of a UTF-8 string (sliced by length for simplicity). */
3553
+ function tail(text, bytes = PROBE_TAIL_BYTES) {
3554
+ return text.length <= bytes ? text : text.slice(text.length - bytes);
3555
+ }
3556
+ /**
3557
+ * Execute one probe on the host and return a structured ProbeResult.
3558
+ *
3559
+ * Behavior notes:
3560
+ * - The shell used is `/bin/sh -c '<probe.command>'` inside a detached
3561
+ * process group (so the entire tree is killed on timeout).
3562
+ * - stdout and stderr are captured independently; each is returned
3563
+ * tailed to PROBE_TAIL_BYTES (≤ 4 KiB) so published tarballs of the
3564
+ * run manifest stay small.
3565
+ * - Timeout defaults to `probe.timeout_ms ?? DEFAULT_PROBE_TIMEOUT_MS`
3566
+ * (60 s). When the timeout fires, the process group is SIGKILL'd and
3567
+ * the returned result has `outcome: 'timeout'`, `exitCode` undefined.
3568
+ * - Never throws. Spawn errors (e.g. exec format error) are returned as
3569
+ * `outcome: 'fail'` with exitCode -1 and the error message captured on
3570
+ * stderrTail, so the caller can emit a deterministic finding.
3571
+ */
3572
+ function executeProbeOnHost(probe, options = {}) {
3573
+ const timeoutMs = probe.timeout_ms ?? DEFAULT_PROBE_TIMEOUT_MS;
3574
+ const cwd = options.cwd ?? process.cwd();
3575
+ const env = options.env ?? process.env;
3576
+ const start = Date.now();
3577
+ return new Promise((resolve$2) => {
3578
+ let stdout = "";
3579
+ let stderr = "";
3580
+ let settled = false;
3581
+ const child = spawn(probe.command, [], {
3582
+ cwd,
3583
+ env,
3584
+ detached: true,
3585
+ shell: true,
3586
+ stdio: [
3587
+ "ignore",
3588
+ "pipe",
3589
+ "pipe"
3590
+ ]
3591
+ });
3592
+ const finalize = (result) => {
3593
+ if (settled) return;
3594
+ settled = true;
3595
+ resolve$2(result);
3596
+ };
3597
+ child.on("error", (err) => {
3598
+ finalize({
3599
+ outcome: "fail",
3600
+ command: probe.command,
3601
+ exitCode: -1,
3602
+ stdoutTail: tail(stdout),
3603
+ stderrTail: tail(stderr + (stderr.length > 0 && !stderr.endsWith("\n") ? "\n" : "") + `spawn error: ${err.message}\n`),
3604
+ durationMs: Date.now() - start
3605
+ });
3606
+ });
3607
+ child.stdout?.on("data", (chunk) => {
3608
+ stdout += chunk.toString();
3609
+ });
3610
+ child.stderr?.on("data", (chunk) => {
3611
+ stderr += chunk.toString();
3612
+ });
3613
+ const timeoutHandle = setTimeout(() => {
3614
+ try {
3615
+ if (child.pid !== void 0) process.kill(-child.pid, "SIGKILL");
3616
+ } catch {}
3617
+ finalize({
3618
+ outcome: "timeout",
3619
+ command: probe.command,
3620
+ stdoutTail: tail(stdout),
3621
+ stderrTail: tail(stderr),
3622
+ durationMs: Date.now() - start
3623
+ });
3624
+ }, timeoutMs);
3625
+ child.on("close", (code) => {
3626
+ clearTimeout(timeoutHandle);
3627
+ const duration = Date.now() - start;
3628
+ finalize({
3629
+ outcome: code === 0 ? "pass" : "fail",
3630
+ command: probe.command,
3631
+ ...code !== null ? { exitCode: code } : {},
3632
+ stdoutTail: tail(stdout),
3633
+ stderrTail: tail(stderr),
3634
+ durationMs: duration
3635
+ });
3636
+ });
3637
+ });
3638
+ }
3639
+
3640
+ //#endregion
3641
+ //#region packages/sdlc/dist/verification/checks/runtime-probe-check.js
3642
+ const CATEGORY_PARSE = "runtime-probe-parse-error";
3643
+ const CATEGORY_SKIP = "runtime-probe-skip";
3644
+ const CATEGORY_DEFERRED = "runtime-probe-deferred";
3645
+ const CATEGORY_FAIL = "runtime-probe-fail";
3646
+ const CATEGORY_TIMEOUT = "runtime-probe-timeout";
3647
+ const defaultExecutors = { host: (probe) => executeProbeOnHost(probe, { cwd: process.cwd() }) };
3648
+ var RuntimeProbeCheck = class {
3649
+ name = "runtime-probes";
3650
+ tier = "A";
3651
+ _executors;
3652
+ constructor(executors) {
3653
+ this._executors = {
3654
+ ...defaultExecutors,
3655
+ ...executors ?? {}
3656
+ };
3657
+ }
3658
+ async run(context) {
3659
+ const start = Date.now();
3660
+ if (context.storyContent === void 0) {
3661
+ const findings$1 = [{
3662
+ category: CATEGORY_SKIP,
3663
+ severity: "warn",
3664
+ message: "story content unavailable — skipping runtime probe check"
3665
+ }];
3666
+ return {
3667
+ status: "warn",
3668
+ details: renderFindings(findings$1),
3669
+ duration_ms: Date.now() - start,
3670
+ findings: findings$1
3671
+ };
3672
+ }
3673
+ const parsed = parseRuntimeProbes(context.storyContent);
3674
+ if (parsed.kind === "absent") return {
3675
+ status: "pass",
3676
+ details: "runtime-probes: no ## Runtime Probes section declared — skipping",
3677
+ duration_ms: Date.now() - start,
3678
+ findings: []
3679
+ };
3680
+ if (parsed.kind === "invalid") {
3681
+ const findings$1 = [{
3682
+ category: CATEGORY_PARSE,
3683
+ severity: "error",
3684
+ message: parsed.error
3685
+ }];
3686
+ return {
3687
+ status: "fail",
3688
+ details: renderFindings(findings$1),
3689
+ duration_ms: Date.now() - start,
3690
+ findings: findings$1
3691
+ };
3692
+ }
3693
+ if (parsed.probes.length === 0) return {
3694
+ status: "pass",
3695
+ details: "runtime-probes: 0 probes declared — skipping",
3696
+ duration_ms: Date.now() - start,
3697
+ findings: []
3698
+ };
3699
+ const findings = [];
3700
+ for (const probe of parsed.probes) {
3701
+ if (probe.sandbox === "twin") {
3702
+ findings.push({
3703
+ category: CATEGORY_DEFERRED,
3704
+ severity: "warn",
3705
+ message: `probe "${probe.name}" uses sandbox=twin which is deferred until Phase 3 (Digital Twin integration); skipping`
3706
+ });
3707
+ continue;
3708
+ }
3709
+ const result = await this._executors.host(probe);
3710
+ if (result.outcome === "pass") continue;
3711
+ const category = result.outcome === "timeout" ? CATEGORY_TIMEOUT : CATEGORY_FAIL;
3712
+ const descriptor = probe.description ? ` (${probe.description})` : "";
3713
+ const message = result.outcome === "timeout" ? `probe "${probe.name}"${descriptor} timed out after ${result.durationMs}ms` : `probe "${probe.name}"${descriptor} failed with exit ${result.exitCode ?? "unknown"}`;
3714
+ findings.push({
3715
+ category,
3716
+ severity: "error",
3717
+ message,
3718
+ command: result.command,
3719
+ ...result.exitCode !== void 0 ? { exitCode: result.exitCode } : {},
3720
+ stdoutTail: result.stdoutTail,
3721
+ stderrTail: result.stderrTail,
3722
+ durationMs: result.durationMs
3723
+ });
3724
+ }
3725
+ const status = findings.some((f) => f.severity === "error") ? "fail" : findings.some((f) => f.severity === "warn") ? "warn" : "pass";
3726
+ return {
3727
+ status,
3728
+ details: findings.length > 0 ? renderFindings(findings) : `runtime-probes: ${parsed.probes.length} probe(s) passed`,
3729
+ duration_ms: Date.now() - start,
3730
+ findings
3731
+ };
3732
+ }
3733
+ };
3734
+
3398
3735
  //#endregion
3399
3736
  //#region packages/sdlc/dist/verification/verification-pipeline.js
3400
3737
  /**
@@ -3460,7 +3797,8 @@ var VerificationPipeline = class {
3460
3797
  checkName: check.name,
3461
3798
  status: runResult.status,
3462
3799
  details: runResult.details,
3463
- duration_ms: runResult.duration_ms
3800
+ duration_ms: runResult.duration_ms,
3801
+ ...runResult.findings !== void 0 ? { findings: runResult.findings } : {}
3464
3802
  };
3465
3803
  } catch (err) {
3466
3804
  const elapsed = Date.now() - checkStart;
@@ -3470,7 +3808,12 @@ var VerificationPipeline = class {
3470
3808
  checkName: check.name,
3471
3809
  status: "warn",
3472
3810
  details: message,
3473
- duration_ms: elapsed
3811
+ duration_ms: elapsed,
3812
+ findings: [{
3813
+ category: "check-exception",
3814
+ severity: "warn",
3815
+ message
3816
+ }]
3474
3817
  };
3475
3818
  }
3476
3819
  checkResults.push(result);
@@ -3495,11 +3838,13 @@ var VerificationPipeline = class {
3495
3838
  /**
3496
3839
  * Create a VerificationPipeline pre-loaded with the canonical check set.
3497
3840
  *
3498
- * Canonical Tier A check order (architecture section 3.5):
3841
+ * Canonical Tier A check order:
3499
3842
  * 1. PhantomReviewCheck — story 51-2 (runs first: unreviewed stories skipped)
3500
3843
  * 2. TrivialOutputCheck — story 51-3
3501
3844
  * 3. AcceptanceCriteriaEvidenceCheck
3502
3845
  * 4. BuildCheck — story 51-4
3846
+ * 5. RuntimeProbeCheck — Epic 55 Phase 2: runtime behavior gate; runs last
3847
+ * in Tier A because probes may depend on built artifacts
3503
3848
  *
3504
3849
  * @param bus Typed event bus for verification events.
3505
3850
  * @param config Optional config (used to forward threshold to TrivialOutputCheck).
@@ -3509,7 +3854,8 @@ function createDefaultVerificationPipeline(bus, config) {
3509
3854
  new PhantomReviewCheck(),
3510
3855
  new TrivialOutputCheck(config),
3511
3856
  new AcceptanceCriteriaEvidenceCheck(),
3512
- new BuildCheck()
3857
+ new BuildCheck(),
3858
+ new RuntimeProbeCheck()
3513
3859
  ];
3514
3860
  return new VerificationPipeline(bus, checks);
3515
3861
  }
@@ -4885,4 +5231,4 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
4885
5231
 
4886
5232
  //#endregion
4887
5233
  export { BMAD_BASELINE_TOKENS_FULL, DEFAULT_STALL_THRESHOLD_SECONDS, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN$1 as STORY_KEY_PATTERN, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter$1 as createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, createStateStore, detectCycles, extractTargetFilesFromStoryContent, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, isOrchestratorProcessLine, parseDbTimestampAsUtc, registerHealthCommand, renderFindings, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveGraphPath, resolveMainRepoRoot, resolveRunManifest, runHealthAction, validateStoryKey };
4888
- //# sourceMappingURL=health-CB48LF0t.js.map
5234
+ //# sourceMappingURL=health-DHLR9Iz1.js.map
@@ -1,4 +1,4 @@
1
- import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-CB48LF0t.js";
1
+ import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-DHLR9Iz1.js";
2
2
  import { createLogger } from "./logger-KeHncl-f.js";
3
3
  import { TypedEventBusImpl, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning, sleep } from "./helpers-CElYrONe.js";
4
4
  import { ADVISORY_NOTES, Categorizer, ConsumerAnalyzer, DEFAULT_GLOBAL_SETTINGS, DispatcherImpl, DoltClient, ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, EfficiencyScorer, IngestionServer, LogTurnAnalyzer, OPERATIONAL_FINDING, Recommender, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, STORY_METRICS, STORY_OUTCOME, SubstrateConfigSchema, TEST_EXPANSION_FINDING, TEST_PLAN, TelemetryNormalizer, TelemetryPipeline, TurnAnalyzer, addTokenUsage, aggregateTokenUsageForRun, aggregateTokenUsageForStory, callLLM, createConfigSystem, createDatabaseAdapter$1, createDecision, createPipelineRun, createRequirement, detectInterfaceChanges, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunMetrics, getRunningPipelineRuns, getStoryMetricsForRun, getTokenUsageSummary, initSchema, listRequirements, loadModelRoutingConfig, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision, writeRunMetrics, writeStoryMetrics } from "./dist-srr3BfCc.js";
@@ -43840,4 +43840,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
43840
43840
 
43841
43841
  //#endregion
43842
43842
  export { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, normalizeGraphSummaryToStatus, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveMaxReviewCycles, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict, wireNdjsonEmitter };
43843
- //# sourceMappingURL=run-BHKqyFFM.js.map
43843
+ //# sourceMappingURL=run-ofO9AWFc.js.map
@@ -1,8 +1,8 @@
1
- import "./health-CB48LF0t.js";
1
+ import "./health-DHLR9Iz1.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./helpers-CElYrONe.js";
4
4
  import "./dist-srr3BfCc.js";
5
- import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-BHKqyFFM.js";
5
+ import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-ofO9AWFc.js";
6
6
  import "./routing-CcBOCuC9.js";
7
7
  import "./decisions-C0pz9Clx.js";
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.20.4",
3
+ "version": "0.20.5",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",