@wairon/cli 5.1.1-dev.24 → 5.1.1-dev.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1113,7 +1113,7 @@ var init_defaults = __esm({
1113
1113
  copilot: ".github/prompts",
1114
1114
  codex: ".codex/agents"
1115
1115
  };
1116
- WAIRON_VERSION = "5.1.1-dev.24";
1116
+ WAIRON_VERSION = "5.1.1-dev.26";
1117
1117
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
1118
1118
  ARCHITECT_AGENT_ID = "agent-architect";
1119
1119
  ARCHITECT_TEMPLATE_ID = "architect";
@@ -1997,7 +1997,7 @@ var init_specs = __esm({
1997
1997
  * entity's write methods must carry (UNASSERTED_INVARIANT otherwise).
1998
1998
  */
1999
1999
  assertsInvariants: import_zod8.z.array(import_zod8.z.string()).optional(),
2000
- // --- flow config (per type; validated by the narrative-flow rule) ---------
2000
+ // --- flow config (per type; validated by narrative-step-config) -----------
2001
2001
  condition: import_zod8.z.string().optional(),
2002
2002
  // branch; loop (while/doWhile)
2003
2003
  onTrueStep: import_zod8.z.number().int().positive().optional(),
@@ -2098,7 +2098,7 @@ var init_specs = __esm({
2098
2098
  methods: import_zod8.z.array(MethodImplementationSchema).default([]),
2099
2099
  /** Spec-level narrative detail default for all methods (each may override). */
2100
2100
  detail: NarrativeDetailSchema.optional(),
2101
- /** Spec-level structural-conformance tier default (each method may override). */
2101
+ /** Spec-level conformance tier default (each method may override). */
2102
2102
  conformance: ConformanceTierSchema.optional(),
2103
2103
  /** Per-spec lint suppressions (see LintConfigSchema). */
2104
2104
  lint: LintConfigSchema.optional(),
@@ -2529,6 +2529,149 @@ var init_step_graph = __esm({
2529
2529
  }
2530
2530
  });
2531
2531
 
2532
+ // src/models/step-config.ts
2533
+ function flowConfigOn(step) {
2534
+ return FLOW_FIELDS.filter((f) => {
2535
+ const v = step[f];
2536
+ if (v === void 0) return false;
2537
+ if (Array.isArray(v) && v.length === 0) return false;
2538
+ return true;
2539
+ });
2540
+ }
2541
+ function stepConfigVerdict(method) {
2542
+ const steps = method.narrative ?? [];
2543
+ const problems = [];
2544
+ let sound = true;
2545
+ const malformed = (stepNumber, detail) => {
2546
+ sound = false;
2547
+ problems.push({ stepNumber, code: "MALFORMED_FLOW_STEP", detail });
2548
+ };
2549
+ const labelled = /* @__PURE__ */ new Map();
2550
+ for (const step of steps) {
2551
+ const label = step.label;
2552
+ if (!label) continue;
2553
+ const first = labelled.get(label);
2554
+ if (first !== void 0) {
2555
+ problems.push({
2556
+ stepNumber: step.stepNumber,
2557
+ code: "DUPLICATE_STEP_LABEL",
2558
+ 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.`
2559
+ });
2560
+ } else {
2561
+ labelled.set(label, step.stepNumber);
2562
+ }
2563
+ }
2564
+ const byNum = /* @__PURE__ */ new Map();
2565
+ for (const s of steps) {
2566
+ if (byNum.has(s.stepNumber)) malformed(s.stepNumber, `duplicate stepNumber ${s.stepNumber} \u2014 jump targets would be ambiguous.`);
2567
+ byNum.set(s.stepNumber, s);
2568
+ }
2569
+ const nums = [...byNum.keys()].sort((a, b) => a - b);
2570
+ const indexOf = new Map(nums.map((n, i) => [n, i]));
2571
+ const nextOf = (n) => {
2572
+ const i = indexOf.get(n);
2573
+ return i !== void 0 && i + 1 < nums.length ? nums[i + 1] : void 0;
2574
+ };
2575
+ for (const s of steps) {
2576
+ if (s.detach && s.type !== "call" && s.type !== "dispatch") {
2577
+ malformed(s.stepNumber, `step ${s.stepNumber} (${s.type}) carries "detach" \u2014 fire-and-forget applies to call/dispatch steps only.`);
2578
+ }
2579
+ switch (s.type) {
2580
+ case "local":
2581
+ case "call":
2582
+ case "register":
2583
+ case "dispatch": {
2584
+ const extra = flowConfigOn(s);
2585
+ if (extra.length) malformed(s.stepNumber, `step ${s.stepNumber} (${s.type}) carries flow config (${extra.join(", ")}) \u2014 use a flow step type instead.`);
2586
+ break;
2587
+ }
2588
+ case "branch":
2589
+ if (!s.condition) malformed(s.stepNumber, `branch step ${s.stepNumber} requires "condition".`);
2590
+ if (s.onFalseStep === void 0) malformed(s.stepNumber, `branch step ${s.stepNumber} requires "onFalseStep" (true continues at onTrueStep or the next step).`);
2591
+ break;
2592
+ case "switch":
2593
+ if (!s.cases || !s.cases.length) malformed(s.stepNumber, `switch step ${s.stepNumber} requires non-empty "cases".`);
2594
+ break;
2595
+ case "loop": {
2596
+ if (s.endStep === void 0) malformed(s.stepNumber, `loop step ${s.stepNumber} requires "endStep" (last step of the body).`);
2597
+ else if (s.endStep <= s.stepNumber) malformed(s.stepNumber, `loop step ${s.stepNumber} "endStep" (${s.endStep}) must lie beyond the header.`);
2598
+ const kind = s.loopKind ?? (s.over ? "forEach" : "while");
2599
+ if ((kind === "while" || kind === "doWhile") && !s.condition) malformed(s.stepNumber, `${kind} loop step ${s.stepNumber} requires "condition".`);
2600
+ if ((kind === "forEach" || kind === "for") && !s.over) malformed(s.stepNumber, `${kind} loop step ${s.stepNumber} requires "over" (the iteration source).`);
2601
+ break;
2602
+ }
2603
+ case "try":
2604
+ if (s.endStep === void 0) malformed(s.stepNumber, `try step ${s.stepNumber} requires "endStep" (last step of the guarded body).`);
2605
+ else if (s.endStep <= s.stepNumber) malformed(s.stepNumber, `try step ${s.stepNumber} "endStep" (${s.endStep}) must lie beyond the header.`);
2606
+ 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.`);
2607
+ break;
2608
+ case "parallel": {
2609
+ if (s.endStep === void 0) malformed(s.stepNumber, `parallel step ${s.stepNumber} requires "endStep" (last step of the fan-out body).`);
2610
+ else if (s.endStep <= s.stepNumber) malformed(s.stepNumber, `parallel step ${s.stepNumber} "endStep" (${s.endStep}) must lie beyond the header.`);
2611
+ const entries = (s.branches ?? []).map((b) => b.step);
2612
+ if (entries.length < 2) {
2613
+ malformed(s.stepNumber, `parallel step ${s.stepNumber} requires "branches" with at least two arms \u2014 a single arm is sequential flow.`);
2614
+ } else {
2615
+ const sorted = [...entries].sort((a, b) => a - b);
2616
+ 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.`);
2617
+ if (new Set(entries).size !== entries.length) malformed(s.stepNumber, `parallel step ${s.stepNumber} "branches" lists the same entry step twice.`);
2618
+ const bodyStart = nextOf(s.stepNumber);
2619
+ 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.`);
2620
+ 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}).`);
2621
+ }
2622
+ break;
2623
+ }
2624
+ case "jump":
2625
+ if (s.toStep === void 0) malformed(s.stepNumber, `jump step ${s.stepNumber} requires "toStep".`);
2626
+ break;
2627
+ }
2628
+ for (const [field, target] of jumpFieldsOf(s)) {
2629
+ if (byNum.has(target)) continue;
2630
+ sound = false;
2631
+ problems.push({
2632
+ stepNumber: s.stepNumber,
2633
+ code: "INVALID_STEP_JUMP",
2634
+ detail: `step ${s.stepNumber} "${field}" targets step ${target}, which does not exist in this narrative.`
2635
+ });
2636
+ }
2637
+ }
2638
+ return { problems, sound };
2639
+ }
2640
+ function jumpFieldsOf(s) {
2641
+ const fields = [];
2642
+ if (s.onTrueStep !== void 0) fields.push(["onTrueStep", s.onTrueStep]);
2643
+ if (s.onFalseStep !== void 0) fields.push(["onFalseStep", s.onFalseStep]);
2644
+ if (s.defaultStep !== void 0) fields.push(["defaultStep", s.defaultStep]);
2645
+ if (s.endStep !== void 0) fields.push(["endStep", s.endStep]);
2646
+ if (s.finallyStep !== void 0) fields.push(["finallyStep", s.finallyStep]);
2647
+ if (s.toStep !== void 0) fields.push(["toStep", s.toStep]);
2648
+ (s.cases ?? []).forEach((c, i) => fields.push([`cases[${i}].step`, c.step]));
2649
+ (s.catches ?? []).forEach((c, i) => fields.push([`catches[${i}].step`, c.step]));
2650
+ (s.branches ?? []).forEach((b, i) => fields.push([`branches[${i}].step`, b.step]));
2651
+ return fields;
2652
+ }
2653
+ var FLOW_FIELDS;
2654
+ var init_step_config = __esm({
2655
+ "src/models/step-config.ts"() {
2656
+ "use strict";
2657
+ FLOW_FIELDS = [
2658
+ "condition",
2659
+ "onTrueStep",
2660
+ "onFalseStep",
2661
+ "on",
2662
+ "cases",
2663
+ "defaultStep",
2664
+ "loopKind",
2665
+ "over",
2666
+ "endStep",
2667
+ "catches",
2668
+ "finallyStep",
2669
+ "branches",
2670
+ "toStep"
2671
+ ];
2672
+ }
2673
+ });
2674
+
2532
2675
  // src/models/code-model.ts
2533
2676
  function pathKey(sourcePath) {
2534
2677
  return sourcePath.replace(/\\/g, "/").replace(/^\.\//, "");
@@ -2596,6 +2739,7 @@ var init_models = __esm({
2596
2739
  init_specs();
2597
2740
  init_type_references();
2598
2741
  init_step_graph();
2742
+ init_step_config();
2599
2743
  init_code_model();
2600
2744
  init_surface_references();
2601
2745
  }
@@ -8550,6 +8694,209 @@ var init_types = __esm({
8550
8694
  }
8551
8695
  });
8552
8696
 
8697
+ // src/core/rules/read-model.ts
8698
+ function appendUnique(map, key, value, same) {
8699
+ const list = map.get(key);
8700
+ if (!list) {
8701
+ map.set(key, [value]);
8702
+ return;
8703
+ }
8704
+ if (same ? list.some((v) => same(v, value)) : list.includes(value)) return;
8705
+ list.push(value);
8706
+ }
8707
+ function buildCodeIndex(model) {
8708
+ const facts = /* @__PURE__ */ new Map();
8709
+ const paths = /* @__PURE__ */ new Set();
8710
+ const exactPaths = /* @__PURE__ */ new Set();
8711
+ for (const f of model.files) {
8712
+ const key = pathKey(f.path);
8713
+ facts.set(key, f);
8714
+ paths.add(key);
8715
+ if (f.status === "analyzed" && f.analysisGrade === "exact") exactPaths.add(key);
8716
+ }
8717
+ const declarations = /* @__PURE__ */ new Map();
8718
+ const anchors = /* @__PURE__ */ new Map();
8719
+ const findingAnchors = /* @__PURE__ */ new Map();
8720
+ const namesAt = (cache, path27, make) => {
8721
+ const cached = cache.get(path27);
8722
+ if (cached) return cached;
8723
+ const f = facts.get(path27);
8724
+ if (!f) return NO_NAMES;
8725
+ const built = make(f);
8726
+ cache.set(path27, built);
8727
+ return built;
8728
+ };
8729
+ return {
8730
+ paths,
8731
+ exactPaths,
8732
+ factsAt: (path27) => facts.get(pathKey(path27)),
8733
+ declarationsAt: (path27) => namesAt(declarations, pathKey(path27), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames])),
8734
+ anchorsAt: (path27) => namesAt(anchors, pathKey(path27), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
8735
+ findingAnchorsAt: (path27) => namesAt(findingAnchors, pathKey(path27), (f) => new Set(f.anchoredNames))
8736
+ };
8737
+ }
8738
+ function buildRealizationIndex(ctx) {
8739
+ const implsByComponent = /* @__PURE__ */ new Map();
8740
+ const implsByPath = /* @__PURE__ */ new Map();
8741
+ const filesByComponent = /* @__PURE__ */ new Map();
8742
+ const filesBySubsystem = /* @__PURE__ */ new Map();
8743
+ const componentsByPath = /* @__PURE__ */ new Map();
8744
+ const paths = [];
8745
+ const seenPaths = /* @__PURE__ */ new Set();
8746
+ for (const impl of ctx.implementations) {
8747
+ const contract = ctx.interfaceMap.get(impl.contract);
8748
+ if (!contract) continue;
8749
+ const component = ctx.componentMap.get(contract.component);
8750
+ if (!component) continue;
8751
+ if (ctx.isInChainedSubproject(component.subsystem)) continue;
8752
+ appendUnique(implsByComponent, component.id, impl);
8753
+ for (const file of implementationSourceFiles(impl)) {
8754
+ const p = pathKey(file);
8755
+ if (!seenPaths.has(p)) {
8756
+ seenPaths.add(p);
8757
+ paths.push(p);
8758
+ }
8759
+ appendUnique(implsByPath, p, impl);
8760
+ appendUnique(filesByComponent, component.id, p);
8761
+ appendUnique(filesBySubsystem, component.subsystem, p);
8762
+ appendUnique(componentsByPath, p, component, (a, b) => a.id === b.id);
8763
+ }
8764
+ }
8765
+ const NONE_IMPL = [];
8766
+ const NONE_COMP = [];
8767
+ const NONE_PATH = [];
8768
+ return {
8769
+ paths,
8770
+ implementationsOf: (compId) => implsByComponent.get(compId) ?? NONE_IMPL,
8771
+ implementationsAt: (path27) => implsByPath.get(pathKey(path27)) ?? NONE_IMPL,
8772
+ filesOf: (compId) => filesByComponent.get(compId) ?? NONE_PATH,
8773
+ filesIn: (subsystemId) => filesBySubsystem.get(subsystemId) ?? NONE_PATH,
8774
+ componentsAt: (path27) => componentsByPath.get(pathKey(path27)) ?? NONE_COMP
8775
+ };
8776
+ }
8777
+ function buildImportGraph(index, universe) {
8778
+ const imports = /* @__PURE__ */ new Map();
8779
+ const traces = /* @__PURE__ */ new Map();
8780
+ const resolveAll = (from, specifiers, into) => {
8781
+ for (const specifier of specifiers) {
8782
+ const to = resolveImport(from, specifier, universe);
8783
+ if (to && to !== from) into.add(to);
8784
+ }
8785
+ };
8786
+ const importsOf = (path27) => {
8787
+ const cached = imports.get(path27);
8788
+ if (cached) return cached;
8789
+ const facts = index.factsAt(path27);
8790
+ const out = /* @__PURE__ */ new Set();
8791
+ if (facts?.status === "analyzed") resolveAll(path27, facts.imports, out);
8792
+ imports.set(path27, out);
8793
+ return out;
8794
+ };
8795
+ const tracesOf = (path27) => {
8796
+ const cached = traces.get(path27);
8797
+ if (cached) return cached;
8798
+ const facts = index.factsAt(path27);
8799
+ const out = new Set(importsOf(path27));
8800
+ if (facts?.status === "analyzed") resolveAll(path27, facts.reexports, out);
8801
+ traces.set(path27, out);
8802
+ return out;
8803
+ };
8804
+ return {
8805
+ paths: [...universe],
8806
+ importsOf,
8807
+ tracesOf,
8808
+ connects: (from, to, allowReverse) => {
8809
+ for (const f of from) {
8810
+ const forward = tracesOf(f);
8811
+ for (const t of to) if (forward.has(t)) return true;
8812
+ }
8813
+ if (allowReverse) {
8814
+ for (const t of to) {
8815
+ const reverse = tracesOf(t);
8816
+ for (const f of from) if (reverse.has(f)) return true;
8817
+ }
8818
+ }
8819
+ return false;
8820
+ },
8821
+ reachFrom: (start) => {
8822
+ const seen = /* @__PURE__ */ new Set([start]);
8823
+ const stack = [start];
8824
+ while (stack.length) {
8825
+ const from = stack.pop();
8826
+ for (const to of tracesOf(from)) {
8827
+ if (seen.has(to)) continue;
8828
+ seen.add(to);
8829
+ stack.push(to);
8830
+ }
8831
+ }
8832
+ return seen;
8833
+ }
8834
+ };
8835
+ }
8836
+ function buildOwnershipIndex(ctx) {
8837
+ const ownedBy = /* @__PURE__ */ new Map();
8838
+ for (const comp of ctx.components) {
8839
+ if (isRetired(comp) || !isPattern(comp)) continue;
8840
+ for (const memberId of comp.owns) {
8841
+ const member = ctx.componentMap.get(memberId);
8842
+ if (!member || isPattern(member)) continue;
8843
+ if (!ownedBy.has(memberId)) ownedBy.set(memberId, comp.id);
8844
+ }
8845
+ }
8846
+ return {
8847
+ ownedMembers: new Set(ownedBy.keys()),
8848
+ ownerOf: (memberId) => ownedBy.get(memberId)
8849
+ };
8850
+ }
8851
+ function buildDependencyEdges(ctx) {
8852
+ const all = [];
8853
+ const matrix = [];
8854
+ for (const from of ctx.components) {
8855
+ const fromDraft = ctx.isComponentDraft(from.id);
8856
+ const profile = ctx.ext.profiles[ctx.getComponentProfile(from.id)];
8857
+ for (const ref of from.dependsOn) {
8858
+ const to = ctx.componentMap.get(ref);
8859
+ if (!to) {
8860
+ all.push(offTreeEdge(ctx, from, ref, fromDraft));
8861
+ continue;
8862
+ }
8863
+ const edge = {
8864
+ from,
8865
+ ref,
8866
+ to,
8867
+ reach: to.subsystem === from.subsystem ? "internal" : "cross-subsystem",
8868
+ retired: isRetired(from) || isRetired(to),
8869
+ draftContext: fromDraft || ctx.isComponentDraft(to.id),
8870
+ licensed: profile?.allowedEdges?.some(
8871
+ (e) => e.from.includes(from.componentType) && e.to.includes(to.componentType)
8872
+ ) ?? false
8873
+ };
8874
+ all.push(edge);
8875
+ if (edge.reach === "internal" && !edge.retired && !edge.licensed) matrix.push(edge);
8876
+ }
8877
+ }
8878
+ return { all, matrix };
8879
+ }
8880
+ function offTreeEdge(ctx, from, ref, fromDraft) {
8881
+ const base = { from, ref, retired: false, draftContext: fromDraft, licensed: false };
8882
+ const external = ctx.isExternalNamespaceRef(ref);
8883
+ if (external || ctx.isCollapsedCrossTreeRef(ref, from.subsystem)) {
8884
+ const surface = ctx.resolveSurfaceRef(ref, from.subsystem);
8885
+ if (surface.kind === "ambiguous") return { ...base, reach: "ambiguous", surface };
8886
+ if (surface.kind === "resolved") return { ...base, reach: "surface", surface };
8887
+ if (external) return { ...base, reach: "unpinned" };
8888
+ }
8889
+ return { ...base, reach: "missing" };
8890
+ }
8891
+ var NO_NAMES;
8892
+ var init_read_model = __esm({
8893
+ "src/core/rules/read-model.ts"() {
8894
+ "use strict";
8895
+ init_models();
8896
+ NO_NAMES = /* @__PURE__ */ new Set();
8897
+ }
8898
+ });
8899
+
8553
8900
  // src/core/rules/integrity/hierarchy-integrity.ts
8554
8901
  var hierarchyRule;
8555
8902
  var init_hierarchy_integrity = __esm({
@@ -8697,18 +9044,16 @@ var init_roundtrip_serialization = __esm({
8697
9044
  }
8698
9045
  });
8699
9046
 
8700
- // src/core/rules/integrity/type-references.ts
8701
- var typeReferencesRule;
8702
- var init_type_references2 = __esm({
8703
- "src/core/rules/integrity/type-references.ts"() {
9047
+ // src/core/rules/integrity/type-declarations.ts
9048
+ var typeDeclarationsRule;
9049
+ var init_type_declarations = __esm({
9050
+ "src/core/rules/integrity/type-declarations.ts"() {
8704
9051
  "use strict";
8705
- init_models();
8706
- typeReferencesRule = {
8707
- name: "type-references",
8708
- 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.",
9052
+ typeDeclarationsRule = {
9053
+ name: "type-declarations",
9054
+ 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.",
8709
9055
  codes: [
8710
9056
  { code: "INVALID_SUBSYSTEM_REFERENCE", defaultSeverity: "error", summary: "Type references a non-existent owning subsystem" },
8711
- { code: "UNDEFINED_TYPE_REFERENCE", defaultSeverity: "error", summary: "Reference to a type that is not defined anywhere" },
8712
9057
  { code: "HOLLOW_TYPE", defaultSeverity: "warning", summary: "Type declares no fields and no methods \u2014 a placeholder that informs neither implementers nor the ERD" }
8713
9058
  ],
8714
9059
  check(ctx) {
@@ -8733,23 +9078,62 @@ var init_type_references2 = __esm({
8733
9078
  isDraftCtx
8734
9079
  );
8735
9080
  }
8736
- if (t.fields) {
8737
- for (const field of t.fields) {
8738
- const refs = fieldTypeRefs(t, field.type);
8739
- for (const ref of refs) {
8740
- if (!ctx.isTypeResolved(ref, /* @__PURE__ */ new Set())) {
8741
- ctx.addIssue(
8742
- "error",
8743
- "UNDEFINED_TYPE_REFERENCE",
8744
- `Type "${t.id}" field "${field.name}" references undefined type "${ref}" in "${field.type}".`,
8745
- t.id,
8746
- isDraftCtx
8747
- );
8748
- }
9081
+ }
9082
+ }
9083
+ };
9084
+ }
9085
+ });
9086
+
9087
+ // src/core/rules/integrity/field-type-references.ts
9088
+ var fieldTypeReferencesRule;
9089
+ var init_field_type_references = __esm({
9090
+ "src/core/rules/integrity/field-type-references.ts"() {
9091
+ "use strict";
9092
+ init_models();
9093
+ fieldTypeReferencesRule = {
9094
+ name: "field-type-references",
9095
+ 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.",
9096
+ codes: [
9097
+ { code: "UNDEFINED_TYPE_REFERENCE", defaultSeverity: "error", summary: "Reference to a type that is not defined anywhere" }
9098
+ ],
9099
+ check(ctx) {
9100
+ for (const t of ctx.types) {
9101
+ const sub = ctx.subsystems.find((s) => s.id === t.subsystem);
9102
+ const isDraftCtx = sub ? sub.status === "draft" || sub.status === "design" : false;
9103
+ if (!t.fields) continue;
9104
+ for (const field of t.fields) {
9105
+ const refs = fieldTypeRefs(t, field.type);
9106
+ for (const ref of refs) {
9107
+ if (!ctx.isTypeResolved(ref, /* @__PURE__ */ new Set())) {
9108
+ ctx.addIssue(
9109
+ "error",
9110
+ "UNDEFINED_TYPE_REFERENCE",
9111
+ `Type "${t.id}" field "${field.name}" references undefined type "${ref}" in "${field.type}".`,
9112
+ t.id,
9113
+ isDraftCtx
9114
+ );
8749
9115
  }
8750
9116
  }
8751
9117
  }
8752
9118
  }
9119
+ }
9120
+ };
9121
+ }
9122
+ });
9123
+
9124
+ // src/core/rules/integrity/signature-type-references.ts
9125
+ var signatureTypeReferencesRule;
9126
+ var init_signature_type_references = __esm({
9127
+ "src/core/rules/integrity/signature-type-references.ts"() {
9128
+ "use strict";
9129
+ init_models();
9130
+ signatureTypeReferencesRule = {
9131
+ name: "signature-type-references",
9132
+ 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.",
9133
+ codes: [
9134
+ { code: "UNDEFINED_TYPE_REFERENCE", defaultSeverity: "error", summary: "Reference to a type that is not defined anywhere" }
9135
+ ],
9136
+ check(ctx) {
8753
9137
  for (const intf of ctx.interfaces) {
8754
9138
  const isDraftCtx = ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design";
8755
9139
  const interfaceGenerics = new Set(
@@ -8905,27 +9289,17 @@ var init_lint_allows = __esm({
8905
9289
  }
8906
9290
  });
8907
9291
 
8908
- // src/core/rules/narrative/contract-symmetry-and-narratives.ts
8909
- var contractsRule;
8910
- var init_contract_symmetry_and_narratives = __esm({
8911
- "src/core/rules/narrative/contract-symmetry-and-narratives.ts"() {
9292
+ // src/core/rules/narrative/contract-symmetry.ts
9293
+ var contractSymmetryRule;
9294
+ var init_contract_symmetry = __esm({
9295
+ "src/core/rules/narrative/contract-symmetry.ts"() {
8912
9296
  "use strict";
8913
- init_models();
8914
- contractsRule = {
8915
- name: "contract-symmetry-and-narratives",
8916
- 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.",
9297
+ contractSymmetryRule = {
9298
+ name: "contract-symmetry",
9299
+ 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.",
8917
9300
  codes: [
8918
9301
  { code: "UNEXPECTED_IMPLEMENTATION_METHOD", defaultSeverity: "error", summary: "Implementation method not present on the contract" },
8919
- { code: "MISSING_IMPLEMENTATION_METHOD", defaultSeverity: "error", summary: "Contract method missing from the implementation" },
8920
- { code: "MISSING_TARGET_COMPONENT", defaultSeverity: "error", summary: "Call/register step missing targetComponent" },
8921
- { code: "MISSING_TARGET_METHOD", defaultSeverity: "error", summary: "Call/register step missing targetMethod" },
8922
- { code: "INVALID_TARGET_COMPONENT_REFERENCE", defaultSeverity: "error", summary: "Call/register step targets a non-existent component" },
8923
- { 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" },
8924
- { code: "SURFACE_REF_NOT_EXPOSED", defaultSeverity: "error", summary: "Cross-tree reference resolves to a surface snapshot that does not expose the called method/capability" },
8925
- { code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree call/dispatch/register target matched by surface snapshots of several providers with different contracts" },
8926
- { code: "UNDECLARED_DEPENDENCY_CALL", defaultSeverity: "error", summary: "Call step targets a component the caller does not depend on or own" },
8927
- { code: "INVALID_TARGET_METHOD_REFERENCE", defaultSeverity: "error", summary: "Call step targets a method not on any target interface" },
8928
- { code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Narrative asserts a guarantee the called contract does not declare" }
9302
+ { code: "MISSING_IMPLEMENTATION_METHOD", defaultSeverity: "error", summary: "Contract method missing from the implementation" }
8929
9303
  ],
8930
9304
  check(ctx) {
8931
9305
  for (const impl of ctx.implementations) {
@@ -8956,6 +9330,33 @@ var init_contract_symmetry_and_narratives = __esm({
8956
9330
  );
8957
9331
  }
8958
9332
  }
9333
+ }
9334
+ }
9335
+ };
9336
+ }
9337
+ });
9338
+
9339
+ // src/core/rules/narrative/narrative-target-references.ts
9340
+ var narrativeTargetReferencesRule;
9341
+ var init_narrative_target_references = __esm({
9342
+ "src/core/rules/narrative/narrative-target-references.ts"() {
9343
+ "use strict";
9344
+ narrativeTargetReferencesRule = {
9345
+ name: "narrative-target-references",
9346
+ 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.",
9347
+ codes: [
9348
+ { code: "MISSING_TARGET_COMPONENT", defaultSeverity: "error", summary: "Call/register step missing targetComponent" },
9349
+ { code: "MISSING_TARGET_METHOD", defaultSeverity: "error", summary: "Call/register step missing targetMethod" },
9350
+ { code: "UNDECLARED_DEPENDENCY_CALL", defaultSeverity: "error", summary: "Call step targets a component the caller does not depend on or own" },
9351
+ { code: "INVALID_TARGET_METHOD_REFERENCE", defaultSeverity: "error", summary: "Call step targets a method not on any target interface" },
9352
+ { code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Narrative asserts a guarantee the called contract does not declare" }
9353
+ ],
9354
+ check(ctx) {
9355
+ for (const impl of ctx.implementations) {
9356
+ const contract = ctx.interfaceMap.get(impl.contract);
9357
+ if (!contract) continue;
9358
+ const isDraftCtx = ctx.isImplementationDraft(impl);
9359
+ const caller = ctx.componentMap.get(contract.component);
8959
9360
  for (const implMethod of impl.methods) {
8960
9361
  for (const step of implMethod.narrative) {
8961
9362
  if (step.type !== "call" && step.type !== "dispatch" && step.type !== "register") continue;
@@ -8970,156 +9371,28 @@ var init_contract_symmetry_and_narratives = __esm({
8970
9371
  continue;
8971
9372
  }
8972
9373
  const target = step.targetComponent;
8973
- const isCrossTreeForm = ctx.isExternalNamespaceRef(target);
8974
- const caller = ctx.componentMap.get(contract.component);
8975
- const fromSubsystem = caller?.subsystem;
8976
- const isCollapsedForm = !isCrossTreeForm && fromSubsystem !== void 0 && ctx.isCollapsedCrossTreeRef(target, fromSubsystem);
8977
- const reportUndeclared = (reaches, surfaceResolved) => {
8978
- if (!caller || target === caller.id || caller.dependsOn.includes(target) || caller.owns.includes(target)) return;
9374
+ const verb = step.type === "dispatch" ? "dispatches through" : step.type === "register" ? "registers callback" : "calls";
9375
+ if (step.type !== "dispatch" && !step.targetMethod) {
8979
9376
  ctx.addIssue(
8980
9377
  "error",
8981
- "UNDECLARED_DEPENDENCY_CALL",
8982
- `Method "${implMethod.name}" in implementation "${impl.id}" (component "${caller.id}") ${reaches} component "${target}" (step ${step.stepNumber}) but component "${caller.id}" does not list "${target}" as a dependency.`,
9378
+ "MISSING_TARGET_METHOD",
9379
+ `Method "${implMethod.name}" in implementation "${impl.id}" has a ${step.type} step (${step.stepNumber}) missing "targetMethod".`,
8983
9380
  impl.id,
8984
- isDraftCtx || ctx.isComponentDraft(caller.id),
8985
- surfaceResolved
9381
+ isDraftCtx
8986
9382
  );
8987
- };
8988
- if (step.type === "dispatch") {
8989
- const dispatchTarget = ctx.componentMap.get(target);
8990
- if (!dispatchTarget) {
8991
- if (isCrossTreeForm || isCollapsedForm) {
8992
- const resolved = ctx.resolveSurfaceRef(target, fromSubsystem);
8993
- if (resolved.kind === "ambiguous") {
8994
- ctx.addIssue(
8995
- "error",
8996
- "SURFACE_REF_AMBIGUOUS",
8997
- ambiguityMessage(
8998
- resolved,
8999
- `Method "${implMethod.name}" in implementation "${impl.id}" dispatches (step ${step.stepNumber}) through`,
9000
- target
9001
- ),
9002
- impl.id,
9003
- isDraftCtx
9004
- );
9005
- continue;
9006
- }
9007
- if (resolved.kind === "resolved") {
9008
- reportUndeclared("dispatches through", true);
9009
- if (step.capability && !(resolved.entry.dispatch ?? []).some((b) => b.capability === step.capability)) {
9010
- ctx.addIssue(
9011
- "error",
9012
- "SURFACE_REF_NOT_EXPOSED",
9013
- `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}".`,
9014
- impl.id,
9015
- isDraftCtx,
9016
- true
9017
- );
9018
- }
9019
- continue;
9020
- }
9021
- }
9022
- if (isCrossTreeForm) {
9023
- ctx.addIssue(
9024
- "warning",
9025
- "CROSS_TREE_REF_UNRESOLVED",
9026
- `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.`,
9027
- impl.id,
9028
- isDraftCtx
9029
- );
9030
- } else {
9031
- ctx.addIssue(
9032
- "error",
9033
- "INVALID_TARGET_COMPONENT_REFERENCE",
9034
- `Method "${implMethod.name}" in implementation "${impl.id}" dispatches through component "${target}" which does not exist (step ${step.stepNumber}).`,
9035
- impl.id,
9036
- isDraftCtx
9037
- );
9038
- }
9039
- continue;
9040
- }
9041
- reportUndeclared("dispatches through");
9042
9383
  continue;
9043
9384
  }
9044
- const verb = step.type === "register" ? "registers callback" : "calls";
9045
- if (!step.targetMethod) {
9385
+ if (!ctx.componentMap.get(target)) continue;
9386
+ if (caller && target !== caller.id && !caller.dependsOn.includes(target) && !caller.owns.includes(target)) {
9046
9387
  ctx.addIssue(
9047
9388
  "error",
9048
- "MISSING_TARGET_METHOD",
9049
- `Method "${implMethod.name}" in implementation "${impl.id}" has a ${step.type} step (${step.stepNumber}) missing "targetMethod".`,
9389
+ "UNDECLARED_DEPENDENCY_CALL",
9390
+ `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.`,
9050
9391
  impl.id,
9051
- isDraftCtx
9392
+ isDraftCtx || ctx.isComponentDraft(caller.id)
9052
9393
  );
9053
- continue;
9054
- }
9055
- const targetComp = ctx.componentMap.get(target);
9056
- if (!targetComp) {
9057
- if (isCrossTreeForm || isCollapsedForm) {
9058
- const resolved = ctx.resolveSurfaceRef(target, fromSubsystem);
9059
- if (resolved.kind === "ambiguous") {
9060
- ctx.addIssue(
9061
- "error",
9062
- "SURFACE_REF_AMBIGUOUS",
9063
- ambiguityMessage(
9064
- resolved,
9065
- `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} "${step.targetMethod}" (step ${step.stepNumber}) on`,
9066
- target
9067
- ),
9068
- impl.id,
9069
- isDraftCtx
9070
- );
9071
- continue;
9072
- }
9073
- if (resolved.kind === "resolved") {
9074
- reportUndeclared(verb, true);
9075
- const surfaceMethod = resolved.entry.methods.find((m) => m.name === step.targetMethod);
9076
- if (!surfaceMethod) {
9077
- ctx.addIssue(
9078
- "error",
9079
- "SURFACE_REF_NOT_EXPOSED",
9080
- `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}".`,
9081
- impl.id,
9082
- isDraftCtx,
9083
- true
9084
- );
9085
- } else if (step.assertsGuarantees) {
9086
- const declared = new Set(surfaceMethod.guarantees ?? []);
9087
- for (const g of step.assertsGuarantees) {
9088
- if (!declared.has(g)) {
9089
- ctx.addIssue(
9090
- "warning",
9091
- "NARRATIVE_SEMANTIC_UNBACKED",
9092
- `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}".`,
9093
- impl.id,
9094
- isDraftCtx,
9095
- true
9096
- );
9097
- }
9098
- }
9099
- }
9100
- continue;
9101
- }
9102
- }
9103
- if (isCrossTreeForm) {
9104
- ctx.addIssue(
9105
- "warning",
9106
- "CROSS_TREE_REF_UNRESOLVED",
9107
- `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.`,
9108
- impl.id,
9109
- isDraftCtx
9110
- );
9111
- } else {
9112
- ctx.addIssue(
9113
- "error",
9114
- "INVALID_TARGET_COMPONENT_REFERENCE",
9115
- `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} component "${target}" which does not exist (step ${step.stepNumber}).`,
9116
- impl.id,
9117
- isDraftCtx
9118
- );
9119
- }
9120
- continue;
9121
9394
  }
9122
- reportUndeclared(verb);
9395
+ if (step.type === "dispatch") continue;
9123
9396
  const targetInterfaces = ctx.interfacesByComponent.get(target) ?? [];
9124
9397
  let targetMethodSpec;
9125
9398
  for (const targetIntf of targetInterfaces) {
@@ -9137,20 +9410,18 @@ var init_contract_symmetry_and_narratives = __esm({
9137
9410
  impl.id,
9138
9411
  isDraftCtx || ctx.isComponentDraft(target)
9139
9412
  );
9140
- } else {
9141
- const declared = new Set(targetMethodSpec.guarantees ?? []);
9142
- if (step.assertsGuarantees) {
9143
- for (const g of step.assertsGuarantees) {
9144
- if (!declared.has(g)) {
9145
- ctx.addIssue(
9146
- "warning",
9147
- "NARRATIVE_SEMANTIC_UNBACKED",
9148
- `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.`,
9149
- impl.id,
9150
- isDraftCtx || ctx.isComponentDraft(target)
9151
- );
9152
- }
9153
- }
9413
+ continue;
9414
+ }
9415
+ const declared = new Set(targetMethodSpec.guarantees ?? []);
9416
+ for (const g of step.assertsGuarantees ?? []) {
9417
+ if (!declared.has(g)) {
9418
+ ctx.addIssue(
9419
+ "warning",
9420
+ "NARRATIVE_SEMANTIC_UNBACKED",
9421
+ `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.`,
9422
+ impl.id,
9423
+ isDraftCtx || ctx.isComponentDraft(target)
9424
+ );
9154
9425
  }
9155
9426
  }
9156
9427
  }
@@ -9161,21 +9432,184 @@ var init_contract_symmetry_and_narratives = __esm({
9161
9432
  }
9162
9433
  });
9163
9434
 
9164
- // src/core/rules/narrative/guarantee-tokens.ts
9165
- var guaranteeTokensRule;
9166
- var init_guarantee_tokens = __esm({
9167
- "src/core/rules/narrative/guarantee-tokens.ts"() {
9435
+ // src/core/rules/narrative/cross-tree-references.ts
9436
+ var crossTreeReferencesRule;
9437
+ var init_cross_tree_references = __esm({
9438
+ "src/core/rules/narrative/cross-tree-references.ts"() {
9168
9439
  "use strict";
9169
9440
  init_models();
9170
- guaranteeTokensRule = {
9171
- name: "guarantee-tokens",
9172
- 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.",
9441
+ crossTreeReferencesRule = {
9442
+ name: "cross-tree-references",
9443
+ 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.",
9173
9444
  codes: [
9174
- { code: "UNKNOWN_GUARANTEE", defaultSeverity: "warning", summary: "Guarantee token is neither builtin nor pack-declared" }
9445
+ { code: "INVALID_TARGET_COMPONENT_REFERENCE", defaultSeverity: "error", summary: "Call/register step targets a non-existent component" },
9446
+ { 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" },
9447
+ { code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree call/dispatch/register target matched by surface snapshots of several providers with different contracts" }
9175
9448
  ],
9176
9449
  check(ctx) {
9177
- const known = /* @__PURE__ */ new Set([...SEMANTIC_GUARANTEES, ...ctx.ext.guarantees]);
9178
- 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.`;
9450
+ for (const impl of ctx.implementations) {
9451
+ const contract = ctx.interfaceMap.get(impl.contract);
9452
+ if (!contract) continue;
9453
+ const isDraftCtx = ctx.isImplementationDraft(impl);
9454
+ const caller = ctx.componentMap.get(contract.component);
9455
+ const fromSubsystem = caller?.subsystem;
9456
+ for (const implMethod of impl.methods) {
9457
+ for (const step of implMethod.narrative) {
9458
+ if (step.type !== "call" && step.type !== "dispatch" && step.type !== "register") continue;
9459
+ if (!step.targetComponent) continue;
9460
+ if (step.type !== "dispatch" && !step.targetMethod) continue;
9461
+ const target = step.targetComponent;
9462
+ if (ctx.componentMap.get(target)) continue;
9463
+ const verb = step.type === "dispatch" ? "dispatches through" : step.type === "register" ? "registers callback" : "calls";
9464
+ const isCrossTreeForm = ctx.isExternalNamespaceRef(target);
9465
+ const isCollapsedForm = !isCrossTreeForm && fromSubsystem !== void 0 && ctx.isCollapsedCrossTreeRef(target, fromSubsystem);
9466
+ if (isCrossTreeForm || isCollapsedForm) {
9467
+ const resolved = ctx.resolveSurfaceRef(target, fromSubsystem);
9468
+ if (resolved.kind === "ambiguous") {
9469
+ ctx.addIssue(
9470
+ "error",
9471
+ "SURFACE_REF_AMBIGUOUS",
9472
+ ambiguityMessage(
9473
+ resolved,
9474
+ 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`,
9475
+ target
9476
+ ),
9477
+ impl.id,
9478
+ isDraftCtx
9479
+ );
9480
+ continue;
9481
+ }
9482
+ if (resolved.kind === "resolved") continue;
9483
+ }
9484
+ if (isCrossTreeForm) {
9485
+ ctx.addIssue(
9486
+ "warning",
9487
+ "CROSS_TREE_REF_UNRESOLVED",
9488
+ `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.`,
9489
+ impl.id,
9490
+ isDraftCtx
9491
+ );
9492
+ } else {
9493
+ ctx.addIssue(
9494
+ "error",
9495
+ "INVALID_TARGET_COMPONENT_REFERENCE",
9496
+ `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} component "${target}" which does not exist (step ${step.stepNumber}).`,
9497
+ impl.id,
9498
+ isDraftCtx
9499
+ );
9500
+ }
9501
+ }
9502
+ }
9503
+ }
9504
+ }
9505
+ };
9506
+ }
9507
+ });
9508
+
9509
+ // src/core/rules/narrative/surface-reference-backing.ts
9510
+ var surfaceReferenceBackingRule;
9511
+ var init_surface_reference_backing = __esm({
9512
+ "src/core/rules/narrative/surface-reference-backing.ts"() {
9513
+ "use strict";
9514
+ surfaceReferenceBackingRule = {
9515
+ name: "surface-reference-backing",
9516
+ 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.",
9517
+ codes: [
9518
+ { code: "SURFACE_REF_NOT_EXPOSED", defaultSeverity: "error", summary: "Cross-tree reference resolves to a surface snapshot that does not expose the called method/capability" },
9519
+ { code: "UNDECLARED_DEPENDENCY_CALL", defaultSeverity: "error", summary: "Call step targets a component the caller does not depend on or own" },
9520
+ { code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Narrative asserts a guarantee the called contract does not declare" }
9521
+ ],
9522
+ check(ctx) {
9523
+ for (const impl of ctx.implementations) {
9524
+ const contract = ctx.interfaceMap.get(impl.contract);
9525
+ if (!contract) continue;
9526
+ const isDraftCtx = ctx.isImplementationDraft(impl);
9527
+ const caller = ctx.componentMap.get(contract.component);
9528
+ const fromSubsystem = caller?.subsystem;
9529
+ for (const implMethod of impl.methods) {
9530
+ for (const step of implMethod.narrative) {
9531
+ if (step.type !== "call" && step.type !== "dispatch" && step.type !== "register") continue;
9532
+ if (!step.targetComponent) continue;
9533
+ if (step.type !== "dispatch" && !step.targetMethod) continue;
9534
+ const target = step.targetComponent;
9535
+ if (ctx.componentMap.get(target)) continue;
9536
+ const isCrossTreeForm = ctx.isExternalNamespaceRef(target);
9537
+ const isCollapsedForm = !isCrossTreeForm && fromSubsystem !== void 0 && ctx.isCollapsedCrossTreeRef(target, fromSubsystem);
9538
+ if (!isCrossTreeForm && !isCollapsedForm) continue;
9539
+ const resolved = ctx.resolveSurfaceRef(target, fromSubsystem);
9540
+ if (resolved.kind !== "resolved") continue;
9541
+ const verb = step.type === "dispatch" ? "dispatches through" : step.type === "register" ? "registers callback" : "calls";
9542
+ if (caller && target !== caller.id && !caller.dependsOn.includes(target) && !caller.owns.includes(target)) {
9543
+ ctx.addIssue(
9544
+ "error",
9545
+ "UNDECLARED_DEPENDENCY_CALL",
9546
+ `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.`,
9547
+ impl.id,
9548
+ isDraftCtx || ctx.isComponentDraft(caller.id),
9549
+ true
9550
+ );
9551
+ }
9552
+ if (step.type === "dispatch") {
9553
+ if (step.capability && !(resolved.entry.dispatch ?? []).some((b) => b.capability === step.capability)) {
9554
+ ctx.addIssue(
9555
+ "error",
9556
+ "SURFACE_REF_NOT_EXPOSED",
9557
+ `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}".`,
9558
+ impl.id,
9559
+ isDraftCtx,
9560
+ true
9561
+ );
9562
+ }
9563
+ continue;
9564
+ }
9565
+ const surfaceMethod = resolved.entry.methods.find((m) => m.name === step.targetMethod);
9566
+ if (!surfaceMethod) {
9567
+ ctx.addIssue(
9568
+ "error",
9569
+ "SURFACE_REF_NOT_EXPOSED",
9570
+ `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}".`,
9571
+ impl.id,
9572
+ isDraftCtx,
9573
+ true
9574
+ );
9575
+ continue;
9576
+ }
9577
+ const declared = new Set(surfaceMethod.guarantees ?? []);
9578
+ for (const g of step.assertsGuarantees ?? []) {
9579
+ if (!declared.has(g)) {
9580
+ ctx.addIssue(
9581
+ "warning",
9582
+ "NARRATIVE_SEMANTIC_UNBACKED",
9583
+ `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}".`,
9584
+ impl.id,
9585
+ isDraftCtx,
9586
+ true
9587
+ );
9588
+ }
9589
+ }
9590
+ }
9591
+ }
9592
+ }
9593
+ }
9594
+ };
9595
+ }
9596
+ });
9597
+
9598
+ // src/core/rules/narrative/guarantee-tokens.ts
9599
+ var guaranteeTokensRule;
9600
+ var init_guarantee_tokens = __esm({
9601
+ "src/core/rules/narrative/guarantee-tokens.ts"() {
9602
+ "use strict";
9603
+ init_models();
9604
+ guaranteeTokensRule = {
9605
+ name: "guarantee-tokens",
9606
+ 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.",
9607
+ codes: [
9608
+ { code: "UNKNOWN_GUARANTEE", defaultSeverity: "warning", summary: "Guarantee token is neither builtin nor pack-declared" }
9609
+ ],
9610
+ check(ctx) {
9611
+ const known = /* @__PURE__ */ new Set([...SEMANTIC_GUARANTEES, ...ctx.ext.guarantees]);
9612
+ 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.`;
9179
9613
  for (const intf of ctx.interfaces) {
9180
9614
  const isDraftCtx = intf.status === "draft" || intf.status === "design" || ctx.isComponentDraft(intf.component);
9181
9615
  for (const method of intf.methods) {
@@ -9215,166 +9649,68 @@ var init_guarantee_tokens = __esm({
9215
9649
  }
9216
9650
  });
9217
9651
 
9218
- // src/core/rules/narrative/narrative-flow.ts
9219
- function flowConfigOn(step) {
9220
- return FLOW_FIELDS.filter((f) => {
9221
- const v = step[f];
9222
- if (v === void 0) return false;
9223
- if (Array.isArray(v) && v.length === 0) return false;
9224
- return true;
9225
- });
9226
- }
9227
- var FLOW_FIELDS, narrativeFlowRule;
9228
- var init_narrative_flow = __esm({
9229
- "src/core/rules/narrative/narrative-flow.ts"() {
9652
+ // src/core/rules/narrative/narrative-step-config.ts
9653
+ var narrativeStepConfigRule;
9654
+ var init_narrative_step_config = __esm({
9655
+ "src/core/rules/narrative/narrative-step-config.ts"() {
9230
9656
  "use strict";
9231
9657
  init_models();
9232
- FLOW_FIELDS = [
9233
- "condition",
9234
- "onTrueStep",
9235
- "onFalseStep",
9236
- "on",
9237
- "cases",
9238
- "defaultStep",
9239
- "loopKind",
9240
- "over",
9241
- "endStep",
9242
- "catches",
9243
- "finallyStep",
9244
- "branches",
9245
- "toStep"
9246
- ];
9247
- narrativeFlowRule = {
9248
- name: "narrative-flow",
9249
- 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.',
9658
+ narrativeStepConfigRule = {
9659
+ name: "narrative-step-config",
9660
+ 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.',
9250
9661
  codes: [
9251
9662
  { code: "MALFORMED_FLOW_STEP", defaultSeverity: "error", summary: "Flow step missing required config (or flow config on a local/call step)" },
9252
9663
  { code: "INVALID_STEP_JUMP", defaultSeverity: "error", summary: "Jump field targets a step number that does not exist in the narrative" },
9253
- { code: "UNREACHABLE_STEP", defaultSeverity: "warning", summary: "Step not reachable from the first step following fall-through and jumps" },
9254
- { code: "REGION_OVERLAP", defaultSeverity: "error", summary: "loop/try regions interleave \u2014 regions must nest or be disjoint to map onto structured code" },
9255
- { 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" },
9256
- { code: "FALLTHROUGH_INTO_HANDLER", defaultSeverity: "warning", summary: "try body falls through into its own catch/finally region on the success path" },
9257
- { 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" },
9258
9664
  { code: "DUPLICATE_STEP_LABEL", defaultSeverity: "error", summary: "Two steps in one narrative share a label \u2014 the symbolic anchor later deltas address by" }
9259
9665
  ],
9666
+ check(ctx) {
9667
+ for (const impl of ctx.implementations) {
9668
+ const isDraftCtx = ctx.isImplementationDraft(impl);
9669
+ for (const implMethod of impl.methods) {
9670
+ if (!implMethod.narrative.length) continue;
9671
+ const where = `Method "${implMethod.name}" in implementation "${impl.id}": `;
9672
+ for (const problem of stepConfigVerdict(implMethod).problems) {
9673
+ ctx.addIssue("error", problem.code, where + problem.detail, impl.id, isDraftCtx);
9674
+ }
9675
+ }
9676
+ }
9677
+ }
9678
+ };
9679
+ }
9680
+ });
9681
+
9682
+ // src/core/rules/narrative/narrative-reachability.ts
9683
+ function regionsOf(steps) {
9684
+ const regions = [];
9685
+ for (const s of steps) {
9686
+ if ((s.type === "loop" || s.type === "try" || s.type === "parallel") && s.endStep !== void 0) {
9687
+ regions.push({ h: s.stepNumber, end: s.endStep, kind: s.type });
9688
+ }
9689
+ }
9690
+ return regions;
9691
+ }
9692
+ var narrativeReachabilityRule;
9693
+ var init_narrative_reachability = __esm({
9694
+ "src/core/rules/narrative/narrative-reachability.ts"() {
9695
+ "use strict";
9696
+ init_models();
9697
+ narrativeReachabilityRule = {
9698
+ name: "narrative-reachability",
9699
+ 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).",
9700
+ codes: [
9701
+ { code: "UNREACHABLE_STEP", defaultSeverity: "warning", summary: "Step not reachable from the first step following fall-through and jumps" },
9702
+ { code: "REGION_OVERLAP", defaultSeverity: "error", summary: "loop/try regions interleave \u2014 regions must nest or be disjoint to map onto structured code" }
9703
+ ],
9260
9704
  check(ctx) {
9261
9705
  for (const impl of ctx.implementations) {
9262
9706
  const isDraftCtx = ctx.isImplementationDraft(impl);
9263
9707
  for (const implMethod of impl.methods) {
9264
9708
  const steps = implMethod.narrative;
9265
9709
  if (!steps.length) continue;
9710
+ if (!stepConfigVerdict(implMethod).sound) continue;
9266
9711
  const where = `Method "${implMethod.name}" in implementation "${impl.id}": `;
9267
- const labelled = /* @__PURE__ */ new Map();
9268
- for (const step of steps) {
9269
- const label = step.label;
9270
- if (!label) continue;
9271
- const first = labelled.get(label);
9272
- if (first !== void 0) {
9273
- ctx.addIssue(
9274
- "error",
9275
- "DUPLICATE_STEP_LABEL",
9276
- `${where}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.`,
9277
- impl.id,
9278
- isDraftCtx
9279
- );
9280
- } else {
9281
- labelled.set(label, step.stepNumber);
9282
- }
9283
- }
9284
- let sound = true;
9285
- const malformed = (msg) => {
9286
- sound = false;
9287
- ctx.addIssue("error", "MALFORMED_FLOW_STEP", where + msg, impl.id, isDraftCtx);
9288
- };
9289
- const byNum = /* @__PURE__ */ new Map();
9290
- for (const s of steps) {
9291
- if (byNum.has(s.stepNumber)) malformed(`duplicate stepNumber ${s.stepNumber} \u2014 jump targets would be ambiguous.`);
9292
- byNum.set(s.stepNumber, s);
9293
- }
9294
- const nums = [...byNum.keys()].sort((a, b) => a - b);
9295
- const indexOf = new Map(nums.map((n, i) => [n, i]));
9296
- const nextOf = (n) => {
9297
- const i = indexOf.get(n);
9298
- return i !== void 0 && i + 1 < nums.length ? nums[i + 1] : void 0;
9299
- };
9300
- for (const s of steps) {
9301
- if (s.detach && s.type !== "call" && s.type !== "dispatch") {
9302
- malformed(`step ${s.stepNumber} (${s.type}) carries "detach" \u2014 fire-and-forget applies to call/dispatch steps only.`);
9303
- }
9304
- switch (s.type) {
9305
- case "local":
9306
- case "call":
9307
- case "register":
9308
- case "dispatch": {
9309
- const extra = flowConfigOn(s);
9310
- if (extra.length) malformed(`step ${s.stepNumber} (${s.type}) carries flow config (${extra.join(", ")}) \u2014 use a flow step type instead.`);
9311
- break;
9312
- }
9313
- case "branch":
9314
- if (!s.condition) malformed(`branch step ${s.stepNumber} requires "condition".`);
9315
- if (s.onFalseStep === void 0) malformed(`branch step ${s.stepNumber} requires "onFalseStep" (true continues at onTrueStep or the next step).`);
9316
- break;
9317
- case "switch":
9318
- if (!s.cases || !s.cases.length) malformed(`switch step ${s.stepNumber} requires non-empty "cases".`);
9319
- break;
9320
- case "loop": {
9321
- if (s.endStep === void 0) malformed(`loop step ${s.stepNumber} requires "endStep" (last step of the body).`);
9322
- else if (s.endStep <= s.stepNumber) malformed(`loop step ${s.stepNumber} "endStep" (${s.endStep}) must lie beyond the header.`);
9323
- const kind = s.loopKind ?? (s.over ? "forEach" : "while");
9324
- if ((kind === "while" || kind === "doWhile") && !s.condition) malformed(`${kind} loop step ${s.stepNumber} requires "condition".`);
9325
- if ((kind === "forEach" || kind === "for") && !s.over) malformed(`${kind} loop step ${s.stepNumber} requires "over" (the iteration source).`);
9326
- break;
9327
- }
9328
- case "try":
9329
- if (s.endStep === void 0) malformed(`try step ${s.stepNumber} requires "endStep" (last step of the guarded body).`);
9330
- else if (s.endStep <= s.stepNumber) malformed(`try step ${s.stepNumber} "endStep" (${s.endStep}) must lie beyond the header.`);
9331
- if ((!s.catches || !s.catches.length) && s.finallyStep === void 0) malformed(`try step ${s.stepNumber} requires "catches" and/or "finallyStep" \u2014 a guard that handles nothing guards nothing.`);
9332
- break;
9333
- case "parallel": {
9334
- if (s.endStep === void 0) malformed(`parallel step ${s.stepNumber} requires "endStep" (last step of the fan-out body).`);
9335
- else if (s.endStep <= s.stepNumber) malformed(`parallel step ${s.stepNumber} "endStep" (${s.endStep}) must lie beyond the header.`);
9336
- const entries = (s.branches ?? []).map((b) => b.step);
9337
- if (entries.length < 2) {
9338
- malformed(`parallel step ${s.stepNumber} requires "branches" with at least two arms \u2014 a single arm is sequential flow.`);
9339
- } else {
9340
- const sorted = [...entries].sort((a, b) => a - b);
9341
- if (entries.some((e, i) => e !== sorted[i])) malformed(`parallel step ${s.stepNumber} "branches" must list arm entries in ascending order \u2014 arms are contiguous sub-regions of the body.`);
9342
- if (new Set(entries).size !== entries.length) malformed(`parallel step ${s.stepNumber} "branches" lists the same entry step twice.`);
9343
- const bodyStart = nextOf(s.stepNumber);
9344
- if (bodyStart !== void 0 && sorted[0] !== bodyStart) malformed(`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.`);
9345
- if (s.endStep !== void 0 && sorted.some((e) => e > s.endStep)) malformed(`parallel step ${s.stepNumber}: every arm entry must lie within the body (..${s.endStep}).`);
9346
- }
9347
- break;
9348
- }
9349
- case "jump":
9350
- if (s.toStep === void 0) malformed(`jump step ${s.stepNumber} requires "toStep".`);
9351
- break;
9352
- }
9353
- const jumpFields = [];
9354
- if (s.onTrueStep !== void 0) jumpFields.push(["onTrueStep", s.onTrueStep]);
9355
- if (s.onFalseStep !== void 0) jumpFields.push(["onFalseStep", s.onFalseStep]);
9356
- if (s.defaultStep !== void 0) jumpFields.push(["defaultStep", s.defaultStep]);
9357
- if (s.endStep !== void 0) jumpFields.push(["endStep", s.endStep]);
9358
- if (s.finallyStep !== void 0) jumpFields.push(["finallyStep", s.finallyStep]);
9359
- if (s.toStep !== void 0) jumpFields.push(["toStep", s.toStep]);
9360
- (s.cases ?? []).forEach((c, i) => jumpFields.push([`cases[${i}].step`, c.step]));
9361
- (s.catches ?? []).forEach((c, i) => jumpFields.push([`catches[${i}].step`, c.step]));
9362
- (s.branches ?? []).forEach((b, i) => jumpFields.push([`branches[${i}].step`, b.step]));
9363
- for (const [field, target] of jumpFields) {
9364
- if (!byNum.has(target)) {
9365
- sound = false;
9366
- ctx.addIssue(
9367
- "error",
9368
- "INVALID_STEP_JUMP",
9369
- `${where}step ${s.stepNumber} "${field}" targets step ${target}, which does not exist in this narrative.`,
9370
- impl.id,
9371
- isDraftCtx
9372
- );
9373
- }
9374
- }
9375
- }
9376
- if (!sound) continue;
9377
9712
  const graph = stepGraph(implMethod);
9713
+ const nums = graph.stepNumbers;
9378
9714
  const visited = /* @__PURE__ */ new Set();
9379
9715
  const stack = [nums[0]];
9380
9716
  while (stack.length) {
@@ -9395,13 +9731,7 @@ var init_narrative_flow = __esm({
9395
9731
  isDraftCtx
9396
9732
  );
9397
9733
  }
9398
- const regions = [];
9399
- for (const s of steps) {
9400
- if ((s.type === "loop" || s.type === "try" || s.type === "parallel") && s.endStep !== void 0) {
9401
- regions.push({ h: s.stepNumber, end: s.endStep, kind: s.type });
9402
- }
9403
- }
9404
- const inBody = (n, r) => n > r.h && n <= r.end;
9734
+ const regions = regionsOf(steps);
9405
9735
  for (const a of regions) {
9406
9736
  for (const b of regions) {
9407
9737
  if (b.h > a.h && b.h <= a.end && b.end > a.end) {
@@ -9415,19 +9745,63 @@ var init_narrative_flow = __esm({
9415
9745
  }
9416
9746
  }
9417
9747
  }
9418
- const jumpEdges = [];
9419
- for (const s of steps) {
9420
- const push = (field, to) => {
9421
- if (to !== void 0) jumpEdges.push({ from: s.stepNumber, to, field, kind: s.type });
9422
- };
9423
- push("onTrueStep", s.onTrueStep);
9424
- push("onFalseStep", s.onFalseStep);
9425
- push("defaultStep", s.defaultStep);
9426
- push("toStep", s.toStep);
9427
- push("finallyStep", s.finallyStep);
9428
- (s.cases ?? []).forEach((c, i) => push(`cases[${i}].step`, c.step));
9429
- (s.catches ?? []).forEach((c, i) => push(`catches[${i}].step`, c.step));
9430
- }
9748
+ }
9749
+ }
9750
+ }
9751
+ };
9752
+ }
9753
+ });
9754
+
9755
+ // src/core/rules/narrative/narrative-jump-edges.ts
9756
+ function regionsOf2(steps) {
9757
+ const regions = [];
9758
+ for (const s of steps) {
9759
+ if ((s.type === "loop" || s.type === "try" || s.type === "parallel") && s.endStep !== void 0) {
9760
+ regions.push({ h: s.stepNumber, end: s.endStep, kind: s.type });
9761
+ }
9762
+ }
9763
+ return regions;
9764
+ }
9765
+ function jumpEdgesOf(steps) {
9766
+ const edges = [];
9767
+ for (const s of steps) {
9768
+ const push = (field, to) => {
9769
+ if (to !== void 0) edges.push({ from: s.stepNumber, to, field, kind: s.type });
9770
+ };
9771
+ push("onTrueStep", s.onTrueStep);
9772
+ push("onFalseStep", s.onFalseStep);
9773
+ push("defaultStep", s.defaultStep);
9774
+ push("toStep", s.toStep);
9775
+ push("finallyStep", s.finallyStep);
9776
+ (s.cases ?? []).forEach((c, i) => push(`cases[${i}].step`, c.step));
9777
+ (s.catches ?? []).forEach((c, i) => push(`catches[${i}].step`, c.step));
9778
+ }
9779
+ return edges;
9780
+ }
9781
+ var narrativeJumpEdgesRule;
9782
+ var init_narrative_jump_edges = __esm({
9783
+ "src/core/rules/narrative/narrative-jump-edges.ts"() {
9784
+ "use strict";
9785
+ init_models();
9786
+ narrativeJumpEdgesRule = {
9787
+ name: "narrative-jump-edges",
9788
+ 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).",
9789
+ codes: [
9790
+ { 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" },
9791
+ { 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" },
9792
+ { code: "FALLTHROUGH_INTO_HANDLER", defaultSeverity: "warning", summary: "try body falls through into its own catch/finally region on the success path" }
9793
+ ],
9794
+ check(ctx) {
9795
+ for (const impl of ctx.implementations) {
9796
+ const isDraftCtx = ctx.isImplementationDraft(impl);
9797
+ for (const implMethod of impl.methods) {
9798
+ const steps = implMethod.narrative;
9799
+ if (!steps.length) continue;
9800
+ if (!stepConfigVerdict(implMethod).sound) continue;
9801
+ const where = `Method "${implMethod.name}" in implementation "${impl.id}": `;
9802
+ const regions = regionsOf2(steps);
9803
+ const jumpEdges = jumpEdgesOf(steps);
9804
+ const inBody = (n, r) => n > r.h && n <= r.end;
9431
9805
  for (const e of jumpEdges) {
9432
9806
  for (const r of regions) {
9433
9807
  if (inBody(e.to, r) && e.from !== r.h && !inBody(e.from, r)) {
@@ -9440,19 +9814,26 @@ var init_narrative_flow = __esm({
9440
9814
  );
9441
9815
  }
9442
9816
  }
9443
- if ((e.kind === "branch" || e.kind === "switch" || e.kind === "jump") && e.to < e.from) {
9444
- const continueToLoop = regions.some((r) => r.kind === "loop" && r.h === e.to && inBody(e.from, r));
9445
- if (!continueToLoop) {
9446
- ctx.addIssue(
9447
- "warning",
9448
- "BACKWARD_JUMP",
9449
- `${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).`,
9450
- impl.id,
9451
- isDraftCtx
9452
- );
9453
- }
9454
- }
9455
9817
  }
9818
+ for (const e of jumpEdges) {
9819
+ if (e.kind !== "branch" && e.kind !== "switch" && e.kind !== "jump" || e.to >= e.from) continue;
9820
+ const continueToLoop = regions.some((r) => r.kind === "loop" && r.h === e.to && inBody(e.from, r));
9821
+ if (continueToLoop) continue;
9822
+ ctx.addIssue(
9823
+ "warning",
9824
+ "BACKWARD_JUMP",
9825
+ `${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).`,
9826
+ impl.id,
9827
+ isDraftCtx
9828
+ );
9829
+ }
9830
+ const byNum = /* @__PURE__ */ new Map();
9831
+ for (const s of steps) byNum.set(s.stepNumber, s);
9832
+ const nums = [...byNum.keys()].sort((a, b) => a - b);
9833
+ const nextOf = (n) => {
9834
+ const i = nums.indexOf(n);
9835
+ return i !== -1 && i + 1 < nums.length ? nums[i + 1] : void 0;
9836
+ };
9456
9837
  for (const s of steps) {
9457
9838
  if (s.type !== "try" || s.endStep === void 0) continue;
9458
9839
  const handlerStarts = new Set((s.catches ?? []).map((c) => c.step));
@@ -9724,8 +10105,7 @@ var init_narrative_detail = __esm({
9724
10105
  { code: "DETAIL_BELOW_STEREOTYPE", defaultSeverity: "warning", summary: "Method explicitly dialed below the full narrative floor of its logic stereotype, with no narrative" }
9725
10106
  ],
9726
10107
  check(ctx) {
9727
- const factsByPath = /* @__PURE__ */ new Map();
9728
- for (const f of ctx.codeModel.files) factsByPath.set(pathKey(f.path), f);
10108
+ const code = ctx.codeIndex();
9729
10109
  for (const impl of ctx.implementations) {
9730
10110
  const contract = ctx.interfaceMap.get(impl.contract);
9731
10111
  if (!contract) continue;
@@ -9751,7 +10131,7 @@ var init_narrative_detail = __esm({
9751
10131
  const tier = implMethod.conformance ?? impl.conformance ?? defaultConformanceTier(component);
9752
10132
  const file = methodSourceFile(implMethod, impl.sourcePath);
9753
10133
  if (file && tier !== "off" && !(component && ctx.isInChainedSubproject(component.subsystem))) {
9754
- const facts = factsByPath.get(pathKey(file));
10134
+ const facts = code.factsAt(file);
9755
10135
  const symbol = implMethod.symbol ?? implMethod.name;
9756
10136
  const complexity = facts?.status === "analyzed" && facts.analysisGrade === "exact" && facts.functionComplexity && Object.prototype.hasOwnProperty.call(facts.functionComplexity, symbol) ? facts.functionComplexity[symbol] : void 0;
9757
10137
  const limit = ctx.complexityConfigFor(component?.subsystem)?.maxUnnarratedComplexity ?? DEFAULT_MAX_UNNARRATED_COMPLEXITY;
@@ -9897,7 +10277,7 @@ var init_logic_declaration = __esm({
9897
10277
  logicDeclarationRule = {
9898
10278
  name: "logic-declaration",
9899
10279
  scope: "spec",
9900
- description: "Only an Orchestrator may declare a dependencyClass (DEPENDENCY_CLASS_ON_NON_ORCHESTRATOR); what the class allows is judged by stereotype-dependencies. Intrinsic to one component: no tree required.",
10280
+ description: "Only an Orchestrator may declare a dependencyClass (DEPENDENCY_CLASS_ON_NON_ORCHESTRATOR); what the class allows is judged by logic-dependency-class. Intrinsic to one component: no tree required.",
9901
10281
  codes: [
9902
10282
  { code: "DEPENDENCY_CLASS_ON_NON_ORCHESTRATOR", defaultSeverity: "error", summary: "dependencyClass declared on a component that is not an Orchestrator" }
9903
10283
  ],
@@ -10123,257 +10503,341 @@ var init_portal_call_auth = __esm({
10123
10503
  }
10124
10504
  });
10125
10505
 
10126
- // src/core/rules/doctrine/stereotype-dependencies.ts
10127
- var NEVER_INLINE_STATE, storeResolutionHint, isPureLogic, isReadLogic, stereotypeOf, stereotypeDepsRule;
10128
- var init_stereotype_dependencies = __esm({
10129
- "src/core/rules/doctrine/stereotype-dependencies.ts"() {
10506
+ // src/core/rules/doctrine/subsystem-boundary-dependencies.ts
10507
+ var subsystemBoundaryDepsRule;
10508
+ var init_subsystem_boundary_dependencies = __esm({
10509
+ "src/core/rules/doctrine/subsystem-boundary-dependencies.ts"() {
10130
10510
  "use strict";
10131
10511
  init_models();
10132
- NEVER_INLINE_STATE = " Never resolve this by merging the store's state into the consuming component \u2014 state hidden inside a logic block is invisible to the spec and unrecoverable.";
10133
- storeResolutionHint = (consumerType, storeId) => {
10134
- if (consumerType === "Orchestrator") {
10135
- return ` Resolution: wrap "${storeId}" in a Repository pattern (owns: Store + Registry + Index) and depend on that Repository facade instead.${NEVER_INLINE_STATE}`;
10136
- }
10137
- return ` Resolution: wrap "${storeId}" in a Repository pattern and reach it through an Orchestrator that uses the Repository facade.${NEVER_INLINE_STATE}`;
10138
- };
10139
- isPureLogic = (comp) => comp.componentType === "Orchestrator" && comp.dependencyClass === "pure";
10140
- isReadLogic = (comp) => comp.componentType === "Orchestrator" && comp.dependencyClass === "read";
10141
- stereotypeOf = (comp) => comp.dependencyClass ? `${comp.componentType} (dependencyClass ${comp.dependencyClass})` : comp.componentType;
10142
- stereotypeDepsRule = {
10143
- name: "stereotype-dependencies",
10144
- description: "Enforces the component-stereotype interaction matrix and the cross-subsystem shape. The matrix: a Portal reaches the data layer only through Repository/Index READ faces, and writes route through Orchestrators (PORTAL_WRITE_SHORTCUT); Stores are depended upon; Registries write their Store; Adapters are sinks; Views stay passive; an Orchestrator's dependencyClass bounds what it may depend on; a Supervisor reaches data only through workflows; a live Actor is reached through a Supervisor that supervises it; a Query reads its Store; any block may use pure logic. Across subsystems: client Adapter \u2192 published remote Portal, OR a direct in-process edge licensed by a trustedLink declared on the SOURCE subsystem (the published-Portal target requirement applies either way). A governing pack profile may license intra-subsystem edges the matrix refuses via allowedEdges (its platform idiom, with the stated reason); boundary rules stay unrelaxable.",
10512
+ subsystemBoundaryDepsRule = {
10513
+ name: "subsystem-boundary-dependencies",
10514
+ description: "Judges every dependsOn edge that leaves its own subsystem, and every one that resolves nowhere. Across subsystems the shape is client Adapter \u2192 published remote Portal, OR a direct in-process edge licensed by a trustedLink declared on the SOURCE subsystem (the published-Portal target requirement applies either way). A reference that names nothing in this tree is resolved against the stored surface snapshots: a hit is a DECLARED remote portal and the same Adapter requirement applies to it, snapshots of several providers that disagree make the reference ambiguous, and a reference authored to leave this root that no snapshot covers warns instead of erroring. Boundary rules are never relaxed by a pack profile.",
10145
10515
  codes: [
10146
10516
  { code: "INVALID_DEPENDENCY_REFERENCE", defaultSeverity: "error", summary: "dependsOn names a non-existent component" },
10147
10517
  { code: "CROSS_TREE_REF_UNRESOLVED", defaultSeverity: "warning", summary: "Cross-tree dependsOn (super::/:: form) with no surface snapshot covering it" },
10148
10518
  { code: "SURFACE_REF_AMBIGUOUS", defaultSeverity: "error", summary: "Cross-tree dependsOn matched by surface snapshots of several providers with different contracts" },
10149
10519
  { code: "CROSS_SUBSYSTEM_NON_ADAPTER", defaultSeverity: "error", summary: "Non-Adapter component crossing a subsystem boundary" },
10150
10520
  { code: "CROSS_SUBSYSTEM_PRIVATE_ACCESS", defaultSeverity: "error", summary: "Cross-subsystem dependency on an unpublished component" },
10151
- { code: "CROSS_SUBSYSTEM_TARGET_NON_PORTAL", defaultSeverity: "error", summary: "Cross-subsystem hop entering through a non-Portal" },
10152
- { code: "ARCHITECTURE_VIOLATION_PORTAL_DEP", defaultSeverity: "error", summary: "Component depending on a Portal/Observer" },
10153
- { code: "ARCHITECTURE_VIOLATION_PORTAL_FORBIDDEN_DEP", defaultSeverity: "error", summary: "Portal/Observer reaching the data layer directly" },
10154
- { code: "ARCHITECTURE_VIOLATION_STORE_DEP", defaultSeverity: "error", summary: "Store depending on anything but another Store, a backend Adapter or pure logic" },
10155
- { code: "ARCHITECTURE_VIOLATION_REGISTRY_DEP", defaultSeverity: "warning", summary: "Registry depending on anything but its Store, a backend Adapter or pure logic (warning while new; aligned to the standard \xA77 validate\u2192write path)" },
10156
- { code: "ARCHITECTURE_VIOLATION_ADAPTER_DEP", defaultSeverity: "error", summary: "Adapter depending on a Store or on an Orchestrator that is not pure" },
10157
- { code: "ARCHITECTURE_VIOLATION_INDEX_DEP", defaultSeverity: "error", summary: "Index depending on anything but its Store, an Adapter or pure logic" },
10158
- { code: "ARCHITECTURE_VIOLATION_VIEW_DEP", defaultSeverity: "error", summary: "View depending on persistence layers or on logic that is not pure" },
10159
- { code: "PORTAL_WRITE_SHORTCUT", defaultSeverity: "error", summary: "Portal narrative call or dispatch-table binding reaches a write-effect method on a Repository/Index directly \u2014 reads may shortcut, writes route through an Orchestrator (judged on effect-tagged facade methods; untagged methods are not yet judged)" },
10160
- { code: "DEPENDENCY_CLASS_VIOLATION", defaultSeverity: "error", summary: "Orchestrator depending on a component its dependencyClass does not allow: pure logic depends only on pure Orchestrators; read logic also on read Orchestrators, Repositories, Indexes and Adapters" },
10161
- { code: "ARCHITECTURE_VIOLATION_SUPERVISOR_DEP", defaultSeverity: "error", summary: "Supervisor depending on anything but Actors, Orchestrators, Adapters or other Supervisors \u2014 it reaches data only through workflows" },
10162
- { code: "ACTOR_REACHED_WITHOUT_SUPERVISOR", defaultSeverity: "error", summary: "Component depending on a live Actor it does not supervise, without also depending on a Supervisor that supervises it \u2014 a live Actor is reached by id through its Supervisor" },
10163
- { code: "ARCHITECTURE_VIOLATION_QUERY_DEP", defaultSeverity: "error", summary: "Query depending on anything but its Store, a backend Adapter or pure logic" }
10521
+ { code: "CROSS_SUBSYSTEM_TARGET_NON_PORTAL", defaultSeverity: "error", summary: "Cross-subsystem hop entering through a non-Portal" }
10164
10522
  ],
10165
10523
  check(ctx) {
10166
- for (const comp of ctx.components) {
10167
- const isDraftCtx = ctx.isComponentDraft(comp.id);
10168
- const dependencies = comp.dependsOn;
10169
- for (const depId of dependencies) {
10170
- const depComp = ctx.componentMap.get(depId);
10171
- if (!depComp) {
10172
- const external = ctx.isExternalNamespaceRef(depId);
10173
- if (external || ctx.isCollapsedCrossTreeRef(depId, comp.subsystem)) {
10174
- const resolved = ctx.resolveSurfaceRef(depId, comp.subsystem);
10175
- if (resolved.kind === "ambiguous") {
10176
- ctx.addIssue("error", "SURFACE_REF_AMBIGUOUS", ambiguityMessage(resolved, `Component "${comp.id}" depends on`, depId), comp.id, isDraftCtx);
10177
- continue;
10178
- }
10179
- if (resolved.kind === "resolved") {
10180
- if (comp.componentType !== "Adapter") {
10181
- ctx.addIssue(
10182
- "error",
10183
- "CROSS_SUBSYSTEM_NON_ADAPTER",
10184
- `Boundary violation: ${comp.componentType} "${comp.id}" depends directly on "${depId}", a surface of project "${resolved.snapshot.projectName}". Only a local client Adapter may cross a project boundary \u2014 route this hop through an Adapter.`,
10185
- comp.id,
10186
- isDraftCtx,
10187
- true
10188
- );
10189
- }
10190
- continue;
10191
- }
10192
- if (external) {
10193
- ctx.addIssue(
10194
- "warning",
10195
- "CROSS_TREE_REF_UNRESOLVED",
10196
- `Component "${comp.id}" depends on cross-tree component "${depId}", 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.`,
10197
- comp.id,
10198
- isDraftCtx
10199
- );
10200
- continue;
10201
- }
10202
- }
10524
+ for (const edge of ctx.dependencyEdges().all) {
10525
+ if (edge.retired) continue;
10526
+ const comp = edge.from;
10527
+ switch (edge.reach) {
10528
+ case "missing":
10203
10529
  ctx.addIssue(
10204
10530
  "error",
10205
10531
  "INVALID_DEPENDENCY_REFERENCE",
10206
- `Component "${comp.id}" lists dependency "${depId}" which does not exist.`,
10532
+ `Component "${comp.id}" lists dependency "${edge.ref}" which does not exist.`,
10207
10533
  comp.id,
10208
- isDraftCtx
10534
+ edge.draftContext
10209
10535
  );
10210
10536
  continue;
10211
- }
10212
- if (isRetired(comp) || isRetired(depComp)) continue;
10213
- if (depComp.subsystem !== comp.subsystem) {
10214
- const crossDraft = isDraftCtx || ctx.isComponentDraft(depComp.id);
10215
- const sourceSub = ctx.subsystems.find((s) => s.id === comp.subsystem);
10216
- const licensed = sourceSub?.trustedLinks?.some((t) => t.subsystem === depComp.subsystem) ?? false;
10217
- if (comp.componentType !== "Adapter" && !licensed) {
10218
- ctx.addIssue(
10219
- "error",
10220
- "CROSS_SUBSYSTEM_NON_ADAPTER",
10221
- `Boundary violation: ${comp.componentType} "${comp.id}" (subsystem "${comp.subsystem}") depends directly on "${depComp.id}" in subsystem "${depComp.subsystem}". Only a local client Adapter may cross a subsystem boundary \u2014 route this hop through an Adapter that calls "${depComp.subsystem}"'s public interface, or declare a trustedLink on "${comp.subsystem}" (with the reason) to license a direct in-process edge to that peer's published Portal.`,
10222
- comp.id,
10223
- crossDraft
10224
- );
10225
- }
10226
- const targetIsPublic = ctx.publicSet.get(depComp.subsystem)?.has(depComp.id) ?? false;
10227
- if (!targetIsPublic) {
10228
- ctx.addIssue(
10229
- "error",
10230
- "CROSS_SUBSYSTEM_PRIVATE_ACCESS",
10231
- `Boundary violation: "${comp.id}" depends on "${depComp.id}", which is not part of subsystem "${depComp.subsystem}"'s published public surface. Depend on one of its publicInterfaces components instead.`,
10232
- comp.id,
10233
- crossDraft
10234
- );
10235
- } else if (depComp.componentType !== "Portal") {
10236
- const crosser = comp.componentType === "Adapter" ? "client Adapter" : comp.componentType;
10237
- ctx.addIssue(
10238
- "error",
10239
- "CROSS_SUBSYSTEM_TARGET_NON_PORTAL",
10240
- `Boundary violation: ${crosser} "${comp.id}" enters subsystem "${depComp.subsystem}" through "${depComp.id}" (${depComp.componentType}), not its inbound Portal. A cross-subsystem hop must target the remote subsystem's Portal (its front door), which dispatches inward \u2014 publishing/depending on an internal ${depComp.componentType} leaks the boundary and breaks the distribution seam. Expose a Portal for "${depComp.subsystem}" and point "${comp.id}" at it.`,
10241
- comp.id,
10242
- crossDraft
10243
- );
10244
- }
10245
- continue;
10246
- }
10247
- const governingProfile2 = ctx.ext.profiles[ctx.getComponentProfile(comp.id)];
10248
- if (governingProfile2?.allowedEdges?.some((e) => e.from.includes(comp.componentType) && e.to.includes(depComp.componentType))) {
10537
+ case "unpinned":
10538
+ ctx.addIssue(
10539
+ "warning",
10540
+ "CROSS_TREE_REF_UNRESOLVED",
10541
+ `Component "${comp.id}" depends on cross-tree component "${edge.ref}", 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.`,
10542
+ comp.id,
10543
+ edge.draftContext
10544
+ );
10249
10545
  continue;
10250
- }
10251
- const edgeDraft = isDraftCtx || ctx.isComponentDraft(depComp.id);
10252
- const depPure = isPureLogic(depComp);
10253
- const depRead = isReadLogic(depComp);
10254
- const storeHint = depComp.componentType === "Store" ? storeResolutionHint(comp.componentType, depComp.id) : "";
10255
- if (depComp.componentType === "Portal" || depComp.componentType === "Observer") {
10546
+ case "ambiguous":
10256
10547
  ctx.addIssue(
10257
10548
  "error",
10258
- "ARCHITECTURE_VIOLATION_PORTAL_DEP",
10259
- `Architectural violation: Component "${comp.id}" cannot depend on ${depComp.componentType} component "${depComp.id}". ${depComp.componentType}s are top-level entry points/subscribers and cannot be dependencies.`,
10549
+ "SURFACE_REF_AMBIGUOUS",
10550
+ ambiguityMessage(edge.surface, `Component "${comp.id}" depends on`, edge.ref),
10260
10551
  comp.id,
10261
- edgeDraft
10552
+ edge.draftContext
10262
10553
  );
10263
10554
  continue;
10264
- }
10265
- if (comp.componentType === "Portal" || comp.componentType === "Observer") {
10266
- const forbiddenTypes = comp.componentType === "Portal" ? ["Store", "Registry", "Adapter", "Query"] : ["Store", "Registry", "Repository", "Index", "Query"];
10267
- if (forbiddenTypes.includes(depComp.componentType)) {
10555
+ case "surface":
10556
+ if (comp.componentType !== "Adapter") {
10557
+ const provider = edge.surface.kind === "resolved" ? edge.surface.snapshot.projectName : "";
10268
10558
  ctx.addIssue(
10269
10559
  "error",
10270
- "ARCHITECTURE_VIOLATION_PORTAL_FORBIDDEN_DEP",
10271
- `Architectural violation: ${comp.componentType} component "${comp.id}" cannot depend directly on "${depComp.componentType}" component "${depComp.id}". ${comp.componentType}s coordinate through Orchestrators (and Supervisors); they do not reach the data layer directly.` + storeHint,
10560
+ "CROSS_SUBSYSTEM_NON_ADAPTER",
10561
+ `Boundary violation: ${comp.componentType} "${comp.id}" depends directly on "${edge.ref}", a surface of project "${provider}". Only a local client Adapter may cross a project boundary \u2014 route this hop through an Adapter.`,
10272
10562
  comp.id,
10273
- edgeDraft
10563
+ edge.draftContext,
10564
+ true
10274
10565
  );
10275
10566
  }
10276
- }
10277
- if (isPureLogic(comp) && !depPure) {
10278
- ctx.addIssue(
10279
- "error",
10280
- "DEPENDENCY_CLASS_VIOLATION",
10281
- `Architectural violation: Orchestrator "${comp.id}" (dependencyClass pure) cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". Pure logic depends only on pure Orchestrators \u2014 move that dependency to the workflow that calls "${comp.id}", or declare "${comp.id}" read when it reads through read logic, a Repository, an Index or an Adapter.` + storeHint,
10282
- comp.id,
10283
- edgeDraft
10284
- );
10285
- }
10286
- if (isReadLogic(comp) && !depPure && !depRead && !["Repository", "Index", "Adapter"].includes(depComp.componentType)) {
10287
- ctx.addIssue(
10288
- "error",
10289
- "DEPENDENCY_CLASS_VIOLATION",
10290
- `Architectural violation: Orchestrator "${comp.id}" (dependencyClass read) cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". Read logic depends only on pure or read Orchestrators, Repositories, Indexes and Adapters \u2014 move that dependency to the workflow that calls "${comp.id}", or unset the dependencyClass of "${comp.id}" when it is itself a workflow.` + storeHint,
10291
- comp.id,
10292
- edgeDraft
10293
- );
10294
- }
10295
- if (comp.componentType === "Store" && depComp.componentType !== "Store" && depComp.componentType !== "Adapter" && !depPure) {
10296
- ctx.addIssue(
10297
- "error",
10298
- "ARCHITECTURE_VIOLATION_STORE_DEP",
10299
- `Architectural violation: Store component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". Stores may only depend on other Stores, a backend Adapter or pure logic \u2014 Registries and Indexes depend on the Store, never the reverse.`,
10300
- comp.id,
10301
- edgeDraft
10302
- );
10303
- }
10304
- if (comp.componentType === "Registry" && depComp.componentType !== "Store" && depComp.componentType !== "Adapter" && !depPure) {
10305
- ctx.addIssue(
10306
- "warning",
10307
- "ARCHITECTURE_VIOLATION_REGISTRY_DEP",
10308
- `Architectural violation: Registry component "${comp.id}" should not depend on "${depComp.componentType}" component "${depComp.id}". A Registry is the write path to its Store and may depend only on that Store, a backend Adapter, or pure logic that validates the write \u2014 the Registry never updates Indexes and never drives workflow.`,
10309
- comp.id,
10310
- edgeDraft
10311
- );
10312
- }
10313
- if (comp.componentType === "Adapter" && (depComp.componentType === "Store" || depComp.componentType === "Orchestrator" && !depPure)) {
10314
- ctx.addIssue(
10315
- "error",
10316
- "ARCHITECTURE_VIOLATION_ADAPTER_DEP",
10317
- `Architectural violation: Adapter component "${comp.id}" cannot depend on ${stereotypeOf(depComp)} component "${depComp.id}". An Adapter may use pure logic, but never a Store or any other Orchestrator.`,
10318
- comp.id,
10319
- edgeDraft
10320
- );
10321
- }
10322
- if (comp.componentType === "Index" && depComp.componentType !== "Store" && depComp.componentType !== "Adapter" && !depPure) {
10323
- ctx.addIssue(
10324
- "error",
10325
- "ARCHITECTURE_VIOLATION_INDEX_DEP",
10326
- `Architectural violation: Index component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". An Index is a read projection and may depend only on its Store, a backend Adapter or pure logic.`,
10327
- comp.id,
10328
- edgeDraft
10329
- );
10330
- }
10331
- if (comp.componentType === "Query" && depComp.componentType !== "Store" && depComp.componentType !== "Adapter" && !depPure) {
10332
- ctx.addIssue(
10333
- "error",
10334
- "ARCHITECTURE_VIOLATION_QUERY_DEP",
10335
- `Architectural violation: Query "${comp.id}" cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". A Query computes reads over its Store, through a backend Adapter where one serves it, with pure logic \u2014 move any other dependency to the workflow that calls the Repository facade.`,
10336
- comp.id,
10337
- edgeDraft
10338
- );
10339
- }
10340
- if (comp.componentType === "View" && (["Store", "Registry", "Index", "Query", "Adapter", "Repository"].includes(depComp.componentType) || depComp.componentType === "Orchestrator" && !depPure)) {
10567
+ continue;
10568
+ case "cross-subsystem":
10569
+ break;
10570
+ default:
10571
+ continue;
10572
+ }
10573
+ const depComp = edge.to;
10574
+ const sourceSub = ctx.subsystems.find((s) => s.id === comp.subsystem);
10575
+ const licensed = sourceSub?.trustedLinks?.some((t) => t.subsystem === depComp.subsystem) ?? false;
10576
+ if (comp.componentType !== "Adapter" && !licensed) {
10577
+ ctx.addIssue(
10578
+ "error",
10579
+ "CROSS_SUBSYSTEM_NON_ADAPTER",
10580
+ `Boundary violation: ${comp.componentType} "${comp.id}" (subsystem "${comp.subsystem}") depends directly on "${depComp.id}" in subsystem "${depComp.subsystem}". Only a local client Adapter may cross a subsystem boundary \u2014 route this hop through an Adapter that calls "${depComp.subsystem}"'s public interface, or declare a trustedLink on "${comp.subsystem}" (with the reason) to license a direct in-process edge to that peer's published Portal.`,
10581
+ comp.id,
10582
+ edge.draftContext
10583
+ );
10584
+ }
10585
+ const targetIsPublic = ctx.publicSet.get(depComp.subsystem)?.has(depComp.id) ?? false;
10586
+ if (!targetIsPublic) {
10587
+ ctx.addIssue(
10588
+ "error",
10589
+ "CROSS_SUBSYSTEM_PRIVATE_ACCESS",
10590
+ `Boundary violation: "${comp.id}" depends on "${depComp.id}", which is not part of subsystem "${depComp.subsystem}"'s published public surface. Depend on one of its publicInterfaces components instead.`,
10591
+ comp.id,
10592
+ edge.draftContext
10593
+ );
10594
+ } else if (depComp.componentType !== "Portal") {
10595
+ const crosser = comp.componentType === "Adapter" ? "client Adapter" : comp.componentType;
10596
+ ctx.addIssue(
10597
+ "error",
10598
+ "CROSS_SUBSYSTEM_TARGET_NON_PORTAL",
10599
+ `Boundary violation: ${crosser} "${comp.id}" enters subsystem "${depComp.subsystem}" through "${depComp.id}" (${depComp.componentType}), not its inbound Portal. A cross-subsystem hop must target the remote subsystem's Portal (its front door), which dispatches inward \u2014 publishing/depending on an internal ${depComp.componentType} leaks the boundary and breaks the distribution seam. Expose a Portal for "${depComp.subsystem}" and point "${comp.id}" at it.`,
10600
+ comp.id,
10601
+ edge.draftContext
10602
+ );
10603
+ }
10604
+ }
10605
+ }
10606
+ };
10607
+ }
10608
+ });
10609
+
10610
+ // src/core/rules/doctrine/stereotype-terms.ts
10611
+ var NEVER_INLINE_STATE, storeResolutionHint, isPureLogic, isReadLogic, stereotypeOf;
10612
+ var init_stereotype_terms = __esm({
10613
+ "src/core/rules/doctrine/stereotype-terms.ts"() {
10614
+ "use strict";
10615
+ NEVER_INLINE_STATE = " Never resolve this by merging the store's state into the consuming component \u2014 state hidden inside a logic block is invisible to the spec and unrecoverable.";
10616
+ storeResolutionHint = (consumerType, storeId) => {
10617
+ if (consumerType === "Orchestrator") {
10618
+ return ` Resolution: wrap "${storeId}" in a Repository pattern (owns: Store + Registry + Index) and depend on that Repository facade instead.${NEVER_INLINE_STATE}`;
10619
+ }
10620
+ return ` Resolution: wrap "${storeId}" in a Repository pattern and reach it through an Orchestrator that uses the Repository facade.${NEVER_INLINE_STATE}`;
10621
+ };
10622
+ isPureLogic = (comp) => comp.componentType === "Orchestrator" && comp.dependencyClass === "pure";
10623
+ isReadLogic = (comp) => comp.componentType === "Orchestrator" && comp.dependencyClass === "read";
10624
+ stereotypeOf = (comp) => comp.dependencyClass ? `${comp.componentType} (dependencyClass ${comp.dependencyClass})` : comp.componentType;
10625
+ }
10626
+ });
10627
+
10628
+ // src/core/rules/doctrine/logic-dependency-class.ts
10629
+ var logicDependencyClassRule;
10630
+ var init_logic_dependency_class = __esm({
10631
+ "src/core/rules/doctrine/logic-dependency-class.ts"() {
10632
+ "use strict";
10633
+ init_stereotype_terms();
10634
+ logicDependencyClassRule = {
10635
+ name: "logic-dependency-class",
10636
+ description: "Bounds an Orchestrator's dependencies by the dependencyClass it declares: pure logic depends only on pure Orchestrators; read logic also on read Orchestrators, Repositories, Indexes and Adapters; an Orchestrator that declares no class is a workflow and may depend on anything the rest of the matrix allows. Whether read logic calls a WRITE method on one of those is judged only once facade methods carry effect tags.",
10637
+ codes: [
10638
+ { code: "DEPENDENCY_CLASS_VIOLATION", defaultSeverity: "error", summary: "Orchestrator depending on a component its dependencyClass does not allow: pure logic depends only on pure Orchestrators; read logic also on read Orchestrators, Repositories, Indexes and Adapters" }
10639
+ ],
10640
+ check(ctx) {
10641
+ for (const edge of ctx.dependencyEdges().matrix) {
10642
+ const comp = edge.from;
10643
+ const depComp = edge.to;
10644
+ if (depComp.componentType === "Portal" || depComp.componentType === "Observer") continue;
10645
+ const depPure = isPureLogic(depComp);
10646
+ const storeHint = depComp.componentType === "Store" ? storeResolutionHint(comp.componentType, depComp.id) : "";
10647
+ if (isPureLogic(comp) && !depPure) {
10648
+ ctx.addIssue(
10649
+ "error",
10650
+ "DEPENDENCY_CLASS_VIOLATION",
10651
+ `Architectural violation: Orchestrator "${comp.id}" (dependencyClass pure) cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". Pure logic depends only on pure Orchestrators \u2014 move that dependency to the workflow that calls "${comp.id}", or declare "${comp.id}" read when it reads through read logic, a Repository, an Index or an Adapter.` + storeHint,
10652
+ comp.id,
10653
+ edge.draftContext
10654
+ );
10655
+ }
10656
+ if (isReadLogic(comp) && !depPure && !isReadLogic(depComp) && !["Repository", "Index", "Adapter"].includes(depComp.componentType)) {
10657
+ ctx.addIssue(
10658
+ "error",
10659
+ "DEPENDENCY_CLASS_VIOLATION",
10660
+ `Architectural violation: Orchestrator "${comp.id}" (dependencyClass read) cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". Read logic depends only on pure or read Orchestrators, Repositories, Indexes and Adapters \u2014 move that dependency to the workflow that calls "${comp.id}", or unset the dependencyClass of "${comp.id}" when it is itself a workflow.` + storeHint,
10661
+ comp.id,
10662
+ edge.draftContext
10663
+ );
10664
+ }
10665
+ }
10666
+ }
10667
+ };
10668
+ }
10669
+ });
10670
+
10671
+ // src/core/rules/doctrine/data-block-dependencies.ts
10672
+ var dataBlockDepsRule;
10673
+ var init_data_block_dependencies = __esm({
10674
+ "src/core/rules/doctrine/data-block-dependencies.ts"() {
10675
+ "use strict";
10676
+ init_stereotype_terms();
10677
+ dataBlockDepsRule = {
10678
+ name: "data-block-dependencies",
10679
+ description: "Keeps the data layer facing down. A Store may depend only on another Store, a backend Adapter or pure logic \u2014 Registries and Indexes depend on the Store, never the reverse. A Registry is the write path to its Store and may depend on that Store, a backend Adapter, or the pure logic that validates the write. An Index is a read projection and a Query a computed read, each over its Store through a backend Adapter where one serves it. An Adapter is a sink toward the system: it may use pure logic, but never a Store or any other Orchestrator.",
10680
+ codes: [
10681
+ { code: "ARCHITECTURE_VIOLATION_STORE_DEP", defaultSeverity: "error", summary: "Store depending on anything but another Store, a backend Adapter or pure logic" },
10682
+ { code: "ARCHITECTURE_VIOLATION_REGISTRY_DEP", defaultSeverity: "warning", summary: "Registry depending on anything but its Store, a backend Adapter or pure logic (warning while new; aligned to the standard \xA77 validate\u2192write path)" },
10683
+ { code: "ARCHITECTURE_VIOLATION_ADAPTER_DEP", defaultSeverity: "error", summary: "Adapter depending on a Store or on an Orchestrator that is not pure" },
10684
+ { code: "ARCHITECTURE_VIOLATION_INDEX_DEP", defaultSeverity: "error", summary: "Index depending on anything but its Store, an Adapter or pure logic" },
10685
+ { code: "ARCHITECTURE_VIOLATION_QUERY_DEP", defaultSeverity: "error", summary: "Query depending on anything but its Store, a backend Adapter or pure logic" }
10686
+ ],
10687
+ check(ctx) {
10688
+ for (const edge of ctx.dependencyEdges().matrix) {
10689
+ const comp = edge.from;
10690
+ const depComp = edge.to;
10691
+ if (depComp.componentType === "Portal" || depComp.componentType === "Observer") continue;
10692
+ const depPure = isPureLogic(depComp);
10693
+ const ownStoreOnly = depComp.componentType !== "Store" && depComp.componentType !== "Adapter" && !depPure;
10694
+ if (comp.componentType === "Store" && ownStoreOnly) {
10695
+ ctx.addIssue(
10696
+ "error",
10697
+ "ARCHITECTURE_VIOLATION_STORE_DEP",
10698
+ `Architectural violation: Store component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". Stores may only depend on other Stores, a backend Adapter or pure logic \u2014 Registries and Indexes depend on the Store, never the reverse.`,
10699
+ comp.id,
10700
+ edge.draftContext
10701
+ );
10702
+ }
10703
+ if (comp.componentType === "Registry" && ownStoreOnly) {
10704
+ ctx.addIssue(
10705
+ "warning",
10706
+ "ARCHITECTURE_VIOLATION_REGISTRY_DEP",
10707
+ `Architectural violation: Registry component "${comp.id}" should not depend on "${depComp.componentType}" component "${depComp.id}". A Registry is the write path to its Store and may depend only on that Store, a backend Adapter, or pure logic that validates the write \u2014 the Registry never updates Indexes and never drives workflow.`,
10708
+ comp.id,
10709
+ edge.draftContext
10710
+ );
10711
+ }
10712
+ if (comp.componentType === "Adapter" && (depComp.componentType === "Store" || depComp.componentType === "Orchestrator" && !depPure)) {
10713
+ ctx.addIssue(
10714
+ "error",
10715
+ "ARCHITECTURE_VIOLATION_ADAPTER_DEP",
10716
+ `Architectural violation: Adapter component "${comp.id}" cannot depend on ${stereotypeOf(depComp)} component "${depComp.id}". An Adapter may use pure logic, but never a Store or any other Orchestrator.`,
10717
+ comp.id,
10718
+ edge.draftContext
10719
+ );
10720
+ }
10721
+ if (comp.componentType === "Index" && ownStoreOnly) {
10722
+ ctx.addIssue(
10723
+ "error",
10724
+ "ARCHITECTURE_VIOLATION_INDEX_DEP",
10725
+ `Architectural violation: Index component "${comp.id}" cannot depend on "${depComp.componentType}" component "${depComp.id}". An Index is a read projection and may depend only on its Store, a backend Adapter or pure logic.`,
10726
+ comp.id,
10727
+ edge.draftContext
10728
+ );
10729
+ }
10730
+ if (comp.componentType === "Query" && ownStoreOnly) {
10731
+ ctx.addIssue(
10732
+ "error",
10733
+ "ARCHITECTURE_VIOLATION_QUERY_DEP",
10734
+ `Architectural violation: Query "${comp.id}" cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". A Query computes reads over its Store, through a backend Adapter where one serves it, with pure logic \u2014 move any other dependency to the workflow that calls the Repository facade.`,
10735
+ comp.id,
10736
+ edge.draftContext
10737
+ );
10738
+ }
10739
+ }
10740
+ }
10741
+ };
10742
+ }
10743
+ });
10744
+
10745
+ // src/core/rules/doctrine/entrypoint-dependencies.ts
10746
+ var entrypointDepsRule;
10747
+ var init_entrypoint_dependencies = __esm({
10748
+ "src/core/rules/doctrine/entrypoint-dependencies.ts"() {
10749
+ "use strict";
10750
+ init_stereotype_terms();
10751
+ entrypointDepsRule = {
10752
+ name: "entrypoint-dependencies",
10753
+ description: "Judges the edges at the system's entry points and its process layer. Portals and Observers are top-level entry points and subscribers, so nothing may depend on them \u2014 and that is the edge's one finding, which is why no other matrix rule judges it. Downward, a Portal dispatches to Orchestrators and may READ through Indexes and Repository facades but never reaches Store/Registry/Query or an Adapter, while an Observer forwards to one Orchestrator or Supervisor over a message-bus Adapter. A View stays a passive presenter. A Supervisor reaches data only through workflows, and a component depending on a live Actor must also depend on a Supervisor that supervises it.",
10754
+ codes: [
10755
+ { code: "ARCHITECTURE_VIOLATION_PORTAL_DEP", defaultSeverity: "error", summary: "Component depending on a Portal/Observer" },
10756
+ { code: "ARCHITECTURE_VIOLATION_PORTAL_FORBIDDEN_DEP", defaultSeverity: "error", summary: "Portal/Observer reaching the data layer directly" },
10757
+ { code: "ARCHITECTURE_VIOLATION_VIEW_DEP", defaultSeverity: "error", summary: "View depending on persistence layers or on logic that is not pure" },
10758
+ { code: "ARCHITECTURE_VIOLATION_SUPERVISOR_DEP", defaultSeverity: "error", summary: "Supervisor depending on anything but Actors, Orchestrators, Adapters or other Supervisors \u2014 it reaches data only through workflows" },
10759
+ { code: "ACTOR_REACHED_WITHOUT_SUPERVISOR", defaultSeverity: "error", summary: "Component depending on a live Actor it does not supervise, without also depending on a Supervisor that supervises it \u2014 a live Actor is reached by id through its Supervisor" }
10760
+ ],
10761
+ check(ctx) {
10762
+ for (const edge of ctx.dependencyEdges().matrix) {
10763
+ const comp = edge.from;
10764
+ const depComp = edge.to;
10765
+ const storeHint = depComp.componentType === "Store" ? storeResolutionHint(comp.componentType, depComp.id) : "";
10766
+ if (depComp.componentType === "Portal" || depComp.componentType === "Observer") {
10767
+ ctx.addIssue(
10768
+ "error",
10769
+ "ARCHITECTURE_VIOLATION_PORTAL_DEP",
10770
+ `Architectural violation: Component "${comp.id}" cannot depend on ${depComp.componentType} component "${depComp.id}". ${depComp.componentType}s are top-level entry points/subscribers and cannot be dependencies.`,
10771
+ comp.id,
10772
+ edge.draftContext
10773
+ );
10774
+ continue;
10775
+ }
10776
+ if (comp.componentType === "Portal" || comp.componentType === "Observer") {
10777
+ const forbiddenTypes = comp.componentType === "Portal" ? ["Store", "Registry", "Adapter", "Query"] : ["Store", "Registry", "Repository", "Index", "Query"];
10778
+ if (forbiddenTypes.includes(depComp.componentType)) {
10341
10779
  ctx.addIssue(
10342
10780
  "error",
10343
- "ARCHITECTURE_VIOLATION_VIEW_DEP",
10344
- `Architectural violation: View component "${comp.id}" cannot depend on ${stereotypeOf(depComp)} component "${depComp.id}". Views must remain passive UI blocks that use pure logic at most.` + storeHint,
10781
+ "ARCHITECTURE_VIOLATION_PORTAL_FORBIDDEN_DEP",
10782
+ `Architectural violation: ${comp.componentType} component "${comp.id}" cannot depend directly on "${depComp.componentType}" component "${depComp.id}". ${comp.componentType}s coordinate through Orchestrators (and Supervisors); they do not reach the data layer directly.` + storeHint,
10345
10783
  comp.id,
10346
- edgeDraft
10784
+ edge.draftContext
10347
10785
  );
10348
10786
  }
10349
- if (comp.componentType === "Supervisor" && ["Store", "Registry", "Repository", "Index", "Query", "View", "FeatureComponent", "RouterComponent"].includes(depComp.componentType)) {
10787
+ }
10788
+ if (comp.componentType === "View" && (["Store", "Registry", "Index", "Query", "Adapter", "Repository"].includes(depComp.componentType) || depComp.componentType === "Orchestrator" && !isPureLogic(depComp))) {
10789
+ ctx.addIssue(
10790
+ "error",
10791
+ "ARCHITECTURE_VIOLATION_VIEW_DEP",
10792
+ `Architectural violation: View component "${comp.id}" cannot depend on ${stereotypeOf(depComp)} component "${depComp.id}". Views must remain passive UI blocks that use pure logic at most.` + storeHint,
10793
+ comp.id,
10794
+ edge.draftContext
10795
+ );
10796
+ }
10797
+ if (comp.componentType === "Supervisor" && ["Store", "Registry", "Repository", "Index", "Query", "View", "FeatureComponent", "RouterComponent"].includes(depComp.componentType)) {
10798
+ ctx.addIssue(
10799
+ "error",
10800
+ "ARCHITECTURE_VIOLATION_SUPERVISOR_DEP",
10801
+ `Architectural violation: Supervisor "${comp.id}" cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". A Supervisor reaches data only through workflows \u2014 depend on the Orchestrator, Actor or Adapter that does that work instead.` + storeHint,
10802
+ comp.id,
10803
+ edge.draftContext
10804
+ );
10805
+ }
10806
+ if (depComp.componentType === "Actor" && comp.componentType !== "Supervisor") {
10807
+ const reachedThroughSupervisor = comp.dependsOn.some((id) => {
10808
+ const supervisor = ctx.componentMap.get(id);
10809
+ return supervisor?.componentType === "Supervisor" && supervisor.dependsOn.includes(depComp.id);
10810
+ });
10811
+ if (!reachedThroughSupervisor) {
10812
+ const supervisors = ctx.components.filter((c) => c.componentType === "Supervisor" && c.dependsOn.includes(depComp.id)).map((c) => `"${c.id}"`);
10813
+ const remedy = supervisors.length > 0 ? `also depend on ${supervisors.length === 1 ? "its Supervisor" : "one of its Supervisors"} (${supervisors.join(", ")})` : `no Supervisor depends on "${depComp.id}" yet, so give the Actor a Supervisor that depends on it and depend on that Supervisor`;
10350
10814
  ctx.addIssue(
10351
10815
  "error",
10352
- "ARCHITECTURE_VIOLATION_SUPERVISOR_DEP",
10353
- `Architectural violation: Supervisor "${comp.id}" cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". A Supervisor reaches data only through workflows \u2014 depend on the Orchestrator, Actor or Adapter that does that work instead.` + storeHint,
10816
+ "ACTOR_REACHED_WITHOUT_SUPERVISOR",
10817
+ `Architectural violation: ${stereotypeOf(comp)} "${comp.id}" depends on the live Actor "${depComp.id}" without a Supervisor that supervises it. A live Actor is reached by id through a Supervisor that supervises it \u2014 ${remedy}.`,
10354
10818
  comp.id,
10355
- edgeDraft
10819
+ edge.draftContext
10356
10820
  );
10357
10821
  }
10358
- if (depComp.componentType === "Actor" && comp.componentType !== "Supervisor") {
10359
- const reachedThroughSupervisor = comp.dependsOn.some((id) => {
10360
- const supervisor = ctx.componentMap.get(id);
10361
- return supervisor?.componentType === "Supervisor" && supervisor.dependsOn.includes(depComp.id);
10362
- });
10363
- if (!reachedThroughSupervisor) {
10364
- const supervisors = ctx.components.filter((c) => c.componentType === "Supervisor" && c.dependsOn.includes(depComp.id)).map((c) => `"${c.id}"`);
10365
- const remedy = supervisors.length > 0 ? `also depend on ${supervisors.length === 1 ? "its Supervisor" : "one of its Supervisors"} (${supervisors.join(", ")})` : `no Supervisor depends on "${depComp.id}" yet, so give the Actor a Supervisor that depends on it and depend on that Supervisor`;
10366
- ctx.addIssue(
10367
- "error",
10368
- "ACTOR_REACHED_WITHOUT_SUPERVISOR",
10369
- `Architectural violation: ${stereotypeOf(comp)} "${comp.id}" depends on the live Actor "${depComp.id}" without a Supervisor that supervises it. A live Actor is reached by id through a Supervisor that supervises it \u2014 ${remedy}.`,
10370
- comp.id,
10371
- edgeDraft
10372
- );
10373
- }
10374
- }
10375
10822
  }
10376
10823
  }
10824
+ }
10825
+ };
10826
+ }
10827
+ });
10828
+
10829
+ // src/core/rules/doctrine/portal-write-shortcut.ts
10830
+ var portalWriteShortcutRule;
10831
+ var init_portal_write_shortcut = __esm({
10832
+ "src/core/rules/doctrine/portal-write-shortcut.ts"() {
10833
+ "use strict";
10834
+ portalWriteShortcutRule = {
10835
+ name: "portal-write-shortcut",
10836
+ description: "A Portal narrative call step or dispatch-table binding that reaches a write-effect method on a Repository or Index directly is the write shortcut: the Portal\u2192data-facade edge is licensed for reads only, and a write routes through an Orchestrator that owns the workflow. A dispatch step reaches its server only through a table binding, so judging every binding judges each dispatch step that takes it, once, where the route is declared. Methods that carry no effect tag are not judged.",
10837
+ codes: [
10838
+ { code: "PORTAL_WRITE_SHORTCUT", defaultSeverity: "error", summary: "Portal narrative call or dispatch-table binding reaches a write-effect method on a Repository/Index directly \u2014 reads may shortcut, writes route through an Orchestrator (judged on effect-tagged facade methods; untagged methods are not yet judged)" }
10839
+ ],
10840
+ check(ctx) {
10377
10841
  for (const impl of ctx.implementations) {
10378
10842
  const contract = ctx.interfaceMap.get(impl.contract);
10379
10843
  const component = contract ? ctx.componentMap.get(contract.component) : void 0;
@@ -10418,31 +10882,23 @@ var init_stereotype_dependencies = __esm({
10418
10882
  }
10419
10883
  });
10420
10884
 
10421
- // src/core/rules/doctrine/pattern-ownership.ts
10422
- var patternsRule;
10423
- var init_pattern_ownership = __esm({
10424
- "src/core/rules/doctrine/pattern-ownership.ts"() {
10885
+ // src/core/rules/doctrine/pattern-membership.ts
10886
+ var patternMembershipRule;
10887
+ var init_pattern_membership = __esm({
10888
+ "src/core/rules/doctrine/pattern-membership.ts"() {
10425
10889
  "use strict";
10426
10890
  init_models();
10427
- patternsRule = {
10428
- name: "pattern-ownership",
10429
- description: "Patterns (Repository/FeatureComponent/RouterComponent) own member blocks with the containment their definition prescribes; blocks own nothing; a member has exactly one owner; and private members are reachable only via their facade or siblings.",
10891
+ patternMembershipRule = {
10892
+ name: "pattern-membership",
10893
+ description: "Only patterns (Repository/FeatureComponent/RouterComponent) own member blocks, and every pattern owns at least one. Each claim must name a component that exists and is itself a building block \u2014 patterns compose at the subsystem (L1) level, never by owning one another \u2014 and a block has exactly one owner, the first pattern to claim it.",
10430
10894
  codes: [
10431
10895
  { code: "EMPTY_PATTERN", defaultSeverity: "error", summary: "Pattern with no owned member blocks" },
10432
10896
  { code: "BLOCK_OWNS_MEMBERS", defaultSeverity: "error", summary: "Building block using owns" },
10433
10897
  { code: "INVALID_OWNED_MEMBER", defaultSeverity: "error", summary: "owns names a non-existent component" },
10434
- { code: "PATTERN_OWNS_PATTERN", defaultSeverity: "error", summary: "Pattern owning another pattern" },
10435
- { code: "SHARED_OWNED_MEMBER", defaultSeverity: "error", summary: "Block owned by two patterns" },
10436
- { code: "REPOSITORY_CONTAINMENT", defaultSeverity: "error", summary: "Repository owning a non Store/Registry/Index/Query/Adapter member" },
10437
- { code: "FEATURE_COMPONENT_CONTAINMENT", defaultSeverity: "error", summary: "FeatureComponent not owning exactly one Orchestrator + one or more Views" },
10438
- { code: "ROUTER_COMPONENT_CONTAINMENT", defaultSeverity: "error", summary: "RouterComponent not owning exactly one Portal facade, or owning no children to route to" },
10439
- { code: "VISIBILITY_VIOLATION", defaultSeverity: "error", summary: "Dependency on a block privately owned by another pattern" },
10440
- { code: "UNOWNED_STORE", defaultSeverity: "warning", summary: "Store not owned by any pattern \u2014 recommended shape is a Repository; a deliberate standalone Store needs a lint.allow" },
10441
- { code: "REGISTRY_WITHOUT_STORE", defaultSeverity: "warning", summary: "Standalone Registry with no Store to write to \u2014 either mistyped (a fused file-backed store belongs typed Store) or orphaned" },
10442
- { code: "UNOWNED_QUERY", defaultSeverity: "error", summary: "Query not owned by a Repository \u2014 a Query computes reads over its own Repository's Store" }
10898
+ { code: "PATTERN_OWNS_PATTERN", defaultSeverity: "error", summary: "Pattern owning another pattern" },
10899
+ { code: "SHARED_OWNED_MEMBER", defaultSeverity: "error", summary: "Block owned by two patterns" }
10443
10900
  ],
10444
10901
  check(ctx) {
10445
- const ownedBy = /* @__PURE__ */ new Map();
10446
10902
  for (const comp of ctx.components) {
10447
10903
  if (isRetired(comp)) continue;
10448
10904
  const isDraftCtx = ctx.isComponentDraft(comp.id);
@@ -10453,6 +10909,12 @@ var init_pattern_ownership = __esm({
10453
10909
  if (!pattern && comp.owns.length > 0) {
10454
10910
  ctx.addIssue("error", "BLOCK_OWNS_MEMBERS", `Building block "${comp.id}" (${comp.componentType}) cannot own members; only patterns (${Array.from(PATTERN_TYPES).join("/")}) use "owns".`, comp.id, isDraftCtx);
10455
10911
  }
10912
+ }
10913
+ const ownership = ctx.ownershipIndex();
10914
+ for (const comp of ctx.components) {
10915
+ if (isRetired(comp)) continue;
10916
+ const isDraftCtx = ctx.isComponentDraft(comp.id);
10917
+ const pattern = isPattern(comp);
10456
10918
  for (const memberId of comp.owns) {
10457
10919
  const member = ctx.componentMap.get(memberId);
10458
10920
  if (!member) {
@@ -10464,26 +10926,50 @@ var init_pattern_ownership = __esm({
10464
10926
  ctx.addIssue("error", "PATTERN_OWNS_PATTERN", `Pattern "${comp.id}" owns "${memberId}", which is itself a pattern. Patterns own only building blocks \u2014 compose patterns at the subsystem (L1) level.`, comp.id, isDraftCtx);
10465
10927
  continue;
10466
10928
  }
10467
- const firstOwner = ownedBy.get(memberId);
10929
+ const firstOwner = ownership.ownerOf(memberId);
10468
10930
  if (firstOwner && firstOwner !== comp.id) {
10469
10931
  ctx.addIssue("error", "SHARED_OWNED_MEMBER", `Block "${memberId}" is owned by both "${firstOwner}" and "${comp.id}"; a block has exactly one owner.`, comp.id, isDraftCtx);
10470
10932
  }
10471
- if (!firstOwner) ownedBy.set(memberId, comp.id);
10472
10933
  }
10473
- if ((comp.componentType === "FeatureComponent" || comp.componentType === "RouterComponent") && comp.owns.some((memberId) => {
10934
+ }
10935
+ }
10936
+ };
10937
+ }
10938
+ });
10939
+
10940
+ // src/core/rules/doctrine/pattern-containment.ts
10941
+ var REPOSITORY_MEMBERS, patternContainmentRule;
10942
+ var init_pattern_containment = __esm({
10943
+ "src/core/rules/doctrine/pattern-containment.ts"() {
10944
+ "use strict";
10945
+ init_models();
10946
+ REPOSITORY_MEMBERS = /* @__PURE__ */ new Set(["Store", "Registry", "Index", "Query", "Adapter"]);
10947
+ patternContainmentRule = {
10948
+ name: "pattern-containment",
10949
+ description: "Holds each pattern to the containment its definition prescribes. A Repository may own only Store, Registry, Index, Query and (optionally) Adapter, judged member by member. A FeatureComponent owns exactly one Orchestrator (the logic side) and one or more Views (its faces \u2014 list, detail, form \u2014 sharing the one logic component), and nothing else. A RouterComponent owns exactly one Portal as its facade and at least one other child to route to. A counting pattern that still owns a retired member is not judged until that member is migrated: its counts change with the migration, and STEREOTYPE_RETIRED is the one finding.",
10950
+ codes: [
10951
+ { code: "REPOSITORY_CONTAINMENT", defaultSeverity: "error", summary: "Repository owning a non Store/Registry/Index/Query/Adapter member" },
10952
+ { code: "FEATURE_COMPONENT_CONTAINMENT", defaultSeverity: "error", summary: "FeatureComponent not owning exactly one Orchestrator + one or more Views" },
10953
+ { code: "ROUTER_COMPONENT_CONTAINMENT", defaultSeverity: "error", summary: "RouterComponent not owning exactly one Portal facade, or owning no children to route to" }
10954
+ ],
10955
+ check(ctx) {
10956
+ for (const comp of ctx.components) {
10957
+ if (comp.componentType !== "Repository" || isRetired(comp)) continue;
10958
+ const isDraftCtx = ctx.isComponentDraft(comp.id);
10959
+ for (const memberId of comp.owns) {
10474
10960
  const member = ctx.componentMap.get(memberId);
10475
- return member !== void 0 && isRetired(member);
10476
- })) {
10477
- continue;
10478
- }
10479
- if (comp.componentType === "Repository") {
10480
- const allowed = /* @__PURE__ */ new Set(["Store", "Registry", "Index", "Query", "Adapter"]);
10481
- for (const memberId of comp.owns) {
10482
- const member = ctx.componentMap.get(memberId);
10483
- if (!member || isRetired(member) || allowed.has(member.componentType)) continue;
10484
- ctx.addIssue("error", "REPOSITORY_CONTAINMENT", `Repository "${comp.id}" owns "${memberId}" of type ${member.componentType}; a Repository may own only Store, Registry, Index, Query, and (optionally) Adapter.`, comp.id, isDraftCtx);
10485
- }
10961
+ if (!member || isRetired(member) || REPOSITORY_MEMBERS.has(member.componentType)) continue;
10962
+ ctx.addIssue("error", "REPOSITORY_CONTAINMENT", `Repository "${comp.id}" owns "${memberId}" of type ${member.componentType}; a Repository may own only Store, Registry, Index, Query, and (optionally) Adapter.`, comp.id, isDraftCtx);
10486
10963
  }
10964
+ }
10965
+ for (const comp of ctx.components) {
10966
+ if (comp.componentType !== "FeatureComponent" && comp.componentType !== "RouterComponent") continue;
10967
+ if (isRetired(comp)) continue;
10968
+ if (comp.owns.some((memberId) => {
10969
+ const member = ctx.componentMap.get(memberId);
10970
+ return member !== void 0 && isRetired(member);
10971
+ })) continue;
10972
+ const isDraftCtx = ctx.isComponentDraft(comp.id);
10487
10973
  if (comp.componentType === "FeatureComponent") {
10488
10974
  let orchestrators = 0;
10489
10975
  let views = 0;
@@ -10514,45 +11000,83 @@ var init_pattern_ownership = __esm({
10514
11000
  }
10515
11001
  }
10516
11002
  }
11003
+ }
11004
+ };
11005
+ }
11006
+ });
11007
+
11008
+ // src/core/rules/doctrine/unowned-blocks.ts
11009
+ var unownedBlocksRule;
11010
+ var init_unowned_blocks = __esm({
11011
+ "src/core/rules/doctrine/unowned-blocks.ts"() {
11012
+ "use strict";
11013
+ unownedBlocksRule = {
11014
+ name: "unowned-blocks",
11015
+ description: `Judges the data blocks no pattern owns. A Store may stand alone deliberately \u2014 the sanctioned lightweight form for genuinely simple state, acknowledged with a lint.allow \u2014 while the recommended shape stays a Repository; what must never happen is folding the state into a consuming component, where no spec, diagram or conformance check can see it again. A Registry standing alone with no Store to write to is either mistyped (the "file-backed Registry" idiom, a fused persistent store that belongs typed Store) or orphaned. A Query has no standalone form at all: it computes reads over its own Repository's Store.`,
11016
+ codes: [
11017
+ { code: "UNOWNED_STORE", defaultSeverity: "warning", summary: "Store not owned by any pattern \u2014 recommended shape is a Repository; a deliberate standalone Store needs a lint.allow" },
11018
+ { code: "REGISTRY_WITHOUT_STORE", defaultSeverity: "warning", summary: "Standalone Registry with no Store to write to \u2014 either mistyped (a fused file-backed store belongs typed Store) or orphaned" },
11019
+ { code: "UNOWNED_QUERY", defaultSeverity: "error", summary: "Query not owned by a Repository \u2014 a Query computes reads over its own Repository's Store" }
11020
+ ],
11021
+ check(ctx) {
11022
+ const ownership = ctx.ownershipIndex();
10517
11023
  for (const comp of ctx.components) {
10518
- if (comp.componentType !== "Store" || ownedBy.has(comp.id)) continue;
10519
- ctx.addIssue(
10520
- "warning",
10521
- "UNOWNED_STORE",
10522
- `Store "${comp.id}" is not owned by any pattern. The recommended shape for held state is a Repository (owns: ["${comp.id}", its Registry, its Index]) with consumers on the facade. For genuinely simple state a deliberately standalone Store is the sanctioned lightweight form \u2014 keep it visible as this Store, reachable from the workflow layer (Orchestrator/Supervisor/Actor), and acknowledge it with a lint.allow reason. Never take the third path of merging the state into a consuming component: state hidden inside a logic block disappears from the architecture permanently.`,
10523
- comp.id,
10524
- ctx.isComponentDraft(comp.id)
10525
- );
10526
- }
10527
- for (const comp of ctx.components) {
10528
- if (comp.componentType !== "Registry" || ownedBy.has(comp.id)) continue;
10529
- const hasStoreDep = comp.dependsOn.some((depId) => ctx.componentMap.get(depId)?.componentType === "Store");
10530
- if (hasStoreDep) continue;
10531
- ctx.addIssue(
10532
- "warning",
10533
- "REGISTRY_WITHOUT_STORE",
10534
- `Registry "${comp.id}" stands alone with no Store to write to. A Registry is the write path to a Store \u2014 if this component itself holds the persisted state (a file-backed record/config store), retype it as a Store with the honest durability (read-through for no-RAM-copy file I/O) so the durability machinery can see it; otherwise wire its Store, or lint.allow with a reason.`,
10535
- comp.id,
10536
- ctx.isComponentDraft(comp.id)
10537
- );
10538
- }
10539
- for (const comp of ctx.components) {
10540
- if (comp.componentType !== "Query" || ownedBy.has(comp.id)) continue;
10541
- ctx.addIssue(
10542
- "error",
10543
- "UNOWNED_QUERY",
10544
- `Query "${comp.id}" is not owned by any pattern. A Query computes reads over its own Repository's Store \u2014 make it a member of the Repository whose Store it reads (owns: ["${comp.id}", \u2026]).`,
10545
- comp.id,
10546
- ctx.isComponentDraft(comp.id)
10547
- );
11024
+ if (ownership.ownedMembers.has(comp.id)) continue;
11025
+ const isDraftCtx = ctx.isComponentDraft(comp.id);
11026
+ if (comp.componentType === "Store") {
11027
+ ctx.addIssue(
11028
+ "warning",
11029
+ "UNOWNED_STORE",
11030
+ `Store "${comp.id}" is not owned by any pattern. The recommended shape for held state is a Repository (owns: ["${comp.id}", its Registry, its Index]) with consumers on the facade. For genuinely simple state a deliberately standalone Store is the sanctioned lightweight form \u2014 keep it visible as this Store, reachable from the workflow layer (Orchestrator/Supervisor/Actor), and acknowledge it with a lint.allow reason. Never take the third path of merging the state into a consuming component: state hidden inside a logic block disappears from the architecture permanently.`,
11031
+ comp.id,
11032
+ isDraftCtx
11033
+ );
11034
+ }
11035
+ if (comp.componentType === "Registry" && !comp.dependsOn.some((depId) => ctx.componentMap.get(depId)?.componentType === "Store")) {
11036
+ ctx.addIssue(
11037
+ "warning",
11038
+ "REGISTRY_WITHOUT_STORE",
11039
+ `Registry "${comp.id}" stands alone with no Store to write to. A Registry is the write path to a Store \u2014 if this component itself holds the persisted state (a file-backed record/config store), retype it as a Store with the honest durability (read-through for no-RAM-copy file I/O) so the durability machinery can see it; otherwise wire its Store, or lint.allow with a reason.`,
11040
+ comp.id,
11041
+ isDraftCtx
11042
+ );
11043
+ }
11044
+ if (comp.componentType === "Query") {
11045
+ ctx.addIssue(
11046
+ "error",
11047
+ "UNOWNED_QUERY",
11048
+ `Query "${comp.id}" is not owned by any pattern. A Query computes reads over its own Repository's Store \u2014 make it a member of the Repository whose Store it reads (owns: ["${comp.id}", \u2026]).`,
11049
+ comp.id,
11050
+ isDraftCtx
11051
+ );
11052
+ }
10548
11053
  }
11054
+ }
11055
+ };
11056
+ }
11057
+ });
11058
+
11059
+ // src/core/rules/doctrine/member-visibility.ts
11060
+ var memberVisibilityRule;
11061
+ var init_member_visibility = __esm({
11062
+ "src/core/rules/doctrine/member-visibility.ts"() {
11063
+ "use strict";
11064
+ init_models();
11065
+ memberVisibilityRule = {
11066
+ name: "member-visibility",
11067
+ description: "A component may depend on a block within its own group (it is the owning pattern, or a sibling member of the same pattern), on any pattern facade, or on a standalone block \u2014 never on a block privately owned by ANOTHER pattern, which must be reached through that pattern's facade. A retired depending component is skipped, as everywhere in this family.",
11068
+ codes: [
11069
+ { code: "VISIBILITY_VIOLATION", defaultSeverity: "error", summary: "Dependency on a block privately owned by another pattern" }
11070
+ ],
11071
+ check(ctx) {
11072
+ const ownership = ctx.ownershipIndex();
10549
11073
  for (const comp of ctx.components) {
10550
11074
  if (isRetired(comp)) continue;
10551
11075
  for (const depId of comp.dependsOn) {
10552
- const owner = ownedBy.get(depId);
11076
+ const owner = ownership.ownerOf(depId);
10553
11077
  if (!owner) continue;
10554
11078
  if (owner === comp.id) continue;
10555
- if (ownedBy.get(comp.id) === owner) continue;
11079
+ if (ownership.ownerOf(comp.id) === owner) continue;
10556
11080
  ctx.addIssue("error", "VISIBILITY_VIOLATION", `Component "${comp.id}" depends on "${depId}", which is privately owned by pattern "${owner}". Depend on the facade "${owner}" instead.`, comp.id, ctx.isComponentDraft(comp.id) || ctx.isComponentDraft(depId));
10557
11081
  }
10558
11082
  }
@@ -10607,28 +11131,28 @@ var init_facade_forwarding = __esm({
10607
11131
  }
10608
11132
  });
10609
11133
 
10610
- // src/core/rules/extension/architectural-profiles.ts
10611
- var BACKEND_LIKE, FRONTEND_LIKE, PROJECT_KINDS2, profilesRule;
10612
- var init_architectural_profiles = __esm({
10613
- "src/core/rules/extension/architectural-profiles.ts"() {
11134
+ // src/core/rules/extension/profile-registration.ts
11135
+ var PROJECT_KINDS2, profileRegistrationRule;
11136
+ var init_profile_registration = __esm({
11137
+ "src/core/rules/extension/profile-registration.ts"() {
10614
11138
  "use strict";
10615
11139
  init_types();
10616
- BACKEND_LIKE = /* @__PURE__ */ new Set(["backend", "lowlevel-os", "game-ecs", "realtime-embedded", "plc-cyclic"]);
10617
- FRONTEND_LIKE = /* @__PURE__ */ new Set(["frontend-reactive", "frontend-controller"]);
10618
11140
  PROJECT_KINDS2 = new Set(PROJECT_KINDS);
10619
- profilesRule = {
10620
- name: "architectural-profiles",
10621
- description: "Per-profile stereotype constraints: View/FeatureComponent/RouterComponent only in frontend profiles; Actor/Supervisor forbidden in plc-cyclic (single scan cycle); Actor/Supervisor in frontend profiles warned. Extension packs may register custom profiles (family + forbidden/discouraged stereotype lists); unknown profile names are flagged.",
11141
+ profileRegistrationRule = {
11142
+ name: "profile-registration",
11143
+ 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.",
10622
11144
  codes: [
10623
- { code: "FRONTEND_STEREOTYPE_IN_BACKEND", defaultSeverity: "error", summary: "Frontend stereotype in a backend-oriented subsystem" },
10624
- { code: "PLC_CYCLIC_CONCURRENCY_VIOLATION", defaultSeverity: "error", summary: "Concurrent stereotype in a PLC-cyclic profile" },
10625
- { code: "BACKEND_STEREOTYPE_IN_FRONTEND", defaultSeverity: "warning", summary: "Backend runtime stereotype in a frontend subsystem" },
10626
- { code: "PROFILE_FORBIDDEN_STEREOTYPE", defaultSeverity: "error", summary: "Stereotype forbidden by the governing pack-defined profile" },
10627
- { code: "PROFILE_DISCOURAGED_STEREOTYPE", defaultSeverity: "warning", summary: "Stereotype discouraged by the governing pack-defined profile" },
10628
11145
  { code: "UNKNOWN_PROFILE", defaultSeverity: "warning", summary: "Profile name is neither built-in nor registered by an extension pack" }
10629
11146
  ],
10630
11147
  check(ctx) {
10631
11148
  const registered = /* @__PURE__ */ new Set([...BUILTIN_PROFILES, ...Object.keys(ctx.ext.profiles)]);
11149
+ if (!registered.has(ctx.projectType) && !PROJECT_KINDS2.has(ctx.projectType)) {
11150
+ ctx.addIssue(
11151
+ "warning",
11152
+ "UNKNOWN_PROFILE",
11153
+ `projectType "${ctx.projectType}" (project.yaml) is neither a built-in profile/kind nor registered by a loaded extension pack. Components default to backend doctrine.`
11154
+ );
11155
+ }
10632
11156
  for (const sub of ctx.subsystems) {
10633
11157
  if (sub.profile && !registered.has(sub.profile)) {
10634
11158
  ctx.addIssue(
@@ -10639,13 +11163,27 @@ var init_architectural_profiles = __esm({
10639
11163
  );
10640
11164
  }
10641
11165
  }
10642
- if (!registered.has(ctx.projectType) && !PROJECT_KINDS2.has(ctx.projectType)) {
10643
- ctx.addIssue(
10644
- "warning",
10645
- "UNKNOWN_PROFILE",
10646
- `projectType "${ctx.projectType}" (project.yaml) is neither a built-in profile/kind nor registered by a loaded extension pack. Components default to backend doctrine.`
10647
- );
10648
- }
11166
+ }
11167
+ };
11168
+ }
11169
+ });
11170
+
11171
+ // src/core/rules/extension/profile-stereotype-fencing.ts
11172
+ var BACKEND_LIKE, FRONTEND_LIKE, profileStereotypeFencingRule;
11173
+ var init_profile_stereotype_fencing = __esm({
11174
+ "src/core/rules/extension/profile-stereotype-fencing.ts"() {
11175
+ "use strict";
11176
+ BACKEND_LIKE = /* @__PURE__ */ new Set(["backend", "lowlevel-os", "game-ecs", "realtime-embedded", "plc-cyclic"]);
11177
+ FRONTEND_LIKE = /* @__PURE__ */ new Set(["frontend-reactive", "frontend-controller"]);
11178
+ profileStereotypeFencingRule = {
11179
+ name: "profile-stereotype-fencing",
11180
+ 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.",
11181
+ codes: [
11182
+ { code: "FRONTEND_STEREOTYPE_IN_BACKEND", defaultSeverity: "error", summary: "Frontend stereotype in a backend-oriented subsystem" },
11183
+ { code: "PLC_CYCLIC_CONCURRENCY_VIOLATION", defaultSeverity: "error", summary: "Concurrent stereotype in a PLC-cyclic profile" },
11184
+ { code: "BACKEND_STEREOTYPE_IN_FRONTEND", defaultSeverity: "warning", summary: "Backend runtime stereotype in a frontend subsystem" }
11185
+ ],
11186
+ check(ctx) {
10649
11187
  for (const comp of ctx.components) {
10650
11188
  const isDraftCtx = ctx.isComponentDraft(comp.id);
10651
11189
  const profile = ctx.getComponentProfile(comp.id);
@@ -10685,28 +11223,50 @@ var init_architectural_profiles = __esm({
10685
11223
  );
10686
11224
  }
10687
11225
  }
10688
- if (packDef) {
10689
- for (const rule of packDef.forbiddenStereotypes) {
10690
- if (rule.types.includes(comp.componentType)) {
10691
- ctx.addIssue(
10692
- "error",
10693
- "PROFILE_FORBIDDEN_STEREOTYPE",
10694
- `Component "${comp.id}" is a ${comp.componentType}, forbidden by profile "${profile}": ${rule.reason}`,
10695
- comp.id,
10696
- isDraftCtx
10697
- );
10698
- }
11226
+ }
11227
+ }
11228
+ };
11229
+ }
11230
+ });
11231
+
11232
+ // src/core/rules/extension/pack-profile-stereotypes.ts
11233
+ var packProfileStereotypesRule;
11234
+ var init_pack_profile_stereotypes = __esm({
11235
+ "src/core/rules/extension/pack-profile-stereotypes.ts"() {
11236
+ "use strict";
11237
+ packProfileStereotypesRule = {
11238
+ name: "pack-profile-stereotypes",
11239
+ 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.",
11240
+ codes: [
11241
+ { code: "PROFILE_FORBIDDEN_STEREOTYPE", defaultSeverity: "error", summary: "Stereotype forbidden by the governing pack-defined profile" },
11242
+ { code: "PROFILE_DISCOURAGED_STEREOTYPE", defaultSeverity: "warning", summary: "Stereotype discouraged by the governing pack-defined profile" }
11243
+ ],
11244
+ check(ctx) {
11245
+ for (const comp of ctx.components) {
11246
+ const profile = ctx.getComponentProfile(comp.id);
11247
+ const packDef = ctx.ext.profiles[profile];
11248
+ if (!packDef) continue;
11249
+ const isDraftCtx = ctx.isComponentDraft(comp.id);
11250
+ for (const rule of packDef.forbiddenStereotypes) {
11251
+ if (rule.types.includes(comp.componentType)) {
11252
+ ctx.addIssue(
11253
+ "error",
11254
+ "PROFILE_FORBIDDEN_STEREOTYPE",
11255
+ `Component "${comp.id}" is a ${comp.componentType}, forbidden by profile "${profile}": ${rule.reason}`,
11256
+ comp.id,
11257
+ isDraftCtx
11258
+ );
10699
11259
  }
10700
- for (const rule of packDef.discouragedStereotypes) {
10701
- if (rule.types.includes(comp.componentType)) {
10702
- ctx.addIssue(
10703
- "warning",
10704
- "PROFILE_DISCOURAGED_STEREOTYPE",
10705
- `Component "${comp.id}" is a ${comp.componentType}, discouraged by profile "${profile}": ${rule.reason}`,
10706
- comp.id,
10707
- isDraftCtx
10708
- );
10709
- }
11260
+ }
11261
+ for (const rule of packDef.discouragedStereotypes) {
11262
+ if (rule.types.includes(comp.componentType)) {
11263
+ ctx.addIssue(
11264
+ "warning",
11265
+ "PROFILE_DISCOURAGED_STEREOTYPE",
11266
+ `Component "${comp.id}" is a ${comp.componentType}, discouraged by profile "${profile}": ${rule.reason}`,
11267
+ comp.id,
11268
+ isDraftCtx
11269
+ );
10710
11270
  }
10711
11271
  }
10712
11272
  }
@@ -11043,22 +11603,20 @@ var init_dependency_cycles = __esm({
11043
11603
  }
11044
11604
  });
11045
11605
 
11046
- // src/core/rules/wiring/dispatch-tables.ts
11047
- var dispatchRule;
11048
- var init_dispatch_tables = __esm({
11049
- "src/core/rules/wiring/dispatch-tables.ts"() {
11606
+ // src/core/rules/wiring/dispatch-table-bindings.ts
11607
+ var dispatchTableBindingsRule;
11608
+ var init_dispatch_table_bindings = __esm({
11609
+ "src/core/rules/wiring/dispatch-table-bindings.ts"() {
11050
11610
  "use strict";
11051
- dispatchRule = {
11052
- name: "dispatch-tables",
11053
- description: "Portal dispatch tables must bind every capability to an existing component.method inside the portal's own subsystem (the portal dispatches inward), with no duplicate capabilities; dispatch narrative steps must route a declared capability through a Portal that actually serves it.",
11611
+ dispatchTableBindingsRule = {
11612
+ name: "dispatch-table-bindings",
11613
+ 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.",
11054
11614
  codes: [
11055
11615
  { code: "DISPATCH_ON_NON_PORTAL", defaultSeverity: "error", summary: "Dispatch table declared on a component that is not a Portal" },
11056
11616
  { code: "DUPLICATE_CAPABILITY", defaultSeverity: "error", summary: "Capability bound more than once in one dispatch table" },
11057
11617
  { 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)" },
11058
11618
  { code: "DISPATCH_CROSS_SUBSYSTEM", defaultSeverity: "error", summary: "Dispatch binding targets a component outside the portal's subsystem" },
11059
- { code: "UNDECLARED_DISPATCH_TARGET", defaultSeverity: "error", summary: "Dispatch binding targets a component the portal does not depend on or own" },
11060
- { code: "MALFORMED_DISPATCH_STEP", defaultSeverity: "error", summary: "Dispatch step missing its capability (targetComponent presence is the contracts rule's finding)" },
11061
- { code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Dispatch step asserts a guarantee the bound capability method does not declare" }
11619
+ { code: "UNDECLARED_DISPATCH_TARGET", defaultSeverity: "error", summary: "Dispatch binding targets a component the portal does not depend on or own" }
11062
11620
  ],
11063
11621
  check(ctx) {
11064
11622
  for (const comp of ctx.components) {
@@ -11125,6 +11683,25 @@ var init_dispatch_tables = __esm({
11125
11683
  }
11126
11684
  }
11127
11685
  }
11686
+ }
11687
+ };
11688
+ }
11689
+ });
11690
+
11691
+ // src/core/rules/wiring/dispatch-step-routing.ts
11692
+ var dispatchStepRoutingRule;
11693
+ var init_dispatch_step_routing = __esm({
11694
+ "src/core/rules/wiring/dispatch-step-routing.ts"() {
11695
+ "use strict";
11696
+ dispatchStepRoutingRule = {
11697
+ name: "dispatch-step-routing",
11698
+ 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.",
11699
+ codes: [
11700
+ { code: "MALFORMED_DISPATCH_STEP", defaultSeverity: "error", summary: "Dispatch step missing its capability (targetComponent presence is narrative-target-references' finding)" },
11701
+ { 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)" },
11702
+ { code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Dispatch step asserts a guarantee the bound capability method does not declare" }
11703
+ ],
11704
+ check(ctx) {
11128
11705
  for (const impl of ctx.implementations) {
11129
11706
  const isDraftCtx = ctx.isImplementationDraft(impl);
11130
11707
  for (const implMethod of impl.methods) {
@@ -11879,35 +12456,25 @@ var init_event_topology = __esm({
11879
12456
  }
11880
12457
  });
11881
12458
 
11882
- // src/core/rules/conformance/structural-conformance.ts
11883
- var quoteList, structuralConformanceRule;
11884
- var init_structural_conformance = __esm({
11885
- "src/core/rules/conformance/structural-conformance.ts"() {
12459
+ // src/core/rules/conformance/source-file-linkage.ts
12460
+ var quoteList, sourceFileLinkageRule;
12461
+ var init_source_file_linkage = __esm({
12462
+ "src/core/rules/conformance/source-file-linkage.ts"() {
11886
12463
  "use strict";
11887
12464
  init_models();
11888
12465
  quoteList = (names) => names.map((n) => `"${n}"`).join(", ");
11889
- structuralConformanceRule = {
11890
- name: "structural-conformance",
11891
- 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 every L3 contract method must be realized in its own source file (the method's sourcePath, else the implementation's) at its conformance tier (declared | anchored | off; Portals default to anchored, everything else to declared; per-method `symbol` maps intent-language names to code names), and every finding code a contract method declares must be anchored in that file \u2014 as a string literal or, at exact grade, a property-access name (UNREALIZED_FINDING). A file that escapes the root, is missing or cannot be read is reported once and blocks only the methods realized in it. Findings carry the analysis grade (exact AST | pattern table | generic scan) so weaker analysis is visible. Implementations under chained subsystems (projectPath) validate standalone in their own project run and are skipped here.",
12466
+ sourceFileLinkageRule = {
12467
+ name: "source-file-linkage",
12468
+ 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.",
11892
12469
  codes: [
11893
12470
  { 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" },
11894
12471
  { 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" },
11895
12472
  { 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)" },
11896
- { 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" },
11897
- { 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)" },
11898
12473
  { 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" },
11899
12474
  { code: "CONFORMANCE_DEGRADED", defaultSeverity: "warning", summary: "TypeScript/JavaScript files were analyzed below exact grade (compiler not resolvable) \u2014 dependency conformance skips them" }
11900
12475
  ],
11901
12476
  check(ctx) {
11902
- const lookups = /* @__PURE__ */ new Map();
11903
- for (const facts of ctx.codeModel.files) {
11904
- lookups.set(pathKey(facts.path), {
11905
- declared: /* @__PURE__ */ new Set([...facts.declaredNames, ...facts.exportedNames]),
11906
- anchored: /* @__PURE__ */ new Set([...facts.declaredNames, ...facts.exportedNames, ...facts.anchoredNames]),
11907
- findingAnchors: new Set(facts.anchoredNames),
11908
- facts
11909
- });
11910
- }
12477
+ const code = ctx.codeIndex();
11911
12478
  const degradedTsFiles = ctx.codeModel.files.filter(
11912
12479
  (f) => f.status === "analyzed" && (f.language === "typescript" || f.language === "javascript") && f.analysisGrade !== "exact"
11913
12480
  );
@@ -11925,8 +12492,7 @@ var init_structural_conformance = __esm({
11925
12492
  if (!component) continue;
11926
12493
  if (ctx.isInChainedSubproject(component.subsystem)) continue;
11927
12494
  const draft = ctx.isImplementationDraft(impl);
11928
- const methodImplOf = (name) => impl.methods.find((m) => m.name === name);
11929
- const unlinked = contract.methods.filter((method) => !methodSourceFile(methodImplOf(method.name) ?? {}, impl.sourcePath)).map((method) => method.name);
12495
+ const unlinked = contract.methods.filter((method) => !methodSourceFile(impl.methods.find((m) => m.name === method.name) ?? {}, impl.sourcePath)).map((method) => method.name);
11930
12496
  const noFileAtAll = implementationSourceFiles(impl).length === 0;
11931
12497
  if (unlinked.length > 0 || noFileAtAll) {
11932
12498
  const hasExternalSource = (component.externalLinks ?? []).some((l) => l.type === "implementation");
@@ -11956,7 +12522,7 @@ var init_structural_conformance = __esm({
11956
12522
  const key = pathKey(file);
11957
12523
  if (reported.has(key)) continue;
11958
12524
  reported.add(key);
11959
- const facts = lookups.get(key)?.facts;
12525
+ const facts = code.factsAt(key);
11960
12526
  if (!facts || facts.status === "analyzed") continue;
11961
12527
  const isImplementationFile = !!impl.sourcePath && pathKey(impl.sourcePath) === key;
11962
12528
  const users = isImplementationFile ? [] : impl.methods.filter((m) => m.sourcePath && pathKey(m.sourcePath) === key).map((m) => m.name);
@@ -11987,25 +12553,45 @@ var init_structural_conformance = __esm({
11987
12553
  );
11988
12554
  }
11989
12555
  }
12556
+ }
12557
+ }
12558
+ };
12559
+ }
12560
+ });
12561
+
12562
+ // src/core/rules/conformance/method-realization.ts
12563
+ var methodRealizationRule;
12564
+ var init_method_realization = __esm({
12565
+ "src/core/rules/conformance/method-realization.ts"() {
12566
+ "use strict";
12567
+ init_models();
12568
+ methodRealizationRule = {
12569
+ name: "method-realization",
12570
+ 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.",
12571
+ codes: [
12572
+ { 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" }
12573
+ ],
12574
+ check(ctx) {
12575
+ const code = ctx.codeIndex();
12576
+ for (const impl of ctx.implementations) {
12577
+ const contract = ctx.interfaceMap.get(impl.contract);
12578
+ if (!contract) continue;
12579
+ const component = ctx.componentMap.get(contract.component);
12580
+ if (!component) continue;
12581
+ if (ctx.isInChainedSubproject(component.subsystem)) continue;
12582
+ const draft = ctx.isImplementationDraft(impl);
11990
12583
  const specTier = impl.conformance ?? defaultConformanceTier(component);
11991
- const checkedFile = (methodName) => {
11992
- const methodImpl = methodImplOf(methodName);
12584
+ for (const method of contract.methods) {
12585
+ const methodImpl = impl.methods.find((m) => m.name === method.name);
11993
12586
  const tier = methodImpl?.conformance ?? specTier;
11994
- if (tier === "off") return void 0;
12587
+ if (tier === "off") continue;
11995
12588
  const file = methodSourceFile(methodImpl ?? {}, impl.sourcePath);
11996
- if (!file) return void 0;
11997
- const lookup = lookups.get(pathKey(file));
11998
- if (!lookup || lookup.facts.status !== "analyzed") return void 0;
11999
- return { file, tier, lookup };
12000
- };
12001
- for (const method of contract.methods) {
12002
- const checked = checkedFile(method.name);
12003
- if (!checked) continue;
12004
- const { file, tier, lookup } = checked;
12005
- const methodImpl = methodImplOf(method.name);
12589
+ if (!file) continue;
12590
+ const facts = code.factsAt(file);
12591
+ if (!facts || facts.status !== "analyzed") continue;
12006
12592
  const symbol = methodImpl?.symbol ?? method.name;
12007
- const inDeclared = lookup.declared.has(symbol);
12008
- const inAnchored = inDeclared || lookup.anchored.has(symbol);
12593
+ const inDeclared = code.declarationsAt(file).has(symbol);
12594
+ const inAnchored = inDeclared || code.anchorsAt(file).has(symbol);
12009
12595
  const realized = tier === "declared" ? inDeclared : inAnchored;
12010
12596
  if (realized) continue;
12011
12597
  const label = methodImpl?.symbol ? `"${method.name}" (symbol "${symbol}")` : `"${method.name}"`;
@@ -12013,22 +12599,55 @@ var init_structural_conformance = __esm({
12013
12599
  ctx.addIssue(
12014
12600
  "warning",
12015
12601
  "UNREALIZED_METHOD",
12016
- `Method ${label} of contract "${impl.contract}" is not realized in "${file}" at the "${tier}" tier (analysis grade: ${lookup.facts.analysisGrade}).${weakHint}`,
12602
+ `Method ${label} of contract "${impl.contract}" is not realized in "${file}" at the "${tier}" tier (analysis grade: ${facts.analysisGrade}).${weakHint}`,
12017
12603
  impl.id,
12018
12604
  draft
12019
12605
  );
12020
12606
  }
12607
+ }
12608
+ }
12609
+ };
12610
+ }
12611
+ });
12612
+
12613
+ // src/core/rules/conformance/finding-realization.ts
12614
+ var findingRealizationRule;
12615
+ var init_finding_realization = __esm({
12616
+ "src/core/rules/conformance/finding-realization.ts"() {
12617
+ "use strict";
12618
+ init_models();
12619
+ findingRealizationRule = {
12620
+ name: "finding-realization",
12621
+ 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.",
12622
+ codes: [
12623
+ { 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)" }
12624
+ ],
12625
+ check(ctx) {
12626
+ const code = ctx.codeIndex();
12627
+ for (const impl of ctx.implementations) {
12628
+ const contract = ctx.interfaceMap.get(impl.contract);
12629
+ if (!contract) continue;
12630
+ const component = ctx.componentMap.get(contract.component);
12631
+ if (!component) continue;
12632
+ if (ctx.isInChainedSubproject(component.subsystem)) continue;
12633
+ const draft = ctx.isImplementationDraft(impl);
12634
+ const specTier = impl.conformance ?? defaultConformanceTier(component);
12021
12635
  for (const method of contract.methods) {
12022
12636
  if (!method.findings?.length) continue;
12023
- const checked = checkedFile(method.name);
12024
- if (!checked) continue;
12025
- const { file, lookup } = checked;
12637
+ const methodImpl = impl.methods.find((m) => m.name === method.name);
12638
+ const tier = methodImpl?.conformance ?? specTier;
12639
+ if (tier === "off") continue;
12640
+ const file = methodSourceFile(methodImpl ?? {}, impl.sourcePath);
12641
+ if (!file) continue;
12642
+ const facts = code.factsAt(file);
12643
+ if (!facts || facts.status !== "analyzed") continue;
12644
+ const findingAnchors = code.findingAnchorsAt(file);
12026
12645
  for (const finding of method.findings) {
12027
- if (lookup.findingAnchors.has(finding.code)) continue;
12646
+ if (findingAnchors.has(finding.code)) continue;
12028
12647
  ctx.addIssue(
12029
12648
  "warning",
12030
12649
  "UNREALIZED_FINDING",
12031
- `Method "${method.name}" of contract "${impl.contract}" declares finding "${finding.code}", but "${file}" has no string literal or property-access name "${finding.code}" (analysis grade: ${lookup.facts.analysisGrade}) \u2014 report the finding under its declared code, or remove the declaration.`,
12650
+ `Method "${method.name}" of contract "${impl.contract}" declares finding "${finding.code}", but "${file}" has no string literal or property-access name "${finding.code}" (analysis grade: ${facts.analysisGrade}) \u2014 report the finding under its declared code, or remove the declaration.`,
12032
12651
  impl.id,
12033
12652
  draft
12034
12653
  );
@@ -12072,8 +12691,7 @@ var init_call_conformance = __esm({
12072
12691
  { code: "CALL_STEP_UNREALIZED", defaultSeverity: "warning", summary: "Narrative call step whose target method name (or symbol) never appears among the realized function's callees in the method's source file (exact grade, set membership)" }
12073
12692
  ],
12074
12693
  check(ctx) {
12075
- const factsByPath = /* @__PURE__ */ new Map();
12076
- for (const f of ctx.codeModel.files) factsByPath.set(pathKey(f.path), f);
12694
+ const code = ctx.codeIndex();
12077
12695
  for (const impl of ctx.implementations) {
12078
12696
  const contract = ctx.interfaceMap.get(impl.contract);
12079
12697
  if (!contract) continue;
@@ -12088,7 +12706,7 @@ var init_call_conformance = __esm({
12088
12706
  if (!implMethod.narrative.length) continue;
12089
12707
  const file = methodSourceFile(implMethod, impl.sourcePath);
12090
12708
  if (!file) continue;
12091
- const facts = factsByPath.get(pathKey(file));
12709
+ const facts = code.factsAt(file);
12092
12710
  if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
12093
12711
  const fnSymbol = implMethod.symbol ?? implMethod.name;
12094
12712
  const callees = closedCallees(facts, fnSymbol);
@@ -12142,6 +12760,7 @@ var init_hidden_state = __esm({
12142
12760
  { code: "HIDDEN_STATE", defaultSeverity: "warning", summary: "Module-scope mutable binding in a file mapped only to stateless logic (Orchestrators) \u2014 held state hiding outside a Store, Supervisor or Actor" }
12143
12761
  ],
12144
12762
  check(ctx) {
12763
+ const code = ctx.codeIndex();
12145
12764
  const byPath = /* @__PURE__ */ new Map();
12146
12765
  for (const impl of ctx.implementations) {
12147
12766
  const contract = ctx.interfaceMap.get(impl.contract);
@@ -12163,11 +12782,11 @@ var init_hidden_state = __esm({
12163
12782
  byPath.set(key, list);
12164
12783
  }
12165
12784
  }
12166
- for (const facts of ctx.codeModel.files) {
12167
- if (facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
12785
+ for (const path27 of code.exactPaths) {
12786
+ const facts = code.factsAt(path27);
12168
12787
  const bindings = facts.topLevelMutableBindings ?? [];
12169
12788
  if (bindings.length === 0) continue;
12170
- const mapped = byPath.get(pathKey(facts.path)) ?? [];
12789
+ const mapped = byPath.get(path27) ?? [];
12171
12790
  if (mapped.length === 0) continue;
12172
12791
  if (!mapped.every((m) => isStatelessLogic(m.component))) continue;
12173
12792
  const anchor = [...mapped].sort((a, b) => a.impl.id.localeCompare(b.impl.id))[0];
@@ -12199,54 +12818,21 @@ var init_dependency_conformance = __esm({
12199
12818
  { code: "UNREALIZED_DEPENDENCY", defaultSeverity: "warning", summary: "A declared dependsOn/owns edge between components in different files is realized by no import between any of their files" }
12200
12819
  ],
12201
12820
  check(ctx) {
12202
- const factsByPath = new Map(ctx.codeModel.files.map((f) => [pathKey(f.path), f]));
12203
- const nodes = /* @__PURE__ */ new Map();
12204
- const filesByComponent = /* @__PURE__ */ new Map();
12205
- const implsByComponent = /* @__PURE__ */ new Map();
12206
- for (const impl of ctx.implementations) {
12207
- const contract = ctx.interfaceMap.get(impl.contract);
12208
- if (!contract) continue;
12209
- const component = ctx.componentMap.get(contract.component);
12210
- if (!component) continue;
12211
- if (ctx.isInChainedSubproject(component.subsystem)) continue;
12212
- let mapped = false;
12213
- for (const file of implementationSourceFiles(impl)) {
12214
- const p = pathKey(file);
12215
- const facts = factsByPath.get(p);
12216
- if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
12217
- let node = nodes.get(p);
12218
- if (!node) {
12219
- node = { path: p, components: [], anchorImplId: impl.id, imports: facts.imports, reexports: facts.reexports, draft: false };
12220
- nodes.set(p, node);
12221
- }
12222
- if (!node.components.some((c) => c.id === component.id)) node.components.push(component);
12223
- node.draft = node.draft || ctx.isImplementationDraft(impl);
12224
- if (!filesByComponent.has(component.id)) filesByComponent.set(component.id, /* @__PURE__ */ new Set());
12225
- filesByComponent.get(component.id).add(p);
12226
- mapped = true;
12227
- }
12228
- if (mapped) {
12229
- if (!implsByComponent.has(component.id)) implsByComponent.set(component.id, []);
12230
- implsByComponent.get(component.id).push(impl);
12231
- }
12232
- }
12233
- const mappedPaths = new Set(nodes.keys());
12234
- const edges = /* @__PURE__ */ new Map();
12235
- const realizationEdges = /* @__PURE__ */ new Map();
12236
- for (const node of nodes.values()) {
12237
- const targets = /* @__PURE__ */ new Set();
12238
- for (const spec of node.imports) {
12239
- const resolved = resolveImport(node.path, spec, mappedPaths);
12240
- if (resolved && resolved !== node.path) targets.add(resolved);
12241
- }
12242
- edges.set(node.path, targets);
12243
- const realized = new Set(targets);
12244
- for (const spec of node.reexports) {
12245
- const resolved = resolveImport(node.path, spec, mappedPaths);
12246
- if (resolved && resolved !== node.path) realized.add(resolved);
12247
- }
12248
- realizationEdges.set(node.path, realized);
12249
- }
12821
+ const code = ctx.codeIndex();
12822
+ const realization = ctx.realizationIndex();
12823
+ const isExact = (p) => code.exactPaths.has(p);
12824
+ const mappedPaths = new Set(realization.paths.filter(isExact));
12825
+ const graph = ctx.importGraph(mappedPaths);
12826
+ const exactFiles = /* @__PURE__ */ new Map();
12827
+ const filesOf = (compId) => {
12828
+ let files = exactFiles.get(compId);
12829
+ if (!files) {
12830
+ files = realization.filesOf(compId).filter(isExact);
12831
+ exactFiles.set(compId, files);
12832
+ }
12833
+ return files;
12834
+ };
12835
+ const mappedImplsOf = (compId) => realization.implementationsOf(compId).filter((impl) => implementationSourceFiles(impl).some((f) => isExact(pathKey(f))));
12250
12836
  const dependsOrOwns = (from, toId) => from.dependsOn.includes(toId) || (from.owns ?? []).includes(toId);
12251
12837
  const ownerOf = /* @__PURE__ */ new Map();
12252
12838
  for (const c of ctx.components) {
@@ -12257,9 +12843,9 @@ var init_dependency_conformance = __esm({
12257
12843
  if (!published || published.size === 0) return false;
12258
12844
  return from.dependsOn.some((d) => published.has(d));
12259
12845
  };
12260
- const edgeJustified = (fromNode, toNode) => {
12261
- for (const cf of fromNode.components) {
12262
- for (const cg of toNode.components) {
12846
+ const edgeJustified = (from, to) => {
12847
+ for (const cf of from) {
12848
+ for (const cg of to) {
12263
12849
  if (cf.id === cg.id) return true;
12264
12850
  if (dependsOrOwns(cf, cg.id)) return true;
12265
12851
  if (cf.subsystem === cg.subsystem && dependsOrOwns(cg, cf.id) && (cg.componentType === "Portal" || cg.componentType === "Observer")) return true;
@@ -12273,60 +12859,35 @@ var init_dependency_conformance = __esm({
12273
12859
  }
12274
12860
  return false;
12275
12861
  };
12276
- for (const [fromPath, targets] of edges) {
12277
- const fromNode = nodes.get(fromPath);
12278
- for (const toPath of targets) {
12279
- const toNode = nodes.get(toPath);
12280
- if (edgeJustified(fromNode, toNode)) continue;
12862
+ const draftAt = (path27) => realization.implementationsAt(path27).some((impl) => ctx.isImplementationDraft(impl));
12863
+ for (const fromPath of mappedPaths) {
12864
+ const fromComponents = realization.componentsAt(fromPath);
12865
+ for (const toPath of graph.importsOf(fromPath)) {
12866
+ const toComponents = realization.componentsAt(toPath);
12867
+ if (edgeJustified(fromComponents, toComponents)) continue;
12281
12868
  ctx.addIssue(
12282
12869
  "warning",
12283
12870
  "UNDECLARED_DEPENDENCY",
12284
- `"${fromPath}" (realizing ${fromNode.components.map((c) => c.id).join(", ")}) imports "${toPath}" (realizing ${toNode.components.map((c) => c.id).join(", ")}) but no declared dependsOn/owns edge justifies it \u2014 declare the collaboration on the component that actually uses it, or route the cross-subsystem hop through the target's published surface.`,
12285
- fromNode.anchorImplId,
12286
- fromNode.draft || toNode.draft
12871
+ `"${fromPath}" (realizing ${fromComponents.map((c) => c.id).join(", ")}) imports "${toPath}" (realizing ${toComponents.map((c) => c.id).join(", ")}) but no declared dependsOn/owns edge justifies it \u2014 declare the collaboration on the component that actually uses it, or route the cross-subsystem hop through the target's published surface.`,
12872
+ realization.implementationsAt(fromPath)[0]?.id,
12873
+ draftAt(fromPath) || draftAt(toPath)
12287
12874
  );
12288
12875
  }
12289
12876
  }
12290
- const hasImportBetween = (fromFiles, toFiles, allowReverse) => {
12291
- for (const f of fromFiles) {
12292
- const targets = realizationEdges.get(f);
12293
- if (!targets) continue;
12294
- for (const t of toFiles) if (targets.has(t)) return true;
12295
- }
12296
- if (allowReverse) {
12297
- for (const t of toFiles) {
12298
- const reverse = realizationEdges.get(t);
12299
- if (!reverse) continue;
12300
- for (const f of fromFiles) if (reverse.has(f)) return true;
12301
- }
12302
- }
12303
- return false;
12304
- };
12305
12877
  for (const component of ctx.components) {
12306
- const fromFiles = filesByComponent.get(component.id);
12307
- if (!fromFiles) continue;
12878
+ const fromFiles = filesOf(component.id);
12879
+ if (fromFiles.length === 0) continue;
12308
12880
  const isMountingDeclarer = component.componentType === "Portal" || component.componentType === "Observer";
12309
12881
  const declaredTargets = [.../* @__PURE__ */ new Set([...component.dependsOn, ...component.owns ?? []])];
12310
12882
  for (const targetId of declaredTargets) {
12311
12883
  const target = ctx.componentMap.get(targetId);
12312
12884
  if (!target) continue;
12313
- if (component.subsystem !== target.subsystem) {
12314
- const subsystemFiles = /* @__PURE__ */ new Set();
12315
- for (const node of nodes.values()) {
12316
- if (node.components.some((c) => c.subsystem === target.subsystem)) subsystemFiles.add(node.path);
12317
- }
12318
- if (subsystemFiles.size === 0) continue;
12319
- for (const f of fromFiles) subsystemFiles.delete(f);
12320
- if (subsystemFiles.size === 0) continue;
12321
- if (hasImportBetween(fromFiles, subsystemFiles, isMountingDeclarer)) continue;
12322
- } else {
12323
- const toFiles = filesByComponent.get(targetId);
12324
- if (!toFiles) continue;
12325
- if ([...fromFiles].some((f) => toFiles.has(f))) continue;
12326
- if (hasImportBetween(fromFiles, toFiles, isMountingDeclarer)) continue;
12327
- }
12328
- const impls = implsByComponent.get(component.id) ?? [];
12329
- const anchor = impls[0];
12885
+ const crossSubsystem = component.subsystem !== target.subsystem;
12886
+ const toFiles = crossSubsystem ? realization.filesIn(target.subsystem).filter((p) => isExact(p) && !fromFiles.includes(p)) : filesOf(targetId);
12887
+ if (toFiles.length === 0) continue;
12888
+ if (fromFiles.some((f) => toFiles.includes(f))) continue;
12889
+ if (graph.connects(fromFiles, toFiles, isMountingDeclarer)) continue;
12890
+ const impls = mappedImplsOf(component.id);
12330
12891
  const declaredDependsOn = component.dependsOn.includes(targetId);
12331
12892
  const declaredOwns = (component.owns ?? []).includes(targetId);
12332
12893
  const relation = declaredDependsOn && declaredOwns ? "dependsOn and owns" : declaredDependsOn ? "dependsOn" : "owns";
@@ -12334,8 +12895,8 @@ var init_dependency_conformance = __esm({
12334
12895
  ctx.addIssue(
12335
12896
  "warning",
12336
12897
  "UNREALIZED_DEPENDENCY",
12337
- `Component "${component.id}" declares ${relation} "${targetId}", but no runtime import connects their source files (${[...fromFiles].join(", ")} \u219B ${target.subsystem !== component.subsystem ? `subsystem ${target.subsystem}` : [...filesByComponent.get(targetId) ?? []].join(", ")}) \u2014 either the collaboration is wired indirectly (DI) or the declared edge is stale.`,
12338
- anchor?.id ?? component.id,
12898
+ `Component "${component.id}" declares ${relation} "${targetId}", but no runtime import connects their source files (${fromFiles.join(", ")} \u219B ${crossSubsystem ? `subsystem ${target.subsystem}` : filesOf(targetId).join(", ")}) \u2014 either the collaboration is wired indirectly (DI) or the declared edge is stale.`,
12899
+ impls[0]?.id ?? component.id,
12339
12900
  draft
12340
12901
  );
12341
12902
  }
@@ -12361,50 +12922,24 @@ var init_integration_conformance = __esm({
12361
12922
  { code: "SIM_PATH_UNCOVERED", defaultSeverity: "warning", summary: "A narrative path has no sim:<component>.<method>[:<label>] anchor in the component's coverage-opted harness" }
12362
12923
  ],
12363
12924
  check(ctx) {
12364
- const factsByPath = new Map(ctx.codeModel.files.map((f) => [pathKey(f.path), f]));
12365
- const allPaths = new Set(factsByPath.keys());
12366
- const filesByComponent = /* @__PURE__ */ new Map();
12367
- const filesBySubsystem = /* @__PURE__ */ new Map();
12368
- const implsByComponent = /* @__PURE__ */ new Map();
12369
- const adopted = /* @__PURE__ */ new Set();
12925
+ const code = ctx.codeIndex();
12926
+ const realization = ctx.realizationIndex();
12927
+ const graph = ctx.importGraph();
12370
12928
  const exists = (p) => {
12371
- const status = factsByPath.get(p)?.status;
12929
+ const status = code.factsAt(p)?.status;
12372
12930
  return status === "analyzed" || status === "unreadable";
12373
12931
  };
12932
+ const modulesOf = (compId) => realization.filesOf(compId).filter(exists);
12933
+ const modulesIn = (subsystemId) => realization.filesIn(subsystemId).filter(exists);
12934
+ const adopted = /* @__PURE__ */ new Set();
12374
12935
  for (const impl of ctx.implementations) {
12936
+ if (!impl.simPath) continue;
12375
12937
  const contract = ctx.interfaceMap.get(impl.contract);
12376
12938
  const comp = contract ? ctx.componentMap.get(contract.component) : void 0;
12377
12939
  if (!comp || ctx.isInChainedSubproject(comp.subsystem)) continue;
12378
- if (!implsByComponent.has(comp.id)) implsByComponent.set(comp.id, []);
12379
- implsByComponent.get(comp.id).push(impl);
12380
- for (const file of implementationSourceFiles(impl)) {
12381
- const p = pathKey(file);
12382
- if (!exists(p)) continue;
12383
- if (!filesByComponent.has(comp.id)) filesByComponent.set(comp.id, /* @__PURE__ */ new Set());
12384
- filesByComponent.get(comp.id).add(p);
12385
- if (!filesBySubsystem.has(comp.subsystem)) filesBySubsystem.set(comp.subsystem, /* @__PURE__ */ new Set());
12386
- filesBySubsystem.get(comp.subsystem).add(p);
12387
- }
12388
- if (impl.simPath) adopted.add(comp.subsystem);
12389
- }
12390
- const isTechBoundary = (compId) => (implsByComponent.get(compId) ?? []).some((i) => (i.technologies ?? []).length > 0);
12391
- const reachFrom = (start) => {
12392
- const seen = /* @__PURE__ */ new Set([start]);
12393
- const stack = [start];
12394
- while (stack.length) {
12395
- const from = stack.pop();
12396
- const facts = factsByPath.get(from);
12397
- if (!facts || facts.status !== "analyzed") continue;
12398
- for (const spec of [...facts.imports, ...facts.reexports]) {
12399
- const to = resolveImport(from, spec, allPaths);
12400
- if (to && !seen.has(to)) {
12401
- seen.add(to);
12402
- stack.push(to);
12403
- }
12404
- }
12405
- }
12406
- return seen;
12407
- };
12940
+ adopted.add(comp.subsystem);
12941
+ }
12942
+ const isTechBoundary = (compId) => realization.implementationsOf(compId).some((i) => (i.technologies ?? []).length > 0);
12408
12943
  for (const impl of ctx.implementations) {
12409
12944
  const contract = ctx.interfaceMap.get(impl.contract);
12410
12945
  const comp = contract ? ctx.componentMap.get(contract.component) : void 0;
@@ -12424,7 +12959,7 @@ var init_integration_conformance = __esm({
12424
12959
  continue;
12425
12960
  }
12426
12961
  const simPath = pathKey(impl.simPath);
12427
- const facts = factsByPath.get(simPath);
12962
+ const facts = code.factsAt(simPath);
12428
12963
  const isDraftCtx = ctx.isImplementationDraft(impl);
12429
12964
  if (!facts || facts.status === "missing" || facts.status === "escaped" || facts.status === "unreadable") {
12430
12965
  const why = !facts || facts.status === "missing" ? "resolves to no file" : facts.status === "escaped" ? "escapes the project root \u2014 sims are project-relative, committed files" : "is not readable as source text";
@@ -12438,19 +12973,19 @@ var init_integration_conformance = __esm({
12438
12973
  continue;
12439
12974
  }
12440
12975
  if (facts.analysisGrade !== "exact") continue;
12441
- const reach = reachFrom(simPath);
12442
- const ownFiles = filesByComponent.get(comp.id) ?? /* @__PURE__ */ new Set();
12976
+ const reach = graph.reachFrom(simPath);
12977
+ const ownFiles = modulesOf(comp.id);
12443
12978
  const missing = [];
12444
- if (ownFiles.size > 0 && ![...ownFiles].some((f) => reach.has(f))) {
12445
- missing.push(`the component's own module (${[...ownFiles].join(" | ")})`);
12979
+ if (ownFiles.length > 0 && !ownFiles.some((f) => reach.has(f))) {
12980
+ missing.push(`the component's own module (${ownFiles.join(" | ")})`);
12446
12981
  }
12447
12982
  for (const dep of deps) {
12448
12983
  if (isTechBoundary(dep)) continue;
12449
12984
  const depComp = ctx.componentMap.get(dep);
12450
- const depFiles = depComp.subsystem !== comp.subsystem ? filesBySubsystem.get(depComp.subsystem) : filesByComponent.get(dep);
12451
- if (!depFiles || depFiles.size === 0) continue;
12452
- if (![...depFiles].some((f) => reach.has(f))) {
12453
- const label = depComp.subsystem !== comp.subsystem ? `"${dep}" (any module of subsystem "${depComp.subsystem}")` : `"${dep}" (${[...depFiles].join(" | ")})`;
12985
+ const depFiles = depComp.subsystem !== comp.subsystem ? modulesIn(depComp.subsystem) : modulesOf(dep);
12986
+ if (depFiles.length === 0) continue;
12987
+ if (!depFiles.some((f) => reach.has(f))) {
12988
+ const label = depComp.subsystem !== comp.subsystem ? `"${dep}" (any module of subsystem "${depComp.subsystem}")` : `"${dep}" (${depFiles.join(" | ")})`;
12454
12989
  missing.push(label);
12455
12990
  }
12456
12991
  }
@@ -13604,12 +14139,19 @@ var init_repository = __esm({
13604
14139
  init_hierarchy_integrity();
13605
14140
  init_namespace_hygiene();
13606
14141
  init_roundtrip_serialization();
13607
- init_type_references2();
14142
+ init_type_declarations();
14143
+ init_field_type_references();
14144
+ init_signature_type_references();
13608
14145
  init_public_surface();
13609
14146
  init_lint_allows();
13610
- init_contract_symmetry_and_narratives();
14147
+ init_contract_symmetry();
14148
+ init_narrative_target_references();
14149
+ init_cross_tree_references();
14150
+ init_surface_reference_backing();
13611
14151
  init_guarantee_tokens();
13612
- init_narrative_flow();
14152
+ init_narrative_step_config();
14153
+ init_narrative_reachability();
14154
+ init_narrative_jump_edges();
13613
14155
  init_narrative_antipatterns();
13614
14156
  init_narrative_detail();
13615
14157
  init_portal_fields();
@@ -13618,17 +14160,27 @@ var init_repository = __esm({
13618
14160
  init_retired_stereotypes();
13619
14161
  init_portal_endpoints();
13620
14162
  init_portal_call_auth();
13621
- init_stereotype_dependencies();
13622
- init_pattern_ownership();
14163
+ init_subsystem_boundary_dependencies();
14164
+ init_logic_dependency_class();
14165
+ init_data_block_dependencies();
14166
+ init_entrypoint_dependencies();
14167
+ init_portal_write_shortcut();
14168
+ init_pattern_membership();
14169
+ init_pattern_containment();
14170
+ init_unowned_blocks();
14171
+ init_member_visibility();
13623
14172
  init_facade_forwarding();
13624
- init_architectural_profiles();
14173
+ init_profile_registration();
14174
+ init_profile_stereotype_fencing();
14175
+ init_pack_profile_stereotypes();
13625
14176
  init_pattern_references();
13626
14177
  init_component_variants();
13627
14178
  init_declarative_assertions();
13628
14179
  init_pack_resolution();
13629
14180
  init_pack_reproducibility();
13630
14181
  init_dependency_cycles();
13631
- init_dispatch_tables();
14182
+ init_dispatch_table_bindings();
14183
+ init_dispatch_step_routing();
13632
14184
  init_lifecycle_entrypoints();
13633
14185
  init_unused_detection();
13634
14186
  init_durability_round_trip();
@@ -13636,7 +14188,9 @@ var init_repository = __esm({
13636
14188
  init_prose_claims();
13637
14189
  init_invariant_backing();
13638
14190
  init_event_topology();
13639
- init_structural_conformance();
14191
+ init_source_file_linkage();
14192
+ init_method_realization();
14193
+ init_finding_realization();
13640
14194
  init_call_conformance();
13641
14195
  init_hidden_state();
13642
14196
  init_dependency_conformance();
@@ -13655,12 +14209,30 @@ var init_repository = __esm({
13655
14209
  // explain many downstream findings, so surface them early in the list.
13656
14210
  namespaceHygieneRule,
13657
14211
  roundtripRule,
13658
- typeReferencesRule,
13659
- contractsRule,
14212
+ // The type vocabulary in three questions: what a type declares about
14213
+ // itself, then the identifiers its fields name, then the ones its
14214
+ // contracts' signatures name.
14215
+ typeDeclarationsRule,
14216
+ fieldTypeReferencesRule,
14217
+ signatureTypeReferencesRule,
14218
+ // Contracts and the targets narratives name, in four questions with one
14219
+ // owner each: does the implementation mirror its contract, does a target
14220
+ // inside this tree resolve, does a target that leaves it pin to exactly one
14221
+ // declared surface, and does that surface back what the step asks of it.
14222
+ contractSymmetryRule,
14223
+ narrativeTargetReferencesRule,
14224
+ crossTreeReferencesRule,
14225
+ surfaceReferenceBackingRule,
13660
14226
  // Vocabulary check right after contracts: an unknown token explains why the
13661
14227
  // consistency findings around it are absent, so surface them together.
13662
14228
  guaranteeTokensRule,
13663
- narrativeFlowRule,
14229
+ // Narrative control flow in three questions: does each step carry the
14230
+ // config its type requires, can every step be reached (and do the regions
14231
+ // nest), and where do the jump edges land. The last two run behind the
14232
+ // first's soundness verdict.
14233
+ narrativeStepConfigRule,
14234
+ narrativeReachabilityRule,
14235
+ narrativeJumpEdgesRule,
13664
14236
  // Antipatterns right after flow soundness: they analyze the same step
13665
14237
  // graphs and only make sense once the graphs are structurally valid.
13666
14238
  narrativeAntipatternsRule,
@@ -13673,11 +14245,32 @@ var init_repository = __esm({
13673
14245
  // Cross-call auth: a narrative call into an authed Portal must name its
13674
14246
  // credential source (rides with the portal family).
13675
14247
  portalCallAuthRule,
13676
- stereotypeDepsRule,
13677
- patternsRule,
14248
+ // Dependencies in five questions, one owner each: where an edge is
14249
+ // allowed to LAND (the boundary, and what an unresolved id means), then
14250
+ // the intra-subsystem matrix by the layer that answers for it — an
14251
+ // Orchestrator's declared class, the data blocks, the entry points and
14252
+ // the process layer — and finally the Portal read-face guard, the one
14253
+ // that reads narratives and dispatch tables rather than dependsOn.
14254
+ subsystemBoundaryDepsRule,
14255
+ logicDependencyClassRule,
14256
+ dataBlockDepsRule,
14257
+ entrypointDepsRule,
14258
+ portalWriteShortcutRule,
14259
+ // Patterns in four questions: who may own and what a claim must name,
14260
+ // what each pattern must contain, which data blocks are left standing
14261
+ // alone, and who may see a private member.
14262
+ patternMembershipRule,
14263
+ patternContainmentRule,
14264
+ unownedBlocksRule,
14265
+ memberVisibilityRule,
13678
14266
  // Facade shape rides with pattern ownership: same §7 doctrine, narrative side.
13679
14267
  facadeForwardingRule,
13680
- profilesRule,
14268
+ // Profiles in three questions, three owners: is the name real (the
14269
+ // project's config), does the built-in family doctrine allow this
14270
+ // stereotype (wairon), does the pack's declared doctrine allow it (the pack).
14271
+ profileRegistrationRule,
14272
+ profileStereotypeFencingRule,
14273
+ packProfileStereotypesRule,
13681
14274
  patternReferencesRule,
13682
14275
  variantReferencesRule,
13683
14276
  // Pack-instantiated declarative doctrine rides with the pack-reference
@@ -13688,7 +14281,8 @@ var init_repository = __esm({
13688
14281
  // Semantic-edge family: dispatch/lifecycle validity BEFORE reachability so a
13689
14282
  // reader sees the broken edge finding next to the unused-detection fallout
13690
14283
  // it explains.
13691
- dispatchRule,
14284
+ dispatchTableBindingsRule,
14285
+ dispatchStepRoutingRule,
13692
14286
  lifecycleRule,
13693
14287
  reachabilityRule,
13694
14288
  // The declaration (spec-scoped, refused at the write boundary) before the
@@ -13707,9 +14301,14 @@ var init_repository = __esm({
13707
14301
  // Pub/sub completeness: emitted topics need subscribers and vice versa.
13708
14302
  eventTopologyRule,
13709
14303
  // Code↔spec: structural conformance consumes the injected CodeModel (built
13710
- // by the source analysis adapter next to the surface snapshots); dependency
13711
- // conformance lifts its import edges onto the declared dependsOn/owns graph.
13712
- structuralConformanceRule,
14304
+ // by the source analysis adapter next to the surface snapshots) and asks it
14305
+ // three questions — does the spec name files that exist, does the file
14306
+ // contain the method, does it report the codes the method declares;
14307
+ // dependency conformance lifts its import edges onto the declared
14308
+ // dependsOn/owns graph.
14309
+ sourceFileLinkageRule,
14310
+ methodRealizationRule,
14311
+ findingRealizationRule,
13713
14312
  // Level 3 opener: narrative call steps must be realized as callees of the
13714
14313
  // realized function (set membership, exact grade).
13715
14314
  callConformanceRule,
@@ -14434,11 +15033,15 @@ function buildRuleContext(opts) {
14434
15033
  }
14435
15034
  return { kind: "unresolved" };
14436
15035
  };
15036
+ const contractMethods = /* @__PURE__ */ new Map();
14437
15037
  const interfaceMethodsOf = (compId) => {
15038
+ const cached = contractMethods.get(compId);
15039
+ if (cached) return cached;
14438
15040
  const out = [];
14439
15041
  for (const intf of interfacesByComponent.get(compId) ?? []) {
14440
15042
  out.push(...intf.methods);
14441
15043
  }
15044
+ contractMethods.set(compId, out);
14442
15045
  return out;
14443
15046
  };
14444
15047
  const profileDefFor = (subsystemId) => {
@@ -14534,7 +15137,26 @@ function buildRuleContext(opts) {
14534
15137
  ...surfaceResolved ? { surfaceResolved: true } : {}
14535
15138
  });
14536
15139
  };
14537
- return {
15140
+ let codeIndexMemo;
15141
+ const codeIndex = () => codeIndexMemo ??= buildCodeIndex(ctx.codeModel);
15142
+ let realizationMemo;
15143
+ const realizationIndex = () => realizationMemo ??= buildRealizationIndex(ctx);
15144
+ const importGraphs = /* @__PURE__ */ new Map();
15145
+ const importGraph = (paths) => {
15146
+ const universe = paths ?? codeIndex().paths;
15147
+ const key = `${universe.size}\0${[...universe].join("\0")}`;
15148
+ let graph = importGraphs.get(key);
15149
+ if (!graph) {
15150
+ graph = buildImportGraph(codeIndex(), universe);
15151
+ importGraphs.set(key, graph);
15152
+ }
15153
+ return graph;
15154
+ };
15155
+ let ownershipMemo;
15156
+ const ownershipIndex = () => ownershipMemo ??= buildOwnershipIndex(ctx);
15157
+ let dependencyEdgesMemo;
15158
+ const dependencyEdges = () => dependencyEdgesMemo ??= buildDependencyEdges(ctx);
15159
+ const ctx = {
14538
15160
  system,
14539
15161
  subsystems,
14540
15162
  components,
@@ -14563,6 +15185,11 @@ function buildRuleContext(opts) {
14563
15185
  isCollapsedCrossTreeRef,
14564
15186
  resolveSurfaceRef,
14565
15187
  interfaceMethodsOf,
15188
+ codeIndex,
15189
+ realizationIndex,
15190
+ importGraph,
15191
+ ownershipIndex,
15192
+ dependencyEdges,
14566
15193
  complexityConfigFor,
14567
15194
  documentationConfigFor,
14568
15195
  namingConfigFor,
@@ -14577,6 +15204,7 @@ function buildRuleContext(opts) {
14577
15204
  knownIssueCodes: opts.knownIssueCodes,
14578
15205
  addIssue
14579
15206
  };
15207
+ return ctx;
14580
15208
  }
14581
15209
  var DEPTH_RANK, DEPTH_GATED_CODES, COMPLETENESS_RULES;
14582
15210
  var init_rules = __esm({
@@ -14585,6 +15213,7 @@ var init_rules = __esm({
14585
15213
  init_models();
14586
15214
  init_extensions();
14587
15215
  init_types();
15216
+ init_read_model();
14588
15217
  init_repository();
14589
15218
  init_lint_allows();
14590
15219
  init_source_analysis();
@@ -24867,6 +25496,7 @@ __export(src_exports, {
24867
25496
  splitNamespace: () => splitNamespace,
24868
25497
  stateIdEquals: () => stateIdEquals,
24869
25498
  stateIdString: () => stateIdString,
25499
+ stepConfigVerdict: () => stepConfigVerdict,
24870
25500
  stepGraph: () => stepGraph,
24871
25501
  syncContextFiles: () => syncContextFiles,
24872
25502
  toMarkdown: () => toMarkdown,
@@ -25586,6 +26216,7 @@ function loadProjectConfig4() {
25586
26216
  splitNamespace,
25587
26217
  stateIdEquals,
25588
26218
  stateIdString,
26219
+ stepConfigVerdict,
25589
26220
  stepGraph,
25590
26221
  syncContextFiles,
25591
26222
  toMarkdown,