@wairon/cli 5.1.1-dev.92 → 5.1.1-dev.93

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
@@ -3621,6 +3621,7 @@ __export(src_exports, {
3621
3621
  getRequestParentReach: () => getRequestParentReach,
3622
3622
  getRequestProjectRoot: () => getRequestProjectRoot,
3623
3623
  getStatusReport: () => getStatusReport,
3624
+ getWriteReach: () => getWriteReach,
3624
3625
  globalGuideFilePath: () => globalGuideFilePath,
3625
3626
  globalPacksDir: () => globalPacksDir,
3626
3627
  globalPacksEnabled: () => globalPacksEnabled,
@@ -3792,6 +3793,8 @@ __export(src_exports, {
3792
3793
  writeFileIfChanged: () => writeFileIfChanged,
3793
3794
  writeJsonFile: () => writeJsonFile,
3794
3795
  writeLockRecord: () => writeLockRecord,
3796
+ writeReachDenials: () => writeReachDenials,
3797
+ writeReachPermits: () => writeReachPermits,
3795
3798
  writeRootGuideDelegator: () => writeRootGuideDelegator,
3796
3799
  writeYamlFile: () => writeYamlFile
3797
3800
  });
@@ -3861,6 +3864,18 @@ function listFilesRecursive(dirPath, ext) {
3861
3864
  return files;
3862
3865
  }
3863
3866
  var projectRootOverride = null;
3867
+ var sameRoot = (a, b) => path.resolve(a) === path.resolve(b);
3868
+ function writeReachPermits(reach2, root, subsystemId) {
3869
+ const own = subsystemId === null ? void 0 : reach2.subsystems.find((s) => s.subsystemId === subsystemId && sameRoot(s.root, root));
3870
+ if (own) return own.writable;
3871
+ return !reach2.unwritableRoots.some((r) => sameRoot(r, root));
3872
+ }
3873
+ function writeReachDenials(reach2, root) {
3874
+ return reach2.subsystems.filter((s) => !s.writable && sameRoot(s.root, root));
3875
+ }
3876
+ function getWriteReach() {
3877
+ return requestRootStore.getStore()?.writeReach ?? null;
3878
+ }
3864
3879
  var requestRootStore = new import_node_async_hooks.AsyncLocalStorage();
3865
3880
  function currentRootBinding() {
3866
3881
  return requestRootStore.getStore();
@@ -3876,7 +3891,8 @@ function runWithProjectBinding(dir, reach2, fn) {
3876
3891
  parentReach: reach2.parentReach,
3877
3892
  ...reach2.narrowed ? { narrowed: true } : {},
3878
3893
  ...reach2.unwritableRoots ? { unwritableRoots: reach2.unwritableRoots.map((r) => path.resolve(r)) } : {},
3879
- ...reach2.hostedLookup ? { hostedLookup: reach2.hostedLookup } : {}
3894
+ ...reach2.hostedLookup ? { hostedLookup: reach2.hostedLookup } : {},
3895
+ ...reach2.writeReach ? { writeReach: reach2.writeReach } : {}
3880
3896
  },
3881
3897
  fn
3882
3898
  );
@@ -15081,7 +15097,7 @@ function defaultTargetConfig(type) {
15081
15097
  enabled: true
15082
15098
  };
15083
15099
  }
15084
- var WAIRON_VERSION = "5.1.1-dev.92";
15100
+ var WAIRON_VERSION = "5.1.1-dev.93";
15085
15101
  var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
15086
15102
  var ARCHITECT_AGENT_ID = "agent-architect";
15087
15103
  var ARCHITECT_TEMPLATE_ID = "architect";
@@ -26846,6 +26862,34 @@ var PartReadOnly = class extends WaironError {
26846
26862
  this.name = "PartReadOnly";
26847
26863
  }
26848
26864
  };
26865
+ var SubsystemWriteDenied = class extends WaironError {
26866
+ constructor(message) {
26867
+ super(message);
26868
+ this.name = "SubsystemWriteDenied";
26869
+ }
26870
+ };
26871
+ function deniedOwners(reach2, owners) {
26872
+ const seen = /* @__PURE__ */ new Set();
26873
+ const denied = [];
26874
+ for (const o of owners) {
26875
+ const key = `${path21.resolve(o.root)}|${o.subsystemId ?? ""}`;
26876
+ if (seen.has(key)) continue;
26877
+ seen.add(key);
26878
+ if (writeReachPermits(reach2, o.root, o.subsystemId)) continue;
26879
+ const rule = o.subsystemId === null ? void 0 : reach2.subsystems.find((s) => s.subsystemId === o.subsystemId && path21.resolve(s.root) === path21.resolve(o.root));
26880
+ denied.push(o.subsystemId === null ? `project "${o.project}" itself (a spec of no subsystem, decided at the project rung)` : `subsystem "${o.subsystemId}" of project "${o.project}" (decided by ${rule?.decidedBy ?? "the project rung"})`);
26881
+ }
26882
+ return denied;
26883
+ }
26884
+ function assertSpecWritesPermitted(owners, what) {
26885
+ const reach2 = getWriteReach();
26886
+ if (!reach2) return;
26887
+ const denied = deniedOwners(reach2, owners);
26888
+ if (denied.length === 0) return;
26889
+ throw new SubsystemWriteDenied(
26890
+ `SubsystemWriteDenied: ${what} would change ${denied.length === 1 ? "a spec" : "specs"} this request may not change \u2014 ${denied.join("; ")}. Nothing was written.`
26891
+ );
26892
+ }
26849
26893
  function unknownKeysIn(raw, parsed, at, out) {
26850
26894
  if (Array.isArray(raw)) {
26851
26895
  if (!Array.isArray(parsed) || parsed.length !== raw.length) return;
@@ -28856,6 +28900,92 @@ var SpecWorkspace = class {
28856
28900
  `The spec file ${file} belongs to the part "${held.part.alias}", fetched from git (${held.source?.git ?? "its repository"} at ${held.part.commit ?? "its pinned commit"}): a git part is read-only here. Edit it in its own repository, commit it there, and take it in with \`wairon member update ${held.part.alias}\`. Nothing was written.`
28857
28901
  );
28858
28902
  }
28903
+ /**
28904
+ * spec_registry.save step 5 / delete step 2: inside a hosted request, may the
28905
+ * request change this spec? Judged against the request's write reach with the
28906
+ * home's project root and the spec's owning subsystem — the document written
28907
+ * and, when a stored document is replaced or removed, the one stored (a
28908
+ * component moved out of a subsystem changes that subsystem too). Outside a
28909
+ * hosted request nothing is judged.
28910
+ */
28911
+ assertHomeInReach(file, kind, document) {
28912
+ if (!getWriteReach()) return;
28913
+ const raw = this.homeRootOf(file);
28914
+ const owners = [];
28915
+ if (document !== null) owners.push(this.documentOwner(raw, kind, document));
28916
+ if (pathExists(file)) {
28917
+ try {
28918
+ owners.push(this.documentOwner(raw, kind, readSpecFile(file)));
28919
+ } catch {
28920
+ }
28921
+ }
28922
+ assertSpecWritesPermitted(owners, `writing ${kind} spec ${path21.basename(path21.dirname(file))}/${path21.basename(file)}`);
28923
+ }
28924
+ /** The project root a spec file is homed under: the root whose specs folder, or one of whose parts' folders, holds it (deepest wins). */
28925
+ homeRootOf(file) {
28926
+ this.scanAll();
28927
+ let best;
28928
+ let depth = -1;
28929
+ for (const raw of this.cachedRawRoots) {
28930
+ const homes = [aiPathsAt(raw.dir).specsDir(), ...raw.record.parts.map((p) => p.directory).filter((d) => d !== void 0)];
28931
+ for (const home of homes) {
28932
+ if (isWithin(home, file) && path21.resolve(home).length > depth) {
28933
+ best = raw;
28934
+ depth = path21.resolve(home).length;
28935
+ }
28936
+ }
28937
+ }
28938
+ if (!best) return { dir: this.rootDir, namespace: "", project: path21.basename(this.rootDir) };
28939
+ return { dir: best.dir, namespace: best.record.namespace, project: idOf(best) ?? (best.record.namespace || path21.basename(best.dir)) };
28940
+ }
28941
+ /**
28942
+ * The owner of one spec DOCUMENT as stored in its root (its references local
28943
+ * to that root): a subsystem is its own, a component or type names its
28944
+ * subsystem, an interface or implementation follows its component; the L0, a
28945
+ * system-level type and a type group belong to no subsystem.
28946
+ */
28947
+ documentOwner(root, kind, document) {
28948
+ const doc = document ?? {};
28949
+ const local = (v) => typeof v === "string" && v ? splitNamespace(v).localId : null;
28950
+ const key = (v) => typeof v === "string" && v ? keyIn(root.namespace, splitNamespace(v).localId) : null;
28951
+ let subsystemId = null;
28952
+ if (kind === "subsystem") subsystemId = local(doc.id);
28953
+ else if (kind === "component" || kind === "type") subsystemId = local(doc.subsystem);
28954
+ else if (kind === "interface") subsystemId = this.componentSubsystem(key(doc.component));
28955
+ else if (kind === "implementation") {
28956
+ const contract = key(doc.contract);
28957
+ subsystemId = this.componentSubsystem(contract ? this.loadInterfaceSpec(contract)?.component ?? null : null);
28958
+ }
28959
+ return { root: root.dir, project: root.project, subsystemId };
28960
+ }
28961
+ /** The local id of a component's subsystem, by the component's key; null when no such component is stored. */
28962
+ componentSubsystem(componentKey) {
28963
+ if (!componentKey) return null;
28964
+ const subsystem = this.loadComponentSpec(componentKey)?.subsystem;
28965
+ return subsystem ? splitNamespace(subsystem).localId : null;
28966
+ }
28967
+ /**
28968
+ * The owner of one IN-MEMORY spec (its references keys, as the loader binds
28969
+ * them) — what a multi-spec write (a rename, a method move) judges before its
28970
+ * first write. `staged` answers a spec the write itself creates.
28971
+ */
28972
+ specOwner(kind, spec, staged) {
28973
+ this.scanAll();
28974
+ const id = kind === "system" ? "" : String(spec?.id ?? "");
28975
+ const namespace = (kind === "system" ? "" : this.ownerKeyOf(id)) ?? "";
28976
+ const raw = this.cachedRawRoots.find((r) => r.record.namespace === namespace);
28977
+ const root = raw ? { dir: raw.dir, project: idOf(raw) ?? (namespace || path21.basename(raw.dir)) } : { dir: this.rootDir, project: path21.basename(this.rootDir) };
28978
+ const localOfKey = (v) => v ? splitNamespace(v).localId : null;
28979
+ let subsystemId = null;
28980
+ if (kind === "subsystem") subsystemId = localOfKey(id);
28981
+ else if (kind === "component" || kind === "type") subsystemId = localOfKey(spec?.subsystem);
28982
+ else if (kind === "interface") subsystemId = this.componentSubsystem(spec?.component ?? null);
28983
+ else if (kind === "implementation" && spec?.contract) {
28984
+ const component = this.loadInterfaceSpec(spec.contract)?.component ?? staged?.("interface", spec.contract)?.component ?? null;
28985
+ subsystemId = this.componentSubsystem(component);
28986
+ }
28987
+ return { root: root.dir, project: root.project, subsystemId };
28988
+ }
28859
28989
  /**
28860
28990
  * The key one spec file is approved under (stage 8): its path from the bound
28861
28991
  * root, POSIX — or, for a part's file, `members/<alias>/<path inside the
@@ -28866,13 +28996,15 @@ var SpecWorkspace = class {
28866
28996
  return approvalKeyIn(file, this.rootDir, this.cachedRoots[0]?.parts ?? []);
28867
28997
  }
28868
28998
  /** The file store's write, at a home the writer resolved — a part's folder included, a git part's refused. */
28869
- writeHome(file, document) {
28999
+ writeHome(file, document, kind) {
28870
29000
  this.assertHomeWritable(file);
29001
+ this.assertHomeInReach(file, kind, document);
28871
29002
  writeSpecFile(file, document);
28872
29003
  }
28873
29004
  /** The file store's delete, pruning up to the specs folder the file lives in (its part's, for a part's file). */
28874
- removeHome(file) {
29005
+ removeHome(file, kind) {
28875
29006
  this.assertHomeWritable(file);
29007
+ this.assertHomeInReach(file, kind, null);
28876
29008
  const part = this.partHolding(file)?.part;
28877
29009
  return removeSpecFile(file, part?.directory ? aiPathsAt(part.directory).specsDir() : this.paths.specsDir());
28878
29010
  }
@@ -29223,6 +29355,7 @@ var SpecWorkspace = class {
29223
29355
  }
29224
29356
  saveSystemSpec(spec) {
29225
29357
  const p = this.paths.specsSystem();
29358
+ this.assertHomeInReach(p, "system", spec);
29226
29359
  ensureDir(path21.dirname(p));
29227
29360
  writeSpecFile(p, parseOrThrow(SystemSpecSchema, spec, "system", spec.name));
29228
29361
  invalidateSpecCache();
@@ -29373,12 +29506,12 @@ var SpecWorkspace = class {
29373
29506
  }
29374
29507
  }
29375
29508
  specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
29376
- this.writeHome(p, parseOrThrow(SubsystemSpecSchema, specToWrite, "subsystem", spec.id));
29509
+ this.writeHome(p, parseOrThrow(SubsystemSpecSchema, specToWrite, "subsystem", spec.id), "subsystem");
29377
29510
  invalidateSpecCache();
29378
29511
  }
29379
29512
  deleteSubsystemSpec(id) {
29380
29513
  const p = this.getSubsystemPath(id);
29381
- if (!this.removeHome(p)) return false;
29514
+ if (!this.removeHome(p, "subsystem")) return false;
29382
29515
  invalidateSpecCache();
29383
29516
  return true;
29384
29517
  }
@@ -29433,14 +29566,14 @@ var SpecWorkspace = class {
29433
29566
  );
29434
29567
  }
29435
29568
  specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
29436
- this.writeHome(p, parseOrThrow(ComponentSpecSchema, specToWrite, "component", spec.id));
29569
+ this.writeHome(p, parseOrThrow(ComponentSpecSchema, specToWrite, "component", spec.id), "component");
29437
29570
  invalidateSpecCache();
29438
29571
  this.normalizeComponentLayout();
29439
29572
  return notices;
29440
29573
  }
29441
29574
  deleteComponentSpec(id) {
29442
29575
  const p = this.getComponentPath(id);
29443
- if (!this.removeHome(p)) return false;
29576
+ if (!this.removeHome(p, "component")) return false;
29444
29577
  invalidateSpecCache();
29445
29578
  return true;
29446
29579
  }
@@ -29562,13 +29695,13 @@ var SpecWorkspace = class {
29562
29695
  }
29563
29696
  }
29564
29697
  specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
29565
- this.writeHome(p, parseOrThrow(InterfaceSpecSchema, specToWrite, "interface", spec.id));
29698
+ this.writeHome(p, parseOrThrow(InterfaceSpecSchema, specToWrite, "interface", spec.id), "interface");
29566
29699
  invalidateSpecCache();
29567
29700
  return notices;
29568
29701
  }
29569
29702
  deleteInterfaceSpec(id) {
29570
29703
  const p = this.getInterfacePath(id);
29571
- if (!this.removeHome(p)) return false;
29704
+ if (!this.removeHome(p, "interface")) return false;
29572
29705
  invalidateSpecCache();
29573
29706
  return true;
29574
29707
  }
@@ -29618,13 +29751,13 @@ var SpecWorkspace = class {
29618
29751
  }
29619
29752
  }
29620
29753
  specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
29621
- this.writeHome(p, parseOrThrow(ImplementationSpecSchema, specToWrite, "implementation", spec.id));
29754
+ this.writeHome(p, parseOrThrow(ImplementationSpecSchema, specToWrite, "implementation", spec.id), "implementation");
29622
29755
  invalidateSpecCache();
29623
29756
  return notices;
29624
29757
  }
29625
29758
  deleteImplementationSpec(id) {
29626
29759
  const p = this.getImplementationPath(id);
29627
- if (!this.removeHome(p)) return false;
29760
+ if (!this.removeHome(p, "implementation")) return false;
29628
29761
  invalidateSpecCache();
29629
29762
  return true;
29630
29763
  }
@@ -29670,14 +29803,14 @@ var SpecWorkspace = class {
29670
29803
  }
29671
29804
  }
29672
29805
  specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
29673
- this.writeHome(p, parseOrThrow(TypeSpecSchema, specToWrite, "type", spec.id));
29806
+ this.writeHome(p, parseOrThrow(TypeSpecSchema, specToWrite, "type", spec.id), "type");
29674
29807
  invalidateSpecCache();
29675
29808
  return notices;
29676
29809
  }
29677
29810
  deleteTypeSpec(id) {
29678
29811
  const spec = this.loadTypeSpec(id);
29679
29812
  const p = this.getTypePath(id, spec?.subsystem, spec?.group);
29680
- if (!this.removeHome(p)) return false;
29813
+ if (!this.removeHome(p, "type")) return false;
29681
29814
  invalidateSpecCache();
29682
29815
  return true;
29683
29816
  }
@@ -29700,12 +29833,12 @@ var SpecWorkspace = class {
29700
29833
  specToWrite.createdAt = existing.createdAt;
29701
29834
  }
29702
29835
  specToWrite.updatedAt = opts?.preserveUpdatedAt && existing?.updatedAt ? existing.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
29703
- this.writeHome(p, parseOrThrow(GroupSpecSchema, specToWrite, "group", spec.id));
29836
+ this.writeHome(p, parseOrThrow(GroupSpecSchema, specToWrite, "group", spec.id), "group");
29704
29837
  invalidateSpecCache();
29705
29838
  }
29706
29839
  deleteGroupSpec(id) {
29707
29840
  const p = this.getGroupPath(id);
29708
- if (!this.removeHome(p)) return false;
29841
+ if (!this.removeHome(p, "group")) return false;
29709
29842
  invalidateSpecCache();
29710
29843
  return true;
29711
29844
  }
@@ -29903,7 +30036,7 @@ var SpecWorkspace = class {
29903
30036
  );
29904
30037
  }
29905
30038
  if (JSON.stringify(document) === JSON.stringify(stored)) return false;
29906
- this.writeHome(file, document);
30039
+ this.writeHome(file, document, kind);
29907
30040
  invalidateSpecCache();
29908
30041
  return true;
29909
30042
  }
@@ -30817,6 +30950,7 @@ var SpecWorkspace = class {
30817
30950
  }
30818
30951
  }
30819
30952
  const carried = projectConfigRepository.rekeyCarried(rename, true);
30953
+ this.assertMoveInReach(from, to, plan6, carried.length > 0);
30820
30954
  if (dryRun) {
30821
30955
  const edits2 = moveChangeReports([...plan6.edits.values()], true);
30822
30956
  return {
@@ -30856,6 +30990,14 @@ var SpecWorkspace = class {
30856
30990
  carried
30857
30991
  };
30858
30992
  }
30993
+ /** Refuse a method move, naming every subsystem it may not change, before anything is written. */
30994
+ assertMoveInReach(from, to, plan6, rekeysRegister) {
30995
+ if (!getWriteReach()) return;
30996
+ const staged = (kind, id) => plan6.edits.get(`${kind}:${id}`)?.after;
30997
+ const owners = [...plan6.edits.values()].flatMap((e) => [e.before, e.after].filter((s) => s !== null && s !== void 0).map((s) => this.specOwner(e.kind, s, staged)));
30998
+ if (rekeysRegister) owners.push(this.specOwner("system", null));
30999
+ assertSpecWritesPermitted(owners, `moving methods from "${from}" to "${to}"`);
31000
+ }
30859
31001
  /**
30860
31002
  * Rekey every lint allow keyed on a moved method, staging each spec it
30861
31003
  * changes like any other reference the move follows. An allow anchored at a
@@ -31586,6 +31728,14 @@ function normalizeReferences(kind, id) {
31586
31728
  function rewriteReferences(kind, id, edits) {
31587
31729
  return current().respellReferences(kind, id, edits);
31588
31730
  }
31731
+ function assertSpecsInReach(targets, what, projectRung = false) {
31732
+ if (!getWriteReach()) return;
31733
+ const ws = current();
31734
+ const writable = ["system", "subsystem", "component", "interface", "implementation", "type"];
31735
+ const owners = targets.map((t) => ws.specOwner(t.kind, t.kind === "system" ? null : (writable.includes(t.kind) ? ws.load(t.kind, t.id) : null) ?? { id: t.id }));
31736
+ if (projectRung) owners.push(ws.specOwner("system", null));
31737
+ assertSpecWritesPermitted(owners, what);
31738
+ }
31589
31739
  function moveMethods(from, to, methods, hooks, dryRun) {
31590
31740
  return current().moveMethods(from, to, methods, hooks, dryRun);
31591
31741
  }
@@ -33151,7 +33301,7 @@ function restateReExports(from, to) {
33151
33301
  publicInterfaces: entries.map((e) => e.from === from ? { ...e, from: to } : e)
33152
33302
  });
33153
33303
  }
33154
- function rewriteRefFields(specsDir, remap, excludeDir) {
33304
+ function rewriteRefFields(specsDir, remap, excludeDir, dryRun = false) {
33155
33305
  const rewritten = [];
33156
33306
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
33157
33307
  if (excludeDir && isWithinDir2(excludeDir, file)) continue;
@@ -33164,12 +33314,12 @@ function rewriteRefFields(specsDir, remap, excludeDir) {
33164
33314
  if (!raw || typeof raw !== "object") continue;
33165
33315
  const kind = specKind(raw);
33166
33316
  if (!rewriteSpecRefs(raw, remap) || !kind) continue;
33167
- writeYamlFile(file, raw);
33317
+ if (!dryRun) writeYamlFile(file, raw);
33168
33318
  rewritten.push({ kind, id: kind === "system" ? "system" : String(raw.id) });
33169
33319
  }
33170
33320
  return rewritten;
33171
33321
  }
33172
- function rekeyLintAllows(specsDir, rename) {
33322
+ function rekeyLintAllows(specsDir, rename, dryRun = false) {
33173
33323
  const rewritten = [];
33174
33324
  for (const file of listFilesRecursive(specsDir, ".yaml")) {
33175
33325
  let raw;
@@ -33200,7 +33350,7 @@ function rekeyLintAllows(specsDir, rename) {
33200
33350
  }
33201
33351
  }
33202
33352
  if (!changed) continue;
33203
- writeYamlFile(file, raw);
33353
+ if (!dryRun) writeYamlFile(file, raw);
33204
33354
  rewritten.push({ kind, id: specId });
33205
33355
  }
33206
33356
  return rewritten;
@@ -33278,15 +33428,22 @@ function renameComponent(componentId, newId) {
33278
33428
  specs: renamed.map(({ from, to }) => ({ from, to })),
33279
33429
  components: [{ from: componentId, to: newId }]
33280
33430
  };
33281
- projectConfigRepository.rekeyCarried(rename, true);
33282
- const rewrittenRefs = rewriteRefFields(aiPathsAt(getProjectRoot()).specsDir(), (ref, position) => {
33431
+ const registerEdits = projectConfigRepository.rekeyCarried(rename, true);
33432
+ const remap = (ref, position) => {
33283
33433
  if (position === "component" || position === "entity-class" || position === "auth-source") {
33284
33434
  return ref === componentId ? newId : ref;
33285
33435
  }
33286
33436
  if (position === "interface" && ref === movingInterface?.id) return interfaceId;
33287
33437
  return ref;
33288
- });
33289
- const allowsRekeyed = rekeyLintAllows(aiPathsAt(getProjectRoot()).specsDir(), rename);
33438
+ };
33439
+ const specsDir = aiPathsAt(getProjectRoot()).specsDir();
33440
+ assertSpecsInReach(
33441
+ [...renamed.map(({ kind, from }) => ({ kind, id: from })), ...rewriteRefFields(specsDir, remap, void 0, true), ...rekeyLintAllows(specsDir, rename, true)],
33442
+ `renaming component "${componentId}"`,
33443
+ Array.isArray(registerEdits) && registerEdits.length > 0
33444
+ );
33445
+ const rewrittenRefs = rewriteRefFields(specsDir, remap);
33446
+ const allowsRekeyed = rekeyLintAllows(specsDir, rename);
33290
33447
  const rewritten = [...new Set([...rewrittenRefs, ...allowsRekeyed].filter((spec) => !renamed.some((moved) => moved.kind === spec.kind && moved.from === spec.id)).map((spec) => spec.id))];
33291
33448
  invalidateSpecCache();
33292
33449
  moveRenamedSpecs(componentId, newId, component, movingInterface, movingImplementation);
@@ -33376,7 +33533,18 @@ function renameMethod(componentId, methodName, newName, pinSymbol) {
33376
33533
  ]
33377
33534
  }]
33378
33535
  };
33379
- projectConfigRepository.rekeyCarried(rename, true);
33536
+ const registerEdits = projectConfigRepository.rekeyCarried(rename, true);
33537
+ const methodRemap = (ref, position, owner) => position === "method" && owner === componentId && ref === methodName ? newName : ref;
33538
+ const reachDir = aiPathsAt(getProjectRoot()).specsDir();
33539
+ assertSpecsInReach(
33540
+ [
33541
+ ...(rename.methods?.[0]?.specs ?? []).map((id) => ({ kind: moving.some((i) => i.id === id) ? "interface" : "implementation", id })),
33542
+ ...rewriteRefFields(reachDir, methodRemap, void 0, true),
33543
+ ...rekeyLintAllows(reachDir, rename, true)
33544
+ ],
33545
+ `renaming method "${componentId}.${methodName}"`,
33546
+ Array.isArray(registerEdits) && registerEdits.length > 0
33547
+ );
33380
33548
  for (const contract of moving) {
33381
33549
  saveInterfaceSpec({
33382
33550
  ...contract,
@@ -33399,7 +33567,7 @@ function renameMethod(componentId, methodName, newName, pinSymbol) {
33399
33567
  }
33400
33568
  invalidateSpecCache();
33401
33569
  const specsDir = aiPathsAt(getProjectRoot()).specsDir();
33402
- const retargeted = rewriteRefFields(specsDir, (ref, position, owner) => position === "method" && owner === componentId && ref === methodName ? newName : ref);
33570
+ const retargeted = rewriteRefFields(specsDir, methodRemap);
33403
33571
  const allowsRekeyed = rekeyLintAllows(specsDir, rename);
33404
33572
  const rewritten = [...new Set([...retargeted, ...allowsRekeyed].map((spec) => spec.id))];
33405
33573
  const mentions = collectMentions(specsDir, methodName, moving);
@@ -39438,10 +39606,13 @@ function rehearse2(plan6) {
39438
39606
  return { ...plan6, refusals: [...plan6.refusals, { code: `${plan6.request.verb}-refused`, project: "", detail }] };
39439
39607
  }
39440
39608
  const changes = diff(rehearsal);
39441
- const outside = unwritableOwners(changes);
39442
- if (outside.length > 0) {
39609
+ const refused2 = [
39610
+ ...unwritableOwners(changes).map((o) => ({ code: "family-partial", project: o, detail: `the plan must write ${o}, which this request may not write` })),
39611
+ ...deniedSubsystems(changes)
39612
+ ];
39613
+ if (refused2.length > 0) {
39443
39614
  discard(rehearsal);
39444
- return { ...plan6, refusals: [...plan6.refusals, ...outside.map((o) => ({ code: "family-partial", project: o, detail: `the plan must write ${o}, which this request may not write` }))] };
39615
+ return { ...plan6, refusals: [...plan6.refusals, ...refused2] };
39445
39616
  }
39446
39617
  return { ...plan6, rehearsal, changes, relock: relockOf(plan6, changes) };
39447
39618
  }
@@ -39485,6 +39656,16 @@ function unwritableOwners(changes) {
39485
39656
  const owners = [...new Set(changes.map((c) => path47.resolve(c.project)))];
39486
39657
  return owners.filter((o) => denied.includes(o));
39487
39658
  }
39659
+ function deniedSubsystems(changes) {
39660
+ const reach2 = getWriteReach();
39661
+ if (!reach2) return [];
39662
+ const owners = [...new Set(changes.map((c) => path47.resolve(c.project)))];
39663
+ return owners.flatMap((o) => writeReachDenials(reach2, o).map((s) => ({
39664
+ code: "subsystem-denied",
39665
+ project: o,
39666
+ detail: `the plan must change ${o}, which holds subsystem "${s.subsystemId}" this request may not change (decided by ${s.decidedBy}); a family migration reshapes its projects whole, so it needs write on every subsystem of every project it changes`
39667
+ })));
39668
+ }
39488
39669
  function pathProducers(plan6, family) {
39489
39670
  const inside = (dir) => {
39490
39671
  const rel2 = path47.relative(plan6.familyRoot, dir);
@@ -41845,6 +42026,7 @@ function loadProjectConfig3() {
41845
42026
  getRequestParentReach,
41846
42027
  getRequestProjectRoot,
41847
42028
  getStatusReport,
42029
+ getWriteReach,
41848
42030
  globalGuideFilePath,
41849
42031
  globalPacksDir,
41850
42032
  globalPacksEnabled,
@@ -42016,6 +42198,8 @@ function loadProjectConfig3() {
42016
42198
  writeFileIfChanged,
42017
42199
  writeJsonFile,
42018
42200
  writeLockRecord,
42201
+ writeReachDenials,
42202
+ writeReachPermits,
42019
42203
  writeRootGuideDelegator,
42020
42204
  writeYamlFile
42021
42205
  });