@wairon/cli 5.1.1-dev.23 → 5.1.1-dev.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +833 -509
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +834 -508
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var init_defaults = __esm({
|
|
|
65
65
|
copilot: ".github/prompts",
|
|
66
66
|
codex: ".codex/agents"
|
|
67
67
|
};
|
|
68
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
68
|
+
WAIRON_VERSION = "5.1.1-dev.25";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
SUPPORTED_ALIASES = ["wai"];
|
|
71
71
|
SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -1710,7 +1710,7 @@ var init_specs = __esm({
|
|
|
1710
1710
|
* entity's write methods must carry (UNASSERTED_INVARIANT otherwise).
|
|
1711
1711
|
*/
|
|
1712
1712
|
assertsInvariants: import_zod7.z.array(import_zod7.z.string()).optional(),
|
|
1713
|
-
// --- flow config (per type; validated by
|
|
1713
|
+
// --- flow config (per type; validated by narrative-step-config) -----------
|
|
1714
1714
|
condition: import_zod7.z.string().optional(),
|
|
1715
1715
|
// branch; loop (while/doWhile)
|
|
1716
1716
|
onTrueStep: import_zod7.z.number().int().positive().optional(),
|
|
@@ -1811,7 +1811,7 @@ var init_specs = __esm({
|
|
|
1811
1811
|
methods: import_zod7.z.array(MethodImplementationSchema).default([]),
|
|
1812
1812
|
/** Spec-level narrative detail default for all methods (each may override). */
|
|
1813
1813
|
detail: NarrativeDetailSchema.optional(),
|
|
1814
|
-
/** Spec-level
|
|
1814
|
+
/** Spec-level conformance tier default (each method may override). */
|
|
1815
1815
|
conformance: ConformanceTierSchema.optional(),
|
|
1816
1816
|
/** Per-spec lint suppressions (see LintConfigSchema). */
|
|
1817
1817
|
lint: LintConfigSchema.optional(),
|
|
@@ -2242,6 +2242,149 @@ var init_step_graph = __esm({
|
|
|
2242
2242
|
}
|
|
2243
2243
|
});
|
|
2244
2244
|
|
|
2245
|
+
// src/models/step-config.ts
|
|
2246
|
+
function flowConfigOn(step) {
|
|
2247
|
+
return FLOW_FIELDS.filter((f) => {
|
|
2248
|
+
const v = step[f];
|
|
2249
|
+
if (v === void 0) return false;
|
|
2250
|
+
if (Array.isArray(v) && v.length === 0) return false;
|
|
2251
|
+
return true;
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
function stepConfigVerdict(method2) {
|
|
2255
|
+
const steps = method2.narrative ?? [];
|
|
2256
|
+
const problems = [];
|
|
2257
|
+
let sound = true;
|
|
2258
|
+
const malformed = (stepNumber, detail) => {
|
|
2259
|
+
sound = false;
|
|
2260
|
+
problems.push({ stepNumber, code: "MALFORMED_FLOW_STEP", detail });
|
|
2261
|
+
};
|
|
2262
|
+
const labelled = /* @__PURE__ */ new Map();
|
|
2263
|
+
for (const step of steps) {
|
|
2264
|
+
const label = step.label;
|
|
2265
|
+
if (!label) continue;
|
|
2266
|
+
const first = labelled.get(label);
|
|
2267
|
+
if (first !== void 0) {
|
|
2268
|
+
problems.push({
|
|
2269
|
+
stepNumber: step.stepNumber,
|
|
2270
|
+
code: "DUPLICATE_STEP_LABEL",
|
|
2271
|
+
detail: `steps ${first} and ${step.stepNumber} share the label "${label}". A label anchors one step for later deltas to address; two steps holding it makes every symbolic reference ambiguous and blocks all further edits to this implementation. Rename one.`
|
|
2272
|
+
});
|
|
2273
|
+
} else {
|
|
2274
|
+
labelled.set(label, step.stepNumber);
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
const byNum = /* @__PURE__ */ new Map();
|
|
2278
|
+
for (const s of steps) {
|
|
2279
|
+
if (byNum.has(s.stepNumber)) malformed(s.stepNumber, `duplicate stepNumber ${s.stepNumber} \u2014 jump targets would be ambiguous.`);
|
|
2280
|
+
byNum.set(s.stepNumber, s);
|
|
2281
|
+
}
|
|
2282
|
+
const nums = [...byNum.keys()].sort((a, b) => a - b);
|
|
2283
|
+
const indexOf = new Map(nums.map((n, i) => [n, i]));
|
|
2284
|
+
const nextOf = (n) => {
|
|
2285
|
+
const i = indexOf.get(n);
|
|
2286
|
+
return i !== void 0 && i + 1 < nums.length ? nums[i + 1] : void 0;
|
|
2287
|
+
};
|
|
2288
|
+
for (const s of steps) {
|
|
2289
|
+
if (s.detach && s.type !== "call" && s.type !== "dispatch") {
|
|
2290
|
+
malformed(s.stepNumber, `step ${s.stepNumber} (${s.type}) carries "detach" \u2014 fire-and-forget applies to call/dispatch steps only.`);
|
|
2291
|
+
}
|
|
2292
|
+
switch (s.type) {
|
|
2293
|
+
case "local":
|
|
2294
|
+
case "call":
|
|
2295
|
+
case "register":
|
|
2296
|
+
case "dispatch": {
|
|
2297
|
+
const extra = flowConfigOn(s);
|
|
2298
|
+
if (extra.length) malformed(s.stepNumber, `step ${s.stepNumber} (${s.type}) carries flow config (${extra.join(", ")}) \u2014 use a flow step type instead.`);
|
|
2299
|
+
break;
|
|
2300
|
+
}
|
|
2301
|
+
case "branch":
|
|
2302
|
+
if (!s.condition) malformed(s.stepNumber, `branch step ${s.stepNumber} requires "condition".`);
|
|
2303
|
+
if (s.onFalseStep === void 0) malformed(s.stepNumber, `branch step ${s.stepNumber} requires "onFalseStep" (true continues at onTrueStep or the next step).`);
|
|
2304
|
+
break;
|
|
2305
|
+
case "switch":
|
|
2306
|
+
if (!s.cases || !s.cases.length) malformed(s.stepNumber, `switch step ${s.stepNumber} requires non-empty "cases".`);
|
|
2307
|
+
break;
|
|
2308
|
+
case "loop": {
|
|
2309
|
+
if (s.endStep === void 0) malformed(s.stepNumber, `loop step ${s.stepNumber} requires "endStep" (last step of the body).`);
|
|
2310
|
+
else if (s.endStep <= s.stepNumber) malformed(s.stepNumber, `loop step ${s.stepNumber} "endStep" (${s.endStep}) must lie beyond the header.`);
|
|
2311
|
+
const kind = s.loopKind ?? (s.over ? "forEach" : "while");
|
|
2312
|
+
if ((kind === "while" || kind === "doWhile") && !s.condition) malformed(s.stepNumber, `${kind} loop step ${s.stepNumber} requires "condition".`);
|
|
2313
|
+
if ((kind === "forEach" || kind === "for") && !s.over) malformed(s.stepNumber, `${kind} loop step ${s.stepNumber} requires "over" (the iteration source).`);
|
|
2314
|
+
break;
|
|
2315
|
+
}
|
|
2316
|
+
case "try":
|
|
2317
|
+
if (s.endStep === void 0) malformed(s.stepNumber, `try step ${s.stepNumber} requires "endStep" (last step of the guarded body).`);
|
|
2318
|
+
else if (s.endStep <= s.stepNumber) malformed(s.stepNumber, `try step ${s.stepNumber} "endStep" (${s.endStep}) must lie beyond the header.`);
|
|
2319
|
+
if ((!s.catches || !s.catches.length) && s.finallyStep === void 0) malformed(s.stepNumber, `try step ${s.stepNumber} requires "catches" and/or "finallyStep" \u2014 a guard that handles nothing guards nothing.`);
|
|
2320
|
+
break;
|
|
2321
|
+
case "parallel": {
|
|
2322
|
+
if (s.endStep === void 0) malformed(s.stepNumber, `parallel step ${s.stepNumber} requires "endStep" (last step of the fan-out body).`);
|
|
2323
|
+
else if (s.endStep <= s.stepNumber) malformed(s.stepNumber, `parallel step ${s.stepNumber} "endStep" (${s.endStep}) must lie beyond the header.`);
|
|
2324
|
+
const entries = (s.branches ?? []).map((b) => b.step);
|
|
2325
|
+
if (entries.length < 2) {
|
|
2326
|
+
malformed(s.stepNumber, `parallel step ${s.stepNumber} requires "branches" with at least two arms \u2014 a single arm is sequential flow.`);
|
|
2327
|
+
} else {
|
|
2328
|
+
const sorted = [...entries].sort((a, b) => a - b);
|
|
2329
|
+
if (entries.some((e, i) => e !== sorted[i])) malformed(s.stepNumber, `parallel step ${s.stepNumber} "branches" must list arm entries in ascending order \u2014 arms are contiguous sub-regions of the body.`);
|
|
2330
|
+
if (new Set(entries).size !== entries.length) malformed(s.stepNumber, `parallel step ${s.stepNumber} "branches" lists the same entry step twice.`);
|
|
2331
|
+
const bodyStart = nextOf(s.stepNumber);
|
|
2332
|
+
if (bodyStart !== void 0 && sorted[0] !== bodyStart) malformed(s.stepNumber, `parallel step ${s.stepNumber}: the first arm must start at the body's first step (${bodyStart}), got ${sorted[0]} \u2014 steps before the first arm would belong to no arm.`);
|
|
2333
|
+
if (s.endStep !== void 0 && sorted.some((e) => e > s.endStep)) malformed(s.stepNumber, `parallel step ${s.stepNumber}: every arm entry must lie within the body (..${s.endStep}).`);
|
|
2334
|
+
}
|
|
2335
|
+
break;
|
|
2336
|
+
}
|
|
2337
|
+
case "jump":
|
|
2338
|
+
if (s.toStep === void 0) malformed(s.stepNumber, `jump step ${s.stepNumber} requires "toStep".`);
|
|
2339
|
+
break;
|
|
2340
|
+
}
|
|
2341
|
+
for (const [field, target] of jumpFieldsOf(s)) {
|
|
2342
|
+
if (byNum.has(target)) continue;
|
|
2343
|
+
sound = false;
|
|
2344
|
+
problems.push({
|
|
2345
|
+
stepNumber: s.stepNumber,
|
|
2346
|
+
code: "INVALID_STEP_JUMP",
|
|
2347
|
+
detail: `step ${s.stepNumber} "${field}" targets step ${target}, which does not exist in this narrative.`
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
return { problems, sound };
|
|
2352
|
+
}
|
|
2353
|
+
function jumpFieldsOf(s) {
|
|
2354
|
+
const fields = [];
|
|
2355
|
+
if (s.onTrueStep !== void 0) fields.push(["onTrueStep", s.onTrueStep]);
|
|
2356
|
+
if (s.onFalseStep !== void 0) fields.push(["onFalseStep", s.onFalseStep]);
|
|
2357
|
+
if (s.defaultStep !== void 0) fields.push(["defaultStep", s.defaultStep]);
|
|
2358
|
+
if (s.endStep !== void 0) fields.push(["endStep", s.endStep]);
|
|
2359
|
+
if (s.finallyStep !== void 0) fields.push(["finallyStep", s.finallyStep]);
|
|
2360
|
+
if (s.toStep !== void 0) fields.push(["toStep", s.toStep]);
|
|
2361
|
+
(s.cases ?? []).forEach((c, i) => fields.push([`cases[${i}].step`, c.step]));
|
|
2362
|
+
(s.catches ?? []).forEach((c, i) => fields.push([`catches[${i}].step`, c.step]));
|
|
2363
|
+
(s.branches ?? []).forEach((b, i) => fields.push([`branches[${i}].step`, b.step]));
|
|
2364
|
+
return fields;
|
|
2365
|
+
}
|
|
2366
|
+
var FLOW_FIELDS;
|
|
2367
|
+
var init_step_config = __esm({
|
|
2368
|
+
"src/models/step-config.ts"() {
|
|
2369
|
+
"use strict";
|
|
2370
|
+
FLOW_FIELDS = [
|
|
2371
|
+
"condition",
|
|
2372
|
+
"onTrueStep",
|
|
2373
|
+
"onFalseStep",
|
|
2374
|
+
"on",
|
|
2375
|
+
"cases",
|
|
2376
|
+
"defaultStep",
|
|
2377
|
+
"loopKind",
|
|
2378
|
+
"over",
|
|
2379
|
+
"endStep",
|
|
2380
|
+
"catches",
|
|
2381
|
+
"finallyStep",
|
|
2382
|
+
"branches",
|
|
2383
|
+
"toStep"
|
|
2384
|
+
];
|
|
2385
|
+
}
|
|
2386
|
+
});
|
|
2387
|
+
|
|
2245
2388
|
// src/models/code-model.ts
|
|
2246
2389
|
function pathKey(sourcePath) {
|
|
2247
2390
|
return sourcePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -2302,6 +2445,7 @@ var init_models = __esm({
|
|
|
2302
2445
|
init_specs();
|
|
2303
2446
|
init_type_references();
|
|
2304
2447
|
init_step_graph();
|
|
2448
|
+
init_step_config();
|
|
2305
2449
|
init_code_model();
|
|
2306
2450
|
init_surface_references();
|
|
2307
2451
|
}
|
|
@@ -8751,18 +8895,16 @@ var init_roundtrip_serialization = __esm({
|
|
|
8751
8895
|
}
|
|
8752
8896
|
});
|
|
8753
8897
|
|
|
8754
|
-
// src/core/rules/integrity/type-
|
|
8755
|
-
var
|
|
8756
|
-
var
|
|
8757
|
-
"src/core/rules/integrity/type-
|
|
8898
|
+
// src/core/rules/integrity/type-declarations.ts
|
|
8899
|
+
var typeDeclarationsRule;
|
|
8900
|
+
var init_type_declarations = __esm({
|
|
8901
|
+
"src/core/rules/integrity/type-declarations.ts"() {
|
|
8758
8902
|
"use strict";
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
description: "Type fields and interface method signatures may only reference builtins, in-scope generic parameters, or defined entity/value-object types. Types owned by a subsystem must reference an existing subsystem.",
|
|
8903
|
+
typeDeclarationsRule = {
|
|
8904
|
+
name: "type-declarations",
|
|
8905
|
+
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.",
|
|
8763
8906
|
codes: [
|
|
8764
8907
|
{ code: "INVALID_SUBSYSTEM_REFERENCE", defaultSeverity: "error", summary: "Type references a non-existent owning subsystem" },
|
|
8765
|
-
{ code: "UNDEFINED_TYPE_REFERENCE", defaultSeverity: "error", summary: "Reference to a type that is not defined anywhere" },
|
|
8766
8908
|
{ code: "HOLLOW_TYPE", defaultSeverity: "warning", summary: "Type declares no fields and no methods \u2014 a placeholder that informs neither implementers nor the ERD" }
|
|
8767
8909
|
],
|
|
8768
8910
|
check(ctx) {
|
|
@@ -8787,23 +8929,62 @@ var init_type_references2 = __esm({
|
|
|
8787
8929
|
isDraftCtx
|
|
8788
8930
|
);
|
|
8789
8931
|
}
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8932
|
+
}
|
|
8933
|
+
}
|
|
8934
|
+
};
|
|
8935
|
+
}
|
|
8936
|
+
});
|
|
8937
|
+
|
|
8938
|
+
// src/core/rules/integrity/field-type-references.ts
|
|
8939
|
+
var fieldTypeReferencesRule;
|
|
8940
|
+
var init_field_type_references = __esm({
|
|
8941
|
+
"src/core/rules/integrity/field-type-references.ts"() {
|
|
8942
|
+
"use strict";
|
|
8943
|
+
init_models();
|
|
8944
|
+
fieldTypeReferencesRule = {
|
|
8945
|
+
name: "field-type-references",
|
|
8946
|
+
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.",
|
|
8947
|
+
codes: [
|
|
8948
|
+
{ code: "UNDEFINED_TYPE_REFERENCE", defaultSeverity: "error", summary: "Reference to a type that is not defined anywhere" }
|
|
8949
|
+
],
|
|
8950
|
+
check(ctx) {
|
|
8951
|
+
for (const t of ctx.types) {
|
|
8952
|
+
const sub = ctx.subsystems.find((s) => s.id === t.subsystem);
|
|
8953
|
+
const isDraftCtx = sub ? sub.status === "draft" || sub.status === "design" : false;
|
|
8954
|
+
if (!t.fields) continue;
|
|
8955
|
+
for (const field of t.fields) {
|
|
8956
|
+
const refs = fieldTypeRefs(t, field.type);
|
|
8957
|
+
for (const ref of refs) {
|
|
8958
|
+
if (!ctx.isTypeResolved(ref, /* @__PURE__ */ new Set())) {
|
|
8959
|
+
ctx.addIssue(
|
|
8960
|
+
"error",
|
|
8961
|
+
"UNDEFINED_TYPE_REFERENCE",
|
|
8962
|
+
`Type "${t.id}" field "${field.name}" references undefined type "${ref}" in "${field.type}".`,
|
|
8963
|
+
t.id,
|
|
8964
|
+
isDraftCtx
|
|
8965
|
+
);
|
|
8803
8966
|
}
|
|
8804
8967
|
}
|
|
8805
8968
|
}
|
|
8806
8969
|
}
|
|
8970
|
+
}
|
|
8971
|
+
};
|
|
8972
|
+
}
|
|
8973
|
+
});
|
|
8974
|
+
|
|
8975
|
+
// src/core/rules/integrity/signature-type-references.ts
|
|
8976
|
+
var signatureTypeReferencesRule;
|
|
8977
|
+
var init_signature_type_references = __esm({
|
|
8978
|
+
"src/core/rules/integrity/signature-type-references.ts"() {
|
|
8979
|
+
"use strict";
|
|
8980
|
+
init_models();
|
|
8981
|
+
signatureTypeReferencesRule = {
|
|
8982
|
+
name: "signature-type-references",
|
|
8983
|
+
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.",
|
|
8984
|
+
codes: [
|
|
8985
|
+
{ code: "UNDEFINED_TYPE_REFERENCE", defaultSeverity: "error", summary: "Reference to a type that is not defined anywhere" }
|
|
8986
|
+
],
|
|
8987
|
+
check(ctx) {
|
|
8807
8988
|
for (const intf of ctx.interfaces) {
|
|
8808
8989
|
const isDraftCtx = ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design";
|
|
8809
8990
|
const interfaceGenerics = new Set(
|
|
@@ -8959,27 +9140,17 @@ var init_lint_allows = __esm({
|
|
|
8959
9140
|
}
|
|
8960
9141
|
});
|
|
8961
9142
|
|
|
8962
|
-
// src/core/rules/narrative/contract-symmetry
|
|
8963
|
-
var
|
|
8964
|
-
var
|
|
8965
|
-
"src/core/rules/narrative/contract-symmetry
|
|
9143
|
+
// src/core/rules/narrative/contract-symmetry.ts
|
|
9144
|
+
var contractSymmetryRule;
|
|
9145
|
+
var init_contract_symmetry = __esm({
|
|
9146
|
+
"src/core/rules/narrative/contract-symmetry.ts"() {
|
|
8966
9147
|
"use strict";
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
description: "Implementations mirror their contract method-for-method. Narrative call steps (and register handoffs, which share the identical target shape) must name an existing component and method, the caller must declare the dependency, and asserted semantic guarantees must be backed by the target contract.",
|
|
9148
|
+
contractSymmetryRule = {
|
|
9149
|
+
name: "contract-symmetry",
|
|
9150
|
+
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.",
|
|
8971
9151
|
codes: [
|
|
8972
9152
|
{ code: "UNEXPECTED_IMPLEMENTATION_METHOD", defaultSeverity: "error", summary: "Implementation method not present on the contract" },
|
|
8973
|
-
{ code: "MISSING_IMPLEMENTATION_METHOD", defaultSeverity: "error", summary: "Contract method missing from the implementation" }
|
|
8974
|
-
{ code: "MISSING_TARGET_COMPONENT", defaultSeverity: "error", summary: "Call/register step missing targetComponent" },
|
|
8975
|
-
{ code: "MISSING_TARGET_METHOD", defaultSeverity: "error", summary: "Call/register step missing targetMethod" },
|
|
8976
|
-
{ code: "INVALID_TARGET_COMPONENT_REFERENCE", defaultSeverity: "error", summary: "Call/register step targets a non-existent component" },
|
|
8977
|
-
{ code: "CROSS_TREE_REF_UNRESOLVED", defaultSeverity: "warning", summary: "Cross-tree reference (super::/:: form) with no surface snapshot covering it \u2014 only the parent project can verify it" },
|
|
8978
|
-
{ code: "SURFACE_REF_NOT_EXPOSED", defaultSeverity: "error", summary: "Cross-tree reference resolves to a surface snapshot that does not expose the called method/capability" },
|
|
8979
|
-
{ code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree call/dispatch/register target matched by surface snapshots of several providers with different contracts" },
|
|
8980
|
-
{ code: "UNDECLARED_DEPENDENCY_CALL", defaultSeverity: "error", summary: "Call step targets a component the caller does not depend on or own" },
|
|
8981
|
-
{ code: "INVALID_TARGET_METHOD_REFERENCE", defaultSeverity: "error", summary: "Call step targets a method not on any target interface" },
|
|
8982
|
-
{ code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Narrative asserts a guarantee the called contract does not declare" }
|
|
9153
|
+
{ code: "MISSING_IMPLEMENTATION_METHOD", defaultSeverity: "error", summary: "Contract method missing from the implementation" }
|
|
8983
9154
|
],
|
|
8984
9155
|
check(ctx) {
|
|
8985
9156
|
for (const impl of ctx.implementations) {
|
|
@@ -9010,6 +9181,33 @@ var init_contract_symmetry_and_narratives = __esm({
|
|
|
9010
9181
|
);
|
|
9011
9182
|
}
|
|
9012
9183
|
}
|
|
9184
|
+
}
|
|
9185
|
+
}
|
|
9186
|
+
};
|
|
9187
|
+
}
|
|
9188
|
+
});
|
|
9189
|
+
|
|
9190
|
+
// src/core/rules/narrative/narrative-target-references.ts
|
|
9191
|
+
var narrativeTargetReferencesRule;
|
|
9192
|
+
var init_narrative_target_references = __esm({
|
|
9193
|
+
"src/core/rules/narrative/narrative-target-references.ts"() {
|
|
9194
|
+
"use strict";
|
|
9195
|
+
narrativeTargetReferencesRule = {
|
|
9196
|
+
name: "narrative-target-references",
|
|
9197
|
+
description: "A narrative call, dispatch or register step must name a target component, and a target method unless it dispatches. 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 step asserts.",
|
|
9198
|
+
codes: [
|
|
9199
|
+
{ code: "MISSING_TARGET_COMPONENT", defaultSeverity: "error", summary: "Call/register step missing targetComponent" },
|
|
9200
|
+
{ code: "MISSING_TARGET_METHOD", defaultSeverity: "error", summary: "Call/register step missing targetMethod" },
|
|
9201
|
+
{ code: "UNDECLARED_DEPENDENCY_CALL", defaultSeverity: "error", summary: "Call step targets a component the caller does not depend on or own" },
|
|
9202
|
+
{ code: "INVALID_TARGET_METHOD_REFERENCE", defaultSeverity: "error", summary: "Call step targets a method not on any target interface" },
|
|
9203
|
+
{ code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Narrative asserts a guarantee the called contract does not declare" }
|
|
9204
|
+
],
|
|
9205
|
+
check(ctx) {
|
|
9206
|
+
for (const impl of ctx.implementations) {
|
|
9207
|
+
const contract = ctx.interfaceMap.get(impl.contract);
|
|
9208
|
+
if (!contract) continue;
|
|
9209
|
+
const isDraftCtx = ctx.isImplementationDraft(impl);
|
|
9210
|
+
const caller = ctx.componentMap.get(contract.component);
|
|
9013
9211
|
for (const implMethod of impl.methods) {
|
|
9014
9212
|
for (const step of implMethod.narrative) {
|
|
9015
9213
|
if (step.type !== "call" && step.type !== "dispatch" && step.type !== "register") continue;
|
|
@@ -9024,156 +9222,28 @@ var init_contract_symmetry_and_narratives = __esm({
|
|
|
9024
9222
|
continue;
|
|
9025
9223
|
}
|
|
9026
9224
|
const target = step.targetComponent;
|
|
9027
|
-
const
|
|
9028
|
-
|
|
9029
|
-
const fromSubsystem = caller?.subsystem;
|
|
9030
|
-
const isCollapsedForm = !isCrossTreeForm && fromSubsystem !== void 0 && ctx.isCollapsedCrossTreeRef(target, fromSubsystem);
|
|
9031
|
-
const reportUndeclared = (reaches, surfaceResolved) => {
|
|
9032
|
-
if (!caller || target === caller.id || caller.dependsOn.includes(target) || caller.owns.includes(target)) return;
|
|
9225
|
+
const verb = step.type === "dispatch" ? "dispatches through" : step.type === "register" ? "registers callback" : "calls";
|
|
9226
|
+
if (step.type !== "dispatch" && !step.targetMethod) {
|
|
9033
9227
|
ctx.addIssue(
|
|
9034
9228
|
"error",
|
|
9035
|
-
"
|
|
9036
|
-
`Method "${implMethod.name}" in implementation "${impl.id}"
|
|
9229
|
+
"MISSING_TARGET_METHOD",
|
|
9230
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" has a ${step.type} step (${step.stepNumber}) missing "targetMethod".`,
|
|
9037
9231
|
impl.id,
|
|
9038
|
-
isDraftCtx
|
|
9039
|
-
surfaceResolved
|
|
9232
|
+
isDraftCtx
|
|
9040
9233
|
);
|
|
9041
|
-
};
|
|
9042
|
-
if (step.type === "dispatch") {
|
|
9043
|
-
const dispatchTarget = ctx.componentMap.get(target);
|
|
9044
|
-
if (!dispatchTarget) {
|
|
9045
|
-
if (isCrossTreeForm || isCollapsedForm) {
|
|
9046
|
-
const resolved = ctx.resolveSurfaceRef(target, fromSubsystem);
|
|
9047
|
-
if (resolved.kind === "ambiguous") {
|
|
9048
|
-
ctx.addIssue(
|
|
9049
|
-
"error",
|
|
9050
|
-
"SURFACE_REF_AMBIGUOUS",
|
|
9051
|
-
ambiguityMessage(
|
|
9052
|
-
resolved,
|
|
9053
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" dispatches (step ${step.stepNumber}) through`,
|
|
9054
|
-
target
|
|
9055
|
-
),
|
|
9056
|
-
impl.id,
|
|
9057
|
-
isDraftCtx
|
|
9058
|
-
);
|
|
9059
|
-
continue;
|
|
9060
|
-
}
|
|
9061
|
-
if (resolved.kind === "resolved") {
|
|
9062
|
-
reportUndeclared("dispatches through", true);
|
|
9063
|
-
if (step.capability && !(resolved.entry.dispatch ?? []).some((b) => b.capability === step.capability)) {
|
|
9064
|
-
ctx.addIssue(
|
|
9065
|
-
"error",
|
|
9066
|
-
"SURFACE_REF_NOT_EXPOSED",
|
|
9067
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" dispatches capability "${step.capability}" through cross-tree portal "${target}" (step ${step.stepNumber}), but the surface snapshot of "${resolved.snapshot.projectName}" does not serve that capability on "${resolved.entry.id}".`,
|
|
9068
|
-
impl.id,
|
|
9069
|
-
isDraftCtx,
|
|
9070
|
-
true
|
|
9071
|
-
);
|
|
9072
|
-
}
|
|
9073
|
-
continue;
|
|
9074
|
-
}
|
|
9075
|
-
}
|
|
9076
|
-
if (isCrossTreeForm) {
|
|
9077
|
-
ctx.addIssue(
|
|
9078
|
-
"warning",
|
|
9079
|
-
"CROSS_TREE_REF_UNRESOLVED",
|
|
9080
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" dispatches through cross-tree component "${target}" (step ${step.stepNumber}), and no surface snapshot covers it \u2014 validate from the parent project, pin the family surfaces ("wairon surface pin"), or import the producing project's surface.`,
|
|
9081
|
-
impl.id,
|
|
9082
|
-
isDraftCtx
|
|
9083
|
-
);
|
|
9084
|
-
} else {
|
|
9085
|
-
ctx.addIssue(
|
|
9086
|
-
"error",
|
|
9087
|
-
"INVALID_TARGET_COMPONENT_REFERENCE",
|
|
9088
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" dispatches through component "${target}" which does not exist (step ${step.stepNumber}).`,
|
|
9089
|
-
impl.id,
|
|
9090
|
-
isDraftCtx
|
|
9091
|
-
);
|
|
9092
|
-
}
|
|
9093
|
-
continue;
|
|
9094
|
-
}
|
|
9095
|
-
reportUndeclared("dispatches through");
|
|
9096
9234
|
continue;
|
|
9097
9235
|
}
|
|
9098
|
-
|
|
9099
|
-
if (!
|
|
9236
|
+
if (!ctx.componentMap.get(target)) continue;
|
|
9237
|
+
if (caller && target !== caller.id && !caller.dependsOn.includes(target) && !caller.owns.includes(target)) {
|
|
9100
9238
|
ctx.addIssue(
|
|
9101
9239
|
"error",
|
|
9102
|
-
"
|
|
9103
|
-
`Method "${implMethod.name}" in implementation "${impl.id}"
|
|
9240
|
+
"UNDECLARED_DEPENDENCY_CALL",
|
|
9241
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" (component "${caller.id}") ${verb} component "${target}" (step ${step.stepNumber}) but component "${caller.id}" does not list "${target}" as a dependency.`,
|
|
9104
9242
|
impl.id,
|
|
9105
|
-
isDraftCtx
|
|
9243
|
+
isDraftCtx || ctx.isComponentDraft(caller.id)
|
|
9106
9244
|
);
|
|
9107
|
-
continue;
|
|
9108
|
-
}
|
|
9109
|
-
const targetComp = ctx.componentMap.get(target);
|
|
9110
|
-
if (!targetComp) {
|
|
9111
|
-
if (isCrossTreeForm || isCollapsedForm) {
|
|
9112
|
-
const resolved = ctx.resolveSurfaceRef(target, fromSubsystem);
|
|
9113
|
-
if (resolved.kind === "ambiguous") {
|
|
9114
|
-
ctx.addIssue(
|
|
9115
|
-
"error",
|
|
9116
|
-
"SURFACE_REF_AMBIGUOUS",
|
|
9117
|
-
ambiguityMessage(
|
|
9118
|
-
resolved,
|
|
9119
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} "${step.targetMethod}" (step ${step.stepNumber}) on`,
|
|
9120
|
-
target
|
|
9121
|
-
),
|
|
9122
|
-
impl.id,
|
|
9123
|
-
isDraftCtx
|
|
9124
|
-
);
|
|
9125
|
-
continue;
|
|
9126
|
-
}
|
|
9127
|
-
if (resolved.kind === "resolved") {
|
|
9128
|
-
reportUndeclared(verb, true);
|
|
9129
|
-
const surfaceMethod = resolved.entry.methods.find((m) => m.name === step.targetMethod);
|
|
9130
|
-
if (!surfaceMethod) {
|
|
9131
|
-
ctx.addIssue(
|
|
9132
|
-
"error",
|
|
9133
|
-
"SURFACE_REF_NOT_EXPOSED",
|
|
9134
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} "${step.targetMethod}" on cross-tree component "${target}" (step ${step.stepNumber}), but the surface snapshot of "${resolved.snapshot.projectName}" does not expose that method on "${resolved.entry.id}".`,
|
|
9135
|
-
impl.id,
|
|
9136
|
-
isDraftCtx,
|
|
9137
|
-
true
|
|
9138
|
-
);
|
|
9139
|
-
} else if (step.assertsGuarantees) {
|
|
9140
|
-
const declared = new Set(surfaceMethod.guarantees ?? []);
|
|
9141
|
-
for (const g of step.assertsGuarantees) {
|
|
9142
|
-
if (!declared.has(g)) {
|
|
9143
|
-
ctx.addIssue(
|
|
9144
|
-
"warning",
|
|
9145
|
-
"NARRATIVE_SEMANTIC_UNBACKED",
|
|
9146
|
-
`Step ${step.stepNumber} of "${implMethod.name}" in implementation "${impl.id}" asserts guarantee "${g}", but the surface snapshot of "${resolved.snapshot.projectName}" does not declare it on "${resolved.entry.id}.${step.targetMethod}".`,
|
|
9147
|
-
impl.id,
|
|
9148
|
-
isDraftCtx,
|
|
9149
|
-
true
|
|
9150
|
-
);
|
|
9151
|
-
}
|
|
9152
|
-
}
|
|
9153
|
-
}
|
|
9154
|
-
continue;
|
|
9155
|
-
}
|
|
9156
|
-
}
|
|
9157
|
-
if (isCrossTreeForm) {
|
|
9158
|
-
ctx.addIssue(
|
|
9159
|
-
"warning",
|
|
9160
|
-
"CROSS_TREE_REF_UNRESOLVED",
|
|
9161
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} cross-tree component "${target}" (step ${step.stepNumber}), and no surface snapshot covers it \u2014 validate from the parent project, pin the family surfaces ("wairon surface pin"), or import the producing project's surface.`,
|
|
9162
|
-
impl.id,
|
|
9163
|
-
isDraftCtx
|
|
9164
|
-
);
|
|
9165
|
-
} else {
|
|
9166
|
-
ctx.addIssue(
|
|
9167
|
-
"error",
|
|
9168
|
-
"INVALID_TARGET_COMPONENT_REFERENCE",
|
|
9169
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} component "${target}" which does not exist (step ${step.stepNumber}).`,
|
|
9170
|
-
impl.id,
|
|
9171
|
-
isDraftCtx
|
|
9172
|
-
);
|
|
9173
|
-
}
|
|
9174
|
-
continue;
|
|
9175
9245
|
}
|
|
9176
|
-
|
|
9246
|
+
if (step.type === "dispatch") continue;
|
|
9177
9247
|
const targetInterfaces = ctx.interfacesByComponent.get(target) ?? [];
|
|
9178
9248
|
let targetMethodSpec;
|
|
9179
9249
|
for (const targetIntf of targetInterfaces) {
|
|
@@ -9191,20 +9261,18 @@ var init_contract_symmetry_and_narratives = __esm({
|
|
|
9191
9261
|
impl.id,
|
|
9192
9262
|
isDraftCtx || ctx.isComponentDraft(target)
|
|
9193
9263
|
);
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
}
|
|
9207
|
-
}
|
|
9264
|
+
continue;
|
|
9265
|
+
}
|
|
9266
|
+
const declared = new Set(targetMethodSpec.guarantees ?? []);
|
|
9267
|
+
for (const g of step.assertsGuarantees ?? []) {
|
|
9268
|
+
if (!declared.has(g)) {
|
|
9269
|
+
ctx.addIssue(
|
|
9270
|
+
"warning",
|
|
9271
|
+
"NARRATIVE_SEMANTIC_UNBACKED",
|
|
9272
|
+
`Step ${step.stepNumber} of "${implMethod.name}" in implementation "${impl.id}" explicitly asserts guarantee "${g}", but the method it calls \u2014 "${step.targetMethod}" on "${target}" \u2014 does not list "${g}" among its L3 contract guarantees. Declare it on that method (and ensure its shape can deliver it), or revise the narrative.`,
|
|
9273
|
+
impl.id,
|
|
9274
|
+
isDraftCtx || ctx.isComponentDraft(target)
|
|
9275
|
+
);
|
|
9208
9276
|
}
|
|
9209
9277
|
}
|
|
9210
9278
|
}
|
|
@@ -9215,220 +9283,285 @@ var init_contract_symmetry_and_narratives = __esm({
|
|
|
9215
9283
|
}
|
|
9216
9284
|
});
|
|
9217
9285
|
|
|
9218
|
-
// src/core/rules/narrative/
|
|
9219
|
-
var
|
|
9220
|
-
var
|
|
9221
|
-
"src/core/rules/narrative/
|
|
9286
|
+
// src/core/rules/narrative/cross-tree-references.ts
|
|
9287
|
+
var crossTreeReferencesRule;
|
|
9288
|
+
var init_cross_tree_references = __esm({
|
|
9289
|
+
"src/core/rules/narrative/cross-tree-references.ts"() {
|
|
9222
9290
|
"use strict";
|
|
9223
9291
|
init_models();
|
|
9224
|
-
|
|
9225
|
-
name: "
|
|
9226
|
-
description: "
|
|
9292
|
+
crossTreeReferencesRule = {
|
|
9293
|
+
name: "cross-tree-references",
|
|
9294
|
+
description: "A narrative call, dispatch or register target that is not a component of this tree must resolve against the stored surface snapshots to exactly one declared contract. Snapshots that expose the name with disagreeing contracts leave the reference ambiguous; no snapshot at all is a warning for a reference that points outside this loading root (only the parent project can verify it) and the error a genuine local typo has always got.",
|
|
9227
9295
|
codes: [
|
|
9228
|
-
{ code: "
|
|
9296
|
+
{ code: "INVALID_TARGET_COMPONENT_REFERENCE", defaultSeverity: "error", summary: "Call/register step targets a non-existent component" },
|
|
9297
|
+
{ code: "CROSS_TREE_REF_UNRESOLVED", defaultSeverity: "warning", summary: "Cross-tree reference (super::/:: form) with no surface snapshot covering it \u2014 only the parent project can verify it" },
|
|
9298
|
+
{ code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree call/dispatch/register target matched by surface snapshots of several providers with different contracts" }
|
|
9229
9299
|
],
|
|
9230
9300
|
check(ctx) {
|
|
9231
|
-
const known = /* @__PURE__ */ new Set([...SEMANTIC_GUARANTEES, ...ctx.ext.guarantees]);
|
|
9232
|
-
const fixHint = `Known tokens: builtin ${SEMANTIC_GUARANTEES.join(", ")}${ctx.ext.guarantees.length ? `; pack-declared ${ctx.ext.guarantees.join(", ")}` : ""}. Fix the spelling or declare the token in an extension pack's \`guarantees\` list.`;
|
|
9233
|
-
for (const intf of ctx.interfaces) {
|
|
9234
|
-
const isDraftCtx = intf.status === "draft" || intf.status === "design" || ctx.isComponentDraft(intf.component);
|
|
9235
|
-
for (const method2 of intf.methods) {
|
|
9236
|
-
for (const g of method2.guarantees ?? []) {
|
|
9237
|
-
if (!known.has(g)) {
|
|
9238
|
-
ctx.addIssue(
|
|
9239
|
-
"warning",
|
|
9240
|
-
"UNKNOWN_GUARANTEE",
|
|
9241
|
-
`Method "${method2.name}" on interface "${intf.id}" declares guarantee "${g}", which is neither a builtin guarantee nor declared by any loaded extension pack \u2014 no narrative assertion can ever match it. ${fixHint}`,
|
|
9242
|
-
intf.id,
|
|
9243
|
-
isDraftCtx
|
|
9244
|
-
);
|
|
9245
|
-
}
|
|
9246
|
-
}
|
|
9247
|
-
}
|
|
9248
|
-
}
|
|
9249
9301
|
for (const impl of ctx.implementations) {
|
|
9302
|
+
const contract = ctx.interfaceMap.get(impl.contract);
|
|
9303
|
+
if (!contract) continue;
|
|
9250
9304
|
const isDraftCtx = ctx.isImplementationDraft(impl);
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9305
|
+
const caller = ctx.componentMap.get(contract.component);
|
|
9306
|
+
const fromSubsystem = caller?.subsystem;
|
|
9307
|
+
for (const implMethod of impl.methods) {
|
|
9308
|
+
for (const step of implMethod.narrative) {
|
|
9309
|
+
if (step.type !== "call" && step.type !== "dispatch" && step.type !== "register") continue;
|
|
9310
|
+
if (!step.targetComponent) continue;
|
|
9311
|
+
if (step.type !== "dispatch" && !step.targetMethod) continue;
|
|
9312
|
+
const target = step.targetComponent;
|
|
9313
|
+
if (ctx.componentMap.get(target)) continue;
|
|
9314
|
+
const verb = step.type === "dispatch" ? "dispatches through" : step.type === "register" ? "registers callback" : "calls";
|
|
9315
|
+
const isCrossTreeForm = ctx.isExternalNamespaceRef(target);
|
|
9316
|
+
const isCollapsedForm = !isCrossTreeForm && fromSubsystem !== void 0 && ctx.isCollapsedCrossTreeRef(target, fromSubsystem);
|
|
9317
|
+
if (isCrossTreeForm || isCollapsedForm) {
|
|
9318
|
+
const resolved = ctx.resolveSurfaceRef(target, fromSubsystem);
|
|
9319
|
+
if (resolved.kind === "ambiguous") {
|
|
9255
9320
|
ctx.addIssue(
|
|
9256
|
-
"
|
|
9257
|
-
"
|
|
9258
|
-
|
|
9321
|
+
"error",
|
|
9322
|
+
"SURFACE_REF_AMBIGUOUS",
|
|
9323
|
+
ambiguityMessage(
|
|
9324
|
+
resolved,
|
|
9325
|
+
step.type === "dispatch" ? `Method "${implMethod.name}" in implementation "${impl.id}" dispatches (step ${step.stepNumber}) through` : `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} "${step.targetMethod}" (step ${step.stepNumber}) on`,
|
|
9326
|
+
target
|
|
9327
|
+
),
|
|
9259
9328
|
impl.id,
|
|
9260
9329
|
isDraftCtx
|
|
9261
9330
|
);
|
|
9331
|
+
continue;
|
|
9262
9332
|
}
|
|
9333
|
+
if (resolved.kind === "resolved") continue;
|
|
9263
9334
|
}
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
}
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9335
|
+
if (isCrossTreeForm) {
|
|
9336
|
+
ctx.addIssue(
|
|
9337
|
+
"warning",
|
|
9338
|
+
"CROSS_TREE_REF_UNRESOLVED",
|
|
9339
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} cross-tree component "${target}" (step ${step.stepNumber}), and no surface snapshot covers it \u2014 validate from the parent project, pin the family surfaces ("wairon surface pin"), or import the producing project's surface.`,
|
|
9340
|
+
impl.id,
|
|
9341
|
+
isDraftCtx
|
|
9342
|
+
);
|
|
9343
|
+
} else {
|
|
9344
|
+
ctx.addIssue(
|
|
9345
|
+
"error",
|
|
9346
|
+
"INVALID_TARGET_COMPONENT_REFERENCE",
|
|
9347
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} component "${target}" which does not exist (step ${step.stepNumber}).`,
|
|
9348
|
+
impl.id,
|
|
9349
|
+
isDraftCtx
|
|
9350
|
+
);
|
|
9351
|
+
}
|
|
9352
|
+
}
|
|
9353
|
+
}
|
|
9354
|
+
}
|
|
9355
|
+
}
|
|
9356
|
+
};
|
|
9357
|
+
}
|
|
9358
|
+
});
|
|
9359
|
+
|
|
9360
|
+
// src/core/rules/narrative/surface-reference-backing.ts
|
|
9361
|
+
var surfaceReferenceBackingRule;
|
|
9362
|
+
var init_surface_reference_backing = __esm({
|
|
9363
|
+
"src/core/rules/narrative/surface-reference-backing.ts"() {
|
|
9284
9364
|
"use strict";
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
"
|
|
9288
|
-
"onTrueStep",
|
|
9289
|
-
"onFalseStep",
|
|
9290
|
-
"on",
|
|
9291
|
-
"cases",
|
|
9292
|
-
"defaultStep",
|
|
9293
|
-
"loopKind",
|
|
9294
|
-
"over",
|
|
9295
|
-
"endStep",
|
|
9296
|
-
"catches",
|
|
9297
|
-
"finallyStep",
|
|
9298
|
-
"branches",
|
|
9299
|
-
"toStep"
|
|
9300
|
-
];
|
|
9301
|
-
narrativeFlowRule = {
|
|
9302
|
-
name: "narrative-flow",
|
|
9303
|
-
description: 'Control-flow soundness of L5 narratives: flow steps (branch, switch, loop, try, parallel, jump) must carry their required config, every jump field must target an existing step number in the same narrative, and every step must be reachable from the first step following fall-through and jumps. A parallel body is covered by contiguous, ordered arms (>= 2) whose last steps continue at the join after endStep; "detach" (fire-and-forget) is legal on call/dispatch steps only.',
|
|
9365
|
+
surfaceReferenceBackingRule = {
|
|
9366
|
+
name: "surface-reference-backing",
|
|
9367
|
+
description: "The surface snapshot a cross-tree narrative target resolves to must back what the step asks of it: the calling component declares the collaborator, the snapshot exposes the called method (or serves the dispatched capability), and it declares every semantic guarantee the step asserts. A snapshot is the provider's declared contract, so these are contract verdicts rather than resolution failures.",
|
|
9304
9368
|
codes: [
|
|
9305
|
-
{ code: "
|
|
9306
|
-
{ code: "
|
|
9307
|
-
{ code: "
|
|
9308
|
-
{ code: "REGION_OVERLAP", defaultSeverity: "error", summary: "loop/try regions interleave \u2014 regions must nest or be disjoint to map onto structured code" },
|
|
9309
|
-
{ 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" },
|
|
9310
|
-
{ code: "FALLTHROUGH_INTO_HANDLER", defaultSeverity: "warning", summary: "try body falls through into its own catch/finally region on the success path" },
|
|
9311
|
-
{ code: "BACKWARD_JUMP", defaultSeverity: "warning", summary: "Backward jump that is not a continue to an enclosing loop header \u2014 model repetition with a loop step" },
|
|
9312
|
-
{ code: "DUPLICATE_STEP_LABEL", defaultSeverity: "error", summary: "Two steps in one narrative share a label \u2014 the symbolic anchor later deltas address by" }
|
|
9369
|
+
{ code: "SURFACE_REF_NOT_EXPOSED", defaultSeverity: "error", summary: "Cross-tree reference resolves to a surface snapshot that does not expose the called method/capability" },
|
|
9370
|
+
{ code: "UNDECLARED_DEPENDENCY_CALL", defaultSeverity: "error", summary: "Call step targets a component the caller does not depend on or own" },
|
|
9371
|
+
{ code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Narrative asserts a guarantee the called contract does not declare" }
|
|
9313
9372
|
],
|
|
9314
9373
|
check(ctx) {
|
|
9315
9374
|
for (const impl of ctx.implementations) {
|
|
9375
|
+
const contract = ctx.interfaceMap.get(impl.contract);
|
|
9376
|
+
if (!contract) continue;
|
|
9316
9377
|
const isDraftCtx = ctx.isImplementationDraft(impl);
|
|
9378
|
+
const caller = ctx.componentMap.get(contract.component);
|
|
9379
|
+
const fromSubsystem = caller?.subsystem;
|
|
9317
9380
|
for (const implMethod of impl.methods) {
|
|
9318
|
-
const
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
const
|
|
9326
|
-
if (
|
|
9381
|
+
for (const step of implMethod.narrative) {
|
|
9382
|
+
if (step.type !== "call" && step.type !== "dispatch" && step.type !== "register") continue;
|
|
9383
|
+
if (!step.targetComponent) continue;
|
|
9384
|
+
if (step.type !== "dispatch" && !step.targetMethod) continue;
|
|
9385
|
+
const target = step.targetComponent;
|
|
9386
|
+
if (ctx.componentMap.get(target)) continue;
|
|
9387
|
+
const isCrossTreeForm = ctx.isExternalNamespaceRef(target);
|
|
9388
|
+
const isCollapsedForm = !isCrossTreeForm && fromSubsystem !== void 0 && ctx.isCollapsedCrossTreeRef(target, fromSubsystem);
|
|
9389
|
+
if (!isCrossTreeForm && !isCollapsedForm) continue;
|
|
9390
|
+
const resolved = ctx.resolveSurfaceRef(target, fromSubsystem);
|
|
9391
|
+
if (resolved.kind !== "resolved") continue;
|
|
9392
|
+
const verb = step.type === "dispatch" ? "dispatches through" : step.type === "register" ? "registers callback" : "calls";
|
|
9393
|
+
if (caller && target !== caller.id && !caller.dependsOn.includes(target) && !caller.owns.includes(target)) {
|
|
9327
9394
|
ctx.addIssue(
|
|
9328
9395
|
"error",
|
|
9329
|
-
"
|
|
9330
|
-
|
|
9396
|
+
"UNDECLARED_DEPENDENCY_CALL",
|
|
9397
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" (component "${caller.id}") ${verb} component "${target}" (step ${step.stepNumber}) but component "${caller.id}" does not list "${target}" as a dependency.`,
|
|
9331
9398
|
impl.id,
|
|
9332
|
-
isDraftCtx
|
|
9399
|
+
isDraftCtx || ctx.isComponentDraft(caller.id),
|
|
9400
|
+
true
|
|
9333
9401
|
);
|
|
9334
|
-
} else {
|
|
9335
|
-
labelled.set(label, step.stepNumber);
|
|
9336
|
-
}
|
|
9337
|
-
}
|
|
9338
|
-
let sound = true;
|
|
9339
|
-
const malformed = (msg) => {
|
|
9340
|
-
sound = false;
|
|
9341
|
-
ctx.addIssue("error", "MALFORMED_FLOW_STEP", where + msg, impl.id, isDraftCtx);
|
|
9342
|
-
};
|
|
9343
|
-
const byNum = /* @__PURE__ */ new Map();
|
|
9344
|
-
for (const s of steps) {
|
|
9345
|
-
if (byNum.has(s.stepNumber)) malformed(`duplicate stepNumber ${s.stepNumber} \u2014 jump targets would be ambiguous.`);
|
|
9346
|
-
byNum.set(s.stepNumber, s);
|
|
9347
|
-
}
|
|
9348
|
-
const nums = [...byNum.keys()].sort((a, b) => a - b);
|
|
9349
|
-
const indexOf = new Map(nums.map((n, i) => [n, i]));
|
|
9350
|
-
const nextOf = (n) => {
|
|
9351
|
-
const i = indexOf.get(n);
|
|
9352
|
-
return i !== void 0 && i + 1 < nums.length ? nums[i + 1] : void 0;
|
|
9353
|
-
};
|
|
9354
|
-
for (const s of steps) {
|
|
9355
|
-
if (s.detach && s.type !== "call" && s.type !== "dispatch") {
|
|
9356
|
-
malformed(`step ${s.stepNumber} (${s.type}) carries "detach" \u2014 fire-and-forget applies to call/dispatch steps only.`);
|
|
9357
9402
|
}
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
if (!s.condition) malformed(`branch step ${s.stepNumber} requires "condition".`);
|
|
9369
|
-
if (s.onFalseStep === void 0) malformed(`branch step ${s.stepNumber} requires "onFalseStep" (true continues at onTrueStep or the next step).`);
|
|
9370
|
-
break;
|
|
9371
|
-
case "switch":
|
|
9372
|
-
if (!s.cases || !s.cases.length) malformed(`switch step ${s.stepNumber} requires non-empty "cases".`);
|
|
9373
|
-
break;
|
|
9374
|
-
case "loop": {
|
|
9375
|
-
if (s.endStep === void 0) malformed(`loop step ${s.stepNumber} requires "endStep" (last step of the body).`);
|
|
9376
|
-
else if (s.endStep <= s.stepNumber) malformed(`loop step ${s.stepNumber} "endStep" (${s.endStep}) must lie beyond the header.`);
|
|
9377
|
-
const kind = s.loopKind ?? (s.over ? "forEach" : "while");
|
|
9378
|
-
if ((kind === "while" || kind === "doWhile") && !s.condition) malformed(`${kind} loop step ${s.stepNumber} requires "condition".`);
|
|
9379
|
-
if ((kind === "forEach" || kind === "for") && !s.over) malformed(`${kind} loop step ${s.stepNumber} requires "over" (the iteration source).`);
|
|
9380
|
-
break;
|
|
9403
|
+
if (step.type === "dispatch") {
|
|
9404
|
+
if (step.capability && !(resolved.entry.dispatch ?? []).some((b) => b.capability === step.capability)) {
|
|
9405
|
+
ctx.addIssue(
|
|
9406
|
+
"error",
|
|
9407
|
+
"SURFACE_REF_NOT_EXPOSED",
|
|
9408
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" dispatches capability "${step.capability}" through cross-tree portal "${target}" (step ${step.stepNumber}), but the surface snapshot of "${resolved.snapshot.projectName}" does not serve that capability on "${resolved.entry.id}".`,
|
|
9409
|
+
impl.id,
|
|
9410
|
+
isDraftCtx,
|
|
9411
|
+
true
|
|
9412
|
+
);
|
|
9381
9413
|
}
|
|
9382
|
-
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9414
|
+
continue;
|
|
9415
|
+
}
|
|
9416
|
+
const surfaceMethod = resolved.entry.methods.find((m) => m.name === step.targetMethod);
|
|
9417
|
+
if (!surfaceMethod) {
|
|
9418
|
+
ctx.addIssue(
|
|
9419
|
+
"error",
|
|
9420
|
+
"SURFACE_REF_NOT_EXPOSED",
|
|
9421
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} "${step.targetMethod}" on cross-tree component "${target}" (step ${step.stepNumber}), but the surface snapshot of "${resolved.snapshot.projectName}" does not expose that method on "${resolved.entry.id}".`,
|
|
9422
|
+
impl.id,
|
|
9423
|
+
isDraftCtx,
|
|
9424
|
+
true
|
|
9425
|
+
);
|
|
9426
|
+
continue;
|
|
9427
|
+
}
|
|
9428
|
+
const declared = new Set(surfaceMethod.guarantees ?? []);
|
|
9429
|
+
for (const g of step.assertsGuarantees ?? []) {
|
|
9430
|
+
if (!declared.has(g)) {
|
|
9431
|
+
ctx.addIssue(
|
|
9432
|
+
"warning",
|
|
9433
|
+
"NARRATIVE_SEMANTIC_UNBACKED",
|
|
9434
|
+
`Step ${step.stepNumber} of "${implMethod.name}" in implementation "${impl.id}" asserts guarantee "${g}", but the surface snapshot of "${resolved.snapshot.projectName}" does not declare it on "${resolved.entry.id}.${step.targetMethod}".`,
|
|
9435
|
+
impl.id,
|
|
9436
|
+
isDraftCtx,
|
|
9437
|
+
true
|
|
9438
|
+
);
|
|
9402
9439
|
}
|
|
9403
|
-
case "jump":
|
|
9404
|
-
if (s.toStep === void 0) malformed(`jump step ${s.stepNumber} requires "toStep".`);
|
|
9405
|
-
break;
|
|
9406
9440
|
}
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9441
|
+
}
|
|
9442
|
+
}
|
|
9443
|
+
}
|
|
9444
|
+
}
|
|
9445
|
+
};
|
|
9446
|
+
}
|
|
9447
|
+
});
|
|
9448
|
+
|
|
9449
|
+
// src/core/rules/narrative/guarantee-tokens.ts
|
|
9450
|
+
var guaranteeTokensRule;
|
|
9451
|
+
var init_guarantee_tokens = __esm({
|
|
9452
|
+
"src/core/rules/narrative/guarantee-tokens.ts"() {
|
|
9453
|
+
"use strict";
|
|
9454
|
+
init_models();
|
|
9455
|
+
guaranteeTokensRule = {
|
|
9456
|
+
name: "guarantee-tokens",
|
|
9457
|
+
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.",
|
|
9458
|
+
codes: [
|
|
9459
|
+
{ code: "UNKNOWN_GUARANTEE", defaultSeverity: "warning", summary: "Guarantee token is neither builtin nor pack-declared" }
|
|
9460
|
+
],
|
|
9461
|
+
check(ctx) {
|
|
9462
|
+
const known = /* @__PURE__ */ new Set([...SEMANTIC_GUARANTEES, ...ctx.ext.guarantees]);
|
|
9463
|
+
const fixHint = `Known tokens: builtin ${SEMANTIC_GUARANTEES.join(", ")}${ctx.ext.guarantees.length ? `; pack-declared ${ctx.ext.guarantees.join(", ")}` : ""}. Fix the spelling or declare the token in an extension pack's \`guarantees\` list.`;
|
|
9464
|
+
for (const intf of ctx.interfaces) {
|
|
9465
|
+
const isDraftCtx = intf.status === "draft" || intf.status === "design" || ctx.isComponentDraft(intf.component);
|
|
9466
|
+
for (const method2 of intf.methods) {
|
|
9467
|
+
for (const g of method2.guarantees ?? []) {
|
|
9468
|
+
if (!known.has(g)) {
|
|
9469
|
+
ctx.addIssue(
|
|
9470
|
+
"warning",
|
|
9471
|
+
"UNKNOWN_GUARANTEE",
|
|
9472
|
+
`Method "${method2.name}" on interface "${intf.id}" declares guarantee "${g}", which is neither a builtin guarantee nor declared by any loaded extension pack \u2014 no narrative assertion can ever match it. ${fixHint}`,
|
|
9473
|
+
intf.id,
|
|
9474
|
+
isDraftCtx
|
|
9475
|
+
);
|
|
9476
|
+
}
|
|
9477
|
+
}
|
|
9478
|
+
}
|
|
9479
|
+
}
|
|
9480
|
+
for (const impl of ctx.implementations) {
|
|
9481
|
+
const isDraftCtx = ctx.isImplementationDraft(impl);
|
|
9482
|
+
for (const method2 of impl.methods) {
|
|
9483
|
+
for (const step of method2.narrative ?? []) {
|
|
9484
|
+
for (const g of step.assertsGuarantees ?? []) {
|
|
9485
|
+
if (!known.has(g)) {
|
|
9420
9486
|
ctx.addIssue(
|
|
9421
|
-
"
|
|
9422
|
-
"
|
|
9423
|
-
|
|
9487
|
+
"warning",
|
|
9488
|
+
"UNKNOWN_GUARANTEE",
|
|
9489
|
+
`Step ${step.stepNumber} of "${method2.name}" in implementation "${impl.id}" asserts guarantee "${g}", which is neither a builtin guarantee nor declared by any loaded extension pack \u2014 no L3 contract can ever back it. ${fixHint}`,
|
|
9424
9490
|
impl.id,
|
|
9425
9491
|
isDraftCtx
|
|
9426
9492
|
);
|
|
9427
9493
|
}
|
|
9428
9494
|
}
|
|
9429
9495
|
}
|
|
9430
|
-
|
|
9496
|
+
}
|
|
9497
|
+
}
|
|
9498
|
+
}
|
|
9499
|
+
};
|
|
9500
|
+
}
|
|
9501
|
+
});
|
|
9502
|
+
|
|
9503
|
+
// src/core/rules/narrative/narrative-step-config.ts
|
|
9504
|
+
var narrativeStepConfigRule;
|
|
9505
|
+
var init_narrative_step_config = __esm({
|
|
9506
|
+
"src/core/rules/narrative/narrative-step-config.ts"() {
|
|
9507
|
+
"use strict";
|
|
9508
|
+
init_models();
|
|
9509
|
+
narrativeStepConfigRule = {
|
|
9510
|
+
name: "narrative-step-config",
|
|
9511
|
+
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, 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.',
|
|
9512
|
+
codes: [
|
|
9513
|
+
{ code: "MALFORMED_FLOW_STEP", defaultSeverity: "error", summary: "Flow step missing required config (or flow config on a local/call step)" },
|
|
9514
|
+
{ code: "INVALID_STEP_JUMP", defaultSeverity: "error", summary: "Jump field targets a step number that does not exist in the narrative" },
|
|
9515
|
+
{ code: "DUPLICATE_STEP_LABEL", defaultSeverity: "error", summary: "Two steps in one narrative share a label \u2014 the symbolic anchor later deltas address by" }
|
|
9516
|
+
],
|
|
9517
|
+
check(ctx) {
|
|
9518
|
+
for (const impl of ctx.implementations) {
|
|
9519
|
+
const isDraftCtx = ctx.isImplementationDraft(impl);
|
|
9520
|
+
for (const implMethod of impl.methods) {
|
|
9521
|
+
if (!implMethod.narrative.length) continue;
|
|
9522
|
+
const where = `Method "${implMethod.name}" in implementation "${impl.id}": `;
|
|
9523
|
+
for (const problem of stepConfigVerdict(implMethod).problems) {
|
|
9524
|
+
ctx.addIssue("error", problem.code, where + problem.detail, impl.id, isDraftCtx);
|
|
9525
|
+
}
|
|
9526
|
+
}
|
|
9527
|
+
}
|
|
9528
|
+
}
|
|
9529
|
+
};
|
|
9530
|
+
}
|
|
9531
|
+
});
|
|
9532
|
+
|
|
9533
|
+
// src/core/rules/narrative/narrative-reachability.ts
|
|
9534
|
+
function regionsOf(steps) {
|
|
9535
|
+
const regions = [];
|
|
9536
|
+
for (const s of steps) {
|
|
9537
|
+
if ((s.type === "loop" || s.type === "try" || s.type === "parallel") && s.endStep !== void 0) {
|
|
9538
|
+
regions.push({ h: s.stepNumber, end: s.endStep, kind: s.type });
|
|
9539
|
+
}
|
|
9540
|
+
}
|
|
9541
|
+
return regions;
|
|
9542
|
+
}
|
|
9543
|
+
var narrativeReachabilityRule;
|
|
9544
|
+
var init_narrative_reachability = __esm({
|
|
9545
|
+
"src/core/rules/narrative/narrative-reachability.ts"() {
|
|
9546
|
+
"use strict";
|
|
9547
|
+
init_models();
|
|
9548
|
+
narrativeReachabilityRule = {
|
|
9549
|
+
name: "narrative-reachability",
|
|
9550
|
+
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).",
|
|
9551
|
+
codes: [
|
|
9552
|
+
{ code: "UNREACHABLE_STEP", defaultSeverity: "warning", summary: "Step not reachable from the first step following fall-through and jumps" },
|
|
9553
|
+
{ code: "REGION_OVERLAP", defaultSeverity: "error", summary: "loop/try regions interleave \u2014 regions must nest or be disjoint to map onto structured code" }
|
|
9554
|
+
],
|
|
9555
|
+
check(ctx) {
|
|
9556
|
+
for (const impl of ctx.implementations) {
|
|
9557
|
+
const isDraftCtx = ctx.isImplementationDraft(impl);
|
|
9558
|
+
for (const implMethod of impl.methods) {
|
|
9559
|
+
const steps = implMethod.narrative;
|
|
9560
|
+
if (!steps.length) continue;
|
|
9561
|
+
if (!stepConfigVerdict(implMethod).sound) continue;
|
|
9562
|
+
const where = `Method "${implMethod.name}" in implementation "${impl.id}": `;
|
|
9431
9563
|
const graph = stepGraph(implMethod);
|
|
9564
|
+
const nums = graph.stepNumbers;
|
|
9432
9565
|
const visited = /* @__PURE__ */ new Set();
|
|
9433
9566
|
const stack = [nums[0]];
|
|
9434
9567
|
while (stack.length) {
|
|
@@ -9449,13 +9582,7 @@ var init_narrative_flow = __esm({
|
|
|
9449
9582
|
isDraftCtx
|
|
9450
9583
|
);
|
|
9451
9584
|
}
|
|
9452
|
-
const regions =
|
|
9453
|
-
for (const s of steps) {
|
|
9454
|
-
if ((s.type === "loop" || s.type === "try" || s.type === "parallel") && s.endStep !== void 0) {
|
|
9455
|
-
regions.push({ h: s.stepNumber, end: s.endStep, kind: s.type });
|
|
9456
|
-
}
|
|
9457
|
-
}
|
|
9458
|
-
const inBody = (n, r) => n > r.h && n <= r.end;
|
|
9585
|
+
const regions = regionsOf(steps);
|
|
9459
9586
|
for (const a of regions) {
|
|
9460
9587
|
for (const b of regions) {
|
|
9461
9588
|
if (b.h > a.h && b.h <= a.end && b.end > a.end) {
|
|
@@ -9469,19 +9596,63 @@ var init_narrative_flow = __esm({
|
|
|
9469
9596
|
}
|
|
9470
9597
|
}
|
|
9471
9598
|
}
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9484
|
-
|
|
9599
|
+
}
|
|
9600
|
+
}
|
|
9601
|
+
}
|
|
9602
|
+
};
|
|
9603
|
+
}
|
|
9604
|
+
});
|
|
9605
|
+
|
|
9606
|
+
// src/core/rules/narrative/narrative-jump-edges.ts
|
|
9607
|
+
function regionsOf2(steps) {
|
|
9608
|
+
const regions = [];
|
|
9609
|
+
for (const s of steps) {
|
|
9610
|
+
if ((s.type === "loop" || s.type === "try" || s.type === "parallel") && s.endStep !== void 0) {
|
|
9611
|
+
regions.push({ h: s.stepNumber, end: s.endStep, kind: s.type });
|
|
9612
|
+
}
|
|
9613
|
+
}
|
|
9614
|
+
return regions;
|
|
9615
|
+
}
|
|
9616
|
+
function jumpEdgesOf(steps) {
|
|
9617
|
+
const edges = [];
|
|
9618
|
+
for (const s of steps) {
|
|
9619
|
+
const push2 = (field, to) => {
|
|
9620
|
+
if (to !== void 0) edges.push({ from: s.stepNumber, to, field, kind: s.type });
|
|
9621
|
+
};
|
|
9622
|
+
push2("onTrueStep", s.onTrueStep);
|
|
9623
|
+
push2("onFalseStep", s.onFalseStep);
|
|
9624
|
+
push2("defaultStep", s.defaultStep);
|
|
9625
|
+
push2("toStep", s.toStep);
|
|
9626
|
+
push2("finallyStep", s.finallyStep);
|
|
9627
|
+
(s.cases ?? []).forEach((c, i) => push2(`cases[${i}].step`, c.step));
|
|
9628
|
+
(s.catches ?? []).forEach((c, i) => push2(`catches[${i}].step`, c.step));
|
|
9629
|
+
}
|
|
9630
|
+
return edges;
|
|
9631
|
+
}
|
|
9632
|
+
var narrativeJumpEdgesRule;
|
|
9633
|
+
var init_narrative_jump_edges = __esm({
|
|
9634
|
+
"src/core/rules/narrative/narrative-jump-edges.ts"() {
|
|
9635
|
+
"use strict";
|
|
9636
|
+
init_models();
|
|
9637
|
+
narrativeJumpEdgesRule = {
|
|
9638
|
+
name: "narrative-jump-edges",
|
|
9639
|
+
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).",
|
|
9640
|
+
codes: [
|
|
9641
|
+
{ 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" },
|
|
9642
|
+
{ code: "BACKWARD_JUMP", defaultSeverity: "warning", summary: "Backward jump that is not a continue to an enclosing loop header \u2014 model repetition with a loop step" },
|
|
9643
|
+
{ code: "FALLTHROUGH_INTO_HANDLER", defaultSeverity: "warning", summary: "try body falls through into its own catch/finally region on the success path" }
|
|
9644
|
+
],
|
|
9645
|
+
check(ctx) {
|
|
9646
|
+
for (const impl of ctx.implementations) {
|
|
9647
|
+
const isDraftCtx = ctx.isImplementationDraft(impl);
|
|
9648
|
+
for (const implMethod of impl.methods) {
|
|
9649
|
+
const steps = implMethod.narrative;
|
|
9650
|
+
if (!steps.length) continue;
|
|
9651
|
+
if (!stepConfigVerdict(implMethod).sound) continue;
|
|
9652
|
+
const where = `Method "${implMethod.name}" in implementation "${impl.id}": `;
|
|
9653
|
+
const regions = regionsOf2(steps);
|
|
9654
|
+
const jumpEdges = jumpEdgesOf(steps);
|
|
9655
|
+
const inBody = (n, r) => n > r.h && n <= r.end;
|
|
9485
9656
|
for (const e of jumpEdges) {
|
|
9486
9657
|
for (const r of regions) {
|
|
9487
9658
|
if (inBody(e.to, r) && e.from !== r.h && !inBody(e.from, r)) {
|
|
@@ -9494,19 +9665,26 @@ var init_narrative_flow = __esm({
|
|
|
9494
9665
|
);
|
|
9495
9666
|
}
|
|
9496
9667
|
}
|
|
9497
|
-
if ((e.kind === "branch" || e.kind === "switch" || e.kind === "jump") && e.to < e.from) {
|
|
9498
|
-
const continueToLoop = regions.some((r) => r.kind === "loop" && r.h === e.to && inBody(e.from, r));
|
|
9499
|
-
if (!continueToLoop) {
|
|
9500
|
-
ctx.addIssue(
|
|
9501
|
-
"warning",
|
|
9502
|
-
"BACKWARD_JUMP",
|
|
9503
|
-
`${where}step ${e.from} "${e.field}" jumps backwards to step ${e.to} \u2014 model repetition with a loop step (backward jumps are only idiomatic as a continue to an enclosing loop header).`,
|
|
9504
|
-
impl.id,
|
|
9505
|
-
isDraftCtx
|
|
9506
|
-
);
|
|
9507
|
-
}
|
|
9508
|
-
}
|
|
9509
9668
|
}
|
|
9669
|
+
for (const e of jumpEdges) {
|
|
9670
|
+
if (e.kind !== "branch" && e.kind !== "switch" && e.kind !== "jump" || e.to >= e.from) continue;
|
|
9671
|
+
const continueToLoop = regions.some((r) => r.kind === "loop" && r.h === e.to && inBody(e.from, r));
|
|
9672
|
+
if (continueToLoop) continue;
|
|
9673
|
+
ctx.addIssue(
|
|
9674
|
+
"warning",
|
|
9675
|
+
"BACKWARD_JUMP",
|
|
9676
|
+
`${where}step ${e.from} "${e.field}" jumps backwards to step ${e.to} \u2014 model repetition with a loop step (backward jumps are only idiomatic as a continue to an enclosing loop header).`,
|
|
9677
|
+
impl.id,
|
|
9678
|
+
isDraftCtx
|
|
9679
|
+
);
|
|
9680
|
+
}
|
|
9681
|
+
const byNum = /* @__PURE__ */ new Map();
|
|
9682
|
+
for (const s of steps) byNum.set(s.stepNumber, s);
|
|
9683
|
+
const nums = [...byNum.keys()].sort((a, b) => a - b);
|
|
9684
|
+
const nextOf = (n) => {
|
|
9685
|
+
const i = nums.indexOf(n);
|
|
9686
|
+
return i !== -1 && i + 1 < nums.length ? nums[i + 1] : void 0;
|
|
9687
|
+
};
|
|
9510
9688
|
for (const s of steps) {
|
|
9511
9689
|
if (s.type !== "try" || s.endStep === void 0) continue;
|
|
9512
9690
|
const handlerStarts = new Set((s.catches ?? []).map((c) => c.step));
|
|
@@ -10661,28 +10839,28 @@ var init_facade_forwarding = __esm({
|
|
|
10661
10839
|
}
|
|
10662
10840
|
});
|
|
10663
10841
|
|
|
10664
|
-
// src/core/rules/extension/
|
|
10665
|
-
var
|
|
10666
|
-
var
|
|
10667
|
-
"src/core/rules/extension/
|
|
10842
|
+
// src/core/rules/extension/profile-registration.ts
|
|
10843
|
+
var PROJECT_KINDS2, profileRegistrationRule;
|
|
10844
|
+
var init_profile_registration = __esm({
|
|
10845
|
+
"src/core/rules/extension/profile-registration.ts"() {
|
|
10668
10846
|
"use strict";
|
|
10669
10847
|
init_types();
|
|
10670
|
-
BACKEND_LIKE = /* @__PURE__ */ new Set(["backend", "lowlevel-os", "game-ecs", "realtime-embedded", "plc-cyclic"]);
|
|
10671
|
-
FRONTEND_LIKE = /* @__PURE__ */ new Set(["frontend-reactive", "frontend-controller"]);
|
|
10672
10848
|
PROJECT_KINDS2 = new Set(PROJECT_KINDS);
|
|
10673
|
-
|
|
10674
|
-
name: "
|
|
10675
|
-
description: "
|
|
10849
|
+
profileRegistrationRule = {
|
|
10850
|
+
name: "profile-registration",
|
|
10851
|
+
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.",
|
|
10676
10852
|
codes: [
|
|
10677
|
-
{ code: "FRONTEND_STEREOTYPE_IN_BACKEND", defaultSeverity: "error", summary: "Frontend stereotype in a backend-oriented subsystem" },
|
|
10678
|
-
{ code: "PLC_CYCLIC_CONCURRENCY_VIOLATION", defaultSeverity: "error", summary: "Concurrent stereotype in a PLC-cyclic profile" },
|
|
10679
|
-
{ code: "BACKEND_STEREOTYPE_IN_FRONTEND", defaultSeverity: "warning", summary: "Backend runtime stereotype in a frontend subsystem" },
|
|
10680
|
-
{ code: "PROFILE_FORBIDDEN_STEREOTYPE", defaultSeverity: "error", summary: "Stereotype forbidden by the governing pack-defined profile" },
|
|
10681
|
-
{ code: "PROFILE_DISCOURAGED_STEREOTYPE", defaultSeverity: "warning", summary: "Stereotype discouraged by the governing pack-defined profile" },
|
|
10682
10853
|
{ code: "UNKNOWN_PROFILE", defaultSeverity: "warning", summary: "Profile name is neither built-in nor registered by an extension pack" }
|
|
10683
10854
|
],
|
|
10684
10855
|
check(ctx) {
|
|
10685
10856
|
const registered = /* @__PURE__ */ new Set([...BUILTIN_PROFILES, ...Object.keys(ctx.ext.profiles)]);
|
|
10857
|
+
if (!registered.has(ctx.projectType) && !PROJECT_KINDS2.has(ctx.projectType)) {
|
|
10858
|
+
ctx.addIssue(
|
|
10859
|
+
"warning",
|
|
10860
|
+
"UNKNOWN_PROFILE",
|
|
10861
|
+
`projectType "${ctx.projectType}" (project.yaml) is neither a built-in profile/kind nor registered by a loaded extension pack. Components default to backend doctrine.`
|
|
10862
|
+
);
|
|
10863
|
+
}
|
|
10686
10864
|
for (const sub of ctx.subsystems) {
|
|
10687
10865
|
if (sub.profile && !registered.has(sub.profile)) {
|
|
10688
10866
|
ctx.addIssue(
|
|
@@ -10693,13 +10871,27 @@ var init_architectural_profiles = __esm({
|
|
|
10693
10871
|
);
|
|
10694
10872
|
}
|
|
10695
10873
|
}
|
|
10696
|
-
|
|
10697
|
-
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
|
|
10874
|
+
}
|
|
10875
|
+
};
|
|
10876
|
+
}
|
|
10877
|
+
});
|
|
10878
|
+
|
|
10879
|
+
// src/core/rules/extension/profile-stereotype-fencing.ts
|
|
10880
|
+
var BACKEND_LIKE, FRONTEND_LIKE, profileStereotypeFencingRule;
|
|
10881
|
+
var init_profile_stereotype_fencing = __esm({
|
|
10882
|
+
"src/core/rules/extension/profile-stereotype-fencing.ts"() {
|
|
10883
|
+
"use strict";
|
|
10884
|
+
BACKEND_LIKE = /* @__PURE__ */ new Set(["backend", "lowlevel-os", "game-ecs", "realtime-embedded", "plc-cyclic"]);
|
|
10885
|
+
FRONTEND_LIKE = /* @__PURE__ */ new Set(["frontend-reactive", "frontend-controller"]);
|
|
10886
|
+
profileStereotypeFencingRule = {
|
|
10887
|
+
name: "profile-stereotype-fencing",
|
|
10888
|
+
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.",
|
|
10889
|
+
codes: [
|
|
10890
|
+
{ code: "FRONTEND_STEREOTYPE_IN_BACKEND", defaultSeverity: "error", summary: "Frontend stereotype in a backend-oriented subsystem" },
|
|
10891
|
+
{ code: "PLC_CYCLIC_CONCURRENCY_VIOLATION", defaultSeverity: "error", summary: "Concurrent stereotype in a PLC-cyclic profile" },
|
|
10892
|
+
{ code: "BACKEND_STEREOTYPE_IN_FRONTEND", defaultSeverity: "warning", summary: "Backend runtime stereotype in a frontend subsystem" }
|
|
10893
|
+
],
|
|
10894
|
+
check(ctx) {
|
|
10703
10895
|
for (const comp of ctx.components) {
|
|
10704
10896
|
const isDraftCtx = ctx.isComponentDraft(comp.id);
|
|
10705
10897
|
const profile = ctx.getComponentProfile(comp.id);
|
|
@@ -10739,28 +10931,50 @@ var init_architectural_profiles = __esm({
|
|
|
10739
10931
|
);
|
|
10740
10932
|
}
|
|
10741
10933
|
}
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
10934
|
+
}
|
|
10935
|
+
}
|
|
10936
|
+
};
|
|
10937
|
+
}
|
|
10938
|
+
});
|
|
10939
|
+
|
|
10940
|
+
// src/core/rules/extension/pack-profile-stereotypes.ts
|
|
10941
|
+
var packProfileStereotypesRule;
|
|
10942
|
+
var init_pack_profile_stereotypes = __esm({
|
|
10943
|
+
"src/core/rules/extension/pack-profile-stereotypes.ts"() {
|
|
10944
|
+
"use strict";
|
|
10945
|
+
packProfileStereotypesRule = {
|
|
10946
|
+
name: "pack-profile-stereotypes",
|
|
10947
|
+
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.",
|
|
10948
|
+
codes: [
|
|
10949
|
+
{ code: "PROFILE_FORBIDDEN_STEREOTYPE", defaultSeverity: "error", summary: "Stereotype forbidden by the governing pack-defined profile" },
|
|
10950
|
+
{ code: "PROFILE_DISCOURAGED_STEREOTYPE", defaultSeverity: "warning", summary: "Stereotype discouraged by the governing pack-defined profile" }
|
|
10951
|
+
],
|
|
10952
|
+
check(ctx) {
|
|
10953
|
+
for (const comp of ctx.components) {
|
|
10954
|
+
const profile = ctx.getComponentProfile(comp.id);
|
|
10955
|
+
const packDef = ctx.ext.profiles[profile];
|
|
10956
|
+
if (!packDef) continue;
|
|
10957
|
+
const isDraftCtx = ctx.isComponentDraft(comp.id);
|
|
10958
|
+
for (const rule of packDef.forbiddenStereotypes) {
|
|
10959
|
+
if (rule.types.includes(comp.componentType)) {
|
|
10960
|
+
ctx.addIssue(
|
|
10961
|
+
"error",
|
|
10962
|
+
"PROFILE_FORBIDDEN_STEREOTYPE",
|
|
10963
|
+
`Component "${comp.id}" is a ${comp.componentType}, forbidden by profile "${profile}": ${rule.reason}`,
|
|
10964
|
+
comp.id,
|
|
10965
|
+
isDraftCtx
|
|
10966
|
+
);
|
|
10753
10967
|
}
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
|
|
10757
|
-
|
|
10758
|
-
|
|
10759
|
-
|
|
10760
|
-
|
|
10761
|
-
|
|
10762
|
-
|
|
10763
|
-
|
|
10968
|
+
}
|
|
10969
|
+
for (const rule of packDef.discouragedStereotypes) {
|
|
10970
|
+
if (rule.types.includes(comp.componentType)) {
|
|
10971
|
+
ctx.addIssue(
|
|
10972
|
+
"warning",
|
|
10973
|
+
"PROFILE_DISCOURAGED_STEREOTYPE",
|
|
10974
|
+
`Component "${comp.id}" is a ${comp.componentType}, discouraged by profile "${profile}": ${rule.reason}`,
|
|
10975
|
+
comp.id,
|
|
10976
|
+
isDraftCtx
|
|
10977
|
+
);
|
|
10764
10978
|
}
|
|
10765
10979
|
}
|
|
10766
10980
|
}
|
|
@@ -11097,22 +11311,20 @@ var init_dependency_cycles = __esm({
|
|
|
11097
11311
|
}
|
|
11098
11312
|
});
|
|
11099
11313
|
|
|
11100
|
-
// src/core/rules/wiring/dispatch-
|
|
11101
|
-
var
|
|
11102
|
-
var
|
|
11103
|
-
"src/core/rules/wiring/dispatch-
|
|
11314
|
+
// src/core/rules/wiring/dispatch-table-bindings.ts
|
|
11315
|
+
var dispatchTableBindingsRule;
|
|
11316
|
+
var init_dispatch_table_bindings = __esm({
|
|
11317
|
+
"src/core/rules/wiring/dispatch-table-bindings.ts"() {
|
|
11104
11318
|
"use strict";
|
|
11105
|
-
|
|
11106
|
-
name: "dispatch-
|
|
11107
|
-
description: "
|
|
11319
|
+
dispatchTableBindingsRule = {
|
|
11320
|
+
name: "dispatch-table-bindings",
|
|
11321
|
+
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.",
|
|
11108
11322
|
codes: [
|
|
11109
11323
|
{ code: "DISPATCH_ON_NON_PORTAL", defaultSeverity: "error", summary: "Dispatch table declared on a component that is not a Portal" },
|
|
11110
11324
|
{ code: "DUPLICATE_CAPABILITY", defaultSeverity: "error", summary: "Capability bound more than once in one dispatch table" },
|
|
11111
11325
|
{ code: "UNSERVED_CAPABILITY", defaultSeverity: "error", summary: "Capability has no existing server (bad table binding, or dispatch step routing a capability the target Portal does not serve)" },
|
|
11112
11326
|
{ code: "DISPATCH_CROSS_SUBSYSTEM", defaultSeverity: "error", summary: "Dispatch binding targets a component outside the portal's subsystem" },
|
|
11113
|
-
{ code: "UNDECLARED_DISPATCH_TARGET", defaultSeverity: "error", summary: "Dispatch binding targets a component the portal does not depend on or own" }
|
|
11114
|
-
{ code: "MALFORMED_DISPATCH_STEP", defaultSeverity: "error", summary: "Dispatch step missing its capability (targetComponent presence is the contracts rule's finding)" },
|
|
11115
|
-
{ code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Dispatch step asserts a guarantee the bound capability method does not declare" }
|
|
11327
|
+
{ code: "UNDECLARED_DISPATCH_TARGET", defaultSeverity: "error", summary: "Dispatch binding targets a component the portal does not depend on or own" }
|
|
11116
11328
|
],
|
|
11117
11329
|
check(ctx) {
|
|
11118
11330
|
for (const comp of ctx.components) {
|
|
@@ -11179,6 +11391,25 @@ var init_dispatch_tables = __esm({
|
|
|
11179
11391
|
}
|
|
11180
11392
|
}
|
|
11181
11393
|
}
|
|
11394
|
+
}
|
|
11395
|
+
};
|
|
11396
|
+
}
|
|
11397
|
+
});
|
|
11398
|
+
|
|
11399
|
+
// src/core/rules/wiring/dispatch-step-routing.ts
|
|
11400
|
+
var dispatchStepRoutingRule;
|
|
11401
|
+
var init_dispatch_step_routing = __esm({
|
|
11402
|
+
"src/core/rules/wiring/dispatch-step-routing.ts"() {
|
|
11403
|
+
"use strict";
|
|
11404
|
+
dispatchStepRoutingRule = {
|
|
11405
|
+
name: "dispatch-step-routing",
|
|
11406
|
+
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.",
|
|
11407
|
+
codes: [
|
|
11408
|
+
{ code: "MALFORMED_DISPATCH_STEP", defaultSeverity: "error", summary: "Dispatch step missing its capability (targetComponent presence is narrative-target-references' finding)" },
|
|
11409
|
+
{ code: "UNSERVED_CAPABILITY", defaultSeverity: "error", summary: "Capability has no existing server (bad table binding, or dispatch step routing a capability the target Portal does not serve)" },
|
|
11410
|
+
{ code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Dispatch step asserts a guarantee the bound capability method does not declare" }
|
|
11411
|
+
],
|
|
11412
|
+
check(ctx) {
|
|
11182
11413
|
for (const impl of ctx.implementations) {
|
|
11183
11414
|
const isDraftCtx = ctx.isImplementationDraft(impl);
|
|
11184
11415
|
for (const implMethod of impl.methods) {
|
|
@@ -11933,35 +12164,26 @@ var init_event_topology = __esm({
|
|
|
11933
12164
|
}
|
|
11934
12165
|
});
|
|
11935
12166
|
|
|
11936
|
-
// src/core/rules/conformance/
|
|
11937
|
-
var quoteList,
|
|
11938
|
-
var
|
|
11939
|
-
"src/core/rules/conformance/
|
|
12167
|
+
// src/core/rules/conformance/source-file-linkage.ts
|
|
12168
|
+
var quoteList, sourceFileLinkageRule;
|
|
12169
|
+
var init_source_file_linkage = __esm({
|
|
12170
|
+
"src/core/rules/conformance/source-file-linkage.ts"() {
|
|
11940
12171
|
"use strict";
|
|
11941
12172
|
init_models();
|
|
11942
12173
|
quoteList = (names) => names.map((n) => `"${n}"`).join(", ");
|
|
11943
|
-
|
|
11944
|
-
name: "
|
|
11945
|
-
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 file inside the project root, and
|
|
12174
|
+
sourceFileLinkageRule = {
|
|
12175
|
+
name: "source-file-linkage",
|
|
12176
|
+
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.",
|
|
11946
12177
|
codes: [
|
|
11947
12178
|
{ 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" },
|
|
11948
12179
|
{ code: "MISSING_SOURCE_FILE", defaultSeverity: "error", summary: "A source file an implementation or one of its methods names does not resolve to a file on disk" },
|
|
11949
12180
|
{ code: "SOURCE_PATH_ESCAPES_ROOT", defaultSeverity: "error", summary: "A source file an implementation or one of its methods names is absolute or escapes the project root (containment refusal)" },
|
|
11950
|
-
{ 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" },
|
|
11951
|
-
{ 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)" },
|
|
11952
12181
|
{ code: "CONFORMANCE_ANALYSIS_SKIPPED", defaultSeverity: "warning", summary: "A source file an implementation or one of its methods names could not be analyzed (binary/unreadable) \u2014 realization of the methods in it was not checked" },
|
|
11953
12182
|
{ code: "CONFORMANCE_DEGRADED", defaultSeverity: "warning", summary: "TypeScript/JavaScript files were analyzed below exact grade (compiler not resolvable) \u2014 dependency conformance skips them" }
|
|
11954
12183
|
],
|
|
11955
12184
|
check(ctx) {
|
|
11956
|
-
const
|
|
11957
|
-
for (const facts of ctx.codeModel.files)
|
|
11958
|
-
lookups.set(pathKey(facts.path), {
|
|
11959
|
-
declared: /* @__PURE__ */ new Set([...facts.declaredNames, ...facts.exportedNames]),
|
|
11960
|
-
anchored: /* @__PURE__ */ new Set([...facts.declaredNames, ...facts.exportedNames, ...facts.anchoredNames]),
|
|
11961
|
-
findingAnchors: new Set(facts.anchoredNames),
|
|
11962
|
-
facts
|
|
11963
|
-
});
|
|
11964
|
-
}
|
|
12185
|
+
const factsByPath = /* @__PURE__ */ new Map();
|
|
12186
|
+
for (const facts of ctx.codeModel.files) factsByPath.set(pathKey(facts.path), facts);
|
|
11965
12187
|
const degradedTsFiles = ctx.codeModel.files.filter(
|
|
11966
12188
|
(f) => f.status === "analyzed" && (f.language === "typescript" || f.language === "javascript") && f.analysisGrade !== "exact"
|
|
11967
12189
|
);
|
|
@@ -11979,8 +12201,7 @@ var init_structural_conformance = __esm({
|
|
|
11979
12201
|
if (!component) continue;
|
|
11980
12202
|
if (ctx.isInChainedSubproject(component.subsystem)) continue;
|
|
11981
12203
|
const draft = ctx.isImplementationDraft(impl);
|
|
11982
|
-
const
|
|
11983
|
-
const unlinked = contract.methods.filter((method2) => !methodSourceFile(methodImplOf(method2.name) ?? {}, impl.sourcePath)).map((method2) => method2.name);
|
|
12204
|
+
const unlinked = contract.methods.filter((method2) => !methodSourceFile(impl.methods.find((m) => m.name === method2.name) ?? {}, impl.sourcePath)).map((method2) => method2.name);
|
|
11984
12205
|
const noFileAtAll = implementationSourceFiles(impl).length === 0;
|
|
11985
12206
|
if (unlinked.length > 0 || noFileAtAll) {
|
|
11986
12207
|
const hasExternalSource = (component.externalLinks ?? []).some((l) => l.type === "implementation");
|
|
@@ -12010,7 +12231,7 @@ var init_structural_conformance = __esm({
|
|
|
12010
12231
|
const key = pathKey(file);
|
|
12011
12232
|
if (reported.has(key)) continue;
|
|
12012
12233
|
reported.add(key);
|
|
12013
|
-
const facts =
|
|
12234
|
+
const facts = factsByPath.get(key);
|
|
12014
12235
|
if (!facts || facts.status === "analyzed") continue;
|
|
12015
12236
|
const isImplementationFile = !!impl.sourcePath && pathKey(impl.sourcePath) === key;
|
|
12016
12237
|
const users = isImplementationFile ? [] : impl.methods.filter((m) => m.sourcePath && pathKey(m.sourcePath) === key).map((m) => m.name);
|
|
@@ -12041,22 +12262,49 @@ var init_structural_conformance = __esm({
|
|
|
12041
12262
|
);
|
|
12042
12263
|
}
|
|
12043
12264
|
}
|
|
12265
|
+
}
|
|
12266
|
+
}
|
|
12267
|
+
};
|
|
12268
|
+
}
|
|
12269
|
+
});
|
|
12270
|
+
|
|
12271
|
+
// src/core/rules/conformance/method-realization.ts
|
|
12272
|
+
var methodRealizationRule;
|
|
12273
|
+
var init_method_realization = __esm({
|
|
12274
|
+
"src/core/rules/conformance/method-realization.ts"() {
|
|
12275
|
+
"use strict";
|
|
12276
|
+
init_models();
|
|
12277
|
+
methodRealizationRule = {
|
|
12278
|
+
name: "method-realization",
|
|
12279
|
+
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. 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.",
|
|
12280
|
+
codes: [
|
|
12281
|
+
{ 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" }
|
|
12282
|
+
],
|
|
12283
|
+
check(ctx) {
|
|
12284
|
+
const lookups = /* @__PURE__ */ new Map();
|
|
12285
|
+
for (const facts of ctx.codeModel.files) {
|
|
12286
|
+
lookups.set(pathKey(facts.path), {
|
|
12287
|
+
declared: /* @__PURE__ */ new Set([...facts.declaredNames, ...facts.exportedNames]),
|
|
12288
|
+
anchored: /* @__PURE__ */ new Set([...facts.declaredNames, ...facts.exportedNames, ...facts.anchoredNames]),
|
|
12289
|
+
facts
|
|
12290
|
+
});
|
|
12291
|
+
}
|
|
12292
|
+
for (const impl of ctx.implementations) {
|
|
12293
|
+
const contract = ctx.interfaceMap.get(impl.contract);
|
|
12294
|
+
if (!contract) continue;
|
|
12295
|
+
const component = ctx.componentMap.get(contract.component);
|
|
12296
|
+
if (!component) continue;
|
|
12297
|
+
if (ctx.isInChainedSubproject(component.subsystem)) continue;
|
|
12298
|
+
const draft = ctx.isImplementationDraft(impl);
|
|
12044
12299
|
const specTier = impl.conformance ?? defaultConformanceTier(component);
|
|
12045
|
-
const
|
|
12046
|
-
const methodImpl =
|
|
12300
|
+
for (const method2 of contract.methods) {
|
|
12301
|
+
const methodImpl = impl.methods.find((m) => m.name === method2.name);
|
|
12047
12302
|
const tier = methodImpl?.conformance ?? specTier;
|
|
12048
|
-
if (tier === "off")
|
|
12303
|
+
if (tier === "off") continue;
|
|
12049
12304
|
const file = methodSourceFile(methodImpl ?? {}, impl.sourcePath);
|
|
12050
|
-
if (!file)
|
|
12305
|
+
if (!file) continue;
|
|
12051
12306
|
const lookup = lookups.get(pathKey(file));
|
|
12052
|
-
if (!lookup || lookup.facts.status !== "analyzed")
|
|
12053
|
-
return { file, tier, lookup };
|
|
12054
|
-
};
|
|
12055
|
-
for (const method2 of contract.methods) {
|
|
12056
|
-
const checked = checkedFile(method2.name);
|
|
12057
|
-
if (!checked) continue;
|
|
12058
|
-
const { file, tier, lookup } = checked;
|
|
12059
|
-
const methodImpl = methodImplOf(method2.name);
|
|
12307
|
+
if (!lookup || lookup.facts.status !== "analyzed") continue;
|
|
12060
12308
|
const symbol = methodImpl?.symbol ?? method2.name;
|
|
12061
12309
|
const inDeclared = lookup.declared.has(symbol);
|
|
12062
12310
|
const inAnchored = inDeclared || lookup.anchored.has(symbol);
|
|
@@ -12072,11 +12320,46 @@ var init_structural_conformance = __esm({
|
|
|
12072
12320
|
draft
|
|
12073
12321
|
);
|
|
12074
12322
|
}
|
|
12323
|
+
}
|
|
12324
|
+
}
|
|
12325
|
+
};
|
|
12326
|
+
}
|
|
12327
|
+
});
|
|
12328
|
+
|
|
12329
|
+
// src/core/rules/conformance/finding-realization.ts
|
|
12330
|
+
var findingRealizationRule;
|
|
12331
|
+
var init_finding_realization = __esm({
|
|
12332
|
+
"src/core/rules/conformance/finding-realization.ts"() {
|
|
12333
|
+
"use strict";
|
|
12334
|
+
init_models();
|
|
12335
|
+
findingRealizationRule = {
|
|
12336
|
+
name: "finding-realization",
|
|
12337
|
+
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.",
|
|
12338
|
+
codes: [
|
|
12339
|
+
{ 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)" }
|
|
12340
|
+
],
|
|
12341
|
+
check(ctx) {
|
|
12342
|
+
const lookups = /* @__PURE__ */ new Map();
|
|
12343
|
+
for (const facts of ctx.codeModel.files) {
|
|
12344
|
+
lookups.set(pathKey(facts.path), { findingAnchors: new Set(facts.anchoredNames), facts });
|
|
12345
|
+
}
|
|
12346
|
+
for (const impl of ctx.implementations) {
|
|
12347
|
+
const contract = ctx.interfaceMap.get(impl.contract);
|
|
12348
|
+
if (!contract) continue;
|
|
12349
|
+
const component = ctx.componentMap.get(contract.component);
|
|
12350
|
+
if (!component) continue;
|
|
12351
|
+
if (ctx.isInChainedSubproject(component.subsystem)) continue;
|
|
12352
|
+
const draft = ctx.isImplementationDraft(impl);
|
|
12353
|
+
const specTier = impl.conformance ?? defaultConformanceTier(component);
|
|
12075
12354
|
for (const method2 of contract.methods) {
|
|
12076
12355
|
if (!method2.findings?.length) continue;
|
|
12077
|
-
const
|
|
12078
|
-
|
|
12079
|
-
|
|
12356
|
+
const methodImpl = impl.methods.find((m) => m.name === method2.name);
|
|
12357
|
+
const tier = methodImpl?.conformance ?? specTier;
|
|
12358
|
+
if (tier === "off") continue;
|
|
12359
|
+
const file = methodSourceFile(methodImpl ?? {}, impl.sourcePath);
|
|
12360
|
+
if (!file) continue;
|
|
12361
|
+
const lookup = lookups.get(pathKey(file));
|
|
12362
|
+
if (!lookup || lookup.facts.status !== "analyzed") continue;
|
|
12080
12363
|
for (const finding of method2.findings) {
|
|
12081
12364
|
if (lookup.findingAnchors.has(finding.code)) continue;
|
|
12082
12365
|
ctx.addIssue(
|
|
@@ -13658,12 +13941,19 @@ var init_repository = __esm({
|
|
|
13658
13941
|
init_hierarchy_integrity();
|
|
13659
13942
|
init_namespace_hygiene();
|
|
13660
13943
|
init_roundtrip_serialization();
|
|
13661
|
-
|
|
13944
|
+
init_type_declarations();
|
|
13945
|
+
init_field_type_references();
|
|
13946
|
+
init_signature_type_references();
|
|
13662
13947
|
init_public_surface();
|
|
13663
13948
|
init_lint_allows();
|
|
13664
|
-
|
|
13949
|
+
init_contract_symmetry();
|
|
13950
|
+
init_narrative_target_references();
|
|
13951
|
+
init_cross_tree_references();
|
|
13952
|
+
init_surface_reference_backing();
|
|
13665
13953
|
init_guarantee_tokens();
|
|
13666
|
-
|
|
13954
|
+
init_narrative_step_config();
|
|
13955
|
+
init_narrative_reachability();
|
|
13956
|
+
init_narrative_jump_edges();
|
|
13667
13957
|
init_narrative_antipatterns();
|
|
13668
13958
|
init_narrative_detail();
|
|
13669
13959
|
init_portal_fields();
|
|
@@ -13675,14 +13965,17 @@ var init_repository = __esm({
|
|
|
13675
13965
|
init_stereotype_dependencies();
|
|
13676
13966
|
init_pattern_ownership();
|
|
13677
13967
|
init_facade_forwarding();
|
|
13678
|
-
|
|
13968
|
+
init_profile_registration();
|
|
13969
|
+
init_profile_stereotype_fencing();
|
|
13970
|
+
init_pack_profile_stereotypes();
|
|
13679
13971
|
init_pattern_references();
|
|
13680
13972
|
init_component_variants();
|
|
13681
13973
|
init_declarative_assertions();
|
|
13682
13974
|
init_pack_resolution();
|
|
13683
13975
|
init_pack_reproducibility();
|
|
13684
13976
|
init_dependency_cycles();
|
|
13685
|
-
|
|
13977
|
+
init_dispatch_table_bindings();
|
|
13978
|
+
init_dispatch_step_routing();
|
|
13686
13979
|
init_lifecycle_entrypoints();
|
|
13687
13980
|
init_unused_detection();
|
|
13688
13981
|
init_durability_round_trip();
|
|
@@ -13690,7 +13983,9 @@ var init_repository = __esm({
|
|
|
13690
13983
|
init_prose_claims();
|
|
13691
13984
|
init_invariant_backing();
|
|
13692
13985
|
init_event_topology();
|
|
13693
|
-
|
|
13986
|
+
init_source_file_linkage();
|
|
13987
|
+
init_method_realization();
|
|
13988
|
+
init_finding_realization();
|
|
13694
13989
|
init_call_conformance();
|
|
13695
13990
|
init_hidden_state();
|
|
13696
13991
|
init_dependency_conformance();
|
|
@@ -13709,12 +14004,30 @@ var init_repository = __esm({
|
|
|
13709
14004
|
// explain many downstream findings, so surface them early in the list.
|
|
13710
14005
|
namespaceHygieneRule,
|
|
13711
14006
|
roundtripRule,
|
|
13712
|
-
|
|
13713
|
-
|
|
14007
|
+
// The type vocabulary in three questions: what a type declares about
|
|
14008
|
+
// itself, then the identifiers its fields name, then the ones its
|
|
14009
|
+
// contracts' signatures name.
|
|
14010
|
+
typeDeclarationsRule,
|
|
14011
|
+
fieldTypeReferencesRule,
|
|
14012
|
+
signatureTypeReferencesRule,
|
|
14013
|
+
// Contracts and the targets narratives name, in four questions with one
|
|
14014
|
+
// owner each: does the implementation mirror its contract, does a target
|
|
14015
|
+
// inside this tree resolve, does a target that leaves it pin to exactly one
|
|
14016
|
+
// declared surface, and does that surface back what the step asks of it.
|
|
14017
|
+
contractSymmetryRule,
|
|
14018
|
+
narrativeTargetReferencesRule,
|
|
14019
|
+
crossTreeReferencesRule,
|
|
14020
|
+
surfaceReferenceBackingRule,
|
|
13714
14021
|
// Vocabulary check right after contracts: an unknown token explains why the
|
|
13715
14022
|
// consistency findings around it are absent, so surface them together.
|
|
13716
14023
|
guaranteeTokensRule,
|
|
13717
|
-
|
|
14024
|
+
// Narrative control flow in three questions: does each step carry the
|
|
14025
|
+
// config its type requires, can every step be reached (and do the regions
|
|
14026
|
+
// nest), and where do the jump edges land. The last two run behind the
|
|
14027
|
+
// first's soundness verdict.
|
|
14028
|
+
narrativeStepConfigRule,
|
|
14029
|
+
narrativeReachabilityRule,
|
|
14030
|
+
narrativeJumpEdgesRule,
|
|
13718
14031
|
// Antipatterns right after flow soundness: they analyze the same step
|
|
13719
14032
|
// graphs and only make sense once the graphs are structurally valid.
|
|
13720
14033
|
narrativeAntipatternsRule,
|
|
@@ -13731,7 +14044,12 @@ var init_repository = __esm({
|
|
|
13731
14044
|
patternsRule,
|
|
13732
14045
|
// Facade shape rides with pattern ownership: same §7 doctrine, narrative side.
|
|
13733
14046
|
facadeForwardingRule,
|
|
13734
|
-
|
|
14047
|
+
// Profiles in three questions, three owners: is the name real (the
|
|
14048
|
+
// project's config), does the built-in family doctrine allow this
|
|
14049
|
+
// stereotype (wairon), does the pack's declared doctrine allow it (the pack).
|
|
14050
|
+
profileRegistrationRule,
|
|
14051
|
+
profileStereotypeFencingRule,
|
|
14052
|
+
packProfileStereotypesRule,
|
|
13735
14053
|
patternReferencesRule,
|
|
13736
14054
|
variantReferencesRule,
|
|
13737
14055
|
// Pack-instantiated declarative doctrine rides with the pack-reference
|
|
@@ -13742,7 +14060,8 @@ var init_repository = __esm({
|
|
|
13742
14060
|
// Semantic-edge family: dispatch/lifecycle validity BEFORE reachability so a
|
|
13743
14061
|
// reader sees the broken edge finding next to the unused-detection fallout
|
|
13744
14062
|
// it explains.
|
|
13745
|
-
|
|
14063
|
+
dispatchTableBindingsRule,
|
|
14064
|
+
dispatchStepRoutingRule,
|
|
13746
14065
|
lifecycleRule,
|
|
13747
14066
|
reachabilityRule,
|
|
13748
14067
|
// The declaration (spec-scoped, refused at the write boundary) before the
|
|
@@ -13761,9 +14080,14 @@ var init_repository = __esm({
|
|
|
13761
14080
|
// Pub/sub completeness: emitted topics need subscribers and vice versa.
|
|
13762
14081
|
eventTopologyRule,
|
|
13763
14082
|
// Code↔spec: structural conformance consumes the injected CodeModel (built
|
|
13764
|
-
// by the source analysis adapter next to the surface snapshots)
|
|
13765
|
-
//
|
|
13766
|
-
|
|
14083
|
+
// by the source analysis adapter next to the surface snapshots) and asks it
|
|
14084
|
+
// three questions — does the spec name files that exist, does the file
|
|
14085
|
+
// contain the method, does it report the codes the method declares;
|
|
14086
|
+
// dependency conformance lifts its import edges onto the declared
|
|
14087
|
+
// dependsOn/owns graph.
|
|
14088
|
+
sourceFileLinkageRule,
|
|
14089
|
+
methodRealizationRule,
|
|
14090
|
+
findingRealizationRule,
|
|
13767
14091
|
// Level 3 opener: narrative call steps must be realized as callees of the
|
|
13768
14092
|
// realized function (set membership, exact grade).
|
|
13769
14093
|
callConformanceRule,
|
|
@@ -26116,7 +26440,7 @@ NOTICE:
|
|
|
26116
26440
|
simPath: import_zod11.z.string().optional().describe("Optional: the committed integration-sim harness file (project-relative; N:1 sharing allowed). The validator proves it exists and its import graph wires the REAL modules (this component + each direct dependency; technology adapters may stay faked) \u2014 running it is CI's job. Declaring the first simPath in a subsystem activates MISSING_INTEGRATION_SIM for its other complete non-leaf implementations"),
|
|
26117
26441
|
technologies: import_zod11.z.array(import_zod11.z.string()).optional().describe(`External technologies this implementation binds to (e.g. ["mysql"]) \u2014 declares this component's ownership tree as the technology's home; references outside it are flagged (TECH_LEAKAGE) and contract identifiers must stay intent-language. Only for Adapter/Store/Registry/Index components.`),
|
|
26118
26442
|
detail: detailEnum.optional().describe("Spec-level narrative detail default for all methods"),
|
|
26119
|
-
conformance: conformanceEnum.optional().describe("Spec-level
|
|
26443
|
+
conformance: conformanceEnum.optional().describe("Spec-level conformance tier default: declared | anchored | off (omitted = stereotype default: Portal \u2192 anchored, else declared)"),
|
|
26120
26444
|
methods: import_zod11.z.array(import_zod11.z.object(implMethodShape)).optional().describe("Method implementations containing L5 narratives")
|
|
26121
26445
|
};
|
|
26122
26446
|
const implInputFields = Object.keys(implInput);
|