substrate-ai 0.2.28 → 0.2.30
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/{errors-CswS7Mzg.js → adapter-registry-PsWhP_1Q.js} +2 -111
- package/dist/cli/index.js +30 -333
- package/dist/config-migrator-DSi8KhQC.js +244 -0
- package/dist/{experimenter-bc40oi8p.js → experimenter-f_Y1rreV.js} +1 -1
- package/dist/{helpers-DljGJnFF.js → helpers-RL22dYtn.js} +111 -2
- package/dist/index.js +2 -2
- package/dist/{run-BCyrbL3w.js → run-Ajt187oE.js} +451 -93
- package/dist/run-KBcR3Jpi.js +8 -0
- package/dist/{upgrade-DzpjKYlD.js → upgrade-CImByfkk.js} +3 -2
- package/dist/{upgrade-CJ0JFQ2c.js → upgrade-Cvwtnwl4.js} +2 -2
- package/dist/{version-manager-impl-CtzNu7YZ.js → version-manager-impl-CizNmmLT.js} +3 -242
- package/dist/version-manager-impl-aL5IemIm.js +4 -0
- package/package.json +1 -1
- package/packs/bmad/prompts/fix-story.md +5 -0
- package/dist/run-CIgIYtKf.js +0 -7
- package/dist/version-manager-impl-BDfiGXWX.js +0 -3
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { createLogger } from "./logger-D2fS2ccL.js";
|
|
2
|
-
import {
|
|
1
|
+
import { createLogger, deepMask } from "./logger-D2fS2ccL.js";
|
|
2
|
+
import { CURRENT_CONFIG_FORMAT_VERSION, PartialSubstrateConfigSchema, SUPPORTED_CONFIG_FORMAT_VERSIONS, SubstrateConfigSchema, defaultConfigMigrator } from "./config-migrator-DSi8KhQC.js";
|
|
3
|
+
import { ConfigError, ConfigIncompatibleFormatError, createEventBus, createTuiApp, isTuiCapable, printNonTtyWarning, sleep } from "./helpers-RL22dYtn.js";
|
|
3
4
|
import { addTokenUsage, createDecision, createPipelineRun, createRequirement, getArtifactByTypeForRun, getArtifactsByRun, getDecisionsByCategory, getDecisionsByPhase, getDecisionsByPhaseForRun, getLatestRun, getPipelineRunById, getRunningPipelineRuns, getTokenUsageSummary, registerArtifact, updatePipelineRun, updatePipelineRunConfig, upsertDecision } from "./decisions-Dq4cAA2L.js";
|
|
4
5
|
import { ESCALATION_DIAGNOSIS, OPERATIONAL_FINDING, STORY_METRICS, STORY_OUTCOME, TEST_EXPANSION_FINDING, TEST_PLAN, aggregateTokenUsageForRun, aggregateTokenUsageForStory, getStoryMetricsForRun, writeRunMetrics, writeStoryMetrics } from "./operational-CnMlvWqc.js";
|
|
5
6
|
import { createRequire } from "module";
|
|
6
|
-
import { dirname, join } from "path";
|
|
7
|
-
import { access, readFile, readdir, stat } from "fs/promises";
|
|
7
|
+
import { dirname, join, resolve } from "path";
|
|
8
|
+
import { access, mkdir, readFile, readdir, stat, writeFile } from "fs/promises";
|
|
8
9
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
9
10
|
import yaml from "js-yaml";
|
|
10
11
|
import { createRequire as createRequire$1 } from "node:module";
|
|
@@ -14,6 +15,7 @@ import { dirname as dirname$1, join as join$1, resolve as resolve$1 } from "node
|
|
|
14
15
|
import BetterSqlite3 from "better-sqlite3";
|
|
15
16
|
import { fileURLToPath } from "node:url";
|
|
16
17
|
import { existsSync as existsSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1 } from "node:fs";
|
|
18
|
+
import { homedir } from "os";
|
|
17
19
|
import { freemem, platform } from "node:os";
|
|
18
20
|
import { createHash, randomUUID } from "node:crypto";
|
|
19
21
|
import { readFile as readFile$1, stat as stat$1 } from "node:fs/promises";
|
|
@@ -21,6 +23,70 @@ import { readFile as readFile$1, stat as stat$1 } from "node:fs/promises";
|
|
|
21
23
|
//#region rolldown:runtime
|
|
22
24
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
23
25
|
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/modules/config/defaults.ts
|
|
28
|
+
const DEFAULT_CLAUDE_PROVIDER = {
|
|
29
|
+
enabled: false,
|
|
30
|
+
subscription_routing: "auto",
|
|
31
|
+
max_concurrent: 2,
|
|
32
|
+
rate_limit: {
|
|
33
|
+
tokens: 22e4,
|
|
34
|
+
window_seconds: 18e3
|
|
35
|
+
},
|
|
36
|
+
api_key_env: "ANTHROPIC_API_KEY",
|
|
37
|
+
api_billing: false
|
|
38
|
+
};
|
|
39
|
+
const DEFAULT_CODEX_PROVIDER = {
|
|
40
|
+
enabled: false,
|
|
41
|
+
subscription_routing: "api",
|
|
42
|
+
max_concurrent: 2,
|
|
43
|
+
api_key_env: "OPENAI_API_KEY",
|
|
44
|
+
api_billing: true
|
|
45
|
+
};
|
|
46
|
+
const DEFAULT_GEMINI_PROVIDER = {
|
|
47
|
+
enabled: false,
|
|
48
|
+
subscription_routing: "api",
|
|
49
|
+
max_concurrent: 2,
|
|
50
|
+
api_key_env: "GOOGLE_API_KEY",
|
|
51
|
+
api_billing: true
|
|
52
|
+
};
|
|
53
|
+
const DEFAULT_GLOBAL_SETTINGS = {
|
|
54
|
+
log_level: "info",
|
|
55
|
+
max_concurrent_tasks: 4,
|
|
56
|
+
budget_cap_tokens: 0,
|
|
57
|
+
budget_cap_usd: 0
|
|
58
|
+
};
|
|
59
|
+
const DEFAULT_ROUTING_POLICY = {
|
|
60
|
+
default_provider: "claude",
|
|
61
|
+
rules: [
|
|
62
|
+
{
|
|
63
|
+
task_type: "planning",
|
|
64
|
+
preferred_provider: "claude",
|
|
65
|
+
fallback_providers: ["gemini", "codex"]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
task_type: "coding",
|
|
69
|
+
preferred_provider: "claude",
|
|
70
|
+
fallback_providers: ["codex", "gemini"]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
task_type: "review",
|
|
74
|
+
preferred_provider: "claude",
|
|
75
|
+
fallback_providers: ["gemini"]
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
};
|
|
79
|
+
const DEFAULT_CONFIG = {
|
|
80
|
+
config_format_version: "1",
|
|
81
|
+
task_graph_version: "1",
|
|
82
|
+
global: DEFAULT_GLOBAL_SETTINGS,
|
|
83
|
+
providers: {
|
|
84
|
+
claude: DEFAULT_CLAUDE_PROVIDER,
|
|
85
|
+
codex: DEFAULT_CODEX_PROVIDER,
|
|
86
|
+
gemini: DEFAULT_GEMINI_PROVIDER
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
24
90
|
//#endregion
|
|
25
91
|
//#region src/utils/git-root.ts
|
|
26
92
|
/**
|
|
@@ -539,7 +605,7 @@ const migration010RunMetrics = {
|
|
|
539
605
|
|
|
540
606
|
//#endregion
|
|
541
607
|
//#region src/persistence/migrations/index.ts
|
|
542
|
-
const logger$
|
|
608
|
+
const logger$22 = createLogger("persistence:migrations");
|
|
543
609
|
const MIGRATIONS = [
|
|
544
610
|
initialSchemaMigration,
|
|
545
611
|
costTrackerSchemaMigration,
|
|
@@ -557,7 +623,7 @@ const MIGRATIONS = [
|
|
|
557
623
|
* Safe to call multiple times — already-applied migrations are skipped.
|
|
558
624
|
*/
|
|
559
625
|
function runMigrations(db) {
|
|
560
|
-
logger$
|
|
626
|
+
logger$22.info("Starting migration runner");
|
|
561
627
|
db.exec(`
|
|
562
628
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
563
629
|
version INTEGER PRIMARY KEY,
|
|
@@ -568,12 +634,12 @@ function runMigrations(db) {
|
|
|
568
634
|
const appliedVersions = new Set(db.prepare("SELECT version FROM schema_migrations").all().map((row) => row.version));
|
|
569
635
|
const pending = MIGRATIONS.filter((m) => !appliedVersions.has(m.version)).sort((a, b) => a.version - b.version);
|
|
570
636
|
if (pending.length === 0) {
|
|
571
|
-
logger$
|
|
637
|
+
logger$22.info("No pending migrations");
|
|
572
638
|
return;
|
|
573
639
|
}
|
|
574
640
|
const insertMigration = db.prepare("INSERT INTO schema_migrations (version, name) VALUES (?, ?)");
|
|
575
641
|
for (const migration of pending) {
|
|
576
|
-
logger$
|
|
642
|
+
logger$22.info({
|
|
577
643
|
version: migration.version,
|
|
578
644
|
name: migration.name
|
|
579
645
|
}, "Applying migration");
|
|
@@ -587,14 +653,14 @@ function runMigrations(db) {
|
|
|
587
653
|
});
|
|
588
654
|
applyMigration();
|
|
589
655
|
}
|
|
590
|
-
logger$
|
|
656
|
+
logger$22.info({ version: migration.version }, "Migration applied successfully");
|
|
591
657
|
}
|
|
592
|
-
logger$
|
|
658
|
+
logger$22.info({ count: pending.length }, "All pending migrations applied");
|
|
593
659
|
}
|
|
594
660
|
|
|
595
661
|
//#endregion
|
|
596
662
|
//#region src/persistence/database.ts
|
|
597
|
-
const logger$
|
|
663
|
+
const logger$21 = createLogger("persistence:database");
|
|
598
664
|
/**
|
|
599
665
|
* Thin wrapper that opens a SQLite database, applies required PRAGMAs,
|
|
600
666
|
* and exposes the raw BetterSqlite3 instance.
|
|
@@ -611,14 +677,14 @@ var DatabaseWrapper = class {
|
|
|
611
677
|
*/
|
|
612
678
|
open() {
|
|
613
679
|
if (this._db !== null) return;
|
|
614
|
-
logger$
|
|
680
|
+
logger$21.info({ path: this._path }, "Opening SQLite database");
|
|
615
681
|
this._db = new BetterSqlite3(this._path);
|
|
616
682
|
const walResult = this._db.pragma("journal_mode = WAL");
|
|
617
|
-
if (walResult?.[0]?.journal_mode !== "wal") logger$
|
|
683
|
+
if (walResult?.[0]?.journal_mode !== "wal") logger$21.warn({ result: walResult?.[0]?.journal_mode }, "WAL pragma did not return expected \"wal\" — journal_mode may be \"memory\" or unsupported");
|
|
618
684
|
this._db.pragma("busy_timeout = 5000");
|
|
619
685
|
this._db.pragma("synchronous = NORMAL");
|
|
620
686
|
this._db.pragma("foreign_keys = ON");
|
|
621
|
-
logger$
|
|
687
|
+
logger$21.info({ path: this._path }, "SQLite database opened with WAL mode");
|
|
622
688
|
}
|
|
623
689
|
/**
|
|
624
690
|
* Close the database. Idempotent — calling close() when already closed is a no-op.
|
|
@@ -627,7 +693,7 @@ var DatabaseWrapper = class {
|
|
|
627
693
|
if (this._db === null) return;
|
|
628
694
|
this._db.close();
|
|
629
695
|
this._db = null;
|
|
630
|
-
logger$
|
|
696
|
+
logger$21.info({ path: this._path }, "SQLite database closed");
|
|
631
697
|
}
|
|
632
698
|
/**
|
|
633
699
|
* Return the raw BetterSqlite3 instance.
|
|
@@ -1361,6 +1427,246 @@ function formatPipelineSummary(run, tokenSummary, decisionsCount, storiesCount,
|
|
|
1361
1427
|
return lines.join("\n");
|
|
1362
1428
|
}
|
|
1363
1429
|
|
|
1430
|
+
//#endregion
|
|
1431
|
+
//#region src/modules/config/version-utils.ts
|
|
1432
|
+
/**
|
|
1433
|
+
* Check whether a version string is in a list of supported versions.
|
|
1434
|
+
*
|
|
1435
|
+
* @param version - Version string to check
|
|
1436
|
+
* @param supported - List of supported version strings
|
|
1437
|
+
* @returns true if version is in the supported list
|
|
1438
|
+
*/
|
|
1439
|
+
function isVersionSupported(version, supported) {
|
|
1440
|
+
return supported.includes(version);
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Format the standard "unsupported version" error message.
|
|
1444
|
+
*
|
|
1445
|
+
* @param formatType - 'config' or 'task_graph'
|
|
1446
|
+
* @param version - The unsupported version string found
|
|
1447
|
+
* @param supported - List of supported version strings
|
|
1448
|
+
* @returns Formatted error message string
|
|
1449
|
+
*/
|
|
1450
|
+
function formatUnsupportedVersionError(formatType, version, supported) {
|
|
1451
|
+
if (formatType === "config") return `Configuration format version "${version}" is not supported. This toolkit supports: ${supported.join(", ")}. Please upgrade the toolkit: npm install -g substrate@latest`;
|
|
1452
|
+
return `Task graph format version "${version}" is not supported. This toolkit supports: ${supported.join(", ")}. Please upgrade the toolkit: npm install -g substrate@latest`;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
//#endregion
|
|
1456
|
+
//#region src/modules/config/config-system-impl.ts
|
|
1457
|
+
const logger$20 = createLogger("config");
|
|
1458
|
+
function deepMerge(base, override) {
|
|
1459
|
+
const result = { ...base };
|
|
1460
|
+
for (const [key, val] of Object.entries(override)) if (val !== null && val !== void 0 && typeof val === "object" && !Array.isArray(val) && typeof result[key] === "object" && result[key] !== null && !Array.isArray(result[key])) result[key] = deepMerge(result[key], val);
|
|
1461
|
+
else if (val !== void 0) result[key] = val;
|
|
1462
|
+
return result;
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Map of ADT_ environment variable names to config paths.
|
|
1466
|
+
* Only overrides scalar values; does not support nested structures via env.
|
|
1467
|
+
*/
|
|
1468
|
+
const ENV_VAR_MAP = {
|
|
1469
|
+
ADT_LOG_LEVEL: "global.log_level",
|
|
1470
|
+
ADT_MAX_CONCURRENT_TASKS: "global.max_concurrent_tasks",
|
|
1471
|
+
ADT_BUDGET_CAP_TOKENS: "global.budget_cap_tokens",
|
|
1472
|
+
ADT_BUDGET_CAP_USD: "global.budget_cap_usd",
|
|
1473
|
+
ADT_WORKSPACE_DIR: "global.workspace_dir",
|
|
1474
|
+
ADT_CLAUDE_ENABLED: "providers.claude.enabled",
|
|
1475
|
+
ADT_CODEX_ENABLED: "providers.codex.enabled",
|
|
1476
|
+
ADT_GEMINI_ENABLED: "providers.gemini.enabled"
|
|
1477
|
+
};
|
|
1478
|
+
/**
|
|
1479
|
+
* Read relevant environment variables and return a partial config overlay.
|
|
1480
|
+
*/
|
|
1481
|
+
function readEnvOverrides() {
|
|
1482
|
+
const overrides = {};
|
|
1483
|
+
for (const [envKey, configPath] of Object.entries(ENV_VAR_MAP)) {
|
|
1484
|
+
const rawValue = process.env[envKey];
|
|
1485
|
+
if (rawValue === void 0) continue;
|
|
1486
|
+
const parts = configPath.split(".");
|
|
1487
|
+
let cursor = overrides;
|
|
1488
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1489
|
+
const part = parts[i] ?? "";
|
|
1490
|
+
if (cursor[part] === void 0) cursor[part] = {};
|
|
1491
|
+
cursor = cursor[part];
|
|
1492
|
+
}
|
|
1493
|
+
const lastKey = parts[parts.length - 1] ?? "";
|
|
1494
|
+
if (rawValue === "true") cursor[lastKey] = true;
|
|
1495
|
+
else if (rawValue === "false") cursor[lastKey] = false;
|
|
1496
|
+
else if (/^\d+$/.test(rawValue)) cursor[lastKey] = parseInt(rawValue, 10);
|
|
1497
|
+
else if (/^\d*\.\d+$/.test(rawValue)) cursor[lastKey] = parseFloat(rawValue);
|
|
1498
|
+
else cursor[lastKey] = rawValue;
|
|
1499
|
+
}
|
|
1500
|
+
const parsed = PartialSubstrateConfigSchema.safeParse(overrides);
|
|
1501
|
+
if (!parsed.success) {
|
|
1502
|
+
logger$20.warn({ errors: parsed.error.issues }, "Invalid environment variable overrides ignored");
|
|
1503
|
+
return {};
|
|
1504
|
+
}
|
|
1505
|
+
return parsed.data;
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Get a value from a nested object using dot-notation key.
|
|
1509
|
+
*/
|
|
1510
|
+
function getByPath(obj, path$1) {
|
|
1511
|
+
const parts = path$1.split(".");
|
|
1512
|
+
let cursor = obj;
|
|
1513
|
+
for (const part of parts) {
|
|
1514
|
+
if (cursor === null || cursor === void 0 || typeof cursor !== "object") return void 0;
|
|
1515
|
+
cursor = cursor[part];
|
|
1516
|
+
}
|
|
1517
|
+
return cursor;
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Return a deep clone of `obj` with `path` set to `value`.
|
|
1521
|
+
* Creates intermediate objects as needed.
|
|
1522
|
+
*/
|
|
1523
|
+
function setByPath(obj, path$1, value) {
|
|
1524
|
+
const parts = path$1.split(".");
|
|
1525
|
+
const result = { ...obj };
|
|
1526
|
+
let cursor = result;
|
|
1527
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1528
|
+
const part = parts[i] ?? "";
|
|
1529
|
+
const existing = cursor[part];
|
|
1530
|
+
if (existing === null || existing === void 0 || typeof existing !== "object") cursor[part] = {};
|
|
1531
|
+
else cursor[part] = { ...existing };
|
|
1532
|
+
cursor = cursor[part];
|
|
1533
|
+
}
|
|
1534
|
+
const lastKey = parts[parts.length - 1] ?? "";
|
|
1535
|
+
cursor[lastKey] = value;
|
|
1536
|
+
return result;
|
|
1537
|
+
}
|
|
1538
|
+
var ConfigSystemImpl = class {
|
|
1539
|
+
_config = null;
|
|
1540
|
+
_projectConfigDir;
|
|
1541
|
+
_globalConfigDir;
|
|
1542
|
+
_cliOverrides;
|
|
1543
|
+
constructor(options = {}) {
|
|
1544
|
+
this._projectConfigDir = options.projectConfigDir ? resolve(options.projectConfigDir) : resolve(process.cwd(), ".substrate");
|
|
1545
|
+
this._globalConfigDir = options.globalConfigDir ? resolve(options.globalConfigDir) : resolve(homedir(), ".substrate");
|
|
1546
|
+
this._cliOverrides = options.cliOverrides ?? {};
|
|
1547
|
+
}
|
|
1548
|
+
get isLoaded() {
|
|
1549
|
+
return this._config !== null;
|
|
1550
|
+
}
|
|
1551
|
+
async load() {
|
|
1552
|
+
let merged = structuredClone(DEFAULT_CONFIG);
|
|
1553
|
+
const globalConfig = await this._loadYamlFile(join(this._globalConfigDir, "config.yaml"));
|
|
1554
|
+
if (globalConfig !== null) merged = deepMerge(merged, globalConfig);
|
|
1555
|
+
const projectConfig = await this._loadYamlFile(join(this._projectConfigDir, "config.yaml"));
|
|
1556
|
+
if (projectConfig !== null) merged = deepMerge(merged, projectConfig);
|
|
1557
|
+
const envOverrides = readEnvOverrides();
|
|
1558
|
+
if (Object.keys(envOverrides).length > 0) merged = deepMerge(merged, envOverrides);
|
|
1559
|
+
if (Object.keys(this._cliOverrides).length > 0) merged = deepMerge(merged, this._cliOverrides);
|
|
1560
|
+
const result = SubstrateConfigSchema.safeParse(merged);
|
|
1561
|
+
if (!result.success) {
|
|
1562
|
+
const issues = result.error.issues.map((issue) => ` • ${issue.path.join(".")}: ${issue.message}`).join("\n");
|
|
1563
|
+
throw new ConfigError(`Configuration validation failed:\n${issues}`, { issues: result.error.issues });
|
|
1564
|
+
}
|
|
1565
|
+
this._config = result.data;
|
|
1566
|
+
logger$20.debug("Configuration loaded successfully");
|
|
1567
|
+
}
|
|
1568
|
+
getConfig() {
|
|
1569
|
+
if (this._config === null) throw new ConfigError("Configuration has not been loaded. Call load() before getConfig().", {});
|
|
1570
|
+
return this._config;
|
|
1571
|
+
}
|
|
1572
|
+
get(key) {
|
|
1573
|
+
const config = this.getConfig();
|
|
1574
|
+
return getByPath(config, key);
|
|
1575
|
+
}
|
|
1576
|
+
async set(key, value) {
|
|
1577
|
+
const current = this.getConfig();
|
|
1578
|
+
const existing = getByPath(current, key);
|
|
1579
|
+
if (existing === void 0) throw new ConfigError(`Unknown config key: ${key}`, { key });
|
|
1580
|
+
if (typeof existing === "object" && existing !== null) throw new ConfigError(`Cannot set object key "${key}" — use a more specific dot-notation path`, { key });
|
|
1581
|
+
let projectConfigRaw = {};
|
|
1582
|
+
const projectConfigPath = join(this._projectConfigDir, "config.yaml");
|
|
1583
|
+
const existingYaml = await this._loadYamlFile(projectConfigPath);
|
|
1584
|
+
if (existingYaml !== null) projectConfigRaw = existingYaml;
|
|
1585
|
+
const updated = setByPath(projectConfigRaw, key, value);
|
|
1586
|
+
const partial = PartialSubstrateConfigSchema.safeParse(updated);
|
|
1587
|
+
if (!partial.success) {
|
|
1588
|
+
const issues = partial.error.issues.map((i) => ` • ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
1589
|
+
throw new ConfigError(`Invalid value for "${key}":\n${issues}`, {
|
|
1590
|
+
key,
|
|
1591
|
+
value,
|
|
1592
|
+
issues: partial.error.issues
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
await mkdir(this._projectConfigDir, { recursive: true });
|
|
1596
|
+
const yamlStr = yaml.dump(updated);
|
|
1597
|
+
await writeFile(projectConfigPath, yamlStr, "utf-8");
|
|
1598
|
+
await this.load();
|
|
1599
|
+
}
|
|
1600
|
+
getMasked() {
|
|
1601
|
+
const config = this.getConfig();
|
|
1602
|
+
return deepMask(config);
|
|
1603
|
+
}
|
|
1604
|
+
getConfigFormatVersion() {
|
|
1605
|
+
return CURRENT_CONFIG_FORMAT_VERSION;
|
|
1606
|
+
}
|
|
1607
|
+
isCompatible(version) {
|
|
1608
|
+
return SUPPORTED_CONFIG_FORMAT_VERSIONS.includes(version);
|
|
1609
|
+
}
|
|
1610
|
+
async _fileExists(filePath) {
|
|
1611
|
+
try {
|
|
1612
|
+
await access(filePath);
|
|
1613
|
+
return true;
|
|
1614
|
+
} catch {
|
|
1615
|
+
return false;
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
async _loadYamlFile(filePath) {
|
|
1619
|
+
if (!await this._fileExists(filePath)) return null;
|
|
1620
|
+
try {
|
|
1621
|
+
const raw = await readFile(filePath, "utf-8");
|
|
1622
|
+
let parsed = yaml.load(raw);
|
|
1623
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1624
|
+
const rawObj = parsed;
|
|
1625
|
+
const version = rawObj["config_format_version"];
|
|
1626
|
+
if (version !== void 0 && typeof version === "string" && !isVersionSupported(version, SUPPORTED_CONFIG_FORMAT_VERSIONS)) if (defaultConfigMigrator.canMigrate(version, CURRENT_CONFIG_FORMAT_VERSION)) {
|
|
1627
|
+
const migrationOutput = defaultConfigMigrator.migrate(rawObj, version, CURRENT_CONFIG_FORMAT_VERSION, filePath);
|
|
1628
|
+
if (migrationOutput.result.success) {
|
|
1629
|
+
logger$20.info({
|
|
1630
|
+
from: version,
|
|
1631
|
+
to: CURRENT_CONFIG_FORMAT_VERSION,
|
|
1632
|
+
backup: migrationOutput.result.backupPath
|
|
1633
|
+
}, "Config auto-migrated successfully");
|
|
1634
|
+
parsed = migrationOutput.config;
|
|
1635
|
+
} else throw new ConfigIncompatibleFormatError(`Config migration failed: ${migrationOutput.result.manualStepsRequired.join("; ")}`, {
|
|
1636
|
+
filePath,
|
|
1637
|
+
version
|
|
1638
|
+
});
|
|
1639
|
+
} else throw new ConfigIncompatibleFormatError(formatUnsupportedVersionError("config", version, SUPPORTED_CONFIG_FORMAT_VERSIONS), { filePath });
|
|
1640
|
+
}
|
|
1641
|
+
const result = PartialSubstrateConfigSchema.safeParse(parsed);
|
|
1642
|
+
if (!result.success) {
|
|
1643
|
+
const issues = result.error.issues.map((i) => ` • ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
1644
|
+
throw new ConfigError(`Invalid config file at ${filePath}:\n${issues}`, {
|
|
1645
|
+
filePath,
|
|
1646
|
+
issues: result.error.issues
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
return result.data;
|
|
1650
|
+
} catch (err) {
|
|
1651
|
+
if (err instanceof ConfigError) throw err;
|
|
1652
|
+
if (err instanceof ConfigIncompatibleFormatError) throw err;
|
|
1653
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1654
|
+
throw new ConfigError(`Failed to read config file at ${filePath}: ${message}`, { filePath });
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
};
|
|
1658
|
+
/**
|
|
1659
|
+
* Create a new ConfigSystem instance.
|
|
1660
|
+
*
|
|
1661
|
+
* @example
|
|
1662
|
+
* const config = createConfigSystem()
|
|
1663
|
+
* await config.load()
|
|
1664
|
+
* const cfg = config.getConfig()
|
|
1665
|
+
*/
|
|
1666
|
+
function createConfigSystem(options = {}) {
|
|
1667
|
+
return new ConfigSystemImpl(options);
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1364
1670
|
//#endregion
|
|
1365
1671
|
//#region src/modules/implementation-orchestrator/event-emitter.ts
|
|
1366
1672
|
/**
|
|
@@ -7123,6 +7429,27 @@ function createPauseGate() {
|
|
|
7123
7429
|
};
|
|
7124
7430
|
}
|
|
7125
7431
|
/**
|
|
7432
|
+
* Build the targeted_files content string from a code-review issue list.
|
|
7433
|
+
* Deduplicates file paths and includes line numbers where available.
|
|
7434
|
+
* Returns empty string when no issues have file references.
|
|
7435
|
+
*/
|
|
7436
|
+
function buildTargetedFilesContent(issueList) {
|
|
7437
|
+
const seen = new Map();
|
|
7438
|
+
for (const issue of issueList) {
|
|
7439
|
+
const iss = issue;
|
|
7440
|
+
if (!iss.file) continue;
|
|
7441
|
+
if (!seen.has(iss.file)) seen.set(iss.file, new Set());
|
|
7442
|
+
if (iss.line !== void 0) seen.get(iss.file).add(iss.line);
|
|
7443
|
+
}
|
|
7444
|
+
if (seen.size === 0) return "";
|
|
7445
|
+
const lines = [];
|
|
7446
|
+
for (const [file, lineNums] of seen) if (lineNums.size > 0) {
|
|
7447
|
+
const sorted = [...lineNums].sort((a, b) => a - b);
|
|
7448
|
+
lines.push(`- ${file} (lines: ${sorted.join(", ")})`);
|
|
7449
|
+
} else lines.push(`- ${file}`);
|
|
7450
|
+
return lines.join("\n");
|
|
7451
|
+
}
|
|
7452
|
+
/**
|
|
7126
7453
|
* Factory function that creates an ImplementationOrchestrator instance.
|
|
7127
7454
|
*
|
|
7128
7455
|
* @param deps - Injected dependencies (db, pack, contextCompiler, dispatcher,
|
|
@@ -7131,7 +7458,7 @@ function createPauseGate() {
|
|
|
7131
7458
|
*/
|
|
7132
7459
|
function createImplementationOrchestrator(deps) {
|
|
7133
7460
|
const { db, pack, contextCompiler, dispatcher, eventBus, config, projectRoot, tokenCeilings } = deps;
|
|
7134
|
-
const logger$
|
|
7461
|
+
const logger$23 = createLogger("implementation-orchestrator");
|
|
7135
7462
|
let _state = "IDLE";
|
|
7136
7463
|
let _startedAt;
|
|
7137
7464
|
let _completedAt;
|
|
@@ -7174,7 +7501,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7174
7501
|
const nowMs = Date.now();
|
|
7175
7502
|
for (const [phase, startMs] of starts) {
|
|
7176
7503
|
const endMs = ends?.get(phase);
|
|
7177
|
-
if (endMs === void 0) logger$
|
|
7504
|
+
if (endMs === void 0) logger$23.warn({
|
|
7178
7505
|
storyKey,
|
|
7179
7506
|
phase
|
|
7180
7507
|
}, "Phase has no end time — story may have errored mid-phase. Duration capped to now() and may be inflated.");
|
|
@@ -7223,7 +7550,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7223
7550
|
rationale: `Story ${storyKey} completed with result=${result} in ${wallClockSeconds}s. Tokens: ${tokenAgg.input}+${tokenAgg.output}. Review cycles: ${reviewCycles}.`
|
|
7224
7551
|
});
|
|
7225
7552
|
} catch (decisionErr) {
|
|
7226
|
-
logger$
|
|
7553
|
+
logger$23.warn({
|
|
7227
7554
|
err: decisionErr,
|
|
7228
7555
|
storyKey
|
|
7229
7556
|
}, "Failed to write story-metrics decision (best-effort)");
|
|
@@ -7251,13 +7578,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
7251
7578
|
dispatches: _storyDispatches.get(storyKey) ?? 0
|
|
7252
7579
|
});
|
|
7253
7580
|
} catch (emitErr) {
|
|
7254
|
-
logger$
|
|
7581
|
+
logger$23.warn({
|
|
7255
7582
|
err: emitErr,
|
|
7256
7583
|
storyKey
|
|
7257
7584
|
}, "Failed to emit story:metrics event (best-effort)");
|
|
7258
7585
|
}
|
|
7259
7586
|
} catch (err) {
|
|
7260
|
-
logger$
|
|
7587
|
+
logger$23.warn({
|
|
7261
7588
|
err,
|
|
7262
7589
|
storyKey
|
|
7263
7590
|
}, "Failed to write story metrics (best-effort)");
|
|
@@ -7286,7 +7613,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7286
7613
|
rationale: `Story ${storyKey} ${outcome} after ${reviewCycles} review cycle(s).`
|
|
7287
7614
|
});
|
|
7288
7615
|
} catch (err) {
|
|
7289
|
-
logger$
|
|
7616
|
+
logger$23.warn({
|
|
7290
7617
|
err,
|
|
7291
7618
|
storyKey
|
|
7292
7619
|
}, "Failed to write story-outcome decision (best-effort)");
|
|
@@ -7312,7 +7639,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7312
7639
|
rationale: `Escalation diagnosis for ${payload.storyKey}: ${diagnosis.recommendedAction} — ${diagnosis.rationale}`
|
|
7313
7640
|
});
|
|
7314
7641
|
} catch (err) {
|
|
7315
|
-
logger$
|
|
7642
|
+
logger$23.warn({
|
|
7316
7643
|
err,
|
|
7317
7644
|
storyKey: payload.storyKey
|
|
7318
7645
|
}, "Failed to persist escalation diagnosis (best-effort)");
|
|
@@ -7362,7 +7689,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7362
7689
|
token_usage_json: serialized
|
|
7363
7690
|
});
|
|
7364
7691
|
} catch (err) {
|
|
7365
|
-
logger$
|
|
7692
|
+
logger$23.warn("Failed to persist orchestrator state", { err });
|
|
7366
7693
|
}
|
|
7367
7694
|
}
|
|
7368
7695
|
function recordProgress() {
|
|
@@ -7409,7 +7736,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7409
7736
|
}
|
|
7410
7737
|
if (childActive) {
|
|
7411
7738
|
_lastProgressTs = Date.now();
|
|
7412
|
-
logger$
|
|
7739
|
+
logger$23.debug({
|
|
7413
7740
|
storyKey: key,
|
|
7414
7741
|
phase: s.phase,
|
|
7415
7742
|
childPids
|
|
@@ -7418,7 +7745,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7418
7745
|
}
|
|
7419
7746
|
_stalledStories.add(key);
|
|
7420
7747
|
_storiesWithStall.add(key);
|
|
7421
|
-
logger$
|
|
7748
|
+
logger$23.warn({
|
|
7422
7749
|
storyKey: key,
|
|
7423
7750
|
phase: s.phase,
|
|
7424
7751
|
elapsedMs: elapsed,
|
|
@@ -7463,7 +7790,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7463
7790
|
for (let attempt = 0; attempt < MEMORY_PRESSURE_BACKOFF_MS.length; attempt++) {
|
|
7464
7791
|
const memState = dispatcher.getMemoryState();
|
|
7465
7792
|
if (!memState.isPressured) return true;
|
|
7466
|
-
logger$
|
|
7793
|
+
logger$23.warn({
|
|
7467
7794
|
storyKey,
|
|
7468
7795
|
freeMB: memState.freeMB,
|
|
7469
7796
|
thresholdMB: memState.thresholdMB,
|
|
@@ -7483,11 +7810,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
7483
7810
|
* exhausted retries the story is ESCALATED.
|
|
7484
7811
|
*/
|
|
7485
7812
|
async function processStory(storyKey) {
|
|
7486
|
-
logger$
|
|
7813
|
+
logger$23.info("Processing story", { storyKey });
|
|
7487
7814
|
{
|
|
7488
7815
|
const memoryOk = await checkMemoryPressure(storyKey);
|
|
7489
7816
|
if (!memoryOk) {
|
|
7490
|
-
logger$
|
|
7817
|
+
logger$23.warn({ storyKey }, "Memory pressure exhausted — escalating story without dispatch");
|
|
7491
7818
|
_stories.set(storyKey, {
|
|
7492
7819
|
phase: "ESCALATED",
|
|
7493
7820
|
reviewCycles: 0,
|
|
@@ -7521,14 +7848,14 @@ function createImplementationOrchestrator(deps) {
|
|
|
7521
7848
|
if (match) {
|
|
7522
7849
|
const candidatePath = join$1(artifactsDir, match);
|
|
7523
7850
|
const validation = await isValidStoryFile(candidatePath);
|
|
7524
|
-
if (!validation.valid) logger$
|
|
7851
|
+
if (!validation.valid) logger$23.warn({
|
|
7525
7852
|
storyKey,
|
|
7526
7853
|
storyFilePath: candidatePath,
|
|
7527
7854
|
reason: validation.reason
|
|
7528
7855
|
}, `Existing story file for ${storyKey} is invalid (${validation.reason}) — re-creating`);
|
|
7529
7856
|
else {
|
|
7530
7857
|
storyFilePath = candidatePath;
|
|
7531
|
-
logger$
|
|
7858
|
+
logger$23.info({
|
|
7532
7859
|
storyKey,
|
|
7533
7860
|
storyFilePath
|
|
7534
7861
|
}, "Found existing story file — skipping create-story");
|
|
@@ -7640,10 +7967,10 @@ function createImplementationOrchestrator(deps) {
|
|
|
7640
7967
|
pipelineRunId: config.pipelineRunId
|
|
7641
7968
|
});
|
|
7642
7969
|
testPlanPhaseResult = testPlanResult.result;
|
|
7643
|
-
if (testPlanResult.result === "success") logger$
|
|
7644
|
-
else logger$
|
|
7970
|
+
if (testPlanResult.result === "success") logger$23.info({ storyKey }, "Test plan generated successfully");
|
|
7971
|
+
else logger$23.warn({ storyKey }, "Test planning returned failed result — proceeding to dev-story without test plan");
|
|
7645
7972
|
} catch (err) {
|
|
7646
|
-
logger$
|
|
7973
|
+
logger$23.warn({
|
|
7647
7974
|
storyKey,
|
|
7648
7975
|
err
|
|
7649
7976
|
}, "Test planning failed — proceeding to dev-story without test plan");
|
|
@@ -7667,7 +7994,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7667
7994
|
try {
|
|
7668
7995
|
storyContentForAnalysis = await readFile$1(storyFilePath ?? "", "utf-8");
|
|
7669
7996
|
} catch (err) {
|
|
7670
|
-
logger$
|
|
7997
|
+
logger$23.error({
|
|
7671
7998
|
storyKey,
|
|
7672
7999
|
storyFilePath,
|
|
7673
8000
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -7675,7 +8002,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7675
8002
|
}
|
|
7676
8003
|
const analysis = analyzeStoryComplexity(storyContentForAnalysis);
|
|
7677
8004
|
const batches = planTaskBatches(analysis);
|
|
7678
|
-
logger$
|
|
8005
|
+
logger$23.info({
|
|
7679
8006
|
storyKey,
|
|
7680
8007
|
estimatedScope: analysis.estimatedScope,
|
|
7681
8008
|
batchCount: batches.length,
|
|
@@ -7693,7 +8020,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7693
8020
|
if (_state !== "RUNNING") break;
|
|
7694
8021
|
const taskScope = batch.taskIds.map((id, i) => `T${id}: ${batch.taskTitles[i] ?? ""}`).join("\n");
|
|
7695
8022
|
const priorFiles = allFilesModified.size > 0 ? Array.from(allFilesModified) : void 0;
|
|
7696
|
-
logger$
|
|
8023
|
+
logger$23.info({
|
|
7697
8024
|
storyKey,
|
|
7698
8025
|
batchIndex: batch.batchIndex,
|
|
7699
8026
|
taskCount: batch.taskIds.length
|
|
@@ -7718,7 +8045,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7718
8045
|
});
|
|
7719
8046
|
} catch (batchErr) {
|
|
7720
8047
|
const errMsg = batchErr instanceof Error ? batchErr.message : String(batchErr);
|
|
7721
|
-
logger$
|
|
8048
|
+
logger$23.warn({
|
|
7722
8049
|
storyKey,
|
|
7723
8050
|
batchIndex: batch.batchIndex,
|
|
7724
8051
|
error: errMsg
|
|
@@ -7738,7 +8065,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7738
8065
|
filesModified: batchFilesModified,
|
|
7739
8066
|
result: batchResult.result === "success" ? "success" : "failed"
|
|
7740
8067
|
};
|
|
7741
|
-
logger$
|
|
8068
|
+
logger$23.info(batchMetrics, "Batch dev-story metrics");
|
|
7742
8069
|
for (const f of batchFilesModified) allFilesModified.add(f);
|
|
7743
8070
|
if (batchFilesModified.length > 0) batchFileGroups.push({
|
|
7744
8071
|
batchIndex: batch.batchIndex,
|
|
@@ -7760,13 +8087,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
7760
8087
|
})
|
|
7761
8088
|
});
|
|
7762
8089
|
} catch (tokenErr) {
|
|
7763
|
-
logger$
|
|
8090
|
+
logger$23.warn({
|
|
7764
8091
|
storyKey,
|
|
7765
8092
|
batchIndex: batch.batchIndex,
|
|
7766
8093
|
err: tokenErr
|
|
7767
8094
|
}, "Failed to record batch token usage");
|
|
7768
8095
|
}
|
|
7769
|
-
if (batchResult.result === "failed") logger$
|
|
8096
|
+
if (batchResult.result === "failed") logger$23.warn({
|
|
7770
8097
|
storyKey,
|
|
7771
8098
|
batchIndex: batch.batchIndex,
|
|
7772
8099
|
error: batchResult.error
|
|
@@ -7802,7 +8129,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7802
8129
|
});
|
|
7803
8130
|
persistState();
|
|
7804
8131
|
if (devResult.result === "success") devStoryWasSuccess = true;
|
|
7805
|
-
else logger$
|
|
8132
|
+
else logger$23.warn("Dev-story reported failure, proceeding to code review", {
|
|
7806
8133
|
storyKey,
|
|
7807
8134
|
error: devResult.error,
|
|
7808
8135
|
filesModified: devFilesModified.length
|
|
@@ -7830,7 +8157,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7830
8157
|
if (devStoryWasSuccess) {
|
|
7831
8158
|
gitDiffFiles = checkGitDiffFiles(projectRoot ?? process.cwd());
|
|
7832
8159
|
if (gitDiffFiles.length === 0) {
|
|
7833
|
-
logger$
|
|
8160
|
+
logger$23.warn({ storyKey }, "Zero-diff detected after COMPLETE dev-story — no file changes in git working tree");
|
|
7834
8161
|
eventBus.emit("orchestrator:zero-diff-escalation", {
|
|
7835
8162
|
storyKey,
|
|
7836
8163
|
reason: "zero-diff-on-complete"
|
|
@@ -7861,7 +8188,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7861
8188
|
});
|
|
7862
8189
|
if (buildVerifyResult.status === "passed") {
|
|
7863
8190
|
eventBus.emit("story:build-verification-passed", { storyKey });
|
|
7864
|
-
logger$
|
|
8191
|
+
logger$23.info({ storyKey }, "Build verification passed");
|
|
7865
8192
|
} else if (buildVerifyResult.status === "failed" || buildVerifyResult.status === "timeout") {
|
|
7866
8193
|
const truncatedOutput = (buildVerifyResult.output ?? "").slice(0, 2e3);
|
|
7867
8194
|
const reason = buildVerifyResult.reason ?? "build-verification-failed";
|
|
@@ -7870,7 +8197,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7870
8197
|
exitCode: buildVerifyResult.exitCode ?? 1,
|
|
7871
8198
|
output: truncatedOutput
|
|
7872
8199
|
});
|
|
7873
|
-
logger$
|
|
8200
|
+
logger$23.warn({
|
|
7874
8201
|
storyKey,
|
|
7875
8202
|
reason,
|
|
7876
8203
|
exitCode: buildVerifyResult.exitCode
|
|
@@ -7900,7 +8227,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7900
8227
|
storyKey
|
|
7901
8228
|
});
|
|
7902
8229
|
if (icResult.potentiallyAffectedTests.length > 0) {
|
|
7903
|
-
logger$
|
|
8230
|
+
logger$23.warn({
|
|
7904
8231
|
storyKey,
|
|
7905
8232
|
modifiedInterfaces: icResult.modifiedInterfaces,
|
|
7906
8233
|
potentiallyAffectedTests: icResult.potentiallyAffectedTests
|
|
@@ -7945,7 +8272,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7945
8272
|
"NEEDS_MAJOR_REWORK": 2
|
|
7946
8273
|
};
|
|
7947
8274
|
for (const group of batchFileGroups) {
|
|
7948
|
-
logger$
|
|
8275
|
+
logger$23.info({
|
|
7949
8276
|
storyKey,
|
|
7950
8277
|
batchIndex: group.batchIndex,
|
|
7951
8278
|
fileCount: group.files.length
|
|
@@ -7983,7 +8310,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
7983
8310
|
rawOutput: lastRawOutput,
|
|
7984
8311
|
tokenUsage: aggregateTokens
|
|
7985
8312
|
};
|
|
7986
|
-
logger$
|
|
8313
|
+
logger$23.info({
|
|
7987
8314
|
storyKey,
|
|
7988
8315
|
batchCount: batchFileGroups.length,
|
|
7989
8316
|
verdict: worstVerdict,
|
|
@@ -8010,7 +8337,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8010
8337
|
const isPhantomReview = reviewResult.dispatchFailed === true || reviewResult.verdict !== "SHIP_IT" && (reviewResult.issue_list === void 0 || reviewResult.issue_list.length === 0) && reviewResult.error !== void 0;
|
|
8011
8338
|
if (isPhantomReview && !timeoutRetried) {
|
|
8012
8339
|
timeoutRetried = true;
|
|
8013
|
-
logger$
|
|
8340
|
+
logger$23.warn({
|
|
8014
8341
|
storyKey,
|
|
8015
8342
|
reviewCycles,
|
|
8016
8343
|
error: reviewResult.error
|
|
@@ -8020,7 +8347,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8020
8347
|
verdict = reviewResult.verdict;
|
|
8021
8348
|
issueList = reviewResult.issue_list ?? [];
|
|
8022
8349
|
if (verdict === "NEEDS_MAJOR_REWORK" && reviewCycles > 0 && previousIssueList.length > 0 && issueList.length < previousIssueList.length) {
|
|
8023
|
-
logger$
|
|
8350
|
+
logger$23.info({
|
|
8024
8351
|
storyKey,
|
|
8025
8352
|
originalVerdict: verdict,
|
|
8026
8353
|
issuesBefore: previousIssueList.length,
|
|
@@ -8056,7 +8383,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8056
8383
|
if (_decomposition !== void 0) parts.push(`decomposed: ${_decomposition.batchCount} batches`);
|
|
8057
8384
|
parts.push(`${fileCount} files`);
|
|
8058
8385
|
parts.push(`${totalTokensK} tokens`);
|
|
8059
|
-
logger$
|
|
8386
|
+
logger$23.info({
|
|
8060
8387
|
storyKey,
|
|
8061
8388
|
verdict,
|
|
8062
8389
|
agentVerdict: reviewResult.agentVerdict
|
|
@@ -8108,7 +8435,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8108
8435
|
filesModified: devFilesModified,
|
|
8109
8436
|
workingDirectory: projectRoot
|
|
8110
8437
|
});
|
|
8111
|
-
logger$
|
|
8438
|
+
logger$23.debug({
|
|
8112
8439
|
storyKey,
|
|
8113
8440
|
expansion_priority: expansionResult.expansion_priority,
|
|
8114
8441
|
coverage_gaps: expansionResult.coverage_gaps.length
|
|
@@ -8121,7 +8448,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8121
8448
|
value: JSON.stringify(expansionResult)
|
|
8122
8449
|
});
|
|
8123
8450
|
} catch (expansionErr) {
|
|
8124
|
-
logger$
|
|
8451
|
+
logger$23.warn({
|
|
8125
8452
|
storyKey,
|
|
8126
8453
|
error: expansionErr instanceof Error ? expansionErr.message : String(expansionErr)
|
|
8127
8454
|
}, "Test expansion failed — story verdict unchanged");
|
|
@@ -8148,7 +8475,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8148
8475
|
persistState();
|
|
8149
8476
|
return;
|
|
8150
8477
|
}
|
|
8151
|
-
logger$
|
|
8478
|
+
logger$23.info({
|
|
8152
8479
|
storyKey,
|
|
8153
8480
|
reviewCycles: finalReviewCycles,
|
|
8154
8481
|
issueCount: issueList.length
|
|
@@ -8158,9 +8485,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
8158
8485
|
updateStory(storyKey, { phase: "NEEDS_FIXES" });
|
|
8159
8486
|
try {
|
|
8160
8487
|
let fixPrompt;
|
|
8488
|
+
let autoApproveMaxTurns;
|
|
8161
8489
|
try {
|
|
8162
8490
|
const fixTemplate = await pack.getPrompt("fix-story");
|
|
8163
8491
|
const storyContent = await readFile$1(storyFilePath ?? "", "utf-8");
|
|
8492
|
+
const complexity = computeStoryComplexity(storyContent);
|
|
8493
|
+
autoApproveMaxTurns = resolveFixStoryMaxTurns(complexity.complexityScore);
|
|
8494
|
+
logComplexityResult(storyKey, complexity, autoApproveMaxTurns);
|
|
8164
8495
|
let reviewFeedback;
|
|
8165
8496
|
if (issueList.length === 0) reviewFeedback = `Verdict: ${verdict}\nIssues: Minor issues flagged but no specifics provided. Review the story ACs and fix any remaining gaps.`;
|
|
8166
8497
|
else reviewFeedback = [
|
|
@@ -8177,6 +8508,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8177
8508
|
const constraints = decisions.filter((d) => d.category === "architecture");
|
|
8178
8509
|
archConstraints = constraints.map((d) => `${d.key}: ${d.value}`).join("\n");
|
|
8179
8510
|
} catch {}
|
|
8511
|
+
const targetedFilesContent = buildTargetedFilesContent(issueList);
|
|
8180
8512
|
const sections = [
|
|
8181
8513
|
{
|
|
8182
8514
|
name: "story_content",
|
|
@@ -8192,19 +8524,25 @@ function createImplementationOrchestrator(deps) {
|
|
|
8192
8524
|
name: "arch_constraints",
|
|
8193
8525
|
content: archConstraints,
|
|
8194
8526
|
priority: "optional"
|
|
8195
|
-
}
|
|
8527
|
+
},
|
|
8528
|
+
...targetedFilesContent ? [{
|
|
8529
|
+
name: "targeted_files",
|
|
8530
|
+
content: targetedFilesContent,
|
|
8531
|
+
priority: "important"
|
|
8532
|
+
}] : []
|
|
8196
8533
|
];
|
|
8197
8534
|
const assembled = assemblePrompt(fixTemplate, sections, 24e3);
|
|
8198
8535
|
fixPrompt = assembled.prompt;
|
|
8199
8536
|
} catch {
|
|
8200
8537
|
fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, minor fixes needed`;
|
|
8201
|
-
logger$
|
|
8538
|
+
logger$23.warn("Failed to assemble auto-approve fix prompt, using fallback", { storyKey });
|
|
8202
8539
|
}
|
|
8203
8540
|
const handle = dispatcher.dispatch({
|
|
8204
8541
|
prompt: fixPrompt,
|
|
8205
8542
|
agent: "claude-code",
|
|
8206
8543
|
taskType: "minor-fixes",
|
|
8207
|
-
workingDirectory: projectRoot
|
|
8544
|
+
workingDirectory: projectRoot,
|
|
8545
|
+
...autoApproveMaxTurns !== void 0 ? { maxTurns: autoApproveMaxTurns } : {}
|
|
8208
8546
|
});
|
|
8209
8547
|
const fixResult = await handle.result;
|
|
8210
8548
|
eventBus.emit("orchestrator:story-phase-complete", {
|
|
@@ -8215,9 +8553,9 @@ function createImplementationOrchestrator(deps) {
|
|
|
8215
8553
|
output: fixResult.tokenEstimate.output
|
|
8216
8554
|
} : void 0 }
|
|
8217
8555
|
});
|
|
8218
|
-
if (fixResult.status === "timeout") logger$
|
|
8556
|
+
if (fixResult.status === "timeout") logger$23.warn("Auto-approve fix timed out — approving anyway (issues were minor)", { storyKey });
|
|
8219
8557
|
} catch (err) {
|
|
8220
|
-
logger$
|
|
8558
|
+
logger$23.warn("Auto-approve fix dispatch failed — approving anyway (issues were minor)", {
|
|
8221
8559
|
storyKey,
|
|
8222
8560
|
err
|
|
8223
8561
|
});
|
|
@@ -8251,7 +8589,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8251
8589
|
try {
|
|
8252
8590
|
const fixTemplate = await pack.getPrompt(templateName);
|
|
8253
8591
|
const storyContent = await readFile$1(storyFilePath ?? "", "utf-8");
|
|
8254
|
-
|
|
8592
|
+
{
|
|
8255
8593
|
const complexity = computeStoryComplexity(storyContent);
|
|
8256
8594
|
fixMaxTurns = resolveFixStoryMaxTurns(complexity.complexityScore);
|
|
8257
8595
|
logComplexityResult(storyKey, complexity, fixMaxTurns);
|
|
@@ -8305,28 +8643,36 @@ function createImplementationOrchestrator(deps) {
|
|
|
8305
8643
|
content: "",
|
|
8306
8644
|
priority: "optional"
|
|
8307
8645
|
}
|
|
8308
|
-
] :
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8646
|
+
] : (() => {
|
|
8647
|
+
const targetedFilesContent = buildTargetedFilesContent(issueList);
|
|
8648
|
+
return [
|
|
8649
|
+
{
|
|
8650
|
+
name: "story_content",
|
|
8651
|
+
content: storyContent,
|
|
8652
|
+
priority: "required"
|
|
8653
|
+
},
|
|
8654
|
+
{
|
|
8655
|
+
name: "review_feedback",
|
|
8656
|
+
content: reviewFeedback,
|
|
8657
|
+
priority: "required"
|
|
8658
|
+
},
|
|
8659
|
+
{
|
|
8660
|
+
name: "arch_constraints",
|
|
8661
|
+
content: archConstraints,
|
|
8662
|
+
priority: "optional"
|
|
8663
|
+
},
|
|
8664
|
+
...targetedFilesContent ? [{
|
|
8665
|
+
name: "targeted_files",
|
|
8666
|
+
content: targetedFilesContent,
|
|
8667
|
+
priority: "important"
|
|
8668
|
+
}] : []
|
|
8669
|
+
];
|
|
8670
|
+
})();
|
|
8325
8671
|
const assembled = assemblePrompt(fixTemplate, sections, 24e3);
|
|
8326
8672
|
fixPrompt = assembled.prompt;
|
|
8327
8673
|
} catch {
|
|
8328
8674
|
fixPrompt = `Fix story ${storyKey}: verdict=${verdict}, taskType=${taskType}`;
|
|
8329
|
-
logger$
|
|
8675
|
+
logger$23.warn("Failed to assemble fix prompt, using fallback", {
|
|
8330
8676
|
storyKey,
|
|
8331
8677
|
taskType
|
|
8332
8678
|
});
|
|
@@ -8345,6 +8691,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8345
8691
|
agent: "claude-code",
|
|
8346
8692
|
taskType,
|
|
8347
8693
|
...fixModel !== void 0 ? { model: fixModel } : {},
|
|
8694
|
+
...fixMaxTurns !== void 0 ? { maxTurns: fixMaxTurns } : {},
|
|
8348
8695
|
...projectRoot !== void 0 ? { workingDirectory: projectRoot } : {}
|
|
8349
8696
|
});
|
|
8350
8697
|
const fixResult = await handle.result;
|
|
@@ -8357,7 +8704,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8357
8704
|
} : void 0 }
|
|
8358
8705
|
});
|
|
8359
8706
|
if (fixResult.status === "timeout") {
|
|
8360
|
-
logger$
|
|
8707
|
+
logger$23.warn("Fix dispatch timed out — escalating story", {
|
|
8361
8708
|
storyKey,
|
|
8362
8709
|
taskType
|
|
8363
8710
|
});
|
|
@@ -8379,7 +8726,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8379
8726
|
}
|
|
8380
8727
|
if (fixResult.status === "failed") {
|
|
8381
8728
|
if (isMajorRework) {
|
|
8382
|
-
logger$
|
|
8729
|
+
logger$23.warn("Major rework dispatch failed — escalating story", {
|
|
8383
8730
|
storyKey,
|
|
8384
8731
|
exitCode: fixResult.exitCode
|
|
8385
8732
|
});
|
|
@@ -8399,14 +8746,14 @@ function createImplementationOrchestrator(deps) {
|
|
|
8399
8746
|
persistState();
|
|
8400
8747
|
return;
|
|
8401
8748
|
}
|
|
8402
|
-
logger$
|
|
8749
|
+
logger$23.warn("Fix dispatch failed", {
|
|
8403
8750
|
storyKey,
|
|
8404
8751
|
taskType,
|
|
8405
8752
|
exitCode: fixResult.exitCode
|
|
8406
8753
|
});
|
|
8407
8754
|
}
|
|
8408
8755
|
} catch (err) {
|
|
8409
|
-
logger$
|
|
8756
|
+
logger$23.warn("Fix dispatch failed, continuing to next review", {
|
|
8410
8757
|
storyKey,
|
|
8411
8758
|
taskType,
|
|
8412
8759
|
err
|
|
@@ -8469,11 +8816,11 @@ function createImplementationOrchestrator(deps) {
|
|
|
8469
8816
|
}
|
|
8470
8817
|
async function run(storyKeys) {
|
|
8471
8818
|
if (_state === "RUNNING" || _state === "PAUSED") {
|
|
8472
|
-
logger$
|
|
8819
|
+
logger$23.warn("run() called while orchestrator is already running or paused — ignoring", { state: _state });
|
|
8473
8820
|
return getStatus();
|
|
8474
8821
|
}
|
|
8475
8822
|
if (_state === "COMPLETE") {
|
|
8476
|
-
logger$
|
|
8823
|
+
logger$23.warn("run() called on a COMPLETE orchestrator — ignoring", { state: _state });
|
|
8477
8824
|
return getStatus();
|
|
8478
8825
|
}
|
|
8479
8826
|
_state = "RUNNING";
|
|
@@ -8491,13 +8838,13 @@ function createImplementationOrchestrator(deps) {
|
|
|
8491
8838
|
if (config.enableHeartbeat) startHeartbeat();
|
|
8492
8839
|
if (projectRoot !== void 0) {
|
|
8493
8840
|
const seedResult = seedMethodologyContext(db, projectRoot);
|
|
8494
|
-
if (seedResult.decisionsCreated > 0) logger$
|
|
8841
|
+
if (seedResult.decisionsCreated > 0) logger$23.info({
|
|
8495
8842
|
decisionsCreated: seedResult.decisionsCreated,
|
|
8496
8843
|
skippedCategories: seedResult.skippedCategories
|
|
8497
8844
|
}, "Methodology context seeded from planning artifacts");
|
|
8498
8845
|
}
|
|
8499
8846
|
const groups = detectConflictGroups(storyKeys, { moduleMap: pack.manifest.conflictGroups });
|
|
8500
|
-
logger$
|
|
8847
|
+
logger$23.info("Orchestrator starting", {
|
|
8501
8848
|
storyCount: storyKeys.length,
|
|
8502
8849
|
groupCount: groups.length,
|
|
8503
8850
|
maxConcurrency: config.maxConcurrency
|
|
@@ -8509,7 +8856,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8509
8856
|
_state = "FAILED";
|
|
8510
8857
|
_completedAt = new Date().toISOString();
|
|
8511
8858
|
persistState();
|
|
8512
|
-
logger$
|
|
8859
|
+
logger$23.error("Orchestrator failed with unhandled error", { err });
|
|
8513
8860
|
return getStatus();
|
|
8514
8861
|
}
|
|
8515
8862
|
stopHeartbeat();
|
|
@@ -8536,7 +8883,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8536
8883
|
_pauseGate = createPauseGate();
|
|
8537
8884
|
_state = "PAUSED";
|
|
8538
8885
|
eventBus.emit("orchestrator:paused", {});
|
|
8539
|
-
logger$
|
|
8886
|
+
logger$23.info("Orchestrator paused");
|
|
8540
8887
|
}
|
|
8541
8888
|
function resume() {
|
|
8542
8889
|
if (_state !== "PAUSED") return;
|
|
@@ -8547,7 +8894,7 @@ function createImplementationOrchestrator(deps) {
|
|
|
8547
8894
|
}
|
|
8548
8895
|
_state = "RUNNING";
|
|
8549
8896
|
eventBus.emit("orchestrator:resumed", {});
|
|
8550
|
-
logger$
|
|
8897
|
+
logger$23.info("Orchestrator resumed");
|
|
8551
8898
|
}
|
|
8552
8899
|
return {
|
|
8553
8900
|
run,
|
|
@@ -12643,6 +12990,14 @@ async function runRunAction(options) {
|
|
|
12643
12990
|
process.exit(130);
|
|
12644
12991
|
});
|
|
12645
12992
|
} catch {}
|
|
12993
|
+
let tokenCeilings;
|
|
12994
|
+
try {
|
|
12995
|
+
const configSystem = createConfigSystem({ projectConfigDir: dbDir });
|
|
12996
|
+
await configSystem.load();
|
|
12997
|
+
tokenCeilings = configSystem.getConfig().token_ceilings;
|
|
12998
|
+
} catch {
|
|
12999
|
+
logger.debug("Config loading skipped — using default token ceilings");
|
|
13000
|
+
}
|
|
12646
13001
|
if (startPhase !== void 0) return runFullPipeline({
|
|
12647
13002
|
packName,
|
|
12648
13003
|
packPath,
|
|
@@ -12654,6 +13009,7 @@ async function runRunAction(options) {
|
|
|
12654
13009
|
concurrency,
|
|
12655
13010
|
outputFormat,
|
|
12656
13011
|
projectRoot,
|
|
13012
|
+
tokenCeilings,
|
|
12657
13013
|
...eventsFlag === true ? { events: true } : {},
|
|
12658
13014
|
...skipUx === true ? { skipUx: true } : {},
|
|
12659
13015
|
...researchFlag === true ? { research: true } : {},
|
|
@@ -13060,7 +13416,8 @@ async function runRunAction(options) {
|
|
|
13060
13416
|
pipelineRunId: pipelineRun.id,
|
|
13061
13417
|
enableHeartbeat: eventsFlag === true
|
|
13062
13418
|
},
|
|
13063
|
-
projectRoot
|
|
13419
|
+
projectRoot,
|
|
13420
|
+
tokenCeilings
|
|
13064
13421
|
});
|
|
13065
13422
|
if (outputFormat === "human" && progressRenderer === void 0 && ndjsonEmitter === void 0) {
|
|
13066
13423
|
process.stdout.write(`Starting pipeline: ${storyKeys.length} story/stories, concurrency=${concurrency}\n`);
|
|
@@ -13156,7 +13513,7 @@ async function runRunAction(options) {
|
|
|
13156
13513
|
}
|
|
13157
13514
|
}
|
|
13158
13515
|
async function runFullPipeline(options) {
|
|
13159
|
-
const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, projectRoot, events: eventsFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, registry: injectedRegistry } = options;
|
|
13516
|
+
const { packName, packPath, dbDir, dbPath, startPhase, stopAfter, concept, concurrency, outputFormat, projectRoot, events: eventsFlag, skipUx, research: researchFlag, skipResearch: skipResearchFlag, registry: injectedRegistry, tokenCeilings } = options;
|
|
13160
13517
|
if (!existsSync(dbDir)) mkdirSync(dbDir, { recursive: true });
|
|
13161
13518
|
const dbWrapper = new DatabaseWrapper(dbPath);
|
|
13162
13519
|
try {
|
|
@@ -13363,7 +13720,8 @@ async function runFullPipeline(options) {
|
|
|
13363
13720
|
maxReviewCycles: 2,
|
|
13364
13721
|
pipelineRunId: runId
|
|
13365
13722
|
},
|
|
13366
|
-
projectRoot
|
|
13723
|
+
projectRoot,
|
|
13724
|
+
tokenCeilings
|
|
13367
13725
|
});
|
|
13368
13726
|
eventBus.on("orchestrator:story-phase-complete", (payload) => {
|
|
13369
13727
|
try {
|
|
@@ -13500,5 +13858,5 @@ function registerRunCommand(program, _version = "0.0.0", projectRoot = process.c
|
|
|
13500
13858
|
}
|
|
13501
13859
|
|
|
13502
13860
|
//#endregion
|
|
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-
|
|
13861
|
+
export { DEFAULT_CONFIG, DEFAULT_ROUTING_POLICY, DatabaseWrapper, SUBSTRATE_OWNED_SETTINGS_KEYS, VALID_PHASES, buildPipelineStatusOutput, createConfigSystem, 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 };
|
|
13862
|
+
//# sourceMappingURL=run-Ajt187oE.js.map
|