substrate-ai 0.20.29 → 0.20.32
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-Dx9hm9x1.js → health-DJ4z2uWN.js} +253 -29
- package/dist/{health-C6x1jDlX.js → health-DLqMd9uN.js} +1 -1
- package/dist/{run-BQQ0QILZ.js → run-D-OcJNtZ.js} +2 -2
- package/dist/{run-D4WYiyK8.js → run-DGyWCmcu.js} +1061 -419
- package/package.json +1 -1
- package/packs/bmad/prompts/dev-story.md +2 -0
- package/packs/bmad/prompts/probe-author.md +154 -0
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, ZERO_FINDING_COUNTS, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts } from "../health-
|
|
2
|
+
import { FileStateStore, RunManifest, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDING_COUNTS, buildPipelineStatusOutput, createDatabaseAdapter, createStateStore, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, parseDbTimestampAsUtc, registerHealthCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts } from "../health-DJ4z2uWN.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-CqtWS9wF.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-DGyWCmcu.js";
|
|
8
8
|
import "../errors-1uLGqnvr.js";
|
|
9
9
|
import "../routing-CcBOCuC9.js";
|
|
10
10
|
import "../decisions-C0pz9Clx.js";
|
|
@@ -3667,7 +3667,7 @@ async function runStatusAction(options) {
|
|
|
3667
3667
|
logger$12.debug({ err }, "Work graph query failed, continuing without work graph data");
|
|
3668
3668
|
}
|
|
3669
3669
|
if (run === void 0) {
|
|
3670
|
-
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-
|
|
3670
|
+
const { inspectProcessTree: inspectProcessTree$1 } = await import("../health-DLqMd9uN.js");
|
|
3671
3671
|
const substrateDirPath = join(projectRoot, ".substrate");
|
|
3672
3672
|
const processInfo = inspectProcessTree$1({
|
|
3673
3673
|
projectRoot,
|
|
@@ -5198,7 +5198,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
5198
5198
|
await initSchema(expAdapter);
|
|
5199
5199
|
const { runRunAction: runPipeline } = await import(
|
|
5200
5200
|
/* @vite-ignore */
|
|
5201
|
-
"../run-
|
|
5201
|
+
"../run-D-OcJNtZ.js"
|
|
5202
5202
|
);
|
|
5203
5203
|
const runStoryFn = async (opts) => {
|
|
5204
5204
|
const exitCode = await runPipeline({
|
|
@@ -7,6 +7,7 @@ import { EventEmitter } from "node:events";
|
|
|
7
7
|
import { YAMLException, load } from "js-yaml";
|
|
8
8
|
import { existsSync, promises, readFileSync, readdirSync, statSync } from "node:fs";
|
|
9
9
|
import { spawn, spawnSync } from "node:child_process";
|
|
10
|
+
import * as path$1 from "node:path";
|
|
10
11
|
import { basename as basename$1, dirname as dirname$1, join as join$1, resolve as resolve$1 } from "node:path";
|
|
11
12
|
import { z } from "zod";
|
|
12
13
|
import { mkdir as mkdir$1, open, readFile as readFile$1, unlink, writeFile as writeFile$1 } from "node:fs/promises";
|
|
@@ -480,20 +481,20 @@ function detectCycles(edges) {
|
|
|
480
481
|
}
|
|
481
482
|
const visited = new Set();
|
|
482
483
|
const visiting = new Set();
|
|
483
|
-
const path$
|
|
484
|
+
const path$2 = [];
|
|
484
485
|
function dfs(node) {
|
|
485
486
|
if (visiting.has(node)) {
|
|
486
|
-
const cycleStart = path$
|
|
487
|
-
return [...path$
|
|
487
|
+
const cycleStart = path$2.indexOf(node);
|
|
488
|
+
return [...path$2.slice(cycleStart), node];
|
|
488
489
|
}
|
|
489
490
|
if (visited.has(node)) return null;
|
|
490
491
|
visiting.add(node);
|
|
491
|
-
path$
|
|
492
|
+
path$2.push(node);
|
|
492
493
|
for (const neighbor of adj.get(node) ?? []) {
|
|
493
494
|
const cycle = dfs(neighbor);
|
|
494
495
|
if (cycle !== null) return cycle;
|
|
495
496
|
}
|
|
496
|
-
path$
|
|
497
|
+
path$2.pop();
|
|
497
498
|
visiting.delete(node);
|
|
498
499
|
visited.add(node);
|
|
499
500
|
return null;
|
|
@@ -3043,6 +3044,21 @@ var TrivialOutputCheck = class {
|
|
|
3043
3044
|
}
|
|
3044
3045
|
const count = context.outputTokenCount;
|
|
3045
3046
|
if (count < this.threshold) {
|
|
3047
|
+
const devResult = context.devStoryResult;
|
|
3048
|
+
const recoveredAfterCheckpoint = devResult?.result === "success" && Array.isArray(devResult.files_modified) && devResult.files_modified.length > 0;
|
|
3049
|
+
if (recoveredAfterCheckpoint) {
|
|
3050
|
+
const findings$1 = [{
|
|
3051
|
+
category: "trivial-output",
|
|
3052
|
+
severity: "warn",
|
|
3053
|
+
message: `output token count ${count} is below threshold ${this.threshold} but dev-story signals success with ${devResult.files_modified.length} files modified — likely checkpoint-recovered dispatch (last dispatch was bookkeeping; earlier dispatches did the work). Verdict downgraded to warn so dispatches that legitimately recovered from checkpoint aren't blocked by the trivial-output gate.`
|
|
3054
|
+
}];
|
|
3055
|
+
return {
|
|
3056
|
+
status: "warn",
|
|
3057
|
+
details: renderFindings(findings$1),
|
|
3058
|
+
duration_ms: Date.now() - start,
|
|
3059
|
+
findings: findings$1
|
|
3060
|
+
};
|
|
3061
|
+
}
|
|
3046
3062
|
const findings = [{
|
|
3047
3063
|
category: "trivial-output",
|
|
3048
3064
|
severity: "error",
|
|
@@ -3090,12 +3106,36 @@ function addExplicitAcRefs(text, ids) {
|
|
|
3090
3106
|
}
|
|
3091
3107
|
function extractAcceptanceSection(storyContent) {
|
|
3092
3108
|
const lines = storyContent.split(/\r?\n/);
|
|
3093
|
-
|
|
3109
|
+
let mode;
|
|
3110
|
+
const start = lines.findIndex((line) => {
|
|
3111
|
+
const trimmed = line.trim();
|
|
3112
|
+
if (/^##\s+Acceptance Criteria\s*$/i.test(trimmed)) {
|
|
3113
|
+
mode = "heading";
|
|
3114
|
+
return true;
|
|
3115
|
+
}
|
|
3116
|
+
if (/^\*\*Acceptance Criteria\*\*:?/i.test(trimmed)) {
|
|
3117
|
+
mode = "bold";
|
|
3118
|
+
return true;
|
|
3119
|
+
}
|
|
3120
|
+
return false;
|
|
3121
|
+
});
|
|
3094
3122
|
if (start === -1) return void 0;
|
|
3095
3123
|
let end = lines.length;
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3124
|
+
const BOLD_PARA_BOUNDARY = /^\*\*[A-Za-z][A-Za-z\s]*\*\*:/;
|
|
3125
|
+
for (let i = start + 1; i < lines.length; i += 1) {
|
|
3126
|
+
const line = lines[i] ?? "";
|
|
3127
|
+
if (/^##\s+\S/.test(line)) {
|
|
3128
|
+
end = i;
|
|
3129
|
+
break;
|
|
3130
|
+
}
|
|
3131
|
+
if (/^###\s+Story\s+/i.test(line)) {
|
|
3132
|
+
end = i;
|
|
3133
|
+
break;
|
|
3134
|
+
}
|
|
3135
|
+
if (mode === "bold" && BOLD_PARA_BOUNDARY.test(line.trim())) {
|
|
3136
|
+
end = i;
|
|
3137
|
+
break;
|
|
3138
|
+
}
|
|
3099
3139
|
}
|
|
3100
3140
|
return lines.slice(start + 1, end).join("\n");
|
|
3101
3141
|
}
|
|
@@ -3103,18 +3143,33 @@ function extractAcceptanceSection(storyContent) {
|
|
|
3103
3143
|
* Extract normalized AC ids from story markdown.
|
|
3104
3144
|
*
|
|
3105
3145
|
* Supports the BMAD default format (`### AC1:`), explicit references such as
|
|
3106
|
-
* `AC: #1`,
|
|
3146
|
+
* `AC: #1`, plain numbered criteria inside the Acceptance Criteria section,
|
|
3147
|
+
* and (Story 61-4) bullet-format ACs where each bullet line under the
|
|
3148
|
+
* Acceptance Criteria section becomes an implicit AC numbered by position
|
|
3149
|
+
* (first bullet → AC1, second → AC2, etc.). Bullet-format inference fires
|
|
3150
|
+
* only when no numbered or explicit-ref ACs were found, so projects mixing
|
|
3151
|
+
* conventions favor the explicit signal.
|
|
3107
3152
|
*/
|
|
3108
3153
|
function extractAcceptanceCriteriaIds(storyContent) {
|
|
3109
3154
|
const ids = new Set();
|
|
3110
3155
|
const acceptanceSection = extractAcceptanceSection(storyContent);
|
|
3111
3156
|
const textToScan = acceptanceSection ?? storyContent;
|
|
3112
3157
|
addExplicitAcRefs(textToScan, ids);
|
|
3113
|
-
if (acceptanceSection !== void 0)
|
|
3114
|
-
const
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3158
|
+
if (acceptanceSection !== void 0) {
|
|
3159
|
+
for (const line of acceptanceSection.split(/\r?\n/)) {
|
|
3160
|
+
const match = line.match(NUMBERED_CRITERION);
|
|
3161
|
+
if (match?.[1] !== void 0) {
|
|
3162
|
+
const id = normalizeAcId(match[1]);
|
|
3163
|
+
if (id !== void 0) ids.add(id);
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
if (ids.size === 0) {
|
|
3167
|
+
let bulletPosition = 0;
|
|
3168
|
+
for (const line of acceptanceSection.split(/\r?\n/)) if (/^\s*[-*]\s+\S/.test(line) && !NUMBERED_CRITERION.test(line)) {
|
|
3169
|
+
bulletPosition += 1;
|
|
3170
|
+
const id = normalizeAcId(String(bulletPosition));
|
|
3171
|
+
if (id !== void 0) ids.add(id);
|
|
3172
|
+
}
|
|
3118
3173
|
}
|
|
3119
3174
|
}
|
|
3120
3175
|
return sortAcIds(ids);
|
|
@@ -3216,14 +3271,31 @@ var AcceptanceCriteriaEvidenceCheck = class {
|
|
|
3216
3271
|
const claimedIds = new Set(extractClaimedAcceptanceCriteriaIds(devResult.ac_met));
|
|
3217
3272
|
const missingIds = expectedIds.filter((id) => !claimedIds.has(id));
|
|
3218
3273
|
if (missingIds.length > 0) {
|
|
3274
|
+
const acceptanceTexts = extractAcceptanceCriteriaTexts(storyContent);
|
|
3275
|
+
const filesModified = devResult.files_modified ?? [];
|
|
3219
3276
|
const claimedSummary = formatIds(sortAcIds(claimedIds)) || "none";
|
|
3220
|
-
const findings = missingIds.map((id) =>
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3277
|
+
const findings = missingIds.map((id) => {
|
|
3278
|
+
const acText = acceptanceTexts.get(id) ?? "";
|
|
3279
|
+
const evidence = findCodeEvidence({
|
|
3280
|
+
acId: id,
|
|
3281
|
+
acText,
|
|
3282
|
+
filesModified,
|
|
3283
|
+
workingDir: context.workingDir
|
|
3284
|
+
});
|
|
3285
|
+
if (evidence.found) return {
|
|
3286
|
+
category: "ac-missing-evidence-claim",
|
|
3287
|
+
severity: "info",
|
|
3288
|
+
message: `dev-story did not claim ${id} but code-evidence was found: ${evidence.reason} (expected ${formatIds(expectedIds)}, claimed ${claimedSummary})`
|
|
3289
|
+
};
|
|
3290
|
+
return {
|
|
3291
|
+
category: "ac-missing-evidence",
|
|
3292
|
+
severity: "error",
|
|
3293
|
+
message: `missing dev-story AC evidence for ${id} (expected ${formatIds(expectedIds)}, claimed ${claimedSummary})`
|
|
3294
|
+
};
|
|
3295
|
+
});
|
|
3296
|
+
const hasErrorFinding = findings.some((f) => f.severity === "error");
|
|
3225
3297
|
return {
|
|
3226
|
-
status: "fail",
|
|
3298
|
+
status: hasErrorFinding ? "fail" : "warn",
|
|
3227
3299
|
details: renderFindings(findings),
|
|
3228
3300
|
duration_ms: Date.now() - start,
|
|
3229
3301
|
findings
|
|
@@ -3250,6 +3322,155 @@ var AcceptanceCriteriaEvidenceCheck = class {
|
|
|
3250
3322
|
};
|
|
3251
3323
|
}
|
|
3252
3324
|
};
|
|
3325
|
+
/**
|
|
3326
|
+
* Build a per-AC text map (id → AC body text). Used by the code-evidence
|
|
3327
|
+
* fallback (Story 61-7) when an AC is missing from the dev's claim list —
|
|
3328
|
+
* the AC's text drives path-token extraction so we can look for evidence
|
|
3329
|
+
* that the work was done despite the under-claim.
|
|
3330
|
+
*
|
|
3331
|
+
* Resolution order per AC id:
|
|
3332
|
+
* 1. Lines in the acceptance section that explicitly mention `AC<N>` or
|
|
3333
|
+
* `AC: #<N>` are concatenated.
|
|
3334
|
+
* 2. If no explicit mention, fall back to position: the Nth bullet
|
|
3335
|
+
* (`- ...`) under the section is AC<N>'s text.
|
|
3336
|
+
* 3. If no bullets, the Nth numbered item (`<N>. ...`).
|
|
3337
|
+
*/
|
|
3338
|
+
function extractAcceptanceCriteriaTexts(storyContent) {
|
|
3339
|
+
const result = new Map();
|
|
3340
|
+
const section = extractAcceptanceSection(storyContent);
|
|
3341
|
+
if (section === void 0) return result;
|
|
3342
|
+
const lines = section.split(/\r?\n/);
|
|
3343
|
+
const explicitByNum = new Map();
|
|
3344
|
+
const explicitRefRe = /\bAC\s*:?\s*#?\s*(\d+)\b/gi;
|
|
3345
|
+
for (const line of lines) {
|
|
3346
|
+
explicitRefRe.lastIndex = 0;
|
|
3347
|
+
let m;
|
|
3348
|
+
while ((m = explicitRefRe.exec(line)) !== null) {
|
|
3349
|
+
const n = Number.parseInt(m[1] ?? "", 10);
|
|
3350
|
+
if (!Number.isFinite(n) || n <= 0) continue;
|
|
3351
|
+
const list = explicitByNum.get(n) ?? [];
|
|
3352
|
+
if (!list.includes(line)) list.push(line);
|
|
3353
|
+
explicitByNum.set(n, list);
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
const bulletLines = [];
|
|
3357
|
+
const numberedLines = [];
|
|
3358
|
+
for (const line of lines) {
|
|
3359
|
+
if (/^\s*[-*]\s+\S/.test(line) && !/^\s*(?:[-*]\s*)?(?:\[[ xX]\]\s*)?(\d+)[.)]\s+\S/.test(line)) bulletLines.push(line);
|
|
3360
|
+
if (/^\s*(?:[-*]\s*)?(?:\[[ xX]\]\s*)?(\d+)[.)]\s+\S/.test(line)) numberedLines.push(line);
|
|
3361
|
+
}
|
|
3362
|
+
const allNums = new Set([...Array.from(explicitByNum.keys()), ...Array.from({ length: Math.max(bulletLines.length, numberedLines.length) }, (_, i) => i + 1)]);
|
|
3363
|
+
for (const n of allNums) {
|
|
3364
|
+
const explicit = explicitByNum.get(n);
|
|
3365
|
+
if (explicit && explicit.length > 0) {
|
|
3366
|
+
result.set(`AC${n}`, explicit.join("\n"));
|
|
3367
|
+
continue;
|
|
3368
|
+
}
|
|
3369
|
+
if (n - 1 < bulletLines.length) {
|
|
3370
|
+
result.set(`AC${n}`, bulletLines[n - 1] ?? "");
|
|
3371
|
+
continue;
|
|
3372
|
+
}
|
|
3373
|
+
if (n - 1 < numberedLines.length) result.set(`AC${n}`, numberedLines[n - 1] ?? "");
|
|
3374
|
+
}
|
|
3375
|
+
return result;
|
|
3376
|
+
}
|
|
3377
|
+
/**
|
|
3378
|
+
* Recognized source/test extensions for path-token extraction. Mirrors
|
|
3379
|
+
* scope-guardrail.ts's RECOGNIZED_EXTENSIONS but lives here to avoid a
|
|
3380
|
+
* cross-package import (sdlc cannot depend on src/modules).
|
|
3381
|
+
*/
|
|
3382
|
+
const PATH_TOKEN_EXTENSIONS = [
|
|
3383
|
+
".ts",
|
|
3384
|
+
".tsx",
|
|
3385
|
+
".js",
|
|
3386
|
+
".jsx",
|
|
3387
|
+
".md",
|
|
3388
|
+
".json",
|
|
3389
|
+
".yaml",
|
|
3390
|
+
".yml",
|
|
3391
|
+
".py",
|
|
3392
|
+
".go",
|
|
3393
|
+
".java",
|
|
3394
|
+
".rb",
|
|
3395
|
+
".rs",
|
|
3396
|
+
".sh",
|
|
3397
|
+
".css",
|
|
3398
|
+
".scss",
|
|
3399
|
+
".html"
|
|
3400
|
+
];
|
|
3401
|
+
/**
|
|
3402
|
+
* Extract path-like tokens from arbitrary text. A path-like token contains
|
|
3403
|
+
* `/`, has a recognized extension, and contains no whitespace.
|
|
3404
|
+
*/
|
|
3405
|
+
function extractPathTokens(text) {
|
|
3406
|
+
if (text === "") return [];
|
|
3407
|
+
const tokens = new Set();
|
|
3408
|
+
const backtickRe = /`([^`]+)`/g;
|
|
3409
|
+
let m;
|
|
3410
|
+
while ((m = backtickRe.exec(text)) !== null) {
|
|
3411
|
+
const candidate = (m[1] ?? "").trim();
|
|
3412
|
+
if (looksLikePath(candidate)) tokens.add(candidate);
|
|
3413
|
+
}
|
|
3414
|
+
const stripped = text.replace(/`[^`]+`/g, " ");
|
|
3415
|
+
for (const raw of stripped.split(/\s+/)) {
|
|
3416
|
+
const clean = raw.replace(/[,;:()\[\]{}'"]+$/g, "").replace(/^[,;:()\[\]{}'"]+/g, "");
|
|
3417
|
+
if (looksLikePath(clean)) tokens.add(clean);
|
|
3418
|
+
}
|
|
3419
|
+
return Array.from(tokens);
|
|
3420
|
+
}
|
|
3421
|
+
function looksLikePath(candidate) {
|
|
3422
|
+
if (candidate === "" || /\s/.test(candidate)) return false;
|
|
3423
|
+
if (!candidate.includes("/")) return false;
|
|
3424
|
+
return PATH_TOKEN_EXTENSIONS.some((ext) => candidate.endsWith(ext));
|
|
3425
|
+
}
|
|
3426
|
+
function isTestFilePath(filePath) {
|
|
3427
|
+
return filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("__tests__/") || filePath.includes("__tests__\\") || filePath.includes("/__tests__") || filePath.includes("\\__tests__");
|
|
3428
|
+
}
|
|
3429
|
+
/**
|
|
3430
|
+
* Find code-evidence that an unclaimed AC was actually implemented.
|
|
3431
|
+
*
|
|
3432
|
+
* Returns `{ found: true, reason }` when any of the three checks succeeds:
|
|
3433
|
+
* 1. AC text mentions a file path that's in `filesModified` (exact or basename match)
|
|
3434
|
+
* 2. AC text mentions a path that exists in the working tree
|
|
3435
|
+
* 3. A test file in `filesModified` contains the AC id (e.g., `AC10`)
|
|
3436
|
+
*
|
|
3437
|
+
* Returns `{ found: false }` otherwise.
|
|
3438
|
+
*/
|
|
3439
|
+
function findCodeEvidence(opts) {
|
|
3440
|
+
const { acId, acText, filesModified, workingDir } = opts;
|
|
3441
|
+
const tokens = extractPathTokens(acText);
|
|
3442
|
+
for (const token of tokens) if (filesModified.includes(token)) return {
|
|
3443
|
+
found: true,
|
|
3444
|
+
reason: `AC text references ${token}, which is in files_modified`
|
|
3445
|
+
};
|
|
3446
|
+
for (const token of tokens) {
|
|
3447
|
+
const base = path$1.basename(token);
|
|
3448
|
+
const match = filesModified.find((f) => path$1.basename(f) === base);
|
|
3449
|
+
if (match !== void 0) return {
|
|
3450
|
+
found: true,
|
|
3451
|
+
reason: `AC text references ${token}; matching basename ${match} is in files_modified`
|
|
3452
|
+
};
|
|
3453
|
+
}
|
|
3454
|
+
for (const token of tokens) try {
|
|
3455
|
+
if (existsSync(path$1.join(workingDir, token))) return {
|
|
3456
|
+
found: true,
|
|
3457
|
+
reason: `AC text references ${token}, which exists in working tree`
|
|
3458
|
+
};
|
|
3459
|
+
} catch {}
|
|
3460
|
+
const num = acId.replace(/^AC/i, "");
|
|
3461
|
+
if (num !== "") {
|
|
3462
|
+
const acMentionRe = new RegExp(`\\bAC\\s*:?\\s*#?\\s*${num}\\b`, "i");
|
|
3463
|
+
const testFiles = filesModified.filter(isTestFilePath);
|
|
3464
|
+
for (const testFile of testFiles) try {
|
|
3465
|
+
const content = readFileSync(path$1.join(workingDir, testFile), "utf-8");
|
|
3466
|
+
if (acMentionRe.test(content)) return {
|
|
3467
|
+
found: true,
|
|
3468
|
+
reason: `${testFile} mentions ${acId}`
|
|
3469
|
+
};
|
|
3470
|
+
} catch {}
|
|
3471
|
+
}
|
|
3472
|
+
return { found: false };
|
|
3473
|
+
}
|
|
3253
3474
|
|
|
3254
3475
|
//#endregion
|
|
3255
3476
|
//#region packages/sdlc/dist/verification/checks/build-check.js
|
|
@@ -3568,11 +3789,11 @@ function parseRuntimeProbes(storyContent) {
|
|
|
3568
3789
|
const validation = RuntimeProbeListSchema.safeParse(parsed);
|
|
3569
3790
|
if (!validation.success) {
|
|
3570
3791
|
const first = validation.error.issues[0];
|
|
3571
|
-
const path$
|
|
3792
|
+
const path$2 = first?.path.join(".") ?? "";
|
|
3572
3793
|
const message = first?.message ?? "schema validation failed";
|
|
3573
3794
|
return {
|
|
3574
3795
|
kind: "invalid",
|
|
3575
|
-
error: `probe list is malformed at ${path$
|
|
3796
|
+
error: `probe list is malformed at ${path$2 || "<root>"}: ${message}`
|
|
3576
3797
|
};
|
|
3577
3798
|
}
|
|
3578
3799
|
const probes = validation.data;
|
|
@@ -3796,6 +4017,9 @@ const TRIGGER_COMMAND_PATTERNS = [
|
|
|
3796
4017
|
];
|
|
3797
4018
|
/**
|
|
3798
4019
|
* Returns true if the source AC text mentions an event-driven mechanism.
|
|
4020
|
+
*
|
|
4021
|
+
* Exported for use by probe-author-integration.ts (Story 60-13) so the
|
|
4022
|
+
* orchestrator can gate probe-author dispatch on the same heuristic.
|
|
3799
4023
|
*/
|
|
3800
4024
|
function detectsEventDrivenAC(sourceEpicContent) {
|
|
3801
4025
|
for (const pattern of EVENT_DRIVEN_KEYWORDS) if (pattern.test(sourceEpicContent)) return true;
|
|
@@ -3988,18 +4212,18 @@ function existsAnywhereUnderRoot(root, base) {
|
|
|
3988
4212
|
depth: 0
|
|
3989
4213
|
}];
|
|
3990
4214
|
while (stack.length > 0) {
|
|
3991
|
-
const { path: path$
|
|
4215
|
+
const { path: path$2, depth } = stack.pop();
|
|
3992
4216
|
if (depth > MAX_WALK_DEPTH) continue;
|
|
3993
4217
|
let entries;
|
|
3994
4218
|
try {
|
|
3995
|
-
entries = readdirSync(path$
|
|
4219
|
+
entries = readdirSync(path$2);
|
|
3996
4220
|
} catch {
|
|
3997
4221
|
continue;
|
|
3998
4222
|
}
|
|
3999
4223
|
for (const entry of entries) {
|
|
4000
4224
|
if (SKIP_DIRS.has(entry)) continue;
|
|
4001
4225
|
if (entry === base) return true;
|
|
4002
|
-
const full = join$1(path$
|
|
4226
|
+
const full = join$1(path$2, entry);
|
|
4003
4227
|
try {
|
|
4004
4228
|
const s = statSync(full);
|
|
4005
4229
|
if (s.isDirectory()) stack.push({
|
|
@@ -5632,7 +5856,7 @@ function inspectProcessTree(opts) {
|
|
|
5632
5856
|
}
|
|
5633
5857
|
const lines = psOutput.split("\n");
|
|
5634
5858
|
if (substrateDirPath !== void 0) try {
|
|
5635
|
-
const readFileSyncFn = readFileSyncOverride ?? ((path$
|
|
5859
|
+
const readFileSyncFn = readFileSyncOverride ?? ((path$2, encoding) => readFileSync(path$2, encoding));
|
|
5636
5860
|
const pidContent = readFileSyncFn(join(substrateDirPath, "orchestrator.pid"), "utf-8");
|
|
5637
5861
|
const pid = parseInt(pidContent.trim(), 10);
|
|
5638
5862
|
if (!isNaN(pid) && pid > 0) {
|
|
@@ -6066,5 +6290,5 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
6066
6290
|
}
|
|
6067
6291
|
|
|
6068
6292
|
//#endregion
|
|
6069
|
-
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, ZERO_FINDING_COUNTS, __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, rollupFindingCounts, runHealthAction, validateStoryKey };
|
|
6070
|
-
//# sourceMappingURL=health-
|
|
6293
|
+
export { BMAD_BASELINE_TOKENS_FULL, DEFAULT_STALL_THRESHOLD_SECONDS, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, RuntimeProbeListSchema, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN$1 as STORY_KEY_PATTERN, SUBSTRATE_OWNED_SETTINGS_KEYS, SupervisorLock, VALID_PHASES, WorkGraphRepository, ZERO_FINDING_COUNTS, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter$1 as createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, createStateStore, detectCycles, detectsEventDrivenAC, extractTargetFilesFromStoryContent, findPackageRoot, formatOutput, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, inspectProcessTree, isOrchestratorProcessLine, parseDbTimestampAsUtc, registerHealthCommand, renderFindings, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveGraphPath, resolveMainRepoRoot, resolveRunManifest, rollupFindingCounts, runHealthAction, validateStoryKey };
|
|
6294
|
+
//# sourceMappingURL=health-DJ4z2uWN.js.map
|
|
@@ -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-DJ4z2uWN.js";
|
|
2
2
|
import "./logger-KeHncl-f.js";
|
|
3
3
|
import "./dist-CqtWS9wF.js";
|
|
4
4
|
import "./decisions-C0pz9Clx.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "./health-
|
|
1
|
+
import "./health-DJ4z2uWN.js";
|
|
2
2
|
import "./logger-KeHncl-f.js";
|
|
3
3
|
import "./helpers-CElYrONe.js";
|
|
4
4
|
import "./dist-CqtWS9wF.js";
|
|
5
|
-
import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-
|
|
5
|
+
import { normalizeGraphSummaryToStatus, registerRunCommand, resolveMaxReviewCycles, runRunAction, wireNdjsonEmitter } from "./run-DGyWCmcu.js";
|
|
6
6
|
import "./routing-CcBOCuC9.js";
|
|
7
7
|
import "./decisions-C0pz9Clx.js";
|
|
8
8
|
|