substrate-ai 0.19.33 → 0.19.34
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 +4 -4
- package/dist/{health-BMz_Vxme.js → health-B28gVggH.js} +1 -1
- package/dist/{health-q4LAKfYz.js → health-Ckl2VOPW.js} +2 -2
- package/dist/{run-nz9xWO1t.js → run-BG1vEtOA.js} +328 -10
- package/dist/{run-Cv8E1iib.js → run-DgayZ4Aq.js} +2 -2
- package/package.json +1 -1
- package/packs/bmad/prompts/code-review.md +9 -1
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-
|
|
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-Ckl2VOPW.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-6ETBV23_.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-
|
|
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-BG1vEtOA.js";
|
|
8
8
|
import "../errors-QZ2_FzOB.js";
|
|
9
9
|
import "../routing-CcBOCuC9.js";
|
|
10
10
|
import "../decisions-C0pz9Clx.js";
|
|
@@ -3307,7 +3307,7 @@ async function runStatusAction(options) {
|
|
|
3307
3307
|
logger$12.debug({ err }, "Work graph query failed, continuing without work graph data");
|
|
3308
3308
|
}
|
|
3309
3309
|
if (run === void 0) {
|
|
3310
|
-
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-
|
|
3310
|
+
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-B28gVggH.js");
|
|
3311
3311
|
const substrateDirPath = join(projectRoot, ".substrate");
|
|
3312
3312
|
const processInfo = inspectProcessTree$1({
|
|
3313
3313
|
projectRoot,
|
|
@@ -4833,7 +4833,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
4833
4833
|
await initSchema(expAdapter);
|
|
4834
4834
|
const { runRunAction: runPipeline } = await import(
|
|
4835
4835
|
/* @vite-ignore */
|
|
4836
|
-
"../run-
|
|
4836
|
+
"../run-DgayZ4Aq.js"
|
|
4837
4837
|
);
|
|
4838
4838
|
const runStoryFn = async (opts) => {
|
|
4839
4839
|
const exitCode = await runPipeline({
|
|
@@ -1,4 +1,4 @@
|
|
|
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-Ckl2VOPW.js";
|
|
2
2
|
import "./logger-KeHncl-f.js";
|
|
3
3
|
import "./dist-6ETBV23_.js";
|
|
4
4
|
import "./decisions-C0pz9Clx.js";
|
|
@@ -2715,7 +2715,7 @@ function createSdlcEventBridge(opts) {
|
|
|
2715
2715
|
const { finalOutcome } = data;
|
|
2716
2716
|
if (finalOutcome.status === "SUCCESS") sdlcBus.emit("orchestrator:story-complete", {
|
|
2717
2717
|
storyKey,
|
|
2718
|
-
reviewCycles: devStoryRetries
|
|
2718
|
+
reviewCycles: devStoryRetries + 1
|
|
2719
2719
|
});
|
|
2720
2720
|
};
|
|
2721
2721
|
const onGoalGateUnsatisfied = (data) => {
|
|
@@ -4253,4 +4253,4 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
4253
4253
|
|
|
4254
4254
|
//#endregion
|
|
4255
4255
|
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, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, createStateStore, detectCycles, extractTargetFilesFromStoryContent, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, isOrchestratorProcessLine, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveGraphPath, resolveMainRepoRoot, resolveRunManifest, runHealthAction, validateStoryKey };
|
|
4256
|
-
//# sourceMappingURL=health-
|
|
4256
|
+
//# sourceMappingURL=health-Ckl2VOPW.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, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-
|
|
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, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-Ckl2VOPW.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-6ETBV23_.js";
|
|
@@ -7372,6 +7372,191 @@ function extractFilesInScope(storyContent) {
|
|
|
7372
7372
|
].join("\n");
|
|
7373
7373
|
}
|
|
7374
7374
|
|
|
7375
|
+
//#endregion
|
|
7376
|
+
//#region src/modules/compiled-workflows/scope-guardrail.ts
|
|
7377
|
+
/**
|
|
7378
|
+
* ScopeGuardrail — utility for detecting out-of-scope file modifications in code review.
|
|
7379
|
+
*
|
|
7380
|
+
* Parses the expected file set from a story spec and compares it against the
|
|
7381
|
+
* actual files modified by the dev agent. Returns a pre-computed scope analysis
|
|
7382
|
+
* markdown string that can be injected into the code-review prompt so the LLM
|
|
7383
|
+
* reviewer does not need to re-parse the spec sections manually.
|
|
7384
|
+
*
|
|
7385
|
+
* Architecture constraints:
|
|
7386
|
+
* - Pure utility: no imports from packages/sdlc/, packages/core/, or persistence layer
|
|
7387
|
+
* - Takes plain strings in, returns plain strings out
|
|
7388
|
+
* - Test-file exemption patterns must match countTestMetrics() in code-review.ts
|
|
7389
|
+
*/
|
|
7390
|
+
/**
|
|
7391
|
+
* File extensions that qualify a token as a "path-like" string in task bullets.
|
|
7392
|
+
* Any token containing `/` and one of these extensions is treated as a file path.
|
|
7393
|
+
*/
|
|
7394
|
+
const RECOGNIZED_EXTENSIONS = [
|
|
7395
|
+
".ts",
|
|
7396
|
+
".js",
|
|
7397
|
+
".tsx",
|
|
7398
|
+
".jsx",
|
|
7399
|
+
".md",
|
|
7400
|
+
".json",
|
|
7401
|
+
".yaml",
|
|
7402
|
+
".yml",
|
|
7403
|
+
".py",
|
|
7404
|
+
".go",
|
|
7405
|
+
".java",
|
|
7406
|
+
".rb",
|
|
7407
|
+
".rs",
|
|
7408
|
+
".sh",
|
|
7409
|
+
".css",
|
|
7410
|
+
".scss",
|
|
7411
|
+
".html"
|
|
7412
|
+
];
|
|
7413
|
+
/**
|
|
7414
|
+
* Section headings that explicitly list expected file paths.
|
|
7415
|
+
*/
|
|
7416
|
+
const FILE_PATH_SECTION_PATTERNS = [
|
|
7417
|
+
/^#{1,4}\s*(file paths to create|files? to create)/i,
|
|
7418
|
+
/^#{1,4}\s*(file paths to modify|files? to modify)/i,
|
|
7419
|
+
/^#{1,4}\s*(key file paths?)/i
|
|
7420
|
+
];
|
|
7421
|
+
/**
|
|
7422
|
+
* The heading that marks the start of tasks/subtasks section.
|
|
7423
|
+
*/
|
|
7424
|
+
const TASKS_SECTION_PATTERN = /^#{1,4}\s*(tasks?\s*\/?\s*subtasks?|tasks?)/i;
|
|
7425
|
+
/**
|
|
7426
|
+
* Returns true if the given file path is a test file.
|
|
7427
|
+
* Patterns must be consistent with countTestMetrics() in code-review.ts,
|
|
7428
|
+
* which checks for `.test.`, `.spec.`, and `__tests__`. We also add `__mocks__/`.
|
|
7429
|
+
*/
|
|
7430
|
+
function isTestFile(filePath) {
|
|
7431
|
+
return filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("__tests__/") || filePath.includes("__tests__\\") || filePath.includes("/__tests__") || filePath.includes("\\__tests__") || filePath.includes("__mocks__/") || filePath.includes("__mocks__\\") || filePath.includes("/__mocks__") || filePath.includes("\\__mocks__");
|
|
7432
|
+
}
|
|
7433
|
+
var ScopeGuardrail = class ScopeGuardrail {
|
|
7434
|
+
/**
|
|
7435
|
+
* Extract expected file paths from a story spec's raw text content.
|
|
7436
|
+
*
|
|
7437
|
+
* Scans for paths under sections:
|
|
7438
|
+
* - "### File Paths to Create" / "### Files to Create"
|
|
7439
|
+
* - "### File Paths to Modify" / "### Files to Modify"
|
|
7440
|
+
* - "### Key File Paths"
|
|
7441
|
+
* - Path-like tokens in "### Tasks / Subtasks" bullets
|
|
7442
|
+
*
|
|
7443
|
+
* A "path-like" token is any token containing `/` and a recognized extension.
|
|
7444
|
+
* Backtick wrappers and leading `- ` list markers are stripped.
|
|
7445
|
+
*
|
|
7446
|
+
* @param storyContent - Raw story spec markdown text
|
|
7447
|
+
* @returns Set of file path strings extracted from the spec
|
|
7448
|
+
*/
|
|
7449
|
+
static parseExpectedFiles(storyContent) {
|
|
7450
|
+
const paths = new Set();
|
|
7451
|
+
const lines = storyContent.split("\n");
|
|
7452
|
+
let mode = "none";
|
|
7453
|
+
for (const rawLine of lines) {
|
|
7454
|
+
const line = rawLine.trimEnd();
|
|
7455
|
+
if (/^#{1,4}\s/.test(line)) {
|
|
7456
|
+
mode = "none";
|
|
7457
|
+
for (const pattern of FILE_PATH_SECTION_PATTERNS) if (pattern.test(line)) {
|
|
7458
|
+
mode = "file-list";
|
|
7459
|
+
break;
|
|
7460
|
+
}
|
|
7461
|
+
if (mode === "none" && TASKS_SECTION_PATTERN.test(line)) mode = "tasks";
|
|
7462
|
+
continue;
|
|
7463
|
+
}
|
|
7464
|
+
if (mode === "file-list") {
|
|
7465
|
+
const extracted = extractPathsFromLine(line);
|
|
7466
|
+
for (const p of extracted) paths.add(p);
|
|
7467
|
+
} else if (mode === "tasks") {
|
|
7468
|
+
const extracted = extractPathLikeTokens(line);
|
|
7469
|
+
for (const p of extracted) paths.add(p);
|
|
7470
|
+
}
|
|
7471
|
+
}
|
|
7472
|
+
return paths;
|
|
7473
|
+
}
|
|
7474
|
+
/**
|
|
7475
|
+
* Build a pre-computed scope analysis markdown string for injection into
|
|
7476
|
+
* the code-review prompt context.
|
|
7477
|
+
*
|
|
7478
|
+
* Returns empty string if no out-of-scope violations are detected (so the
|
|
7479
|
+
* assemblePrompt optional-section drop behavior applies automatically).
|
|
7480
|
+
*
|
|
7481
|
+
* @param storyContent - Raw story spec markdown text
|
|
7482
|
+
* @param filesModified - List of file paths from the git diff
|
|
7483
|
+
* @returns Markdown string listing expected/actual/delta file sets,
|
|
7484
|
+
* or empty string if no violations found
|
|
7485
|
+
*/
|
|
7486
|
+
static buildAnalysis(storyContent, filesModified) {
|
|
7487
|
+
const expectedFiles = ScopeGuardrail.parseExpectedFiles(storyContent);
|
|
7488
|
+
const nonTestFiles = filesModified.filter((f$1) => !isTestFile(f$1));
|
|
7489
|
+
const outOfScope = nonTestFiles.filter((f$1) => !expectedFiles.has(f$1));
|
|
7490
|
+
if (outOfScope.length === 0) return "";
|
|
7491
|
+
const expectedList = expectedFiles.size > 0 ? Array.from(expectedFiles).map((f$1) => ` - ${f$1}`).join("\n") : " (none specified)";
|
|
7492
|
+
const actualList = nonTestFiles.length > 0 ? nonTestFiles.map((f$1) => ` - ${f$1}`).join("\n") : " (none)";
|
|
7493
|
+
const deltaList = outOfScope.map((f$1) => ` - ${f$1}`).join("\n");
|
|
7494
|
+
return [
|
|
7495
|
+
"## Pre-Computed Scope Analysis",
|
|
7496
|
+
"",
|
|
7497
|
+
"Expected files (from spec):",
|
|
7498
|
+
expectedList,
|
|
7499
|
+
"",
|
|
7500
|
+
"Actual files (from diff):",
|
|
7501
|
+
actualList,
|
|
7502
|
+
"",
|
|
7503
|
+
"Out-of-scope files (excluding tests):",
|
|
7504
|
+
deltaList,
|
|
7505
|
+
"",
|
|
7506
|
+
"Note: Test files (*.test.ts, *.spec.ts, __tests__/, __mocks__/) are excluded from scope checking."
|
|
7507
|
+
].join("\n");
|
|
7508
|
+
}
|
|
7509
|
+
};
|
|
7510
|
+
/**
|
|
7511
|
+
* Extract all path-like strings from a line (for file-list sections).
|
|
7512
|
+
* Handles both backtick-wrapped paths and plain paths.
|
|
7513
|
+
* Strips leading `- ` list markers.
|
|
7514
|
+
*/
|
|
7515
|
+
function extractPathsFromLine(line) {
|
|
7516
|
+
const paths = [];
|
|
7517
|
+
const trimmed = line.trim().replace(/^[-*]\s+/, "");
|
|
7518
|
+
if (!trimmed) return paths;
|
|
7519
|
+
const backtickMatches = trimmed.matchAll(/`([^`]+)`/g);
|
|
7520
|
+
for (const match$1 of backtickMatches) {
|
|
7521
|
+
const candidate = match$1[1].trim();
|
|
7522
|
+
if (isFilePath(candidate)) paths.push(candidate);
|
|
7523
|
+
}
|
|
7524
|
+
if (paths.length > 0) return paths;
|
|
7525
|
+
const noSpaces = trimmed.split(/\s/)[0];
|
|
7526
|
+
if (noSpaces && isFilePath(noSpaces)) paths.push(noSpaces);
|
|
7527
|
+
return paths;
|
|
7528
|
+
}
|
|
7529
|
+
/**
|
|
7530
|
+
* Extract path-like tokens from a line (for tasks/subtasks sections).
|
|
7531
|
+
* A path-like token contains `/` and a recognized extension.
|
|
7532
|
+
*/
|
|
7533
|
+
function extractPathLikeTokens(line) {
|
|
7534
|
+
const paths = [];
|
|
7535
|
+
const backtickMatches = line.matchAll(/`([^`]+)`/g);
|
|
7536
|
+
for (const match$1 of backtickMatches) {
|
|
7537
|
+
const candidate = match$1[1].trim();
|
|
7538
|
+
if (isFilePath(candidate)) paths.push(candidate);
|
|
7539
|
+
}
|
|
7540
|
+
const withoutBackticks = line.replace(/`[^`]+`/g, " ");
|
|
7541
|
+
const tokens = withoutBackticks.split(/\s+/);
|
|
7542
|
+
for (const token of tokens) {
|
|
7543
|
+
const clean = token.replace(/[,;:()\[\]{}'"]+$/g, "").replace(/^[,;:()\[\]{}'"]+/g, "");
|
|
7544
|
+
if (clean && isFilePath(clean)) paths.push(clean);
|
|
7545
|
+
}
|
|
7546
|
+
return paths;
|
|
7547
|
+
}
|
|
7548
|
+
/**
|
|
7549
|
+
* Returns true if the candidate string looks like a file path:
|
|
7550
|
+
* - Contains at least one `/`
|
|
7551
|
+
* - Has a recognized file extension
|
|
7552
|
+
* - Does not contain spaces
|
|
7553
|
+
*/
|
|
7554
|
+
function isFilePath(candidate) {
|
|
7555
|
+
if (!candidate.includes("/")) return false;
|
|
7556
|
+
if (/\s/.test(candidate)) return false;
|
|
7557
|
+
return RECOGNIZED_EXTENSIONS.some((ext$1) => candidate.endsWith(ext$1));
|
|
7558
|
+
}
|
|
7559
|
+
|
|
7375
7560
|
//#endregion
|
|
7376
7561
|
//#region src/modules/compiled-workflows/code-review.ts
|
|
7377
7562
|
const logger$14 = createLogger("compiled-workflows:code-review");
|
|
@@ -7568,6 +7753,8 @@ async function runCodeReview(deps, params) {
|
|
|
7568
7753
|
} catch {}
|
|
7569
7754
|
const testMetricsContent = await countTestMetrics(filesModified, cwd);
|
|
7570
7755
|
if (testMetricsContent) logger$14.debug({ storyKey }, "Injecting verified test-count metrics into code-review context");
|
|
7756
|
+
const scopeAnalysisContent = storyContent && filesModified ? ScopeGuardrail.buildAnalysis(storyContent, filesModified) : "";
|
|
7757
|
+
if (scopeAnalysisContent) logger$14.debug({ storyKey }, "Scope analysis detected out-of-scope files");
|
|
7571
7758
|
const buildStatusPrefix = buildPassed === true ? "BUILD STATUS: PASSED — code compiles and passes build verification. Focus on logic correctness, style, and acceptance criteria rather than compilation errors.\n\n" : "";
|
|
7572
7759
|
const sections = [
|
|
7573
7760
|
{
|
|
@@ -7585,6 +7772,11 @@ async function runCodeReview(deps, params) {
|
|
|
7585
7772
|
content: testMetricsContent,
|
|
7586
7773
|
priority: "important"
|
|
7587
7774
|
},
|
|
7775
|
+
{
|
|
7776
|
+
name: "scope_analysis",
|
|
7777
|
+
content: scopeAnalysisContent,
|
|
7778
|
+
priority: "optional"
|
|
7779
|
+
},
|
|
7588
7780
|
{
|
|
7589
7781
|
name: "previous_findings",
|
|
7590
7782
|
content: previousFindingsContent,
|
|
@@ -9478,20 +9670,21 @@ function verifyContracts(declarations, projectRoot) {
|
|
|
9478
9670
|
const mismatches = [];
|
|
9479
9671
|
for (const exp of exports$1) {
|
|
9480
9672
|
if (!exp.filePath) continue;
|
|
9481
|
-
const
|
|
9673
|
+
const cleanPath = exp.filePath.replace(/`/g, "");
|
|
9674
|
+
const absPath = join$1(projectRoot, cleanPath);
|
|
9482
9675
|
if (!existsSync(absPath)) {
|
|
9483
9676
|
const importers = imports.filter((i) => i.contractName === exp.contractName);
|
|
9484
9677
|
if (importers.length > 0) for (const imp of importers) mismatches.push({
|
|
9485
9678
|
exporter: exp.storyKey,
|
|
9486
9679
|
importer: imp.storyKey,
|
|
9487
9680
|
contractName: exp.contractName,
|
|
9488
|
-
mismatchDescription: `Exported file not found: ${
|
|
9681
|
+
mismatchDescription: `Exported file not found: ${cleanPath}`
|
|
9489
9682
|
});
|
|
9490
9683
|
else mismatches.push({
|
|
9491
9684
|
exporter: exp.storyKey,
|
|
9492
9685
|
importer: null,
|
|
9493
9686
|
contractName: exp.contractName,
|
|
9494
|
-
mismatchDescription: `Exported file not found: ${
|
|
9687
|
+
mismatchDescription: `Exported file not found: ${cleanPath}`
|
|
9495
9688
|
});
|
|
9496
9689
|
}
|
|
9497
9690
|
}
|
|
@@ -12914,6 +13107,16 @@ function createImplementationOrchestrator(deps) {
|
|
|
12914
13107
|
modifiedInterfaces: icResult.modifiedInterfaces,
|
|
12915
13108
|
potentiallyAffectedTests: icResult.potentiallyAffectedTests
|
|
12916
13109
|
});
|
|
13110
|
+
if (config.pipelineRunId !== void 0) createDecision(db, {
|
|
13111
|
+
pipeline_run_id: config.pipelineRunId,
|
|
13112
|
+
phase: "implementation",
|
|
13113
|
+
category: "INTERFACE_WARNING",
|
|
13114
|
+
key: `${storyKey}:${config.pipelineRunId}`,
|
|
13115
|
+
value: JSON.stringify({
|
|
13116
|
+
modifiedInterfaces: icResult.modifiedInterfaces,
|
|
13117
|
+
potentiallyAffectedTests: icResult.potentiallyAffectedTests
|
|
13118
|
+
})
|
|
13119
|
+
}).catch(() => {});
|
|
12917
13120
|
}
|
|
12918
13121
|
}
|
|
12919
13122
|
} catch {}
|
|
@@ -13197,11 +13400,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
13197
13400
|
phase: "COMPLETE",
|
|
13198
13401
|
completedAt: new Date().toISOString()
|
|
13199
13402
|
});
|
|
13200
|
-
|
|
13201
|
-
await
|
|
13403
|
+
const completedReviewCycles = reviewCycles + 1;
|
|
13404
|
+
await writeStoryMetricsBestEffort(storyKey, verdict, completedReviewCycles);
|
|
13405
|
+
await writeStoryOutcomeBestEffort(storyKey, "complete", completedReviewCycles);
|
|
13202
13406
|
eventBus.emit("orchestrator:story-complete", {
|
|
13203
13407
|
storyKey,
|
|
13204
|
-
reviewCycles
|
|
13408
|
+
reviewCycles: completedReviewCycles
|
|
13205
13409
|
});
|
|
13206
13410
|
await persistState();
|
|
13207
13411
|
if (verdict === "LGTM_WITH_NOTES" && reviewResult.notes && config.pipelineRunId) try {
|
|
@@ -18134,6 +18338,7 @@ async function runResearchPhase(deps, params) {
|
|
|
18134
18338
|
|
|
18135
18339
|
//#endregion
|
|
18136
18340
|
//#region src/modules/telemetry/mesh-reporter.ts
|
|
18341
|
+
const logger$2 = createLogger("mesh-reporter");
|
|
18137
18342
|
/** Lazy-loaded substrate version (avoids module-load-time side effects that break test mocking). */
|
|
18138
18343
|
let _cachedVersion;
|
|
18139
18344
|
function getSubstrateVersion() {
|
|
@@ -18147,7 +18352,68 @@ function getSubstrateVersion() {
|
|
|
18147
18352
|
}
|
|
18148
18353
|
return _cachedVersion;
|
|
18149
18354
|
}
|
|
18150
|
-
|
|
18355
|
+
async function loadVerificationResults(runId, runsDir) {
|
|
18356
|
+
const results = {};
|
|
18357
|
+
try {
|
|
18358
|
+
const manifest = RunManifest.open(runId, runsDir);
|
|
18359
|
+
const data = await manifest.read();
|
|
18360
|
+
if (data?.per_story_state) for (const [storyKey, state] of Object.entries(data.per_story_state)) {
|
|
18361
|
+
const vr = state["verification_result"];
|
|
18362
|
+
if (vr) results[storyKey] = {
|
|
18363
|
+
status: vr.status ?? "pass",
|
|
18364
|
+
checks: (vr.checks ?? []).map((c) => ({
|
|
18365
|
+
checkName: c.checkName,
|
|
18366
|
+
status: c.status,
|
|
18367
|
+
...c.details !== void 0 && { details: c.details },
|
|
18368
|
+
...c.duration_ms !== void 0 && { durationMs: c.duration_ms }
|
|
18369
|
+
}))
|
|
18370
|
+
};
|
|
18371
|
+
}
|
|
18372
|
+
} catch {
|
|
18373
|
+
logger$2.debug({ runId }, "Could not read RunManifest for verification results — skipping");
|
|
18374
|
+
}
|
|
18375
|
+
return results;
|
|
18376
|
+
}
|
|
18377
|
+
async function loadEfficiencyScores(adapter, storyKeys) {
|
|
18378
|
+
const scores = {};
|
|
18379
|
+
if (storyKeys.length === 0) return scores;
|
|
18380
|
+
try {
|
|
18381
|
+
for (const key of storyKeys) {
|
|
18382
|
+
const rows = await adapter.query("SELECT composite_score FROM efficiency_scores WHERE story_key = ? ORDER BY timestamp DESC LIMIT 1", [key]);
|
|
18383
|
+
if (rows.length > 0 && rows[0] !== void 0) scores[key] = rows[0].composite_score;
|
|
18384
|
+
}
|
|
18385
|
+
} catch {
|
|
18386
|
+
logger$2.debug("Could not query efficiency_scores table — skipping");
|
|
18387
|
+
}
|
|
18388
|
+
return scores;
|
|
18389
|
+
}
|
|
18390
|
+
async function loadContractVerification(adapter, runId) {
|
|
18391
|
+
try {
|
|
18392
|
+
const rows = await adapter.query(`SELECT key, value FROM decisions WHERE pipeline_run_id = ? AND category = 'interface-contract'`, [runId]);
|
|
18393
|
+
if (rows.length === 0) return void 0;
|
|
18394
|
+
const mismatches = [];
|
|
18395
|
+
let verified = 0;
|
|
18396
|
+
for (const row of rows) try {
|
|
18397
|
+
const parsed = JSON.parse(row.value);
|
|
18398
|
+
if (parsed.verdict === "fail" && parsed.contractName && parsed.mismatchDescription) mismatches.push({
|
|
18399
|
+
exporter: parsed.exporter ?? row.key,
|
|
18400
|
+
importer: parsed.importer ?? null,
|
|
18401
|
+
contractName: parsed.contractName,
|
|
18402
|
+
mismatchDescription: parsed.mismatchDescription
|
|
18403
|
+
});
|
|
18404
|
+
else verified++;
|
|
18405
|
+
} catch {}
|
|
18406
|
+
return {
|
|
18407
|
+
verified,
|
|
18408
|
+
mismatches: mismatches.length,
|
|
18409
|
+
verdict: mismatches.length > 0 ? "fail" : "pass",
|
|
18410
|
+
...mismatches.length > 0 && { details: mismatches }
|
|
18411
|
+
};
|
|
18412
|
+
} catch {
|
|
18413
|
+
logger$2.debug("Could not query contract verification decisions — skipping");
|
|
18414
|
+
return void 0;
|
|
18415
|
+
}
|
|
18416
|
+
}
|
|
18151
18417
|
async function buildRunReport(adapter, runId, opts) {
|
|
18152
18418
|
const runMetrics = await getRunMetrics(adapter, runId);
|
|
18153
18419
|
if (!runMetrics) {
|
|
@@ -18155,11 +18421,46 @@ async function buildRunReport(adapter, runId, opts) {
|
|
|
18155
18421
|
return null;
|
|
18156
18422
|
}
|
|
18157
18423
|
const storyMetrics = await getStoryMetricsForRun(adapter, runId);
|
|
18424
|
+
const storyKeys = storyMetrics.map((s$1) => s$1.story_key);
|
|
18425
|
+
const dbDir = opts.projectRoot ? join$1(opts.projectRoot, ".substrate") : ".substrate";
|
|
18426
|
+
const runsDir = join$1(dbDir, "runs");
|
|
18427
|
+
const verificationResults = await loadVerificationResults(runId, runsDir);
|
|
18428
|
+
const efficiencyScores = await loadEfficiencyScores(adapter, storyKeys);
|
|
18429
|
+
const contractVerification = await loadContractVerification(adapter, runId);
|
|
18430
|
+
const warnings = [];
|
|
18431
|
+
try {
|
|
18432
|
+
const warnRows = await adapter.query(`SELECT key, value FROM decisions WHERE pipeline_run_id = ? AND category = 'INTERFACE_WARNING'`, [runId]);
|
|
18433
|
+
for (const row of warnRows) try {
|
|
18434
|
+
const parsed = JSON.parse(row.value);
|
|
18435
|
+
const storyKey = row.key.split(":")[0] ?? "unknown";
|
|
18436
|
+
const ifaces = parsed.modifiedInterfaces?.join(", ") ?? "";
|
|
18437
|
+
warnings.push(`${storyKey}: modified interfaces [${ifaces}]`);
|
|
18438
|
+
} catch {}
|
|
18439
|
+
} catch {
|
|
18440
|
+
logger$2.debug("Could not query INTERFACE_WARNING decisions — skipping");
|
|
18441
|
+
}
|
|
18442
|
+
const testExpansions = {};
|
|
18443
|
+
try {
|
|
18444
|
+
const expRows = await adapter.query(`SELECT key, value FROM decisions WHERE pipeline_run_id = ? AND category = 'TEST_EXPANSION_FINDING'`, [runId]);
|
|
18445
|
+
for (const row of expRows) try {
|
|
18446
|
+
const parsed = JSON.parse(row.value);
|
|
18447
|
+
const storyKey = row.key.split(":")[0] ?? "unknown";
|
|
18448
|
+
testExpansions[storyKey] = {
|
|
18449
|
+
priority: parsed.expansion_priority ?? "unknown",
|
|
18450
|
+
gapCount: parsed.coverage_gaps?.length ?? 0
|
|
18451
|
+
};
|
|
18452
|
+
} catch {}
|
|
18453
|
+
} catch {
|
|
18454
|
+
logger$2.debug("Could not query TEST_EXPANSION_FINDING decisions — skipping");
|
|
18455
|
+
}
|
|
18456
|
+
for (const [storyKey, exp] of Object.entries(testExpansions)) if (exp.gapCount > 0) warnings.push(`${storyKey}: test expansion found ${exp.gapCount} coverage gap(s), priority=${exp.priority}`);
|
|
18158
18457
|
const stories = storyMetrics.map((s$1) => {
|
|
18159
18458
|
let phaseDurations;
|
|
18160
18459
|
if (s$1.phase_durations_json) try {
|
|
18161
18460
|
phaseDurations = JSON.parse(s$1.phase_durations_json);
|
|
18162
18461
|
} catch {}
|
|
18462
|
+
const vr = verificationResults[s$1.story_key];
|
|
18463
|
+
const qualityScore = efficiencyScores[s$1.story_key];
|
|
18163
18464
|
return {
|
|
18164
18465
|
storyKey: s$1.story_key,
|
|
18165
18466
|
result: s$1.result,
|
|
@@ -18169,9 +18470,16 @@ async function buildRunReport(adapter, runId, opts) {
|
|
|
18169
18470
|
costUsd: s$1.cost_usd,
|
|
18170
18471
|
reviewCycles: s$1.review_cycles,
|
|
18171
18472
|
dispatches: s$1.dispatches,
|
|
18172
|
-
...phaseDurations !== void 0 && { phaseDurations }
|
|
18473
|
+
...phaseDurations !== void 0 && { phaseDurations },
|
|
18474
|
+
...vr !== void 0 && {
|
|
18475
|
+
verificationStatus: vr.status,
|
|
18476
|
+
verificationChecks: vr.checks
|
|
18477
|
+
},
|
|
18478
|
+
...qualityScore !== void 0 && { qualityScore }
|
|
18173
18479
|
};
|
|
18174
18480
|
});
|
|
18481
|
+
const storyScores = stories.map((s$1) => s$1.qualityScore).filter((s$1) => s$1 !== void 0);
|
|
18482
|
+
const avgEfficiencyScore = storyScores.length > 0 ? Math.round(storyScores.reduce((a, b) => a + b, 0) / storyScores.length) : void 0;
|
|
18175
18483
|
const rawStatus = runMetrics.status.toLowerCase();
|
|
18176
18484
|
const status = rawStatus === "completed" ? "completed" : rawStatus === "failed" ? "failed" : "partial";
|
|
18177
18485
|
const projectId = opts.projectId ?? (opts.projectRoot ? basename$1(opts.projectRoot) : "unknown");
|
|
@@ -18193,6 +18501,9 @@ async function buildRunReport(adapter, runId, opts) {
|
|
|
18193
18501
|
totalDispatches: runMetrics.total_dispatches,
|
|
18194
18502
|
restarts: runMetrics.restarts,
|
|
18195
18503
|
stories,
|
|
18504
|
+
...contractVerification !== void 0 && { contractVerification },
|
|
18505
|
+
...warnings.length > 0 && { warnings },
|
|
18506
|
+
...avgEfficiencyScore !== void 0 && { efficiencyScore: avgEfficiencyScore },
|
|
18196
18507
|
agentBackend: opts.agentBackend ?? "claude-code",
|
|
18197
18508
|
engineType: opts.engineType ?? "linear",
|
|
18198
18509
|
concurrency: opts.concurrency ?? runMetrics.concurrency_setting
|
|
@@ -42266,6 +42577,13 @@ async function runFullPipeline(options) {
|
|
|
42266
42577
|
runId
|
|
42267
42578
|
});
|
|
42268
42579
|
process.stdout.write(summary + "\n");
|
|
42580
|
+
if (fpMeshUrl !== void 0) await reportToMesh(adapter, runId, fpMeshUrl, {
|
|
42581
|
+
projectId: fpMeshProjectId,
|
|
42582
|
+
projectRoot,
|
|
42583
|
+
agentBackend: agentId ?? "claude-code",
|
|
42584
|
+
engineType: fpEngineType ?? "linear",
|
|
42585
|
+
concurrency
|
|
42586
|
+
});
|
|
42269
42587
|
return 0;
|
|
42270
42588
|
}
|
|
42271
42589
|
}
|
|
@@ -42375,4 +42693,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
42375
42693
|
|
|
42376
42694
|
//#endregion
|
|
42377
42695
|
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 };
|
|
42378
|
-
//# sourceMappingURL=run-
|
|
42696
|
+
//# sourceMappingURL=run-BG1vEtOA.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "./health-
|
|
1
|
+
import "./health-Ckl2VOPW.js";
|
|
2
2
|
import "./logger-KeHncl-f.js";
|
|
3
3
|
import "./helpers-CElYrONe.js";
|
|
4
4
|
import "./dist-6ETBV23_.js";
|
|
5
|
-
import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-
|
|
5
|
+
import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-BG1vEtOA.js";
|
|
6
6
|
import "./routing-CcBOCuC9.js";
|
|
7
7
|
import "./decisions-C0pz9Clx.js";
|
|
8
8
|
|
package/package.json
CHANGED
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
### Prior Run Findings
|
|
20
20
|
{{prior_findings}}
|
|
21
21
|
|
|
22
|
+
<!-- scope_analysis -->
|
|
23
|
+
{{scope_analysis}}
|
|
24
|
+
|
|
22
25
|
---
|
|
23
26
|
|
|
24
27
|
## Mission
|
|
@@ -39,12 +42,13 @@ Adversarial code review. Find what's wrong. Validate story claims against actual
|
|
|
39
42
|
|
|
40
43
|
3. **Build AC Checklist** — For each acceptance criterion (AC1, AC2, ...) in the story, determine: `met` (code implements it), `not_met` (code does not implement it), or `partial` (partially implemented). Cite the specific file and function as evidence.
|
|
41
44
|
|
|
42
|
-
4. **Execute adversarial review** across
|
|
45
|
+
4. **Execute adversarial review** across 6 dimensions:
|
|
43
46
|
- **AC Validation** — Is each acceptance criterion implemented?
|
|
44
47
|
- **AC-to-Test Traceability** — For each AC, identify the specific test file and test function that validates it. If an AC has no corresponding test evidence, flag it as a major issue: "AC{N} has no test evidence". A test "covers" an AC if it directly exercises the behavior described in the criterion — tangential tests do not count.
|
|
45
48
|
- **Task Audit** — Tasks marked `[x]` that aren't done are BLOCKER issues
|
|
46
49
|
- **Code Quality** — Security, error handling, edge cases, maintainability
|
|
47
50
|
- **Test Quality** — Real assertions, not placeholders or skipped tests
|
|
51
|
+
- **Scope Compliance** — Compare files in the git diff against the expected file set from the story spec's "Key File Paths", "File Paths to Create", "File Paths to Modify", and "Tasks / Subtasks" sections. Any non-test file created or modified that does not appear in that expected set should be recorded as a `scope-creep` finding with `category: scope-creep` and `severity: minor`. **Test files (paths containing `.test.ts`, `.spec.ts`, `__tests__/`, or `__mocks__/`) are always exempt from scope checking — do not flag them, regardless of whether they appear in the story spec.** If a pre-computed scope analysis is provided in the `scope_analysis` section above, use it as ground truth — do not re-parse the story spec manually. **ADVISORY ONLY: Scope-creep findings are informational. If the only issues found are `scope-creep` entries, the verdict must be SHIP_IT or LGTM_WITH_NOTES — scope-creep findings do not independently trigger NEEDS_MINOR_FIXES or NEEDS_MAJOR_REWORK.**
|
|
48
52
|
|
|
49
53
|
5. **Severity classification:**
|
|
50
54
|
- **blocker** — Task `[x]` but not implemented; security vulnerability; data loss risk
|
|
@@ -85,6 +89,10 @@ issue_list:
|
|
|
85
89
|
description: "Variable name `d` should be more descriptive"
|
|
86
90
|
file: "src/modules/foo/foo.ts"
|
|
87
91
|
line: 15
|
|
92
|
+
- severity: minor
|
|
93
|
+
category: scope-creep
|
|
94
|
+
description: "File src/modules/foo/extra.ts was created but not listed in the story spec's expected file set"
|
|
95
|
+
file: "src/modules/foo/extra.ts"
|
|
88
96
|
ac_checklist:
|
|
89
97
|
- ac_id: AC1
|
|
90
98
|
status: met
|