@wairon/cli 5.1.1-dev.81 → 5.1.1-dev.82

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.81";
68
+ WAIRON_VERSION = "5.1.1-dev.82";
69
69
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
70
70
  SUPPORTED_ALIASES = ["wai"];
71
71
  SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
@@ -155,7 +155,7 @@ var init_logger = __esm({
155
155
  });
156
156
 
157
157
  // src/utils/errors.ts
158
- var WaironError, ProjectNotInitializedError, TemplateNotFoundError;
158
+ var WaironError, ProjectNotInitializedError, TemplateNotFoundError, DoctorOptionsError, ChainingMigrationRefusedError;
159
159
  var init_errors = __esm({
160
160
  "src/utils/errors.ts"() {
161
161
  "use strict";
@@ -179,6 +179,20 @@ var init_errors = __esm({
179
179
  this.name = "TemplateNotFoundError";
180
180
  }
181
181
  };
182
+ DoctorOptionsError = class extends WaironError {
183
+ constructor(detail) {
184
+ super(`wairon doctor: ${detail}`);
185
+ this.name = "DoctorOptionsError";
186
+ }
187
+ };
188
+ ChainingMigrationRefusedError = class extends WaironError {
189
+ constructor(reasons) {
190
+ super(`The chaining migration was not applied; nothing was written:
191
+ ${reasons.map((r) => ` - ${r}`).join("\n")}`);
192
+ this.reasons = reasons;
193
+ this.name = "ChainingMigrationRefusedError";
194
+ }
195
+ };
182
196
  }
183
197
  });
184
198
 
@@ -1122,7 +1136,12 @@ function projectConfigFsAdapterAt(rootDir) {
1122
1136
  return readYamlFile(file());
1123
1137
  },
1124
1138
  writeDocument(document) {
1125
- writeYamlFile(file(), document);
1139
+ const text2 = readFileOrNull(file());
1140
+ if (text2 === null) {
1141
+ writeYamlFile(file(), document);
1142
+ return;
1143
+ }
1144
+ writeFile(file(), editDocumentText(text2, document, root));
1126
1145
  },
1127
1146
  documentExists() {
1128
1147
  return fs4.existsSync(file());
@@ -1216,21 +1235,21 @@ function childrenOf(tokens, index) {
1216
1235
  }
1217
1236
  return out;
1218
1237
  }
1219
- function locateScalar(tokens, path76) {
1238
+ function locateScalar(tokens, path77) {
1220
1239
  let region = tokens;
1221
- for (let k = 0; k < path76.length; k += 1) {
1222
- const segment = path76[k];
1223
- if (region.length === 0) return `nothing is nested at ${path76.slice(0, k).join(".")}`;
1240
+ for (let k = 0; k < path77.length; k += 1) {
1241
+ const segment = path77[k];
1242
+ if (region.length === 0) return `nothing is nested at ${path77.slice(0, k).join(".")}`;
1224
1243
  const base = Math.min(...region.map((t) => t.col));
1225
- const last = k === path76.length - 1;
1244
+ const last = k === path77.length - 1;
1226
1245
  let at;
1227
1246
  if (typeof segment === "string") {
1228
1247
  at = region.findIndex((t) => t.kind === "key" && t.col === base && t.name === segment);
1229
- if (at < 0) return `no block key "${segment}" at ${path76.slice(0, k).join(".") || "the document root"}`;
1248
+ if (at < 0) return `no block key "${segment}" at ${path77.slice(0, k).join(".") || "the document root"}`;
1230
1249
  if (last) return region[at].valueStart !== void 0 ? region[at] : `"${segment}" holds no inline scalar`;
1231
1250
  } else {
1232
1251
  const items = region.map((t, i) => ({ t, i })).filter(({ t }) => t.kind === "item" && t.col === base);
1233
- if (segment >= items.length) return `no block sequence item ${segment} at ${path76.slice(0, k).join(".")}`;
1252
+ if (segment >= items.length) return `no block sequence item ${segment} at ${path77.slice(0, k).join(".")}`;
1234
1253
  at = items[segment].i;
1235
1254
  if (last) {
1236
1255
  const value = region[at + 1];
@@ -1278,10 +1297,10 @@ function writeScalar(value, style) {
1278
1297
  if (style === "plain" && /^[A-Za-z0-9_][A-Za-z0-9_./-]*$/.test(value)) return value;
1279
1298
  return `'${value.replace(/'/g, "''")}'`;
1280
1299
  }
1281
- function setAt(document, path76, value) {
1300
+ function setAt(document, path77, value) {
1282
1301
  let node = document;
1283
- for (const segment of path76.slice(0, -1)) node = node[segment];
1284
- node[path76[path76.length - 1]] = value;
1302
+ for (const segment of path77.slice(0, -1)) node = node[segment];
1303
+ node[path77[path77.length - 1]] = value;
1285
1304
  }
1286
1305
  function rewriteScalarsInText(text2, edits, root) {
1287
1306
  const refuse = (why) => {
@@ -1309,6 +1328,95 @@ function rewriteScalarsInText(text2, edits, root) {
1309
1328
  }
1310
1329
  return next;
1311
1330
  }
1331
+ function sameValue(a, b) {
1332
+ if (a instanceof Date || b instanceof Date) {
1333
+ return a instanceof Date && b instanceof Date && a.getTime() === b.getTime();
1334
+ }
1335
+ if (Array.isArray(a) || Array.isArray(b)) {
1336
+ return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((item, i) => sameValue(item, b[i]));
1337
+ }
1338
+ if (isPlainObject(a) || isPlainObject(b)) {
1339
+ if (!isPlainObject(a) || !isPlainObject(b)) return false;
1340
+ const keysA = Object.keys(a).filter((k) => a[k] !== void 0);
1341
+ const keysB = Object.keys(b).filter((k) => b[k] !== void 0);
1342
+ return keysA.length === keysB.length && keysA.every((k) => hasOwn(b, k) && sameValue(a[k], b[k]));
1343
+ }
1344
+ return a === b;
1345
+ }
1346
+ function lineStartOf(text2, pos) {
1347
+ return text2.lastIndexOf("\n", pos - 1) + 1;
1348
+ }
1349
+ function lineEndFrom(text2, pos) {
1350
+ if (pos === 0 || text2[pos - 1] === "\n") return pos;
1351
+ const newline = text2.indexOf("\n", pos);
1352
+ return newline < 0 ? text2.length : newline + 1;
1353
+ }
1354
+ function indentBlock(text2, col) {
1355
+ const pad = " ".repeat(col);
1356
+ const body = text2.endsWith("\n") ? text2 : `${text2}
1357
+ `;
1358
+ return body.split("\n").map((line2, i, all) => line2 === "" || i === all.length - 1 ? line2 : pad + line2).join("\n");
1359
+ }
1360
+ function freshBlock(value, col) {
1361
+ return indentBlock(YAML.stringify(value, FRESH_TEXT_OPTIONS), col);
1362
+ }
1363
+ function schemaFieldOrder(at) {
1364
+ const objectsOf = (schema) => {
1365
+ const shape = shapeOf(schema);
1366
+ if (shape instanceof import_zod4.z.ZodObject) return [shape];
1367
+ if (shape instanceof import_zod4.z.ZodUnion) return shape._def.options.flatMap(objectsOf);
1368
+ return [];
1369
+ };
1370
+ let current2 = ProjectConfigSchema;
1371
+ for (const segment of at) {
1372
+ if (current2 === null) return null;
1373
+ const shape = shapeOf(current2);
1374
+ if (typeof segment === "number") {
1375
+ current2 = shape instanceof import_zod4.z.ZodArray ? shape.element : null;
1376
+ } else if (shape instanceof import_zod4.z.ZodRecord) {
1377
+ current2 = shape._def.valueType;
1378
+ } else {
1379
+ current2 = objectsOf(current2).map((o) => o.shape[segment]).find((s) => s !== void 0) ?? null;
1380
+ }
1381
+ }
1382
+ if (current2 === null) return null;
1383
+ const fields = objectsOf(current2).flatMap((o) => Object.keys(o.shape));
1384
+ return fields.length > 0 ? [...new Set(fields)] : null;
1385
+ }
1386
+ function inlineScalar(value, style) {
1387
+ if (value !== null && typeof value === "object") return null;
1388
+ const quoting = style === "QUOTE_SINGLE" || style === "QUOTE_DOUBLE" ? style : "PLAIN";
1389
+ const text2 = YAML.stringify(value, { ...FRESH_TEXT_OPTIONS, lineWidth: 0, defaultStringType: quoting }).replace(/\n$/, "");
1390
+ return text2.includes("\n") ? null : text2;
1391
+ }
1392
+ function applySplices(text2, splices) {
1393
+ const ordered = [...splices].sort((a, b) => b.start - a.start || b.end - a.end || b.rank - a.rank);
1394
+ let out = text2;
1395
+ for (const splice of ordered) out = out.slice(0, splice.start) + splice.text + out.slice(splice.end);
1396
+ return out;
1397
+ }
1398
+ function editDocumentText(text2, document, root) {
1399
+ const refuse = (why) => {
1400
+ throw new WaironError(`Cannot write .wai/project.yaml at ${root} as an edit of the file: ${why}. Nothing was written.`);
1401
+ };
1402
+ const crlf = text2.includes("\r\n");
1403
+ const source = crlf ? text2.replace(/\r\n/g, "\n") : text2;
1404
+ const doc = YAML.parseDocument(source, PARSE_OPTIONS);
1405
+ if (doc.errors.length > 0) refuse(`the file does not parse (${doc.errors[0].message})`);
1406
+ const reconciler = new DocumentReconciler(source, doc);
1407
+ reconciler.node(doc.contents, document, [], (value) => {
1408
+ reconciler.splices.push({ start: 0, end: source.length, text: YAML.stringify(value, FRESH_TEXT_OPTIONS), rank: 0 });
1409
+ });
1410
+ const edited = applySplices(source, reconciler.splices);
1411
+ let reread;
1412
+ try {
1413
+ reread = parseYaml(edited);
1414
+ } catch (e) {
1415
+ refuse(`the edited text does not parse (${e instanceof Error ? e.message : String(e)})`);
1416
+ }
1417
+ if (!sameValue(reread, document)) refuse("the edited text does not read back as the document to write");
1418
+ return crlf ? edited.replace(/\n/g, "\r\n") : edited;
1419
+ }
1312
1420
  function parseConfig(document, root) {
1313
1421
  try {
1314
1422
  return ProjectConfigSchema.parse(document);
@@ -1467,9 +1575,9 @@ function registryOver(store, root) {
1467
1575
  const next = rekeyAnchor(rename, { spec: entry.spec, at, covers });
1468
1576
  const base = ["rules", "conformance", "carried", g, "findings", f];
1469
1577
  const named = { code: String(entry.code), spec: entry.spec, at: at ?? "" };
1470
- const change = (path76, field, from, to) => {
1578
+ const change = (path77, field, from, to) => {
1471
1579
  if (from === to) return;
1472
- edits.push({ path: [...base, ...path76], from, to });
1580
+ edits.push({ path: [...base, ...path77], from, to });
1473
1581
  rekeys.push({ ...named, field, from, to });
1474
1582
  };
1475
1583
  change(["spec"], "spec", entry.spec, next.spec);
@@ -1479,6 +1587,38 @@ function registryOver(store, root) {
1479
1587
  });
1480
1588
  store.rewriteScalars(edits, dryRun);
1481
1589
  return rekeys;
1590
+ },
1591
+ setId(id) {
1592
+ if (!PROJECT_ID_RE.test(id)) {
1593
+ throw new WaironError(`Refusing to declare the project id "${id}" at ${root}: it breaks the project-id grammar ([a-z0-9-_.], starting and ending alphanumeric).`);
1594
+ }
1595
+ const config = current2();
1596
+ if (config.id === id) return false;
1597
+ if (config.id !== void 0) {
1598
+ throw new WaironError(
1599
+ `Refusing to declare the project id "${id}" at ${root}: the project already declares "${config.id}". An id is immutable; moving it is the rename migration, never a set.`
1600
+ );
1601
+ }
1602
+ save4({ ...config, id });
1603
+ return true;
1604
+ },
1605
+ declareExternal(alias, declaration2) {
1606
+ if (!EXTERNAL_ALIAS_RE.test(alias)) {
1607
+ throw new WaironError(`Refusing to declare the external "${alias}" at ${root}: an alias must fit [a-z0-9-_]+.`);
1608
+ }
1609
+ if (declaration2.project !== void 0 && !PROJECT_ID_RE.test(declaration2.project)) {
1610
+ throw new WaironError(`Refusing to declare the external "${alias}" at ${root}: the producer id "${declaration2.project}" breaks the project-id grammar.`);
1611
+ }
1612
+ const config = current2();
1613
+ const existing = config.externals?.[alias];
1614
+ if (existing !== void 0) {
1615
+ if (sameValue(existing, declaration2)) return false;
1616
+ throw new WaironError(
1617
+ `Refusing to declare the external "${alias}" at ${root} as ${JSON.stringify(declaration2)}: it is already declared as ${JSON.stringify(existing)}, and a declaration a person wrote is never overwritten.`
1618
+ );
1619
+ }
1620
+ save4({ ...config, externals: { ...config.externals, [alias]: declaration2 } });
1621
+ return true;
1482
1622
  }
1483
1623
  };
1484
1624
  }
@@ -1566,18 +1706,25 @@ function projectConfigRepositoryOver(adapter, rootDir) {
1566
1706
  },
1567
1707
  rekeyCarried(rename, dryRun) {
1568
1708
  return registry.rekeyCarried(rename, dryRun);
1709
+ },
1710
+ setId(id) {
1711
+ return registry.setId(id);
1712
+ },
1713
+ declareExternal(alias, declaration2) {
1714
+ return registry.declareExternal(alias, declaration2);
1569
1715
  }
1570
1716
  };
1571
1717
  }
1572
1718
  function projectConfigRepositoryAt(rootDir) {
1573
1719
  return projectConfigRepositoryOver(projectConfigFsAdapterAt(rootDir), rootDir);
1574
1720
  }
1575
- var fs4, path6, import_zod4, KEY_RE, USE_GLOBAL_PACKS_DEFAULT, bound, projectConfigRepository;
1721
+ var fs4, path6, YAML, import_zod4, KEY_RE, FRESH_TEXT_OPTIONS, PARSE_OPTIONS, DocumentReconciler, USE_GLOBAL_PACKS_DEFAULT, bound, projectConfigRepository;
1576
1722
  var init_project_config = __esm({
1577
1723
  "src/config/project-config.ts"() {
1578
1724
  "use strict";
1579
1725
  fs4 = __toESM(require("fs"));
1580
1726
  path6 = __toESM(require("path"));
1727
+ YAML = __toESM(require("yaml"));
1581
1728
  import_zod4 = require("zod");
1582
1729
  init_fs();
1583
1730
  init_yaml();
@@ -1586,6 +1733,124 @@ var init_project_config = __esm({
1586
1733
  init_identity_rename();
1587
1734
  init_project();
1588
1735
  KEY_RE = /^('(?:[^']|'')*'|"(?:[^"\\]|\\.)*"|[^\s'"#{}[\],&*!|>%@`][^:#]*?)\s*:(?=\s|$)/;
1736
+ FRESH_TEXT_OPTIONS = {
1737
+ indent: 2,
1738
+ indentSeq: true,
1739
+ lineWidth: 100,
1740
+ customTags: ["timestamp"]
1741
+ };
1742
+ PARSE_OPTIONS = { customTags: ["timestamp"] };
1743
+ DocumentReconciler = class {
1744
+ constructor(text2, doc) {
1745
+ this.text = text2;
1746
+ this.doc = doc;
1747
+ this.splices = [];
1748
+ }
1749
+ /** Reconcile one node to its target; `replace` rewrites the node wholesale where an edit cannot be precise. */
1750
+ node(node, target, at, replace) {
1751
+ if (YAML.isNode(node) && sameValue(node.toJS(this.doc, { maxAliasCount: -1 }), target)) return;
1752
+ if (YAML.isMap(node) && !node.flow && node.items.length > 0 && isPlainObject(target) && Object.keys(target).length > 0) {
1753
+ this.mapping(node, target, at, replace);
1754
+ } else if (YAML.isSeq(node) && !node.flow && node.items.length > 0 && Array.isArray(target) && target.length > 0) {
1755
+ this.sequence(node, target, at);
1756
+ } else if (YAML.isScalar(node) && !this.scalarInPlace(node, target)) {
1757
+ replace(target);
1758
+ } else if (!YAML.isScalar(node)) {
1759
+ replace(target);
1760
+ }
1761
+ }
1762
+ /** A changed one-line scalar, replaced in its own quoting; false when it is not one. */
1763
+ scalarInPlace(node, target) {
1764
+ const [start2, end] = node.range ?? [0, 0];
1765
+ const flowStyle = node.type === "PLAIN" || node.type === "QUOTE_SINGLE" || node.type === "QUOTE_DOUBLE";
1766
+ if (!flowStyle || start2 === end) return false;
1767
+ const written = inlineScalar(target, node.type);
1768
+ if (written === null) return false;
1769
+ this.splices.push({ start: start2, end, text: written, rank: 0 });
1770
+ return true;
1771
+ }
1772
+ /** Where a pair's text starts (its key's line) and ends (the line after its value). */
1773
+ pairSpan(pair) {
1774
+ const key = pair.key;
1775
+ const start2 = lineStartOf(this.text, key.range[0]);
1776
+ const value = pair.value;
1777
+ const valueEnd = value && value.range ? value.range[1] : key.range[1];
1778
+ return { start: start2, end: lineEndFrom(this.text, valueEnd), col: key.range[0] - start2 };
1779
+ }
1780
+ /** Where a sequence item's text starts (its dash's line) and ends, and the dash's column. */
1781
+ itemSpan(item) {
1782
+ let dash = item.range[0] - 1;
1783
+ while (dash >= 0 && /\s/.test(this.text[dash])) dash -= 1;
1784
+ const start2 = lineStartOf(this.text, dash);
1785
+ return { start: start2, end: lineEndFrom(this.text, item.range[1]), col: dash - start2 };
1786
+ }
1787
+ /** The first line of the comment block directly above the line starting at `lineStart`. */
1788
+ commentBlockStart(lineStart) {
1789
+ let start2 = lineStart;
1790
+ while (start2 > 0) {
1791
+ const above = lineStartOf(this.text, start2 - 1);
1792
+ if (!this.text.slice(above, start2).trim().startsWith("#")) break;
1793
+ start2 = above;
1794
+ }
1795
+ return start2;
1796
+ }
1797
+ mapping(map, target, at, replace) {
1798
+ const keyOf = (pair) => String(YAML.isScalar(pair.key) ? pair.key.value : pair.key);
1799
+ const kept = map.items.filter((pair) => target[keyOf(pair)] !== void 0);
1800
+ if (kept.length === 0) {
1801
+ replace(target);
1802
+ return;
1803
+ }
1804
+ for (const pair of map.items) {
1805
+ const key = keyOf(pair);
1806
+ const span = this.pairSpan(pair);
1807
+ if (target[key] === void 0) {
1808
+ this.splices.push({ start: span.start, end: span.end, text: "", rank: 0 });
1809
+ continue;
1810
+ }
1811
+ this.node(pair.value, target[key], [...at, key], (value) => {
1812
+ this.splices.push({ start: span.start, end: span.end, text: freshBlock({ [key]: value }, span.col), rank: 0 });
1813
+ });
1814
+ }
1815
+ this.insertNewKeys(map, kept, target, at, keyOf);
1816
+ }
1817
+ /**
1818
+ * Insert the keys the document adds at their place in the schema's field
1819
+ * order: after the nearest preceding schema field the mapping keeps, else
1820
+ * before the nearest following one (above its comments), else after the
1821
+ * mapping's last key.
1822
+ */
1823
+ insertNewKeys(map, kept, target, at, keyOf) {
1824
+ const present = new Map(kept.map((pair) => [keyOf(pair), pair]));
1825
+ const order = schemaFieldOrder(at) ?? [];
1826
+ const col = this.pairSpan(kept[0]).col;
1827
+ const added = Object.keys(target).filter((key) => target[key] !== void 0 && !map.items.some((pair) => keyOf(pair) === key));
1828
+ added.forEach((key, n) => {
1829
+ const index = order.indexOf(key);
1830
+ const before = index < 0 ? void 0 : order.slice(0, index).reverse().find((k) => present.has(k));
1831
+ const after = index < 0 ? void 0 : order.slice(index + 1).find((k) => present.has(k));
1832
+ const offset = before !== void 0 ? this.pairSpan(present.get(before)).end : after !== void 0 ? this.commentBlockStart(this.pairSpan(present.get(after)).start) : this.pairSpan(kept[kept.length - 1]).end;
1833
+ const rank = -at.length * 1e5 + (index < 0 ? order.length + n : index);
1834
+ this.splices.push({ start: offset, end: offset, text: freshBlock({ [key]: target[key] }, col), rank });
1835
+ });
1836
+ }
1837
+ sequence(seq, target, at) {
1838
+ seq.items.forEach((item, i) => {
1839
+ const span = this.itemSpan(item);
1840
+ if (i >= target.length) {
1841
+ this.splices.push({ start: span.start, end: span.end, text: "", rank: 0 });
1842
+ return;
1843
+ }
1844
+ this.node(item, target[i], [...at, i], (value) => {
1845
+ this.splices.push({ start: span.start, end: span.end, text: freshBlock([value], span.col), rank: 0 });
1846
+ });
1847
+ });
1848
+ if (target.length > seq.items.length) {
1849
+ const last = this.itemSpan(seq.items[seq.items.length - 1]);
1850
+ this.splices.push({ start: last.end, end: last.end, text: freshBlock(target.slice(seq.items.length), last.col), rank: -(at.length + 1) * 1e5 });
1851
+ }
1852
+ }
1853
+ };
1589
1854
  USE_GLOBAL_PACKS_DEFAULT = ProjectConfigSchema.shape.extensions.unwrap().parse({}).useGlobalPacks;
1590
1855
  bound = () => projectConfigRepositoryAt(getProjectRoot());
1591
1856
  projectConfigRepository = {
@@ -1633,6 +1898,12 @@ var init_project_config = __esm({
1633
1898
  },
1634
1899
  rekeyCarried(rename, dryRun) {
1635
1900
  return bound().rekeyCarried(rename, dryRun);
1901
+ },
1902
+ setId(id) {
1903
+ return bound().setId(id);
1904
+ },
1905
+ declareExternal(alias, declaration2) {
1906
+ return bound().declareExternal(alias, declaration2);
1636
1907
  }
1637
1908
  };
1638
1909
  }
@@ -3269,10 +3540,10 @@ function stepGraph(method2) {
3269
3540
  const s = byNum.get(n);
3270
3541
  if (s.type !== "parallel" || s.endStep === void 0 || !s.branches?.length) continue;
3271
3542
  const entries = s.branches.map((b) => b.step).sort((a, b) => a - b);
3272
- const join55 = fallNext(s.endStep);
3543
+ const join56 = fallNext(s.endStep);
3273
3544
  for (let i = 0; i < entries.length; i++) {
3274
3545
  const armEnd = i + 1 < entries.length ? prevOf(entries[i + 1]) : s.endStep;
3275
- if (armEnd !== void 0 && armEnd >= entries[i]) armEndJoin.set(armEnd, join55);
3546
+ if (armEnd !== void 0 && armEnd >= entries[i]) armEndJoin.set(armEnd, join56);
3276
3547
  }
3277
3548
  }
3278
3549
  const successorsOf = (n) => {
@@ -3363,17 +3634,17 @@ function stepConfigVerdict(method2) {
3363
3634
  };
3364
3635
  const labelled = /* @__PURE__ */ new Map();
3365
3636
  for (const step of steps) {
3366
- const label2 = step.label;
3367
- if (!label2) continue;
3368
- const first = labelled.get(label2);
3637
+ const label3 = step.label;
3638
+ if (!label3) continue;
3639
+ const first = labelled.get(label3);
3369
3640
  if (first !== void 0) {
3370
3641
  problems.push({
3371
3642
  stepNumber: step.stepNumber,
3372
3643
  code: "DUPLICATE_STEP_LABEL",
3373
- detail: `steps ${first} and ${step.stepNumber} share the label "${label2}". A label anchors one step for later deltas to address; two steps holding it makes every symbolic reference ambiguous and blocks all further edits to this implementation. Rename one.`
3644
+ detail: `steps ${first} and ${step.stepNumber} share the label "${label3}". A label anchors one step for later deltas to address; two steps holding it makes every symbolic reference ambiguous and blocks all further edits to this implementation. Rename one.`
3374
3645
  });
3375
3646
  } else {
3376
- labelled.set(label2, step.stepNumber);
3647
+ labelled.set(label3, step.stepNumber);
3377
3648
  }
3378
3649
  }
3379
3650
  const byNum = /* @__PURE__ */ new Map();
@@ -4051,9 +4322,9 @@ function buildDrawioXml(model, L) {
4051
4322
  const parentId = nestInPattern ? "comp_" + owner.id : "sub_" + comp.subsystem;
4052
4323
  const parentBox = nestInPattern ? L.boxes[owner.id] : L.subs[comp.subsystem];
4053
4324
  const colors = COLORS[stereo(comp.componentType)];
4054
- const label2 = comp.name + "\n\xAB" + comp.componentType + (comp.portalType ? "/" + comp.portalType : "") + "\xBB";
4325
+ const label3 = comp.name + "\n\xAB" + comp.componentType + (comp.portalType ? "/" + comp.portalType : "") + "\xBB";
4055
4326
  const style = isPattern2 ? "rounded=1;fillColor=" + colors.fill + ";strokeColor=" + colors.stroke + ";dashed=1;verticalAlign=top;fontStyle=1;container=1;collapsible=1;whiteSpace=wrap;" : "rounded=1;fillColor=" + colors.fill + ";strokeColor=" + colors.stroke + ";whiteSpace=wrap;fontSize=11;" + (comp.public ? "strokeWidth=3;" : "");
4056
- vertex("comp_" + comp.id, parentId, label2, style, box, parentBox);
4327
+ vertex("comp_" + comp.id, parentId, label3, style, box, parentBox);
4057
4328
  });
4058
4329
  let edgeN = 0;
4059
4330
  model.edges.forEach(function(edge) {
@@ -4137,19 +4408,19 @@ function buildExcalidrawScene(model, L) {
4137
4408
  }
4138
4409
  function boundLabel(rect, text2, fontSize, verticalAlign) {
4139
4410
  const id = rect.id + "-label";
4140
- const label2 = base(id, "text", { x: rect.x + 8, y: rect.y + 6, w: rect.width - 16, h: 20 });
4141
- label2.roundness = null;
4142
- label2.text = text2;
4143
- label2.originalText = text2;
4144
- label2.fontSize = fontSize;
4145
- label2.fontFamily = 1;
4146
- label2.textAlign = "center";
4147
- label2.verticalAlign = verticalAlign;
4148
- label2.containerId = rect.id;
4149
- label2.autoResize = true;
4150
- label2.lineHeight = 1.25;
4411
+ const label3 = base(id, "text", { x: rect.x + 8, y: rect.y + 6, w: rect.width - 16, h: 20 });
4412
+ label3.roundness = null;
4413
+ label3.text = text2;
4414
+ label3.originalText = text2;
4415
+ label3.fontSize = fontSize;
4416
+ label3.fontFamily = 1;
4417
+ label3.textAlign = "center";
4418
+ label3.verticalAlign = verticalAlign;
4419
+ label3.containerId = rect.id;
4420
+ label3.autoResize = true;
4421
+ label3.lineHeight = 1.25;
4151
4422
  rect.boundElements.push({ id, type: "text" });
4152
- return label2;
4423
+ return label3;
4153
4424
  }
4154
4425
  const elements = [];
4155
4426
  const rectById = {};
@@ -4174,8 +4445,8 @@ function buildExcalidrawScene(model, L) {
4174
4445
  rect.strokeStyle = isPattern2 || stereo(comp.componentType) === "pattern" ? "dashed" : "solid";
4175
4446
  elements.push(rect);
4176
4447
  rectById[comp.id] = rect;
4177
- const label2 = comp.name + "\n\xAB" + comp.componentType + (comp.portalType ? "/" + comp.portalType : "") + "\xBB";
4178
- elements.push(boundLabel(rect, label2, 11, isPattern2 ? "top" : "middle"));
4448
+ const label3 = comp.name + "\n\xAB" + comp.componentType + (comp.portalType ? "/" + comp.portalType : "") + "\xBB";
4449
+ elements.push(boundLabel(rect, label3, 11, isPattern2 ? "top" : "middle"));
4179
4450
  });
4180
4451
  let edgeN = 0;
4181
4452
  model.edges.forEach(function(edge) {
@@ -9586,8 +9857,8 @@ function pinExternals(aliases) {
9586
9857
  pins.push({ alias: binding.external.alias, outcome: "unresolved", project: binding.external.project, usedNames: 0, unexported: [], detail: binding.external.problem });
9587
9858
  continue;
9588
9859
  }
9589
- const { pin, changed } = pinBinding(binding, lock);
9590
- pins.push(pin);
9860
+ const { pin: pin2, changed } = pinBinding(binding, lock);
9861
+ pins.push(pin2);
9591
9862
  lockChanged ||= changed;
9592
9863
  }
9593
9864
  if (!named) {
@@ -10500,21 +10771,21 @@ function buildCodeIndex(model) {
10500
10771
  const declarations = /* @__PURE__ */ new Map();
10501
10772
  const anchors = /* @__PURE__ */ new Map();
10502
10773
  const findingAnchors = /* @__PURE__ */ new Map();
10503
- const namesAt = (cache2, path76, make) => {
10504
- const cached = cache2.get(path76);
10774
+ const namesAt = (cache2, path77, make) => {
10775
+ const cached = cache2.get(path77);
10505
10776
  if (cached) return cached;
10506
- const f = facts.get(path76);
10777
+ const f = facts.get(path77);
10507
10778
  if (!f) return NO_NAMES;
10508
10779
  const built = make(f);
10509
- cache2.set(path76, built);
10780
+ cache2.set(path77, built);
10510
10781
  return built;
10511
10782
  };
10512
10783
  const republished = /* @__PURE__ */ new Map();
10513
- const republishedFrom = (path76) => {
10514
- const cached = republished.get(path76);
10784
+ const republishedFrom = (path77) => {
10785
+ const cached = republished.get(path77);
10515
10786
  if (cached) return cached;
10516
- const out = /* @__PURE__ */ new Set([path76]);
10517
- const queue = [path76];
10787
+ const out = /* @__PURE__ */ new Set([path77]);
10788
+ const queue = [path77];
10518
10789
  while (queue.length) {
10519
10790
  const from = queue.pop();
10520
10791
  const f = facts.get(from);
@@ -10527,7 +10798,7 @@ function buildCodeIndex(model) {
10527
10798
  }
10528
10799
  }
10529
10800
  }
10530
- republished.set(path76, out);
10801
+ republished.set(path77, out);
10531
10802
  return out;
10532
10803
  };
10533
10804
  const NO_ORIGIN = /* @__PURE__ */ new Set();
@@ -10536,9 +10807,9 @@ function buildCodeIndex(model) {
10536
10807
  return to ? republishedFrom(to) : NO_ORIGIN;
10537
10808
  };
10538
10809
  const scopeOf2 = (site, from) => {
10539
- const path76 = pathKey(site.from ?? from);
10540
- const f = facts.get(path76);
10541
- return f?.status === "analyzed" && f.analysisGrade === "exact" ? { path: path76, facts: f } : void 0;
10810
+ const path77 = pathKey(site.from ?? from);
10811
+ const f = facts.get(path77);
10812
+ return f?.status === "analyzed" && f.analysisGrade === "exact" ? { path: path77, facts: f } : void 0;
10542
10813
  };
10543
10814
  const originOf2 = (site, from) => {
10544
10815
  const resolved = scopeOf2(site, from);
@@ -10577,7 +10848,7 @@ function buildCodeIndex(model) {
10577
10848
  if (site.via) for (const typeName of localTypesOf(f, site.via)) widenThroughType(typeName);
10578
10849
  return out;
10579
10850
  };
10580
- const declarationsAt = (path76) => namesAt(declarations, pathKey(path76), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames]));
10851
+ const declarationsAt = (path77) => namesAt(declarations, pathKey(path77), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames]));
10581
10852
  const forwardsOf = (start2, name) => {
10582
10853
  const out = [];
10583
10854
  const seen = /* @__PURE__ */ new Set();
@@ -10603,10 +10874,10 @@ function buildCodeIndex(model) {
10603
10874
  return {
10604
10875
  paths,
10605
10876
  exactPaths,
10606
- factsAt: (path76) => facts.get(pathKey(path76)),
10877
+ factsAt: (path77) => facts.get(pathKey(path77)),
10607
10878
  declarationsAt,
10608
- anchorsAt: (path76) => namesAt(anchors, pathKey(path76), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
10609
- findingAnchorsAt: (path76) => namesAt(findingAnchors, pathKey(path76), (f) => new Set(f.anchoredNames)),
10879
+ anchorsAt: (path77) => namesAt(anchors, pathKey(path77), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
10880
+ findingAnchorsAt: (path77) => namesAt(findingAnchors, pathKey(path77), (f) => new Set(f.anchoredNames)),
10610
10881
  originOf: originOf2,
10611
10882
  possibleOriginsOf,
10612
10883
  forwardsOf
@@ -10645,10 +10916,10 @@ function buildRealizationIndex(ctx) {
10645
10916
  return {
10646
10917
  paths,
10647
10918
  implementationsOf: (compId) => implsByComponent.get(compId) ?? NONE_IMPL,
10648
- implementationsAt: (path76) => implsByPath.get(pathKey(path76)) ?? NONE_IMPL,
10919
+ implementationsAt: (path77) => implsByPath.get(pathKey(path77)) ?? NONE_IMPL,
10649
10920
  filesOf: (compId) => filesByComponent.get(compId) ?? NONE_PATH,
10650
10921
  filesIn: (subsystemId) => filesBySubsystem.get(subsystemId) ?? NONE_PATH,
10651
- componentsAt: (path76) => componentsByPath.get(pathKey(path76)) ?? NONE_COMP
10922
+ componentsAt: (path77) => componentsByPath.get(pathKey(path77)) ?? NONE_COMP
10652
10923
  };
10653
10924
  }
10654
10925
  function buildImportGraph(index, universe) {
@@ -10660,22 +10931,22 @@ function buildImportGraph(index, universe) {
10660
10931
  if (to && to !== from) into.add(to);
10661
10932
  }
10662
10933
  };
10663
- const importsOf = (path76) => {
10664
- const cached = imports.get(path76);
10934
+ const importsOf = (path77) => {
10935
+ const cached = imports.get(path77);
10665
10936
  if (cached) return cached;
10666
- const facts = index.factsAt(path76);
10937
+ const facts = index.factsAt(path77);
10667
10938
  const out = /* @__PURE__ */ new Set();
10668
- if (facts?.status === "analyzed") resolveAll(path76, facts.imports, out);
10669
- imports.set(path76, out);
10939
+ if (facts?.status === "analyzed") resolveAll(path77, facts.imports, out);
10940
+ imports.set(path77, out);
10670
10941
  return out;
10671
10942
  };
10672
- const tracesOf = (path76) => {
10673
- const cached = traces.get(path76);
10943
+ const tracesOf = (path77) => {
10944
+ const cached = traces.get(path77);
10674
10945
  if (cached) return cached;
10675
- const facts = index.factsAt(path76);
10676
- const out = new Set(importsOf(path76));
10677
- if (facts?.status === "analyzed") resolveAll(path76, facts.reexports, out);
10678
- traces.set(path76, out);
10946
+ const facts = index.factsAt(path77);
10947
+ const out = new Set(importsOf(path77));
10948
+ if (facts?.status === "analyzed") resolveAll(path77, facts.reexports, out);
10949
+ traces.set(path77, out);
10679
10950
  return out;
10680
10951
  };
10681
10952
  return {
@@ -12455,11 +12726,11 @@ var init_unconditional_call_cycles = __esm({
12455
12726
  const memberEdges = keys.flatMap((k) => (adjacency.get(k) ?? []).filter((e) => inScc.has(e.toKey)));
12456
12727
  if (memberEdges.length === 0) continue;
12457
12728
  const anchor = [...memberEdges].sort((a, b) => byCodeUnit(a.fromKey, b.fromKey) || byCodeUnit(a.impl.id, b.impl.id) || a.stepNumber - b.stepNumber)[0];
12458
- const path76 = [...keys].sort(byCodeUnit).join(" \u2192 ");
12729
+ const path77 = [...keys].sort(byCodeUnit).join(" \u2192 ");
12459
12730
  ctx.addIssue(
12460
12731
  "warning",
12461
12732
  "UNCONDITIONAL_CALL_CYCLE",
12462
- `Call cycle with no guard: ${path76} \u2014 every call edge in this cycle is unavoidable on all paths of its narrative (e.g. step ${anchor.stepNumber} of "${anchor.methodName}" in "${anchor.impl.id}" always calls ${anchor.toLabel}). This recurses without a base case, by construction. Guard at least one edge with a branch/return before the call, or lint.allow with the termination argument.`,
12733
+ `Call cycle with no guard: ${path77} \u2014 every call edge in this cycle is unavoidable on all paths of its narrative (e.g. step ${anchor.stepNumber} of "${anchor.methodName}" in "${anchor.impl.id}" always calls ${anchor.toLabel}). This recurses without a base case, by construction. Guard at least one edge with a branch/return before the call, or lint.allow with the termination argument.`,
12463
12734
  anchor.impl.id,
12464
12735
  memberEdges.some((e) => ctx.isImplementationDraft(e.impl))
12465
12736
  );
@@ -14382,8 +14653,8 @@ var init_lifecycle_entrypoints = __esm({
14382
14653
  });
14383
14654
 
14384
14655
  // src/core/rules/wiring/portal-mounts.ts
14385
- function liesUnder(path76, prefix) {
14386
- return path76 === prefix || path76.startsWith(prefix + "/");
14656
+ function liesUnder(path77, prefix) {
14657
+ return path77 === prefix || path77.startsWith(prefix + "/");
14387
14658
  }
14388
14659
  var portalMountsRule;
14389
14660
  var init_portal_mounts = __esm({
@@ -15378,12 +15649,12 @@ var init_method_realization = __esm({
15378
15649
  );
15379
15650
  continue;
15380
15651
  }
15381
- const label2 = methodImpl?.symbol ? `"${method2.name}" (symbol "${symbol}")` : `"${method2.name}"`;
15652
+ const label3 = methodImpl?.symbol ? `"${method2.name}" (symbol "${symbol}")` : `"${method2.name}"`;
15382
15653
  const weakHint = tier === "declared" && inAnchored ? " Only a weak string/word anchor exists \u2014 declare the symbol, map it via a per-method `symbol`, or dial this method to `anchored`." : "";
15383
15654
  ctx.addIssue(
15384
15655
  "warning",
15385
15656
  "UNREALIZED_METHOD",
15386
- `Method ${label2} of contract "${impl.contract}" is not realized in "${file}" at the "${tier}" tier (analysis grade: ${facts.analysisGrade}).${weakHint}`,
15657
+ `Method ${label3} of contract "${impl.contract}" is not realized in "${file}" at the "${tier}" tier (analysis grade: ${facts.analysisGrade}).${weakHint}`,
15387
15658
  impl.id,
15388
15659
  draft
15389
15660
  );
@@ -15693,11 +15964,11 @@ var init_hidden_state = __esm({
15693
15964
  byPath.set(key, list2);
15694
15965
  }
15695
15966
  }
15696
- for (const path76 of code.exactPaths) {
15697
- const facts = code.factsAt(path76);
15967
+ for (const path77 of code.exactPaths) {
15968
+ const facts = code.factsAt(path77);
15698
15969
  const bindings = facts.topLevelMutableBindings ?? [];
15699
15970
  if (bindings.length === 0) continue;
15700
- const mapped = byPath.get(path76) ?? [];
15971
+ const mapped = byPath.get(path77) ?? [];
15701
15972
  if (mapped.length === 0) continue;
15702
15973
  if (!mapped.every((m) => isStatelessLogic(m.component))) continue;
15703
15974
  const anchor = [...mapped].sort((a, b) => a.impl.id.localeCompare(b.impl.id))[0];
@@ -15770,7 +16041,7 @@ var init_dependency_conformance = __esm({
15770
16041
  }
15771
16042
  return false;
15772
16043
  };
15773
- const draftAt = (path76) => realization.implementationsAt(path76).some((impl) => ctx.isImplementationDraft(impl));
16044
+ const draftAt = (path77) => realization.implementationsAt(path77).some((impl) => ctx.isImplementationDraft(impl));
15774
16045
  for (const fromPath of mappedPaths) {
15775
16046
  const fromComponents = realization.componentsAt(fromPath);
15776
16047
  for (const toPath of graph2.importsOf(fromPath)) {
@@ -15861,7 +16132,7 @@ var init_portal_imports = __esm({
15861
16132
  }
15862
16133
  return files;
15863
16134
  };
15864
- const draftAt = (path76) => realization.implementationsAt(path76).some((impl) => ctx.isImplementationDraft(impl));
16135
+ const draftAt = (path77) => realization.implementationsAt(path77).some((impl) => ctx.isImplementationDraft(impl));
15865
16136
  for (const fromPath of mappedPaths) {
15866
16137
  const fromSubsystems = new Set(realization.componentsAt(fromPath).map((c) => c.subsystem));
15867
16138
  for (const toPath of graph2.importsOf(fromPath)) {
@@ -16012,8 +16283,8 @@ var init_integration_sim_wiring = __esm({
16012
16283
  const depFiles = depComp.subsystem !== comp.subsystem ? modulesIn(depComp.subsystem) : modulesOf(dep);
16013
16284
  if (depFiles.length === 0) continue;
16014
16285
  if (!depFiles.some((f) => reach2.has(f))) {
16015
- const label2 = depComp.subsystem !== comp.subsystem ? `"${dep}" (any module of subsystem "${depComp.subsystem}")` : `"${dep}" (${depFiles.join(" | ")})`;
16016
- missing.push(label2);
16286
+ const label3 = depComp.subsystem !== comp.subsystem ? `"${dep}" (any module of subsystem "${depComp.subsystem}")` : `"${dep}" (${depFiles.join(" | ")})`;
16287
+ missing.push(label3);
16017
16288
  }
16018
16289
  }
16019
16290
  if (missing.length === 0) continue;
@@ -16131,27 +16402,27 @@ var init_type_realization = __esm({
16131
16402
  for (const type of ctx.types) {
16132
16403
  if (!type.sourcePath) continue;
16133
16404
  if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
16134
- const blocked = /* @__PURE__ */ new Set();
16405
+ const blocked2 = /* @__PURE__ */ new Set();
16135
16406
  for (const file of typeSourceFiles(type)) {
16136
16407
  const key = pathKey(file);
16137
16408
  const facts = code.factsAt(key);
16138
16409
  if (!facts || facts.status === "analyzed") continue;
16139
- blocked.add(key);
16410
+ blocked2.add(key);
16140
16411
  const problem = FILE_PROBLEMS[facts.status];
16141
16412
  if (!problem) continue;
16142
16413
  ctx.addIssue(problem.severity, problem.code, `${ownerOf2(type, file)} sourcePath "${file}" ${problem.detail}`, type.id);
16143
16414
  }
16144
16415
  const typeKey = pathKey(type.sourcePath);
16145
16416
  const typeFacts = code.factsAt(typeKey);
16146
- if (typeFacts && !blocked.has(typeKey)) {
16417
+ if (typeFacts && !blocked2.has(typeKey)) {
16147
16418
  const symbol = type.symbol ?? type.name;
16148
16419
  const published = typeFacts.analysisGrade === "exact" ? new Set(typeFacts.reexportOnly ? [] : typeFacts.exportedNames) : code.declarationsAt(typeKey);
16149
16420
  if (!published.has(symbol)) {
16150
- const label2 = type.symbol ? `"${type.name}" (symbol "${symbol}")` : `"${type.name}"`;
16421
+ const label3 = type.symbol ? `"${type.name}" (symbol "${symbol}")` : `"${type.name}"`;
16151
16422
  ctx.addIssue(
16152
16423
  "warning",
16153
16424
  "UNREALIZED_TYPE",
16154
- `Type ${label2} is not published by "${type.sourcePath}" (analysis grade: ${typeFacts.analysisGrade})${typeFacts.reexportOnly ? ", which is a pure re-export barrel and declares nothing of its own" : ""} \u2014 name the file the declaration lives in, or bind the code-level name with \`symbol\`.`,
16425
+ `Type ${label3} is not published by "${type.sourcePath}" (analysis grade: ${typeFacts.analysisGrade})${typeFacts.reexportOnly ? ", which is a pure re-export barrel and declares nothing of its own" : ""} \u2014 name the file the declaration lives in, or bind the code-level name with \`symbol\`.`,
16155
16426
  type.id
16156
16427
  );
16157
16428
  }
@@ -16159,16 +16430,16 @@ var init_type_realization = __esm({
16159
16430
  for (const method2 of type.methods ?? []) {
16160
16431
  const file = method2.sourcePath ?? type.sourcePath;
16161
16432
  const key = pathKey(file);
16162
- if (blocked.has(key)) continue;
16433
+ if (blocked2.has(key)) continue;
16163
16434
  const facts = code.factsAt(key);
16164
16435
  if (!facts || facts.status !== "analyzed") continue;
16165
16436
  const symbol = method2.symbol ?? method2.name;
16166
16437
  if (code.declarationsAt(key).has(symbol)) continue;
16167
- const label2 = method2.symbol ? `"${method2.name}" (symbol "${symbol}")` : `"${method2.name}"`;
16438
+ const label3 = method2.symbol ? `"${method2.name}" (symbol "${symbol}")` : `"${method2.name}"`;
16168
16439
  ctx.addIssue(
16169
16440
  "warning",
16170
16441
  "UNREALIZED_TYPE_METHOD",
16171
- `Method ${label2} of type "${type.id}" is not declared in "${file}" (analysis grade: ${facts.analysisGrade}) \u2014 a pure type method is often a free function named after its type; bind that name with \`symbol\`, or name the file it lives in with the method's own \`sourcePath\`.`,
16442
+ `Method ${label3} of type "${type.id}" is not declared in "${file}" (analysis grade: ${facts.analysisGrade}) \u2014 a pure type method is often a free function named after its type; bind that name with \`symbol\`, or name the file it lives in with the method's own \`sourcePath\`.`,
16172
16443
  type.id
16173
16444
  );
16174
16445
  }
@@ -16447,8 +16718,8 @@ var init_param_conformance = __esm({
16447
16718
  });
16448
16719
 
16449
16720
  // src/core/rules/conformance/route-coverage.ts
16450
- function liesUnder2(path76, prefix) {
16451
- return path76 === prefix || path76.startsWith(prefix + "/");
16721
+ function liesUnder2(path77, prefix) {
16722
+ return path77 === prefix || path77.startsWith(prefix + "/");
16452
16723
  }
16453
16724
  function normalizeSegment(segment) {
16454
16725
  return /^\{.+\}$/.test(segment) || segment.startsWith(":") ? "*" : segment;
@@ -20553,8 +20824,8 @@ function truncate(text2, max) {
20553
20824
  const clean = text2.replace(/\s+/g, " ").trim();
20554
20825
  return clean.length <= max ? clean : `${clean.slice(0, max - 1)}\u2026`;
20555
20826
  }
20556
- function nodeDecl(id, label2, comp) {
20557
- const l = escapeLabel(label2);
20827
+ function nodeDecl(id, label3, comp) {
20828
+ const l = escapeLabel(label3);
20558
20829
  if (comp.componentType === "Portal" || comp.componentType === "Observer") return `${id}(["${l}"])`;
20559
20830
  if (comp.componentType === "Store" || comp.componentType === "Index") return `${id}[("${l}")]`;
20560
20831
  if (PATTERN_TYPES.has(comp.componentType)) return `${id}[["${l}"]]`;
@@ -20631,8 +20902,8 @@ function generateComponentDiagram(options) {
20631
20902
  lines.push(` subgraph ${ids.idFor(`sub:${subId}`)}["${subLabel}"]`);
20632
20903
  for (const comp of comps) {
20633
20904
  const nodeId = ids.idFor(comp.id);
20634
- const label2 = `${comp.name}<br/>\xAB${comp.componentType}\xBB`;
20635
- lines.push(` ${nodeDecl(nodeId, label2, comp)}`);
20905
+ const label3 = `${comp.name}<br/>\xAB${comp.componentType}\xBB`;
20906
+ lines.push(` ${nodeDecl(nodeId, label3, comp)}`);
20636
20907
  assignClass(stereotypeClass(comp), nodeId);
20637
20908
  if (graph2.publicComponents.has(comp.id)) assignClass("publicSurface", nodeId);
20638
20909
  }
@@ -20977,8 +21248,8 @@ function makeNodes(roots) {
20977
21248
  });
20978
21249
  }
20979
21250
  function describe(node) {
20980
- const label2 = node.id ?? node.name ?? "(no id)";
20981
- return node.namespace === "" ? `"${label2}" (the bound root)` : `"${label2}" (mounted at "${node.namespace}")`;
21251
+ const label3 = node.id ?? node.name ?? "(no id)";
21252
+ return node.namespace === "" ? `"${label3}" (the bound root)` : `"${label3}" (mounted at "${node.namespace}")`;
20982
21253
  }
20983
21254
  function identityProblems(nodes) {
20984
21255
  const problems = [];
@@ -21410,8 +21681,8 @@ function reach(audience) {
21410
21681
  const idx = SURFACE_AUDIENCES.indexOf(audience ?? "instance");
21411
21682
  return idx === -1 ? SURFACE_AUDIENCES.indexOf("instance") : idx;
21412
21683
  }
21413
- function classifySource(world, sourceId, label2, context) {
21414
- if (!sourceId) return { kind: "unresolvable", detail: `"${label2}" names no source subsystem and no item whose owner could supply one` };
21684
+ function classifySource(world, sourceId, label3, context) {
21685
+ if (!sourceId) return { kind: "unresolvable", detail: `"${label3}" names no source subsystem and no item whose owner could supply one` };
21415
21686
  const sub = world.subsystems.get(sourceId);
21416
21687
  const ownSub = sub && (!context || (context.family.owners.get(sourceId) ?? context.namespace) === context.namespace);
21417
21688
  if (sub && ownSub) return { kind: "subsystem", subsystem: sub };
@@ -21424,13 +21695,13 @@ function classifySource(world, sourceId, label2, context) {
21424
21695
  return { kind: "project", namespace: external.producer, label: `external "${external.alias}"` };
21425
21696
  }
21426
21697
  if (external?.sourceKind === "path") {
21427
- return { kind: "unresolvable", detail: `"${label2}" re-exports from external "${external.alias}", which is found only through source.path \xE2\u20AC\u201D the scan cannot follow its table, so a project re-exports only from projects of its family` };
21698
+ return { kind: "unresolvable", detail: `"${label3}" re-exports from external "${external.alias}", which is found only through source.path \xE2\u20AC\u201D the scan cannot follow its table, so a project re-exports only from projects of its family` };
21428
21699
  }
21429
21700
  if (external) {
21430
- return { kind: "unresolvable", detail: `"${label2}" re-exports from external "${external.alias}", which does not resolve: ${external.problem}` };
21701
+ return { kind: "unresolvable", detail: `"${label3}" re-exports from external "${external.alias}", which does not resolve: ${external.problem}` };
21431
21702
  }
21432
21703
  }
21433
- return { kind: "unresolvable", detail: `"${label2}" re-exports from "${sourceId}", which is neither a subsystem of this project, a member nor a declared external` };
21704
+ return { kind: "unresolvable", detail: `"${label3}" re-exports from "${sourceId}", which is neither a subsystem of this project, a member nor a declared external` };
21434
21705
  }
21435
21706
  function bindFromProject(owner, e, source, table, index, problems) {
21436
21707
  const named = e.component !== void 0 || e.typeDef !== void 0 || e.interface !== void 0;
@@ -21465,16 +21736,16 @@ function resolveProjectTable(system, subsystems, components, interfaces, types,
21465
21736
  const problems = [];
21466
21737
  const candidates = [];
21467
21738
  (system.publicInterfaces ?? []).forEach((e, index) => {
21468
- const label2 = e.as ?? e.id ?? e.interface ?? e.component ?? e.typeDef ?? `#${index + 1}`;
21469
- const source = classifySource(world, sourceOf(world, e), label2, context);
21739
+ const label3 = e.as ?? e.id ?? e.interface ?? e.component ?? e.typeDef ?? `#${index + 1}`;
21740
+ const source = classifySource(world, sourceOf(world, e), label3, context);
21470
21741
  if (source.kind === "unresolvable") {
21471
- problems.push({ kind: "invalid", owner, publicName: label2, detail: source.detail });
21742
+ problems.push({ kind: "invalid", owner, publicName: label3, detail: source.detail });
21472
21743
  return;
21473
21744
  }
21474
21745
  if (source.kind === "project") {
21475
21746
  const table = context.projectTable(source.namespace);
21476
21747
  if (!table) {
21477
- problems.push({ kind: "named-cycle", owner, publicName: label2, targets: [owner, source.namespace], detail: `re-exports "${label2}" from ${source.label}, whose table leads back to this project without reaching the item` });
21748
+ problems.push({ kind: "named-cycle", owner, publicName: label3, targets: [owner, source.namespace], detail: `re-exports "${label3}" from ${source.label}, whose table leads back to this project without reaching the item` });
21478
21749
  return;
21479
21750
  }
21480
21751
  candidates.push(...bindFromProject(owner, e, source, table, index, problems));
@@ -21488,7 +21759,10 @@ function resolveProjectTable(system, subsystems, components, interfaces, types,
21488
21759
  source.subsystem,
21489
21760
  sourceTable,
21490
21761
  { component: e.component, interface: e.interface, typeDef: e.typeDef },
21491
- () => e.as ?? e.id ?? e.interface ?? e.component ?? e.typeDef,
21762
+ // The default public name is the item's own LOCAL id: a member's L0 is
21763
+ // read with its ids qualified into its mount, and its entry names the
21764
+ // item as the member wrote it.
21765
+ () => e.as ?? e.id ?? localName2(e.interface ?? e.component ?? e.typeDef),
21492
21766
  false,
21493
21767
  problems
21494
21768
  );
@@ -21606,7 +21880,7 @@ function landingEntries(ref, visible, interfaces, componentIds) {
21606
21880
  });
21607
21881
  }
21608
21882
  if (ref.position === "type" || ref.position === "reexport") {
21609
- const typed = visible.filter((e) => e.kind === "type" && (e.typeDef === ref.target || matchTypeRef(ref.target, e.typeDef ?? "")));
21883
+ const typed = visible.filter((e) => e.kind === "type" && (e.typeDef === ref.target || matchTypeRef(ref.target, e.typeDef ?? "") || ref.target === `${e.source}::${localName2(e.typeDef ?? "")}`));
21610
21884
  if (typed.length > 0 || ref.position === "type") return typed;
21611
21885
  }
21612
21886
  return [];
@@ -22422,13 +22696,13 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
22422
22696
  const isPlain = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
22423
22697
  const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
22424
22698
  const at = (prefix, key) => prefix ? `${prefix}.${key}` : key;
22425
- const say = (path76, why) => {
22426
- out.push(`${path76} \u2014 ${why}`);
22699
+ const say = (path77, why) => {
22700
+ out.push(`${path77} \u2014 ${why}`);
22427
22701
  };
22428
- const walkValue = (d, m, s, path76, field) => {
22702
+ const walkValue = (d, m, s, path77, field) => {
22429
22703
  if (Array.isArray(d)) {
22430
22704
  if (!Array.isArray(m)) {
22431
- say(path76, "the level does not have this field, so the write dropped it");
22705
+ say(path77, "the level does not have this field, so the write dropped it");
22432
22706
  return;
22433
22707
  }
22434
22708
  if (field === "narrative" && d.some(carriesEditMarker)) return;
@@ -22436,32 +22710,32 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
22436
22710
  if (carriesEditMarker(element)) continue;
22437
22711
  const key = reportIdentityOf(field, element);
22438
22712
  if (key === null) {
22439
- if (!same(m, d)) say(path76, "the level does not keep this value, so the write dropped it");
22440
- else if (same(s, d)) say(path76, "the stored spec already held this value");
22713
+ if (!same(m, d)) say(path77, "the level does not keep this value, so the write dropped it");
22714
+ else if (same(s, d)) say(path77, "the stored spec already held this value");
22441
22715
  return;
22442
22716
  }
22443
22717
  const mMatch = m.find((i) => reportIdentityOf(field, i) === key);
22444
22718
  const sMatch = Array.isArray(s) ? s.find((i) => reportIdentityOf(field, i) === key) : void 0;
22445
22719
  if (mMatch === void 0) {
22446
- say(at(path76, key), "the write did not store this element");
22720
+ say(at(path77, key), "the write did not store this element");
22447
22721
  continue;
22448
22722
  }
22449
- walk2(element, mMatch, sMatch, at(path76, key), field);
22723
+ walk2(element, mMatch, sMatch, at(path77, key), field);
22450
22724
  }
22451
22725
  return;
22452
22726
  }
22453
22727
  if (isPlain(d)) {
22454
22728
  if (!isPlain(m)) {
22455
- say(path76, "the level does not have this field, so the write dropped it");
22729
+ say(path77, "the level does not have this field, so the write dropped it");
22456
22730
  return;
22457
22731
  }
22458
- walk2(d, m, s, path76, field);
22732
+ walk2(d, m, s, path77, field);
22459
22733
  return;
22460
22734
  }
22461
- if (!same(m, d)) say(path76, "the write did not store it \u2014 the level's schema does not keep this value");
22462
- else if (same(s, d)) say(path76, "the stored spec already held this value");
22735
+ if (!same(m, d)) say(path77, "the write did not store it \u2014 the level's schema does not keep this value");
22736
+ else if (same(s, d)) say(path77, "the stored spec already held this value");
22463
22737
  };
22464
- function walk2(d, m, s, path76, container) {
22738
+ function walk2(d, m, s, path77, container) {
22465
22739
  for (const [key, value] of Object.entries(d)) {
22466
22740
  if (DELTA_VERBS.has(key) || JUMP_LABEL_TWINS.has(key)) continue;
22467
22741
  if (key === "label" && LABEL_TWIN_CONTAINERS.has(container)) continue;
@@ -22471,13 +22745,13 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
22471
22745
  if (typeof name !== "string") continue;
22472
22746
  const had = isPlain(s) && s[name] !== void 0;
22473
22747
  const still = isPlain(m) && m[name] !== void 0;
22474
- if (!had) say(at(path76, name), "unset named a field the stored spec did not have");
22475
- else if (still) say(at(path76, name), "unset did not remove it");
22748
+ if (!had) say(at(path77, name), "unset named a field the stored spec did not have");
22749
+ else if (still) say(at(path77, name), "unset did not remove it");
22476
22750
  }
22477
22751
  continue;
22478
22752
  }
22479
22753
  if (value === void 0 || value === null) continue;
22480
- const here = at(path76, key);
22754
+ const here = at(path77, key);
22481
22755
  const mValue = isPlain(m) ? m[key] : void 0;
22482
22756
  const sValue = isPlain(s) ? s[key] : void 0;
22483
22757
  if (mValue === void 0) {
@@ -22523,10 +22797,10 @@ function namesInProse(value, named) {
22523
22797
  if (!value || typeof value !== "object") return false;
22524
22798
  return Object.entries(value).some(([key, nested]) => typeof nested === "string" ? PROSE_FIELDS.has(key) && named.test(nested) : namesInProse(nested, named));
22525
22799
  }
22526
- function judgeMoveResult(hooks, plan) {
22800
+ function judgeMoveResult(hooks, plan2) {
22527
22801
  const refusals = [];
22528
22802
  const notices = [];
22529
- for (const resulting of [plan.edits.get(`component:${plan.source.id}`), plan.edits.get(`component:${plan.target.id}`)]) {
22803
+ for (const resulting of [plan2.edits.get(`component:${plan2.source.id}`), plan2.edits.get(`component:${plan2.target.id}`)]) {
22530
22804
  if (!resulting) continue;
22531
22805
  try {
22532
22806
  const raised = hooks.gate?.("component", resulting.after);
@@ -22565,9 +22839,9 @@ function moveChangeReports(edits, dryRun) {
22565
22839
  summary: edit.before === null ? `${dryRun ? "Would create" : "Created"} ${edit.kind} "${edit.id}" to receive the moved methods.` : `${dryRun ? "Would move" : "Moved"} into ${edit.kind} "${edit.id}": ${changes.length} change${changes.length === 1 ? "" : "s"}.`
22566
22840
  }));
22567
22841
  }
22568
- function moveRename(from, to, methods, plan) {
22842
+ function moveRename(from, to, methods, plan2) {
22569
22843
  return {
22570
- specs: plan.rehomed.map(({ from: left, to: arrived }) => ({ from: left, to: arrived, sites: [...methods] })),
22844
+ specs: plan2.rehomed.map(({ from: left, to: arrived }) => ({ from: left, to: arrived, sites: [...methods] })),
22571
22845
  methods: methods.map((method2) => ({ component: from, method: method2, toComponent: to, toMethod: method2 }))
22572
22846
  };
22573
22847
  }
@@ -22816,6 +23090,12 @@ function deregisterPackRef(ref) {
22816
23090
  function markSelectionsBundled(bundled) {
22817
23091
  projectConfigRepository.markSelectionsBundled(bundled);
22818
23092
  }
23093
+ function setProjectId(id) {
23094
+ return projectConfigRepository.setId(id);
23095
+ }
23096
+ function declareExternal(alias, declaration2) {
23097
+ return projectConfigRepository.declareExternal(alias, declaration2);
23098
+ }
22819
23099
  function specPathsInScope(scopeSubsystem) {
22820
23100
  return current().specPathsInScope(scopeSubsystem);
22821
23101
  }
@@ -24446,23 +24726,23 @@ var init_specs2 = __esm({
24446
24726
  for (const field of fields) delete out[field];
24447
24727
  return out;
24448
24728
  };
24449
- const assertDeltaMarkers = (item, label2) => {
24729
+ const assertDeltaMarkers = (item, label3) => {
24450
24730
  if (!item || typeof item !== "object") return;
24451
24731
  if ("remove" in item && typeof item.remove !== "boolean") {
24452
24732
  throw new Error(
24453
- `Refusing to update ${label2}: "remove" must be the boolean true, got ${JSON.stringify(item.remove)}. A non-boolean is ignored, which would leave the element in place while reporting success.`
24733
+ `Refusing to update ${label3}: "remove" must be the boolean true, got ${JSON.stringify(item.remove)}. A non-boolean is ignored, which would leave the element in place while reporting success.`
24454
24734
  );
24455
24735
  }
24456
24736
  if ("action" in item && item.action !== "add" && item.action !== "delete") {
24457
24737
  throw new Error(
24458
- `Refusing to update ${label2}: unknown action ${JSON.stringify(item.action)} \u2014 expected "add" or "delete". An unknown verb is ignored, which would leave the element in place while reporting success.`
24738
+ `Refusing to update ${label3}: unknown action ${JSON.stringify(item.action)} \u2014 expected "add" or "delete". An unknown verb is ignored, which would leave the element in place while reporting success.`
24459
24739
  );
24460
24740
  }
24461
24741
  };
24462
- const assertUnmatchedIsAnAddition = (deltaItem, key, existingKeys, label2) => {
24742
+ const assertUnmatchedIsAnAddition = (deltaItem, key, existingKeys, label3) => {
24463
24743
  if (deltaItem?.remove === true || deltaItem?.action === "delete") {
24464
24744
  throw new Error(
24465
- `Refusing to delete ${label2} "${String(key)}": nothing with that identity exists. ` + (existingKeys.length ? `Present: ${existingKeys.map(String).join(", ")}.` : "The collection is empty.")
24745
+ `Refusing to delete ${label3} "${String(key)}": nothing with that identity exists. ` + (existingKeys.length ? `Present: ${existingKeys.map(String).join(", ")}.` : "The collection is empty.")
24466
24746
  );
24467
24747
  }
24468
24748
  const near = existingKeys.find(
@@ -24470,7 +24750,7 @@ var init_specs2 = __esm({
24470
24750
  );
24471
24751
  if (near !== void 0) {
24472
24752
  throw new Error(
24473
- `Refusing to add ${label2} "${String(key)}": "${String(near)}" already exists and differs only in case or separators. Use the existing identity to edit it, or pick a name that is not a near-duplicate.`
24753
+ `Refusing to add ${label3} "${String(key)}": "${String(near)}" already exists and differs only in case or separators. Use the existing identity to edit it, or pick a name that is not a near-duplicate.`
24474
24754
  );
24475
24755
  }
24476
24756
  };
@@ -24963,16 +25243,16 @@ var init_specs2 = __esm({
24963
25243
  const complaint = this.unmovableRequest(source, from, to, methods);
24964
25244
  if (complaint) throw new Error(`unmovable request: ${complaint}`);
24965
25245
  const contracts = this.loadInterfaceSpecs();
24966
- const plan = this.planMethodMove(source, to, methods, contracts);
24967
- const repointed = this.repointToNewHome(from, to, methods, plan);
24968
- const mentions = this.moveMentions(from, plan);
24969
- const rename = moveRename(from, to, methods, plan);
24970
- this.rekeyMoveAllows(plan, rename);
25246
+ const plan2 = this.planMethodMove(source, to, methods, contracts);
25247
+ const repointed = this.repointToNewHome(from, to, methods, plan2);
25248
+ const mentions = this.moveMentions(from, plan2);
25249
+ const rename = moveRename(from, to, methods, plan2);
25250
+ this.rekeyMoveAllows(plan2, rename);
24971
25251
  if (hooks) {
24972
- const verdict = judgeMoveResult(hooks, plan);
24973
- plan.notices.push(...verdict.notices);
25252
+ const verdict = judgeMoveResult(hooks, plan2);
25253
+ plan2.notices.push(...verdict.notices);
24974
25254
  if (verdict.refusals.length > 0) {
24975
- const alternatives = this.rankMoveHomes(hooks, from, to, plan);
25255
+ const alternatives = this.rankMoveHomes(hooks, from, to, plan2);
24976
25256
  return {
24977
25257
  moved: false,
24978
25258
  from,
@@ -24982,7 +25262,7 @@ var init_specs2 = __esm({
24982
25262
  repointed: 0,
24983
25263
  refusals: verdict.refusals,
24984
25264
  alternatives,
24985
- notices: plan.notices,
25265
+ notices: plan2.notices,
24986
25266
  summary: refusedMoveSummary(from, to, methods, verdict.refusals, alternatives),
24987
25267
  dryRun,
24988
25268
  mentions,
@@ -24993,7 +25273,7 @@ var init_specs2 = __esm({
24993
25273
  }
24994
25274
  const carried = projectConfigRepository.rekeyCarried(rename, true);
24995
25275
  if (dryRun) {
24996
- const edits2 = moveChangeReports([...plan.edits.values()], true);
25276
+ const edits2 = moveChangeReports([...plan2.edits.values()], true);
24997
25277
  return {
24998
25278
  moved: false,
24999
25279
  from,
@@ -25003,15 +25283,15 @@ var init_specs2 = __esm({
25003
25283
  repointed,
25004
25284
  refusals: [],
25005
25285
  alternatives: [],
25006
- notices: plan.notices,
25007
- summary: `Dry run: moving ${namedMethods(methods)} from "${from}" to "${to}" would touch ${edits2.length} spec${edits2.length === 1 ? "" : "s"}${createdClause(plan.created, "would create")} and re-point ${repointed} reference${repointed === 1 ? "" : "s"}${registerClause(carried, "would rekey")}. Nothing was written.`,
25286
+ notices: plan2.notices,
25287
+ summary: `Dry run: moving ${namedMethods(methods)} from "${from}" to "${to}" would touch ${edits2.length} spec${edits2.length === 1 ? "" : "s"}${createdClause(plan2.created, "would create")} and re-point ${repointed} reference${repointed === 1 ? "" : "s"}${registerClause(carried, "would rekey")}. Nothing was written.`,
25008
25288
  dryRun: true,
25009
25289
  mentions,
25010
- created: plan.created,
25290
+ created: plan2.created,
25011
25291
  carried
25012
25292
  };
25013
25293
  }
25014
- const edits = this.writeMoveEdits([...plan.edits.values()], () => {
25294
+ const edits = this.writeMoveEdits([...plan2.edits.values()], () => {
25015
25295
  projectConfigRepository.rekeyCarried(rename);
25016
25296
  });
25017
25297
  return {
@@ -25023,11 +25303,11 @@ var init_specs2 = __esm({
25023
25303
  repointed,
25024
25304
  refusals: [],
25025
25305
  alternatives: [],
25026
- notices: plan.notices,
25027
- summary: `Moved ${namedMethods(methods)} from "${from}" to "${to}": ${edits.length} spec${edits.length === 1 ? "" : "s"} written${createdClause(plan.created, "created")}, ${repointed} reference${repointed === 1 ? "" : "s"} re-pointed${registerClause(carried, "rekeyed")}${mentions.length ? `, ${mentions.length} still naming the old home in prose or on the wire` : ""}.`,
25306
+ notices: plan2.notices,
25307
+ summary: `Moved ${namedMethods(methods)} from "${from}" to "${to}": ${edits.length} spec${edits.length === 1 ? "" : "s"} written${createdClause(plan2.created, "created")}, ${repointed} reference${repointed === 1 ? "" : "s"} re-pointed${registerClause(carried, "rekeyed")}${mentions.length ? `, ${mentions.length} still naming the old home in prose or on the wire` : ""}.`,
25028
25308
  dryRun: false,
25029
25309
  mentions,
25030
- created: plan.created,
25310
+ created: plan2.created,
25031
25311
  carried
25032
25312
  };
25033
25313
  }
@@ -25038,15 +25318,15 @@ var init_specs2 = __esm({
25038
25318
  * covered `<from>.<method>` unit anywhere names `<to>.<method>`. An allow
25039
25319
  * whose new home the plan does not stage stays where it is.
25040
25320
  */
25041
- rekeyMoveAllows(plan, rename) {
25321
+ rekeyMoveAllows(plan2, rename) {
25042
25322
  const subjects = [...this.everySpecInTree()];
25043
- for (const edit of plan.edits.values()) if (edit.before === null) subjects.push({ kind: edit.kind, id: edit.id, spec: edit.after });
25044
- const stagedAfter = (kind, id) => plan.edits.get(`${kind}:${id}`)?.after;
25323
+ for (const edit of plan2.edits.values()) if (edit.before === null) subjects.push({ kind: edit.kind, id: edit.id, spec: edit.after });
25324
+ const stagedAfter = (kind, id) => plan2.edits.get(`${kind}:${id}`)?.after;
25045
25325
  for (const { kind, id, spec } of subjects) {
25046
25326
  if (id.includes("::")) continue;
25047
25327
  const allows = spec?.lint?.allow ?? [];
25048
25328
  if (allows.length === 0) continue;
25049
- const staged = plan.edits.get(`${kind}:${id}`);
25329
+ const staged = plan2.edits.get(`${kind}:${id}`);
25050
25330
  const subject = staged ? staged.after : cloneSpec(spec);
25051
25331
  const kept = [];
25052
25332
  let changed = false;
@@ -25064,7 +25344,7 @@ var init_specs2 = __esm({
25064
25344
  }
25065
25345
  if (!changed) continue;
25066
25346
  subject.lint = { ...subject.lint, allow: kept };
25067
- if (!staged) this.stageMoveEdit(plan, kind, id, spec, subject);
25347
+ if (!staged) this.stageMoveEdit(plan2, kind, id, spec, subject);
25068
25348
  }
25069
25349
  }
25070
25350
  /**
@@ -25131,7 +25411,7 @@ var init_specs2 = __esm({
25131
25411
  const target = this.loadComponentSpec(to);
25132
25412
  const stated = this.moveTargetContract(to, contracts.filter((i) => i.component === to));
25133
25413
  const implementations = this.loadImplementationSpecs();
25134
- const plan = {
25414
+ const plan2 = {
25135
25415
  edits: /* @__PURE__ */ new Map(),
25136
25416
  source,
25137
25417
  target,
@@ -25153,10 +25433,10 @@ var init_specs2 = __esm({
25153
25433
  arriving.push(...cloneSpec(leaving));
25154
25434
  const remaining = cloneSpec(contract);
25155
25435
  remaining.methods = remaining.methods.filter((m) => !methods.includes(m.name));
25156
- this.stageMoveEdit(plan, "interface", contract.id, contract, remaining);
25157
- plan.rehomed.push({ from: contract.id, to: receiving.id });
25436
+ this.stageMoveEdit(plan2, "interface", contract.id, contract, remaining);
25437
+ plan2.rehomed.push({ from: contract.id, to: receiving.id });
25158
25438
  if (contract.methods.length === leaving.length) {
25159
- plan.notices.push(`"${contract.id}" is left with no methods \u2014 "${source.id}" no longer declares anything on it.`);
25439
+ plan2.notices.push(`"${contract.id}" is left with no methods \u2014 "${source.id}" no longer declares anything on it.`);
25160
25440
  }
25161
25441
  for (const impl of implementations.filter((i) => i.contract === contract.id)) {
25162
25442
  const moving = impl.methods.filter((m) => methods.includes(m.name));
@@ -25165,22 +25445,22 @@ var init_specs2 = __esm({
25165
25445
  for (const entry of moving) if (impl.sourcePath) realizedIn.set(entry.name, impl.sourcePath);
25166
25446
  const kept = cloneSpec(impl);
25167
25447
  kept.methods = kept.methods.filter((m) => !methods.includes(m.name));
25168
- this.stageMoveEdit(plan, "implementation", impl.id, impl, kept);
25448
+ this.stageMoveEdit(plan2, "implementation", impl.id, impl, kept);
25169
25449
  leftImplementations.push(impl.id);
25170
25450
  }
25171
25451
  }
25172
- this.stageMoveEdit(plan, "interface", receiving.id, stated, {
25452
+ this.stageMoveEdit(plan2, "interface", receiving.id, stated, {
25173
25453
  ...cloneSpec(receiving),
25174
25454
  methods: [...cloneSpec(receiving.methods), ...arriving]
25175
25455
  });
25176
- if (!stated) plan.created.push(`interface "${receiving.id}"`);
25177
- if (arriving.some((m) => m.endpoint)) plan.wireBound.push(receiving.id);
25456
+ if (!stated) plan2.created.push(`interface "${receiving.id}"`);
25457
+ if (arriving.some((m) => m.endpoint)) plan2.wireBound.push(receiving.id);
25178
25458
  if (stated && receiving.status !== void 0 && receiving.status !== statusOfContracts(contracts, source.id)) {
25179
- plan.notices.push(`"${receiving.id}" is ${receiving.status} where the methods came from a ${statusOfContracts(contracts, source.id)} contract \u2014 the status of a moved method is the status of its new home.`);
25459
+ plan2.notices.push(`"${receiving.id}" is ${receiving.status} where the methods came from a ${statusOfContracts(contracts, source.id)} contract \u2014 the status of a moved method is the status of its new home.`);
25180
25460
  }
25181
25461
  for (const entry of travelling) {
25182
25462
  if ((entry.narrative ?? []).length === 0 && !entry.intent) {
25183
- plan.notices.push(`"${entry.name}" moves with an empty narrative and no intent \u2014 nothing followed it but its name.`);
25463
+ plan2.notices.push(`"${entry.name}" moves with an empty narrative and no intent \u2014 nothing followed it but its name.`);
25184
25464
  }
25185
25465
  }
25186
25466
  const targetImpl = implementations.find((i) => i.contract === receiving.id);
@@ -25189,33 +25469,33 @@ var init_specs2 = __esm({
25189
25469
  const file = realizedIn.get(entry.name);
25190
25470
  if (entry.sourcePath || !file || file === targetImpl.sourcePath) continue;
25191
25471
  entry.sourcePath = file;
25192
- plan.notices.push(`"${entry.name}" keeps ${file} as its own sourcePath: it was realized there by default, and "${targetImpl.id}" names ${targetImpl.sourcePath ? targetImpl.sourcePath : "no file"} \u2014 moving a method is not moving its code.`);
25472
+ plan2.notices.push(`"${entry.name}" keeps ${file} as its own sourcePath: it was realized there by default, and "${targetImpl.id}" names ${targetImpl.sourcePath ? targetImpl.sourcePath : "no file"} \u2014 moving a method is not moving its code.`);
25193
25473
  }
25194
- this.stageMoveEdit(plan, "implementation", targetImpl.id, targetImpl, {
25474
+ this.stageMoveEdit(plan2, "implementation", targetImpl.id, targetImpl, {
25195
25475
  ...cloneSpec(targetImpl),
25196
25476
  methods: [...cloneSpec(targetImpl.methods), ...travelling]
25197
25477
  });
25198
- for (const id of leftImplementations) plan.rehomed.push({ from: id, to: targetImpl.id });
25478
+ for (const id of leftImplementations) plan2.rehomed.push({ from: id, to: targetImpl.id });
25199
25479
  } else if (travelling.length > 0) {
25200
25480
  const created = createdImplementation(target, receiving.id, source.id, travelling, implementations, contracts);
25201
- this.stageMoveEdit(plan, "implementation", created.spec.id, null, created.spec);
25202
- for (const id of leftImplementations) plan.rehomed.push({ from: id, to: created.spec.id });
25203
- plan.created.push(created.named);
25204
- plan.notices.push(...created.notices);
25481
+ this.stageMoveEdit(plan2, "implementation", created.spec.id, null, created.spec);
25482
+ for (const id of leftImplementations) plan2.rehomed.push({ from: id, to: created.spec.id });
25483
+ plan2.created.push(created.named);
25484
+ plan2.notices.push(...created.notices);
25205
25485
  }
25206
- plan.reach = reachOf(travelling, source.id, new Set(methods)).filter((id) => id !== to);
25486
+ plan2.reach = reachOf(travelling, source.id, new Set(methods)).filter((id) => id !== to);
25207
25487
  const held = /* @__PURE__ */ new Set([...target.dependsOn ?? [], ...target.owns ?? []]);
25208
- plan.newDependencies = plan.reach.filter((id) => !held.has(id) && id !== to);
25209
- this.stageMoveEdit(plan, "component", source.id, source, cloneSpec(source));
25210
- this.stageMoveEdit(plan, "component", target.id, target, {
25488
+ plan2.newDependencies = plan2.reach.filter((id) => !held.has(id) && id !== to);
25489
+ this.stageMoveEdit(plan2, "component", source.id, source, cloneSpec(source));
25490
+ this.stageMoveEdit(plan2, "component", target.id, target, {
25211
25491
  ...cloneSpec(target),
25212
- dependsOn: [...target.dependsOn ?? [], ...plan.newDependencies]
25492
+ dependsOn: [...target.dependsOn ?? [], ...plan2.newDependencies]
25213
25493
  });
25214
- return plan;
25494
+ return plan2;
25215
25495
  }
25216
25496
  /** Stage one spec's rewrite, keeping the version it started from as the snapshot a failed write restores. */
25217
- stageMoveEdit(plan, kind, id, before, after) {
25218
- plan.edits.set(`${kind}:${id}`, { kind, id, before, after });
25497
+ stageMoveEdit(plan2, kind, id, before, after) {
25498
+ plan2.edits.set(`${kind}:${id}`, { kind, id, before, after });
25219
25499
  }
25220
25500
  /**
25221
25501
  * Re-point every reference that names the old home, through the one
@@ -25225,7 +25505,7 @@ var init_specs2 = __esm({
25225
25505
  * that is moving: another method of the same component, and another
25226
25506
  * component's method of the same name, stay exactly where they point.
25227
25507
  */
25228
- repointToNewHome(from, to, methods, plan) {
25508
+ repointToNewHome(from, to, methods, plan2) {
25229
25509
  const moved = new Set(methods);
25230
25510
  let repointed = 0;
25231
25511
  const remap = (ref, position, owner) => {
@@ -25234,21 +25514,21 @@ var init_specs2 = __esm({
25234
25514
  return to;
25235
25515
  };
25236
25516
  for (const { kind, id, spec } of this.everySpecInTree()) {
25237
- const staged = plan.edits.get(`${kind}:${id}`);
25517
+ const staged = plan2.edits.get(`${kind}:${id}`);
25238
25518
  const subject = staged ? staged.after : cloneSpec(spec);
25239
25519
  if (!rewriteSpecRefs(subject, remap)) continue;
25240
25520
  if (staged) continue;
25241
25521
  if (id.includes("::")) {
25242
- plan.notices.push(`"${id}" lives in a chained subproject and still names "${from}" \u2014 re-point it from that project's own root.`);
25522
+ plan2.notices.push(`"${id}" lives in a chained subproject and still names "${from}" \u2014 re-point it from that project's own root.`);
25243
25523
  repointed -= 1;
25244
25524
  continue;
25245
25525
  }
25246
- this.stageMoveEdit(plan, kind, id, spec, subject);
25526
+ this.stageMoveEdit(plan2, kind, id, spec, subject);
25247
25527
  }
25248
- for (const edit of plan.edits.values()) {
25528
+ for (const edit of plan2.edits.values()) {
25249
25529
  if (edit.before === null) rewriteSpecRefs(edit.after, remap);
25250
25530
  }
25251
- plan.notices.push(...this.callersOwingADependency(to, plan));
25531
+ plan2.notices.push(...this.callersOwingADependency(to, plan2));
25252
25532
  return repointed;
25253
25533
  }
25254
25534
  /**
@@ -25257,13 +25537,13 @@ var init_specs2 = __esm({
25257
25537
  * list, because a dependency nobody judged is exactly what the gate exists to
25258
25538
  * prevent.
25259
25539
  */
25260
- callersOwingADependency(to, plan) {
25540
+ callersOwingADependency(to, plan2) {
25261
25541
  const contracts = this.loadInterfaceSpecs();
25262
25542
  const owing = /* @__PURE__ */ new Set();
25263
- for (const edit of plan.edits.values()) {
25543
+ for (const edit of plan2.edits.values()) {
25264
25544
  const owner = edit.kind === "component" ? edit.id : contracts.find((i) => i.id === edit.after.contract)?.component;
25265
25545
  if (!owner || owner === to) continue;
25266
- const component = owner === plan.target.id ? plan.target : this.loadComponentSpec(owner);
25546
+ const component = owner === plan2.target.id ? plan2.target : this.loadComponentSpec(owner);
25267
25547
  if (!component) continue;
25268
25548
  const held = /* @__PURE__ */ new Set([...component.dependsOn ?? [], ...component.owns ?? []]);
25269
25549
  if (!held.has(to)) owing.add(owner);
@@ -25278,11 +25558,11 @@ var init_specs2 = __esm({
25278
25558
  * because the gate holds the judgement. `assess` answers rather than throws,
25279
25559
  * so this is a search and not a sequence of caught exceptions.
25280
25560
  */
25281
- rankMoveHomes(hooks, from, to, plan) {
25561
+ rankMoveHomes(hooks, from, to, plan2) {
25282
25562
  const limit = this.dependencyLimit();
25283
- const pool = this.loadComponentSpecs().filter((c) => c.subsystem === plan.source.subsystem && c.id !== from);
25284
- if (!pool.some((c) => c.id === to)) pool.push(plan.target);
25285
- return pool.map((candidate) => weighMoveHome(hooks, candidate, plan.reach, limit)).sort((a, b) => Number(b.legal) - Number(a.legal) || a.newDependencies.length - b.newDependencies.length || a.component.localeCompare(b.component));
25563
+ const pool = this.loadComponentSpecs().filter((c) => c.subsystem === plan2.source.subsystem && c.id !== from);
25564
+ if (!pool.some((c) => c.id === to)) pool.push(plan2.target);
25565
+ return pool.map((candidate) => weighMoveHome(hooks, candidate, plan2.reach, limit)).sort((a, b) => Number(b.legal) - Number(a.legal) || a.newDependencies.length - b.newDependencies.length || a.component.localeCompare(b.component));
25286
25566
  }
25287
25567
  /**
25288
25568
  * The project's `maxComponentDependencies`, so a candidate's dependency count
@@ -25312,8 +25592,8 @@ var init_specs2 = __esm({
25312
25592
  * receiving contract whose moved methods keep their wire address. A move
25313
25593
  * never edits prose and never re-addresses an RPC.
25314
25594
  */
25315
- moveMentions(from, plan) {
25316
- const mentions = new Set(plan.wireBound);
25595
+ moveMentions(from, plan2) {
25596
+ const mentions = new Set(plan2.wireBound);
25317
25597
  const named = new RegExp(`(?<![A-Za-z0-9_])${from}(?![A-Za-z0-9_])`);
25318
25598
  for (const { kind, id, spec } of this.everySpecInTree()) {
25319
25599
  if (namesInProse(spec, named)) mentions.add(kind === "system" ? "system" : id);
@@ -25333,9 +25613,9 @@ var init_specs2 = __esm({
25333
25613
  const reports = moveChangeReports(edits, false);
25334
25614
  const written = [];
25335
25615
  try {
25336
- for (const report2 of reports) {
25337
- const edit = edits.find((e) => e.kind === report2.kind && e.id === report2.id);
25338
- report2.notices.push(...this.saveMovedSpec(edit.kind, edit.after));
25616
+ for (const report3 of reports) {
25617
+ const edit = edits.find((e) => e.kind === report3.kind && e.id === report3.id);
25618
+ report3.notices.push(...this.saveMovedSpec(edit.kind, edit.after));
25339
25619
  written.push(edit);
25340
25620
  }
25341
25621
  finish?.();
@@ -26107,11 +26387,11 @@ function renameMethod(componentId, methodName, newName, pinSymbol) {
26107
26387
  if (!movingContracts.has(implementation.contract)) continue;
26108
26388
  const realization = implementation.methods.find((m) => m.name === methodName);
26109
26389
  if (!realization) continue;
26110
- const pin = realization.symbol === void 0 && pinSymbol !== false;
26111
- if (pin) pinnedSymbol = methodName;
26390
+ const pin2 = realization.symbol === void 0 && pinSymbol !== false;
26391
+ if (pin2) pinnedSymbol = methodName;
26112
26392
  saveImplementationSpec({
26113
26393
  ...implementation,
26114
- methods: implementation.methods.map((m) => m.name === methodName ? { ...m, name: newName, ...pin ? { symbol: methodName } : {} } : m)
26394
+ methods: implementation.methods.map((m) => m.name === methodName ? { ...m, name: newName, ...pin2 ? { symbol: methodName } : {} } : m)
26115
26395
  });
26116
26396
  renamed.push(implementation.id);
26117
26397
  }
@@ -27206,14 +27486,14 @@ var init_custom = __esm({
27206
27486
  }
27207
27487
  export(ctx) {
27208
27488
  const { agent, target, renderedInstructions } = ctx;
27209
- const label2 = "label" in target ? target.label : "Custom";
27489
+ const label3 = "label" in target ? target.label : "Custom";
27210
27490
  const filePath = this.outputPath(ctx);
27211
27491
  const content = [
27212
27492
  "---",
27213
27493
  `name: ${agent.name}`,
27214
27494
  `description: ${agent.description}`,
27215
27495
  `id: ${agent.id}`,
27216
- `target: ${label2}`,
27496
+ `target: ${label3}`,
27217
27497
  "---",
27218
27498
  "",
27219
27499
  renderedInstructions,
@@ -30410,7 +30690,7 @@ var require_dist = __commonJS({
30410
30690
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
30411
30691
  }
30412
30692
  var fs60 = __toESM2(require("fs"));
30413
- var path76 = __toESM2(require("path"));
30693
+ var path77 = __toESM2(require("path"));
30414
30694
  var import_fflate = require_node();
30415
30695
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".hg", ".svn"]);
30416
30696
  function listEntries(archive) {
@@ -30448,8 +30728,8 @@ var require_dist = __commonJS({
30448
30728
  }
30449
30729
  function writeTree(destDir, files) {
30450
30730
  for (const file of files) {
30451
- const absolute = path76.join(destDir, file.path);
30452
- fs60.mkdirSync(path76.dirname(absolute), { recursive: true });
30731
+ const absolute = path77.join(destDir, file.path);
30732
+ fs60.mkdirSync(path77.dirname(absolute), { recursive: true });
30453
30733
  fs60.writeFileSync(absolute, file.contents);
30454
30734
  }
30455
30735
  }
@@ -30462,10 +30742,10 @@ var require_dist = __commonJS({
30462
30742
  for (const entry of fs60.readdirSync(current2, { withFileTypes: true })) {
30463
30743
  if (entry.isDirectory()) {
30464
30744
  if (SKIP_DIRS.has(entry.name)) continue;
30465
- walkPackDir(root, path76.join(current2, entry.name), out);
30745
+ walkPackDir(root, path77.join(current2, entry.name), out);
30466
30746
  } else if (entry.isFile()) {
30467
- const absolute = path76.join(current2, entry.name);
30468
- const relative22 = path76.relative(root, absolute).split(path76.sep).join("/");
30747
+ const absolute = path77.join(current2, entry.name);
30748
+ const relative22 = path77.relative(root, absolute).split(path77.sep).join("/");
30469
30749
  out.push({ path: relative22, contents: fs60.readFileSync(absolute) });
30470
30750
  }
30471
30751
  }
@@ -30561,9 +30841,9 @@ var require_dist = __commonJS({
30561
30841
  function extractPack3(archiveBytes, destDir, limits) {
30562
30842
  const effectiveLimits = limits === void 0 ? defaultLimits() : limits;
30563
30843
  const entries = listEntries(archiveBytes);
30564
- const plan = planExtraction(entries, effectiveLimits);
30844
+ const plan2 = planExtraction(entries, effectiveLimits);
30565
30845
  const files = [];
30566
- for (const approvedPath of plan.paths) {
30846
+ for (const approvedPath of plan2.paths) {
30567
30847
  const contents = inflateEntry(archiveBytes, approvedPath);
30568
30848
  files.push({ path: approvedPath, contents });
30569
30849
  }
@@ -30636,12 +30916,12 @@ var require_dist = __commonJS({
30636
30916
  function extractTreeArchive3(archiveBytes, destDir, limits, refuseExecutableEntries) {
30637
30917
  const effectiveLimits = limits === void 0 ? defaultTreeLimits() : limits;
30638
30918
  const entries = listEntries(archiveBytes);
30639
- const plan = planTreeExtraction(entries, effectiveLimits);
30919
+ const plan2 = planTreeExtraction(entries, effectiveLimits);
30640
30920
  if (refuseExecutableEntries === true) {
30641
- assertPortableTreePaths(plan.paths);
30921
+ assertPortableTreePaths(plan2.paths);
30642
30922
  }
30643
30923
  const files = [];
30644
- for (const approvedPath of plan.paths) {
30924
+ for (const approvedPath of plan2.paths) {
30645
30925
  const contents = inflateEntry(archiveBytes, approvedPath);
30646
30926
  files.push({ path: approvedPath, contents });
30647
30927
  }
@@ -30899,7 +31179,7 @@ var init_treetransfer = __esm({
30899
31179
  });
30900
31180
 
30901
31181
  // src/core/stereotype-migration.ts
30902
- function retireSpecialists(apply) {
31182
+ function retireSpecialists(apply2) {
30903
31183
  const components = loadComponentSpecs();
30904
31184
  const byId = new Map(components.map((c) => [c.id, c]));
30905
31185
  const specialists = components.filter((c) => c.componentType === "Specialist" && !c.id.includes("::")).sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
@@ -30986,7 +31266,7 @@ function retireSpecialists(apply) {
30986
31266
  reason: reasons.get(specialist.id)
30987
31267
  };
30988
31268
  });
30989
- if (!apply) return { retyped, rebasedVariants: specialistVariants, applied: false };
31269
+ if (!apply2) return { retyped, rebasedVariants: specialistVariants, applied: false };
30990
31270
  for (const retype of retyped) {
30991
31271
  const orchestrator = { ...byId.get(retype.component), componentType: "Orchestrator" };
30992
31272
  delete orchestrator.dependencyClass;
@@ -31008,7 +31288,7 @@ var init_stereotype_migration = __esm({
31008
31288
  });
31009
31289
 
31010
31290
  // src/core/narrative-repair.ts
31011
- function repairForeignStepFields(apply) {
31291
+ function repairForeignStepFields(apply2) {
31012
31292
  const implementations = loadImplementationSpecs();
31013
31293
  const repairs = [];
31014
31294
  const changed = [];
@@ -31031,7 +31311,7 @@ function repairForeignStepFields(apply) {
31031
31311
  }
31032
31312
  if (touched) changed.push(impl);
31033
31313
  }
31034
- if (apply && changed.length) {
31314
+ if (apply2 && changed.length) {
31035
31315
  for (const impl of changed) saveImplementationSpec(impl);
31036
31316
  invalidateSpecCache();
31037
31317
  }
@@ -31047,8 +31327,8 @@ var init_narrative_repair = __esm({
31047
31327
 
31048
31328
  // src/models/lock.ts
31049
31329
  function describeApprover(who) {
31050
- const label2 = who.name ? `${who.name} (${who.id})` : who.id;
31051
- return who.source === "hosted" ? `${label2} [authenticated]` : label2;
31330
+ const label3 = who.name ? `${who.name} (${who.id})` : who.id;
31331
+ return who.source === "hosted" ? `${label3} [authenticated]` : label3;
31052
31332
  }
31053
31333
  var init_lock = __esm({
31054
31334
  "src/models/lock.ts"() {
@@ -32244,9 +32524,9 @@ function statusForCreate(restatement, existing) {
32244
32524
  const held = stored !== void 0 && STATUS_ORDER.includes(stored) ? stored : void 0;
32245
32525
  if (stated === void 0) return { status: held ?? "draft" };
32246
32526
  if (held === void 0 || STATUS_ORDER.indexOf(stated) >= STATUS_ORDER.indexOf(held)) return { status: stated };
32247
- const label2 = `${restatement.kind} "${restatement.spec.id}"`;
32527
+ const label3 = `${restatement.kind} "${restatement.spec.id}"`;
32248
32528
  return {
32249
- refusal: `Refusing to re-author ${label2} at status "${stated}": it is stored at "${held}", and a create tool never lowers a spec's status \u2014 a restatement that reopened a frozen spec would undo a lock without saying so. Nothing was written. To reopen it deliberately, use sdd_update_spec with {"status": "${stated}"}; to keep the level it has, leave status out.`
32529
+ refusal: `Refusing to re-author ${label3} at status "${stated}": it is stored at "${held}", and a create tool never lowers a spec's status \u2014 a restatement that reopened a frozen spec would undo a lock without saying so. Nothing was written. To reopen it deliberately, use sdd_update_spec with {"status": "${stated}"}; to keep the level it has, leave status out.`
32250
32530
  };
32251
32531
  }
32252
32532
  function carryInto(restatement, existing, candidate) {
@@ -32430,17 +32710,17 @@ function updateSpecGated(kind, id, delta, dryRun) {
32430
32710
  const testRoots = bound2.rules?.conformance?.testRoots ?? [];
32431
32711
  const stored = loadSpec(kind, id);
32432
32712
  const gate = componentCandidateGate(bound2, stored?.subsystem);
32433
- const report2 = updateSpec(kind, id, delta, gate, dryRun);
32434
- const changed = changedMethods(report2);
32713
+ const report3 = updateSpec(kind, id, delta, gate, dryRun);
32714
+ const changed = changedMethods(report3);
32435
32715
  if (changed.length > 0 && testRoots.length > 0) {
32436
32716
  const written = Array.isArray(delta?.methods) ? delta.methods : [];
32437
- report2.testsToRevisit = testsInvalidatedBy(kind, id, changed, [stored], bound2, written);
32717
+ report3.testsToRevisit = testsInvalidatedBy(kind, id, changed, [stored], bound2, written);
32438
32718
  }
32439
- return report2;
32719
+ return report3;
32440
32720
  }
32441
- function changedMethods(report2) {
32721
+ function changedMethods(report3) {
32442
32722
  const named = /* @__PURE__ */ new Set();
32443
- for (const change of report2.changes) {
32723
+ for (const change of report3.changes) {
32444
32724
  const [field, name] = change.path.split(".");
32445
32725
  if (field === "methods" && name) named.add(name);
32446
32726
  }
@@ -32725,18 +33005,18 @@ function structured(content, data) {
32725
33005
  function jsonStructured(value) {
32726
33006
  return structured(JSON.stringify(value, null, 2), value);
32727
33007
  }
32728
- function renderChangeReport(report2) {
32729
- const lines = [report2.dryRun ? `DRY RUN \u2014 nothing was written. ${report2.summary}` : report2.summary];
32730
- for (const change of report2.changes) {
33008
+ function renderChangeReport(report3) {
33009
+ const lines = [report3.dryRun ? `DRY RUN \u2014 nothing was written. ${report3.summary}` : report3.summary];
33010
+ for (const change of report3.changes) {
32731
33011
  const to = change.after === void 0 ? "" : `: ${JSON.stringify(change.after)}`;
32732
33012
  const from = change.before === void 0 ? "" : ` (was ${JSON.stringify(change.before)})`;
32733
- lines.push(`- ${change.path} ${report2.dryRun ? `would be ${change.change}` : change.change}${to}${from}`);
33013
+ lines.push(`- ${change.path} ${report3.dryRun ? `would be ${change.change}` : change.change}${to}${from}`);
32734
33014
  }
32735
- if (report2.ineffective.length) {
32736
- lines.push("", "NO EFFECT:", ...report2.ineffective.map((i) => `- ${i}`));
33015
+ if (report3.ineffective.length) {
33016
+ lines.push("", "NO EFFECT:", ...report3.ineffective.map((i) => `- ${i}`));
32737
33017
  }
32738
- if (report2.notices.length) lines.push("", "NOTICE:", ...report2.notices.map((n) => `- ${n}`));
32739
- lines.push(...renderTestsToRevisit(report2.testsToRevisit));
33018
+ if (report3.notices.length) lines.push("", "NOTICE:", ...report3.notices.map((n) => `- ${n}`));
33019
+ lines.push(...renderTestsToRevisit(report3.testsToRevisit));
32740
33020
  return lines.join("\n");
32741
33021
  }
32742
33022
  function renderTestsToRevisit(entries) {
@@ -33251,11 +33531,11 @@ function createMcpServer(options = {}) {
33251
33531
  if (!sub) return errText(`Subsystem "${subsystem}" does not exist.`);
33252
33532
  const delta = publicInterfacesReplacement(subsystem, sub.publicInterfaces ?? [], publicInterfaces);
33253
33533
  if (typeof delta === "string") return errText(delta);
33254
- const report2 = updateSpecGated("subsystem", subsystem, delta);
33534
+ const report3 = updateSpecGated("subsystem", subsystem, delta);
33255
33535
  return structured(
33256
33536
  `Updated public interfaces for subsystem "${subsystem}" (${publicInterfaces.length} ${publicInterfaces.length === 1 ? "entry" : "entries"}).
33257
- ${renderChangeReport(report2)}`,
33258
- report2
33537
+ ${renderChangeReport(report3)}`,
33538
+ report3
33259
33539
  );
33260
33540
  } catch (e) {
33261
33541
  return errMessage(e);
@@ -33276,11 +33556,11 @@ ${renderChangeReport(report2)}`,
33276
33556
  ({ subsystem, projectPath }) => {
33277
33557
  try {
33278
33558
  const delta = projectPath ? { projectPath } : { unset: ["projectPath"] };
33279
- const report2 = updateSpecGated("subsystem", subsystem, delta);
33559
+ const report3 = updateSpecGated("subsystem", subsystem, delta);
33280
33560
  return structured(
33281
33561
  `Updated projectPath for subsystem "${subsystem}" to: ${projectPath || "none (cleared)"}
33282
- ${renderChangeReport(report2)}`,
33283
- report2
33562
+ ${renderChangeReport(report3)}`,
33563
+ report3
33284
33564
  );
33285
33565
  } catch (e) {
33286
33566
  return errMessage(e);
@@ -33614,11 +33894,11 @@ ${renderChangeReport(report2)}`,
33614
33894
  methods.push({ name: e.method, endpoint: parsed.data });
33615
33895
  bound2.push(`${e.method}\u2192${e.transport}`);
33616
33896
  }
33617
- const report2 = updateSpecGated("interface", interfaceId, { methods });
33897
+ const report3 = updateSpecGated("interface", interfaceId, { methods });
33618
33898
  return structured(
33619
33899
  `Bound ${bound2.length} endpoint(s) on "${interfaceId}": ${bound2.join(", ")}.
33620
- ${renderChangeReport(report2)}`,
33621
- report2
33900
+ ${renderChangeReport(report3)}`,
33901
+ report3
33622
33902
  );
33623
33903
  } catch (e) {
33624
33904
  return errMessage(e);
@@ -33960,8 +34240,8 @@ ${testsBlock}` : ""}`, deletion);
33960
34240
  },
33961
34241
  ({ kind, id, delta, dryRun }) => {
33962
34242
  try {
33963
- const report2 = updateSpecGated(kind, id, delta, dryRun);
33964
- return structured(renderChangeReport(report2), report2);
34243
+ const report3 = updateSpecGated(kind, id, delta, dryRun);
34244
+ return structured(renderChangeReport(report3), report3);
33965
34245
  } catch (e) {
33966
34246
  return errText(String(e));
33967
34247
  }
@@ -33979,13 +34259,13 @@ ${testsBlock}` : ""}`, deletion);
33979
34259
  },
33980
34260
  ({ subsystem, recursive }) => {
33981
34261
  try {
33982
- const report2 = getStatusReport({
34262
+ const report3 = getStatusReport({
33983
34263
  subsystem,
33984
34264
  recursive: recursive ?? true
33985
34265
  });
33986
34266
  const family = statusFamilyContext();
33987
34267
  const verdict = approvalVerdict();
33988
- return text(`${family}${report2.text}${verdict.text}`);
34268
+ return text(`${family}${report3.text}${verdict.text}`);
33989
34269
  } catch (e) {
33990
34270
  return errText(String(e));
33991
34271
  }
@@ -34662,18 +34942,18 @@ async function runMcpStatus() {
34662
34942
  { label: "Antigravity (project)", filePath: geminiProject, fallbackName: "settings.json" },
34663
34943
  { label: "Antigravity (global)", filePath: geminiGlobal, fallbackName: "mcp_config.json" }
34664
34944
  ];
34665
- for (const { label: label2, filePath, fallbackName } of checks) {
34945
+ for (const { label: label3, filePath, fallbackName } of checks) {
34666
34946
  if (!fs24.existsSync(filePath)) {
34667
- console.log(` ${label2}: ${import_chalk3.default.gray(`${fallbackName} not found`)}`);
34947
+ console.log(` ${label3}: ${import_chalk3.default.gray(`${fallbackName} not found`)}`);
34668
34948
  continue;
34669
34949
  }
34670
34950
  try {
34671
34951
  const s = JSON.parse(fs24.readFileSync(filePath, "utf8"));
34672
34952
  const registered = !!s["mcpServers"]?.["wairon"];
34673
34953
  const mark = registered ? import_chalk3.default.green("\u2713 registered") : import_chalk3.default.gray("not registered");
34674
- console.log(` ${label2}: ${mark} ${import_chalk3.default.gray(filePath)}`);
34954
+ console.log(` ${label3}: ${mark} ${import_chalk3.default.gray(filePath)}`);
34675
34955
  } catch {
34676
- console.log(` ${label2}: ${import_chalk3.default.red("parse error")} ${import_chalk3.default.gray(filePath)}`);
34956
+ console.log(` ${label3}: ${import_chalk3.default.red("parse error")} ${import_chalk3.default.gray(filePath)}`);
34677
34957
  }
34678
34958
  }
34679
34959
  logger.blank();
@@ -34732,7 +35012,7 @@ var init_mcp = __esm({
34732
35012
  });
34733
35013
 
34734
35014
  // src/cli/index.ts
34735
- var path75 = __toESM(require("path"));
35015
+ var path76 = __toESM(require("path"));
34736
35016
  var import_commander = require("commander");
34737
35017
  init_defaults();
34738
35018
  init_logger();
@@ -36015,14 +36295,14 @@ var http = __toESM(require("http"));
36015
36295
  var https = __toESM(require("https"));
36016
36296
  init_defaults();
36017
36297
  function downloadFile(url, dest) {
36018
- return new Promise((resolve36, reject) => {
36298
+ return new Promise((resolve37, reject) => {
36019
36299
  const file = fs26.createWriteStream(dest);
36020
36300
  const get4 = url.startsWith("https://") ? https.get : http.get;
36021
36301
  get4(url, { headers: { "User-Agent": `wairon/${WAIRON_VERSION}` }, agent: false }, (res) => {
36022
36302
  if (res.statusCode === 301 || res.statusCode === 302) {
36023
36303
  file.close();
36024
36304
  res.destroy();
36025
- downloadFile(res.headers.location, dest).then(resolve36).catch(reject);
36305
+ downloadFile(res.headers.location, dest).then(resolve37).catch(reject);
36026
36306
  return;
36027
36307
  }
36028
36308
  if (res.statusCode !== 200) {
@@ -36034,7 +36314,7 @@ function downloadFile(url, dest) {
36034
36314
  res.pipe(file);
36035
36315
  file.on("finish", () => {
36036
36316
  res.destroy();
36037
- file.close(() => resolve36());
36317
+ file.close(() => resolve37());
36038
36318
  });
36039
36319
  file.on("error", (err) => {
36040
36320
  res.destroy();
@@ -36086,8 +36366,8 @@ async function runUpdate(options = {}) {
36086
36366
  return;
36087
36367
  }
36088
36368
  const latestVersion = release.tag_name.replace(/^v/, "");
36089
- const label2 = releaseChannelLabel(release.tag_name);
36090
- const channelLabel = label2 === "stable" ? "" : ` [${label2}]`;
36369
+ const label3 = releaseChannelLabel(release.tag_name);
36370
+ const channelLabel = label3 === "stable" ? "" : ` [${label3}]`;
36091
36371
  logger.info(`New version available: ${release.tag_name}${channelLabel} (current: ${WAIRON_VERSION})`);
36092
36372
  if (release.body) {
36093
36373
  logger.blank();
@@ -36176,19 +36456,19 @@ function releaseTier(tag, githubPrerelease = false) {
36176
36456
  if (dash === -1) {
36177
36457
  return githubPrerelease ? CHANNEL_RANK.dev : CHANNEL_RANK.stable;
36178
36458
  }
36179
- const label2 = version.slice(dash + 1).replace(/\.\d+$/, "");
36180
- const rank = CHANNEL_RANK[label2];
36459
+ const label3 = version.slice(dash + 1).replace(/\.\d+$/, "");
36460
+ const rank = CHANNEL_RANK[label3];
36181
36461
  return rank === void 0 || rank === CHANNEL_RANK.stable ? CHANNEL_RANK.dev : rank;
36182
36462
  }
36183
36463
  function reportOffChannelBuild(channel, currentVersion, newestOnChannel) {
36184
36464
  const tier = releaseTier(currentVersion);
36185
36465
  if (tier <= (CHANNEL_RANK[channel] ?? CHANNEL_RANK.stable)) return;
36186
- const label2 = releaseChannelLabel(currentVersion);
36466
+ const label3 = releaseChannelLabel(currentVersion);
36187
36467
  logger.warn(
36188
- `This is a ${label2} build (${WAIRON_VERSION}) but the update channel is ${channel}.`
36468
+ `This is a ${label3} build (${WAIRON_VERSION}) but the update channel is ${channel}.`
36189
36469
  );
36190
36470
  const target = newestOnChannel ? ` The newest ${channel} release is ${newestOnChannel}.` : "";
36191
- const track = isUpdateChannel(label2) ? `Track this build with \`wairon update --channel ${label2}\`, or reinstall` : "Reinstall";
36471
+ const track = isUpdateChannel(label3) ? `Track this build with \`wairon update --channel ${label3}\`, or reinstall` : "Reinstall";
36192
36472
  logger.info(`It will not update until ${channel} passes it.${target} ${track} to return to ${channel}.`);
36193
36473
  logger.blank();
36194
36474
  }
@@ -36197,9 +36477,9 @@ function reportWiderChannel(releases, channel, currentVersion) {
36197
36477
  (r) => !isEligibleForChannel(r.tag_name, channel, r.prerelease) && isNewer(currentVersion, r.tag_name.replace(/^v/, ""))
36198
36478
  );
36199
36479
  if (!wider) return;
36200
- const label2 = releaseChannelLabel(wider.tag_name);
36201
- const hint = isUpdateChannel(label2) ? ` \u2014 install it with \`wairon update --channel ${label2}\`.` : ".";
36202
- logger.info(`A newer pre-release exists: ${wider.tag_name} [${label2}]${hint}`);
36480
+ const label3 = releaseChannelLabel(wider.tag_name);
36481
+ const hint = isUpdateChannel(label3) ? ` \u2014 install it with \`wairon update --channel ${label3}\`.` : ".";
36482
+ logger.info(`A newer pre-release exists: ${wider.tag_name} [${label3}]${hint}`);
36203
36483
  }
36204
36484
  function isEligibleForChannel(tag, channel, githubPrerelease = false) {
36205
36485
  return releaseTier(tag, githubPrerelease) <= (CHANNEL_RANK[channel] ?? CHANNEL_RANK.stable);
@@ -36211,7 +36491,7 @@ function releaseChannelLabel(tag) {
36211
36491
  return version.slice(dash + 1).replace(/\.\d+$/, "") || "stable";
36212
36492
  }
36213
36493
  function fetchReleases(repo) {
36214
- return new Promise((resolve36, reject) => {
36494
+ return new Promise((resolve37, reject) => {
36215
36495
  const url = `https://api.github.com/repos/${repo}/releases?per_page=100`;
36216
36496
  const options = {
36217
36497
  headers: {
@@ -36231,7 +36511,7 @@ function fetchReleases(repo) {
36231
36511
  return;
36232
36512
  }
36233
36513
  try {
36234
- resolve36(JSON.parse(data));
36514
+ resolve37(JSON.parse(data));
36235
36515
  } catch {
36236
36516
  reject(new Error("Failed to parse GitHub API response"));
36237
36517
  }
@@ -36375,13 +36655,13 @@ function withoutTrailingNewline(text2) {
36375
36655
  }
36376
36656
  async function runStatus(options = {}) {
36377
36657
  assertProjectInitialized();
36378
- const report2 = getStatusReport(options, TERMINAL_DECOR);
36379
- if (report2.failed) {
36380
- refuseUnreadableTree(report2.text);
36658
+ const report3 = getStatusReport(options, TERMINAL_DECOR);
36659
+ if (report3.failed) {
36660
+ refuseUnreadableTree(report3.text);
36381
36661
  }
36382
36662
  logger.header("Architecture Status Dashboard");
36383
36663
  logger.blank();
36384
- console.log(withoutTrailingNewline(report2.text));
36664
+ console.log(withoutTrailingNewline(report3.text));
36385
36665
  const lock = approvalVerdict();
36386
36666
  if (lock.text.trim()) {
36387
36667
  logger.blank();
@@ -36458,7 +36738,7 @@ async function filteredCheckbox(config) {
36458
36738
  32,
36459
36739
  Math.max(...items.map((i) => i.label.length))
36460
36740
  );
36461
- return new Promise((resolve36) => {
36741
+ return new Promise((resolve37) => {
36462
36742
  const checked = /* @__PURE__ */ new Set();
36463
36743
  let cursor = 0;
36464
36744
  let filterIdx = 0;
@@ -36525,7 +36805,7 @@ async function filteredCheckbox(config) {
36525
36805
  );
36526
36806
  teardown();
36527
36807
  const result = items.filter((_, idx) => checked.has(idx)).map((i) => i.value);
36528
- resolve36(result);
36808
+ resolve37(result);
36529
36809
  }
36530
36810
  function abort() {
36531
36811
  process.stdout.write("\n");
@@ -36797,8 +37077,9 @@ async function runSkillsInstall() {
36797
37077
  // src/commands/doctor.ts
36798
37078
  var fs28 = __toESM(require("fs"));
36799
37079
  var os9 = __toESM(require("os"));
36800
- var path42 = __toESM(require("path"));
37080
+ var path43 = __toESM(require("path"));
36801
37081
  var import_chalk12 = __toESM(require("chalk"));
37082
+ var import_inquirer4 = __toESM(require("inquirer"));
36802
37083
  init_logger();
36803
37084
  init_defaults();
36804
37085
  init_paths();
@@ -36807,6 +37088,356 @@ init_fs();
36807
37088
  init_project();
36808
37089
  init_lock();
36809
37090
  init_mcp();
37091
+
37092
+ // src/commands/chaining-migration.ts
37093
+ var path42 = __toESM(require("path"));
37094
+
37095
+ // src/commands/adapters/surfaces.ts
37096
+ init_surface_portal();
37097
+
37098
+ // src/commands/chaining-migration.ts
37099
+ init_fs();
37100
+ init_paths();
37101
+ init_errors();
37102
+ init_project();
37103
+ init_project_family();
37104
+ init_exports();
37105
+ function isEmpty(migration) {
37106
+ return migration.projects.every((p) => !hasWrites(p));
37107
+ }
37108
+ function blocked(migration) {
37109
+ return migration.findings.some((f) => f.blocking);
37110
+ }
37111
+ function hasWrites(p) {
37112
+ return p.idToWrite !== void 0 || p.exports.length > 0 || p.externals.length > 0 || p.pins.length > 0;
37113
+ }
37114
+ function plan() {
37115
+ const bound2 = getProjectRoot();
37116
+ const climbed = climb2(bound2);
37117
+ return runWithProjectRoot(climbed.top, () => planFamily(climbed.top, climbed.whole));
37118
+ }
37119
+ function climb2(start2) {
37120
+ let current2 = start2;
37121
+ let parent;
37122
+ do {
37123
+ parent = runWithProjectRoot(current2, () => resolveChainingParent());
37124
+ if (parent) current2 = parent.parentRoot;
37125
+ } while (parent);
37126
+ return { top: current2, whole: !parentWithheld() };
37127
+ }
37128
+ function parentWithheld() {
37129
+ const reach2 = getRequestParentReach();
37130
+ return reach2 !== null && !reach2.parentReach;
37131
+ }
37132
+ function planFamily(top, whole) {
37133
+ const ctx = { family: graph(), projects: /* @__PURE__ */ new Map(), findings: [], absent: /* @__PURE__ */ new Map() };
37134
+ if (!whole) {
37135
+ ctx.findings.push({
37136
+ kind: "family-partial",
37137
+ project: "",
37138
+ blocking: true,
37139
+ detail: "the climb stopped at a hop out of reach, so a producer above it could need writes this plan cannot see \u2014 run the migration from the family's top root"
37140
+ });
37141
+ }
37142
+ decideIds(ctx);
37143
+ planCrossings(ctx);
37144
+ convertLegacyPins(ctx);
37145
+ return settle2(ctx, top, whole);
37146
+ }
37147
+ function entryOf(ctx, node) {
37148
+ let entry = ctx.projects.get(node.namespace);
37149
+ if (!entry) {
37150
+ entry = { project: node.namespace, directory: node.directory, exports: [], externals: [], pins: [], supersededPins: [] };
37151
+ ctx.projects.set(node.namespace, entry);
37152
+ }
37153
+ return entry;
37154
+ }
37155
+ function idOf(ctx, namespace) {
37156
+ return ctx.projects.get(namespace)?.id;
37157
+ }
37158
+ var label2 = (namespace) => namespace === "" ? "the top root" : `"${namespace}"`;
37159
+ function decideIds(ctx) {
37160
+ for (const node of ctx.family.nodes) {
37161
+ const approved = approvalRecord(node.directory)?.projectId;
37162
+ decideId(ctx, node, approved);
37163
+ }
37164
+ checkPlannedIds(ctx);
37165
+ }
37166
+ function decideId(ctx, node, approved) {
37167
+ const entry = entryOf(ctx, node);
37168
+ const member = node.parent !== void 0;
37169
+ if (node.idSource === "declared") {
37170
+ entry.id = node.id;
37171
+ return;
37172
+ }
37173
+ const planned = approved ?? (member ? node.mountAlias : node.id);
37174
+ if (node.name === void 0) absentProject(ctx, node);
37175
+ if (planned === void 0) {
37176
+ ctx.findings.push({ kind: "id-ambiguous", project: node.namespace, blocking: false, detail: `${label2(node.namespace)} declares no id and its name yields none \u2014 declare one in its project.yaml` });
37177
+ return;
37178
+ }
37179
+ entry.id = planned;
37180
+ entry.idToWrite = planned;
37181
+ if (approved !== void 0 && member && approved !== node.mountAlias) {
37182
+ ctx.findings.push({
37183
+ kind: "id-locked",
37184
+ project: node.namespace,
37185
+ blocking: false,
37186
+ detail: `${label2(node.namespace)} keeps "${approved}", the id its lock approved, rather than its mount's subsystem id "${node.mountAlias}" \u2014 moving it is the rename migration`
37187
+ });
37188
+ }
37189
+ }
37190
+ function absentProject(ctx, node) {
37191
+ const finding = {
37192
+ kind: "project-absent",
37193
+ project: node.namespace,
37194
+ blocking: false,
37195
+ detail: `${label2(node.namespace)} (${node.directory}) has no readable project.yaml \u2014 \`wairon doctor --fix\` writes one for a chained child first`
37196
+ };
37197
+ ctx.findings.push(finding);
37198
+ ctx.absent.set(node.namespace, finding);
37199
+ }
37200
+ function checkPlannedIds(ctx) {
37201
+ const holders = /* @__PURE__ */ new Map();
37202
+ for (const entry of ctx.projects.values()) {
37203
+ if (entry.id !== void 0) holders.set(entry.id, [...holders.get(entry.id) ?? [], entry.project]);
37204
+ }
37205
+ for (const entry of ctx.projects.values()) {
37206
+ if (entry.idToWrite === void 0) continue;
37207
+ const others = (holders.get(entry.idToWrite) ?? []).filter((ns) => ns !== entry.project);
37208
+ const why = !PROJECT_ID_RE.test(entry.idToWrite) ? "breaks the project-id grammar" : others.length > 0 ? `is also the id of ${others.map(label2).join(", ")}` : void 0;
37209
+ if (why === void 0) continue;
37210
+ ctx.findings.push({ kind: "id-ambiguous", project: entry.project, blocking: false, detail: `the id planned for ${label2(entry.project)}, "${entry.idToWrite}", ${why} \u2014 declare one by hand` });
37211
+ delete entry.id;
37212
+ delete entry.idToWrite;
37213
+ }
37214
+ reportOrphanedDeclarations(ctx);
37215
+ }
37216
+ function reportOrphanedDeclarations(ctx) {
37217
+ for (const node of ctx.family.nodes) {
37218
+ for (const external of node.externals) {
37219
+ if (external.sourceKind !== "family" || external.producer === void 0) continue;
37220
+ const next = ctx.projects.get(external.producer)?.idToWrite;
37221
+ if (next === void 0 || next === external.project) continue;
37222
+ ctx.findings.push({
37223
+ kind: "declaration-orphaned",
37224
+ project: node.namespace,
37225
+ blocking: false,
37226
+ detail: `the external "${external.alias}" finds ${label2(external.producer)} by "${external.project}", which it leaves for "${next}" \u2014 re-point the declaration after the migration`
37227
+ });
37228
+ }
37229
+ }
37230
+ }
37231
+ function planCrossings(ctx) {
37232
+ const pairs = /* @__PURE__ */ new Map();
37233
+ for (const ref of ctx.family.references) pairs.set(`${ref.consumer}\0${ref.producer}`, { consumer: ref.consumer, producer: ref.producer });
37234
+ for (const { consumer, producer } of pairs.values()) planPair(ctx, consumer, producer);
37235
+ }
37236
+ function planPair(ctx, consumer, producer) {
37237
+ const consumerNode2 = familyNode(ctx.family, consumer);
37238
+ const producerNode = familyNode(ctx.family, producer);
37239
+ if (!consumerNode2 || !producerNode) return;
37240
+ const usage = exportUsage(consumer, producer);
37241
+ const table = resolveProjectExports(producer || void 0);
37242
+ let entries = false;
37243
+ for (const ref of usage.unexported) entries = planEntry(ctx, producerNode, ref, table) || entries;
37244
+ const alias = declares(ctx.family, consumer, producer) ? declaredAlias(consumerNode2, producer) : planExternal(ctx, consumerNode2, producer, "reference");
37245
+ if (alias !== void 0 && (entries || alias.planned)) addPin(ctx, consumerNode2, alias.alias);
37246
+ }
37247
+ function declaredAlias(consumer, producer) {
37248
+ const external = consumer.externals.find((e) => e.sourceKind === "family" && e.producer === producer);
37249
+ return external ? { alias: external.alias, planned: false } : void 0;
37250
+ }
37251
+ function addPin(ctx, consumer, alias) {
37252
+ const entry = entryOf(ctx, consumer);
37253
+ if (!entry.pins.includes(alias)) entry.pins.push(alias);
37254
+ }
37255
+ function planEntry(ctx, producer, ref, table) {
37256
+ const target = targetOf(ref);
37257
+ if (!target) return report(ctx, "target-unpublished", ref, `${ref.specId} names "${ref.target}", which ${label2(producer.namespace)} does not hold`);
37258
+ if (target.kind === "subsystem") {
37259
+ return report(ctx, "subsystem-reference", ref, `${ref.specId} re-exports ${label2(producer.namespace)}'s subsystem "${target.id}" wholesale \u2014 no L0 entry short of exporting it expresses that; stage 3 rewrites the reference`);
37260
+ }
37261
+ const published = resolveSubsystemExports(target.source);
37262
+ return decideEntry(ctx, producer, ref, target, table, published);
37263
+ }
37264
+ function targetOf(ref) {
37265
+ const component = loadSpec("component", ref.target);
37266
+ if (component) return { kind: "component", id: component.id, source: component.subsystem };
37267
+ const last = ref.target.split("::").pop();
37268
+ for (const candidate of [ref.target, ref.producer ? `${ref.producer}::${last}` : last]) {
37269
+ const type = loadSpec("type", candidate);
37270
+ if (type) return type.subsystem ? { kind: "type", id: type.id, source: type.subsystem } : null;
37271
+ }
37272
+ const subsystem = loadSpec("subsystem", ref.target);
37273
+ return subsystem ? { kind: "subsystem", id: subsystem.id, source: subsystem.id } : null;
37274
+ }
37275
+ function decideEntry(ctx, producer, ref, target, table, published) {
37276
+ const key = targetKey(target);
37277
+ const at = published.entries.filter((e) => exportTargetKey({ ...e, interface: void 0 }) === key);
37278
+ if (at.length === 0) {
37279
+ return report(ctx, "target-unpublished", ref, `"${target.id}" is not in its subsystem's L1 table, so no L0 re-export can follow it \u2014 publish it at L1 first, or the reference is wrong`);
37280
+ }
37281
+ if (table.entries.some((e) => exportTargetKey({ ...e, interface: void 0 }) === key) || !reachableThrough(at, ref)) {
37282
+ return report(ctx, "narrowed", ref, `"${target.id}" is exported under a narrowing that does not declare "${ref.member ?? "what the reference reaches"}" \u2014 widen the narrowing or reach another member`);
37283
+ }
37284
+ const local = localIn(producer.namespace, target.id);
37285
+ const publicName = local.split("::").pop();
37286
+ const taken = table.entries.find((e) => e.publicName === publicName && exportTargetKey({ ...e, interface: void 0 }) !== key) ?? entryOf(ctx, producer).exports.find((e) => e.publicName === publicName && (e.component ?? e.typeDef) !== local);
37287
+ if (taken) {
37288
+ return report(ctx, "name-taken", ref, `"${publicName}" already names another item in ${label2(producer.namespace)} \u2014 export "${target.id}" under a name a person chooses`);
37289
+ }
37290
+ mergeEntry(entryOf(ctx, producer), target, localIn(producer.namespace, target.source), local, publicName, ref);
37291
+ return true;
37292
+ }
37293
+ function reachableThrough(entries, ref) {
37294
+ if (ref.member === void 0 || ref.member.startsWith("capability:")) return true;
37295
+ return entries.some((e) => {
37296
+ if (e.kind !== "component" || e.interface === void 0) return true;
37297
+ const narrowing = loadSpec("interface", e.interface);
37298
+ return narrowing?.methods.some((m) => m.name === ref.member) ?? false;
37299
+ });
37300
+ }
37301
+ var targetKey = (t) => t.kind === "type" ? `type:${t.id}` : `component:${t.id}`;
37302
+ function localIn(namespace, id) {
37303
+ return namespace && id.startsWith(`${namespace}::`) ? id.slice(namespace.length + 2) : id;
37304
+ }
37305
+ function mergeEntry(entry, target, from, item, publicName, ref) {
37306
+ let planned = entry.exports.find((e) => e.from === from && (target.kind === "type" ? e.typeDef : e.component) === item);
37307
+ if (!planned) {
37308
+ planned = { from, ...target.kind === "type" ? { typeDef: item } : { component: item }, publicName, audience: "project", consumers: [], members: [] };
37309
+ entry.exports.push(planned);
37310
+ }
37311
+ addSorted(planned.consumers, ref.consumer);
37312
+ const member = target.kind === "type" ? "type" : ref.member;
37313
+ if (member !== void 0) addSorted(planned.members, member);
37314
+ }
37315
+ function addSorted(list2, value) {
37316
+ if (!list2.includes(value)) {
37317
+ list2.push(value);
37318
+ list2.sort();
37319
+ }
37320
+ }
37321
+ function report(ctx, kind, ref, detail) {
37322
+ ctx.findings.push({ kind, project: ref.producer, detail, reference: ref, blocking: false });
37323
+ return false;
37324
+ }
37325
+ function planExternal(ctx, consumer, producer, reason) {
37326
+ const alias = idOf(ctx, producer);
37327
+ const refuse = (kind, detail) => {
37328
+ ctx.findings.push({ kind, project: consumer.namespace, blocking: false, detail });
37329
+ return void 0;
37330
+ };
37331
+ if (alias === void 0) return refuse("alias-invalid", `${label2(producer)} has no id to declare it under \u2014 declare its id first`);
37332
+ if (!EXTERNAL_ALIAS_RE.test(alias)) return refuse("alias-invalid", `"${alias}" is not an alias ([a-z0-9-_]+) \u2014 declare ${label2(producer)} under an alias a person chooses`);
37333
+ const existing = consumer.externals.find((e) => e.alias === alias);
37334
+ if (existing && existing.project === alias) return { alias, planned: true };
37335
+ const mounted = consumer.members.some((m) => familyNode(ctx.family, m)?.mountAlias === alias);
37336
+ const entry = entryOf(ctx, consumer);
37337
+ const planned = entry.externals.find((e) => e.alias === alias);
37338
+ if (existing || mounted || planned && planned.producer !== producer) {
37339
+ return refuse("alias-taken", `${label2(consumer.namespace)} already uses the alias "${alias}" for something else \u2014 declare ${label2(producer)} under another`);
37340
+ }
37341
+ if (!planned) entry.externals.push({ alias, project: alias, producer, reason });
37342
+ return { alias, planned: true };
37343
+ }
37344
+ function convertLegacyPins(ctx) {
37345
+ for (const node of ctx.family.nodes) {
37346
+ if (node.parent === void 0) continue;
37347
+ const vendored = runWithProjectRoot(node.directory, () => listExternalInterfaces2());
37348
+ for (const surface of vendored) {
37349
+ if (surface.sourceKind === "sibling") addSorted(entryOf(ctx, node).supersededPins, surface.projectName);
37350
+ else if (surface.sourceKind === "parent") convertParentPin(ctx, node, node.parent);
37351
+ }
37352
+ }
37353
+ }
37354
+ function convertParentPin(ctx, member, parent) {
37355
+ if (declares(ctx.family, member.namespace, parent)) return;
37356
+ if (entryOf(ctx, member).externals.some((e) => e.producer === parent)) return;
37357
+ const alias = planExternal(ctx, member, parent, "legacy-pin");
37358
+ if (alias !== void 0) addPin(ctx, member, alias.alias);
37359
+ }
37360
+ function settle2(ctx, top, whole) {
37361
+ const projects = [];
37362
+ for (const node of ctx.family.nodes) {
37363
+ const entry = ctx.projects.get(node.namespace);
37364
+ if (!entry || !hasWrites(entry)) continue;
37365
+ entry.exports.sort((a, b) => compare(a.from, b.from) || compare(a.component ?? a.typeDef ?? "", b.component ?? b.typeDef ?? ""));
37366
+ entry.externals.sort((a, b) => compare(a.alias, b.alias));
37367
+ entry.pins.sort();
37368
+ projects.push(entry);
37369
+ }
37370
+ for (const [namespace, finding] of ctx.absent) finding.blocking = projects.some((p) => p.project === namespace);
37371
+ return { familyRoot: top, whole, projects, findings: ctx.findings };
37372
+ }
37373
+ var compare = (a, b) => a < b ? -1 : a > b ? 1 : 0;
37374
+ var WriteLog = class {
37375
+ constructor() {
37376
+ this.written = [];
37377
+ this.relock = [];
37378
+ }
37379
+ file(file) {
37380
+ if (!this.written.includes(file)) this.written.push(file);
37381
+ }
37382
+ stale(directory) {
37383
+ if (!this.relock.includes(directory)) this.relock.push(directory);
37384
+ }
37385
+ };
37386
+ function apply(plan2) {
37387
+ if (isEmpty(plan2)) return { plan: plan2, applied: false, written: [], relock: [] };
37388
+ if (blocked(plan2)) {
37389
+ throw new ChainingMigrationRefusedError(plan2.findings.filter((f) => f.blocking).map((f) => `${f.kind} (${label2(f.project)}): ${f.detail}`));
37390
+ }
37391
+ const projects = plan2.projects.filter(hasWrites);
37392
+ for (const p of projects) runWithProjectRoot(p.directory, () => preflight(p));
37393
+ const log = new WriteLog();
37394
+ for (const p of projects.filter((q2) => q2.idToWrite !== void 0)) runWithProjectRoot(p.directory, () => declareId(p, log));
37395
+ for (const p of projects.filter((q2) => q2.exports.length > 0)) runWithProjectRoot(p.directory, () => addEntries(p, log));
37396
+ for (const p of projects.filter((q2) => q2.externals.length > 0)) runWithProjectRoot(p.directory, () => declareExternals(p, log));
37397
+ for (const p of projects.filter((q2) => q2.pins.length > 0)) runWithProjectRoot(p.directory, () => pin(p, log));
37398
+ return { plan: plan2, applied: true, written: log.written, relock: log.relock };
37399
+ }
37400
+ function preflight(p) {
37401
+ if (!projectConfigExists()) {
37402
+ throw new ChainingMigrationRefusedError([`project-absent (${label2(p.project)}): ${p.directory} has no project.yaml any more`]);
37403
+ }
37404
+ if (p.exports.length > 0) updateSpecGated("system", "system", exportDelta(p), true);
37405
+ }
37406
+ function exportDelta(p) {
37407
+ return {
37408
+ publicInterfaces: p.exports.map((e) => ({
37409
+ from: e.from,
37410
+ ...e.typeDef !== void 0 ? { typeDef: e.typeDef } : { component: e.component },
37411
+ audience: e.audience
37412
+ }))
37413
+ };
37414
+ }
37415
+ function declareId(p, log) {
37416
+ if (setProjectId(p.idToWrite)) log.file(AI_PATHS.projectConfig());
37417
+ }
37418
+ function addEntries(p, log) {
37419
+ const change = updateSpecGated("system", "system", exportDelta(p));
37420
+ if (!change.written) return;
37421
+ log.file(AI_PATHS.specsSystem());
37422
+ log.stale(p.directory);
37423
+ }
37424
+ function declareExternals(p, log) {
37425
+ for (const external of p.externals) {
37426
+ if (!declareExternal(external.alias, {})) continue;
37427
+ log.file(AI_PATHS.projectConfig());
37428
+ log.stale(p.directory);
37429
+ }
37430
+ }
37431
+ function pin(p, log) {
37432
+ for (const pinned of pinExternals2(p.pins)) {
37433
+ if (pinned.outcome !== "pinned") continue;
37434
+ if (pinned.snapshot) log.file(path42.resolve(p.directory, pinned.snapshot));
37435
+ log.file(path42.join(p.directory, ".wai", "externals.lock.yaml"));
37436
+ log.stale(p.directory);
37437
+ }
37438
+ }
37439
+
37440
+ // src/commands/doctor.ts
36810
37441
  function enabledTargets() {
36811
37442
  const config = loadProjectConfig();
36812
37443
  return config ? activeTargetTypes(config) : [];
@@ -36848,11 +37479,15 @@ function mcpEntryHealth(settingsPath) {
36848
37479
  }
36849
37480
  async function runDoctor(options = {}) {
36850
37481
  const tally = { warn: 0, error: 0 };
37482
+ if (options.report !== void 0) {
37483
+ reportOnly(options);
37484
+ return;
37485
+ }
36851
37486
  logger.blank();
36852
37487
  console.log(`${import_chalk12.default.bold("wairon doctor")} ${import_chalk12.default.gray(`\u2014 installed v${WAIRON_VERSION}`)}`);
36853
37488
  logger.blank();
36854
37489
  if (options.fix) {
36855
- await applyFixes();
37490
+ await applyFixes(options, tally);
36856
37491
  }
36857
37492
  console.log(import_chalk12.default.bold("Project"));
36858
37493
  if (!projectConfigExists()) {
@@ -36891,7 +37526,7 @@ async function runDoctor(options = {}) {
36891
37526
  try {
36892
37527
  const missing = findChainingSubprojectsMissingConfig(getProjectRoot());
36893
37528
  if (missing.length > 0) {
36894
- line(tally, "warn", `${missing.length} chained subproject(s) have specs but no project.yaml (un-runnable standalone): ${missing.map((d) => path42.relative(getProjectRoot(), d) || ".").join(", ")}. Run \`wairon doctor --fix\` to initialize them.`);
37529
+ line(tally, "warn", `${missing.length} chained subproject(s) have specs but no project.yaml (un-runnable standalone): ${missing.map((d) => path43.relative(getProjectRoot(), d) || ".").join(", ")}. Run \`wairon doctor --fix\` to initialize them.`);
36895
37530
  }
36896
37531
  } catch {
36897
37532
  }
@@ -36915,15 +37550,15 @@ async function runDoctor(options = {}) {
36915
37550
  }
36916
37551
  logger.blank();
36917
37552
  try {
36918
- const plan = retireSpecialists(false);
36919
- if (plan.retyped.length > 0) {
37553
+ const plan2 = retireSpecialists(false);
37554
+ if (plan2.retyped.length > 0) {
36920
37555
  console.log(import_chalk12.default.bold("Stereotypes"));
36921
- for (const retype of plan.retyped) {
37556
+ for (const retype of plan2.retyped) {
36922
37557
  const cls = retype.dependencyClass ?? "workflow";
36923
37558
  line(tally, "warn", `${retype.component}: Specialist \u2192 Orchestrator (${cls}) \u2014 ${retype.reason}`);
36924
37559
  }
36925
- if (plan.rebasedVariants.length > 0) {
36926
- line(tally, "warn", `${plan.rebasedVariants.length} project variant(s) to rebase from Specialist to Orchestrator: ${plan.rebasedVariants.join(", ")}`);
37560
+ if (plan2.rebasedVariants.length > 0) {
37561
+ line(tally, "warn", `${plan2.rebasedVariants.length} project variant(s) to rebase from Specialist to Orchestrator: ${plan2.rebasedVariants.join(", ")}`);
36927
37562
  }
36928
37563
  line(tally, "warn", "Run `wairon doctor --fix` to retire the Specialist stereotype.");
36929
37564
  logger.blank();
@@ -36948,23 +37583,33 @@ async function runDoctor(options = {}) {
36948
37583
  } catch (e) {
36949
37584
  line(tally, "warn", `Could not plan the narrative-step repair: ${e instanceof Error ? e.message : String(e)}`);
36950
37585
  }
37586
+ try {
37587
+ const pending = plan();
37588
+ if (!isEmpty(pending) || pending.findings.length > 0) {
37589
+ console.log(import_chalk12.default.bold("Chaining"));
37590
+ line(tally, "warn", `Chaining: ${pendingCount(pending)} pending (${chainingTotals(pending)}) \u2014 see \`wairon doctor --report chaining\`, apply with \`wairon doctor --fix\``);
37591
+ logger.blank();
37592
+ }
37593
+ } catch (e) {
37594
+ line(tally, "warn", `Could not plan the chaining migration: ${e instanceof Error ? e.message : String(e)}`);
37595
+ }
36951
37596
  }
36952
37597
  console.log(import_chalk12.default.bold(`Generated files ${import_chalk12.default.gray("(stale = older than installed)")}`));
36953
37598
  for (const p of derivedDocPaths()) {
36954
- const label2 = path42.relative(getProjectRoot(), p).replace(/\\/g, "/");
37599
+ const label3 = path43.relative(getProjectRoot(), p).replace(/\\/g, "/");
36955
37600
  if (!pathExists(p)) {
36956
- line(tally, "warn", `${label2} \u2014 not generated yet (run \`wairon generate\`)`);
37601
+ line(tally, "warn", `${label3} \u2014 not generated yet (run \`wairon generate\`)`);
36957
37602
  continue;
36958
37603
  }
36959
37604
  const { mark, note } = stampVerdict(readFileOrNull(p));
36960
- line(tally, mark, `${label2} (${note})`);
37605
+ line(tally, mark, `${label3} (${note})`);
36961
37606
  }
36962
37607
  const seenGuides = /* @__PURE__ */ new Set();
36963
37608
  for (const t of targets) {
36964
37609
  const gp = localGuideFilePath(process.cwd(), t);
36965
37610
  if (!gp || seenGuides.has(gp)) continue;
36966
37611
  seenGuides.add(gp);
36967
- const rel2 = path42.relative(process.cwd(), gp).replace(/\\/g, "/");
37612
+ const rel2 = path43.relative(process.cwd(), gp).replace(/\\/g, "/");
36968
37613
  if (!pathExists(gp)) {
36969
37614
  line(tally, "warn", `${rel2} guide \u2014 not injected (run \`wairon generate\`)`);
36970
37615
  continue;
@@ -37013,7 +37658,7 @@ async function runDoctor(options = {}) {
37013
37658
  }
37014
37659
  }
37015
37660
  try {
37016
- const legacy = path42.join(os9.homedir(), ".wairon", "baselines");
37661
+ const legacy = path43.join(os9.homedir(), ".wairon", "baselines");
37017
37662
  const leftovers = fs28.existsSync(legacy) ? fs28.readdirSync(legacy).filter((f) => f.endsWith(".json")) : [];
37018
37663
  if (leftovers.length) {
37019
37664
  console.log(import_chalk12.default.bold("Approvals"));
@@ -37063,7 +37708,7 @@ async function runDoctor(options = {}) {
37063
37708
  line(tally, h.mark, `Claude (project .mcp.json): ${h.note}${h.mark === "ok" ? "" : " \u2014 run `wairon mcp install --backend claude`"}`);
37064
37709
  }
37065
37710
  if (wantGemini) {
37066
- const globalCfg = path42.join(os9.homedir(), ".gemini", "antigravity-cli", "mcp_config.json");
37711
+ const globalCfg = path43.join(os9.homedir(), ".gemini", "antigravity-cli", "mcp_config.json");
37067
37712
  const hg = mcpEntryHealth(globalCfg);
37068
37713
  line(tally, hg.mark, `Antigravity (global mcp_config.json): ${hg.note}${hg.mark === "ok" ? "" : " \u2014 run `wairon mcp install --backend gemini --global`"}`);
37069
37714
  const projPath = fromProjectRoot(".gemini", "settings.json");
@@ -37072,7 +37717,7 @@ async function runDoctor(options = {}) {
37072
37717
  line(tally, hp.mark === "error" ? "error" : "ok", `Gemini CLI (project): ${hp.note} ${import_chalk12.default.gray("(Antigravity ignores this file)")}`);
37073
37718
  }
37074
37719
  }
37075
- const pluginDir = path42.join(os9.homedir(), ".gemini", "config", "plugins", "wairon");
37720
+ const pluginDir = path43.join(os9.homedir(), ".gemini", "config", "plugins", "wairon");
37076
37721
  if (fs28.existsSync(pluginDir)) {
37077
37722
  line(tally, "warn", `Legacy Antigravity plugin present (${pluginDir}) \u2014 it collides with the wairon MCP server. Remove it with \`wairon doctor --fix\`.`);
37078
37723
  }
@@ -37080,7 +37725,7 @@ async function runDoctor(options = {}) {
37080
37725
  printSummary(tally);
37081
37726
  if (tally.error > 0) process.exit(1);
37082
37727
  }
37083
- async function applyFixes() {
37728
+ async function applyFixes(options, tally) {
37084
37729
  if (!projectConfigExists()) return;
37085
37730
  let targets;
37086
37731
  try {
@@ -37149,6 +37794,7 @@ async function applyFixes() {
37149
37794
  } catch (e) {
37150
37795
  console.log(` ${icon("error")} Narrative-step repair failed: ${e instanceof Error ? e.message : String(e)}`);
37151
37796
  }
37797
+ await migrateChaining(options, tally);
37152
37798
  const { runMcpInstall: runMcpInstall2 } = (init_mcp(), __toCommonJS(mcp_exports));
37153
37799
  if (targets.includes("claude")) {
37154
37800
  try {
@@ -37172,6 +37818,106 @@ async function applyFixes() {
37172
37818
  }
37173
37819
  logger.blank();
37174
37820
  }
37821
+ function reportOnly(options) {
37822
+ if (options.fix) {
37823
+ throw new DoctorOptionsError("--report prints a plan and writes nothing, so it never combines with --fix.");
37824
+ }
37825
+ if (options.report !== "chaining") {
37826
+ throw new DoctorOptionsError(`--report knows one section, \`chaining\`; "${options.report}" is not one.`);
37827
+ }
37828
+ const migration = plan();
37829
+ logger.blank();
37830
+ console.log(`${import_chalk12.default.bold("wairon doctor --report chaining")} ${import_chalk12.default.gray(`\u2014 installed v${WAIRON_VERSION}`)}`);
37831
+ logger.blank();
37832
+ printChainingPlan(migration);
37833
+ console.log(import_chalk12.default.gray("Nothing was written."));
37834
+ logger.blank();
37835
+ }
37836
+ async function migrateChaining(options, tally) {
37837
+ let migration;
37838
+ try {
37839
+ migration = plan();
37840
+ } catch (e) {
37841
+ console.log(` ${icon("error")} Could not plan the chaining migration: ${e instanceof Error ? e.message : String(e)}`);
37842
+ tally.error++;
37843
+ return;
37844
+ }
37845
+ if (isEmpty(migration)) return;
37846
+ printChainingPlan(migration);
37847
+ if (!await confirmChaining(options)) return;
37848
+ try {
37849
+ const applied = apply(migration);
37850
+ printApplied(applied.written, applied.relock);
37851
+ } catch (e) {
37852
+ if (!(e instanceof ChainingMigrationRefusedError)) throw e;
37853
+ console.log(` ${icon("error")} ${e.message}`);
37854
+ tally.error++;
37855
+ }
37856
+ }
37857
+ async function confirmChaining(options) {
37858
+ if (options.yes) return true;
37859
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
37860
+ console.log(` ${icon("warn")} Chaining migration skipped: no terminal to confirm it \u2014 re-run with \`--fix --yes\` to apply it. Nothing of it was written.`);
37861
+ return false;
37862
+ }
37863
+ const { confirmed } = await import_inquirer4.default.prompt([
37864
+ { type: "confirm", name: "confirmed", message: "Apply the chaining migration above?", default: false }
37865
+ ]);
37866
+ if (!confirmed) console.log(` ${icon("warn")} Chaining migration skipped. Nothing of it was written.`);
37867
+ return confirmed;
37868
+ }
37869
+ function printApplied(written, relock) {
37870
+ console.log(` ${icon("ok")} Applied the chaining migration: ${written.length} file(s) written.`);
37871
+ for (const file of written) console.log(` ${file}`);
37872
+ if (relock.length === 0) return;
37873
+ console.log(` ${icon("warn")} The writes staled the approval of ${relock.length} project(s); nothing was locked here. Re-lock each with \`wairon lock\`:`);
37874
+ for (const dir of relock) console.log(` ${dir}`);
37875
+ }
37876
+ function pendingCount(migration) {
37877
+ return migration.projects.reduce((n, p) => n + (p.idToWrite ? 1 : 0) + p.exports.length + p.externals.length + p.pins.length, 0);
37878
+ }
37879
+ function chainingTotals(migration) {
37880
+ const sum = (f) => migration.projects.reduce((n, p) => n + f(p), 0);
37881
+ const entries = sum((p) => p.exports.length);
37882
+ return [
37883
+ `${sum((p) => p.idToWrite ? 1 : 0)} id(s)`,
37884
+ `${entries} L0 entr${entries === 1 ? "y" : "ies"}`,
37885
+ `${sum((p) => p.externals.length)} external(s)`,
37886
+ `${sum((p) => p.pins.length)} pin(s)`,
37887
+ `${sum((p) => p.supersededPins.length)} superseded pin(s)`,
37888
+ `${migration.findings.length} finding(s)`
37889
+ ].join(", ");
37890
+ }
37891
+ function printChainingPlan(migration) {
37892
+ console.log(import_chalk12.default.bold("Chaining migration"));
37893
+ console.log(import_chalk12.default.gray(` family root: ${migration.familyRoot}${migration.whole ? "" : " (partial: a hop above was out of reach)"}`));
37894
+ if (isEmpty(migration)) console.log(" Nothing to migrate.");
37895
+ for (const p of migration.projects) printProjectMigration(p);
37896
+ for (const f of migration.findings) {
37897
+ const where = f.project === "" ? "top root" : f.project;
37898
+ console.log(` ${icon(f.blocking ? "error" : "warn")} ${f.kind} [${where}]${f.blocking ? " (blocks apply)" : ""}: ${f.detail}`);
37899
+ }
37900
+ console.log(` Totals: ${chainingTotals(migration)}.`);
37901
+ logger.blank();
37902
+ }
37903
+ function printProjectMigration(p) {
37904
+ console.log(` ${import_chalk12.default.bold(p.project === "" ? "(top root)" : p.project)} ${import_chalk12.default.gray(p.directory)}`);
37905
+ if (p.idToWrite) console.log(` id: declare "${p.idToWrite}"`);
37906
+ for (const e of p.exports) {
37907
+ const item = e.component !== void 0 ? `component: ${e.component}` : `typeDef: ${e.typeDef}`;
37908
+ const members = e.members.length > 0 ? `, reaching ${e.members.join(", ")}` : "";
37909
+ const consumers = e.consumers.map((c) => c || "(top root)").join(", ");
37910
+ console.log(` L0 entry: { from: ${e.from}, ${item}, audience: ${e.audience} } as "${e.publicName}" \u2014 for ${consumers}${members}`);
37911
+ }
37912
+ for (const x of p.externals) {
37913
+ const why = x.reason === "legacy-pin" ? "replaces the stage-1 family pin of" : "its references cross into";
37914
+ console.log(` external: ${x.alias}: {} \u2014 ${why} ${x.producer || "(top root)"}`);
37915
+ }
37916
+ if (p.pins.length > 0) console.log(` pin: ${p.pins.join(", ")}`);
37917
+ for (const key of p.supersededPins) {
37918
+ console.log(` superseded sibling pin: ${key} \u2014 left on disk until stage 3 (stage-1 resolution still reads it; nothing deletes it)`);
37919
+ }
37920
+ }
37175
37921
  function printSummary(tally) {
37176
37922
  if (tally.error === 0 && tally.warn === 0) {
37177
37923
  logger.success("All checks passed \u2014 everything is current.");
@@ -37189,13 +37935,13 @@ function printSummary(tally) {
37189
37935
 
37190
37936
  // src/commands/diagram.ts
37191
37937
  var fs29 = __toESM(require("fs"));
37192
- var path44 = __toESM(require("path"));
37938
+ var path45 = __toESM(require("path"));
37193
37939
  init_logger();
37194
37940
  init_paths();
37195
37941
  init_fs();
37196
37942
 
37197
37943
  // src/commands/subsystem.ts
37198
- var path43 = __toESM(require("path"));
37944
+ var path44 = __toESM(require("path"));
37199
37945
  init_logger();
37200
37946
  init_errors();
37201
37947
  init_fs();
@@ -37230,10 +37976,10 @@ async function runSubsystemAdd(id, options = {}) {
37230
37976
  const stored = loadSubsystemSpec(id);
37231
37977
  const restatement = subsystemAddRestatement(id, options.projectPath, options.name, stored);
37232
37978
  const receipt = writeSpec(restatement);
37233
- const childDir = path43.resolve(getProjectRoot(), options.projectPath);
37979
+ const childDir = path44.resolve(getProjectRoot(), options.projectPath);
37234
37980
  logger.success(`${receipt.replacedExisting ? "Re-authored" : "Added"} external subsystem "${id}" \u2192 ${options.projectPath}`);
37235
37981
  for (const notice of receipt.notices) logger.info(notice);
37236
- logger.info(`Scaffolded child project at ${path43.relative(process.cwd(), childDir) || "."}`);
37982
+ logger.info(`Scaffolded child project at ${path44.relative(process.cwd(), childDir) || "."}`);
37237
37983
  logger.info(`Design its spec tree from this parent using namespaced ids (e.g. ${id}::<component>).`);
37238
37984
  }
37239
37985
  async function runSubsystemMove(id, options = {}) {
@@ -37256,9 +38002,9 @@ async function runSubsystemExternalize(id, options = {}) {
37256
38002
  throw new WaironError("--project-path (the subproject destination) is required.");
37257
38003
  }
37258
38004
  externalizeSubsystem(id, options.projectPath);
37259
- const childDir = path43.resolve(getProjectRoot(), options.projectPath);
38005
+ const childDir = path44.resolve(getProjectRoot(), options.projectPath);
37260
38006
  logger.success(`Externalized subsystem "${id}" \u2192 ${options.projectPath}`);
37261
- logger.info(`Moved its specs into ${path43.relative(process.cwd(), childDir) || "."} (now a standalone subproject).`);
38007
+ logger.info(`Moved its specs into ${path44.relative(process.cwd(), childDir) || "."} (now a standalone subproject).`);
37262
38008
  logger.info("Move the source code there yourself, then run `wairon validate` to confirm the tree.");
37263
38009
  }
37264
38010
  async function runSubsystemInternalize(id) {
@@ -37289,7 +38035,7 @@ function applyFormat(options) {
37289
38035
  };
37290
38036
  }
37291
38037
  function writeArtifact(dest, content) {
37292
- ensureDir(path44.dirname(path44.resolve(dest)));
38038
+ ensureDir(path45.dirname(path45.resolve(dest)));
37293
38039
  fs29.writeFileSync(dest, content, "utf-8");
37294
38040
  }
37295
38041
  function parseSequenceRef(ref) {
@@ -37305,21 +38051,21 @@ async function runDiagram(rawOptions = {}) {
37305
38051
  assertProjectInitialized();
37306
38052
  const options = applyFormat(rawOptions);
37307
38053
  if (options.canvas && !options.all) {
37308
- const dest2 = options.out ?? path44.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
38054
+ const dest2 = options.out ?? path45.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
37309
38055
  writeArtifact(dest2, renderDiagram("canvas"));
37310
38056
  logger.success(`Interactive canvas written to ${dest2}`);
37311
38057
  logger.info("Open it in a browser \u2014 fully self-contained (works offline).");
37312
38058
  return;
37313
38059
  }
37314
38060
  if (options.drawio && !options.all) {
37315
- const dest2 = options.out ?? path44.join(AI_PATHS.docsDir(), "diagrams", "architecture.drawio");
38061
+ const dest2 = options.out ?? path45.join(AI_PATHS.docsDir(), "diagrams", "architecture.drawio");
37316
38062
  writeArtifact(dest2, renderDiagram("drawio"));
37317
38063
  logger.success(`draw.io diagram written to ${dest2}`);
37318
38064
  logger.info("Open with draw.io / diagrams.net (or import into tools that accept the format).");
37319
38065
  return;
37320
38066
  }
37321
38067
  if (options.excalidraw && !options.all) {
37322
- const dest2 = options.out ?? path44.join(AI_PATHS.docsDir(), "diagrams", "architecture.excalidraw");
38068
+ const dest2 = options.out ?? path45.join(AI_PATHS.docsDir(), "diagrams", "architecture.excalidraw");
37323
38069
  writeArtifact(dest2, renderDiagram("excalidraw"));
37324
38070
  logger.success(`Excalidraw scene written to ${dest2}`);
37325
38071
  logger.info("Open with excalidraw.com or the VS Code extension.");
@@ -37327,27 +38073,27 @@ async function runDiagram(rawOptions = {}) {
37327
38073
  }
37328
38074
  const wantsMermaid = options.format?.toLowerCase().startsWith("mermaid") || !!options.subsystem || !!options.sequence;
37329
38075
  if (!options.all && !options.sequence && !wantsMermaid) {
37330
- const dest2 = options.out ?? path44.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
38076
+ const dest2 = options.out ?? path45.join(AI_PATHS.docsDir(), "diagrams", "canvas.html");
37331
38077
  writeArtifact(dest2, renderDiagram("canvas"));
37332
38078
  logger.success(`Interactive canvas written to ${dest2}`);
37333
38079
  logger.info("Open it in a browser \u2014 fully self-contained (works offline). Other formats: --format mermaid|drawio|excalidraw.");
37334
38080
  return;
37335
38081
  }
37336
38082
  if (options.all) {
37337
- const outDir = options.out ?? path44.join(AI_PATHS.docsDir(), "diagrams");
38083
+ const outDir = options.out ?? path45.join(AI_PATHS.docsDir(), "diagrams");
37338
38084
  const files = generateDiagramSet();
37339
38085
  if (files.length === 0) {
37340
38086
  logger.warn("No diagrams to generate \u2014 the spec tree has no components yet.");
37341
38087
  return;
37342
38088
  }
37343
38089
  for (const file of files) {
37344
- writeArtifact(path44.join(outDir, file.relPath), toMarkdown(file));
38090
+ writeArtifact(path45.join(outDir, file.relPath), toMarkdown(file));
37345
38091
  }
37346
- writeArtifact(path44.join(outDir, "canvas.html"), renderDiagram("canvas"));
37347
- writeArtifact(path44.join(outDir, "architecture.drawio"), renderDiagram("drawio"));
37348
- writeArtifact(path44.join(outDir, "architecture.excalidraw"), renderDiagram("excalidraw"));
38092
+ writeArtifact(path45.join(outDir, "canvas.html"), renderDiagram("canvas"));
38093
+ writeArtifact(path45.join(outDir, "architecture.drawio"), renderDiagram("drawio"));
38094
+ writeArtifact(path45.join(outDir, "architecture.excalidraw"), renderDiagram("excalidraw"));
37349
38095
  const graph2 = loadSpecGraph();
37350
- const indexPath = path44.join(outDir, "README.md");
38096
+ const indexPath = path45.join(outDir, "README.md");
37351
38097
  writeArtifact(indexPath, diagramSetIndex(files, graph2.systemName));
37352
38098
  logger.success(`Generated ${files.length} diagram(s) + interactive canvas.html + index into ${outDir}`);
37353
38099
  for (const file of files.slice(0, 12)) {
@@ -37359,20 +38105,20 @@ async function runDiagram(rawOptions = {}) {
37359
38105
  let mermaid;
37360
38106
  let title;
37361
38107
  let defaultDest;
37362
- const diagramsDir = path44.join(AI_PATHS.docsDir(), "diagrams");
38108
+ const diagramsDir = path45.join(AI_PATHS.docsDir(), "diagrams");
37363
38109
  if (options.sequence) {
37364
38110
  const { component, method: method2 } = parseSequenceRef(options.sequence);
37365
38111
  mermaid = generateSequenceDiagram(component, method2, { depth: options.depth });
37366
38112
  title = `${component}.${method2} \u2014 narrative sequence`;
37367
- defaultDest = path44.join(diagramsDir, "sequences", `${component.replace(/::/g, "--")}.${method2}.md`);
38113
+ defaultDest = path45.join(diagramsDir, "sequences", `${component.replace(/::/g, "--")}.${method2}.md`);
37368
38114
  } else if (options.subsystem) {
37369
38115
  mermaid = generateComponentDiagram({ subsystem: options.subsystem });
37370
38116
  title = `${options.subsystem} \u2014 components`;
37371
- defaultDest = path44.join(diagramsDir, "subsystems", `${options.subsystem.replace(/::/g, "--")}.md`);
38117
+ defaultDest = path45.join(diagramsDir, "subsystems", `${options.subsystem.replace(/::/g, "--")}.md`);
37372
38118
  } else {
37373
38119
  mermaid = renderDiagram("mermaid");
37374
38120
  title = "Component architecture";
37375
- defaultDest = path44.join(diagramsDir, "system.md");
38121
+ defaultDest = path45.join(diagramsDir, "system.md");
37376
38122
  }
37377
38123
  const dest = options.out ?? defaultDest;
37378
38124
  const content = dest.endsWith(".mmd") ? `${mermaid}
@@ -37488,7 +38234,7 @@ Component variants (${variants.length})
37488
38234
  // src/commands/packs.ts
37489
38235
  var fs30 = __toESM(require("fs"));
37490
38236
  var os10 = __toESM(require("os"));
37491
- var path45 = __toESM(require("path"));
38237
+ var path46 = __toESM(require("path"));
37492
38238
  var import_chalk16 = __toESM(require("chalk"));
37493
38239
  var import_sdk = __toESM(require_dist());
37494
38240
  init_logger();
@@ -37515,7 +38261,7 @@ function describe2(probe2) {
37515
38261
  return parts.join(", ");
37516
38262
  }
37517
38263
  function resolveSourceUnit(source) {
37518
- const abs = path45.resolve(source);
38264
+ const abs = path46.resolve(source);
37519
38265
  if (!fs30.existsSync(abs)) {
37520
38266
  throw new Error(`Pack source "${source}" does not exist.`);
37521
38267
  }
@@ -37535,7 +38281,7 @@ async function addPack(source, options = {}) {
37535
38281
  }
37536
38282
  const { abs } = resolveSourceUnit(source);
37537
38283
  const scope = options.global ? "global" : "project";
37538
- const probe2 = probePack(abs, path45.dirname(abs), scope);
38284
+ const probe2 = probePack(abs, path46.dirname(abs), scope);
37539
38285
  if (probe2.error) {
37540
38286
  logger.error(probe2.error);
37541
38287
  process.exitCode = 1;
@@ -37543,8 +38289,8 @@ async function addPack(source, options = {}) {
37543
38289
  }
37544
38290
  if (options.global) {
37545
38291
  const destDir = globalPacksDir();
37546
- const dest2 = path45.join(destDir, path45.basename(abs));
37547
- if (path45.resolve(dest2) !== abs) {
38292
+ const dest2 = path46.join(destDir, path46.basename(abs));
38293
+ if (path46.resolve(dest2) !== abs) {
37548
38294
  fs30.mkdirSync(destDir, { recursive: true });
37549
38295
  fs30.cpSync(abs, dest2, { recursive: true, force: true });
37550
38296
  }
@@ -37559,10 +38305,10 @@ async function addPack(source, options = {}) {
37559
38305
  return;
37560
38306
  }
37561
38307
  const root = getProjectRoot();
37562
- const relRef = `.wai/packs/${path45.basename(abs)}`;
37563
- const dest = path45.join(root, ".wai", "packs", path45.basename(abs));
37564
- if (path45.resolve(dest) !== abs) {
37565
- fs30.mkdirSync(path45.dirname(dest), { recursive: true });
38308
+ const relRef = `.wai/packs/${path46.basename(abs)}`;
38309
+ const dest = path46.join(root, ".wai", "packs", path46.basename(abs));
38310
+ if (path46.resolve(dest) !== abs) {
38311
+ fs30.mkdirSync(path46.dirname(dest), { recursive: true });
37566
38312
  fs30.cpSync(abs, dest, { recursive: true, force: true });
37567
38313
  }
37568
38314
  if (registerPackRef(relRef)) {
@@ -37574,7 +38320,7 @@ async function addPack(source, options = {}) {
37574
38320
  logger.info(`${describe2(probe2)} \u2014 commit .wai/ so CI and every clone enforce it.`);
37575
38321
  }
37576
38322
  async function addPackFromArchive(source, options) {
37577
- const abs = path45.resolve(source);
38323
+ const abs = path46.resolve(source);
37578
38324
  if (!fs30.existsSync(abs) || !fs30.statSync(abs).isFile()) {
37579
38325
  logger.error(`Pack archive "${source}" does not exist.`);
37580
38326
  process.exitCode = 1;
@@ -37590,25 +38336,25 @@ async function addPackFromArchive(source, options) {
37590
38336
  process.exitCode = 1;
37591
38337
  return;
37592
38338
  }
37593
- baseDir = path45.join(getProjectRoot(), ".wai", "packs");
38339
+ baseDir = path46.join(getProjectRoot(), ".wai", "packs");
37594
38340
  }
37595
38341
  const bytes = fs30.readFileSync(abs);
37596
38342
  fs30.mkdirSync(baseDir, { recursive: true });
37597
- const staging = fs30.mkdtempSync(path45.join(baseDir, ".wpack-staging-"));
38343
+ const staging = fs30.mkdtempSync(path46.join(baseDir, ".wpack-staging-"));
37598
38344
  let result;
37599
38345
  try {
37600
38346
  result = (0, import_sdk.extractPack)(bytes, staging);
37601
38347
  } catch (err) {
37602
38348
  fs30.rmSync(staging, { recursive: true, force: true });
37603
- logger.error(`Failed to extract pack archive "${path45.basename(abs)}": ${err instanceof Error ? err.message : String(err)}`);
38349
+ logger.error(`Failed to extract pack archive "${path46.basename(abs)}": ${err instanceof Error ? err.message : String(err)}`);
37604
38350
  process.exitCode = 1;
37605
38351
  return;
37606
38352
  }
37607
38353
  const name = result.name;
37608
- const destDir = path45.join(baseDir, name);
38354
+ const destDir = path46.join(baseDir, name);
37609
38355
  if (fs30.existsSync(destDir)) fs30.rmSync(destDir, { recursive: true, force: true });
37610
38356
  fs30.renameSync(staging, destDir);
37611
- const probe2 = probePack(destDir, path45.dirname(destDir), scope);
38357
+ const probe2 = probePack(destDir, path46.dirname(destDir), scope);
37612
38358
  if (probe2.error) {
37613
38359
  fs30.rmSync(destDir, { recursive: true, force: true });
37614
38360
  logger.error(probe2.error);
@@ -37624,7 +38370,7 @@ async function addPackFromArchive(source, options) {
37624
38370
  if (registerPackRef(relRef)) {
37625
38371
  logger.success(`Installed pack "${probe2.name ?? name}" into ${relRef} and registered it in .wai/project.yaml.`);
37626
38372
  } else {
37627
- logger.success(`Pack "${probe2.name ?? name}" already registered \u2014 refreshed ${relRef} from ${path45.basename(abs)}.`);
38373
+ logger.success(`Pack "${probe2.name ?? name}" already registered \u2014 refreshed ${relRef} from ${path46.basename(abs)}.`);
37628
38374
  }
37629
38375
  logger.info(`${describe2(probe2)} \u2014 commit .wai/ so CI and every clone enforce it.`);
37630
38376
  }
@@ -37656,8 +38402,8 @@ function expandSource(source, version) {
37656
38402
  return source.replace(/\{version\}/g, version ?? "").replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => process.env[name] ?? "");
37657
38403
  }
37658
38404
  async function fetchArchive(url) {
37659
- const dir = fs30.mkdtempSync(path45.join(os10.tmpdir(), "wairon-packdl-"));
37660
- const dest = path45.join(dir, "pack.wpack");
38405
+ const dir = fs30.mkdtempSync(path46.join(os10.tmpdir(), "wairon-packdl-"));
38406
+ const dest = path46.join(dir, "pack.wpack");
37661
38407
  await downloadFile(url, dest);
37662
38408
  return dest;
37663
38409
  }
@@ -37676,7 +38422,7 @@ async function installPack(source) {
37676
38422
  }
37677
38423
  const extracted = extractArchiveToTemp(archive);
37678
38424
  try {
37679
- fs30.rmSync(path45.dirname(archive), { recursive: true, force: true });
38425
+ fs30.rmSync(path46.dirname(archive), { recursive: true, force: true });
37680
38426
  } catch {
37681
38427
  }
37682
38428
  if (!extracted) return;
@@ -37699,7 +38445,7 @@ async function installPack(source) {
37699
38445
  if (!extracted) return;
37700
38446
  sourceDir = extracted.dir;
37701
38447
  cleanup = extracted.dir;
37702
- origin = path45.resolve(source);
38448
+ origin = path46.resolve(source);
37703
38449
  } else {
37704
38450
  let unit;
37705
38451
  try {
@@ -37742,13 +38488,13 @@ function reportInstalled(installed, origin) {
37742
38488
  }
37743
38489
  }
37744
38490
  function extractArchiveToTemp(source) {
37745
- const abs = path45.resolve(source);
38491
+ const abs = path46.resolve(source);
37746
38492
  if (!fs30.existsSync(abs)) {
37747
38493
  logger.error(`Pack archive "${source}" does not exist.`);
37748
38494
  process.exitCode = 1;
37749
38495
  return null;
37750
38496
  }
37751
- const tempDir = fs30.mkdtempSync(path45.join(os10.tmpdir(), "wairon-packinstall-"));
38497
+ const tempDir = fs30.mkdtempSync(path46.join(os10.tmpdir(), "wairon-packinstall-"));
37752
38498
  try {
37753
38499
  const result = (0, import_sdk.extractPack)(new Uint8Array(fs30.readFileSync(abs)), tempDir);
37754
38500
  void result;
@@ -37857,10 +38603,10 @@ async function syncPacks() {
37857
38603
  }
37858
38604
  let installed = 0;
37859
38605
  for (const selection of missing) {
37860
- const label2 = packEntryLabel(selection);
38606
+ const label3 = packEntryLabel(selection);
37861
38607
  const blocker = syncBlocker(selection);
37862
38608
  if (blocker) {
37863
- logger.error(`"${label2}" ${blocker}`);
38609
+ logger.error(`"${label3}" ${blocker}`);
37864
38610
  process.exitCode = 1;
37865
38611
  continue;
37866
38612
  }
@@ -37871,7 +38617,7 @@ async function syncPacks() {
37871
38617
  console.log(` ${import_chalk16.default.green("+")} ${pack.name} v${pack.version} ${import_chalk16.default.dim(`\u2190 ${url}`)}`);
37872
38618
  installed++;
37873
38619
  } catch (e) {
37874
- logger.error(`Could not sync "${label2}" from ${url}: ${e instanceof Error ? e.message : String(e)}`);
38620
+ logger.error(`Could not sync "${label3}" from ${url}: ${e instanceof Error ? e.message : String(e)}`);
37875
38621
  process.exitCode = 1;
37876
38622
  }
37877
38623
  }
@@ -37892,7 +38638,7 @@ async function fetchAndInstall(selection, url) {
37892
38638
  const archive = await fetchArchive(url);
37893
38639
  const extracted = extractArchiveToTemp(archive);
37894
38640
  try {
37895
- fs30.rmSync(path45.dirname(archive), { recursive: true, force: true });
38641
+ fs30.rmSync(path46.dirname(archive), { recursive: true, force: true });
37896
38642
  } catch {
37897
38643
  }
37898
38644
  if (!extracted) return null;
@@ -37915,9 +38661,9 @@ function bundleTargets(selections, name, all) {
37915
38661
  return selections.filter((s) => s.bundle === true);
37916
38662
  }
37917
38663
  function writeBundle(root, resolved) {
37918
- const dest = path45.join(root, ".wai", "packs", resolved.name, resolved.version);
38664
+ const dest = path46.join(root, ".wai", "packs", resolved.name, resolved.version);
37919
38665
  fs30.rmSync(dest, { recursive: true, force: true });
37920
- fs30.mkdirSync(path45.dirname(dest), { recursive: true });
38666
+ fs30.mkdirSync(path46.dirname(dest), { recursive: true });
37921
38667
  fs30.cpSync(resolved.path, dest, { recursive: true });
37922
38668
  return dest;
37923
38669
  }
@@ -37949,7 +38695,7 @@ async function bundlePack(name, options = {}) {
37949
38695
  const dest = writeBundle(root, resolved);
37950
38696
  bundledSelections.push({ name: selection.name, version: resolved.version });
37951
38697
  bundled.push(`${resolved.name}@${resolved.version}`);
37952
- console.log(` ${import_chalk16.default.green("+")} ${import_chalk16.default.dim(path45.relative(root, dest).replace(/\\/g, "/"))}`);
38698
+ console.log(` ${import_chalk16.default.green("+")} ${import_chalk16.default.dim(path46.relative(root, dest).replace(/\\/g, "/"))}`);
37953
38699
  }
37954
38700
  if (bundled.length === 0) return;
37955
38701
  markSelectionsBundled(bundledSelections);
@@ -37986,9 +38732,9 @@ async function listPacks() {
37986
38732
  console.log(import_chalk16.default.bold.cyan(`\u25A0 Global (${globalPacksDir()})${useGlobal ? "" : import_chalk16.default.yellow(" [disabled: extensions.useGlobalPacks: false]")}`));
37987
38733
  if (globalRefs.length === 0) console.log(import_chalk16.default.dim(" (none)"));
37988
38734
  for (const ref of globalRefs) {
37989
- const probe2 = probePack(ref, path45.dirname(ref), "global");
37990
- if (probe2.error) console.log(` ${import_chalk16.default.red("\u2716")} ${path45.basename(ref)} \u2014 ${import_chalk16.default.red(probe2.error)}`);
37991
- else console.log(` ${import_chalk16.default.green("\u25CF")} ${import_chalk16.default.bold(probe2.name ?? path45.basename(ref))} ${import_chalk16.default.dim(describe2(probe2))}`);
38735
+ const probe2 = probePack(ref, path46.dirname(ref), "global");
38736
+ if (probe2.error) console.log(` ${import_chalk16.default.red("\u2716")} ${path46.basename(ref)} \u2014 ${import_chalk16.default.red(probe2.error)}`);
38737
+ else console.log(` ${import_chalk16.default.green("\u25CF")} ${import_chalk16.default.bold(probe2.name ?? path46.basename(ref))} ${import_chalk16.default.dim(describe2(probe2))}`);
37992
38738
  }
37993
38739
  console.log("");
37994
38740
  if (!inProject) {
@@ -37999,16 +38745,16 @@ async function listPacks() {
37999
38745
  const projectEntries = config?.extensions?.packs ?? [];
38000
38746
  if (projectEntries.length === 0) console.log(import_chalk16.default.dim(" (none)"));
38001
38747
  for (const entry of projectEntries) {
38002
- const label2 = packEntryLabel(entry);
38748
+ const label3 = packEntryLabel(entry);
38003
38749
  const ref = packEntryRef(entry, root);
38004
38750
  if (!ref) {
38005
- console.log(` ${import_chalk16.default.red("\u2716")} ${label2} \u2014 ${import_chalk16.default.red("declared but not installed or bundled")}`);
38751
+ console.log(` ${import_chalk16.default.red("\u2716")} ${label3} \u2014 ${import_chalk16.default.red("declared but not installed or bundled")}`);
38006
38752
  continue;
38007
38753
  }
38008
38754
  const probe2 = probePack(ref, root, "project");
38009
38755
  const origin = typeof entry === "string" ? ref : `selected \u2192 ${ref}`;
38010
- if (probe2.error) console.log(` ${import_chalk16.default.red("\u2716")} ${label2} \u2014 ${import_chalk16.default.red(probe2.error)}`);
38011
- else console.log(` ${import_chalk16.default.green("\u25CF")} ${import_chalk16.default.bold(probe2.name ?? label2)} ${import_chalk16.default.dim(origin)} ${import_chalk16.default.dim(describe2(probe2))}`);
38756
+ if (probe2.error) console.log(` ${import_chalk16.default.red("\u2716")} ${label3} \u2014 ${import_chalk16.default.red(probe2.error)}`);
38757
+ else console.log(` ${import_chalk16.default.green("\u25CF")} ${import_chalk16.default.bold(probe2.name ?? label3)} ${import_chalk16.default.dim(origin)} ${import_chalk16.default.dim(describe2(probe2))}`);
38012
38758
  }
38013
38759
  console.log("");
38014
38760
  logger.info("Rules from packs show up in `wairon rules list`; add packs with `wairon pack add <source> [--global]`.");
@@ -38016,8 +38762,8 @@ async function listPacks() {
38016
38762
  async function removePack(name, options = {}) {
38017
38763
  if (options.global) {
38018
38764
  for (const ref of discoverPacks(globalPacksDir())) {
38019
- const probe2 = probePack(ref, path45.dirname(ref), "global");
38020
- if (probe2.name === name || path45.basename(ref) === name) {
38765
+ const probe2 = probePack(ref, path46.dirname(ref), "global");
38766
+ if (probe2.name === name || path46.basename(ref) === name) {
38021
38767
  fs30.rmSync(ref, { recursive: true, force: true });
38022
38768
  logger.success(`Removed global pack "${probe2.name ?? name}" (${ref}).`);
38023
38769
  return;
@@ -38039,11 +38785,11 @@ async function removePack(name, options = {}) {
38039
38785
  if (typeof entry !== "string") continue;
38040
38786
  const ref = entry;
38041
38787
  const probe2 = probePack(ref, root, "project");
38042
- if (probe2.name === name || ref === name || path45.basename(ref) === name) {
38788
+ if (probe2.name === name || ref === name || path46.basename(ref) === name) {
38043
38789
  deregisterPackRef(ref);
38044
- const resolved = path45.resolve(root, ref);
38045
- const vendorDir = path45.resolve(root, ".wai", "packs");
38046
- if (resolved.startsWith(vendorDir + path45.sep)) {
38790
+ const resolved = path46.resolve(root, ref);
38791
+ const vendorDir = path46.resolve(root, ".wai", "packs");
38792
+ if (resolved.startsWith(vendorDir + path46.sep)) {
38047
38793
  fs30.rmSync(resolved, { recursive: true, force: true });
38048
38794
  logger.success(`Deregistered pack "${probe2.name ?? name}" and deleted ${ref}.`);
38049
38795
  } else {
@@ -38058,7 +38804,7 @@ async function removePack(name, options = {}) {
38058
38804
 
38059
38805
  // src/commands/host.ts
38060
38806
  var fs57 = __toESM(require("fs"));
38061
- var path72 = __toESM(require("path"));
38807
+ var path73 = __toESM(require("path"));
38062
38808
  var os11 = __toESM(require("os"));
38063
38809
  var crypto24 = __toESM(require("crypto"));
38064
38810
  var import_child_process7 = require("child_process");
@@ -38087,14 +38833,14 @@ var WEB_SESSION_PREFIX = "ws_";
38087
38833
 
38088
38834
  // src/server/credentials.ts
38089
38835
  var fs31 = __toESM(require("fs"));
38090
- var path46 = __toESM(require("path"));
38836
+ var path47 = __toESM(require("path"));
38091
38837
  var crypto7 = __toESM(require("crypto"));
38092
38838
  var HASH_NS = "wairon:token:v1";
38093
38839
  function hashToken(token) {
38094
38840
  return crypto7.createHash("sha256").update(`${HASH_NS}:${token}`).digest("hex");
38095
38841
  }
38096
38842
  function storePath(dataDir) {
38097
- return path46.join(dataDir, "auth", "credentials.json");
38843
+ return path47.join(dataDir, "auth", "credentials.json");
38098
38844
  }
38099
38845
  function load3(dataDir) {
38100
38846
  try {
@@ -38105,7 +38851,7 @@ function load3(dataDir) {
38105
38851
  }
38106
38852
  function save(dataDir, records) {
38107
38853
  const p = storePath(dataDir);
38108
- fs31.mkdirSync(path46.dirname(p), { recursive: true });
38854
+ fs31.mkdirSync(path47.dirname(p), { recursive: true });
38109
38855
  const tmp = `${p}.tmp`;
38110
38856
  fs31.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
38111
38857
  fs31.renameSync(tmp, p);
@@ -38154,10 +38900,10 @@ function listByOwner(dataDir, ownerUserId) {
38154
38900
 
38155
38901
  // src/server/websessions.ts
38156
38902
  var fs32 = __toESM(require("fs"));
38157
- var path47 = __toESM(require("path"));
38903
+ var path48 = __toESM(require("path"));
38158
38904
  var crypto8 = __toESM(require("crypto"));
38159
38905
  function storePath2(dataDir) {
38160
- return path47.join(dataDir, "web-sessions.json");
38906
+ return path48.join(dataDir, "web-sessions.json");
38161
38907
  }
38162
38908
  function readSessions(dataDir) {
38163
38909
  const p = storePath2(dataDir);
@@ -38178,7 +38924,7 @@ function readSessions(dataDir) {
38178
38924
  }
38179
38925
  function persistSessions(dataDir, sessions) {
38180
38926
  const p = storePath2(dataDir);
38181
- fs32.mkdirSync(path47.dirname(p), { recursive: true });
38927
+ fs32.mkdirSync(path48.dirname(p), { recursive: true });
38182
38928
  const tmp = `${p}.tmp`;
38183
38929
  fs32.writeFileSync(tmp, JSON.stringify(sessions, null, 2) + "\n");
38184
38930
  fs32.renameSync(tmp, p);
@@ -38343,10 +39089,10 @@ function listWebSessionsBySubject(dataDir, userId) {
38343
39089
 
38344
39090
  // src/server/users.ts
38345
39091
  var fs33 = __toESM(require("fs"));
38346
- var path48 = __toESM(require("path"));
39092
+ var path49 = __toESM(require("path"));
38347
39093
  var VALID_STATUSES = ["active", "inactive", "suspended", "deactivated", "disabled"];
38348
39094
  function storePath3(dataDir) {
38349
- return path48.join(dataDir, "users.json");
39095
+ return path49.join(dataDir, "users.json");
38350
39096
  }
38351
39097
  function loadStore(dataDir) {
38352
39098
  const p = storePath3(dataDir);
@@ -38370,7 +39116,7 @@ function loadStore(dataDir) {
38370
39116
  }
38371
39117
  function replaceAll(dataDir, records) {
38372
39118
  const p = storePath3(dataDir);
38373
- fs33.mkdirSync(path48.dirname(p), { recursive: true });
39119
+ fs33.mkdirSync(path49.dirname(p), { recursive: true });
38374
39120
  const tmp = `${p}.tmp`;
38375
39121
  fs33.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
38376
39122
  fs33.renameSync(tmp, p);
@@ -38476,10 +39222,10 @@ function remapUnitReferences(dataDir, remap, removedScopeIds) {
38476
39222
 
38477
39223
  // src/server/instance.ts
38478
39224
  var fs34 = __toESM(require("fs"));
38479
- var path49 = __toESM(require("path"));
39225
+ var path50 = __toESM(require("path"));
38480
39226
  var import_crypto2 = require("crypto");
38481
39227
  function storePath4(dataDir) {
38482
- return path49.join(dataDir, "instance.json");
39228
+ return path50.join(dataDir, "instance.json");
38483
39229
  }
38484
39230
  var InstanceIdentityStore = class {
38485
39231
  constructor(dataDir) {
@@ -38519,7 +39265,7 @@ var InstanceIdentityStore = class {
38519
39265
  * never truncates the file. Only called by the registry's create-once seed. */
38520
39266
  replace(identity) {
38521
39267
  const p = storePath4(this.dataDir);
38522
- fs34.mkdirSync(path49.dirname(p), { recursive: true });
39268
+ fs34.mkdirSync(path50.dirname(p), { recursive: true });
38523
39269
  const tmp = `${p}.tmp`;
38524
39270
  fs34.writeFileSync(tmp, JSON.stringify(identity, null, 2) + "\n");
38525
39271
  fs34.renameSync(tmp, p);
@@ -38579,7 +39325,7 @@ function getInstanceIdentity(dataDir) {
38579
39325
 
38580
39326
  // src/utils/secrets.ts
38581
39327
  var fs35 = __toESM(require("fs"));
38582
- var path50 = __toESM(require("path"));
39328
+ var path51 = __toESM(require("path"));
38583
39329
  var ENV_FALLBACK = {
38584
39330
  "git-token": ["WAIRON_GIT_TOKEN"],
38585
39331
  "notion-token": ["WAIRON_NOTION_TOKEN"],
@@ -38588,7 +39334,7 @@ var ENV_FALLBACK = {
38588
39334
  };
38589
39335
  function storePath5() {
38590
39336
  const dataDir = process.env["WAIRON_DATA_DIR"];
38591
- return dataDir ? path50.join(dataDir, "auth", "secrets.json") : null;
39337
+ return dataDir ? path51.join(dataDir, "auth", "secrets.json") : null;
38592
39338
  }
38593
39339
  function readStore() {
38594
39340
  const p = storePath5();
@@ -38619,7 +39365,7 @@ function setSecret(key, value) {
38619
39365
  if (!p) throw new Error("WAIRON_DATA_DIR is not set \u2014 a running server needs it to store secrets.");
38620
39366
  const store = readStore();
38621
39367
  store[key] = value;
38622
- fs35.mkdirSync(path50.dirname(p), { recursive: true });
39368
+ fs35.mkdirSync(path51.dirname(p), { recursive: true });
38623
39369
  const tmp = `${p}.tmp`;
38624
39370
  fs35.writeFileSync(tmp, JSON.stringify(store, null, 2) + "\n");
38625
39371
  fs35.renameSync(tmp, p);
@@ -38831,10 +39577,10 @@ function verifySsoState(state) {
38831
39577
 
38832
39578
  // src/server/organization.ts
38833
39579
  var fs36 = __toESM(require("fs"));
38834
- var path51 = __toESM(require("path"));
39580
+ var path52 = __toESM(require("path"));
38835
39581
  var crypto10 = __toESM(require("crypto"));
38836
39582
  function storePath6(dataDir) {
38837
- return path51.join(dataDir, "organization.json");
39583
+ return path52.join(dataDir, "organization.json");
38838
39584
  }
38839
39585
  function readState(dataDir) {
38840
39586
  const p = storePath6(dataDir);
@@ -38857,7 +39603,7 @@ function readState(dataDir) {
38857
39603
  }
38858
39604
  function persistState(dataDir, state) {
38859
39605
  const p = storePath6(dataDir);
38860
- fs36.mkdirSync(path51.dirname(p), { recursive: true });
39606
+ fs36.mkdirSync(path52.dirname(p), { recursive: true });
38861
39607
  const tmp = `${p}.tmp`;
38862
39608
  fs36.writeFileSync(tmp, JSON.stringify(state, null, 2) + "\n");
38863
39609
  fs36.renameSync(tmp, p);
@@ -39237,10 +39983,10 @@ function getOrganizationUnit(dataDir, id) {
39237
39983
 
39238
39984
  // src/server/permissions.ts
39239
39985
  var fs37 = __toESM(require("fs"));
39240
- var path52 = __toESM(require("path"));
39986
+ var path53 = __toESM(require("path"));
39241
39987
  var import_crypto3 = require("crypto");
39242
39988
  function storePath7(dataDir) {
39243
- return path52.join(dataDir, "permissions.json");
39989
+ return path53.join(dataDir, "permissions.json");
39244
39990
  }
39245
39991
  function assignmentKey(a) {
39246
39992
  return [a.subjectKind, a.subjectId ?? "", a.scopeKind, a.scopeId ?? "", a.capability].join("|");
@@ -39267,7 +40013,7 @@ function load4(dataDir) {
39267
40013
  }
39268
40014
  function replaceAll2(dataDir, assignments) {
39269
40015
  const p = storePath7(dataDir);
39270
- fs37.mkdirSync(path52.dirname(p), { recursive: true });
40016
+ fs37.mkdirSync(path53.dirname(p), { recursive: true });
39271
40017
  const tmp = `${p}.tmp`;
39272
40018
  fs37.writeFileSync(tmp, JSON.stringify(assignments, null, 2) + "\n");
39273
40019
  fs37.renameSync(tmp, p);
@@ -39357,7 +40103,7 @@ function getAssignment(dataDir, assignmentId) {
39357
40103
 
39358
40104
  // src/server/roles.ts
39359
40105
  var fs38 = __toESM(require("fs"));
39360
- var path53 = __toESM(require("path"));
40106
+ var path54 = __toESM(require("path"));
39361
40107
  var BUILTIN_ROLES = [
39362
40108
  {
39363
40109
  id: SSO_ADMIN_ROLE_ID,
@@ -39375,7 +40121,7 @@ function isBuiltinRoleId(roleId) {
39375
40121
  return BUILTIN_ROLE_IDS.has(roleId);
39376
40122
  }
39377
40123
  function storePath8(dataDir) {
39378
- return path53.join(dataDir, "roles.json");
40124
+ return path54.join(dataDir, "roles.json");
39379
40125
  }
39380
40126
  function load5(dataDir) {
39381
40127
  const p = storePath8(dataDir);
@@ -39399,7 +40145,7 @@ function load5(dataDir) {
39399
40145
  }
39400
40146
  function replaceAll3(dataDir, roles) {
39401
40147
  const p = storePath8(dataDir);
39402
- fs38.mkdirSync(path53.dirname(p), { recursive: true });
40148
+ fs38.mkdirSync(path54.dirname(p), { recursive: true });
39403
40149
  const tmp = `${p}.tmp`;
39404
40150
  fs38.writeFileSync(tmp, JSON.stringify(roles, null, 2) + "\n");
39405
40151
  fs38.renameSync(tmp, p);
@@ -39637,7 +40383,7 @@ function actionableUnitIds(scopes) {
39637
40383
 
39638
40384
  // src/server/projects.ts
39639
40385
  var fs39 = __toESM(require("fs"));
39640
- var path54 = __toESM(require("path"));
40386
+ var path55 = __toESM(require("path"));
39641
40387
  init_paths();
39642
40388
  init_yaml();
39643
40389
  init_fs();
@@ -39651,7 +40397,7 @@ function isValidProjectId(id) {
39651
40397
  return typeof id === "string" && ID_RE.test(id);
39652
40398
  }
39653
40399
  function registryPath(dataDir) {
39654
- return path54.join(dataDir, "projects.json");
40400
+ return path55.join(dataDir, "projects.json");
39655
40401
  }
39656
40402
  function load6(dataDir) {
39657
40403
  try {
@@ -39662,13 +40408,13 @@ function load6(dataDir) {
39662
40408
  }
39663
40409
  function save2(dataDir, records) {
39664
40410
  const p = registryPath(dataDir);
39665
- fs39.mkdirSync(path54.dirname(p), { recursive: true });
40411
+ fs39.mkdirSync(path55.dirname(p), { recursive: true });
39666
40412
  const tmp = `${p}.tmp`;
39667
40413
  fs39.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
39668
40414
  fs39.renameSync(tmp, p);
39669
40415
  }
39670
40416
  function projectRoot(dataDir, id) {
39671
- return path54.join(dataDir, "projects", id);
40417
+ return path55.join(dataDir, "projects", id);
39672
40418
  }
39673
40419
  function existingProjectRoot(dataDir, id) {
39674
40420
  if (!isValidProjectId(id)) return null;
@@ -39821,7 +40567,7 @@ function resolveProjectRoot(dataDir, principal, selector) {
39821
40567
 
39822
40568
  // src/git/config.ts
39823
40569
  var fs40 = __toESM(require("fs"));
39824
- var path55 = __toESM(require("path"));
40570
+ var path56 = __toESM(require("path"));
39825
40571
  init_fs();
39826
40572
  function configPath() {
39827
40573
  return aiDir("git.json");
@@ -39835,7 +40581,7 @@ function readGitConfig() {
39835
40581
  }
39836
40582
  function writeGitConfig(config) {
39837
40583
  const p = configPath();
39838
- fs40.mkdirSync(path55.dirname(p), { recursive: true });
40584
+ fs40.mkdirSync(path56.dirname(p), { recursive: true });
39839
40585
  const tmp = `${p}.tmp`;
39840
40586
  fs40.writeFileSync(tmp, JSON.stringify(config, null, 2) + "\n");
39841
40587
  fs40.renameSync(tmp, p);
@@ -39850,7 +40596,7 @@ function clearGitConfig() {
39850
40596
  // src/git/adapter.ts
39851
40597
  var import_child_process5 = require("child_process");
39852
40598
  var fs41 = __toESM(require("fs"));
39853
- var path56 = __toESM(require("path"));
40599
+ var path57 = __toESM(require("path"));
39854
40600
  init_fs();
39855
40601
  function git(args, cwd) {
39856
40602
  return (0, import_child_process5.execFileSync)("git", args, {
@@ -39901,9 +40647,9 @@ function compareUrl(remote, defaultBranch, workingBranch) {
39901
40647
  return `${web}/compare/${encodeURIComponent(defaultBranch)}...${encodeURIComponent(workingBranch)}`;
39902
40648
  }
39903
40649
  function excludeLocalFiles() {
39904
- const excludePath = path56.join(getProjectRoot(), ".git", "info", "exclude");
40650
+ const excludePath = path57.join(getProjectRoot(), ".git", "info", "exclude");
39905
40651
  try {
39906
- fs41.mkdirSync(path56.dirname(excludePath), { recursive: true });
40652
+ fs41.mkdirSync(path57.dirname(excludePath), { recursive: true });
39907
40653
  const existing = fs41.existsSync(excludePath) ? fs41.readFileSync(excludePath, "utf8") : "";
39908
40654
  const lines = existing.split(/\r?\n/).filter((line2) => line2.length > 0);
39909
40655
  const hadLegacyLockExclusion = lines.some((line2) => line2.trim() === ".wai/lock.json");
@@ -39982,7 +40728,7 @@ function configureSync(periodicSyncMinutes, skipIfClean) {
39982
40728
 
39983
40729
  // src/producers/config.ts
39984
40730
  var fs42 = __toESM(require("fs"));
39985
- var path57 = __toESM(require("path"));
40731
+ var path58 = __toESM(require("path"));
39986
40732
  init_fs();
39987
40733
  function configPath2() {
39988
40734
  return aiDir("producers.json");
@@ -39996,7 +40742,7 @@ function load7() {
39996
40742
  }
39997
40743
  function save3(configs) {
39998
40744
  const p = configPath2();
39999
- fs42.mkdirSync(path57.dirname(p), { recursive: true });
40745
+ fs42.mkdirSync(path58.dirname(p), { recursive: true });
40000
40746
  const tmp = `${p}.tmp`;
40001
40747
  fs42.writeFileSync(tmp, JSON.stringify(configs, null, 2) + "\n");
40002
40748
  fs42.renameSync(tmp, p);
@@ -40625,8 +41371,8 @@ async function produceProducer(cfg, credential, project2, target) {
40625
41371
  const configured = runWithProjectRoot(root, () => list()).some((p) => p.target === target);
40626
41372
  const token = configured ? resolveSecret(`${target}-token`) : null;
40627
41373
  if (configured && !token) {
40628
- const label2 = target.charAt(0).toUpperCase() + target.slice(1);
40629
- throw new Error(`No ${label2} token \u2014 set WAIRON_${target.toUpperCase()}_TOKEN or run \`wairon host secret set ${target}-token <secret>\`.`);
41374
+ const label3 = target.charAt(0).toUpperCase() + target.slice(1);
41375
+ throw new Error(`No ${label3} token \u2014 set WAIRON_${target.toUpperCase()}_TOKEN or run \`wairon host secret set ${target}-token <secret>\`.`);
40630
41376
  }
40631
41377
  await runWithProjectRoot(root, () => produce(target, diagramUrl, token ?? ""));
40632
41378
  }
@@ -40686,7 +41432,7 @@ function diagramViewLink(cfg, credential, project2) {
40686
41432
 
40687
41433
  // src/server/packs.ts
40688
41434
  var fs43 = __toESM(require("fs"));
40689
- var path58 = __toESM(require("path"));
41435
+ var path59 = __toESM(require("path"));
40690
41436
  init_fs();
40691
41437
  init_yaml();
40692
41438
 
@@ -40742,7 +41488,7 @@ function assertNotCodeArchive(info) {
40742
41488
  function extractDeclarativeArchive(archive, destDir) {
40743
41489
  const result = extractArchive(archive, destDir, HOSTED_STRICT_LIMITS);
40744
41490
  try {
40745
- assertDeclarative(fs43.readFileSync(path58.join(result.directory, result.entryPath), "utf8"));
41491
+ assertDeclarative(fs43.readFileSync(path59.join(result.directory, result.entryPath), "utf8"));
40746
41492
  } catch (e) {
40747
41493
  fs43.rmSync(destDir, { recursive: true, force: true });
40748
41494
  throw e;
@@ -40751,10 +41497,10 @@ function extractDeclarativeArchive(archive, destDir) {
40751
41497
  function probe(loadRef, baseRoot, scope, displayRef) {
40752
41498
  const loaded = loadExtensionPacks([{ ref: loadRef, scope }], baseRoot);
40753
41499
  if (loaded.errors.length) {
40754
- return { name: path58.basename(displayRef), scope, ref: displayRef, profiles: 0, languages: 0, rules: 0, error: loaded.errors[0] };
41500
+ return { name: path59.basename(displayRef), scope, ref: displayRef, profiles: 0, languages: 0, rules: 0, error: loaded.errors[0] };
40755
41501
  }
40756
41502
  return {
40757
- name: loaded.packNames[0] ?? path58.basename(displayRef),
41503
+ name: loaded.packNames[0] ?? path59.basename(displayRef),
40758
41504
  scope,
40759
41505
  ref: displayRef,
40760
41506
  profiles: Object.keys(loaded.profiles).length,
@@ -40768,20 +41514,20 @@ function probe(loadRef, baseRoot, scope, displayRef) {
40768
41514
  };
40769
41515
  }
40770
41516
  function writeFileAtomic(file, content) {
40771
- fs43.mkdirSync(path58.dirname(file), { recursive: true });
41517
+ fs43.mkdirSync(path59.dirname(file), { recursive: true });
40772
41518
  const tmp = `${file}.tmp`;
40773
41519
  fs43.writeFileSync(tmp, content);
40774
41520
  fs43.renameSync(tmp, file);
40775
41521
  }
40776
41522
  function stem(ref) {
40777
- return path58.basename(ref).replace(PACK_EXT_RE2, "");
41523
+ return path59.basename(ref).replace(PACK_EXT_RE2, "");
40778
41524
  }
40779
41525
  function imagePacksDir() {
40780
41526
  return process.env.WAIRON_IMAGE_PACKS_DIR ?? "/opt/wairon/packs";
40781
41527
  }
40782
41528
  function probeTier(dir, tier) {
40783
41529
  return discoverPacks(dir).map((full) => {
40784
- const d = probe(full, path58.dirname(full), "global", path58.basename(full));
41530
+ const d = probe(full, path59.dirname(full), "global", path59.basename(full));
40785
41531
  d.tier = tier;
40786
41532
  return d;
40787
41533
  });
@@ -40799,9 +41545,9 @@ function scanGlobalPackProfiles() {
40799
41545
  for (const dir of [globalPacksDir(), imagePacksDir()]) {
40800
41546
  for (const full of discoverPacks(dir)) {
40801
41547
  try {
40802
- const loaded = loadExtensionPacks([{ ref: full, scope: "global" }], path58.dirname(full));
41548
+ const loaded = loadExtensionPacks([{ ref: full, scope: "global" }], path59.dirname(full));
40803
41549
  if (loaded.errors.length) continue;
40804
- const source = loaded.packNames[0] ?? path58.basename(full);
41550
+ const source = loaded.packNames[0] ?? path59.basename(full);
40805
41551
  for (const [id, def] of Object.entries(loaded.profiles)) out.push({ id, source, family: def.family });
40806
41552
  } catch {
40807
41553
  }
@@ -40857,7 +41603,7 @@ function readPackContent(full) {
40857
41603
  if (st.isFile()) return fs43.readFileSync(full, "utf8");
40858
41604
  if (st.isDirectory()) {
40859
41605
  for (const entry of ["pack.yaml", "pack.yml"]) {
40860
- const p = path58.join(full, entry);
41606
+ const p = path59.join(full, entry);
40861
41607
  if (fs43.existsSync(p) && fs43.statSync(p).isFile()) return fs43.readFileSync(p, "utf8");
40862
41608
  }
40863
41609
  }
@@ -40867,9 +41613,9 @@ function storeResolveGlobalPacks(names) {
40867
41613
  const index = /* @__PURE__ */ new Map();
40868
41614
  const indexTier = (dir, tier) => {
40869
41615
  for (const full of discoverPacks(dir)) {
40870
- const manifestName = probe(full, path58.dirname(full), "global", path58.basename(full)).name;
41616
+ const manifestName = probe(full, path59.dirname(full), "global", path59.basename(full)).name;
40871
41617
  const candidate = { full, manifestName, tier };
40872
- for (const key of [manifestName, stem(full), path58.basename(full)]) {
41618
+ for (const key of [manifestName, stem(full), path59.basename(full)]) {
40873
41619
  if (!index.has(key)) index.set(key, candidate);
40874
41620
  }
40875
41621
  }
@@ -40896,7 +41642,7 @@ function storeInstallGlobalPack(name, content) {
40896
41642
  assertName(name);
40897
41643
  assertDeclarative(content);
40898
41644
  const dir = globalPacksDir();
40899
- const file = path58.join(dir, `${name}.yaml`);
41645
+ const file = path59.join(dir, `${name}.yaml`);
40900
41646
  writeFileAtomic(file, content);
40901
41647
  const descriptor = probe(file, dir, "global", `${name}.yaml`);
40902
41648
  descriptor.tier = "instance";
@@ -40907,7 +41653,7 @@ function storeInstallGlobalPackArchive(archive, name) {
40907
41653
  assertNotCodeArchive(info);
40908
41654
  const packName = name ?? info.name;
40909
41655
  assertName(packName);
40910
- const dir = path58.join(globalPacksDir(), packName);
41656
+ const dir = path59.join(globalPacksDir(), packName);
40911
41657
  extractDeclarativeArchive(archive, dir);
40912
41658
  const descriptor = probe(dir, globalPacksDir(), "global", packName);
40913
41659
  descriptor.tier = "instance";
@@ -40918,7 +41664,7 @@ function declaredName2(loadRef, baseRoot, scope) {
40918
41664
  return d.error ? void 0 : d.name;
40919
41665
  }
40920
41666
  function pickByName(candidates, name, fileOf, manifestOf) {
40921
- const byFile = candidates.find((c) => stem(fileOf(c)) === name || path58.basename(fileOf(c)) === name);
41667
+ const byFile = candidates.find((c) => stem(fileOf(c)) === name || path59.basename(fileOf(c)) === name);
40922
41668
  if (byFile !== void 0) return byFile;
40923
41669
  const byManifest = candidates.filter((c) => manifestOf(c) === name);
40924
41670
  if (byManifest.length > 1) {
@@ -40931,11 +41677,11 @@ function pickByName(candidates, name, fileOf, manifestOf) {
40931
41677
  function storeRemoveGlobalPack(name) {
40932
41678
  assertName(name);
40933
41679
  const dir = globalPacksDir();
40934
- const manifestOf = (ref) => declaredName2(ref, path58.dirname(ref), "global");
40935
- const match = pickByName(discoverPacks(dir), name, path58.basename, manifestOf);
41680
+ const manifestOf = (ref) => declaredName2(ref, path59.dirname(ref), "global");
41681
+ const match = pickByName(discoverPacks(dir), name, path59.basename, manifestOf);
40936
41682
  if (!match) {
40937
41683
  const inImage = discoverPacks(imagePacksDir()).some(
40938
- (ref) => stem(ref) === name || path58.basename(ref) === name || manifestOf(ref) === name
41684
+ (ref) => stem(ref) === name || path59.basename(ref) === name || manifestOf(ref) === name
40939
41685
  );
40940
41686
  throw new Error(
40941
41687
  inImage ? `Pack "${name}" is an immutable image-layer pack (WAIRON_IMAGE_PACKS_DIR) and cannot be removed at runtime; rebuild the extension image without it.` : `No server-global instance pack named "${name}".`
@@ -40947,9 +41693,9 @@ function storeListProjectPacks(config) {
40947
41693
  const root = getProjectRoot();
40948
41694
  const entries = config?.extensions?.packs ?? [];
40949
41695
  return entries.map((entry) => {
40950
- const label2 = packEntryLabel(entry);
41696
+ const label3 = packEntryLabel(entry);
40951
41697
  const ref = packEntryRef(entry, root);
40952
- return probe(ref ?? label2, root, "project", label2);
41698
+ return probe(ref ?? label3, root, "project", label3);
40953
41699
  });
40954
41700
  }
40955
41701
  function storeInstallProjectPack(name, content) {
@@ -40957,7 +41703,7 @@ function storeInstallProjectPack(name, content) {
40957
41703
  assertDeclarative(content);
40958
41704
  const root = getProjectRoot();
40959
41705
  const relRef = `.wai/packs/${name}.yaml`;
40960
- writeFileAtomic(path58.join(root, ".wai", "packs", `${name}.yaml`), content);
41706
+ writeFileAtomic(path59.join(root, ".wai", "packs", `${name}.yaml`), content);
40961
41707
  return probe(relRef, root, "project", relRef);
40962
41708
  }
40963
41709
  function storeInstallProjectPackArchive(archive, name) {
@@ -40967,15 +41713,15 @@ function storeInstallProjectPackArchive(archive, name) {
40967
41713
  assertName(packName);
40968
41714
  const root = getProjectRoot();
40969
41715
  const relRef = `.wai/packs/${packName}`;
40970
- extractDeclarativeArchive(archive, path58.join(root, ".wai", "packs", packName));
41716
+ extractDeclarativeArchive(archive, path59.join(root, ".wai", "packs", packName));
40971
41717
  return probe(relRef, root, "project", relRef);
40972
41718
  }
40973
41719
  function storeRemoveProjectPack(name, ref) {
40974
41720
  assertName(name);
40975
41721
  const root = getProjectRoot();
40976
- const resolved = path58.resolve(root, ref ?? path58.join(".wai", "packs", name));
40977
- const vendorDir = path58.resolve(root, ".wai", "packs");
40978
- if (resolved.startsWith(vendorDir + path58.sep)) {
41722
+ const resolved = path59.resolve(root, ref ?? path59.join(".wai", "packs", name));
41723
+ const vendorDir = path59.resolve(root, ".wai", "packs");
41724
+ if (resolved.startsWith(vendorDir + path59.sep)) {
40979
41725
  fs43.rmSync(resolved, { recursive: true, force: true });
40980
41726
  }
40981
41727
  }
@@ -40998,7 +41744,7 @@ function boundProject2(cfg, project2) {
40998
41744
  function findPackRegistration(config, name) {
40999
41745
  const relRef = `.wai/packs/${name}.yaml`;
41000
41746
  const entries = config?.extensions?.packs ?? [];
41001
- const direct = entries.find((entry) => typeof entry === "string" ? entry === relRef || stem(entry) === name || path58.basename(entry) === name : entry.name === name);
41747
+ const direct = entries.find((entry) => typeof entry === "string" ? entry === relRef || stem(entry) === name || path59.basename(entry) === name : entry.name === name);
41002
41748
  if (direct !== void 0) return direct;
41003
41749
  const root = getProjectRoot();
41004
41750
  const refs = entries.filter((entry) => typeof entry === "string");
@@ -41113,7 +41859,7 @@ function installProjectPackArchive(cfg, credential, project2, archive, name) {
41113
41859
 
41114
41860
  // src/server/audit.ts
41115
41861
  var fs44 = __toESM(require("fs"));
41116
- var path59 = __toESM(require("path"));
41862
+ var path60 = __toESM(require("path"));
41117
41863
  var crypto12 = __toESM(require("crypto"));
41118
41864
  var LEVEL_ORDER = {
41119
41865
  debug: 0,
@@ -41136,7 +41882,7 @@ var DEFAULT_AUDIT_POLICY = {
41136
41882
  metadataMode: "redacted"
41137
41883
  };
41138
41884
  function storePath9(dataDir) {
41139
- return path59.join(dataDir, "audit-events.json");
41885
+ return path60.join(dataDir, "audit-events.json");
41140
41886
  }
41141
41887
  function readEvents(dataDir) {
41142
41888
  const p = storePath9(dataDir);
@@ -41157,7 +41903,7 @@ function readEvents(dataDir) {
41157
41903
  }
41158
41904
  function persistEvents(dataDir, events) {
41159
41905
  const p = storePath9(dataDir);
41160
- fs44.mkdirSync(path59.dirname(p), { recursive: true });
41906
+ fs44.mkdirSync(path60.dirname(p), { recursive: true });
41161
41907
  const tmp = `${p}.tmp`;
41162
41908
  fs44.writeFileSync(tmp, JSON.stringify(events, null, 2) + "\n");
41163
41909
  fs44.renameSync(tmp, p);
@@ -41849,7 +42595,7 @@ function assertAllowedRedirectUri(provider, redirectUri) {
41849
42595
 
41850
42596
  // src/server/policy.ts
41851
42597
  var fs45 = __toESM(require("fs"));
41852
- var path60 = __toESM(require("path"));
42598
+ var path61 = __toESM(require("path"));
41853
42599
  init_fs();
41854
42600
 
41855
42601
  // src/server/httpio.ts
@@ -41866,7 +42612,7 @@ function sendJson(res, status2, body) {
41866
42612
 
41867
42613
  // src/server/policy.ts
41868
42614
  function packPolicyPath(dataDir) {
41869
- return path60.join(dataDir, "pack-policy.json");
42615
+ return path61.join(dataDir, "pack-policy.json");
41870
42616
  }
41871
42617
  function getPackPolicyRecord(dataDir) {
41872
42618
  const p = packPolicyPath(dataDir);
@@ -41886,14 +42632,14 @@ function getPackPolicyRecord(dataDir) {
41886
42632
  function setPackPolicyRecord(dataDir, policy) {
41887
42633
  const stored = { ...policy, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
41888
42634
  const p = packPolicyPath(dataDir);
41889
- fs45.mkdirSync(path60.dirname(p), { recursive: true });
42635
+ fs45.mkdirSync(path61.dirname(p), { recursive: true });
41890
42636
  const tmp = `${p}.tmp`;
41891
42637
  fs45.writeFileSync(tmp, JSON.stringify(stored, null, 2) + "\n");
41892
42638
  fs45.renameSync(tmp, p);
41893
42639
  return stored;
41894
42640
  }
41895
42641
  function exposurePolicyPath(dataDir) {
41896
- return path60.join(dataDir, "exposure-policy.json");
42642
+ return path61.join(dataDir, "exposure-policy.json");
41897
42643
  }
41898
42644
  var COMPATIBLE_DEFAULT_EXPOSURE = {
41899
42645
  adminApiMode: "local_only",
@@ -41923,7 +42669,7 @@ function getExposurePolicyRecord(dataDir) {
41923
42669
  }
41924
42670
  function setExposurePolicyRecord(dataDir, policy) {
41925
42671
  const p = exposurePolicyPath(dataDir);
41926
- fs45.mkdirSync(path60.dirname(p), { recursive: true });
42672
+ fs45.mkdirSync(path61.dirname(p), { recursive: true });
41927
42673
  const tmp = `${p}.tmp`;
41928
42674
  fs45.writeFileSync(tmp, JSON.stringify(policy, null, 2) + "\n");
41929
42675
  fs45.renameSync(tmp, p);
@@ -41944,7 +42690,7 @@ function effectivePolicy(dataDir) {
41944
42690
  return getPackPolicyRecord(dataDir) ?? PERMISSIVE_DEFAULT_POLICY;
41945
42691
  }
41946
42692
  function identityProvidersPath(dataDir) {
41947
- return path60.join(dataDir, "identity-providers.json");
42693
+ return path61.join(dataDir, "identity-providers.json");
41948
42694
  }
41949
42695
  function sanitizeIdentityProvider(config) {
41950
42696
  const clean = {
@@ -41968,7 +42714,7 @@ function sanitizeIdentityProvider(config) {
41968
42714
  }
41969
42715
  function writeIdentityProviderRecords(dataDir, records) {
41970
42716
  const p = identityProvidersPath(dataDir);
41971
- fs45.mkdirSync(path60.dirname(p), { recursive: true });
42717
+ fs45.mkdirSync(path61.dirname(p), { recursive: true });
41972
42718
  const tmp = `${p}.tmp`;
41973
42719
  fs45.writeFileSync(tmp, JSON.stringify(records, null, 2) + "\n");
41974
42720
  fs45.renameSync(tmp, p);
@@ -42171,8 +42917,8 @@ function buildEvaluation(input) {
42171
42917
  missingPackNames = requiredDefaultNames(policy).filter((n) => !present.has(n));
42172
42918
  unresolvedPacks = [];
42173
42919
  }
42174
- const blocked = new Set(policy.blockedPackNames ?? []);
42175
- const blockedPackNames = presentPackNames.filter((n) => blocked.has(n));
42920
+ const blocked2 = new Set(policy.blockedPackNames ?? []);
42921
+ const blockedPackNames = presentPackNames.filter((n) => blocked2.has(n));
42176
42922
  const requiredProfileIds = policy.requiredProfileIds ?? [];
42177
42923
  const selected = new Set(selectedProfileIds);
42178
42924
  const missingProfileIds = requiredProfileIds.filter((id) => !selected.has(id));
@@ -43014,10 +43760,10 @@ init_fs();
43014
43760
 
43015
43761
  // src/server/relations.ts
43016
43762
  var fs46 = __toESM(require("fs"));
43017
- var path61 = __toESM(require("path"));
43763
+ var path62 = __toESM(require("path"));
43018
43764
  var crypto15 = __toESM(require("crypto"));
43019
43765
  function storePath10(dataDir) {
43020
- return path61.join(dataDir, "relations.json");
43766
+ return path62.join(dataDir, "relations.json");
43021
43767
  }
43022
43768
  function readRelations(dataDir) {
43023
43769
  const p = storePath10(dataDir);
@@ -43038,7 +43784,7 @@ function readRelations(dataDir) {
43038
43784
  }
43039
43785
  function persistRelations(dataDir, relations) {
43040
43786
  const p = storePath10(dataDir);
43041
- fs46.mkdirSync(path61.dirname(p), { recursive: true });
43787
+ fs46.mkdirSync(path62.dirname(p), { recursive: true });
43042
43788
  const tmp = `${p}.tmp`;
43043
43789
  fs46.writeFileSync(tmp, JSON.stringify(relations, null, 2) + "\n");
43044
43790
  fs46.renameSync(tmp, p);
@@ -43152,9 +43898,9 @@ function listProjectRelations(dataDir, sourceProjectId, targetProjectId, status2
43152
43898
 
43153
43899
  // src/server/surfaces.ts
43154
43900
  var fs47 = __toESM(require("fs"));
43155
- var path62 = __toESM(require("path"));
43901
+ var path63 = __toESM(require("path"));
43156
43902
  function storePath11(dataDir) {
43157
- return path62.join(dataDir, "public-surfaces.json");
43903
+ return path63.join(dataDir, "public-surfaces.json");
43158
43904
  }
43159
43905
  function readSnapshots(dataDir) {
43160
43906
  const p = storePath11(dataDir);
@@ -43175,7 +43921,7 @@ function readSnapshots(dataDir) {
43175
43921
  }
43176
43922
  function persistSnapshots(dataDir, snapshots) {
43177
43923
  const p = storePath11(dataDir);
43178
- fs47.mkdirSync(path62.dirname(p), { recursive: true });
43924
+ fs47.mkdirSync(path63.dirname(p), { recursive: true });
43179
43925
  const tmp = `${p}.tmp`;
43180
43926
  fs47.writeFileSync(tmp, JSON.stringify(snapshots, null, 2) + "\n");
43181
43927
  fs47.renameSync(tmp, p);
@@ -43321,12 +44067,12 @@ function resolveVisibility(observerProjectId, units, placements) {
43321
44067
  const best = /* @__PURE__ */ new Map();
43322
44068
  for (const placement of placements) {
43323
44069
  if (placement.projectId === observerProjectId) continue;
43324
- const path76 = chainOf(placement.unitId, unitById);
43325
- if (!path76.length) continue;
43326
- const closedOk = path76.every((u) => effectivePosture(u, unitById) !== "closed" || observerInside(u.id) || grantedTo(u));
44070
+ const path77 = chainOf(placement.unitId, unitById);
44071
+ if (!path77.length) continue;
44072
+ const closedOk = path77.every((u) => effectivePosture(u, unitById) !== "closed" || observerInside(u.id) || grantedTo(u));
43327
44073
  if (!closedOk) continue;
43328
- const crossTenant = !tenantRoots.has(path76[path76.length - 1].id);
43329
- if (crossTenant && !path76.some(grantedTo)) continue;
44074
+ const crossTenant = !tenantRoots.has(path77[path77.length - 1].id);
44075
+ if (crossTenant && !path77.some(grantedTo)) continue;
43330
44076
  if (crossTenant && directUnits.length === 0) continue;
43331
44077
  const distance = crossTenant ? "partner" : sameBranch(placement.unitId) ? "department" : "instance";
43332
44078
  const existing = best.get(placement.projectId);
@@ -43946,7 +44692,7 @@ function handleLandscapeRequest(cfg, credential, req, res, body, url) {
43946
44692
 
43947
44693
  // src/server/migration.ts
43948
44694
  var fs48 = __toESM(require("fs"));
43949
- var path63 = __toESM(require("path"));
44695
+ var path64 = __toESM(require("path"));
43950
44696
  var LEGACY_CAPABILITY_MAP = {
43951
44697
  "mcp:read": "project:read",
43952
44698
  "operations:read": "project:read",
@@ -43984,7 +44730,7 @@ function readRaw(file) {
43984
44730
  }
43985
44731
  }
43986
44732
  function writeRaw(file, value) {
43987
- fs48.mkdirSync(path63.dirname(file), { recursive: true });
44733
+ fs48.mkdirSync(path64.dirname(file), { recursive: true });
43988
44734
  const tmp = `${file}.tmp`;
43989
44735
  fs48.writeFileSync(tmp, JSON.stringify(value, null, 2) + "\n");
43990
44736
  fs48.renameSync(tmp, file);
@@ -44015,19 +44761,19 @@ function seedAssignment(dataDir, existing, subjectId, capability, scope) {
44015
44761
  existing.push({ subjectId, scopeKind: scope.scopeKind, scopeId: scope.scopeId, capability });
44016
44762
  return true;
44017
44763
  }
44018
- function migratePermissionModel(dataDir, apply) {
44764
+ function migratePermissionModel(dataDir, apply2) {
44019
44765
  const findings = [];
44020
44766
  const found = (area, detail) => {
44021
44767
  findings.push({ area, detail });
44022
44768
  };
44023
44769
  const grid = readRaw(
44024
- path63.join(dataDir, "permissions.json")
44770
+ path64.join(dataDir, "permissions.json")
44025
44771
  ) ?? [];
44026
44772
  if (!getInstanceIdentity(dataDir)) {
44027
44773
  found("instance", "instance identity is not seeded \u2014 the built-in admin/dev subjects have no persisted UUIDs");
44028
- if (apply) ensureInstanceIdentity(dataDir);
44774
+ if (apply2) ensureInstanceIdentity(dataDir);
44029
44775
  }
44030
- const orgPath = path63.join(dataDir, "organization.json");
44776
+ const orgPath = path64.join(dataDir, "organization.json");
44031
44777
  const org = readRaw(orgPath);
44032
44778
  if (org?.units?.length) {
44033
44779
  const units = org.units;
@@ -44055,14 +44801,14 @@ function migratePermissionModel(dataDir, apply) {
44055
44801
  newIds.set(unit.id, candidate);
44056
44802
  if (!unit.slug || unit.slug !== slug) {
44057
44803
  found("units", `unit "${unit.id}" gets slug "${slug}"`);
44058
- if (apply) unit.slug = slug;
44804
+ if (apply2) unit.slug = slug;
44059
44805
  }
44060
44806
  if (candidate !== unit.id) {
44061
44807
  found("units", `unit id "${unit.id}" becomes the qualified path "${candidate}"`);
44062
44808
  remap.push({ oldId: unit.id, newId: candidate });
44063
44809
  }
44064
44810
  }
44065
- if (apply) {
44811
+ if (apply2) {
44066
44812
  const lookup2 = new Map(remap.map((r) => [r.oldId, r.newId]));
44067
44813
  for (const unit of units) {
44068
44814
  unit.id = lookup2.get(unit.id) ?? unit.id;
@@ -44081,7 +44827,7 @@ function migratePermissionModel(dataDir, apply) {
44081
44827
  found("units", `${remap.length} unit id(s) would be remapped across placements, exposeTo, assignments, and users`);
44082
44828
  }
44083
44829
  }
44084
- const usersPath = path63.join(dataDir, "users.json");
44830
+ const usersPath = path64.join(dataDir, "users.json");
44085
44831
  const users = readRaw(usersPath);
44086
44832
  if (users) {
44087
44833
  let changed = false;
@@ -44095,7 +44841,7 @@ function migratePermissionModel(dataDir, apply) {
44095
44841
  }
44096
44842
  const subjectId = user.subject?.userId ?? user.id;
44097
44843
  found("users", `user "${user.id}": ${user.grants.length} stored grant(s) become assignments for subject "${subjectId}"`);
44098
- if (apply) {
44844
+ if (apply2) {
44099
44845
  for (const grant of user.grants) {
44100
44846
  const scope = scopeOf(grant);
44101
44847
  for (const capability of capabilitiesOf(grant.permissions)) {
@@ -44106,9 +44852,9 @@ function migratePermissionModel(dataDir, apply) {
44106
44852
  changed = true;
44107
44853
  }
44108
44854
  }
44109
- if (apply && changed) writeRaw(usersPath, users);
44855
+ if (apply2 && changed) writeRaw(usersPath, users);
44110
44856
  }
44111
- const keysPath = path63.join(dataDir, "auth", "credentials.json");
44857
+ const keysPath = path64.join(dataDir, "auth", "credentials.json");
44112
44858
  const keys = readRaw(keysPath);
44113
44859
  if (keys) {
44114
44860
  let changed = false;
@@ -44116,7 +44862,7 @@ function migratePermissionModel(dataDir, apply) {
44116
44862
  if (key.revokedAt) continue;
44117
44863
  if (isLegacyBuiltin(key.ownerSubject?.userId)) {
44118
44864
  found("keys", `key "${key.id}" is owned by the retired literal "${key.ownerSubject?.userId}" \u2014 revoked; re-mint for the seeded UUID subject`);
44119
- if (apply) {
44865
+ if (apply2) {
44120
44866
  key.revokedAt = (/* @__PURE__ */ new Date()).toISOString();
44121
44867
  changed = true;
44122
44868
  }
@@ -44126,7 +44872,7 @@ function migratePermissionModel(dataDir, apply) {
44126
44872
  if (legacyGrants?.length) {
44127
44873
  const projects = [...new Set(legacyGrants.map((g) => g.orgUnitId || !g.projectId ? "*" : g.projectId))];
44128
44874
  found("keys", `key "${key.id}": stored grants become the projects narrowing [${projects.join(", ")}]`);
44129
- if (apply) {
44875
+ if (apply2) {
44130
44876
  key.projects = projects.includes("*") ? ["*"] : projects;
44131
44877
  delete key.grants;
44132
44878
  changed = true;
@@ -44135,7 +44881,7 @@ function migratePermissionModel(dataDir, apply) {
44135
44881
  if (!key.ownerSubject) {
44136
44882
  const ownerId = `svc-key-${key.id}`;
44137
44883
  found("keys", `key "${key.id}" has no owner \u2014 bound to the synthesized service subject "${ownerId}" carrying its legacy authority`);
44138
- if (apply) {
44884
+ if (apply2) {
44139
44885
  key.ownerSubject = { userId: ownerId, kind: "service", issuer: "local" };
44140
44886
  const caps = [];
44141
44887
  if (legacyGrants?.length) {
@@ -44156,9 +44902,9 @@ function migratePermissionModel(dataDir, apply) {
44156
44902
  }
44157
44903
  }
44158
44904
  }
44159
- if (apply && changed) writeRaw(keysPath, keys);
44905
+ if (apply2 && changed) writeRaw(keysPath, keys);
44160
44906
  }
44161
- const sessionsPath = path63.join(dataDir, "web-sessions.json");
44907
+ const sessionsPath = path64.join(dataDir, "web-sessions.json");
44162
44908
  const sessions = readRaw(sessionsPath);
44163
44909
  if (sessions) {
44164
44910
  let changed = false;
@@ -44166,7 +44912,7 @@ function migratePermissionModel(dataDir, apply) {
44166
44912
  if (isLegacyBuiltin(s.subject?.userId)) {
44167
44913
  found("sessions", `session "${s.id}" belongs to the retired literal "${s.subject?.userId}" \u2014 dropped (re-login mints the UUID subject)`);
44168
44914
  changed = true;
44169
- return !apply;
44915
+ return !apply2;
44170
44916
  }
44171
44917
  return true;
44172
44918
  });
@@ -44174,19 +44920,19 @@ function migratePermissionModel(dataDir, apply) {
44174
44920
  if (!s.grants) continue;
44175
44921
  const projects = [...new Set(s.grants.map((g) => g.orgUnitId || !g.projectId ? "*" : g.projectId))];
44176
44922
  found("sessions", `session "${s.id}": stored grants become the projects narrowing [${projects.join(", ")}]`);
44177
- if (apply) {
44923
+ if (apply2) {
44178
44924
  s.projects = projects.length === 0 || projects.includes("*") ? ["*"] : projects;
44179
44925
  delete s.grants;
44180
44926
  changed = true;
44181
44927
  }
44182
44928
  }
44183
- if (apply && changed) writeRaw(sessionsPath, kept);
44929
+ if (apply2 && changed) writeRaw(sessionsPath, kept);
44184
44930
  }
44185
44931
  const placedIds = new Set(listProjectPlacements(dataDir).map((p) => p.projectId));
44186
44932
  const unplaced = listProjectRecords(dataDir).filter((r) => !placedIds.has(r.id));
44187
44933
  if (unplaced.length > 0) {
44188
44934
  found("projects", `${unplaced.length} project(s) are placed in no organization unit: ${unplaced.map((r) => r.id).join(", ")} \u2014 placed into the 'unassigned' root unit`);
44189
- if (apply) {
44935
+ if (apply2) {
44190
44936
  const system = { userId: "migration", kind: "service", issuer: "local" };
44191
44937
  if (!getOrganizationUnit(dataDir, "unassigned")) {
44192
44938
  createUnit(dataDir, {
@@ -44228,17 +44974,17 @@ function migratePermissionModel(dataDir, apply) {
44228
44974
  "placements",
44229
44975
  `project "${projectId}" carries ${rows.length} owner placements \u2014 keeping the newest (${sorted[0].unitId}), removing ${stale.map((p) => p.unitId).join(", ")}`
44230
44976
  );
44231
- if (apply) {
44977
+ if (apply2) {
44232
44978
  for (const p of stale) deletePlacement(dataDir, p.id);
44233
44979
  }
44234
44980
  }
44235
- return { findings, applied: apply };
44981
+ return { findings, applied: apply2 };
44236
44982
  }
44237
44983
 
44238
44984
  // src/server/http.ts
44239
44985
  var http2 = __toESM(require("http"));
44240
44986
  var fs56 = __toESM(require("fs"));
44241
- var path71 = __toESM(require("path"));
44987
+ var path72 = __toESM(require("path"));
44242
44988
 
44243
44989
  // src/server/request.ts
44244
44990
  var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
@@ -44252,10 +44998,10 @@ function createScopedServer() {
44252
44998
 
44253
44999
  // src/server/approvals.ts
44254
45000
  var fs49 = __toESM(require("fs"));
44255
- var path64 = __toESM(require("path"));
45001
+ var path65 = __toESM(require("path"));
44256
45002
  var crypto16 = __toESM(require("crypto"));
44257
45003
  function storePath12(dataDir) {
44258
- return path64.join(dataDir, "approvals.json");
45004
+ return path65.join(dataDir, "approvals.json");
44259
45005
  }
44260
45006
  function readRequests(dataDir) {
44261
45007
  const p = storePath12(dataDir);
@@ -44276,7 +45022,7 @@ function readRequests(dataDir) {
44276
45022
  }
44277
45023
  function persistRequests(dataDir, requests) {
44278
45024
  const p = storePath12(dataDir);
44279
- fs49.mkdirSync(path64.dirname(p), { recursive: true });
45025
+ fs49.mkdirSync(path65.dirname(p), { recursive: true });
44280
45026
  const tmp = `${p}.tmp`;
44281
45027
  fs49.writeFileSync(tmp, JSON.stringify(requests, null, 2) + "\n");
44282
45028
  fs49.renameSync(tmp, p);
@@ -44800,7 +45546,7 @@ async function awaitApproval(cfg, credential, requestId, timeoutSeconds) {
44800
45546
  let current2 = req;
44801
45547
  while (current2.status === "pending" && Date.now() < deadline) {
44802
45548
  const remaining = deadline - Date.now();
44803
- await new Promise((resolve36) => setTimeout(resolve36, Math.min(AWAIT_POLL_INTERVAL_MS, remaining)));
45549
+ await new Promise((resolve37) => setTimeout(resolve37, Math.min(AWAIT_POLL_INTERVAL_MS, remaining)));
44804
45550
  expirePendingApprovals(cfg.dataDir, (/* @__PURE__ */ new Date()).toISOString());
44805
45551
  current2 = getApprovalRequestById(cfg.dataDir, requestId) ?? current2;
44806
45552
  }
@@ -44954,24 +45700,24 @@ function collectUsage(projects, scope) {
44954
45700
  }
44955
45701
  function buildHealthReport(checks, usage) {
44956
45702
  const status2 = checks.some((c) => c.status === "fail") ? "unhealthy" : checks.some((c) => c.status === "warn") ? "degraded" : "ok";
44957
- const report2 = { status: status2, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), checks };
44958
- if (usage) report2.usage = usage;
44959
- return report2;
45703
+ const report3 = { status: status2, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), checks };
45704
+ if (usage) report3.usage = usage;
45705
+ return report3;
44960
45706
  }
44961
45707
  function evaluateUsage(snapshots, policy) {
44962
45708
  if (!policy.enabled) return snapshots;
44963
45709
  const advisory = policy.mode === "block" ? "observe" : policy.mode || "observe";
44964
45710
  return snapshots.map((snapshot) => {
44965
45711
  const messages = [];
44966
- const compare = (value, limit, label2) => {
45712
+ const compare2 = (value, limit, label3) => {
44967
45713
  if (typeof value === "number" && typeof limit === "number" && value > limit) {
44968
- messages.push(`[${advisory}] ${label2} ${value} exceeds limit ${limit}`);
45714
+ messages.push(`[${advisory}] ${label3} ${value} exceeds limit ${limit}`);
44969
45715
  }
44970
45716
  };
44971
- compare(snapshot.projectCount, policy.maxProjectsPerUser, "projectCount");
44972
- compare(snapshot.projectBytes, policy.maxProjectBytes, "projectBytes");
44973
- compare(snapshot.mcpRequestsLastMinute, policy.maxMcpRequestsPerMinute, "mcpRequestsPerMinute");
44974
- compare(snapshot.auditEventsToday, policy.maxAuditEventsPerDay, "auditEventsPerDay");
45717
+ compare2(snapshot.projectCount, policy.maxProjectsPerUser, "projectCount");
45718
+ compare2(snapshot.projectBytes, policy.maxProjectBytes, "projectBytes");
45719
+ compare2(snapshot.mcpRequestsLastMinute, policy.maxMcpRequestsPerMinute, "mcpRequestsPerMinute");
45720
+ compare2(snapshot.auditEventsToday, policy.maxAuditEventsPerDay, "auditEventsPerDay");
44975
45721
  if (messages.length === 0) return snapshot;
44976
45722
  return { ...snapshot, quotaMessages: [...snapshot.quotaMessages, ...messages] };
44977
45723
  });
@@ -45088,11 +45834,11 @@ function handleOperationsRequest(cfg, credential, req, res, url) {
45088
45834
 
45089
45835
  // src/server/gitbacking.ts
45090
45836
  var fs50 = __toESM(require("fs"));
45091
- var path65 = __toESM(require("path"));
45837
+ var path66 = __toESM(require("path"));
45092
45838
  var crypto17 = __toESM(require("crypto"));
45093
45839
  var import_child_process6 = require("child_process");
45094
45840
  function storePath13(dataDir) {
45095
- return path65.join(dataDir, "git-backing.json");
45841
+ return path66.join(dataDir, "git-backing.json");
45096
45842
  }
45097
45843
  var GitBackingStore = class {
45098
45844
  constructor(dataDir) {
@@ -45123,7 +45869,7 @@ var GitBackingStore = class {
45123
45869
  /** Swap the persisted collection wholesale via write-temp-then-rename. */
45124
45870
  replaceAll(bindings) {
45125
45871
  const p = storePath13(this.dataDir);
45126
- fs50.mkdirSync(path65.dirname(p), { recursive: true });
45872
+ fs50.mkdirSync(path66.dirname(p), { recursive: true });
45127
45873
  const tmp = `${p}.tmp`;
45128
45874
  fs50.writeFileSync(tmp, JSON.stringify(bindings, null, 2) + "\n");
45129
45875
  fs50.renameSync(tmp, p);
@@ -45231,7 +45977,7 @@ function checkoutBranch(workdir, branch) {
45231
45977
  }
45232
45978
  }
45233
45979
  function cloneOrOpen(remote, branch, workdir, credentialRef) {
45234
- if (!fs50.existsSync(path65.join(workdir, ".git"))) {
45980
+ if (!fs50.existsSync(path66.join(workdir, ".git"))) {
45235
45981
  fs50.mkdirSync(workdir, { recursive: true });
45236
45982
  git2(["clone", authRemote2(remote, credentialRef), "."], workdir);
45237
45983
  git2(["config", "user.name", process.env["WAIRON_GIT_NAME"] || "wairon-bot"], workdir);
@@ -45247,7 +45993,7 @@ function mirrorTree(sourceDir, targetDir) {
45247
45993
  fs50.rmSync(targetDir, { recursive: true, force: true });
45248
45994
  const stat = fs50.statSync(sourceDir, { throwIfNoEntry: false });
45249
45995
  if (!stat) return;
45250
- fs50.mkdirSync(path65.dirname(targetDir), { recursive: true });
45996
+ fs50.mkdirSync(path66.dirname(targetDir), { recursive: true });
45251
45997
  fs50.cpSync(sourceDir, targetDir, { recursive: true });
45252
45998
  }
45253
45999
  function commitAndPush(workdir, message) {
@@ -45370,7 +46116,7 @@ function unbindScope(cfg, credential, bindingId) {
45370
46116
  tryAppendAudit7(cfg, buildAuditEvent6(principal, "git.backing.unbind", "security", bindingId));
45371
46117
  }
45372
46118
  function runMirrorSync(cfg, binding) {
45373
- const workdir = path65.join(cfg.dataDir, "git-backing", binding.id);
46119
+ const workdir = path66.join(cfg.dataDir, "git-backing", binding.id);
45374
46120
  cloneOrOpen(binding.remote, binding.branch, workdir, binding.credentialRef);
45375
46121
  if (binding.scopeKind === "unit") {
45376
46122
  const units = listOrganizationUnits(cfg.dataDir);
@@ -45378,23 +46124,23 @@ function runMirrorSync(cfg, binding) {
45378
46124
  const placedIds = new Set(
45379
46125
  listProjectPlacements(cfg.dataDir).filter((p) => subtree.has(p.unitId)).map((p) => p.projectId)
45380
46126
  );
45381
- const projectsDir = path65.join(workdir, "projects");
46127
+ const projectsDir = path66.join(workdir, "projects");
45382
46128
  fs50.rmSync(projectsDir, { recursive: true, force: true });
45383
46129
  for (const rec of listProjectRecords(cfg.dataDir)) {
45384
46130
  if (!placedIds.has(rec.id)) continue;
45385
- mirrorTree(path65.join(rec.rootPath, ".wai"), path65.join(projectsDir, rec.id, ".wai"));
46131
+ mirrorTree(path66.join(rec.rootPath, ".wai"), path66.join(projectsDir, rec.id, ".wai"));
45386
46132
  }
45387
46133
  } else {
45388
- const instanceDir = path65.join(workdir, "instance");
46134
+ const instanceDir = path66.join(workdir, "instance");
45389
46135
  fs50.rmSync(instanceDir, { recursive: true, force: true });
45390
46136
  const files = binding.includeCredentials ? INSTANCE_STRUCTURE_FILES : INSTANCE_STRUCTURE_FILES.filter((rel2) => rel2 !== "auth/credentials.json");
45391
46137
  for (const rel2 of files) {
45392
- mirrorTree(path65.join(cfg.dataDir, rel2), path65.join(instanceDir, rel2));
46138
+ mirrorTree(path66.join(cfg.dataDir, rel2), path66.join(instanceDir, rel2));
45393
46139
  }
45394
- const projectsDir = path65.join(workdir, "projects");
46140
+ const projectsDir = path66.join(workdir, "projects");
45395
46141
  fs50.rmSync(projectsDir, { recursive: true, force: true });
45396
46142
  for (const rec of listProjectRecords(cfg.dataDir)) {
45397
- mirrorTree(path65.join(rec.rootPath, ".wai"), path65.join(projectsDir, rec.id, ".wai"));
46143
+ mirrorTree(path66.join(rec.rootPath, ".wai"), path66.join(projectsDir, rec.id, ".wai"));
45398
46144
  }
45399
46145
  }
45400
46146
  const published = commitAndPush(
@@ -45712,7 +46458,7 @@ function encodeFrame(opcode, payload) {
45712
46458
  // src/server/web.ts
45713
46459
  var crypto23 = __toESM(require("crypto"));
45714
46460
  var fs55 = __toESM(require("fs"));
45715
- var path70 = __toESM(require("path"));
46461
+ var path71 = __toESM(require("path"));
45716
46462
 
45717
46463
  // src/server/webadmin.ts
45718
46464
  function listUsers3(cfg, sessionId, project2) {
@@ -45967,14 +46713,14 @@ var crypto22 = __toESM(require("crypto"));
45967
46713
 
45968
46714
  // src/server/sharesnapshots.ts
45969
46715
  var fs51 = __toESM(require("fs"));
45970
- var path66 = __toESM(require("path"));
46716
+ var path67 = __toESM(require("path"));
45971
46717
  var crypto19 = __toESM(require("crypto"));
45972
46718
  init_fs();
45973
46719
  function snapshotDir(dataDir) {
45974
- return path66.join(dataDir, "share-snapshots");
46720
+ return path67.join(dataDir, "share-snapshots");
45975
46721
  }
45976
46722
  function snapshotPath2(dataDir, id) {
45977
- return path66.join(snapshotDir(dataDir), `${id}.json`);
46723
+ return path67.join(snapshotDir(dataDir), `${id}.json`);
45978
46724
  }
45979
46725
  var ShareSnapshotStore = class {
45980
46726
  constructor(dataDir) {
@@ -45983,7 +46729,7 @@ var ShareSnapshotStore = class {
45983
46729
  write(snapshot) {
45984
46730
  const p = snapshotPath2(this.dataDir, snapshot.id);
45985
46731
  if (fs51.existsSync(p)) return;
45986
- fs51.mkdirSync(path66.dirname(p), { recursive: true });
46732
+ fs51.mkdirSync(path67.dirname(p), { recursive: true });
45987
46733
  const tmp = `${p}.tmp`;
45988
46734
  fs51.writeFileSync(tmp, JSON.stringify(snapshot, null, 2) + "\n");
45989
46735
  fs51.renameSync(tmp, p);
@@ -46085,10 +46831,10 @@ function captureSnapshot(dataDir, principal, projectId, view, artifacts) {
46085
46831
 
46086
46832
  // src/server/sharelinks.ts
46087
46833
  var fs52 = __toESM(require("fs"));
46088
- var path67 = __toESM(require("path"));
46834
+ var path68 = __toESM(require("path"));
46089
46835
  var crypto20 = __toESM(require("crypto"));
46090
46836
  function storePath14(dataDir) {
46091
- return path67.join(dataDir, "share-links.json");
46837
+ return path68.join(dataDir, "share-links.json");
46092
46838
  }
46093
46839
  var ShareLinkStore = class {
46094
46840
  constructor(dataDir) {
@@ -46116,7 +46862,7 @@ var ShareLinkStore = class {
46116
46862
  }
46117
46863
  replaceAll(links) {
46118
46864
  const p = storePath14(this.dataDir);
46119
- fs52.mkdirSync(path67.dirname(p), { recursive: true });
46865
+ fs52.mkdirSync(path68.dirname(p), { recursive: true });
46120
46866
  const tmp = `${p}.tmp`;
46121
46867
  fs52.writeFileSync(tmp, JSON.stringify(links, null, 2) + "\n");
46122
46868
  fs52.renameSync(tmp, p);
@@ -46188,10 +46934,10 @@ function listProjectLinks(dataDir, projectId) {
46188
46934
 
46189
46935
  // src/server/shareaccesslog.ts
46190
46936
  var fs53 = __toESM(require("fs"));
46191
- var path68 = __toESM(require("path"));
46937
+ var path69 = __toESM(require("path"));
46192
46938
  var crypto21 = __toESM(require("crypto"));
46193
46939
  function storePath15(dataDir) {
46194
- return path68.join(dataDir, "share-access.json");
46940
+ return path69.join(dataDir, "share-access.json");
46195
46941
  }
46196
46942
  var ShareAccessStore = class {
46197
46943
  constructor(dataDir) {
@@ -46219,7 +46965,7 @@ var ShareAccessStore = class {
46219
46965
  }
46220
46966
  append(entry) {
46221
46967
  const p = storePath15(this.dataDir);
46222
- fs53.mkdirSync(path68.dirname(p), { recursive: true });
46968
+ fs53.mkdirSync(path69.dirname(p), { recursive: true });
46223
46969
  const next = [...this.load(), entry];
46224
46970
  const tmp = `${p}.tmp`;
46225
46971
  fs53.writeFileSync(tmp, JSON.stringify(next, null, 2) + "\n");
@@ -46375,15 +47121,15 @@ init_fs();
46375
47121
 
46376
47122
  // src/server/swagger.ts
46377
47123
  var fs54 = __toESM(require("fs"));
46378
- var path69 = __toESM(require("path"));
47124
+ var path70 = __toESM(require("path"));
46379
47125
  var cache;
46380
47126
  function loadAssets() {
46381
47127
  if (cache !== void 0) return cache;
46382
47128
  try {
46383
- const dir = path69.dirname(require.resolve("swagger-ui-dist/package.json"));
47129
+ const dir = path70.dirname(require.resolve("swagger-ui-dist/package.json"));
46384
47130
  cache = {
46385
- css: fs54.readFileSync(path69.join(dir, "swagger-ui.css"), "utf8"),
46386
- js: fs54.readFileSync(path69.join(dir, "swagger-ui-bundle.js"), "utf8")
47131
+ css: fs54.readFileSync(path70.join(dir, "swagger-ui.css"), "utf8"),
47132
+ js: fs54.readFileSync(path70.join(dir, "swagger-ui-bundle.js"), "utf8")
46387
47133
  };
46388
47134
  } catch {
46389
47135
  cache = null;
@@ -46795,11 +47541,11 @@ function nonceMatches(a, b) {
46795
47541
  }
46796
47542
  function loadReactBundle() {
46797
47543
  const candidates = [
46798
- path70.resolve(__dirname, "webapp.html"),
47544
+ path71.resolve(__dirname, "webapp.html"),
46799
47545
  // dist/index.js -> dist/webapp.html
46800
- path70.resolve(__dirname, "..", "webapp.html"),
47546
+ path71.resolve(__dirname, "..", "webapp.html"),
46801
47547
  // dist/cli/index.js -> dist/webapp.html
46802
- path70.resolve(__dirname, "..", "..", "web", "dist", "index.html")
47548
+ path71.resolve(__dirname, "..", "..", "web", "dist", "index.html")
46803
47549
  // tsx dev: src/server -> web/dist
46804
47550
  ];
46805
47551
  for (const candidate of candidates) {
@@ -49383,8 +50129,8 @@ var RealtimeHub = class {
49383
50129
  * complete the handshake, and register the connection. A bad path or session
49384
50130
  * destroys the socket. */
49385
50131
  handleUpgrade(cfg, req, socket) {
49386
- const path76 = (req.url ?? "/").split("?")[0];
49387
- if (path76 !== REALTIME_PATH || !isWebSocketUpgrade(req)) {
50132
+ const path77 = (req.url ?? "/").split("?")[0];
50133
+ if (path77 !== REALTIME_PATH || !isWebSocketUpgrade(req)) {
49388
50134
  socket.destroy();
49389
50135
  return;
49390
50136
  }
@@ -50381,7 +51127,7 @@ function routeData(cfg, req, res) {
50381
51127
  }
50382
51128
  function readExposurePolicyFile(dataDir) {
50383
51129
  try {
50384
- const raw = fs56.readFileSync(path71.join(dataDir, "exposure-policy.json"), "utf8");
51130
+ const raw = fs56.readFileSync(path72.join(dataDir, "exposure-policy.json"), "utf8");
50385
51131
  const parsed = JSON.parse(raw);
50386
51132
  return parsed && typeof parsed === "object" ? parsed : void 0;
50387
51133
  } catch {
@@ -50568,17 +51314,17 @@ async function routeAdmin(cfg, req, res) {
50568
51314
  }
50569
51315
  function warnIfDataDirUnmigrated(cfg) {
50570
51316
  try {
50571
- const report2 = migratePermissionModel(cfg.dataDir, false);
50572
- if (report2.findings.length === 0) return;
51317
+ const report3 = migratePermissionModel(cfg.dataDir, false);
51318
+ if (report3.findings.length === 0) return;
50573
51319
  console.error(
50574
- `[wairon migrate] ${report2.findings.length} legacy data shape(s) found in ${cfg.dataDir} \u2014 this instance has NOT been migrated to the permission model. Legacy users and tokens resolve to zero permissions until you run: wairon host doctor --fix (run it without --fix first for a dry run).`
51320
+ `[wairon migrate] ${report3.findings.length} legacy data shape(s) found in ${cfg.dataDir} \u2014 this instance has NOT been migrated to the permission model. Legacy users and tokens resolve to zero permissions until you run: wairon host doctor --fix (run it without --fix first for a dry run).`
50575
51321
  );
50576
- const shown = report2.findings.slice(0, 10);
51322
+ const shown = report3.findings.slice(0, 10);
50577
51323
  for (const finding of shown) {
50578
51324
  console.error(`[wairon migrate] ${finding.area}: ${finding.detail}`);
50579
51325
  }
50580
- if (report2.findings.length > shown.length) {
50581
- console.error(`[wairon migrate] \u2026 and ${report2.findings.length - shown.length} more (see \`wairon host doctor\`).`);
51326
+ if (report3.findings.length > shown.length) {
51327
+ console.error(`[wairon migrate] \u2026 and ${report3.findings.length - shown.length} more (see \`wairon host doctor\`).`);
50582
51328
  }
50583
51329
  } catch (err) {
50584
51330
  console.error(`[wairon migrate] migration check skipped: ${err instanceof Error ? err.message : String(err)}`);
@@ -51073,9 +51819,9 @@ function seedDemoTree() {
51073
51819
 
51074
51820
  // src/commands/host.ts
51075
51821
  function resolveHostConfig(options) {
51076
- const dataDir = options.dataDir || process.env["WAIRON_DATA_DIR"] || path72.join(os11.homedir(), ".wairon", "data");
51822
+ const dataDir = options.dataDir || process.env["WAIRON_DATA_DIR"] || path73.join(os11.homedir(), ".wairon", "data");
51077
51823
  if (!process.env["WAIRON_PACKS_DIR"]) {
51078
- process.env["WAIRON_PACKS_DIR"] = path72.join(dataDir, "packs");
51824
+ process.env["WAIRON_PACKS_DIR"] = path73.join(dataDir, "packs");
51079
51825
  }
51080
51826
  const cfg = {
51081
51827
  host: options.host || "0.0.0.0",
@@ -51132,11 +51878,11 @@ async function runServe(options = {}) {
51132
51878
  logger.info(` data dir: ${import_chalk17.default.gray(cfg.dataDir)}`);
51133
51879
  logger.blank();
51134
51880
  logger.info("Press Ctrl+C to stop.");
51135
- await new Promise((resolve36) => {
51881
+ await new Promise((resolve37) => {
51136
51882
  const shutdown = () => {
51137
51883
  logger.info("Shutting down\u2026");
51138
51884
  handle.close();
51139
- resolve36();
51885
+ resolve37();
51140
51886
  };
51141
51887
  process.on("SIGINT", shutdown);
51142
51888
  process.on("SIGTERM", shutdown);
@@ -51156,13 +51902,13 @@ function openBrowser(url) {
51156
51902
  }
51157
51903
  async function runDev(options = {}) {
51158
51904
  const cwd = process.cwd();
51159
- if (!fs57.existsSync(path72.join(cwd, ".wai"))) {
51905
+ if (!fs57.existsSync(path73.join(cwd, ".wai"))) {
51160
51906
  throw new WaironError(
51161
51907
  "No .wai/ found in the current directory. Run `wairon dev` from a wairon project root (or run `wairon init` first)."
51162
51908
  );
51163
51909
  }
51164
51910
  const hash = crypto24.createHash("sha256").update(cwd).digest("hex").slice(0, 16);
51165
- const dataDir = path72.join(os11.tmpdir(), "wairon-dev", hash);
51911
+ const dataDir = path73.join(os11.tmpdir(), "wairon-dev", hash);
51166
51912
  fs57.mkdirSync(dataDir, { recursive: true });
51167
51913
  const port = options.port ? Number(options.port) : 8080;
51168
51914
  const exposurePolicy = {
@@ -51201,11 +51947,11 @@ async function runDev(options = {}) {
51201
51947
  logger.blank();
51202
51948
  logger.info("An agent edits specs; refresh the page to see the live graph. Press Ctrl+C to stop.");
51203
51949
  if (options.open) openBrowser(url);
51204
- await new Promise((resolve36) => {
51950
+ await new Promise((resolve37) => {
51205
51951
  const shutdown = () => {
51206
51952
  logger.info("Shutting down\u2026");
51207
51953
  handle.close();
51208
- resolve36();
51954
+ resolve37();
51209
51955
  };
51210
51956
  process.on("SIGINT", shutdown);
51211
51957
  process.on("SIGTERM", shutdown);
@@ -51329,16 +52075,16 @@ async function runHostUnit(action, options = {}) {
51329
52075
  }
51330
52076
  async function runHostDoctor(options = {}) {
51331
52077
  const cfg = resolveHostConfig(options);
51332
- const report2 = migratePermissionModel(cfg.dataDir, options.fix === true);
51333
- if (report2.findings.length === 0) {
52078
+ const report3 = migratePermissionModel(cfg.dataDir, options.fix === true);
52079
+ if (report3.findings.length === 0) {
51334
52080
  logger.success("Hosted data is on the permission model \u2014 nothing to migrate.");
51335
52081
  return;
51336
52082
  }
51337
- logger.info(`${report2.findings.length} finding(s)${report2.applied ? " \u2014 applied" : " (dry run; re-run with --fix to apply)"}:`);
51338
- for (const f of report2.findings) {
52083
+ logger.info(`${report3.findings.length} finding(s)${report3.applied ? " \u2014 applied" : " (dry run; re-run with --fix to apply)"}:`);
52084
+ for (const f of report3.findings) {
51339
52085
  logger.info(` [${f.area.padEnd(8)}] ${f.detail}`);
51340
52086
  }
51341
- if (!report2.applied) {
52087
+ if (!report3.applied) {
51342
52088
  logger.warn("NOT applied. Without --fix, pre-permission-model users and tokens resolve to ZERO permissions.");
51343
52089
  }
51344
52090
  }
@@ -51559,8 +52305,8 @@ async function runHostPacks(action, options = {}) {
51559
52305
  }
51560
52306
  case "install": {
51561
52307
  if (!options.file) throw new WaironError("`--file <path>` (a declarative pack YAML) is required for install.");
51562
- const name = options.name ?? path72.basename(options.file).replace(/\.(ya?ml)$/i, "");
51563
- const content = fs57.readFileSync(path72.resolve(options.file), "utf8");
52308
+ const name = options.name ?? path73.basename(options.file).replace(/\.(ya?ml)$/i, "");
52309
+ const content = fs57.readFileSync(path73.resolve(options.file), "utf8");
51564
52310
  const desc = project2 ? installProjectPack(cfg, cred, project2, name, content) : installGlobalPack(cfg, cred, name, content);
51565
52311
  logger.success(`Installed ${scope} pack "${desc.name}" (${desc.profiles} profile(s), ${desc.languages} language(s)).`);
51566
52312
  if (project2) logger.info("Committed with the project \u2014 every clone and CI will enforce it.");
@@ -51647,7 +52393,7 @@ async function runHostGit(action, options = {}) {
51647
52393
  }
51648
52394
 
51649
52395
  // src/commands/produce.ts
51650
- var import_inquirer4 = __toESM(require("inquirer"));
52396
+ var import_inquirer5 = __toESM(require("inquirer"));
51651
52397
  var import_chalk18 = __toESM(require("chalk"));
51652
52398
  init_logger();
51653
52399
  init_errors();
@@ -51664,7 +52410,7 @@ async function runProduce(target, options = {}) {
51664
52410
  let token = options.token || process.env[t.envKey] || null;
51665
52411
  if (!token) {
51666
52412
  if (!process.stdin.isTTY) throw new WaironError(`No ${t.label} token \u2014 pass --token or set ${t.envKey}.`);
51667
- const ans = await import_inquirer4.default.prompt([
52413
+ const ans = await import_inquirer5.default.prompt([
51668
52414
  { type: "password", name: "token", message: `${t.label} token:`, mask: "*" }
51669
52415
  ]);
51670
52416
  token = ans.token;
@@ -51685,11 +52431,6 @@ var import_chalk19 = __toESM(require("chalk"));
51685
52431
  init_logger();
51686
52432
  init_errors();
51687
52433
  init_paths();
51688
-
51689
- // src/commands/adapters/surfaces.ts
51690
- init_surface_portal();
51691
-
51692
- // src/commands/surface.ts
51693
52434
  init_models();
51694
52435
  async function runSurface(action, options = {}) {
51695
52436
  assertProjectInitialized();
@@ -51815,10 +52556,10 @@ function printPins(pins) {
51815
52556
  return;
51816
52557
  }
51817
52558
  const outcome = (o) => o === "pinned" ? import_chalk20.default.green(o) : o === "unchanged" ? import_chalk20.default.gray(o) : import_chalk20.default.yellow(o);
51818
- for (const pin of pins) {
51819
- const what = pin.digest ? ` ${pin.usedNames} used name(s), ${pin.digest.slice(0, 19)}\u2026` : "";
51820
- logger.info(`${import_chalk20.default.cyan(pin.alias)} \u2192 ${pin.project ?? "?"}: ${outcome(pin.outcome)}${what}${pin.detail ? ` \u2014 ${pin.detail}` : ""}`);
51821
- for (const ref of pin.unexported) {
52559
+ for (const pin2 of pins) {
52560
+ const what = pin2.digest ? ` ${pin2.usedNames} used name(s), ${pin2.digest.slice(0, 19)}\u2026` : "";
52561
+ logger.info(`${import_chalk20.default.cyan(pin2.alias)} \u2192 ${pin2.project ?? "?"}: ${outcome(pin2.outcome)}${what}${pin2.detail ? ` \u2014 ${pin2.detail}` : ""}`);
52562
+ for (const ref of pin2.unexported) {
51822
52563
  logger.info(` unexported: "${ref.specId}" reaches "${ref.target}"${ref.member ? ` (${ref.member})` : ""} \u2014 export it from the producer's L0 to pin it`);
51823
52564
  }
51824
52565
  }
@@ -51882,7 +52623,7 @@ async function runExternals(action, aliases, options = {}) {
51882
52623
 
51883
52624
  // src/commands/remote.ts
51884
52625
  var fs59 = __toESM(require("fs"));
51885
- var path74 = __toESM(require("path"));
52626
+ var path75 = __toESM(require("path"));
51886
52627
  var import_chalk21 = __toESM(require("chalk"));
51887
52628
  init_logger();
51888
52629
  init_errors();
@@ -51893,15 +52634,15 @@ init_mcp();
51893
52634
  // src/commands/remotebinding.ts
51894
52635
  var fs58 = __toESM(require("fs"));
51895
52636
  var os12 = __toESM(require("os"));
51896
- var path73 = __toESM(require("path"));
52637
+ var path74 = __toESM(require("path"));
51897
52638
  init_paths();
51898
52639
  var BINDING_FILE = "remote.json";
51899
52640
  var CREDENTIALS_FILE = "credentials.json";
51900
52641
  function bindingPath(root) {
51901
- return path73.join(aiPathsAt(root).root(), BINDING_FILE);
52642
+ return path74.join(aiPathsAt(root).root(), BINDING_FILE);
51902
52643
  }
51903
52644
  function credentialsPath() {
51904
- return path73.join(os12.homedir(), ".wairon", CREDENTIALS_FILE);
52645
+ return path74.join(os12.homedir(), ".wairon", CREDENTIALS_FILE);
51905
52646
  }
51906
52647
  function normalizeInstanceUrl(url) {
51907
52648
  return url.trim().replace(/\/+$/, "").toLowerCase();
@@ -51915,7 +52656,7 @@ function readJson(file) {
51915
52656
  }
51916
52657
  }
51917
52658
  function writeJsonAtomic(file, value, mode) {
51918
- fs58.mkdirSync(path73.dirname(file), { recursive: true });
52659
+ fs58.mkdirSync(path74.dirname(file), { recursive: true });
51919
52660
  const tmp = `${file}.tmp`;
51920
52661
  fs58.writeFileSync(tmp, JSON.stringify(value, null, 2) + "\n", mode !== void 0 ? { mode } : void 0);
51921
52662
  fs58.renameSync(tmp, file);
@@ -52248,7 +52989,7 @@ async function runRemote(action, options = {}) {
52248
52989
  };
52249
52990
  if (options.unit) transfer.createUnitId = options.unit;
52250
52991
  if (options.archive) transfer.archivePath = options.archive;
52251
- report(await pushTree(target, transfer));
52992
+ report2(await pushTree(target, transfer));
52252
52993
  return;
52253
52994
  }
52254
52995
  case "pull": {
@@ -52257,8 +52998,8 @@ async function runRemote(action, options = {}) {
52257
52998
  allowPartial: options.allowPartial === true
52258
52999
  };
52259
53000
  if (options.archive) transfer.archivePath = options.archive;
52260
- if (options.dir) transfer.destDir = path74.resolve(options.dir);
52261
- report(await pullTree(target, transfer));
53001
+ if (options.dir) transfer.destDir = path75.resolve(options.dir);
53002
+ report2(await pullTree(target, transfer));
52262
53003
  return;
52263
53004
  }
52264
53005
  default:
@@ -52341,13 +53082,13 @@ function topProjectId2(selector) {
52341
53082
  }
52342
53083
  function writeArchiveIfAsked(archive, archivePath, suggestedFileName) {
52343
53084
  if (!archivePath) return void 0;
52344
- const resolved = path74.resolve(archivePath);
52345
- const target = fs59.existsSync(resolved) && fs59.statSync(resolved).isDirectory() ? path74.join(resolved, suggestedFileName) : resolved;
52346
- fs59.mkdirSync(path74.dirname(target), { recursive: true });
53085
+ const resolved = path75.resolve(archivePath);
53086
+ const target = fs59.existsSync(resolved) && fs59.statSync(resolved).isDirectory() ? path75.join(resolved, suggestedFileName) : resolved;
53087
+ fs59.mkdirSync(path75.dirname(target), { recursive: true });
52347
53088
  fs59.writeFileSync(target, Buffer.from(archive));
52348
53089
  return target;
52349
53090
  }
52350
- function report(outcome) {
53091
+ function report2(outcome) {
52351
53092
  const where = outcome.direction === "push" ? `${outcome.url} \u2192 ${outcome.projectId}` : `${outcome.projectId} \u2192 ${outcome.destDir ?? getProjectRoot()}`;
52352
53093
  logger.success(
52353
53094
  `${outcome.direction === "push" ? "Pushed" : "Pulled"} "${outcome.projectName}": ${outcome.fileCount} file(s) across ${outcome.roots.length} root(s) \u2014 ${where}`
@@ -52564,10 +53305,10 @@ async function lockCommand(opts) {
52564
53305
  async function validateCommand(opts) {
52565
53306
  const target = resolveTarget(getProjectRoot(), {});
52566
53307
  if (target) {
52567
- const report2 = await validateAttached(target, opts.subsystem);
52568
- const errors = report2.errors ?? [];
52569
- const warnings = report2.warnings ?? [];
52570
- const notices = report2.notices ?? [];
53308
+ const report3 = await validateAttached(target, opts.subsystem);
53309
+ const errors = report3.errors ?? [];
53310
+ const warnings = report3.warnings ?? [];
53311
+ const notices = report3.notices ?? [];
52571
53312
  logger.info(`Validated "${target.projectId}" on ${target.url} \u2014 ${errors.length} error(s), ${warnings.length} warning(s), ${notices.length} notice(s).`);
52572
53313
  for (const e of errors) logger.error(` [${e.code}] ${e.specId ? `${e.specId}: ` : ""}${e.message}`);
52573
53314
  for (const w of warnings) logger.warn(` [${w.code}] ${w.specId ? `${w.specId}: ` : ""}${w.message}`);
@@ -52607,8 +53348,8 @@ program.command("validate").description("Validate the project configuration and
52607
53348
  program.command("status").description("Show a hierarchical completeness graph of the SDD Spec Tree").option("--subsystem <id>", "only show status for the specified subsystem").option("--no-recursive", "do not recursively show status for subprojects").action(async (opts) => {
52608
53349
  await statusCommand(opts);
52609
53350
  });
52610
- program.command("doctor").description("Health check: flags stale generated guides/skills, an unregistered MCP server, and spec-tree issues").option("--fix", "regenerate stale in-project guides/context/skills and register the MCP server").action(async (opts) => {
52611
- await runDoctor({ fix: opts.fix });
53351
+ program.command("doctor").description("Health check: flags stale generated guides/skills, an unregistered MCP server, spec-tree issues and the chaining migration still pending").option("--fix", "regenerate stale in-project guides/context/skills, register the MCP server, then apply the chaining migration once confirmed").option("--report <section>", "print one section's plan and nothing else, writing nothing (the one section: chaining); never combines with --fix").option("-y, --yes", "answer the chaining migration's confirmation (for a non-interactive --fix)").action(async (opts) => {
53352
+ await runDoctor({ fix: opts.fix, report: opts.report, yes: opts.yes });
52612
53353
  });
52613
53354
  async function runRules() {
52614
53355
  await listRules3();
@@ -52761,7 +53502,7 @@ async function runAgent(action, id) {
52761
53502
  }
52762
53503
  case "customize": {
52763
53504
  const brief = composeAgentBrief(id);
52764
- const guidancePath = path75.join(AI_PATHS.root(), "agents", `${id}.md`);
53505
+ const guidancePath = path76.join(AI_PATHS.root(), "agents", `${id}.md`);
52765
53506
  const relPath = `.wai/agents/${id}.md`;
52766
53507
  if (pathExists(guidancePath)) {
52767
53508
  throw new GuidanceFileExistsError(relPath);