substrate-ai 0.20.78 → 0.20.80

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.
@@ -1,11 +1,11 @@
1
- import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, validateStoryKey } from "./health-EyALt_76.js";
1
+ import { BMAD_BASELINE_TOKENS_FULL, DoltMergeConflict, FileStateStore, STOP_AFTER_VALID_PHASES, STORY_KEY_PATTERN, VALID_PHASES, __commonJS, __require, __toESM, buildPipelineStatusOutput, createDatabaseAdapter, formatOutput, formatPipelineSummary, formatTokenTelemetry, inspectProcessTree, parseDbTimestampAsUtc, validateStoryKey } from "./health-CuKzY0Fn.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, classifyVersionGap, createConfigSystem, createDatabaseAdapter$1, createDecision, createPipelineRun, createRequirement, detectInterfaceChanges, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunMetrics, getRunningPipelineRuns, getStoryMetricsForRun, getTokenUsageSummary, initSchema, listRequirements, loadModelRoutingConfig, registerArtifact, swallowDebug, updatePipelineRun, updatePipelineRunConfig, upsertDecision, writeRunMetrics, writeStoryMetrics } from "./dist-DCBSXUiX.js";
5
- import { FindingsInjector, RunManifest, RuntimeProbeListSchema, applyConfigToGraph, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, renderFindings, resolveGraphPath, resolveMainRepoRoot, runAcTraceabilityCheck, runStaleVerificationRecovery } from "./manifest-read-DP4lBymM.js";
5
+ import { FindingsInjector, RunManifest, RuntimeProbeListSchema, applyConfigToGraph, createDefaultVerificationPipeline, createGraphOrchestrator, createSdlcCodeReviewHandler, createSdlcCreateStoryHandler, createSdlcDevStoryHandler, createSdlcPhaseHandler, detectsEventDrivenAC, detectsStateIntegratingAC, extractTargetFilesFromStoryContent, renderFindings, resolveGraphPath, resolveMainRepoRoot, runAcTraceabilityCheck, runStaleVerificationRecovery } from "./manifest-read-Boipz5aP.js";
6
6
  import { WorkGraphRepository, detectCycles } from "./work-graph-repository-DZyJv5pV.js";
7
7
  import { deriveExitCode, routeDecision } from "./decision-router-DblHY8se.js";
8
- import { runInteractivePrompt } from "./interactive-prompt-DqvUvzPv.js";
8
+ import { runInteractivePrompt } from "./interactive-prompt-CsvMelhG.js";
9
9
  import { runRecoveryEngine } from "./recovery-engine-BKGBeBnW.js";
10
10
  import { basename, dirname, extname, join } from "path";
11
11
  import { access, readFile, readdir, stat } from "fs/promises";
@@ -14,12 +14,14 @@ import yaml from "js-yaml";
14
14
  import * as actualFS from "node:fs";
15
15
  import { accessSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, unwatchFile, watchFile, writeFileSync } from "node:fs";
16
16
  import { exec, execFile, execFileSync, execSync, spawn } from "node:child_process";
17
+ import * as path$5 from "node:path";
18
+ import * as path$4 from "node:path";
17
19
  import * as path$2 from "node:path";
18
20
  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";
19
21
  import { tmpdir } from "node:os";
20
22
  import { createHash, randomUUID } from "node:crypto";
21
23
  import { z } from "zod";
22
- 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 as unlink$1, writeFile as writeFile$1 } from "node:fs/promises";
24
+ import { access as access$1, lstat, mkdir as mkdir$1, readFile as readFile$1, readdir as readdir$1, readlink, realpath, rename, rm, stat as stat$1, unlink as unlink$1, writeFile as writeFile$1 } from "node:fs/promises";
23
25
  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";
24
26
  import { promisify } from "node:util";
25
27
  import { fileURLToPath } from "node:url";
@@ -31,6 +33,623 @@ import { channel, tracingChannel } from "node:diagnostics_channel";
31
33
  import Stream from "node:stream";
32
34
  import { StringDecoder } from "node:string_decoder";
33
35
 
36
+ //#region packages/core/dist/git/git-utils.js
37
+ const MIN_GIT_MAJOR = 2;
38
+ const MIN_GIT_MINOR = 20;
39
+ /**
40
+ * Spawn a git subprocess with the given args.
41
+ *
42
+ * @param args - Arguments to pass to git (e.g., ['worktree', 'add', ...])
43
+ * @param options - Optional spawn options (cwd, env)
44
+ * @returns - Object with stdout, stderr, and exit code
45
+ */
46
+ function spawnGit(args, options) {
47
+ return new Promise((resolve$6) => {
48
+ const proc$1 = spawn("git", args, {
49
+ cwd: options?.cwd,
50
+ env: options?.env ?? process.env,
51
+ stdio: [
52
+ "ignore",
53
+ "pipe",
54
+ "pipe"
55
+ ]
56
+ });
57
+ let stdout = "";
58
+ let stderr = "";
59
+ proc$1.stdout?.on("data", (chunk) => {
60
+ stdout += chunk.toString();
61
+ });
62
+ proc$1.stderr?.on("data", (chunk) => {
63
+ stderr += chunk.toString();
64
+ });
65
+ proc$1.on("close", (code) => {
66
+ resolve$6({
67
+ stdout: stdout.trim(),
68
+ stderr: stderr.trim(),
69
+ code: code ?? 1
70
+ });
71
+ });
72
+ proc$1.on("error", (err) => {
73
+ resolve$6({
74
+ stdout: "",
75
+ stderr: err.message,
76
+ code: 1
77
+ });
78
+ });
79
+ });
80
+ }
81
+ /**
82
+ * Get the installed git version string.
83
+ *
84
+ * @returns Version string like "2.42.0"
85
+ * @throws Error if git is not installed or version cannot be parsed
86
+ */
87
+ async function getGitVersion() {
88
+ const result = await spawnGit(["--version"]);
89
+ if (result.code !== 0) throw new Error(`git --version failed: ${result.stderr}`);
90
+ const match$2 = /git version\s+(\d+\.\d+(?:\.\d+)?)/.exec(result.stdout);
91
+ if (match$2 === null || match$2[1] === void 0) throw new Error(`Unable to parse git version from output: "${result.stdout}"`);
92
+ return match$2[1];
93
+ }
94
+ /**
95
+ * Parse a git version string into major/minor/patch components.
96
+ *
97
+ * @param versionString - Version string like "2.42.0" or "2.20"
98
+ * @returns - Parsed version components
99
+ */
100
+ function parseGitVersion(versionString) {
101
+ const parts = versionString.split(".").map(Number);
102
+ return {
103
+ major: parts[0] ?? 0,
104
+ minor: parts[1] ?? 0,
105
+ patch: parts[2] ?? 0
106
+ };
107
+ }
108
+ /**
109
+ * Check if the given git version string is >= 2.20.0.
110
+ *
111
+ * @param version - Version string like "2.42.0"
112
+ * @returns - true if version >= 2.20.0
113
+ */
114
+ function isGitVersionSupported(version) {
115
+ const { major, minor } = parseGitVersion(version);
116
+ if (major > MIN_GIT_MAJOR) return true;
117
+ if (major === MIN_GIT_MAJOR && minor >= MIN_GIT_MINOR) return true;
118
+ return false;
119
+ }
120
+ /**
121
+ * Verify that git is installed and version >= 2.20.
122
+ *
123
+ * @throws Error with clear message if git is not installed or too old
124
+ */
125
+ async function verifyGitVersion() {
126
+ let version;
127
+ try {
128
+ version = await getGitVersion();
129
+ } catch (err) {
130
+ throw new Error(`git is not installed or could not be executed. Please install git 2.20 or newer. Details: ${String(err)}`);
131
+ }
132
+ if (!isGitVersionSupported(version)) {
133
+ const { major, minor, patch } = parseGitVersion(version);
134
+ throw new Error(`Git version ${major}.${minor}.${patch} is too old. Substrate requires git 2.20 or newer. Please upgrade git: https://git-scm.com/downloads`);
135
+ }
136
+ }
137
+ /**
138
+ * Create a git worktree and its associated branch.
139
+ *
140
+ * Uses a single `git worktree add {worktreePath} -b {branchName} {baseBranch}`
141
+ * command to create the worktree with a new branch in one step.
142
+ *
143
+ * @param projectRoot - Absolute path to the git repository root
144
+ * @param taskId - Task identifier (used in path derivation)
145
+ * @param branchName - Branch name to create (e.g., "substrate/task-abc123")
146
+ * @param baseBranch - Branch to base the worktree on (e.g., "main")
147
+ * @returns - Object with the worktreePath
148
+ * @throws - Error if git command fails
149
+ */
150
+ async function createWorktree(projectRoot, taskId, branchName, baseBranch) {
151
+ const worktreePath = path$5.join(projectRoot, ".substrate-worktrees", taskId);
152
+ const worktreeExists = await access$1(worktreePath).then(() => true).catch((err) => {
153
+ if (err.code === "ENOENT") return false;
154
+ throw err;
155
+ });
156
+ if (worktreeExists) {
157
+ const listResult = await spawnGit([
158
+ "worktree",
159
+ "list",
160
+ "--porcelain"
161
+ ], { cwd: projectRoot });
162
+ const registeredPaths = listResult.stdout.split("\n").filter((line) => line.startsWith("worktree ")).map((line) => line.slice(9).trim());
163
+ const isRegistered = registeredPaths.includes(worktreePath);
164
+ if (!isRegistered) await rm(worktreePath, {
165
+ recursive: true,
166
+ force: true
167
+ });
168
+ else throw new Error(`Worktree at ${worktreePath} is already registered. Run \`substrate worktrees --cleanup\` to remove it.`);
169
+ }
170
+ const addResult = await spawnGit([
171
+ "worktree",
172
+ "add",
173
+ worktreePath,
174
+ "-b",
175
+ branchName,
176
+ baseBranch
177
+ ], { cwd: projectRoot });
178
+ if (addResult.code !== 0) throw new Error(`git worktree add failed for task "${taskId}": ${addResult.stderr || addResult.stdout}`);
179
+ return { worktreePath };
180
+ }
181
+ /**
182
+ * Remove a git worktree by path.
183
+ *
184
+ * Uses `git worktree remove --force` to handle unclean worktrees.
185
+ *
186
+ * @param worktreePath - Absolute path to the worktree directory
187
+ * @param projectRoot - Absolute path to the git repository root
188
+ * @throws - Error if git command fails
189
+ */
190
+ async function removeWorktree(worktreePath, projectRoot) {
191
+ const spawnOpts = {};
192
+ if (projectRoot !== void 0) spawnOpts.cwd = projectRoot;
193
+ const result = await spawnGit([
194
+ "worktree",
195
+ "remove",
196
+ "--force",
197
+ worktreePath
198
+ ], spawnOpts);
199
+ if (result.code !== 0) throw new Error(`git worktree remove failed for "${worktreePath}": ${result.stderr || result.stdout}`);
200
+ }
201
+ /**
202
+ * Delete a git branch using `git branch -D`.
203
+ *
204
+ * @param branchName - Branch name to delete (e.g., "substrate/task-abc123")
205
+ * @param projectRoot - Absolute path to the git repository root
206
+ * @returns - true if branch was successfully deleted, false otherwise
207
+ */
208
+ async function removeBranch(branchName, projectRoot) {
209
+ const spawnOpts = {};
210
+ if (projectRoot !== void 0) spawnOpts.cwd = projectRoot;
211
+ const result = await spawnGit([
212
+ "branch",
213
+ "-D",
214
+ branchName
215
+ ], spawnOpts);
216
+ if (result.code !== 0) return false;
217
+ return true;
218
+ }
219
+ /**
220
+ * Scan the worktrees base directory and return paths of all found worktree directories.
221
+ *
222
+ * @param projectRoot - Absolute path to the git repository root
223
+ * @param baseDirectory - Relative directory name for worktrees (default: '.substrate-worktrees')
224
+ * @returns - Array of absolute worktree directory paths
225
+ */
226
+ async function getOrphanedWorktrees(projectRoot, baseDirectory = ".substrate-worktrees") {
227
+ const worktreesDir = path$5.join(projectRoot, baseDirectory);
228
+ try {
229
+ await access$1(worktreesDir);
230
+ } catch {
231
+ return [];
232
+ }
233
+ let entries;
234
+ try {
235
+ entries = await readdir$1(worktreesDir, { withFileTypes: true });
236
+ } catch {
237
+ return [];
238
+ }
239
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => path$5.join(worktreesDir, entry.name));
240
+ }
241
+ /**
242
+ * Simulate a merge using git merge --no-commit --no-ff without committing.
243
+ *
244
+ * This runs in the target branch's working directory (the project root or
245
+ * worktree path). The simulation must be aborted after checking conflicts
246
+ * via abortMerge().
247
+ *
248
+ * @param branchName - The source branch to simulate merging
249
+ * @param cwd - Working directory (must be on the target branch)
250
+ * @returns - true if merge would be clean, false if there are conflicts
251
+ */
252
+ async function simulateMerge(branchName, cwd) {
253
+ const result = await spawnGit([
254
+ "merge",
255
+ "--no-commit",
256
+ "--no-ff",
257
+ branchName
258
+ ], { cwd });
259
+ if (result.code === 0) return true;
260
+ return false;
261
+ }
262
+ /**
263
+ * Abort a merge in progress using git merge --abort.
264
+ *
265
+ * Should be called after simulateMerge() to clean up the merge state,
266
+ * regardless of whether conflicts were found.
267
+ *
268
+ * @param cwd - Working directory (same as used for simulateMerge)
269
+ */
270
+ async function abortMerge(cwd) {
271
+ await spawnGit(["merge", "--abort"], { cwd });
272
+ }
273
+ /**
274
+ * Get a list of files with conflicts during a merge.
275
+ *
276
+ * Must be called while a merge is in progress (after simulateMerge() and
277
+ * before abortMerge()).
278
+ *
279
+ * @param cwd - Working directory of the repository
280
+ * @returns - Array of conflicting file paths
281
+ */
282
+ async function getConflictingFiles(cwd) {
283
+ const result = await spawnGit([
284
+ "diff",
285
+ "--name-only",
286
+ "--diff-filter=U"
287
+ ], { cwd });
288
+ if (result.code !== 0) return [];
289
+ if (result.stdout.trim() === "") return [];
290
+ return result.stdout.trim().split("\n").filter((f$1) => f$1.trim().length > 0);
291
+ }
292
+ /**
293
+ * Perform an actual merge using git merge --no-ff.
294
+ *
295
+ * Should only be called after detectConflicts() confirms there are no conflicts.
296
+ * Creates a merge commit even if fast-forward is possible (--no-ff ensures history).
297
+ *
298
+ * @param branchName - The source branch to merge
299
+ * @param cwd - Working directory (must be on the target branch)
300
+ * @returns - true if merge succeeded, false otherwise
301
+ */
302
+ async function performMerge(branchName, cwd) {
303
+ const result = await spawnGit([
304
+ "merge",
305
+ "--no-ff",
306
+ branchName
307
+ ], { cwd });
308
+ if (result.code !== 0) return false;
309
+ return true;
310
+ }
311
+ /**
312
+ * Get a list of files changed in the most recent merge.
313
+ *
314
+ * Uses git diff --name-only HEAD~1..HEAD to find files in the merge commit.
315
+ * Falls back to empty array if commit history is insufficient.
316
+ *
317
+ * @param cwd - Working directory of the repository
318
+ * @returns - Array of file paths that were merged
319
+ */
320
+ async function getMergedFiles(cwd) {
321
+ const result = await spawnGit([
322
+ "diff",
323
+ "--name-only",
324
+ "HEAD~1..HEAD"
325
+ ], { cwd });
326
+ if (result.code !== 0) {
327
+ const altResult = await spawnGit([
328
+ "show",
329
+ "--name-only",
330
+ "--format=",
331
+ "HEAD"
332
+ ], { cwd });
333
+ if (altResult.code !== 0) return [];
334
+ return altResult.stdout.trim().split("\n").filter((f$1) => f$1.trim().length > 0);
335
+ }
336
+ if (result.stdout.trim() === "") return [];
337
+ return result.stdout.trim().split("\n").filter((f$1) => f$1.trim().length > 0);
338
+ }
339
+
340
+ //#endregion
341
+ //#region packages/core/dist/git/git-worktree-manager-impl.js
342
+ const BRANCH_PREFIX = "substrate/task-";
343
+ const DEFAULT_WORKTREE_BASE = ".substrate-worktrees";
344
+ var GitWorktreeManagerImpl = class {
345
+ _eventBus;
346
+ _projectRoot;
347
+ _baseDirectory;
348
+ _db;
349
+ _logger;
350
+ /** Bound listener references for cleanup in shutdown() */
351
+ _onTaskReady;
352
+ _onTaskComplete;
353
+ _onTaskFailed;
354
+ constructor(eventBus, projectRoot, baseDirectory = DEFAULT_WORKTREE_BASE, db = null, logger$27) {
355
+ this._eventBus = eventBus;
356
+ this._projectRoot = projectRoot;
357
+ this._baseDirectory = baseDirectory;
358
+ this._db = db;
359
+ this._logger = logger$27 ?? console;
360
+ this._onTaskReady = ({ taskId }) => {
361
+ this._handleTaskReady(taskId).catch((err) => {
362
+ this._logger.error({
363
+ taskId,
364
+ err
365
+ }, "Unhandled error in _handleTaskReady");
366
+ });
367
+ };
368
+ this._onTaskComplete = ({ taskId }) => {
369
+ this._handleTaskDone(taskId);
370
+ };
371
+ this._onTaskFailed = ({ taskId }) => {
372
+ this._handleTaskDone(taskId);
373
+ };
374
+ }
375
+ async initialize() {
376
+ this._logger.info({ projectRoot: this._projectRoot }, "GitWorktreeManager.initialize()");
377
+ await this.verifyGitVersion();
378
+ const cleaned = await this.cleanupAllWorktrees();
379
+ if (cleaned > 0) this._logger.info({ cleaned }, "Recovered orphaned worktrees on startup");
380
+ this._eventBus.on("task:ready", this._onTaskReady);
381
+ this._eventBus.on("task:complete", this._onTaskComplete);
382
+ this._eventBus.on("task:failed", this._onTaskFailed);
383
+ this._logger.info("GitWorktreeManager initialized");
384
+ }
385
+ async shutdown() {
386
+ this._logger.info("GitWorktreeManager.shutdown()");
387
+ this._eventBus.off("task:ready", this._onTaskReady);
388
+ this._eventBus.off("task:complete", this._onTaskComplete);
389
+ this._eventBus.off("task:failed", this._onTaskFailed);
390
+ await this.cleanupAllWorktrees();
391
+ this._logger.info("GitWorktreeManager shutdown complete");
392
+ }
393
+ async _handleTaskReady(taskId) {
394
+ this._logger.debug({ taskId }, "task:ready — creating worktree");
395
+ try {
396
+ await this.createWorktree(taskId);
397
+ } catch (err) {
398
+ this._logger.error({
399
+ taskId,
400
+ err
401
+ }, "Failed to create worktree for task");
402
+ }
403
+ }
404
+ async _handleTaskDone(taskId) {
405
+ this._logger.debug({ taskId }, "task done — cleaning up worktree");
406
+ try {
407
+ await this.cleanupWorktree(taskId);
408
+ } catch (err) {
409
+ this._logger.warn({
410
+ taskId,
411
+ err
412
+ }, "Failed to cleanup worktree for task");
413
+ }
414
+ }
415
+ async createWorktree(taskId, baseBranch = "main") {
416
+ if (!taskId || taskId.trim().length === 0) throw new Error("createWorktree: taskId must be a non-empty string");
417
+ const branchName = BRANCH_PREFIX + taskId;
418
+ const worktreePath = this.getWorktreePath(taskId);
419
+ this._logger.debug({
420
+ taskId,
421
+ branchName,
422
+ worktreePath,
423
+ baseBranch
424
+ }, "createWorktree");
425
+ await createWorktree(this._projectRoot, taskId, branchName, baseBranch);
426
+ const createdAt = new Date();
427
+ this._eventBus.emit("worktree:created", {
428
+ taskId,
429
+ branchName,
430
+ worktreePath,
431
+ createdAt
432
+ });
433
+ const info = {
434
+ taskId,
435
+ branchName,
436
+ worktreePath,
437
+ createdAt
438
+ };
439
+ this._logger.info({
440
+ taskId,
441
+ branchName,
442
+ worktreePath
443
+ }, "Worktree created");
444
+ return info;
445
+ }
446
+ async cleanupWorktree(taskId) {
447
+ const branchName = BRANCH_PREFIX + taskId;
448
+ const worktreePath = this.getWorktreePath(taskId);
449
+ this._logger.debug({
450
+ taskId,
451
+ branchName,
452
+ worktreePath
453
+ }, "cleanupWorktree");
454
+ let worktreeExists = false;
455
+ try {
456
+ await access$1(worktreePath);
457
+ worktreeExists = true;
458
+ } catch {
459
+ this._logger.debug({
460
+ taskId,
461
+ worktreePath
462
+ }, "cleanupWorktree: worktree does not exist, skipping removal");
463
+ }
464
+ if (worktreeExists) try {
465
+ await removeWorktree(worktreePath, this._projectRoot);
466
+ } catch (err) {
467
+ this._logger.warn({
468
+ taskId,
469
+ worktreePath,
470
+ err
471
+ }, "removeWorktree failed during cleanup");
472
+ }
473
+ try {
474
+ await removeBranch(branchName, this._projectRoot);
475
+ } catch (err) {
476
+ this._logger.warn({
477
+ taskId,
478
+ branchName,
479
+ err
480
+ }, "removeBranch failed during cleanup");
481
+ }
482
+ this._eventBus.emit("worktree:removed", {
483
+ taskId,
484
+ branchName
485
+ });
486
+ this._logger.info({
487
+ taskId,
488
+ branchName
489
+ }, "Worktree cleaned up");
490
+ }
491
+ async cleanupAllWorktrees() {
492
+ this._logger.debug({ projectRoot: this._projectRoot }, "cleanupAllWorktrees");
493
+ const orphanedPaths = await getOrphanedWorktrees(this._projectRoot, this._baseDirectory);
494
+ let cleaned = 0;
495
+ for (const worktreePath of orphanedPaths) {
496
+ const taskId = path$4.basename(worktreePath);
497
+ let worktreeRemoved = false;
498
+ try {
499
+ await removeWorktree(worktreePath, this._projectRoot);
500
+ worktreeRemoved = true;
501
+ this._logger.debug({
502
+ taskId,
503
+ worktreePath
504
+ }, "cleanupAllWorktrees: removed orphaned worktree");
505
+ } catch (err) {
506
+ this._logger.warn({
507
+ taskId,
508
+ worktreePath,
509
+ err
510
+ }, "cleanupAllWorktrees: failed to remove worktree");
511
+ }
512
+ const branchName = BRANCH_PREFIX + taskId;
513
+ try {
514
+ const branchRemoved = await removeBranch(branchName, this._projectRoot);
515
+ if (branchRemoved) this._logger.debug({
516
+ taskId,
517
+ branchName
518
+ }, "cleanupAllWorktrees: removed orphaned branch");
519
+ } catch (err) {
520
+ this._logger.warn({
521
+ taskId,
522
+ branchName,
523
+ err
524
+ }, "cleanupAllWorktrees: failed to remove branch");
525
+ }
526
+ if (worktreeRemoved) cleaned++;
527
+ }
528
+ if (cleaned > 0) this._logger.info({ cleaned }, "cleanupAllWorktrees: recovered orphaned worktrees");
529
+ return cleaned;
530
+ }
531
+ async detectConflicts(taskId, targetBranch = "main") {
532
+ if (!taskId || taskId.trim().length === 0) throw new Error("detectConflicts: taskId must be a non-empty string");
533
+ const branchName = BRANCH_PREFIX + taskId;
534
+ const worktreePath = this.getWorktreePath(taskId);
535
+ this._logger.debug({
536
+ taskId,
537
+ branchName,
538
+ targetBranch
539
+ }, "detectConflicts");
540
+ try {
541
+ await access$1(worktreePath);
542
+ } catch {
543
+ throw new Error(`detectConflicts: Worktree for task "${taskId}" not found at "${worktreePath}". The worktree may have already been cleaned up.`);
544
+ }
545
+ const mergeClean = await simulateMerge(branchName, this._projectRoot);
546
+ let conflictingFiles = [];
547
+ try {
548
+ if (!mergeClean) conflictingFiles = await getConflictingFiles(this._projectRoot);
549
+ } finally {
550
+ await abortMerge(this._projectRoot);
551
+ }
552
+ const report = {
553
+ hasConflicts: !mergeClean || conflictingFiles.length > 0,
554
+ conflictingFiles,
555
+ taskId,
556
+ targetBranch
557
+ };
558
+ if (report.hasConflicts) this._eventBus.emit("worktree:conflict", {
559
+ taskId,
560
+ branch: branchName,
561
+ conflictingFiles: report.conflictingFiles
562
+ });
563
+ this._logger.info({
564
+ taskId,
565
+ hasConflicts: report.hasConflicts,
566
+ conflictCount: conflictingFiles.length
567
+ }, "Conflict detection complete");
568
+ return report;
569
+ }
570
+ async mergeWorktree(taskId, targetBranch = "main") {
571
+ if (!taskId || taskId.trim().length === 0) throw new Error("mergeWorktree: taskId must be a non-empty string");
572
+ const branchName = BRANCH_PREFIX + taskId;
573
+ this._logger.debug({
574
+ taskId,
575
+ branchName,
576
+ targetBranch
577
+ }, "mergeWorktree");
578
+ const conflictReport = await this.detectConflicts(taskId, targetBranch);
579
+ if (conflictReport.hasConflicts) {
580
+ this._logger.info({
581
+ taskId,
582
+ conflictCount: conflictReport.conflictingFiles.length
583
+ }, "Merge skipped due to conflicts");
584
+ return {
585
+ success: false,
586
+ mergedFiles: [],
587
+ conflicts: conflictReport
588
+ };
589
+ }
590
+ const mergeSuccess = await performMerge(branchName, this._projectRoot);
591
+ if (!mergeSuccess) throw new Error(`mergeWorktree: git merge --no-ff failed for task "${taskId}" branch "${branchName}"`);
592
+ const mergedFiles = await getMergedFiles(this._projectRoot);
593
+ this._eventBus.emit("worktree:merged", {
594
+ taskId,
595
+ branch: branchName,
596
+ mergedFiles
597
+ });
598
+ const result = {
599
+ success: true,
600
+ mergedFiles
601
+ };
602
+ this._logger.info({
603
+ taskId,
604
+ branchName,
605
+ mergedFileCount: mergedFiles.length
606
+ }, "Worktree merged successfully");
607
+ return result;
608
+ }
609
+ async listWorktrees() {
610
+ this._logger.debug({
611
+ projectRoot: this._projectRoot,
612
+ baseDirectory: this._baseDirectory
613
+ }, "listWorktrees");
614
+ const worktreePaths = await getOrphanedWorktrees(this._projectRoot, this._baseDirectory);
615
+ const results = [];
616
+ for (const worktreePath of worktreePaths) {
617
+ const taskId = path$4.basename(worktreePath);
618
+ const branchName = BRANCH_PREFIX + taskId;
619
+ let createdAt;
620
+ try {
621
+ const { stat: stat$2 } = await import("node:fs/promises");
622
+ const stats = await stat$2(worktreePath);
623
+ createdAt = stats.birthtime ?? stats.ctime;
624
+ } catch {
625
+ createdAt = new Date();
626
+ }
627
+ results.push({
628
+ taskId,
629
+ branchName,
630
+ worktreePath,
631
+ createdAt
632
+ });
633
+ }
634
+ this._logger.debug({ count: results.length }, "listWorktrees: found worktrees");
635
+ return results;
636
+ }
637
+ getWorktreePath(taskId) {
638
+ return path$4.join(this._projectRoot, this._baseDirectory, taskId);
639
+ }
640
+ async verifyGitVersion() {
641
+ try {
642
+ await verifyGitVersion();
643
+ } catch (err) {
644
+ throw new Error(`GitWorktreeManager: git version check failed: ${String(err)}`);
645
+ }
646
+ }
647
+ };
648
+ function createGitWorktreeManager(options) {
649
+ return new GitWorktreeManagerImpl(options.eventBus, options.projectRoot, options.baseDirectory, options.db ?? null, options.logger);
650
+ }
651
+
652
+ //#endregion
34
653
  //#region src/modules/methodology-pack/schemas.ts
35
654
  /**
36
655
  * A reference to a context value to inject into a step prompt.
@@ -1820,6 +2439,33 @@ const PIPELINE_EVENT_METADATA = [
1820
2439
  description: "Human-readable reason for skipping."
1821
2440
  }
1822
2441
  ]
2442
+ },
2443
+ {
2444
+ type: "pipeline:merge-conflict-detected",
2445
+ description: "A story branch could not be merged into the base branch — both branches edited the same lines. The story is marked ESCALATED. The worktree and branch are preserved for operator inspection.",
2446
+ when: "Emitted during the merge-to-main phase when a 3-way merge fails due to conflicts. Story 75-2.",
2447
+ fields: [
2448
+ {
2449
+ name: "ts",
2450
+ type: "string",
2451
+ description: "Timestamp."
2452
+ },
2453
+ {
2454
+ name: "storyKey",
2455
+ type: "string",
2456
+ description: "Story key whose branch could not be merged."
2457
+ },
2458
+ {
2459
+ name: "branchName",
2460
+ type: "string",
2461
+ description: "Branch name being merged (e.g., \"substrate/story-75-2\")."
2462
+ },
2463
+ {
2464
+ name: "conflictingFiles",
2465
+ type: "string[]",
2466
+ description: "Files with unresolved merge conflicts."
2467
+ }
2468
+ ]
1823
2469
  }
1824
2470
  ];
1825
2471
  /**
@@ -1899,6 +2545,7 @@ Options:
1899
2545
  - \`--research\` — Enable the research phase even if not set in the pack config
1900
2546
  - \`--skip-research\` — Skip the research phase even if enabled in the pack config
1901
2547
  - \`--skip-ux\` — Skip the UX design phase even if enabled in the pack config
2548
+ - \`--no-worktree\` — Disable per-story git worktrees; all implementation runs in the main working tree (use for submodule repos or bare repos that don't support worktrees)
1902
2549
  - \`--help-agent\` — Print this agent instruction fragment and exit
1903
2550
 
1904
2551
  Examples:
@@ -2064,6 +2711,7 @@ These on-disk files back the new autonomy commands. External monitors (dashboard
2064
2711
  - \`.substrate/current-run-id\` — plain text file containing the latest run ID; consulted by the canonical run-discovery chain.
2065
2712
  - \`.substrate/notifications/<run-id>-<timestamp>.json\` — operator halt notifications written by the Recovery Engine when \`--halt-on\` triggers; deleted by \`substrate report\` after read.
2066
2713
  - \`pending_proposals[]\` field in the run manifest — Recovery Engine Tier B re-scope proposals collected here for next-morning operator review. Back-pressure pauses dispatching at \`>= 2\` proposals (work-graph-aware) or \`>= 5\` (safety valve).
2714
+ - \`.substrate-worktrees/story-<key>/\` — per-story git worktree directories created during dispatch. Removed on successful merge; preserved on failure for \`substrate reconcile-from-disk\` inspection. Use \`--no-worktree\` to disable.
2067
2715
 
2068
2716
  ## Environment Variables
2069
2717
 
@@ -2230,7 +2878,7 @@ async function runHelpAgent() {
2230
2878
 
2231
2879
  //#endregion
2232
2880
  //#region src/persistence/dolt-server.ts
2233
- const logger$25 = createLogger("dolt-server");
2881
+ const logger$26 = createLogger("dolt-server");
2234
2882
  /**
2235
2883
  * Start a dolt sql-server for the given project, if Dolt is available and no
2236
2884
  * server is already running (socket already exists).
@@ -2247,7 +2895,7 @@ async function startDoltServer(projectRoot) {
2247
2895
  if (!existsSync(join$1(stateDir, ".dolt"))) return null;
2248
2896
  try {
2249
2897
  await access$1(socketPath);
2250
- logger$25.debug("Dolt socket already exists at %s — using existing server", socketPath);
2898
+ logger$26.debug("Dolt socket already exists at %s — using existing server", socketPath);
2251
2899
  return null;
2252
2900
  } catch {}
2253
2901
  try {
@@ -2256,10 +2904,10 @@ async function startDoltServer(projectRoot) {
2256
2904
  stdio: "pipe"
2257
2905
  });
2258
2906
  } catch {
2259
- logger$25.debug("dolt binary not on PATH — cannot start server");
2907
+ logger$26.debug("dolt binary not on PATH — cannot start server");
2260
2908
  return null;
2261
2909
  }
2262
- logger$25.debug("Starting dolt sql-server at %s", socketPath);
2910
+ logger$26.debug("Starting dolt sql-server at %s", socketPath);
2263
2911
  let proc$1;
2264
2912
  try {
2265
2913
  proc$1 = spawn("dolt", [
@@ -2280,26 +2928,26 @@ async function startDoltServer(projectRoot) {
2280
2928
  detached: false
2281
2929
  });
2282
2930
  } catch (err) {
2283
- logger$25.debug("Failed to spawn dolt sql-server: %s", err instanceof Error ? err.message : String(err));
2931
+ logger$26.debug("Failed to spawn dolt sql-server: %s", err instanceof Error ? err.message : String(err));
2284
2932
  return null;
2285
2933
  }
2286
2934
  let failed = false;
2287
2935
  proc$1.on("error", (err) => {
2288
- logger$25.debug("dolt sql-server error: %s", err.message);
2936
+ logger$26.debug("dolt sql-server error: %s", err.message);
2289
2937
  failed = true;
2290
2938
  });
2291
2939
  proc$1.on("exit", (code) => {
2292
- if (code !== null && code !== 0) logger$25.debug("dolt sql-server exited with code %d", code);
2940
+ if (code !== null && code !== 0) logger$26.debug("dolt sql-server exited with code %d", code);
2293
2941
  });
2294
2942
  proc$1.stderr?.on("data", (chunk) => {
2295
2943
  const line = chunk.toString().trim();
2296
- if (line) logger$25.debug("dolt-server: %s", line);
2944
+ if (line) logger$26.debug("dolt-server: %s", line);
2297
2945
  });
2298
2946
  const deadline = Date.now() + 5e3;
2299
2947
  while (Date.now() < deadline && !failed) try {
2300
2948
  await access$1(socketPath);
2301
2949
  const pid = proc$1.pid ?? 0;
2302
- logger$25.info("Auto-started dolt sql-server (pid=%d, socket=%s)", pid, socketPath);
2950
+ logger$26.info("Auto-started dolt sql-server (pid=%d, socket=%s)", pid, socketPath);
2303
2951
  let stopped = false;
2304
2952
  return {
2305
2953
  pid,
@@ -2307,14 +2955,14 @@ async function startDoltServer(projectRoot) {
2307
2955
  stop: () => {
2308
2956
  if (stopped) return;
2309
2957
  stopped = true;
2310
- logger$25.debug("Stopping dolt sql-server (pid=%d)", pid);
2958
+ logger$26.debug("Stopping dolt sql-server (pid=%d)", pid);
2311
2959
  proc$1.kill("SIGTERM");
2312
2960
  }
2313
2961
  };
2314
2962
  } catch {
2315
2963
  await new Promise((resolve$6) => setTimeout(resolve$6, 100));
2316
2964
  }
2317
- logger$25.debug("dolt sql-server did not start within 5s — killing");
2965
+ logger$26.debug("dolt sql-server did not start within 5s — killing");
2318
2966
  proc$1.kill("SIGTERM");
2319
2967
  return null;
2320
2968
  }
@@ -2384,7 +3032,7 @@ function truncateToTokens(text, maxTokens) {
2384
3032
 
2385
3033
  //#endregion
2386
3034
  //#region src/modules/context-compiler/context-compiler-impl.ts
2387
- const logger$24 = createLogger("context-compiler");
3035
+ const logger$25 = createLogger("context-compiler");
2388
3036
  /**
2389
3037
  * Fraction of the original token budget that must remain (after required +
2390
3038
  * important sections) before an optional section is included.
@@ -2445,7 +3093,7 @@ var ContextCompilerImpl = class {
2445
3093
  }
2446
3094
  _applyExclusionFilter(text, sectionName) {
2447
3095
  for (const excludedPath of this._excludedPaths) if (text.includes(excludedPath)) {
2448
- logger$24.warn({
3096
+ logger$25.warn({
2449
3097
  sectionName,
2450
3098
  excludedPath
2451
3099
  }, "ContextCompiler: section excluded — contains path from exclusion list");
@@ -2503,7 +3151,7 @@ var ContextCompilerImpl = class {
2503
3151
  includedParts.push(truncated);
2504
3152
  remainingBudget -= truncatedTokens;
2505
3153
  anyTruncated = true;
2506
- logger$24.warn({
3154
+ logger$25.warn({
2507
3155
  section: section.name,
2508
3156
  originalTokens: tokens,
2509
3157
  budgetTokens: truncatedTokens
@@ -2517,7 +3165,7 @@ var ContextCompilerImpl = class {
2517
3165
  });
2518
3166
  } else {
2519
3167
  anyTruncated = true;
2520
- logger$24.warn({
3168
+ logger$25.warn({
2521
3169
  section: section.name,
2522
3170
  tokens
2523
3171
  }, "Context compiler: omitted \"important\" section — no budget remaining");
@@ -2544,7 +3192,7 @@ var ContextCompilerImpl = class {
2544
3192
  } else {
2545
3193
  if (tokens > 0) {
2546
3194
  anyTruncated = true;
2547
- logger$24.warn({
3195
+ logger$25.warn({
2548
3196
  section: section.name,
2549
3197
  tokens,
2550
3198
  budgetFractionRemaining: budgetFractionRemaining.toFixed(2)
@@ -2638,8 +3286,8 @@ var GrammarLoader = class {
2638
3286
  _extensionMap;
2639
3287
  _cache = new Map();
2640
3288
  _unavailable = false;
2641
- constructor(logger$26) {
2642
- this._logger = logger$26;
3289
+ constructor(logger$27) {
3290
+ this._logger = logger$27;
2643
3291
  this._extensionMap = new Map([
2644
3292
  [".ts", "tree-sitter-typescript/typescript"],
2645
3293
  [".tsx", "tree-sitter-typescript/tsx"],
@@ -2676,8 +3324,8 @@ var GrammarLoader = class {
2676
3324
  throw err;
2677
3325
  }
2678
3326
  }
2679
- _loadModule(path$4) {
2680
- return __require(path$4);
3327
+ _loadModule(path$6) {
3328
+ return __require(path$6);
2681
3329
  }
2682
3330
  };
2683
3331
 
@@ -2725,9 +3373,9 @@ const ERR_REPO_MAP_GIT_FAILED = "ERR_REPO_MAP_GIT_FAILED";
2725
3373
  var SymbolParser = class {
2726
3374
  _grammarLoader;
2727
3375
  _logger;
2728
- constructor(grammarLoader, logger$26) {
3376
+ constructor(grammarLoader, logger$27) {
2729
3377
  this._grammarLoader = grammarLoader;
2730
- this._logger = logger$26;
3378
+ this._logger = logger$27;
2731
3379
  }
2732
3380
  async parseFile(filePath) {
2733
3381
  const ext$2 = extname$1(filePath);
@@ -2872,9 +3520,9 @@ async function computeFileHash(filePath) {
2872
3520
  var DoltSymbolRepository = class {
2873
3521
  _client;
2874
3522
  _logger;
2875
- constructor(client, logger$26) {
3523
+ constructor(client, logger$27) {
2876
3524
  this._client = client;
2877
- this._logger = logger$26;
3525
+ this._logger = logger$27;
2878
3526
  }
2879
3527
  /**
2880
3528
  * Atomically replace all symbols for filePath.
@@ -3080,11 +3728,11 @@ var RepoMapStorage = class {
3080
3728
  _metaRepo;
3081
3729
  _gitClient;
3082
3730
  _logger;
3083
- constructor(symbolRepo, metaRepo, gitClient, logger$26) {
3731
+ constructor(symbolRepo, metaRepo, gitClient, logger$27) {
3084
3732
  this._symbolRepo = symbolRepo;
3085
3733
  this._metaRepo = metaRepo;
3086
3734
  this._gitClient = gitClient;
3087
- this._logger = logger$26;
3735
+ this._logger = logger$27;
3088
3736
  }
3089
3737
  /**
3090
3738
  * Returns true if the file's current content hash differs from the stored hash.
@@ -3201,8 +3849,8 @@ function runGit(args, cwd) {
3201
3849
  */
3202
3850
  var GitClient = class {
3203
3851
  _logger;
3204
- constructor(logger$26) {
3205
- this._logger = logger$26;
3852
+ constructor(logger$27) {
3853
+ this._logger = logger$27;
3206
3854
  }
3207
3855
  /**
3208
3856
  * Returns the current HEAD commit SHA.
@@ -4558,9 +5206,9 @@ var RepoMapQueryEngine = class {
4558
5206
  repo;
4559
5207
  logger;
4560
5208
  telemetry;
4561
- constructor(repo, logger$26, telemetry) {
5209
+ constructor(repo, logger$27, telemetry) {
4562
5210
  this.repo = repo;
4563
- this.logger = logger$26;
5211
+ this.logger = logger$27;
4564
5212
  this.telemetry = telemetry;
4565
5213
  }
4566
5214
  async query(q) {
@@ -4780,9 +5428,9 @@ var RepoMapFormatter = class {
4780
5428
  var RepoMapTelemetry = class {
4781
5429
  _telemetry;
4782
5430
  _logger;
4783
- constructor(telemetry, logger$26) {
5431
+ constructor(telemetry, logger$27) {
4784
5432
  this._telemetry = telemetry;
4785
- this._logger = logger$26;
5433
+ this._logger = logger$27;
4786
5434
  }
4787
5435
  /**
4788
5436
  * Emit a `repo_map.query` span.
@@ -4807,9 +5455,9 @@ var RepoMapTelemetry = class {
4807
5455
  var RepoMapModule = class {
4808
5456
  _metaRepo;
4809
5457
  _logger;
4810
- constructor(metaRepo, logger$26) {
5458
+ constructor(metaRepo, logger$27) {
4811
5459
  this._metaRepo = metaRepo;
4812
- this._logger = logger$26;
5460
+ this._logger = logger$27;
4813
5461
  }
4814
5462
  /**
4815
5463
  * Check whether the stored repo-map is stale relative to the current HEAD commit.
@@ -4853,9 +5501,9 @@ var RepoMapModule = class {
4853
5501
  var RepoMapInjector = class {
4854
5502
  _queryEngine;
4855
5503
  _logger;
4856
- constructor(queryEngine, logger$26) {
5504
+ constructor(queryEngine, logger$27) {
4857
5505
  this._queryEngine = queryEngine;
4858
- this._logger = logger$26;
5506
+ this._logger = logger$27;
4859
5507
  }
4860
5508
  /**
4861
5509
  * Build repo-map context by extracting file references from the story content,
@@ -4937,7 +5585,7 @@ const DEFAULT_TIMEOUTS = {
4937
5585
 
4938
5586
  //#endregion
4939
5587
  //#region src/modules/agent-dispatch/dispatcher-impl.ts
4940
- const logger$23 = createLogger("agent-dispatch");
5588
+ const logger$24 = createLogger("agent-dispatch");
4941
5589
  /**
4942
5590
  * Create a new Dispatcher instance.
4943
5591
  *
@@ -5081,7 +5729,7 @@ function runBuildVerification(options) {
5081
5729
  let cmd;
5082
5730
  if (verifyCommand === void 0) {
5083
5731
  const detection = detectPackageManager(projectRoot);
5084
- logger$23.info({
5732
+ logger$24.info({
5085
5733
  packageManager: detection.packageManager,
5086
5734
  lockfile: detection.lockfile,
5087
5735
  resolvedCommand: detection.command
@@ -5093,7 +5741,7 @@ function runBuildVerification(options) {
5093
5741
  const filters = deriveTurboFilters(changedFiles, projectRoot);
5094
5742
  if (filters.length > 0) {
5095
5743
  cmd = `${cmd} ${filters.join(" ")}`;
5096
- logger$23.info({
5744
+ logger$24.info({
5097
5745
  filters,
5098
5746
  originalCmd: options.verifyCommand ?? "(auto-detected)"
5099
5747
  }, "Build verification: scoped turbo build to affected packages");
@@ -5136,7 +5784,7 @@ function runBuildVerification(options) {
5136
5784
  };
5137
5785
  const missingScriptPattern = /Missing script[:\s]|No script found|Command "build" not found/i;
5138
5786
  if (missingScriptPattern.test(combinedOutput)) {
5139
- logger$23.warn("Build script not found — skipping pre-flight (greenfield repo)");
5787
+ logger$24.warn("Build script not found — skipping pre-flight (greenfield repo)");
5140
5788
  return {
5141
5789
  status: "skipped",
5142
5790
  exitCode,
@@ -5146,7 +5794,7 @@ function runBuildVerification(options) {
5146
5794
  }
5147
5795
  const pep668Pattern = /externally-managed-environment|This environment is externally managed/i;
5148
5796
  if (pep668Pattern.test(combinedOutput)) {
5149
- logger$23.warn("PEP 668: pip blocked by externally-managed-environment — skipping pre-flight. Create a .venv to resolve.");
5797
+ logger$24.warn("PEP 668: pip blocked by externally-managed-environment — skipping pre-flight. Create a .venv to resolve.");
5150
5798
  return {
5151
5799
  status: "skipped",
5152
5800
  exitCode,
@@ -5330,7 +5978,7 @@ function pickRecommendation(distribution, profile, totalIssues, reviewCycles, la
5330
5978
 
5331
5979
  //#endregion
5332
5980
  //#region src/modules/implementation-orchestrator/project-findings.ts
5333
- const logger$22 = createLogger("project-findings");
5981
+ const logger$23 = createLogger("project-findings");
5334
5982
  /** Maximum character length for the findings summary */
5335
5983
  const MAX_CHARS = 2e3;
5336
5984
  /**
@@ -5403,7 +6051,7 @@ async function getProjectFindings(db) {
5403
6051
  if (summary.length > MAX_CHARS) summary = summary.slice(0, MAX_CHARS - 3) + "...";
5404
6052
  return summary;
5405
6053
  } catch (err) {
5406
- logger$22.warn({ err }, "Failed to query project findings (graceful fallback)");
6054
+ logger$23.warn({ err }, "Failed to query project findings (graceful fallback)");
5407
6055
  return "";
5408
6056
  }
5409
6057
  }
@@ -5426,7 +6074,7 @@ function extractRecurringPatterns(outcomes) {
5426
6074
 
5427
6075
  //#endregion
5428
6076
  //#region src/modules/compiled-workflows/prompt-assembler.ts
5429
- const logger$21 = createLogger("compiled-workflows:prompt-assembler");
6077
+ const logger$22 = createLogger("compiled-workflows:prompt-assembler");
5430
6078
  /**
5431
6079
  * Assemble a final prompt from a template and sections map.
5432
6080
  *
@@ -5451,7 +6099,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
5451
6099
  tokenCount,
5452
6100
  truncated: false
5453
6101
  };
5454
- logger$21.warn({
6102
+ logger$22.warn({
5455
6103
  tokenCount,
5456
6104
  ceiling: tokenCeiling
5457
6105
  }, "Prompt exceeds token ceiling — truncating optional sections");
@@ -5467,10 +6115,10 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
5467
6115
  const targetSectionTokens = Math.max(0, currentSectionTokens - overBy);
5468
6116
  if (targetSectionTokens === 0) {
5469
6117
  contentMap[section.name] = "";
5470
- logger$21.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
6118
+ logger$22.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
5471
6119
  } else {
5472
6120
  contentMap[section.name] = truncateToTokens(section.content, targetSectionTokens);
5473
- logger$21.warn({
6121
+ logger$22.warn({
5474
6122
  sectionName: section.name,
5475
6123
  targetSectionTokens
5476
6124
  }, "Section truncated to fit token budget");
@@ -5481,7 +6129,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
5481
6129
  }
5482
6130
  if (tokenCount <= tokenCeiling) break;
5483
6131
  }
5484
- if (tokenCount > tokenCeiling) logger$21.warn({
6132
+ if (tokenCount > tokenCeiling) logger$22.warn({
5485
6133
  tokenCount,
5486
6134
  ceiling: tokenCeiling
5487
6135
  }, "Required sections alone exceed token ceiling — returning over-budget prompt");
@@ -5821,7 +6469,7 @@ function getTokenCeiling(workflowType, tokenCeilings) {
5821
6469
 
5822
6470
  //#endregion
5823
6471
  //#region src/modules/compiled-workflows/create-story.ts
5824
- const logger$20 = createLogger("compiled-workflows:create-story");
6472
+ const logger$21 = createLogger("compiled-workflows:create-story");
5825
6473
  /**
5826
6474
  * Compute a hex SHA-256 of the normalized source AC section text.
5827
6475
  *
@@ -5856,13 +6504,13 @@ function hashSourceAcSection(section) {
5856
6504
  */
5857
6505
  async function runCreateStory(deps, params) {
5858
6506
  const { epicId, storyKey, pipelineRunId, source_ac_hash, priorDriftFeedback } = params;
5859
- logger$20.debug({
6507
+ logger$21.debug({
5860
6508
  epicId,
5861
6509
  storyKey,
5862
6510
  pipelineRunId
5863
6511
  }, "Starting create-story workflow");
5864
6512
  const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("create-story", deps.tokenCeilings);
5865
- logger$20.info({
6513
+ logger$21.info({
5866
6514
  workflow: "create-story",
5867
6515
  ceiling: TOKEN_CEILING,
5868
6516
  source: tokenCeilingSource
@@ -5872,7 +6520,7 @@ async function runCreateStory(deps, params) {
5872
6520
  template = await deps.pack.getPrompt("create-story");
5873
6521
  } catch (err) {
5874
6522
  const error = err instanceof Error ? err.message : String(err);
5875
- logger$20.error({ error }, "Failed to retrieve create-story prompt template");
6523
+ logger$21.error({ error }, "Failed to retrieve create-story prompt template");
5876
6524
  return {
5877
6525
  result: "failed",
5878
6526
  error: `Failed to retrieve prompt template: ${error}`,
@@ -5889,7 +6537,7 @@ async function runCreateStory(deps, params) {
5889
6537
  const storySection = extractStorySection(epicShardContent, storyKey);
5890
6538
  if (storySection !== null) {
5891
6539
  const computedHash = hashSourceAcSection(storySection);
5892
- if (source_ac_hash !== void 0 && source_ac_hash !== computedHash) logger$20.debug({
6540
+ if (source_ac_hash !== void 0 && source_ac_hash !== computedHash) logger$21.debug({
5893
6541
  storyKey,
5894
6542
  suppliedHash: source_ac_hash,
5895
6543
  computedHash
@@ -5904,7 +6552,7 @@ async function runCreateStory(deps, params) {
5904
6552
  const storyDef = storyDecisions.find((d) => d.category === "stories" && d.key === storyKey);
5905
6553
  if (storyDef) {
5906
6554
  storyDefinitionContent = storyDef.value;
5907
- logger$20.debug({ storyKey }, "Injected story definition from solutioning decisions");
6555
+ logger$21.debug({ storyKey }, "Injected story definition from solutioning decisions");
5908
6556
  }
5909
6557
  } catch {}
5910
6558
  const archConstraintsContent = await getArchConstraints$3(deps);
@@ -5955,7 +6603,7 @@ async function runCreateStory(deps, params) {
5955
6603
  priority: "optional"
5956
6604
  }]
5957
6605
  ], TOKEN_CEILING);
5958
- logger$20.debug({
6606
+ logger$21.debug({
5959
6607
  tokenCount,
5960
6608
  truncated,
5961
6609
  tokenCeiling: TOKEN_CEILING
@@ -5975,7 +6623,7 @@ async function runCreateStory(deps, params) {
5975
6623
  dispatchResult = await handle.result;
5976
6624
  } catch (err) {
5977
6625
  const error = err instanceof Error ? err.message : String(err);
5978
- logger$20.error({
6626
+ logger$21.error({
5979
6627
  epicId,
5980
6628
  storyKey,
5981
6629
  error
@@ -5996,7 +6644,7 @@ async function runCreateStory(deps, params) {
5996
6644
  if (dispatchResult.status === "failed") {
5997
6645
  const errorMsg = dispatchResult.parseError ?? `Dispatch failed with exit code ${dispatchResult.exitCode}`;
5998
6646
  const stderrDetail = dispatchResult.output ? ` Output: ${dispatchResult.output}` : "";
5999
- logger$20.warn({
6647
+ logger$21.warn({
6000
6648
  epicId,
6001
6649
  storyKey,
6002
6650
  exitCode: dispatchResult.exitCode,
@@ -6009,7 +6657,7 @@ async function runCreateStory(deps, params) {
6009
6657
  };
6010
6658
  }
6011
6659
  if (dispatchResult.status === "timeout") {
6012
- logger$20.warn({
6660
+ logger$21.warn({
6013
6661
  epicId,
6014
6662
  storyKey
6015
6663
  }, "Create-story dispatch timed out");
@@ -6022,7 +6670,7 @@ async function runCreateStory(deps, params) {
6022
6670
  if (dispatchResult.parsed === null) {
6023
6671
  const details = dispatchResult.parseError ?? "No YAML block found in output";
6024
6672
  const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
6025
- logger$20.warn({
6673
+ logger$21.warn({
6026
6674
  epicId,
6027
6675
  storyKey,
6028
6676
  details,
@@ -6038,7 +6686,7 @@ async function runCreateStory(deps, params) {
6038
6686
  const parseResult = CreateStoryResultSchema.safeParse(dispatchResult.parsed);
6039
6687
  if (!parseResult.success) {
6040
6688
  const details = parseResult.error.message;
6041
- logger$20.warn({
6689
+ logger$21.warn({
6042
6690
  epicId,
6043
6691
  storyKey,
6044
6692
  details
@@ -6051,7 +6699,7 @@ async function runCreateStory(deps, params) {
6051
6699
  };
6052
6700
  }
6053
6701
  const parsed = parseResult.data;
6054
- logger$20.info({
6702
+ logger$21.info({
6055
6703
  epicId,
6056
6704
  storyKey,
6057
6705
  storyFile: parsed.story_file,
@@ -6079,7 +6727,7 @@ async function getImplementationDecisions(deps, pipelineRunId) {
6079
6727
  }
6080
6728
  return await getDecisionsByPhase(deps.db, "implementation");
6081
6729
  } catch (err) {
6082
- logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
6730
+ logger$21.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
6083
6731
  return [];
6084
6732
  }
6085
6733
  }
@@ -6166,10 +6814,10 @@ function computeStoryFileFidelity(storyFileContent, namedPaths) {
6166
6814
  };
6167
6815
  const missing = [];
6168
6816
  const present = [];
6169
- for (const path$4 of namedPaths) {
6170
- const basename$2 = path$4.includes("/") ? path$4.slice(path$4.lastIndexOf("/") + 1) : path$4;
6171
- if (storyFileContent.includes(path$4) || storyFileContent.includes(basename$2)) present.push(path$4);
6172
- else missing.push(path$4);
6817
+ for (const path$6 of namedPaths) {
6818
+ const basename$2 = path$6.includes("/") ? path$6.slice(path$6.lastIndexOf("/") + 1) : path$6;
6819
+ if (storyFileContent.includes(path$6) || storyFileContent.includes(basename$2)) present.push(path$6);
6820
+ else missing.push(path$6);
6173
6821
  }
6174
6822
  return {
6175
6823
  missing,
@@ -6370,7 +7018,7 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
6370
7018
  if (storyKey) {
6371
7019
  const perStoryShard = decisions.find((d) => d.category === "epic-shard" && d.key === storyKey);
6372
7020
  if (perStoryShard?.value) {
6373
- logger$20.debug({
7021
+ logger$21.debug({
6374
7022
  epicId,
6375
7023
  storyKey
6376
7024
  }, "Found per-story epic shard (direct lookup)");
@@ -6382,13 +7030,13 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
6382
7030
  if (shardContent && storyKey) {
6383
7031
  const storySection = extractStorySection(shardContent, storyKey);
6384
7032
  if (storySection) {
6385
- logger$20.debug({
7033
+ logger$21.debug({
6386
7034
  epicId,
6387
7035
  storyKey
6388
7036
  }, "Extracted per-story section from epic shard (pre-37-0 fallback)");
6389
7037
  return storySection;
6390
7038
  }
6391
- logger$20.info({
7039
+ logger$21.info({
6392
7040
  epicId,
6393
7041
  storyKey
6394
7042
  }, "Story section absent in decisions-store shard — attempting file-based fallback before returning stale shard");
@@ -6396,11 +7044,11 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
6396
7044
  if (projectRoot) {
6397
7045
  const fallback = readEpicShardFromFile(projectRoot, epicId);
6398
7046
  if (fallback) {
6399
- logger$20.info({ epicId }, "Using file-based fallback for epic shard");
7047
+ logger$21.info({ epicId }, "Using file-based fallback for epic shard");
6400
7048
  if (storyKey) {
6401
7049
  const storySection = extractStorySection(fallback, storyKey);
6402
7050
  if (storySection) {
6403
- logger$20.debug({
7051
+ logger$21.debug({
6404
7052
  epicId,
6405
7053
  storyKey
6406
7054
  }, "Extracted per-story section from file-based epic shard");
@@ -6413,7 +7061,7 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
6413
7061
  if (shardContent) return shardContent;
6414
7062
  return "";
6415
7063
  } catch (err) {
6416
- logger$20.warn({
7064
+ logger$21.warn({
6417
7065
  epicId,
6418
7066
  error: err instanceof Error ? err.message : String(err)
6419
7067
  }, "Failed to retrieve epic shard");
@@ -6430,7 +7078,7 @@ function getPrevDevNotes(decisions, epicId) {
6430
7078
  if (devNotes.length === 0) return "";
6431
7079
  return devNotes[devNotes.length - 1].value;
6432
7080
  } catch (err) {
6433
- logger$20.warn({
7081
+ logger$21.warn({
6434
7082
  epicId,
6435
7083
  error: err instanceof Error ? err.message : String(err)
6436
7084
  }, "Failed to retrieve prev dev notes");
@@ -6463,7 +7111,7 @@ async function getArchConstraints$3(deps) {
6463
7111
  const truncatedBody = body.length > 300 ? body.slice(0, 297) + "..." : body;
6464
7112
  return `${header}\n${truncatedBody}`;
6465
7113
  }).join("\n\n");
6466
- logger$20.info({
7114
+ logger$21.info({
6467
7115
  fullLength: full.length,
6468
7116
  summarizedLength: summarized.length,
6469
7117
  decisions: constraints.length
@@ -6473,13 +7121,13 @@ async function getArchConstraints$3(deps) {
6473
7121
  if (deps.projectRoot) {
6474
7122
  const fallback = readArchConstraintsFromFile(deps.projectRoot);
6475
7123
  if (fallback) {
6476
- logger$20.info("Using file-based fallback for architecture constraints (decisions table empty)");
7124
+ logger$21.info("Using file-based fallback for architecture constraints (decisions table empty)");
6477
7125
  return fallback.length > ARCH_CONSTRAINT_MAX_CHARS ? fallback.slice(0, ARCH_CONSTRAINT_MAX_CHARS) + "\n\n[truncated for token budget]" : fallback;
6478
7126
  }
6479
7127
  }
6480
7128
  return "";
6481
7129
  } catch (err) {
6482
- logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
7130
+ logger$21.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
6483
7131
  return "";
6484
7132
  }
6485
7133
  }
@@ -6532,7 +7180,7 @@ function readEpicShardFromFile(projectRoot, epicId) {
6532
7180
  } catch {}
6533
7181
  return "";
6534
7182
  } catch (err) {
6535
- logger$20.warn({
7183
+ logger$21.warn({
6536
7184
  epicId,
6537
7185
  error: err instanceof Error ? err.message : String(err)
6538
7186
  }, "File-based epic shard fallback failed");
@@ -6555,7 +7203,7 @@ function readArchConstraintsFromFile(projectRoot) {
6555
7203
  const content = readFileSync(archPath, "utf-8");
6556
7204
  return content.slice(0, 1500);
6557
7205
  } catch (err) {
6558
- logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
7206
+ logger$21.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
6559
7207
  return "";
6560
7208
  }
6561
7209
  }
@@ -6568,7 +7216,7 @@ async function getStoryTemplate(deps) {
6568
7216
  try {
6569
7217
  return await deps.pack.getTemplate("story");
6570
7218
  } catch (err) {
6571
- logger$20.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
7219
+ logger$21.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
6572
7220
  return "";
6573
7221
  }
6574
7222
  }
@@ -6605,7 +7253,7 @@ async function isValidStoryFile(filePath) {
6605
7253
 
6606
7254
  //#endregion
6607
7255
  //#region src/modules/compiled-workflows/git-helpers.ts
6608
- const logger$19 = createLogger("compiled-workflows:git-helpers");
7256
+ const logger$20 = createLogger("compiled-workflows:git-helpers");
6609
7257
  /**
6610
7258
  * Check whether the repo at `cwd` has at least one commit (HEAD resolves).
6611
7259
  * Returns false for fresh repos with no commits, avoiding `fatal: bad revision 'HEAD'`.
@@ -6642,7 +7290,7 @@ function hasCommits(cwd) {
6642
7290
  */
6643
7291
  async function getGitDiffSummary(workingDirectory = process.cwd()) {
6644
7292
  if (!hasCommits(workingDirectory)) {
6645
- logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff");
7293
+ logger$20.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff");
6646
7294
  return "";
6647
7295
  }
6648
7296
  return runGitCommand(["diff", "HEAD"], workingDirectory, "git-diff-summary");
@@ -6659,7 +7307,7 @@ async function getGitDiffSummary(workingDirectory = process.cwd()) {
6659
7307
  */
6660
7308
  async function getGitDiffStatSummary(workingDirectory = process.cwd()) {
6661
7309
  if (!hasCommits(workingDirectory)) {
6662
- logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat");
7310
+ logger$20.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat");
6663
7311
  return "";
6664
7312
  }
6665
7313
  return runGitCommand([
@@ -6708,7 +7356,7 @@ async function getGitDiffStatBetweenCommits(baseCommit, endCommit = "HEAD", work
6708
7356
  async function getGitDiffForFiles(files, workingDirectory = process.cwd()) {
6709
7357
  if (files.length === 0) return "";
6710
7358
  if (!hasCommits(workingDirectory)) {
6711
- logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff for files");
7359
+ logger$20.debug({ cwd: workingDirectory }, "No commits in repo — returning empty diff for files");
6712
7360
  return "";
6713
7361
  }
6714
7362
  await stageIntentToAdd(files, workingDirectory);
@@ -6735,7 +7383,7 @@ async function getGitDiffForFiles(files, workingDirectory = process.cwd()) {
6735
7383
  async function getGitDiffStatForFiles(files, workingDirectory = process.cwd()) {
6736
7384
  if (files.length === 0) return "";
6737
7385
  if (!hasCommits(workingDirectory)) {
6738
- logger$19.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat for files");
7386
+ logger$20.debug({ cwd: workingDirectory }, "No commits in repo — returning empty stat for files");
6739
7387
  return "";
6740
7388
  }
6741
7389
  return runGitCommand([
@@ -6784,7 +7432,7 @@ async function stageIntentToAdd(files, workingDirectory) {
6784
7432
  if (files.length === 0) return;
6785
7433
  const existing = files.filter((f$1) => {
6786
7434
  const exists = existsSync(f$1);
6787
- if (!exists) logger$19.debug({ file: f$1 }, "Skipping nonexistent file in stageIntentToAdd");
7435
+ if (!exists) logger$20.debug({ file: f$1 }, "Skipping nonexistent file in stageIntentToAdd");
6788
7436
  return exists;
6789
7437
  });
6790
7438
  if (existing.length === 0) return;
@@ -6818,7 +7466,7 @@ async function runGitCommand(args, cwd, logLabel) {
6818
7466
  stderr += chunk.toString("utf-8");
6819
7467
  });
6820
7468
  proc$1.on("error", (err) => {
6821
- logger$19.warn({
7469
+ logger$20.warn({
6822
7470
  label: logLabel,
6823
7471
  cwd,
6824
7472
  error: err.message
@@ -6827,7 +7475,7 @@ async function runGitCommand(args, cwd, logLabel) {
6827
7475
  });
6828
7476
  proc$1.on("close", (code) => {
6829
7477
  if (code !== 0) {
6830
- logger$19.warn({
7478
+ logger$20.warn({
6831
7479
  label: logLabel,
6832
7480
  cwd,
6833
7481
  code,
@@ -6843,7 +7491,7 @@ async function runGitCommand(args, cwd, logLabel) {
6843
7491
 
6844
7492
  //#endregion
6845
7493
  //#region src/modules/compiled-workflows/story-complexity.ts
6846
- const logger$18 = createLogger("compiled-workflows:story-complexity");
7494
+ const logger$19 = createLogger("compiled-workflows:story-complexity");
6847
7495
  /**
6848
7496
  * Compute a complexity score from story markdown content.
6849
7497
  *
@@ -6895,7 +7543,7 @@ function resolveFixStoryMaxTurns(complexityScore) {
6895
7543
  * @param resolvedMaxTurns - Turn limit resolved for this dispatch
6896
7544
  */
6897
7545
  function logComplexityResult(storyKey, complexity, resolvedMaxTurns) {
6898
- logger$18.info({
7546
+ logger$19.info({
6899
7547
  storyKey,
6900
7548
  taskCount: complexity.taskCount,
6901
7549
  subtaskCount: complexity.subtaskCount,
@@ -7151,7 +7799,7 @@ function resolveInstallCommand(projectRoot) {
7151
7799
 
7152
7800
  //#endregion
7153
7801
  //#region src/modules/compiled-workflows/dev-story.ts
7154
- const logger$17 = createLogger("compiled-workflows:dev-story");
7802
+ const logger$18 = createLogger("compiled-workflows:dev-story");
7155
7803
  /** Default timeout for dev-story dispatches in milliseconds (30 min) */
7156
7804
  const DEFAULT_TIMEOUT_MS$2 = 18e5;
7157
7805
  /**
@@ -7163,12 +7811,12 @@ const DEFAULT_TIMEOUT_MS$2 = 18e5;
7163
7811
  */
7164
7812
  async function runDevStory(deps, params) {
7165
7813
  const { storyKey, storyFilePath, taskScope, priorFiles, findingsPrompt: handlerFindingsPrompt } = params;
7166
- logger$17.info({
7814
+ logger$18.info({
7167
7815
  storyKey,
7168
7816
  storyFilePath
7169
7817
  }, "Starting compiled dev-story workflow");
7170
7818
  const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("dev-story", deps.tokenCeilings);
7171
- logger$17.info({
7819
+ logger$18.info({
7172
7820
  workflow: "dev-story",
7173
7821
  ceiling: TOKEN_CEILING,
7174
7822
  source: tokenCeilingSource
@@ -7211,10 +7859,10 @@ async function runDevStory(deps, params) {
7211
7859
  let template;
7212
7860
  try {
7213
7861
  template = await deps.pack.getPrompt("dev-story");
7214
- logger$17.debug({ storyKey }, "Retrieved dev-story prompt template from pack");
7862
+ logger$18.debug({ storyKey }, "Retrieved dev-story prompt template from pack");
7215
7863
  } catch (err) {
7216
7864
  const error = err instanceof Error ? err.message : String(err);
7217
- logger$17.error({
7865
+ logger$18.error({
7218
7866
  storyKey,
7219
7867
  error
7220
7868
  }, "Failed to retrieve dev-story prompt template");
@@ -7225,14 +7873,14 @@ async function runDevStory(deps, params) {
7225
7873
  storyContent = await readFile$1(storyFilePath, "utf-8");
7226
7874
  } catch (err) {
7227
7875
  if (err.code === "ENOENT") {
7228
- logger$17.error({
7876
+ logger$18.error({
7229
7877
  storyKey,
7230
7878
  storyFilePath
7231
7879
  }, "Story file not found");
7232
7880
  return makeFailureResult("story_file_not_found");
7233
7881
  }
7234
7882
  const error = err instanceof Error ? err.message : String(err);
7235
- logger$17.error({
7883
+ logger$18.error({
7236
7884
  storyKey,
7237
7885
  storyFilePath,
7238
7886
  error
@@ -7240,7 +7888,7 @@ async function runDevStory(deps, params) {
7240
7888
  return makeFailureResult(`story_file_read_error: ${error}`);
7241
7889
  }
7242
7890
  if (storyContent.trim().length === 0) {
7243
- logger$17.error({
7891
+ logger$18.error({
7244
7892
  storyKey,
7245
7893
  storyFilePath
7246
7894
  }, "Story file is empty");
@@ -7248,7 +7896,7 @@ async function runDevStory(deps, params) {
7248
7896
  }
7249
7897
  const staleStatus = detectDeprecatedStatusField(storyContent);
7250
7898
  if (staleStatus !== null) {
7251
- logger$17.warn({
7899
+ logger$18.warn({
7252
7900
  storyFilePath,
7253
7901
  staleStatus
7254
7902
  }, "Story spec contains deprecated Status field — stripped before dispatch (status is managed by Dolt work graph)");
@@ -7263,17 +7911,17 @@ async function runDevStory(deps, params) {
7263
7911
  const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
7264
7912
  if (testPatternDecisions.length > 0) {
7265
7913
  testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
7266
- logger$17.debug({
7914
+ logger$18.debug({
7267
7915
  storyKey,
7268
7916
  count: testPatternDecisions.length
7269
7917
  }, "Loaded test patterns from decision store");
7270
7918
  } else {
7271
7919
  testPatternsContent = resolveDefaultTestPatterns(deps.projectRoot);
7272
- logger$17.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
7920
+ logger$18.debug({ storyKey }, "No test-pattern decisions — using stack-aware defaults");
7273
7921
  }
7274
7922
  } catch (err) {
7275
7923
  const error = err instanceof Error ? err.message : String(err);
7276
- logger$17.warn({
7924
+ logger$18.warn({
7277
7925
  storyKey,
7278
7926
  error
7279
7927
  }, "Failed to load test patterns — using defaults");
@@ -7287,7 +7935,7 @@ async function runDevStory(deps, params) {
7287
7935
  if (deps.repoMapInjector !== void 0) {
7288
7936
  const injection = await deps.repoMapInjector.buildContext(storyContent, deps.maxRepoMapTokens ?? 2e3);
7289
7937
  repoContextContent = injection.text;
7290
- logger$17.info({
7938
+ logger$18.info({
7291
7939
  storyKey,
7292
7940
  repoMapTokens: Math.ceil(injection.text.length / 4),
7293
7941
  symbolCount: injection.symbolCount,
@@ -7297,7 +7945,7 @@ async function runDevStory(deps, params) {
7297
7945
  let priorFindingsContent = "";
7298
7946
  if (handlerFindingsPrompt !== void 0 && handlerFindingsPrompt.length > 0) {
7299
7947
  priorFindingsContent = handlerFindingsPrompt;
7300
- logger$17.debug({
7948
+ logger$18.debug({
7301
7949
  storyKey,
7302
7950
  findingsLen: handlerFindingsPrompt.length
7303
7951
  }, "Using pre-computed findings from handler (Story 53-8 AC2)");
@@ -7309,7 +7957,7 @@ async function runDevStory(deps, params) {
7309
7957
  });
7310
7958
  if (findings.length > 0) {
7311
7959
  priorFindingsContent = findings;
7312
- logger$17.debug({
7960
+ logger$18.debug({
7313
7961
  storyKey,
7314
7962
  findingsLen: findings.length
7315
7963
  }, "Injecting relevance-scored findings into dev-story prompt");
@@ -7329,7 +7977,7 @@ async function runDevStory(deps, params) {
7329
7977
  if (plan.test_categories && plan.test_categories.length > 0) parts.push(`\n### Categories: ${plan.test_categories.join(", ")}`);
7330
7978
  if (plan.coverage_notes) parts.push(`\n### Coverage Notes\n${plan.coverage_notes}`);
7331
7979
  testPlanContent = parts.join("\n");
7332
- logger$17.debug({ storyKey }, "Injecting test plan into dev-story prompt");
7980
+ logger$18.debug({ storyKey }, "Injecting test plan into dev-story prompt");
7333
7981
  }
7334
7982
  } catch {}
7335
7983
  const sections = [
@@ -7390,7 +8038,7 @@ async function runDevStory(deps, params) {
7390
8038
  }
7391
8039
  ];
7392
8040
  const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING);
7393
- logger$17.info({
8041
+ logger$18.info({
7394
8042
  storyKey,
7395
8043
  tokenCount,
7396
8044
  ceiling: TOKEN_CEILING,
@@ -7414,7 +8062,7 @@ async function runDevStory(deps, params) {
7414
8062
  dispatchResult = await handle.result;
7415
8063
  } catch (err) {
7416
8064
  const error = err instanceof Error ? err.message : String(err);
7417
- logger$17.error({
8065
+ logger$18.error({
7418
8066
  storyKey,
7419
8067
  error
7420
8068
  }, "Dispatch threw an unexpected error");
@@ -7425,11 +8073,11 @@ async function runDevStory(deps, params) {
7425
8073
  output: dispatchResult.tokenEstimate.output
7426
8074
  };
7427
8075
  if (dispatchResult.status === "timeout") {
7428
- logger$17.error({
8076
+ logger$18.error({
7429
8077
  storyKey,
7430
8078
  durationMs: dispatchResult.durationMs
7431
8079
  }, "Dev-story dispatch timed out");
7432
- if (dispatchResult.output.length > 0) logger$17.info({
8080
+ if (dispatchResult.output.length > 0) logger$18.info({
7433
8081
  storyKey,
7434
8082
  partialOutput: dispatchResult.output.slice(0, 500)
7435
8083
  }, "Partial output before timeout");
@@ -7439,12 +8087,12 @@ async function runDevStory(deps, params) {
7439
8087
  };
7440
8088
  }
7441
8089
  if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
7442
- logger$17.error({
8090
+ logger$18.error({
7443
8091
  storyKey,
7444
8092
  exitCode: dispatchResult.exitCode,
7445
8093
  status: dispatchResult.status
7446
8094
  }, "Dev-story dispatch failed");
7447
- if (dispatchResult.output.length > 0) logger$17.info({
8095
+ if (dispatchResult.output.length > 0) logger$18.info({
7448
8096
  storyKey,
7449
8097
  partialOutput: dispatchResult.output.slice(0, 500)
7450
8098
  }, "Partial output from failed dispatch");
@@ -7456,7 +8104,7 @@ async function runDevStory(deps, params) {
7456
8104
  if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
7457
8105
  const details = dispatchResult.parseError ?? "parsed result was null";
7458
8106
  const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
7459
- logger$17.error({
8107
+ logger$18.error({
7460
8108
  storyKey,
7461
8109
  parseError: details,
7462
8110
  rawOutputSnippet: rawSnippet
@@ -7464,12 +8112,12 @@ async function runDevStory(deps, params) {
7464
8112
  let filesModified = [];
7465
8113
  try {
7466
8114
  filesModified = await getGitChangedFiles(deps.projectRoot ?? process.cwd());
7467
- if (filesModified.length > 0) logger$17.info({
8115
+ if (filesModified.length > 0) logger$18.info({
7468
8116
  storyKey,
7469
8117
  fileCount: filesModified.length
7470
8118
  }, "Recovered files_modified from git status (YAML fallback)");
7471
8119
  } catch (err) {
7472
- logger$17.warn({
8120
+ logger$18.warn({
7473
8121
  storyKey,
7474
8122
  error: err instanceof Error ? err.message : String(err)
7475
8123
  }, "Failed to recover files_modified from git");
@@ -7486,7 +8134,7 @@ async function runDevStory(deps, params) {
7486
8134
  };
7487
8135
  }
7488
8136
  const parsed = dispatchResult.parsed;
7489
- logger$17.info({
8137
+ logger$18.info({
7490
8138
  storyKey,
7491
8139
  result: parsed.result,
7492
8140
  acMet: parsed.ac_met.length
@@ -7602,7 +8250,7 @@ function extractReferencedFiles(storyContent) {
7602
8250
  const matches = storyContent.match(filePathRegex) ?? [];
7603
8251
  const freq = new Map();
7604
8252
  for (const m of matches) freq.set(m, (freq.get(m) ?? 0) + 1);
7605
- const sorted = [...freq.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([path$4]) => path$4);
8253
+ const sorted = [...freq.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([path$6]) => path$6);
7606
8254
  return sorted.filter((p) => !p.includes(".test."));
7607
8255
  }
7608
8256
  function extractFilesInScope(storyContent) {
@@ -7899,7 +8547,7 @@ function resolvesIntoExpected(modifiedFilePath, relativePath, expectedFiles) {
7899
8547
 
7900
8548
  //#endregion
7901
8549
  //#region src/modules/compiled-workflows/code-review.ts
7902
- const logger$16 = createLogger("compiled-workflows:code-review");
8550
+ const logger$17 = createLogger("compiled-workflows:code-review");
7903
8551
  /**
7904
8552
  * Default fallback result when dispatch fails or times out.
7905
8553
  * Uses NEEDS_MINOR_FIXES (not NEEDS_MAJOR_REWORK) so a parse/schema failure
@@ -7974,14 +8622,14 @@ async function countTestMetrics(filesModified, cwd) {
7974
8622
  async function runCodeReview(deps, params) {
7975
8623
  const { storyKey, storyFilePath, workingDirectory, pipelineRunId, filesModified, previousIssues, buildPassed, baselineCommit } = params;
7976
8624
  const cwd = workingDirectory ?? process.cwd();
7977
- logger$16.debug({
8625
+ logger$17.debug({
7978
8626
  storyKey,
7979
8627
  storyFilePath,
7980
8628
  cwd,
7981
8629
  pipelineRunId
7982
8630
  }, "Starting code-review workflow");
7983
8631
  const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("code-review", deps.tokenCeilings);
7984
- logger$16.info({
8632
+ logger$17.info({
7985
8633
  workflow: "code-review",
7986
8634
  ceiling: TOKEN_CEILING,
7987
8635
  source: tokenCeilingSource
@@ -7991,7 +8639,7 @@ async function runCodeReview(deps, params) {
7991
8639
  template = await deps.pack.getPrompt("code-review");
7992
8640
  } catch (err) {
7993
8641
  const error = err instanceof Error ? err.message : String(err);
7994
- logger$16.error({ error }, "Failed to retrieve code-review prompt template");
8642
+ logger$17.error({ error }, "Failed to retrieve code-review prompt template");
7995
8643
  return defaultFailResult(`Failed to retrieve prompt template: ${error}`, {
7996
8644
  input: 0,
7997
8645
  output: 0
@@ -8002,7 +8650,7 @@ async function runCodeReview(deps, params) {
8002
8650
  storyContent = await readFile$1(storyFilePath, "utf-8");
8003
8651
  } catch (err) {
8004
8652
  const error = err instanceof Error ? err.message : String(err);
8005
- logger$16.error({
8653
+ logger$17.error({
8006
8654
  storyFilePath,
8007
8655
  error
8008
8656
  }, "Failed to read story file");
@@ -8022,12 +8670,12 @@ async function runCodeReview(deps, params) {
8022
8670
  const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
8023
8671
  if (scopedTotal <= TOKEN_CEILING) {
8024
8672
  gitDiffContent = scopedDiff;
8025
- logger$16.debug({
8673
+ logger$17.debug({
8026
8674
  fileCount: filesModified.length,
8027
8675
  tokenCount: scopedTotal
8028
8676
  }, "Using scoped file diff");
8029
8677
  } else {
8030
- logger$16.warn({
8678
+ logger$17.warn({
8031
8679
  estimatedTotal: scopedTotal,
8032
8680
  ceiling: TOKEN_CEILING,
8033
8681
  fileCount: filesModified.length
@@ -8041,7 +8689,7 @@ async function runCodeReview(deps, params) {
8041
8689
  const fullTotal = nonDiffTokens + countTokens(fullDiff);
8042
8690
  if (fullTotal <= TOKEN_CEILING) gitDiffContent = fullDiff;
8043
8691
  else {
8044
- logger$16.warn({
8692
+ logger$17.warn({
8045
8693
  estimatedTotal: fullTotal,
8046
8694
  ceiling: TOKEN_CEILING
8047
8695
  }, "Full git diff would exceed token ceiling — using stat-only summary");
@@ -8049,7 +8697,7 @@ async function runCodeReview(deps, params) {
8049
8697
  }
8050
8698
  }
8051
8699
  if (gitDiffContent.trim().length === 0 && baselineCommit) {
8052
- logger$16.info({
8700
+ logger$17.info({
8053
8701
  storyKey,
8054
8702
  baselineCommit
8055
8703
  }, "Working tree diff empty — diffing against baseline commit");
@@ -8057,7 +8705,7 @@ async function runCodeReview(deps, params) {
8057
8705
  const commitTotal = nonDiffTokens + countTokens(commitDiff);
8058
8706
  if (commitDiff.trim().length > 0) if (commitTotal <= TOKEN_CEILING) gitDiffContent = commitDiff;
8059
8707
  else {
8060
- logger$16.warn({
8708
+ logger$17.warn({
8061
8709
  estimatedTotal: commitTotal,
8062
8710
  ceiling: TOKEN_CEILING
8063
8711
  }, "Baseline..HEAD diff exceeds token ceiling — using stat-only summary");
@@ -8065,7 +8713,7 @@ async function runCodeReview(deps, params) {
8065
8713
  }
8066
8714
  }
8067
8715
  if (gitDiffContent.trim().length === 0) {
8068
- logger$16.info({ storyKey }, "Empty git diff — skipping review with SHIP_IT");
8716
+ logger$17.info({ storyKey }, "Empty git diff — skipping review with SHIP_IT");
8069
8717
  return {
8070
8718
  verdict: "SHIP_IT",
8071
8719
  issues: 0,
@@ -8081,7 +8729,7 @@ async function runCodeReview(deps, params) {
8081
8729
  if (deps.repoMapInjector !== void 0) {
8082
8730
  const injection = await deps.repoMapInjector.buildContext(storyContent, deps.maxRepoMapTokens ?? 2e3);
8083
8731
  repoContextContent = injection.text;
8084
- logger$16.info({
8732
+ logger$17.info({
8085
8733
  storyKey,
8086
8734
  repoMapTokens: Math.ceil(injection.text.length / 4),
8087
8735
  symbolCount: injection.symbolCount,
@@ -8101,17 +8749,17 @@ async function runCodeReview(deps, params) {
8101
8749
  const findings = await getProjectFindings(deps.db);
8102
8750
  if (findings.length > 0) {
8103
8751
  priorFindingsContent = "Previous reviews found these recurring patterns — pay special attention:\n\n" + findings;
8104
- logger$16.debug({
8752
+ logger$17.debug({
8105
8753
  storyKey,
8106
8754
  findingsLen: findings.length
8107
8755
  }, "Injecting prior findings into code-review prompt");
8108
8756
  }
8109
8757
  } catch {}
8110
8758
  const testMetricsContent = await countTestMetrics(filesModified, cwd);
8111
- if (testMetricsContent) logger$16.debug({ storyKey }, "Injecting verified test-count metrics into code-review context");
8759
+ if (testMetricsContent) logger$17.debug({ storyKey }, "Injecting verified test-count metrics into code-review context");
8112
8760
  const fileDiffs = gitDiffContent ? parseDiffByFile(gitDiffContent) : void 0;
8113
8761
  const scopeAnalysisContent = storyContent && filesModified ? ScopeGuardrail.buildAnalysis(storyContent, filesModified, fileDiffs) : "";
8114
- if (scopeAnalysisContent) logger$16.debug({ storyKey }, "Scope analysis detected out-of-scope files");
8762
+ if (scopeAnalysisContent) logger$17.debug({ storyKey }, "Scope analysis detected out-of-scope files");
8115
8763
  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" : "";
8116
8764
  const sections = [
8117
8765
  {
@@ -8156,11 +8804,11 @@ async function runCodeReview(deps, params) {
8156
8804
  }
8157
8805
  ];
8158
8806
  const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
8159
- if (assembleResult.truncated) logger$16.warn({
8807
+ if (assembleResult.truncated) logger$17.warn({
8160
8808
  storyKey,
8161
8809
  tokenCount: assembleResult.tokenCount
8162
8810
  }, "Code-review prompt truncated to fit token ceiling");
8163
- logger$16.debug({
8811
+ logger$17.debug({
8164
8812
  storyKey,
8165
8813
  tokenCount: assembleResult.tokenCount,
8166
8814
  truncated: assembleResult.truncated
@@ -8181,7 +8829,7 @@ async function runCodeReview(deps, params) {
8181
8829
  dispatchResult = await handle.result;
8182
8830
  } catch (err) {
8183
8831
  const error = err instanceof Error ? err.message : String(err);
8184
- logger$16.error({
8832
+ logger$17.error({
8185
8833
  storyKey,
8186
8834
  error
8187
8835
  }, "Code-review dispatch threw unexpected error");
@@ -8197,7 +8845,7 @@ async function runCodeReview(deps, params) {
8197
8845
  const rawOutput = dispatchResult.output ?? void 0;
8198
8846
  if (dispatchResult.status === "failed") {
8199
8847
  const errorMsg = `Dispatch status: failed. Exit code: ${dispatchResult.exitCode}. ${dispatchResult.parseError ?? ""} ${dispatchResult.output ? `Stderr: ${dispatchResult.output}` : ""}`.trim();
8200
- logger$16.warn({
8848
+ logger$17.warn({
8201
8849
  storyKey,
8202
8850
  exitCode: dispatchResult.exitCode
8203
8851
  }, "Code-review dispatch failed");
@@ -8207,7 +8855,7 @@ async function runCodeReview(deps, params) {
8207
8855
  };
8208
8856
  }
8209
8857
  if (dispatchResult.status === "timeout") {
8210
- logger$16.warn({ storyKey }, "Code-review dispatch timed out");
8858
+ logger$17.warn({ storyKey }, "Code-review dispatch timed out");
8211
8859
  return {
8212
8860
  ...defaultFailResult("Dispatch status: timeout. The agent did not complete within the allowed time.", tokenUsage),
8213
8861
  rawOutput
@@ -8215,7 +8863,7 @@ async function runCodeReview(deps, params) {
8215
8863
  }
8216
8864
  if (dispatchResult.parsed === null) {
8217
8865
  const details = dispatchResult.parseError ?? "No YAML block found in output";
8218
- logger$16.warn({
8866
+ logger$17.warn({
8219
8867
  storyKey,
8220
8868
  details
8221
8869
  }, "Code-review output schema validation failed");
@@ -8233,7 +8881,7 @@ async function runCodeReview(deps, params) {
8233
8881
  const parseResult = CodeReviewResultSchema.safeParse(dispatchResult.parsed);
8234
8882
  if (!parseResult.success) {
8235
8883
  const details = parseResult.error.message;
8236
- logger$16.warn({
8884
+ logger$17.warn({
8237
8885
  storyKey,
8238
8886
  details
8239
8887
  }, "Code-review output failed schema validation");
@@ -8249,13 +8897,13 @@ async function runCodeReview(deps, params) {
8249
8897
  };
8250
8898
  }
8251
8899
  const parsed = parseResult.data;
8252
- if (parsed.agentVerdict !== parsed.verdict) logger$16.info({
8900
+ if (parsed.agentVerdict !== parsed.verdict) logger$17.info({
8253
8901
  storyKey,
8254
8902
  agentVerdict: parsed.agentVerdict,
8255
8903
  pipelineVerdict: parsed.verdict,
8256
8904
  issues: parsed.issues
8257
8905
  }, "Pipeline overrode agent verdict based on issue severities");
8258
- logger$16.info({
8906
+ logger$17.info({
8259
8907
  storyKey,
8260
8908
  verdict: parsed.verdict,
8261
8909
  issues: parsed.issues
@@ -8280,11 +8928,236 @@ async function getArchConstraints$2(deps) {
8280
8928
  if (constraints.length === 0) return "";
8281
8929
  return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
8282
8930
  } catch (err) {
8283
- logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
8931
+ logger$17.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
8284
8932
  return "";
8285
8933
  }
8286
8934
  }
8287
8935
 
8936
+ //#endregion
8937
+ //#region src/modules/compiled-workflows/merge-to-main.ts
8938
+ const logger$16 = createLogger("compiled-workflows:merge-to-main");
8939
+ /**
8940
+ * Execute the merge-to-main phase: merge the story branch into the base branch.
8941
+ *
8942
+ * Attempts fast-forward first, falls back to 3-way merge. On conflict:
8943
+ * emits `pipeline:merge-conflict-detected` event, aborts the merge, preserves
8944
+ * the worktree and branch for operator inspection, and returns a failure result.
8945
+ *
8946
+ * Merge commands run against `projectRoot` (the main working tree).
8947
+ * The main working tree must already be on `startBranch` (checked out at
8948
+ * orchestrator startup — no branch switching is performed here).
8949
+ *
8950
+ * @param params - Merge phase parameters (storyKey, branchName, startBranch,
8951
+ * worktreeManager, eventBus, projectRoot)
8952
+ * @returns MergeToMainResult — success=true on merged; success=false with
8953
+ * reason='merge-conflict-detected' and conflictingFiles on conflict.
8954
+ */
8955
+ async function runMergeToMain(params) {
8956
+ const { storyKey, branchName, startBranch, worktreeManager, eventBus, projectRoot } = params;
8957
+ logger$16.info({
8958
+ storyKey,
8959
+ branchName,
8960
+ startBranch
8961
+ }, "Starting merge-to-main phase");
8962
+ const ffSuccess = tryFfMerge(branchName, projectRoot);
8963
+ if (ffSuccess) {
8964
+ logger$16.info({
8965
+ storyKey,
8966
+ branchName
8967
+ }, "Fast-forward merge succeeded");
8968
+ await cleanupAfterSuccessfulMerge(storyKey, branchName, worktreeManager, projectRoot);
8969
+ return { success: true };
8970
+ }
8971
+ logger$16.info({
8972
+ storyKey,
8973
+ branchName
8974
+ }, "Fast-forward merge failed — attempting 3-way merge");
8975
+ const threeWayResult = tryThreeWayMerge(branchName, projectRoot);
8976
+ if (threeWayResult.success) {
8977
+ logger$16.info({
8978
+ storyKey,
8979
+ branchName
8980
+ }, "3-way merge succeeded");
8981
+ await cleanupAfterSuccessfulMerge(storyKey, branchName, worktreeManager, projectRoot);
8982
+ return { success: true };
8983
+ }
8984
+ const { conflictingFiles } = threeWayResult;
8985
+ logger$16.warn({
8986
+ storyKey,
8987
+ branchName,
8988
+ conflictingFiles
8989
+ }, "Merge conflict detected — preserving worktree for operator inspection");
8990
+ eventBus.emit("pipeline:merge-conflict-detected", {
8991
+ storyKey,
8992
+ branchName,
8993
+ conflictingFiles
8994
+ });
8995
+ return {
8996
+ success: false,
8997
+ reason: "merge-conflict-detected",
8998
+ conflictingFiles
8999
+ };
9000
+ }
9001
+ /**
9002
+ * Attempt a fast-forward merge of `branchName` into the current branch.
9003
+ *
9004
+ * @param branchName - Branch to merge
9005
+ * @param projectRoot - Working directory (main worktree, already on startBranch)
9006
+ * @returns - true if FF succeeded, false if it failed (branch diverged)
9007
+ */
9008
+ function tryFfMerge(branchName, projectRoot) {
9009
+ try {
9010
+ execFileSync("git", [
9011
+ "merge",
9012
+ "--ff-only",
9013
+ branchName
9014
+ ], {
9015
+ cwd: projectRoot,
9016
+ stdio: [
9017
+ "ignore",
9018
+ "pipe",
9019
+ "pipe"
9020
+ ]
9021
+ });
9022
+ return true;
9023
+ } catch {
9024
+ return false;
9025
+ }
9026
+ }
9027
+ /**
9028
+ * Attempt a 3-way merge of `branchName` into the current branch.
9029
+ *
9030
+ * On conflict: parses conflicting files from `git diff --name-only --diff-filter=U`
9031
+ * and aborts the merge before returning.
9032
+ *
9033
+ * @param branchName - Branch to merge
9034
+ * @param projectRoot - Working directory (main worktree, already on startBranch)
9035
+ * @returns - { success: true } on clean merge;
9036
+ * { success: false, conflictingFiles: string[] } on conflict
9037
+ */
9038
+ function tryThreeWayMerge(branchName, projectRoot) {
9039
+ try {
9040
+ execFileSync("git", [
9041
+ "merge",
9042
+ "--no-edit",
9043
+ branchName
9044
+ ], {
9045
+ cwd: projectRoot,
9046
+ stdio: [
9047
+ "ignore",
9048
+ "pipe",
9049
+ "pipe"
9050
+ ]
9051
+ });
9052
+ return {
9053
+ success: true,
9054
+ conflictingFiles: []
9055
+ };
9056
+ } catch {
9057
+ let conflictingFiles = [];
9058
+ try {
9059
+ const diffOutput = execFileSync("git", [
9060
+ "diff",
9061
+ "--name-only",
9062
+ "--diff-filter=U"
9063
+ ], {
9064
+ cwd: projectRoot,
9065
+ encoding: "utf-8",
9066
+ stdio: [
9067
+ "ignore",
9068
+ "pipe",
9069
+ "pipe"
9070
+ ]
9071
+ });
9072
+ conflictingFiles = diffOutput.trim().split("\n").filter((line) => line.length > 0);
9073
+ } catch (diffErr) {
9074
+ logger$16.warn({ err: diffErr }, "Failed to list conflicting files (best-effort)");
9075
+ }
9076
+ try {
9077
+ execFileSync("git", ["merge", "--abort"], {
9078
+ cwd: projectRoot,
9079
+ stdio: [
9080
+ "ignore",
9081
+ "pipe",
9082
+ "pipe"
9083
+ ]
9084
+ });
9085
+ } catch (abortErr) {
9086
+ logger$16.warn({ err: abortErr }, "Failed to abort merge (best-effort)");
9087
+ }
9088
+ return {
9089
+ success: false,
9090
+ conflictingFiles
9091
+ };
9092
+ }
9093
+ }
9094
+ /**
9095
+ * Clean up the worktree and delete the merged branch after a successful merge.
9096
+ *
9097
+ * Cleanup is best-effort — a failure here does not revert the already-completed
9098
+ * git merge. Both operations are attempted independently.
9099
+ *
9100
+ * @param storyKey - Story key (used as worktree task ID)
9101
+ * @param branchName - Branch that was merged (to delete)
9102
+ * @param worktreeManager - Manager for removing the worktree directory
9103
+ * @param projectRoot - Working directory for git branch deletion
9104
+ */
9105
+ async function cleanupAfterSuccessfulMerge(storyKey, branchName, worktreeManager, projectRoot) {
9106
+ try {
9107
+ await worktreeManager.cleanupWorktree(storyKey);
9108
+ logger$16.info({ storyKey }, "Worktree removed after successful merge");
9109
+ } catch (worktreeErr) {
9110
+ logger$16.warn({
9111
+ storyKey,
9112
+ err: worktreeErr
9113
+ }, "Failed to remove worktree (best-effort)");
9114
+ }
9115
+ try {
9116
+ execFileSync("git", [
9117
+ "branch",
9118
+ "-d",
9119
+ branchName
9120
+ ], {
9121
+ cwd: projectRoot,
9122
+ stdio: [
9123
+ "ignore",
9124
+ "pipe",
9125
+ "pipe"
9126
+ ]
9127
+ });
9128
+ logger$16.info({
9129
+ storyKey,
9130
+ branchName
9131
+ }, "Merged branch deleted");
9132
+ } catch (branchErr) {
9133
+ logger$16.warn({
9134
+ storyKey,
9135
+ branchName,
9136
+ err: branchErr
9137
+ }, "Failed to delete merged branch (best-effort)");
9138
+ }
9139
+ }
9140
+ /**
9141
+ * Create a serialized merge queue to prevent concurrent git merge operations.
9142
+ *
9143
+ * Returns an `enqueueMerge` function that wraps `runMergeToMain`. All calls
9144
+ * through the returned function are serialized: each invocation waits for the
9145
+ * previous to fully complete before starting. This prevents data-corruption
9146
+ * from concurrent `git merge` operations against the same base branch (AC7).
9147
+ *
9148
+ * Used by `orchestrator-impl.ts` to create a per-run merge queue. Exported
9149
+ * separately so the serialization logic can be unit-tested independently of
9150
+ * the orchestrator (see AC8d in Story 75-2).
9151
+ *
9152
+ * @returns A queue-serialized wrapper around `runMergeToMain`.
9153
+ */
9154
+ function createMergeQueue() {
9155
+ let queue = Promise.resolve();
9156
+ return (params) => new Promise((resolve$6, reject) => {
9157
+ queue = queue.then(() => runMergeToMain(params)).then(resolve$6, reject).then(() => void 0, () => void 0);
9158
+ });
9159
+ }
9160
+
8288
9161
  //#endregion
8289
9162
  //#region src/modules/compiled-workflows/test-plan.ts
8290
9163
  const logger$15 = createLogger("compiled-workflows:test-plan");
@@ -12071,6 +12944,17 @@ function topologicalSortByDependencies(keys, projectRoot) {
12071
12944
  function toSdlcEventBus(bus) {
12072
12945
  return bus;
12073
12946
  }
12947
+ /**
12948
+ * Project the monolith event bus to `TypedEventBus<CoreEvents>` for GitWorktreeManager.
12949
+ *
12950
+ * Story 75-1 (Path E spike 2026-05-10): OrchestratorEvents mirrors the CoreEvents worktree
12951
+ * events ('worktree:created', 'worktree:removed') so the cast is safe at runtime.
12952
+ * TypeScript cannot perform the direct cast due to generic method signatures; the
12953
+ * `as unknown` intermediate is isolated here to keep call sites clean.
12954
+ */
12955
+ function toCoreEventBus(bus) {
12956
+ return bus;
12957
+ }
12074
12958
  function estimateDispatchCost(input, output) {
12075
12959
  return (input * 3 + output * 15) / 1e6;
12076
12960
  }
@@ -12386,8 +13270,13 @@ function checkProfileStaleness(projectRoot) {
12386
13270
  * @returns A fully-configured ImplementationOrchestrator ready to call run()
12387
13271
  */
12388
13272
  function createImplementationOrchestrator(deps) {
12389
- const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot, tokenCeilings, stateStore, telemetryPersistence, ingestionServer, repoMapInjector, maxRepoMapTokens, agentId, runManifest = null } = deps;
12390
- const logger$26 = createLogger("implementation-orchestrator");
13273
+ const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot, tokenCeilings, stateStore, telemetryPersistence, ingestionServer, repoMapInjector, maxRepoMapTokens, agentId, runManifest = null, worktreeManager } = deps;
13274
+ const noWorktree = config.noWorktree ?? false;
13275
+ const _worktreeManager = worktreeManager ?? (projectRoot !== void 0 && !noWorktree ? createGitWorktreeManager({
13276
+ eventBus: toCoreEventBus(eventBus),
13277
+ projectRoot
13278
+ }) : void 0);
13279
+ const logger$27 = createLogger("implementation-orchestrator");
12391
13280
  const telemetryAdvisor = db !== void 0 ? createTelemetryAdvisor({ db }) : void 0;
12392
13281
  const wgRepo = new WorkGraphRepository(db);
12393
13282
  const _wgInProgressWritten = new Set();
@@ -12413,6 +13302,7 @@ function createImplementationOrchestrator(deps) {
12413
13302
  let _completedDispatches = 0;
12414
13303
  let _maxConcurrentActual = 0;
12415
13304
  let _packageSnapshot;
13305
+ let _orchestratorStartBranch;
12416
13306
  let _contractMismatches;
12417
13307
  const _costChecker = new CostGovernanceChecker();
12418
13308
  let _costWarningEmitted = false;
@@ -12429,6 +13319,7 @@ function createImplementationOrchestrator(deps) {
12429
13319
  const verificationPipeline = createDefaultVerificationPipeline(toSdlcEventBus(eventBus), void 0);
12430
13320
  const _stateStoreCache = new Map();
12431
13321
  const _checkpoints = new Map();
13322
+ const enqueueMerge = createMergeQueue();
12432
13323
  const MEMORY_PRESSURE_BACKOFF_MS = [
12433
13324
  3e4,
12434
13325
  6e4,
@@ -12471,7 +13362,7 @@ function createImplementationOrchestrator(deps) {
12471
13362
  const existingCount = storyState?.retry_count ?? 0;
12472
13363
  _storyRetryCount.set(storyKey, existingCount);
12473
13364
  } catch (err) {
12474
- logger$26.warn({
13365
+ logger$27.warn({
12475
13366
  err,
12476
13367
  storyKey
12477
13368
  }, "initRetryCount: failed to read manifest — starting at 0");
@@ -12485,7 +13376,7 @@ function createImplementationOrchestrator(deps) {
12485
13376
  const current = _storyRetryCount.get(storyKey) ?? 0;
12486
13377
  const next = current + 1;
12487
13378
  _storyRetryCount.set(storyKey, next);
12488
- if (runManifest !== null && runManifest !== void 0) runManifest.patchStoryState(storyKey, { retry_count: next }).catch((err) => logger$26.warn({
13379
+ if (runManifest !== null && runManifest !== void 0) runManifest.patchStoryState(storyKey, { retry_count: next }).catch((err) => logger$27.warn({
12489
13380
  err,
12490
13381
  storyKey
12491
13382
  }, "patchStoryState(retry_count) failed — pipeline continues"));
@@ -12498,7 +13389,7 @@ function createImplementationOrchestrator(deps) {
12498
13389
  const nowMs = Date.now();
12499
13390
  for (const [phase, startMs] of starts) {
12500
13391
  const endMs = ends?.get(phase);
12501
- if (endMs === void 0) logger$26.warn({
13392
+ if (endMs === void 0) logger$27.warn({
12502
13393
  storyKey,
12503
13394
  phase
12504
13395
  }, "Phase has no end time — story may have errored mid-phase. Duration capped to now() and may be inflated.");
@@ -12515,7 +13406,7 @@ function createImplementationOrchestrator(deps) {
12515
13406
  const wallClockSeconds = startedAt ? Math.round((new Date(completedAt).getTime() - new Date(startedAt).getTime()) / 1e3) : 0;
12516
13407
  const wallClockMs = startedAt ? new Date(completedAt).getTime() - new Date(startedAt).getTime() : 0;
12517
13408
  const tokenAgg = await aggregateTokenUsageForStory(db, config.pipelineRunId, storyKey);
12518
- if (runManifest !== null) runManifest.patchStoryState(storyKey, { cost_usd: tokenAgg.cost }).catch((err) => logger$26.warn({
13409
+ if (runManifest !== null) runManifest.patchStoryState(storyKey, { cost_usd: tokenAgg.cost }).catch((err) => logger$27.warn({
12519
13410
  err,
12520
13411
  storyKey
12521
13412
  }, "patchStoryState(cost_usd) failed — pipeline continues"));
@@ -12551,7 +13442,7 @@ function createImplementationOrchestrator(deps) {
12551
13442
  recordedAt: completedAt,
12552
13443
  timestamp: completedAt
12553
13444
  }).catch((storeErr) => {
12554
- logger$26.warn({
13445
+ logger$27.warn({
12555
13446
  err: storeErr,
12556
13447
  storyKey
12557
13448
  }, "Failed to record metric to StateStore (best-effort)");
@@ -12573,7 +13464,7 @@ function createImplementationOrchestrator(deps) {
12573
13464
  rationale: `Story ${storyKey} completed with result=${result} in ${wallClockSeconds}s. Tokens: ${tokenAgg.input}+${tokenAgg.output}. Review cycles: ${reviewCycles}.`
12574
13465
  });
12575
13466
  } catch (decisionErr) {
12576
- logger$26.warn({
13467
+ logger$27.warn({
12577
13468
  err: decisionErr,
12578
13469
  storyKey
12579
13470
  }, "Failed to write story-metrics decision (best-effort)");
@@ -12652,7 +13543,7 @@ function createImplementationOrchestrator(deps) {
12652
13543
  const LOW_OUTPUT_TOKEN_THRESHOLD = 100;
12653
13544
  const unverified = tokenAgg.output < LOW_OUTPUT_TOKEN_THRESHOLD;
12654
13545
  if (unverified) {
12655
- logger$26.warn({
13546
+ logger$27.warn({
12656
13547
  storyKey,
12657
13548
  outputTokens: tokenAgg.output,
12658
13549
  threshold: LOW_OUTPUT_TOKEN_THRESHOLD
@@ -12676,13 +13567,13 @@ function createImplementationOrchestrator(deps) {
12676
13567
  ...unverified ? { unverified: true } : {}
12677
13568
  });
12678
13569
  } catch (emitErr) {
12679
- logger$26.warn({
13570
+ logger$27.warn({
12680
13571
  err: emitErr,
12681
13572
  storyKey
12682
13573
  }, "Failed to emit story:metrics event (best-effort)");
12683
13574
  }
12684
13575
  } catch (err) {
12685
- logger$26.warn({
13576
+ logger$27.warn({
12686
13577
  err,
12687
13578
  storyKey
12688
13579
  }, "Failed to write story metrics (best-effort)");
@@ -12711,7 +13602,7 @@ function createImplementationOrchestrator(deps) {
12711
13602
  rationale: `Story ${storyKey} ${outcome} after ${reviewCycles} review cycle(s).`
12712
13603
  });
12713
13604
  } catch (err) {
12714
- logger$26.warn({
13605
+ logger$27.warn({
12715
13606
  err,
12716
13607
  storyKey
12717
13608
  }, "Failed to write story-outcome decision (best-effort)");
@@ -12749,7 +13640,7 @@ function createImplementationOrchestrator(deps) {
12749
13640
  rationale: `Escalation diagnosis for ${payload.storyKey}: ${diagnosis.recommendedAction} — ${diagnosis.rationale}`
12750
13641
  });
12751
13642
  } catch (err) {
12752
- logger$26.warn({
13643
+ logger$27.warn({
12753
13644
  err,
12754
13645
  storyKey: payload.storyKey
12755
13646
  }, "Failed to persist escalation diagnosis (best-effort)");
@@ -12799,7 +13690,7 @@ function createImplementationOrchestrator(deps) {
12799
13690
  const existing = _stories.get(storyKey);
12800
13691
  if (existing !== void 0) {
12801
13692
  Object.assign(existing, updates);
12802
- persistStoryState(storyKey, existing).catch((err) => logger$26.warn({
13693
+ persistStoryState(storyKey, existing).catch((err) => logger$27.warn({
12803
13694
  err,
12804
13695
  storyKey
12805
13696
  }, "StateStore write failed after updateStory"));
@@ -12808,12 +13699,12 @@ function createImplementationOrchestrator(deps) {
12808
13699
  storyKey,
12809
13700
  conflict: err
12810
13701
  });
12811
- else logger$26.warn({
13702
+ else logger$27.warn({
12812
13703
  err,
12813
13704
  storyKey
12814
13705
  }, "mergeStory failed");
12815
13706
  });
12816
- else if (updates.phase === "ESCALATED" || updates.phase === "VERIFICATION_FAILED") stateStore?.rollbackStory(storyKey).catch((err) => logger$26.warn({
13707
+ else if (updates.phase === "ESCALATED" || updates.phase === "VERIFICATION_FAILED") stateStore?.rollbackStory(storyKey).catch((err) => logger$27.warn({
12817
13708
  err,
12818
13709
  storyKey
12819
13710
  }, "rollbackStory failed — branch may persist"));
@@ -12825,7 +13716,7 @@ function createImplementationOrchestrator(deps) {
12825
13716
  ...updates
12826
13717
  };
12827
13718
  const opts = targetStatus === "complete" || targetStatus === "escalated" ? { completedAt: fullUpdated.completedAt } : void 0;
12828
- wgRepo.updateStoryStatus(storyKey, targetStatus, opts).catch((err) => logger$26.warn({
13719
+ wgRepo.updateStoryStatus(storyKey, targetStatus, opts).catch((err) => logger$27.warn({
12829
13720
  err,
12830
13721
  storyKey
12831
13722
  }, "wg_stories status update failed (best-effort)"));
@@ -12841,7 +13732,7 @@ function createImplementationOrchestrator(deps) {
12841
13732
  status: "dispatched",
12842
13733
  phase: String(updates.phase),
12843
13734
  started_at: fullUpdated.startedAt ?? new Date().toISOString()
12844
- }).catch((err) => logger$26.warn({
13735
+ }).catch((err) => logger$27.warn({
12845
13736
  err,
12846
13737
  storyKey
12847
13738
  }, "patchStoryState(dispatched) failed — pipeline continues"));
@@ -12853,13 +13744,13 @@ function createImplementationOrchestrator(deps) {
12853
13744
  completed_at: fullUpdated.completedAt ?? new Date().toISOString(),
12854
13745
  review_cycles: fullUpdated.reviewCycles ?? 0,
12855
13746
  dispatches: _storyDispatches.get(storyKey) ?? 0
12856
- }).catch((err) => logger$26.warn({
13747
+ }).catch((err) => logger$27.warn({
12857
13748
  err,
12858
13749
  storyKey
12859
13750
  }, `patchStoryState(${manifestStatus}) failed — pipeline continues`));
12860
13751
  } else {
12861
13752
  const intermediatePhase = updates.phase;
12862
- runManifest.patchStoryState(storyKey, { phase: String(intermediatePhase) }).catch((err) => logger$26.warn({
13753
+ runManifest.patchStoryState(storyKey, { phase: String(intermediatePhase) }).catch((err) => logger$27.warn({
12863
13754
  err,
12864
13755
  storyKey,
12865
13756
  phase: intermediatePhase
@@ -12890,7 +13781,7 @@ function createImplementationOrchestrator(deps) {
12890
13781
  };
12891
13782
  await stateStore.setStoryState(storyKey, record);
12892
13783
  } catch (err) {
12893
- logger$26.warn({
13784
+ logger$27.warn({
12894
13785
  err,
12895
13786
  storyKey
12896
13787
  }, "StateStore.setStoryState failed (best-effort)");
@@ -12906,7 +13797,7 @@ function createImplementationOrchestrator(deps) {
12906
13797
  token_usage_json: serialized
12907
13798
  });
12908
13799
  } catch (err) {
12909
- logger$26.warn({ err }, "Failed to persist orchestrator state");
13800
+ logger$27.warn({ err }, "Failed to persist orchestrator state");
12910
13801
  }
12911
13802
  }
12912
13803
  function recordProgress() {
@@ -12954,7 +13845,7 @@ function createImplementationOrchestrator(deps) {
12954
13845
  ...Object.keys(perStoryState).length > 0 ? { perStoryState } : {}
12955
13846
  });
12956
13847
  if (config.pipelineRunId !== void 0) updatePipelineRun(db, config.pipelineRunId, { current_phase: "implementation" }).catch((err) => {
12957
- logger$26.debug({ err }, "Heartbeat: failed to touch updated_at (non-fatal)");
13848
+ logger$27.debug({ err }, "Heartbeat: failed to touch updated_at (non-fatal)");
12958
13849
  });
12959
13850
  const elapsed = Date.now() - _lastProgressTs;
12960
13851
  let childPids = [];
@@ -12976,7 +13867,7 @@ function createImplementationOrchestrator(deps) {
12976
13867
  }
12977
13868
  if (childActive) {
12978
13869
  _lastProgressTs = Date.now();
12979
- logger$26.debug({
13870
+ logger$27.debug({
12980
13871
  storyKey: key,
12981
13872
  phase: s$1.phase,
12982
13873
  childPids
@@ -12985,7 +13876,7 @@ function createImplementationOrchestrator(deps) {
12985
13876
  }
12986
13877
  _stalledStories.add(key);
12987
13878
  _storiesWithStall.add(key);
12988
- logger$26.warn({
13879
+ logger$27.warn({
12989
13880
  storyKey: key,
12990
13881
  phase: s$1.phase,
12991
13882
  elapsedMs: elapsed,
@@ -13030,7 +13921,7 @@ function createImplementationOrchestrator(deps) {
13030
13921
  for (let attempt = 0; attempt < MEMORY_PRESSURE_BACKOFF_MS.length; attempt++) {
13031
13922
  const memState = dispatcher.getMemoryState();
13032
13923
  if (!memState.isPressured) return true;
13033
- logger$26.warn({
13924
+ logger$27.warn({
13034
13925
  storyKey,
13035
13926
  freeMB: memState.freeMB,
13036
13927
  thresholdMB: memState.thresholdMB,
@@ -13050,12 +13941,12 @@ function createImplementationOrchestrator(deps) {
13050
13941
  * exhausted retries the story is ESCALATED.
13051
13942
  */
13052
13943
  async function processStory(storyKey, storyOptions) {
13053
- logger$26.info({ storyKey }, "Processing story");
13944
+ logger$27.info({ storyKey }, "Processing story");
13054
13945
  await initRetryCount(storyKey);
13055
13946
  {
13056
13947
  const memoryOk = await checkMemoryPressure(storyKey);
13057
13948
  if (!memoryOk) {
13058
- logger$26.warn({ storyKey }, "Memory pressure exhausted — escalating story without dispatch");
13949
+ logger$27.warn({ storyKey }, "Memory pressure exhausted — escalating story without dispatch");
13059
13950
  const memPressureState = {
13060
13951
  phase: "ESCALATED",
13061
13952
  reviewCycles: 0,
@@ -13064,7 +13955,7 @@ function createImplementationOrchestrator(deps) {
13064
13955
  completedAt: new Date().toISOString()
13065
13956
  };
13066
13957
  _stories.set(storyKey, memPressureState);
13067
- persistStoryState(storyKey, memPressureState).catch((err) => logger$26.warn({
13958
+ persistStoryState(storyKey, memPressureState).catch((err) => logger$27.warn({
13068
13959
  err,
13069
13960
  storyKey
13070
13961
  }, "StateStore write failed after memory-pressure escalation"));
@@ -13079,9 +13970,14 @@ function createImplementationOrchestrator(deps) {
13079
13970
  return;
13080
13971
  }
13081
13972
  }
13973
+ let effectiveProjectRoot = projectRoot;
13974
+ if (!noWorktree && _worktreeManager !== void 0 && projectRoot !== void 0) {
13975
+ const wt = await _worktreeManager.createWorktree(storyKey);
13976
+ effectiveProjectRoot = wt.worktreePath;
13977
+ }
13082
13978
  await waitIfPaused();
13083
13979
  if (_state !== "RUNNING") return;
13084
- stateStore?.branchForStory(storyKey).catch((err) => logger$26.warn({
13980
+ stateStore?.branchForStory(storyKey).catch((err) => logger$27.warn({
13085
13981
  err,
13086
13982
  storyKey
13087
13983
  }, "branchForStory failed — continuing without branch isolation"));
@@ -13092,7 +13988,7 @@ function createImplementationOrchestrator(deps) {
13092
13988
  });
13093
13989
  let storyFilePath;
13094
13990
  let sourceAcHash;
13095
- const artifactsDir = projectRoot ? join$1(projectRoot, "_bmad-output", "implementation-artifacts") : void 0;
13991
+ const artifactsDir = effectiveProjectRoot ? join$1(effectiveProjectRoot, "_bmad-output", "implementation-artifacts") : void 0;
13096
13992
  if (artifactsDir && existsSync(artifactsDir)) try {
13097
13993
  const files = readdirSync(artifactsDir);
13098
13994
  const STALE_SUFFIX = /\.stale-\d+\.md$/;
@@ -13100,7 +13996,7 @@ function createImplementationOrchestrator(deps) {
13100
13996
  if (match$2) {
13101
13997
  const candidatePath = join$1(artifactsDir, match$2);
13102
13998
  const validation = await isValidStoryFile(candidatePath);
13103
- if (!validation.valid) logger$26.warn({
13999
+ if (!validation.valid) logger$27.warn({
13104
14000
  storyKey,
13105
14001
  storyFilePath: candidatePath,
13106
14002
  reason: validation.reason
@@ -13108,7 +14004,7 @@ function createImplementationOrchestrator(deps) {
13108
14004
  else {
13109
14005
  let isDrift = false;
13110
14006
  try {
13111
- const epicsPath = projectRoot ? findEpicsFile(projectRoot) : void 0;
14007
+ const epicsPath = effectiveProjectRoot ? findEpicsFile(effectiveProjectRoot) : void 0;
13112
14008
  if (epicsPath !== void 0) {
13113
14009
  const epicContent = readFileSync(epicsPath, "utf-8");
13114
14010
  const sourceSection = extractStorySection(epicContent, storyKey);
@@ -13125,7 +14021,7 @@ function createImplementationOrchestrator(deps) {
13125
14021
  storedHash,
13126
14022
  currentHash
13127
14023
  });
13128
- logger$26.info({
14024
+ logger$27.info({
13129
14025
  storyKey,
13130
14026
  storedHash,
13131
14027
  currentHash
@@ -13136,7 +14032,7 @@ function createImplementationOrchestrator(deps) {
13136
14032
  } catch {}
13137
14033
  if (!isDrift) {
13138
14034
  storyFilePath = candidatePath;
13139
- logger$26.info({
14035
+ logger$27.info({
13140
14036
  storyKey,
13141
14037
  storyFilePath
13142
14038
  }, "Found existing story file — skipping create-story");
@@ -13156,12 +14052,12 @@ function createImplementationOrchestrator(deps) {
13156
14052
  const staleName = match$2.replace(/\.md$/, `.stale-${ts}.md`);
13157
14053
  const stalePath = join$1(artifactsDir, staleName);
13158
14054
  renameSync(candidatePath, stalePath);
13159
- logger$26.info({
14055
+ logger$27.info({
13160
14056
  storyKey,
13161
14057
  staleName
13162
14058
  }, `[orchestrator] story ${storyKey}: renamed drifted artifact to ${staleName} before re-dispatch`);
13163
14059
  } catch (renameErr) {
13164
- logger$26.warn({
14060
+ logger$27.warn({
13165
14061
  storyKey,
13166
14062
  err: renameErr
13167
14063
  }, "Failed to rename stale artifact before create-story re-dispatch; relying on 58-9d fraud-guard");
@@ -13169,8 +14065,8 @@ function createImplementationOrchestrator(deps) {
13169
14065
  }
13170
14066
  }
13171
14067
  } catch {}
13172
- if (storyFilePath === void 0 && projectRoot && isImplicitlyCovered(storyKey, projectRoot)) {
13173
- logger$26.info({ storyKey }, `Story ${storyKey} appears implicitly covered — all expected new files already exist. Skipping create-story.`);
14068
+ if (storyFilePath === void 0 && effectiveProjectRoot && isImplicitlyCovered(storyKey, effectiveProjectRoot)) {
14069
+ logger$27.info({ storyKey }, `Story ${storyKey} appears implicitly covered — all expected new files already exist. Skipping create-story.`);
13174
14070
  endPhase(storyKey, "create-story");
13175
14071
  eventBus.emit("orchestrator:story-phase-complete", {
13176
14072
  storyKey,
@@ -13199,7 +14095,7 @@ function createImplementationOrchestrator(deps) {
13199
14095
  pack,
13200
14096
  contextCompiler,
13201
14097
  dispatcher,
13202
- projectRoot,
14098
+ projectRoot: effectiveProjectRoot,
13203
14099
  tokenCeilings,
13204
14100
  otlpEndpoint: _otlpEndpoint,
13205
14101
  agentId
@@ -13223,7 +14119,7 @@ function createImplementationOrchestrator(deps) {
13223
14119
  output_tokens: createResult.tokenUsage.output,
13224
14120
  cost_usd: estimateDispatchCost(createResult.tokenUsage.input, createResult.tokenUsage.output),
13225
14121
  metadata: JSON.stringify({ storyKey })
13226
- })).catch((tokenErr) => logger$26.warn({
14122
+ })).catch((tokenErr) => logger$27.warn({
13227
14123
  storyKey,
13228
14124
  err: tokenErr
13229
14125
  }, "Failed to record create-story token usage"));
@@ -13231,7 +14127,7 @@ function createImplementationOrchestrator(deps) {
13231
14127
  if (createResult.result === "failed") {
13232
14128
  const errMsg = createResult.error ?? "create-story failed";
13233
14129
  const stderrSnippet = errMsg.includes("--- stderr ---") ? errMsg.slice(errMsg.indexOf("--- stderr ---") + 15, errMsg.indexOf("--- stderr ---") + 515) : errMsg.slice(0, 500);
13234
- logger$26.error({
14130
+ logger$27.error({
13235
14131
  storyKey,
13236
14132
  stderrSnippet
13237
14133
  }, `Create-story failed: ${stderrSnippet.split("\n")[0]}`);
@@ -13267,13 +14163,13 @@ function createImplementationOrchestrator(deps) {
13267
14163
  await persistState();
13268
14164
  return;
13269
14165
  }
13270
- if (projectRoot !== void 0) {
13271
- const expectedArtifactsDir = join$1(projectRoot, "_bmad-output", "implementation-artifacts");
14166
+ if (effectiveProjectRoot !== void 0) {
14167
+ const expectedArtifactsDir = join$1(effectiveProjectRoot, "_bmad-output", "implementation-artifacts");
13272
14168
  const escapedExpectedDir = expectedArtifactsDir.replace("/_bmad-output/", "/\\_bmad-output/");
13273
14169
  let claimedPath = createResult.story_file;
13274
14170
  if (claimedPath.startsWith(escapedExpectedDir)) {
13275
14171
  claimedPath = claimedPath.replace("/\\_bmad-output/", "/_bmad-output/");
13276
- logger$26.warn({
14172
+ logger$27.warn({
13277
14173
  storyKey,
13278
14174
  originalClaim: createResult.story_file,
13279
14175
  normalizedClaim: claimedPath
@@ -13291,7 +14187,7 @@ function createImplementationOrchestrator(deps) {
13291
14187
  if (escapedVariant !== claimedPath && existsSync(escapedVariant)) try {
13292
14188
  renameSync(escapedVariant, claimedPath);
13293
14189
  actualPath = claimedPath;
13294
- logger$26.warn({
14190
+ logger$27.warn({
13295
14191
  storyKey,
13296
14192
  escapedVariant,
13297
14193
  canonicalPath: claimedPath
@@ -13302,7 +14198,7 @@ function createImplementationOrchestrator(deps) {
13302
14198
  });
13303
14199
  } catch (renameErr) {
13304
14200
  actualPath = escapedVariant;
13305
- logger$26.warn({
14201
+ logger$27.warn({
13306
14202
  storyKey,
13307
14203
  escapedVariant,
13308
14204
  canonicalPath: claimedPath,
@@ -13317,7 +14213,7 @@ function createImplementationOrchestrator(deps) {
13317
14213
  if (actualPath === null) {
13318
14214
  const outputTokens = createResult.tokenUsage?.output ?? 0;
13319
14215
  const errMsg = `create-story claimed success (story_file: ${createResult.story_file}) but the file does not exist on disk (output tokens: ${outputTokens})`;
13320
- logger$26.error({
14216
+ logger$27.error({
13321
14217
  storyKey,
13322
14218
  claimedPath: createResult.story_file,
13323
14219
  outputTokens
@@ -13344,7 +14240,7 @@ function createImplementationOrchestrator(deps) {
13344
14240
  const mtimeISO = new Date(claimedStat.mtimeMs).toISOString();
13345
14241
  const dispatchStartISO = new Date(dispatchStartMs).toISOString();
13346
14242
  const errMsg = `create-story claimed success but did not rewrite ${actualPath} during this dispatch (file mtime ${mtimeISO} predates dispatch start ${dispatchStartISO}; output tokens: ${outputTokens})`;
13347
- logger$26.error({
14243
+ logger$27.error({
13348
14244
  storyKey,
13349
14245
  claimedPath: actualPath,
13350
14246
  mtimeISO,
@@ -13367,7 +14263,7 @@ function createImplementationOrchestrator(deps) {
13367
14263
  return;
13368
14264
  }
13369
14265
  } catch (verifyErr) {
13370
- logger$26.warn({
14266
+ logger$27.warn({
13371
14267
  storyKey,
13372
14268
  err: verifyErr
13373
14269
  }, "create-story post-dispatch file verification threw; proceeding with claimed path");
@@ -13390,7 +14286,7 @@ function createImplementationOrchestrator(deps) {
13390
14286
  const overlap = computeTitleOverlap(expectedTitle, createResult.story_title);
13391
14287
  if (overlap < TITLE_OVERLAP_WARNING_THRESHOLD) {
13392
14288
  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.`;
13393
- logger$26.warn({
14289
+ logger$27.warn({
13394
14290
  storyKey,
13395
14291
  expectedTitle,
13396
14292
  generatedTitle: createResult.story_title,
@@ -13400,7 +14296,7 @@ function createImplementationOrchestrator(deps) {
13400
14296
  storyKey,
13401
14297
  msg
13402
14298
  });
13403
- } else logger$26.debug({
14299
+ } else logger$27.debug({
13404
14300
  storyKey,
13405
14301
  expectedTitle,
13406
14302
  generatedTitle: createResult.story_title,
@@ -13409,7 +14305,7 @@ function createImplementationOrchestrator(deps) {
13409
14305
  }
13410
14306
  }
13411
14307
  } catch (titleValidationErr) {
13412
- logger$26.debug({
14308
+ logger$27.debug({
13413
14309
  storyKey,
13414
14310
  err: titleValidationErr
13415
14311
  }, "Story title validation skipped due to error");
@@ -13432,7 +14328,7 @@ function createImplementationOrchestrator(deps) {
13432
14328
  await persistState();
13433
14329
  return;
13434
14330
  }
13435
- if (storyFilePath !== void 0 && projectRoot !== void 0) try {
14331
+ if (storyFilePath !== void 0 && effectiveProjectRoot !== void 0) try {
13436
14332
  const epicId = storyKey.split("-")[0] ?? storyKey;
13437
14333
  const fidelityImplDecisions = await getDecisionsByPhase(db, "implementation");
13438
14334
  let fidelitySourceContent;
@@ -13450,7 +14346,7 @@ function createImplementationOrchestrator(deps) {
13450
14346
  const pathDrift = pathFidelity?.drift ?? 0;
13451
14347
  const clauseDrift = clauseFidelity.drift;
13452
14348
  const overallDrift = Math.max(pathDrift, clauseDrift);
13453
- logger$26.debug({
14349
+ logger$27.debug({
13454
14350
  storyKey,
13455
14351
  pathDrift,
13456
14352
  clauseDrift,
@@ -13472,7 +14368,7 @@ function createImplementationOrchestrator(deps) {
13472
14368
  if (pathMissing.length > 0) reasons.push(`${pathMissing.length} named path(s) missing`);
13473
14369
  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})`);
13474
14370
  if (clauseFidelity.clauseRatio < .7) reasons.push(`clause shortfall (rendered ${clauseFidelity.renderedClauseCount}/${clauseFidelity.sourceClauseCount} = ${Math.round(clauseFidelity.clauseRatio * 100)}%)`);
13475
- logger$26.warn({
14371
+ logger$27.warn({
13476
14372
  storyKey,
13477
14373
  pathDrift,
13478
14374
  clauseDrift,
@@ -13515,7 +14411,7 @@ function createImplementationOrchestrator(deps) {
13515
14411
  storyFilePath = void 0;
13516
14412
  continue;
13517
14413
  } catch (renameErr) {
13518
- logger$26.warn({
14414
+ logger$27.warn({
13519
14415
  storyKey,
13520
14416
  err: renameErr,
13521
14417
  stalePath
@@ -13529,7 +14425,7 @@ function createImplementationOrchestrator(deps) {
13529
14425
  if (numericMismatches.length > 0) reasons.push(`numeric mismatches: ${numericMismatches.map((m) => `${m.noun} (source=${m.sourceCount}, rendered=${m.renderedCount})`).join("; ")}`);
13530
14426
  if (clauseFidelity.clauseRatio < .7) reasons.push(`clause shortfall: source=${clauseFidelity.sourceClauseCount}, rendered=${clauseFidelity.renderedClauseCount}`);
13531
14427
  const errMsg = `create-story output drifted from source AC after ${MAX_FIDELITY_RETRIES} retries; ` + reasons.join("; ");
13532
- logger$26.error({
14428
+ logger$27.error({
13533
14429
  storyKey,
13534
14430
  pathDrift,
13535
14431
  clauseDrift,
@@ -13556,7 +14452,7 @@ function createImplementationOrchestrator(deps) {
13556
14452
  }
13557
14453
  }
13558
14454
  } catch (fidelityErr) {
13559
- logger$26.warn({
14455
+ logger$27.warn({
13560
14456
  storyKey,
13561
14457
  err: fidelityErr
13562
14458
  }, "fidelity gate threw; proceeding without retry");
@@ -13587,21 +14483,21 @@ function createImplementationOrchestrator(deps) {
13587
14483
  ...contract.transport !== void 0 ? { transport: contract.transport } : {}
13588
14484
  })
13589
14485
  });
13590
- logger$26.info({
14486
+ logger$27.info({
13591
14487
  storyKey,
13592
14488
  contractCount: contracts.length,
13593
14489
  contracts
13594
14490
  }, "Stored interface contract declarations");
13595
14491
  }
13596
14492
  } catch (err) {
13597
- logger$26.warn({
14493
+ logger$27.warn({
13598
14494
  storyKey,
13599
14495
  error: err instanceof Error ? err.message : String(err)
13600
14496
  }, "Failed to parse interface contracts — continuing without contract declarations");
13601
14497
  }
13602
14498
  if (storyFilePath && _probeAuthorEffectiveMode === "enabled") try {
13603
14499
  let probeAuthorEpicContent = "";
13604
- const probeAuthorEpicsPath = findEpicFileForStory(projectRoot ?? process.cwd(), storyKey);
14500
+ const probeAuthorEpicsPath = findEpicFileForStory(effectiveProjectRoot ?? process.cwd(), storyKey);
13605
14501
  if (probeAuthorEpicsPath) try {
13606
14502
  const epicFull = readFileSync(probeAuthorEpicsPath, "utf-8");
13607
14503
  const section = extractStorySection(epicFull, storyKey);
@@ -13612,7 +14508,7 @@ function createImplementationOrchestrator(deps) {
13612
14508
  const isStateIntegrating = stateIntegratingEnabled && detectsStateIntegratingAC(probeAuthorEpicContent);
13613
14509
  if (isEventDriven || isStateIntegrating) {
13614
14510
  const triggerClass = isEventDriven && isStateIntegrating ? "both" : isStateIntegrating ? "state-integrating" : "event-driven";
13615
- if (runManifest !== null && runManifest !== void 0) runManifest.patchStoryState(storyKey, { probe_author_triggered_by: triggerClass }).catch((err) => logger$26.warn({
14511
+ if (runManifest !== null && runManifest !== void 0) runManifest.patchStoryState(storyKey, { probe_author_triggered_by: triggerClass }).catch((err) => logger$27.warn({
13616
14512
  err,
13617
14513
  storyKey
13618
14514
  }, "patchStoryState(probe_author_triggered_by) failed — pipeline continues"));
@@ -13627,7 +14523,7 @@ function createImplementationOrchestrator(deps) {
13627
14523
  pack,
13628
14524
  contextCompiler,
13629
14525
  dispatcher,
13630
- projectRoot,
14526
+ projectRoot: effectiveProjectRoot,
13631
14527
  tokenCeilings,
13632
14528
  otlpEndpoint: _otlpEndpoint,
13633
14529
  agentId
@@ -13646,7 +14542,7 @@ function createImplementationOrchestrator(deps) {
13646
14542
  });
13647
14543
  }
13648
14544
  });
13649
- logger$26.info({
14545
+ logger$27.info({
13650
14546
  storyKey,
13651
14547
  result: probeAuthorResult.result,
13652
14548
  probesAuthoredCount: probeAuthorResult.probesAuthoredCount
@@ -13658,14 +14554,14 @@ function createImplementationOrchestrator(deps) {
13658
14554
  output_tokens: probeAuthorResult.tokenUsage.output,
13659
14555
  cost_usd: estimateDispatchCost(probeAuthorResult.tokenUsage.input, probeAuthorResult.tokenUsage.output),
13660
14556
  metadata: JSON.stringify({ storyKey })
13661
- })).catch((tokenErr) => logger$26.warn({
14557
+ })).catch((tokenErr) => logger$27.warn({
13662
14558
  storyKey,
13663
14559
  err: tokenErr
13664
14560
  }, "Failed to record probe-author token usage"));
13665
- } else logger$26.debug({ storyKey }, "probe-author: story artifact already has ## Runtime Probes — skipping gate");
13666
- } else logger$26.debug({ storyKey }, "probe-author: source AC not event-driven — skipping gate");
14561
+ } else logger$27.debug({ storyKey }, "probe-author: story artifact already has ## Runtime Probes — skipping gate");
14562
+ } else logger$27.debug({ storyKey }, "probe-author: source AC not event-driven — skipping gate");
13667
14563
  } catch (probeAuthorErr) {
13668
- logger$26.warn({
14564
+ logger$27.warn({
13669
14565
  storyKey,
13670
14566
  err: probeAuthorErr
13671
14567
  }, "probe-author gate threw unexpectedly; proceeding to test-plan without authored probes");
@@ -13683,7 +14579,7 @@ function createImplementationOrchestrator(deps) {
13683
14579
  pack,
13684
14580
  contextCompiler,
13685
14581
  dispatcher,
13686
- projectRoot,
14582
+ projectRoot: effectiveProjectRoot,
13687
14583
  tokenCeilings,
13688
14584
  otlpEndpoint: _otlpEndpoint,
13689
14585
  agentId
@@ -13694,10 +14590,10 @@ function createImplementationOrchestrator(deps) {
13694
14590
  });
13695
14591
  testPlanPhaseResult = testPlanResult.result;
13696
14592
  testPlanTokenUsage = testPlanResult.tokenUsage;
13697
- if (testPlanResult.result === "success") logger$26.info({ storyKey }, "Test plan generated successfully");
13698
- else logger$26.warn({ storyKey }, "Test planning returned failed result — proceeding to dev-story without test plan");
14593
+ if (testPlanResult.result === "success") logger$27.info({ storyKey }, "Test plan generated successfully");
14594
+ else logger$27.warn({ storyKey }, "Test planning returned failed result — proceeding to dev-story without test plan");
13699
14595
  } catch (err) {
13700
- logger$26.warn({
14596
+ logger$27.warn({
13701
14597
  storyKey,
13702
14598
  err
13703
14599
  }, "Test planning failed — proceeding to dev-story without test plan");
@@ -13710,7 +14606,7 @@ function createImplementationOrchestrator(deps) {
13710
14606
  output_tokens: testPlanTokenUsage.output,
13711
14607
  cost_usd: estimateDispatchCost(testPlanTokenUsage.input, testPlanTokenUsage.output),
13712
14608
  metadata: JSON.stringify({ storyKey })
13713
- })).catch((tokenErr) => logger$26.warn({
14609
+ })).catch((tokenErr) => logger$27.warn({
13714
14610
  storyKey,
13715
14611
  err: tokenErr
13716
14612
  }, "Failed to record test-plan token usage"));
@@ -13762,7 +14658,7 @@ function createImplementationOrchestrator(deps) {
13762
14658
  let baselineHeadSha;
13763
14659
  try {
13764
14660
  baselineHeadSha = execSync("git rev-parse HEAD", {
13765
- cwd: projectRoot ?? process.cwd(),
14661
+ cwd: effectiveProjectRoot ?? process.cwd(),
13766
14662
  encoding: "utf-8",
13767
14663
  timeout: 3e3,
13768
14664
  stdio: [
@@ -13778,7 +14674,7 @@ function createImplementationOrchestrator(deps) {
13778
14674
  storyContentForAnalysis = await readFile$1(storyFilePath ?? "", "utf-8");
13779
14675
  storyContentForVerification = storyContentForAnalysis;
13780
14676
  } catch (err) {
13781
- logger$26.error({
14677
+ logger$27.error({
13782
14678
  storyKey,
13783
14679
  storyFilePath,
13784
14680
  error: err instanceof Error ? err.message : String(err)
@@ -13786,7 +14682,7 @@ function createImplementationOrchestrator(deps) {
13786
14682
  }
13787
14683
  const analysis = analyzeStoryComplexity(storyContentForAnalysis);
13788
14684
  const batches = planTaskBatches(analysis);
13789
- logger$26.info({
14685
+ logger$27.info({
13790
14686
  storyKey,
13791
14687
  estimatedScope: analysis.estimatedScope,
13792
14688
  batchCount: batches.length,
@@ -13804,7 +14700,7 @@ function createImplementationOrchestrator(deps) {
13804
14700
  if (_state !== "RUNNING") break;
13805
14701
  const taskScope = batch.taskIds.map((id, i) => `T${id}: ${batch.taskTitles[i] ?? ""}`).join("\n");
13806
14702
  const priorFiles = allFilesModified.size > 0 ? Array.from(allFilesModified) : void 0;
13807
- logger$26.info({
14703
+ logger$27.info({
13808
14704
  storyKey,
13809
14705
  batchIndex: batch.batchIndex,
13810
14706
  taskCount: batch.taskIds.length
@@ -13818,7 +14714,7 @@ function createImplementationOrchestrator(deps) {
13818
14714
  pack,
13819
14715
  contextCompiler,
13820
14716
  dispatcher,
13821
- projectRoot,
14717
+ projectRoot: effectiveProjectRoot,
13822
14718
  tokenCeilings,
13823
14719
  otlpEndpoint: _otlpEndpoint,
13824
14720
  repoMapInjector,
@@ -13835,7 +14731,7 @@ function createImplementationOrchestrator(deps) {
13835
14731
  });
13836
14732
  } catch (batchErr) {
13837
14733
  const errMsg = batchErr instanceof Error ? batchErr.message : String(batchErr);
13838
- logger$26.warn({
14734
+ logger$27.warn({
13839
14735
  storyKey,
13840
14736
  batchIndex: batch.batchIndex,
13841
14737
  error: errMsg
@@ -13856,7 +14752,7 @@ function createImplementationOrchestrator(deps) {
13856
14752
  filesModified: batchFilesModified,
13857
14753
  result: batchResult.result === "success" ? "success" : "failed"
13858
14754
  };
13859
- logger$26.info(batchMetrics, "Batch dev-story metrics");
14755
+ logger$27.info(batchMetrics, "Batch dev-story metrics");
13860
14756
  for (const f$1 of batchFilesModified) allFilesModified.add(f$1);
13861
14757
  if (batchFilesModified.length > 0) batchFileGroups.push({
13862
14758
  batchIndex: batch.batchIndex,
@@ -13875,13 +14771,13 @@ function createImplementationOrchestrator(deps) {
13875
14771
  durationMs: batchDurationMs,
13876
14772
  result: batchMetrics.result
13877
14773
  })
13878
- })).catch((tokenErr) => logger$26.warn({
14774
+ })).catch((tokenErr) => logger$27.warn({
13879
14775
  storyKey,
13880
14776
  batchIndex: batch.batchIndex,
13881
14777
  err: tokenErr
13882
14778
  }, "Failed to record batch token usage"));
13883
14779
  if (batchResult.tokenUsage?.output !== void 0) devOutputTokenCount = (devOutputTokenCount ?? 0) + batchResult.tokenUsage.output;
13884
- if (batchResult.result === "failed") logger$26.warn({
14780
+ if (batchResult.result === "failed") logger$27.warn({
13885
14781
  storyKey,
13886
14782
  batchIndex: batch.batchIndex,
13887
14783
  error: batchResult.error
@@ -13902,7 +14798,7 @@ function createImplementationOrchestrator(deps) {
13902
14798
  pack,
13903
14799
  contextCompiler,
13904
14800
  dispatcher,
13905
- projectRoot,
14801
+ projectRoot: effectiveProjectRoot,
13906
14802
  tokenCeilings,
13907
14803
  otlpEndpoint: _otlpEndpoint,
13908
14804
  repoMapInjector,
@@ -13924,7 +14820,7 @@ function createImplementationOrchestrator(deps) {
13924
14820
  output_tokens: devResult.tokenUsage.output,
13925
14821
  cost_usd: estimateDispatchCost(devResult.tokenUsage.input, devResult.tokenUsage.output),
13926
14822
  metadata: JSON.stringify({ storyKey })
13927
- })).catch((tokenErr) => logger$26.warn({
14823
+ })).catch((tokenErr) => logger$27.warn({
13928
14824
  storyKey,
13929
14825
  err: tokenErr
13930
14826
  }, "Failed to record dev-story token usage"));
@@ -13937,9 +14833,9 @@ function createImplementationOrchestrator(deps) {
13937
14833
  let checkpointHandled = false;
13938
14834
  if (devResult.result === "failed" && devResult.error?.startsWith("dispatch_timeout")) {
13939
14835
  endPhase(storyKey, "dev-story");
13940
- const timeoutFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
14836
+ const timeoutFiles = checkGitDiffFiles(effectiveProjectRoot ?? process.cwd());
13941
14837
  if (timeoutFiles.length === 0) {
13942
- logger$26.warn({ storyKey }, "Dev-story timeout with zero modified files — escalating immediately (no checkpoint)");
14838
+ logger$27.warn({ storyKey }, "Dev-story timeout with zero modified files — escalating immediately (no checkpoint)");
13943
14839
  updateStory(storyKey, {
13944
14840
  phase: "ESCALATED",
13945
14841
  error: "timeout-no-files",
@@ -13955,14 +14851,14 @@ function createImplementationOrchestrator(deps) {
13955
14851
  await persistState();
13956
14852
  return;
13957
14853
  }
13958
- logger$26.info({
14854
+ logger$27.info({
13959
14855
  storyKey,
13960
14856
  filesCount: timeoutFiles.length
13961
14857
  }, "Dev-story timeout with partial files — capturing checkpoint");
13962
14858
  let gitDiff = "";
13963
14859
  try {
13964
14860
  gitDiff = execSync(`git diff HEAD -- ${timeoutFiles.map((f$1) => `"${f$1}"`).join(" ")}`, {
13965
- cwd: projectRoot ?? process.cwd(),
14861
+ cwd: effectiveProjectRoot ?? process.cwd(),
13966
14862
  encoding: "utf-8",
13967
14863
  timeout: 1e4,
13968
14864
  stdio: [
@@ -13972,7 +14868,7 @@ function createImplementationOrchestrator(deps) {
13972
14868
  ]
13973
14869
  }).trim();
13974
14870
  } catch (diffErr) {
13975
- logger$26.warn({
14871
+ logger$27.warn({
13976
14872
  storyKey,
13977
14873
  error: diffErr instanceof Error ? diffErr.message : String(diffErr)
13978
14874
  }, "Failed to capture git diff for checkpoint — proceeding with empty diff");
@@ -13999,7 +14895,7 @@ function createImplementationOrchestrator(deps) {
13999
14895
  recordedAt: new Date().toISOString(),
14000
14896
  sprint: config.sprint
14001
14897
  }).catch((storeErr) => {
14002
- logger$26.warn({
14898
+ logger$27.warn({
14003
14899
  err: storeErr,
14004
14900
  storyKey
14005
14901
  }, "Failed to record timeout metric to StateStore (best-effort)");
@@ -14058,9 +14954,9 @@ function createImplementationOrchestrator(deps) {
14058
14954
  checkpointRetryPrompt = assembled.prompt;
14059
14955
  } catch {
14060
14956
  checkpointRetryPrompt = `Continue story ${storyKey} from checkpoint. Your prior attempt timed out. Do not redo completed work.`;
14061
- logger$26.warn({ storyKey }, "Failed to assemble checkpoint retry prompt — using fallback");
14957
+ logger$27.warn({ storyKey }, "Failed to assemble checkpoint retry prompt — using fallback");
14062
14958
  }
14063
- logger$26.info({
14959
+ logger$27.info({
14064
14960
  storyKey,
14065
14961
  filesCount: checkpointData.filesModified.length
14066
14962
  }, "Dispatching checkpoint retry for timed-out story");
@@ -14073,7 +14969,7 @@ function createImplementationOrchestrator(deps) {
14073
14969
  taskType: "dev-story",
14074
14970
  outputSchema: DevStoryResultSchema,
14075
14971
  ...checkpointRetryMaxTurns !== void 0 ? { maxTurns: checkpointRetryMaxTurns } : {},
14076
- ...projectRoot !== void 0 ? { workingDirectory: projectRoot } : {},
14972
+ ...effectiveProjectRoot !== void 0 ? { workingDirectory: effectiveProjectRoot } : {},
14077
14973
  ..._otlpEndpoint !== void 0 ? { otlpEndpoint: _otlpEndpoint } : {},
14078
14974
  ...config.perStoryContextCeilings?.[storyKey] !== void 0 ? { maxContextTokens: config.perStoryContextCeilings[storyKey] } : {},
14079
14975
  storyKey
@@ -14089,7 +14985,7 @@ function createImplementationOrchestrator(deps) {
14089
14985
  } : void 0 }
14090
14986
  });
14091
14987
  if (checkpointRetryResult.status === "timeout") {
14092
- logger$26.warn({ storyKey }, "Checkpoint retry dispatch timed out — escalating story");
14988
+ logger$27.warn({ storyKey }, "Checkpoint retry dispatch timed out — escalating story");
14093
14989
  updateStory(storyKey, {
14094
14990
  phase: "ESCALATED",
14095
14991
  error: "checkpoint-retry-timeout",
@@ -14107,9 +15003,9 @@ function createImplementationOrchestrator(deps) {
14107
15003
  }
14108
15004
  const retryParsed = checkpointRetryResult.parsed;
14109
15005
  replaceDevStorySignals(retryParsed);
14110
- devFilesModified = retryParsed?.files_modified ?? checkGitDiffFiles(projectRoot ?? process.cwd());
15006
+ devFilesModified = retryParsed?.files_modified ?? checkGitDiffFiles(effectiveProjectRoot ?? process.cwd());
14111
15007
  if (checkpointRetryResult.status === "completed" && retryParsed?.result === "success") devStoryWasSuccess = true;
14112
- else logger$26.warn({
15008
+ else logger$27.warn({
14113
15009
  storyKey,
14114
15010
  status: checkpointRetryResult.status
14115
15011
  }, "Checkpoint retry completed with failure — proceeding to code review");
@@ -14119,13 +15015,13 @@ function createImplementationOrchestrator(deps) {
14119
15015
  replaceDevStorySignals(devResult);
14120
15016
  if (devResult.result === "success") devStoryWasSuccess = true;
14121
15017
  else {
14122
- logger$26.warn({
15018
+ logger$27.warn({
14123
15019
  storyKey,
14124
15020
  error: devResult.error,
14125
15021
  filesModified: devFilesModified.length
14126
15022
  }, "Dev-story reported failure, proceeding to code review");
14127
15023
  if (!devResult.error?.startsWith("dispatch_timeout")) {
14128
- logger$26.warn({
15024
+ logger$27.warn({
14129
15025
  storyKey,
14130
15026
  error: devResult.error
14131
15027
  }, "Agent process failure (non-timeout) — story will proceed to code review with partial work");
@@ -14157,12 +15053,12 @@ function createImplementationOrchestrator(deps) {
14157
15053
  }
14158
15054
  let gitDiffFiles;
14159
15055
  if (devStoryWasSuccess) {
14160
- gitDiffFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
15056
+ gitDiffFiles = checkGitDiffFiles(effectiveProjectRoot ?? process.cwd());
14161
15057
  if (gitDiffFiles.length === 0) {
14162
15058
  let hasNewCommits = false;
14163
15059
  if (baselineHeadSha) try {
14164
15060
  const currentHead = execSync("git rev-parse HEAD", {
14165
- cwd: projectRoot ?? process.cwd(),
15061
+ cwd: effectiveProjectRoot ?? process.cwd(),
14166
15062
  encoding: "utf-8",
14167
15063
  timeout: 3e3,
14168
15064
  stdio: [
@@ -14176,7 +15072,7 @@ function createImplementationOrchestrator(deps) {
14176
15072
  if (hasNewCommits && baselineHeadSha) {
14177
15073
  try {
14178
15074
  const committedFiles = execSync(`git diff --name-only ${baselineHeadSha}..HEAD`, {
14179
- cwd: projectRoot ?? process.cwd(),
15075
+ cwd: effectiveProjectRoot ?? process.cwd(),
14180
15076
  encoding: "utf-8",
14181
15077
  timeout: 5e3,
14182
15078
  stdio: [
@@ -14187,13 +15083,13 @@ function createImplementationOrchestrator(deps) {
14187
15083
  }).trim();
14188
15084
  if (committedFiles.length > 0) gitDiffFiles = committedFiles.split("\n").filter(Boolean);
14189
15085
  } catch {}
14190
- logger$26.info({
15086
+ logger$27.info({
14191
15087
  storyKey,
14192
15088
  baselineHeadSha,
14193
15089
  committedFileCount: gitDiffFiles?.length ?? 0
14194
15090
  }, "Working tree clean but new commits detected since dispatch — skipping zero-diff escalation");
14195
15091
  } else {
14196
- logger$26.warn({ storyKey }, "Zero-diff detected after COMPLETE dev-story — no file changes and no new commits");
15092
+ logger$27.warn({ storyKey }, "Zero-diff detected after COMPLETE dev-story — no file changes and no new commits");
14197
15093
  eventBus.emit("orchestrator:zero-diff-escalation", {
14198
15094
  storyKey,
14199
15095
  reason: "zero-diff-on-complete"
@@ -14222,11 +15118,11 @@ function createImplementationOrchestrator(deps) {
14222
15118
  let buildVerifyResult = config.skipBuildVerify === true ? { status: "skipped" } : runBuildVerification({
14223
15119
  verifyCommand: pack.manifest.verifyCommand,
14224
15120
  verifyTimeoutMs: pack.manifest.verifyTimeoutMs,
14225
- projectRoot: projectRoot ?? process.cwd(),
15121
+ projectRoot: effectiveProjectRoot ?? process.cwd(),
14226
15122
  changedFiles: gitDiffFiles
14227
15123
  });
14228
15124
  if (buildVerifyResult.status === "passed") {
14229
- const resolvedRootForTsc = projectRoot ?? process.cwd();
15125
+ const resolvedRootForTsc = effectiveProjectRoot ?? process.cwd();
14230
15126
  const tscBin = join$1(resolvedRootForTsc, "node_modules", ".bin", "tsc");
14231
15127
  const typecheckConfig = join$1(resolvedRootForTsc, "tsconfig.typecheck.json");
14232
15128
  const defaultConfig = join$1(resolvedRootForTsc, "tsconfig.json");
@@ -14243,10 +15139,10 @@ function createImplementationOrchestrator(deps) {
14243
15139
  "pipe"
14244
15140
  ]
14245
15141
  });
14246
- logger$26.info({ storyKey }, "Secondary typecheck (tsc --noEmit) passed");
15142
+ logger$27.info({ storyKey }, "Secondary typecheck (tsc --noEmit) passed");
14247
15143
  } catch (tscErr) {
14248
15144
  const tscOutput = tscErr instanceof Error && "stdout" in tscErr ? String(tscErr.stdout ?? "").slice(0, 2e3) : "";
14249
- logger$26.warn({
15145
+ logger$27.warn({
14250
15146
  storyKey,
14251
15147
  tscOutput
14252
15148
  }, "Secondary typecheck (tsc --noEmit) failed — treating as build failure");
@@ -14261,16 +15157,16 @@ function createImplementationOrchestrator(deps) {
14261
15157
  if (buildVerifyResult.status === "passed") {
14262
15158
  _buildPassed = true;
14263
15159
  eventBus.emit("story:build-verification-passed", { storyKey });
14264
- logger$26.info({ storyKey }, "Build verification passed");
15160
+ logger$27.info({ storyKey }, "Build verification passed");
14265
15161
  } else if (buildVerifyResult.status === "failed" || buildVerifyResult.status === "timeout") {
14266
15162
  const truncatedOutput = (buildVerifyResult.output ?? "").slice(0, 2e3);
14267
15163
  const reason = buildVerifyResult.reason ?? "build-verification-failed";
14268
15164
  let retryPassed = false;
14269
15165
  if (_packageSnapshot !== void 0 && buildVerifyResult.status !== "timeout") {
14270
- const resolvedRoot = projectRoot ?? process.cwd();
15166
+ const resolvedRoot = effectiveProjectRoot ?? process.cwd();
14271
15167
  const hasChanges = detectPackageChanges(_packageSnapshot, resolvedRoot);
14272
15168
  if (hasChanges) {
14273
- logger$26.warn({ storyKey }, "Package files changed since snapshot — restoring to prevent cascade");
15169
+ logger$27.warn({ storyKey }, "Package files changed since snapshot — restoring to prevent cascade");
14274
15170
  const restoreResult = restorePackageSnapshot(_packageSnapshot, { projectRoot: resolvedRoot });
14275
15171
  if (restoreResult.restored) {
14276
15172
  const retryAfterRestore = runBuildVerification({
@@ -14283,11 +15179,11 @@ function createImplementationOrchestrator(deps) {
14283
15179
  retryPassed = true;
14284
15180
  _buildPassed = true;
14285
15181
  eventBus.emit("story:build-verification-passed", { storyKey });
14286
- logger$26.warn({
15182
+ logger$27.warn({
14287
15183
  storyKey,
14288
15184
  filesRestored: restoreResult.filesRestored
14289
15185
  }, "Build passed after package snapshot restore — cross-story pollution detected and cleaned");
14290
- } else logger$26.warn({
15186
+ } else logger$27.warn({
14291
15187
  storyKey,
14292
15188
  filesRestored: restoreResult.filesRestored
14293
15189
  }, "Build still fails after snapshot restore — story has its own build errors");
@@ -14298,8 +15194,8 @@ function createImplementationOrchestrator(deps) {
14298
15194
  const missingPkgMatch = fullOutput.match(/Cannot find (?:module|package) ['"]([^'"]+)['"]/) ?? fullOutput.match(/ERR_MODULE_NOT_FOUND[^]*?['"]([^'"]+)['"]/);
14299
15195
  if (missingPkgMatch && buildVerifyResult.status !== "timeout") {
14300
15196
  const missingPkg = missingPkgMatch[1].replace(/^(@[^/]+\/[^/]+)\/.*$/, "$1").replace(/^([^@][^/]*)\/.*$/, "$1");
14301
- const resolvedRoot = projectRoot ?? process.cwd();
14302
- logger$26.warn({
15197
+ const resolvedRoot = effectiveProjectRoot ?? process.cwd();
15198
+ logger$27.warn({
14303
15199
  storyKey,
14304
15200
  missingPkg
14305
15201
  }, "Build-fix retry: detected missing npm package — attempting npm install");
@@ -14310,7 +15206,7 @@ function createImplementationOrchestrator(deps) {
14310
15206
  encoding: "utf-8",
14311
15207
  stdio: "pipe"
14312
15208
  });
14313
- logger$26.warn({
15209
+ logger$27.warn({
14314
15210
  storyKey,
14315
15211
  missingPkg
14316
15212
  }, "Build-fix retry: npm install succeeded — retrying build verification");
@@ -14324,18 +15220,18 @@ function createImplementationOrchestrator(deps) {
14324
15220
  retryPassed = true;
14325
15221
  _buildPassed = true;
14326
15222
  eventBus.emit("story:build-verification-passed", { storyKey });
14327
- logger$26.warn({
15223
+ logger$27.warn({
14328
15224
  storyKey,
14329
15225
  missingPkg
14330
15226
  }, "Build-fix retry: build verification passed after installing missing package");
14331
- } else logger$26.warn({
15227
+ } else logger$27.warn({
14332
15228
  storyKey,
14333
15229
  missingPkg,
14334
15230
  retryStatus: retryResult.status
14335
15231
  }, "Build-fix retry: build still fails after installing missing package — escalating");
14336
15232
  } catch (installErr) {
14337
15233
  const installMsg = installErr instanceof Error ? installErr.message : String(installErr);
14338
- logger$26.warn({
15234
+ logger$27.warn({
14339
15235
  storyKey,
14340
15236
  missingPkg,
14341
15237
  error: installMsg
@@ -14345,7 +15241,7 @@ function createImplementationOrchestrator(deps) {
14345
15241
  if (!retryPassed) {
14346
15242
  let buildFixPassed = false;
14347
15243
  if (buildVerifyResult.status === "failed" && storyFilePath !== void 0) try {
14348
- logger$26.info({ storyKey }, "Dispatching build-fix agent");
15244
+ logger$27.info({ storyKey }, "Dispatching build-fix agent");
14349
15245
  startPhase(storyKey, "build-fix");
14350
15246
  const storyContent = await readFile$1(storyFilePath, "utf-8");
14351
15247
  let buildFixTemplate;
@@ -14366,7 +15262,7 @@ function createImplementationOrchestrator(deps) {
14366
15262
  agent: deps.agentId ?? "claude-code",
14367
15263
  taskType: "build-fix",
14368
15264
  maxTurns: 15,
14369
- workingDirectory: projectRoot ?? process.cwd(),
15265
+ workingDirectory: effectiveProjectRoot ?? process.cwd(),
14370
15266
  ...config.perStoryContextCeilings?.[storyKey] !== void 0 ? { maxContextTokens: config.perStoryContextCeilings[storyKey] } : {},
14371
15267
  ..._otlpEndpoint !== void 0 ? { otlpEndpoint: _otlpEndpoint } : {}
14372
15268
  });
@@ -14375,18 +15271,18 @@ function createImplementationOrchestrator(deps) {
14375
15271
  const retryAfterFix = runBuildVerification({
14376
15272
  verifyCommand: pack.manifest.verifyCommand,
14377
15273
  verifyTimeoutMs: pack.manifest.verifyTimeoutMs,
14378
- projectRoot: projectRoot ?? process.cwd(),
15274
+ projectRoot: effectiveProjectRoot ?? process.cwd(),
14379
15275
  changedFiles: gitDiffFiles
14380
15276
  });
14381
15277
  if (retryAfterFix.status === "passed") {
14382
15278
  buildFixPassed = true;
14383
15279
  _buildPassed = true;
14384
15280
  eventBus.emit("story:build-verification-passed", { storyKey });
14385
- logger$26.info({ storyKey }, "Build passed after build-fix dispatch");
14386
- } else logger$26.warn({ storyKey }, "Build still fails after build-fix dispatch — escalating");
15281
+ logger$27.info({ storyKey }, "Build passed after build-fix dispatch");
15282
+ } else logger$27.warn({ storyKey }, "Build still fails after build-fix dispatch — escalating");
14387
15283
  } catch (fixErr) {
14388
15284
  const fixMsg = fixErr instanceof Error ? fixErr.message : String(fixErr);
14389
- logger$26.warn({
15285
+ logger$27.warn({
14390
15286
  storyKey,
14391
15287
  error: fixMsg
14392
15288
  }, "Build-fix dispatch failed — escalating");
@@ -14423,7 +15319,7 @@ function createImplementationOrchestrator(deps) {
14423
15319
  eventBus.emit("decision:halt-skipped-non-interactive", payload);
14424
15320
  }
14425
15321
  }).catch((err) => {
14426
- logger$26.warn({
15322
+ logger$27.warn({
14427
15323
  err,
14428
15324
  storyKey
14429
15325
  }, "interactive prompt failed — continuing with default action");
@@ -14440,7 +15336,7 @@ function createImplementationOrchestrator(deps) {
14440
15336
  exitCode: buildVerifyResult.exitCode ?? 1,
14441
15337
  output: truncatedOutput
14442
15338
  });
14443
- logger$26.warn({
15339
+ logger$27.warn({
14444
15340
  storyKey,
14445
15341
  reason,
14446
15342
  exitCode: buildVerifyResult.exitCode,
@@ -14470,11 +15366,11 @@ function createImplementationOrchestrator(deps) {
14470
15366
  if (filesModified.length > 0) {
14471
15367
  const icResult = detectInterfaceChanges({
14472
15368
  filesModified,
14473
- projectRoot: projectRoot ?? process.cwd(),
15369
+ projectRoot: effectiveProjectRoot ?? process.cwd(),
14474
15370
  storyKey
14475
15371
  });
14476
15372
  if (icResult.potentiallyAffectedTests.length > 0) {
14477
- logger$26.warn({
15373
+ logger$27.warn({
14478
15374
  storyKey,
14479
15375
  modifiedInterfaces: icResult.modifiedInterfaces,
14480
15376
  potentiallyAffectedTests: icResult.potentiallyAffectedTests
@@ -14514,7 +15410,7 @@ function createImplementationOrchestrator(deps) {
14514
15410
  rawOutput: reviewResult.rawOutput
14515
15411
  } : void 0;
14516
15412
  let sourceEpicContent;
14517
- const epicsPath = findEpicFileForStory(projectRoot ?? process.cwd(), storyKey);
15413
+ const epicsPath = findEpicFileForStory(effectiveProjectRoot ?? process.cwd(), storyKey);
14518
15414
  if (epicsPath) try {
14519
15415
  const epicFull = readFileSync(epicsPath, "utf-8");
14520
15416
  const section = extractStorySection(epicFull, storyKey);
@@ -14523,7 +15419,7 @@ function createImplementationOrchestrator(deps) {
14523
15419
  await persistDevStorySignals(storyKey, devStorySignals, runManifest);
14524
15420
  const verifContext = assembleVerificationContext({
14525
15421
  storyKey,
14526
- workingDir: projectRoot ?? process.cwd(),
15422
+ workingDir: effectiveProjectRoot ?? process.cwd(),
14527
15423
  reviewResult: latestReviewSignals,
14528
15424
  storyContent: storyContentForVerification,
14529
15425
  devStoryResult: devStorySignals,
@@ -14557,20 +15453,20 @@ function createImplementationOrchestrator(deps) {
14557
15453
  adapter: db,
14558
15454
  engine: "linear"
14559
15455
  }).catch((recoveryErr) => {
14560
- logger$26.warn({
15456
+ logger$27.warn({
14561
15457
  storyKey,
14562
15458
  err: recoveryErr instanceof Error ? recoveryErr.message : String(recoveryErr)
14563
15459
  }, "Recovery Engine invocation failed — falling through to VERIFICATION_FAILED (best-effort)");
14564
15460
  return null;
14565
15461
  });
14566
15462
  if (recoveryResult?.action === "halt-entire-run") {
14567
- logger$26.error({
15463
+ logger$27.error({
14568
15464
  storyKey,
14569
15465
  pendingProposalsCount: recoveryResult.pendingProposalsCount
14570
15466
  }, "Recovery Engine safety valve: halting entire run due to >= 5 pending proposals");
14571
15467
  _budgetExhausted = true;
14572
15468
  } else if (recoveryResult?.action === "retry") {
14573
- logger$26.info({
15469
+ logger$27.info({
14574
15470
  storyKey,
14575
15471
  attempt: recoveryResult.attempt,
14576
15472
  retryBudgetRemaining: recoveryResult.retryBudgetRemaining
@@ -14582,7 +15478,7 @@ function createImplementationOrchestrator(deps) {
14582
15478
  pack,
14583
15479
  contextCompiler,
14584
15480
  dispatcher,
14585
- projectRoot,
15481
+ projectRoot: effectiveProjectRoot,
14586
15482
  tokenCeilings,
14587
15483
  otlpEndpoint: _otlpEndpoint,
14588
15484
  repoMapInjector,
@@ -14598,7 +15494,7 @@ function createImplementationOrchestrator(deps) {
14598
15494
  await persistDevStorySignals(storyKey, devStorySignals, runManifest);
14599
15495
  const retryVerifContext = assembleVerificationContext({
14600
15496
  storyKey,
14601
- workingDir: projectRoot ?? process.cwd(),
15497
+ workingDir: effectiveProjectRoot ?? process.cwd(),
14602
15498
  reviewResult: latestReviewSignals,
14603
15499
  storyContent: storyContentForVerification,
14604
15500
  devStoryResult: devStorySignals,
@@ -14610,23 +15506,23 @@ function createImplementationOrchestrator(deps) {
14610
15506
  verificationStore.set(storyKey, retryVerifSummary);
14611
15507
  await persistVerificationResult(storyKey, retryVerifSummary, runManifest);
14612
15508
  if (retryVerifSummary.status !== "fail") {
14613
- logger$26.info({ storyKey }, "Recovery Engine Tier A retry succeeded — story proceeding to COMPLETE");
15509
+ logger$27.info({ storyKey }, "Recovery Engine Tier A retry succeeded — story proceeding to COMPLETE");
14614
15510
  shouldFallThroughToComplete = true;
14615
- } else logger$26.warn({ storyKey }, "Recovery Engine Tier A retry still failed — falling through to VERIFICATION_FAILED");
15511
+ } else logger$27.warn({ storyKey }, "Recovery Engine Tier A retry still failed — falling through to VERIFICATION_FAILED");
14616
15512
  } catch (retryErr) {
14617
- logger$26.warn({
15513
+ logger$27.warn({
14618
15514
  storyKey,
14619
15515
  err: retryErr instanceof Error ? retryErr.message : String(retryErr)
14620
15516
  }, "Recovery Engine Tier A re-dispatch threw — falling through to VERIFICATION_FAILED");
14621
15517
  }
14622
15518
  } else if (recoveryResult?.action === "propose") {
14623
- logger$26.info({ storyKey }, "Recovery Engine Tier B: proposal appended — marking story ESCALATED for operator re-scope");
15519
+ logger$27.info({ storyKey }, "Recovery Engine Tier B: proposal appended — marking story ESCALATED for operator re-scope");
14624
15520
  updateStory(storyKey, {
14625
15521
  phase: "ESCALATED",
14626
15522
  completedAt: new Date().toISOString(),
14627
15523
  error: "recovery-engine-propose"
14628
15524
  });
14629
- persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
15525
+ persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$27.warn({
14630
15526
  err,
14631
15527
  storyKey
14632
15528
  }, "StateStore write failed after recovery-propose"));
@@ -14640,7 +15536,7 @@ function createImplementationOrchestrator(deps) {
14640
15536
  await persistState();
14641
15537
  return "verification-failed";
14642
15538
  } else if (recoveryResult?.action === "halt") {
14643
- logger$26.warn({ storyKey }, "Recovery Engine Tier C: halt — invoking interactive prompt for operator decision");
15539
+ logger$27.warn({ storyKey }, "Recovery Engine Tier C: halt — invoking interactive prompt for operator decision");
14644
15540
  const haltRunId = config.pipelineRunId ?? storyKey;
14645
15541
  await runInteractivePrompt({
14646
15542
  runId: haltRunId,
@@ -14657,7 +15553,7 @@ function createImplementationOrchestrator(deps) {
14657
15553
  eventBus.emit("decision:halt-skipped-non-interactive", haltPayload);
14658
15554
  }
14659
15555
  }).catch((err) => {
14660
- logger$26.warn({
15556
+ logger$27.warn({
14661
15557
  err,
14662
15558
  storyKey
14663
15559
  }, "Recovery Engine Tier C: interactive prompt failed — escalating anyway");
@@ -14667,7 +15563,7 @@ function createImplementationOrchestrator(deps) {
14667
15563
  completedAt: new Date().toISOString(),
14668
15564
  error: "recovery-engine-halt"
14669
15565
  });
14670
- persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
15566
+ persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$27.warn({
14671
15567
  err,
14672
15568
  storyKey
14673
15569
  }, "StateStore write failed after recovery-halt"));
@@ -14687,7 +15583,7 @@ function createImplementationOrchestrator(deps) {
14687
15583
  phase: "VERIFICATION_FAILED",
14688
15584
  completedAt: new Date().toISOString()
14689
15585
  });
14690
- persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$26.warn({
15586
+ persistStoryState(storyKey, _stories.get(storyKey)).catch((err) => logger$27.warn({
14691
15587
  err,
14692
15588
  storyKey
14693
15589
  }, "StateStore write failed after verification-failed"));
@@ -14713,7 +15609,7 @@ function createImplementationOrchestrator(deps) {
14713
15609
  if (autoApprove?.downgradeLastVerdict !== void 0) completeUpdate.lastVerdict = autoApprove.downgradeLastVerdict;
14714
15610
  updateStory(storyKey, completeUpdate);
14715
15611
  if (config.skipVerification !== true && runManifest != null) Promise.resolve().then(() => runManifest.read()).then((manifest) => {
14716
- if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$26.warn({
15612
+ if (manifest?.per_story_state?.[storyKey]?.verification_result == null) logger$27.warn({
14717
15613
  storyKey,
14718
15614
  category: "verification-result-missing"
14719
15615
  }, "post-COMPLETE invariant: verification_result absent in manifest");
@@ -14782,7 +15678,7 @@ function createImplementationOrchestrator(deps) {
14782
15678
  "NEEDS_MAJOR_REWORK": 2
14783
15679
  };
14784
15680
  for (const group of batchFileGroups) {
14785
- logger$26.info({
15681
+ logger$27.info({
14786
15682
  storyKey,
14787
15683
  batchIndex: group.batchIndex,
14788
15684
  fileCount: group.files.length
@@ -14793,7 +15689,7 @@ function createImplementationOrchestrator(deps) {
14793
15689
  pack,
14794
15690
  contextCompiler,
14795
15691
  dispatcher,
14796
- projectRoot,
15692
+ projectRoot: effectiveProjectRoot,
14797
15693
  tokenCeilings,
14798
15694
  otlpEndpoint: _otlpEndpoint,
14799
15695
  repoMapInjector,
@@ -14803,7 +15699,7 @@ function createImplementationOrchestrator(deps) {
14803
15699
  }, {
14804
15700
  storyKey,
14805
15701
  storyFilePath: storyFilePath ?? "",
14806
- workingDirectory: projectRoot,
15702
+ workingDirectory: effectiveProjectRoot,
14807
15703
  pipelineRunId: config.pipelineRunId,
14808
15704
  filesModified: group.files,
14809
15705
  buildPassed: _buildPassed,
@@ -14827,7 +15723,7 @@ function createImplementationOrchestrator(deps) {
14827
15723
  rawOutput: lastRawOutput,
14828
15724
  tokenUsage: aggregateTokens
14829
15725
  };
14830
- logger$26.info({
15726
+ logger$27.info({
14831
15727
  storyKey,
14832
15728
  batchCount: batchFileGroups.length,
14833
15729
  verdict: worstVerdict,
@@ -14840,7 +15736,7 @@ function createImplementationOrchestrator(deps) {
14840
15736
  pack,
14841
15737
  contextCompiler,
14842
15738
  dispatcher,
14843
- projectRoot,
15739
+ projectRoot: effectiveProjectRoot,
14844
15740
  tokenCeilings,
14845
15741
  otlpEndpoint: _otlpEndpoint,
14846
15742
  repoMapInjector,
@@ -14850,7 +15746,7 @@ function createImplementationOrchestrator(deps) {
14850
15746
  }, {
14851
15747
  storyKey,
14852
15748
  storyFilePath: storyFilePath ?? "",
14853
- workingDirectory: projectRoot,
15749
+ workingDirectory: effectiveProjectRoot,
14854
15750
  pipelineRunId: config.pipelineRunId,
14855
15751
  filesModified: devFilesModified,
14856
15752
  buildPassed: _buildPassed,
@@ -14870,7 +15766,7 @@ function createImplementationOrchestrator(deps) {
14870
15766
  storyKey,
14871
15767
  reviewCycle: reviewCycles
14872
15768
  })
14873
- })).catch((tokenErr) => logger$26.warn({
15769
+ })).catch((tokenErr) => logger$27.warn({
14874
15770
  storyKey,
14875
15771
  err: tokenErr
14876
15772
  }, "Failed to record code-review token usage"));
@@ -14888,7 +15784,7 @@ function createImplementationOrchestrator(deps) {
14888
15784
  ...reviewResult.details !== void 0 ? { details: reviewResult.details } : {}
14889
15785
  });
14890
15786
  if (schemaValidationRetries <= MAX_SCHEMA_VALIDATION_RETRIES) {
14891
- logger$26.warn({
15787
+ logger$27.warn({
14892
15788
  storyKey,
14893
15789
  reviewCycles,
14894
15790
  attempt: schemaValidationRetries,
@@ -14897,7 +15793,7 @@ function createImplementationOrchestrator(deps) {
14897
15793
  previousIterationWasMalformed = true;
14898
15794
  continue;
14899
15795
  }
14900
- logger$26.warn({
15796
+ logger$27.warn({
14901
15797
  storyKey,
14902
15798
  reviewCycles,
14903
15799
  schemaValidationRetries
@@ -14920,7 +15816,7 @@ function createImplementationOrchestrator(deps) {
14920
15816
  }
14921
15817
  if (isPhantomReview && !timeoutRetried) {
14922
15818
  timeoutRetried = true;
14923
- logger$26.warn({
15819
+ logger$27.warn({
14924
15820
  storyKey,
14925
15821
  reviewCycles,
14926
15822
  error: reviewResult.error
@@ -14928,7 +15824,7 @@ function createImplementationOrchestrator(deps) {
14928
15824
  continue;
14929
15825
  }
14930
15826
  if (isPhantomReview && timeoutRetried) {
14931
- logger$26.warn({
15827
+ logger$27.warn({
14932
15828
  storyKey,
14933
15829
  reviewCycles,
14934
15830
  error: reviewResult.error
@@ -14952,7 +15848,7 @@ function createImplementationOrchestrator(deps) {
14952
15848
  verdict = reviewResult.verdict;
14953
15849
  issueList = reviewResult.issue_list ?? [];
14954
15850
  if (verdict === "NEEDS_MAJOR_REWORK" && reviewCycles > 0 && previousIssueList.length > 0 && issueList.length < previousIssueList.length) {
14955
- logger$26.info({
15851
+ logger$27.info({
14956
15852
  storyKey,
14957
15853
  originalVerdict: verdict,
14958
15854
  issuesBefore: previousIssueList.length,
@@ -14988,7 +15884,7 @@ function createImplementationOrchestrator(deps) {
14988
15884
  if (_decomposition !== void 0) parts.push(`decomposed: ${_decomposition.batchCount} batches`);
14989
15885
  parts.push(`${fileCount} files`);
14990
15886
  parts.push(`${totalTokensK} tokens`);
14991
- logger$26.info({
15887
+ logger$27.info({
14992
15888
  storyKey,
14993
15889
  verdict,
14994
15890
  agentVerdict: reviewResult.agentVerdict
@@ -15032,9 +15928,9 @@ function createImplementationOrchestrator(deps) {
15032
15928
  }),
15033
15929
  rationale: `Advisory notes from LGTM_WITH_NOTES review of ${storyKey}`
15034
15930
  });
15035
- logger$26.info({ storyKey }, "Advisory notes persisted to decision store");
15931
+ logger$27.info({ storyKey }, "Advisory notes persisted to decision store");
15036
15932
  } catch (advisoryErr) {
15037
- logger$26.warn({
15933
+ logger$27.warn({
15038
15934
  storyKey,
15039
15935
  error: advisoryErr instanceof Error ? advisoryErr.message : String(advisoryErr)
15040
15936
  }, "Failed to persist advisory notes (best-effort)");
@@ -15042,27 +15938,27 @@ function createImplementationOrchestrator(deps) {
15042
15938
  if (telemetryPersistence !== void 0) try {
15043
15939
  const turns = await telemetryPersistence.getTurnAnalysis(storyKey);
15044
15940
  if (turns.length > 0) {
15045
- const scorer = new EfficiencyScorer(logger$26);
15941
+ const scorer = new EfficiencyScorer(logger$27);
15046
15942
  const effScore = scorer.score(storyKey, turns);
15047
15943
  await telemetryPersistence.storeEfficiencyScore(effScore);
15048
- logger$26.info({
15944
+ logger$27.info({
15049
15945
  storyKey,
15050
15946
  compositeScore: effScore.compositeScore,
15051
15947
  modelCount: effScore.perModelBreakdown.length
15052
15948
  }, "Efficiency score computed and persisted");
15053
- } else logger$26.debug({ storyKey }, "No turn analysis data available — skipping efficiency scoring");
15949
+ } else logger$27.debug({ storyKey }, "No turn analysis data available — skipping efficiency scoring");
15054
15950
  } catch (effErr) {
15055
- logger$26.warn({
15951
+ logger$27.warn({
15056
15952
  storyKey,
15057
15953
  error: effErr instanceof Error ? effErr.message : String(effErr)
15058
15954
  }, "Efficiency scoring failed — story verdict unchanged");
15059
15955
  }
15060
15956
  if (telemetryPersistence !== void 0) try {
15061
15957
  const turns = await telemetryPersistence.getTurnAnalysis(storyKey);
15062
- if (turns.length === 0) logger$26.debug({ storyKey }, "No turn analysis data for telemetry categorization — skipping");
15958
+ if (turns.length === 0) logger$27.debug({ storyKey }, "No turn analysis data for telemetry categorization — skipping");
15063
15959
  else {
15064
- const categorizer = new Categorizer(logger$26);
15065
- const consumerAnalyzer = new ConsumerAnalyzer(categorizer, logger$26);
15960
+ const categorizer = new Categorizer(logger$27);
15961
+ const consumerAnalyzer = new ConsumerAnalyzer(categorizer, logger$27);
15066
15962
  const categoryStats = categorizer.computeCategoryStatsFromTurns(turns);
15067
15963
  const consumerStats = consumerAnalyzer.analyzeFromTurns(turns);
15068
15964
  await telemetryPersistence.storeCategoryStats(storyKey, categoryStats);
@@ -15070,7 +15966,7 @@ function createImplementationOrchestrator(deps) {
15070
15966
  const growingCount = categoryStats.filter((c) => c.trend === "growing").length;
15071
15967
  const topCategory = categoryStats[0]?.category ?? "none";
15072
15968
  const topConsumer = consumerStats[0]?.consumerKey ?? "none";
15073
- logger$26.info({
15969
+ logger$27.info({
15074
15970
  storyKey,
15075
15971
  topCategory,
15076
15972
  topConsumer,
@@ -15078,7 +15974,7 @@ function createImplementationOrchestrator(deps) {
15078
15974
  }, "Semantic categorization and consumer analysis complete");
15079
15975
  }
15080
15976
  } catch (catErr) {
15081
- logger$26.warn({
15977
+ logger$27.warn({
15082
15978
  storyKey,
15083
15979
  error: catErr instanceof Error ? catErr.message : String(catErr)
15084
15980
  }, "Semantic categorization failed — story verdict unchanged");
@@ -15089,7 +15985,7 @@ function createImplementationOrchestrator(deps) {
15089
15985
  pack,
15090
15986
  contextCompiler,
15091
15987
  dispatcher,
15092
- projectRoot,
15988
+ projectRoot: effectiveProjectRoot,
15093
15989
  tokenCeilings,
15094
15990
  otlpEndpoint: _otlpEndpoint,
15095
15991
  agentId
@@ -15098,9 +15994,9 @@ function createImplementationOrchestrator(deps) {
15098
15994
  storyFilePath: storyFilePath ?? "",
15099
15995
  pipelineRunId: config.pipelineRunId,
15100
15996
  filesModified: devFilesModified,
15101
- workingDirectory: projectRoot
15997
+ workingDirectory: effectiveProjectRoot
15102
15998
  });
15103
- logger$26.debug({
15999
+ logger$27.debug({
15104
16000
  storyKey,
15105
16001
  expansion_priority: expansionResult.expansion_priority,
15106
16002
  coverage_gaps: expansionResult.coverage_gaps.length
@@ -15113,11 +16009,73 @@ function createImplementationOrchestrator(deps) {
15113
16009
  value: JSON.stringify(expansionResult)
15114
16010
  });
15115
16011
  } catch (expansionErr) {
15116
- logger$26.warn({
16012
+ logger$27.warn({
15117
16013
  storyKey,
15118
16014
  error: expansionErr instanceof Error ? expansionErr.message : String(expansionErr)
15119
16015
  }, "Test expansion failed — story verdict unchanged");
15120
16016
  }
16017
+ if (worktreeManager !== void 0 && _orchestratorStartBranch !== void 0 && projectRoot !== void 0) {
16018
+ const branchName = `substrate/story-${storyKey}`;
16019
+ logger$27.info({
16020
+ storyKey,
16021
+ branchName,
16022
+ startBranch: _orchestratorStartBranch
16023
+ }, "Invoking merge-to-main phase");
16024
+ let mergeResult;
16025
+ try {
16026
+ mergeResult = await enqueueMerge({
16027
+ storyKey,
16028
+ branchName,
16029
+ startBranch: _orchestratorStartBranch,
16030
+ worktreeManager,
16031
+ eventBus,
16032
+ projectRoot
16033
+ });
16034
+ } catch (mergeErr) {
16035
+ const errMsg = mergeErr instanceof Error ? mergeErr.message : String(mergeErr);
16036
+ logger$27.error({
16037
+ storyKey,
16038
+ err: mergeErr
16039
+ }, "merge-to-main phase threw unexpectedly — escalating story");
16040
+ updateStory(storyKey, {
16041
+ phase: "ESCALATED",
16042
+ error: `merge-to-main-error: ${errMsg}`,
16043
+ completedAt: new Date().toISOString()
16044
+ });
16045
+ await emitEscalation({
16046
+ storyKey,
16047
+ lastVerdict: "merge-to-main-error",
16048
+ reviewCycles: completedReviewCycles,
16049
+ issues: [`merge-to-main phase threw: ${errMsg}`]
16050
+ });
16051
+ await persistState();
16052
+ return;
16053
+ }
16054
+ if (!mergeResult.success) {
16055
+ logger$27.warn({
16056
+ storyKey,
16057
+ branchName,
16058
+ conflictingFiles: mergeResult.conflictingFiles
16059
+ }, "merge-to-main conflict — escalating story with merge-conflict-detected");
16060
+ updateStory(storyKey, {
16061
+ phase: "ESCALATED",
16062
+ error: "merge-conflict-detected",
16063
+ completedAt: new Date().toISOString()
16064
+ });
16065
+ await emitEscalation({
16066
+ storyKey,
16067
+ lastVerdict: "merge-conflict-detected",
16068
+ reviewCycles: completedReviewCycles,
16069
+ issues: [`merge conflict in ${mergeResult.conflictingFiles?.length ?? 0} file(s): ${(mergeResult.conflictingFiles ?? []).join(", ")}`]
16070
+ });
16071
+ await persistState();
16072
+ return;
16073
+ }
16074
+ logger$27.info({
16075
+ storyKey,
16076
+ branchName
16077
+ }, "merge-to-main phase completed successfully");
16078
+ }
15121
16079
  keepReviewing = false;
15122
16080
  return;
15123
16081
  }
@@ -15140,7 +16098,7 @@ function createImplementationOrchestrator(deps) {
15140
16098
  await persistState();
15141
16099
  return;
15142
16100
  }
15143
- logger$26.info({
16101
+ logger$27.info({
15144
16102
  storyKey,
15145
16103
  reviewCycles: finalReviewCycles,
15146
16104
  issueCount: issueList.length
@@ -15206,13 +16164,13 @@ function createImplementationOrchestrator(deps) {
15206
16164
  fixPrompt = assembled.prompt;
15207
16165
  } catch {
15208
16166
  fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, minor fixes needed`;
15209
- logger$26.warn({ storyKey }, "Failed to assemble auto-approve fix prompt, using fallback");
16167
+ logger$27.warn({ storyKey }, "Failed to assemble auto-approve fix prompt, using fallback");
15210
16168
  }
15211
16169
  const handle = dispatcher.dispatch({
15212
16170
  prompt: fixPrompt,
15213
16171
  agent: deps.agentId ?? "claude-code",
15214
16172
  taskType: "minor-fixes",
15215
- workingDirectory: projectRoot,
16173
+ workingDirectory: effectiveProjectRoot,
15216
16174
  ...autoApproveMaxTurns !== void 0 ? { maxTurns: autoApproveMaxTurns } : {},
15217
16175
  ...config.perStoryContextCeilings?.[storyKey] !== void 0 ? { maxContextTokens: config.perStoryContextCeilings[storyKey] } : {},
15218
16176
  ..._otlpEndpoint !== void 0 ? { otlpEndpoint: _otlpEndpoint } : {},
@@ -15227,9 +16185,9 @@ function createImplementationOrchestrator(deps) {
15227
16185
  output: fixResult.tokenEstimate.output
15228
16186
  } : void 0 }
15229
16187
  });
15230
- if (fixResult.status === "timeout") logger$26.warn({ storyKey }, "Auto-approve fix timed out — approving anyway (issues were minor)");
16188
+ if (fixResult.status === "timeout") logger$27.warn({ storyKey }, "Auto-approve fix timed out — approving anyway (issues were minor)");
15231
16189
  } catch (err) {
15232
- logger$26.warn({
16190
+ logger$27.warn({
15233
16191
  storyKey,
15234
16192
  err
15235
16193
  }, "Auto-approve fix dispatch failed — approving anyway (issues were minor)");
@@ -15257,7 +16215,7 @@ function createImplementationOrchestrator(deps) {
15257
16215
  outcome: "retried",
15258
16216
  cost_usd: 0,
15259
16217
  timestamp: new Date().toISOString()
15260
- }).catch((err) => logger$26.warn({
16218
+ }).catch((err) => logger$27.warn({
15261
16219
  err,
15262
16220
  storyKey
15263
16221
  }, "appendRecoveryEntry failed — pipeline continues"));
@@ -15309,9 +16267,9 @@ function createImplementationOrchestrator(deps) {
15309
16267
  } catch {}
15310
16268
  let gitDiffContent = "";
15311
16269
  try {
15312
- const diffFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
16270
+ const diffFiles = checkGitDiffFiles(effectiveProjectRoot ?? process.cwd());
15313
16271
  if (diffFiles.length > 0) gitDiffContent = execSync(`git diff HEAD -- ${diffFiles.map((f$1) => `"${f$1}"`).join(" ")}`, {
15314
- cwd: projectRoot ?? process.cwd(),
16272
+ cwd: effectiveProjectRoot ?? process.cwd(),
15315
16273
  encoding: "utf-8",
15316
16274
  timeout: 1e4,
15317
16275
  stdio: [
@@ -15397,7 +16355,7 @@ function createImplementationOrchestrator(deps) {
15397
16355
  fixPrompt = assembled.prompt;
15398
16356
  } catch {
15399
16357
  fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, taskType=${taskType}`;
15400
- logger$26.warn({
16358
+ logger$27.warn({
15401
16359
  storyKey,
15402
16360
  taskType
15403
16361
  }, "Failed to assemble fix prompt, using fallback");
@@ -15411,7 +16369,7 @@ function createImplementationOrchestrator(deps) {
15411
16369
  outputSchema: DevStoryResultSchema,
15412
16370
  ...fixMaxTurns !== void 0 ? { maxTurns: fixMaxTurns } : {},
15413
16371
  ...config.perStoryContextCeilings?.[storyKey] !== void 0 ? { maxContextTokens: config.perStoryContextCeilings[storyKey] } : {},
15414
- ...projectRoot !== void 0 ? { workingDirectory: projectRoot } : {},
16372
+ ...effectiveProjectRoot !== void 0 ? { workingDirectory: effectiveProjectRoot } : {},
15415
16373
  ..._otlpEndpoint !== void 0 ? { otlpEndpoint: _otlpEndpoint } : {}
15416
16374
  }) : dispatcher.dispatch({
15417
16375
  prompt: fixPrompt,
@@ -15420,7 +16378,7 @@ function createImplementationOrchestrator(deps) {
15420
16378
  ...fixModel !== void 0 ? { model: fixModel } : {},
15421
16379
  ...fixMaxTurns !== void 0 ? { maxTurns: fixMaxTurns } : {},
15422
16380
  ...config.perStoryContextCeilings?.[storyKey] !== void 0 ? { maxContextTokens: config.perStoryContextCeilings[storyKey] } : {},
15423
- ...projectRoot !== void 0 ? { workingDirectory: projectRoot } : {},
16381
+ ...effectiveProjectRoot !== void 0 ? { workingDirectory: effectiveProjectRoot } : {},
15424
16382
  ..._otlpEndpoint !== void 0 ? { otlpEndpoint: _otlpEndpoint } : {}
15425
16383
  });
15426
16384
  const fixResult = await handle.result;
@@ -15437,7 +16395,7 @@ function createImplementationOrchestrator(deps) {
15437
16395
  if (taskType === "minor-fixes") {
15438
16396
  const finalReviewCycles = reviewCycles + 1;
15439
16397
  const downgradedVerdict = "LGTM_WITH_NOTES";
15440
- logger$26.warn({
16398
+ logger$27.warn({
15441
16399
  storyKey,
15442
16400
  reviewCycles: finalReviewCycles,
15443
16401
  issueCount: issueList.length
@@ -15456,7 +16414,7 @@ function createImplementationOrchestrator(deps) {
15456
16414
  keepReviewing = false;
15457
16415
  return;
15458
16416
  }
15459
- logger$26.warn({
16417
+ logger$27.warn({
15460
16418
  storyKey,
15461
16419
  taskType
15462
16420
  }, "Fix dispatch timed out — escalating story");
@@ -15478,7 +16436,7 @@ function createImplementationOrchestrator(deps) {
15478
16436
  }
15479
16437
  if (fixResult.status === "failed") {
15480
16438
  if (isMajorRework) {
15481
- logger$26.warn({
16439
+ logger$27.warn({
15482
16440
  storyKey,
15483
16441
  exitCode: fixResult.exitCode
15484
16442
  }, "Major rework dispatch failed — escalating story");
@@ -15498,7 +16456,7 @@ function createImplementationOrchestrator(deps) {
15498
16456
  await persistState();
15499
16457
  return;
15500
16458
  }
15501
- logger$26.warn({
16459
+ logger$27.warn({
15502
16460
  storyKey,
15503
16461
  taskType,
15504
16462
  exitCode: fixResult.exitCode
@@ -15506,7 +16464,7 @@ function createImplementationOrchestrator(deps) {
15506
16464
  }
15507
16465
  if (isMajorRework) replaceDevStorySignals(fixResult.parsed);
15508
16466
  } catch (err) {
15509
- logger$26.warn({
16467
+ logger$27.warn({
15510
16468
  storyKey,
15511
16469
  taskType,
15512
16470
  err
@@ -15564,7 +16522,7 @@ function createImplementationOrchestrator(deps) {
15564
16522
  eventBus.emit("decision:halt-skipped-non-interactive", payload);
15565
16523
  }
15566
16524
  }).catch((err) => {
15567
- logger$26.warn({ err }, "interactive prompt failed during cost-ceiling halt — continuing with default action");
16525
+ logger$27.warn({ err }, "interactive prompt failed during cost-ceiling halt — continuing with default action");
15568
16526
  });
15569
16527
  } else eventBus.emit("decision:autonomous", {
15570
16528
  runId,
@@ -15592,7 +16550,7 @@ function createImplementationOrchestrator(deps) {
15592
16550
  severity: routeResult.severity
15593
16551
  });
15594
16552
  _budgetExhausted = true;
15595
- logger$26.warn({
16553
+ logger$27.warn({
15596
16554
  skipped: allSkipped.length,
15597
16555
  cumulative: result.cumulative,
15598
16556
  ceiling: result.ceiling,
@@ -15611,7 +16569,7 @@ function createImplementationOrchestrator(deps) {
15611
16569
  const completedStoryKeys = [];
15612
16570
  for (const storyKey of group) {
15613
16571
  if (_shutdownRequested) {
15614
- logger$26.info({ storyKey }, "shutdown requested — skipping dispatch");
16572
+ logger$27.info({ storyKey }, "shutdown requested — skipping dispatch");
15615
16573
  return;
15616
16574
  }
15617
16575
  if (runManifest !== null && runManifest !== void 0) try {
@@ -15634,7 +16592,7 @@ function createImplementationOrchestrator(deps) {
15634
16592
  }
15635
16593
  }
15636
16594
  } catch (err) {
15637
- logger$26.debug({ err }, "Cost ceiling check failed — proceeding without enforcement");
16595
+ logger$27.debug({ err }, "Cost ceiling check failed — proceeding without enforcement");
15638
16596
  }
15639
16597
  let optimizationDirectives;
15640
16598
  if (telemetryAdvisor !== void 0 && completedStoryKeys.length > 0) try {
@@ -15642,13 +16600,13 @@ function createImplementationOrchestrator(deps) {
15642
16600
  const directives = telemetryAdvisor.formatOptimizationDirectives(recs);
15643
16601
  if (directives.length > 0) {
15644
16602
  optimizationDirectives = directives;
15645
- logger$26.debug({
16603
+ logger$27.debug({
15646
16604
  storyKey,
15647
16605
  directiveCount: recs.filter((r) => r.severity !== "info").length
15648
16606
  }, "Optimization directives ready for dispatch");
15649
16607
  }
15650
16608
  } catch (err) {
15651
- logger$26.debug({
16609
+ logger$27.debug({
15652
16610
  err,
15653
16611
  storyKey
15654
16612
  }, "Failed to fetch optimization directives — proceeding without");
@@ -15772,7 +16730,7 @@ function createImplementationOrchestrator(deps) {
15772
16730
  recommendedAction: "serialize"
15773
16731
  });
15774
16732
  } catch {}
15775
- logger$26.info({
16733
+ logger$27.info({
15776
16734
  storyKeys: evt.storyKeys,
15777
16735
  collisionPaths: evt.collisionPaths
15778
16736
  }, "Cross-story file collision detected — serializing affected groups to prevent race conditions");
@@ -15785,7 +16743,7 @@ function createImplementationOrchestrator(deps) {
15785
16743
  mergedGroupMap.set(root2, [...existing, ...group]);
15786
16744
  }
15787
16745
  const result = [...mergedGroupMap.values()];
15788
- logger$26.info({
16746
+ logger$27.info({
15789
16747
  originalGroupCount: batchGroups.length,
15790
16748
  mergedGroupCount: result.length,
15791
16749
  collisionCount
@@ -15829,7 +16787,7 @@ function createImplementationOrchestrator(deps) {
15829
16787
  async function shutdownGracefully(reason, signal) {
15830
16788
  if (_shutdownRequested) return;
15831
16789
  _shutdownRequested = true;
15832
- logger$26.info({
16790
+ logger$27.info({
15833
16791
  reason,
15834
16792
  signal
15835
16793
  }, "Graceful shutdown initiated — stopping new dispatches");
@@ -15839,8 +16797,8 @@ function createImplementationOrchestrator(deps) {
15839
16797
  run_status: "stopped",
15840
16798
  stopped_reason: reason,
15841
16799
  stopped_at: new Date().toISOString()
15842
- }).catch((err) => logger$26.warn({ err }, "patchRunStatus failed during shutdown (best-effort)"));
15843
- 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)"));
16800
+ }).catch((err) => logger$27.warn({ err }, "patchRunStatus failed during shutdown (best-effort)"));
16801
+ if (config.pipelineRunId !== void 0) await updatePipelineRun(db, config.pipelineRunId, { status: "stopped" }).catch((err) => logger$27.warn({ err }, "updatePipelineRun(stopped) failed during shutdown (best-effort)"));
15844
16802
  const activePhases = [
15845
16803
  "PENDING",
15846
16804
  "IN_STORY_CREATION",
@@ -15851,7 +16809,7 @@ function createImplementationOrchestrator(deps) {
15851
16809
  "CHECKPOINT"
15852
16810
  ];
15853
16811
  const cancellations = [];
15854
- for (const [storyKey, state] of _stories.entries()) if (activePhases.includes(state.phase)) cancellations.push(wgRepo.updateStoryStatus(storyKey, "cancelled").catch((err) => logger$26.warn({
16812
+ for (const [storyKey, state] of _stories.entries()) if (activePhases.includes(state.phase)) cancellations.push(wgRepo.updateStoryStatus(storyKey, "cancelled").catch((err) => logger$27.warn({
15855
16813
  err,
15856
16814
  storyKey
15857
16815
  }, "wg_stories → cancelled failed during shutdown (best-effort)")));
@@ -15860,15 +16818,29 @@ function createImplementationOrchestrator(deps) {
15860
16818
  }
15861
16819
  async function run(storyKeys) {
15862
16820
  if (_state === "RUNNING" || _state === "PAUSED") {
15863
- logger$26.warn({ state: _state }, "run() called while orchestrator is already running or paused — ignoring");
16821
+ logger$27.warn({ state: _state }, "run() called while orchestrator is already running or paused — ignoring");
15864
16822
  return getStatus();
15865
16823
  }
15866
16824
  if (_state === "COMPLETE") {
15867
- logger$26.warn({ state: _state }, "run() called on a COMPLETE orchestrator — ignoring");
16825
+ logger$27.warn({ state: _state }, "run() called on a COMPLETE orchestrator — ignoring");
15868
16826
  return getStatus();
15869
16827
  }
15870
16828
  _state = "RUNNING";
15871
16829
  _startedAt = new Date().toISOString();
16830
+ if (projectRoot !== void 0) try {
16831
+ _orchestratorStartBranch = execSync("git rev-parse --abbrev-ref HEAD", {
16832
+ cwd: projectRoot,
16833
+ encoding: "utf-8",
16834
+ stdio: [
16835
+ "ignore",
16836
+ "pipe",
16837
+ "pipe"
16838
+ ]
16839
+ }).trim();
16840
+ logger$27.info({ orchestratorStartBranch: _orchestratorStartBranch }, "Captured orchestrator start branch for merge-to-main");
16841
+ } catch (branchErr) {
16842
+ logger$27.warn({ err: branchErr }, "Failed to capture orchestrator start branch — merge-to-main will skip worktree integration");
16843
+ }
15872
16844
  for (const key of storyKeys) {
15873
16845
  const pendingState = {
15874
16846
  phase: "PENDING",
@@ -15910,12 +16882,13 @@ function createImplementationOrchestrator(deps) {
15910
16882
  await persistState();
15911
16883
  recordProgress();
15912
16884
  if (config.enableHeartbeat) startHeartbeat();
16885
+ if (_orchestratorStartBranch !== void 0 && runManifest !== null) runManifest.patchRunStatus({ orchestrator_start_branch: _orchestratorStartBranch }).catch((err) => logger$27.warn({ err }, "Failed to persist orchestrator_start_branch to manifest (best-effort)"));
15913
16886
  const _startupTimings = {};
15914
16887
  if (projectRoot !== void 0) {
15915
16888
  const seedStart = Date.now();
15916
16889
  const seedResult = await seedMethodologyContext(db, projectRoot);
15917
16890
  _startupTimings.seedMethodologyMs = Date.now() - seedStart;
15918
- if (seedResult.decisionsCreated > 0) logger$26.info({
16891
+ if (seedResult.decisionsCreated > 0) logger$27.info({
15919
16892
  decisionsCreated: seedResult.decisionsCreated,
15920
16893
  skippedCategories: seedResult.skippedCategories,
15921
16894
  durationMs: _startupTimings.seedMethodologyMs
@@ -15925,12 +16898,12 @@ function createImplementationOrchestrator(deps) {
15925
16898
  const ingestStart = Date.now();
15926
16899
  try {
15927
16900
  const ingestResult = await autoIngestEpicsDependencies(db, projectRoot);
15928
- if (ingestResult.storiesIngested > 0 || ingestResult.dependenciesIngested > 0) logger$26.info({
16901
+ if (ingestResult.storiesIngested > 0 || ingestResult.dependenciesIngested > 0) logger$27.info({
15929
16902
  ...ingestResult,
15930
16903
  durationMs: Date.now() - ingestStart
15931
16904
  }, "Auto-ingested stories and dependencies from epics document");
15932
16905
  } catch (err) {
15933
- logger$26.debug({ err }, "Auto-ingest from epics document skipped — work graph may be unavailable");
16906
+ logger$27.debug({ err }, "Auto-ingest from epics document skipped — work graph may be unavailable");
15934
16907
  }
15935
16908
  }
15936
16909
  const sigtermHandler = () => {
@@ -15948,7 +16921,7 @@ function createImplementationOrchestrator(deps) {
15948
16921
  _startupTimings.stateStoreInitMs = Date.now() - stateStoreInitStart;
15949
16922
  for (const key of storyKeys) {
15950
16923
  const pendingState = _stories.get(key);
15951
- if (pendingState !== void 0) persistStoryState(key, pendingState).catch((err) => logger$26.warn({
16924
+ if (pendingState !== void 0) persistStoryState(key, pendingState).catch((err) => logger$27.warn({
15952
16925
  err,
15953
16926
  storyKey: key
15954
16927
  }, "StateStore write failed during PENDING init"));
@@ -15959,12 +16932,12 @@ function createImplementationOrchestrator(deps) {
15959
16932
  _startupTimings.queryStoriesMs = Date.now() - queryStoriesStart;
15960
16933
  for (const record of existingRecords) _stateStoreCache.set(record.storyKey, record);
15961
16934
  } catch (err) {
15962
- logger$26.warn({ err }, "StateStore.queryStories() failed during init — status merge will be empty (best-effort)");
16935
+ logger$27.warn({ err }, "StateStore.queryStories() failed during init — status merge will be empty (best-effort)");
15963
16936
  }
15964
16937
  }
15965
16938
  if (ingestionServer !== void 0) {
15966
16939
  if (telemetryPersistence !== void 0) try {
15967
- const pipelineLogger = logger$26;
16940
+ const pipelineLogger = logger$27;
15968
16941
  const telemetryPipeline = new TelemetryPipeline({
15969
16942
  normalizer: new TelemetryNormalizer(pipelineLogger),
15970
16943
  turnAnalyzer: new TurnAnalyzer(pipelineLogger),
@@ -15976,14 +16949,14 @@ function createImplementationOrchestrator(deps) {
15976
16949
  persistence: telemetryPersistence
15977
16950
  });
15978
16951
  ingestionServer.setPipeline(telemetryPipeline);
15979
- logger$26.info("TelemetryPipeline wired to IngestionServer");
16952
+ logger$27.info("TelemetryPipeline wired to IngestionServer");
15980
16953
  } catch (pipelineErr) {
15981
- logger$26.warn({ err: pipelineErr }, "Failed to create TelemetryPipeline — continuing without analysis pipeline");
16954
+ logger$27.warn({ err: pipelineErr }, "Failed to create TelemetryPipeline — continuing without analysis pipeline");
15982
16955
  }
15983
- await ingestionServer.start().catch((err) => logger$26.warn({ err }, "IngestionServer.start() failed — continuing without telemetry (best-effort)"));
16956
+ await ingestionServer.start().catch((err) => logger$27.warn({ err }, "IngestionServer.start() failed — continuing without telemetry (best-effort)"));
15984
16957
  try {
15985
16958
  _otlpEndpoint = ingestionServer.getOtlpEnvVars().OTEL_EXPORTER_OTLP_ENDPOINT;
15986
- logger$26.info({ otlpEndpoint: _otlpEndpoint }, "OTLP telemetry ingestion active");
16959
+ logger$27.info({ otlpEndpoint: _otlpEndpoint }, "OTLP telemetry ingestion active");
15987
16960
  } catch {}
15988
16961
  }
15989
16962
  let contractDeclarations = [];
@@ -16023,12 +16996,12 @@ function createImplementationOrchestrator(deps) {
16023
16996
  const conflictDetectStart = Date.now();
16024
16997
  const { batches, edges: contractEdges } = detectConflictGroupsWithContracts(storyKeys, { moduleMap: pack.manifest.conflictGroups }, contractDeclarations);
16025
16998
  _startupTimings.conflictDetectMs = Date.now() - conflictDetectStart;
16026
- if (contractEdges.length > 0) logger$26.info({
16999
+ if (contractEdges.length > 0) logger$27.info({
16027
17000
  contractEdges,
16028
17001
  edgeCount: contractEdges.length
16029
17002
  }, "Contract dependency edges detected — applying contract-aware dispatch ordering");
16030
- wgRepo.addContractDependencies(contractEdges).catch((err) => logger$26.warn({ err }, "contract dep persistence failed (best-effort)"));
16031
- logger$26.info({
17003
+ wgRepo.addContractDependencies(contractEdges).catch((err) => logger$27.warn({ err }, "contract dep persistence failed (best-effort)"));
17004
+ logger$27.info({
16032
17005
  storyCount: storyKeys.length,
16033
17006
  groupCount: batches.reduce((sum, b) => sum + b.length, 0),
16034
17007
  batchCount: batches.length,
@@ -16038,7 +17011,7 @@ function createImplementationOrchestrator(deps) {
16038
17011
  groups: batch.map((g) => g.join(","))
16039
17012
  }))
16040
17013
  }, "Orchestrator starting");
16041
- logger$26.info({
17014
+ logger$27.info({
16042
17015
  storyCount: storyKeys.length,
16043
17016
  conflictGroups: batches.length,
16044
17017
  maxConcurrency: config.maxConcurrency
@@ -16059,7 +17032,7 @@ function createImplementationOrchestrator(deps) {
16059
17032
  exitCode,
16060
17033
  output: truncatedOutput
16061
17034
  });
16062
- logger$26.error({
17035
+ logger$27.error({
16063
17036
  exitCode,
16064
17037
  reason: preFlightResult.reason
16065
17038
  }, "Pre-flight build check failed — aborting pipeline before any story dispatch");
@@ -16068,19 +17041,19 @@ function createImplementationOrchestrator(deps) {
16068
17041
  await persistState();
16069
17042
  return getStatus();
16070
17043
  }
16071
- if (preFlightResult.status !== "skipped") logger$26.info("Pre-flight build check passed");
17044
+ if (preFlightResult.status !== "skipped") logger$27.info("Pre-flight build check passed");
16072
17045
  }
16073
- logger$26.info(_startupTimings, "Orchestrator startup timings (ms)");
17046
+ logger$27.info(_startupTimings, "Orchestrator startup timings (ms)");
16074
17047
  const totalGroups = batches.reduce((sum, b) => sum + b.length, 0);
16075
17048
  const actualConcurrency = Math.min(config.maxConcurrency, totalGroups);
16076
17049
  if (actualConcurrency > 1 && projectRoot !== void 0) try {
16077
17050
  _packageSnapshot = capturePackageSnapshot({ projectRoot });
16078
- logger$26.info({
17051
+ logger$27.info({
16079
17052
  fileCount: _packageSnapshot.files.size,
16080
17053
  installCommand: _packageSnapshot.installCommand
16081
17054
  }, "Package snapshot captured for concurrent story protection");
16082
17055
  } catch (snapErr) {
16083
- logger$26.warn({ err: snapErr }, "Failed to capture package snapshot — continuing without protection");
17056
+ logger$27.warn({ err: snapErr }, "Failed to capture package snapshot — continuing without protection");
16084
17057
  }
16085
17058
  try {
16086
17059
  for (const rawBatchGroups of batches) {
@@ -16105,14 +17078,14 @@ function createImplementationOrchestrator(deps) {
16105
17078
  manifest: runManifest,
16106
17079
  adapter: db
16107
17080
  });
16108
- if (!recoveryResult.noStale) logger$26.info({
17081
+ if (!recoveryResult.noStale) logger$27.info({
16109
17082
  recovered: recoveryResult.recovered,
16110
17083
  stillFailed: recoveryResult.stillFailed,
16111
17084
  recoveredCount: recoveryResult.recovered.length,
16112
17085
  stillFailedCount: recoveryResult.stillFailed.length
16113
17086
  }, "Cross-story race recovery complete");
16114
17087
  } catch (recoveryErr) {
16115
- logger$26.warn({ err: recoveryErr }, "Cross-story race recovery failed (non-fatal) — pipeline continues");
17088
+ logger$27.warn({ err: recoveryErr }, "Cross-story race recovery failed (non-fatal) — pipeline continues");
16116
17089
  }
16117
17090
  }
16118
17091
  }
@@ -16121,7 +17094,7 @@ function createImplementationOrchestrator(deps) {
16121
17094
  _state = "FAILED";
16122
17095
  _completedAt = new Date().toISOString();
16123
17096
  await persistState();
16124
- logger$26.error({ err }, "Orchestrator failed with unhandled error");
17097
+ logger$27.error({ err }, "Orchestrator failed with unhandled error");
16125
17098
  return getStatus();
16126
17099
  }
16127
17100
  stopHeartbeat();
@@ -16131,7 +17104,7 @@ function createImplementationOrchestrator(deps) {
16131
17104
  const totalDeclarations = contractDeclarations.length;
16132
17105
  const currentSprintDeclarations = contractDeclarations.filter((d) => storyKeys.includes(d.storyKey));
16133
17106
  const stalePruned = totalDeclarations - currentSprintDeclarations.length;
16134
- if (stalePruned > 0) logger$26.info({
17107
+ if (stalePruned > 0) logger$27.info({
16135
17108
  stalePruned,
16136
17109
  remaining: currentSprintDeclarations.length
16137
17110
  }, "Pruned stale contract declarations from previous epics");
@@ -16145,11 +17118,11 @@ function createImplementationOrchestrator(deps) {
16145
17118
  contractName: mismatch.contractName,
16146
17119
  mismatchDescription: mismatch.mismatchDescription
16147
17120
  });
16148
- logger$26.warn({
17121
+ logger$27.warn({
16149
17122
  mismatchCount: mismatches.length,
16150
17123
  mismatches
16151
17124
  }, "Post-sprint contract verification found mismatches — manual review required");
16152
- } else if (currentSprintDeclarations.length > 0) logger$26.info("Post-sprint contract verification passed — all declared contracts satisfied");
17125
+ } else if (currentSprintDeclarations.length > 0) logger$27.info("Post-sprint contract verification passed — all declared contracts satisfied");
16153
17126
  eventBus.emit("pipeline:contract-verification-summary", {
16154
17127
  verified: currentSprintDeclarations.length,
16155
17128
  stalePruned,
@@ -16184,12 +17157,12 @@ function createImplementationOrchestrator(deps) {
16184
17157
  });
16185
17158
  await stateStore.setContractVerification(sk, records);
16186
17159
  }
16187
- logger$26.info({ storyCount: contractsByStory.size }, "Contract verification results persisted to StateStore");
17160
+ logger$27.info({ storyCount: contractsByStory.size }, "Contract verification results persisted to StateStore");
16188
17161
  } catch (persistErr) {
16189
- logger$26.warn({ err: persistErr }, "Failed to persist contract verification results to StateStore");
17162
+ logger$27.warn({ err: persistErr }, "Failed to persist contract verification results to StateStore");
16190
17163
  }
16191
17164
  } catch (err) {
16192
- logger$26.error({ err }, "Post-sprint contract verification threw an error — skipping");
17165
+ logger$27.error({ err }, "Post-sprint contract verification threw an error — skipping");
16193
17166
  }
16194
17167
  if (projectRoot !== void 0) try {
16195
17168
  const indicators = checkProfileStaleness(projectRoot);
@@ -16199,10 +17172,10 @@ function createImplementationOrchestrator(deps) {
16199
17172
  message,
16200
17173
  indicators
16201
17174
  });
16202
- logger$26.warn({ indicators }, message);
17175
+ logger$27.warn({ indicators }, message);
16203
17176
  }
16204
17177
  } catch (err) {
16205
- logger$26.debug({ err }, "Profile staleness check failed (best-effort)");
17178
+ logger$27.debug({ err }, "Profile staleness check failed (best-effort)");
16206
17179
  }
16207
17180
  let completed = 0;
16208
17181
  let escalated = 0;
@@ -16221,8 +17194,8 @@ function createImplementationOrchestrator(deps) {
16221
17194
  } finally {
16222
17195
  process.off("SIGTERM", sigtermHandler);
16223
17196
  process.off("SIGINT", sigintHandler);
16224
- if (stateStore !== void 0) await stateStore.close().catch((err) => logger$26.warn({ err }, "StateStore.close() failed (best-effort)"));
16225
- if (ingestionServer !== void 0) await ingestionServer.stop().catch((err) => logger$26.warn({ err }, "IngestionServer.stop() failed (best-effort)"));
17197
+ if (stateStore !== void 0) await stateStore.close().catch((err) => logger$27.warn({ err }, "StateStore.close() failed (best-effort)"));
17198
+ if (ingestionServer !== void 0) await ingestionServer.stop().catch((err) => logger$27.warn({ err }, "IngestionServer.stop() failed (best-effort)"));
16226
17199
  }
16227
17200
  }
16228
17201
  function pause() {
@@ -16231,7 +17204,7 @@ function createImplementationOrchestrator(deps) {
16231
17204
  _pauseGate = createPauseGate();
16232
17205
  _state = "PAUSED";
16233
17206
  eventBus.emit("orchestrator:paused", {});
16234
- logger$26.info("Orchestrator paused");
17207
+ logger$27.info("Orchestrator paused");
16235
17208
  }
16236
17209
  function resume() {
16237
17210
  if (_state !== "PAUSED") return;
@@ -16242,7 +17215,7 @@ function createImplementationOrchestrator(deps) {
16242
17215
  }
16243
17216
  _state = "RUNNING";
16244
17217
  eventBus.emit("orchestrator:resumed", {});
16245
- logger$26.info("Orchestrator resumed");
17218
+ logger$27.info("Orchestrator resumed");
16246
17219
  }
16247
17220
  return {
16248
17221
  run,
@@ -17658,8 +18631,8 @@ async function resolveContext(ref, deps, runId, params, stepOutputs) {
17658
18631
  return params[key] ?? "";
17659
18632
  }
17660
18633
  if (source.startsWith("decision:")) {
17661
- const path$4 = source.slice(9);
17662
- const [phase, category] = path$4.split(".");
18634
+ const path$6 = source.slice(9);
18635
+ const [phase, category] = path$6.split(".");
17663
18636
  if (!phase || !category) return "";
17664
18637
  const decisions = await getDecisionsByPhaseForRun(deps.db, runId, phase);
17665
18638
  const filtered = decisions.filter((d) => d.category === category);
@@ -17730,8 +18703,8 @@ async function runSteps(steps, deps, runId, phase, params) {
17730
18703
  for (const ref of step.context) {
17731
18704
  let value;
17732
18705
  if (ref.source.startsWith("decision:")) {
17733
- const path$4 = ref.source.slice(9);
17734
- const [decPhase, decCategory] = path$4.split(".");
18706
+ const path$6 = ref.source.slice(9);
18707
+ const [decPhase, decCategory] = path$6.split(".");
17735
18708
  if (decPhase && decCategory) {
17736
18709
  const decisions = await getDecisionsByPhaseForRun(deps.db, runId, decPhase);
17737
18710
  const filtered = decisions.filter((d) => d.category === decCategory);
@@ -28778,8 +29751,8 @@ var require_uri_all = __commonJS({ "node_modules/uri-js/dist/es5/uri.all.js"(exp
28778
29751
  wsComponents.secure = void 0;
28779
29752
  }
28780
29753
  if (wsComponents.resourceName) {
28781
- var _wsComponents$resourc = wsComponents.resourceName.split("?"), _wsComponents$resourc2 = slicedToArray(_wsComponents$resourc, 2), path$4 = _wsComponents$resourc2[0], query = _wsComponents$resourc2[1];
28782
- wsComponents.path = path$4 && path$4 !== "/" ? path$4 : void 0;
29754
+ var _wsComponents$resourc = wsComponents.resourceName.split("?"), _wsComponents$resourc2 = slicedToArray(_wsComponents$resourc, 2), path$6 = _wsComponents$resourc2[0], query = _wsComponents$resourc2[1];
29755
+ wsComponents.path = path$6 && path$6 !== "/" ? path$6 : void 0;
28783
29756
  wsComponents.query = query;
28784
29757
  wsComponents.resourceName = void 0;
28785
29758
  }
@@ -29120,12 +30093,12 @@ var require_util = __commonJS({ "node_modules/ajv/lib/compile/util.js"(exports,
29120
30093
  return "'" + escapeQuotes(str) + "'";
29121
30094
  }
29122
30095
  function getPathExpr(currentPath, expr, jsonPointers, isNumber) {
29123
- var path$4 = jsonPointers ? "'/' + " + expr + (isNumber ? "" : ".replace(/~/g, '~0').replace(/\\//g, '~1')") : isNumber ? "'[' + " + expr + " + ']'" : "'[\\'' + " + expr + " + '\\']'";
29124
- return joinPaths(currentPath, path$4);
30096
+ var path$6 = jsonPointers ? "'/' + " + expr + (isNumber ? "" : ".replace(/~/g, '~0').replace(/\\//g, '~1')") : isNumber ? "'[' + " + expr + " + ']'" : "'[\\'' + " + expr + " + '\\']'";
30097
+ return joinPaths(currentPath, path$6);
29125
30098
  }
29126
30099
  function getPath(currentPath, prop, jsonPointers) {
29127
- var path$4 = jsonPointers ? toQuotedString("/" + escapeJsonPointer(prop)) : toQuotedString(getProperty(prop));
29128
- return joinPaths(currentPath, path$4);
30100
+ var path$6 = jsonPointers ? toQuotedString("/" + escapeJsonPointer(prop)) : toQuotedString(getProperty(prop));
30101
+ return joinPaths(currentPath, path$6);
29129
30102
  }
29130
30103
  var JSON_POINTER$1 = /^\/(?:[^~]|~0|~1)*$/;
29131
30104
  var RELATIVE_JSON_POINTER$1 = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
@@ -33293,16 +34266,16 @@ var require_ajv = __commonJS({ "node_modules/ajv/lib/ajv.js"(exports, module) {
33293
34266
  return metaOpts;
33294
34267
  }
33295
34268
  function setLogger(self) {
33296
- var logger$26 = self._opts.logger;
33297
- if (logger$26 === false) self.logger = {
34269
+ var logger$27 = self._opts.logger;
34270
+ if (logger$27 === false) self.logger = {
33298
34271
  log: noop,
33299
34272
  warn: noop,
33300
34273
  error: noop
33301
34274
  };
33302
34275
  else {
33303
- if (logger$26 === void 0) logger$26 = console;
33304
- 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");
33305
- self.logger = logger$26;
34276
+ if (logger$27 === void 0) logger$27 = console;
34277
+ if (!(typeof logger$27 == "object" && logger$27.log && logger$27.warn && logger$27.error)) throw new Error("logger must implement log, warn and error methods");
34278
+ self.logger = logger$27;
33306
34279
  }
33307
34280
  }
33308
34281
  function noop() {}
@@ -33334,8 +34307,8 @@ var ToolRegistry = class {
33334
34307
  if (!valid) {
33335
34308
  const errors = validate$1.errors ?? [];
33336
34309
  const messages = errors.map((e) => {
33337
- const path$4 = e.instancePath ?? e.dataPath ?? "";
33338
- return `${path$4} ${e.message ?? ""}`.trim();
34310
+ const path$6 = e.instancePath ?? e.dataPath ?? "";
34311
+ return `${path$6} ${e.message ?? ""}`.trim();
33339
34312
  }).join(", ");
33340
34313
  return {
33341
34314
  content: `Validation failed for tool '${name}': ${messages}`,
@@ -40041,10 +41014,10 @@ var PathBase = class {
40041
41014
  /**
40042
41015
  * Get the Path object referenced by the string path, resolved from this Path
40043
41016
  */
40044
- resolve(path$4) {
40045
- if (!path$4) return this;
40046
- const rootPath = this.getRootString(path$4);
40047
- const dir = path$4.substring(rootPath.length);
41017
+ resolve(path$6) {
41018
+ if (!path$6) return this;
41019
+ const rootPath = this.getRootString(path$6);
41020
+ const dir = path$6.substring(rootPath.length);
40048
41021
  const dirParts = dir.split(this.splitSep);
40049
41022
  const result = rootPath ? this.getRoot(rootPath).#resolveParts(dirParts) : this.#resolveParts(dirParts);
40050
41023
  return result;
@@ -40699,8 +41672,8 @@ var PathWin32 = class PathWin32 extends PathBase {
40699
41672
  /**
40700
41673
  * @internal
40701
41674
  */
40702
- getRootString(path$4) {
40703
- return win32.parse(path$4).root;
41675
+ getRootString(path$6) {
41676
+ return win32.parse(path$6).root;
40704
41677
  }
40705
41678
  /**
40706
41679
  * @internal
@@ -40745,8 +41718,8 @@ var PathPosix = class PathPosix extends PathBase {
40745
41718
  /**
40746
41719
  * @internal
40747
41720
  */
40748
- getRootString(path$4) {
40749
- return path$4.startsWith("/") ? "/" : "";
41721
+ getRootString(path$6) {
41722
+ return path$6.startsWith("/") ? "/" : "";
40750
41723
  }
40751
41724
  /**
40752
41725
  * @internal
@@ -40839,9 +41812,9 @@ var PathScurryBase = class {
40839
41812
  /**
40840
41813
  * Get the depth of a provided path, string, or the cwd
40841
41814
  */
40842
- depth(path$4 = this.cwd) {
40843
- if (typeof path$4 === "string") path$4 = this.cwd.resolve(path$4);
40844
- return path$4.depth();
41815
+ depth(path$6 = this.cwd) {
41816
+ if (typeof path$6 === "string") path$6 = this.cwd.resolve(path$6);
41817
+ return path$6.depth();
40845
41818
  }
40846
41819
  /**
40847
41820
  * Return the cache of child entries. Exposed so subclasses can create
@@ -41222,9 +42195,9 @@ var PathScurryBase = class {
41222
42195
  process$1();
41223
42196
  return results;
41224
42197
  }
41225
- chdir(path$4 = this.cwd) {
42198
+ chdir(path$6 = this.cwd) {
41226
42199
  const oldCwd = this.cwd;
41227
- this.cwd = typeof path$4 === "string" ? this.cwd.resolve(path$4) : path$4;
42200
+ this.cwd = typeof path$6 === "string" ? this.cwd.resolve(path$6) : path$6;
41228
42201
  this.cwd[setAsCwd](oldCwd);
41229
42202
  }
41230
42203
  };
@@ -41608,8 +42581,8 @@ var MatchRecord = class {
41608
42581
  this.store.set(target, current === void 0 ? n$1 : n$1 & current);
41609
42582
  }
41610
42583
  entries() {
41611
- return [...this.store.entries()].map(([path$4, n$1]) => [
41612
- path$4,
42584
+ return [...this.store.entries()].map(([path$6, n$1]) => [
42585
+ path$6,
41613
42586
  !!(n$1 & 2),
41614
42587
  !!(n$1 & 1)
41615
42588
  ]);
@@ -41785,9 +42758,9 @@ var GlobUtil = class {
41785
42758
  signal;
41786
42759
  maxDepth;
41787
42760
  includeChildMatches;
41788
- constructor(patterns, path$4, opts) {
42761
+ constructor(patterns, path$6, opts) {
41789
42762
  this.patterns = patterns;
41790
- this.path = path$4;
42763
+ this.path = path$6;
41791
42764
  this.opts = opts;
41792
42765
  this.#sep = !opts.posix && opts.platform === "win32" ? "\\" : "/";
41793
42766
  this.includeChildMatches = opts.includeChildMatches !== false;
@@ -41808,11 +42781,11 @@ var GlobUtil = class {
41808
42781
  });
41809
42782
  }
41810
42783
  }
41811
- #ignored(path$4) {
41812
- return this.seen.has(path$4) || !!this.#ignore?.ignored?.(path$4);
42784
+ #ignored(path$6) {
42785
+ return this.seen.has(path$6) || !!this.#ignore?.ignored?.(path$6);
41813
42786
  }
41814
- #childrenIgnored(path$4) {
41815
- return !!this.#ignore?.childrenIgnored?.(path$4);
42787
+ #childrenIgnored(path$6) {
42788
+ return !!this.#ignore?.childrenIgnored?.(path$6);
41816
42789
  }
41817
42790
  pause() {
41818
42791
  this.paused = true;
@@ -41994,8 +42967,8 @@ var GlobUtil = class {
41994
42967
  };
41995
42968
  var GlobWalker = class extends GlobUtil {
41996
42969
  matches = new Set();
41997
- constructor(patterns, path$4, opts) {
41998
- super(patterns, path$4, opts);
42970
+ constructor(patterns, path$6, opts) {
42971
+ super(patterns, path$6, opts);
41999
42972
  }
42000
42973
  matchEmit(e) {
42001
42974
  this.matches.add(e);
@@ -42022,8 +42995,8 @@ var GlobWalker = class extends GlobUtil {
42022
42995
  };
42023
42996
  var GlobStream = class extends GlobUtil {
42024
42997
  results;
42025
- constructor(patterns, path$4, opts) {
42026
- super(patterns, path$4, opts);
42998
+ constructor(patterns, path$6, opts) {
42999
+ super(patterns, path$6, opts);
42027
43000
  this.results = new Minipass({
42028
43001
  signal: this.signal,
42029
43002
  objectMode: true
@@ -44583,7 +45556,7 @@ function resolveProbeAuthorStateIntegrating(cliFlag) {
44583
45556
  * substrate run --non-interactive --halt-on none --events --output-format json
44584
45557
  */
44585
45558
  async function runRunAction(options) {
44586
- const { pack: packName, from: startPhase, stopAfter, concept: conceptArg, conceptFile, stories: storiesArg, concurrency, outputFormat, projectRoot, events: eventsFlag, verbose: verboseFlag, tui: tuiFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, skipVerification, epic: epicNumber, dryRun, maxReviewCycles = 2, engine, agent: agentId, registry: injectedRegistry, haltOn: haltOnOpt, costCeiling, probeAuthor, probeAuthorStateIntegrating: probeAuthorStateIntegratingFlag, nonInteractive, verifyAc } = options;
45559
+ const { pack: packName, from: startPhase, stopAfter, concept: conceptArg, conceptFile, stories: storiesArg, concurrency, outputFormat, projectRoot, events: eventsFlag, verbose: verboseFlag, tui: tuiFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, skipVerification, epic: epicNumber, dryRun, maxReviewCycles = 2, engine, agent: agentId, registry: injectedRegistry, haltOn: haltOnOpt, costCeiling, probeAuthor, probeAuthorStateIntegrating: probeAuthorStateIntegratingFlag, nonInteractive, verifyAc, noWorktree } = options;
44587
45560
  const haltOn = haltOnOpt;
44588
45561
  const VALID_PROBE_AUTHOR_MODES = [
44589
45562
  "enabled",
@@ -44803,7 +45776,8 @@ async function runRunAction(options) {
44803
45776
  engineType: resolvedEngine,
44804
45777
  maxReviewCycles: effectiveMaxReviewCycles,
44805
45778
  retryBudget: configRetryBudget ?? 2,
44806
- agentId
45779
+ agentId,
45780
+ ...noWorktree === true ? { noWorktree: true } : {}
44807
45781
  });
44808
45782
  let storyKeys = [...parsedStoryKeys];
44809
45783
  if (!existsSync$1(dbDir)) mkdirSync$1(dbDir, { recursive: true });
@@ -44902,7 +45876,8 @@ async function runRunAction(options) {
44902
45876
  ...agentId !== void 0 ? { agent: agentId } : {},
44903
45877
  ...skipVerification === true ? { skip_verification: true } : {},
44904
45878
  ...eventsFlag === true ? { events: true } : {},
44905
- ...nonInteractive === true ? { non_interactive: true } : {}
45879
+ ...nonInteractive === true ? { non_interactive: true } : {},
45880
+ ...noWorktree === true ? { no_worktree: true } : {}
44906
45881
  };
44907
45882
  const manifest = RunManifest.open(pipelineRun.id, runsDir);
44908
45883
  await manifest.patchCLIFlags(cliFlags);
@@ -45354,7 +46329,8 @@ async function runRunAction(options) {
45354
46329
  skipPreflight: skipPreflight === true,
45355
46330
  ...skipVerification === true ? { skipVerification: true } : {},
45356
46331
  ...probeAuthor !== void 0 ? { probeAuthorMode: probeAuthor } : {},
45357
- probeAuthorStateIntegrating: resolvedProbeAuthorStateIntegrating
46332
+ probeAuthorStateIntegrating: resolvedProbeAuthorStateIntegrating,
46333
+ ...noWorktree === true ? { noWorktree: true } : {}
45358
46334
  },
45359
46335
  projectRoot,
45360
46336
  tokenCeilings,
@@ -45572,7 +46548,7 @@ async function runRunAction(options) {
45572
46548
  }
45573
46549
  }
45574
46550
  async function runFullPipeline(options) {
45575
- const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, projectRoot, events: eventsFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, skipVerification, maxReviewCycles = 2, retryBudget, registry: injectedRegistry, tokenCeilings, stories: explicitStories, telemetryEnabled: fullTelemetryEnabled, telemetryPort: fullTelemetryPort, agentId, meshUrl: fpMeshUrl, meshProjectId: fpMeshProjectId, engineType: fpEngineType, probeAuthor, probeAuthorStateIntegrating: fpProbeAuthorStateIntegrating } = options;
46551
+ const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, projectRoot, events: eventsFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, skipPreflight, skipVerification, maxReviewCycles = 2, retryBudget, registry: injectedRegistry, tokenCeilings, stories: explicitStories, telemetryEnabled: fullTelemetryEnabled, telemetryPort: fullTelemetryPort, agentId, meshUrl: fpMeshUrl, meshProjectId: fpMeshProjectId, engineType: fpEngineType, probeAuthor, probeAuthorStateIntegrating: fpProbeAuthorStateIntegrating, noWorktree } = options;
45576
46552
  if (!existsSync$1(dbDir)) mkdirSync$1(dbDir, { recursive: true });
45577
46553
  let doltServerFull = null;
45578
46554
  try {
@@ -45881,7 +46857,8 @@ async function runFullPipeline(options) {
45881
46857
  skipPreflight: skipPreflight === true,
45882
46858
  ...skipVerification === true ? { skipVerification: true } : {},
45883
46859
  ...probeAuthor !== void 0 ? { probeAuthorMode: probeAuthor } : {},
45884
- ...fpProbeAuthorStateIntegrating !== void 0 ? { probeAuthorStateIntegrating: fpProbeAuthorStateIntegrating } : {}
46860
+ ...fpProbeAuthorStateIntegrating !== void 0 ? { probeAuthorStateIntegrating: fpProbeAuthorStateIntegrating } : {},
46861
+ ...noWorktree === true ? { noWorktree: true } : {}
45885
46862
  },
45886
46863
  projectRoot,
45887
46864
  tokenCeilings,
@@ -46091,7 +47068,7 @@ function registerRunCommand(program, version = "0.0.0", projectRoot = process.cw
46091
47068
  "",
46092
47069
  "--halt-on defaults to critical. Skipped halt decisions are recorded as",
46093
47070
  "decision:halt-skipped-non-interactive events in --non-interactive mode."
46094
- ].join("\n ")).option("--verify-ac", "Run AC-to-test traceability heuristic after the pipeline completes (Story 74-1)").action(async (opts) => {
47071
+ ].join("\n ")).option("--verify-ac", "Run AC-to-test traceability heuristic after the pipeline completes (Story 74-1)").option("--no-worktree", "Bypass per-story git worktrees (safety valve for submodules, bare repos, or large checkouts where parallel worktrees blow disk — not the recommended path)").action(async (opts) => {
46095
47072
  if (opts.helpAgent) {
46096
47073
  process.exitCode = await runHelpAgent();
46097
47074
  return;
@@ -46138,7 +47115,8 @@ function registerRunCommand(program, version = "0.0.0", projectRoot = process.cw
46138
47115
  probeAuthor: opts.probeAuthor,
46139
47116
  probeAuthorStateIntegrating: opts.probeAuthorStateIntegrating,
46140
47117
  nonInteractive: opts.nonInteractive,
46141
- verifyAc: opts.verifyAc
47118
+ verifyAc: opts.verifyAc,
47119
+ noWorktree: opts.worktree === false || process.env["SUBSTRATE_NO_WORKTREE"] === "1"
46142
47120
  });
46143
47121
  if (opts.nonInteractive === true) process.exit(exitCode);
46144
47122
  process.exitCode = exitCode;
@@ -46146,5 +47124,5 @@ function registerRunCommand(program, version = "0.0.0", projectRoot = process.cw
46146
47124
  }
46147
47125
 
46148
47126
  //#endregion
46149
- export { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GLOBSTAR$1 as GLOBSTAR, GitClient, GrammarLoader, Minimatch$1 as Minimatch, Minipass, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, escape$1 as escape, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, normalizeGraphSummaryToStatus, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runProbeAuthor, runRunAction, runSolutioningPhase, unescape$1 as unescape, validateStopAfterFromConflict, wireNdjsonEmitter };
46150
- //# sourceMappingURL=run-5R5ZPgSw.js.map
47127
+ export { AdapterTelemetryPersistence, AppError, DoltRepoMapMetaRepository, DoltSymbolRepository, ERR_REPO_MAP_STORAGE_WRITE, EpicIngester, GLOBSTAR$1 as GLOBSTAR, GitClient, GrammarLoader, Minimatch$1 as Minimatch, Minipass, RepoMapInjector, RepoMapModule, RepoMapQueryEngine, RepoMapStorage, SymbolParser, createContextCompiler, createDispatcher, createEventEmitter, createGitWorktreeManager, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, createTelemetryAdvisor, escape$1 as escape, formatPhaseCompletionSummary, getFactoryRunSummaries, getScenarioResultsForRun, getTwinRunsForRun, listGraphRuns, normalizeGraphSummaryToStatus, registerExportCommand, registerFactoryCommand, registerRunCommand, registerScenariosCommand, resolveMaxReviewCycles, resolveProbeAuthorStateIntegrating, resolveStoryKeys, runAnalysisPhase, runPlanningPhase, runProbeAuthor, runRunAction, runSolutioningPhase, unescape$1 as unescape, validateStopAfterFromConflict, wireNdjsonEmitter };
47128
+ //# sourceMappingURL=run-Bhe2gX2V.js.map