substrate-ai 0.20.8 → 0.20.12
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 +15 -7
- package/dist/{health-DXW-E2vb.js → health-0jqPFBEL.js} +236 -45
- package/dist/{health-Q8-69N9P.js → health-C401hYzi.js} +1 -1
- package/dist/{run-Bz3dW9Wn.js → run-B6qbsy-F.js} +108 -90
- package/dist/{run-C3kaptjF.js → run-DEyd1p7U.js} +2 -2
- package/package.json +1 -1
- package/packs/bmad/prompts/create-story.md +14 -9
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-0jqPFBEL.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-B6qbsy-F.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-C401hYzi.js");
|
|
3671
3671
|
const substrateDirPath = join(projectRoot, ".substrate");
|
|
3672
3672
|
const processInfo = inspectProcessTree$1({
|
|
3673
3673
|
projectRoot,
|
|
@@ -3712,6 +3712,7 @@ async function runStatusAction(options) {
|
|
|
3712
3712
|
} catch {}
|
|
3713
3713
|
const verificationResult = manifestPerStoryState?.[row.story_key]?.verification_result;
|
|
3714
3714
|
const verificationFindings = rollupFindingCounts(verificationResult);
|
|
3715
|
+
const verificationRan = verificationResult !== void 0 && verificationResult !== null;
|
|
3715
3716
|
return {
|
|
3716
3717
|
story_key: row.story_key,
|
|
3717
3718
|
result: row.result,
|
|
@@ -3723,7 +3724,8 @@ async function runStatusAction(options) {
|
|
|
3723
3724
|
},
|
|
3724
3725
|
review_cycles: row.review_cycles ?? 0,
|
|
3725
3726
|
dispatches: row.dispatches ?? 0,
|
|
3726
|
-
verification_findings: verificationFindings
|
|
3727
|
+
verification_findings: verificationFindings,
|
|
3728
|
+
verification_ran: verificationRan
|
|
3727
3729
|
};
|
|
3728
3730
|
});
|
|
3729
3731
|
let pipelineWallClockMs = 0;
|
|
@@ -5196,7 +5198,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
5196
5198
|
await initSchema(expAdapter);
|
|
5197
5199
|
const { runRunAction: runPipeline } = await import(
|
|
5198
5200
|
/* @vite-ignore */
|
|
5199
|
-
"../run-
|
|
5201
|
+
"../run-DEyd1p7U.js"
|
|
5200
5202
|
);
|
|
5201
5203
|
const runStoryFn = async (opts) => {
|
|
5202
5204
|
const exitCode = await runPipeline({
|
|
@@ -5959,12 +5961,17 @@ async function runMetricsAction(options) {
|
|
|
5959
5961
|
}
|
|
5960
5962
|
} catch {}
|
|
5961
5963
|
const findingCountsByStoryRun = new Map();
|
|
5964
|
+
const verificationRanByStoryRun = new Map();
|
|
5962
5965
|
const uniqueRunIds = Array.from(new Set(storyMetrics.map((sm) => sm.run_id).filter((id) => id !== "")));
|
|
5963
5966
|
for (const uniqueRunId of uniqueRunIds) try {
|
|
5964
5967
|
const { manifest } = await resolveRunManifest(dbRoot, uniqueRunId);
|
|
5965
5968
|
if (manifest === null) continue;
|
|
5966
5969
|
const data = await manifest.read();
|
|
5967
|
-
for (const [storyKey, entry] of Object.entries(data.per_story_state))
|
|
5970
|
+
for (const [storyKey, entry] of Object.entries(data.per_story_state)) {
|
|
5971
|
+
const key = `${storyKey}:${uniqueRunId}`;
|
|
5972
|
+
findingCountsByStoryRun.set(key, rollupFindingCounts(entry.verification_result));
|
|
5973
|
+
verificationRanByStoryRun.set(key, entry.verification_result !== void 0 && entry.verification_result !== null);
|
|
5974
|
+
}
|
|
5968
5975
|
} catch {}
|
|
5969
5976
|
let factoryRuns = [];
|
|
5970
5977
|
try {
|
|
@@ -5980,7 +5987,8 @@ async function runMetricsAction(options) {
|
|
|
5980
5987
|
}));
|
|
5981
5988
|
const storyMetricsWithFindings = storyMetrics.map((sm) => ({
|
|
5982
5989
|
...sm,
|
|
5983
|
-
verification_findings: findingCountsByStoryRun.get(`${sm.story_key}:${sm.run_id}`) ?? { ...ZERO_FINDING_COUNTS }
|
|
5990
|
+
verification_findings: findingCountsByStoryRun.get(`${sm.story_key}:${sm.run_id}`) ?? { ...ZERO_FINDING_COUNTS },
|
|
5991
|
+
verification_ran: verificationRanByStoryRun.get(`${sm.story_key}:${sm.run_id}`) ?? false
|
|
5984
5992
|
}));
|
|
5985
5993
|
const jsonPayload = {
|
|
5986
5994
|
runs: runsWithBreakdown,
|
|
@@ -3442,21 +3442,52 @@ const RuntimeProbeListSchema = z.array(RuntimeProbeSchema);
|
|
|
3442
3442
|
//#endregion
|
|
3443
3443
|
//#region packages/sdlc/dist/verification/probes/parser.js
|
|
3444
3444
|
const SECTION_HEADING = /^##\s+Runtime\s+Probes\s*$/i;
|
|
3445
|
+
const FENCE_DELIMITER = /^\s*```/;
|
|
3445
3446
|
/**
|
|
3446
3447
|
* Return the raw text of the story's `## Runtime Probes` section (excluding
|
|
3447
3448
|
* the heading line itself), or `undefined` if the section is not present.
|
|
3448
3449
|
*
|
|
3449
3450
|
* The section ends at the next `##` heading or end-of-file. Sub-headings
|
|
3450
3451
|
* (`###`, `####`) remain part of the section body.
|
|
3452
|
+
*
|
|
3453
|
+
* Story 58-4: the scan tracks code-fence depth so a `## Runtime Probes`
|
|
3454
|
+
* heading that appears *inside* an outer ``` block is ignored. Stories that
|
|
3455
|
+
* DOCUMENT probes in prose — regression fixtures, how-to-author docs, the
|
|
3456
|
+
* Epic 58 e2e test spec — contain illustrative `## Runtime Probes` examples
|
|
3457
|
+
* inside outer fences. Without fence-awareness the parser matches those
|
|
3458
|
+
* illustrations as the story's own section, fails to find a terminated
|
|
3459
|
+
* yaml block (the inner fences are typically escaped), and emits a spurious
|
|
3460
|
+
* `runtime-probe-parse-error`. Hit live during the Epic 58 substrate
|
|
3461
|
+
* dispatch on 58-3's artifact.
|
|
3451
3462
|
*/
|
|
3452
3463
|
function extractRuntimeProbesSection(storyContent) {
|
|
3453
3464
|
const lines = storyContent.split(/\r?\n/);
|
|
3454
|
-
|
|
3465
|
+
let inCodeFence = false;
|
|
3466
|
+
let start = -1;
|
|
3467
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
3468
|
+
const line = lines[i] ?? "";
|
|
3469
|
+
if (FENCE_DELIMITER.test(line)) {
|
|
3470
|
+
inCodeFence = !inCodeFence;
|
|
3471
|
+
continue;
|
|
3472
|
+
}
|
|
3473
|
+
if (!inCodeFence && SECTION_HEADING.test(line.trim())) {
|
|
3474
|
+
start = i;
|
|
3475
|
+
break;
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3455
3478
|
if (start === -1) return void 0;
|
|
3456
3479
|
let end = lines.length;
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3480
|
+
inCodeFence = false;
|
|
3481
|
+
for (let i = start + 1; i < lines.length; i += 1) {
|
|
3482
|
+
const line = lines[i] ?? "";
|
|
3483
|
+
if (FENCE_DELIMITER.test(line)) {
|
|
3484
|
+
inCodeFence = !inCodeFence;
|
|
3485
|
+
continue;
|
|
3486
|
+
}
|
|
3487
|
+
if (!inCodeFence && /^##\s+\S/.test(line)) {
|
|
3488
|
+
end = i;
|
|
3489
|
+
break;
|
|
3490
|
+
}
|
|
3460
3491
|
}
|
|
3461
3492
|
return lines.slice(start + 1, end).join("\n");
|
|
3462
3493
|
}
|
|
@@ -3732,6 +3763,113 @@ var RuntimeProbeCheck = class {
|
|
|
3732
3763
|
}
|
|
3733
3764
|
};
|
|
3734
3765
|
|
|
3766
|
+
//#endregion
|
|
3767
|
+
//#region packages/sdlc/dist/verification/source-ac-fidelity-check.js
|
|
3768
|
+
/**
|
|
3769
|
+
* Extract the story's section from the full epic content.
|
|
3770
|
+
*
|
|
3771
|
+
* Uses the same heading pattern as `isImplicitlyCovered` in the monolith:
|
|
3772
|
+
* `### Story <storyKey>:` or `### Story <storyKey> ` or `### Story <storyKey>\n`
|
|
3773
|
+
*
|
|
3774
|
+
* Returns the extracted section text (from the heading match through to the
|
|
3775
|
+
* next `### Story` heading or end of file), or the full content if no
|
|
3776
|
+
* matching heading is found.
|
|
3777
|
+
*/
|
|
3778
|
+
function extractStorySection(epicContent, storyKey) {
|
|
3779
|
+
const escapedKey = storyKey.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3780
|
+
const headingPattern = new RegExp(`^###\\s+Story\\s+${escapedKey}[:\\s]`, "m");
|
|
3781
|
+
const match = headingPattern.exec(epicContent);
|
|
3782
|
+
if (!match) return epicContent;
|
|
3783
|
+
const start = match.index;
|
|
3784
|
+
const nextHeading = /\n### Story /m.exec(epicContent.slice(start + 1));
|
|
3785
|
+
if (nextHeading) return epicContent.slice(start, start + 1 + nextHeading.index);
|
|
3786
|
+
return epicContent.slice(start);
|
|
3787
|
+
}
|
|
3788
|
+
/**
|
|
3789
|
+
* Extract hard clauses from a story section of an epic file.
|
|
3790
|
+
*
|
|
3791
|
+
* Hard clauses:
|
|
3792
|
+
* 1. Lines containing MUST NOT / MUST / SHALL NOT / SHALL as standalone keywords (case-sensitive)
|
|
3793
|
+
* 2. Backtick-wrapped paths with at least one `/` (excludes bare filenames)
|
|
3794
|
+
* 3. The presence of `## Runtime Probes` heading followed by a fenced yaml block
|
|
3795
|
+
* (represented as a single "runtime-probes-section" clause)
|
|
3796
|
+
*/
|
|
3797
|
+
function extractHardClauses(sectionContent) {
|
|
3798
|
+
const clauses = [];
|
|
3799
|
+
const mustPattern = /\b(MUST NOT|MUST|SHALL NOT|SHALL)\b/;
|
|
3800
|
+
const lines = sectionContent.split("\n");
|
|
3801
|
+
for (const line of lines) {
|
|
3802
|
+
const match = mustPattern.exec(line);
|
|
3803
|
+
if (match) {
|
|
3804
|
+
const keyword = match[1];
|
|
3805
|
+
clauses.push({
|
|
3806
|
+
type: keyword,
|
|
3807
|
+
text: line.trim()
|
|
3808
|
+
});
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
const pathPattern = /`([a-zA-Z0-9_./-]+\/[a-zA-Z0-9_./-]+)`/g;
|
|
3812
|
+
let pathMatch;
|
|
3813
|
+
while ((pathMatch = pathPattern.exec(sectionContent)) !== null) clauses.push({
|
|
3814
|
+
type: "path",
|
|
3815
|
+
text: `\`${pathMatch[1]}\``
|
|
3816
|
+
});
|
|
3817
|
+
const probesPattern = /^##\s+Runtime Probes[\s\S]*?```yaml/m;
|
|
3818
|
+
if (probesPattern.test(sectionContent)) clauses.push({
|
|
3819
|
+
type: "runtime-probes-section",
|
|
3820
|
+
text: "## Runtime Probes"
|
|
3821
|
+
});
|
|
3822
|
+
return clauses;
|
|
3823
|
+
}
|
|
3824
|
+
var SourceAcFidelityCheck = class {
|
|
3825
|
+
name = "source-ac-fidelity";
|
|
3826
|
+
tier = "A";
|
|
3827
|
+
async run(context) {
|
|
3828
|
+
const start = Date.now();
|
|
3829
|
+
if (!context.sourceEpicContent) {
|
|
3830
|
+
const findings$1 = [{
|
|
3831
|
+
category: "source-ac-source-unavailable",
|
|
3832
|
+
severity: "warn",
|
|
3833
|
+
message: "source epic content unavailable — skipping fidelity check"
|
|
3834
|
+
}];
|
|
3835
|
+
return {
|
|
3836
|
+
status: "pass",
|
|
3837
|
+
details: renderFindings(findings$1),
|
|
3838
|
+
duration_ms: Date.now() - start,
|
|
3839
|
+
findings: findings$1
|
|
3840
|
+
};
|
|
3841
|
+
}
|
|
3842
|
+
const storySection = extractStorySection(context.sourceEpicContent, context.storyKey);
|
|
3843
|
+
const hardClauses = extractHardClauses(storySection);
|
|
3844
|
+
const findings = [];
|
|
3845
|
+
const storyContent = context.storyContent ?? "";
|
|
3846
|
+
for (const clause of hardClauses) if (clause.type === "runtime-probes-section") {
|
|
3847
|
+
if (!storyContent.includes("## Runtime Probes")) {
|
|
3848
|
+
const truncated = clause.text.length > 120 ? clause.text.slice(0, 120) : clause.text;
|
|
3849
|
+
findings.push({
|
|
3850
|
+
category: "source-ac-drift",
|
|
3851
|
+
severity: "error",
|
|
3852
|
+
message: `runtime-probes-section: "${truncated}" present in epics source but absent in story artifact`
|
|
3853
|
+
});
|
|
3854
|
+
}
|
|
3855
|
+
} else if (!storyContent.includes(clause.text)) {
|
|
3856
|
+
const truncated = clause.text.length > 120 ? clause.text.slice(0, 120) : clause.text;
|
|
3857
|
+
findings.push({
|
|
3858
|
+
category: "source-ac-drift",
|
|
3859
|
+
severity: "error",
|
|
3860
|
+
message: `${clause.type}: "${truncated}" present in epics source but absent in story artifact`
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3863
|
+
const status = findings.some((f) => f.severity === "error") ? "fail" : "pass";
|
|
3864
|
+
return {
|
|
3865
|
+
status,
|
|
3866
|
+
details: findings.length > 0 ? renderFindings(findings) : `source-ac-fidelity: ${hardClauses.length} hard clause(s) verified — all present`,
|
|
3867
|
+
duration_ms: Date.now() - start,
|
|
3868
|
+
findings
|
|
3869
|
+
};
|
|
3870
|
+
}
|
|
3871
|
+
};
|
|
3872
|
+
|
|
3735
3873
|
//#endregion
|
|
3736
3874
|
//#region packages/sdlc/dist/verification/verification-pipeline.js
|
|
3737
3875
|
/**
|
|
@@ -3839,12 +3977,14 @@ var VerificationPipeline = class {
|
|
|
3839
3977
|
* Create a VerificationPipeline pre-loaded with the canonical check set.
|
|
3840
3978
|
*
|
|
3841
3979
|
* Canonical Tier A check order:
|
|
3842
|
-
* 1. PhantomReviewCheck
|
|
3843
|
-
* 2. TrivialOutputCheck
|
|
3980
|
+
* 1. PhantomReviewCheck — story 51-2 (runs first: unreviewed stories skipped)
|
|
3981
|
+
* 2. TrivialOutputCheck — story 51-3
|
|
3844
3982
|
* 3. AcceptanceCriteriaEvidenceCheck
|
|
3845
|
-
* 4. BuildCheck
|
|
3846
|
-
* 5. RuntimeProbeCheck
|
|
3847
|
-
*
|
|
3983
|
+
* 4. BuildCheck — story 51-4
|
|
3984
|
+
* 5. RuntimeProbeCheck — Epic 55 Phase 2: runtime behavior gate; runs last
|
|
3985
|
+
* in Tier A because probes may depend on built artifacts
|
|
3986
|
+
* 6. SourceAcFidelityCheck — Story 58-2: cross-references rendered story artifact
|
|
3987
|
+
* against the source epic's hard clauses (MUST/SHALL/paths)
|
|
3848
3988
|
*
|
|
3849
3989
|
* @param bus Typed event bus for verification events.
|
|
3850
3990
|
* @param config Optional config (used to forward threshold to TrivialOutputCheck).
|
|
@@ -3855,7 +3995,8 @@ function createDefaultVerificationPipeline(bus, config) {
|
|
|
3855
3995
|
new TrivialOutputCheck(config),
|
|
3856
3996
|
new AcceptanceCriteriaEvidenceCheck(),
|
|
3857
3997
|
new BuildCheck(),
|
|
3858
|
-
new RuntimeProbeCheck()
|
|
3998
|
+
new RuntimeProbeCheck(),
|
|
3999
|
+
new SourceAcFidelityCheck()
|
|
3859
4000
|
];
|
|
3860
4001
|
return new VerificationPipeline(bus, checks);
|
|
3861
4002
|
}
|
|
@@ -4185,6 +4326,11 @@ var RunManifest = class RunManifest {
|
|
|
4185
4326
|
baseDir;
|
|
4186
4327
|
/** Optional Dolt adapter for degraded-mode fallback on read. */
|
|
4187
4328
|
doltAdapter;
|
|
4329
|
+
/**
|
|
4330
|
+
* Serializes all write operations on this instance to prevent lost-update races.
|
|
4331
|
+
* Initialized to a resolved promise; each enqueued operation chains off the tail.
|
|
4332
|
+
*/
|
|
4333
|
+
_writeChain = Promise.resolve();
|
|
4188
4334
|
constructor(runId, baseDir = defaultBaseDir(), doltAdapter = null) {
|
|
4189
4335
|
this.runId = runId;
|
|
4190
4336
|
this.baseDir = baseDir;
|
|
@@ -4231,7 +4377,22 @@ var RunManifest = class RunManifest {
|
|
|
4231
4377
|
await this.write(merged);
|
|
4232
4378
|
}
|
|
4233
4379
|
/**
|
|
4234
|
-
*
|
|
4380
|
+
* Append `fn` to the per-instance write chain so all write operations execute
|
|
4381
|
+
* strictly sequentially, preventing lost-update races on concurrent callers.
|
|
4382
|
+
*
|
|
4383
|
+
* The chain itself never rejects (errors are swallowed on the chain side);
|
|
4384
|
+
* the returned promise resolves or rejects exactly when `fn` settles, so
|
|
4385
|
+
* fire-and-forget `.catch()` callers still receive failure signals.
|
|
4386
|
+
*/
|
|
4387
|
+
_enqueue(fn) {
|
|
4388
|
+
const next = this._writeChain.then(() => fn());
|
|
4389
|
+
this._writeChain = next.then(() => void 0, () => void 0);
|
|
4390
|
+
return next;
|
|
4391
|
+
}
|
|
4392
|
+
/**
|
|
4393
|
+
* Raw implementation of the atomic manifest write.
|
|
4394
|
+
* Must only be called from within an `_enqueue`-d function to maintain
|
|
4395
|
+
* the serialization invariant.
|
|
4235
4396
|
*
|
|
4236
4397
|
* Sequence:
|
|
4237
4398
|
* 1. Auto-increment `generation`, set `updated_at`
|
|
@@ -4241,7 +4402,7 @@ var RunManifest = class RunManifest {
|
|
|
4241
4402
|
* 5. If primary exists, copy to `.bak`
|
|
4242
4403
|
* 6. Rename `.tmp` → primary path
|
|
4243
4404
|
*/
|
|
4244
|
-
async
|
|
4405
|
+
async _writeImpl(data) {
|
|
4245
4406
|
let currentGeneration = 0;
|
|
4246
4407
|
const existing = await tryReadFile(this.primaryPath);
|
|
4247
4408
|
if (existing !== null) currentGeneration = existing.generation;
|
|
@@ -4267,6 +4428,15 @@ var RunManifest = class RunManifest {
|
|
|
4267
4428
|
await promises.rename(tmp, this.primaryPath);
|
|
4268
4429
|
}
|
|
4269
4430
|
/**
|
|
4431
|
+
* Atomically write the manifest to disk.
|
|
4432
|
+
*
|
|
4433
|
+
* Enqueues the write via `_enqueue` so concurrent calls are serialized.
|
|
4434
|
+
* The returned promise resolves when this call's write completes.
|
|
4435
|
+
*/
|
|
4436
|
+
async write(data) {
|
|
4437
|
+
return this._enqueue(() => this._writeImpl(data));
|
|
4438
|
+
}
|
|
4439
|
+
/**
|
|
4270
4440
|
* Return a bound `RunManifest` instance without performing any file I/O.
|
|
4271
4441
|
*
|
|
4272
4442
|
* Use `open()` when you want to call instance methods (`read()`, `patchCLIFlags()`)
|
|
@@ -4280,13 +4450,9 @@ var RunManifest = class RunManifest {
|
|
|
4280
4450
|
return new RunManifest(runId, baseDir, doltAdapter);
|
|
4281
4451
|
}
|
|
4282
4452
|
/**
|
|
4283
|
-
*
|
|
4284
|
-
* CLI flags into `cli_flags`, and write the result atomically.
|
|
4285
|
-
*
|
|
4286
|
-
* Non-fatal: callers should wrap in try/catch and log a warning on failure.
|
|
4287
|
-
* The pipeline must not abort if manifest write fails.
|
|
4453
|
+
* Raw implementation — must only be called from within `_enqueue`.
|
|
4288
4454
|
*/
|
|
4289
|
-
async
|
|
4455
|
+
async _patchCLIFlagsImpl(flags) {
|
|
4290
4456
|
let existingData;
|
|
4291
4457
|
try {
|
|
4292
4458
|
const read = await RunManifest.read(this.runId, this.baseDir, this.doltAdapter);
|
|
@@ -4310,7 +4476,7 @@ var RunManifest = class RunManifest {
|
|
|
4310
4476
|
created_at: now
|
|
4311
4477
|
};
|
|
4312
4478
|
}
|
|
4313
|
-
await this.
|
|
4479
|
+
await this._writeImpl({
|
|
4314
4480
|
...existingData,
|
|
4315
4481
|
cli_flags: {
|
|
4316
4482
|
...existingData.cli_flags,
|
|
@@ -4319,21 +4485,20 @@ var RunManifest = class RunManifest {
|
|
|
4319
4485
|
});
|
|
4320
4486
|
}
|
|
4321
4487
|
/**
|
|
4322
|
-
*
|
|
4323
|
-
*
|
|
4324
|
-
* Reads the current manifest (or creates a minimal default if absent),
|
|
4325
|
-
* shallowly merges `updates` into `per_story_state[storyKey]`, and writes
|
|
4326
|
-
* the result atomically via a single `write()` call.
|
|
4327
|
-
*
|
|
4328
|
-
* Fields not included in `updates` on an existing entry are preserved unchanged.
|
|
4329
|
-
*
|
|
4330
|
-
* Non-fatal: callers MUST wrap in `.catch((err) => logger.warn(...))`.
|
|
4331
|
-
* The pipeline must never abort due to a manifest write failure.
|
|
4488
|
+
* Read the current manifest (or create a minimal default), merge the provided
|
|
4489
|
+
* CLI flags into `cli_flags`, and write the result atomically.
|
|
4332
4490
|
*
|
|
4333
|
-
*
|
|
4334
|
-
*
|
|
4491
|
+
* Enqueues the operation via `_enqueue` so concurrent calls are serialized.
|
|
4492
|
+
* Non-fatal: callers should wrap in try/catch and log a warning on failure.
|
|
4493
|
+
* The pipeline must not abort if manifest write fails.
|
|
4335
4494
|
*/
|
|
4336
|
-
async
|
|
4495
|
+
async patchCLIFlags(flags) {
|
|
4496
|
+
return this._enqueue(() => this._patchCLIFlagsImpl(flags));
|
|
4497
|
+
}
|
|
4498
|
+
/**
|
|
4499
|
+
* Raw implementation — must only be called from within `_enqueue`.
|
|
4500
|
+
*/
|
|
4501
|
+
async _patchStoryStateImpl(storyKey, updates) {
|
|
4337
4502
|
let existingData;
|
|
4338
4503
|
try {
|
|
4339
4504
|
const read = await RunManifest.read(this.runId, this.baseDir, this.doltAdapter);
|
|
@@ -4365,7 +4530,7 @@ var RunManifest = class RunManifest {
|
|
|
4365
4530
|
...existing,
|
|
4366
4531
|
...updates
|
|
4367
4532
|
};
|
|
4368
|
-
await this.
|
|
4533
|
+
await this._writeImpl({
|
|
4369
4534
|
...existingData,
|
|
4370
4535
|
per_story_state: {
|
|
4371
4536
|
...existingData.per_story_state,
|
|
@@ -4374,22 +4539,28 @@ var RunManifest = class RunManifest {
|
|
|
4374
4539
|
});
|
|
4375
4540
|
}
|
|
4376
4541
|
/**
|
|
4377
|
-
* Atomically
|
|
4542
|
+
* Atomically upsert the per-story lifecycle state for a single story key.
|
|
4378
4543
|
*
|
|
4379
|
-
* Reads the current manifest
|
|
4380
|
-
*
|
|
4381
|
-
*
|
|
4382
|
-
*
|
|
4544
|
+
* Reads the current manifest (or creates a minimal default if absent),
|
|
4545
|
+
* shallowly merges `updates` into `per_story_state[storyKey]`, and writes
|
|
4546
|
+
* the result atomically via a single `write()` call.
|
|
4547
|
+
*
|
|
4548
|
+
* Fields not included in `updates` on an existing entry are preserved unchanged.
|
|
4383
4549
|
*
|
|
4550
|
+
* Enqueues the operation via `_enqueue` so concurrent calls are serialized.
|
|
4384
4551
|
* Non-fatal: callers MUST wrap in `.catch((err) => logger.warn(...))`.
|
|
4385
4552
|
* The pipeline must never abort due to a manifest write failure.
|
|
4386
4553
|
*
|
|
4387
|
-
*
|
|
4388
|
-
*
|
|
4389
|
-
*
|
|
4390
|
-
* @param entry - Recovery entry to append (attempt_number is 1-indexed)
|
|
4554
|
+
* @param storyKey - Story key (e.g. '52-4')
|
|
4555
|
+
* @param updates - Partial PerStoryState fields to merge
|
|
4391
4556
|
*/
|
|
4392
|
-
async
|
|
4557
|
+
async patchStoryState(storyKey, updates) {
|
|
4558
|
+
return this._enqueue(() => this._patchStoryStateImpl(storyKey, updates));
|
|
4559
|
+
}
|
|
4560
|
+
/**
|
|
4561
|
+
* Raw implementation — must only be called from within `_enqueue`.
|
|
4562
|
+
*/
|
|
4563
|
+
async _appendRecoveryEntryImpl(entry) {
|
|
4393
4564
|
let existingData;
|
|
4394
4565
|
try {
|
|
4395
4566
|
const read = await RunManifest.read(this.runId, this.baseDir, this.doltAdapter);
|
|
@@ -4425,7 +4596,27 @@ var RunManifest = class RunManifest {
|
|
|
4425
4596
|
run_total: existingData.cost_accumulation.run_total + entry.cost_usd
|
|
4426
4597
|
}
|
|
4427
4598
|
};
|
|
4428
|
-
await this.
|
|
4599
|
+
await this._writeImpl(updated);
|
|
4600
|
+
}
|
|
4601
|
+
/**
|
|
4602
|
+
* Atomically append a recovery entry and update cost accumulation.
|
|
4603
|
+
*
|
|
4604
|
+
* Reads the current manifest, appends `entry` to `recovery_history[]`,
|
|
4605
|
+
* increments `cost_accumulation.per_story[entry.story_key]` by `entry.cost_usd`,
|
|
4606
|
+
* increments `cost_accumulation.run_total` by `entry.cost_usd`, then writes
|
|
4607
|
+
* atomically via a single `write()` call.
|
|
4608
|
+
*
|
|
4609
|
+
* Enqueues the operation via `_enqueue` so concurrent calls are serialized.
|
|
4610
|
+
* Non-fatal: callers MUST wrap in `.catch((err) => logger.warn(...))`.
|
|
4611
|
+
* The pipeline must never abort due to a manifest write failure.
|
|
4612
|
+
*
|
|
4613
|
+
* `entry.cost_usd` is the cost of this single retry attempt only (NOT cumulative).
|
|
4614
|
+
* Cumulative per-story retry cost is tracked in `cost_accumulation.per_story`.
|
|
4615
|
+
*
|
|
4616
|
+
* @param entry - Recovery entry to append (attempt_number is 1-indexed)
|
|
4617
|
+
*/
|
|
4618
|
+
async appendRecoveryEntry(entry) {
|
|
4619
|
+
return this._enqueue(() => this._appendRecoveryEntryImpl(entry));
|
|
4429
4620
|
}
|
|
4430
4621
|
/**
|
|
4431
4622
|
* Create a new manifest with `generation: 0` and write it.
|
|
@@ -5286,4 +5477,4 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
5286
5477
|
|
|
5287
5478
|
//#endregion
|
|
5288
5479
|
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 };
|
|
5289
|
-
//# sourceMappingURL=health-
|
|
5480
|
+
//# sourceMappingURL=health-0jqPFBEL.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-0jqPFBEL.js";
|
|
2
2
|
import "./logger-KeHncl-f.js";
|
|
3
3
|
import "./dist-CqtWS9wF.js";
|
|
4
4
|
import "./decisions-C0pz9Clx.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-
|
|
1
|
+
import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-0jqPFBEL.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-CqtWS9wF.js";
|
|
@@ -5911,13 +5911,14 @@ async function getImplementationDecisions(deps, pipelineRunId) {
|
|
|
5911
5911
|
*/
|
|
5912
5912
|
function extractStorySection(shardContent, storyKey) {
|
|
5913
5913
|
if (!shardContent || !storyKey) return null;
|
|
5914
|
-
const
|
|
5915
|
-
const
|
|
5914
|
+
const parts = storyKey.split(/[-._ ]/);
|
|
5915
|
+
const normalized = parts.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("[-._ ]");
|
|
5916
|
+
const headingPattern = new RegExp(`(?:^#{2,4}\\s+Story\\s+${normalized}\\b|^Story\\s+${normalized}:|^\\*\\*${normalized}\\*\\*|^${normalized}:)`, "mi");
|
|
5916
5917
|
const match$2 = headingPattern.exec(shardContent);
|
|
5917
5918
|
if (!match$2) return null;
|
|
5918
5919
|
const startIdx = match$2.index;
|
|
5919
5920
|
const rest = shardContent.slice(startIdx + match$2[0].length);
|
|
5920
|
-
const nextStoryPattern = new RegExp(`(?:^#{2,4}\\s+Story\\s+[\\d]|^Story\\s+[\\d][\\d-]*:|^\\*\\*[\\d][\\d-]*\\*\\*|^[\\d][\\d-]*:)`, "mi");
|
|
5921
|
+
const nextStoryPattern = new RegExp(`(?:^#{2,4}\\s+Story\\s+[\\d]|^Story\\s+[\\d][\\d.\\-_a-z]*:|^\\*\\*[\\d][\\d.\\-_a-z]*\\*\\*|^[\\d][\\d.\\-_a-z]*:)`, "mi");
|
|
5921
5922
|
const nextMatch = nextStoryPattern.exec(rest);
|
|
5922
5923
|
const endIdx = nextMatch !== null ? startIdx + match$2[0].length + nextMatch.index : shardContent.length;
|
|
5923
5924
|
const section = shardContent.slice(startIdx, endIdx).trim();
|
|
@@ -10429,7 +10430,8 @@ function assembleVerificationContext(opts) {
|
|
|
10429
10430
|
reviewResult: opts.reviewResult,
|
|
10430
10431
|
storyContent: opts.storyContent,
|
|
10431
10432
|
devStoryResult: opts.devStoryResult,
|
|
10432
|
-
outputTokenCount: opts.outputTokenCount
|
|
10433
|
+
outputTokenCount: opts.outputTokenCount,
|
|
10434
|
+
sourceEpicContent: opts.sourceEpicContent
|
|
10433
10435
|
};
|
|
10434
10436
|
}
|
|
10435
10437
|
/**
|
|
@@ -10462,20 +10464,23 @@ var VerificationStore = class {
|
|
|
10462
10464
|
* verification results survive process crashes.
|
|
10463
10465
|
*
|
|
10464
10466
|
* Design notes:
|
|
10465
|
-
* - Non-fatal: the
|
|
10466
|
-
*
|
|
10467
|
+
* - Non-fatal: the `.catch()` handler swallows any rejection and logs at warn.
|
|
10468
|
+
* - Returns a `Promise<void>` so callers can optionally `await` it to ensure
|
|
10469
|
+
* ordering (Story 57-2). Fire-and-forget callers that discard the return
|
|
10470
|
+
* value continue to compile and work correctly.
|
|
10467
10471
|
* - Reuses the single RunManifest instance injected by the orchestrator to
|
|
10468
10472
|
* avoid concurrent-write conflicts with the atomic-write lock.
|
|
10469
10473
|
* - `runManifest` is optional (`undefined | null`) — callers from contexts
|
|
10470
|
-
* where no manifest is configured pass `null` and this function
|
|
10474
|
+
* where no manifest is configured pass `null` and this function returns a
|
|
10475
|
+
* resolved promise (no-op).
|
|
10471
10476
|
*
|
|
10472
10477
|
* @param storyKey - Story key being verified (e.g. '52-7')
|
|
10473
10478
|
* @param summary - VerificationSummary returned by VerificationPipeline.run()
|
|
10474
10479
|
* @param runManifest - RunManifest instance to write to, or null/undefined to skip
|
|
10475
10480
|
*/
|
|
10476
10481
|
function persistVerificationResult(storyKey, summary, runManifest) {
|
|
10477
|
-
if (runManifest == null) return;
|
|
10478
|
-
runManifest.patchStoryState(storyKey, { verification_result: summary }).catch((err) => _logger.warn({
|
|
10482
|
+
if (runManifest == null) return Promise.resolve();
|
|
10483
|
+
return runManifest.patchStoryState(storyKey, { verification_result: summary }).catch((err) => _logger.warn({
|
|
10479
10484
|
err,
|
|
10480
10485
|
storyKey
|
|
10481
10486
|
}, "manifest verification_result write failed — pipeline continues"));
|
|
@@ -12072,21 +12077,17 @@ function createImplementationOrchestrator(deps) {
|
|
|
12072
12077
|
phase: "IN_STORY_CREATION",
|
|
12073
12078
|
result: createResult
|
|
12074
12079
|
});
|
|
12075
|
-
if (config.pipelineRunId !== void 0 && createResult.tokenUsage !== void 0)
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
storyKey,
|
|
12087
|
-
err: tokenErr
|
|
12088
|
-
}, "Failed to record create-story token usage");
|
|
12089
|
-
}
|
|
12080
|
+
if (config.pipelineRunId !== void 0 && createResult.tokenUsage !== void 0) Promise.resolve().then(() => addTokenUsage(db, config.pipelineRunId, {
|
|
12081
|
+
phase: "create-story",
|
|
12082
|
+
agent: "create-story",
|
|
12083
|
+
input_tokens: createResult.tokenUsage.input,
|
|
12084
|
+
output_tokens: createResult.tokenUsage.output,
|
|
12085
|
+
cost_usd: estimateDispatchCost(createResult.tokenUsage.input, createResult.tokenUsage.output),
|
|
12086
|
+
metadata: JSON.stringify({ storyKey })
|
|
12087
|
+
})).catch((tokenErr) => logger$24.warn({
|
|
12088
|
+
storyKey,
|
|
12089
|
+
err: tokenErr
|
|
12090
|
+
}, "Failed to record create-story token usage"));
|
|
12090
12091
|
await persistState();
|
|
12091
12092
|
if (createResult.result === "failed") {
|
|
12092
12093
|
const errMsg = createResult.error ?? "create-story failed";
|
|
@@ -12256,21 +12257,17 @@ function createImplementationOrchestrator(deps) {
|
|
|
12256
12257
|
}, "Test planning failed — proceeding to dev-story without test plan");
|
|
12257
12258
|
}
|
|
12258
12259
|
endPhase(storyKey, "test-plan");
|
|
12259
|
-
if (config.pipelineRunId !== void 0 && testPlanTokenUsage !== void 0)
|
|
12260
|
-
|
|
12261
|
-
|
|
12262
|
-
|
|
12263
|
-
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
storyKey,
|
|
12271
|
-
err: tokenErr
|
|
12272
|
-
}, "Failed to record test-plan token usage");
|
|
12273
|
-
}
|
|
12260
|
+
if (config.pipelineRunId !== void 0 && testPlanTokenUsage !== void 0) Promise.resolve().then(() => addTokenUsage(db, config.pipelineRunId, {
|
|
12261
|
+
phase: "test-plan",
|
|
12262
|
+
agent: "test-plan",
|
|
12263
|
+
input_tokens: testPlanTokenUsage.input,
|
|
12264
|
+
output_tokens: testPlanTokenUsage.output,
|
|
12265
|
+
cost_usd: estimateDispatchCost(testPlanTokenUsage.input, testPlanTokenUsage.output),
|
|
12266
|
+
metadata: JSON.stringify({ storyKey })
|
|
12267
|
+
})).catch((tokenErr) => logger$24.warn({
|
|
12268
|
+
storyKey,
|
|
12269
|
+
err: tokenErr
|
|
12270
|
+
}, "Failed to record test-plan token usage"));
|
|
12274
12271
|
eventBus.emit("orchestrator:story-phase-complete", {
|
|
12275
12272
|
storyKey,
|
|
12276
12273
|
phase: "IN_TEST_PLANNING",
|
|
@@ -12419,28 +12416,24 @@ function createImplementationOrchestrator(deps) {
|
|
|
12419
12416
|
batchIndex: batch.batchIndex,
|
|
12420
12417
|
files: batchFilesModified
|
|
12421
12418
|
});
|
|
12422
|
-
if (config.pipelineRunId !== void 0 && batchResult.tokenUsage !== void 0)
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
12429
|
-
metadata: JSON.stringify({
|
|
12430
|
-
storyKey,
|
|
12431
|
-
batchIndex: batch.batchIndex,
|
|
12432
|
-
taskIds: batch.taskIds,
|
|
12433
|
-
durationMs: batchDurationMs,
|
|
12434
|
-
result: batchMetrics.result
|
|
12435
|
-
})
|
|
12436
|
-
});
|
|
12437
|
-
} catch (tokenErr) {
|
|
12438
|
-
logger$24.warn({
|
|
12419
|
+
if (config.pipelineRunId !== void 0 && batchResult.tokenUsage !== void 0) Promise.resolve().then(() => addTokenUsage(db, config.pipelineRunId, {
|
|
12420
|
+
phase: "dev-story",
|
|
12421
|
+
agent: `batch-${batch.batchIndex}`,
|
|
12422
|
+
input_tokens: batchResult.tokenUsage.input,
|
|
12423
|
+
output_tokens: batchResult.tokenUsage.output,
|
|
12424
|
+
cost_usd: estimateDispatchCost(batchResult.tokenUsage.input, batchResult.tokenUsage.output),
|
|
12425
|
+
metadata: JSON.stringify({
|
|
12439
12426
|
storyKey,
|
|
12440
12427
|
batchIndex: batch.batchIndex,
|
|
12441
|
-
|
|
12442
|
-
|
|
12443
|
-
|
|
12428
|
+
taskIds: batch.taskIds,
|
|
12429
|
+
durationMs: batchDurationMs,
|
|
12430
|
+
result: batchMetrics.result
|
|
12431
|
+
})
|
|
12432
|
+
})).catch((tokenErr) => logger$24.warn({
|
|
12433
|
+
storyKey,
|
|
12434
|
+
batchIndex: batch.batchIndex,
|
|
12435
|
+
err: tokenErr
|
|
12436
|
+
}, "Failed to record batch token usage"));
|
|
12444
12437
|
if (batchResult.tokenUsage?.output !== void 0) devOutputTokenCount = (devOutputTokenCount ?? 0) + batchResult.tokenUsage.output;
|
|
12445
12438
|
if (batchResult.result === "failed") logger$24.warn({
|
|
12446
12439
|
storyKey,
|
|
@@ -12478,21 +12471,17 @@ function createImplementationOrchestrator(deps) {
|
|
|
12478
12471
|
});
|
|
12479
12472
|
devFilesModified = devResult.files_modified ?? [];
|
|
12480
12473
|
devOutputTokenCount = devResult.tokenUsage?.output ?? void 0;
|
|
12481
|
-
if (config.pipelineRunId !== void 0 && devResult.tokenUsage !== void 0)
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
|
|
12487
|
-
|
|
12488
|
-
|
|
12489
|
-
|
|
12490
|
-
|
|
12491
|
-
|
|
12492
|
-
storyKey,
|
|
12493
|
-
err: tokenErr
|
|
12494
|
-
}, "Failed to record dev-story token usage");
|
|
12495
|
-
}
|
|
12474
|
+
if (config.pipelineRunId !== void 0 && devResult.tokenUsage !== void 0) Promise.resolve().then(() => addTokenUsage(db, config.pipelineRunId, {
|
|
12475
|
+
phase: "dev-story",
|
|
12476
|
+
agent: "dev-story",
|
|
12477
|
+
input_tokens: devResult.tokenUsage.input,
|
|
12478
|
+
output_tokens: devResult.tokenUsage.output,
|
|
12479
|
+
cost_usd: estimateDispatchCost(devResult.tokenUsage.input, devResult.tokenUsage.output),
|
|
12480
|
+
metadata: JSON.stringify({ storyKey })
|
|
12481
|
+
})).catch((tokenErr) => logger$24.warn({
|
|
12482
|
+
storyKey,
|
|
12483
|
+
err: tokenErr
|
|
12484
|
+
}, "Failed to record dev-story token usage"));
|
|
12496
12485
|
eventBus.emit("orchestrator:story-phase-complete", {
|
|
12497
12486
|
storyKey,
|
|
12498
12487
|
phase: "IN_DEV",
|
|
@@ -13151,23 +13140,22 @@ function createImplementationOrchestrator(deps) {
|
|
|
13151
13140
|
...baselineHeadSha ? { baselineCommit: baselineHeadSha } : {}
|
|
13152
13141
|
});
|
|
13153
13142
|
}
|
|
13154
|
-
if (config.pipelineRunId !== void 0 && reviewResult
|
|
13155
|
-
|
|
13143
|
+
if (config.pipelineRunId !== void 0 && reviewResult?.tokenUsage !== void 0) {
|
|
13144
|
+
const reviewTokens = reviewResult.tokenUsage;
|
|
13145
|
+
Promise.resolve().then(() => addTokenUsage(db, config.pipelineRunId, {
|
|
13156
13146
|
phase: "code-review",
|
|
13157
13147
|
agent: useBatchedReview ? "code-review-batched" : "code-review",
|
|
13158
|
-
input_tokens:
|
|
13159
|
-
output_tokens:
|
|
13160
|
-
cost_usd: estimateDispatchCost(
|
|
13148
|
+
input_tokens: reviewTokens.input,
|
|
13149
|
+
output_tokens: reviewTokens.output,
|
|
13150
|
+
cost_usd: estimateDispatchCost(reviewTokens.input, reviewTokens.output),
|
|
13161
13151
|
metadata: JSON.stringify({
|
|
13162
13152
|
storyKey,
|
|
13163
13153
|
reviewCycle: reviewCycles
|
|
13164
13154
|
})
|
|
13165
|
-
})
|
|
13166
|
-
} catch (tokenErr) {
|
|
13167
|
-
logger$24.warn({
|
|
13155
|
+
})).catch((tokenErr) => logger$24.warn({
|
|
13168
13156
|
storyKey,
|
|
13169
13157
|
err: tokenErr
|
|
13170
|
-
}, "Failed to record code-review token usage");
|
|
13158
|
+
}, "Failed to record code-review token usage"));
|
|
13171
13159
|
}
|
|
13172
13160
|
const isPhantomReview = reviewResult.dispatchFailed === true || reviewResult.verdict !== "SHIP_IT" && reviewResult.verdict !== "LGTM_WITH_NOTES" && (reviewResult.issue_list === void 0 || reviewResult.issue_list.length === 0) && reviewResult.error !== void 0;
|
|
13173
13161
|
if (isPhantomReview && !timeoutRetried) {
|
|
@@ -13272,17 +13260,25 @@ function createImplementationOrchestrator(deps) {
|
|
|
13272
13260
|
error: reviewResult.error,
|
|
13273
13261
|
rawOutput: reviewResult.rawOutput
|
|
13274
13262
|
} : void 0;
|
|
13263
|
+
let sourceEpicContent;
|
|
13264
|
+
const epicsPath1 = findEpicsFile(projectRoot ?? process.cwd());
|
|
13265
|
+
if (epicsPath1) try {
|
|
13266
|
+
const epicFull = readFileSync(epicsPath1, "utf-8");
|
|
13267
|
+
const section = extractStorySection(epicFull, storyKey);
|
|
13268
|
+
if (section) sourceEpicContent = section;
|
|
13269
|
+
} catch {}
|
|
13275
13270
|
const verifContext = assembleVerificationContext({
|
|
13276
13271
|
storyKey,
|
|
13277
13272
|
workingDir: projectRoot ?? process.cwd(),
|
|
13278
13273
|
reviewResult: latestReviewSignals,
|
|
13279
13274
|
storyContent: storyContentForVerification,
|
|
13280
13275
|
devStoryResult: devStorySignals,
|
|
13281
|
-
outputTokenCount: devOutputTokenCount
|
|
13276
|
+
outputTokenCount: devOutputTokenCount,
|
|
13277
|
+
sourceEpicContent
|
|
13282
13278
|
});
|
|
13283
13279
|
const verifSummary = await verificationPipeline.run(verifContext, "A");
|
|
13284
13280
|
verificationStore.set(storyKey, verifSummary);
|
|
13285
|
-
persistVerificationResult(storyKey, verifSummary, runManifest);
|
|
13281
|
+
await persistVerificationResult(storyKey, verifSummary, runManifest);
|
|
13286
13282
|
if (verifSummary.status === "fail") {
|
|
13287
13283
|
updateStory(storyKey, {
|
|
13288
13284
|
phase: "VERIFICATION_FAILED",
|
|
@@ -13301,6 +13297,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
13301
13297
|
phase: "COMPLETE",
|
|
13302
13298
|
completedAt: new Date().toISOString()
|
|
13303
13299
|
});
|
|
13300
|
+
if (config.skipVerification !== true && runManifest != null) Promise.resolve().then(() => runManifest.read()).then((manifest) => {
|
|
13301
|
+
if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$24.warn({
|
|
13302
|
+
storyKey,
|
|
13303
|
+
category: "verification-result-missing"
|
|
13304
|
+
}, "post-COMPLETE invariant: verification_result absent in manifest");
|
|
13305
|
+
}).catch(() => {});
|
|
13304
13306
|
const completedReviewCycles = reviewCycles + 1;
|
|
13305
13307
|
await writeStoryMetricsBestEffort(storyKey, verdict, completedReviewCycles);
|
|
13306
13308
|
await writeStoryOutcomeBestEffort(storyKey, "complete", completedReviewCycles);
|
|
@@ -13530,17 +13532,25 @@ function createImplementationOrchestrator(deps) {
|
|
|
13530
13532
|
error: reviewResult.error,
|
|
13531
13533
|
rawOutput: reviewResult.rawOutput
|
|
13532
13534
|
} : void 0;
|
|
13535
|
+
let sourceEpicContent2;
|
|
13536
|
+
const epicsPath2 = findEpicsFile(projectRoot ?? process.cwd());
|
|
13537
|
+
if (epicsPath2) try {
|
|
13538
|
+
const epicFull2 = readFileSync(epicsPath2, "utf-8");
|
|
13539
|
+
const section2 = extractStorySection(epicFull2, storyKey);
|
|
13540
|
+
if (section2) sourceEpicContent2 = section2;
|
|
13541
|
+
} catch {}
|
|
13533
13542
|
const verifContext = assembleVerificationContext({
|
|
13534
13543
|
storyKey,
|
|
13535
13544
|
workingDir: projectRoot ?? process.cwd(),
|
|
13536
13545
|
reviewResult: latestReviewSignals,
|
|
13537
13546
|
storyContent: storyContentForVerification,
|
|
13538
13547
|
devStoryResult: devStorySignals,
|
|
13539
|
-
outputTokenCount: devOutputTokenCount
|
|
13548
|
+
outputTokenCount: devOutputTokenCount,
|
|
13549
|
+
sourceEpicContent: sourceEpicContent2
|
|
13540
13550
|
});
|
|
13541
13551
|
const verifSummary = await verificationPipeline.run(verifContext, "A");
|
|
13542
13552
|
verificationStore.set(storyKey, verifSummary);
|
|
13543
|
-
persistVerificationResult(storyKey, verifSummary, runManifest);
|
|
13553
|
+
await persistVerificationResult(storyKey, verifSummary, runManifest);
|
|
13544
13554
|
if (verifSummary.status === "fail") {
|
|
13545
13555
|
updateStory(storyKey, {
|
|
13546
13556
|
phase: "VERIFICATION_FAILED",
|
|
@@ -13568,6 +13578,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
13568
13578
|
reviewCycles: finalReviewCycles,
|
|
13569
13579
|
completedAt: new Date().toISOString()
|
|
13570
13580
|
});
|
|
13581
|
+
if (config.skipVerification !== true && runManifest != null) Promise.resolve().then(() => runManifest.read()).then((manifest) => {
|
|
13582
|
+
if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$24.warn({
|
|
13583
|
+
storyKey,
|
|
13584
|
+
category: "verification-result-missing"
|
|
13585
|
+
}, "post-COMPLETE invariant: verification_result absent in manifest");
|
|
13586
|
+
}).catch(() => {});
|
|
13571
13587
|
await writeStoryMetricsBestEffort(storyKey, verdict, finalReviewCycles);
|
|
13572
13588
|
await writeStoryOutcomeBestEffort(storyKey, "complete", finalReviewCycles);
|
|
13573
13589
|
eventBus.emit("orchestrator:story-complete", {
|
|
@@ -18477,7 +18493,8 @@ async function buildRunReportFromManifest(runId, runsDir, adapter, opts) {
|
|
|
18477
18493
|
...vr !== void 0 && {
|
|
18478
18494
|
verificationStatus: vr.status,
|
|
18479
18495
|
verificationChecks: vr.checks
|
|
18480
|
-
}
|
|
18496
|
+
},
|
|
18497
|
+
verification_ran: state.verification_result !== void 0 && state.verification_result !== null
|
|
18481
18498
|
});
|
|
18482
18499
|
}
|
|
18483
18500
|
const escalationFindings = Object.values(escalationDiagnoses).map((d) => d.finding);
|
|
@@ -18608,6 +18625,7 @@ async function buildRunReport(adapter, runId, opts) {
|
|
|
18608
18625
|
verificationStatus: vr.status,
|
|
18609
18626
|
verificationChecks: vr.checks
|
|
18610
18627
|
},
|
|
18628
|
+
verification_ran: vr !== void 0,
|
|
18611
18629
|
...qualityScore !== void 0 && { qualityScore },
|
|
18612
18630
|
...s$1.primary_agent_id && { agentId: s$1.primary_agent_id },
|
|
18613
18631
|
...s$1.primary_model && { model: s$1.primary_model },
|
|
@@ -43840,4 +43858,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
43840
43858
|
|
|
43841
43859
|
//#endregion
|
|
43842
43860
|
export { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, normalizeGraphSummaryToStatus, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveMaxReviewCycles, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict, wireNdjsonEmitter };
|
|
43843
|
-
//# sourceMappingURL=run-
|
|
43861
|
+
//# sourceMappingURL=run-B6qbsy-F.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "./health-
|
|
1
|
+
import "./health-0jqPFBEL.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-B6qbsy-F.js";
|
|
6
6
|
import "./routing-CcBOCuC9.js";
|
|
7
7
|
import "./decisions-C0pz9Clx.js";
|
|
8
8
|
|
package/package.json
CHANGED
|
@@ -32,15 +32,16 @@ a different story from the epic scope. The story key, title, and core scope are
|
|
|
32
32
|
## Instructions
|
|
33
33
|
|
|
34
34
|
1. **Use the Story Definition as your primary input** — it specifies exactly what this story builds. The epic scope provides surrounding context only.
|
|
35
|
-
2. **
|
|
36
|
-
3. **
|
|
37
|
-
4. **
|
|
35
|
+
2. **Acceptance-criteria text from the Story Definition is read-only input.** Any AC clause from the source that contains `MUST`, `MUST NOT`, `SHALL`, `SHALL NOT`, enumerated file or directory paths, or explicit technology / storage / data-format choices (e.g., "plain JSON file", "LanceDB table", "systemd unit") must appear in the rendered story artifact's Acceptance Criteria section **verbatim**. Never soften, abstract, or paraphrase a hard clause — reshaping "MUST remove X" into "consider deprecating X" silently strips the requirement and ships code that violates its own spec. When in doubt, copy the source clause literally and add any clarifying BDD phrasing alongside it, not in place of it.
|
|
36
|
+
3. **Apply architecture constraints** — every constraint listed above is mandatory (file paths, import style, test framework, etc.)
|
|
37
|
+
4. **Use previous dev notes** as guardrails — don't repeat mistakes, build on patterns that worked
|
|
38
|
+
5. **Fill out the story template** with:
|
|
38
39
|
- A clear user story (As a / I want / So that)
|
|
39
|
-
- Acceptance criteria
|
|
40
|
+
- Acceptance criteria: preserve the hard-clause text from the Story Definition verbatim. BDD Given/When/Then phrasing is **optional** — permitted for behavior-oriented criteria where it adds clarity, not mandatory. Never let BDD reshape a MUST / MUST NOT / SHALL clause; copy those clauses literally and, if BDD adds clarity, append the Given/When/Then alongside the original clause rather than replacing it. Aim for the same number of ACs as the source — do not condense clauses into fewer items to hit a target count.
|
|
40
41
|
- Concrete tasks broken into 2–4 hour subtasks, each tied to specific ACs
|
|
41
42
|
- Dev Notes with file paths, import patterns, testing requirements
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
6. **Apply the scope cap** — see Scope Cap Guidance below
|
|
44
|
+
7. **Write the story file** to: `_bmad-output/implementation-artifacts/{{story_key}}-<kebab-title>.md`
|
|
44
45
|
- Do NOT add a `Status:` field to the story file — story status is managed exclusively by the Dolt work graph (`wg_stories` table)
|
|
45
46
|
- Dev Agent Record section must be present but left blank (to be filled by dev agent)
|
|
46
47
|
|
|
@@ -66,7 +67,9 @@ Use this exact format for each item:
|
|
|
66
67
|
|
|
67
68
|
## Runtime Verification Guidance
|
|
68
69
|
|
|
69
|
-
**
|
|
70
|
+
**If the Story Definition already contains a `## Runtime Probes` section, transfer it verbatim** — including every probe entry, YAML fenced block, and surrounding prose — into the rendered story artifact. Do not independently re-evaluate whether the story is runtime-dependent; the epic author already decided when they authored probes in the source. Adding, removing, renaming, or reshaping a source-declared probe silently subverts the author's runtime contract.
|
|
71
|
+
|
|
72
|
+
**If the Story Definition has no `## Runtime Probes` section, decide whether this story's artifact is runtime-dependent.** An artifact is runtime-dependent if correctness depends on execution — systemd units, container definitions (Podman Quadlet, Docker Compose), install scripts, migration runners, anything whose behavior is only observable by running it against a real host or ephemeral sandbox.
|
|
70
73
|
|
|
71
74
|
If the artifact is runtime-dependent, add a `## Runtime Probes` section to the story file. Each probe is a short shell command whose exit status answers "does this artifact actually work?".
|
|
72
75
|
|
|
@@ -156,11 +159,13 @@ Treat the probes you draft as a **first pass** the human author will refine. Pro
|
|
|
156
159
|
|
|
157
160
|
## Scope Cap Guidance
|
|
158
161
|
|
|
159
|
-
**Aim for 6-7 acceptance criteria and 7-8 tasks per story
|
|
162
|
+
**Aim for 6-7 acceptance criteria and 7-8 tasks per story** when you are authoring ACs from scratch.
|
|
160
163
|
|
|
161
164
|
Each story will be implemented by an AI agent in a single pass. Stories with more than 7 ACs tend to exceed agent capabilities and require decomposition, adding latency and complexity to the pipeline.
|
|
162
165
|
|
|
163
|
-
If the
|
|
166
|
+
**The scope cap does NOT license condensing source ACs.** If the Story Definition supplies more ACs than the guidance target, preserve them all verbatim — never collapse hard clauses (MUST / MUST NOT / SHALL / enumerated paths) into fewer items just to hit a count. If the source scope is too large for a single story, surface that as a failure (`result: failure`, `error: source scope exceeds single-story capacity — split upstream`) rather than silently dropping ACs.
|
|
167
|
+
|
|
168
|
+
If the scope *you are authoring from scratch* requires more than 7 ACs, split into multiple sequential stories (e.g., `7-1a: Core Setup`, `7-1b: Advanced Features`). Splitting is preferable to cramming too much scope into a single story.
|
|
164
169
|
|
|
165
170
|
This is guidance, not enforcement — if the scope genuinely fits in a slightly larger story, use your judgment. The goal is to avoid stories that will predictably fail during implementation.
|
|
166
171
|
|