substrate-ai 0.2.27 → 0.2.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +6 -6
- package/dist/index.d.ts +7 -0
- package/dist/{run-jLeEo8FW.js → run-DFFZVzZK.js} +1 -1
- package/dist/{run-DG5j6vJI.js → run-IeDyGCak.js} +497 -183
- package/dist/{upgrade-CjjAx5kD.js → upgrade-CJ0JFQ2c.js} +2 -2
- package/dist/{upgrade-CF8EjNuO.js → upgrade-DzpjKYlD.js} +2 -2
- package/dist/{version-manager-impl-C6jmvble.js → version-manager-impl-BDfiGXWX.js} +1 -1
- package/dist/{version-manager-impl-CZ6KF1Ds.js → version-manager-impl-CtzNu7YZ.js} +17 -3
- package/package.json +1 -1
- package/packs/bmad/prompts/code-review.md +23 -2
- package/packs/bmad/prompts/fix-story.md +5 -0
|
@@ -539,7 +539,7 @@ const migration010RunMetrics = {
|
|
|
539
539
|
|
|
540
540
|
//#endregion
|
|
541
541
|
//#region src/persistence/migrations/index.ts
|
|
542
|
-
const logger$
|
|
542
|
+
const logger$21 = createLogger("persistence:migrations");
|
|
543
543
|
const MIGRATIONS = [
|
|
544
544
|
initialSchemaMigration,
|
|
545
545
|
costTrackerSchemaMigration,
|
|
@@ -557,7 +557,7 @@ const MIGRATIONS = [
|
|
|
557
557
|
* Safe to call multiple times — already-applied migrations are skipped.
|
|
558
558
|
*/
|
|
559
559
|
function runMigrations(db) {
|
|
560
|
-
logger$
|
|
560
|
+
logger$21.info("Starting migration runner");
|
|
561
561
|
db.exec(`
|
|
562
562
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
563
563
|
version INTEGER PRIMARY KEY,
|
|
@@ -568,12 +568,12 @@ function runMigrations(db) {
|
|
|
568
568
|
const appliedVersions = new Set(db.prepare("SELECT version FROM schema_migrations").all().map((row) => row.version));
|
|
569
569
|
const pending = MIGRATIONS.filter((m) => !appliedVersions.has(m.version)).sort((a, b) => a.version - b.version);
|
|
570
570
|
if (pending.length === 0) {
|
|
571
|
-
logger$
|
|
571
|
+
logger$21.info("No pending migrations");
|
|
572
572
|
return;
|
|
573
573
|
}
|
|
574
574
|
const insertMigration = db.prepare("INSERT INTO schema_migrations (version, name) VALUES (?, ?)");
|
|
575
575
|
for (const migration of pending) {
|
|
576
|
-
logger$
|
|
576
|
+
logger$21.info({
|
|
577
577
|
version: migration.version,
|
|
578
578
|
name: migration.name
|
|
579
579
|
}, "Applying migration");
|
|
@@ -587,14 +587,14 @@ function runMigrations(db) {
|
|
|
587
587
|
});
|
|
588
588
|
applyMigration();
|
|
589
589
|
}
|
|
590
|
-
logger$
|
|
590
|
+
logger$21.info({ version: migration.version }, "Migration applied successfully");
|
|
591
591
|
}
|
|
592
|
-
logger$
|
|
592
|
+
logger$21.info({ count: pending.length }, "All pending migrations applied");
|
|
593
593
|
}
|
|
594
594
|
|
|
595
595
|
//#endregion
|
|
596
596
|
//#region src/persistence/database.ts
|
|
597
|
-
const logger$
|
|
597
|
+
const logger$20 = createLogger("persistence:database");
|
|
598
598
|
/**
|
|
599
599
|
* Thin wrapper that opens a SQLite database, applies required PRAGMAs,
|
|
600
600
|
* and exposes the raw BetterSqlite3 instance.
|
|
@@ -611,14 +611,14 @@ var DatabaseWrapper = class {
|
|
|
611
611
|
*/
|
|
612
612
|
open() {
|
|
613
613
|
if (this._db !== null) return;
|
|
614
|
-
logger$
|
|
614
|
+
logger$20.info({ path: this._path }, "Opening SQLite database");
|
|
615
615
|
this._db = new BetterSqlite3(this._path);
|
|
616
616
|
const walResult = this._db.pragma("journal_mode = WAL");
|
|
617
|
-
if (walResult?.[0]?.journal_mode !== "wal") logger$
|
|
617
|
+
if (walResult?.[0]?.journal_mode !== "wal") logger$20.warn({ result: walResult?.[0]?.journal_mode }, "WAL pragma did not return expected \"wal\" — journal_mode may be \"memory\" or unsupported");
|
|
618
618
|
this._db.pragma("busy_timeout = 5000");
|
|
619
619
|
this._db.pragma("synchronous = NORMAL");
|
|
620
620
|
this._db.pragma("foreign_keys = ON");
|
|
621
|
-
logger$
|
|
621
|
+
logger$20.info({ path: this._path }, "SQLite database opened with WAL mode");
|
|
622
622
|
}
|
|
623
623
|
/**
|
|
624
624
|
* Close the database. Idempotent — calling close() when already closed is a no-op.
|
|
@@ -627,7 +627,7 @@ var DatabaseWrapper = class {
|
|
|
627
627
|
if (this._db === null) return;
|
|
628
628
|
this._db.close();
|
|
629
629
|
this._db = null;
|
|
630
|
-
logger$
|
|
630
|
+
logger$20.info({ path: this._path }, "SQLite database closed");
|
|
631
631
|
}
|
|
632
632
|
/**
|
|
633
633
|
* Return the raw BetterSqlite3 instance.
|
|
@@ -2675,7 +2675,7 @@ function truncateToTokens(text, maxTokens) {
|
|
|
2675
2675
|
|
|
2676
2676
|
//#endregion
|
|
2677
2677
|
//#region src/modules/context-compiler/context-compiler-impl.ts
|
|
2678
|
-
const logger$
|
|
2678
|
+
const logger$19 = createLogger("context-compiler");
|
|
2679
2679
|
/**
|
|
2680
2680
|
* Fraction of the original token budget that must remain (after required +
|
|
2681
2681
|
* important sections) before an optional section is included.
|
|
@@ -2767,7 +2767,7 @@ var ContextCompilerImpl = class {
|
|
|
2767
2767
|
includedParts.push(truncated);
|
|
2768
2768
|
remainingBudget -= truncatedTokens;
|
|
2769
2769
|
anyTruncated = true;
|
|
2770
|
-
logger$
|
|
2770
|
+
logger$19.warn({
|
|
2771
2771
|
section: section.name,
|
|
2772
2772
|
originalTokens: tokens,
|
|
2773
2773
|
budgetTokens: truncatedTokens
|
|
@@ -2781,7 +2781,7 @@ var ContextCompilerImpl = class {
|
|
|
2781
2781
|
});
|
|
2782
2782
|
} else {
|
|
2783
2783
|
anyTruncated = true;
|
|
2784
|
-
logger$
|
|
2784
|
+
logger$19.warn({
|
|
2785
2785
|
section: section.name,
|
|
2786
2786
|
tokens
|
|
2787
2787
|
}, "Context compiler: omitted \"important\" section — no budget remaining");
|
|
@@ -2808,7 +2808,7 @@ var ContextCompilerImpl = class {
|
|
|
2808
2808
|
} else {
|
|
2809
2809
|
if (tokens > 0) {
|
|
2810
2810
|
anyTruncated = true;
|
|
2811
|
-
logger$
|
|
2811
|
+
logger$19.warn({
|
|
2812
2812
|
section: section.name,
|
|
2813
2813
|
tokens,
|
|
2814
2814
|
budgetFractionRemaining: budgetFractionRemaining.toFixed(2)
|
|
@@ -3093,7 +3093,7 @@ function parseYamlResult(yamlText, schema) {
|
|
|
3093
3093
|
|
|
3094
3094
|
//#endregion
|
|
3095
3095
|
//#region src/modules/agent-dispatch/dispatcher-impl.ts
|
|
3096
|
-
const logger$
|
|
3096
|
+
const logger$18 = createLogger("agent-dispatch");
|
|
3097
3097
|
const SHUTDOWN_GRACE_MS = 1e4;
|
|
3098
3098
|
const SHUTDOWN_MAX_WAIT_MS = 3e4;
|
|
3099
3099
|
const CHARS_PER_TOKEN = 4;
|
|
@@ -3138,7 +3138,7 @@ function getAvailableMemory() {
|
|
|
3138
3138
|
}).trim(), 10);
|
|
3139
3139
|
_lastKnownPressureLevel = pressureLevel;
|
|
3140
3140
|
if (pressureLevel >= 4) {
|
|
3141
|
-
logger$
|
|
3141
|
+
logger$18.warn({ pressureLevel }, "macOS kernel reports critical memory pressure");
|
|
3142
3142
|
return 0;
|
|
3143
3143
|
}
|
|
3144
3144
|
} catch {}
|
|
@@ -3153,7 +3153,7 @@ function getAvailableMemory() {
|
|
|
3153
3153
|
const speculative = parseInt(vmstat.match(/Pages speculative:\s+(\d+)/)?.[1] ?? "0", 10);
|
|
3154
3154
|
const available = (free + purgeable + speculative) * pageSize;
|
|
3155
3155
|
if (pressureLevel >= 2) {
|
|
3156
|
-
logger$
|
|
3156
|
+
logger$18.warn({
|
|
3157
3157
|
pressureLevel,
|
|
3158
3158
|
availableBeforeDiscount: available
|
|
3159
3159
|
}, "macOS kernel reports memory pressure — discounting estimate");
|
|
@@ -3233,7 +3233,7 @@ var DispatcherImpl = class {
|
|
|
3233
3233
|
resolve: typedResolve,
|
|
3234
3234
|
reject
|
|
3235
3235
|
});
|
|
3236
|
-
logger$
|
|
3236
|
+
logger$18.debug({
|
|
3237
3237
|
id,
|
|
3238
3238
|
queueLength: this._queue.length
|
|
3239
3239
|
}, "Dispatch queued");
|
|
@@ -3264,7 +3264,7 @@ var DispatcherImpl = class {
|
|
|
3264
3264
|
async shutdown() {
|
|
3265
3265
|
this._shuttingDown = true;
|
|
3266
3266
|
this._stopMemoryPressureTimer();
|
|
3267
|
-
logger$
|
|
3267
|
+
logger$18.info({
|
|
3268
3268
|
running: this._running.size,
|
|
3269
3269
|
queued: this._queue.length
|
|
3270
3270
|
}, "Dispatcher shutting down");
|
|
@@ -3297,13 +3297,13 @@ var DispatcherImpl = class {
|
|
|
3297
3297
|
}
|
|
3298
3298
|
}, 50);
|
|
3299
3299
|
});
|
|
3300
|
-
logger$
|
|
3300
|
+
logger$18.info("Dispatcher shutdown complete");
|
|
3301
3301
|
}
|
|
3302
3302
|
async _startDispatch(id, request, resolve$2) {
|
|
3303
3303
|
const { prompt, agent, taskType, timeout, outputSchema, workingDirectory, model, maxTurns } = request;
|
|
3304
3304
|
const adapter = this._adapterRegistry.get(agent);
|
|
3305
3305
|
if (adapter === void 0) {
|
|
3306
|
-
logger$
|
|
3306
|
+
logger$18.warn({
|
|
3307
3307
|
id,
|
|
3308
3308
|
agent
|
|
3309
3309
|
}, "No adapter found for agent");
|
|
@@ -3349,7 +3349,7 @@ var DispatcherImpl = class {
|
|
|
3349
3349
|
});
|
|
3350
3350
|
const startedAt = Date.now();
|
|
3351
3351
|
proc.on("error", (err) => {
|
|
3352
|
-
logger$
|
|
3352
|
+
logger$18.error({
|
|
3353
3353
|
id,
|
|
3354
3354
|
binary: cmd.binary,
|
|
3355
3355
|
error: err.message
|
|
@@ -3357,7 +3357,7 @@ var DispatcherImpl = class {
|
|
|
3357
3357
|
});
|
|
3358
3358
|
if (proc.stdin !== null) {
|
|
3359
3359
|
proc.stdin.on("error", (err) => {
|
|
3360
|
-
if (err.code !== "EPIPE") logger$
|
|
3360
|
+
if (err.code !== "EPIPE") logger$18.warn({
|
|
3361
3361
|
id,
|
|
3362
3362
|
error: err.message
|
|
3363
3363
|
}, "stdin write error");
|
|
@@ -3399,7 +3399,7 @@ var DispatcherImpl = class {
|
|
|
3399
3399
|
agent,
|
|
3400
3400
|
taskType
|
|
3401
3401
|
});
|
|
3402
|
-
logger$
|
|
3402
|
+
logger$18.debug({
|
|
3403
3403
|
id,
|
|
3404
3404
|
agent,
|
|
3405
3405
|
taskType,
|
|
@@ -3416,7 +3416,7 @@ var DispatcherImpl = class {
|
|
|
3416
3416
|
dispatchId: id,
|
|
3417
3417
|
timeoutMs
|
|
3418
3418
|
});
|
|
3419
|
-
logger$
|
|
3419
|
+
logger$18.warn({
|
|
3420
3420
|
id,
|
|
3421
3421
|
agent,
|
|
3422
3422
|
taskType,
|
|
@@ -3470,7 +3470,7 @@ var DispatcherImpl = class {
|
|
|
3470
3470
|
exitCode: code,
|
|
3471
3471
|
output: stdout
|
|
3472
3472
|
});
|
|
3473
|
-
logger$
|
|
3473
|
+
logger$18.debug({
|
|
3474
3474
|
id,
|
|
3475
3475
|
agent,
|
|
3476
3476
|
taskType,
|
|
@@ -3496,7 +3496,7 @@ var DispatcherImpl = class {
|
|
|
3496
3496
|
error: stderr || `Process exited with code ${String(code)}`,
|
|
3497
3497
|
exitCode: code
|
|
3498
3498
|
});
|
|
3499
|
-
logger$
|
|
3499
|
+
logger$18.debug({
|
|
3500
3500
|
id,
|
|
3501
3501
|
agent,
|
|
3502
3502
|
taskType,
|
|
@@ -3555,7 +3555,7 @@ var DispatcherImpl = class {
|
|
|
3555
3555
|
const next = this._queue.shift();
|
|
3556
3556
|
if (next === void 0) return;
|
|
3557
3557
|
next.handle.status = "running";
|
|
3558
|
-
logger$
|
|
3558
|
+
logger$18.debug({
|
|
3559
3559
|
id: next.id,
|
|
3560
3560
|
queueLength: this._queue.length
|
|
3561
3561
|
}, "Dequeued dispatch");
|
|
@@ -3568,7 +3568,7 @@ var DispatcherImpl = class {
|
|
|
3568
3568
|
_isMemoryPressured() {
|
|
3569
3569
|
const free = getAvailableMemory();
|
|
3570
3570
|
if (free < MIN_FREE_MEMORY_BYTES) {
|
|
3571
|
-
logger$
|
|
3571
|
+
logger$18.warn({
|
|
3572
3572
|
freeMB: Math.round(free / 1024 / 1024),
|
|
3573
3573
|
thresholdMB: Math.round(MIN_FREE_MEMORY_BYTES / 1024 / 1024),
|
|
3574
3574
|
pressureLevel: _lastKnownPressureLevel
|
|
@@ -3608,6 +3608,54 @@ var DispatcherImpl = class {
|
|
|
3608
3608
|
};
|
|
3609
3609
|
/** Default command for the build verification gate */
|
|
3610
3610
|
const DEFAULT_VERIFY_COMMAND = "npm run build";
|
|
3611
|
+
/**
|
|
3612
|
+
* Detect the package manager used in a project by checking for lockfiles.
|
|
3613
|
+
*
|
|
3614
|
+
* Priority order (checked in sequence):
|
|
3615
|
+
* pnpm-lock.yaml → pnpm run build
|
|
3616
|
+
* yarn.lock → yarn run build
|
|
3617
|
+
* bun.lockb → bun run build
|
|
3618
|
+
* package-lock.json → npm run build
|
|
3619
|
+
* (none found) → npm run build (fallback, matches DEFAULT_VERIFY_COMMAND)
|
|
3620
|
+
*
|
|
3621
|
+
* Lockfile presence is used rather than the `packageManager` field in
|
|
3622
|
+
* package.json because lockfiles are more reliable and always present in
|
|
3623
|
+
* real projects.
|
|
3624
|
+
*/
|
|
3625
|
+
function detectPackageManager(projectRoot) {
|
|
3626
|
+
const candidates = [
|
|
3627
|
+
{
|
|
3628
|
+
file: "pnpm-lock.yaml",
|
|
3629
|
+
packageManager: "pnpm",
|
|
3630
|
+
command: "pnpm run build"
|
|
3631
|
+
},
|
|
3632
|
+
{
|
|
3633
|
+
file: "yarn.lock",
|
|
3634
|
+
packageManager: "yarn",
|
|
3635
|
+
command: "yarn run build"
|
|
3636
|
+
},
|
|
3637
|
+
{
|
|
3638
|
+
file: "bun.lockb",
|
|
3639
|
+
packageManager: "bun",
|
|
3640
|
+
command: "bun run build"
|
|
3641
|
+
},
|
|
3642
|
+
{
|
|
3643
|
+
file: "package-lock.json",
|
|
3644
|
+
packageManager: "npm",
|
|
3645
|
+
command: "npm run build"
|
|
3646
|
+
}
|
|
3647
|
+
];
|
|
3648
|
+
for (const candidate of candidates) if (existsSync$1(join$1(projectRoot, candidate.file))) return {
|
|
3649
|
+
packageManager: candidate.packageManager,
|
|
3650
|
+
lockfile: candidate.file,
|
|
3651
|
+
command: candidate.command
|
|
3652
|
+
};
|
|
3653
|
+
return {
|
|
3654
|
+
packageManager: "npm",
|
|
3655
|
+
lockfile: null,
|
|
3656
|
+
command: DEFAULT_VERIFY_COMMAND
|
|
3657
|
+
};
|
|
3658
|
+
}
|
|
3611
3659
|
/** Default timeout in milliseconds for the build verification gate */
|
|
3612
3660
|
const DEFAULT_VERIFY_TIMEOUT_MS = 6e4;
|
|
3613
3661
|
/**
|
|
@@ -3624,7 +3672,16 @@ const DEFAULT_VERIFY_TIMEOUT_MS = 6e4;
|
|
|
3624
3672
|
*/
|
|
3625
3673
|
function runBuildVerification(options) {
|
|
3626
3674
|
const { verifyCommand, verifyTimeoutMs, projectRoot } = options;
|
|
3627
|
-
|
|
3675
|
+
let cmd;
|
|
3676
|
+
if (verifyCommand === void 0) {
|
|
3677
|
+
const detection = detectPackageManager(projectRoot);
|
|
3678
|
+
logger$18.info({
|
|
3679
|
+
packageManager: detection.packageManager,
|
|
3680
|
+
lockfile: detection.lockfile,
|
|
3681
|
+
resolvedCommand: detection.command
|
|
3682
|
+
}, "Build verification: resolved command via package manager detection");
|
|
3683
|
+
cmd = detection.command;
|
|
3684
|
+
} else cmd = verifyCommand;
|
|
3628
3685
|
if (!cmd) return { status: "skipped" };
|
|
3629
3686
|
const timeoutMs = verifyTimeoutMs ?? DEFAULT_VERIFY_TIMEOUT_MS;
|
|
3630
3687
|
try {
|
|
@@ -3817,7 +3874,7 @@ function pickRecommendation(distribution, profile, totalIssues, reviewCycles, la
|
|
|
3817
3874
|
|
|
3818
3875
|
//#endregion
|
|
3819
3876
|
//#region src/modules/compiled-workflows/prompt-assembler.ts
|
|
3820
|
-
const logger$
|
|
3877
|
+
const logger$17 = createLogger("compiled-workflows:prompt-assembler");
|
|
3821
3878
|
/**
|
|
3822
3879
|
* Assemble a final prompt from a template and sections map.
|
|
3823
3880
|
*
|
|
@@ -3842,7 +3899,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
3842
3899
|
tokenCount,
|
|
3843
3900
|
truncated: false
|
|
3844
3901
|
};
|
|
3845
|
-
logger$
|
|
3902
|
+
logger$17.warn({
|
|
3846
3903
|
tokenCount,
|
|
3847
3904
|
ceiling: tokenCeiling
|
|
3848
3905
|
}, "Prompt exceeds token ceiling — truncating optional sections");
|
|
@@ -3858,10 +3915,10 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
3858
3915
|
const targetSectionTokens = Math.max(0, currentSectionTokens - overBy);
|
|
3859
3916
|
if (targetSectionTokens === 0) {
|
|
3860
3917
|
contentMap[section.name] = "";
|
|
3861
|
-
logger$
|
|
3918
|
+
logger$17.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
|
|
3862
3919
|
} else {
|
|
3863
3920
|
contentMap[section.name] = truncateToTokens(section.content, targetSectionTokens);
|
|
3864
|
-
logger$
|
|
3921
|
+
logger$17.warn({
|
|
3865
3922
|
sectionName: section.name,
|
|
3866
3923
|
targetSectionTokens
|
|
3867
3924
|
}, "Section truncated to fit token budget");
|
|
@@ -3872,7 +3929,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
3872
3929
|
}
|
|
3873
3930
|
if (tokenCount <= tokenCeiling) break;
|
|
3874
3931
|
}
|
|
3875
|
-
if (tokenCount > tokenCeiling) logger$
|
|
3932
|
+
if (tokenCount > tokenCeiling) logger$17.warn({
|
|
3876
3933
|
tokenCount,
|
|
3877
3934
|
ceiling: tokenCeiling
|
|
3878
3935
|
}, "Required sections alone exceed token ceiling — returning over-budget prompt");
|
|
@@ -3959,6 +4016,20 @@ const CodeReviewIssueSchema = z.object({
|
|
|
3959
4016
|
line: coerceOptionalNumber
|
|
3960
4017
|
});
|
|
3961
4018
|
/**
|
|
4019
|
+
* Schema for a single entry in the AC verification checklist emitted by
|
|
4020
|
+
* the code-review sub-agent. Each entry maps an acceptance criterion to a
|
|
4021
|
+
* status determination backed by concrete evidence from the diff.
|
|
4022
|
+
*/
|
|
4023
|
+
const AcChecklistEntrySchema = z.object({
|
|
4024
|
+
ac_id: z.string(),
|
|
4025
|
+
status: z.enum([
|
|
4026
|
+
"met",
|
|
4027
|
+
"not_met",
|
|
4028
|
+
"partial"
|
|
4029
|
+
]),
|
|
4030
|
+
evidence: z.string()
|
|
4031
|
+
});
|
|
4032
|
+
/**
|
|
3962
4033
|
* Compute the verdict from the issue list using deterministic rules.
|
|
3963
4034
|
*
|
|
3964
4035
|
* The agent reports issues with severities; the pipeline computes the
|
|
@@ -3980,7 +4051,8 @@ function computeVerdict(issueList) {
|
|
|
3980
4051
|
/**
|
|
3981
4052
|
* Schema for the YAML output contract of the code-review sub-agent.
|
|
3982
4053
|
*
|
|
3983
|
-
* The agent must emit YAML with verdict, issues count, and
|
|
4054
|
+
* The agent must emit YAML with verdict, issues count, issue_list, and
|
|
4055
|
+
* optionally an ac_checklist mapping each AC to its implementation status.
|
|
3984
4056
|
* Example:
|
|
3985
4057
|
* verdict: NEEDS_MINOR_FIXES
|
|
3986
4058
|
* issues: 3
|
|
@@ -3989,11 +4061,20 @@ function computeVerdict(issueList) {
|
|
|
3989
4061
|
* description: "Missing error handling in createFoo()"
|
|
3990
4062
|
* file: "src/modules/foo/foo.ts"
|
|
3991
4063
|
* line: 42
|
|
4064
|
+
* ac_checklist:
|
|
4065
|
+
* - ac_id: AC1
|
|
4066
|
+
* status: met
|
|
4067
|
+
* evidence: "Implemented in src/modules/foo/foo.ts:createFoo()"
|
|
4068
|
+
* - ac_id: AC2
|
|
4069
|
+
* status: not_met
|
|
4070
|
+
* evidence: "No implementation found for getConstraints()"
|
|
3992
4071
|
* notes: "Generally clean implementation."
|
|
3993
4072
|
*
|
|
3994
|
-
* The transform
|
|
3995
|
-
*
|
|
3996
|
-
*
|
|
4073
|
+
* The transform:
|
|
4074
|
+
* 1. Auto-corrects the issues count from issue_list length.
|
|
4075
|
+
* 2. Auto-injects a major issue for each not_met AC not already flagged.
|
|
4076
|
+
* 3. Recomputes the verdict from the (possibly augmented) issue list.
|
|
4077
|
+
* The agent's original verdict is preserved as `agentVerdict` for logging.
|
|
3997
4078
|
*/
|
|
3998
4079
|
const CodeReviewResultSchema = z.object({
|
|
3999
4080
|
verdict: z.enum([
|
|
@@ -4003,13 +4084,27 @@ const CodeReviewResultSchema = z.object({
|
|
|
4003
4084
|
]),
|
|
4004
4085
|
issues: coerceNumber,
|
|
4005
4086
|
issue_list: z.array(CodeReviewIssueSchema),
|
|
4087
|
+
ac_checklist: z.array(AcChecklistEntrySchema).default([]),
|
|
4006
4088
|
notes: z.string().optional()
|
|
4007
|
-
}).transform((data) =>
|
|
4008
|
-
...data
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4089
|
+
}).transform((data) => {
|
|
4090
|
+
const augmentedIssueList = [...data.issue_list];
|
|
4091
|
+
for (const entry of data.ac_checklist) if (entry.status === "not_met") {
|
|
4092
|
+
const acIdPattern = new RegExp(`\\b${entry.ac_id}\\b`);
|
|
4093
|
+
const alreadyFlagged = augmentedIssueList.some((issue) => (issue.severity === "major" || issue.severity === "blocker") && acIdPattern.test(issue.description));
|
|
4094
|
+
if (!alreadyFlagged) augmentedIssueList.push({
|
|
4095
|
+
severity: "major",
|
|
4096
|
+
description: `${entry.ac_id} not implemented: ${entry.evidence}`,
|
|
4097
|
+
file: ""
|
|
4098
|
+
});
|
|
4099
|
+
}
|
|
4100
|
+
return {
|
|
4101
|
+
...data,
|
|
4102
|
+
issue_list: augmentedIssueList,
|
|
4103
|
+
issues: augmentedIssueList.length,
|
|
4104
|
+
agentVerdict: data.verdict,
|
|
4105
|
+
verdict: computeVerdict(augmentedIssueList)
|
|
4106
|
+
};
|
|
4107
|
+
});
|
|
4013
4108
|
/**
|
|
4014
4109
|
* Schema for the YAML output contract of the test-plan sub-agent.
|
|
4015
4110
|
*
|
|
@@ -4087,12 +4182,46 @@ const TestExpansionResultSchema = z.object({
|
|
|
4087
4182
|
});
|
|
4088
4183
|
|
|
4089
4184
|
//#endregion
|
|
4090
|
-
//#region src/modules/compiled-workflows/
|
|
4091
|
-
|
|
4185
|
+
//#region src/modules/compiled-workflows/token-ceiling.ts
|
|
4186
|
+
/**
|
|
4187
|
+
* Default token ceilings for each compiled workflow.
|
|
4188
|
+
* These match the hardcoded constants previously defined inline in each workflow.
|
|
4189
|
+
*/
|
|
4190
|
+
const TOKEN_CEILING_DEFAULTS = {
|
|
4191
|
+
"create-story": 3e3,
|
|
4192
|
+
"dev-story": 24e3,
|
|
4193
|
+
"code-review": 1e5,
|
|
4194
|
+
"test-plan": 8e3,
|
|
4195
|
+
"test-expansion": 2e4
|
|
4196
|
+
};
|
|
4092
4197
|
/**
|
|
4093
|
-
*
|
|
4198
|
+
* Resolve the effective token ceiling for a workflow type.
|
|
4199
|
+
*
|
|
4200
|
+
* Returns the ceiling from `tokenCeilings` config if present and valid,
|
|
4201
|
+
* otherwise falls back to the hardcoded default.
|
|
4202
|
+
*
|
|
4203
|
+
* @param workflowType - One of: 'create-story', 'dev-story', 'code-review', 'test-plan', 'test-expansion'
|
|
4204
|
+
* @param tokenCeilings - Optional per-workflow overrides from parsed config
|
|
4205
|
+
* @returns `{ ceiling: number, source: 'config' | 'default' }`
|
|
4094
4206
|
*/
|
|
4095
|
-
|
|
4207
|
+
function getTokenCeiling(workflowType, tokenCeilings) {
|
|
4208
|
+
if (tokenCeilings !== void 0) {
|
|
4209
|
+
const configured = tokenCeilings[workflowType];
|
|
4210
|
+
if (configured !== void 0) return {
|
|
4211
|
+
ceiling: configured,
|
|
4212
|
+
source: "config"
|
|
4213
|
+
};
|
|
4214
|
+
}
|
|
4215
|
+
const defaultValue = TOKEN_CEILING_DEFAULTS[workflowType] ?? 0;
|
|
4216
|
+
return {
|
|
4217
|
+
ceiling: defaultValue,
|
|
4218
|
+
source: "default"
|
|
4219
|
+
};
|
|
4220
|
+
}
|
|
4221
|
+
|
|
4222
|
+
//#endregion
|
|
4223
|
+
//#region src/modules/compiled-workflows/create-story.ts
|
|
4224
|
+
const logger$16 = createLogger("compiled-workflows:create-story");
|
|
4096
4225
|
/**
|
|
4097
4226
|
* Execute the compiled create-story workflow.
|
|
4098
4227
|
*
|
|
@@ -4112,17 +4241,23 @@ const TOKEN_CEILING$4 = 3e3;
|
|
|
4112
4241
|
*/
|
|
4113
4242
|
async function runCreateStory(deps, params) {
|
|
4114
4243
|
const { epicId, storyKey, pipelineRunId } = params;
|
|
4115
|
-
logger$
|
|
4244
|
+
logger$16.debug({
|
|
4116
4245
|
epicId,
|
|
4117
4246
|
storyKey,
|
|
4118
4247
|
pipelineRunId
|
|
4119
4248
|
}, "Starting create-story workflow");
|
|
4249
|
+
const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("create-story", deps.tokenCeilings);
|
|
4250
|
+
logger$16.info({
|
|
4251
|
+
workflow: "create-story",
|
|
4252
|
+
ceiling: TOKEN_CEILING,
|
|
4253
|
+
source: tokenCeilingSource
|
|
4254
|
+
}, "Token ceiling resolved");
|
|
4120
4255
|
let template;
|
|
4121
4256
|
try {
|
|
4122
4257
|
template = await deps.pack.getPrompt("create-story");
|
|
4123
4258
|
} catch (err) {
|
|
4124
4259
|
const error = err instanceof Error ? err.message : String(err);
|
|
4125
|
-
logger$
|
|
4260
|
+
logger$16.error({ error }, "Failed to retrieve create-story prompt template");
|
|
4126
4261
|
return {
|
|
4127
4262
|
result: "failed",
|
|
4128
4263
|
error: `Failed to retrieve prompt template: ${error}`,
|
|
@@ -4163,11 +4298,11 @@ async function runCreateStory(deps, params) {
|
|
|
4163
4298
|
content: storyTemplateContent,
|
|
4164
4299
|
priority: "important"
|
|
4165
4300
|
}
|
|
4166
|
-
], TOKEN_CEILING
|
|
4167
|
-
logger$
|
|
4301
|
+
], TOKEN_CEILING);
|
|
4302
|
+
logger$16.debug({
|
|
4168
4303
|
tokenCount,
|
|
4169
4304
|
truncated,
|
|
4170
|
-
tokenCeiling: TOKEN_CEILING
|
|
4305
|
+
tokenCeiling: TOKEN_CEILING
|
|
4171
4306
|
}, "Prompt assembled for create-story");
|
|
4172
4307
|
const handle = deps.dispatcher.dispatch({
|
|
4173
4308
|
prompt,
|
|
@@ -4181,7 +4316,7 @@ async function runCreateStory(deps, params) {
|
|
|
4181
4316
|
dispatchResult = await handle.result;
|
|
4182
4317
|
} catch (err) {
|
|
4183
4318
|
const error = err instanceof Error ? err.message : String(err);
|
|
4184
|
-
logger$
|
|
4319
|
+
logger$16.error({
|
|
4185
4320
|
epicId,
|
|
4186
4321
|
storyKey,
|
|
4187
4322
|
error
|
|
@@ -4202,7 +4337,7 @@ async function runCreateStory(deps, params) {
|
|
|
4202
4337
|
if (dispatchResult.status === "failed") {
|
|
4203
4338
|
const errorMsg = dispatchResult.parseError ?? `Dispatch failed with exit code ${dispatchResult.exitCode}`;
|
|
4204
4339
|
const stderrDetail = dispatchResult.output ? ` Output: ${dispatchResult.output}` : "";
|
|
4205
|
-
logger$
|
|
4340
|
+
logger$16.warn({
|
|
4206
4341
|
epicId,
|
|
4207
4342
|
storyKey,
|
|
4208
4343
|
exitCode: dispatchResult.exitCode
|
|
@@ -4214,7 +4349,7 @@ async function runCreateStory(deps, params) {
|
|
|
4214
4349
|
};
|
|
4215
4350
|
}
|
|
4216
4351
|
if (dispatchResult.status === "timeout") {
|
|
4217
|
-
logger$
|
|
4352
|
+
logger$16.warn({
|
|
4218
4353
|
epicId,
|
|
4219
4354
|
storyKey
|
|
4220
4355
|
}, "Create-story dispatch timed out");
|
|
@@ -4227,7 +4362,7 @@ async function runCreateStory(deps, params) {
|
|
|
4227
4362
|
if (dispatchResult.parsed === null) {
|
|
4228
4363
|
const details = dispatchResult.parseError ?? "No YAML block found in output";
|
|
4229
4364
|
const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
|
|
4230
|
-
logger$
|
|
4365
|
+
logger$16.warn({
|
|
4231
4366
|
epicId,
|
|
4232
4367
|
storyKey,
|
|
4233
4368
|
details,
|
|
@@ -4243,7 +4378,7 @@ async function runCreateStory(deps, params) {
|
|
|
4243
4378
|
const parseResult = CreateStoryResultSchema.safeParse(dispatchResult.parsed);
|
|
4244
4379
|
if (!parseResult.success) {
|
|
4245
4380
|
const details = parseResult.error.message;
|
|
4246
|
-
logger$
|
|
4381
|
+
logger$16.warn({
|
|
4247
4382
|
epicId,
|
|
4248
4383
|
storyKey,
|
|
4249
4384
|
details
|
|
@@ -4256,7 +4391,7 @@ async function runCreateStory(deps, params) {
|
|
|
4256
4391
|
};
|
|
4257
4392
|
}
|
|
4258
4393
|
const parsed = parseResult.data;
|
|
4259
|
-
logger$
|
|
4394
|
+
logger$16.info({
|
|
4260
4395
|
epicId,
|
|
4261
4396
|
storyKey,
|
|
4262
4397
|
storyFile: parsed.story_file,
|
|
@@ -4278,7 +4413,7 @@ function getImplementationDecisions(deps) {
|
|
|
4278
4413
|
try {
|
|
4279
4414
|
return getDecisionsByPhase(deps.db, "implementation");
|
|
4280
4415
|
} catch (err) {
|
|
4281
|
-
logger$
|
|
4416
|
+
logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
|
|
4282
4417
|
return [];
|
|
4283
4418
|
}
|
|
4284
4419
|
}
|
|
@@ -4321,13 +4456,13 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
|
4321
4456
|
if (storyKey) {
|
|
4322
4457
|
const storySection = extractStorySection(shardContent, storyKey);
|
|
4323
4458
|
if (storySection) {
|
|
4324
|
-
logger$
|
|
4459
|
+
logger$16.debug({
|
|
4325
4460
|
epicId,
|
|
4326
4461
|
storyKey
|
|
4327
4462
|
}, "Extracted per-story section from epic shard");
|
|
4328
4463
|
return storySection;
|
|
4329
4464
|
}
|
|
4330
|
-
logger$
|
|
4465
|
+
logger$16.debug({
|
|
4331
4466
|
epicId,
|
|
4332
4467
|
storyKey
|
|
4333
4468
|
}, "No matching story section found — using full epic shard");
|
|
@@ -4337,11 +4472,11 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
|
4337
4472
|
if (projectRoot) {
|
|
4338
4473
|
const fallback = readEpicShardFromFile(projectRoot, epicId);
|
|
4339
4474
|
if (fallback) {
|
|
4340
|
-
logger$
|
|
4475
|
+
logger$16.info({ epicId }, "Using file-based fallback for epic shard (decisions table empty)");
|
|
4341
4476
|
if (storyKey) {
|
|
4342
4477
|
const storySection = extractStorySection(fallback, storyKey);
|
|
4343
4478
|
if (storySection) {
|
|
4344
|
-
logger$
|
|
4479
|
+
logger$16.debug({
|
|
4345
4480
|
epicId,
|
|
4346
4481
|
storyKey
|
|
4347
4482
|
}, "Extracted per-story section from file-based epic shard");
|
|
@@ -4353,7 +4488,7 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
|
4353
4488
|
}
|
|
4354
4489
|
return "";
|
|
4355
4490
|
} catch (err) {
|
|
4356
|
-
logger$
|
|
4491
|
+
logger$16.warn({
|
|
4357
4492
|
epicId,
|
|
4358
4493
|
error: err instanceof Error ? err.message : String(err)
|
|
4359
4494
|
}, "Failed to retrieve epic shard");
|
|
@@ -4370,7 +4505,7 @@ function getPrevDevNotes(decisions, epicId) {
|
|
|
4370
4505
|
if (devNotes.length === 0) return "";
|
|
4371
4506
|
return devNotes[devNotes.length - 1].value;
|
|
4372
4507
|
} catch (err) {
|
|
4373
|
-
logger$
|
|
4508
|
+
logger$16.warn({
|
|
4374
4509
|
epicId,
|
|
4375
4510
|
error: err instanceof Error ? err.message : String(err)
|
|
4376
4511
|
}, "Failed to retrieve prev dev notes");
|
|
@@ -4390,13 +4525,13 @@ function getArchConstraints$2(deps) {
|
|
|
4390
4525
|
if (deps.projectRoot) {
|
|
4391
4526
|
const fallback = readArchConstraintsFromFile(deps.projectRoot);
|
|
4392
4527
|
if (fallback) {
|
|
4393
|
-
logger$
|
|
4528
|
+
logger$16.info("Using file-based fallback for architecture constraints (decisions table empty)");
|
|
4394
4529
|
return fallback;
|
|
4395
4530
|
}
|
|
4396
4531
|
}
|
|
4397
4532
|
return "";
|
|
4398
4533
|
} catch (err) {
|
|
4399
|
-
logger$
|
|
4534
|
+
logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
|
|
4400
4535
|
return "";
|
|
4401
4536
|
}
|
|
4402
4537
|
}
|
|
@@ -4416,7 +4551,7 @@ function readEpicShardFromFile(projectRoot, epicId) {
|
|
|
4416
4551
|
const match = pattern.exec(content);
|
|
4417
4552
|
return match ? match[0].trim() : "";
|
|
4418
4553
|
} catch (err) {
|
|
4419
|
-
logger$
|
|
4554
|
+
logger$16.warn({
|
|
4420
4555
|
epicId,
|
|
4421
4556
|
error: err instanceof Error ? err.message : String(err)
|
|
4422
4557
|
}, "File-based epic shard fallback failed");
|
|
@@ -4439,7 +4574,7 @@ function readArchConstraintsFromFile(projectRoot) {
|
|
|
4439
4574
|
const content = readFileSync$1(archPath, "utf-8");
|
|
4440
4575
|
return content.slice(0, 1500);
|
|
4441
4576
|
} catch (err) {
|
|
4442
|
-
logger$
|
|
4577
|
+
logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
|
|
4443
4578
|
return "";
|
|
4444
4579
|
}
|
|
4445
4580
|
}
|
|
@@ -4452,7 +4587,7 @@ async function getStoryTemplate(deps) {
|
|
|
4452
4587
|
try {
|
|
4453
4588
|
return await deps.pack.getTemplate("story");
|
|
4454
4589
|
} catch (err) {
|
|
4455
|
-
logger$
|
|
4590
|
+
logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
|
|
4456
4591
|
return "";
|
|
4457
4592
|
}
|
|
4458
4593
|
}
|
|
@@ -4489,7 +4624,7 @@ async function isValidStoryFile(filePath) {
|
|
|
4489
4624
|
|
|
4490
4625
|
//#endregion
|
|
4491
4626
|
//#region src/modules/compiled-workflows/git-helpers.ts
|
|
4492
|
-
const logger$
|
|
4627
|
+
const logger$15 = createLogger("compiled-workflows:git-helpers");
|
|
4493
4628
|
/**
|
|
4494
4629
|
* Capture the full git diff for HEAD (working tree vs current commit).
|
|
4495
4630
|
*
|
|
@@ -4585,7 +4720,7 @@ async function stageIntentToAdd(files, workingDirectory) {
|
|
|
4585
4720
|
if (files.length === 0) return;
|
|
4586
4721
|
const existing = files.filter((f) => {
|
|
4587
4722
|
const exists = existsSync$1(f);
|
|
4588
|
-
if (!exists) logger$
|
|
4723
|
+
if (!exists) logger$15.debug({ file: f }, "Skipping nonexistent file in stageIntentToAdd");
|
|
4589
4724
|
return exists;
|
|
4590
4725
|
});
|
|
4591
4726
|
if (existing.length === 0) return;
|
|
@@ -4619,7 +4754,7 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
4619
4754
|
stderr += chunk.toString("utf-8");
|
|
4620
4755
|
});
|
|
4621
4756
|
proc.on("error", (err) => {
|
|
4622
|
-
logger$
|
|
4757
|
+
logger$15.warn({
|
|
4623
4758
|
label: logLabel,
|
|
4624
4759
|
cwd,
|
|
4625
4760
|
error: err.message
|
|
@@ -4628,7 +4763,7 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
4628
4763
|
});
|
|
4629
4764
|
proc.on("close", (code) => {
|
|
4630
4765
|
if (code !== 0) {
|
|
4631
|
-
logger$
|
|
4766
|
+
logger$15.warn({
|
|
4632
4767
|
label: logLabel,
|
|
4633
4768
|
cwd,
|
|
4634
4769
|
code,
|
|
@@ -4644,7 +4779,7 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
4644
4779
|
|
|
4645
4780
|
//#endregion
|
|
4646
4781
|
//#region src/modules/implementation-orchestrator/project-findings.ts
|
|
4647
|
-
const logger$
|
|
4782
|
+
const logger$14 = createLogger("project-findings");
|
|
4648
4783
|
/** Maximum character length for the findings summary */
|
|
4649
4784
|
const MAX_CHARS = 2e3;
|
|
4650
4785
|
/**
|
|
@@ -4699,7 +4834,7 @@ function getProjectFindings(db) {
|
|
|
4699
4834
|
if (summary.length > MAX_CHARS) summary = summary.slice(0, MAX_CHARS - 3) + "...";
|
|
4700
4835
|
return summary;
|
|
4701
4836
|
} catch (err) {
|
|
4702
|
-
logger$
|
|
4837
|
+
logger$14.warn({ err }, "Failed to query project findings (graceful fallback)");
|
|
4703
4838
|
return "";
|
|
4704
4839
|
}
|
|
4705
4840
|
}
|
|
@@ -4720,11 +4855,119 @@ function extractRecurringPatterns(outcomes) {
|
|
|
4720
4855
|
return [...patternCounts.entries()].filter(([, count]) => count >= 2).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => `${pattern} (${count} occurrences)`);
|
|
4721
4856
|
}
|
|
4722
4857
|
|
|
4858
|
+
//#endregion
|
|
4859
|
+
//#region src/modules/compiled-workflows/story-complexity.ts
|
|
4860
|
+
const logger$13 = createLogger("compiled-workflows:story-complexity");
|
|
4861
|
+
/**
|
|
4862
|
+
* Compute a complexity score from story markdown content.
|
|
4863
|
+
*
|
|
4864
|
+
* Returns { taskCount, subtaskCount, fileCount, complexityScore }.
|
|
4865
|
+
* All counts default to 0 when the corresponding sections are absent.
|
|
4866
|
+
*
|
|
4867
|
+
* @param storyContent - Raw markdown content of the story file
|
|
4868
|
+
*/
|
|
4869
|
+
function computeStoryComplexity(storyContent) {
|
|
4870
|
+
const taskCount = countTopLevelTasks(storyContent);
|
|
4871
|
+
const subtaskCount = countSubtasks(storyContent);
|
|
4872
|
+
const fileCount = countFilesInLayout(storyContent);
|
|
4873
|
+
const complexityScore = Math.round(taskCount + subtaskCount * .5 + fileCount * .5);
|
|
4874
|
+
return {
|
|
4875
|
+
taskCount,
|
|
4876
|
+
subtaskCount,
|
|
4877
|
+
fileCount,
|
|
4878
|
+
complexityScore
|
|
4879
|
+
};
|
|
4880
|
+
}
|
|
4881
|
+
/**
|
|
4882
|
+
* Compute the resolved maxTurns for a dev-story dispatch.
|
|
4883
|
+
*
|
|
4884
|
+
* base 75 turns for score <= 10, +10 turns per additional complexity point, capped at 200.
|
|
4885
|
+
*
|
|
4886
|
+
* @param complexityScore - Score returned by computeStoryComplexity
|
|
4887
|
+
*/
|
|
4888
|
+
function resolveDevStoryMaxTurns(complexityScore) {
|
|
4889
|
+
return Math.min(200, 75 + Math.max(0, complexityScore - 10) * 10);
|
|
4890
|
+
}
|
|
4891
|
+
/**
|
|
4892
|
+
* Compute the resolved maxTurns for a fix-story (major-rework) dispatch.
|
|
4893
|
+
*
|
|
4894
|
+
* base 50 turns for score <= 10, +10 turns per additional complexity point, capped at 150.
|
|
4895
|
+
*
|
|
4896
|
+
* @param complexityScore - Score returned by computeStoryComplexity
|
|
4897
|
+
*/
|
|
4898
|
+
function resolveFixStoryMaxTurns(complexityScore) {
|
|
4899
|
+
return Math.min(150, 50 + Math.max(0, complexityScore - 10) * 10);
|
|
4900
|
+
}
|
|
4901
|
+
/**
|
|
4902
|
+
* Log the complexity result at info level.
|
|
4903
|
+
*
|
|
4904
|
+
* Emits a structured log with storyKey, taskCount, subtaskCount, fileCount,
|
|
4905
|
+
* complexityScore, and resolvedMaxTurns so operators can observe turn-limit scaling.
|
|
4906
|
+
*
|
|
4907
|
+
* @param storyKey - Story identifier (e.g. "24-6")
|
|
4908
|
+
* @param complexity - Result from computeStoryComplexity
|
|
4909
|
+
* @param resolvedMaxTurns - Turn limit resolved for this dispatch
|
|
4910
|
+
*/
|
|
4911
|
+
function logComplexityResult(storyKey, complexity, resolvedMaxTurns) {
|
|
4912
|
+
logger$13.info({
|
|
4913
|
+
storyKey,
|
|
4914
|
+
taskCount: complexity.taskCount,
|
|
4915
|
+
subtaskCount: complexity.subtaskCount,
|
|
4916
|
+
fileCount: complexity.fileCount,
|
|
4917
|
+
complexityScore: complexity.complexityScore,
|
|
4918
|
+
resolvedMaxTurns
|
|
4919
|
+
}, "Story complexity computed");
|
|
4920
|
+
}
|
|
4921
|
+
/**
|
|
4922
|
+
* Count top-level task lines matching `- [ ] Task N:` (with literal "Task" keyword).
|
|
4923
|
+
*/
|
|
4924
|
+
function countTopLevelTasks(content) {
|
|
4925
|
+
const taskPattern = /^- \[ \] Task \d+:/gm;
|
|
4926
|
+
return (content.match(taskPattern) ?? []).length;
|
|
4927
|
+
}
|
|
4928
|
+
/**
|
|
4929
|
+
* Count nested subtask lines — `- [ ]` lines that begin with one or more spaces or tabs.
|
|
4930
|
+
*
|
|
4931
|
+
* IMPORTANT: Use `[ \t]+` (space/tab only), NOT `\s+`, because `\s` includes `\n`.
|
|
4932
|
+
* With multiline `/gm`, using `\s+` causes false matches when an empty line (`\n`)
|
|
4933
|
+
* precedes a top-level `- [ ]` line — the `\n` gets consumed as "whitespace".
|
|
4934
|
+
*/
|
|
4935
|
+
function countSubtasks(content) {
|
|
4936
|
+
const subtaskPattern = /^[ \t]+- \[ \]/gm;
|
|
4937
|
+
return (content.match(subtaskPattern) ?? []).length;
|
|
4938
|
+
}
|
|
4939
|
+
/**
|
|
4940
|
+
* Count file entries inside a "File Layout" fenced code block.
|
|
4941
|
+
*
|
|
4942
|
+
* Uses a split-based section extraction to avoid regex lazy-matching pitfalls.
|
|
4943
|
+
* Finds a heading containing "File Layout", extracts section content up to the
|
|
4944
|
+
* next heading (or end of string), then finds fenced code blocks within it.
|
|
4945
|
+
*
|
|
4946
|
+
* Returns 0 when no File Layout section or fenced code block is found.
|
|
4947
|
+
*/
|
|
4948
|
+
function countFilesInLayout(content) {
|
|
4949
|
+
const headingMatch = content.match(/^#{2,4}\s+File\s+Layout\s*$/im);
|
|
4950
|
+
if (!headingMatch || headingMatch.index === void 0) return 0;
|
|
4951
|
+
const afterHeading = content.slice(headingMatch.index + headingMatch[0].length);
|
|
4952
|
+
const nextHeadingMatch = afterHeading.match(/^#{2,4}\s+/m);
|
|
4953
|
+
const sectionContent = nextHeadingMatch?.index !== void 0 ? afterHeading.slice(0, nextHeadingMatch.index) : afterHeading;
|
|
4954
|
+
const codeBlocks = sectionContent.match(/```[\s\S]*?```/g);
|
|
4955
|
+
if (!codeBlocks) return 0;
|
|
4956
|
+
const fileExtPattern = /\.(ts|js|json|sql|yaml|yml|md)\b/;
|
|
4957
|
+
let count = 0;
|
|
4958
|
+
for (const block of codeBlocks) {
|
|
4959
|
+
const lines = block.split("\n");
|
|
4960
|
+
for (const line of lines) {
|
|
4961
|
+
if (line.trimStart().startsWith("```")) continue;
|
|
4962
|
+
if (fileExtPattern.test(line)) count++;
|
|
4963
|
+
}
|
|
4964
|
+
}
|
|
4965
|
+
return count;
|
|
4966
|
+
}
|
|
4967
|
+
|
|
4723
4968
|
//#endregion
|
|
4724
4969
|
//#region src/modules/compiled-workflows/dev-story.ts
|
|
4725
4970
|
const logger$12 = createLogger("compiled-workflows:dev-story");
|
|
4726
|
-
/** Hard token ceiling for the assembled dev-story prompt */
|
|
4727
|
-
const TOKEN_CEILING$3 = 24e3;
|
|
4728
4971
|
/** Default timeout for dev-story dispatches in milliseconds (30 min) */
|
|
4729
4972
|
const DEFAULT_TIMEOUT_MS$1 = 18e5;
|
|
4730
4973
|
/** Default Vitest test patterns injected when no test-pattern decisions exist */
|
|
@@ -4751,6 +4994,12 @@ async function runDevStory(deps, params) {
|
|
|
4751
4994
|
storyKey,
|
|
4752
4995
|
storyFilePath
|
|
4753
4996
|
}, "Starting compiled dev-story workflow");
|
|
4997
|
+
const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("dev-story", deps.tokenCeilings);
|
|
4998
|
+
logger$12.info({
|
|
4999
|
+
workflow: "dev-story",
|
|
5000
|
+
ceiling: TOKEN_CEILING,
|
|
5001
|
+
source: tokenCeilingSource
|
|
5002
|
+
}, "Token ceiling resolved");
|
|
4754
5003
|
const devStoryContextTemplate = {
|
|
4755
5004
|
taskType: "dev-story",
|
|
4756
5005
|
sections: [{
|
|
@@ -4824,6 +5073,9 @@ async function runDevStory(deps, params) {
|
|
|
4824
5073
|
}, "Story file is empty");
|
|
4825
5074
|
return makeFailureResult("story_file_empty");
|
|
4826
5075
|
}
|
|
5076
|
+
const complexity = computeStoryComplexity(storyContent);
|
|
5077
|
+
const resolvedMaxTurns = resolveDevStoryMaxTurns(complexity.complexityScore);
|
|
5078
|
+
logComplexityResult(storyKey, complexity, resolvedMaxTurns);
|
|
4827
5079
|
let testPatternsContent = "";
|
|
4828
5080
|
try {
|
|
4829
5081
|
const solutioningDecisions = getDecisionsByPhase(deps.db, "solutioning");
|
|
@@ -4920,11 +5172,11 @@ async function runDevStory(deps, params) {
|
|
|
4920
5172
|
priority: "optional"
|
|
4921
5173
|
}
|
|
4922
5174
|
];
|
|
4923
|
-
const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING
|
|
5175
|
+
const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING);
|
|
4924
5176
|
logger$12.info({
|
|
4925
5177
|
storyKey,
|
|
4926
5178
|
tokenCount,
|
|
4927
|
-
ceiling: TOKEN_CEILING
|
|
5179
|
+
ceiling: TOKEN_CEILING,
|
|
4928
5180
|
truncated
|
|
4929
5181
|
}, "Assembled dev-story prompt");
|
|
4930
5182
|
let dispatchResult;
|
|
@@ -4935,6 +5187,7 @@ async function runDevStory(deps, params) {
|
|
|
4935
5187
|
taskType: "dev-story",
|
|
4936
5188
|
timeout: DEFAULT_TIMEOUT_MS$1,
|
|
4937
5189
|
outputSchema: DevStoryResultSchema,
|
|
5190
|
+
maxTurns: resolvedMaxTurns,
|
|
4938
5191
|
...deps.projectRoot !== void 0 ? { workingDirectory: deps.projectRoot } : {}
|
|
4939
5192
|
});
|
|
4940
5193
|
dispatchResult = await handle.result;
|
|
@@ -5153,12 +5406,6 @@ function extractFilesInScope(storyContent) {
|
|
|
5153
5406
|
//#region src/modules/compiled-workflows/code-review.ts
|
|
5154
5407
|
const logger$11 = createLogger("compiled-workflows:code-review");
|
|
5155
5408
|
/**
|
|
5156
|
-
* Hard token ceiling for the assembled code-review prompt (50,000 tokens).
|
|
5157
|
-
* Quality reviews require seeing actual code diffs, not just file names.
|
|
5158
|
-
* // TODO: consider externalizing to pack config when multiple packs exist
|
|
5159
|
-
*/
|
|
5160
|
-
const TOKEN_CEILING$2 = 1e5;
|
|
5161
|
-
/**
|
|
5162
5409
|
* Default fallback result when dispatch fails or times out.
|
|
5163
5410
|
* Uses NEEDS_MINOR_FIXES (not NEEDS_MAJOR_REWORK) so a parse/schema failure
|
|
5164
5411
|
* doesn't trigger a full rework cycle. The orchestrator's phantom-review
|
|
@@ -5201,6 +5448,12 @@ async function runCodeReview(deps, params) {
|
|
|
5201
5448
|
cwd,
|
|
5202
5449
|
pipelineRunId
|
|
5203
5450
|
}, "Starting code-review workflow");
|
|
5451
|
+
const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("code-review", deps.tokenCeilings);
|
|
5452
|
+
logger$11.info({
|
|
5453
|
+
workflow: "code-review",
|
|
5454
|
+
ceiling: TOKEN_CEILING,
|
|
5455
|
+
source: tokenCeilingSource
|
|
5456
|
+
}, "Token ceiling resolved");
|
|
5204
5457
|
let template;
|
|
5205
5458
|
try {
|
|
5206
5459
|
template = await deps.pack.getPrompt("code-review");
|
|
@@ -5235,7 +5488,7 @@ async function runCodeReview(deps, params) {
|
|
|
5235
5488
|
if (filesModified && filesModified.length > 0) {
|
|
5236
5489
|
const scopedDiff = await getGitDiffForFiles(filesModified, cwd);
|
|
5237
5490
|
const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
|
|
5238
|
-
if (scopedTotal <= TOKEN_CEILING
|
|
5491
|
+
if (scopedTotal <= TOKEN_CEILING) {
|
|
5239
5492
|
gitDiffContent = scopedDiff;
|
|
5240
5493
|
logger$11.debug({
|
|
5241
5494
|
fileCount: filesModified.length,
|
|
@@ -5244,7 +5497,7 @@ async function runCodeReview(deps, params) {
|
|
|
5244
5497
|
} else {
|
|
5245
5498
|
logger$11.warn({
|
|
5246
5499
|
estimatedTotal: scopedTotal,
|
|
5247
|
-
ceiling: TOKEN_CEILING
|
|
5500
|
+
ceiling: TOKEN_CEILING,
|
|
5248
5501
|
fileCount: filesModified.length
|
|
5249
5502
|
}, "Scoped diff exceeds token ceiling — falling back to stat-only summary");
|
|
5250
5503
|
gitDiffContent = await getGitDiffStatSummary(cwd);
|
|
@@ -5254,11 +5507,11 @@ async function runCodeReview(deps, params) {
|
|
|
5254
5507
|
await stageIntentToAdd(changedFiles, cwd);
|
|
5255
5508
|
const fullDiff = await getGitDiffSummary(cwd);
|
|
5256
5509
|
const fullTotal = nonDiffTokens + countTokens(fullDiff);
|
|
5257
|
-
if (fullTotal <= TOKEN_CEILING
|
|
5510
|
+
if (fullTotal <= TOKEN_CEILING) gitDiffContent = fullDiff;
|
|
5258
5511
|
else {
|
|
5259
5512
|
logger$11.warn({
|
|
5260
5513
|
estimatedTotal: fullTotal,
|
|
5261
|
-
ceiling: TOKEN_CEILING
|
|
5514
|
+
ceiling: TOKEN_CEILING
|
|
5262
5515
|
}, "Full git diff would exceed token ceiling — using stat-only summary");
|
|
5263
5516
|
gitDiffContent = await getGitDiffStatSummary(cwd);
|
|
5264
5517
|
}
|
|
@@ -5322,7 +5575,7 @@ async function runCodeReview(deps, params) {
|
|
|
5322
5575
|
priority: "optional"
|
|
5323
5576
|
}
|
|
5324
5577
|
];
|
|
5325
|
-
const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING
|
|
5578
|
+
const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
|
|
5326
5579
|
if (assembleResult.truncated) logger$11.warn({
|
|
5327
5580
|
storyKey,
|
|
5328
5581
|
tokenCount: assembleResult.tokenCount
|
|
@@ -5450,8 +5703,6 @@ function getArchConstraints$1(deps) {
|
|
|
5450
5703
|
//#endregion
|
|
5451
5704
|
//#region src/modules/compiled-workflows/test-plan.ts
|
|
5452
5705
|
const logger$10 = createLogger("compiled-workflows:test-plan");
|
|
5453
|
-
/** Hard token ceiling for the assembled test-plan prompt */
|
|
5454
|
-
const TOKEN_CEILING$1 = 8e3;
|
|
5455
5706
|
/** Default timeout for test-plan dispatches in milliseconds (5 min — lightweight call) */
|
|
5456
5707
|
const DEFAULT_TIMEOUT_MS = 3e5;
|
|
5457
5708
|
/**
|
|
@@ -5467,6 +5718,12 @@ async function runTestPlan(deps, params) {
|
|
|
5467
5718
|
storyKey,
|
|
5468
5719
|
storyFilePath
|
|
5469
5720
|
}, "Starting compiled test-plan workflow");
|
|
5721
|
+
const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("test-plan", deps.tokenCeilings);
|
|
5722
|
+
logger$10.info({
|
|
5723
|
+
workflow: "test-plan",
|
|
5724
|
+
ceiling: TOKEN_CEILING,
|
|
5725
|
+
source: tokenCeilingSource
|
|
5726
|
+
}, "Token ceiling resolved");
|
|
5470
5727
|
let template;
|
|
5471
5728
|
try {
|
|
5472
5729
|
template = await deps.pack.getPrompt("test-plan");
|
|
@@ -5502,11 +5759,11 @@ async function runTestPlan(deps, params) {
|
|
|
5502
5759
|
name: "story_content",
|
|
5503
5760
|
content: storyContent,
|
|
5504
5761
|
priority: "required"
|
|
5505
|
-
}], TOKEN_CEILING
|
|
5762
|
+
}], TOKEN_CEILING);
|
|
5506
5763
|
logger$10.info({
|
|
5507
5764
|
storyKey,
|
|
5508
5765
|
tokenCount,
|
|
5509
|
-
ceiling: TOKEN_CEILING
|
|
5766
|
+
ceiling: TOKEN_CEILING,
|
|
5510
5767
|
truncated
|
|
5511
5768
|
}, "Assembled test-plan prompt");
|
|
5512
5769
|
let dispatchResult;
|
|
@@ -5622,10 +5879,6 @@ function makeTestPlanFailureResult(error) {
|
|
|
5622
5879
|
//#endregion
|
|
5623
5880
|
//#region src/modules/compiled-workflows/test-expansion.ts
|
|
5624
5881
|
const logger$9 = createLogger("compiled-workflows:test-expansion");
|
|
5625
|
-
/**
|
|
5626
|
-
* Hard token ceiling for the assembled test-expansion prompt (20,000 tokens).
|
|
5627
|
-
*/
|
|
5628
|
-
const TOKEN_CEILING = 2e4;
|
|
5629
5882
|
function defaultFallbackResult(error, tokenUsage) {
|
|
5630
5883
|
return {
|
|
5631
5884
|
expansion_priority: "low",
|
|
@@ -5661,6 +5914,12 @@ async function runTestExpansion(deps, params) {
|
|
|
5661
5914
|
cwd,
|
|
5662
5915
|
pipelineRunId
|
|
5663
5916
|
}, "Starting test-expansion workflow");
|
|
5917
|
+
const { ceiling: TOKEN_CEILING, source: tokenCeilingSource } = getTokenCeiling("test-expansion", deps.tokenCeilings);
|
|
5918
|
+
logger$9.info({
|
|
5919
|
+
workflow: "test-expansion",
|
|
5920
|
+
ceiling: TOKEN_CEILING,
|
|
5921
|
+
source: tokenCeilingSource
|
|
5922
|
+
}, "Token ceiling resolved");
|
|
5664
5923
|
let template;
|
|
5665
5924
|
try {
|
|
5666
5925
|
template = await deps.pack.getPrompt("test-expansion");
|
|
@@ -6864,6 +7123,27 @@ function createPauseGate() {
|
|
|
6864
7123
|
};
|
|
6865
7124
|
}
|
|
6866
7125
|
/**
|
|
7126
|
+
* Build the targeted_files content string from a code-review issue list.
|
|
7127
|
+
* Deduplicates file paths and includes line numbers where available.
|
|
7128
|
+
* Returns empty string when no issues have file references.
|
|
7129
|
+
*/
|
|
7130
|
+
function buildTargetedFilesContent(issueList) {
|
|
7131
|
+
const seen = new Map();
|
|
7132
|
+
for (const issue of issueList) {
|
|
7133
|
+
const iss = issue;
|
|
7134
|
+
if (!iss.file) continue;
|
|
7135
|
+
if (!seen.has(iss.file)) seen.set(iss.file, new Set());
|
|
7136
|
+
if (iss.line !== void 0) seen.get(iss.file).add(iss.line);
|
|
7137
|
+
}
|
|
7138
|
+
if (seen.size === 0) return "";
|
|
7139
|
+
const lines = [];
|
|
7140
|
+
for (const [file, lineNums] of seen) if (lineNums.size > 0) {
|
|
7141
|
+
const sorted = [...lineNums].sort((a, b) => a - b);
|
|
7142
|
+
lines.push(`- ${file} (lines: ${sorted.join(", ")})`);
|
|
7143
|
+
} else lines.push(`- ${file}`);
|
|
7144
|
+
return lines.join("\n");
|
|
7145
|
+
}
|
|
7146
|
+
/**
|
|
6867
7147
|
* Factory function that creates an ImplementationOrchestrator instance.
|
|
6868
7148
|
*
|
|
6869
7149
|
* @param deps - Injected dependencies (db, pack, contextCompiler, dispatcher,
|
|
@@ -6871,8 +7151,8 @@ function createPauseGate() {
|
|
|
6871
7151
|
* @returns A fully-configured ImplementationOrchestrator ready to call run()
|
|
6872
7152
|
*/
|
|
6873
7153
|
function createImplementationOrchestrator(deps) {
|
|
6874
|
-
const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot } = deps;
|
|
6875
|
-
const logger$
|
|
7154
|
+
const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot, tokenCeilings } = deps;
|
|
7155
|
+
const logger$22 = createLogger("implementation-orchestrator");
|
|
6876
7156
|
let _state = "IDLE";
|
|
6877
7157
|
let _startedAt;
|
|
6878
7158
|
let _completedAt;
|
|
@@ -6915,7 +7195,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6915
7195
|
const nowMs = Date.now();
|
|
6916
7196
|
for (const [phase, startMs] of starts) {
|
|
6917
7197
|
const endMs = ends?.get(phase);
|
|
6918
|
-
if (endMs === void 0) logger$
|
|
7198
|
+
if (endMs === void 0) logger$22.warn({
|
|
6919
7199
|
storyKey,
|
|
6920
7200
|
phase
|
|
6921
7201
|
}, "Phase has no end time — story may have errored mid-phase. Duration capped to now() and may be inflated.");
|
|
@@ -6964,7 +7244,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6964
7244
|
rationale: `Story ${storyKey} completed with result=${result} in ${wallClockSeconds}s. Tokens: ${tokenAgg.input}+${tokenAgg.output}. Review cycles: ${reviewCycles}.`
|
|
6965
7245
|
});
|
|
6966
7246
|
} catch (decisionErr) {
|
|
6967
|
-
logger$
|
|
7247
|
+
logger$22.warn({
|
|
6968
7248
|
err: decisionErr,
|
|
6969
7249
|
storyKey
|
|
6970
7250
|
}, "Failed to write story-metrics decision (best-effort)");
|
|
@@ -6992,13 +7272,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
6992
7272
|
dispatches: _storyDispatches.get(storyKey) ?? 0
|
|
6993
7273
|
});
|
|
6994
7274
|
} catch (emitErr) {
|
|
6995
|
-
logger$
|
|
7275
|
+
logger$22.warn({
|
|
6996
7276
|
err: emitErr,
|
|
6997
7277
|
storyKey
|
|
6998
7278
|
}, "Failed to emit story:metrics event (best-effort)");
|
|
6999
7279
|
}
|
|
7000
7280
|
} catch (err) {
|
|
7001
|
-
logger$
|
|
7281
|
+
logger$22.warn({
|
|
7002
7282
|
err,
|
|
7003
7283
|
storyKey
|
|
7004
7284
|
}, "Failed to write story metrics (best-effort)");
|
|
@@ -7027,7 +7307,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7027
7307
|
rationale: `Story ${storyKey} ${outcome} after ${reviewCycles} review cycle(s).`
|
|
7028
7308
|
});
|
|
7029
7309
|
} catch (err) {
|
|
7030
|
-
logger$
|
|
7310
|
+
logger$22.warn({
|
|
7031
7311
|
err,
|
|
7032
7312
|
storyKey
|
|
7033
7313
|
}, "Failed to write story-outcome decision (best-effort)");
|
|
@@ -7053,7 +7333,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7053
7333
|
rationale: `Escalation diagnosis for ${payload.storyKey}: ${diagnosis.recommendedAction} — ${diagnosis.rationale}`
|
|
7054
7334
|
});
|
|
7055
7335
|
} catch (err) {
|
|
7056
|
-
logger$
|
|
7336
|
+
logger$22.warn({
|
|
7057
7337
|
err,
|
|
7058
7338
|
storyKey: payload.storyKey
|
|
7059
7339
|
}, "Failed to persist escalation diagnosis (best-effort)");
|
|
@@ -7103,7 +7383,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7103
7383
|
token_usage_json: serialized
|
|
7104
7384
|
});
|
|
7105
7385
|
} catch (err) {
|
|
7106
|
-
logger$
|
|
7386
|
+
logger$22.warn("Failed to persist orchestrator state", { err });
|
|
7107
7387
|
}
|
|
7108
7388
|
}
|
|
7109
7389
|
function recordProgress() {
|
|
@@ -7150,7 +7430,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7150
7430
|
}
|
|
7151
7431
|
if (childActive) {
|
|
7152
7432
|
_lastProgressTs = Date.now();
|
|
7153
|
-
logger$
|
|
7433
|
+
logger$22.debug({
|
|
7154
7434
|
storyKey: key,
|
|
7155
7435
|
phase: s.phase,
|
|
7156
7436
|
childPids
|
|
@@ -7159,7 +7439,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7159
7439
|
}
|
|
7160
7440
|
_stalledStories.add(key);
|
|
7161
7441
|
_storiesWithStall.add(key);
|
|
7162
|
-
logger$
|
|
7442
|
+
logger$22.warn({
|
|
7163
7443
|
storyKey: key,
|
|
7164
7444
|
phase: s.phase,
|
|
7165
7445
|
elapsedMs: elapsed,
|
|
@@ -7204,7 +7484,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7204
7484
|
for (let attempt = 0; attempt < MEMORY_PRESSURE_BACKOFF_MS.length; attempt++) {
|
|
7205
7485
|
const memState = dispatcher.getMemoryState();
|
|
7206
7486
|
if (!memState.isPressured) return true;
|
|
7207
|
-
logger$
|
|
7487
|
+
logger$22.warn({
|
|
7208
7488
|
storyKey,
|
|
7209
7489
|
freeMB: memState.freeMB,
|
|
7210
7490
|
thresholdMB: memState.thresholdMB,
|
|
@@ -7224,11 +7504,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
7224
7504
|
* exhausted retries the story is ESCALATED.
|
|
7225
7505
|
*/
|
|
7226
7506
|
async function processStory(storyKey) {
|
|
7227
|
-
logger$
|
|
7507
|
+
logger$22.info("Processing story", { storyKey });
|
|
7228
7508
|
{
|
|
7229
7509
|
const memoryOk = await checkMemoryPressure(storyKey);
|
|
7230
7510
|
if (!memoryOk) {
|
|
7231
|
-
logger$
|
|
7511
|
+
logger$22.warn({ storyKey }, "Memory pressure exhausted — escalating story without dispatch");
|
|
7232
7512
|
_stories.set(storyKey, {
|
|
7233
7513
|
phase: "ESCALATED",
|
|
7234
7514
|
reviewCycles: 0,
|
|
@@ -7262,14 +7542,14 @@ function createImplementationOrchestrator(deps) {
|
|
|
7262
7542
|
if (match) {
|
|
7263
7543
|
const candidatePath = join$1(artifactsDir, match);
|
|
7264
7544
|
const validation = await isValidStoryFile(candidatePath);
|
|
7265
|
-
if (!validation.valid) logger$
|
|
7545
|
+
if (!validation.valid) logger$22.warn({
|
|
7266
7546
|
storyKey,
|
|
7267
7547
|
storyFilePath: candidatePath,
|
|
7268
7548
|
reason: validation.reason
|
|
7269
7549
|
}, `Existing story file for ${storyKey} is invalid (${validation.reason}) — re-creating`);
|
|
7270
7550
|
else {
|
|
7271
7551
|
storyFilePath = candidatePath;
|
|
7272
|
-
logger$
|
|
7552
|
+
logger$22.info({
|
|
7273
7553
|
storyKey,
|
|
7274
7554
|
storyFilePath
|
|
7275
7555
|
}, "Found existing story file — skipping create-story");
|
|
@@ -7294,7 +7574,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7294
7574
|
pack,
|
|
7295
7575
|
contextCompiler,
|
|
7296
7576
|
dispatcher,
|
|
7297
|
-
projectRoot
|
|
7577
|
+
projectRoot,
|
|
7578
|
+
tokenCeilings
|
|
7298
7579
|
}, {
|
|
7299
7580
|
epicId: storyKey.split("-")[0] ?? storyKey,
|
|
7300
7581
|
storyKey,
|
|
@@ -7372,17 +7653,18 @@ function createImplementationOrchestrator(deps) {
|
|
|
7372
7653
|
pack,
|
|
7373
7654
|
contextCompiler,
|
|
7374
7655
|
dispatcher,
|
|
7375
|
-
projectRoot
|
|
7656
|
+
projectRoot,
|
|
7657
|
+
tokenCeilings
|
|
7376
7658
|
}, {
|
|
7377
7659
|
storyKey,
|
|
7378
7660
|
storyFilePath: storyFilePath ?? "",
|
|
7379
7661
|
pipelineRunId: config.pipelineRunId
|
|
7380
7662
|
});
|
|
7381
7663
|
testPlanPhaseResult = testPlanResult.result;
|
|
7382
|
-
if (testPlanResult.result === "success") logger$
|
|
7383
|
-
else logger$
|
|
7664
|
+
if (testPlanResult.result === "success") logger$22.info({ storyKey }, "Test plan generated successfully");
|
|
7665
|
+
else logger$22.warn({ storyKey }, "Test planning returned failed result — proceeding to dev-story without test plan");
|
|
7384
7666
|
} catch (err) {
|
|
7385
|
-
logger$
|
|
7667
|
+
logger$22.warn({
|
|
7386
7668
|
storyKey,
|
|
7387
7669
|
err
|
|
7388
7670
|
}, "Test planning failed — proceeding to dev-story without test plan");
|
|
@@ -7406,7 +7688,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7406
7688
|
try {
|
|
7407
7689
|
storyContentForAnalysis = await readFile$1(storyFilePath ?? "", "utf-8");
|
|
7408
7690
|
} catch (err) {
|
|
7409
|
-
logger$
|
|
7691
|
+
logger$22.error({
|
|
7410
7692
|
storyKey,
|
|
7411
7693
|
storyFilePath,
|
|
7412
7694
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -7414,7 +7696,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7414
7696
|
}
|
|
7415
7697
|
const analysis = analyzeStoryComplexity(storyContentForAnalysis);
|
|
7416
7698
|
const batches = planTaskBatches(analysis);
|
|
7417
|
-
logger$
|
|
7699
|
+
logger$22.info({
|
|
7418
7700
|
storyKey,
|
|
7419
7701
|
estimatedScope: analysis.estimatedScope,
|
|
7420
7702
|
batchCount: batches.length,
|
|
@@ -7432,7 +7714,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7432
7714
|
if (_state !== "RUNNING") break;
|
|
7433
7715
|
const taskScope = batch.taskIds.map((id, i) => `T${id}: ${batch.taskTitles[i] ?? ""}`).join("\n");
|
|
7434
7716
|
const priorFiles = allFilesModified.size > 0 ? Array.from(allFilesModified) : void 0;
|
|
7435
|
-
logger$
|
|
7717
|
+
logger$22.info({
|
|
7436
7718
|
storyKey,
|
|
7437
7719
|
batchIndex: batch.batchIndex,
|
|
7438
7720
|
taskCount: batch.taskIds.length
|
|
@@ -7446,7 +7728,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7446
7728
|
pack,
|
|
7447
7729
|
contextCompiler,
|
|
7448
7730
|
dispatcher,
|
|
7449
|
-
projectRoot
|
|
7731
|
+
projectRoot,
|
|
7732
|
+
tokenCeilings
|
|
7450
7733
|
}, {
|
|
7451
7734
|
storyKey,
|
|
7452
7735
|
storyFilePath: storyFilePath ?? "",
|
|
@@ -7456,7 +7739,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7456
7739
|
});
|
|
7457
7740
|
} catch (batchErr) {
|
|
7458
7741
|
const errMsg = batchErr instanceof Error ? batchErr.message : String(batchErr);
|
|
7459
|
-
logger$
|
|
7742
|
+
logger$22.warn({
|
|
7460
7743
|
storyKey,
|
|
7461
7744
|
batchIndex: batch.batchIndex,
|
|
7462
7745
|
error: errMsg
|
|
@@ -7476,7 +7759,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7476
7759
|
filesModified: batchFilesModified,
|
|
7477
7760
|
result: batchResult.result === "success" ? "success" : "failed"
|
|
7478
7761
|
};
|
|
7479
|
-
logger$
|
|
7762
|
+
logger$22.info(batchMetrics, "Batch dev-story metrics");
|
|
7480
7763
|
for (const f of batchFilesModified) allFilesModified.add(f);
|
|
7481
7764
|
if (batchFilesModified.length > 0) batchFileGroups.push({
|
|
7482
7765
|
batchIndex: batch.batchIndex,
|
|
@@ -7498,13 +7781,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
7498
7781
|
})
|
|
7499
7782
|
});
|
|
7500
7783
|
} catch (tokenErr) {
|
|
7501
|
-
logger$
|
|
7784
|
+
logger$22.warn({
|
|
7502
7785
|
storyKey,
|
|
7503
7786
|
batchIndex: batch.batchIndex,
|
|
7504
7787
|
err: tokenErr
|
|
7505
7788
|
}, "Failed to record batch token usage");
|
|
7506
7789
|
}
|
|
7507
|
-
if (batchResult.result === "failed") logger$
|
|
7790
|
+
if (batchResult.result === "failed") logger$22.warn({
|
|
7508
7791
|
storyKey,
|
|
7509
7792
|
batchIndex: batch.batchIndex,
|
|
7510
7793
|
error: batchResult.error
|
|
@@ -7525,7 +7808,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7525
7808
|
pack,
|
|
7526
7809
|
contextCompiler,
|
|
7527
7810
|
dispatcher,
|
|
7528
|
-
projectRoot
|
|
7811
|
+
projectRoot,
|
|
7812
|
+
tokenCeilings
|
|
7529
7813
|
}, {
|
|
7530
7814
|
storyKey,
|
|
7531
7815
|
storyFilePath: storyFilePath ?? "",
|
|
@@ -7539,7 +7823,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7539
7823
|
});
|
|
7540
7824
|
persistState();
|
|
7541
7825
|
if (devResult.result === "success") devStoryWasSuccess = true;
|
|
7542
|
-
else logger$
|
|
7826
|
+
else logger$22.warn("Dev-story reported failure, proceeding to code review", {
|
|
7543
7827
|
storyKey,
|
|
7544
7828
|
error: devResult.error,
|
|
7545
7829
|
filesModified: devFilesModified.length
|
|
@@ -7567,7 +7851,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7567
7851
|
if (devStoryWasSuccess) {
|
|
7568
7852
|
gitDiffFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
|
|
7569
7853
|
if (gitDiffFiles.length === 0) {
|
|
7570
|
-
logger$
|
|
7854
|
+
logger$22.warn({ storyKey }, "Zero-diff detected after COMPLETE dev-story — no file changes in git working tree");
|
|
7571
7855
|
eventBus.emit("orchestrator:zero-diff-escalation", {
|
|
7572
7856
|
storyKey,
|
|
7573
7857
|
reason: "zero-diff-on-complete"
|
|
@@ -7598,7 +7882,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7598
7882
|
});
|
|
7599
7883
|
if (buildVerifyResult.status === "passed") {
|
|
7600
7884
|
eventBus.emit("story:build-verification-passed", { storyKey });
|
|
7601
|
-
logger$
|
|
7885
|
+
logger$22.info({ storyKey }, "Build verification passed");
|
|
7602
7886
|
} else if (buildVerifyResult.status === "failed" || buildVerifyResult.status === "timeout") {
|
|
7603
7887
|
const truncatedOutput = (buildVerifyResult.output ?? "").slice(0, 2e3);
|
|
7604
7888
|
const reason = buildVerifyResult.reason ?? "build-verification-failed";
|
|
@@ -7607,7 +7891,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7607
7891
|
exitCode: buildVerifyResult.exitCode ?? 1,
|
|
7608
7892
|
output: truncatedOutput
|
|
7609
7893
|
});
|
|
7610
|
-
logger$
|
|
7894
|
+
logger$22.warn({
|
|
7611
7895
|
storyKey,
|
|
7612
7896
|
reason,
|
|
7613
7897
|
exitCode: buildVerifyResult.exitCode
|
|
@@ -7637,7 +7921,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7637
7921
|
storyKey
|
|
7638
7922
|
});
|
|
7639
7923
|
if (icResult.potentiallyAffectedTests.length > 0) {
|
|
7640
|
-
logger$
|
|
7924
|
+
logger$22.warn({
|
|
7641
7925
|
storyKey,
|
|
7642
7926
|
modifiedInterfaces: icResult.modifiedInterfaces,
|
|
7643
7927
|
potentiallyAffectedTests: icResult.potentiallyAffectedTests
|
|
@@ -7682,7 +7966,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7682
7966
|
"NEEDS_MAJOR_REWORK": 2
|
|
7683
7967
|
};
|
|
7684
7968
|
for (const group of batchFileGroups) {
|
|
7685
|
-
logger$
|
|
7969
|
+
logger$22.info({
|
|
7686
7970
|
storyKey,
|
|
7687
7971
|
batchIndex: group.batchIndex,
|
|
7688
7972
|
fileCount: group.files.length
|
|
@@ -7693,7 +7977,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7693
7977
|
pack,
|
|
7694
7978
|
contextCompiler,
|
|
7695
7979
|
dispatcher,
|
|
7696
|
-
projectRoot
|
|
7980
|
+
projectRoot,
|
|
7981
|
+
tokenCeilings
|
|
7697
7982
|
}, {
|
|
7698
7983
|
storyKey,
|
|
7699
7984
|
storyFilePath: storyFilePath ?? "",
|
|
@@ -7719,7 +8004,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7719
8004
|
rawOutput: lastRawOutput,
|
|
7720
8005
|
tokenUsage: aggregateTokens
|
|
7721
8006
|
};
|
|
7722
|
-
logger$
|
|
8007
|
+
logger$22.info({
|
|
7723
8008
|
storyKey,
|
|
7724
8009
|
batchCount: batchFileGroups.length,
|
|
7725
8010
|
verdict: worstVerdict,
|
|
@@ -7732,7 +8017,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7732
8017
|
pack,
|
|
7733
8018
|
contextCompiler,
|
|
7734
8019
|
dispatcher,
|
|
7735
|
-
projectRoot
|
|
8020
|
+
projectRoot,
|
|
8021
|
+
tokenCeilings
|
|
7736
8022
|
}, {
|
|
7737
8023
|
storyKey,
|
|
7738
8024
|
storyFilePath: storyFilePath ?? "",
|
|
@@ -7745,7 +8031,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7745
8031
|
const isPhantomReview = reviewResult.dispatchFailed === true || reviewResult.verdict !== "SHIP_IT" && (reviewResult.issue_list === void 0 || reviewResult.issue_list.length === 0) && reviewResult.error !== void 0;
|
|
7746
8032
|
if (isPhantomReview && !timeoutRetried) {
|
|
7747
8033
|
timeoutRetried = true;
|
|
7748
|
-
logger$
|
|
8034
|
+
logger$22.warn({
|
|
7749
8035
|
storyKey,
|
|
7750
8036
|
reviewCycles,
|
|
7751
8037
|
error: reviewResult.error
|
|
@@ -7755,7 +8041,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7755
8041
|
verdict = reviewResult.verdict;
|
|
7756
8042
|
issueList = reviewResult.issue_list ?? [];
|
|
7757
8043
|
if (verdict === "NEEDS_MAJOR_REWORK" && reviewCycles > 0 && previousIssueList.length > 0 && issueList.length < previousIssueList.length) {
|
|
7758
|
-
logger$
|
|
8044
|
+
logger$22.info({
|
|
7759
8045
|
storyKey,
|
|
7760
8046
|
originalVerdict: verdict,
|
|
7761
8047
|
issuesBefore: previousIssueList.length,
|
|
@@ -7791,7 +8077,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7791
8077
|
if (_decomposition !== void 0) parts.push(`decomposed: ${_decomposition.batchCount} batches`);
|
|
7792
8078
|
parts.push(`${fileCount} files`);
|
|
7793
8079
|
parts.push(`${totalTokensK} tokens`);
|
|
7794
|
-
logger$
|
|
8080
|
+
logger$22.info({
|
|
7795
8081
|
storyKey,
|
|
7796
8082
|
verdict,
|
|
7797
8083
|
agentVerdict: reviewResult.agentVerdict
|
|
@@ -7834,7 +8120,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7834
8120
|
pack,
|
|
7835
8121
|
contextCompiler,
|
|
7836
8122
|
dispatcher,
|
|
7837
|
-
projectRoot
|
|
8123
|
+
projectRoot,
|
|
8124
|
+
tokenCeilings
|
|
7838
8125
|
}, {
|
|
7839
8126
|
storyKey,
|
|
7840
8127
|
storyFilePath: storyFilePath ?? "",
|
|
@@ -7842,7 +8129,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7842
8129
|
filesModified: devFilesModified,
|
|
7843
8130
|
workingDirectory: projectRoot
|
|
7844
8131
|
});
|
|
7845
|
-
logger$
|
|
8132
|
+
logger$22.debug({
|
|
7846
8133
|
storyKey,
|
|
7847
8134
|
expansion_priority: expansionResult.expansion_priority,
|
|
7848
8135
|
coverage_gaps: expansionResult.coverage_gaps.length
|
|
@@ -7855,7 +8142,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7855
8142
|
value: JSON.stringify(expansionResult)
|
|
7856
8143
|
});
|
|
7857
8144
|
} catch (expansionErr) {
|
|
7858
|
-
logger$
|
|
8145
|
+
logger$22.warn({
|
|
7859
8146
|
storyKey,
|
|
7860
8147
|
error: expansionErr instanceof Error ? expansionErr.message : String(expansionErr)
|
|
7861
8148
|
}, "Test expansion failed — story verdict unchanged");
|
|
@@ -7882,7 +8169,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7882
8169
|
persistState();
|
|
7883
8170
|
return;
|
|
7884
8171
|
}
|
|
7885
|
-
logger$
|
|
8172
|
+
logger$22.info({
|
|
7886
8173
|
storyKey,
|
|
7887
8174
|
reviewCycles: finalReviewCycles,
|
|
7888
8175
|
issueCount: issueList.length
|
|
@@ -7892,9 +8179,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
7892
8179
|
updateStory(storyKey, { phase: "NEEDS_FIXES" });
|
|
7893
8180
|
try {
|
|
7894
8181
|
let fixPrompt;
|
|
8182
|
+
let autoApproveMaxTurns;
|
|
7895
8183
|
try {
|
|
7896
8184
|
const fixTemplate = await pack.getPrompt("fix-story");
|
|
7897
8185
|
const storyContent = await readFile$1(storyFilePath ?? "", "utf-8");
|
|
8186
|
+
const complexity = computeStoryComplexity(storyContent);
|
|
8187
|
+
autoApproveMaxTurns = resolveFixStoryMaxTurns(complexity.complexityScore);
|
|
8188
|
+
logComplexityResult(storyKey, complexity, autoApproveMaxTurns);
|
|
7898
8189
|
let reviewFeedback;
|
|
7899
8190
|
if (issueList.length === 0) reviewFeedback = `Verdict: ${verdict}\nIssues: Minor issues flagged but no specifics provided. Review the story ACs and fix any remaining gaps.`;
|
|
7900
8191
|
else reviewFeedback = [
|
|
@@ -7911,6 +8202,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7911
8202
|
const constraints = decisions.filter((d) => d.category === "architecture");
|
|
7912
8203
|
archConstraints = constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
|
|
7913
8204
|
} catch {}
|
|
8205
|
+
const targetedFilesContent = buildTargetedFilesContent(issueList);
|
|
7914
8206
|
const sections = [
|
|
7915
8207
|
{
|
|
7916
8208
|
name: "story_content",
|
|
@@ -7926,19 +8218,25 @@ function createImplementationOrchestrator(deps) {
|
|
|
7926
8218
|
name: "arch_constraints",
|
|
7927
8219
|
content: archConstraints,
|
|
7928
8220
|
priority: "optional"
|
|
7929
|
-
}
|
|
8221
|
+
},
|
|
8222
|
+
...targetedFilesContent ? [{
|
|
8223
|
+
name: "targeted_files",
|
|
8224
|
+
content: targetedFilesContent,
|
|
8225
|
+
priority: "important"
|
|
8226
|
+
}] : []
|
|
7930
8227
|
];
|
|
7931
8228
|
const assembled = assemblePrompt(fixTemplate, sections, 24e3);
|
|
7932
8229
|
fixPrompt = assembled.prompt;
|
|
7933
8230
|
} catch {
|
|
7934
8231
|
fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, minor fixes needed`;
|
|
7935
|
-
logger$
|
|
8232
|
+
logger$22.warn("Failed to assemble auto-approve fix prompt, using fallback", { storyKey });
|
|
7936
8233
|
}
|
|
7937
8234
|
const handle = dispatcher.dispatch({
|
|
7938
8235
|
prompt: fixPrompt,
|
|
7939
8236
|
agent: "claude-code",
|
|
7940
8237
|
taskType: "minor-fixes",
|
|
7941
|
-
workingDirectory: projectRoot
|
|
8238
|
+
workingDirectory: projectRoot,
|
|
8239
|
+
...autoApproveMaxTurns !== void 0 ? { maxTurns: autoApproveMaxTurns } : {}
|
|
7942
8240
|
});
|
|
7943
8241
|
const fixResult = await handle.result;
|
|
7944
8242
|
eventBus.emit("orchestrator:story-phase-complete", {
|
|
@@ -7949,9 +8247,9 @@ function createImplementationOrchestrator(deps) {
|
|
|
7949
8247
|
output: fixResult.tokenEstimate.output
|
|
7950
8248
|
} : void 0 }
|
|
7951
8249
|
});
|
|
7952
|
-
if (fixResult.status === "timeout") logger$
|
|
8250
|
+
if (fixResult.status === "timeout") logger$22.warn("Auto-approve fix timed out — approving anyway (issues were minor)", { storyKey });
|
|
7953
8251
|
} catch (err) {
|
|
7954
|
-
logger$
|
|
8252
|
+
logger$22.warn("Auto-approve fix dispatch failed — approving anyway (issues were minor)", {
|
|
7955
8253
|
storyKey,
|
|
7956
8254
|
err
|
|
7957
8255
|
});
|
|
@@ -7981,9 +8279,15 @@ function createImplementationOrchestrator(deps) {
|
|
|
7981
8279
|
let fixPrompt;
|
|
7982
8280
|
const isMajorRework = taskType === "major-rework";
|
|
7983
8281
|
const templateName = isMajorRework ? "rework-story" : "fix-story";
|
|
8282
|
+
let fixMaxTurns;
|
|
7984
8283
|
try {
|
|
7985
8284
|
const fixTemplate = await pack.getPrompt(templateName);
|
|
7986
8285
|
const storyContent = await readFile$1(storyFilePath ?? "", "utf-8");
|
|
8286
|
+
{
|
|
8287
|
+
const complexity = computeStoryComplexity(storyContent);
|
|
8288
|
+
fixMaxTurns = resolveFixStoryMaxTurns(complexity.complexityScore);
|
|
8289
|
+
logComplexityResult(storyKey, complexity, fixMaxTurns);
|
|
8290
|
+
}
|
|
7987
8291
|
let reviewFeedback;
|
|
7988
8292
|
if (issueList.length === 0) reviewFeedback = isMajorRework ? [
|
|
7989
8293
|
`Verdict: ${verdict}`,
|
|
@@ -8033,28 +8337,36 @@ function createImplementationOrchestrator(deps) {
|
|
|
8033
8337
|
content: "",
|
|
8034
8338
|
priority: "optional"
|
|
8035
8339
|
}
|
|
8036
|
-
] :
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8340
|
+
] : (() => {
|
|
8341
|
+
const targetedFilesContent = buildTargetedFilesContent(issueList);
|
|
8342
|
+
return [
|
|
8343
|
+
{
|
|
8344
|
+
name: "story_content",
|
|
8345
|
+
content: storyContent,
|
|
8346
|
+
priority: "required"
|
|
8347
|
+
},
|
|
8348
|
+
{
|
|
8349
|
+
name: "review_feedback",
|
|
8350
|
+
content: reviewFeedback,
|
|
8351
|
+
priority: "required"
|
|
8352
|
+
},
|
|
8353
|
+
{
|
|
8354
|
+
name: "arch_constraints",
|
|
8355
|
+
content: archConstraints,
|
|
8356
|
+
priority: "optional"
|
|
8357
|
+
},
|
|
8358
|
+
...targetedFilesContent ? [{
|
|
8359
|
+
name: "targeted_files",
|
|
8360
|
+
content: targetedFilesContent,
|
|
8361
|
+
priority: "important"
|
|
8362
|
+
}] : []
|
|
8363
|
+
];
|
|
8364
|
+
})();
|
|
8053
8365
|
const assembled = assemblePrompt(fixTemplate, sections, 24e3);
|
|
8054
8366
|
fixPrompt = assembled.prompt;
|
|
8055
8367
|
} catch {
|
|
8056
8368
|
fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, taskType=${taskType}`;
|
|
8057
|
-
logger$
|
|
8369
|
+
logger$22.warn("Failed to assemble fix prompt, using fallback", {
|
|
8058
8370
|
storyKey,
|
|
8059
8371
|
taskType
|
|
8060
8372
|
});
|
|
@@ -8066,12 +8378,14 @@ function createImplementationOrchestrator(deps) {
|
|
|
8066
8378
|
taskType,
|
|
8067
8379
|
...fixModel !== void 0 ? { model: fixModel } : {},
|
|
8068
8380
|
outputSchema: DevStoryResultSchema,
|
|
8381
|
+
...fixMaxTurns !== void 0 ? { maxTurns: fixMaxTurns } : {},
|
|
8069
8382
|
...projectRoot !== void 0 ? { workingDirectory: projectRoot } : {}
|
|
8070
8383
|
}) : dispatcher.dispatch({
|
|
8071
8384
|
prompt: fixPrompt,
|
|
8072
8385
|
agent: "claude-code",
|
|
8073
8386
|
taskType,
|
|
8074
8387
|
...fixModel !== void 0 ? { model: fixModel } : {},
|
|
8388
|
+
...fixMaxTurns !== void 0 ? { maxTurns: fixMaxTurns } : {},
|
|
8075
8389
|
...projectRoot !== void 0 ? { workingDirectory: projectRoot } : {}
|
|
8076
8390
|
});
|
|
8077
8391
|
const fixResult = await handle.result;
|
|
@@ -8084,7 +8398,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8084
8398
|
} : void 0 }
|
|
8085
8399
|
});
|
|
8086
8400
|
if (fixResult.status === "timeout") {
|
|
8087
|
-
logger$
|
|
8401
|
+
logger$22.warn("Fix dispatch timed out — escalating story", {
|
|
8088
8402
|
storyKey,
|
|
8089
8403
|
taskType
|
|
8090
8404
|
});
|
|
@@ -8106,7 +8420,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8106
8420
|
}
|
|
8107
8421
|
if (fixResult.status === "failed") {
|
|
8108
8422
|
if (isMajorRework) {
|
|
8109
|
-
logger$
|
|
8423
|
+
logger$22.warn("Major rework dispatch failed — escalating story", {
|
|
8110
8424
|
storyKey,
|
|
8111
8425
|
exitCode: fixResult.exitCode
|
|
8112
8426
|
});
|
|
@@ -8126,14 +8440,14 @@ function createImplementationOrchestrator(deps) {
|
|
|
8126
8440
|
persistState();
|
|
8127
8441
|
return;
|
|
8128
8442
|
}
|
|
8129
|
-
logger$
|
|
8443
|
+
logger$22.warn("Fix dispatch failed", {
|
|
8130
8444
|
storyKey,
|
|
8131
8445
|
taskType,
|
|
8132
8446
|
exitCode: fixResult.exitCode
|
|
8133
8447
|
});
|
|
8134
8448
|
}
|
|
8135
8449
|
} catch (err) {
|
|
8136
|
-
logger$
|
|
8450
|
+
logger$22.warn("Fix dispatch failed, continuing to next review", {
|
|
8137
8451
|
storyKey,
|
|
8138
8452
|
taskType,
|
|
8139
8453
|
err
|
|
@@ -8196,11 +8510,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
8196
8510
|
}
|
|
8197
8511
|
async function run(storyKeys) {
|
|
8198
8512
|
if (_state === "RUNNING" || _state === "PAUSED") {
|
|
8199
|
-
logger$
|
|
8513
|
+
logger$22.warn("run() called while orchestrator is already running or paused — ignoring", { state: _state });
|
|
8200
8514
|
return getStatus();
|
|
8201
8515
|
}
|
|
8202
8516
|
if (_state === "COMPLETE") {
|
|
8203
|
-
logger$
|
|
8517
|
+
logger$22.warn("run() called on a COMPLETE orchestrator — ignoring", { state: _state });
|
|
8204
8518
|
return getStatus();
|
|
8205
8519
|
}
|
|
8206
8520
|
_state = "RUNNING";
|
|
@@ -8218,13 +8532,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
8218
8532
|
if (config.enableHeartbeat) startHeartbeat();
|
|
8219
8533
|
if (projectRoot !== void 0) {
|
|
8220
8534
|
const seedResult = seedMethodologyContext(db, projectRoot);
|
|
8221
|
-
if (seedResult.decisionsCreated > 0) logger$
|
|
8535
|
+
if (seedResult.decisionsCreated > 0) logger$22.info({
|
|
8222
8536
|
decisionsCreated: seedResult.decisionsCreated,
|
|
8223
8537
|
skippedCategories: seedResult.skippedCategories
|
|
8224
8538
|
}, "Methodology context seeded from planning artifacts");
|
|
8225
8539
|
}
|
|
8226
8540
|
const groups = detectConflictGroups(storyKeys, { moduleMap: pack.manifest.conflictGroups });
|
|
8227
|
-
logger$
|
|
8541
|
+
logger$22.info("Orchestrator starting", {
|
|
8228
8542
|
storyCount: storyKeys.length,
|
|
8229
8543
|
groupCount: groups.length,
|
|
8230
8544
|
maxConcurrency: config.maxConcurrency
|
|
@@ -8236,7 +8550,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8236
8550
|
_state = "FAILED";
|
|
8237
8551
|
_completedAt = new Date().toISOString();
|
|
8238
8552
|
persistState();
|
|
8239
|
-
logger$
|
|
8553
|
+
logger$22.error("Orchestrator failed with unhandled error", { err });
|
|
8240
8554
|
return getStatus();
|
|
8241
8555
|
}
|
|
8242
8556
|
stopHeartbeat();
|
|
@@ -8263,7 +8577,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8263
8577
|
_pauseGate = createPauseGate();
|
|
8264
8578
|
_state = "PAUSED";
|
|
8265
8579
|
eventBus.emit("orchestrator:paused", {});
|
|
8266
|
-
logger$
|
|
8580
|
+
logger$22.info("Orchestrator paused");
|
|
8267
8581
|
}
|
|
8268
8582
|
function resume() {
|
|
8269
8583
|
if (_state !== "PAUSED") return;
|
|
@@ -8274,7 +8588,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8274
8588
|
}
|
|
8275
8589
|
_state = "RUNNING";
|
|
8276
8590
|
eventBus.emit("orchestrator:resumed", {});
|
|
8277
|
-
logger$
|
|
8591
|
+
logger$22.info("Orchestrator resumed");
|
|
8278
8592
|
}
|
|
8279
8593
|
return {
|
|
8280
8594
|
run,
|
|
@@ -13228,4 +13542,4 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
13228
13542
|
|
|
13229
13543
|
//#endregion
|
|
13230
13544
|
export { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, parseDbTimestampAsUtc, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
|
|
13231
|
-
//# sourceMappingURL=run-
|
|
13545
|
+
//# sourceMappingURL=run-IeDyGCak.js.map
|