@wairon/cli 5.1.1-dev.84 → 5.1.1-dev.85

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/index.js CHANGED
@@ -2510,8 +2510,8 @@ var require_dist = __commonJS({
2510
2510
  maxCompressionRatio: 100,
2511
2511
  maxDepth: 16
2512
2512
  };
2513
- function parseManifest(text2) {
2514
- const raw = (0, import_js_yaml.load)(text2);
2513
+ function parseManifest(text3) {
2514
+ const raw = (0, import_js_yaml.load)(text3);
2515
2515
  const manifest = validateEnvelope(raw);
2516
2516
  if (Math.trunc(manifest.formatVersion) > SUPPORTED_FORMAT_VERSION) {
2517
2517
  throw new Error("unsupported pack-archive formatVersion (newer than this wairon understands)");
@@ -2597,7 +2597,7 @@ var require_dist = __commonJS({
2597
2597
  if (Math.trunc(manifest.formatVersion) > SUPPORTED_FORMAT_VERSION) {
2598
2598
  return false;
2599
2599
  }
2600
- if (manifest.minWaironVersion && isBelow(waironVersion, manifest.minWaironVersion)) {
2600
+ if (manifest.minWaironVersion && isBelow2(waironVersion, manifest.minWaironVersion)) {
2601
2601
  return false;
2602
2602
  }
2603
2603
  return true;
@@ -2662,7 +2662,7 @@ var require_dist = __commonJS({
2662
2662
  function sha2562(data) {
2663
2663
  return (0, import_crypto3.createHash)("sha256").update(data).digest("hex");
2664
2664
  }
2665
- function isBelow(running, minimum) {
2665
+ function isBelow2(running, minimum) {
2666
2666
  const [rMaj, rMin, rPat] = parseSemverCore(running);
2667
2667
  const [mMaj, mMin, mPat] = parseSemverCore(minimum);
2668
2668
  if (rMaj !== mMaj) return rMaj < mMaj;
@@ -2689,8 +2689,8 @@ var require_dist = __commonJS({
2689
2689
  var DERIVED_ROOTS = ["generated", "docs"];
2690
2690
  var EXECUTABLE_EXT_RE = /\.(c|m)?js$/i;
2691
2691
  var WAI_SEGMENT = ".wai";
2692
- function parseTreeManifest(text2) {
2693
- const raw = (0, import_js_yaml2.load)(text2);
2692
+ function parseTreeManifest(text3) {
2693
+ const raw = (0, import_js_yaml2.load)(text3);
2694
2694
  const manifest = validateEnvelope2(raw);
2695
2695
  if (Math.trunc(manifest.formatVersion) > SUPPORTED_FORMAT_VERSION2) {
2696
2696
  throw new Error("unsupported tree-archive formatVersion (newer than this wairon understands)");
@@ -2791,7 +2791,7 @@ var require_dist = __commonJS({
2791
2791
  if (Math.trunc(manifest.formatVersion) > SUPPORTED_FORMAT_VERSION2) {
2792
2792
  return false;
2793
2793
  }
2794
- if (manifest.minWaironVersion && isBelow2(waironVersion, manifest.minWaironVersion)) {
2794
+ if (manifest.minWaironVersion && isBelow22(waironVersion, manifest.minWaironVersion)) {
2795
2795
  return false;
2796
2796
  }
2797
2797
  return true;
@@ -2864,7 +2864,7 @@ var require_dist = __commonJS({
2864
2864
  function sha25622(data) {
2865
2865
  return (0, import_crypto22.createHash)("sha256").update(data).digest("hex");
2866
2866
  }
2867
- function isBelow2(running, minimum) {
2867
+ function isBelow22(running, minimum) {
2868
2868
  const [rMaj, rMin, rPat] = parseSemverCore2(running);
2869
2869
  const [mMaj, mMin, mPat] = parseSemverCore2(minimum);
2870
2870
  if (rMaj !== mMaj) return rMaj < mMaj;
@@ -3061,7 +3061,7 @@ var require_dist = __commonJS({
3061
3061
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
3062
3062
  }
3063
3063
  var fs21 = __toESM2(require("fs"));
3064
- var path37 = __toESM2(require("path"));
3064
+ var path38 = __toESM2(require("path"));
3065
3065
  var import_fflate = require_node();
3066
3066
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".hg", ".svn"]);
3067
3067
  function listEntries(archive) {
@@ -3099,8 +3099,8 @@ var require_dist = __commonJS({
3099
3099
  }
3100
3100
  function writeTree(destDir, files) {
3101
3101
  for (const file of files) {
3102
- const absolute = path37.join(destDir, file.path);
3103
- fs21.mkdirSync(path37.dirname(absolute), { recursive: true });
3102
+ const absolute = path38.join(destDir, file.path);
3103
+ fs21.mkdirSync(path38.dirname(absolute), { recursive: true });
3104
3104
  fs21.writeFileSync(absolute, file.contents);
3105
3105
  }
3106
3106
  }
@@ -3113,10 +3113,10 @@ var require_dist = __commonJS({
3113
3113
  for (const entry of fs21.readdirSync(current2, { withFileTypes: true })) {
3114
3114
  if (entry.isDirectory()) {
3115
3115
  if (SKIP_DIRS.has(entry.name)) continue;
3116
- walkPackDir(root, path37.join(current2, entry.name), out);
3116
+ walkPackDir(root, path38.join(current2, entry.name), out);
3117
3117
  } else if (entry.isFile()) {
3118
- const absolute = path37.join(current2, entry.name);
3119
- const relative22 = path37.relative(root, absolute).split(path37.sep).join("/");
3118
+ const absolute = path38.join(current2, entry.name);
3119
+ const relative22 = path38.relative(root, absolute).split(path38.sep).join("/");
3120
3120
  out.push({ path: relative22, contents: fs21.readFileSync(absolute) });
3121
3121
  }
3122
3122
  }
@@ -3176,8 +3176,8 @@ var require_dist = __commonJS({
3176
3176
  throw new Error("pack build requires a wairon-pack.yaml envelope (run `wairon pack init` to scaffold one)");
3177
3177
  }
3178
3178
  const manifest = parseManifest(decode(envelope.contents));
3179
- const identity = readInnerIdentity(files, manifest);
3180
- verifyIdentity(manifest, identity.name, identity.version);
3179
+ const identity2 = readInnerIdentity(files, manifest);
3180
+ verifyIdentity(manifest, identity2.name, identity2.version);
3181
3181
  const sealed = sealIntegrity(manifest, files, `@wairon/sdk@${SDK_VERSION}`, (/* @__PURE__ */ new Date()).toISOString());
3182
3182
  const sealedText = serializeManifest(sealed);
3183
3183
  const finalFiles = upsertEnvelope(files, sealedText);
@@ -3408,6 +3408,7 @@ __export(src_exports, {
3408
3408
  ComplexityRuleConfigSchema: () => ComplexityRuleConfigSchema,
3409
3409
  ComponentSpecSchema: () => ComponentSpecSchema,
3410
3410
  ComponentTypeSchema: () => ComponentTypeSchema,
3411
+ CompositionConfigSchema: () => CompositionConfigSchema,
3411
3412
  ConfigValidationError: () => ConfigValidationError,
3412
3413
  ConformanceRuleConfigSchema: () => ConformanceRuleConfigSchema,
3413
3414
  ConformanceTierSchema: () => ConformanceTierSchema,
@@ -3462,6 +3463,7 @@ __export(src_exports, {
3462
3463
  PROJECT_ID_RE: () => PROJECT_ID_RE,
3463
3464
  PROJECT_KINDS: () => PROJECT_KINDS,
3464
3465
  PUBLIC_NAME_RE: () => PUBLIC_NAME_RE,
3466
+ PackRequirementSchema: () => PackRequirementSchema,
3465
3467
  PackSelectionSchema: () => PackSelectionSchema,
3466
3468
  ParallelBranchSchema: () => ParallelBranchSchema,
3467
3469
  PathsConfigSchema: () => PathsConfigSchema,
@@ -3517,6 +3519,7 @@ __export(src_exports, {
3517
3519
  WaironError: () => WaironError,
3518
3520
  activeTargetTypes: () => activeTargetTypes2,
3519
3521
  addDomain: () => addDomain,
3522
+ admits: () => admits,
3520
3523
  aiDir: () => aiDir,
3521
3524
  aiDirAt: () => aiDirAt,
3522
3525
  aiPathsAt: () => aiPathsAt,
@@ -3639,6 +3642,7 @@ __export(src_exports, {
3639
3642
  loadComponentSpecs: () => loadComponentSpecs,
3640
3643
  loadExtensionPacks: () => loadExtensionPacks,
3641
3644
  loadExtensions: () => loadExtensions,
3645
+ loadExtensionsFor: () => loadFor,
3642
3646
  loadImplementationSpec: () => loadImplementationSpec,
3643
3647
  loadImplementationSpecs: () => loadImplementationSpecs,
3644
3648
  loadInterfaceSpec: () => loadInterfaceSpec,
@@ -3678,7 +3682,9 @@ __export(src_exports, {
3678
3682
  packDirEntry: () => packDirEntry,
3679
3683
  packEntryLabel: () => packEntryLabel,
3680
3684
  packEntryRef: () => packEntryRef,
3685
+ packManifest: () => manifestOf,
3681
3686
  packStoreDir: () => packStoreDir,
3687
+ parseDeclarativePack: () => parseDeclarativePack,
3682
3688
  parseDeclaredCall: () => parseDeclaredCall,
3683
3689
  parseYaml: () => parseYaml,
3684
3690
  passesIntentFloor: () => passesIntentFloor,
@@ -3691,6 +3697,7 @@ __export(src_exports, {
3691
3697
  projectIdentity: () => projectIdentity,
3692
3698
  provisionProject: () => provisionProject,
3693
3699
  qualifiedTypeId: () => qualifiedTypeId,
3700
+ rangeProblem: () => rangeProblem,
3694
3701
  readFileOrNull: () => readFileOrNull,
3695
3702
  readJsonFile: () => readJsonFile,
3696
3703
  readLockRecord: () => readLockRecord,
@@ -3707,6 +3714,7 @@ __export(src_exports, {
3707
3714
  renameMethod: () => renameMethod,
3708
3715
  renderDiagram: () => renderDiagram,
3709
3716
  repairForeignStepFields: () => repairForeignStepFields,
3717
+ requiredPolicies: () => requiredPolicies,
3710
3718
  resolveAgentTopology: () => resolveAgentTopology,
3711
3719
  resolveBudget: () => resolveBudget,
3712
3720
  resolveChainingParent: () => resolveChainingParent,
@@ -3751,6 +3759,7 @@ __export(src_exports, {
3751
3759
  upsertPackSelection: () => upsertPackSelection,
3752
3760
  validateFamily: () => validateFamily,
3753
3761
  validateProject: () => validateProject,
3762
+ withPack: () => withPack,
3754
3763
  writeFile: () => writeFile,
3755
3764
  writeFileIfChanged: () => writeFileIfChanged,
3756
3765
  writeJsonFile: () => writeJsonFile,
@@ -4192,7 +4201,7 @@ var AgentBriefSchema = import_zod2.z.object({
4192
4201
  profile: ExecutionProfileSchema.optional(),
4193
4202
  budget: ExecutionBudgetSchema.optional()
4194
4203
  });
4195
- function createAgentRecord(partial) {
4204
+ function createAgentRecord(partial2) {
4196
4205
  const now = (/* @__PURE__ */ new Date()).toISOString();
4197
4206
  return AgentRecordSchema.parse({
4198
4207
  description: "",
@@ -4205,10 +4214,32 @@ function createAgentRecord(partial) {
4205
4214
  targets: ["claude"],
4206
4215
  createdAt: now,
4207
4216
  updatedAt: now,
4208
- ...partial
4217
+ ...partial2
4209
4218
  });
4210
4219
  }
4211
4220
 
4221
+ // src/utils/version.ts
4222
+ function isNewerVersion(current2, candidate) {
4223
+ const baseVersion = (v) => v.replace(/-.*$/, "");
4224
+ const preRelease = (v) => {
4225
+ const match = v.match(/-(.+)\.(\d+)$/);
4226
+ return match ? { label: match[1], n: parseInt(match[2], 10) } : null;
4227
+ };
4228
+ const parse = (v) => baseVersion(v).split(".").map((n) => parseInt(n, 10) || 0);
4229
+ const [cMaj, cMin, cPat] = parse(current2);
4230
+ const [nMaj, nMin, nPat] = parse(candidate);
4231
+ if (nMaj !== cMaj) return nMaj > cMaj;
4232
+ if (nMin !== cMin) return nMin > cMin;
4233
+ if (nPat !== cPat) return nPat > cPat;
4234
+ const cPre = preRelease(current2);
4235
+ const nPre = preRelease(candidate);
4236
+ if (!cPre && !nPre) return false;
4237
+ if (!cPre && nPre) return false;
4238
+ if (cPre && !nPre) return true;
4239
+ if (cPre && nPre) return nPre.n > cPre.n;
4240
+ return false;
4241
+ }
4242
+
4212
4243
  // src/models/project.ts
4213
4244
  var BuiltinTargetConfigSchema = import_zod3.z.object({
4214
4245
  type: import_zod3.z.enum(["claude", "gemini", "agy", "cursor", "copilot", "codex"]),
@@ -4541,6 +4572,15 @@ var MemberDeclarationSchema = import_zod3.z.object({
4541
4572
  /** The member's public names this project imports, exactly as an external's `use`. */
4542
4573
  use: import_zod3.z.array(import_zod3.z.string()).optional()
4543
4574
  });
4575
+ var plainString = import_zod3.z.preprocess((v) => v === void 0 || v === null ? "" : String(v), import_zod3.z.string());
4576
+ var PackRequirementSchema = import_zod3.z.object({
4577
+ pack: plainString,
4578
+ version: plainString,
4579
+ profile: import_zod3.z.string().optional()
4580
+ });
4581
+ var CompositionConfigSchema = import_zod3.z.object({
4582
+ requirePolicies: import_zod3.z.array(PackRequirementSchema).optional()
4583
+ });
4544
4584
  var ProjectConfigSchema = import_zod3.z.object({
4545
4585
  /**
4546
4586
  * Schema version — used to detect incompatible config formats in future
@@ -4577,6 +4617,15 @@ var ProjectConfigSchema = import_zod3.z.object({
4577
4617
  * `projectPath` mount still loads for one release (DEPRECATED_MOUNT_FORM).
4578
4618
  */
4579
4619
  members: import_zod3.z.record(import_zod3.z.union([import_zod3.z.string(), MemberDeclarationSchema])).optional(),
4620
+ /**
4621
+ * What this project requires of the projects it contains when they are
4622
+ * composed: `requirePolicies`, the packs each member must select (by semver
4623
+ * range, optionally one profile). This project's own gate judges only its
4624
+ * syntax (POLICY_REQUIREMENT_INVALID); the family run judges members against
4625
+ * it, and createMember reads it once when it scaffolds a new member. No
4626
+ * member's own gate reads it. Written by hand.
4627
+ */
4628
+ composition: CompositionConfigSchema.optional(),
4580
4629
  /**
4581
4630
  * The type/profile of the project, which configures targeted guidelines, rules,
4582
4631
  * templates, and validation constraints. Open string: built-ins are backend,
@@ -4684,6 +4733,135 @@ function declaredPackNames(config) {
4684
4733
  function declaredProfileIds(config) {
4685
4734
  return [...new Set(config.profileSelection?.profileIds ?? [])];
4686
4735
  }
4736
+ function withPack(config, entry, remove = false) {
4737
+ const packs = config.extensions?.packs ?? [];
4738
+ const same = (e) => typeof entry === "string" ? e === entry : typeof e !== "string" && e.name === entry.name;
4739
+ const others = packs.filter((e) => !same(e));
4740
+ let next;
4741
+ if (remove) next = others;
4742
+ else if (typeof entry === "string") next = packs.includes(entry) ? packs : [...packs, entry];
4743
+ else next = [...others, entry];
4744
+ return { ...config, extensions: { useGlobalPacks: false, ...config.extensions, packs: next } };
4745
+ }
4746
+ function requiredPolicies(config) {
4747
+ const seen = /* @__PURE__ */ new Set();
4748
+ const out = [];
4749
+ for (const requirement of config.composition?.requirePolicies ?? []) {
4750
+ if (seen.has(requirement.pack)) continue;
4751
+ seen.add(requirement.pack);
4752
+ out.push(requirement);
4753
+ }
4754
+ return out;
4755
+ }
4756
+ var CONCRETE_RE = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
4757
+ var PARTIAL_RE = /^v?(\d+|[xX*])(?:\.(\d+|[xX*]))?(?:\.(\d+|[xX*]))?(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
4758
+ var OPERATOR_RE = /^(>=|<=|>|<|=|\^|~)/;
4759
+ function concrete(v) {
4760
+ const m = CONCRETE_RE.exec(v.trim());
4761
+ if (!m) return null;
4762
+ return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3]), ...m[4] ? { pre: m[4] } : {} };
4763
+ }
4764
+ var text = (v) => `${v.major}.${v.minor}.${v.patch}${v.pre ? `-${v.pre}` : ""}`;
4765
+ function compare(a, b) {
4766
+ if (isNewerVersion(text(a), text(b))) return -1;
4767
+ if (isNewerVersion(text(b), text(a))) return 1;
4768
+ return 0;
4769
+ }
4770
+ function partial(v) {
4771
+ const m = PARTIAL_RE.exec(v);
4772
+ if (!m) return null;
4773
+ const num = (s) => s === void 0 || /^[xX*]$/.test(s) ? null : Number(s);
4774
+ const major = num(m[1]);
4775
+ const minor = major === null ? null : num(m[2]);
4776
+ const patch = minor === null ? null : num(m[3]);
4777
+ if (m[4] && patch === null) return null;
4778
+ return { major, minor, patch, ...m[4] ? { pre: m[4] } : {} };
4779
+ }
4780
+ var cv = (major, minor, patch, pre) => ({ major, minor, patch, ...pre ? { pre } : {} });
4781
+ function nextOf(p) {
4782
+ if (p.minor === null) return cv(p.major + 1, 0, 0);
4783
+ return cv(p.major, p.minor + 1, 0);
4784
+ }
4785
+ function expand(token) {
4786
+ const opMatch = OPERATOR_RE.exec(token);
4787
+ const op = opMatch ? opMatch[1] : "";
4788
+ const p = partial(token.slice(op.length));
4789
+ if (!p) return null;
4790
+ const low = cv(p.major ?? 0, p.minor ?? 0, p.patch ?? 0, p.pre);
4791
+ const exact = p.patch !== null;
4792
+ if (p.major === null) return op === "<" || op === ">" ? [{ op: "<", version: cv(0, 0, 0) }] : [];
4793
+ switch (op) {
4794
+ case "":
4795
+ case "=":
4796
+ return exact ? [{ op: "=", version: low }] : [{ op: ">=", version: low }, { op: "<", version: nextOf(p) }];
4797
+ case "^": {
4798
+ const upper = p.major > 0 || p.minor === null ? cv(p.major + 1, 0, 0) : p.minor > 0 || p.patch === null ? cv(0, p.minor + 1, 0) : cv(0, 0, p.patch + 1);
4799
+ return [{ op: ">=", version: low }, { op: "<", version: upper }];
4800
+ }
4801
+ case "~":
4802
+ return [{ op: ">=", version: low }, { op: "<", version: p.minor === null ? cv(p.major + 1, 0, 0) : cv(p.major, p.minor + 1, 0) }];
4803
+ case ">=":
4804
+ return [{ op: ">=", version: low }];
4805
+ case "<":
4806
+ return [{ op: "<", version: low }];
4807
+ case ">":
4808
+ return exact ? [{ op: ">", version: low }] : [{ op: ">=", version: nextOf(p) }];
4809
+ case "<=":
4810
+ return exact ? [{ op: "<=", version: low }] : [{ op: "<", version: nextOf(p) }];
4811
+ default:
4812
+ return null;
4813
+ }
4814
+ }
4815
+ function readRange(range) {
4816
+ if (range.trim() === "") return { problem: "the range is empty" };
4817
+ const alternatives = [];
4818
+ for (const raw of range.split("||")) {
4819
+ const tokens = raw.trim().split(/\s+/).filter((t) => t !== "");
4820
+ const joined = [];
4821
+ for (const t of tokens) {
4822
+ if (joined.length > 0 && /^(>=|<=|>|<|=|\^|~)$/.test(joined[joined.length - 1])) joined[joined.length - 1] += t;
4823
+ else joined.push(t);
4824
+ }
4825
+ const comparators = [];
4826
+ for (const token of joined) {
4827
+ const expanded = expand(token);
4828
+ if (!expanded) return { problem: `"${token}" is neither a version, an x-range nor a comparator` };
4829
+ comparators.push(...expanded);
4830
+ }
4831
+ alternatives.push({ comparators, any: joined.length === 0 || joined.length === 1 && /^[xX*]$/.test(joined[0]) });
4832
+ }
4833
+ return { alternatives };
4834
+ }
4835
+ function satisfies(version, c) {
4836
+ const d = compare(version, c.version);
4837
+ switch (c.op) {
4838
+ case "=":
4839
+ return d === 0;
4840
+ case ">=":
4841
+ return d >= 0;
4842
+ case ">":
4843
+ return d > 0;
4844
+ case "<":
4845
+ return d < 0;
4846
+ case "<=":
4847
+ return d <= 0;
4848
+ }
4849
+ }
4850
+ function rangeProblem(requirement) {
4851
+ const read = readRange(requirement.version);
4852
+ return "problem" in read ? read.problem : null;
4853
+ }
4854
+ function admits(requirement, version) {
4855
+ const read = readRange(requirement.version);
4856
+ if ("problem" in read) return false;
4857
+ if (version === void 0 || version.trim() === "") return read.alternatives.some((a) => a.any);
4858
+ const v = concrete(version);
4859
+ if (!v) return false;
4860
+ return read.alternatives.some((alt) => {
4861
+ if (v.pre && !alt.comparators.some((c) => c.version.pre && c.version.major === v.major && c.version.minor === v.minor && c.version.patch === v.patch)) return false;
4862
+ return alt.comparators.every((c) => satisfies(v, c));
4863
+ });
4864
+ }
4687
4865
  var PROJECT_ID_RE = /^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/;
4688
4866
  function slugifyProjectName(name) {
4689
4867
  const slug = name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[^a-z0-9]+/, "").replace(/[^a-z0-9]+$/, "");
@@ -4779,12 +4957,12 @@ function projectConfigFsAdapterAt(rootDir) {
4779
4957
  return readYamlFile(file());
4780
4958
  },
4781
4959
  writeDocument(document) {
4782
- const text2 = readFileOrNull(file());
4783
- if (text2 === null) {
4960
+ const text3 = readFileOrNull(file());
4961
+ if (text3 === null) {
4784
4962
  writeYamlFile(file(), document);
4785
4963
  return;
4786
4964
  }
4787
- writeFile(file(), editDocumentText(text2, document, root));
4965
+ writeFile(file(), editDocumentText(text3, document, root));
4788
4966
  },
4789
4967
  documentExists() {
4790
4968
  return fs2.existsSync(file());
@@ -4792,8 +4970,8 @@ function projectConfigFsAdapterAt(rootDir) {
4792
4970
  readText() {
4793
4971
  return readFileOrNull(file());
4794
4972
  },
4795
- writeText(text2) {
4796
- writeFile(file(), text2);
4973
+ writeText(text3) {
4974
+ writeFile(file(), text3);
4797
4975
  }
4798
4976
  };
4799
4977
  }
@@ -4822,9 +5000,9 @@ function storeOver(adapter, root) {
4822
5000
  },
4823
5001
  rewriteScalars(edits, dryRun = false) {
4824
5002
  if (edits.length === 0) return;
4825
- const text2 = adapter.readText();
4826
- if (text2 === null) throw new ProjectNotInitializedError();
4827
- const next = rewriteScalarsInText(text2, edits, root);
5003
+ const text3 = adapter.readText();
5004
+ if (text3 === null) throw new ProjectNotInitializedError();
5005
+ const next = rewriteScalarsInText(text3, edits, root);
4828
5006
  if (!dryRun) adapter.writeText(next);
4829
5007
  }
4830
5008
  };
@@ -4879,21 +5057,21 @@ function childrenOf(tokens, index) {
4879
5057
  }
4880
5058
  return out;
4881
5059
  }
4882
- function locateScalar(tokens, path37) {
5060
+ function locateScalar(tokens, path38) {
4883
5061
  let region = tokens;
4884
- for (let k = 0; k < path37.length; k += 1) {
4885
- const segment = path37[k];
4886
- if (region.length === 0) return `nothing is nested at ${path37.slice(0, k).join(".")}`;
5062
+ for (let k = 0; k < path38.length; k += 1) {
5063
+ const segment = path38[k];
5064
+ if (region.length === 0) return `nothing is nested at ${path38.slice(0, k).join(".")}`;
4887
5065
  const base = Math.min(...region.map((t) => t.col));
4888
- const last = k === path37.length - 1;
5066
+ const last = k === path38.length - 1;
4889
5067
  let at;
4890
5068
  if (typeof segment === "string") {
4891
5069
  at = region.findIndex((t) => t.kind === "key" && t.col === base && t.name === segment);
4892
- if (at < 0) return `no block key "${segment}" at ${path37.slice(0, k).join(".") || "the document root"}`;
5070
+ if (at < 0) return `no block key "${segment}" at ${path38.slice(0, k).join(".") || "the document root"}`;
4893
5071
  if (last) return region[at].valueStart !== void 0 ? region[at] : `"${segment}" holds no inline scalar`;
4894
5072
  } else {
4895
5073
  const items = region.map((t, i) => ({ t, i })).filter(({ t }) => t.kind === "item" && t.col === base);
4896
- if (segment >= items.length) return `no block sequence item ${segment} at ${path37.slice(0, k).join(".")}`;
5074
+ if (segment >= items.length) return `no block sequence item ${segment} at ${path38.slice(0, k).join(".")}`;
4897
5075
  at = items[segment].i;
4898
5076
  if (last) {
4899
5077
  const value = region[at + 1];
@@ -4905,26 +5083,26 @@ function locateScalar(tokens, path37) {
4905
5083
  }
4906
5084
  return "an empty path addresses no scalar";
4907
5085
  }
4908
- function readScalar(text2) {
4909
- if (text2.startsWith("'")) {
5086
+ function readScalar(text3) {
5087
+ if (text3.startsWith("'")) {
4910
5088
  let i = 1;
4911
5089
  let value = "";
4912
- while (i < text2.length) {
4913
- if (text2[i] === "'") {
4914
- if (text2[i + 1] === "'") {
5090
+ while (i < text3.length) {
5091
+ if (text3[i] === "'") {
5092
+ if (text3[i + 1] === "'") {
4915
5093
  value += "'";
4916
5094
  i += 2;
4917
5095
  continue;
4918
5096
  }
4919
5097
  return { value, style: "single", end: i + 1 };
4920
5098
  }
4921
- value += text2[i];
5099
+ value += text3[i];
4922
5100
  i += 1;
4923
5101
  }
4924
5102
  return null;
4925
5103
  }
4926
- if (text2.startsWith('"')) {
4927
- const closing = /^"(?:[^"\\]|\\.)*"/.exec(text2);
5104
+ if (text3.startsWith('"')) {
5105
+ const closing = /^"(?:[^"\\]|\\.)*"/.exec(text3);
4928
5106
  if (!closing) return null;
4929
5107
  try {
4930
5108
  return { value: JSON.parse(closing[0]), style: "double", end: closing[0].length };
@@ -4932,8 +5110,8 @@ function readScalar(text2) {
4932
5110
  return null;
4933
5111
  }
4934
5112
  }
4935
- const comment = text2.search(/\s#/);
4936
- const body = (comment < 0 ? text2 : text2.slice(0, comment)).trimEnd();
5113
+ const comment = text3.search(/\s#/);
5114
+ const body = (comment < 0 ? text3 : text3.slice(0, comment)).trimEnd();
4937
5115
  return { value: body, style: "plain", end: body.length };
4938
5116
  }
4939
5117
  function writeScalar(value, style) {
@@ -4941,18 +5119,18 @@ function writeScalar(value, style) {
4941
5119
  if (style === "plain" && /^[A-Za-z0-9_][A-Za-z0-9_./-]*$/.test(value)) return value;
4942
5120
  return `'${value.replace(/'/g, "''")}'`;
4943
5121
  }
4944
- function setAt(document, path37, value) {
5122
+ function setAt(document, path38, value) {
4945
5123
  let node = document;
4946
- for (const segment of path37.slice(0, -1)) node = node[segment];
4947
- node[path37[path37.length - 1]] = value;
5124
+ for (const segment of path38.slice(0, -1)) node = node[segment];
5125
+ node[path38[path38.length - 1]] = value;
4948
5126
  }
4949
- function rewriteScalarsInText(text2, edits, root) {
5127
+ function rewriteScalarsInText(text3, edits, root) {
4950
5128
  const refuse = (why) => {
4951
5129
  throw new WaironError(`Cannot rewrite .wai/project.yaml at ${root} without disturbing its formatting: ${why}. Nothing was written.`);
4952
5130
  };
4953
- const lines = text2.split(/(?<=\n)/);
5131
+ const lines = text3.split(/(?<=\n)/);
4954
5132
  const tokens = tokenize(lines);
4955
- const expected = parseYaml(text2);
5133
+ const expected = parseYaml(text3);
4956
5134
  for (const edit of edits) {
4957
5135
  const where = edit.path.join(".");
4958
5136
  const token = locateScalar(tokens, edit.path);
@@ -4994,17 +5172,17 @@ function sameValue(a, b) {
4994
5172
  }
4995
5173
  return a === b;
4996
5174
  }
4997
- function lineStartOf(text2, pos) {
4998
- return text2.lastIndexOf("\n", pos - 1) + 1;
5175
+ function lineStartOf(text3, pos) {
5176
+ return text3.lastIndexOf("\n", pos - 1) + 1;
4999
5177
  }
5000
- function lineEndFrom(text2, pos) {
5001
- if (pos === 0 || text2[pos - 1] === "\n") return pos;
5002
- const newline = text2.indexOf("\n", pos);
5003
- return newline < 0 ? text2.length : newline + 1;
5178
+ function lineEndFrom(text3, pos) {
5179
+ if (pos === 0 || text3[pos - 1] === "\n") return pos;
5180
+ const newline = text3.indexOf("\n", pos);
5181
+ return newline < 0 ? text3.length : newline + 1;
5004
5182
  }
5005
- function indentBlock(text2, col) {
5183
+ function indentBlock(text3, col) {
5006
5184
  const pad = " ".repeat(col);
5007
- const body = text2.endsWith("\n") ? text2 : `${text2}
5185
+ const body = text3.endsWith("\n") ? text3 : `${text3}
5008
5186
  `;
5009
5187
  return body.split("\n").map((line, i, all) => line === "" || i === all.length - 1 ? line : pad + line).join("\n");
5010
5188
  }
@@ -5037,12 +5215,12 @@ function schemaFieldOrder(at) {
5037
5215
  function inlineScalar(value, style) {
5038
5216
  if (value !== null && typeof value === "object") return null;
5039
5217
  const quoting = style === "QUOTE_SINGLE" || style === "QUOTE_DOUBLE" ? style : "PLAIN";
5040
- const text2 = YAML.stringify(value, { ...FRESH_TEXT_OPTIONS, lineWidth: 0, defaultStringType: quoting }).replace(/\n$/, "");
5041
- return text2.includes("\n") ? null : text2;
5218
+ const text3 = YAML.stringify(value, { ...FRESH_TEXT_OPTIONS, lineWidth: 0, defaultStringType: quoting }).replace(/\n$/, "");
5219
+ return text3.includes("\n") ? null : text3;
5042
5220
  }
5043
5221
  var DocumentReconciler = class {
5044
- constructor(text2, doc) {
5045
- this.text = text2;
5222
+ constructor(text3, doc) {
5223
+ this.text = text3;
5046
5224
  this.doc = doc;
5047
5225
  this.splices = [];
5048
5226
  }
@@ -5151,18 +5329,18 @@ var DocumentReconciler = class {
5151
5329
  }
5152
5330
  }
5153
5331
  };
5154
- function applySplices(text2, splices) {
5332
+ function applySplices(text3, splices) {
5155
5333
  const ordered = [...splices].sort((a, b) => b.start - a.start || b.end - a.end || b.rank - a.rank);
5156
- let out = text2;
5334
+ let out = text3;
5157
5335
  for (const splice of ordered) out = out.slice(0, splice.start) + splice.text + out.slice(splice.end);
5158
5336
  return out;
5159
5337
  }
5160
- function editDocumentText(text2, document, root) {
5338
+ function editDocumentText(text3, document, root) {
5161
5339
  const refuse = (why) => {
5162
5340
  throw new WaironError(`Cannot write .wai/project.yaml at ${root} as an edit of the file: ${why}. Nothing was written.`);
5163
5341
  };
5164
- const crlf = text2.includes("\r\n");
5165
- const source = crlf ? text2.replace(/\r\n/g, "\n") : text2;
5342
+ const crlf = text3.includes("\r\n");
5343
+ const source = crlf ? text3.replace(/\r\n/g, "\n") : text3;
5166
5344
  const doc = YAML.parseDocument(source, PARSE_OPTIONS);
5167
5345
  if (doc.errors.length > 0) refuse(`the file does not parse (${doc.errors[0].message})`);
5168
5346
  const reconciler = new DocumentReconciler(source, doc);
@@ -5271,19 +5449,21 @@ function registryOver(store, root) {
5271
5449
  }
5272
5450
  save(config);
5273
5451
  },
5452
+ // The four pack writes read the change through project_config.withPack —
5453
+ // the same reading a pack impact's dry run measures, so the report and the
5454
+ // write cannot disagree about what the configuration becomes.
5274
5455
  upsertPackSelection(selection) {
5275
5456
  const config = current2();
5276
5457
  const existing = packsOf(config);
5277
- const without = existing.filter((entry) => typeof entry === "string" || entry.name !== selection.name);
5278
- save(withPacks(config, [...without, selection]));
5279
- return without.length !== existing.length;
5458
+ const replaced = existing.some((entry) => typeof entry !== "string" && entry.name === selection.name);
5459
+ save(withPacks(config, packsOf(withPack(config, selection))));
5460
+ return replaced;
5280
5461
  },
5281
5462
  removePackSelection(packName) {
5282
5463
  const config = current2();
5283
5464
  const existing = packsOf(config);
5284
- const remaining = existing.filter((entry) => typeof entry === "string" || entry.name !== packName);
5285
- if (remaining.length === existing.length) return false;
5286
- save(withPacks(config, remaining));
5465
+ if (!existing.some((entry) => typeof entry !== "string" && entry.name === packName)) return false;
5466
+ save(withPacks(config, packsOf(withPack(config, { name: packName }, true))));
5287
5467
  return true;
5288
5468
  },
5289
5469
  setProjectType(projectType) {
@@ -5298,17 +5478,14 @@ function registryOver(store, root) {
5298
5478
  },
5299
5479
  registerPackRef(ref) {
5300
5480
  const config = current2();
5301
- const packs = packsOf(config);
5302
- if (packs.includes(ref)) return false;
5303
- save(withPacks(config, [...packs, ref]));
5481
+ if (packsOf(config).includes(ref)) return false;
5482
+ save(withPacks(config, packsOf(withPack(config, ref))));
5304
5483
  return true;
5305
5484
  },
5306
5485
  deregisterPackRef(ref) {
5307
5486
  const config = current2();
5308
- const packs = packsOf(config);
5309
- const remaining = packs.filter((entry) => entry !== ref);
5310
- if (remaining.length === packs.length) return false;
5311
- save(withPacks(config, remaining));
5487
+ if (!packsOf(config).includes(ref)) return false;
5488
+ save(withPacks(config, packsOf(withPack(config, ref, true))));
5312
5489
  return true;
5313
5490
  },
5314
5491
  markSelectionsBundled(bundled) {
@@ -5338,9 +5515,9 @@ function registryOver(store, root) {
5338
5515
  const next = rekeyAnchor(rename, { spec: entry.spec, at, covers });
5339
5516
  const base = ["rules", "conformance", "carried", g, "findings", f];
5340
5517
  const named2 = { code: String(entry.code), spec: entry.spec, at: at ?? "" };
5341
- const change = (path37, field, from, to) => {
5518
+ const change = (path38, field, from, to) => {
5342
5519
  if (from === to) return;
5343
- edits.push({ path: [...base, ...path37], from, to });
5520
+ edits.push({ path: [...base, ...path38], from, to });
5344
5521
  rekeys.push({ ...named2, field, from, to });
5345
5522
  };
5346
5523
  change(["spec"], "spec", entry.spec, next.spec);
@@ -5652,11 +5829,11 @@ var projectConfigRepository = {
5652
5829
  };
5653
5830
 
5654
5831
  // src/core/domain_projector.ts
5655
- var path19 = __toESM(require("path"));
5832
+ var path20 = __toESM(require("path"));
5656
5833
 
5657
5834
  // src/core/specs.ts
5658
5835
  var fs12 = __toESM(require("fs"));
5659
- var path18 = __toESM(require("path"));
5836
+ var path19 = __toESM(require("path"));
5660
5837
 
5661
5838
  // src/config/paths.ts
5662
5839
  var fs3 = __toESM(require("fs"));
@@ -6437,9 +6614,9 @@ var MethodSignatureSchema = import_zod8.z.object({
6437
6614
  ext: ExtDataSchema.optional()
6438
6615
  });
6439
6616
  var INTENT_FLOOR_MIN_CHARS = 40;
6440
- function passesIntentFloor(text2, methodName) {
6441
- if (!text2) return false;
6442
- const t = text2.trim();
6617
+ function passesIntentFloor(text3, methodName) {
6618
+ if (!text3) return false;
6619
+ const t = text3.trim();
6443
6620
  if (t.length < INTENT_FLOOR_MIN_CHARS) return false;
6444
6621
  const norm = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
6445
6622
  if (norm(t) === norm(methodName)) return false;
@@ -7205,7 +7382,7 @@ function stepGraph(method) {
7205
7382
  for (const s of steps) byNum.set(s.stepNumber, s);
7206
7383
  const nums = [...byNum.keys()].sort((a, b) => a - b);
7207
7384
  const indexOf = new Map(nums.map((n, i) => [n, i]));
7208
- const nextOf = (n) => {
7385
+ const nextOf2 = (n) => {
7209
7386
  const i = indexOf.get(n);
7210
7387
  return i !== void 0 && i + 1 < nums.length ? nums[i + 1] : void 0;
7211
7388
  };
@@ -7214,7 +7391,7 @@ function stepGraph(method) {
7214
7391
  return i !== void 0 && i > 0 ? nums[i - 1] : void 0;
7215
7392
  };
7216
7393
  const armEndJoin = /* @__PURE__ */ new Map();
7217
- const fallNext = (n) => armEndJoin.has(n) ? armEndJoin.get(n) : nextOf(n);
7394
+ const fallNext = (n) => armEndJoin.has(n) ? armEndJoin.get(n) : nextOf2(n);
7218
7395
  for (const n of nums) {
7219
7396
  const s = byNum.get(n);
7220
7397
  if (s.type !== "parallel" || s.endStep === void 0 || !s.branches?.length) continue;
@@ -7243,11 +7420,11 @@ function stepGraph(method) {
7243
7420
  succ.push(...(s.cases ?? []).map((c) => c.step), s.defaultStep ?? fallNext(n));
7244
7421
  break;
7245
7422
  case "loop":
7246
- succ.push(nextOf(n), s.endStep !== void 0 ? fallNext(s.endStep) : void 0);
7423
+ succ.push(nextOf2(n), s.endStep !== void 0 ? fallNext(s.endStep) : void 0);
7247
7424
  break;
7248
7425
  case "try":
7249
7426
  succ.push(
7250
- nextOf(n),
7427
+ nextOf2(n),
7251
7428
  ...(s.catches ?? []).map((c) => c.step),
7252
7429
  s.finallyStep,
7253
7430
  s.endStep !== void 0 ? fallNext(s.endStep) : void 0
@@ -7265,7 +7442,7 @@ function stepGraph(method) {
7265
7442
  }
7266
7443
  return [...new Set(succ.filter((t) => t !== void 0 && byNum.has(t)))];
7267
7444
  };
7268
- return { stepNumbers: nums, stepAt: (n) => byNum.get(n), nextOf, successorsOf };
7445
+ return { stepNumbers: nums, stepAt: (n) => byNum.get(n), nextOf: nextOf2, successorsOf };
7269
7446
  }
7270
7447
 
7271
7448
  // src/models/step-config.ts
@@ -7362,7 +7539,7 @@ function stepConfigVerdict(method) {
7362
7539
  }
7363
7540
  const nums = [...byNum.keys()].sort((a, b) => a - b);
7364
7541
  const indexOf = new Map(nums.map((n, i) => [n, i]));
7365
- const nextOf = (n) => {
7542
+ const nextOf2 = (n) => {
7366
7543
  const i = indexOf.get(n);
7367
7544
  return i !== void 0 && i + 1 < nums.length ? nums[i + 1] : void 0;
7368
7545
  };
@@ -7414,7 +7591,7 @@ function stepConfigVerdict(method) {
7414
7591
  const sorted = [...entries].sort((a, b) => a - b);
7415
7592
  if (entries.some((e, i) => e !== sorted[i])) malformed(s.stepNumber, `parallel step ${s.stepNumber} "branches" must list arm entries in ascending order \u2014 arms are contiguous sub-regions of the body.`);
7416
7593
  if (new Set(entries).size !== entries.length) malformed(s.stepNumber, `parallel step ${s.stepNumber} "branches" lists the same entry step twice.`);
7417
- const bodyStart = nextOf(s.stepNumber);
7594
+ const bodyStart = nextOf2(s.stepNumber);
7418
7595
  if (bodyStart !== void 0 && sorted[0] !== bodyStart) malformed(s.stepNumber, `parallel step ${s.stepNumber}: the first arm must start at the body's first step (${bodyStart}), got ${sorted[0]} \u2014 steps before the first arm would belong to no arm.`);
7419
7596
  if (s.endStep !== void 0 && sorted.some((e) => e > s.endStep)) malformed(s.stepNumber, `parallel step ${s.stepNumber}: every arm entry must lie within the body (..${s.endStep}).`);
7420
7597
  }
@@ -7524,8 +7701,8 @@ function ambiguityMessage(resolution, subject, ref) {
7524
7701
  const local = ref.split("::").filter((seg) => seg && seg !== "super").pop() ?? ref;
7525
7702
  return `${subject} cross-tree component "${ref}", which the surface snapshots of ${providers.map((p) => `"${p}"`).join(", ")} expose with different contracts \u2014 the reference matches more than one declared contract, so none of them can judge it. Name the provider it means (super::<provider>::${local}), or remove the snapshot that no longer applies.`;
7526
7703
  }
7527
- function sha256(text2) {
7528
- return `sha256:${(0, import_crypto.createHash)("sha256").update(text2).digest("hex")}`;
7704
+ function sha256(text3) {
7705
+ return `sha256:${(0, import_crypto.createHash)("sha256").update(text3).digest("hex")}`;
7529
7706
  }
7530
7707
  var TYPE_IDENTIFIER = /(?:::)?[A-Za-z0-9_][A-Za-z0-9_-]*(?:(?:::|\.)[A-Za-z0-9_][A-Za-z0-9_-]*)*/g;
7531
7708
  function closureTypeOf(snapshot, identifier) {
@@ -7781,9 +7958,9 @@ function shortestPath(from, to, members, edges, rank) {
7781
7958
  const v = queue.shift();
7782
7959
  for (const w of [...edges.get(v) ?? []].filter((x) => members.has(x)).sort((a, b) => rank(a) - rank(b))) {
7783
7960
  if (w === to) {
7784
- const path37 = [w];
7785
- for (let at = v; at !== from; at = prev.get(at)) path37.unshift(at);
7786
- return path37;
7961
+ const path38 = [w];
7962
+ for (let at = v; at !== from; at = prev.get(at)) path38.unshift(at);
7963
+ return path38;
7787
7964
  }
7788
7965
  if (seen.has(w)) continue;
7789
7966
  seen.add(w);
@@ -8198,12 +8375,12 @@ function buildExcalidrawScene(model, L) {
8198
8375
  locked: false
8199
8376
  };
8200
8377
  }
8201
- function boundLabel(rect, text2, fontSize, verticalAlign) {
8378
+ function boundLabel(rect, text3, fontSize, verticalAlign) {
8202
8379
  const id = rect.id + "-label";
8203
8380
  const label2 = base(id, "text", { x: rect.x + 8, y: rect.y + 6, w: rect.width - 16, h: 20 });
8204
8381
  label2.roundness = null;
8205
- label2.text = text2;
8206
- label2.originalText = text2;
8382
+ label2.text = text3;
8383
+ label2.originalText = text3;
8207
8384
  label2.fontSize = fontSize;
8208
8385
  label2.fontFamily = 1;
8209
8386
  label2.textAlign = "center";
@@ -12742,10 +12919,10 @@ function lockPath2(root) {
12742
12919
  function snapshotPath(root, alias) {
12743
12920
  return path9.join(root, ".wai", "externals", `${alias}.yaml`);
12744
12921
  }
12745
- function writeAtomically(file, text2) {
12922
+ function writeAtomically(file, text3) {
12746
12923
  fs7.mkdirSync(path9.dirname(file), { recursive: true });
12747
12924
  const tmp = `${file}.${process.pid}.tmp`;
12748
- fs7.writeFileSync(tmp, text2);
12925
+ fs7.writeFileSync(tmp, text3);
12749
12926
  fs7.renameSync(tmp, file);
12750
12927
  }
12751
12928
  function sortedLock(lock) {
@@ -13214,6 +13391,109 @@ function listPinnedExternals2() {
13214
13391
 
13215
13392
  // src/core/family-validation.ts
13216
13393
  var path11 = __toESM(require("path"));
13394
+
13395
+ // src/models/pack-impact.ts
13396
+ function packSettings(manifest, profile) {
13397
+ const severities = {};
13398
+ for (const rule of manifest.rules) {
13399
+ for (const code of rule.codes) severities[code.code] = code.defaultSeverity;
13400
+ }
13401
+ for (const assertion of manifest.assertions) severities[assertionCode(manifest.name, assertion.code)] = assertion.severity;
13402
+ const def = profile !== null ? manifest.profiles[profile] : void 0;
13403
+ if (!def) return { pack: manifest.name, severities };
13404
+ Object.assign(severities, def.rules?.sddRuleSeverity ?? {});
13405
+ const rules = def.rules;
13406
+ return {
13407
+ pack: manifest.name,
13408
+ profile,
13409
+ severities,
13410
+ ...rules?.designDepth ? { designDepth: rules.designDepth } : {},
13411
+ ...rules?.naming ? { naming: rules.naming } : {},
13412
+ ...rules?.complexity ? { complexity: rules.complexity } : {},
13413
+ ...rules?.documentation ? { documentation: rules.documentation } : {}
13414
+ };
13415
+ }
13416
+ function assertionCode(packName, code) {
13417
+ const norm = (s) => s.toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
13418
+ return `${norm(packName)}_${norm(code)}`;
13419
+ }
13420
+ var RANK = { notice: 1, warning: 2, error: 3 };
13421
+ function severityChange(code, to, profile, baseline, packCodes) {
13422
+ const from = baseline.ruleDefaults[code];
13423
+ if (from === void 0) {
13424
+ return { axis: "rule", change: packCodes.has(code) ? "added" : "inert", subject: code, profile, to };
13425
+ }
13426
+ if (from === to) return null;
13427
+ if (to === "off") return { axis: "rule", change: "off", subject: code, profile, from, to };
13428
+ return { axis: "rule", change: (RANK[to] ?? 0) < (RANK[from] ?? 0) ? "loosened" : "raised", subject: code, profile, from, to };
13429
+ }
13430
+ function profileChanges(id, manifest, baseline, packCodes) {
13431
+ const def = manifest.profiles[id];
13432
+ const out = [{ axis: "concept", change: baseline.profiles.includes(id) ? "redefined" : "added", subject: id, profile: id }];
13433
+ for (const [code, to] of Object.entries(def.rules?.sddRuleSeverity ?? {})) {
13434
+ const change = severityChange(code, to, id, baseline, packCodes);
13435
+ if (change) out.push(change);
13436
+ }
13437
+ const depth = def.rules?.designDepth;
13438
+ if (depth && depth !== baseline.designDepth) out.push({ axis: "rule", change: "depth", subject: "designDepth", profile: id, from: baseline.designDepth, to: depth });
13439
+ for (const f of def.forbiddenStereotypes) for (const t of f.types) out.push({ axis: "concept", change: "removed", subject: t, profile: id, reason: f.reason });
13440
+ for (const d of def.discouragedStereotypes) for (const t of d.types) out.push({ axis: "concept", change: "discouraged", subject: t, profile: id, reason: d.reason });
13441
+ for (const e of def.allowedEdges) {
13442
+ for (const from of e.from) for (const to of e.to) out.push({ axis: "concept", change: "licensed", subject: `${from} \u2192 ${to}`, profile: id, reason: e.reason });
13443
+ }
13444
+ return out;
13445
+ }
13446
+ function packWideChanges(manifest, baseline) {
13447
+ const out = [];
13448
+ for (const a of manifest.assertions) {
13449
+ out.push({ axis: "rule", change: "added", subject: assertionCode(manifest.name, a.code), to: a.severity, reason: a.reason });
13450
+ }
13451
+ for (const rule of manifest.rules) {
13452
+ for (const code of rule.codes) out.push({ axis: "rule", change: "added", subject: code.code, to: code.defaultSeverity });
13453
+ }
13454
+ for (const p of manifest.patterns) out.push({ axis: "concept", change: "added", subject: `pattern ${p.id}@${p.version}` });
13455
+ for (const g of manifest.guarantees) {
13456
+ if (!baseline.guarantees.includes(g)) out.push({ axis: "concept", change: "added", subject: `guarantee ${g}` });
13457
+ }
13458
+ for (const [lang, def] of Object.entries(manifest.languages)) {
13459
+ out.push({ axis: "concept", change: "added", subject: `language ${lang}` });
13460
+ for (const [construct, guidance] of Object.entries(def.unsupportedFlow)) {
13461
+ out.push({ axis: "concept", change: "gated", subject: `${lang}: ${construct}`, reason: guidance });
13462
+ }
13463
+ }
13464
+ return out;
13465
+ }
13466
+ function doctrineChanges(manifest, baseline) {
13467
+ const packCodes = /* @__PURE__ */ new Set([
13468
+ ...manifest.assertions.map((a) => assertionCode(manifest.name, a.code)),
13469
+ ...manifest.rules.flatMap((r) => r.codes.map((c) => c.code))
13470
+ ]);
13471
+ const perProfile = Object.keys(manifest.profiles).flatMap((id) => profileChanges(id, manifest, baseline, packCodes));
13472
+ return [...perProfile, ...packWideChanges(manifest, baseline)];
13473
+ }
13474
+ function identity(issue2) {
13475
+ const message = issue2.message.replace(/\b(error|warning|notice)s?\b/gi, "").replace(/\s+/g, " ").trim();
13476
+ return [issue2.code, issue2.project ?? "", issue2.specId ?? "", message].join("\0");
13477
+ }
13478
+ function findingChanges(before, after) {
13479
+ const pending = /* @__PURE__ */ new Map();
13480
+ for (const issue2 of before.issues) {
13481
+ const key = identity(issue2);
13482
+ pending.set(key, [...pending.get(key) ?? [], issue2]);
13483
+ }
13484
+ const introduced = [];
13485
+ const regraded = [];
13486
+ for (const issue2 of after.issues) {
13487
+ const matches = pending.get(identity(issue2));
13488
+ const earlier = matches?.shift();
13489
+ if (!earlier) introduced.push(issue2);
13490
+ else if (earlier.severity !== issue2.severity) regraded.push({ finding: issue2, from: earlier.severity });
13491
+ }
13492
+ const resolved = [...pending.values()].flat();
13493
+ return { introduced, resolved, regraded };
13494
+ }
13495
+
13496
+ // src/core/family-validation.ts
13217
13497
  var FAMILY_SEVERITY = {
13218
13498
  EXTERNAL_INCOMPATIBLE: "error",
13219
13499
  EXTERNAL_DRIFTED: "notice",
@@ -13224,7 +13504,11 @@ var FAMILY_SEVERITY = {
13224
13504
  MEMBER_UNAPPROVED: "warning",
13225
13505
  MEMBER_DRIFTED: "warning",
13226
13506
  PROJECT_ID_DEFAULTED: "notice",
13227
- PROJECT_ID_AMBIGUOUS: "warning"
13507
+ PROJECT_ID_AMBIGUOUS: "warning",
13508
+ // Governance: a required pack not adopted blocks; a member's deviation from
13509
+ // an adopted pack's settings is visible and never fails --ci.
13510
+ POLICY_NOT_ADOPTED: "error",
13511
+ POLICY_DEVIATION: "notice"
13228
13512
  };
13229
13513
  function configOrNull() {
13230
13514
  try {
@@ -13326,6 +13610,7 @@ function run(options) {
13326
13610
  left.push(...judged.composed.notSelected.map((alias) => ({ project: node.namespace, alias })));
13327
13611
  }
13328
13612
  issues.push(...within(root, ceiling, () => checkMembers(narrowed(family, selected))));
13613
+ issues.push(...within(root, ceiling, () => checkPolicies(narrowed(family, selected))));
13329
13614
  const hint = notSelectedHint(left);
13330
13615
  return {
13331
13616
  valid: issues.every((i) => i.severity !== "error"),
@@ -13476,6 +13761,147 @@ function checkMembers(family) {
13476
13761
  }
13477
13762
  return out.filter((f) => f !== null);
13478
13763
  }
13764
+ function requiredBy(key) {
13765
+ return key === "" ? "the family root" : `"${key}"`;
13766
+ }
13767
+ function requirementText(requirement) {
13768
+ return `"${requirement.pack}" ${requirement.version}${requirement.profile ? ` (profile "${requirement.profile}")` : ""}`;
13769
+ }
13770
+ function isBelow(family, node, ancestor) {
13771
+ let at = node;
13772
+ while (at && at.parent !== void 0) {
13773
+ if (at.parent === ancestor) return true;
13774
+ at = familyNode(family, at.parent);
13775
+ }
13776
+ return false;
13777
+ }
13778
+ function policyIssue(code, message, member, specId) {
13779
+ return { severity: FAMILY_SEVERITY[code], code, message, project: member, ...specId !== void 0 ? { specId } : {} };
13780
+ }
13781
+ function tunedBy(config, issues) {
13782
+ const out = [];
13783
+ for (const issue2 of issues) {
13784
+ const tuned = finding(config, issue2.code, issue2.message, issue2.project ?? "", issue2.specId);
13785
+ if (tuned) out.push(tuned);
13786
+ }
13787
+ return out;
13788
+ }
13789
+ function checkPolicies(family) {
13790
+ const out = [];
13791
+ for (const requirer of family.nodes) {
13792
+ const members = family.nodes.filter((n) => isBelow(family, n, requirer.namespace));
13793
+ if (members.length === 0) continue;
13794
+ const config = runWithProjectRoot(requirer.directory, configOrNull);
13795
+ const valid = config ? requiredPolicies(config).filter((r) => r.pack.trim() !== "" && rangeProblem(r) === null) : [];
13796
+ if (valid.length === 0) continue;
13797
+ const caused = [];
13798
+ for (const member of members) {
13799
+ caused.push(...runWithProjectRoot(member.directory, () => judgeAdoption(requirer.namespace, member.namespace, valid)));
13800
+ }
13801
+ out.push(...tunedBy(config, caused));
13802
+ }
13803
+ return out;
13804
+ }
13805
+ function entryFor(config, loaded, pack) {
13806
+ const entries = config.extensions?.packs ?? [];
13807
+ const selection = entries.find((e) => typeof e !== "string" && e.name === pack);
13808
+ if (selection) return { entry: selection, ...selection.version ? { version: selection.version } : {}, label: selection.version ? `${selection.name}@${selection.version}` : selection.name };
13809
+ for (const ref of entries.filter((e) => typeof e === "string")) {
13810
+ const committed = loaded.packs.find((p) => p.scope === "project" && p.ref === ref && p.name === pack);
13811
+ if (committed) return { entry: ref, ...committed.version ? { version: committed.version } : {}, label: `${ref} (${committed.name}${committed.version ? `@${committed.version}` : ", unversioned"})` };
13812
+ }
13813
+ return null;
13814
+ }
13815
+ function judgeAdoption(requiring, member, requirements) {
13816
+ const config = configOrNull();
13817
+ if (!config) return [];
13818
+ const loaded = loadProjectExtensions();
13819
+ const out = [];
13820
+ const who = `The member ${named(member)}`;
13821
+ for (const requirement of requirements) {
13822
+ const wanted = `${requirementText(requirement)}, required by ${requiredBy(requiring)}`;
13823
+ const adopted = entryFor(config, loaded, requirement.pack);
13824
+ if (!adopted) {
13825
+ out.push(policyIssue("POLICY_NOT_ADOPTED", `${who} does not select the pack ${wanted}. Select it in the member's own configuration (\`wairon pack use ${requirement.pack}@<version> --pin\` at its root).`, member));
13826
+ continue;
13827
+ }
13828
+ if (typeof adopted.entry !== "string" && !adopted.entry.version) {
13829
+ out.push(policyIssue("POLICY_NOT_ADOPTED", `${who} selects "${adopted.label}" for the pack ${wanted}, unpinned \u2014 a family cannot judge a floating selection; pin a version (\`wairon pack use ${requirement.pack}@<version> --pin\`).`, member));
13830
+ continue;
13831
+ }
13832
+ if (!admits(requirement, adopted.version)) {
13833
+ out.push(policyIssue("POLICY_NOT_ADOPTED", `${who} selects "${adopted.label}", outside the range of the pack ${wanted}: ${adopted.version ? `version ${adopted.version} is not admitted by "${requirement.version}"` : `an unversioned pack is admitted by "*" only`}.`, member));
13834
+ continue;
13835
+ }
13836
+ if (requirement.profile && config.projectType !== requirement.profile) {
13837
+ out.push(policyIssue("POLICY_NOT_ADOPTED", `${who} selects "${adopted.label}" for the pack ${wanted}, but is governed by "${config.projectType}", not the required profile "${requirement.profile}". Set its projectType to "${requirement.profile}".`, member));
13838
+ continue;
13839
+ }
13840
+ const manifest = manifestOf(adopted.entry);
13841
+ if (!manifest) continue;
13842
+ const governing = requirement.profile ?? (config.projectType in manifest.profiles ? config.projectType : null);
13843
+ out.push(...policyDeviations(member, requirement, packSettings(manifest, governing)));
13844
+ }
13845
+ return out;
13846
+ }
13847
+ function shown(value) {
13848
+ return typeof value === "string" ? `"${value}"` : JSON.stringify(value);
13849
+ }
13850
+ function keyDeviations(label2, packValues, memberValues) {
13851
+ const out = [];
13852
+ for (const [key, value] of Object.entries(packValues ?? {})) {
13853
+ if (value === void 0 || key === "stereotypes") continue;
13854
+ const mine = memberValues?.[key];
13855
+ if (mine !== void 0 && JSON.stringify(mine) !== JSON.stringify(value)) out.push({ setting: `${label2}.${key}`, pack: value, member: mine });
13856
+ }
13857
+ return out;
13858
+ }
13859
+ function lintAllows() {
13860
+ const specs = [
13861
+ ...loadSubsystemSpecs(),
13862
+ ...loadComponentSpecs(),
13863
+ ...loadInterfaceSpecs(),
13864
+ ...loadImplementationSpecs(),
13865
+ ...loadTypeSpecs()
13866
+ ];
13867
+ return specs.flatMap((s) => (s.lint?.allow ?? []).map((a) => ({ specId: s.id, code: a.code, ...a.at !== void 0 ? { at: a.at } : {} })));
13868
+ }
13869
+ function policyDeviations(member, requirement, settings) {
13870
+ const rules = configOrNull()?.rules;
13871
+ const subsystems = loadSubsystemSpecs();
13872
+ const allows = lintAllows();
13873
+ const under = settings.profile ? `under its profile "${settings.profile}"` : "with none of its profiles governing";
13874
+ const about = `the pack "${requirement.pack}" (required ${requirement.version})`;
13875
+ const deviation = (setting, pack, mine, specId) => policyIssue("POLICY_DEVIATION", `The member ${named(member)} adopted ${about} but changes ${setting}: the pack sets ${shown(pack)} ${under}, the member ${shown(mine)}. Visible, not blocking \u2014 the member's own configuration decides.`, member, specId);
13876
+ const out = [];
13877
+ for (const [code, severity] of Object.entries(rules?.sddRuleSeverity ?? {})) {
13878
+ const packs = settings.severities[code];
13879
+ if (packs !== void 0 && packs !== severity) out.push(deviation(`rules.sddRuleSeverity.${code}`, packs, severity));
13880
+ }
13881
+ if (settings.designDepth) {
13882
+ if (rules?.designDepth && rules.designDepth !== settings.designDepth) out.push(deviation("rules.designDepth", settings.designDepth, rules.designDepth));
13883
+ for (const s of subsystems) {
13884
+ if (s.designDepth && s.designDepth !== settings.designDepth) out.push(deviation(`the designDepth of subsystem "${s.id}"`, settings.designDepth, s.designDepth, s.id));
13885
+ }
13886
+ }
13887
+ const overlays = [
13888
+ ...keyDeviations("rules.naming", settings.naming, rules?.naming),
13889
+ ...keyDeviations("rules.naming.stereotypes", settings.naming?.stereotypes, rules?.naming?.stereotypes),
13890
+ ...keyDeviations("rules.complexity", settings.complexity, rules?.complexity),
13891
+ ...keyDeviations("rules.documentation", settings.documentation, rules?.documentation)
13892
+ ];
13893
+ for (const o of overlays) out.push(deviation(o.setting, o.pack, o.member));
13894
+ if (settings.profile) {
13895
+ for (const s of subsystems) {
13896
+ if (s.profile && s.profile !== settings.profile) out.push(deviation(`the profile of subsystem "${s.id}"`, settings.profile, s.profile, s.id));
13897
+ }
13898
+ }
13899
+ for (const allow of allows) {
13900
+ if (settings.severities[allow.code] === void 0) continue;
13901
+ out.push(deviation(`a lint.allow of ${allow.code}${allow.at ? ` at "${allow.at}"` : ""} on "${allow.specId}"`, settings.severities[allow.code], "allowed", allow.specId));
13902
+ }
13903
+ return out;
13904
+ }
13479
13905
 
13480
13906
  // src/core/rules/index.ts
13481
13907
  var import_crypto2 = require("crypto");
@@ -13487,28 +13913,6 @@ var path14 = __toESM(require("path"));
13487
13913
  var import_module = require("module");
13488
13914
  var import_zod9 = require("zod");
13489
13915
 
13490
- // src/utils/version.ts
13491
- function isNewerVersion(current2, candidate) {
13492
- const baseVersion = (v) => v.replace(/-.*$/, "");
13493
- const preRelease = (v) => {
13494
- const match = v.match(/-(.+)\.(\d+)$/);
13495
- return match ? { label: match[1], n: parseInt(match[2], 10) } : null;
13496
- };
13497
- const parse = (v) => baseVersion(v).split(".").map((n) => parseInt(n, 10) || 0);
13498
- const [cMaj, cMin, cPat] = parse(current2);
13499
- const [nMaj, nMin, nPat] = parse(candidate);
13500
- if (nMaj !== cMaj) return nMaj > cMaj;
13501
- if (nMin !== cMin) return nMin > cMin;
13502
- if (nPat !== cPat) return nPat > cPat;
13503
- const cPre = preRelease(current2);
13504
- const nPre = preRelease(candidate);
13505
- if (!cPre && !nPre) return false;
13506
- if (!cPre && nPre) return false;
13507
- if (cPre && !nPre) return true;
13508
- if (cPre && nPre) return nPre.n > cPre.n;
13509
- return false;
13510
- }
13511
-
13512
13916
  // src/core/packstore.ts
13513
13917
  var crypto2 = __toESM(require("crypto"));
13514
13918
  var fs9 = __toESM(require("fs"));
@@ -13532,7 +13936,7 @@ function defaultTargetConfig(type) {
13532
13936
  enabled: true
13533
13937
  };
13534
13938
  }
13535
- var WAIRON_VERSION = "5.1.1-dev.84";
13939
+ var WAIRON_VERSION = "5.1.1-dev.85";
13536
13940
  var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
13537
13941
  var ARCHITECT_AGENT_ID = "agent-architect";
13538
13942
  var ARCHITECT_TEMPLATE_ID = "architect";
@@ -13783,7 +14187,7 @@ var PackSkillSchema = import_zod9.z.object({
13783
14187
  });
13784
14188
  var PackInstructionBlockSchema = import_zod9.z.union([
13785
14189
  // Scalar shorthand: `instructions: >- …` — the common case, unscoped.
13786
- import_zod9.z.string().min(1).transform((text2) => ({ text: text2 })),
14190
+ import_zod9.z.string().min(1).transform((text3) => ({ text: text3 })),
13787
14191
  import_zod9.z.object({
13788
14192
  text: import_zod9.z.string().min(1),
13789
14193
  profile: import_zod9.z.array(import_zod9.z.string().min(1)).min(1).optional()
@@ -13884,6 +14288,11 @@ function checkDeclarativePack(manifest) {
13884
14288
  const result = DeclarativePackSchema.safeParse(manifest);
13885
14289
  return result.success ? null : result.error.issues[0]?.message ?? "shape mismatch";
13886
14290
  }
14291
+ function parseDeclarativePack(manifest) {
14292
+ const result = DeclarativePackSchema.safeParse(manifest);
14293
+ if (!result.success) throw new Error(result.error.issues[0]?.message ?? "shape mismatch");
14294
+ return { ...result.data, rules: [] };
14295
+ }
13887
14296
  function globalPacksEnabled(config) {
13888
14297
  return config.extensions?.useGlobalPacks ?? GLOBAL_PACKS_DEFAULT;
13889
14298
  }
@@ -14004,32 +14413,57 @@ function readManifest(target, projectRoot) {
14004
14413
  }
14005
14414
  function loadExtensionPacks(refs, projectRoot) {
14006
14415
  const out = emptyExtensions();
14007
- for (const { ref, scope } of refs) {
14008
- try {
14009
- const target = resolvePackRef(ref, projectRoot);
14010
- const manifest = readManifest(target, projectRoot);
14011
- mergePack(out, manifest, ref, scope, path14.dirname(target));
14012
- for (const r of manifest.rules) out.rules.push(r);
14013
- } catch (e) {
14014
- out.errors.push(`Extension pack "${ref}" failed to load: ${e instanceof Error ? e.message : String(e)}`);
14416
+ for (const ref of refs) loadRefInto(out, ref, projectRoot);
14417
+ return out;
14418
+ }
14419
+ function loadRefInto(out, { ref, scope, manifest: supplied }, projectRoot) {
14420
+ try {
14421
+ if (supplied) {
14422
+ mergePack(out, supplied, ref, scope, path14.resolve(projectRoot, ref));
14423
+ for (const r of supplied.rules) out.rules.push(r);
14424
+ return;
14015
14425
  }
14426
+ const target = resolvePackRef(ref, projectRoot);
14427
+ const manifest = readManifest(target, projectRoot);
14428
+ mergePack(out, manifest, ref, scope, path14.dirname(target));
14429
+ for (const r of manifest.rules) out.rules.push(r);
14430
+ } catch (e) {
14431
+ out.errors.push(`Extension pack "${ref}" failed to load: ${e instanceof Error ? e.message : String(e)}`);
14016
14432
  }
14017
- return out;
14018
14433
  }
14019
14434
  function loadExtensions(packRefs, projectRoot) {
14020
14435
  return loadExtensionPacks(packRefs.map((ref) => ({ ref, scope: "project" })), projectRoot);
14021
14436
  }
14022
14437
  function loadProjectExtensions() {
14438
+ let config;
14439
+ try {
14440
+ config = projectConfigRepository.load();
14441
+ } catch {
14442
+ config = null;
14443
+ }
14444
+ if (!config) return emptyExtensions();
14445
+ return loadFor(config);
14446
+ }
14447
+ function isCandidateEntry(entry, candidate) {
14448
+ const target = candidate?.entry;
14449
+ if (target === void 0) return false;
14450
+ if (typeof target === "string") return entry === target;
14451
+ return typeof entry !== "string" && entry.name === target.name;
14452
+ }
14453
+ function loadFor(config, candidate) {
14023
14454
  try {
14024
- const config = projectConfigRepository.load();
14025
- if (!config) return emptyExtensions();
14026
14455
  const projectRoot = getProjectRoot();
14027
14456
  const refs = [];
14028
14457
  const unresolved = [];
14458
+ const supplied = candidate?.manifest && !candidate.remove ? candidate.manifest : void 0;
14029
14459
  if (globalPacksEnabled(config)) {
14030
14460
  for (const ref of discoverPacks(globalPacksDir())) refs.push({ ref, scope: "global" });
14031
14461
  }
14032
14462
  for (const entry of config.extensions?.packs ?? []) {
14463
+ if (supplied && isCandidateEntry(entry, candidate)) {
14464
+ refs.push({ ref: packEntryLabel(entry), scope: "project", manifest: supplied });
14465
+ continue;
14466
+ }
14033
14467
  if (typeof entry === "string") {
14034
14468
  refs.push({ ref: entry, scope: "project" });
14035
14469
  continue;
@@ -14039,7 +14473,8 @@ function loadProjectExtensions() {
14039
14473
  if (resolution.failure) unresolved.push(resolution.failure);
14040
14474
  }
14041
14475
  if (refs.length === 0 && unresolved.length === 0) return emptyExtensions();
14042
- const loaded = loadExtensionPacks(refs, projectRoot);
14476
+ const loaded = emptyExtensions();
14477
+ for (const item of refs) loadRefInto(loaded, item, projectRoot);
14043
14478
  for (const problem of skillExtendErrors(loaded.skills)) loaded.errors.push(problem);
14044
14479
  loaded.selectionFailures.push(...unresolved);
14045
14480
  return loaded;
@@ -14047,6 +14482,23 @@ function loadProjectExtensions() {
14047
14482
  return emptyExtensions();
14048
14483
  }
14049
14484
  }
14485
+ function manifestOf(entry) {
14486
+ const projectRoot = getProjectRoot();
14487
+ let ref;
14488
+ if (typeof entry !== "string") {
14489
+ const resolution = resolveSelection(entry, projectRoot);
14490
+ if (!resolution.ref) return null;
14491
+ ref = resolution.ref;
14492
+ } else {
14493
+ ref = entry;
14494
+ }
14495
+ try {
14496
+ const target = resolvePackRef(ref, projectRoot);
14497
+ return readManifest(target, projectRoot);
14498
+ } catch {
14499
+ return null;
14500
+ }
14501
+ }
14050
14502
  function defaultPackSelections() {
14051
14503
  try {
14052
14504
  const out = [];
@@ -14243,21 +14695,21 @@ function buildCodeIndex(model) {
14243
14695
  const declarations = /* @__PURE__ */ new Map();
14244
14696
  const anchors = /* @__PURE__ */ new Map();
14245
14697
  const findingAnchors = /* @__PURE__ */ new Map();
14246
- const namesAt = (cache, path37, make) => {
14247
- const cached = cache.get(path37);
14698
+ const namesAt = (cache, path38, make) => {
14699
+ const cached = cache.get(path38);
14248
14700
  if (cached) return cached;
14249
- const f = facts.get(path37);
14701
+ const f = facts.get(path38);
14250
14702
  if (!f) return NO_NAMES;
14251
14703
  const built = make(f);
14252
- cache.set(path37, built);
14704
+ cache.set(path38, built);
14253
14705
  return built;
14254
14706
  };
14255
14707
  const republished = /* @__PURE__ */ new Map();
14256
- const republishedFrom = (path37) => {
14257
- const cached = republished.get(path37);
14708
+ const republishedFrom = (path38) => {
14709
+ const cached = republished.get(path38);
14258
14710
  if (cached) return cached;
14259
- const out = /* @__PURE__ */ new Set([path37]);
14260
- const queue = [path37];
14711
+ const out = /* @__PURE__ */ new Set([path38]);
14712
+ const queue = [path38];
14261
14713
  while (queue.length) {
14262
14714
  const from = queue.pop();
14263
14715
  const f = facts.get(from);
@@ -14270,7 +14722,7 @@ function buildCodeIndex(model) {
14270
14722
  }
14271
14723
  }
14272
14724
  }
14273
- republished.set(path37, out);
14725
+ republished.set(path38, out);
14274
14726
  return out;
14275
14727
  };
14276
14728
  const NO_ORIGIN = /* @__PURE__ */ new Set();
@@ -14279,9 +14731,9 @@ function buildCodeIndex(model) {
14279
14731
  return to ? republishedFrom(to) : NO_ORIGIN;
14280
14732
  };
14281
14733
  const scopeOf = (site, from) => {
14282
- const path37 = pathKey(site.from ?? from);
14283
- const f = facts.get(path37);
14284
- return f?.status === "analyzed" && f.analysisGrade === "exact" ? { path: path37, facts: f } : void 0;
14734
+ const path38 = pathKey(site.from ?? from);
14735
+ const f = facts.get(path38);
14736
+ return f?.status === "analyzed" && f.analysisGrade === "exact" ? { path: path38, facts: f } : void 0;
14285
14737
  };
14286
14738
  const originOf2 = (site, from) => {
14287
14739
  const resolved = scopeOf(site, from);
@@ -14320,7 +14772,7 @@ function buildCodeIndex(model) {
14320
14772
  if (site.via) for (const typeName of localTypesOf(f, site.via)) widenThroughType(typeName);
14321
14773
  return out;
14322
14774
  };
14323
- const declarationsAt = (path37) => namesAt(declarations, pathKey(path37), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames]));
14775
+ const declarationsAt = (path38) => namesAt(declarations, pathKey(path38), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames]));
14324
14776
  const forwardsOf = (start, name) => {
14325
14777
  const out = [];
14326
14778
  const seen = /* @__PURE__ */ new Set();
@@ -14346,10 +14798,10 @@ function buildCodeIndex(model) {
14346
14798
  return {
14347
14799
  paths,
14348
14800
  exactPaths,
14349
- factsAt: (path37) => facts.get(pathKey(path37)),
14801
+ factsAt: (path38) => facts.get(pathKey(path38)),
14350
14802
  declarationsAt,
14351
- anchorsAt: (path37) => namesAt(anchors, pathKey(path37), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
14352
- findingAnchorsAt: (path37) => namesAt(findingAnchors, pathKey(path37), (f) => new Set(f.anchoredNames)),
14803
+ anchorsAt: (path38) => namesAt(anchors, pathKey(path38), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
14804
+ findingAnchorsAt: (path38) => namesAt(findingAnchors, pathKey(path38), (f) => new Set(f.anchoredNames)),
14353
14805
  originOf: originOf2,
14354
14806
  possibleOriginsOf,
14355
14807
  forwardsOf
@@ -14388,10 +14840,10 @@ function buildRealizationIndex(ctx) {
14388
14840
  return {
14389
14841
  paths,
14390
14842
  implementationsOf: (compId) => implsByComponent.get(compId) ?? NONE_IMPL,
14391
- implementationsAt: (path37) => implsByPath.get(pathKey(path37)) ?? NONE_IMPL,
14843
+ implementationsAt: (path38) => implsByPath.get(pathKey(path38)) ?? NONE_IMPL,
14392
14844
  filesOf: (compId) => filesByComponent.get(compId) ?? NONE_PATH,
14393
14845
  filesIn: (subsystemId) => filesBySubsystem.get(subsystemId) ?? NONE_PATH,
14394
- componentsAt: (path37) => componentsByPath.get(pathKey(path37)) ?? NONE_COMP
14846
+ componentsAt: (path38) => componentsByPath.get(pathKey(path38)) ?? NONE_COMP
14395
14847
  };
14396
14848
  }
14397
14849
  function buildImportGraph(index, universe) {
@@ -14403,22 +14855,22 @@ function buildImportGraph(index, universe) {
14403
14855
  if (to && to !== from) into.add(to);
14404
14856
  }
14405
14857
  };
14406
- const importsOf = (path37) => {
14407
- const cached = imports.get(path37);
14858
+ const importsOf = (path38) => {
14859
+ const cached = imports.get(path38);
14408
14860
  if (cached) return cached;
14409
- const facts = index.factsAt(path37);
14861
+ const facts = index.factsAt(path38);
14410
14862
  const out = /* @__PURE__ */ new Set();
14411
- if (facts?.status === "analyzed") resolveAll(path37, facts.imports, out);
14412
- imports.set(path37, out);
14863
+ if (facts?.status === "analyzed") resolveAll(path38, facts.imports, out);
14864
+ imports.set(path38, out);
14413
14865
  return out;
14414
14866
  };
14415
- const tracesOf = (path37) => {
14416
- const cached = traces.get(path37);
14867
+ const tracesOf = (path38) => {
14868
+ const cached = traces.get(path38);
14417
14869
  if (cached) return cached;
14418
- const facts = index.factsAt(path37);
14419
- const out = new Set(importsOf(path37));
14420
- if (facts?.status === "analyzed") resolveAll(path37, facts.reexports, out);
14421
- traces.set(path37, out);
14870
+ const facts = index.factsAt(path38);
14871
+ const out = new Set(importsOf(path38));
14872
+ if (facts?.status === "analyzed") resolveAll(path38, facts.reexports, out);
14873
+ traces.set(path38, out);
14422
14874
  return out;
14423
14875
  };
14424
14876
  return {
@@ -14698,17 +15150,17 @@ var projectIdentityRule = {
14698
15150
  { code: "PROJECT_ID_DEFAULTED", defaultSeverity: "notice", summary: "Project declares no id; it answers to one derived from its display name" }
14699
15151
  ],
14700
15152
  check(ctx) {
14701
- const identity = ctx.projectIdentity;
14702
- if (identity) judgeOwnIdentity(ctx, identity);
15153
+ const identity2 = ctx.projectIdentity;
15154
+ if (identity2) judgeOwnIdentity(ctx, identity2);
14703
15155
  }
14704
15156
  };
14705
- function judgeOwnIdentity(ctx, identity) {
14706
- const problem = (kind) => identity.problems.find((p) => p.kind === kind);
15157
+ function judgeOwnIdentity(ctx, identity2) {
15158
+ const problem = (kind) => identity2.problems.find((p) => p.kind === kind);
14707
15159
  if (problem("defaulted")) {
14708
15160
  ctx.addIssue(
14709
15161
  "notice",
14710
15162
  "PROJECT_ID_DEFAULTED",
14711
- `Project "${identity.name}" declares no id in .wai/project.yaml, so it answers to "${identity.id}", derived from its display name \u2014 renaming the project would move it. No save writes it for you; declare it with \`id: ${identity.id}\` (stage 3 requires an explicit id).`
15163
+ `Project "${identity2.name}" declares no id in .wai/project.yaml, so it answers to "${identity2.id}", derived from its display name \u2014 renaming the project would move it. No save writes it for you; declare it with \`id: ${identity2.id}\` (stage 3 requires an explicit id).`
14712
15164
  );
14713
15165
  }
14714
15166
  const ambiguous = problem("ambiguous");
@@ -14716,15 +15168,15 @@ function judgeOwnIdentity(ctx, identity) {
14716
15168
  ctx.addIssue(
14717
15169
  "warning",
14718
15170
  "PROJECT_ID_AMBIGUOUS",
14719
- `Project "${identity.name}" has no usable id: ${ambiguous.detail}. A project id is lower-case letters, digits, "-", "_" and ".", starting and ending with a letter or a digit \u2014 declare one in .wai/project.yaml.`
15171
+ `Project "${identity2.name}" has no usable id: ${ambiguous.detail}. A project id is lower-case letters, digits, "-", "_" and ".", starting and ending with a letter or a digit \u2014 declare one in .wai/project.yaml.`
14720
15172
  );
14721
15173
  }
14722
15174
  if (problem("changed")) {
14723
- const now = identity.id === void 0 ? "no id at all" : `"${identity.id}"`;
15175
+ const now = identity2.id === void 0 ? "no id at all" : `"${identity2.id}"`;
14724
15176
  ctx.addIssue(
14725
15177
  "error",
14726
15178
  "PROJECT_ID_CHANGED",
14727
- `Project "${identity.name}" now resolves to ${now}, but its lock approved "${identity.lockedId}" \u2014 everything keyed on the approved id no longer finds this project. Restore \`id: ${identity.lockedId}\` in .wai/project.yaml, or rename deliberately and re-lock.`
15179
+ `Project "${identity2.name}" now resolves to ${now}, but its lock approved "${identity2.lockedId}" \u2014 everything keyed on the approved id no longer finds this project. Restore \`id: ${identity2.lockedId}\` in .wai/project.yaml, or rename deliberately and re-lock.`
14728
15180
  );
14729
15181
  }
14730
15182
  }
@@ -15848,7 +16300,7 @@ var narrativeJumpEdgesRule = {
15848
16300
  const byNum = /* @__PURE__ */ new Map();
15849
16301
  for (const s of steps) byNum.set(s.stepNumber, s);
15850
16302
  const nums = [...byNum.keys()].sort((a, b) => a - b);
15851
- const nextOf = (n) => {
16303
+ const nextOf2 = (n) => {
15852
16304
  const i = nums.indexOf(n);
15853
16305
  return i !== -1 && i + 1 < nums.length ? nums[i + 1] : void 0;
15854
16306
  };
@@ -15857,7 +16309,7 @@ var narrativeJumpEdgesRule = {
15857
16309
  const handlerStarts = new Set((s.catches ?? []).map((c) => c.step));
15858
16310
  if (s.finallyStep !== void 0) handlerStarts.add(s.finallyStep);
15859
16311
  const last = byNum.get(s.endStep);
15860
- const nxt = nextOf(s.endStep);
16312
+ const nxt = nextOf2(s.endStep);
15861
16313
  if (last && (last.type === "local" || last.type === "call" || last.type === "register" || last.type === "dispatch") && nxt !== void 0 && handlerStarts.has(nxt)) {
15862
16314
  ctx.addIssue(
15863
16315
  "warning",
@@ -16155,11 +16607,11 @@ var unconditionalCallCyclesRule = {
16155
16607
  const memberEdges = keys.flatMap((k) => (adjacency.get(k) ?? []).filter((e) => inScc.has(e.toKey)));
16156
16608
  if (memberEdges.length === 0) continue;
16157
16609
  const anchor = [...memberEdges].sort((a, b) => byCodeUnit(a.fromKey, b.fromKey) || byCodeUnit(a.impl.id, b.impl.id) || a.stepNumber - b.stepNumber)[0];
16158
- const path37 = [...keys].sort(byCodeUnit).join(" \u2192 ");
16610
+ const path38 = [...keys].sort(byCodeUnit).join(" \u2192 ");
16159
16611
  ctx.addIssue(
16160
16612
  "warning",
16161
16613
  "UNCONDITIONAL_CALL_CYCLE",
16162
- `Call cycle with no guard: ${path37} \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.`,
16614
+ `Call cycle with no guard: ${path38} \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.`,
16163
16615
  anchor.impl.id,
16164
16616
  memberEdges.some((e) => ctx.isImplementationDraft(e.impl))
16165
16617
  );
@@ -17604,6 +18056,27 @@ var reproducibilityRule = {
17604
18056
  }
17605
18057
  };
17606
18058
 
18059
+ // src/core/rules/extension/pack-requirements.ts
18060
+ var packRequirementsRule = {
18061
+ name: "pack-requirements",
18062
+ description: "Every requirement in the bound project's own composition.requirePolicies must be readable: a pack name and a semver range that parses (pack_requirement.rangeProblem). One that does not is POLICY_REQUIREMENT_INVALID, quoting the range and the unreadable token \u2014 an error on the requiring project's own gate, because the requirement is its own configuration and the family run cannot judge a member against it. It judges only the syntax: whether members adopt the requirement is the family run's question (family_validator.adoption), and nothing here reads a parent's requirements.",
18063
+ codes: [
18064
+ { code: "POLICY_REQUIREMENT_INVALID", defaultSeverity: "error", summary: "A project's composition.requirePolicies names a pack range that does not parse" }
18065
+ ],
18066
+ check(ctx) {
18067
+ for (const requirement of ctx.ext.packRequirements) {
18068
+ const problem = requirement.pack.trim() === "" ? "it names no pack" : rangeProblem(requirement);
18069
+ if (problem === null) continue;
18070
+ const which = requirement.pack.trim() === "" ? "A requirement" : `The requirement for pack "${requirement.pack}"`;
18071
+ ctx.addIssue(
18072
+ "error",
18073
+ "POLICY_REQUIREMENT_INVALID",
18074
+ `${which} in composition.requirePolicies cannot be read: range "${requirement.version}" \u2014 ${problem}. The family run cannot judge any member against it until it parses; write an exact version (1.2.0), a caret or tilde range (^1.2, ~1.2.3), an x-range (1.x, *), a comparator set (>=1.2.0 <2.0.0) or alternatives joined by \`||\`.`
18075
+ );
18076
+ }
18077
+ }
18078
+ };
18079
+
17607
18080
  // src/core/rules/wiring/dependency-cycles.ts
17608
18081
  var cyclesRule = {
17609
18082
  name: "dependency-cycles",
@@ -17844,8 +18317,8 @@ var lifecycleRule = {
17844
18317
  };
17845
18318
 
17846
18319
  // src/core/rules/wiring/portal-mounts.ts
17847
- function liesUnder(path37, prefix) {
17848
- return path37 === prefix || path37.startsWith(prefix + "/");
18320
+ function liesUnder(path38, prefix) {
18321
+ return path38 === prefix || path38.startsWith(prefix + "/");
17849
18322
  }
17850
18323
  var portalMountsRule = {
17851
18324
  name: "portal-mounts",
@@ -18298,8 +18771,8 @@ function componentOfImpl(ctx, implContract) {
18298
18771
  }
18299
18772
  var CLAIM_PHRASES = /\b(persist(s|ed|ent|ing|ence)?|survives?\s+(a\s+)?restart|writ(es?|ten)\s+to\s+disk|registered\s+into|durabl[ey])\b/i;
18300
18773
  var QUOTED_SPAN = /"[^"]*"|“[^”]*”|`[^`]*`|(?<![\p{L}\p{N}])['‘](?:[^'‘’]|(?<=[\p{L}\p{N}])['’](?=[\p{L}\p{N}]))*['’](?![\p{L}\p{N}])/gu;
18301
- function withoutQuotedText(text2) {
18302
- return text2.replace(QUOTED_SPAN, " ");
18774
+ function withoutQuotedText(text3) {
18775
+ return text3.replace(QUOTED_SPAN, " ");
18303
18776
  }
18304
18777
  var proseClaimRule = {
18305
18778
  name: "prose-claims",
@@ -19052,11 +19525,11 @@ var hiddenStateRule = {
19052
19525
  byPath.set(key, list);
19053
19526
  }
19054
19527
  }
19055
- for (const path37 of code.exactPaths) {
19056
- const facts = code.factsAt(path37);
19528
+ for (const path38 of code.exactPaths) {
19529
+ const facts = code.factsAt(path38);
19057
19530
  const bindings = facts.topLevelMutableBindings ?? [];
19058
19531
  if (bindings.length === 0) continue;
19059
- const mapped = byPath.get(path37) ?? [];
19532
+ const mapped = byPath.get(path38) ?? [];
19060
19533
  if (mapped.length === 0) continue;
19061
19534
  if (!mapped.every((m) => isStatelessLogic(m.component))) continue;
19062
19535
  const anchor = [...mapped].sort((a, b) => a.impl.id.localeCompare(b.impl.id))[0];
@@ -19122,7 +19595,7 @@ var dependencyConformanceRule = {
19122
19595
  }
19123
19596
  return false;
19124
19597
  };
19125
- const draftAt = (path37) => realization.implementationsAt(path37).some((impl) => ctx.isImplementationDraft(impl));
19598
+ const draftAt = (path38) => realization.implementationsAt(path38).some((impl) => ctx.isImplementationDraft(impl));
19126
19599
  for (const fromPath of mappedPaths) {
19127
19600
  const fromComponents = realization.componentsAt(fromPath);
19128
19601
  for (const toPath of graph2.importsOf(fromPath)) {
@@ -19207,7 +19680,7 @@ var portalImportsRule = {
19207
19680
  }
19208
19681
  return files;
19209
19682
  };
19210
- const draftAt = (path37) => realization.implementationsAt(path37).some((impl) => ctx.isImplementationDraft(impl));
19683
+ const draftAt = (path38) => realization.implementationsAt(path38).some((impl) => ctx.isImplementationDraft(impl));
19211
19684
  for (const fromPath of mappedPaths) {
19212
19685
  const fromSubsystems = new Set(realization.componentsAt(fromPath).map((c) => c.subsystem));
19213
19686
  for (const toPath of graph2.importsOf(fromPath)) {
@@ -19668,7 +20141,7 @@ var paramConformanceRule = {
19668
20141
  codeNameOf.set(type.id, named2);
19669
20142
  if (type.subsystem) codeNameOf.set(`${type.subsystem}.${type.id}`, named2);
19670
20143
  }
19671
- const normalize3 = (text2) => text2.replace(/\s+/g, " ").trim();
20144
+ const normalize3 = (text3) => text3.replace(/\s+/g, " ").trim();
19672
20145
  const typeAgrees = (declared, realized) => {
19673
20146
  if (!realized.type) return false;
19674
20147
  const stated = normalize3(declared.type);
@@ -19743,8 +20216,8 @@ var paramConformanceRule = {
19743
20216
  };
19744
20217
 
19745
20218
  // src/core/rules/conformance/route-coverage.ts
19746
- function liesUnder2(path37, prefix) {
19747
- return path37 === prefix || path37.startsWith(prefix + "/");
20219
+ function liesUnder2(path38, prefix) {
20220
+ return path38 === prefix || path38.startsWith(prefix + "/");
19748
20221
  }
19749
20222
  function normalizeSegment(segment) {
19750
20223
  return /^\{.+\}$/.test(segment) || segment.startsWith(":") ? "*" : segment;
@@ -20499,9 +20972,9 @@ function makeMatcher(tech) {
20499
20972
  const joined = parts.join("");
20500
20973
  if (joined.length < 3) return null;
20501
20974
  const n = parts.length;
20502
- return (text2) => {
20503
- if (!text2) return false;
20504
- const words2 = text2.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
20975
+ return (text3) => {
20976
+ if (!text3) return false;
20977
+ const words2 = text3.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
20505
20978
  if (words2.some((w) => w.includes(joined))) return true;
20506
20979
  if (n < 2) return false;
20507
20980
  for (let i = 0; i + n <= words2.length; i++) {
@@ -21425,6 +21898,10 @@ var SDD_RULES = [
21425
21898
  // elsewhere?) rather than spec content.
21426
21899
  packResolutionRule,
21427
21900
  reproducibilityRule,
21901
+ // The syntax of this project's own composition.requirePolicies (governance
21902
+ // stage): configuration too, and the family run cannot judge a member against
21903
+ // a requirement that does not parse.
21904
+ packRequirementsRule,
21428
21905
  // MUST run last, in this order: each audits what the earlier rules did
21429
21906
  // with a declared exception. The debt register first (which carried
21430
21907
  // findings the conformance family actually matched), then the allows
@@ -21590,8 +22067,8 @@ LANGUAGE_PATTERNS.cpp = LANGUAGE_PATTERNS.c;
21590
22067
  var PATTERN_ANALYSIS_MAX_BYTES = 1e6;
21591
22068
  var IDENTIFIER_RE = /[A-Za-z_$][\w$]*/g;
21592
22069
  var STRING_RE = /'([^'\\\n]*(?:\\.[^'\\\n]*)*)'|"([^"\\\n]*(?:\\.[^"\\\n]*)*)"|`([^`\\]*(?:\\.[^`\\]*)*)`/g;
21593
- function stripComments(text2, patterns) {
21594
- let out = text2;
22070
+ function stripComments(text3, patterns) {
22071
+ let out = text3;
21595
22072
  for (const [open, close] of patterns.blockComments) {
21596
22073
  const re = new RegExp(`${escapeRe(open)}[\\s\\S]*?${escapeRe(close)}`, "g");
21597
22074
  out = out.replace(re, " ");
@@ -21617,16 +22094,16 @@ function boundNames(captured, localOnly) {
21617
22094
  }
21618
22095
  return names;
21619
22096
  }
21620
- function collectStrings(text2) {
22097
+ function collectStrings(text3) {
21621
22098
  const anchors = /* @__PURE__ */ new Set();
21622
- for (const m of text2.matchAll(STRING_RE)) {
22099
+ for (const m of text3.matchAll(STRING_RE)) {
21623
22100
  const value = m[1] ?? m[2] ?? m[3];
21624
22101
  if (value) anchors.add(value);
21625
22102
  }
21626
22103
  return anchors;
21627
22104
  }
21628
- function analyzeWithPatterns(text2, patterns) {
21629
- const stripped = stripComments(text2, patterns);
22105
+ function analyzeWithPatterns(text3, patterns) {
22106
+ const stripped = stripComments(text3, patterns);
21630
22107
  const declared = /* @__PURE__ */ new Set();
21631
22108
  const exported = /* @__PURE__ */ new Set();
21632
22109
  const imports = /* @__PURE__ */ new Set();
@@ -21654,7 +22131,7 @@ function analyzeWithPatterns(text2, patterns) {
21654
22131
  }
21655
22132
  }
21656
22133
  }
21657
- const anchors = collectStrings(text2);
22134
+ const anchors = collectStrings(text3);
21658
22135
  for (const m of stripped.matchAll(IDENTIFIER_RE)) anchors.add(m[0]);
21659
22136
  return {
21660
22137
  analysisGrade: "pattern",
@@ -21665,11 +22142,11 @@ function analyzeWithPatterns(text2, patterns) {
21665
22142
  reexports: []
21666
22143
  };
21667
22144
  }
21668
- function analyzeGeneric(text2) {
22145
+ function analyzeGeneric(text3) {
21669
22146
  const words2 = /* @__PURE__ */ new Set();
21670
- for (const m of text2.matchAll(IDENTIFIER_RE)) words2.add(m[0]);
22147
+ for (const m of text3.matchAll(IDENTIFIER_RE)) words2.add(m[0]);
21671
22148
  const anchors = new Set(words2);
21672
- for (const s of collectStrings(text2)) anchors.add(s);
22149
+ for (const s of collectStrings(text3)) anchors.add(s);
21673
22150
  return {
21674
22151
  analysisGrade: "generic",
21675
22152
  declaredNames: [...words2],
@@ -22239,14 +22716,14 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
22239
22716
  files.push({ path: sourcePath, status: "unreadable", ...empty });
22240
22717
  continue;
22241
22718
  }
22242
- const text2 = buffer.toString("utf8");
22719
+ const text3 = buffer.toString("utf8");
22243
22720
  const language = EXTENSION_LANGUAGE[path15.extname(sourcePath).toLowerCase()];
22244
22721
  let analyzed;
22245
22722
  if (language === "typescript" || language === "javascript") {
22246
22723
  const ts = resolveTypeScript(projectRoot);
22247
22724
  if (ts) {
22248
22725
  try {
22249
- const facts = walkExact(ts, text2, absolute);
22726
+ const facts = walkExact(ts, text3, absolute);
22250
22727
  chaseReexports(ts, facts, absolute, projectRoot, /* @__PURE__ */ new Set([absolute]), exactCache);
22251
22728
  analyzed = {
22252
22729
  analysisGrade: "exact",
@@ -22272,15 +22749,15 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
22272
22749
  reexportOnly: facts.reexportOnly
22273
22750
  };
22274
22751
  } catch {
22275
- analyzed = analyzeGeneric(text2);
22752
+ analyzed = analyzeGeneric(text3);
22276
22753
  }
22277
22754
  } else {
22278
- analyzed = analyzeWithPatterns(text2, LANGUAGE_PATTERNS[language]);
22755
+ analyzed = analyzeWithPatterns(text3, LANGUAGE_PATTERNS[language]);
22279
22756
  }
22280
- } else if (language && LANGUAGE_PATTERNS[language] && text2.length <= PATTERN_ANALYSIS_MAX_BYTES) {
22281
- analyzed = analyzeWithPatterns(text2, LANGUAGE_PATTERNS[language]);
22757
+ } else if (language && LANGUAGE_PATTERNS[language] && text3.length <= PATTERN_ANALYSIS_MAX_BYTES) {
22758
+ analyzed = analyzeWithPatterns(text3, LANGUAGE_PATTERNS[language]);
22282
22759
  } else {
22283
- analyzed = analyzeGeneric(text2);
22760
+ analyzed = analyzeGeneric(text3);
22284
22761
  }
22285
22762
  files.push({ path: sourcePath, status: "analyzed", language, ...analyzed });
22286
22763
  }
@@ -22311,24 +22788,24 @@ function clauseBindings(clause, specifier) {
22311
22788
  function readTestFiles(testRoots, projectRoot) {
22312
22789
  const scanned = [];
22313
22790
  for (const key of walkDeclaredRoots(testRoots, [], projectRoot)) {
22314
- let text2;
22791
+ let text3;
22315
22792
  try {
22316
22793
  const buffer = fs11.readFileSync(path15.resolve(projectRoot, key));
22317
22794
  if (looksBinary(buffer)) continue;
22318
- text2 = buffer.toString("utf8");
22795
+ text3 = buffer.toString("utf8");
22319
22796
  } catch {
22320
22797
  continue;
22321
22798
  }
22322
22799
  const imports = /* @__PURE__ */ new Set();
22323
- for (const m of text2.matchAll(NAMED_IMPORT_BINDINGS_RE)) {
22800
+ for (const m of text3.matchAll(NAMED_IMPORT_BINDINGS_RE)) {
22324
22801
  for (const name of boundNames(m[1] ?? "", false)) imports.add(name);
22325
22802
  }
22326
- const code = stripComments(text2, JS_PATTERNS);
22803
+ const code = stripComments(text3, JS_PATTERNS);
22327
22804
  const bindings = [];
22328
22805
  for (const m of code.matchAll(IMPORT_FROM_RE)) bindings.push(...clauseBindings(m[1], m[2]));
22329
22806
  for (const m of code.matchAll(DYNAMIC_IMPORT_RE)) bindings.push(...clauseBindings(`{${m[1].replace(/:/g, " as ")}}`, m[2]));
22330
22807
  const words2 = /* @__PURE__ */ new Set();
22331
- for (const m of text2.matchAll(IDENTIFIER_RE)) words2.add(m[0]);
22808
+ for (const m of text3.matchAll(IDENTIFIER_RE)) words2.add(m[0]);
22332
22809
  scanned.push({ path: key, imports, bindings, words: words2 });
22333
22810
  }
22334
22811
  return scanned;
@@ -22745,8 +23222,8 @@ function buildRuleContext(opts) {
22745
23222
  };
22746
23223
  const authoredIndex = /* @__PURE__ */ new Map();
22747
23224
  for (const r of family?.authoredReferences ?? []) {
22748
- for (const text2 of [r.authored, r.resolved]) {
22749
- const k = `${r.specId}\0${r.position}\0${text2}`;
23225
+ for (const text3 of [r.authored, r.resolved]) {
23226
+ const k = `${r.specId}\0${r.position}\0${text3}`;
22750
23227
  if (!authoredIndex.has(k)) authoredIndex.set(k, r);
22751
23228
  }
22752
23229
  }
@@ -22926,12 +23403,12 @@ function buildRuleContext(opts) {
22926
23403
  }
22927
23404
  return defaultSeverity;
22928
23405
  };
22929
- const lintAllows = [];
23406
+ const lintAllows2 = [];
22930
23407
  const allowLookup = /* @__PURE__ */ new Map();
22931
23408
  const collectAllows = (specId, lint) => {
22932
23409
  for (const a of lint?.allow ?? []) {
22933
23410
  const entry = { specId, code: a.code, at: a.at, covers: a.covers, reason: a.reason, used: false };
22934
- lintAllows.push(entry);
23411
+ lintAllows2.push(entry);
22935
23412
  if (!allowLookup.has(specId)) allowLookup.set(specId, /* @__PURE__ */ new Map());
22936
23413
  const forSpec = allowLookup.get(specId);
22937
23414
  const forCode = forSpec.get(a.code) ?? [];
@@ -22981,7 +23458,7 @@ function buildRuleContext(opts) {
22981
23458
  }
22982
23459
  const severity = getRuleSeverity(code, defaultSeverity, isDraftContext, owner);
22983
23460
  if (severity === "off") return;
22984
- let text2 = message;
23461
+ let text3 = message;
22985
23462
  if (specId) {
22986
23463
  const key = `${specId}\0${code}`;
22987
23464
  let seen = sitesSeen.get(key);
@@ -22999,7 +23476,7 @@ function buildRuleContext(opts) {
22999
23476
  if (grew.length === 0) {
23000
23477
  if (severity !== "error") return;
23001
23478
  } else {
23002
- text2 = `${text2} A lint.allow covers this site, but not ${grew.length} part(s) of it \u2014 ${grew.map((u) => `"${u}"`).join("; ")} ${grew.length === 1 ? "is" : "are"} new. Decide on them: add them to the allow's \`covers\` with a reason that is actually true, or fix them.`;
23479
+ text3 = `${text3} A lint.allow covers this site, but not ${grew.length} part(s) of it \u2014 ${grew.map((u) => `"${u}"`).join("; ")} ${grew.length === 1 ? "is" : "are"} new. Decide on them: add them to the allow's \`covers\` with a reason that is actually true, or fix them.`;
23003
23480
  }
23004
23481
  }
23005
23482
  }
@@ -23014,13 +23491,13 @@ function buildRuleContext(opts) {
23014
23491
  else grew.push(unit);
23015
23492
  }
23016
23493
  if (grew.length === 0) return;
23017
- text2 = `${text2} The conformance debt register carries this finding, but not ${grew.length} part(s) of it \u2014 ${grew.map((u) => `"${u}"`).join("; ")} ${grew.length === 1 ? "is" : "are"} new. Fix them, or add them to the entry's \`covers\` with a reason that is actually true.`;
23494
+ text3 = `${text3} The conformance debt register carries this finding, but not ${grew.length} part(s) of it \u2014 ${grew.map((u) => `"${u}"`).join("; ")} ${grew.length === 1 ? "is" : "are"} new. Fix them, or add them to the entry's \`covers\` with a reason that is actually true.`;
23018
23495
  }
23019
23496
  }
23020
23497
  issues.push({
23021
23498
  severity,
23022
23499
  code,
23023
- message: text2,
23500
+ message: text3,
23024
23501
  specId,
23025
23502
  ...isDraftContext ? { draftContext: true } : {},
23026
23503
  ...resolution ? { resolution } : {}
@@ -23089,7 +23566,7 @@ function buildRuleContext(opts) {
23089
23566
  documentationConfigFor,
23090
23567
  namingConfigFor,
23091
23568
  isBuiltinType,
23092
- ext: { profiles: extensions.profiles, languages: extensions.languages, patterns: extensions.patterns, guarantees: extensions.guarantees, assertions: extensions.assertions, packSelections: opts.packSelections ?? [], selectionFailures: extensions.selectionFailures ?? [] },
23569
+ ext: { profiles: extensions.profiles, languages: extensions.languages, patterns: extensions.patterns, guarantees: extensions.guarantees, assertions: extensions.assertions, packSelections: opts.packSelections ?? [], selectionFailures: extensions.selectionFailures ?? [], packRequirements: opts.packRequirements ?? [] },
23093
23570
  variants: opts.variants ?? [],
23094
23571
  surfaceSnapshots,
23095
23572
  ...opts.projectIdentity ? { projectIdentity: opts.projectIdentity } : {},
@@ -23099,7 +23576,7 @@ function buildRuleContext(opts) {
23099
23576
  pinnedExternals,
23100
23577
  codeModel: opts.codeModel ?? emptyCodeModel(),
23101
23578
  roundTripIssues: opts.roundTripIssues,
23102
- lintAllows,
23579
+ lintAllows: lintAllows2,
23103
23580
  sitesReported,
23104
23581
  knownIssueCodes: opts.knownIssueCodes,
23105
23582
  carriedFindings,
@@ -23158,10 +23635,103 @@ function validateComponentCandidate(candidate, opts = {}) {
23158
23635
  };
23159
23636
  }
23160
23637
 
23638
+ // src/core/pack-impact.ts
23639
+ var path16 = __toESM(require("path"));
23640
+ function authorableStereotypes() {
23641
+ return ComponentTypeSchema.options.filter((t) => !RETIRED_STEREOTYPES.has(t));
23642
+ }
23643
+ function defaultsBaseline() {
23644
+ registerBuiltinRules();
23645
+ const ruleDefaults = {};
23646
+ for (const code of knownIssueCodes()) ruleDefaults[code.code] = code.defaultSeverity;
23647
+ return {
23648
+ ruleDefaults,
23649
+ profiles: builtinProfileIds(),
23650
+ projectKinds: builtinProjectKinds(),
23651
+ stereotypes: authorableStereotypes(),
23652
+ guarantees: [...SEMANTIC_GUARANTEES],
23653
+ designDepth: "narratives"
23654
+ };
23655
+ }
23656
+ function sameEntry(a, b) {
23657
+ if (typeof b === "string") return a === b;
23658
+ return typeof a !== "string" && a.name === b.name;
23659
+ }
23660
+ function entryName(entry) {
23661
+ return typeof entry === "string" ? path16.basename(entry).replace(/\.(ya?ml|cjs|js)$/i, "") : entry.name;
23662
+ }
23663
+ function unresolvedReason(config, entry) {
23664
+ const alone = { ...config, extensions: { useGlobalPacks: false, packs: [entry] } };
23665
+ const loaded = loadFor(alone);
23666
+ const failure = loaded.selectionFailures[0];
23667
+ if (failure) return `[${failure.code}] ${failure.message}`;
23668
+ return loaded.errors[0] ?? `the pack "${entryName(entry)}" does not resolve`;
23669
+ }
23670
+ function totals(config, result) {
23671
+ const count = (severity) => result.issues.filter((i) => i.severity === severity).length;
23672
+ const id = effectiveProjectId(config);
23673
+ return {
23674
+ key: "",
23675
+ ...id !== null ? { id } : {},
23676
+ directory: getProjectRoot(),
23677
+ valid: result.valid,
23678
+ errors: count("error"),
23679
+ warnings: count("warning"),
23680
+ notices: count("notice")
23681
+ };
23682
+ }
23683
+ function governingProfiles(manifest, config) {
23684
+ if (!manifest) return [];
23685
+ const named2 = [config.projectType, ...loadSubsystemSpecs().map((s) => s.profile ?? "")];
23686
+ return [...new Set(named2.filter((p) => p !== "" && p in manifest.profiles))];
23687
+ }
23688
+ function measure(candidate) {
23689
+ const config = loadProjectConfig();
23690
+ if (!config) throw new Error("Not inside a wairon project: a pack impact is measured on a project \u2014 run `wairon init` first.");
23691
+ const entry = candidate.entry;
23692
+ const remove = candidate.remove === true;
23693
+ let next = entry !== void 0 ? withPack(config, entry, remove) : config;
23694
+ if (candidate.projectType !== void 0) next = { ...next, projectType: candidate.projectType };
23695
+ const current2 = entry !== void 0 ? (config.extensions?.packs ?? []).find((e) => sameEntry(e, entry)) : void 0;
23696
+ let manifest = null;
23697
+ if (candidate.manifest && !remove || entry === void 0) {
23698
+ manifest = candidate.manifest ?? null;
23699
+ } else {
23700
+ const measured = remove ? current2 ?? entry : entry;
23701
+ manifest = manifestOf(measured);
23702
+ if (!manifest) throw new Error(`Cannot measure the pack: ${unresolvedReason(config, measured)}`);
23703
+ }
23704
+ const previous = !remove && current2 !== void 0 ? manifestOf(current2) : null;
23705
+ const baseline = defaultsBaseline();
23706
+ const doctrine2 = manifest ? doctrineChanges(manifest, baseline) : [];
23707
+ const previousDoctrine = previous ? doctrineChanges(previous, baseline) : void 0;
23708
+ const before = validateProject({ rules: config.rules, projectType: config.projectType });
23709
+ const extensions = loadFor(next, candidate);
23710
+ const after = validateProject({
23711
+ rules: next.rules,
23712
+ projectType: next.projectType,
23713
+ extensions,
23714
+ packSelections: (next.extensions?.packs ?? []).filter((e) => typeof e !== "string")
23715
+ });
23716
+ const replaces = previous?.version ?? (current2 !== void 0 && typeof current2 !== "string" ? current2.version : void 0);
23717
+ return {
23718
+ pack: manifest?.name ?? (entry !== void 0 ? entryName(entry) : `projectType ${next.projectType}`),
23719
+ ...manifest?.version ? { version: manifest.version } : {},
23720
+ ...!remove && current2 !== void 0 && replaces ? { replaces } : {},
23721
+ direction: remove ? "remove" : "apply",
23722
+ doctrine: doctrine2,
23723
+ ...previousDoctrine && !remove ? { previousDoctrine } : {},
23724
+ governing: governingProfiles(manifest, remove ? config : next),
23725
+ findings: findingChanges(before, after),
23726
+ before: totals(config, before),
23727
+ after: totals(next, after)
23728
+ };
23729
+ }
23730
+
23161
23731
  // src/core/rules/gate-identity.ts
23162
23732
  var crypto3 = __toESM(require("crypto"));
23163
23733
  var GATE_ALGORITHM = "sha256+content+doctrine+inputs";
23164
- function doctrineIdentity(doctrine, builtinRules, gate) {
23734
+ function doctrineIdentity(doctrine2, builtinRules, gate) {
23165
23735
  const byKey = (items, key) => [...items].sort((a, b) => compareOrdinal(key(a), key(b)));
23166
23736
  return {
23167
23737
  /**
@@ -23194,29 +23764,29 @@ function doctrineIdentity(doctrine, builtinRules, gate) {
23194
23764
  projectType: gate.projectType ?? null,
23195
23765
  rulesConfig: gate.rules ?? null,
23196
23766
  packs: byKey(
23197
- doctrine.packs.map((p) => ({ name: p.name, version: p.version ?? null, scope: p.scope })),
23767
+ doctrine2.packs.map((p) => ({ name: p.name, version: p.version ?? null, scope: p.scope })),
23198
23768
  (p) => `${p.name}@${p.version ?? ""}#${p.scope}`
23199
23769
  ),
23200
23770
  // Merged records already encode pack precedence, so their resolved content is
23201
23771
  // the identity — a load-order change that flips a collision winner shows up here.
23202
- profiles: doctrine.profiles,
23203
- languages: doctrine.languages,
23772
+ profiles: doctrine2.profiles,
23773
+ languages: doctrine2.languages,
23204
23774
  patterns: byKey(
23205
- doctrine.patterns.map((p) => ({ id: p.id, version: p.version, pack: p.pack })),
23775
+ doctrine2.patterns.map((p) => ({ id: p.id, version: p.version, pack: p.pack })),
23206
23776
  (p) => `${p.pack}/${p.id}@${p.version}`
23207
23777
  ),
23208
- guarantees: [...doctrine.guarantees].sort(),
23209
- assertions: byKey(doctrine.assertions.map((a) => ({ ...a })), (a) => a.fullCode),
23778
+ guarantees: [...doctrine2.guarantees].sort(),
23779
+ assertions: byKey(doctrine2.assertions.map((a) => ({ ...a })), (a) => a.fullCode),
23210
23780
  rules: byKey(
23211
- doctrine.rules.map((r) => ({ name: r.name, codes: r.codes.map((c) => c.code).sort() })),
23781
+ doctrine2.rules.map((r) => ({ name: r.name, codes: r.codes.map((c) => c.code).sort() })),
23212
23782
  (r) => r.name
23213
23783
  )
23214
23784
  };
23215
23785
  }
23216
- function computeGateIdentity(content, doctrine, builtinRules, inputs, gate) {
23786
+ function computeGateIdentity(content, doctrine2, builtinRules, inputs, gate) {
23217
23787
  const payload = {
23218
23788
  content: content.digest,
23219
- doctrine: doctrineIdentity(doctrine, builtinRules, gate),
23789
+ doctrine: doctrineIdentity(doctrine2, builtinRules, gate),
23220
23790
  inputs: [...inputs].sort()
23221
23791
  };
23222
23792
  const digest2 = crypto3.createHash("sha256").update(canonicalize(payload)).digest("hex");
@@ -23224,7 +23794,7 @@ function computeGateIdentity(content, doctrine, builtinRules, inputs, gate) {
23224
23794
  }
23225
23795
 
23226
23796
  // src/core/validation.ts
23227
- var path16 = __toESM(require("path"));
23797
+ var path17 = __toESM(require("path"));
23228
23798
  function projectPackSelections() {
23229
23799
  try {
23230
23800
  return (loadProjectConfig()?.extensions?.packs ?? []).filter((e) => typeof e !== "string");
@@ -23232,6 +23802,14 @@ function projectPackSelections() {
23232
23802
  return [];
23233
23803
  }
23234
23804
  }
23805
+ function boundPackRequirements() {
23806
+ try {
23807
+ const config = loadProjectConfig();
23808
+ return config ? requiredPolicies(config) : [];
23809
+ } catch {
23810
+ return [];
23811
+ }
23812
+ }
23235
23813
  function boundProjectIdentity() {
23236
23814
  let config;
23237
23815
  try {
@@ -23324,13 +23902,15 @@ function validateProject(rulesOrOptions, projectType = "backend") {
23324
23902
  let scopeSubsystem;
23325
23903
  let extensions;
23326
23904
  let treatAllAsComplete = false;
23327
- if (rulesOrOptions && ("scopeSubsystem" in rulesOrOptions || "recursive" in rulesOrOptions || "rules" in rulesOrOptions || "projectType" in rulesOrOptions || "extensions" in rulesOrOptions || "treatAllAsComplete" in rulesOrOptions || "family" in rulesOrOptions)) {
23905
+ let packSelections;
23906
+ if (rulesOrOptions && ("scopeSubsystem" in rulesOrOptions || "recursive" in rulesOrOptions || "rules" in rulesOrOptions || "projectType" in rulesOrOptions || "extensions" in rulesOrOptions || "treatAllAsComplete" in rulesOrOptions || "family" in rulesOrOptions || "packSelections" in rulesOrOptions)) {
23328
23907
  const opts = rulesOrOptions;
23329
23908
  rules = opts.rules;
23330
23909
  projectType = opts.projectType ?? "backend";
23331
23910
  scopeSubsystem = opts.scopeSubsystem;
23332
23911
  extensions = opts.extensions;
23333
23912
  treatAllAsComplete = opts.treatAllAsComplete ?? false;
23913
+ packSelections = opts.packSelections;
23334
23914
  }
23335
23915
  extensions ??= loadProjectExtensions();
23336
23916
  scanAllSpecs({ recursive: true });
@@ -23429,8 +24009,11 @@ function validateProject(rulesOrOptions, projectType = "backend") {
23429
24009
  projectFamily: family,
23430
24010
  exportUsages,
23431
24011
  pinnedExternals,
23432
- // By-name selections only: a legacy path ref pins nothing to check.
23433
- packSelections: projectPackSelections(),
24012
+ // By-name selections only: a legacy path ref pins nothing to check. A dry
24013
+ // run supplies its candidate's; otherwise the stored ones.
24014
+ packSelections: packSelections ?? projectPackSelections(),
24015
+ // The bound project's own requirements, for their syntax only.
24016
+ packRequirements: boundPackRequirements(),
23434
24017
  projectIdentity: boundProjectIdentity(),
23435
24018
  surfaceSnapshots,
23436
24019
  codeModel,
@@ -23465,7 +24048,7 @@ function settledStatusBearing(loaded) {
23465
24048
  if (!settled || settled.size === 0) return [];
23466
24049
  const root = getProjectRoot();
23467
24050
  const index = scanAllSpecs();
23468
- const rel2 = (abs) => path16.relative(root, abs).split(path16.sep).join("/");
24051
+ const rel2 = (abs) => path17.relative(root, abs).split(path17.sep).join("/");
23469
24052
  const out = [];
23470
24053
  const take = (specs, paths) => {
23471
24054
  for (const spec of specs) {
@@ -23482,6 +24065,15 @@ function settledStatusBearing(loaded) {
23482
24065
  function validateFamily(options) {
23483
24066
  return run(options);
23484
24067
  }
24068
+ function measurePackImpact(candidate) {
24069
+ return measure(candidate);
24070
+ }
24071
+ function builtinProfileIds() {
24072
+ return [...BUILTIN_PROFILES];
24073
+ }
24074
+ function builtinProjectKinds() {
24075
+ return [...PROJECT_KINDS];
24076
+ }
23485
24077
  function computeGateStateId() {
23486
24078
  const content = computeStateId();
23487
24079
  const extensions = loadProjectExtensions();
@@ -23646,11 +24238,11 @@ var IdPool = class {
23646
24238
  return candidate;
23647
24239
  }
23648
24240
  };
23649
- function escapeLabel(text2) {
23650
- return text2.replace(/"/g, "#quot;");
24241
+ function escapeLabel(text3) {
24242
+ return text3.replace(/"/g, "#quot;");
23651
24243
  }
23652
- function truncate(text2, max) {
23653
- const clean = text2.replace(/\s+/g, " ").trim();
24244
+ function truncate(text3, max) {
24245
+ const clean = text3.replace(/\s+/g, " ").trim();
23654
24246
  return clean.length <= max ? clean : `${clean.slice(0, max - 1)}\u2026`;
23655
24247
  }
23656
24248
  function nodeDecl(id, label2, comp) {
@@ -24024,10 +24616,10 @@ function diagramSetIndex(files, systemName) {
24024
24616
  }
24025
24617
 
24026
24618
  // src/core/project-family.ts
24027
- var path17 = __toESM(require("path"));
24619
+ var path18 = __toESM(require("path"));
24028
24620
  var memo = /* @__PURE__ */ new WeakMap();
24029
24621
  function dirKey(dir) {
24030
- const resolved = path17.resolve(dir);
24622
+ const resolved = path18.resolve(dir);
24031
24623
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
24032
24624
  }
24033
24625
  function makeNodes(roots) {
@@ -24101,7 +24693,7 @@ function bindExternals(node, root, nodes) {
24101
24693
  const unresolved = (problem) => ({ alias: decl.alias, project: decl.project, sourceKind: "unresolved", audience: "instance", problem });
24102
24694
  if (decl.problem) return unresolved(decl.problem);
24103
24695
  if (decl.sourcePath !== void 0) {
24104
- const directory = path17.resolve(node.directory, decl.sourcePath);
24696
+ const directory = path18.resolve(node.directory, decl.sourcePath);
24105
24697
  const found = nodes.find((n) => dirKey(n.directory) === dirKey(directory));
24106
24698
  if (!found) return { alias: decl.alias, project: decl.project, sourceKind: "path", directory, audience: "instance" };
24107
24699
  if (found.id !== decl.project) {
@@ -24717,42 +25309,42 @@ function emptyIndex() {
24717
25309
  };
24718
25310
  }
24719
25311
  function isWithin(dir, file) {
24720
- const d = path18.resolve(dir);
24721
- const f = path18.resolve(file);
24722
- return f === d || f.startsWith(d + path18.sep);
25312
+ const d = path19.resolve(dir);
25313
+ const f = path19.resolve(file);
25314
+ return f === d || f.startsWith(d + path19.sep);
24723
25315
  }
24724
25316
  function canonicalPath(target) {
24725
- let existing = path18.resolve(target);
25317
+ let existing = path19.resolve(target);
24726
25318
  const rest = [];
24727
25319
  for (; ; ) {
24728
25320
  try {
24729
25321
  fs12.lstatSync(existing);
24730
25322
  break;
24731
25323
  } catch {
24732
- const up = path18.dirname(existing);
24733
- if (up === existing) return path18.resolve(target);
24734
- rest.unshift(path18.basename(existing));
25324
+ const up = path19.dirname(existing);
25325
+ if (up === existing) return path19.resolve(target);
25326
+ rest.unshift(path19.basename(existing));
24735
25327
  existing = up;
24736
25328
  }
24737
25329
  }
24738
25330
  try {
24739
- return path18.join(fs12.realpathSync.native(existing), ...rest);
25331
+ return path19.join(fs12.realpathSync.native(existing), ...rest);
24740
25332
  } catch {
24741
25333
  return null;
24742
25334
  }
24743
25335
  }
24744
25336
  function chainDirKey(dir) {
24745
- return canonicalPath(dir) ?? path18.resolve(dir);
25337
+ return canonicalPath(dir) ?? path19.resolve(dir);
24746
25338
  }
24747
25339
  function projectPathEscapesRoot(projectRoot, projectPath, resolvedChildDir) {
24748
- if (path18.isAbsolute(projectPath) || !isWithin(projectRoot, resolvedChildDir)) return true;
25340
+ if (path19.isAbsolute(projectPath) || !isWithin(projectRoot, resolvedChildDir)) return true;
24749
25341
  const root = canonicalPath(projectRoot);
24750
25342
  const child = canonicalPath(resolvedChildDir);
24751
25343
  return root === null || child === null || !isWithin(root, child);
24752
25344
  }
24753
25345
  function assertContainedProjectPath(projectRoot, projectPath) {
24754
- const root = path18.resolve(projectRoot);
24755
- const resolved = path18.resolve(root, projectPath);
25346
+ const root = path19.resolve(projectRoot);
25347
+ const resolved = path19.resolve(root, projectPath);
24756
25348
  if (projectPathEscapesRoot(root, projectPath, resolved)) {
24757
25349
  throw new Error(
24758
25350
  `projectPath "${projectPath}" must resolve within the project root "${root}", but resolves to "${resolved}"; absolute, ../-escaping and link-escaping paths are rejected so a chained subproject is always contained by its parent.`
@@ -24822,19 +25414,19 @@ function memberDeclarationsAt(dir) {
24822
25414
  function findChainingParent(childRoot, ceiling) {
24823
25415
  let childResolved;
24824
25416
  try {
24825
- childResolved = path18.resolve(childRoot);
25417
+ childResolved = path19.resolve(childRoot);
24826
25418
  } catch {
24827
25419
  return null;
24828
25420
  }
24829
- const bound2 = ceiling ? path18.resolve(ceiling) : void 0;
24830
- let dir = path18.dirname(childResolved);
25421
+ const bound2 = ceiling ? path19.resolve(ceiling) : void 0;
25422
+ let dir = path19.dirname(childResolved);
24831
25423
  for (let hops = 0; hops < 32; hops++) {
24832
25424
  if (bound2 && !isWithin(bound2, dir)) break;
24833
25425
  if (pathExists(aiPathsAt(dir).specsDir()) || pathExists(aiPathsAt(dir).projectConfig())) {
24834
25426
  for (const member of memberDeclarationsAt(dir)) {
24835
25427
  if (member.problem || member.path.trim() === "") continue;
24836
25428
  try {
24837
- const memberDir = path18.resolve(dir, member.path);
25429
+ const memberDir = path19.resolve(dir, member.path);
24838
25430
  if (chainDirKey(memberDir) === chainDirKey(childResolved) && !projectPathEscapesRoot(dir, member.path, memberDir)) {
24839
25431
  return { parentRoot: dir, alias: member.alias, form: member.form };
24840
25432
  }
@@ -24842,14 +25434,14 @@ function findChainingParent(childRoot, ceiling) {
24842
25434
  }
24843
25435
  }
24844
25436
  }
24845
- const up = path18.dirname(dir);
25437
+ const up = path19.dirname(dir);
24846
25438
  if (up === dir) break;
24847
25439
  dir = up;
24848
25440
  }
24849
25441
  return null;
24850
25442
  }
24851
25443
  function inspectChainedRoots(rootDir = getProjectRoot()) {
24852
- const root = path18.resolve(rootDir);
25444
+ const root = path19.resolve(rootDir);
24853
25445
  const inspection = { roots: [], skipped: [] };
24854
25446
  const listed = /* @__PURE__ */ new Set();
24855
25447
  const walk2 = (projectDir, prefix, ancestors, depth) => {
@@ -24857,11 +25449,11 @@ function inspectChainedRoots(rootDir = getProjectRoot()) {
24857
25449
  const key = prefix ? `${prefix}::${member.alias}` : member.alias;
24858
25450
  let childDir;
24859
25451
  try {
24860
- childDir = path18.resolve(projectDir, member.path);
25452
+ childDir = path19.resolve(projectDir, member.path);
24861
25453
  } catch {
24862
25454
  continue;
24863
25455
  }
24864
- if (member.problem && !path18.isAbsolute(member.path)) continue;
25456
+ if (member.problem && !path19.isAbsolute(member.path)) continue;
24865
25457
  let reason;
24866
25458
  if (projectPathEscapesRoot(projectDir, member.path, childDir)) reason = "escapes";
24867
25459
  else if (ancestors.has(chainDirKey(childDir))) reason = "cyclic";
@@ -24874,7 +25466,7 @@ function inspectChainedRoots(rootDir = getProjectRoot()) {
24874
25466
  const dirKey3 = chainDirKey(childDir);
24875
25467
  if (listed.has(dirKey3)) continue;
24876
25468
  listed.add(dirKey3);
24877
- inspection.roots.push(path18.relative(root, childDir).split(path18.sep).join("/"));
25469
+ inspection.roots.push(path19.relative(root, childDir).split(path19.sep).join("/"));
24878
25470
  walk2(childDir, key, /* @__PURE__ */ new Set([...ancestors, dirKey3]), depth + 1);
24879
25471
  }
24880
25472
  };
@@ -25079,9 +25671,9 @@ function mapRawReferences(kind, spec, map) {
25079
25671
  }
25080
25672
  function childRelativeFilePaths(spec, authoringRoot, childRoot) {
25081
25673
  const reexpress = (p) => {
25082
- if (path18.isAbsolute(p)) return p;
25083
- const rel2 = path18.relative(childRoot, path18.resolve(authoringRoot, p));
25084
- const inside = rel2 !== "" && rel2 !== ".." && !rel2.startsWith(`..${path18.sep}`) && !path18.isAbsolute(rel2);
25674
+ if (path19.isAbsolute(p)) return p;
25675
+ const rel2 = path19.relative(childRoot, path19.resolve(authoringRoot, p));
25676
+ const inside = rel2 !== "" && rel2 !== ".." && !rel2.startsWith(`..${path19.sep}`) && !path19.isAbsolute(rel2);
25085
25677
  return inside ? rel2.replace(/\\/g, "/") : p;
25086
25678
  };
25087
25679
  return {
@@ -25335,9 +25927,9 @@ function findOwner(id, components) {
25335
25927
  }) ?? null;
25336
25928
  }
25337
25929
  function moveComponentFolder(fromDir, toDir) {
25338
- if (path18.normalize(fromDir) === path18.normalize(toDir)) return false;
25930
+ if (path19.normalize(fromDir) === path19.normalize(toDir)) return false;
25339
25931
  if (!fs12.existsSync(fromDir) || fs12.existsSync(toDir)) return false;
25340
- ensureDir(path18.dirname(toDir));
25932
+ ensureDir(path19.dirname(toDir));
25341
25933
  fs12.renameSync(fromDir, toDir);
25342
25934
  return true;
25343
25935
  }
@@ -25475,8 +26067,8 @@ function rewriteSpecRefs(raw, remap) {
25475
26067
  }
25476
26068
  function summarizeValue(value) {
25477
26069
  if (value === void 0) return "";
25478
- const text2 = typeof value === "string" ? value : JSON.stringify(value) ?? String(value);
25479
- return text2.length > 120 ? `${text2.slice(0, 117)}...` : text2;
26070
+ const text3 = typeof value === "string" ? value : JSON.stringify(value) ?? String(value);
26071
+ return text3.length > 120 ? `${text3.slice(0, 117)}...` : text3;
25480
26072
  }
25481
26073
  function identityKeyOf(field, item) {
25482
26074
  if (item === null || typeof item !== "object") return null;
@@ -25799,13 +26391,13 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
25799
26391
  const isPlain = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
25800
26392
  const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
25801
26393
  const at = (prefix, key) => prefix ? `${prefix}.${key}` : key;
25802
- const say = (path37, why) => {
25803
- out.push(`${path37} \u2014 ${why}`);
26394
+ const say = (path38, why) => {
26395
+ out.push(`${path38} \u2014 ${why}`);
25804
26396
  };
25805
- const walkValue = (d, m, s, path37, field) => {
26397
+ const walkValue = (d, m, s, path38, field) => {
25806
26398
  if (Array.isArray(d)) {
25807
26399
  if (!Array.isArray(m)) {
25808
- say(path37, "the level does not have this field, so the write dropped it");
26400
+ say(path38, "the level does not have this field, so the write dropped it");
25809
26401
  return;
25810
26402
  }
25811
26403
  if (field === "narrative" && d.some(carriesEditMarker)) return;
@@ -25813,32 +26405,32 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
25813
26405
  if (carriesEditMarker(element)) continue;
25814
26406
  const key = reportIdentityOf(field, element);
25815
26407
  if (key === null) {
25816
- if (!same(m, d)) say(path37, "the level does not keep this value, so the write dropped it");
25817
- else if (same(s, d)) say(path37, "the stored spec already held this value");
26408
+ if (!same(m, d)) say(path38, "the level does not keep this value, so the write dropped it");
26409
+ else if (same(s, d)) say(path38, "the stored spec already held this value");
25818
26410
  return;
25819
26411
  }
25820
26412
  const mMatch = m.find((i) => reportIdentityOf(field, i) === key);
25821
26413
  const sMatch = Array.isArray(s) ? s.find((i) => reportIdentityOf(field, i) === key) : void 0;
25822
26414
  if (mMatch === void 0) {
25823
- say(at(path37, key), "the write did not store this element");
26415
+ say(at(path38, key), "the write did not store this element");
25824
26416
  continue;
25825
26417
  }
25826
- walk2(element, mMatch, sMatch, at(path37, key), field);
26418
+ walk2(element, mMatch, sMatch, at(path38, key), field);
25827
26419
  }
25828
26420
  return;
25829
26421
  }
25830
26422
  if (isPlain(d)) {
25831
26423
  if (!isPlain(m)) {
25832
- say(path37, "the level does not have this field, so the write dropped it");
26424
+ say(path38, "the level does not have this field, so the write dropped it");
25833
26425
  return;
25834
26426
  }
25835
- walk2(d, m, s, path37, field);
26427
+ walk2(d, m, s, path38, field);
25836
26428
  return;
25837
26429
  }
25838
- if (!same(m, d)) say(path37, "the write did not store it \u2014 the level's schema does not keep this value");
25839
- else if (same(s, d)) say(path37, "the stored spec already held this value");
26430
+ if (!same(m, d)) say(path38, "the write did not store it \u2014 the level's schema does not keep this value");
26431
+ else if (same(s, d)) say(path38, "the stored spec already held this value");
25840
26432
  };
25841
- function walk2(d, m, s, path37, container) {
26433
+ function walk2(d, m, s, path38, container) {
25842
26434
  for (const [key, value] of Object.entries(d)) {
25843
26435
  if (DELTA_VERBS.has(key) || JUMP_LABEL_TWINS.has(key)) continue;
25844
26436
  if (key === "label" && LABEL_TWIN_CONTAINERS.has(container)) continue;
@@ -25848,13 +26440,13 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
25848
26440
  if (typeof name !== "string") continue;
25849
26441
  const had = isPlain(s) && s[name] !== void 0;
25850
26442
  const still = isPlain(m) && m[name] !== void 0;
25851
- if (!had) say(at(path37, name), "unset named a field the stored spec did not have");
25852
- else if (still) say(at(path37, name), "unset did not remove it");
26443
+ if (!had) say(at(path38, name), "unset named a field the stored spec did not have");
26444
+ else if (still) say(at(path38, name), "unset did not remove it");
25853
26445
  }
25854
26446
  continue;
25855
26447
  }
25856
26448
  if (value === void 0 || value === null) continue;
25857
- const here = at(path37, key);
26449
+ const here = at(path38, key);
25858
26450
  const mValue = isPlain(m) ? m[key] : void 0;
25859
26451
  const sValue = isPlain(s) ? s[key] : void 0;
25860
26452
  if (mValue === void 0) {
@@ -25885,7 +26477,7 @@ var SpecWorkspace = class {
25885
26477
  /** Set while normalizeReferences writes: every reference in its canonical form, no authored text carried. */
25886
26478
  this.carryDisabled = false;
25887
26479
  this.loaderIssues = [];
25888
- this.rootDir = path18.resolve(rootDir);
26480
+ this.rootDir = path19.resolve(rootDir);
25889
26481
  this.paths = aiPathsAt(this.rootDir);
25890
26482
  }
25891
26483
  /**
@@ -25948,8 +26540,8 @@ var SpecWorkspace = class {
25948
26540
  * itself — so scoping a validate to a member keeps it.
25949
26541
  */
25950
26542
  loaderIssueSpecId(file, rawId, key) {
25951
- const stem = path18.basename(file, ".yaml");
25952
- const local = rawId ?? (stem.startsWith(".") ? path18.basename(path18.dirname(file)) : stem);
26543
+ const stem = path19.basename(file, ".yaml");
26544
+ const local = rawId ?? (stem.startsWith(".") ? path19.basename(path19.dirname(file)) : stem);
25953
26545
  return keyIn(key, local);
25954
26546
  }
25955
26547
  /**
@@ -25999,7 +26591,7 @@ var SpecWorkspace = class {
25999
26591
  ...member ? { mountAlias: member.alias, mountForm: member.form } : {},
26000
26592
  legacyMount: member?.legacyMount ?? null,
26001
26593
  ...member?.description !== void 0 ? { memberDescription: member.description } : {},
26002
- directory: path18.resolve(dir),
26594
+ directory: path19.resolve(dir),
26003
26595
  system: null,
26004
26596
  config,
26005
26597
  aliases: /* @__PURE__ */ new Map(),
@@ -26012,7 +26604,7 @@ var SpecWorkspace = class {
26012
26604
  const raw = {
26013
26605
  record,
26014
26606
  aliasPath,
26015
- dir: path18.resolve(dir),
26607
+ dir: path19.resolve(dir),
26016
26608
  index: emptyIndex(),
26017
26609
  mounts: [],
26018
26610
  members: [],
@@ -26042,14 +26634,14 @@ var SpecWorkspace = class {
26042
26634
  followableMember(raw, decl, ancestors) {
26043
26635
  const memberKey = keyIn(raw.record.namespace, decl.alias);
26044
26636
  const by = decl.form === "mount" ? "subsystem" : "member";
26045
- const absolute = path18.isAbsolute(decl.path) || path18.win32.isAbsolute(decl.path);
26637
+ const absolute = path19.isAbsolute(decl.path) || path19.win32.isAbsolute(decl.path);
26046
26638
  if (decl.problem && !absolute) {
26047
26639
  if (decl.problem.includes("`externals`")) {
26048
26640
  raw.record.problems.push({ kind: "alias-conflict", id: decl.alias, projects: [raw.record.namespace], detail: decl.problem });
26049
26641
  }
26050
26642
  return null;
26051
26643
  }
26052
- const childDir = path18.resolve(raw.dir, decl.path);
26644
+ const childDir = path19.resolve(raw.dir, decl.path);
26053
26645
  if (projectPathEscapesRoot(raw.dir, decl.path, childDir)) {
26054
26646
  this.loaderIssues.push({
26055
26647
  severity: "error",
@@ -26086,7 +26678,7 @@ var SpecWorkspace = class {
26086
26678
  const specsDir = projectPaths.specsDir();
26087
26679
  this.scanVisitedSpecDirs.push(specsDir);
26088
26680
  if (!pathExists(specsDir)) return;
26089
- const systemYaml = path18.normalize(projectPaths.specsSystem());
26681
+ const systemYaml = path19.normalize(projectPaths.specsSystem());
26090
26682
  if (pathExists(systemYaml)) {
26091
26683
  try {
26092
26684
  raw.rawSystem = SystemSpecSchema.parse(readSpecFile(systemYaml));
@@ -26110,7 +26702,7 @@ var SpecWorkspace = class {
26110
26702
  index.paths[kind][spec.id] = file;
26111
26703
  };
26112
26704
  for (const file of listSpecFiles(specsDir)) {
26113
- if (path18.normalize(file) === systemYaml) continue;
26705
+ if (path19.normalize(file) === systemYaml) continue;
26114
26706
  let detectedType = "spec";
26115
26707
  let rawId;
26116
26708
  try {
@@ -26140,7 +26732,7 @@ var SpecWorkspace = class {
26140
26732
  } else if ("contract" in doc) {
26141
26733
  detectedType = "implementation";
26142
26734
  const parsed = ImplementationSpecSchema.parse(doc);
26143
- const normalizeSourcePath = (p) => path18.relative(raw.dir, path18.resolve(raw.dir, p)).replace(/\\/g, "/");
26735
+ const normalizeSourcePath = (p) => path19.relative(raw.dir, path19.resolve(raw.dir, p)).replace(/\\/g, "/");
26144
26736
  if (parsed.sourcePath) parsed.sourcePath = normalizeSourcePath(parsed.sourcePath);
26145
26737
  for (const method of parsed.methods) {
26146
26738
  if (method.sourcePath) method.sourcePath = normalizeSourcePath(method.sourcePath);
@@ -26279,7 +26871,7 @@ var SpecWorkspace = class {
26279
26871
  const others = raws.filter((r) => r !== raw);
26280
26872
  let producer;
26281
26873
  if (decl.sourcePath !== void 0) {
26282
- const at = chainDirKey(path18.resolve(raw.dir, decl.sourcePath));
26874
+ const at = chainDirKey(path19.resolve(raw.dir, decl.sourcePath));
26283
26875
  producer = others.find((r) => chainDirKey(r.dir) === at && idOf(r) === decl.project);
26284
26876
  } else {
26285
26877
  const holders = others.filter((r) => idOf(r) === decl.project);
@@ -26431,9 +27023,9 @@ var SpecWorkspace = class {
26431
27023
  return index.paths.subsystem[matches[0]];
26432
27024
  }
26433
27025
  if (pathExists(this.paths.specsSubsystemsDir()) && listFiles(this.paths.specsSubsystemsDir(), ".yaml").length > 0) {
26434
- return path18.join(this.paths.specsSubsystemsDir(), `${id}.yaml`);
27026
+ return path19.join(this.paths.specsSubsystemsDir(), `${id}.yaml`);
26435
27027
  }
26436
- return path18.join(this.paths.specsDir(), id, ".index.yaml");
27028
+ return path19.join(this.paths.specsDir(), id, ".index.yaml");
26437
27029
  }
26438
27030
  getComponentPath(id, subsystemId) {
26439
27031
  const index = this.scanAll();
@@ -26453,21 +27045,21 @@ var SpecWorkspace = class {
26453
27045
  if (owner) {
26454
27046
  const ownerPath = index.paths.component[owner.id];
26455
27047
  if (ownerPath && ownerPath.endsWith(".index.yaml")) {
26456
- return path18.join(path18.dirname(ownerPath), id, ".index.yaml");
27048
+ return path19.join(path19.dirname(ownerPath), id, ".index.yaml");
26457
27049
  }
26458
27050
  }
26459
27051
  if (subsystemId) {
26460
27052
  const subPath = this.getSubsystemPath(subsystemId);
26461
- const subDir = path18.dirname(subPath);
27053
+ const subDir = path19.dirname(subPath);
26462
27054
  if (subPath.endsWith(".index.yaml")) {
26463
- return path18.join(subDir, id, ".index.yaml");
27055
+ return path19.join(subDir, id, ".index.yaml");
26464
27056
  }
26465
27057
  }
26466
27058
  if (pathExists(this.paths.specsComponentsDir()) && listFiles(this.paths.specsComponentsDir(), ".yaml").length > 0) {
26467
- return path18.join(this.paths.specsComponentsDir(), `${id}.yaml`);
27059
+ return path19.join(this.paths.specsComponentsDir(), `${id}.yaml`);
26468
27060
  }
26469
27061
  const targetSubsystem = subsystemId || "default";
26470
- return path18.join(this.paths.specsDir(), targetSubsystem, id, ".index.yaml");
27062
+ return path19.join(this.paths.specsDir(), targetSubsystem, id, ".index.yaml");
26471
27063
  }
26472
27064
  getInterfacePath(id, componentId) {
26473
27065
  const index = this.scanAll();
@@ -26493,16 +27085,16 @@ var SpecWorkspace = class {
26493
27085
  }
26494
27086
  if (componentId) {
26495
27087
  const compPath = this.getComponentPath(componentId);
26496
- const compDir = path18.dirname(compPath);
27088
+ const compDir = path19.dirname(compPath);
26497
27089
  if (compPath.endsWith(".index.yaml")) {
26498
- return path18.join(compDir, ".interface.yaml");
27090
+ return path19.join(compDir, ".interface.yaml");
26499
27091
  }
26500
27092
  }
26501
27093
  if (pathExists(this.paths.specsInterfacesDir()) && listFiles(this.paths.specsInterfacesDir(), ".yaml").length > 0) {
26502
- return path18.join(this.paths.specsInterfacesDir(), `${id}.yaml`);
27094
+ return path19.join(this.paths.specsInterfacesDir(), `${id}.yaml`);
26503
27095
  }
26504
27096
  const targetComponent = componentId || "default";
26505
- return path18.join(this.paths.specsDir(), "default", targetComponent, ".interface.yaml");
27097
+ return path19.join(this.paths.specsDir(), "default", targetComponent, ".interface.yaml");
26506
27098
  }
26507
27099
  getImplementationPath(id, contractId) {
26508
27100
  const index = this.scanAll();
@@ -26528,16 +27120,16 @@ var SpecWorkspace = class {
26528
27120
  }
26529
27121
  if (contractId) {
26530
27122
  const intfPath = this.getInterfacePath(contractId);
26531
- const intfDir = path18.dirname(intfPath);
27123
+ const intfDir = path19.dirname(intfPath);
26532
27124
  if (intfPath.endsWith(".interface.yaml")) {
26533
- return path18.join(intfDir, ".implementation.yaml");
27125
+ return path19.join(intfDir, ".implementation.yaml");
26534
27126
  }
26535
27127
  }
26536
27128
  if (pathExists(this.paths.specsImplementationsDir()) && listFiles(this.paths.specsImplementationsDir(), ".yaml").length > 0) {
26537
- return path18.join(this.paths.specsImplementationsDir(), `${id}.yaml`);
27129
+ return path19.join(this.paths.specsImplementationsDir(), `${id}.yaml`);
26538
27130
  }
26539
27131
  const targetContract = contractId ? contractId.replace(/^i/, "") : "default";
26540
- return path18.join(this.paths.specsDir(), "default", targetContract, ".implementation.yaml");
27132
+ return path19.join(this.paths.specsDir(), "default", targetContract, ".implementation.yaml");
26541
27133
  }
26542
27134
  getTypePath(id, subsystemId, group) {
26543
27135
  const index = this.scanAll();
@@ -26568,7 +27160,7 @@ var SpecWorkspace = class {
26568
27160
  const groupPath = index.paths.group[targetGroup] || index.paths.group[plainGroup];
26569
27161
  if (groupPath) {
26570
27162
  const localId2 = id.split("::").pop();
26571
- return path18.join(path18.dirname(groupPath), `${localId2}.yaml`);
27163
+ return path19.join(path19.dirname(groupPath), `${localId2}.yaml`);
26572
27164
  }
26573
27165
  }
26574
27166
  let localId = id;
@@ -26581,12 +27173,12 @@ var SpecWorkspace = class {
26581
27173
  }
26582
27174
  if (subsystemId) {
26583
27175
  const subPath = this.getSubsystemPath(subsystemId);
26584
- const subDir = path18.dirname(subPath);
27176
+ const subDir = path19.dirname(subPath);
26585
27177
  if (subPath.endsWith(".index.yaml")) {
26586
- return path18.join(subDir, "types", `${localId}.yaml`);
27178
+ return path19.join(subDir, "types", `${localId}.yaml`);
26587
27179
  }
26588
27180
  }
26589
- return path18.join(this.paths.specsTypesDir(), `${localId}.yaml`);
27181
+ return path19.join(this.paths.specsTypesDir(), `${localId}.yaml`);
26590
27182
  }
26591
27183
  getGroupPath(id, subsystemId) {
26592
27184
  const index = this.scanAll();
@@ -26620,12 +27212,12 @@ var SpecWorkspace = class {
26620
27212
  }
26621
27213
  if (subsystemId) {
26622
27214
  const subPath = this.getSubsystemPath(subsystemId);
26623
- const subDir = path18.dirname(subPath);
27215
+ const subDir = path19.dirname(subPath);
26624
27216
  if (subPath.endsWith(".index.yaml")) {
26625
- return path18.join(subDir, "types", localId, ".index.yaml");
27217
+ return path19.join(subDir, "types", localId, ".index.yaml");
26626
27218
  }
26627
27219
  }
26628
- return path18.join(this.paths.specsTypesDir(), localId, ".index.yaml");
27220
+ return path19.join(this.paths.specsTypesDir(), localId, ".index.yaml");
26629
27221
  }
26630
27222
  // -------------------------------------------------------------------------
26631
27223
  // Level 0: System
@@ -26648,7 +27240,7 @@ var SpecWorkspace = class {
26648
27240
  }
26649
27241
  saveSystemSpec(spec) {
26650
27242
  const p = this.paths.specsSystem();
26651
- ensureDir(path18.dirname(p));
27243
+ ensureDir(path19.dirname(p));
26652
27244
  writeSpecFile(p, parseOrThrow(SystemSpecSchema, spec, "system", spec.name));
26653
27245
  invalidateSpecCache();
26654
27246
  }
@@ -26769,7 +27361,7 @@ var SpecWorkspace = class {
26769
27361
  */
26770
27362
  writeSubsystemSpec(spec, opts) {
26771
27363
  const p = this.getSubsystemPath(spec.id);
26772
- ensureDir(path18.dirname(p));
27364
+ ensureDir(path19.dirname(p));
26773
27365
  const { prefix } = splitNamespace(spec.id);
26774
27366
  let specToWrite = this.prepareSubsystemForWrite(spec);
26775
27367
  if (prefix) {
@@ -26833,7 +27425,7 @@ var SpecWorkspace = class {
26833
27425
  saveComponentSpec(spec, opts) {
26834
27426
  const notices = [];
26835
27427
  const p = this.getComponentPath(spec.id, spec.subsystem);
26836
- ensureDir(path18.dirname(p));
27428
+ ensureDir(path19.dirname(p));
26837
27429
  const specToWrite = this.prepareComponentForWrite(spec);
26838
27430
  const existing = this.loadComponentSpec(spec.id);
26839
27431
  if (existing) {
@@ -26850,7 +27442,7 @@ var SpecWorkspace = class {
26850
27442
  const subsystemChanged = existing && existing.subsystem !== spec.subsystem && splitNamespace(existing.subsystem).localId !== splitNamespace(spec.subsystem).localId;
26851
27443
  if (subsystemChanged && !p.endsWith(".index.yaml")) {
26852
27444
  notices.push(
26853
- `component "${spec.id}" already exists at ${path18.relative(this.rootDir, p)} \u2014 the flat layout re-saves in place and never relocates the file. The subsystem field is now "${spec.subsystem}" (was "${existing.subsystem}").`
27445
+ `component "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 the flat layout re-saves in place and never relocates the file. The subsystem field is now "${spec.subsystem}" (was "${existing.subsystem}").`
26854
27446
  );
26855
27447
  }
26856
27448
  specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
@@ -26874,12 +27466,12 @@ var SpecWorkspace = class {
26874
27466
  if (owner) {
26875
27467
  const ownerPath = index.paths.component[owner.id];
26876
27468
  if (ownerPath && ownerPath.endsWith(".index.yaml")) {
26877
- const ownerSubDir = path18.dirname(this.getSubsystemPath(owner.subsystem));
26878
- return path18.join(ownerSubDir, owner.id, comp.id);
27469
+ const ownerSubDir = path19.dirname(this.getSubsystemPath(owner.subsystem));
27470
+ return path19.join(ownerSubDir, owner.id, comp.id);
26879
27471
  }
26880
27472
  }
26881
- const subDir = path18.dirname(this.getSubsystemPath(comp.subsystem));
26882
- return path18.join(subDir, comp.id);
27473
+ const subDir = path19.dirname(this.getSubsystemPath(comp.subsystem));
27474
+ return path19.join(subDir, comp.id);
26883
27475
  }
26884
27476
  /**
26885
27477
  * Move component folders so the physical tree mirrors ownership: each owned
@@ -26895,7 +27487,7 @@ var SpecWorkspace = class {
26895
27487
  if (!currentPath) continue;
26896
27488
  const desiredDir = this.desiredComponentDir(comp, index);
26897
27489
  if (!desiredDir) continue;
26898
- if (moveComponentFolder(path18.dirname(currentPath), desiredDir)) moved.push(comp.id);
27490
+ if (moveComponentFolder(path19.dirname(currentPath), desiredDir)) moved.push(comp.id);
26899
27491
  }
26900
27492
  if (moved.length) invalidateSpecCache();
26901
27493
  return moved;
@@ -26951,7 +27543,7 @@ var SpecWorkspace = class {
26951
27543
  if (!occupantId) return;
26952
27544
  if (occupantId === id || splitNamespace(occupantId).localId === splitNamespace(id).localId) return;
26953
27545
  throw new Error(
26954
- `Cannot write ${kind} "${id}": ${parentLabel} is already ${kind === "interface" ? "served by" : "implemented by"} "${occupantId}" at ${path18.relative(this.rootDir, p)}, and both ids resolve to that one file. Re-author "${occupantId}" instead, or delete it first if you meant to replace it.`
27546
+ `Cannot write ${kind} "${id}": ${parentLabel} is already ${kind === "interface" ? "served by" : "implemented by"} "${occupantId}" at ${path19.relative(this.rootDir, p)}, and both ids resolve to that one file. Re-author "${occupantId}" instead, or delete it first if you meant to replace it.`
26955
27547
  );
26956
27548
  }
26957
27549
  /** Returns non-fatal placement notices (see saveTypeSpec) — empty when there is nothing to clarify. */
@@ -26959,12 +27551,12 @@ var SpecWorkspace = class {
26959
27551
  const notices = [];
26960
27552
  const p = this.getInterfacePath(spec.id, spec.component);
26961
27553
  this.assertPathHoldsNoOtherSpec(p, spec.id, "interface", `component "${spec.component}"`);
26962
- ensureDir(path18.dirname(p));
27554
+ ensureDir(path19.dirname(p));
26963
27555
  const specToWrite = this.prepareInterfaceForWrite(spec);
26964
27556
  const existing = this.loadInterfaceSpec(spec.id);
26965
27557
  if (existing && existing.component !== spec.component && splitNamespace(existing.component).localId !== splitNamespace(spec.component).localId) {
26966
27558
  notices.push(
26967
- `interface "${spec.id}" already exists at ${path18.relative(this.rootDir, p)} \u2014 re-saving updates the component binding field in place (now "${spec.component}", was "${existing.component}") and never moves the file.`
27559
+ `interface "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates the component binding field in place (now "${spec.component}", was "${existing.component}") and never moves the file.`
26968
27560
  );
26969
27561
  }
26970
27562
  if (existing) {
@@ -27021,12 +27613,12 @@ var SpecWorkspace = class {
27021
27613
  const notices = [];
27022
27614
  const p = this.getImplementationPath(spec.id, spec.contract);
27023
27615
  this.assertPathHoldsNoOtherSpec(p, spec.id, "implementation", `contract "${spec.contract}"`);
27024
- ensureDir(path18.dirname(p));
27616
+ ensureDir(path19.dirname(p));
27025
27617
  const specToWrite = this.prepareImplementationForWrite(spec);
27026
27618
  const existing = this.loadImplementationSpec(spec.id);
27027
27619
  if (existing && existing.contract !== spec.contract && splitNamespace(existing.contract).localId !== splitNamespace(spec.contract).localId) {
27028
27620
  notices.push(
27029
- `implementation "${spec.id}" already exists at ${path18.relative(this.rootDir, p)} \u2014 re-saving updates the contract binding field in place (now "${spec.contract}", was "${existing.contract}") and never moves the file.`
27621
+ `implementation "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates the contract binding field in place (now "${spec.contract}", was "${existing.contract}") and never moves the file.`
27030
27622
  );
27031
27623
  }
27032
27624
  if (existing) {
@@ -27067,11 +27659,11 @@ var SpecWorkspace = class {
27067
27659
  const existing = this.loadTypeSpec(spec.id);
27068
27660
  const group = spec.group || (existing ? existing.group : void 0);
27069
27661
  const p = this.getTypePath(spec.id, spec.subsystem, group);
27070
- ensureDir(path18.dirname(p));
27662
+ ensureDir(path19.dirname(p));
27071
27663
  const subsystemChanged = existing && (existing.subsystem ?? "") !== (spec.subsystem ?? "") && splitNamespace(existing.subsystem ?? "").localId !== splitNamespace(spec.subsystem ?? "").localId;
27072
27664
  if (subsystemChanged) {
27073
27665
  notices.push(
27074
- `type "${spec.id}" already exists at ${path18.relative(this.rootDir, p)} \u2014 re-saving updates fields in place and never relocates the file. The subsystem field is now "${spec.subsystem ?? "(none)"}" (was "${existing.subsystem ?? "(none)"}").`
27666
+ `type "${spec.id}" already exists at ${path19.relative(this.rootDir, p)} \u2014 re-saving updates fields in place and never relocates the file. The subsystem field is now "${spec.subsystem ?? "(none)"}" (was "${existing.subsystem ?? "(none)"}").`
27075
27667
  );
27076
27668
  }
27077
27669
  if (spec.subsystem && (!existing || subsystemChanged) && !this.getSubsystemPath(spec.subsystem).endsWith(".index.yaml")) {
@@ -27109,7 +27701,7 @@ var SpecWorkspace = class {
27109
27701
  }
27110
27702
  saveGroupSpec(spec, opts) {
27111
27703
  const p = this.getGroupPath(spec.id);
27112
- ensureDir(path18.dirname(p));
27704
+ ensureDir(path19.dirname(p));
27113
27705
  const specToWrite = this.prepareGroupForWrite(spec);
27114
27706
  const existing = this.loadGroupSpec(spec.id);
27115
27707
  if (existing) {
@@ -27360,7 +27952,7 @@ var SpecWorkspace = class {
27360
27952
  };
27361
27953
  const out = /* @__PURE__ */ new Set();
27362
27954
  const add = (p) => {
27363
- if (p) out.add(path18.resolve(p));
27955
+ if (p) out.add(path19.resolve(p));
27364
27956
  };
27365
27957
  for (const s of this.loadSubsystemSpecs()) {
27366
27958
  if (!scopeSubsystem || s.id === scopeSubsystem || s.id.startsWith(`${scopeSubsystem}::`)) {
@@ -27431,11 +28023,11 @@ var SpecWorkspace = class {
27431
28023
  const files = /* @__PURE__ */ new Set();
27432
28024
  const sysPath = this.paths.specsSystem();
27433
28025
  if (pathExists(sysPath)) {
27434
- files.add(path18.resolve(sysPath));
28026
+ files.add(path19.resolve(sysPath));
27435
28027
  }
27436
28028
  for (const group of Object.values(index.paths)) {
27437
28029
  for (const file of Object.values(group)) {
27438
- files.add(path18.resolve(file));
28030
+ files.add(path19.resolve(file));
27439
28031
  }
27440
28032
  }
27441
28033
  for (const file of files) {
@@ -27454,20 +28046,20 @@ var SpecWorkspace = class {
27454
28046
  const files = listSpecFiles(specsDir);
27455
28047
  const legacy = [];
27456
28048
  for (const f of files) {
27457
- const base = path18.basename(f);
27458
- const dir = path18.dirname(f);
28049
+ const base = path19.basename(f);
28050
+ const dir = path19.dirname(f);
27459
28051
  if (base === "system.yaml") {
27460
- legacy.push({ path: f, expected: path18.join(dir, ".index.yaml") });
28052
+ legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
27461
28053
  } else if (base === "subsystem.yaml") {
27462
- legacy.push({ path: f, expected: path18.join(dir, ".index.yaml") });
28054
+ legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
27463
28055
  } else if (base === "component.yaml") {
27464
- legacy.push({ path: f, expected: path18.join(dir, ".index.yaml") });
28056
+ legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
27465
28057
  } else if (base === "group.yaml") {
27466
- legacy.push({ path: f, expected: path18.join(dir, ".index.yaml") });
28058
+ legacy.push({ path: f, expected: path19.join(dir, ".index.yaml") });
27467
28059
  } else if (base === "interface.yaml") {
27468
- legacy.push({ path: f, expected: path18.join(dir, ".interface.yaml") });
28060
+ legacy.push({ path: f, expected: path19.join(dir, ".interface.yaml") });
27469
28061
  } else if (base === "implementation.yaml") {
27470
- legacy.push({ path: f, expected: path18.join(dir, ".implementation.yaml") });
28062
+ legacy.push({ path: f, expected: path19.join(dir, ".implementation.yaml") });
27471
28063
  }
27472
28064
  }
27473
28065
  return legacy;
@@ -28644,7 +29236,7 @@ function refusedMoveSummary(from, to, methods, refusals, alternatives) {
28644
29236
  }
28645
29237
  var workspaces = /* @__PURE__ */ new Map();
28646
29238
  function workspaceFor(rootDir) {
28647
- const key = path18.resolve(rootDir);
29239
+ const key = path19.resolve(rootDir);
28648
29240
  let ws = workspaces.get(key);
28649
29241
  if (!ws) {
28650
29242
  ws = new SpecWorkspace(key);
@@ -28771,7 +29363,7 @@ function snapshotInputsIn(dir, kind) {
28771
29363
  for (const file of fs12.readdirSync(dir).sort()) {
28772
29364
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
28773
29365
  try {
28774
- keys.push(`${kind}:${snapshotInputKey(SurfaceSnapshotSchema.parse(readYamlFile(path18.join(dir, file))))}`);
29366
+ keys.push(`${kind}:${snapshotInputKey(SurfaceSnapshotSchema.parse(readYamlFile(path19.join(dir, file))))}`);
28775
29367
  } catch {
28776
29368
  }
28777
29369
  }
@@ -28779,10 +29371,10 @@ function snapshotInputsIn(dir, kind) {
28779
29371
  }
28780
29372
  function consumedSurfaceInputsAt(rootDir) {
28781
29373
  const keys = [
28782
- ...snapshotInputsIn(path18.join(rootDir, ".wai", "surfaces"), "surface"),
28783
- ...snapshotInputsIn(path18.join(rootDir, ".wai", "externals"), "external")
29374
+ ...snapshotInputsIn(path19.join(rootDir, ".wai", "surfaces"), "surface"),
29375
+ ...snapshotInputsIn(path19.join(rootDir, ".wai", "externals"), "external")
28784
29376
  ];
28785
- const lock = path18.join(rootDir, ".wai", "externals.lock.yaml");
29377
+ const lock = path19.join(rootDir, ".wai", "externals.lock.yaml");
28786
29378
  if (pathExists(lock)) {
28787
29379
  try {
28788
29380
  keys.push(`lock:${canonicalize(readYamlFile(lock))}`);
@@ -28793,7 +29385,7 @@ function consumedSurfaceInputsAt(rootDir) {
28793
29385
  }
28794
29386
  function consumedContractInputs() {
28795
29387
  const root = getProjectRoot();
28796
- const roots = [root, ...listChainedRoots(root).map((rel2) => path18.join(root, rel2))];
29388
+ const roots = [root, ...listChainedRoots(root).map((rel2) => path19.join(root, rel2))];
28797
29389
  return roots.flatMap(consumedSurfaceInputsAt);
28798
29390
  }
28799
29391
  function readLockState(current2) {
@@ -28838,7 +29430,7 @@ function importNames(alias, names) {
28838
29430
  return projectConfigRepository.importNames(alias, names);
28839
29431
  }
28840
29432
  function computeStateIdAt(root) {
28841
- const resolved = path18.resolve(root);
29433
+ const resolved = path19.resolve(root);
28842
29434
  return runWithProjectRoot(resolved, () => {
28843
29435
  workspaceFor(resolved).invalidate();
28844
29436
  const system = loadSystemSpec();
@@ -28927,7 +29519,7 @@ function loadConfig() {
28927
29519
 
28928
29520
  // src/core/domain_projector.ts
28929
29521
  function rel(p) {
28930
- return path19.relative(process.cwd(), p).replace(/\\/g, "/");
29522
+ return path20.relative(process.cwd(), p).replace(/\\/g, "/");
28931
29523
  }
28932
29524
  function deriveSubsystemDomains() {
28933
29525
  const subsystems = loadSubsystemSpecs();
@@ -28968,9 +29560,9 @@ function unregisterDomain(id) {
28968
29560
  }
28969
29561
 
28970
29562
  // src/core/external-producers.ts
28971
- var path20 = __toESM(require("path"));
29563
+ var path21 = __toESM(require("path"));
28972
29564
  function dirKey2(dir) {
28973
- const resolved = path20.resolve(dir);
29565
+ const resolved = path21.resolve(dir);
28974
29566
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
28975
29567
  }
28976
29568
  function climb(bound2) {
@@ -28978,7 +29570,7 @@ function climb(bound2) {
28978
29570
  for (; ; ) {
28979
29571
  const parent = runWithProjectRoot(top, () => resolveChainingParent());
28980
29572
  if (!parent) break;
28981
- top = path20.resolve(parent.parentRoot);
29573
+ top = path21.resolve(parent.parentRoot);
28982
29574
  }
28983
29575
  const reach2 = getRequestParentReach();
28984
29576
  const stoppedAtNarrowedCeiling = !!reach2?.narrowed && reach2.topRoot !== void 0 && dirKey2(top) === dirKey2(reach2.topRoot);
@@ -28988,7 +29580,7 @@ function consumerNode(family, bound2) {
28988
29580
  return family.nodes.find((n) => dirKey2(n.directory) === dirKey2(bound2));
28989
29581
  }
28990
29582
  function resolveDeclared() {
28991
- const bound2 = path20.resolve(getProjectRoot());
29583
+ const bound2 = path21.resolve(getProjectRoot());
28992
29584
  const { top, whole } = climb(bound2);
28993
29585
  const answer = (root) => runWithProjectRoot(root, () => {
28994
29586
  const family = graph();
@@ -29005,7 +29597,7 @@ function resolveDeclared() {
29005
29597
 
29006
29598
  // src/core/provision.ts
29007
29599
  var fs13 = __toESM(require("fs"));
29008
- var path21 = __toESM(require("path"));
29600
+ var path22 = __toESM(require("path"));
29009
29601
  function defaultProjectConfig(name, now, id) {
29010
29602
  const declared = id ?? effectiveProjectId({ name });
29011
29603
  return {
@@ -29109,7 +29701,7 @@ function memberDeclarationsAt2(dir) {
29109
29701
  function walkMembers(projectRoot, onMember) {
29110
29702
  const visited = /* @__PURE__ */ new Set();
29111
29703
  const walk2 = (dir) => {
29112
- const resolved = path21.resolve(dir);
29704
+ const resolved = path22.resolve(dir);
29113
29705
  if (visited.has(resolved)) return;
29114
29706
  visited.add(resolved);
29115
29707
  for (const member of memberDeclarationsAt2(dir)) {
@@ -29160,25 +29752,65 @@ function backfillChainedSubprojectConfigs(projectRoot) {
29160
29752
  });
29161
29753
  return backfilled;
29162
29754
  }
29163
- function createMember(alias, path37, description) {
29164
- if (!EXTERNAL_ALIAS_RE.test(alias) || typeof path37 !== "string" || path37.trim() === "") {
29755
+ var FETCHABLE_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
29756
+ function scaffoldRequirements(requirements, installed) {
29757
+ const adopted = [];
29758
+ const unadopted = [];
29759
+ const profiles = [];
29760
+ for (const requirement of requirements) {
29761
+ let best = null;
29762
+ for (const pack of installed) {
29763
+ if (pack.name !== requirement.pack || !admits(requirement, pack.version)) continue;
29764
+ if (!best || isNewerVersion(best.version, pack.version)) best = pack;
29765
+ }
29766
+ if (!best) {
29767
+ unadopted.push(requirement);
29768
+ continue;
29769
+ }
29770
+ adopted.push({
29771
+ name: best.name,
29772
+ version: best.version,
29773
+ integrity: best.digest,
29774
+ ...best.origin && FETCHABLE_RE.test(best.origin) ? { source: best.origin } : {}
29775
+ });
29776
+ if (requirement.profile) profiles.push(requirement.profile);
29777
+ }
29778
+ return { adopted, unadopted, ...profiles.length === 1 ? { projectType: profiles[0] } : {} };
29779
+ }
29780
+ function createMember(alias, path38, description) {
29781
+ if (!EXTERNAL_ALIAS_RE.test(alias) || typeof path38 !== "string" || path38.trim() === "") {
29165
29782
  throw new WaironError(
29166
29783
  `an alias and a path are required to create a member: the alias must fit [a-z0-9-_]+ (got "${alias}") and the path must not be empty.`
29167
29784
  );
29168
29785
  }
29169
- const relPath = toPosixPath(path37);
29786
+ const relPath = toPosixPath(path38);
29170
29787
  const memberDir = assertContainedProjectPath(getProjectRoot(), relPath);
29788
+ const requirements = requiredPolicies(projectConfigRepository.load() ?? {});
29171
29789
  const parentName = loadSystemSpec()?.name;
29172
29790
  const vision = description ?? `Member ${alias} of ${parentName ? `the ${parentName} project` : "its parent project"}`;
29173
- runWithProjectRoot(memberDir, () => {
29791
+ const creation = runWithProjectRoot(memberDir, () => {
29174
29792
  ensureDir(aiPathsAt(memberDir).specsDir());
29793
+ if (projectConfigRepository.exists()) {
29794
+ ensureProjectInitialized(alias, alias, vision);
29795
+ return { configCreated: false, adopted: [], unadopted: [] };
29796
+ }
29797
+ const scaffold = scaffoldRequirements(requirements, listInstalledPacks());
29798
+ const name = loadSystemSpec()?.name ?? alias;
29799
+ const config = defaultProjectConfig(name, (/* @__PURE__ */ new Date()).toISOString(), alias);
29800
+ projectConfigRepository.create({
29801
+ ...config,
29802
+ ...scaffold.projectType ? { projectType: scaffold.projectType } : {},
29803
+ ...scaffold.adopted.length > 0 ? { extensions: { packs: scaffold.adopted, useGlobalPacks: false } } : {}
29804
+ });
29175
29805
  ensureProjectInitialized(alias, alias, vision);
29806
+ return { configCreated: true, ...scaffold };
29176
29807
  });
29177
29808
  projectConfigRepository.declareMember(alias, {
29178
29809
  path: relPath,
29179
29810
  ...description !== void 0 ? { description } : {}
29180
29811
  });
29181
29812
  invalidateSpecCache();
29813
+ return creation;
29182
29814
  }
29183
29815
  function moveMember(alias, newPath) {
29184
29816
  const member = graph().nodes.find((n) => n.parent === "" && n.mountAlias === alias);
@@ -29207,7 +29839,7 @@ function moveMember(alias, newPath) {
29207
29839
  if (fs13.existsSync(newDir)) {
29208
29840
  throw new WaironError(`Target directory already exists: ${newDir}`);
29209
29841
  }
29210
- ensureDir(path21.dirname(newDir));
29842
+ ensureDir(path22.dirname(newDir));
29211
29843
  fs13.renameSync(oldDir, newDir);
29212
29844
  }
29213
29845
  projectConfigRepository.setMemberPath(alias, nextPath);
@@ -29229,9 +29861,9 @@ function toPosixPath(p) {
29229
29861
  return p.replace(/\\/g, "/");
29230
29862
  }
29231
29863
  function isWithinDir2(dir, file) {
29232
- const d = path21.resolve(dir);
29233
- const f = path21.resolve(file);
29234
- return f === d || f.startsWith(d + path21.sep);
29864
+ const d = path22.resolve(dir);
29865
+ const f = path22.resolve(file);
29866
+ return f === d || f.startsWith(d + path22.sep);
29235
29867
  }
29236
29868
  var MOVED_REF_POSITIONS = /* @__PURE__ */ new Set(["component", "interface"]);
29237
29869
  var INTO_MEMBER_POSITIONS = /* @__PURE__ */ new Set([...MOVED_REF_POSITIONS, "type"]);
@@ -29245,13 +29877,13 @@ function externalizeSubsystem(subsystemId, memberPath) {
29245
29877
  }
29246
29878
  const parentRoot = getProjectRoot();
29247
29879
  const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
29248
- const fooDir = path21.join(parentSpecsDir, subsystemId);
29880
+ const fooDir = path22.join(parentSpecsDir, subsystemId);
29249
29881
  if (!fs13.existsSync(fooDir)) {
29250
29882
  throw new WaironError(`subsystem specs directory not found: ${fooDir}`);
29251
29883
  }
29252
29884
  const relPath = toPosixPath(memberPath);
29253
29885
  const memberDir = assertContainedProjectPath(parentRoot, relPath);
29254
- const memberFooDir = path21.join(aiPathsAt(memberDir).specsDir(), subsystemId);
29886
+ const memberFooDir = path22.join(aiPathsAt(memberDir).specsDir(), subsystemId);
29255
29887
  if (fs13.existsSync(memberFooDir)) {
29256
29888
  throw new WaironError(`target already contains a "${subsystemId}" subsystem: ${memberFooDir}`);
29257
29889
  }
@@ -29275,10 +29907,10 @@ function externalizeSubsystem(subsystemId, memberPath) {
29275
29907
  ensureDir(aiPathsAt(memberDir).specsDir());
29276
29908
  saveSystemSpec(bootstrapSystemSpec(memberName, now));
29277
29909
  });
29278
- ensureDir(path21.dirname(memberFooDir));
29910
+ ensureDir(path22.dirname(memberFooDir));
29279
29911
  fs13.renameSync(fooDir, memberFooDir);
29280
29912
  rebaseImplementationPaths(memberFooDir, parentRoot, memberDir);
29281
- patchSubsystemIndex(path21.join(memberFooDir, ".index.yaml"), (s) => {
29913
+ patchSubsystemIndex(path22.join(memberFooDir, ".index.yaml"), (s) => {
29282
29914
  s.parentSystem = memberName;
29283
29915
  delete s.projectPath;
29284
29916
  });
@@ -29376,16 +30008,16 @@ function internalizeMember(alias) {
29376
30008
  }
29377
30009
  const parentRoot = getProjectRoot();
29378
30010
  const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
29379
- const memberDir = assertContainedProjectPath(parentRoot, path21.relative(parentRoot, member.directory) || ".");
30011
+ const memberDir = assertContainedProjectPath(parentRoot, path22.relative(parentRoot, member.directory) || ".");
29380
30012
  const memberSpecsDir = aiPathsAt(memberDir).specsDir();
29381
30013
  const localSub = subsystems[0].id.slice(member.namespace.length + 2);
29382
30014
  const intoMember = crossingReferences(family, "", member.namespace);
29383
30015
  const backOut = crossingReferences(family, member.namespace, "");
29384
30016
  deleteMount(alias);
29385
- const movedFiles = moveTreeInto(memberSpecsDir, parentSpecsDir, /* @__PURE__ */ new Set([path21.resolve(aiPathsAt(memberDir).specsSystem())]));
30017
+ const movedFiles = moveTreeInto(memberSpecsDir, parentSpecsDir, /* @__PURE__ */ new Set([path22.resolve(aiPathsAt(memberDir).specsSystem())]));
29386
30018
  rebaseImplementationPaths(parentSpecsDir, memberDir, parentRoot, movedFiles);
29387
30019
  const parentSystemName = loadSystemSpec()?.name;
29388
- patchSubsystemIndex(path21.join(parentSpecsDir, localSub, ".index.yaml"), (s) => {
30020
+ patchSubsystemIndex(path22.join(parentSpecsDir, localSub, ".index.yaml"), (s) => {
29389
30021
  if (parentSystemName) s.parentSystem = parentSystemName;
29390
30022
  delete s.projectPath;
29391
30023
  });
@@ -29393,7 +30025,7 @@ function internalizeMember(alias) {
29393
30025
  ...rewriteRefFields(parentSpecsDir, (ref) => intoMember.get(ref) ?? backOut.get(ref) ?? ref)
29394
30026
  ];
29395
30027
  projectConfigRepository.removeMember(alias);
29396
- fs13.rmSync(path21.join(memberDir, ".wai"), { recursive: true, force: true });
30028
+ fs13.rmSync(path22.join(memberDir, ".wai"), { recursive: true, force: true });
29397
30029
  invalidateSpecCache();
29398
30030
  for (const spec of rewritten) if (spec.kind !== "system") normalizeReferences(spec.kind, spec.id);
29399
30031
  restateReExports(alias, localSub);
@@ -29477,14 +30109,14 @@ function referencesUnder(dir, matches) {
29477
30109
  }
29478
30110
  function moveTreeInto(src, dst, skip) {
29479
30111
  if (!fs13.existsSync(src)) return [];
29480
- const files = listFilesRecursive(src, "").filter((f) => !skip.has(path21.resolve(f)));
29481
- const targets = files.map((f) => path21.join(dst, path21.relative(src, f)));
30112
+ const files = listFilesRecursive(src, "").filter((f) => !skip.has(path22.resolve(f)));
30113
+ const targets = files.map((f) => path22.join(dst, path22.relative(src, f)));
29482
30114
  const taken = targets.filter((t) => fs13.existsSync(t));
29483
30115
  if (taken.length > 0) {
29484
- throw new WaironError(`cannot internalize: the member's specs would overwrite ${taken.map((t) => path21.relative(dst, t)).join(", ")} in this project.`);
30116
+ throw new WaironError(`cannot internalize: the member's specs would overwrite ${taken.map((t) => path22.relative(dst, t)).join(", ")} in this project.`);
29485
30117
  }
29486
30118
  files.forEach((file, i) => {
29487
- ensureDir(path21.dirname(targets[i]));
30119
+ ensureDir(path22.dirname(targets[i]));
29488
30120
  fs13.renameSync(file, targets[i]);
29489
30121
  });
29490
30122
  return targets;
@@ -29564,8 +30196,8 @@ function rebaseImplementationPaths(specsDir, fromRoot, toRoot, only) {
29564
30196
  let changed = false;
29565
30197
  const rebase = (holder, key) => {
29566
30198
  const p = holder[key];
29567
- if (typeof p !== "string" || p === "" || path21.isAbsolute(p)) return;
29568
- const next = toPosixPath(path21.relative(toRoot, path21.resolve(fromRoot, p)));
30199
+ if (typeof p !== "string" || p === "" || path22.isAbsolute(p)) return;
30200
+ const next = toPosixPath(path22.relative(toRoot, path22.resolve(fromRoot, p)));
29569
30201
  if (next !== p) {
29570
30202
  holder[key] = next;
29571
30203
  changed = true;
@@ -29650,7 +30282,7 @@ function moveRenamedSpecs(componentId, newId, component, movingInterface, moving
29650
30282
  if (movedInterface) saveInterfaceSpec({ ...movedInterface, id: interfaceId, component: newId });
29651
30283
  if (movedImplementation) {
29652
30284
  const oldFile = getImplementationPath(movedImplementation.id);
29653
- if (path21.resolve(getImplementationPath(implementationId, movedImplementation.contract)) === path21.resolve(oldFile)) {
30285
+ if (path22.resolve(getImplementationPath(implementationId, movedImplementation.contract)) === path22.resolve(oldFile)) {
29654
30286
  fs13.unlinkSync(oldFile);
29655
30287
  }
29656
30288
  saveImplementationSpec({ ...movedImplementation, id: implementationId });
@@ -29668,8 +30300,8 @@ function removeSpecFile2(file, id) {
29668
30300
  return;
29669
30301
  }
29670
30302
  fs13.unlinkSync(file);
29671
- const specsRoot = path21.resolve(aiPathsAt(getProjectRoot()).specsDir());
29672
- for (let dir = path21.dirname(path21.resolve(file)); dir !== specsRoot && isWithinDir2(specsRoot, dir); dir = path21.dirname(dir)) {
30303
+ const specsRoot = path22.resolve(aiPathsAt(getProjectRoot()).specsDir());
30304
+ for (let dir = path22.dirname(path22.resolve(file)); dir !== specsRoot && isWithinDir2(specsRoot, dir); dir = path22.dirname(dir)) {
29673
30305
  if (fs13.readdirSync(dir).length > 0) break;
29674
30306
  fs13.rmdirSync(dir);
29675
30307
  }
@@ -29818,7 +30450,7 @@ function normalizeReferences2(kind, id) {
29818
30450
 
29819
30451
  // src/core/detection.ts
29820
30452
  var fs14 = __toESM(require("fs"));
29821
- var path22 = __toESM(require("path"));
30453
+ var path23 = __toESM(require("path"));
29822
30454
  var PACKAGE_MARKERS = [
29823
30455
  "package.json",
29824
30456
  "pyproject.toml",
@@ -29891,7 +30523,7 @@ function parseGitmodules(filePath) {
29891
30523
  return entries;
29892
30524
  }
29893
30525
  function detectGitSubmodules(projectRoot) {
29894
- const gitmodulesPath = path22.join(projectRoot, ".gitmodules");
30526
+ const gitmodulesPath = path23.join(projectRoot, ".gitmodules");
29895
30527
  if (!fs14.existsSync(gitmodulesPath)) return [];
29896
30528
  return parseGitmodules(gitmodulesPath).map((entry) => ({
29897
30529
  suggestedId: pathToId(entry.path),
@@ -29917,10 +30549,10 @@ function walkForGit(projectRoot, currentDir, depth, results) {
29917
30549
  for (const entry of entries) {
29918
30550
  if (!entry.isDirectory()) continue;
29919
30551
  if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
29920
- const fullPath = path22.join(currentDir, entry.name);
29921
- const relPath = normalizePath(path22.relative(projectRoot, fullPath));
30552
+ const fullPath = path23.join(currentDir, entry.name);
30553
+ const relPath = normalizePath(path23.relative(projectRoot, fullPath));
29922
30554
  if (relPath === "" || relPath === ".") continue;
29923
- const gitPath = path22.join(fullPath, ".git");
30555
+ const gitPath = path23.join(fullPath, ".git");
29924
30556
  if (fs14.existsSync(gitPath)) {
29925
30557
  results.push({
29926
30558
  suggestedId: pathToId(relPath),
@@ -29950,10 +30582,10 @@ function walkForPackages(projectRoot, currentDir, depth, results) {
29950
30582
  for (const entry of entries) {
29951
30583
  if (!entry.isDirectory()) continue;
29952
30584
  if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
29953
- const fullPath = path22.join(currentDir, entry.name);
29954
- const relPath = normalizePath(path22.relative(projectRoot, fullPath));
30585
+ const fullPath = path23.join(currentDir, entry.name);
30586
+ const relPath = normalizePath(path23.relative(projectRoot, fullPath));
29955
30587
  if (relPath === "" || relPath === ".") continue;
29956
- const hasMarker = PACKAGE_MARKERS.some((m) => fs14.existsSync(path22.join(fullPath, m)));
30588
+ const hasMarker = PACKAGE_MARKERS.some((m) => fs14.existsSync(path23.join(fullPath, m)));
29957
30589
  if (hasMarker) {
29958
30590
  results.push({
29959
30591
  suggestedId: pathToId(relPath),
@@ -29967,8 +30599,8 @@ function walkForPackages(projectRoot, currentDir, depth, results) {
29967
30599
  }
29968
30600
  }
29969
30601
  function pathToId(relPath) {
29970
- const basename10 = path22.basename(relPath);
29971
- return basename10.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
30602
+ const basename11 = path23.basename(relPath);
30603
+ return basename11.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
29972
30604
  }
29973
30605
  function pathToName(relPath) {
29974
30606
  const id = pathToId(relPath);
@@ -29981,7 +30613,7 @@ function normalizePath(p) {
29981
30613
  // src/core/variants.ts
29982
30614
  var fs15 = __toESM(require("fs"));
29983
30615
  var os3 = __toESM(require("os"));
29984
- var path23 = __toESM(require("path"));
30616
+ var path24 = __toESM(require("path"));
29985
30617
  var import_zod10 = require("zod");
29986
30618
  var VariantDefSchema = import_zod10.z.object({
29987
30619
  /** Variant id referenced by a component's `variant` (e.g. "publisher", "org/external-config-adapter"). */
@@ -29996,19 +30628,19 @@ var VariantDefSchema = import_zod10.z.object({
29996
30628
  profile: import_zod10.z.string().optional()
29997
30629
  });
29998
30630
  function globalVariantsDir() {
29999
- return process.env.WAIRON_VARIANTS_DIR ?? path23.join(os3.homedir(), ".wairon", "variants");
30631
+ return process.env.WAIRON_VARIANTS_DIR ?? path24.join(os3.homedir(), ".wairon", "variants");
30000
30632
  }
30001
30633
  function builtinVariantsDir() {
30002
30634
  const candidates = [
30003
- path23.resolve(__dirname, "..", "templates", "variants"),
30635
+ path24.resolve(__dirname, "..", "templates", "variants"),
30004
30636
  // src/core & dist/cli
30005
- path23.resolve(__dirname, "templates", "variants")
30637
+ path24.resolve(__dirname, "templates", "variants")
30006
30638
  // dist (library entry)
30007
30639
  ];
30008
30640
  return candidates.find((dir) => fs15.existsSync(dir)) ?? candidates[0];
30009
30641
  }
30010
30642
  function projectVariantsDir() {
30011
- return path23.join(getProjectRoot(), ".wai", "variants");
30643
+ return path24.join(getProjectRoot(), ".wai", "variants");
30012
30644
  }
30013
30645
  function variantFiles(dir) {
30014
30646
  let entries;
@@ -30017,7 +30649,7 @@ function variantFiles(dir) {
30017
30649
  } catch {
30018
30650
  return [];
30019
30651
  }
30020
- return entries.filter((e) => e.isFile() && /\.ya?ml$/i.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => path23.join(dir, e.name));
30652
+ return entries.filter((e) => e.isFile() && /\.ya?ml$/i.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => path24.join(dir, e.name));
30021
30653
  }
30022
30654
  function parseVariants(raw) {
30023
30655
  if (raw == null) return [];
@@ -30128,18 +30760,18 @@ function rebasedText(content, fromBase, toBase, rebasedContent) {
30128
30760
  }
30129
30761
 
30130
30762
  // src/core/agent_resolver.ts
30131
- var path25 = __toESM(require("path"));
30763
+ var path26 = __toESM(require("path"));
30132
30764
  var fs16 = __toESM(require("fs"));
30133
30765
 
30134
30766
  // src/core/templates.ts
30135
- var path24 = __toESM(require("path"));
30767
+ var path25 = __toESM(require("path"));
30136
30768
  function builtinTemplatesDir() {
30137
- return path24.resolve(__dirname, "..", "templates");
30769
+ return path25.resolve(__dirname, "..", "templates");
30138
30770
  }
30139
30771
  function loadTemplate(id, globalOverride) {
30140
30772
  const dirs = templateSearchDirs(globalOverride);
30141
30773
  for (const dir of dirs) {
30142
- const filePath = path24.join(dir, `${id}.yaml`);
30774
+ const filePath = path25.join(dir, `${id}.yaml`);
30143
30775
  if (pathExists(filePath)) {
30144
30776
  const raw = readYamlFile(filePath);
30145
30777
  return { ...TemplateSchema.parse(raw), templateName: id };
@@ -30148,7 +30780,7 @@ function loadTemplate(id, globalOverride) {
30148
30780
  throw new TemplateNotFoundError(id);
30149
30781
  }
30150
30782
  function loadAgentOverride(agentId) {
30151
- const filePath = path24.join(AI_PATHS.root(), "agents", `${agentId}.md`);
30783
+ const filePath = path25.join(AI_PATHS.root(), "agents", `${agentId}.md`);
30152
30784
  return readFileOrNull(filePath);
30153
30785
  }
30154
30786
  function templateSearchDirs(globalOverride) {
@@ -30300,7 +30932,7 @@ function resolveBudget(profile, config, agentId) {
30300
30932
  var projectFilesCache = /* @__PURE__ */ new Map();
30301
30933
  function listFilesRecursiveSafe(dirPath, ext) {
30302
30934
  if (!fs16.existsSync(dirPath)) return [];
30303
- const nameLower = path25.basename(dirPath).toLowerCase();
30935
+ const nameLower = path26.basename(dirPath).toLowerCase();
30304
30936
  const IGNORED_DIRS = /* @__PURE__ */ new Set([
30305
30937
  "node_modules",
30306
30938
  "target",
@@ -30318,7 +30950,7 @@ function listFilesRecursiveSafe(dirPath, ext) {
30318
30950
  const entries = fs16.readdirSync(dirPath, { withFileTypes: true });
30319
30951
  const files = [];
30320
30952
  for (const entry of entries) {
30321
- const fullPath = path25.join(dirPath, entry.name);
30953
+ const fullPath = path26.join(dirPath, entry.name);
30322
30954
  if (entry.isDirectory()) {
30323
30955
  files.push(...listFilesRecursiveSafe(fullPath, ext));
30324
30956
  } else if (entry.isFile() && entry.name.endsWith(ext)) {
@@ -30331,8 +30963,8 @@ function getProjectFiles(projectDir) {
30331
30963
  let files = projectFilesCache.get(projectDir);
30332
30964
  if (!files) {
30333
30965
  files = [];
30334
- const srcDir = path25.join(projectDir, "src");
30335
- const legacySrcDir = path25.join(projectDir, "legacy-src");
30966
+ const srcDir = path26.join(projectDir, "src");
30967
+ const legacySrcDir = path26.join(projectDir, "legacy-src");
30336
30968
  let searchDir = projectDir;
30337
30969
  if (pathExists(srcDir)) {
30338
30970
  searchDir = srcDir;
@@ -30347,7 +30979,7 @@ function getProjectFiles(projectDir) {
30347
30979
  files.push(...listFilesRecursiveSafe(searchDir, ext));
30348
30980
  }
30349
30981
  const rootDir = getProjectRoot();
30350
- files = files.map((f) => path25.relative(rootDir, f).replace(/\\/g, "/"));
30982
+ files = files.map((f) => path26.relative(rootDir, f).replace(/\\/g, "/"));
30351
30983
  projectFilesCache.set(projectDir, files);
30352
30984
  }
30353
30985
  return files;
@@ -30372,8 +31004,8 @@ function inferSourcePathForComponent(comp, subsystems) {
30372
31004
  let bestFile = null;
30373
31005
  let bestScore = -1;
30374
31006
  for (const f of files) {
30375
- const ext = path25.extname(f);
30376
- const base = path25.basename(f, ext).toLowerCase();
31007
+ const ext = path26.extname(f);
31008
+ const base = path26.basename(f, ext).toLowerCase();
30377
31009
  if (candidates.has(base)) {
30378
31010
  let score = 0;
30379
31011
  const normalizedPath = f.toLowerCase();
@@ -30428,8 +31060,8 @@ function inferSourcePathForComponent(comp, subsystems) {
30428
31060
  return null;
30429
31061
  }
30430
31062
  }
30431
- function summarize2(text2, max = 140) {
30432
- const clean = text2.replace(/\s+/g, " ").trim();
31063
+ function summarize2(text3, max = 140) {
31064
+ const clean = text3.replace(/\s+/g, " ").trim();
30433
31065
  const period = clean.indexOf(". ");
30434
31066
  const firstSentence = period > 0 ? clean.slice(0, period + 1) : clean;
30435
31067
  if (firstSentence.length <= max) return firstSentence;
@@ -30446,7 +31078,7 @@ function resolveAgentTopology() {
30446
31078
  const members = graph().nodes.filter((n) => n.parent === "" && n.mountAlias !== void 0).map((n) => ({
30447
31079
  id: n.mountAlias,
30448
31080
  name: n.mountAlias,
30449
- projectPath: path25.relative(getProjectRoot(), n.directory).replace(/\\/g, "/"),
31081
+ projectPath: path26.relative(getProjectRoot(), n.directory).replace(/\\/g, "/"),
30450
31082
  form: n.mountForm,
30451
31083
  createdAt: n.legacyMount?.createdAt ?? system.createdAt,
30452
31084
  updatedAt: n.legacyMount?.updatedAt ?? system.updatedAt
@@ -30481,7 +31113,7 @@ function resolveAgentTopology() {
30481
31113
  });
30482
31114
  for (const sub of subsystems) {
30483
31115
  if (sub.projectPath) {
30484
- const mountSpecPath = sub.form === "members" ? `${sub.projectPath}/**` : path25.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/");
31116
+ const mountSpecPath = sub.form === "members" ? `${sub.projectPath}/**` : path26.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/");
30485
31117
  agents.push({
30486
31118
  id: `${sub.id}-owner`,
30487
31119
  name: `${sub.name} (member project)`,
@@ -30503,9 +31135,9 @@ function resolveAgentTopology() {
30503
31135
  }
30504
31136
  const subComponents = components.filter((c) => c.subsystem === sub.id);
30505
31137
  const ownedPaths = [];
30506
- ownedPaths.push(path25.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/"));
31138
+ ownedPaths.push(path26.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/"));
30507
31139
  for (const c of subComponents) {
30508
- ownedPaths.push(path25.relative(getProjectRoot(), getComponentPath(c.id, sub.id)).replace(/\\/g, "/"));
31140
+ ownedPaths.push(path26.relative(getProjectRoot(), getComponentPath(c.id, sub.id)).replace(/\\/g, "/"));
30509
31141
  }
30510
31142
  if (!config.rules.generateComponentImplementers) {
30511
31143
  for (const comp of subComponents) {
@@ -30581,10 +31213,10 @@ function resolveAgentTopology() {
30581
31213
  }
30582
31214
  const dependencies = comp.dependsOn.map((depId) => `${depId}-implementer`);
30583
31215
  const readPaths = [
30584
- path25.relative(getProjectRoot(), AI_PATHS.specsSystem()).replace(/\\/g, "/"),
30585
- path25.relative(getProjectRoot(), getComponentPath(comp.id, comp.subsystem)).replace(/\\/g, "/"),
30586
- ...compInterfaces.map((i) => path25.relative(getProjectRoot(), getInterfacePath(i.id, comp.id)).replace(/\\/g, "/")),
30587
- ...compImpls.map((impl) => path25.relative(getProjectRoot(), getImplementationPath(impl.id, impl.contract)).replace(/\\/g, "/"))
31216
+ path26.relative(getProjectRoot(), AI_PATHS.specsSystem()).replace(/\\/g, "/"),
31217
+ path26.relative(getProjectRoot(), getComponentPath(comp.id, comp.subsystem)).replace(/\\/g, "/"),
31218
+ ...compInterfaces.map((i) => path26.relative(getProjectRoot(), getInterfacePath(i.id, comp.id)).replace(/\\/g, "/")),
31219
+ ...compImpls.map((impl) => path26.relative(getProjectRoot(), getImplementationPath(impl.id, impl.contract)).replace(/\\/g, "/"))
30588
31220
  ];
30589
31221
  agents.push({
30590
31222
  id: `${comp.id}-implementer`,
@@ -30690,10 +31322,10 @@ function loadRegistry() {
30690
31322
  }
30691
31323
 
30692
31324
  // src/exporters/generate.ts
30693
- var path29 = __toESM(require("path"));
31325
+ var path30 = __toESM(require("path"));
30694
31326
 
30695
31327
  // src/exporters/claude.ts
30696
- var path26 = __toESM(require("path"));
31328
+ var path27 = __toESM(require("path"));
30697
31329
  var MODEL_BY_TIER = {
30698
31330
  small: "haiku",
30699
31331
  standard: "sonnet",
@@ -30732,7 +31364,7 @@ var ClaudeExporter = class {
30732
31364
  outputPath(ctx) {
30733
31365
  const { agent, target, projectRoot } = ctx;
30734
31366
  const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
30735
- return path26.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
31367
+ return path27.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
30736
31368
  }
30737
31369
  export(ctx) {
30738
31370
  const { agent, renderedInstructions, budget } = ctx;
@@ -30757,7 +31389,7 @@ var ClaudeExporter = class {
30757
31389
  };
30758
31390
 
30759
31391
  // src/exporters/custom.ts
30760
- var path27 = __toESM(require("path"));
31392
+ var path28 = __toESM(require("path"));
30761
31393
  var CustomExporter = class {
30762
31394
  constructor() {
30763
31395
  this.targetType = "custom";
@@ -30767,7 +31399,7 @@ var CustomExporter = class {
30767
31399
  if (!("outputDir" in target)) {
30768
31400
  throw new Error("CustomExporter requires target.outputDir");
30769
31401
  }
30770
- return path27.resolve(projectRoot, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
31402
+ return path28.resolve(projectRoot, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
30771
31403
  }
30772
31404
  export(ctx) {
30773
31405
  const { agent, target, renderedInstructions } = ctx;
@@ -30790,7 +31422,7 @@ var CustomExporter = class {
30790
31422
  };
30791
31423
 
30792
31424
  // src/exporters/gemini.ts
30793
- var path28 = __toESM(require("path"));
31425
+ var path29 = __toESM(require("path"));
30794
31426
  var GeminiExporter = class {
30795
31427
  constructor() {
30796
31428
  this.targetType = "gemini";
@@ -30798,7 +31430,7 @@ var GeminiExporter = class {
30798
31430
  outputPath(ctx) {
30799
31431
  const { agent, target, projectRoot } = ctx;
30800
31432
  const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
30801
- return path28.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
31433
+ return path29.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
30802
31434
  }
30803
31435
  export(ctx) {
30804
31436
  const { agent, renderedInstructions } = ctx;
@@ -30895,7 +31527,7 @@ function resolveExpectedOutputPaths(agents, projectConfig, projectRoot = getProj
30895
31527
  const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
30896
31528
  if (!targetConfig) continue;
30897
31529
  const ctx = { agent, projectRoot, target: targetConfig };
30898
- expected.add(path29.resolve(getExporter(targetConfig).outputPath(ctx)));
31530
+ expected.add(path30.resolve(getExporter(targetConfig).outputPath(ctx)));
30899
31531
  }
30900
31532
  }
30901
31533
  return expected;
@@ -30910,7 +31542,7 @@ function resolveTargetConfig(agentTarget, projectConfig) {
30910
31542
 
30911
31543
  // src/core/treetransfer.ts
30912
31544
  var fs17 = __toESM(require("fs"));
30913
- var path30 = __toESM(require("path"));
31545
+ var path31 = __toESM(require("path"));
30914
31546
  var sdkPortal = __toESM(require_dist());
30915
31547
  var STAGING_PREFIX = ".wai-staging-";
30916
31548
  var BACKUP_DIR = ".wai-backups";
@@ -30936,39 +31568,39 @@ function hasAuthoredSpecs(root) {
30936
31568
  if (!pathExists(specsDir)) {
30937
31569
  return false;
30938
31570
  }
30939
- const systemIndex = path30.resolve(paths.specsSystem());
31571
+ const systemIndex = path31.resolve(paths.specsSystem());
30940
31572
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
30941
- if (path30.resolve(file) !== systemIndex) {
31573
+ if (path31.resolve(file) !== systemIndex) {
30942
31574
  return true;
30943
31575
  }
30944
31576
  }
30945
31577
  return false;
30946
31578
  }
30947
31579
  function createStagingDir(destDir) {
30948
- const root = path30.resolve(destDir);
31580
+ const root = path31.resolve(destDir);
30949
31581
  fs17.mkdirSync(root, { recursive: true });
30950
- const stagingDir = fs17.mkdtempSync(path30.join(root, STAGING_PREFIX));
31582
+ const stagingDir = fs17.mkdtempSync(path31.join(root, STAGING_PREFIX));
30951
31583
  return stagingDir;
30952
31584
  }
30953
31585
  function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
30954
- const root = path30.resolve(destDir);
30955
- const backupDir = path30.join(root, BACKUP_DIR, timestampSlug());
31586
+ const root = path31.resolve(destDir);
31587
+ const backupDir = path31.join(root, BACKUP_DIR, timestampSlug());
30956
31588
  let backedUp = false;
30957
31589
  for (const rootRel of roots) {
30958
31590
  const relative12 = rootRel === "." ? "" : rootRel;
30959
- const stagedSpecDir = path30.join(stagingDir, relative12, ".wai");
31591
+ const stagedSpecDir = path31.join(stagingDir, relative12, ".wai");
30960
31592
  if (!fs17.existsSync(stagedSpecDir)) continue;
30961
- const liveSpecDir = liveSpecDirFor(path30.join(root, relative12));
31593
+ const liveSpecDir = liveSpecDirFor(path31.join(root, relative12));
30962
31594
  if (backupExisting && liveSpecDir && fs17.existsSync(liveSpecDir)) {
30963
- const backupTarget = path30.join(backupDir, relative12, path30.basename(liveSpecDir));
30964
- fs17.mkdirSync(path30.dirname(backupTarget), { recursive: true });
31595
+ const backupTarget = path31.join(backupDir, relative12, path31.basename(liveSpecDir));
31596
+ fs17.mkdirSync(path31.dirname(backupTarget), { recursive: true });
30965
31597
  fs17.renameSync(liveSpecDir, backupTarget);
30966
31598
  backedUp = true;
30967
31599
  } else if (liveSpecDir && fs17.existsSync(liveSpecDir)) {
30968
31600
  fs17.rmSync(liveSpecDir, { recursive: true, force: true });
30969
31601
  }
30970
- const target = path30.join(root, relative12, ".wai");
30971
- fs17.mkdirSync(path30.dirname(target), { recursive: true });
31602
+ const target = path31.join(root, relative12, ".wai");
31603
+ fs17.mkdirSync(path31.dirname(target), { recursive: true });
30972
31604
  if (fs17.existsSync(target)) fs17.rmSync(target, { recursive: true, force: true });
30973
31605
  fs17.renameSync(stagedSpecDir, target);
30974
31606
  }
@@ -30993,7 +31625,7 @@ function exportSpecTree(includeDerived, allowPartial) {
30993
31625
  const skipped = inspection.skipped.map(({ mount, projectPath, reason }) => ({ mount, projectPath, reason }));
30994
31626
  const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
30995
31627
  for (const rel2 of inspection.roots) {
30996
- const waiDir = aiPathsAt(path30.resolve(root, rel2)).root();
31628
+ const waiDir = aiPathsAt(path31.resolve(root, rel2)).root();
30997
31629
  if (pathExists(waiDir)) {
30998
31630
  roots.push({ relativePath: rel2, waiDir });
30999
31631
  } else {
@@ -31024,7 +31656,7 @@ function importSpecTree(archive, options) {
31024
31656
  if (!info.compatible) {
31025
31657
  throw new Error("incompatible .waitree archive (format or minimum wairon version)");
31026
31658
  }
31027
- const destDir = path30.resolve(options.destDir ?? getProjectRoot());
31659
+ const destDir = path31.resolve(options.destDir ?? getProjectRoot());
31028
31660
  const occupied = hasSpecTree(destDir);
31029
31661
  const authored = hasAuthoredSpecs(destDir);
31030
31662
  if (authored && options.replaceExisting !== true) {
@@ -31055,9 +31687,9 @@ function importSpecTree(archive, options) {
31055
31687
  return result;
31056
31688
  }
31057
31689
  function liveSpecDirFor(rootDir) {
31058
- const modern = path30.join(rootDir, ".wai");
31690
+ const modern = path31.join(rootDir, ".wai");
31059
31691
  if (fs17.existsSync(modern)) return modern;
31060
- const legacy = path30.join(rootDir, ".wairon");
31692
+ const legacy = path31.join(rootDir, ".wairon");
31061
31693
  if (fs17.existsSync(legacy)) return legacy;
31062
31694
  return null;
31063
31695
  }
@@ -31199,7 +31831,7 @@ function repairForeignStepFields(apply) {
31199
31831
 
31200
31832
  // src/core/approval.ts
31201
31833
  var crypto4 = __toESM(require("crypto"));
31202
- var path31 = __toESM(require("path"));
31834
+ var path32 = __toESM(require("path"));
31203
31835
 
31204
31836
  // src/models/lock.ts
31205
31837
  function describeApprover(who) {
@@ -31216,12 +31848,12 @@ function digest(content) {
31216
31848
  return crypto4.createHash("sha256").update(normalized, "utf8").digest("hex");
31217
31849
  }
31218
31850
  function currentSpecDigests(root = getProjectRoot()) {
31219
- const mountDirs = graph().nodes.filter((n) => n.namespace !== "").map((n) => `${path31.resolve(n.directory).split(path31.sep).join("/")}/`);
31851
+ const mountDirs = graph().nodes.filter((n) => n.namespace !== "").map((n) => `${path32.resolve(n.directory).split(path32.sep).join("/")}/`);
31220
31852
  const out = {};
31221
31853
  for (const [abs, content] of snapshotSpecFiles()) {
31222
- const normalized = path31.resolve(abs).split(path31.sep).join("/");
31854
+ const normalized = path32.resolve(abs).split(path32.sep).join("/");
31223
31855
  if (mountDirs.some((dir) => normalized.startsWith(dir))) continue;
31224
- out[path31.relative(root, abs).split(path31.sep).join("/")] = digest(content);
31856
+ out[path32.relative(root, abs).split(path32.sep).join("/")] = digest(content);
31225
31857
  }
31226
31858
  return out;
31227
31859
  }
@@ -31279,7 +31911,7 @@ function currentChildPins(mounts, root = getProjectRoot()) {
31279
31911
  const pins = {};
31280
31912
  for (const mount of mounts) {
31281
31913
  if (!mount.projectPath || mount.id.includes("::")) continue;
31282
- const child = readLockRecordAt(path31.resolve(root, mount.projectPath));
31914
+ const child = readLockRecordAt(path32.resolve(root, mount.projectPath));
31283
31915
  if (child) pins[mount.id] = pinOf(child.stateId);
31284
31916
  }
31285
31917
  return pins;
@@ -31296,7 +31928,7 @@ function movedChildren(mounts, root = getProjectRoot()) {
31296
31928
  return moved;
31297
31929
  }
31298
31930
  function memberMounts(root = getProjectRoot()) {
31299
- return graph().nodes.filter((n) => n.parent === "" && n.mountAlias !== void 0).map((n) => ({ id: n.mountAlias, projectPath: path31.relative(root, n.directory).split(path31.sep).join("/") }));
31931
+ return graph().nodes.filter((n) => n.parent === "" && n.mountAlias !== void 0).map((n) => ({ id: n.mountAlias, projectPath: path32.relative(root, n.directory).split(path32.sep).join("/") }));
31300
31932
  }
31301
31933
  function approvalVerdict() {
31302
31934
  const quiet = { text: "", drifted: false };
@@ -31384,7 +32016,7 @@ function localApprover() {
31384
32016
  // src/utils/ai-guide.ts
31385
32017
  var fs18 = __toESM(require("fs"));
31386
32018
  var os5 = __toESM(require("os"));
31387
- var path32 = __toESM(require("path"));
32019
+ var path33 = __toESM(require("path"));
31388
32020
 
31389
32021
  // src/core/stamp.ts
31390
32022
  function versionStamp() {
@@ -31444,13 +32076,13 @@ This project uses **wairon**. System specs live under \`.wai/specs/\` (L0 System
31444
32076
  * **Variants**: \`gateway\`, a Portal that authenticates, authorizes, validates or rate-limits before it dispatches. Logic is an Orchestrator, and \`dependencyClass: pure | read\` bounds what it may depend on (unset = a workflow). Specialist and Gateway are retired stereotypes.
31445
32077
  * Use \`owns\` for private member containment (exactly one hop) and \`dependsOn\` for collaborators. Never use generic suffixes like "Manager", "Helper", or "Utils".`;
31446
32078
  function globalGuideFilePath(targetType) {
31447
- if (targetType === "claude") return path32.join(process.env["CLAUDE_CONFIG_DIR"] || path32.join(os5.homedir(), ".claude"), "CLAUDE.md");
31448
- if (targetType === "gemini") return path32.join(process.env["GEMINI_CONFIG_DIR"] || path32.join(os5.homedir(), ".gemini"), "GEMINI.md");
32079
+ if (targetType === "claude") return path33.join(process.env["CLAUDE_CONFIG_DIR"] || path33.join(os5.homedir(), ".claude"), "CLAUDE.md");
32080
+ if (targetType === "gemini") return path33.join(process.env["GEMINI_CONFIG_DIR"] || path33.join(os5.homedir(), ".gemini"), "GEMINI.md");
31449
32081
  return null;
31450
32082
  }
31451
32083
  function localGuideFilePath(projectRoot, targetType) {
31452
- if (targetType === "claude") return path32.join(projectRoot, ".claude", "CLAUDE.md");
31453
- if (targetType === "gemini" || targetType === "agy") return path32.join(projectRoot, ".gemini", "GEMINI.md");
32084
+ if (targetType === "claude") return path33.join(projectRoot, ".claude", "CLAUDE.md");
32085
+ if (targetType === "gemini" || targetType === "agy") return path33.join(projectRoot, ".gemini", "GEMINI.md");
31454
32086
  return null;
31455
32087
  }
31456
32088
  function injectGuide(filePath, scope) {
@@ -31465,7 +32097,7 @@ ${GUIDE_MARKER_END}
31465
32097
  const existing = fs18.existsSync(filePath) ? fs18.readFileSync(filePath, "utf-8") : "";
31466
32098
  const stripped = stripGuideSection(existing);
31467
32099
  const newContent = stripped.trimEnd() + section;
31468
- fs18.mkdirSync(path32.dirname(filePath), { recursive: true });
32100
+ fs18.mkdirSync(path33.dirname(filePath), { recursive: true });
31469
32101
  fs18.writeFileSync(filePath, newContent, "utf-8");
31470
32102
  }
31471
32103
  function stripGuideSection(content) {
@@ -31476,7 +32108,7 @@ function stripGuideSection(content) {
31476
32108
  }
31477
32109
  function writeRootGuideDelegator(projectRoot, targetType) {
31478
32110
  if (targetType === "claude") {
31479
- const filePath = path32.join(projectRoot, "CLAUDE.md");
32111
+ const filePath = path33.join(projectRoot, "CLAUDE.md");
31480
32112
  const content = `@.claude/CLAUDE.md
31481
32113
 
31482
32114
  # Wairon SDD Project
@@ -31491,7 +32123,7 @@ the \`wairon\` CLI is the human developer's tool, not yours.
31491
32123
  `;
31492
32124
  fs18.writeFileSync(filePath, content, "utf-8");
31493
32125
  } else if (targetType === "gemini" || targetType === "agy") {
31494
- const filePath = path32.join(projectRoot, "GEMINI.md");
32126
+ const filePath = path33.join(projectRoot, "GEMINI.md");
31495
32127
  const content = `# Wairon SDD Project
31496
32128
  ${GUIDE_MARKER_START}
31497
32129
  ${versionStamp()}
@@ -31500,7 +32132,7 @@ ${GUIDE_MARKER_END}
31500
32132
  `;
31501
32133
  fs18.writeFileSync(filePath, content, "utf-8");
31502
32134
  } else if (targetType === "cursor") {
31503
- const filePath = path32.join(projectRoot, ".cursorrules");
32135
+ const filePath = path33.join(projectRoot, ".cursorrules");
31504
32136
  const content = `# Wairon SDD Project
31505
32137
 
31506
32138
  This project uses the Wairon Spec-Driven Development (SDD) framework.
@@ -31509,8 +32141,8 @@ Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
31509
32141
  `;
31510
32142
  fs18.writeFileSync(filePath, content, "utf-8");
31511
32143
  } else if (targetType === "copilot") {
31512
- const filePath = path32.join(projectRoot, ".github", "copilot-instructions.md");
31513
- fs18.mkdirSync(path32.dirname(filePath), { recursive: true });
32144
+ const filePath = path33.join(projectRoot, ".github", "copilot-instructions.md");
32145
+ fs18.mkdirSync(path33.dirname(filePath), { recursive: true });
31514
32146
  const content = `# Wairon SDD Project
31515
32147
 
31516
32148
  This project uses the Wairon Spec-Driven Development (SDD) framework.
@@ -31519,7 +32151,7 @@ Refer to the prompts in [.github/prompts/](.github/prompts/) for instructions.
31519
32151
  `;
31520
32152
  fs18.writeFileSync(filePath, content, "utf-8");
31521
32153
  } else if (targetType === "codex") {
31522
- const filePath = path32.join(projectRoot, ".codexrules");
32154
+ const filePath = path33.join(projectRoot, ".codexrules");
31523
32155
  const content = `# Wairon SDD Project
31524
32156
 
31525
32157
  Refer to [.codex/agents/](.codex/agents/) for full instructions.
@@ -31676,14 +32308,14 @@ function syncContextFiles() {
31676
32308
  }
31677
32309
 
31678
32310
  // src/core/status.ts
31679
- var path33 = __toESM(require("path"));
31680
- var asWritten = (text2) => text2;
32311
+ var path34 = __toESM(require("path"));
32312
+ var asWritten = (text3) => text3;
31681
32313
  function fillDecor(decor) {
31682
32314
  return {
31683
32315
  structure: decor?.structure ?? asWritten,
31684
32316
  emphasis: decor?.emphasis ?? asWritten,
31685
- layer: decor?.layer ?? ((_kind, text2) => text2),
31686
- score: decor?.score ?? ((_pct, text2) => text2),
32317
+ layer: decor?.layer ?? ((_kind, text3) => text3),
32318
+ score: decor?.score ?? ((_pct, text3) => text3),
31687
32319
  draft: decor?.draft ?? asWritten,
31688
32320
  present: decor?.present ?? asWritten,
31689
32321
  missing: decor?.missing ?? asWritten
@@ -31765,7 +32397,7 @@ function getStatusReport(options = {}, decor) {
31765
32397
  const sourceExists = (specId, file) => {
31766
32398
  const owner = ownerOf3(specId);
31767
32399
  const dir = owner === "" ? void 0 : family.nodes.find((n) => n.namespace === owner)?.directory;
31768
- return pathExists(dir ? path33.resolve(dir, file) : fromProjectRoot(file));
32400
+ return pathExists(dir ? path34.resolve(dir, file) : fromProjectRoot(file));
31769
32401
  };
31770
32402
  const holdsShown = (ns) => subsystems.some((s) => ownerOf3(s.id) === ns) || family.nodes.some((n) => n.parent === ns && n.namespace !== ns && holdsShown(n.namespace));
31771
32403
  const renderSubsystem = (sub, label2, indent, isLastSub) => {
@@ -31929,7 +32561,7 @@ var logger = {
31929
32561
  };
31930
32562
 
31931
32563
  // src/core/skills.ts
31932
- var path34 = __toESM(require("path"));
32564
+ var path35 = __toESM(require("path"));
31933
32565
  var fs19 = __toESM(require("fs"));
31934
32566
 
31935
32567
  // src/core/instructions.ts
@@ -32085,10 +32717,10 @@ function readFrontmatter(raw, fallbackName) {
32085
32717
  return { name: field("name") || fallbackName, description: field("description") };
32086
32718
  }
32087
32719
  function builtinSkillsDir() {
32088
- return path34.resolve(__dirname, "..", "templates", "skills");
32720
+ return path35.resolve(__dirname, "..", "templates", "skills");
32089
32721
  }
32090
32722
  function skillTemplatePath(name) {
32091
- return path34.join(builtinSkillsDir(), `${name}.md`);
32723
+ return path35.join(builtinSkillsDir(), `${name}.md`);
32092
32724
  }
32093
32725
  function activeTargetTypes2() {
32094
32726
  const config = loadProjectConfig();
@@ -32156,7 +32788,7 @@ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
32156
32788
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
32157
32789
  var import_zod11 = require("zod");
32158
32790
  var import_types4 = require("@modelcontextprotocol/sdk/types.js");
32159
- var path36 = __toESM(require("path"));
32791
+ var path37 = __toESM(require("path"));
32160
32792
  var crypto5 = __toESM(require("crypto"));
32161
32793
 
32162
32794
  // src/core/adapters/authoring-core.ts
@@ -32551,7 +33183,7 @@ function moveMethods2(from, to, methods, dryRun) {
32551
33183
 
32552
33184
  // src/mcp/build.ts
32553
33185
  var fs20 = __toESM(require("fs"));
32554
- var path35 = __toESM(require("path"));
33186
+ var path36 = __toESM(require("path"));
32555
33187
  var import_child_process2 = require("child_process");
32556
33188
  function captureBuildStamp(entryPath) {
32557
33189
  try {
@@ -32595,21 +33227,21 @@ function readBuildFingerprint(stamp) {
32595
33227
  return printed;
32596
33228
  }
32597
33229
  function fingerprintModuleFor(entryPath) {
32598
- let dir = path35.dirname(entryPath);
33230
+ let dir = path36.dirname(entryPath);
32599
33231
  for (; ; ) {
32600
- const manifest = path35.join(dir, "package.json");
33232
+ const manifest = path36.join(dir, "package.json");
32601
33233
  if (fs20.existsSync(manifest)) {
32602
33234
  try {
32603
33235
  const pkg = JSON.parse(fs20.readFileSync(manifest, "utf8"));
32604
- const inDist = path35.relative(path35.join(dir, "dist"), entryPath);
32605
- if (pkg.name === "@wairon/cli" && pkg.main && !inDist.startsWith("..") && !path35.isAbsolute(inDist)) {
32606
- return path35.resolve(dir, pkg.main);
33236
+ const inDist = path36.relative(path36.join(dir, "dist"), entryPath);
33237
+ if (pkg.name === "@wairon/cli" && pkg.main && !inDist.startsWith("..") && !path36.isAbsolute(inDist)) {
33238
+ return path36.resolve(dir, pkg.main);
32607
33239
  }
32608
33240
  } catch {
32609
33241
  }
32610
33242
  return entryPath;
32611
33243
  }
32612
- const parent = path35.dirname(dir);
33244
+ const parent = path36.dirname(dir);
32613
33245
  if (parent === dir) return entryPath;
32614
33246
  dir = parent;
32615
33247
  }
@@ -32635,7 +33267,7 @@ function statusFamilyContext() {
32635
33267
  const members = listDirectChainedSubprojects(root);
32636
33268
  if (members.length > 0) {
32637
33269
  const described = members.map((m) => {
32638
- const rel2 = path36.relative(root, m.dir).split(path36.sep).join("/") || ".";
33270
+ const rel2 = path37.relative(root, m.dir).split(path37.sep).join("/") || ".";
32639
33271
  return `${m.alias} (${rel2}${m.form === "mount" ? ", legacy L1 mount" : ""})`;
32640
33272
  });
32641
33273
  lines.push(`Family: member projects declared here \u2014 ${described.join(", ")}.`);
@@ -32660,7 +33292,7 @@ function qualifiedComponentId(id) {
32660
33292
  return chained.length === 1 ? chained[0] : id;
32661
33293
  }
32662
33294
  function publicInterfacesReplacement(subsystem, stored, list) {
32663
- const identity = (pi) => {
33295
+ const identity2 = (pi) => {
32664
33296
  if (pi.from || pi.typeDef) {
32665
33297
  const item = pi.typeDef ? `type ${pi.typeDef}` : pi.component ?? "*";
32666
33298
  return `${pi.from ? `${pi.from}:` : ""}${item}${pi.interface ? `.${pi.interface}` : ""}${pi.as ? ` as ${pi.as}` : ""}`;
@@ -32668,7 +33300,7 @@ function publicInterfacesReplacement(subsystem, stored, list) {
32668
33300
  return pi.component ? `${pi.component}${pi.interface ? `.${pi.interface}` : ""}` : `${pi.type} ${pi.details}`;
32669
33301
  };
32670
33302
  const restated = /* @__PURE__ */ new Map();
32671
- for (const pi of list) restated.set(identity(pi), (restated.get(identity(pi)) ?? 0) + 1);
33303
+ for (const pi of list) restated.set(identity2(pi), (restated.get(identity2(pi)) ?? 0) + 1);
32672
33304
  const twice = [...restated].filter(([, n]) => n > 1).map(([key]) => `"${key}"`);
32673
33305
  if (twice.length) {
32674
33306
  return `Refusing to set the public interfaces of subsystem "${subsystem}": the list names ${twice.join(", ")} more than once. An entry is identified by the component and interface it is bound to \u2014 or, not yet bound, by its type and details \u2014 so two entries sharing one are the same entry stated twice. Drop the duplicate. Nothing was written.`;
@@ -32676,24 +33308,24 @@ function publicInterfacesReplacement(subsystem, stored, list) {
32676
33308
  const seen = /* @__PURE__ */ new Map();
32677
33309
  const deletes = [];
32678
33310
  for (const pi of stored) {
32679
- const key = identity(pi);
33311
+ const key = identity2(pi);
32680
33312
  seen.set(key, (seen.get(key) ?? 0) + 1);
32681
33313
  if (seen.get(key) > (restated.get(key) ?? 0)) {
32682
33314
  deletes.push(pi.from || pi.typeDef ? { from: pi.from, component: pi.component, interface: pi.interface, typeDef: pi.typeDef, as: pi.as, action: "delete" } : pi.component ? { component: pi.component, interface: pi.interface, action: "delete" } : { type: pi.type, details: pi.details, action: "delete" });
32683
33315
  }
32684
33316
  }
32685
33317
  const restatedEntries = list.map((pi) => {
32686
- const prev = stored.find((s) => identity(s) === identity(pi));
33318
+ const prev = stored.find((s) => identity2(s) === identity2(pi));
32687
33319
  const dropped = prev ? Object.keys(prev).filter((f) => prev[f] !== void 0 && pi[f] === void 0) : [];
32688
33320
  return dropped.length ? { ...pi, unset: dropped } : pi;
32689
33321
  });
32690
33322
  return { publicInterfaces: [...deletes, ...restatedEntries] };
32691
33323
  }
32692
- function text(content) {
33324
+ function text2(content) {
32693
33325
  return { content: [{ type: "text", text: content }] };
32694
33326
  }
32695
33327
  function json(value) {
32696
- return text(JSON.stringify(value, null, 2));
33328
+ return text2(JSON.stringify(value, null, 2));
32697
33329
  }
32698
33330
  function errText(message) {
32699
33331
  return { content: [{ type: "text", text: `Error: ${message.replace(/^(?:\s*Error:\s*)+/, "")}` }], isError: true };
@@ -32869,6 +33501,90 @@ var validationIssueOutput = {
32869
33501
  "In a family run: the key of the project whose gate the finding belongs to ('' for the family root)."
32870
33502
  )
32871
33503
  };
33504
+ var memberCreationOutput = {
33505
+ configCreated: import_zod11.z.boolean().describe("Whether this call created the member's configuration; only then were the declaring project's required packs scaffolded."),
33506
+ adopted: import_zod11.z.array(import_zod11.z.object({
33507
+ name: import_zod11.z.string(),
33508
+ version: import_zod11.z.string().optional(),
33509
+ integrity: import_zod11.z.string().optional(),
33510
+ source: import_zod11.z.string().optional(),
33511
+ bundle: import_zod11.z.boolean().optional()
33512
+ })).describe("The selections written into the new member's extensions.packs, each pinned to the highest installed version its requirement admits."),
33513
+ unadopted: import_zod11.z.array(import_zod11.z.object({
33514
+ pack: import_zod11.z.string(),
33515
+ version: import_zod11.z.string(),
33516
+ profile: import_zod11.z.string().optional()
33517
+ })).describe("Requirements no installed version satisfies: nothing was written for them, and the next family run reports each as POLICY_NOT_ADOPTED."),
33518
+ projectType: import_zod11.z.string().optional().describe("The profile written as the new member's projectType, when exactly one adopted requirement names one."),
33519
+ ...staleServerOutput
33520
+ };
33521
+ function describeMemberPacks(creation) {
33522
+ if (!creation.configCreated) return "";
33523
+ const parts = [];
33524
+ if (creation.adopted.length > 0) {
33525
+ parts.push(` Applied the required pack(s) ${creation.adopted.map((s) => `${s.name}@${s.version}`).join(", ")} to its new configuration (an unattended write: no impact report was shown).`);
33526
+ }
33527
+ if (creation.projectType) parts.push(` Set its projectType to "${creation.projectType}".`);
33528
+ if (creation.unadopted.length > 0) {
33529
+ parts.push(` No installed version satisfies ${creation.unadopted.map((r) => `"${r.pack}" ${r.version}`).join(", ")}: nothing was written for ${creation.unadopted.length === 1 ? "it" : "them"}, and the family run reports POLICY_NOT_ADOPTED until the member selects ${creation.unadopted.length === 1 ? "it" : "them"} (\`wairon pack install <source>\`, then \`wairon pack use <name>@<version> --pin\` at the member's root).`);
33530
+ }
33531
+ return parts.join("");
33532
+ }
33533
+ var doctrineChangeOutput = import_zod11.z.object({
33534
+ axis: import_zod11.z.enum(["rule", "concept"]),
33535
+ change: import_zod11.z.string().describe("loosened | raised | off | added | depth | inert | redefined | removed | discouraged | licensed | gated \u2014 stated, never judged."),
33536
+ subject: import_zod11.z.string(),
33537
+ profile: import_zod11.z.string().optional(),
33538
+ from: import_zod11.z.string().optional(),
33539
+ to: import_zod11.z.string().optional(),
33540
+ reason: import_zod11.z.string().optional()
33541
+ });
33542
+ var impactTotalsOutput = import_zod11.z.object({
33543
+ key: import_zod11.z.string(),
33544
+ id: import_zod11.z.string().optional(),
33545
+ directory: import_zod11.z.string(),
33546
+ valid: import_zod11.z.boolean(),
33547
+ errors: import_zod11.z.number(),
33548
+ warnings: import_zod11.z.number(),
33549
+ notices: import_zod11.z.number()
33550
+ });
33551
+ var packImpactOutput = {
33552
+ pack: import_zod11.z.string().describe("The pack measured."),
33553
+ version: import_zod11.z.string().optional().describe("The version the candidate resolves to; absent for an unversioned pack."),
33554
+ replaces: import_zod11.z.string().optional().describe("The version applied now under the same name, when the selection would replace it."),
33555
+ direction: import_zod11.z.enum(["apply", "remove"]).describe("remove when the pack is already applied exactly as asked: the findings then read as what the pack accounts for now."),
33556
+ doctrine: import_zod11.z.array(doctrineChangeOutput).describe("The pack's changes against wairon's defaults. Nothing in it is a finding."),
33557
+ previousDoctrine: import_zod11.z.array(doctrineChangeOutput).optional().describe("On an update: the replaced version's changes against the same defaults."),
33558
+ governing: import_zod11.z.array(import_zod11.z.string()).describe("The pack's profiles that would govern something in this project."),
33559
+ findings: import_zod11.z.object({
33560
+ introduced: import_zod11.z.array(import_zod11.z.object(validationIssueOutput)),
33561
+ resolved: import_zod11.z.array(import_zod11.z.object(validationIssueOutput)),
33562
+ regraded: import_zod11.z.array(import_zod11.z.object({ finding: import_zod11.z.object(validationIssueOutput), from: import_zod11.z.string() }))
33563
+ }).describe("The owner's-gate findings that differ between the current and the candidate configuration."),
33564
+ before: impactTotalsOutput.describe("The current configuration's totals."),
33565
+ after: impactTotalsOutput.describe("The candidate configuration's totals."),
33566
+ ...staleServerOutput
33567
+ };
33568
+ function renderPackImpact(impact) {
33569
+ const lines = [];
33570
+ const removal = impact.direction === "remove";
33571
+ lines.push(`Pack impact: ${impact.pack}${impact.version ? ` v${impact.version}` : ""}${impact.replaces ? ` (replaces v${impact.replaces})` : ""} \u2014 ${removal ? "already applied exactly as asked; measured as removed, so the findings are what the pack accounts for now" : "measured as applied"}. Nothing was written.`);
33572
+ lines.push("", "Doctrine against wairon's defaults (stated, never judged):");
33573
+ if (impact.doctrine.length === 0) lines.push(" (no changes)");
33574
+ for (const c of impact.doctrine) {
33575
+ const range = c.from !== void 0 || c.to !== void 0 ? ` (${c.from ?? "-"} -> ${c.to ?? "-"})` : "";
33576
+ lines.push(` ${c.profile ? `[${c.profile}] ` : ""}${c.axis} ${c.change}: ${c.subject}${range}${c.reason ? ` \u2014 ${c.reason}` : ""}`);
33577
+ }
33578
+ lines.push("", `Governing here: ${impact.governing.length > 0 ? impact.governing.join(", ") : "none of its profiles"}`);
33579
+ const f = impact.findings;
33580
+ lines.push("", `Findings: ${f.introduced.length} introduced, ${f.resolved.length} resolved, ${f.regraded.length} regraded.`);
33581
+ for (const i of f.introduced) lines.push(` + [${i.severity}] ${i.code}${i.specId ? ` (${i.specId})` : ""}: ${i.message}`);
33582
+ for (const i of f.resolved) lines.push(` - [${i.severity}] ${i.code}${i.specId ? ` (${i.specId})` : ""}: ${i.message}`);
33583
+ for (const r of f.regraded) lines.push(` ~ [${r.from} -> ${r.finding.severity}] ${r.finding.code}${r.finding.specId ? ` (${r.finding.specId})` : ""}`);
33584
+ const totals2 = (v) => `${v.errors} error(s), ${v.warnings} warning(s), ${v.notices} notice(s)`;
33585
+ lines.push("", `Before: ${totals2(impact.before)}. After: ${totals2(impact.after)}.`);
33586
+ return lines.join(String.fromCharCode(10));
33587
+ }
32872
33588
  var validateTreeOutput = {
32873
33589
  valid: import_zod11.z.boolean().describe("False when the tree holds at least one error; warnings and notices never make it false."),
32874
33590
  errors: import_zod11.z.array(import_zod11.z.object(validationIssueOutput)).describe("Every finding of severity error."),
@@ -33451,22 +34167,53 @@ ${renderChangeReport(report)}`,
33451
34167
  server,
33452
34168
  "sdd_add_member",
33453
34169
  {
33454
- description: "Create a member of the bound project: scaffold the member project at the path (its project.yaml declaring the alias as its project id, and its L0), each part only when absent, and declare it in the bound project's project.yaml `members` with the description. Nothing is written into the bound project's spec tree: a member is reached only as `alias::name` through its own L0 exports. Idempotent \u2014 the same call again writes nothing; a different member under the alias is refused.",
34170
+ description: "Create a member of the bound project: scaffold the member project at the path (its project.yaml declaring the alias as its project id, and its L0), each part only when absent, and declare it in the bound project's project.yaml `members` with the description. A newly created member configuration also selects the bound project's required packs (composition.requirePolicies), once, each pinned to the highest installed version its range admits; the answer states what was applied and which requirements nothing installed satisfies. Nothing is written into the bound project's spec tree: a member is reached only as `alias::name` through its own L0 exports. Idempotent \u2014 the same call again writes nothing; a different member under the alias is refused.",
33455
34171
  inputSchema: {
33456
34172
  alias: import_zod11.z.string().describe("The member's alias and new project id ([a-z0-9-_]+)"),
33457
34173
  path: import_zod11.z.string().describe("The member's root, relative to the bound project"),
33458
34174
  description: import_zod11.z.string().optional().describe("What the member is to this project; also the vision of a bootstrapped L0")
33459
- }
34175
+ },
34176
+ outputSchema: memberCreationOutput
33460
34177
  },
33461
34178
  ({ alias, path: memberPath, description }) => {
33462
34179
  try {
33463
- createMember(alias, memberPath, description);
33464
- return text(`Added member "${alias}" at ${memberPath} (declared in project.yaml \`members\`). Design it from its own root and export what others consume from its L0; reference it here as ${alias}::<name>.`);
34180
+ const creation = createMember(alias, memberPath, description);
34181
+ return structured(
34182
+ `Added member "${alias}" at ${memberPath} (declared in project.yaml \`members\`).${describeMemberPacks(creation)} Design it from its own root and export what others consume from its L0; reference it here as ${alias}::<name>.`,
34183
+ creation
34184
+ );
33465
34185
  } catch (e) {
33466
34186
  return errText(String(e));
33467
34187
  }
33468
34188
  }
33469
34189
  );
34190
+ if (!options.hostedTools) {
34191
+ reg(
34192
+ server,
34193
+ "sdd_pack_impact",
34194
+ {
34195
+ description: "What selecting (or updating to) an installed pack would change on the bound project, writing nothing \u2014 the pack's doctrine against wairon's defaults (rules loosened, raised, turned off or added; profiles, stereotype fencing, edge licenses, patterns, guarantees and language tables added, redefined or removed), which of its profiles would govern, and the findings that change here. A pack the project already applies exactly as asked is measured as removed, so the findings read as what it accounts for now. Nothing in it is a finding: packs exist to adjust wairon's checks, and a loosening is stated like any other change. Apply a pack with `wairon pack use`.",
34196
+ inputSchema: {
34197
+ pack: import_zod11.z.string().describe("The pack name"),
34198
+ version: import_zod11.z.string().optional().describe("An exact version; omitted, the latest installed")
34199
+ },
34200
+ outputSchema: packImpactOutput
34201
+ },
34202
+ ({ pack, version }) => {
34203
+ try {
34204
+ const config = loadProjectConfig();
34205
+ if (!config) throw new ProjectNotInitializedError();
34206
+ const stored = (config.extensions?.packs ?? []).find((e) => typeof e !== "string" && e.name === pack);
34207
+ const exact = stored !== void 0 && typeof stored !== "string" && (stored.version ?? "") === (version ?? "");
34208
+ const candidate = exact ? { entry: stored, remove: true } : { entry: { name: pack, ...version ? { version } : {} } };
34209
+ const impact = measurePackImpact(candidate);
34210
+ return structured(renderPackImpact(impact), impact);
34211
+ } catch (e) {
34212
+ return errMessage(e);
34213
+ }
34214
+ }
34215
+ );
34216
+ }
33470
34217
  reg(
33471
34218
  server,
33472
34219
  "sdd_move_member",
@@ -33480,7 +34227,7 @@ ${renderChangeReport(report)}`,
33480
34227
  ({ alias, newPath }) => {
33481
34228
  try {
33482
34229
  moveMember(alias, newPath);
33483
- return text(`Moved member "${alias}" to: ${newPath}`);
34230
+ return text2(`Moved member "${alias}" to: ${newPath}`);
33484
34231
  } catch (e) {
33485
34232
  return errText(String(e));
33486
34233
  }
@@ -33499,7 +34246,7 @@ ${renderChangeReport(report)}`,
33499
34246
  ({ subsystem, path: memberPath }) => {
33500
34247
  try {
33501
34248
  externalizeSubsystem(subsystem, memberPath);
33502
- return text(`Externalized subsystem "${subsystem}" into the member project at ${memberPath}. Move its source code there, run \`wairon doctor --fix\` for the exports either side now needs, and re-validate.`);
34249
+ return text2(`Externalized subsystem "${subsystem}" into the member project at ${memberPath}. Move its source code there, run \`wairon doctor --fix\` for the exports either side now needs, and re-validate.`);
33503
34250
  } catch (e) {
33504
34251
  return errText(String(e));
33505
34252
  }
@@ -33517,7 +34264,7 @@ ${renderChangeReport(report)}`,
33517
34264
  ({ alias }) => {
33518
34265
  try {
33519
34266
  internalizeMember(alias);
33520
- return text(`Internalized member "${alias}" into this project (its .wai project removed).`);
34267
+ return text2(`Internalized member "${alias}" into this project (its .wai project removed).`);
33521
34268
  } catch (e) {
33522
34269
  return errText(String(e));
33523
34270
  }
@@ -34044,7 +34791,7 @@ ${renderChangeReport(report)}`,
34044
34791
  ({ kind, id, methods }) => {
34045
34792
  try {
34046
34793
  let result = null;
34047
- let partial = null;
34794
+ let partial2 = null;
34048
34795
  switch (kind) {
34049
34796
  case "system":
34050
34797
  result = loadSystemSpec();
@@ -34081,7 +34828,7 @@ ${renderChangeReport(report)}`,
34081
34828
  );
34082
34829
  }
34083
34830
  const kept = declared.filter((m) => methods.includes(String(m?.name)));
34084
- partial = {
34831
+ partial2 = {
34085
34832
  shown: kept.map((m) => String(m?.name)),
34086
34833
  omitted: names.length - kept.length,
34087
34834
  warning: `PARTIAL: ${names.length - kept.length} of this spec's ${names.length} methods are not in this answer. Never re-author from it \u2014 sdd_define_interface and sdd_write_narrative REPLACE the method list, so every method missing here would be removed from the spec.`
@@ -34091,14 +34838,14 @@ ${renderChangeReport(report)}`,
34091
34838
  const guidance = kind === "component" ? resolveComponentVariantGuidance(result) : null;
34092
34839
  const folded = {
34093
34840
  ...result,
34094
- ...partial ? { partialResult: partial } : {},
34841
+ ...partial2 ? { partialResult: partial2 } : {},
34095
34842
  ...guidance ? { variantGuidance: guidance } : {}
34096
34843
  };
34097
34844
  return structured(JSON.stringify(folded, null, 2), {
34098
34845
  kind,
34099
34846
  id,
34100
34847
  spec: result,
34101
- ...partial ? { partialResult: partial } : {},
34848
+ ...partial2 ? { partialResult: partial2 } : {},
34102
34849
  ...guidance ? { variantGuidance: guidance } : {}
34103
34850
  });
34104
34851
  } catch (e) {
@@ -34169,7 +34916,7 @@ ${testsBlock}` : ""}`, deletion);
34169
34916
  });
34170
34917
  const family = statusFamilyContext();
34171
34918
  const verdict = approvalVerdict();
34172
- return text(`${family}${report.text}${verdict.text}`);
34919
+ return text2(`${family}${report.text}${verdict.text}`);
34173
34920
  } catch (e) {
34174
34921
  return errText(String(e));
34175
34922
  }
@@ -34299,6 +35046,13 @@ function advertiseHostedTools(server, options) {
34299
35046
  content: import_zod11.z.string().describe("The declarative pack YAML content")
34300
35047
  }
34301
35048
  }, hostedStub);
35049
+ reg(server, "sdd_host_pack_impact", {
35050
+ description: "Hosted project ops: measure, writing nothing, what installing a DECLARATIVE pack (content given) or adopting the server-global pack of that name (content omitted) would change on the BOUND project \u2014 the pack's doctrine against wairon's defaults (rules loosened, raised, turned off or added; profiles and concepts added, redefined or removed), which of its profiles would govern, and the findings that change. Show it to your human before sdd_host_pack_install, which applies without it. Nothing in it is a finding: packs exist to adjust wairon's checks. Requires project:read over the project.",
35051
+ inputSchema: {
35052
+ name: import_zod11.z.string().describe("Pack name (letters, digits, dot, underscore, hyphen); the server-global pack to adopt when content is omitted"),
35053
+ content: import_zod11.z.string().optional().describe("The declarative pack YAML an install would upload; omitted, the server-global pack an adoption would vendor")
35054
+ }
35055
+ }, hostedStub);
34302
35056
  reg(server, "sdd_host_policy_evaluate", {
34303
35057
  description: "Hosted project ops: evaluate the BOUND project's packs and profile selection against the instance pack policy \u2014 compliance without side effects (requires project:write over it).",
34304
35058
  inputSchema: {}
@@ -34365,6 +35119,7 @@ function loadProjectConfig3() {
34365
35119
  ComplexityRuleConfigSchema,
34366
35120
  ComponentSpecSchema,
34367
35121
  ComponentTypeSchema,
35122
+ CompositionConfigSchema,
34368
35123
  ConfigValidationError,
34369
35124
  ConformanceRuleConfigSchema,
34370
35125
  ConformanceTierSchema,
@@ -34419,6 +35174,7 @@ function loadProjectConfig3() {
34419
35174
  PROJECT_ID_RE,
34420
35175
  PROJECT_KINDS,
34421
35176
  PUBLIC_NAME_RE,
35177
+ PackRequirementSchema,
34422
35178
  PackSelectionSchema,
34423
35179
  ParallelBranchSchema,
34424
35180
  PathsConfigSchema,
@@ -34474,6 +35230,7 @@ function loadProjectConfig3() {
34474
35230
  WaironError,
34475
35231
  activeTargetTypes,
34476
35232
  addDomain,
35233
+ admits,
34477
35234
  aiDir,
34478
35235
  aiDirAt,
34479
35236
  aiPathsAt,
@@ -34596,6 +35353,7 @@ function loadProjectConfig3() {
34596
35353
  loadComponentSpecs,
34597
35354
  loadExtensionPacks,
34598
35355
  loadExtensions,
35356
+ loadExtensionsFor,
34599
35357
  loadImplementationSpec,
34600
35358
  loadImplementationSpecs,
34601
35359
  loadInterfaceSpec,
@@ -34635,7 +35393,9 @@ function loadProjectConfig3() {
34635
35393
  packDirEntry,
34636
35394
  packEntryLabel,
34637
35395
  packEntryRef,
35396
+ packManifest,
34638
35397
  packStoreDir,
35398
+ parseDeclarativePack,
34639
35399
  parseDeclaredCall,
34640
35400
  parseYaml,
34641
35401
  passesIntentFloor,
@@ -34648,6 +35408,7 @@ function loadProjectConfig3() {
34648
35408
  projectIdentity,
34649
35409
  provisionProject,
34650
35410
  qualifiedTypeId,
35411
+ rangeProblem,
34651
35412
  readFileOrNull,
34652
35413
  readJsonFile,
34653
35414
  readLockRecord,
@@ -34664,6 +35425,7 @@ function loadProjectConfig3() {
34664
35425
  renameMethod,
34665
35426
  renderDiagram,
34666
35427
  repairForeignStepFields,
35428
+ requiredPolicies,
34667
35429
  resolveAgentTopology,
34668
35430
  resolveBudget,
34669
35431
  resolveChainingParent,
@@ -34708,6 +35470,7 @@ function loadProjectConfig3() {
34708
35470
  upsertPackSelection,
34709
35471
  validateFamily,
34710
35472
  validateProject,
35473
+ withPack,
34711
35474
  writeFile,
34712
35475
  writeFileIfChanged,
34713
35476
  writeJsonFile,