@wairon/cli 5.1.1-dev.35 → 5.1.1-dev.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -65,7 +65,7 @@ var init_defaults = __esm({
65
65
  copilot: ".github/prompts",
66
66
  codex: ".codex/agents"
67
67
  };
68
- WAIRON_VERSION = "5.1.1-dev.35";
68
+ WAIRON_VERSION = "5.1.1-dev.36";
69
69
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
70
70
  SUPPORTED_ALIASES = ["wai"];
71
71
  SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
@@ -2243,13 +2243,34 @@ var init_step_graph = __esm({
2243
2243
  });
2244
2244
 
2245
2245
  // src/models/step-config.ts
2246
+ function stepFieldsFor(type) {
2247
+ const own = STEP_TYPE_FIELDS[type] ?? [];
2248
+ const allowed = /* @__PURE__ */ new Set([...STEP_COMMON_FIELDS, ...own]);
2249
+ for (const field of own) {
2250
+ const twin = STEP_LABEL_TWINS[field];
2251
+ if (twin) allowed.add(twin);
2252
+ }
2253
+ return allowed;
2254
+ }
2255
+ function isSet(step, field) {
2256
+ const v = step[field];
2257
+ if (v === void 0 || v === null) return false;
2258
+ if (Array.isArray(v) && v.length === 0) return false;
2259
+ return true;
2260
+ }
2246
2261
  function flowConfigOn(step) {
2247
- return FLOW_FIELDS.filter((f) => {
2248
- const v = step[f];
2249
- if (v === void 0) return false;
2250
- if (Array.isArray(v) && v.length === 0) return false;
2251
- return true;
2252
- });
2262
+ return FLOW_FIELDS.filter((f) => isSet(step, f));
2263
+ }
2264
+ function narrativeStepForeignFields(step) {
2265
+ const type = step.type;
2266
+ if (!Object.prototype.hasOwnProperty.call(STEP_TYPE_FIELDS, type)) return [];
2267
+ const allowed = stepFieldsFor(type);
2268
+ return Object.keys(step).filter((f) => !allowed.has(f) && isSet(step, f));
2269
+ }
2270
+ function listFields(fields) {
2271
+ const quoted = fields.map((f) => `"${f}"`);
2272
+ if (quoted.length === 1) return quoted[0];
2273
+ return `${quoted.slice(0, -1).join(", ")} and ${quoted[quoted.length - 1]}`;
2253
2274
  }
2254
2275
  function stepConfigVerdict(method2) {
2255
2276
  const steps = method2.narrative ?? [];
@@ -2286,8 +2307,10 @@ function stepConfigVerdict(method2) {
2286
2307
  return i !== void 0 && i + 1 < nums.length ? nums[i + 1] : void 0;
2287
2308
  };
2288
2309
  for (const s of steps) {
2310
+ const alreadyNamed = /* @__PURE__ */ new Set();
2289
2311
  if (s.detach && s.type !== "call" && s.type !== "dispatch") {
2290
2312
  malformed(s.stepNumber, `step ${s.stepNumber} (${s.type}) carries "detach" \u2014 fire-and-forget applies to call/dispatch steps only.`);
2313
+ alreadyNamed.add("detach");
2291
2314
  }
2292
2315
  switch (s.type) {
2293
2316
  case "local":
@@ -2295,7 +2318,10 @@ function stepConfigVerdict(method2) {
2295
2318
  case "register":
2296
2319
  case "dispatch": {
2297
2320
  const extra = flowConfigOn(s);
2298
- if (extra.length) malformed(s.stepNumber, `step ${s.stepNumber} (${s.type}) carries flow config (${extra.join(", ")}) \u2014 use a flow step type instead.`);
2321
+ if (extra.length) {
2322
+ malformed(s.stepNumber, `step ${s.stepNumber} (${s.type}) carries flow config (${extra.join(", ")}) \u2014 use a flow step type instead.`);
2323
+ for (const f of extra) alreadyNamed.add(f);
2324
+ }
2299
2325
  break;
2300
2326
  }
2301
2327
  case "branch":
@@ -2338,6 +2364,14 @@ function stepConfigVerdict(method2) {
2338
2364
  if (s.toStep === void 0) malformed(s.stepNumber, `jump step ${s.stepNumber} requires "toStep".`);
2339
2365
  break;
2340
2366
  }
2367
+ const foreign = narrativeStepForeignFields(s).filter((f) => !alreadyNamed.has(f));
2368
+ if (foreign.length) {
2369
+ problems.push({
2370
+ stepNumber: s.stepNumber,
2371
+ code: "FOREIGN_STEP_FIELD",
2372
+ detail: `step ${s.stepNumber} (${s.type}) carries ${listFields(foreign)}, which a ${s.type} step cannot \u2014 left over from an edit that changed the step's type, configuring nothing. \`wairon doctor --fix\` drops them; give the step the type that carries them instead if that is what it does.`
2373
+ });
2374
+ }
2341
2375
  for (const [field, target] of jumpFieldsOf(s)) {
2342
2376
  if (byNum.has(target)) continue;
2343
2377
  sound = false;
@@ -2363,24 +2397,43 @@ function jumpFieldsOf(s) {
2363
2397
  (s.branches ?? []).forEach((b, i) => fields.push([`branches[${i}].step`, b.step]));
2364
2398
  return fields;
2365
2399
  }
2366
- var FLOW_FIELDS;
2400
+ var STEP_TYPE_FIELDS, STEP_COMMON_FIELDS, STEP_LABEL_TWINS, FLOW_STEP_TYPES, FLOW_FIELDS;
2367
2401
  var init_step_config = __esm({
2368
2402
  "src/models/step-config.ts"() {
2369
2403
  "use strict";
2404
+ STEP_TYPE_FIELDS = {
2405
+ local: [],
2406
+ call: ["targetComponent", "targetMethod", "auth", "detach"],
2407
+ register: ["targetComponent", "targetMethod"],
2408
+ dispatch: ["targetComponent", "capability", "auth", "detach"],
2409
+ branch: ["condition", "onTrueStep", "onFalseStep"],
2410
+ switch: ["on", "cases", "defaultStep"],
2411
+ loop: ["loopKind", "over", "condition", "endStep"],
2412
+ try: ["endStep", "catches", "finallyStep"],
2413
+ parallel: ["endStep", "branches"],
2414
+ jump: ["toStep"],
2415
+ return: ["outcome"],
2416
+ throw: ["error"]
2417
+ };
2418
+ STEP_COMMON_FIELDS = [
2419
+ "stepNumber",
2420
+ "label",
2421
+ "description",
2422
+ "type",
2423
+ "assertsGuarantees",
2424
+ "assertsInvariants"
2425
+ ];
2426
+ STEP_LABEL_TWINS = {
2427
+ onTrueStep: "onTrueLabel",
2428
+ onFalseStep: "onFalseLabel",
2429
+ defaultStep: "defaultLabel",
2430
+ endStep: "endLabel",
2431
+ finallyStep: "finallyLabel",
2432
+ toStep: "toLabel"
2433
+ };
2434
+ FLOW_STEP_TYPES = ["branch", "switch", "loop", "try", "parallel", "jump"];
2370
2435
  FLOW_FIELDS = [
2371
- "condition",
2372
- "onTrueStep",
2373
- "onFalseStep",
2374
- "on",
2375
- "cases",
2376
- "defaultStep",
2377
- "loopKind",
2378
- "over",
2379
- "endStep",
2380
- "catches",
2381
- "finallyStep",
2382
- "branches",
2383
- "toStep"
2436
+ ...new Set(FLOW_STEP_TYPES.flatMap((t) => [...STEP_TYPE_FIELDS[t]]))
2384
2437
  ];
2385
2438
  }
2386
2439
  });
@@ -9798,20 +9851,22 @@ var init_narrative_step_config = __esm({
9798
9851
  init_models();
9799
9852
  narrativeStepConfigRule = {
9800
9853
  name: "narrative-step-config",
9801
- 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.',
9854
+ description: 'The step configuration of L5 narratives: every flow step (branch, switch, loop, try, parallel, jump) carries the config its type requires and no simple step carries flow config, "detach" (fire-and-forget) is legal on call/dispatch steps only, no step carries a field foreign to its type, a parallel body is covered by contiguous, ordered arms (>= 2), no two steps share a step number or a label, and every jump field targets an existing step number in the same narrative.',
9802
9855
  codes: [
9803
9856
  { code: "MALFORMED_FLOW_STEP", defaultSeverity: "error", summary: "Flow step missing required config (or flow config on a local/call step)" },
9804
9857
  { code: "INVALID_STEP_JUMP", defaultSeverity: "error", summary: "Jump field targets a step number that does not exist in the narrative" },
9805
- { code: "DUPLICATE_STEP_LABEL", defaultSeverity: "error", summary: "Two steps in one narrative share a label \u2014 the symbolic anchor later deltas address by" }
9858
+ { code: "DUPLICATE_STEP_LABEL", defaultSeverity: "error", summary: "Two steps in one narrative share a label \u2014 the symbolic anchor later deltas address by" },
9859
+ { code: "FOREIGN_STEP_FIELD", defaultSeverity: "warning", summary: "Step carries a field its own type cannot have \u2014 a leftover of an edit, meaning nothing" }
9806
9860
  ],
9807
9861
  check(ctx) {
9862
+ const severityOf = new Map(narrativeStepConfigRule.codes.map((c) => [c.code, c.defaultSeverity]));
9808
9863
  for (const impl of ctx.implementations) {
9809
9864
  const isDraftCtx = ctx.isImplementationDraft(impl);
9810
9865
  for (const implMethod of impl.methods) {
9811
9866
  if (!implMethod.narrative.length) continue;
9812
9867
  const where = `Method "${implMethod.name}" in implementation "${impl.id}": `;
9813
9868
  for (const problem of stepConfigVerdict(implMethod).problems) {
9814
- ctx.addIssue("error", problem.code, where + problem.detail, impl.id, isDraftCtx);
9869
+ ctx.addIssue(severityOf.get(problem.code) ?? "error", problem.code, where + problem.detail, impl.id, isDraftCtx);
9815
9870
  }
9816
9871
  }
9817
9872
  }
@@ -12402,15 +12457,16 @@ var init_unused_types = __esm({
12402
12457
  init_models();
12403
12458
  unusedTypesRule = {
12404
12459
  name: "unused-types",
12405
- description: "Flags types no field of any type and no interface method signature references. References are matched through the type-reference grammar (generic arguments, collections, qualified ids), and the declaring type's own generic parameters are left out \u2014 a type parameter is not a reference to a type.",
12460
+ description: "Flags types no field of any type, no interface method signature and no other type's method signature references. References are matched through the type-reference grammar (generic arguments, collections, qualified ids), and the declaring type's own generic parameters are left out \u2014 a type parameter is not a reference to a type. A type named only by its OWN methods stays unused, the way a function that only calls itself is.",
12406
12461
  codes: [
12407
- { code: "UNUSED_TYPE", defaultSeverity: "warning", summary: "Type never referenced by fields or signatures" }
12462
+ { code: "UNUSED_TYPE", defaultSeverity: "warning", summary: "Type never referenced by fields, signatures or type methods" }
12408
12463
  ],
12409
12464
  check(ctx) {
12410
12465
  const referencedTypes = /* @__PURE__ */ new Set();
12411
- const markTypeReferenced = (ref) => {
12466
+ const markTypeReferenced = (ref, exceptId) => {
12412
12467
  if (ctx.isBuiltinType(ref)) return;
12413
12468
  for (const spec of ctx.types) {
12469
+ if (spec.id === exceptId) continue;
12414
12470
  if (typeMatchesRef(spec, ref)) {
12415
12471
  referencedTypes.add(spec.id);
12416
12472
  }
@@ -12432,6 +12488,14 @@ var init_unused_types = __esm({
12432
12488
  }
12433
12489
  }
12434
12490
  }
12491
+ for (const t of ctx.types) {
12492
+ for (const m of t.methods) {
12493
+ const refs = methodTypeRefs(m);
12494
+ for (const ref of refs) {
12495
+ markTypeReferenced(ref, t.id);
12496
+ }
12497
+ }
12498
+ }
12435
12499
  for (const t of ctx.types) {
12436
12500
  if (!ctx.isSpecInScope(t.id)) continue;
12437
12501
  if (!referencedTypes.has(t.id)) {
@@ -12440,7 +12504,7 @@ var init_unused_types = __esm({
12440
12504
  ctx.addIssue(
12441
12505
  "warning",
12442
12506
  "UNUSED_TYPE",
12443
- `Type "${t.id}" is defined but never referenced by any type fields or interface methods.`,
12507
+ `Type "${t.id}" is defined but never referenced by any type field, interface method or other type's method.`,
12444
12508
  t.id,
12445
12509
  isDraftCtx
12446
12510
  );
@@ -21868,6 +21932,44 @@ var init_stereotype_migration = __esm({
21868
21932
  }
21869
21933
  });
21870
21934
 
21935
+ // src/core/narrative-repair.ts
21936
+ function repairForeignStepFields(apply) {
21937
+ const implementations = loadImplementationSpecs();
21938
+ const repairs = [];
21939
+ const changed = [];
21940
+ for (const impl of implementations) {
21941
+ let touched = false;
21942
+ for (const method2 of impl.methods) {
21943
+ for (const step of method2.narrative) {
21944
+ const fields = narrativeStepForeignFields(step);
21945
+ if (!fields.length) continue;
21946
+ repairs.push({
21947
+ implementation: impl.id,
21948
+ method: method2.name,
21949
+ stepNumber: step.stepNumber,
21950
+ stepType: step.type,
21951
+ fields
21952
+ });
21953
+ for (const field of fields) delete step[field];
21954
+ touched = true;
21955
+ }
21956
+ }
21957
+ if (touched) changed.push(impl);
21958
+ }
21959
+ if (apply && changed.length) {
21960
+ for (const impl of changed) saveImplementationSpec(impl);
21961
+ invalidateSpecCache();
21962
+ }
21963
+ return repairs;
21964
+ }
21965
+ var init_narrative_repair = __esm({
21966
+ "src/core/narrative-repair.ts"() {
21967
+ "use strict";
21968
+ init_specs2();
21969
+ init_models();
21970
+ }
21971
+ });
21972
+
21871
21973
  // src/core/approval.ts
21872
21974
  function diffSize(d) {
21873
21975
  return d.added.length + d.changed.length + d.removed.length;
@@ -22045,6 +22147,7 @@ var init_core = __esm({
22045
22147
  init_treetransfer();
22046
22148
  init_provision();
22047
22149
  init_stereotype_migration();
22150
+ init_narrative_repair();
22048
22151
  init_approval();
22049
22152
  init_approver();
22050
22153
  init_specs2();
@@ -23577,15 +23680,6 @@ function identityKeyOf(field, item) {
23577
23680
  return str(o.name) ?? str(o.id);
23578
23681
  }
23579
23682
  }
23580
- function stepFieldsFor(type) {
23581
- const own = STEP_TYPE_FIELDS[type] ?? [];
23582
- const allowed = /* @__PURE__ */ new Set([...STEP_COMMON_FIELDS, ...own]);
23583
- for (const field of own) {
23584
- const twin = STEP_LABEL_TWINS[field];
23585
- if (twin) allowed.add(twin);
23586
- }
23587
- return allowed;
23588
- }
23589
23683
  function reportIdentityOf(field, item) {
23590
23684
  if (item !== null && typeof item === "object") {
23591
23685
  const step = item.stepNumber;
@@ -23861,7 +23955,7 @@ function findLegacySpecFiles() {
23861
23955
  function updateSpec(kind, id, delta, hooks) {
23862
23956
  return current().updateSpec(kind, id, delta, hooks);
23863
23957
  }
23864
- var fs17, path23, NO_ROOT_SUBSYSTEMS, SIGNATURE_TTL_MS, STEP_TYPE_FIELDS, STEP_COMMON_FIELDS, STEP_LABEL_TWINS, SpecWorkspace, workspaces;
23958
+ var fs17, path23, NO_ROOT_SUBSYSTEMS, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
23865
23959
  var init_specs2 = __esm({
23866
23960
  "src/core/specs.ts"() {
23867
23961
  "use strict";
@@ -23879,36 +23973,6 @@ var init_specs2 = __esm({
23879
23973
  init_diagram();
23880
23974
  NO_ROOT_SUBSYSTEMS = /* @__PURE__ */ new Set();
23881
23975
  SIGNATURE_TTL_MS = 2e3;
23882
- STEP_TYPE_FIELDS = {
23883
- local: [],
23884
- call: ["targetComponent", "targetMethod", "auth", "detach"],
23885
- register: ["targetComponent", "targetMethod"],
23886
- dispatch: ["targetComponent", "capability", "auth", "detach"],
23887
- branch: ["condition", "onTrueStep", "onFalseStep"],
23888
- switch: ["on", "cases", "defaultStep"],
23889
- loop: ["loopKind", "over", "condition", "endStep"],
23890
- try: ["endStep", "catches", "finallyStep"],
23891
- parallel: ["endStep", "branches"],
23892
- jump: ["toStep"],
23893
- return: ["outcome"],
23894
- throw: ["error"]
23895
- };
23896
- STEP_COMMON_FIELDS = [
23897
- "stepNumber",
23898
- "label",
23899
- "description",
23900
- "type",
23901
- "assertsGuarantees",
23902
- "assertsInvariants"
23903
- ];
23904
- STEP_LABEL_TWINS = {
23905
- onTrueStep: "onTrueLabel",
23906
- onFalseStep: "onFalseLabel",
23907
- defaultStep: "defaultLabel",
23908
- endStep: "endLabel",
23909
- finallyStep: "finallyLabel",
23910
- toStep: "toLabel"
23911
- };
23912
23976
  SpecWorkspace = class {
23913
23977
  constructor(rootDir) {
23914
23978
  this.cachedIndex = null;
@@ -28759,6 +28823,9 @@ function listDirectChainedSubprojects2(projectRoot2) {
28759
28823
  function retireSpecialists2(apply) {
28760
28824
  return retireSpecialists(apply);
28761
28825
  }
28826
+ function repairForeignStepFields2(apply) {
28827
+ return repairForeignStepFields(apply);
28828
+ }
28762
28829
  function defaultPackSelections2() {
28763
28830
  return defaultPackSelections();
28764
28831
  }
@@ -30900,6 +30967,23 @@ async function runDoctor(options = {}) {
30900
30967
  } catch (e) {
30901
30968
  line(tally, "warn", `Could not plan Specialist retirement: ${e instanceof Error ? e.message : String(e)}`);
30902
30969
  }
30970
+ try {
30971
+ const foreign = repairForeignStepFields2(false);
30972
+ if (foreign.length > 0) {
30973
+ console.log(import_chalk12.default.bold("Narrative steps"));
30974
+ for (const repair of foreign) {
30975
+ line(
30976
+ tally,
30977
+ "warn",
30978
+ `${repair.implementation}.${repair.method} step ${repair.stepNumber} (${repair.stepType}) carries ${repair.fields.join(", ")} \u2014 a ${repair.stepType} step cannot.`
30979
+ );
30980
+ }
30981
+ line(tally, "warn", "Run `wairon doctor --fix` to drop them (or give each step the type that carries them).");
30982
+ logger.blank();
30983
+ }
30984
+ } catch (e) {
30985
+ line(tally, "warn", `Could not plan the narrative-step repair: ${e instanceof Error ? e.message : String(e)}`);
30986
+ }
30903
30987
  }
30904
30988
  console.log(import_chalk12.default.bold(`Generated files ${import_chalk12.default.gray("(stale = older than installed)")}`));
30905
30989
  for (const [label, p] of [
@@ -31092,6 +31176,17 @@ async function applyFixes() {
31092
31176
  } catch (e) {
31093
31177
  console.log(` ${icon("error")} Specialist retirement failed: ${e instanceof Error ? e.message : String(e)}`);
31094
31178
  }
31179
+ try {
31180
+ const repairs = repairForeignStepFields2(true);
31181
+ if (repairs.length > 0) {
31182
+ console.log(` ${icon("ok")} Dropped foreign fields from ${repairs.length} narrative step(s):`);
31183
+ for (const repair of repairs) {
31184
+ console.log(` ${repair.implementation}.${repair.method} step ${repair.stepNumber} (${repair.stepType}): ${repair.fields.join(", ")}`);
31185
+ }
31186
+ }
31187
+ } catch (e) {
31188
+ console.log(` ${icon("error")} Narrative-step repair failed: ${e instanceof Error ? e.message : String(e)}`);
31189
+ }
31095
31190
  const { runMcpInstall: runMcpInstall2 } = (init_mcp(), __toCommonJS(mcp_exports));
31096
31191
  if (targets.includes("claude")) {
31097
31192
  try {