@wairon/cli 5.1.1-dev.56 → 5.1.1-dev.58

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 CHANGED
@@ -65,7 +65,7 @@ var init_defaults = __esm({
65
65
  copilot: ".github/prompts",
66
66
  codex: ".codex/agents"
67
67
  };
68
- WAIRON_VERSION = "5.1.1-dev.56";
68
+ WAIRON_VERSION = "5.1.1-dev.58";
69
69
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
70
70
  SUPPORTED_ALIASES = ["wai"];
71
71
  SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
@@ -546,6 +546,21 @@ var init_yaml = __esm({
546
546
  });
547
547
 
548
548
  // src/models/execution.ts
549
+ function summarize(budget, profile) {
550
+ const lines = [
551
+ `- **Work shape**: ${profile.breadth} breadth, ${profile.reasoningDepth} reasoning${profile.writes ? "" : ", read-only"}${profile.delegates ? ", delegating" : ""}`,
552
+ `- **Why**: ${profile.rationale}`
553
+ ];
554
+ if (budget.modelTier) lines.push(`- **Capability tier**: ${budget.modelTier}`);
555
+ if (budget.effort) lines.push(`- **Effort**: ${budget.effort}`);
556
+ if (budget.maxTurns !== void 0) {
557
+ lines.push(`- **Turn ceiling**: ${budget.maxTurns} (a circuit breaker \u2014 hitting it should read as a scoping error, not a limit to work up to)`);
558
+ }
559
+ lines.push(`- **Tool grant**: ${budget.toolClass}`);
560
+ lines.push(`- **May delegate further**: ${budget.allowNestedDelegation ? "yes" : "no"}`);
561
+ lines.push(`- **MCP access**: ${budget.mcp}`);
562
+ return lines;
563
+ }
549
564
  var import_zod, WorkBreadthSchema, ReasoningDepthSchema, ExecutionProfileSchema, ModelTierSchema, EffortTierSchema, ToolClassSchema, McpAccessSchema, ExecutionBudgetSchema, BudgetTierSchema, BUDGET_TIER_DESCRIPTIONS, ExecutionConfigSchema;
550
565
  var init_execution = __esm({
551
566
  "src/models/execution.ts"() {
@@ -1570,172 +1585,162 @@ var init_paths = __esm({
1570
1585
  }
1571
1586
  });
1572
1587
 
1573
- // src/core/detection.ts
1574
- function detectDomainCandidates(projectRoot2, alreadyTrackedPaths = /* @__PURE__ */ new Set(), alreadyTrackedIds = /* @__PURE__ */ new Set()) {
1575
- const candidates = /* @__PURE__ */ new Map();
1576
- for (const c of detectGitSubmodules(projectRoot2)) {
1577
- candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
1578
- }
1579
- for (const c of detectNestedGitRepos(projectRoot2)) {
1580
- if (!candidates.has(c.path)) {
1581
- candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
1588
+ // src/utils/canonical-json.ts
1589
+ function canonicalize(value) {
1590
+ return JSON.stringify(sortKeys(value));
1591
+ }
1592
+ function compareOrdinal(a, b) {
1593
+ if (a < b) return -1;
1594
+ return a > b ? 1 : 0;
1595
+ }
1596
+ function sortKeys(v) {
1597
+ if (Array.isArray(v)) return v.map(sortKeys);
1598
+ if (v && typeof v === "object") {
1599
+ const src = v;
1600
+ const out = {};
1601
+ for (const k of Object.keys(src).sort()) {
1602
+ if (k === "createdAt" || k === "updatedAt") continue;
1603
+ out[k] = sortKeys(src[k]);
1582
1604
  }
1605
+ return out;
1583
1606
  }
1584
- const gitPaths = new Set(
1585
- Array.from(candidates.values()).filter((c) => c.type === "git-submodule" || c.type === "git-repo").map((c) => c.path)
1586
- );
1587
- for (const c of detectPackageRoots(projectRoot2)) {
1588
- if (candidates.has(c.path)) continue;
1589
- const insideGit = Array.from(gitPaths).some(
1590
- (gp) => c.path === gp || c.path.startsWith(gp + "/")
1591
- );
1592
- if (insideGit) continue;
1593
- candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
1594
- }
1595
- const sorted = Array.from(candidates.values()).sort((a, b) => a.path.localeCompare(b.path));
1596
- return deduplicateIds(sorted, alreadyTrackedIds);
1607
+ return v;
1597
1608
  }
1598
- function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
1599
- const idCount = /* @__PURE__ */ new Map();
1600
- for (const id of existingIds) {
1601
- idCount.set(id, (idCount.get(id) ?? 0) + 1);
1602
- }
1603
- for (const c of candidates) {
1604
- idCount.set(c.suggestedId, (idCount.get(c.suggestedId) ?? 0) + 1);
1609
+ var init_canonical_json = __esm({
1610
+ "src/utils/canonical-json.ts"() {
1611
+ "use strict";
1605
1612
  }
1606
- return candidates.map((c) => {
1607
- if ((idCount.get(c.suggestedId) ?? 0) <= 1) return c;
1608
- const parts = c.path.split("/");
1609
- const qualifiedId2 = parts.length >= 2 ? pathToId(`${parts[parts.length - 2]}-${parts[parts.length - 1]}`) : c.suggestedId;
1610
- return { ...c, suggestedId: qualifiedId2 };
1611
- });
1613
+ });
1614
+
1615
+ // src/core/statehash.ts
1616
+ function inIdentityOrder(specs) {
1617
+ return specs.map((spec) => ({ spec, content: canonicalize(spec) })).sort((a, b) => compareOrdinal(a.spec.id, b.spec.id) || compareOrdinal(a.content, b.content)).map((entry) => entry.spec);
1612
1618
  }
1613
- function parseGitmodules(filePath) {
1614
- const content = fs7.readFileSync(filePath, "utf-8");
1615
- const entries = [];
1616
- let current2 = {};
1617
- for (const line2 of content.split("\n")) {
1618
- const trimmed = line2.trim();
1619
- const headerMatch = trimmed.match(/^\[submodule "(.+)"\]$/);
1620
- if (headerMatch) {
1621
- if (current2.path) entries.push(current2);
1622
- current2 = { name: headerMatch[1] };
1623
- continue;
1624
- }
1625
- const keyVal = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
1626
- if (keyVal) {
1627
- const [, key, value] = keyVal;
1628
- if (key === "path") current2.path = value.trim();
1629
- if (key === "url") current2.url = value.trim();
1630
- }
1619
+ function computeStateId() {
1620
+ const tree = {
1621
+ system: loadSystemSpec(),
1622
+ subsystems: inIdentityOrder(loadSubsystemSpecs()),
1623
+ components: inIdentityOrder(loadComponentSpecs()),
1624
+ interfaces: inIdentityOrder(loadInterfaceSpecs()),
1625
+ implementations: inIdentityOrder(loadImplementationSpecs()),
1626
+ types: inIdentityOrder(loadTypeSpecs())
1627
+ };
1628
+ const digest2 = crypto.createHash("sha256").update(canonicalize(tree)).digest("hex");
1629
+ return { algorithm: CONTENT_ALGORITHM, digest: digest2 };
1630
+ }
1631
+ function stateIdEquals(a, b) {
1632
+ return !!a && !!b && a.algorithm === b.algorithm && a.digest === b.digest;
1633
+ }
1634
+ var crypto, CONTENT_ALGORITHM;
1635
+ var init_statehash = __esm({
1636
+ "src/core/statehash.ts"() {
1637
+ "use strict";
1638
+ crypto = __toESM(require("crypto"));
1639
+ init_specs2();
1640
+ init_canonical_json();
1641
+ CONTENT_ALGORITHM = "sha256";
1631
1642
  }
1632
- if (current2.path) entries.push(current2);
1633
- return entries;
1643
+ });
1644
+
1645
+ // src/core/lockfile.ts
1646
+ function lockPath() {
1647
+ return aiDir("lock.json");
1634
1648
  }
1635
- function detectGitSubmodules(projectRoot2) {
1636
- const gitmodulesPath = path8.join(projectRoot2, ".gitmodules");
1637
- if (!fs7.existsSync(gitmodulesPath)) return [];
1638
- return parseGitmodules(gitmodulesPath).map((entry) => ({
1639
- suggestedId: pathToId(entry.path),
1640
- suggestedName: pathToName(entry.path),
1641
- path: normalizePath(entry.path),
1642
- type: "git-submodule",
1643
- alreadyTracked: false
1644
- }));
1649
+ function normalizeApprover(value) {
1650
+ if (value && typeof value === "object" && typeof value.id === "string") {
1651
+ const v = value;
1652
+ const source = v.source === "git" || v.source === "hosted" || v.source === "os" ? v.source : "legacy";
1653
+ return { id: v.id, ...v.name ? { name: v.name } : {}, source };
1654
+ }
1655
+ return { id: typeof value === "string" && value ? value : "unknown", source: "legacy" };
1645
1656
  }
1646
- function detectNestedGitRepos(projectRoot2) {
1647
- const results = [];
1648
- walkForGit(projectRoot2, projectRoot2, 0, results);
1649
- return results;
1657
+ function describeApprover(who) {
1658
+ const label = who.name ? `${who.name} (${who.id})` : who.id;
1659
+ return who.source === "hosted" ? `${label} [authenticated]` : label;
1650
1660
  }
1651
- function walkForGit(projectRoot2, currentDir, depth, results) {
1652
- if (depth > MAX_SCAN_DEPTH) return;
1653
- let entries;
1661
+ function readLockRecord() {
1654
1662
  try {
1655
- entries = fs7.readdirSync(currentDir, { withFileTypes: true });
1663
+ return normalizeRecord(JSON.parse(fs7.readFileSync(lockPath(), "utf8")));
1656
1664
  } catch {
1657
- return;
1658
- }
1659
- for (const entry of entries) {
1660
- if (!entry.isDirectory()) continue;
1661
- if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
1662
- const fullPath = path8.join(currentDir, entry.name);
1663
- const relPath = normalizePath(path8.relative(projectRoot2, fullPath));
1664
- if (relPath === "" || relPath === ".") continue;
1665
- const gitPath = path8.join(fullPath, ".git");
1666
- if (fs7.existsSync(gitPath)) {
1667
- results.push({
1668
- suggestedId: pathToId(relPath),
1669
- suggestedName: pathToName(relPath),
1670
- path: relPath,
1671
- type: "git-repo",
1672
- alreadyTracked: false
1673
- });
1674
- continue;
1675
- }
1676
- walkForGit(projectRoot2, fullPath, depth + 1, results);
1665
+ return null;
1677
1666
  }
1678
1667
  }
1679
- function detectPackageRoots(projectRoot2) {
1680
- const results = [];
1681
- walkForPackages(projectRoot2, projectRoot2, 0, results);
1682
- return results;
1683
- }
1684
- function walkForPackages(projectRoot2, currentDir, depth, results) {
1685
- if (depth > MAX_SCAN_DEPTH) return;
1686
- let entries;
1668
+ function readLockRecordAt(root) {
1687
1669
  try {
1688
- entries = fs7.readdirSync(currentDir, { withFileTypes: true });
1670
+ return normalizeRecord(JSON.parse(fs7.readFileSync(aiDirAt(root, "lock.json"), "utf8")));
1689
1671
  } catch {
1690
- return;
1691
- }
1692
- for (const entry of entries) {
1693
- if (!entry.isDirectory()) continue;
1694
- if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
1695
- const fullPath = path8.join(currentDir, entry.name);
1696
- const relPath = normalizePath(path8.relative(projectRoot2, fullPath));
1697
- if (relPath === "" || relPath === ".") continue;
1698
- const hasMarker = PACKAGE_MARKERS.some((m) => fs7.existsSync(path8.join(fullPath, m)));
1699
- if (hasMarker) {
1700
- results.push({
1701
- suggestedId: pathToId(relPath),
1702
- suggestedName: pathToName(relPath),
1703
- path: relPath,
1704
- type: "package-root",
1705
- alreadyTracked: false
1706
- });
1707
- }
1708
- walkForPackages(projectRoot2, fullPath, depth + 1, results);
1672
+ return null;
1709
1673
  }
1710
1674
  }
1711
- function pathToId(relPath) {
1712
- const basename15 = path8.basename(relPath);
1713
- return basename15.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
1675
+ function normalizeRecord(raw) {
1676
+ const record2 = raw;
1677
+ return { ...record2, lockedBy: normalizeApprover(record2.lockedBy) };
1714
1678
  }
1715
- function pathToName(relPath) {
1716
- const id = pathToId(relPath);
1717
- return id.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
1679
+ function writeLockRecord(record2) {
1680
+ const p = lockPath();
1681
+ fs7.mkdirSync(path8.dirname(p), { recursive: true });
1682
+ const tmp = `${p}.tmp`;
1683
+ fs7.writeFileSync(tmp, JSON.stringify(withSortedMaps(record2), null, 2) + "\n");
1684
+ fs7.renameSync(tmp, p);
1718
1685
  }
1719
- function normalizePath(p) {
1720
- return p.replace(/\\/g, "/");
1686
+ function withSortedMaps(record2) {
1687
+ const sorted = (m) => {
1688
+ if (!m) return void 0;
1689
+ const out = {};
1690
+ for (const k of Object.keys(m).sort()) out[k] = m[k];
1691
+ return out;
1692
+ };
1693
+ const specs = sorted(record2.specs);
1694
+ const children = sorted(record2.children);
1695
+ return {
1696
+ ...record2,
1697
+ ...specs ? { specs } : {},
1698
+ ...children ? { children } : {}
1699
+ };
1721
1700
  }
1722
- var fs7, path8, PACKAGE_MARKERS, MAX_SCAN_DEPTH;
1723
- var init_detection = __esm({
1724
- "src/core/detection.ts"() {
1701
+ var fs7, path8;
1702
+ var init_lockfile = __esm({
1703
+ "src/core/lockfile.ts"() {
1725
1704
  "use strict";
1726
1705
  fs7 = __toESM(require("fs"));
1727
1706
  path8 = __toESM(require("path"));
1728
- init_defaults();
1729
- PACKAGE_MARKERS = [
1730
- "package.json",
1731
- "pyproject.toml",
1732
- "Cargo.toml",
1733
- "go.mod",
1734
- "build.gradle",
1735
- "build.gradle.kts",
1736
- "pom.xml"
1737
- ];
1738
- MAX_SCAN_DEPTH = 5;
1707
+ init_fs();
1708
+ }
1709
+ });
1710
+
1711
+ // src/core/spec-files.ts
1712
+ function readSpecFile(filePath) {
1713
+ return readYamlFile(filePath);
1714
+ }
1715
+ function writeSpecFile(filePath, document) {
1716
+ writeYamlFile(filePath, document);
1717
+ }
1718
+ function listSpecFiles(specsDir) {
1719
+ return listFilesRecursive(specsDir, SPEC_FILE_EXTENSION);
1720
+ }
1721
+ function removeSpecFile(filePath, specsRoot) {
1722
+ if (!fs8.existsSync(filePath)) return false;
1723
+ fs8.unlinkSync(filePath);
1724
+ pruneEmptyDirs(path9.dirname(filePath), path9.resolve(specsRoot));
1725
+ return true;
1726
+ }
1727
+ function pruneEmptyDirs(dir, specsRoot) {
1728
+ let at = dir;
1729
+ while (at !== specsRoot && at.startsWith(specsRoot)) {
1730
+ if (!fs8.existsSync(at) || fs8.readdirSync(at).length > 0) return;
1731
+ fs8.rmdirSync(at);
1732
+ at = path9.dirname(at);
1733
+ }
1734
+ }
1735
+ var fs8, path9, SPEC_FILE_EXTENSION;
1736
+ var init_spec_files = __esm({
1737
+ "src/core/spec-files.ts"() {
1738
+ "use strict";
1739
+ fs8 = __toESM(require("fs"));
1740
+ path9 = __toESM(require("path"));
1741
+ init_fs();
1742
+ init_yaml();
1743
+ SPEC_FILE_EXTENSION = ".yaml";
1739
1744
  }
1740
1745
  });
1741
1746
 
@@ -3295,7 +3300,7 @@ function hasFunctionBody(facts, symbol) {
3295
3300
  }
3296
3301
  function resolveImport(fromFile, specifier, knownPaths) {
3297
3302
  if (!specifier.startsWith(".")) return void 0;
3298
- const joined = pathKey(path9.posix.normalize(path9.posix.join(path9.posix.dirname(fromFile), specifier)));
3303
+ const joined = pathKey(path10.posix.normalize(path10.posix.join(path10.posix.dirname(fromFile), specifier)));
3299
3304
  const candidates = [
3300
3305
  joined,
3301
3306
  joined.replace(/\.js$/, ".ts"),
@@ -3311,11 +3316,11 @@ function resolveImport(fromFile, specifier, knownPaths) {
3311
3316
  }
3312
3317
  return void 0;
3313
3318
  }
3314
- var path9, NO_DECLARED_TYPES;
3319
+ var path10, NO_DECLARED_TYPES;
3315
3320
  var init_code_model = __esm({
3316
3321
  "src/models/code-model.ts"() {
3317
3322
  "use strict";
3318
- path9 = __toESM(require("path"));
3323
+ path10 = __toESM(require("path"));
3319
3324
  NO_DECLARED_TYPES = [];
3320
3325
  }
3321
3326
  });
@@ -3356,186 +3361,6 @@ var init_models = __esm({
3356
3361
  }
3357
3362
  });
3358
3363
 
3359
- // src/config/loader.ts
3360
- function loadTopologyConfig() {
3361
- assertProjectInitialized();
3362
- if (!pathExists(AI_PATHS.topologyConfig())) return createEmptyTopologyConfig();
3363
- const raw = readYamlFile(AI_PATHS.topologyConfig());
3364
- if (!raw) return createEmptyTopologyConfig();
3365
- return TopologyConfigSchema.parse(raw);
3366
- }
3367
- function saveTopologyConfig(config) {
3368
- writeYamlFile(AI_PATHS.topologyConfig(), config);
3369
- }
3370
- var init_loader = __esm({
3371
- "src/config/loader.ts"() {
3372
- "use strict";
3373
- init_fs();
3374
- init_yaml();
3375
- init_models();
3376
- init_paths();
3377
- }
3378
- });
3379
-
3380
- // src/utils/canonical-json.ts
3381
- function canonicalize(value) {
3382
- return JSON.stringify(sortKeys(value));
3383
- }
3384
- function compareOrdinal(a, b) {
3385
- if (a < b) return -1;
3386
- return a > b ? 1 : 0;
3387
- }
3388
- function sortKeys(v) {
3389
- if (Array.isArray(v)) return v.map(sortKeys);
3390
- if (v && typeof v === "object") {
3391
- const src = v;
3392
- const out = {};
3393
- for (const k of Object.keys(src).sort()) {
3394
- if (k === "createdAt" || k === "updatedAt") continue;
3395
- out[k] = sortKeys(src[k]);
3396
- }
3397
- return out;
3398
- }
3399
- return v;
3400
- }
3401
- var init_canonical_json = __esm({
3402
- "src/utils/canonical-json.ts"() {
3403
- "use strict";
3404
- }
3405
- });
3406
-
3407
- // src/core/statehash.ts
3408
- function inIdentityOrder(specs) {
3409
- return specs.map((spec) => ({ spec, content: canonicalize(spec) })).sort((a, b) => compareOrdinal(a.spec.id, b.spec.id) || compareOrdinal(a.content, b.content)).map((entry) => entry.spec);
3410
- }
3411
- function computeStateId() {
3412
- const tree = {
3413
- system: loadSystemSpec(),
3414
- subsystems: inIdentityOrder(loadSubsystemSpecs()),
3415
- components: inIdentityOrder(loadComponentSpecs()),
3416
- interfaces: inIdentityOrder(loadInterfaceSpecs()),
3417
- implementations: inIdentityOrder(loadImplementationSpecs()),
3418
- types: inIdentityOrder(loadTypeSpecs())
3419
- };
3420
- const digest2 = crypto.createHash("sha256").update(canonicalize(tree)).digest("hex");
3421
- return { algorithm: CONTENT_ALGORITHM, digest: digest2 };
3422
- }
3423
- function stateIdEquals(a, b) {
3424
- return !!a && !!b && a.algorithm === b.algorithm && a.digest === b.digest;
3425
- }
3426
- var crypto, CONTENT_ALGORITHM;
3427
- var init_statehash = __esm({
3428
- "src/core/statehash.ts"() {
3429
- "use strict";
3430
- crypto = __toESM(require("crypto"));
3431
- init_specs2();
3432
- init_canonical_json();
3433
- CONTENT_ALGORITHM = "sha256";
3434
- }
3435
- });
3436
-
3437
- // src/core/lockfile.ts
3438
- function lockPath() {
3439
- return aiDir("lock.json");
3440
- }
3441
- function normalizeApprover(value) {
3442
- if (value && typeof value === "object" && typeof value.id === "string") {
3443
- const v = value;
3444
- const source = v.source === "git" || v.source === "hosted" || v.source === "os" ? v.source : "legacy";
3445
- return { id: v.id, ...v.name ? { name: v.name } : {}, source };
3446
- }
3447
- return { id: typeof value === "string" && value ? value : "unknown", source: "legacy" };
3448
- }
3449
- function describeApprover(who) {
3450
- const label = who.name ? `${who.name} (${who.id})` : who.id;
3451
- return who.source === "hosted" ? `${label} [authenticated]` : label;
3452
- }
3453
- function readLockRecord() {
3454
- try {
3455
- return normalizeRecord(JSON.parse(fs8.readFileSync(lockPath(), "utf8")));
3456
- } catch {
3457
- return null;
3458
- }
3459
- }
3460
- function readLockRecordAt(root) {
3461
- try {
3462
- return normalizeRecord(JSON.parse(fs8.readFileSync(aiDirAt(root, "lock.json"), "utf8")));
3463
- } catch {
3464
- return null;
3465
- }
3466
- }
3467
- function normalizeRecord(raw) {
3468
- const record2 = raw;
3469
- return { ...record2, lockedBy: normalizeApprover(record2.lockedBy) };
3470
- }
3471
- function writeLockRecord(record2) {
3472
- const p = lockPath();
3473
- fs8.mkdirSync(path10.dirname(p), { recursive: true });
3474
- const tmp = `${p}.tmp`;
3475
- fs8.writeFileSync(tmp, JSON.stringify(withSortedMaps(record2), null, 2) + "\n");
3476
- fs8.renameSync(tmp, p);
3477
- }
3478
- function withSortedMaps(record2) {
3479
- const sorted = (m) => {
3480
- if (!m) return void 0;
3481
- const out = {};
3482
- for (const k of Object.keys(m).sort()) out[k] = m[k];
3483
- return out;
3484
- };
3485
- const specs = sorted(record2.specs);
3486
- const children = sorted(record2.children);
3487
- return {
3488
- ...record2,
3489
- ...specs ? { specs } : {},
3490
- ...children ? { children } : {}
3491
- };
3492
- }
3493
- var fs8, path10;
3494
- var init_lockfile = __esm({
3495
- "src/core/lockfile.ts"() {
3496
- "use strict";
3497
- fs8 = __toESM(require("fs"));
3498
- path10 = __toESM(require("path"));
3499
- init_fs();
3500
- }
3501
- });
3502
-
3503
- // src/core/spec-files.ts
3504
- function readSpecFile(filePath) {
3505
- return readYamlFile(filePath);
3506
- }
3507
- function writeSpecFile(filePath, document) {
3508
- writeYamlFile(filePath, document);
3509
- }
3510
- function listSpecFiles(specsDir) {
3511
- return listFilesRecursive(specsDir, SPEC_FILE_EXTENSION);
3512
- }
3513
- function removeSpecFile(filePath, specsRoot) {
3514
- if (!fs9.existsSync(filePath)) return false;
3515
- fs9.unlinkSync(filePath);
3516
- pruneEmptyDirs(path11.dirname(filePath), path11.resolve(specsRoot));
3517
- return true;
3518
- }
3519
- function pruneEmptyDirs(dir, specsRoot) {
3520
- let at = dir;
3521
- while (at !== specsRoot && at.startsWith(specsRoot)) {
3522
- if (!fs9.existsSync(at) || fs9.readdirSync(at).length > 0) return;
3523
- fs9.rmdirSync(at);
3524
- at = path11.dirname(at);
3525
- }
3526
- }
3527
- var fs9, path11, SPEC_FILE_EXTENSION;
3528
- var init_spec_files = __esm({
3529
- "src/core/spec-files.ts"() {
3530
- "use strict";
3531
- fs9 = __toESM(require("fs"));
3532
- path11 = __toESM(require("path"));
3533
- init_fs();
3534
- init_yaml();
3535
- SPEC_FILE_EXTENSION = ".yaml";
3536
- }
3537
- });
3538
-
3539
3364
  // src/core/narrative-labels.ts
3540
3365
  function resolveNarrativeLabels(methodName, steps) {
3541
3366
  const errors = [];
@@ -4274,14 +4099,14 @@ function embedJson(value) {
4274
4099
  }
4275
4100
  function loadCytoscapeLib() {
4276
4101
  const candidates = [
4277
- path12.resolve(__dirname, "..", "templates", "canvas", "cytoscape.min.js"),
4102
+ path11.resolve(__dirname, "..", "templates", "canvas", "cytoscape.min.js"),
4278
4103
  // src/core & dist/cli
4279
- path12.resolve(__dirname, "templates", "canvas", "cytoscape.min.js")
4104
+ path11.resolve(__dirname, "templates", "canvas", "cytoscape.min.js")
4280
4105
  // dist (library entry)
4281
4106
  ];
4282
4107
  for (const p of candidates) {
4283
- if (fs10.existsSync(p)) {
4284
- return fs10.readFileSync(p, "utf-8").replace(/<\/script/gi, "<\\/script");
4108
+ if (fs9.existsSync(p)) {
4109
+ return fs9.readFileSync(p, "utf-8").replace(/<\/script/gi, "<\\/script");
4285
4110
  }
4286
4111
  }
4287
4112
  throw new Error("Vendored cytoscape bundle not found (templates/canvas/cytoscape.min.js) \u2014 the wairon installation is incomplete.");
@@ -4293,12 +4118,12 @@ function renderCanvasHtml(model) {
4293
4118
  function escapeHtml(s) {
4294
4119
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
4295
4120
  }
4296
- var fs10, path12, CANVAS_TEMPLATE;
4121
+ var fs9, path11, CANVAS_TEMPLATE;
4297
4122
  var init_canvas = __esm({
4298
4123
  "src/core/canvas.ts"() {
4299
4124
  "use strict";
4300
- fs10 = __toESM(require("fs"));
4301
- path12 = __toESM(require("path"));
4125
+ fs9 = __toESM(require("fs"));
4126
+ path11 = __toESM(require("path"));
4302
4127
  init_specs2();
4303
4128
  init_models();
4304
4129
  init_diagram_export();
@@ -8493,7 +8318,7 @@ function packStoreDir() {
8493
8318
  function packFiles(dir, prefix = "") {
8494
8319
  let entries;
8495
8320
  try {
8496
- entries = fs11.readdirSync(dir, { withFileTypes: true });
8321
+ entries = fs10.readdirSync(dir, { withFileTypes: true });
8497
8322
  } catch {
8498
8323
  return [];
8499
8324
  }
@@ -8501,25 +8326,25 @@ function packFiles(dir, prefix = "") {
8501
8326
  for (const e of entries) {
8502
8327
  if (DIGEST_EXCLUDED.has(e.name)) continue;
8503
8328
  const rel2 = prefix ? `${prefix}/${e.name}` : e.name;
8504
- if (e.isDirectory()) out.push(...packFiles(path13.join(dir, e.name), rel2));
8329
+ if (e.isDirectory()) out.push(...packFiles(path12.join(dir, e.name), rel2));
8505
8330
  else if (e.isFile()) out.push(rel2);
8506
8331
  }
8507
8332
  return out.sort();
8508
8333
  }
8509
8334
  function computePackDigest(packDir) {
8510
8335
  const hash = crypto2.createHash("sha256");
8511
- const abs = path13.resolve(packDir);
8336
+ const abs = path12.resolve(packDir);
8512
8337
  let isFile = false;
8513
8338
  try {
8514
- isFile = fs11.statSync(abs).isFile();
8339
+ isFile = fs10.statSync(abs).isFile();
8515
8340
  } catch {
8516
8341
  }
8517
- const entries = isFile ? [[path13.basename(abs), abs]] : packFiles(abs).map((rel2) => [rel2, path13.join(abs, rel2)]);
8342
+ const entries = isFile ? [[path12.basename(abs), abs]] : packFiles(abs).map((rel2) => [rel2, path12.join(abs, rel2)]);
8518
8343
  for (const [rel2, full] of entries) {
8519
8344
  hash.update(rel2);
8520
8345
  hash.update("\0");
8521
8346
  try {
8522
- hash.update(fs11.readFileSync(full));
8347
+ hash.update(fs10.readFileSync(full));
8523
8348
  } catch {
8524
8349
  }
8525
8350
  hash.update("\0");
@@ -8528,7 +8353,7 @@ function computePackDigest(packDir) {
8528
8353
  }
8529
8354
  function readInstallRecord(packPath) {
8530
8355
  try {
8531
- const raw = readYamlFile(path13.join(packPath, INSTALL_RECORD));
8356
+ const raw = readYamlFile(path12.join(packPath, INSTALL_RECORD));
8532
8357
  return raw && typeof raw === "object" ? raw : {};
8533
8358
  } catch {
8534
8359
  return {};
@@ -8558,14 +8383,14 @@ function installedFrom(location, name, version) {
8558
8383
  function versionedEntries(nameDir, fallbackName) {
8559
8384
  let versions;
8560
8385
  try {
8561
- versions = fs11.readdirSync(nameDir, { withFileTypes: true });
8386
+ versions = fs10.readdirSync(nameDir, { withFileTypes: true });
8562
8387
  } catch {
8563
8388
  return [];
8564
8389
  }
8565
8390
  const out = [];
8566
8391
  for (const v of versions) {
8567
8392
  if (!v.isDirectory()) continue;
8568
- const versionDir = path13.join(nameDir, v.name);
8393
+ const versionDir = path12.join(nameDir, v.name);
8569
8394
  const entryFile = packDirEntry(versionDir);
8570
8395
  if (!entryFile) continue;
8571
8396
  out.push(installedFrom(versionDir, declaredName(entryFile) ?? fallbackName, v.name));
@@ -8573,7 +8398,7 @@ function versionedEntries(nameDir, fallbackName) {
8573
8398
  return out;
8574
8399
  }
8575
8400
  function storeEntryPacks(store, entry) {
8576
- const full = path13.join(store, entry.name);
8401
+ const full = path12.join(store, entry.name);
8577
8402
  if (entry.isFile()) {
8578
8403
  if (!PACK_FILE_RE.test(entry.name)) return [];
8579
8404
  const name = declaredName(full) ?? entry.name.replace(PACK_FILE_RE, "");
@@ -8588,7 +8413,7 @@ function listInstalledPacks() {
8588
8413
  const store = packStoreDir();
8589
8414
  let entries;
8590
8415
  try {
8591
- entries = fs11.readdirSync(store, { withFileTypes: true });
8416
+ entries = fs10.readdirSync(store, { withFileTypes: true });
8592
8417
  } catch {
8593
8418
  return [];
8594
8419
  }
@@ -8605,17 +8430,17 @@ function resolveInstalledPack(name, version) {
8605
8430
  return candidates[0];
8606
8431
  }
8607
8432
  function installPackFromDirectory(sourceDir, origin) {
8608
- const abs = path13.resolve(sourceDir);
8433
+ const abs = path12.resolve(sourceDir);
8609
8434
  const entryFile = packDirEntry(abs);
8610
8435
  if (!entryFile) {
8611
8436
  throw new Error(`"${sourceDir}" is not a pack directory (no pack.yaml | pack.cjs | index.cjs | \u2026).`);
8612
8437
  }
8613
- const manifest = readManifest(entryFile, path13.dirname(entryFile));
8438
+ const manifest = readManifest(entryFile, path12.dirname(entryFile));
8614
8439
  const version = manifest.version && manifest.version.length > 0 ? manifest.version : UNVERSIONED;
8615
- const dest = path13.join(packStoreDir(), manifest.name, version);
8616
- fs11.rmSync(dest, { recursive: true, force: true });
8617
- fs11.mkdirSync(path13.dirname(dest), { recursive: true });
8618
- fs11.cpSync(abs, dest, { recursive: true });
8440
+ const dest = path12.join(packStoreDir(), manifest.name, version);
8441
+ fs10.rmSync(dest, { recursive: true, force: true });
8442
+ fs10.mkdirSync(path12.dirname(dest), { recursive: true });
8443
+ fs10.cpSync(abs, dest, { recursive: true });
8619
8444
  const digest2 = computePackDigest(dest);
8620
8445
  const record2 = {
8621
8446
  origin,
@@ -8623,7 +8448,7 @@ function installPackFromDirectory(sourceDir, origin) {
8623
8448
  waironVersion: WAIRON_VERSION,
8624
8449
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
8625
8450
  };
8626
- writeYamlFile(path13.join(dest, INSTALL_RECORD), record2);
8451
+ writeYamlFile(path12.join(dest, INSTALL_RECORD), record2);
8627
8452
  return {
8628
8453
  name: manifest.name,
8629
8454
  version,
@@ -8638,23 +8463,23 @@ function uninstallPack(name, version) {
8638
8463
  const targets = listInstalledPacks().filter((p) => p.name === name && (!version || p.version === version));
8639
8464
  if (targets.length === 0) return false;
8640
8465
  for (const target of targets) {
8641
- fs11.rmSync(target.path, { recursive: true, force: true });
8642
- const parent = path13.dirname(target.path);
8643
- if (path13.resolve(parent) === path13.resolve(packStoreDir())) continue;
8466
+ fs10.rmSync(target.path, { recursive: true, force: true });
8467
+ const parent = path12.dirname(target.path);
8468
+ if (path12.resolve(parent) === path12.resolve(packStoreDir())) continue;
8644
8469
  try {
8645
- if (fs11.readdirSync(parent).length === 0) fs11.rmdirSync(parent);
8470
+ if (fs10.readdirSync(parent).length === 0) fs10.rmdirSync(parent);
8646
8471
  } catch {
8647
8472
  }
8648
8473
  }
8649
8474
  return true;
8650
8475
  }
8651
- var crypto2, fs11, path13, UNVERSIONED, INSTALL_RECORD, DIGEST_EXCLUDED, PACK_FILE_RE;
8476
+ var crypto2, fs10, path12, UNVERSIONED, INSTALL_RECORD, DIGEST_EXCLUDED, PACK_FILE_RE;
8652
8477
  var init_packstore = __esm({
8653
8478
  "src/core/packstore.ts"() {
8654
8479
  "use strict";
8655
8480
  crypto2 = __toESM(require("crypto"));
8656
- fs11 = __toESM(require("fs"));
8657
- path13 = __toESM(require("path"));
8481
+ fs10 = __toESM(require("fs"));
8482
+ path12 = __toESM(require("path"));
8658
8483
  init_yaml();
8659
8484
  init_defaults();
8660
8485
  init_version();
@@ -8681,26 +8506,26 @@ function emptyExtensions() {
8681
8506
  return { packNames: [], packs: [], rules: [], profiles: {}, languages: {}, skills: [], patterns: [], guarantees: [], assertions: [], instructions: [], errors: [], selectionFailures: [] };
8682
8507
  }
8683
8508
  function globalPacksDir() {
8684
- return process.env.WAIRON_PACKS_DIR ?? path14.join(os4.homedir(), ".wairon", "packs");
8509
+ return process.env.WAIRON_PACKS_DIR ?? path13.join(os4.homedir(), ".wairon", "packs");
8685
8510
  }
8686
8511
  function packDirEntry(dir) {
8687
8512
  for (const name of PACK_DIR_ENTRIES) {
8688
- const p = path14.join(dir, name);
8689
- if (fs12.existsSync(p)) return p;
8513
+ const p = path13.join(dir, name);
8514
+ if (fs11.existsSync(p)) return p;
8690
8515
  }
8691
8516
  return null;
8692
8517
  }
8693
8518
  function latestVersionDir(nameDir) {
8694
8519
  let versions;
8695
8520
  try {
8696
- versions = fs12.readdirSync(nameDir, { withFileTypes: true });
8521
+ versions = fs11.readdirSync(nameDir, { withFileTypes: true });
8697
8522
  } catch {
8698
8523
  return null;
8699
8524
  }
8700
8525
  let best = null;
8701
8526
  for (const v of versions) {
8702
8527
  if (!v.isDirectory()) continue;
8703
- const dir = path14.join(nameDir, v.name);
8528
+ const dir = path13.join(nameDir, v.name);
8704
8529
  if (!packDirEntry(dir)) continue;
8705
8530
  if (!best || isNewerVersion(best.version, v.name)) best = { version: v.name, dir };
8706
8531
  }
@@ -8709,13 +8534,13 @@ function latestVersionDir(nameDir) {
8709
8534
  function discoverPacks(dir) {
8710
8535
  let entries;
8711
8536
  try {
8712
- entries = fs12.readdirSync(dir, { withFileTypes: true });
8537
+ entries = fs11.readdirSync(dir, { withFileTypes: true });
8713
8538
  } catch {
8714
8539
  return [];
8715
8540
  }
8716
8541
  const refs = [];
8717
8542
  for (const e of entries.sort((a, b) => compareOrdinal(a.name, b.name))) {
8718
- const full = path14.join(dir, e.name);
8543
+ const full = path13.join(dir, e.name);
8719
8544
  if (e.isDirectory()) {
8720
8545
  if (packDirEntry(full)) refs.push(full);
8721
8546
  else {
@@ -8745,17 +8570,17 @@ function mergePack(out, pack, ref, scope, packDir) {
8745
8570
  foreignBuiltins: [.../* @__PURE__ */ new Set([...existing.foreignBuiltins, ...def.foreignBuiltins])]
8746
8571
  } : def;
8747
8572
  }
8748
- for (const s of pack.skills) out.skills.push({ ...s, pack: pack.name, packVersion: pack.version, sourcePath: path14.resolve(packDir, s.source) });
8573
+ for (const s of pack.skills) out.skills.push({ ...s, pack: pack.name, packVersion: pack.version, sourcePath: path13.resolve(packDir, s.source) });
8749
8574
  for (const p of pack.patterns) out.patterns.push({ ...p, pack: pack.name });
8750
8575
  out.guarantees = [.../* @__PURE__ */ new Set([...out.guarantees, ...pack.guarantees])];
8751
8576
  for (const a of pack.assertions) out.assertions.push({ ...a, pack: pack.name, fullCode: assertionFullCode(pack.name, a.code) });
8752
8577
  for (const b of pack.instructions) out.instructions.push({ ...b, pack: pack.name });
8753
8578
  }
8754
8579
  function resolvePackRef(ref, projectRoot2) {
8755
- const pathLike = ref.startsWith(".") || path14.isAbsolute(ref) || isYamlPath(ref);
8580
+ const pathLike = ref.startsWith(".") || path13.isAbsolute(ref) || isYamlPath(ref);
8756
8581
  if (!pathLike) return ref;
8757
- const resolved = path14.resolve(projectRoot2, ref);
8758
- if (fs12.existsSync(resolved) && fs12.statSync(resolved).isDirectory()) {
8582
+ const resolved = path13.resolve(projectRoot2, ref);
8583
+ if (fs11.existsSync(resolved) && fs11.statSync(resolved).isDirectory()) {
8759
8584
  const entry = packDirEntry(resolved);
8760
8585
  if (!entry) throw new Error(`directory pack has no entry file (${PACK_DIR_ENTRIES.join(" | ")})`);
8761
8586
  return entry;
@@ -8768,7 +8593,7 @@ function readManifest(target, projectRoot2) {
8768
8593
  if (raw == null) throw new Error("file not found or empty");
8769
8594
  return { ...DeclarativePackSchema.parse(raw), rules: [] };
8770
8595
  }
8771
- const req = (0, import_module.createRequire)(path14.join(projectRoot2, "package.json"));
8596
+ const req = (0, import_module.createRequire)(path13.join(projectRoot2, "package.json"));
8772
8597
  const modRaw = req(target);
8773
8598
  const mod = modRaw?.default ?? modRaw;
8774
8599
  const parsed = DeclarativePackSchema.parse({
@@ -8796,7 +8621,7 @@ function loadExtensionPacks(refs, projectRoot2) {
8796
8621
  try {
8797
8622
  const target = resolvePackRef(ref, projectRoot2);
8798
8623
  const manifest = readManifest(target, projectRoot2);
8799
- mergePack(out, manifest, ref, scope, path14.dirname(target));
8624
+ mergePack(out, manifest, ref, scope, path13.dirname(target));
8800
8625
  for (const r of manifest.rules) out.rules.push(r);
8801
8626
  } catch (e) {
8802
8627
  out.errors.push(`Extension pack "${ref}" failed to load: ${e instanceof Error ? e.message : String(e)}`);
@@ -8839,7 +8664,7 @@ function defaultPackSelections() {
8839
8664
  const entry = packDirEntry(pack.path) ?? pack.path;
8840
8665
  let manifest;
8841
8666
  try {
8842
- manifest = readManifest(entry, path14.dirname(entry));
8667
+ manifest = readManifest(entry, path13.dirname(entry));
8843
8668
  } catch {
8844
8669
  continue;
8845
8670
  }
@@ -8878,7 +8703,7 @@ function diagnoseProjectPacks() {
8878
8703
  const globalsApplied = [];
8879
8704
  if (globalsOn) {
8880
8705
  for (const ref of discoverPacks(globalPacksDir())) {
8881
- const match = installed.find((p) => path14.resolve(p.path) === path14.resolve(ref));
8706
+ const match = installed.find((p) => path13.resolve(p.path) === path13.resolve(ref));
8882
8707
  if (match) globalsApplied.push({ name: match.name, version: match.version });
8883
8708
  }
8884
8709
  }
@@ -8956,7 +8781,7 @@ function verifyIntegrity(selection, packDir, where) {
8956
8781
  };
8957
8782
  }
8958
8783
  function detectBundleStoreDrift(selection, bundleDir) {
8959
- const bundledVersion = path14.basename(bundleDir);
8784
+ const bundledVersion = path13.basename(bundleDir);
8960
8785
  const installed = resolveInstalledPack(selection.name, bundledVersion);
8961
8786
  if (!installed) return void 0;
8962
8787
  const bundleDigest = computePackDigest(bundleDir);
@@ -8970,34 +8795,34 @@ function detectBundleStoreDrift(selection, bundleDir) {
8970
8795
  };
8971
8796
  }
8972
8797
  function findBundledPack(projectRoot2, selection) {
8973
- const base = path14.join(projectRoot2, ".wai", "packs", selection.name);
8798
+ const base = path13.join(projectRoot2, ".wai", "packs", selection.name);
8974
8799
  if (selection.version) {
8975
- const pinned = path14.join(base, selection.version);
8800
+ const pinned = path13.join(base, selection.version);
8976
8801
  return packDirEntry(pinned) ? pinned : null;
8977
8802
  }
8978
8803
  if (packDirEntry(base)) return base;
8979
8804
  let versions;
8980
8805
  try {
8981
- versions = fs12.readdirSync(base, { withFileTypes: true });
8806
+ versions = fs11.readdirSync(base, { withFileTypes: true });
8982
8807
  } catch {
8983
8808
  return null;
8984
8809
  }
8985
8810
  let best = null;
8986
8811
  for (const v of versions) {
8987
8812
  if (!v.isDirectory()) continue;
8988
- const dir = path14.join(base, v.name);
8813
+ const dir = path13.join(base, v.name);
8989
8814
  if (!packDirEntry(dir)) continue;
8990
8815
  if (!best || isNewerVersion(best.version, v.name)) best = { version: v.name, dir };
8991
8816
  }
8992
8817
  return best?.dir ?? null;
8993
8818
  }
8994
- var fs12, os4, path14, import_module, import_zod9, ProfileDefSchema, LanguagePackDefSchema, PackSkillSchema, PackInstructionBlockSchema, PackInstructionsSchema, PatternDefSchema, AssertionSelectorSchema, assertionBase, PackAssertionSchema, DeclarativePackSchema, EXTENDABLE_BUILTIN_SKILLS, GLOBAL_PACKS_DEFAULT, PACK_DIR_ENTRIES, isYamlPath;
8819
+ var fs11, os4, path13, import_module, import_zod9, ProfileDefSchema, LanguagePackDefSchema, PackSkillSchema, PackInstructionBlockSchema, PackInstructionsSchema, PatternDefSchema, AssertionSelectorSchema, assertionBase, PackAssertionSchema, DeclarativePackSchema, EXTENDABLE_BUILTIN_SKILLS, GLOBAL_PACKS_DEFAULT, PACK_DIR_ENTRIES, isYamlPath;
8995
8820
  var init_extensions = __esm({
8996
8821
  "src/core/extensions.ts"() {
8997
8822
  "use strict";
8998
- fs12 = __toESM(require("fs"));
8823
+ fs11 = __toESM(require("fs"));
8999
8824
  os4 = __toESM(require("os"));
9000
- path14 = __toESM(require("path"));
8825
+ path13 = __toESM(require("path"));
9001
8826
  import_module = require("module");
9002
8827
  import_zod9 = require("zod");
9003
8828
  init_yaml();
@@ -14446,12 +14271,109 @@ var init_carried_debt = __esm({
14446
14271
  }
14447
14272
  });
14448
14273
 
14274
+ // src/core/rules/heuristic/method-cohesion.ts
14275
+ function groupsSharingCollaborators(calledBy) {
14276
+ const parent = /* @__PURE__ */ new Map();
14277
+ const find = (m) => {
14278
+ let root = m;
14279
+ while (parent.get(root) !== root) root = parent.get(root);
14280
+ return root;
14281
+ };
14282
+ const union = (a, b) => {
14283
+ const [ra, rb] = [find(a), find(b)];
14284
+ if (ra !== rb) parent.set(ra, rb);
14285
+ };
14286
+ const methods = [...calledBy.keys()];
14287
+ for (const m of methods) parent.set(m, m);
14288
+ for (let i = 0; i < methods.length; i++) {
14289
+ for (let j = i + 1; j < methods.length; j++) {
14290
+ const shares = [...calledBy.get(methods[i])].some((c) => calledBy.get(methods[j])?.has(c));
14291
+ if (shares) union(methods[i], methods[j]);
14292
+ }
14293
+ }
14294
+ const groups = /* @__PURE__ */ new Map();
14295
+ for (const m of methods) {
14296
+ const root = find(m);
14297
+ groups.set(root, [...groups.get(root) ?? [], m]);
14298
+ }
14299
+ return [...groups.values()];
14300
+ }
14301
+ function isForwardingMethod(narrative) {
14302
+ let handoffs = 0;
14303
+ for (const step of narrative) {
14304
+ if (step.type === "call" || step.type === "dispatch") handoffs++;
14305
+ else if (step.type !== "return") return false;
14306
+ }
14307
+ return handoffs === 1;
14308
+ }
14309
+ function isPureForwarder(methods) {
14310
+ const narrated = methods.filter((m) => (m.narrative ?? []).length > 0);
14311
+ return narrated.length > 0 && narrated.every((m) => isForwardingMethod(m.narrative ?? []));
14312
+ }
14313
+ function pureForwarderComponents(ctx) {
14314
+ const realized = /* @__PURE__ */ new Map();
14315
+ for (const impl of ctx.implementations) {
14316
+ const intf = ctx.interfaceMap.get(impl.contract);
14317
+ if (!intf) continue;
14318
+ realized.set(intf.component, [...realized.get(intf.component) ?? [], ...impl.methods]);
14319
+ }
14320
+ return new Set([...realized].filter(([, methods]) => isPureForwarder(methods)).map(([id]) => id));
14321
+ }
14322
+ var methodCohesionRule;
14323
+ var init_method_cohesion = __esm({
14324
+ "src/core/rules/heuristic/method-cohesion.ts"() {
14325
+ "use strict";
14326
+ methodCohesionRule = {
14327
+ name: "method-cohesion",
14328
+ description: "An Orchestrator's methods share collaborators; when they split into groups that call no component in common, the component holds more than one responsibility. Reported only where two or more groups each hold two or more methods, so a single specialized method is not a finding \u2014 and never on a pure forwarder, whose every narrated method is a single hand-off and whose responsibility therefore lives in what it forwards to.",
14329
+ codes: [
14330
+ { code: "INCOHESIVE_METHODS", defaultSeverity: "warning", summary: "Orchestrator's methods form two or more groups of two or more that share no called component" }
14331
+ ],
14332
+ check(ctx) {
14333
+ const subjects = [];
14334
+ for (const impl of ctx.implementations) {
14335
+ const intf = ctx.interfaceMap.get(impl.contract);
14336
+ const comp = intf ? ctx.componentMap.get(intf.component) : void 0;
14337
+ if (!comp || comp.componentType !== "Orchestrator") continue;
14338
+ if (isPureForwarder(impl.methods)) continue;
14339
+ const calledBy = /* @__PURE__ */ new Map();
14340
+ for (const method2 of impl.methods) {
14341
+ const reached = /* @__PURE__ */ new Set();
14342
+ for (const step of method2.narrative ?? []) {
14343
+ if ((step.type === "call" || step.type === "dispatch") && step.targetComponent) reached.add(step.targetComponent);
14344
+ }
14345
+ if (reached.size > 0) calledBy.set(method2.name, reached);
14346
+ }
14347
+ subjects.push({ implementation: impl, component: comp, calledBy });
14348
+ }
14349
+ for (const { component, calledBy } of subjects) {
14350
+ if (calledBy.size < 2) continue;
14351
+ const groups = groupsSharingCollaborators(calledBy).filter((g) => g.length >= 2);
14352
+ if (groups.length < 2) continue;
14353
+ const described = groups.map((g) => {
14354
+ const collaborators = [...new Set(g.flatMap((m) => [...calledBy.get(m)]))].sort();
14355
+ return `${g.join(", ")} (calling ${collaborators.join(", ")})`;
14356
+ }).join("; ");
14357
+ ctx.addIssue(
14358
+ "warning",
14359
+ "INCOHESIVE_METHODS",
14360
+ `Orchestrator "${component.id}" holds ${groups.length} groups of methods that share no called component: ${described}. Split it along those groups, move the logic out until every method is a single hand-off (a pure forwarder is exempt), or acknowledge a deliberate facade with a reasoned lint.allow for INCOHESIVE_METHODS.`,
14361
+ component.id,
14362
+ ctx.isComponentDraft(component.id)
14363
+ );
14364
+ }
14365
+ }
14366
+ };
14367
+ }
14368
+ });
14369
+
14449
14370
  // src/core/rules/heuristic/coupling-health.ts
14450
14371
  var DEFAULT_GOD_COMPONENT_THRESHOLD, couplingRule;
14451
14372
  var init_coupling_health = __esm({
14452
14373
  "src/core/rules/heuristic/coupling-health.ts"() {
14453
14374
  "use strict";
14454
14375
  init_models();
14376
+ init_method_cohesion();
14455
14377
  DEFAULT_GOD_COMPONENT_THRESHOLD = 8;
14456
14378
  couplingRule = {
14457
14379
  name: "coupling-health",
@@ -14460,7 +14382,7 @@ var init_coupling_health = __esm({
14460
14382
  { code: "MUTUAL_SUBSYSTEM_DEPENDENCY", defaultSeverity: "warning", summary: "Subsystems depend on each other without a declared trusted link" },
14461
14383
  { code: "INVALID_TRUSTED_LINK", defaultSeverity: "error", summary: "trustedLinks references a non-existent subsystem" },
14462
14384
  { code: "UNUSED_TRUSTED_LINK", defaultSeverity: "warning", summary: "trustedLinks declares a peer no dependency actually reaches" },
14463
- { code: "GOD_COMPONENT", defaultSeverity: "warning", summary: "Component with excessive dependency fan-out" }
14385
+ { code: "GOD_COMPONENT", defaultSeverity: "warning", summary: "Component with excessive dependency fan-out; not reported on a pure forwarder, whose every narrated method is a single hand-off" }
14464
14386
  ],
14465
14387
  check(ctx) {
14466
14388
  const subsystemDeps = /* @__PURE__ */ new Map();
@@ -14532,9 +14454,10 @@ var init_coupling_health = __esm({
14532
14454
  );
14533
14455
  }
14534
14456
  }
14457
+ const forwarders = pureForwarderComponents(ctx);
14535
14458
  for (const comp of ctx.components) {
14536
14459
  const threshold = ctx.complexityConfigFor(comp.subsystem)?.maxComponentDependencies ?? DEFAULT_GOD_COMPONENT_THRESHOLD;
14537
- if (comp.dependsOn.length > threshold) {
14460
+ if (comp.dependsOn.length > threshold && !forwarders.has(comp.id)) {
14538
14461
  ctx.addIssue(
14539
14462
  "warning",
14540
14463
  "GOD_COMPONENT",
@@ -15158,6 +15081,7 @@ var init_complexity_and_metadata = __esm({
15158
15081
  "src/core/rules/heuristic/complexity-and-metadata.ts"() {
15159
15082
  "use strict";
15160
15083
  init_models();
15084
+ init_method_cohesion();
15161
15085
  complexityRule = {
15162
15086
  name: "complexity-and-metadata",
15163
15087
  description: "Enforces metadata documentation completeness (checking if descriptions are missing or too short) and structural complexity caps (limits on methods per interface, parameters per method, dependencies per component, and components per subsystem) configured globally or in architectural profiles. A narrative's own length and shape are judged by narrative-complexity.",
@@ -15166,7 +15090,7 @@ var init_complexity_and_metadata = __esm({
15166
15090
  { code: "DESCRIPTION_TOO_SHORT", defaultSeverity: "warning", summary: "Description is shorter than the configured minimum length" },
15167
15091
  { code: "EXCESSIVE_METHODS", defaultSeverity: "warning", summary: "Interface declares more methods than the configured limit" },
15168
15092
  { code: "EXCESSIVE_METHOD_PARAMS", defaultSeverity: "warning", summary: "Interface method declares more parameters than the configured limit" },
15169
- { code: "EXCESSIVE_DEPENDENCIES", defaultSeverity: "warning", summary: "Component has more dependencies than the configured limit" },
15093
+ { code: "EXCESSIVE_DEPENDENCIES", defaultSeverity: "warning", summary: "Component has more dependencies than the configured limit; not reported on a pure forwarder, whose fan-out is a routing table rather than knowledge" },
15170
15094
  { code: "EXCESSIVE_SUBSYSTEM_COMPONENTS", defaultSeverity: "warning", summary: "Subsystem has more direct components than the configured limit" }
15171
15095
  ],
15172
15096
  check(ctx) {
@@ -15186,6 +15110,7 @@ var init_complexity_and_metadata = __esm({
15186
15110
  );
15187
15111
  }
15188
15112
  }
15113
+ const forwarders = pureForwarderComponents(ctx);
15189
15114
  for (const comp of ctx.components) {
15190
15115
  const docConfig = ctx.documentationConfigFor(comp.subsystem);
15191
15116
  const complexityConfig = ctx.complexityConfigFor(comp.subsystem);
@@ -15199,7 +15124,7 @@ var init_complexity_and_metadata = __esm({
15199
15124
  "Component",
15200
15125
  isDraft
15201
15126
  );
15202
- if (complexityConfig?.maxComponentDependencies !== void 0 && comp.dependsOn.length > complexityConfig.maxComponentDependencies) {
15127
+ if (complexityConfig?.maxComponentDependencies !== void 0 && comp.dependsOn.length > complexityConfig.maxComponentDependencies && !forwarders.has(comp.id)) {
15203
15128
  ctx.addIssue(
15204
15129
  "warning",
15205
15130
  "EXCESSIVE_DEPENDENCIES",
@@ -15498,90 +15423,6 @@ var init_naming_discipline = __esm({
15498
15423
  }
15499
15424
  });
15500
15425
 
15501
- // src/core/rules/heuristic/method-cohesion.ts
15502
- function groupsSharingCollaborators(calledBy) {
15503
- const parent = /* @__PURE__ */ new Map();
15504
- const find = (m) => {
15505
- let root = m;
15506
- while (parent.get(root) !== root) root = parent.get(root);
15507
- return root;
15508
- };
15509
- const union = (a, b) => {
15510
- const [ra, rb] = [find(a), find(b)];
15511
- if (ra !== rb) parent.set(ra, rb);
15512
- };
15513
- const methods = [...calledBy.keys()];
15514
- for (const m of methods) parent.set(m, m);
15515
- for (let i = 0; i < methods.length; i++) {
15516
- for (let j = i + 1; j < methods.length; j++) {
15517
- const shares = [...calledBy.get(methods[i])].some((c) => calledBy.get(methods[j])?.has(c));
15518
- if (shares) union(methods[i], methods[j]);
15519
- }
15520
- }
15521
- const groups = /* @__PURE__ */ new Map();
15522
- for (const m of methods) {
15523
- const root = find(m);
15524
- groups.set(root, [...groups.get(root) ?? [], m]);
15525
- }
15526
- return [...groups.values()];
15527
- }
15528
- function isForwardingMethod(narrative) {
15529
- let handoffs = 0;
15530
- for (const step of narrative) {
15531
- if (step.type === "call" || step.type === "dispatch") handoffs++;
15532
- else if (step.type !== "return") return false;
15533
- }
15534
- return handoffs === 1;
15535
- }
15536
- var methodCohesionRule;
15537
- var init_method_cohesion = __esm({
15538
- "src/core/rules/heuristic/method-cohesion.ts"() {
15539
- "use strict";
15540
- methodCohesionRule = {
15541
- name: "method-cohesion",
15542
- description: "An Orchestrator's methods share collaborators; when they split into groups that call no component in common, the component holds more than one responsibility. Reported only where two or more groups each hold two or more methods, so a single specialized method is not a finding \u2014 and never on a pure forwarder, whose every narrated method is a single hand-off and whose responsibility therefore lives in what it forwards to.",
15543
- codes: [
15544
- { code: "INCOHESIVE_METHODS", defaultSeverity: "warning", summary: "Orchestrator's methods form two or more groups of two or more that share no called component" }
15545
- ],
15546
- check(ctx) {
15547
- const subjects = [];
15548
- for (const impl of ctx.implementations) {
15549
- const intf = ctx.interfaceMap.get(impl.contract);
15550
- const comp = intf ? ctx.componentMap.get(intf.component) : void 0;
15551
- if (!comp || comp.componentType !== "Orchestrator") continue;
15552
- const narrated = impl.methods.filter((m) => (m.narrative ?? []).length > 0);
15553
- if (narrated.length > 0 && narrated.every((m) => isForwardingMethod(m.narrative ?? []))) continue;
15554
- const calledBy = /* @__PURE__ */ new Map();
15555
- for (const method2 of impl.methods) {
15556
- const reached = /* @__PURE__ */ new Set();
15557
- for (const step of method2.narrative ?? []) {
15558
- if ((step.type === "call" || step.type === "dispatch") && step.targetComponent) reached.add(step.targetComponent);
15559
- }
15560
- if (reached.size > 0) calledBy.set(method2.name, reached);
15561
- }
15562
- subjects.push({ implementation: impl, component: comp, calledBy });
15563
- }
15564
- for (const { component, calledBy } of subjects) {
15565
- if (calledBy.size < 2) continue;
15566
- const groups = groupsSharingCollaborators(calledBy).filter((g) => g.length >= 2);
15567
- if (groups.length < 2) continue;
15568
- const described = groups.map((g) => {
15569
- const collaborators = [...new Set(g.flatMap((m) => [...calledBy.get(m)]))].sort();
15570
- return `${g.join(", ")} (calling ${collaborators.join(", ")})`;
15571
- }).join("; ");
15572
- ctx.addIssue(
15573
- "warning",
15574
- "INCOHESIVE_METHODS",
15575
- `Orchestrator "${component.id}" holds ${groups.length} groups of methods that share no called component: ${described}. Split it along those groups, move the logic out until every method is a single hand-off (a pure forwarder is exempt), or acknowledge a deliberate facade with a reasoned lint.allow for INCOHESIVE_METHODS.`,
15576
- component.id,
15577
- ctx.isComponentDraft(component.id)
15578
- );
15579
- }
15580
- }
15581
- };
15582
- }
15583
- });
15584
-
15585
15426
  // src/core/rules/repository.ts
15586
15427
  function addRule(rule) {
15587
15428
  ruleSet.push(rule);
@@ -15993,7 +15834,7 @@ function resolveTypeScript(projectRoot2) {
15993
15834
  return cached.ts;
15994
15835
  }
15995
15836
  let ts = null;
15996
- const bases = [path15.join(projectRoot2, "package.json"), __filename];
15837
+ const bases = [path14.join(projectRoot2, "package.json"), __filename];
15997
15838
  for (const base of bases) {
15998
15839
  try {
15999
15840
  const req = (0, import_module2.createRequire)(base);
@@ -16208,7 +16049,7 @@ function walkExact(ts, sourceText, fileName) {
16208
16049
  }
16209
16050
  function resolveRelativeModule(fromFile, specifier) {
16210
16051
  if (!specifier.startsWith(".")) return null;
16211
- const base = path15.resolve(path15.dirname(fromFile), specifier);
16052
+ const base = path14.resolve(path14.dirname(fromFile), specifier);
16212
16053
  const candidates = [
16213
16054
  base,
16214
16055
  base.replace(/\.js$/, ".ts"),
@@ -16216,12 +16057,12 @@ function resolveRelativeModule(fromFile, specifier) {
16216
16057
  `${base}.ts`,
16217
16058
  `${base}.tsx`,
16218
16059
  `${base}.js`,
16219
- path15.join(base, "index.ts"),
16220
- path15.join(base, "index.js")
16060
+ path14.join(base, "index.ts"),
16061
+ path14.join(base, "index.js")
16221
16062
  ];
16222
16063
  for (const c of candidates) {
16223
16064
  try {
16224
- if (fs13.statSync(c).isFile()) return c;
16065
+ if (fs12.statSync(c).isFile()) return c;
16225
16066
  } catch {
16226
16067
  }
16227
16068
  }
@@ -16229,11 +16070,11 @@ function resolveRelativeModule(fromFile, specifier) {
16229
16070
  }
16230
16071
  function reexportTarget(ts, filePath, specifier, projectRoot2, visited, exactCache) {
16231
16072
  const target = resolveRelativeModule(filePath, specifier);
16232
- if (!target || path15.relative(projectRoot2, target).startsWith("..")) return null;
16073
+ if (!target || path14.relative(projectRoot2, target).startsWith("..")) return null;
16233
16074
  let targetFacts = exactCache.get(target);
16234
16075
  if (targetFacts === void 0) {
16235
16076
  try {
16236
- targetFacts = walkExact(ts, fs13.readFileSync(target, "utf8"), target);
16077
+ targetFacts = walkExact(ts, fs12.readFileSync(target, "utf8"), target);
16237
16078
  } catch {
16238
16079
  targetFacts = null;
16239
16080
  }
@@ -16251,7 +16092,7 @@ function carryBody(facts, target, targetPath, local, exported, projectRoot2) {
16251
16092
  if (score !== void 0) facts.complexity.set(exported, Math.max(facts.complexity.get(exported) ?? 0, score));
16252
16093
  const targetCalls = target.calls.get(local);
16253
16094
  if (!targetCalls) return;
16254
- const carriedFrom = pathKey(path15.relative(projectRoot2, targetPath));
16095
+ const carriedFrom = pathKey(path14.relative(projectRoot2, targetPath));
16255
16096
  const sites = facts.calls.get(exported) ?? /* @__PURE__ */ new Map();
16256
16097
  for (const [key, site] of targetCalls) sites.set(carriedFrom + "|" + key, { ...site, from: site.from ?? carriedFrom });
16257
16098
  facts.calls.set(exported, sites);
@@ -16281,24 +16122,24 @@ function walkDeclaredRoots(roots, exclude, projectRoot2) {
16281
16122
  const excluded = exclude.map(pathKey);
16282
16123
  const isExcluded = (key) => excluded.some((e) => key === e || key.startsWith(`${e}/`));
16283
16124
  const record2 = (absolute) => {
16284
- const key = pathKey(path15.relative(projectRoot2, absolute));
16125
+ const key = pathKey(path14.relative(projectRoot2, absolute));
16285
16126
  if (seen.has(key) || isExcluded(key)) return;
16286
- if (!EXTENSION_LANGUAGE[path15.extname(key).toLowerCase()]) return;
16127
+ if (!EXTENSION_LANGUAGE[path14.extname(key).toLowerCase()]) return;
16287
16128
  seen.add(key);
16288
16129
  found.push(key);
16289
16130
  };
16290
16131
  const descend = (dir) => {
16291
16132
  let entries;
16292
16133
  try {
16293
- entries = fs13.readdirSync(dir, { withFileTypes: true });
16134
+ entries = fs12.readdirSync(dir, { withFileTypes: true });
16294
16135
  } catch {
16295
16136
  return;
16296
16137
  }
16297
16138
  for (const entry of entries.slice().sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
16298
- const child = path15.join(dir, entry.name);
16139
+ const child = path14.join(dir, entry.name);
16299
16140
  if (entry.isDirectory()) {
16300
16141
  if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
16301
- if (isExcluded(pathKey(path15.relative(projectRoot2, child)))) continue;
16142
+ if (isExcluded(pathKey(path14.relative(projectRoot2, child)))) continue;
16302
16143
  descend(child);
16303
16144
  } else if (entry.isFile()) {
16304
16145
  record2(child);
@@ -16307,13 +16148,13 @@ function walkDeclaredRoots(roots, exclude, projectRoot2) {
16307
16148
  };
16308
16149
  for (const root of roots) {
16309
16150
  const key = pathKey(root);
16310
- if (path15.isAbsolute(key) || path15.normalize(key).split(path15.sep)[0] === "..") continue;
16311
- const absolute = path15.resolve(projectRoot2, key);
16312
- if (path15.relative(projectRoot2, absolute).startsWith("..")) continue;
16151
+ if (path14.isAbsolute(key) || path14.normalize(key).split(path14.sep)[0] === "..") continue;
16152
+ const absolute = path14.resolve(projectRoot2, key);
16153
+ if (path14.relative(projectRoot2, absolute).startsWith("..")) continue;
16313
16154
  if (isExcluded(key)) continue;
16314
16155
  let stat;
16315
16156
  try {
16316
- stat = fs13.statSync(absolute);
16157
+ stat = fs12.statSync(absolute);
16317
16158
  } catch {
16318
16159
  continue;
16319
16160
  }
@@ -16339,23 +16180,23 @@ function buildCodeModel(implementations, types, projectRoot2, sourceRoots = [],
16339
16180
  if (seen.has(sourcePath)) continue;
16340
16181
  seen.add(sourcePath);
16341
16182
  const empty = { declaredNames: [], anchoredNames: [], exportedNames: [], imports: [], reexports: [] };
16342
- if (path15.isAbsolute(sourcePath) || path15.normalize(sourcePath).split(path15.sep)[0] === "..") {
16183
+ if (path14.isAbsolute(sourcePath) || path14.normalize(sourcePath).split(path14.sep)[0] === "..") {
16343
16184
  files.push({ path: sourcePath, status: "escaped", ...empty });
16344
16185
  continue;
16345
16186
  }
16346
- const absolute = path15.resolve(projectRoot2, sourcePath);
16347
- if (path15.relative(projectRoot2, absolute).startsWith("..")) {
16187
+ const absolute = path14.resolve(projectRoot2, sourcePath);
16188
+ if (path14.relative(projectRoot2, absolute).startsWith("..")) {
16348
16189
  files.push({ path: sourcePath, status: "escaped", ...empty });
16349
16190
  continue;
16350
16191
  }
16351
16192
  let buffer;
16352
16193
  try {
16353
- const stat = fs13.statSync(absolute);
16194
+ const stat = fs12.statSync(absolute);
16354
16195
  if (!stat.isFile()) {
16355
16196
  files.push({ path: sourcePath, status: "missing", ...empty });
16356
16197
  continue;
16357
16198
  }
16358
- buffer = fs13.readFileSync(absolute);
16199
+ buffer = fs12.readFileSync(absolute);
16359
16200
  } catch {
16360
16201
  files.push({ path: sourcePath, status: "missing", ...empty });
16361
16202
  continue;
@@ -16365,7 +16206,7 @@ function buildCodeModel(implementations, types, projectRoot2, sourceRoots = [],
16365
16206
  continue;
16366
16207
  }
16367
16208
  const text2 = buffer.toString("utf8");
16368
- const language = EXTENSION_LANGUAGE[path15.extname(sourcePath).toLowerCase()];
16209
+ const language = EXTENSION_LANGUAGE[path14.extname(sourcePath).toLowerCase()];
16369
16210
  let analyzed;
16370
16211
  if (language === "typescript" || language === "javascript") {
16371
16212
  const ts = resolveTypeScript(projectRoot2);
@@ -16409,7 +16250,7 @@ function readTestFiles(testRoots, projectRoot2) {
16409
16250
  for (const key of walkDeclaredRoots(testRoots, [], projectRoot2)) {
16410
16251
  let text2;
16411
16252
  try {
16412
- const buffer = fs13.readFileSync(path15.resolve(projectRoot2, key));
16253
+ const buffer = fs12.readFileSync(path14.resolve(projectRoot2, key));
16413
16254
  if (looksBinary(buffer)) continue;
16414
16255
  text2 = buffer.toString("utf8");
16415
16256
  } catch {
@@ -16446,12 +16287,12 @@ function findTestsReferencing(methods, projectRoot2, testRoots = []) {
16446
16287
  }
16447
16288
  return found;
16448
16289
  }
16449
- var fs13, path15, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, NAMED_IMPORT_BINDINGS_RE, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
16290
+ var fs12, path14, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, NAMED_IMPORT_BINDINGS_RE, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
16450
16291
  var init_source_analysis = __esm({
16451
16292
  "src/core/source-analysis.ts"() {
16452
16293
  "use strict";
16453
- fs13 = __toESM(require("fs"));
16454
- path15 = __toESM(require("path"));
16294
+ fs12 = __toESM(require("fs"));
16295
+ path14 = __toESM(require("path"));
16455
16296
  import_module2 = require("module");
16456
16297
  init_models();
16457
16298
  EXTENSION_LANGUAGE = {
@@ -17221,28 +17062,28 @@ var init_gate_identity = __esm({
17221
17062
 
17222
17063
  // src/core/variants.ts
17223
17064
  function globalVariantsDir() {
17224
- return process.env.WAIRON_VARIANTS_DIR ?? path16.join(os5.homedir(), ".wairon", "variants");
17065
+ return process.env.WAIRON_VARIANTS_DIR ?? path15.join(os5.homedir(), ".wairon", "variants");
17225
17066
  }
17226
17067
  function builtinVariantsDir() {
17227
17068
  const candidates = [
17228
- path16.resolve(__dirname, "..", "templates", "variants"),
17069
+ path15.resolve(__dirname, "..", "templates", "variants"),
17229
17070
  // src/core & dist/cli
17230
- path16.resolve(__dirname, "templates", "variants")
17071
+ path15.resolve(__dirname, "templates", "variants")
17231
17072
  // dist (library entry)
17232
17073
  ];
17233
- return candidates.find((dir) => fs14.existsSync(dir)) ?? candidates[0];
17074
+ return candidates.find((dir) => fs13.existsSync(dir)) ?? candidates[0];
17234
17075
  }
17235
17076
  function projectVariantsDir() {
17236
- return path16.join(getProjectRoot(), ".wai", "variants");
17077
+ return path15.join(getProjectRoot(), ".wai", "variants");
17237
17078
  }
17238
17079
  function variantFiles(dir) {
17239
17080
  let entries;
17240
17081
  try {
17241
- entries = fs14.readdirSync(dir, { withFileTypes: true });
17082
+ entries = fs13.readdirSync(dir, { withFileTypes: true });
17242
17083
  } catch {
17243
17084
  return [];
17244
17085
  }
17245
- return entries.filter((e) => e.isFile() && /\.ya?ml$/i.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => path16.join(dir, e.name));
17086
+ return entries.filter((e) => e.isFile() && /\.ya?ml$/i.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => path15.join(dir, e.name));
17246
17087
  }
17247
17088
  function parseVariants(raw) {
17248
17089
  if (raw == null) return [];
@@ -17351,13 +17192,13 @@ function rebasedText(content, fromBase, toBase, rebasedContent) {
17351
17192
  }
17352
17193
  return serializeYaml(rebasedContent);
17353
17194
  }
17354
- var fs14, os5, path16, import_zod10, VariantDefSchema;
17195
+ var fs13, os5, path15, import_zod10, VariantDefSchema;
17355
17196
  var init_variants = __esm({
17356
17197
  "src/core/variants.ts"() {
17357
17198
  "use strict";
17358
- fs14 = __toESM(require("fs"));
17199
+ fs13 = __toESM(require("fs"));
17359
17200
  os5 = __toESM(require("os"));
17360
- path16 = __toESM(require("path"));
17201
+ path15 = __toESM(require("path"));
17361
17202
  import_zod10 = require("zod");
17362
17203
  init_yaml();
17363
17204
  init_fs();
@@ -17743,7 +17584,7 @@ var init_openapi = __esm({
17743
17584
 
17744
17585
  // src/core/surfaces.ts
17745
17586
  function surfacesDir(rootDir) {
17746
- return path17.join(rootDir, ".wai", SURFACES_DIRNAME);
17587
+ return path16.join(rootDir, ".wai", SURFACES_DIRNAME);
17747
17588
  }
17748
17589
  function audienceRank(audience) {
17749
17590
  const idx = SURFACE_AUDIENCES.indexOf(audience ?? "instance");
@@ -17901,12 +17742,12 @@ function projectSubsystemSurface(subsystemId) {
17901
17742
  }
17902
17743
  function listSnapshots(rootDir = getProjectRoot()) {
17903
17744
  const dir = surfacesDir(rootDir);
17904
- if (!fs15.existsSync(dir)) return [];
17745
+ if (!fs14.existsSync(dir)) return [];
17905
17746
  const out = [];
17906
- for (const file of fs15.readdirSync(dir)) {
17747
+ for (const file of fs14.readdirSync(dir)) {
17907
17748
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
17908
17749
  try {
17909
- out.push(SurfaceSnapshotSchema.parse(readYamlFile(path17.join(dir, file))));
17750
+ out.push(SurfaceSnapshotSchema.parse(readYamlFile(path16.join(dir, file))));
17910
17751
  } catch {
17911
17752
  }
17912
17753
  }
@@ -17917,10 +17758,10 @@ function snapshotFilename(projectName) {
17917
17758
  }
17918
17759
  function writeSnapshotIfChanged(snapshot, rootDir) {
17919
17760
  const dir = surfacesDir(rootDir);
17920
- fs15.mkdirSync(dir, { recursive: true });
17921
- const p = path17.join(dir, snapshotFilename(snapshot.projectName));
17761
+ fs14.mkdirSync(dir, { recursive: true });
17762
+ const p = path16.join(dir, snapshotFilename(snapshot.projectName));
17922
17763
  const next = SurfaceSnapshotSchema.parse(snapshot);
17923
- if (fs15.existsSync(p)) {
17764
+ if (fs14.existsSync(p)) {
17924
17765
  try {
17925
17766
  const existing = SurfaceSnapshotSchema.parse(readYamlFile(p));
17926
17767
  if (surfaceContentKey(existing) === surfaceContentKey(next)) {
@@ -17939,11 +17780,11 @@ function loadSurfaceSnapshots() {
17939
17780
  return listSnapshots();
17940
17781
  }
17941
17782
  function listMountSnapshots(mounts) {
17942
- const bound2 = path17.resolve(getProjectRoot());
17783
+ const bound2 = path16.resolve(getProjectRoot());
17943
17784
  const out = [];
17944
17785
  for (const namespace of mounts) {
17945
17786
  const dir = resolveSubprojectForNamespace(namespace);
17946
- if (!dir || path17.resolve(dir) === bound2) continue;
17787
+ if (!dir || path16.resolve(dir) === bound2) continue;
17947
17788
  const snapshots = listSnapshots(dir);
17948
17789
  if (snapshots.length > 0) out.push({ namespace, snapshots });
17949
17790
  }
@@ -17961,24 +17802,24 @@ function selectPortalSpec(renderedSet, portalId) {
17961
17802
  return [hit];
17962
17803
  }
17963
17804
  function perPortalPath(resolvedOut, portalId) {
17964
- const ext = path17.extname(resolvedOut);
17805
+ const ext = path16.extname(resolvedOut);
17965
17806
  const stem2 = ext ? resolvedOut.slice(0, -ext.length) : resolvedOut;
17966
17807
  return `${stem2}.${safeFilenamePart(portalId)}${ext}`;
17967
17808
  }
17968
17809
  function writeSurfaceFile(outPath, snapshot, renderedSet) {
17969
- const resolved = path17.resolve(outPath);
17970
- fs15.mkdirSync(path17.dirname(resolved), { recursive: true });
17810
+ const resolved = path16.resolve(outPath);
17811
+ fs14.mkdirSync(path16.dirname(resolved), { recursive: true });
17971
17812
  if (!renderedSet || renderedSet.length === 0) {
17972
17813
  writeYamlFile(resolved, snapshot);
17973
17814
  return [resolved];
17974
17815
  }
17975
17816
  if (renderedSet.length === 1) {
17976
- fs15.writeFileSync(resolved, renderedSet[0].document);
17817
+ fs14.writeFileSync(resolved, renderedSet[0].document);
17977
17818
  return [resolved];
17978
17819
  }
17979
17820
  return renderedSet.map((spec) => {
17980
17821
  const target = perPortalPath(resolved, spec.portalId);
17981
- fs15.writeFileSync(target, spec.document);
17822
+ fs14.writeFileSync(target, spec.document);
17982
17823
  return target;
17983
17824
  });
17984
17825
  }
@@ -18000,14 +17841,14 @@ function exportSurface(maxAudience, format, outPath, portalId) {
18000
17841
  return exportResult(snapshot, renderedSet, writtenPaths);
18001
17842
  }
18002
17843
  function importSurface(sourcePath, origin) {
18003
- const resolved = path17.resolve(sourcePath);
18004
- if (!fs15.existsSync(resolved)) {
17844
+ const resolved = path16.resolve(sourcePath);
17845
+ if (!fs14.existsSync(resolved)) {
18005
17846
  throw new Error(`Surface document not found: ${resolved}`);
18006
17847
  }
18007
- const body = fs15.readFileSync(resolved, "utf8");
17848
+ const body = fs14.readFileSync(resolved, "utf8");
18008
17849
  let snapshot;
18009
17850
  if (isOpenApiDocument(body)) {
18010
- const projectName = path17.basename(resolved).replace(/\.(json|ya?ml)$/i, "");
17851
+ const projectName = path16.basename(resolved).replace(/\.(json|ya?ml)$/i, "");
18011
17852
  snapshot = fromOpenApi(body, projectName);
18012
17853
  snapshot = { ...snapshot, origin };
18013
17854
  } else {
@@ -18070,12 +17911,12 @@ function surfaceContentKey(snapshot) {
18070
17911
  const { stateId, generatedAt, origin, ...content } = snapshot;
18071
17912
  return JSON.stringify(content);
18072
17913
  }
18073
- var fs15, path17, SURFACES_DIRNAME, CROSS_BOUNDARY_TARGETS;
17914
+ var fs14, path16, SURFACES_DIRNAME, CROSS_BOUNDARY_TARGETS;
18074
17915
  var init_surfaces = __esm({
18075
17916
  "src/core/surfaces.ts"() {
18076
17917
  "use strict";
18077
- fs15 = __toESM(require("fs"));
18078
- path17 = __toESM(require("path"));
17918
+ fs14 = __toESM(require("fs"));
17919
+ path16 = __toESM(require("path"));
18079
17920
  init_fs();
18080
17921
  init_yaml();
18081
17922
  init_filenames();
@@ -18381,13 +18222,13 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
18381
18222
  function resolveThroughParent(boundRoot, treatAllAsComplete, childRules) {
18382
18223
  const reach = getRequestParentReach();
18383
18224
  if (reach && !reach.parentReach) return null;
18384
- const ceiling = reach?.topRoot ? path18.resolve(reach.topRoot) : void 0;
18225
+ const ceiling = reach?.topRoot ? path17.resolve(reach.topRoot) : void 0;
18385
18226
  const chain = [];
18386
- let top = path18.resolve(boundRoot);
18227
+ let top = path17.resolve(boundRoot);
18387
18228
  while (top !== ceiling) {
18388
18229
  const hop = findChainingParent(top, ceiling);
18389
18230
  if (!hop) break;
18390
- const next = path18.resolve(hop.parentRoot);
18231
+ const next = path17.resolve(hop.parentRoot);
18391
18232
  if (ceiling && !isWithinOrEqual(ceiling, next)) break;
18392
18233
  chain.unshift(hop.subsystemId);
18393
18234
  top = next;
@@ -18434,8 +18275,8 @@ function isIdChar(ch) {
18434
18275
  return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "_" || ch === "-" || ch === "." || ch === ":";
18435
18276
  }
18436
18277
  function isWithinOrEqual(dir, target) {
18437
- const rel2 = path18.relative(dir, target);
18438
- return rel2 === "" || !rel2.startsWith("..") && !path18.isAbsolute(rel2);
18278
+ const rel2 = path17.relative(dir, target);
18279
+ return rel2 === "" || !rel2.startsWith("..") && !path17.isAbsolute(rel2);
18439
18280
  }
18440
18281
  function dedupeIssues(list2) {
18441
18282
  const seen = /* @__PURE__ */ new Set();
@@ -18456,7 +18297,7 @@ function settledStatusBearing(loaded) {
18456
18297
  if (!settled || settled.size === 0) return [];
18457
18298
  const root = getProjectRoot();
18458
18299
  const index = scanAllSpecs();
18459
- const rel2 = (abs) => path18.relative(root, abs).split(path18.sep).join("/");
18300
+ const rel2 = (abs) => path17.relative(root, abs).split(path17.sep).join("/");
18460
18301
  const out = [];
18461
18302
  const take = (specs, paths) => {
18462
18303
  for (const spec of specs) {
@@ -18486,7 +18327,7 @@ function computeGateStateId() {
18486
18327
  registerBuiltinRules();
18487
18328
  return computeGateIdentity(content, extensions, ruleSequence(), inputs, gate);
18488
18329
  }
18489
- var path18, RESOLUTION_FAILURE_CODES, SEVERITY_RANK;
18330
+ var path17, RESOLUTION_FAILURE_CODES, SEVERITY_RANK;
18490
18331
  var init_validation = __esm({
18491
18332
  "src/core/validation.ts"() {
18492
18333
  "use strict";
@@ -18502,7 +18343,7 @@ var init_validation = __esm({
18502
18343
  init_source_analysis();
18503
18344
  init_specs2();
18504
18345
  init_fs();
18505
- path18 = __toESM(require("path"));
18346
+ path17 = __toESM(require("path"));
18506
18347
  init_core();
18507
18348
  RESOLUTION_FAILURE_CODES = /* @__PURE__ */ new Set([
18508
18349
  "UNDEFINED_TYPE_REFERENCE",
@@ -19222,42 +19063,42 @@ function rebaseReference(ref, mount, direction, isMoved = () => false) {
19222
19063
  return relativizeId(travels ? qualifyId(left, to, NO_ROOT_SUBSYSTEMS) : target, to);
19223
19064
  }
19224
19065
  function isWithin(dir, file) {
19225
- const d = path19.resolve(dir);
19226
- const f = path19.resolve(file);
19227
- return f === d || f.startsWith(d + path19.sep);
19066
+ const d = path18.resolve(dir);
19067
+ const f = path18.resolve(file);
19068
+ return f === d || f.startsWith(d + path18.sep);
19228
19069
  }
19229
19070
  function canonicalPath(target) {
19230
- let existing = path19.resolve(target);
19071
+ let existing = path18.resolve(target);
19231
19072
  const rest = [];
19232
19073
  for (; ; ) {
19233
19074
  try {
19234
- fs16.lstatSync(existing);
19075
+ fs15.lstatSync(existing);
19235
19076
  break;
19236
19077
  } catch {
19237
- const up = path19.dirname(existing);
19238
- if (up === existing) return path19.resolve(target);
19239
- rest.unshift(path19.basename(existing));
19078
+ const up = path18.dirname(existing);
19079
+ if (up === existing) return path18.resolve(target);
19080
+ rest.unshift(path18.basename(existing));
19240
19081
  existing = up;
19241
19082
  }
19242
19083
  }
19243
19084
  try {
19244
- return path19.join(fs16.realpathSync.native(existing), ...rest);
19085
+ return path18.join(fs15.realpathSync.native(existing), ...rest);
19245
19086
  } catch {
19246
19087
  return null;
19247
19088
  }
19248
19089
  }
19249
19090
  function chainDirKey(dir) {
19250
- return canonicalPath(dir) ?? path19.resolve(dir);
19091
+ return canonicalPath(dir) ?? path18.resolve(dir);
19251
19092
  }
19252
19093
  function projectPathEscapesRoot(projectRoot2, projectPath, resolvedChildDir) {
19253
- if (path19.isAbsolute(projectPath) || !isWithin(projectRoot2, resolvedChildDir)) return true;
19094
+ if (path18.isAbsolute(projectPath) || !isWithin(projectRoot2, resolvedChildDir)) return true;
19254
19095
  const root = canonicalPath(projectRoot2);
19255
19096
  const child = canonicalPath(resolvedChildDir);
19256
19097
  return root === null || child === null || !isWithin(root, child);
19257
19098
  }
19258
19099
  function assertContainedProjectPath(projectRoot2, projectPath) {
19259
- const root = path19.resolve(projectRoot2);
19260
- const resolved = path19.resolve(root, projectPath);
19100
+ const root = path18.resolve(projectRoot2);
19101
+ const resolved = path18.resolve(root, projectPath);
19261
19102
  if (projectPathEscapesRoot(root, projectPath, resolved)) {
19262
19103
  throw new Error(
19263
19104
  `projectPath "${projectPath}" must resolve within the project root "${root}", but resolves to "${resolved}"; absolute, ../-escaping and link-escaping paths are rejected so a chained subproject is always contained by its parent.`
@@ -19268,12 +19109,12 @@ function assertContainedProjectPath(projectRoot2, projectPath) {
19268
19109
  function findChainingParent(childRoot, ceiling) {
19269
19110
  let childResolved;
19270
19111
  try {
19271
- childResolved = path19.resolve(childRoot);
19112
+ childResolved = path18.resolve(childRoot);
19272
19113
  } catch {
19273
19114
  return null;
19274
19115
  }
19275
- const bound2 = ceiling ? path19.resolve(ceiling) : void 0;
19276
- let dir = path19.dirname(childResolved);
19116
+ const bound2 = ceiling ? path18.resolve(ceiling) : void 0;
19117
+ let dir = path18.dirname(childResolved);
19277
19118
  for (let hops = 0; hops < 32; hops++) {
19278
19119
  if (bound2 && !isWithin(bound2, dir)) break;
19279
19120
  const specsDir = aiPathsAt(dir).specsDir();
@@ -19289,7 +19130,7 @@ function findChainingParent(childRoot, ceiling) {
19289
19130
  const projectPath = raw.projectPath;
19290
19131
  if (typeof projectPath === "string" && projectPath.trim() !== "") {
19291
19132
  try {
19292
- const mountDir = path19.resolve(dir, projectPath);
19133
+ const mountDir = path18.resolve(dir, projectPath);
19293
19134
  if (mountDir === childResolved && !projectPathEscapesRoot(dir, projectPath, mountDir)) {
19294
19135
  const id = raw.id;
19295
19136
  return { parentRoot: dir, subsystemId: typeof id === "string" ? id : "?" };
@@ -19300,14 +19141,14 @@ function findChainingParent(childRoot, ceiling) {
19300
19141
  }
19301
19142
  }
19302
19143
  }
19303
- const up = path19.dirname(dir);
19144
+ const up = path18.dirname(dir);
19304
19145
  if (up === dir) break;
19305
19146
  dir = up;
19306
19147
  }
19307
19148
  return null;
19308
19149
  }
19309
19150
  function inspectChainedRoots(rootDir = getProjectRoot()) {
19310
- const root = path19.resolve(rootDir);
19151
+ const root = path18.resolve(rootDir);
19311
19152
  const inspection = { roots: [], skipped: [] };
19312
19153
  const listed = /* @__PURE__ */ new Set();
19313
19154
  const walk2 = (projectDir, prefix, ancestors, depth) => {
@@ -19326,14 +19167,14 @@ function inspectChainedRoots(rootDir = getProjectRoot()) {
19326
19167
  const localId = typeof id === "string" ? id : "?";
19327
19168
  let childDir;
19328
19169
  try {
19329
- childDir = path19.resolve(projectDir, projectPath);
19170
+ childDir = path18.resolve(projectDir, projectPath);
19330
19171
  } catch {
19331
19172
  continue;
19332
19173
  }
19333
19174
  let reason;
19334
19175
  if (projectPathEscapesRoot(projectDir, projectPath, childDir)) reason = "escapes";
19335
19176
  else if (ancestors.has(chainDirKey(childDir))) reason = "cyclic";
19336
- else if (!fs16.existsSync(childDir)) reason = "missing";
19177
+ else if (!fs15.existsSync(childDir)) reason = "missing";
19337
19178
  else if (depth >= 32) reason = "too-deep";
19338
19179
  if (reason) {
19339
19180
  const mount = prefix ? qualifyDeclaredId(localId, prefix, true) : localId;
@@ -19343,7 +19184,7 @@ function inspectChainedRoots(rootDir = getProjectRoot()) {
19343
19184
  const key = chainDirKey(childDir);
19344
19185
  if (listed.has(key)) continue;
19345
19186
  listed.add(key);
19346
- inspection.roots.push(path19.relative(root, childDir).split(path19.sep).join("/"));
19187
+ inspection.roots.push(path18.relative(root, childDir).split(path18.sep).join("/"));
19347
19188
  walk2(childDir, prefix ? `${prefix}::${localId}` : localId, /* @__PURE__ */ new Set([...ancestors, key]), depth + 1);
19348
19189
  }
19349
19190
  };
@@ -19429,9 +19270,9 @@ function stripNamespaceFromImplementation(spec, prefix) {
19429
19270
  }
19430
19271
  function childRelativeFilePaths(spec, authoringRoot, childRoot) {
19431
19272
  const reexpress = (p) => {
19432
- if (path19.isAbsolute(p)) return p;
19433
- const rel2 = path19.relative(childRoot, path19.resolve(authoringRoot, p));
19434
- const inside = rel2 !== "" && rel2 !== ".." && !rel2.startsWith(`..${path19.sep}`) && !path19.isAbsolute(rel2);
19273
+ if (path18.isAbsolute(p)) return p;
19274
+ const rel2 = path18.relative(childRoot, path18.resolve(authoringRoot, p));
19275
+ const inside = rel2 !== "" && rel2 !== ".." && !rel2.startsWith(`..${path18.sep}`) && !path18.isAbsolute(rel2);
19435
19276
  return inside ? rel2.replace(/\\/g, "/") : p;
19436
19277
  };
19437
19278
  return {
@@ -19466,10 +19307,10 @@ function findOwner(id, components) {
19466
19307
  }) ?? null;
19467
19308
  }
19468
19309
  function moveComponentFolder(fromDir, toDir) {
19469
- if (path19.normalize(fromDir) === path19.normalize(toDir)) return false;
19470
- if (!fs16.existsSync(fromDir) || fs16.existsSync(toDir)) return false;
19471
- ensureDir(path19.dirname(toDir));
19472
- fs16.renameSync(fromDir, toDir);
19310
+ if (path18.normalize(fromDir) === path18.normalize(toDir)) return false;
19311
+ if (!fs15.existsSync(fromDir) || fs15.existsSync(toDir)) return false;
19312
+ ensureDir(path18.dirname(toDir));
19313
+ fs15.renameSync(fromDir, toDir);
19473
19314
  return true;
19474
19315
  }
19475
19316
  function formatZodIssues(error) {
@@ -19485,13 +19326,13 @@ function parseOrThrow(schema, value, kind, id) {
19485
19326
  function computeSpecTreeSignature(dirs) {
19486
19327
  const parts = [];
19487
19328
  for (const dir of dirs) {
19488
- if (!fs16.existsSync(dir)) {
19329
+ if (!fs15.existsSync(dir)) {
19489
19330
  parts.push(`${dir}:missing`);
19490
19331
  continue;
19491
19332
  }
19492
19333
  for (const f of listSpecFiles(dir)) {
19493
19334
  try {
19494
- const st = fs16.statSync(f);
19335
+ const st = fs15.statSync(f);
19495
19336
  parts.push(`${f}:${st.mtimeMs}:${st.size}`);
19496
19337
  } catch {
19497
19338
  parts.push(`${f}:gone`);
@@ -19884,7 +19725,7 @@ function refusedMoveSummary(from, to, methods, refusals, alternatives) {
19884
19725
  return `Refused: moving ${namedMethods(methods)} from "${from}" to "${to}" is refused by ${refusals.join(", ")}. Nothing was written. ` + (cheapest ? `The cheapest legal home is "${cheapest.component}" (${cheapest.newDependencies.length} new dependenc${cheapest.newDependencies.length === 1 ? "y" : "ies"}, ${cheapest.dependencyCount} of ${cheapest.dependencyLimit}).` : `No component of this subsystem can take them as they stand \u2014 ${alternatives.length} were weighed.`);
19885
19726
  }
19886
19727
  function workspaceFor(rootDir) {
19887
- const key = path19.resolve(rootDir);
19728
+ const key = path18.resolve(rootDir);
19888
19729
  let ws = workspaces.get(key);
19889
19730
  if (!ws) {
19890
19731
  ws = new SpecWorkspace(key);
@@ -20014,13 +19855,13 @@ function snapshotInputKey(snapshot) {
20014
19855
  return canonicalize(content);
20015
19856
  }
20016
19857
  function consumedSurfaceInputsAt(rootDir) {
20017
- const dir = path19.join(rootDir, ".wai", "surfaces");
19858
+ const dir = path18.join(rootDir, ".wai", "surfaces");
20018
19859
  if (!pathExists(dir)) return [];
20019
19860
  const keys = [];
20020
- for (const file of fs16.readdirSync(dir)) {
19861
+ for (const file of fs15.readdirSync(dir)) {
20021
19862
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
20022
19863
  try {
20023
- keys.push(snapshotInputKey(SurfaceSnapshotSchema.parse(readYamlFile(path19.join(dir, file)))));
19864
+ keys.push(snapshotInputKey(SurfaceSnapshotSchema.parse(readYamlFile(path18.join(dir, file)))));
20024
19865
  } catch {
20025
19866
  }
20026
19867
  }
@@ -20028,7 +19869,7 @@ function consumedSurfaceInputsAt(rootDir) {
20028
19869
  }
20029
19870
  function consumedContractInputs() {
20030
19871
  const root = getProjectRoot();
20031
- const roots = [root, ...listChainedRoots(root).map((rel2) => path19.join(root, rel2))];
19872
+ const roots = [root, ...listChainedRoots(root).map((rel2) => path18.join(root, rel2))];
20032
19873
  return roots.flatMap(consumedSurfaceInputsAt);
20033
19874
  }
20034
19875
  function readLockState(current2) {
@@ -20064,7 +19905,7 @@ function markSelectionsBundled(bundled) {
20064
19905
  projectConfigRepository.markSelectionsBundled(bundled);
20065
19906
  }
20066
19907
  function computeStateIdAt(root) {
20067
- const resolved = path19.resolve(root);
19908
+ const resolved = path18.resolve(root);
20068
19909
  return runWithProjectRoot(resolved, () => {
20069
19910
  workspaceFor(resolved).invalidate();
20070
19911
  const system = loadSystemSpec();
@@ -20102,7 +19943,7 @@ function snapshotSpecFiles() {
20102
19943
  }
20103
19944
  function restoreSpecFiles(snapshot) {
20104
19945
  for (const [file, content] of snapshot) {
20105
- fs16.writeFileSync(file, content);
19946
+ fs15.writeFileSync(file, content);
20106
19947
  }
20107
19948
  }
20108
19949
  function findLegacySpecFiles() {
@@ -20114,12 +19955,12 @@ function updateSpec(kind, id, delta, hooks, dryRun) {
20114
19955
  function moveMethods(from, to, methods, hooks, dryRun) {
20115
19956
  return current().moveMethods(from, to, methods, hooks, dryRun);
20116
19957
  }
20117
- var fs16, path19, NO_ROOT_SUBSYSTEMS, SIGNATURE_TTL_MS, COMPONENT_AUTH_SOURCE, DELTA_VERBS, JUMP_LABEL_TWINS, LABEL_TWIN_CONTAINERS, SpecWorkspace, DEFAULT_DEPENDENCY_LIMIT, PROSE_FIELDS, workspaces;
19958
+ var fs15, path18, NO_ROOT_SUBSYSTEMS, SIGNATURE_TTL_MS, COMPONENT_AUTH_SOURCE, DELTA_VERBS, JUMP_LABEL_TWINS, LABEL_TWIN_CONTAINERS, SpecWorkspace, DEFAULT_DEPENDENCY_LIMIT, PROSE_FIELDS, workspaces;
20118
19959
  var init_specs2 = __esm({
20119
19960
  "src/core/specs.ts"() {
20120
19961
  "use strict";
20121
- fs16 = __toESM(require("fs"));
20122
- path19 = __toESM(require("path"));
19962
+ fs15 = __toESM(require("fs"));
19963
+ path18 = __toESM(require("path"));
20123
19964
  init_paths();
20124
19965
  init_project_config();
20125
19966
  init_fs();
@@ -20154,7 +19995,7 @@ var init_specs2 = __esm({
20154
19995
  this.rootSubsystems = /* @__PURE__ */ new Set();
20155
19996
  this.scanVisitedSpecDirs = [];
20156
19997
  this.loaderIssues = [];
20157
- this.rootDir = path19.resolve(rootDir);
19998
+ this.rootDir = path18.resolve(rootDir);
20158
19999
  this.paths = aiPathsAt(this.rootDir);
20159
20000
  }
20160
20001
  invalidate() {
@@ -20197,8 +20038,8 @@ var init_specs2 = __esm({
20197
20038
  * qualifies the spec itself — so scoping a validate to a mount keeps it.
20198
20039
  */
20199
20040
  loaderIssueSpecId(file, rawId, namespacePrefix) {
20200
- const stem2 = path19.basename(file, ".yaml");
20201
- const local = rawId ?? (stem2.startsWith(".") ? path19.basename(path19.dirname(file)) : stem2);
20041
+ const stem2 = path18.basename(file, ".yaml");
20042
+ const local = rawId ?? (stem2.startsWith(".") ? path18.basename(path18.dirname(file)) : stem2);
20202
20043
  return namespacePrefix ? qualifyId(local, namespacePrefix, this.rootSubsystems) : local;
20203
20044
  }
20204
20045
  scanSpecsForProject(projectDir, namespacePrefix, visitedDirs, maxDepth, currentDepth) {
@@ -20208,10 +20049,10 @@ var init_specs2 = __esm({
20208
20049
  this.scanVisitedSpecDirs.push(specsDir);
20209
20050
  if (!pathExists(specsDir)) return index;
20210
20051
  const files = listSpecFiles(specsDir);
20211
- const systemYaml = path19.normalize(projectPaths.specsSystem());
20052
+ const systemYaml = path18.normalize(projectPaths.specsSystem());
20212
20053
  const localSubprojects = [];
20213
20054
  for (const file of files) {
20214
- const normFile = path19.normalize(file);
20055
+ const normFile = path18.normalize(file);
20215
20056
  if (normFile === systemYaml) continue;
20216
20057
  let detectedType = "spec";
20217
20058
  let rawId;
@@ -20255,7 +20096,7 @@ var init_specs2 = __esm({
20255
20096
  } else if ("contract" in raw) {
20256
20097
  detectedType = "implementation";
20257
20098
  const parsed = ImplementationSpecSchema.parse(raw);
20258
- const normalizeSourcePath = (p) => path19.relative(projectDir, path19.resolve(projectDir, p)).replace(/\\/g, "/");
20099
+ const normalizeSourcePath = (p) => path18.relative(projectDir, path18.resolve(projectDir, p)).replace(/\\/g, "/");
20259
20100
  if (parsed.sourcePath) parsed.sourcePath = normalizeSourcePath(parsed.sourcePath);
20260
20101
  for (const method2 of parsed.methods) {
20261
20102
  if (method2.sourcePath) method2.sourcePath = normalizeSourcePath(method2.sourcePath);
@@ -20381,7 +20222,7 @@ var init_specs2 = __esm({
20381
20222
  }
20382
20223
  if (currentDepth < maxDepth) {
20383
20224
  for (const subproj of localSubprojects) {
20384
- const childDir = path19.resolve(projectDir, subproj.projectPath);
20225
+ const childDir = path18.resolve(projectDir, subproj.projectPath);
20385
20226
  const mountId = namespacePrefix ? qualifyDeclaredId(subproj.subsystemId, namespacePrefix, true) : subproj.subsystemId;
20386
20227
  if (projectPathEscapesRoot(projectDir, subproj.projectPath, childDir)) {
20387
20228
  this.loaderIssues.push({
@@ -20401,7 +20242,7 @@ var init_specs2 = __esm({
20401
20242
  });
20402
20243
  continue;
20403
20244
  }
20404
- if (!fs16.existsSync(childDir)) {
20245
+ if (!fs15.existsSync(childDir)) {
20405
20246
  this.loaderIssues.push({
20406
20247
  severity: "error",
20407
20248
  code: "SUBPROJECT_NOT_FOUND",
@@ -20444,7 +20285,7 @@ var init_specs2 = __esm({
20444
20285
  const index = this.scanAll();
20445
20286
  const sub = index.subsystems.find((s) => s.id === currentPrefix);
20446
20287
  if (sub && sub.projectPath) {
20447
- const nextDir = path19.resolve(currentDir, sub.projectPath);
20288
+ const nextDir = path18.resolve(currentDir, sub.projectPath);
20448
20289
  if (projectPathEscapesRoot(currentDir, sub.projectPath, nextDir)) {
20449
20290
  this.loaderIssues.push({
20450
20291
  severity: "error",
@@ -20496,9 +20337,9 @@ var init_specs2 = __esm({
20496
20337
  return index.paths.subsystem[matches[0]];
20497
20338
  }
20498
20339
  if (pathExists(this.paths.specsSubsystemsDir()) && listFiles(this.paths.specsSubsystemsDir(), ".yaml").length > 0) {
20499
- return path19.join(this.paths.specsSubsystemsDir(), `${id}.yaml`);
20340
+ return path18.join(this.paths.specsSubsystemsDir(), `${id}.yaml`);
20500
20341
  }
20501
- return path19.join(this.paths.specsDir(), id, ".index.yaml");
20342
+ return path18.join(this.paths.specsDir(), id, ".index.yaml");
20502
20343
  }
20503
20344
  getComponentPath(id, subsystemId) {
20504
20345
  const index = this.scanAll();
@@ -20518,21 +20359,21 @@ var init_specs2 = __esm({
20518
20359
  if (owner) {
20519
20360
  const ownerPath = index.paths.component[owner.id];
20520
20361
  if (ownerPath && ownerPath.endsWith(".index.yaml")) {
20521
- return path19.join(path19.dirname(ownerPath), id, ".index.yaml");
20362
+ return path18.join(path18.dirname(ownerPath), id, ".index.yaml");
20522
20363
  }
20523
20364
  }
20524
20365
  if (subsystemId) {
20525
20366
  const subPath = this.getSubsystemPath(subsystemId);
20526
- const subDir = path19.dirname(subPath);
20367
+ const subDir = path18.dirname(subPath);
20527
20368
  if (subPath.endsWith(".index.yaml")) {
20528
- return path19.join(subDir, id, ".index.yaml");
20369
+ return path18.join(subDir, id, ".index.yaml");
20529
20370
  }
20530
20371
  }
20531
20372
  if (pathExists(this.paths.specsComponentsDir()) && listFiles(this.paths.specsComponentsDir(), ".yaml").length > 0) {
20532
- return path19.join(this.paths.specsComponentsDir(), `${id}.yaml`);
20373
+ return path18.join(this.paths.specsComponentsDir(), `${id}.yaml`);
20533
20374
  }
20534
20375
  const targetSubsystem = subsystemId || "default";
20535
- return path19.join(this.paths.specsDir(), targetSubsystem, id, ".index.yaml");
20376
+ return path18.join(this.paths.specsDir(), targetSubsystem, id, ".index.yaml");
20536
20377
  }
20537
20378
  getInterfacePath(id, componentId) {
20538
20379
  const index = this.scanAll();
@@ -20558,16 +20399,16 @@ var init_specs2 = __esm({
20558
20399
  }
20559
20400
  if (componentId) {
20560
20401
  const compPath = this.getComponentPath(componentId);
20561
- const compDir = path19.dirname(compPath);
20402
+ const compDir = path18.dirname(compPath);
20562
20403
  if (compPath.endsWith(".index.yaml")) {
20563
- return path19.join(compDir, ".interface.yaml");
20404
+ return path18.join(compDir, ".interface.yaml");
20564
20405
  }
20565
20406
  }
20566
20407
  if (pathExists(this.paths.specsInterfacesDir()) && listFiles(this.paths.specsInterfacesDir(), ".yaml").length > 0) {
20567
- return path19.join(this.paths.specsInterfacesDir(), `${id}.yaml`);
20408
+ return path18.join(this.paths.specsInterfacesDir(), `${id}.yaml`);
20568
20409
  }
20569
20410
  const targetComponent = componentId || "default";
20570
- return path19.join(this.paths.specsDir(), "default", targetComponent, ".interface.yaml");
20411
+ return path18.join(this.paths.specsDir(), "default", targetComponent, ".interface.yaml");
20571
20412
  }
20572
20413
  getImplementationPath(id, contractId) {
20573
20414
  const index = this.scanAll();
@@ -20593,16 +20434,16 @@ var init_specs2 = __esm({
20593
20434
  }
20594
20435
  if (contractId) {
20595
20436
  const intfPath = this.getInterfacePath(contractId);
20596
- const intfDir = path19.dirname(intfPath);
20437
+ const intfDir = path18.dirname(intfPath);
20597
20438
  if (intfPath.endsWith(".interface.yaml")) {
20598
- return path19.join(intfDir, ".implementation.yaml");
20439
+ return path18.join(intfDir, ".implementation.yaml");
20599
20440
  }
20600
20441
  }
20601
20442
  if (pathExists(this.paths.specsImplementationsDir()) && listFiles(this.paths.specsImplementationsDir(), ".yaml").length > 0) {
20602
- return path19.join(this.paths.specsImplementationsDir(), `${id}.yaml`);
20443
+ return path18.join(this.paths.specsImplementationsDir(), `${id}.yaml`);
20603
20444
  }
20604
20445
  const targetContract = contractId ? contractId.replace(/^i/, "") : "default";
20605
- return path19.join(this.paths.specsDir(), "default", targetContract, ".implementation.yaml");
20446
+ return path18.join(this.paths.specsDir(), "default", targetContract, ".implementation.yaml");
20606
20447
  }
20607
20448
  getTypePath(id, subsystemId, group) {
20608
20449
  const index = this.scanAll();
@@ -20633,7 +20474,7 @@ var init_specs2 = __esm({
20633
20474
  const groupPath = index.paths.group[targetGroup] || index.paths.group[plainGroup];
20634
20475
  if (groupPath) {
20635
20476
  const localId2 = id.split("::").pop();
20636
- return path19.join(path19.dirname(groupPath), `${localId2}.yaml`);
20477
+ return path18.join(path18.dirname(groupPath), `${localId2}.yaml`);
20637
20478
  }
20638
20479
  }
20639
20480
  let localId = id;
@@ -20646,12 +20487,12 @@ var init_specs2 = __esm({
20646
20487
  }
20647
20488
  if (subsystemId) {
20648
20489
  const subPath = this.getSubsystemPath(subsystemId);
20649
- const subDir = path19.dirname(subPath);
20490
+ const subDir = path18.dirname(subPath);
20650
20491
  if (subPath.endsWith(".index.yaml")) {
20651
- return path19.join(subDir, "types", `${localId}.yaml`);
20492
+ return path18.join(subDir, "types", `${localId}.yaml`);
20652
20493
  }
20653
20494
  }
20654
- return path19.join(this.paths.specsTypesDir(), `${localId}.yaml`);
20495
+ return path18.join(this.paths.specsTypesDir(), `${localId}.yaml`);
20655
20496
  }
20656
20497
  getGroupPath(id, subsystemId) {
20657
20498
  const index = this.scanAll();
@@ -20685,12 +20526,12 @@ var init_specs2 = __esm({
20685
20526
  }
20686
20527
  if (subsystemId) {
20687
20528
  const subPath = this.getSubsystemPath(subsystemId);
20688
- const subDir = path19.dirname(subPath);
20529
+ const subDir = path18.dirname(subPath);
20689
20530
  if (subPath.endsWith(".index.yaml")) {
20690
- return path19.join(subDir, "types", localId, ".index.yaml");
20531
+ return path18.join(subDir, "types", localId, ".index.yaml");
20691
20532
  }
20692
20533
  }
20693
- return path19.join(this.paths.specsTypesDir(), localId, ".index.yaml");
20534
+ return path18.join(this.paths.specsTypesDir(), localId, ".index.yaml");
20694
20535
  }
20695
20536
  // -------------------------------------------------------------------------
20696
20537
  // Level 0: System
@@ -20713,7 +20554,7 @@ var init_specs2 = __esm({
20713
20554
  }
20714
20555
  saveSystemSpec(spec) {
20715
20556
  const p = this.paths.specsSystem();
20716
- ensureDir(path19.dirname(p));
20557
+ ensureDir(path18.dirname(p));
20717
20558
  writeSpecFile(p, parseOrThrow(SystemSpecSchema, spec, "system", spec.name));
20718
20559
  invalidateSpecCache();
20719
20560
  }
@@ -20835,7 +20676,7 @@ var init_specs2 = __esm({
20835
20676
  }
20836
20677
  saveSubsystemSpec(spec, opts) {
20837
20678
  const p = this.getSubsystemPath(spec.id);
20838
- ensureDir(path19.dirname(p));
20679
+ ensureDir(path18.dirname(p));
20839
20680
  const { prefix } = splitNamespace(spec.id);
20840
20681
  if (!prefix && spec.projectPath && spec.projectPath.trim() !== "") {
20841
20682
  assertContainedProjectPath(this.rootDir, spec.projectPath);
@@ -20902,7 +20743,7 @@ var init_specs2 = __esm({
20902
20743
  saveComponentSpec(spec, opts) {
20903
20744
  const notices = [];
20904
20745
  const p = this.getComponentPath(spec.id, spec.subsystem);
20905
- ensureDir(path19.dirname(p));
20746
+ ensureDir(path18.dirname(p));
20906
20747
  const specToWrite = this.prepareComponentForWrite(spec);
20907
20748
  const existing = this.loadComponentSpec(spec.id);
20908
20749
  if (existing) {
@@ -20919,7 +20760,7 @@ var init_specs2 = __esm({
20919
20760
  const subsystemChanged = existing && existing.subsystem !== spec.subsystem && splitNamespace(existing.subsystem).localId !== splitNamespace(spec.subsystem).localId;
20920
20761
  if (subsystemChanged && !p.endsWith(".index.yaml")) {
20921
20762
  notices.push(
20922
- `component "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 the flat layout re-saves in place and never relocates the file. The subsystem field is now "${spec.subsystem}" (was "${existing.subsystem}").`
20763
+ `component "${spec.id}" already exists at ${path18.relative(this.rootDir, p)} \u2014 the flat layout re-saves in place and never relocates the file. The subsystem field is now "${spec.subsystem}" (was "${existing.subsystem}").`
20923
20764
  );
20924
20765
  }
20925
20766
  specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
@@ -20943,12 +20784,12 @@ var init_specs2 = __esm({
20943
20784
  if (owner) {
20944
20785
  const ownerPath = index.paths.component[owner.id];
20945
20786
  if (ownerPath && ownerPath.endsWith(".index.yaml")) {
20946
- const ownerSubDir = path19.dirname(this.getSubsystemPath(owner.subsystem));
20947
- return path19.join(ownerSubDir, owner.id, comp.id);
20787
+ const ownerSubDir = path18.dirname(this.getSubsystemPath(owner.subsystem));
20788
+ return path18.join(ownerSubDir, owner.id, comp.id);
20948
20789
  }
20949
20790
  }
20950
- const subDir = path19.dirname(this.getSubsystemPath(comp.subsystem));
20951
- return path19.join(subDir, comp.id);
20791
+ const subDir = path18.dirname(this.getSubsystemPath(comp.subsystem));
20792
+ return path18.join(subDir, comp.id);
20952
20793
  }
20953
20794
  /**
20954
20795
  * Move component folders so the physical tree mirrors ownership: each owned
@@ -20964,7 +20805,7 @@ var init_specs2 = __esm({
20964
20805
  if (!currentPath) continue;
20965
20806
  const desiredDir = this.desiredComponentDir(comp, index);
20966
20807
  if (!desiredDir) continue;
20967
- if (moveComponentFolder(path19.dirname(currentPath), desiredDir)) moved.push(comp.id);
20808
+ if (moveComponentFolder(path18.dirname(currentPath), desiredDir)) moved.push(comp.id);
20968
20809
  }
20969
20810
  if (moved.length) invalidateSpecCache();
20970
20811
  return moved;
@@ -21020,7 +20861,7 @@ var init_specs2 = __esm({
21020
20861
  if (!occupantId) return;
21021
20862
  if (occupantId === id || splitNamespace(occupantId).localId === splitNamespace(id).localId) return;
21022
20863
  throw new Error(
21023
- `Cannot write ${kind} "${id}": ${parentLabel} is already ${kind === "interface" ? "served by" : "implemented by"} "${occupantId}" at ${path19.relative(this.rootDir, p)}, and both ids resolve to that one file. Re-author "${occupantId}" instead, or delete it first if you meant to replace it.`
20864
+ `Cannot write ${kind} "${id}": ${parentLabel} is already ${kind === "interface" ? "served by" : "implemented by"} "${occupantId}" at ${path18.relative(this.rootDir, p)}, and both ids resolve to that one file. Re-author "${occupantId}" instead, or delete it first if you meant to replace it.`
21024
20865
  );
21025
20866
  }
21026
20867
  /** Returns non-fatal placement notices (see saveTypeSpec) — empty when there is nothing to clarify. */
@@ -21028,12 +20869,12 @@ var init_specs2 = __esm({
21028
20869
  const notices = [];
21029
20870
  const p = this.getInterfacePath(spec.id, spec.component);
21030
20871
  this.assertPathHoldsNoOtherSpec(p, spec.id, "interface", `component "${spec.component}"`);
21031
- ensureDir(path19.dirname(p));
20872
+ ensureDir(path18.dirname(p));
21032
20873
  const specToWrite = this.prepareInterfaceForWrite(spec);
21033
20874
  const existing = this.loadInterfaceSpec(spec.id);
21034
20875
  if (existing && existing.component !== spec.component && splitNamespace(existing.component).localId !== splitNamespace(spec.component).localId) {
21035
20876
  notices.push(
21036
- `interface "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates the component binding field in place (now "${spec.component}", was "${existing.component}") and never moves the file.`
20877
+ `interface "${spec.id}" already exists at ${path18.relative(this.rootDir, p)} \u2014 re-saving updates the component binding field in place (now "${spec.component}", was "${existing.component}") and never moves the file.`
21037
20878
  );
21038
20879
  }
21039
20880
  if (existing) {
@@ -21090,12 +20931,12 @@ var init_specs2 = __esm({
21090
20931
  const notices = [];
21091
20932
  const p = this.getImplementationPath(spec.id, spec.contract);
21092
20933
  this.assertPathHoldsNoOtherSpec(p, spec.id, "implementation", `contract "${spec.contract}"`);
21093
- ensureDir(path19.dirname(p));
20934
+ ensureDir(path18.dirname(p));
21094
20935
  const specToWrite = this.prepareImplementationForWrite(spec);
21095
20936
  const existing = this.loadImplementationSpec(spec.id);
21096
20937
  if (existing && existing.contract !== spec.contract && splitNamespace(existing.contract).localId !== splitNamespace(spec.contract).localId) {
21097
20938
  notices.push(
21098
- `implementation "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates the contract binding field in place (now "${spec.contract}", was "${existing.contract}") and never moves the file.`
20939
+ `implementation "${spec.id}" already exists at ${path18.relative(this.rootDir, p)} \u2014 re-saving updates the contract binding field in place (now "${spec.contract}", was "${existing.contract}") and never moves the file.`
21099
20940
  );
21100
20941
  }
21101
20942
  if (existing) {
@@ -21136,11 +20977,11 @@ var init_specs2 = __esm({
21136
20977
  const existing = this.loadTypeSpec(spec.id);
21137
20978
  const group = spec.group || (existing ? existing.group : void 0);
21138
20979
  const p = this.getTypePath(spec.id, spec.subsystem, group);
21139
- ensureDir(path19.dirname(p));
20980
+ ensureDir(path18.dirname(p));
21140
20981
  const subsystemChanged = existing && (existing.subsystem ?? "") !== (spec.subsystem ?? "") && splitNamespace(existing.subsystem ?? "").localId !== splitNamespace(spec.subsystem ?? "").localId;
21141
20982
  if (subsystemChanged) {
21142
20983
  notices.push(
21143
- `type "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates fields in place and never relocates the file. The subsystem field is now "${spec.subsystem ?? "(none)"}" (was "${existing.subsystem ?? "(none)"}").`
20984
+ `type "${spec.id}" already exists at ${path18.relative(this.rootDir, p)} \u2014 re-saving updates fields in place and never relocates the file. The subsystem field is now "${spec.subsystem ?? "(none)"}" (was "${existing.subsystem ?? "(none)"}").`
21144
20985
  );
21145
20986
  }
21146
20987
  if (spec.subsystem && (!existing || subsystemChanged) && !this.getSubsystemPath(spec.subsystem).endsWith(".index.yaml")) {
@@ -21178,7 +21019,7 @@ var init_specs2 = __esm({
21178
21019
  }
21179
21020
  saveGroupSpec(spec, opts) {
21180
21021
  const p = this.getGroupPath(spec.id);
21181
- ensureDir(path19.dirname(p));
21022
+ ensureDir(path18.dirname(p));
21182
21023
  const specToWrite = this.prepareGroupForWrite(spec);
21183
21024
  const existing = this.loadGroupSpec(spec.id);
21184
21025
  if (existing) {
@@ -21256,7 +21097,7 @@ var init_specs2 = __esm({
21256
21097
  };
21257
21098
  const out = /* @__PURE__ */ new Set();
21258
21099
  const add = (p) => {
21259
- if (p) out.add(path19.resolve(p));
21100
+ if (p) out.add(path18.resolve(p));
21260
21101
  };
21261
21102
  for (const s of this.loadSubsystemSpecs()) {
21262
21103
  if (!scopeSubsystem || s.id === scopeSubsystem || s.id.startsWith(`${scopeSubsystem}::`)) {
@@ -21327,16 +21168,16 @@ var init_specs2 = __esm({
21327
21168
  const files = /* @__PURE__ */ new Set();
21328
21169
  const sysPath = this.paths.specsSystem();
21329
21170
  if (pathExists(sysPath)) {
21330
- files.add(path19.resolve(sysPath));
21171
+ files.add(path18.resolve(sysPath));
21331
21172
  }
21332
21173
  for (const group of Object.values(index.paths)) {
21333
21174
  for (const file of Object.values(group)) {
21334
- files.add(path19.resolve(file));
21175
+ files.add(path18.resolve(file));
21335
21176
  }
21336
21177
  }
21337
21178
  for (const file of files) {
21338
- if (fs16.existsSync(file)) {
21339
- snapshot.set(file, fs16.readFileSync(file, "utf8"));
21179
+ if (fs15.existsSync(file)) {
21180
+ snapshot.set(file, fs15.readFileSync(file, "utf8"));
21340
21181
  }
21341
21182
  }
21342
21183
  return snapshot;
@@ -21350,20 +21191,20 @@ var init_specs2 = __esm({
21350
21191
  const files = listSpecFiles(specsDir);
21351
21192
  const legacy = [];
21352
21193
  for (const f of files) {
21353
- const base = path19.basename(f);
21354
- const dir = path19.dirname(f);
21194
+ const base = path18.basename(f);
21195
+ const dir = path18.dirname(f);
21355
21196
  if (base === "system.yaml") {
21356
- legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
21197
+ legacy.push({ path: f, expected: path18.join(dir, ".index.yaml") });
21357
21198
  } else if (base === "subsystem.yaml") {
21358
- legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
21199
+ legacy.push({ path: f, expected: path18.join(dir, ".index.yaml") });
21359
21200
  } else if (base === "component.yaml") {
21360
- legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
21201
+ legacy.push({ path: f, expected: path18.join(dir, ".index.yaml") });
21361
21202
  } else if (base === "group.yaml") {
21362
- legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
21203
+ legacy.push({ path: f, expected: path18.join(dir, ".index.yaml") });
21363
21204
  } else if (base === "interface.yaml") {
21364
- legacy.push({ path: f, expected: path19.join(dir, ".interface.yaml") });
21205
+ legacy.push({ path: f, expected: path18.join(dir, ".interface.yaml") });
21365
21206
  } else if (base === "implementation.yaml") {
21366
- legacy.push({ path: f, expected: path19.join(dir, ".implementation.yaml") });
21207
+ legacy.push({ path: f, expected: path18.join(dir, ".implementation.yaml") });
21367
21208
  }
21368
21209
  }
21369
21210
  return legacy;
@@ -22366,18 +22207,76 @@ var init_specs2 = __esm({
22366
22207
  }
22367
22208
  });
22368
22209
 
22210
+ // src/config/loader.ts
22211
+ function loadTopologyConfig() {
22212
+ assertProjectInitialized();
22213
+ if (!pathExists(AI_PATHS.topologyConfig())) return createEmptyTopologyConfig();
22214
+ const raw = readYamlFile(AI_PATHS.topologyConfig());
22215
+ if (!raw) return createEmptyTopologyConfig();
22216
+ return TopologyConfigSchema.parse(raw);
22217
+ }
22218
+ function saveTopologyConfig(config) {
22219
+ writeYamlFile(AI_PATHS.topologyConfig(), config);
22220
+ }
22221
+ var init_loader = __esm({
22222
+ "src/config/loader.ts"() {
22223
+ "use strict";
22224
+ init_fs();
22225
+ init_yaml();
22226
+ init_models();
22227
+ init_paths();
22228
+ }
22229
+ });
22230
+
22369
22231
  // src/core/domains.ts
22370
- var domains_exports = {};
22371
- __export(domains_exports, {
22372
- addFreeStandingDomain: () => addFreeStandingDomain,
22373
- deriveSubsystemDomains: () => deriveSubsystemDomains,
22374
- findDomain: () => findDomain,
22375
- listFreeStandingDomains: () => listFreeStandingDomains,
22376
- removeFreeStandingDomain: () => removeFreeStandingDomain,
22377
- resolveDomains: () => resolveDomains
22232
+ function addFreeStandingDomain(domain) {
22233
+ const config = loadTopologyConfig();
22234
+ if (config.domains.some((d) => d.id === domain.id)) {
22235
+ throw new WaironError(`A free-standing domain "${domain.id}" already exists in .wai/topology.yaml.`);
22236
+ }
22237
+ config.domains.push(domain);
22238
+ saveTopologyConfig(config);
22239
+ }
22240
+ function removeFreeStandingDomain(id) {
22241
+ const config = loadTopologyConfig();
22242
+ const idx = config.domains.findIndex((d) => d.id === id);
22243
+ if (idx === -1) {
22244
+ throw new WaironError(
22245
+ `"${id}" is not a free-standing domain. Subsystem-backed domains are derived from the spec tree and cannot be removed here.`
22246
+ );
22247
+ }
22248
+ config.domains.splice(idx, 1);
22249
+ saveTopologyConfig(config);
22250
+ }
22251
+ var init_domains = __esm({
22252
+ "src/core/domains.ts"() {
22253
+ "use strict";
22254
+ init_loader();
22255
+ init_errors();
22256
+ }
22257
+ });
22258
+
22259
+ // src/core/topology.ts
22260
+ function addFreeStanding(domain) {
22261
+ addFreeStandingDomain(domain);
22262
+ }
22263
+ function removeFreeStanding(id) {
22264
+ removeFreeStandingDomain(id);
22265
+ }
22266
+ function loadConfig() {
22267
+ return loadTopologyConfig();
22268
+ }
22269
+ var init_topology = __esm({
22270
+ "src/core/topology.ts"() {
22271
+ "use strict";
22272
+ init_domains();
22273
+ init_loader();
22274
+ }
22378
22275
  });
22276
+
22277
+ // src/core/domain_projector.ts
22379
22278
  function rel(p) {
22380
- return path20.relative(process.cwd(), p).replace(/\\/g, "/");
22279
+ return path19.relative(process.cwd(), p).replace(/\\/g, "/");
22381
22280
  }
22382
22281
  function deriveSubsystemDomains() {
22383
22282
  const subsystems = loadSubsystemSpecs();
@@ -22396,45 +22295,210 @@ function deriveSubsystemDomains() {
22396
22295
  };
22397
22296
  });
22398
22297
  }
22399
- function listFreeStandingDomains() {
22400
- return loadTopologyConfig().domains;
22401
- }
22402
22298
  function resolveDomains() {
22403
- return [...deriveSubsystemDomains(), ...listFreeStandingDomains()];
22404
- }
22405
- function findDomain(id) {
22406
- return resolveDomains().find((d) => d.id === id);
22299
+ const derived = deriveSubsystemDomains();
22300
+ const registered = loadConfig().domains;
22301
+ return [...derived, ...registered];
22407
22302
  }
22408
- function addFreeStandingDomain(domain) {
22409
- const config = loadTopologyConfig();
22410
- if (config.domains.some((d) => d.id === domain.id)) {
22411
- throw new WaironError(`A free-standing domain "${domain.id}" already exists in .wai/topology.yaml.`);
22303
+ var path19;
22304
+ var init_domain_projector = __esm({
22305
+ "src/core/domain_projector.ts"() {
22306
+ "use strict";
22307
+ path19 = __toESM(require("path"));
22308
+ init_specs2();
22309
+ init_topology();
22412
22310
  }
22413
- if (deriveSubsystemDomains().some((d) => d.id === domain.id)) {
22414
- throw new WaironError(`Domain id "${domain.id}" collides with a subsystem-derived domain.`);
22311
+ });
22312
+
22313
+ // src/core/domain_curator.ts
22314
+ function registerDomain(domain) {
22315
+ const taken = resolveDomains().find((d) => d.id === domain.id);
22316
+ if (taken) {
22317
+ throw new WaironError(refusal(taken));
22415
22318
  }
22416
- config.domains.push(domain);
22417
- saveTopologyConfig(config);
22319
+ addFreeStanding(domain);
22418
22320
  }
22419
- function removeFreeStandingDomain(id) {
22420
- const config = loadTopologyConfig();
22421
- const idx = config.domains.findIndex((d) => d.id === id);
22422
- if (idx === -1) {
22423
- throw new WaironError(
22424
- `"${id}" is not a free-standing domain. Subsystem-backed domains are derived from the spec tree and cannot be removed here.`
22321
+ function refusal(taken) {
22322
+ return taken.boundTo ? `Domain id "${taken.id}" collides with a subsystem-derived domain.` : `A free-standing domain "${taken.id}" already exists in .wai/topology.yaml.`;
22323
+ }
22324
+ function unregisterDomain(id) {
22325
+ removeFreeStanding(id);
22326
+ }
22327
+ var init_domain_curator = __esm({
22328
+ "src/core/domain_curator.ts"() {
22329
+ "use strict";
22330
+ init_domain_projector();
22331
+ init_topology();
22332
+ init_errors();
22333
+ }
22334
+ });
22335
+
22336
+ // src/core/detection.ts
22337
+ function detectDomainCandidates(projectRoot2, alreadyTrackedPaths = /* @__PURE__ */ new Set(), alreadyTrackedIds = /* @__PURE__ */ new Set()) {
22338
+ const candidates = /* @__PURE__ */ new Map();
22339
+ for (const c of detectGitSubmodules(projectRoot2)) {
22340
+ candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
22341
+ }
22342
+ for (const c of detectNestedGitRepos(projectRoot2)) {
22343
+ if (!candidates.has(c.path)) {
22344
+ candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
22345
+ }
22346
+ }
22347
+ const gitPaths = new Set(
22348
+ Array.from(candidates.values()).filter((c) => c.type === "git-submodule" || c.type === "git-repo").map((c) => c.path)
22349
+ );
22350
+ for (const c of detectPackageRoots(projectRoot2)) {
22351
+ if (candidates.has(c.path)) continue;
22352
+ const insideGit = Array.from(gitPaths).some(
22353
+ (gp) => c.path === gp || c.path.startsWith(gp + "/")
22425
22354
  );
22355
+ if (insideGit) continue;
22356
+ candidates.set(c.path, { ...c, alreadyTracked: alreadyTrackedPaths.has(c.path) });
22426
22357
  }
22427
- config.domains.splice(idx, 1);
22428
- saveTopologyConfig(config);
22358
+ const sorted = Array.from(candidates.values()).sort((a, b) => a.path.localeCompare(b.path));
22359
+ return deduplicateIds(sorted, alreadyTrackedIds);
22429
22360
  }
22430
- var path20;
22431
- var init_domains = __esm({
22432
- "src/core/domains.ts"() {
22361
+ function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
22362
+ const idCount = /* @__PURE__ */ new Map();
22363
+ for (const id of existingIds) {
22364
+ idCount.set(id, (idCount.get(id) ?? 0) + 1);
22365
+ }
22366
+ for (const c of candidates) {
22367
+ idCount.set(c.suggestedId, (idCount.get(c.suggestedId) ?? 0) + 1);
22368
+ }
22369
+ return candidates.map((c) => {
22370
+ if ((idCount.get(c.suggestedId) ?? 0) <= 1) return c;
22371
+ const parts = c.path.split("/");
22372
+ const qualifiedId2 = parts.length >= 2 ? pathToId(`${parts[parts.length - 2]}-${parts[parts.length - 1]}`) : c.suggestedId;
22373
+ return { ...c, suggestedId: qualifiedId2 };
22374
+ });
22375
+ }
22376
+ function parseGitmodules(filePath) {
22377
+ const content = fs16.readFileSync(filePath, "utf-8");
22378
+ const entries = [];
22379
+ let current2 = {};
22380
+ for (const line2 of content.split("\n")) {
22381
+ const trimmed = line2.trim();
22382
+ const headerMatch = trimmed.match(/^\[submodule "(.+)"\]$/);
22383
+ if (headerMatch) {
22384
+ if (current2.path) entries.push(current2);
22385
+ current2 = { name: headerMatch[1] };
22386
+ continue;
22387
+ }
22388
+ const keyVal = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
22389
+ if (keyVal) {
22390
+ const [, key, value] = keyVal;
22391
+ if (key === "path") current2.path = value.trim();
22392
+ if (key === "url") current2.url = value.trim();
22393
+ }
22394
+ }
22395
+ if (current2.path) entries.push(current2);
22396
+ return entries;
22397
+ }
22398
+ function detectGitSubmodules(projectRoot2) {
22399
+ const gitmodulesPath = path20.join(projectRoot2, ".gitmodules");
22400
+ if (!fs16.existsSync(gitmodulesPath)) return [];
22401
+ return parseGitmodules(gitmodulesPath).map((entry) => ({
22402
+ suggestedId: pathToId(entry.path),
22403
+ suggestedName: pathToName(entry.path),
22404
+ path: normalizePath(entry.path),
22405
+ type: "git-submodule",
22406
+ alreadyTracked: false
22407
+ }));
22408
+ }
22409
+ function detectNestedGitRepos(projectRoot2) {
22410
+ const results = [];
22411
+ walkForGit(projectRoot2, projectRoot2, 0, results);
22412
+ return results;
22413
+ }
22414
+ function walkForGit(projectRoot2, currentDir, depth, results) {
22415
+ if (depth > MAX_SCAN_DEPTH) return;
22416
+ let entries;
22417
+ try {
22418
+ entries = fs16.readdirSync(currentDir, { withFileTypes: true });
22419
+ } catch {
22420
+ return;
22421
+ }
22422
+ for (const entry of entries) {
22423
+ if (!entry.isDirectory()) continue;
22424
+ if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
22425
+ const fullPath = path20.join(currentDir, entry.name);
22426
+ const relPath = normalizePath(path20.relative(projectRoot2, fullPath));
22427
+ if (relPath === "" || relPath === ".") continue;
22428
+ const gitPath = path20.join(fullPath, ".git");
22429
+ if (fs16.existsSync(gitPath)) {
22430
+ results.push({
22431
+ suggestedId: pathToId(relPath),
22432
+ suggestedName: pathToName(relPath),
22433
+ path: relPath,
22434
+ type: "git-repo",
22435
+ alreadyTracked: false
22436
+ });
22437
+ continue;
22438
+ }
22439
+ walkForGit(projectRoot2, fullPath, depth + 1, results);
22440
+ }
22441
+ }
22442
+ function detectPackageRoots(projectRoot2) {
22443
+ const results = [];
22444
+ walkForPackages(projectRoot2, projectRoot2, 0, results);
22445
+ return results;
22446
+ }
22447
+ function walkForPackages(projectRoot2, currentDir, depth, results) {
22448
+ if (depth > MAX_SCAN_DEPTH) return;
22449
+ let entries;
22450
+ try {
22451
+ entries = fs16.readdirSync(currentDir, { withFileTypes: true });
22452
+ } catch {
22453
+ return;
22454
+ }
22455
+ for (const entry of entries) {
22456
+ if (!entry.isDirectory()) continue;
22457
+ if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
22458
+ const fullPath = path20.join(currentDir, entry.name);
22459
+ const relPath = normalizePath(path20.relative(projectRoot2, fullPath));
22460
+ if (relPath === "" || relPath === ".") continue;
22461
+ const hasMarker = PACKAGE_MARKERS.some((m) => fs16.existsSync(path20.join(fullPath, m)));
22462
+ if (hasMarker) {
22463
+ results.push({
22464
+ suggestedId: pathToId(relPath),
22465
+ suggestedName: pathToName(relPath),
22466
+ path: relPath,
22467
+ type: "package-root",
22468
+ alreadyTracked: false
22469
+ });
22470
+ }
22471
+ walkForPackages(projectRoot2, fullPath, depth + 1, results);
22472
+ }
22473
+ }
22474
+ function pathToId(relPath) {
22475
+ const basename15 = path20.basename(relPath);
22476
+ return basename15.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
22477
+ }
22478
+ function pathToName(relPath) {
22479
+ const id = pathToId(relPath);
22480
+ return id.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
22481
+ }
22482
+ function normalizePath(p) {
22483
+ return p.replace(/\\/g, "/");
22484
+ }
22485
+ var fs16, path20, PACKAGE_MARKERS, MAX_SCAN_DEPTH;
22486
+ var init_detection = __esm({
22487
+ "src/core/detection.ts"() {
22433
22488
  "use strict";
22489
+ fs16 = __toESM(require("fs"));
22434
22490
  path20 = __toESM(require("path"));
22435
- init_loader();
22436
- init_specs2();
22437
- init_errors();
22491
+ init_defaults();
22492
+ PACKAGE_MARKERS = [
22493
+ "package.json",
22494
+ "pyproject.toml",
22495
+ "Cargo.toml",
22496
+ "go.mod",
22497
+ "build.gradle",
22498
+ "build.gradle.kts",
22499
+ "pom.xml"
22500
+ ];
22501
+ MAX_SCAN_DEPTH = 5;
22438
22502
  }
22439
22503
  });
22440
22504
 
@@ -23218,21 +23282,6 @@ function resolveBudget(profile, config, agentId) {
23218
23282
  const override = config.overrides[agentId];
23219
23283
  return override ? { ...budget, ...override } : budget;
23220
23284
  }
23221
- function describeBudget(profile, budget) {
23222
- const lines = [
23223
- `- **Work shape**: ${profile.breadth} breadth, ${profile.reasoningDepth} reasoning${profile.writes ? "" : ", read-only"}${profile.delegates ? ", delegating" : ""}`,
23224
- `- **Why**: ${profile.rationale}`
23225
- ];
23226
- if (budget.modelTier) lines.push(`- **Capability tier**: ${budget.modelTier}`);
23227
- if (budget.effort) lines.push(`- **Effort**: ${budget.effort}`);
23228
- if (budget.maxTurns !== void 0) {
23229
- lines.push(`- **Turn ceiling**: ${budget.maxTurns} (a circuit breaker \u2014 hitting it should read as a scoping error, not a limit to work up to)`);
23230
- }
23231
- lines.push(`- **Tool grant**: ${budget.toolClass}`);
23232
- lines.push(`- **May delegate further**: ${budget.allowNestedDelegation ? "yes" : "no"}`);
23233
- lines.push(`- **MCP access**: ${budget.mcp}`);
23234
- return lines;
23235
- }
23236
23285
  var TIER_ORDER, TIER_STEPS;
23237
23286
  var init_budget_policy = __esm({
23238
23287
  "src/core/budget_policy.ts"() {
@@ -23373,7 +23422,7 @@ function inferSourcePathForComponent(comp, subsystems) {
23373
23422
  return null;
23374
23423
  }
23375
23424
  }
23376
- function summarize(text2, max = 140) {
23425
+ function summarize2(text2, max = 140) {
23377
23426
  const clean = text2.replace(/\s+/g, " ").trim();
23378
23427
  const period = clean.indexOf(". ");
23379
23428
  const firstSentence = period > 0 ? clean.slice(0, period + 1) : clean;
@@ -23400,7 +23449,7 @@ function resolveAgentTopology() {
23400
23449
  agents.push({
23401
23450
  id: "system-architect",
23402
23451
  name: `${system.name} Architect`,
23403
- description: `Global architect for ${system.name} \u2014 owns the spec tree and topology. ${summarize(system.vision)}`,
23452
+ description: `Global architect for ${system.name} \u2014 owns the spec tree and topology. ${summarize2(system.vision)}`,
23404
23453
  template: "architect",
23405
23454
  creationReason: "Automatically inferred from L0 system spec",
23406
23455
  ownedPaths: [".wai/specs/**"],
@@ -23480,7 +23529,7 @@ function resolveAgentTopology() {
23480
23529
  agents.push({
23481
23530
  id: `${sub.id}-owner`,
23482
23531
  name: `${sub.name} Owner`,
23483
- description: `Owns the ${sub.id} subsystem. ${summarize(sub.description)}`,
23532
+ description: `Owns the ${sub.id} subsystem. ${summarize2(sub.description)}`,
23484
23533
  template: "domain-owner",
23485
23534
  creationReason: `Automatically inferred from L1 subsystem spec: ${sub.id}`,
23486
23535
  domainRoot: sub.id,
@@ -23541,11 +23590,11 @@ function resolveAgentTopology() {
23541
23590
  }
23542
23591
  }
23543
23592
  const now = (/* @__PURE__ */ new Date()).toISOString();
23544
- for (const dom of loadTopologyConfig().domains) {
23593
+ for (const dom of loadConfig().domains) {
23545
23594
  agents.push({
23546
23595
  id: `${dom.id}-owner`,
23547
23596
  name: `${dom.name ?? dom.id} Owner`,
23548
- description: dom.description ? summarize(dom.description) : `Owner agent for the free-standing "${dom.id}" domain.`,
23597
+ description: dom.description ? summarize2(dom.description) : `Owner agent for the free-standing "${dom.id}" domain.`,
23549
23598
  template: "domain-owner",
23550
23599
  creationReason: "Inferred from a free-standing domain in .wai/topology.yaml",
23551
23600
  domainRoot: dom.id,
@@ -23622,7 +23671,7 @@ var init_agent_resolver = __esm({
23622
23671
  "use strict";
23623
23672
  path23 = __toESM(require("path"));
23624
23673
  fs18 = __toESM(require("fs"));
23625
- init_loader();
23674
+ init_topology();
23626
23675
  init_paths();
23627
23676
  init_registry();
23628
23677
  init_project_config();
@@ -24005,8 +24054,7 @@ function renderDomainsDoc() {
24005
24054
  return lines.join("\n") + "\n";
24006
24055
  }
24007
24056
  function renderWaironGuide() {
24008
- const { resolveDomains: resolveDomains2 } = (init_domains(), __toCommonJS(domains_exports));
24009
- const domains = resolveDomains2();
24057
+ const domains = resolveDomains();
24010
24058
  let projectName = "this project";
24011
24059
  try {
24012
24060
  projectName = projectConfigRepository.load()?.name ?? projectName;
@@ -24090,7 +24138,7 @@ var init_context = __esm({
24090
24138
  init_fs();
24091
24139
  init_ai_guide();
24092
24140
  init_stamp();
24093
- init_domains();
24141
+ init_domain_projector();
24094
24142
  init_project_config();
24095
24143
  CONTEXT_PATHS = {
24096
24144
  dir: () => contextDir(),
@@ -28175,6 +28223,15 @@ var init_approver = __esm({
28175
28223
  });
28176
28224
 
28177
28225
  // src/core/index.ts
28226
+ function resolveDomains2() {
28227
+ return resolveDomains();
28228
+ }
28229
+ function addDomain(domain) {
28230
+ registerDomain(domain);
28231
+ }
28232
+ function removeDomain(id) {
28233
+ unregisterDomain(id);
28234
+ }
28178
28235
  function loadProjectConfig() {
28179
28236
  return projectConfigRepository.load();
28180
28237
  }
@@ -28185,8 +28242,9 @@ var init_core = __esm({
28185
28242
  "src/core/index.ts"() {
28186
28243
  "use strict";
28187
28244
  init_project_config();
28245
+ init_domain_projector();
28246
+ init_domain_curator();
28188
28247
  init_detection();
28189
- init_domains();
28190
28248
  init_templates();
28191
28249
  init_validation();
28192
28250
  init_extensions();
@@ -28215,6 +28273,8 @@ var init_core = __esm({
28215
28273
  init_approver();
28216
28274
  init_specs2();
28217
28275
  init_project_config();
28276
+ init_execution_profile();
28277
+ init_budget_policy();
28218
28278
  }
28219
28279
  });
28220
28280
 
@@ -28916,7 +28976,7 @@ function renderAgentBriefMarkdown(brief) {
28916
28976
  "",
28917
28977
  "## Execution budget",
28918
28978
  "",
28919
- ...describeBudget(brief.profile, brief.budget),
28979
+ ...summarize(brief.budget, brief.profile),
28920
28980
  "",
28921
28981
  "Advisory \u2014 apply these when spawning. Map the capability tier onto your host tool's models; a tool that cannot express a field should ignore it rather than approximate it."
28922
28982
  );
@@ -29047,7 +29107,7 @@ function createMcpServer(options = {}) {
29047
29107
  },
29048
29108
  () => {
29049
29109
  try {
29050
- return json(resolveDomains());
29110
+ return json(resolveDomains2());
29051
29111
  } catch (e) {
29052
29112
  return errText(String(e));
29053
29113
  }
@@ -30299,12 +30359,12 @@ var init_server = __esm({
30299
30359
  init_paths();
30300
30360
  init_validation();
30301
30361
  init_provision();
30302
- init_domains();
30362
+ init_core();
30303
30363
  init_authoring();
30304
30364
  init_variants();
30305
30365
  init_agent_resolver();
30306
30366
  init_core();
30307
- init_budget_policy();
30367
+ init_execution();
30308
30368
  init_surfaces();
30309
30369
  TYPE_REF_GRAMMAR = (what) => `${what}: a primitive/builtin or a defined type id (qualified across subsystems, e.g. "billing.Invoice" or "billing::Invoice"). Generics, arrays and UNIONS are all read, at any depth: "Invoice | null" (the commonest shape there is), "Invoice | undefined", "Invoice | Receipt", "Promise<Invoice | null>", "Map<string, Invoice | null>", "Invoice[] | null", "(Invoice | null)[]". Every identifier the string names must resolve \u2014 a union of two defined types means BOTH must exist. A union of string literals ("read" | "write") names no type and resolves to nothing.`;
30310
30370
  staleServerOutput = {
@@ -31062,6 +31122,24 @@ function generateAll2(agents, config, options) {
31062
31122
  function resolveExpectedOutputPaths2(agents, config) {
31063
31123
  return resolveExpectedOutputPaths(agents, config);
31064
31124
  }
31125
+ function resolveDomains3() {
31126
+ return resolveDomains2();
31127
+ }
31128
+ function addDomain2(domain) {
31129
+ addDomain(domain);
31130
+ }
31131
+ function removeDomain2(id) {
31132
+ removeDomain(id);
31133
+ }
31134
+ function detectDomainCandidates2(projectRoot2, trackedPaths, trackedIds) {
31135
+ return detectDomainCandidates(projectRoot2, trackedPaths, trackedIds);
31136
+ }
31137
+ function deriveExecutionProfile2(agent) {
31138
+ return deriveExecutionProfile(agent);
31139
+ }
31140
+ function resolveBudget2(profile, config, agentId) {
31141
+ return resolveBudget(profile, config, agentId);
31142
+ }
31065
31143
  async function runSubsystemAdd(id, options = {}) {
31066
31144
  logger.header("wairon subsystem add");
31067
31145
  if (!projectConfigExists2()) {
@@ -32730,12 +32808,10 @@ async function filteredCheckbox(config) {
32730
32808
 
32731
32809
  // src/commands/domains.ts
32732
32810
  init_paths();
32733
- init_detection();
32734
- init_domains();
32735
32811
  init_domain();
32736
32812
  async function runDomainsList() {
32737
32813
  assertProjectInitialized();
32738
- const domains = resolveDomains();
32814
+ const domains = resolveDomains3();
32739
32815
  if (domains.length === 0) {
32740
32816
  logger.info("No domains yet.");
32741
32817
  logger.info("Define subsystems in the spec tree, or run `wairon domains scan --add` for free-standing domains.");
@@ -32760,10 +32836,10 @@ function printDomain(domain) {
32760
32836
  async function runDomainsScan(options = {}) {
32761
32837
  assertProjectInitialized();
32762
32838
  const projectRoot2 = fromProjectRoot();
32763
- const existing = resolveDomains();
32839
+ const existing = resolveDomains3();
32764
32840
  const trackedPaths = new Set(existing.map((d) => d.path).filter((p) => !!p));
32765
32841
  const trackedIds = new Set(existing.map((d) => d.id));
32766
- const candidates = detectDomainCandidates(projectRoot2, trackedPaths, trackedIds);
32842
+ const candidates = detectDomainCandidates2(projectRoot2, trackedPaths, trackedIds);
32767
32843
  const newCandidates = candidates.filter((c) => !c.alreadyTracked && !trackedIds.has(c.suggestedId));
32768
32844
  if (newCandidates.length === 0) {
32769
32845
  logger.success("No new domain candidates found.");
@@ -32804,7 +32880,7 @@ async function runDomainsScan(options = {}) {
32804
32880
  ownedPaths: [`${candidate.path}/**`]
32805
32881
  });
32806
32882
  try {
32807
- addFreeStandingDomain(domain);
32883
+ addDomain2(domain);
32808
32884
  logger.success(`Added free-standing domain: ${domain.id} (${candidate.path})`);
32809
32885
  added++;
32810
32886
  } catch (err) {
@@ -32854,13 +32930,13 @@ async function runDomainsAdd(options = {}) {
32854
32930
  path: domainPath.trim() || void 0,
32855
32931
  ownedPaths
32856
32932
  });
32857
- addFreeStandingDomain(domain);
32933
+ addDomain2(domain);
32858
32934
  logger.success(`Free-standing domain "${domain.id}" added to .wai/topology.yaml.`);
32859
32935
  logger.info("Run `wairon generate` to produce its owner agent.");
32860
32936
  }
32861
32937
  async function runDomainsRemove(id) {
32862
32938
  assertProjectInitialized();
32863
- const domain = findDomain(id);
32939
+ const domain = resolveDomains3().find((d) => d.id === id);
32864
32940
  if (!domain) {
32865
32941
  logger.error(`Domain "${id}" not found.`);
32866
32942
  process.exit(1);
@@ -32882,7 +32958,7 @@ async function runDomainsRemove(id) {
32882
32958
  logger.info("Cancelled.");
32883
32959
  return;
32884
32960
  }
32885
- removeFreeStandingDomain(id);
32961
+ removeDomain2(id);
32886
32962
  logger.success(`Domain "${id}" removed. Run \`wairon generate\` to refresh agent files.`);
32887
32963
  }
32888
32964
 
@@ -48320,15 +48396,12 @@ function report(outcome) {
48320
48396
  }
48321
48397
 
48322
48398
  // src/cli/index.ts
48323
- init_budget_policy();
48399
+ init_execution();
48324
48400
 
48325
48401
  // src/commands/execution.ts
48326
48402
  var import_chalk21 = __toESM(require("chalk"));
48327
48403
  init_logger();
48328
48404
  init_errors();
48329
- init_agent_resolver();
48330
- init_execution_profile();
48331
- init_budget_policy();
48332
48405
  init_execution();
48333
48406
  function assertInitialized() {
48334
48407
  if (!projectConfigExists2()) {
@@ -48356,7 +48429,7 @@ async function showExecution() {
48356
48429
  logger.info(`Enable with ${import_chalk21.default.bold("wairon execution set-tier default")}.`);
48357
48430
  return;
48358
48431
  }
48359
- const agents = resolveAgentTopology();
48432
+ const agents = resolveAgentTopology2();
48360
48433
  if (agents.length === 0) {
48361
48434
  logger.blank();
48362
48435
  logger.warn("No agents in the topology yet \u2014 nothing to budget.");
@@ -48364,8 +48437,8 @@ async function showExecution() {
48364
48437
  }
48365
48438
  logger.blank();
48366
48439
  const rows = agents.map((agent) => {
48367
- const profile = deriveExecutionProfile(agent);
48368
- const budget = resolveBudget(profile, config.execution, agent.id);
48440
+ const profile = deriveExecutionProfile2(agent);
48441
+ const budget = resolveBudget2(profile, config.execution, agent.id);
48369
48442
  return { agent, profile, budget };
48370
48443
  });
48371
48444
  const idWidth = Math.max(...rows.map((r) => r.agent.id.length));
@@ -48700,7 +48773,7 @@ async function runAgent(action, id) {
48700
48773
  if (brief.budget && brief.profile) {
48701
48774
  logger.blank();
48702
48775
  logger.info("Execution budget (advisory \u2014 apply when spawning):");
48703
- for (const line2 of describeBudget(brief.profile, brief.budget)) {
48776
+ for (const line2 of summarize(brief.budget, brief.profile)) {
48704
48777
  logger.info(` ${line2.replace(/^- \*\*(.+?)\*\*: /, "$1: ")}`);
48705
48778
  }
48706
48779
  }