substrate-ai 0.2.26 → 0.2.28
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 +53 -7
- package/dist/index.d.ts +69 -1
- package/dist/{run-CEtHPG4I.js → run-BCyrbL3w.js} +764 -247
- package/dist/{run-BXKRGSeL.js → run-CIgIYtKf.js} +1 -1
- 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
|
@@ -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.
|
|
@@ -2277,6 +2277,75 @@ const PIPELINE_EVENT_METADATA = [
|
|
|
2277
2277
|
type: "string",
|
|
2278
2278
|
description: "Story key."
|
|
2279
2279
|
}]
|
|
2280
|
+
},
|
|
2281
|
+
{
|
|
2282
|
+
type: "story:interface-change-warning",
|
|
2283
|
+
description: "Non-blocking warning: modified files export shared TypeScript interfaces that may be referenced by test files outside the same module (potential stale mock risk). Story proceeds to code-review.",
|
|
2284
|
+
when: "After build verification passes, before code-review, when exported interfaces in modified .ts files are referenced by cross-module test files.",
|
|
2285
|
+
fields: [
|
|
2286
|
+
{
|
|
2287
|
+
name: "ts",
|
|
2288
|
+
type: "string",
|
|
2289
|
+
description: "Timestamp."
|
|
2290
|
+
},
|
|
2291
|
+
{
|
|
2292
|
+
name: "storyKey",
|
|
2293
|
+
type: "string",
|
|
2294
|
+
description: "Story key."
|
|
2295
|
+
},
|
|
2296
|
+
{
|
|
2297
|
+
name: "modifiedInterfaces",
|
|
2298
|
+
type: "string[]",
|
|
2299
|
+
description: "Exported interface/type names found in modified files."
|
|
2300
|
+
},
|
|
2301
|
+
{
|
|
2302
|
+
name: "potentiallyAffectedTests",
|
|
2303
|
+
type: "string[]",
|
|
2304
|
+
description: "Test file paths (relative to project root) that reference the modified interface names."
|
|
2305
|
+
}
|
|
2306
|
+
]
|
|
2307
|
+
},
|
|
2308
|
+
{
|
|
2309
|
+
type: "story:metrics",
|
|
2310
|
+
description: "Per-story metrics on terminal state.",
|
|
2311
|
+
when: "After terminal state (success/escalation/failure).",
|
|
2312
|
+
fields: [
|
|
2313
|
+
{
|
|
2314
|
+
name: "ts",
|
|
2315
|
+
type: "string",
|
|
2316
|
+
description: "Timestamp."
|
|
2317
|
+
},
|
|
2318
|
+
{
|
|
2319
|
+
name: "storyKey",
|
|
2320
|
+
type: "string",
|
|
2321
|
+
description: "Story key."
|
|
2322
|
+
},
|
|
2323
|
+
{
|
|
2324
|
+
name: "wallClockMs",
|
|
2325
|
+
type: "number",
|
|
2326
|
+
description: "Wall-clock ms."
|
|
2327
|
+
},
|
|
2328
|
+
{
|
|
2329
|
+
name: "phaseBreakdown",
|
|
2330
|
+
type: "Record<string,number>",
|
|
2331
|
+
description: "Phase→ms durations."
|
|
2332
|
+
},
|
|
2333
|
+
{
|
|
2334
|
+
name: "tokens",
|
|
2335
|
+
type: "{input:number;output:number}",
|
|
2336
|
+
description: "Token counts."
|
|
2337
|
+
},
|
|
2338
|
+
{
|
|
2339
|
+
name: "reviewCycles",
|
|
2340
|
+
type: "number",
|
|
2341
|
+
description: "Review cycle count."
|
|
2342
|
+
},
|
|
2343
|
+
{
|
|
2344
|
+
name: "dispatches",
|
|
2345
|
+
type: "number",
|
|
2346
|
+
description: "Dispatch count."
|
|
2347
|
+
}
|
|
2348
|
+
]
|
|
2280
2349
|
}
|
|
2281
2350
|
];
|
|
2282
2351
|
/**
|
|
@@ -2606,7 +2675,7 @@ function truncateToTokens(text, maxTokens) {
|
|
|
2606
2675
|
|
|
2607
2676
|
//#endregion
|
|
2608
2677
|
//#region src/modules/context-compiler/context-compiler-impl.ts
|
|
2609
|
-
const logger$
|
|
2678
|
+
const logger$19 = createLogger("context-compiler");
|
|
2610
2679
|
/**
|
|
2611
2680
|
* Fraction of the original token budget that must remain (after required +
|
|
2612
2681
|
* important sections) before an optional section is included.
|
|
@@ -2698,7 +2767,7 @@ var ContextCompilerImpl = class {
|
|
|
2698
2767
|
includedParts.push(truncated);
|
|
2699
2768
|
remainingBudget -= truncatedTokens;
|
|
2700
2769
|
anyTruncated = true;
|
|
2701
|
-
logger$
|
|
2770
|
+
logger$19.warn({
|
|
2702
2771
|
section: section.name,
|
|
2703
2772
|
originalTokens: tokens,
|
|
2704
2773
|
budgetTokens: truncatedTokens
|
|
@@ -2712,7 +2781,7 @@ var ContextCompilerImpl = class {
|
|
|
2712
2781
|
});
|
|
2713
2782
|
} else {
|
|
2714
2783
|
anyTruncated = true;
|
|
2715
|
-
logger$
|
|
2784
|
+
logger$19.warn({
|
|
2716
2785
|
section: section.name,
|
|
2717
2786
|
tokens
|
|
2718
2787
|
}, "Context compiler: omitted \"important\" section — no budget remaining");
|
|
@@ -2739,7 +2808,7 @@ var ContextCompilerImpl = class {
|
|
|
2739
2808
|
} else {
|
|
2740
2809
|
if (tokens > 0) {
|
|
2741
2810
|
anyTruncated = true;
|
|
2742
|
-
logger$
|
|
2811
|
+
logger$19.warn({
|
|
2743
2812
|
section: section.name,
|
|
2744
2813
|
tokens,
|
|
2745
2814
|
budgetFractionRemaining: budgetFractionRemaining.toFixed(2)
|
|
@@ -3024,7 +3093,7 @@ function parseYamlResult(yamlText, schema) {
|
|
|
3024
3093
|
|
|
3025
3094
|
//#endregion
|
|
3026
3095
|
//#region src/modules/agent-dispatch/dispatcher-impl.ts
|
|
3027
|
-
const logger$
|
|
3096
|
+
const logger$18 = createLogger("agent-dispatch");
|
|
3028
3097
|
const SHUTDOWN_GRACE_MS = 1e4;
|
|
3029
3098
|
const SHUTDOWN_MAX_WAIT_MS = 3e4;
|
|
3030
3099
|
const CHARS_PER_TOKEN = 4;
|
|
@@ -3069,7 +3138,7 @@ function getAvailableMemory() {
|
|
|
3069
3138
|
}).trim(), 10);
|
|
3070
3139
|
_lastKnownPressureLevel = pressureLevel;
|
|
3071
3140
|
if (pressureLevel >= 4) {
|
|
3072
|
-
logger$
|
|
3141
|
+
logger$18.warn({ pressureLevel }, "macOS kernel reports critical memory pressure");
|
|
3073
3142
|
return 0;
|
|
3074
3143
|
}
|
|
3075
3144
|
} catch {}
|
|
@@ -3084,7 +3153,7 @@ function getAvailableMemory() {
|
|
|
3084
3153
|
const speculative = parseInt(vmstat.match(/Pages speculative:\s+(\d+)/)?.[1] ?? "0", 10);
|
|
3085
3154
|
const available = (free + purgeable + speculative) * pageSize;
|
|
3086
3155
|
if (pressureLevel >= 2) {
|
|
3087
|
-
logger$
|
|
3156
|
+
logger$18.warn({
|
|
3088
3157
|
pressureLevel,
|
|
3089
3158
|
availableBeforeDiscount: available
|
|
3090
3159
|
}, "macOS kernel reports memory pressure — discounting estimate");
|
|
@@ -3164,7 +3233,7 @@ var DispatcherImpl = class {
|
|
|
3164
3233
|
resolve: typedResolve,
|
|
3165
3234
|
reject
|
|
3166
3235
|
});
|
|
3167
|
-
logger$
|
|
3236
|
+
logger$18.debug({
|
|
3168
3237
|
id,
|
|
3169
3238
|
queueLength: this._queue.length
|
|
3170
3239
|
}, "Dispatch queued");
|
|
@@ -3195,7 +3264,7 @@ var DispatcherImpl = class {
|
|
|
3195
3264
|
async shutdown() {
|
|
3196
3265
|
this._shuttingDown = true;
|
|
3197
3266
|
this._stopMemoryPressureTimer();
|
|
3198
|
-
logger$
|
|
3267
|
+
logger$18.info({
|
|
3199
3268
|
running: this._running.size,
|
|
3200
3269
|
queued: this._queue.length
|
|
3201
3270
|
}, "Dispatcher shutting down");
|
|
@@ -3228,13 +3297,13 @@ var DispatcherImpl = class {
|
|
|
3228
3297
|
}
|
|
3229
3298
|
}, 50);
|
|
3230
3299
|
});
|
|
3231
|
-
logger$
|
|
3300
|
+
logger$18.info("Dispatcher shutdown complete");
|
|
3232
3301
|
}
|
|
3233
3302
|
async _startDispatch(id, request, resolve$2) {
|
|
3234
3303
|
const { prompt, agent, taskType, timeout, outputSchema, workingDirectory, model, maxTurns } = request;
|
|
3235
3304
|
const adapter = this._adapterRegistry.get(agent);
|
|
3236
3305
|
if (adapter === void 0) {
|
|
3237
|
-
logger$
|
|
3306
|
+
logger$18.warn({
|
|
3238
3307
|
id,
|
|
3239
3308
|
agent
|
|
3240
3309
|
}, "No adapter found for agent");
|
|
@@ -3280,7 +3349,7 @@ var DispatcherImpl = class {
|
|
|
3280
3349
|
});
|
|
3281
3350
|
const startedAt = Date.now();
|
|
3282
3351
|
proc.on("error", (err) => {
|
|
3283
|
-
logger$
|
|
3352
|
+
logger$18.error({
|
|
3284
3353
|
id,
|
|
3285
3354
|
binary: cmd.binary,
|
|
3286
3355
|
error: err.message
|
|
@@ -3288,7 +3357,7 @@ var DispatcherImpl = class {
|
|
|
3288
3357
|
});
|
|
3289
3358
|
if (proc.stdin !== null) {
|
|
3290
3359
|
proc.stdin.on("error", (err) => {
|
|
3291
|
-
if (err.code !== "EPIPE") logger$
|
|
3360
|
+
if (err.code !== "EPIPE") logger$18.warn({
|
|
3292
3361
|
id,
|
|
3293
3362
|
error: err.message
|
|
3294
3363
|
}, "stdin write error");
|
|
@@ -3330,7 +3399,7 @@ var DispatcherImpl = class {
|
|
|
3330
3399
|
agent,
|
|
3331
3400
|
taskType
|
|
3332
3401
|
});
|
|
3333
|
-
logger$
|
|
3402
|
+
logger$18.debug({
|
|
3334
3403
|
id,
|
|
3335
3404
|
agent,
|
|
3336
3405
|
taskType,
|
|
@@ -3347,7 +3416,7 @@ var DispatcherImpl = class {
|
|
|
3347
3416
|
dispatchId: id,
|
|
3348
3417
|
timeoutMs
|
|
3349
3418
|
});
|
|
3350
|
-
logger$
|
|
3419
|
+
logger$18.warn({
|
|
3351
3420
|
id,
|
|
3352
3421
|
agent,
|
|
3353
3422
|
taskType,
|
|
@@ -3401,7 +3470,7 @@ var DispatcherImpl = class {
|
|
|
3401
3470
|
exitCode: code,
|
|
3402
3471
|
output: stdout
|
|
3403
3472
|
});
|
|
3404
|
-
logger$
|
|
3473
|
+
logger$18.debug({
|
|
3405
3474
|
id,
|
|
3406
3475
|
agent,
|
|
3407
3476
|
taskType,
|
|
@@ -3427,7 +3496,7 @@ var DispatcherImpl = class {
|
|
|
3427
3496
|
error: stderr || `Process exited with code ${String(code)}`,
|
|
3428
3497
|
exitCode: code
|
|
3429
3498
|
});
|
|
3430
|
-
logger$
|
|
3499
|
+
logger$18.debug({
|
|
3431
3500
|
id,
|
|
3432
3501
|
agent,
|
|
3433
3502
|
taskType,
|
|
@@ -3486,7 +3555,7 @@ var DispatcherImpl = class {
|
|
|
3486
3555
|
const next = this._queue.shift();
|
|
3487
3556
|
if (next === void 0) return;
|
|
3488
3557
|
next.handle.status = "running";
|
|
3489
|
-
logger$
|
|
3558
|
+
logger$18.debug({
|
|
3490
3559
|
id: next.id,
|
|
3491
3560
|
queueLength: this._queue.length
|
|
3492
3561
|
}, "Dequeued dispatch");
|
|
@@ -3499,7 +3568,7 @@ var DispatcherImpl = class {
|
|
|
3499
3568
|
_isMemoryPressured() {
|
|
3500
3569
|
const free = getAvailableMemory();
|
|
3501
3570
|
if (free < MIN_FREE_MEMORY_BYTES) {
|
|
3502
|
-
logger$
|
|
3571
|
+
logger$18.warn({
|
|
3503
3572
|
freeMB: Math.round(free / 1024 / 1024),
|
|
3504
3573
|
thresholdMB: Math.round(MIN_FREE_MEMORY_BYTES / 1024 / 1024),
|
|
3505
3574
|
pressureLevel: _lastKnownPressureLevel
|
|
@@ -3539,6 +3608,54 @@ var DispatcherImpl = class {
|
|
|
3539
3608
|
};
|
|
3540
3609
|
/** Default command for the build verification gate */
|
|
3541
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
|
+
}
|
|
3542
3659
|
/** Default timeout in milliseconds for the build verification gate */
|
|
3543
3660
|
const DEFAULT_VERIFY_TIMEOUT_MS = 6e4;
|
|
3544
3661
|
/**
|
|
@@ -3555,7 +3672,16 @@ const DEFAULT_VERIFY_TIMEOUT_MS = 6e4;
|
|
|
3555
3672
|
*/
|
|
3556
3673
|
function runBuildVerification(options) {
|
|
3557
3674
|
const { verifyCommand, verifyTimeoutMs, projectRoot } = options;
|
|
3558
|
-
|
|
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;
|
|
3559
3685
|
if (!cmd) return { status: "skipped" };
|
|
3560
3686
|
const timeoutMs = verifyTimeoutMs ?? DEFAULT_VERIFY_TIMEOUT_MS;
|
|
3561
3687
|
try {
|
|
@@ -3748,7 +3874,7 @@ function pickRecommendation(distribution, profile, totalIssues, reviewCycles, la
|
|
|
3748
3874
|
|
|
3749
3875
|
//#endregion
|
|
3750
3876
|
//#region src/modules/compiled-workflows/prompt-assembler.ts
|
|
3751
|
-
const logger$
|
|
3877
|
+
const logger$17 = createLogger("compiled-workflows:prompt-assembler");
|
|
3752
3878
|
/**
|
|
3753
3879
|
* Assemble a final prompt from a template and sections map.
|
|
3754
3880
|
*
|
|
@@ -3773,7 +3899,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
3773
3899
|
tokenCount,
|
|
3774
3900
|
truncated: false
|
|
3775
3901
|
};
|
|
3776
|
-
logger$
|
|
3902
|
+
logger$17.warn({
|
|
3777
3903
|
tokenCount,
|
|
3778
3904
|
ceiling: tokenCeiling
|
|
3779
3905
|
}, "Prompt exceeds token ceiling — truncating optional sections");
|
|
@@ -3789,10 +3915,10 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
3789
3915
|
const targetSectionTokens = Math.max(0, currentSectionTokens - overBy);
|
|
3790
3916
|
if (targetSectionTokens === 0) {
|
|
3791
3917
|
contentMap[section.name] = "";
|
|
3792
|
-
logger$
|
|
3918
|
+
logger$17.warn({ sectionName: section.name }, "Section eliminated to fit token budget");
|
|
3793
3919
|
} else {
|
|
3794
3920
|
contentMap[section.name] = truncateToTokens(section.content, targetSectionTokens);
|
|
3795
|
-
logger$
|
|
3921
|
+
logger$17.warn({
|
|
3796
3922
|
sectionName: section.name,
|
|
3797
3923
|
targetSectionTokens
|
|
3798
3924
|
}, "Section truncated to fit token budget");
|
|
@@ -3803,7 +3929,7 @@ function assemblePrompt(template, sections, tokenCeiling = 2200) {
|
|
|
3803
3929
|
}
|
|
3804
3930
|
if (tokenCount <= tokenCeiling) break;
|
|
3805
3931
|
}
|
|
3806
|
-
if (tokenCount > tokenCeiling) logger$
|
|
3932
|
+
if (tokenCount > tokenCeiling) logger$17.warn({
|
|
3807
3933
|
tokenCount,
|
|
3808
3934
|
ceiling: tokenCeiling
|
|
3809
3935
|
}, "Required sections alone exceed token ceiling — returning over-budget prompt");
|
|
@@ -3890,6 +4016,20 @@ const CodeReviewIssueSchema = z.object({
|
|
|
3890
4016
|
line: coerceOptionalNumber
|
|
3891
4017
|
});
|
|
3892
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
|
+
/**
|
|
3893
4033
|
* Compute the verdict from the issue list using deterministic rules.
|
|
3894
4034
|
*
|
|
3895
4035
|
* The agent reports issues with severities; the pipeline computes the
|
|
@@ -3911,7 +4051,8 @@ function computeVerdict(issueList) {
|
|
|
3911
4051
|
/**
|
|
3912
4052
|
* Schema for the YAML output contract of the code-review sub-agent.
|
|
3913
4053
|
*
|
|
3914
|
-
* 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.
|
|
3915
4056
|
* Example:
|
|
3916
4057
|
* verdict: NEEDS_MINOR_FIXES
|
|
3917
4058
|
* issues: 3
|
|
@@ -3920,11 +4061,20 @@ function computeVerdict(issueList) {
|
|
|
3920
4061
|
* description: "Missing error handling in createFoo()"
|
|
3921
4062
|
* file: "src/modules/foo/foo.ts"
|
|
3922
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()"
|
|
3923
4071
|
* notes: "Generally clean implementation."
|
|
3924
4072
|
*
|
|
3925
|
-
* The transform
|
|
3926
|
-
*
|
|
3927
|
-
*
|
|
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.
|
|
3928
4078
|
*/
|
|
3929
4079
|
const CodeReviewResultSchema = z.object({
|
|
3930
4080
|
verdict: z.enum([
|
|
@@ -3934,13 +4084,27 @@ const CodeReviewResultSchema = z.object({
|
|
|
3934
4084
|
]),
|
|
3935
4085
|
issues: coerceNumber,
|
|
3936
4086
|
issue_list: z.array(CodeReviewIssueSchema),
|
|
4087
|
+
ac_checklist: z.array(AcChecklistEntrySchema).default([]),
|
|
3937
4088
|
notes: z.string().optional()
|
|
3938
|
-
}).transform((data) =>
|
|
3939
|
-
...data
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
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
|
+
});
|
|
3944
4108
|
/**
|
|
3945
4109
|
* Schema for the YAML output contract of the test-plan sub-agent.
|
|
3946
4110
|
*
|
|
@@ -4018,12 +4182,46 @@ const TestExpansionResultSchema = z.object({
|
|
|
4018
4182
|
});
|
|
4019
4183
|
|
|
4020
4184
|
//#endregion
|
|
4021
|
-
//#region src/modules/compiled-workflows/
|
|
4022
|
-
|
|
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
|
+
};
|
|
4023
4197
|
/**
|
|
4024
|
-
*
|
|
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' }`
|
|
4025
4206
|
*/
|
|
4026
|
-
|
|
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");
|
|
4027
4225
|
/**
|
|
4028
4226
|
* Execute the compiled create-story workflow.
|
|
4029
4227
|
*
|
|
@@ -4043,17 +4241,23 @@ const TOKEN_CEILING$4 = 3e3;
|
|
|
4043
4241
|
*/
|
|
4044
4242
|
async function runCreateStory(deps, params) {
|
|
4045
4243
|
const { epicId, storyKey, pipelineRunId } = params;
|
|
4046
|
-
logger$
|
|
4244
|
+
logger$16.debug({
|
|
4047
4245
|
epicId,
|
|
4048
4246
|
storyKey,
|
|
4049
4247
|
pipelineRunId
|
|
4050
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");
|
|
4051
4255
|
let template;
|
|
4052
4256
|
try {
|
|
4053
4257
|
template = await deps.pack.getPrompt("create-story");
|
|
4054
4258
|
} catch (err) {
|
|
4055
4259
|
const error = err instanceof Error ? err.message : String(err);
|
|
4056
|
-
logger$
|
|
4260
|
+
logger$16.error({ error }, "Failed to retrieve create-story prompt template");
|
|
4057
4261
|
return {
|
|
4058
4262
|
result: "failed",
|
|
4059
4263
|
error: `Failed to retrieve prompt template: ${error}`,
|
|
@@ -4094,11 +4298,11 @@ async function runCreateStory(deps, params) {
|
|
|
4094
4298
|
content: storyTemplateContent,
|
|
4095
4299
|
priority: "important"
|
|
4096
4300
|
}
|
|
4097
|
-
], TOKEN_CEILING
|
|
4098
|
-
logger$
|
|
4301
|
+
], TOKEN_CEILING);
|
|
4302
|
+
logger$16.debug({
|
|
4099
4303
|
tokenCount,
|
|
4100
4304
|
truncated,
|
|
4101
|
-
tokenCeiling: TOKEN_CEILING
|
|
4305
|
+
tokenCeiling: TOKEN_CEILING
|
|
4102
4306
|
}, "Prompt assembled for create-story");
|
|
4103
4307
|
const handle = deps.dispatcher.dispatch({
|
|
4104
4308
|
prompt,
|
|
@@ -4112,7 +4316,7 @@ async function runCreateStory(deps, params) {
|
|
|
4112
4316
|
dispatchResult = await handle.result;
|
|
4113
4317
|
} catch (err) {
|
|
4114
4318
|
const error = err instanceof Error ? err.message : String(err);
|
|
4115
|
-
logger$
|
|
4319
|
+
logger$16.error({
|
|
4116
4320
|
epicId,
|
|
4117
4321
|
storyKey,
|
|
4118
4322
|
error
|
|
@@ -4133,7 +4337,7 @@ async function runCreateStory(deps, params) {
|
|
|
4133
4337
|
if (dispatchResult.status === "failed") {
|
|
4134
4338
|
const errorMsg = dispatchResult.parseError ?? `Dispatch failed with exit code ${dispatchResult.exitCode}`;
|
|
4135
4339
|
const stderrDetail = dispatchResult.output ? ` Output: ${dispatchResult.output}` : "";
|
|
4136
|
-
logger$
|
|
4340
|
+
logger$16.warn({
|
|
4137
4341
|
epicId,
|
|
4138
4342
|
storyKey,
|
|
4139
4343
|
exitCode: dispatchResult.exitCode
|
|
@@ -4145,7 +4349,7 @@ async function runCreateStory(deps, params) {
|
|
|
4145
4349
|
};
|
|
4146
4350
|
}
|
|
4147
4351
|
if (dispatchResult.status === "timeout") {
|
|
4148
|
-
logger$
|
|
4352
|
+
logger$16.warn({
|
|
4149
4353
|
epicId,
|
|
4150
4354
|
storyKey
|
|
4151
4355
|
}, "Create-story dispatch timed out");
|
|
@@ -4158,7 +4362,7 @@ async function runCreateStory(deps, params) {
|
|
|
4158
4362
|
if (dispatchResult.parsed === null) {
|
|
4159
4363
|
const details = dispatchResult.parseError ?? "No YAML block found in output";
|
|
4160
4364
|
const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
|
|
4161
|
-
logger$
|
|
4365
|
+
logger$16.warn({
|
|
4162
4366
|
epicId,
|
|
4163
4367
|
storyKey,
|
|
4164
4368
|
details,
|
|
@@ -4174,7 +4378,7 @@ async function runCreateStory(deps, params) {
|
|
|
4174
4378
|
const parseResult = CreateStoryResultSchema.safeParse(dispatchResult.parsed);
|
|
4175
4379
|
if (!parseResult.success) {
|
|
4176
4380
|
const details = parseResult.error.message;
|
|
4177
|
-
logger$
|
|
4381
|
+
logger$16.warn({
|
|
4178
4382
|
epicId,
|
|
4179
4383
|
storyKey,
|
|
4180
4384
|
details
|
|
@@ -4187,7 +4391,7 @@ async function runCreateStory(deps, params) {
|
|
|
4187
4391
|
};
|
|
4188
4392
|
}
|
|
4189
4393
|
const parsed = parseResult.data;
|
|
4190
|
-
logger$
|
|
4394
|
+
logger$16.info({
|
|
4191
4395
|
epicId,
|
|
4192
4396
|
storyKey,
|
|
4193
4397
|
storyFile: parsed.story_file,
|
|
@@ -4209,7 +4413,7 @@ function getImplementationDecisions(deps) {
|
|
|
4209
4413
|
try {
|
|
4210
4414
|
return getDecisionsByPhase(deps.db, "implementation");
|
|
4211
4415
|
} catch (err) {
|
|
4212
|
-
logger$
|
|
4416
|
+
logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve implementation decisions");
|
|
4213
4417
|
return [];
|
|
4214
4418
|
}
|
|
4215
4419
|
}
|
|
@@ -4252,13 +4456,13 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
|
4252
4456
|
if (storyKey) {
|
|
4253
4457
|
const storySection = extractStorySection(shardContent, storyKey);
|
|
4254
4458
|
if (storySection) {
|
|
4255
|
-
logger$
|
|
4459
|
+
logger$16.debug({
|
|
4256
4460
|
epicId,
|
|
4257
4461
|
storyKey
|
|
4258
4462
|
}, "Extracted per-story section from epic shard");
|
|
4259
4463
|
return storySection;
|
|
4260
4464
|
}
|
|
4261
|
-
logger$
|
|
4465
|
+
logger$16.debug({
|
|
4262
4466
|
epicId,
|
|
4263
4467
|
storyKey
|
|
4264
4468
|
}, "No matching story section found — using full epic shard");
|
|
@@ -4268,11 +4472,11 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
|
4268
4472
|
if (projectRoot) {
|
|
4269
4473
|
const fallback = readEpicShardFromFile(projectRoot, epicId);
|
|
4270
4474
|
if (fallback) {
|
|
4271
|
-
logger$
|
|
4475
|
+
logger$16.info({ epicId }, "Using file-based fallback for epic shard (decisions table empty)");
|
|
4272
4476
|
if (storyKey) {
|
|
4273
4477
|
const storySection = extractStorySection(fallback, storyKey);
|
|
4274
4478
|
if (storySection) {
|
|
4275
|
-
logger$
|
|
4479
|
+
logger$16.debug({
|
|
4276
4480
|
epicId,
|
|
4277
4481
|
storyKey
|
|
4278
4482
|
}, "Extracted per-story section from file-based epic shard");
|
|
@@ -4284,7 +4488,7 @@ function getEpicShard(decisions, epicId, projectRoot, storyKey) {
|
|
|
4284
4488
|
}
|
|
4285
4489
|
return "";
|
|
4286
4490
|
} catch (err) {
|
|
4287
|
-
logger$
|
|
4491
|
+
logger$16.warn({
|
|
4288
4492
|
epicId,
|
|
4289
4493
|
error: err instanceof Error ? err.message : String(err)
|
|
4290
4494
|
}, "Failed to retrieve epic shard");
|
|
@@ -4301,7 +4505,7 @@ function getPrevDevNotes(decisions, epicId) {
|
|
|
4301
4505
|
if (devNotes.length === 0) return "";
|
|
4302
4506
|
return devNotes[devNotes.length - 1].value;
|
|
4303
4507
|
} catch (err) {
|
|
4304
|
-
logger$
|
|
4508
|
+
logger$16.warn({
|
|
4305
4509
|
epicId,
|
|
4306
4510
|
error: err instanceof Error ? err.message : String(err)
|
|
4307
4511
|
}, "Failed to retrieve prev dev notes");
|
|
@@ -4321,13 +4525,13 @@ function getArchConstraints$2(deps) {
|
|
|
4321
4525
|
if (deps.projectRoot) {
|
|
4322
4526
|
const fallback = readArchConstraintsFromFile(deps.projectRoot);
|
|
4323
4527
|
if (fallback) {
|
|
4324
|
-
logger$
|
|
4528
|
+
logger$16.info("Using file-based fallback for architecture constraints (decisions table empty)");
|
|
4325
4529
|
return fallback;
|
|
4326
4530
|
}
|
|
4327
4531
|
}
|
|
4328
4532
|
return "";
|
|
4329
4533
|
} catch (err) {
|
|
4330
|
-
logger$
|
|
4534
|
+
logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
|
|
4331
4535
|
return "";
|
|
4332
4536
|
}
|
|
4333
4537
|
}
|
|
@@ -4347,7 +4551,7 @@ function readEpicShardFromFile(projectRoot, epicId) {
|
|
|
4347
4551
|
const match = pattern.exec(content);
|
|
4348
4552
|
return match ? match[0].trim() : "";
|
|
4349
4553
|
} catch (err) {
|
|
4350
|
-
logger$
|
|
4554
|
+
logger$16.warn({
|
|
4351
4555
|
epicId,
|
|
4352
4556
|
error: err instanceof Error ? err.message : String(err)
|
|
4353
4557
|
}, "File-based epic shard fallback failed");
|
|
@@ -4370,7 +4574,7 @@ function readArchConstraintsFromFile(projectRoot) {
|
|
|
4370
4574
|
const content = readFileSync$1(archPath, "utf-8");
|
|
4371
4575
|
return content.slice(0, 1500);
|
|
4372
4576
|
} catch (err) {
|
|
4373
|
-
logger$
|
|
4577
|
+
logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "File-based architecture fallback failed");
|
|
4374
4578
|
return "";
|
|
4375
4579
|
}
|
|
4376
4580
|
}
|
|
@@ -4383,7 +4587,7 @@ async function getStoryTemplate(deps) {
|
|
|
4383
4587
|
try {
|
|
4384
4588
|
return await deps.pack.getTemplate("story");
|
|
4385
4589
|
} catch (err) {
|
|
4386
|
-
logger$
|
|
4590
|
+
logger$16.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve story template from pack");
|
|
4387
4591
|
return "";
|
|
4388
4592
|
}
|
|
4389
4593
|
}
|
|
@@ -4420,7 +4624,7 @@ async function isValidStoryFile(filePath) {
|
|
|
4420
4624
|
|
|
4421
4625
|
//#endregion
|
|
4422
4626
|
//#region src/modules/compiled-workflows/git-helpers.ts
|
|
4423
|
-
const logger$
|
|
4627
|
+
const logger$15 = createLogger("compiled-workflows:git-helpers");
|
|
4424
4628
|
/**
|
|
4425
4629
|
* Capture the full git diff for HEAD (working tree vs current commit).
|
|
4426
4630
|
*
|
|
@@ -4516,7 +4720,7 @@ async function stageIntentToAdd(files, workingDirectory) {
|
|
|
4516
4720
|
if (files.length === 0) return;
|
|
4517
4721
|
const existing = files.filter((f) => {
|
|
4518
4722
|
const exists = existsSync$1(f);
|
|
4519
|
-
if (!exists) logger$
|
|
4723
|
+
if (!exists) logger$15.debug({ file: f }, "Skipping nonexistent file in stageIntentToAdd");
|
|
4520
4724
|
return exists;
|
|
4521
4725
|
});
|
|
4522
4726
|
if (existing.length === 0) return;
|
|
@@ -4550,7 +4754,7 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
4550
4754
|
stderr += chunk.toString("utf-8");
|
|
4551
4755
|
});
|
|
4552
4756
|
proc.on("error", (err) => {
|
|
4553
|
-
logger$
|
|
4757
|
+
logger$15.warn({
|
|
4554
4758
|
label: logLabel,
|
|
4555
4759
|
cwd,
|
|
4556
4760
|
error: err.message
|
|
@@ -4559,7 +4763,7 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
4559
4763
|
});
|
|
4560
4764
|
proc.on("close", (code) => {
|
|
4561
4765
|
if (code !== 0) {
|
|
4562
|
-
logger$
|
|
4766
|
+
logger$15.warn({
|
|
4563
4767
|
label: logLabel,
|
|
4564
4768
|
cwd,
|
|
4565
4769
|
code,
|
|
@@ -4575,7 +4779,7 @@ async function runGitCommand(args, cwd, logLabel) {
|
|
|
4575
4779
|
|
|
4576
4780
|
//#endregion
|
|
4577
4781
|
//#region src/modules/implementation-orchestrator/project-findings.ts
|
|
4578
|
-
const logger$
|
|
4782
|
+
const logger$14 = createLogger("project-findings");
|
|
4579
4783
|
/** Maximum character length for the findings summary */
|
|
4580
4784
|
const MAX_CHARS = 2e3;
|
|
4581
4785
|
/**
|
|
@@ -4630,7 +4834,7 @@ function getProjectFindings(db) {
|
|
|
4630
4834
|
if (summary.length > MAX_CHARS) summary = summary.slice(0, MAX_CHARS - 3) + "...";
|
|
4631
4835
|
return summary;
|
|
4632
4836
|
} catch (err) {
|
|
4633
|
-
logger$
|
|
4837
|
+
logger$14.warn({ err }, "Failed to query project findings (graceful fallback)");
|
|
4634
4838
|
return "";
|
|
4635
4839
|
}
|
|
4636
4840
|
}
|
|
@@ -4651,11 +4855,119 @@ function extractRecurringPatterns(outcomes) {
|
|
|
4651
4855
|
return [...patternCounts.entries()].filter(([, count]) => count >= 2).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([pattern, count]) => `${pattern} (${count} occurrences)`);
|
|
4652
4856
|
}
|
|
4653
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
|
+
|
|
4654
4968
|
//#endregion
|
|
4655
4969
|
//#region src/modules/compiled-workflows/dev-story.ts
|
|
4656
|
-
const logger$
|
|
4657
|
-
/** Hard token ceiling for the assembled dev-story prompt */
|
|
4658
|
-
const TOKEN_CEILING$3 = 24e3;
|
|
4970
|
+
const logger$12 = createLogger("compiled-workflows:dev-story");
|
|
4659
4971
|
/** Default timeout for dev-story dispatches in milliseconds (30 min) */
|
|
4660
4972
|
const DEFAULT_TIMEOUT_MS$1 = 18e5;
|
|
4661
4973
|
/** Default Vitest test patterns injected when no test-pattern decisions exist */
|
|
@@ -4678,10 +4990,16 @@ const DEFAULT_VITEST_PATTERNS = `## Test Patterns (defaults)
|
|
|
4678
4990
|
*/
|
|
4679
4991
|
async function runDevStory(deps, params) {
|
|
4680
4992
|
const { storyKey, storyFilePath, taskScope, priorFiles } = params;
|
|
4681
|
-
logger$
|
|
4993
|
+
logger$12.info({
|
|
4682
4994
|
storyKey,
|
|
4683
4995
|
storyFilePath
|
|
4684
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");
|
|
4685
5003
|
const devStoryContextTemplate = {
|
|
4686
5004
|
taskType: "dev-story",
|
|
4687
5005
|
sections: [{
|
|
@@ -4720,10 +5038,10 @@ async function runDevStory(deps, params) {
|
|
|
4720
5038
|
let template;
|
|
4721
5039
|
try {
|
|
4722
5040
|
template = await deps.pack.getPrompt("dev-story");
|
|
4723
|
-
logger$
|
|
5041
|
+
logger$12.debug({ storyKey }, "Retrieved dev-story prompt template from pack");
|
|
4724
5042
|
} catch (err) {
|
|
4725
5043
|
const error = err instanceof Error ? err.message : String(err);
|
|
4726
|
-
logger$
|
|
5044
|
+
logger$12.error({
|
|
4727
5045
|
storyKey,
|
|
4728
5046
|
error
|
|
4729
5047
|
}, "Failed to retrieve dev-story prompt template");
|
|
@@ -4734,14 +5052,14 @@ async function runDevStory(deps, params) {
|
|
|
4734
5052
|
storyContent = await readFile$1(storyFilePath, "utf-8");
|
|
4735
5053
|
} catch (err) {
|
|
4736
5054
|
if (err.code === "ENOENT") {
|
|
4737
|
-
logger$
|
|
5055
|
+
logger$12.error({
|
|
4738
5056
|
storyKey,
|
|
4739
5057
|
storyFilePath
|
|
4740
5058
|
}, "Story file not found");
|
|
4741
5059
|
return makeFailureResult("story_file_not_found");
|
|
4742
5060
|
}
|
|
4743
5061
|
const error = err instanceof Error ? err.message : String(err);
|
|
4744
|
-
logger$
|
|
5062
|
+
logger$12.error({
|
|
4745
5063
|
storyKey,
|
|
4746
5064
|
storyFilePath,
|
|
4747
5065
|
error
|
|
@@ -4749,29 +5067,32 @@ async function runDevStory(deps, params) {
|
|
|
4749
5067
|
return makeFailureResult(`story_file_read_error: ${error}`);
|
|
4750
5068
|
}
|
|
4751
5069
|
if (storyContent.trim().length === 0) {
|
|
4752
|
-
logger$
|
|
5070
|
+
logger$12.error({
|
|
4753
5071
|
storyKey,
|
|
4754
5072
|
storyFilePath
|
|
4755
5073
|
}, "Story file is empty");
|
|
4756
5074
|
return makeFailureResult("story_file_empty");
|
|
4757
5075
|
}
|
|
5076
|
+
const complexity = computeStoryComplexity(storyContent);
|
|
5077
|
+
const resolvedMaxTurns = resolveDevStoryMaxTurns(complexity.complexityScore);
|
|
5078
|
+
logComplexityResult(storyKey, complexity, resolvedMaxTurns);
|
|
4758
5079
|
let testPatternsContent = "";
|
|
4759
5080
|
try {
|
|
4760
5081
|
const solutioningDecisions = getDecisionsByPhase(deps.db, "solutioning");
|
|
4761
5082
|
const testPatternDecisions = solutioningDecisions.filter((d) => d.category === "test-patterns");
|
|
4762
5083
|
if (testPatternDecisions.length > 0) {
|
|
4763
5084
|
testPatternsContent = "## Test Patterns\n" + testPatternDecisions.map((d) => `- ${d.key}: ${d.value}`).join("\n");
|
|
4764
|
-
logger$
|
|
5085
|
+
logger$12.debug({
|
|
4765
5086
|
storyKey,
|
|
4766
5087
|
count: testPatternDecisions.length
|
|
4767
5088
|
}, "Loaded test patterns from decision store");
|
|
4768
5089
|
} else {
|
|
4769
5090
|
testPatternsContent = DEFAULT_VITEST_PATTERNS;
|
|
4770
|
-
logger$
|
|
5091
|
+
logger$12.debug({ storyKey }, "No test-pattern decisions found — using default Vitest patterns");
|
|
4771
5092
|
}
|
|
4772
5093
|
} catch (err) {
|
|
4773
5094
|
const error = err instanceof Error ? err.message : String(err);
|
|
4774
|
-
logger$
|
|
5095
|
+
logger$12.warn({
|
|
4775
5096
|
storyKey,
|
|
4776
5097
|
error
|
|
4777
5098
|
}, "Failed to load test patterns — using defaults");
|
|
@@ -4786,7 +5107,7 @@ async function runDevStory(deps, params) {
|
|
|
4786
5107
|
const findings = getProjectFindings(deps.db);
|
|
4787
5108
|
if (findings.length > 0) {
|
|
4788
5109
|
priorFindingsContent = "Previous pipeline runs encountered these issues — avoid repeating them:\n\n" + findings;
|
|
4789
|
-
logger$
|
|
5110
|
+
logger$12.debug({
|
|
4790
5111
|
storyKey,
|
|
4791
5112
|
findingsLen: findings.length
|
|
4792
5113
|
}, "Injecting prior findings into dev-story prompt");
|
|
@@ -4806,7 +5127,7 @@ async function runDevStory(deps, params) {
|
|
|
4806
5127
|
if (plan.test_categories && plan.test_categories.length > 0) parts.push(`\n### Categories: ${plan.test_categories.join(", ")}`);
|
|
4807
5128
|
if (plan.coverage_notes) parts.push(`\n### Coverage Notes\n${plan.coverage_notes}`);
|
|
4808
5129
|
testPlanContent = parts.join("\n");
|
|
4809
|
-
logger$
|
|
5130
|
+
logger$12.debug({ storyKey }, "Injecting test plan into dev-story prompt");
|
|
4810
5131
|
}
|
|
4811
5132
|
} catch {}
|
|
4812
5133
|
const sections = [
|
|
@@ -4851,11 +5172,11 @@ async function runDevStory(deps, params) {
|
|
|
4851
5172
|
priority: "optional"
|
|
4852
5173
|
}
|
|
4853
5174
|
];
|
|
4854
|
-
const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING
|
|
4855
|
-
logger$
|
|
5175
|
+
const { prompt, tokenCount, truncated } = assemblePrompt(template, sections, TOKEN_CEILING);
|
|
5176
|
+
logger$12.info({
|
|
4856
5177
|
storyKey,
|
|
4857
5178
|
tokenCount,
|
|
4858
|
-
ceiling: TOKEN_CEILING
|
|
5179
|
+
ceiling: TOKEN_CEILING,
|
|
4859
5180
|
truncated
|
|
4860
5181
|
}, "Assembled dev-story prompt");
|
|
4861
5182
|
let dispatchResult;
|
|
@@ -4866,12 +5187,13 @@ async function runDevStory(deps, params) {
|
|
|
4866
5187
|
taskType: "dev-story",
|
|
4867
5188
|
timeout: DEFAULT_TIMEOUT_MS$1,
|
|
4868
5189
|
outputSchema: DevStoryResultSchema,
|
|
5190
|
+
maxTurns: resolvedMaxTurns,
|
|
4869
5191
|
...deps.projectRoot !== void 0 ? { workingDirectory: deps.projectRoot } : {}
|
|
4870
5192
|
});
|
|
4871
5193
|
dispatchResult = await handle.result;
|
|
4872
5194
|
} catch (err) {
|
|
4873
5195
|
const error = err instanceof Error ? err.message : String(err);
|
|
4874
|
-
logger$
|
|
5196
|
+
logger$12.error({
|
|
4875
5197
|
storyKey,
|
|
4876
5198
|
error
|
|
4877
5199
|
}, "Dispatch threw an unexpected error");
|
|
@@ -4882,11 +5204,11 @@ async function runDevStory(deps, params) {
|
|
|
4882
5204
|
output: dispatchResult.tokenEstimate.output
|
|
4883
5205
|
};
|
|
4884
5206
|
if (dispatchResult.status === "timeout") {
|
|
4885
|
-
logger$
|
|
5207
|
+
logger$12.error({
|
|
4886
5208
|
storyKey,
|
|
4887
5209
|
durationMs: dispatchResult.durationMs
|
|
4888
5210
|
}, "Dev-story dispatch timed out");
|
|
4889
|
-
if (dispatchResult.output.length > 0) logger$
|
|
5211
|
+
if (dispatchResult.output.length > 0) logger$12.info({
|
|
4890
5212
|
storyKey,
|
|
4891
5213
|
partialOutput: dispatchResult.output.slice(0, 500)
|
|
4892
5214
|
}, "Partial output before timeout");
|
|
@@ -4896,12 +5218,12 @@ async function runDevStory(deps, params) {
|
|
|
4896
5218
|
};
|
|
4897
5219
|
}
|
|
4898
5220
|
if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
|
|
4899
|
-
logger$
|
|
5221
|
+
logger$12.error({
|
|
4900
5222
|
storyKey,
|
|
4901
5223
|
exitCode: dispatchResult.exitCode,
|
|
4902
5224
|
status: dispatchResult.status
|
|
4903
5225
|
}, "Dev-story dispatch failed");
|
|
4904
|
-
if (dispatchResult.output.length > 0) logger$
|
|
5226
|
+
if (dispatchResult.output.length > 0) logger$12.info({
|
|
4905
5227
|
storyKey,
|
|
4906
5228
|
partialOutput: dispatchResult.output.slice(0, 500)
|
|
4907
5229
|
}, "Partial output from failed dispatch");
|
|
@@ -4913,7 +5235,7 @@ async function runDevStory(deps, params) {
|
|
|
4913
5235
|
if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
|
|
4914
5236
|
const details = dispatchResult.parseError ?? "parsed result was null";
|
|
4915
5237
|
const rawSnippet = dispatchResult.output ? dispatchResult.output.slice(0, 1e3) : "(empty)";
|
|
4916
|
-
logger$
|
|
5238
|
+
logger$12.error({
|
|
4917
5239
|
storyKey,
|
|
4918
5240
|
parseError: details,
|
|
4919
5241
|
rawOutputSnippet: rawSnippet
|
|
@@ -4921,12 +5243,12 @@ async function runDevStory(deps, params) {
|
|
|
4921
5243
|
let filesModified = [];
|
|
4922
5244
|
try {
|
|
4923
5245
|
filesModified = await getGitChangedFiles(deps.projectRoot ?? process.cwd());
|
|
4924
|
-
if (filesModified.length > 0) logger$
|
|
5246
|
+
if (filesModified.length > 0) logger$12.info({
|
|
4925
5247
|
storyKey,
|
|
4926
5248
|
fileCount: filesModified.length
|
|
4927
5249
|
}, "Recovered files_modified from git status (YAML fallback)");
|
|
4928
5250
|
} catch (err) {
|
|
4929
|
-
logger$
|
|
5251
|
+
logger$12.warn({
|
|
4930
5252
|
storyKey,
|
|
4931
5253
|
error: err instanceof Error ? err.message : String(err)
|
|
4932
5254
|
}, "Failed to recover files_modified from git");
|
|
@@ -4943,7 +5265,7 @@ async function runDevStory(deps, params) {
|
|
|
4943
5265
|
};
|
|
4944
5266
|
}
|
|
4945
5267
|
const parsed = dispatchResult.parsed;
|
|
4946
|
-
logger$
|
|
5268
|
+
logger$12.info({
|
|
4947
5269
|
storyKey,
|
|
4948
5270
|
result: parsed.result,
|
|
4949
5271
|
acMet: parsed.ac_met.length
|
|
@@ -5082,13 +5404,7 @@ function extractFilesInScope(storyContent) {
|
|
|
5082
5404
|
|
|
5083
5405
|
//#endregion
|
|
5084
5406
|
//#region src/modules/compiled-workflows/code-review.ts
|
|
5085
|
-
const logger$
|
|
5086
|
-
/**
|
|
5087
|
-
* Hard token ceiling for the assembled code-review prompt (50,000 tokens).
|
|
5088
|
-
* Quality reviews require seeing actual code diffs, not just file names.
|
|
5089
|
-
* // TODO: consider externalizing to pack config when multiple packs exist
|
|
5090
|
-
*/
|
|
5091
|
-
const TOKEN_CEILING$2 = 1e5;
|
|
5407
|
+
const logger$11 = createLogger("compiled-workflows:code-review");
|
|
5092
5408
|
/**
|
|
5093
5409
|
* Default fallback result when dispatch fails or times out.
|
|
5094
5410
|
* Uses NEEDS_MINOR_FIXES (not NEEDS_MAJOR_REWORK) so a parse/schema failure
|
|
@@ -5126,18 +5442,24 @@ function defaultFailResult(error, tokenUsage) {
|
|
|
5126
5442
|
async function runCodeReview(deps, params) {
|
|
5127
5443
|
const { storyKey, storyFilePath, workingDirectory, pipelineRunId, filesModified, previousIssues } = params;
|
|
5128
5444
|
const cwd = workingDirectory ?? process.cwd();
|
|
5129
|
-
logger$
|
|
5445
|
+
logger$11.debug({
|
|
5130
5446
|
storyKey,
|
|
5131
5447
|
storyFilePath,
|
|
5132
5448
|
cwd,
|
|
5133
5449
|
pipelineRunId
|
|
5134
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");
|
|
5135
5457
|
let template;
|
|
5136
5458
|
try {
|
|
5137
5459
|
template = await deps.pack.getPrompt("code-review");
|
|
5138
5460
|
} catch (err) {
|
|
5139
5461
|
const error = err instanceof Error ? err.message : String(err);
|
|
5140
|
-
logger$
|
|
5462
|
+
logger$11.error({ error }, "Failed to retrieve code-review prompt template");
|
|
5141
5463
|
return defaultFailResult(`Failed to retrieve prompt template: ${error}`, {
|
|
5142
5464
|
input: 0,
|
|
5143
5465
|
output: 0
|
|
@@ -5148,7 +5470,7 @@ async function runCodeReview(deps, params) {
|
|
|
5148
5470
|
storyContent = await readFile$1(storyFilePath, "utf-8");
|
|
5149
5471
|
} catch (err) {
|
|
5150
5472
|
const error = err instanceof Error ? err.message : String(err);
|
|
5151
|
-
logger$
|
|
5473
|
+
logger$11.error({
|
|
5152
5474
|
storyFilePath,
|
|
5153
5475
|
error
|
|
5154
5476
|
}, "Failed to read story file");
|
|
@@ -5166,16 +5488,16 @@ async function runCodeReview(deps, params) {
|
|
|
5166
5488
|
if (filesModified && filesModified.length > 0) {
|
|
5167
5489
|
const scopedDiff = await getGitDiffForFiles(filesModified, cwd);
|
|
5168
5490
|
const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
|
|
5169
|
-
if (scopedTotal <= TOKEN_CEILING
|
|
5491
|
+
if (scopedTotal <= TOKEN_CEILING) {
|
|
5170
5492
|
gitDiffContent = scopedDiff;
|
|
5171
|
-
logger$
|
|
5493
|
+
logger$11.debug({
|
|
5172
5494
|
fileCount: filesModified.length,
|
|
5173
5495
|
tokenCount: scopedTotal
|
|
5174
5496
|
}, "Using scoped file diff");
|
|
5175
5497
|
} else {
|
|
5176
|
-
logger$
|
|
5498
|
+
logger$11.warn({
|
|
5177
5499
|
estimatedTotal: scopedTotal,
|
|
5178
|
-
ceiling: TOKEN_CEILING
|
|
5500
|
+
ceiling: TOKEN_CEILING,
|
|
5179
5501
|
fileCount: filesModified.length
|
|
5180
5502
|
}, "Scoped diff exceeds token ceiling — falling back to stat-only summary");
|
|
5181
5503
|
gitDiffContent = await getGitDiffStatSummary(cwd);
|
|
@@ -5185,17 +5507,17 @@ async function runCodeReview(deps, params) {
|
|
|
5185
5507
|
await stageIntentToAdd(changedFiles, cwd);
|
|
5186
5508
|
const fullDiff = await getGitDiffSummary(cwd);
|
|
5187
5509
|
const fullTotal = nonDiffTokens + countTokens(fullDiff);
|
|
5188
|
-
if (fullTotal <= TOKEN_CEILING
|
|
5510
|
+
if (fullTotal <= TOKEN_CEILING) gitDiffContent = fullDiff;
|
|
5189
5511
|
else {
|
|
5190
|
-
logger$
|
|
5512
|
+
logger$11.warn({
|
|
5191
5513
|
estimatedTotal: fullTotal,
|
|
5192
|
-
ceiling: TOKEN_CEILING
|
|
5514
|
+
ceiling: TOKEN_CEILING
|
|
5193
5515
|
}, "Full git diff would exceed token ceiling — using stat-only summary");
|
|
5194
5516
|
gitDiffContent = await getGitDiffStatSummary(cwd);
|
|
5195
5517
|
}
|
|
5196
5518
|
}
|
|
5197
5519
|
if (gitDiffContent.trim().length === 0) {
|
|
5198
|
-
logger$
|
|
5520
|
+
logger$11.info({ storyKey }, "Empty git diff — skipping review with SHIP_IT");
|
|
5199
5521
|
return {
|
|
5200
5522
|
verdict: "SHIP_IT",
|
|
5201
5523
|
issues: 0,
|
|
@@ -5220,7 +5542,7 @@ async function runCodeReview(deps, params) {
|
|
|
5220
5542
|
const findings = getProjectFindings(deps.db);
|
|
5221
5543
|
if (findings.length > 0) {
|
|
5222
5544
|
priorFindingsContent = "Previous reviews found these recurring patterns — pay special attention:\n\n" + findings;
|
|
5223
|
-
logger$
|
|
5545
|
+
logger$11.debug({
|
|
5224
5546
|
storyKey,
|
|
5225
5547
|
findingsLen: findings.length
|
|
5226
5548
|
}, "Injecting prior findings into code-review prompt");
|
|
@@ -5253,12 +5575,12 @@ async function runCodeReview(deps, params) {
|
|
|
5253
5575
|
priority: "optional"
|
|
5254
5576
|
}
|
|
5255
5577
|
];
|
|
5256
|
-
const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING
|
|
5257
|
-
if (assembleResult.truncated) logger$
|
|
5578
|
+
const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
|
|
5579
|
+
if (assembleResult.truncated) logger$11.warn({
|
|
5258
5580
|
storyKey,
|
|
5259
5581
|
tokenCount: assembleResult.tokenCount
|
|
5260
5582
|
}, "Code-review prompt truncated to fit token ceiling");
|
|
5261
|
-
logger$
|
|
5583
|
+
logger$11.debug({
|
|
5262
5584
|
storyKey,
|
|
5263
5585
|
tokenCount: assembleResult.tokenCount,
|
|
5264
5586
|
truncated: assembleResult.truncated
|
|
@@ -5276,7 +5598,7 @@ async function runCodeReview(deps, params) {
|
|
|
5276
5598
|
dispatchResult = await handle.result;
|
|
5277
5599
|
} catch (err) {
|
|
5278
5600
|
const error = err instanceof Error ? err.message : String(err);
|
|
5279
|
-
logger$
|
|
5601
|
+
logger$11.error({
|
|
5280
5602
|
storyKey,
|
|
5281
5603
|
error
|
|
5282
5604
|
}, "Code-review dispatch threw unexpected error");
|
|
@@ -5292,7 +5614,7 @@ async function runCodeReview(deps, params) {
|
|
|
5292
5614
|
const rawOutput = dispatchResult.output ?? void 0;
|
|
5293
5615
|
if (dispatchResult.status === "failed") {
|
|
5294
5616
|
const errorMsg = `Dispatch status: failed. Exit code: ${dispatchResult.exitCode}. ${dispatchResult.parseError ?? ""} ${dispatchResult.output ? `Stderr: ${dispatchResult.output}` : ""}`.trim();
|
|
5295
|
-
logger$
|
|
5617
|
+
logger$11.warn({
|
|
5296
5618
|
storyKey,
|
|
5297
5619
|
exitCode: dispatchResult.exitCode
|
|
5298
5620
|
}, "Code-review dispatch failed");
|
|
@@ -5302,7 +5624,7 @@ async function runCodeReview(deps, params) {
|
|
|
5302
5624
|
};
|
|
5303
5625
|
}
|
|
5304
5626
|
if (dispatchResult.status === "timeout") {
|
|
5305
|
-
logger$
|
|
5627
|
+
logger$11.warn({ storyKey }, "Code-review dispatch timed out");
|
|
5306
5628
|
return {
|
|
5307
5629
|
...defaultFailResult("Dispatch status: timeout. The agent did not complete within the allowed time.", tokenUsage),
|
|
5308
5630
|
rawOutput
|
|
@@ -5310,7 +5632,7 @@ async function runCodeReview(deps, params) {
|
|
|
5310
5632
|
}
|
|
5311
5633
|
if (dispatchResult.parsed === null) {
|
|
5312
5634
|
const details = dispatchResult.parseError ?? "No YAML block found in output";
|
|
5313
|
-
logger$
|
|
5635
|
+
logger$11.warn({
|
|
5314
5636
|
storyKey,
|
|
5315
5637
|
details
|
|
5316
5638
|
}, "Code-review output schema validation failed");
|
|
@@ -5327,7 +5649,7 @@ async function runCodeReview(deps, params) {
|
|
|
5327
5649
|
const parseResult = CodeReviewResultSchema.safeParse(dispatchResult.parsed);
|
|
5328
5650
|
if (!parseResult.success) {
|
|
5329
5651
|
const details = parseResult.error.message;
|
|
5330
|
-
logger$
|
|
5652
|
+
logger$11.warn({
|
|
5331
5653
|
storyKey,
|
|
5332
5654
|
details
|
|
5333
5655
|
}, "Code-review output failed schema validation");
|
|
@@ -5342,13 +5664,13 @@ async function runCodeReview(deps, params) {
|
|
|
5342
5664
|
};
|
|
5343
5665
|
}
|
|
5344
5666
|
const parsed = parseResult.data;
|
|
5345
|
-
if (parsed.agentVerdict !== parsed.verdict) logger$
|
|
5667
|
+
if (parsed.agentVerdict !== parsed.verdict) logger$11.info({
|
|
5346
5668
|
storyKey,
|
|
5347
5669
|
agentVerdict: parsed.agentVerdict,
|
|
5348
5670
|
pipelineVerdict: parsed.verdict,
|
|
5349
5671
|
issues: parsed.issues
|
|
5350
5672
|
}, "Pipeline overrode agent verdict based on issue severities");
|
|
5351
|
-
logger$
|
|
5673
|
+
logger$11.info({
|
|
5352
5674
|
storyKey,
|
|
5353
5675
|
verdict: parsed.verdict,
|
|
5354
5676
|
issues: parsed.issues
|
|
@@ -5373,16 +5695,14 @@ function getArchConstraints$1(deps) {
|
|
|
5373
5695
|
if (constraints.length === 0) return "";
|
|
5374
5696
|
return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
|
|
5375
5697
|
} catch (err) {
|
|
5376
|
-
logger$
|
|
5698
|
+
logger$11.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
|
|
5377
5699
|
return "";
|
|
5378
5700
|
}
|
|
5379
5701
|
}
|
|
5380
5702
|
|
|
5381
5703
|
//#endregion
|
|
5382
5704
|
//#region src/modules/compiled-workflows/test-plan.ts
|
|
5383
|
-
const logger$
|
|
5384
|
-
/** Hard token ceiling for the assembled test-plan prompt */
|
|
5385
|
-
const TOKEN_CEILING$1 = 8e3;
|
|
5705
|
+
const logger$10 = createLogger("compiled-workflows:test-plan");
|
|
5386
5706
|
/** Default timeout for test-plan dispatches in milliseconds (5 min — lightweight call) */
|
|
5387
5707
|
const DEFAULT_TIMEOUT_MS = 3e5;
|
|
5388
5708
|
/**
|
|
@@ -5394,17 +5714,23 @@ const DEFAULT_TIMEOUT_MS = 3e5;
|
|
|
5394
5714
|
*/
|
|
5395
5715
|
async function runTestPlan(deps, params) {
|
|
5396
5716
|
const { storyKey, storyFilePath, pipelineRunId } = params;
|
|
5397
|
-
logger$
|
|
5717
|
+
logger$10.info({
|
|
5398
5718
|
storyKey,
|
|
5399
5719
|
storyFilePath
|
|
5400
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");
|
|
5401
5727
|
let template;
|
|
5402
5728
|
try {
|
|
5403
5729
|
template = await deps.pack.getPrompt("test-plan");
|
|
5404
|
-
logger$
|
|
5730
|
+
logger$10.debug({ storyKey }, "Retrieved test-plan prompt template from pack");
|
|
5405
5731
|
} catch (err) {
|
|
5406
5732
|
const error = err instanceof Error ? err.message : String(err);
|
|
5407
|
-
logger$
|
|
5733
|
+
logger$10.warn({
|
|
5408
5734
|
storyKey,
|
|
5409
5735
|
error
|
|
5410
5736
|
}, "Failed to retrieve test-plan prompt template");
|
|
@@ -5415,14 +5741,14 @@ async function runTestPlan(deps, params) {
|
|
|
5415
5741
|
storyContent = await readFile$1(storyFilePath, "utf-8");
|
|
5416
5742
|
} catch (err) {
|
|
5417
5743
|
if (err.code === "ENOENT") {
|
|
5418
|
-
logger$
|
|
5744
|
+
logger$10.warn({
|
|
5419
5745
|
storyKey,
|
|
5420
5746
|
storyFilePath
|
|
5421
5747
|
}, "Story file not found for test planning");
|
|
5422
5748
|
return makeTestPlanFailureResult("story_file_not_found");
|
|
5423
5749
|
}
|
|
5424
5750
|
const error = err instanceof Error ? err.message : String(err);
|
|
5425
|
-
logger$
|
|
5751
|
+
logger$10.warn({
|
|
5426
5752
|
storyKey,
|
|
5427
5753
|
storyFilePath,
|
|
5428
5754
|
error
|
|
@@ -5433,11 +5759,11 @@ async function runTestPlan(deps, params) {
|
|
|
5433
5759
|
name: "story_content",
|
|
5434
5760
|
content: storyContent,
|
|
5435
5761
|
priority: "required"
|
|
5436
|
-
}], TOKEN_CEILING
|
|
5437
|
-
logger$
|
|
5762
|
+
}], TOKEN_CEILING);
|
|
5763
|
+
logger$10.info({
|
|
5438
5764
|
storyKey,
|
|
5439
5765
|
tokenCount,
|
|
5440
|
-
ceiling: TOKEN_CEILING
|
|
5766
|
+
ceiling: TOKEN_CEILING,
|
|
5441
5767
|
truncated
|
|
5442
5768
|
}, "Assembled test-plan prompt");
|
|
5443
5769
|
let dispatchResult;
|
|
@@ -5453,7 +5779,7 @@ async function runTestPlan(deps, params) {
|
|
|
5453
5779
|
dispatchResult = await handle.result;
|
|
5454
5780
|
} catch (err) {
|
|
5455
5781
|
const error = err instanceof Error ? err.message : String(err);
|
|
5456
|
-
logger$
|
|
5782
|
+
logger$10.warn({
|
|
5457
5783
|
storyKey,
|
|
5458
5784
|
error
|
|
5459
5785
|
}, "Test-plan dispatch threw an unexpected error");
|
|
@@ -5464,7 +5790,7 @@ async function runTestPlan(deps, params) {
|
|
|
5464
5790
|
output: dispatchResult.tokenEstimate.output
|
|
5465
5791
|
};
|
|
5466
5792
|
if (dispatchResult.status === "timeout") {
|
|
5467
|
-
logger$
|
|
5793
|
+
logger$10.warn({
|
|
5468
5794
|
storyKey,
|
|
5469
5795
|
durationMs: dispatchResult.durationMs
|
|
5470
5796
|
}, "Test-plan dispatch timed out");
|
|
@@ -5474,7 +5800,7 @@ async function runTestPlan(deps, params) {
|
|
|
5474
5800
|
};
|
|
5475
5801
|
}
|
|
5476
5802
|
if (dispatchResult.status === "failed" || dispatchResult.exitCode !== 0) {
|
|
5477
|
-
logger$
|
|
5803
|
+
logger$10.warn({
|
|
5478
5804
|
storyKey,
|
|
5479
5805
|
exitCode: dispatchResult.exitCode,
|
|
5480
5806
|
status: dispatchResult.status
|
|
@@ -5486,7 +5812,7 @@ async function runTestPlan(deps, params) {
|
|
|
5486
5812
|
}
|
|
5487
5813
|
if (dispatchResult.parseError !== null || dispatchResult.parsed === null) {
|
|
5488
5814
|
const details = dispatchResult.parseError ?? "parsed result was null";
|
|
5489
|
-
logger$
|
|
5815
|
+
logger$10.warn({
|
|
5490
5816
|
storyKey,
|
|
5491
5817
|
parseError: details
|
|
5492
5818
|
}, "Test-plan YAML schema validation failed");
|
|
@@ -5509,19 +5835,19 @@ async function runTestPlan(deps, params) {
|
|
|
5509
5835
|
}),
|
|
5510
5836
|
rationale: `Test plan for ${storyKey}: ${parsed.test_files.length} test files, categories: ${parsed.test_categories.join(", ")}`
|
|
5511
5837
|
});
|
|
5512
|
-
logger$
|
|
5838
|
+
logger$10.info({
|
|
5513
5839
|
storyKey,
|
|
5514
5840
|
fileCount: parsed.test_files.length,
|
|
5515
5841
|
categories: parsed.test_categories
|
|
5516
5842
|
}, "Test plan stored in decision store");
|
|
5517
5843
|
} catch (err) {
|
|
5518
5844
|
const error = err instanceof Error ? err.message : String(err);
|
|
5519
|
-
logger$
|
|
5845
|
+
logger$10.warn({
|
|
5520
5846
|
storyKey,
|
|
5521
5847
|
error
|
|
5522
5848
|
}, "Failed to store test plan in decision store — proceeding anyway");
|
|
5523
5849
|
}
|
|
5524
|
-
logger$
|
|
5850
|
+
logger$10.info({
|
|
5525
5851
|
storyKey,
|
|
5526
5852
|
result: parsed.result
|
|
5527
5853
|
}, "Test-plan workflow completed");
|
|
@@ -5552,11 +5878,7 @@ function makeTestPlanFailureResult(error) {
|
|
|
5552
5878
|
|
|
5553
5879
|
//#endregion
|
|
5554
5880
|
//#region src/modules/compiled-workflows/test-expansion.ts
|
|
5555
|
-
const logger$
|
|
5556
|
-
/**
|
|
5557
|
-
* Hard token ceiling for the assembled test-expansion prompt (20,000 tokens).
|
|
5558
|
-
*/
|
|
5559
|
-
const TOKEN_CEILING = 2e4;
|
|
5881
|
+
const logger$9 = createLogger("compiled-workflows:test-expansion");
|
|
5560
5882
|
function defaultFallbackResult(error, tokenUsage) {
|
|
5561
5883
|
return {
|
|
5562
5884
|
expansion_priority: "low",
|
|
@@ -5586,18 +5908,24 @@ function defaultFallbackResult(error, tokenUsage) {
|
|
|
5586
5908
|
async function runTestExpansion(deps, params) {
|
|
5587
5909
|
const { storyKey, storyFilePath, pipelineRunId, filesModified, workingDirectory } = params;
|
|
5588
5910
|
const cwd = workingDirectory ?? process.cwd();
|
|
5589
|
-
logger$
|
|
5911
|
+
logger$9.debug({
|
|
5590
5912
|
storyKey,
|
|
5591
5913
|
storyFilePath,
|
|
5592
5914
|
cwd,
|
|
5593
5915
|
pipelineRunId
|
|
5594
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");
|
|
5595
5923
|
let template;
|
|
5596
5924
|
try {
|
|
5597
5925
|
template = await deps.pack.getPrompt("test-expansion");
|
|
5598
5926
|
} catch (err) {
|
|
5599
5927
|
const error = err instanceof Error ? err.message : String(err);
|
|
5600
|
-
logger$
|
|
5928
|
+
logger$9.warn({ error }, "Failed to retrieve test-expansion prompt template");
|
|
5601
5929
|
return defaultFallbackResult(`Failed to retrieve prompt template: ${error}`, {
|
|
5602
5930
|
input: 0,
|
|
5603
5931
|
output: 0
|
|
@@ -5608,7 +5936,7 @@ async function runTestExpansion(deps, params) {
|
|
|
5608
5936
|
storyContent = await readFile$1(storyFilePath, "utf-8");
|
|
5609
5937
|
} catch (err) {
|
|
5610
5938
|
const error = err instanceof Error ? err.message : String(err);
|
|
5611
|
-
logger$
|
|
5939
|
+
logger$9.warn({
|
|
5612
5940
|
storyFilePath,
|
|
5613
5941
|
error
|
|
5614
5942
|
}, "Failed to read story file");
|
|
@@ -5628,12 +5956,12 @@ async function runTestExpansion(deps, params) {
|
|
|
5628
5956
|
const scopedTotal = nonDiffTokens + countTokens(scopedDiff);
|
|
5629
5957
|
if (scopedTotal <= TOKEN_CEILING) {
|
|
5630
5958
|
gitDiffContent = scopedDiff;
|
|
5631
|
-
logger$
|
|
5959
|
+
logger$9.debug({
|
|
5632
5960
|
fileCount: filesModified.length,
|
|
5633
5961
|
tokenCount: scopedTotal
|
|
5634
5962
|
}, "Using scoped file diff");
|
|
5635
5963
|
} else {
|
|
5636
|
-
logger$
|
|
5964
|
+
logger$9.warn({
|
|
5637
5965
|
estimatedTotal: scopedTotal,
|
|
5638
5966
|
ceiling: TOKEN_CEILING,
|
|
5639
5967
|
fileCount: filesModified.length
|
|
@@ -5641,7 +5969,7 @@ async function runTestExpansion(deps, params) {
|
|
|
5641
5969
|
gitDiffContent = await getGitDiffStatSummary(cwd);
|
|
5642
5970
|
}
|
|
5643
5971
|
} catch (err) {
|
|
5644
|
-
logger$
|
|
5972
|
+
logger$9.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to get git diff — proceeding with empty diff");
|
|
5645
5973
|
}
|
|
5646
5974
|
const sections = [
|
|
5647
5975
|
{
|
|
@@ -5661,11 +5989,11 @@ async function runTestExpansion(deps, params) {
|
|
|
5661
5989
|
}
|
|
5662
5990
|
];
|
|
5663
5991
|
const assembleResult = assemblePrompt(template, sections, TOKEN_CEILING);
|
|
5664
|
-
if (assembleResult.truncated) logger$
|
|
5992
|
+
if (assembleResult.truncated) logger$9.warn({
|
|
5665
5993
|
storyKey,
|
|
5666
5994
|
tokenCount: assembleResult.tokenCount
|
|
5667
5995
|
}, "Test-expansion prompt truncated to fit token ceiling");
|
|
5668
|
-
logger$
|
|
5996
|
+
logger$9.debug({
|
|
5669
5997
|
storyKey,
|
|
5670
5998
|
tokenCount: assembleResult.tokenCount,
|
|
5671
5999
|
truncated: assembleResult.truncated
|
|
@@ -5683,7 +6011,7 @@ async function runTestExpansion(deps, params) {
|
|
|
5683
6011
|
dispatchResult = await handle.result;
|
|
5684
6012
|
} catch (err) {
|
|
5685
6013
|
const error = err instanceof Error ? err.message : String(err);
|
|
5686
|
-
logger$
|
|
6014
|
+
logger$9.warn({
|
|
5687
6015
|
storyKey,
|
|
5688
6016
|
error
|
|
5689
6017
|
}, "Test-expansion dispatch threw unexpected error");
|
|
@@ -5698,19 +6026,19 @@ async function runTestExpansion(deps, params) {
|
|
|
5698
6026
|
};
|
|
5699
6027
|
if (dispatchResult.status === "failed") {
|
|
5700
6028
|
const errorMsg = `Dispatch status: failed. Exit code: ${dispatchResult.exitCode}. ${dispatchResult.parseError ?? ""}`.trim();
|
|
5701
|
-
logger$
|
|
6029
|
+
logger$9.warn({
|
|
5702
6030
|
storyKey,
|
|
5703
6031
|
exitCode: dispatchResult.exitCode
|
|
5704
6032
|
}, "Test-expansion dispatch failed");
|
|
5705
6033
|
return defaultFallbackResult(errorMsg, tokenUsage);
|
|
5706
6034
|
}
|
|
5707
6035
|
if (dispatchResult.status === "timeout") {
|
|
5708
|
-
logger$
|
|
6036
|
+
logger$9.warn({ storyKey }, "Test-expansion dispatch timed out");
|
|
5709
6037
|
return defaultFallbackResult("Dispatch status: timeout. The agent did not complete within the allowed time.", tokenUsage);
|
|
5710
6038
|
}
|
|
5711
6039
|
if (dispatchResult.parsed === null) {
|
|
5712
6040
|
const details = dispatchResult.parseError ?? "No YAML block found in output";
|
|
5713
|
-
logger$
|
|
6041
|
+
logger$9.warn({
|
|
5714
6042
|
storyKey,
|
|
5715
6043
|
details
|
|
5716
6044
|
}, "Test-expansion output has no parseable YAML");
|
|
@@ -5719,14 +6047,14 @@ async function runTestExpansion(deps, params) {
|
|
|
5719
6047
|
const parseResult = TestExpansionResultSchema.safeParse(dispatchResult.parsed);
|
|
5720
6048
|
if (!parseResult.success) {
|
|
5721
6049
|
const details = parseResult.error.message;
|
|
5722
|
-
logger$
|
|
6050
|
+
logger$9.warn({
|
|
5723
6051
|
storyKey,
|
|
5724
6052
|
details
|
|
5725
6053
|
}, "Test-expansion output failed schema validation");
|
|
5726
6054
|
return defaultFallbackResult(`schema_validation_failed: ${details}`, tokenUsage);
|
|
5727
6055
|
}
|
|
5728
6056
|
const parsed = parseResult.data;
|
|
5729
|
-
logger$
|
|
6057
|
+
logger$9.info({
|
|
5730
6058
|
storyKey,
|
|
5731
6059
|
expansion_priority: parsed.expansion_priority,
|
|
5732
6060
|
coverage_gaps: parsed.coverage_gaps.length,
|
|
@@ -5751,7 +6079,7 @@ function getArchConstraints(deps) {
|
|
|
5751
6079
|
if (constraints.length === 0) return "";
|
|
5752
6080
|
return constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
|
|
5753
6081
|
} catch (err) {
|
|
5754
|
-
logger$
|
|
6082
|
+
logger$9.warn({ error: err instanceof Error ? err.message : String(err) }, "Failed to retrieve architecture constraints");
|
|
5755
6083
|
return "";
|
|
5756
6084
|
}
|
|
5757
6085
|
}
|
|
@@ -6064,7 +6392,7 @@ function detectConflictGroups(storyKeys, config) {
|
|
|
6064
6392
|
|
|
6065
6393
|
//#endregion
|
|
6066
6394
|
//#region src/cli/commands/health.ts
|
|
6067
|
-
const logger$
|
|
6395
|
+
const logger$8 = createLogger("health-cmd");
|
|
6068
6396
|
/** Default stall threshold in seconds — also used by supervisor default */
|
|
6069
6397
|
const DEFAULT_STALL_THRESHOLD_SECONDS = 600;
|
|
6070
6398
|
/**
|
|
@@ -6329,7 +6657,7 @@ async function runHealthAction(options) {
|
|
|
6329
6657
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6330
6658
|
if (outputFormat === "json") process.stdout.write(formatOutput(null, "json", false, msg) + "\n");
|
|
6331
6659
|
else process.stderr.write(`Error: ${msg}\n`);
|
|
6332
|
-
logger$
|
|
6660
|
+
logger$8.error({ err }, "health action failed");
|
|
6333
6661
|
return 1;
|
|
6334
6662
|
}
|
|
6335
6663
|
}
|
|
@@ -6347,7 +6675,7 @@ function registerHealthCommand(program, _version = "0.0.0", projectRoot = proces
|
|
|
6347
6675
|
|
|
6348
6676
|
//#endregion
|
|
6349
6677
|
//#region src/modules/implementation-orchestrator/seed-methodology-context.ts
|
|
6350
|
-
const logger$
|
|
6678
|
+
const logger$7 = createLogger("implementation-orchestrator:seed");
|
|
6351
6679
|
/** Max chars for the architecture summary seeded into decisions */
|
|
6352
6680
|
const MAX_ARCH_CHARS = 6e3;
|
|
6353
6681
|
/** Max chars per epic shard (fallback when per-story extraction returns null) */
|
|
@@ -6381,12 +6709,12 @@ function seedMethodologyContext(db, projectRoot) {
|
|
|
6381
6709
|
const testCount = seedTestPatterns(db, projectRoot);
|
|
6382
6710
|
if (testCount === -1) result.skippedCategories.push("test-patterns");
|
|
6383
6711
|
else result.decisionsCreated += testCount;
|
|
6384
|
-
logger$
|
|
6712
|
+
logger$7.info({
|
|
6385
6713
|
decisionsCreated: result.decisionsCreated,
|
|
6386
6714
|
skippedCategories: result.skippedCategories
|
|
6387
6715
|
}, "Methodology context seeding complete");
|
|
6388
6716
|
} catch (err) {
|
|
6389
|
-
logger$
|
|
6717
|
+
logger$7.warn({ error: err instanceof Error ? err.message : String(err) }, "Methodology context seeding failed (non-fatal)");
|
|
6390
6718
|
}
|
|
6391
6719
|
return result;
|
|
6392
6720
|
}
|
|
@@ -6430,7 +6758,7 @@ function seedArchitecture(db, projectRoot) {
|
|
|
6430
6758
|
});
|
|
6431
6759
|
count = 1;
|
|
6432
6760
|
}
|
|
6433
|
-
logger$
|
|
6761
|
+
logger$7.debug({ count }, "Seeded architecture decisions");
|
|
6434
6762
|
return count;
|
|
6435
6763
|
}
|
|
6436
6764
|
/**
|
|
@@ -6454,11 +6782,11 @@ function seedEpicShards(db, projectRoot) {
|
|
|
6454
6782
|
const storedHashDecision = implementationDecisions.find((d) => d.category === "epic-shard-hash" && d.key === "epics-file");
|
|
6455
6783
|
const storedHash = storedHashDecision?.value;
|
|
6456
6784
|
if (storedHash === currentHash) {
|
|
6457
|
-
logger$
|
|
6785
|
+
logger$7.debug({ hash: currentHash }, "Epic shards up-to-date (hash unchanged) — skipping re-seed");
|
|
6458
6786
|
return -1;
|
|
6459
6787
|
}
|
|
6460
6788
|
if (implementationDecisions.some((d) => d.category === "epic-shard")) {
|
|
6461
|
-
logger$
|
|
6789
|
+
logger$7.debug({
|
|
6462
6790
|
storedHash,
|
|
6463
6791
|
currentHash
|
|
6464
6792
|
}, "Epics file changed — deleting stale epic-shard decisions");
|
|
@@ -6486,7 +6814,7 @@ function seedEpicShards(db, projectRoot) {
|
|
|
6486
6814
|
value: currentHash,
|
|
6487
6815
|
rationale: "SHA-256 hash of epics file content for change detection"
|
|
6488
6816
|
});
|
|
6489
|
-
logger$
|
|
6817
|
+
logger$7.debug({
|
|
6490
6818
|
count,
|
|
6491
6819
|
hash: currentHash
|
|
6492
6820
|
}, "Seeded epic shard decisions");
|
|
@@ -6510,7 +6838,7 @@ function seedTestPatterns(db, projectRoot) {
|
|
|
6510
6838
|
value: patterns.slice(0, MAX_TEST_PATTERNS_CHARS),
|
|
6511
6839
|
rationale: "Detected from project configuration at orchestrator startup"
|
|
6512
6840
|
});
|
|
6513
|
-
logger$
|
|
6841
|
+
logger$7.debug("Seeded test patterns decision");
|
|
6514
6842
|
return 1;
|
|
6515
6843
|
}
|
|
6516
6844
|
/**
|
|
@@ -6681,6 +7009,107 @@ function findArtifact(projectRoot, candidates) {
|
|
|
6681
7009
|
return void 0;
|
|
6682
7010
|
}
|
|
6683
7011
|
|
|
7012
|
+
//#endregion
|
|
7013
|
+
//#region src/modules/agent-dispatch/interface-change-detector.ts
|
|
7014
|
+
const logger$6 = createLogger("interface-change-detector");
|
|
7015
|
+
/**
|
|
7016
|
+
* Extract exported interface and type names from TypeScript source content.
|
|
7017
|
+
*
|
|
7018
|
+
* Matches:
|
|
7019
|
+
* export interface Foo { ... } → 'Foo'
|
|
7020
|
+
* export type Bar = ... → 'Bar'
|
|
7021
|
+
*
|
|
7022
|
+
* Uses simple regex (not AST) for performance (<500ms target).
|
|
7023
|
+
* Does NOT match: re-exports (`export { Foo }`), default exports, internal declarations.
|
|
7024
|
+
*/
|
|
7025
|
+
function extractExportedNames(content) {
|
|
7026
|
+
const names = [];
|
|
7027
|
+
const pattern = /^export\s+(?:interface|type)\s+(\w+)/gm;
|
|
7028
|
+
let match;
|
|
7029
|
+
while ((match = pattern.exec(content)) !== null) if (match[1] !== void 0) names.push(match[1]);
|
|
7030
|
+
return names;
|
|
7031
|
+
}
|
|
7032
|
+
/**
|
|
7033
|
+
* Detect whether modified .ts files export interfaces/types that are
|
|
7034
|
+
* referenced by test files outside the same module.
|
|
7035
|
+
*
|
|
7036
|
+
* Non-blocking: any errors during detection are caught, logged, and an empty
|
|
7037
|
+
* result is returned rather than throwing. Detection failure never blocks pipeline.
|
|
7038
|
+
*
|
|
7039
|
+
* @param options.filesModified - List of file paths modified by the dev-story (relative to projectRoot)
|
|
7040
|
+
* @param options.projectRoot - Absolute path to the project root
|
|
7041
|
+
* @param options.storyKey - Story key for logging context
|
|
7042
|
+
*/
|
|
7043
|
+
function detectInterfaceChanges(options) {
|
|
7044
|
+
try {
|
|
7045
|
+
const { filesModified, projectRoot, storyKey } = options;
|
|
7046
|
+
const tsSourceFiles = filesModified.filter((f) => f.endsWith(".ts") && !f.endsWith(".test.ts") && !f.endsWith(".spec.ts"));
|
|
7047
|
+
if (tsSourceFiles.length === 0) return {
|
|
7048
|
+
modifiedInterfaces: [],
|
|
7049
|
+
potentiallyAffectedTests: []
|
|
7050
|
+
};
|
|
7051
|
+
const allNames = new Set();
|
|
7052
|
+
const sourceDirs = [];
|
|
7053
|
+
for (const relPath of tsSourceFiles) {
|
|
7054
|
+
const absPath = join$1(projectRoot, relPath);
|
|
7055
|
+
try {
|
|
7056
|
+
const content = readFileSync$1(absPath, "utf-8");
|
|
7057
|
+
const names = extractExportedNames(content);
|
|
7058
|
+
for (const name of names) allNames.add(name);
|
|
7059
|
+
sourceDirs.push(dirname$1(relPath));
|
|
7060
|
+
} catch {
|
|
7061
|
+
logger$6.debug({
|
|
7062
|
+
absPath,
|
|
7063
|
+
storyKey
|
|
7064
|
+
}, "Could not read modified file for interface extraction");
|
|
7065
|
+
}
|
|
7066
|
+
}
|
|
7067
|
+
if (allNames.size === 0) return {
|
|
7068
|
+
modifiedInterfaces: [],
|
|
7069
|
+
potentiallyAffectedTests: []
|
|
7070
|
+
};
|
|
7071
|
+
const affectedTests = new Set();
|
|
7072
|
+
for (const name of allNames) {
|
|
7073
|
+
let grepOutput = "";
|
|
7074
|
+
try {
|
|
7075
|
+
grepOutput = execSync(`grep -r --include="*.test.ts" --include="*.spec.ts" -l "${name}" .`, {
|
|
7076
|
+
cwd: projectRoot,
|
|
7077
|
+
encoding: "utf-8",
|
|
7078
|
+
timeout: 1e4,
|
|
7079
|
+
stdio: [
|
|
7080
|
+
"ignore",
|
|
7081
|
+
"pipe",
|
|
7082
|
+
"pipe"
|
|
7083
|
+
]
|
|
7084
|
+
});
|
|
7085
|
+
} catch (grepErr) {
|
|
7086
|
+
const e = grepErr;
|
|
7087
|
+
if (typeof e.stdout === "string" && e.stdout.trim().length > 0) grepOutput = e.stdout;
|
|
7088
|
+
else continue;
|
|
7089
|
+
}
|
|
7090
|
+
const testFiles = grepOutput.split("\n").map((l) => l.trim().replace(/^\.\//, "")).filter((l) => l.length > 0);
|
|
7091
|
+
for (const tf of testFiles) {
|
|
7092
|
+
const tfDir = dirname$1(tf);
|
|
7093
|
+
const isSameModule = sourceDirs.some((srcDir) => tfDir === srcDir || tfDir.startsWith(srcDir + "/"));
|
|
7094
|
+
if (!isSameModule) affectedTests.add(tf);
|
|
7095
|
+
}
|
|
7096
|
+
}
|
|
7097
|
+
return {
|
|
7098
|
+
modifiedInterfaces: Array.from(allNames),
|
|
7099
|
+
potentiallyAffectedTests: Array.from(affectedTests)
|
|
7100
|
+
};
|
|
7101
|
+
} catch (err) {
|
|
7102
|
+
logger$6.warn({
|
|
7103
|
+
err,
|
|
7104
|
+
storyKey: options.storyKey
|
|
7105
|
+
}, "Interface change detection failed — skipping");
|
|
7106
|
+
return {
|
|
7107
|
+
modifiedInterfaces: [],
|
|
7108
|
+
potentiallyAffectedTests: []
|
|
7109
|
+
};
|
|
7110
|
+
}
|
|
7111
|
+
}
|
|
7112
|
+
|
|
6684
7113
|
//#endregion
|
|
6685
7114
|
//#region src/modules/implementation-orchestrator/orchestrator-impl.ts
|
|
6686
7115
|
function createPauseGate() {
|
|
@@ -6701,8 +7130,8 @@ function createPauseGate() {
|
|
|
6701
7130
|
* @returns A fully-configured ImplementationOrchestrator ready to call run()
|
|
6702
7131
|
*/
|
|
6703
7132
|
function createImplementationOrchestrator(deps) {
|
|
6704
|
-
const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot } = deps;
|
|
6705
|
-
const logger$
|
|
7133
|
+
const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot, tokenCeilings } = deps;
|
|
7134
|
+
const logger$22 = createLogger("implementation-orchestrator");
|
|
6706
7135
|
let _state = "IDLE";
|
|
6707
7136
|
let _startedAt;
|
|
6708
7137
|
let _completedAt;
|
|
@@ -6745,7 +7174,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6745
7174
|
const nowMs = Date.now();
|
|
6746
7175
|
for (const [phase, startMs] of starts) {
|
|
6747
7176
|
const endMs = ends?.get(phase);
|
|
6748
|
-
if (endMs === void 0) logger$
|
|
7177
|
+
if (endMs === void 0) logger$22.warn({
|
|
6749
7178
|
storyKey,
|
|
6750
7179
|
phase
|
|
6751
7180
|
}, "Phase has no end time — story may have errored mid-phase. Duration capped to now() and may be inflated.");
|
|
@@ -6760,12 +7189,14 @@ function createImplementationOrchestrator(deps) {
|
|
|
6760
7189
|
const startedAt = storyState?.startedAt;
|
|
6761
7190
|
const completedAt = storyState?.completedAt ?? new Date().toISOString();
|
|
6762
7191
|
const wallClockSeconds = startedAt ? Math.round((new Date(completedAt).getTime() - new Date(startedAt).getTime()) / 1e3) : 0;
|
|
7192
|
+
const wallClockMs = startedAt ? new Date(completedAt).getTime() - new Date(startedAt).getTime() : 0;
|
|
6763
7193
|
const tokenAgg = aggregateTokenUsageForStory(db, config.pipelineRunId, storyKey);
|
|
7194
|
+
const phaseDurationsJson = buildPhaseDurationsJson(storyKey);
|
|
6764
7195
|
writeStoryMetrics(db, {
|
|
6765
7196
|
run_id: config.pipelineRunId,
|
|
6766
7197
|
story_key: storyKey,
|
|
6767
7198
|
result,
|
|
6768
|
-
phase_durations_json:
|
|
7199
|
+
phase_durations_json: phaseDurationsJson,
|
|
6769
7200
|
started_at: startedAt,
|
|
6770
7201
|
completed_at: completedAt,
|
|
6771
7202
|
wall_clock_seconds: wallClockSeconds,
|
|
@@ -6792,13 +7223,41 @@ function createImplementationOrchestrator(deps) {
|
|
|
6792
7223
|
rationale: `Story ${storyKey} completed with result=${result} in ${wallClockSeconds}s. Tokens: ${tokenAgg.input}+${tokenAgg.output}. Review cycles: ${reviewCycles}.`
|
|
6793
7224
|
});
|
|
6794
7225
|
} catch (decisionErr) {
|
|
6795
|
-
logger$
|
|
7226
|
+
logger$22.warn({
|
|
6796
7227
|
err: decisionErr,
|
|
6797
7228
|
storyKey
|
|
6798
7229
|
}, "Failed to write story-metrics decision (best-effort)");
|
|
6799
7230
|
}
|
|
7231
|
+
try {
|
|
7232
|
+
const phaseBreakdown = {};
|
|
7233
|
+
const starts = _phaseStartMs.get(storyKey);
|
|
7234
|
+
const ends = _phaseEndMs.get(storyKey);
|
|
7235
|
+
if (starts) {
|
|
7236
|
+
const nowMs = Date.now();
|
|
7237
|
+
for (const [phase, startMs] of starts) {
|
|
7238
|
+
const endMs = ends?.get(phase);
|
|
7239
|
+
phaseBreakdown[phase] = endMs !== void 0 ? endMs - startMs : nowMs - startMs;
|
|
7240
|
+
}
|
|
7241
|
+
}
|
|
7242
|
+
eventBus.emit("story:metrics", {
|
|
7243
|
+
storyKey,
|
|
7244
|
+
wallClockMs,
|
|
7245
|
+
phaseBreakdown,
|
|
7246
|
+
tokens: {
|
|
7247
|
+
input: tokenAgg.input,
|
|
7248
|
+
output: tokenAgg.output
|
|
7249
|
+
},
|
|
7250
|
+
reviewCycles,
|
|
7251
|
+
dispatches: _storyDispatches.get(storyKey) ?? 0
|
|
7252
|
+
});
|
|
7253
|
+
} catch (emitErr) {
|
|
7254
|
+
logger$22.warn({
|
|
7255
|
+
err: emitErr,
|
|
7256
|
+
storyKey
|
|
7257
|
+
}, "Failed to emit story:metrics event (best-effort)");
|
|
7258
|
+
}
|
|
6800
7259
|
} catch (err) {
|
|
6801
|
-
logger$
|
|
7260
|
+
logger$22.warn({
|
|
6802
7261
|
err,
|
|
6803
7262
|
storyKey
|
|
6804
7263
|
}, "Failed to write story metrics (best-effort)");
|
|
@@ -6827,7 +7286,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6827
7286
|
rationale: `Story ${storyKey} ${outcome} after ${reviewCycles} review cycle(s).`
|
|
6828
7287
|
});
|
|
6829
7288
|
} catch (err) {
|
|
6830
|
-
logger$
|
|
7289
|
+
logger$22.warn({
|
|
6831
7290
|
err,
|
|
6832
7291
|
storyKey
|
|
6833
7292
|
}, "Failed to write story-outcome decision (best-effort)");
|
|
@@ -6853,7 +7312,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6853
7312
|
rationale: `Escalation diagnosis for ${payload.storyKey}: ${diagnosis.recommendedAction} — ${diagnosis.rationale}`
|
|
6854
7313
|
});
|
|
6855
7314
|
} catch (err) {
|
|
6856
|
-
logger$
|
|
7315
|
+
logger$22.warn({
|
|
6857
7316
|
err,
|
|
6858
7317
|
storyKey: payload.storyKey
|
|
6859
7318
|
}, "Failed to persist escalation diagnosis (best-effort)");
|
|
@@ -6903,7 +7362,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6903
7362
|
token_usage_json: serialized
|
|
6904
7363
|
});
|
|
6905
7364
|
} catch (err) {
|
|
6906
|
-
logger$
|
|
7365
|
+
logger$22.warn("Failed to persist orchestrator state", { err });
|
|
6907
7366
|
}
|
|
6908
7367
|
}
|
|
6909
7368
|
function recordProgress() {
|
|
@@ -6950,7 +7409,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6950
7409
|
}
|
|
6951
7410
|
if (childActive) {
|
|
6952
7411
|
_lastProgressTs = Date.now();
|
|
6953
|
-
logger$
|
|
7412
|
+
logger$22.debug({
|
|
6954
7413
|
storyKey: key,
|
|
6955
7414
|
phase: s.phase,
|
|
6956
7415
|
childPids
|
|
@@ -6959,7 +7418,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
6959
7418
|
}
|
|
6960
7419
|
_stalledStories.add(key);
|
|
6961
7420
|
_storiesWithStall.add(key);
|
|
6962
|
-
logger$
|
|
7421
|
+
logger$22.warn({
|
|
6963
7422
|
storyKey: key,
|
|
6964
7423
|
phase: s.phase,
|
|
6965
7424
|
elapsedMs: elapsed,
|
|
@@ -7004,7 +7463,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7004
7463
|
for (let attempt = 0; attempt < MEMORY_PRESSURE_BACKOFF_MS.length; attempt++) {
|
|
7005
7464
|
const memState = dispatcher.getMemoryState();
|
|
7006
7465
|
if (!memState.isPressured) return true;
|
|
7007
|
-
logger$
|
|
7466
|
+
logger$22.warn({
|
|
7008
7467
|
storyKey,
|
|
7009
7468
|
freeMB: memState.freeMB,
|
|
7010
7469
|
thresholdMB: memState.thresholdMB,
|
|
@@ -7024,11 +7483,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
7024
7483
|
* exhausted retries the story is ESCALATED.
|
|
7025
7484
|
*/
|
|
7026
7485
|
async function processStory(storyKey) {
|
|
7027
|
-
logger$
|
|
7486
|
+
logger$22.info("Processing story", { storyKey });
|
|
7028
7487
|
{
|
|
7029
7488
|
const memoryOk = await checkMemoryPressure(storyKey);
|
|
7030
7489
|
if (!memoryOk) {
|
|
7031
|
-
logger$
|
|
7490
|
+
logger$22.warn({ storyKey }, "Memory pressure exhausted — escalating story without dispatch");
|
|
7032
7491
|
_stories.set(storyKey, {
|
|
7033
7492
|
phase: "ESCALATED",
|
|
7034
7493
|
reviewCycles: 0,
|
|
@@ -7062,14 +7521,14 @@ function createImplementationOrchestrator(deps) {
|
|
|
7062
7521
|
if (match) {
|
|
7063
7522
|
const candidatePath = join$1(artifactsDir, match);
|
|
7064
7523
|
const validation = await isValidStoryFile(candidatePath);
|
|
7065
|
-
if (!validation.valid) logger$
|
|
7524
|
+
if (!validation.valid) logger$22.warn({
|
|
7066
7525
|
storyKey,
|
|
7067
7526
|
storyFilePath: candidatePath,
|
|
7068
7527
|
reason: validation.reason
|
|
7069
7528
|
}, `Existing story file for ${storyKey} is invalid (${validation.reason}) — re-creating`);
|
|
7070
7529
|
else {
|
|
7071
7530
|
storyFilePath = candidatePath;
|
|
7072
|
-
logger$
|
|
7531
|
+
logger$22.info({
|
|
7073
7532
|
storyKey,
|
|
7074
7533
|
storyFilePath
|
|
7075
7534
|
}, "Found existing story file — skipping create-story");
|
|
@@ -7094,7 +7553,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7094
7553
|
pack,
|
|
7095
7554
|
contextCompiler,
|
|
7096
7555
|
dispatcher,
|
|
7097
|
-
projectRoot
|
|
7556
|
+
projectRoot,
|
|
7557
|
+
tokenCeilings
|
|
7098
7558
|
}, {
|
|
7099
7559
|
epicId: storyKey.split("-")[0] ?? storyKey,
|
|
7100
7560
|
storyKey,
|
|
@@ -7172,17 +7632,18 @@ function createImplementationOrchestrator(deps) {
|
|
|
7172
7632
|
pack,
|
|
7173
7633
|
contextCompiler,
|
|
7174
7634
|
dispatcher,
|
|
7175
|
-
projectRoot
|
|
7635
|
+
projectRoot,
|
|
7636
|
+
tokenCeilings
|
|
7176
7637
|
}, {
|
|
7177
7638
|
storyKey,
|
|
7178
7639
|
storyFilePath: storyFilePath ?? "",
|
|
7179
7640
|
pipelineRunId: config.pipelineRunId
|
|
7180
7641
|
});
|
|
7181
7642
|
testPlanPhaseResult = testPlanResult.result;
|
|
7182
|
-
if (testPlanResult.result === "success") logger$
|
|
7183
|
-
else logger$
|
|
7643
|
+
if (testPlanResult.result === "success") logger$22.info({ storyKey }, "Test plan generated successfully");
|
|
7644
|
+
else logger$22.warn({ storyKey }, "Test planning returned failed result — proceeding to dev-story without test plan");
|
|
7184
7645
|
} catch (err) {
|
|
7185
|
-
logger$
|
|
7646
|
+
logger$22.warn({
|
|
7186
7647
|
storyKey,
|
|
7187
7648
|
err
|
|
7188
7649
|
}, "Test planning failed — proceeding to dev-story without test plan");
|
|
@@ -7206,7 +7667,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7206
7667
|
try {
|
|
7207
7668
|
storyContentForAnalysis = await readFile$1(storyFilePath ?? "", "utf-8");
|
|
7208
7669
|
} catch (err) {
|
|
7209
|
-
logger$
|
|
7670
|
+
logger$22.error({
|
|
7210
7671
|
storyKey,
|
|
7211
7672
|
storyFilePath,
|
|
7212
7673
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -7214,7 +7675,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7214
7675
|
}
|
|
7215
7676
|
const analysis = analyzeStoryComplexity(storyContentForAnalysis);
|
|
7216
7677
|
const batches = planTaskBatches(analysis);
|
|
7217
|
-
logger$
|
|
7678
|
+
logger$22.info({
|
|
7218
7679
|
storyKey,
|
|
7219
7680
|
estimatedScope: analysis.estimatedScope,
|
|
7220
7681
|
batchCount: batches.length,
|
|
@@ -7232,7 +7693,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7232
7693
|
if (_state !== "RUNNING") break;
|
|
7233
7694
|
const taskScope = batch.taskIds.map((id, i) => `T${id}: ${batch.taskTitles[i] ?? ""}`).join("\n");
|
|
7234
7695
|
const priorFiles = allFilesModified.size > 0 ? Array.from(allFilesModified) : void 0;
|
|
7235
|
-
logger$
|
|
7696
|
+
logger$22.info({
|
|
7236
7697
|
storyKey,
|
|
7237
7698
|
batchIndex: batch.batchIndex,
|
|
7238
7699
|
taskCount: batch.taskIds.length
|
|
@@ -7246,7 +7707,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7246
7707
|
pack,
|
|
7247
7708
|
contextCompiler,
|
|
7248
7709
|
dispatcher,
|
|
7249
|
-
projectRoot
|
|
7710
|
+
projectRoot,
|
|
7711
|
+
tokenCeilings
|
|
7250
7712
|
}, {
|
|
7251
7713
|
storyKey,
|
|
7252
7714
|
storyFilePath: storyFilePath ?? "",
|
|
@@ -7256,7 +7718,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7256
7718
|
});
|
|
7257
7719
|
} catch (batchErr) {
|
|
7258
7720
|
const errMsg = batchErr instanceof Error ? batchErr.message : String(batchErr);
|
|
7259
|
-
logger$
|
|
7721
|
+
logger$22.warn({
|
|
7260
7722
|
storyKey,
|
|
7261
7723
|
batchIndex: batch.batchIndex,
|
|
7262
7724
|
error: errMsg
|
|
@@ -7276,7 +7738,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7276
7738
|
filesModified: batchFilesModified,
|
|
7277
7739
|
result: batchResult.result === "success" ? "success" : "failed"
|
|
7278
7740
|
};
|
|
7279
|
-
logger$
|
|
7741
|
+
logger$22.info(batchMetrics, "Batch dev-story metrics");
|
|
7280
7742
|
for (const f of batchFilesModified) allFilesModified.add(f);
|
|
7281
7743
|
if (batchFilesModified.length > 0) batchFileGroups.push({
|
|
7282
7744
|
batchIndex: batch.batchIndex,
|
|
@@ -7298,13 +7760,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
7298
7760
|
})
|
|
7299
7761
|
});
|
|
7300
7762
|
} catch (tokenErr) {
|
|
7301
|
-
logger$
|
|
7763
|
+
logger$22.warn({
|
|
7302
7764
|
storyKey,
|
|
7303
7765
|
batchIndex: batch.batchIndex,
|
|
7304
7766
|
err: tokenErr
|
|
7305
7767
|
}, "Failed to record batch token usage");
|
|
7306
7768
|
}
|
|
7307
|
-
if (batchResult.result === "failed") logger$
|
|
7769
|
+
if (batchResult.result === "failed") logger$22.warn({
|
|
7308
7770
|
storyKey,
|
|
7309
7771
|
batchIndex: batch.batchIndex,
|
|
7310
7772
|
error: batchResult.error
|
|
@@ -7325,7 +7787,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7325
7787
|
pack,
|
|
7326
7788
|
contextCompiler,
|
|
7327
7789
|
dispatcher,
|
|
7328
|
-
projectRoot
|
|
7790
|
+
projectRoot,
|
|
7791
|
+
tokenCeilings
|
|
7329
7792
|
}, {
|
|
7330
7793
|
storyKey,
|
|
7331
7794
|
storyFilePath: storyFilePath ?? "",
|
|
@@ -7339,7 +7802,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7339
7802
|
});
|
|
7340
7803
|
persistState();
|
|
7341
7804
|
if (devResult.result === "success") devStoryWasSuccess = true;
|
|
7342
|
-
else logger$
|
|
7805
|
+
else logger$22.warn("Dev-story reported failure, proceeding to code review", {
|
|
7343
7806
|
storyKey,
|
|
7344
7807
|
error: devResult.error,
|
|
7345
7808
|
filesModified: devFilesModified.length
|
|
@@ -7363,10 +7826,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
7363
7826
|
persistState();
|
|
7364
7827
|
return;
|
|
7365
7828
|
}
|
|
7829
|
+
let gitDiffFiles;
|
|
7366
7830
|
if (devStoryWasSuccess) {
|
|
7367
|
-
|
|
7368
|
-
if (
|
|
7369
|
-
logger$
|
|
7831
|
+
gitDiffFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
|
|
7832
|
+
if (gitDiffFiles.length === 0) {
|
|
7833
|
+
logger$22.warn({ storyKey }, "Zero-diff detected after COMPLETE dev-story — no file changes in git working tree");
|
|
7370
7834
|
eventBus.emit("orchestrator:zero-diff-escalation", {
|
|
7371
7835
|
storyKey,
|
|
7372
7836
|
reason: "zero-diff-on-complete"
|
|
@@ -7397,7 +7861,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7397
7861
|
});
|
|
7398
7862
|
if (buildVerifyResult.status === "passed") {
|
|
7399
7863
|
eventBus.emit("story:build-verification-passed", { storyKey });
|
|
7400
|
-
logger$
|
|
7864
|
+
logger$22.info({ storyKey }, "Build verification passed");
|
|
7401
7865
|
} else if (buildVerifyResult.status === "failed" || buildVerifyResult.status === "timeout") {
|
|
7402
7866
|
const truncatedOutput = (buildVerifyResult.output ?? "").slice(0, 2e3);
|
|
7403
7867
|
const reason = buildVerifyResult.reason ?? "build-verification-failed";
|
|
@@ -7406,7 +7870,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7406
7870
|
exitCode: buildVerifyResult.exitCode ?? 1,
|
|
7407
7871
|
output: truncatedOutput
|
|
7408
7872
|
});
|
|
7409
|
-
logger$
|
|
7873
|
+
logger$22.warn({
|
|
7410
7874
|
storyKey,
|
|
7411
7875
|
reason,
|
|
7412
7876
|
exitCode: buildVerifyResult.exitCode
|
|
@@ -7427,6 +7891,28 @@ function createImplementationOrchestrator(deps) {
|
|
|
7427
7891
|
return;
|
|
7428
7892
|
}
|
|
7429
7893
|
}
|
|
7894
|
+
try {
|
|
7895
|
+
const filesModified = gitDiffFiles ?? devFilesModified;
|
|
7896
|
+
if (filesModified.length > 0) {
|
|
7897
|
+
const icResult = detectInterfaceChanges({
|
|
7898
|
+
filesModified,
|
|
7899
|
+
projectRoot: projectRoot ?? process.cwd(),
|
|
7900
|
+
storyKey
|
|
7901
|
+
});
|
|
7902
|
+
if (icResult.potentiallyAffectedTests.length > 0) {
|
|
7903
|
+
logger$22.warn({
|
|
7904
|
+
storyKey,
|
|
7905
|
+
modifiedInterfaces: icResult.modifiedInterfaces,
|
|
7906
|
+
potentiallyAffectedTests: icResult.potentiallyAffectedTests
|
|
7907
|
+
}, "Interface change warning: modified exports may affect cross-module test mocks");
|
|
7908
|
+
eventBus.emit("story:interface-change-warning", {
|
|
7909
|
+
storyKey,
|
|
7910
|
+
modifiedInterfaces: icResult.modifiedInterfaces,
|
|
7911
|
+
potentiallyAffectedTests: icResult.potentiallyAffectedTests
|
|
7912
|
+
});
|
|
7913
|
+
}
|
|
7914
|
+
}
|
|
7915
|
+
} catch {}
|
|
7430
7916
|
let reviewCycles = 0;
|
|
7431
7917
|
let keepReviewing = true;
|
|
7432
7918
|
let timeoutRetried = false;
|
|
@@ -7459,7 +7945,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7459
7945
|
"NEEDS_MAJOR_REWORK": 2
|
|
7460
7946
|
};
|
|
7461
7947
|
for (const group of batchFileGroups) {
|
|
7462
|
-
logger$
|
|
7948
|
+
logger$22.info({
|
|
7463
7949
|
storyKey,
|
|
7464
7950
|
batchIndex: group.batchIndex,
|
|
7465
7951
|
fileCount: group.files.length
|
|
@@ -7470,7 +7956,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7470
7956
|
pack,
|
|
7471
7957
|
contextCompiler,
|
|
7472
7958
|
dispatcher,
|
|
7473
|
-
projectRoot
|
|
7959
|
+
projectRoot,
|
|
7960
|
+
tokenCeilings
|
|
7474
7961
|
}, {
|
|
7475
7962
|
storyKey,
|
|
7476
7963
|
storyFilePath: storyFilePath ?? "",
|
|
@@ -7496,7 +7983,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7496
7983
|
rawOutput: lastRawOutput,
|
|
7497
7984
|
tokenUsage: aggregateTokens
|
|
7498
7985
|
};
|
|
7499
|
-
logger$
|
|
7986
|
+
logger$22.info({
|
|
7500
7987
|
storyKey,
|
|
7501
7988
|
batchCount: batchFileGroups.length,
|
|
7502
7989
|
verdict: worstVerdict,
|
|
@@ -7509,7 +7996,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7509
7996
|
pack,
|
|
7510
7997
|
contextCompiler,
|
|
7511
7998
|
dispatcher,
|
|
7512
|
-
projectRoot
|
|
7999
|
+
projectRoot,
|
|
8000
|
+
tokenCeilings
|
|
7513
8001
|
}, {
|
|
7514
8002
|
storyKey,
|
|
7515
8003
|
storyFilePath: storyFilePath ?? "",
|
|
@@ -7522,7 +8010,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7522
8010
|
const isPhantomReview = reviewResult.dispatchFailed === true || reviewResult.verdict !== "SHIP_IT" && (reviewResult.issue_list === void 0 || reviewResult.issue_list.length === 0) && reviewResult.error !== void 0;
|
|
7523
8011
|
if (isPhantomReview && !timeoutRetried) {
|
|
7524
8012
|
timeoutRetried = true;
|
|
7525
|
-
logger$
|
|
8013
|
+
logger$22.warn({
|
|
7526
8014
|
storyKey,
|
|
7527
8015
|
reviewCycles,
|
|
7528
8016
|
error: reviewResult.error
|
|
@@ -7532,7 +8020,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7532
8020
|
verdict = reviewResult.verdict;
|
|
7533
8021
|
issueList = reviewResult.issue_list ?? [];
|
|
7534
8022
|
if (verdict === "NEEDS_MAJOR_REWORK" && reviewCycles > 0 && previousIssueList.length > 0 && issueList.length < previousIssueList.length) {
|
|
7535
|
-
logger$
|
|
8023
|
+
logger$22.info({
|
|
7536
8024
|
storyKey,
|
|
7537
8025
|
originalVerdict: verdict,
|
|
7538
8026
|
issuesBefore: previousIssueList.length,
|
|
@@ -7568,7 +8056,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7568
8056
|
if (_decomposition !== void 0) parts.push(`decomposed: ${_decomposition.batchCount} batches`);
|
|
7569
8057
|
parts.push(`${fileCount} files`);
|
|
7570
8058
|
parts.push(`${totalTokensK} tokens`);
|
|
7571
|
-
logger$
|
|
8059
|
+
logger$22.info({
|
|
7572
8060
|
storyKey,
|
|
7573
8061
|
verdict,
|
|
7574
8062
|
agentVerdict: reviewResult.agentVerdict
|
|
@@ -7611,7 +8099,8 @@ function createImplementationOrchestrator(deps) {
|
|
|
7611
8099
|
pack,
|
|
7612
8100
|
contextCompiler,
|
|
7613
8101
|
dispatcher,
|
|
7614
|
-
projectRoot
|
|
8102
|
+
projectRoot,
|
|
8103
|
+
tokenCeilings
|
|
7615
8104
|
}, {
|
|
7616
8105
|
storyKey,
|
|
7617
8106
|
storyFilePath: storyFilePath ?? "",
|
|
@@ -7619,7 +8108,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7619
8108
|
filesModified: devFilesModified,
|
|
7620
8109
|
workingDirectory: projectRoot
|
|
7621
8110
|
});
|
|
7622
|
-
logger$
|
|
8111
|
+
logger$22.debug({
|
|
7623
8112
|
storyKey,
|
|
7624
8113
|
expansion_priority: expansionResult.expansion_priority,
|
|
7625
8114
|
coverage_gaps: expansionResult.coverage_gaps.length
|
|
@@ -7632,7 +8121,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7632
8121
|
value: JSON.stringify(expansionResult)
|
|
7633
8122
|
});
|
|
7634
8123
|
} catch (expansionErr) {
|
|
7635
|
-
logger$
|
|
8124
|
+
logger$22.warn({
|
|
7636
8125
|
storyKey,
|
|
7637
8126
|
error: expansionErr instanceof Error ? expansionErr.message : String(expansionErr)
|
|
7638
8127
|
}, "Test expansion failed — story verdict unchanged");
|
|
@@ -7659,7 +8148,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7659
8148
|
persistState();
|
|
7660
8149
|
return;
|
|
7661
8150
|
}
|
|
7662
|
-
logger$
|
|
8151
|
+
logger$22.info({
|
|
7663
8152
|
storyKey,
|
|
7664
8153
|
reviewCycles: finalReviewCycles,
|
|
7665
8154
|
issueCount: issueList.length
|
|
@@ -7709,7 +8198,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7709
8198
|
fixPrompt = assembled.prompt;
|
|
7710
8199
|
} catch {
|
|
7711
8200
|
fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, minor fixes needed`;
|
|
7712
|
-
logger$
|
|
8201
|
+
logger$22.warn("Failed to assemble auto-approve fix prompt, using fallback", { storyKey });
|
|
7713
8202
|
}
|
|
7714
8203
|
const handle = dispatcher.dispatch({
|
|
7715
8204
|
prompt: fixPrompt,
|
|
@@ -7726,9 +8215,9 @@ function createImplementationOrchestrator(deps) {
|
|
|
7726
8215
|
output: fixResult.tokenEstimate.output
|
|
7727
8216
|
} : void 0 }
|
|
7728
8217
|
});
|
|
7729
|
-
if (fixResult.status === "timeout") logger$
|
|
8218
|
+
if (fixResult.status === "timeout") logger$22.warn("Auto-approve fix timed out — approving anyway (issues were minor)", { storyKey });
|
|
7730
8219
|
} catch (err) {
|
|
7731
|
-
logger$
|
|
8220
|
+
logger$22.warn("Auto-approve fix dispatch failed — approving anyway (issues were minor)", {
|
|
7732
8221
|
storyKey,
|
|
7733
8222
|
err
|
|
7734
8223
|
});
|
|
@@ -7758,9 +8247,15 @@ function createImplementationOrchestrator(deps) {
|
|
|
7758
8247
|
let fixPrompt;
|
|
7759
8248
|
const isMajorRework = taskType === "major-rework";
|
|
7760
8249
|
const templateName = isMajorRework ? "rework-story" : "fix-story";
|
|
8250
|
+
let fixMaxTurns;
|
|
7761
8251
|
try {
|
|
7762
8252
|
const fixTemplate = await pack.getPrompt(templateName);
|
|
7763
8253
|
const storyContent = await readFile$1(storyFilePath ?? "", "utf-8");
|
|
8254
|
+
if (isMajorRework) {
|
|
8255
|
+
const complexity = computeStoryComplexity(storyContent);
|
|
8256
|
+
fixMaxTurns = resolveFixStoryMaxTurns(complexity.complexityScore);
|
|
8257
|
+
logComplexityResult(storyKey, complexity, fixMaxTurns);
|
|
8258
|
+
}
|
|
7764
8259
|
let reviewFeedback;
|
|
7765
8260
|
if (issueList.length === 0) reviewFeedback = isMajorRework ? [
|
|
7766
8261
|
`Verdict: ${verdict}`,
|
|
@@ -7831,7 +8326,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7831
8326
|
fixPrompt = assembled.prompt;
|
|
7832
8327
|
} catch {
|
|
7833
8328
|
fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, taskType=${taskType}`;
|
|
7834
|
-
logger$
|
|
8329
|
+
logger$22.warn("Failed to assemble fix prompt, using fallback", {
|
|
7835
8330
|
storyKey,
|
|
7836
8331
|
taskType
|
|
7837
8332
|
});
|
|
@@ -7843,6 +8338,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7843
8338
|
taskType,
|
|
7844
8339
|
...fixModel !== void 0 ? { model: fixModel } : {},
|
|
7845
8340
|
outputSchema: DevStoryResultSchema,
|
|
8341
|
+
...fixMaxTurns !== void 0 ? { maxTurns: fixMaxTurns } : {},
|
|
7846
8342
|
...projectRoot !== void 0 ? { workingDirectory: projectRoot } : {}
|
|
7847
8343
|
}) : dispatcher.dispatch({
|
|
7848
8344
|
prompt: fixPrompt,
|
|
@@ -7861,7 +8357,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7861
8357
|
} : void 0 }
|
|
7862
8358
|
});
|
|
7863
8359
|
if (fixResult.status === "timeout") {
|
|
7864
|
-
logger$
|
|
8360
|
+
logger$22.warn("Fix dispatch timed out — escalating story", {
|
|
7865
8361
|
storyKey,
|
|
7866
8362
|
taskType
|
|
7867
8363
|
});
|
|
@@ -7883,7 +8379,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7883
8379
|
}
|
|
7884
8380
|
if (fixResult.status === "failed") {
|
|
7885
8381
|
if (isMajorRework) {
|
|
7886
|
-
logger$
|
|
8382
|
+
logger$22.warn("Major rework dispatch failed — escalating story", {
|
|
7887
8383
|
storyKey,
|
|
7888
8384
|
exitCode: fixResult.exitCode
|
|
7889
8385
|
});
|
|
@@ -7903,14 +8399,14 @@ function createImplementationOrchestrator(deps) {
|
|
|
7903
8399
|
persistState();
|
|
7904
8400
|
return;
|
|
7905
8401
|
}
|
|
7906
|
-
logger$
|
|
8402
|
+
logger$22.warn("Fix dispatch failed", {
|
|
7907
8403
|
storyKey,
|
|
7908
8404
|
taskType,
|
|
7909
8405
|
exitCode: fixResult.exitCode
|
|
7910
8406
|
});
|
|
7911
8407
|
}
|
|
7912
8408
|
} catch (err) {
|
|
7913
|
-
logger$
|
|
8409
|
+
logger$22.warn("Fix dispatch failed, continuing to next review", {
|
|
7914
8410
|
storyKey,
|
|
7915
8411
|
taskType,
|
|
7916
8412
|
err
|
|
@@ -7973,11 +8469,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
7973
8469
|
}
|
|
7974
8470
|
async function run(storyKeys) {
|
|
7975
8471
|
if (_state === "RUNNING" || _state === "PAUSED") {
|
|
7976
|
-
logger$
|
|
8472
|
+
logger$22.warn("run() called while orchestrator is already running or paused — ignoring", { state: _state });
|
|
7977
8473
|
return getStatus();
|
|
7978
8474
|
}
|
|
7979
8475
|
if (_state === "COMPLETE") {
|
|
7980
|
-
logger$
|
|
8476
|
+
logger$22.warn("run() called on a COMPLETE orchestrator — ignoring", { state: _state });
|
|
7981
8477
|
return getStatus();
|
|
7982
8478
|
}
|
|
7983
8479
|
_state = "RUNNING";
|
|
@@ -7995,13 +8491,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
7995
8491
|
if (config.enableHeartbeat) startHeartbeat();
|
|
7996
8492
|
if (projectRoot !== void 0) {
|
|
7997
8493
|
const seedResult = seedMethodologyContext(db, projectRoot);
|
|
7998
|
-
if (seedResult.decisionsCreated > 0) logger$
|
|
8494
|
+
if (seedResult.decisionsCreated > 0) logger$22.info({
|
|
7999
8495
|
decisionsCreated: seedResult.decisionsCreated,
|
|
8000
8496
|
skippedCategories: seedResult.skippedCategories
|
|
8001
8497
|
}, "Methodology context seeded from planning artifacts");
|
|
8002
8498
|
}
|
|
8003
8499
|
const groups = detectConflictGroups(storyKeys, { moduleMap: pack.manifest.conflictGroups });
|
|
8004
|
-
logger$
|
|
8500
|
+
logger$22.info("Orchestrator starting", {
|
|
8005
8501
|
storyCount: storyKeys.length,
|
|
8006
8502
|
groupCount: groups.length,
|
|
8007
8503
|
maxConcurrency: config.maxConcurrency
|
|
@@ -8013,7 +8509,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8013
8509
|
_state = "FAILED";
|
|
8014
8510
|
_completedAt = new Date().toISOString();
|
|
8015
8511
|
persistState();
|
|
8016
|
-
logger$
|
|
8512
|
+
logger$22.error("Orchestrator failed with unhandled error", { err });
|
|
8017
8513
|
return getStatus();
|
|
8018
8514
|
}
|
|
8019
8515
|
stopHeartbeat();
|
|
@@ -8040,7 +8536,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8040
8536
|
_pauseGate = createPauseGate();
|
|
8041
8537
|
_state = "PAUSED";
|
|
8042
8538
|
eventBus.emit("orchestrator:paused", {});
|
|
8043
|
-
logger$
|
|
8539
|
+
logger$22.info("Orchestrator paused");
|
|
8044
8540
|
}
|
|
8045
8541
|
function resume() {
|
|
8046
8542
|
if (_state !== "PAUSED") return;
|
|
@@ -8051,7 +8547,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8051
8547
|
}
|
|
8052
8548
|
_state = "RUNNING";
|
|
8053
8549
|
eventBus.emit("orchestrator:resumed", {});
|
|
8054
|
-
logger$
|
|
8550
|
+
logger$22.info("Orchestrator resumed");
|
|
8055
8551
|
}
|
|
8056
8552
|
return {
|
|
8057
8553
|
run,
|
|
@@ -12530,6 +13026,27 @@ async function runRunAction(options) {
|
|
|
12530
13026
|
output: payload.output
|
|
12531
13027
|
});
|
|
12532
13028
|
});
|
|
13029
|
+
eventBus.on("story:interface-change-warning", (payload) => {
|
|
13030
|
+
ndjsonEmitter.emit({
|
|
13031
|
+
type: "story:interface-change-warning",
|
|
13032
|
+
ts: new Date().toISOString(),
|
|
13033
|
+
storyKey: payload.storyKey,
|
|
13034
|
+
modifiedInterfaces: payload.modifiedInterfaces,
|
|
13035
|
+
potentiallyAffectedTests: payload.potentiallyAffectedTests
|
|
13036
|
+
});
|
|
13037
|
+
});
|
|
13038
|
+
eventBus.on("story:metrics", (payload) => {
|
|
13039
|
+
ndjsonEmitter.emit({
|
|
13040
|
+
type: "story:metrics",
|
|
13041
|
+
ts: new Date().toISOString(),
|
|
13042
|
+
storyKey: payload.storyKey,
|
|
13043
|
+
wallClockMs: payload.wallClockMs,
|
|
13044
|
+
phaseBreakdown: payload.phaseBreakdown,
|
|
13045
|
+
tokens: payload.tokens,
|
|
13046
|
+
reviewCycles: payload.reviewCycles,
|
|
13047
|
+
dispatches: payload.dispatches
|
|
13048
|
+
});
|
|
13049
|
+
});
|
|
12533
13050
|
}
|
|
12534
13051
|
const orchestrator = createImplementationOrchestrator({
|
|
12535
13052
|
db,
|
|
@@ -12983,5 +13500,5 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
12983
13500
|
}
|
|
12984
13501
|
|
|
12985
13502
|
//#endregion
|
|
12986
|
-
export { DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createContextCompiler, createDispatcher, createImplementationOrchestrator, createPackLoader, createPhaseOrchestrator, createStopAfterGate, findPackageRoot, formatOutput, formatPhaseCompletionSummary, formatPipelineStatusHuman, formatPipelineSummary, formatTokenTelemetry, getAllDescendantPids, getAutoHealthData, getSubstrateDefaultSettings, registerHealthCommand, registerRunCommand, resolveBmadMethodSrcPath, resolveBmadMethodVersion, resolveMainRepoRoot, runAnalysisPhase, runMigrations, runPlanningPhase, runRunAction, runSolutioningPhase, validateStopAfterFromConflict };
|
|
12987
|
-
//# sourceMappingURL=run-
|
|
13503
|
+
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 };
|
|
13504
|
+
//# sourceMappingURL=run-BCyrbL3w.js.map
|