@wairon/cli 5.1.1-dev.45 → 5.1.1-dev.46

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
@@ -1222,7 +1222,7 @@ var init_defaults = __esm({
1222
1222
  copilot: ".github/prompts",
1223
1223
  codex: ".codex/agents"
1224
1224
  };
1225
- WAIRON_VERSION = "5.1.1-dev.45";
1225
+ WAIRON_VERSION = "5.1.1-dev.46";
1226
1226
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
1227
1227
  ARCHITECT_AGENT_ID = "agent-architect";
1228
1228
  ARCHITECT_TEMPLATE_ID = "architect";
@@ -2230,8 +2230,12 @@ var init_specs = __esm({
2230
2230
  * so a lower detail dial no longer vouches for everything the component
2231
2231
  * declares. Each entry gets the same target validation a `call` step gets
2232
2232
  * (the component resolves, the caller declares it, the method is on its
2233
- * contract). Refused beside a non-empty narrative, where the steps already
2234
- * say what is called and a second spelling could only disagree.
2233
+ * contract) AND the same code↔spec reading (call-conformance, both
2234
+ * directions): an entry asserts the same call a step does, minus the
2235
+ * position in the flow that check never reads, so it can no more buy
2236
+ * reachability without the code than a step can. Refused beside a non-empty
2237
+ * narrative, where the steps already say what is called and a second
2238
+ * spelling could only disagree.
2235
2239
  */
2236
2240
  calls: import_zod8.z.array(import_zod8.z.string().min(1)).optional(),
2237
2241
  /**
@@ -13559,6 +13563,18 @@ function resolveCallTarget(ctx, componentId, methodName) {
13559
13563
  }
13560
13564
  return { accepted, files };
13561
13565
  }
13566
+ function claimsOf(implMethod) {
13567
+ const claims = [];
13568
+ for (const step of implMethod.narrative) {
13569
+ if (step.type !== "call" || !step.targetComponent || !step.targetMethod) continue;
13570
+ claims.push({ step: step.stepNumber, component: step.targetComponent, method: step.targetMethod });
13571
+ }
13572
+ for (const ref of implMethod.calls ?? []) {
13573
+ const parsed = parseDeclaredCall(ref);
13574
+ if (parsed) claims.push({ component: parsed.compId, method: parsed.methodName });
13575
+ }
13576
+ return claims;
13577
+ }
13562
13578
  function describeSite(site) {
13563
13579
  if (!site.member) return `${site.name}(\u2026)`;
13564
13580
  if (site.via) return `${site.via}.${site.name}(\u2026)`;
@@ -13567,23 +13583,26 @@ function describeSite(site) {
13567
13583
  return `<receiver>.${site.name}(\u2026)`;
13568
13584
  }
13569
13585
  function describeMiss(m) {
13570
- const head = `step ${m.step} \u2192 ${m.target} (looked for ${m.accepted.map((a) => `"${a}"`).join(" / ")}`;
13586
+ const where = m.step === void 0 ? "declared call" : `step ${m.step}`;
13587
+ const head = `${where} \u2192 ${m.target} (looked for ${m.accepted.map((a) => `"${a}"`).join(" / ")}`;
13571
13588
  if (m.miss.kind === "elsewhere") return `${head}, called but resolved to ${m.miss.landed.map((p) => `"${p}"`).join(", ")})`;
13572
13589
  if (m.miss.kind === "unresolved") return `${head}, written as ${m.miss.shapes.map((s) => `\`${s}\``).join(" / ")})`;
13573
13590
  return `${head})`;
13574
13591
  }
13575
- var callConformanceRule;
13592
+ var unitOf, claimNoun, callConformanceRule;
13576
13593
  var init_call_conformance = __esm({
13577
13594
  "src/core/rules/conformance/call-conformance.ts"() {
13578
13595
  "use strict";
13579
13596
  init_models();
13597
+ unitOf = (m) => m.step === void 0 ? m.target : `${m.step}:${m.target}`;
13598
+ claimNoun = (implMethod) => implMethod.narrative.length ? "narrative call step(s)" : "declared call(s)";
13580
13599
  callConformanceRule = {
13581
13600
  name: "call-conformance",
13582
- description: "Code\u2194spec Level 3: the narrative `call` step \u2194 realized call relation, judged both ways against the method's own source file (its sourcePath, else the implementation's), at exact analysis grade only. Forward: every `call` step must be realized by a call whose callee RESOLVES TO one of the target method's own source files \u2014 the target's contract name or a per-method `symbol` override, closed transitively over the named helpers the realized function calls, and resolved against every file the call CAN have reached: a `this.<field>.<method>()` receiver followed through the field's DECLARED TYPE, a `new Class(...).<method>()` receiver through the module its CLASS NAME came from. A matching call whose origin a pure model cannot resolve is reported apart as CALL_ORIGIN_UNRESOLVED, which NAMES the shape it could not follow and asks for nothing \u2014 a coverage hole in the reader, reported for the reason CONFORMANCE_DEGRADED is: a silently degraded gate is worse than a degraded one. A finding names a landing only from the PROVEN tier so that widening what a call reached can accept a step but never accuse one, and a target that names no file of its own falls back to name membership. Converse: a call that resolves to a modelled method of ANOTHER component in the SAME file crosses a component boundary while looking local, so the narrative must declare it (UNDECLARED_COLOCATED_CALL) \u2014 judged on the proven tier alone, and a same-file private helper is no modelled method and is never reported. Order, arguments and conditions stay unverified, dispatch steps (runtime-table routed) are skipped, the conformance dial (off) skips, and weaker analysis grades never guess.",
13601
+ description: "Code\u2194spec Level 3: the CLAIMED call \u2194 realized call relation, judged both ways against the method's own source file (its sourcePath, else the implementation's), at exact analysis grade only. A method claims a call two ways and they face one check: a narrative `call` step, and an entry of the declared `calls` a narrative-less method reaches its collaborators through. They assert the same thing, and the step number is no part of the claim \u2014 this check never reads order, so it is only how a finding points at one. Forward: every claim must be realized by a call whose callee RESOLVES TO one of the target method's own source files \u2014 the target's contract name or a per-method `symbol` override, closed transitively over the named helpers the realized function calls, and resolved against every file the call CAN have reached: a `this.<field>.<method>()` receiver followed through the field's DECLARED TYPE, a `new Class(...).<method>()` receiver through the module its CLASS NAME came from. A matching call whose origin a pure model cannot resolve is reported apart as CALL_ORIGIN_UNRESOLVED, which NAMES the shape it could not follow and asks for nothing \u2014 a coverage hole in the reader, reported for the reason CONFORMANCE_DEGRADED is: a silently degraded gate is worse than a degraded one. A finding names a landing only from the PROVEN tier so that widening what a call reached can accept a claim but never accuse one, and a target that names no file of its own falls back to name membership. Converse: a call that resolves to a modelled method of ANOTHER component in the SAME file crosses a component boundary while looking local, so the method must claim it \u2014 in a step or in `calls` (UNDECLARED_COLOCATED_CALL) \u2014 judged on the proven tier alone, and a same-file private helper is no modelled method and is never reported. A method that claims nothing at all is judged in neither direction: what it leaves unsaid is UNUSED_*'s subject. Order, arguments and conditions stay unverified, dispatch steps (runtime-table routed) are no claim about a call site, a malformed declared reference is MALFORMED_DECLARED_CALL's finding and names no target here, the conformance dial (off) skips, and weaker analysis grades never guess.",
13583
13602
  codes: [
13584
- { code: "CALL_STEP_UNREALIZED", defaultSeverity: "warning", summary: "Narrative call step realized by no call that resolves to the target method's own source file \u2014 the call is absent, or it lands in another module", carryable: true },
13585
- { code: "CALL_ORIGIN_UNRESOLVED", defaultSeverity: "warning", summary: "Narrative call step whose target name IS called, but only from call sites written in a shape this analysis cannot resolve to a file \u2014 the step was not checked, and is neither proven realized nor accused", carryable: true },
13586
- { code: "UNDECLARED_COLOCATED_CALL", defaultSeverity: "warning", summary: "The realized function calls a modelled method of another component living in the same source file, and no narrative step declares that call", carryable: true }
13603
+ { code: "CALL_STEP_UNREALIZED", defaultSeverity: "warning", summary: "Narrative call step or declared call realized by no call that resolves to the target method's own source file \u2014 the call is absent, or it lands in another module", carryable: true },
13604
+ { code: "CALL_ORIGIN_UNRESOLVED", defaultSeverity: "warning", summary: "Narrative call step or declared call whose target name IS called, but only from call sites written in a shape this analysis cannot resolve to a file \u2014 the claim was not checked, and is neither proven realized nor accused", carryable: true },
13605
+ { code: "UNDECLARED_COLOCATED_CALL", defaultSeverity: "warning", summary: "The realized function calls a modelled method of another component living in the same source file, and neither a narrative step nor a declared call names it", carryable: true }
13587
13606
  ],
13588
13607
  check(ctx) {
13589
13608
  const code = ctx.codeIndex();
@@ -13600,7 +13619,7 @@ var init_call_conformance = __esm({
13600
13619
  const { implementation: impl, method: implMethod, component, sourceFile: file } = entry;
13601
13620
  const tier = implMethod.conformance ?? impl.conformance ?? defaultConformanceTier(component);
13602
13621
  if (tier === "off") continue;
13603
- if (!implMethod.narrative.length) continue;
13622
+ if (!implMethod.narrative.length && !implMethod.calls?.length) continue;
13604
13623
  if (!file) continue;
13605
13624
  const facts = code.factsAt(file);
13606
13625
  if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
@@ -13610,16 +13629,15 @@ var init_call_conformance = __esm({
13610
13629
  const sites = closedCallSites(code, file, fnSymbol);
13611
13630
  if (!sites) continue;
13612
13631
  const missed = [];
13613
- for (const step of implMethod.narrative) {
13614
- if (step.type !== "call" || !step.targetComponent || !step.targetMethod) continue;
13615
- if (!ctx.componentMap.has(step.targetComponent)) continue;
13616
- const target = resolveCallTarget(ctx, step.targetComponent, step.targetMethod);
13632
+ for (const claim of claimsOf(implMethod)) {
13633
+ if (!ctx.componentMap.has(claim.component)) continue;
13634
+ const target = resolveCallTarget(ctx, claim.component, claim.method);
13617
13635
  if (target.accepted.has(fnSymbol)) continue;
13618
13636
  const matching = sites.filter((s) => target.accepted.has(s.name));
13619
- const ref = `${step.targetComponent}.${step.targetMethod}`;
13637
+ const ref = `${claim.component}.${claim.method}`;
13620
13638
  if (target.files.size === 0) {
13621
13639
  if (matching.length === 0) {
13622
- missed.push({ step: step.stepNumber, target: ref, accepted: [...target.accepted], miss: { kind: "absent" } });
13640
+ missed.push({ step: claim.step, target: ref, accepted: [...target.accepted], miss: { kind: "absent" } });
13623
13641
  }
13624
13642
  continue;
13625
13643
  }
@@ -13633,7 +13651,7 @@ var init_call_conformance = __esm({
13633
13651
  }
13634
13652
  if (realized) continue;
13635
13653
  const miss = matching.length === 0 ? { kind: "absent" } : landed.size === 0 ? { kind: "unresolved", shapes: [...new Set(matching.map(describeSite))].sort() } : { kind: "elsewhere", landed: [...landed].sort() };
13636
- missed.push({ step: step.stepNumber, target: ref, accepted: [...target.accepted], miss });
13654
+ missed.push({ step: claim.step, target: ref, accepted: [...target.accepted], miss });
13637
13655
  }
13638
13656
  const unrealized = missed.filter((m) => m.miss.kind !== "unresolved");
13639
13657
  const unresolved = missed.filter((m) => m.miss.kind === "unresolved");
@@ -13641,30 +13659,36 @@ var init_call_conformance = __esm({
13641
13659
  ctx.addIssue(
13642
13660
  "warning",
13643
13661
  "CALL_STEP_UNREALIZED",
13644
- `Method "${implMethod.name}" in implementation "${impl.id}": ${unrealized.length} narrative call step(s) are realized by no call of the function "${fnSymbol}" in "${file}" that resolves to the target's own source file \u2014 ${unrealized.map(describeMiss).join("; ")}. Callees are closed over the named helpers the function calls, and each call site is resolved through this file's import bindings (order, arguments and conditions are not checked). Realize the calls, fix the narrative, or map code names via per-method symbols on the targets.`,
13662
+ `Method "${implMethod.name}" in implementation "${impl.id}": ${unrealized.length} ${claimNoun(implMethod)} are realized by no call of the function "${fnSymbol}" in "${file}" that resolves to the target's own source file \u2014 ${unrealized.map(describeMiss).join("; ")}. Callees are closed over the named helpers the function calls, and each call site is resolved through this file's import bindings (order, arguments and conditions are not checked). Realize the calls, fix what the method claims, or map code names via per-method symbols on the targets.`,
13645
13663
  impl.id,
13646
13664
  entry.draftContext,
13647
13665
  void 0,
13648
- // A step, named by its number and its target: what the register
13649
- // carries is ONE unresolved step, never "this method's call steps".
13650
- { at: implMethod.name, covers: unrealized.map((m) => `${m.step}:${m.target}`) }
13666
+ // One claim, named the way the register names a unit: a step by its
13667
+ // number and target, a declaration by its target alone. What the
13668
+ // register carries is ONE unrealized claim, never "this method's
13669
+ // calls".
13670
+ { at: implMethod.name, covers: unrealized.map(unitOf) }
13651
13671
  );
13652
13672
  }
13653
13673
  if (unresolved.length) {
13654
13674
  ctx.addIssue(
13655
13675
  "warning",
13656
13676
  "CALL_ORIGIN_UNRESOLVED",
13657
- `Method "${implMethod.name}" in implementation "${impl.id}": ${unresolved.length} narrative call step(s) ARE called by name inside the function "${fnSymbol}" in "${file}", but every site calling them is written in a shape this analysis cannot resolve to a file \u2014 ${unresolved.map(describeMiss).join("; ")}. What resolves is a name bound to a module of THIS project: a bare or namespaced call through such an import binding, a \`this.<field>\` receiver whose declared type names one, a \`new Class(\u2026)\` receiver whose class does. What does not: an import from a PACKAGE specifier, a receiver holding a value the module assembled, a receiver this model records no name for. This reports what was not checked, not what is wrong \u2014 the step is neither proven realized nor accused of being missing, and no working call is asked to be rewritten to suit the reader.`,
13677
+ `Method "${implMethod.name}" in implementation "${impl.id}": ${unresolved.length} ${claimNoun(implMethod)} ARE called by name inside the function "${fnSymbol}" in "${file}", but every site calling them is written in a shape this analysis cannot resolve to a file \u2014 ${unresolved.map(describeMiss).join("; ")}. What resolves is a name bound to a module of THIS project: a bare or namespaced call through such an import binding, a \`this.<field>\` receiver whose declared type names one, a \`new Class(\u2026)\` receiver whose class does. What does not: an import from a PACKAGE specifier, a receiver holding a value the module assembled, a receiver this model records no name for. This reports what was not checked, not what is wrong \u2014 the claim is neither proven realized nor accused of being missing, and no working call is asked to be rewritten to suit the reader.`,
13658
13678
  impl.id,
13659
13679
  entry.draftContext,
13660
13680
  void 0,
13661
- { at: implMethod.name, covers: unresolved.map((m) => `${m.step}:${m.target}`) }
13681
+ { at: implMethod.name, covers: unresolved.map(unitOf) }
13662
13682
  );
13663
13683
  }
13664
13684
  const declared = /* @__PURE__ */ new Set();
13665
13685
  for (const step of implMethod.narrative) {
13666
13686
  if (step.targetComponent && step.targetMethod) declared.add(`${step.targetComponent}.${step.targetMethod}`);
13667
13687
  }
13688
+ for (const ref of implMethod.calls ?? []) {
13689
+ const parsed = parseDeclaredCall(ref);
13690
+ if (parsed) declared.add(`${parsed.compId}.${parsed.methodName}`);
13691
+ }
13668
13692
  const crossings = /* @__PURE__ */ new Map();
13669
13693
  for (const site of closedCallSites(code, file, fnSymbol, new Set(colocated.keys())) ?? []) {
13670
13694
  const owner = colocated.get(site.name);
@@ -13679,7 +13703,7 @@ var init_call_conformance = __esm({
13679
13703
  ctx.addIssue(
13680
13704
  "warning",
13681
13705
  "UNDECLARED_COLOCATED_CALL",
13682
- `Method "${implMethod.name}" in implementation "${impl.id}": the function "${fnSymbol}" in "${file}" calls ${crossings.size} modelled method(s) of OTHER components living in that same file, and no narrative step declares the call \u2014 ${detail}. Sharing a file does not make the hop internal: it crosses a component boundary nothing imports, so no file-level check can see it. Narrate the call, or move the code so the boundary is real.`,
13706
+ `Method "${implMethod.name}" in implementation "${impl.id}": the function "${fnSymbol}" in "${file}" calls ${crossings.size} modelled method(s) of OTHER components living in that same file, and neither a narrative step nor a declared call names it \u2014 ${detail}. Sharing a file does not make the hop internal: it crosses a component boundary nothing imports, so no file-level check can see it. Narrate the call, declare it in \`calls\`, or move the code so the boundary is real.`,
13683
13707
  impl.id,
13684
13708
  entry.draftContext,
13685
13709
  void 0,