substrate-ai 0.20.29 → 0.20.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +4 -4
- package/dist/{health-Dx9hm9x1.js → health-DJ4z2uWN.js} +253 -29
- package/dist/{health-C6x1jDlX.js → health-DLqMd9uN.js} +1 -1
- package/dist/{run-BQQ0QILZ.js → run-D-OcJNtZ.js} +2 -2
- package/dist/{run-D4WYiyK8.js → run-DGyWCmcu.js} +1061 -419
- package/package.json +1 -1
- package/packs/bmad/prompts/dev-story.md +2 -0
- package/packs/bmad/prompts/probe-author.md +154 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-
|
|
1
|
+
import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, FindingsInjector, RunManifest, RuntimeProbeListSchema, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, WorkGraphRepository, __commonJS, __require, __toESM, applyConfigToGraph, buildPipelineStatusOutput, createDatabaseAdapter, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectCycles, detectsEventDrivenAC, extractTargetFilesFromStoryContent, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, renderFindings, resolveGraphPath, resolveMainRepoRoot, validateStoryKey } from "./health-DJ4z2uWN.js";
|
|
2
2
|
import { createLogger } from "./logger-KeHncl-f.js";
|
|
3
3
|
import { TypedEventBusImpl, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning, sleep } from "./helpers-CElYrONe.js";
|
|
4
4
|
import { ADVISORY_NOTES, Categorizer, ConsumerAnalyzer, DEFAULT_GLOBAL_SETTINGS, DispatcherImpl, DoltClient, ESCALATION_DIAGNOSIS, EXPERIMENT_RESULT, EfficiencyScorer, IngestionServer, LogTurnAnalyzer, OPERATIONAL_FINDING, Recommender, RoutingRecommender, RoutingResolver, RoutingTelemetry, RoutingTokenAccumulator, RoutingTuner, STORY_METRICS, STORY_OUTCOME, SubstrateConfigSchema, TEST_EXPANSION_FINDING, TEST_PLAN, TelemetryNormalizer, TelemetryPipeline, TurnAnalyzer, addTokenUsage, aggregateTokenUsageForRun, aggregateTokenUsageForStory, callLLM, createConfigSystem, createDatabaseAdapter$1, createDecision, createPipelineRun, createRequirement, detectInterfaceChanges, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunMetrics, getRunningPipelineRuns, getStoryMetricsForRun, getTokenUsageSummary, initSchema, listRequirements, loadModelRoutingConfig, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision, writeRunMetrics, writeStoryMetrics } from "./dist-CqtWS9wF.js";
|
|
@@ -9,11 +9,12 @@ import yaml from "js-yaml";
|
|
|
9
9
|
import * as actualFS from "node:fs";
|
|
10
10
|
import { accessSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
|
|
11
11
|
import { exec, execFile, execFileSync, execSync, spawn } from "node:child_process";
|
|
12
|
+
import * as path$2 from "node:path";
|
|
12
13
|
import path, { basename as basename$1, dirname as dirname$1, extname as extname$1, isAbsolute, join as join$1, posix, resolve as resolve$1, win32 } from "node:path";
|
|
13
14
|
import { tmpdir } from "node:os";
|
|
14
15
|
import { createHash, randomUUID } from "node:crypto";
|
|
15
16
|
import { z } from "zod";
|
|
16
|
-
import { access as access$1, lstat, mkdir as mkdir$1, readFile as readFile$1, readdir as readdir$1, readlink, realpath, stat as stat$1, unlink, writeFile as writeFile$1 } from "node:fs/promises";
|
|
17
|
+
import { access as access$1, lstat, mkdir as mkdir$1, readFile as readFile$1, readdir as readdir$1, readlink, realpath, rename, stat as stat$1, unlink, writeFile as writeFile$1 } from "node:fs/promises";
|
|
17
18
|
import { existsSync as existsSync$1, lstatSync, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdir as readdir$2, readdirSync as readdirSync$1, readlinkSync, realpathSync as realpathSync$1, unlinkSync as unlinkSync$1, writeFileSync as writeFileSync$1 } from "fs";
|
|
18
19
|
import { promisify } from "node:util";
|
|
19
20
|
import { fileURLToPath } from "node:url";
|
|
@@ -2122,7 +2123,7 @@ async function runHelpAgent() {
|
|
|
2122
2123
|
|
|
2123
2124
|
//#endregion
|
|
2124
2125
|
//#region src/persistence/dolt-server.ts
|
|
2125
|
-
const logger$
|
|
2126
|
+
const logger$25 = createLogger("dolt-server");
|
|
2126
2127
|
/**
|
|
2127
2128
|
* Start a dolt sql-server for the given project, if Dolt is available and no
|
|
2128
2129
|
* server is already running (socket already exists).
|
|
@@ -2139,7 +2140,7 @@ async function startDoltServer(projectRoot) {
|
|
|
2139
2140
|
if (!existsSync(join$1(stateDir, ".dolt"))) return null;
|
|
2140
2141
|
try {
|
|
2141
2142
|
await access$1(socketPath);
|
|
2142
|
-
logger$
|
|
2143
|
+
logger$25.debug("Dolt socket already exists at %s — using existing server", socketPath);
|
|
2143
2144
|
return null;
|
|
2144
2145
|
} catch {}
|
|
2145
2146
|
try {
|
|
@@ -2148,10 +2149,10 @@ async function startDoltServer(projectRoot) {
|
|
|
2148
2149
|
stdio: "pipe"
|
|
2149
2150
|
});
|
|
2150
2151
|
} catch {
|
|
2151
|
-
logger$
|
|
2152
|
+
logger$25.debug("dolt binary not on PATH — cannot start server");
|
|
2152
2153
|
return null;
|
|
2153
2154
|
}
|
|
2154
|
-
logger$
|
|
2155
|
+
logger$25.debug("Starting dolt sql-server at %s", socketPath);
|
|
2155
2156
|
let proc$1;
|
|
2156
2157
|
try {
|
|
2157
2158
|
proc$1 = spawn("dolt", [
|
|
@@ -2172,26 +2173,26 @@ async function startDoltServer(projectRoot) {
|
|
|
2172
2173
|
detached: false
|
|
2173
2174
|
});
|
|
2174
2175
|
} catch (err) {
|
|
2175
|
-
logger$
|
|
2176
|
+
logger$25.debug("Failed to spawn dolt sql-server: %s", err instanceof Error ? err.message : String(err));
|
|
2176
2177
|
return null;
|
|
2177
2178
|
}
|
|
2178
2179
|
let failed = false;
|
|
2179
2180
|
proc$1.on("error", (err) => {
|
|
2180
|
-
logger$
|
|
2181
|
+
logger$25.debug("dolt sql-server error: %s", err.message);
|
|
2181
2182
|
failed = true;
|
|
2182
2183
|
});
|
|
2183
2184
|
proc$1.on("exit", (code) => {
|
|
2184
|
-
if (code !== null && code !== 0) logger$
|
|
2185
|
+
if (code !== null && code !== 0) logger$25.debug("dolt sql-server exited with code %d", code);
|
|
2185
2186
|
});
|
|
2186
2187
|
proc$1.stderr?.on("data", (chunk) => {
|
|
2187
2188
|
const line = chunk.toString().trim();
|
|
2188
|
-
if (line) logger$
|
|
2189
|
+
if (line) logger$25.debug("dolt-server: %s", line);
|
|
2189
2190
|
});
|
|
2190
2191
|
const deadline = Date.now() + 5e3;
|
|
2191
2192
|
while (Date.now() < deadline && !failed) try {
|
|
2192
2193
|
await access$1(socketPath);
|
|
2193
2194
|
const pid = proc$1.pid ?? 0;
|
|
2194
|
-
logger$
|
|
2195
|
+
logger$25.info("Auto-started dolt sql-server (pid=%d, socket=%s)", pid, socketPath);
|
|
2195
2196
|
let stopped = false;
|
|
2196
2197
|
return {
|
|
2197
2198
|
pid,
|
|
@@ -2199,14 +2200,14 @@ async function startDoltServer(projectRoot) {
|
|
|
2199
2200
|
stop: () => {
|
|
2200
2201
|
if (stopped) return;
|
|
2201
2202
|
stopped = true;
|
|
2202
|
-
logger$
|
|
2203
|
+
logger$25.debug("Stopping dolt sql-server (pid=%d)", pid);
|
|
2203
2204
|
proc$1.kill("SIGTERM");
|
|
2204
2205
|
}
|
|
2205
2206
|
};
|
|
2206
2207
|
} catch {
|
|
2207
2208
|
await new Promise((resolve$6) => setTimeout(resolve$6, 100));
|
|
2208
2209
|
}
|
|
2209
|
-
logger$
|
|
2210
|
+
logger$25.debug("dolt sql-server did not start within 5s — killing");
|
|
2210
2211
|
proc$1.kill("SIGTERM");
|
|
2211
2212
|
return null;
|
|
2212
2213
|
}
|
|
@@ -2276,7 +2277,7 @@ function truncateToTokens(text, maxTokens) {
|
|
|
2276
2277
|
|
|
2277
2278
|
//#endregion
|
|
2278
2279
|
//#region src/modules/context-compiler/context-compiler-impl.ts
|
|
2279
|
-
const logger$
|
|
2280
|
+
const logger$24 = createLogger("context-compiler");
|
|
2280
2281
|
/**
|
|
2281
2282
|
* Fraction of the original token budget that must remain (after required +
|
|
2282
2283
|
* important sections) before an optional section is included.
|
|
@@ -2337,7 +2338,7 @@ var ContextCompilerImpl = class {
|
|
|
2337
2338
|
}
|
|
2338
2339
|
_applyExclusionFilter(text, sectionName) {
|
|
2339
2340
|
for (const excludedPath of this._excludedPaths) if (text.includes(excludedPath)) {
|
|
2340
|
-
logger$
|
|
2341
|
+
logger$24.warn({
|
|
2341
2342
|
sectionName,
|
|
2342
2343
|
excludedPath
|
|
2343
2344
|
}, "ContextCompiler: section excluded — contains path from exclusion list");
|
|
@@ -2395,7 +2396,7 @@ var ContextCompilerImpl = class {
|
|
|
2395
2396
|
includedParts.push(truncated);
|
|
2396
2397
|
remainingBudget -= truncatedTokens;
|
|
2397
2398
|
anyTruncated = true;
|
|
2398
|
-
logger$
|
|
2399
|
+
logger$24.warn({
|
|
2399
2400
|
section: section.name,
|
|
2400
2401
|
originalTokens: tokens,
|
|
2401
2402
|
budgetTokens: truncatedTokens
|
|
@@ -2409,7 +2410,7 @@ var ContextCompilerImpl = class {
|
|
|
2409
2410
|
});
|
|
2410
2411
|
} else {
|
|
2411
2412
|
anyTruncated = true;
|
|
2412
|
-
logger$
|
|
2413
|
+
logger$24.warn({
|
|
2413
2414
|
section: section.name,
|
|
2414
2415
|
tokens
|
|
2415
2416
|
}, "Context compiler: omitted \"important\" section — no budget remaining");
|
|
@@ -2436,7 +2437,7 @@ var ContextCompilerImpl = class {
|
|
|
2436
2437
|
} else {
|
|
2437
2438
|
if (tokens > 0) {
|
|
2438
2439
|
anyTruncated = true;
|
|
2439
|
-
logger$
|
|
2440
|
+
logger$24.warn({
|
|
2440
2441
|
section: section.name,
|
|
2441
2442
|
tokens,
|
|
2442
2443
|
budgetFractionRemaining: budgetFractionRemaining.toFixed(2)
|
|
@@ -2530,8 +2531,8 @@ var GrammarLoader = class {
|
|
|
2530
2531
|
_extensionMap;
|
|
2531
2532
|
_cache = new Map();
|
|
2532
2533
|
_unavailable = false;
|
|
2533
|
-
constructor(logger$
|
|
2534
|
-
this._logger = logger$
|
|
2534
|
+
constructor(logger$26) {
|
|
2535
|
+
this._logger = logger$26;
|
|
2535
2536
|
this._extensionMap = new Map([
|
|
2536
2537
|
[".ts", "tree-sitter-typescript/typescript"],
|
|
2537
2538
|
[".tsx", "tree-sitter-typescript/tsx"],
|
|
@@ -2568,8 +2569,8 @@ var GrammarLoader = class {
|
|
|
2568
2569
|
throw err;
|
|
2569
2570
|
}
|
|
2570
2571
|
}
|
|
2571
|
-
_loadModule(path$
|
|
2572
|
-
return __require(path$
|
|
2572
|
+
_loadModule(path$4) {
|
|
2573
|
+
return __require(path$4);
|
|
2573
2574
|
}
|
|
2574
2575
|
};
|
|
2575
2576
|
|
|
@@ -2617,9 +2618,9 @@ const ERR_REPO_MAP_GIT_FAILED = "ERR_REPO_MAP_GIT_FAILED";
|
|
|
2617
2618
|
var SymbolParser = class {
|
|
2618
2619
|
_grammarLoader;
|
|
2619
2620
|
_logger;
|
|
2620
|
-
constructor(grammarLoader, logger$
|
|
2621
|
+
constructor(grammarLoader, logger$26) {
|
|
2621
2622
|
this._grammarLoader = grammarLoader;
|
|
2622
|
-
this._logger = logger$
|
|
2623
|
+
this._logger = logger$26;
|
|
2623
2624
|
}
|
|
2624
2625
|
async parseFile(filePath) {
|
|
2625
2626
|
const ext$2 = extname$1(filePath);
|
|
@@ -2764,9 +2765,9 @@ async function computeFileHash(filePath) {
|
|
|
2764
2765
|
var DoltSymbolRepository = class {
|
|
2765
2766
|
_client;
|
|
2766
2767
|
_logger;
|
|
2767
|
-
constructor(client, logger$
|
|
2768
|
+
constructor(client, logger$26) {
|
|
2768
2769
|
this._client = client;
|
|
2769
|
-
this._logger = logger$
|
|
2770
|
+
this._logger = logger$26;
|
|
2770
2771
|
}
|
|
2771
2772
|
/**
|
|
2772
2773
|
* Atomically replace all symbols for filePath.
|
|
@@ -2972,11 +2973,11 @@ var RepoMapStorage = class {
|
|
|
2972
2973
|
_metaRepo;
|
|
2973
2974
|
_gitClient;
|
|
2974
2975
|
_logger;
|
|
2975
|
-
constructor(symbolRepo, metaRepo, gitClient, logger$
|
|
2976
|
+
constructor(symbolRepo, metaRepo, gitClient, logger$26) {
|
|
2976
2977
|
this._symbolRepo = symbolRepo;
|
|
2977
2978
|
this._metaRepo = metaRepo;
|
|
2978
2979
|
this._gitClient = gitClient;
|
|
2979
|
-
this._logger = logger$
|
|
2980
|
+
this._logger = logger$26;
|
|
2980
2981
|
}
|
|
2981
2982
|
/**
|
|
2982
2983
|
* Returns true if the file's current content hash differs from the stored hash.
|
|
@@ -3093,8 +3094,8 @@ function runGit(args, cwd) {
|
|
|
3093
3094
|
*/
|
|
3094
3095
|
var GitClient = class {
|
|
3095
3096
|
_logger;
|
|
3096
|
-
constructor(logger$
|
|
3097
|
-
this._logger = logger$
|
|
3097
|
+
constructor(logger$26) {
|
|
3098
|
+
this._logger = logger$26;
|
|
3098
3099
|
}
|
|
3099
3100
|
/**
|
|
3100
3101
|
* Returns the current HEAD commit SHA.
|
|
@@ -3926,12 +3927,12 @@ const qmarksTestNoExtDot$1 = ([$0]) => {
|
|
|
3926
3927
|
};
|
|
3927
3928
|
/* c8 ignore start */
|
|
3928
3929
|
const defaultPlatform$3 = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
3929
|
-
const path$
|
|
3930
|
+
const path$3 = {
|
|
3930
3931
|
win32: { sep: "\\" },
|
|
3931
3932
|
posix: { sep: "/" }
|
|
3932
3933
|
};
|
|
3933
3934
|
/* c8 ignore stop */
|
|
3934
|
-
const sep$1 = defaultPlatform$3 === "win32" ? path$
|
|
3935
|
+
const sep$1 = defaultPlatform$3 === "win32" ? path$3.win32.sep : path$3.posix.sep;
|
|
3935
3936
|
minimatch$1.sep = sep$1;
|
|
3936
3937
|
const GLOBSTAR$1 = Symbol("globstar **");
|
|
3937
3938
|
minimatch$1.GLOBSTAR = GLOBSTAR$1;
|
|
@@ -4450,9 +4451,9 @@ var RepoMapQueryEngine = class {
|
|
|
4450
4451
|
repo;
|
|
4451
4452
|
logger;
|
|
4452
4453
|
telemetry;
|
|
4453
|
-
constructor(repo, logger$
|
|
4454
|
+
constructor(repo, logger$26, telemetry) {
|
|
4454
4455
|
this.repo = repo;
|
|
4455
|
-
this.logger = logger$
|
|
4456
|
+
this.logger = logger$26;
|
|
4456
4457
|
this.telemetry = telemetry;
|
|
4457
4458
|
}
|
|
4458
4459
|
async query(q) {
|
|
@@ -4672,9 +4673,9 @@ var RepoMapFormatter = class {
|
|
|
4672
4673
|
var RepoMapTelemetry = class {
|
|
4673
4674
|
_telemetry;
|
|
4674
4675
|
_logger;
|
|
4675
|
-
constructor(telemetry, logger$
|
|
4676
|
+
constructor(telemetry, logger$26) {
|
|
4676
4677
|
this._telemetry = telemetry;
|
|
4677
|
-
this._logger = logger$
|
|
4678
|
+
this._logger = logger$26;
|
|
4678
4679
|
}
|
|
4679
4680
|
/**
|
|
4680
4681
|
* Emit a `repo_map.query` span.
|
|
@@ -4699,9 +4700,9 @@ var RepoMapTelemetry = class {
|
|
|
4699
4700
|
var RepoMapModule = class {
|
|
4700
4701
|
_metaRepo;
|
|
4701
4702
|
_logger;
|
|
4702
|
-
constructor(metaRepo, logger$
|
|
4703
|
+
constructor(metaRepo, logger$26) {
|
|
4703
4704
|
this._metaRepo = metaRepo;
|
|
4704
|
-
this._logger = logger$
|
|
4705
|
+
this._logger = logger$26;
|
|
4705
4706
|
}
|
|
4706
4707
|
/**
|
|
4707
4708
|
* Check whether the stored repo-map is stale relative to the current HEAD commit.
|
|
@@ -4745,9 +4746,9 @@ var RepoMapModule = class {
|
|
|
4745
4746
|
var RepoMapInjector = class {
|
|
4746
4747
|
_queryEngine;
|
|
4747
4748
|
_logger;
|
|
4748
|
-
constructor(queryEngine, logger$
|
|
4749
|
+
constructor(queryEngine, logger$26) {
|
|
4749
4750
|
this._queryEngine = queryEngine;
|
|
4750
|
-
this._logger = logger$
|
|
4751
|
+
this._logger = logger$26;
|
|
4751
4752
|
}
|
|
4752
4753
|
/**
|
|
4753
4754
|
* Build repo-map context by extracting file references from the story content,
|
|
@@ -4823,12 +4824,13 @@ const DEFAULT_TIMEOUTS = {
|
|
|
4823
4824
|
"arch-decisions": 24e4,
|
|
4824
4825
|
"arch-patterns": 24e4,
|
|
4825
4826
|
"story-epics": 24e4,
|
|
4826
|
-
"story-stories": 6e5
|
|
4827
|
+
"story-stories": 6e5,
|
|
4828
|
+
"probe-author": 3e5
|
|
4827
4829
|
};
|
|
4828
4830
|
|
|
4829
4831
|
//#endregion
|
|
4830
4832
|
//#region src/modules/agent-dispatch/dispatcher-impl.ts
|
|
4831
|
-
const logger$
|
|
4833
|
+
const logger$23 = createLogger("agent-dispatch");
|
|
4832
4834
|
/**
|
|
4833
4835
|
* Create a new Dispatcher instance.
|
|
4834
4836
|
*
|
|
@@ -4972,7 +4974,7 @@ function runBuildVerification(options) {
|
|
|
4972
4974
|
let cmd;
|
|
4973
4975
|
if (verifyCommand === void 0) {
|
|
4974
4976
|
const detection = detectPackageManager(projectRoot);
|
|
4975
|
-
logger$
|
|
4977
|
+
logger$23.info({
|
|
4976
4978
|
packageManager: detection.packageManager,
|
|
4977
4979
|
lockfile: detection.lockfile,
|
|
4978
4980
|
resolvedCommand: detection.command
|
|
@@ -4984,7 +4986,7 @@ function runBuildVerification(options) {
|
|
|
4984
4986
|
const filters = deriveTurboFilters(changedFiles, projectRoot);
|
|
4985
4987
|
if (filters.length > 0) {
|
|
4986
4988
|
cmd = `${cmd} ${filters.join(" ")}`;
|
|
4987
|
-
logger$
|
|
4989
|
+
logger$23.info({
|
|
4988
4990
|
filters,
|
|
4989
4991
|
originalCmd: options.verifyCommand ?? "(auto-detected)"
|
|
4990
4992
|
}, "Build verification: scoped turbo build to affected packages");
|
|
@@ -5027,7 +5029,7 @@ function runBuildVerification(options) {
|
|
|
5027
5029
|
};
|
|
5028
5030
|
const missingScriptPattern = /Missing script[:\s]|No script found|Command "build" not found/i;
|
|
5029
5031
|
if (missingScriptPattern.test(combinedOutput)) {
|
|
5030
|
-
logger$
|
|
5032
|
+
logger$23.warn("Build script not found — skipping pre-flight (greenfield repo)");
|
|
5031
5033
|
return {
|
|
5032
5034
|
status: "skipped",
|
|
5033
5035
|
exitCode,
|
|
@@ -5037,7 +5039,7 @@ function runBuildVerification(options) {
|
|
|
5037
5039
|
}
|
|
5038
5040
|
const pep668Pattern = /externally-managed-environment|This environment is externally managed/i;
|
|
5039
5041
|
if (pep668Pattern.test(combinedOutput)) {
|
|
5040
|
-
logger$
|
|
5042
|
+
logger$23.warn("PEP 668: pip blocked by externally-managed-environment — skipping pre-flight. Create a .venv to resolve.");
|
|
5041
5043
|
return {
|
|
5042
5044
|
status: "skipped",
|
|
5043
5045
|
exitCode,
|
|
@@ -5221,7 +5223,7 @@ function pickRecommendation(distribution, profile, totalIssues, reviewCycles, la
|
|
|
5221
5223
|
|
|
5222
5224
|
//#endregion
|
|
5223
5225
|
//#region src/modules/implementation-orchestrator/project-findings.ts
|
|
5224
|
-
const logger$
|
|
5226
|
+
const logger$22 = createLogger("project-findings");
|
|
5225
5227
|
/** Maximum character length for the findings summary */
|
|
5226
5228
|
const MAX_CHARS = 2e3;
|
|
5227
5229
|
/**
|
|
@@ -5294,7 +5296,7 @@ async function getProjectFindings(db) {
|
|
|
5294
5296
|
if (summary.length > MAX_CHARS) summary = summary.slice(0, MAX_CHARS - 3) + "...";
|
|
5295
5297
|
return summary;
|
|
5296
5298
|
} catch (err) {
|
|
5297
|
-
logger$
|
|
5299
|
+
logger$22.warn({ err }, "Failed to query project findings (graceful fallback)");
|
|
5298
5300
|
return "";
|
|
5299
5301
|
}
|
|
5300
5302
|
}
|
|
@@ -5317,7 +5319,7 @@ function extractRecurringPatterns(outcomes) {
|
|
|
5317
5319
|
|
|
5318
5320
|
//#endregion
|
|
5319
5321
|
//#region src/modules/compiled-workflows/prompt-assembler.ts
|
|
5320
|
-
const logger$
|
|
5322
|
+
const logger$21 = createLogger("compiled-workflows:prompt-assembler");
|
|
5321
5323
|
/**
|
|
5322
5324
|
* Assemble a final prompt from a template and sections map.
|
|
5323
5325
|
*
|
|
@@ -5342,7 +5344,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
5342
5344
|
tokenCount,
|
|
5343
5345
|
truncated: false
|
|
5344
5346
|
};
|
|
5345
|
-
logger$
|
|
5347
|
+
logger$21.warn({
|
|
5346
5348
|
tokenCount,
|
|
5347
5349
|
ceiling: tokenCeiling
|
|
5348
5350
|
}, "Prompt exceeds token ceiling — truncating optional sections");
|
|
@@ -5358,10 +5360,10 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
5358
5360
|
const targetSectionTokens = Math.max(0, currentSectionTokens - overBy);
|
|
5359
5361
|
if (targetSectionTokens === 0) {
|
|
5360
5362
|
contentMap[section.name] = "";
|
|
5361
|
-
logger$
|
|
5363
|
+
logger$21.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
|
|
5362
5364
|
} else {
|
|
5363
5365
|
contentMap[section.name] = truncateToTokens(section.content, targetSectionTokens);
|
|
5364
|
-
logger$
|
|
5366
|
+
logger$21.warn({
|
|
5365
5367
|
sectionName: section.name,
|
|
5366
5368
|
targetSectionTokens
|
|
5367
5369
|
}, "Section truncated to fit token budget");
|
|
@@ -5372,7 +5374,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
5372
5374
|
}
|
|
5373
5375
|
if (tokenCount <= tokenCeiling) break;
|
|
5374
5376
|
}
|
|
5375
|
-
if (tokenCount > tokenCeiling) logger$
|
|
5377
|
+
if (tokenCount > tokenCeiling) logger$21.warn({
|
|
5376
5378
|
tokenCount,
|
|
5377
5379
|
ceiling: tokenCeiling
|
|
5378
5380
|
}, "Required sections alone exceed token ceiling — returning over-budget prompt");
|
|
@@ -5633,6 +5635,43 @@ const TestExpansionResultSchema = z.object({
|
|
|
5633
5635
|
suggested_tests: z.array(SuggestedTestSchema).default([]),
|
|
5634
5636
|
notes: z.string().optional()
|
|
5635
5637
|
});
|
|
5638
|
+
/**
|
|
5639
|
+
* Inline mirror of RuntimeProbeSchema from @substrate-ai/sdlc.
|
|
5640
|
+
*
|
|
5641
|
+
* Inlined here instead of imported to avoid triggering Vitest's mock validation
|
|
5642
|
+
* error in orchestrator tests that mock @substrate-ai/sdlc without including
|
|
5643
|
+
* RuntimeProbeListSchema. The canonical definition lives in
|
|
5644
|
+
* packages/sdlc/src/verification/probes/types.ts — keep in sync when that
|
|
5645
|
+
* schema changes.
|
|
5646
|
+
*/
|
|
5647
|
+
const InlineRuntimeProbeSchema = z.object({
|
|
5648
|
+
name: z.string().min(1, "probe name is required"),
|
|
5649
|
+
sandbox: z.enum(["host", "twin"]),
|
|
5650
|
+
command: z.string().min(1, "probe command is required"),
|
|
5651
|
+
timeout_ms: z.number().int().positive().optional(),
|
|
5652
|
+
description: z.string().optional(),
|
|
5653
|
+
expect_stdout_no_regex: z.array(z.string().min(1)).optional(),
|
|
5654
|
+
expect_stdout_regex: z.array(z.string().min(1)).optional()
|
|
5655
|
+
});
|
|
5656
|
+
const InlineRuntimeProbeListSchema = z.array(InlineRuntimeProbeSchema);
|
|
5657
|
+
/**
|
|
5658
|
+
* Schema for the YAML output contract of the probe-author sub-agent.
|
|
5659
|
+
*
|
|
5660
|
+
* The agent emits a yaml block containing result and probes list.
|
|
5661
|
+
* The probes field is validated against the RuntimeProbe shape from @substrate-ai/sdlc
|
|
5662
|
+
* (inlined to avoid module mock conflicts in orchestrator unit tests).
|
|
5663
|
+
*
|
|
5664
|
+
* Example:
|
|
5665
|
+
* result: success
|
|
5666
|
+
* probes:
|
|
5667
|
+
* - name: my-probe
|
|
5668
|
+
* sandbox: host
|
|
5669
|
+
* command: echo "hello"
|
|
5670
|
+
*/
|
|
5671
|
+
const ProbeAuthorResultSchema = z.object({
|
|
5672
|
+
result: z.preprocess((val) => val === "failure" ? "failed" : val, z.enum(["success", "failed"])),
|
|
5673
|
+
probes: InlineRuntimeProbeListSchema
|
|
5674
|
+
});
|
|
5636
5675
|
|
|
5637
5676
|
//#endregion
|
|
5638
5677
|
//#region src/modules/compiled-workflows/token-ceiling.ts
|
|
@@ -5645,7 +5684,8 @@ const TOKEN_CEILING_DEFAULTS = {
|
|
|
5645
5684
|
"dev-story": 4e5,
|
|
5646
5685
|
"code-review": 5e5,
|
|
5647
5686
|
"test-plan": 1e5,
|
|
5648
|
-
"test-expansion": 2e5
|
|
5687
|
+
"test-expansion": 2e5,
|
|
5688
|
+
"probe-author": 5e4
|
|
5649
5689
|
};
|
|
5650
5690
|
/**
|
|
5651
5691
|
* Resolve the effective token ceiling for a workflow type.
|
|
@@ -5674,7 +5714,7 @@ function getTokenCeiling(workflowType, tokenCeilings) {
|
|
|
5674
5714
|
|
|
5675
5715
|
//#endregion
|
|
5676
5716
|
//#region src/modules/compiled-workflows/create-story.ts
|
|
5677
|
-
const logger$
|
|
5717
|
+
const logger$20 = createLogger("compiled-workflows:create-story");
|
|
5678
5718
|
/**
|
|
5679
5719
|
* Compute a hex SHA-256 of the normalized source AC section text.
|
|
5680
5720
|
*
|
|
@@ -5709,13 +5749,13 @@ function hashSourceAcSection(section) {
|
|
|
5709
5749
|
*/
|
|
5710
5750
|
async function runCreateStory(deps, params) {
|
|
5711
5751
|
const { epicId, storyKey, pipelineRunId, source_ac_hash, priorDriftFeedback } = params;
|
|
5712
|
-
logger$
|
|
5752
|
+
logger$20.debug({
|
|
5713
5753
|
epicId,
|
|
5714
5754
|
storyKey,
|
|
5715
5755
|
pipelineRunId
|
|
5716
5756
|
}, "Starting create-story workflow");
|
|
5717
5757
|
const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("create-story", deps.tokenCeilings);
|
|
5718
|
-
logger$
|
|
5758
|
+
logger$20.info({
|
|
5719
5759
|
workflow: "create-story",
|
|
5720
5760
|
ceiling: TOKEN_CEILING,
|
|
5721
5761
|
source: tokenCeilingSource
|
|
@@ -5725,7 +5765,7 @@ async function runCreateStory(deps, params) {
|
|
|
5725
5765
|
template = await deps.pack.getPrompt("create-story");
|
|
5726
5766
|
} catch (err) {
|
|
5727
5767
|
const error = err instanceof Error ? err.message : String(err);
|
|
5728
|
-
logger$
|
|
5768
|
+
logger$20.error({ error }, "Failed to retrieve create-story prompt template");
|
|
5729
5769
|
return {
|
|
5730
5770
|
result: "failed",
|
|
5731
5771
|
error: `Failed to retrieve prompt template: ${error}`,
|
|
@@ -5742,7 +5782,7 @@ async function runCreateStory(deps, params) {
|
|
|
5742
5782
|
const storySection = extractStorySection(epicShardContent, storyKey);
|
|
5743
5783
|
if (storySection !== null) {
|
|
5744
5784
|
const computedHash = hashSourceAcSection(storySection);
|
|
5745
|
-
if (source_ac_hash !== void 0 && source_ac_hash !== computedHash) logger$
|
|
5785
|
+
if (source_ac_hash !== void 0 && source_ac_hash !== computedHash) logger$20.debug({
|
|
5746
5786
|
storyKey,
|
|
5747
5787
|
suppliedHash: source_ac_hash,
|
|
5748
5788
|
computedHash
|
|
@@ -5757,7 +5797,7 @@ async function runCreateStory(deps, params) {
|
|
|
5757
5797
|
const storyDef = storyDecisions.find((d) => d.category === "stories" && d.key === storyKey);
|
|
5758
5798
|
if (storyDef) {
|
|
5759
5799
|
storyDefinitionContent = storyDef.value;
|
|
5760
|
-
logger$
|
|
5800
|
+
logger$20.debug({ storyKey }, "Injected story definition from solutioning decisions");
|
|
5761
5801
|
}
|
|
5762
5802
|
} catch {}
|
|
5763
5803
|
const archConstraintsContent = await getArchConstraints$3(deps);
|
|
@@ -5808,7 +5848,7 @@ async function runCreateStory(deps, params) {
|
|
|
5808
5848
|
priority: "optional"
|
|
5809
5849
|
}]
|
|
5810
5850
|
], TOKEN_CEILING);
|
|
5811
|
-
logger$
|
|
5851
|
+
logger$20.debug({
|
|
5812
5852
|
tokenCount,
|
|
5813
5853
|
truncated,
|
|
5814
5854
|
tokenCeiling: TOKEN_CEILING
|
|
@@ -5828,7 +5868,7 @@ async function runCreateStory(deps, params) {
|
|
|
5828
5868
|
dispatchResult = await handle.result;
|
|
5829
5869
|
} catch (err) {
|
|
5830
5870
|
const error = err instanceof Error ? err.message : String(err);
|
|
5831
|
-
logger$
|
|
5871
|
+
logger$20.error({
|
|
5832
5872
|
epicId,
|
|
5833
5873
|
storyKey,
|
|
5834
5874
|
error
|
|
@@ -5849,7 +5889,7 @@ async function runCreateStory(deps, params) {
|
|
|
5849
5889
|
if (dispatchResult.status === "failed") {
|
|
5850
5890
|
const errorMsg = dispatchResult.parseError ?? `Dispatch failed with exit code ${dispatchResult.exitCode}`;
|
|
5851
5891
|
const stderrDetail = dispatchResult.output ? ` Output: ${dispatchResult.output}` : "";
|
|
5852
|
-
logger$
|
|
5892
|
+
logger$20.warn({
|
|
5853
5893
|
epicId,
|
|
5854
5894
|
storyKey,
|
|
5855
5895
|
exitCode: dispatchResult.exitCode,
|
|
@@ -5862,7 +5902,7 @@ async function runCreateStory(deps, params) {
|
|
|
5862
5902
|
};
|
|
5863
5903
|
}
|
|
5864
5904
|
if (dispatchResult.status === "timeout") {
|
|
5865
|
-
logger$
|
|
5905
|
+
logger$20.warn({
|
|
5866
5906
|
epicId,
|
|
5867
5907
|
storyKey
|
|
5868
5908
|
}, "Create-story dispatch timed out");
|
|
@@ -5875,7 +5915,7 @@ async function runCreateStory(deps, params) {
|
|
|
5875
5915
|
if (dispatchResult.parsed === null) {
|
|
5876
5916
|
const details = dispatchResult.parseError ?? "No YAML block found in output";
|
|
5877
5917
|
const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
|
|
5878
|
-
logger$
|
|
5918
|
+
logger$20.warn({
|
|
5879
5919
|
epicId,
|
|
5880
5920
|
storyKey,
|
|
5881
5921
|
details,
|
|
@@ -5891,7 +5931,7 @@ async function runCreateStory(deps, params) {
|
|
|
5891
5931
|
const parseResult = CreateStoryResultSchema.safeParse(dispatchResult.parsed);
|
|
5892
5932
|
if (!parseResult.success) {
|
|
5893
5933
|
const details = parseResult.error.message;
|
|
5894
|
-
logger$
|
|
5934
|
+
logger$20.warn({
|
|
5895
5935
|
epicId,
|
|
5896
5936
|
storyKey,
|
|
5897
5937
|
details
|
|
@@ -5904,7 +5944,7 @@ async function runCreateStory(deps, params) {
|
|
|
5904
5944
|
};
|
|
5905
5945
|
}
|
|
5906
5946
|
const parsed = parseResult.data;
|
|
5907
|
-
logger$
|
|
5947
|
+
logger$20.info({
|
|
5908
5948
|
epicId,
|
|
5909
5949
|
storyKey,
|
|
5910
5950
|
storyFile: parsed.story_file,
|
|
@@ -5932,7 +5972,7 @@ async function getImplementationDecisions(deps, pipelineRunId) {
|
|
|
5932
5972
|
}
|
|
5933
5973
|
return await getDecisionsByPhase(deps.db, "implementation");
|
|
5934
5974
|
} catch (err) {
|
|
5935
|
-
logger$
|
|
5975
|
+
logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
|
|
5936
5976
|
return [];
|
|
5937
5977
|
}
|
|
5938
5978
|
}
|
|
@@ -6019,10 +6059,10 @@ function computeStoryFileFidelity(storyFileContent, namedPaths) {
|
|
|
6019
6059
|
};
|
|
6020
6060
|
const missing = [];
|
|
6021
6061
|
const present = [];
|
|
6022
|
-
for (const path$
|
|
6023
|
-
const basename$2 = path$
|
|
6024
|
-
if (storyFileContent.includes(path$
|
|
6025
|
-
else missing.push(path$
|
|
6062
|
+
for (const path$4 of namedPaths) {
|
|
6063
|
+
const basename$2 = path$4.includes("/") ? path$4.slice(path$4.lastIndexOf("/") + 1) : path$4;
|
|
6064
|
+
if (storyFileContent.includes(path$4) || storyFileContent.includes(basename$2)) present.push(path$4);
|
|
6065
|
+
else missing.push(path$4);
|
|
6026
6066
|
}
|
|
6027
6067
|
return {
|
|
6028
6068
|
missing,
|
|
@@ -6134,7 +6174,7 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
|
6134
6174
|
if (storyKey) {
|
|
6135
6175
|
const perStoryShard = decisions.find((d) => d.category === "epic-shard" && d.key === storyKey);
|
|
6136
6176
|
if (perStoryShard?.value) {
|
|
6137
|
-
logger$
|
|
6177
|
+
logger$20.debug({
|
|
6138
6178
|
epicId,
|
|
6139
6179
|
storyKey
|
|
6140
6180
|
}, "Found per-story epic shard (direct lookup)");
|
|
@@ -6146,13 +6186,13 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
|
6146
6186
|
if (shardContent && storyKey) {
|
|
6147
6187
|
const storySection = extractStorySection(shardContent, storyKey);
|
|
6148
6188
|
if (storySection) {
|
|
6149
|
-
logger$
|
|
6189
|
+
logger$20.debug({
|
|
6150
6190
|
epicId,
|
|
6151
6191
|
storyKey
|
|
6152
6192
|
}, "Extracted per-story section from epic shard (pre-37-0 fallback)");
|
|
6153
6193
|
return storySection;
|
|
6154
6194
|
}
|
|
6155
|
-
logger$
|
|
6195
|
+
logger$20.info({
|
|
6156
6196
|
epicId,
|
|
6157
6197
|
storyKey
|
|
6158
6198
|
}, "Story section absent in decisions-store shard — attempting file-based fallback before returning stale shard");
|
|
@@ -6160,11 +6200,11 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
|
6160
6200
|
if (projectRoot) {
|
|
6161
6201
|
const fallback = readEpicShardFromFile(projectRoot, epicId);
|
|
6162
6202
|
if (fallback) {
|
|
6163
|
-
logger$
|
|
6203
|
+
logger$20.info({ epicId }, "Using file-based fallback for epic shard");
|
|
6164
6204
|
if (storyKey) {
|
|
6165
6205
|
const storySection = extractStorySection(fallback, storyKey);
|
|
6166
6206
|
if (storySection) {
|
|
6167
|
-
logger$
|
|
6207
|
+
logger$20.debug({
|
|
6168
6208
|
epicId,
|
|
6169
6209
|
storyKey
|
|
6170
6210
|
}, "Extracted per-story section from file-based epic shard");
|
|
@@ -6177,7 +6217,7 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
|
6177
6217
|
if (shardContent) return shardContent;
|
|
6178
6218
|
return "";
|
|
6179
6219
|
} catch (err) {
|
|
6180
|
-
logger$
|
|
6220
|
+
logger$20.warn({
|
|
6181
6221
|
epicId,
|
|
6182
6222
|
error: err instanceof Error ? err.message : String(err)
|
|
6183
6223
|
}, "Failed to retrieve epic shard");
|
|
@@ -6194,7 +6234,7 @@ function getPrevDevNotes(decisions, epicId) {
|
|
|
6194
6234
|
if (devNotes.length === 0) return "";
|
|
6195
6235
|
return devNotes[devNotes.length - 1].value;
|
|
6196
6236
|
} catch (err) {
|
|
6197
|
-
logger$
|
|
6237
|
+
logger$20.warn({
|
|
6198
6238
|
epicId,
|
|
6199
6239
|
error: err instanceof Error ? err.message : String(err)
|
|
6200
6240
|
}, "Failed to retrieve prev dev notes");
|
|
@@ -6227,7 +6267,7 @@ async function getArchConstraints$3(deps) {
|
|
|
6227
6267
|
const truncatedBody = body.length > 300 ? body.slice(0, 297) + "..." : body;
|
|
6228
6268
|
return `${header}\n${truncatedBody}`;
|
|
6229
6269
|
}).join("\n\n");
|
|
6230
|
-
logger$
|
|
6270
|
+
logger$20.info({
|
|
6231
6271
|
fullLength: full.length,
|
|
6232
6272
|
summarizedLength: summarized.length,
|
|
6233
6273
|
decisions: constraints.length
|
|
@@ -6237,13 +6277,13 @@ async function getArchConstraints$3(deps) {
|
|
|
6237
6277
|
if (deps.projectRoot) {
|
|
6238
6278
|
const fallback = readArchConstraintsFromFile(deps.projectRoot);
|
|
6239
6279
|
if (fallback) {
|
|
6240
|
-
logger$
|
|
6280
|
+
logger$20.info("Using file-based fallback for architecture constraints (decisions table empty)");
|
|
6241
6281
|
return fallback.length > ARCH_CONSTRAINT_MAX_CHARS ? fallback.slice(0, ARCH_CONSTRAINT_MAX_CHARS) + "\n\n[truncated for token budget]" : fallback;
|
|
6242
6282
|
}
|
|
6243
6283
|
}
|
|
6244
6284
|
return "";
|
|
6245
6285
|
} catch (err) {
|
|
6246
|
-
logger$
|
|
6286
|
+
logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
|
|
6247
6287
|
return "";
|
|
6248
6288
|
}
|
|
6249
6289
|
}
|
|
@@ -6296,7 +6336,7 @@ function readEpicShardFromFile(projectRoot, epicId) {
|
|
|
6296
6336
|
} catch {}
|
|
6297
6337
|
return "";
|
|
6298
6338
|
} catch (err) {
|
|
6299
|
-
logger$
|
|
6339
|
+
logger$20.warn({
|
|
6300
6340
|
epicId,
|
|
6301
6341
|
error: err instanceof Error ? err.message : String(err)
|
|
6302
6342
|
}, "File-based epic shard fallback failed");
|
|
@@ -6319,7 +6359,7 @@ function readArchConstraintsFromFile(projectRoot) {
|
|
|
6319
6359
|
const content = readFileSync(archPath, "utf-8");
|
|
6320
6360
|
return content.slice(0, 1500);
|
|
6321
6361
|
} catch (err) {
|
|
6322
|
-
logger$
|
|
6362
|
+
logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
|
|
6323
6363
|
return "";
|
|
6324
6364
|
}
|
|
6325
6365
|
}
|
|
@@ -6332,7 +6372,7 @@ async function getStoryTemplate(deps) {
|
|
|
6332
6372
|
try {
|
|
6333
6373
|
return await deps.pack.getTemplate("story");
|
|
6334
6374
|
} catch (err) {
|
|
6335
|
-
logger$
|
|
6375
|
+
logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
|
|
6336
6376
|
return "";
|
|
6337
6377
|
}
|
|
6338
6378
|
}
|
|
@@ -6369,7 +6409,7 @@ async function isValidStoryFile(filePath) {
|
|
|
6369
6409
|
|
|
6370
6410
|
//#endregion
|
|
6371
6411
|
//#region src/modules/compiled-workflows/git-helpers.ts
|
|
6372
|
-
const logger$
|
|
6412
|
+
const logger$19 = createLogger("compiled-workflows:git-helpers");
|
|
6373
6413
|
/**
|
|
6374
6414
|
* Check whether the repo at `cwd` has at least one commit (HEAD resolves).
|
|
6375
6415
|
* Returns false for fresh repos with no commits, avoiding `fatal: bad revision 'HEAD'`.
|
|
@@ -6406,7 +6446,7 @@ function hasCommits(cwd) {
|
|
|
6406
6446
|
*/
|
|
6407
6447
|
async function getGitDiffSummary(workingDirectory = process.cwd()) {
|
|
6408
6448
|
if (!hasCommits(workingDirectory)) {
|
|
6409
|
-
logger$
|
|
6449
|
+
logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff");
|
|
6410
6450
|
return "";
|
|
6411
6451
|
}
|
|
6412
6452
|
return runGitCommand(["diff", "HEAD"], workingDirectory, "git-diff-summary");
|
|
@@ -6423,7 +6463,7 @@ async function getGitDiffSummary(workingDirectory = process.cwd()) {
|
|
|
6423
6463
|
*/
|
|
6424
6464
|
async function getGitDiffStatSummary(workingDirectory = process.cwd()) {
|
|
6425
6465
|
if (!hasCommits(workingDirectory)) {
|
|
6426
|
-
logger$
|
|
6466
|
+
logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat");
|
|
6427
6467
|
return "";
|
|
6428
6468
|
}
|
|
6429
6469
|
return runGitCommand([
|
|
@@ -6472,7 +6512,7 @@ async function getGitDiffStatBetweenCommits(baseCommit, endCommit = "HEAD", work
|
|
|
6472
6512
|
async function getGitDiffForFiles(files, workingDirectory = process.cwd()) {
|
|
6473
6513
|
if (files.length === 0) return "";
|
|
6474
6514
|
if (!hasCommits(workingDirectory)) {
|
|
6475
|
-
logger$
|
|
6515
|
+
logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff for files");
|
|
6476
6516
|
return "";
|
|
6477
6517
|
}
|
|
6478
6518
|
await stageIntentToAdd(files, workingDirectory);
|
|
@@ -6499,7 +6539,7 @@ async function getGitDiffForFiles(files, workingDirectory = process.cwd()) {
|
|
|
6499
6539
|
async function getGitDiffStatForFiles(files, workingDirectory = process.cwd()) {
|
|
6500
6540
|
if (files.length === 0) return "";
|
|
6501
6541
|
if (!hasCommits(workingDirectory)) {
|
|
6502
|
-
logger$
|
|
6542
|
+
logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat for files");
|
|
6503
6543
|
return "";
|
|
6504
6544
|
}
|
|
6505
6545
|
return runGitCommand([
|
|
@@ -6548,7 +6588,7 @@ async function stageIntentToAdd(files, workingDirectory) {
|
|
|
6548
6588
|
if (files.length === 0) return;
|
|
6549
6589
|
const existing = files.filter((f$1) => {
|
|
6550
6590
|
const exists = existsSync(f$1);
|
|
6551
|
-
if (!exists) logger$
|
|
6591
|
+
if (!exists) logger$19.debug({ file: f$1 }, "Skipping nonexistent file in stageIntentToAdd");
|
|
6552
6592
|
return exists;
|
|
6553
6593
|
});
|
|
6554
6594
|
if (existing.length === 0) return;
|
|
@@ -6582,7 +6622,7 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
6582
6622
|
stderr += chunk.toString("utf-8");
|
|
6583
6623
|
});
|
|
6584
6624
|
proc$1.on("error", (err) => {
|
|
6585
|
-
logger$
|
|
6625
|
+
logger$19.warn({
|
|
6586
6626
|
label: logLabel,
|
|
6587
6627
|
cwd,
|
|
6588
6628
|
error: err.message
|
|
@@ -6591,7 +6631,7 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
6591
6631
|
});
|
|
6592
6632
|
proc$1.on("close", (code) => {
|
|
6593
6633
|
if (code !== 0) {
|
|
6594
|
-
logger$
|
|
6634
|
+
logger$19.warn({
|
|
6595
6635
|
label: logLabel,
|
|
6596
6636
|
cwd,
|
|
6597
6637
|
code,
|
|
@@ -6607,7 +6647,7 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
6607
6647
|
|
|
6608
6648
|
//#endregion
|
|
6609
6649
|
//#region src/modules/compiled-workflows/story-complexity.ts
|
|
6610
|
-
const logger$
|
|
6650
|
+
const logger$18 = createLogger("compiled-workflows:story-complexity");
|
|
6611
6651
|
/**
|
|
6612
6652
|
* Compute a complexity score from story markdown content.
|
|
6613
6653
|
*
|
|
@@ -6659,7 +6699,7 @@ function resolveFixStoryMaxTurns(complexityScore) {
|
|
|
6659
6699
|
* @param resolvedMaxTurns - Turn limit resolved for this dispatch
|
|
6660
6700
|
*/
|
|
6661
6701
|
function logComplexityResult(storyKey, complexity, resolvedMaxTurns) {
|
|
6662
|
-
logger$
|
|
6702
|
+
logger$18.info({
|
|
6663
6703
|
storyKey,
|
|
6664
6704
|
taskCount: complexity.taskCount,
|
|
6665
6705
|
subtaskCount: complexity.subtaskCount,
|
|
@@ -6915,9 +6955,9 @@ function resolveInstallCommand(projectRoot) {
|
|
|
6915
6955
|
|
|
6916
6956
|
//#endregion
|
|
6917
6957
|
//#region src/modules/compiled-workflows/dev-story.ts
|
|
6918
|
-
const logger$
|
|
6958
|
+
const logger$17 = createLogger("compiled-workflows:dev-story");
|
|
6919
6959
|
/** Default timeout for dev-story dispatches in milliseconds (30 min) */
|
|
6920
|
-
const DEFAULT_TIMEOUT_MS$
|
|
6960
|
+
const DEFAULT_TIMEOUT_MS$2 = 18e5;
|
|
6921
6961
|
/**
|
|
6922
6962
|
* Execute the compiled dev-story workflow.
|
|
6923
6963
|
*
|
|
@@ -6927,12 +6967,12 @@ const DEFAULT_TIMEOUT_MS$1 = 18e5;
|
|
|
6927
6967
|
*/
|
|
6928
6968
|
async function runDevStory(deps, params) {
|
|
6929
6969
|
const { storyKey, storyFilePath, taskScope, priorFiles, findingsPrompt: handlerFindingsPrompt } = params;
|
|
6930
|
-
logger$
|
|
6970
|
+
logger$17.info({
|
|
6931
6971
|
storyKey,
|
|
6932
6972
|
storyFilePath
|
|
6933
6973
|
}, "Starting compiled dev-story workflow");
|
|
6934
6974
|
const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("dev-story", deps.tokenCeilings);
|
|
6935
|
-
logger$
|
|
6975
|
+
logger$17.info({
|
|
6936
6976
|
workflow: "dev-story",
|
|
6937
6977
|
ceiling: TOKEN_CEILING,
|
|
6938
6978
|
source: tokenCeilingSource
|
|
@@ -6975,10 +7015,10 @@ async function runDevStory(deps, params) {
|
|
|
6975
7015
|
let template;
|
|
6976
7016
|
try {
|
|
6977
7017
|
template = await deps.pack.getPrompt("dev-story");
|
|
6978
|
-
logger$
|
|
7018
|
+
logger$17.debug({ storyKey }, "Retrieved dev-story prompt template from pack");
|
|
6979
7019
|
} catch (err) {
|
|
6980
7020
|
const error = err instanceof Error ? err.message : String(err);
|
|
6981
|
-
logger$
|
|
7021
|
+
logger$17.error({
|
|
6982
7022
|
storyKey,
|
|
6983
7023
|
error
|
|
6984
7024
|
}, "Failed to retrieve dev-story prompt template");
|
|
@@ -6989,14 +7029,14 @@ async function runDevStory(deps, params) {
|
|
|
6989
7029
|
storyContent = await readFile$1(storyFilePath, "utf-8");
|
|
6990
7030
|
} catch (err) {
|
|
6991
7031
|
if (err.code === "ENOENT") {
|
|
6992
|
-
logger$
|
|
7032
|
+
logger$17.error({
|
|
6993
7033
|
storyKey,
|
|
6994
7034
|
storyFilePath
|
|
6995
7035
|
}, "Story file not found");
|
|
6996
7036
|
return makeFailureResult("story_file_not_found");
|
|
6997
7037
|
}
|
|
6998
7038
|
const error = err instanceof Error ? err.message : String(err);
|
|
6999
|
-
logger$
|
|
7039
|
+
logger$17.error({
|
|
7000
7040
|
storyKey,
|
|
7001
7041
|
storyFilePath,
|
|
7002
7042
|
error
|
|
@@ -7004,7 +7044,7 @@ async function runDevStory(deps, params) {
|
|
|
7004
7044
|
return makeFailureResult(`story_file_read_error: ${error}`);
|
|
7005
7045
|
}
|
|
7006
7046
|
if (storyContent.trim().length === 0) {
|
|
7007
|
-
logger$
|
|
7047
|
+
logger$17.error({
|
|
7008
7048
|
storyKey,
|
|
7009
7049
|
storyFilePath
|
|
7010
7050
|
}, "Story file is empty");
|
|
@@ -7012,7 +7052,7 @@ async function runDevStory(deps, params) {
|
|
|
7012
7052
|
}
|
|
7013
7053
|
const staleStatus = detectDeprecatedStatusField(storyContent);
|
|
7014
7054
|
if (staleStatus !== null) {
|
|
7015
|
-
logger$
|
|
7055
|
+
logger$17.warn({
|
|
7016
7056
|
storyFilePath,
|
|
7017
7057
|
staleStatus
|
|
7018
7058
|
}, "Story spec contains deprecated Status field — stripped before dispatch (status is managed by Dolt work graph)");
|
|
@@ -7027,17 +7067,17 @@ async function runDevStory(deps, params) {
|
|
|
7027
7067
|
const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
|
|
7028
7068
|
if (testPatternDecisions.length > 0) {
|
|
7029
7069
|
testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
|
|
7030
|
-
logger$
|
|
7070
|
+
logger$17.debug({
|
|
7031
7071
|
storyKey,
|
|
7032
7072
|
count: testPatternDecisions.length
|
|
7033
7073
|
}, "Loaded test patterns from decision store");
|
|
7034
7074
|
} else {
|
|
7035
7075
|
testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
|
|
7036
|
-
logger$
|
|
7076
|
+
logger$17.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
|
|
7037
7077
|
}
|
|
7038
7078
|
} catch (err) {
|
|
7039
7079
|
const error = err instanceof Error ? err.message : String(err);
|
|
7040
|
-
logger$
|
|
7080
|
+
logger$17.warn({
|
|
7041
7081
|
storyKey,
|
|
7042
7082
|
error
|
|
7043
7083
|
}, "Failed to load test patterns — using defaults");
|
|
@@ -7051,7 +7091,7 @@ async function runDevStory(deps, params) {
|
|
|
7051
7091
|
if (deps.repoMapInjector !== void 0) {
|
|
7052
7092
|
const injection = await deps.repoMapInjector.buildContext(storyContent, deps.maxRepoMapTokens ?? 2e3);
|
|
7053
7093
|
repoContextContent = injection.text;
|
|
7054
|
-
logger$
|
|
7094
|
+
logger$17.info({
|
|
7055
7095
|
storyKey,
|
|
7056
7096
|
repoMapTokens: Math.ceil(injection.text.length / 4),
|
|
7057
7097
|
symbolCount: injection.symbolCount,
|
|
@@ -7061,7 +7101,7 @@ async function runDevStory(deps, params) {
|
|
|
7061
7101
|
let priorFindingsContent = "";
|
|
7062
7102
|
if (handlerFindingsPrompt !== void 0 && handlerFindingsPrompt.length > 0) {
|
|
7063
7103
|
priorFindingsContent = handlerFindingsPrompt;
|
|
7064
|
-
logger$
|
|
7104
|
+
logger$17.debug({
|
|
7065
7105
|
storyKey,
|
|
7066
7106
|
findingsLen: handlerFindingsPrompt.length
|
|
7067
7107
|
}, "Using pre-computed findings from handler (Story 53-8 AC2)");
|
|
@@ -7073,7 +7113,7 @@ async function runDevStory(deps, params) {
|
|
|
7073
7113
|
});
|
|
7074
7114
|
if (findings.length > 0) {
|
|
7075
7115
|
priorFindingsContent = findings;
|
|
7076
|
-
logger$
|
|
7116
|
+
logger$17.debug({
|
|
7077
7117
|
storyKey,
|
|
7078
7118
|
findingsLen: findings.length
|
|
7079
7119
|
}, "Injecting relevance-scored findings into dev-story prompt");
|
|
@@ -7093,7 +7133,7 @@ async function runDevStory(deps, params) {
|
|
|
7093
7133
|
if (plan.test_categories && plan.test_categories.length > 0) parts.push(`\n### Categories: ${plan.test_categories.join(", ")}`);
|
|
7094
7134
|
if (plan.coverage_notes) parts.push(`\n### Coverage Notes\n${plan.coverage_notes}`);
|
|
7095
7135
|
testPlanContent = parts.join("\n");
|
|
7096
|
-
logger$
|
|
7136
|
+
logger$17.debug({ storyKey }, "Injecting test plan into dev-story prompt");
|
|
7097
7137
|
}
|
|
7098
7138
|
} catch {}
|
|
7099
7139
|
const sections = [
|
|
@@ -7154,7 +7194,7 @@ async function runDevStory(deps, params) {
|
|
|
7154
7194
|
}
|
|
7155
7195
|
];
|
|
7156
7196
|
const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING);
|
|
7157
|
-
logger$
|
|
7197
|
+
logger$17.info({
|
|
7158
7198
|
storyKey,
|
|
7159
7199
|
tokenCount,
|
|
7160
7200
|
ceiling: TOKEN_CEILING,
|
|
@@ -7166,7 +7206,7 @@ async function runDevStory(deps, params) {
|
|
|
7166
7206
|
prompt,
|
|
7167
7207
|
agent: deps.agentId ?? "claude-code",
|
|
7168
7208
|
taskType: "dev-story",
|
|
7169
|
-
timeout: DEFAULT_TIMEOUT_MS$
|
|
7209
|
+
timeout: DEFAULT_TIMEOUT_MS$2,
|
|
7170
7210
|
outputSchema: DevStoryResultSchema,
|
|
7171
7211
|
maxTurns: resolvedMaxTurns,
|
|
7172
7212
|
...deps.projectRoot !== void 0 ? { workingDirectory: deps.projectRoot } : {},
|
|
@@ -7178,7 +7218,7 @@ async function runDevStory(deps, params) {
|
|
|
7178
7218
|
dispatchResult = await handle.result;
|
|
7179
7219
|
} catch (err) {
|
|
7180
7220
|
const error = err instanceof Error ? err.message : String(err);
|
|
7181
|
-
logger$
|
|
7221
|
+
logger$17.error({
|
|
7182
7222
|
storyKey,
|
|
7183
7223
|
error
|
|
7184
7224
|
}, "Dispatch threw an unexpected error");
|
|
@@ -7189,11 +7229,11 @@ async function runDevStory(deps, params) {
|
|
|
7189
7229
|
output: dispatchResult.tokenEstimate.output
|
|
7190
7230
|
};
|
|
7191
7231
|
if (dispatchResult.status === "timeout") {
|
|
7192
|
-
logger$
|
|
7232
|
+
logger$17.error({
|
|
7193
7233
|
storyKey,
|
|
7194
7234
|
durationMs: dispatchResult.durationMs
|
|
7195
7235
|
}, "Dev-story dispatch timed out");
|
|
7196
|
-
if (dispatchResult.output.length > 0) logger$
|
|
7236
|
+
if (dispatchResult.output.length > 0) logger$17.info({
|
|
7197
7237
|
storyKey,
|
|
7198
7238
|
partialOutput: dispatchResult.output.slice(0, 500)
|
|
7199
7239
|
}, "Partial output before timeout");
|
|
@@ -7203,12 +7243,12 @@ async function runDevStory(deps, params) {
|
|
|
7203
7243
|
};
|
|
7204
7244
|
}
|
|
7205
7245
|
if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
|
|
7206
|
-
logger$
|
|
7246
|
+
logger$17.error({
|
|
7207
7247
|
storyKey,
|
|
7208
7248
|
exitCode: dispatchResult.exitCode,
|
|
7209
7249
|
status: dispatchResult.status
|
|
7210
7250
|
}, "Dev-story dispatch failed");
|
|
7211
|
-
if (dispatchResult.output.length > 0) logger$
|
|
7251
|
+
if (dispatchResult.output.length > 0) logger$17.info({
|
|
7212
7252
|
storyKey,
|
|
7213
7253
|
partialOutput: dispatchResult.output.slice(0, 500)
|
|
7214
7254
|
}, "Partial output from failed dispatch");
|
|
@@ -7220,7 +7260,7 @@ async function runDevStory(deps, params) {
|
|
|
7220
7260
|
if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
|
|
7221
7261
|
const details = dispatchResult.parseError ?? "parsed result was null";
|
|
7222
7262
|
const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
|
|
7223
|
-
logger$
|
|
7263
|
+
logger$17.error({
|
|
7224
7264
|
storyKey,
|
|
7225
7265
|
parseError: details,
|
|
7226
7266
|
rawOutputSnippet: rawSnippet
|
|
@@ -7228,12 +7268,12 @@ async function runDevStory(deps, params) {
|
|
|
7228
7268
|
let filesModified = [];
|
|
7229
7269
|
try {
|
|
7230
7270
|
filesModified = await getGitChangedFiles(deps.projectRoot ?? process.cwd());
|
|
7231
|
-
if (filesModified.length > 0) logger$
|
|
7271
|
+
if (filesModified.length > 0) logger$17.info({
|
|
7232
7272
|
storyKey,
|
|
7233
7273
|
fileCount: filesModified.length
|
|
7234
7274
|
}, "Recovered files_modified from git status (YAML fallback)");
|
|
7235
7275
|
} catch (err) {
|
|
7236
|
-
logger$
|
|
7276
|
+
logger$17.warn({
|
|
7237
7277
|
storyKey,
|
|
7238
7278
|
error: err instanceof Error ? err.message : String(err)
|
|
7239
7279
|
}, "Failed to recover files_modified from git");
|
|
@@ -7250,7 +7290,7 @@ async function runDevStory(deps, params) {
|
|
|
7250
7290
|
};
|
|
7251
7291
|
}
|
|
7252
7292
|
const parsed = dispatchResult.parsed;
|
|
7253
|
-
logger$
|
|
7293
|
+
logger$17.info({
|
|
7254
7294
|
storyKey,
|
|
7255
7295
|
result: parsed.result,
|
|
7256
7296
|
acMet: parsed.ac_met.length
|
|
@@ -7366,7 +7406,7 @@ function extractReferencedFiles(storyContent) {
|
|
|
7366
7406
|
const matches = storyContent.match(filePathRegex) ?? [];
|
|
7367
7407
|
const freq = new Map();
|
|
7368
7408
|
for (const m of matches) freq.set(m, (freq.get(m) ?? 0) + 1);
|
|
7369
|
-
const sorted = [...freq.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([path$
|
|
7409
|
+
const sorted = [...freq.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([path$4]) => path$4);
|
|
7370
7410
|
return sorted.filter((p) => !p.includes(".test."));
|
|
7371
7411
|
}
|
|
7372
7412
|
function extractFilesInScope(storyContent) {
|
|
@@ -7390,19 +7430,6 @@ function extractFilesInScope(storyContent) {
|
|
|
7390
7430
|
//#endregion
|
|
7391
7431
|
//#region src/modules/compiled-workflows/scope-guardrail.ts
|
|
7392
7432
|
/**
|
|
7393
|
-
* ScopeGuardrail — utility for detecting out-of-scope file modifications in code review.
|
|
7394
|
-
*
|
|
7395
|
-
* Parses the expected file set from a story spec and compares it against the
|
|
7396
|
-
* actual files modified by the dev agent. Returns a pre-computed scope analysis
|
|
7397
|
-
* markdown string that can be injected into the code-review prompt so the LLM
|
|
7398
|
-
* reviewer does not need to re-parse the spec sections manually.
|
|
7399
|
-
*
|
|
7400
|
-
* Architecture constraints:
|
|
7401
|
-
* - Pure utility: no imports from packages/sdlc/, packages/core/, or persistence layer
|
|
7402
|
-
* - Takes plain strings in, returns plain strings out
|
|
7403
|
-
* - Test-file exemption patterns must match countTestMetrics() in code-review.ts
|
|
7404
|
-
*/
|
|
7405
|
-
/**
|
|
7406
7433
|
* File extensions that qualify a token as a "path-like" string in task bullets.
|
|
7407
7434
|
* Any token containing `/` and one of these extensions is treated as a file path.
|
|
7408
7435
|
*/
|
|
@@ -7495,13 +7522,17 @@ var ScopeGuardrail = class ScopeGuardrail {
|
|
|
7495
7522
|
*
|
|
7496
7523
|
* @param storyContent - Raw story spec markdown text
|
|
7497
7524
|
* @param filesModified - List of file paths from the git diff
|
|
7525
|
+
* @param fileDiffs - Optional per-file diff map. When provided, transitive
|
|
7526
|
+
* re-exports (Story 61-5) whose `from` source IS in
|
|
7527
|
+
* expectedFiles are excluded from the out-of-scope set.
|
|
7498
7528
|
* @returns Markdown string listing expected/actual/delta file sets,
|
|
7499
7529
|
* or empty string if no violations found
|
|
7500
7530
|
*/
|
|
7501
|
-
static buildAnalysis(storyContent, filesModified) {
|
|
7531
|
+
static buildAnalysis(storyContent, filesModified, fileDiffs) {
|
|
7502
7532
|
const expectedFiles = ScopeGuardrail.parseExpectedFiles(storyContent);
|
|
7503
7533
|
const nonTestFiles = filesModified.filter((f$1) => !isTestFile(f$1));
|
|
7504
|
-
|
|
7534
|
+
let outOfScope = nonTestFiles.filter((f$1) => !expectedFiles.has(f$1));
|
|
7535
|
+
if (fileDiffs !== void 0) outOfScope = outOfScope.filter((filePath) => !isPureTransitiveReExport(filePath, fileDiffs.get(filePath), expectedFiles));
|
|
7505
7536
|
if (outOfScope.length === 0) return "";
|
|
7506
7537
|
const expectedList = expectedFiles.size > 0 ? Array.from(expectedFiles).map((f$1) => ` - ${f$1}`).join("\n") : " (none specified)";
|
|
7507
7538
|
const actualList = nonTestFiles.length > 0 ? nonTestFiles.map((f$1) => ` - ${f$1}`).join("\n") : " (none)";
|
|
@@ -7571,10 +7602,108 @@ function isFilePath(candidate) {
|
|
|
7571
7602
|
if (/\s/.test(candidate)) return false;
|
|
7572
7603
|
return RECOGNIZED_EXTENSIONS.some((ext$2) => candidate.endsWith(ext$2));
|
|
7573
7604
|
}
|
|
7605
|
+
/**
|
|
7606
|
+
* Split a combined `git diff` output into per-file diff sections, keyed by
|
|
7607
|
+
* the post-image (`b/`) path. Returns an empty map if the input is empty.
|
|
7608
|
+
*
|
|
7609
|
+
* Used by callers that need to feed per-file diffs into
|
|
7610
|
+
* `ScopeGuardrail.buildAnalysis` for transitive re-export detection.
|
|
7611
|
+
*/
|
|
7612
|
+
function parseDiffByFile(combinedDiff) {
|
|
7613
|
+
const result = new Map();
|
|
7614
|
+
if (combinedDiff.trim() === "") return result;
|
|
7615
|
+
let currentPath = null;
|
|
7616
|
+
let currentLines = [];
|
|
7617
|
+
for (const line of combinedDiff.split("\n")) {
|
|
7618
|
+
const headerMatch = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
|
|
7619
|
+
if (headerMatch !== null) {
|
|
7620
|
+
if (currentPath !== null) result.set(currentPath, currentLines.join("\n"));
|
|
7621
|
+
currentPath = headerMatch[2] ?? null;
|
|
7622
|
+
currentLines = [line];
|
|
7623
|
+
continue;
|
|
7624
|
+
}
|
|
7625
|
+
if (currentPath !== null) currentLines.push(line);
|
|
7626
|
+
}
|
|
7627
|
+
if (currentPath !== null) result.set(currentPath, currentLines.join("\n"));
|
|
7628
|
+
return result;
|
|
7629
|
+
}
|
|
7630
|
+
/**
|
|
7631
|
+
* Single-line re-export pattern. Matches:
|
|
7632
|
+
* export { Foo } from './bar.js'
|
|
7633
|
+
* export { Foo, Bar as Baz } from './bar'
|
|
7634
|
+
* export type { Foo } from './bar.js'
|
|
7635
|
+
* export { type Foo, Bar } from './bar.js'
|
|
7636
|
+
*
|
|
7637
|
+
* Captures the relative `from` path (group 1). Path must start with `./`
|
|
7638
|
+
* (downward re-exports only — the canonical pattern is package barrel files
|
|
7639
|
+
* re-exporting submodule symbols). `../` paths are not tolerated; those
|
|
7640
|
+
* suggest cross-package coupling that warrants real review.
|
|
7641
|
+
*/
|
|
7642
|
+
const REEXPORT_LINE_RE = /^\s*export\s+(?:type\s+)?\{[^}]+\}\s+from\s+['"](\.\/[^'"]+)['"]\s*;?\s*$/;
|
|
7643
|
+
/**
|
|
7644
|
+
* Returns true if `diffContent` represents a pure transitive re-export
|
|
7645
|
+
* change for `modifiedFilePath` — meaning every added/removed line is a
|
|
7646
|
+
* re-export whose `from` source resolves to a file in `expectedFiles`.
|
|
7647
|
+
*
|
|
7648
|
+
* Story 61-5: surfaced by 60-13 dispatch where dev added a 2-line re-export
|
|
7649
|
+
* of `detectsEventDrivenAC` to `verification/index.ts` (an `index.ts`
|
|
7650
|
+
* re-export hop required for cross-package access via `@substrate-ai/sdlc`).
|
|
7651
|
+
* scope-guardrail flagged it as out-of-scope, reviewer admitted "the change
|
|
7652
|
+
* is clearly required" but flagged anyway, story timed out → escalated.
|
|
7653
|
+
*
|
|
7654
|
+
* Returns false when:
|
|
7655
|
+
* - diff content unavailable
|
|
7656
|
+
* - any non-re-export, non-comment, non-blank change exists
|
|
7657
|
+
* - any re-export's resolved `from` source is NOT in expectedFiles
|
|
7658
|
+
* - diff is empty (no changes — preserve existing behavior)
|
|
7659
|
+
*/
|
|
7660
|
+
function isPureTransitiveReExport(modifiedFilePath, diffContent, expectedFiles) {
|
|
7661
|
+
if (diffContent === void 0 || diffContent.trim() === "") return false;
|
|
7662
|
+
let sawChange = false;
|
|
7663
|
+
for (const line of diffContent.split("\n")) {
|
|
7664
|
+
if (line.startsWith("+++") || line.startsWith("---") || line.startsWith("@@") || line.startsWith("diff ") || line.startsWith("index ") || line.startsWith("similarity ") || line.startsWith("rename ") || line.startsWith("new file ") || line.startsWith("deleted file ") || line.startsWith("Binary ") || line.startsWith("\\ No newline")) continue;
|
|
7665
|
+
if (!line.startsWith("+") && !line.startsWith("-")) continue;
|
|
7666
|
+
const content = line.slice(1);
|
|
7667
|
+
const trimmed = content.trim();
|
|
7668
|
+
if (trimmed === "") continue;
|
|
7669
|
+
if (trimmed.startsWith("//")) continue;
|
|
7670
|
+
sawChange = true;
|
|
7671
|
+
const match$2 = trimmed.match(REEXPORT_LINE_RE);
|
|
7672
|
+
if (match$2 === null) return false;
|
|
7673
|
+
const fromPath = match$2[1] ?? "";
|
|
7674
|
+
if (!resolvesIntoExpected(modifiedFilePath, fromPath, expectedFiles)) return false;
|
|
7675
|
+
}
|
|
7676
|
+
return sawChange;
|
|
7677
|
+
}
|
|
7678
|
+
/**
|
|
7679
|
+
* Resolve a `from './x.js'` path relative to `modifiedFilePath`'s directory
|
|
7680
|
+
* and check if the resolved path (or its `.ts` sibling) is in `expectedFiles`.
|
|
7681
|
+
*
|
|
7682
|
+
* ESM convention: source files are `.ts`, but imports use `.js` extensions.
|
|
7683
|
+
* We accept either extension when matching against expectedFiles.
|
|
7684
|
+
*/
|
|
7685
|
+
function resolvesIntoExpected(modifiedFilePath, relativePath, expectedFiles) {
|
|
7686
|
+
const dir = path$2.posix.dirname(modifiedFilePath);
|
|
7687
|
+
const resolved = path$2.posix.normalize(path$2.posix.join(dir, relativePath));
|
|
7688
|
+
const candidates = new Set([resolved]);
|
|
7689
|
+
if (resolved.endsWith(".js")) {
|
|
7690
|
+
candidates.add(resolved.slice(0, -3) + ".ts");
|
|
7691
|
+
candidates.add(resolved.slice(0, -3) + ".tsx");
|
|
7692
|
+
}
|
|
7693
|
+
if (resolved.endsWith(".ts")) candidates.add(resolved.slice(0, -3) + ".js");
|
|
7694
|
+
if (!/\.(t|j)sx?$/.test(resolved)) {
|
|
7695
|
+
candidates.add(resolved + ".ts");
|
|
7696
|
+
candidates.add(resolved + ".tsx");
|
|
7697
|
+
candidates.add(resolved + ".js");
|
|
7698
|
+
candidates.add(resolved + "/index.ts");
|
|
7699
|
+
}
|
|
7700
|
+
for (const candidate of candidates) if (expectedFiles.has(candidate)) return true;
|
|
7701
|
+
return false;
|
|
7702
|
+
}
|
|
7574
7703
|
|
|
7575
7704
|
//#endregion
|
|
7576
7705
|
//#region src/modules/compiled-workflows/code-review.ts
|
|
7577
|
-
const logger$
|
|
7706
|
+
const logger$16 = createLogger("compiled-workflows:code-review");
|
|
7578
7707
|
/**
|
|
7579
7708
|
* Default fallback result when dispatch fails or times out.
|
|
7580
7709
|
* Uses NEEDS_MINOR_FIXES (not NEEDS_MAJOR_REWORK) so a parse/schema failure
|
|
@@ -7649,14 +7778,14 @@ async function countTestMetrics(filesModified, cwd) {
|
|
|
7649
7778
|
async function runCodeReview(deps, params) {
|
|
7650
7779
|
const { storyKey, storyFilePath, workingDirectory, pipelineRunId, filesModified, previousIssues, buildPassed, baselineCommit } = params;
|
|
7651
7780
|
const cwd = workingDirectory ?? process.cwd();
|
|
7652
|
-
logger$
|
|
7781
|
+
logger$16.debug({
|
|
7653
7782
|
storyKey,
|
|
7654
7783
|
storyFilePath,
|
|
7655
7784
|
cwd,
|
|
7656
7785
|
pipelineRunId
|
|
7657
7786
|
}, "Starting code-review workflow");
|
|
7658
7787
|
const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("code-review", deps.tokenCeilings);
|
|
7659
|
-
logger$
|
|
7788
|
+
logger$16.info({
|
|
7660
7789
|
workflow: "code-review",
|
|
7661
7790
|
ceiling: TOKEN_CEILING,
|
|
7662
7791
|
source: tokenCeilingSource
|
|
@@ -7666,7 +7795,7 @@ async function runCodeReview(deps, params) {
|
|
|
7666
7795
|
template = await deps.pack.getPrompt("code-review");
|
|
7667
7796
|
} catch (err) {
|
|
7668
7797
|
const error = err instanceof Error ? err.message : String(err);
|
|
7669
|
-
logger$
|
|
7798
|
+
logger$16.error({ error }, "Failed to retrieve code-review prompt template");
|
|
7670
7799
|
return defaultFailResult(`Failed to retrieve prompt template: ${error}`, {
|
|
7671
7800
|
input: 0,
|
|
7672
7801
|
output: 0
|
|
@@ -7677,7 +7806,7 @@ async function runCodeReview(deps, params) {
|
|
|
7677
7806
|
storyContent = await readFile$1(storyFilePath, "utf-8");
|
|
7678
7807
|
} catch (err) {
|
|
7679
7808
|
const error = err instanceof Error ? err.message : String(err);
|
|
7680
|
-
logger$
|
|
7809
|
+
logger$16.error({
|
|
7681
7810
|
storyFilePath,
|
|
7682
7811
|
error
|
|
7683
7812
|
}, "Failed to read story file");
|
|
@@ -7697,12 +7826,12 @@ async function runCodeReview(deps, params) {
|
|
|
7697
7826
|
const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
|
|
7698
7827
|
if (scopedTotal <= TOKEN_CEILING) {
|
|
7699
7828
|
gitDiffContent = scopedDiff;
|
|
7700
|
-
logger$
|
|
7829
|
+
logger$16.debug({
|
|
7701
7830
|
fileCount: filesModified.length,
|
|
7702
7831
|
tokenCount: scopedTotal
|
|
7703
7832
|
}, "Using scoped file diff");
|
|
7704
7833
|
} else {
|
|
7705
|
-
logger$
|
|
7834
|
+
logger$16.warn({
|
|
7706
7835
|
estimatedTotal: scopedTotal,
|
|
7707
7836
|
ceiling: TOKEN_CEILING,
|
|
7708
7837
|
fileCount: filesModified.length
|
|
@@ -7716,7 +7845,7 @@ async function runCodeReview(deps, params) {
|
|
|
7716
7845
|
const fullTotal = nonDiffTokens + countTokens(fullDiff);
|
|
7717
7846
|
if (fullTotal <= TOKEN_CEILING) gitDiffContent = fullDiff;
|
|
7718
7847
|
else {
|
|
7719
|
-
logger$
|
|
7848
|
+
logger$16.warn({
|
|
7720
7849
|
estimatedTotal: fullTotal,
|
|
7721
7850
|
ceiling: TOKEN_CEILING
|
|
7722
7851
|
}, "Full git diff would exceed token ceiling — using stat-only summary");
|
|
@@ -7724,7 +7853,7 @@ async function runCodeReview(deps, params) {
|
|
|
7724
7853
|
}
|
|
7725
7854
|
}
|
|
7726
7855
|
if (gitDiffContent.trim().length === 0 && baselineCommit) {
|
|
7727
|
-
logger$
|
|
7856
|
+
logger$16.info({
|
|
7728
7857
|
storyKey,
|
|
7729
7858
|
baselineCommit
|
|
7730
7859
|
}, "Working tree diff empty — diffing against baseline commit");
|
|
@@ -7732,7 +7861,7 @@ async function runCodeReview(deps, params) {
|
|
|
7732
7861
|
const commitTotal = nonDiffTokens + countTokens(commitDiff);
|
|
7733
7862
|
if (commitDiff.trim().length > 0) if (commitTotal <= TOKEN_CEILING) gitDiffContent = commitDiff;
|
|
7734
7863
|
else {
|
|
7735
|
-
logger$
|
|
7864
|
+
logger$16.warn({
|
|
7736
7865
|
estimatedTotal: commitTotal,
|
|
7737
7866
|
ceiling: TOKEN_CEILING
|
|
7738
7867
|
}, "Baseline..HEAD diff exceeds token ceiling — using stat-only summary");
|
|
@@ -7740,7 +7869,7 @@ async function runCodeReview(deps, params) {
|
|
|
7740
7869
|
}
|
|
7741
7870
|
}
|
|
7742
7871
|
if (gitDiffContent.trim().length === 0) {
|
|
7743
|
-
logger$
|
|
7872
|
+
logger$16.info({ storyKey }, "Empty git diff — skipping review with SHIP_IT");
|
|
7744
7873
|
return {
|
|
7745
7874
|
verdict: "SHIP_IT",
|
|
7746
7875
|
issues: 0,
|
|
@@ -7756,7 +7885,7 @@ async function runCodeReview(deps, params) {
|
|
|
7756
7885
|
if (deps.repoMapInjector !== void 0) {
|
|
7757
7886
|
const injection = await deps.repoMapInjector.buildContext(storyContent, deps.maxRepoMapTokens ?? 2e3);
|
|
7758
7887
|
repoContextContent = injection.text;
|
|
7759
|
-
logger$
|
|
7888
|
+
logger$16.info({
|
|
7760
7889
|
storyKey,
|
|
7761
7890
|
repoMapTokens: Math.ceil(injection.text.length / 4),
|
|
7762
7891
|
symbolCount: injection.symbolCount,
|
|
@@ -7776,16 +7905,17 @@ async function runCodeReview(deps, params) {
|
|
|
7776
7905
|
const findings = await getProjectFindings(deps.db);
|
|
7777
7906
|
if (findings.length > 0) {
|
|
7778
7907
|
priorFindingsContent = "Previous reviews found these recurring patterns — pay special attention:\n\n" + findings;
|
|
7779
|
-
logger$
|
|
7908
|
+
logger$16.debug({
|
|
7780
7909
|
storyKey,
|
|
7781
7910
|
findingsLen: findings.length
|
|
7782
7911
|
}, "Injecting prior findings into code-review prompt");
|
|
7783
7912
|
}
|
|
7784
7913
|
} catch {}
|
|
7785
7914
|
const testMetricsContent = await countTestMetrics(filesModified, cwd);
|
|
7786
|
-
if (testMetricsContent) logger$
|
|
7787
|
-
const
|
|
7788
|
-
|
|
7915
|
+
if (testMetricsContent) logger$16.debug({ storyKey }, "Injecting verified test-count metrics into code-review context");
|
|
7916
|
+
const fileDiffs = gitDiffContent ? parseDiffByFile(gitDiffContent) : void 0;
|
|
7917
|
+
const scopeAnalysisContent = storyContent && filesModified ? ScopeGuardrail.buildAnalysis(storyContent, filesModified, fileDiffs) : "";
|
|
7918
|
+
if (scopeAnalysisContent) logger$16.debug({ storyKey }, "Scope analysis detected out-of-scope files");
|
|
7789
7919
|
const buildStatusPrefix = buildPassed === true ? "BUILD STATUS: PASSED — code compiles and passes build verification. Focus on logic correctness, style, and acceptance criteria rather than compilation errors.\n\n" : "";
|
|
7790
7920
|
const sections = [
|
|
7791
7921
|
{
|
|
@@ -7830,11 +7960,11 @@ async function runCodeReview(deps, params) {
|
|
|
7830
7960
|
}
|
|
7831
7961
|
];
|
|
7832
7962
|
const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
|
|
7833
|
-
if (assembleResult.truncated) logger$
|
|
7963
|
+
if (assembleResult.truncated) logger$16.warn({
|
|
7834
7964
|
storyKey,
|
|
7835
7965
|
tokenCount: assembleResult.tokenCount
|
|
7836
7966
|
}, "Code-review prompt truncated to fit token ceiling");
|
|
7837
|
-
logger$
|
|
7967
|
+
logger$16.debug({
|
|
7838
7968
|
storyKey,
|
|
7839
7969
|
tokenCount: assembleResult.tokenCount,
|
|
7840
7970
|
truncated: assembleResult.truncated
|
|
@@ -7855,7 +7985,7 @@ async function runCodeReview(deps, params) {
|
|
|
7855
7985
|
dispatchResult = await handle.result;
|
|
7856
7986
|
} catch (err) {
|
|
7857
7987
|
const error = err instanceof Error ? err.message : String(err);
|
|
7858
|
-
logger$
|
|
7988
|
+
logger$16.error({
|
|
7859
7989
|
storyKey,
|
|
7860
7990
|
error
|
|
7861
7991
|
}, "Code-review dispatch threw unexpected error");
|
|
@@ -7871,7 +8001,7 @@ async function runCodeReview(deps, params) {
|
|
|
7871
8001
|
const rawOutput = dispatchResult.output ?? void 0;
|
|
7872
8002
|
if (dispatchResult.status === "failed") {
|
|
7873
8003
|
const errorMsg = `Dispatch status: failed. Exit code: ${dispatchResult.exitCode}. ${dispatchResult.parseError ?? ""} ${dispatchResult.output ? `Stderr: ${dispatchResult.output}` : ""}`.trim();
|
|
7874
|
-
logger$
|
|
8004
|
+
logger$16.warn({
|
|
7875
8005
|
storyKey,
|
|
7876
8006
|
exitCode: dispatchResult.exitCode
|
|
7877
8007
|
}, "Code-review dispatch failed");
|
|
@@ -7881,7 +8011,7 @@ async function runCodeReview(deps, params) {
|
|
|
7881
8011
|
};
|
|
7882
8012
|
}
|
|
7883
8013
|
if (dispatchResult.status === "timeout") {
|
|
7884
|
-
logger$
|
|
8014
|
+
logger$16.warn({ storyKey }, "Code-review dispatch timed out");
|
|
7885
8015
|
return {
|
|
7886
8016
|
...defaultFailResult("Dispatch status: timeout. The agent did not complete within the allowed time.", tokenUsage),
|
|
7887
8017
|
rawOutput
|
|
@@ -7889,7 +8019,7 @@ async function runCodeReview(deps, params) {
|
|
|
7889
8019
|
}
|
|
7890
8020
|
if (dispatchResult.parsed === null) {
|
|
7891
8021
|
const details = dispatchResult.parseError ?? "No YAML block found in output";
|
|
7892
|
-
logger$
|
|
8022
|
+
logger$16.warn({
|
|
7893
8023
|
storyKey,
|
|
7894
8024
|
details
|
|
7895
8025
|
}, "Code-review output schema validation failed");
|
|
@@ -7907,7 +8037,7 @@ async function runCodeReview(deps, params) {
|
|
|
7907
8037
|
const parseResult = CodeReviewResultSchema.safeParse(dispatchResult.parsed);
|
|
7908
8038
|
if (!parseResult.success) {
|
|
7909
8039
|
const details = parseResult.error.message;
|
|
7910
|
-
logger$
|
|
8040
|
+
logger$16.warn({
|
|
7911
8041
|
storyKey,
|
|
7912
8042
|
details
|
|
7913
8043
|
}, "Code-review output failed schema validation");
|
|
@@ -7923,13 +8053,13 @@ async function runCodeReview(deps, params) {
|
|
|
7923
8053
|
};
|
|
7924
8054
|
}
|
|
7925
8055
|
const parsed = parseResult.data;
|
|
7926
|
-
if (parsed.agentVerdict !== parsed.verdict) logger$
|
|
8056
|
+
if (parsed.agentVerdict !== parsed.verdict) logger$16.info({
|
|
7927
8057
|
storyKey,
|
|
7928
8058
|
agentVerdict: parsed.agentVerdict,
|
|
7929
8059
|
pipelineVerdict: parsed.verdict,
|
|
7930
8060
|
issues: parsed.issues
|
|
7931
8061
|
}, "Pipeline overrode agent verdict based on issue severities");
|
|
7932
|
-
logger$
|
|
8062
|
+
logger$16.info({
|
|
7933
8063
|
storyKey,
|
|
7934
8064
|
verdict: parsed.verdict,
|
|
7935
8065
|
issues: parsed.issues
|
|
@@ -7954,16 +8084,16 @@ async function getArchConstraints$2(deps) {
|
|
|
7954
8084
|
if (constraints.length === 0) return "";
|
|
7955
8085
|
return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
|
|
7956
8086
|
} catch (err) {
|
|
7957
|
-
logger$
|
|
8087
|
+
logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
|
|
7958
8088
|
return "";
|
|
7959
8089
|
}
|
|
7960
8090
|
}
|
|
7961
8091
|
|
|
7962
8092
|
//#endregion
|
|
7963
8093
|
//#region src/modules/compiled-workflows/test-plan.ts
|
|
7964
|
-
const logger$
|
|
8094
|
+
const logger$15 = createLogger("compiled-workflows:test-plan");
|
|
7965
8095
|
/** Default timeout for test-plan dispatches in milliseconds (5 min — lightweight call) */
|
|
7966
|
-
const DEFAULT_TIMEOUT_MS = 3e5;
|
|
8096
|
+
const DEFAULT_TIMEOUT_MS$1 = 3e5;
|
|
7967
8097
|
/**
|
|
7968
8098
|
* Execute the compiled test-plan workflow.
|
|
7969
8099
|
*
|
|
@@ -7973,12 +8103,12 @@ const DEFAULT_TIMEOUT_MS = 3e5;
|
|
|
7973
8103
|
*/
|
|
7974
8104
|
async function runTestPlan(deps, params) {
|
|
7975
8105
|
const { storyKey, storyFilePath, pipelineRunId } = params;
|
|
7976
|
-
logger$
|
|
8106
|
+
logger$15.info({
|
|
7977
8107
|
storyKey,
|
|
7978
8108
|
storyFilePath
|
|
7979
8109
|
}, "Starting compiled test-plan workflow");
|
|
7980
8110
|
const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("test-plan", deps.tokenCeilings);
|
|
7981
|
-
logger$
|
|
8111
|
+
logger$15.info({
|
|
7982
8112
|
workflow: "test-plan",
|
|
7983
8113
|
ceiling: TOKEN_CEILING,
|
|
7984
8114
|
source: tokenCeilingSource
|
|
@@ -7986,10 +8116,10 @@ async function runTestPlan(deps, params) {
|
|
|
7986
8116
|
let template;
|
|
7987
8117
|
try {
|
|
7988
8118
|
template = await deps.pack.getPrompt("test-plan");
|
|
7989
|
-
logger$
|
|
8119
|
+
logger$15.debug({ storyKey }, "Retrieved test-plan prompt template from pack");
|
|
7990
8120
|
} catch (err) {
|
|
7991
8121
|
const error = err instanceof Error ? err.message : String(err);
|
|
7992
|
-
logger$
|
|
8122
|
+
logger$15.warn({
|
|
7993
8123
|
storyKey,
|
|
7994
8124
|
error
|
|
7995
8125
|
}, "Failed to retrieve test-plan prompt template");
|
|
@@ -8000,14 +8130,14 @@ async function runTestPlan(deps, params) {
|
|
|
8000
8130
|
storyContent = await readFile$1(storyFilePath, "utf-8");
|
|
8001
8131
|
} catch (err) {
|
|
8002
8132
|
if (err.code === "ENOENT") {
|
|
8003
|
-
logger$
|
|
8133
|
+
logger$15.warn({
|
|
8004
8134
|
storyKey,
|
|
8005
8135
|
storyFilePath
|
|
8006
8136
|
}, "Story file not found for test planning");
|
|
8007
8137
|
return makeTestPlanFailureResult("story_file_not_found");
|
|
8008
8138
|
}
|
|
8009
8139
|
const error = err instanceof Error ? err.message : String(err);
|
|
8010
|
-
logger$
|
|
8140
|
+
logger$15.warn({
|
|
8011
8141
|
storyKey,
|
|
8012
8142
|
storyFilePath,
|
|
8013
8143
|
error
|
|
@@ -8021,13 +8151,13 @@ async function runTestPlan(deps, params) {
|
|
|
8021
8151
|
const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
|
|
8022
8152
|
if (testPatternDecisions.length > 0) {
|
|
8023
8153
|
testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
|
|
8024
|
-
logger$
|
|
8154
|
+
logger$15.debug({
|
|
8025
8155
|
storyKey,
|
|
8026
8156
|
count: testPatternDecisions.length
|
|
8027
8157
|
}, "Loaded test patterns from decision store");
|
|
8028
8158
|
} else {
|
|
8029
8159
|
testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
|
|
8030
|
-
logger$
|
|
8160
|
+
logger$15.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
|
|
8031
8161
|
}
|
|
8032
8162
|
} catch {
|
|
8033
8163
|
testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
|
|
@@ -8049,7 +8179,7 @@ async function runTestPlan(deps, params) {
|
|
|
8049
8179
|
priority: "optional"
|
|
8050
8180
|
}
|
|
8051
8181
|
], TOKEN_CEILING);
|
|
8052
|
-
logger$
|
|
8182
|
+
logger$15.info({
|
|
8053
8183
|
storyKey,
|
|
8054
8184
|
tokenCount,
|
|
8055
8185
|
ceiling: TOKEN_CEILING,
|
|
@@ -8061,7 +8191,7 @@ async function runTestPlan(deps, params) {
|
|
|
8061
8191
|
prompt,
|
|
8062
8192
|
agent: deps.agentId ?? "claude-code",
|
|
8063
8193
|
taskType: "test-plan",
|
|
8064
|
-
timeout: DEFAULT_TIMEOUT_MS,
|
|
8194
|
+
timeout: DEFAULT_TIMEOUT_MS$1,
|
|
8065
8195
|
outputSchema: TestPlanResultSchema,
|
|
8066
8196
|
...deps.projectRoot !== void 0 ? { workingDirectory: deps.projectRoot } : {},
|
|
8067
8197
|
...deps.otlpEndpoint !== void 0 ? { otlpEndpoint: deps.otlpEndpoint } : {},
|
|
@@ -8070,7 +8200,7 @@ async function runTestPlan(deps, params) {
|
|
|
8070
8200
|
dispatchResult = await handle.result;
|
|
8071
8201
|
} catch (err) {
|
|
8072
8202
|
const error = err instanceof Error ? err.message : String(err);
|
|
8073
|
-
logger$
|
|
8203
|
+
logger$15.warn({
|
|
8074
8204
|
storyKey,
|
|
8075
8205
|
error
|
|
8076
8206
|
}, "Test-plan dispatch threw an unexpected error");
|
|
@@ -8081,7 +8211,7 @@ async function runTestPlan(deps, params) {
|
|
|
8081
8211
|
output: dispatchResult.tokenEstimate.output
|
|
8082
8212
|
};
|
|
8083
8213
|
if (dispatchResult.status === "timeout") {
|
|
8084
|
-
logger$
|
|
8214
|
+
logger$15.warn({
|
|
8085
8215
|
storyKey,
|
|
8086
8216
|
durationMs: dispatchResult.durationMs
|
|
8087
8217
|
}, "Test-plan dispatch timed out");
|
|
@@ -8091,7 +8221,7 @@ async function runTestPlan(deps, params) {
|
|
|
8091
8221
|
};
|
|
8092
8222
|
}
|
|
8093
8223
|
if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
|
|
8094
|
-
logger$
|
|
8224
|
+
logger$15.warn({
|
|
8095
8225
|
storyKey,
|
|
8096
8226
|
exitCode: dispatchResult.exitCode,
|
|
8097
8227
|
status: dispatchResult.status
|
|
@@ -8103,7 +8233,7 @@ async function runTestPlan(deps, params) {
|
|
|
8103
8233
|
}
|
|
8104
8234
|
if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
|
|
8105
8235
|
const details = dispatchResult.parseError ?? "parsed result was null";
|
|
8106
|
-
logger$
|
|
8236
|
+
logger$15.warn({
|
|
8107
8237
|
storyKey,
|
|
8108
8238
|
parseError: details
|
|
8109
8239
|
}, "Test-plan YAML schema validation failed");
|
|
@@ -8126,19 +8256,19 @@ async function runTestPlan(deps, params) {
|
|
|
8126
8256
|
}),
|
|
8127
8257
|
rationale: `Test plan for ${storyKey}: ${parsed.test_files.length} test files, categories: ${parsed.test_categories.join(", ")}`
|
|
8128
8258
|
});
|
|
8129
|
-
logger$
|
|
8259
|
+
logger$15.info({
|
|
8130
8260
|
storyKey,
|
|
8131
8261
|
fileCount: parsed.test_files.length,
|
|
8132
8262
|
categories: parsed.test_categories
|
|
8133
8263
|
}, "Test plan stored in decision store");
|
|
8134
8264
|
} catch (err) {
|
|
8135
8265
|
const error = err instanceof Error ? err.message : String(err);
|
|
8136
|
-
logger$
|
|
8266
|
+
logger$15.warn({
|
|
8137
8267
|
storyKey,
|
|
8138
8268
|
error
|
|
8139
8269
|
}, "Failed to store test plan in decision store — proceeding anyway");
|
|
8140
8270
|
}
|
|
8141
|
-
logger$
|
|
8271
|
+
logger$15.info({
|
|
8142
8272
|
storyKey,
|
|
8143
8273
|
result: parsed.result
|
|
8144
8274
|
}, "Test-plan workflow completed");
|
|
@@ -8178,14 +8308,312 @@ async function getArchConstraints$1(deps) {
|
|
|
8178
8308
|
if (constraints.length === 0) return "";
|
|
8179
8309
|
return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
|
|
8180
8310
|
} catch (err) {
|
|
8181
|
-
logger$
|
|
8311
|
+
logger$15.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints for test-plan — proceeding without them");
|
|
8182
8312
|
return "";
|
|
8183
8313
|
}
|
|
8184
8314
|
}
|
|
8185
8315
|
|
|
8316
|
+
//#endregion
|
|
8317
|
+
//#region src/modules/implementation-orchestrator/probe-author-integration.ts
|
|
8318
|
+
const logger$14 = createLogger("implementation-orchestrator:probe-author");
|
|
8319
|
+
/** Default timeout for probe-author dispatches (5 min) */
|
|
8320
|
+
const DEFAULT_TIMEOUT_MS = 3e5;
|
|
8321
|
+
/** Timeout multiplier for the single retry after a timeout failure */
|
|
8322
|
+
const TIMEOUT_RETRY_MULTIPLIER = 1.5;
|
|
8323
|
+
/**
|
|
8324
|
+
* Execute the probe-author integration phase.
|
|
8325
|
+
*
|
|
8326
|
+
* Gate 1 — Event-driven AC check: calls `detectsEventDrivenAC(epicContent)`.
|
|
8327
|
+
* Skip if the source AC does not describe a hook, timer, signal, or webhook.
|
|
8328
|
+
*
|
|
8329
|
+
* Gate 2 — Idempotency check: reads storyFilePath and checks for an existing
|
|
8330
|
+
* `## Runtime Probes` section. Skip if present (probes already authored).
|
|
8331
|
+
*
|
|
8332
|
+
* Dispatch: assembles the probe-author prompt and dispatches via WorkflowDeps.
|
|
8333
|
+
* Uses ProbeAuthorResultSchema (result + probes) as outputSchema so the
|
|
8334
|
+
* existing YAML-parser anchor-key detection works correctly.
|
|
8335
|
+
*
|
|
8336
|
+
* Retry policy:
|
|
8337
|
+
* - Timeout → single retry at TIMEOUT_RETRY_MULTIPLIER × DEFAULT_TIMEOUT_MS.
|
|
8338
|
+
* Fall through if second attempt also times out.
|
|
8339
|
+
* - Invalid YAML → single retry with augmented prompt that includes the parse
|
|
8340
|
+
* error and the first 500 chars of bad output. Fall through if retry fails.
|
|
8341
|
+
* - Dispatch error (process crash, network failure) → fall through immediately.
|
|
8342
|
+
* - Empty probes list → emit `probe-author:no-probes-authored`, fall through.
|
|
8343
|
+
*
|
|
8344
|
+
* All failure paths are non-fatal — returns result: 'failed' instead of
|
|
8345
|
+
* throwing so the caller can unconditionally fall through to dev-story.
|
|
8346
|
+
*/
|
|
8347
|
+
async function runProbeAuthor(deps, params) {
|
|
8348
|
+
const start = Date.now();
|
|
8349
|
+
const { storyKey, storyFilePath, pipelineRunId, sourceAcContent, epicContent, emitEvent } = params;
|
|
8350
|
+
const tokenUsage = {
|
|
8351
|
+
input: 0,
|
|
8352
|
+
output: 0
|
|
8353
|
+
};
|
|
8354
|
+
if (!detectsEventDrivenAC(epicContent)) {
|
|
8355
|
+
logger$14.debug({ storyKey }, "probe-author: source AC not event-driven — skipping");
|
|
8356
|
+
return makeSkippedResult(tokenUsage, start);
|
|
8357
|
+
}
|
|
8358
|
+
let storyContent;
|
|
8359
|
+
try {
|
|
8360
|
+
storyContent = await readFile$1(storyFilePath, "utf-8");
|
|
8361
|
+
if (/^## Runtime Probes/m.test(storyContent)) {
|
|
8362
|
+
logger$14.info({ storyKey }, "probe-author: story artifact already has ## Runtime Probes — skipping");
|
|
8363
|
+
return makeSkippedResult(tokenUsage, start);
|
|
8364
|
+
}
|
|
8365
|
+
} catch (err) {
|
|
8366
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
8367
|
+
logger$14.warn({
|
|
8368
|
+
storyKey,
|
|
8369
|
+
error
|
|
8370
|
+
}, "probe-author: failed to read story file — falling through");
|
|
8371
|
+
emitEvent?.("probe-author:dispatch-error", {
|
|
8372
|
+
storyKey,
|
|
8373
|
+
runId: pipelineRunId,
|
|
8374
|
+
error
|
|
8375
|
+
});
|
|
8376
|
+
return makeFailedResult(`story_file_read_error: ${error}`, tokenUsage, start);
|
|
8377
|
+
}
|
|
8378
|
+
const { ceiling: TOKEN_CEILING } = getTokenCeiling("probe-author", deps.tokenCeilings);
|
|
8379
|
+
let template;
|
|
8380
|
+
try {
|
|
8381
|
+
template = await deps.pack.getPrompt("probe-author");
|
|
8382
|
+
logger$14.debug({ storyKey }, "probe-author: retrieved prompt template");
|
|
8383
|
+
} catch (err) {
|
|
8384
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
8385
|
+
logger$14.warn({
|
|
8386
|
+
storyKey,
|
|
8387
|
+
error
|
|
8388
|
+
}, "probe-author: failed to get prompt template — falling through");
|
|
8389
|
+
emitEvent?.("probe-author:dispatch-error", {
|
|
8390
|
+
storyKey,
|
|
8391
|
+
runId: pipelineRunId,
|
|
8392
|
+
error
|
|
8393
|
+
});
|
|
8394
|
+
return makeFailedResult(`template_load_failed: ${error}`, tokenUsage, start);
|
|
8395
|
+
}
|
|
8396
|
+
const { prompt: basePrompt } = assemblePrompt(template, [{
|
|
8397
|
+
name: "rendered_ac_section",
|
|
8398
|
+
content: storyContent,
|
|
8399
|
+
priority: "required"
|
|
8400
|
+
}, {
|
|
8401
|
+
name: "source_epic_ac_section",
|
|
8402
|
+
content: sourceAcContent,
|
|
8403
|
+
priority: "required"
|
|
8404
|
+
}], TOKEN_CEILING);
|
|
8405
|
+
const doDispatch = async (promptText, timeoutMs) => {
|
|
8406
|
+
const handle = deps.dispatcher.dispatch({
|
|
8407
|
+
prompt: promptText,
|
|
8408
|
+
agent: deps.agentId ?? "claude-code",
|
|
8409
|
+
taskType: "probe-author",
|
|
8410
|
+
timeout: timeoutMs,
|
|
8411
|
+
outputSchema: ProbeAuthorResultSchema,
|
|
8412
|
+
...deps.projectRoot !== void 0 ? { workingDirectory: deps.projectRoot } : {},
|
|
8413
|
+
...deps.otlpEndpoint !== void 0 ? { otlpEndpoint: deps.otlpEndpoint } : {},
|
|
8414
|
+
storyKey
|
|
8415
|
+
});
|
|
8416
|
+
return await handle.result;
|
|
8417
|
+
};
|
|
8418
|
+
let dispatchResult;
|
|
8419
|
+
try {
|
|
8420
|
+
logger$14.info({ storyKey }, "probe-author: dispatching probe-author agent");
|
|
8421
|
+
dispatchResult = await doDispatch(basePrompt, DEFAULT_TIMEOUT_MS);
|
|
8422
|
+
tokenUsage.input += dispatchResult.tokenEstimate.input;
|
|
8423
|
+
tokenUsage.output += dispatchResult.tokenEstimate.output;
|
|
8424
|
+
} catch (err) {
|
|
8425
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
8426
|
+
logger$14.warn({
|
|
8427
|
+
storyKey,
|
|
8428
|
+
error
|
|
8429
|
+
}, "probe-author: dispatch error — falling through to dev-story");
|
|
8430
|
+
emitEvent?.("probe-author:dispatch-error", {
|
|
8431
|
+
storyKey,
|
|
8432
|
+
runId: pipelineRunId,
|
|
8433
|
+
error
|
|
8434
|
+
});
|
|
8435
|
+
return makeFailedResult(`dispatch_error: ${error}`, tokenUsage, start);
|
|
8436
|
+
}
|
|
8437
|
+
if (dispatchResult.status === "timeout") {
|
|
8438
|
+
const elapsedMs = Date.now() - start;
|
|
8439
|
+
logger$14.warn({
|
|
8440
|
+
storyKey,
|
|
8441
|
+
elapsedMs
|
|
8442
|
+
}, "probe-author: dispatch timed out — retrying with 1.5× timeout");
|
|
8443
|
+
emitEvent?.("probe-author:timeout", {
|
|
8444
|
+
storyKey,
|
|
8445
|
+
runId: pipelineRunId,
|
|
8446
|
+
elapsedMs
|
|
8447
|
+
});
|
|
8448
|
+
try {
|
|
8449
|
+
const retryResult = await doDispatch(basePrompt, Math.round(DEFAULT_TIMEOUT_MS * TIMEOUT_RETRY_MULTIPLIER));
|
|
8450
|
+
tokenUsage.input += retryResult.tokenEstimate.input;
|
|
8451
|
+
tokenUsage.output += retryResult.tokenEstimate.output;
|
|
8452
|
+
if (retryResult.status === "timeout") {
|
|
8453
|
+
logger$14.warn({ storyKey }, "probe-author: retry also timed out — falling through to dev-story");
|
|
8454
|
+
return makeFailedResult("dispatch_timeout", tokenUsage, start);
|
|
8455
|
+
}
|
|
8456
|
+
dispatchResult = retryResult;
|
|
8457
|
+
} catch (retryErr) {
|
|
8458
|
+
const error = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
|
8459
|
+
logger$14.warn({
|
|
8460
|
+
storyKey,
|
|
8461
|
+
error
|
|
8462
|
+
}, "probe-author: retry dispatch error — falling through to dev-story");
|
|
8463
|
+
return makeFailedResult(`retry_dispatch_error: ${error}`, tokenUsage, start);
|
|
8464
|
+
}
|
|
8465
|
+
}
|
|
8466
|
+
if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
|
|
8467
|
+
const error = `dispatch_failed with exit_code=${dispatchResult.exitCode}`;
|
|
8468
|
+
logger$14.warn({ storyKey }, `probe-author: ${error} — falling through to dev-story`);
|
|
8469
|
+
emitEvent?.("probe-author:dispatch-error", {
|
|
8470
|
+
storyKey,
|
|
8471
|
+
runId: pipelineRunId,
|
|
8472
|
+
error
|
|
8473
|
+
});
|
|
8474
|
+
return makeFailedResult(error, tokenUsage, start);
|
|
8475
|
+
}
|
|
8476
|
+
if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
|
|
8477
|
+
const parseError = dispatchResult.parseError ?? "parsed result was null";
|
|
8478
|
+
const rawOutputSnippet = dispatchResult.output.slice(0, 500);
|
|
8479
|
+
logger$14.warn({
|
|
8480
|
+
storyKey,
|
|
8481
|
+
parseError,
|
|
8482
|
+
rawOutputSnippet
|
|
8483
|
+
}, "probe-author: YAML parse failure — retrying with augmented prompt");
|
|
8484
|
+
emitEvent?.("probe-author:invalid-output", {
|
|
8485
|
+
storyKey,
|
|
8486
|
+
runId: pipelineRunId,
|
|
8487
|
+
parseError,
|
|
8488
|
+
rawOutputSnippet
|
|
8489
|
+
});
|
|
8490
|
+
const augmentedPrompt = `${basePrompt}\n\n---\n\nPrevious output failed parsing with: ${parseError}; produce a single yaml block conforming to RuntimeProbeListSchema`;
|
|
8491
|
+
try {
|
|
8492
|
+
const retryResult = await doDispatch(augmentedPrompt, DEFAULT_TIMEOUT_MS);
|
|
8493
|
+
tokenUsage.input += retryResult.tokenEstimate.input;
|
|
8494
|
+
tokenUsage.output += retryResult.tokenEstimate.output;
|
|
8495
|
+
if (retryResult.parseError !== null || retryResult.parsed === null) {
|
|
8496
|
+
logger$14.warn({ storyKey }, "probe-author: retry still produced invalid YAML — falling through");
|
|
8497
|
+
return makeFailedResult("invalid_yaml_after_retry", tokenUsage, start);
|
|
8498
|
+
}
|
|
8499
|
+
dispatchResult = retryResult;
|
|
8500
|
+
} catch (retryErr) {
|
|
8501
|
+
const error = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
|
8502
|
+
logger$14.warn({
|
|
8503
|
+
storyKey,
|
|
8504
|
+
error
|
|
8505
|
+
}, "probe-author: retry error after invalid YAML — falling through");
|
|
8506
|
+
return makeFailedResult(`retry_error_after_invalid_yaml: ${error}`, tokenUsage, start);
|
|
8507
|
+
}
|
|
8508
|
+
}
|
|
8509
|
+
const parsedOutput = dispatchResult.parsed;
|
|
8510
|
+
const probeValidation = RuntimeProbeListSchema.safeParse(parsedOutput.probes);
|
|
8511
|
+
if (!probeValidation.success) {
|
|
8512
|
+
const validationError = probeValidation.error.message;
|
|
8513
|
+
logger$14.warn({
|
|
8514
|
+
storyKey,
|
|
8515
|
+
validationError
|
|
8516
|
+
}, "probe-author: probes failed RuntimeProbeListSchema — falling through");
|
|
8517
|
+
return makeFailedResult(`schema_validation_failed: ${validationError}`, tokenUsage, start);
|
|
8518
|
+
}
|
|
8519
|
+
const probes = probeValidation.data;
|
|
8520
|
+
if (probes.length === 0) {
|
|
8521
|
+
logger$14.info({ storyKey }, "probe-author: authored empty probes list — no probes needed");
|
|
8522
|
+
emitEvent?.("probe-author:no-probes-authored", {
|
|
8523
|
+
storyKey,
|
|
8524
|
+
runId: pipelineRunId
|
|
8525
|
+
});
|
|
8526
|
+
return {
|
|
8527
|
+
result: "success",
|
|
8528
|
+
probesAuthoredCount: 0,
|
|
8529
|
+
tokenUsage,
|
|
8530
|
+
durationMs: Date.now() - start
|
|
8531
|
+
};
|
|
8532
|
+
}
|
|
8533
|
+
try {
|
|
8534
|
+
const refreshedContent = await readFile$1(storyFilePath, "utf-8");
|
|
8535
|
+
if (/^## Runtime Probes/m.test(refreshedContent)) {
|
|
8536
|
+
logger$14.info({ storyKey }, "probe-author: ## Runtime Probes section appeared after dispatch — skipping append (idempotent)");
|
|
8537
|
+
const dispatchDurationMs$1 = Date.now() - start;
|
|
8538
|
+
emitEvent?.("probe-author:dispatched", {
|
|
8539
|
+
storyKey,
|
|
8540
|
+
runId: pipelineRunId,
|
|
8541
|
+
probesAuthoredCount: 0,
|
|
8542
|
+
dispatchDurationMs: dispatchDurationMs$1,
|
|
8543
|
+
costUsd: estimateDispatchCost$1(tokenUsage.input, tokenUsage.output)
|
|
8544
|
+
});
|
|
8545
|
+
return makeSkippedResult(tokenUsage, start);
|
|
8546
|
+
}
|
|
8547
|
+
const probesYaml = yaml.dump(probes, { lineWidth: 120 }).trimEnd();
|
|
8548
|
+
const probesSection = `\n## Runtime Probes\n\n\`\`\`yaml\n${probesYaml}\n\`\`\`\n`;
|
|
8549
|
+
const newContent = refreshedContent + probesSection;
|
|
8550
|
+
const targetDir = dirname$1(storyFilePath);
|
|
8551
|
+
const tmpPath = join$1(targetDir, `.probe-author-${Date.now()}.tmp.md`);
|
|
8552
|
+
await writeFile$1(tmpPath, newContent, "utf-8");
|
|
8553
|
+
await rename(tmpPath, storyFilePath);
|
|
8554
|
+
logger$14.info({
|
|
8555
|
+
storyKey,
|
|
8556
|
+
probesCount: probes.length
|
|
8557
|
+
}, "probe-author: appended ## Runtime Probes section");
|
|
8558
|
+
} catch (err) {
|
|
8559
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
8560
|
+
logger$14.warn({
|
|
8561
|
+
storyKey,
|
|
8562
|
+
error
|
|
8563
|
+
}, "probe-author: failed to append probes — falling through to dev-story");
|
|
8564
|
+
return {
|
|
8565
|
+
result: "failed",
|
|
8566
|
+
probesAuthoredCount: probes.length,
|
|
8567
|
+
error: `append_error: ${error}`,
|
|
8568
|
+
tokenUsage,
|
|
8569
|
+
durationMs: Date.now() - start
|
|
8570
|
+
};
|
|
8571
|
+
}
|
|
8572
|
+
const dispatchDurationMs = Date.now() - start;
|
|
8573
|
+
const costUsd = estimateDispatchCost$1(tokenUsage.input, tokenUsage.output);
|
|
8574
|
+
emitEvent?.("probe-author:dispatched", {
|
|
8575
|
+
storyKey,
|
|
8576
|
+
runId: pipelineRunId,
|
|
8577
|
+
probesAuthoredCount: probes.length,
|
|
8578
|
+
dispatchDurationMs,
|
|
8579
|
+
costUsd
|
|
8580
|
+
});
|
|
8581
|
+
logger$14.info({
|
|
8582
|
+
storyKey,
|
|
8583
|
+
probesAuthoredCount: probes.length
|
|
8584
|
+
}, "probe-author: phase complete");
|
|
8585
|
+
return {
|
|
8586
|
+
result: "success",
|
|
8587
|
+
probesAuthoredCount: probes.length,
|
|
8588
|
+
tokenUsage,
|
|
8589
|
+
durationMs: Date.now() - start
|
|
8590
|
+
};
|
|
8591
|
+
}
|
|
8592
|
+
function makeSkippedResult(tokenUsage, start) {
|
|
8593
|
+
return {
|
|
8594
|
+
result: "skipped",
|
|
8595
|
+
probesAuthoredCount: 0,
|
|
8596
|
+
tokenUsage,
|
|
8597
|
+
durationMs: Date.now() - start
|
|
8598
|
+
};
|
|
8599
|
+
}
|
|
8600
|
+
function makeFailedResult(error, tokenUsage, start) {
|
|
8601
|
+
return {
|
|
8602
|
+
result: "failed",
|
|
8603
|
+
probesAuthoredCount: 0,
|
|
8604
|
+
error,
|
|
8605
|
+
tokenUsage,
|
|
8606
|
+
durationMs: Date.now() - start
|
|
8607
|
+
};
|
|
8608
|
+
}
|
|
8609
|
+
/** Claude pricing: $3/1M input, $15/1M output */
|
|
8610
|
+
function estimateDispatchCost$1(input, output) {
|
|
8611
|
+
return (input * 3 + output * 15) / 1e6;
|
|
8612
|
+
}
|
|
8613
|
+
|
|
8186
8614
|
//#endregion
|
|
8187
8615
|
//#region src/modules/compiled-workflows/test-expansion.ts
|
|
8188
|
-
const logger$
|
|
8616
|
+
const logger$13 = createLogger("compiled-workflows:test-expansion");
|
|
8189
8617
|
function defaultFallbackResult(error, tokenUsage) {
|
|
8190
8618
|
return {
|
|
8191
8619
|
expansion_priority: "low",
|
|
@@ -8215,14 +8643,14 @@ function defaultFallbackResult(error, tokenUsage) {
|
|
|
8215
8643
|
async function runTestExpansion(deps, params) {
|
|
8216
8644
|
const { storyKey, storyFilePath, pipelineRunId, filesModified, workingDirectory } = params;
|
|
8217
8645
|
const cwd = workingDirectory ?? process.cwd();
|
|
8218
|
-
logger$
|
|
8646
|
+
logger$13.debug({
|
|
8219
8647
|
storyKey,
|
|
8220
8648
|
storyFilePath,
|
|
8221
8649
|
cwd,
|
|
8222
8650
|
pipelineRunId
|
|
8223
8651
|
}, "Starting test-expansion workflow");
|
|
8224
8652
|
const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("test-expansion", deps.tokenCeilings);
|
|
8225
|
-
logger$
|
|
8653
|
+
logger$13.info({
|
|
8226
8654
|
workflow: "test-expansion",
|
|
8227
8655
|
ceiling: TOKEN_CEILING,
|
|
8228
8656
|
source: tokenCeilingSource
|
|
@@ -8232,7 +8660,7 @@ async function runTestExpansion(deps, params) {
|
|
|
8232
8660
|
template = await deps.pack.getPrompt("test-expansion");
|
|
8233
8661
|
} catch (err) {
|
|
8234
8662
|
const error = err instanceof Error ? err.message : String(err);
|
|
8235
|
-
logger$
|
|
8663
|
+
logger$13.warn({ error }, "Failed to retrieve test-expansion prompt template");
|
|
8236
8664
|
return defaultFallbackResult(`Failed to retrieve prompt template: ${error}`, {
|
|
8237
8665
|
input: 0,
|
|
8238
8666
|
output: 0
|
|
@@ -8243,7 +8671,7 @@ async function runTestExpansion(deps, params) {
|
|
|
8243
8671
|
storyContent = await readFile$1(storyFilePath, "utf-8");
|
|
8244
8672
|
} catch (err) {
|
|
8245
8673
|
const error = err instanceof Error ? err.message : String(err);
|
|
8246
|
-
logger$
|
|
8674
|
+
logger$13.warn({
|
|
8247
8675
|
storyFilePath,
|
|
8248
8676
|
error
|
|
8249
8677
|
}, "Failed to read story file");
|
|
@@ -8259,13 +8687,13 @@ async function runTestExpansion(deps, params) {
|
|
|
8259
8687
|
const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
|
|
8260
8688
|
if (testPatternDecisions.length > 0) {
|
|
8261
8689
|
testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
|
|
8262
|
-
logger$
|
|
8690
|
+
logger$13.debug({
|
|
8263
8691
|
storyKey,
|
|
8264
8692
|
count: testPatternDecisions.length
|
|
8265
8693
|
}, "Loaded test patterns from decision store");
|
|
8266
8694
|
} else {
|
|
8267
8695
|
testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
|
|
8268
|
-
logger$
|
|
8696
|
+
logger$13.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
|
|
8269
8697
|
}
|
|
8270
8698
|
} catch {
|
|
8271
8699
|
testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
|
|
@@ -8280,12 +8708,12 @@ async function runTestExpansion(deps, params) {
|
|
|
8280
8708
|
const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
|
|
8281
8709
|
if (scopedTotal <= TOKEN_CEILING) {
|
|
8282
8710
|
gitDiffContent = scopedDiff;
|
|
8283
|
-
logger$
|
|
8711
|
+
logger$13.debug({
|
|
8284
8712
|
fileCount: filesModified.length,
|
|
8285
8713
|
tokenCount: scopedTotal
|
|
8286
8714
|
}, "Using scoped file diff");
|
|
8287
8715
|
} else {
|
|
8288
|
-
logger$
|
|
8716
|
+
logger$13.warn({
|
|
8289
8717
|
estimatedTotal: scopedTotal,
|
|
8290
8718
|
ceiling: TOKEN_CEILING,
|
|
8291
8719
|
fileCount: filesModified.length
|
|
@@ -8293,7 +8721,7 @@ async function runTestExpansion(deps, params) {
|
|
|
8293
8721
|
gitDiffContent = await getGitDiffStatForFiles(filesModified, cwd);
|
|
8294
8722
|
}
|
|
8295
8723
|
} catch (err) {
|
|
8296
|
-
logger$
|
|
8724
|
+
logger$13.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to get git diff — proceeding with empty diff");
|
|
8297
8725
|
}
|
|
8298
8726
|
const sections = [
|
|
8299
8727
|
{
|
|
@@ -8318,11 +8746,11 @@ async function runTestExpansion(deps, params) {
|
|
|
8318
8746
|
}
|
|
8319
8747
|
];
|
|
8320
8748
|
const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
|
|
8321
|
-
if (assembleResult.truncated) logger$
|
|
8749
|
+
if (assembleResult.truncated) logger$13.warn({
|
|
8322
8750
|
storyKey,
|
|
8323
8751
|
tokenCount: assembleResult.tokenCount
|
|
8324
8752
|
}, "Test-expansion prompt truncated to fit token ceiling");
|
|
8325
|
-
logger$
|
|
8753
|
+
logger$13.debug({
|
|
8326
8754
|
storyKey,
|
|
8327
8755
|
tokenCount: assembleResult.tokenCount,
|
|
8328
8756
|
truncated: assembleResult.truncated
|
|
@@ -8342,7 +8770,7 @@ async function runTestExpansion(deps, params) {
|
|
|
8342
8770
|
dispatchResult = await handle.result;
|
|
8343
8771
|
} catch (err) {
|
|
8344
8772
|
const error = err instanceof Error ? err.message : String(err);
|
|
8345
|
-
logger$
|
|
8773
|
+
logger$13.warn({
|
|
8346
8774
|
storyKey,
|
|
8347
8775
|
error
|
|
8348
8776
|
}, "Test-expansion dispatch threw unexpected error");
|
|
@@ -8357,19 +8785,19 @@ async function runTestExpansion(deps, params) {
|
|
|
8357
8785
|
};
|
|
8358
8786
|
if (dispatchResult.status === "failed") {
|
|
8359
8787
|
const errorMsg = `Dispatch status: failed. Exit code: ${dispatchResult.exitCode}. ${dispatchResult.parseError ?? ""}`.trim();
|
|
8360
|
-
logger$
|
|
8788
|
+
logger$13.warn({
|
|
8361
8789
|
storyKey,
|
|
8362
8790
|
exitCode: dispatchResult.exitCode
|
|
8363
8791
|
}, "Test-expansion dispatch failed");
|
|
8364
8792
|
return defaultFallbackResult(errorMsg, tokenUsage);
|
|
8365
8793
|
}
|
|
8366
8794
|
if (dispatchResult.status === "timeout") {
|
|
8367
|
-
logger$
|
|
8795
|
+
logger$13.warn({ storyKey }, "Test-expansion dispatch timed out");
|
|
8368
8796
|
return defaultFallbackResult("Dispatch status: timeout. The agent did not complete within the allowed time.", tokenUsage);
|
|
8369
8797
|
}
|
|
8370
8798
|
if (dispatchResult.parsed === null) {
|
|
8371
8799
|
const details = dispatchResult.parseError ?? "No YAML block found in output";
|
|
8372
|
-
logger$
|
|
8800
|
+
logger$13.warn({
|
|
8373
8801
|
storyKey,
|
|
8374
8802
|
details
|
|
8375
8803
|
}, "Test-expansion output has no parseable YAML");
|
|
@@ -8378,14 +8806,14 @@ async function runTestExpansion(deps, params) {
|
|
|
8378
8806
|
const parseResult = TestExpansionResultSchema.safeParse(dispatchResult.parsed);
|
|
8379
8807
|
if (!parseResult.success) {
|
|
8380
8808
|
const details = parseResult.error.message;
|
|
8381
|
-
logger$
|
|
8809
|
+
logger$13.warn({
|
|
8382
8810
|
storyKey,
|
|
8383
8811
|
details
|
|
8384
8812
|
}, "Test-expansion output failed schema validation");
|
|
8385
8813
|
return defaultFallbackResult(`schema_validation_failed: ${details}`, tokenUsage);
|
|
8386
8814
|
}
|
|
8387
8815
|
const parsed = parseResult.data;
|
|
8388
|
-
logger$
|
|
8816
|
+
logger$13.info({
|
|
8389
8817
|
storyKey,
|
|
8390
8818
|
expansion_priority: parsed.expansion_priority,
|
|
8391
8819
|
coverage_gaps: parsed.coverage_gaps.length,
|
|
@@ -8410,11 +8838,15 @@ async function getArchConstraints(deps) {
|
|
|
8410
8838
|
if (constraints.length === 0) return "";
|
|
8411
8839
|
return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
|
|
8412
8840
|
} catch (err) {
|
|
8413
|
-
logger$
|
|
8841
|
+
logger$13.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
|
|
8414
8842
|
return "";
|
|
8415
8843
|
}
|
|
8416
8844
|
}
|
|
8417
8845
|
|
|
8846
|
+
//#endregion
|
|
8847
|
+
//#region src/modules/compiled-workflows/probe-author.ts
|
|
8848
|
+
const logger$12 = createLogger("compiled-workflows:probe-author");
|
|
8849
|
+
|
|
8418
8850
|
//#endregion
|
|
8419
8851
|
//#region src/modules/compiled-workflows/story-analyzer.ts
|
|
8420
8852
|
/**
|
|
@@ -11103,6 +11535,76 @@ function findEpicFiles(projectRoot) {
|
|
|
11103
11535
|
}
|
|
11104
11536
|
}
|
|
11105
11537
|
/**
|
|
11538
|
+
* Story 61-3: find the epic file relevant to a specific story.
|
|
11539
|
+
*
|
|
11540
|
+
* Sibling to `findEpicsFile` for the verification path
|
|
11541
|
+
* (`assembleVerificationContext` populates `sourceEpicContent` from this).
|
|
11542
|
+
* `findEpicsFile` only checks the consolidated convention (`epics.md`)
|
|
11543
|
+
* → returns undefined for projects using per-epic files (substrate's own
|
|
11544
|
+
* planning artifacts), causing `SourceAcFidelityCheck` to silently skip
|
|
11545
|
+
* with a `source-ac-source-unavailable` warn — exactly what happened on
|
|
11546
|
+
* the 60-12 redispatch (run 4700c6e8, 2026-04-27).
|
|
11547
|
+
*
|
|
11548
|
+
* Story 61-3 v2 (post-round-3): the v1 implementation honored
|
|
11549
|
+
* findEpicsFile's returned path without verifying it contained the
|
|
11550
|
+
* requested story. substrate's own findEpicsFile glob-matches
|
|
11551
|
+
* `epics-and-stories-*.md` files, so for projects with stale consolidated
|
|
11552
|
+
* files (substrate has `epics-and-stories-software-factory.md` for old
|
|
11553
|
+
* epics 40-50) the function returned that path → caller's
|
|
11554
|
+
* extractStorySection found nothing for new stories → sourceEpicContent
|
|
11555
|
+
* stayed undefined. This rev verifies file contains the story (via the
|
|
11556
|
+
* SAME `### Story X:` heading match the caller uses) before returning,
|
|
11557
|
+
* and falls through to per-epic search if not.
|
|
11558
|
+
*
|
|
11559
|
+
* Lookup order:
|
|
11560
|
+
* 1. Consolidated epics.md (existing findEpicsFile path) — return ONLY
|
|
11561
|
+
* if file content contains a `### Story <storyKey>:` heading.
|
|
11562
|
+
* 2. Per-epic file `epic-<epicNum>-*.md` derived from storyKey's first
|
|
11563
|
+
* numeric segment (e.g. storyKey '60-12' → epicNum '60' →
|
|
11564
|
+
* `epic-60-*.md`). Per-epic files contain the entire epic so a
|
|
11565
|
+
* filename match is sufficient (no content verification needed —
|
|
11566
|
+
* mirrors readEpicShardFromFile in create-story.ts).
|
|
11567
|
+
*
|
|
11568
|
+
* Returns the matched path, or undefined if no file contains the story.
|
|
11569
|
+
*/
|
|
11570
|
+
function findEpicFileForStory(projectRoot, storyKey) {
|
|
11571
|
+
const consolidated = findEpicsFile(projectRoot);
|
|
11572
|
+
if (consolidated !== void 0) {
|
|
11573
|
+
if (fileContainsStory(consolidated, storyKey)) return consolidated;
|
|
11574
|
+
}
|
|
11575
|
+
const epicNumMatch = /^(\d+)/.exec(storyKey);
|
|
11576
|
+
if (!epicNumMatch) return void 0;
|
|
11577
|
+
const epicNum = epicNumMatch[1];
|
|
11578
|
+
const planningDir = join$1(projectRoot, "_bmad-output", "planning-artifacts");
|
|
11579
|
+
if (!existsSync(planningDir)) return void 0;
|
|
11580
|
+
try {
|
|
11581
|
+
const entries = readdirSync(planningDir, { encoding: "utf-8" });
|
|
11582
|
+
const perEpicPattern = new RegExp(`^epic-${epicNum}-.*\\.md$`);
|
|
11583
|
+
const matches = entries.filter((e) => perEpicPattern.test(e)).sort();
|
|
11584
|
+
if (matches.length > 0) return join$1(planningDir, matches[0]);
|
|
11585
|
+
} catch {}
|
|
11586
|
+
return void 0;
|
|
11587
|
+
}
|
|
11588
|
+
/**
|
|
11589
|
+
* Story 61-3 v2: check whether a file's content contains a story heading
|
|
11590
|
+
* matching the storyKey, with the same separator tolerance as
|
|
11591
|
+
* `extractStorySection` (Story 60-6) so both call sites agree.
|
|
11592
|
+
*
|
|
11593
|
+
* Cheap to call (one synchronous read, one regex test); gracefully
|
|
11594
|
+
* returns false on any I/O error.
|
|
11595
|
+
*/
|
|
11596
|
+
function fileContainsStory(filePath, storyKey) {
|
|
11597
|
+
try {
|
|
11598
|
+
const content = readFileSync(filePath, "utf-8");
|
|
11599
|
+
const parts = storyKey.split(/[-._ ]/);
|
|
11600
|
+
const normalized = parts.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("[-._ ]");
|
|
11601
|
+
const headingPattern = new RegExp(`^###\\s+Story\\s+${normalized}[:\\s]`, "m");
|
|
11602
|
+
return headingPattern.test(content);
|
|
11603
|
+
} catch {
|
|
11604
|
+
return false;
|
|
11605
|
+
}
|
|
11606
|
+
}
|
|
11607
|
+
/**
|
|
11106
11608
|
* Collect story keys that already have implementation artifact files.
|
|
11107
11609
|
* Scans _bmad-output/implementation-artifacts/ for files matching N-M-*.md.
|
|
11108
11610
|
*/
|
|
@@ -11637,7 +12139,7 @@ function checkProfileStaleness(projectRoot) {
|
|
|
11637
12139
|
*/
|
|
11638
12140
|
function createImplementationOrchestrator(deps) {
|
|
11639
12141
|
const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot, tokenCeilings, stateStore, telemetryPersistence, ingestionServer, repoMapInjector, maxRepoMapTokens, agentId, runManifest = null } = deps;
|
|
11640
|
-
const logger$
|
|
12142
|
+
const logger$26 = createLogger("implementation-orchestrator");
|
|
11641
12143
|
const telemetryAdvisor = db !== void 0 ? createTelemetryAdvisor({ db }) : void 0;
|
|
11642
12144
|
const wgRepo = new WorkGraphRepository(db);
|
|
11643
12145
|
const _wgInProgressWritten = new Set();
|
|
@@ -11720,7 +12222,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
11720
12222
|
const existingCount = storyState?.retry_count ?? 0;
|
|
11721
12223
|
_storyRetryCount.set(storyKey, existingCount);
|
|
11722
12224
|
} catch (err) {
|
|
11723
|
-
logger$
|
|
12225
|
+
logger$26.warn({
|
|
11724
12226
|
err,
|
|
11725
12227
|
storyKey
|
|
11726
12228
|
}, "initRetryCount: failed to read manifest — starting at 0");
|
|
@@ -11734,7 +12236,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
11734
12236
|
const current = _storyRetryCount.get(storyKey) ?? 0;
|
|
11735
12237
|
const next = current + 1;
|
|
11736
12238
|
_storyRetryCount.set(storyKey, next);
|
|
11737
|
-
if (runManifest !== null && runManifest !== void 0) runManifest.patchStoryState(storyKey, { retry_count: next }).catch((err) => logger$
|
|
12239
|
+
if (runManifest !== null && runManifest !== void 0) runManifest.patchStoryState(storyKey, { retry_count: next }).catch((err) => logger$26.warn({
|
|
11738
12240
|
err,
|
|
11739
12241
|
storyKey
|
|
11740
12242
|
}, "patchStoryState(retry_count) failed — pipeline continues"));
|
|
@@ -11747,7 +12249,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
11747
12249
|
const nowMs = Date.now();
|
|
11748
12250
|
for (const [phase, startMs] of starts) {
|
|
11749
12251
|
const endMs = ends?.get(phase);
|
|
11750
|
-
if (endMs === void 0) logger$
|
|
12252
|
+
if (endMs === void 0) logger$26.warn({
|
|
11751
12253
|
storyKey,
|
|
11752
12254
|
phase
|
|
11753
12255
|
}, "Phase has no end time — story may have errored mid-phase. Duration capped to now() and may be inflated.");
|
|
@@ -11764,7 +12266,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
11764
12266
|
const wallClockSeconds = startedAt ? Math.round((new Date(completedAt).getTime() - new Date(startedAt).getTime()) / 1e3) : 0;
|
|
11765
12267
|
const wallClockMs = startedAt ? new Date(completedAt).getTime() - new Date(startedAt).getTime() : 0;
|
|
11766
12268
|
const tokenAgg = await aggregateTokenUsageForStory(db, config.pipelineRunId, storyKey);
|
|
11767
|
-
if (runManifest !== null) runManifest.patchStoryState(storyKey, { cost_usd: tokenAgg.cost }).catch((err) => logger$
|
|
12269
|
+
if (runManifest !== null) runManifest.patchStoryState(storyKey, { cost_usd: tokenAgg.cost }).catch((err) => logger$26.warn({
|
|
11768
12270
|
err,
|
|
11769
12271
|
storyKey
|
|
11770
12272
|
}, "patchStoryState(cost_usd) failed — pipeline continues"));
|
|
@@ -11800,7 +12302,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
11800
12302
|
recordedAt: completedAt,
|
|
11801
12303
|
timestamp: completedAt
|
|
11802
12304
|
}).catch((storeErr) => {
|
|
11803
|
-
logger$
|
|
12305
|
+
logger$26.warn({
|
|
11804
12306
|
err: storeErr,
|
|
11805
12307
|
storyKey
|
|
11806
12308
|
}, "Failed to record metric to StateStore (best-effort)");
|
|
@@ -11822,7 +12324,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
11822
12324
|
rationale: `Story ${storyKey} completed with result=${result} in ${wallClockSeconds}s. Tokens: ${tokenAgg.input}+${tokenAgg.output}. Review cycles: ${reviewCycles}.`
|
|
11823
12325
|
});
|
|
11824
12326
|
} catch (decisionErr) {
|
|
11825
|
-
logger$
|
|
12327
|
+
logger$26.warn({
|
|
11826
12328
|
err: decisionErr,
|
|
11827
12329
|
storyKey
|
|
11828
12330
|
}, "Failed to write story-metrics decision (best-effort)");
|
|
@@ -11901,7 +12403,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
11901
12403
|
const LOW_OUTPUT_TOKEN_THRESHOLD = 100;
|
|
11902
12404
|
const unverified = tokenAgg.output < LOW_OUTPUT_TOKEN_THRESHOLD;
|
|
11903
12405
|
if (unverified) {
|
|
11904
|
-
logger$
|
|
12406
|
+
logger$26.warn({
|
|
11905
12407
|
storyKey,
|
|
11906
12408
|
outputTokens: tokenAgg.output,
|
|
11907
12409
|
threshold: LOW_OUTPUT_TOKEN_THRESHOLD
|
|
@@ -11925,13 +12427,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
11925
12427
|
...unverified ? { unverified: true } : {}
|
|
11926
12428
|
});
|
|
11927
12429
|
} catch (emitErr) {
|
|
11928
|
-
logger$
|
|
12430
|
+
logger$26.warn({
|
|
11929
12431
|
err: emitErr,
|
|
11930
12432
|
storyKey
|
|
11931
12433
|
}, "Failed to emit story:metrics event (best-effort)");
|
|
11932
12434
|
}
|
|
11933
12435
|
} catch (err) {
|
|
11934
|
-
logger$
|
|
12436
|
+
logger$26.warn({
|
|
11935
12437
|
err,
|
|
11936
12438
|
storyKey
|
|
11937
12439
|
}, "Failed to write story metrics (best-effort)");
|
|
@@ -11960,7 +12462,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
11960
12462
|
rationale: `Story ${storyKey} ${outcome} after ${reviewCycles} review cycle(s).`
|
|
11961
12463
|
});
|
|
11962
12464
|
} catch (err) {
|
|
11963
|
-
logger$
|
|
12465
|
+
logger$26.warn({
|
|
11964
12466
|
err,
|
|
11965
12467
|
storyKey
|
|
11966
12468
|
}, "Failed to write story-outcome decision (best-effort)");
|
|
@@ -11998,7 +12500,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
11998
12500
|
rationale: `Escalation diagnosis for ${payload.storyKey}: ${diagnosis.recommendedAction} — ${diagnosis.rationale}`
|
|
11999
12501
|
});
|
|
12000
12502
|
} catch (err) {
|
|
12001
|
-
logger$
|
|
12503
|
+
logger$26.warn({
|
|
12002
12504
|
err,
|
|
12003
12505
|
storyKey: payload.storyKey
|
|
12004
12506
|
}, "Failed to persist escalation diagnosis (best-effort)");
|
|
@@ -12048,7 +12550,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12048
12550
|
const existing = _stories.get(storyKey);
|
|
12049
12551
|
if (existing !== void 0) {
|
|
12050
12552
|
Object.assign(existing, updates);
|
|
12051
|
-
persistStoryState(storyKey, existing).catch((err) => logger$
|
|
12553
|
+
persistStoryState(storyKey, existing).catch((err) => logger$26.warn({
|
|
12052
12554
|
err,
|
|
12053
12555
|
storyKey
|
|
12054
12556
|
}, "StateStore write failed after updateStory"));
|
|
@@ -12057,12 +12559,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
12057
12559
|
storyKey,
|
|
12058
12560
|
conflict: err
|
|
12059
12561
|
});
|
|
12060
|
-
else logger$
|
|
12562
|
+
else logger$26.warn({
|
|
12061
12563
|
err,
|
|
12062
12564
|
storyKey
|
|
12063
12565
|
}, "mergeStory failed");
|
|
12064
12566
|
});
|
|
12065
|
-
else if (updates.phase === "ESCALATED" || updates.phase === "VERIFICATION_FAILED") stateStore?.rollbackStory(storyKey).catch((err) => logger$
|
|
12567
|
+
else if (updates.phase === "ESCALATED" || updates.phase === "VERIFICATION_FAILED") stateStore?.rollbackStory(storyKey).catch((err) => logger$26.warn({
|
|
12066
12568
|
err,
|
|
12067
12569
|
storyKey
|
|
12068
12570
|
}, "rollbackStory failed — branch may persist"));
|
|
@@ -12074,7 +12576,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12074
12576
|
...updates
|
|
12075
12577
|
};
|
|
12076
12578
|
const opts = targetStatus === "complete" || targetStatus === "escalated" ? { completedAt: fullUpdated.completedAt } : void 0;
|
|
12077
|
-
wgRepo.updateStoryStatus(storyKey, targetStatus, opts).catch((err) => logger$
|
|
12579
|
+
wgRepo.updateStoryStatus(storyKey, targetStatus, opts).catch((err) => logger$26.warn({
|
|
12078
12580
|
err,
|
|
12079
12581
|
storyKey
|
|
12080
12582
|
}, "wg_stories status update failed (best-effort)"));
|
|
@@ -12090,7 +12592,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12090
12592
|
status: "dispatched",
|
|
12091
12593
|
phase: String(updates.phase),
|
|
12092
12594
|
started_at: fullUpdated.startedAt ?? new Date().toISOString()
|
|
12093
|
-
}).catch((err) => logger$
|
|
12595
|
+
}).catch((err) => logger$26.warn({
|
|
12094
12596
|
err,
|
|
12095
12597
|
storyKey
|
|
12096
12598
|
}, "patchStoryState(dispatched) failed — pipeline continues"));
|
|
@@ -12102,7 +12604,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12102
12604
|
completed_at: fullUpdated.completedAt ?? new Date().toISOString(),
|
|
12103
12605
|
review_cycles: fullUpdated.reviewCycles ?? 0,
|
|
12104
12606
|
dispatches: _storyDispatches.get(storyKey) ?? 0
|
|
12105
|
-
}).catch((err) => logger$
|
|
12607
|
+
}).catch((err) => logger$26.warn({
|
|
12106
12608
|
err,
|
|
12107
12609
|
storyKey
|
|
12108
12610
|
}, `patchStoryState(${manifestStatus}) failed — pipeline continues`));
|
|
@@ -12132,7 +12634,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12132
12634
|
};
|
|
12133
12635
|
await stateStore.setStoryState(storyKey, record);
|
|
12134
12636
|
} catch (err) {
|
|
12135
|
-
logger$
|
|
12637
|
+
logger$26.warn({
|
|
12136
12638
|
err,
|
|
12137
12639
|
storyKey
|
|
12138
12640
|
}, "StateStore.setStoryState failed (best-effort)");
|
|
@@ -12148,7 +12650,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12148
12650
|
token_usage_json: serialized
|
|
12149
12651
|
});
|
|
12150
12652
|
} catch (err) {
|
|
12151
|
-
logger$
|
|
12653
|
+
logger$26.warn({ err }, "Failed to persist orchestrator state");
|
|
12152
12654
|
}
|
|
12153
12655
|
}
|
|
12154
12656
|
function recordProgress() {
|
|
@@ -12174,7 +12676,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12174
12676
|
queuedDispatches: queued
|
|
12175
12677
|
});
|
|
12176
12678
|
if (config.pipelineRunId !== void 0) updatePipelineRun(db, config.pipelineRunId, { current_phase: "implementation" }).catch((err) => {
|
|
12177
|
-
logger$
|
|
12679
|
+
logger$26.debug({ err }, "Heartbeat: failed to touch updated_at (non-fatal)");
|
|
12178
12680
|
});
|
|
12179
12681
|
const elapsed = Date.now() - _lastProgressTs;
|
|
12180
12682
|
let childPids = [];
|
|
@@ -12196,7 +12698,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12196
12698
|
}
|
|
12197
12699
|
if (childActive) {
|
|
12198
12700
|
_lastProgressTs = Date.now();
|
|
12199
|
-
logger$
|
|
12701
|
+
logger$26.debug({
|
|
12200
12702
|
storyKey: key,
|
|
12201
12703
|
phase: s$1.phase,
|
|
12202
12704
|
childPids
|
|
@@ -12205,7 +12707,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12205
12707
|
}
|
|
12206
12708
|
_stalledStories.add(key);
|
|
12207
12709
|
_storiesWithStall.add(key);
|
|
12208
|
-
logger$
|
|
12710
|
+
logger$26.warn({
|
|
12209
12711
|
storyKey: key,
|
|
12210
12712
|
phase: s$1.phase,
|
|
12211
12713
|
elapsedMs: elapsed,
|
|
@@ -12250,7 +12752,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12250
12752
|
for (let attempt = 0; attempt < MEMORY_PRESSURE_BACKOFF_MS.length; attempt++) {
|
|
12251
12753
|
const memState = dispatcher.getMemoryState();
|
|
12252
12754
|
if (!memState.isPressured) return true;
|
|
12253
|
-
logger$
|
|
12755
|
+
logger$26.warn({
|
|
12254
12756
|
storyKey,
|
|
12255
12757
|
freeMB: memState.freeMB,
|
|
12256
12758
|
thresholdMB: memState.thresholdMB,
|
|
@@ -12270,12 +12772,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
12270
12772
|
* exhausted retries the story is ESCALATED.
|
|
12271
12773
|
*/
|
|
12272
12774
|
async function processStory(storyKey, storyOptions) {
|
|
12273
|
-
logger$
|
|
12775
|
+
logger$26.info({ storyKey }, "Processing story");
|
|
12274
12776
|
await initRetryCount(storyKey);
|
|
12275
12777
|
{
|
|
12276
12778
|
const memoryOk = await checkMemoryPressure(storyKey);
|
|
12277
12779
|
if (!memoryOk) {
|
|
12278
|
-
logger$
|
|
12780
|
+
logger$26.warn({ storyKey }, "Memory pressure exhausted — escalating story without dispatch");
|
|
12279
12781
|
const memPressureState = {
|
|
12280
12782
|
phase: "ESCALATED",
|
|
12281
12783
|
reviewCycles: 0,
|
|
@@ -12284,7 +12786,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12284
12786
|
completedAt: new Date().toISOString()
|
|
12285
12787
|
};
|
|
12286
12788
|
_stories.set(storyKey, memPressureState);
|
|
12287
|
-
persistStoryState(storyKey, memPressureState).catch((err) => logger$
|
|
12789
|
+
persistStoryState(storyKey, memPressureState).catch((err) => logger$26.warn({
|
|
12288
12790
|
err,
|
|
12289
12791
|
storyKey
|
|
12290
12792
|
}, "StateStore write failed after memory-pressure escalation"));
|
|
@@ -12301,7 +12803,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12301
12803
|
}
|
|
12302
12804
|
await waitIfPaused();
|
|
12303
12805
|
if (_state !== "RUNNING") return;
|
|
12304
|
-
stateStore?.branchForStory(storyKey).catch((err) => logger$
|
|
12806
|
+
stateStore?.branchForStory(storyKey).catch((err) => logger$26.warn({
|
|
12305
12807
|
err,
|
|
12306
12808
|
storyKey
|
|
12307
12809
|
}, "branchForStory failed — continuing without branch isolation"));
|
|
@@ -12320,7 +12822,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12320
12822
|
if (match$2) {
|
|
12321
12823
|
const candidatePath = join$1(artifactsDir, match$2);
|
|
12322
12824
|
const validation = await isValidStoryFile(candidatePath);
|
|
12323
|
-
if (!validation.valid) logger$
|
|
12825
|
+
if (!validation.valid) logger$26.warn({
|
|
12324
12826
|
storyKey,
|
|
12325
12827
|
storyFilePath: candidatePath,
|
|
12326
12828
|
reason: validation.reason
|
|
@@ -12345,7 +12847,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12345
12847
|
storedHash,
|
|
12346
12848
|
currentHash
|
|
12347
12849
|
});
|
|
12348
|
-
logger$
|
|
12850
|
+
logger$26.info({
|
|
12349
12851
|
storyKey,
|
|
12350
12852
|
storedHash,
|
|
12351
12853
|
currentHash
|
|
@@ -12356,7 +12858,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12356
12858
|
} catch {}
|
|
12357
12859
|
if (!isDrift) {
|
|
12358
12860
|
storyFilePath = candidatePath;
|
|
12359
|
-
logger$
|
|
12861
|
+
logger$26.info({
|
|
12360
12862
|
storyKey,
|
|
12361
12863
|
storyFilePath
|
|
12362
12864
|
}, "Found existing story file — skipping create-story");
|
|
@@ -12376,12 +12878,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
12376
12878
|
const staleName = match$2.replace(/\.md$/, `.stale-${ts}.md`);
|
|
12377
12879
|
const stalePath = join$1(artifactsDir, staleName);
|
|
12378
12880
|
renameSync(candidatePath, stalePath);
|
|
12379
|
-
logger$
|
|
12881
|
+
logger$26.info({
|
|
12380
12882
|
storyKey,
|
|
12381
12883
|
staleName
|
|
12382
12884
|
}, `[orchestrator] story ${storyKey}: renamed drifted artifact to ${staleName} before re-dispatch`);
|
|
12383
12885
|
} catch (renameErr) {
|
|
12384
|
-
logger$
|
|
12886
|
+
logger$26.warn({
|
|
12385
12887
|
storyKey,
|
|
12386
12888
|
err: renameErr
|
|
12387
12889
|
}, "Failed to rename stale artifact before create-story re-dispatch; relying on 58-9d fraud-guard");
|
|
@@ -12390,7 +12892,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12390
12892
|
}
|
|
12391
12893
|
} catch {}
|
|
12392
12894
|
if (storyFilePath === void 0 && projectRoot && isImplicitlyCovered(storyKey, projectRoot)) {
|
|
12393
|
-
logger$
|
|
12895
|
+
logger$26.info({ storyKey }, `Story ${storyKey} appears implicitly covered — all expected new files already exist. Skipping create-story.`);
|
|
12394
12896
|
endPhase(storyKey, "create-story");
|
|
12395
12897
|
eventBus.emit("orchestrator:story-phase-complete", {
|
|
12396
12898
|
storyKey,
|
|
@@ -12443,7 +12945,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12443
12945
|
output_tokens: createResult.tokenUsage.output,
|
|
12444
12946
|
cost_usd: estimateDispatchCost(createResult.tokenUsage.input, createResult.tokenUsage.output),
|
|
12445
12947
|
metadata: JSON.stringify({ storyKey })
|
|
12446
|
-
})).catch((tokenErr) => logger$
|
|
12948
|
+
})).catch((tokenErr) => logger$26.warn({
|
|
12447
12949
|
storyKey,
|
|
12448
12950
|
err: tokenErr
|
|
12449
12951
|
}, "Failed to record create-story token usage"));
|
|
@@ -12451,7 +12953,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12451
12953
|
if (createResult.result === "failed") {
|
|
12452
12954
|
const errMsg = createResult.error ?? "create-story failed";
|
|
12453
12955
|
const stderrSnippet = errMsg.includes("--- stderr ---") ? errMsg.slice(errMsg.indexOf("--- stderr ---") + 15, errMsg.indexOf("--- stderr ---") + 515) : errMsg.slice(0, 500);
|
|
12454
|
-
logger$
|
|
12956
|
+
logger$26.error({
|
|
12455
12957
|
storyKey,
|
|
12456
12958
|
stderrSnippet
|
|
12457
12959
|
}, `Create-story failed: ${stderrSnippet.split("\n")[0]}`);
|
|
@@ -12493,7 +12995,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12493
12995
|
let claimedPath = createResult.story_file;
|
|
12494
12996
|
if (claimedPath.startsWith(escapedExpectedDir)) {
|
|
12495
12997
|
claimedPath = claimedPath.replace("/\\_bmad-output/", "/_bmad-output/");
|
|
12496
|
-
logger$
|
|
12998
|
+
logger$26.warn({
|
|
12497
12999
|
storyKey,
|
|
12498
13000
|
originalClaim: createResult.story_file,
|
|
12499
13001
|
normalizedClaim: claimedPath
|
|
@@ -12511,7 +13013,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12511
13013
|
if (escapedVariant !== claimedPath && existsSync(escapedVariant)) try {
|
|
12512
13014
|
renameSync(escapedVariant, claimedPath);
|
|
12513
13015
|
actualPath = claimedPath;
|
|
12514
|
-
logger$
|
|
13016
|
+
logger$26.warn({
|
|
12515
13017
|
storyKey,
|
|
12516
13018
|
escapedVariant,
|
|
12517
13019
|
canonicalPath: claimedPath
|
|
@@ -12522,7 +13024,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12522
13024
|
});
|
|
12523
13025
|
} catch (renameErr) {
|
|
12524
13026
|
actualPath = escapedVariant;
|
|
12525
|
-
logger$
|
|
13027
|
+
logger$26.warn({
|
|
12526
13028
|
storyKey,
|
|
12527
13029
|
escapedVariant,
|
|
12528
13030
|
canonicalPath: claimedPath,
|
|
@@ -12537,7 +13039,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12537
13039
|
if (actualPath === null) {
|
|
12538
13040
|
const outputTokens = createResult.tokenUsage?.output ?? 0;
|
|
12539
13041
|
const errMsg = `create-story claimed success (story_file: ${createResult.story_file}) but the file does not exist on disk (output tokens: ${outputTokens})`;
|
|
12540
|
-
logger$
|
|
13042
|
+
logger$26.error({
|
|
12541
13043
|
storyKey,
|
|
12542
13044
|
claimedPath: createResult.story_file,
|
|
12543
13045
|
outputTokens
|
|
@@ -12564,7 +13066,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12564
13066
|
const mtimeISO = new Date(claimedStat.mtimeMs).toISOString();
|
|
12565
13067
|
const dispatchStartISO = new Date(dispatchStartMs).toISOString();
|
|
12566
13068
|
const errMsg = `create-story claimed success but did not rewrite ${actualPath} during this dispatch (file mtime ${mtimeISO} predates dispatch start ${dispatchStartISO}; output tokens: ${outputTokens})`;
|
|
12567
|
-
logger$
|
|
13069
|
+
logger$26.error({
|
|
12568
13070
|
storyKey,
|
|
12569
13071
|
claimedPath: actualPath,
|
|
12570
13072
|
mtimeISO,
|
|
@@ -12587,7 +13089,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12587
13089
|
return;
|
|
12588
13090
|
}
|
|
12589
13091
|
} catch (verifyErr) {
|
|
12590
|
-
logger$
|
|
13092
|
+
logger$26.warn({
|
|
12591
13093
|
storyKey,
|
|
12592
13094
|
err: verifyErr
|
|
12593
13095
|
}, "create-story post-dispatch file verification threw; proceeding with claimed path");
|
|
@@ -12610,7 +13112,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12610
13112
|
const overlap = computeTitleOverlap(expectedTitle, createResult.story_title);
|
|
12611
13113
|
if (overlap < TITLE_OVERLAP_WARNING_THRESHOLD) {
|
|
12612
13114
|
const msg = `Story title mismatch: expected "${expectedTitle}" but got "${createResult.story_title}" (word overlap: ${Math.round(overlap * 100)}%). This may indicate the create-story agent received truncated context.`;
|
|
12613
|
-
logger$
|
|
13115
|
+
logger$26.warn({
|
|
12614
13116
|
storyKey,
|
|
12615
13117
|
expectedTitle,
|
|
12616
13118
|
generatedTitle: createResult.story_title,
|
|
@@ -12620,7 +13122,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12620
13122
|
storyKey,
|
|
12621
13123
|
msg
|
|
12622
13124
|
});
|
|
12623
|
-
} else logger$
|
|
13125
|
+
} else logger$26.debug({
|
|
12624
13126
|
storyKey,
|
|
12625
13127
|
expectedTitle,
|
|
12626
13128
|
generatedTitle: createResult.story_title,
|
|
@@ -12629,7 +13131,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12629
13131
|
}
|
|
12630
13132
|
}
|
|
12631
13133
|
} catch (titleValidationErr) {
|
|
12632
|
-
logger$
|
|
13134
|
+
logger$26.debug({
|
|
12633
13135
|
storyKey,
|
|
12634
13136
|
err: titleValidationErr
|
|
12635
13137
|
}, "Story title validation skipped due to error");
|
|
@@ -12670,7 +13172,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12670
13172
|
const pathDrift = pathFidelity?.drift ?? 0;
|
|
12671
13173
|
const clauseDrift = clauseFidelity.drift;
|
|
12672
13174
|
const overallDrift = Math.max(pathDrift, clauseDrift);
|
|
12673
|
-
logger$
|
|
13175
|
+
logger$26.debug({
|
|
12674
13176
|
storyKey,
|
|
12675
13177
|
pathDrift,
|
|
12676
13178
|
clauseDrift,
|
|
@@ -12692,7 +13194,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12692
13194
|
if (pathMissing.length > 0) reasons.push(`${pathMissing.length} named path(s) missing`);
|
|
12693
13195
|
if (numericMismatches.length > 0) reasons.push(`${numericMismatches.length} numeric quantifier mismatch(es) (e.g., "${numericMismatches[0].noun}" source=${numericMismatches[0].sourceCount} rendered=${numericMismatches[0].renderedCount})`);
|
|
12694
13196
|
if (clauseFidelity.clauseRatio < .7) reasons.push(`clause shortfall (rendered ${clauseFidelity.renderedClauseCount}/${clauseFidelity.sourceClauseCount} = ${Math.round(clauseFidelity.clauseRatio * 100)}%)`);
|
|
12695
|
-
logger$
|
|
13197
|
+
logger$26.warn({
|
|
12696
13198
|
storyKey,
|
|
12697
13199
|
pathDrift,
|
|
12698
13200
|
clauseDrift,
|
|
@@ -12735,7 +13237,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12735
13237
|
storyFilePath = void 0;
|
|
12736
13238
|
continue;
|
|
12737
13239
|
} catch (renameErr) {
|
|
12738
|
-
logger$
|
|
13240
|
+
logger$26.warn({
|
|
12739
13241
|
storyKey,
|
|
12740
13242
|
err: renameErr,
|
|
12741
13243
|
stalePath
|
|
@@ -12749,7 +13251,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12749
13251
|
if (numericMismatches.length > 0) reasons.push(`numeric mismatches: ${numericMismatches.map((m) => `${m.noun} (source=${m.sourceCount}, rendered=${m.renderedCount})`).join("; ")}`);
|
|
12750
13252
|
if (clauseFidelity.clauseRatio < .7) reasons.push(`clause shortfall: source=${clauseFidelity.sourceClauseCount}, rendered=${clauseFidelity.renderedClauseCount}`);
|
|
12751
13253
|
const errMsg = `create-story output drifted from source AC after ${MAX_FIDELITY_RETRIES} retries; ` + reasons.join("; ");
|
|
12752
|
-
logger$
|
|
13254
|
+
logger$26.error({
|
|
12753
13255
|
storyKey,
|
|
12754
13256
|
pathDrift,
|
|
12755
13257
|
clauseDrift,
|
|
@@ -12776,7 +13278,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12776
13278
|
}
|
|
12777
13279
|
}
|
|
12778
13280
|
} catch (fidelityErr) {
|
|
12779
|
-
logger$
|
|
13281
|
+
logger$26.warn({
|
|
12780
13282
|
storyKey,
|
|
12781
13283
|
err: fidelityErr
|
|
12782
13284
|
}, "fidelity gate threw; proceeding without retry");
|
|
@@ -12807,18 +13309,79 @@ function createImplementationOrchestrator(deps) {
|
|
|
12807
13309
|
...contract.transport !== void 0 ? { transport: contract.transport } : {}
|
|
12808
13310
|
})
|
|
12809
13311
|
});
|
|
12810
|
-
logger$
|
|
13312
|
+
logger$26.info({
|
|
12811
13313
|
storyKey,
|
|
12812
13314
|
contractCount: contracts.length,
|
|
12813
13315
|
contracts
|
|
12814
13316
|
}, "Stored interface contract declarations");
|
|
12815
13317
|
}
|
|
12816
13318
|
} catch (err) {
|
|
12817
|
-
logger$
|
|
13319
|
+
logger$26.warn({
|
|
12818
13320
|
storyKey,
|
|
12819
13321
|
error: err instanceof Error ? err.message : String(err)
|
|
12820
13322
|
}, "Failed to parse interface contracts — continuing without contract declarations");
|
|
12821
13323
|
}
|
|
13324
|
+
if (storyFilePath) try {
|
|
13325
|
+
let probeAuthorEpicContent = "";
|
|
13326
|
+
const probeAuthorEpicsPath = findEpicFileForStory(projectRoot ?? process.cwd(), storyKey);
|
|
13327
|
+
if (probeAuthorEpicsPath) try {
|
|
13328
|
+
const epicFull = readFileSync(probeAuthorEpicsPath, "utf-8");
|
|
13329
|
+
const section = extractStorySection(epicFull, storyKey);
|
|
13330
|
+
probeAuthorEpicContent = section ?? epicFull;
|
|
13331
|
+
} catch {}
|
|
13332
|
+
if (detectsEventDrivenAC(probeAuthorEpicContent)) {
|
|
13333
|
+
let artifactHasProbes = false;
|
|
13334
|
+
try {
|
|
13335
|
+
const artifactContent = readFileSync(storyFilePath, "utf-8");
|
|
13336
|
+
artifactHasProbes = /^## Runtime Probes/m.test(artifactContent);
|
|
13337
|
+
} catch {}
|
|
13338
|
+
if (!artifactHasProbes) {
|
|
13339
|
+
const probeAuthorResult = await runProbeAuthor({
|
|
13340
|
+
db,
|
|
13341
|
+
pack,
|
|
13342
|
+
contextCompiler,
|
|
13343
|
+
dispatcher,
|
|
13344
|
+
projectRoot,
|
|
13345
|
+
tokenCeilings,
|
|
13346
|
+
otlpEndpoint: _otlpEndpoint,
|
|
13347
|
+
agentId
|
|
13348
|
+
}, {
|
|
13349
|
+
storyKey,
|
|
13350
|
+
storyFilePath,
|
|
13351
|
+
pipelineRunId: config.pipelineRunId ?? "",
|
|
13352
|
+
sourceAcContent: probeAuthorEpicContent,
|
|
13353
|
+
epicContent: probeAuthorEpicContent,
|
|
13354
|
+
emitEvent: (name, payload) => {
|
|
13355
|
+
eventBus.emit("orchestrator:story-warn", {
|
|
13356
|
+
storyKey,
|
|
13357
|
+
msg: `probe-author:${name.replace("probe-author:", "")} ${JSON.stringify(payload)}`
|
|
13358
|
+
});
|
|
13359
|
+
}
|
|
13360
|
+
});
|
|
13361
|
+
logger$26.info({
|
|
13362
|
+
storyKey,
|
|
13363
|
+
result: probeAuthorResult.result,
|
|
13364
|
+
probesAuthoredCount: probeAuthorResult.probesAuthoredCount
|
|
13365
|
+
}, "probe-author phase complete");
|
|
13366
|
+
if (config.pipelineRunId !== void 0 && probeAuthorResult.tokenUsage.input + probeAuthorResult.tokenUsage.output > 0) Promise.resolve().then(() => addTokenUsage(db, config.pipelineRunId, {
|
|
13367
|
+
phase: "probe-author",
|
|
13368
|
+
agent: "probe-author",
|
|
13369
|
+
input_tokens: probeAuthorResult.tokenUsage.input,
|
|
13370
|
+
output_tokens: probeAuthorResult.tokenUsage.output,
|
|
13371
|
+
cost_usd: estimateDispatchCost(probeAuthorResult.tokenUsage.input, probeAuthorResult.tokenUsage.output),
|
|
13372
|
+
metadata: JSON.stringify({ storyKey })
|
|
13373
|
+
})).catch((tokenErr) => logger$26.warn({
|
|
13374
|
+
storyKey,
|
|
13375
|
+
err: tokenErr
|
|
13376
|
+
}, "Failed to record probe-author token usage"));
|
|
13377
|
+
} else logger$26.debug({ storyKey }, "probe-author: story artifact already has ## Runtime Probes — skipping gate");
|
|
13378
|
+
} else logger$26.debug({ storyKey }, "probe-author: source AC not event-driven — skipping gate");
|
|
13379
|
+
} catch (probeAuthorErr) {
|
|
13380
|
+
logger$26.warn({
|
|
13381
|
+
storyKey,
|
|
13382
|
+
err: probeAuthorErr
|
|
13383
|
+
}, "probe-author gate threw unexpectedly; proceeding to test-plan without authored probes");
|
|
13384
|
+
}
|
|
12822
13385
|
await waitIfPaused();
|
|
12823
13386
|
if (_state !== "RUNNING") return;
|
|
12824
13387
|
startPhase(storyKey, "test-plan");
|
|
@@ -12843,10 +13406,10 @@ function createImplementationOrchestrator(deps) {
|
|
|
12843
13406
|
});
|
|
12844
13407
|
testPlanPhaseResult = testPlanResult.result;
|
|
12845
13408
|
testPlanTokenUsage = testPlanResult.tokenUsage;
|
|
12846
|
-
if (testPlanResult.result === "success") logger$
|
|
12847
|
-
else logger$
|
|
13409
|
+
if (testPlanResult.result === "success") logger$26.info({ storyKey }, "Test plan generated successfully");
|
|
13410
|
+
else logger$26.warn({ storyKey }, "Test planning returned failed result — proceeding to dev-story without test plan");
|
|
12848
13411
|
} catch (err) {
|
|
12849
|
-
logger$
|
|
13412
|
+
logger$26.warn({
|
|
12850
13413
|
storyKey,
|
|
12851
13414
|
err
|
|
12852
13415
|
}, "Test planning failed — proceeding to dev-story without test plan");
|
|
@@ -12859,7 +13422,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12859
13422
|
output_tokens: testPlanTokenUsage.output,
|
|
12860
13423
|
cost_usd: estimateDispatchCost(testPlanTokenUsage.input, testPlanTokenUsage.output),
|
|
12861
13424
|
metadata: JSON.stringify({ storyKey })
|
|
12862
|
-
})).catch((tokenErr) => logger$
|
|
13425
|
+
})).catch((tokenErr) => logger$26.warn({
|
|
12863
13426
|
storyKey,
|
|
12864
13427
|
err: tokenErr
|
|
12865
13428
|
}, "Failed to record test-plan token usage"));
|
|
@@ -12927,7 +13490,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12927
13490
|
storyContentForAnalysis = await readFile$1(storyFilePath ?? "", "utf-8");
|
|
12928
13491
|
storyContentForVerification = storyContentForAnalysis;
|
|
12929
13492
|
} catch (err) {
|
|
12930
|
-
logger$
|
|
13493
|
+
logger$26.error({
|
|
12931
13494
|
storyKey,
|
|
12932
13495
|
storyFilePath,
|
|
12933
13496
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -12935,7 +13498,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12935
13498
|
}
|
|
12936
13499
|
const analysis = analyzeStoryComplexity(storyContentForAnalysis);
|
|
12937
13500
|
const batches = planTaskBatches(analysis);
|
|
12938
|
-
logger$
|
|
13501
|
+
logger$26.info({
|
|
12939
13502
|
storyKey,
|
|
12940
13503
|
estimatedScope: analysis.estimatedScope,
|
|
12941
13504
|
batchCount: batches.length,
|
|
@@ -12953,7 +13516,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12953
13516
|
if (_state !== "RUNNING") break;
|
|
12954
13517
|
const taskScope = batch.taskIds.map((id, i) => `T${id}: ${batch.taskTitles[i] ?? ""}`).join("\n");
|
|
12955
13518
|
const priorFiles = allFilesModified.size > 0 ? Array.from(allFilesModified) : void 0;
|
|
12956
|
-
logger$
|
|
13519
|
+
logger$26.info({
|
|
12957
13520
|
storyKey,
|
|
12958
13521
|
batchIndex: batch.batchIndex,
|
|
12959
13522
|
taskCount: batch.taskIds.length
|
|
@@ -12984,7 +13547,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
12984
13547
|
});
|
|
12985
13548
|
} catch (batchErr) {
|
|
12986
13549
|
const errMsg = batchErr instanceof Error ? batchErr.message : String(batchErr);
|
|
12987
|
-
logger$
|
|
13550
|
+
logger$26.warn({
|
|
12988
13551
|
storyKey,
|
|
12989
13552
|
batchIndex: batch.batchIndex,
|
|
12990
13553
|
error: errMsg
|
|
@@ -13005,7 +13568,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13005
13568
|
filesModified: batchFilesModified,
|
|
13006
13569
|
result: batchResult.result === "success" ? "success" : "failed"
|
|
13007
13570
|
};
|
|
13008
|
-
logger$
|
|
13571
|
+
logger$26.info(batchMetrics, "Batch dev-story metrics");
|
|
13009
13572
|
for (const f$1 of batchFilesModified) allFilesModified.add(f$1);
|
|
13010
13573
|
if (batchFilesModified.length > 0) batchFileGroups.push({
|
|
13011
13574
|
batchIndex: batch.batchIndex,
|
|
@@ -13024,13 +13587,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
13024
13587
|
durationMs: batchDurationMs,
|
|
13025
13588
|
result: batchMetrics.result
|
|
13026
13589
|
})
|
|
13027
|
-
})).catch((tokenErr) => logger$
|
|
13590
|
+
})).catch((tokenErr) => logger$26.warn({
|
|
13028
13591
|
storyKey,
|
|
13029
13592
|
batchIndex: batch.batchIndex,
|
|
13030
13593
|
err: tokenErr
|
|
13031
13594
|
}, "Failed to record batch token usage"));
|
|
13032
13595
|
if (batchResult.tokenUsage?.output !== void 0) devOutputTokenCount = (devOutputTokenCount ?? 0) + batchResult.tokenUsage.output;
|
|
13033
|
-
if (batchResult.result === "failed") logger$
|
|
13596
|
+
if (batchResult.result === "failed") logger$26.warn({
|
|
13034
13597
|
storyKey,
|
|
13035
13598
|
batchIndex: batch.batchIndex,
|
|
13036
13599
|
error: batchResult.error
|
|
@@ -13073,7 +13636,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13073
13636
|
output_tokens: devResult.tokenUsage.output,
|
|
13074
13637
|
cost_usd: estimateDispatchCost(devResult.tokenUsage.input, devResult.tokenUsage.output),
|
|
13075
13638
|
metadata: JSON.stringify({ storyKey })
|
|
13076
|
-
})).catch((tokenErr) => logger$
|
|
13639
|
+
})).catch((tokenErr) => logger$26.warn({
|
|
13077
13640
|
storyKey,
|
|
13078
13641
|
err: tokenErr
|
|
13079
13642
|
}, "Failed to record dev-story token usage"));
|
|
@@ -13088,7 +13651,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13088
13651
|
endPhase(storyKey, "dev-story");
|
|
13089
13652
|
const timeoutFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
|
|
13090
13653
|
if (timeoutFiles.length === 0) {
|
|
13091
|
-
logger$
|
|
13654
|
+
logger$26.warn({ storyKey }, "Dev-story timeout with zero modified files — escalating immediately (no checkpoint)");
|
|
13092
13655
|
updateStory(storyKey, {
|
|
13093
13656
|
phase: "ESCALATED",
|
|
13094
13657
|
error: "timeout-no-files",
|
|
@@ -13104,7 +13667,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13104
13667
|
await persistState();
|
|
13105
13668
|
return;
|
|
13106
13669
|
}
|
|
13107
|
-
logger$
|
|
13670
|
+
logger$26.info({
|
|
13108
13671
|
storyKey,
|
|
13109
13672
|
filesCount: timeoutFiles.length
|
|
13110
13673
|
}, "Dev-story timeout with partial files — capturing checkpoint");
|
|
@@ -13121,7 +13684,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13121
13684
|
]
|
|
13122
13685
|
}).trim();
|
|
13123
13686
|
} catch (diffErr) {
|
|
13124
|
-
logger$
|
|
13687
|
+
logger$26.warn({
|
|
13125
13688
|
storyKey,
|
|
13126
13689
|
error: diffErr instanceof Error ? diffErr.message : String(diffErr)
|
|
13127
13690
|
}, "Failed to capture git diff for checkpoint — proceeding with empty diff");
|
|
@@ -13148,7 +13711,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13148
13711
|
recordedAt: new Date().toISOString(),
|
|
13149
13712
|
sprint: config.sprint
|
|
13150
13713
|
}).catch((storeErr) => {
|
|
13151
|
-
logger$
|
|
13714
|
+
logger$26.warn({
|
|
13152
13715
|
err: storeErr,
|
|
13153
13716
|
storyKey
|
|
13154
13717
|
}, "Failed to record timeout metric to StateStore (best-effort)");
|
|
@@ -13207,9 +13770,9 @@ function createImplementationOrchestrator(deps) {
|
|
|
13207
13770
|
checkpointRetryPrompt = assembled.prompt;
|
|
13208
13771
|
} catch {
|
|
13209
13772
|
checkpointRetryPrompt = `Continue story ${storyKey} from checkpoint. Your prior attempt timed out. Do not redo completed work.`;
|
|
13210
|
-
logger$
|
|
13773
|
+
logger$26.warn({ storyKey }, "Failed to assemble checkpoint retry prompt — using fallback");
|
|
13211
13774
|
}
|
|
13212
|
-
logger$
|
|
13775
|
+
logger$26.info({
|
|
13213
13776
|
storyKey,
|
|
13214
13777
|
filesCount: checkpointData.filesModified.length
|
|
13215
13778
|
}, "Dispatching checkpoint retry for timed-out story");
|
|
@@ -13238,7 +13801,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13238
13801
|
} : void 0 }
|
|
13239
13802
|
});
|
|
13240
13803
|
if (checkpointRetryResult.status === "timeout") {
|
|
13241
|
-
logger$
|
|
13804
|
+
logger$26.warn({ storyKey }, "Checkpoint retry dispatch timed out — escalating story");
|
|
13242
13805
|
updateStory(storyKey, {
|
|
13243
13806
|
phase: "ESCALATED",
|
|
13244
13807
|
error: "checkpoint-retry-timeout",
|
|
@@ -13258,7 +13821,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13258
13821
|
replaceDevStorySignals(retryParsed);
|
|
13259
13822
|
devFilesModified = retryParsed?.files_modified ?? checkGitDiffFiles(projectRoot ?? process.cwd());
|
|
13260
13823
|
if (checkpointRetryResult.status === "completed" && retryParsed?.result === "success") devStoryWasSuccess = true;
|
|
13261
|
-
else logger$
|
|
13824
|
+
else logger$26.warn({
|
|
13262
13825
|
storyKey,
|
|
13263
13826
|
status: checkpointRetryResult.status
|
|
13264
13827
|
}, "Checkpoint retry completed with failure — proceeding to code review");
|
|
@@ -13268,13 +13831,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
13268
13831
|
replaceDevStorySignals(devResult);
|
|
13269
13832
|
if (devResult.result === "success") devStoryWasSuccess = true;
|
|
13270
13833
|
else {
|
|
13271
|
-
logger$
|
|
13834
|
+
logger$26.warn({
|
|
13272
13835
|
storyKey,
|
|
13273
13836
|
error: devResult.error,
|
|
13274
13837
|
filesModified: devFilesModified.length
|
|
13275
13838
|
}, "Dev-story reported failure, proceeding to code review");
|
|
13276
13839
|
if (!devResult.error?.startsWith("dispatch_timeout")) {
|
|
13277
|
-
logger$
|
|
13840
|
+
logger$26.warn({
|
|
13278
13841
|
storyKey,
|
|
13279
13842
|
error: devResult.error
|
|
13280
13843
|
}, "Agent process failure (non-timeout) — story will proceed to code review with partial work");
|
|
@@ -13336,13 +13899,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
13336
13899
|
}).trim();
|
|
13337
13900
|
if (committedFiles.length > 0) gitDiffFiles = committedFiles.split("\n").filter(Boolean);
|
|
13338
13901
|
} catch {}
|
|
13339
|
-
logger$
|
|
13902
|
+
logger$26.info({
|
|
13340
13903
|
storyKey,
|
|
13341
13904
|
baselineHeadSha,
|
|
13342
13905
|
committedFileCount: gitDiffFiles?.length ?? 0
|
|
13343
13906
|
}, "Working tree clean but new commits detected since dispatch — skipping zero-diff escalation");
|
|
13344
13907
|
} else {
|
|
13345
|
-
logger$
|
|
13908
|
+
logger$26.warn({ storyKey }, "Zero-diff detected after COMPLETE dev-story — no file changes and no new commits");
|
|
13346
13909
|
eventBus.emit("orchestrator:zero-diff-escalation", {
|
|
13347
13910
|
storyKey,
|
|
13348
13911
|
reason: "zero-diff-on-complete"
|
|
@@ -13392,10 +13955,10 @@ function createImplementationOrchestrator(deps) {
|
|
|
13392
13955
|
"pipe"
|
|
13393
13956
|
]
|
|
13394
13957
|
});
|
|
13395
|
-
logger$
|
|
13958
|
+
logger$26.info({ storyKey }, "Secondary typecheck (tsc --noEmit) passed");
|
|
13396
13959
|
} catch (tscErr) {
|
|
13397
13960
|
const tscOutput = tscErr instanceof Error && "stdout" in tscErr ? String(tscErr.stdout ?? "").slice(0, 2e3) : "";
|
|
13398
|
-
logger$
|
|
13961
|
+
logger$26.warn({
|
|
13399
13962
|
storyKey,
|
|
13400
13963
|
tscOutput
|
|
13401
13964
|
}, "Secondary typecheck (tsc --noEmit) failed — treating as build failure");
|
|
@@ -13410,7 +13973,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13410
13973
|
if (buildVerifyResult.status === "passed") {
|
|
13411
13974
|
_buildPassed = true;
|
|
13412
13975
|
eventBus.emit("story:build-verification-passed", { storyKey });
|
|
13413
|
-
logger$
|
|
13976
|
+
logger$26.info({ storyKey }, "Build verification passed");
|
|
13414
13977
|
} else if (buildVerifyResult.status === "failed" || buildVerifyResult.status === "timeout") {
|
|
13415
13978
|
const truncatedOutput = (buildVerifyResult.output ?? "").slice(0, 2e3);
|
|
13416
13979
|
const reason = buildVerifyResult.reason ?? "build-verification-failed";
|
|
@@ -13419,7 +13982,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13419
13982
|
const resolvedRoot = projectRoot ?? process.cwd();
|
|
13420
13983
|
const hasChanges = detectPackageChanges(_packageSnapshot, resolvedRoot);
|
|
13421
13984
|
if (hasChanges) {
|
|
13422
|
-
logger$
|
|
13985
|
+
logger$26.warn({ storyKey }, "Package files changed since snapshot — restoring to prevent cascade");
|
|
13423
13986
|
const restoreResult = restorePackageSnapshot(_packageSnapshot, { projectRoot: resolvedRoot });
|
|
13424
13987
|
if (restoreResult.restored) {
|
|
13425
13988
|
const retryAfterRestore = runBuildVerification({
|
|
@@ -13432,11 +13995,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
13432
13995
|
retryPassed = true;
|
|
13433
13996
|
_buildPassed = true;
|
|
13434
13997
|
eventBus.emit("story:build-verification-passed", { storyKey });
|
|
13435
|
-
logger$
|
|
13998
|
+
logger$26.warn({
|
|
13436
13999
|
storyKey,
|
|
13437
14000
|
filesRestored: restoreResult.filesRestored
|
|
13438
14001
|
}, "Build passed after package snapshot restore — cross-story pollution detected and cleaned");
|
|
13439
|
-
} else logger$
|
|
14002
|
+
} else logger$26.warn({
|
|
13440
14003
|
storyKey,
|
|
13441
14004
|
filesRestored: restoreResult.filesRestored
|
|
13442
14005
|
}, "Build still fails after snapshot restore — story has its own build errors");
|
|
@@ -13448,7 +14011,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13448
14011
|
if (missingPkgMatch && buildVerifyResult.status !== "timeout") {
|
|
13449
14012
|
const missingPkg = missingPkgMatch[1].replace(/^(@[^/]+\/[^/]+)\/.*$/, "$1").replace(/^([^@][^/]*)\/.*$/, "$1");
|
|
13450
14013
|
const resolvedRoot = projectRoot ?? process.cwd();
|
|
13451
|
-
logger$
|
|
14014
|
+
logger$26.warn({
|
|
13452
14015
|
storyKey,
|
|
13453
14016
|
missingPkg
|
|
13454
14017
|
}, "Build-fix retry: detected missing npm package — attempting npm install");
|
|
@@ -13459,7 +14022,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13459
14022
|
encoding: "utf-8",
|
|
13460
14023
|
stdio: "pipe"
|
|
13461
14024
|
});
|
|
13462
|
-
logger$
|
|
14025
|
+
logger$26.warn({
|
|
13463
14026
|
storyKey,
|
|
13464
14027
|
missingPkg
|
|
13465
14028
|
}, "Build-fix retry: npm install succeeded — retrying build verification");
|
|
@@ -13473,18 +14036,18 @@ function createImplementationOrchestrator(deps) {
|
|
|
13473
14036
|
retryPassed = true;
|
|
13474
14037
|
_buildPassed = true;
|
|
13475
14038
|
eventBus.emit("story:build-verification-passed", { storyKey });
|
|
13476
|
-
logger$
|
|
14039
|
+
logger$26.warn({
|
|
13477
14040
|
storyKey,
|
|
13478
14041
|
missingPkg
|
|
13479
14042
|
}, "Build-fix retry: build verification passed after installing missing package");
|
|
13480
|
-
} else logger$
|
|
14043
|
+
} else logger$26.warn({
|
|
13481
14044
|
storyKey,
|
|
13482
14045
|
missingPkg,
|
|
13483
14046
|
retryStatus: retryResult.status
|
|
13484
14047
|
}, "Build-fix retry: build still fails after installing missing package — escalating");
|
|
13485
14048
|
} catch (installErr) {
|
|
13486
14049
|
const installMsg = installErr instanceof Error ? installErr.message : String(installErr);
|
|
13487
|
-
logger$
|
|
14050
|
+
logger$26.warn({
|
|
13488
14051
|
storyKey,
|
|
13489
14052
|
missingPkg,
|
|
13490
14053
|
error: installMsg
|
|
@@ -13494,7 +14057,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13494
14057
|
if (!retryPassed) {
|
|
13495
14058
|
let buildFixPassed = false;
|
|
13496
14059
|
if (buildVerifyResult.status === "failed" && storyFilePath !== void 0) try {
|
|
13497
|
-
logger$
|
|
14060
|
+
logger$26.info({ storyKey }, "Dispatching build-fix agent");
|
|
13498
14061
|
startPhase(storyKey, "build-fix");
|
|
13499
14062
|
const storyContent = await readFile$1(storyFilePath, "utf-8");
|
|
13500
14063
|
let buildFixTemplate;
|
|
@@ -13531,11 +14094,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
13531
14094
|
buildFixPassed = true;
|
|
13532
14095
|
_buildPassed = true;
|
|
13533
14096
|
eventBus.emit("story:build-verification-passed", { storyKey });
|
|
13534
|
-
logger$
|
|
13535
|
-
} else logger$
|
|
14097
|
+
logger$26.info({ storyKey }, "Build passed after build-fix dispatch");
|
|
14098
|
+
} else logger$26.warn({ storyKey }, "Build still fails after build-fix dispatch — escalating");
|
|
13536
14099
|
} catch (fixErr) {
|
|
13537
14100
|
const fixMsg = fixErr instanceof Error ? fixErr.message : String(fixErr);
|
|
13538
|
-
logger$
|
|
14101
|
+
logger$26.warn({
|
|
13539
14102
|
storyKey,
|
|
13540
14103
|
error: fixMsg
|
|
13541
14104
|
}, "Build-fix dispatch failed — escalating");
|
|
@@ -13546,7 +14109,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13546
14109
|
exitCode: buildVerifyResult.exitCode ?? 1,
|
|
13547
14110
|
output: truncatedOutput
|
|
13548
14111
|
});
|
|
13549
|
-
logger$
|
|
14112
|
+
logger$26.warn({
|
|
13550
14113
|
storyKey,
|
|
13551
14114
|
reason,
|
|
13552
14115
|
exitCode: buildVerifyResult.exitCode
|
|
@@ -13578,7 +14141,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13578
14141
|
storyKey
|
|
13579
14142
|
});
|
|
13580
14143
|
if (icResult.potentiallyAffectedTests.length > 0) {
|
|
13581
|
-
logger$
|
|
14144
|
+
logger$26.warn({
|
|
13582
14145
|
storyKey,
|
|
13583
14146
|
modifiedInterfaces: icResult.modifiedInterfaces,
|
|
13584
14147
|
potentiallyAffectedTests: icResult.potentiallyAffectedTests
|
|
@@ -13659,7 +14222,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13659
14222
|
"NEEDS_MAJOR_REWORK": 2
|
|
13660
14223
|
};
|
|
13661
14224
|
for (const group of batchFileGroups) {
|
|
13662
|
-
logger$
|
|
14225
|
+
logger$26.info({
|
|
13663
14226
|
storyKey,
|
|
13664
14227
|
batchIndex: group.batchIndex,
|
|
13665
14228
|
fileCount: group.files.length
|
|
@@ -13704,7 +14267,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13704
14267
|
rawOutput: lastRawOutput,
|
|
13705
14268
|
tokenUsage: aggregateTokens
|
|
13706
14269
|
};
|
|
13707
|
-
logger$
|
|
14270
|
+
logger$26.info({
|
|
13708
14271
|
storyKey,
|
|
13709
14272
|
batchCount: batchFileGroups.length,
|
|
13710
14273
|
verdict: worstVerdict,
|
|
@@ -13747,7 +14310,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13747
14310
|
storyKey,
|
|
13748
14311
|
reviewCycle: reviewCycles
|
|
13749
14312
|
})
|
|
13750
|
-
})).catch((tokenErr) => logger$
|
|
14313
|
+
})).catch((tokenErr) => logger$26.warn({
|
|
13751
14314
|
storyKey,
|
|
13752
14315
|
err: tokenErr
|
|
13753
14316
|
}, "Failed to record code-review token usage"));
|
|
@@ -13755,7 +14318,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13755
14318
|
const isPhantomReview = reviewResult.dispatchFailed === true || reviewResult.verdict !== "SHIP_IT" && reviewResult.verdict !== "LGTM_WITH_NOTES" && (reviewResult.issue_list === void 0 || reviewResult.issue_list.length === 0) && reviewResult.error !== void 0;
|
|
13756
14319
|
if (isPhantomReview && !timeoutRetried) {
|
|
13757
14320
|
timeoutRetried = true;
|
|
13758
|
-
logger$
|
|
14321
|
+
logger$26.warn({
|
|
13759
14322
|
storyKey,
|
|
13760
14323
|
reviewCycles,
|
|
13761
14324
|
error: reviewResult.error
|
|
@@ -13763,7 +14326,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13763
14326
|
continue;
|
|
13764
14327
|
}
|
|
13765
14328
|
if (isPhantomReview && timeoutRetried) {
|
|
13766
|
-
logger$
|
|
14329
|
+
logger$26.warn({
|
|
13767
14330
|
storyKey,
|
|
13768
14331
|
reviewCycles,
|
|
13769
14332
|
error: reviewResult.error
|
|
@@ -13787,7 +14350,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13787
14350
|
verdict = reviewResult.verdict;
|
|
13788
14351
|
issueList = reviewResult.issue_list ?? [];
|
|
13789
14352
|
if (verdict === "NEEDS_MAJOR_REWORK" && reviewCycles > 0 && previousIssueList.length > 0 && issueList.length < previousIssueList.length) {
|
|
13790
|
-
logger$
|
|
14353
|
+
logger$26.info({
|
|
13791
14354
|
storyKey,
|
|
13792
14355
|
originalVerdict: verdict,
|
|
13793
14356
|
issuesBefore: previousIssueList.length,
|
|
@@ -13823,7 +14386,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13823
14386
|
if (_decomposition !== void 0) parts.push(`decomposed: ${_decomposition.batchCount} batches`);
|
|
13824
14387
|
parts.push(`${fileCount} files`);
|
|
13825
14388
|
parts.push(`${totalTokensK} tokens`);
|
|
13826
|
-
logger$
|
|
14389
|
+
logger$26.info({
|
|
13827
14390
|
storyKey,
|
|
13828
14391
|
verdict,
|
|
13829
14392
|
agentVerdict: reviewResult.agentVerdict
|
|
@@ -13856,7 +14419,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13856
14419
|
rawOutput: reviewResult.rawOutput
|
|
13857
14420
|
} : void 0;
|
|
13858
14421
|
let sourceEpicContent;
|
|
13859
|
-
const epicsPath1 =
|
|
14422
|
+
const epicsPath1 = findEpicFileForStory(projectRoot ?? process.cwd(), storyKey);
|
|
13860
14423
|
if (epicsPath1) try {
|
|
13861
14424
|
const epicFull = readFileSync(epicsPath1, "utf-8");
|
|
13862
14425
|
const section = extractStorySection(epicFull, storyKey);
|
|
@@ -13880,7 +14443,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13880
14443
|
phase: "VERIFICATION_FAILED",
|
|
13881
14444
|
completedAt: new Date().toISOString()
|
|
13882
14445
|
});
|
|
13883
|
-
persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$
|
|
14446
|
+
persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
|
|
13884
14447
|
err,
|
|
13885
14448
|
storyKey
|
|
13886
14449
|
}, "StateStore write failed after verification-failed"));
|
|
@@ -13894,7 +14457,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13894
14457
|
completedAt: new Date().toISOString()
|
|
13895
14458
|
});
|
|
13896
14459
|
if (config.skipVerification !== true && runManifest != null) Promise.resolve().then(() => runManifest.read()).then((manifest) => {
|
|
13897
|
-
if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$
|
|
14460
|
+
if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$26.warn({
|
|
13898
14461
|
storyKey,
|
|
13899
14462
|
category: "verification-result-missing"
|
|
13900
14463
|
}, "post-COMPLETE invariant: verification_result absent in manifest");
|
|
@@ -13919,9 +14482,9 @@ function createImplementationOrchestrator(deps) {
|
|
|
13919
14482
|
}),
|
|
13920
14483
|
rationale: `Advisory notes from LGTM_WITH_NOTES review of ${storyKey}`
|
|
13921
14484
|
});
|
|
13922
|
-
logger$
|
|
14485
|
+
logger$26.info({ storyKey }, "Advisory notes persisted to decision store");
|
|
13923
14486
|
} catch (advisoryErr) {
|
|
13924
|
-
logger$
|
|
14487
|
+
logger$26.warn({
|
|
13925
14488
|
storyKey,
|
|
13926
14489
|
error: advisoryErr instanceof Error ? advisoryErr.message : String(advisoryErr)
|
|
13927
14490
|
}, "Failed to persist advisory notes (best-effort)");
|
|
@@ -13929,27 +14492,27 @@ function createImplementationOrchestrator(deps) {
|
|
|
13929
14492
|
if (telemetryPersistence !== void 0) try {
|
|
13930
14493
|
const turns = await telemetryPersistence.getTurnAnalysis(storyKey);
|
|
13931
14494
|
if (turns.length > 0) {
|
|
13932
|
-
const scorer = new EfficiencyScorer(logger$
|
|
14495
|
+
const scorer = new EfficiencyScorer(logger$26);
|
|
13933
14496
|
const effScore = scorer.score(storyKey, turns);
|
|
13934
14497
|
await telemetryPersistence.storeEfficiencyScore(effScore);
|
|
13935
|
-
logger$
|
|
14498
|
+
logger$26.info({
|
|
13936
14499
|
storyKey,
|
|
13937
14500
|
compositeScore: effScore.compositeScore,
|
|
13938
14501
|
modelCount: effScore.perModelBreakdown.length
|
|
13939
14502
|
}, "Efficiency score computed and persisted");
|
|
13940
|
-
} else logger$
|
|
14503
|
+
} else logger$26.debug({ storyKey }, "No turn analysis data available — skipping efficiency scoring");
|
|
13941
14504
|
} catch (effErr) {
|
|
13942
|
-
logger$
|
|
14505
|
+
logger$26.warn({
|
|
13943
14506
|
storyKey,
|
|
13944
14507
|
error: effErr instanceof Error ? effErr.message : String(effErr)
|
|
13945
14508
|
}, "Efficiency scoring failed — story verdict unchanged");
|
|
13946
14509
|
}
|
|
13947
14510
|
if (telemetryPersistence !== void 0) try {
|
|
13948
14511
|
const turns = await telemetryPersistence.getTurnAnalysis(storyKey);
|
|
13949
|
-
if (turns.length === 0) logger$
|
|
14512
|
+
if (turns.length === 0) logger$26.debug({ storyKey }, "No turn analysis data for telemetry categorization — skipping");
|
|
13950
14513
|
else {
|
|
13951
|
-
const categorizer = new Categorizer(logger$
|
|
13952
|
-
const consumerAnalyzer = new ConsumerAnalyzer(categorizer, logger$
|
|
14514
|
+
const categorizer = new Categorizer(logger$26);
|
|
14515
|
+
const consumerAnalyzer = new ConsumerAnalyzer(categorizer, logger$26);
|
|
13953
14516
|
const categoryStats = categorizer.computeCategoryStatsFromTurns(turns);
|
|
13954
14517
|
const consumerStats = consumerAnalyzer.analyzeFromTurns(turns);
|
|
13955
14518
|
await telemetryPersistence.storeCategoryStats(storyKey, categoryStats);
|
|
@@ -13957,7 +14520,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13957
14520
|
const growingCount = categoryStats.filter((c) => c.trend === "growing").length;
|
|
13958
14521
|
const topCategory = categoryStats[0]?.category ?? "none";
|
|
13959
14522
|
const topConsumer = consumerStats[0]?.consumerKey ?? "none";
|
|
13960
|
-
logger$
|
|
14523
|
+
logger$26.info({
|
|
13961
14524
|
storyKey,
|
|
13962
14525
|
topCategory,
|
|
13963
14526
|
topConsumer,
|
|
@@ -13965,7 +14528,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13965
14528
|
}, "Semantic categorization and consumer analysis complete");
|
|
13966
14529
|
}
|
|
13967
14530
|
} catch (catErr) {
|
|
13968
|
-
logger$
|
|
14531
|
+
logger$26.warn({
|
|
13969
14532
|
storyKey,
|
|
13970
14533
|
error: catErr instanceof Error ? catErr.message : String(catErr)
|
|
13971
14534
|
}, "Semantic categorization failed — story verdict unchanged");
|
|
@@ -13987,7 +14550,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
13987
14550
|
filesModified: devFilesModified,
|
|
13988
14551
|
workingDirectory: projectRoot
|
|
13989
14552
|
});
|
|
13990
|
-
logger$
|
|
14553
|
+
logger$26.debug({
|
|
13991
14554
|
storyKey,
|
|
13992
14555
|
expansion_priority: expansionResult.expansion_priority,
|
|
13993
14556
|
coverage_gaps: expansionResult.coverage_gaps.length
|
|
@@ -14000,7 +14563,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14000
14563
|
value: JSON.stringify(expansionResult)
|
|
14001
14564
|
});
|
|
14002
14565
|
} catch (expansionErr) {
|
|
14003
|
-
logger$
|
|
14566
|
+
logger$26.warn({
|
|
14004
14567
|
storyKey,
|
|
14005
14568
|
error: expansionErr instanceof Error ? expansionErr.message : String(expansionErr)
|
|
14006
14569
|
}, "Test expansion failed — story verdict unchanged");
|
|
@@ -14027,7 +14590,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14027
14590
|
await persistState();
|
|
14028
14591
|
return;
|
|
14029
14592
|
}
|
|
14030
|
-
logger$
|
|
14593
|
+
logger$26.info({
|
|
14031
14594
|
storyKey,
|
|
14032
14595
|
reviewCycles: finalReviewCycles,
|
|
14033
14596
|
issueCount: issueList.length
|
|
@@ -14093,7 +14656,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14093
14656
|
fixPrompt = assembled.prompt;
|
|
14094
14657
|
} catch {
|
|
14095
14658
|
fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, minor fixes needed`;
|
|
14096
|
-
logger$
|
|
14659
|
+
logger$26.warn({ storyKey }, "Failed to assemble auto-approve fix prompt, using fallback");
|
|
14097
14660
|
}
|
|
14098
14661
|
const handle = dispatcher.dispatch({
|
|
14099
14662
|
prompt: fixPrompt,
|
|
@@ -14114,9 +14677,9 @@ function createImplementationOrchestrator(deps) {
|
|
|
14114
14677
|
output: fixResult.tokenEstimate.output
|
|
14115
14678
|
} : void 0 }
|
|
14116
14679
|
});
|
|
14117
|
-
if (fixResult.status === "timeout") logger$
|
|
14680
|
+
if (fixResult.status === "timeout") logger$26.warn({ storyKey }, "Auto-approve fix timed out — approving anyway (issues were minor)");
|
|
14118
14681
|
} catch (err) {
|
|
14119
|
-
logger$
|
|
14682
|
+
logger$26.warn({
|
|
14120
14683
|
storyKey,
|
|
14121
14684
|
err
|
|
14122
14685
|
}, "Auto-approve fix dispatch failed — approving anyway (issues were minor)");
|
|
@@ -14129,7 +14692,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14129
14692
|
rawOutput: reviewResult.rawOutput
|
|
14130
14693
|
} : void 0;
|
|
14131
14694
|
let sourceEpicContent2;
|
|
14132
|
-
const epicsPath2 =
|
|
14695
|
+
const epicsPath2 = findEpicFileForStory(projectRoot ?? process.cwd(), storyKey);
|
|
14133
14696
|
if (epicsPath2) try {
|
|
14134
14697
|
const epicFull2 = readFileSync(epicsPath2, "utf-8");
|
|
14135
14698
|
const section2 = extractStorySection(epicFull2, storyKey);
|
|
@@ -14153,7 +14716,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14153
14716
|
phase: "VERIFICATION_FAILED",
|
|
14154
14717
|
completedAt: new Date().toISOString()
|
|
14155
14718
|
});
|
|
14156
|
-
persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$
|
|
14719
|
+
persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
|
|
14157
14720
|
err,
|
|
14158
14721
|
storyKey
|
|
14159
14722
|
}, "StateStore write failed after verification-failed"));
|
|
@@ -14176,7 +14739,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14176
14739
|
completedAt: new Date().toISOString()
|
|
14177
14740
|
});
|
|
14178
14741
|
if (config.skipVerification !== true && runManifest != null) Promise.resolve().then(() => runManifest.read()).then((manifest) => {
|
|
14179
|
-
if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$
|
|
14742
|
+
if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$26.warn({
|
|
14180
14743
|
storyKey,
|
|
14181
14744
|
category: "verification-result-missing"
|
|
14182
14745
|
}, "post-COMPLETE invariant: verification_result absent in manifest");
|
|
@@ -14201,7 +14764,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14201
14764
|
outcome: "retried",
|
|
14202
14765
|
cost_usd: 0,
|
|
14203
14766
|
timestamp: new Date().toISOString()
|
|
14204
|
-
}).catch((err) => logger$
|
|
14767
|
+
}).catch((err) => logger$26.warn({
|
|
14205
14768
|
err,
|
|
14206
14769
|
storyKey
|
|
14207
14770
|
}, "appendRecoveryEntry failed — pipeline continues"));
|
|
@@ -14341,7 +14904,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14341
14904
|
fixPrompt = assembled.prompt;
|
|
14342
14905
|
} catch {
|
|
14343
14906
|
fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, taskType=${taskType}`;
|
|
14344
|
-
logger$
|
|
14907
|
+
logger$26.warn({
|
|
14345
14908
|
storyKey,
|
|
14346
14909
|
taskType
|
|
14347
14910
|
}, "Failed to assemble fix prompt, using fallback");
|
|
@@ -14378,7 +14941,86 @@ function createImplementationOrchestrator(deps) {
|
|
|
14378
14941
|
} : void 0 }
|
|
14379
14942
|
});
|
|
14380
14943
|
if (fixResult.status === "timeout") {
|
|
14381
|
-
|
|
14944
|
+
if (taskType === "minor-fixes") {
|
|
14945
|
+
const finalReviewCycles = reviewCycles + 1;
|
|
14946
|
+
const downgradedVerdict = "LGTM_WITH_NOTES";
|
|
14947
|
+
logger$26.warn({
|
|
14948
|
+
storyKey,
|
|
14949
|
+
reviewCycles: finalReviewCycles,
|
|
14950
|
+
issueCount: issueList.length
|
|
14951
|
+
}, "Minor-fixes dispatch timed out — auto-approving as LGTM_WITH_NOTES (original findings retained as warnings)");
|
|
14952
|
+
endPhase(storyKey, "code-review");
|
|
14953
|
+
if (config.skipVerification !== true) {
|
|
14954
|
+
const latestReviewSignals = reviewResult != null ? {
|
|
14955
|
+
dispatchFailed: reviewResult.dispatchFailed,
|
|
14956
|
+
error: reviewResult.error,
|
|
14957
|
+
rawOutput: reviewResult.rawOutput
|
|
14958
|
+
} : void 0;
|
|
14959
|
+
let sourceEpicContent3;
|
|
14960
|
+
const epicsPath3 = findEpicFileForStory(projectRoot ?? process.cwd(), storyKey);
|
|
14961
|
+
if (epicsPath3) try {
|
|
14962
|
+
const epicFull3 = readFileSync(epicsPath3, "utf-8");
|
|
14963
|
+
const section3 = extractStorySection(epicFull3, storyKey);
|
|
14964
|
+
if (section3) sourceEpicContent3 = section3;
|
|
14965
|
+
} catch {}
|
|
14966
|
+
await persistDevStorySignals(storyKey, devStorySignals, runManifest);
|
|
14967
|
+
const verifContext = assembleVerificationContext({
|
|
14968
|
+
storyKey,
|
|
14969
|
+
workingDir: projectRoot ?? process.cwd(),
|
|
14970
|
+
reviewResult: latestReviewSignals,
|
|
14971
|
+
storyContent: storyContentForVerification,
|
|
14972
|
+
devStoryResult: devStorySignals,
|
|
14973
|
+
outputTokenCount: devOutputTokenCount,
|
|
14974
|
+
sourceEpicContent: sourceEpicContent3
|
|
14975
|
+
});
|
|
14976
|
+
const verifSummary = await verificationPipeline.run(verifContext, "A");
|
|
14977
|
+
verificationStore.set(storyKey, verifSummary);
|
|
14978
|
+
await persistVerificationResult(storyKey, verifSummary, runManifest);
|
|
14979
|
+
if (verifSummary.status === "fail") {
|
|
14980
|
+
updateStory(storyKey, {
|
|
14981
|
+
phase: "VERIFICATION_FAILED",
|
|
14982
|
+
completedAt: new Date().toISOString()
|
|
14983
|
+
});
|
|
14984
|
+
persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
|
|
14985
|
+
err,
|
|
14986
|
+
storyKey
|
|
14987
|
+
}, "StateStore write failed after verification-failed"));
|
|
14988
|
+
await writeStoryMetricsBestEffort(storyKey, "verification-failed", finalReviewCycles);
|
|
14989
|
+
await persistState();
|
|
14990
|
+
return;
|
|
14991
|
+
}
|
|
14992
|
+
}
|
|
14993
|
+
eventBus.emit("story:auto-approved", {
|
|
14994
|
+
storyKey,
|
|
14995
|
+
verdict: downgradedVerdict,
|
|
14996
|
+
reviewCycles: finalReviewCycles,
|
|
14997
|
+
maxReviewCycles: config.maxReviewCycles,
|
|
14998
|
+
issueCount: issueList.length,
|
|
14999
|
+
reason: `Minor-fixes dispatch timed out (cycle ${finalReviewCycles}) — auto-approving as LGTM_WITH_NOTES with original findings retained as warnings`
|
|
15000
|
+
});
|
|
15001
|
+
updateStory(storyKey, {
|
|
15002
|
+
phase: "COMPLETE",
|
|
15003
|
+
reviewCycles: finalReviewCycles,
|
|
15004
|
+
lastVerdict: downgradedVerdict,
|
|
15005
|
+
completedAt: new Date().toISOString()
|
|
15006
|
+
});
|
|
15007
|
+
if (config.skipVerification !== true && runManifest != null) Promise.resolve().then(() => runManifest.read()).then((manifest) => {
|
|
15008
|
+
if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$26.warn({
|
|
15009
|
+
storyKey,
|
|
15010
|
+
category: "verification-result-missing"
|
|
15011
|
+
}, "post-COMPLETE invariant: verification_result absent in manifest");
|
|
15012
|
+
}).catch(() => {});
|
|
15013
|
+
await writeStoryMetricsBestEffort(storyKey, downgradedVerdict, finalReviewCycles);
|
|
15014
|
+
await writeStoryOutcomeBestEffort(storyKey, "complete", finalReviewCycles);
|
|
15015
|
+
eventBus.emit("orchestrator:story-complete", {
|
|
15016
|
+
storyKey,
|
|
15017
|
+
reviewCycles: finalReviewCycles
|
|
15018
|
+
});
|
|
15019
|
+
await persistState();
|
|
15020
|
+
keepReviewing = false;
|
|
15021
|
+
return;
|
|
15022
|
+
}
|
|
15023
|
+
logger$26.warn({
|
|
14382
15024
|
storyKey,
|
|
14383
15025
|
taskType
|
|
14384
15026
|
}, "Fix dispatch timed out — escalating story");
|
|
@@ -14400,7 +15042,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14400
15042
|
}
|
|
14401
15043
|
if (fixResult.status === "failed") {
|
|
14402
15044
|
if (isMajorRework) {
|
|
14403
|
-
logger$
|
|
15045
|
+
logger$26.warn({
|
|
14404
15046
|
storyKey,
|
|
14405
15047
|
exitCode: fixResult.exitCode
|
|
14406
15048
|
}, "Major rework dispatch failed — escalating story");
|
|
@@ -14420,7 +15062,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14420
15062
|
await persistState();
|
|
14421
15063
|
return;
|
|
14422
15064
|
}
|
|
14423
|
-
logger$
|
|
15065
|
+
logger$26.warn({
|
|
14424
15066
|
storyKey,
|
|
14425
15067
|
taskType,
|
|
14426
15068
|
exitCode: fixResult.exitCode
|
|
@@ -14428,7 +15070,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14428
15070
|
}
|
|
14429
15071
|
if (isMajorRework) replaceDevStorySignals(fixResult.parsed);
|
|
14430
15072
|
} catch (err) {
|
|
14431
|
-
logger$
|
|
15073
|
+
logger$26.warn({
|
|
14432
15074
|
storyKey,
|
|
14433
15075
|
taskType,
|
|
14434
15076
|
err
|
|
@@ -14479,7 +15121,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14479
15121
|
...haltOn !== "none" ? { severity: "critical" } : {}
|
|
14480
15122
|
});
|
|
14481
15123
|
_budgetExhausted = true;
|
|
14482
|
-
logger$
|
|
15124
|
+
logger$26.warn({
|
|
14483
15125
|
skipped: allSkipped.length,
|
|
14484
15126
|
cumulative: result.cumulative,
|
|
14485
15127
|
ceiling: result.ceiling
|
|
@@ -14496,7 +15138,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14496
15138
|
const completedStoryKeys = [];
|
|
14497
15139
|
for (const storyKey of group) {
|
|
14498
15140
|
if (_shutdownRequested) {
|
|
14499
|
-
logger$
|
|
15141
|
+
logger$26.info({ storyKey }, "shutdown requested — skipping dispatch");
|
|
14500
15142
|
return;
|
|
14501
15143
|
}
|
|
14502
15144
|
if (runManifest !== null && runManifest !== void 0) try {
|
|
@@ -14519,7 +15161,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14519
15161
|
}
|
|
14520
15162
|
}
|
|
14521
15163
|
} catch (err) {
|
|
14522
|
-
logger$
|
|
15164
|
+
logger$26.debug({ err }, "Cost ceiling check failed — proceeding without enforcement");
|
|
14523
15165
|
}
|
|
14524
15166
|
let optimizationDirectives;
|
|
14525
15167
|
if (telemetryAdvisor !== void 0 && completedStoryKeys.length > 0) try {
|
|
@@ -14527,13 +15169,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
14527
15169
|
const directives = telemetryAdvisor.formatOptimizationDirectives(recs);
|
|
14528
15170
|
if (directives.length > 0) {
|
|
14529
15171
|
optimizationDirectives = directives;
|
|
14530
|
-
logger$
|
|
15172
|
+
logger$26.debug({
|
|
14531
15173
|
storyKey,
|
|
14532
15174
|
directiveCount: recs.filter((r) => r.severity !== "info").length
|
|
14533
15175
|
}, "Optimization directives ready for dispatch");
|
|
14534
15176
|
}
|
|
14535
15177
|
} catch (err) {
|
|
14536
|
-
logger$
|
|
15178
|
+
logger$26.debug({
|
|
14537
15179
|
err,
|
|
14538
15180
|
storyKey
|
|
14539
15181
|
}, "Failed to fetch optimization directives — proceeding without");
|
|
@@ -14588,7 +15230,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14588
15230
|
async function shutdownGracefully(reason, signal) {
|
|
14589
15231
|
if (_shutdownRequested) return;
|
|
14590
15232
|
_shutdownRequested = true;
|
|
14591
|
-
logger$
|
|
15233
|
+
logger$26.info({
|
|
14592
15234
|
reason,
|
|
14593
15235
|
signal
|
|
14594
15236
|
}, "Graceful shutdown initiated — stopping new dispatches");
|
|
@@ -14598,8 +15240,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
14598
15240
|
run_status: "stopped",
|
|
14599
15241
|
stopped_reason: reason,
|
|
14600
15242
|
stopped_at: new Date().toISOString()
|
|
14601
|
-
}).catch((err) => logger$
|
|
14602
|
-
if (config.pipelineRunId !== void 0) await updatePipelineRun(db, config.pipelineRunId, { status: "stopped" }).catch((err) => logger$
|
|
15243
|
+
}).catch((err) => logger$26.warn({ err }, "patchRunStatus failed during shutdown (best-effort)"));
|
|
15244
|
+
if (config.pipelineRunId !== void 0) await updatePipelineRun(db, config.pipelineRunId, { status: "stopped" }).catch((err) => logger$26.warn({ err }, "updatePipelineRun(stopped) failed during shutdown (best-effort)"));
|
|
14603
15245
|
const activePhases = [
|
|
14604
15246
|
"PENDING",
|
|
14605
15247
|
"IN_STORY_CREATION",
|
|
@@ -14610,7 +15252,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14610
15252
|
"CHECKPOINT"
|
|
14611
15253
|
];
|
|
14612
15254
|
const cancellations = [];
|
|
14613
|
-
for (const [storyKey, state] of _stories.entries()) if (activePhases.includes(state.phase)) cancellations.push(wgRepo.updateStoryStatus(storyKey, "cancelled").catch((err) => logger$
|
|
15255
|
+
for (const [storyKey, state] of _stories.entries()) if (activePhases.includes(state.phase)) cancellations.push(wgRepo.updateStoryStatus(storyKey, "cancelled").catch((err) => logger$26.warn({
|
|
14614
15256
|
err,
|
|
14615
15257
|
storyKey
|
|
14616
15258
|
}, "wg_stories → cancelled failed during shutdown (best-effort)")));
|
|
@@ -14619,11 +15261,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
14619
15261
|
}
|
|
14620
15262
|
async function run(storyKeys) {
|
|
14621
15263
|
if (_state === "RUNNING" || _state === "PAUSED") {
|
|
14622
|
-
logger$
|
|
15264
|
+
logger$26.warn({ state: _state }, "run() called while orchestrator is already running or paused — ignoring");
|
|
14623
15265
|
return getStatus();
|
|
14624
15266
|
}
|
|
14625
15267
|
if (_state === "COMPLETE") {
|
|
14626
|
-
logger$
|
|
15268
|
+
logger$26.warn({ state: _state }, "run() called on a COMPLETE orchestrator — ignoring");
|
|
14627
15269
|
return getStatus();
|
|
14628
15270
|
}
|
|
14629
15271
|
_state = "RUNNING";
|
|
@@ -14647,7 +15289,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14647
15289
|
const seedStart = Date.now();
|
|
14648
15290
|
const seedResult = await seedMethodologyContext(db, projectRoot);
|
|
14649
15291
|
_startupTimings.seedMethodologyMs = Date.now() - seedStart;
|
|
14650
|
-
if (seedResult.decisionsCreated > 0) logger$
|
|
15292
|
+
if (seedResult.decisionsCreated > 0) logger$26.info({
|
|
14651
15293
|
decisionsCreated: seedResult.decisionsCreated,
|
|
14652
15294
|
skippedCategories: seedResult.skippedCategories,
|
|
14653
15295
|
durationMs: _startupTimings.seedMethodologyMs
|
|
@@ -14657,12 +15299,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
14657
15299
|
const ingestStart = Date.now();
|
|
14658
15300
|
try {
|
|
14659
15301
|
const ingestResult = await autoIngestEpicsDependencies(db, projectRoot);
|
|
14660
|
-
if (ingestResult.storiesIngested > 0 || ingestResult.dependenciesIngested > 0) logger$
|
|
15302
|
+
if (ingestResult.storiesIngested > 0 || ingestResult.dependenciesIngested > 0) logger$26.info({
|
|
14661
15303
|
...ingestResult,
|
|
14662
15304
|
durationMs: Date.now() - ingestStart
|
|
14663
15305
|
}, "Auto-ingested stories and dependencies from epics document");
|
|
14664
15306
|
} catch (err) {
|
|
14665
|
-
logger$
|
|
15307
|
+
logger$26.debug({ err }, "Auto-ingest from epics document skipped — work graph may be unavailable");
|
|
14666
15308
|
}
|
|
14667
15309
|
}
|
|
14668
15310
|
const sigtermHandler = () => {
|
|
@@ -14680,7 +15322,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14680
15322
|
_startupTimings.stateStoreInitMs = Date.now() - stateStoreInitStart;
|
|
14681
15323
|
for (const key of storyKeys) {
|
|
14682
15324
|
const pendingState = _stories.get(key);
|
|
14683
|
-
if (pendingState !== void 0) persistStoryState(key, pendingState).catch((err) => logger$
|
|
15325
|
+
if (pendingState !== void 0) persistStoryState(key, pendingState).catch((err) => logger$26.warn({
|
|
14684
15326
|
err,
|
|
14685
15327
|
storyKey: key
|
|
14686
15328
|
}, "StateStore write failed during PENDING init"));
|
|
@@ -14691,12 +15333,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
14691
15333
|
_startupTimings.queryStoriesMs = Date.now() - queryStoriesStart;
|
|
14692
15334
|
for (const record of existingRecords) _stateStoreCache.set(record.storyKey, record);
|
|
14693
15335
|
} catch (err) {
|
|
14694
|
-
logger$
|
|
15336
|
+
logger$26.warn({ err }, "StateStore.queryStories() failed during init — status merge will be empty (best-effort)");
|
|
14695
15337
|
}
|
|
14696
15338
|
}
|
|
14697
15339
|
if (ingestionServer !== void 0) {
|
|
14698
15340
|
if (telemetryPersistence !== void 0) try {
|
|
14699
|
-
const pipelineLogger = logger$
|
|
15341
|
+
const pipelineLogger = logger$26;
|
|
14700
15342
|
const telemetryPipeline = new TelemetryPipeline({
|
|
14701
15343
|
normalizer: new TelemetryNormalizer(pipelineLogger),
|
|
14702
15344
|
turnAnalyzer: new TurnAnalyzer(pipelineLogger),
|
|
@@ -14708,14 +15350,14 @@ function createImplementationOrchestrator(deps) {
|
|
|
14708
15350
|
persistence: telemetryPersistence
|
|
14709
15351
|
});
|
|
14710
15352
|
ingestionServer.setPipeline(telemetryPipeline);
|
|
14711
|
-
logger$
|
|
15353
|
+
logger$26.info("TelemetryPipeline wired to IngestionServer");
|
|
14712
15354
|
} catch (pipelineErr) {
|
|
14713
|
-
logger$
|
|
15355
|
+
logger$26.warn({ err: pipelineErr }, "Failed to create TelemetryPipeline — continuing without analysis pipeline");
|
|
14714
15356
|
}
|
|
14715
|
-
await ingestionServer.start().catch((err) => logger$
|
|
15357
|
+
await ingestionServer.start().catch((err) => logger$26.warn({ err }, "IngestionServer.start() failed — continuing without telemetry (best-effort)"));
|
|
14716
15358
|
try {
|
|
14717
15359
|
_otlpEndpoint = ingestionServer.getOtlpEnvVars().OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
14718
|
-
logger$
|
|
15360
|
+
logger$26.info({ otlpEndpoint: _otlpEndpoint }, "OTLP telemetry ingestion active");
|
|
14719
15361
|
} catch {}
|
|
14720
15362
|
}
|
|
14721
15363
|
let contractDeclarations = [];
|
|
@@ -14755,12 +15397,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
14755
15397
|
const conflictDetectStart = Date.now();
|
|
14756
15398
|
const { batches, edges: contractEdges } = detectConflictGroupsWithContracts(storyKeys, { moduleMap: pack.manifest.conflictGroups }, contractDeclarations);
|
|
14757
15399
|
_startupTimings.conflictDetectMs = Date.now() - conflictDetectStart;
|
|
14758
|
-
if (contractEdges.length > 0) logger$
|
|
15400
|
+
if (contractEdges.length > 0) logger$26.info({
|
|
14759
15401
|
contractEdges,
|
|
14760
15402
|
edgeCount: contractEdges.length
|
|
14761
15403
|
}, "Contract dependency edges detected — applying contract-aware dispatch ordering");
|
|
14762
|
-
wgRepo.addContractDependencies(contractEdges).catch((err) => logger$
|
|
14763
|
-
logger$
|
|
15404
|
+
wgRepo.addContractDependencies(contractEdges).catch((err) => logger$26.warn({ err }, "contract dep persistence failed (best-effort)"));
|
|
15405
|
+
logger$26.info({
|
|
14764
15406
|
storyCount: storyKeys.length,
|
|
14765
15407
|
groupCount: batches.reduce((sum, b) => sum + b.length, 0),
|
|
14766
15408
|
batchCount: batches.length,
|
|
@@ -14770,7 +15412,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14770
15412
|
groups: batch.map((g) => g.join(","))
|
|
14771
15413
|
}))
|
|
14772
15414
|
}, "Orchestrator starting");
|
|
14773
|
-
logger$
|
|
15415
|
+
logger$26.info({
|
|
14774
15416
|
storyCount: storyKeys.length,
|
|
14775
15417
|
conflictGroups: batches.length,
|
|
14776
15418
|
maxConcurrency: config.maxConcurrency
|
|
@@ -14791,7 +15433,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14791
15433
|
exitCode,
|
|
14792
15434
|
output: truncatedOutput
|
|
14793
15435
|
});
|
|
14794
|
-
logger$
|
|
15436
|
+
logger$26.error({
|
|
14795
15437
|
exitCode,
|
|
14796
15438
|
reason: preFlightResult.reason
|
|
14797
15439
|
}, "Pre-flight build check failed — aborting pipeline before any story dispatch");
|
|
@@ -14800,19 +15442,19 @@ function createImplementationOrchestrator(deps) {
|
|
|
14800
15442
|
await persistState();
|
|
14801
15443
|
return getStatus();
|
|
14802
15444
|
}
|
|
14803
|
-
if (preFlightResult.status !== "skipped") logger$
|
|
15445
|
+
if (preFlightResult.status !== "skipped") logger$26.info("Pre-flight build check passed");
|
|
14804
15446
|
}
|
|
14805
|
-
logger$
|
|
15447
|
+
logger$26.info(_startupTimings, "Orchestrator startup timings (ms)");
|
|
14806
15448
|
const totalGroups = batches.reduce((sum, b) => sum + b.length, 0);
|
|
14807
15449
|
const actualConcurrency = Math.min(config.maxConcurrency, totalGroups);
|
|
14808
15450
|
if (actualConcurrency > 1 && projectRoot !== void 0) try {
|
|
14809
15451
|
_packageSnapshot = capturePackageSnapshot({ projectRoot });
|
|
14810
|
-
logger$
|
|
15452
|
+
logger$26.info({
|
|
14811
15453
|
fileCount: _packageSnapshot.files.size,
|
|
14812
15454
|
installCommand: _packageSnapshot.installCommand
|
|
14813
15455
|
}, "Package snapshot captured for concurrent story protection");
|
|
14814
15456
|
} catch (snapErr) {
|
|
14815
|
-
logger$
|
|
15457
|
+
logger$26.warn({ err: snapErr }, "Failed to capture package snapshot — continuing without protection");
|
|
14816
15458
|
}
|
|
14817
15459
|
try {
|
|
14818
15460
|
for (const batchGroups of batches) await runWithConcurrency(batchGroups, config.maxConcurrency);
|
|
@@ -14821,7 +15463,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14821
15463
|
_state = "FAILED";
|
|
14822
15464
|
_completedAt = new Date().toISOString();
|
|
14823
15465
|
await persistState();
|
|
14824
|
-
logger$
|
|
15466
|
+
logger$26.error({ err }, "Orchestrator failed with unhandled error");
|
|
14825
15467
|
return getStatus();
|
|
14826
15468
|
}
|
|
14827
15469
|
stopHeartbeat();
|
|
@@ -14831,7 +15473,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14831
15473
|
const totalDeclarations = contractDeclarations.length;
|
|
14832
15474
|
const currentSprintDeclarations = contractDeclarations.filter((d) => storyKeys.includes(d.storyKey));
|
|
14833
15475
|
const stalePruned = totalDeclarations - currentSprintDeclarations.length;
|
|
14834
|
-
if (stalePruned > 0) logger$
|
|
15476
|
+
if (stalePruned > 0) logger$26.info({
|
|
14835
15477
|
stalePruned,
|
|
14836
15478
|
remaining: currentSprintDeclarations.length
|
|
14837
15479
|
}, "Pruned stale contract declarations from previous epics");
|
|
@@ -14845,11 +15487,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
14845
15487
|
contractName: mismatch.contractName,
|
|
14846
15488
|
mismatchDescription: mismatch.mismatchDescription
|
|
14847
15489
|
});
|
|
14848
|
-
logger$
|
|
15490
|
+
logger$26.warn({
|
|
14849
15491
|
mismatchCount: mismatches.length,
|
|
14850
15492
|
mismatches
|
|
14851
15493
|
}, "Post-sprint contract verification found mismatches — manual review required");
|
|
14852
|
-
} else if (currentSprintDeclarations.length > 0) logger$
|
|
15494
|
+
} else if (currentSprintDeclarations.length > 0) logger$26.info("Post-sprint contract verification passed — all declared contracts satisfied");
|
|
14853
15495
|
eventBus.emit("pipeline:contract-verification-summary", {
|
|
14854
15496
|
verified: currentSprintDeclarations.length,
|
|
14855
15497
|
stalePruned,
|
|
@@ -14884,12 +15526,12 @@ function createImplementationOrchestrator(deps) {
|
|
|
14884
15526
|
});
|
|
14885
15527
|
await stateStore.setContractVerification(sk, records);
|
|
14886
15528
|
}
|
|
14887
|
-
logger$
|
|
15529
|
+
logger$26.info({ storyCount: contractsByStory.size }, "Contract verification results persisted to StateStore");
|
|
14888
15530
|
} catch (persistErr) {
|
|
14889
|
-
logger$
|
|
15531
|
+
logger$26.warn({ err: persistErr }, "Failed to persist contract verification results to StateStore");
|
|
14890
15532
|
}
|
|
14891
15533
|
} catch (err) {
|
|
14892
|
-
logger$
|
|
15534
|
+
logger$26.error({ err }, "Post-sprint contract verification threw an error — skipping");
|
|
14893
15535
|
}
|
|
14894
15536
|
if (projectRoot !== void 0) try {
|
|
14895
15537
|
const indicators = checkProfileStaleness(projectRoot);
|
|
@@ -14899,10 +15541,10 @@ function createImplementationOrchestrator(deps) {
|
|
|
14899
15541
|
message,
|
|
14900
15542
|
indicators
|
|
14901
15543
|
});
|
|
14902
|
-
logger$
|
|
15544
|
+
logger$26.warn({ indicators }, message);
|
|
14903
15545
|
}
|
|
14904
15546
|
} catch (err) {
|
|
14905
|
-
logger$
|
|
15547
|
+
logger$26.debug({ err }, "Profile staleness check failed (best-effort)");
|
|
14906
15548
|
}
|
|
14907
15549
|
let completed = 0;
|
|
14908
15550
|
let escalated = 0;
|
|
@@ -14921,8 +15563,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
14921
15563
|
} finally {
|
|
14922
15564
|
process.off("SIGTERM", sigtermHandler);
|
|
14923
15565
|
process.off("SIGINT", sigintHandler);
|
|
14924
|
-
if (stateStore !== void 0) await stateStore.close().catch((err) => logger$
|
|
14925
|
-
if (ingestionServer !== void 0) await ingestionServer.stop().catch((err) => logger$
|
|
15566
|
+
if (stateStore !== void 0) await stateStore.close().catch((err) => logger$26.warn({ err }, "StateStore.close() failed (best-effort)"));
|
|
15567
|
+
if (ingestionServer !== void 0) await ingestionServer.stop().catch((err) => logger$26.warn({ err }, "IngestionServer.stop() failed (best-effort)"));
|
|
14926
15568
|
}
|
|
14927
15569
|
}
|
|
14928
15570
|
function pause() {
|
|
@@ -14931,7 +15573,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14931
15573
|
_pauseGate = createPauseGate();
|
|
14932
15574
|
_state = "PAUSED";
|
|
14933
15575
|
eventBus.emit("orchestrator:paused", {});
|
|
14934
|
-
logger$
|
|
15576
|
+
logger$26.info("Orchestrator paused");
|
|
14935
15577
|
}
|
|
14936
15578
|
function resume() {
|
|
14937
15579
|
if (_state !== "PAUSED") return;
|
|
@@ -14942,7 +15584,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
14942
15584
|
}
|
|
14943
15585
|
_state = "RUNNING";
|
|
14944
15586
|
eventBus.emit("orchestrator:resumed", {});
|
|
14945
|
-
logger$
|
|
15587
|
+
logger$26.info("Orchestrator resumed");
|
|
14946
15588
|
}
|
|
14947
15589
|
return {
|
|
14948
15590
|
run,
|
|
@@ -16358,8 +17000,8 @@ async function resolveContext(ref, deps, runId, params, stepOutputs) {
|
|
|
16358
17000
|
return params[key] ?? "";
|
|
16359
17001
|
}
|
|
16360
17002
|
if (source.startsWith("decision:")) {
|
|
16361
|
-
const path$
|
|
16362
|
-
const [phase, category] = path$
|
|
17003
|
+
const path$4 = source.slice(9);
|
|
17004
|
+
const [phase, category] = path$4.split(".");
|
|
16363
17005
|
if (!phase || !category) return "";
|
|
16364
17006
|
const decisions = await getDecisionsByPhaseForRun(deps.db, runId, phase);
|
|
16365
17007
|
const filtered = decisions.filter((d) => d.category === category);
|
|
@@ -16430,8 +17072,8 @@ async function runSteps(steps, deps, runId, phase, params) {
|
|
|
16430
17072
|
for (const ref of step.context) {
|
|
16431
17073
|
let value;
|
|
16432
17074
|
if (ref.source.startsWith("decision:")) {
|
|
16433
|
-
const path$
|
|
16434
|
-
const [decPhase, decCategory] = path$
|
|
17075
|
+
const path$4 = ref.source.slice(9);
|
|
17076
|
+
const [decPhase, decCategory] = path$4.split(".");
|
|
16435
17077
|
if (decPhase && decCategory) {
|
|
16436
17078
|
const decisions = await getDecisionsByPhaseForRun(deps.db, runId, decPhase);
|
|
16437
17079
|
const filtered = decisions.filter((d) => d.category === decCategory);
|
|
@@ -27478,8 +28120,8 @@ var require_uri_all = __commonJS({ "node_modules/uri-js/dist/es5/uri.all.js"(exp
|
|
|
27478
28120
|
wsComponents.secure = void 0;
|
|
27479
28121
|
}
|
|
27480
28122
|
if (wsComponents.resourceName) {
|
|
27481
|
-
var _wsComponents$resourc = wsComponents.resourceName.split("?"), _wsComponents$resourc2 = slicedToArray(_wsComponents$resourc, 2), path$
|
|
27482
|
-
wsComponents.path = path$
|
|
28123
|
+
var _wsComponents$resourc = wsComponents.resourceName.split("?"), _wsComponents$resourc2 = slicedToArray(_wsComponents$resourc, 2), path$4 = _wsComponents$resourc2[0], query = _wsComponents$resourc2[1];
|
|
28124
|
+
wsComponents.path = path$4 && path$4 !== "/" ? path$4 : void 0;
|
|
27483
28125
|
wsComponents.query = query;
|
|
27484
28126
|
wsComponents.resourceName = void 0;
|
|
27485
28127
|
}
|
|
@@ -27820,12 +28462,12 @@ var require_util = __commonJS({ "node_modules/ajv/lib/compile/util.js"(exports,
|
|
|
27820
28462
|
return "'" + escapeQuotes(str) + "'";
|
|
27821
28463
|
}
|
|
27822
28464
|
function getPathExpr(currentPath, expr, jsonPointers, isNumber) {
|
|
27823
|
-
var path$
|
|
27824
|
-
return joinPaths(currentPath, path$
|
|
28465
|
+
var path$4 = jsonPointers ? "'/' + " + expr + (isNumber ? "" : ".replace(/~/g, '~0').replace(/\\//g, '~1')") : isNumber ? "'[' + " + expr + " + ']'" : "'[\\'' + " + expr + " + '\\']'";
|
|
28466
|
+
return joinPaths(currentPath, path$4);
|
|
27825
28467
|
}
|
|
27826
28468
|
function getPath(currentPath, prop, jsonPointers) {
|
|
27827
|
-
var path$
|
|
27828
|
-
return joinPaths(currentPath, path$
|
|
28469
|
+
var path$4 = jsonPointers ? toQuotedString("/" + escapeJsonPointer(prop)) : toQuotedString(getProperty(prop));
|
|
28470
|
+
return joinPaths(currentPath, path$4);
|
|
27829
28471
|
}
|
|
27830
28472
|
var JSON_POINTER$1 = /^\/(?:[^~]|~0|~1)*$/;
|
|
27831
28473
|
var RELATIVE_JSON_POINTER$1 = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
|
|
@@ -31993,16 +32635,16 @@ var require_ajv = __commonJS({ "node_modules/ajv/lib/ajv.js"(exports, module) {
|
|
|
31993
32635
|
return metaOpts;
|
|
31994
32636
|
}
|
|
31995
32637
|
function setLogger(self) {
|
|
31996
|
-
var logger$
|
|
31997
|
-
if (logger$
|
|
32638
|
+
var logger$26 = self._opts.logger;
|
|
32639
|
+
if (logger$26 === false) self.logger = {
|
|
31998
32640
|
log: noop,
|
|
31999
32641
|
warn: noop,
|
|
32000
32642
|
error: noop
|
|
32001
32643
|
};
|
|
32002
32644
|
else {
|
|
32003
|
-
if (logger$
|
|
32004
|
-
if (!(typeof logger$
|
|
32005
|
-
self.logger = logger$
|
|
32645
|
+
if (logger$26 === void 0) logger$26 = console;
|
|
32646
|
+
if (!(typeof logger$26 == "object" && logger$26.log && logger$26.warn && logger$26.error)) throw new Error("logger must implement log, warn and error methods");
|
|
32647
|
+
self.logger = logger$26;
|
|
32006
32648
|
}
|
|
32007
32649
|
}
|
|
32008
32650
|
function noop() {}
|
|
@@ -32034,8 +32676,8 @@ var ToolRegistry = class {
|
|
|
32034
32676
|
if (!valid) {
|
|
32035
32677
|
const errors = validate$1.errors ?? [];
|
|
32036
32678
|
const messages = errors.map((e) => {
|
|
32037
|
-
const path$
|
|
32038
|
-
return `${path$
|
|
32679
|
+
const path$4 = e.instancePath ?? e.dataPath ?? "";
|
|
32680
|
+
return `${path$4} ${e.message ?? ""}`.trim();
|
|
32039
32681
|
}).join(", ");
|
|
32040
32682
|
return {
|
|
32041
32683
|
content: `Validation failed for tool '${name}': ${messages}`,
|
|
@@ -38639,10 +39281,10 @@ var PathBase = class {
|
|
|
38639
39281
|
/**
|
|
38640
39282
|
* Get the Path object referenced by the string path, resolved from this Path
|
|
38641
39283
|
*/
|
|
38642
|
-
resolve(path$
|
|
38643
|
-
if (!path$
|
|
38644
|
-
const rootPath = this.getRootString(path$
|
|
38645
|
-
const dir = path$
|
|
39284
|
+
resolve(path$4) {
|
|
39285
|
+
if (!path$4) return this;
|
|
39286
|
+
const rootPath = this.getRootString(path$4);
|
|
39287
|
+
const dir = path$4.substring(rootPath.length);
|
|
38646
39288
|
const dirParts = dir.split(this.splitSep);
|
|
38647
39289
|
const result = rootPath ? this.getRoot(rootPath).#resolveParts(dirParts) : this.#resolveParts(dirParts);
|
|
38648
39290
|
return result;
|
|
@@ -39297,8 +39939,8 @@ var PathWin32 = class PathWin32 extends PathBase {
|
|
|
39297
39939
|
/**
|
|
39298
39940
|
* @internal
|
|
39299
39941
|
*/
|
|
39300
|
-
getRootString(path$
|
|
39301
|
-
return win32.parse(path$
|
|
39942
|
+
getRootString(path$4) {
|
|
39943
|
+
return win32.parse(path$4).root;
|
|
39302
39944
|
}
|
|
39303
39945
|
/**
|
|
39304
39946
|
* @internal
|
|
@@ -39343,8 +39985,8 @@ var PathPosix = class PathPosix extends PathBase {
|
|
|
39343
39985
|
/**
|
|
39344
39986
|
* @internal
|
|
39345
39987
|
*/
|
|
39346
|
-
getRootString(path$
|
|
39347
|
-
return path$
|
|
39988
|
+
getRootString(path$4) {
|
|
39989
|
+
return path$4.startsWith("/") ? "/" : "";
|
|
39348
39990
|
}
|
|
39349
39991
|
/**
|
|
39350
39992
|
* @internal
|
|
@@ -39437,9 +40079,9 @@ var PathScurryBase = class {
|
|
|
39437
40079
|
/**
|
|
39438
40080
|
* Get the depth of a provided path, string, or the cwd
|
|
39439
40081
|
*/
|
|
39440
|
-
depth(path$
|
|
39441
|
-
if (typeof path$
|
|
39442
|
-
return path$
|
|
40082
|
+
depth(path$4 = this.cwd) {
|
|
40083
|
+
if (typeof path$4 === "string") path$4 = this.cwd.resolve(path$4);
|
|
40084
|
+
return path$4.depth();
|
|
39443
40085
|
}
|
|
39444
40086
|
/**
|
|
39445
40087
|
* Return the cache of child entries. Exposed so subclasses can create
|
|
@@ -39820,9 +40462,9 @@ var PathScurryBase = class {
|
|
|
39820
40462
|
process$1();
|
|
39821
40463
|
return results;
|
|
39822
40464
|
}
|
|
39823
|
-
chdir(path$
|
|
40465
|
+
chdir(path$4 = this.cwd) {
|
|
39824
40466
|
const oldCwd = this.cwd;
|
|
39825
|
-
this.cwd = typeof path$
|
|
40467
|
+
this.cwd = typeof path$4 === "string" ? this.cwd.resolve(path$4) : path$4;
|
|
39826
40468
|
this.cwd[setAsCwd](oldCwd);
|
|
39827
40469
|
}
|
|
39828
40470
|
};
|
|
@@ -40206,8 +40848,8 @@ var MatchRecord = class {
|
|
|
40206
40848
|
this.store.set(target, current === void 0 ? n$1 : n$1 & current);
|
|
40207
40849
|
}
|
|
40208
40850
|
entries() {
|
|
40209
|
-
return [...this.store.entries()].map(([path$
|
|
40210
|
-
path$
|
|
40851
|
+
return [...this.store.entries()].map(([path$4, n$1]) => [
|
|
40852
|
+
path$4,
|
|
40211
40853
|
!!(n$1 & 2),
|
|
40212
40854
|
!!(n$1 & 1)
|
|
40213
40855
|
]);
|
|
@@ -40383,9 +41025,9 @@ var GlobUtil = class {
|
|
|
40383
41025
|
signal;
|
|
40384
41026
|
maxDepth;
|
|
40385
41027
|
includeChildMatches;
|
|
40386
|
-
constructor(patterns, path$
|
|
41028
|
+
constructor(patterns, path$4, opts) {
|
|
40387
41029
|
this.patterns = patterns;
|
|
40388
|
-
this.path = path$
|
|
41030
|
+
this.path = path$4;
|
|
40389
41031
|
this.opts = opts;
|
|
40390
41032
|
this.#sep = !opts.posix && opts.platform === "win32" ? "\\" : "/";
|
|
40391
41033
|
this.includeChildMatches = opts.includeChildMatches !== false;
|
|
@@ -40406,11 +41048,11 @@ var GlobUtil = class {
|
|
|
40406
41048
|
});
|
|
40407
41049
|
}
|
|
40408
41050
|
}
|
|
40409
|
-
#ignored(path$
|
|
40410
|
-
return this.seen.has(path$
|
|
41051
|
+
#ignored(path$4) {
|
|
41052
|
+
return this.seen.has(path$4) || !!this.#ignore?.ignored?.(path$4);
|
|
40411
41053
|
}
|
|
40412
|
-
#childrenIgnored(path$
|
|
40413
|
-
return !!this.#ignore?.childrenIgnored?.(path$
|
|
41054
|
+
#childrenIgnored(path$4) {
|
|
41055
|
+
return !!this.#ignore?.childrenIgnored?.(path$4);
|
|
40414
41056
|
}
|
|
40415
41057
|
pause() {
|
|
40416
41058
|
this.paused = true;
|
|
@@ -40592,8 +41234,8 @@ var GlobUtil = class {
|
|
|
40592
41234
|
};
|
|
40593
41235
|
var GlobWalker = class extends GlobUtil {
|
|
40594
41236
|
matches = new Set();
|
|
40595
|
-
constructor(patterns, path$
|
|
40596
|
-
super(patterns, path$
|
|
41237
|
+
constructor(patterns, path$4, opts) {
|
|
41238
|
+
super(patterns, path$4, opts);
|
|
40597
41239
|
}
|
|
40598
41240
|
matchEmit(e) {
|
|
40599
41241
|
this.matches.add(e);
|
|
@@ -40620,8 +41262,8 @@ var GlobWalker = class extends GlobUtil {
|
|
|
40620
41262
|
};
|
|
40621
41263
|
var GlobStream = class extends GlobUtil {
|
|
40622
41264
|
results;
|
|
40623
|
-
constructor(patterns, path$
|
|
40624
|
-
super(patterns, path$
|
|
41265
|
+
constructor(patterns, path$4, opts) {
|
|
41266
|
+
super(patterns, path$4, opts);
|
|
40625
41267
|
this.results = new Minipass({
|
|
40626
41268
|
signal: this.signal,
|
|
40627
41269
|
objectMode: true
|
|
@@ -44513,4 +45155,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
44513
45155
|
|
|
44514
45156
|
//#endregion
|
|
44515
45157
|
export { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GitClient, GrammarLoader, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, normalizeGraphSummaryToStatus, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveMaxReviewCycles, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict, wireNdjsonEmitter };
|
|
44516
|
-
//# sourceMappingURL=run-
|
|
45158
|
+
//# sourceMappingURL=run-DGyWCmcu.js.map
|