@wairon/cli 5.1.1-dev.76 → 5.1.1-dev.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +566 -45
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +553 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3919,6 +3919,40 @@ var BundleNotFoundError = class extends WaironError {
|
|
|
3919
3919
|
}
|
|
3920
3920
|
};
|
|
3921
3921
|
|
|
3922
|
+
// src/models/identity-rename.ts
|
|
3923
|
+
var EDGE = " -> ";
|
|
3924
|
+
var QUALIFIED_UNIT = /^(\d+:)?([^.\s]+)\.([^.\s]+)$/;
|
|
3925
|
+
function movedComponent(rename, id) {
|
|
3926
|
+
return rename.components?.find((c) => c.from === id)?.to ?? id;
|
|
3927
|
+
}
|
|
3928
|
+
function rekeyUnit(rename, unit) {
|
|
3929
|
+
if (unit.includes(EDGE)) return unit.split(EDGE).map((side) => movedComponent(rename, side)).join(EDGE);
|
|
3930
|
+
const qualified = QUALIFIED_UNIT.exec(unit);
|
|
3931
|
+
if (!qualified) return unit;
|
|
3932
|
+
const [, step = "", component, method] = qualified;
|
|
3933
|
+
const moved = rename.methods?.find((m) => m.component === component && m.method === method);
|
|
3934
|
+
if (moved) return `${step}${moved.toComponent}.${moved.toMethod}`;
|
|
3935
|
+
return `${step}${movedComponent(rename, component)}.${method}`;
|
|
3936
|
+
}
|
|
3937
|
+
function rekeyAnchor(rename, anchor) {
|
|
3938
|
+
const { spec, at, covers } = anchor;
|
|
3939
|
+
const movedSpec = rename.specs?.find((s) => s.from === spec && (!s.sites || at !== void 0 && s.sites.includes(at)));
|
|
3940
|
+
let site = at;
|
|
3941
|
+
if (at !== void 0) {
|
|
3942
|
+
if (at.includes(EDGE)) {
|
|
3943
|
+
site = rekeyUnit(rename, at);
|
|
3944
|
+
} else {
|
|
3945
|
+
const moved = rename.methods?.find((m) => m.method === at && m.specs?.includes(spec));
|
|
3946
|
+
if (moved) site = moved.toMethod;
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
return {
|
|
3950
|
+
spec: movedSpec?.to ?? spec,
|
|
3951
|
+
...site !== void 0 ? { at: site } : {},
|
|
3952
|
+
...covers !== void 0 ? { covers: covers.map((unit) => rekeyUnit(rename, unit)) } : {}
|
|
3953
|
+
};
|
|
3954
|
+
}
|
|
3955
|
+
|
|
3922
3956
|
// src/models/project.ts
|
|
3923
3957
|
var path2 = __toESM(require("path"));
|
|
3924
3958
|
var import_zod3 = require("zod");
|
|
@@ -4554,6 +4588,12 @@ function projectConfigFsAdapterAt(rootDir) {
|
|
|
4554
4588
|
},
|
|
4555
4589
|
documentExists() {
|
|
4556
4590
|
return fs2.existsSync(file());
|
|
4591
|
+
},
|
|
4592
|
+
readText() {
|
|
4593
|
+
return readFileOrNull(file());
|
|
4594
|
+
},
|
|
4595
|
+
writeText(text2) {
|
|
4596
|
+
writeFile(file(), text2);
|
|
4557
4597
|
}
|
|
4558
4598
|
};
|
|
4559
4599
|
}
|
|
@@ -4579,9 +4619,159 @@ function storeOver(adapter, root) {
|
|
|
4579
4619
|
},
|
|
4580
4620
|
readField(field) {
|
|
4581
4621
|
return valueAt(currentDocument(), field);
|
|
4622
|
+
},
|
|
4623
|
+
rewriteScalars(edits, dryRun = false) {
|
|
4624
|
+
if (edits.length === 0) return;
|
|
4625
|
+
const text2 = adapter.readText();
|
|
4626
|
+
if (text2 === null) throw new ProjectNotInitializedError();
|
|
4627
|
+
const next = rewriteScalarsInText(text2, edits, root);
|
|
4628
|
+
if (!dryRun) adapter.writeText(next);
|
|
4582
4629
|
}
|
|
4583
4630
|
};
|
|
4584
4631
|
}
|
|
4632
|
+
var KEY_RE = /^('(?:[^']|'')*'|"(?:[^"\\]|\\.)*"|[^\s'"#{}[\],&*!|>%@`][^:#]*?)\s*:(?=\s|$)/;
|
|
4633
|
+
function tokenize(lines) {
|
|
4634
|
+
const tokens = [];
|
|
4635
|
+
let blockAbove = null;
|
|
4636
|
+
lines.forEach((raw, line) => {
|
|
4637
|
+
const content = raw.replace(/\r?\n$/, "");
|
|
4638
|
+
const indent = content.length - content.trimStart().length;
|
|
4639
|
+
const trimmed = content.trim();
|
|
4640
|
+
if (blockAbove !== null) {
|
|
4641
|
+
if (trimmed === "" || indent > blockAbove) return;
|
|
4642
|
+
blockAbove = null;
|
|
4643
|
+
}
|
|
4644
|
+
if (trimmed === "" || trimmed.startsWith("#")) return;
|
|
4645
|
+
let col = indent;
|
|
4646
|
+
let rest = content.slice(indent);
|
|
4647
|
+
while (rest === "-" || rest.startsWith("- ")) {
|
|
4648
|
+
tokens.push({ kind: "item", col, line });
|
|
4649
|
+
const after = rest.slice(1);
|
|
4650
|
+
const pad = after.length - after.trimStart().length;
|
|
4651
|
+
col += 1 + pad;
|
|
4652
|
+
rest = after.trimStart();
|
|
4653
|
+
}
|
|
4654
|
+
if (rest === "") return;
|
|
4655
|
+
const key = KEY_RE.exec(rest);
|
|
4656
|
+
if (key) {
|
|
4657
|
+
const name = key[1].startsWith("'") ? key[1].slice(1, -1).replace(/''/g, "'") : key[1].startsWith('"') ? JSON.parse(key[1]) : key[1].trim();
|
|
4658
|
+
const afterKey = rest.slice(key[0].length);
|
|
4659
|
+
const value = afterKey.trimStart();
|
|
4660
|
+
const token = { kind: "key", col, line, name };
|
|
4661
|
+
if (value !== "" && !value.startsWith("#")) token.valueStart = col + key[0].length + (afterKey.length - value.length);
|
|
4662
|
+
tokens.push(token);
|
|
4663
|
+
if (/^[|>][-+0-9]*\s*(#.*)?$/.test(value)) blockAbove = col;
|
|
4664
|
+
return;
|
|
4665
|
+
}
|
|
4666
|
+
tokens.push({ kind: "scalar", col, line, valueStart: col });
|
|
4667
|
+
if (/^[|>][-+0-9]*\s*(#.*)?$/.test(rest)) blockAbove = col - 2;
|
|
4668
|
+
});
|
|
4669
|
+
return tokens;
|
|
4670
|
+
}
|
|
4671
|
+
function childrenOf(tokens, index) {
|
|
4672
|
+
const parent = tokens[index];
|
|
4673
|
+
const out = [];
|
|
4674
|
+
for (let i = index + 1; i < tokens.length; i += 1) {
|
|
4675
|
+
const t = tokens[i];
|
|
4676
|
+
const nested = t.col > parent.col || parent.kind === "key" && t.kind === "item" && t.col === parent.col;
|
|
4677
|
+
if (!nested) break;
|
|
4678
|
+
out.push(t);
|
|
4679
|
+
}
|
|
4680
|
+
return out;
|
|
4681
|
+
}
|
|
4682
|
+
function locateScalar(tokens, path31) {
|
|
4683
|
+
let region = tokens;
|
|
4684
|
+
for (let k = 0; k < path31.length; k += 1) {
|
|
4685
|
+
const segment = path31[k];
|
|
4686
|
+
if (region.length === 0) return `nothing is nested at ${path31.slice(0, k).join(".")}`;
|
|
4687
|
+
const base = Math.min(...region.map((t) => t.col));
|
|
4688
|
+
const last = k === path31.length - 1;
|
|
4689
|
+
let at;
|
|
4690
|
+
if (typeof segment === "string") {
|
|
4691
|
+
at = region.findIndex((t) => t.kind === "key" && t.col === base && t.name === segment);
|
|
4692
|
+
if (at < 0) return `no block key "${segment}" at ${path31.slice(0, k).join(".") || "the document root"}`;
|
|
4693
|
+
if (last) return region[at].valueStart !== void 0 ? region[at] : `"${segment}" holds no inline scalar`;
|
|
4694
|
+
} else {
|
|
4695
|
+
const items = region.map((t, i) => ({ t, i })).filter(({ t }) => t.kind === "item" && t.col === base);
|
|
4696
|
+
if (segment >= items.length) return `no block sequence item ${segment} at ${path31.slice(0, k).join(".")}`;
|
|
4697
|
+
at = items[segment].i;
|
|
4698
|
+
if (last) {
|
|
4699
|
+
const value = region[at + 1];
|
|
4700
|
+
return value && value.kind === "scalar" && value.line === region[at].line ? value : `item ${segment} holds no inline scalar`;
|
|
4701
|
+
}
|
|
4702
|
+
}
|
|
4703
|
+
const regionStart = tokens.indexOf(region[at]);
|
|
4704
|
+
region = childrenOf(tokens, regionStart);
|
|
4705
|
+
}
|
|
4706
|
+
return "an empty path addresses no scalar";
|
|
4707
|
+
}
|
|
4708
|
+
function readScalar(text2) {
|
|
4709
|
+
if (text2.startsWith("'")) {
|
|
4710
|
+
let i = 1;
|
|
4711
|
+
let value = "";
|
|
4712
|
+
while (i < text2.length) {
|
|
4713
|
+
if (text2[i] === "'") {
|
|
4714
|
+
if (text2[i + 1] === "'") {
|
|
4715
|
+
value += "'";
|
|
4716
|
+
i += 2;
|
|
4717
|
+
continue;
|
|
4718
|
+
}
|
|
4719
|
+
return { value, style: "single", end: i + 1 };
|
|
4720
|
+
}
|
|
4721
|
+
value += text2[i];
|
|
4722
|
+
i += 1;
|
|
4723
|
+
}
|
|
4724
|
+
return null;
|
|
4725
|
+
}
|
|
4726
|
+
if (text2.startsWith('"')) {
|
|
4727
|
+
const closing = /^"(?:[^"\\]|\\.)*"/.exec(text2);
|
|
4728
|
+
if (!closing) return null;
|
|
4729
|
+
try {
|
|
4730
|
+
return { value: JSON.parse(closing[0]), style: "double", end: closing[0].length };
|
|
4731
|
+
} catch {
|
|
4732
|
+
return null;
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4735
|
+
const comment = text2.search(/\s#/);
|
|
4736
|
+
const body = (comment < 0 ? text2 : text2.slice(0, comment)).trimEnd();
|
|
4737
|
+
return { value: body, style: "plain", end: body.length };
|
|
4738
|
+
}
|
|
4739
|
+
function writeScalar(value, style) {
|
|
4740
|
+
if (style === "double") return JSON.stringify(value);
|
|
4741
|
+
if (style === "plain" && /^[A-Za-z0-9_][A-Za-z0-9_./-]*$/.test(value)) return value;
|
|
4742
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
4743
|
+
}
|
|
4744
|
+
function setAt(document, path31, value) {
|
|
4745
|
+
let node = document;
|
|
4746
|
+
for (const segment of path31.slice(0, -1)) node = node[segment];
|
|
4747
|
+
node[path31[path31.length - 1]] = value;
|
|
4748
|
+
}
|
|
4749
|
+
function rewriteScalarsInText(text2, edits, root) {
|
|
4750
|
+
const refuse = (why) => {
|
|
4751
|
+
throw new WaironError(`Cannot rewrite .wai/project.yaml at ${root} without disturbing its formatting: ${why}. Nothing was written.`);
|
|
4752
|
+
};
|
|
4753
|
+
const lines = text2.split(/(?<=\n)/);
|
|
4754
|
+
const tokens = tokenize(lines);
|
|
4755
|
+
const expected = parseYaml(text2);
|
|
4756
|
+
for (const edit of edits) {
|
|
4757
|
+
const where = edit.path.join(".");
|
|
4758
|
+
const token = locateScalar(tokens, edit.path);
|
|
4759
|
+
if (typeof token === "string") refuse(`${where}: ${token}`);
|
|
4760
|
+
const { line, valueStart } = token;
|
|
4761
|
+
const content = lines[line].replace(/\r?\n$/, "");
|
|
4762
|
+
const ending = lines[line].slice(content.length);
|
|
4763
|
+
const scalar = readScalar(content.slice(valueStart));
|
|
4764
|
+
if (!scalar) refuse(`${where}: the scalar there is not written on one line`);
|
|
4765
|
+
if (scalar.value !== edit.from) refuse(`${where} holds "${scalar.value}", not "${edit.from}"`);
|
|
4766
|
+
lines[line] = content.slice(0, valueStart) + writeScalar(edit.to, scalar.style) + content.slice(valueStart + scalar.end) + ending;
|
|
4767
|
+
setAt(expected, edit.path, edit.to);
|
|
4768
|
+
}
|
|
4769
|
+
const next = lines.join("");
|
|
4770
|
+
if (JSON.stringify(parseYaml(next)) !== JSON.stringify(expected)) {
|
|
4771
|
+
refuse("the edited text does not read back as exactly the intended change");
|
|
4772
|
+
}
|
|
4773
|
+
return next;
|
|
4774
|
+
}
|
|
4585
4775
|
function parseConfig(document, root) {
|
|
4586
4776
|
try {
|
|
4587
4777
|
return ProjectConfigSchema.parse(document);
|
|
@@ -4723,6 +4913,35 @@ function registryOver(store, root) {
|
|
|
4723
4913
|
...config,
|
|
4724
4914
|
extensions: { ...config.extensions, packs: [...packsOf(config), ...selections], useGlobalPacks: false }
|
|
4725
4915
|
});
|
|
4916
|
+
},
|
|
4917
|
+
rekeyCarried(rename, dryRun = false) {
|
|
4918
|
+
const groups = store.readField("rules.conformance.carried");
|
|
4919
|
+
if (!Array.isArray(groups)) return [];
|
|
4920
|
+
const edits = [];
|
|
4921
|
+
const rekeys = [];
|
|
4922
|
+
groups.forEach((group, g) => {
|
|
4923
|
+
const findings = group?.findings;
|
|
4924
|
+
if (!Array.isArray(findings)) return;
|
|
4925
|
+
findings.forEach((finding, f) => {
|
|
4926
|
+
const entry = finding;
|
|
4927
|
+
if (!entry || typeof entry.spec !== "string") return;
|
|
4928
|
+
const at = typeof entry.at === "string" ? entry.at : void 0;
|
|
4929
|
+
const covers = Array.isArray(entry.covers) ? entry.covers.filter((u) => typeof u === "string") : void 0;
|
|
4930
|
+
const next = rekeyAnchor(rename, { spec: entry.spec, at, covers });
|
|
4931
|
+
const base = ["rules", "conformance", "carried", g, "findings", f];
|
|
4932
|
+
const named = { code: String(entry.code), spec: entry.spec, at: at ?? "" };
|
|
4933
|
+
const change = (path31, field, from, to) => {
|
|
4934
|
+
if (from === to) return;
|
|
4935
|
+
edits.push({ path: [...base, ...path31], from, to });
|
|
4936
|
+
rekeys.push({ ...named, field, from, to });
|
|
4937
|
+
};
|
|
4938
|
+
change(["spec"], "spec", entry.spec, next.spec);
|
|
4939
|
+
if (at !== void 0) change(["at"], "at", at, next.at);
|
|
4940
|
+
(covers ?? []).forEach((unit, u) => change(["covers", u], "covers", unit, next.covers[u]));
|
|
4941
|
+
});
|
|
4942
|
+
});
|
|
4943
|
+
store.rewriteScalars(edits, dryRun);
|
|
4944
|
+
return rekeys;
|
|
4726
4945
|
}
|
|
4727
4946
|
};
|
|
4728
4947
|
}
|
|
@@ -4807,6 +5026,9 @@ function projectConfigRepositoryOver(adapter, rootDir) {
|
|
|
4807
5026
|
},
|
|
4808
5027
|
pinGlobalPacksAsSelections(selections) {
|
|
4809
5028
|
registry.pinGlobalPacksAsSelections(selections);
|
|
5029
|
+
},
|
|
5030
|
+
rekeyCarried(rename, dryRun) {
|
|
5031
|
+
return registry.rekeyCarried(rename, dryRun);
|
|
4810
5032
|
}
|
|
4811
5033
|
};
|
|
4812
5034
|
}
|
|
@@ -4856,6 +5078,9 @@ var projectConfigRepository = {
|
|
|
4856
5078
|
},
|
|
4857
5079
|
pinGlobalPacksAsSelections(selections) {
|
|
4858
5080
|
bound().pinGlobalPacksAsSelections(selections);
|
|
5081
|
+
},
|
|
5082
|
+
rekeyCarried(rename, dryRun) {
|
|
5083
|
+
return bound().rekeyCarried(rename, dryRun);
|
|
4859
5084
|
}
|
|
4860
5085
|
};
|
|
4861
5086
|
|
|
@@ -11858,7 +12083,7 @@ function defaultTargetConfig(type) {
|
|
|
11858
12083
|
enabled: true
|
|
11859
12084
|
};
|
|
11860
12085
|
}
|
|
11861
|
-
var WAIRON_VERSION = "5.1.1-dev.
|
|
12086
|
+
var WAIRON_VERSION = "5.1.1-dev.77";
|
|
11862
12087
|
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
11863
12088
|
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
11864
12089
|
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -12647,6 +12872,28 @@ function buildCodeIndex(model) {
|
|
|
12647
12872
|
return out;
|
|
12648
12873
|
};
|
|
12649
12874
|
const declarationsAt = (path31) => namesAt(declarations, pathKey(path31), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames]));
|
|
12875
|
+
const forwardsOf = (start, name) => {
|
|
12876
|
+
const out = [];
|
|
12877
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12878
|
+
const queue = [{ file: pathKey(start), name }];
|
|
12879
|
+
while (queue.length) {
|
|
12880
|
+
const at = queue.shift();
|
|
12881
|
+
const key = `${at.file}#${at.name}`;
|
|
12882
|
+
if (seen.has(key)) continue;
|
|
12883
|
+
seen.add(key);
|
|
12884
|
+
out.push(at);
|
|
12885
|
+
for (const binding of facts.get(at.file)?.reexportBindings ?? []) {
|
|
12886
|
+
const star = binding.exported === "*";
|
|
12887
|
+
if (!star && binding.exported !== at.name) continue;
|
|
12888
|
+
if (star && at.name === "default") continue;
|
|
12889
|
+
const to = resolveImport(at.file, binding.from, paths);
|
|
12890
|
+
if (!to) continue;
|
|
12891
|
+
if (star && !facts.get(to)?.exportedNames.includes(at.name)) continue;
|
|
12892
|
+
queue.push({ file: to, name: star ? at.name : binding.local });
|
|
12893
|
+
}
|
|
12894
|
+
}
|
|
12895
|
+
return out;
|
|
12896
|
+
};
|
|
12650
12897
|
return {
|
|
12651
12898
|
paths,
|
|
12652
12899
|
exactPaths,
|
|
@@ -12655,7 +12902,8 @@ function buildCodeIndex(model) {
|
|
|
12655
12902
|
anchorsAt: (path31) => namesAt(anchors, pathKey(path31), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
|
|
12656
12903
|
findingAnchorsAt: (path31) => namesAt(findingAnchors, pathKey(path31), (f) => new Set(f.anchoredNames)),
|
|
12657
12904
|
originOf: originOf2,
|
|
12658
|
-
possibleOriginsOf
|
|
12905
|
+
possibleOriginsOf,
|
|
12906
|
+
forwardsOf
|
|
12659
12907
|
};
|
|
12660
12908
|
}
|
|
12661
12909
|
function buildRealizationIndex(ctx) {
|
|
@@ -16718,6 +16966,25 @@ function resolveCallTarget(ctx, componentId, methodName) {
|
|
|
16718
16966
|
}
|
|
16719
16967
|
return { accepted, files };
|
|
16720
16968
|
}
|
|
16969
|
+
function isTarget(pairs, target) {
|
|
16970
|
+
return pairs.some((p) => target.files.has(p.file) && target.accepted.has(p.name));
|
|
16971
|
+
}
|
|
16972
|
+
function invokedAs(code, site, file) {
|
|
16973
|
+
const scope = pathKey(site.from ?? file);
|
|
16974
|
+
const facts = code.factsAt(scope);
|
|
16975
|
+
if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") return [];
|
|
16976
|
+
if (!site.member) {
|
|
16977
|
+
const binding = importBindingOf(facts, site.name);
|
|
16978
|
+
if (!binding || binding.namespace) return [];
|
|
16979
|
+
const module3 = resolveImport(scope, binding.from, code.paths);
|
|
16980
|
+
return module3 ? code.forwardsOf(module3, binding.imported ?? site.name) : [];
|
|
16981
|
+
}
|
|
16982
|
+
if (!site.via) return [];
|
|
16983
|
+
const receiver = importBindingOf(facts, site.via);
|
|
16984
|
+
if (!receiver?.namespace) return [];
|
|
16985
|
+
const module2 = resolveImport(scope, receiver.from, code.paths);
|
|
16986
|
+
return module2 ? code.forwardsOf(module2, site.name) : [];
|
|
16987
|
+
}
|
|
16721
16988
|
function claimsOf(implMethod) {
|
|
16722
16989
|
const claims = [];
|
|
16723
16990
|
for (const step of implMethod.narrative) {
|
|
@@ -16783,6 +17050,7 @@ var callConformanceRule = {
|
|
|
16783
17050
|
if (!ctx.componentMap.has(claim.component)) continue;
|
|
16784
17051
|
const target = resolveCallTarget(ctx, claim.component, claim.method);
|
|
16785
17052
|
if (target.accepted.has(fnSymbol)) continue;
|
|
17053
|
+
if (isTarget(code.forwardsOf(file, fnSymbol), target)) continue;
|
|
16786
17054
|
const matching = sites.filter((s) => target.accepted.has(s.name));
|
|
16787
17055
|
const ref = `${claim.component}.${claim.method}`;
|
|
16788
17056
|
if (target.files.size === 0) {
|
|
@@ -16791,6 +17059,7 @@ var callConformanceRule = {
|
|
|
16791
17059
|
}
|
|
16792
17060
|
continue;
|
|
16793
17061
|
}
|
|
17062
|
+
if (sites.some((s) => !target.accepted.has(s.name) && isTarget(invokedAs(code, s, file), target))) continue;
|
|
16794
17063
|
const landed = /* @__PURE__ */ new Set();
|
|
16795
17064
|
let realized = false;
|
|
16796
17065
|
for (const site of matching) {
|
|
@@ -20087,6 +20356,10 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
20087
20356
|
exportedNames: [...facts.exported],
|
|
20088
20357
|
imports: [...facts.imports],
|
|
20089
20358
|
reexports: [...facts.reexports],
|
|
20359
|
+
reexportBindings: [
|
|
20360
|
+
...facts.namedReexports.map(({ exported, local, from }) => ({ exported, local, from })),
|
|
20361
|
+
...facts.starExports.map((from) => ({ exported: "*", local: "*", from }))
|
|
20362
|
+
],
|
|
20090
20363
|
functionComplexity: Object.fromEntries(facts.complexity),
|
|
20091
20364
|
functionCallSites: Object.fromEntries([...facts.calls].map(([k, v]) => [k, [...v.values()]])),
|
|
20092
20365
|
importBindings: Object.fromEntries(facts.importBindings),
|
|
@@ -20114,6 +20387,28 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
|
|
|
20114
20387
|
}
|
|
20115
20388
|
return { files, projectRoot, rootFiles };
|
|
20116
20389
|
}
|
|
20390
|
+
var IMPORT_FROM_RE = /(?:^|;)[ \t]*import\s+(?!\()([^'";]*?)\s*from\s*['"]([^'"]+)['"]/gm;
|
|
20391
|
+
var DYNAMIC_IMPORT_RE = /\{([^{}]*)\}\s*=\s*await\s+import\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
20392
|
+
var IDENTIFIER_ONLY_RE = /^[A-Za-z_$][\w$]*$/;
|
|
20393
|
+
function clauseBindings(clause, specifier) {
|
|
20394
|
+
const out = [];
|
|
20395
|
+
const body = clause.replace(/^\s*type\s+/, "");
|
|
20396
|
+
const named = /\{([^}]*)\}/.exec(body);
|
|
20397
|
+
if (named) {
|
|
20398
|
+
for (const piece of named[1].split(",")) {
|
|
20399
|
+
const sides = piece.replace(/^\s*type\s+/, "").split(/\s+as\s+/).map((s) => s.trim());
|
|
20400
|
+
const exported = sides[0];
|
|
20401
|
+
const local = sides[1] ?? exported;
|
|
20402
|
+
if (IDENTIFIER_ONLY_RE.test(exported) && IDENTIFIER_ONLY_RE.test(local)) out.push({ exported, local, specifier });
|
|
20403
|
+
}
|
|
20404
|
+
}
|
|
20405
|
+
const namespace = /\*\s*as\s+([A-Za-z_$][\w$]*)/.exec(body);
|
|
20406
|
+
if (namespace) out.push({ exported: "*", local: namespace[1], specifier });
|
|
20407
|
+
const head = body.replace(/\{[^}]*\}/, "").replace(/\*\s*as\s+[A-Za-z_$][\w$]*/, "");
|
|
20408
|
+
const defaulted = /^\s*([A-Za-z_$][\w$]*)/.exec(head);
|
|
20409
|
+
if (defaulted) out.push({ exported: "default", local: defaulted[1], specifier });
|
|
20410
|
+
return out;
|
|
20411
|
+
}
|
|
20117
20412
|
function readTestFiles(testRoots, projectRoot) {
|
|
20118
20413
|
const scanned = [];
|
|
20119
20414
|
for (const key of walkDeclaredRoots(testRoots, [], projectRoot)) {
|
|
@@ -20129,25 +20424,100 @@ function readTestFiles(testRoots, projectRoot) {
|
|
|
20129
20424
|
for (const m of text2.matchAll(NAMED_IMPORT_BINDINGS_RE)) {
|
|
20130
20425
|
for (const name of boundNames(m[1] ?? "", false)) imports.add(name);
|
|
20131
20426
|
}
|
|
20427
|
+
const code = stripComments(text2, JS_PATTERNS);
|
|
20428
|
+
const bindings = [];
|
|
20429
|
+
for (const m of code.matchAll(IMPORT_FROM_RE)) bindings.push(...clauseBindings(m[1], m[2]));
|
|
20430
|
+
for (const m of code.matchAll(DYNAMIC_IMPORT_RE)) bindings.push(...clauseBindings(`{${m[1].replace(/:/g, " as ")}}`, m[2]));
|
|
20132
20431
|
const words2 = /* @__PURE__ */ new Set();
|
|
20133
20432
|
for (const m of text2.matchAll(IDENTIFIER_RE)) words2.add(m[0]);
|
|
20134
|
-
scanned.push({ path: key, imports, words: words2 });
|
|
20433
|
+
scanned.push({ path: key, imports, bindings, words: words2 });
|
|
20135
20434
|
}
|
|
20136
20435
|
return scanned;
|
|
20137
20436
|
}
|
|
20437
|
+
var NAMED_REEXPORT_RE = /\bexport\s+(?:type\s+)?\{([^}]*)\}\s*from\s*['"]([^'"]+)['"]/g;
|
|
20438
|
+
var STAR_REEXPORT_RE = /\bexport\s+\*\s+from\s*['"]([^'"]+)['"]/g;
|
|
20439
|
+
function reexportReader() {
|
|
20440
|
+
const cache = /* @__PURE__ */ new Map();
|
|
20441
|
+
return (file) => {
|
|
20442
|
+
const cached = cache.get(file);
|
|
20443
|
+
if (cached) return cached;
|
|
20444
|
+
const found = { named: [], stars: [] };
|
|
20445
|
+
try {
|
|
20446
|
+
const code = stripComments(fs10.readFileSync(file, "utf8"), JS_PATTERNS);
|
|
20447
|
+
for (const m of code.matchAll(NAMED_REEXPORT_RE)) {
|
|
20448
|
+
for (const piece of m[1].split(",")) {
|
|
20449
|
+
const sides = piece.replace(/^\s*type\s+/, "").split(/\s+as\s+/).map((s) => s.trim());
|
|
20450
|
+
if (!IDENTIFIER_ONLY_RE.test(sides[0] ?? "")) continue;
|
|
20451
|
+
found.named.push({ local: sides[0], exported: sides[1] ?? sides[0], specifier: m[2] });
|
|
20452
|
+
}
|
|
20453
|
+
}
|
|
20454
|
+
for (const m of code.matchAll(STAR_REEXPORT_RE)) found.stars.push(m[1]);
|
|
20455
|
+
} catch {
|
|
20456
|
+
}
|
|
20457
|
+
cache.set(file, found);
|
|
20458
|
+
return found;
|
|
20459
|
+
};
|
|
20460
|
+
}
|
|
20461
|
+
function publishesFrom(module2, name, target, symbol, readReexports, visited = /* @__PURE__ */ new Set()) {
|
|
20462
|
+
if (path13.resolve(module2) === target && name === symbol) return true;
|
|
20463
|
+
const key = `${module2}#${name}`;
|
|
20464
|
+
if (visited.has(key)) return false;
|
|
20465
|
+
visited.add(key);
|
|
20466
|
+
const reexports = readReexports(module2);
|
|
20467
|
+
for (const re of reexports.named) {
|
|
20468
|
+
if (re.exported !== name) continue;
|
|
20469
|
+
const next = resolveRelativeModule(module2, re.specifier);
|
|
20470
|
+
if (next && publishesFrom(next, re.local, target, symbol, readReexports, visited)) return true;
|
|
20471
|
+
}
|
|
20472
|
+
if (name === "default") return false;
|
|
20473
|
+
for (const specifier of reexports.stars) {
|
|
20474
|
+
const next = resolveRelativeModule(module2, specifier);
|
|
20475
|
+
if (next && publishesFrom(next, name, target, symbol, readReexports, visited)) return true;
|
|
20476
|
+
}
|
|
20477
|
+
return false;
|
|
20478
|
+
}
|
|
20138
20479
|
function findTestsReferencing(methods, projectRoot, testRoots = []) {
|
|
20139
20480
|
const scanned = readTestFiles(testRoots, projectRoot);
|
|
20140
20481
|
if (scanned.length === 0) return [];
|
|
20141
20482
|
const indiscriminateAbove = scanned.length / 10;
|
|
20142
|
-
const
|
|
20483
|
+
const searches = [];
|
|
20143
20484
|
for (const method of methods) {
|
|
20144
20485
|
const symbol = method.symbol ?? method.name;
|
|
20145
|
-
|
|
20146
|
-
|
|
20486
|
+
let search = searches.find((s) => s.method === method.name && s.symbol === symbol);
|
|
20487
|
+
if (!search) {
|
|
20488
|
+
search = { method: method.name, symbol, files: [] };
|
|
20489
|
+
searches.push(search);
|
|
20490
|
+
}
|
|
20491
|
+
const file = method.sourcePath ? path13.resolve(projectRoot, method.sourcePath) : void 0;
|
|
20492
|
+
if (file && !search.files.includes(file)) search.files.push(file);
|
|
20493
|
+
}
|
|
20494
|
+
const readReexports = reexportReader();
|
|
20495
|
+
const found = [];
|
|
20496
|
+
for (const { method, symbol, files } of searches) {
|
|
20497
|
+
const imported = [];
|
|
20498
|
+
const mentioned = [];
|
|
20499
|
+
for (const test of scanned) {
|
|
20500
|
+
if (files.length === 0) {
|
|
20501
|
+
if (test.imports.has(symbol)) imported.push(test.path);
|
|
20502
|
+
else if (test.words.has(symbol)) mentioned.push(test.path);
|
|
20503
|
+
continue;
|
|
20504
|
+
}
|
|
20505
|
+
const testFile = path13.resolve(projectRoot, test.path);
|
|
20506
|
+
const fromMethod = (binding, name) => {
|
|
20507
|
+
const module2 = resolveRelativeModule(testFile, binding.specifier);
|
|
20508
|
+
return module2 !== null && files.some((target) => publishesFrom(module2, name, target, symbol, readReexports));
|
|
20509
|
+
};
|
|
20510
|
+
const naming = test.bindings.filter((b) => b.exported === symbol || b.local === symbol);
|
|
20511
|
+
if (test.bindings.some((b) => b.exported !== "*" && fromMethod(b, b.exported)) || test.words.has(symbol) && test.bindings.some((b) => b.exported === "*" && fromMethod(b, symbol))) {
|
|
20512
|
+
imported.push(test.path);
|
|
20513
|
+
} else if (naming.length === 0 && test.words.has(symbol)) {
|
|
20514
|
+
mentioned.push(test.path);
|
|
20515
|
+
}
|
|
20516
|
+
}
|
|
20147
20517
|
const indiscriminate = mentioned.length > indiscriminateAbove;
|
|
20148
20518
|
if (imported.length === 0 && mentioned.length === 0) continue;
|
|
20149
20519
|
found.push({
|
|
20150
|
-
method
|
|
20520
|
+
method,
|
|
20151
20521
|
symbol,
|
|
20152
20522
|
imported,
|
|
20153
20523
|
mentioned: indiscriminate ? [] : mentioned,
|
|
@@ -24373,6 +24743,8 @@ var SpecWorkspace = class {
|
|
|
24373
24743
|
const plan = this.planMethodMove(source, to, methods, contracts);
|
|
24374
24744
|
const repointed = this.repointToNewHome(from, to, methods, plan);
|
|
24375
24745
|
const mentions = this.moveMentions(from, plan);
|
|
24746
|
+
const rename = moveRename(from, to, methods, plan);
|
|
24747
|
+
this.rekeyMoveAllows(plan, rename);
|
|
24376
24748
|
if (hooks) {
|
|
24377
24749
|
const verdict = judgeMoveResult(hooks, plan);
|
|
24378
24750
|
plan.notices.push(...verdict.notices);
|
|
@@ -24391,10 +24763,12 @@ var SpecWorkspace = class {
|
|
|
24391
24763
|
summary: refusedMoveSummary(from, to, methods, verdict.refusals, alternatives),
|
|
24392
24764
|
dryRun,
|
|
24393
24765
|
mentions,
|
|
24394
|
-
created: []
|
|
24766
|
+
created: [],
|
|
24767
|
+
carried: []
|
|
24395
24768
|
};
|
|
24396
24769
|
}
|
|
24397
24770
|
}
|
|
24771
|
+
const carried = projectConfigRepository.rekeyCarried(rename, true);
|
|
24398
24772
|
if (dryRun) {
|
|
24399
24773
|
const edits2 = moveChangeReports([...plan.edits.values()], true);
|
|
24400
24774
|
return {
|
|
@@ -24407,13 +24781,16 @@ var SpecWorkspace = class {
|
|
|
24407
24781
|
refusals: [],
|
|
24408
24782
|
alternatives: [],
|
|
24409
24783
|
notices: plan.notices,
|
|
24410
|
-
summary: `Dry run: moving ${namedMethods(methods)} from "${from}" to "${to}" would touch ${edits2.length} spec${edits2.length === 1 ? "" : "s"}${createdClause(plan.created, "would create")} and re-point ${repointed} reference${repointed === 1 ? "" : "s"}. Nothing was written.`,
|
|
24784
|
+
summary: `Dry run: moving ${namedMethods(methods)} from "${from}" to "${to}" would touch ${edits2.length} spec${edits2.length === 1 ? "" : "s"}${createdClause(plan.created, "would create")} and re-point ${repointed} reference${repointed === 1 ? "" : "s"}${registerClause(carried, "would rekey")}. Nothing was written.`,
|
|
24411
24785
|
dryRun: true,
|
|
24412
24786
|
mentions,
|
|
24413
|
-
created: plan.created
|
|
24787
|
+
created: plan.created,
|
|
24788
|
+
carried
|
|
24414
24789
|
};
|
|
24415
24790
|
}
|
|
24416
|
-
const edits = this.writeMoveEdits([...plan.edits.values()])
|
|
24791
|
+
const edits = this.writeMoveEdits([...plan.edits.values()], () => {
|
|
24792
|
+
projectConfigRepository.rekeyCarried(rename);
|
|
24793
|
+
});
|
|
24417
24794
|
return {
|
|
24418
24795
|
moved: true,
|
|
24419
24796
|
from,
|
|
@@ -24424,12 +24801,49 @@ var SpecWorkspace = class {
|
|
|
24424
24801
|
refusals: [],
|
|
24425
24802
|
alternatives: [],
|
|
24426
24803
|
notices: plan.notices,
|
|
24427
|
-
summary: `Moved ${namedMethods(methods)} from "${from}" to "${to}": ${edits.length} spec${edits.length === 1 ? "" : "s"} written${createdClause(plan.created, "created")}, ${repointed} reference${repointed === 1 ? "" : "s"} re-pointed${mentions.length ? `, ${mentions.length} still naming the old home in prose or on the wire` : ""}.`,
|
|
24804
|
+
summary: `Moved ${namedMethods(methods)} from "${from}" to "${to}": ${edits.length} spec${edits.length === 1 ? "" : "s"} written${createdClause(plan.created, "created")}, ${repointed} reference${repointed === 1 ? "" : "s"} re-pointed${registerClause(carried, "rekeyed")}${mentions.length ? `, ${mentions.length} still naming the old home in prose or on the wire` : ""}.`,
|
|
24428
24805
|
dryRun: false,
|
|
24429
24806
|
mentions,
|
|
24430
|
-
created: plan.created
|
|
24807
|
+
created: plan.created,
|
|
24808
|
+
carried
|
|
24431
24809
|
};
|
|
24432
24810
|
}
|
|
24811
|
+
/**
|
|
24812
|
+
* Rekey every lint allow keyed on a moved method, staging each spec it
|
|
24813
|
+
* changes like any other reference the move follows. An allow anchored at a
|
|
24814
|
+
* moved method on a spec the method left moves to the spec it arrived in; a
|
|
24815
|
+
* covered `<from>.<method>` unit anywhere names `<to>.<method>`. An allow
|
|
24816
|
+
* whose new home the plan does not stage stays where it is.
|
|
24817
|
+
*/
|
|
24818
|
+
rekeyMoveAllows(plan, rename) {
|
|
24819
|
+
const subjects = [...this.everySpecInTree()];
|
|
24820
|
+
for (const edit of plan.edits.values()) if (edit.before === null) subjects.push({ kind: edit.kind, id: edit.id, spec: edit.after });
|
|
24821
|
+
const stagedAfter = (kind, id) => plan.edits.get(`${kind}:${id}`)?.after;
|
|
24822
|
+
for (const { kind, id, spec } of subjects) {
|
|
24823
|
+
if (id.includes("::")) continue;
|
|
24824
|
+
const allows = spec?.lint?.allow ?? [];
|
|
24825
|
+
if (allows.length === 0) continue;
|
|
24826
|
+
const staged = plan.edits.get(`${kind}:${id}`);
|
|
24827
|
+
const subject = staged ? staged.after : cloneSpec(spec);
|
|
24828
|
+
const kept = [];
|
|
24829
|
+
let changed = false;
|
|
24830
|
+
for (const allow of subject.lint?.allow ?? []) {
|
|
24831
|
+
const next = rekeyAnchor(rename, { spec: id, ...allow.at !== void 0 ? { at: allow.at } : {}, ...allow.covers ? { covers: allow.covers } : {} });
|
|
24832
|
+
const rekeyed = { ...allow, ...next.at !== void 0 ? { at: next.at } : {}, ...next.covers ? { covers: next.covers } : {} };
|
|
24833
|
+
if (JSON.stringify(rekeyed) !== JSON.stringify(allow)) changed = true;
|
|
24834
|
+
const home = next.spec !== id ? stagedAfter(kind, next.spec) : void 0;
|
|
24835
|
+
if (home) {
|
|
24836
|
+
home.lint = { ...home.lint ?? {}, allow: [...home.lint?.allow ?? [], rekeyed] };
|
|
24837
|
+
changed = true;
|
|
24838
|
+
continue;
|
|
24839
|
+
}
|
|
24840
|
+
kept.push(rekeyed);
|
|
24841
|
+
}
|
|
24842
|
+
if (!changed) continue;
|
|
24843
|
+
subject.lint = { ...subject.lint, allow: kept };
|
|
24844
|
+
if (!staged) this.stageMoveEdit(plan, kind, id, spec, subject);
|
|
24845
|
+
}
|
|
24846
|
+
}
|
|
24433
24847
|
/**
|
|
24434
24848
|
* Why this move cannot be attempted at all, or null when the request is
|
|
24435
24849
|
* answerable. Every check here is about what the CALLER named, so its answer
|
|
@@ -24502,8 +24916,10 @@ var SpecWorkspace = class {
|
|
|
24502
24916
|
newDependencies: [],
|
|
24503
24917
|
wireBound: [],
|
|
24504
24918
|
notices: [],
|
|
24505
|
-
created: []
|
|
24919
|
+
created: [],
|
|
24920
|
+
rehomed: []
|
|
24506
24921
|
};
|
|
24922
|
+
const leftImplementations = [];
|
|
24507
24923
|
const receiving = stated ?? createdContract(target, source.id, methods, contracts);
|
|
24508
24924
|
const arriving = [];
|
|
24509
24925
|
const travelling = [];
|
|
@@ -24515,6 +24931,7 @@ var SpecWorkspace = class {
|
|
|
24515
24931
|
const remaining = cloneSpec(contract);
|
|
24516
24932
|
remaining.methods = remaining.methods.filter((m) => !methods.includes(m.name));
|
|
24517
24933
|
this.stageMoveEdit(plan, "interface", contract.id, contract, remaining);
|
|
24934
|
+
plan.rehomed.push({ from: contract.id, to: receiving.id });
|
|
24518
24935
|
if (contract.methods.length === leaving.length) {
|
|
24519
24936
|
plan.notices.push(`"${contract.id}" is left with no methods \u2014 "${source.id}" no longer declares anything on it.`);
|
|
24520
24937
|
}
|
|
@@ -24526,6 +24943,7 @@ var SpecWorkspace = class {
|
|
|
24526
24943
|
const kept = cloneSpec(impl);
|
|
24527
24944
|
kept.methods = kept.methods.filter((m) => !methods.includes(m.name));
|
|
24528
24945
|
this.stageMoveEdit(plan, "implementation", impl.id, impl, kept);
|
|
24946
|
+
leftImplementations.push(impl.id);
|
|
24529
24947
|
}
|
|
24530
24948
|
}
|
|
24531
24949
|
this.stageMoveEdit(plan, "interface", receiving.id, stated, {
|
|
@@ -24554,9 +24972,11 @@ var SpecWorkspace = class {
|
|
|
24554
24972
|
...cloneSpec(targetImpl),
|
|
24555
24973
|
methods: [...cloneSpec(targetImpl.methods), ...travelling]
|
|
24556
24974
|
});
|
|
24975
|
+
for (const id of leftImplementations) plan.rehomed.push({ from: id, to: targetImpl.id });
|
|
24557
24976
|
} else if (travelling.length > 0) {
|
|
24558
24977
|
const created = createdImplementation(target, receiving.id, source.id, travelling, implementations, contracts);
|
|
24559
24978
|
this.stageMoveEdit(plan, "implementation", created.spec.id, null, created.spec);
|
|
24979
|
+
for (const id of leftImplementations) plan.rehomed.push({ from: id, to: created.spec.id });
|
|
24560
24980
|
plan.created.push(created.named);
|
|
24561
24981
|
plan.notices.push(...created.notices);
|
|
24562
24982
|
}
|
|
@@ -24686,7 +25106,7 @@ var SpecWorkspace = class {
|
|
|
24686
25106
|
* here: every save does it already, and a second call would only be a hop
|
|
24687
25107
|
* across a component boundary that no narrative claims.
|
|
24688
25108
|
*/
|
|
24689
|
-
writeMoveEdits(edits) {
|
|
25109
|
+
writeMoveEdits(edits, finish) {
|
|
24690
25110
|
const reports = moveChangeReports(edits, false);
|
|
24691
25111
|
const written = [];
|
|
24692
25112
|
try {
|
|
@@ -24695,6 +25115,7 @@ var SpecWorkspace = class {
|
|
|
24695
25115
|
report.notices.push(...this.saveMovedSpec(edit.kind, edit.after));
|
|
24696
25116
|
written.push(edit);
|
|
24697
25117
|
}
|
|
25118
|
+
finish?.();
|
|
24698
25119
|
} catch (e) {
|
|
24699
25120
|
for (const edit of [...written].reverse()) {
|
|
24700
25121
|
try {
|
|
@@ -24801,6 +25222,15 @@ function moveChangeReports(edits, dryRun) {
|
|
|
24801
25222
|
summary: edit.before === null ? `${dryRun ? "Would create" : "Created"} ${edit.kind} "${edit.id}" to receive the moved methods.` : `${dryRun ? "Would move" : "Moved"} into ${edit.kind} "${edit.id}": ${changes.length} change${changes.length === 1 ? "" : "s"}.`
|
|
24802
25223
|
}));
|
|
24803
25224
|
}
|
|
25225
|
+
function moveRename(from, to, methods, plan) {
|
|
25226
|
+
return {
|
|
25227
|
+
specs: plan.rehomed.map(({ from: left, to: arrived }) => ({ from: left, to: arrived, sites: [...methods] })),
|
|
25228
|
+
methods: methods.map((method) => ({ component: from, method, toComponent: to, toMethod: method }))
|
|
25229
|
+
};
|
|
25230
|
+
}
|
|
25231
|
+
function registerClause(carried, verb) {
|
|
25232
|
+
return carried.length ? `, ${verb} ${carried.length} debt-register key${carried.length === 1 ? "" : "s"}` : "";
|
|
25233
|
+
}
|
|
24804
25234
|
function createdClause(created, verb) {
|
|
24805
25235
|
return created.length ? `, ${verb} ${created.join(" and ")}` : "";
|
|
24806
25236
|
}
|
|
@@ -25490,6 +25920,42 @@ function rewriteRefFields(specsDir, remap, excludeDir) {
|
|
|
25490
25920
|
}
|
|
25491
25921
|
return rewritten;
|
|
25492
25922
|
}
|
|
25923
|
+
function rekeyLintAllows(specsDir, rename) {
|
|
25924
|
+
const rewritten = [];
|
|
25925
|
+
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
25926
|
+
let raw;
|
|
25927
|
+
try {
|
|
25928
|
+
raw = readYamlFile(file);
|
|
25929
|
+
} catch {
|
|
25930
|
+
continue;
|
|
25931
|
+
}
|
|
25932
|
+
const allows = raw?.lint?.allow;
|
|
25933
|
+
const kind = raw && typeof raw === "object" ? specKind(raw) : null;
|
|
25934
|
+
if (!kind || !Array.isArray(allows)) continue;
|
|
25935
|
+
const specId = kind === "system" ? "system" : String(raw.id);
|
|
25936
|
+
let changed = false;
|
|
25937
|
+
for (const allow of allows) {
|
|
25938
|
+
if (!allow || typeof allow !== "object") continue;
|
|
25939
|
+
const next = rekeyAnchor(rename, {
|
|
25940
|
+
spec: specId,
|
|
25941
|
+
...typeof allow.at === "string" ? { at: allow.at } : {},
|
|
25942
|
+
...Array.isArray(allow.covers) ? { covers: allow.covers } : {}
|
|
25943
|
+
});
|
|
25944
|
+
if (next.at !== void 0 && next.at !== allow.at) {
|
|
25945
|
+
allow.at = next.at;
|
|
25946
|
+
changed = true;
|
|
25947
|
+
}
|
|
25948
|
+
if (next.covers && JSON.stringify(next.covers) !== JSON.stringify(allow.covers)) {
|
|
25949
|
+
allow.covers = next.covers;
|
|
25950
|
+
changed = true;
|
|
25951
|
+
}
|
|
25952
|
+
}
|
|
25953
|
+
if (!changed) continue;
|
|
25954
|
+
writeYamlFile(file, raw);
|
|
25955
|
+
rewritten.push({ kind, id: specId });
|
|
25956
|
+
}
|
|
25957
|
+
return rewritten;
|
|
25958
|
+
}
|
|
25493
25959
|
function rebaseImplementationPaths(specsDir, fromRoot, toRoot) {
|
|
25494
25960
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
25495
25961
|
let raw;
|
|
@@ -25559,24 +26025,32 @@ function renameComponent(componentId, newId) {
|
|
|
25559
26025
|
...movingInterface ? [{ kind: "interface", from: movingInterface.id, to: interfaceId }] : [],
|
|
25560
26026
|
...movingImplementation ? [{ kind: "implementation", from: movingImplementation.id, to: implementationId }] : []
|
|
25561
26027
|
];
|
|
25562
|
-
const
|
|
26028
|
+
const rename = {
|
|
26029
|
+
specs: renamed.map(({ from, to }) => ({ from, to })),
|
|
26030
|
+
components: [{ from: componentId, to: newId }]
|
|
26031
|
+
};
|
|
26032
|
+
projectConfigRepository.rekeyCarried(rename, true);
|
|
26033
|
+
const rewrittenRefs = rewriteRefFields(aiPathsAt(getProjectRoot()).specsDir(), (ref, position) => {
|
|
25563
26034
|
if (position === "component" || position === "entity-class" || position === "auth-source") {
|
|
25564
26035
|
return ref === componentId ? newId : ref;
|
|
25565
26036
|
}
|
|
25566
26037
|
if (position === "interface" && ref === movingInterface?.id) return interfaceId;
|
|
25567
26038
|
return ref;
|
|
25568
|
-
})
|
|
26039
|
+
});
|
|
26040
|
+
const allowsRekeyed = rekeyLintAllows(aiPathsAt(getProjectRoot()).specsDir(), rename);
|
|
26041
|
+
const rewritten = [...new Set([...rewrittenRefs, ...allowsRekeyed].filter((spec) => !renamed.some((moved) => moved.kind === spec.kind && moved.from === spec.id)).map((spec) => spec.id))];
|
|
25569
26042
|
invalidateSpecCache();
|
|
25570
26043
|
moveRenamedSpecs(componentId, newId, component, movingInterface, movingImplementation);
|
|
25571
|
-
|
|
26044
|
+
const carried = projectConfigRepository.rekeyCarried(rename);
|
|
26045
|
+
return { renamed, rewritten, carried };
|
|
25572
26046
|
}
|
|
25573
26047
|
function moveRenamedSpecs(componentId, newId, component, movingInterface, movingImplementation) {
|
|
25574
26048
|
const interfaceId = `i${newId}`;
|
|
25575
26049
|
const implementationId = `${newId}_impl`;
|
|
25576
|
-
const
|
|
26050
|
+
const movedComponent2 = loadComponentSpec(componentId) ?? component;
|
|
25577
26051
|
const movedInterface = movingInterface ? loadInterfaceSpec(movingInterface.id) ?? movingInterface : null;
|
|
25578
26052
|
const movedImplementation = movingImplementation ? loadImplementationSpec(movingImplementation.id) ?? movingImplementation : null;
|
|
25579
|
-
saveComponentSpec({ ...
|
|
26053
|
+
saveComponentSpec({ ...movedComponent2, id: newId });
|
|
25580
26054
|
if (movedInterface) saveInterfaceSpec({ ...movedInterface, id: interfaceId, component: newId });
|
|
25581
26055
|
if (movedImplementation) {
|
|
25582
26056
|
const oldFile = getImplementationPath(movedImplementation.id);
|
|
@@ -25641,6 +26115,19 @@ function renameMethod(componentId, methodName, newName, pinSymbol) {
|
|
|
25641
26115
|
const implementations = loadImplementationSpecs();
|
|
25642
26116
|
const movingContracts = new Set(moving.map((i) => i.id));
|
|
25643
26117
|
const renamed = [];
|
|
26118
|
+
const rename = {
|
|
26119
|
+
methods: [{
|
|
26120
|
+
component: componentId,
|
|
26121
|
+
method: methodName,
|
|
26122
|
+
toComponent: componentId,
|
|
26123
|
+
toMethod: newName,
|
|
26124
|
+
specs: [
|
|
26125
|
+
...moving.map((i) => i.id),
|
|
26126
|
+
...implementations.filter((impl) => movingContracts.has(impl.contract) && impl.methods.some((m) => m.name === methodName)).map((impl) => impl.id)
|
|
26127
|
+
]
|
|
26128
|
+
}]
|
|
26129
|
+
};
|
|
26130
|
+
projectConfigRepository.rekeyCarried(rename, true);
|
|
25644
26131
|
for (const contract of moving) {
|
|
25645
26132
|
saveInterfaceSpec({
|
|
25646
26133
|
...contract,
|
|
@@ -25663,8 +26150,11 @@ function renameMethod(componentId, methodName, newName, pinSymbol) {
|
|
|
25663
26150
|
}
|
|
25664
26151
|
invalidateSpecCache();
|
|
25665
26152
|
const specsDir = aiPathsAt(getProjectRoot()).specsDir();
|
|
25666
|
-
const
|
|
26153
|
+
const retargeted = rewriteRefFields(specsDir, (ref, position, owner) => position === "method" && owner === componentId && ref === methodName ? newName : ref);
|
|
26154
|
+
const allowsRekeyed = rekeyLintAllows(specsDir, rename);
|
|
26155
|
+
const rewritten = [...new Set([...retargeted, ...allowsRekeyed].map((spec) => spec.id))];
|
|
25667
26156
|
const mentions = collectMentions(specsDir, methodName, moving);
|
|
26157
|
+
const carried = projectConfigRepository.rekeyCarried(rename);
|
|
25668
26158
|
return {
|
|
25669
26159
|
component: componentId,
|
|
25670
26160
|
from: methodName,
|
|
@@ -25672,7 +26162,8 @@ function renameMethod(componentId, methodName, newName, pinSymbol) {
|
|
|
25672
26162
|
renamed,
|
|
25673
26163
|
rewritten,
|
|
25674
26164
|
mentions,
|
|
25675
|
-
...pinnedSymbol ? { pinnedSymbol } : {}
|
|
26165
|
+
...pinnedSymbol ? { pinnedSymbol } : {},
|
|
26166
|
+
carried
|
|
25676
26167
|
};
|
|
25677
26168
|
}
|
|
25678
26169
|
function renameInSignature(signature, from, to) {
|
|
@@ -28048,7 +28539,8 @@ var MISSING_PARENT = {
|
|
|
28048
28539
|
subsystem: () => "System spec must be initialized (sdd_initialize_system) first.",
|
|
28049
28540
|
component: (id) => `Parent subsystem "${id}" does not exist.`,
|
|
28050
28541
|
interface: (id) => `Component "${id}" does not exist.`,
|
|
28051
|
-
implementation: (id) => `Interface contract "${id}" does not exist
|
|
28542
|
+
implementation: (id) => `Interface contract "${id}" does not exist.`,
|
|
28543
|
+
type: (id) => `Owning subsystem "${id}" does not exist \u2014 name a subsystem the tree has, or omit subsystem for a system-level value object.`
|
|
28052
28544
|
};
|
|
28053
28545
|
var REWRITE_LABEL = {
|
|
28054
28546
|
system: "System spec",
|
|
@@ -28069,6 +28561,8 @@ function restatementParent(restatement) {
|
|
|
28069
28561
|
return { kind: "component", id: spec.component };
|
|
28070
28562
|
case "implementation":
|
|
28071
28563
|
return { kind: "interface", id: spec.contract };
|
|
28564
|
+
case "type":
|
|
28565
|
+
return typeof spec.subsystem === "string" && spec.subsystem !== "" ? { kind: "subsystem", id: spec.subsystem } : null;
|
|
28072
28566
|
default:
|
|
28073
28567
|
return null;
|
|
28074
28568
|
}
|
|
@@ -28272,7 +28766,7 @@ function writeSpec(restatement) {
|
|
|
28272
28766
|
if (application.refusal !== void 0) throw new Error(application.refusal);
|
|
28273
28767
|
const gateNotices = restatement.kind === "component" ? judgeComponent(application.spec, bound2) : [];
|
|
28274
28768
|
const persisted = persistCandidate(restatement.kind, application.spec);
|
|
28275
|
-
const testsToRevisit = testsInvalidatedBy(application.changedMethods, [existing, application.spec], bound2);
|
|
28769
|
+
const testsToRevisit = testsInvalidatedBy(restatement.kind, id, application.changedMethods, [existing, application.spec], bound2);
|
|
28276
28770
|
return {
|
|
28277
28771
|
kind: restatement.kind,
|
|
28278
28772
|
id,
|
|
@@ -28297,42 +28791,59 @@ function persistCandidate(kind, spec) {
|
|
|
28297
28791
|
}
|
|
28298
28792
|
return { notices: saveSpec(kind, spec) };
|
|
28299
28793
|
}
|
|
28300
|
-
function testsInvalidatedBy(methods, specs, options) {
|
|
28794
|
+
function testsInvalidatedBy(kind, id, methods, specs, options, written = []) {
|
|
28301
28795
|
const testRoots = options.rules?.conformance?.testRoots ?? [];
|
|
28302
28796
|
if (methods.length === 0 || testRoots.length === 0) return [];
|
|
28303
|
-
|
|
28304
|
-
|
|
28797
|
+
return findTestsReferencing2(searchedMethods(kind, id, methods, specs, written), getProjectRoot(), testRoots);
|
|
28798
|
+
}
|
|
28799
|
+
function searchedMethods(kind, id, names, specs, written) {
|
|
28800
|
+
const stated = (name) => written.find((m) => m?.name === name)?.symbol;
|
|
28801
|
+
if (kind === "interface") {
|
|
28802
|
+
const realizations = loadImplementationSpecs().filter((impl) => impl.contract === id);
|
|
28803
|
+
return names.flatMap((name) => {
|
|
28804
|
+
const found = realizations.flatMap((impl) => impl.methods.filter((m) => m.name === name).map((m) => ({ name, symbol: stated(name) ?? m.symbol, sourcePath: m.sourcePath ?? impl.sourcePath })));
|
|
28805
|
+
return found.length > 0 ? found : [{ name, symbol: stated(name) }];
|
|
28806
|
+
});
|
|
28807
|
+
}
|
|
28808
|
+
return names.map((name) => {
|
|
28809
|
+
const holders = specs.map((spec) => {
|
|
28810
|
+
const holder = spec;
|
|
28811
|
+
const method = holder?.methods?.find((m) => m.name === name);
|
|
28812
|
+
return method ? { method, file: method.sourcePath ?? holder?.sourcePath } : null;
|
|
28813
|
+
}).filter((h) => h !== null);
|
|
28814
|
+
const symbol = stated(name) ?? holders.map((h) => h.method.symbol).find((s) => s !== void 0);
|
|
28815
|
+
const sourcePath = holders.map((h) => h.file).find((f) => f !== void 0) ?? specs.map((spec) => spec?.sourcePath).find((f) => f !== void 0);
|
|
28816
|
+
return { name, symbol, sourcePath };
|
|
28817
|
+
});
|
|
28305
28818
|
}
|
|
28306
28819
|
function deleteSpec2(kind, id) {
|
|
28307
28820
|
const bound2 = candidateOptions();
|
|
28308
28821
|
const stored = loadSpec(kind, id);
|
|
28309
28822
|
const deleted = deleteSpec(kind, id);
|
|
28310
28823
|
const methods = deleted ? (stored?.methods ?? []).map((m) => m.name) : [];
|
|
28311
|
-
const testsToRevisit = testsInvalidatedBy(methods, [stored], bound2);
|
|
28824
|
+
const testsToRevisit = testsInvalidatedBy(kind, id, methods, [stored], bound2);
|
|
28312
28825
|
return { kind, id, deleted, testsToRevisit };
|
|
28313
28826
|
}
|
|
28314
28827
|
function updateSpecGated(kind, id, delta, dryRun) {
|
|
28315
28828
|
const bound2 = candidateOptions();
|
|
28316
28829
|
const gate = componentCandidateGate(bound2);
|
|
28317
|
-
const report = updateSpec(kind, id, delta, gate, dryRun);
|
|
28318
28830
|
const testRoots = bound2.rules?.conformance?.testRoots ?? [];
|
|
28319
|
-
const
|
|
28831
|
+
const stored = testRoots.length > 0 ? loadSpec(kind, id) : null;
|
|
28832
|
+
const report = updateSpec(kind, id, delta, gate, dryRun);
|
|
28833
|
+
const changed = changedMethods(report);
|
|
28320
28834
|
if (changed.length > 0 && testRoots.length > 0) {
|
|
28321
|
-
|
|
28835
|
+
const written = Array.isArray(delta?.methods) ? delta.methods : [];
|
|
28836
|
+
report.testsToRevisit = testsInvalidatedBy(kind, id, changed, [stored], bound2, written);
|
|
28322
28837
|
}
|
|
28323
28838
|
return report;
|
|
28324
28839
|
}
|
|
28325
|
-
function changedMethods(report
|
|
28840
|
+
function changedMethods(report) {
|
|
28326
28841
|
const named = /* @__PURE__ */ new Set();
|
|
28327
28842
|
for (const change of report.changes) {
|
|
28328
28843
|
const [field, name] = change.path.split(".");
|
|
28329
28844
|
if (field === "methods" && name) named.add(name);
|
|
28330
28845
|
}
|
|
28331
|
-
|
|
28332
|
-
return [...named].map((name) => ({
|
|
28333
|
-
name,
|
|
28334
|
-
symbol: written.find((m) => m?.name === name)?.symbol
|
|
28335
|
-
}));
|
|
28846
|
+
return [...named];
|
|
28336
28847
|
}
|
|
28337
28848
|
function methodMoveGate(source) {
|
|
28338
28849
|
const bound2 = candidateOptions();
|
|
@@ -29288,7 +29799,7 @@ ${renderChangeReport(report)}`,
|
|
|
29288
29799
|
server,
|
|
29289
29800
|
"sdd_rename_component",
|
|
29290
29801
|
{
|
|
29291
|
-
description: "Rename a component and rewrite every reference to it in the bound tree: dependsOn, owns and dispatch entries, lifecycle entrypoints, published interfaces at L1 and L0, narrative call/dispatch/register targets, an interface's component, an implementation's contract and an entity's componentClass. The interface named i<id> and the implementation named <id>_impl move to the new ids and files; interfaces and implementations named otherwise keep their ids. Display names are left as they are. Refuses, writing nothing: a component that does not exist (component-missing), one inside a chained subproject (chained-component \u2014 rename it from that project's own root), a new id that is not a lowercase identifier without a namespace separator (invalid-id), and a new id \u2014 or i<newId> or <newId>_impl \u2014 already in use (id-taken). Returns the specs moved, each with its old and new id,
|
|
29802
|
+
description: "Rename a component and rewrite every reference to it in the bound tree: dependsOn, owns and dispatch entries, lifecycle entrypoints, published interfaces at L1 and L0, narrative call/dispatch/register targets, an interface's component, an implementation's contract and an entity's componentClass. The interface named i<id> and the implementation named <id>_impl move to the new ids and files; interfaces and implementations named otherwise keep their ids. Findings keyed on the component follow it too: every lint allow naming it on an edge site or in a covered unit, and every entry of the debt register (.wai/project.yaml rules.conformance.carried) anchored on a renamed spec or naming the component in its site or units \u2014 rewritten in place, every comment and all other formatting of the register kept, so unchanged debt never reads as paid and new at once. A register that cannot be rewritten that precisely is refused before the first write. Display names are left as they are. Refuses, writing nothing: a component that does not exist (component-missing), one inside a chained subproject (chained-component \u2014 rename it from that project's own root), a new id that is not a lowercase identifier without a namespace separator (invalid-id), and a new id \u2014 or i<newId> or <newId>_impl \u2014 already in use (id-taken). Returns the specs moved, each with its old and new id, the ids of the other specs rewritten (lint allows included), and `carried`: each register edit \u2014 the entry by code, spec and site as it stood, the key changed (spec, at or covers), from and to.",
|
|
29292
29803
|
inputSchema: {
|
|
29293
29804
|
id: import_zod11.z.string().describe("The component to rename (namespaced if needed)"),
|
|
29294
29805
|
newId: import_zod11.z.string().describe("Its new id: a lowercase identifier with no namespace separator")
|
|
@@ -29306,7 +29817,7 @@ ${renderChangeReport(report)}`,
|
|
|
29306
29817
|
server,
|
|
29307
29818
|
"sdd_rename_method",
|
|
29308
29819
|
{
|
|
29309
|
-
description: "Rename a contract method and retarget every reference to it in the bound tree. The method moves on every interface of the component that declares it \u2014 its name, and the name inside its signature \u2014 and on the implementations of those contracts, carrying narrative, sourcePath, symbol, detail, intent and findings unchanged; an implementation that declared no symbol is pinned to the old name unless pinSymbol is false, so the function it already binds to keeps binding. Narrative call, register and dispatch steps naming this component and method, dispatch-table bindings and lifecycle entrypoints are retargeted. Prose is never rewritten and a gRPC endpoint binding keeps its wire method \u2014 renaming a contract method must not silently rename an RPC; both are reported as mentions. Refuses, writing nothing: a component that does not exist (component-missing), one inside a chained subproject (chained-component \u2014 rename its method from that project's own root), a new name that is not a camel-case identifier (invalid-name), a method the component does not declare (method-missing), and a name a moving contract already declares (name-taken). Returns the specs the method moved in, the specs retargeted, the specs whose prose still names it,
|
|
29820
|
+
description: "Rename a contract method and retarget every reference to it in the bound tree. The method moves on every interface of the component that declares it \u2014 its name, and the name inside its signature \u2014 and on the implementations of those contracts, carrying narrative, sourcePath, symbol, detail, intent and findings unchanged; an implementation that declared no symbol is pinned to the old name unless pinSymbol is false, so the function it already binds to keeps binding. Narrative call, register and dispatch steps naming this component and method, dispatch-table bindings and lifecycle entrypoints are retargeted, and so are the findings keyed on it: a lint allow at the method on a spec it moved in or covering `<component>.<method>`, and every debt-register entry (.wai/project.yaml rules.conformance.carried) keyed the same way \u2014 rewritten in place with every comment and all other formatting kept (a register that cannot be rewritten that precisely is refused before the first write). Prose is never rewritten and a gRPC endpoint binding keeps its wire method \u2014 renaming a contract method must not silently rename an RPC; both are reported as mentions. Refuses, writing nothing: a component that does not exist (component-missing), one inside a chained subproject (chained-component \u2014 rename its method from that project's own root), a new name that is not a camel-case identifier (invalid-name), a method the component does not declare (method-missing), and a name a moving contract already declares (name-taken). Returns the specs the method moved in, the specs retargeted (lint allows included), the specs whose prose still names it, the pinned symbol when one was set, and `carried`: each register edit.",
|
|
29310
29821
|
inputSchema: {
|
|
29311
29822
|
id: import_zod11.z.string().describe("The component whose method is renamed (namespaced if needed)"),
|
|
29312
29823
|
method: import_zod11.z.string().describe("The method name as it stands"),
|
|
@@ -29326,7 +29837,7 @@ ${renderChangeReport(report)}`,
|
|
|
29326
29837
|
server,
|
|
29327
29838
|
"sdd_move_methods",
|
|
29328
29839
|
{
|
|
29329
|
-
description: "Move contract methods from one component to another, carrying each method's narrative, calls and bindings, and re-pointing every reference that named the old home: narrative call/register/dispatch steps, dispatch-table bindings, lifecycle entrypoints and `calls` entries. The target gains the dependencies the moved narratives call. ONE all-or-nothing gated write: if a rule refuses the move nothing is written and the report names the rule plus where these methods could live instead, cheapest legal home first, with the requested target among them so its shortfall reads beside the others. Prose is never rewritten and a wire endpoint keeps its address \u2014 moving a method between components must not silently re-address an RPC; both are reported as mentions. A target that declares no contract yet receives one, i<target>, and when implementation entries travel and no implementation realizes the receiving contract, <target>_impl is created for them, inheriting the source implementation's sourcePath; the report's `created` names every spec the move created. Refuses before the first write (unmovable request): a component or method that does not exist, a method name already declared on the target, a source and target that are the same component, a target with several contracts none named i<target>, a contract or implementation to create whose id another spec already holds, and a component inside a chained subproject (move it from that project's own root). A dry run runs the whole move, gate included, and reports what it would have done.",
|
|
29840
|
+
description: "Move contract methods from one component to another, carrying each method's narrative, calls and bindings, and re-pointing every reference that named the old home: narrative call/register/dispatch steps, dispatch-table bindings, lifecycle entrypoints and `calls` entries. The target gains the dependencies the moved narratives call. Findings keyed on a moved method follow it: a lint allow anchored at it moves to the receiving spec, one covering `<from>.<method>` names `<to>.<method>`, and the debt register (.wai/project.yaml rules.conformance.carried) is rekeyed the same way in place, every comment and all other formatting kept \u2014 the report's `carried` lists each register edit, and a register that cannot be rewritten that precisely is refused before the first write. ONE all-or-nothing gated write: if a rule refuses the move nothing is written and the report names the rule plus where these methods could live instead, cheapest legal home first, with the requested target among them so its shortfall reads beside the others. Prose is never rewritten and a wire endpoint keeps its address \u2014 moving a method between components must not silently re-address an RPC; both are reported as mentions. A target that declares no contract yet receives one, i<target>, and when implementation entries travel and no implementation realizes the receiving contract, <target>_impl is created for them, inheriting the source implementation's sourcePath; the report's `created` names every spec the move created. Refuses before the first write (unmovable request): a component or method that does not exist, a method name already declared on the target, a source and target that are the same component, a target with several contracts none named i<target>, a contract or implementation to create whose id another spec already holds, and a component inside a chained subproject (move it from that project's own root). A dry run runs the whole move, gate included, and reports what it would have done.",
|
|
29330
29841
|
inputSchema: {
|
|
29331
29842
|
from: import_zod11.z.string().describe("The component the methods belong to (namespaced if needed)"),
|
|
29332
29843
|
to: import_zod11.z.string().describe("The component that should receive them (namespaced if needed)"),
|
|
@@ -29679,7 +30190,7 @@ ${renderChangeReport(report)}`,
|
|
|
29679
30190
|
id: import_zod11.z.string().describe("Lowercase identifier"),
|
|
29680
30191
|
name: import_zod11.z.string().describe("Human-readable name"),
|
|
29681
30192
|
description: import_zod11.z.string().optional(),
|
|
29682
|
-
subsystem: import_zod11.z.string().optional().describe("Owning subsystem id; omit for a system-level shared value object"),
|
|
30193
|
+
subsystem: import_zod11.z.string().optional().describe("Owning subsystem id; omit for a system-level shared value object. A subsystem the tree does not have is refused and nothing is written"),
|
|
29683
30194
|
group: import_zod11.z.string().optional().describe("Optional logical group ID to organize this type in subfolders"),
|
|
29684
30195
|
fields: import_zod11.z.array(import_zod11.z.object({
|
|
29685
30196
|
name: import_zod11.z.string(),
|
|
@@ -29713,7 +30224,7 @@ ${renderChangeReport(report)}`,
|
|
|
29713
30224
|
server,
|
|
29714
30225
|
"sdd_add_type",
|
|
29715
30226
|
{
|
|
29716
|
-
description: "Define an entity or value-object type (the data components operate on). Entities are owned by a subsystem; shared value objects omit subsystem (system-level). Fields are data; methods are PURE intrinsic behaviour only \u2014 anything needing a collaborator belongs on a component, taking the entity as an argument. A type may also CLAIM code: sourcePath names the file holding its declaration (and each method may name its own), symbol binds the code-level name when it differs \u2014 the file must then resolve and the declaration must be anchored in it. Re-defining an existing id REPLACES fields/methods/invariants and restates sourcePath/symbol (an omitted list or path is CLEARED, and a dropped member is reported); lint/ext are carried forward. The answer carries a write receipt as structured content beside the sentence \u2014 whether a spec already held the id, and the notices a restatement raised, each as its own entry. A type carries no lifecycle status, so the receipt states none.",
|
|
30227
|
+
description: "Define an entity or value-object type (the data components operate on). Entities are owned by a subsystem; shared value objects omit subsystem (system-level). Fields are data; methods are PURE intrinsic behaviour only \u2014 anything needing a collaborator belongs on a component, taking the entity as an argument. A type may also CLAIM code: sourcePath names the file holding its declaration (and each method may name its own), symbol binds the code-level name when it differs \u2014 the file must then resolve and the declaration must be anchored in it. An owning subsystem the tree does not have is refused, writing nothing, exactly as a component under an unknown subsystem is. Re-defining an existing id REPLACES fields/methods/invariants and restates sourcePath/symbol (an omitted list or path is CLEARED, and a dropped member is reported); lint/ext are carried forward. The answer carries a write receipt as structured content beside the sentence \u2014 whether a spec already held the id, and the notices a restatement raised, each as its own entry. A type carries no lifecycle status, so the receipt states none.",
|
|
29717
30228
|
inputSchema: typeInput,
|
|
29718
30229
|
outputSchema: specWriteReceiptOutput
|
|
29719
30230
|
},
|