substrate-ai 0.2.19 → 0.2.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +4 -4
- package/dist/{experimenter-CHRVkV3d.js → experimenter-prkFLFPw.js} +2 -2
- package/dist/index.d.ts +13 -0
- package/dist/{operational-CobuCGbM.js → operational-Dq4IfJzE.js} +41 -2
- package/dist/run-C5zfaWYN.js +7 -0
- package/dist/{run-mFmS2pw6.js → run-CcWb6Kb-.js} +371 -104
- package/package.json +1 -1
- package/packs/bmad/prompts/code-review.md +5 -1
- package/packs/bmad/prompts/dev-story.md +3 -0
- package/dist/run-CUGB4FQx.js +0 -7
package/dist/cli/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-
|
|
2
|
+
import { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runSolutioningPhase, validateStopAfterFromConflict } from "../run-CcWb6Kb-.js";
|
|
3
3
|
import { createLogger, deepMask } from "../logger-D2fS2ccL.js";
|
|
4
4
|
import { AdapterRegistry, createEventBus } from "../event-bus-BMxhfxfT.js";
|
|
5
5
|
import { CURRENT_CONFIG_FORMAT_VERSION, CURRENT_TASK_GRAPH_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator } from "../version-manager-impl-CZ6KF1Ds.js";
|
|
6
6
|
import { ConfigError, ConfigIncompatibleFormatError } from "../errors-BPqtzQ4U.js";
|
|
7
7
|
import { addTokenUsage, createDecision, getDecisionsByCategory, getDecisionsByPhaseForRun, getLatestRun, getTokenUsageSummary, listRequirements, updatePipelineRun } from "../decisions-Dq4cAA2L.js";
|
|
8
|
-
import { EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-
|
|
8
|
+
import { EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline } from "../operational-Dq4IfJzE.js";
|
|
9
9
|
import { abortMerge, createWorktree, getConflictingFiles, getMergedFiles, getOrphanedWorktrees, performMerge, removeBranch, removeWorktree, simulateMerge, verifyGitVersion } from "../git-utils-CtmrZrHS.js";
|
|
10
10
|
import { registerUpgradeCommand } from "../upgrade-CjjAx5kD.js";
|
|
11
11
|
import { Command } from "commander";
|
|
@@ -2828,7 +2828,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
2828
2828
|
try {
|
|
2829
2829
|
const { createExperimenter } = await import(
|
|
2830
2830
|
/* @vite-ignore */
|
|
2831
|
-
"../experimenter-
|
|
2831
|
+
"../experimenter-prkFLFPw.js"
|
|
2832
2832
|
);
|
|
2833
2833
|
const { getLatestRun: getLatest } = await import(
|
|
2834
2834
|
/* @vite-ignore */
|
|
@@ -2842,7 +2842,7 @@ async function runSupervisorAction(options, deps = {}) {
|
|
|
2842
2842
|
const expDb = expDbWrapper.db;
|
|
2843
2843
|
const { runRunAction: runPipeline } = await import(
|
|
2844
2844
|
/* @vite-ignore */
|
|
2845
|
-
"../run-
|
|
2845
|
+
"../run-C5zfaWYN.js"
|
|
2846
2846
|
);
|
|
2847
2847
|
const runStoryFn = async (opts) => {
|
|
2848
2848
|
const exitCode = await runPipeline({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./logger-D2fS2ccL.js";
|
|
2
2
|
import { createDecision } from "./decisions-Dq4cAA2L.js";
|
|
3
|
-
import { EXPERIMENT_RESULT, getRunMetrics, getStoryMetricsForRun } from "./operational-
|
|
3
|
+
import { EXPERIMENT_RESULT, getRunMetrics, getStoryMetricsForRun } from "./operational-Dq4IfJzE.js";
|
|
4
4
|
import { spawnGit } from "./git-utils-CtmrZrHS.js";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { join } from "node:path";
|
|
@@ -500,4 +500,4 @@ function createExperimenter(config, deps) {
|
|
|
500
500
|
|
|
501
501
|
//#endregion
|
|
502
502
|
export { createExperimenter };
|
|
503
|
-
//# sourceMappingURL=experimenter-
|
|
503
|
+
//# sourceMappingURL=experimenter-prkFLFPw.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -995,6 +995,19 @@ interface OrchestratorEvents {
|
|
|
995
995
|
lastVerdict: string;
|
|
996
996
|
reviewCycles: number;
|
|
997
997
|
issues: unknown[];
|
|
998
|
+
/** Structured diagnosis with classification and recommended action (Story 22-3) */
|
|
999
|
+
diagnosis?: {
|
|
1000
|
+
issueDistribution: 'concentrated' | 'widespread';
|
|
1001
|
+
severityProfile: 'blocker-present' | 'major-only' | 'minor-only' | 'no-structured-issues';
|
|
1002
|
+
totalIssues: number;
|
|
1003
|
+
blockerCount: number;
|
|
1004
|
+
majorCount: number;
|
|
1005
|
+
minorCount: number;
|
|
1006
|
+
affectedFiles: string[];
|
|
1007
|
+
reviewCycles: number;
|
|
1008
|
+
recommendedAction: 'retry-targeted' | 'split-story' | 'human-intervention';
|
|
1009
|
+
rationale: string;
|
|
1010
|
+
};
|
|
998
1011
|
};
|
|
999
1012
|
/** A non-fatal warning occurred during story processing */
|
|
1000
1013
|
'orchestrator:story-warn': {
|
|
@@ -255,7 +255,46 @@ const EXPERIMENT_RESULT = "experiment-result";
|
|
|
255
255
|
* ```
|
|
256
256
|
*/
|
|
257
257
|
const STORY_METRICS = "story-metrics";
|
|
258
|
+
/**
|
|
259
|
+
* Category for structured escalation diagnoses.
|
|
260
|
+
*
|
|
261
|
+
* Key schema: "{storyKey}:{runId}"
|
|
262
|
+
*
|
|
263
|
+
* Value shape:
|
|
264
|
+
* ```json
|
|
265
|
+
* {
|
|
266
|
+
* "issueDistribution": "concentrated",
|
|
267
|
+
* "severityProfile": "major-only",
|
|
268
|
+
* "totalIssues": 3,
|
|
269
|
+
* "blockerCount": 0,
|
|
270
|
+
* "majorCount": 3,
|
|
271
|
+
* "minorCount": 0,
|
|
272
|
+
* "affectedFiles": ["src/foo.ts"],
|
|
273
|
+
* "reviewCycles": 3,
|
|
274
|
+
* "recommendedAction": "retry-targeted",
|
|
275
|
+
* "rationale": "3 major issues concentrated in few files."
|
|
276
|
+
* }
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
const ESCALATION_DIAGNOSIS = "escalation-diagnosis";
|
|
280
|
+
/**
|
|
281
|
+
* Category for per-story outcome findings (learning loop).
|
|
282
|
+
*
|
|
283
|
+
* Key schema: "{storyKey}:{runId}"
|
|
284
|
+
*
|
|
285
|
+
* Value shape:
|
|
286
|
+
* ```json
|
|
287
|
+
* {
|
|
288
|
+
* "storyKey": "22-1",
|
|
289
|
+
* "outcome": "complete",
|
|
290
|
+
* "reviewCycles": 2,
|
|
291
|
+
* "verdictHistory": ["NEEDS_MINOR_FIXES", "SHIP_IT"],
|
|
292
|
+
* "recurringPatterns": ["missing error handling"]
|
|
293
|
+
* }
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
const STORY_OUTCOME = "story-outcome";
|
|
258
297
|
|
|
259
298
|
//#endregion
|
|
260
|
-
export { EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, aggregateTokenUsageForRun, aggregateTokenUsageForStory, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline, writeRunMetrics, writeStoryMetrics };
|
|
261
|
-
//# sourceMappingURL=operational-
|
|
299
|
+
export { ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, OPERATIONAL_FINDING, STORY_METRICS, STORY_OUTCOME, aggregateTokenUsageForRun, aggregateTokenUsageForStory, compareRunMetrics, getBaselineRunMetrics, getRunMetrics, getStoryMetricsForRun, incrementRunRestarts, listRunMetrics, tagRunAsBaseline, writeRunMetrics, writeStoryMetrics };
|
|
300
|
+
//# sourceMappingURL=operational-Dq4IfJzE.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "./logger-D2fS2ccL.js";
|
|
2
2
|
import { AdapterRegistry, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning } from "./event-bus-BMxhfxfT.js";
|
|
3
|
-
import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./decisions-Dq4cAA2L.js";
|
|
4
|
-
import { STORY_METRICS, aggregateTokenUsageForRun, aggregateTokenUsageForStory, getStoryMetricsForRun, writeRunMetrics, writeStoryMetrics } from "./operational-
|
|
3
|
+
import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./decisions-Dq4cAA2L.js";
|
|
4
|
+
import { ESCALATION_DIAGNOSIS, OPERATIONAL_FINDING, STORY_METRICS, STORY_OUTCOME, aggregateTokenUsageForRun, aggregateTokenUsageForStory, getStoryMetricsForRun, writeRunMetrics, writeStoryMetrics } from "./operational-Dq4IfJzE.js";
|
|
5
5
|
import { createRequire } from "module";
|
|
6
6
|
import { dirname, join } from "path";
|
|
7
7
|
import { access, readFile, readdir, stat } from "fs/promises";
|
|
@@ -539,7 +539,7 @@ const migration010RunMetrics = {
|
|
|
539
539
|
|
|
540
540
|
//#endregion
|
|
541
541
|
//#region src/persistence/migrations/index.ts
|
|
542
|
-
const logger$
|
|
542
|
+
const logger$17 = createLogger("persistence:migrations");
|
|
543
543
|
const MIGRATIONS = [
|
|
544
544
|
initialSchemaMigration,
|
|
545
545
|
costTrackerSchemaMigration,
|
|
@@ -557,7 +557,7 @@ const MIGRATIONS = [
|
|
|
557
557
|
* Safe to call multiple times — already-applied migrations are skipped.
|
|
558
558
|
*/
|
|
559
559
|
function runMigrations(db) {
|
|
560
|
-
logger$
|
|
560
|
+
logger$17.info("Starting migration runner");
|
|
561
561
|
db.exec(`
|
|
562
562
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
563
563
|
version INTEGER PRIMARY KEY,
|
|
@@ -568,12 +568,12 @@ function runMigrations(db) {
|
|
|
568
568
|
const appliedVersions = new Set(db.prepare("SELECT version FROM schema_migrations").all().map((row) => row.version));
|
|
569
569
|
const pending = MIGRATIONS.filter((m) => !appliedVersions.has(m.version)).sort((a, b) => a.version - b.version);
|
|
570
570
|
if (pending.length === 0) {
|
|
571
|
-
logger$
|
|
571
|
+
logger$17.info("No pending migrations");
|
|
572
572
|
return;
|
|
573
573
|
}
|
|
574
574
|
const insertMigration = db.prepare("INSERT INTO schema_migrations (version, name) VALUES (?, ?)");
|
|
575
575
|
for (const migration of pending) {
|
|
576
|
-
logger$
|
|
576
|
+
logger$17.info({
|
|
577
577
|
version: migration.version,
|
|
578
578
|
name: migration.name
|
|
579
579
|
}, "Applying migration");
|
|
@@ -587,14 +587,14 @@ function runMigrations(db) {
|
|
|
587
587
|
});
|
|
588
588
|
applyMigration();
|
|
589
589
|
}
|
|
590
|
-
logger$
|
|
590
|
+
logger$17.info({ version: migration.version }, "Migration applied successfully");
|
|
591
591
|
}
|
|
592
|
-
logger$
|
|
592
|
+
logger$17.info({ count: pending.length }, "All pending migrations applied");
|
|
593
593
|
}
|
|
594
594
|
|
|
595
595
|
//#endregion
|
|
596
596
|
//#region src/persistence/database.ts
|
|
597
|
-
const logger$
|
|
597
|
+
const logger$16 = createLogger("persistence:database");
|
|
598
598
|
/**
|
|
599
599
|
* Thin wrapper that opens a SQLite database, applies required PRAGMAs,
|
|
600
600
|
* and exposes the raw BetterSqlite3 instance.
|
|
@@ -611,14 +611,14 @@ var DatabaseWrapper = class {
|
|
|
611
611
|
*/
|
|
612
612
|
open() {
|
|
613
613
|
if (this._db !== null) return;
|
|
614
|
-
logger$
|
|
614
|
+
logger$16.info({ path: this._path }, "Opening SQLite database");
|
|
615
615
|
this._db = new BetterSqlite3(this._path);
|
|
616
616
|
const walResult = this._db.pragma("journal_mode = WAL");
|
|
617
|
-
if (walResult?.[0]?.journal_mode !== "wal") logger$
|
|
617
|
+
if (walResult?.[0]?.journal_mode !== "wal") logger$16.warn({ result: walResult?.[0]?.journal_mode }, "WAL pragma did not return expected \"wal\" — journal_mode may be \"memory\" or unsupported");
|
|
618
618
|
this._db.pragma("busy_timeout = 5000");
|
|
619
619
|
this._db.pragma("synchronous = NORMAL");
|
|
620
620
|
this._db.pragma("foreign_keys = ON");
|
|
621
|
-
logger$
|
|
621
|
+
logger$16.info({ path: this._path }, "SQLite database opened with WAL mode");
|
|
622
622
|
}
|
|
623
623
|
/**
|
|
624
624
|
* Close the database. Idempotent — calling close() when already closed is a no-op.
|
|
@@ -627,7 +627,7 @@ var DatabaseWrapper = class {
|
|
|
627
627
|
if (this._db === null) return;
|
|
628
628
|
this._db.close();
|
|
629
629
|
this._db = null;
|
|
630
|
-
logger$
|
|
630
|
+
logger$16.info({ path: this._path }, "SQLite database closed");
|
|
631
631
|
}
|
|
632
632
|
/**
|
|
633
633
|
* Return the raw BetterSqlite3 instance.
|
|
@@ -2496,7 +2496,7 @@ function truncateToTokens(text, maxTokens) {
|
|
|
2496
2496
|
|
|
2497
2497
|
//#endregion
|
|
2498
2498
|
//#region src/modules/context-compiler/context-compiler-impl.ts
|
|
2499
|
-
const logger$
|
|
2499
|
+
const logger$15 = createLogger("context-compiler");
|
|
2500
2500
|
/**
|
|
2501
2501
|
* Fraction of the original token budget that must remain (after required +
|
|
2502
2502
|
* important sections) before an optional section is included.
|
|
@@ -2588,7 +2588,7 @@ var ContextCompilerImpl = class {
|
|
|
2588
2588
|
includedParts.push(truncated);
|
|
2589
2589
|
remainingBudget -= truncatedTokens;
|
|
2590
2590
|
anyTruncated = true;
|
|
2591
|
-
logger$
|
|
2591
|
+
logger$15.warn({
|
|
2592
2592
|
section: section.name,
|
|
2593
2593
|
originalTokens: tokens,
|
|
2594
2594
|
budgetTokens: truncatedTokens
|
|
@@ -2602,7 +2602,7 @@ var ContextCompilerImpl = class {
|
|
|
2602
2602
|
});
|
|
2603
2603
|
} else {
|
|
2604
2604
|
anyTruncated = true;
|
|
2605
|
-
logger$
|
|
2605
|
+
logger$15.warn({
|
|
2606
2606
|
section: section.name,
|
|
2607
2607
|
tokens
|
|
2608
2608
|
}, "Context compiler: omitted \"important\" section — no budget remaining");
|
|
@@ -2629,7 +2629,7 @@ var ContextCompilerImpl = class {
|
|
|
2629
2629
|
} else {
|
|
2630
2630
|
if (tokens > 0) {
|
|
2631
2631
|
anyTruncated = true;
|
|
2632
|
-
logger$
|
|
2632
|
+
logger$15.warn({
|
|
2633
2633
|
section: section.name,
|
|
2634
2634
|
tokens,
|
|
2635
2635
|
budgetFractionRemaining: budgetFractionRemaining.toFixed(2)
|
|
@@ -2914,7 +2914,7 @@ function parseYamlResult(yamlText, schema) {
|
|
|
2914
2914
|
|
|
2915
2915
|
//#endregion
|
|
2916
2916
|
//#region src/modules/agent-dispatch/dispatcher-impl.ts
|
|
2917
|
-
const logger$
|
|
2917
|
+
const logger$14 = createLogger("agent-dispatch");
|
|
2918
2918
|
const SHUTDOWN_GRACE_MS = 1e4;
|
|
2919
2919
|
const SHUTDOWN_MAX_WAIT_MS = 3e4;
|
|
2920
2920
|
const CHARS_PER_TOKEN = 4;
|
|
@@ -2957,7 +2957,7 @@ function getAvailableMemory() {
|
|
|
2957
2957
|
encoding: "utf-8"
|
|
2958
2958
|
}).trim(), 10);
|
|
2959
2959
|
if (pressureLevel >= 4) {
|
|
2960
|
-
logger$
|
|
2960
|
+
logger$14.warn({ pressureLevel }, "macOS kernel reports critical memory pressure");
|
|
2961
2961
|
return 0;
|
|
2962
2962
|
}
|
|
2963
2963
|
} catch {}
|
|
@@ -2972,7 +2972,7 @@ function getAvailableMemory() {
|
|
|
2972
2972
|
const speculative = parseInt(vmstat.match(/Pages speculative:\s+(\d+)/)?.[1] ?? "0", 10);
|
|
2973
2973
|
const available = (free + purgeable + speculative) * pageSize;
|
|
2974
2974
|
if (pressureLevel >= 2) {
|
|
2975
|
-
logger$
|
|
2975
|
+
logger$14.warn({
|
|
2976
2976
|
pressureLevel,
|
|
2977
2977
|
availableBeforeDiscount: available
|
|
2978
2978
|
}, "macOS kernel reports memory pressure — discounting estimate");
|
|
@@ -3051,7 +3051,7 @@ var DispatcherImpl = class {
|
|
|
3051
3051
|
resolve: typedResolve,
|
|
3052
3052
|
reject
|
|
3053
3053
|
});
|
|
3054
|
-
logger$
|
|
3054
|
+
logger$14.debug({
|
|
3055
3055
|
id,
|
|
3056
3056
|
queueLength: this._queue.length
|
|
3057
3057
|
}, "Dispatch queued");
|
|
@@ -3082,7 +3082,7 @@ var DispatcherImpl = class {
|
|
|
3082
3082
|
async shutdown() {
|
|
3083
3083
|
this._shuttingDown = true;
|
|
3084
3084
|
this._stopMemoryPressureTimer();
|
|
3085
|
-
logger$
|
|
3085
|
+
logger$14.info({
|
|
3086
3086
|
running: this._running.size,
|
|
3087
3087
|
queued: this._queue.length
|
|
3088
3088
|
}, "Dispatcher shutting down");
|
|
@@ -3115,13 +3115,13 @@ var DispatcherImpl = class {
|
|
|
3115
3115
|
}
|
|
3116
3116
|
}, 50);
|
|
3117
3117
|
});
|
|
3118
|
-
logger$
|
|
3118
|
+
logger$14.info("Dispatcher shutdown complete");
|
|
3119
3119
|
}
|
|
3120
3120
|
async _startDispatch(id, request, resolve$2) {
|
|
3121
3121
|
const { prompt, agent, taskType, timeout, outputSchema, workingDirectory, model, maxTurns } = request;
|
|
3122
3122
|
const adapter = this._adapterRegistry.get(agent);
|
|
3123
3123
|
if (adapter === void 0) {
|
|
3124
|
-
logger$
|
|
3124
|
+
logger$14.warn({
|
|
3125
3125
|
id,
|
|
3126
3126
|
agent
|
|
3127
3127
|
}, "No adapter found for agent");
|
|
@@ -3167,7 +3167,7 @@ var DispatcherImpl = class {
|
|
|
3167
3167
|
});
|
|
3168
3168
|
const startedAt = Date.now();
|
|
3169
3169
|
proc.on("error", (err) => {
|
|
3170
|
-
logger$
|
|
3170
|
+
logger$14.error({
|
|
3171
3171
|
id,
|
|
3172
3172
|
binary: cmd.binary,
|
|
3173
3173
|
error: err.message
|
|
@@ -3175,7 +3175,7 @@ var DispatcherImpl = class {
|
|
|
3175
3175
|
});
|
|
3176
3176
|
if (proc.stdin !== null) {
|
|
3177
3177
|
proc.stdin.on("error", (err) => {
|
|
3178
|
-
if (err.code !== "EPIPE") logger$
|
|
3178
|
+
if (err.code !== "EPIPE") logger$14.warn({
|
|
3179
3179
|
id,
|
|
3180
3180
|
error: err.message
|
|
3181
3181
|
}, "stdin write error");
|
|
@@ -3217,7 +3217,7 @@ var DispatcherImpl = class {
|
|
|
3217
3217
|
agent,
|
|
3218
3218
|
taskType
|
|
3219
3219
|
});
|
|
3220
|
-
logger$
|
|
3220
|
+
logger$14.debug({
|
|
3221
3221
|
id,
|
|
3222
3222
|
agent,
|
|
3223
3223
|
taskType,
|
|
@@ -3234,7 +3234,7 @@ var DispatcherImpl = class {
|
|
|
3234
3234
|
dispatchId: id,
|
|
3235
3235
|
timeoutMs
|
|
3236
3236
|
});
|
|
3237
|
-
logger$
|
|
3237
|
+
logger$14.warn({
|
|
3238
3238
|
id,
|
|
3239
3239
|
agent,
|
|
3240
3240
|
taskType,
|
|
@@ -3288,7 +3288,7 @@ var DispatcherImpl = class {
|
|
|
3288
3288
|
exitCode: code,
|
|
3289
3289
|
output: stdout
|
|
3290
3290
|
});
|
|
3291
|
-
logger$
|
|
3291
|
+
logger$14.debug({
|
|
3292
3292
|
id,
|
|
3293
3293
|
agent,
|
|
3294
3294
|
taskType,
|
|
@@ -3314,7 +3314,7 @@ var DispatcherImpl = class {
|
|
|
3314
3314
|
error: stderr || `Process exited with code ${String(code)}`,
|
|
3315
3315
|
exitCode: code
|
|
3316
3316
|
});
|
|
3317
|
-
logger$
|
|
3317
|
+
logger$14.debug({
|
|
3318
3318
|
id,
|
|
3319
3319
|
agent,
|
|
3320
3320
|
taskType,
|
|
@@ -3373,7 +3373,7 @@ var DispatcherImpl = class {
|
|
|
3373
3373
|
const next = this._queue.shift();
|
|
3374
3374
|
if (next === void 0) return;
|
|
3375
3375
|
next.handle.status = "running";
|
|
3376
|
-
logger$
|
|
3376
|
+
logger$14.debug({
|
|
3377
3377
|
id: next.id,
|
|
3378
3378
|
queueLength: this._queue.length
|
|
3379
3379
|
}, "Dequeued dispatch");
|
|
@@ -3386,7 +3386,7 @@ var DispatcherImpl = class {
|
|
|
3386
3386
|
_isMemoryPressured() {
|
|
3387
3387
|
const free = getAvailableMemory();
|
|
3388
3388
|
if (free < MIN_FREE_MEMORY_BYTES) {
|
|
3389
|
-
logger$
|
|
3389
|
+
logger$14.warn({
|
|
3390
3390
|
freeMB: Math.round(free / 1024 / 1024),
|
|
3391
3391
|
thresholdMB: Math.round(MIN_FREE_MEMORY_BYTES / 1024 / 1024)
|
|
3392
3392
|
}, "Memory pressure detected — holding dispatch queue");
|
|
@@ -3424,9 +3424,94 @@ function createDispatcher(options) {
|
|
|
3424
3424
|
return new DispatcherImpl(options.eventBus, options.adapterRegistry, config);
|
|
3425
3425
|
}
|
|
3426
3426
|
|
|
3427
|
+
//#endregion
|
|
3428
|
+
//#region src/modules/implementation-orchestrator/escalation-diagnosis.ts
|
|
3429
|
+
/**
|
|
3430
|
+
* Generate a structured diagnosis from escalation data.
|
|
3431
|
+
*
|
|
3432
|
+
* Handles both structured issue lists (from code-review) and plain string
|
|
3433
|
+
* arrays (from create-story/dev-story failures).
|
|
3434
|
+
*/
|
|
3435
|
+
function generateEscalationDiagnosis(issues, reviewCycles, lastVerdict) {
|
|
3436
|
+
const structured = issues.map((issue) => {
|
|
3437
|
+
if (typeof issue === "string") return {
|
|
3438
|
+
severity: "major",
|
|
3439
|
+
description: issue
|
|
3440
|
+
};
|
|
3441
|
+
const iss = issue;
|
|
3442
|
+
return {
|
|
3443
|
+
severity: iss.severity ?? "unknown",
|
|
3444
|
+
description: iss.description ?? "",
|
|
3445
|
+
file: iss.file,
|
|
3446
|
+
line: iss.line
|
|
3447
|
+
};
|
|
3448
|
+
});
|
|
3449
|
+
const blockerCount = structured.filter((i) => i.severity === "blocker").length;
|
|
3450
|
+
const majorCount = structured.filter((i) => i.severity === "major").length;
|
|
3451
|
+
const minorCount = structured.filter((i) => i.severity === "minor").length;
|
|
3452
|
+
const totalIssues = structured.length;
|
|
3453
|
+
const fileCounts = new Map();
|
|
3454
|
+
for (const issue of structured) if (issue.file) fileCounts.set(issue.file, (fileCounts.get(issue.file) ?? 0) + 1);
|
|
3455
|
+
const sortedFiles = [...fileCounts.entries()].sort((a, b) => b[1] - a[1]).map(([file]) => file);
|
|
3456
|
+
const issuesWithFiles = structured.filter((i) => i.file).length;
|
|
3457
|
+
let issueDistribution = "widespread";
|
|
3458
|
+
if (issuesWithFiles > 0 && sortedFiles.length > 0) {
|
|
3459
|
+
const topTwoCount = sortedFiles.slice(0, 2).reduce((sum, file) => sum + (fileCounts.get(file) ?? 0), 0);
|
|
3460
|
+
if (topTwoCount > issuesWithFiles * .5) issueDistribution = "concentrated";
|
|
3461
|
+
}
|
|
3462
|
+
let severityProfile;
|
|
3463
|
+
if (totalIssues === 0) severityProfile = "no-structured-issues";
|
|
3464
|
+
else if (blockerCount > 0) severityProfile = "blocker-present";
|
|
3465
|
+
else if (majorCount > 0) severityProfile = "major-only";
|
|
3466
|
+
else severityProfile = "minor-only";
|
|
3467
|
+
const { action, rationale } = pickRecommendation(issueDistribution, severityProfile, totalIssues, reviewCycles, lastVerdict);
|
|
3468
|
+
return {
|
|
3469
|
+
issueDistribution,
|
|
3470
|
+
severityProfile,
|
|
3471
|
+
totalIssues,
|
|
3472
|
+
blockerCount,
|
|
3473
|
+
majorCount,
|
|
3474
|
+
minorCount,
|
|
3475
|
+
affectedFiles: sortedFiles.slice(0, 5),
|
|
3476
|
+
reviewCycles,
|
|
3477
|
+
recommendedAction: action,
|
|
3478
|
+
rationale
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
function pickRecommendation(distribution, profile, totalIssues, reviewCycles, lastVerdict) {
|
|
3482
|
+
if (lastVerdict.startsWith("create-story") || lastVerdict.startsWith("dev-story")) return {
|
|
3483
|
+
action: "human-intervention",
|
|
3484
|
+
rationale: `Pipeline failed during ${lastVerdict.replace(/-/g, " ")} before code review. Manual investigation needed.`
|
|
3485
|
+
};
|
|
3486
|
+
if (lastVerdict === "fix-dispatch-timeout") return {
|
|
3487
|
+
action: "retry-targeted",
|
|
3488
|
+
rationale: "Fix dispatch timed out. Retry with a targeted prompt focusing on the remaining issues."
|
|
3489
|
+
};
|
|
3490
|
+
if (profile === "no-structured-issues") return {
|
|
3491
|
+
action: "retry-targeted",
|
|
3492
|
+
rationale: "Review produced no structured issues — likely a schema parse failure. Retry may resolve."
|
|
3493
|
+
};
|
|
3494
|
+
if (profile === "blocker-present") return {
|
|
3495
|
+
action: "human-intervention",
|
|
3496
|
+
rationale: `${totalIssues} issues including blockers after ${reviewCycles} review cycles. Fundamental problems remain — human review recommended.`
|
|
3497
|
+
};
|
|
3498
|
+
if (distribution === "concentrated" && profile === "major-only" && totalIssues <= 5) return {
|
|
3499
|
+
action: "retry-targeted",
|
|
3500
|
+
rationale: `${totalIssues} major issues concentrated in few files. A targeted retry prompt could resolve them.`
|
|
3501
|
+
};
|
|
3502
|
+
if (distribution === "widespread" && totalIssues > 3) return {
|
|
3503
|
+
action: "split-story",
|
|
3504
|
+
rationale: `${totalIssues} issues spread across many files after ${reviewCycles} cycles. Consider splitting into smaller stories.`
|
|
3505
|
+
};
|
|
3506
|
+
return {
|
|
3507
|
+
action: "retry-targeted",
|
|
3508
|
+
rationale: `${totalIssues} issues (${profile}) after ${reviewCycles} cycles. Retry with focused prompt.`
|
|
3509
|
+
};
|
|
3510
|
+
}
|
|
3511
|
+
|
|
3427
3512
|
//#endregion
|
|
3428
3513
|
//#region src/modules/compiled-workflows/prompt-assembler.ts
|
|
3429
|
-
const logger$
|
|
3514
|
+
const logger$13 = createLogger("compiled-workflows:prompt-assembler");
|
|
3430
3515
|
/**
|
|
3431
3516
|
* Assemble a final prompt from a template and sections map.
|
|
3432
3517
|
*
|
|
@@ -3451,7 +3536,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
3451
3536
|
tokenCount,
|
|
3452
3537
|
truncated: false
|
|
3453
3538
|
};
|
|
3454
|
-
logger$
|
|
3539
|
+
logger$13.warn({
|
|
3455
3540
|
tokenCount,
|
|
3456
3541
|
ceiling: tokenCeiling
|
|
3457
3542
|
}, "Prompt exceeds token ceiling — truncating optional sections");
|
|
@@ -3467,10 +3552,10 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
3467
3552
|
const targetSectionTokens = Math.max(0, currentSectionTokens - overBy);
|
|
3468
3553
|
if (targetSectionTokens === 0) {
|
|
3469
3554
|
contentMap[section.name] = "";
|
|
3470
|
-
logger$
|
|
3555
|
+
logger$13.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
|
|
3471
3556
|
} else {
|
|
3472
3557
|
contentMap[section.name] = truncateToTokens(section.content, targetSectionTokens);
|
|
3473
|
-
logger$
|
|
3558
|
+
logger$13.warn({
|
|
3474
3559
|
sectionName: section.name,
|
|
3475
3560
|
targetSectionTokens
|
|
3476
3561
|
}, "Section truncated to fit token budget");
|
|
@@ -3481,7 +3566,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
3481
3566
|
}
|
|
3482
3567
|
if (tokenCount <= tokenCeiling) break;
|
|
3483
3568
|
}
|
|
3484
|
-
if (tokenCount > tokenCeiling) logger$
|
|
3569
|
+
if (tokenCount > tokenCeiling) logger$13.warn({
|
|
3485
3570
|
tokenCount,
|
|
3486
3571
|
ceiling: tokenCeiling
|
|
3487
3572
|
}, "Required sections alone exceed token ceiling — returning over-budget prompt");
|
|
@@ -3622,7 +3707,7 @@ const CodeReviewResultSchema = z.object({
|
|
|
3622
3707
|
|
|
3623
3708
|
//#endregion
|
|
3624
3709
|
//#region src/modules/compiled-workflows/create-story.ts
|
|
3625
|
-
const logger$
|
|
3710
|
+
const logger$12 = createLogger("compiled-workflows:create-story");
|
|
3626
3711
|
/**
|
|
3627
3712
|
* Hard ceiling for the assembled create-story prompt.
|
|
3628
3713
|
*/
|
|
@@ -3646,7 +3731,7 @@ const TOKEN_CEILING$2 = 3e3;
|
|
|
3646
3731
|
*/
|
|
3647
3732
|
async function runCreateStory(deps, params) {
|
|
3648
3733
|
const { epicId, storyKey, pipelineRunId } = params;
|
|
3649
|
-
logger$
|
|
3734
|
+
logger$12.debug({
|
|
3650
3735
|
epicId,
|
|
3651
3736
|
storyKey,
|
|
3652
3737
|
pipelineRunId
|
|
@@ -3656,7 +3741,7 @@ async function runCreateStory(deps, params) {
|
|
|
3656
3741
|
template = await deps.pack.getPrompt("create-story");
|
|
3657
3742
|
} catch (err) {
|
|
3658
3743
|
const error = err instanceof Error ? err.message : String(err);
|
|
3659
|
-
logger$
|
|
3744
|
+
logger$12.error({ error }, "Failed to retrieve create-story prompt template");
|
|
3660
3745
|
return {
|
|
3661
3746
|
result: "failed",
|
|
3662
3747
|
error: `Failed to retrieve prompt template: ${error}`,
|
|
@@ -3698,7 +3783,7 @@ async function runCreateStory(deps, params) {
|
|
|
3698
3783
|
priority: "important"
|
|
3699
3784
|
}
|
|
3700
3785
|
], TOKEN_CEILING$2);
|
|
3701
|
-
logger$
|
|
3786
|
+
logger$12.debug({
|
|
3702
3787
|
tokenCount,
|
|
3703
3788
|
truncated,
|
|
3704
3789
|
tokenCeiling: TOKEN_CEILING$2
|
|
@@ -3715,7 +3800,7 @@ async function runCreateStory(deps, params) {
|
|
|
3715
3800
|
dispatchResult = await handle.result;
|
|
3716
3801
|
} catch (err) {
|
|
3717
3802
|
const error = err instanceof Error ? err.message : String(err);
|
|
3718
|
-
logger$
|
|
3803
|
+
logger$12.error({
|
|
3719
3804
|
epicId,
|
|
3720
3805
|
storyKey,
|
|
3721
3806
|
error
|
|
@@ -3736,7 +3821,7 @@ async function runCreateStory(deps, params) {
|
|
|
3736
3821
|
if (dispatchResult.status === "failed") {
|
|
3737
3822
|
const errorMsg = dispatchResult.parseError ?? `Dispatch failed with exit code ${dispatchResult.exitCode}`;
|
|
3738
3823
|
const stderrDetail = dispatchResult.output ? ` Output: ${dispatchResult.output}` : "";
|
|
3739
|
-
logger$
|
|
3824
|
+
logger$12.warn({
|
|
3740
3825
|
epicId,
|
|
3741
3826
|
storyKey,
|
|
3742
3827
|
exitCode: dispatchResult.exitCode
|
|
@@ -3748,7 +3833,7 @@ async function runCreateStory(deps, params) {
|
|
|
3748
3833
|
};
|
|
3749
3834
|
}
|
|
3750
3835
|
if (dispatchResult.status === "timeout") {
|
|
3751
|
-
logger$
|
|
3836
|
+
logger$12.warn({
|
|
3752
3837
|
epicId,
|
|
3753
3838
|
storyKey
|
|
3754
3839
|
}, "Create-story dispatch timed out");
|
|
@@ -3761,7 +3846,7 @@ async function runCreateStory(deps, params) {
|
|
|
3761
3846
|
if (dispatchResult.parsed === null) {
|
|
3762
3847
|
const details = dispatchResult.parseError ?? "No YAML block found in output";
|
|
3763
3848
|
const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
|
|
3764
|
-
logger$
|
|
3849
|
+
logger$12.warn({
|
|
3765
3850
|
epicId,
|
|
3766
3851
|
storyKey,
|
|
3767
3852
|
details,
|
|
@@ -3777,7 +3862,7 @@ async function runCreateStory(deps, params) {
|
|
|
3777
3862
|
const parseResult = CreateStoryResultSchema.safeParse(dispatchResult.parsed);
|
|
3778
3863
|
if (!parseResult.success) {
|
|
3779
3864
|
const details = parseResult.error.message;
|
|
3780
|
-
logger$
|
|
3865
|
+
logger$12.warn({
|
|
3781
3866
|
epicId,
|
|
3782
3867
|
storyKey,
|
|
3783
3868
|
details
|
|
@@ -3790,7 +3875,7 @@ async function runCreateStory(deps, params) {
|
|
|
3790
3875
|
};
|
|
3791
3876
|
}
|
|
3792
3877
|
const parsed = parseResult.data;
|
|
3793
|
-
logger$
|
|
3878
|
+
logger$12.info({
|
|
3794
3879
|
epicId,
|
|
3795
3880
|
storyKey,
|
|
3796
3881
|
storyFile: parsed.story_file,
|
|
@@ -3812,7 +3897,7 @@ function getImplementationDecisions(deps) {
|
|
|
3812
3897
|
try {
|
|
3813
3898
|
return getDecisionsByPhase(deps.db, "implementation");
|
|
3814
3899
|
} catch (err) {
|
|
3815
|
-
logger$
|
|
3900
|
+
logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
|
|
3816
3901
|
return [];
|
|
3817
3902
|
}
|
|
3818
3903
|
}
|
|
@@ -3828,13 +3913,13 @@ function getEpicShard(decisions, epicId, projectRoot) {
|
|
|
3828
3913
|
if (projectRoot) {
|
|
3829
3914
|
const fallback = readEpicShardFromFile(projectRoot, epicId);
|
|
3830
3915
|
if (fallback) {
|
|
3831
|
-
logger$
|
|
3916
|
+
logger$12.info({ epicId }, "Using file-based fallback for epic shard (decisions table empty)");
|
|
3832
3917
|
return fallback;
|
|
3833
3918
|
}
|
|
3834
3919
|
}
|
|
3835
3920
|
return "";
|
|
3836
3921
|
} catch (err) {
|
|
3837
|
-
logger$
|
|
3922
|
+
logger$12.warn({
|
|
3838
3923
|
epicId,
|
|
3839
3924
|
error: err instanceof Error ? err.message : String(err)
|
|
3840
3925
|
}, "Failed to retrieve epic shard");
|
|
@@ -3851,7 +3936,7 @@ function getPrevDevNotes(decisions, epicId) {
|
|
|
3851
3936
|
if (devNotes.length === 0) return "";
|
|
3852
3937
|
return devNotes[devNotes.length - 1].value;
|
|
3853
3938
|
} catch (err) {
|
|
3854
|
-
logger$
|
|
3939
|
+
logger$12.warn({
|
|
3855
3940
|
epicId,
|
|
3856
3941
|
error: err instanceof Error ? err.message : String(err)
|
|
3857
3942
|
}, "Failed to retrieve prev dev notes");
|
|
@@ -3871,13 +3956,13 @@ function getArchConstraints$1(deps) {
|
|
|
3871
3956
|
if (deps.projectRoot) {
|
|
3872
3957
|
const fallback = readArchConstraintsFromFile(deps.projectRoot);
|
|
3873
3958
|
if (fallback) {
|
|
3874
|
-
logger$
|
|
3959
|
+
logger$12.info("Using file-based fallback for architecture constraints (decisions table empty)");
|
|
3875
3960
|
return fallback;
|
|
3876
3961
|
}
|
|
3877
3962
|
}
|
|
3878
3963
|
return "";
|
|
3879
3964
|
} catch (err) {
|
|
3880
|
-
logger$
|
|
3965
|
+
logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
|
|
3881
3966
|
return "";
|
|
3882
3967
|
}
|
|
3883
3968
|
}
|
|
@@ -3897,7 +3982,7 @@ function readEpicShardFromFile(projectRoot, epicId) {
|
|
|
3897
3982
|
const match = pattern.exec(content);
|
|
3898
3983
|
return match ? match[0].trim() : "";
|
|
3899
3984
|
} catch (err) {
|
|
3900
|
-
logger$
|
|
3985
|
+
logger$12.warn({
|
|
3901
3986
|
epicId,
|
|
3902
3987
|
error: err instanceof Error ? err.message : String(err)
|
|
3903
3988
|
}, "File-based epic shard fallback failed");
|
|
@@ -3920,7 +4005,7 @@ function readArchConstraintsFromFile(projectRoot) {
|
|
|
3920
4005
|
const content = readFileSync$1(archPath, "utf-8");
|
|
3921
4006
|
return content.slice(0, 1500);
|
|
3922
4007
|
} catch (err) {
|
|
3923
|
-
logger$
|
|
4008
|
+
logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
|
|
3924
4009
|
return "";
|
|
3925
4010
|
}
|
|
3926
4011
|
}
|
|
@@ -3933,14 +4018,14 @@ async function getStoryTemplate(deps) {
|
|
|
3933
4018
|
try {
|
|
3934
4019
|
return await deps.pack.getTemplate("story");
|
|
3935
4020
|
} catch (err) {
|
|
3936
|
-
logger$
|
|
4021
|
+
logger$12.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
|
|
3937
4022
|
return "";
|
|
3938
4023
|
}
|
|
3939
4024
|
}
|
|
3940
4025
|
|
|
3941
4026
|
//#endregion
|
|
3942
4027
|
//#region src/modules/compiled-workflows/git-helpers.ts
|
|
3943
|
-
const logger$
|
|
4028
|
+
const logger$11 = createLogger("compiled-workflows:git-helpers");
|
|
3944
4029
|
/**
|
|
3945
4030
|
* Capture the full git diff for HEAD (working tree vs current commit).
|
|
3946
4031
|
*
|
|
@@ -4064,7 +4149,7 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
4064
4149
|
stderr += chunk.toString("utf-8");
|
|
4065
4150
|
});
|
|
4066
4151
|
proc.on("error", (err) => {
|
|
4067
|
-
logger$
|
|
4152
|
+
logger$11.warn({
|
|
4068
4153
|
label: logLabel,
|
|
4069
4154
|
cwd,
|
|
4070
4155
|
error: err.message
|
|
@@ -4073,7 +4158,7 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
4073
4158
|
});
|
|
4074
4159
|
proc.on("close", (code) => {
|
|
4075
4160
|
if (code !== 0) {
|
|
4076
|
-
logger$
|
|
4161
|
+
logger$11.warn({
|
|
4077
4162
|
label: logLabel,
|
|
4078
4163
|
cwd,
|
|
4079
4164
|
code,
|
|
@@ -4087,6 +4172,84 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
4087
4172
|
});
|
|
4088
4173
|
}
|
|
4089
4174
|
|
|
4175
|
+
//#endregion
|
|
4176
|
+
//#region src/modules/implementation-orchestrator/project-findings.ts
|
|
4177
|
+
const logger$10 = createLogger("project-findings");
|
|
4178
|
+
/** Maximum character length for the findings summary */
|
|
4179
|
+
const MAX_CHARS = 2e3;
|
|
4180
|
+
/**
|
|
4181
|
+
* Query the decision store for prior project findings and return a formatted
|
|
4182
|
+
* markdown summary suitable for prompt injection.
|
|
4183
|
+
*
|
|
4184
|
+
* Returns an empty string if no findings exist (AC5: graceful fallback).
|
|
4185
|
+
*/
|
|
4186
|
+
function getProjectFindings(db) {
|
|
4187
|
+
try {
|
|
4188
|
+
const outcomes = getDecisionsByCategory(db, STORY_OUTCOME);
|
|
4189
|
+
const operational = getDecisionsByCategory(db, OPERATIONAL_FINDING);
|
|
4190
|
+
const metrics = getDecisionsByCategory(db, STORY_METRICS);
|
|
4191
|
+
const diagnoses = getDecisionsByCategory(db, ESCALATION_DIAGNOSIS);
|
|
4192
|
+
if (outcomes.length === 0 && operational.length === 0 && metrics.length === 0 && diagnoses.length === 0) return "";
|
|
4193
|
+
const sections = [];
|
|
4194
|
+
if (outcomes.length > 0) {
|
|
4195
|
+
const patterns = extractRecurringPatterns(outcomes);
|
|
4196
|
+
if (patterns.length > 0) {
|
|
4197
|
+
sections.push("**Recurring patterns from prior runs:**");
|
|
4198
|
+
for (const p of patterns) sections.push(`- ${p}`);
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
if (diagnoses.length > 0) {
|
|
4202
|
+
sections.push("**Prior escalations:**");
|
|
4203
|
+
for (const d of diagnoses.slice(-3)) try {
|
|
4204
|
+
const val = JSON.parse(d.value);
|
|
4205
|
+
sections.push(`- ${d.key.split(":")[0]}: ${val.recommendedAction} — ${val.rationale}`);
|
|
4206
|
+
} catch {
|
|
4207
|
+
sections.push(`- ${d.key}: escalated`);
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
const highCycleStories = metrics.filter((m) => {
|
|
4211
|
+
try {
|
|
4212
|
+
const val = JSON.parse(m.value);
|
|
4213
|
+
return val.review_cycles >= 2;
|
|
4214
|
+
} catch {
|
|
4215
|
+
return false;
|
|
4216
|
+
}
|
|
4217
|
+
}).slice(-5);
|
|
4218
|
+
if (highCycleStories.length > 0) {
|
|
4219
|
+
sections.push("**Stories with high review cycles:**");
|
|
4220
|
+
for (const m of highCycleStories) try {
|
|
4221
|
+
const val = JSON.parse(m.value);
|
|
4222
|
+
sections.push(`- ${m.key.split(":")[0]}: ${val.review_cycles} cycles`);
|
|
4223
|
+
} catch {}
|
|
4224
|
+
}
|
|
4225
|
+
const stalls = operational.filter((o) => o.key.startsWith("stall:"));
|
|
4226
|
+
if (stalls.length > 0) sections.push(`**Prior stalls:** ${stalls.length} stall event(s) recorded`);
|
|
4227
|
+
if (sections.length === 0) return "";
|
|
4228
|
+
let summary = sections.join("\n");
|
|
4229
|
+
if (summary.length > MAX_CHARS) summary = summary.slice(0, MAX_CHARS - 3) + "...";
|
|
4230
|
+
return summary;
|
|
4231
|
+
} catch (err) {
|
|
4232
|
+
logger$10.warn({ err }, "Failed to query project findings (graceful fallback)");
|
|
4233
|
+
return "";
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
/**
|
|
4237
|
+
* Extract recurring patterns from story-outcome decisions.
|
|
4238
|
+
*
|
|
4239
|
+
* Looks for patterns that appear across multiple story outcomes
|
|
4240
|
+
* (e.g., "missing error handling" flagged in 3/5 stories).
|
|
4241
|
+
*/
|
|
4242
|
+
function extractRecurringPatterns(outcomes) {
|
|
4243
|
+
const patternCounts = new Map();
|
|
4244
|
+
for (const o of outcomes) try {
|
|
4245
|
+
const val = JSON.parse(o.value);
|
|
4246
|
+
if (Array.isArray(val.recurringPatterns)) {
|
|
4247
|
+
for (const pattern of val.recurringPatterns) if (typeof pattern === "string") patternCounts.set(pattern, (patternCounts.get(pattern) ?? 0) + 1);
|
|
4248
|
+
}
|
|
4249
|
+
} catch {}
|
|
4250
|
+
return [...patternCounts.entries()].filter(([, count]) => count >= 2).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => `${pattern} (${count} occurrences)`);
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4090
4253
|
//#endregion
|
|
4091
4254
|
//#region src/modules/compiled-workflows/dev-story.ts
|
|
4092
4255
|
const logger$9 = createLogger("compiled-workflows:dev-story");
|
|
@@ -4217,6 +4380,17 @@ async function runDevStory(deps, params) {
|
|
|
4217
4380
|
const priorFilesContent = priorFiles !== void 0 && priorFiles.length > 0 ? `## Files Modified by Previous Batches\n\nThe following files were created or modified by prior batch dispatches. Review them for context before implementing:\n\n${priorFiles.map((f) => `- ${f}`).join("\n")}` : "";
|
|
4218
4381
|
const filesInScopeContent = extractFilesInScope(storyContent);
|
|
4219
4382
|
const projectContextContent = deps.projectRoot ? await buildProjectContext(storyContent, deps.projectRoot) : "";
|
|
4383
|
+
let priorFindingsContent = "";
|
|
4384
|
+
try {
|
|
4385
|
+
const findings = getProjectFindings(deps.db);
|
|
4386
|
+
if (findings.length > 0) {
|
|
4387
|
+
priorFindingsContent = "Previous pipeline runs encountered these issues — avoid repeating them:\n\n" + findings;
|
|
4388
|
+
logger$9.debug({
|
|
4389
|
+
storyKey,
|
|
4390
|
+
findingsLen: findings.length
|
|
4391
|
+
}, "Injecting prior findings into dev-story prompt");
|
|
4392
|
+
}
|
|
4393
|
+
} catch {}
|
|
4220
4394
|
const sections = [
|
|
4221
4395
|
{
|
|
4222
4396
|
name: "story_content",
|
|
@@ -4247,6 +4421,11 @@ async function runDevStory(deps, params) {
|
|
|
4247
4421
|
name: "test_patterns",
|
|
4248
4422
|
content: testPatternsContent,
|
|
4249
4423
|
priority: "optional"
|
|
4424
|
+
},
|
|
4425
|
+
{
|
|
4426
|
+
name: "prior_findings",
|
|
4427
|
+
content: priorFindingsContent,
|
|
4428
|
+
priority: "optional"
|
|
4250
4429
|
}
|
|
4251
4430
|
];
|
|
4252
4431
|
const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING$1);
|
|
@@ -4599,6 +4778,17 @@ async function runCodeReview(deps, params) {
|
|
|
4599
4778
|
"",
|
|
4600
4779
|
...previousIssues.map((iss, i) => ` ${i + 1}. [${iss.severity ?? "unknown"}] ${iss.description ?? "no description"}${iss.file ? ` (${iss.file}${iss.line ? `:${iss.line}` : ""})` : ""}`)
|
|
4601
4780
|
].join("\n");
|
|
4781
|
+
let priorFindingsContent = "";
|
|
4782
|
+
try {
|
|
4783
|
+
const findings = getProjectFindings(deps.db);
|
|
4784
|
+
if (findings.length > 0) {
|
|
4785
|
+
priorFindingsContent = "Previous reviews found these recurring patterns — pay special attention:\n\n" + findings;
|
|
4786
|
+
logger$8.debug({
|
|
4787
|
+
storyKey,
|
|
4788
|
+
findingsLen: findings.length
|
|
4789
|
+
}, "Injecting prior findings into code-review prompt");
|
|
4790
|
+
}
|
|
4791
|
+
} catch {}
|
|
4602
4792
|
const sections = [
|
|
4603
4793
|
{
|
|
4604
4794
|
name: "story_content",
|
|
@@ -4619,6 +4809,11 @@ async function runCodeReview(deps, params) {
|
|
|
4619
4809
|
name: "arch_constraints",
|
|
4620
4810
|
content: archConstraintsContent,
|
|
4621
4811
|
priority: "optional"
|
|
4812
|
+
},
|
|
4813
|
+
{
|
|
4814
|
+
name: "prior_findings",
|
|
4815
|
+
content: priorFindingsContent,
|
|
4816
|
+
priority: "optional"
|
|
4622
4817
|
}
|
|
4623
4818
|
];
|
|
4624
4819
|
const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
|
|
@@ -5397,7 +5592,7 @@ function createPauseGate() {
|
|
|
5397
5592
|
*/
|
|
5398
5593
|
function createImplementationOrchestrator(deps) {
|
|
5399
5594
|
const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot } = deps;
|
|
5400
|
-
const logger$
|
|
5595
|
+
const logger$18 = createLogger("implementation-orchestrator");
|
|
5401
5596
|
let _state = "IDLE";
|
|
5402
5597
|
let _startedAt;
|
|
5403
5598
|
let _completedAt;
|
|
@@ -5434,7 +5629,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5434
5629
|
const nowMs = Date.now();
|
|
5435
5630
|
for (const [phase, startMs] of starts) {
|
|
5436
5631
|
const endMs = ends?.get(phase);
|
|
5437
|
-
if (endMs === void 0) logger$
|
|
5632
|
+
if (endMs === void 0) logger$18.warn({
|
|
5438
5633
|
storyKey,
|
|
5439
5634
|
phase
|
|
5440
5635
|
}, "Phase has no end time — story may have errored mid-phase. Duration capped to now() and may be inflated.");
|
|
@@ -5481,18 +5676,87 @@ function createImplementationOrchestrator(deps) {
|
|
|
5481
5676
|
rationale: `Story ${storyKey} completed with result=${result} in ${wallClockSeconds}s. Tokens: ${tokenAgg.input}+${tokenAgg.output}. Review cycles: ${reviewCycles}.`
|
|
5482
5677
|
});
|
|
5483
5678
|
} catch (decisionErr) {
|
|
5484
|
-
logger$
|
|
5679
|
+
logger$18.warn({
|
|
5485
5680
|
err: decisionErr,
|
|
5486
5681
|
storyKey
|
|
5487
5682
|
}, "Failed to write story-metrics decision (best-effort)");
|
|
5488
5683
|
}
|
|
5489
5684
|
} catch (err) {
|
|
5490
|
-
logger$
|
|
5685
|
+
logger$18.warn({
|
|
5491
5686
|
err,
|
|
5492
5687
|
storyKey
|
|
5493
5688
|
}, "Failed to write story metrics (best-effort)");
|
|
5494
5689
|
}
|
|
5495
5690
|
}
|
|
5691
|
+
/**
|
|
5692
|
+
* Persist a story outcome finding to the decision store (Story 22-1, AC4).
|
|
5693
|
+
*
|
|
5694
|
+
* Records outcome, review cycles, and any recurring issue patterns for
|
|
5695
|
+
* future prompt injection via the learning loop.
|
|
5696
|
+
*/
|
|
5697
|
+
function writeStoryOutcomeBestEffort(storyKey, outcome, reviewCycles, issuePatterns) {
|
|
5698
|
+
if (config.pipelineRunId === void 0) return;
|
|
5699
|
+
try {
|
|
5700
|
+
createDecision(db, {
|
|
5701
|
+
pipeline_run_id: config.pipelineRunId,
|
|
5702
|
+
phase: "implementation",
|
|
5703
|
+
category: STORY_OUTCOME,
|
|
5704
|
+
key: `${storyKey}:${config.pipelineRunId}`,
|
|
5705
|
+
value: JSON.stringify({
|
|
5706
|
+
storyKey,
|
|
5707
|
+
outcome,
|
|
5708
|
+
reviewCycles,
|
|
5709
|
+
recurringPatterns: issuePatterns ?? []
|
|
5710
|
+
}),
|
|
5711
|
+
rationale: `Story ${storyKey} ${outcome} after ${reviewCycles} review cycle(s).`
|
|
5712
|
+
});
|
|
5713
|
+
} catch (err) {
|
|
5714
|
+
logger$18.warn({
|
|
5715
|
+
err,
|
|
5716
|
+
storyKey
|
|
5717
|
+
}, "Failed to write story-outcome decision (best-effort)");
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
/**
|
|
5721
|
+
* Emit an escalation event with structured diagnosis and persist the
|
|
5722
|
+
* diagnosis to the decision store (Story 22-3).
|
|
5723
|
+
*/
|
|
5724
|
+
function emitEscalation(payload) {
|
|
5725
|
+
const diagnosis = generateEscalationDiagnosis(payload.issues, payload.reviewCycles, payload.lastVerdict);
|
|
5726
|
+
eventBus.emit("orchestrator:story-escalated", {
|
|
5727
|
+
...payload,
|
|
5728
|
+
diagnosis
|
|
5729
|
+
});
|
|
5730
|
+
if (config.pipelineRunId !== void 0) try {
|
|
5731
|
+
createDecision(db, {
|
|
5732
|
+
pipeline_run_id: config.pipelineRunId,
|
|
5733
|
+
phase: "implementation",
|
|
5734
|
+
category: ESCALATION_DIAGNOSIS,
|
|
5735
|
+
key: `${payload.storyKey}:${config.pipelineRunId}`,
|
|
5736
|
+
value: JSON.stringify(diagnosis),
|
|
5737
|
+
rationale: `Escalation diagnosis for ${payload.storyKey}: ${diagnosis.recommendedAction} — ${diagnosis.rationale}`
|
|
5738
|
+
});
|
|
5739
|
+
} catch (err) {
|
|
5740
|
+
logger$18.warn({
|
|
5741
|
+
err,
|
|
5742
|
+
storyKey: payload.storyKey
|
|
5743
|
+
}, "Failed to persist escalation diagnosis (best-effort)");
|
|
5744
|
+
}
|
|
5745
|
+
const issuePatterns = extractIssuePatterns(payload.issues);
|
|
5746
|
+
writeStoryOutcomeBestEffort(payload.storyKey, "escalated", payload.reviewCycles, issuePatterns);
|
|
5747
|
+
}
|
|
5748
|
+
/**
|
|
5749
|
+
* Extract short pattern descriptions from an issue list for recurring pattern tracking.
|
|
5750
|
+
*/
|
|
5751
|
+
function extractIssuePatterns(issues) {
|
|
5752
|
+
const patterns = [];
|
|
5753
|
+
for (const issue of issues) if (typeof issue === "string") patterns.push(issue.slice(0, 100));
|
|
5754
|
+
else {
|
|
5755
|
+
const iss = issue;
|
|
5756
|
+
if (iss.description && (iss.severity === "blocker" || iss.severity === "major")) patterns.push(iss.description.slice(0, 100));
|
|
5757
|
+
}
|
|
5758
|
+
return patterns.slice(0, 10);
|
|
5759
|
+
}
|
|
5496
5760
|
function getStatus() {
|
|
5497
5761
|
const stories = {};
|
|
5498
5762
|
for (const [key, s] of _stories) stories[key] = { ...s };
|
|
@@ -5523,7 +5787,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5523
5787
|
token_usage_json: serialized
|
|
5524
5788
|
});
|
|
5525
5789
|
} catch (err) {
|
|
5526
|
-
logger$
|
|
5790
|
+
logger$18.warn("Failed to persist orchestrator state", { err });
|
|
5527
5791
|
}
|
|
5528
5792
|
}
|
|
5529
5793
|
function recordProgress() {
|
|
@@ -5553,7 +5817,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5553
5817
|
if (_stalledStories.has(key)) continue;
|
|
5554
5818
|
_stalledStories.add(key);
|
|
5555
5819
|
_storiesWithStall.add(key);
|
|
5556
|
-
logger$
|
|
5820
|
+
logger$18.warn({
|
|
5557
5821
|
storyKey: key,
|
|
5558
5822
|
phase: s.phase,
|
|
5559
5823
|
elapsedMs: elapsed
|
|
@@ -5590,7 +5854,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5590
5854
|
* exhausted retries the story is ESCALATED.
|
|
5591
5855
|
*/
|
|
5592
5856
|
async function processStory(storyKey) {
|
|
5593
|
-
logger$
|
|
5857
|
+
logger$18.info("Processing story", { storyKey });
|
|
5594
5858
|
await waitIfPaused();
|
|
5595
5859
|
if (_state !== "RUNNING") return;
|
|
5596
5860
|
startPhase(storyKey, "create-story");
|
|
@@ -5605,7 +5869,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5605
5869
|
const match = files.find((f) => f.startsWith(`${storyKey}-`) && f.endsWith(".md"));
|
|
5606
5870
|
if (match) {
|
|
5607
5871
|
storyFilePath = join$1(artifactsDir, match);
|
|
5608
|
-
logger$
|
|
5872
|
+
logger$18.info({
|
|
5609
5873
|
storyKey,
|
|
5610
5874
|
storyFilePath
|
|
5611
5875
|
}, "Found existing story file — skipping create-story");
|
|
@@ -5650,7 +5914,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5650
5914
|
completedAt: new Date().toISOString()
|
|
5651
5915
|
});
|
|
5652
5916
|
writeStoryMetricsBestEffort(storyKey, "failed", 0);
|
|
5653
|
-
|
|
5917
|
+
emitEscalation({
|
|
5654
5918
|
storyKey,
|
|
5655
5919
|
lastVerdict: "create-story-failed",
|
|
5656
5920
|
reviewCycles: 0,
|
|
@@ -5667,7 +5931,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5667
5931
|
completedAt: new Date().toISOString()
|
|
5668
5932
|
});
|
|
5669
5933
|
writeStoryMetricsBestEffort(storyKey, "failed", 0);
|
|
5670
|
-
|
|
5934
|
+
emitEscalation({
|
|
5671
5935
|
storyKey,
|
|
5672
5936
|
lastVerdict: "create-story-no-file",
|
|
5673
5937
|
reviewCycles: 0,
|
|
@@ -5686,7 +5950,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5686
5950
|
completedAt: new Date().toISOString()
|
|
5687
5951
|
});
|
|
5688
5952
|
writeStoryMetricsBestEffort(storyKey, "failed", 0);
|
|
5689
|
-
|
|
5953
|
+
emitEscalation({
|
|
5690
5954
|
storyKey,
|
|
5691
5955
|
lastVerdict: "create-story-exception",
|
|
5692
5956
|
reviewCycles: 0,
|
|
@@ -5707,7 +5971,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5707
5971
|
try {
|
|
5708
5972
|
storyContentForAnalysis = await readFile$1(storyFilePath ?? "", "utf-8");
|
|
5709
5973
|
} catch (err) {
|
|
5710
|
-
logger$
|
|
5974
|
+
logger$18.error({
|
|
5711
5975
|
storyKey,
|
|
5712
5976
|
storyFilePath,
|
|
5713
5977
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -5715,7 +5979,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5715
5979
|
}
|
|
5716
5980
|
const analysis = analyzeStoryComplexity(storyContentForAnalysis);
|
|
5717
5981
|
const batches = planTaskBatches(analysis);
|
|
5718
|
-
logger$
|
|
5982
|
+
logger$18.info({
|
|
5719
5983
|
storyKey,
|
|
5720
5984
|
estimatedScope: analysis.estimatedScope,
|
|
5721
5985
|
batchCount: batches.length,
|
|
@@ -5733,7 +5997,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5733
5997
|
if (_state !== "RUNNING") break;
|
|
5734
5998
|
const taskScope = batch.taskIds.map((id, i) => `T${id}: ${batch.taskTitles[i] ?? ""}`).join("\n");
|
|
5735
5999
|
const priorFiles = allFilesModified.size > 0 ? Array.from(allFilesModified) : void 0;
|
|
5736
|
-
logger$
|
|
6000
|
+
logger$18.info({
|
|
5737
6001
|
storyKey,
|
|
5738
6002
|
batchIndex: batch.batchIndex,
|
|
5739
6003
|
taskCount: batch.taskIds.length
|
|
@@ -5757,7 +6021,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5757
6021
|
});
|
|
5758
6022
|
} catch (batchErr) {
|
|
5759
6023
|
const errMsg = batchErr instanceof Error ? batchErr.message : String(batchErr);
|
|
5760
|
-
logger$
|
|
6024
|
+
logger$18.warn({
|
|
5761
6025
|
storyKey,
|
|
5762
6026
|
batchIndex: batch.batchIndex,
|
|
5763
6027
|
error: errMsg
|
|
@@ -5777,7 +6041,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5777
6041
|
filesModified: batchFilesModified,
|
|
5778
6042
|
result: batchResult.result === "success" ? "success" : "failed"
|
|
5779
6043
|
};
|
|
5780
|
-
logger$
|
|
6044
|
+
logger$18.info(batchMetrics, "Batch dev-story metrics");
|
|
5781
6045
|
for (const f of batchFilesModified) allFilesModified.add(f);
|
|
5782
6046
|
if (batchFilesModified.length > 0) batchFileGroups.push({
|
|
5783
6047
|
batchIndex: batch.batchIndex,
|
|
@@ -5799,13 +6063,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
5799
6063
|
})
|
|
5800
6064
|
});
|
|
5801
6065
|
} catch (tokenErr) {
|
|
5802
|
-
logger$
|
|
6066
|
+
logger$18.warn({
|
|
5803
6067
|
storyKey,
|
|
5804
6068
|
batchIndex: batch.batchIndex,
|
|
5805
6069
|
err: tokenErr
|
|
5806
6070
|
}, "Failed to record batch token usage");
|
|
5807
6071
|
}
|
|
5808
|
-
if (batchResult.result === "failed") logger$
|
|
6072
|
+
if (batchResult.result === "failed") logger$18.warn({
|
|
5809
6073
|
storyKey,
|
|
5810
6074
|
batchIndex: batch.batchIndex,
|
|
5811
6075
|
error: batchResult.error
|
|
@@ -5838,7 +6102,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5838
6102
|
result: devResult
|
|
5839
6103
|
});
|
|
5840
6104
|
persistState();
|
|
5841
|
-
if (devResult.result === "failed") logger$
|
|
6105
|
+
if (devResult.result === "failed") logger$18.warn("Dev-story reported failure, proceeding to code review", {
|
|
5842
6106
|
storyKey,
|
|
5843
6107
|
error: devResult.error,
|
|
5844
6108
|
filesModified: devFilesModified.length
|
|
@@ -5853,7 +6117,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5853
6117
|
completedAt: new Date().toISOString()
|
|
5854
6118
|
});
|
|
5855
6119
|
writeStoryMetricsBestEffort(storyKey, "failed", 0);
|
|
5856
|
-
|
|
6120
|
+
emitEscalation({
|
|
5857
6121
|
storyKey,
|
|
5858
6122
|
lastVerdict: "dev-story-exception",
|
|
5859
6123
|
reviewCycles: 0,
|
|
@@ -5895,7 +6159,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5895
6159
|
"NEEDS_MAJOR_REWORK": 2
|
|
5896
6160
|
};
|
|
5897
6161
|
for (const group of batchFileGroups) {
|
|
5898
|
-
logger$
|
|
6162
|
+
logger$18.info({
|
|
5899
6163
|
storyKey,
|
|
5900
6164
|
batchIndex: group.batchIndex,
|
|
5901
6165
|
fileCount: group.files.length
|
|
@@ -5932,7 +6196,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5932
6196
|
rawOutput: lastRawOutput,
|
|
5933
6197
|
tokenUsage: aggregateTokens
|
|
5934
6198
|
};
|
|
5935
|
-
logger$
|
|
6199
|
+
logger$18.info({
|
|
5936
6200
|
storyKey,
|
|
5937
6201
|
batchCount: batchFileGroups.length,
|
|
5938
6202
|
verdict: worstVerdict,
|
|
@@ -5958,7 +6222,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5958
6222
|
const isPhantomReview = reviewResult.verdict !== "SHIP_IT" && (reviewResult.issue_list === void 0 || reviewResult.issue_list.length === 0) && reviewResult.error !== void 0;
|
|
5959
6223
|
if (isPhantomReview && !timeoutRetried) {
|
|
5960
6224
|
timeoutRetried = true;
|
|
5961
|
-
logger$
|
|
6225
|
+
logger$18.warn({
|
|
5962
6226
|
storyKey,
|
|
5963
6227
|
reviewCycles,
|
|
5964
6228
|
error: reviewResult.error
|
|
@@ -5968,7 +6232,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
5968
6232
|
verdict = reviewResult.verdict;
|
|
5969
6233
|
issueList = reviewResult.issue_list ?? [];
|
|
5970
6234
|
if (verdict === "NEEDS_MAJOR_REWORK" && reviewCycles > 0 && previousIssueList.length > 0 && issueList.length < previousIssueList.length) {
|
|
5971
|
-
logger$
|
|
6235
|
+
logger$18.info({
|
|
5972
6236
|
storyKey,
|
|
5973
6237
|
originalVerdict: verdict,
|
|
5974
6238
|
issuesBefore: previousIssueList.length,
|
|
@@ -6004,7 +6268,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6004
6268
|
if (_decomposition !== void 0) parts.push(`decomposed: ${_decomposition.batchCount} batches`);
|
|
6005
6269
|
parts.push(`${fileCount} files`);
|
|
6006
6270
|
parts.push(`${totalTokensK} tokens`);
|
|
6007
|
-
logger$
|
|
6271
|
+
logger$18.info({
|
|
6008
6272
|
storyKey,
|
|
6009
6273
|
verdict,
|
|
6010
6274
|
agentVerdict: reviewResult.agentVerdict
|
|
@@ -6019,7 +6283,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6019
6283
|
completedAt: new Date().toISOString()
|
|
6020
6284
|
});
|
|
6021
6285
|
writeStoryMetricsBestEffort(storyKey, "failed", reviewCycles);
|
|
6022
|
-
|
|
6286
|
+
emitEscalation({
|
|
6023
6287
|
storyKey,
|
|
6024
6288
|
lastVerdict: "code-review-exception",
|
|
6025
6289
|
reviewCycles,
|
|
@@ -6035,6 +6299,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6035
6299
|
completedAt: new Date().toISOString()
|
|
6036
6300
|
});
|
|
6037
6301
|
writeStoryMetricsBestEffort(storyKey, "success", reviewCycles + 1);
|
|
6302
|
+
writeStoryOutcomeBestEffort(storyKey, "complete", reviewCycles + 1);
|
|
6038
6303
|
eventBus.emit("orchestrator:story-complete", {
|
|
6039
6304
|
storyKey,
|
|
6040
6305
|
reviewCycles
|
|
@@ -6053,7 +6318,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6053
6318
|
completedAt: new Date().toISOString()
|
|
6054
6319
|
});
|
|
6055
6320
|
writeStoryMetricsBestEffort(storyKey, "escalated", finalReviewCycles);
|
|
6056
|
-
|
|
6321
|
+
emitEscalation({
|
|
6057
6322
|
storyKey,
|
|
6058
6323
|
lastVerdict: verdict,
|
|
6059
6324
|
reviewCycles: finalReviewCycles,
|
|
@@ -6062,7 +6327,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6062
6327
|
persistState();
|
|
6063
6328
|
return;
|
|
6064
6329
|
}
|
|
6065
|
-
logger$
|
|
6330
|
+
logger$18.info({
|
|
6066
6331
|
storyKey,
|
|
6067
6332
|
reviewCycles: finalReviewCycles,
|
|
6068
6333
|
issueCount: issueList.length
|
|
@@ -6112,7 +6377,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6112
6377
|
fixPrompt = assembled.prompt;
|
|
6113
6378
|
} catch {
|
|
6114
6379
|
fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, minor fixes needed`;
|
|
6115
|
-
logger$
|
|
6380
|
+
logger$18.warn("Failed to assemble auto-approve fix prompt, using fallback", { storyKey });
|
|
6116
6381
|
}
|
|
6117
6382
|
const handle = dispatcher.dispatch({
|
|
6118
6383
|
prompt: fixPrompt,
|
|
@@ -6129,9 +6394,9 @@ function createImplementationOrchestrator(deps) {
|
|
|
6129
6394
|
output: fixResult.tokenEstimate.output
|
|
6130
6395
|
} : void 0 }
|
|
6131
6396
|
});
|
|
6132
|
-
if (fixResult.status === "timeout") logger$
|
|
6397
|
+
if (fixResult.status === "timeout") logger$18.warn("Auto-approve fix timed out — approving anyway (issues were minor)", { storyKey });
|
|
6133
6398
|
} catch (err) {
|
|
6134
|
-
logger$
|
|
6399
|
+
logger$18.warn("Auto-approve fix dispatch failed — approving anyway (issues were minor)", {
|
|
6135
6400
|
storyKey,
|
|
6136
6401
|
err
|
|
6137
6402
|
});
|
|
@@ -6143,6 +6408,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6143
6408
|
completedAt: new Date().toISOString()
|
|
6144
6409
|
});
|
|
6145
6410
|
writeStoryMetricsBestEffort(storyKey, "success", finalReviewCycles);
|
|
6411
|
+
writeStoryOutcomeBestEffort(storyKey, "complete", finalReviewCycles);
|
|
6146
6412
|
eventBus.emit("orchestrator:story-complete", {
|
|
6147
6413
|
storyKey,
|
|
6148
6414
|
reviewCycles: finalReviewCycles
|
|
@@ -6203,7 +6469,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6203
6469
|
fixPrompt = assembled.prompt;
|
|
6204
6470
|
} catch {
|
|
6205
6471
|
fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, taskType=${taskType}`;
|
|
6206
|
-
logger$
|
|
6472
|
+
logger$18.warn("Failed to assemble fix prompt, using fallback", {
|
|
6207
6473
|
storyKey,
|
|
6208
6474
|
taskType
|
|
6209
6475
|
});
|
|
@@ -6226,7 +6492,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6226
6492
|
} : void 0 }
|
|
6227
6493
|
});
|
|
6228
6494
|
if (fixResult.status === "timeout") {
|
|
6229
|
-
logger$
|
|
6495
|
+
logger$18.warn("Fix dispatch timed out — escalating story", {
|
|
6230
6496
|
storyKey,
|
|
6231
6497
|
taskType
|
|
6232
6498
|
});
|
|
@@ -6237,7 +6503,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6237
6503
|
completedAt: new Date().toISOString()
|
|
6238
6504
|
});
|
|
6239
6505
|
writeStoryMetricsBestEffort(storyKey, "escalated", reviewCycles + 1);
|
|
6240
|
-
|
|
6506
|
+
emitEscalation({
|
|
6241
6507
|
storyKey,
|
|
6242
6508
|
lastVerdict: verdict,
|
|
6243
6509
|
reviewCycles: reviewCycles + 1,
|
|
@@ -6246,13 +6512,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
6246
6512
|
persistState();
|
|
6247
6513
|
return;
|
|
6248
6514
|
}
|
|
6249
|
-
if (fixResult.status === "failed") logger$
|
|
6515
|
+
if (fixResult.status === "failed") logger$18.warn("Fix dispatch failed", {
|
|
6250
6516
|
storyKey,
|
|
6251
6517
|
taskType,
|
|
6252
6518
|
exitCode: fixResult.exitCode
|
|
6253
6519
|
});
|
|
6254
6520
|
} catch (err) {
|
|
6255
|
-
logger$
|
|
6521
|
+
logger$18.warn("Fix dispatch failed, continuing to next review", {
|
|
6256
6522
|
storyKey,
|
|
6257
6523
|
taskType,
|
|
6258
6524
|
err
|
|
@@ -6306,11 +6572,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
6306
6572
|
}
|
|
6307
6573
|
async function run(storyKeys) {
|
|
6308
6574
|
if (_state === "RUNNING" || _state === "PAUSED") {
|
|
6309
|
-
logger$
|
|
6575
|
+
logger$18.warn("run() called while orchestrator is already running or paused — ignoring", { state: _state });
|
|
6310
6576
|
return getStatus();
|
|
6311
6577
|
}
|
|
6312
6578
|
if (_state === "COMPLETE") {
|
|
6313
|
-
logger$
|
|
6579
|
+
logger$18.warn("run() called on a COMPLETE orchestrator — ignoring", { state: _state });
|
|
6314
6580
|
return getStatus();
|
|
6315
6581
|
}
|
|
6316
6582
|
_state = "RUNNING";
|
|
@@ -6328,13 +6594,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
6328
6594
|
if (config.enableHeartbeat) startHeartbeat();
|
|
6329
6595
|
if (projectRoot !== void 0) {
|
|
6330
6596
|
const seedResult = seedMethodologyContext(db, projectRoot);
|
|
6331
|
-
if (seedResult.decisionsCreated > 0) logger$
|
|
6597
|
+
if (seedResult.decisionsCreated > 0) logger$18.info({
|
|
6332
6598
|
decisionsCreated: seedResult.decisionsCreated,
|
|
6333
6599
|
skippedCategories: seedResult.skippedCategories
|
|
6334
6600
|
}, "Methodology context seeded from planning artifacts");
|
|
6335
6601
|
}
|
|
6336
6602
|
const groups = detectConflictGroups(storyKeys);
|
|
6337
|
-
logger$
|
|
6603
|
+
logger$18.info("Orchestrator starting", {
|
|
6338
6604
|
storyCount: storyKeys.length,
|
|
6339
6605
|
groupCount: groups.length,
|
|
6340
6606
|
maxConcurrency: config.maxConcurrency
|
|
@@ -6346,7 +6612,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6346
6612
|
_state = "FAILED";
|
|
6347
6613
|
_completedAt = new Date().toISOString();
|
|
6348
6614
|
persistState();
|
|
6349
|
-
logger$
|
|
6615
|
+
logger$18.error("Orchestrator failed with unhandled error", { err });
|
|
6350
6616
|
return getStatus();
|
|
6351
6617
|
}
|
|
6352
6618
|
stopHeartbeat();
|
|
@@ -6373,7 +6639,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6373
6639
|
_pauseGate = createPauseGate();
|
|
6374
6640
|
_state = "PAUSED";
|
|
6375
6641
|
eventBus.emit("orchestrator:paused", {});
|
|
6376
|
-
logger$
|
|
6642
|
+
logger$18.info("Orchestrator paused");
|
|
6377
6643
|
}
|
|
6378
6644
|
function resume() {
|
|
6379
6645
|
if (_state !== "PAUSED") return;
|
|
@@ -6384,7 +6650,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6384
6650
|
}
|
|
6385
6651
|
_state = "RUNNING";
|
|
6386
6652
|
eventBus.emit("orchestrator:resumed", {});
|
|
6387
|
-
logger$
|
|
6653
|
+
logger$18.info("Orchestrator resumed");
|
|
6388
6654
|
}
|
|
6389
6655
|
return {
|
|
6390
6656
|
run,
|
|
@@ -11022,7 +11288,8 @@ async function runRunAction(options) {
|
|
|
11022
11288
|
key: payload.storyKey,
|
|
11023
11289
|
reason: payload.lastVerdict ?? "escalated",
|
|
11024
11290
|
cycles: payload.reviewCycles ?? 0,
|
|
11025
|
-
issues
|
|
11291
|
+
issues,
|
|
11292
|
+
...payload.diagnosis !== void 0 ? { diagnosis: payload.diagnosis } : {}
|
|
11026
11293
|
});
|
|
11027
11294
|
});
|
|
11028
11295
|
eventBus.on("orchestrator:story-warn", (payload) => {
|
|
@@ -11508,4 +11775,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
11508
11775
|
|
|
11509
11776
|
//#endregion
|
|
11510
11777
|
export { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
|
|
11511
|
-
//# sourceMappingURL=run-
|
|
11778
|
+
//# sourceMappingURL=run-CcWb6Kb-.js.map
|
package/package.json
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
### Architecture Constraints
|
|
15
15
|
{{arch_constraints}}
|
|
16
16
|
|
|
17
|
+
### Prior Run Findings
|
|
18
|
+
{{prior_findings}}
|
|
19
|
+
|
|
17
20
|
---
|
|
18
21
|
|
|
19
22
|
## Mission
|
|
@@ -32,8 +35,9 @@ Adversarial code review. Find what's wrong. Validate story claims against actual
|
|
|
32
35
|
- Whether each AC is actually implemented
|
|
33
36
|
- Whether each `[x]` task is actually done
|
|
34
37
|
|
|
35
|
-
3. **Execute adversarial review** across
|
|
38
|
+
3. **Execute adversarial review** across 5 dimensions:
|
|
36
39
|
- **AC Validation** — Is each acceptance criterion implemented?
|
|
40
|
+
- **AC-to-Test Traceability** — For each AC, identify the specific test file and test function that validates it. If an AC has no corresponding test evidence, flag it as a major issue: "AC{N} has no test evidence". A test "covers" an AC if it directly exercises the behavior described in the criterion — tangential tests do not count.
|
|
37
41
|
- **Task Audit** — Tasks marked `[x]` that aren't done are BLOCKER issues
|
|
38
42
|
- **Code Quality** — Security, error handling, edge cases, maintainability
|
|
39
43
|
- **Test Quality** — Real assertions, not placeholders or skipped tests
|
package/dist/run-CUGB4FQx.js
DELETED