@wairon/cli 5.1.1-dev.85 → 5.1.1-dev.86
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/README.md +1 -1
- package/dist/cli/index.js +956 -418
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +428 -134
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3317,8 +3317,8 @@ var require_dist = __commonJS({
|
|
|
3317
3317
|
return normalized === "." ? ".wai/" : `${normalized}/.wai/`;
|
|
3318
3318
|
}
|
|
3319
3319
|
function normalizeRootPath(relativePath) {
|
|
3320
|
-
const
|
|
3321
|
-
return
|
|
3320
|
+
const posix3 = relativePath.split(path210.sep).join("/").replace(/^\.\//, "").replace(/\/+$/, "");
|
|
3321
|
+
return posix3 === "" || posix3 === "." ? "." : posix3;
|
|
3322
3322
|
}
|
|
3323
3323
|
function slugify(projectName) {
|
|
3324
3324
|
const slug2 = projectName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -3542,6 +3542,7 @@ __export(src_exports, {
|
|
|
3542
3542
|
complexityLevel: () => complexityLevel,
|
|
3543
3543
|
composeAgentBrief: () => composeAgentBrief,
|
|
3544
3544
|
composeRuleSequence: () => composeRuleSequence,
|
|
3545
|
+
computeOwnStateId: () => computeOwnStateId,
|
|
3545
3546
|
computeStateId: () => computeStateId,
|
|
3546
3547
|
computeStateIdAt: () => computeStateIdAt,
|
|
3547
3548
|
conceptNoun: () => conceptNoun,
|
|
@@ -3552,7 +3553,6 @@ __export(src_exports, {
|
|
|
3552
3553
|
createEmptyTopologyConfig: () => createEmptyTopologyConfig,
|
|
3553
3554
|
createMember: () => createMember,
|
|
3554
3555
|
createProjectConfig: () => createProjectConfig,
|
|
3555
|
-
currentChildPins: () => currentChildPins,
|
|
3556
3556
|
currentRootBinding: () => currentRootBinding,
|
|
3557
3557
|
declareExternal: () => declareExternal,
|
|
3558
3558
|
declaredExternals: () => declaredExternals,
|
|
@@ -3632,6 +3632,7 @@ __export(src_exports, {
|
|
|
3632
3632
|
isProjectInitialized: () => isProjectInitialized,
|
|
3633
3633
|
isProvidedBy: () => isProvidedBy,
|
|
3634
3634
|
isRetired: () => isRetired,
|
|
3635
|
+
judgesCode: () => judgesCode,
|
|
3635
3636
|
keyIn: () => keyIn,
|
|
3636
3637
|
landReference: () => landReference,
|
|
3637
3638
|
listDirectChainedSubprojects: () => listDirectChainedSubprojects,
|
|
@@ -3674,7 +3675,6 @@ __export(src_exports, {
|
|
|
3674
3675
|
moveMember: () => moveMember,
|
|
3675
3676
|
moveMethods: () => moveMethods,
|
|
3676
3677
|
moveMountToMembers: () => moveMountToMembers,
|
|
3677
|
-
movedChildren: () => movedChildren,
|
|
3678
3678
|
nameKey: () => nameKey,
|
|
3679
3679
|
narrativeStepForeignFields: () => narrativeStepForeignFields,
|
|
3680
3680
|
normalizeReferences: () => normalizeReferences2,
|
|
@@ -4154,6 +4154,13 @@ var AgentRecordSchema = import_zod2.z.object({
|
|
|
4154
4154
|
dependencies: import_zod2.z.array(import_zod2.z.string()).default([]),
|
|
4155
4155
|
/** Rendered implementation guidance for this agent's variant-tagged components (deep variant integration); empty when none. */
|
|
4156
4156
|
variantGuidance: import_zod2.z.string().optional(),
|
|
4157
|
+
/**
|
|
4158
|
+
* A member project's delegating owner only: the ids of the member's own
|
|
4159
|
+
* agents, each qualified `<alias>::<agentId>`, resolved from the member's
|
|
4160
|
+
* topology at the member's own root. REFERENCES, never copies: nothing of the
|
|
4161
|
+
* member's agents is rendered into this layer (one level deep, ids only).
|
|
4162
|
+
*/
|
|
4163
|
+
delegatesTo: import_zod2.z.array(import_zod2.z.string()).optional(),
|
|
4157
4164
|
/** Why this agent was created — the architectural reason for its existence */
|
|
4158
4165
|
creationReason: import_zod2.z.string(),
|
|
4159
4166
|
status: AgentStatusSchema.default("active"),
|
|
@@ -4185,6 +4192,13 @@ var AgentBriefSchema = import_zod2.z.object({
|
|
|
4185
4192
|
instructions: import_zod2.z.string(),
|
|
4186
4193
|
/** Rendered variant guidance, also folded into instructions */
|
|
4187
4194
|
variantGuidance: import_zod2.z.string().optional(),
|
|
4195
|
+
/**
|
|
4196
|
+
* Set only on a brief for a member's agent (an id qualified `<alias>::`): the
|
|
4197
|
+
* member's directory relative to the root the brief was asked from. The
|
|
4198
|
+
* brief was composed at that root, and its ownedPaths and readPaths are
|
|
4199
|
+
* re-expressed under this prefix so the fence is correct from the asking root.
|
|
4200
|
+
*/
|
|
4201
|
+
root: import_zod2.z.string().optional(),
|
|
4188
4202
|
/**
|
|
4189
4203
|
* The resource shape of this agent's work, and the allowance it earns.
|
|
4190
4204
|
*
|
|
@@ -4579,7 +4593,13 @@ var PackRequirementSchema = import_zod3.z.object({
|
|
|
4579
4593
|
profile: import_zod3.z.string().optional()
|
|
4580
4594
|
});
|
|
4581
4595
|
var CompositionConfigSchema = import_zod3.z.object({
|
|
4582
|
-
requirePolicies: import_zod3.z.array(PackRequirementSchema).optional()
|
|
4596
|
+
requirePolicies: import_zod3.z.array(PackRequirementSchema).optional(),
|
|
4597
|
+
/**
|
|
4598
|
+
* Opt-in, off by default (stage 5). When true, this project's lock refuses
|
|
4599
|
+
* while any DIRECT member is drifted or never approved, naming each, and
|
|
4600
|
+
* writes nothing. Off: the lock proceeds and records each member's state.
|
|
4601
|
+
*/
|
|
4602
|
+
requireApprovedMembers: import_zod3.z.boolean().optional()
|
|
4583
4603
|
});
|
|
4584
4604
|
var ProjectConfigSchema = import_zod3.z.object({
|
|
4585
4605
|
/**
|
|
@@ -5929,18 +5949,26 @@ var CONTENT_ALGORITHM = "sha256";
|
|
|
5929
5949
|
function inIdentityOrder(specs) {
|
|
5930
5950
|
return specs.map((spec) => ({ spec, content: canonicalize(spec) })).sort((a, b) => compareOrdinal(a.spec.id, b.spec.id) || compareOrdinal(a.content, b.content)).map((entry) => entry.spec);
|
|
5931
5951
|
}
|
|
5932
|
-
function
|
|
5952
|
+
function digestTree(keep) {
|
|
5953
|
+
const kind = (specs) => inIdentityOrder(specs.filter((s) => keep(s.id)));
|
|
5933
5954
|
const tree = {
|
|
5934
5955
|
system: loadSystemSpec(),
|
|
5935
|
-
subsystems:
|
|
5936
|
-
components:
|
|
5937
|
-
interfaces:
|
|
5938
|
-
implementations:
|
|
5939
|
-
types:
|
|
5956
|
+
subsystems: kind(loadSubsystemSpecs()),
|
|
5957
|
+
components: kind(loadComponentSpecs()),
|
|
5958
|
+
interfaces: kind(loadInterfaceSpecs()),
|
|
5959
|
+
implementations: kind(loadImplementationSpecs()),
|
|
5960
|
+
types: kind(loadTypeSpecs())
|
|
5940
5961
|
};
|
|
5941
5962
|
const digest2 = crypto.createHash("sha256").update(canonicalize(tree)).digest("hex");
|
|
5942
5963
|
return { algorithm: CONTENT_ALGORITHM, digest: digest2 };
|
|
5943
5964
|
}
|
|
5965
|
+
function computeStateId() {
|
|
5966
|
+
return digestTree(() => true);
|
|
5967
|
+
}
|
|
5968
|
+
function computeOwnStateId() {
|
|
5969
|
+
const owners = graph().owners;
|
|
5970
|
+
return digestTree((id) => (owners.get(id) ?? "") === "");
|
|
5971
|
+
}
|
|
5944
5972
|
function stateIdEquals(a, b) {
|
|
5945
5973
|
return !!a && !!b && a.algorithm === b.algorithm && a.digest === b.digest;
|
|
5946
5974
|
}
|
|
@@ -5991,12 +6019,13 @@ function withSortedMaps(record) {
|
|
|
5991
6019
|
for (const k of Object.keys(m).sort()) out[k] = m[k];
|
|
5992
6020
|
return out;
|
|
5993
6021
|
};
|
|
5994
|
-
const
|
|
5995
|
-
const
|
|
6022
|
+
const { children: _legacy, ...rest } = record;
|
|
6023
|
+
const specs = sorted(rest.specs);
|
|
6024
|
+
const members = sorted(rest.members);
|
|
5996
6025
|
return {
|
|
5997
|
-
...
|
|
6026
|
+
...rest,
|
|
5998
6027
|
...specs ? { specs } : {},
|
|
5999
|
-
...
|
|
6028
|
+
...members ? { members } : {}
|
|
6000
6029
|
};
|
|
6001
6030
|
}
|
|
6002
6031
|
|
|
@@ -7797,12 +7826,12 @@ function exportTargetKey(e) {
|
|
|
7797
7826
|
}
|
|
7798
7827
|
|
|
7799
7828
|
// src/models/project-family.ts
|
|
7800
|
-
function walkMemberPath(
|
|
7829
|
+
function walkMemberPath(byKey2, start, segments) {
|
|
7801
7830
|
let node = start;
|
|
7802
7831
|
for (const segment of segments.slice(0, -1)) {
|
|
7803
7832
|
const next = node.aliases.get(segment);
|
|
7804
7833
|
if (next !== void 0) {
|
|
7805
|
-
const found =
|
|
7834
|
+
const found = byKey2.get(next);
|
|
7806
7835
|
if (!found) return null;
|
|
7807
7836
|
node = found;
|
|
7808
7837
|
continue;
|
|
@@ -7814,13 +7843,13 @@ function walkMemberPath(byKey, start, segments) {
|
|
|
7814
7843
|
return { project: node, name: segments[segments.length - 1] };
|
|
7815
7844
|
}
|
|
7816
7845
|
function landReference(projects, reference, from) {
|
|
7817
|
-
const
|
|
7818
|
-
const origin =
|
|
7819
|
-
const root =
|
|
7846
|
+
const byKey2 = new Map(projects.map((p) => [p.namespace, p]));
|
|
7847
|
+
const origin = byKey2.get(from);
|
|
7848
|
+
const root = byKey2.get("");
|
|
7820
7849
|
if (!reference.includes("::")) return { kind: "project", project: from, name: reference };
|
|
7821
7850
|
const land = (form, start, segments2) => {
|
|
7822
7851
|
if (!start) return { kind: "unresolved", form };
|
|
7823
|
-
const walked = walkMemberPath(
|
|
7852
|
+
const walked = walkMemberPath(byKey2, start, segments2);
|
|
7824
7853
|
if (walked === "outside") return { kind: "outside", form };
|
|
7825
7854
|
if (walked) return { kind: "project", form, project: walked.project.namespace, name: walked.name };
|
|
7826
7855
|
return { kind: "unresolved", form };
|
|
@@ -7832,7 +7861,7 @@ function landReference(projects, reference, from) {
|
|
|
7832
7861
|
while (segments[0] === "super") {
|
|
7833
7862
|
segments.shift();
|
|
7834
7863
|
if (!node || node.parent === void 0) return { kind: "outside", form: "super" };
|
|
7835
|
-
node =
|
|
7864
|
+
node = byKey2.get(node.parent);
|
|
7836
7865
|
}
|
|
7837
7866
|
if (segments.length === 0 || !node) return { kind: "unresolved", form: "super" };
|
|
7838
7867
|
return land("super", node, segments);
|
|
@@ -7844,11 +7873,11 @@ function landReference(projects, reference, from) {
|
|
|
7844
7873
|
return land(segments.length === 2 ? "alias" : "path", origin, segments);
|
|
7845
7874
|
}
|
|
7846
7875
|
if (origin.subsystems.has(first)) return { kind: "project", project: from, name: segments[segments.length - 1] };
|
|
7847
|
-
const fromRoot = root ? walkMemberPath(
|
|
7876
|
+
const fromRoot = root ? walkMemberPath(byKey2, root, segments) : null;
|
|
7848
7877
|
if (fromRoot && fromRoot !== "outside") return { kind: "project", form: "path", project: fromRoot.project.namespace, name: fromRoot.name };
|
|
7849
7878
|
const byId = projects.filter((p) => p.id === first || p.namespace !== "" && p.namespace === first);
|
|
7850
7879
|
if (byId.length === 1) {
|
|
7851
|
-
const walked = walkMemberPath(
|
|
7880
|
+
const walked = walkMemberPath(byKey2, byId[0], segments.slice(1));
|
|
7852
7881
|
if (walked === "outside") return { kind: "outside", form: "path" };
|
|
7853
7882
|
if (walked) return { kind: "project", form: "path", project: walked.project.namespace, name: walked.name };
|
|
7854
7883
|
}
|
|
@@ -13729,38 +13758,102 @@ function cycleFindings(family, config) {
|
|
|
13729
13758
|
}
|
|
13730
13759
|
return out;
|
|
13731
13760
|
}
|
|
13732
|
-
function approvalFinding(
|
|
13733
|
-
|
|
13734
|
-
|
|
13735
|
-
|
|
13736
|
-
|
|
13737
|
-
|
|
13738
|
-
|
|
13739
|
-
|
|
13740
|
-
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
|
|
13748
|
-
|
|
13749
|
-
|
|
13750
|
-
|
|
13751
|
-
|
|
13752
|
-
|
|
13753
|
-
|
|
13761
|
+
function approvalFinding(entry, config) {
|
|
13762
|
+
if (entry.state === "never") {
|
|
13763
|
+
return finding(
|
|
13764
|
+
config,
|
|
13765
|
+
"MEMBER_UNAPPROVED",
|
|
13766
|
+
`The member ${named(entry.key)} has never been locked: nobody has approved its design. Lock it at its own root (\`wairon lock\`).`,
|
|
13767
|
+
entry.key
|
|
13768
|
+
);
|
|
13769
|
+
}
|
|
13770
|
+
if (entry.state === "drifted" && entry.upgraded) {
|
|
13771
|
+
return finding(
|
|
13772
|
+
config,
|
|
13773
|
+
"MEMBER_DRIFTED",
|
|
13774
|
+
`The member ${named(entry.key)} was approved under an earlier gate identity \u2014 the gate identity gained inputs in stage 5 (members' composition subjects, \`composition\`; code conformance moved beside the claim), which says nothing about its design. Re-lock it once at its own root (\`wairon lock\`).`,
|
|
13775
|
+
entry.key
|
|
13776
|
+
);
|
|
13777
|
+
}
|
|
13778
|
+
if (entry.state === "drifted") {
|
|
13779
|
+
return finding(
|
|
13780
|
+
config,
|
|
13781
|
+
"MEMBER_DRIFTED",
|
|
13782
|
+
`The member ${named(entry.key)} changed since it was approved: its lock no longer matches its own gate identity. Re-lock it at its own root (\`wairon lock\`).`,
|
|
13783
|
+
entry.key
|
|
13784
|
+
);
|
|
13785
|
+
}
|
|
13786
|
+
return null;
|
|
13754
13787
|
}
|
|
13755
13788
|
function checkMembers(family) {
|
|
13756
13789
|
const config = configOrNull();
|
|
13757
13790
|
const out = [...problemFindings(family, config), ...cycleFindings(family, config)];
|
|
13758
|
-
|
|
13759
|
-
|
|
13760
|
-
out.push(approvalFinding(
|
|
13791
|
+
const present = new Set(family.nodes.filter((n) => n.namespace !== "").map((n) => n.namespace));
|
|
13792
|
+
for (const entry of projectApprovals()) {
|
|
13793
|
+
if (present.has(entry.key)) out.push(approvalFinding(entry, config));
|
|
13761
13794
|
}
|
|
13762
13795
|
return out.filter((f) => f !== null);
|
|
13763
13796
|
}
|
|
13797
|
+
function subjectOf(record) {
|
|
13798
|
+
return `${record.stateId.algorithm}:${record.stateId.digest}`;
|
|
13799
|
+
}
|
|
13800
|
+
function ownApproval(node, root) {
|
|
13801
|
+
return within(node.directory, { topRoot: root, narrowed: true }, () => {
|
|
13802
|
+
const current2 = computeGateStateId();
|
|
13803
|
+
const record = readLockRecord();
|
|
13804
|
+
const base = {
|
|
13805
|
+
key: node.namespace,
|
|
13806
|
+
...node.mountAlias !== void 0 ? { alias: node.mountAlias } : {},
|
|
13807
|
+
...node.parent !== void 0 ? { parent: node.parent } : {},
|
|
13808
|
+
...node.id !== void 0 ? { projectId: node.id } : {},
|
|
13809
|
+
state: "never"
|
|
13810
|
+
};
|
|
13811
|
+
if (!record) return base;
|
|
13812
|
+
const matches = record.stateId.algorithm === current2.algorithm && record.stateId.digest === current2.digest;
|
|
13813
|
+
return {
|
|
13814
|
+
...base,
|
|
13815
|
+
state: matches ? "approved" : "drifted",
|
|
13816
|
+
subject: subjectOf(record),
|
|
13817
|
+
...!matches && record.stateId.algorithm !== current2.algorithm ? { upgraded: true } : {}
|
|
13818
|
+
};
|
|
13819
|
+
});
|
|
13820
|
+
}
|
|
13821
|
+
function pinOf(parent, alias, subject) {
|
|
13822
|
+
if (!parent) return "unpinned";
|
|
13823
|
+
const recorded = parent.members ? alias in parent.members ? parent.members[alias].subject ?? "never" : void 0 : parent.children?.[alias];
|
|
13824
|
+
if (recorded === void 0) return "unpinned";
|
|
13825
|
+
return recorded === (subject ?? "never") ? "matches" : "moved";
|
|
13826
|
+
}
|
|
13827
|
+
function projectApprovals(depth) {
|
|
13828
|
+
const family = graph();
|
|
13829
|
+
const root = path11.resolve(getProjectRoot());
|
|
13830
|
+
const limit = depth ?? Infinity;
|
|
13831
|
+
const selected = family.nodes.filter((n) => depthOf(family, n) <= limit);
|
|
13832
|
+
const directoryOf = new Map(family.nodes.map((n) => [n.namespace, n.directory]));
|
|
13833
|
+
const parentRecord = (key) => {
|
|
13834
|
+
const dir = directoryOf.get(key);
|
|
13835
|
+
return dir === void 0 ? null : approvalRecord(dir);
|
|
13836
|
+
};
|
|
13837
|
+
const entries = selected.map((node) => {
|
|
13838
|
+
const own = ownApproval(node, root);
|
|
13839
|
+
if (node.parent === void 0 || node.mountAlias === void 0) return own;
|
|
13840
|
+
return { ...own, pinned: pinOf(parentRecord(node.parent), node.mountAlias, own.subject) };
|
|
13841
|
+
});
|
|
13842
|
+
for (const problem of family.problems) {
|
|
13843
|
+
if (problem.kind !== "member-absent" || !problem.id) continue;
|
|
13844
|
+
const parentKey = problem.projects[0] ?? "";
|
|
13845
|
+
const parentNode = family.nodes.find((n) => n.namespace === parentKey);
|
|
13846
|
+
if (!parentNode || depthOf(family, parentNode) + 1 > limit) continue;
|
|
13847
|
+
entries.push({
|
|
13848
|
+
key: parentKey === "" ? problem.id : `${parentKey}::${problem.id}`,
|
|
13849
|
+
alias: problem.id,
|
|
13850
|
+
parent: parentKey,
|
|
13851
|
+
state: "never",
|
|
13852
|
+
pinned: pinOf(parentRecord(parentKey), problem.id, void 0)
|
|
13853
|
+
});
|
|
13854
|
+
}
|
|
13855
|
+
return entries;
|
|
13856
|
+
}
|
|
13764
13857
|
function requiredBy(key) {
|
|
13765
13858
|
return key === "" ? "the family root" : `"${key}"`;
|
|
13766
13859
|
}
|
|
@@ -13936,7 +14029,7 @@ function defaultTargetConfig(type) {
|
|
|
13936
14029
|
enabled: true
|
|
13937
14030
|
};
|
|
13938
14031
|
}
|
|
13939
|
-
var WAIRON_VERSION = "5.1.1-dev.
|
|
14032
|
+
var WAIRON_VERSION = "5.1.1-dev.86";
|
|
13940
14033
|
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
13941
14034
|
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
13942
14035
|
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -14670,6 +14763,9 @@ var BUILTIN_PROFILES = [
|
|
|
14670
14763
|
"plc-cyclic"
|
|
14671
14764
|
];
|
|
14672
14765
|
var PROJECT_KINDS = ["fullstack", "system-of-systems", "monorepo"];
|
|
14766
|
+
function judgesCode(rule) {
|
|
14767
|
+
return rule.judges === "code";
|
|
14768
|
+
}
|
|
14673
14769
|
|
|
14674
14770
|
// src/core/rules/read-model.ts
|
|
14675
14771
|
var NO_NAMES = /* @__PURE__ */ new Set();
|
|
@@ -14993,6 +15089,7 @@ function offTreeEdge(ctx, from, ref, fromDraft) {
|
|
|
14993
15089
|
// src/core/rules/integrity/hierarchy-integrity.ts
|
|
14994
15090
|
var hierarchyRule = {
|
|
14995
15091
|
name: "hierarchy-integrity",
|
|
15092
|
+
judges: "design",
|
|
14996
15093
|
description: "Every subsystem references the system, every component an existing subsystem, every interface an existing component, and every implementation an existing interface contract. Draft/design specs are reported informationally.",
|
|
14997
15094
|
codes: [
|
|
14998
15095
|
{ code: "DRAFT_SUBSYSTEM_WARNING", defaultSeverity: "warning", summary: "Subsystem is in draft/design status" },
|
|
@@ -15066,6 +15163,7 @@ var hierarchyRule = {
|
|
|
15066
15163
|
// src/core/rules/integrity/reserved-id-segments.ts
|
|
15067
15164
|
var reservedIdSegmentsRule = {
|
|
15068
15165
|
name: "reserved-id-segments",
|
|
15166
|
+
judges: "design",
|
|
15069
15167
|
description: 'No segment of a spec id may be the reserved namespace keyword "super" \u2014 the :: grammar consumes it as a namespace hop, so a stored reference to such an id resolves to a different spec.',
|
|
15070
15168
|
codes: [
|
|
15071
15169
|
{ code: "RESERVED_ID_SEGMENT", defaultSeverity: "error", summary: 'Id uses the reserved namespace keyword "super"' }
|
|
@@ -15099,6 +15197,7 @@ function localIdOf(key, project) {
|
|
|
15099
15197
|
}
|
|
15100
15198
|
var localIdsRule = {
|
|
15101
15199
|
name: "local-ids",
|
|
15200
|
+
judges: "design",
|
|
15102
15201
|
description: "Inside one project an id names one spec, and an id without `::` is always local. Two spec files of one project declaring one key are DUPLICATE_SPEC_ID: whichever the loader kept, every reference to that key \u2014 local, or `alias::name` from another project \u2014 could mean either, so the tree has no single reading. A local id equal to one of the project's aliases (a `members` or `externals` key) is LOCAL_ID_SHADOWS_PROJECT: the grammar keeps them apart (a bare id is local, `alias::name` crosses), but a reader seeing `billing` beside `billing::invoice_portal` cannot tell a local subsystem from a member, so the shadow is made visible. It reads the project graph's owners, alias tables and duplicate-spec problems.",
|
|
15103
15202
|
codes: [
|
|
15104
15203
|
{ code: "DUPLICATE_SPEC_ID", defaultSeverity: "error", summary: "Two spec files of one project declare the same id" },
|
|
@@ -15143,6 +15242,7 @@ var localIdsRule = {
|
|
|
15143
15242
|
// src/core/rules/integrity/project-identity.ts
|
|
15144
15243
|
var projectIdentityRule = {
|
|
15145
15244
|
name: "project-identity",
|
|
15245
|
+
judges: "design",
|
|
15146
15246
|
description: "The bound project is keyed on its id, so the id must be declared, well-formed, stable and its own: a project that declares none answers to its display name slugified until it writes one (PROJECT_ID_DEFAULTED), a project whose name yields no id or whose declared id breaks the grammar has nothing reliable to key on (PROJECT_ID_AMBIGUOUS), and an id that differs from the one the lock approved has moved under everything that keyed on it (PROJECT_ID_CHANGED). It judges the bound project's own identity only. The family half \u2014 a member with a defaulted id (named with its alias as the id to declare), a member with no id, and two members resolving to one id \u2014 is a family check since stage 4 (family_validator.checkMembers), which reads the family root's graph.",
|
|
15147
15247
|
codes: [
|
|
15148
15248
|
{ code: "PROJECT_ID_AMBIGUOUS", defaultSeverity: "warning", summary: "Project has no usable id: its name yields no slug, or its declared id breaks the grammar" },
|
|
@@ -15184,6 +15284,7 @@ function judgeOwnIdentity(ctx, identity2) {
|
|
|
15184
15284
|
// src/core/rules/integrity/roundtrip-serialization.ts
|
|
15185
15285
|
var roundtripRule = {
|
|
15186
15286
|
name: "roundtrip-serialization",
|
|
15287
|
+
judges: "design",
|
|
15187
15288
|
description: "Every loaded spec must re-serialize through the exact writer pipeline (same relativization, same schema, no I/O) \u2014 validate must predict every refusal that lock's status promotion or any later save would otherwise raise mid-write.",
|
|
15188
15289
|
codes: [
|
|
15189
15290
|
{ code: "ROUNDTRIP_SERIALIZATION", defaultSeverity: "error", summary: "Spec cannot be re-serialized through the writer schema (any save or lock would refuse it)" }
|
|
@@ -15198,6 +15299,7 @@ var roundtripRule = {
|
|
|
15198
15299
|
// src/core/rules/integrity/type-declarations.ts
|
|
15199
15300
|
var typeDeclarationsRule = {
|
|
15200
15301
|
name: "type-declarations",
|
|
15302
|
+
judges: "design",
|
|
15201
15303
|
description: "A type owned by a subsystem must reference an existing one, and a type declaring neither fields nor methods is a placeholder that can inform neither implementers nor the ERD.",
|
|
15202
15304
|
codes: [
|
|
15203
15305
|
{ code: "INVALID_SUBSYSTEM_REFERENCE", defaultSeverity: "error", summary: "Type references a non-existent owning subsystem" },
|
|
@@ -15232,6 +15334,7 @@ var typeDeclarationsRule = {
|
|
|
15232
15334
|
// src/core/rules/integrity/field-type-references.ts
|
|
15233
15335
|
var fieldTypeReferencesRule = {
|
|
15234
15336
|
name: "field-type-references",
|
|
15337
|
+
judges: "design",
|
|
15235
15338
|
description: "Every type identifier a type field's declared type names must resolve to a builtin or a defined entity/value-object type \u2014 the type's own generic parameters are not field references.",
|
|
15236
15339
|
codes: [
|
|
15237
15340
|
{ code: "UNDEFINED_TYPE_REFERENCE", defaultSeverity: "error", summary: "Reference to a type that is not defined anywhere" }
|
|
@@ -15263,6 +15366,7 @@ var fieldTypeReferencesRule = {
|
|
|
15263
15366
|
// src/core/rules/integrity/signature-type-references.ts
|
|
15264
15367
|
var signatureTypeReferencesRule = {
|
|
15265
15368
|
name: "signature-type-references",
|
|
15369
|
+
judges: "design",
|
|
15266
15370
|
description: "Every type identifier an interface method signature names must resolve to a builtin, a generic parameter in scope on the interface or the method, or a defined entity/value-object type.",
|
|
15267
15371
|
codes: [
|
|
15268
15372
|
{ code: "UNDEFINED_TYPE_REFERENCE", defaultSeverity: "error", summary: "Reference to a type that is not defined anywhere" }
|
|
@@ -15299,6 +15403,7 @@ var signatureTypeReferencesRule = {
|
|
|
15299
15403
|
// src/core/rules/integrity/public-surface-binding.ts
|
|
15300
15404
|
var publicSurfaceBindingRule = {
|
|
15301
15405
|
name: "public-surface-binding",
|
|
15406
|
+
judges: "design",
|
|
15302
15407
|
description: "Every declared publicInterface names a backing component, that component exists, and the subsystem owns it \u2014 a subsystem may only publish its own components.",
|
|
15303
15408
|
codes: [
|
|
15304
15409
|
{ code: "PUBLIC_INTERFACE_UNBOUND", defaultSeverity: "error", summary: "Public interface with no backing component" },
|
|
@@ -15330,6 +15435,7 @@ var publicSurfaceBindingRule = {
|
|
|
15330
15435
|
// src/core/rules/integrity/public-surface-consumers.ts
|
|
15331
15436
|
var publicSurfaceConsumersRule = {
|
|
15332
15437
|
name: "public-surface-consumers",
|
|
15438
|
+
judges: "design",
|
|
15333
15439
|
description: "Every subsystem a published surface names as its consumer must exist in the tree. A consumers list restricts who may depend on the surface, so an id that names nothing locks the surface against a caller nobody can be \u2014 and a typo there silently locks out the caller that was meant, which the boundary rule would then report against the wrong party.",
|
|
15334
15440
|
codes: [
|
|
15335
15441
|
{ code: "PUBLIC_INTERFACE_UNKNOWN_CONSUMER", defaultSeverity: "error", summary: "A published surface names as its consumer a subsystem the tree does not have" }
|
|
@@ -15387,6 +15493,7 @@ var expectedFor = (t) => {
|
|
|
15387
15493
|
};
|
|
15388
15494
|
var publicSurfaceDeclaredTypeRule = {
|
|
15389
15495
|
name: "public-surface-declared-type",
|
|
15496
|
+
judges: "design",
|
|
15390
15497
|
description: "A publicInterface entry's declared type must be realizable by the stereotype of its backing component; Custom entries whose prose implies eventing must be backed by an event-capable component.",
|
|
15391
15498
|
codes: [
|
|
15392
15499
|
{ code: "PUBLIC_INTERFACE_TYPE_MISMATCH", defaultSeverity: "error", summary: "Backing component cannot realize the declared interface type" },
|
|
@@ -15417,6 +15524,7 @@ var publicSurfaceDeclaredTypeRule = {
|
|
|
15417
15524
|
// src/core/rules/integrity/public-surface-bound-contract.ts
|
|
15418
15525
|
var publicSurfaceBoundContractRule = {
|
|
15419
15526
|
name: "public-surface-bound-contract",
|
|
15527
|
+
judges: "design",
|
|
15420
15528
|
description: "A publicInterface entry that binds an L3 interface must bind one that exists and that belongs to the component the entry publishes.",
|
|
15421
15529
|
codes: [
|
|
15422
15530
|
{ code: "PUBLIC_INTERFACE_INVALID_INTERFACE", defaultSeverity: "error", summary: "Bound L3 interface missing or belonging to another component" }
|
|
@@ -15441,6 +15549,7 @@ var publicSurfaceBoundContractRule = {
|
|
|
15441
15549
|
// src/core/rules/integrity/export-tables.ts
|
|
15442
15550
|
var exportTablesRule = {
|
|
15443
15551
|
name: "export-tables",
|
|
15552
|
+
judges: "design",
|
|
15444
15553
|
description: "Every level's export table must resolve the way a module resolver resolves `export \u2026 from`: one public name binds one target (EXPORT_ID_DUPLICATE when two wildcards \u2014 or two explicit entries \u2014 bind a name to different targets, or two public names share one nameKey, since a bare import could not tell them apart; the name is left out), no chain leads back to itself (EXPORT_CYCLE \u2014 an error for a named chain that never reaches a component or type, a warning for a wildcard cycle, which resolves to its union), every entry names a source and an item that exist, that the source exports and, for an own type, that the level owns \u2014 a subsystem-owned type at L1, a project-level type (no subsystem) for an L0 `{ typeDef }` without `from` \u2014 under a public name of [a-z0-9-_]+ (EXPORT_INVALID), every re-exported component can serve a boundary caller \u2014 a Portal, a gateway being a Portal variant, or an Observer for events (EXPORT_UNCONSUMABLE, at L0 and for L1 re-exports; an error since stage 4), and an L0 entry that re-exports a member's or an external's export never declares a wider audience than that export has (EXPORT_WIDENS_AUDIENCE): a re-export can narrow reach, never widen it. The rule judges the facts the export resolver recorded in the scan \u2014 for the bound root's table and every contained member's \u2014 and resolves nothing itself.",
|
|
15445
15554
|
codes: [
|
|
15446
15555
|
{ code: "EXPORT_ID_DUPLICATE", defaultSeverity: "error", summary: "One public name bound to different targets by two wildcard re-exports or two explicit entries" },
|
|
@@ -15486,6 +15595,7 @@ var exportTablesRule = {
|
|
|
15486
15595
|
// src/core/rules/integrity/external-declarations.ts
|
|
15487
15596
|
var externalDeclarationsRule = {
|
|
15488
15597
|
name: "external-declarations",
|
|
15598
|
+
judges: "design",
|
|
15489
15599
|
description: "Every external a project declares in project.yaml must be usable: its alias fits [a-z0-9-_]+ (a dotted producer id needs an explicit alias) and is not also a `members` key (one alias names one project), its producer id fits the project-id grammar, and it has something to be judged against \u2014 a project of the scan answers to the id, source.path names a directory (whose project, when it is in the scan, answers to the declared id), or the project holds a pin for the alias. A declaration that fails is reported (EXTERNAL_UNRESOLVED) with its reason, so a misspelled declaration never passes as declared. A member validated from its own root that declares its parent or a sibling is not reported when it has pinned it: the pin is what its gate judges against, and nothing is climbed to find the producer. Every name an external's or a member's `use` lists by name must be a public name the producer exports to this project \u2014 a contained member's live L0 table, an external's pin \u2014 compared by nameKey (IMPORT_UNRESOLVED, naming the closest public names; a `use` on an unpinned external is left to the pin's own EXTERNAL_CHECK_UNAVAILABLE), and must not share its nameKey with a spec of the project itself (IMPORT_SHADOWED_BY_LOCAL: the local spec wins every bare reference, so the named import can never take effect and says something false \u2014 rename one or drop the import). A `*` import shadowed by locals is silent, as a glob import is in Rust. One code, one meaning: IMPORT_UNRESOLVED is only a name the producer does not export, IMPORT_SHADOWED_BY_LOCAL only a named import a local spec hides. A malformed `use` entry is the declaration's problem (EXTERNAL_UNRESOLVED for an external, the member's for a member). The findings name no spec: externals and members live in configuration. It reads the bound root's node of the project graph, its alias-conflict problems, the members' export tables, the project's own spec ids and ctx.pinnedExternals.",
|
|
15490
15600
|
codes: [
|
|
15491
15601
|
{ code: "EXTERNAL_UNRESOLVED", defaultSeverity: "notice", summary: "A declared external whose alias is malformed or taken by a member, whose producer id is malformed, or whose producer the family and its source.path do not provide" },
|
|
@@ -15601,6 +15711,7 @@ function whyNoRewrite(ref) {
|
|
|
15601
15711
|
}
|
|
15602
15712
|
var referenceFormsRule = {
|
|
15603
15713
|
name: "reference-forms",
|
|
15714
|
+
judges: "design",
|
|
15604
15715
|
description: "A cross-project reference is `alias::name` (or a bare name a `use` imports): the alias is one of the referring project's members or externals, the name a public name of that project's L0 table. The older forms still bind for one release and are reported (DEPRECATED_REFERENCE_FORM), each naming the spec, where in it, the form as written and the text to write instead (the scan's rewrite: a bare local id, or `alias::publicName`): a leading `::`, which escapes to the root of whatever checkout loads it; `super::`, which climbs the containing projects \u2014 a place in one family, not a name; a member path (`desktop::shell` written inside desktop, `waffler_core::transpiler::x` written inside transpiler), a first segment the referring project does not declare, read from the bound root; and a self-prefix \u2014 the referring project's own id as the first segment, with `::` or `.` (`registry.advisory-channel` inside registry), which stage 3's migration did not rewrite and stage 4's positional step does. Wairon's own writer emits none of them. A form with no rewrite (its target is out of reach) is reported with the reason. It reads the project graph's authored references, which the scan records before binding erases the form.",
|
|
15605
15716
|
codes: [
|
|
15606
15717
|
{ code: "DEPRECATED_REFERENCE_FORM", defaultSeverity: "notice", summary: "A reference written with a leading ::, super:: or a member path where a local id or alias::name belongs" }
|
|
@@ -15631,6 +15742,7 @@ function carriedFields(mount) {
|
|
|
15631
15742
|
}
|
|
15632
15743
|
var memberDeclarationsRule = {
|
|
15633
15744
|
name: "member-declarations",
|
|
15745
|
+
judges: "design",
|
|
15634
15746
|
description: "A project declares its members in project.yaml `members`; a member is not a subsystem and carries no content in its parent. The L1 form \u2014 a subsystem carrying projectPath \u2014 still loads for one release and is reported (DEPRECATED_MOUNT_FORM), naming the mount, its path, the `members` entry that replaces it, and every field it carries that `doctor --fix` will move into the member (or stop on); a mount whose alias `members` also declares is reported as ignored. It reads the project graph's nodes and their legacy mounts.",
|
|
15635
15747
|
codes: [
|
|
15636
15748
|
{ code: "DEPRECATED_MOUNT_FORM", defaultSeverity: "notice", summary: "A member declared as an L1 subsystem carrying projectPath instead of a project.yaml `members` entry" }
|
|
@@ -15687,6 +15799,7 @@ function declaration(producer) {
|
|
|
15687
15799
|
}
|
|
15688
15800
|
var projectBoundariesRule = {
|
|
15689
15801
|
name: "project-boundaries",
|
|
15802
|
+
judges: "design",
|
|
15690
15803
|
description: "The one judge of every cross-project reference the bound project writes (decision 9 \u2014 every project is a crate of its own, reached only through its L0 exports and only as a dependency it declared). It reads each of the project's own references from the project graph \u2014 `::` forms and bare names bound through `use` imports alike \u2014 takes its resolution from resolveCrossProject, decided before any severity from the project's own files alone, and reports every outcome but resolved once, carrying the resolution: forbidden is EXTERNAL_UNDECLARED (the reference reaches a project the referrer declares neither as a member nor as an external, or names a first segment that is none of its subsystems, aliases or foreign providers); missing is EXTERNAL_NOT_EXPORTED (the producer's table \u2014 a contained member's live one, or the external's pin \u2014 has no public name covering the referrer, or not the used method, or exports the type only inside a signature's closure); an ambiguous import is IMPORT_AMBIGUOUS (two imports supply one bare name: two explicit `use` names, or two `*` with no explicit one \u2014 name it `alias::name` or narrow a `use`); unavailable is EXTERNAL_CHECK_UNAVAILABLE (no pin, an unreadable pin, a member absent on disk, or a deprecated form naming no alias \u2014 never a pass). A bare name no import supplies is not this rule's: it is the unresolved-reference finding it always was, with rule_context.importHint appended. A trustedLinks entry naming a subsystem of another project (`alias::sub`) is TRUSTED_LINK_CROSSES_PROJECT: a sanctioned fast lane stays inside one project. Stage 4 makes the three stage-2 notices errors now that the owner's gate judges them exactly; a reference is never left to a parent's verdict.",
|
|
15691
15804
|
codes: [
|
|
15692
15805
|
{ code: "EXTERNAL_UNDECLARED", defaultSeverity: "error", summary: "A reference reaches another project the referring project declares neither as a member nor as an external, or names a first segment that is none of its subsystems, aliases or foreign providers" },
|
|
@@ -15778,6 +15891,7 @@ var projectBoundariesRule = {
|
|
|
15778
15891
|
// src/core/rules/integrity/lint-allows.ts
|
|
15779
15892
|
var lintAllowsRule = {
|
|
15780
15893
|
name: "lint-allows",
|
|
15894
|
+
judges: "design",
|
|
15781
15895
|
description: "Per-spec lint suppressions (lint.allow) must name real issue codes and actually suppress a finding \u2014 unknown codes and stale allows are flagged. An allow covers exactly the occurrence it names: a finding that reports a site is silenced only by an allow whose `at` is that site, a finding that reports none only by an allow that names none, and an aggregating finding only by an allow whose `covers` lists every unit it reports \u2014 a unit nobody listed is named back as new instead of inheriting a decision taken about its neighbours. So a coarse allow left on a rule that names sites, and an allow whose site the run no longer reports, are both UNUSED_LINT_ALLOW, and the finding names the sites that did fire. Allows silence warnings and notices; errors always surface.",
|
|
15782
15896
|
codes: [
|
|
15783
15897
|
{ code: "UNKNOWN_LINT_ALLOW_CODE", defaultSeverity: "warning", summary: "lint.allow names an issue code no registered rule emits" },
|
|
@@ -15819,6 +15933,7 @@ var lintAllowsRule = {
|
|
|
15819
15933
|
// src/core/rules/narrative/contract-symmetry.ts
|
|
15820
15934
|
var contractSymmetryRule = {
|
|
15821
15935
|
name: "contract-symmetry",
|
|
15936
|
+
judges: "design",
|
|
15822
15937
|
description: "Implementations mirror their contract method-for-method: every method an implementation declares is defined on the contract it realizes, and every contract method has an implementation.",
|
|
15823
15938
|
codes: [
|
|
15824
15939
|
{ code: "UNEXPECTED_IMPLEMENTATION_METHOD", defaultSeverity: "error", summary: "Implementation method not present on the contract" },
|
|
@@ -15878,6 +15993,7 @@ var targetsOf = (implMethod) => {
|
|
|
15878
15993
|
};
|
|
15879
15994
|
var narrativeTargetReferencesRule = {
|
|
15880
15995
|
name: "narrative-target-references",
|
|
15996
|
+
judges: "design",
|
|
15881
15997
|
description: `A narrative call, dispatch or register step must name a target component, and a target method unless it dispatches; a method's declared calls name the same pair as one "<component>.<method>" reference, the spelling the conformance debt register and a lint allow name a unit with. A target this tree contains must be a collaborator the calling component declares, must carry the named method on one of its interfaces, and that method must declare every semantic guarantee the entry asserts.`,
|
|
15882
15998
|
codes: [
|
|
15883
15999
|
{ code: "MISSING_TARGET_COMPONENT", defaultSeverity: "error", summary: "Call/register step missing targetComponent" },
|
|
@@ -15979,6 +16095,7 @@ var narrativeTargetReferencesRule = {
|
|
|
15979
16095
|
// src/core/rules/narrative/cross-tree-references.ts
|
|
15980
16096
|
var crossTreeReferencesRule = {
|
|
15981
16097
|
name: "cross-tree-references",
|
|
16098
|
+
judges: "design",
|
|
15982
16099
|
description: `A narrative call, dispatch or register target that is not a component of this tree either leaves the project \u2014 and then its resolution (resolveCrossProject) decides: resolved is surface-reference-backing's, foreign snapshots that disagree are SURFACE_REF_AMBIGUOUS here, and every other outcome is project-boundaries' single finding \u2014 or it is a local id that names nothing, the INVALID_TARGET_COMPONENT_REFERENCE typo it always was. The CROSS_TREE_REF_UNRESOLVED warning ("only the parent project can verify it") is retired: no reference is left for a parent to judge.`,
|
|
15983
16100
|
codes: [
|
|
15984
16101
|
{ code: "INVALID_TARGET_COMPONENT_REFERENCE", defaultSeverity: "error", summary: "Call/register step targets a non-existent component" },
|
|
@@ -16035,6 +16152,7 @@ var crossTreeReferencesRule = {
|
|
|
16035
16152
|
// src/core/rules/narrative/surface-reference-backing.ts
|
|
16036
16153
|
var surfaceReferenceBackingRule = {
|
|
16037
16154
|
name: "surface-reference-backing",
|
|
16155
|
+
judges: "design",
|
|
16038
16156
|
description: "The contract entry a narrative target in another project resolves to \u2014 a contained member's live export table, a declared external's pinned snapshot, or a foreign surface snapshot \u2014 must back what the step asks of it: the calling component declares the collaborator, the entry exposes the called method (or serves the dispatched capability), and it declares every semantic guarantee the step asserts. These are contract verdicts on a resolved reference, and each carries that resolution.",
|
|
16039
16157
|
codes: [
|
|
16040
16158
|
{ code: "SURFACE_REF_NOT_EXPOSED", defaultSeverity: "error", summary: "Cross-tree reference resolves to a surface snapshot that does not expose the called method/capability" },
|
|
@@ -16117,6 +16235,7 @@ var surfaceReferenceBackingRule = {
|
|
|
16117
16235
|
// src/core/rules/narrative/guarantee-tokens.ts
|
|
16118
16236
|
var guaranteeTokensRule = {
|
|
16119
16237
|
name: "guarantee-tokens",
|
|
16238
|
+
judges: "design",
|
|
16120
16239
|
description: "Every semantic-guarantee token an L3 method declares or a narrative step asserts must be a builtin guarantee or one a loaded extension pack declares in its `guarantees` list. The vocabulary is open for packs, not for typos \u2014 an undeclared token is matched by nothing and silently escapes the narrative\u2194contract consistency checks.",
|
|
16121
16240
|
codes: [
|
|
16122
16241
|
{ code: "UNKNOWN_GUARANTEE", defaultSeverity: "warning", summary: "Guarantee token is neither builtin nor pack-declared" }
|
|
@@ -16164,6 +16283,7 @@ var guaranteeTokensRule = {
|
|
|
16164
16283
|
// src/core/rules/narrative/narrative-step-config.ts
|
|
16165
16284
|
var narrativeStepConfigRule = {
|
|
16166
16285
|
name: "narrative-step-config",
|
|
16286
|
+
judges: "design",
|
|
16167
16287
|
description: 'The step configuration of L5 narratives: every flow step (branch, switch, loop, try, parallel, jump) carries the config its type requires and no simple step carries flow config, "detach" (fire-and-forget) is legal on call/dispatch steps only, no step carries a field foreign to its type, a parallel body is covered by contiguous, ordered arms (>= 2), no two steps share a step number or a label, and every jump field targets an existing step number in the same narrative.',
|
|
16168
16288
|
codes: [
|
|
16169
16289
|
{ code: "MALFORMED_FLOW_STEP", defaultSeverity: "error", summary: "Flow step missing required config (or flow config on a local/call step)" },
|
|
@@ -16189,6 +16309,7 @@ var narrativeStepConfigRule = {
|
|
|
16189
16309
|
// src/core/rules/narrative/narrative-reachability.ts
|
|
16190
16310
|
var narrativeReachabilityRule = {
|
|
16191
16311
|
name: "narrative-reachability",
|
|
16312
|
+
judges: "design",
|
|
16192
16313
|
description: "Every step of an L5 narrative must be reachable from the first step following fall-through and jumps, and the loop/try/parallel regions it declares must nest or stay disjoint \u2014 interleaved regions map onto no structured code. Judged over the step graph, and only for a narrative whose step configuration is sound (narrative-step-config reports the rest).",
|
|
16193
16314
|
codes: [
|
|
16194
16315
|
{ code: "UNREACHABLE_STEP", defaultSeverity: "warning", summary: "Step not reachable from the first step following fall-through and jumps" },
|
|
@@ -16255,6 +16376,7 @@ function regionsOf(steps) {
|
|
|
16255
16376
|
// src/core/rules/narrative/narrative-jump-edges.ts
|
|
16256
16377
|
var narrativeJumpEdgesRule = {
|
|
16257
16378
|
name: "narrative-jump-edges",
|
|
16379
|
+
judges: "design",
|
|
16258
16380
|
description: "Where an L5 narrative's jump edges land: a loop/try/parallel region is entered through its header, never into the middle of its body; a backward jump is only idiomatic as a continue to an enclosing loop header, and otherwise models repetition that belongs in a loop step; and a try body must not fall through into its own catch/finally region on the success path. Judged only for a narrative whose step configuration is sound (narrative-step-config reports the rest).",
|
|
16259
16381
|
codes: [
|
|
16260
16382
|
{ code: "JUMP_INTO_REGION", defaultSeverity: "warning", summary: "Jump lands in the middle of a loop/try body from outside \u2014 regions are entered through their header" },
|
|
@@ -16455,6 +16577,7 @@ function stronglyConnected(nums, successorsOf) {
|
|
|
16455
16577
|
// src/core/rules/narrative/meaningless-branches.ts
|
|
16456
16578
|
var meaninglessBranchesRule = {
|
|
16457
16579
|
name: "meaningless-branches",
|
|
16580
|
+
judges: "design",
|
|
16458
16581
|
description: "A branch or switch whose arms all target the same step decides nothing: the condition is evaluated and flow continues at one place whatever it answers. A decision's targets are its explicit ones plus, where it leaves its fall-through field unset, the steps the step graph continues it at \u2014 so the classic authoring slip of pointing both arms at the next step is caught. Structure-only \u2014 a warning, lint.allow-suppressible.",
|
|
16459
16582
|
codes: [
|
|
16460
16583
|
{ code: "MEANINGLESS_BRANCH", defaultSeverity: "warning", summary: "Branch/switch whose arms all target the same step \u2014 the decision changes nothing" }
|
|
@@ -16507,6 +16630,7 @@ var meaninglessBranchesRule = {
|
|
|
16507
16630
|
// src/core/rules/narrative/inescapable-cycles.ts
|
|
16508
16631
|
var inescapableCyclesRule = {
|
|
16509
16632
|
name: "inescapable-cycles",
|
|
16633
|
+
judges: "design",
|
|
16510
16634
|
description: "A cycle in a narrative's step graph with no exit edge and no return or throw member never terminates, by construction: every path that enters it stays inside it forever. Read over the step graph closed by a completion step, so falling off the end of the narrative counts as leaving. Structure-only \u2014 prose conditions are never judged \u2014 and reported as a warning, lint.allow-suppressible.",
|
|
16511
16635
|
codes: [
|
|
16512
16636
|
{ code: "INESCAPABLE_CYCLE", defaultSeverity: "warning", summary: "Step cycle with no exit edge and no return/throw member \u2014 never terminates by construction" }
|
|
@@ -16548,6 +16672,7 @@ var inescapableCyclesRule = {
|
|
|
16548
16672
|
var byCodeUnit = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
16549
16673
|
var unconditionalCallCyclesRule = {
|
|
16550
16674
|
name: "unconditional-call-cycles",
|
|
16675
|
+
judges: "design",
|
|
16551
16676
|
description: "A method-level call cycle across components in which every call edge is unavoidable on every entry-to-exit path of its narrative is unbounded recursion, by construction. Dispatch steps are resolved through their portal's table; register steps are never edges (a handoff defers the invocation to the runtime, so it cannot recurse). A cycle with even one guarded edge is NOT flagged, since prose conditions are never judged. Structure-only \u2014 a warning, lint.allow-suppressible, anchored on the member edge that sorts first so one allow covers the cycle.",
|
|
16552
16677
|
codes: [
|
|
16553
16678
|
{ code: "UNCONDITIONAL_CALL_CYCLE", defaultSeverity: "warning", summary: "Cross-component call cycle in which every call edge is unavoidable \u2014 unbounded recursion by construction" }
|
|
@@ -16622,6 +16747,7 @@ var unconditionalCallCyclesRule = {
|
|
|
16622
16747
|
// src/core/rules/narrative/narrative-detail.ts
|
|
16623
16748
|
var narrativeDetailRule = {
|
|
16624
16749
|
name: "narrative-detail",
|
|
16750
|
+
judges: "design",
|
|
16625
16751
|
description: "The narrative detail dial: each method resolves to full | calls-only | intent (method override \u2192 spec default \u2192 stereotype default). full requires a narrative; intent-level methods without a narrative must specify behavior as non-trivial prose (L4 intent or L3 description) \u2014 dialing detail down never means leaving behavior unspecified. Explicit declarations are held to their promise as errors; stereotype-defaulted gaps surface as warnings. An honest lint over declarations \u2014 it never claims the narrative or the prose is CORRECT.",
|
|
16626
16752
|
codes: [
|
|
16627
16753
|
{ code: "MISSING_NARRATIVE", defaultSeverity: "warning", summary: "Method resolved to detail: full but has no narrative (error when the level was declared explicitly)" },
|
|
@@ -16667,6 +16793,7 @@ var narrativeDetailRule = {
|
|
|
16667
16793
|
var DEFAULT_MAX_UNNARRATED_COMPLEXITY = 8;
|
|
16668
16794
|
var detailSufficiencyRule = {
|
|
16669
16795
|
name: "detail-sufficiency",
|
|
16796
|
+
judges: "code",
|
|
16670
16797
|
description: "A method dialed below detail: full with no narrative may not be hiding behavior it owes a reader. A method whose realized function \u2014 in the method's own source file, else the implementation's \u2014 measures real branching (cyclomatic complexity above rules.complexity.maxUnnarratedComplexity, exact AST grade only) is reported (UNNARRATED_COMPLEXITY), and an explicit dial below a full-floor logic stereotype without a narrative is a visible, lint.allow-justifiable choice (DETAIL_BELOW_STEREOTYPE) \u2014 reported only where the measured finding did not already fire, since evidence outranks expectation. Both are honest lints over declarations: they never claim a narrative would be correct, only that the dial hides something.",
|
|
16671
16798
|
codes: [
|
|
16672
16799
|
{ code: "UNNARRATED_COMPLEXITY", defaultSeverity: "warning", summary: "Method below detail: full with no narrative whose realized function, in the method's source file, has real branching (cyclomatic complexity over the configured threshold, exact-grade analysis only)" },
|
|
@@ -16724,6 +16851,7 @@ var detailSufficiencyRule = {
|
|
|
16724
16851
|
// src/core/rules/intrinsic/portal-fields.ts
|
|
16725
16852
|
var portalFieldsRule = {
|
|
16726
16853
|
name: "portal-fields",
|
|
16854
|
+
judges: "design",
|
|
16727
16855
|
scope: "spec",
|
|
16728
16856
|
description: "A Portal declares its portalType. Non-Portal components carry no portalType, basePath, or auth (auth is inbound transport auth \u2014 it belongs on the Portal that exposes the surface). Intrinsic to one component: no tree required.",
|
|
16729
16857
|
codes: [
|
|
@@ -16771,6 +16899,7 @@ var portalFieldsRule = {
|
|
|
16771
16899
|
// src/core/rules/intrinsic/durability-declaration.ts
|
|
16772
16900
|
var durabilityDeclarationRule = {
|
|
16773
16901
|
name: "durability-declaration",
|
|
16902
|
+
judges: "design",
|
|
16774
16903
|
scope: "spec",
|
|
16775
16904
|
description: "Every Store declares its durability (MISSING_DURABILITY) and nothing but a Store may declare one (DURABILITY_ON_NON_STORE). Intrinsic to one component: no tree required. The round-trip consequences of the declaration are enforced by durability-round-trip.",
|
|
16776
16905
|
codes: [
|
|
@@ -16808,6 +16937,7 @@ var durabilityDeclarationRule = {
|
|
|
16808
16937
|
// src/core/rules/intrinsic/logic-declaration.ts
|
|
16809
16938
|
var logicDeclarationRule = {
|
|
16810
16939
|
name: "logic-declaration",
|
|
16940
|
+
judges: "design",
|
|
16811
16941
|
scope: "spec",
|
|
16812
16942
|
description: "Only an Orchestrator may declare a dependencyClass (DEPENDENCY_CLASS_ON_NON_ORCHESTRATOR); what the class allows is judged by logic-dependency-class. Intrinsic to one component: no tree required.",
|
|
16813
16943
|
codes: [
|
|
@@ -16832,6 +16962,7 @@ var logicDeclarationRule = {
|
|
|
16832
16962
|
// src/core/rules/intrinsic/retired-stereotypes.ts
|
|
16833
16963
|
var retiredStereotypesRule = {
|
|
16834
16964
|
name: "retired-stereotypes",
|
|
16965
|
+
judges: "design",
|
|
16835
16966
|
scope: "spec",
|
|
16836
16967
|
description: "A component typed with a retired stereotype is an error until it is migrated (STEREOTYPE_RETIRED). A Specialist becomes an Orchestrator with the dependencyClass its dependencies give it, which `wairon doctor --fix` applies. A Gateway becomes the Portal it owns, with the gateway variant: its other members are depended on rather than owned, its consumers depend on that Portal, and the Gateway spec is deleted, steps the finding lists for the author. Intrinsic to one component: no tree required.",
|
|
16837
16968
|
codes: [
|
|
@@ -16875,6 +17006,7 @@ var PORTAL_TRANSPORT = {
|
|
|
16875
17006
|
};
|
|
16876
17007
|
var portalsRule = {
|
|
16877
17008
|
name: "portal-endpoints",
|
|
17009
|
+
judges: "design",
|
|
16878
17010
|
description: "A Portal binds every interface method to a concrete endpoint of the transport its portalType expects.",
|
|
16879
17011
|
codes: [
|
|
16880
17012
|
{ code: "MISSING_ENDPOINT", defaultSeverity: "error", summary: "Portal method without a wire endpoint binding" },
|
|
@@ -16915,6 +17047,7 @@ var portalsRule = {
|
|
|
16915
17047
|
// src/core/rules/doctrine/non-portal-endpoints.ts
|
|
16916
17048
|
var nonPortalEndpointsRule = {
|
|
16917
17049
|
name: "non-portal-endpoints",
|
|
17050
|
+
judges: "design",
|
|
16918
17051
|
description: "Only a Portal may carry endpoints: a method on a non-Portal component's interface that declares one is publishing a block that is not the system's boundary as if it were.",
|
|
16919
17052
|
codes: [
|
|
16920
17053
|
{ code: "ARCHITECTURE_VIOLATION_NON_PORTAL_ENDPOINT", defaultSeverity: "error", summary: "Non-Portal component method declaring an endpoint" }
|
|
@@ -16944,6 +17077,7 @@ var nonPortalEndpointsRule = {
|
|
|
16944
17077
|
// src/core/rules/doctrine/portal-call-auth.ts
|
|
16945
17078
|
var portalCallAuthRule = {
|
|
16946
17079
|
name: "portal-call-auth",
|
|
17080
|
+
judges: "design",
|
|
16947
17081
|
description: "Hardens authenticated cross-service calls. An OUTBOUND narrative `call` into ANOTHER component's Portal whose auth is not `none` must (a) be made by an Adapter \u2014 the only block that does external I/O \u2014 and (b) declare the credential source it presents via the step's `auth.from`. Absence of a source warns PORTAL_AUTH_UNMET; a non-Adapter presenter warns AUTH_PRESENTER_NOT_ADAPTER. The actual secret is never stored in the spec. Dispatch steps (a portal's OWN inbound routing) and self-calls are not cross-service calls and are excluded.",
|
|
16948
17082
|
codes: [
|
|
16949
17083
|
{ code: "PORTAL_AUTH_UNMET", defaultSeverity: "warning", summary: "A narrative call into another component's authed Portal does not declare where its credential loads from" },
|
|
@@ -16989,6 +17123,7 @@ var portalCallAuthRule = {
|
|
|
16989
17123
|
var COMPONENT_REF_PREFIX = "component:";
|
|
16990
17124
|
var authSourceWiringRule = {
|
|
16991
17125
|
name: "auth-source-wiring",
|
|
17126
|
+
judges: "design",
|
|
16992
17127
|
description: "Resolves the credential source a narrative call presents to an authenticated Portal. When the step's `auth.from` is a modeled reference (`component:<id>`) it must name a component that exists (UNKNOWN_AUTH_SOURCE), that can hold or load a secret \u2014 an Adapter or a Store (AUTH_SOURCE_NOT_PROVIDER) \u2014 and that the presenting component depends on or owns (AUTH_SOURCE_UNWIRED). Opaque sources (env:/config:/vault:/free note) are design notes and are never resolved; a missing source is portal-call-auth's finding.",
|
|
16993
17128
|
codes: [
|
|
16994
17129
|
{ code: "UNKNOWN_AUTH_SOURCE", defaultSeverity: "warning", summary: "auth.from references a component: source that does not exist" },
|
|
@@ -17049,6 +17184,7 @@ var authSourceWiringRule = {
|
|
|
17049
17184
|
// src/core/rules/doctrine/subsystem-boundary-dependencies.ts
|
|
17050
17185
|
var subsystemBoundaryDepsRule = {
|
|
17051
17186
|
name: "subsystem-boundary-dependencies",
|
|
17187
|
+
judges: "design",
|
|
17052
17188
|
description: "Judges every dependsOn edge that leaves its own subsystem, and every one that resolves nowhere. Across subsystems the shape is client Adapter \u2192 published remote Portal, OR a direct in-process edge licensed by a trustedLink declared on the SOURCE subsystem (the published-Portal target requirement applies either way). A published surface may also name the subsystems it serves (a publicInterfaces entry's consumers): a component every one of whose entries names consumers may be depended on only from those subsystems. An edge into another project is judged on the contract entry it resolves to (resolveSurfaceRef \u2014 a contained member's live table, a declared external's pin, or a foreign snapshot): the same Adapter requirement applies to it, and foreign snapshots of several providers that disagree make it SURFACE_REF_AMBIGUOUS. An edge into another project that does not resolve is reported once, by project-boundaries, with its resolution \u2014 never here, and never as the retired CROSS_TREE_REF_UNRESOLVED warning. A bare id that names nothing is INVALID_DEPENDENCY_REFERENCE. Boundary rules are never relaxed by a pack profile.",
|
|
17053
17189
|
codes: [
|
|
17054
17190
|
{ code: "INVALID_DEPENDENCY_REFERENCE", defaultSeverity: "error", summary: "dependsOn names a non-existent component" },
|
|
@@ -17169,6 +17305,7 @@ var stereotypeOf = (comp) => comp.dependencyClass ? `${comp.componentType} (depe
|
|
|
17169
17305
|
// src/core/rules/doctrine/logic-dependency-class.ts
|
|
17170
17306
|
var logicDependencyClassRule = {
|
|
17171
17307
|
name: "logic-dependency-class",
|
|
17308
|
+
judges: "design",
|
|
17172
17309
|
description: "Bounds an Orchestrator's dependencies by the dependencyClass it declares: pure logic depends only on pure Orchestrators; read logic also on read Orchestrators, Repositories, Indexes and Adapters; an Orchestrator that declares no class is a workflow and may depend on anything the rest of the matrix allows. Whether read logic calls a WRITE method on one of those is judged only once facade methods carry effect tags.",
|
|
17173
17310
|
codes: [
|
|
17174
17311
|
{ code: "DEPENDENCY_CLASS_VIOLATION", defaultSeverity: "error", summary: "Orchestrator depending on a component its dependencyClass does not allow: pure logic depends only on pure Orchestrators; read logic also on read Orchestrators, Repositories, Indexes and Adapters" }
|
|
@@ -17226,6 +17363,7 @@ function derivedIndexBreach(ctx, from, to) {
|
|
|
17226
17363
|
}
|
|
17227
17364
|
var dataBlockDepsRule = {
|
|
17228
17365
|
name: "data-block-dependencies",
|
|
17366
|
+
judges: "design",
|
|
17229
17367
|
description: "Keeps the data layer facing down. A Store may depend only on another Store, a backend Adapter or pure logic \u2014 Registries and Indexes depend on the Store, never the reverse. A Registry is the write path to its Store and may depend on that Store, a backend Adapter, or the pure logic that validates the write. An Index is a read projection and a Query a computed read, each over its Store through a backend Adapter where one serves it; as an exceptional case an Index may project another Index of the same Repository, never in a cycle. An Adapter is a sink toward the system: it may use pure logic, but never a Store or any other Orchestrator.",
|
|
17230
17368
|
codes: [
|
|
17231
17369
|
{ code: "ARCHITECTURE_VIOLATION_STORE_DEP", defaultSeverity: "error", summary: "Store depending on anything but another Store, a backend Adapter or pure logic" },
|
|
@@ -17295,6 +17433,7 @@ var dataBlockDepsRule = {
|
|
|
17295
17433
|
// src/core/rules/doctrine/entrypoint-dependencies.ts
|
|
17296
17434
|
var entrypointDepsRule = {
|
|
17297
17435
|
name: "entrypoint-dependencies",
|
|
17436
|
+
judges: "design",
|
|
17298
17437
|
description: "Judges the edges at the system's entry points and its process layer. Portals and Observers are top-level entry points and subscribers, so nothing may depend on them \u2014 and that is the edge's one finding, which is why no other matrix rule judges it. Downward, a Portal dispatches to Orchestrators and may READ through Indexes and Repository facades but never reaches Store/Registry/Query or an Adapter, while an Observer forwards to one Orchestrator or Supervisor over a message-bus Adapter. A View stays a passive presenter. A Supervisor reaches data only through workflows, and a component depending on a live Actor must also depend on a Supervisor that supervises it.",
|
|
17299
17438
|
codes: [
|
|
17300
17439
|
{ code: "ARCHITECTURE_VIOLATION_PORTAL_DEP", defaultSeverity: "error", summary: "Component depending on a Portal/Observer" },
|
|
@@ -17372,6 +17511,7 @@ var entrypointDepsRule = {
|
|
|
17372
17511
|
// src/core/rules/doctrine/portal-write-shortcut.ts
|
|
17373
17512
|
var portalWriteShortcutRule = {
|
|
17374
17513
|
name: "portal-write-shortcut",
|
|
17514
|
+
judges: "design",
|
|
17375
17515
|
description: "A Portal narrative call step or dispatch-table binding that reaches a write-effect method on a Repository or Index directly is the write shortcut: the Portal\u2192data-facade edge is licensed for reads only, and a write routes through an Orchestrator that owns the workflow. A dispatch step reaches its server only through a table binding, so judging every binding judges each dispatch step that takes it, once, where the route is declared. Methods that carry no effect tag are not judged.",
|
|
17376
17516
|
codes: [
|
|
17377
17517
|
{ code: "PORTAL_WRITE_SHORTCUT", defaultSeverity: "error", summary: "Portal narrative call or dispatch-table binding reaches a write-effect method on a Repository/Index directly \u2014 reads may shortcut, writes route through an Orchestrator (judged on effect-tagged facade methods; untagged methods are not yet judged)" }
|
|
@@ -17422,6 +17562,7 @@ var portalWriteShortcutRule = {
|
|
|
17422
17562
|
// src/core/rules/doctrine/pattern-membership.ts
|
|
17423
17563
|
var patternMembershipRule = {
|
|
17424
17564
|
name: "pattern-membership",
|
|
17565
|
+
judges: "design",
|
|
17425
17566
|
description: "Only patterns (Repository/FeatureComponent/RouterComponent) own member blocks, and every pattern owns at least one. Each claim must name a component that exists and is itself a building block \u2014 patterns compose at the subsystem (L1) level, never by owning one another \u2014 and a block has exactly one owner, the first pattern to claim it.",
|
|
17426
17567
|
codes: [
|
|
17427
17568
|
{ code: "EMPTY_PATTERN", defaultSeverity: "error", summary: "Pattern with no owned member blocks" },
|
|
@@ -17471,6 +17612,7 @@ var patternMembershipRule = {
|
|
|
17471
17612
|
var REPOSITORY_MEMBERS = /* @__PURE__ */ new Set(["Store", "Registry", "Index", "Query", "Adapter"]);
|
|
17472
17613
|
var patternContainmentRule = {
|
|
17473
17614
|
name: "pattern-containment",
|
|
17615
|
+
judges: "design",
|
|
17474
17616
|
description: "Holds each pattern to the containment its definition prescribes. A Repository may own only Store, Registry, Index, Query and (optionally) Adapter, judged member by member. A FeatureComponent owns exactly one Orchestrator (the logic side) and one or more Views (its faces \u2014 list, detail, form \u2014 sharing the one logic component), and nothing else. A RouterComponent owns exactly one Portal as its facade and at least one other child to route to. A counting pattern that still owns a retired member is not judged until that member is migrated: its counts change with the migration, and STEREOTYPE_RETIRED is the one finding.",
|
|
17475
17617
|
codes: [
|
|
17476
17618
|
{ code: "REPOSITORY_CONTAINMENT", defaultSeverity: "error", summary: "Repository owning a non Store/Registry/Index/Query/Adapter member" },
|
|
@@ -17531,6 +17673,7 @@ var patternContainmentRule = {
|
|
|
17531
17673
|
// src/core/rules/doctrine/unowned-blocks.ts
|
|
17532
17674
|
var unownedBlocksRule = {
|
|
17533
17675
|
name: "unowned-blocks",
|
|
17676
|
+
judges: "design",
|
|
17534
17677
|
description: `Judges the data blocks no pattern owns. A Store may stand alone deliberately \u2014 the sanctioned lightweight form for genuinely simple state, acknowledged with a lint.allow \u2014 while the recommended shape stays a Repository; what must never happen is folding the state into a consuming component, where no spec, diagram or conformance check can see it again. A Registry standing alone with no Store to write to is either mistyped (the "file-backed Registry" idiom, a fused persistent store that belongs typed Store) or orphaned. A Query has no standalone form at all: it computes reads over its own Repository's Store.`,
|
|
17535
17678
|
codes: [
|
|
17536
17679
|
{ code: "UNOWNED_STORE", defaultSeverity: "warning", summary: "Store not owned by any pattern \u2014 recommended shape is a Repository; a deliberate standalone Store needs a lint.allow" },
|
|
@@ -17576,6 +17719,7 @@ var unownedBlocksRule = {
|
|
|
17576
17719
|
// src/core/rules/doctrine/member-visibility.ts
|
|
17577
17720
|
var memberVisibilityRule = {
|
|
17578
17721
|
name: "member-visibility",
|
|
17722
|
+
judges: "design",
|
|
17579
17723
|
description: "A component may depend on a block within its own group (it is the owning pattern, or a sibling member of the same pattern), on any pattern facade, or on a standalone block \u2014 never on a block privately owned by ANOTHER pattern, which must be reached through that pattern's facade. A retired depending component is skipped, as everywhere in this family.",
|
|
17580
17724
|
codes: [
|
|
17581
17725
|
{ code: "VISIBILITY_VIOLATION", defaultSeverity: "error", summary: "Dependency on a block privately owned by another pattern" }
|
|
@@ -17598,6 +17742,7 @@ var memberVisibilityRule = {
|
|
|
17598
17742
|
// src/core/rules/doctrine/facade-forwarding.ts
|
|
17599
17743
|
var facadeForwardingRule = {
|
|
17600
17744
|
name: "facade-forwarding",
|
|
17745
|
+
judges: "design",
|
|
17601
17746
|
description: "A Repository facade method with an authored narrative must be pure 1:1 forwarding: exactly one call step, targeting one of the pattern's owned members. Anything else is logic living on the facade \u2014 move it into a member block, or acknowledge a deliberate exception via lint.allow.",
|
|
17602
17747
|
codes: [
|
|
17603
17748
|
{ code: "FACADE_FORWARDING", defaultSeverity: "warning", summary: "Pattern facade narrative is not a single call to an owned member" }
|
|
@@ -17639,6 +17784,7 @@ var facadeForwardingRule = {
|
|
|
17639
17784
|
var PROJECT_KINDS2 = new Set(PROJECT_KINDS);
|
|
17640
17785
|
var profileRegistrationRule = {
|
|
17641
17786
|
name: "profile-registration",
|
|
17787
|
+
judges: "design",
|
|
17642
17788
|
description: "Every profile name in play \u2014 the project's own projectType and each subsystem's declared profile \u2014 must be a built-in profile, a composite project kind, or one a loaded extension pack registers. An unregistered name is flagged rather than guessed at: no doctrine is governing it.",
|
|
17643
17789
|
codes: [
|
|
17644
17790
|
{ code: "UNKNOWN_PROFILE", defaultSeverity: "warning", summary: "Profile name is neither built-in nor registered by an extension pack" }
|
|
@@ -17670,6 +17816,7 @@ var BACKEND_LIKE = /* @__PURE__ */ new Set(["backend", "lowlevel-os", "game-ecs"
|
|
|
17670
17816
|
var FRONTEND_LIKE = /* @__PURE__ */ new Set(["frontend-reactive", "frontend-controller"]);
|
|
17671
17817
|
var profileStereotypeFencingRule = {
|
|
17672
17818
|
name: "profile-stereotype-fencing",
|
|
17819
|
+
judges: "design",
|
|
17673
17820
|
description: "The built-in profile families fence which stereotypes may run under them: View/FeatureComponent/RouterComponent only in a frontend-like profile; Actor/Supervisor forbidden in plc-cyclic, whose logic runs single-threaded inside one execution scan cycle; Actor/Supervisor in a frontend-like profile warned. A pack profile opts in by declaring its family; an unregistered profile is neutral, with no doctrine to guess.",
|
|
17674
17821
|
codes: [
|
|
17675
17822
|
{ code: "FRONTEND_STEREOTYPE_IN_BACKEND", defaultSeverity: "error", summary: "Frontend stereotype in a backend-oriented subsystem" },
|
|
@@ -17723,6 +17870,7 @@ var profileStereotypeFencingRule = {
|
|
|
17723
17870
|
// src/core/rules/extension/pack-profile-stereotypes.ts
|
|
17724
17871
|
var packProfileStereotypesRule = {
|
|
17725
17872
|
name: "pack-profile-stereotypes",
|
|
17873
|
+
judges: "design",
|
|
17726
17874
|
description: "A pack-registered profile carries its own stereotype doctrine on top of the built-in family fencing: the stereotypes it lists as forbidden are errors and the ones it discourages are warnings, each reported with the reason the pack states for it.",
|
|
17727
17875
|
codes: [
|
|
17728
17876
|
{ code: "PROFILE_FORBIDDEN_STEREOTYPE", defaultSeverity: "error", summary: "Stereotype forbidden by the governing pack-defined profile" },
|
|
@@ -17763,6 +17911,7 @@ var packProfileStereotypesRule = {
|
|
|
17763
17911
|
// src/core/rules/extension/pattern-references.ts
|
|
17764
17912
|
var patternReferencesRule = {
|
|
17765
17913
|
name: "pattern-references",
|
|
17914
|
+
judges: "design",
|
|
17766
17915
|
description: "Every component pattern reference resolves to a reusable pattern declared by a loaded extension pack (UNKNOWN_PATTERN_REF); a pinned version with no matching pack pattern is warned (PATTERN_VERSION_MISMATCH). Pattern constraint enforcement itself is delegated to the declaring pack's rules.",
|
|
17767
17916
|
codes: [
|
|
17768
17917
|
{ code: "UNKNOWN_PATTERN_REF", defaultSeverity: "warning", summary: "Component references a pattern no loaded pack declares" },
|
|
@@ -17807,6 +17956,7 @@ var patternReferencesRule = {
|
|
|
17807
17956
|
// src/core/rules/extension/component-variants.ts
|
|
17808
17957
|
var variantReferencesRule = {
|
|
17809
17958
|
name: "component-variants",
|
|
17959
|
+
judges: "design",
|
|
17810
17960
|
description: "A component's variant resolves to a declared registry variant (UNKNOWN_VARIANT), and the component's stereotype equals the variant's declared base (VARIANT_BASE_MISMATCH). A variant is a base-anchored specialization (a kind of the base stereotype), not a cross-cutting attribute \u2014 those stay method guarantees.",
|
|
17811
17961
|
codes: [
|
|
17812
17962
|
{ code: "UNKNOWN_VARIANT", defaultSeverity: "warning", summary: "Component references a variant no registry declares" },
|
|
@@ -17866,6 +18016,7 @@ function reportAssertion(ctx, a, message, specId, isDraftCtx) {
|
|
|
17866
18016
|
// src/core/rules/extension/assertion-forbidden-edges.ts
|
|
17867
18017
|
var assertionForbiddenEdgesRule = {
|
|
17868
18018
|
name: "assertion-forbidden-edges",
|
|
18019
|
+
judges: "design",
|
|
17869
18020
|
description: "Evaluates the `forbid-edge` assertions loaded packs declare: for every component matching the assertion's from-selector, every dependsOn/owns id whose target matches the to-selector is reported. Findings carry the pack's namespaced code (<PACK>_<CODE>) and its stated reason; severity is the pack's declaration (project sddRuleSeverity still wins, and error downgrades to warning in draft context). The codes are the packs' own, so no fixed code list applies.",
|
|
17870
18021
|
// Static codes are unknown here — packs bring their own. The validator
|
|
17871
18022
|
// gathers every loaded assertion's fullCode into knownIssueCodes.
|
|
@@ -17930,6 +18081,7 @@ function holdersAt(ctx, level) {
|
|
|
17930
18081
|
}
|
|
17931
18082
|
var assertionRequiredFieldsRule = {
|
|
17932
18083
|
name: "assertion-required-fields",
|
|
18084
|
+
judges: "design",
|
|
17933
18085
|
description: "Evaluates the `require-field` assertions loaded packs declare: at the assertion's level (component / interface / implementation), every spec whose owning component matches the on-selector must declare the named field \u2014 a top-level spec field or an `ext.*` path \u2014 and, when the assertion states a closed value set, hold one of those values. Findings carry the pack's namespaced code (<PACK>_<CODE>) and its stated reason; severity is the pack's declaration (project sddRuleSeverity still wins, and error downgrades to warning in draft context). The codes are the packs' own, so no fixed code list applies.",
|
|
17934
18086
|
// Static codes are unknown here — packs bring their own. The validator
|
|
17935
18087
|
// gathers every loaded assertion's fullCode into knownIssueCodes.
|
|
@@ -17987,6 +18139,7 @@ function boundEndpoints(ctx, on) {
|
|
|
17987
18139
|
}
|
|
17988
18140
|
var assertionEndpointShapesRule = {
|
|
17989
18141
|
name: "assertion-endpoint-shapes",
|
|
18142
|
+
judges: "design",
|
|
17990
18143
|
description: "Evaluates the `endpoint-shape` assertions loaded packs declare: every endpoint bound on a contract whose component matches the on-selector must use an allowed transport and bind an address (path / topic / command / pipe / channel) matching the assertion's anchored pattern. Findings carry the pack's namespaced code (<PACK>_<CODE>) and its stated reason; severity is the pack's declaration (project sddRuleSeverity still wins, and error downgrades to warning in draft context). The codes are the packs' own, so no fixed code list applies.",
|
|
17991
18144
|
// Static codes are unknown here — packs bring their own. The validator
|
|
17992
18145
|
// gathers every loaded assertion's fullCode into knownIssueCodes.
|
|
@@ -18012,6 +18165,7 @@ var assertionEndpointShapesRule = {
|
|
|
18012
18165
|
// src/core/rules/extension/pack-resolution.ts
|
|
18013
18166
|
var packResolutionRule = {
|
|
18014
18167
|
name: "pack-resolution",
|
|
18168
|
+
judges: "design",
|
|
18015
18169
|
description: "Every pack a project declares must resolve \u2014 from a committed bundle under .wai/packs/, or from this wairon install's pack store \u2014 and must be the content it claims. An unresolvable selection is an error reported under the code that names the remedy: PACK_NOT_INSTALLED (absent entirely), PACK_VERSION_UNSATISFIED (installed, but no version satisfies the pin), or PACK_INTEGRITY_MISMATCH (resolved content does not match the pinned integrity digest, recomputed from the files rather than read from metadata, on whichever path won). Those never downgrade to warnings: a project whose declared doctrine is missing or altered is misconfigured, and a gate running without it must not look clean. PACK_STORE_DRIFT is the one warning \u2014 a committed bundle and the store hold different content for the same name@version; the bundle applies, so the store copy is silently ignored and that deserves saying.",
|
|
18016
18170
|
codes: [
|
|
18017
18171
|
{ code: "PACK_NOT_INSTALLED", defaultSeverity: "error", summary: "A declared pack is neither bundled nor installed in the pack store" },
|
|
@@ -18029,6 +18183,7 @@ var packResolutionRule = {
|
|
|
18029
18183
|
// src/core/rules/extension/pack-reproducibility.ts
|
|
18030
18184
|
var reproducibilityRule = {
|
|
18031
18185
|
name: "pack-reproducibility",
|
|
18186
|
+
judges: "design",
|
|
18032
18187
|
description: "Under rules.enforceReproducibility (default true), every by-name pack selection must be reproducible elsewhere: pinned to an exact version (ideally with an integrity digest) or bundled into the repository. A floating selection resolves against whatever the local machine happens to have installed, so a clone or CI can validate the same tree against a different rule set (UNPINNED_PACK_SELECTION). A selection that records no fetchable source cannot be obtained by CI at all (PACK_SOURCE_UNFETCHABLE).",
|
|
18033
18188
|
codes: [
|
|
18034
18189
|
{ code: "UNPINNED_PACK_SELECTION", defaultSeverity: "warning", summary: "Pack selection floats to the latest installed version instead of pinning one" },
|
|
@@ -18059,6 +18214,7 @@ var reproducibilityRule = {
|
|
|
18059
18214
|
// src/core/rules/extension/pack-requirements.ts
|
|
18060
18215
|
var packRequirementsRule = {
|
|
18061
18216
|
name: "pack-requirements",
|
|
18217
|
+
judges: "design",
|
|
18062
18218
|
description: "Every requirement in the bound project's own composition.requirePolicies must be readable: a pack name and a semver range that parses (pack_requirement.rangeProblem). One that does not is POLICY_REQUIREMENT_INVALID, quoting the range and the unreadable token \u2014 an error on the requiring project's own gate, because the requirement is its own configuration and the family run cannot judge a member against it. It judges only the syntax: whether members adopt the requirement is the family run's question (family_validator.adoption), and nothing here reads a parent's requirements.",
|
|
18063
18219
|
codes: [
|
|
18064
18220
|
{ code: "POLICY_REQUIREMENT_INVALID", defaultSeverity: "error", summary: "A project's composition.requirePolicies names a pack range that does not parse" }
|
|
@@ -18080,6 +18236,7 @@ var packRequirementsRule = {
|
|
|
18080
18236
|
// src/core/rules/wiring/dependency-cycles.ts
|
|
18081
18237
|
var cyclesRule = {
|
|
18082
18238
|
name: "dependency-cycles",
|
|
18239
|
+
judges: "design",
|
|
18083
18240
|
description: "The component dependsOn graph must be a DAG; the first cycle through a component in the run's scope is reported with its full path.",
|
|
18084
18241
|
codes: [
|
|
18085
18242
|
{ code: "CIRCULAR_DEPENDENCY", defaultSeverity: "error", summary: "Circular dependency between components" }
|
|
@@ -18124,6 +18281,7 @@ var cyclesRule = {
|
|
|
18124
18281
|
// src/core/rules/wiring/dispatch-table-bindings.ts
|
|
18125
18282
|
var dispatchTableBindingsRule = {
|
|
18126
18283
|
name: "dispatch-table-bindings",
|
|
18284
|
+
judges: "design",
|
|
18127
18285
|
description: "A dispatch table belongs to a Portal, binds each capability once, and binds it to an existing component.method inside the portal's own subsystem (the portal dispatches inward) that the portal declares under dependsOn or owns \u2014 the table is a real runtime invocation path.",
|
|
18128
18286
|
codes: [
|
|
18129
18287
|
{ code: "DISPATCH_ON_NON_PORTAL", defaultSeverity: "error", summary: "Dispatch table declared on a component that is not a Portal" },
|
|
@@ -18203,6 +18361,7 @@ var dispatchTableBindingsRule = {
|
|
|
18203
18361
|
// src/core/rules/wiring/dispatch-step-routing.ts
|
|
18204
18362
|
var dispatchStepRoutingRule = {
|
|
18205
18363
|
name: "dispatch-step-routing",
|
|
18364
|
+
judges: "design",
|
|
18206
18365
|
description: "A dispatch narrative step must name the capability it routes and route it through a Portal whose dispatch table actually serves that capability; a guarantee the step asserts must be declared by the contract method the capability resolves to.",
|
|
18207
18366
|
codes: [
|
|
18208
18367
|
{ code: "MALFORMED_DISPATCH_STEP", defaultSeverity: "error", summary: "Dispatch step missing its capability (targetComponent presence is narrative-target-references' finding)" },
|
|
@@ -18273,6 +18432,7 @@ var dispatchStepRoutingRule = {
|
|
|
18273
18432
|
// src/core/rules/wiring/lifecycle-entrypoints.ts
|
|
18274
18433
|
var lifecycleRule = {
|
|
18275
18434
|
name: "lifecycle-entrypoints",
|
|
18435
|
+
judges: "design",
|
|
18276
18436
|
description: "Declared subsystem lifecycle entrypoints (init/shutdown flows) must name an existing component and a method on one of its interfaces \u2014 they are reachability roots, so a dangling entrypoint would silently detach every flow rooted in it.",
|
|
18277
18437
|
codes: [
|
|
18278
18438
|
{ code: "INVALID_LIFECYCLE_ENTRYPOINT", defaultSeverity: "error", summary: "Lifecycle entrypoint names a missing component or method" },
|
|
@@ -18322,6 +18482,7 @@ function liesUnder(path38, prefix) {
|
|
|
18322
18482
|
}
|
|
18323
18483
|
var portalMountsRule = {
|
|
18324
18484
|
name: "portal-mounts",
|
|
18485
|
+
judges: "design",
|
|
18325
18486
|
description: "Which listener serves which portal: a portal's routes are its methods' endpoint bindings, but nothing said which listener hands it its requests, so a portal no listener serves was unreachable without anyone noticing, and the call that mounts one crossed a boundary no contract described. A listener Portal declares its mounts \u2014 each a portal, the path prefixes routed to it, and optionally the router entry it calls \u2014 and this rule checks them against the tree: a mount must name a Portal, every HTTP endpoint of a mounted portal must lie under one of its prefixes, and every portal with HTTP endpoints must be mounted by some listener unless it is one. A path lies under a prefix when it equals it or continues it past a slash, so `/` covers only the root. Whether the named entry is really exported is the code's question, and export-conformance asks it.",
|
|
18326
18487
|
codes: [
|
|
18327
18488
|
{
|
|
@@ -18494,6 +18655,7 @@ function walk(ctx, seeds, options = {}) {
|
|
|
18494
18655
|
// src/core/rules/wiring/unused-detection.ts
|
|
18495
18656
|
var reachabilityRule = {
|
|
18496
18657
|
name: "unused-detection",
|
|
18658
|
+
judges: "design",
|
|
18497
18659
|
description: "Walks the narrative execution graph (call steps, register handoffs, dispatch-table routing, lifecycle flows) from every entrypoint (Portal/Observer/published component/lifecycle entrypoint/invokedBy-declared method) and flags components and methods no execution chain reaches. An interface method declaring invokedBy (a real caller outside the modeled graph) seeds the walk, so its narrative propagates reachability; a declaration the INTERNAL walk (without those seeds) already reaches is stale and is reported.",
|
|
18498
18660
|
codes: [
|
|
18499
18661
|
{ code: "UNUSED_COMPONENT", defaultSeverity: "warning", summary: "Component never reached by any narrative call chain" },
|
|
@@ -18578,6 +18740,7 @@ var reachabilityRule = {
|
|
|
18578
18740
|
// src/core/rules/wiring/invoked-by-description.ts
|
|
18579
18741
|
var invokedByDescriptionRule = {
|
|
18580
18742
|
name: "invoked-by-description",
|
|
18743
|
+
judges: "design",
|
|
18581
18744
|
description: 'An invokedBy declaration names a caller outside the modeled graph and silences unused-detection for its method, so the claim must stay reviewable: its "caller" prose is held to the intent floor (missing or placeholder-thin prose is reported), stating WHO invokes the method and when.',
|
|
18582
18745
|
codes: [
|
|
18583
18746
|
{ code: "INVOKED_BY_UNDESCRIBED", defaultSeverity: "warning", summary: "invokedBy declaration whose caller prose is missing or placeholder-thin" }
|
|
@@ -18605,6 +18768,7 @@ var invokedByDescriptionRule = {
|
|
|
18605
18768
|
// src/core/rules/wiring/unused-types.ts
|
|
18606
18769
|
var unusedTypesRule = {
|
|
18607
18770
|
name: "unused-types",
|
|
18771
|
+
judges: "design",
|
|
18608
18772
|
description: "Flags types no field of any type, no interface method signature and no other type's method signature references. References are matched through the type-reference grammar (generic arguments, collections, qualified ids), and the declaring type's own generic parameters are left out \u2014 a type parameter is not a reference to a type. A type named only by its OWN methods stays unused, the way a function that only calls itself is.",
|
|
18609
18773
|
codes: [
|
|
18610
18774
|
{ code: "UNUSED_TYPE", defaultSeverity: "warning", summary: "Type never referenced by fields, signatures or type methods" }
|
|
@@ -18664,6 +18828,7 @@ var unusedTypesRule = {
|
|
|
18664
18828
|
// src/core/rules/wiring/durability-round-trip.ts
|
|
18665
18829
|
var durabilityRule = {
|
|
18666
18830
|
name: "durability-round-trip",
|
|
18831
|
+
judges: "design",
|
|
18667
18832
|
description: "A durable Store (persisted RAM projection) must carry effect-tagged contract methods, and its writes require a hydration read-back reachable from a lifecycle init entrypoint. read-through is exempt (every read IS the read-back), as are ram-projection (rebuilt not restored) and cache (evictable, loss-safe). The flagship semantic check is opt-out by declaration, never silently absent \u2014 the declaration itself is enforced by durability-declaration.",
|
|
18668
18833
|
codes: [
|
|
18669
18834
|
{ code: "MISSING_EFFECT_TAG", defaultSeverity: "warning", summary: "Durable Store contract method lacks an effect: read | write tag" },
|
|
@@ -18719,6 +18884,7 @@ function namesBareType(refs) {
|
|
|
18719
18884
|
}
|
|
18720
18885
|
var untypedSeamRule = {
|
|
18721
18886
|
name: "untyped-seams",
|
|
18887
|
+
judges: "design",
|
|
18722
18888
|
description: "Methods on a subsystem's published components (its public surface) should not take or return bare Json/any/unknown/object, judged through each method's type references so a prose signature is judged like structured params: cross-subsystem contracts are the swap seam and must be typed. Generic-dispatch portals carry per-capability types via their dispatch table instead.",
|
|
18723
18889
|
codes: [
|
|
18724
18890
|
{ code: "UNTYPED_SEAM", defaultSeverity: "warning", summary: "Bare Json/any/unknown/object parameter or return crossing a subsystem public surface" }
|
|
@@ -18776,6 +18942,7 @@ function withoutQuotedText(text3) {
|
|
|
18776
18942
|
}
|
|
18777
18943
|
var proseClaimRule = {
|
|
18778
18944
|
name: "prose-claims",
|
|
18945
|
+
judges: "design",
|
|
18779
18946
|
description: 'Flags durability/side-effect claims that exist only in prose: a local step description or an intent paragraph claiming persistence ("persisted", "persistence", "survives restart", "registered into") on a logic component whose narrative has no call, register or dispatch edge to any data-layer component (Store/Registry/Index/Adapter/Repository). Text inside quotes or backticks names a value or quotes a message and claims nothing. Data-layer components are exempt \u2014 they ARE the persistence.',
|
|
18780
18947
|
codes: [
|
|
18781
18948
|
{ code: "UNREALIZED_CLAIM", defaultSeverity: "warning", summary: "Durability/side-effect claim in prose with no matching structural edge" }
|
|
@@ -18833,6 +19000,7 @@ var proseClaimRule = {
|
|
|
18833
19000
|
// src/core/rules/wiring/unique-invariant-ids.ts
|
|
18834
19001
|
var uniqueInvariantIdsRule = {
|
|
18835
19002
|
name: "unique-invariant-ids",
|
|
19003
|
+
judges: "design",
|
|
18836
19004
|
description: `An entity's declared invariant ids must be unique within the entity: a narrative step asserts an invariant by "<type-id>.<invariant-id>", so a repeated id makes one assertion stand for two properties and satisfying either silences both.`,
|
|
18837
19005
|
codes: [
|
|
18838
19006
|
{ code: "DUPLICATE_INVARIANT_ID", defaultSeverity: "error", summary: "An entity declares two invariants with the same id" }
|
|
@@ -18923,6 +19091,7 @@ function resolveComponentClass(t, ctx) {
|
|
|
18923
19091
|
}
|
|
18924
19092
|
var invariantBackingRule = {
|
|
18925
19093
|
name: "invariant-backing",
|
|
19094
|
+
judges: "design",
|
|
18926
19095
|
description: 'The invariant registry: entities may declare domain invariants (type.invariants), anchored through their componentClass. Every write-effect contract method of the owning component must carry a narrative step asserting each invariant (step.assertsInvariants: "<type-id>.<invariant-id>") \u2014 the same declared-and-backed shape as semantic guarantees. This is an HONEST lint over declarations: a green run means every write path visibly claims the invariant, never that the narrative or code actually enforces it. An invariant with no resolvable owner or no declared write path is unanchored, and nothing further is claimed about it.',
|
|
18927
19096
|
codes: [
|
|
18928
19097
|
{ code: "INVARIANT_UNANCHORED", defaultSeverity: "warning", summary: "An entity declares invariants but has no componentClass, its componentClass does not resolve, or the owning component declares no write-effect contract methods" },
|
|
@@ -18981,6 +19150,7 @@ var invariantBackingRule = {
|
|
|
18981
19150
|
// src/core/rules/wiring/invariant-references.ts
|
|
18982
19151
|
var invariantReferencesRule = {
|
|
18983
19152
|
name: "invariant-references",
|
|
19153
|
+
judges: "design",
|
|
18984
19154
|
description: "Every invariant a narrative step asserts (step.assertsInvariants) must resolve to a declared entity invariant: the reference splits at its last dot into a type reference and an invariant id, and some entity matching that type reference must declare that id. A reference nothing declares is an error \u2014 the step claims a property that exists nowhere.",
|
|
18985
19155
|
codes: [
|
|
18986
19156
|
{ code: "UNKNOWN_INVARIANT_REF", defaultSeverity: "error", summary: "A narrative step asserts an invariant that no entity declares" }
|
|
@@ -19041,6 +19211,7 @@ function collectEnds(ctx) {
|
|
|
19041
19211
|
}
|
|
19042
19212
|
var eventTopologyRule = {
|
|
19043
19213
|
name: "event-topology",
|
|
19214
|
+
judges: "design",
|
|
19044
19215
|
description: "Bipartite completeness of the declared pub/sub graph: every topic a component emits (emits declarations, MessageBus publish endpoints) must have at least one subscriber (subscribesTo declarations, MessageBus subscribe endpoints), and every subscription must have at least one source. Pairing is by exact topic name, and a topic is reported once per component however many declarations bind it there. Trees that declare no event edges see nothing \u2014 the rule costs request/response systems zero noise. Warnings + lint.allow: a topic produced for (or consumed from) an EXTERNAL system is a legitimate, declarable exception.",
|
|
19045
19216
|
codes: [
|
|
19046
19217
|
{ code: "UNCONSUMED_TOPIC", defaultSeverity: "warning", summary: "Topic is emitted but nothing in the tree subscribes to it" },
|
|
@@ -19078,6 +19249,7 @@ var eventTopologyRule = {
|
|
|
19078
19249
|
var quoteList = (names) => names.map((n) => `"${n}"`).join(", ");
|
|
19079
19250
|
var sourceFileLinkageRule = {
|
|
19080
19251
|
name: "source-file-linkage",
|
|
19252
|
+
judges: "code",
|
|
19081
19253
|
description: "Code\u2194spec Level 1: every source file an implementation names \u2014 its own sourcePath and each method's \u2014 must resolve to a real, readable file inside the project root, and an implementation that names no file at all, or leaves a contract method without one, is linked to no code. A file that escapes the root, is missing or cannot be analyzed is reported once and blocks only the methods realized in it. A run in which TypeScript/JavaScript files were analyzed below exact grade reports that once (CONFORMANCE_DEGRADED), since dependency conformance then skips them. Implementations under chained subsystems (projectPath) validate standalone in their own project run and are skipped here.",
|
|
19082
19254
|
codes: [
|
|
19083
19255
|
{ code: "MISSING_SOURCE_PATH", defaultSeverity: "warning", summary: "An implementation names no source file at all, or a contract method is left without one \u2014 structural conformance cannot link it to code" },
|
|
@@ -19188,6 +19360,7 @@ function bodyReachable(code, file, symbol) {
|
|
|
19188
19360
|
}
|
|
19189
19361
|
var methodRealizationRule = {
|
|
19190
19362
|
name: "method-realization",
|
|
19363
|
+
judges: "code",
|
|
19191
19364
|
description: "Code\u2194spec Level 1: every L3 contract method must be realized in its own source file (the method's sourcePath, else the implementation's) at its conformance tier \u2014 declared | anchored | off; Portals default to anchored, everything else to declared, and a per-method `symbol` maps an intent-language name onto the code name. A method realized by a DECLARATION owes a function BODY as well: at exact grade one must be reachable under the symbol, here or through the imports and republications this file forwards it by, so a signature, an ambient or interface declaration or a plain value binding stops reading as an implementation (METHOD_BODY_NOT_FOUND). Findings carry the analysis grade (exact AST | pattern table | generic scan) so weaker analysis is visible. Methods whose file escapes the root, is missing or could not be analyzed are left to source-file-linkage, and implementations under chained subsystems (projectPath) validate standalone in their own project run.",
|
|
19192
19365
|
codes: [
|
|
19193
19366
|
{ code: "UNREALIZED_METHOD", defaultSeverity: "warning", summary: "An L3 contract method has no anchor in its own source file (the method's sourcePath, else the implementation's) at the required conformance tier" },
|
|
@@ -19248,6 +19421,7 @@ var methodRealizationRule = {
|
|
|
19248
19421
|
// src/core/rules/conformance/finding-realization.ts
|
|
19249
19422
|
var findingRealizationRule = {
|
|
19250
19423
|
name: "finding-realization",
|
|
19424
|
+
judges: "code",
|
|
19251
19425
|
description: "Code\u2194spec Level 1: every finding code an L3 contract method declares must be anchored in the method's own source file (the method's sourcePath, else the implementation's) \u2014 as a string literal or, at exact grade, a property-access name, so a code reported through a constants object counts. Below exact grade any identifier or word counts, and the grade rides on the finding. Respects the conformance dial (off skips), leaves unresolvable files to source-file-linkage, and skips implementations under chained subsystems (projectPath), which validate standalone in their own project run.",
|
|
19252
19426
|
codes: [
|
|
19253
19427
|
{ code: "UNREALIZED_FINDING", defaultSeverity: "warning", summary: "A finding code a contract method declares appears in the method's source file neither as a string literal nor as a property-access name (below exact grade, any identifier counts)" }
|
|
@@ -19373,6 +19547,7 @@ function describeMiss(m) {
|
|
|
19373
19547
|
}
|
|
19374
19548
|
var callConformanceRule = {
|
|
19375
19549
|
name: "call-conformance",
|
|
19550
|
+
judges: "code",
|
|
19376
19551
|
description: "Code\u2194spec Level 3: the CLAIMED call \u2194 realized call relation, judged both ways against the method's own source file (its sourcePath, else the implementation's), at exact analysis grade only. A method claims a call two ways and they face one check: a narrative `call` step, and an entry of the declared `calls` a narrative-less method reaches its collaborators through. They assert the same thing, and the step number is no part of the claim \u2014 this check never reads order, so it is only how a finding points at one. Forward: every claim must be realized by a call whose callee RESOLVES TO one of the target method's own source files \u2014 the target's contract name or a per-method `symbol` override, closed transitively over the named helpers the realized function calls, and resolved against every file the call CAN have reached: a `this.<field>.<method>()` receiver followed through the field's DECLARED TYPE, a `new Class(...).<method>()` receiver through the module its CLASS NAME came from, and a plain `<name>.<method>()` receiver through the type the file ANNOTATES that name with \u2014 a parameter's, or an annotated variable's, which is how a module that wires its collaborators as closures writes every one of its calls. A matching call whose origin a pure model cannot resolve is reported apart as CALL_ORIGIN_UNRESOLVED, which NAMES the shape it could not follow and asks for nothing \u2014 a coverage hole in the reader, reported for the reason CONFORMANCE_DEGRADED is: a silently degraded gate is worse than a degraded one. A finding names a landing only from the PROVEN tier so that widening what a call reached can accept a claim but never accuse one, and a target that names no file of its own falls back to name membership. Converse: a call that resolves to a modelled method of ANOTHER component in the SAME file crosses a component boundary while looking local, so the method must claim it \u2014 in a step or in `calls` (UNDECLARED_COLOCATED_CALL) \u2014 judged on the proven tier alone, and a same-file private helper is no modelled method and is never reported. A method that claims nothing at all is judged in neither direction: what it leaves unsaid is UNUSED_*'s subject. Order, arguments and conditions stay unverified, dispatch steps (runtime-table routed) are no claim about a call site, a malformed declared reference is MALFORMED_DECLARED_CALL's finding and names no target here, the conformance dial (off) skips, and weaker analysis grades never guess.",
|
|
19377
19552
|
codes: [
|
|
19378
19553
|
{ code: "CALL_STEP_UNREALIZED", defaultSeverity: "warning", summary: "Narrative call step or declared call realized by no call that resolves to the target method's own source file \u2014 the call is absent, or it lands in another module", carryable: true },
|
|
@@ -19498,6 +19673,7 @@ var callConformanceRule = {
|
|
|
19498
19673
|
var isStatelessLogic = (component) => isLogic(component) && !holdsState(component);
|
|
19499
19674
|
var hiddenStateRule = {
|
|
19500
19675
|
name: "hidden-state",
|
|
19676
|
+
judges: "code",
|
|
19501
19677
|
description: "The fields-vs-Store criterion, statically approximated: module-scope mutable bindings (let/var) in a source file mapped EXCLUSIVELY to logic that holds no state (a component whose type isLogic() and not holdsState(): an Orchestrator, or a Specialist until it is retired) are flagged as hidden held state. A component maps every file its implementations and their methods name. State a logic component keeps for itself is invisible to the spec, the canvas, and every persistence rule: promote it to a Store (durability: cache for loss-safe memo state), or lint.allow with the reason it is genuinely wiring or ephemeral. Supervisor and Actor files may hold runtime state. Exact analysis grade only; files also mapped to data or boundary components are exempt (N:1 collapse); const-bound container mutation is beyond this check and the finding says so.",
|
|
19502
19678
|
codes: [
|
|
19503
19679
|
{ code: "HIDDEN_STATE", defaultSeverity: "warning", summary: "Module-scope mutable binding in a file mapped only to stateless logic (Orchestrators) \u2014 held state hiding outside a Store, Supervisor or Actor" }
|
|
@@ -19548,6 +19724,7 @@ var hiddenStateRule = {
|
|
|
19548
19724
|
// src/core/rules/conformance/dependency-conformance.ts
|
|
19549
19725
|
var dependencyConformanceRule = {
|
|
19550
19726
|
name: "dependency-conformance",
|
|
19727
|
+
judges: "code",
|
|
19551
19728
|
description: "Code\u2194spec Level 2: runtime import edges between component-mapped source files (a component maps to every file its implementations and their methods name) must be justified by declared relations \u2014 a direct dependsOn/owns pair, a shared component, membership in a depended-on pattern, or (across subsystems) a declared edge to the target subsystem's published surface (UNDECLARED_DEPENDENCY). Conversely, a declared dependsOn/owns edge between components realized in different files should be visible as an import between any file of the source and any file of the target (UNREALIZED_DEPENDENCY \u2014 DI indirection can defeat this, hence warning). Only exact-grade analyzed files participate; type-only imports are exempt; chained subprojects validate standalone.",
|
|
19552
19729
|
codes: [
|
|
19553
19730
|
{ code: "UNDECLARED_DEPENDENCY", defaultSeverity: "warning", summary: "A runtime import between component-mapped files has no declared dependsOn/owns (or published-surface) justification" },
|
|
@@ -19654,6 +19831,7 @@ var dependencyConformanceRule = {
|
|
|
19654
19831
|
// src/core/rules/conformance/portal-imports.ts
|
|
19655
19832
|
var portalImportsRule = {
|
|
19656
19833
|
name: "portal-imports",
|
|
19834
|
+
judges: "code",
|
|
19657
19835
|
description: "A runtime import that crosses a subsystem boundary must land on a file that realizes one of the target subsystem's PUBLISHED components \u2014 its portal file. dependency-conformance asks whether the hop is declared; this asks where it lands. An import that reaches past the portal into a concrete module can use anything that module exports, published or not, so the portal stops being the boundary and names no contract ever promised leak across it \u2014 which is how a surface grows that nobody designed. In-process it is cosmetic at runtime; it is not cosmetic for the contract. Only exact-grade component-mapped files participate; type-only imports are exempt; chained subprojects validate standalone.",
|
|
19658
19836
|
codes: [
|
|
19659
19837
|
{
|
|
@@ -19708,6 +19886,7 @@ var portalImportsRule = {
|
|
|
19708
19886
|
// src/core/rules/conformance/integration-sim-declaration.ts
|
|
19709
19887
|
var integrationSimDeclarationRule = {
|
|
19710
19888
|
name: "integration-sim-declaration",
|
|
19889
|
+
judges: "code",
|
|
19711
19890
|
description: "Once a subsystem has adopted integration sims (its first declared L4 simPath), every OTHER complete, non-leaf implementation of that subsystem must declare one too: its unit suite proves contract shape against mocks, not that the wired components run together. A leaf component's unit suite IS its sim, and a subsystem that has adopted nothing is never flooded.",
|
|
19712
19891
|
codes: [
|
|
19713
19892
|
{ code: "MISSING_INTEGRATION_SIM", defaultSeverity: "warning", summary: "Complete non-leaf implementation in a sim-adopting subsystem declares no simPath" }
|
|
@@ -19744,6 +19923,7 @@ var integrationSimDeclarationRule = {
|
|
|
19744
19923
|
// src/core/rules/conformance/integration-sim-file.ts
|
|
19745
19924
|
var integrationSimFileRule = {
|
|
19746
19925
|
name: "integration-sim-file",
|
|
19926
|
+
judges: "code",
|
|
19747
19927
|
description: "A declared L4 simPath must resolve to a readable, committed file inside the project root \u2014 the integration harness is a re-runnable file, not a claim. Execution stays CI's job.",
|
|
19748
19928
|
codes: [
|
|
19749
19929
|
{ code: "SIM_FILE_MISSING", defaultSeverity: "warning", summary: "Declared simPath resolves to no file inside the project root" }
|
|
@@ -19773,6 +19953,7 @@ var integrationSimFileRule = {
|
|
|
19773
19953
|
// src/core/rules/conformance/integration-sim-wiring.ts
|
|
19774
19954
|
var integrationSimWiringRule = {
|
|
19775
19955
|
name: "integration-sim-wiring",
|
|
19956
|
+
judges: "code",
|
|
19776
19957
|
description: "A committed integration harness's import graph \u2014 closed transitively over the analyzed module set, exact grade only \u2014 must reach at least one of the component's own source modules (its implementation's or a method's) and at least one source module of each direct dependsOn/owns component (technology-boundary dependencies exempt: their contract-faithful fakes are sanctioned). Execution is CI's job \u2014 this proves wiring, never that the sim passes.",
|
|
19777
19958
|
codes: [
|
|
19778
19959
|
{ code: "UNWIRED_INTEGRATION_SIM", defaultSeverity: "warning", summary: "Sim file does not import the real modules it claims to wire" }
|
|
@@ -19830,6 +20011,7 @@ var integrationSimWiringRule = {
|
|
|
19830
20011
|
// src/core/rules/conformance/integration-sim-coverage.ts
|
|
19831
20012
|
var integrationSimCoverageRule = {
|
|
19832
20013
|
name: "integration-sim-coverage",
|
|
20014
|
+
judges: "code",
|
|
19833
20015
|
description: "Once a committed integration harness carries sim:<component-id>. anchors, that component claims path coverage: every narrated method's happy path and every labeled throw step needs its exact sim:<component>.<method>[:<label>] string-literal anchor. Anchors prove the path is NAMED \u2014 assertion quality and execution stay CI's job.",
|
|
19834
20016
|
codes: [
|
|
19835
20017
|
{ code: "SIM_PATH_UNCOVERED", defaultSeverity: "warning", summary: "A narrative path has no sim:<component>.<method>[:<label>] anchor in the component's coverage-opted harness" }
|
|
@@ -19903,6 +20085,7 @@ function ownerOf2(type, file) {
|
|
|
19903
20085
|
}
|
|
19904
20086
|
var typeRealizationRule = {
|
|
19905
20087
|
name: "type-realization",
|
|
20088
|
+
judges: "code",
|
|
19906
20089
|
description: "Code\u2194spec Level 1 for the data model: a type that names a sourcePath is claiming code, so every file it names \u2014 its own and each method's \u2014 must resolve to a real, readable file inside the project root, its file must PUBLISH its declaration (an exported name at exact grade, the declaration tier below it, under its `symbol` when the code-level name differs from the type's name \u2014 and a pure re-export barrel publishes nothing of its own, so a claim on one is never realized), and each of its pure methods must appear in its own file (the method's sourcePath, else the type's) at the declaration tier, under the method's `symbol`, else the method's name. A type that names no sourcePath claims nothing and is never reported. Findings carry the analysis grade (exact AST | pattern table | generic scan) so weaker analysis is visible, a file that escapes the root, is missing or could not be analyzed is reported once and blocks only what it would have realized, and types under chained subsystems (projectPath) validate standalone in their own project run.",
|
|
19907
20090
|
codes: [
|
|
19908
20091
|
{ code: "UNREALIZED_TYPE", defaultSeverity: "warning", summary: "A type names a sourcePath but its declaration is nowhere in that file \u2014 the claim points at code that does not hold it" },
|
|
@@ -19971,6 +20154,7 @@ function absence(optional) {
|
|
|
19971
20154
|
}
|
|
19972
20155
|
var typeShapeRule = {
|
|
19973
20156
|
name: "type-shape",
|
|
20157
|
+
judges: "code",
|
|
19974
20158
|
description: "Code-to-contract for the DATA: a type spec's `fields` are compared, name by name, against the shape its `sourcePath` actually declares. `typeRealization` asks whether a type EXISTS in code; nothing asked whether it is the shape the spec claims, so a type spec could describe two fields of a six-field record \u2014 and call the two that are optional required \u2014 straight through a lock and a CI gate, while the ERD, the agent briefs and every implementer read it as truth. That is the worst of the code-to-spec gaps, because a wrong signature eventually breaks at a call site and a type spec that lies is only ever read by humans and agents. Two origins answer at exact grade: a DECLARED shape, whose members the file lists, and a DERIVED one, an alias followed one hop to the object literal its schema is built from, where the keys are the members. A shape that extends another is judged on what it shows and never on what it omits, since its inherited members are not in this file to count.",
|
|
19975
20159
|
codes: [
|
|
19976
20160
|
{
|
|
@@ -20104,6 +20288,7 @@ function agreed(judgements, reading) {
|
|
|
20104
20288
|
}
|
|
20105
20289
|
var paramConformanceRule = {
|
|
20106
20290
|
name: "param-conformance",
|
|
20291
|
+
judges: "code",
|
|
20107
20292
|
description: "Code-to-contract for the SIGNATURE, the last of the three readings a spec-driven gate never made: a contract declares `params`, and nothing ever compared them to the parameters of the function that realizes the method. A contract could promise an argument the code does not take, take one the contract never mentions \u2014 including a secret \u2014 or name the same argument two different things, and the brief handed to an implementer would carry the contract's version. Parameters are matched by POSITION against the tail of the realization's list, and the declared type is what tells a rename from a dropped argument: `seed(config: HostConfig)` realized as `bootstrapInstance(cfg: HostConfig)` is one parameter under two names, which anything matching on names alone reads as a parameter the code lost. What a realization takes BEFORE the contract's own parameters is wiring, and it is declared on the implementation as `injectedParams` rather than inferred, because an inferred prefix cannot be told from a renamed first argument. A method the named file only CALLS is left to `methodRealization`, which already reports that the body is not here.",
|
|
20108
20293
|
codes: [
|
|
20109
20294
|
{
|
|
@@ -20238,6 +20423,7 @@ function answers(route, endpoint) {
|
|
|
20238
20423
|
}
|
|
20239
20424
|
var routeCoverageRule = {
|
|
20240
20425
|
name: "route-coverage",
|
|
20426
|
+
judges: "code",
|
|
20241
20427
|
description: "Code-to-contract for the ROUTES: does every route a router actually answers have a contract endpoint, and does every contract endpoint have a route that answers it? A portal's endpoints are what its contract promises; the router is what the code serves; nothing compared the two, so a route with no contract \u2014 including a write \u2014 could run for months without a single rule noticing. The listener's mount names the router function (its `via`), and the routes are read out of that function's guards; the path segment the router never checks is the one the mount's prefix guarantees. One idiom is read \u2014 a method comparison with comparisons on the path's split segments \u2014 and a mounted router that yields no route in it is reported as unread, never passed: a check that cannot see a router must say so rather than stay quiet.",
|
|
20242
20428
|
codes: [
|
|
20243
20429
|
{
|
|
@@ -20340,6 +20526,7 @@ var routeCoverageRule = {
|
|
|
20340
20526
|
// src/core/rules/conformance/unclaimed-source.ts
|
|
20341
20527
|
var unclaimedSourceRule = {
|
|
20342
20528
|
name: "unclaimed-source",
|
|
20529
|
+
judges: "code",
|
|
20343
20530
|
description: "Code\u2194spec Level 1, asked the other way round: every source file under the project's declared source roots must be named by some spec \u2014 an implementation's sourcePath, a method's, a simPath, or a type's \u2014 or be carried in the frozen `rules.conformance.unclaimed` list. A pure re-export barrel declares nothing of its own and only republishes other modules, so it has no code to claim and is exempt (exact grade only: a weaker grade cannot tell a barrel from a file it failed to parse). Declaring no source roots leaves the check silent, which is what makes it opt-in; the unclaimed list is a one-way debt register, so an entry the walk no longer finds unclaimed must be deleted and the list can only shrink.",
|
|
20344
20531
|
codes: [
|
|
20345
20532
|
{ code: "UNCLAIMED_SOURCE_FILE", defaultSeverity: "warning", summary: "A source file under a declared source root that no spec names and the frozen unclaimed list does not carry \u2014 code nobody designed" },
|
|
@@ -20387,6 +20574,7 @@ var unclaimedSourceRule = {
|
|
|
20387
20574
|
var NOTHING_PROMISED = /* @__PURE__ */ new Set();
|
|
20388
20575
|
var exportConformanceRule = {
|
|
20389
20576
|
name: "export-conformance",
|
|
20577
|
+
judges: "code",
|
|
20390
20578
|
description: "Code-to-contract for the SURFACE: does this file publish anything the components it realizes never promised, that another component then takes? The rest of the conformance set reads contract-to-code \u2014 it asks whether the code holds what a spec claims \u2014 so a file could export whatever it liked under a component's name and nothing looked. That is one half of the same hole as a contract promising a parameter it never passes. A method's `exportedVia` names the export a consumer imports to REACH it \u2014 the value that composes it, which `symbol` cannot name because `symbol` names the function inside \u2014 and a listener's mount names the router entry it calls to hand a portal its requests; both are declared publication, and a declared handle the file does not actually export is itself a finding, so naming one can never become a free-text suppression. Both questions are asked only of a file read at exact grade, and a taker counts only when its own import specifier resolves to the file it is accused of taking from: below that nothing separates an export from a mention, or a shared word from a shared module.",
|
|
20391
20579
|
codes: [
|
|
20392
20580
|
{
|
|
@@ -20545,6 +20733,7 @@ var exportConformanceRule = {
|
|
|
20545
20733
|
// src/core/rules/conformance/carried-debt.ts
|
|
20546
20734
|
var carriedDebtRule = {
|
|
20547
20735
|
name: "carried-debt",
|
|
20736
|
+
judges: "code",
|
|
20548
20737
|
description: "The conformance debt register (`rules.conformance.carried`) holds the code\u2194spec findings this tree carries as declared, classified debt \u2014 `unclaimed`'s one-way shape, for findings about code a spec does name. It is not a second lint.allow: an allow says a finding is wrong here by design, an entry here says the finding is right and unpaid, and each entry states which of the three kinds it is (drift: spec and code disagree; undecided: the fix waits on a modelling decision nobody has taken; unreadable: the analysis cannot follow the shape the code is written in) and why. Only a code a rule declares CARRYABLE may appear, and naming any other is an error, so the register can never widen into general-purpose suppression. An entry carries a finding only when it lists EVERY unit that finding reports, so an aggregating finding cannot grow behind it; an entry that matched nothing, that duplicates another, that names a spec the tree does not hold, or that lists a unit no longer reported is STALE_CARRIED_FINDING, so the register only shrinks. The audit is silent in a run that could not have fired the findings at all: one that built no code model, the specs outside a scoped run, and a code the project switched off. Nothing can check a reason for TRUTH \u2014 a stale entry is one that stopped applying, never one that still applies for a reason that has become false \u2014 so a reason group may declare its own classification provisional (`revisit`: what would settle it), and every run counts those findings beside the kind totals.",
|
|
20549
20738
|
codes: [
|
|
20550
20739
|
{ code: "UNCARRYABLE_FINDING", defaultSeverity: "error", summary: "The conformance debt register names an issue code no rule emits, or one no rule declares carryable \u2014 the register holds measured code\u2194spec debt, never a design rule somebody wants quiet" },
|
|
@@ -20656,6 +20845,7 @@ function routingTableComponents(ctx) {
|
|
|
20656
20845
|
}
|
|
20657
20846
|
var methodCohesionRule = {
|
|
20658
20847
|
name: "method-cohesion",
|
|
20848
|
+
judges: "design",
|
|
20659
20849
|
description: "An Orchestrator's methods share collaborators; when they split into groups that call no component in common, the component holds more than one responsibility. Reported only where two or more groups each hold two or more methods, so a single specialized method is not a finding \u2014 and never on a pure forwarder, whose every narrated method is a single hand-off and whose responsibility therefore lives in what it forwards to.",
|
|
20660
20850
|
codes: [
|
|
20661
20851
|
{ code: "INCOHESIVE_METHODS", defaultSeverity: "warning", summary: "Orchestrator's methods form two or more groups of two or more that share no called component" }
|
|
@@ -20700,6 +20890,7 @@ var methodCohesionRule = {
|
|
|
20700
20890
|
var DEFAULT_GOD_COMPONENT_THRESHOLD = 8;
|
|
20701
20891
|
var couplingRule = {
|
|
20702
20892
|
name: "coupling-health",
|
|
20893
|
+
judges: "design",
|
|
20703
20894
|
description: "Two subsystems depending on each other is a real architectural commitment (deployment affinity, backpressure, scaling coupling). It is allowed but must be acknowledged with a trustedLinks declaration on either side, stating the reason (e.g. a latency fast lane that bypasses the bus). A trustedLink on the SOURCE subsystem additionally licenses its direct edges into the peer without the client-Adapter shim; the published-Portal target requirement stays either way. Also flags components whose dependsOn fan-out suggests a god component.",
|
|
20704
20895
|
codes: [
|
|
20705
20896
|
{ code: "MUTUAL_SUBSYSTEM_DEPENDENCY", defaultSeverity: "warning", summary: "Subsystems depend on each other without a declared trusted link" },
|
|
@@ -20828,6 +21019,7 @@ var LANGUAGE_MARKERS = {
|
|
|
20828
21019
|
};
|
|
20829
21020
|
var signatureLanguageBuiltinsRule = {
|
|
20830
21021
|
name: "signature-language-builtins",
|
|
21022
|
+
judges: "design",
|
|
20831
21023
|
description: "Contracts must speak the declared target language: builtin types that unambiguously belong to another language family are flagged in method signatures (e.g. `usize` in a TypeScript system, `Promise` in a Rust one). Set targetLanguage on the system (L0) or override per subsystem (L1).",
|
|
20832
21024
|
codes: [
|
|
20833
21025
|
{ code: "LANGUAGE_FOREIGN_BUILTIN", defaultSeverity: "warning", summary: "Signature uses a builtin from a different language family" }
|
|
@@ -20903,6 +21095,7 @@ function constructLabel(construct) {
|
|
|
20903
21095
|
}
|
|
20904
21096
|
var narrativeLanguageConstructsRule = {
|
|
20905
21097
|
name: "narrative-language-constructs",
|
|
21098
|
+
judges: "design",
|
|
20906
21099
|
description: "Narratives must describe flows the declared target language can express: steps using constructs the language lacks (e.g. exceptions in Rust/Go, do-while in Python) are flagged with the idiomatic alternative. Set targetLanguage on the system (L0) or override per subsystem (L1).",
|
|
20907
21100
|
codes: [
|
|
20908
21101
|
{ code: "LANGUAGE_FOREIGN_FLOW", defaultSeverity: "warning", summary: "Narrative flow step uses a construct the target language does not have" }
|
|
@@ -20946,6 +21139,7 @@ var narrativeLanguageConstructsRule = {
|
|
|
20946
21139
|
var DATA_LAYER = /* @__PURE__ */ new Set(["Adapter", "Store", "Registry", "Index"]);
|
|
20947
21140
|
var technologyBindingRule = {
|
|
20948
21141
|
name: "technology-binding",
|
|
21142
|
+
judges: "design",
|
|
20949
21143
|
description: "Only data-layer stereotypes (Adapter/Store/Registry/Index) should bind a technology directly: an L4 that declares `technologies` on logic has no swap seam. No hardcoded vendor lists \u2014 only declared tokens are policed, so the rule never fires on a tree that doesn't opt in.",
|
|
20950
21144
|
codes: [
|
|
20951
21145
|
{ code: "TECH_ON_LOGIC_COMPONENT", defaultSeverity: "warning", summary: "Technology bound by a non-data-layer stereotype" }
|
|
@@ -20991,6 +21185,7 @@ function contractIdentifiers(m) {
|
|
|
20991
21185
|
}
|
|
20992
21186
|
var technologyRule = {
|
|
20993
21187
|
name: "technology-boundaries",
|
|
21188
|
+
judges: "design",
|
|
20994
21189
|
description: "Technology stays behind its owning boundary: an L4 that declares `technologies` (e.g. [mysql]) makes its component's ownership tree the technology's home. References outside that tree are leakage, and L3 contract identifiers must stay intent-language \u2014 the contract is the swap seam, so the vendor name is wrong even on the owning component's own interface.",
|
|
20995
21190
|
codes: [
|
|
20996
21191
|
{ code: "TECH_LEAKAGE", defaultSeverity: "warning", summary: "Technology referenced outside its owning boundary" },
|
|
@@ -21208,6 +21403,7 @@ function getBaseId(id) {
|
|
|
21208
21403
|
var isAllUppercase = (s) => /^[A-Z0-9_]+$/.test(s);
|
|
21209
21404
|
var namingRule = {
|
|
21210
21405
|
name: "naming-conventions",
|
|
21406
|
+
judges: "design",
|
|
21211
21407
|
description: "Enforces naming conventions (casing styles or regular expressions) for subsystem, component, interface, type (differentiating entities and value-objects), method, variables/parameters, fields, and constants names/IDs, plus stereotype-specific naming patterns.",
|
|
21212
21408
|
codes: [
|
|
21213
21409
|
{ code: "NAMING_CONVENTION_VIOLATION", defaultSeverity: "warning", summary: "Item name or ID does not match the configured casing pattern or regex" },
|
|
@@ -21366,6 +21562,7 @@ function checkDescription(ctx, desc, required, minLength, specId, typeName, isDr
|
|
|
21366
21562
|
}
|
|
21367
21563
|
var complexityRule = {
|
|
21368
21564
|
name: "complexity-and-metadata",
|
|
21565
|
+
judges: "design",
|
|
21369
21566
|
description: "Enforces metadata documentation completeness (checking if descriptions are missing or too short) and structural complexity caps (limits on methods per interface, parameters per method, dependencies per component, and components per subsystem) configured globally or in architectural profiles. A narrative's own length and shape are judged by narrative-complexity.",
|
|
21370
21567
|
codes: [
|
|
21371
21568
|
{ code: "MISSING_DESCRIPTION", defaultSeverity: "warning", summary: "Required description field is missing or empty" },
|
|
@@ -21507,6 +21704,7 @@ var DEFAULT_WARN_ABOVE = "moderate";
|
|
|
21507
21704
|
var DEFAULT_MAX_STEPS = 25;
|
|
21508
21705
|
var narrativeComplexityRule = {
|
|
21509
21706
|
name: "narrative-complexity",
|
|
21707
|
+
judges: "design",
|
|
21510
21708
|
description: "Judges a narrative on two independent axes: the cognitive band its shape earns, from branching and nesting, and the number of steps it lists. A flat list of registrations is long but linear; a deeply nested guard is short but severe. Each axis warns above its configured level, and reports an error only where a maximum is configured.",
|
|
21511
21709
|
codes: [
|
|
21512
21710
|
{ code: "NARRATIVE_COMPLEXITY", defaultSeverity: "warning", summary: "Narrative's cognitive band is above the configured level (default: above moderate)" },
|
|
@@ -21620,6 +21818,7 @@ function withoutWord(methodWords, dropIndex) {
|
|
|
21620
21818
|
}
|
|
21621
21819
|
var namingDisciplineRule = {
|
|
21622
21820
|
name: "naming-discipline",
|
|
21821
|
+
judges: "design",
|
|
21623
21822
|
description: "A name says what a thing is and never repeats itself: a component's head noun names its own block, generic role words say nothing, a method does not repeat its component's head noun, and a component with one method is not merely that method's name. Adapters and Portals are exempt from the repetition check, because a forwarder's method mirrors the command or route it exposes.",
|
|
21624
21823
|
codes: [
|
|
21625
21824
|
{ code: "MISLEADING_BLOCK_WORD", defaultSeverity: "warning", summary: "Component's head noun names a building block that is not its componentType" },
|
|
@@ -23730,9 +23929,27 @@ function measure(candidate) {
|
|
|
23730
23929
|
|
|
23731
23930
|
// src/core/rules/gate-identity.ts
|
|
23732
23931
|
var crypto3 = __toESM(require("crypto"));
|
|
23733
|
-
var GATE_ALGORITHM = "sha256+content+doctrine+inputs";
|
|
23932
|
+
var GATE_ALGORITHM = "sha256+content+doctrine+inputs+members";
|
|
23933
|
+
var byKey = (items, key) => [...items].sort((a, b) => compareOrdinal(key(a), key(b)));
|
|
23934
|
+
function ruleIdentities(rules) {
|
|
23935
|
+
return byKey(
|
|
23936
|
+
rules.map((r) => ({
|
|
23937
|
+
name: r.name,
|
|
23938
|
+
codes: byKey(r.codes.map((c) => ({ code: c.code, severity: c.defaultSeverity })), (c) => c.code)
|
|
23939
|
+
})),
|
|
23940
|
+
(r) => r.name
|
|
23941
|
+
);
|
|
23942
|
+
}
|
|
23943
|
+
function codeJudgedCodes(builtinRules) {
|
|
23944
|
+
return new Set(builtinRules.filter(judgesCode).flatMap((r) => r.codes.map((c) => c.code)));
|
|
23945
|
+
}
|
|
23946
|
+
function designRulesConfig(rules, codeCodes) {
|
|
23947
|
+
if (!rules) return null;
|
|
23948
|
+
const { conformance: _codeTuning, sddRuleSeverity, ...rest } = rules;
|
|
23949
|
+
const severities = Object.fromEntries(Object.entries(sddRuleSeverity ?? {}).filter(([code]) => !codeCodes.has(code)));
|
|
23950
|
+
return { ...rest, ...Object.keys(severities).length > 0 ? { sddRuleSeverity: severities } : {} };
|
|
23951
|
+
}
|
|
23734
23952
|
function doctrineIdentity(doctrine2, builtinRules, gate) {
|
|
23735
|
-
const byKey = (items, key) => [...items].sort((a, b) => compareOrdinal(key(a), key(b)));
|
|
23736
23953
|
return {
|
|
23737
23954
|
/**
|
|
23738
23955
|
* The BUILTIN rule set, by identity rather than by wairon version.
|
|
@@ -23749,20 +23966,23 @@ function doctrineIdentity(doctrine2, builtinRules, gate) {
|
|
|
23749
23966
|
* in the wairon version would catch it at the cost of churning every lock on
|
|
23750
23967
|
* every release — the trade this projection deliberately declines.
|
|
23751
23968
|
*/
|
|
23752
|
-
|
|
23753
|
-
|
|
23754
|
-
|
|
23755
|
-
|
|
23756
|
-
})),
|
|
23757
|
-
(r) => r.name
|
|
23758
|
-
),
|
|
23969
|
+
//
|
|
23970
|
+
// Only the rules that judge DESIGN: a code analyzer that gains a code or a
|
|
23971
|
+
// stricter check must never stale an approval (analyzer-upgrade-keeps-approval).
|
|
23972
|
+
builtinRules: ruleIdentities(builtinRules.filter((r) => !judgesCode(r))),
|
|
23759
23973
|
/**
|
|
23760
|
-
* Which profile GOVERNS, and the project's own rule tuning. Both
|
|
23761
|
-
* verdicts — a projectType switch changes the doctrine family
|
|
23762
|
-
* `rules` carries severity overrides, complexity caps
|
|
23974
|
+
* Which profile GOVERNS, and the project's own design rule tuning. Both
|
|
23975
|
+
* decide verdicts — a projectType switch changes the doctrine family
|
|
23976
|
+
* outright, and `rules` carries design severity overrides, complexity caps
|
|
23977
|
+
* and designDepth. Its code-conformance tuning is the analyzer's.
|
|
23763
23978
|
*/
|
|
23764
23979
|
projectType: gate.projectType ?? null,
|
|
23765
|
-
rulesConfig: gate.rules
|
|
23980
|
+
rulesConfig: designRulesConfig(gate.rules, codeJudgedCodes(builtinRules)),
|
|
23981
|
+
/**
|
|
23982
|
+
* What the project requires of the projects it contains (stage 5): a
|
|
23983
|
+
* changed requirement stales the requiring project's lock.
|
|
23984
|
+
*/
|
|
23985
|
+
composition: gate.composition ?? null,
|
|
23766
23986
|
packs: byKey(
|
|
23767
23987
|
doctrine2.packs.map((p) => ({ name: p.name, version: p.version ?? null, scope: p.scope })),
|
|
23768
23988
|
(p) => `${p.name}@${p.version ?? ""}#${p.scope}`
|
|
@@ -23783,11 +24003,12 @@ function doctrineIdentity(doctrine2, builtinRules, gate) {
|
|
|
23783
24003
|
)
|
|
23784
24004
|
};
|
|
23785
24005
|
}
|
|
23786
|
-
function computeGateIdentity(content, doctrine2, builtinRules, inputs, gate) {
|
|
24006
|
+
function computeGateIdentity(content, doctrine2, builtinRules, inputs, gate, members) {
|
|
23787
24007
|
const payload = {
|
|
23788
24008
|
content: content.digest,
|
|
23789
24009
|
doctrine: doctrineIdentity(doctrine2, builtinRules, gate),
|
|
23790
|
-
inputs: [...inputs].sort()
|
|
24010
|
+
inputs: [...inputs].sort(compareOrdinal),
|
|
24011
|
+
members: byKey(Object.entries(members), ([alias]) => alias).map(([alias, subject]) => ({ alias, subject }))
|
|
23791
24012
|
};
|
|
23792
24013
|
const digest2 = crypto3.createHash("sha256").update(canonicalize(payload)).digest("hex");
|
|
23793
24014
|
return { algorithm: GATE_ALGORITHM, digest: digest2 };
|
|
@@ -23898,6 +24119,9 @@ function memberOwning(owners, memberKeys, key) {
|
|
|
23898
24119
|
return best;
|
|
23899
24120
|
}
|
|
23900
24121
|
function validateProject(rulesOrOptions, projectType = "backend") {
|
|
24122
|
+
return runOwnersGate(rulesOrOptions, projectType).result;
|
|
24123
|
+
}
|
|
24124
|
+
function runOwnersGate(rulesOrOptions, projectType = "backend") {
|
|
23901
24125
|
let rules = rulesOrOptions;
|
|
23902
24126
|
let scopeSubsystem;
|
|
23903
24127
|
let extensions;
|
|
@@ -23948,7 +24172,7 @@ function validateProject(rulesOrOptions, projectType = "backend") {
|
|
|
23948
24172
|
if (!system) {
|
|
23949
24173
|
const pending = extensions.errors.length > 0 || extensions.selectionFailures.length > 0 ? " Extension packs were not checked yet either, and at least one problem is already known there \u2014 re-run once the L0 exists." : " Extension-pack configuration is not checked until the L0 exists.";
|
|
23950
24174
|
issues.push(issue("error", "MISSING_SYSTEM_SPEC", `L0 System specification (.system.yaml) is missing.${pending}`));
|
|
23951
|
-
return { valid: false, issues };
|
|
24175
|
+
return { result: { valid: false, issues }, codeModel };
|
|
23952
24176
|
}
|
|
23953
24177
|
if (scopeSubsystem) {
|
|
23954
24178
|
const scopeMatches = subsystems.some(
|
|
@@ -23966,7 +24190,7 @@ function validateProject(rulesOrOptions, projectType = "backend") {
|
|
|
23966
24190
|
scopeSubsystem
|
|
23967
24191
|
)
|
|
23968
24192
|
);
|
|
23969
|
-
return { valid: false, issues };
|
|
24193
|
+
return { result: { valid: false, issues }, codeModel };
|
|
23970
24194
|
}
|
|
23971
24195
|
}
|
|
23972
24196
|
for (const err of extensions.errors) {
|
|
@@ -24028,9 +24252,12 @@ function validateProject(rulesOrOptions, projectType = "backend") {
|
|
|
24028
24252
|
const count = pinnedExternals.length;
|
|
24029
24253
|
const hint = count > 0 ? `${count} external${count === 1 ? " was" : "s were"} judged against ${count === 1 ? "its pin" : "their pins"} alone; \`wairon validate --family\` composes ${count === 1 ? "it" : "them"} against the live producer${count === 1 ? "" : "s"}.` : void 0;
|
|
24030
24254
|
return {
|
|
24031
|
-
|
|
24032
|
-
|
|
24033
|
-
|
|
24255
|
+
result: {
|
|
24256
|
+
valid: issues.every((i) => i.severity !== "error"),
|
|
24257
|
+
issues,
|
|
24258
|
+
...hint ? { hint } : {}
|
|
24259
|
+
},
|
|
24260
|
+
codeModel
|
|
24034
24261
|
};
|
|
24035
24262
|
} finally {
|
|
24036
24263
|
statusBearing.forEach((s, i) => {
|
|
@@ -24075,17 +24302,36 @@ function builtinProjectKinds() {
|
|
|
24075
24302
|
return [...PROJECT_KINDS];
|
|
24076
24303
|
}
|
|
24077
24304
|
function computeGateStateId() {
|
|
24078
|
-
|
|
24305
|
+
scanAllSpecs({ recursive: true });
|
|
24306
|
+
const content = computeOwnStateId();
|
|
24079
24307
|
const extensions = loadProjectExtensions();
|
|
24080
24308
|
let gate = {};
|
|
24081
24309
|
try {
|
|
24082
24310
|
const config = loadProjectConfig();
|
|
24083
|
-
if (config) gate = { projectType: config.projectType, rules: config.rules };
|
|
24311
|
+
if (config) gate = { projectType: config.projectType, rules: config.rules, composition: config.composition ?? null };
|
|
24084
24312
|
} catch {
|
|
24085
24313
|
}
|
|
24086
24314
|
const inputs = consumedContractInputs();
|
|
24315
|
+
const members = directMemberSubjects();
|
|
24087
24316
|
registerBuiltinRules();
|
|
24088
|
-
return computeGateIdentity(content, extensions, ruleSequence(), inputs, gate);
|
|
24317
|
+
return computeGateIdentity(content, extensions, ruleSequence(), inputs, gate, members);
|
|
24318
|
+
}
|
|
24319
|
+
var NEVER_SUBJECT = "never";
|
|
24320
|
+
function directMemberSubjects() {
|
|
24321
|
+
const family = graph();
|
|
24322
|
+
const subjects = {};
|
|
24323
|
+
for (const node of family.nodes) {
|
|
24324
|
+
if (node.parent !== "" || node.mountAlias === void 0) continue;
|
|
24325
|
+
const record = approvalRecord(node.directory);
|
|
24326
|
+
subjects[node.mountAlias] = record ? `${record.stateId.algorithm}:${record.stateId.digest}` : NEVER_SUBJECT;
|
|
24327
|
+
}
|
|
24328
|
+
for (const problem of family.problems) {
|
|
24329
|
+
if (problem.kind === "member-absent" && problem.projects[0] === "" && problem.id) subjects[problem.id] = NEVER_SUBJECT;
|
|
24330
|
+
}
|
|
24331
|
+
return subjects;
|
|
24332
|
+
}
|
|
24333
|
+
function familyApprovals(depth) {
|
|
24334
|
+
return projectApprovals(depth);
|
|
24089
24335
|
}
|
|
24090
24336
|
|
|
24091
24337
|
// src/core/diagram.ts
|
|
@@ -24429,8 +24675,8 @@ function generateSequenceDiagram(componentId, methodName, options) {
|
|
|
24429
24675
|
const steps = [...methodImpl.narrative].sort((a, b) => a.stepNumber - b.stepNumber);
|
|
24430
24676
|
for (const step of steps) {
|
|
24431
24677
|
for (const par of parallelArms) {
|
|
24432
|
-
const
|
|
24433
|
-
if (
|
|
24678
|
+
const sep8 = par.sepByStep.get(step.stepNumber);
|
|
24679
|
+
if (sep8 !== void 0) lines.push(` and ${escapeLabel(sep8)}`);
|
|
24434
24680
|
}
|
|
24435
24681
|
switch (step.type) {
|
|
24436
24682
|
case "local":
|
|
@@ -25005,10 +25251,10 @@ function settle(owner, level, candidates, problems) {
|
|
|
25005
25251
|
detail: `"${name}" is bound to ${targets.length} different targets (${targets.join(", ")}) by ${explicit.length ? "explicit entries" : "wildcard re-exports"}; it is left out of the table`
|
|
25006
25252
|
});
|
|
25007
25253
|
}
|
|
25008
|
-
const
|
|
25009
|
-
for (const e of entries)
|
|
25254
|
+
const byKey2 = /* @__PURE__ */ new Map();
|
|
25255
|
+
for (const e of entries) byKey2.set(nameKey(e.publicName), [...byKey2.get(nameKey(e.publicName)) ?? [], e]);
|
|
25010
25256
|
const clashing = /* @__PURE__ */ new Set();
|
|
25011
|
-
for (const group of
|
|
25257
|
+
for (const group of byKey2.values()) {
|
|
25012
25258
|
const targets = [...new Set(group.map((e) => exportTargetKey(e)))];
|
|
25013
25259
|
if (group.length < 2 || targets.length < 2) continue;
|
|
25014
25260
|
group.forEach((e) => clashing.add(e));
|
|
@@ -25473,9 +25719,6 @@ function inspectChainedRoots(rootDir = getProjectRoot()) {
|
|
|
25473
25719
|
walk2(root, "", /* @__PURE__ */ new Set([chainDirKey(root)]), 0);
|
|
25474
25720
|
return inspection;
|
|
25475
25721
|
}
|
|
25476
|
-
function listChainedRoots(rootDir = getProjectRoot()) {
|
|
25477
|
-
return inspectChainedRoots(rootDir).roots;
|
|
25478
|
-
}
|
|
25479
25722
|
function mapSpecReferences(kind, spec, map) {
|
|
25480
25723
|
switch (kind) {
|
|
25481
25724
|
case "subsystem": {
|
|
@@ -25734,10 +25977,10 @@ function keyedSystem(system, key, subsystems) {
|
|
|
25734
25977
|
};
|
|
25735
25978
|
}
|
|
25736
25979
|
function resolveFamilyTables(raws) {
|
|
25737
|
-
const
|
|
25980
|
+
const byKey2 = new Map(raws.map((r) => [r.record.namespace, r]));
|
|
25738
25981
|
const done = /* @__PURE__ */ new Set();
|
|
25739
25982
|
const resolving = /* @__PURE__ */ new Set();
|
|
25740
|
-
const
|
|
25983
|
+
const resolve27 = (raw) => {
|
|
25741
25984
|
const key = raw.record.namespace;
|
|
25742
25985
|
if (done.has(key)) return raw.record.exports;
|
|
25743
25986
|
if (resolving.has(key)) return null;
|
|
@@ -25751,8 +25994,8 @@ function resolveFamilyTables(raws) {
|
|
|
25751
25994
|
const source = e.from ?? e.subsystem;
|
|
25752
25995
|
const producerKey = source !== void 0 ? raw.record.aliases.get(source) : void 0;
|
|
25753
25996
|
if (producerKey === void 0 || producerTables.has(producerKey)) continue;
|
|
25754
|
-
const producer =
|
|
25755
|
-
producerTables.set(producerKey, producer ?
|
|
25997
|
+
const producer = byKey2.get(producerKey);
|
|
25998
|
+
producerTables.set(producerKey, producer ? resolve27(producer) : null);
|
|
25756
25999
|
audienceOf.set(producerKey, "project");
|
|
25757
26000
|
}
|
|
25758
26001
|
raw.record.subsystemExports = subsystemTables;
|
|
@@ -25768,7 +26011,7 @@ function resolveFamilyTables(raws) {
|
|
|
25768
26011
|
resolving.delete(key);
|
|
25769
26012
|
}
|
|
25770
26013
|
};
|
|
25771
|
-
raws.forEach((raw) =>
|
|
26014
|
+
raws.forEach((raw) => resolve27(raw));
|
|
25772
26015
|
}
|
|
25773
26016
|
function bindAuthored(raw, raws, readables, authored) {
|
|
25774
26017
|
const key = raw.record.namespace;
|
|
@@ -29384,9 +29627,7 @@ function consumedSurfaceInputsAt(rootDir) {
|
|
|
29384
29627
|
return keys;
|
|
29385
29628
|
}
|
|
29386
29629
|
function consumedContractInputs() {
|
|
29387
|
-
|
|
29388
|
-
const roots = [root, ...listChainedRoots(root).map((rel2) => path19.join(root, rel2))];
|
|
29389
|
-
return roots.flatMap(consumedSurfaceInputsAt);
|
|
29630
|
+
return consumedSurfaceInputsAt(getProjectRoot());
|
|
29390
29631
|
}
|
|
29391
29632
|
function readLockState(current2) {
|
|
29392
29633
|
const record = readLockRecord();
|
|
@@ -31071,6 +31312,16 @@ function buildVariantGuidance(comps, allComponents, variantsById) {
|
|
|
31071
31312
|
return composeVariantGuidance(comps, allComponents, variantsById);
|
|
31072
31313
|
}
|
|
31073
31314
|
function resolveAgentTopology() {
|
|
31315
|
+
return resolveLayer(true);
|
|
31316
|
+
}
|
|
31317
|
+
function memberAgentIds(alias, memberDir) {
|
|
31318
|
+
try {
|
|
31319
|
+
return runWithProjectRoot(memberDir, () => resolveLayer(false).map((r) => `${alias}::${r.id}`));
|
|
31320
|
+
} catch {
|
|
31321
|
+
return [];
|
|
31322
|
+
}
|
|
31323
|
+
}
|
|
31324
|
+
function resolveLayer(delegate) {
|
|
31074
31325
|
projectFilesCache.clear();
|
|
31075
31326
|
const system = loadSystemSpec();
|
|
31076
31327
|
if (!system) return [];
|
|
@@ -31126,6 +31377,8 @@ function resolveAgentTopology() {
|
|
|
31126
31377
|
writePaths: [mountSpecPath],
|
|
31127
31378
|
tags: ["owner", "subproject", "delegate", "sdd"],
|
|
31128
31379
|
dependencies: [],
|
|
31380
|
+
// The member's own agents BY REFERENCE, never copied into this layer.
|
|
31381
|
+
...delegate ? { delegatesTo: memberAgentIds(sub.id, path26.resolve(getProjectRoot(), sub.projectPath)) } : {},
|
|
31129
31382
|
status: "active",
|
|
31130
31383
|
targets: activeTargets,
|
|
31131
31384
|
createdAt: sub.createdAt,
|
|
@@ -31267,6 +31520,11 @@ var UnknownAgentError = class extends WaironError {
|
|
|
31267
31520
|
}
|
|
31268
31521
|
};
|
|
31269
31522
|
function composeAgentBrief(agentId) {
|
|
31523
|
+
const hop = memberHop(agentId);
|
|
31524
|
+
if (hop) {
|
|
31525
|
+
const brief = runWithProjectRoot(hop.directory, () => composeAgentBrief(hop.rest));
|
|
31526
|
+
return throughMount(brief, hop.alias, hop.relative);
|
|
31527
|
+
}
|
|
31270
31528
|
const records = resolveAgentTopology();
|
|
31271
31529
|
const record = records.find((r) => r.id === agentId);
|
|
31272
31530
|
if (!record) {
|
|
@@ -31311,6 +31569,25 @@ ${guidance.trim()}
|
|
|
31311
31569
|
budget
|
|
31312
31570
|
};
|
|
31313
31571
|
}
|
|
31572
|
+
function memberHop(agentId) {
|
|
31573
|
+
const at = agentId.indexOf("::");
|
|
31574
|
+
if (at < 0) return null;
|
|
31575
|
+
const alias = agentId.slice(0, at);
|
|
31576
|
+
const member = graph().nodes.find((n) => n.parent === "" && n.mountAlias === alias);
|
|
31577
|
+
if (!member) return null;
|
|
31578
|
+
const relative12 = path26.relative(getProjectRoot(), member.directory).split(path26.sep).join("/");
|
|
31579
|
+
return { alias, rest: agentId.slice(at + 2), directory: member.directory, relative: relative12 };
|
|
31580
|
+
}
|
|
31581
|
+
function throughMount(brief, alias, relative12) {
|
|
31582
|
+
const under = (p) => path26.posix.join(relative12, p);
|
|
31583
|
+
return {
|
|
31584
|
+
...brief,
|
|
31585
|
+
agentId: `${alias}::${brief.agentId}`,
|
|
31586
|
+
ownedPaths: brief.ownedPaths.map(under),
|
|
31587
|
+
...brief.readPaths ? { readPaths: brief.readPaths.map(under) } : {},
|
|
31588
|
+
root: brief.root ? under(brief.root) : relative12
|
|
31589
|
+
};
|
|
31590
|
+
}
|
|
31314
31591
|
function loadRegistry() {
|
|
31315
31592
|
assertProjectInitialized();
|
|
31316
31593
|
if (!pathExists(AI_PATHS.specsSystem())) return createEmptyRegistry();
|
|
@@ -31904,48 +32181,22 @@ function settledSpecPaths(root = getProjectRoot()) {
|
|
|
31904
32181
|
const diff = diffAgainstApproval(root);
|
|
31905
32182
|
return diff ? new Set(diff.unchangedPaths) : null;
|
|
31906
32183
|
}
|
|
31907
|
-
function
|
|
31908
|
-
return `${stateId.algorithm}:${stateId.digest}`;
|
|
31909
|
-
}
|
|
31910
|
-
function currentChildPins(mounts, root = getProjectRoot()) {
|
|
31911
|
-
const pins = {};
|
|
31912
|
-
for (const mount of mounts) {
|
|
31913
|
-
if (!mount.projectPath || mount.id.includes("::")) continue;
|
|
31914
|
-
const child = readLockRecordAt(path32.resolve(root, mount.projectPath));
|
|
31915
|
-
if (child) pins[mount.id] = pinOf(child.stateId);
|
|
31916
|
-
}
|
|
31917
|
-
return pins;
|
|
31918
|
-
}
|
|
31919
|
-
function movedChildren(mounts, root = getProjectRoot()) {
|
|
31920
|
-
const pinned = approvalRecord(root)?.children;
|
|
31921
|
-
if (!pinned) return [];
|
|
31922
|
-
const now = currentChildPins(mounts, root);
|
|
31923
|
-
const moved = [];
|
|
31924
|
-
for (const [id, was] of Object.entries(pinned)) {
|
|
31925
|
-
const current2 = now[id] ?? null;
|
|
31926
|
-
if (current2 !== was) moved.push({ id, pinned: was, now: current2 });
|
|
31927
|
-
}
|
|
31928
|
-
return moved;
|
|
31929
|
-
}
|
|
31930
|
-
function memberMounts(root = getProjectRoot()) {
|
|
31931
|
-
return graph().nodes.filter((n) => n.parent === "" && n.mountAlias !== void 0).map((n) => ({ id: n.mountAlias, projectPath: path32.relative(root, n.directory).split(path32.sep).join("/") }));
|
|
31932
|
-
}
|
|
31933
|
-
function approvalVerdict() {
|
|
32184
|
+
function approvalVerdict(approvals) {
|
|
31934
32185
|
const quiet = { text: "", drifted: false };
|
|
31935
32186
|
try {
|
|
31936
32187
|
const approval = approvalRecord();
|
|
31937
32188
|
if (!approval) return quiet;
|
|
31938
32189
|
const by = describeApprover(approval.lockedBy);
|
|
31939
|
-
const
|
|
31940
|
-
const
|
|
31941
|
-
|
|
32190
|
+
const notes = memberNotes(approvals ?? []);
|
|
32191
|
+
const pinDrift = notes.drift;
|
|
32192
|
+
const childNote = notes.text;
|
|
31942
32193
|
const diff = diffAgainstApproval();
|
|
31943
32194
|
if (!diff) {
|
|
31944
32195
|
return {
|
|
31945
32196
|
text: `
|
|
31946
32197
|
Approved: ${approval.lockedAt} by ${by} \u2014 this lock predates per-spec approval, so drift is only visible at whole-tree level. Re-lock to record it.${childNote}
|
|
31947
32198
|
`,
|
|
31948
|
-
drifted:
|
|
32199
|
+
drifted: pinDrift
|
|
31949
32200
|
};
|
|
31950
32201
|
}
|
|
31951
32202
|
if (diffSize(diff) === 0) {
|
|
@@ -31953,7 +32204,7 @@ Approved: ${approval.lockedAt} by ${by} \u2014 this lock predates per-spec appro
|
|
|
31953
32204
|
text: `
|
|
31954
32205
|
Approved: ${approval.lockedAt} by ${by} \u2014 no spec has changed since.${childNote}
|
|
31955
32206
|
`,
|
|
31956
|
-
drifted:
|
|
32207
|
+
drifted: pinDrift
|
|
31957
32208
|
};
|
|
31958
32209
|
}
|
|
31959
32210
|
const parts = [];
|
|
@@ -31976,6 +32227,28 @@ ${headline} \u2014 approved ${approval.lockedAt} by ${by}:
|
|
|
31976
32227
|
return quiet;
|
|
31977
32228
|
}
|
|
31978
32229
|
}
|
|
32230
|
+
var GATE_UPGRADE_NOTE = "the gate identity gained inputs in stage 5 (members' composition subjects, `composition`; code conformance moved beside the claim)";
|
|
32231
|
+
function memberNotes(approvals) {
|
|
32232
|
+
const own = approvals.find((a) => a.key === "");
|
|
32233
|
+
const direct = approvals.filter((a) => a.parent === "");
|
|
32234
|
+
const moved = direct.filter((a) => a.pinned === "moved");
|
|
32235
|
+
const unapproved = direct.filter((a) => a.state !== "approved");
|
|
32236
|
+
const lines = [];
|
|
32237
|
+
if (own?.upgraded) {
|
|
32238
|
+
lines.push(`This approval was taken under an earlier gate identity \u2014 ${GATE_UPGRADE_NOTE}. Re-lock once.`);
|
|
32239
|
+
}
|
|
32240
|
+
if (moved.length) {
|
|
32241
|
+
lines.push(`${moved.length} member pin(s) moved since approval: ${moved.map((a) => a.alias ?? a.key).join(", ")} \u2014 re-lock to approve them.`);
|
|
32242
|
+
}
|
|
32243
|
+
if (unapproved.length) {
|
|
32244
|
+
lines.push(`Member(s) not approved at their own root: ${unapproved.map((a) => `${a.alias ?? a.key} (${a.state}${a.upgraded ? ", re-lock once" : ""})`).join(", ")}.`);
|
|
32245
|
+
}
|
|
32246
|
+
return {
|
|
32247
|
+
text: lines.map((l) => `
|
|
32248
|
+
${l}`).join(""),
|
|
32249
|
+
drift: !!own?.upgraded || moved.length > 0
|
|
32250
|
+
};
|
|
32251
|
+
}
|
|
31979
32252
|
|
|
31980
32253
|
// src/core/approver.ts
|
|
31981
32254
|
var import_child_process = require("child_process");
|
|
@@ -32309,6 +32582,11 @@ function syncContextFiles() {
|
|
|
32309
32582
|
|
|
32310
32583
|
// src/core/status.ts
|
|
32311
32584
|
var path34 = __toESM(require("path"));
|
|
32585
|
+
function approvalTag(entry) {
|
|
32586
|
+
if (!entry) return "";
|
|
32587
|
+
const state = entry.upgraded ? `${entry.state}, re-lock once` : entry.state;
|
|
32588
|
+
return entry.pinned ? ` [${state} \xB7 pin ${entry.pinned}]` : ` [${state}]`;
|
|
32589
|
+
}
|
|
32312
32590
|
var asWritten = (text3) => text3;
|
|
32313
32591
|
function fillDecor(decor) {
|
|
32314
32592
|
return {
|
|
@@ -32469,12 +32747,21 @@ function getStatusReport(options = {}, decor) {
|
|
|
32469
32747
|
const isLast = ownSubs.length + j === total - 1;
|
|
32470
32748
|
const form = member.mountForm === "mount" ? mark.draft(" [mount form]") : "";
|
|
32471
32749
|
const label2 = `[Project] ${member.mountAlias ?? member.namespace} (${member.id ?? "no id"})`;
|
|
32472
|
-
|
|
32750
|
+
const approval = approvalTag(options.approvals?.find((a) => a.key === member.namespace));
|
|
32751
|
+
output += `${mark.structure(indent + (isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "))}${mark.layer("project", label2)}${form}${approval}
|
|
32473
32752
|
`;
|
|
32474
32753
|
renderProject(member.namespace, indent + (isLast ? " " : "\u2502 "));
|
|
32475
32754
|
});
|
|
32476
32755
|
};
|
|
32477
32756
|
renderProject("", "");
|
|
32757
|
+
for (const absent of (options.approvals ?? []).filter((a) => a.key !== "" && !family.nodes.some((n) => n.namespace === a.key))) {
|
|
32758
|
+
output += `${mark.structure(" ")}${mark.missing(`[Project] ${absent.alias ?? absent.key} (no project on disk)`)}${approvalTag(absent)}
|
|
32759
|
+
`;
|
|
32760
|
+
}
|
|
32761
|
+
const own = options.approvals?.find((a) => a.key === "");
|
|
32762
|
+
const hasMembers = (options.approvals ?? []).some((a) => a.key !== "");
|
|
32763
|
+
if (own && (own.state !== "never" || hasMembers)) output += `${mark.layer("system", "Approval:")} this project is ${own.state}${own.upgraded ? " (locked under the pre-stage-5 gate identity \u2014 re-lock once)" : ""}
|
|
32764
|
+
`;
|
|
32478
32765
|
return { text: output, failed: false };
|
|
32479
32766
|
}
|
|
32480
32767
|
|
|
@@ -32787,7 +33074,7 @@ function readResource(resourceId) {
|
|
|
32787
33074
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
32788
33075
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
32789
33076
|
var import_zod11 = require("zod");
|
|
32790
|
-
var
|
|
33077
|
+
var import_types5 = require("@modelcontextprotocol/sdk/types.js");
|
|
32791
33078
|
var path37 = __toESM(require("path"));
|
|
32792
33079
|
var crypto5 = __toESM(require("crypto"));
|
|
32793
33080
|
|
|
@@ -33854,7 +34141,7 @@ function renderAgentBriefMarkdown(brief) {
|
|
|
33854
34141
|
}
|
|
33855
34142
|
function registerSkillResources(server, listChanged) {
|
|
33856
34143
|
server.server.registerCapabilities({ resources: listChanged ? { listChanged: true } : {} });
|
|
33857
|
-
server.server.setRequestHandler(
|
|
34144
|
+
server.server.setRequestHandler(import_types5.ListResourcesRequestSchema, () => ({
|
|
33858
34145
|
resources: [
|
|
33859
34146
|
...listResources().map((d) => ({
|
|
33860
34147
|
uri: d.resourceUri,
|
|
@@ -33865,14 +34152,14 @@ function registerSkillResources(server, listChanged) {
|
|
|
33865
34152
|
...listAgentBriefResources()
|
|
33866
34153
|
]
|
|
33867
34154
|
}));
|
|
33868
|
-
server.server.setRequestHandler(
|
|
34155
|
+
server.server.setRequestHandler(import_types5.ReadResourceRequestSchema, (request) => {
|
|
33869
34156
|
const uri = request.params.uri;
|
|
33870
34157
|
if (uri.startsWith(AGENT_BRIEF_SCHEME)) {
|
|
33871
34158
|
try {
|
|
33872
34159
|
const brief = composeAgentBrief(uri.slice(AGENT_BRIEF_SCHEME.length));
|
|
33873
34160
|
return { contents: [{ uri, mimeType: SKILL_RESOURCE_MIME, text: renderAgentBriefMarkdown(brief) }] };
|
|
33874
34161
|
} catch (e) {
|
|
33875
|
-
throw new
|
|
34162
|
+
throw new import_types5.McpError(import_types5.ErrorCode.InvalidParams, e instanceof Error ? e.message : String(e));
|
|
33876
34163
|
}
|
|
33877
34164
|
}
|
|
33878
34165
|
try {
|
|
@@ -33880,20 +34167,20 @@ function registerSkillResources(server, listChanged) {
|
|
|
33880
34167
|
return { contents: [{ uri, mimeType: SKILL_RESOURCE_MIME, text: content }] };
|
|
33881
34168
|
} catch (e) {
|
|
33882
34169
|
const message = e instanceof Error ? e.message : String(e);
|
|
33883
|
-
throw new
|
|
34170
|
+
throw new import_types5.McpError(import_types5.ErrorCode.InvalidParams, message);
|
|
33884
34171
|
}
|
|
33885
34172
|
});
|
|
33886
34173
|
}
|
|
33887
34174
|
function registerSkillPrompts(server, listChanged) {
|
|
33888
34175
|
server.server.registerCapabilities({ prompts: listChanged ? { listChanged: true } : {} });
|
|
33889
|
-
server.server.setRequestHandler(
|
|
34176
|
+
server.server.setRequestHandler(import_types5.ListPromptsRequestSchema, () => ({
|
|
33890
34177
|
prompts: listResources().map((d) => ({
|
|
33891
34178
|
name: d.id,
|
|
33892
34179
|
title: d.name,
|
|
33893
34180
|
description: d.description
|
|
33894
34181
|
}))
|
|
33895
34182
|
}));
|
|
33896
|
-
server.server.setRequestHandler(
|
|
34183
|
+
server.server.setRequestHandler(import_types5.GetPromptRequestSchema, (request) => {
|
|
33897
34184
|
const name = request.params.name;
|
|
33898
34185
|
try {
|
|
33899
34186
|
const content = readResource(name);
|
|
@@ -33902,7 +34189,7 @@ function registerSkillPrompts(server, listChanged) {
|
|
|
33902
34189
|
messages: [{ role: "user", content: { type: "text", text: content } }]
|
|
33903
34190
|
};
|
|
33904
34191
|
} catch (e) {
|
|
33905
|
-
throw new
|
|
34192
|
+
throw new import_types5.McpError(import_types5.ErrorCode.InvalidParams, e instanceof Error ? e.message : String(e));
|
|
33906
34193
|
}
|
|
33907
34194
|
});
|
|
33908
34195
|
}
|
|
@@ -34910,12 +35197,19 @@ ${testsBlock}` : ""}`, deletion);
|
|
|
34910
35197
|
},
|
|
34911
35198
|
({ subsystem, recursive }) => {
|
|
34912
35199
|
try {
|
|
35200
|
+
let approvals;
|
|
35201
|
+
try {
|
|
35202
|
+
approvals = familyApprovals(recursive === false ? 0 : void 0);
|
|
35203
|
+
} catch {
|
|
35204
|
+
approvals = void 0;
|
|
35205
|
+
}
|
|
34913
35206
|
const report = getStatusReport({
|
|
34914
35207
|
subsystem,
|
|
34915
|
-
recursive: recursive ?? true
|
|
35208
|
+
recursive: recursive ?? true,
|
|
35209
|
+
approvals
|
|
34916
35210
|
});
|
|
34917
35211
|
const family = statusFamilyContext();
|
|
34918
|
-
const verdict = approvalVerdict();
|
|
35212
|
+
const verdict = approvalVerdict(approvals);
|
|
34919
35213
|
return text2(`${family}${report.text}${verdict.text}`);
|
|
34920
35214
|
} catch (e) {
|
|
34921
35215
|
return errText(String(e));
|
|
@@ -35253,6 +35547,7 @@ function loadProjectConfig3() {
|
|
|
35253
35547
|
complexityLevel,
|
|
35254
35548
|
composeAgentBrief,
|
|
35255
35549
|
composeRuleSequence,
|
|
35550
|
+
computeOwnStateId,
|
|
35256
35551
|
computeStateId,
|
|
35257
35552
|
computeStateIdAt,
|
|
35258
35553
|
conceptNoun,
|
|
@@ -35263,7 +35558,6 @@ function loadProjectConfig3() {
|
|
|
35263
35558
|
createEmptyTopologyConfig,
|
|
35264
35559
|
createMember,
|
|
35265
35560
|
createProjectConfig,
|
|
35266
|
-
currentChildPins,
|
|
35267
35561
|
currentRootBinding,
|
|
35268
35562
|
declareExternal,
|
|
35269
35563
|
declaredExternals,
|
|
@@ -35343,6 +35637,7 @@ function loadProjectConfig3() {
|
|
|
35343
35637
|
isProjectInitialized,
|
|
35344
35638
|
isProvidedBy,
|
|
35345
35639
|
isRetired,
|
|
35640
|
+
judgesCode,
|
|
35346
35641
|
keyIn,
|
|
35347
35642
|
landReference,
|
|
35348
35643
|
listDirectChainedSubprojects,
|
|
@@ -35385,7 +35680,6 @@ function loadProjectConfig3() {
|
|
|
35385
35680
|
moveMember,
|
|
35386
35681
|
moveMethods,
|
|
35387
35682
|
moveMountToMembers,
|
|
35388
|
-
movedChildren,
|
|
35389
35683
|
nameKey,
|
|
35390
35684
|
narrativeStepForeignFields,
|
|
35391
35685
|
normalizeReferences,
|