@wairon/cli 5.1.1-dev.71 → 5.1.1-dev.73

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
@@ -2497,7 +2497,7 @@ var require_dist = __commonJS({
2497
2497
  scaffoldPack: () => scaffoldPack2
2498
2498
  });
2499
2499
  module2.exports = __toCommonJS2(src_exports2);
2500
- var path28 = __toESM2(require("path"));
2500
+ var path29 = __toESM2(require("path"));
2501
2501
  var import_js_yaml3 = require("js-yaml");
2502
2502
  var import_js_yaml = require("js-yaml");
2503
2503
  var import_crypto = require("crypto");
@@ -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 fs18 = __toESM2(require("fs"));
3064
- var path29 = __toESM2(require("path"));
3064
+ var path30 = __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 = path29.join(destDir, file.path);
3103
- fs18.mkdirSync(path29.dirname(absolute), { recursive: true });
3102
+ const absolute = path30.join(destDir, file.path);
3103
+ fs18.mkdirSync(path30.dirname(absolute), { recursive: true });
3104
3104
  fs18.writeFileSync(absolute, file.contents);
3105
3105
  }
3106
3106
  }
@@ -3113,10 +3113,10 @@ var require_dist = __commonJS({
3113
3113
  for (const entry of fs18.readdirSync(current2, { withFileTypes: true })) {
3114
3114
  if (entry.isDirectory()) {
3115
3115
  if (SKIP_DIRS.has(entry.name)) continue;
3116
- walkPackDir(root, path29.join(current2, entry.name), out);
3116
+ walkPackDir(root, path30.join(current2, entry.name), out);
3117
3117
  } else if (entry.isFile()) {
3118
- const absolute = path29.join(current2, entry.name);
3119
- const relative22 = path29.relative(root, absolute).split(path29.sep).join("/");
3118
+ const absolute = path30.join(current2, entry.name);
3119
+ const relative22 = path30.relative(root, absolute).split(path30.sep).join("/");
3120
3120
  out.push({ path: relative22, contents: fs18.readFileSync(absolute) });
3121
3121
  }
3122
3122
  }
@@ -3167,7 +3167,7 @@ var require_dist = __commonJS({
3167
3167
  function scaffoldPack(request) {
3168
3168
  const files = render(request);
3169
3169
  writeTree(request.targetDir, files);
3170
- return files.map((file) => path28.join(request.targetDir, file.path));
3170
+ return files.map((file) => path29.join(request.targetDir, file.path));
3171
3171
  }
3172
3172
  function buildPack(sourceDir) {
3173
3173
  const files = readPackDir(sourceDir);
@@ -3224,7 +3224,7 @@ var require_dist = __commonJS({
3224
3224
  }
3225
3225
  const manifest = parseManifest(decode(envelope.contents));
3226
3226
  verifyIntegrity2(manifest, files);
3227
- const directory = path28.resolve(destDir);
3227
+ const directory = path29.resolve(destDir);
3228
3228
  writeTree(directory, files);
3229
3229
  const result = {
3230
3230
  directory,
@@ -3302,7 +3302,7 @@ var require_dist = __commonJS({
3302
3302
  }
3303
3303
  const manifest = parseTreeManifest(decode(envelope.contents));
3304
3304
  verifyTreeIntegrity(manifest, files);
3305
- const directory = path28.resolve(destDir);
3305
+ const directory = path29.resolve(destDir);
3306
3306
  writeTree(directory, files);
3307
3307
  const result = {
3308
3308
  destDir: directory,
@@ -3317,7 +3317,7 @@ var require_dist = __commonJS({
3317
3317
  return normalized === "." ? ".wai/" : `${normalized}/.wai/`;
3318
3318
  }
3319
3319
  function normalizeRootPath(relativePath) {
3320
- const posix2 = relativePath.split(path28.sep).join("/").replace(/^\.\//, "").replace(/\/+$/, "");
3320
+ const posix2 = relativePath.split(path29.sep).join("/").replace(/^\.\//, "").replace(/\/+$/, "");
3321
3321
  return posix2 === "" || posix2 === "." ? "." : posix2;
3322
3322
  }
3323
3323
  function slugify(projectName) {
@@ -3511,13 +3511,17 @@ __export(src_exports, {
3511
3511
  ambiguityMessage: () => ambiguityMessage,
3512
3512
  approvalRecord: () => approvalRecord,
3513
3513
  approvalVerdict: () => approvalVerdict,
3514
+ assertContainedProjectPath: () => assertContainedProjectPath,
3514
3515
  assertProjectInitialized: () => assertProjectInitialized,
3515
3516
  backendCommand: () => backendCommand,
3517
+ backfillChainedSubprojectConfigs: () => backfillChainedSubprojectConfigs,
3516
3518
  buildCanvasDataModel: () => buildCanvasDataModel,
3517
3519
  buildProjectGraph: () => buildProjectGraph,
3518
3520
  buildRuleContext: () => buildRuleContext,
3519
3521
  callSitesOf: () => callSitesOf,
3520
3522
  captureApprovedSpecs: () => captureApprovedSpecs,
3523
+ checkDeclarativePack: () => checkDeclarativePack,
3524
+ clearLoaderIssues: () => clearLoaderIssues,
3521
3525
  cognitiveScore: () => cognitiveScore,
3522
3526
  complexityLevel: () => complexityLevel,
3523
3527
  composeAgentBrief: () => composeAgentBrief,
@@ -3532,6 +3536,7 @@ __export(src_exports, {
3532
3536
  createEmptyTopologyConfig: () => createEmptyTopologyConfig,
3533
3537
  createProjectConfig: () => createProjectConfig,
3534
3538
  currentChildPins: () => currentChildPins,
3539
+ currentRootBinding: () => currentRootBinding,
3535
3540
  declaredPackNames: () => declaredPackNames,
3536
3541
  declaredProfileIds: () => declaredProfileIds,
3537
3542
  declaresGlobalPacks: () => declaresGlobalPacks,
@@ -3539,16 +3544,12 @@ __export(src_exports, {
3539
3544
  defaultNarrativeDetail: () => defaultNarrativeDetail,
3540
3545
  defaultPackSelections: () => defaultPackSelections,
3541
3546
  defaultTargetConfig: () => defaultTargetConfig,
3542
- deleteComponentSpec: () => deleteComponentSpec,
3543
- deleteImplementationSpec: () => deleteImplementationSpec,
3544
- deleteInterfaceSpec: () => deleteInterfaceSpec,
3545
3547
  deleteSpec: () => deleteSpec,
3546
- deleteSubsystemSpec: () => deleteSubsystemSpec,
3547
- deleteTypeSpec: () => deleteTypeSpec,
3548
3548
  deregisterPackRef: () => deregisterPackRef,
3549
3549
  deriveExecutionProfile: () => deriveExecutionProfile,
3550
3550
  derivedDocPaths: () => derivedDocPaths,
3551
3551
  detectDomainCandidates: () => detectDomainCandidates,
3552
+ diagnoseProjectPacks: () => diagnoseProjectPacks,
3552
3553
  diagramSetIndex: () => diagramSetIndex,
3553
3554
  diffAgainstApproval: () => diffAgainstApproval,
3554
3555
  diffSize: () => diffSize,
@@ -3564,6 +3565,9 @@ __export(src_exports, {
3564
3565
  factsFor: () => factsFor,
3565
3566
  fieldTypeRefs: () => fieldTypeRefs,
3566
3567
  fieldTypesOf: () => fieldTypesOf,
3568
+ findChainingParent: () => findChainingParent,
3569
+ findChainingSubprojectsMissingConfig: () => findChainingSubprojectsMissingConfig,
3570
+ findLegacySpecFiles: () => findLegacySpecFiles,
3567
3571
  findProjectRoot: () => findProjectRoot,
3568
3572
  findSystemRoot: () => findSystemRoot,
3569
3573
  fromProjectRoot: () => fromProjectRoot,
@@ -3573,6 +3577,7 @@ __export(src_exports, {
3573
3577
  generateDiagramSet: () => generateDiagramSet,
3574
3578
  generateSequenceDiagram: () => generateSequenceDiagram,
3575
3579
  getExporter: () => getExporter,
3580
+ getLoaderIssues: () => getLoaderIssues,
3576
3581
  getProjectRoot: () => getProjectRoot,
3577
3582
  getProjectRootOverride: () => getProjectRootOverride,
3578
3583
  getRequestParentReach: () => getRequestParentReach,
@@ -3580,6 +3585,7 @@ __export(src_exports, {
3580
3585
  getStatusReport: () => getStatusReport,
3581
3586
  globalGuideFilePath: () => globalGuideFilePath,
3582
3587
  globalPacksDir: () => globalPacksDir,
3588
+ globalPacksEnabled: () => globalPacksEnabled,
3583
3589
  globalTemplatesDir: () => globalTemplatesDir,
3584
3590
  hasContext: () => hasContext,
3585
3591
  hasFunctionBody: () => hasFunctionBody,
@@ -3610,8 +3616,9 @@ __export(src_exports, {
3610
3616
  loadImplementationSpecs: () => loadImplementationSpecs,
3611
3617
  loadInterfaceSpec: () => loadInterfaceSpec,
3612
3618
  loadInterfaceSpecs: () => loadInterfaceSpecs,
3613
- loadProjectConfig: () => loadProjectConfig4,
3619
+ loadProjectConfig: () => loadProjectConfig2,
3614
3620
  loadProjectExtensions: () => loadProjectExtensions,
3621
+ loadProjectVariants: () => loadProjectVariants,
3615
3622
  loadRegistry: () => loadRegistry,
3616
3623
  loadSpec: () => loadSpec,
3617
3624
  loadSpecGraph: () => loadSpecGraph,
@@ -3635,12 +3642,16 @@ __export(src_exports, {
3635
3642
  moveSubsystemProject: () => moveSubsystemProject,
3636
3643
  movedChildren: () => movedChildren,
3637
3644
  narrativeStepForeignFields: () => narrativeStepForeignFields,
3645
+ packDirEntry: () => packDirEntry,
3646
+ packEntryLabel: () => packEntryLabel,
3647
+ packEntryRef: () => packEntryRef,
3638
3648
  packStoreDir: () => packStoreDir,
3639
3649
  parseDeclaredCall: () => parseDeclaredCall,
3640
3650
  parseYaml: () => parseYaml,
3641
3651
  passesIntentFloor: () => passesIntentFloor,
3642
3652
  pathExists: () => pathExists,
3643
3653
  pathKey: () => pathKey,
3654
+ pinInstalledPacksAsSelections: () => pinInstalledPacksAsSelections,
3644
3655
  projectConfigExists: () => projectConfigExists,
3645
3656
  provisionProject: () => provisionProject,
3646
3657
  qualifiedTypeId: () => qualifiedTypeId,
@@ -3668,18 +3679,14 @@ __export(src_exports, {
3668
3679
  resolveImport: () => resolveImport,
3669
3680
  resolveInstalledPack: () => resolveInstalledPack,
3670
3681
  resolveSubprojectForNamespace: () => resolveSubprojectForNamespace,
3682
+ resolveVariantGuidance: () => resolveVariantGuidance,
3671
3683
  retireSpecialists: () => retireSpecialists,
3672
3684
  runWithProjectBinding: () => runWithProjectBinding,
3673
3685
  runWithProjectRoot: () => runWithProjectRoot,
3674
3686
  sameContract: () => sameContract,
3675
- saveComponentSpec: () => saveComponentSpec,
3676
- saveImplementationSpec: () => saveImplementationSpec,
3677
- saveInterfaceSpec: () => saveInterfaceSpec,
3678
3687
  saveSpec: () => saveSpec,
3679
- saveSubsystemSpec: () => saveSubsystemSpec,
3680
- saveSystemSpec: () => saveSystemSpec,
3681
3688
  saveTopologyConfig: () => saveTopologyConfig,
3682
- saveTypeSpec: () => saveTypeSpec,
3689
+ scanAllSpecs: () => scanAllSpecs,
3683
3690
  serializeYaml: () => serializeYaml,
3684
3691
  setExecutionTier: () => setExecutionTier,
3685
3692
  setLogLevel: () => setLogLevel,
@@ -3712,7 +3719,7 @@ module.exports = __toCommonJS(src_exports);
3712
3719
 
3713
3720
  // src/config/project-config.ts
3714
3721
  var fs2 = __toESM(require("fs"));
3715
- var path2 = __toESM(require("path"));
3722
+ var path3 = __toESM(require("path"));
3716
3723
  var import_zod4 = require("zod");
3717
3724
 
3718
3725
  // src/utils/fs.ts
@@ -3765,6 +3772,9 @@ function listFilesRecursive(dirPath, ext) {
3765
3772
  }
3766
3773
  var projectRootOverride = null;
3767
3774
  var requestRootStore = new import_node_async_hooks.AsyncLocalStorage();
3775
+ function currentRootBinding() {
3776
+ return requestRootStore.getStore();
3777
+ }
3768
3778
  function runWithProjectRoot(dir, fn) {
3769
3779
  return requestRootStore.run({ ...requestRootStore.getStore(), root: path.resolve(dir) }, fn);
3770
3780
  }
@@ -3909,6 +3919,7 @@ var BundleNotFoundError = class extends WaironError {
3909
3919
  };
3910
3920
 
3911
3921
  // src/models/project.ts
3922
+ var path2 = __toESM(require("path"));
3912
3923
  var import_zod3 = require("zod");
3913
3924
 
3914
3925
  // src/models/agent.ts
@@ -4496,16 +4507,28 @@ function activeTargetTypes(config) {
4496
4507
  const targets = config.targets;
4497
4508
  return targets.filter((t) => typeof t === "string" || t.enabled !== false).map((t) => typeof t === "string" ? t : t.type);
4498
4509
  }
4510
+ var PACK_EXT_RE = /\.(ya?ml|cjs|js)$/i;
4511
+ function declaredPackNames(config) {
4512
+ const selection = config.profileSelection;
4513
+ return [.../* @__PURE__ */ new Set([
4514
+ ...(config.extensions?.packs ?? []).map((entry) => typeof entry === "string" ? path2.basename(entry).replace(PACK_EXT_RE, "") : entry.name),
4515
+ ...selection?.requiredPackNames ?? [],
4516
+ ...selection?.defaultPackNames ?? []
4517
+ ])];
4518
+ }
4519
+ function declaredProfileIds(config) {
4520
+ return [...new Set(config.profileSelection?.profileIds ?? [])];
4521
+ }
4499
4522
 
4500
4523
  // src/config/project-config.ts
4501
4524
  function waiDirAt(root) {
4502
- const wai = path2.join(root, ".wai");
4503
- const legacy = path2.join(root, ".wairon");
4525
+ const wai = path3.join(root, ".wai");
4526
+ const legacy = path3.join(root, ".wairon");
4504
4527
  return !fs2.existsSync(wai) && fs2.existsSync(legacy) ? legacy : wai;
4505
4528
  }
4506
4529
  function projectConfigFsAdapterAt(rootDir) {
4507
- const root = path2.resolve(rootDir);
4508
- const file = () => path2.join(waiDirAt(root), "project.yaml");
4530
+ const root = path3.resolve(rootDir);
4531
+ const file = () => path3.join(waiDirAt(root), "project.yaml");
4509
4532
  return {
4510
4533
  readDocument() {
4511
4534
  return readYamlFile(file());
@@ -4714,15 +4737,15 @@ function indexOver(store, root) {
4714
4737
  specsDir() {
4715
4738
  try {
4716
4739
  const declared = store.readField("paths.specsDir");
4717
- if (declared) return path2.resolve(root, declared);
4740
+ if (declared) return path3.resolve(root, declared);
4718
4741
  } catch {
4719
4742
  }
4720
- return path2.join(waiDirAt(root), "specs");
4743
+ return path3.join(waiDirAt(root), "specs");
4721
4744
  }
4722
4745
  };
4723
4746
  }
4724
4747
  function projectConfigRepositoryOver(adapter, rootDir) {
4725
- const root = path2.resolve(rootDir);
4748
+ const root = path3.resolve(rootDir);
4726
4749
  const store = storeOver(adapter, root);
4727
4750
  const index = indexOver(store, root);
4728
4751
  const registry = registryOver(store, root);
@@ -4819,36 +4842,24 @@ var projectConfigRepository = {
4819
4842
  bound().pinGlobalPacksAsSelections(selections);
4820
4843
  }
4821
4844
  };
4822
- var PACK_EXT_RE = /\.(ya?ml|cjs|js)$/i;
4823
- function declaredPackNames(config) {
4824
- const selection = config.profileSelection;
4825
- return [.../* @__PURE__ */ new Set([
4826
- ...(config.extensions?.packs ?? []).map((entry) => typeof entry === "string" ? path2.basename(entry).replace(PACK_EXT_RE, "") : entry.name),
4827
- ...selection?.requiredPackNames ?? [],
4828
- ...selection?.defaultPackNames ?? []
4829
- ])];
4830
- }
4831
- function declaredProfileIds(config) {
4832
- return [...new Set(config.profileSelection?.profileIds ?? [])];
4833
- }
4834
4845
 
4835
4846
  // src/core/domain_projector.ts
4836
4847
  var path16 = __toESM(require("path"));
4837
4848
 
4838
4849
  // src/core/specs.ts
4839
- var fs12 = __toESM(require("fs"));
4850
+ var fs11 = __toESM(require("fs"));
4840
4851
  var path15 = __toESM(require("path"));
4841
4852
 
4842
4853
  // src/config/paths.ts
4843
4854
  var fs3 = __toESM(require("fs"));
4844
- var path3 = __toESM(require("path"));
4855
+ var path4 = __toESM(require("path"));
4845
4856
  function aiPathsAt(rootDir) {
4846
- const resolvedRoot = path3.resolve(rootDir);
4857
+ const resolvedRoot = path4.resolve(rootDir);
4847
4858
  const aiDirAt2 = (...segments) => {
4848
- const waiPath = path3.join(resolvedRoot, ".wai");
4849
- const waironPath = path3.join(resolvedRoot, ".wairon");
4859
+ const waiPath = path4.join(resolvedRoot, ".wai");
4860
+ const waironPath = path4.join(resolvedRoot, ".wairon");
4850
4861
  const base = !fs3.existsSync(waiPath) && fs3.existsSync(waironPath) ? waironPath : waiPath;
4851
- return path3.join(base, ...segments);
4862
+ return path4.join(base, ...segments);
4852
4863
  };
4853
4864
  const specsDir = () => projectConfigRepositoryAt(resolvedRoot).specsDir();
4854
4865
  return {
@@ -4865,12 +4876,12 @@ function aiPathsAt(rootDir) {
4865
4876
  contextDomainsMd: () => aiDirAt2("context", "domains.md"),
4866
4877
  contextWaironGuideMd: () => aiDirAt2("context", "wairon-guide.md"),
4867
4878
  specsDir,
4868
- specsSystem: () => path3.join(specsDir(), ".index.yaml"),
4869
- specsSubsystemsDir: () => path3.join(specsDir(), "subsystems"),
4870
- specsComponentsDir: () => path3.join(specsDir(), "components"),
4871
- specsInterfacesDir: () => path3.join(specsDir(), "interfaces"),
4872
- specsImplementationsDir: () => path3.join(specsDir(), "implementations"),
4873
- specsTypesDir: () => path3.join(specsDir(), "types")
4879
+ specsSystem: () => path4.join(specsDir(), ".index.yaml"),
4880
+ specsSubsystemsDir: () => path4.join(specsDir(), "subsystems"),
4881
+ specsComponentsDir: () => path4.join(specsDir(), "components"),
4882
+ specsInterfacesDir: () => path4.join(specsDir(), "interfaces"),
4883
+ specsImplementationsDir: () => path4.join(specsDir(), "implementations"),
4884
+ specsTypesDir: () => path4.join(specsDir(), "types")
4874
4885
  };
4875
4886
  }
4876
4887
  var AI_PATHS = {
@@ -4951,7 +4962,7 @@ function stateIdEquals(a, b) {
4951
4962
 
4952
4963
  // src/core/lockfile.ts
4953
4964
  var fs4 = __toESM(require("fs"));
4954
- var path4 = __toESM(require("path"));
4965
+ var path5 = __toESM(require("path"));
4955
4966
  function lockPath() {
4956
4967
  return aiDir("lock.json");
4957
4968
  }
@@ -4983,7 +4994,7 @@ function normalizeRecord(raw) {
4983
4994
  }
4984
4995
  function writeLockRecord(record) {
4985
4996
  const p = lockPath();
4986
- fs4.mkdirSync(path4.dirname(p), { recursive: true });
4997
+ fs4.mkdirSync(path5.dirname(p), { recursive: true });
4987
4998
  const tmp = `${p}.tmp`;
4988
4999
  fs4.writeFileSync(tmp, JSON.stringify(withSortedMaps(record), null, 2) + "\n");
4989
5000
  fs4.renameSync(tmp, p);
@@ -5006,7 +5017,7 @@ function withSortedMaps(record) {
5006
5017
 
5007
5018
  // src/core/spec-files.ts
5008
5019
  var fs5 = __toESM(require("fs"));
5009
- var path5 = __toESM(require("path"));
5020
+ var path6 = __toESM(require("path"));
5010
5021
  var SPEC_FILE_EXTENSION = ".yaml";
5011
5022
  function readSpecFile(filePath) {
5012
5023
  return readYamlFile(filePath);
@@ -5020,7 +5031,7 @@ function listSpecFiles(specsDir) {
5020
5031
  function removeSpecFile(filePath, specsRoot) {
5021
5032
  if (!fs5.existsSync(filePath)) return false;
5022
5033
  fs5.unlinkSync(filePath);
5023
- pruneEmptyDirs(path5.dirname(filePath), path5.resolve(specsRoot));
5034
+ pruneEmptyDirs(path6.dirname(filePath), path6.resolve(specsRoot));
5024
5035
  return true;
5025
5036
  }
5026
5037
  function pruneEmptyDirs(dir, specsRoot) {
@@ -5028,7 +5039,7 @@ function pruneEmptyDirs(dir, specsRoot) {
5028
5039
  while (at !== specsRoot && at.startsWith(specsRoot)) {
5029
5040
  if (!fs5.existsSync(at) || fs5.readdirSync(at).length > 0) return;
5030
5041
  fs5.rmdirSync(at);
5031
- at = path5.dirname(at);
5042
+ at = path6.dirname(at);
5032
5043
  }
5033
5044
  }
5034
5045
 
@@ -5206,7 +5217,13 @@ var PublicInterfaceSchema = import_zod8.z.object({
5206
5217
  /** The L2 component that realizes this public interface (the subsystem's published surface). */
5207
5218
  component: SpecIdSchema.optional(),
5208
5219
  /** Optional L3 interface on that component backing this entry. */
5209
- interface: SpecIdSchema.optional()
5220
+ interface: SpecIdSchema.optional(),
5221
+ /**
5222
+ * The subsystems this surface is published to. Absent, anyone may depend on
5223
+ * it; present, only these subsystems may — when every entry publishing the
5224
+ * component names consumers, the union of those lists is the whole set.
5225
+ */
5226
+ consumers: import_zod8.z.array(SpecIdSchema).optional()
5210
5227
  });
5211
5228
  var TrustedLinkSchema = import_zod8.z.object({
5212
5229
  /** The peer subsystem id this link sanctions tight coupling with. */
@@ -6580,7 +6597,7 @@ function jumpFieldsOf(s) {
6580
6597
  }
6581
6598
 
6582
6599
  // src/models/code-model.ts
6583
- var path6 = __toESM(require("path"));
6600
+ var path7 = __toESM(require("path"));
6584
6601
  function pathKey(sourcePath) {
6585
6602
  return sourcePath.replace(/\\/g, "/").replace(/^\.\//, "");
6586
6603
  }
@@ -6615,7 +6632,7 @@ function hasFunctionBody(facts, symbol) {
6615
6632
  }
6616
6633
  function resolveImport(fromFile, specifier, knownPaths) {
6617
6634
  if (!specifier.startsWith(".")) return void 0;
6618
- const joined = pathKey(path6.posix.normalize(path6.posix.join(path6.posix.dirname(fromFile), specifier)));
6635
+ const joined = pathKey(path7.posix.normalize(path7.posix.join(path7.posix.dirname(fromFile), specifier)));
6619
6636
  const candidates = [
6620
6637
  joined,
6621
6638
  joined.replace(/\.js$/, ".ts"),
@@ -6711,7 +6728,7 @@ function resolveNarrativeLabels(methodName, steps) {
6711
6728
 
6712
6729
  // src/core/canvas.ts
6713
6730
  var fs6 = __toESM(require("fs"));
6714
- var path7 = __toESM(require("path"));
6731
+ var path8 = __toESM(require("path"));
6715
6732
 
6716
6733
  // src/core/canvas-layout.ts
6717
6734
  function computeLayout(model, collapsed) {
@@ -7370,9 +7387,9 @@ function embedJson(value) {
7370
7387
  }
7371
7388
  function loadCytoscapeLib() {
7372
7389
  const candidates = [
7373
- path7.resolve(__dirname, "..", "templates", "canvas", "cytoscape.min.js"),
7390
+ path8.resolve(__dirname, "..", "templates", "canvas", "cytoscape.min.js"),
7374
7391
  // src/core & dist/cli
7375
- path7.resolve(__dirname, "templates", "canvas", "cytoscape.min.js")
7392
+ path8.resolve(__dirname, "templates", "canvas", "cytoscape.min.js")
7376
7393
  // dist (library entry)
7377
7394
  ];
7378
7395
  for (const p of candidates) {
@@ -11544,10 +11561,47 @@ var MODEL = __MODEL_JSON__;
11544
11561
  </html>
11545
11562
  `;
11546
11563
 
11564
+ // src/core/surfaces.ts
11565
+ var fs7 = __toESM(require("fs"));
11566
+ var path9 = __toESM(require("path"));
11567
+
11568
+ // src/core/openapi.ts
11569
+ var yaml2 = __toESM(require("js-yaml"));
11570
+
11571
+ // src/core/surfaces.ts
11572
+ var SURFACES_DIRNAME = "surfaces";
11573
+ function surfacesDir(rootDir) {
11574
+ return path9.join(rootDir, ".wai", SURFACES_DIRNAME);
11575
+ }
11576
+ function listSnapshots(rootDir = getProjectRoot()) {
11577
+ const dir = surfacesDir(rootDir);
11578
+ if (!fs7.existsSync(dir)) return [];
11579
+ const out = [];
11580
+ for (const file of fs7.readdirSync(dir)) {
11581
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
11582
+ try {
11583
+ out.push(SurfaceSnapshotSchema.parse(readYamlFile(path9.join(dir, file))));
11584
+ } catch {
11585
+ }
11586
+ }
11587
+ return out;
11588
+ }
11589
+ function listMountSnapshots(mounts) {
11590
+ const bound2 = path9.resolve(getProjectRoot());
11591
+ const out = [];
11592
+ for (const namespace of mounts) {
11593
+ const dir = resolveSubprojectForNamespace(namespace);
11594
+ if (!dir || path9.resolve(dir) === bound2) continue;
11595
+ const snapshots = listSnapshots(dir);
11596
+ if (snapshots.length > 0) out.push({ namespace, snapshots });
11597
+ }
11598
+ return out;
11599
+ }
11600
+
11547
11601
  // src/core/extensions.ts
11548
- var fs8 = __toESM(require("fs"));
11602
+ var fs9 = __toESM(require("fs"));
11549
11603
  var os2 = __toESM(require("os"));
11550
- var path10 = __toESM(require("path"));
11604
+ var path12 = __toESM(require("path"));
11551
11605
  var import_module = require("module");
11552
11606
  var import_zod9 = require("zod");
11553
11607
 
@@ -11575,12 +11629,12 @@ function isNewerVersion(current2, candidate) {
11575
11629
 
11576
11630
  // src/core/packstore.ts
11577
11631
  var crypto2 = __toESM(require("crypto"));
11578
- var fs7 = __toESM(require("fs"));
11579
- var path9 = __toESM(require("path"));
11632
+ var fs8 = __toESM(require("fs"));
11633
+ var path11 = __toESM(require("path"));
11580
11634
 
11581
11635
  // src/config/defaults.ts
11582
11636
  var os = __toESM(require("os"));
11583
- var path8 = __toESM(require("path"));
11637
+ var path10 = __toESM(require("path"));
11584
11638
  var DEFAULT_TARGET_DIRS = {
11585
11639
  claude: ".claude/agents",
11586
11640
  gemini: ".gemini/agents",
@@ -11596,7 +11650,7 @@ function defaultTargetConfig(type) {
11596
11650
  enabled: true
11597
11651
  };
11598
11652
  }
11599
- var WAIRON_VERSION = "5.1.1-dev.71";
11653
+ var WAIRON_VERSION = "5.1.1-dev.73";
11600
11654
  var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
11601
11655
  var ARCHITECT_AGENT_ID = "agent-architect";
11602
11656
  var ARCHITECT_TEMPLATE_ID = "architect";
@@ -11607,7 +11661,7 @@ function globalTemplatesDir(projectOverride) {
11607
11661
  if (projectOverride) {
11608
11662
  return projectOverride;
11609
11663
  }
11610
- return path8.join(os.homedir(), ".wairon", "templates");
11664
+ return path10.join(os.homedir(), ".wairon", "templates");
11611
11665
  }
11612
11666
  var BACKEND_COMMANDS = {
11613
11667
  claude: "claude",
@@ -11643,7 +11697,7 @@ function packStoreDir() {
11643
11697
  function packFiles(dir, prefix = "") {
11644
11698
  let entries;
11645
11699
  try {
11646
- entries = fs7.readdirSync(dir, { withFileTypes: true });
11700
+ entries = fs8.readdirSync(dir, { withFileTypes: true });
11647
11701
  } catch {
11648
11702
  return [];
11649
11703
  }
@@ -11651,25 +11705,25 @@ function packFiles(dir, prefix = "") {
11651
11705
  for (const e of entries) {
11652
11706
  if (DIGEST_EXCLUDED.has(e.name)) continue;
11653
11707
  const rel2 = prefix ? `${prefix}/${e.name}` : e.name;
11654
- if (e.isDirectory()) out.push(...packFiles(path9.join(dir, e.name), rel2));
11708
+ if (e.isDirectory()) out.push(...packFiles(path11.join(dir, e.name), rel2));
11655
11709
  else if (e.isFile()) out.push(rel2);
11656
11710
  }
11657
11711
  return out.sort();
11658
11712
  }
11659
11713
  function computePackDigest(packDir) {
11660
11714
  const hash = crypto2.createHash("sha256");
11661
- const abs = path9.resolve(packDir);
11715
+ const abs = path11.resolve(packDir);
11662
11716
  let isFile = false;
11663
11717
  try {
11664
- isFile = fs7.statSync(abs).isFile();
11718
+ isFile = fs8.statSync(abs).isFile();
11665
11719
  } catch {
11666
11720
  }
11667
- const entries = isFile ? [[path9.basename(abs), abs]] : packFiles(abs).map((rel2) => [rel2, path9.join(abs, rel2)]);
11721
+ const entries = isFile ? [[path11.basename(abs), abs]] : packFiles(abs).map((rel2) => [rel2, path11.join(abs, rel2)]);
11668
11722
  for (const [rel2, full] of entries) {
11669
11723
  hash.update(rel2);
11670
11724
  hash.update("\0");
11671
11725
  try {
11672
- hash.update(fs7.readFileSync(full));
11726
+ hash.update(fs8.readFileSync(full));
11673
11727
  } catch {
11674
11728
  }
11675
11729
  hash.update("\0");
@@ -11678,7 +11732,7 @@ function computePackDigest(packDir) {
11678
11732
  }
11679
11733
  function readInstallRecord(packPath) {
11680
11734
  try {
11681
- const raw = readYamlFile(path9.join(packPath, INSTALL_RECORD));
11735
+ const raw = readYamlFile(path11.join(packPath, INSTALL_RECORD));
11682
11736
  return raw && typeof raw === "object" ? raw : {};
11683
11737
  } catch {
11684
11738
  return {};
@@ -11709,14 +11763,14 @@ function installedFrom(location, name, version) {
11709
11763
  function versionedEntries(nameDir, fallbackName) {
11710
11764
  let versions;
11711
11765
  try {
11712
- versions = fs7.readdirSync(nameDir, { withFileTypes: true });
11766
+ versions = fs8.readdirSync(nameDir, { withFileTypes: true });
11713
11767
  } catch {
11714
11768
  return [];
11715
11769
  }
11716
11770
  const out = [];
11717
11771
  for (const v of versions) {
11718
11772
  if (!v.isDirectory()) continue;
11719
- const versionDir = path9.join(nameDir, v.name);
11773
+ const versionDir = path11.join(nameDir, v.name);
11720
11774
  const entryFile = packDirEntry(versionDir);
11721
11775
  if (!entryFile) continue;
11722
11776
  out.push(installedFrom(versionDir, declaredName(entryFile) ?? fallbackName, v.name));
@@ -11724,7 +11778,7 @@ function versionedEntries(nameDir, fallbackName) {
11724
11778
  return out;
11725
11779
  }
11726
11780
  function storeEntryPacks(store, entry) {
11727
- const full = path9.join(store, entry.name);
11781
+ const full = path11.join(store, entry.name);
11728
11782
  if (entry.isFile()) {
11729
11783
  if (!PACK_FILE_RE.test(entry.name)) return [];
11730
11784
  const name = declaredName(full) ?? entry.name.replace(PACK_FILE_RE, "");
@@ -11739,7 +11793,7 @@ function listInstalledPacks() {
11739
11793
  const store = packStoreDir();
11740
11794
  let entries;
11741
11795
  try {
11742
- entries = fs7.readdirSync(store, { withFileTypes: true });
11796
+ entries = fs8.readdirSync(store, { withFileTypes: true });
11743
11797
  } catch {
11744
11798
  return [];
11745
11799
  }
@@ -11756,17 +11810,17 @@ function resolveInstalledPack(name, version) {
11756
11810
  return candidates[0];
11757
11811
  }
11758
11812
  function installPackFromDirectory(sourceDir, origin) {
11759
- const abs = path9.resolve(sourceDir);
11813
+ const abs = path11.resolve(sourceDir);
11760
11814
  const entryFile = packDirEntry(abs);
11761
11815
  if (!entryFile) {
11762
11816
  throw new Error(`"${sourceDir}" is not a pack directory (no pack.yaml | pack.cjs | index.cjs | \u2026).`);
11763
11817
  }
11764
- const manifest = readManifest(entryFile, path9.dirname(entryFile));
11818
+ const manifest = readManifest(entryFile, path11.dirname(entryFile));
11765
11819
  const version = manifest.version && manifest.version.length > 0 ? manifest.version : UNVERSIONED;
11766
- const dest = path9.join(packStoreDir(), manifest.name, version);
11767
- fs7.rmSync(dest, { recursive: true, force: true });
11768
- fs7.mkdirSync(path9.dirname(dest), { recursive: true });
11769
- fs7.cpSync(abs, dest, { recursive: true });
11820
+ const dest = path11.join(packStoreDir(), manifest.name, version);
11821
+ fs8.rmSync(dest, { recursive: true, force: true });
11822
+ fs8.mkdirSync(path11.dirname(dest), { recursive: true });
11823
+ fs8.cpSync(abs, dest, { recursive: true });
11770
11824
  const digest2 = computePackDigest(dest);
11771
11825
  const record = {
11772
11826
  origin,
@@ -11774,7 +11828,7 @@ function installPackFromDirectory(sourceDir, origin) {
11774
11828
  waironVersion: WAIRON_VERSION,
11775
11829
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
11776
11830
  };
11777
- writeYamlFile(path9.join(dest, INSTALL_RECORD), record);
11831
+ writeYamlFile(path11.join(dest, INSTALL_RECORD), record);
11778
11832
  return {
11779
11833
  name: manifest.name,
11780
11834
  version,
@@ -11789,11 +11843,11 @@ function uninstallPack(name, version) {
11789
11843
  const targets = listInstalledPacks().filter((p) => p.name === name && (!version || p.version === version));
11790
11844
  if (targets.length === 0) return false;
11791
11845
  for (const target of targets) {
11792
- fs7.rmSync(target.path, { recursive: true, force: true });
11793
- const parent = path9.dirname(target.path);
11794
- if (path9.resolve(parent) === path9.resolve(packStoreDir())) continue;
11846
+ fs8.rmSync(target.path, { recursive: true, force: true });
11847
+ const parent = path11.dirname(target.path);
11848
+ if (path11.resolve(parent) === path11.resolve(packStoreDir())) continue;
11795
11849
  try {
11796
- if (fs7.readdirSync(parent).length === 0) fs7.rmdirSync(parent);
11850
+ if (fs8.readdirSync(parent).length === 0) fs8.rmdirSync(parent);
11797
11851
  } catch {
11798
11852
  }
11799
11853
  }
@@ -11944,6 +11998,10 @@ function skillExtendErrors(skills) {
11944
11998
  return skills.filter((s) => s.extends !== void 0 && !EXTENDABLE_BUILTIN_SKILLS.includes(s.extends)).map((s) => `Extension pack "${s.pack}" declares a skill extending "${s.extends}", which is not a builtin skill (extendable: ${EXTENDABLE_BUILTIN_SKILLS.join(", ")}).`);
11945
11999
  }
11946
12000
  var GLOBAL_PACKS_DEFAULT = false;
12001
+ function checkDeclarativePack(manifest) {
12002
+ const result = DeclarativePackSchema.safeParse(manifest);
12003
+ return result.success ? null : result.error.issues[0]?.message ?? "shape mismatch";
12004
+ }
11947
12005
  function globalPacksEnabled(config) {
11948
12006
  return config.extensions?.useGlobalPacks ?? GLOBAL_PACKS_DEFAULT;
11949
12007
  }
@@ -11951,27 +12009,27 @@ function emptyExtensions() {
11951
12009
  return { packNames: [], packs: [], rules: [], profiles: {}, languages: {}, skills: [], patterns: [], guarantees: [], assertions: [], instructions: [], errors: [], selectionFailures: [] };
11952
12010
  }
11953
12011
  function globalPacksDir() {
11954
- return process.env.WAIRON_PACKS_DIR ?? path10.join(os2.homedir(), ".wairon", "packs");
12012
+ return process.env.WAIRON_PACKS_DIR ?? path12.join(os2.homedir(), ".wairon", "packs");
11955
12013
  }
11956
12014
  var PACK_DIR_ENTRIES = ["pack.yaml", "pack.yml", "pack.cjs", "pack.js", "index.cjs", "index.js"];
11957
12015
  function packDirEntry(dir) {
11958
12016
  for (const name of PACK_DIR_ENTRIES) {
11959
- const p = path10.join(dir, name);
11960
- if (fs8.existsSync(p)) return p;
12017
+ const p = path12.join(dir, name);
12018
+ if (fs9.existsSync(p)) return p;
11961
12019
  }
11962
12020
  return null;
11963
12021
  }
11964
12022
  function latestVersionDir(nameDir) {
11965
12023
  let versions;
11966
12024
  try {
11967
- versions = fs8.readdirSync(nameDir, { withFileTypes: true });
12025
+ versions = fs9.readdirSync(nameDir, { withFileTypes: true });
11968
12026
  } catch {
11969
12027
  return null;
11970
12028
  }
11971
12029
  let best = null;
11972
12030
  for (const v of versions) {
11973
12031
  if (!v.isDirectory()) continue;
11974
- const dir = path10.join(nameDir, v.name);
12032
+ const dir = path12.join(nameDir, v.name);
11975
12033
  if (!packDirEntry(dir)) continue;
11976
12034
  if (!best || isNewerVersion(best.version, v.name)) best = { version: v.name, dir };
11977
12035
  }
@@ -11980,13 +12038,13 @@ function latestVersionDir(nameDir) {
11980
12038
  function discoverPacks(dir) {
11981
12039
  let entries;
11982
12040
  try {
11983
- entries = fs8.readdirSync(dir, { withFileTypes: true });
12041
+ entries = fs9.readdirSync(dir, { withFileTypes: true });
11984
12042
  } catch {
11985
12043
  return [];
11986
12044
  }
11987
12045
  const refs = [];
11988
12046
  for (const e of entries.sort((a, b) => compareOrdinal(a.name, b.name))) {
11989
- const full = path10.join(dir, e.name);
12047
+ const full = path12.join(dir, e.name);
11990
12048
  if (e.isDirectory()) {
11991
12049
  if (packDirEntry(full)) refs.push(full);
11992
12050
  else {
@@ -12016,7 +12074,7 @@ function mergePack(out, pack, ref, scope, packDir) {
12016
12074
  foreignBuiltins: [.../* @__PURE__ */ new Set([...existing.foreignBuiltins, ...def.foreignBuiltins])]
12017
12075
  } : def;
12018
12076
  }
12019
- for (const s of pack.skills) out.skills.push({ ...s, pack: pack.name, packVersion: pack.version, sourcePath: path10.resolve(packDir, s.source) });
12077
+ for (const s of pack.skills) out.skills.push({ ...s, pack: pack.name, packVersion: pack.version, sourcePath: path12.resolve(packDir, s.source) });
12020
12078
  for (const p of pack.patterns) out.patterns.push({ ...p, pack: pack.name });
12021
12079
  out.guarantees = [.../* @__PURE__ */ new Set([...out.guarantees, ...pack.guarantees])];
12022
12080
  for (const a of pack.assertions) out.assertions.push({ ...a, pack: pack.name, fullCode: assertionFullCode(pack.name, a.code) });
@@ -12024,10 +12082,10 @@ function mergePack(out, pack, ref, scope, packDir) {
12024
12082
  }
12025
12083
  var isYamlPath = (p) => /\.ya?ml$/i.test(p);
12026
12084
  function resolvePackRef(ref, projectRoot) {
12027
- const pathLike = ref.startsWith(".") || path10.isAbsolute(ref) || isYamlPath(ref);
12085
+ const pathLike = ref.startsWith(".") || path12.isAbsolute(ref) || isYamlPath(ref);
12028
12086
  if (!pathLike) return ref;
12029
- const resolved = path10.resolve(projectRoot, ref);
12030
- if (fs8.existsSync(resolved) && fs8.statSync(resolved).isDirectory()) {
12087
+ const resolved = path12.resolve(projectRoot, ref);
12088
+ if (fs9.existsSync(resolved) && fs9.statSync(resolved).isDirectory()) {
12031
12089
  const entry = packDirEntry(resolved);
12032
12090
  if (!entry) throw new Error(`directory pack has no entry file (${PACK_DIR_ENTRIES.join(" | ")})`);
12033
12091
  return entry;
@@ -12040,7 +12098,7 @@ function readManifest(target, projectRoot) {
12040
12098
  if (raw == null) throw new Error("file not found or empty");
12041
12099
  return { ...DeclarativePackSchema.parse(raw), rules: [] };
12042
12100
  }
12043
- const req = (0, import_module.createRequire)(path10.join(projectRoot, "package.json"));
12101
+ const req = (0, import_module.createRequire)(path12.join(projectRoot, "package.json"));
12044
12102
  const modRaw = req(target);
12045
12103
  const mod = modRaw?.default ?? modRaw;
12046
12104
  const parsed = DeclarativePackSchema.parse({
@@ -12068,7 +12126,7 @@ function loadExtensionPacks(refs, projectRoot) {
12068
12126
  try {
12069
12127
  const target = resolvePackRef(ref, projectRoot);
12070
12128
  const manifest = readManifest(target, projectRoot);
12071
- mergePack(out, manifest, ref, scope, path10.dirname(target));
12129
+ mergePack(out, manifest, ref, scope, path12.dirname(target));
12072
12130
  for (const r of manifest.rules) out.rules.push(r);
12073
12131
  } catch (e) {
12074
12132
  out.errors.push(`Extension pack "${ref}" failed to load: ${e instanceof Error ? e.message : String(e)}`);
@@ -12114,7 +12172,7 @@ function defaultPackSelections() {
12114
12172
  const entry = packDirEntry(pack.path) ?? pack.path;
12115
12173
  let manifest;
12116
12174
  try {
12117
- manifest = readManifest(entry, path10.dirname(entry));
12175
+ manifest = readManifest(entry, path12.dirname(entry));
12118
12176
  } catch {
12119
12177
  continue;
12120
12178
  }
@@ -12130,6 +12188,66 @@ function defaultPackSelections() {
12130
12188
  return [];
12131
12189
  }
12132
12190
  }
12191
+ function diagnoseProjectPacks() {
12192
+ const empty = { globalsUndeclared: false, notApplied: [], unresolved: [], globalsApplied: [] };
12193
+ try {
12194
+ const config = projectConfigRepository.load();
12195
+ if (!config) return empty;
12196
+ const globalsUndeclared = !projectConfigRepository.declaresGlobalPacks();
12197
+ const projectRoot = getProjectRoot();
12198
+ const entries = config.extensions?.packs ?? [];
12199
+ const unresolved = [];
12200
+ const selectedNames = /* @__PURE__ */ new Set();
12201
+ for (const entry of entries) {
12202
+ if (typeof entry === "string") continue;
12203
+ selectedNames.add(entry.name);
12204
+ const resolution = resolveSelection(entry, projectRoot);
12205
+ if (resolution.failure) {
12206
+ unresolved.push({ label: packEntryLabel(entry), message: `[${resolution.failure.code}] ${resolution.failure.message}` });
12207
+ }
12208
+ }
12209
+ const installed = listInstalledPacks();
12210
+ const globalsOn = globalPacksEnabled(config);
12211
+ const globalsApplied = [];
12212
+ if (globalsOn) {
12213
+ for (const ref of discoverPacks(globalPacksDir())) {
12214
+ const match = installed.find((p) => path12.resolve(p.path) === path12.resolve(ref));
12215
+ if (match) globalsApplied.push({ name: match.name, version: match.version });
12216
+ }
12217
+ }
12218
+ const applying = /* @__PURE__ */ new Set([...selectedNames, ...globalsApplied.map((p) => p.name)]);
12219
+ const notApplied = installed.filter((p) => !applying.has(p.name)).map((p) => ({ name: p.name, version: p.version, origin: p.origin }));
12220
+ return { globalsUndeclared, notApplied, unresolved, globalsApplied };
12221
+ } catch {
12222
+ return empty;
12223
+ }
12224
+ }
12225
+ function pinInstalledPacksAsSelections() {
12226
+ const diagnosis = diagnoseProjectPacks();
12227
+ if (!diagnosis.globalsUndeclared || diagnosis.notApplied.length === 0) return [];
12228
+ const added = [];
12229
+ const selections = [];
12230
+ for (const pack of diagnosis.notApplied) {
12231
+ selections.push({
12232
+ name: pack.name,
12233
+ version: pack.version,
12234
+ // Carry the origin so the selection can be obtained elsewhere; a local
12235
+ // path is not fetchable, so it is deliberately not recorded as a source.
12236
+ ...pack.origin && /^[a-z][a-z0-9+.-]*:\/\//i.test(pack.origin) ? { source: pack.origin } : {}
12237
+ });
12238
+ added.push(`${pack.name}@${pack.version}`);
12239
+ }
12240
+ projectConfigRepository.pinGlobalPacksAsSelections(selections);
12241
+ return added;
12242
+ }
12243
+ function packEntryLabel(entry) {
12244
+ if (typeof entry === "string") return entry;
12245
+ return entry.version ? `${entry.name}@${entry.version}` : entry.name;
12246
+ }
12247
+ function packEntryRef(entry, projectRoot) {
12248
+ if (typeof entry === "string") return entry;
12249
+ return resolveSelection(entry, projectRoot).ref ?? null;
12250
+ }
12133
12251
  function resolveSelection(selection, projectRoot) {
12134
12252
  const wanted = selection.version ? `${selection.name}@${selection.version}` : selection.name;
12135
12253
  const fail = (code, message) => ({ failure: { code, severity: "error", name: selection.name, message } });
@@ -12171,7 +12289,7 @@ function verifyIntegrity(selection, packDir, where) {
12171
12289
  };
12172
12290
  }
12173
12291
  function detectBundleStoreDrift(selection, bundleDir) {
12174
- const bundledVersion = path10.basename(bundleDir);
12292
+ const bundledVersion = path12.basename(bundleDir);
12175
12293
  const installed = resolveInstalledPack(selection.name, bundledVersion);
12176
12294
  if (!installed) return void 0;
12177
12295
  const bundleDigest = computePackDigest(bundleDir);
@@ -12185,22 +12303,22 @@ function detectBundleStoreDrift(selection, bundleDir) {
12185
12303
  };
12186
12304
  }
12187
12305
  function findBundledPack(projectRoot, selection) {
12188
- const base = path10.join(projectRoot, ".wai", "packs", selection.name);
12306
+ const base = path12.join(projectRoot, ".wai", "packs", selection.name);
12189
12307
  if (selection.version) {
12190
- const pinned = path10.join(base, selection.version);
12308
+ const pinned = path12.join(base, selection.version);
12191
12309
  return packDirEntry(pinned) ? pinned : null;
12192
12310
  }
12193
12311
  if (packDirEntry(base)) return base;
12194
12312
  let versions;
12195
12313
  try {
12196
- versions = fs8.readdirSync(base, { withFileTypes: true });
12314
+ versions = fs9.readdirSync(base, { withFileTypes: true });
12197
12315
  } catch {
12198
12316
  return null;
12199
12317
  }
12200
12318
  let best = null;
12201
12319
  for (const v of versions) {
12202
12320
  if (!v.isDirectory()) continue;
12203
- const dir = path10.join(base, v.name);
12321
+ const dir = path12.join(base, v.name);
12204
12322
  if (!packDirEntry(dir)) continue;
12205
12323
  if (!best || isNewerVersion(best.version, v.name)) best = { version: v.name, dir };
12206
12324
  }
@@ -12243,21 +12361,21 @@ function buildCodeIndex(model) {
12243
12361
  const declarations = /* @__PURE__ */ new Map();
12244
12362
  const anchors = /* @__PURE__ */ new Map();
12245
12363
  const findingAnchors = /* @__PURE__ */ new Map();
12246
- const namesAt = (cache, path28, make) => {
12247
- const cached = cache.get(path28);
12364
+ const namesAt = (cache, path29, make) => {
12365
+ const cached = cache.get(path29);
12248
12366
  if (cached) return cached;
12249
- const f = facts.get(path28);
12367
+ const f = facts.get(path29);
12250
12368
  if (!f) return NO_NAMES;
12251
12369
  const built = make(f);
12252
- cache.set(path28, built);
12370
+ cache.set(path29, built);
12253
12371
  return built;
12254
12372
  };
12255
12373
  const republished = /* @__PURE__ */ new Map();
12256
- const republishedFrom = (path28) => {
12257
- const cached = republished.get(path28);
12374
+ const republishedFrom = (path29) => {
12375
+ const cached = republished.get(path29);
12258
12376
  if (cached) return cached;
12259
- const out = /* @__PURE__ */ new Set([path28]);
12260
- const queue = [path28];
12377
+ const out = /* @__PURE__ */ new Set([path29]);
12378
+ const queue = [path29];
12261
12379
  while (queue.length) {
12262
12380
  const from = queue.pop();
12263
12381
  const f = facts.get(from);
@@ -12270,7 +12388,7 @@ function buildCodeIndex(model) {
12270
12388
  }
12271
12389
  }
12272
12390
  }
12273
- republished.set(path28, out);
12391
+ republished.set(path29, out);
12274
12392
  return out;
12275
12393
  };
12276
12394
  const NO_ORIGIN = /* @__PURE__ */ new Set();
@@ -12279,9 +12397,9 @@ function buildCodeIndex(model) {
12279
12397
  return to ? republishedFrom(to) : NO_ORIGIN;
12280
12398
  };
12281
12399
  const scopeOf = (site, from) => {
12282
- const path28 = pathKey(site.from ?? from);
12283
- const f = facts.get(path28);
12284
- return f?.status === "analyzed" && f.analysisGrade === "exact" ? { path: path28, facts: f } : void 0;
12400
+ const path29 = pathKey(site.from ?? from);
12401
+ const f = facts.get(path29);
12402
+ return f?.status === "analyzed" && f.analysisGrade === "exact" ? { path: path29, facts: f } : void 0;
12285
12403
  };
12286
12404
  const originOf2 = (site, from) => {
12287
12405
  const resolved = scopeOf(site, from);
@@ -12320,14 +12438,14 @@ function buildCodeIndex(model) {
12320
12438
  if (site.via) for (const typeName of localTypesOf(f, site.via)) widenThroughType(typeName);
12321
12439
  return out;
12322
12440
  };
12323
- const declarationsAt = (path28) => namesAt(declarations, pathKey(path28), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames]));
12441
+ const declarationsAt = (path29) => namesAt(declarations, pathKey(path29), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames]));
12324
12442
  return {
12325
12443
  paths,
12326
12444
  exactPaths,
12327
- factsAt: (path28) => facts.get(pathKey(path28)),
12445
+ factsAt: (path29) => facts.get(pathKey(path29)),
12328
12446
  declarationsAt,
12329
- anchorsAt: (path28) => namesAt(anchors, pathKey(path28), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
12330
- findingAnchorsAt: (path28) => namesAt(findingAnchors, pathKey(path28), (f) => new Set(f.anchoredNames)),
12447
+ anchorsAt: (path29) => namesAt(anchors, pathKey(path29), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
12448
+ findingAnchorsAt: (path29) => namesAt(findingAnchors, pathKey(path29), (f) => new Set(f.anchoredNames)),
12331
12449
  originOf: originOf2,
12332
12450
  possibleOriginsOf
12333
12451
  };
@@ -12365,10 +12483,10 @@ function buildRealizationIndex(ctx) {
12365
12483
  return {
12366
12484
  paths,
12367
12485
  implementationsOf: (compId) => implsByComponent.get(compId) ?? NONE_IMPL,
12368
- implementationsAt: (path28) => implsByPath.get(pathKey(path28)) ?? NONE_IMPL,
12486
+ implementationsAt: (path29) => implsByPath.get(pathKey(path29)) ?? NONE_IMPL,
12369
12487
  filesOf: (compId) => filesByComponent.get(compId) ?? NONE_PATH,
12370
12488
  filesIn: (subsystemId) => filesBySubsystem.get(subsystemId) ?? NONE_PATH,
12371
- componentsAt: (path28) => componentsByPath.get(pathKey(path28)) ?? NONE_COMP
12489
+ componentsAt: (path29) => componentsByPath.get(pathKey(path29)) ?? NONE_COMP
12372
12490
  };
12373
12491
  }
12374
12492
  function buildImportGraph(index, universe) {
@@ -12380,22 +12498,22 @@ function buildImportGraph(index, universe) {
12380
12498
  if (to && to !== from) into.add(to);
12381
12499
  }
12382
12500
  };
12383
- const importsOf = (path28) => {
12384
- const cached = imports.get(path28);
12501
+ const importsOf = (path29) => {
12502
+ const cached = imports.get(path29);
12385
12503
  if (cached) return cached;
12386
- const facts = index.factsAt(path28);
12504
+ const facts = index.factsAt(path29);
12387
12505
  const out = /* @__PURE__ */ new Set();
12388
- if (facts?.status === "analyzed") resolveAll(path28, facts.imports, out);
12389
- imports.set(path28, out);
12506
+ if (facts?.status === "analyzed") resolveAll(path29, facts.imports, out);
12507
+ imports.set(path29, out);
12390
12508
  return out;
12391
12509
  };
12392
- const tracesOf = (path28) => {
12393
- const cached = traces.get(path28);
12510
+ const tracesOf = (path29) => {
12511
+ const cached = traces.get(path29);
12394
12512
  if (cached) return cached;
12395
- const facts = index.factsAt(path28);
12396
- const out = new Set(importsOf(path28));
12397
- if (facts?.status === "analyzed") resolveAll(path28, facts.reexports, out);
12398
- traces.set(path28, out);
12513
+ const facts = index.factsAt(path29);
12514
+ const out = new Set(importsOf(path29));
12515
+ if (facts?.status === "analyzed") resolveAll(path29, facts.reexports, out);
12516
+ traces.set(path29, out);
12399
12517
  return out;
12400
12518
  };
12401
12519
  return {
@@ -12779,6 +12897,32 @@ var publicSurfaceBindingRule = {
12779
12897
  }
12780
12898
  };
12781
12899
 
12900
+ // src/core/rules/integrity/public-surface-consumers.ts
12901
+ var publicSurfaceConsumersRule = {
12902
+ name: "public-surface-consumers",
12903
+ description: "Every subsystem a published surface names as its consumer must exist in the tree. A consumers list restricts who may depend on the surface, so an id that names nothing locks the surface against a caller nobody can be \u2014 and a typo there silently locks out the caller that was meant, which the boundary rule would then report against the wrong party.",
12904
+ codes: [
12905
+ { code: "PUBLIC_INTERFACE_UNKNOWN_CONSUMER", defaultSeverity: "error", summary: "A published surface names as its consumer a subsystem the tree does not have" }
12906
+ ],
12907
+ check(ctx) {
12908
+ const known = new Set(ctx.subsystems.map((s) => s.id));
12909
+ for (const sub of ctx.subsystems) {
12910
+ for (const pi of sub.publicInterfaces) {
12911
+ for (const consumer of pi.consumers ?? []) {
12912
+ if (known.has(consumer)) continue;
12913
+ ctx.addIssue(
12914
+ "error",
12915
+ "PUBLIC_INTERFACE_UNKNOWN_CONSUMER",
12916
+ `Subsystem "${sub.id}" publishes "${pi.component ?? `${pi.type} ${pi.details}`}" to consumer "${consumer}", which names no subsystem of this tree. Name an existing subsystem, or drop the id \u2014 a consumers list admits only the subsystems it names.`,
12917
+ sub.id,
12918
+ isDraftSubsystem(sub)
12919
+ );
12920
+ }
12921
+ }
12922
+ }
12923
+ }
12924
+ };
12925
+
12782
12926
  // src/core/rules/integrity/public-surface-declared-type.ts
12783
12927
  var pubTypeMatches = (piType, ct, portalType) => {
12784
12928
  switch (piType) {
@@ -13706,11 +13850,11 @@ var unconditionalCallCyclesRule = {
13706
13850
  const memberEdges = keys.flatMap((k) => (adjacency.get(k) ?? []).filter((e) => inScc.has(e.toKey)));
13707
13851
  if (memberEdges.length === 0) continue;
13708
13852
  const anchor = [...memberEdges].sort((a, b) => byCodeUnit(a.fromKey, b.fromKey) || byCodeUnit(a.impl.id, b.impl.id) || a.stepNumber - b.stepNumber)[0];
13709
- const path28 = [...keys].sort(byCodeUnit).join(" \u2192 ");
13853
+ const path29 = [...keys].sort(byCodeUnit).join(" \u2192 ");
13710
13854
  ctx.addIssue(
13711
13855
  "warning",
13712
13856
  "UNCONDITIONAL_CALL_CYCLE",
13713
- `Call cycle with no guard: ${path28} \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.`,
13857
+ `Call cycle with no guard: ${path29} \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.`,
13714
13858
  anchor.impl.id,
13715
13859
  memberEdges.some((e) => ctx.isImplementationDraft(e.impl))
13716
13860
  );
@@ -14148,14 +14292,15 @@ var authSourceWiringRule = {
14148
14292
  // src/core/rules/doctrine/subsystem-boundary-dependencies.ts
14149
14293
  var subsystemBoundaryDepsRule = {
14150
14294
  name: "subsystem-boundary-dependencies",
14151
- description: "Judges every dependsOn edge that leaves its own subsystem, and every one that resolves nowhere. Across subsystems the shape is client Adapter \u2192 published remote Portal, OR a direct in-process edge licensed by a trustedLink declared on the SOURCE subsystem (the published-Portal target requirement applies either way). A reference that names nothing in this tree is resolved against the stored surface snapshots: a hit is a DECLARED remote portal and the same Adapter requirement applies to it, snapshots of several providers that disagree make the reference ambiguous, and a reference authored to leave this root that no snapshot covers warns instead of erroring. Boundary rules are never relaxed by a pack profile.",
14295
+ description: "Judges every dependsOn edge that leaves its own subsystem, and every one that resolves nowhere. Across subsystems the shape is client Adapter \u2192 published remote Portal, OR a direct in-process edge licensed by a trustedLink declared on the SOURCE subsystem (the published-Portal target requirement applies either way). A published surface may also name the subsystems it serves (a publicInterfaces entry's consumers): a component every one of whose entries names consumers may be depended on only from those subsystems, which is how a provider keeps a surface \u2014 the raw spec writes \u2014 away from a door that must not reach it. A reference that names nothing in this tree is resolved against the stored surface snapshots: a hit is a DECLARED remote portal and the same Adapter requirement applies to it, snapshots of several providers that disagree make the reference ambiguous, and a reference authored to leave this root that no snapshot covers warns instead of erroring. Boundary rules are never relaxed by a pack profile.",
14152
14296
  codes: [
14153
14297
  { code: "INVALID_DEPENDENCY_REFERENCE", defaultSeverity: "error", summary: "dependsOn names a non-existent component" },
14154
14298
  { code: "CROSS_TREE_REF_UNRESOLVED", defaultSeverity: "warning", summary: "Cross-tree dependsOn (super::/:: form) with no surface snapshot covering it" },
14155
14299
  { code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree dependsOn matched by surface snapshots of several providers with different contracts" },
14156
14300
  { code: "CROSS_SUBSYSTEM_NON_ADAPTER", defaultSeverity: "error", summary: "Non-Adapter component crossing a subsystem boundary" },
14157
14301
  { code: "CROSS_SUBSYSTEM_PRIVATE_ACCESS", defaultSeverity: "error", summary: "Cross-subsystem dependency on an unpublished component" },
14158
- { code: "CROSS_SUBSYSTEM_TARGET_NON_PORTAL", defaultSeverity: "error", summary: "Cross-subsystem hop entering through a non-Portal" }
14302
+ { code: "CROSS_SUBSYSTEM_TARGET_NON_PORTAL", defaultSeverity: "error", summary: "Cross-subsystem hop entering through a non-Portal" },
14303
+ { code: "CROSS_SUBSYSTEM_UNLISTED_CONSUMER", defaultSeverity: "error", summary: "Cross-subsystem dependency on a surface published only to other subsystems" }
14159
14304
  ],
14160
14305
  check(ctx) {
14161
14306
  for (const edge of ctx.dependencyEdges().all) {
@@ -14238,9 +14383,24 @@ var subsystemBoundaryDepsRule = {
14238
14383
  edge.draftContext
14239
14384
  );
14240
14385
  }
14386
+ const publishedTo = restrictedConsumers(ctx, depComp.subsystem, depComp.id);
14387
+ if (publishedTo && !publishedTo.includes(comp.subsystem)) {
14388
+ ctx.addIssue(
14389
+ "error",
14390
+ "CROSS_SUBSYSTEM_UNLISTED_CONSUMER",
14391
+ `Boundary violation: "${comp.id}" (subsystem "${comp.subsystem}") depends on "${depComp.id}", which subsystem "${depComp.subsystem}" publishes only to ${publishedTo.map((s) => `"${s}"`).join(", ")}. Depend on a surface published to "${comp.subsystem}" instead, or \u2014 when "${comp.subsystem}" really is a caller the provider meant to serve \u2014 add it to the entry's consumers.`,
14392
+ comp.id,
14393
+ edge.draftContext
14394
+ );
14395
+ }
14241
14396
  }
14242
14397
  }
14243
14398
  };
14399
+ function restrictedConsumers(ctx, subsystemId, componentId) {
14400
+ const entries = (ctx.subsystems.find((s) => s.id === subsystemId)?.publicInterfaces ?? []).filter((pi) => pi.component === componentId);
14401
+ if (entries.length === 0 || entries.some((pi) => pi.consumers === void 0)) return null;
14402
+ return [...new Set(entries.flatMap((pi) => pi.consumers ?? []))];
14403
+ }
14244
14404
 
14245
14405
  // src/core/rules/doctrine/stereotype-terms.ts
14246
14406
  var NEVER_INLINE_STATE = " Never resolve this by merging the store's state into the consuming component \u2014 state hidden inside a logic block is invisible to the spec and unrecoverable.";
@@ -15361,8 +15521,8 @@ var lifecycleRule = {
15361
15521
  };
15362
15522
 
15363
15523
  // src/core/rules/wiring/portal-mounts.ts
15364
- function liesUnder(path28, prefix) {
15365
- return path28 === prefix || path28.startsWith(prefix + "/");
15524
+ function liesUnder(path29, prefix) {
15525
+ return path29 === prefix || path29.startsWith(prefix + "/");
15366
15526
  }
15367
15527
  var portalMountsRule = {
15368
15528
  name: "portal-mounts",
@@ -16530,11 +16690,11 @@ var hiddenStateRule = {
16530
16690
  byPath.set(key, list);
16531
16691
  }
16532
16692
  }
16533
- for (const path28 of code.exactPaths) {
16534
- const facts = code.factsAt(path28);
16693
+ for (const path29 of code.exactPaths) {
16694
+ const facts = code.factsAt(path29);
16535
16695
  const bindings = facts.topLevelMutableBindings ?? [];
16536
16696
  if (bindings.length === 0) continue;
16537
- const mapped = byPath.get(path28) ?? [];
16697
+ const mapped = byPath.get(path29) ?? [];
16538
16698
  if (mapped.length === 0) continue;
16539
16699
  if (!mapped.every((m) => isStatelessLogic(m.component))) continue;
16540
16700
  const anchor = [...mapped].sort((a, b) => a.impl.id.localeCompare(b.impl.id))[0];
@@ -16600,7 +16760,7 @@ var dependencyConformanceRule = {
16600
16760
  }
16601
16761
  return false;
16602
16762
  };
16603
- const draftAt = (path28) => realization.implementationsAt(path28).some((impl) => ctx.isImplementationDraft(impl));
16763
+ const draftAt = (path29) => realization.implementationsAt(path29).some((impl) => ctx.isImplementationDraft(impl));
16604
16764
  for (const fromPath of mappedPaths) {
16605
16765
  const fromComponents = realization.componentsAt(fromPath);
16606
16766
  for (const toPath of graph.importsOf(fromPath)) {
@@ -16656,6 +16816,60 @@ var dependencyConformanceRule = {
16656
16816
  }
16657
16817
  };
16658
16818
 
16819
+ // src/core/rules/conformance/portal-imports.ts
16820
+ var portalImportsRule = {
16821
+ name: "portal-imports",
16822
+ description: "A runtime import that crosses a subsystem boundary must land on a file that realizes one of the target subsystem's PUBLISHED components \u2014 its portal file. dependency-conformance asks whether the hop is declared; this asks where it lands. An import that reaches past the portal into a concrete module can use anything that module exports, published or not, so the portal stops being the boundary and names no contract ever promised leak across it \u2014 which is how a surface grows that nobody designed. In-process it is cosmetic at runtime; it is not cosmetic for the contract. Only exact-grade component-mapped files participate; type-only imports are exempt; chained subprojects validate standalone.",
16823
+ codes: [
16824
+ {
16825
+ code: "IMPORT_BYPASSES_PORTAL",
16826
+ defaultSeverity: "warning",
16827
+ summary: "A runtime import crosses into another subsystem through a file that realizes none of its published components",
16828
+ carryable: true
16829
+ }
16830
+ ],
16831
+ check(ctx) {
16832
+ const code = ctx.codeIndex();
16833
+ const realization = ctx.realizationIndex();
16834
+ const mappedPaths = new Set(realization.paths.filter((p) => code.exactPaths.has(p)));
16835
+ const graph = ctx.importGraph(mappedPaths);
16836
+ const portalFiles = /* @__PURE__ */ new Map();
16837
+ const portalFilesOf = (subsystemId) => {
16838
+ let files = portalFiles.get(subsystemId);
16839
+ if (!files) {
16840
+ files = /* @__PURE__ */ new Set();
16841
+ for (const compId of ctx.publicSet.get(subsystemId) ?? []) {
16842
+ for (const f of realization.filesOf(compId)) if (mappedPaths.has(f)) files.add(f);
16843
+ }
16844
+ portalFiles.set(subsystemId, files);
16845
+ }
16846
+ return files;
16847
+ };
16848
+ const draftAt = (path29) => realization.implementationsAt(path29).some((impl) => ctx.isImplementationDraft(impl));
16849
+ for (const fromPath of mappedPaths) {
16850
+ const fromSubsystems = new Set(realization.componentsAt(fromPath).map((c) => c.subsystem));
16851
+ for (const toPath of graph.importsOf(fromPath)) {
16852
+ const crossed = [...new Set(realization.componentsAt(toPath).map((c) => c.subsystem))].filter((s) => !fromSubsystems.has(s));
16853
+ const bypassed = crossed.filter((s) => !portalFilesOf(s).has(toPath));
16854
+ if (bypassed.length === 0) continue;
16855
+ for (const subsystemId of bypassed) {
16856
+ const portals = [...portalFilesOf(subsystemId)];
16857
+ ctx.addIssue(
16858
+ "warning",
16859
+ "IMPORT_BYPASSES_PORTAL",
16860
+ `"${fromPath}" imports "${toPath}", crossing into subsystem "${subsystemId}" somewhere other than its portal file (${portals.length > 0 ? portals.join(", ") : "none realized at exact grade"}). Import the name from the portal file instead; when no portal of "${subsystemId}" publishes it, it is a surface the provider never promised \u2014 publish it on the contract of the portal whose capability it is, or stop depending on it.`,
16861
+ realization.implementationsAt(fromPath)[0]?.id,
16862
+ draftAt(fromPath) || draftAt(toPath),
16863
+ void 0,
16864
+ // One import edge, one indivisible fact: the EDGE is the site.
16865
+ { at: `${fromPath} -> ${toPath}` }
16866
+ );
16867
+ }
16868
+ }
16869
+ }
16870
+ }
16871
+ };
16872
+
16659
16873
  // src/core/rules/conformance/integration-sim-declaration.ts
16660
16874
  var integrationSimDeclarationRule = {
16661
16875
  name: "integration-sim-declaration",
@@ -17167,8 +17381,8 @@ var paramConformanceRule = {
17167
17381
  };
17168
17382
 
17169
17383
  // src/core/rules/conformance/route-coverage.ts
17170
- function liesUnder2(path28, prefix) {
17171
- return path28 === prefix || path28.startsWith(prefix + "/");
17384
+ function liesUnder2(path29, prefix) {
17385
+ return path29 === prefix || path29.startsWith(prefix + "/");
17172
17386
  }
17173
17387
  function normalizeSegment(segment) {
17174
17388
  return /^\{.+\}$/.test(segment) || segment.startsWith(":") ? "*" : segment;
@@ -18733,10 +18947,11 @@ var SDD_RULES = [
18733
18947
  assertionForbiddenEdgesRule,
18734
18948
  assertionRequiredFieldsRule,
18735
18949
  assertionEndpointShapesRule,
18736
- // The published surface in three questions: what backs the entry, whether
18737
- // that component's stereotype can realize the type it declares, and whether
18738
- // the contract it binds is that component's own.
18950
+ // The published surface in four questions: what backs the entry, who it is
18951
+ // published to, whether that component's stereotype can realize the type it
18952
+ // declares, and whether the contract it binds is that component's own.
18739
18953
  publicSurfaceBindingRule,
18954
+ publicSurfaceConsumersRule,
18740
18955
  publicSurfaceDeclaredTypeRule,
18741
18956
  publicSurfaceBoundContractRule,
18742
18957
  cyclesRule,
@@ -18789,6 +19004,8 @@ var SDD_RULES = [
18789
19004
  // The fields-vs-Store criterion: mutable module state in logic-only files.
18790
19005
  hiddenStateRule,
18791
19006
  dependencyConformanceRule,
19007
+ // Where a declared crossing LANDS: on the target subsystem's portal file.
19008
+ portalImportsRule,
18792
19009
  // Integration wiring proof rides after the code↔spec family: it consumes
18793
19010
  // the same code model and speaks about the same sourcePath modules. Four
18794
19011
  // questions about one harness, in the order a reader meets them: is one
@@ -18865,8 +19082,8 @@ function knownIssueCodes() {
18865
19082
  }
18866
19083
 
18867
19084
  // src/core/source-analysis.ts
18868
- var fs9 = __toESM(require("fs"));
18869
- var path11 = __toESM(require("path"));
19085
+ var fs10 = __toESM(require("fs"));
19086
+ var path13 = __toESM(require("path"));
18870
19087
  var import_module2 = require("module");
18871
19088
  function emptyCodeModel() {
18872
19089
  return { files: [], projectRoot: "", rootFiles: [] };
@@ -19091,7 +19308,7 @@ function resolveTypeScript(projectRoot) {
19091
19308
  return cached.ts;
19092
19309
  }
19093
19310
  let ts = null;
19094
- const bases = [path11.join(projectRoot, "package.json"), __filename];
19311
+ const bases = [path13.join(projectRoot, "package.json"), __filename];
19095
19312
  for (const base of bases) {
19096
19313
  try {
19097
19314
  const req = (0, import_module2.createRequire)(base);
@@ -19487,7 +19704,7 @@ function walkExact(ts, sourceText, fileName) {
19487
19704
  }
19488
19705
  function resolveRelativeModule(fromFile, specifier) {
19489
19706
  if (!specifier.startsWith(".")) return null;
19490
- const base = path11.resolve(path11.dirname(fromFile), specifier);
19707
+ const base = path13.resolve(path13.dirname(fromFile), specifier);
19491
19708
  const candidates = [
19492
19709
  base,
19493
19710
  base.replace(/\.js$/, ".ts"),
@@ -19495,12 +19712,12 @@ function resolveRelativeModule(fromFile, specifier) {
19495
19712
  `${base}.ts`,
19496
19713
  `${base}.tsx`,
19497
19714
  `${base}.js`,
19498
- path11.join(base, "index.ts"),
19499
- path11.join(base, "index.js")
19715
+ path13.join(base, "index.ts"),
19716
+ path13.join(base, "index.js")
19500
19717
  ];
19501
19718
  for (const c of candidates) {
19502
19719
  try {
19503
- if (fs9.statSync(c).isFile()) return c;
19720
+ if (fs10.statSync(c).isFile()) return c;
19504
19721
  } catch {
19505
19722
  }
19506
19723
  }
@@ -19508,11 +19725,11 @@ function resolveRelativeModule(fromFile, specifier) {
19508
19725
  }
19509
19726
  function reexportTarget(ts, filePath, specifier, projectRoot, visited, exactCache) {
19510
19727
  const target = resolveRelativeModule(filePath, specifier);
19511
- if (!target || path11.relative(projectRoot, target).startsWith("..")) return null;
19728
+ if (!target || path13.relative(projectRoot, target).startsWith("..")) return null;
19512
19729
  let targetFacts = exactCache.get(target);
19513
19730
  if (targetFacts === void 0) {
19514
19731
  try {
19515
- targetFacts = walkExact(ts, fs9.readFileSync(target, "utf8"), target);
19732
+ targetFacts = walkExact(ts, fs10.readFileSync(target, "utf8"), target);
19516
19733
  } catch {
19517
19734
  targetFacts = null;
19518
19735
  }
@@ -19530,7 +19747,7 @@ function carryBody(facts, target, targetPath, local, exported, projectRoot) {
19530
19747
  if (score !== void 0) facts.complexity.set(exported, Math.max(facts.complexity.get(exported) ?? 0, score));
19531
19748
  const targetCalls = target.calls.get(local);
19532
19749
  if (!targetCalls) return;
19533
- const carriedFrom = pathKey(path11.relative(projectRoot, targetPath));
19750
+ const carriedFrom = pathKey(path13.relative(projectRoot, targetPath));
19534
19751
  const sites = facts.calls.get(exported) ?? /* @__PURE__ */ new Map();
19535
19752
  for (const [key, site] of targetCalls) sites.set(carriedFrom + "|" + key, { ...site, from: site.from ?? carriedFrom });
19536
19753
  facts.calls.set(exported, sites);
@@ -19560,24 +19777,24 @@ function walkDeclaredRoots(roots, exclude, projectRoot) {
19560
19777
  const excluded = exclude.map(pathKey);
19561
19778
  const isExcluded = (key) => excluded.some((e) => key === e || key.startsWith(`${e}/`));
19562
19779
  const record = (absolute) => {
19563
- const key = pathKey(path11.relative(projectRoot, absolute));
19780
+ const key = pathKey(path13.relative(projectRoot, absolute));
19564
19781
  if (seen.has(key) || isExcluded(key)) return;
19565
- if (!EXTENSION_LANGUAGE[path11.extname(key).toLowerCase()]) return;
19782
+ if (!EXTENSION_LANGUAGE[path13.extname(key).toLowerCase()]) return;
19566
19783
  seen.add(key);
19567
19784
  found.push(key);
19568
19785
  };
19569
19786
  const descend = (dir) => {
19570
19787
  let entries;
19571
19788
  try {
19572
- entries = fs9.readdirSync(dir, { withFileTypes: true });
19789
+ entries = fs10.readdirSync(dir, { withFileTypes: true });
19573
19790
  } catch {
19574
19791
  return;
19575
19792
  }
19576
19793
  for (const entry of entries.slice().sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
19577
- const child = path11.join(dir, entry.name);
19794
+ const child = path13.join(dir, entry.name);
19578
19795
  if (entry.isDirectory()) {
19579
19796
  if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
19580
- if (isExcluded(pathKey(path11.relative(projectRoot, child)))) continue;
19797
+ if (isExcluded(pathKey(path13.relative(projectRoot, child)))) continue;
19581
19798
  descend(child);
19582
19799
  } else if (entry.isFile()) {
19583
19800
  record(child);
@@ -19586,13 +19803,13 @@ function walkDeclaredRoots(roots, exclude, projectRoot) {
19586
19803
  };
19587
19804
  for (const root of roots) {
19588
19805
  const key = pathKey(root);
19589
- if (path11.isAbsolute(key) || path11.normalize(key).split(path11.sep)[0] === "..") continue;
19590
- const absolute = path11.resolve(projectRoot, key);
19591
- if (path11.relative(projectRoot, absolute).startsWith("..")) continue;
19806
+ if (path13.isAbsolute(key) || path13.normalize(key).split(path13.sep)[0] === "..") continue;
19807
+ const absolute = path13.resolve(projectRoot, key);
19808
+ if (path13.relative(projectRoot, absolute).startsWith("..")) continue;
19592
19809
  if (isExcluded(key)) continue;
19593
19810
  let stat;
19594
19811
  try {
19595
- stat = fs9.statSync(absolute);
19812
+ stat = fs10.statSync(absolute);
19596
19813
  } catch {
19597
19814
  continue;
19598
19815
  }
@@ -19618,23 +19835,23 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
19618
19835
  if (seen.has(sourcePath)) continue;
19619
19836
  seen.add(sourcePath);
19620
19837
  const empty = { declaredNames: [], anchoredNames: [], exportedNames: [], imports: [], reexports: [] };
19621
- if (path11.isAbsolute(sourcePath) || path11.normalize(sourcePath).split(path11.sep)[0] === "..") {
19838
+ if (path13.isAbsolute(sourcePath) || path13.normalize(sourcePath).split(path13.sep)[0] === "..") {
19622
19839
  files.push({ path: sourcePath, status: "escaped", ...empty });
19623
19840
  continue;
19624
19841
  }
19625
- const absolute = path11.resolve(projectRoot, sourcePath);
19626
- if (path11.relative(projectRoot, absolute).startsWith("..")) {
19842
+ const absolute = path13.resolve(projectRoot, sourcePath);
19843
+ if (path13.relative(projectRoot, absolute).startsWith("..")) {
19627
19844
  files.push({ path: sourcePath, status: "escaped", ...empty });
19628
19845
  continue;
19629
19846
  }
19630
19847
  let buffer;
19631
19848
  try {
19632
- const stat = fs9.statSync(absolute);
19849
+ const stat = fs10.statSync(absolute);
19633
19850
  if (!stat.isFile()) {
19634
19851
  files.push({ path: sourcePath, status: "missing", ...empty });
19635
19852
  continue;
19636
19853
  }
19637
- buffer = fs9.readFileSync(absolute);
19854
+ buffer = fs10.readFileSync(absolute);
19638
19855
  } catch {
19639
19856
  files.push({ path: sourcePath, status: "missing", ...empty });
19640
19857
  continue;
@@ -19644,7 +19861,7 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
19644
19861
  continue;
19645
19862
  }
19646
19863
  const text = buffer.toString("utf8");
19647
- const language = EXTENSION_LANGUAGE[path11.extname(sourcePath).toLowerCase()];
19864
+ const language = EXTENSION_LANGUAGE[path13.extname(sourcePath).toLowerCase()];
19648
19865
  let analyzed;
19649
19866
  if (language === "typescript" || language === "javascript") {
19650
19867
  const ts = resolveTypeScript(projectRoot);
@@ -19715,6 +19932,7 @@ var DEPTH_GATED_CODES = {
19715
19932
  CONFORMANCE_DEGRADED: "implementations",
19716
19933
  UNDECLARED_DEPENDENCY: "implementations",
19717
19934
  UNREALIZED_DEPENDENCY: "implementations",
19935
+ IMPORT_BYPASSES_PORTAL: "implementations",
19718
19936
  MISSING_INTEGRATION_SIM: "implementations",
19719
19937
  // L5 expectations: narratives and everything whose fuel is narrative edges
19720
19938
  // (the reachability walk and the hydration round-trip would drown a
@@ -19749,6 +19967,7 @@ var COMPLETENESS_RULES = /* @__PURE__ */ new Set([
19749
19967
  "CONFORMANCE_ANALYSIS_SKIPPED",
19750
19968
  "UNDECLARED_DEPENDENCY",
19751
19969
  "UNREALIZED_DEPENDENCY",
19970
+ "IMPORT_BYPASSES_PORTAL",
19752
19971
  // Detail sufficiency reads the realized code like the conformance family
19753
19972
  // does — a draft tree is allowed to disagree with its code.
19754
19973
  "UNNARRATED_COMPLEXITY",
@@ -20180,244 +20399,49 @@ function buildRuleContext(opts) {
20180
20399
  return ctx;
20181
20400
  }
20182
20401
 
20183
- // src/core/variants.ts
20184
- var fs10 = __toESM(require("fs"));
20185
- var os3 = __toESM(require("os"));
20186
- var path12 = __toESM(require("path"));
20187
- var import_zod10 = require("zod");
20188
- var VariantDefSchema = import_zod10.z.object({
20189
- /** Variant id referenced by a component's `variant` (e.g. "publisher", "org/external-config-adapter"). */
20190
- id: import_zod10.z.string().min(1),
20191
- /** The core stereotype this variant specializes — authoritative for generic semantics (Adapter, Orchestrator, …). */
20192
- base: import_zod10.z.string().min(1),
20193
- /** How to implement a component of this variant — the recipe the implementer follows and reuses across same-variant components. */
20194
- guidance: import_zod10.z.string().min(1),
20195
- /** Optional: this variant only applies for the given target language (else it applies everywhere). */
20196
- target: import_zod10.z.string().optional(),
20197
- /** Optional: this variant only applies under the given architectural profile. */
20198
- profile: import_zod10.z.string().optional()
20199
- });
20200
- function globalVariantsDir() {
20201
- return process.env.WAIRON_VARIANTS_DIR ?? path12.join(os3.homedir(), ".wairon", "variants");
20202
- }
20203
- function builtinVariantsDir() {
20204
- const candidates = [
20205
- path12.resolve(__dirname, "..", "templates", "variants"),
20206
- // src/core & dist/cli
20207
- path12.resolve(__dirname, "templates", "variants")
20208
- // dist (library entry)
20209
- ];
20210
- return candidates.find((dir) => fs10.existsSync(dir)) ?? candidates[0];
20211
- }
20212
- function projectVariantsDir() {
20213
- return path12.join(getProjectRoot(), ".wai", "variants");
20214
- }
20215
- function variantFiles(dir) {
20216
- let entries;
20402
+ // src/core/validation.ts
20403
+ var path14 = __toESM(require("path"));
20404
+ function projectPackSelections() {
20217
20405
  try {
20218
- entries = fs10.readdirSync(dir, { withFileTypes: true });
20406
+ return (loadProjectConfig()?.extensions?.packs ?? []).filter((e) => typeof e !== "string");
20219
20407
  } catch {
20220
20408
  return [];
20221
20409
  }
20222
- return entries.filter((e) => e.isFile() && /\.ya?ml$/i.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => path12.join(dir, e.name));
20223
- }
20224
- function parseVariants(raw) {
20225
- if (raw == null) return [];
20226
- return (Array.isArray(raw) ? raw : [raw]).map((item) => VariantDefSchema.parse(item));
20227
20410
  }
20228
- function reportSkippedFile(file, err) {
20229
- console.error(`[variants] skipped "${file}": ${err instanceof Error ? err.message : String(err)}`);
20411
+ var RESOLUTION_FAILURE_CODES = /* @__PURE__ */ new Set([
20412
+ "UNDEFINED_TYPE_REFERENCE",
20413
+ "INVALID_DEPENDENCY_REFERENCE",
20414
+ "INVALID_TARGET_COMPONENT_REFERENCE",
20415
+ "INVALID_SUBSYSTEM_REFERENCE",
20416
+ "INVALID_TRUSTED_LINK",
20417
+ "CROSS_TREE_REF_UNRESOLVED"
20418
+ ]);
20419
+ var SEVERITY_RANK = { off: 0, warning: 1, error: 2 };
20420
+ function issueKey(issue2) {
20421
+ return `${issue2.code}|${issue2.specId ?? ""}`;
20230
20422
  }
20231
- function readVariantsDir(dir) {
20232
- const out = [];
20233
- for (const file of variantFiles(dir)) {
20234
- try {
20235
- out.push(...parseVariants(readYamlFile(file)));
20236
- } catch (err) {
20237
- reportSkippedFile(file, err);
20238
- }
20423
+ function worstByKey(issues) {
20424
+ const worst = /* @__PURE__ */ new Map();
20425
+ for (const issue2 of issues) {
20426
+ const key = issueKey(issue2);
20427
+ const seen = worst.get(key);
20428
+ if (!seen || SEVERITY_RANK[issue2.severity] > SEVERITY_RANK[seen]) worst.set(key, issue2.severity);
20239
20429
  }
20240
- return out;
20430
+ return worst;
20241
20431
  }
20242
- function mergeById(...layers) {
20243
- const byId = /* @__PURE__ */ new Map();
20244
- for (const layer of layers) {
20245
- for (const v of layer) byId.set(v.id, v);
20432
+ function stricterSeverities(parent, child) {
20433
+ const fromParent = parent?.sddRuleSeverity ?? {};
20434
+ const fromChild = child?.sddRuleSeverity ?? {};
20435
+ const codes = /* @__PURE__ */ new Set([...Object.keys(fromParent), ...Object.keys(fromChild)]);
20436
+ if (codes.size === 0) return parent ?? child;
20437
+ const defaults = new Map(knownIssueCodes().map((rc) => [rc.code, rc.defaultSeverity]));
20438
+ const merged = {};
20439
+ for (const code of codes) {
20440
+ const p = fromParent[code] ?? defaults.get(code);
20441
+ const c = fromChild[code] ?? defaults.get(code);
20442
+ merged[code] = p === void 0 || c === void 0 ? p ?? c : SEVERITY_RANK[p] >= SEVERITY_RANK[c] ? p : c;
20246
20443
  }
20247
- return [...byId.values()];
20248
- }
20249
- function resolveVariantGuidance(component, allComponents, variantsById) {
20250
- if (!component.variant) return null;
20251
- const def = variantsById.get(component.variant);
20252
- if (!def) return null;
20253
- return {
20254
- variant: component.variant,
20255
- base: def.base,
20256
- guidance: def.guidance,
20257
- siblings: allComponents.filter((o) => o.variant === component.variant && o.id !== component.id).map((o) => o.id),
20258
- ...def.target ? { target: def.target } : {},
20259
- ...def.profile ? { profile: def.profile } : {}
20260
- };
20261
- }
20262
- function composeVariantGuidance(comps, allComponents, variantsById) {
20263
- const resolved = comps.map((c) => ({ id: c.id, guidance: resolveVariantGuidance(c, allComponents, variantsById) })).filter((r) => r.guidance !== null);
20264
- if (resolved.length === 0) return "";
20265
- const lines = [
20266
- "## Component variants \u2014 reuse the shared approach",
20267
- "",
20268
- "One or more of your components declare a variant \u2014 a base-anchored kind with implementation guidance. Implement every component of the same variant alike, reusing one shared approach instead of reinventing it per instance:",
20269
- ""
20270
- ];
20271
- for (const { id, guidance } of resolved) {
20272
- let line = `- **${id}** \u2014 variant \`${guidance.variant}\` (a kind of ${guidance.base}): ${guidance.guidance}`;
20273
- if (guidance.siblings.length > 0) {
20274
- line += ` Same-variant components elsewhere: ${guidance.siblings.join(", ")} \u2014 implement them consistently, reusing the same logic/concept.`;
20275
- }
20276
- lines.push(line);
20277
- }
20278
- return lines.join("\n");
20279
- }
20280
- function loadProjectVariants() {
20281
- try {
20282
- return mergeById(
20283
- readVariantsDir(builtinVariantsDir()),
20284
- readVariantsDir(globalVariantsDir()),
20285
- readVariantsDir(projectVariantsDir())
20286
- );
20287
- } catch {
20288
- return [];
20289
- }
20290
- }
20291
- function listProjectVariants() {
20292
- return mergeById(readVariantsDir(projectVariantsDir()));
20293
- }
20294
- function rebaseProjectVariants(fromBase, toBase) {
20295
- const rebased = [];
20296
- for (const file of variantFiles(projectVariantsDir())) {
20297
- const content = readFileOrNull(file);
20298
- if (content === null) continue;
20299
- let raw;
20300
- let variants;
20301
- try {
20302
- raw = parseYaml(content, file);
20303
- variants = parseVariants(raw);
20304
- } catch (err) {
20305
- reportSkippedFile(file, err);
20306
- continue;
20307
- }
20308
- const matching = variants.filter((v) => v.base === fromBase);
20309
- if (matching.length === 0) continue;
20310
- writeFile(file, rebasedText(content, fromBase, toBase, withRebasedEntries(raw, fromBase, toBase)));
20311
- for (const v of matching) {
20312
- if (!rebased.includes(v.id)) rebased.push(v.id);
20313
- }
20314
- }
20315
- return rebased;
20316
- }
20317
- function withRebasedEntries(raw, fromBase, toBase) {
20318
- const rebase = (entry) => entry.base === fromBase ? { ...entry, base: toBase } : entry;
20319
- return Array.isArray(raw) ? raw.map(rebase) : rebase(raw);
20320
- }
20321
- function rebasedText(content, fromBase, toBase, rebasedContent) {
20322
- const escaped = fromBase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20323
- const baseLine = new RegExp(`^([ \\t]*(?:-[ \\t]+)?base:[ \\t]*)(["']?)${escaped}\\2(?=[ \\t]*(?:#.*)?$)`, "gm");
20324
- const edited = content.replace(baseLine, (_line, key, quote) => `${key}${quote}${toBase}${quote}`);
20325
- try {
20326
- if (JSON.stringify(parseYaml(edited)) === JSON.stringify(rebasedContent)) return edited;
20327
- } catch {
20328
- }
20329
- return serializeYaml(rebasedContent);
20330
- }
20331
-
20332
- // src/core/surfaces.ts
20333
- var fs11 = __toESM(require("fs"));
20334
- var path13 = __toESM(require("path"));
20335
-
20336
- // src/core/openapi.ts
20337
- var yaml2 = __toESM(require("js-yaml"));
20338
-
20339
- // src/core/surfaces.ts
20340
- var SURFACES_DIRNAME = "surfaces";
20341
- function surfacesDir(rootDir) {
20342
- return path13.join(rootDir, ".wai", SURFACES_DIRNAME);
20343
- }
20344
- function listSnapshots(rootDir = getProjectRoot()) {
20345
- const dir = surfacesDir(rootDir);
20346
- if (!fs11.existsSync(dir)) return [];
20347
- const out = [];
20348
- for (const file of fs11.readdirSync(dir)) {
20349
- if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
20350
- try {
20351
- out.push(SurfaceSnapshotSchema.parse(readYamlFile(path13.join(dir, file))));
20352
- } catch {
20353
- }
20354
- }
20355
- return out;
20356
- }
20357
- function loadSurfaceSnapshots() {
20358
- return listSnapshots();
20359
- }
20360
- function listMountSnapshots(mounts) {
20361
- const bound2 = path13.resolve(getProjectRoot());
20362
- const out = [];
20363
- for (const namespace of mounts) {
20364
- const dir = resolveSubprojectForNamespace(namespace);
20365
- if (!dir || path13.resolve(dir) === bound2) continue;
20366
- const snapshots = listSnapshots(dir);
20367
- if (snapshots.length > 0) out.push({ namespace, snapshots });
20368
- }
20369
- return out;
20370
- }
20371
- function loadMountSurfaceSnapshots(mounts) {
20372
- return listMountSnapshots(mounts);
20373
- }
20374
-
20375
- // src/core/validation.ts
20376
- var path14 = __toESM(require("path"));
20377
- function loadProjectConfig2() {
20378
- return loadProjectConfig();
20379
- }
20380
- function projectPackSelections() {
20381
- try {
20382
- return (loadProjectConfig2()?.extensions?.packs ?? []).filter((e) => typeof e !== "string");
20383
- } catch {
20384
- return [];
20385
- }
20386
- }
20387
- var RESOLUTION_FAILURE_CODES = /* @__PURE__ */ new Set([
20388
- "UNDEFINED_TYPE_REFERENCE",
20389
- "INVALID_DEPENDENCY_REFERENCE",
20390
- "INVALID_TARGET_COMPONENT_REFERENCE",
20391
- "INVALID_SUBSYSTEM_REFERENCE",
20392
- "INVALID_TRUSTED_LINK",
20393
- "CROSS_TREE_REF_UNRESOLVED"
20394
- ]);
20395
- var SEVERITY_RANK = { off: 0, warning: 1, error: 2 };
20396
- function issueKey(issue2) {
20397
- return `${issue2.code}|${issue2.specId ?? ""}`;
20398
- }
20399
- function worstByKey(issues) {
20400
- const worst = /* @__PURE__ */ new Map();
20401
- for (const issue2 of issues) {
20402
- const key = issueKey(issue2);
20403
- const seen = worst.get(key);
20404
- if (!seen || SEVERITY_RANK[issue2.severity] > SEVERITY_RANK[seen]) worst.set(key, issue2.severity);
20405
- }
20406
- return worst;
20407
- }
20408
- function stricterSeverities(parent, child) {
20409
- const fromParent = parent?.sddRuleSeverity ?? {};
20410
- const fromChild = child?.sddRuleSeverity ?? {};
20411
- const codes = /* @__PURE__ */ new Set([...Object.keys(fromParent), ...Object.keys(fromChild)]);
20412
- if (codes.size === 0) return parent ?? child;
20413
- const defaults = new Map(knownIssueCodes().map((rc) => [rc.code, rc.defaultSeverity]));
20414
- const merged = {};
20415
- for (const code of codes) {
20416
- const p = fromParent[code] ?? defaults.get(code);
20417
- const c = fromChild[code] ?? defaults.get(code);
20418
- merged[code] = p === void 0 || c === void 0 ? p ?? c : SEVERITY_RANK[p] >= SEVERITY_RANK[c] ? p : c;
20419
- }
20420
- return { ...parent ?? child, sddRuleSeverity: merged };
20444
+ return { ...parent ?? child, sddRuleSeverity: merged };
20421
20445
  }
20422
20446
  function issue(severity, code, message, agentId, specId) {
20423
20447
  return { severity, code, message, agentId, specId };
@@ -20449,7 +20473,7 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
20449
20473
  const interfaces = loadInterfaceSpecs();
20450
20474
  const implementations = loadImplementationSpecs();
20451
20475
  const types = loadTypeSpecs();
20452
- const surfaceSnapshots = loadSurfaceSnapshots();
20476
+ const surfaceSnapshots = listSnapshots();
20453
20477
  const conformance = rules?.conformance;
20454
20478
  const codeModel = buildCodeModel(
20455
20479
  implementations,
@@ -20496,7 +20520,7 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
20496
20520
  for (const err of extensions.errors) {
20497
20521
  issues.push(issue("error", "EXTENSION_LOAD_ERROR", err));
20498
20522
  }
20499
- const mountSurfaceSnapshots = loadMountSurfaceSnapshots(
20523
+ const mountSurfaceSnapshots = listMountSnapshots(
20500
20524
  subsystems.filter((s) => s.projectPath).map((s) => s.id)
20501
20525
  );
20502
20526
  const roundTripIssues = dryRunSerializeSpecs(isSpecInScope);
@@ -20584,10 +20608,9 @@ function resolveThroughParent(boundRoot, treatAllAsComplete, childRules) {
20584
20608
  if (chain.length === 0) return null;
20585
20609
  const scope = chain.join("::");
20586
20610
  const inner = runWithProjectRoot(top, () => {
20587
- invalidateSpecCache();
20588
20611
  let governing = { rules: stricterSeverities(void 0, childRules) };
20589
20612
  try {
20590
- const config = loadProjectConfig2();
20613
+ const config = loadProjectConfig();
20591
20614
  if (config) governing = { rules: stricterSeverities(config.rules, childRules), projectType: config.projectType };
20592
20615
  } catch {
20593
20616
  }
@@ -21287,7 +21310,7 @@ function canonicalPath(target) {
21287
21310
  const rest = [];
21288
21311
  for (; ; ) {
21289
21312
  try {
21290
- fs12.lstatSync(existing);
21313
+ fs11.lstatSync(existing);
21291
21314
  break;
21292
21315
  } catch {
21293
21316
  const up = path15.dirname(existing);
@@ -21297,7 +21320,7 @@ function canonicalPath(target) {
21297
21320
  }
21298
21321
  }
21299
21322
  try {
21300
- return path15.join(fs12.realpathSync.native(existing), ...rest);
21323
+ return path15.join(fs11.realpathSync.native(existing), ...rest);
21301
21324
  } catch {
21302
21325
  return null;
21303
21326
  }
@@ -21389,7 +21412,7 @@ function inspectChainedRoots(rootDir = getProjectRoot()) {
21389
21412
  let reason;
21390
21413
  if (projectPathEscapesRoot(projectDir, projectPath, childDir)) reason = "escapes";
21391
21414
  else if (ancestors.has(chainDirKey(childDir))) reason = "cyclic";
21392
- else if (!fs12.existsSync(childDir)) reason = "missing";
21415
+ else if (!fs11.existsSync(childDir)) reason = "missing";
21393
21416
  else if (depth >= 32) reason = "too-deep";
21394
21417
  if (reason) {
21395
21418
  const mount = prefix ? qualifyDeclaredId(localId, prefix, true) : localId;
@@ -21441,7 +21464,10 @@ function stripNamespaceFromSubsystem(spec, prefix) {
21441
21464
  publicInterfaces: spec.publicInterfaces.map((pi) => ({
21442
21465
  ...pi,
21443
21466
  component: pi.component ? relMember(pi.component) : void 0,
21444
- interface: pi.interface ? relMember(pi.interface) : void 0
21467
+ interface: pi.interface ? relMember(pi.interface) : void 0,
21468
+ // consumers name SUBSYSTEMS — peers of this one, so they are relative to
21469
+ // the namespace the subsystem itself is declared in, never its members'.
21470
+ ...pi.consumers ? { consumers: pi.consumers.map((c) => c.includes("::") ? relativizeId(c, prefix) : c) } : {}
21445
21471
  })),
21446
21472
  lifecycle: spec.lifecycle?.map((le) => ({
21447
21473
  ...le,
@@ -21527,9 +21553,9 @@ function findOwner(id, components) {
21527
21553
  }
21528
21554
  function moveComponentFolder(fromDir, toDir) {
21529
21555
  if (path15.normalize(fromDir) === path15.normalize(toDir)) return false;
21530
- if (!fs12.existsSync(fromDir) || fs12.existsSync(toDir)) return false;
21556
+ if (!fs11.existsSync(fromDir) || fs11.existsSync(toDir)) return false;
21531
21557
  ensureDir(path15.dirname(toDir));
21532
- fs12.renameSync(fromDir, toDir);
21558
+ fs11.renameSync(fromDir, toDir);
21533
21559
  return true;
21534
21560
  }
21535
21561
  function formatZodIssues(error) {
@@ -21546,13 +21572,13 @@ var SIGNATURE_TTL_MS = 2e3;
21546
21572
  function computeSpecTreeSignature(dirs) {
21547
21573
  const parts = [];
21548
21574
  for (const dir of dirs) {
21549
- if (!fs12.existsSync(dir)) {
21575
+ if (!fs11.existsSync(dir)) {
21550
21576
  parts.push(`${dir}:missing`);
21551
21577
  continue;
21552
21578
  }
21553
21579
  for (const f of listSpecFiles(dir)) {
21554
21580
  try {
21555
- const st = fs12.statSync(f);
21581
+ const st = fs11.statSync(f);
21556
21582
  parts.push(`${f}:${st.mtimeMs}:${st.size}`);
21557
21583
  } catch {
21558
21584
  parts.push(`${f}:gone`);
@@ -21824,13 +21850,13 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
21824
21850
  const isPlain = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
21825
21851
  const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
21826
21852
  const at = (prefix, key) => prefix ? `${prefix}.${key}` : key;
21827
- const say = (path28, why) => {
21828
- out.push(`${path28} \u2014 ${why}`);
21853
+ const say = (path29, why) => {
21854
+ out.push(`${path29} \u2014 ${why}`);
21829
21855
  };
21830
- const walkValue = (d, m, s, path28, field) => {
21856
+ const walkValue = (d, m, s, path29, field) => {
21831
21857
  if (Array.isArray(d)) {
21832
21858
  if (!Array.isArray(m)) {
21833
- say(path28, "the level does not have this field, so the write dropped it");
21859
+ say(path29, "the level does not have this field, so the write dropped it");
21834
21860
  return;
21835
21861
  }
21836
21862
  if (field === "narrative" && d.some(carriesEditMarker)) return;
@@ -21838,32 +21864,32 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
21838
21864
  if (carriesEditMarker(element)) continue;
21839
21865
  const key = reportIdentityOf(field, element);
21840
21866
  if (key === null) {
21841
- if (!same(m, d)) say(path28, "the level does not keep this value, so the write dropped it");
21842
- else if (same(s, d)) say(path28, "the stored spec already held this value");
21867
+ if (!same(m, d)) say(path29, "the level does not keep this value, so the write dropped it");
21868
+ else if (same(s, d)) say(path29, "the stored spec already held this value");
21843
21869
  return;
21844
21870
  }
21845
21871
  const mMatch = m.find((i) => reportIdentityOf(field, i) === key);
21846
21872
  const sMatch = Array.isArray(s) ? s.find((i) => reportIdentityOf(field, i) === key) : void 0;
21847
21873
  if (mMatch === void 0) {
21848
- say(at(path28, key), "the write did not store this element");
21874
+ say(at(path29, key), "the write did not store this element");
21849
21875
  continue;
21850
21876
  }
21851
- walk2(element, mMatch, sMatch, at(path28, key), field);
21877
+ walk2(element, mMatch, sMatch, at(path29, key), field);
21852
21878
  }
21853
21879
  return;
21854
21880
  }
21855
21881
  if (isPlain(d)) {
21856
21882
  if (!isPlain(m)) {
21857
- say(path28, "the level does not have this field, so the write dropped it");
21883
+ say(path29, "the level does not have this field, so the write dropped it");
21858
21884
  return;
21859
21885
  }
21860
- walk2(d, m, s, path28, field);
21886
+ walk2(d, m, s, path29, field);
21861
21887
  return;
21862
21888
  }
21863
- if (!same(m, d)) say(path28, "the write did not store it \u2014 the level's schema does not keep this value");
21864
- else if (same(s, d)) say(path28, "the stored spec already held this value");
21889
+ if (!same(m, d)) say(path29, "the write did not store it \u2014 the level's schema does not keep this value");
21890
+ else if (same(s, d)) say(path29, "the stored spec already held this value");
21865
21891
  };
21866
- function walk2(d, m, s, path28, container) {
21892
+ function walk2(d, m, s, path29, container) {
21867
21893
  for (const [key, value] of Object.entries(d)) {
21868
21894
  if (DELTA_VERBS.has(key) || JUMP_LABEL_TWINS.has(key)) continue;
21869
21895
  if (key === "label" && LABEL_TWIN_CONTAINERS.has(container)) continue;
@@ -21873,13 +21899,13 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
21873
21899
  if (typeof name !== "string") continue;
21874
21900
  const had = isPlain(s) && s[name] !== void 0;
21875
21901
  const still = isPlain(m) && m[name] !== void 0;
21876
- if (!had) say(at(path28, name), "unset named a field the stored spec did not have");
21877
- else if (still) say(at(path28, name), "unset did not remove it");
21902
+ if (!had) say(at(path29, name), "unset named a field the stored spec did not have");
21903
+ else if (still) say(at(path29, name), "unset did not remove it");
21878
21904
  }
21879
21905
  continue;
21880
21906
  }
21881
21907
  if (value === void 0 || value === null) continue;
21882
- const here = at(path28, key);
21908
+ const here = at(path29, key);
21883
21909
  const mValue = isPlain(m) ? m[key] : void 0;
21884
21910
  const sValue = isPlain(s) ? s[key] : void 0;
21885
21911
  if (mValue === void 0) {
@@ -21905,6 +21931,15 @@ var SpecWorkspace = class {
21905
21931
  this.rootDir = path15.resolve(rootDir);
21906
21932
  this.paths = aiPathsAt(this.rootDir);
21907
21933
  }
21934
+ /**
21935
+ * The next scan re-verifies the tree's file signature whatever the TTL says.
21936
+ * Called when the bound project root switches to this workspace: a caller that
21937
+ * just bound a root reads it as it is NOW, even if another process changed it
21938
+ * a moment after this workspace last looked.
21939
+ */
21940
+ expireFreshness() {
21941
+ this.lastSignatureCheckMs = 0;
21942
+ }
21908
21943
  invalidate() {
21909
21944
  this.cachedIndex = null;
21910
21945
  this.cachedRecursive = null;
@@ -22049,7 +22084,10 @@ var SpecWorkspace = class {
22049
22084
  publicInterfaces: sub.publicInterfaces.map((p) => ({
22050
22085
  ...p,
22051
22086
  component: p.component ? qualifyId(p.component, componentPrefix, this.rootSubsystems) : void 0,
22052
- interface: p.interface ? qualifyId(p.interface, componentPrefix, this.rootSubsystems) : void 0
22087
+ interface: p.interface ? qualifyId(p.interface, componentPrefix, this.rootSubsystems) : void 0,
22088
+ // Each consumer is a subsystem reference: qualified into the namespace
22089
+ // this subsystem is declared in, like a component's `subsystem` field.
22090
+ ...p.consumers ? { consumers: p.consumers.map((c) => qualifySubsystemRef(c, namespacePrefix, this.rootSubsystems)) } : {}
22053
22091
  })),
22054
22092
  lifecycle: sub.lifecycle?.map((le) => ({
22055
22093
  ...le,
@@ -22155,7 +22193,7 @@ var SpecWorkspace = class {
22155
22193
  });
22156
22194
  continue;
22157
22195
  }
22158
- if (!fs12.existsSync(childDir)) {
22196
+ if (!fs11.existsSync(childDir)) {
22159
22197
  this.loaderIssues.push({
22160
22198
  severity: "error",
22161
22199
  code: "SUBPROJECT_NOT_FOUND",
@@ -23159,8 +23197,8 @@ var SpecWorkspace = class {
23159
23197
  }
23160
23198
  }
23161
23199
  for (const file of files) {
23162
- if (fs12.existsSync(file)) {
23163
- snapshot.set(file, fs12.readFileSync(file, "utf8"));
23200
+ if (fs11.existsSync(file)) {
23201
+ snapshot.set(file, fs11.readFileSync(file, "utf8"));
23164
23202
  }
23165
23203
  }
23166
23204
  return snapshot;
@@ -23663,7 +23701,8 @@ var SpecWorkspace = class {
23663
23701
  out.publicInterfaces = out.publicInterfaces.map((pi) => ({
23664
23702
  ...pi,
23665
23703
  ...typeof pi?.component === "string" ? { component: qm(pi.component) } : {},
23666
- ...typeof pi?.interface === "string" ? { interface: qm(pi.interface) } : {}
23704
+ ...typeof pi?.interface === "string" ? { interface: qm(pi.interface) } : {},
23705
+ ...Array.isArray(pi?.consumers) ? { consumers: pi.consumers.map((c) => typeof c === "string" ? qualifySubsystemRef(c, prefix, this.rootSubsystems) : c) } : {}
23667
23706
  }));
23668
23707
  }
23669
23708
  if (Array.isArray(out.lifecycle)) {
@@ -24228,8 +24267,15 @@ function workspaceFor(rootDir) {
24228
24267
  }
24229
24268
  return ws;
24230
24269
  }
24270
+ var lastServedBinding = null;
24231
24271
  function current() {
24232
- return workspaceFor(getProjectRoot());
24272
+ const ws = workspaceFor(getProjectRoot());
24273
+ const binding = currentRootBinding() ?? ws.rootDir;
24274
+ if (binding !== lastServedBinding) {
24275
+ lastServedBinding = binding;
24276
+ ws.expireFreshness();
24277
+ }
24278
+ return ws;
24233
24279
  }
24234
24280
  function invalidateSpecCache() {
24235
24281
  for (const ws of workspaces.values()) ws.invalidate();
@@ -24274,9 +24320,6 @@ function loadSubsystemSpec(id) {
24274
24320
  function saveSubsystemSpec(spec) {
24275
24321
  current().saveSubsystemSpec(spec);
24276
24322
  }
24277
- function deleteSubsystemSpec(id) {
24278
- return current().deleteSubsystemSpec(id);
24279
- }
24280
24323
  function loadComponentSpecs() {
24281
24324
  return current().loadComponentSpecs();
24282
24325
  }
@@ -24286,9 +24329,6 @@ function loadComponentSpec(id) {
24286
24329
  function saveComponentSpec(spec, opts) {
24287
24330
  return current().saveComponentSpec(spec, opts);
24288
24331
  }
24289
- function deleteComponentSpec(id) {
24290
- return current().deleteComponentSpec(id);
24291
- }
24292
24332
  function loadInterfaceSpecs() {
24293
24333
  return current().loadInterfaceSpecs();
24294
24334
  }
@@ -24298,9 +24338,6 @@ function loadInterfaceSpec(id) {
24298
24338
  function saveInterfaceSpec(spec, opts) {
24299
24339
  return current().saveInterfaceSpec(spec, opts);
24300
24340
  }
24301
- function deleteInterfaceSpec(id) {
24302
- return current().deleteInterfaceSpec(id);
24303
- }
24304
24341
  function loadImplementationSpecs() {
24305
24342
  return current().loadImplementationSpecs();
24306
24343
  }
@@ -24310,18 +24347,12 @@ function loadImplementationSpec(id) {
24310
24347
  function saveImplementationSpec(spec, opts) {
24311
24348
  return current().saveImplementationSpec(spec, opts);
24312
24349
  }
24313
- function deleteImplementationSpec(id) {
24314
- return current().deleteImplementationSpec(id);
24315
- }
24316
24350
  function loadTypeSpecs() {
24317
24351
  return current().loadTypeSpecs();
24318
24352
  }
24319
24353
  function loadTypeSpec(id) {
24320
24354
  return current().loadTypeSpec(id);
24321
24355
  }
24322
- function saveTypeSpec(spec) {
24323
- return current().saveTypeSpec(spec);
24324
- }
24325
24356
  function dryRunSerializeSpecs(include) {
24326
24357
  return current().dryRunSerializeSpecs(include);
24327
24358
  }
@@ -24341,7 +24372,7 @@ function consumedSurfaceInputsAt(rootDir) {
24341
24372
  const dir = path15.join(rootDir, ".wai", "surfaces");
24342
24373
  if (!pathExists(dir)) return [];
24343
24374
  const keys = [];
24344
- for (const file of fs12.readdirSync(dir)) {
24375
+ for (const file of fs11.readdirSync(dir)) {
24345
24376
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
24346
24377
  try {
24347
24378
  keys.push(snapshotInputKey(SurfaceSnapshotSchema.parse(readYamlFile(path15.join(dir, file)))));
@@ -24397,15 +24428,15 @@ function computeStateIdAt(root) {
24397
24428
  return `${s.algorithm}:${s.digest}`;
24398
24429
  });
24399
24430
  }
24400
- function deleteTypeSpec(id) {
24401
- return current().deleteTypeSpec(id);
24402
- }
24403
24431
  function specPathsInScope(scopeSubsystem) {
24404
24432
  return current().specPathsInScope(scopeSubsystem);
24405
24433
  }
24406
24434
  function snapshotSpecFiles() {
24407
24435
  return current().snapshotSpecFiles();
24408
24436
  }
24437
+ function findLegacySpecFiles() {
24438
+ return current().findLegacySpecFiles();
24439
+ }
24409
24440
  function loadSpec(kind, id) {
24410
24441
  const workspace = current();
24411
24442
  return workspace.load(kind, id);
@@ -24512,7 +24543,7 @@ function unregisterDomain(id) {
24512
24543
  }
24513
24544
 
24514
24545
  // src/core/provision.ts
24515
- var fs13 = __toESM(require("fs"));
24546
+ var fs12 = __toESM(require("fs"));
24516
24547
  var path17 = __toESM(require("path"));
24517
24548
  function defaultProjectConfig(name, now) {
24518
24549
  return {
@@ -24561,7 +24592,7 @@ function provisionProject(name) {
24561
24592
  function ensureProjectInitialized(fallbackName) {
24562
24593
  const now = (/* @__PURE__ */ new Date()).toISOString();
24563
24594
  const paths = aiPathsAt(getProjectRoot());
24564
- const hasSystem = fs13.existsSync(paths.specsSystem());
24595
+ const hasSystem = fs12.existsSync(paths.specsSystem());
24565
24596
  let name = fallbackName;
24566
24597
  if (hasSystem) {
24567
24598
  const existing = loadSystemSpec();
@@ -24580,10 +24611,41 @@ function ensureProjectInitialized(fallbackName) {
24580
24611
  if (wroteConfig || wroteSystem) invalidateSpecCache();
24581
24612
  return { wroteConfig, wroteSystem };
24582
24613
  }
24614
+ function walkChainedSubprojects(projectRoot, onChild) {
24615
+ const visited = /* @__PURE__ */ new Set();
24616
+ const walk2 = (dir) => {
24617
+ const resolved = path17.resolve(dir);
24618
+ if (visited.has(resolved)) return;
24619
+ visited.add(resolved);
24620
+ const specsDir = aiPathsAt(dir).specsDir();
24621
+ if (!fs12.existsSync(specsDir)) return;
24622
+ for (const file of listFilesRecursive(specsDir, ".yaml")) {
24623
+ let raw;
24624
+ try {
24625
+ raw = readYamlFile(file);
24626
+ } catch {
24627
+ continue;
24628
+ }
24629
+ if (!(raw && typeof raw === "object" && "parentSystem" in raw)) continue;
24630
+ const pp = raw.projectPath;
24631
+ if (typeof pp !== "string" || pp.trim() === "") continue;
24632
+ let childDir;
24633
+ try {
24634
+ childDir = assertContainedProjectPath(dir, pp);
24635
+ } catch {
24636
+ continue;
24637
+ }
24638
+ const id = raw.id;
24639
+ onChild(childDir, typeof id === "string" ? id : path17.basename(childDir));
24640
+ walk2(childDir);
24641
+ }
24642
+ };
24643
+ walk2(projectRoot);
24644
+ }
24583
24645
  function listDirectChainedSubprojects(projectRoot) {
24584
24646
  const out = [];
24585
24647
  const specsDir = aiPathsAt(projectRoot).specsDir();
24586
- if (!fs13.existsSync(specsDir)) return out;
24648
+ if (!fs12.existsSync(specsDir)) return out;
24587
24649
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
24588
24650
  let raw;
24589
24651
  try {
@@ -24605,6 +24667,28 @@ function listDirectChainedSubprojects(projectRoot) {
24605
24667
  }
24606
24668
  return out;
24607
24669
  }
24670
+ function childHasSpecsButNoConfig(childDir) {
24671
+ return fs12.existsSync(aiPathsAt(childDir).specsDir()) && !projectConfigRepositoryAt(childDir).exists();
24672
+ }
24673
+ function findChainingSubprojectsMissingConfig(projectRoot) {
24674
+ const missing = [];
24675
+ walkChainedSubprojects(projectRoot, (childDir) => {
24676
+ if (childHasSpecsButNoConfig(childDir)) missing.push(childDir);
24677
+ });
24678
+ return missing;
24679
+ }
24680
+ function backfillChainedSubprojectConfigs(projectRoot) {
24681
+ const backfilled = [];
24682
+ walkChainedSubprojects(projectRoot, (childDir, subsystemId) => {
24683
+ if (childHasSpecsButNoConfig(childDir)) {
24684
+ runWithProjectRoot(childDir, () => {
24685
+ ensureProjectInitialized(subsystemId);
24686
+ });
24687
+ backfilled.push(childDir);
24688
+ }
24689
+ });
24690
+ return backfilled;
24691
+ }
24608
24692
  function createChainedSubsystem(subsystem, projectName) {
24609
24693
  if (!subsystem.projectPath || subsystem.projectPath.trim() === "") {
24610
24694
  throw new WaironError("projectPath is required to create a chained subsystem.");
@@ -24633,14 +24717,14 @@ function moveSubsystemProject(subsystemId, newProjectPath) {
24633
24717
  const oldDir = assertContainedProjectPath(root, sub.projectPath);
24634
24718
  const newDir = assertContainedProjectPath(root, nextPath);
24635
24719
  if (oldDir !== newDir) {
24636
- if (!fs13.existsSync(oldDir)) {
24720
+ if (!fs12.existsSync(oldDir)) {
24637
24721
  throw new WaironError(`Subproject directory not found at its current path: ${oldDir}`);
24638
24722
  }
24639
- if (fs13.existsSync(newDir)) {
24723
+ if (fs12.existsSync(newDir)) {
24640
24724
  throw new WaironError(`Target directory already exists: ${newDir}`);
24641
24725
  }
24642
24726
  ensureDir(path17.dirname(newDir));
24643
- fs13.renameSync(oldDir, newDir);
24727
+ fs12.renameSync(oldDir, newDir);
24644
24728
  }
24645
24729
  saveSubsystemSpec({ ...sub, projectPath: nextPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
24646
24730
  invalidateSpecCache();
@@ -24664,13 +24748,13 @@ function externalizeSubsystem(subsystemId, projectPath) {
24664
24748
  const parentRoot = getProjectRoot();
24665
24749
  const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
24666
24750
  const fooDir = path17.join(parentSpecsDir, subsystemId);
24667
- if (!fs13.existsSync(fooDir)) {
24751
+ if (!fs12.existsSync(fooDir)) {
24668
24752
  throw new WaironError(`subsystem specs directory not found: ${fooDir}`);
24669
24753
  }
24670
24754
  const relPath = toPosixPath(projectPath);
24671
24755
  const childDir = assertContainedProjectPath(parentRoot, relPath);
24672
24756
  const childFooDir = path17.join(childDir, ".wai", "specs", subsystemId);
24673
- if (fs13.existsSync(childFooDir)) {
24757
+ if (fs12.existsSync(childFooDir)) {
24674
24758
  throw new WaironError(`target already contains a "${subsystemId}" subsystem: ${childFooDir}`);
24675
24759
  }
24676
24760
  const renameMap = buildRenameMap(
@@ -24686,7 +24770,7 @@ function externalizeSubsystem(subsystemId, projectPath) {
24686
24770
  saveSystemSpec(bootstrapSystemSpec(childSystemName, now));
24687
24771
  });
24688
24772
  ensureDir(path17.dirname(childFooDir));
24689
- fs13.renameSync(fooDir, childFooDir);
24773
+ fs12.renameSync(fooDir, childFooDir);
24690
24774
  rebaseImplementationPaths(childFooDir, parentRoot, childDir);
24691
24775
  patchSubsystemIndex(path17.join(childFooDir, ".index.yaml"), (s) => {
24692
24776
  s.parentSystem = childSystemName;
@@ -24722,7 +24806,7 @@ function internalizeSubsystem(subsystemId) {
24722
24806
  const childDir = assertContainedProjectPath(parentRoot, foo.projectPath);
24723
24807
  const childWai = path17.join(childDir, ".wai");
24724
24808
  const childFooDir = path17.join(childDir, ".wai", "specs", subsystemId);
24725
- if (!fs13.existsSync(childFooDir)) {
24809
+ if (!fs12.existsSync(childFooDir)) {
24726
24810
  throw new WaironError(`external subproject missing subsystem "${subsystemId}": ${childFooDir}`);
24727
24811
  }
24728
24812
  const childOwnSubs = runWithProjectRoot(childDir, () => loadSubsystemSpecs()).filter((s) => !s.id.includes("::"));
@@ -24736,15 +24820,15 @@ function internalizeSubsystem(subsystemId) {
24736
24820
  );
24737
24821
  const parentSystemName = loadSystemSpec()?.name ?? foo.parentSystem;
24738
24822
  const fooDir = path17.join(parentSpecsDir, subsystemId);
24739
- fs13.rmSync(fooDir, { recursive: true, force: true });
24823
+ fs12.rmSync(fooDir, { recursive: true, force: true });
24740
24824
  ensureDir(path17.dirname(fooDir));
24741
- fs13.renameSync(childFooDir, fooDir);
24825
+ fs12.renameSync(childFooDir, fooDir);
24742
24826
  rebaseImplementationPaths(fooDir, childDir, parentRoot);
24743
24827
  patchSubsystemIndex(path17.join(fooDir, ".index.yaml"), (s) => {
24744
24828
  s.parentSystem = parentSystemName;
24745
24829
  delete s.projectPath;
24746
24830
  });
24747
- fs13.rmSync(childWai, { recursive: true, force: true });
24831
+ fs12.rmSync(childWai, { recursive: true, force: true });
24748
24832
  rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
24749
24833
  rebaseMovedRefs(fooDir, subsystemId, "outOf");
24750
24834
  invalidateSpecCache();
@@ -24839,7 +24923,7 @@ function rebaseImplementationPaths(specsDir, fromRoot, toRoot) {
24839
24923
  }
24840
24924
  }
24841
24925
  function patchSubsystemIndex(indexPath, mutate) {
24842
- if (!fs13.existsSync(indexPath)) return;
24926
+ if (!fs12.existsSync(indexPath)) return;
24843
24927
  const raw = readYamlFile(indexPath);
24844
24928
  mutate(raw);
24845
24929
  raw.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -24901,7 +24985,7 @@ function moveRenamedSpecs(componentId, newId, component, movingInterface, moving
24901
24985
  if (movedImplementation) {
24902
24986
  const oldFile = getImplementationPath(movedImplementation.id);
24903
24987
  if (path17.resolve(getImplementationPath(implementationId, movedImplementation.contract)) === path17.resolve(oldFile)) {
24904
- fs13.unlinkSync(oldFile);
24988
+ fs12.unlinkSync(oldFile);
24905
24989
  }
24906
24990
  saveImplementationSpec({ ...movedImplementation, id: implementationId });
24907
24991
  }
@@ -24911,17 +24995,17 @@ function moveRenamedSpecs(componentId, newId, component, movingInterface, moving
24911
24995
  invalidateSpecCache();
24912
24996
  }
24913
24997
  function removeSpecFile2(file, id) {
24914
- if (!fs13.existsSync(file)) return;
24998
+ if (!fs12.existsSync(file)) return;
24915
24999
  try {
24916
25000
  if (readYamlFile(file)?.id !== id) return;
24917
25001
  } catch {
24918
25002
  return;
24919
25003
  }
24920
- fs13.unlinkSync(file);
25004
+ fs12.unlinkSync(file);
24921
25005
  const specsRoot = path17.resolve(aiPathsAt(getProjectRoot()).specsDir());
24922
25006
  for (let dir = path17.dirname(path17.resolve(file)); dir !== specsRoot && isWithinDir(specsRoot, dir); dir = path17.dirname(dir)) {
24923
- if (fs13.readdirSync(dir).length > 0) break;
24924
- fs13.rmdirSync(dir);
25007
+ if (fs12.readdirSync(dir).length > 0) break;
25008
+ fs12.rmdirSync(dir);
24925
25009
  }
24926
25010
  }
24927
25011
  var METHOD_NAME = /^[a-z][a-zA-Z0-9]*$/;
@@ -25032,7 +25116,7 @@ function identifierPattern(methodName) {
25032
25116
  }
25033
25117
 
25034
25118
  // src/core/detection.ts
25035
- var fs14 = __toESM(require("fs"));
25119
+ var fs13 = __toESM(require("fs"));
25036
25120
  var path18 = __toESM(require("path"));
25037
25121
  var PACKAGE_MARKERS = [
25038
25122
  "package.json",
@@ -25084,7 +25168,7 @@ function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
25084
25168
  });
25085
25169
  }
25086
25170
  function parseGitmodules(filePath) {
25087
- const content = fs14.readFileSync(filePath, "utf-8");
25171
+ const content = fs13.readFileSync(filePath, "utf-8");
25088
25172
  const entries = [];
25089
25173
  let current2 = {};
25090
25174
  for (const line of content.split("\n")) {
@@ -25107,7 +25191,7 @@ function parseGitmodules(filePath) {
25107
25191
  }
25108
25192
  function detectGitSubmodules(projectRoot) {
25109
25193
  const gitmodulesPath = path18.join(projectRoot, ".gitmodules");
25110
- if (!fs14.existsSync(gitmodulesPath)) return [];
25194
+ if (!fs13.existsSync(gitmodulesPath)) return [];
25111
25195
  return parseGitmodules(gitmodulesPath).map((entry) => ({
25112
25196
  suggestedId: pathToId(entry.path),
25113
25197
  suggestedName: pathToName(entry.path),
@@ -25125,7 +25209,7 @@ function walkForGit(projectRoot, currentDir, depth, results) {
25125
25209
  if (depth > MAX_SCAN_DEPTH) return;
25126
25210
  let entries;
25127
25211
  try {
25128
- entries = fs14.readdirSync(currentDir, { withFileTypes: true });
25212
+ entries = fs13.readdirSync(currentDir, { withFileTypes: true });
25129
25213
  } catch {
25130
25214
  return;
25131
25215
  }
@@ -25136,7 +25220,7 @@ function walkForGit(projectRoot, currentDir, depth, results) {
25136
25220
  const relPath = normalizePath(path18.relative(projectRoot, fullPath));
25137
25221
  if (relPath === "" || relPath === ".") continue;
25138
25222
  const gitPath = path18.join(fullPath, ".git");
25139
- if (fs14.existsSync(gitPath)) {
25223
+ if (fs13.existsSync(gitPath)) {
25140
25224
  results.push({
25141
25225
  suggestedId: pathToId(relPath),
25142
25226
  suggestedName: pathToName(relPath),
@@ -25158,7 +25242,7 @@ function walkForPackages(projectRoot, currentDir, depth, results) {
25158
25242
  if (depth > MAX_SCAN_DEPTH) return;
25159
25243
  let entries;
25160
25244
  try {
25161
- entries = fs14.readdirSync(currentDir, { withFileTypes: true });
25245
+ entries = fs13.readdirSync(currentDir, { withFileTypes: true });
25162
25246
  } catch {
25163
25247
  return;
25164
25248
  }
@@ -25168,7 +25252,7 @@ function walkForPackages(projectRoot, currentDir, depth, results) {
25168
25252
  const fullPath = path18.join(currentDir, entry.name);
25169
25253
  const relPath = normalizePath(path18.relative(projectRoot, fullPath));
25170
25254
  if (relPath === "" || relPath === ".") continue;
25171
- const hasMarker = PACKAGE_MARKERS.some((m) => fs14.existsSync(path18.join(fullPath, m)));
25255
+ const hasMarker = PACKAGE_MARKERS.some((m) => fs13.existsSync(path18.join(fullPath, m)));
25172
25256
  if (hasMarker) {
25173
25257
  results.push({
25174
25258
  suggestedId: pathToId(relPath),
@@ -25193,19 +25277,168 @@ function normalizePath(p) {
25193
25277
  return p.replace(/\\/g, "/");
25194
25278
  }
25195
25279
 
25280
+ // src/core/variants.ts
25281
+ var fs14 = __toESM(require("fs"));
25282
+ var os3 = __toESM(require("os"));
25283
+ var path19 = __toESM(require("path"));
25284
+ var import_zod10 = require("zod");
25285
+ var VariantDefSchema = import_zod10.z.object({
25286
+ /** Variant id referenced by a component's `variant` (e.g. "publisher", "org/external-config-adapter"). */
25287
+ id: import_zod10.z.string().min(1),
25288
+ /** The core stereotype this variant specializes — authoritative for generic semantics (Adapter, Orchestrator, …). */
25289
+ base: import_zod10.z.string().min(1),
25290
+ /** How to implement a component of this variant — the recipe the implementer follows and reuses across same-variant components. */
25291
+ guidance: import_zod10.z.string().min(1),
25292
+ /** Optional: this variant only applies for the given target language (else it applies everywhere). */
25293
+ target: import_zod10.z.string().optional(),
25294
+ /** Optional: this variant only applies under the given architectural profile. */
25295
+ profile: import_zod10.z.string().optional()
25296
+ });
25297
+ function globalVariantsDir() {
25298
+ return process.env.WAIRON_VARIANTS_DIR ?? path19.join(os3.homedir(), ".wairon", "variants");
25299
+ }
25300
+ function builtinVariantsDir() {
25301
+ const candidates = [
25302
+ path19.resolve(__dirname, "..", "templates", "variants"),
25303
+ // src/core & dist/cli
25304
+ path19.resolve(__dirname, "templates", "variants")
25305
+ // dist (library entry)
25306
+ ];
25307
+ return candidates.find((dir) => fs14.existsSync(dir)) ?? candidates[0];
25308
+ }
25309
+ function projectVariantsDir() {
25310
+ return path19.join(getProjectRoot(), ".wai", "variants");
25311
+ }
25312
+ function variantFiles(dir) {
25313
+ let entries;
25314
+ try {
25315
+ entries = fs14.readdirSync(dir, { withFileTypes: true });
25316
+ } catch {
25317
+ return [];
25318
+ }
25319
+ return entries.filter((e) => e.isFile() && /\.ya?ml$/i.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => path19.join(dir, e.name));
25320
+ }
25321
+ function parseVariants(raw) {
25322
+ if (raw == null) return [];
25323
+ return (Array.isArray(raw) ? raw : [raw]).map((item) => VariantDefSchema.parse(item));
25324
+ }
25325
+ function reportSkippedFile(file, err) {
25326
+ console.error(`[variants] skipped "${file}": ${err instanceof Error ? err.message : String(err)}`);
25327
+ }
25328
+ function readVariantsDir(dir) {
25329
+ const out = [];
25330
+ for (const file of variantFiles(dir)) {
25331
+ try {
25332
+ out.push(...parseVariants(readYamlFile(file)));
25333
+ } catch (err) {
25334
+ reportSkippedFile(file, err);
25335
+ }
25336
+ }
25337
+ return out;
25338
+ }
25339
+ function mergeById(...layers) {
25340
+ const byId = /* @__PURE__ */ new Map();
25341
+ for (const layer of layers) {
25342
+ for (const v of layer) byId.set(v.id, v);
25343
+ }
25344
+ return [...byId.values()];
25345
+ }
25346
+ function resolveVariantGuidance(component, allComponents, variantsById) {
25347
+ if (!component.variant) return null;
25348
+ const def = variantsById.get(component.variant);
25349
+ if (!def) return null;
25350
+ return {
25351
+ variant: component.variant,
25352
+ base: def.base,
25353
+ guidance: def.guidance,
25354
+ siblings: allComponents.filter((o) => o.variant === component.variant && o.id !== component.id).map((o) => o.id),
25355
+ ...def.target ? { target: def.target } : {},
25356
+ ...def.profile ? { profile: def.profile } : {}
25357
+ };
25358
+ }
25359
+ function composeVariantGuidance(comps, allComponents, variantsById) {
25360
+ const resolved = comps.map((c) => ({ id: c.id, guidance: resolveVariantGuidance(c, allComponents, variantsById) })).filter((r) => r.guidance !== null);
25361
+ if (resolved.length === 0) return "";
25362
+ const lines = [
25363
+ "## Component variants \u2014 reuse the shared approach",
25364
+ "",
25365
+ "One or more of your components declare a variant \u2014 a base-anchored kind with implementation guidance. Implement every component of the same variant alike, reusing one shared approach instead of reinventing it per instance:",
25366
+ ""
25367
+ ];
25368
+ for (const { id, guidance } of resolved) {
25369
+ let line = `- **${id}** \u2014 variant \`${guidance.variant}\` (a kind of ${guidance.base}): ${guidance.guidance}`;
25370
+ if (guidance.siblings.length > 0) {
25371
+ line += ` Same-variant components elsewhere: ${guidance.siblings.join(", ")} \u2014 implement them consistently, reusing the same logic/concept.`;
25372
+ }
25373
+ lines.push(line);
25374
+ }
25375
+ return lines.join("\n");
25376
+ }
25377
+ function loadProjectVariants() {
25378
+ try {
25379
+ return mergeById(
25380
+ readVariantsDir(builtinVariantsDir()),
25381
+ readVariantsDir(globalVariantsDir()),
25382
+ readVariantsDir(projectVariantsDir())
25383
+ );
25384
+ } catch {
25385
+ return [];
25386
+ }
25387
+ }
25388
+ function listProjectVariants() {
25389
+ return mergeById(readVariantsDir(projectVariantsDir()));
25390
+ }
25391
+ function rebaseProjectVariants(fromBase, toBase) {
25392
+ const rebased = [];
25393
+ for (const file of variantFiles(projectVariantsDir())) {
25394
+ const content = readFileOrNull(file);
25395
+ if (content === null) continue;
25396
+ let raw;
25397
+ let variants;
25398
+ try {
25399
+ raw = parseYaml(content, file);
25400
+ variants = parseVariants(raw);
25401
+ } catch (err) {
25402
+ reportSkippedFile(file, err);
25403
+ continue;
25404
+ }
25405
+ const matching = variants.filter((v) => v.base === fromBase);
25406
+ if (matching.length === 0) continue;
25407
+ writeFile(file, rebasedText(content, fromBase, toBase, withRebasedEntries(raw, fromBase, toBase)));
25408
+ for (const v of matching) {
25409
+ if (!rebased.includes(v.id)) rebased.push(v.id);
25410
+ }
25411
+ }
25412
+ return rebased;
25413
+ }
25414
+ function withRebasedEntries(raw, fromBase, toBase) {
25415
+ const rebase = (entry) => entry.base === fromBase ? { ...entry, base: toBase } : entry;
25416
+ return Array.isArray(raw) ? raw.map(rebase) : rebase(raw);
25417
+ }
25418
+ function rebasedText(content, fromBase, toBase, rebasedContent) {
25419
+ const escaped = fromBase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25420
+ const baseLine = new RegExp(`^([ \\t]*(?:-[ \\t]+)?base:[ \\t]*)(["']?)${escaped}\\2(?=[ \\t]*(?:#.*)?$)`, "gm");
25421
+ const edited = content.replace(baseLine, (_line, key, quote) => `${key}${quote}${toBase}${quote}`);
25422
+ try {
25423
+ if (JSON.stringify(parseYaml(edited)) === JSON.stringify(rebasedContent)) return edited;
25424
+ } catch {
25425
+ }
25426
+ return serializeYaml(rebasedContent);
25427
+ }
25428
+
25196
25429
  // src/core/agent_resolver.ts
25197
- var path20 = __toESM(require("path"));
25430
+ var path21 = __toESM(require("path"));
25198
25431
  var fs15 = __toESM(require("fs"));
25199
25432
 
25200
25433
  // src/core/templates.ts
25201
- var path19 = __toESM(require("path"));
25434
+ var path20 = __toESM(require("path"));
25202
25435
  function builtinTemplatesDir() {
25203
- return path19.resolve(__dirname, "..", "templates");
25436
+ return path20.resolve(__dirname, "..", "templates");
25204
25437
  }
25205
25438
  function loadTemplate(id, globalOverride) {
25206
25439
  const dirs = templateSearchDirs(globalOverride);
25207
25440
  for (const dir of dirs) {
25208
- const filePath = path19.join(dir, `${id}.yaml`);
25441
+ const filePath = path20.join(dir, `${id}.yaml`);
25209
25442
  if (pathExists(filePath)) {
25210
25443
  const raw = readYamlFile(filePath);
25211
25444
  return { ...TemplateSchema.parse(raw), templateName: id };
@@ -25214,7 +25447,7 @@ function loadTemplate(id, globalOverride) {
25214
25447
  throw new TemplateNotFoundError(id);
25215
25448
  }
25216
25449
  function loadAgentOverride(agentId) {
25217
- const filePath = path19.join(AI_PATHS.root(), "agents", `${agentId}.md`);
25450
+ const filePath = path20.join(AI_PATHS.root(), "agents", `${agentId}.md`);
25218
25451
  return readFileOrNull(filePath);
25219
25452
  }
25220
25453
  function templateSearchDirs(globalOverride) {
@@ -25366,7 +25599,7 @@ function resolveBudget(profile, config, agentId) {
25366
25599
  var projectFilesCache = /* @__PURE__ */ new Map();
25367
25600
  function listFilesRecursiveSafe(dirPath, ext) {
25368
25601
  if (!fs15.existsSync(dirPath)) return [];
25369
- const nameLower = path20.basename(dirPath).toLowerCase();
25602
+ const nameLower = path21.basename(dirPath).toLowerCase();
25370
25603
  const IGNORED_DIRS = /* @__PURE__ */ new Set([
25371
25604
  "node_modules",
25372
25605
  "target",
@@ -25384,7 +25617,7 @@ function listFilesRecursiveSafe(dirPath, ext) {
25384
25617
  const entries = fs15.readdirSync(dirPath, { withFileTypes: true });
25385
25618
  const files = [];
25386
25619
  for (const entry of entries) {
25387
- const fullPath = path20.join(dirPath, entry.name);
25620
+ const fullPath = path21.join(dirPath, entry.name);
25388
25621
  if (entry.isDirectory()) {
25389
25622
  files.push(...listFilesRecursiveSafe(fullPath, ext));
25390
25623
  } else if (entry.isFile() && entry.name.endsWith(ext)) {
@@ -25397,8 +25630,8 @@ function getProjectFiles(projectDir) {
25397
25630
  let files = projectFilesCache.get(projectDir);
25398
25631
  if (!files) {
25399
25632
  files = [];
25400
- const srcDir = path20.join(projectDir, "src");
25401
- const legacySrcDir = path20.join(projectDir, "legacy-src");
25633
+ const srcDir = path21.join(projectDir, "src");
25634
+ const legacySrcDir = path21.join(projectDir, "legacy-src");
25402
25635
  let searchDir = projectDir;
25403
25636
  if (pathExists(srcDir)) {
25404
25637
  searchDir = srcDir;
@@ -25413,7 +25646,7 @@ function getProjectFiles(projectDir) {
25413
25646
  files.push(...listFilesRecursiveSafe(searchDir, ext));
25414
25647
  }
25415
25648
  const rootDir = getProjectRoot();
25416
- files = files.map((f) => path20.relative(rootDir, f).replace(/\\/g, "/"));
25649
+ files = files.map((f) => path21.relative(rootDir, f).replace(/\\/g, "/"));
25417
25650
  projectFilesCache.set(projectDir, files);
25418
25651
  }
25419
25652
  return files;
@@ -25438,8 +25671,8 @@ function inferSourcePathForComponent(comp, subsystems) {
25438
25671
  let bestFile = null;
25439
25672
  let bestScore = -1;
25440
25673
  for (const f of files) {
25441
- const ext = path20.extname(f);
25442
- const base = path20.basename(f, ext).toLowerCase();
25674
+ const ext = path21.extname(f);
25675
+ const base = path21.basename(f, ext).toLowerCase();
25443
25676
  if (candidates.has(base)) {
25444
25677
  let score = 0;
25445
25678
  const normalizedPath = f.toLowerCase();
@@ -25536,7 +25769,7 @@ function resolveAgentTopology() {
25536
25769
  });
25537
25770
  for (const sub of subsystems) {
25538
25771
  if (sub.projectPath) {
25539
- const mountSpecPath = path20.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/");
25772
+ const mountSpecPath = path21.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/");
25540
25773
  agents.push({
25541
25774
  id: `${sub.id}-owner`,
25542
25775
  name: `${sub.name} (chained subproject)`,
@@ -25558,9 +25791,9 @@ function resolveAgentTopology() {
25558
25791
  }
25559
25792
  const subComponents = components.filter((c) => c.subsystem === sub.id);
25560
25793
  const ownedPaths = [];
25561
- ownedPaths.push(path20.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/"));
25794
+ ownedPaths.push(path21.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/"));
25562
25795
  for (const c of subComponents) {
25563
- ownedPaths.push(path20.relative(getProjectRoot(), getComponentPath(c.id, sub.id)).replace(/\\/g, "/"));
25796
+ ownedPaths.push(path21.relative(getProjectRoot(), getComponentPath(c.id, sub.id)).replace(/\\/g, "/"));
25564
25797
  }
25565
25798
  if (!config.rules.generateComponentImplementers) {
25566
25799
  for (const comp of subComponents) {
@@ -25636,10 +25869,10 @@ function resolveAgentTopology() {
25636
25869
  }
25637
25870
  const dependencies = comp.dependsOn.map((depId) => `${depId}-implementer`);
25638
25871
  const readPaths = [
25639
- path20.relative(getProjectRoot(), AI_PATHS.specsSystem()).replace(/\\/g, "/"),
25640
- path20.relative(getProjectRoot(), getComponentPath(comp.id, comp.subsystem)).replace(/\\/g, "/"),
25641
- ...compInterfaces.map((i) => path20.relative(getProjectRoot(), getInterfacePath(i.id, comp.id)).replace(/\\/g, "/")),
25642
- ...compImpls.map((impl) => path20.relative(getProjectRoot(), getImplementationPath(impl.id, impl.contract)).replace(/\\/g, "/"))
25872
+ path21.relative(getProjectRoot(), AI_PATHS.specsSystem()).replace(/\\/g, "/"),
25873
+ path21.relative(getProjectRoot(), getComponentPath(comp.id, comp.subsystem)).replace(/\\/g, "/"),
25874
+ ...compInterfaces.map((i) => path21.relative(getProjectRoot(), getInterfacePath(i.id, comp.id)).replace(/\\/g, "/")),
25875
+ ...compImpls.map((impl) => path21.relative(getProjectRoot(), getImplementationPath(impl.id, impl.contract)).replace(/\\/g, "/"))
25643
25876
  ];
25644
25877
  agents.push({
25645
25878
  id: `${comp.id}-implementer`,
@@ -25745,10 +25978,10 @@ function loadRegistry() {
25745
25978
  }
25746
25979
 
25747
25980
  // src/exporters/generate.ts
25748
- var path24 = __toESM(require("path"));
25981
+ var path25 = __toESM(require("path"));
25749
25982
 
25750
25983
  // src/exporters/claude.ts
25751
- var path21 = __toESM(require("path"));
25984
+ var path22 = __toESM(require("path"));
25752
25985
  var MODEL_BY_TIER = {
25753
25986
  small: "haiku",
25754
25987
  standard: "sonnet",
@@ -25787,7 +26020,7 @@ var ClaudeExporter = class {
25787
26020
  outputPath(ctx) {
25788
26021
  const { agent, target, projectRoot } = ctx;
25789
26022
  const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
25790
- return path21.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
26023
+ return path22.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
25791
26024
  }
25792
26025
  export(ctx) {
25793
26026
  const { agent, renderedInstructions, budget } = ctx;
@@ -25812,7 +26045,7 @@ var ClaudeExporter = class {
25812
26045
  };
25813
26046
 
25814
26047
  // src/exporters/custom.ts
25815
- var path22 = __toESM(require("path"));
26048
+ var path23 = __toESM(require("path"));
25816
26049
  var CustomExporter = class {
25817
26050
  constructor() {
25818
26051
  this.targetType = "custom";
@@ -25822,7 +26055,7 @@ var CustomExporter = class {
25822
26055
  if (!("outputDir" in target)) {
25823
26056
  throw new Error("CustomExporter requires target.outputDir");
25824
26057
  }
25825
- return path22.resolve(projectRoot, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
26058
+ return path23.resolve(projectRoot, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
25826
26059
  }
25827
26060
  export(ctx) {
25828
26061
  const { agent, target, renderedInstructions } = ctx;
@@ -25845,7 +26078,7 @@ var CustomExporter = class {
25845
26078
  };
25846
26079
 
25847
26080
  // src/exporters/gemini.ts
25848
- var path23 = __toESM(require("path"));
26081
+ var path24 = __toESM(require("path"));
25849
26082
  var GeminiExporter = class {
25850
26083
  constructor() {
25851
26084
  this.targetType = "gemini";
@@ -25853,7 +26086,7 @@ var GeminiExporter = class {
25853
26086
  outputPath(ctx) {
25854
26087
  const { agent, target, projectRoot } = ctx;
25855
26088
  const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
25856
- return path23.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
26089
+ return path24.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
25857
26090
  }
25858
26091
  export(ctx) {
25859
26092
  const { agent, renderedInstructions } = ctx;
@@ -25950,7 +26183,7 @@ function resolveExpectedOutputPaths(agents, projectConfig, projectRoot = getProj
25950
26183
  const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
25951
26184
  if (!targetConfig) continue;
25952
26185
  const ctx = { agent, projectRoot, target: targetConfig };
25953
- expected.add(path24.resolve(getExporter(targetConfig).outputPath(ctx)));
26186
+ expected.add(path25.resolve(getExporter(targetConfig).outputPath(ctx)));
25954
26187
  }
25955
26188
  }
25956
26189
  return expected;
@@ -25965,7 +26198,7 @@ function resolveTargetConfig(agentTarget, projectConfig) {
25965
26198
 
25966
26199
  // src/core/treetransfer.ts
25967
26200
  var fs16 = __toESM(require("fs"));
25968
- var path25 = __toESM(require("path"));
26201
+ var path26 = __toESM(require("path"));
25969
26202
  var sdkPortal = __toESM(require_dist());
25970
26203
  var STAGING_PREFIX = ".wai-staging-";
25971
26204
  var BACKUP_DIR = ".wai-backups";
@@ -25991,39 +26224,39 @@ function hasAuthoredSpecs(root) {
25991
26224
  if (!pathExists(specsDir)) {
25992
26225
  return false;
25993
26226
  }
25994
- const systemIndex = path25.resolve(paths.specsSystem());
26227
+ const systemIndex = path26.resolve(paths.specsSystem());
25995
26228
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
25996
- if (path25.resolve(file) !== systemIndex) {
26229
+ if (path26.resolve(file) !== systemIndex) {
25997
26230
  return true;
25998
26231
  }
25999
26232
  }
26000
26233
  return false;
26001
26234
  }
26002
26235
  function createStagingDir(destDir) {
26003
- const root = path25.resolve(destDir);
26236
+ const root = path26.resolve(destDir);
26004
26237
  fs16.mkdirSync(root, { recursive: true });
26005
- const stagingDir = fs16.mkdtempSync(path25.join(root, STAGING_PREFIX));
26238
+ const stagingDir = fs16.mkdtempSync(path26.join(root, STAGING_PREFIX));
26006
26239
  return stagingDir;
26007
26240
  }
26008
26241
  function commitStagedTree(stagingDir, destDir, roots, backupExisting) {
26009
- const root = path25.resolve(destDir);
26010
- const backupDir = path25.join(root, BACKUP_DIR, timestampSlug());
26242
+ const root = path26.resolve(destDir);
26243
+ const backupDir = path26.join(root, BACKUP_DIR, timestampSlug());
26011
26244
  let backedUp = false;
26012
26245
  for (const rootRel of roots) {
26013
26246
  const relative9 = rootRel === "." ? "" : rootRel;
26014
- const stagedSpecDir = path25.join(stagingDir, relative9, ".wai");
26247
+ const stagedSpecDir = path26.join(stagingDir, relative9, ".wai");
26015
26248
  if (!fs16.existsSync(stagedSpecDir)) continue;
26016
- const liveSpecDir = liveSpecDirFor(path25.join(root, relative9));
26249
+ const liveSpecDir = liveSpecDirFor(path26.join(root, relative9));
26017
26250
  if (backupExisting && liveSpecDir && fs16.existsSync(liveSpecDir)) {
26018
- const backupTarget = path25.join(backupDir, relative9, path25.basename(liveSpecDir));
26019
- fs16.mkdirSync(path25.dirname(backupTarget), { recursive: true });
26251
+ const backupTarget = path26.join(backupDir, relative9, path26.basename(liveSpecDir));
26252
+ fs16.mkdirSync(path26.dirname(backupTarget), { recursive: true });
26020
26253
  fs16.renameSync(liveSpecDir, backupTarget);
26021
26254
  backedUp = true;
26022
26255
  } else if (liveSpecDir && fs16.existsSync(liveSpecDir)) {
26023
26256
  fs16.rmSync(liveSpecDir, { recursive: true, force: true });
26024
26257
  }
26025
- const target = path25.join(root, relative9, ".wai");
26026
- fs16.mkdirSync(path25.dirname(target), { recursive: true });
26258
+ const target = path26.join(root, relative9, ".wai");
26259
+ fs16.mkdirSync(path26.dirname(target), { recursive: true });
26027
26260
  if (fs16.existsSync(target)) fs16.rmSync(target, { recursive: true, force: true });
26028
26261
  fs16.renameSync(stagedSpecDir, target);
26029
26262
  }
@@ -26048,7 +26281,7 @@ function exportSpecTree(includeDerived, allowPartial) {
26048
26281
  const skipped = [...inspection.skipped];
26049
26282
  const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
26050
26283
  for (const rel2 of inspection.roots) {
26051
- const waiDir = aiPathsAt(path25.resolve(root, rel2)).root();
26284
+ const waiDir = aiPathsAt(path26.resolve(root, rel2)).root();
26052
26285
  if (pathExists(waiDir)) {
26053
26286
  roots.push({ relativePath: rel2, waiDir });
26054
26287
  } else {
@@ -26079,7 +26312,7 @@ function importSpecTree(archive, options) {
26079
26312
  if (!info.compatible) {
26080
26313
  throw new Error("incompatible .waitree archive (format or minimum wairon version)");
26081
26314
  }
26082
- const destDir = path25.resolve(options.destDir ?? getProjectRoot());
26315
+ const destDir = path26.resolve(options.destDir ?? getProjectRoot());
26083
26316
  const occupied = hasSpecTree(destDir);
26084
26317
  const authored = hasAuthoredSpecs(destDir);
26085
26318
  if (authored && options.replaceExisting !== true) {
@@ -26110,9 +26343,9 @@ function importSpecTree(archive, options) {
26110
26343
  return result;
26111
26344
  }
26112
26345
  function liveSpecDirFor(rootDir) {
26113
- const modern = path25.join(rootDir, ".wai");
26346
+ const modern = path26.join(rootDir, ".wai");
26114
26347
  if (fs16.existsSync(modern)) return modern;
26115
- const legacy = path25.join(rootDir, ".wairon");
26348
+ const legacy = path26.join(rootDir, ".wairon");
26116
26349
  if (fs16.existsSync(legacy)) return legacy;
26117
26350
  return null;
26118
26351
  }
@@ -26254,7 +26487,7 @@ function repairForeignStepFields(apply) {
26254
26487
 
26255
26488
  // src/core/approval.ts
26256
26489
  var crypto3 = __toESM(require("crypto"));
26257
- var path26 = __toESM(require("path"));
26490
+ var path27 = __toESM(require("path"));
26258
26491
 
26259
26492
  // src/models/lock.ts
26260
26493
  function describeApprover(who) {
@@ -26271,12 +26504,12 @@ function digest(content) {
26271
26504
  return crypto3.createHash("sha256").update(normalized, "utf8").digest("hex");
26272
26505
  }
26273
26506
  function currentSpecDigests(root = getProjectRoot()) {
26274
- const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path26.resolve(root, s.projectPath).split(path26.sep).join("/")}/`);
26507
+ const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path27.resolve(root, s.projectPath).split(path27.sep).join("/")}/`);
26275
26508
  const out = {};
26276
26509
  for (const [abs, content] of snapshotSpecFiles()) {
26277
- const normalized = path26.resolve(abs).split(path26.sep).join("/");
26510
+ const normalized = path27.resolve(abs).split(path27.sep).join("/");
26278
26511
  if (mountDirs.some((dir) => normalized.startsWith(dir))) continue;
26279
- out[path26.relative(root, abs).split(path26.sep).join("/")] = digest(content);
26512
+ out[path27.relative(root, abs).split(path27.sep).join("/")] = digest(content);
26280
26513
  }
26281
26514
  return out;
26282
26515
  }
@@ -26334,7 +26567,7 @@ function currentChildPins(mounts, root = getProjectRoot()) {
26334
26567
  const pins = {};
26335
26568
  for (const mount of mounts) {
26336
26569
  if (!mount.projectPath || mount.id.includes("::")) continue;
26337
- const child = readLockRecordAt(path26.resolve(root, mount.projectPath));
26570
+ const child = readLockRecordAt(path27.resolve(root, mount.projectPath));
26338
26571
  if (child) pins[mount.id] = pinOf(child.stateId);
26339
26572
  }
26340
26573
  return pins;
@@ -26436,7 +26669,7 @@ function localApprover() {
26436
26669
  // src/utils/ai-guide.ts
26437
26670
  var fs17 = __toESM(require("fs"));
26438
26671
  var os5 = __toESM(require("os"));
26439
- var path27 = __toESM(require("path"));
26672
+ var path28 = __toESM(require("path"));
26440
26673
 
26441
26674
  // src/core/stamp.ts
26442
26675
  function versionStamp() {
@@ -26496,13 +26729,13 @@ This project uses **wairon**. System specs live under \`.wai/specs/\` (L0 System
26496
26729
  * **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.
26497
26730
  * Use \`owns\` for private member containment (exactly one hop) and \`dependsOn\` for collaborators. Never use generic suffixes like "Manager", "Helper", or "Utils".`;
26498
26731
  function globalGuideFilePath(targetType) {
26499
- if (targetType === "claude") return path27.join(process.env["CLAUDE_CONFIG_DIR"] || path27.join(os5.homedir(), ".claude"), "CLAUDE.md");
26500
- if (targetType === "gemini") return path27.join(process.env["GEMINI_CONFIG_DIR"] || path27.join(os5.homedir(), ".gemini"), "GEMINI.md");
26732
+ if (targetType === "claude") return path28.join(process.env["CLAUDE_CONFIG_DIR"] || path28.join(os5.homedir(), ".claude"), "CLAUDE.md");
26733
+ if (targetType === "gemini") return path28.join(process.env["GEMINI_CONFIG_DIR"] || path28.join(os5.homedir(), ".gemini"), "GEMINI.md");
26501
26734
  return null;
26502
26735
  }
26503
26736
  function localGuideFilePath(projectRoot, targetType) {
26504
- if (targetType === "claude") return path27.join(projectRoot, ".claude", "CLAUDE.md");
26505
- if (targetType === "gemini" || targetType === "agy") return path27.join(projectRoot, ".gemini", "GEMINI.md");
26737
+ if (targetType === "claude") return path28.join(projectRoot, ".claude", "CLAUDE.md");
26738
+ if (targetType === "gemini" || targetType === "agy") return path28.join(projectRoot, ".gemini", "GEMINI.md");
26506
26739
  return null;
26507
26740
  }
26508
26741
  function injectGuide(filePath, scope) {
@@ -26517,7 +26750,7 @@ ${GUIDE_MARKER_END}
26517
26750
  const existing = fs17.existsSync(filePath) ? fs17.readFileSync(filePath, "utf-8") : "";
26518
26751
  const stripped = stripGuideSection(existing);
26519
26752
  const newContent = stripped.trimEnd() + section;
26520
- fs17.mkdirSync(path27.dirname(filePath), { recursive: true });
26753
+ fs17.mkdirSync(path28.dirname(filePath), { recursive: true });
26521
26754
  fs17.writeFileSync(filePath, newContent, "utf-8");
26522
26755
  }
26523
26756
  function stripGuideSection(content) {
@@ -26528,7 +26761,7 @@ function stripGuideSection(content) {
26528
26761
  }
26529
26762
  function writeRootGuideDelegator(projectRoot, targetType) {
26530
26763
  if (targetType === "claude") {
26531
- const filePath = path27.join(projectRoot, "CLAUDE.md");
26764
+ const filePath = path28.join(projectRoot, "CLAUDE.md");
26532
26765
  const content = `@.claude/CLAUDE.md
26533
26766
 
26534
26767
  # Wairon SDD Project
@@ -26543,7 +26776,7 @@ the \`wairon\` CLI is the human developer's tool, not yours.
26543
26776
  `;
26544
26777
  fs17.writeFileSync(filePath, content, "utf-8");
26545
26778
  } else if (targetType === "gemini" || targetType === "agy") {
26546
- const filePath = path27.join(projectRoot, "GEMINI.md");
26779
+ const filePath = path28.join(projectRoot, "GEMINI.md");
26547
26780
  const content = `# Wairon SDD Project
26548
26781
  ${GUIDE_MARKER_START}
26549
26782
  ${versionStamp()}
@@ -26552,7 +26785,7 @@ ${GUIDE_MARKER_END}
26552
26785
  `;
26553
26786
  fs17.writeFileSync(filePath, content, "utf-8");
26554
26787
  } else if (targetType === "cursor") {
26555
- const filePath = path27.join(projectRoot, ".cursorrules");
26788
+ const filePath = path28.join(projectRoot, ".cursorrules");
26556
26789
  const content = `# Wairon SDD Project
26557
26790
 
26558
26791
  This project uses the Wairon Spec-Driven Development (SDD) framework.
@@ -26561,8 +26794,8 @@ Refer to the rules in [.cursor/rules/](.cursor/rules/) for full instructions.
26561
26794
  `;
26562
26795
  fs17.writeFileSync(filePath, content, "utf-8");
26563
26796
  } else if (targetType === "copilot") {
26564
- const filePath = path27.join(projectRoot, ".github", "copilot-instructions.md");
26565
- fs17.mkdirSync(path27.dirname(filePath), { recursive: true });
26797
+ const filePath = path28.join(projectRoot, ".github", "copilot-instructions.md");
26798
+ fs17.mkdirSync(path28.dirname(filePath), { recursive: true });
26566
26799
  const content = `# Wairon SDD Project
26567
26800
 
26568
26801
  This project uses the Wairon Spec-Driven Development (SDD) framework.
@@ -26571,7 +26804,7 @@ Refer to the prompts in [.github/prompts/](.github/prompts/) for instructions.
26571
26804
  `;
26572
26805
  fs17.writeFileSync(filePath, content, "utf-8");
26573
26806
  } else if (targetType === "codex") {
26574
- const filePath = path27.join(projectRoot, ".codexrules");
26807
+ const filePath = path28.join(projectRoot, ".codexrules");
26575
26808
  const content = `# Wairon SDD Project
26576
26809
 
26577
26810
  Refer to [.codex/agents/](.codex/agents/) for full instructions.
@@ -26947,18 +27180,15 @@ var logger = {
26947
27180
 
26948
27181
  // src/core/skills.ts
26949
27182
  var SKILL_NAMES = ["sdd-architect", "sdd-narrative", "sdd-auditor", "sdd-implement", "sdd-delegate"];
26950
- function loadProjectConfig3() {
26951
- return loadProjectConfig();
26952
- }
26953
27183
  function activeTargetTypes2() {
26954
- const config = loadProjectConfig3();
27184
+ const config = loadProjectConfig();
26955
27185
  if (!config) return [];
26956
27186
  return activeTargetTypes(config);
26957
27187
  }
26958
27188
  var RESOURCE_SKILL_IDS = [...SKILL_NAMES].sort();
26959
27189
 
26960
27190
  // src/index.ts
26961
- function loadProjectConfig4() {
27191
+ function loadProjectConfig2() {
26962
27192
  const config = loadProjectConfig();
26963
27193
  if (!config) throw new ProjectNotInitializedError();
26964
27194
  return config;
@@ -27091,13 +27321,17 @@ function loadProjectConfig4() {
27091
27321
  ambiguityMessage,
27092
27322
  approvalRecord,
27093
27323
  approvalVerdict,
27324
+ assertContainedProjectPath,
27094
27325
  assertProjectInitialized,
27095
27326
  backendCommand,
27327
+ backfillChainedSubprojectConfigs,
27096
27328
  buildCanvasDataModel,
27097
27329
  buildProjectGraph,
27098
27330
  buildRuleContext,
27099
27331
  callSitesOf,
27100
27332
  captureApprovedSpecs,
27333
+ checkDeclarativePack,
27334
+ clearLoaderIssues,
27101
27335
  cognitiveScore,
27102
27336
  complexityLevel,
27103
27337
  composeAgentBrief,
@@ -27112,6 +27346,7 @@ function loadProjectConfig4() {
27112
27346
  createEmptyTopologyConfig,
27113
27347
  createProjectConfig,
27114
27348
  currentChildPins,
27349
+ currentRootBinding,
27115
27350
  declaredPackNames,
27116
27351
  declaredProfileIds,
27117
27352
  declaresGlobalPacks,
@@ -27119,16 +27354,12 @@ function loadProjectConfig4() {
27119
27354
  defaultNarrativeDetail,
27120
27355
  defaultPackSelections,
27121
27356
  defaultTargetConfig,
27122
- deleteComponentSpec,
27123
- deleteImplementationSpec,
27124
- deleteInterfaceSpec,
27125
27357
  deleteSpec,
27126
- deleteSubsystemSpec,
27127
- deleteTypeSpec,
27128
27358
  deregisterPackRef,
27129
27359
  deriveExecutionProfile,
27130
27360
  derivedDocPaths,
27131
27361
  detectDomainCandidates,
27362
+ diagnoseProjectPacks,
27132
27363
  diagramSetIndex,
27133
27364
  diffAgainstApproval,
27134
27365
  diffSize,
@@ -27144,6 +27375,9 @@ function loadProjectConfig4() {
27144
27375
  factsFor,
27145
27376
  fieldTypeRefs,
27146
27377
  fieldTypesOf,
27378
+ findChainingParent,
27379
+ findChainingSubprojectsMissingConfig,
27380
+ findLegacySpecFiles,
27147
27381
  findProjectRoot,
27148
27382
  findSystemRoot,
27149
27383
  fromProjectRoot,
@@ -27153,6 +27387,7 @@ function loadProjectConfig4() {
27153
27387
  generateDiagramSet,
27154
27388
  generateSequenceDiagram,
27155
27389
  getExporter,
27390
+ getLoaderIssues,
27156
27391
  getProjectRoot,
27157
27392
  getProjectRootOverride,
27158
27393
  getRequestParentReach,
@@ -27160,6 +27395,7 @@ function loadProjectConfig4() {
27160
27395
  getStatusReport,
27161
27396
  globalGuideFilePath,
27162
27397
  globalPacksDir,
27398
+ globalPacksEnabled,
27163
27399
  globalTemplatesDir,
27164
27400
  hasContext,
27165
27401
  hasFunctionBody,
@@ -27192,6 +27428,7 @@ function loadProjectConfig4() {
27192
27428
  loadInterfaceSpecs,
27193
27429
  loadProjectConfig,
27194
27430
  loadProjectExtensions,
27431
+ loadProjectVariants,
27195
27432
  loadRegistry,
27196
27433
  loadSpec,
27197
27434
  loadSpecGraph,
@@ -27215,12 +27452,16 @@ function loadProjectConfig4() {
27215
27452
  moveSubsystemProject,
27216
27453
  movedChildren,
27217
27454
  narrativeStepForeignFields,
27455
+ packDirEntry,
27456
+ packEntryLabel,
27457
+ packEntryRef,
27218
27458
  packStoreDir,
27219
27459
  parseDeclaredCall,
27220
27460
  parseYaml,
27221
27461
  passesIntentFloor,
27222
27462
  pathExists,
27223
27463
  pathKey,
27464
+ pinInstalledPacksAsSelections,
27224
27465
  projectConfigExists,
27225
27466
  provisionProject,
27226
27467
  qualifiedTypeId,
@@ -27248,18 +27489,14 @@ function loadProjectConfig4() {
27248
27489
  resolveImport,
27249
27490
  resolveInstalledPack,
27250
27491
  resolveSubprojectForNamespace,
27492
+ resolveVariantGuidance,
27251
27493
  retireSpecialists,
27252
27494
  runWithProjectBinding,
27253
27495
  runWithProjectRoot,
27254
27496
  sameContract,
27255
- saveComponentSpec,
27256
- saveImplementationSpec,
27257
- saveInterfaceSpec,
27258
27497
  saveSpec,
27259
- saveSubsystemSpec,
27260
- saveSystemSpec,
27261
27498
  saveTopologyConfig,
27262
- saveTypeSpec,
27499
+ scanAllSpecs,
27263
27500
  serializeYaml,
27264
27501
  setExecutionTier,
27265
27502
  setLogLevel,