substrate-ai 0.7.0 → 0.8.0
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 +21 -8
- package/dist/{health-Dnx-FGva.js → health-C-VRJruD.js} +502 -23
- package/dist/{health-4fyhDU6T.js → health-DMbNP9bw.js} +1 -1
- package/dist/index.d.ts +24 -0
- package/dist/{run-BdqqWU9p.js → run-B40PykZL.js} +2 -2
- package/dist/{run-CfF0-tVP.js → run-ohzPz3r1.js} +550 -189
- package/package.json +1 -2
- package/packs/bmad/prompts/dev-story.md +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BMAD_BASELINE_TOKENS_FULL, DoltClient, DoltMergeConflict, FileStateStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter, formatOutput, formatPipelineSummary, formatTokenTelemetry, initSchema, inspectProcessTree, parseDbTimestampAsUtc, resolveMainRepoRoot, validateStoryKey } from "./health-
|
|
1
|
+
import { BMAD_BASELINE_TOKENS_FULL, DoltClient, DoltMergeConflict, FileStateStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter, formatOutput, formatPipelineSummary, formatTokenTelemetry, initSchema, inspectProcessTree, parseDbTimestampAsUtc, resolveMainRepoRoot, validateStoryKey } from "./health-C-VRJruD.js";
|
|
2
2
|
import { createLogger, deepMask } from "./logger-D2fS2ccL.js";
|
|
3
3
|
import { CURRENT_CONFIG_FORMAT_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator } from "./config-migrator-CtGelIsG.js";
|
|
4
4
|
import { ConfigError, ConfigIncompatibleFormatError, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning, sleep } from "./helpers-CpMs8VZX.js";
|
|
@@ -7,7 +7,7 @@ import { addTokenUsage, createDecision, createPipelineRun, createRequirement, ge
|
|
|
7
7
|
import { ADVISORY_NOTES, ESCALATION_DIAGNOSIS, OPERATIONAL_FINDING, STORY_METRICS, STORY_OUTCOME, TEST_EXPANSION_FINDING, TEST_PLAN, aggregateTokenUsageForRun, aggregateTokenUsageForStory, getStoryMetricsForRun, writeRunMetrics, writeStoryMetrics } from "./operational-BdcdmDqS.js";
|
|
8
8
|
import { dirname, join, resolve } from "path";
|
|
9
9
|
import { access, mkdir, readFile, readdir, stat, writeFile } from "fs/promises";
|
|
10
|
-
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "fs";
|
|
11
11
|
import yaml from "js-yaml";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { execFile, execSync, spawn } from "node:child_process";
|
|
@@ -4665,7 +4665,7 @@ const DEFAULT_TIMEOUTS = {
|
|
|
4665
4665
|
"create-story": 6e5,
|
|
4666
4666
|
"dev-story": 18e5,
|
|
4667
4667
|
"code-review": 9e5,
|
|
4668
|
-
"minor-fixes":
|
|
4668
|
+
"minor-fixes": 12e5,
|
|
4669
4669
|
"major-rework": 9e5,
|
|
4670
4670
|
"readiness-check": 6e5,
|
|
4671
4671
|
"elicitation": 9e5,
|
|
@@ -5669,7 +5669,22 @@ function runBuildVerification(options) {
|
|
|
5669
5669
|
*/
|
|
5670
5670
|
function checkGitDiffFiles(workingDir = process.cwd()) {
|
|
5671
5671
|
const results = new Set();
|
|
5672
|
+
let repoHasCommits = true;
|
|
5672
5673
|
try {
|
|
5674
|
+
execSync("git rev-parse --verify HEAD", {
|
|
5675
|
+
cwd: workingDir,
|
|
5676
|
+
stdio: [
|
|
5677
|
+
"ignore",
|
|
5678
|
+
"pipe",
|
|
5679
|
+
"pipe"
|
|
5680
|
+
],
|
|
5681
|
+
timeout: 3e3
|
|
5682
|
+
});
|
|
5683
|
+
} catch {
|
|
5684
|
+
repoHasCommits = false;
|
|
5685
|
+
}
|
|
5686
|
+
try {
|
|
5687
|
+
if (!repoHasCommits) throw new Error("no commits — skip HEAD diff");
|
|
5673
5688
|
const unstaged = execSync("git diff --name-only HEAD", {
|
|
5674
5689
|
cwd: workingDir,
|
|
5675
5690
|
encoding: "utf-8",
|
|
@@ -6600,6 +6615,27 @@ async function isValidStoryFile(filePath) {
|
|
|
6600
6615
|
//#region src/modules/compiled-workflows/git-helpers.ts
|
|
6601
6616
|
const logger$19 = createLogger("compiled-workflows:git-helpers");
|
|
6602
6617
|
/**
|
|
6618
|
+
* Check whether the repo at `cwd` has at least one commit (HEAD resolves).
|
|
6619
|
+
* Returns false for fresh repos with no commits, avoiding `fatal: bad revision 'HEAD'`.
|
|
6620
|
+
* Synchronous (execSync) to keep it simple — this is a fast local check.
|
|
6621
|
+
*/
|
|
6622
|
+
function hasCommits(cwd) {
|
|
6623
|
+
try {
|
|
6624
|
+
execSync("git rev-parse --verify HEAD", {
|
|
6625
|
+
cwd,
|
|
6626
|
+
stdio: [
|
|
6627
|
+
"ignore",
|
|
6628
|
+
"pipe",
|
|
6629
|
+
"pipe"
|
|
6630
|
+
],
|
|
6631
|
+
timeout: 3e3
|
|
6632
|
+
});
|
|
6633
|
+
return true;
|
|
6634
|
+
} catch {
|
|
6635
|
+
return false;
|
|
6636
|
+
}
|
|
6637
|
+
}
|
|
6638
|
+
/**
|
|
6603
6639
|
* Capture the full git diff for HEAD (working tree vs current commit).
|
|
6604
6640
|
*
|
|
6605
6641
|
* Runs `git diff HEAD` in the specified working directory and returns
|
|
@@ -6613,6 +6649,10 @@ const logger$19 = createLogger("compiled-workflows:git-helpers");
|
|
|
6613
6649
|
* @returns The diff output string, or '' on error
|
|
6614
6650
|
*/
|
|
6615
6651
|
async function getGitDiffSummary(workingDirectory = process.cwd()) {
|
|
6652
|
+
if (!hasCommits(workingDirectory)) {
|
|
6653
|
+
logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff");
|
|
6654
|
+
return "";
|
|
6655
|
+
}
|
|
6616
6656
|
return runGitCommand(["diff", "HEAD"], workingDirectory, "git-diff-summary");
|
|
6617
6657
|
}
|
|
6618
6658
|
/**
|
|
@@ -6626,6 +6666,10 @@ async function getGitDiffSummary(workingDirectory = process.cwd()) {
|
|
|
6626
6666
|
* @returns The stat summary string, or '' on error
|
|
6627
6667
|
*/
|
|
6628
6668
|
async function getGitDiffStatSummary(workingDirectory = process.cwd()) {
|
|
6669
|
+
if (!hasCommits(workingDirectory)) {
|
|
6670
|
+
logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat");
|
|
6671
|
+
return "";
|
|
6672
|
+
}
|
|
6629
6673
|
return runGitCommand([
|
|
6630
6674
|
"diff",
|
|
6631
6675
|
"--stat",
|
|
@@ -6648,6 +6692,10 @@ async function getGitDiffStatSummary(workingDirectory = process.cwd()) {
|
|
|
6648
6692
|
*/
|
|
6649
6693
|
async function getGitDiffForFiles(files, workingDirectory = process.cwd()) {
|
|
6650
6694
|
if (files.length === 0) return "";
|
|
6695
|
+
if (!hasCommits(workingDirectory)) {
|
|
6696
|
+
logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff for files");
|
|
6697
|
+
return "";
|
|
6698
|
+
}
|
|
6651
6699
|
await stageIntentToAdd(files, workingDirectory);
|
|
6652
6700
|
return runGitCommand([
|
|
6653
6701
|
"diff",
|
|
@@ -6671,6 +6719,10 @@ async function getGitDiffForFiles(files, workingDirectory = process.cwd()) {
|
|
|
6671
6719
|
*/
|
|
6672
6720
|
async function getGitDiffStatForFiles(files, workingDirectory = process.cwd()) {
|
|
6673
6721
|
if (files.length === 0) return "";
|
|
6722
|
+
if (!hasCommits(workingDirectory)) {
|
|
6723
|
+
logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat for files");
|
|
6724
|
+
return "";
|
|
6725
|
+
}
|
|
6674
6726
|
return runGitCommand([
|
|
6675
6727
|
"diff",
|
|
6676
6728
|
"--stat",
|
|
@@ -10706,22 +10758,49 @@ var IngestionServer = class {
|
|
|
10706
10758
|
/**
|
|
10707
10759
|
* Start the HTTP ingestion server.
|
|
10708
10760
|
* Resolves when the server is listening and ready to accept connections.
|
|
10761
|
+
* On EADDRINUSE, retries up to 10 consecutive ports before failing.
|
|
10709
10762
|
*/
|
|
10710
10763
|
async start() {
|
|
10711
10764
|
if (this._server !== null) {
|
|
10712
10765
|
logger$8.warn("IngestionServer.start() called while already started — ignoring");
|
|
10713
10766
|
return;
|
|
10714
10767
|
}
|
|
10768
|
+
const maxRetries = 10;
|
|
10769
|
+
let lastErr;
|
|
10770
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
10771
|
+
const port = this._port + attempt;
|
|
10772
|
+
try {
|
|
10773
|
+
await this._tryListen(port);
|
|
10774
|
+
return;
|
|
10775
|
+
} catch (err) {
|
|
10776
|
+
const nodeErr = err;
|
|
10777
|
+
if (nodeErr.code === "EADDRINUSE" && attempt < maxRetries) {
|
|
10778
|
+
logger$8.warn({
|
|
10779
|
+
port,
|
|
10780
|
+
attempt: attempt + 1
|
|
10781
|
+
}, `Port ${port} in use — trying ${port + 1}`);
|
|
10782
|
+
lastErr = nodeErr;
|
|
10783
|
+
continue;
|
|
10784
|
+
}
|
|
10785
|
+
throw err;
|
|
10786
|
+
}
|
|
10787
|
+
}
|
|
10788
|
+
throw lastErr ?? new Error("IngestionServer: exhausted port retry attempts");
|
|
10789
|
+
}
|
|
10790
|
+
_tryListen(port) {
|
|
10715
10791
|
return new Promise((resolve$2, reject) => {
|
|
10716
10792
|
const server = createServer(this._handleRequest.bind(this));
|
|
10717
10793
|
server.on("error", (err) => {
|
|
10718
|
-
|
|
10794
|
+
server.close();
|
|
10719
10795
|
reject(err);
|
|
10720
10796
|
});
|
|
10721
|
-
server.listen(
|
|
10797
|
+
server.listen(port, "127.0.0.1", () => {
|
|
10722
10798
|
this._server = server;
|
|
10723
10799
|
const addr = server.address();
|
|
10724
|
-
logger$8.info({
|
|
10800
|
+
logger$8.info({
|
|
10801
|
+
port: addr.port,
|
|
10802
|
+
requestedPort: this._port
|
|
10803
|
+
}, "IngestionServer listening");
|
|
10725
10804
|
this._buffer?.start();
|
|
10726
10805
|
resolve$2();
|
|
10727
10806
|
});
|
|
@@ -13190,7 +13269,8 @@ function wgStatusForPhase(phase) {
|
|
|
13190
13269
|
case "IN_TEST_PLANNING":
|
|
13191
13270
|
case "IN_DEV":
|
|
13192
13271
|
case "IN_REVIEW":
|
|
13193
|
-
case "NEEDS_FIXES":
|
|
13272
|
+
case "NEEDS_FIXES":
|
|
13273
|
+
case "CHECKPOINT": return "in_progress";
|
|
13194
13274
|
case "COMPLETE": return "complete";
|
|
13195
13275
|
case "ESCALATED": return "escalated";
|
|
13196
13276
|
}
|
|
@@ -13279,6 +13359,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13279
13359
|
let _contractMismatches;
|
|
13280
13360
|
let _otlpEndpoint;
|
|
13281
13361
|
const _stateStoreCache = new Map();
|
|
13362
|
+
const _checkpoints = new Map();
|
|
13282
13363
|
const MEMORY_PRESSURE_BACKOFF_MS = [
|
|
13283
13364
|
3e4,
|
|
13284
13365
|
6e4,
|
|
@@ -13489,7 +13570,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
13489
13570
|
lastVerdict: record.lastVerdict,
|
|
13490
13571
|
error: record.error,
|
|
13491
13572
|
startedAt: record.startedAt,
|
|
13492
|
-
completedAt: record.completedAt
|
|
13573
|
+
completedAt: record.completedAt,
|
|
13574
|
+
checkpointFilesCount: record.checkpointFilesCount
|
|
13493
13575
|
};
|
|
13494
13576
|
for (const [key, s] of _stories) stories[key] = { ...s };
|
|
13495
13577
|
const status = {
|
|
@@ -13562,7 +13644,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
13562
13644
|
error: state.error,
|
|
13563
13645
|
startedAt: state.startedAt,
|
|
13564
13646
|
completedAt: state.completedAt,
|
|
13565
|
-
sprint: config.sprint
|
|
13647
|
+
sprint: config.sprint,
|
|
13648
|
+
checkpointFilesCount: state.checkpointFilesCount
|
|
13566
13649
|
};
|
|
13567
13650
|
await stateStore.setStoryState(storyKey, record);
|
|
13568
13651
|
} catch (err) {
|
|
@@ -14165,7 +14248,187 @@ function createImplementationOrchestrator(deps) {
|
|
|
14165
14248
|
result: devResult
|
|
14166
14249
|
});
|
|
14167
14250
|
await persistState();
|
|
14168
|
-
|
|
14251
|
+
let checkpointHandled = false;
|
|
14252
|
+
if (devResult.result === "failed" && devResult.error?.startsWith("dispatch_timeout")) {
|
|
14253
|
+
endPhase(storyKey, "dev-story");
|
|
14254
|
+
const timeoutFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
|
|
14255
|
+
if (timeoutFiles.length === 0) {
|
|
14256
|
+
logger$25.warn({ storyKey }, "Dev-story timeout with zero modified files — escalating immediately (no checkpoint)");
|
|
14257
|
+
updateStory(storyKey, {
|
|
14258
|
+
phase: "ESCALATED",
|
|
14259
|
+
error: "timeout-no-files",
|
|
14260
|
+
completedAt: new Date().toISOString()
|
|
14261
|
+
});
|
|
14262
|
+
await writeStoryMetricsBestEffort(storyKey, "escalated", 0);
|
|
14263
|
+
await emitEscalation({
|
|
14264
|
+
storyKey,
|
|
14265
|
+
lastVerdict: "timeout-no-files",
|
|
14266
|
+
reviewCycles: 0,
|
|
14267
|
+
issues: ["dev-story timed out with no partial files — nothing to checkpoint"]
|
|
14268
|
+
});
|
|
14269
|
+
await persistState();
|
|
14270
|
+
return;
|
|
14271
|
+
}
|
|
14272
|
+
logger$25.info({
|
|
14273
|
+
storyKey,
|
|
14274
|
+
filesCount: timeoutFiles.length
|
|
14275
|
+
}, "Dev-story timeout with partial files — capturing checkpoint");
|
|
14276
|
+
let gitDiff = "";
|
|
14277
|
+
try {
|
|
14278
|
+
gitDiff = execSync(`git diff HEAD -- ${timeoutFiles.map((f) => `"${f}"`).join(" ")}`, {
|
|
14279
|
+
cwd: projectRoot ?? process.cwd(),
|
|
14280
|
+
encoding: "utf-8",
|
|
14281
|
+
timeout: 1e4,
|
|
14282
|
+
stdio: [
|
|
14283
|
+
"ignore",
|
|
14284
|
+
"pipe",
|
|
14285
|
+
"pipe"
|
|
14286
|
+
]
|
|
14287
|
+
}).trim();
|
|
14288
|
+
} catch (diffErr) {
|
|
14289
|
+
logger$25.warn({
|
|
14290
|
+
storyKey,
|
|
14291
|
+
error: diffErr instanceof Error ? diffErr.message : String(diffErr)
|
|
14292
|
+
}, "Failed to capture git diff for checkpoint — proceeding with empty diff");
|
|
14293
|
+
}
|
|
14294
|
+
_checkpoints.set(storyKey, {
|
|
14295
|
+
filesModified: timeoutFiles,
|
|
14296
|
+
gitDiff,
|
|
14297
|
+
partialOutput: devResult.error ?? ""
|
|
14298
|
+
});
|
|
14299
|
+
updateStory(storyKey, {
|
|
14300
|
+
phase: "CHECKPOINT",
|
|
14301
|
+
checkpointFilesCount: timeoutFiles.length
|
|
14302
|
+
});
|
|
14303
|
+
const diffSizeBytes = Buffer.byteLength(gitDiff, "utf-8");
|
|
14304
|
+
eventBus.emit("story:checkpoint-saved", {
|
|
14305
|
+
storyKey,
|
|
14306
|
+
filesCount: timeoutFiles.length,
|
|
14307
|
+
diffSizeBytes
|
|
14308
|
+
});
|
|
14309
|
+
if (stateStore !== void 0) stateStore.recordMetric({
|
|
14310
|
+
storyKey,
|
|
14311
|
+
taskType: "dev-story",
|
|
14312
|
+
result: "timeout",
|
|
14313
|
+
recordedAt: new Date().toISOString(),
|
|
14314
|
+
sprint: config.sprint
|
|
14315
|
+
}).catch((storeErr) => {
|
|
14316
|
+
logger$25.warn({
|
|
14317
|
+
err: storeErr,
|
|
14318
|
+
storyKey
|
|
14319
|
+
}, "Failed to record timeout metric to StateStore (best-effort)");
|
|
14320
|
+
});
|
|
14321
|
+
await persistState();
|
|
14322
|
+
eventBus.emit("story:checkpoint-retry", {
|
|
14323
|
+
storyKey,
|
|
14324
|
+
filesCount: timeoutFiles.length,
|
|
14325
|
+
attempt: 2
|
|
14326
|
+
});
|
|
14327
|
+
const checkpointData = _checkpoints.get(storyKey);
|
|
14328
|
+
let checkpointRetryPrompt;
|
|
14329
|
+
let checkpointRetryMaxTurns;
|
|
14330
|
+
try {
|
|
14331
|
+
const devStoryTemplate = await pack.getPrompt("dev-story");
|
|
14332
|
+
const storyContent = await readFile$1(storyFilePath ?? "", "utf-8");
|
|
14333
|
+
const complexity = computeStoryComplexity(storyContent);
|
|
14334
|
+
checkpointRetryMaxTurns = resolveDevStoryMaxTurns(complexity.complexityScore);
|
|
14335
|
+
logComplexityResult(storyKey, complexity, checkpointRetryMaxTurns);
|
|
14336
|
+
let archConstraints = "";
|
|
14337
|
+
try {
|
|
14338
|
+
const decisions = await getDecisionsByPhase(db, "solutioning");
|
|
14339
|
+
const constraints = decisions.filter((d) => d.category === "architecture");
|
|
14340
|
+
archConstraints = constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
|
|
14341
|
+
} catch {}
|
|
14342
|
+
const checkpointContext = [
|
|
14343
|
+
"Your prior attempt timed out. Here is the work you completed:",
|
|
14344
|
+
"",
|
|
14345
|
+
`Files modified (${checkpointData.filesModified.length}):`,
|
|
14346
|
+
...checkpointData.filesModified.map((f) => `- ${f}`),
|
|
14347
|
+
"",
|
|
14348
|
+
"```diff",
|
|
14349
|
+
checkpointData.gitDiff || "(no diff available)",
|
|
14350
|
+
"```",
|
|
14351
|
+
"",
|
|
14352
|
+
"Continue from where you left off. Do not redo completed work."
|
|
14353
|
+
].join("\n");
|
|
14354
|
+
const sections = [
|
|
14355
|
+
{
|
|
14356
|
+
name: "story_content",
|
|
14357
|
+
content: storyContent,
|
|
14358
|
+
priority: "required"
|
|
14359
|
+
},
|
|
14360
|
+
{
|
|
14361
|
+
name: "checkpoint_context",
|
|
14362
|
+
content: checkpointContext,
|
|
14363
|
+
priority: "required"
|
|
14364
|
+
},
|
|
14365
|
+
{
|
|
14366
|
+
name: "arch_constraints",
|
|
14367
|
+
content: archConstraints,
|
|
14368
|
+
priority: "optional"
|
|
14369
|
+
}
|
|
14370
|
+
];
|
|
14371
|
+
const assembled = assemblePrompt(devStoryTemplate, sections, 24e3);
|
|
14372
|
+
checkpointRetryPrompt = assembled.prompt;
|
|
14373
|
+
} catch {
|
|
14374
|
+
checkpointRetryPrompt = `Continue story ${storyKey} from checkpoint. Your prior attempt timed out. Do not redo completed work.`;
|
|
14375
|
+
logger$25.warn({ storyKey }, "Failed to assemble checkpoint retry prompt — using fallback");
|
|
14376
|
+
}
|
|
14377
|
+
logger$25.info({
|
|
14378
|
+
storyKey,
|
|
14379
|
+
filesCount: checkpointData.filesModified.length
|
|
14380
|
+
}, "Dispatching checkpoint retry for timed-out story");
|
|
14381
|
+
incrementDispatches(storyKey);
|
|
14382
|
+
updateStory(storyKey, { phase: "IN_DEV" });
|
|
14383
|
+
startPhase(storyKey, "dev-story-retry");
|
|
14384
|
+
const checkpointRetryHandle = dispatcher.dispatch({
|
|
14385
|
+
prompt: checkpointRetryPrompt,
|
|
14386
|
+
agent: "claude-code",
|
|
14387
|
+
taskType: "dev-story",
|
|
14388
|
+
outputSchema: DevStoryResultSchema,
|
|
14389
|
+
...checkpointRetryMaxTurns !== void 0 ? { maxTurns: checkpointRetryMaxTurns } : {},
|
|
14390
|
+
...projectRoot !== void 0 ? { workingDirectory: projectRoot } : {},
|
|
14391
|
+
..._otlpEndpoint !== void 0 ? { otlpEndpoint: _otlpEndpoint } : {},
|
|
14392
|
+
...config.perStoryContextCeilings?.[storyKey] !== void 0 ? { maxContextTokens: config.perStoryContextCeilings[storyKey] } : {},
|
|
14393
|
+
storyKey
|
|
14394
|
+
});
|
|
14395
|
+
const checkpointRetryResult = await checkpointRetryHandle.result;
|
|
14396
|
+
endPhase(storyKey, "dev-story-retry");
|
|
14397
|
+
eventBus.emit("orchestrator:story-phase-complete", {
|
|
14398
|
+
storyKey,
|
|
14399
|
+
phase: "IN_DEV",
|
|
14400
|
+
result: { tokenUsage: checkpointRetryResult.tokenEstimate ? {
|
|
14401
|
+
input: checkpointRetryResult.tokenEstimate.input,
|
|
14402
|
+
output: checkpointRetryResult.tokenEstimate.output
|
|
14403
|
+
} : void 0 }
|
|
14404
|
+
});
|
|
14405
|
+
if (checkpointRetryResult.status === "timeout") {
|
|
14406
|
+
logger$25.warn({ storyKey }, "Checkpoint retry dispatch timed out — escalating story");
|
|
14407
|
+
updateStory(storyKey, {
|
|
14408
|
+
phase: "ESCALATED",
|
|
14409
|
+
error: "checkpoint-retry-timeout",
|
|
14410
|
+
completedAt: new Date().toISOString()
|
|
14411
|
+
});
|
|
14412
|
+
await writeStoryMetricsBestEffort(storyKey, "escalated", 0);
|
|
14413
|
+
await emitEscalation({
|
|
14414
|
+
storyKey,
|
|
14415
|
+
lastVerdict: "checkpoint-retry-timeout",
|
|
14416
|
+
reviewCycles: 0,
|
|
14417
|
+
issues: ["checkpoint retry timed out — no infinite retry loop"]
|
|
14418
|
+
});
|
|
14419
|
+
await persistState();
|
|
14420
|
+
return;
|
|
14421
|
+
}
|
|
14422
|
+
const retryParsed = checkpointRetryResult.parsed;
|
|
14423
|
+
devFilesModified = retryParsed?.files_modified ?? checkGitDiffFiles(projectRoot ?? process.cwd());
|
|
14424
|
+
if (checkpointRetryResult.status === "completed" && retryParsed?.result === "success") devStoryWasSuccess = true;
|
|
14425
|
+
else logger$25.warn({
|
|
14426
|
+
storyKey,
|
|
14427
|
+
status: checkpointRetryResult.status
|
|
14428
|
+
}, "Checkpoint retry completed with failure — proceeding to code review");
|
|
14429
|
+
checkpointHandled = true;
|
|
14430
|
+
}
|
|
14431
|
+
if (!checkpointHandled) if (devResult.result === "success") devStoryWasSuccess = true;
|
|
14169
14432
|
else logger$25.warn({
|
|
14170
14433
|
storyKey,
|
|
14171
14434
|
error: devResult.error,
|
|
@@ -19715,6 +19978,189 @@ function mapInternalPhaseToEventPhase(internalPhase) {
|
|
|
19715
19978
|
default: return null;
|
|
19716
19979
|
}
|
|
19717
19980
|
}
|
|
19981
|
+
/**
|
|
19982
|
+
* Wire all NDJSON event subscriptions from the event bus to the emitter.
|
|
19983
|
+
* Shared helper called from both the implementation-only path and the full
|
|
19984
|
+
* pipeline path (AC2: shared event subscription logic).
|
|
19985
|
+
*/
|
|
19986
|
+
function wireNdjsonEmitter(eventBus, ndjsonEmitter) {
|
|
19987
|
+
eventBus.on("orchestrator:story-phase-start", (payload) => {
|
|
19988
|
+
const phase = mapInternalPhaseToEventPhase(payload.phase);
|
|
19989
|
+
if (phase !== null) ndjsonEmitter.emit({
|
|
19990
|
+
type: "story:phase",
|
|
19991
|
+
ts: new Date().toISOString(),
|
|
19992
|
+
key: payload.storyKey,
|
|
19993
|
+
phase,
|
|
19994
|
+
status: "in_progress"
|
|
19995
|
+
});
|
|
19996
|
+
});
|
|
19997
|
+
eventBus.on("orchestrator:story-phase-complete", (payload) => {
|
|
19998
|
+
const phase = mapInternalPhaseToEventPhase(payload.phase);
|
|
19999
|
+
if (phase !== null) {
|
|
20000
|
+
const result = payload.result;
|
|
20001
|
+
ndjsonEmitter.emit({
|
|
20002
|
+
type: "story:phase",
|
|
20003
|
+
ts: new Date().toISOString(),
|
|
20004
|
+
key: payload.storyKey,
|
|
20005
|
+
phase,
|
|
20006
|
+
status: "complete",
|
|
20007
|
+
...phase === "code-review" && result?.verdict !== void 0 ? { verdict: result.verdict } : {},
|
|
20008
|
+
...phase === "create-story" && result?.story_file !== void 0 ? { file: result.story_file } : {}
|
|
20009
|
+
});
|
|
20010
|
+
}
|
|
20011
|
+
});
|
|
20012
|
+
eventBus.on("routing:model-selected", (payload) => {
|
|
20013
|
+
ndjsonEmitter.emit({
|
|
20014
|
+
type: "routing:model-selected",
|
|
20015
|
+
ts: new Date().toISOString(),
|
|
20016
|
+
dispatch_id: payload.dispatchId,
|
|
20017
|
+
task_type: payload.taskType,
|
|
20018
|
+
phase: payload.phase,
|
|
20019
|
+
model: payload.model,
|
|
20020
|
+
source: payload.source
|
|
20021
|
+
});
|
|
20022
|
+
});
|
|
20023
|
+
eventBus.on("orchestrator:story-complete", (payload) => {
|
|
20024
|
+
ndjsonEmitter.emit({
|
|
20025
|
+
type: "story:done",
|
|
20026
|
+
ts: new Date().toISOString(),
|
|
20027
|
+
key: payload.storyKey,
|
|
20028
|
+
result: "success",
|
|
20029
|
+
review_cycles: payload.reviewCycles
|
|
20030
|
+
});
|
|
20031
|
+
});
|
|
20032
|
+
eventBus.on("orchestrator:story-escalated", (payload) => {
|
|
20033
|
+
const rawIssues = Array.isArray(payload.issues) ? payload.issues : [];
|
|
20034
|
+
const issues = rawIssues.map((issue) => {
|
|
20035
|
+
const iss = issue;
|
|
20036
|
+
return {
|
|
20037
|
+
severity: iss.severity ?? "unknown",
|
|
20038
|
+
file: iss.file ?? "",
|
|
20039
|
+
desc: iss.desc ?? iss.description ?? ""
|
|
20040
|
+
};
|
|
20041
|
+
});
|
|
20042
|
+
ndjsonEmitter.emit({
|
|
20043
|
+
type: "story:escalation",
|
|
20044
|
+
ts: new Date().toISOString(),
|
|
20045
|
+
key: payload.storyKey,
|
|
20046
|
+
reason: payload.lastVerdict ?? "escalated",
|
|
20047
|
+
cycles: payload.reviewCycles ?? 0,
|
|
20048
|
+
issues,
|
|
20049
|
+
...payload.diagnosis !== void 0 ? { diagnosis: payload.diagnosis } : {}
|
|
20050
|
+
});
|
|
20051
|
+
});
|
|
20052
|
+
eventBus.on("orchestrator:story-warn", (payload) => {
|
|
20053
|
+
ndjsonEmitter.emit({
|
|
20054
|
+
type: "story:warn",
|
|
20055
|
+
ts: new Date().toISOString(),
|
|
20056
|
+
key: payload.storyKey,
|
|
20057
|
+
msg: payload.msg
|
|
20058
|
+
});
|
|
20059
|
+
});
|
|
20060
|
+
eventBus.on("orchestrator:heartbeat", (payload) => {
|
|
20061
|
+
ndjsonEmitter.emit({
|
|
20062
|
+
type: "pipeline:heartbeat",
|
|
20063
|
+
ts: new Date().toISOString(),
|
|
20064
|
+
run_id: payload.runId,
|
|
20065
|
+
active_dispatches: payload.activeDispatches,
|
|
20066
|
+
completed_dispatches: payload.completedDispatches,
|
|
20067
|
+
queued_dispatches: payload.queuedDispatches
|
|
20068
|
+
});
|
|
20069
|
+
});
|
|
20070
|
+
eventBus.on("orchestrator:stall", (payload) => {
|
|
20071
|
+
ndjsonEmitter.emit({
|
|
20072
|
+
type: "story:stall",
|
|
20073
|
+
ts: new Date().toISOString(),
|
|
20074
|
+
run_id: payload.runId,
|
|
20075
|
+
story_key: payload.storyKey,
|
|
20076
|
+
phase: payload.phase,
|
|
20077
|
+
elapsed_ms: payload.elapsedMs,
|
|
20078
|
+
child_pids: payload.childPids,
|
|
20079
|
+
child_active: payload.childActive
|
|
20080
|
+
});
|
|
20081
|
+
});
|
|
20082
|
+
eventBus.on("orchestrator:zero-diff-escalation", (payload) => {
|
|
20083
|
+
ndjsonEmitter.emit({
|
|
20084
|
+
type: "story:zero-diff-escalation",
|
|
20085
|
+
ts: new Date().toISOString(),
|
|
20086
|
+
storyKey: payload.storyKey,
|
|
20087
|
+
reason: payload.reason
|
|
20088
|
+
});
|
|
20089
|
+
});
|
|
20090
|
+
eventBus.on("story:build-verification-passed", (payload) => {
|
|
20091
|
+
ndjsonEmitter.emit({
|
|
20092
|
+
type: "story:build-verification-passed",
|
|
20093
|
+
ts: new Date().toISOString(),
|
|
20094
|
+
storyKey: payload.storyKey
|
|
20095
|
+
});
|
|
20096
|
+
});
|
|
20097
|
+
eventBus.on("story:build-verification-failed", (payload) => {
|
|
20098
|
+
ndjsonEmitter.emit({
|
|
20099
|
+
type: "story:build-verification-failed",
|
|
20100
|
+
ts: new Date().toISOString(),
|
|
20101
|
+
storyKey: payload.storyKey,
|
|
20102
|
+
exitCode: payload.exitCode,
|
|
20103
|
+
output: payload.output
|
|
20104
|
+
});
|
|
20105
|
+
});
|
|
20106
|
+
eventBus.on("story:interface-change-warning", (payload) => {
|
|
20107
|
+
ndjsonEmitter.emit({
|
|
20108
|
+
type: "story:interface-change-warning",
|
|
20109
|
+
ts: new Date().toISOString(),
|
|
20110
|
+
storyKey: payload.storyKey,
|
|
20111
|
+
modifiedInterfaces: payload.modifiedInterfaces,
|
|
20112
|
+
potentiallyAffectedTests: payload.potentiallyAffectedTests
|
|
20113
|
+
});
|
|
20114
|
+
});
|
|
20115
|
+
eventBus.on("story:metrics", (payload) => {
|
|
20116
|
+
ndjsonEmitter.emit({
|
|
20117
|
+
type: "story:metrics",
|
|
20118
|
+
ts: new Date().toISOString(),
|
|
20119
|
+
storyKey: payload.storyKey,
|
|
20120
|
+
wallClockMs: payload.wallClockMs,
|
|
20121
|
+
phaseBreakdown: payload.phaseBreakdown,
|
|
20122
|
+
tokens: payload.tokens,
|
|
20123
|
+
reviewCycles: payload.reviewCycles,
|
|
20124
|
+
dispatches: payload.dispatches
|
|
20125
|
+
});
|
|
20126
|
+
});
|
|
20127
|
+
eventBus.on("pipeline:pre-flight-failure", (payload) => {
|
|
20128
|
+
ndjsonEmitter.emit({
|
|
20129
|
+
type: "pipeline:pre-flight-failure",
|
|
20130
|
+
ts: new Date().toISOString(),
|
|
20131
|
+
exitCode: payload.exitCode,
|
|
20132
|
+
output: payload.output + "\nTip: Use --skip-preflight to bypass, or check your build command in .substrate/project-profile.yaml"
|
|
20133
|
+
});
|
|
20134
|
+
});
|
|
20135
|
+
eventBus.on("pipeline:contract-mismatch", (payload) => {
|
|
20136
|
+
ndjsonEmitter.emit({
|
|
20137
|
+
type: "pipeline:contract-mismatch",
|
|
20138
|
+
ts: new Date().toISOString(),
|
|
20139
|
+
exporter: payload.exporter,
|
|
20140
|
+
importer: payload.importer,
|
|
20141
|
+
contractName: payload.contractName,
|
|
20142
|
+
mismatchDescription: payload.mismatchDescription
|
|
20143
|
+
});
|
|
20144
|
+
});
|
|
20145
|
+
eventBus.on("pipeline:contract-verification-summary", (payload) => {
|
|
20146
|
+
ndjsonEmitter.emit({
|
|
20147
|
+
type: "pipeline:contract-verification-summary",
|
|
20148
|
+
ts: new Date().toISOString(),
|
|
20149
|
+
verified: payload.verified,
|
|
20150
|
+
stalePruned: payload.stalePruned,
|
|
20151
|
+
mismatches: payload.mismatches,
|
|
20152
|
+
verdict: payload.verdict
|
|
20153
|
+
});
|
|
20154
|
+
});
|
|
20155
|
+
eventBus.on("pipeline:profile-stale", (payload) => {
|
|
20156
|
+
ndjsonEmitter.emit({
|
|
20157
|
+
type: "pipeline:profile-stale",
|
|
20158
|
+
ts: new Date().toISOString(),
|
|
20159
|
+
message: payload.message,
|
|
20160
|
+
indicators: payload.indicators
|
|
20161
|
+
});
|
|
20162
|
+
});
|
|
20163
|
+
}
|
|
19718
20164
|
async function runRunAction(options) {
|
|
19719
20165
|
const { pack: packName, from: startPhase, stopAfter, concept: conceptArg, conceptFile, stories: storiesArg, concurrency, outputFormat, projectRoot, events: eventsFlag, verbose: verboseFlag, tui: tuiFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, epic: epicNumber, dryRun, maxReviewCycles = 2, registry: injectedRegistry } = options;
|
|
19720
20166
|
if (startPhase !== void 0 && !VALID_PHASES.includes(startPhase)) {
|
|
@@ -19805,7 +20251,25 @@ async function runRunAction(options) {
|
|
|
19805
20251
|
}
|
|
19806
20252
|
}
|
|
19807
20253
|
let effectiveStartPhase = startPhase;
|
|
19808
|
-
if (effectiveStartPhase === void 0) {
|
|
20254
|
+
if (effectiveStartPhase === void 0) if (parsedStoryKeys.length > 0) {
|
|
20255
|
+
const artifactsDir = join(projectRoot, "_bmad-output", "implementation-artifacts");
|
|
20256
|
+
if (existsSync(artifactsDir)) {
|
|
20257
|
+
let files;
|
|
20258
|
+
try {
|
|
20259
|
+
const result = readdirSync(artifactsDir, { encoding: "utf-8" });
|
|
20260
|
+
if (Array.isArray(result)) files = result;
|
|
20261
|
+
} catch {}
|
|
20262
|
+
if (files !== void 0) for (const key of parsedStoryKeys) {
|
|
20263
|
+
const found = files.some((f) => f.startsWith(`${key}-`) && f.endsWith(".md"));
|
|
20264
|
+
if (!found) {
|
|
20265
|
+
const errorMsg = `Story file not found for key: ${key}`;
|
|
20266
|
+
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, errorMsg) + "\n");
|
|
20267
|
+
else process.stderr.write(`Error: ${errorMsg}\n`);
|
|
20268
|
+
return 1;
|
|
20269
|
+
}
|
|
20270
|
+
}
|
|
20271
|
+
}
|
|
20272
|
+
} else {
|
|
19809
20273
|
mkdirSync(dbDir, { recursive: true });
|
|
19810
20274
|
try {
|
|
19811
20275
|
const detectAdapter = createDatabaseAdapter({
|
|
@@ -19940,6 +20404,24 @@ async function runRunAction(options) {
|
|
|
19940
20404
|
...parsedStoryKeys.length > 0 ? { explicitStories: parsedStoryKeys } : {}
|
|
19941
20405
|
})
|
|
19942
20406
|
});
|
|
20407
|
+
const runIdFilePath = join(dbDir, "current-run-id");
|
|
20408
|
+
try {
|
|
20409
|
+
writeFileSync(runIdFilePath, pipelineRun.id, "utf-8");
|
|
20410
|
+
const cleanupRunIdFile = () => {
|
|
20411
|
+
try {
|
|
20412
|
+
unlinkSync(runIdFilePath);
|
|
20413
|
+
} catch {}
|
|
20414
|
+
};
|
|
20415
|
+
process.on("exit", cleanupRunIdFile);
|
|
20416
|
+
process.once("SIGTERM", () => {
|
|
20417
|
+
cleanupRunIdFile();
|
|
20418
|
+
process.exit(0);
|
|
20419
|
+
});
|
|
20420
|
+
process.once("SIGINT", () => {
|
|
20421
|
+
cleanupRunIdFile();
|
|
20422
|
+
process.exit(130);
|
|
20423
|
+
});
|
|
20424
|
+
} catch {}
|
|
19943
20425
|
const eventBus = createEventBus();
|
|
19944
20426
|
const contextCompiler = createContextCompiler({ db: adapter });
|
|
19945
20427
|
if (!injectedRegistry) throw new Error("AdapterRegistry is required — must be initialized at CLI startup");
|
|
@@ -20213,182 +20695,7 @@ async function runRunAction(options) {
|
|
|
20213
20695
|
stories: storyKeys,
|
|
20214
20696
|
concurrency
|
|
20215
20697
|
});
|
|
20216
|
-
eventBus
|
|
20217
|
-
const phase = mapInternalPhaseToEventPhase(payload.phase);
|
|
20218
|
-
if (phase !== null) ndjsonEmitter.emit({
|
|
20219
|
-
type: "story:phase",
|
|
20220
|
-
ts: new Date().toISOString(),
|
|
20221
|
-
key: payload.storyKey,
|
|
20222
|
-
phase,
|
|
20223
|
-
status: "in_progress"
|
|
20224
|
-
});
|
|
20225
|
-
});
|
|
20226
|
-
eventBus.on("orchestrator:story-phase-complete", (payload) => {
|
|
20227
|
-
const phase = mapInternalPhaseToEventPhase(payload.phase);
|
|
20228
|
-
if (phase !== null) {
|
|
20229
|
-
const result = payload.result;
|
|
20230
|
-
ndjsonEmitter.emit({
|
|
20231
|
-
type: "story:phase",
|
|
20232
|
-
ts: new Date().toISOString(),
|
|
20233
|
-
key: payload.storyKey,
|
|
20234
|
-
phase,
|
|
20235
|
-
status: "complete",
|
|
20236
|
-
...phase === "code-review" && result?.verdict !== void 0 ? { verdict: result.verdict } : {},
|
|
20237
|
-
...phase === "create-story" && result?.story_file !== void 0 ? { file: result.story_file } : {}
|
|
20238
|
-
});
|
|
20239
|
-
}
|
|
20240
|
-
});
|
|
20241
|
-
eventBus.on("routing:model-selected", (payload) => {
|
|
20242
|
-
ndjsonEmitter.emit({
|
|
20243
|
-
type: "routing:model-selected",
|
|
20244
|
-
ts: new Date().toISOString(),
|
|
20245
|
-
dispatch_id: payload.dispatchId,
|
|
20246
|
-
task_type: payload.taskType,
|
|
20247
|
-
phase: payload.phase,
|
|
20248
|
-
model: payload.model,
|
|
20249
|
-
source: payload.source
|
|
20250
|
-
});
|
|
20251
|
-
});
|
|
20252
|
-
eventBus.on("orchestrator:story-complete", (payload) => {
|
|
20253
|
-
ndjsonEmitter.emit({
|
|
20254
|
-
type: "story:done",
|
|
20255
|
-
ts: new Date().toISOString(),
|
|
20256
|
-
key: payload.storyKey,
|
|
20257
|
-
result: "success",
|
|
20258
|
-
review_cycles: payload.reviewCycles
|
|
20259
|
-
});
|
|
20260
|
-
});
|
|
20261
|
-
eventBus.on("orchestrator:story-escalated", (payload) => {
|
|
20262
|
-
const rawIssues = Array.isArray(payload.issues) ? payload.issues : [];
|
|
20263
|
-
const issues = rawIssues.map((issue) => {
|
|
20264
|
-
const iss = issue;
|
|
20265
|
-
return {
|
|
20266
|
-
severity: iss.severity ?? "unknown",
|
|
20267
|
-
file: iss.file ?? "",
|
|
20268
|
-
desc: iss.desc ?? iss.description ?? ""
|
|
20269
|
-
};
|
|
20270
|
-
});
|
|
20271
|
-
ndjsonEmitter.emit({
|
|
20272
|
-
type: "story:escalation",
|
|
20273
|
-
ts: new Date().toISOString(),
|
|
20274
|
-
key: payload.storyKey,
|
|
20275
|
-
reason: payload.lastVerdict ?? "escalated",
|
|
20276
|
-
cycles: payload.reviewCycles ?? 0,
|
|
20277
|
-
issues,
|
|
20278
|
-
...payload.diagnosis !== void 0 ? { diagnosis: payload.diagnosis } : {}
|
|
20279
|
-
});
|
|
20280
|
-
});
|
|
20281
|
-
eventBus.on("orchestrator:story-warn", (payload) => {
|
|
20282
|
-
ndjsonEmitter.emit({
|
|
20283
|
-
type: "story:warn",
|
|
20284
|
-
ts: new Date().toISOString(),
|
|
20285
|
-
key: payload.storyKey,
|
|
20286
|
-
msg: payload.msg
|
|
20287
|
-
});
|
|
20288
|
-
});
|
|
20289
|
-
eventBus.on("orchestrator:heartbeat", (payload) => {
|
|
20290
|
-
ndjsonEmitter.emit({
|
|
20291
|
-
type: "pipeline:heartbeat",
|
|
20292
|
-
ts: new Date().toISOString(),
|
|
20293
|
-
run_id: payload.runId,
|
|
20294
|
-
active_dispatches: payload.activeDispatches,
|
|
20295
|
-
completed_dispatches: payload.completedDispatches,
|
|
20296
|
-
queued_dispatches: payload.queuedDispatches
|
|
20297
|
-
});
|
|
20298
|
-
});
|
|
20299
|
-
eventBus.on("orchestrator:stall", (payload) => {
|
|
20300
|
-
ndjsonEmitter.emit({
|
|
20301
|
-
type: "story:stall",
|
|
20302
|
-
ts: new Date().toISOString(),
|
|
20303
|
-
run_id: payload.runId,
|
|
20304
|
-
story_key: payload.storyKey,
|
|
20305
|
-
phase: payload.phase,
|
|
20306
|
-
elapsed_ms: payload.elapsedMs,
|
|
20307
|
-
child_pids: payload.childPids,
|
|
20308
|
-
child_active: payload.childActive
|
|
20309
|
-
});
|
|
20310
|
-
});
|
|
20311
|
-
eventBus.on("orchestrator:zero-diff-escalation", (payload) => {
|
|
20312
|
-
ndjsonEmitter.emit({
|
|
20313
|
-
type: "story:zero-diff-escalation",
|
|
20314
|
-
ts: new Date().toISOString(),
|
|
20315
|
-
storyKey: payload.storyKey,
|
|
20316
|
-
reason: payload.reason
|
|
20317
|
-
});
|
|
20318
|
-
});
|
|
20319
|
-
eventBus.on("story:build-verification-passed", (payload) => {
|
|
20320
|
-
ndjsonEmitter.emit({
|
|
20321
|
-
type: "story:build-verification-passed",
|
|
20322
|
-
ts: new Date().toISOString(),
|
|
20323
|
-
storyKey: payload.storyKey
|
|
20324
|
-
});
|
|
20325
|
-
});
|
|
20326
|
-
eventBus.on("story:build-verification-failed", (payload) => {
|
|
20327
|
-
ndjsonEmitter.emit({
|
|
20328
|
-
type: "story:build-verification-failed",
|
|
20329
|
-
ts: new Date().toISOString(),
|
|
20330
|
-
storyKey: payload.storyKey,
|
|
20331
|
-
exitCode: payload.exitCode,
|
|
20332
|
-
output: payload.output
|
|
20333
|
-
});
|
|
20334
|
-
});
|
|
20335
|
-
eventBus.on("story:interface-change-warning", (payload) => {
|
|
20336
|
-
ndjsonEmitter.emit({
|
|
20337
|
-
type: "story:interface-change-warning",
|
|
20338
|
-
ts: new Date().toISOString(),
|
|
20339
|
-
storyKey: payload.storyKey,
|
|
20340
|
-
modifiedInterfaces: payload.modifiedInterfaces,
|
|
20341
|
-
potentiallyAffectedTests: payload.potentiallyAffectedTests
|
|
20342
|
-
});
|
|
20343
|
-
});
|
|
20344
|
-
eventBus.on("story:metrics", (payload) => {
|
|
20345
|
-
ndjsonEmitter.emit({
|
|
20346
|
-
type: "story:metrics",
|
|
20347
|
-
ts: new Date().toISOString(),
|
|
20348
|
-
storyKey: payload.storyKey,
|
|
20349
|
-
wallClockMs: payload.wallClockMs,
|
|
20350
|
-
phaseBreakdown: payload.phaseBreakdown,
|
|
20351
|
-
tokens: payload.tokens,
|
|
20352
|
-
reviewCycles: payload.reviewCycles,
|
|
20353
|
-
dispatches: payload.dispatches
|
|
20354
|
-
});
|
|
20355
|
-
});
|
|
20356
|
-
eventBus.on("pipeline:pre-flight-failure", (payload) => {
|
|
20357
|
-
ndjsonEmitter.emit({
|
|
20358
|
-
type: "pipeline:pre-flight-failure",
|
|
20359
|
-
ts: new Date().toISOString(),
|
|
20360
|
-
exitCode: payload.exitCode,
|
|
20361
|
-
output: payload.output + "\nTip: Use --skip-preflight to bypass, or check your build command in .substrate/project-profile.yaml"
|
|
20362
|
-
});
|
|
20363
|
-
});
|
|
20364
|
-
eventBus.on("pipeline:contract-mismatch", (payload) => {
|
|
20365
|
-
ndjsonEmitter.emit({
|
|
20366
|
-
type: "pipeline:contract-mismatch",
|
|
20367
|
-
ts: new Date().toISOString(),
|
|
20368
|
-
exporter: payload.exporter,
|
|
20369
|
-
importer: payload.importer,
|
|
20370
|
-
contractName: payload.contractName,
|
|
20371
|
-
mismatchDescription: payload.mismatchDescription
|
|
20372
|
-
});
|
|
20373
|
-
});
|
|
20374
|
-
eventBus.on("pipeline:contract-verification-summary", (payload) => {
|
|
20375
|
-
ndjsonEmitter.emit({
|
|
20376
|
-
type: "pipeline:contract-verification-summary",
|
|
20377
|
-
ts: new Date().toISOString(),
|
|
20378
|
-
verified: payload.verified,
|
|
20379
|
-
stalePruned: payload.stalePruned,
|
|
20380
|
-
mismatches: payload.mismatches,
|
|
20381
|
-
verdict: payload.verdict
|
|
20382
|
-
});
|
|
20383
|
-
});
|
|
20384
|
-
eventBus.on("pipeline:profile-stale", (payload) => {
|
|
20385
|
-
ndjsonEmitter.emit({
|
|
20386
|
-
type: "pipeline:profile-stale",
|
|
20387
|
-
ts: new Date().toISOString(),
|
|
20388
|
-
message: payload.message,
|
|
20389
|
-
indicators: payload.indicators
|
|
20390
|
-
});
|
|
20391
|
-
});
|
|
20698
|
+
wireNdjsonEmitter(eventBus, ndjsonEmitter);
|
|
20392
20699
|
}
|
|
20393
20700
|
const ingestionServer = telemetryEnabled ? new IngestionServer({ port: telemetryPort }) : void 0;
|
|
20394
20701
|
if (ingestionServer !== void 0) {
|
|
@@ -20609,6 +20916,24 @@ async function runFullPipeline(options) {
|
|
|
20609
20916
|
});
|
|
20610
20917
|
const startedAt = Date.now();
|
|
20611
20918
|
const runId = await phaseOrchestrator.startRun(concept ?? "", startPhase);
|
|
20919
|
+
const runIdFilePath = join(dbDir, "current-run-id");
|
|
20920
|
+
try {
|
|
20921
|
+
writeFileSync(runIdFilePath, runId, "utf-8");
|
|
20922
|
+
const cleanupRunIdFile = () => {
|
|
20923
|
+
try {
|
|
20924
|
+
unlinkSync(runIdFilePath);
|
|
20925
|
+
} catch {}
|
|
20926
|
+
};
|
|
20927
|
+
process.on("exit", cleanupRunIdFile);
|
|
20928
|
+
process.once("SIGTERM", () => {
|
|
20929
|
+
cleanupRunIdFile();
|
|
20930
|
+
process.exit(0);
|
|
20931
|
+
});
|
|
20932
|
+
process.once("SIGINT", () => {
|
|
20933
|
+
cleanupRunIdFile();
|
|
20934
|
+
process.exit(130);
|
|
20935
|
+
});
|
|
20936
|
+
} catch {}
|
|
20612
20937
|
if (explicitStories !== void 0 && explicitStories.length > 0 || options.epic !== void 0) {
|
|
20613
20938
|
const existingRun = (await adapter.query("SELECT config_json FROM pipeline_runs WHERE id = ?", [runId]))[0];
|
|
20614
20939
|
const existing = JSON.parse(existingRun?.config_json ?? "{}");
|
|
@@ -20623,14 +20948,34 @@ async function runFullPipeline(options) {
|
|
|
20623
20948
|
process.stdout.write(`Starting full pipeline from phase: ${startPhase}\n`);
|
|
20624
20949
|
process.stdout.write(`Pipeline run ID: ${runId}\n`);
|
|
20625
20950
|
}
|
|
20951
|
+
let fullPipelineNdjsonEmitter;
|
|
20952
|
+
if (eventsFlag === true) {
|
|
20953
|
+
fullPipelineNdjsonEmitter = createEventEmitter(process.stdout);
|
|
20954
|
+
fullPipelineNdjsonEmitter.emit({
|
|
20955
|
+
type: "pipeline:start",
|
|
20956
|
+
ts: new Date().toISOString(),
|
|
20957
|
+
run_id: runId,
|
|
20958
|
+
stories: explicitStories ?? [],
|
|
20959
|
+
concurrency
|
|
20960
|
+
});
|
|
20961
|
+
wireNdjsonEmitter(eventBus, fullPipelineNdjsonEmitter);
|
|
20962
|
+
}
|
|
20626
20963
|
const phaseOrder = [];
|
|
20627
20964
|
if (effectiveResearch) phaseOrder.push("research");
|
|
20628
20965
|
phaseOrder.push("analysis", "planning");
|
|
20629
20966
|
if (effectiveUxDesign) phaseOrder.push("ux-design");
|
|
20630
20967
|
phaseOrder.push("solutioning", "implementation");
|
|
20631
20968
|
const startIdx = phaseOrder.indexOf(startPhase);
|
|
20969
|
+
const fpSucceededKeys = [];
|
|
20970
|
+
const fpFailedKeys = [];
|
|
20971
|
+
const fpEscalatedKeys = [];
|
|
20632
20972
|
for (let i = startIdx; i < phaseOrder.length; i++) {
|
|
20633
20973
|
const currentPhase = phaseOrder[i];
|
|
20974
|
+
if (fullPipelineNdjsonEmitter !== void 0) fullPipelineNdjsonEmitter.emit({
|
|
20975
|
+
type: "pipeline:phase-start",
|
|
20976
|
+
ts: new Date().toISOString(),
|
|
20977
|
+
phase: currentPhase
|
|
20978
|
+
});
|
|
20634
20979
|
if (outputFormat === "human") process.stdout.write(`\n[${currentPhase.toUpperCase()}] Starting...\n`);
|
|
20635
20980
|
if (currentPhase === "analysis") {
|
|
20636
20981
|
const result = await runAnalysisPhase(phaseDeps, {
|
|
@@ -20831,9 +21176,18 @@ async function runFullPipeline(options) {
|
|
|
20831
21176
|
});
|
|
20832
21177
|
if (storyKeys.length === 0 && outputFormat === "human") process.stdout.write("[IMPLEMENTATION] No stories found. Run solutioning first or pass --stories.\n");
|
|
20833
21178
|
if (outputFormat === "human") process.stdout.write(`[IMPLEMENTATION] Starting ${storyKeys.length} stories with concurrency=${concurrency}\n`);
|
|
20834
|
-
await orchestrator.run(storyKeys);
|
|
21179
|
+
const implStatus = await orchestrator.run(storyKeys);
|
|
20835
21180
|
if (outputFormat === "human") process.stdout.write("[IMPLEMENTATION] Complete\n");
|
|
21181
|
+
for (const [key, s] of Object.entries(implStatus.stories)) if (s.phase === "COMPLETE") fpSucceededKeys.push(key);
|
|
21182
|
+
else if (s.phase === "ESCALATED") if (s.error !== void 0) fpFailedKeys.push(key);
|
|
21183
|
+
else fpEscalatedKeys.push(key);
|
|
21184
|
+
else fpFailedKeys.push(key);
|
|
20836
21185
|
}
|
|
21186
|
+
if (fullPipelineNdjsonEmitter !== void 0) fullPipelineNdjsonEmitter.emit({
|
|
21187
|
+
type: "pipeline:phase-complete",
|
|
21188
|
+
ts: new Date().toISOString(),
|
|
21189
|
+
phase: currentPhase
|
|
21190
|
+
});
|
|
20837
21191
|
if (stopAfter !== void 0 && currentPhase === stopAfter) {
|
|
20838
21192
|
const gate = createStopAfterGate(stopAfter);
|
|
20839
21193
|
if (gate.shouldHalt()) {
|
|
@@ -20882,6 +21236,13 @@ async function runFullPipeline(options) {
|
|
|
20882
21236
|
process.stdout.write("\n");
|
|
20883
21237
|
process.stdout.write(formatTokenTelemetry(tokenSummary, BMAD_BASELINE_TOKENS_FULL) + "\n");
|
|
20884
21238
|
}
|
|
21239
|
+
if (fullPipelineNdjsonEmitter !== void 0) fullPipelineNdjsonEmitter.emit({
|
|
21240
|
+
type: "pipeline:complete",
|
|
21241
|
+
ts: new Date().toISOString(),
|
|
21242
|
+
succeeded: fpSucceededKeys,
|
|
21243
|
+
failed: fpFailedKeys,
|
|
21244
|
+
escalated: fpEscalatedKeys
|
|
21245
|
+
});
|
|
20885
21246
|
return 0;
|
|
20886
21247
|
} catch (err) {
|
|
20887
21248
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -20941,4 +21302,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
20941
21302
|
|
|
20942
21303
|
//#endregion
|
|
20943
21304
|
export { AdapterTelemetryPersistence, AppError, DEFAULT_CONFIG, DEFAULT_ROUTING_POLICY, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, GitClient, GrammarLoader, IngestionServer, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createConfigSystem, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, registerRunCommand, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
|
|
20944
|
-
//# sourceMappingURL=run-
|
|
21305
|
+
//# sourceMappingURL=run-ohzPz3r1.js.map
|