@wairon/cli 5.1.1-dev.15 → 5.1.1-dev.16

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
@@ -1092,7 +1092,7 @@ var init_defaults = __esm({
1092
1092
  copilot: ".github/prompts",
1093
1093
  codex: ".codex/agents"
1094
1094
  };
1095
- WAIRON_VERSION = "5.1.1-dev.15";
1095
+ WAIRON_VERSION = "5.1.1-dev.16";
1096
1096
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
1097
1097
  ARCHITECT_AGENT_ID = "agent-architect";
1098
1098
  ARCHITECT_TEMPLATE_ID = "architect";
@@ -2766,15 +2766,13 @@ var init_hierarchy_integrity = __esm({
2766
2766
  }
2767
2767
  }
2768
2768
  for (const impl of ctx.implementations) {
2769
- const contract = ctx.interfaces.find((i) => i.id === impl.contract);
2770
- const isDraftCtx = impl.status === "draft" || impl.status === "design" || contract && (ctx.isComponentDraft(contract.component) || contract.status === "draft" || contract.status === "design");
2771
2769
  if (!ctx.interfaceIds.has(impl.contract)) {
2772
2770
  ctx.addIssue(
2773
2771
  "error",
2774
2772
  "INVALID_INTERFACE_REFERENCE",
2775
2773
  `Implementation "${impl.id}" references non-existent interface contract "${impl.contract}".`,
2776
2774
  impl.id,
2777
- isDraftCtx
2775
+ ctx.isImplementationDraft(impl)
2778
2776
  );
2779
2777
  }
2780
2778
  }
@@ -2890,11 +2888,7 @@ var init_type_references2 = __esm({
2890
2888
  for (const field of t.fields) {
2891
2889
  const refs = fieldTypeRefs(t, field.type);
2892
2890
  for (const ref of refs) {
2893
- if (ctx.isBuiltinType(ref)) {
2894
- continue;
2895
- }
2896
- const resolved = ctx.types.find((spec) => typeMatchesRef(spec, ref));
2897
- if (!resolved) {
2891
+ if (!ctx.isTypeResolved(ref, /* @__PURE__ */ new Set())) {
2898
2892
  ctx.addIssue(
2899
2893
  "error",
2900
2894
  "UNDEFINED_TYPE_REFERENCE",
@@ -3126,14 +3120,27 @@ var init_contract_symmetry_and_narratives = __esm({
3126
3120
  );
3127
3121
  continue;
3128
3122
  }
3129
- const isCrossTreeForm = ctx.isExternalNamespaceRef(step.targetComponent);
3130
- const fromSubsystem = ctx.componentMap.get(contract.component)?.subsystem;
3131
- const isCollapsedForm = !isCrossTreeForm && fromSubsystem !== void 0 && ctx.isCollapsedCrossTreeRef(step.targetComponent, fromSubsystem);
3123
+ const target = step.targetComponent;
3124
+ const isCrossTreeForm = ctx.isExternalNamespaceRef(target);
3125
+ const caller = ctx.componentMap.get(contract.component);
3126
+ const fromSubsystem = caller?.subsystem;
3127
+ const isCollapsedForm = !isCrossTreeForm && fromSubsystem !== void 0 && ctx.isCollapsedCrossTreeRef(target, fromSubsystem);
3128
+ const reportUndeclared = (reaches, surfaceResolved) => {
3129
+ if (!caller || target === caller.id || caller.dependsOn.includes(target) || caller.owns.includes(target)) return;
3130
+ ctx.addIssue(
3131
+ "error",
3132
+ "UNDECLARED_DEPENDENCY_CALL",
3133
+ `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.`,
3134
+ impl.id,
3135
+ isDraftCtx || ctx.isComponentDraft(caller.id),
3136
+ surfaceResolved
3137
+ );
3138
+ };
3132
3139
  if (step.type === "dispatch") {
3133
- const dispatchTarget = ctx.componentMap.get(step.targetComponent);
3140
+ const dispatchTarget = ctx.componentMap.get(target);
3134
3141
  if (!dispatchTarget) {
3135
3142
  if (isCrossTreeForm || isCollapsedForm) {
3136
- const resolved = ctx.resolveSurfaceRef(step.targetComponent, fromSubsystem);
3143
+ const resolved = ctx.resolveSurfaceRef(target, fromSubsystem);
3137
3144
  if (resolved.kind === "ambiguous") {
3138
3145
  ctx.addIssue(
3139
3146
  "error",
@@ -3141,7 +3148,7 @@ var init_contract_symmetry_and_narratives = __esm({
3141
3148
  ambiguityMessage(
3142
3149
  resolved,
3143
3150
  `Method "${implMethod.name}" in implementation "${impl.id}" dispatches (step ${step.stepNumber}) through`,
3144
- step.targetComponent
3151
+ target
3145
3152
  ),
3146
3153
  impl.id,
3147
3154
  isDraftCtx
@@ -3149,11 +3156,12 @@ var init_contract_symmetry_and_narratives = __esm({
3149
3156
  continue;
3150
3157
  }
3151
3158
  if (resolved.kind === "resolved") {
3159
+ reportUndeclared("dispatches through", true);
3152
3160
  if (step.capability && !(resolved.entry.dispatch ?? []).some((b) => b.capability === step.capability)) {
3153
3161
  ctx.addIssue(
3154
3162
  "error",
3155
3163
  "SURFACE_REF_NOT_EXPOSED",
3156
- `Method "${implMethod.name}" in implementation "${impl.id}" dispatches capability "${step.capability}" through cross-tree portal "${step.targetComponent}" (step ${step.stepNumber}), but the surface snapshot of "${resolved.snapshot.projectName}" does not serve that capability on "${resolved.entry.id}".`,
3164
+ `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}".`,
3157
3165
  impl.id,
3158
3166
  isDraftCtx,
3159
3167
  true
@@ -3166,7 +3174,7 @@ var init_contract_symmetry_and_narratives = __esm({
3166
3174
  ctx.addIssue(
3167
3175
  "warning",
3168
3176
  "CROSS_TREE_REF_UNRESOLVED",
3169
- `Method "${implMethod.name}" in implementation "${impl.id}" dispatches through cross-tree component "${step.targetComponent}" (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.`,
3177
+ `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.`,
3170
3178
  impl.id,
3171
3179
  isDraftCtx
3172
3180
  );
@@ -3174,23 +3182,14 @@ var init_contract_symmetry_and_narratives = __esm({
3174
3182
  ctx.addIssue(
3175
3183
  "error",
3176
3184
  "INVALID_TARGET_COMPONENT_REFERENCE",
3177
- `Method "${implMethod.name}" in implementation "${impl.id}" dispatches through component "${step.targetComponent}" which does not exist (step ${step.stepNumber}).`,
3185
+ `Method "${implMethod.name}" in implementation "${impl.id}" dispatches through component "${target}" which does not exist (step ${step.stepNumber}).`,
3178
3186
  impl.id,
3179
3187
  isDraftCtx
3180
3188
  );
3181
3189
  }
3182
3190
  continue;
3183
3191
  }
3184
- const dispatchCaller = ctx.componentMap.get(contract.component);
3185
- if (dispatchCaller && step.targetComponent !== dispatchCaller.id && !dispatchCaller.dependsOn.includes(step.targetComponent) && !dispatchCaller.owns.includes(step.targetComponent)) {
3186
- ctx.addIssue(
3187
- "error",
3188
- "UNDECLARED_DEPENDENCY_CALL",
3189
- `Method "${implMethod.name}" in implementation "${impl.id}" (component "${dispatchCaller.id}") dispatches through component "${step.targetComponent}" (step ${step.stepNumber}) but component "${dispatchCaller.id}" does not list "${step.targetComponent}" as a dependency.`,
3190
- impl.id,
3191
- isDraftCtx || ctx.isComponentDraft(dispatchCaller.id)
3192
- );
3193
- }
3192
+ reportUndeclared("dispatches through");
3194
3193
  continue;
3195
3194
  }
3196
3195
  const verb = step.type === "register" ? "registers callback" : "calls";
@@ -3204,10 +3203,10 @@ var init_contract_symmetry_and_narratives = __esm({
3204
3203
  );
3205
3204
  continue;
3206
3205
  }
3207
- const targetComp = ctx.componentMap.get(step.targetComponent);
3206
+ const targetComp = ctx.componentMap.get(target);
3208
3207
  if (!targetComp) {
3209
3208
  if (isCrossTreeForm || isCollapsedForm) {
3210
- const resolved = ctx.resolveSurfaceRef(step.targetComponent, fromSubsystem);
3209
+ const resolved = ctx.resolveSurfaceRef(target, fromSubsystem);
3211
3210
  if (resolved.kind === "ambiguous") {
3212
3211
  ctx.addIssue(
3213
3212
  "error",
@@ -3215,7 +3214,7 @@ var init_contract_symmetry_and_narratives = __esm({
3215
3214
  ambiguityMessage(
3216
3215
  resolved,
3217
3216
  `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} "${step.targetMethod}" (step ${step.stepNumber}) on`,
3218
- step.targetComponent
3217
+ target
3219
3218
  ),
3220
3219
  impl.id,
3221
3220
  isDraftCtx
@@ -3223,12 +3222,13 @@ var init_contract_symmetry_and_narratives = __esm({
3223
3222
  continue;
3224
3223
  }
3225
3224
  if (resolved.kind === "resolved") {
3225
+ reportUndeclared(verb, true);
3226
3226
  const surfaceMethod = resolved.entry.methods.find((m) => m.name === step.targetMethod);
3227
3227
  if (!surfaceMethod) {
3228
3228
  ctx.addIssue(
3229
3229
  "error",
3230
3230
  "SURFACE_REF_NOT_EXPOSED",
3231
- `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} "${step.targetMethod}" on cross-tree component "${step.targetComponent}" (step ${step.stepNumber}), but the surface snapshot of "${resolved.snapshot.projectName}" does not expose that method on "${resolved.entry.id}".`,
3231
+ `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}".`,
3232
3232
  impl.id,
3233
3233
  isDraftCtx,
3234
3234
  true
@@ -3255,7 +3255,7 @@ var init_contract_symmetry_and_narratives = __esm({
3255
3255
  ctx.addIssue(
3256
3256
  "warning",
3257
3257
  "CROSS_TREE_REF_UNRESOLVED",
3258
- `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} cross-tree component "${step.targetComponent}" (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.`,
3258
+ `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.`,
3259
3259
  impl.id,
3260
3260
  isDraftCtx
3261
3261
  );
@@ -3263,26 +3263,15 @@ var init_contract_symmetry_and_narratives = __esm({
3263
3263
  ctx.addIssue(
3264
3264
  "error",
3265
3265
  "INVALID_TARGET_COMPONENT_REFERENCE",
3266
- `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} component "${step.targetComponent}" which does not exist (step ${step.stepNumber}).`,
3266
+ `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} component "${target}" which does not exist (step ${step.stepNumber}).`,
3267
3267
  impl.id,
3268
3268
  isDraftCtx
3269
3269
  );
3270
3270
  }
3271
3271
  continue;
3272
3272
  }
3273
- const callingComponent = ctx.componentMap.get(contract.component);
3274
- if (callingComponent && step.targetComponent !== callingComponent.id) {
3275
- if (!callingComponent.dependsOn.includes(step.targetComponent) && !callingComponent.owns.includes(step.targetComponent)) {
3276
- ctx.addIssue(
3277
- "error",
3278
- "UNDECLARED_DEPENDENCY_CALL",
3279
- `Method "${implMethod.name}" in implementation "${impl.id}" (component "${callingComponent.id}") ${verb} component "${step.targetComponent}" (step ${step.stepNumber}) but component "${callingComponent.id}" does not list "${step.targetComponent}" as a dependency.`,
3280
- impl.id,
3281
- isDraftCtx || ctx.isComponentDraft(callingComponent.id)
3282
- );
3283
- }
3284
- }
3285
- const targetInterfaces = ctx.interfacesByComponent.get(step.targetComponent) ?? [];
3273
+ reportUndeclared(verb);
3274
+ const targetInterfaces = ctx.interfacesByComponent.get(target) ?? [];
3286
3275
  let targetMethodSpec;
3287
3276
  for (const targetIntf of targetInterfaces) {
3288
3277
  const found = targetIntf.methods.find((m) => m.name === step.targetMethod);
@@ -3295,9 +3284,9 @@ var init_contract_symmetry_and_narratives = __esm({
3295
3284
  ctx.addIssue(
3296
3285
  "error",
3297
3286
  "INVALID_TARGET_METHOD_REFERENCE",
3298
- `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} method "${step.targetMethod}" on component "${step.targetComponent}" which is not defined on any of its interfaces (step ${step.stepNumber}).`,
3287
+ `Method "${implMethod.name}" in implementation "${impl.id}" ${verb} method "${step.targetMethod}" on component "${target}" which is not defined on any of its interfaces (step ${step.stepNumber}).`,
3299
3288
  impl.id,
3300
- isDraftCtx || ctx.isComponentDraft(step.targetComponent)
3289
+ isDraftCtx || ctx.isComponentDraft(target)
3301
3290
  );
3302
3291
  } else {
3303
3292
  const declared = new Set(targetMethodSpec.guarantees ?? []);
@@ -3307,9 +3296,9 @@ var init_contract_symmetry_and_narratives = __esm({
3307
3296
  ctx.addIssue(
3308
3297
  "warning",
3309
3298
  "NARRATIVE_SEMANTIC_UNBACKED",
3310
- `Step ${step.stepNumber} of "${implMethod.name}" in implementation "${impl.id}" explicitly asserts guarantee "${g}", but the method it calls \u2014 "${step.targetMethod}" on "${step.targetComponent}" \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.`,
3299
+ `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.`,
3311
3300
  impl.id,
3312
- isDraftCtx || ctx.isComponentDraft(step.targetComponent)
3301
+ isDraftCtx || ctx.isComponentDraft(target)
3313
3302
  );
3314
3303
  }
3315
3304
  }
@@ -3639,43 +3628,47 @@ var init_narrative_flow = __esm({
3639
3628
  });
3640
3629
 
3641
3630
  // src/core/rules/narrative/narrative-antipatterns.ts
3642
- function isTerminal(step, nextOf) {
3643
- if (step.type === "return" || step.type === "throw") return true;
3644
- const n = step.stepNumber;
3645
- switch (step.type) {
3646
- case "local":
3647
- case "call":
3648
- case "register":
3649
- case "dispatch":
3650
- return nextOf(n) === void 0;
3651
- case "branch":
3652
- return step.onTrueStep === void 0 && nextOf(n) === void 0;
3653
- case "switch":
3654
- return step.defaultStep === void 0 && nextOf(n) === void 0;
3655
- case "loop":
3656
- case "try":
3657
- return step.endStep !== void 0 && nextOf(step.endStep) === void 0;
3658
- default:
3659
- return false;
3660
- }
3631
+ function completedStepGraph(steps) {
3632
+ const numbers = steps.flatMap((s) => [
3633
+ s.stepNumber,
3634
+ s.onTrueStep,
3635
+ s.onFalseStep,
3636
+ s.defaultStep,
3637
+ s.endStep,
3638
+ s.finallyStep,
3639
+ s.toStep,
3640
+ ...(s.cases ?? []).map((c) => c.step),
3641
+ ...(s.catches ?? []).map((c) => c.step),
3642
+ ...(s.branches ?? []).map((b) => b.step)
3643
+ ]).filter((n) => n !== void 0);
3644
+ const completion = {
3645
+ stepNumber: Math.max(0, ...numbers) + 1,
3646
+ description: "The method completes by falling off the end of its narrative",
3647
+ type: "return"
3648
+ };
3649
+ return stepGraph({ narrative: [...steps, completion] });
3661
3650
  }
3662
3651
  function isUnavoidable(steps, target) {
3663
- const { stepNumbers: nums, stepAt, nextOf, successorsOf } = stepGraph({ narrative: steps });
3664
- if (nums.length === 0) return false;
3665
- if (nums[0] === target) return true;
3666
- const visited = /* @__PURE__ */ new Set();
3667
- const stack = [nums[0]];
3668
- while (stack.length) {
3669
- const n = stack.pop();
3670
- if (n === target || visited.has(n)) continue;
3671
- visited.add(n);
3672
- const s = stepAt(n);
3673
- if (isTerminal(s, nextOf)) return false;
3674
- for (const t of successorsOf(n)) {
3675
- if (t !== target && !visited.has(t)) stack.push(t);
3652
+ if (steps.length === 0) return false;
3653
+ const graph = completedStepGraph(steps);
3654
+ const completes = /* @__PURE__ */ new Set();
3655
+ const canComplete = (n) => {
3656
+ const s = graph.stepAt(n);
3657
+ if (s.type === "return" || s.type === "throw") return true;
3658
+ const arms = s.type === "parallel" ? (s.branches ?? []).map((b) => b.step).filter((e) => graph.stepAt(e) !== void 0) : [];
3659
+ if (arms.length) return arms.every((e) => completes.has(e));
3660
+ return graph.successorsOf(n).some((t) => completes.has(t));
3661
+ };
3662
+ const order = [...graph.stepNumbers].reverse();
3663
+ for (let grew = true; grew; ) {
3664
+ grew = false;
3665
+ for (const n of order) {
3666
+ if (n === target || completes.has(n) || !canComplete(n)) continue;
3667
+ completes.add(n);
3668
+ grew = true;
3676
3669
  }
3677
3670
  }
3678
- return true;
3671
+ return !completes.has(graph.stepNumbers[0]);
3679
3672
  }
3680
3673
  function stronglyConnected(nums, successorsOf) {
3681
3674
  const index = /* @__PURE__ */ new Map();
@@ -3727,11 +3720,12 @@ function stronglyConnected(nums, successorsOf) {
3727
3720
  }
3728
3721
  return sccs;
3729
3722
  }
3730
- var narrativeAntipatternsRule;
3723
+ var byCodeUnit, narrativeAntipatternsRule;
3731
3724
  var init_narrative_antipatterns = __esm({
3732
3725
  "src/core/rules/narrative/narrative-antipatterns.ts"() {
3733
3726
  "use strict";
3734
3727
  init_models();
3728
+ byCodeUnit = (a, b) => a < b ? -1 : a > b ? 1 : 0;
3735
3729
  narrativeAntipatternsRule = {
3736
3730
  name: "narrative-antipatterns",
3737
3731
  description: "Provable narrative bugs, caught at spec level before implementation: a step-graph cycle with no exit edge and no return/throw member never terminates by construction (INESCAPABLE_CYCLE); a branch or switch whose arms all target the same step decides nothing (MEANINGLESS_BRANCH); a method-level call cycle across components in which every call edge is unavoidable on every entry-to-exit path is unbounded recursion (UNCONDITIONAL_CALL_CYCLE \u2014 dispatch steps resolved through their portal tables; a cycle with even one guarded edge is NOT flagged, since prose conditions are never judged). Structure-only claims \u2014 warnings, lint.allow-suppressible.",
@@ -3750,29 +3744,33 @@ var init_narrative_antipatterns = __esm({
3750
3744
  const steps = implMethod.narrative;
3751
3745
  if (!steps.length) continue;
3752
3746
  const where = `Method "${implMethod.name}" in implementation "${impl.id}": `;
3753
- const graph = stepGraph(implMethod);
3747
+ const graph = completedStepGraph(steps);
3748
+ const decisionTargets = (stepNumber, explicit, fallThroughField) => {
3749
+ const targets = new Set(explicit);
3750
+ if (fallThroughField !== void 0) targets.add(fallThroughField);
3751
+ else for (const t of graph.successorsOf(stepNumber)) targets.add(t);
3752
+ return [...targets];
3753
+ };
3754
3754
  for (const s of steps) {
3755
3755
  if (s.type === "branch" && s.onFalseStep !== void 0) {
3756
- const onTrue = s.onTrueStep ?? graph.nextOf(s.stepNumber);
3757
- if (onTrue !== void 0 && onTrue === s.onFalseStep) {
3756
+ const targets = decisionTargets(s.stepNumber, [s.onFalseStep], s.onTrueStep);
3757
+ if (targets.length === 1) {
3758
3758
  ctx.addIssue(
3759
3759
  "warning",
3760
3760
  "MEANINGLESS_BRANCH",
3761
- `${where}branch step ${s.stepNumber} sends both arms to step ${onTrue} \u2014 the condition decides nothing. Point the arms at different steps, or replace the branch with a local step.`,
3761
+ `${where}branch step ${s.stepNumber} sends both arms to step ${targets[0]} \u2014 the condition decides nothing. Point the arms at different steps, or replace the branch with a local step.`,
3762
3762
  impl.id,
3763
3763
  isDraftCtx
3764
3764
  );
3765
3765
  }
3766
3766
  }
3767
3767
  if (s.type === "switch" && s.cases?.length) {
3768
- const targets = new Set(s.cases.map((c) => c.step));
3769
- const def = s.defaultStep ?? graph.nextOf(s.stepNumber);
3770
- if (def !== void 0) targets.add(def);
3771
- if (targets.size === 1) {
3768
+ const targets = decisionTargets(s.stepNumber, s.cases.map((c) => c.step), s.defaultStep);
3769
+ if (targets.length === 1) {
3772
3770
  ctx.addIssue(
3773
3771
  "warning",
3774
3772
  "MEANINGLESS_BRANCH",
3775
- `${where}switch step ${s.stepNumber} sends every case (and the default) to step ${[...targets][0]} \u2014 the dispatch decides nothing.`,
3773
+ `${where}switch step ${s.stepNumber} sends every case (and the default) to step ${targets[0]} \u2014 the dispatch decides nothing.`,
3776
3774
  impl.id,
3777
3775
  isDraftCtx
3778
3776
  );
@@ -3845,8 +3843,8 @@ var init_narrative_antipatterns = __esm({
3845
3843
  if (!isCycle) continue;
3846
3844
  const memberEdges = keys.flatMap((k) => (adjacency.get(k) ?? []).filter((e) => inScc.has(e.toKey)));
3847
3845
  if (memberEdges.length === 0) continue;
3848
- const anchor = [...memberEdges].sort((a, b) => a.fromKey.localeCompare(b.fromKey))[0];
3849
- const path27 = [...keys].sort().join(" \u2192 ");
3846
+ const anchor = [...memberEdges].sort((a, b) => byCodeUnit(a.fromKey, b.fromKey) || byCodeUnit(a.impl.id, b.impl.id) || a.stepNumber - b.stepNumber)[0];
3847
+ const path27 = [...keys].sort(byCodeUnit).join(" \u2192 ");
3850
3848
  ctx.addIssue(
3851
3849
  "warning",
3852
3850
  "UNCONDITIONAL_CALL_CYCLE",
@@ -4098,14 +4096,15 @@ var init_portal_endpoints = __esm({
4098
4096
  continue;
4099
4097
  }
4100
4098
  for (const intf of compInterfaces) {
4099
+ const isIntfDraft = intf.status === "draft" || intf.status === "design";
4101
4100
  for (const m of intf.methods) {
4102
4101
  if (m.endpoint) {
4103
4102
  ctx.addIssue(
4104
4103
  "error",
4105
4104
  "ARCHITECTURE_VIOLATION_NON_PORTAL_ENDPOINT",
4106
4105
  `Architectural violation: Component "${comp.id}" is a ${comp.componentType}, but method "${m.name}" on its interface "${intf.id}" declares an endpoint. Only Portal components may carry endpoints.`,
4107
- comp.id,
4108
- isDraftCtx
4106
+ intf.id,
4107
+ isDraftCtx || isIntfDraft
4109
4108
  );
4110
4109
  }
4111
4110
  }
@@ -4236,7 +4235,7 @@ var init_stereotype_dependencies = __esm({
4236
4235
  { code: "ARCHITECTURE_VIOLATION_ADAPTER_DEP", defaultSeverity: "error", summary: "Adapter depending on Orchestrators or Stores" },
4237
4236
  { code: "ARCHITECTURE_VIOLATION_INDEX_DEP", defaultSeverity: "error", summary: "Index depending on anything but its Store or an Adapter" },
4238
4237
  { code: "ARCHITECTURE_VIOLATION_VIEW_DEP", defaultSeverity: "error", summary: "View depending on logic/persistence layers" },
4239
- { code: "PORTAL_WRITE_SHORTCUT", defaultSeverity: "error", summary: "Portal narrative calls 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)" }
4238
+ { 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)" }
4240
4239
  ],
4241
4240
  check(ctx) {
4242
4241
  for (const comp of ctx.components) {
@@ -4308,10 +4307,11 @@ var init_stereotype_dependencies = __esm({
4308
4307
  crossDraft
4309
4308
  );
4310
4309
  } else if (depComp.componentType !== "Portal" && depComp.componentType !== "Gateway") {
4310
+ const crosser = comp.componentType === "Adapter" ? "client Adapter" : comp.componentType;
4311
4311
  ctx.addIssue(
4312
4312
  "error",
4313
4313
  "CROSS_SUBSYSTEM_TARGET_NON_PORTAL",
4314
- `Boundary violation: client Adapter "${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 this Adapter at it.`,
4314
+ `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.`,
4315
4315
  comp.id,
4316
4316
  crossDraft
4317
4317
  );
@@ -4435,6 +4435,23 @@ var init_stereotype_dependencies = __esm({
4435
4435
  }
4436
4436
  }
4437
4437
  }
4438
+ for (const comp of ctx.components) {
4439
+ if (comp.componentType !== "Portal" || !comp.dispatch || comp.dispatch.length === 0) continue;
4440
+ const isDraftCtx = ctx.isComponentDraft(comp.id);
4441
+ for (const binding of comp.dispatch) {
4442
+ const target = ctx.componentMap.get(binding.component);
4443
+ if (!target || target.componentType !== "Repository" && target.componentType !== "Index") continue;
4444
+ const targetMethod = ctx.interfaceMethodsOf(target.id).find((m) => m.name === binding.method);
4445
+ if (targetMethod?.effect !== "write") continue;
4446
+ ctx.addIssue(
4447
+ "error",
4448
+ "PORTAL_WRITE_SHORTCUT",
4449
+ `Portal "${comp.id}": dispatch binding "${binding.capability}" routes to write-effect method ${target.id}.${binding.method} directly. The Portal\u2192${target.componentType} shortcut is licensed for READS only \u2014 route the write through an Orchestrator that owns the workflow.`,
4450
+ comp.id,
4451
+ isDraftCtx || ctx.isComponentDraft(target.id)
4452
+ );
4453
+ }
4454
+ }
4438
4455
  }
4439
4456
  };
4440
4457
  }
@@ -4458,7 +4475,7 @@ var init_pattern_ownership = __esm({
4458
4475
  { code: "REPOSITORY_CONTAINMENT", defaultSeverity: "error", summary: "Repository owning a non Store/Registry/Index/Adapter member" },
4459
4476
  { code: "GATEWAY_CONTAINMENT", defaultSeverity: "error", summary: "Gateway owning a non Portal/Orchestrator/Specialist member" },
4460
4477
  { code: "FEATURE_COMPONENT_CONTAINMENT", defaultSeverity: "error", summary: "FeatureComponent not owning exactly one Orchestrator + one or more Views" },
4461
- { code: "ROUTER_COMPONENT_CONTAINMENT", defaultSeverity: "error", summary: "RouterComponent missing its Portal facade or children" },
4478
+ { code: "ROUTER_COMPONENT_CONTAINMENT", defaultSeverity: "error", summary: "RouterComponent not owning exactly one Portal facade, or owning no children to route to" },
4462
4479
  { code: "VISIBILITY_VIOLATION", defaultSeverity: "error", summary: "Dependency on a block privately owned by another pattern" },
4463
4480
  { 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" },
4464
4481
  { 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" }
@@ -4480,14 +4497,15 @@ var init_pattern_ownership = __esm({
4480
4497
  ctx.addIssue("error", "INVALID_OWNED_MEMBER", `Component "${comp.id}" owns "${memberId}" which does not exist.`, comp.id, isDraftCtx);
4481
4498
  continue;
4482
4499
  }
4500
+ if (!pattern) continue;
4483
4501
  if (isPattern(member)) {
4484
4502
  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);
4485
4503
  }
4486
- const prev = ownedBy.get(memberId);
4487
- if (prev && prev !== comp.id) {
4488
- ctx.addIssue("error", "SHARED_OWNED_MEMBER", `Block "${memberId}" is owned by both "${prev}" and "${comp.id}"; a block has exactly one owner.`, comp.id, isDraftCtx);
4504
+ const firstOwner = ownedBy.get(memberId);
4505
+ if (firstOwner && firstOwner !== comp.id) {
4506
+ 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);
4489
4507
  }
4490
- ownedBy.set(memberId, comp.id);
4508
+ if (!firstOwner) ownedBy.set(memberId, comp.id);
4491
4509
  }
4492
4510
  if (comp.componentType === "Repository") {
4493
4511
  const allowed = /* @__PURE__ */ new Set(["Store", "Registry", "Index", "Adapter"]);
@@ -4522,14 +4540,14 @@ var init_pattern_ownership = __esm({
4522
4540
  }
4523
4541
  }
4524
4542
  if (comp.componentType === "RouterComponent") {
4525
- let hasPortal = false;
4543
+ let portals = 0;
4526
4544
  let hasChildren = false;
4527
4545
  for (const memberId of comp.owns) {
4528
4546
  const t = ctx.componentMap.get(memberId)?.componentType;
4529
- if (t === "Portal") hasPortal = true;
4547
+ if (t === "Portal") portals++;
4530
4548
  else if (t) hasChildren = true;
4531
4549
  }
4532
- if (!hasPortal) {
4550
+ if (portals !== 1) {
4533
4551
  ctx.addIssue("error", "ROUTER_COMPONENT_CONTAINMENT", `RouterComponent "${comp.id}" must own exactly one Portal component to act as its facade.`, comp.id, isDraftCtx);
4534
4552
  }
4535
4553
  if (!hasChildren) {
@@ -5031,54 +5049,43 @@ var init_dependency_cycles = __esm({
5031
5049
  "use strict";
5032
5050
  cyclesRule = {
5033
5051
  name: "dependency-cycles",
5034
- description: "The component dependsOn graph must be a DAG; the first detected cycle is reported with its full path.",
5052
+ description: "The component dependsOn graph must be a DAG; the first cycle through a component in the run's scope is reported with its full path.",
5035
5053
  codes: [
5036
5054
  { code: "CIRCULAR_DEPENDENCY", defaultSeverity: "error", summary: "Circular dependency between components" }
5037
5055
  ],
5038
5056
  check(ctx) {
5039
- const visited = /* @__PURE__ */ new Set();
5040
- const recStack = /* @__PURE__ */ new Set();
5041
- const dfs = (compId, pathTrace) => {
5042
- visited.add(compId);
5043
- recStack.add(compId);
5044
- pathTrace.push(compId);
5045
- const comp = ctx.componentMap.get(compId);
5046
- if (comp) {
5047
- for (const depId of comp.dependsOn) {
5048
- if (!visited.has(depId)) {
5049
- if (dfs(depId, pathTrace)) {
5050
- recStack.delete(compId);
5051
- pathTrace.pop();
5052
- return true;
5053
- }
5054
- } else if (recStack.has(depId)) {
5057
+ const cycleFrom = (startId) => {
5058
+ const visited = /* @__PURE__ */ new Set([startId]);
5059
+ const pathTrace = [startId];
5060
+ const search = (compId) => {
5061
+ for (const depId of ctx.componentMap.get(compId)?.dependsOn ?? []) {
5062
+ if (depId === startId) {
5055
5063
  pathTrace.push(depId);
5056
- const cyclePath = pathTrace.slice(pathTrace.indexOf(depId)).join(" -> ");
5057
- const isDraftCtx = ctx.isComponentDraft(compId) || ctx.isComponentDraft(depId);
5058
- ctx.addIssue(
5059
- "error",
5060
- "CIRCULAR_DEPENDENCY",
5061
- `Circular dependency detected: ${cyclePath}`,
5062
- compId,
5063
- isDraftCtx
5064
- );
5065
- pathTrace.pop();
5066
- recStack.delete(compId);
5067
- pathTrace.pop();
5068
5064
  return true;
5069
5065
  }
5066
+ if (visited.has(depId)) continue;
5067
+ visited.add(depId);
5068
+ pathTrace.push(depId);
5069
+ if (search(depId)) return true;
5070
+ pathTrace.pop();
5070
5071
  }
5071
- }
5072
- recStack.delete(compId);
5073
- pathTrace.pop();
5074
- return false;
5072
+ return false;
5073
+ };
5074
+ return search(startId) ? pathTrace : void 0;
5075
5075
  };
5076
5076
  for (const comp of ctx.components) {
5077
- if (!visited.has(comp.id)) {
5078
- if (dfs(comp.id, [])) {
5079
- break;
5080
- }
5081
- }
5077
+ if (!ctx.isSpecInScope(comp.id)) continue;
5078
+ const cycle = cycleFrom(comp.id);
5079
+ if (!cycle) continue;
5080
+ const closing = cycle[cycle.length - 2];
5081
+ ctx.addIssue(
5082
+ "error",
5083
+ "CIRCULAR_DEPENDENCY",
5084
+ `Circular dependency detected: ${cycle.join(" -> ")}`,
5085
+ comp.id,
5086
+ ctx.isComponentDraft(comp.id) || ctx.isComponentDraft(closing)
5087
+ );
5088
+ return;
5082
5089
  }
5083
5090
  }
5084
5091
  };
@@ -5207,19 +5214,18 @@ var init_dispatch_tables = __esm({
5207
5214
  );
5208
5215
  continue;
5209
5216
  }
5210
- if (step.assertsGuarantees?.length) {
5211
- const boundMethod = ctx.interfaceMethodsOf(binding.component).find((m) => m.name === binding.method);
5212
- const declared = new Set(boundMethod?.guarantees ?? []);
5213
- for (const g of step.assertsGuarantees) {
5214
- if (!declared.has(g)) {
5215
- ctx.addIssue(
5216
- "warning",
5217
- "NARRATIVE_SEMANTIC_UNBACKED",
5218
- `${where} asserts guarantee "${g}", but capability "${step.capability}" resolves to "${binding.component}.${binding.method}", which does not list "${g}" among its L3 contract guarantees. Declare it there (and ensure its shape can deliver it), or revise the narrative.`,
5219
- impl.id,
5220
- isDraftCtx || ctx.isComponentDraft(binding.component)
5221
- );
5222
- }
5217
+ const boundMethod = ctx.interfaceMethodsOf(binding.component).find((m) => m.name === binding.method);
5218
+ if (!boundMethod) continue;
5219
+ const declared = new Set(boundMethod.guarantees ?? []);
5220
+ for (const g of step.assertsGuarantees ?? []) {
5221
+ if (!declared.has(g)) {
5222
+ ctx.addIssue(
5223
+ "warning",
5224
+ "NARRATIVE_SEMANTIC_UNBACKED",
5225
+ `${where} asserts guarantee "${g}", but capability "${step.capability}" resolves to "${binding.component}.${binding.method}", which does not list "${g}" among its L3 contract guarantees. Declare it there (and ensure its shape can deliver it), or revise the narrative.`,
5226
+ impl.id,
5227
+ isDraftCtx || ctx.isComponentDraft(binding.component)
5228
+ );
5223
5229
  }
5224
5230
  }
5225
5231
  }
@@ -5424,12 +5430,11 @@ var init_unused_detection = __esm({
5424
5430
  const invokedBySeeds = [];
5425
5431
  for (const intf of ctx.interfaces) {
5426
5432
  if (!ctx.componentMap.has(intf.component)) continue;
5427
- const comp = ctx.componentMap.get(intf.component);
5428
5433
  for (const m of intf.methods) {
5429
5434
  if (!m.invokedBy) continue;
5430
5435
  invokedBySeeds.push({ compId: intf.component, methodName: m.name });
5431
5436
  if (!ctx.isSpecInScope(intf.id)) continue;
5432
- const isDraftCtx = comp.status === "draft" || comp.status === "design" || intf.status === "draft" || intf.status === "design";
5437
+ const isDraftCtx = ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design";
5433
5438
  if (!passesIntentFloor(m.invokedBy.caller, m.name)) {
5434
5439
  ctx.addIssue(
5435
5440
  "warning",
@@ -5454,7 +5459,7 @@ var init_unused_detection = __esm({
5454
5459
  for (const comp of ctx.components) {
5455
5460
  if (!ctx.isSpecInScope(comp.id)) continue;
5456
5461
  if (!reach.reachesComponent(comp.id)) {
5457
- const isDraftCtx = comp.status === "draft" || comp.status === "design";
5462
+ const isDraftCtx = ctx.isComponentDraft(comp.id);
5458
5463
  ctx.addIssue(
5459
5464
  "warning",
5460
5465
  "UNUSED_COMPONENT",
@@ -5466,12 +5471,12 @@ var init_unused_detection = __esm({
5466
5471
  for (const intf of ctx.interfacesByComponent.get(comp.id) ?? []) {
5467
5472
  for (const m of intf.methods) {
5468
5473
  if (!reach.reachesMethod(comp.id, m.name)) {
5469
- const isDraftCtx = comp.status === "draft" || comp.status === "design" || intf.status === "draft" || intf.status === "design";
5474
+ const isDraftCtx = ctx.isComponentDraft(comp.id) || intf.status === "draft" || intf.status === "design";
5470
5475
  ctx.addIssue(
5471
5476
  "warning",
5472
5477
  "UNUSED_METHOD",
5473
5478
  `Method "${m.name}" on component "${comp.id}" is defined but never called by any narrative step.`,
5474
- comp.id,
5479
+ intf.id,
5475
5480
  isDraftCtx
5476
5481
  );
5477
5482
  }
@@ -5582,20 +5587,20 @@ var init_durability_round_trip = __esm({
5582
5587
  });
5583
5588
 
5584
5589
  // src/core/rules/wiring/untyped-seams.ts
5585
- function unwrapPromise(typeRef) {
5586
- const m = /^promise\s*<(.+)>$/i.exec(typeRef.trim());
5587
- return (m ? m[1] : typeRef).trim();
5590
+ function namesBareType(refs) {
5591
+ return refs.some((ref) => BARE_SEAM_TYPES.has(ref.toLowerCase()));
5588
5592
  }
5589
5593
  var BARE_SEAM_TYPES, untypedSeamRule;
5590
5594
  var init_untyped_seams = __esm({
5591
5595
  "src/core/rules/wiring/untyped-seams.ts"() {
5592
5596
  "use strict";
5597
+ init_models();
5593
5598
  BARE_SEAM_TYPES = /* @__PURE__ */ new Set(["json", "any", "unknown", "object"]);
5594
5599
  untypedSeamRule = {
5595
5600
  name: "untyped-seams",
5596
- description: "Methods on a subsystem's published components (its public surface) should not take or return bare Json/any/unknown \u2014 cross-subsystem contracts are the swap seam and must be typed. Generic-dispatch portals carry per-capability types via their dispatch table instead.",
5601
+ description: "Methods on a subsystem's published components (its public surface) should not take or return bare Json/any/unknown/object, judged through each method's type references so a prose signature is judged like structured params: cross-subsystem contracts are the swap seam and must be typed. Generic-dispatch portals carry per-capability types via their dispatch table instead.",
5597
5602
  codes: [
5598
- { code: "UNTYPED_SEAM", defaultSeverity: "warning", summary: "Bare Json/any/unknown parameter or return crossing a subsystem public surface" }
5603
+ { code: "UNTYPED_SEAM", defaultSeverity: "warning", summary: "Bare Json/any/unknown/object parameter or return crossing a subsystem public surface" }
5599
5604
  ],
5600
5605
  check(ctx) {
5601
5606
  for (const sub of ctx.subsystems) {
@@ -5609,14 +5614,17 @@ var init_untyped_seams = __esm({
5609
5614
  const isDraftCtx = ctx.isComponentDraft(compId) || intf.status === "draft" || intf.status === "design";
5610
5615
  for (const m of intf.methods) {
5611
5616
  const offenders = [];
5612
- for (const p of m.params ?? []) {
5613
- if (BARE_SEAM_TYPES.has(unwrapPromise(p.type).toLowerCase())) {
5614
- offenders.push(`param "${p.name}: ${p.type}"`);
5617
+ if (m.params && m.params.length > 0) {
5618
+ for (const p of m.params) {
5619
+ if (namesBareType(extractTypeIdentifiers(p.type))) {
5620
+ offenders.push(`param "${p.name}: ${p.type}"`);
5621
+ }
5615
5622
  }
5616
- }
5617
- const ret = unwrapPromise(m.returns ?? "");
5618
- if (BARE_SEAM_TYPES.has(ret.toLowerCase())) {
5619
- offenders.push(`return "${m.returns}"`);
5623
+ if (namesBareType(extractTypeIdentifiers(m.returns ?? ""))) {
5624
+ offenders.push(`return "${m.returns}"`);
5625
+ }
5626
+ } else if (namesBareType(methodTypeRefs(m))) {
5627
+ offenders.push(`signature "${m.signature}"`);
5620
5628
  }
5621
5629
  if (offenders.length) {
5622
5630
  ctx.addIssue(
@@ -5641,15 +5649,19 @@ function componentOfImpl(ctx, implContract) {
5641
5649
  const contract = ctx.interfaceMap.get(implContract);
5642
5650
  return contract ? ctx.componentMap.get(contract.component) : void 0;
5643
5651
  }
5644
- var DATA_STEREOTYPES, CLAIM_PHRASES, proseClaimRule;
5652
+ function withoutQuotedText(text) {
5653
+ return text.replace(QUOTED_SPAN, " ");
5654
+ }
5655
+ var DATA_STEREOTYPES, CLAIM_PHRASES, QUOTED_SPAN, proseClaimRule;
5645
5656
  var init_prose_claims = __esm({
5646
5657
  "src/core/rules/wiring/prose-claims.ts"() {
5647
5658
  "use strict";
5648
5659
  DATA_STEREOTYPES = /* @__PURE__ */ new Set(["Store", "Registry", "Index", "Adapter", "Repository"]);
5649
- CLAIM_PHRASES = /\b(persist(s|ed|ent)?|survives?\s+(a\s+)?restart|writ(es?|ten)\s+to\s+disk|registered\s+into|durabl[ey])\b/i;
5660
+ CLAIM_PHRASES = /\b(persist(s|ed|ent|ing|ence)?|survives?\s+(a\s+)?restart|writ(es?|ten)\s+to\s+disk|registered\s+into|durabl[ey])\b/i;
5661
+ QUOTED_SPAN = /"[^"]*"|“[^”]*”|`[^`]*`|(?<![\p{L}\p{N}])['‘](?:[^'‘’]|(?<=[\p{L}\p{N}])['’](?=[\p{L}\p{N}]))*['’](?![\p{L}\p{N}])/gu;
5650
5662
  proseClaimRule = {
5651
5663
  name: "prose-claims",
5652
- description: 'Flags durability/side-effect claims that exist only in prose: a local step description or an intent paragraph claiming persistence ("persisted", "survives restart", "registered into") on a logic component whose narrative has no call/dispatch edge to any data-layer component (Store/Registry/Index/Adapter/Repository). Data-layer components are exempt \u2014 they ARE the persistence.',
5664
+ description: 'Flags durability/side-effect claims that exist only in prose: a local step description or an intent paragraph claiming persistence ("persisted", "persistence", "survives restart", "registered into") on a logic component whose narrative has no call, register or dispatch edge to any data-layer component (Store/Registry/Index/Adapter/Repository). Text inside quotes or backticks names a value or quotes a message and claims nothing. Data-layer components are exempt \u2014 they ARE the persistence.',
5653
5665
  codes: [
5654
5666
  { code: "UNREALIZED_CLAIM", defaultSeverity: "warning", summary: "Durability/side-effect claim in prose with no matching structural edge" }
5655
5667
  ],
@@ -5660,11 +5672,11 @@ var init_prose_claims = __esm({
5660
5672
  if (DATA_STEREOTYPES.has(comp.componentType)) continue;
5661
5673
  const isDraftCtx = ctx.isImplementationDraft(impl);
5662
5674
  for (const implMethod of impl.methods) {
5663
- const stepClaims = implMethod.narrative.filter((step) => step.type === "local").map((step) => ({ step, claim: CLAIM_PHRASES.exec(step.description) })).filter((c) => c.claim !== null);
5664
- const intentClaim = implMethod.intent ? CLAIM_PHRASES.exec(implMethod.intent) : null;
5675
+ const stepClaims = implMethod.narrative.filter((step) => step.type === "local").map((step) => ({ step, claim: CLAIM_PHRASES.exec(withoutQuotedText(step.description)) })).filter((c) => c.claim !== null);
5676
+ const intentClaim = implMethod.intent ? CLAIM_PHRASES.exec(withoutQuotedText(implMethod.intent)) : null;
5665
5677
  if (!stepClaims.length && !intentClaim) continue;
5666
5678
  const hasDataEdge = implMethod.narrative.some((step) => {
5667
- if (step.type !== "call" && step.type !== "dispatch") return false;
5679
+ if (step.type !== "call" && step.type !== "register" && step.type !== "dispatch") return false;
5668
5680
  if (!step.targetComponent) return false;
5669
5681
  const target = ctx.componentMap.get(step.targetComponent);
5670
5682
  if (target && DATA_STEREOTYPES.has(target.componentType)) return true;
@@ -5677,7 +5689,7 @@ var init_prose_claims = __esm({
5677
5689
  ctx.addIssue(
5678
5690
  "warning",
5679
5691
  "UNREALIZED_CLAIM",
5680
- `Step ${step.stepNumber} of "${implMethod.name}" in implementation "${impl.id}" claims "${claim[0]}" but no call/dispatch edge in this narrative reaches a Store/Registry/Index/Adapter \u2014 realize the claim as a structural edge (and durability tags), or reword the prose.`,
5692
+ `Step ${step.stepNumber} of "${implMethod.name}" in implementation "${impl.id}" claims "${claim[0]}" but no call/register/dispatch edge in this narrative reaches a Store/Registry/Index/Adapter \u2014 realize the claim as a structural edge (and durability tags), or reword the prose.`,
5681
5693
  impl.id,
5682
5694
  isDraftCtx
5683
5695
  );
@@ -5736,6 +5748,10 @@ function stepAsserts(impl, methodName, type, invariantId) {
5736
5748
  (step) => (step.assertsInvariants ?? []).some((ref) => refMatchesInvariant(ref, type, invariantId))
5737
5749
  );
5738
5750
  }
5751
+ function isEntityDraft(t, ctx) {
5752
+ const sub = t.subsystem ? ctx.subsystems.find((s) => s.id === t.subsystem) : void 0;
5753
+ return sub !== void 0 && isDraftSubsystem(sub);
5754
+ }
5739
5755
  function resolveComponentClass(t, ctx) {
5740
5756
  if (!t.componentClass) return void 0;
5741
5757
  const direct = ctx.componentMap.get(t.componentClass);
@@ -5762,6 +5778,7 @@ var init_invariant_backing = __esm({
5762
5778
  for (const t of ctx.types) {
5763
5779
  const invariants = t.invariants ?? [];
5764
5780
  if (invariants.length === 0) continue;
5781
+ const entityDraft = isEntityDraft(t, ctx);
5765
5782
  const seen = /* @__PURE__ */ new Set();
5766
5783
  for (const inv of invariants) {
5767
5784
  if (seen.has(inv.id)) {
@@ -5769,7 +5786,8 @@ var init_invariant_backing = __esm({
5769
5786
  "error",
5770
5787
  "DUPLICATE_INVARIANT_ID",
5771
5788
  `Entity "${t.id}" declares invariant id "${inv.id}" more than once \u2014 invariant ids must be unique within the entity.`,
5772
- t.id
5789
+ t.id,
5790
+ entityDraft
5773
5791
  );
5774
5792
  }
5775
5793
  seen.add(inv.id);
@@ -5780,7 +5798,8 @@ var init_invariant_backing = __esm({
5780
5798
  "warning",
5781
5799
  "INVARIANT_UNANCHORED",
5782
5800
  `Entity "${t.id}" declares ${invariants.length} invariant(s) but ${t.componentClass ? `its componentClass "${t.componentClass}" does not resolve to a component` : "has no componentClass"} \u2014 without an owning component there is no write path to hold the invariant against. Link the lifecycle owner via componentClass.`,
5783
- t.id
5801
+ t.id,
5802
+ entityDraft
5784
5803
  );
5785
5804
  continue;
5786
5805
  }
@@ -5794,13 +5813,13 @@ var init_invariant_backing = __esm({
5794
5813
  "INVARIANT_UNANCHORED",
5795
5814
  `Entity "${t.id}" declares ${invariants.length} invariant(s) anchored to "${comp.id}", but none of that component's contract methods declare effect: write \u2014 the validator cannot identify the write paths that must assert them. Tag the mutating methods with effect: write.`,
5796
5815
  t.id,
5797
- ctx.isComponentDraft(comp.id)
5816
+ entityDraft || ctx.isComponentDraft(comp.id)
5798
5817
  );
5799
5818
  continue;
5800
5819
  }
5801
5820
  for (const { intf, method } of writeMethods) {
5802
5821
  for (const impl of ctx.implementationsByContract.get(intf.id) ?? []) {
5803
- const isDraftCtx = ctx.isImplementationDraft(impl);
5822
+ const isDraftCtx = entityDraft || ctx.isImplementationDraft(impl);
5804
5823
  for (const inv of invariants) {
5805
5824
  if (stepAsserts(impl, method.name, t, inv.id)) continue;
5806
5825
  ctx.addIssue(
@@ -5838,30 +5857,35 @@ var init_invariant_backing = __esm({
5838
5857
 
5839
5858
  // src/core/rules/wiring/event-topology.ts
5840
5859
  function collectEnds(ctx) {
5841
- const emitters = [];
5842
- const subscribers = [];
5860
+ const emitters = /* @__PURE__ */ new Map();
5861
+ const subscribers = /* @__PURE__ */ new Map();
5862
+ const addEnd = (ends, topic, compId, via) => {
5863
+ const key = `${compId}#${topic}`;
5864
+ const end = ends.get(key);
5865
+ if (!end) ends.set(key, { topic, compId, vias: [via] });
5866
+ else if (!end.vias.includes(via)) end.vias.push(via);
5867
+ };
5843
5868
  for (const comp of ctx.components) {
5844
5869
  for (const e of comp.emits ?? []) {
5845
- emitters.push({ topic: e.topic, compId: comp.id, via: "emits declaration" });
5870
+ addEnd(emitters, e.topic, comp.id, "emits declaration");
5846
5871
  }
5847
5872
  for (const s of comp.subscribesTo ?? []) {
5848
- subscribers.push({ topic: s.topic, compId: comp.id, via: "subscribesTo declaration" });
5873
+ addEnd(subscribers, s.topic, comp.id, "subscribesTo declaration");
5849
5874
  }
5850
5875
  }
5851
5876
  for (const intf of ctx.interfaces) {
5852
5877
  for (const method of intf.methods) {
5853
5878
  const ep = method.endpoint;
5854
5879
  if (!ep || ep.transport !== "MessageBus") continue;
5855
- const end = {
5856
- topic: ep.topic,
5857
- compId: intf.component,
5858
- via: `MessageBus endpoint on ${intf.id}.${method.name}`
5859
- };
5860
- if (ep.direction === "publish") emitters.push(end);
5861
- else subscribers.push(end);
5880
+ addEnd(
5881
+ ep.direction === "publish" ? emitters : subscribers,
5882
+ ep.topic,
5883
+ intf.component,
5884
+ `MessageBus endpoint on ${intf.id}.${method.name}`
5885
+ );
5862
5886
  }
5863
5887
  }
5864
- return { emitters, subscribers };
5888
+ return { emitters: [...emitters.values()], subscribers: [...subscribers.values()] };
5865
5889
  }
5866
5890
  var eventTopologyRule;
5867
5891
  var init_event_topology = __esm({
@@ -5869,7 +5893,7 @@ var init_event_topology = __esm({
5869
5893
  "use strict";
5870
5894
  eventTopologyRule = {
5871
5895
  name: "event-topology",
5872
- description: "Bipartite completeness of the declared pub/sub graph: every topic a component emits (emits declarations, MessageBus publish endpoints) must have at least one subscriber (subscribesTo declarations, MessageBus subscribe endpoints), and every subscription must have at least one source. Pairing is by exact topic name. Trees that declare no event edges see nothing \u2014 the rule costs request/response systems zero noise. Warnings + lint.allow: a topic produced for (or consumed from) an EXTERNAL system is a legitimate, declarable exception.",
5896
+ description: "Bipartite completeness of the declared pub/sub graph: every topic a component emits (emits declarations, MessageBus publish endpoints) must have at least one subscriber (subscribesTo declarations, MessageBus subscribe endpoints), and every subscription must have at least one source. Pairing is by exact topic name, and a topic is reported once per component however many declarations bind it there. Trees that declare no event edges see nothing \u2014 the rule costs request/response systems zero noise. Warnings + lint.allow: a topic produced for (or consumed from) an EXTERNAL system is a legitimate, declarable exception.",
5873
5897
  codes: [
5874
5898
  { code: "UNCONSUMED_TOPIC", defaultSeverity: "warning", summary: "Topic is emitted but nothing in the tree subscribes to it" },
5875
5899
  { code: "UNSOURCED_SUBSCRIPTION", defaultSeverity: "warning", summary: "Topic is subscribed to but nothing in the tree emits it" }
@@ -5884,7 +5908,7 @@ var init_event_topology = __esm({
5884
5908
  ctx.addIssue(
5885
5909
  "warning",
5886
5910
  "UNCONSUMED_TOPIC",
5887
- `Component "${e.compId}" emits topic "${e.topic}" (${e.via}), but nothing in this tree subscribes to it \u2014 the event goes nowhere. Wire a subscriber (subscribesTo, or a MessageBus subscribe endpoint), fix the topic name, or lint.allow with the external consumer named.`,
5911
+ `Component "${e.compId}" emits topic "${e.topic}" (${e.vias.join(", ")}), but nothing in this tree subscribes to it \u2014 the event goes nowhere. Wire a subscriber (subscribesTo, or a MessageBus subscribe endpoint), fix the topic name, or lint.allow with the external consumer named.`,
5888
5912
  e.compId,
5889
5913
  ctx.isComponentDraft(e.compId)
5890
5914
  );
@@ -5894,7 +5918,7 @@ var init_event_topology = __esm({
5894
5918
  ctx.addIssue(
5895
5919
  "warning",
5896
5920
  "UNSOURCED_SUBSCRIPTION",
5897
- `Component "${s.compId}" subscribes to topic "${s.topic}" (${s.via}), but nothing in this tree emits it \u2014 the handler can never fire from inside this system. Wire the emitter (emits, or a MessageBus publish endpoint), fix the topic name, or lint.allow with the external source named.`,
5921
+ `Component "${s.compId}" subscribes to topic "${s.topic}" (${s.vias.join(", ")}), but nothing in this tree emits it \u2014 the handler can never fire from inside this system. Wire the emitter (emits, or a MessageBus publish endpoint), fix the topic name, or lint.allow with the external source named.`,
5898
5922
  s.compId,
5899
5923
  ctx.isComponentDraft(s.compId)
5900
5924
  );
@@ -5913,13 +5937,13 @@ var init_structural_conformance = __esm({
5913
5937
  quoteList = (names) => names.map((n) => `"${n}"`).join(", ");
5914
5938
  structuralConformanceRule = {
5915
5939
  name: "structural-conformance",
5916
- 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 appear as a string literal in that file (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.",
5940
+ 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.",
5917
5941
  codes: [
5918
5942
  { 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" },
5919
5943
  { 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" },
5920
5944
  { 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)" },
5921
5945
  { 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" },
5922
- { code: "UNREALIZED_FINDING", defaultSeverity: "warning", summary: "A finding code a contract method declares does not appear as a string literal in the method's source file" },
5946
+ { 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)" },
5923
5947
  { 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" },
5924
5948
  { code: "CONFORMANCE_DEGRADED", defaultSeverity: "warning", summary: "TypeScript/JavaScript files were analyzed below exact grade (compiler not resolvable) \u2014 dependency conformance skips them" }
5925
5949
  ],
@@ -5929,7 +5953,7 @@ var init_structural_conformance = __esm({
5929
5953
  lookups.set(pathKey(facts.path), {
5930
5954
  declared: /* @__PURE__ */ new Set([...facts.declaredNames, ...facts.exportedNames]),
5931
5955
  anchored: /* @__PURE__ */ new Set([...facts.declaredNames, ...facts.exportedNames, ...facts.anchoredNames]),
5932
- literals: new Set(facts.anchoredNames),
5956
+ findingAnchors: new Set(facts.anchoredNames),
5933
5957
  facts
5934
5958
  });
5935
5959
  }
@@ -6049,11 +6073,11 @@ var init_structural_conformance = __esm({
6049
6073
  if (!checked) continue;
6050
6074
  const { file, lookup } = checked;
6051
6075
  for (const finding of method.findings) {
6052
- if (lookup.literals.has(finding.code)) continue;
6076
+ if (lookup.findingAnchors.has(finding.code)) continue;
6053
6077
  ctx.addIssue(
6054
6078
  "warning",
6055
6079
  "UNREALIZED_FINDING",
6056
- `Method "${method.name}" of contract "${impl.contract}" declares finding "${finding.code}", but "${file}" has no string literal "${finding.code}" (analysis grade: ${lookup.facts.analysisGrade}) \u2014 report the finding under its declared code, or remove the declaration.`,
6080
+ `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.`,
6057
6081
  impl.id,
6058
6082
  draft
6059
6083
  );
@@ -6169,12 +6193,18 @@ var init_hidden_state = __esm({
6169
6193
  check(ctx) {
6170
6194
  const byPath = /* @__PURE__ */ new Map();
6171
6195
  for (const impl of ctx.implementations) {
6172
- if (impl.conformance === "off") continue;
6173
6196
  const contract = ctx.interfaceMap.get(impl.contract);
6174
6197
  const component = contract ? ctx.componentMap.get(contract.component) : void 0;
6175
6198
  if (!component) continue;
6176
6199
  if (ctx.isInChainedSubproject(component.subsystem)) continue;
6177
- for (const file of implementationSourceFiles(impl)) {
6200
+ const trusted = [];
6201
+ if (impl.sourcePath && impl.conformance !== "off") trusted.push(impl.sourcePath);
6202
+ for (const method of impl.methods) {
6203
+ if ((method.conformance ?? impl.conformance) === "off") continue;
6204
+ const file = methodSourceFile(method, impl.sourcePath);
6205
+ if (file) trusted.push(file);
6206
+ }
6207
+ for (const file of trusted) {
6178
6208
  const key = pathKey(file);
6179
6209
  const list = byPath.get(key) ?? [];
6180
6210
  if (list.some((m) => m.impl === impl)) continue;
@@ -6325,7 +6355,7 @@ var init_dependency_conformance = __esm({
6325
6355
  const fromFiles = filesByComponent.get(component.id);
6326
6356
  if (!fromFiles) continue;
6327
6357
  const isMountingDeclarer = component.componentType === "Portal" || component.componentType === "Observer";
6328
- const declaredTargets = [...component.dependsOn, ...component.owns ?? []];
6358
+ const declaredTargets = [.../* @__PURE__ */ new Set([...component.dependsOn, ...component.owns ?? []])];
6329
6359
  for (const targetId of declaredTargets) {
6330
6360
  const target = ctx.componentMap.get(targetId);
6331
6361
  if (!target) continue;
@@ -6346,11 +6376,14 @@ var init_dependency_conformance = __esm({
6346
6376
  }
6347
6377
  const impls = implsByComponent.get(component.id) ?? [];
6348
6378
  const anchor = impls[0];
6379
+ const declaredDependsOn = component.dependsOn.includes(targetId);
6380
+ const declaredOwns = (component.owns ?? []).includes(targetId);
6381
+ const relation = declaredDependsOn && declaredOwns ? "dependsOn and owns" : declaredDependsOn ? "dependsOn" : "owns";
6349
6382
  const draft = ctx.isComponentDraft(component.id) || ctx.isComponentDraft(targetId) || impls.some((i) => ctx.isImplementationDraft(i));
6350
6383
  ctx.addIssue(
6351
6384
  "warning",
6352
6385
  "UNREALIZED_DEPENDENCY",
6353
- `Component "${component.id}" declares ${component.dependsOn.includes(targetId) ? "dependsOn" : "owns"} "${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.`,
6386
+ `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.`,
6354
6387
  anchor?.id ?? component.id,
6355
6388
  draft
6356
6389
  );
@@ -6382,28 +6415,28 @@ var init_integration_conformance = __esm({
6382
6415
  const filesByComponent = /* @__PURE__ */ new Map();
6383
6416
  const filesBySubsystem = /* @__PURE__ */ new Map();
6384
6417
  const implsByComponent = /* @__PURE__ */ new Map();
6418
+ const adopted = /* @__PURE__ */ new Set();
6419
+ const exists = (p) => {
6420
+ const status = factsByPath.get(p)?.status;
6421
+ return status === "analyzed" || status === "unreadable";
6422
+ };
6385
6423
  for (const impl of ctx.implementations) {
6386
6424
  const contract = ctx.interfaceMap.get(impl.contract);
6387
6425
  const comp = contract ? ctx.componentMap.get(contract.component) : void 0;
6388
- if (!comp) continue;
6426
+ if (!comp || ctx.isInChainedSubproject(comp.subsystem)) continue;
6389
6427
  if (!implsByComponent.has(comp.id)) implsByComponent.set(comp.id, []);
6390
6428
  implsByComponent.get(comp.id).push(impl);
6391
6429
  for (const file of implementationSourceFiles(impl)) {
6392
6430
  const p = pathKey(file);
6431
+ if (!exists(p)) continue;
6393
6432
  if (!filesByComponent.has(comp.id)) filesByComponent.set(comp.id, /* @__PURE__ */ new Set());
6394
6433
  filesByComponent.get(comp.id).add(p);
6395
6434
  if (!filesBySubsystem.has(comp.subsystem)) filesBySubsystem.set(comp.subsystem, /* @__PURE__ */ new Set());
6396
6435
  filesBySubsystem.get(comp.subsystem).add(p);
6397
6436
  }
6437
+ if (impl.simPath) adopted.add(comp.subsystem);
6398
6438
  }
6399
6439
  const isTechBoundary = (compId) => (implsByComponent.get(compId) ?? []).some((i) => (i.technologies ?? []).length > 0);
6400
- const adopted = /* @__PURE__ */ new Set();
6401
- for (const impl of ctx.implementations) {
6402
- if (!impl.simPath) continue;
6403
- const contract = ctx.interfaceMap.get(impl.contract);
6404
- const comp = contract ? ctx.componentMap.get(contract.component) : void 0;
6405
- if (comp) adopted.add(comp.subsystem);
6406
- }
6407
6440
  const reachFrom = (start) => {
6408
6441
  const seen = /* @__PURE__ */ new Set([start]);
6409
6442
  const stack = [start];
@@ -6600,8 +6633,8 @@ var init_coupling_health = __esm({
6600
6633
  );
6601
6634
  }
6602
6635
  }
6603
- const threshold = ctx.rules?.complexity?.maxComponentDependencies ?? DEFAULT_GOD_COMPONENT_THRESHOLD;
6604
6636
  for (const comp of ctx.components) {
6637
+ const threshold = ctx.complexityConfigFor(comp.subsystem)?.maxComponentDependencies ?? DEFAULT_GOD_COMPONENT_THRESHOLD;
6605
6638
  if (comp.dependsOn.length > threshold) {
6606
6639
  ctx.addIssue(
6607
6640
  "warning",
@@ -6729,7 +6762,7 @@ var init_target_language = __esm({
6729
6762
  if (!lang) continue;
6730
6763
  const gaps = gapsFor(lang);
6731
6764
  if (Object.keys(gaps).length === 0) continue;
6732
- const isDraftCtx = impl.status === "draft" || impl.status === "design" || contract.status === "draft" || contract.status === "design" || ctx.isComponentDraft(contract.component);
6765
+ const isDraftCtx = ctx.isImplementationDraft(impl);
6733
6766
  for (const implMethod of impl.methods) {
6734
6767
  for (const step of implMethod.narrative) {
6735
6768
  const constructs = [
@@ -6831,7 +6864,7 @@ var init_technology_boundaries = __esm({
6831
6864
  "TECH_ON_LOGIC_COMPONENT",
6832
6865
  `Implementation "${impl.id}" binds technology (${impl.technologies.join(", ")}) on component "${comp.id}" (${comp.componentType}). Technology belongs behind a data-layer seam \u2014 extract an Adapter (or Store/Registry/Index) behind an intent interface and let this component depend on that.`,
6833
6866
  impl.id,
6834
- ctx.isComponentDraft(comp.id)
6867
+ ctx.isImplementationDraft(impl)
6835
6868
  );
6836
6869
  }
6837
6870
  const compSet = ownsClosure(ownershipRoot(comp.id));
@@ -6869,7 +6902,7 @@ var init_technology_boundaries = __esm({
6869
6902
  "VENDOR_NAME_IN_CONTRACT",
6870
6903
  `Interface "${intf.id}" exposes technology "${home.label}" in contract identifiers (method${offending.length > 1 ? "s" : ""}: ${offending.join(", ")}). Name the intent, not the vendor \u2014 the technology is owned by component ${ownersDesc(home)} and must stay swappable behind this contract.`,
6871
6904
  intf.id,
6872
- ctx.isComponentDraft(intf.component)
6905
+ ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design"
6873
6906
  );
6874
6907
  }
6875
6908
  }
@@ -6900,7 +6933,8 @@ var init_technology_boundaries = __esm({
6900
6933
  "warning",
6901
6934
  "TECH_LEAKAGE",
6902
6935
  `Subsystem "${sub.id}" references "${home.label}" outside the technology's owning boundary (component ${ownersDesc(home)}).`,
6903
- sub.id
6936
+ sub.id,
6937
+ isDraftSubsystem(sub)
6904
6938
  );
6905
6939
  }
6906
6940
  }
@@ -6913,7 +6947,7 @@ var init_technology_boundaries = __esm({
6913
6947
  "TECH_LEAKAGE",
6914
6948
  `Interface "${intf.id}" describes "${home.label}" outside its owning boundary (component ${ownersDesc(home)}) \u2014 consumers must not know backend specifics.`,
6915
6949
  intf.id,
6916
- ctx.isComponentDraft(intf.component)
6950
+ ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design"
6917
6951
  );
6918
6952
  }
6919
6953
  }
@@ -6933,7 +6967,8 @@ var init_technology_boundaries = __esm({
6933
6967
  "warning",
6934
6968
  "TECH_LEAKAGE",
6935
6969
  `Implementation "${impl.id}" references "${home.label}" outside its owning boundary \u2014 call the intent interface of component ${ownersDesc(home)} instead of naming its technology.`,
6936
- impl.id
6970
+ impl.id,
6971
+ ctx.isImplementationDraft(impl)
6937
6972
  );
6938
6973
  }
6939
6974
  }
@@ -6996,6 +7031,7 @@ var casingPatterns, isAllUppercase, namingRule;
6996
7031
  var init_naming_conventions = __esm({
6997
7032
  "src/core/rules/heuristic/naming-conventions.ts"() {
6998
7033
  "use strict";
7034
+ init_models();
6999
7035
  casingPatterns = {
7000
7036
  camelCase: /^[a-z][a-zA-Z0-9]*$/,
7001
7037
  PascalCase: /^[A-Z][a-zA-Z0-9]*$/,
@@ -7016,9 +7052,10 @@ var init_naming_conventions = __esm({
7016
7052
  for (const sub of ctx.subsystems) {
7017
7053
  const namingConfig = ctx.namingConfigFor(sub.id);
7018
7054
  if (!namingConfig?.subsystems) continue;
7055
+ const isDraft = isDraftSubsystem(sub);
7019
7056
  const baseId = getBaseId(sub.id);
7020
- checkNamedValue(ctx, baseId, namingConfig.subsystems, `Subsystem ID "${sub.id}" does not match naming convention "${namingConfig.subsystems}".`, sub.id);
7021
- checkNamedValue(ctx, sub.name, namingConfig.subsystems, `Subsystem Name "${sub.name}" does not match naming convention "${namingConfig.subsystems}".`, sub.id);
7057
+ checkNamedValue(ctx, baseId, namingConfig.subsystems, `Subsystem ID "${sub.id}" does not match naming convention "${namingConfig.subsystems}".`, sub.id, isDraft);
7058
+ checkNamedValue(ctx, sub.name, namingConfig.subsystems, `Subsystem Name "${sub.name}" does not match naming convention "${namingConfig.subsystems}".`, sub.id, isDraft);
7022
7059
  }
7023
7060
  for (const comp of ctx.components) {
7024
7061
  const namingConfig = ctx.namingConfigFor(comp.subsystem);
@@ -7103,7 +7140,7 @@ var init_naming_conventions = __esm({
7103
7140
  const intf = ctx.interfaceMap.get(impl.contract);
7104
7141
  const comp = intf ? ctx.componentMap.get(intf.component) : void 0;
7105
7142
  const namingConfig = ctx.namingConfigFor(comp?.subsystem);
7106
- const isDraft = impl.status === "draft" || impl.status === "design";
7143
+ const isDraft = ctx.isImplementationDraft(impl);
7107
7144
  if (namingConfig?.methods) {
7108
7145
  for (const m of impl.methods) {
7109
7146
  checkNamedValue(ctx, m.name, namingConfig.methods, `Implementation method "${m.name}" on "${impl.id}" does not match naming convention "${namingConfig.methods}".`, impl.id, isDraft);
@@ -7167,6 +7204,7 @@ var complexityRule;
7167
7204
  var init_complexity_and_metadata = __esm({
7168
7205
  "src/core/rules/heuristic/complexity-and-metadata.ts"() {
7169
7206
  "use strict";
7207
+ init_models();
7170
7208
  complexityRule = {
7171
7209
  name: "complexity-and-metadata",
7172
7210
  description: "Enforces metadata documentation completeness (checking if descriptions are missing or too short) and structural complexity caps (limits on methods per interface, dependencies per component, and narrative steps per method) configured globally or in architectural profiles.",
@@ -7182,7 +7220,8 @@ var init_complexity_and_metadata = __esm({
7182
7220
  check(ctx) {
7183
7221
  for (const sub of ctx.subsystems) {
7184
7222
  const docConfig = ctx.documentationConfigFor(sub.id);
7185
- checkDescription(ctx, sub.description, docConfig?.requireDescriptions ?? false, docConfig?.minDescriptionLength, sub.id, "Subsystem", false);
7223
+ const isSubDraft = isDraftSubsystem(sub);
7224
+ checkDescription(ctx, sub.description, docConfig?.requireDescriptions ?? false, docConfig?.minDescriptionLength, sub.id, "Subsystem", isSubDraft);
7186
7225
  const complexityConfig = ctx.complexityConfigFor(sub.id);
7187
7226
  const directComponents = ctx.components.filter((c) => c.subsystem === sub.id).length;
7188
7227
  if (complexityConfig?.maxSubsystemComponents !== void 0 && directComponents > complexityConfig.maxSubsystemComponents) {
@@ -7190,7 +7229,8 @@ var init_complexity_and_metadata = __esm({
7190
7229
  "warning",
7191
7230
  "EXCESSIVE_SUBSYSTEM_COMPONENTS",
7192
7231
  `Subsystem "${sub.id}" has ${directComponents} direct components, exceeding the configured limit of ${complexityConfig.maxSubsystemComponents}.`,
7193
- sub.id
7232
+ sub.id,
7233
+ isSubDraft
7194
7234
  );
7195
7235
  }
7196
7236
  }
@@ -7266,7 +7306,7 @@ var init_complexity_and_metadata = __esm({
7266
7306
  const intf = ctx.interfaceMap.get(impl.contract);
7267
7307
  const comp = intf ? ctx.componentMap.get(intf.component) : void 0;
7268
7308
  const complexityConfig = ctx.complexityConfigFor(comp?.subsystem);
7269
- const isDraft = impl.status === "draft" || impl.status === "design";
7309
+ const isDraft = ctx.isImplementationDraft(impl);
7270
7310
  if (complexityConfig?.maxNarrativeSteps !== void 0) {
7271
7311
  for (const m of impl.methods) {
7272
7312
  if (m.narrative.length > complexityConfig.maxNarrativeSteps) {
@@ -13980,7 +14020,7 @@ function buildRuleContext(opts) {
13980
14020
  return rules.sddRuleSeverity[ruleCode];
13981
14021
  }
13982
14022
  const sub = subsystemId ? subsystems.find((s) => s.id === subsystemId) : void 0;
13983
- const profileSeverity = extensions.profiles[sub?.profile ?? projectType]?.rules?.sddRuleSeverity?.[ruleCode];
14023
+ const profileSeverity = extensions.profiles[sub?.profile || projectType]?.rules?.sddRuleSeverity?.[ruleCode];
13984
14024
  if (profileSeverity) {
13985
14025
  return profileSeverity;
13986
14026
  }