@wairon/cli 5.1.1-dev.11 → 5.1.1-dev.13

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
@@ -1312,7 +1312,7 @@ var init_defaults = __esm({
1312
1312
  copilot: ".github/prompts",
1313
1313
  codex: ".codex/agents"
1314
1314
  };
1315
- WAIRON_VERSION = "5.1.1-dev.11";
1315
+ WAIRON_VERSION = "5.1.1-dev.13";
1316
1316
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
1317
1317
  ARCHITECT_AGENT_ID = "agent-architect";
1318
1318
  ARCHITECT_TEMPLATE_ID = "architect";
@@ -2884,17 +2884,43 @@ function isExternalNamespaceRef(ctx, ref) {
2884
2884
  if (sep6 === -1) return false;
2885
2885
  return !ctx.subsystemIds.has(ref.slice(0, sep6));
2886
2886
  }
2887
+ function isProvidedBy(snapshot, provider) {
2888
+ return snapshot.projectName === provider || snapshot.projectName.split("::").pop() === provider;
2889
+ }
2890
+ function sameContract(a, b) {
2891
+ return a.component.split("::").pop() === b.component.split("::").pop() && JSON.stringify(a.methods) === JSON.stringify(b.methods) && JSON.stringify(a.dispatch ?? []) === JSON.stringify(b.dispatch ?? []);
2892
+ }
2887
2893
  function resolveSurfaceRef(ctx, ref, fromSubsystem) {
2888
- const local = ref.split("::").filter((seg) => seg && seg !== "super").pop();
2889
- if (!local) return null;
2894
+ const segments = ref.split("::").filter((seg) => seg && seg !== "super");
2895
+ const local = segments.pop();
2896
+ if (!local) return { kind: "unresolved" };
2897
+ const provider = segments.pop();
2890
2898
  const mountPools = fromSubsystem ? enclosingMounts(ctx, fromSubsystem).reverse().map((ns) => ctx.mountSurfaceSnapshots.find((m) => m.namespace === ns)?.snapshots ?? []) : [];
2891
2899
  for (const pool of [...mountPools, ctx.surfaceSnapshots]) {
2900
+ const candidates = [];
2892
2901
  for (const snapshot of pool) {
2902
+ if (provider !== void 0 && !isProvidedBy(snapshot, provider)) continue;
2893
2903
  const entry = snapshot.interfaces.find((e) => e.component === local || e.id === local);
2894
- if (entry) return { snapshot, entry };
2904
+ if (entry) candidates.push({ snapshot, entry });
2905
+ }
2906
+ if (candidates.length === 0) continue;
2907
+ const [first] = candidates;
2908
+ if (candidates.every((c) => sameContract(c.entry, first.entry))) {
2909
+ return { kind: "resolved", ...first };
2895
2910
  }
2911
+ return { kind: "ambiguous", providers: [...new Set(candidates.map((c) => c.snapshot.projectName))] };
2896
2912
  }
2897
- return null;
2913
+ return { kind: "unresolved" };
2914
+ }
2915
+ function reportAmbiguousSurfaceRef(ctx, subject, ref, providers, specId, isDraftContext) {
2916
+ const local = ref.split("::").filter((seg) => seg && seg !== "super").pop() ?? ref;
2917
+ ctx.addIssue(
2918
+ "error",
2919
+ "SURFACE_REF_AMBIGUOUS",
2920
+ `${subject} cross-tree component "${ref}", which the surface snapshots of ${providers.map((p) => `"${p}"`).join(", ")} expose with different contracts \u2014 the reference matches more than one declared contract, so none of them can judge it. Name the provider it means (super::<provider>::${local}), or remove the snapshot that no longer applies.`,
2921
+ specId,
2922
+ isDraftContext
2923
+ );
2898
2924
  }
2899
2925
  function enclosingMounts(ctx, subsystemId) {
2900
2926
  const mounts = [];
@@ -2985,6 +3011,7 @@ var init_contracts = __esm({
2985
3011
  { code: "INVALID_TARGET_COMPONENT_REFERENCE", defaultSeverity: "error", summary: "Call/register step targets a non-existent component" },
2986
3012
  { code: "CROSS_TREE_REF_UNRESOLVED", defaultSeverity: "warning", summary: "Cross-tree reference (super::/:: form) with no surface snapshot covering it \u2014 only the parent project can verify it" },
2987
3013
  { code: "SURFACE_REF_NOT_EXPOSED", defaultSeverity: "error", summary: "Cross-tree reference resolves to a surface snapshot that does not expose the called method/capability" },
3014
+ { code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree call/dispatch/register target matched by surface snapshots of several providers with different contracts" },
2988
3015
  { code: "UNDECLARED_DEPENDENCY_CALL", defaultSeverity: "error", summary: "Call step targets a component the caller does not depend on or own" },
2989
3016
  { code: "INVALID_TARGET_METHOD_REFERENCE", defaultSeverity: "error", summary: "Call step targets a method not on any target interface" },
2990
3017
  { code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Narrative asserts a guarantee the called contract does not declare" }
@@ -3039,7 +3066,18 @@ var init_contracts = __esm({
3039
3066
  if (!dispatchTarget) {
3040
3067
  if (isCrossTreeForm || isCollapsedForm) {
3041
3068
  const resolved = resolveSurfaceRef(ctx, step.targetComponent, fromSubsystem);
3042
- if (resolved) {
3069
+ if (resolved.kind === "ambiguous") {
3070
+ reportAmbiguousSurfaceRef(
3071
+ ctx,
3072
+ `Method "${implMethod.name}" in implementation "${impl.id}" dispatches (step ${step.stepNumber}) through`,
3073
+ step.targetComponent,
3074
+ resolved.providers,
3075
+ impl.id,
3076
+ isDraftCtx
3077
+ );
3078
+ continue;
3079
+ }
3080
+ if (resolved.kind === "resolved") {
3043
3081
  if (step.capability && !(resolved.entry.dispatch ?? []).some((b) => b.capability === step.capability)) {
3044
3082
  ctx.addIssue(
3045
3083
  "error",
@@ -3099,7 +3137,18 @@ var init_contracts = __esm({
3099
3137
  if (!targetComp) {
3100
3138
  if (isCrossTreeForm || isCollapsedForm) {
3101
3139
  const resolved = resolveSurfaceRef(ctx, step.targetComponent, fromSubsystem);
3102
- if (resolved) {
3140
+ if (resolved.kind === "ambiguous") {
3141
+ reportAmbiguousSurfaceRef(
3142
+ ctx,
3143
+ `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} "${step.targetMethod}" (step ${step.stepNumber}) on`,
3144
+ step.targetComponent,
3145
+ resolved.providers,
3146
+ impl.id,
3147
+ isDraftCtx
3148
+ );
3149
+ continue;
3150
+ }
3151
+ if (resolved.kind === "resolved") {
3103
3152
  const surfaceMethod = resolved.entry.methods.find((m) => m.name === step.targetMethod);
3104
3153
  if (!surfaceMethod) {
3105
3154
  ctx.addIssue(
@@ -4614,6 +4663,7 @@ var init_stereotype_deps = __esm({
4614
4663
  codes: [
4615
4664
  { code: "INVALID_DEPENDENCY_REFERENCE", defaultSeverity: "error", summary: "dependsOn names a non-existent component" },
4616
4665
  { code: "CROSS_TREE_REF_UNRESOLVED", defaultSeverity: "warning", summary: "Cross-tree dependsOn (super::/:: form) with no surface snapshot covering it" },
4666
+ { code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree dependsOn matched by surface snapshots of several providers with different contracts" },
4617
4667
  { code: "CROSS_SUBSYSTEM_NON_ADAPTER", defaultSeverity: "error", summary: "Non-Adapter component crossing a subsystem boundary" },
4618
4668
  { code: "CROSS_SUBSYSTEM_PRIVATE_ACCESS", defaultSeverity: "error", summary: "Cross-subsystem dependency on an unpublished component" },
4619
4669
  { code: "CROSS_SUBSYSTEM_TARGET_NON_PORTAL", defaultSeverity: "error", summary: "Cross-subsystem hop entering through a non-Portal" },
@@ -4637,7 +4687,11 @@ var init_stereotype_deps = __esm({
4637
4687
  const external = isExternalNamespaceRef(ctx, depId);
4638
4688
  if (external || isCollapsedCrossTreeRef(ctx, depId, comp.subsystem)) {
4639
4689
  const resolved = resolveSurfaceRef(ctx, depId, comp.subsystem);
4640
- if (resolved) {
4690
+ if (resolved.kind === "ambiguous") {
4691
+ reportAmbiguousSurfaceRef(ctx, `Component "${comp.id}" depends on`, depId, resolved.providers, comp.id, isDraftCtx);
4692
+ continue;
4693
+ }
4694
+ if (resolved.kind === "resolved") {
4641
4695
  if (comp.componentType !== "Adapter") {
4642
4696
  ctx.addIssue(
4643
4697
  "error",
@@ -4729,12 +4783,12 @@ var init_stereotype_deps = __esm({
4729
4783
  }
4730
4784
  }
4731
4785
  if (comp.componentType === "Specialist") {
4732
- const forbiddenTypes = ["Portal", "Observer", "Orchestrator", "Store", "Supervisor"];
4786
+ const forbiddenTypes = ["Portal", "Observer", "Orchestrator", "Store", "Registry", "Supervisor", "Actor"];
4733
4787
  if (forbiddenTypes.includes(depComp.componentType)) {
4734
4788
  ctx.addIssue(
4735
4789
  "error",
4736
4790
  "ARCHITECTURE_VIOLATION_SPECIALIST_DEP",
4737
- `Architectural violation: Specialist component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". Specialists are narrow capabilities \u2014 they may use Repositories, Indexes, and Adapters, but not Orchestrators, Supervisors, Stores, Portals, or Observers.` + (depComp.componentType === "Store" ? storeResolutionHint(comp.componentType, depComp.id) : ""),
4791
+ `Architectural violation: Specialist component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". Specialists are pure capabilities \u2014 they may use Repository facades, Indexes, and Adapters, but never workflow/runtime blocks (Orchestrators, Supervisors, Actors) and never persistence directly (Stores, Registries \u2014 all storage, even in-memory, is reached through a Repository facade), nor Portals or Observers.` + (depComp.componentType === "Store" ? storeResolutionHint(comp.componentType, depComp.id) : ""),
4738
4792
  comp.id,
4739
4793
  isDraftCtx || ctx.isComponentDraft(depComp.id)
4740
4794
  );
@@ -8010,7 +8064,7 @@ function buildRuleContext(opts) {
8010
8064
  for (const i of interfaces) collectAllows(i.id, i.lint);
8011
8065
  for (const im of implementations) collectAllows(im.id, im.lint);
8012
8066
  for (const t of types) collectAllows(t.id, t.lint);
8013
- const knownIssueCodes = /* @__PURE__ */ new Set([
8067
+ const knownIssueCodes2 = /* @__PURE__ */ new Set([
8014
8068
  ...[...SDD_RULES, ...extensions.rules].flatMap((r) => r.codes.map((c) => c.code)),
8015
8069
  // Declarative assertions bring their own namespaced codes — lint.allow
8016
8070
  // and severity overrides treat them exactly like builtins.
@@ -8073,7 +8127,7 @@ function buildRuleContext(opts) {
8073
8127
  mountSurfaceSnapshots: opts.mountSurfaceSnapshots ?? [],
8074
8128
  codeModel: opts.codeModel ?? emptyCodeModel(),
8075
8129
  lintAllows,
8076
- knownIssueCodes,
8130
+ knownIssueCodes: knownIssueCodes2,
8077
8131
  addIssue
8078
8132
  };
8079
8133
  }
@@ -8343,8 +8397,8 @@ function doctrineIdentity(doctrine, gate) {
8343
8397
  )
8344
8398
  };
8345
8399
  }
8346
- function hashGateState(doctrine, gate = {}) {
8347
- const payload = { tree: loadTree(), doctrine: doctrineIdentity(doctrine, gate) };
8400
+ function hashGateState(doctrine, inputs, gate = {}) {
8401
+ const payload = { tree: loadTree(), doctrine: doctrineIdentity(doctrine, gate), inputs: [...inputs].sort() };
8348
8402
  const digest2 = crypto2.createHash("sha256").update(canonicalize(payload)).digest("hex");
8349
8403
  return { algorithm: GATE_ALGORITHM, digest: digest2 };
8350
8404
  }
@@ -8375,7 +8429,7 @@ var init_statehash = __esm({
8375
8429
  init_specs2();
8376
8430
  init_rules();
8377
8431
  CONTENT_ALGORITHM = "sha256";
8378
- GATE_ALGORITHM = "sha256+doctrine";
8432
+ GATE_ALGORITHM = "sha256+doctrine+inputs";
8379
8433
  }
8380
8434
  });
8381
8435
 
@@ -13349,6 +13403,9 @@ var MODEL = __MODEL_JSON__;
13349
13403
  function addRule(rule) {
13350
13404
  ruleSet.push(rule);
13351
13405
  }
13406
+ function listRules() {
13407
+ return ruleSet;
13408
+ }
13352
13409
  function registerBuiltinRules() {
13353
13410
  ruleSet = [];
13354
13411
  for (const rule of SDD_RULES) addRule(rule);
@@ -13360,6 +13417,9 @@ function ruleSequence() {
13360
13417
  const base = ruleSet.filter((r) => r !== lintAllowsRule);
13361
13418
  return ruleSet.includes(lintAllowsRule) ? [...base, lintAllowsRule] : base;
13362
13419
  }
13420
+ function knownIssueCodes() {
13421
+ return listRules().flatMap((r) => r.codes);
13422
+ }
13363
13423
  var ruleSet;
13364
13424
  var init_repository = __esm({
13365
13425
  "src/core/rules/repository.ts"() {
@@ -14132,15 +14192,18 @@ function importSurface(sourcePath, origin) {
14132
14192
  saveSnapshot(snapshot);
14133
14193
  return snapshot;
14134
14194
  }
14135
- function pinFamilySurfaces() {
14136
- const parent = resolveChainingParent();
14137
- if (!parent) return null;
14138
- const childRoot = getProjectRoot();
14139
- const projected = runWithProjectRoot(parent.parentRoot, () => {
14195
+ function projectFamilySurfaces(parent) {
14196
+ return runWithProjectRoot(parent.parentRoot, () => {
14140
14197
  invalidateSpecCache();
14141
14198
  const siblings = loadSubsystemSpecs().filter((s) => !s.id.includes("::") && s.id !== parent.subsystemId);
14142
14199
  return [projectChildSurface(), ...siblings.map((s) => projectSubsystemSurface(s.id))];
14143
14200
  });
14201
+ }
14202
+ function pinFamilySurfaces() {
14203
+ const parent = resolveChainingParent();
14204
+ if (!parent) return null;
14205
+ const childRoot = getProjectRoot();
14206
+ const projected = projectFamilySurfaces(parent);
14144
14207
  const before = new Map(listSnapshots(childRoot).map((s) => [s.projectName, surfaceContentKey(s)]));
14145
14208
  const changed = [];
14146
14209
  for (const snapshot of projected) {
@@ -14151,17 +14214,21 @@ function pinFamilySurfaces() {
14151
14214
  }
14152
14215
  return changed;
14153
14216
  }
14154
- function computeParentStateId(parentRoot) {
14155
- return computeStateIdAt(parentRoot);
14217
+ function projectedFamilyContent(parent) {
14218
+ try {
14219
+ return new Map(projectFamilySurfaces(parent).map((s) => [s.projectName, surfaceContentKey(s)]));
14220
+ } catch {
14221
+ return null;
14222
+ }
14156
14223
  }
14157
14224
  function listExternalInterfaces() {
14158
14225
  const snapshots = listSnapshots();
14159
14226
  const chainingParent = resolveChainingParent();
14160
- const parentStateId = chainingParent ? computeParentStateId(chainingParent.parentRoot) : null;
14227
+ const projected = chainingParent ? projectedFamilyContent(chainingParent) : null;
14161
14228
  return snapshots.map((snapshot) => {
14162
14229
  const generated = snapshot.origin === "generated";
14163
14230
  const sourceKind = !generated ? "foreign" : snapshot.projectName.includes("::") ? "sibling" : "parent";
14164
- const freshness = generated && parentStateId ? snapshot.stateId === parentStateId ? "fresh" : "stale" : "unverifiable";
14231
+ const freshness = generated && projected ? projected.get(snapshot.projectName) === surfaceContentKey(snapshot) ? "fresh" : "stale" : "unverifiable";
14165
14232
  return {
14166
14233
  projectName: snapshot.projectName,
14167
14234
  origin: snapshot.origin,
@@ -14291,6 +14358,32 @@ function projectPackSelections() {
14291
14358
  return [];
14292
14359
  }
14293
14360
  }
14361
+ function issueKey(issue2) {
14362
+ return `${issue2.code}|${issue2.specId ?? ""}`;
14363
+ }
14364
+ function worstByKey(issues) {
14365
+ const worst = /* @__PURE__ */ new Map();
14366
+ for (const issue2 of issues) {
14367
+ const key = issueKey(issue2);
14368
+ const seen = worst.get(key);
14369
+ if (!seen || SEVERITY_RANK[issue2.severity] > SEVERITY_RANK[seen]) worst.set(key, issue2.severity);
14370
+ }
14371
+ return worst;
14372
+ }
14373
+ function stricterSeverities(parent, child) {
14374
+ const fromParent = parent?.sddRuleSeverity ?? {};
14375
+ const fromChild = child?.sddRuleSeverity ?? {};
14376
+ const codes = /* @__PURE__ */ new Set([...Object.keys(fromParent), ...Object.keys(fromChild)]);
14377
+ if (codes.size === 0) return parent ?? child;
14378
+ const defaults = new Map(knownIssueCodes().map((rc) => [rc.code, rc.defaultSeverity]));
14379
+ const merged = {};
14380
+ for (const code of codes) {
14381
+ const p = fromParent[code] ?? defaults.get(code);
14382
+ const c = fromChild[code] ?? defaults.get(code);
14383
+ merged[code] = p === void 0 || c === void 0 ? p ?? c : SEVERITY_RANK[p] >= SEVERITY_RANK[c] ? p : c;
14384
+ }
14385
+ return { ...parent ?? child, sddRuleSeverity: merged };
14386
+ }
14294
14387
  function issue(severity, code, message, agentId, specId) {
14295
14388
  return { severity, code, message, agentId, specId };
14296
14389
  }
@@ -14372,7 +14465,7 @@ function validateProjectConfig(config) {
14372
14465
  issues
14373
14466
  };
14374
14467
  }
14375
- function listRules() {
14468
+ function listRules2() {
14376
14469
  const extensions = loadProjectExtensions();
14377
14470
  registerBuiltinRules();
14378
14471
  registerPackRules(extensions.rules);
@@ -14472,21 +14565,22 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
14472
14565
  for (const rule of ruleSequence()) {
14473
14566
  rule.check(ctx);
14474
14567
  }
14475
- const uncovered = (i) => SUBPROJECT_REFERENCE_CODES.has(i.code) && !i.surfaceResolved;
14476
- const chainingParent = crossTree !== "off" && issues.some(uncovered) ? findChainingParent(getProjectRoot()) : null;
14477
- const resolution = chainingParent ? resolveThroughParent(getProjectRoot(), treatAllAsComplete) : null;
14568
+ const unresolved = (i) => RESOLUTION_FAILURE_CODES.has(i.code) && !i.surfaceResolved;
14569
+ const chainingParent = crossTree !== "off" && issues.some(unresolved) ? resolveChainingParent() : null;
14570
+ const resolution = chainingParent ? resolveThroughParent(getProjectRoot(), treatAllAsComplete, rules) : null;
14478
14571
  if (resolution) {
14479
- const parentSeverity = /* @__PURE__ */ new Map();
14480
- for (const i of resolution.issues) {
14481
- const key = `${i.code}|${i.specId ?? ""}`;
14482
- if (i.severity === "error" || !parentSeverity.has(key)) parentSeverity.set(key, i.severity);
14483
- }
14484
- const rejudged = (i) => {
14485
- const judged = parentSeverity.get(`${i.code}|${i.specId ?? ""}`);
14486
- return judged === "error" || judged === "warning" && i.severity === "warning";
14487
- };
14488
- const kept = issues.filter((i) => !uncovered(i) && !(i.surfaceResolved && rejudged(i)));
14489
- const merged = dedupeIssues([...kept, ...resolution.issues]);
14572
+ const judgedByParent = worstByKey(resolution.issues);
14573
+ const kept = issues.filter((i) => {
14574
+ const judged = judgedByParent.get(issueKey(i));
14575
+ if (judged !== void 0) return SEVERITY_RANK[judged] < SEVERITY_RANK[i.severity];
14576
+ return !unresolved(i);
14577
+ });
14578
+ const keptByChild = worstByKey(kept);
14579
+ const added = resolution.issues.filter((i) => {
14580
+ const own = keptByChild.get(issueKey(i));
14581
+ return own === void 0 || SEVERITY_RANK[own] <= SEVERITY_RANK[i.severity];
14582
+ });
14583
+ const merged = dedupeIssues([...kept, ...added]);
14490
14584
  return {
14491
14585
  valid: merged.every((i) => i.severity !== "error"),
14492
14586
  issues: merged,
@@ -14503,14 +14597,14 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
14503
14597
  });
14504
14598
  }
14505
14599
  }
14506
- function resolveThroughParent(boundRoot, treatAllAsComplete) {
14600
+ function resolveThroughParent(boundRoot, treatAllAsComplete, childRules) {
14507
14601
  const reach = getRequestParentReach();
14508
14602
  if (reach && !reach.parentReach) return null;
14509
14603
  const ceiling = reach?.topRoot ? path14.resolve(reach.topRoot) : void 0;
14510
14604
  const chain = [];
14511
14605
  let top = path14.resolve(boundRoot);
14512
14606
  while (top !== ceiling) {
14513
- const hop = findChainingParent(top);
14607
+ const hop = findChainingParent(top, ceiling);
14514
14608
  if (!hop) break;
14515
14609
  const next = path14.resolve(hop.parentRoot);
14516
14610
  if (ceiling && !isWithinOrEqual(ceiling, next)) break;
@@ -14521,10 +14615,10 @@ function resolveThroughParent(boundRoot, treatAllAsComplete) {
14521
14615
  const scope = chain.join("::");
14522
14616
  const inner = runWithProjectRoot(top, () => {
14523
14617
  invalidateSpecCache();
14524
- let governing = {};
14618
+ let governing = { rules: stricterSeverities(void 0, childRules) };
14525
14619
  try {
14526
14620
  const config = loadProjectConfig();
14527
- governing = { rules: config.rules, projectType: config.projectType };
14621
+ governing = { rules: stricterSeverities(config.rules, childRules), projectType: config.projectType };
14528
14622
  } catch {
14529
14623
  }
14530
14624
  return validateSddTree({
@@ -14598,7 +14692,7 @@ function settledStatusBearing(loaded) {
14598
14692
  function validateAsComplete(options) {
14599
14693
  return validateSddTree({ ...options ?? {}, treatAllAsComplete: true });
14600
14694
  }
14601
- var path14, SUBPROJECT_REFERENCE_CODES;
14695
+ var path14, RESOLUTION_FAILURE_CODES, SEVERITY_RANK;
14602
14696
  var init_validation = __esm({
14603
14697
  "src/core/validation.ts"() {
14604
14698
  "use strict";
@@ -14614,16 +14708,15 @@ var init_validation = __esm({
14614
14708
  init_fs();
14615
14709
  path14 = __toESM(require("path"));
14616
14710
  init_approval();
14617
- SUBPROJECT_REFERENCE_CODES = /* @__PURE__ */ new Set([
14711
+ RESOLUTION_FAILURE_CODES = /* @__PURE__ */ new Set([
14618
14712
  "UNDEFINED_TYPE_REFERENCE",
14619
14713
  "INVALID_DEPENDENCY_REFERENCE",
14620
14714
  "INVALID_TARGET_COMPONENT_REFERENCE",
14621
14715
  "INVALID_SUBSYSTEM_REFERENCE",
14622
- "UNDECLARED_DEPENDENCY_CALL",
14623
14716
  "INVALID_TRUSTED_LINK",
14624
- "CROSS_SUBSYSTEM_NON_ADAPTER",
14625
14717
  "CROSS_TREE_REF_UNRESOLVED"
14626
14718
  ]);
14719
+ SEVERITY_RANK = { off: 0, warning: 1, error: 2 };
14627
14720
  }
14628
14721
  });
14629
14722
 
@@ -15197,6 +15290,20 @@ function qualifyId(id, prefix, rootSubsystems) {
15197
15290
  }
15198
15291
  return prefix ? `${prefix}::${id}` : id;
15199
15292
  }
15293
+ function qualifyDeclaredId(id, prefix, mountRealization = false) {
15294
+ if (!id || !prefix) return id;
15295
+ if (id.startsWith("::") || id.startsWith("super::")) {
15296
+ return qualifyId(id, prefix, NO_ROOT_SUBSYSTEMS);
15297
+ }
15298
+ if (mountRealization && id === prefix.split("::").pop()) {
15299
+ return prefix;
15300
+ }
15301
+ return `${prefix}::${id}`;
15302
+ }
15303
+ function qualifySubsystemRef(id, prefix, rootSubsystems) {
15304
+ if (prefix && !id.includes("::") && id === prefix.split("::").pop()) return prefix;
15305
+ return qualifyId(id, prefix, rootSubsystems);
15306
+ }
15200
15307
  function splitNamespace(qualifiedId) {
15201
15308
  if (!qualifiedId.includes("::")) {
15202
15309
  return { prefix: "", localId: qualifiedId };
@@ -15227,33 +15334,75 @@ function relativizeId(id, prefix) {
15227
15334
  if (common === idParts.length) common--;
15228
15335
  return `${"super::".repeat(prefixParts.length - common)}${idParts.slice(common).join("::")}`;
15229
15336
  }
15337
+ function rebaseReference(ref, mount, direction, isMoved = () => false) {
15338
+ if (!ref || ref.startsWith("::")) return ref;
15339
+ const segments = ref.split("::");
15340
+ let hops = 0;
15341
+ while (segments[hops] === "super") hops++;
15342
+ if (hops === segments.length) return ref;
15343
+ const outer = Array.from({ length: hops + 1 }, (_, i) => `<n${i}>`).join("::");
15344
+ const inner = `${outer}::${mount}`;
15345
+ const [from, to] = direction === "into" ? [outer, inner] : [inner, outer];
15346
+ const target = qualifyId(ref, from, NO_ROOT_SUBSYSTEMS);
15347
+ const left = relativizeId(target, from);
15348
+ const travels = !left.startsWith("super::") && (direction === "outOf" || isMoved(left));
15349
+ return relativizeId(travels ? qualifyId(left, to, NO_ROOT_SUBSYSTEMS) : target, to);
15350
+ }
15230
15351
  function isWithin(dir, file) {
15231
15352
  const d = path15.resolve(dir);
15232
15353
  const f = path15.resolve(file);
15233
15354
  return f === d || f.startsWith(d + path15.sep);
15234
15355
  }
15356
+ function canonicalPath(target) {
15357
+ let existing = path15.resolve(target);
15358
+ const rest = [];
15359
+ for (; ; ) {
15360
+ try {
15361
+ fs9.lstatSync(existing);
15362
+ break;
15363
+ } catch {
15364
+ const up = path15.dirname(existing);
15365
+ if (up === existing) return path15.resolve(target);
15366
+ rest.unshift(path15.basename(existing));
15367
+ existing = up;
15368
+ }
15369
+ }
15370
+ try {
15371
+ return path15.join(fs9.realpathSync.native(existing), ...rest);
15372
+ } catch {
15373
+ return null;
15374
+ }
15375
+ }
15376
+ function chainDirKey(dir) {
15377
+ return canonicalPath(dir) ?? path15.resolve(dir);
15378
+ }
15235
15379
  function projectPathEscapesRoot(projectRoot, projectPath, resolvedChildDir) {
15236
- return path15.isAbsolute(projectPath) || !isWithin(projectRoot, resolvedChildDir);
15380
+ if (path15.isAbsolute(projectPath) || !isWithin(projectRoot, resolvedChildDir)) return true;
15381
+ const root = canonicalPath(projectRoot);
15382
+ const child = canonicalPath(resolvedChildDir);
15383
+ return root === null || child === null || !isWithin(root, child);
15237
15384
  }
15238
15385
  function assertContainedProjectPath(projectRoot, projectPath) {
15239
15386
  const root = path15.resolve(projectRoot);
15240
15387
  const resolved = path15.resolve(root, projectPath);
15241
15388
  if (projectPathEscapesRoot(root, projectPath, resolved)) {
15242
15389
  throw new Error(
15243
- `projectPath "${projectPath}" must resolve within the project root "${root}", but resolves to "${resolved}"; absolute paths and ../-escaping paths are rejected so a chained subproject is always contained by its parent.`
15390
+ `projectPath "${projectPath}" must resolve within the project root "${root}", but resolves to "${resolved}"; absolute, ../-escaping and link-escaping paths are rejected so a chained subproject is always contained by its parent.`
15244
15391
  );
15245
15392
  }
15246
15393
  return resolved;
15247
15394
  }
15248
- function findChainingParent(childRoot) {
15395
+ function findChainingParent(childRoot, ceiling) {
15249
15396
  let childResolved;
15250
15397
  try {
15251
15398
  childResolved = path15.resolve(childRoot);
15252
15399
  } catch {
15253
15400
  return null;
15254
15401
  }
15402
+ const bound = ceiling ? path15.resolve(ceiling) : void 0;
15255
15403
  let dir = path15.dirname(childResolved);
15256
15404
  for (let hops = 0; hops < 32; hops++) {
15405
+ if (bound && !isWithin(bound, dir)) break;
15257
15406
  const specsDir = aiPathsAt(dir).specsDir();
15258
15407
  if (pathExists(specsDir)) {
15259
15408
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
@@ -15267,7 +15416,8 @@ function findChainingParent(childRoot) {
15267
15416
  const projectPath = raw.projectPath;
15268
15417
  if (typeof projectPath === "string" && projectPath.trim() !== "") {
15269
15418
  try {
15270
- if (path15.resolve(dir, projectPath) === childResolved) {
15419
+ const mountDir = path15.resolve(dir, projectPath);
15420
+ if (mountDir === childResolved && !projectPathEscapesRoot(dir, projectPath, mountDir)) {
15271
15421
  const id = raw.id;
15272
15422
  return { parentRoot: dir, subsystemId: typeof id === "string" ? id : "?" };
15273
15423
  }
@@ -15283,12 +15433,11 @@ function findChainingParent(childRoot) {
15283
15433
  }
15284
15434
  return null;
15285
15435
  }
15286
- function listChainedRoots(rootDir = getProjectRoot()) {
15436
+ function inspectChainedRoots(rootDir = getProjectRoot()) {
15287
15437
  const root = path15.resolve(rootDir);
15288
- const found = [];
15289
- const visited = /* @__PURE__ */ new Set([root]);
15290
- const walk = (projectDir, depth) => {
15291
- if (depth > 32) return;
15438
+ const inspection = { roots: [], skipped: [] };
15439
+ const listed = /* @__PURE__ */ new Set();
15440
+ const walk = (projectDir, prefix, ancestors, depth) => {
15292
15441
  const specsDir = aiPathsAt(projectDir).specsDir();
15293
15442
  if (!pathExists(specsDir)) return;
15294
15443
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
@@ -15299,24 +15448,37 @@ function listChainedRoots(rootDir = getProjectRoot()) {
15299
15448
  continue;
15300
15449
  }
15301
15450
  if (!raw || typeof raw !== "object" || !("parentSystem" in raw)) continue;
15302
- const projectPath = raw.projectPath;
15451
+ const { id, projectPath } = raw;
15303
15452
  if (typeof projectPath !== "string" || projectPath.trim() === "") continue;
15453
+ const localId = typeof id === "string" ? id : "?";
15304
15454
  let childDir;
15305
15455
  try {
15306
15456
  childDir = path15.resolve(projectDir, projectPath);
15307
15457
  } catch {
15308
15458
  continue;
15309
15459
  }
15310
- if (projectPathEscapesRoot(root, projectPath, childDir)) continue;
15311
- if (visited.has(childDir)) continue;
15312
- if (!fs9.existsSync(childDir)) continue;
15313
- visited.add(childDir);
15314
- found.push(path15.relative(root, childDir).split(path15.sep).join("/"));
15315
- walk(childDir, depth + 1);
15460
+ let reason;
15461
+ if (projectPathEscapesRoot(projectDir, projectPath, childDir)) reason = "escapes";
15462
+ else if (ancestors.has(chainDirKey(childDir))) reason = "cyclic";
15463
+ else if (!fs9.existsSync(childDir)) reason = "missing";
15464
+ else if (depth >= 32) reason = "too-deep";
15465
+ if (reason) {
15466
+ const mount = prefix ? qualifyDeclaredId(localId, prefix, true) : localId;
15467
+ inspection.skipped.push({ mount, projectPath, reason });
15468
+ continue;
15469
+ }
15470
+ const key = chainDirKey(childDir);
15471
+ if (listed.has(key)) continue;
15472
+ listed.add(key);
15473
+ inspection.roots.push(path15.relative(root, childDir).split(path15.sep).join("/"));
15474
+ walk(childDir, prefix ? `${prefix}::${localId}` : localId, /* @__PURE__ */ new Set([...ancestors, key]), depth + 1);
15316
15475
  }
15317
15476
  };
15318
- walk(root, 0);
15319
- return found;
15477
+ walk(root, "", /* @__PURE__ */ new Set([chainDirKey(root)]), 0);
15478
+ return inspection;
15479
+ }
15480
+ function listChainedRoots(rootDir = getProjectRoot()) {
15481
+ return inspectChainedRoots(rootDir).roots;
15320
15482
  }
15321
15483
  function mergeMountRealizations(subs) {
15322
15484
  const result = [];
@@ -15597,7 +15759,26 @@ function buildProjectGraph(level) {
15597
15759
  return buildGraphModel(level);
15598
15760
  }
15599
15761
  function resolveChainingParent() {
15600
- return findChainingParent(getProjectRoot());
15762
+ const reach = getRequestParentReach();
15763
+ if (reach && !reach.parentReach) return null;
15764
+ return findChainingParent(getProjectRoot(), reach?.topRoot);
15765
+ }
15766
+ function snapshotInputKey(snapshot) {
15767
+ const { stateId, generatedAt, origin, ...content } = snapshot;
15768
+ return canonicalize(content);
15769
+ }
15770
+ function consumedSurfaceInputsAt(rootDir) {
15771
+ const dir = path15.join(rootDir, ".wai", "surfaces");
15772
+ if (!pathExists(dir)) return [];
15773
+ const keys = [];
15774
+ for (const file of fs9.readdirSync(dir)) {
15775
+ if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
15776
+ try {
15777
+ keys.push(snapshotInputKey(SurfaceSnapshotSchema.parse(readYamlFile(path15.join(dir, file)))));
15778
+ } catch {
15779
+ }
15780
+ }
15781
+ return keys;
15601
15782
  }
15602
15783
  function computeGateStateId() {
15603
15784
  let gate = {};
@@ -15606,7 +15787,10 @@ function computeGateStateId() {
15606
15787
  gate = { projectType: config.projectType, rules: config.rules };
15607
15788
  } catch {
15608
15789
  }
15609
- return hashGateState(loadProjectExtensions(), gate);
15790
+ const root = getProjectRoot();
15791
+ const roots = [root, ...listChainedRoots(root).map((rel2) => path15.join(root, rel2))];
15792
+ const inputs = roots.flatMap(consumedSurfaceInputsAt);
15793
+ return hashGateState(loadProjectExtensions(), inputs, gate);
15610
15794
  }
15611
15795
  function readLockState() {
15612
15796
  const record = readLockRecord();
@@ -15662,7 +15846,7 @@ function findLegacySpecFiles() {
15662
15846
  function updateSpec(kind, id, delta, hooks) {
15663
15847
  return current().updateSpec(kind, id, delta, hooks);
15664
15848
  }
15665
- var fs9, path15, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
15849
+ var fs9, path15, NO_ROOT_SUBSYSTEMS, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
15666
15850
  var init_specs2 = __esm({
15667
15851
  "src/core/specs.ts"() {
15668
15852
  "use strict";
@@ -15677,6 +15861,7 @@ var init_specs2 = __esm({
15677
15861
  init_models();
15678
15862
  init_narrative_labels();
15679
15863
  init_diagram();
15864
+ NO_ROOT_SUBSYSTEMS = /* @__PURE__ */ new Set();
15680
15865
  SIGNATURE_TTL_MS = 2e3;
15681
15866
  SpecWorkspace = class {
15682
15867
  constructor(rootDir) {
@@ -15715,7 +15900,7 @@ var init_specs2 = __esm({
15715
15900
  this.rootSubsystems.clear();
15716
15901
  this.cachedRecursive = recursive;
15717
15902
  this.scanVisitedSpecDirs = [];
15718
- const visited = /* @__PURE__ */ new Set([path15.resolve(this.rootDir)]);
15903
+ const visited = /* @__PURE__ */ new Set([chainDirKey(this.rootDir)]);
15719
15904
  const maxDepth = typeof recursive === "number" ? recursive : recursive ? Infinity : 0;
15720
15905
  this.cachedIndex = this.scanSpecsForProject(this.rootDir, "", visited, maxDepth, 0);
15721
15906
  this.cachedSpecDirs = this.scanVisitedSpecDirs;
@@ -15826,7 +16011,7 @@ var init_specs2 = __esm({
15826
16011
  }
15827
16012
  }
15828
16013
  index.subsystems = index.subsystems.map((sub) => {
15829
- const qualifiedSubId = namespacePrefix ? qualifyId(sub.id, namespacePrefix, this.rootSubsystems) : sub.id;
16014
+ const qualifiedSubId = namespacePrefix ? qualifyDeclaredId(sub.id, namespacePrefix, true) : sub.id;
15830
16015
  const componentPrefix = sub.projectPath ? qualifiedSubId : namespacePrefix;
15831
16016
  return {
15832
16017
  ...sub,
@@ -15845,14 +16030,14 @@ var init_specs2 = __esm({
15845
16030
  const originalSubsystemPaths = index.paths.subsystem;
15846
16031
  index.paths.subsystem = {};
15847
16032
  for (const [k, v] of Object.entries(originalSubsystemPaths)) {
15848
- const qualifiedK = namespacePrefix ? qualifyId(k, namespacePrefix, this.rootSubsystems) : k;
16033
+ const qualifiedK = namespacePrefix ? qualifyDeclaredId(k, namespacePrefix, true) : k;
15849
16034
  index.paths.subsystem[qualifiedK] = v;
15850
16035
  }
15851
16036
  if (namespacePrefix) {
15852
16037
  index.components = index.components.map((comp) => ({
15853
16038
  ...comp,
15854
- id: qualifyId(comp.id, namespacePrefix, this.rootSubsystems),
15855
- subsystem: qualifyId(comp.subsystem, namespacePrefix, this.rootSubsystems),
16039
+ id: qualifyDeclaredId(comp.id, namespacePrefix),
16040
+ subsystem: qualifySubsystemRef(comp.subsystem, namespacePrefix, this.rootSubsystems),
15856
16041
  owns: comp.owns.map((o) => qualifyId(o, namespacePrefix, this.rootSubsystems)),
15857
16042
  dependsOn: comp.dependsOn.map((d) => qualifyId(d, namespacePrefix, this.rootSubsystems)),
15858
16043
  dispatch: comp.dispatch?.map((b) => ({
@@ -15862,12 +16047,12 @@ var init_specs2 = __esm({
15862
16047
  }));
15863
16048
  index.interfaces = index.interfaces.map((intf) => ({
15864
16049
  ...intf,
15865
- id: qualifyId(intf.id, namespacePrefix, this.rootSubsystems),
16050
+ id: qualifyDeclaredId(intf.id, namespacePrefix),
15866
16051
  component: qualifyId(intf.component, namespacePrefix, this.rootSubsystems)
15867
16052
  }));
15868
16053
  index.implementations = index.implementations.map((impl) => ({
15869
16054
  ...impl,
15870
- id: qualifyId(impl.id, namespacePrefix, this.rootSubsystems),
16055
+ id: qualifyDeclaredId(impl.id, namespacePrefix),
15871
16056
  contract: qualifyId(impl.contract, namespacePrefix, this.rootSubsystems),
15872
16057
  methods: impl.methods.map((m) => ({
15873
16058
  ...m,
@@ -15879,13 +16064,13 @@ var init_specs2 = __esm({
15879
16064
  }));
15880
16065
  index.types = index.types.map((t) => ({
15881
16066
  ...t,
15882
- id: qualifyId(t.id, namespacePrefix, this.rootSubsystems),
15883
- subsystem: t.subsystem ? qualifyId(t.subsystem, namespacePrefix, this.rootSubsystems) : void 0,
16067
+ id: qualifyDeclaredId(t.id, namespacePrefix),
16068
+ subsystem: t.subsystem ? qualifySubsystemRef(t.subsystem, namespacePrefix, this.rootSubsystems) : void 0,
15884
16069
  group: t.group ? qualifyId(t.group, namespacePrefix, this.rootSubsystems) : void 0
15885
16070
  }));
15886
16071
  index.groups = index.groups.map((g) => ({
15887
16072
  ...g,
15888
- id: qualifyId(g.id, namespacePrefix, this.rootSubsystems)
16073
+ id: qualifyDeclaredId(g.id, namespacePrefix)
15889
16074
  }));
15890
16075
  const originalPaths = index.paths;
15891
16076
  index.paths = {
@@ -15897,39 +16082,40 @@ var init_specs2 = __esm({
15897
16082
  group: {}
15898
16083
  };
15899
16084
  for (const [k, v] of Object.entries(originalPaths.component)) {
15900
- index.paths.component[qualifyId(k, namespacePrefix, this.rootSubsystems)] = v;
16085
+ index.paths.component[qualifyDeclaredId(k, namespacePrefix)] = v;
15901
16086
  }
15902
16087
  for (const [k, v] of Object.entries(originalPaths.interface)) {
15903
- index.paths.interface[qualifyId(k, namespacePrefix, this.rootSubsystems)] = v;
16088
+ index.paths.interface[qualifyDeclaredId(k, namespacePrefix)] = v;
15904
16089
  }
15905
16090
  for (const [k, v] of Object.entries(originalPaths.implementation)) {
15906
- index.paths.implementation[qualifyId(k, namespacePrefix, this.rootSubsystems)] = v;
16091
+ index.paths.implementation[qualifyDeclaredId(k, namespacePrefix)] = v;
15907
16092
  }
15908
16093
  for (const [k, v] of Object.entries(originalPaths.type)) {
15909
- index.paths.type[qualifyId(k, namespacePrefix, this.rootSubsystems)] = v;
16094
+ index.paths.type[qualifyDeclaredId(k, namespacePrefix)] = v;
15910
16095
  }
15911
16096
  for (const [k, v] of Object.entries(originalPaths.group)) {
15912
- index.paths.group[qualifyId(k, namespacePrefix, this.rootSubsystems)] = v;
16097
+ index.paths.group[qualifyDeclaredId(k, namespacePrefix)] = v;
15913
16098
  }
15914
16099
  }
15915
16100
  if (currentDepth < maxDepth) {
15916
16101
  for (const subproj of localSubprojects) {
15917
16102
  const childDir = path15.resolve(projectDir, subproj.projectPath);
15918
- if (projectPathEscapesRoot(this.rootDir, subproj.projectPath, childDir)) {
16103
+ const mountId = namespacePrefix ? qualifyDeclaredId(subproj.subsystemId, namespacePrefix, true) : subproj.subsystemId;
16104
+ if (projectPathEscapesRoot(projectDir, subproj.projectPath, childDir)) {
15919
16105
  this.loaderIssues.push({
15920
16106
  severity: "error",
15921
16107
  code: "PROJECTPATH_ESCAPE",
15922
- message: `Subproject path "${subproj.projectPath}" declared by subsystem "${subproj.subsystemId}" escapes the project root "${this.rootDir}" (resolves to "${childDir}"); absolute and ../-escaping projectPaths are rejected. Skipping this subproject.`,
15923
- specId: subproj.subsystemId
16108
+ message: `Subproject path "${subproj.projectPath}" declared by subsystem "${mountId}" escapes the root of the project declaring it, "${projectDir}" (resolves to "${childDir}"); absolute, ../-escaping and link-escaping projectPaths are rejected. Skipping this subproject.`,
16109
+ specId: mountId
15924
16110
  });
15925
16111
  continue;
15926
16112
  }
15927
- if (visitedDirs.has(childDir)) {
16113
+ if (visitedDirs.has(chainDirKey(childDir))) {
15928
16114
  this.loaderIssues.push({
15929
16115
  severity: "error",
15930
16116
  code: "CIRCULAR_SUBPROJECT_REFERENCE",
15931
- message: `Circular reference detected: Subsystem "${subproj.subsystemId}" refers to subproject "${childDir}" which is already loaded.`,
15932
- specId: subproj.subsystemId
16117
+ message: `Circular reference detected: Subsystem "${mountId}" refers to subproject "${childDir}" which is already loaded.`,
16118
+ specId: mountId
15933
16119
  });
15934
16120
  continue;
15935
16121
  }
@@ -15937,14 +16123,14 @@ var init_specs2 = __esm({
15937
16123
  this.loaderIssues.push({
15938
16124
  severity: "error",
15939
16125
  code: "SUBPROJECT_NOT_FOUND",
15940
- message: `Subproject directory "${childDir}" declared by subsystem "${subproj.subsystemId}" does not exist.`,
15941
- specId: subproj.subsystemId
16126
+ message: `Subproject directory "${childDir}" declared by subsystem "${mountId}" does not exist.`,
16127
+ specId: mountId
15942
16128
  });
15943
16129
  continue;
15944
16130
  }
15945
16131
  const childNamespace = namespacePrefix ? `${namespacePrefix}::${subproj.subsystemId}` : subproj.subsystemId;
15946
16132
  const newVisited = new Set(visitedDirs);
15947
- newVisited.add(childDir);
16133
+ newVisited.add(chainDirKey(childDir));
15948
16134
  const childIndex = this.scanSpecsForProject(childDir, childNamespace, newVisited, maxDepth, currentDepth + 1);
15949
16135
  index.subsystems.push(...childIndex.subsystems);
15950
16136
  index.components.push(...childIndex.components);
@@ -15977,11 +16163,11 @@ var init_specs2 = __esm({
15977
16163
  const sub = index.subsystems.find((s) => s.id === currentPrefix);
15978
16164
  if (sub && sub.projectPath) {
15979
16165
  const nextDir = path15.resolve(currentDir, sub.projectPath);
15980
- if (projectPathEscapesRoot(this.rootDir, sub.projectPath, nextDir)) {
16166
+ if (projectPathEscapesRoot(currentDir, sub.projectPath, nextDir)) {
15981
16167
  this.loaderIssues.push({
15982
16168
  severity: "error",
15983
16169
  code: "PROJECTPATH_ESCAPE",
15984
- message: `Subproject path "${sub.projectPath}" declared by subsystem "${currentPrefix}" escapes the project root "${this.rootDir}" (resolves to "${nextDir}"); absolute and ../-escaping projectPaths are rejected. Skipping this subproject.`,
16170
+ message: `Subproject path "${sub.projectPath}" declared by subsystem "${currentPrefix}" escapes the root of the project declaring it, "${currentDir}" (resolves to "${nextDir}"); absolute, ../-escaping and link-escaping projectPaths are rejected. Skipping this subproject.`,
15985
16171
  specId: currentPrefix
15986
16172
  });
15987
16173
  continue;
@@ -21454,6 +21640,7 @@ __export(src_exports, {
21454
21640
  buildRuleContext: () => buildRuleContext,
21455
21641
  buildServerInstructions: () => buildServerInstructions2,
21456
21642
  buildTreeArchive: () => buildTreeArchive2,
21643
+ canonicalize: () => canonicalize,
21457
21644
  captureApprovedSpecs: () => captureApprovedSpecs,
21458
21645
  checkSkillFreshness: () => checkSkillFreshness,
21459
21646
  clearLoaderIssues: () => clearLoaderIssues,
@@ -21464,7 +21651,6 @@ __export(src_exports, {
21464
21651
  composeVariantGuidance: () => composeVariantGuidance,
21465
21652
  computeGateStateId: () => computeGateStateId,
21466
21653
  computePackDigest: () => computePackDigest,
21467
- computeParentStateId: () => computeParentStateId,
21468
21654
  computeStateId: () => computeStateId,
21469
21655
  computeStateIdAt: () => computeStateIdAt,
21470
21656
  contextDir: () => contextDir,
@@ -21541,6 +21727,7 @@ __export(src_exports, {
21541
21727
  hashGateState: () => hashGateState,
21542
21728
  importSpecTree: () => importSpecTree,
21543
21729
  importSurface: () => importSurface,
21730
+ inspectChainedRoots: () => inspectChainedRoots,
21544
21731
  inspectTreeArchive: () => inspectTreeArchive2,
21545
21732
  installPackFromDirectory: () => installPackFromDirectory,
21546
21733
  internalizeSubsystem: () => internalizeSubsystem,
@@ -21556,7 +21743,7 @@ __export(src_exports, {
21556
21743
  listInstalledPacks: () => listInstalledPacks,
21557
21744
  listMountSnapshots: () => listMountSnapshots,
21558
21745
  listResources: () => listResources,
21559
- listRules: () => listRules,
21746
+ listRules: () => listRules2,
21560
21747
  listSkillNames: () => listSkillNames,
21561
21748
  listSkillResources: () => listSkillResources,
21562
21749
  listSnapshots: () => listSnapshots,
@@ -21619,6 +21806,7 @@ __export(src_exports, {
21619
21806
  readResource: () => readResource,
21620
21807
  readSkillResource: () => readSkillResource,
21621
21808
  readYamlFile: () => readYamlFile,
21809
+ rebaseReference: () => rebaseReference,
21622
21810
  registerExporter: () => registerExporter,
21623
21811
  removeFreeStandingDomain: () => removeFreeStandingDomain,
21624
21812
  removeSnapshot: () => removeSnapshot,
@@ -22112,6 +22300,7 @@ function externalizeSubsystem(subsystemId, projectPath) {
22112
22300
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
22113
22301
  });
22114
22302
  rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
22303
+ rebaseMovedRefs(childFooDir, subsystemId, "into");
22115
22304
  invalidateSpecCache();
22116
22305
  }
22117
22306
  function internalizeSubsystem(subsystemId) {
@@ -22151,6 +22340,7 @@ function internalizeSubsystem(subsystemId) {
22151
22340
  });
22152
22341
  fs13.rmSync(childWai, { recursive: true, force: true });
22153
22342
  rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
22343
+ rebaseMovedRefs(fooDir, subsystemId, "outOf");
22154
22344
  invalidateSpecCache();
22155
22345
  }
22156
22346
  function buildRenameMap(subsystemId, externalize) {
@@ -22177,7 +22367,27 @@ function buildRenameMap(subsystemId, externalize) {
22177
22367
  }
22178
22368
  function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
22179
22369
  if (renameMap.size === 0) return;
22180
- const remap = (id) => id !== void 0 && renameMap.has(id) ? renameMap.get(id) : id;
22370
+ rewriteRefFields(specsDir, (ref) => renameMap.get(ref) ?? ref, excludeDir);
22371
+ }
22372
+ function rebaseMovedRefs(movedDir, mount, direction) {
22373
+ const declared = componentIdsUnder(movedDir);
22374
+ rewriteRefFields(movedDir, (ref, position) => position === "component" ? rebaseReference(ref, mount, direction, (id) => declared.has(id)) : ref);
22375
+ }
22376
+ function componentIdsUnder(dir) {
22377
+ const ids = /* @__PURE__ */ new Set();
22378
+ for (const file of listFilesRecursive(dir, ".yaml")) {
22379
+ let raw;
22380
+ try {
22381
+ raw = readYamlFile(file);
22382
+ } catch {
22383
+ continue;
22384
+ }
22385
+ if (raw && typeof raw === "object" && "componentType" in raw && typeof raw.id === "string") ids.add(raw.id);
22386
+ }
22387
+ return ids;
22388
+ }
22389
+ function rewriteRefFields(specsDir, remap, excludeDir) {
22390
+ const at = (ref, position) => typeof ref === "string" ? remap(ref, position) : ref;
22181
22391
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
22182
22392
  if (excludeDir && isWithinDir(excludeDir, file)) continue;
22183
22393
  let raw;
@@ -22189,14 +22399,14 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
22189
22399
  if (!raw || typeof raw !== "object") continue;
22190
22400
  let changed = false;
22191
22401
  if ("componentType" in raw && Array.isArray(raw.dependsOn)) {
22192
- const next = raw.dependsOn.map((d) => remap(d));
22402
+ const next = raw.dependsOn.map((d) => at(d, "component"));
22193
22403
  if (next.some((v, i) => v !== raw.dependsOn[i])) {
22194
22404
  raw.dependsOn = next;
22195
22405
  changed = true;
22196
22406
  }
22197
22407
  if (Array.isArray(raw.dispatch)) {
22198
22408
  for (const b of raw.dispatch) {
22199
- const nc = remap(b.component);
22409
+ const nc = at(b.component, "component");
22200
22410
  if (nc !== b.component) {
22201
22411
  b.component = nc;
22202
22412
  changed = true;
@@ -22205,7 +22415,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
22205
22415
  }
22206
22416
  } else if ("parentSystem" in raw && Array.isArray(raw.lifecycle)) {
22207
22417
  for (const le of raw.lifecycle) {
22208
- const nc = remap(le.component);
22418
+ const nc = at(le.component, "component");
22209
22419
  if (nc !== le.component) {
22210
22420
  le.component = nc;
22211
22421
  changed = true;
@@ -22215,7 +22425,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
22215
22425
  for (const m of raw.methods) {
22216
22426
  if (!Array.isArray(m.params)) continue;
22217
22427
  for (const p of m.params) {
22218
- const nt = remap(p.type);
22428
+ const nt = at(p.type, "type");
22219
22429
  if (nt !== p.type) {
22220
22430
  p.type = nt;
22221
22431
  changed = true;
@@ -22226,7 +22436,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
22226
22436
  for (const m of raw.methods) {
22227
22437
  if (!Array.isArray(m.narrative)) continue;
22228
22438
  for (const step of m.narrative) {
22229
- const nt = remap(step.targetComponent);
22439
+ const nt = at(step.targetComponent, "component");
22230
22440
  if (nt !== step.targetComponent) {
22231
22441
  step.targetComponent = nt;
22232
22442
  changed = true;
@@ -22235,7 +22445,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
22235
22445
  }
22236
22446
  } else if ("kind" in raw && Array.isArray(raw.fields)) {
22237
22447
  for (const f of raw.fields) {
22238
- const nt = remap(f.type);
22448
+ const nt = at(f.type, "type");
22239
22449
  if (nt !== f.type) {
22240
22450
  f.type = nt;
22241
22451
  changed = true;
@@ -22831,18 +23041,29 @@ function discardStagingDir(stagingDir) {
22831
23041
  } catch {
22832
23042
  }
22833
23043
  }
22834
- function exportSpecTree(includeDerived) {
23044
+ function exportSpecTree(includeDerived, allowPartial) {
22835
23045
  const system = loadSystemSpec();
22836
23046
  if (!system) {
22837
23047
  throw new Error("no spec tree to export at this project root");
22838
23048
  }
22839
- const chained = listChainedRoots();
23049
+ const inspection = inspectChainedRoots();
22840
23050
  const root = getProjectRoot();
22841
- const candidates = [
22842
- { relativePath: ".", waiDir: aiPathsAt(root).root() },
22843
- ...chained.map((rel2) => ({ relativePath: rel2, waiDir: aiPathsAt(path22.resolve(root, rel2)).root() }))
22844
- ];
22845
- const roots = candidates.filter((source) => pathExists(source.waiDir));
23051
+ const skipped = [...inspection.skipped];
23052
+ const roots = [{ relativePath: ".", waiDir: aiPathsAt(root).root() }];
23053
+ for (const rel2 of inspection.roots) {
23054
+ const waiDir = aiPathsAt(path22.resolve(root, rel2)).root();
23055
+ if (pathExists(waiDir)) {
23056
+ roots.push({ relativePath: rel2, waiDir });
23057
+ } else {
23058
+ skipped.push({ mount: rel2, projectPath: rel2, reason: "no-spec-tree" });
23059
+ }
23060
+ }
23061
+ if (skipped.length > 0 && !allowPartial) {
23062
+ const detail = skipped.map((s) => `${s.mount} (${s.reason})`).join(", ");
23063
+ throw new Error(
23064
+ `cannot export the whole spec tree \u2014 skipped: ${detail}. Pass allowPartial to export the rest anyway.`
23065
+ );
23066
+ }
22846
23067
  const stateId = computeStateId();
22847
23068
  const built = buildTreeArchive2(roots, system.name, stateId.digest, includeDerived);
22848
23069
  const result = {
@@ -22851,7 +23072,8 @@ function exportSpecTree(includeDerived) {
22851
23072
  projectName: built.manifest.projectName,
22852
23073
  roots: built.manifest.roots,
22853
23074
  fileCount: built.fileCount,
22854
- stateId: stateId.digest
23075
+ stateId: stateId.digest,
23076
+ skipped
22855
23077
  };
22856
23078
  return result;
22857
23079
  }
@@ -23372,6 +23594,7 @@ init_yaml();
23372
23594
  buildRuleContext,
23373
23595
  buildServerInstructions,
23374
23596
  buildTreeArchive,
23597
+ canonicalize,
23375
23598
  captureApprovedSpecs,
23376
23599
  checkSkillFreshness,
23377
23600
  clearLoaderIssues,
@@ -23382,7 +23605,6 @@ init_yaml();
23382
23605
  composeVariantGuidance,
23383
23606
  computeGateStateId,
23384
23607
  computePackDigest,
23385
- computeParentStateId,
23386
23608
  computeStateId,
23387
23609
  computeStateIdAt,
23388
23610
  contextDir,
@@ -23459,6 +23681,7 @@ init_yaml();
23459
23681
  hashGateState,
23460
23682
  importSpecTree,
23461
23683
  importSurface,
23684
+ inspectChainedRoots,
23462
23685
  inspectTreeArchive,
23463
23686
  installPackFromDirectory,
23464
23687
  internalizeSubsystem,
@@ -23537,6 +23760,7 @@ init_yaml();
23537
23760
  readResource,
23538
23761
  readSkillResource,
23539
23762
  readYamlFile,
23763
+ rebaseReference,
23540
23764
  registerExporter,
23541
23765
  removeFreeStandingDomain,
23542
23766
  removeSnapshot,