substrate-ai 0.20.143 → 0.20.145

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,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-FUl73R5F.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-muiK_xRZ.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, 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-DiNK6QOD.js";
6
- import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GLOBSTAR, GitClient, GrammarLoader, Minimatch, Minipass, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createGitWorktreeManager, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, escape, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runProbeAuthor, runSolutioningPhase, unescape, validateStopAfterFromConflict } from "../run-B2duge-W.js";
6
+ import { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GLOBSTAR, GitClient, GrammarLoader, Minimatch, Minipass, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createGitWorktreeManager, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, escape, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runProbeAuthor, runSolutioningPhase, unescape, validateStopAfterFromConflict } from "../run-DHPGxjlk.js";
7
7
  import "../adapter-registry-DIcrxjH8.js";
8
- 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-BkijWyWk.js";
8
+ 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-CX6KKQHX.js";
9
9
  import "../errors-B21JUhcc.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-CtO5Tm6u.js";
14
+ import "../interactive-prompt-PgHUXA-A.js";
15
15
  import "../recovery-engine-BKGBeBnW.js";
16
16
  import "../version-manager-impl-qFBiO4Eh.js";
17
17
  import { registerUpgradeCommand } from "../upgrade-DB7KzD9c.js";
@@ -7052,7 +7052,7 @@ async function runStatusAction(options) {
7052
7052
  logger$15.debug({ err }, "Work graph query failed, continuing without work graph data");
7053
7053
  }
7054
7054
  if (run === void 0) {
7055
- const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-CoxLvCcT.js");
7055
+ const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-Dk6gHNDC.js");
7056
7056
  const substrateDirPath = join(projectRoot, ".substrate");
7057
7057
  const processInfo = inspectProcessTree$1({
7058
7058
  projectRoot,
@@ -8581,7 +8581,7 @@ async function runSupervisorAction(options, deps = {}) {
8581
8581
  await initSchema(expAdapter);
8582
8582
  const { runRunAction: runPipeline } = await import(
8583
8583
  /* @vite-ignore */
8584
- "../run-DRwOPt3k.js"
8584
+ "../run-C0uq3ETL.js"
8585
8585
  );
8586
8586
  const runStoryFn = async (opts) => {
8587
8587
  const exitCode = await runPipeline({
@@ -1,7 +1,7 @@
1
- import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-FUl73R5F.js";
1
+ import { DEFAULT_STALL_THRESHOLD_SECONDS, getAllDescendantPids, getAutoHealthData, inspectProcessTree, isOrchestratorProcessLine, registerHealthCommand, runHealthAction } from "./health-muiK_xRZ.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./dist-DiNK6QOD.js";
4
- import "./manifest-read-BkijWyWk.js";
4
+ import "./manifest-read-CX6KKQHX.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-DiNK6QOD.js";
3
- import { resolveMainRepoRoot, resolveRunManifest } from "./manifest-read-BkijWyWk.js";
3
+ import { resolveMainRepoRoot, resolveRunManifest } from "./manifest-read-CX6KKQHX.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-FUl73R5F.js.map
1003
+ //# sourceMappingURL=health-muiK_xRZ.js.map
@@ -1,5 +1,5 @@
1
1
  import { createLogger } from "./logger-KeHncl-f.js";
2
- import { readCurrentRunId, resolveMainRepoRoot } from "./manifest-read-BkijWyWk.js";
2
+ import { readCurrentRunId, resolveMainRepoRoot } from "./manifest-read-CX6KKQHX.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-CtO5Tm6u.js.map
183
+ //# sourceMappingURL=interactive-prompt-PgHUXA-A.js.map
@@ -1737,6 +1737,16 @@ function extractAcceptanceCriteriaIds(storyContent) {
1737
1737
  if (id !== void 0) ids.add(id);
1738
1738
  }
1739
1739
  }
1740
+ if (ids.size === 0) {
1741
+ const lines = acceptanceSection.split(/\r?\n/);
1742
+ const scenarioHeadings = lines.filter((l) => /^\s*(?:#+\s*)?(?:\*\*)?Scenario\b/i.test(l)).length;
1743
+ let scenarioCount = scenarioHeadings;
1744
+ if (scenarioCount === 0) scenarioCount = lines.filter((l) => /^\s*(?:\*\*)?Given\b/i.test(l)).length;
1745
+ for (let i = 1; i <= scenarioCount; i++) {
1746
+ const id = normalizeAcId(String(i));
1747
+ if (id !== void 0) ids.add(id);
1748
+ }
1749
+ }
1740
1750
  }
1741
1751
  return sortAcIds(ids);
1742
1752
  }
@@ -1783,7 +1793,7 @@ var AcceptanceCriteriaEvidenceCheck = class {
1783
1793
  const findings = [{
1784
1794
  category: "ac-context-missing",
1785
1795
  severity: "warn",
1786
- message: "no numbered acceptance criteria found in story"
1796
+ message: "no acceptance criteria could be extracted from the story (numbered, AC-ref, bullet, and Gherkin scenario forms all matched nothing) — AC-EVIDENCE ENFORCEMENT IS SKIPPED for this story. If the story genuinely has ACs, their format is unrecognized; number them (1. / AC1) or use Given/When/Then scenarios."
1787
1797
  }];
1788
1798
  return {
1789
1799
  status: "warn",
@@ -2345,6 +2355,11 @@ var TestSuiteCheck = class {
2345
2355
  stderrTail: tail$1(stderr),
2346
2356
  durationMs: duration
2347
2357
  }];
2358
+ if (context.devStoryResult?.tests === "pass") findings.push({
2359
+ category: "tests-claim-mismatch",
2360
+ severity: "error",
2361
+ message: `dev-story self-reported tests: pass, but the real suite failed (exit ${String(code)}) — the claim is contradicted by ground truth`
2362
+ });
2348
2363
  resolve$2({
2349
2364
  status: "fail",
2350
2365
  details: renderFindings(findings),
@@ -2357,6 +2372,204 @@ var TestSuiteCheck = class {
2357
2372
  }
2358
2373
  };
2359
2374
 
2375
+ //#endregion
2376
+ //#region packages/sdlc/dist/verification/checks/contamination-check.js
2377
+ /** Map of language → source extensions used for foreign-language detection. */
2378
+ const LANGUAGE_EXTENSIONS = {
2379
+ typescript: [
2380
+ ".ts",
2381
+ ".tsx",
2382
+ ".mts",
2383
+ ".cts"
2384
+ ],
2385
+ javascript: [
2386
+ ".js",
2387
+ ".jsx",
2388
+ ".mjs",
2389
+ ".cjs"
2390
+ ],
2391
+ python: [".py"],
2392
+ go: [".go"],
2393
+ rust: [".rs"],
2394
+ java: [".java"],
2395
+ kotlin: [".kt", ".kts"]
2396
+ };
2397
+ /** JS-family toolchain manifests whose appearance flips build detection. */
2398
+ const JS_TOOLCHAIN_MANIFESTS = [
2399
+ "package.json",
2400
+ "tsconfig.json",
2401
+ "package-lock.json",
2402
+ "pnpm-lock.yaml",
2403
+ "yarn.lock",
2404
+ "bun.lockb",
2405
+ "vite.config.ts",
2406
+ "vite.config.js"
2407
+ ];
2408
+ /** Droppings that must never appear in a story diff, regardless of language. */
2409
+ const ALWAYS_DENY_SEGMENTS = [
2410
+ "node_modules",
2411
+ ".venv",
2412
+ "__pycache__"
2413
+ ];
2414
+ /**
2415
+ * Collect every `language: <x>` declaration in the profile (single-project
2416
+ * `project.language` plus monorepo `packages[].language`). Line-based parse —
2417
+ * no yaml dependency. Returns [] when the profile is absent/unreadable.
2418
+ */
2419
+ function readProfileLanguages(workingDir) {
2420
+ const profilePath = join$1(workingDir, ".substrate", "project-profile.yaml");
2421
+ if (!existsSync(profilePath)) return [];
2422
+ try {
2423
+ const content = readFileSync(profilePath, "utf-8");
2424
+ const langs = new Set();
2425
+ for (const m of content.matchAll(/^\s*(?:-\s+)?language:\s*['"]?([a-z]+)['"]?\s*$/gm)) if (m[1] !== void 0) langs.add(m[1]);
2426
+ return [...langs];
2427
+ } catch {
2428
+ return [];
2429
+ }
2430
+ }
2431
+ /** Language of a file per LANGUAGE_EXTENSIONS, or undefined for neutral files. */
2432
+ function fileLanguage(file) {
2433
+ const lower = file.toLowerCase();
2434
+ for (const [lang, exts] of Object.entries(LANGUAGE_EXTENSIONS)) if (exts.some((e) => lower.endsWith(e))) return lang;
2435
+ return void 0;
2436
+ }
2437
+ /**
2438
+ * Pure classification of a changed-file list against the project's declared
2439
+ * languages. Exported for unit testing.
2440
+ */
2441
+ function classifyContamination(changedFiles, allowedLanguages) {
2442
+ const allowJs = allowedLanguages.includes("typescript") || allowedLanguages.includes("javascript");
2443
+ const foreignSourceFiles = [];
2444
+ const foreignToolchainManifests = [];
2445
+ const droppings = [];
2446
+ for (const raw of changedFiles) {
2447
+ const file = raw.replace(/\\/g, "/").replace(/^\.\//, "");
2448
+ const segments = file.split("/");
2449
+ if (segments.some((s) => ALWAYS_DENY_SEGMENTS.includes(s))) {
2450
+ droppings.push(file);
2451
+ continue;
2452
+ }
2453
+ if (!allowJs && segments.includes("dist")) {
2454
+ droppings.push(file);
2455
+ continue;
2456
+ }
2457
+ const base = segments[segments.length - 1] ?? "";
2458
+ if (!allowJs && JS_TOOLCHAIN_MANIFESTS.includes(base)) {
2459
+ foreignToolchainManifests.push(file);
2460
+ continue;
2461
+ }
2462
+ const lang = fileLanguage(file);
2463
+ if (lang !== void 0 && !allowedLanguages.includes(lang)) {
2464
+ if (allowJs && (lang === "typescript" || lang === "javascript")) continue;
2465
+ foreignSourceFiles.push({
2466
+ file,
2467
+ language: lang
2468
+ });
2469
+ }
2470
+ }
2471
+ return {
2472
+ foreignSourceFiles,
2473
+ foreignToolchainManifests,
2474
+ droppings
2475
+ };
2476
+ }
2477
+ var ContaminationCheck = class {
2478
+ name = "scope-contamination";
2479
+ tier = "A";
2480
+ async run(context) {
2481
+ const start = Date.now();
2482
+ const changedFiles = context.changedFiles ?? [];
2483
+ if (changedFiles.length === 0) return {
2484
+ status: "pass",
2485
+ details: "no changed files to inspect",
2486
+ duration_ms: Date.now() - start,
2487
+ findings: []
2488
+ };
2489
+ const allowedLanguages = readProfileLanguages(context.workingDir);
2490
+ if (allowedLanguages.length === 0) {
2491
+ const findings$1 = [{
2492
+ category: "contamination-skip",
2493
+ severity: "warn",
2494
+ message: `no project profile with declared languages at ${context.workingDir} — cannot check for foreign-toolchain contamination (run substrate init to generate one)`
2495
+ }];
2496
+ return {
2497
+ status: "warn",
2498
+ details: renderFindings(findings$1),
2499
+ duration_ms: Date.now() - start,
2500
+ findings: findings$1
2501
+ };
2502
+ }
2503
+ const verdict = classifyContamination(changedFiles, allowedLanguages);
2504
+ const findings = [];
2505
+ if (verdict.droppings.length > 0) findings.push({
2506
+ category: "contamination-droppings",
2507
+ severity: "error",
2508
+ message: `story diff contains build/dependency droppings that must never be committed: ${verdict.droppings.slice(0, 10).join(", ")}` + (verdict.droppings.length > 10 ? ` (+${String(verdict.droppings.length - 10)} more)` : "")
2509
+ });
2510
+ if (verdict.foreignToolchainManifests.length > 0) findings.push({
2511
+ category: "contamination-toolchain",
2512
+ severity: "error",
2513
+ message: `story introduces a JS toolchain on a ${allowedLanguages.join("/")} project: ${verdict.foreignToolchainManifests.join(", ")}. This is the exact trigger that flips build detection to npm and masks real story outcomes (field finding #12).`
2514
+ });
2515
+ if (verdict.foreignSourceFiles.length > 0) findings.push({
2516
+ category: "contamination-language",
2517
+ severity: "error",
2518
+ message: `story writes source files in language(s) the project does not use (declared: ${allowedLanguages.join(", ")}): ` + verdict.foreignSourceFiles.slice(0, 10).map((f) => `${f.file} [${f.language}]`).join(", ")
2519
+ });
2520
+ if (findings.length === 0) return {
2521
+ status: "pass",
2522
+ details: `no contamination across ${String(changedFiles.length)} changed file(s)`,
2523
+ duration_ms: Date.now() - start,
2524
+ findings: []
2525
+ };
2526
+ return {
2527
+ status: "fail",
2528
+ details: renderFindings(findings),
2529
+ duration_ms: Date.now() - start,
2530
+ findings
2531
+ };
2532
+ }
2533
+ };
2534
+
2535
+ //#endregion
2536
+ //#region packages/sdlc/dist/verification/checks/test-mutation-check.js
2537
+ /** Heuristic: is this path a test file? Covers the JS/TS/Python/Go idioms. */
2538
+ function isTestPath(file) {
2539
+ const norm = file.replace(/\\/g, "/").toLowerCase();
2540
+ const base = norm.split("/").pop() ?? "";
2541
+ if (/(^|\/)(__tests__|tests?)\//.test(norm)) return true;
2542
+ if (/\.(test|spec)\.[a-z]+$/.test(base)) return true;
2543
+ if (/^test_.*\.py$/.test(base) || /_test\.(py|go)$/.test(base)) return true;
2544
+ return false;
2545
+ }
2546
+ var TestMutationCheck = class {
2547
+ name = "test-mutation";
2548
+ tier = "A";
2549
+ async run(context) {
2550
+ const start = Date.now();
2551
+ const modified = context.modifiedTrackedFiles ?? [];
2552
+ const mutatedTests = modified.filter(isTestPath);
2553
+ if (mutatedTests.length === 0) return {
2554
+ status: "pass",
2555
+ details: "no pre-existing test files were modified",
2556
+ duration_ms: Date.now() - start,
2557
+ findings: []
2558
+ };
2559
+ const findings = [{
2560
+ category: "test-mutation",
2561
+ severity: "warn",
2562
+ message: `story MODIFIED ${String(mutatedTests.length)} pre-existing test file(s): ${mutatedTests.slice(0, 10).join(", ")}` + (mutatedTests.length > 10 ? ` (+${String(mutatedTests.length - 10)} more)` : "") + ". Editing existing tests to satisfy a suite is the measured reward-hack pattern — review whether these edits weaken assertions the story was supposed to satisfy."
2563
+ }];
2564
+ return {
2565
+ status: "warn",
2566
+ details: renderFindings(findings),
2567
+ duration_ms: Date.now() - start,
2568
+ findings
2569
+ };
2570
+ }
2571
+ };
2572
+
2360
2573
  //#endregion
2361
2574
  //#region packages/sdlc/dist/verification/probes/types.js
2362
2575
  /**
@@ -3846,7 +4059,7 @@ var SourceAcFidelityCheck = class {
3846
4059
  }
3847
4060
  }
3848
4061
  const existsInCode = pathSatisfiedByCode(context.workingDir, clause.text);
3849
- const modifiedFiles = context.devStoryResult?.files_modified ?? [];
4062
+ const modifiedFiles = context.changedFiles !== void 0 && context.changedFiles.length > 0 ? context.changedFiles : context.devStoryResult?.files_modified ?? [];
3850
4063
  const referencedByStory = pathReferencedInModifiedFiles(context.workingDir, clause.text, modifiedFiles);
3851
4064
  let severity;
3852
4065
  let driftMessage;
@@ -4292,6 +4505,8 @@ function createDefaultVerificationPipeline(bus, config) {
4292
4505
  new AcceptanceCriteriaEvidenceCheck(),
4293
4506
  new BuildCheck(),
4294
4507
  new TestSuiteCheck(),
4508
+ new ContaminationCheck(),
4509
+ new TestMutationCheck(),
4295
4510
  new RuntimeProbeCheck(),
4296
4511
  new SourceAcFidelityCheck(),
4297
4512
  new SourceAcShelloutCheck(),
@@ -6075,4 +6290,4 @@ async function resolveRunManifest(dbRoot, runId) {
6075
6290
 
6076
6291
  //#endregion
6077
6292
  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 };
6078
- //# sourceMappingURL=manifest-read-BkijWyWk.js.map
6293
+ //# sourceMappingURL=manifest-read-CX6KKQHX.js.map
@@ -1,6 +1,6 @@
1
1
  import "../../logger-KeHncl-f.js";
2
2
  import "../../dist-DiNK6QOD.js";
3
- import "../../manifest-read-BkijWyWk.js";
4
- import { runInteractivePrompt } from "../../interactive-prompt-CtO5Tm6u.js";
3
+ import "../../manifest-read-CX6KKQHX.js";
4
+ import { runInteractivePrompt } from "../../interactive-prompt-PgHUXA-A.js";
5
5
 
6
6
  export { runInteractivePrompt };
@@ -1,14 +1,14 @@
1
- import "./health-FUl73R5F.js";
1
+ import "./health-muiK_xRZ.js";
2
2
  import "./logger-KeHncl-f.js";
3
3
  import "./helpers-CElYrONe.js";
4
4
  import "./dist-DiNK6QOD.js";
5
- import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-B2duge-W.js";
6
- import "./manifest-read-BkijWyWk.js";
5
+ import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, runRunAction, wireNdjsonEmitter } from "./run-DHPGxjlk.js";
6
+ import "./manifest-read-CX6KKQHX.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-CtO5Tm6u.js";
11
+ import "./interactive-prompt-PgHUXA-A.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-FUl73R5F.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-muiK_xRZ.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, 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, createPipelineRun, createRequirement, createStderrLogger, 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-DiNK6QOD.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-BkijWyWk.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-CX6KKQHX.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-CtO5Tm6u.js";
8
+ import { runInteractivePrompt } from "./interactive-prompt-PgHUXA-A.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";
@@ -5999,6 +5999,43 @@ function runBuildVerification(options) {
5999
5999
  * @param workingDir - Directory to run git commands in (defaults to process.cwd())
6000
6000
  * @returns Array of changed file paths; empty when nothing changed
6001
6001
  */
6002
+ /**
6003
+ * H1.7 (hardening program): the PRE-EXISTING files this story modified or
6004
+ * deleted — the tracked-diff portion of the working-tree change (unstaged
6005
+ * HEAD diff + staged), deliberately EXCLUDING untracked/new files. A tracked
6006
+ * file in this list existed before the dispatch; new tests the story wrote
6007
+ * never appear here. Feeds the reward-hack tripwire (agents editing existing
6008
+ * tests to go green — the measured exploit pattern).
6009
+ */
6010
+ function checkGitModifiedTrackedFiles(workingDir = process.cwd()) {
6011
+ const results = new Set();
6012
+ try {
6013
+ execSync("git rev-parse --verify HEAD", {
6014
+ cwd: workingDir,
6015
+ stdio: [
6016
+ "ignore",
6017
+ "pipe",
6018
+ "pipe"
6019
+ ],
6020
+ timeout: 3e3
6021
+ });
6022
+ } catch {
6023
+ return [];
6024
+ }
6025
+ for (const args of ["git diff --name-only HEAD", "git diff --cached --name-only"]) try {
6026
+ execSync(args, {
6027
+ cwd: workingDir,
6028
+ encoding: "utf-8",
6029
+ timeout: 5e3,
6030
+ stdio: [
6031
+ "ignore",
6032
+ "pipe",
6033
+ "pipe"
6034
+ ]
6035
+ }).split("\n").map((l) => l.trim()).filter((l) => l.length > 0).forEach((f$1) => results.add(f$1));
6036
+ } catch {}
6037
+ return [...results];
6038
+ }
6002
6039
  function checkGitDiffFiles(workingDir = process.cwd()) {
6003
6040
  const results = new Set();
6004
6041
  let repoHasCommits = true;
@@ -7542,6 +7579,12 @@ const logger$20 = createLogger("compiled-workflows:git-helpers");
7542
7579
  * @returns Structured result for the orchestrator to inspect
7543
7580
  */
7544
7581
  async function commitDevStoryOutput(storyKey, storyTitle, filesModified, workingDir) {
7582
+ const COMMIT_DENY_SEGMENTS = [
7583
+ "node_modules",
7584
+ ".venv",
7585
+ "__pycache__",
7586
+ ".substrate-worktrees"
7587
+ ];
7545
7588
  const insideWorktree = [];
7546
7589
  for (const p of filesModified) {
7547
7590
  const abs = isAbsolute(p) ? p : resolve$1(workingDir, p);
@@ -7554,6 +7597,14 @@ async function commitDevStoryOutput(storyKey, storyTitle, filesModified, working
7554
7597
  }, "commitDevStoryOutput: filtered out path outside worktree");
7555
7598
  continue;
7556
7599
  }
7600
+ const segments = rel.replace(/\\/g, "/").split("/");
7601
+ if (segments.some((s$1) => COMMIT_DENY_SEGMENTS.includes(s$1))) {
7602
+ logger$20.warn({
7603
+ path: rel,
7604
+ storyKey
7605
+ }, "commitDevStoryOutput: denylisted dependency/artifact path excluded from substrate commit");
7606
+ continue;
7607
+ }
7557
7608
  insideWorktree.push(rel);
7558
7609
  }
7559
7610
  if (insideWorktree.length === 0) return {
@@ -12619,6 +12670,8 @@ function assembleVerificationContext(opts) {
12619
12670
  outputTokenCount: opts.outputTokenCount,
12620
12671
  sourceEpicContent: opts.sourceEpicContent,
12621
12672
  runId: opts.runId,
12673
+ ...opts.changedFiles !== void 0 ? { changedFiles: opts.changedFiles } : {},
12674
+ ...opts.modifiedTrackedFiles !== void 0 ? { modifiedTrackedFiles: opts.modifiedTrackedFiles } : {},
12622
12675
  ...profileBuildCommand !== void 0 ? { buildCommand: profileBuildCommand } : {},
12623
12676
  ...profileTestCommand !== void 0 ? { testCommand: profileTestCommand } : {}
12624
12677
  };
@@ -14714,6 +14767,33 @@ function createImplementationOrchestrator(deps) {
14714
14767
  await persistState();
14715
14768
  return;
14716
14769
  }
14770
+ if (effectiveProjectRoot !== void 0 && effectiveProjectRoot !== projectRoot && isAbsolute(createResult.story_file)) {
14771
+ const relToWorktree = relative(effectiveProjectRoot, createResult.story_file);
14772
+ const outsideWorktree = relToWorktree.startsWith("..") || isAbsolute(relToWorktree);
14773
+ if (outsideWorktree) {
14774
+ const errMsg = `create-story wrote its artifact OUTSIDE the story worktree: ${createResult.story_file} (worktree: ${effectiveProjectRoot}). The agent escaped its working directory — this is the parent/home-directory write-leak class (field finding #15). The stray file was NOT adopted; inspect and remove it.`;
14775
+ logger$27.error({
14776
+ storyKey,
14777
+ storyFile: createResult.story_file,
14778
+ worktree: effectiveProjectRoot
14779
+ }, "create-story artifact outside worktree — escalating");
14780
+ updateStory(storyKey, {
14781
+ phase: "ESCALATED",
14782
+ error: "create-story-outside-project",
14783
+ completedAt: new Date().toISOString()
14784
+ });
14785
+ await writeStoryMetricsBestEffort(storyKey, "escalated", 0);
14786
+ await emitEscalation({
14787
+ storyKey,
14788
+ lastVerdict: "create-story-outside-project",
14789
+ reviewCycles: 0,
14790
+ issues: [errMsg],
14791
+ escalationReason: "create-story-outside-project"
14792
+ });
14793
+ await persistState();
14794
+ return;
14795
+ }
14796
+ }
14717
14797
  if (effectiveProjectRoot !== void 0) {
14718
14798
  const expectedArtifactsDir = join$1(effectiveProjectRoot, "_bmad-output", "implementation-artifacts");
14719
14799
  const escapedExpectedDir = expectedArtifactsDir.replace("/_bmad-output/", "/\\_bmad-output/");
@@ -15660,8 +15740,14 @@ function createImplementationOrchestrator(deps) {
15660
15740
  return;
15661
15741
  }
15662
15742
  let gitDiffFiles;
15743
+ let modifiedTrackedFiles;
15663
15744
  if (devStoryWasSuccess) {
15664
15745
  gitDiffFiles = checkGitDiffFiles(effectiveProjectRoot ?? process.cwd());
15746
+ try {
15747
+ modifiedTrackedFiles = checkGitModifiedTrackedFiles(effectiveProjectRoot ?? process.cwd());
15748
+ } catch {
15749
+ modifiedTrackedFiles = void 0;
15750
+ }
15665
15751
  if (gitDiffFiles.length === 0) {
15666
15752
  let hasNewCommits = false;
15667
15753
  if (baselineHeadSha) try {
@@ -16109,6 +16195,8 @@ function createImplementationOrchestrator(deps) {
16109
16195
  devStoryResult: devStorySignals,
16110
16196
  outputTokenCount: devOutputTokenCount,
16111
16197
  sourceEpicContent,
16198
+ ...gitDiffFiles !== void 0 ? { changedFiles: gitDiffFiles } : {},
16199
+ ...modifiedTrackedFiles !== void 0 ? { modifiedTrackedFiles } : {},
16112
16200
  runId: config.pipelineRunId
16113
16201
  });
16114
16202
  const verifSummary = await verificationPipeline.run(verifContext, "A");
@@ -48121,4 +48209,4 @@ function registerRunCommand(program, version = "0.0.0", projectRoot = process.cw
48121
48209
 
48122
48210
  //#endregion
48123
48211
  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, createGitWorktreeManager, 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 };
48124
- //# sourceMappingURL=run-B2duge-W.js.map
48212
+ //# sourceMappingURL=run-DHPGxjlk.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "substrate-ai",
3
- "version": "0.20.143",
3
+ "version": "0.20.145",
4
4
  "description": "Substrate — multi-agent orchestration daemon for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",