@wairon/cli 5.1.1-dev.95 → 5.1.1-dev.96

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.95";
68
+ WAIRON_VERSION = "5.1.1-dev.96";
69
69
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
70
70
  SUPPORTED_ALIASES = ["wai"];
71
71
  SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
@@ -651,6 +651,14 @@ var init_agent = __esm({
651
651
  instructions: import_zod2.z.string(),
652
652
  /** Rendered variant guidance, also folded into instructions */
653
653
  variantGuidance: import_zod2.z.string().optional(),
654
+ /**
655
+ * How the neutral types of the contracts this agent implements are spelled
656
+ * in its language: the write table (type_dialect.mappingLines) of the dialect
657
+ * for the language its implementations are written in, also folded into
658
+ * instructions. Absent when no dialect is shipped for that language or the
659
+ * agent implements nothing.
660
+ */
661
+ typeMapping: import_zod2.z.array(import_zod2.z.string()).optional(),
654
662
  /**
655
663
  * Set only on a brief for a member's agent (an id qualified `<alias>::`): the
656
664
  * member's directory relative to the root the brief was asked from. The
@@ -1658,14 +1666,16 @@ function locateScalar(tokens, path92) {
1658
1666
  const base = Math.min(...region.map((t) => t.col));
1659
1667
  const last = k === path92.length - 1;
1660
1668
  let at;
1661
- if (typeof segment === "string") {
1669
+ const items = region.map((t, i) => ({ t, i })).filter(({ t }) => t.kind === "item" && t.col === base);
1670
+ if (items.length === 0) {
1662
1671
  at = region.findIndex((t) => t.kind === "key" && t.col === base && t.name === segment);
1663
1672
  if (at < 0) return `no block key "${segment}" at ${path92.slice(0, k).join(".") || "the document root"}`;
1664
1673
  if (last) return region[at].valueStart !== void 0 ? region[at] : `"${segment}" holds no inline scalar`;
1665
1674
  } else {
1666
- const items = region.map((t, i) => ({ t, i })).filter(({ t }) => t.kind === "item" && t.col === base);
1667
- if (segment >= items.length) return `no block sequence item ${segment} at ${path92.slice(0, k).join(".")}`;
1668
- at = items[segment].i;
1675
+ if (!/^\d+$/.test(segment)) return `"${segment}" is not a sequence index at ${path92.slice(0, k).join(".") || "the document root"}`;
1676
+ const index = Number(segment);
1677
+ if (index >= items.length) return `no block sequence item ${segment} at ${path92.slice(0, k).join(".")}`;
1678
+ at = items[index].i;
1669
1679
  if (last) {
1670
1680
  const value = region[at + 1];
1671
1681
  return value && value.kind === "scalar" && value.line === region[at].line ? value : `item ${segment} holds no inline scalar`;
@@ -1994,7 +2004,7 @@ function registryOver(store, root) {
1994
2004
  const at = typeof entry.at === "string" ? entry.at : void 0;
1995
2005
  const covers = Array.isArray(entry.covers) ? entry.covers.filter((u) => typeof u === "string") : void 0;
1996
2006
  const next = rekeyAnchor(rename, { spec: entry.spec, at, covers });
1997
- const base = ["rules", "conformance", "carried", g, "findings", f];
2007
+ const base = ["rules", "conformance", "carried", String(g), "findings", String(f)];
1998
2008
  const named2 = { code: String(entry.code), spec: entry.spec, at: at ?? "" };
1999
2009
  const change = (path92, field, from, to) => {
2000
2010
  if (from === to) return;
@@ -2003,7 +2013,7 @@ function registryOver(store, root) {
2003
2013
  };
2004
2014
  change(["spec"], "spec", entry.spec, next.spec);
2005
2015
  if (at !== void 0) change(["at"], "at", at, next.at);
2006
- (covers ?? []).forEach((unit, u) => change(["covers", u], "covers", unit, next.covers[u]));
2016
+ (covers ?? []).forEach((unit, u) => change(["covers", String(u)], "covers", unit, next.covers[u]));
2007
2017
  });
2008
2018
  });
2009
2019
  store.rewriteScalars(edits, dryRun);
@@ -3304,7 +3314,7 @@ function relationHealth(status2) {
3304
3314
  if (status2.drifted) return "drifted";
3305
3315
  return "ok";
3306
3316
  }
3307
- var import_zod8, SpecIdSchema, SpecStatusSchema, BoundaryItemSchema, RequirementItemSchema, DatabaseSpecSchema, DiagramConfigSchema, SURFACE_AUDIENCES, SurfaceAudienceSchema, SystemPublicInterfaceSchema, SystemSpecSchema, PublicInterfaceTypeSchema, PublicInterfaceSchema, TrustedLinkSchema, LintAllowSchema, LintConfigSchema, ExtDataSchema, LifecycleEntrypointSchema, SubsystemSpecSchema, ComponentTypeSchema, PATTERN_TYPES, RETIRED_STEREOTYPES, PortalTypeSchema, DispatchBindingSchema, DurabilitySchema, DependencyClassSchema, PatternRefSchema, EventBindingSchema, ExternalLinkTypeSchema, ExternalLinkSchema, PortalAuthSchemeSchema, PortalAuthSchema, PortalMountSchema, ComponentSpecSchema, BLOCK_NOUNS, HttpMethodSchema, TransportSchema, EndpointSchema, SEMANTIC_GUARANTEES, GuaranteeSchema, MethodParamSchema, FindingDeclarationSchema, MethodSignatureSchema, StoredMethodSignatureSchema, ResolvedMethodSignatureSchema, INTENT_FLOOR_MIN_CHARS, InterfaceSpecSchema, ResolvedInterfaceSpecSchema, NarrativeStepTypeSchema, LoopKindSchema, SwitchCaseSchema, CatchClauseSchema, ParallelBranchSchema, NarrativeStepSchema, NarrativeDetailSchema, ConformanceTierSchema, MethodImplementationSchema, TechnologySchema, ImplementationSpecSchema, TypeKindSchema, TypeFieldSchema, TypeMethodSchema, ResolvedTypeMethodSchema, StoredTypeMethodSchema, InvariantSchema, TypeSpecSchema, ResolvedTypeSpecSchema, SurfaceOriginSchema, SurfaceTypeDefSchema, SurfaceTypeExportSchema, SurfaceContractEntrySchema, SurfaceSnapshotSchema, ExternalLockEntrySchema, ExternalsLockSchema, ParentExcerptSchema, NamedOpenApiSpecSchema, GroupSpecSchema;
3317
+ var import_zod8, SpecIdSchema, SpecStatusSchema, BoundaryItemSchema, RequirementItemSchema, DatabaseSpecSchema, DiagramConfigSchema, SURFACE_AUDIENCES, SurfaceAudienceSchema, SystemPublicInterfaceSchema, SystemSpecSchema, PublicInterfaceTypeSchema, PublicInterfaceSchema, TrustedLinkSchema, LintAllowSchema, LintConfigSchema, ExtDataSchema, LifecycleEntrypointSchema, SubsystemSpecSchema, ComponentTypeSchema, PATTERN_TYPES, RETIRED_STEREOTYPES, PortalTypeSchema, DispatchBindingSchema, DurabilitySchema, DependencyClassSchema, PatternRefSchema, EventBindingSchema, ExternalLinkTypeSchema, ExternalLinkSchema, PortalAuthSchemeSchema, PortalAuthSchema, PortalMountSchema, ComponentSpecSchema, BLOCK_NOUNS, HttpMethodSchema, TransportSchema, EndpointSchema, SEMANTIC_GUARANTEES, GuaranteeSchema, MethodParamSchema, FindingDeclarationSchema, MethodSignatureSchema, StoredMethodSignatureSchema, ResolvedMethodSignatureSchema, INTENT_FLOOR_MIN_CHARS, InterfaceSpecSchema, ResolvedInterfaceSpecSchema, NarrativeStepTypeSchema, LoopKindSchema, SwitchCaseSchema, CatchClauseSchema, ParallelBranchSchema, NarrativeStepSchema, NarrativeDetailSchema, ConformanceTierSchema, MethodImplementationSchema, TechnologySchema, ImplementationSpecSchema, TypeKindSchema, TypeFieldSchema, TypeMethodSchema, ResolvedTypeMethodSchema, StoredTypeMethodSchema, InvariantSchema, EnumValueSchema, TypeSpecSchema, ResolvedTypeSpecSchema, SurfaceOriginSchema, SurfaceTypeDefSchema, SurfaceTypeExportSchema, SurfaceContractEntrySchema, SurfaceSnapshotSchema, ExternalLockEntrySchema, ExternalsLockSchema, ParentExcerptSchema, NamedOpenApiSpecSchema, GroupSpecSchema;
3308
3318
  var init_specs = __esm({
3309
3319
  "src/models/specs.ts"() {
3310
3320
  "use strict";
@@ -3681,11 +3691,10 @@ var init_specs = __esm({
3681
3691
  MethodParamSchema = import_zod8.z.object({
3682
3692
  name: import_zod8.z.string(),
3683
3693
  /**
3684
- * A primitive/builtin or a defined type id (qualified across subsystems, e.g.
3685
- * "billing.Invoice"), on its own or inside a generic, an array or a UNION:
3686
- * `Invoice | null`, `Promise<Invoice | null>`, `Invoice[] | null`. Every
3687
- * identifier the string names has to resolve — see the grammar on
3688
- * src/models/type-references.ts.
3694
+ * A type expression in the neutral type grammar (src/models/type-grammar.ts):
3695
+ * a primitive, a named type (`billing.Invoice`, `alias::name`), `list<T>`,
3696
+ * `set<T>`, `map<K, V>`, `T?`, a union of named types. Stored canonical; an
3697
+ * alias (`Invoice[]`, `Invoice | null`, `boolean`) is normalised on write.
3689
3698
  */
3690
3699
  type: import_zod8.z.string(),
3691
3700
  description: import_zod8.z.string().optional(),
@@ -3704,8 +3713,9 @@ var init_specs = __esm({
3704
3713
  description: import_zod8.z.string(),
3705
3714
  signature: import_zod8.z.string().optional(),
3706
3715
  // e.g. "save(key: string, data: Buffer): Promise<void>"
3707
- // e.g. "Promise<void>", or a union: "Invoice | null" — the commonest shape in
3708
- // any real tree. See the grammar on src/models/type-references.ts.
3716
+ // A type expression in the neutral grammar, at the returns position — the only
3717
+ // one `async T` and `void` may stand in: "async void", "Invoice?". Stored
3718
+ // canonical; see src/models/type-grammar.ts.
3709
3719
  returns: import_zod8.z.string().optional(),
3710
3720
  /** Structured parameters (authoritative for type checking when present). */
3711
3721
  params: import_zod8.z.array(MethodParamSchema).optional(),
@@ -4069,12 +4079,12 @@ var init_specs = __esm({
4069
4079
  createdAt: import_zod8.z.string().datetime(),
4070
4080
  updatedAt: import_zod8.z.string().datetime()
4071
4081
  });
4072
- TypeKindSchema = import_zod8.z.enum(["entity", "value-object", "signature"]);
4082
+ TypeKindSchema = import_zod8.z.enum(["entity", "value-object", "signature", "enum"]);
4073
4083
  TypeFieldSchema = import_zod8.z.object({
4074
4084
  name: import_zod8.z.string(),
4075
- // A primitive, or another type id (qualified across subsystems, e.g.
4076
- // "billing.Invoice") — on its own or inside a generic, an array or a union
4077
- // ("Invoice[] | null"). See the grammar on src/models/type-references.ts.
4085
+ // A type expression in the neutral grammar (src/models/type-grammar.ts): a
4086
+ // primitive, a named type ("billing.Invoice"), list/set/map, `T?`, a union
4087
+ // of named types. Stored canonical.
4078
4088
  type: import_zod8.z.string(),
4079
4089
  description: import_zod8.z.string().optional(),
4080
4090
  optional: import_zod8.z.boolean().default(false),
@@ -4128,9 +4138,13 @@ var init_specs = __esm({
4128
4138
  /** The property that must hold, stated precisely enough to test against. */
4129
4139
  description: import_zod8.z.string().min(1)
4130
4140
  });
4141
+ EnumValueSchema = import_zod8.z.object({
4142
+ name: import_zod8.z.string().min(1),
4143
+ description: import_zod8.z.string().optional()
4144
+ });
4131
4145
  TypeSpecSchema = import_zod8.z.object({
4132
4146
  kind: TypeKindSchema,
4133
- // discriminator — entity | value-object | signature
4147
+ // discriminator — entity | value-object | signature | enum
4134
4148
  id: SpecIdSchema,
4135
4149
  name: import_zod8.z.string(),
4136
4150
  description: import_zod8.z.string().optional(),
@@ -4193,6 +4207,20 @@ var init_specs = __esm({
4193
4207
  params: import_zod8.z.array(MethodParamSchema).optional(),
4194
4208
  /** A signature's one output type; required on kind signature and only there (SIGNATURE_TYPE_MEMBERS). */
4195
4209
  returns: import_zod8.z.string().optional(),
4210
+ /**
4211
+ * An enum's values, in declared order (the order is part of the design),
4212
+ * unique by nameKey; required and non-empty on kind enum and only there
4213
+ * (ENUM_MEMBERS).
4214
+ */
4215
+ values: import_zod8.z.array(EnumValueSchema).optional(),
4216
+ /**
4217
+ * A named scalar's one primitive (string, int, float, bool, bytes, date,
4218
+ * datetime or duration), in place of fields: a value-object that is a
4219
+ * newtype or type alias in every language. Read as a type position (the
4220
+ * grammar's `holds` position); only on a value-object and never beside
4221
+ * fields (NAMED_SCALAR_MEMBERS).
4222
+ */
4223
+ holds: import_zod8.z.string().optional(),
4196
4224
  createdAt: import_zod8.z.string().datetime(),
4197
4225
  updatedAt: import_zod8.z.string().datetime()
4198
4226
  });
@@ -4218,7 +4246,11 @@ var init_specs = __esm({
4218
4246
  optional: import_zod8.z.boolean().optional()
4219
4247
  })).optional(),
4220
4248
  /** A signature's one output type; only on kind signature. */
4221
- returns: import_zod8.z.string().optional()
4249
+ returns: import_zod8.z.string().optional(),
4250
+ /** An enum's values in declared order; only on kind enum. */
4251
+ values: import_zod8.z.array(EnumValueSchema).optional(),
4252
+ /** A named scalar's one primitive, in place of fields; only on a value-object that declares it. */
4253
+ holds: import_zod8.z.string().optional()
4222
4254
  });
4223
4255
  SurfaceTypeExportSchema = import_zod8.z.object({
4224
4256
  /** The type's public name in the producer's export table. */
@@ -4323,9 +4355,773 @@ var init_specs = __esm({
4323
4355
  }
4324
4356
  });
4325
4357
 
4358
+ // src/models/type-grammar.ts
4359
+ function isTypeVocabulary(name) {
4360
+ const lower = name.toLowerCase();
4361
+ return LOWER_PRIMITIVE.has(lower) || LOWER_GENERIC.has(lower) || NOT_NEUTRAL.has(lower) || NONES.has(lower) || lower === "true" || lower === "false" || lower === "number";
4362
+ }
4363
+ function primitiveOf(name) {
4364
+ if (PRIMITIVES.has(name)) return name;
4365
+ const exact = PRIMITIVE_ALIASES.get(name);
4366
+ if (exact) return exact;
4367
+ return LOWER_PRIMITIVE.get(name.toLowerCase());
4368
+ }
4369
+ function genericOf(name) {
4370
+ return GENERIC_ALIASES.get(name) ?? LOWER_GENERIC.get(name.toLowerCase());
4371
+ }
4372
+ function tokenize2(text3) {
4373
+ const out = [];
4374
+ let i = 0;
4375
+ while (i < text3.length) {
4376
+ const c = text3[i];
4377
+ if (/\s/.test(c)) {
4378
+ i++;
4379
+ continue;
4380
+ }
4381
+ if (/[A-Za-z_$]/.test(c)) {
4382
+ let j = i + 1;
4383
+ while (j < text3.length && /[A-Za-z0-9_$-]/.test(text3[j])) j++;
4384
+ while (text3[j - 1] === "-") j--;
4385
+ out.push({ t: "id", v: text3.slice(i, j) });
4386
+ i = j;
4387
+ continue;
4388
+ }
4389
+ if (/[0-9]/.test(c)) {
4390
+ let j = i + 1;
4391
+ while (j < text3.length && /[0-9.]/.test(text3[j])) j++;
4392
+ out.push({ t: "num", v: text3.slice(i, j) });
4393
+ i = j;
4394
+ continue;
4395
+ }
4396
+ if (c === "'" || c === '"' || c === "`") {
4397
+ let j = i + 1;
4398
+ while (j < text3.length && text3[j] !== c) j += text3[j] === "\\" ? 2 : 1;
4399
+ if (j >= text3.length) throw new TypeSyntaxError(`an unterminated string literal at "${text3.slice(i)}"`);
4400
+ out.push({ t: "str", v: text3.slice(i, j + 1) });
4401
+ i = j + 1;
4402
+ continue;
4403
+ }
4404
+ const punct = PUNCTUATION.find((p) => text3.startsWith(p, i));
4405
+ if (!punct) throw new TypeSyntaxError(`"${c}" is not part of any type spelling`);
4406
+ out.push({ t: "p", v: punct });
4407
+ i += punct.length;
4408
+ }
4409
+ return out;
4410
+ }
4411
+ function readRaw(text3) {
4412
+ return new RawParser(tokenize2(text3)).position();
4413
+ }
4414
+ function primitive(name) {
4415
+ return { form: "primitive", name, args: [] };
4416
+ }
4417
+ function optionalOf(inner2) {
4418
+ if (inner2.form === "primitive" && inner2.name === "any") return inner2;
4419
+ if (inner2.form === "optional") throw new TypeProblemError("TYPE_POSITION_INVALID", "a type is made optional twice (T??)", "T?");
4420
+ if (inner2.form === "primitive" && inner2.name === "void") throw new TypeProblemError("TYPE_POSITION_INVALID", "void cannot be optional (void?)", "void");
4421
+ return { form: "optional", args: [inner2] };
4422
+ }
4423
+ function inner(ctx) {
4424
+ return { ...ctx, top: false, voidAllowed: false, omittable: false };
4425
+ }
4426
+ function normalise(node, ctx, notes) {
4427
+ switch (node.k) {
4428
+ case "paren":
4429
+ return normalise(node.inner, ctx, notes);
4430
+ case "async":
4431
+ return asyncOf(node.inner, ctx, notes, "async");
4432
+ case "array":
4433
+ return { form: "list", args: [normalise(node.elem, inner(ctx), notes)] };
4434
+ case "opt":
4435
+ return optionalOf(normalise(node.inner, inner(ctx), notes));
4436
+ case "union":
4437
+ return unionOf(node.members, ctx, notes);
4438
+ case "name":
4439
+ return node.args === void 0 ? bareName(node.name, ctx, notes) : appliedName(node.name, node.args, ctx, notes);
4440
+ case "literal":
4441
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", `${node.text} is a literal value, not a type \u2014 a literal union is not in the grammar`, "an enum type naming the values");
4442
+ case "object":
4443
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "an inline object shape is not in the grammar", "a named value-object");
4444
+ case "function":
4445
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "an inline function type is not in the grammar", "a signature type");
4446
+ case "intersection":
4447
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "an intersection (A & B) is not in the grammar", "a named type");
4448
+ case "tuple":
4449
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "a tuple is not in the grammar", "a named value-object");
4450
+ case "operator":
4451
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", `"${node.op}" is TypeScript's type algebra, not in the grammar`, "a named type");
4452
+ }
4453
+ }
4454
+ function asyncOf(innerNode, ctx, notes, spelled) {
4455
+ if (!ctx.top || !RETURNS_POSITIONS.has(ctx.position)) {
4456
+ throw new TypeProblemError("TYPE_POSITION_INVALID", `${spelled} may stand only at the top of a returns, not in a ${ctx.position}`);
4457
+ }
4458
+ const read2 = normalise(innerNode, { ...inner(ctx), voidAllowed: true }, notes);
4459
+ return { form: "async", args: [read2] };
4460
+ }
4461
+ function bareName(name, ctx, notes) {
4462
+ const lower = name.toLowerCase();
4463
+ if (NONES.has(name)) {
4464
+ throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" on its own names no type \u2014 "no value" joins a type as T?`, "T?, or void for a returns that answers nothing");
4465
+ }
4466
+ if (lower === "true" || lower === "false") {
4467
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", `${name} is a literal value, not a type`, "bool, or an enum type naming the values");
4468
+ }
4469
+ if (name === "number" || lower === "number") {
4470
+ if (ctx.looseNumber) return primitive(LOOSE_NUMBER);
4471
+ notes.notNeutral ??= { detail: '"number" does not say whether it holds an integer \u2014 int or float?', replacement: "int or float" };
4472
+ return primitive("float");
4473
+ }
4474
+ const prim = primitiveOf(name);
4475
+ if (prim !== void 0) {
4476
+ if (prim === "void" && !ctx.voidAllowed) {
4477
+ throw new TypeProblemError("TYPE_POSITION_INVALID", `void may stand only as a whole returns (or async void), not in a ${ctx.top ? ctx.position : "collection, union or argument"}`);
4478
+ }
4479
+ return primitive(prim);
4480
+ }
4481
+ if (genericOf(name) !== void 0) {
4482
+ throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" needs its type arguments`, `${genericOf(name)?.form ?? "list"}<...>`);
4483
+ }
4484
+ const legacy = NOT_NEUTRAL.get(lower);
4485
+ if (legacy !== void 0) {
4486
+ notes.notNeutral ??= { detail: `"${name}" has no neutral meaning`, replacement: legacy };
4487
+ return primitive("any");
4488
+ }
4489
+ return { form: "named", name, args: [] };
4490
+ }
4491
+ function appliedName(name, args, ctx, notes) {
4492
+ const alias = genericOf(name);
4493
+ if (alias !== void 0) {
4494
+ if (args.length !== alias.arity) {
4495
+ throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" takes ${alias.arity} type argument${alias.arity === 1 ? "" : "s"}, ${args.length} given`);
4496
+ }
4497
+ if (alias.form === "async") return asyncOf(args[0], ctx, notes, `${name}<...>`);
4498
+ if (alias.form === "optional") return optionalOf(normalise(args[0], inner(ctx), notes));
4499
+ const read2 = args.map((a) => normalise(a, inner(ctx), notes));
4500
+ if (alias.form === "map") assertMapKey(read2[0], args[0]);
4501
+ return { form: alias.form, args: read2 };
4502
+ }
4503
+ if (UTILITY_TYPES.has(name)) {
4504
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", `"${name}<...>" is a TypeScript utility type, not in the grammar`, "a named type");
4505
+ }
4506
+ if (primitiveOf(name) !== void 0 || NONES.has(name)) {
4507
+ throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" takes no type arguments`);
4508
+ }
4509
+ const legacy = NOT_NEUTRAL.get(name.toLowerCase());
4510
+ if (legacy !== void 0) {
4511
+ args.forEach((a) => normalise(a, { ...inner(ctx), voidAllowed: true }, notes));
4512
+ notes.notNeutral ??= { detail: `"${name}<...>" has no neutral meaning`, replacement: legacy };
4513
+ return primitive("any");
4514
+ }
4515
+ return { form: "applied", name, args: args.map((a) => normalise(a, inner(ctx), notes)) };
4516
+ }
4517
+ function assertHoldable(expression) {
4518
+ if (expression.form === "primitive" && HOLDABLE_PRIMITIVES.includes(expression.name ?? "")) return;
4519
+ throw new TypeProblemError(
4520
+ "TYPE_POSITION_INVALID",
4521
+ `a named scalar holds one primitive (${HOLDABLE_PRIMITIVES.join(", ")}), not ${canonicalTypeText(expression)}`,
4522
+ "one of those primitives, or fields for a value-object that holds more than one value"
4523
+ );
4524
+ }
4525
+ function assertMapKey(key, written) {
4526
+ if (key.form === "named") return;
4527
+ if (key.form === "primitive" && (key.name === "string" || key.name === "int" || key.name === LOOSE_NUMBER)) return;
4528
+ if (key.form === "primitive" && key.name === "float" && written.k === "name" && written.name.toLowerCase() === "number") return;
4529
+ throw new TypeProblemError("TYPE_POSITION_INVALID", "a map key must be string, int or an enum", "map<string, V>");
4530
+ }
4531
+ function unionOf(members, ctx, notes) {
4532
+ const nones = members.filter((m) => m.k === "name" && m.args === void 0 && NONES.has(m.name));
4533
+ const rest = members.filter((m) => !(m.k === "name" && m.args === void 0 && NONES.has(m.name)));
4534
+ if (rest.length === 0) throw new TypeProblemError("TYPE_EXPRESSION_INVALID", "a union of nones names no type");
4535
+ if (rest.some((m) => m.k === "literal" || m.k === "name" && m.args === void 0 && /^(true|false)$/i.test(m.name))) {
4536
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "a union of literal values is not in the grammar", "an enum type naming the values");
4537
+ }
4538
+ const memberCtx = inner(ctx);
4539
+ let hasNone = nones.length > 0;
4540
+ const read2 = rest.map((m) => {
4541
+ const e = normalise(m, memberCtx, notes);
4542
+ if (e.form !== "optional") return e;
4543
+ hasNone = true;
4544
+ return e.args[0];
4545
+ });
4546
+ let base;
4547
+ if (read2.length === 1) {
4548
+ base = read2[0];
4549
+ } else {
4550
+ const loose = read2.find((e) => e.form !== "named" && e.form !== "applied");
4551
+ if (loose) {
4552
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "a union may hold only named types \u2014 this one mixes in a primitive or a collection", "a named type (an entity, value-object or enum), or two params");
4553
+ }
4554
+ base = { form: "union", args: read2 };
4555
+ }
4556
+ if (!hasNone) return base;
4557
+ const onlyUndefined = nones.length > 0 && nones.every((n) => n.name === "undefined") && read2.length === rest.length && !rest.some((m) => m.k === "opt");
4558
+ if (ctx.omittable && ctx.top && onlyUndefined) return base;
4559
+ return optionalOf(base);
4560
+ }
4561
+ function canonicalTypeText(expr) {
4562
+ switch (expr.form) {
4563
+ case "primitive":
4564
+ case "named":
4565
+ return expr.name ?? "";
4566
+ case "list":
4567
+ case "set":
4568
+ return `${expr.form}<${canonicalTypeText(expr.args[0])}>`;
4569
+ case "map":
4570
+ return `map<${canonicalTypeText(expr.args[0])}, ${canonicalTypeText(expr.args[1])}>`;
4571
+ case "optional": {
4572
+ const innerText = canonicalTypeText(expr.args[0]);
4573
+ return expr.args[0].form === "union" ? `(${innerText})?` : `${innerText}?`;
4574
+ }
4575
+ case "union":
4576
+ return expr.args.map(canonicalTypeText).join(" | ");
4577
+ case "async":
4578
+ return `async ${canonicalTypeText(expr.args[0])}`;
4579
+ case "applied":
4580
+ return `${expr.name}<${expr.args.map(canonicalTypeText).join(", ")}>`;
4581
+ }
4582
+ }
4583
+ function typeNamedRefs(expr) {
4584
+ if (expr.form === "named") return [expr.name];
4585
+ const own = expr.form === "applied" ? [expr.name] : [];
4586
+ return [...own, ...expr.args.flatMap(typeNamedRefs)];
4587
+ }
4588
+ function typeIsMany(expr) {
4589
+ if (expr.form === "list" || expr.form === "set" || expr.form === "map") return true;
4590
+ if (expr.form === "optional" || expr.form === "async") return typeIsMany(expr.args[0]);
4591
+ return false;
4592
+ }
4593
+ function contextFor(position, omittable2, looseNumber = false) {
4594
+ return { position, top: true, voidAllowed: RETURNS_POSITIONS.has(position), omittable: omittable2, looseNumber };
4595
+ }
4596
+ function parseTypePosition(text3, position, omittable2 = false) {
4597
+ let raw;
4598
+ try {
4599
+ raw = readRaw(text3);
4600
+ } catch (e) {
4601
+ if (!(e instanceof TypeSyntaxError)) throw e;
4602
+ return { expression: null, canonical: text3, problem: { code: "TYPE_EXPRESSION_INVALID", written: text3, detail: `"${text3}" does not parse as a type: ${e.message}` } };
4603
+ }
4604
+ const notes = {};
4605
+ try {
4606
+ const expression = normalise(raw, contextFor(position, omittable2), notes);
4607
+ if (position === "holds") assertHoldable(expression);
4608
+ const problem = notes.notNeutral ? { code: "TYPE_NOT_NEUTRAL", written: text3, detail: notes.notNeutral.detail, replacement: notes.notNeutral.replacement } : null;
4609
+ return { expression, canonical: canonicalTypeText(expression), problem };
4610
+ } catch (e) {
4611
+ if (!(e instanceof TypeProblemError)) throw e;
4612
+ return {
4613
+ expression: null,
4614
+ canonical: text3,
4615
+ problem: { code: e.code, written: text3, detail: `"${text3}": ${e.detail}`, ...e.replacement ? { replacement: e.replacement } : {} }
4616
+ };
4617
+ }
4618
+ }
4619
+ function parseTypeExpression(text3, position) {
4620
+ return parseTypePosition(text3, position);
4621
+ }
4622
+ function readCodeAnnotation(annotation, looseNumber) {
4623
+ let raw;
4624
+ try {
4625
+ raw = readRaw(annotation);
4626
+ } catch (e) {
4627
+ if (e instanceof TypeSyntaxError) return null;
4628
+ throw e;
4629
+ }
4630
+ try {
4631
+ return normalise(raw, contextFor("returns", false, looseNumber), {});
4632
+ } catch (e) {
4633
+ if (e instanceof TypeProblemError) return null;
4634
+ throw e;
4635
+ }
4636
+ }
4637
+ function writtenTypeRefs(text3, position) {
4638
+ const parse = parseTypePosition(text3, position);
4639
+ if (parse.expression && !parse.problem) return typeNamedRefs(parse.expression);
4640
+ try {
4641
+ return rawRefs(readRaw(text3));
4642
+ } catch (e) {
4643
+ if (e instanceof TypeSyntaxError) return [];
4644
+ throw e;
4645
+ }
4646
+ }
4647
+ function rawRefs(node) {
4648
+ switch (node.k) {
4649
+ case "name": {
4650
+ const own = isTypeVocabulary(node.name) || UTILITY_TYPES.has(node.name) ? [] : [node.name];
4651
+ return [...own, ...(node.args ?? []).flatMap(rawRefs)];
4652
+ }
4653
+ case "array":
4654
+ return rawRefs(node.elem);
4655
+ case "opt":
4656
+ case "paren":
4657
+ case "async":
4658
+ case "operator":
4659
+ return rawRefs(node.inner);
4660
+ case "union":
4661
+ case "intersection":
4662
+ case "object":
4663
+ return node.members.flatMap(rawRefs);
4664
+ case "tuple":
4665
+ return node.elems.flatMap(rawRefs);
4666
+ case "function":
4667
+ return [...node.params.flatMap(rawRefs), ...rawRefs(node.ret)];
4668
+ case "literal":
4669
+ return [];
4670
+ }
4671
+ }
4672
+ function interfaceCanonicalTypes(intf) {
4673
+ const reader = new Canonicalizer(intf.id, "interface");
4674
+ const methods = (intf.methods ?? []).map((m) => {
4675
+ if (m.signatureFrom !== void 0) return m;
4676
+ const out = { ...m };
4677
+ if (m.params !== void 0) out.params = reader.params(m.params, "param", `methods.${m.name}.params`);
4678
+ if (typeof m.returns === "string") out.returns = reader.read(m.returns, "returns", `methods.${m.name}.returns`);
4679
+ return out;
4680
+ });
4681
+ return { spec: { ...intf, methods }, respellings: reader.respellings, problems: reader.problems };
4682
+ }
4683
+ function typeCanonicalTypes(type) {
4684
+ const reader = new Canonicalizer(type.id, "type");
4685
+ const out = { ...type };
4686
+ if (Array.isArray(type.fields)) {
4687
+ out.fields = type.fields.map((f) => ({ ...f, type: reader.read(f.type, "field", `fields.${f.name}`, !!f.optional) }));
4688
+ }
4689
+ if (Array.isArray(type.methods)) {
4690
+ out.methods = type.methods.map((m) => {
4691
+ const method2 = { ...m };
4692
+ if (m.params !== void 0) method2.params = reader.params(m.params, "type-method-param", `methods.${m.name}.params`);
4693
+ if (typeof m.returns === "string") method2.returns = reader.read(m.returns, "type-method-returns", `methods.${m.name}.returns`);
4694
+ return method2;
4695
+ });
4696
+ }
4697
+ if (type.params !== void 0) out.params = reader.params(type.params, "signature-param", "params");
4698
+ if (typeof type.holds === "string") out.holds = reader.read(type.holds, "holds", "holds");
4699
+ if (typeof type.returns === "string") out.returns = reader.read(type.returns, "signature-returns", "returns");
4700
+ return { spec: out, respellings: reader.respellings, problems: reader.problems };
4701
+ }
4702
+ function namesAnInteger(name) {
4703
+ const words2 = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[\s_\-.]+/).filter(Boolean);
4704
+ return words2.some((word) => INTEGER_WORDS.has(word));
4705
+ }
4706
+ function positionNameOf(path92) {
4707
+ const segments = path92.split(".");
4708
+ const last = segments[segments.length - 1];
4709
+ return last === "returns" && segments.length >= 3 ? segments[segments.length - 2] : last;
4710
+ }
4711
+ function typeProblemIntProposal(problem) {
4712
+ if (problem.code !== "TYPE_NOT_NEUTRAL" || problem.replacement !== "int or float") return null;
4713
+ if (problem.specId === void 0 || problem.kind === void 0 || problem.path === void 0) return null;
4714
+ if (!namesAnInteger(positionNameOf(problem.path))) return null;
4715
+ const parse = parseTypePosition(problem.written.replace(NUMBER_NAME, "int"), "returns");
4716
+ if (!parse.expression || parse.problem) return null;
4717
+ return { specId: problem.specId, kind: problem.kind, path: problem.path, written: problem.written, stored: parse.canonical };
4718
+ }
4719
+ function emptyTypeSpellingFacts() {
4720
+ return { respellings: [], problems: [] };
4721
+ }
4722
+ var PRIMITIVE_TYPES, PRIMITIVES, HOLDABLE_PRIMITIVES, PRIMITIVE_ALIASES, GENERIC_ALIASES, NOT_NEUTRAL, UTILITY_TYPES, NONES, LOWER_PRIMITIVE, LOWER_GENERIC, TypeSyntaxError, PUNCTUATION, RawParser, TypeProblemError, RETURNS_POSITIONS, LOOSE_NUMBER, Canonicalizer, INTEGER_WORDS, NUMBER_NAME;
4723
+ var init_type_grammar = __esm({
4724
+ "src/models/type-grammar.ts"() {
4725
+ "use strict";
4726
+ PRIMITIVE_TYPES = ["string", "int", "float", "bool", "bytes", "date", "datetime", "duration", "void", "any"];
4727
+ PRIMITIVES = new Set(PRIMITIVE_TYPES);
4728
+ HOLDABLE_PRIMITIVES = PRIMITIVE_TYPES.filter((p) => p !== "void" && p !== "any");
4729
+ PRIMITIVE_ALIASES = /* @__PURE__ */ new Map([
4730
+ ["boolean", "bool"],
4731
+ ["integer", "int"],
4732
+ ["long", "int"],
4733
+ ["i8", "int"],
4734
+ ["i16", "int"],
4735
+ ["i32", "int"],
4736
+ ["i64", "int"],
4737
+ ["i128", "int"],
4738
+ ["isize", "int"],
4739
+ ["u8", "int"],
4740
+ ["u16", "int"],
4741
+ ["u32", "int"],
4742
+ ["u64", "int"],
4743
+ ["u128", "int"],
4744
+ ["usize", "int"],
4745
+ ["double", "float"],
4746
+ ["f32", "float"],
4747
+ ["f64", "float"],
4748
+ ["Buffer", "bytes"],
4749
+ ["Uint8Array", "bytes"],
4750
+ ["Date", "datetime"],
4751
+ ["timestamp", "datetime"],
4752
+ ["object", "any"],
4753
+ ["unknown", "any"],
4754
+ ["json", "any"],
4755
+ ["Json", "any"],
4756
+ ["str", "string"]
4757
+ ]);
4758
+ GENERIC_ALIASES = /* @__PURE__ */ new Map([
4759
+ ["list", { form: "list", arity: 1 }],
4760
+ ["Array", { form: "list", arity: 1 }],
4761
+ ["ReadonlyArray", { form: "list", arity: 1 }],
4762
+ ["List", { form: "list", arity: 1 }],
4763
+ ["vec", { form: "list", arity: 1 }],
4764
+ ["vector", { form: "list", arity: 1 }],
4765
+ ["set", { form: "set", arity: 1 }],
4766
+ ["Set", { form: "set", arity: 1 }],
4767
+ ["ReadonlySet", { form: "set", arity: 1 }],
4768
+ ["map", { form: "map", arity: 2 }],
4769
+ ["Map", { form: "map", arity: 2 }],
4770
+ ["Record", { form: "map", arity: 2 }],
4771
+ ["ReadonlyMap", { form: "map", arity: 2 }],
4772
+ ["dict", { form: "map", arity: 2 }],
4773
+ ["dictionary", { form: "map", arity: 2 }],
4774
+ ["HashMap", { form: "map", arity: 2 }],
4775
+ ["Option", { form: "optional", arity: 1 }],
4776
+ ["Optional", { form: "optional", arity: 1 }],
4777
+ ["Promise", { form: "async", arity: 1 }]
4778
+ ]);
4779
+ NOT_NEUTRAL = /* @__PURE__ */ new Map([
4780
+ ["uuid", "a named value-object holding a string (e.g. order_id)"],
4781
+ ["decimal", "a named value-object (e.g. money), or float"],
4782
+ ["char", "string"],
4783
+ ["byte", "int, or bytes for binary data"],
4784
+ ["time", "datetime for an instant, or duration for an elapsed time"],
4785
+ ["tuple", "a named value-object"],
4786
+ ["result", "the success type as the returns (failures as findings or throws), or a named value-object"],
4787
+ ["error", "a named value-object"],
4788
+ ["never", "void"],
4789
+ ["box", "the inner type (ownership is an implementation detail)"],
4790
+ ["arc", "the inner type (ownership is an implementation detail)"],
4791
+ ["rc", "the inner type (ownership is an implementation detail)"],
4792
+ ["ref", "the inner type (ownership is an implementation detail)"],
4793
+ ["cell", "the inner type (ownership is an implementation detail)"],
4794
+ ["refcell", "the inner type (ownership is an implementation detail)"],
4795
+ ["mutex", "the inner type (synchronisation is an implementation detail)"],
4796
+ ["rwlock", "the inner type (synchronisation is an implementation detail)"],
4797
+ ["std", "a named type"],
4798
+ ["mcpserver", "a named type"]
4799
+ ]);
4800
+ UTILITY_TYPES = /* @__PURE__ */ new Set([
4801
+ "Partial",
4802
+ "Required",
4803
+ "Readonly",
4804
+ "Pick",
4805
+ "Omit",
4806
+ "Exclude",
4807
+ "Extract",
4808
+ "NonNullable",
4809
+ "ReturnType",
4810
+ "Parameters",
4811
+ "InstanceType",
4812
+ "Awaited",
4813
+ "ConstructorParameters",
4814
+ "ThisType",
4815
+ "Uppercase",
4816
+ "Lowercase"
4817
+ ]);
4818
+ NONES = /* @__PURE__ */ new Set(["null", "undefined"]);
4819
+ LOWER_PRIMITIVE = new Map([
4820
+ ...PRIMITIVE_TYPES.map((p) => [p, p]),
4821
+ ...[...PRIMITIVE_ALIASES].map(([k, v]) => [k.toLowerCase(), v])
4822
+ ]);
4823
+ LOWER_GENERIC = new Map([...GENERIC_ALIASES].map(([k, v]) => [k.toLowerCase(), v]));
4824
+ TypeSyntaxError = class extends Error {
4825
+ };
4826
+ PUNCTUATION = ["...", "::", "=>", "<", ">", ",", "|", "?", "(", ")", "[", "]", "{", "}", ":", "&", ";", "."];
4827
+ RawParser = class {
4828
+ constructor(tokens) {
4829
+ this.tokens = tokens;
4830
+ this.at = 0;
4831
+ }
4832
+ /** type-position: an optional `async` prefix, one type, and nothing after it. */
4833
+ position() {
4834
+ if (this.tokens.length === 0) throw new TypeSyntaxError("the position is empty");
4835
+ const node = this.isAsyncPrefix() ? (this.at++, { k: "async", inner: this.type() }) : this.type();
4836
+ if (this.at < this.tokens.length) throw new TypeSyntaxError(`"${this.rest()}" is left over after the type`);
4837
+ return node;
4838
+ }
4839
+ isAsyncPrefix() {
4840
+ const first = this.tokens[0];
4841
+ const next = this.tokens[1];
4842
+ if (!first || first.t !== "id" || first.v !== "async" || !next) return false;
4843
+ return next.t !== "p" || ["(", "{", "[", "::"].includes(next.v);
4844
+ }
4845
+ rest() {
4846
+ return this.tokens.slice(this.at).map((t) => t.v).join(" ");
4847
+ }
4848
+ peek(offset = 0) {
4849
+ return this.tokens[this.at + offset];
4850
+ }
4851
+ isP(v, offset = 0) {
4852
+ const t = this.peek(offset);
4853
+ return !!t && t.t === "p" && t.v === v;
4854
+ }
4855
+ expect(v) {
4856
+ if (!this.isP(v)) {
4857
+ const got = this.peek();
4858
+ throw new TypeSyntaxError(got ? `expected "${v}" but found "${got.v}"` : `expected "${v}" but the text ended`);
4859
+ }
4860
+ this.at++;
4861
+ }
4862
+ type() {
4863
+ if (this.isP("|")) this.at++;
4864
+ const members = [this.intersection()];
4865
+ while (this.isP("|")) {
4866
+ this.at++;
4867
+ members.push(this.intersection());
4868
+ }
4869
+ return members.length === 1 ? members[0] : { k: "union", members };
4870
+ }
4871
+ intersection() {
4872
+ const members = [this.postfix()];
4873
+ while (this.isP("&")) {
4874
+ this.at++;
4875
+ members.push(this.postfix());
4876
+ }
4877
+ return members.length === 1 ? members[0] : { k: "intersection", members };
4878
+ }
4879
+ postfix() {
4880
+ let node = this.primary();
4881
+ for (; ; ) {
4882
+ if (this.isP("[") && this.isP("]", 1)) {
4883
+ this.at += 2;
4884
+ node = { k: "array", elem: node };
4885
+ } else if (this.isP("?")) {
4886
+ this.at++;
4887
+ node = { k: "opt", inner: node };
4888
+ } else {
4889
+ return node;
4890
+ }
4891
+ }
4892
+ }
4893
+ primary() {
4894
+ const tok = this.peek();
4895
+ if (!tok) throw new TypeSyntaxError("a type was expected but the text ended");
4896
+ if (tok.t === "str" || tok.t === "num") {
4897
+ this.at++;
4898
+ return { k: "literal", text: tok.v };
4899
+ }
4900
+ if (tok.t === "id" && (tok.v === "keyof" || tok.v === "typeof" || tok.v === "unique") && this.peek(1)?.t === "id") {
4901
+ this.at++;
4902
+ return { k: "operator", op: tok.v, inner: this.postfix() };
4903
+ }
4904
+ const next = this.peek(1);
4905
+ if (tok.t === "id" && tok.v === "readonly" && next && (next.t === "id" || next.t === "p" && (next.v === "(" || next.v === "["))) {
4906
+ this.at++;
4907
+ return this.postfix();
4908
+ }
4909
+ if (tok.t === "id" || tok.t === "p" && tok.v === "::") return this.named();
4910
+ if (tok.t === "p" && tok.v === "(") return this.parenOrFunction();
4911
+ if (tok.t === "p" && tok.v === "{") return this.objectShape();
4912
+ if (tok.t === "p" && tok.v === "[") return this.tuple();
4913
+ throw new TypeSyntaxError(`"${tok.v}" cannot start a type`);
4914
+ }
4915
+ ident() {
4916
+ const tok = this.peek();
4917
+ if (!tok || tok.t !== "id") throw new TypeSyntaxError(tok ? `expected a name but found "${tok.v}"` : "expected a name but the text ended");
4918
+ this.at++;
4919
+ return tok.v;
4920
+ }
4921
+ named() {
4922
+ let name = "";
4923
+ if (this.isP("::")) {
4924
+ this.at++;
4925
+ name = "::";
4926
+ }
4927
+ name += this.ident();
4928
+ while (this.isP("::") || this.isP(".")) {
4929
+ const sep18 = this.peek().v;
4930
+ this.at++;
4931
+ name += sep18 + this.ident();
4932
+ }
4933
+ if (!this.isP("<")) return { k: "name", name };
4934
+ this.at++;
4935
+ const args = [this.type()];
4936
+ while (this.isP(",")) {
4937
+ this.at++;
4938
+ args.push(this.type());
4939
+ }
4940
+ this.expect(">");
4941
+ return { k: "name", name, args };
4942
+ }
4943
+ /** The index of the `)` closing the `(` at the cursor, or -1. */
4944
+ closingParen() {
4945
+ let depth = 0;
4946
+ for (let i = this.at; i < this.tokens.length; i++) {
4947
+ const t = this.tokens[i];
4948
+ if (t.t !== "p") continue;
4949
+ if (t.v === "(") depth++;
4950
+ else if (t.v === ")" && --depth === 0) return i;
4951
+ }
4952
+ return -1;
4953
+ }
4954
+ parenOrFunction() {
4955
+ const close2 = this.closingParen();
4956
+ const after = close2 === -1 ? void 0 : this.tokens[close2 + 1];
4957
+ if (!after || after.t !== "p" || after.v !== "=>") {
4958
+ this.expect("(");
4959
+ const inner2 = this.type();
4960
+ this.expect(")");
4961
+ return { k: "paren", inner: inner2 };
4962
+ }
4963
+ this.expect("(");
4964
+ const params = [];
4965
+ while (!this.isP(")")) {
4966
+ if (this.isP("...")) this.at++;
4967
+ this.ident();
4968
+ if (this.isP("?")) this.at++;
4969
+ if (this.isP(":")) {
4970
+ this.at++;
4971
+ params.push(this.type());
4972
+ }
4973
+ if (!this.isP(",")) break;
4974
+ this.at++;
4975
+ }
4976
+ this.expect(")");
4977
+ this.expect("=>");
4978
+ return { k: "function", params, ret: this.type() };
4979
+ }
4980
+ objectShape() {
4981
+ this.expect("{");
4982
+ const members = [];
4983
+ while (!this.isP("}")) {
4984
+ if (this.isP("[")) {
4985
+ this.at++;
4986
+ this.ident();
4987
+ this.expect(":");
4988
+ members.push(this.type());
4989
+ this.expect("]");
4990
+ } else {
4991
+ if (this.peek()?.t === "id" && this.peek().v === "readonly" && this.peek(1)?.t === "id") this.at++;
4992
+ const key = this.peek();
4993
+ if (!key || key.t !== "id" && key.t !== "str" && key.t !== "num") throw new TypeSyntaxError("an inline object member has no name");
4994
+ this.at++;
4995
+ if (this.isP("?")) this.at++;
4996
+ if (this.isP("(")) {
4997
+ members.push(this.parenOrFunctionMember());
4998
+ this.skipSeparator();
4999
+ continue;
5000
+ }
5001
+ }
5002
+ this.expect(":");
5003
+ members.push(this.type());
5004
+ this.skipSeparator();
5005
+ }
5006
+ this.expect("}");
5007
+ return { k: "object", members };
5008
+ }
5009
+ /** A method member of an inline object: (params): returns. */
5010
+ parenOrFunctionMember() {
5011
+ this.expect("(");
5012
+ const params = [];
5013
+ while (!this.isP(")")) {
5014
+ if (this.isP("...")) this.at++;
5015
+ this.ident();
5016
+ if (this.isP("?")) this.at++;
5017
+ if (this.isP(":")) {
5018
+ this.at++;
5019
+ params.push(this.type());
5020
+ }
5021
+ if (!this.isP(",")) break;
5022
+ this.at++;
5023
+ }
5024
+ this.expect(")");
5025
+ this.expect(":");
5026
+ return { k: "function", params, ret: this.type() };
5027
+ }
5028
+ skipSeparator() {
5029
+ if (this.isP(";") || this.isP(",")) this.at++;
5030
+ }
5031
+ tuple() {
5032
+ this.expect("[");
5033
+ const elems = [];
5034
+ while (!this.isP("]")) {
5035
+ elems.push(this.type());
5036
+ if (!this.isP(",")) break;
5037
+ this.at++;
5038
+ }
5039
+ this.expect("]");
5040
+ return { k: "tuple", elems };
5041
+ }
5042
+ };
5043
+ TypeProblemError = class extends Error {
5044
+ constructor(code, detail, replacement) {
5045
+ super(detail);
5046
+ this.code = code;
5047
+ this.detail = detail;
5048
+ this.replacement = replacement;
5049
+ }
5050
+ };
5051
+ RETURNS_POSITIONS = /* @__PURE__ */ new Set(["returns", "type-method-returns", "signature-returns"]);
5052
+ LOOSE_NUMBER = "number";
5053
+ Canonicalizer = class {
5054
+ constructor(specId, kind) {
5055
+ this.specId = specId;
5056
+ this.kind = kind;
5057
+ this.respellings = [];
5058
+ this.problems = [];
5059
+ }
5060
+ /** One position: its canonical text when it has one, else the text as written, recorded either way. */
5061
+ read(text3, position, path92, omittable2 = false) {
5062
+ const parse = parseTypePosition(text3, position, omittable2);
5063
+ if (parse.problem) {
5064
+ this.problems.push({ ...parse.problem, specId: this.specId, kind: this.kind, path: path92 });
5065
+ return text3;
5066
+ }
5067
+ if (parse.canonical !== text3) this.respellings.push({ specId: this.specId, kind: this.kind, path: path92, written: text3, stored: parse.canonical });
5068
+ return parse.canonical;
5069
+ }
5070
+ params(params, position, prefix) {
5071
+ return params.map((p) => ({ ...p, type: this.read(p.type, position, `${prefix}.${p.name}`, !!p.optional) }));
5072
+ }
5073
+ };
5074
+ INTEGER_WORDS = /* @__PURE__ */ new Set([
5075
+ "count",
5076
+ "counts",
5077
+ "size",
5078
+ "sizes",
5079
+ "length",
5080
+ "len",
5081
+ "limit",
5082
+ "limits",
5083
+ "port",
5084
+ "step",
5085
+ "steps",
5086
+ "index",
5087
+ "idx",
5088
+ "depth",
5089
+ "level",
5090
+ "levels",
5091
+ "days",
5092
+ "day",
5093
+ "hours",
5094
+ "minutes",
5095
+ "seconds",
5096
+ "ms",
5097
+ "millis",
5098
+ "milliseconds",
5099
+ "bytes",
5100
+ "version",
5101
+ "total",
5102
+ "offset",
5103
+ "page",
5104
+ "pages",
5105
+ "retries",
5106
+ "attempts",
5107
+ "number",
5108
+ "num",
5109
+ "line",
5110
+ "lines",
5111
+ "column",
5112
+ "columns",
5113
+ "max",
5114
+ "min",
5115
+ "width",
5116
+ "height"
5117
+ ]);
5118
+ NUMBER_NAME = /(?<![\w:.-])number(?![\w:.-])/g;
5119
+ }
5120
+ });
5121
+
4326
5122
  // src/models/type-references.ts
4327
5123
  function extractTypeIdentifiers(typeStr) {
4328
- let cleaned = typeStr.replace(/\/\/.*$/gm, "").replace(/#.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
5124
+ let cleaned = typeStr.replace(/^\s*async\s+(?=\S)/, "").replace(/\/\/.*$/gm, "").replace(/#.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
4329
5125
  cleaned = cleaned.replace(/(?<=\S)\s*\([^)]*\)\s*$/, "");
4330
5126
  cleaned = cleaned.replace(/(?<=\S)\s+[-—:]\s+[a-z\s_-]+$/, "");
4331
5127
  cleaned = cleaned.replace(/(["'`])(?:\\.|[^\\])*?\1/g, " ");
@@ -4411,16 +5207,19 @@ function methodTypeRefs(m) {
4411
5207
  if (m.params && m.params.length > 0) {
4412
5208
  const refs = [];
4413
5209
  for (const p of m.params) {
4414
- refs.push(...extractTypeIdentifiers(p.type));
5210
+ refs.push(...writtenTypeRefs(p.type, "param"));
4415
5211
  }
4416
- refs.push(...extractTypeIdentifiers(m.returns ?? ""));
5212
+ if (m.returns !== void 0) refs.push(...writtenTypeRefs(m.returns, "returns"));
4417
5213
  return Array.from(new Set(refs));
4418
5214
  }
4419
5215
  return extractTypesFromSignature(m.signature ?? "", m.returns ?? "");
4420
5216
  }
4421
5217
  function signatureTypeRefs(type) {
4422
5218
  if (type.kind !== "signature") return [];
4423
- const refs = [...(type.params ?? []).flatMap((p) => extractTypeIdentifiers(p.type)), ...extractTypeIdentifiers(type.returns ?? "")];
5219
+ const refs = [
5220
+ ...(type.params ?? []).flatMap((p) => writtenTypeRefs(p.type, "signature-param")),
5221
+ ...type.returns !== void 0 ? writtenTypeRefs(type.returns, "signature-returns") : []
5222
+ ];
4424
5223
  return Array.from(new Set(refs));
4425
5224
  }
4426
5225
  function methodGenericParameters(method2) {
@@ -4455,85 +5254,117 @@ function typeGenericParameters(type) {
4455
5254
  }
4456
5255
  function fieldTypeRefs(type, fieldType) {
4457
5256
  const generics = new Set(Array.from(typeGenericParameters(type)).map((g) => g.toLowerCase()));
4458
- return extractTypeIdentifiers(fieldType).filter((ref) => !generics.has(ref.toLowerCase()));
5257
+ return writtenTypeRefs(fieldType, "field").filter((ref) => !generics.has(ref.toLowerCase()));
4459
5258
  }
4460
5259
  function interfaceGenericParameters(intf) {
4461
5260
  return extractTypeGenerics(intf.name);
4462
5261
  }
4463
- var BUILTIN_TYPES;
4464
5262
  var init_type_references = __esm({
4465
5263
  "src/models/type-references.ts"() {
4466
5264
  "use strict";
4467
- BUILTIN_TYPES = /* @__PURE__ */ new Set([
4468
- "string",
4469
- "str",
4470
- "number",
4471
- "boolean",
4472
- "bool",
4473
- "float",
4474
- "double",
4475
- "int",
4476
- "integer",
4477
- "u8",
4478
- "u16",
4479
- "u32",
4480
- "u64",
4481
- "u128",
4482
- "usize",
4483
- "i8",
4484
- "i16",
4485
- "i32",
4486
- "i64",
4487
- "i128",
4488
- "isize",
4489
- "f32",
4490
- "f64",
4491
- "char",
4492
- "byte",
4493
- "bytes",
4494
- "any",
4495
- "void",
4496
- "null",
4497
- "undefined",
4498
- "object",
4499
- "date",
4500
- "datetime",
4501
- "time",
4502
- "timestamp",
4503
- "duration",
4504
- "uuid",
4505
- "decimal",
4506
- "json",
4507
- "true",
4508
- "false",
4509
- "list",
4510
- "vector",
4511
- "vec",
4512
- "array",
4513
- "map",
4514
- "set",
4515
- "dict",
4516
- "dictionary",
4517
- "hashmap",
4518
- "tuple",
4519
- "result",
4520
- "option",
4521
- "box",
4522
- "arc",
4523
- "rc",
4524
- "ref",
4525
- "cell",
4526
- "refcell",
4527
- "mutex",
4528
- "rwlock",
4529
- "std",
4530
- "promise",
4531
- "record",
4532
- "json",
4533
- "unknown",
4534
- "never",
4535
- "error",
4536
- "mcpserver"
5265
+ init_type_grammar();
5266
+ }
5267
+ });
5268
+
5269
+ // src/models/type-dialects.ts
5270
+ function lastSegment(name) {
5271
+ return name.split(/::|\./).filter(Boolean).pop() ?? name;
5272
+ }
5273
+ function sameType(code, spec, codeNames) {
5274
+ if (code.form === "primitive" && code.name === "number") return spec.form === "primitive" && (spec.name === "int" || spec.name === "float");
5275
+ if (code.form === "named" && (spec.form === "named" || spec.form === "applied")) {
5276
+ if (spec.form === "applied") return false;
5277
+ return namesAgree(code.name, spec.name, codeNames);
5278
+ }
5279
+ if (code.form !== spec.form) return false;
5280
+ if (code.form === "primitive") return code.name === spec.name;
5281
+ if (code.form === "applied" && !namesAgree(code.name, spec.name, codeNames)) return false;
5282
+ if (code.args.length !== spec.args.length) return false;
5283
+ return code.args.every((arg, i) => sameType(arg, spec.args[i], codeNames));
5284
+ }
5285
+ function namesAgree(codeName, specName, codeNames) {
5286
+ if (codeName === specName) return true;
5287
+ const mapped = codeNames.get(specName) ?? codeNames.get(lastSegment(specName));
5288
+ return mapped === codeName || mapped === lastSegment(codeName);
5289
+ }
5290
+ function writeTypeScript(expr) {
5291
+ switch (expr.form) {
5292
+ case "primitive":
5293
+ return TS_PRIMITIVES[expr.name ?? ""] ?? expr.name ?? "unknown";
5294
+ case "named":
5295
+ return expr.name ?? "unknown";
5296
+ case "list": {
5297
+ const elem = writeTypeScript(expr.args[0]);
5298
+ return /[|\s]/.test(elem) ? `(${elem})[]` : `${elem}[]`;
5299
+ }
5300
+ case "set":
5301
+ return `Set<${writeTypeScript(expr.args[0])}>`;
5302
+ case "map":
5303
+ return `Record<${writeTypeScript(expr.args[0])}, ${writeTypeScript(expr.args[1])}>`;
5304
+ case "optional":
5305
+ return `${writeTypeScript(expr.args[0])} | null`;
5306
+ case "union":
5307
+ return expr.args.map(writeTypeScript).join(" | ");
5308
+ case "async":
5309
+ return `Promise<${writeTypeScript(expr.args[0])}>`;
5310
+ case "applied":
5311
+ return `${expr.name}<${expr.args.map(writeTypeScript).join(", ")}>`;
5312
+ }
5313
+ }
5314
+ function typeDialectFor(language) {
5315
+ return DIALECTS.get(language.toLowerCase()) ?? null;
5316
+ }
5317
+ var TS_PRIMITIVES, typescriptDialect, DIALECTS;
5318
+ var init_type_dialects = __esm({
5319
+ "src/models/type-dialects.ts"() {
5320
+ "use strict";
5321
+ init_type_grammar();
5322
+ TS_PRIMITIVES = {
5323
+ string: "string",
5324
+ int: "number",
5325
+ float: "number",
5326
+ bool: "boolean",
5327
+ bytes: "Uint8Array",
5328
+ date: "string",
5329
+ datetime: "string",
5330
+ duration: "string",
5331
+ void: "void",
5332
+ any: "unknown"
5333
+ };
5334
+ typescriptDialect = {
5335
+ language: "typescript",
5336
+ read(annotation) {
5337
+ return readCodeAnnotation(annotation, false);
5338
+ },
5339
+ agrees(annotation, expression, codeNames) {
5340
+ const code = readCodeAnnotation(annotation, true);
5341
+ return code !== null && sameType(code, expression, codeNames);
5342
+ },
5343
+ write(expression) {
5344
+ return writeTypeScript(expression);
5345
+ },
5346
+ mappingLines() {
5347
+ return [
5348
+ "string \u2192 string",
5349
+ "int, float \u2192 number",
5350
+ "bool \u2192 boolean",
5351
+ "bytes \u2192 Uint8Array",
5352
+ "date, datetime, duration \u2192 string (ISO 8601)",
5353
+ "any \u2192 unknown",
5354
+ "list<T> \u2192 T[]",
5355
+ "set<T> \u2192 Set<T>",
5356
+ "map<K, V> \u2192 Record<K, V>",
5357
+ "T? \u2192 T | null",
5358
+ "A | B \u2192 A | B",
5359
+ "async T \u2192 Promise<T>",
5360
+ "an enum E \u2192 type E = 'a' | 'b' (a string-literal union alias)",
5361
+ "a named scalar E holding P \u2192 type E = P (an alias of the primitive)"
5362
+ ];
5363
+ }
5364
+ };
5365
+ DIALECTS = /* @__PURE__ */ new Map([
5366
+ ["typescript", typescriptDialect],
5367
+ ["javascript", typescriptDialect]
4537
5368
  ]);
4538
5369
  }
4539
5370
  });
@@ -4834,6 +5665,12 @@ function callSitesOf(facts, fn) {
4834
5665
  function hasFunctionBody(facts, symbol) {
4835
5666
  return callSitesOf(facts, symbol) !== void 0;
4836
5667
  }
5668
+ function languageOfSourcePath(sourcePath) {
5669
+ return EXTENSION_LANGUAGE[path12.extname(sourcePath).toLowerCase()];
5670
+ }
5671
+ function dialectOf(facts) {
5672
+ return facts.language ? typeDialectFor(facts.language) : null;
5673
+ }
4837
5674
  function resolveImport(fromFile, specifier, knownPaths) {
4838
5675
  if (!specifier.startsWith(".")) return void 0;
4839
5676
  const joined = pathKey(path12.posix.normalize(path12.posix.join(path12.posix.dirname(fromFile), specifier)));
@@ -4852,12 +5689,37 @@ function resolveImport(fromFile, specifier, knownPaths) {
4852
5689
  }
4853
5690
  return void 0;
4854
5691
  }
4855
- var path12, NO_DECLARED_TYPES;
5692
+ var path12, NO_DECLARED_TYPES, EXTENSION_LANGUAGE;
4856
5693
  var init_code_model = __esm({
4857
5694
  "src/models/code-model.ts"() {
4858
5695
  "use strict";
4859
5696
  path12 = __toESM(require("path"));
5697
+ init_type_dialects();
4860
5698
  NO_DECLARED_TYPES = [];
5699
+ EXTENSION_LANGUAGE = {
5700
+ ".ts": "typescript",
5701
+ ".tsx": "typescript",
5702
+ ".mts": "typescript",
5703
+ ".cts": "typescript",
5704
+ ".js": "javascript",
5705
+ ".jsx": "javascript",
5706
+ ".mjs": "javascript",
5707
+ ".cjs": "javascript",
5708
+ ".py": "python",
5709
+ ".rs": "rust",
5710
+ ".go": "go",
5711
+ ".cs": "csharp",
5712
+ ".java": "java",
5713
+ ".c": "c",
5714
+ ".h": "c",
5715
+ ".cpp": "cpp",
5716
+ ".cc": "cpp",
5717
+ ".hpp": "cpp",
5718
+ ".rb": "ruby",
5719
+ ".php": "php",
5720
+ ".kt": "kotlin",
5721
+ ".swift": "swift"
5722
+ };
4861
5723
  }
4862
5724
  });
4863
5725
 
@@ -4885,11 +5747,18 @@ function closureTypeOf(snapshot, identifier) {
4885
5747
  if (matches.length === 1) return matches[0];
4886
5748
  return matches.find((def) => nameKey(def.id) === nameKey(ref));
4887
5749
  }
5750
+ function renamedRefs(expr, rename) {
5751
+ const args = expr.args.map((a) => renamedRefs(a, rename));
5752
+ return expr.form === "named" || expr.form === "applied" ? { ...expr, name: rename(expr.name), args } : { ...expr, args };
5753
+ }
4888
5754
  function canonicalTypeRef(snapshot, typeExpr) {
5755
+ const rename = (identifier) => closureTypeOf(snapshot, identifier)?.id ?? identifier;
5756
+ const parse = parseTypeExpression(typeExpr, "returns");
5757
+ if (parse.expression) return canonicalTypeText(renamedRefs(parse.expression, rename));
4889
5758
  return typeExpr.replace(TYPE_IDENTIFIER, (identifier) => {
4890
5759
  if (!/[A-Za-z]/.test(identifier)) return identifier;
4891
- if (BUILTIN_TYPES.has(identifier.toLowerCase())) return identifier.toLowerCase();
4892
- return closureTypeOf(snapshot, identifier)?.id ?? identifier;
5760
+ if (isTypeVocabulary(identifier)) return identifier.toLowerCase();
5761
+ return rename(identifier);
4893
5762
  });
4894
5763
  }
4895
5764
  function typeShape(snapshot, def) {
@@ -4901,7 +5770,9 @@ function typeShape(snapshot, def) {
4901
5770
  ...def.kind === "signature" ? {
4902
5771
  params: (def.params ?? []).map((p) => ({ type: canonicalTypeRef(snapshot, p.type), optional: p.optional === true })),
4903
5772
  returns: canonicalTypeRef(snapshot, def.returns ?? "unknown")
4904
- } : {}
5773
+ } : {},
5774
+ ...def.kind === "enum" ? { values: (def.values ?? []).map((v) => v.name) } : {},
5775
+ ...def.holds !== void 0 ? { holds: canonicalTypeRef(snapshot, def.holds) } : {}
4905
5776
  };
4906
5777
  }
4907
5778
  function typeDefExprs(def) {
@@ -4978,6 +5849,7 @@ var init_surface_references = __esm({
4978
5849
  "use strict";
4979
5850
  import_crypto = require("crypto");
4980
5851
  init_type_references();
5852
+ init_type_grammar();
4981
5853
  init_canonical_json();
4982
5854
  TYPE_IDENTIFIER = /(?:::)?[A-Za-z0-9_][A-Za-z0-9_-]*(?:(?:::|\.)[A-Za-z0-9_][A-Za-z0-9_-]*)*/g;
4983
5855
  }
@@ -5191,7 +6063,7 @@ var init_project_family = __esm({
5191
6063
  // src/models/validation-options.ts
5192
6064
  function selectsFamily(options, declaresMembers) {
5193
6065
  if (options?.family) return true;
5194
- return declaresMembers && options?.recursive !== false;
6066
+ return declaresMembers && options?.memberDepth !== 0;
5195
6067
  }
5196
6068
  var init_validation_options = __esm({
5197
6069
  "src/models/validation-options.ts"() {
@@ -5210,6 +6082,8 @@ var init_models = __esm({
5210
6082
  init_template();
5211
6083
  init_specs();
5212
6084
  init_type_references();
6085
+ init_type_grammar();
6086
+ init_type_dialects();
5213
6087
  init_step_graph();
5214
6088
  init_step_config();
5215
6089
  init_code_model();
@@ -5959,17 +6833,23 @@ function buildCanvasModel(issues = [], relations) {
5959
6833
  })),
5960
6834
  methods: t.methods.map((m) => ({ name: m.name, signature: m.signature, returns: m.returns, ...m.description ? { description: m.description } : {} })),
5961
6835
  ...t.kind === "signature" ? { signature: deriveTypeSignature(t) } : {},
6836
+ ...t.kind === "enum" ? { values: (t.values ?? []).map((v) => ({ name: v.name, ...v.description ? { description: v.description } : {} })) } : {},
6837
+ ...t.holds !== void 0 ? { holds: t.holds } : {},
5962
6838
  usedBy: usedByFor(t),
5963
6839
  ...t.componentClass ? { componentClass: t.componentClass } : {},
5964
6840
  ...t.database ? { database: t.database } : {},
5965
6841
  ...t.table ? { table: t.table } : {},
5966
6842
  ...t.linkedEntity ? { linkedEntity: t.linkedEntity } : {}
5967
6843
  }));
5968
- const MANY_SHAPE = /\[\s*\]|Array<|Vec<|Set<|List<|Map<|Record<|HashMap</i;
6844
+ const multiplicity = (field) => {
6845
+ const expression = parseTypeExpression(field.type, "field").expression;
6846
+ if (expression && typeIsMany(expression)) return "*";
6847
+ return field.optional || expression?.form === "optional" ? "0..1" : "1";
6848
+ };
5969
6849
  const typeEdges = [];
5970
6850
  for (const t of typeSpecs) {
5971
6851
  for (const field of t.fields) {
5972
- const refs = new Set(extractTypeIdentifiers(field.type));
6852
+ const refs = new Set(writtenTypeRefs(field.type, "field"));
5973
6853
  if (field.references) {
5974
6854
  const refStr = field.references;
5975
6855
  refs.add(refStr);
@@ -5984,8 +6864,8 @@ function buildCanvasModel(issues = [], relations) {
5984
6864
  const qualified = other.subsystem && !other.id.startsWith(`${other.subsystem}::`) ? `${other.subsystem}::${other.id}` : other.id;
5985
6865
  return matchTypeRef(ref, qualified);
5986
6866
  });
5987
- if (target && target.id !== t.id) {
5988
- const card = MANY_SHAPE.test(field.type) ? "*" : field.optional ? "0..1" : "1";
6867
+ if (target && target.id !== t.id && target.kind !== "enum" && target.holds === void 0) {
6868
+ const card = multiplicity(field);
5989
6869
  if (!typeEdges.some((e) => e.from === t.id && e.to === target.id && e.field === field.name)) {
5990
6870
  typeEdges.push({ from: t.id, to: target.id, field: field.name, card });
5991
6871
  }
@@ -7659,7 +8539,7 @@ var MODEL = __MODEL_JSON__;
7659
8539
  return '';
7660
8540
  }
7661
8541
  function visibleFields(t) {
7662
- if (det === 'names') return [];
8542
+ if (det === 'names' || t.values || t.holds) return [];
7663
8543
  if (det === 'keys') return t.fields.filter(function (f) { return markerOf(t, f) !== ''; });
7664
8544
  return t.fields;
7665
8545
  }
@@ -7704,15 +8584,19 @@ var MODEL = __MODEL_JSON__;
7704
8584
  var meths = det === 'full' ? t.methods : [];
7705
8585
  // A signature type is drawn with its derived text in place of a field list.
7706
8586
  var sig = t.signature && det !== 'names' ? t.signature : '';
7707
- var head = t.name + ' \\u00AB' + t.kind + '\\u00BB';
7708
- var rows = (sig ? [sig] : []).concat(fields.map(function (f) { return rowText(t, f); }))
8587
+ // An enum is drawn with its values in place of a field list.
8588
+ var vals = t.values && det !== 'names' ? t.values : [];
8589
+ // A named scalar is drawn compactly: its name and the primitive it holds.
8590
+ var head = t.name + (t.holds ? ' = ' + t.holds : '') + ' \\u00AB' + t.kind + '\\u00BB';
8591
+ var rows = (sig ? [sig] : []).concat(vals.map(function (v) { return '\\u2022 ' + v.name; }))
8592
+ .concat(fields.map(function (f) { return rowText(t, f); }))
7709
8593
  .concat(meths.map(function (m) { return '\\u0192 ' + m.name + '(): ' + m.returns; }));
7710
8594
  var longest = head.length + 4;
7711
8595
  rows.forEach(function (r) { if (r.length > longest) longest = r.length; });
7712
8596
  var plain = rows.length === 0;
7713
8597
  var pw = Math.max(170, head.length * 6.8 + 26);
7714
8598
  var W = Math.max(210, Math.min(400, longest * 6.6 + 30));
7715
- return { fields: fields, meths: meths, sig: sig, head: head, plain: plain, w: plain ? pw : W, h: plain ? 40 : TH_H + (sig ? ROW_H : 0) + fields.length * ROW_H + meths.length * ROW_H };
8599
+ return { fields: fields, meths: meths, sig: sig, vals: vals, head: head, plain: plain, w: plain ? pw : W, h: plain ? 40 : TH_H + (sig ? ROW_H : 0) + vals.length * ROW_H + fields.length * ROW_H + meths.length * ROW_H };
7716
8600
  }
7717
8601
 
7718
8602
  // Emit one type table with its top-left at (ax, ay); returns its size.
@@ -7743,6 +8627,13 @@ var MODEL = __MODEL_JSON__;
7743
8627
  });
7744
8628
  ry += ROW_H;
7745
8629
  }
8630
+ sh.vals.forEach(function (v, vi) {
8631
+ eles.push({
8632
+ data: { id: 'TV~' + t.id + '~' + vi, parent: 'T~' + t.id, label: '\\u2022 ' + v.name, w: sh.w, h: ROW_H, tw: sh.w - 14 },
8633
+ position: { x: ax + sh.w / 2, y: ry + ROW_H / 2 }, classes: 'typeRow' + (dim ? ' dimmed' : ''), grabbable: false,
8634
+ });
8635
+ ry += ROW_H;
8636
+ });
7746
8637
  sh.fields.forEach(function (f) {
7747
8638
  var rid = 'TF~' + t.id + '~' + f.name;
7748
8639
  rowIds[rid] = 1;
@@ -10276,6 +11167,17 @@ var MODEL = __MODEL_JSON__;
10276
11167
  : '<span class="desc">no fields</span>';
10277
11168
  if (ty.signature) {
10278
11169
  body += section('Signature', 1, '<div class="method"><code>' + esc(ty.signature) + '</code></div>', true);
11170
+ } else if (ty.holds) {
11171
+ body += section('Holds', 1, '<div class="method"><code>' + esc(ty.holds) + '</code>'
11172
+ + '<div class="mdesc">A named scalar: one ' + esc(ty.holds) + ' under this name.</div></div>', true);
11173
+ } else if (ty.values) {
11174
+ body += section('Values', ty.values.length, ty.values.length
11175
+ ? ty.values.map(function (v) {
11176
+ return '<div class="method"><div class="mname">' + esc(v.name) + '</div>'
11177
+ + (v.description ? '<div class="mdesc">' + esc(v.description) + '</div>' : '')
11178
+ + '</div>';
11179
+ }).join('')
11180
+ : '<span class="desc">no values</span>', true);
10279
11181
  } else {
10280
11182
  body += section('Fields', ty.fields.length, fieldsInner, true);
10281
11183
  }
@@ -10602,21 +11504,66 @@ var init_externals = __esm({
10602
11504
  });
10603
11505
 
10604
11506
  // src/core/openapi.ts
10605
- function schemaFor(typeRef, closureIds) {
10606
- const trimmed = typeRef.trim().replace(/^promise\s*<(.+)>$/i, "$1").trim();
10607
- const arrayMatch = /^(.+)\[\]$/.exec(trimmed);
10608
- if (arrayMatch) {
10609
- return { type: "array", items: schemaFor(arrayMatch[1], closureIds) };
11507
+ function expressionOf(typeRef) {
11508
+ return parseTypeExpression(typeRef.trim(), "returns").expression;
11509
+ }
11510
+ function closureHit(name, closureIds) {
11511
+ const local = name.split(/::|\./).pop().toLowerCase().replace(/[^a-z0-9_-]/g, "");
11512
+ return [...closureIds].find((id) => id.toLowerCase() === local);
11513
+ }
11514
+ function schemaOfExpression(expr, closureIds) {
11515
+ switch (expr.form) {
11516
+ case "primitive":
11517
+ return { ...PRIMITIVE_SCHEMAS[expr.name ?? ""] ?? {} };
11518
+ case "named":
11519
+ case "applied": {
11520
+ const hit = closureHit(expr.name ?? "", closureIds);
11521
+ return hit ? { $ref: `#/components/schemas/${hit}` } : { description: `Unresolved type: ${expr.name}` };
11522
+ }
11523
+ case "list":
11524
+ return { type: "array", items: schemaOfExpression(expr.args[0], closureIds) };
11525
+ case "set":
11526
+ return { type: "array", uniqueItems: true, items: schemaOfExpression(expr.args[0], closureIds) };
11527
+ case "map": {
11528
+ const key = expr.args[0];
11529
+ const keyHit = key.form === "named" ? closureHit(key.name ?? "", closureIds) : void 0;
11530
+ const propertyNames = keyHit ? { propertyNames: { $ref: `#/components/schemas/${keyHit}` } } : key.form === "primitive" && key.name === "int" ? { propertyNames: { pattern: "^-?[0-9]+$" } } : {};
11531
+ return { type: "object", ...propertyNames, additionalProperties: schemaOfExpression(expr.args[1], closureIds) };
11532
+ }
11533
+ case "optional": {
11534
+ const inner2 = schemaOfExpression(expr.args[0], closureIds);
11535
+ if (Object.keys(inner2).length === 0) return inner2;
11536
+ if (typeof inner2.type === "string") return { ...inner2, type: [inner2.type, "null"] };
11537
+ return { anyOf: [inner2, { type: "null" }] };
11538
+ }
11539
+ case "union":
11540
+ return { oneOf: expr.args.map((member) => schemaOfExpression(member, closureIds)) };
11541
+ case "async":
11542
+ return schemaOfExpression(expr.args[0], closureIds);
10610
11543
  }
10611
- const lower = trimmed.toLowerCase();
10612
- if (lower in PRIMITIVES) {
10613
- const p = PRIMITIVES[lower];
10614
- return p.type ? { ...p } : {};
11544
+ }
11545
+ function schemaFor(typeRef, closureIds) {
11546
+ const expr = expressionOf(typeRef);
11547
+ if (!expr) return { description: `Not a canonical type expression: ${typeRef.trim()}` };
11548
+ return schemaOfExpression(expr, closureIds);
11549
+ }
11550
+ function returnsNothing(returns) {
11551
+ const expr = expressionOf(returns);
11552
+ const inner2 = expr?.form === "async" ? expr.args[0] : expr;
11553
+ return inner2?.form === "primitive" && inner2.name === "void";
11554
+ }
11555
+ function enumDescription(values) {
11556
+ return ["Values:", ...values.map((v) => `- \`${v.name}\`${v.description ? `: ${v.description}` : ""}`)].join("\n");
11557
+ }
11558
+ function enumValuesFrom(schema) {
11559
+ const described = /* @__PURE__ */ new Map();
11560
+ if (typeof schema.description === "string") {
11561
+ for (const line2 of schema.description.split("\n")) {
11562
+ const m = /^- `([^`]+)`: (.+)$/.exec(line2.trim());
11563
+ if (m) described.set(m[1], m[2]);
11564
+ }
10615
11565
  }
10616
- const local = trimmed.split(/::|\./).pop().toLowerCase().replace(/[^a-z0-9_-]/g, "");
10617
- const hit = [...closureIds].find((id) => id.toLowerCase() === local);
10618
- if (hit) return { $ref: `#/components/schemas/${hit}` };
10619
- return { type: "object", description: `Unresolved type: ${trimmed}` };
11566
+ return schema.enum.filter((v) => typeof v === "string" && v.length > 0).map((name) => ({ name, ...described.has(name) ? { description: described.get(name) } : {} }));
10620
11567
  }
10621
11568
  function operationFor(method2, closureIds) {
10622
11569
  const endpoint = method2.endpoint;
@@ -10629,7 +11576,7 @@ function operationFor(method2, closureIds) {
10629
11576
  responses: {
10630
11577
  "200": {
10631
11578
  description: method2.returns || "Success",
10632
- ...method2.returns && method2.returns.toLowerCase() !== "void" ? { content: { "application/json": { schema: schemaFor(method2.returns, closureIds) } } } : {}
11579
+ ...method2.returns && !returnsNothing(method2.returns) ? { content: { "application/json": { schema: schemaFor(method2.returns, closureIds) } } } : {}
10633
11580
  }
10634
11581
  }
10635
11582
  };
@@ -10664,7 +11611,7 @@ function operationFor(method2, closureIds) {
10664
11611
  }
10665
11612
  function signatureComponent(t, closureIds) {
10666
11613
  const params = t.params ?? [];
10667
- const returns = t.returns ?? "unknown";
11614
+ const returns = t.returns ?? "any";
10668
11615
  const text3 = `(${params.map((p) => `${p.name}${p.optional ? "?" : ""}: ${p.type}`).join(", ")}): ${returns}`;
10669
11616
  return {
10670
11617
  title: t.name,
@@ -10689,9 +11636,7 @@ function signatureFromComponent(id, schema) {
10689
11636
  const ext = raw;
10690
11637
  const typeOf = (entry) => {
10691
11638
  if (typeof entry.type === "string" && entry.type.trim()) return entry.type;
10692
- const schema2 = entry.schema;
10693
- if (schema2 && typeof schema2 === "object" && Object.keys(schema2).length === 0) return "unknown";
10694
- return typeRefFromSchema(schema2);
11639
+ return typeRefFromSchema(entry.schema);
10695
11640
  };
10696
11641
  const params = (Array.isArray(ext.params) ? ext.params : []).filter((p) => !!p && typeof p === "object" && typeof p.name === "string").map((p) => ({
10697
11642
  name: p.name,
@@ -10699,7 +11644,7 @@ function signatureFromComponent(id, schema) {
10699
11644
  ...p.optional === true ? { optional: true } : {},
10700
11645
  ...typeof p.description === "string" ? { description: p.description } : {}
10701
11646
  }));
10702
- const returns = ext.returns && typeof ext.returns === "object" && !Array.isArray(ext.returns) ? typeOf(ext.returns) : "unknown";
11647
+ const returns = ext.returns && typeof ext.returns === "object" && !Array.isArray(ext.returns) ? typeOf(ext.returns) : "any";
10703
11648
  return { id, name: typeof schema.title === "string" ? schema.title : id, kind: "signature", fields: [], params, returns };
10704
11649
  }
10705
11650
  function securitySchemeObject(auth) {
@@ -10773,6 +11718,15 @@ function renderDoc(snapshot, entries, closureIds, opts = {}) {
10773
11718
  }
10774
11719
  const schemas = {};
10775
11720
  for (const t of snapshot.types) {
11721
+ if (t.kind === "enum") {
11722
+ const values = t.values ?? [];
11723
+ schemas[t.id] = { type: "string", title: t.name, enum: values.map((v) => v.name), description: enumDescription(values) };
11724
+ continue;
11725
+ }
11726
+ if (t.holds !== void 0) {
11727
+ schemas[t.id] = { ...schemaFor(t.holds, closureIds), title: t.name };
11728
+ continue;
11729
+ }
10776
11730
  schemas[t.id] = t.kind === "signature" ? signatureComponent(t, closureIds) : {
10777
11731
  type: "object",
10778
11732
  title: t.name,
@@ -10824,17 +11778,73 @@ function isOpenApiDocument(body) {
10824
11778
  return false;
10825
11779
  }
10826
11780
  }
11781
+ function isNullSchema(schema) {
11782
+ return !!schema && typeof schema === "object" && schema.type === "null";
11783
+ }
11784
+ function isTopLevelUnion(text3) {
11785
+ let depth = 0;
11786
+ for (const ch of text3) {
11787
+ if (ch === "<" || ch === "(") depth++;
11788
+ else if (ch === ">" || ch === ")") depth--;
11789
+ else if (ch === "|" && depth === 0) return true;
11790
+ }
11791
+ return false;
11792
+ }
10827
11793
  function typeRefFromSchema(schema) {
10828
- if (!schema) return "json";
11794
+ if (!schema || typeof schema !== "object") return "any";
10829
11795
  const ref = schema.$ref;
10830
- if (typeof ref === "string") return ref.split("/").pop() ?? "json";
10831
- if (schema.type === "array") {
10832
- return `${typeRefFromSchema(schema.items)}[]`;
11796
+ if (typeof ref === "string") return ref.split("/").pop() ?? "any";
11797
+ for (const key of ["anyOf", "oneOf"]) {
11798
+ const members = schema[key];
11799
+ if (!Array.isArray(members) || members.length === 0) continue;
11800
+ const nullable = members.some(isNullSchema);
11801
+ const texts = members.filter((m) => !isNullSchema(m)).map((m) => typeRefFromSchema(m));
11802
+ if (texts.length === 0 || texts.includes("any")) return "any";
11803
+ const text3 = texts.join(" | ");
11804
+ if (!nullable) return text3;
11805
+ return isTopLevelUnion(text3) ? `(${text3})?` : `${text3}?`;
10833
11806
  }
10834
11807
  const t = schema.type;
11808
+ if (Array.isArray(t)) {
11809
+ const types = t.filter((x) => typeof x === "string");
11810
+ const nonNull = types.filter((x) => x !== "null");
11811
+ if (nonNull.length !== 1) return "any";
11812
+ const inner2 = typeRefFromSchema({ ...schema, type: nonNull[0] });
11813
+ return types.includes("null") && inner2 !== "any" ? `${inner2}?` : inner2;
11814
+ }
11815
+ if (t === "array") {
11816
+ const elem = typeRefFromSchema(schema.items);
11817
+ return schema.uniqueItems === true ? `set<${elem}>` : `list<${elem}>`;
11818
+ }
10835
11819
  if (t === "integer") return "int";
10836
- if (typeof t === "string" && t !== "object") return t;
10837
- return "json";
11820
+ if (t === "number") return "float";
11821
+ if (t === "boolean") return "bool";
11822
+ if (t === "string") {
11823
+ if (schema.format === "date") return "date";
11824
+ if (schema.format === "date-time") return "datetime";
11825
+ if (schema.format === "duration") return "duration";
11826
+ if (schema.contentEncoding === "base64" || schema.format === "byte" || schema.format === "binary") return "bytes";
11827
+ return "string";
11828
+ }
11829
+ if (t === "object" || t === void 0) {
11830
+ const additional = schema.additionalProperties;
11831
+ if (additional && typeof additional === "object") {
11832
+ const names = schema.propertyNames;
11833
+ const key = names && typeof names.$ref === "string" ? typeRefFromSchema(names) : names && typeof names.pattern === "string" && names.pattern.includes("[0-9]") ? "int" : "string";
11834
+ return `map<${key}, ${typeRefFromSchema(additional)}>`;
11835
+ }
11836
+ if (additional === true) return "map<string, any>";
11837
+ }
11838
+ return "any";
11839
+ }
11840
+ function inlineEnumNote(schema) {
11841
+ if (!schema || schema.$ref !== void 0 || !Array.isArray(schema.enum)) return void 0;
11842
+ const values = schema.enum.filter((v) => typeof v === "string" || typeof v === "number");
11843
+ return values.length ? `One of: ${values.join(", ")}.` : void 0;
11844
+ }
11845
+ function describedWith(description, note) {
11846
+ const text3 = [description, note].filter((x) => !!x).join(" ");
11847
+ return text3 ? { description: text3 } : {};
10838
11848
  }
10839
11849
  function authFromSecurityScheme(scheme) {
10840
11850
  const desc = typeof scheme.description === "string" ? { description: scheme.description } : {};
@@ -10889,11 +11899,12 @@ function fromOpenApi(document, projectName) {
10889
11899
  const params = [];
10890
11900
  for (const p of op.parameters ?? []) {
10891
11901
  if (typeof p.name !== "string") continue;
11902
+ const schema = p.schema;
10892
11903
  params.push({
10893
11904
  name: p.name,
10894
- type: typeRefFromSchema(p.schema),
11905
+ type: typeRefFromSchema(schema),
10895
11906
  ...p.required === true ? {} : { optional: true },
10896
- ...typeof p.description === "string" ? { description: p.description } : {}
11907
+ ...describedWith(typeof p.description === "string" ? p.description : void 0, inlineEnumNote(schema))
10897
11908
  });
10898
11909
  }
10899
11910
  const bodySchema = op.requestBody?.content?.["application/json"]?.schema;
@@ -10902,7 +11913,12 @@ function fromOpenApi(document, projectName) {
10902
11913
  const required = new Set(bodySchema.required ?? []);
10903
11914
  if (Object.keys(props).length) {
10904
11915
  for (const [pname, pschema] of Object.entries(props)) {
10905
- params.push({ name: pname, type: typeRefFromSchema(pschema), ...required.has(pname) ? {} : { optional: true } });
11916
+ params.push({
11917
+ name: pname,
11918
+ type: typeRefFromSchema(pschema),
11919
+ ...required.has(pname) ? {} : { optional: true },
11920
+ ...describedWith(typeof pschema.description === "string" ? pschema.description : void 0, inlineEnumNote(pschema))
11921
+ });
10906
11922
  }
10907
11923
  } else {
10908
11924
  params.push({ name: "body", type: typeRefFromSchema(bodySchema) });
@@ -10938,6 +11954,14 @@ function fromOpenApi(document, projectName) {
10938
11954
  types.push(signature);
10939
11955
  continue;
10940
11956
  }
11957
+ if (schema.type === "string" && Array.isArray(schema.enum)) {
11958
+ types.push({ id, name: typeof schema.title === "string" ? schema.title : id, kind: "enum", fields: [], values: enumValuesFrom(schema) });
11959
+ continue;
11960
+ }
11961
+ if (typeof schema.type === "string" && SCALAR_SCHEMA_TYPES.has(schema.type) && schema.properties === void 0) {
11962
+ types.push({ id, name: typeof schema.title === "string" ? schema.title : id, kind: "value-object", fields: [], holds: typeRefFromSchema(schema) });
11963
+ continue;
11964
+ }
10941
11965
  const props = schema.properties ?? {};
10942
11966
  const required = new Set(schema.required ?? []);
10943
11967
  types.push({
@@ -10974,31 +11998,26 @@ function fromOpenApi(document, projectName) {
10974
11998
  types
10975
11999
  });
10976
12000
  }
10977
- var yaml2, PRIMITIVES, SIGNATURE_EXTENSION;
12001
+ var yaml2, PRIMITIVE_SCHEMAS, SIGNATURE_EXTENSION, SCALAR_SCHEMA_TYPES;
10978
12002
  var init_openapi = __esm({
10979
12003
  "src/core/openapi.ts"() {
10980
12004
  "use strict";
10981
12005
  yaml2 = __toESM(require("js-yaml"));
10982
12006
  init_models();
10983
- PRIMITIVES = {
12007
+ PRIMITIVE_SCHEMAS = {
10984
12008
  string: { type: "string" },
10985
- number: { type: "number" },
10986
- float: { type: "number" },
10987
- decimal: { type: "number" },
10988
12009
  int: { type: "integer" },
10989
- integer: { type: "integer" },
10990
- boolean: { type: "boolean" },
12010
+ float: { type: "number" },
10991
12011
  bool: { type: "boolean" },
10992
- date: { type: "string", format: "date-time" },
12012
+ bytes: { type: "string", contentEncoding: "base64" },
12013
+ date: { type: "string", format: "date" },
10993
12014
  datetime: { type: "string", format: "date-time" },
10994
- uuid: { type: "string", format: "uuid" },
10995
- json: { type: "object" },
10996
- object: { type: "object" },
12015
+ duration: { type: "string", format: "duration" },
10997
12016
  any: {},
10998
- unknown: {},
10999
12017
  void: {}
11000
12018
  };
11001
12019
  SIGNATURE_EXTENSION = "x-wairon-signature";
12020
+ SCALAR_SCHEMA_TYPES = /* @__PURE__ */ new Set(["string", "integer", "number", "boolean"]);
11002
12021
  }
11003
12022
  });
11004
12023
 
@@ -11027,7 +12046,7 @@ function computeTypeClosure(entries, types, exported = []) {
11027
12046
  queue.push(key);
11028
12047
  };
11029
12048
  const enqueueRef = (ref) => {
11030
- if (BUILTIN_TYPES.has(ref.toLowerCase())) return;
12049
+ if (isTypeVocabulary(ref)) return;
11031
12050
  for (const spec of types) {
11032
12051
  if (matchTypeRef(ref, qualifiedTypeId2(spec))) include(spec);
11033
12052
  }
@@ -11041,13 +12060,13 @@ function computeTypeClosure(entries, types, exported = []) {
11041
12060
  while (queue.length) {
11042
12061
  const spec = included.get(queue.shift());
11043
12062
  for (const field of spec.fields) {
11044
- for (const ref of extractTypeIdentifiers(field.type)) enqueueRef(ref);
12063
+ for (const ref of writtenTypeRefs(field.type, "field")) enqueueRef(ref);
11045
12064
  }
11046
12065
  for (const param of spec.params ?? []) {
11047
- for (const ref of extractTypeIdentifiers(param.type)) enqueueRef(ref);
12066
+ for (const ref of writtenTypeRefs(param.type, "signature-param")) enqueueRef(ref);
11048
12067
  }
11049
12068
  if (spec.returns) {
11050
- for (const ref of extractTypeIdentifiers(spec.returns)) enqueueRef(ref);
12069
+ for (const ref of writtenTypeRefs(spec.returns, "signature-returns")) enqueueRef(ref);
11051
12070
  }
11052
12071
  }
11053
12072
  const usedIds = /* @__PURE__ */ new Set();
@@ -11066,7 +12085,11 @@ function computeTypeClosure(entries, types, exported = []) {
11066
12085
  kind: t.kind,
11067
12086
  fields: t.fields.map(describe3),
11068
12087
  // A signature type travels complete: its params and returns with it.
11069
- ...t.kind === "signature" ? { params: (t.params ?? []).map(describe3), returns: t.returns ?? "unknown" } : {}
12088
+ ...t.kind === "signature" ? { params: (t.params ?? []).map(describe3), returns: t.returns ?? "any" } : {},
12089
+ // An enum travels with its values, in declared order.
12090
+ ...t.kind === "enum" ? { values: (t.values ?? []).map((v) => ({ name: v.name, ...v.description ? { description: v.description } : {} })) } : {},
12091
+ // A named scalar travels with the primitive it holds.
12092
+ ...t.holds !== void 0 ? { holds: t.holds } : {}
11070
12093
  };
11071
12094
  });
11072
12095
  }
@@ -11810,7 +12833,7 @@ function depthOf(family, node) {
11810
12833
  return depth;
11811
12834
  }
11812
12835
  function selectProjects(family, options) {
11813
- const limit = typeof options.recursive === "number" ? options.recursive : options.recursive === false ? 0 : Infinity;
12836
+ const limit = options.memberDepth ?? Infinity;
11814
12837
  const nodes = family.nodes.filter((n) => depthOf(family, n) <= limit);
11815
12838
  const scope = options.scopeSubsystem;
11816
12839
  if (!scope) return nodes.map((node) => ({ node }));
@@ -12617,7 +13640,7 @@ function globalPacksEnabled(config) {
12617
13640
  return config.extensions?.useGlobalPacks ?? GLOBAL_PACKS_DEFAULT;
12618
13641
  }
12619
13642
  function emptyExtensions() {
12620
- return { packNames: [], packs: [], rules: [], profiles: {}, languages: {}, skills: [], patterns: [], guarantees: [], assertions: [], instructions: [], errors: [], selectionFailures: [] };
13643
+ return { packNames: [], packs: [], rules: [], profiles: {}, languages: {}, skills: [], patterns: [], guarantees: [], assertions: [], instructions: [], errors: [], selectionFailures: [], deprecations: [] };
12621
13644
  }
12622
13645
  function globalPacksDir() {
12623
13646
  return process.env.WAIRON_PACKS_DIR ?? path19.join(os4.homedir(), ".wairon", "packs");
@@ -12678,11 +13701,11 @@ function mergePack(out, pack, ref, scope, packDir) {
12678
13701
  Object.assign(out.profiles, pack.profiles);
12679
13702
  for (const [lang, def] of Object.entries(pack.languages)) {
12680
13703
  const key = lang.toLowerCase();
13704
+ if (def.foreignBuiltins !== void 0) {
13705
+ out.deprecations.push(`${pack.name}: languages.${lang}.foreignBuiltins \u2014 ${FOREIGN_BUILTINS_RETIRED}`);
13706
+ }
12681
13707
  const existing = out.languages[key];
12682
- out.languages[key] = existing ? {
12683
- unsupportedFlow: { ...existing.unsupportedFlow, ...def.unsupportedFlow },
12684
- foreignBuiltins: [.../* @__PURE__ */ new Set([...existing.foreignBuiltins, ...def.foreignBuiltins])]
12685
- } : def;
13708
+ out.languages[key] = { unsupportedFlow: { ...existing?.unsupportedFlow, ...def.unsupportedFlow } };
12686
13709
  }
12687
13710
  for (const s of pack.skills) out.skills.push({ ...s, pack: pack.name, packVersion: pack.version, sourcePath: path19.resolve(packDir, s.source) });
12688
13711
  for (const p of pack.patterns) out.patterns.push({ ...p, pack: pack.name });
@@ -12973,7 +13996,7 @@ function findBundledPack(projectRoot2, selection) {
12973
13996
  }
12974
13997
  return best?.dir ?? null;
12975
13998
  }
12976
- var fs14, os4, path19, import_module, import_zod9, ProfileDefSchema, LanguagePackDefSchema, PackSkillSchema, PackInstructionBlockSchema, PackInstructionsSchema, PatternDefSchema, AssertionSelectorSchema, assertionBase, PackAssertionSchema, DeclarativePackSchema, EXTENDABLE_BUILTIN_SKILLS, GLOBAL_PACKS_DEFAULT, PACK_DIR_ENTRIES, isYamlPath;
13999
+ var fs14, os4, path19, import_module, import_zod9, ProfileDefSchema, LanguagePackDefSchema, PackSkillSchema, PackInstructionBlockSchema, PackInstructionsSchema, PatternDefSchema, AssertionSelectorSchema, assertionBase, PackAssertionSchema, DeclarativePackSchema, EXTENDABLE_BUILTIN_SKILLS, GLOBAL_PACKS_DEFAULT, FOREIGN_BUILTINS_RETIRED, PACK_DIR_ENTRIES, isYamlPath;
12977
14000
  var init_extensions = __esm({
12978
14001
  "src/core/extensions.ts"() {
12979
14002
  "use strict";
@@ -13012,7 +14035,7 @@ var init_extensions = __esm({
13012
14035
  });
13013
14036
  LanguagePackDefSchema = import_zod9.z.object({
13014
14037
  unsupportedFlow: import_zod9.z.record(import_zod9.z.string()).default({}),
13015
- foreignBuiltins: import_zod9.z.array(import_zod9.z.string()).default([])
14038
+ foreignBuiltins: import_zod9.z.array(import_zod9.z.string()).optional()
13016
14039
  });
13017
14040
  PackSkillSchema = import_zod9.z.object({
13018
14041
  /** A NEW skill, installed namespaced as `<pack-id>-<id>`. Mutually exclusive with `extends`. */
@@ -13126,6 +14149,7 @@ var init_extensions = __esm({
13126
14149
  });
13127
14150
  EXTENDABLE_BUILTIN_SKILLS = ["sdd-architect", "sdd-narrative", "sdd-auditor", "sdd-implement", "sdd-delegate"];
13128
14151
  GLOBAL_PACKS_DEFAULT = false;
14152
+ FOREIGN_BUILTINS_RETIRED = "nothing replaces it: contracts are written in the neutral type grammar and a language is chosen at L4, so the foreign-builtin check it fed is retired";
13129
14153
  PACK_DIR_ENTRIES = ["pack.yaml", "pack.yml", "pack.cjs", "pack.js", "index.cjs", "index.js"];
13130
14154
  isYamlPath = (p) => /\.ya?ml$/i.test(p);
13131
14155
  }
@@ -13446,7 +14470,7 @@ function buildImplementationMethods(ctx) {
13446
14470
  const component = ctx.componentMap.get(contract.component);
13447
14471
  if (!component) continue;
13448
14472
  if (ctx.isInChainedSubproject(component.subsystem)) continue;
13449
- const draftContext3 = ctx.isImplementationDraft(impl);
14473
+ const draftContext4 = ctx.isImplementationDraft(impl);
13450
14474
  for (const method2 of impl.methods) {
13451
14475
  const sourceFile = methodSourceFile(method2, impl.sourcePath);
13452
14476
  out.push({
@@ -13454,7 +14478,7 @@ function buildImplementationMethods(ctx) {
13454
14478
  method: method2,
13455
14479
  component,
13456
14480
  ...sourceFile !== void 0 ? { sourceFile } : {},
13457
- draftContext: draftContext3
14481
+ draftContext: draftContext4
13458
14482
  });
13459
14483
  }
13460
14484
  }
@@ -13736,7 +14760,7 @@ var init_type_declarations = __esm({
13736
14760
  typeDeclarationsRule = {
13737
14761
  name: "type-declarations",
13738
14762
  judges: "design",
13739
- description: "A type owned by a subsystem must reference an existing one, and a type declaring neither fields nor methods is a placeholder that can inform neither implementers nor the ERD.",
14763
+ description: "A type owned by a subsystem must reference an existing one, and a type declaring neither fields nor methods (nor, for an enum, values, nor, for a signature type, returns, nor, for a named scalar, the primitive it holds) is a placeholder that can inform neither implementers nor the ERD.",
13740
14764
  codes: [
13741
14765
  { code: "INVALID_SUBSYSTEM_REFERENCE", defaultSeverity: "error", summary: "Type references a non-existent owning subsystem" },
13742
14766
  { code: "HOLLOW_TYPE", defaultSeverity: "warning", summary: "Type declares no fields and no methods \u2014 a placeholder that informs neither implementers nor the ERD" }
@@ -13754,7 +14778,8 @@ var init_type_declarations = __esm({
13754
14778
  isDraftCtx
13755
14779
  );
13756
14780
  }
13757
- if ((!t.fields || t.fields.length === 0) && (!t.methods || t.methods.length === 0)) {
14781
+ const models = (t.fields?.length ?? 0) > 0 || (t.methods?.length ?? 0) > 0 || (t.values?.length ?? 0) > 0 || t.returns !== void 0 || t.holds !== void 0;
14782
+ if (!models) {
13758
14783
  ctx.addIssue(
13759
14784
  "warning",
13760
14785
  "HOLLOW_TYPE",
@@ -14018,6 +15043,7 @@ var init_signature_types = __esm({
14018
15043
  );
14019
15044
  continue;
14020
15045
  }
15046
+ if (t.kind === "enum") continue;
14021
15047
  const stated = [...t.params !== void 0 ? ["params"] : [], ...t.returns !== void 0 ? ["returns"] : []];
14022
15048
  if (stated.length === 0) continue;
14023
15049
  ctx.addIssue(
@@ -14072,6 +15098,204 @@ var init_signature_text = __esm({
14072
15098
  }
14073
15099
  });
14074
15100
 
15101
+ // src/core/rules/integrity/type-expressions.ts
15102
+ function draftContext3(ctx, specId, kind) {
15103
+ if (kind === "interface") {
15104
+ const intf = ctx.interfaceMap.get(specId);
15105
+ return !!intf && (ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design");
15106
+ }
15107
+ const type = ctx.types.find((t) => t.id === specId);
15108
+ const sub = type?.subsystem ? ctx.subsystems.find((s) => s.id === type.subsystem) : void 0;
15109
+ return !!sub && (sub.status === "draft" || sub.status === "design");
15110
+ }
15111
+ function siteOf(path92) {
15112
+ const [head2, name] = path92.split(".");
15113
+ return (head2 === "methods" || head2 === "fields") && name ? name : path92;
15114
+ }
15115
+ function located(kind, specId, path92) {
15116
+ return `${path92 ? `"${path92}" of ` : ""}${kind ?? "spec"} "${specId}"`;
15117
+ }
15118
+ function problemMessage(problem, specId) {
15119
+ const where = located(problem.kind, specId, problem.path);
15120
+ const replacement = problem.replacement ? ` Write ${problem.replacement} instead.` : "";
15121
+ switch (problem.code) {
15122
+ case "TYPE_EXPRESSION_INVALID":
15123
+ return `The type at ${where} does not parse under the type grammar: ${problem.detail}.${replacement} Every reader treats the position as opaque any until it does.`;
15124
+ case "TYPE_POSITION_INVALID":
15125
+ return `The type at ${where} breaks a position rule: ${problem.detail}.${replacement}`;
15126
+ case "TYPE_FORM_UNSUPPORTED":
15127
+ return `The type at ${where} uses a form the type grammar leaves out: ${problem.detail}.${replacement} Every reader treats the position as opaque any until it is remodelled; the writer refuses it in a new write.`;
15128
+ case "TYPE_NOT_NEUTRAL": {
15129
+ if (problem.replacement === "int or float") {
15130
+ const proposal = typeProblemIntProposal({ ...problem, specId }) ? ` Its name says a whole number, so \`wairon doctor\` proposes int here \u2014 confirm it by writing int, or write float.` : " Write int or float.";
15131
+ return `The type at ${where} is "${problem.written}": number does not say whether it holds an integer \u2014 int or float?${proposal} Until then it is read as float.`;
15132
+ }
15133
+ return `The type at ${where} is "${problem.written}": ${problem.detail}, so it is read as any.${replacement}`;
15134
+ }
15135
+ }
15136
+ }
15137
+ var typeExpressionsRule;
15138
+ var init_type_expressions = __esm({
15139
+ "src/core/rules/integrity/type-expressions.ts"() {
15140
+ "use strict";
15141
+ init_models();
15142
+ typeExpressionsRule = {
15143
+ name: "type-expressions",
15144
+ judges: "design",
15145
+ description: "Reports what the scan's type canonicalisation recorded (ctx.typeSpellingFacts), one finding per position, located by spec and path: a stored text that does not parse (TYPE_EXPRESSION_INVALID); one that breaks a position rule \u2014 void or async out of place, a map key that is not string, int or an enum, a named scalar's holds that is not one primitive other than void and any, `T??` (TYPE_POSITION_INVALID); one using a form the grammar leaves out \u2014 an inline object shape, an inline function type, a string-literal union, a union mixing in a primitive or a collection, an intersection, a utility type or a tuple (TYPE_FORM_UNSUPPORTED); one naming `number` or a legacy builtin with no neutral meaning (TYPE_NOT_NEUTRAL); and one that is an alias of its canonical spelling (TYPE_SPELLING_STALE, which any save or doctor --fix repairs). Every message names the replacement: `number` asks \"int or float?\" (and says when the doctor repair proposes int), a function type names a signature type, a literal union an enum, an inline object or a mixed union a named value-object. The writer refuses all but the stale spelling at write time; these findings are how a tree that held them before the grammar existed is told, while it still loads and its consumers read the position as opaque any \u2014 which is why only what no real tree holds (a text that does not parse, a broken position rule) is an error. No finding when the run carries no facts (a candidate run).",
15146
+ codes: [
15147
+ { code: "TYPE_EXPRESSION_INVALID", defaultSeverity: "error", summary: "A structured type position does not parse under the type grammar" },
15148
+ { code: "TYPE_POSITION_INVALID", defaultSeverity: "error", summary: "A type position breaks a position rule: void or async out of place, a non-scalar map key, or T??" },
15149
+ { code: "TYPE_FORM_UNSUPPORTED", defaultSeverity: "warning", summary: "A type position uses a form the grammar leaves out \u2014 inline object, inline function type, string-literal union, union mixing in a primitive or collection, intersection, utility type or tuple \u2014 and the message names its named replacement" },
15150
+ { code: "TYPE_NOT_NEUTRAL", defaultSeverity: "warning", summary: "A type position names number (int or float?) or a legacy builtin with no neutral meaning, and the message names the replacement" },
15151
+ { code: "TYPE_SPELLING_STALE", defaultSeverity: "warning", summary: "A stored type position is an alias of its canonical spelling; any save or doctor --fix rewrites it" }
15152
+ ],
15153
+ check(ctx) {
15154
+ const facts = ctx.typeSpellingFacts;
15155
+ if (!facts) return;
15156
+ for (const problem of facts.problems) {
15157
+ if (problem.specId === void 0 || !ctx.isSpecInScope(problem.specId)) continue;
15158
+ const severity = problem.code === "TYPE_EXPRESSION_INVALID" || problem.code === "TYPE_POSITION_INVALID" ? "error" : "warning";
15159
+ ctx.addIssue(
15160
+ severity,
15161
+ problem.code,
15162
+ problemMessage(problem, problem.specId),
15163
+ problem.specId,
15164
+ draftContext3(ctx, problem.specId, problem.kind),
15165
+ void 0,
15166
+ problem.path ? { at: siteOf(problem.path) } : void 0
15167
+ );
15168
+ }
15169
+ for (const respelling of facts.respellings) {
15170
+ if (!ctx.isSpecInScope(respelling.specId)) continue;
15171
+ ctx.addIssue(
15172
+ "warning",
15173
+ "TYPE_SPELLING_STALE",
15174
+ `The type at ${located(respelling.kind, respelling.specId, respelling.path)} is stored as "${respelling.written}", an alias of its canonical spelling "${respelling.stored}" \u2014 which is what every reader is shown. Any save of the spec writes the canonical text, and \`wairon doctor --fix\` rewrites every stale spelling at once.`,
15175
+ respelling.specId,
15176
+ draftContext3(ctx, respelling.specId, respelling.kind),
15177
+ void 0,
15178
+ { at: siteOf(respelling.path) }
15179
+ );
15180
+ }
15181
+ }
15182
+ };
15183
+ }
15184
+ });
15185
+
15186
+ // src/core/rules/integrity/enum-types.ts
15187
+ var enumTypesRule;
15188
+ var init_enum_types = __esm({
15189
+ "src/core/rules/integrity/enum-types.ts"() {
15190
+ "use strict";
15191
+ init_models();
15192
+ enumTypesRule = {
15193
+ name: "enum-types",
15194
+ judges: "design",
15195
+ description: "An enum is a closed, ordered set of named values with optional pure methods, nothing else: an enum without values, with two values equal by reference_resolution.nameKey (every language derives one identifier from both), or carrying fields, params, returns, invariants, componentClass, database, table or linkedEntity is reported; so are values on a type of any other kind, where they mean nothing.",
15196
+ codes: [
15197
+ { code: "ENUM_MEMBERS", defaultSeverity: "error", summary: "An enum has no values, two values equal by nameKey, or a member it cannot carry; or a type of another kind carries values" }
15198
+ ],
15199
+ check(ctx) {
15200
+ for (const t of ctx.types) {
15201
+ if (!ctx.isSpecInScope(t.id)) continue;
15202
+ const sub = t.subsystem ? ctx.subsystems.find((s) => s.id === t.subsystem) : void 0;
15203
+ const isDraft = !!sub && (sub.status === "draft" || sub.status === "design");
15204
+ if (t.kind === "enum") {
15205
+ const values = t.values ?? [];
15206
+ const problems = [];
15207
+ if (values.length === 0) problems.push("lists no values");
15208
+ const seen = /* @__PURE__ */ new Map();
15209
+ for (const value of values) {
15210
+ const key = nameKey(value.name);
15211
+ const earlier = seen.get(key);
15212
+ if (earlier !== void 0) problems.push(`lists "${earlier}" and "${value.name}", which every language derives one identifier from`);
15213
+ else seen.set(key, value.name);
15214
+ }
15215
+ const members = [
15216
+ ...t.fields.length > 0 ? ["fields"] : [],
15217
+ ...t.params !== void 0 ? ["params"] : [],
15218
+ ...t.returns !== void 0 ? ["returns"] : [],
15219
+ ...(t.invariants?.length ?? 0) > 0 ? ["invariants"] : [],
15220
+ ...t.componentClass !== void 0 ? ["componentClass"] : [],
15221
+ ...t.database !== void 0 ? ["database"] : [],
15222
+ ...t.table !== void 0 ? ["table"] : [],
15223
+ ...t.linkedEntity !== void 0 ? ["linkedEntity"] : []
15224
+ ];
15225
+ if (members.length > 0) problems.push(`carries ${members.join(", ")}`);
15226
+ if (problems.length === 0) continue;
15227
+ ctx.addIssue(
15228
+ "error",
15229
+ "ENUM_MEMBERS",
15230
+ `Enum type "${t.id}" ${problems.join("; ")}. An enum is a closed, ordered set of named values, unique by name ignoring case and separators, with optional pure methods and nothing else.`,
15231
+ t.id,
15232
+ isDraft
15233
+ );
15234
+ continue;
15235
+ }
15236
+ if (t.values === void 0) continue;
15237
+ ctx.addIssue(
15238
+ "error",
15239
+ "ENUM_MEMBERS",
15240
+ `Type "${t.id}" is ${t.kind === "entity" ? "an entity" : `a ${t.kind}`} and carries values, which only an enum has. Make it kind enum, or drop them.`,
15241
+ t.id,
15242
+ isDraft
15243
+ );
15244
+ }
15245
+ }
15246
+ };
15247
+ }
15248
+ });
15249
+
15250
+ // src/core/rules/integrity/named-scalar-types.ts
15251
+ var namedScalarTypesRule;
15252
+ var init_named_scalar_types = __esm({
15253
+ "src/core/rules/integrity/named-scalar-types.ts"() {
15254
+ "use strict";
15255
+ namedScalarTypesRule = {
15256
+ name: "named-scalar-types",
15257
+ judges: "design",
15258
+ description: "A named scalar is a value-object that declares `holds: <primitive>` in place of fields \u2014 a newtype or type alias in every language. It models its value and nothing else beside its pure methods and invariants: holds beside fields, componentClass, database, table or linkedEntity is reported, and so is holds on an entity, an enum or a signature type, where it means nothing. Whether holds names one of the primitives is the type-expressions rule's question (TYPE_POSITION_INVALID on the holds position), never a second voice here. Mirrors enum-types and signature-types: judged at validate time, on the type's own fields.",
15259
+ codes: [
15260
+ { code: "NAMED_SCALAR_MEMBERS", defaultSeverity: "error", summary: "A named scalar (a value-object holding a primitive) carries fields or a member it cannot carry; or a type of another kind declares holds" }
15261
+ ],
15262
+ check(ctx) {
15263
+ for (const t of ctx.types) {
15264
+ if (!ctx.isSpecInScope(t.id)) continue;
15265
+ if (t.holds === void 0) continue;
15266
+ const sub = t.subsystem ? ctx.subsystems.find((s) => s.id === t.subsystem) : void 0;
15267
+ const isDraft = !!sub && (sub.status === "draft" || sub.status === "design");
15268
+ if (t.kind !== "value-object") {
15269
+ ctx.addIssue(
15270
+ "error",
15271
+ "NAMED_SCALAR_MEMBERS",
15272
+ `Type "${t.id}" is ${t.kind === "entity" ? "an entity" : `a ${t.kind}`} and declares holds, which only a value-object has: a named scalar is a value-object holding one primitive. Make it a value-object, or drop holds.`,
15273
+ t.id,
15274
+ isDraft
15275
+ );
15276
+ continue;
15277
+ }
15278
+ const members = [
15279
+ ...t.fields.length > 0 ? ["fields"] : [],
15280
+ ...t.componentClass !== void 0 ? ["componentClass"] : [],
15281
+ ...t.database !== void 0 ? ["database"] : [],
15282
+ ...t.table !== void 0 ? ["table"] : [],
15283
+ ...t.linkedEntity !== void 0 ? ["linkedEntity"] : []
15284
+ ];
15285
+ if (members.length === 0) continue;
15286
+ ctx.addIssue(
15287
+ "error",
15288
+ "NAMED_SCALAR_MEMBERS",
15289
+ `Named scalar "${t.id}" holds ${t.holds} and carries ${members.join(", ")}. A named scalar is one primitive under a name, with optional pure methods and invariants and nothing else: drop ${members.length === 1 ? "it" : "them"}, or drop holds and model the value-object's fields.`,
15290
+ t.id,
15291
+ isDraft
15292
+ );
15293
+ }
15294
+ }
15295
+ };
15296
+ }
15297
+ });
15298
+
14075
15299
  // src/core/rules/integrity/public-surface-binding.ts
14076
15300
  var publicSurfaceBindingRule;
14077
15301
  var init_public_surface_binding = __esm({
@@ -15661,11 +16885,11 @@ var init_detail_sufficiency = __esm({
15661
16885
  const contract = ctx.interfaceMap.get(impl.contract);
15662
16886
  if (!contract) continue;
15663
16887
  const component = ctx.componentMap.get(contract.component);
15664
- const draftContext3 = ctx.isImplementationDraft(impl);
16888
+ const draftContext4 = ctx.isImplementationDraft(impl);
15665
16889
  for (const method2 of impl.methods) {
15666
16890
  const detail = effectiveDetail(method2, impl, component);
15667
16891
  if (contract.methods.some((m) => m.name === method2.name) && method2.narrative.length === 0 && detail.level !== "full") {
15668
- dialedDown.push({ implementation: impl, method: method2, component, detail, draftContext: draftContext3 });
16892
+ dialedDown.push({ implementation: impl, method: method2, component, detail, draftContext: draftContext4 });
15669
16893
  }
15670
16894
  }
15671
16895
  }
@@ -17291,6 +18515,31 @@ var init_pack_requirements = __esm({
17291
18515
  }
17292
18516
  });
17293
18517
 
18518
+ // src/core/rules/extension/pack-deprecations.ts
18519
+ var packDeprecationsRule;
18520
+ var init_pack_deprecations = __esm({
18521
+ "src/core/rules/extension/pack-deprecations.ts"() {
18522
+ "use strict";
18523
+ packDeprecationsRule = {
18524
+ name: "pack-deprecations",
18525
+ judges: "design",
18526
+ description: "A loaded pack still declaring a field wairon has deprecated (ctx.ext.deprecations \u2014 today a language's foreignBuiltins, whose foreign-builtin check is retired because contracts are written in the neutral type grammar) is told so: the field is accepted and ignored for one release and removed in the release after. A notice: it never fails the gate.",
18527
+ codes: [
18528
+ { code: "PACK_FIELD_DEPRECATED", defaultSeverity: "notice", summary: "A loaded pack declares a deprecated field, accepted and ignored for one release" }
18529
+ ],
18530
+ check(ctx) {
18531
+ for (const deprecation of ctx.ext.deprecations) {
18532
+ ctx.addIssue(
18533
+ "notice",
18534
+ "PACK_FIELD_DEPRECATED",
18535
+ `Deprecated pack field \u2014 ${deprecation}. It is accepted and ignored in this release and removed in the next; take it out of the pack.`
18536
+ );
18537
+ }
18538
+ }
18539
+ };
18540
+ }
18541
+ });
18542
+
17294
18543
  // src/core/rules/wiring/dependency-cycles.ts
17295
18544
  var cyclesRule;
17296
18545
  var init_dependency_cycles = __esm({
@@ -18018,19 +19267,27 @@ var init_durability_round_trip = __esm({
18018
19267
  });
18019
19268
 
18020
19269
  // src/core/rules/wiring/untyped-seams.ts
18021
- function namesBareType(refs) {
18022
- return refs.some((ref) => BARE_SEAM_TYPES.has(ref.toLowerCase()));
19270
+ function holdsAny(expr) {
19271
+ return expr.form === "primitive" && expr.name === "any" || expr.args.some(holdsAny);
19272
+ }
19273
+ function tokensUntyped(text3) {
19274
+ return extractTypeIdentifiers(text3).some((ref) => UNTYPED_SPELLINGS.has(ref.toLowerCase()));
19275
+ }
19276
+ function positionUntyped(text3, position) {
19277
+ const parse = parseTypePosition(text3, position);
19278
+ if (parse.expression && !parse.problem) return holdsAny(parse.expression);
19279
+ return tokensUntyped(text3);
18023
19280
  }
18024
- var BARE_SEAM_TYPES, untypedSeamRule;
19281
+ var UNTYPED_SPELLINGS, untypedSeamRule;
18025
19282
  var init_untyped_seams = __esm({
18026
19283
  "src/core/rules/wiring/untyped-seams.ts"() {
18027
19284
  "use strict";
18028
19285
  init_models();
18029
- BARE_SEAM_TYPES = /* @__PURE__ */ new Set(["json", "any", "unknown", "object"]);
19286
+ UNTYPED_SPELLINGS = /* @__PURE__ */ new Set(["any", "json", "unknown", "object"]);
18030
19287
  untypedSeamRule = {
18031
19288
  name: "untyped-seams",
18032
19289
  judges: "design",
18033
- 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.",
19290
+ description: "Methods on a subsystem's published components (its public surface) should not take or return `any` \u2014 the one untyped type of the grammar, which object, unknown, json and Json are canonicalised to \u2014 judged through each method's named types and parsed expressions so a prose signature is judged like structured params (its tokens still read leniently): cross-subsystem contracts are the swap seam and must be typed. Generic-dispatch portals carry per-capability types via their dispatch table instead.",
18034
19291
  codes: [
18035
19292
  { code: "UNTYPED_SEAM", defaultSeverity: "warning", summary: "Bare Json/any/unknown/object parameter or return crossing a subsystem public surface" }
18036
19293
  ],
@@ -18048,14 +19305,14 @@ var init_untyped_seams = __esm({
18048
19305
  const offenders = [];
18049
19306
  if (m.params && m.params.length > 0) {
18050
19307
  for (const p of m.params) {
18051
- if (namesBareType(extractTypeIdentifiers(p.type))) {
19308
+ if (positionUntyped(p.type, "param")) {
18052
19309
  offenders.push(`param "${p.name}: ${p.type}"`);
18053
19310
  }
18054
19311
  }
18055
- if (namesBareType(extractTypeIdentifiers(m.returns ?? ""))) {
19312
+ if (m.returns && positionUntyped(m.returns, "returns")) {
18056
19313
  offenders.push(`return "${m.returns}"`);
18057
19314
  }
18058
- } else if (namesBareType(methodTypeRefs(m))) {
19315
+ } else if (methodTypeRefs(m).some((ref) => UNTYPED_SPELLINGS.has(ref.toLowerCase()))) {
18059
19316
  offenders.push(`signature "${m.signature}"`);
18060
19317
  }
18061
19318
  if (offenders.length) {
@@ -19423,6 +20680,33 @@ function originOf(shape) {
19423
20680
  function absence(optional) {
19424
20681
  return optional ? "may be absent" : "is always there";
19425
20682
  }
20683
+ function judgeNamedScalar(ctx, type, file, facts) {
20684
+ if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") return;
20685
+ const dialect = dialectOf(facts);
20686
+ const holds = parseTypePosition(type.holds, "holds").expression;
20687
+ if (!dialect || !holds) return;
20688
+ const symbol = type.symbol ?? type.name;
20689
+ const own = (record2) => !!record2 && Object.prototype.hasOwnProperty.call(record2, symbol);
20690
+ let declared;
20691
+ if (own(facts.aliasTypes)) {
20692
+ const annotation = facts.aliasTypes[symbol];
20693
+ if (dialect.agrees(annotation, holds, /* @__PURE__ */ new Map())) return;
20694
+ declared = `an alias of \`${annotation}\``;
20695
+ } else if (own(facts.typeShapes)) {
20696
+ declared = "a record (an object shape)";
20697
+ } else {
20698
+ return;
20699
+ }
20700
+ ctx.addIssue(
20701
+ "warning",
20702
+ "TYPE_HOLDS_MISMATCH",
20703
+ `Named scalar "${type.id}" holds ${type.holds}, but "${file}" declares "${symbol}" as ${declared}. Every reader of a named scalar is told what one value of it is, and the code says otherwise. Make the declaration an alias of the primitive (in TypeScript, \`type ${symbol} = ${dialect.write(holds)}\`), or change what the type holds.`,
20704
+ type.id,
20705
+ void 0,
20706
+ void 0,
20707
+ { at: symbol }
20708
+ );
20709
+ }
19426
20710
  var typeShapeRule;
19427
20711
  var init_type_shape = __esm({
19428
20712
  "src/core/rules/conformance/type-shape.ts"() {
@@ -19431,7 +20715,7 @@ var init_type_shape = __esm({
19431
20715
  typeShapeRule = {
19432
20716
  name: "type-shape",
19433
20717
  judges: "code",
19434
- description: "Code-to-contract for the DATA: a type spec's `fields` are compared, name by name, against the shape its `sourcePath` actually declares. `typeRealization` asks whether a type EXISTS in code; nothing asked whether it is the shape the spec claims, so a type spec could describe two fields of a six-field record \u2014 and call the two that are optional required \u2014 straight through a lock and a CI gate, while the ERD, the agent briefs and every implementer read it as truth. That is the worst of the code-to-spec gaps, because a wrong signature eventually breaks at a call site and a type spec that lies is only ever read by humans and agents. Two origins answer at exact grade: a DECLARED shape, whose members the file lists, and a DERIVED one, an alias followed one hop to the object literal its schema is built from, where the keys are the members \u2014 and on through a composition the same file writes out (`Base.extend({\u2026})` on a schema constant it declares), the extension's keys laid over the base's. A shape that extends another is judged on what it shows and never on what it omits, since its inherited members are not in this file to count.",
20718
+ description: "Code-to-contract for the DATA: a type spec's `fields` are compared, name by name, against the shape its `sourcePath` actually declares. `typeRealization` asks whether a type EXISTS in code; nothing asked whether it is the shape the spec claims, so a type spec could describe two fields of a six-field record \u2014 and call the two that are optional required \u2014 straight through a lock and a CI gate, while the ERD, the agent briefs and every implementer read it as truth. That is the worst of the code-to-spec gaps, because a wrong signature eventually breaks at a call site and a type spec that lies is only ever read by humans and agents. Two origins answer at exact grade: a DECLARED shape, whose members the file lists, and a DERIVED one, an alias followed one hop to the object literal its schema is built from, where the keys are the members \u2014 and on through a composition the same file writes out (`Base.extend({\u2026})` on a schema constant it declares), the extension's keys laid over the base's. A shape that extends another is judged on what it shows and never on what it omits, since its inherited members are not in this file to count. A NAMED SCALAR (a value-object declaring `holds`) has no field list: its shape is the primitive it holds, compared with the right side of the type alias the file declares under its name, read through the file's dialect (`type PackPath = string` agrees with holds string); an object shape under that name, or an alias the dialect does not read as that primitive, is TYPE_HOLDS_MISMATCH.",
19435
20719
  codes: [
19436
20720
  {
19437
20721
  code: "UNREALIZED_TYPE_FIELD",
@@ -19447,6 +20731,12 @@ var init_type_shape = __esm({
19447
20731
  summary: "The shape at a type's sourcePath carries a field the type spec does not declare \u2014 data the design never described, so no diagram draws it and no brief hands it to an implementer",
19448
20732
  carryable: true
19449
20733
  },
20734
+ {
20735
+ code: "TYPE_HOLDS_MISMATCH",
20736
+ defaultSeverity: "warning",
20737
+ summary: "A named scalar's holds disagrees with what its sourcePath declares under its name \u2014 an alias of another type, or a record \u2014 so every reader is told one value of it is something the code says it is not",
20738
+ carryable: true
20739
+ },
19450
20740
  {
19451
20741
  code: "TYPE_FIELD_OPTIONALITY",
19452
20742
  defaultSeverity: "warning",
@@ -19461,6 +20751,10 @@ var init_type_shape = __esm({
19461
20751
  if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
19462
20752
  const file = pathKey(type.sourcePath);
19463
20753
  const facts = code.factsAt(file);
20754
+ if (type.holds !== void 0) {
20755
+ judgeNamedScalar(ctx, type, file, facts);
20756
+ continue;
20757
+ }
19464
20758
  if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
19465
20759
  const symbol = type.symbol ?? type.name;
19466
20760
  const shapes = facts.typeShapes;
@@ -19517,6 +20811,74 @@ var init_type_shape = __esm({
19517
20811
  }
19518
20812
  });
19519
20813
 
20814
+ // src/core/rules/conformance/enum-values.ts
20815
+ var enumValuesRule;
20816
+ var init_enum_values = __esm({
20817
+ "src/core/rules/conformance/enum-values.ts"() {
20818
+ "use strict";
20819
+ init_models();
20820
+ enumValuesRule = {
20821
+ name: "enum-values",
20822
+ judges: "code",
20823
+ description: "Code-to-contract for an ENUM: an enum type's values are compared, name by name, with the values the declaration at its sourcePath holds \u2014 a string-literal union alias, the array a z.enum constant holds, or a string enum's member values (source_file_facts.enumValues). `typeRealization` already asks whether the declaration exists; nothing asked whether it holds the values the design lists, and an enum is read by every diagram, OpenAPI document and brief as the closed set it claims. Exact grade only; a declaration the analyzer reads no values from is typeRealization's question, never this rule's. Order is not judged: languages differ on whether declaration order is observable.",
20824
+ codes: [
20825
+ {
20826
+ code: "UNREALIZED_ENUM_VALUE",
20827
+ defaultSeverity: "warning",
20828
+ summary: "An enum type lists a value the declaration at its sourcePath does not hold \u2014 the design promises a value the code would reject",
20829
+ carryable: true
20830
+ },
20831
+ {
20832
+ code: "UNDECLARED_ENUM_VALUE",
20833
+ defaultSeverity: "warning",
20834
+ summary: "The declaration at an enum type's sourcePath holds a value the enum does not list \u2014 a value the design never described",
20835
+ carryable: true
20836
+ }
20837
+ ],
20838
+ check(ctx) {
20839
+ const code = ctx.codeIndex();
20840
+ for (const type of ctx.types) {
20841
+ if (type.kind !== "enum" || !type.sourcePath) continue;
20842
+ if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
20843
+ const file = pathKey(type.sourcePath);
20844
+ const facts = code.factsAt(file);
20845
+ const symbol = type.symbol ?? type.name;
20846
+ if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
20847
+ const declared = facts.enumValues;
20848
+ if (!declared || !Object.prototype.hasOwnProperty.call(declared, symbol)) continue;
20849
+ const held = new Set(declared[symbol]);
20850
+ const listed = (type.values ?? []).map((value) => value.name);
20851
+ const listedSet = new Set(listed);
20852
+ const unrealized = listed.filter((value) => !held.has(value));
20853
+ if (unrealized.length > 0) {
20854
+ ctx.addIssue(
20855
+ "warning",
20856
+ "UNREALIZED_ENUM_VALUE",
20857
+ `Enum type "${type.id}" lists ${unrealized.length} value(s) the declaration "${symbol}" in "${file}" does not hold \u2014 ${unrealized.map((value) => `"${value}"`).join(", ")}. The design promises a value the code would reject, and every diagram, OpenAPI document and brief offers it. Add it to the declaration, or drop it from the enum (values compare exactly: a value is data).`,
20858
+ type.id,
20859
+ void 0,
20860
+ void 0,
20861
+ { at: symbol, covers: unrealized }
20862
+ );
20863
+ }
20864
+ const undeclared = declared[symbol].filter((value) => !listedSet.has(value));
20865
+ if (undeclared.length > 0) {
20866
+ ctx.addIssue(
20867
+ "warning",
20868
+ "UNDECLARED_ENUM_VALUE",
20869
+ `The declaration "${symbol}" in "${file}" holds ${undeclared.length} value(s) enum type "${type.id}" does not list \u2014 ${undeclared.map((value) => `"${value}"`).join(", ")}. A value the design never described is one no diagram shows and no brief hands an implementer. List it on the enum, or take it out of the code.`,
20870
+ type.id,
20871
+ void 0,
20872
+ void 0,
20873
+ { at: symbol, covers: undeclared }
20874
+ );
20875
+ }
20876
+ }
20877
+ }
20878
+ };
20879
+ }
20880
+ });
20881
+
19520
20882
  // src/core/rules/conformance/param-conformance.ts
19521
20883
  function omittable(optional) {
19522
20884
  return optional ? "may be left out" : "is required";
@@ -19572,7 +20934,7 @@ var init_param_conformance = __esm({
19572
20934
  paramConformanceRule = {
19573
20935
  name: "param-conformance",
19574
20936
  judges: "code",
19575
- description: "Code-to-contract for the SIGNATURE, the last of the three readings a spec-driven gate never made: a contract declares `params`, and nothing ever compared them to the parameters of the function that realizes the method. A contract could promise an argument the code does not take, take one the contract never mentions \u2014 including a secret \u2014 or name the same argument two different things, and the brief handed to an implementer would carry the contract's version. Parameters are matched by POSITION against the tail of the realization's list, and the declared type is what tells a rename from a dropped argument: `seed(config: HostConfig)` realized as `bootstrapInstance(cfg: HostConfig)` is one parameter under two names, which anything matching on names alone reads as a parameter the code lost. What a realization takes BEFORE the contract's own parameters is wiring, and it is declared on the implementation as `injectedParams` rather than inferred, because an inferred prefix cannot be told from a renamed first argument. A method the named file only CALLS is left to `methodRealization`, which already reports that the body is not here.",
20937
+ description: "Code-to-contract for the SIGNATURE, the last of the three readings a spec-driven gate never made: a contract declares `params`, and nothing ever compared them to the parameters of the function that realizes the method. A contract could promise an argument the code does not take, take one the contract never mentions \u2014 including a secret \u2014 or name the same argument two different things, and the brief handed to an implementer would carry the contract's version. Parameters are matched by POSITION against the tail of the realization's list, and the declared type is what tells a rename from a dropped argument: `seed(config: HostConfig)` realized as `bootstrapInstance(cfg: HostConfig)` is one parameter under two names, which anything matching on names alone reads as a parameter the code lost. Types agree when the code's annotation, read through the dialect of the language the file was analyzed as (type_dialect.agrees), is the contract's canonical type \u2014 so `string[]` in the code agrees with `list<string>` in the contract, TypeScript's `number` with int and float alike, and an annotation the dialect cannot read agrees with nothing. What a realization takes BEFORE the contract's own parameters is wiring, and it is declared on the implementation as `injectedParams` rather than inferred, because an inferred prefix cannot be told from a renamed first argument. A method the named file only CALLS is left to `methodRealization`, which already reports that the body is not here.",
19576
20938
  codes: [
19577
20939
  {
19578
20940
  code: "UNREALIZED_PARAM",
@@ -19609,13 +20971,13 @@ var init_param_conformance = __esm({
19609
20971
  codeNameOf.set(type.id, named2);
19610
20972
  if (type.subsystem) codeNameOf.set(`${type.subsystem}.${type.id}`, named2);
19611
20973
  }
19612
- const normalize3 = (text3) => text3.replace(/\s+/g, " ").trim();
19613
- const typeAgrees = (declared, realized) => {
19614
- if (!realized.type) return false;
19615
- const stated = normalize3(declared.type);
19616
- return stated === realized.type || codeNameOf.get(stated) === realized.type;
20974
+ const typeAgreesIn = (dialect) => (declared, realized) => {
20975
+ if (!realized.type || !dialect) return false;
20976
+ const stated = parseTypePosition(declared.type, "param", !!declared.optional);
20977
+ if (!stated.expression) return false;
20978
+ return dialect.agrees(realized.type, stated.expression, codeNameOf);
19617
20979
  };
19618
- for (const { implementation, method: method2, sourceFile, draftContext: draftContext3 } of ctx.implementationMethods()) {
20980
+ for (const { implementation, method: method2, sourceFile, draftContext: draftContext4 } of ctx.implementationMethods()) {
19619
20981
  const contract = ctx.interfaceMap.get(implementation.contract);
19620
20982
  const declared = contract?.methods.find((m) => m.name === method2.name)?.params ?? [];
19621
20983
  if (declared.length === 0 || !sourceFile) continue;
@@ -19628,6 +20990,7 @@ var init_param_conformance = __esm({
19628
20990
  const candidates = signatures[symbol];
19629
20991
  if (candidates.length === 0) continue;
19630
20992
  const injected = new Set(implementation.injectedParams ?? []);
20993
+ const typeAgrees = typeAgreesIn(dialectOf(facts));
19631
20994
  const judgements = candidates.map((realized) => judge(declared, realized, injected, typeAgrees));
19632
20995
  const subject = candidates.length > 1 ? `every function called "${symbol}" in "${file}"` : `the function "${symbol}" in "${file}"`;
19633
20996
  const opening = subject.charAt(0).toUpperCase() + subject.slice(1);
@@ -19638,7 +21001,7 @@ var init_param_conformance = __esm({
19638
21001
  "UNREALIZED_PARAM",
19639
21002
  `Method "${method2.name}" of contract "${implementation.contract}" declares ${unrealized.length} parameter(s) ${subject} does not take \u2014 ${unrealized.map((found) => found.told).join(", ")}. The contract is promising an argument that would go nowhere, and nothing breaks at a call site to correct it: every brief and every caller built from the contract passes one. Take the parameter in the code, or drop it from the contract.`,
19640
21003
  implementation.id,
19641
- draftContext3,
21004
+ draftContext4,
19642
21005
  void 0,
19643
21006
  { at: method2.name, covers: unrealized.map((found) => found.unit) }
19644
21007
  );
@@ -19650,7 +21013,7 @@ var init_param_conformance = __esm({
19650
21013
  "UNDECLARED_PARAM",
19651
21014
  `${opening} realizing method "${method2.name}" of contract "${implementation.contract}" takes ${undeclared.length} parameter(s) the contract does not declare and no declared injection accounts for \u2014 ${undeclared.map((found) => found.told).join(", ")}. An argument a caller must supply that the design never mentions is how a credential ends up in a signature nobody has read against its contract. Declare it on the contract, name it in the implementation's \`injectedParams\` when whatever wires this component up supplies it (a LEADING run only), or take it out of the signature.`,
19652
21015
  implementation.id,
19653
- draftContext3,
21016
+ draftContext4,
19654
21017
  void 0,
19655
21018
  { at: method2.name, covers: undeclared.map((found) => found.unit) }
19656
21019
  );
@@ -19662,7 +21025,7 @@ var init_param_conformance = __esm({
19662
21025
  "PARAM_NAME_MISMATCH",
19663
21026
  `Method "${method2.name}" of contract "${implementation.contract}" and ${subject} agree on position and type but not on name for ${renamed.length} parameter(s) \u2014 ${renamed.map((f) => f.told).join("; ")}. The contract, the ERD and every brief carry one word and the code answers to another, which is a rename nobody recorded rather than a different argument \u2014 the declared type agreeing is what says so. Rename one side to the other.`,
19664
21027
  implementation.id,
19665
- draftContext3,
21028
+ draftContext4,
19666
21029
  void 0,
19667
21030
  { at: method2.name, covers: renamed.map((found) => found.unit) }
19668
21031
  );
@@ -19674,7 +21037,7 @@ var init_param_conformance = __esm({
19674
21037
  "PARAM_OPTIONALITY",
19675
21038
  `Method "${method2.name}" of contract "${implementation.contract}" and ${subject} disagree about whether ${disagreed.length} parameter(s) may be left out \u2014 ${disagreed.map((f) => f.told).join("; ")}. One of them is telling a caller an argument is required when it is not, or the reverse. Omittable is the code's word for it: a default value and a rest parameter make an argument omittable exactly as a question mark does.`,
19676
21039
  implementation.id,
19677
- draftContext3,
21040
+ draftContext4,
19678
21041
  void 0,
19679
21042
  { at: method2.name, covers: disagreed.map((found) => found.unit) }
19680
21043
  );
@@ -19685,6 +21048,69 @@ var init_param_conformance = __esm({
19685
21048
  }
19686
21049
  });
19687
21050
 
21051
+ // src/core/rules/conformance/async-conformance.ts
21052
+ var asyncConformanceRule;
21053
+ var init_async_conformance = __esm({
21054
+ "src/core/rules/conformance/async-conformance.ts"() {
21055
+ "use strict";
21056
+ init_models();
21057
+ asyncConformanceRule = {
21058
+ name: "async-conformance",
21059
+ judges: "code",
21060
+ description: "Code-to-contract for ASYNC: a contract method whose returns is `async T` is realized by a function that completes later, and one whose returns is not, by one that completes now \u2014 every caller in every language with async/await is shaped by the difference, so a contract that hides it misleads every implementer and translation built from it. Compared with source_file_facts.asyncFunctions for the method's realizing function (under its `symbol`), at exact grade only; where a name has several bodies only what they all agree on is reported, as param-conformance does. A method whose file only calls the function is methodRealization's finding.",
21061
+ codes: [
21062
+ {
21063
+ code: "ASYNC_MISMATCH",
21064
+ defaultSeverity: "warning",
21065
+ summary: "A contract method and the function realizing it disagree about whether the call completes later: the returns says `async T` and the code completes now, or the code is async and the returns does not say so",
21066
+ carryable: true
21067
+ }
21068
+ ],
21069
+ check(ctx) {
21070
+ const code = ctx.codeIndex();
21071
+ for (const { implementation, method: method2, sourceFile, draftContext: draftContext4 } of ctx.implementationMethods()) {
21072
+ const contractMethod = ctx.interfaceMap.get(implementation.contract)?.methods.find((m) => m.name === method2.name);
21073
+ if (!contractMethod || typeof contractMethod.returns !== "string" || !sourceFile) continue;
21074
+ const file = pathKey(sourceFile);
21075
+ const facts = code.factsAt(file);
21076
+ if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
21077
+ const symbol = method2.symbol ?? method2.name;
21078
+ const signatures = facts.functionParams;
21079
+ if (!signatures || !Object.prototype.hasOwnProperty.call(signatures, symbol)) continue;
21080
+ const bodies = signatures[symbol].length;
21081
+ if (bodies === 0) continue;
21082
+ const returns = parseTypeExpression(contractMethod.returns, "returns");
21083
+ if (!returns.expression) continue;
21084
+ const contractAsync = returns.expression.form === "async";
21085
+ const asyncBodies = (facts.asyncFunctions ?? []).filter((name) => name === symbol).length;
21086
+ const subject = bodies > 1 ? `every function called "${symbol}" in "${file}"` : `the function "${symbol}" in "${file}"`;
21087
+ if (contractAsync && asyncBodies === 0) {
21088
+ ctx.addIssue(
21089
+ "warning",
21090
+ "ASYNC_MISMATCH",
21091
+ `Method "${method2.name}" of contract "${implementation.contract}" returns "${contractMethod.returns}", which completes later, but ${subject} completes now \u2014 it is neither declared async nor annotated to return a Promise. Every caller is told to await a call that answers at once. Make the code async, or drop \`async\` from the returns.`,
21092
+ implementation.id,
21093
+ draftContext4,
21094
+ void 0,
21095
+ { at: method2.name }
21096
+ );
21097
+ } else if (!contractAsync && asyncBodies === bodies) {
21098
+ ctx.addIssue(
21099
+ "warning",
21100
+ "ASYNC_MISMATCH",
21101
+ `Method "${method2.name}" of contract "${implementation.contract}" returns "${contractMethod.returns}", which completes now, but ${subject} completes later (declared async, or annotated to return a Promise). Every caller built from the contract forgets to await it. Write the returns as "async ${contractMethod.returns}" \u2014 or, if the code is async by accident, make it complete now.`,
21102
+ implementation.id,
21103
+ draftContext4,
21104
+ void 0,
21105
+ { at: method2.name }
21106
+ );
21107
+ }
21108
+ }
21109
+ }
21110
+ };
21111
+ }
21112
+ });
21113
+
19688
21114
  // src/core/rules/conformance/route-coverage.ts
19689
21115
  function liesUnder2(path92, prefix) {
19690
21116
  return path92 === prefix || path92.startsWith(prefix + "/");
@@ -19758,7 +21184,7 @@ var init_route_coverage = __esm({
19758
21184
  const routes = code.factsAt(file).functionRoutes;
19759
21185
  return routes && Object.prototype.hasOwnProperty.call(routes, via) ? routes[via] : [];
19760
21186
  });
19761
- const draftContext3 = ctx.isComponentDraft(listener.id) || ctx.isComponentDraft(portal.id);
21187
+ const draftContext4 = ctx.isComponentDraft(listener.id) || ctx.isComponentDraft(portal.id);
19762
21188
  const where = holders.map((file) => `"${file}"`).join(", ");
19763
21189
  if (read2.length === 0) {
19764
21190
  ctx.addIssue(
@@ -19766,7 +21192,7 @@ var init_route_coverage = __esm({
19766
21192
  "UNREADABLE_ROUTER",
19767
21193
  `Listener "${listener.id}" mounts portal "${portal.id}" through "${via}" in ${where}, but no branch of "${via}" reads as a route \u2014 so none of its routes were checked against the contract at all. Only one idiom is read: an \`if\` whose conditions, together with those of every enclosing \`if\`, compare \`<request>.method\` with a string and \`parts[i]\` / \`parts.length\` with literals. Silence here would read as a clean router; it is only one this analysis cannot see. Write the router in that idiom, or carry this finding with the reason it cannot be.`,
19768
21194
  anchor,
19769
- draftContext3,
21195
+ draftContext4,
19770
21196
  void 0,
19771
21197
  { at: via }
19772
21198
  );
@@ -19791,7 +21217,7 @@ var init_route_coverage = __esm({
19791
21217
  "UNDECLARED_ROUTE",
19792
21218
  `Portal "${portal.id}"'s router "${via}" (${where}), mounted by listener "${listener.id}", answers ${undeclared.length} route(s) no contract endpoint of "${portal.id}" declares \u2014 ${undeclared.map((key) => `"${key}"`).join(", ")}. A surface the code serves and the design never promised is how a write runs with no contract: no brief, no auth review, no rule reading it. Declare each as a contract method with its endpoint, or take the route out of the router.`,
19793
21219
  anchor,
19794
- draftContext3,
21220
+ draftContext4,
19795
21221
  void 0,
19796
21222
  { at: via, covers: undeclared }
19797
21223
  );
@@ -19803,7 +21229,7 @@ var init_route_coverage = __esm({
19803
21229
  "UNROUTED_ENDPOINT",
19804
21230
  `Portal "${portal.id}" binds ${unrouted.length} HTTP endpoint(s) under listener "${listener.id}"'s mount that no route of its router "${via}" (${where}) answers \u2014 ${unrouted.map((key) => `"${key}"`).join(", ")}. The contract promises a route no request can reach. Route it in the router, or drop the endpoint from the contract.`,
19805
21231
  anchor,
19806
- draftContext3,
21232
+ draftContext4,
19807
21233
  void 0,
19808
21234
  { at: via, covers: unrouted }
19809
21235
  );
@@ -19920,7 +21346,7 @@ var init_export_conformance = __esm({
19920
21346
  }
19921
21347
  }
19922
21348
  }
19923
- for (const { implementation, method: method2, sourceFile, draftContext: draftContext3 } of ctx.implementationMethods()) {
21349
+ for (const { implementation, method: method2, sourceFile, draftContext: draftContext4 } of ctx.implementationMethods()) {
19924
21350
  const handle = method2.exportedVia;
19925
21351
  if (!handle || !sourceFile) continue;
19926
21352
  const file = pathKey(sourceFile);
@@ -19935,7 +21361,7 @@ var init_export_conformance = __esm({
19935
21361
  "UNREALIZED_EXPORT_HANDLE",
19936
21362
  `Method "${method2.name}" in implementation "${implementation.id}" declares exportedVia "${handle}", but "${file}" exports no such name. A handle names the export a consumer imports to REACH the method \u2014 the value that composes it, which \`symbol\` cannot name because \`symbol\` names the function inside \u2014 so one the file does not publish promises a route nobody can import. It allows nothing either: every export of this file is still read against the contracts. Name the binding the file actually exports, export it, or drop the handle.`,
19937
21363
  implementation.id,
19938
- draftContext3
21364
+ draftContext4
19939
21365
  );
19940
21366
  }
19941
21367
  for (const listener of ctx.components) {
@@ -20330,89 +21756,6 @@ var init_coupling_health = __esm({
20330
21756
  }
20331
21757
  });
20332
21758
 
20333
- // src/core/rules/heuristic/signature-language-builtins.ts
20334
- var LANGUAGE_MARKERS, signatureLanguageBuiltinsRule;
20335
- var init_signature_language_builtins = __esm({
20336
- "src/core/rules/heuristic/signature-language-builtins.ts"() {
20337
- "use strict";
20338
- init_models();
20339
- LANGUAGE_MARKERS = {
20340
- rust: /* @__PURE__ */ new Set([
20341
- "u8",
20342
- "u16",
20343
- "u32",
20344
- "u64",
20345
- "u128",
20346
- "usize",
20347
- "i8",
20348
- "i16",
20349
- "i32",
20350
- "i64",
20351
- "i128",
20352
- "isize",
20353
- "f32",
20354
- "f64",
20355
- "vec",
20356
- "box",
20357
- "arc",
20358
- "rc",
20359
- "refcell",
20360
- "cell",
20361
- "mutex",
20362
- "rwlock",
20363
- "str"
20364
- ]),
20365
- typescript: /* @__PURE__ */ new Set(["any", "unknown", "never", "undefined", "promise", "record"]),
20366
- javascript: /* @__PURE__ */ new Set(["promise", "undefined"]),
20367
- python: /* @__PURE__ */ new Set(["dict", "tuple"]),
20368
- csharp: /* @__PURE__ */ new Set(["task"]),
20369
- go: /* @__PURE__ */ new Set(["chan", "rune"])
20370
- };
20371
- signatureLanguageBuiltinsRule = {
20372
- name: "signature-language-builtins",
20373
- judges: "design",
20374
- description: "Contracts must speak the declared target language: builtin types that unambiguously belong to another language family are flagged in method signatures (e.g. `usize` in a TypeScript system, `Promise` in a Rust one). Set targetLanguage on the system (L0) or override per subsystem (L1).",
20375
- codes: [
20376
- { code: "LANGUAGE_FOREIGN_BUILTIN", defaultSeverity: "warning", summary: "Signature uses a builtin from a different language family" }
20377
- ],
20378
- check(ctx) {
20379
- const markersFor = (family) => {
20380
- const base = LANGUAGE_MARKERS[family];
20381
- const extra = ctx.ext.languages[family]?.foreignBuiltins;
20382
- if (!extra?.length) return base;
20383
- return /* @__PURE__ */ new Set([...base ?? [], ...extra.map((s) => s.toLowerCase())]);
20384
- };
20385
- const families = [.../* @__PURE__ */ new Set([...Object.keys(LANGUAGE_MARKERS), ...Object.keys(ctx.ext.languages)])];
20386
- const scans = [];
20387
- for (const intf of ctx.interfaces) {
20388
- const comp = ctx.componentMap.get(intf.component);
20389
- const lang = ctx.targetLanguageFor(comp?.subsystem);
20390
- if (!lang) continue;
20391
- const ownMarkers = markersFor(lang);
20392
- if (!ownMarkers || ownMarkers.size === 0) continue;
20393
- const draft = ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design";
20394
- for (const method2 of intf.methods) {
20395
- for (const ref of methodTypeRefs(method2)) scans.push({ intf, method: method2, ref, lang, ownMarkers, draft });
20396
- }
20397
- }
20398
- for (const scan of scans) {
20399
- const refLower = scan.ref.toLowerCase();
20400
- if (scan.ownMarkers.has(refLower)) continue;
20401
- const foreign = families.find((family) => family !== scan.lang && markersFor(family)?.has(refLower));
20402
- if (!foreign) continue;
20403
- ctx.addIssue(
20404
- "warning",
20405
- "LANGUAGE_FOREIGN_BUILTIN",
20406
- `Method "${scan.method.name}" on interface "${scan.intf.id}" uses "${scan.ref}", a ${foreign} builtin, but the target language here is ${scan.lang}. Use the ${scan.lang} equivalent so implementers generate idiomatic code.`,
20407
- scan.intf.id,
20408
- scan.draft
20409
- );
20410
- }
20411
- }
20412
- };
20413
- }
20414
- });
20415
-
20416
21759
  // src/core/rules/heuristic/narrative-language-constructs.ts
20417
21760
  function constructsOf(step) {
20418
21761
  const constructs = [
@@ -21413,6 +22756,9 @@ var init_repository = __esm({
21413
22756
  init_signature_sources();
21414
22757
  init_signature_types();
21415
22758
  init_signature_text();
22759
+ init_type_expressions();
22760
+ init_enum_types();
22761
+ init_named_scalar_types();
21416
22762
  init_public_surface_binding();
21417
22763
  init_public_surface_consumers();
21418
22764
  init_public_surface_declared_type();
@@ -21465,6 +22811,7 @@ var init_repository = __esm({
21465
22811
  init_pack_resolution();
21466
22812
  init_pack_reproducibility();
21467
22813
  init_pack_requirements();
22814
+ init_pack_deprecations();
21468
22815
  init_dependency_cycles();
21469
22816
  init_dispatch_table_bindings();
21470
22817
  init_dispatch_step_routing();
@@ -21493,13 +22840,14 @@ var init_repository = __esm({
21493
22840
  init_integration_sim_coverage();
21494
22841
  init_type_realization();
21495
22842
  init_type_shape();
22843
+ init_enum_values();
21496
22844
  init_param_conformance();
22845
+ init_async_conformance();
21497
22846
  init_route_coverage();
21498
22847
  init_unclaimed_source();
21499
22848
  init_export_conformance();
21500
22849
  init_carried_debt();
21501
22850
  init_coupling_health();
21502
- init_signature_language_builtins();
21503
22851
  init_narrative_language_constructs();
21504
22852
  init_technology_binding();
21505
22853
  init_technology_boundaries();
@@ -21530,6 +22878,11 @@ var init_repository = __esm({
21530
22878
  signatureSourcesRule,
21531
22879
  signatureTypesRule,
21532
22880
  signatureTextRule,
22881
+ // The type grammar beside the signatures: how each stored type position is
22882
+ // spelled, then what an enum and a named scalar may hold.
22883
+ typeExpressionsRule,
22884
+ enumTypesRule,
22885
+ namedScalarTypesRule,
21533
22886
  // Contracts and the targets narratives name, in four questions with one
21534
22887
  // owner each: does the implementation mirror its contract, does a target
21535
22888
  // inside this tree resolve, does a target that leaves it pin to exactly one
@@ -21694,14 +23047,17 @@ var init_repository = __esm({
21694
23047
  // others named.
21695
23048
  typeRealizationRule,
21696
23049
  typeShapeRule,
23050
+ // An enum's values beside the data shape, and whether the call completes
23051
+ // later beside the signature: the same readings, of their own facts.
23052
+ enumValuesRule,
21697
23053
  paramConformanceRule,
23054
+ asyncConformanceRule,
21698
23055
  routeCoverageRule,
21699
23056
  unclaimedSourceRule,
21700
23057
  exportConformanceRule,
21701
23058
  couplingRule,
21702
- // Target-language fit in two questions: what a CONTRACT may name, and what
21703
- // a NARRATIVE may describe.
21704
- signatureLanguageBuiltinsRule,
23059
+ // Target-language fit: what a NARRATIVE may describe. What a contract may
23060
+ // name is the neutral type grammar's (type-expressions), not a language's.
21705
23061
  narrativeLanguageConstructsRule,
21706
23062
  // The declaration before the consequences: which stereotype may bind a
21707
23063
  // technology at all, then where its name may appear.
@@ -21725,6 +23081,9 @@ var init_repository = __esm({
21725
23081
  // stage): configuration too, and the family run cannot judge a member against
21726
23082
  // a requirement that does not parse.
21727
23083
  packRequirementsRule,
23084
+ // The fields a loaded pack still declares that wairon has deprecated: about
23085
+ // the packs' configuration too, and only ever a notice.
23086
+ packDeprecationsRule,
21728
23087
  // MUST run last, in this order: each audits what the earlier rules did
21729
23088
  // with a declared exception. The debt register first (which carried
21730
23089
  // findings the conformance family actually matched), then the allows
@@ -21871,6 +23230,9 @@ function walkExact(ts, sourceText, fileName) {
21871
23230
  const typeShapes = /* @__PURE__ */ new Map();
21872
23231
  const functionParams = /* @__PURE__ */ new Map();
21873
23232
  const functionRoutes = /* @__PURE__ */ new Map();
23233
+ const asyncFunctions = [];
23234
+ const enumValues = /* @__PURE__ */ new Map();
23235
+ const aliasTypes = /* @__PURE__ */ new Map();
21874
23236
  const mutableBindings = /* @__PURE__ */ new Set();
21875
23237
  const schemaConstants = /* @__PURE__ */ new Map();
21876
23238
  const derivedAliases = [];
@@ -22085,6 +23447,48 @@ function walkExact(ts, sourceText, fileName) {
22085
23447
  if (fn.body) walk2(fn.body, []);
22086
23448
  return routes;
22087
23449
  };
23450
+ const completesLater = (fn) => {
23451
+ const mods = fn.modifiers;
23452
+ if (mods?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) return true;
23453
+ const returned = fn.type;
23454
+ return !!returned && ts.isTypeReferenceNode(returned) && ts.isIdentifier(returned.typeName) && returned.typeName.text === "Promise";
23455
+ };
23456
+ const stringLiteralUnion = (type) => {
23457
+ const members = ts.isUnionTypeNode(type) ? [...type.types] : [type];
23458
+ const values = [];
23459
+ for (const member of members) {
23460
+ if (!ts.isLiteralTypeNode(member) || !ts.isStringLiteral(member.literal)) return void 0;
23461
+ values.push(member.literal.text);
23462
+ }
23463
+ return values;
23464
+ };
23465
+ const zodEnumValues = (expression) => {
23466
+ const strings = (array, hops) => {
23467
+ let node2 = array;
23468
+ while (node2 && (ts.isAsExpression(node2) || ts.isParenthesizedExpression(node2) || ts.isSatisfiesExpression(node2))) node2 = node2.expression;
23469
+ if (node2 && ts.isIdentifier(node2) && hops > 0) return strings(schemaConstants.get(node2.text), hops - 1);
23470
+ if (!node2 || !ts.isArrayLiteralExpression(node2) || node2.elements.length === 0) return void 0;
23471
+ const values = [];
23472
+ for (const element of node2.elements) {
23473
+ if (!ts.isStringLiteral(element) && !ts.isNoSubstitutionTemplateLiteral(element)) return void 0;
23474
+ values.push(element.text);
23475
+ }
23476
+ return values;
23477
+ };
23478
+ let node = expression;
23479
+ const walked = /* @__PURE__ */ new Set();
23480
+ while (node && !walked.has(node)) {
23481
+ walked.add(node);
23482
+ if (ts.isCallExpression(node)) {
23483
+ const callee = node.expression;
23484
+ if (ts.isPropertyAccessExpression(callee) && callee.name.text === "enum") return strings(node.arguments[0], 1);
23485
+ node = callee;
23486
+ } else if (ts.isPropertyAccessExpression(node)) {
23487
+ node = node.expression;
23488
+ } else break;
23489
+ }
23490
+ return void 0;
23491
+ };
22088
23492
  const visit = (node) => {
22089
23493
  const fnName = namedFunctionName(node);
22090
23494
  if (fnName && node.body) {
@@ -22096,6 +23500,7 @@ function walkExact(ts, sourceText, fileName) {
22096
23500
  const signatures = functionParams.get(fnName) ?? [];
22097
23501
  signatures.push(declaredParameters(node));
22098
23502
  functionParams.set(fnName, signatures);
23503
+ if (completesLater(node)) asyncFunctions.push(fnName);
22099
23504
  const routes = collectRoutes(node);
22100
23505
  if (routes.size > 0) {
22101
23506
  const known = functionRoutes.get(fnName) ?? /* @__PURE__ */ new Map();
@@ -22118,10 +23523,17 @@ function walkExact(ts, sourceText, fileName) {
22118
23523
  } else if (ts.isTypeAliasDeclaration(node)) {
22119
23524
  if (ts.isTypeLiteralNode(node.type)) recordDeclaredShape(node.name.text, node.type.members, false);
22120
23525
  else {
23526
+ if (!aliasTypes.has(node.name.text)) aliasTypes.set(node.name.text, node.type.getText(sf));
22121
23527
  const constant = inferredSchemaConstant(node.type);
22122
23528
  if (constant) derivedAliases.push({ name: node.name.text, constant });
23529
+ const literals = stringLiteralUnion(node.type);
23530
+ if (literals) enumValues.set(node.name.text, literals);
22123
23531
  }
22124
23532
  }
23533
+ if (ts.isEnumDeclaration(node)) {
23534
+ const values = node.members.map((member) => member.initializer && (ts.isStringLiteral(member.initializer) || ts.isNoSubstitutionTemplateLiteral(member.initializer)) ? member.initializer.text : void 0);
23535
+ if (values.length > 0 && values.every((v) => v !== void 0)) enumValues.set(node.name.text, values);
23536
+ }
22125
23537
  if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && !schemaConstants.has(node.name.text)) {
22126
23538
  schemaConstants.set(node.name.text, node.initializer);
22127
23539
  }
@@ -22243,8 +23655,17 @@ function walkExact(ts, sourceText, fileName) {
22243
23655
  if (!fields) continue;
22244
23656
  typeShapes.set(alias.name, { origin: "derived", fields, methods: [] });
22245
23657
  }
23658
+ for (const [name, initializer] of schemaConstants) {
23659
+ const values = zodEnumValues(initializer);
23660
+ if (values) enumValues.set(name, values);
23661
+ }
23662
+ for (const alias of derivedAliases) {
23663
+ if (enumValues.has(alias.name)) continue;
23664
+ const values = enumValues.get(alias.constant);
23665
+ if (values && schemaConstants.has(alias.constant)) enumValues.set(alias.name, values);
23666
+ }
22246
23667
  const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
22247
- return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, functionRoutes, mutableBindings, reexportOnly };
23668
+ return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, functionRoutes, asyncFunctions, enumValues, aliasTypes, mutableBindings, reexportOnly };
22248
23669
  }
22249
23670
  function resolveRelativeModule(fromFile, specifier) {
22250
23671
  if (!specifier.startsWith(".")) return null;
@@ -22323,7 +23744,7 @@ function walkDeclaredRoots(roots, exclude, projectRoot2) {
22323
23744
  const record2 = (absolute) => {
22324
23745
  const key = pathKey(path20.relative(projectRoot2, absolute));
22325
23746
  if (seen.has(key) || isExcluded(key)) return;
22326
- if (!EXTENSION_LANGUAGE[path20.extname(key).toLowerCase()]) return;
23747
+ if (!languageOfSourcePath(key)) return;
22327
23748
  seen.add(key);
22328
23749
  found.push(key);
22329
23750
  };
@@ -22405,7 +23826,7 @@ function buildCodeModel(implementations, types, projectRoot2, sourceRoots = [],
22405
23826
  continue;
22406
23827
  }
22407
23828
  const text3 = buffer.toString("utf8");
22408
- const language = EXTENSION_LANGUAGE[path20.extname(sourcePath).toLowerCase()];
23829
+ const language = languageOfSourcePath(sourcePath);
22409
23830
  let analyzed;
22410
23831
  if (language === "typescript" || language === "javascript") {
22411
23832
  const ts = resolveTypeScript(projectRoot2);
@@ -22433,6 +23854,9 @@ function buildCodeModel(implementations, types, projectRoot2, sourceRoots = [],
22433
23854
  typeShapes: Object.fromEntries(facts.typeShapes),
22434
23855
  functionParams: Object.fromEntries(facts.functionParams),
22435
23856
  functionRoutes: Object.fromEntries([...facts.functionRoutes].map(([k, v]) => [k, [...v.values()]])),
23857
+ asyncFunctions: [...facts.asyncFunctions],
23858
+ enumValues: Object.fromEntries(facts.enumValues),
23859
+ aliasTypes: Object.fromEntries(facts.aliasTypes),
22436
23860
  topLevelMutableBindings: [...facts.mutableBindings],
22437
23861
  reexportOnly: facts.reexportOnly
22438
23862
  };
@@ -22585,7 +24009,7 @@ function findTestsReferencing(methods, projectRoot2, testRoots) {
22585
24009
  }
22586
24010
  return found;
22587
24011
  }
22588
- var fs15, path20, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, NAMED_IMPORT_BINDINGS_RE, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS, IMPORT_FROM_RE, DYNAMIC_IMPORT_RE, IDENTIFIER_ONLY_RE, NAMED_REEXPORT_RE, STAR_REEXPORT_RE;
24012
+ var fs15, path20, import_module2, C_FAMILY_COMMENTS, NAMED_IMPORT_BINDINGS_RE, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS, IMPORT_FROM_RE, DYNAMIC_IMPORT_RE, IDENTIFIER_ONLY_RE, NAMED_REEXPORT_RE, STAR_REEXPORT_RE;
22589
24013
  var init_source_analysis = __esm({
22590
24014
  "src/core/source-analysis.ts"() {
22591
24015
  "use strict";
@@ -22593,30 +24017,6 @@ var init_source_analysis = __esm({
22593
24017
  path20 = __toESM(require("path"));
22594
24018
  import_module2 = require("module");
22595
24019
  init_models();
22596
- EXTENSION_LANGUAGE = {
22597
- ".ts": "typescript",
22598
- ".tsx": "typescript",
22599
- ".mts": "typescript",
22600
- ".cts": "typescript",
22601
- ".js": "javascript",
22602
- ".jsx": "javascript",
22603
- ".mjs": "javascript",
22604
- ".cjs": "javascript",
22605
- ".py": "python",
22606
- ".rs": "rust",
22607
- ".go": "go",
22608
- ".cs": "csharp",
22609
- ".java": "java",
22610
- ".c": "c",
22611
- ".h": "c",
22612
- ".cpp": "cpp",
22613
- ".cc": "cpp",
22614
- ".hpp": "cpp",
22615
- ".rb": "ruby",
22616
- ".php": "php",
22617
- ".kt": "kotlin",
22618
- ".swift": "swift"
22619
- };
22620
24020
  C_FAMILY_COMMENTS = { lineComments: ["//"], blockComments: [["/*", "*/"]] };
22621
24021
  NAMED_IMPORT_BINDINGS_RE = /\bimport\s*\{([^}]*)\}/g;
22622
24022
  JS_PATTERNS = {
@@ -22920,7 +24320,7 @@ function buildRuleContext(opts) {
22920
24320
  const authoredTypeRefs = new Set((family?.authoredReferences ?? []).filter((r) => r.position === "type" && r.binding !== "local" && memberOf(r.specId) === void 0).map((r) => r.authored));
22921
24321
  const isTypeResolved = (ref, generics) => {
22922
24322
  const refLower = ref.toLowerCase();
22923
- if (BUILTIN_TYPES.has(refLower)) return true;
24323
+ if (isTypeVocabulary(refLower)) return true;
22924
24324
  if (generics.has(refLower)) return true;
22925
24325
  if (ownTypes.some((spec) => typeMatchesRef(spec, ref))) return true;
22926
24326
  const [first, ...rest] = ref.split(/::|\./);
@@ -23134,7 +24534,7 @@ function buildRuleContext(opts) {
23134
24534
  }
23135
24535
  return projectNaming;
23136
24536
  };
23137
- const isBuiltinType = (ref) => BUILTIN_TYPES.has(ref.toLowerCase());
24537
+ const isBuiltinType = (ref) => isTypeVocabulary(ref);
23138
24538
  const getRuleSeverity = (ruleCode, defaultSeverity, isDraftContext, subsystemId) => {
23139
24539
  if (rules?.sddRuleSeverity?.[ruleCode]) {
23140
24540
  return rules.sddRuleSeverity[ruleCode];
@@ -23312,7 +24712,7 @@ function buildRuleContext(opts) {
23312
24712
  documentationConfigFor,
23313
24713
  namingConfigFor,
23314
24714
  isBuiltinType,
23315
- ext: { profiles: extensions.profiles, languages: extensions.languages, patterns: extensions.patterns, guarantees: extensions.guarantees, assertions: extensions.assertions, packSelections: opts.packSelections ?? [], selectionFailures: extensions.selectionFailures ?? [], packRequirements: opts.packRequirements ?? [] },
24715
+ ext: { profiles: extensions.profiles, languages: extensions.languages, patterns: extensions.patterns, guarantees: extensions.guarantees, assertions: extensions.assertions, packSelections: opts.packSelections ?? [], selectionFailures: extensions.selectionFailures ?? [], packRequirements: opts.packRequirements ?? [], deprecations: extensions.deprecations ?? [] },
23316
24716
  variants: opts.variants ?? [],
23317
24717
  surfaceSnapshots,
23318
24718
  ...opts.projectIdentity ? { projectIdentity: opts.projectIdentity } : {},
@@ -23321,6 +24721,7 @@ function buildRuleContext(opts) {
23321
24721
  ...opts.projectFamily ? { projectFamily: opts.projectFamily } : {},
23322
24722
  ...opts.exportUsages ? { exportUsages: opts.exportUsages } : {},
23323
24723
  ...opts.signatureFacts ? { signatureFacts: opts.signatureFacts } : {},
24724
+ ...opts.typeSpellingFacts ? { typeSpellingFacts: opts.typeSpellingFacts } : {},
23324
24725
  pinnedExternals,
23325
24726
  codeModel: opts.codeModel ?? emptyCodeModel(),
23326
24727
  roundTripIssues: opts.roundTripIssues,
@@ -23829,7 +25230,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
23829
25230
  let extensions;
23830
25231
  let treatAllAsComplete = false;
23831
25232
  let packSelections;
23832
- if (rulesOrOptions && ("scopeSubsystem" in rulesOrOptions || "recursive" in rulesOrOptions || "rules" in rulesOrOptions || "projectType" in rulesOrOptions || "extensions" in rulesOrOptions || "treatAllAsComplete" in rulesOrOptions || "family" in rulesOrOptions || "packSelections" in rulesOrOptions)) {
25233
+ if (rulesOrOptions && ("scopeSubsystem" in rulesOrOptions || "memberDepth" in rulesOrOptions || "rules" in rulesOrOptions || "projectType" in rulesOrOptions || "extensions" in rulesOrOptions || "treatAllAsComplete" in rulesOrOptions || "family" in rulesOrOptions || "packSelections" in rulesOrOptions)) {
23833
25234
  const opts = rulesOrOptions;
23834
25235
  rules = opts.rules;
23835
25236
  projectType = opts.projectType ?? "backend";
@@ -23839,7 +25240,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
23839
25240
  packSelections = opts.packSelections;
23840
25241
  }
23841
25242
  extensions ??= loadProjectExtensions();
23842
- scanAllSpecs({ recursive: true });
25243
+ scanAllSpecs();
23843
25244
  const issues = [];
23844
25245
  clearLoaderIssues();
23845
25246
  const boundConfig = boundConfiguration();
@@ -23917,6 +25318,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
23917
25318
  const producers = new Set(family.references.filter((r) => r.consumer === "").map((r) => r.producer));
23918
25319
  const exportUsages = [...producers].map((producer) => exportUsage("", producer));
23919
25320
  const signatures = signatureFacts();
25321
+ const typeSpellings = typeSpellingFacts();
23920
25322
  const ctx = buildRuleContext({
23921
25323
  system,
23922
25324
  subsystems,
@@ -23939,6 +25341,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
23939
25341
  projectFamily: family,
23940
25342
  exportUsages,
23941
25343
  signatureFacts: signatures,
25344
+ typeSpellingFacts: typeSpellings,
23942
25345
  pinnedExternals,
23943
25346
  // By-name selections only: a legacy path ref pins nothing to check. A dry
23944
25347
  // run supplies its candidate's; otherwise the stored ones.
@@ -24058,7 +25461,12 @@ function judgePartAlone(config, scopeSubsystem) {
24058
25461
  issues.push(issue("warning", "EXTERNAL_CHECK_UNAVAILABLE", `"${by}" names "${key}", which neither this part nor its pinned excerpt of "${parentId}" holds \u2014 used since the pin: re-pin with \`wairon externals pin\` while the parent is on disk.`, by));
24059
25462
  }
24060
25463
  const ownSignatures = signatureFacts();
24061
- const resolved = resolveTree(
25464
+ const ownTypeSpellings = typeSpellingFacts();
25465
+ const partTypeSpellings = {
25466
+ respellings: ownTypeSpellings.respellings.filter((r) => isOwn(r.specId)),
25467
+ problems: ownTypeSpellings.problems.filter((p) => p.specId === void 0 || isOwn(p.specId))
25468
+ };
25469
+ const resolved = resolveSignatures(
24062
25470
  [...own.interfaces, ...context.interfaces],
24063
25471
  [...own.components, ...context.components],
24064
25472
  [...own.types, ...context.types]
@@ -24097,6 +25505,7 @@ function judgePartAlone(config, scopeSubsystem) {
24097
25505
  projectFamily: graph(),
24098
25506
  exportUsages: [],
24099
25507
  signatureFacts: partSignatures,
25508
+ typeSpellingFacts: partTypeSpellings,
24100
25509
  pinnedExternals: [],
24101
25510
  packSelections: packs.filter((p) => typeof p !== "string"),
24102
25511
  packRequirements: [],
@@ -24184,7 +25593,7 @@ function builtinProjectKinds() {
24184
25593
  return [...PROJECT_KINDS];
24185
25594
  }
24186
25595
  function computeGateStateId() {
24187
- scanAllSpecs({ recursive: true });
25596
+ scanAllSpecs();
24188
25597
  const content = computeOwnStateId();
24189
25598
  const extensions = loadProjectExtensions();
24190
25599
  let gate = {};
@@ -24873,16 +26282,16 @@ function bindReferencedMembers(node, root) {
24873
26282
  out.push({ ...base, sourceKind: "hosted", hosted: member.source.hosted });
24874
26283
  continue;
24875
26284
  }
24876
- const located = root.referenced.find((r) => r.alias === member.alias);
26285
+ const located2 = root.referenced.find((r) => r.alias === member.alias);
24877
26286
  const sourceKind = member.storage === "git" ? "git" : "path";
24878
- const directory = located?.directory ?? (member.storage === "path" ? path22.resolve(node.directory, member.source.path ?? "") : void 0);
24879
- const commit3 = located?.commit ?? member.source.commit;
26287
+ const directory = located2?.directory ?? (member.storage === "path" ? path22.resolve(node.directory, member.source.path ?? "") : void 0);
26288
+ const commit3 = located2?.commit ?? member.source.commit;
24880
26289
  out.push({
24881
26290
  ...base,
24882
26291
  sourceKind,
24883
26292
  ...directory !== void 0 ? { directory } : {},
24884
26293
  ...commit3 !== void 0 ? { commit: commit3 } : {},
24885
- ...located?.availability === "unavailable" ? { problem: located.reason } : {}
26294
+ ...located2?.availability === "unavailable" ? { problem: located2.reason } : {}
24886
26295
  });
24887
26296
  }
24888
26297
  return out;
@@ -25720,7 +27129,8 @@ function emptyIndex() {
25720
27129
  type: {},
25721
27130
  group: {}
25722
27131
  },
25723
- signatures: { sources: [], staleTexts: [] }
27132
+ signatures: { sources: [], staleTexts: [] },
27133
+ typeSpellings: emptyTypeSpellingFacts()
25724
27134
  };
25725
27135
  }
25726
27136
  function isWithin(dir, file) {
@@ -26075,7 +27485,7 @@ function bareRawReferences(kind, spec) {
26075
27485
  const out = [];
26076
27486
  const seen = /* @__PURE__ */ new Set();
26077
27487
  const add2 = (position, value, generics = /* @__PURE__ */ new Set()) => {
26078
- if (!value || value.includes("::") || BUILTIN_TYPES.has(value.toLowerCase()) || generics.has(value.toLowerCase())) return;
27488
+ if (!value || value.includes("::") || isTypeVocabulary(value) || generics.has(value.toLowerCase())) return;
26079
27489
  const k = `${position}|${value}`;
26080
27490
  if (seen.has(k)) return;
26081
27491
  seen.add(k);
@@ -26495,6 +27905,49 @@ function bindImport(raw, suppliers, p) {
26495
27905
  }
26496
27906
  };
26497
27907
  }
27908
+ function typePositionText(spec, at) {
27909
+ const [head2, name, member, param] = at.split(".");
27910
+ const named2 = (list2, key) => Array.isArray(list2) ? list2.find((x) => x?.name === key) : void 0;
27911
+ if (head2 === "returns") return spec.returns;
27912
+ if (head2 === "params") return named2(spec.params, name)?.type;
27913
+ if (head2 === "fields") return named2(spec.fields, name)?.type;
27914
+ if (head2 !== "methods") return void 0;
27915
+ const method2 = named2(spec.methods, name);
27916
+ return member === "returns" ? method2?.returns : named2(method2?.params, param)?.type;
27917
+ }
27918
+ function writtenSignatureTexts(raws) {
27919
+ const texts = /* @__PURE__ */ new Map();
27920
+ const record2 = (kind, specId, methods) => {
27921
+ for (const m of methods) {
27922
+ if (m.signatureFrom !== void 0) continue;
27923
+ const derived = deriveMethodSignature(m);
27924
+ if (derived !== void 0) texts.set(`${kind}|${specId}|${m.name}`, derived);
27925
+ }
27926
+ };
27927
+ for (const raw of raws) {
27928
+ for (const intf of raw.index.interfaces) record2("interface", intf.id, intf.methods ?? []);
27929
+ for (const type of raw.index.types) record2("type", type.id, type.methods ?? []);
27930
+ }
27931
+ return texts;
27932
+ }
27933
+ function canonicalizeRootTypes(raws) {
27934
+ const facts = emptyTypeSpellingFacts();
27935
+ for (const raw of raws) {
27936
+ raw.index.interfaces = raw.index.interfaces.map((intf) => {
27937
+ const read2 = interfaceCanonicalTypes(intf);
27938
+ facts.respellings.push(...read2.respellings);
27939
+ facts.problems.push(...read2.problems);
27940
+ return read2.spec;
27941
+ });
27942
+ raw.index.types = raw.index.types.map((type) => {
27943
+ const read2 = typeCanonicalTypes(type);
27944
+ facts.respellings.push(...read2.respellings);
27945
+ facts.problems.push(...read2.problems);
27946
+ return read2.spec;
27947
+ });
27948
+ }
27949
+ return facts;
27950
+ }
26498
27951
  function bindRootReferences(raw, raws, readables) {
26499
27952
  const bound2 = /* @__PURE__ */ new Map();
26500
27953
  for (const p of raw.pending) {
@@ -26587,7 +28040,8 @@ function specKind(raw) {
26587
28040
  if ("component" in raw && Array.isArray(raw.methods)) return "interface";
26588
28041
  if ("contract" in raw && Array.isArray(raw.methods)) return "implementation";
26589
28042
  if ("kind" in raw && Array.isArray(raw.fields)) return "type";
26590
- if (raw.kind === "signature") return "type";
28043
+ if (raw.kind === "signature" || raw.kind === "enum") return "type";
28044
+ if ("kind" in raw && typeof raw.holds === "string") return "type";
26591
28045
  return void 0;
26592
28046
  }
26593
28047
  function rewriteSpecRefs(raw, remap) {
@@ -26883,12 +28337,12 @@ function pairSteps(before, after) {
26883
28337
  const gapBefore = before.slice(oi + 1, oj).map((step, k) => ({ step, at: oi + 1 + k }));
26884
28338
  const gapAfter = after.slice(ni + 1, nj).map((step, k) => ({ step, at: ni + 1 + k }));
26885
28339
  const similar = commonRun(gapBefore, gapAfter, (x, y) => recognisablySame(x.step, y.step)).map(([x, y]) => [gapBefore[x].at, gapAfter[y].at]);
26886
- const inner = [[oi, ni], ...similar, [oj, nj]];
26887
- for (let k = 0; k < inner.length - 1; k++) {
26888
- const [a0, b0] = inner[k];
26889
- const [a1, b1] = inner[k + 1];
28340
+ const inner2 = [[oi, ni], ...similar, [oj, nj]];
28341
+ for (let k = 0; k < inner2.length - 1; k++) {
28342
+ const [a0, b0] = inner2[k];
28343
+ const [a1, b1] = inner2[k + 1];
26890
28344
  if (a1 - a0 === b1 - b0) for (let d = 1; d < a1 - a0; d++) pairs.push([a0 + d, b0 + d]);
26891
- if (k < inner.length - 2) pairs.push(inner[k + 1]);
28345
+ if (k < inner2.length - 2) pairs.push(inner2[k + 1]);
26892
28346
  }
26893
28347
  if (g < bounds2.length - 2) pairs.push(bounds2[g + 1]);
26894
28348
  }
@@ -27173,6 +28627,7 @@ function moveChangeReports(edits, dryRun) {
27173
28627
  changes,
27174
28628
  ineffective: [],
27175
28629
  notices: [],
28630
+ respellings: [],
27176
28631
  testsToRevisit: [],
27177
28632
  summary: edit4.before === null ? `${dryRun ? "Would create" : "Created"} ${edit4.kind} "${edit4.id}" to receive the moved methods.` : `${dryRun ? "Would move" : "Moved"} into ${edit4.kind} "${edit4.id}": ${changes.length} change${changes.length === 1 ? "" : "s"}.`
27178
28633
  }));
@@ -27462,6 +28917,9 @@ function snapshotSpecFiles() {
27462
28917
  function signatureFacts() {
27463
28918
  return current().signatureFacts();
27464
28919
  }
28920
+ function typeSpellingFacts() {
28921
+ return current().typeSpellingFacts();
28922
+ }
27465
28923
  function findLegacySpecFiles() {
27466
28924
  return current().findLegacySpecFiles();
27467
28925
  }
@@ -27559,7 +29017,7 @@ var init_specs2 = __esm({
27559
29017
  SpecWorkspace = class {
27560
29018
  constructor(rootDir) {
27561
29019
  this.cachedIndex = null;
27562
- this.cachedRecursive = null;
29020
+ this.cachedDepth = null;
27563
29021
  this.cachedSpecDirs = [];
27564
29022
  this.cachedSignature = null;
27565
29023
  this.lastSignatureCheckMs = 0;
@@ -27588,7 +29046,7 @@ var init_specs2 = __esm({
27588
29046
  }
27589
29047
  invalidate() {
27590
29048
  this.cachedIndex = null;
27591
- this.cachedRecursive = null;
29049
+ this.cachedDepth = null;
27592
29050
  this.cachedSpecDirs = [];
27593
29051
  this.cachedConfigFiles = [];
27594
29052
  this.cachedRoots = [];
@@ -27603,8 +29061,8 @@ var init_specs2 = __esm({
27603
29061
  // Scanning
27604
29062
  // -------------------------------------------------------------------------
27605
29063
  scanAll(options) {
27606
- const recursive = options?.recursive ?? true;
27607
- if (this.cachedIndex && this.cachedRecursive === recursive) {
29064
+ const maxDepth = options?.memberDepth ?? Infinity;
29065
+ if (this.cachedIndex && this.cachedDepth === maxDepth) {
27608
29066
  const now = Date.now();
27609
29067
  if (now - this.lastSignatureCheckMs <= SIGNATURE_TTL_MS) return this.cachedIndex;
27610
29068
  this.lastSignatureCheckMs = now;
@@ -27612,10 +29070,9 @@ var init_specs2 = __esm({
27612
29070
  this.invalidate();
27613
29071
  }
27614
29072
  this.loaderIssues = [];
27615
- this.cachedRecursive = recursive;
29073
+ this.cachedDepth = maxDepth;
27616
29074
  this.scanVisitedSpecDirs = [];
27617
29075
  this.scanVisitedConfigFiles = [];
27618
- const maxDepth = typeof recursive === "number" ? recursive : recursive ? Infinity : 0;
27619
29076
  const scan = this.scanFamily(maxDepth);
27620
29077
  this.cachedIndex = scan.index;
27621
29078
  this.cachedRoots = scan.raws.map((raw) => raw.record);
@@ -27656,6 +29113,8 @@ var init_specs2 = __esm({
27656
29113
  raws.forEach((raw) => this.bindAliases(raw, raws));
27657
29114
  const readables = raws.map((raw) => readableOf(raw));
27658
29115
  resolveFamilyTables(raws);
29116
+ const writtenTexts = writtenSignatureTexts(raws);
29117
+ const typeSpellings = canonicalizeRootTypes(raws);
27659
29118
  raws.forEach((raw) => bindRootReferences(raw, raws, readables));
27660
29119
  const index = emptyIndex();
27661
29120
  for (const raw of raws) {
@@ -27670,7 +29129,11 @@ var init_specs2 = __esm({
27670
29129
  const signatures = resolveTree(index.interfaces, index.components, index.types, raws.flatMap((r) => r.record.authoredReferences));
27671
29130
  index.interfaces = signatures.interfaces;
27672
29131
  index.types = signatures.types;
27673
- index.signatures = signatures.facts;
29132
+ index.signatures = {
29133
+ ...signatures.facts,
29134
+ staleTexts: signatures.facts.staleTexts.filter((stale) => writtenTexts.get(`${stale.kind}|${stale.specId}|${stale.method}`) !== stale.stored)
29135
+ };
29136
+ index.typeSpellings = typeSpellings;
27674
29137
  return { index, raws, readables };
27675
29138
  }
27676
29139
  /**
@@ -27816,26 +29279,26 @@ var init_specs2 = __esm({
27816
29279
  decl.kind = "project";
27817
29280
  return;
27818
29281
  }
27819
- const located = this.memberRoot(raw, decl);
27820
- if (located === null) return;
27821
- if ("unavailable" in located) {
29282
+ const located2 = this.memberRoot(raw, decl);
29283
+ if (located2 === null) return;
29284
+ if ("unavailable" in located2) {
27822
29285
  if (asserted === "project" || pinnedAsMember(raw.dir, decl.alias)) {
27823
29286
  decl.kind = "project";
27824
- raw.record.referenced.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason: located.unavailable });
29287
+ raw.record.referenced.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason: located2.unavailable });
27825
29288
  return;
27826
29289
  }
27827
29290
  decl.kind = "part";
27828
- this.memberProblem(raw, "part-unavailable", decl.alias, located.unavailable);
27829
- raw.record.parts.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason: located.unavailable });
29291
+ this.memberProblem(raw, "part-unavailable", decl.alias, located2.unavailable);
29292
+ raw.record.parts.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason: located2.unavailable });
27830
29293
  return;
27831
29294
  }
27832
- const kind = projectContentAt(located.dir) ? "project" : "part";
29295
+ const kind = projectContentAt(located2.dir) ? "project" : "part";
27833
29296
  decl.kind = kind;
27834
29297
  if (asserted !== void 0 && asserted !== kind) {
27835
29298
  this.memberProblem(raw, "kind-mismatch", decl.alias, `the member "${decl.alias}" is asserted \`as: ${asserted}\`, but its content makes it a ${kind}: ${kind === "project" ? "its tree declares an id, holds an L0 or carries a lock \u2014 drop the `as`, or remove that content with `wairon member demote`" : "its tree declares no id, holds no L0 and carries no lock \u2014 drop the `as`, or create the project content with `wairon member promote`"}`);
27836
29299
  }
27837
- if (kind === "part") this.readPart(raw, decl, located.dir, located.commit);
27838
- else if (decl.storage !== "contained") this.recordReferenced(raw, decl, located.dir, located.commit);
29300
+ if (kind === "part") this.readPart(raw, decl, located2.dir, located2.commit);
29301
+ else if (decl.storage !== "contained") this.recordReferenced(raw, decl, located2.dir, located2.commit);
27839
29302
  }
27840
29303
  /**
27841
29304
  * A referenced project member (stage 8): where its root is — the sibling
@@ -28734,7 +30197,8 @@ var init_specs2 = __esm({
28734
30197
  * the owning project.
28735
30198
  */
28736
30199
  prepareInterfaceForWrite(spec) {
28737
- const stored = { ...spec, methods: (spec.methods ?? []).map((m) => storedMethodSignature(m)) };
30200
+ const canonical3 = interfaceCanonicalTypes(spec).spec;
30201
+ const stored = { ...canonical3, methods: (canonical3.methods ?? []).map((m) => storedMethodSignature(m)) };
28738
30202
  return this.relativizeSpec("interface", stored);
28739
30203
  }
28740
30204
  prepareImplementationForWrite(spec, carry = true) {
@@ -28745,9 +30209,14 @@ var init_specs2 = __esm({
28745
30209
  const partDir = this.partHolding(this.getImplementationPath(spec.id, spec.contract))?.part.directory;
28746
30210
  return partDir ? partRelativeFilePaths(relative26, this.rootDir, partDir) : relative26;
28747
30211
  }
28748
- /** A type written in its stored form — every params-bearing method's text derived — relative to the owning project. */
30212
+ /**
30213
+ * A type written in its stored form — every type position canonical (one
30214
+ * that is not canonical written as it stands), every params-bearing method's
30215
+ * text derived from them — relative to the owning project.
30216
+ */
28749
30217
  prepareTypeForWrite(spec) {
28750
- const stored = { ...spec, methods: (spec.methods ?? []).map((m) => storedTypeMethod(m)) };
30218
+ const canonical3 = typeCanonicalTypes(spec).spec;
30219
+ const stored = { ...canonical3, methods: (canonical3.methods ?? []).map((m) => storedTypeMethod(m)) };
28751
30220
  return this.relativizeSpec("type", stored);
28752
30221
  }
28753
30222
  prepareGroupForWrite(spec) {
@@ -28958,7 +30427,7 @@ var init_specs2 = __esm({
28958
30427
  if (!pathExists(p)) return null;
28959
30428
  try {
28960
30429
  const raw = readSpecFile(p);
28961
- return resolveTree([InterfaceSpecSchema.parse(raw)], index.components, index.types).interfaces[0];
30430
+ return resolveTree([interfaceCanonicalTypes(InterfaceSpecSchema.parse(raw)).spec], index.components, index.types).interfaces[0];
28962
30431
  } catch (e) {
28963
30432
  this.loaderIssues.push({
28964
30433
  severity: "error",
@@ -29534,6 +31003,33 @@ var init_specs2 = __esm({
29534
31003
  index.listProjectRoots();
29535
31004
  return this.cachedIndex.signatures;
29536
31005
  }
31006
+ /**
31007
+ * A delta's merged spec with every type position canonical, IN PLACE (an
31008
+ * interface or a type; nothing else holds type positions). Answers the
31009
+ * respellings the write applies: each alias the merge holds — written by the
31010
+ * delta — and each alias the stored file still holds at a position the merge
31011
+ * kept, which the scan recorded and the save rewrites with it. A position
31012
+ * that is not canonical stays as written.
31013
+ */
31014
+ canonicalizeMerged(kind, merged) {
31015
+ if (kind !== "interface" && kind !== "type") return [];
31016
+ const read2 = kind === "interface" ? interfaceCanonicalTypes(merged) : typeCanonicalTypes(merged);
31017
+ Object.assign(merged, read2.spec);
31018
+ const respelled = new Set(read2.respellings.map((r) => r.path));
31019
+ const fromFile = this.scanAll().typeSpellings.respellings.filter((r) => r.specId === merged.id && r.kind === kind && !respelled.has(r.path) && typePositionText(merged, r.path) === r.stored);
31020
+ return [...read2.respellings, ...fromFile];
31021
+ }
31022
+ /**
31023
+ * ispec_index.typeSpellingFacts — what the current scan's type
31024
+ * canonicalisation recorded: every stored type position that is an alias of
31025
+ * its canonical spelling, and every one that is not canonical at all. Read
31026
+ * from the cached scan, rescanning first when the tree changed.
31027
+ */
31028
+ typeSpellingFacts() {
31029
+ const index = this;
31030
+ index.listProjectRoots();
31031
+ return this.cachedIndex.typeSpellings;
31032
+ }
29537
31033
  snapshotSpecFiles() {
29538
31034
  const index = this.scanAll();
29539
31035
  const snapshot = /* @__PURE__ */ new Map();
@@ -30144,6 +31640,7 @@ var init_specs2 = __esm({
30144
31640
  const qualifiedDelta = qualifyDeltaRefs(mergeableDelta);
30145
31641
  const mergedResult = mergeDelta(result, qualifiedDelta);
30146
31642
  for (const field of unsetFields) delete mergedResult[field];
31643
+ const respellings = this.canonicalizeMerged(kind, mergedResult);
30147
31644
  deriveMergedTexts(kind, mergedResult);
30148
31645
  const withDefaults = deltaSchema.safeParse(mergedResult);
30149
31646
  if (withDefaults.success) {
@@ -30184,6 +31681,7 @@ var init_specs2 = __esm({
30184
31681
  changes,
30185
31682
  ineffective,
30186
31683
  notices,
31684
+ respellings: [],
30187
31685
  testsToRevisit: [],
30188
31686
  summary: `No change to ${kind} "${id}" \u2014 the delta matches what is stored, so nothing ${dryRun ? "would be" : "was"} written.`
30189
31687
  };
@@ -30200,6 +31698,7 @@ var init_specs2 = __esm({
30200
31698
  changes,
30201
31699
  ineffective,
30202
31700
  notices,
31701
+ respellings,
30203
31702
  testsToRevisit: [],
30204
31703
  ...stripped.length ? { strippedKeys: stripped } : {},
30205
31704
  summary: `Dry run on ${kind} "${id}": ${changes.length} change${changes.length === 1 ? "" : "s"} would be made. Nothing was written.`
@@ -30218,6 +31717,7 @@ var init_specs2 = __esm({
30218
31717
  changes,
30219
31718
  ineffective,
30220
31719
  notices,
31720
+ respellings,
30221
31721
  // The store never searches for tests: it holds the tree and knows
30222
31722
  // nothing about the rule engine that reads code. The GATED write above
30223
31723
  // it fills this in, which is also the only write path a human drives.
@@ -33579,6 +35079,19 @@ function composeAgentBrief(agentId) {
33579
35079
  ## Project guidance
33580
35080
 
33581
35081
  ${guidance.trim()}
35082
+ `;
35083
+ }
35084
+ const language = implementationLanguage(record2);
35085
+ const dialect = language ? typeDialectFor(language) : null;
35086
+ const typeMapping = dialect ? dialect.mappingLines() : void 0;
35087
+ if (typeMapping) {
35088
+ instructions = `${instructions.trimEnd()}
35089
+
35090
+ ## Types in ${language}
35091
+
35092
+ Contracts speak wairon's neutral type grammar; write each type in ${language} as:
35093
+
35094
+ ${typeMapping.map((line2) => `- ${line2}`).join("\n")}
33582
35095
  `;
33583
35096
  }
33584
35097
  const profile = deriveExecutionProfile(record2);
@@ -33592,10 +35105,28 @@ ${guidance.trim()}
33592
35105
  readPaths: record2.readPaths,
33593
35106
  instructions,
33594
35107
  variantGuidance: record2.variantGuidance || void 0,
35108
+ ...typeMapping ? { typeMapping } : {},
33595
35109
  profile: budget ? profile : void 0,
33596
35110
  budget
33597
35111
  };
33598
35112
  }
35113
+ function implementationLanguage(record2) {
35114
+ const owned = new Set(record2.ownedPaths);
35115
+ const implementations = loadImplementationSpecs().filter((impl) => implementationSourceFiles(impl).some((file) => owned.has(file)));
35116
+ for (const impl of implementations) {
35117
+ for (const technology of impl.technologies ?? []) {
35118
+ const name = typeof technology === "string" ? technology : technology.name;
35119
+ if (typeDialectFor(name)) return name.toLowerCase();
35120
+ }
35121
+ }
35122
+ for (const impl of implementations) {
35123
+ for (const file of implementationSourceFiles(impl)) {
35124
+ const language = languageOfSourcePath(file);
35125
+ if (language) return language;
35126
+ }
35127
+ }
35128
+ return void 0;
35129
+ }
33599
35130
  function memberHop(agentId) {
33600
35131
  const at = agentId.indexOf("::");
33601
35132
  if (at < 0) return null;
@@ -33641,6 +35172,8 @@ var init_agent_resolver = __esm({
33641
35172
  init_budget_policy();
33642
35173
  init_specs2();
33643
35174
  init_specs();
35175
+ init_code_model();
35176
+ init_type_dialects();
33644
35177
  init_variants();
33645
35178
  projectFilesCache = /* @__PURE__ */ new Map();
33646
35179
  UnknownAgentError = class extends WaironError {
@@ -36845,7 +38378,6 @@ var require_dist = __commonJS({
36845
38378
  "# languages:",
36846
38379
  "# rust:",
36847
38380
  "# unsupportedFlow: {}",
36848
- "# foreignBuiltins: []",
36849
38381
  "languages: {}",
36850
38382
  "",
36851
38383
  "# Reusable, versioned architecture patterns:",
@@ -37072,12 +38604,12 @@ var require_dist = __commonJS({
37072
38604
  const suggestedFileName = `${sealed.name}-${sealed.version}.wpack`;
37073
38605
  return { archive: archiveBytes, info, suggestedFileName };
37074
38606
  }
37075
- function inspectArchive3(archiveBytes) {
37076
- const entries = listEntries(archiveBytes);
38607
+ function inspectArchive3(archive) {
38608
+ const entries = listEntries(archive);
37077
38609
  if (!entries.some((entry) => entry.path === ENVELOPE_FILENAME)) {
37078
38610
  throw new Error("not a .wpack archive (missing wairon-pack.yaml envelope)");
37079
38611
  }
37080
- const envelopeBytes = inflateEntry(archiveBytes, ENVELOPE_FILENAME);
38612
+ const envelopeBytes = inflateEntry(archive, ENVELOPE_FILENAME);
37081
38613
  const manifest = parseManifest(decode(envelopeBytes));
37082
38614
  const compatible = checkCompatibility(manifest, SDK_VERSION);
37083
38615
  const fileEntries = entries.filter((entry) => entry.kind === "file");
@@ -37095,13 +38627,13 @@ var require_dist = __commonJS({
37095
38627
  if (manifest.minWaironVersion !== void 0) info.minWaironVersion = manifest.minWaironVersion;
37096
38628
  return info;
37097
38629
  }
37098
- function extractPack3(archiveBytes, destDir, limits) {
38630
+ function extractPack3(archive, destDir, limits) {
37099
38631
  const effectiveLimits = limits === void 0 ? defaultLimits() : limits;
37100
- const entries = listEntries(archiveBytes);
38632
+ const entries = listEntries(archive);
37101
38633
  const plan7 = planExtraction(entries, effectiveLimits);
37102
38634
  const files = [];
37103
38635
  for (const approvedPath of plan7.paths) {
37104
- const contents = inflateEntry(archiveBytes, approvedPath);
38636
+ const contents = inflateEntry(archive, approvedPath);
37105
38637
  files.push({ path: approvedPath, contents });
37106
38638
  }
37107
38639
  const envelope = files.find((file) => file.path === ENVELOPE_FILENAME);
@@ -37158,28 +38690,28 @@ var require_dist = __commonJS({
37158
38690
  const suggestedFileName = `${slugify2(projectName)}.waitree`;
37159
38691
  return { archive: archiveBytes, manifest: sealed, suggestedFileName, fileCount: files.length };
37160
38692
  }
37161
- function inspectTreeArchive3(archiveBytes) {
37162
- const entries = listEntries(archiveBytes);
38693
+ function inspectTreeArchive3(archive) {
38694
+ const entries = listEntries(archive);
37163
38695
  if (!entries.some((entry) => entry.path === TREE_ENVELOPE_FILENAME)) {
37164
38696
  throw new Error("not a .waitree archive (missing wairon-tree.yaml envelope)");
37165
38697
  }
37166
- const envelopeBytes = inflateEntry(archiveBytes, TREE_ENVELOPE_FILENAME);
38698
+ const envelopeBytes = inflateEntry(archive, TREE_ENVELOPE_FILENAME);
37167
38699
  const manifest = parseTreeManifest(decode(envelopeBytes));
37168
38700
  const compatible = checkTreeCompatibility(manifest, SDK_VERSION);
37169
38701
  const fileEntries = entries.filter((entry) => entry.kind === "file");
37170
38702
  const totalUncompressedBytes = entries.reduce((sum, entry) => sum + entry.uncompressedSize, 0);
37171
38703
  return { manifest, entryCount: fileEntries.length, totalUncompressedBytes, compatible };
37172
38704
  }
37173
- function extractTreeArchive3(archiveBytes, destDir, limits, refuseExecutableEntries) {
38705
+ function extractTreeArchive3(archive, destDir, limits, refuseExecutableEntries) {
37174
38706
  const effectiveLimits = limits === void 0 ? defaultTreeLimits() : limits;
37175
- const entries = listEntries(archiveBytes);
38707
+ const entries = listEntries(archive);
37176
38708
  const plan7 = planTreeExtraction(entries, effectiveLimits);
37177
38709
  if (refuseExecutableEntries === true) {
37178
38710
  assertPortableTreePaths(plan7.paths);
37179
38711
  }
37180
38712
  const files = [];
37181
38713
  for (const approvedPath of plan7.paths) {
37182
- const contents = inflateEntry(archiveBytes, approvedPath);
38714
+ const contents = inflateEntry(archive, approvedPath);
37183
38715
  files.push({ path: approvedPath, contents });
37184
38716
  }
37185
38717
  const envelope = files.find((file) => file.path === TREE_ENVELOPE_FILENAME);
@@ -37212,9 +38744,9 @@ var require_dist = __commonJS({
37212
38744
  }
37213
38745
  function readInnerIdentity(files, manifest) {
37214
38746
  const innerPath = manifest.kind === "code" ? "package.json" : manifest.entry;
37215
- const inner = files.find((file) => file.path === innerPath);
37216
- if (!inner) throw new Error(`pack build: inner pack manifest "${innerPath}" is missing`);
37217
- const parsed = manifest.kind === "code" ? JSON.parse(decode(inner.contents)) : (0, import_js_yaml3.load)(decode(inner.contents));
38747
+ const inner2 = files.find((file) => file.path === innerPath);
38748
+ if (!inner2) throw new Error(`pack build: inner pack manifest "${innerPath}" is missing`);
38749
+ const parsed = manifest.kind === "code" ? JSON.parse(decode(inner2.contents)) : (0, import_js_yaml3.load)(decode(inner2.contents));
37218
38750
  const name = parsed?.name;
37219
38751
  const version = parsed?.version;
37220
38752
  if (typeof name !== "string" || typeof version !== "string") {
@@ -37628,6 +39160,59 @@ var init_signature_repair = __esm({
37628
39160
  }
37629
39161
  });
37630
39162
 
39163
+ // src/core/type-spelling-repair.ts
39164
+ function repairTypeSpellings(apply4) {
39165
+ const facts = typeSpellingFacts();
39166
+ const held = new Set(signatureFacts().sources.filter((f) => f.outcome === "restated" && f.differs).map((f) => f.interfaceId));
39167
+ const repairs = /* @__PURE__ */ new Map();
39168
+ const repairOf = (specId, kind) => {
39169
+ const key = `${kind}:${specId}`;
39170
+ let repair = repairs.get(key);
39171
+ if (!repair) {
39172
+ repair = { specId, kind, rewritten: [], proposals: [], authorNeeded: [] };
39173
+ repairs.set(key, repair);
39174
+ }
39175
+ return repair;
39176
+ };
39177
+ const own = (specId) => !specId.includes("::");
39178
+ for (const respelling of facts.respellings) {
39179
+ if (!own(respelling.specId)) continue;
39180
+ if (respelling.kind === "interface" && held.has(respelling.specId)) continue;
39181
+ repairOf(respelling.specId, respelling.kind).rewritten.push(respelling);
39182
+ }
39183
+ for (const problem of facts.problems) {
39184
+ if (problem.specId === void 0 || problem.kind === void 0 || !own(problem.specId)) continue;
39185
+ const repair = repairOf(problem.specId, problem.kind);
39186
+ const proposal = typeProblemIntProposal(problem);
39187
+ if (proposal) repair.proposals.push(proposal);
39188
+ else repair.authorNeeded.push(problem);
39189
+ }
39190
+ const planned = [...repairs.values()];
39191
+ const rewriting = planned.filter((repair) => repair.rewritten.length > 0);
39192
+ if (apply4 && rewriting.length > 0) {
39193
+ const interfaces = new Map(loadInterfaceSpecs().map((i) => [i.id, i]));
39194
+ const types = new Map(loadTypeSpecs().map((t) => [t.id, t]));
39195
+ for (const repair of rewriting) {
39196
+ if (repair.kind === "interface") {
39197
+ const intf = interfaces.get(repair.specId);
39198
+ if (intf) saveInterfaceSpec(intf);
39199
+ continue;
39200
+ }
39201
+ const type = types.get(repair.specId);
39202
+ if (type) saveSpec("type", type);
39203
+ }
39204
+ invalidateSpecCache();
39205
+ }
39206
+ return planned;
39207
+ }
39208
+ var init_type_spelling_repair = __esm({
39209
+ "src/core/type-spelling-repair.ts"() {
39210
+ "use strict";
39211
+ init_specs2();
39212
+ init_models();
39213
+ }
39214
+ });
39215
+
37631
39216
  // src/models/lock.ts
37632
39217
  function describeApprover(who) {
37633
39218
  const label7 = who.name ? `${who.name} (${who.id})` : who.id;
@@ -38210,7 +39795,7 @@ function parentPinState(parent) {
38210
39795
  }
38211
39796
  function getStatusReport(options = {}, decor) {
38212
39797
  const mark = fillDecor(decor);
38213
- scanAllSpecs({ recursive: options.recursive ?? true });
39798
+ scanAllSpecs({ memberDepth: options.memberDepth });
38214
39799
  const system = loadSystemSpec();
38215
39800
  const loaderErrors = getLoaderIssues();
38216
39801
  if (loaderErrors.length > 0) {
@@ -38412,6 +39997,11 @@ var init_status = __esm({
38412
39997
  });
38413
39998
 
38414
39999
  // src/core/index.ts
40000
+ function resolveSignatures(interfaces, components, types) {
40001
+ const canonicalInterfaces = interfaces.map((intf) => interfaceCanonicalTypes(intf).spec);
40002
+ const canonicalTypes = types.map((type) => typeCanonicalTypes(type).spec);
40003
+ return resolveTree(canonicalInterfaces, components, canonicalTypes);
40004
+ }
38415
40005
  function resolveDomains2() {
38416
40006
  return resolveDomains();
38417
40007
  }
@@ -38433,9 +40023,10 @@ var init_core = __esm({
38433
40023
  init_project_config();
38434
40024
  init_domain_projector();
38435
40025
  init_domain_curator();
40026
+ init_models();
40027
+ init_signature_sources2();
38436
40028
  init_rules();
38437
40029
  init_specs2();
38438
- init_signature_sources2();
38439
40030
  init_external_producers();
38440
40031
  init_part_context();
38441
40032
  init_provision();
@@ -38457,6 +40048,7 @@ var init_core = __esm({
38457
40048
  init_stereotype_migration();
38458
40049
  init_narrative_repair();
38459
40050
  init_signature_repair();
40051
+ init_type_spelling_repair();
38460
40052
  init_approval();
38461
40053
  init_approver();
38462
40054
  init_specs2();
@@ -38863,12 +40455,14 @@ function noticesFrom(verdict) {
38863
40455
  ...verdict.notices.map((n) => `${n.code} (notice): ${n.message}`)
38864
40456
  ];
38865
40457
  }
38866
- function componentCandidateGate(options = candidateOptions(), storedOwner) {
40458
+ function componentCandidateGate(options = candidateOptions(), storedOwner, stored) {
38867
40459
  return {
38868
40460
  gate: (kind, merged) => {
38869
40461
  refuseUnknownOwner(kind, merged, storedOwner);
38870
40462
  const restated = kind === "interface" ? restatedSources(merged.methods) : [];
38871
40463
  if (restated.length) throw new Error(`${restatedSourceRefusal(String(merged.id), restated)} Nothing was written.`);
40464
+ const written = deltaWrittenTypeProblems(kind, merged, stored ?? null);
40465
+ if (written.length) throw new Error(`${typeProblemRefusal(kind, String(merged.id), written)} Nothing was written.`);
38872
40466
  if (kind !== "component") return;
38873
40467
  const verdict = validateComponentCandidate(merged, options);
38874
40468
  if (verdict.errors.length) throw new Error(formatCandidateRefusal(verdict));
@@ -38876,6 +40470,22 @@ function componentCandidateGate(options = candidateOptions(), storedOwner) {
38876
40470
  }
38877
40471
  };
38878
40472
  }
40473
+ function canonicalTypesOf(kind, spec) {
40474
+ if (!spec || typeof spec !== "object") return null;
40475
+ if (kind === "interface") return interfaceCanonicalTypes(spec);
40476
+ if (kind === "type") return typeCanonicalTypes(spec);
40477
+ return null;
40478
+ }
40479
+ function deltaWrittenTypeProblems(kind, merged, stored) {
40480
+ const now = canonicalTypesOf(kind, merged)?.problems ?? [];
40481
+ if (now.length === 0) return [];
40482
+ const before = new Set((canonicalTypesOf(kind, stored)?.problems ?? []).map((p) => `${p.path}|${p.written}`));
40483
+ return now.filter((p) => !before.has(`${p.path}|${p.written}`));
40484
+ }
40485
+ function typeProblemRefusal(kind, id, problems) {
40486
+ const lines = problems.map((p) => `${p.code} at ${p.path}: ${p.detail}${p.replacement ? ` \u2014 write ${p.replacement} instead` : ""}.`);
40487
+ return `Refusing to write ${kind} "${id}": ${problems.length === 1 ? "a type position is" : `${problems.length} type positions are`} not in the neutral type grammar. ${lines.join(" ")} Aliases (string[], T | null, boolean, Promise<T>, ...) are accepted and respelled; these forms have no canonical spelling.`;
40488
+ }
38879
40489
  function restatedSources(methods) {
38880
40490
  if (!Array.isArray(methods)) return [];
38881
40491
  return methods.filter((m) => m && typeof m === "object" && m.signatureFrom !== void 0 && (m.params !== void 0 || m.returns !== void 0)).map((m) => String(m.name));
@@ -38962,6 +40572,11 @@ function applyRestatement(restatement, loaded, parent) {
38962
40572
  `Refusing to write ${restatement.kind} "${String(candidate.id)}": ${unsigned.map((n) => `"${n}"`).join(", ")} ${unsigned.length === 1 ? "states" : "state"} neither params${restatement.kind === "interface" ? ", a signatureFrom" : ""} nor a prose signature, so nothing says what the method takes. Nothing was written.`
38963
40573
  );
38964
40574
  }
40575
+ const types = canonicalTypesOf(restatement.kind, candidate);
40576
+ if (types && types.problems.length) {
40577
+ return refused(restatement, replacedExisting, `${typeProblemRefusal(restatement.kind, String(candidate.id), types.problems)} Nothing was written.`);
40578
+ }
40579
+ if (types) Object.assign(candidate, types.spec);
38965
40580
  const derivedNotices = deriveStatedTexts(restatement.kind, candidate);
38966
40581
  const labelErrors = resolveLabelsOf(restatement.kind, candidate);
38967
40582
  if (labelErrors.length) {
@@ -38975,7 +40590,8 @@ function applyRestatement(restatement, loaded, parent) {
38975
40590
  ...status2.status ? { status: status2.status } : {},
38976
40591
  replacedExisting,
38977
40592
  notices: [...existing ? rewriteNotices(restatement.kind, existing, candidate, carried, cleared) : [], ...derivedNotices],
38978
- changedMethods: changedMethodsOf(restatement.kind, existing, candidate)
40593
+ changedMethods: changedMethodsOf(restatement.kind, existing, candidate),
40594
+ respellings: types?.respellings ?? []
38979
40595
  };
38980
40596
  }
38981
40597
  function parseCandidate(kind, candidate) {
@@ -39006,7 +40622,7 @@ function parseCandidate(kind, candidate) {
39006
40622
  };
39007
40623
  }
39008
40624
  function refused(restatement, replacedExisting, refusal2) {
39009
- return { spec: restatement.spec, refusal: refusal2, replacedExisting, notices: [], changedMethods: [] };
40625
+ return { spec: restatement.spec, refusal: refusal2, replacedExisting, notices: [], changedMethods: [], respellings: [] };
39010
40626
  }
39011
40627
  function statusForCreate(restatement, existing) {
39012
40628
  if (!STATUSED_KINDS.has(restatement.kind)) return {};
@@ -39146,6 +40762,7 @@ function writeSpec(restatement) {
39146
40762
  replacedExisting: application.replacedExisting,
39147
40763
  ...application.status ? { status: application.status } : {},
39148
40764
  notices: [...gateNotices, ...persisted.notices, ...application.notices],
40765
+ respellings: application.respellings,
39149
40766
  testsToRevisit
39150
40767
  };
39151
40768
  }
@@ -39201,7 +40818,7 @@ function updateSpecGated(kind, id, delta, dryRun) {
39201
40818
  const bound2 = candidateOptions();
39202
40819
  const testRoots = bound2.rules?.conformance?.testRoots ?? [];
39203
40820
  const stored = loadSpec(kind, id);
39204
- const gate = componentCandidateGate(bound2, stored?.subsystem);
40821
+ const gate = componentCandidateGate(bound2, stored?.subsystem, stored);
39205
40822
  const report3 = updateSpec(kind, id, delta, gate, dryRun);
39206
40823
  const changed = changedMethods(report3);
39207
40824
  if (changed.length > 0 && testRoots.length > 0) {
@@ -39249,6 +40866,7 @@ var init_authoring = __esm({
39249
40866
  "src/core/authoring.ts"() {
39250
40867
  "use strict";
39251
40868
  init_specs();
40869
+ init_type_grammar();
39252
40870
  init_authoring_core();
39253
40871
  init_authoring_validator();
39254
40872
  init_candidate2();
@@ -42950,6 +44568,7 @@ function renderChangeReport(report3) {
42950
44568
  if (report3.strippedKeys?.length) {
42951
44569
  lines.push("", `DROPPED FROM THE STORED FILE (${report3.dryRun ? "would be " : ""}removed \u2014 the schema does not know them):`, ...report3.strippedKeys.map((k) => `- ${k}`));
42952
44570
  }
44571
+ lines.push(...renderRespellings(report3.respellings));
42953
44572
  lines.push(...renderTestsToRevisit(report3.testsToRevisit));
42954
44573
  return lines.join("\n");
42955
44574
  }
@@ -42966,13 +44585,19 @@ function renderTestsToRevisit(entries) {
42966
44585
  }
42967
44586
  return lines;
42968
44587
  }
44588
+ function renderRespellings(entries) {
44589
+ if (!entries.length) return [];
44590
+ return ["", "RESPELLED (stored canonical):", ...entries.map((r) => `- ${r.path}: "${r.written}" \u2192 "${r.stored}"`)];
44591
+ }
42969
44592
  function writeReceipt(sentence, receipt) {
42970
44593
  const noticeBlock = receipt.notices.length ? `
42971
44594
 
42972
44595
  NOTICE:
42973
44596
  - ${receipt.notices.join("\n- ")}` : "";
44597
+ const respelledBlock = renderRespellings(receipt.respellings).join("\n");
42974
44598
  const testsBlock = renderTestsToRevisit(receipt.testsToRevisit).join("\n");
42975
- return structured(`${sentence}${noticeBlock}${testsBlock ? `
44599
+ return structured(`${sentence}${noticeBlock}${respelledBlock ? `
44600
+ ${respelledBlock}` : ""}${testsBlock ? `
42976
44601
  ${testsBlock}` : ""}`, receipt);
42977
44602
  }
42978
44603
  function describeMemberPacks(creation) {
@@ -43030,46 +44655,46 @@ function schemaShape(schema, seen = /* @__PURE__ */ new Map()) {
43030
44655
  const known = seen.get(schema);
43031
44656
  if (known !== void 0) return { ref: known };
43032
44657
  seen.set(schema, seen.size);
43033
- const inner = (s) => schemaShape(s, seen);
44658
+ const inner2 = (s) => schemaShape(s, seen);
43034
44659
  const kind = String(def.typeName);
43035
44660
  switch (kind) {
43036
44661
  case "ZodObject": {
43037
44662
  const shape = typeof def.shape === "function" ? def.shape() : def.shape;
43038
44663
  return {
43039
44664
  kind,
43040
- keys: Object.keys(shape).sort().map((key) => [key, inner(shape[key])]),
44665
+ keys: Object.keys(shape).sort().map((key) => [key, inner2(shape[key])]),
43041
44666
  unknownKeys: def.unknownKeys,
43042
- catchall: inner(def.catchall)
44667
+ catchall: inner2(def.catchall)
43043
44668
  };
43044
44669
  }
43045
44670
  case "ZodArray":
43046
- return { kind, of: inner(def.type), min: def.minLength?.value, max: def.maxLength?.value };
44671
+ return { kind, of: inner2(def.type), min: def.minLength?.value, max: def.maxLength?.value };
43047
44672
  case "ZodOptional":
43048
44673
  case "ZodNullable":
43049
44674
  case "ZodReadonly":
43050
44675
  case "ZodBranded":
43051
44676
  case "ZodCatch":
43052
- return { kind, of: inner(def.innerType ?? def.type) };
44677
+ return { kind, of: inner2(def.innerType ?? def.type) };
43053
44678
  case "ZodDefault":
43054
- return { kind, of: inner(def.innerType), default: JSON.stringify(def.defaultValue?.()) };
44679
+ return { kind, of: inner2(def.innerType), default: JSON.stringify(def.defaultValue?.()) };
43055
44680
  case "ZodEffects":
43056
- return { kind, effect: def.effect?.type, of: inner(def.schema) };
44681
+ return { kind, effect: def.effect?.type, of: inner2(def.schema) };
43057
44682
  case "ZodLazy":
43058
- return { kind, of: inner(def.getter()) };
44683
+ return { kind, of: inner2(def.getter()) };
43059
44684
  case "ZodUnion":
43060
44685
  case "ZodDiscriminatedUnion": {
43061
44686
  const options = def.options instanceof Map ? [...def.options.values()] : def.options;
43062
- return { kind, discriminator: def.discriminator, of: options.map(inner) };
44687
+ return { kind, discriminator: def.discriminator, of: options.map(inner2) };
43063
44688
  }
43064
44689
  case "ZodIntersection":
43065
- return { kind, left: inner(def.left), right: inner(def.right) };
44690
+ return { kind, left: inner2(def.left), right: inner2(def.right) };
43066
44691
  case "ZodTuple":
43067
- return { kind, of: def.items.map(inner), rest: inner(def.rest) };
44692
+ return { kind, of: def.items.map(inner2), rest: inner2(def.rest) };
43068
44693
  case "ZodRecord":
43069
44694
  case "ZodMap":
43070
- return { kind, key: inner(def.keyType), value: inner(def.valueType) };
44695
+ return { kind, key: inner2(def.keyType), value: inner2(def.valueType) };
43071
44696
  case "ZodSet":
43072
- return { kind, of: inner(def.valueType) };
44697
+ return { kind, of: inner2(def.valueType) };
43073
44698
  case "ZodEnum":
43074
44699
  return { kind, values: def.values };
43075
44700
  case "ZodNativeEnum":
@@ -43077,7 +44702,7 @@ function schemaShape(schema, seen = /* @__PURE__ */ new Map()) {
43077
44702
  case "ZodLiteral":
43078
44703
  return { kind, value: def.value };
43079
44704
  case "ZodPipeline":
43080
- return { kind, in: inner(def.in), out: inner(def.out) };
44705
+ return { kind, in: inner2(def.in), out: inner2(def.out) };
43081
44706
  default:
43082
44707
  return { kind, checks: def.checks?.map((c) => [c.kind, c.value ?? null]) ?? [] };
43083
44708
  }
@@ -43878,7 +45503,7 @@ ${renderChangeReport(report3)}`,
43878
45503
  type: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The parameter's type")),
43879
45504
  description: import_zod11.z.string().optional(),
43880
45505
  optional: import_zod11.z.boolean().optional().describe(
43881
- 'Whether the parameter may be OMITTED by a caller. It is not nullability: a parameter that must be passed but may be passed as nothing is a required parameter whose type is a union \u2014 "ProjectConfig | null". Say whichever is true; they are different contracts.'
45506
+ 'Whether the parameter may be OMITTED by a caller. It is not nullability: a parameter that must be passed but may be passed as nothing is a required parameter whose type says so \u2014 "ProjectConfig?". Say whichever is true; they are different contracts (both together: may be left out, and may be none).'
43882
45507
  )
43883
45508
  }).strict();
43884
45509
  const interfaceMethodShape = {
@@ -44127,7 +45752,7 @@ ${renderChangeReport(report3)}`,
44127
45752
  }
44128
45753
  );
44129
45754
  const typeInput = {
44130
- kind: import_zod11.z.enum(["entity", "value-object", "signature"]).describe("entity (owned by a subsystem), value-object (often system-level shared), or signature \u2014 a named function type: params and returns, nothing else"),
45755
+ kind: import_zod11.z.enum(["entity", "value-object", "signature", "enum"]).describe("entity (owned by a subsystem), value-object (often system-level shared), signature \u2014 a named function type: params and returns, nothing else \u2014 or enum: a closed, ordered set of named values"),
44131
45756
  id: import_zod11.z.string().describe("Lowercase identifier"),
44132
45757
  name: import_zod11.z.string().describe("Human-readable name"),
44133
45758
  description: import_zod11.z.string().optional(),
@@ -44161,18 +45786,23 @@ ${renderChangeReport(report3)}`,
44161
45786
  sourcePath: import_zod11.z.string().optional().describe("Source file holding the declaration of this type (project-relative). Naming one turns the type into a claim on code: the file must resolve and the declaration must be anchored in it (UNREALIZED_TYPE)"),
44162
45787
  symbol: import_zod11.z.string().optional().describe('Code-level name realizing the declaration when it differs from name, e.g. a type named "Invoice Line" declared as InvoiceLine'),
44163
45788
  params: import_zod11.z.array(methodParamItem).optional().describe("kind signature only: the function type's parameters, in declared order (SIGNATURE_TYPE_MEMBERS on any other kind)"),
44164
- returns: import_zod11.z.string().optional().describe(TYPE_REF_GRAMMAR("kind signature only, and required there: the function type's one output"))
45789
+ returns: import_zod11.z.string().optional().describe(TYPE_REF_GRAMMAR("kind signature only, and required there: the function type's one output")),
45790
+ values: import_zod11.z.array(import_zod11.z.object({
45791
+ name: import_zod11.z.string().min(1).describe(`The value's name, exactly as data carries it ("stable", "local_only")`),
45792
+ description: import_zod11.z.string().optional().describe("What the value means")
45793
+ }).strict()).optional().describe("kind enum only, and required there: the values in declared order \u2014 the order is part of the design (a narrowest-first track list means something). Unique by name ignoring case and separators; no ordinals and no separate wire value"),
45794
+ holds: import_zod11.z.string().optional().describe('kind value-object only: makes it a NAMED SCALAR \u2014 one primitive under a name (string, int, float, bool, bytes, date, datetime or duration), a newtype or type alias in every language (`type PackPath = string`), declared in place of fields. Not a union, not a named type, not any; fields beside it, or holds on another kind, is NAMED_SCALAR_MEMBERS. Aliases are respelled (boolean \u2192 bool) and number is refused ("int or float?")')
44165
45795
  };
44166
45796
  const typeInputFields = Object.keys(typeInput);
44167
45797
  reg(
44168
45798
  server,
44169
45799
  "sdd_add_type",
44170
45800
  {
44171
- description: "Define a type: an entity or value-object (the data components operate on), or a signature \u2014 a named function type. Entities are owned by a subsystem; shared value objects and signatures omit subsystem (system-level). Fields are data; methods are PURE intrinsic behaviour only \u2014 anything needing a collaborator belongs on a component, taking the entity as an argument \u2014 and a type method may carry structured params, which then derive its signature text as a contract method's do. A signature carries top-level params and returns and nothing else (no fields, methods or invariants \u2014 SIGNATURE_TYPE_MEMBERS); a contract method takes it by naming it as its signatureFrom, and a param may be typed by one (a callback). A type may also CLAIM code: sourcePath names the file holding its declaration (and each method may name its own), symbol binds the code-level name when it differs \u2014 the file must then resolve and the declaration must be anchored in it. An owning subsystem the tree does not have is refused, writing nothing, exactly as a component under an unknown subsystem is. Re-defining an existing id REPLACES fields/methods/invariants and restates sourcePath/symbol (an omitted list or path is CLEARED, and a dropped member is reported); lint/ext are carried forward. The answer carries a write receipt as structured content beside the sentence \u2014 whether a spec already held the id, and the notices a restatement raised, each as its own entry. A type carries no lifecycle status, so the receipt states none.",
45801
+ description: "Define a type: an entity or value-object (the data components operate on), a signature \u2014 a named function type \u2014 or an enum: a closed, ordered set of named `values` ({name, description?}, unique by name ignoring case and separators; the name is also the value as data carries it), with optional pure methods and nothing else (no fields, params or returns). A value-object may instead be a NAMED SCALAR: it `holds` one primitive in place of fields (a newtype or type alias in every language). Every type position (a field's type, a method's params and returns, a signature's params and returns, a named scalar's holds) is written in the neutral type grammar; aliases are respelled and listed in the receipt. Entities are owned by a subsystem; shared value objects and signatures omit subsystem (system-level). Fields are data; methods are PURE intrinsic behaviour only \u2014 anything needing a collaborator belongs on a component, taking the entity as an argument \u2014 and a type method may carry structured params, which then derive its signature text as a contract method's do. A signature carries top-level params and returns and nothing else (no fields, methods or invariants \u2014 SIGNATURE_TYPE_MEMBERS); a contract method takes it by naming it as its signatureFrom, and a param may be typed by one (a callback). A type may also CLAIM code: sourcePath names the file holding its declaration (and each method may name its own), symbol binds the code-level name when it differs \u2014 the file must then resolve and the declaration must be anchored in it. An owning subsystem the tree does not have is refused, writing nothing, exactly as a component under an unknown subsystem is. Re-defining an existing id REPLACES fields/methods/invariants and restates sourcePath/symbol (an omitted list or path is CLEARED, and a dropped member is reported); lint/ext are carried forward. The answer carries a write receipt as structured content beside the sentence \u2014 whether a spec already held the id, and the notices a restatement raised, each as its own entry. A type carries no lifecycle status, so the receipt states none.",
44172
45802
  inputSchema: typeInput,
44173
45803
  outputSchema: specWriteReceiptOutput
44174
45804
  },
44175
- ({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol, params, returns }) => {
45805
+ ({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol, params, returns, values, holds }) => {
44176
45806
  try {
44177
45807
  const spec = {
44178
45808
  kind,
@@ -44198,7 +45828,9 @@ ${renderChangeReport(report3)}`,
44198
45828
  ...sourcePath ? { sourcePath } : {},
44199
45829
  ...symbol ? { symbol } : {},
44200
45830
  ...params ? { params } : {},
44201
- ...returns ? { returns } : {}
45831
+ ...returns ? { returns } : {},
45832
+ ...values ? { values } : {},
45833
+ ...holds ? { holds } : {}
44202
45834
  };
44203
45835
  const receipt = writeSpec({ kind: "type", spec, fields: typeInputFields });
44204
45836
  return writeReceipt(
@@ -44231,7 +45863,8 @@ ${renderChangeReport(report3)}`,
44231
45863
  rules: config.rules,
44232
45864
  projectType: config.projectType,
44233
45865
  scopeSubsystem: subsystem,
44234
- recursive: recursive ?? true,
45866
+ // The tool's flag at the edge: recursive false is a member depth of 0.
45867
+ ...recursive === false ? { memberDepth: 0 } : {},
44235
45868
  family
44236
45869
  };
44237
45870
  const result = selectsFamily(options2, declaredMembers(config).length > 0) ? validateFamily(options2) : validateProject(options2);
@@ -44366,7 +45999,7 @@ ${testsBlock}` : ""}`, deletion);
44366
45999
  server,
44367
46000
  "sdd_update_spec",
44368
46001
  {
44369
- description: "Update/patch an existing SDD specification (subsystem, component, interface, implementation, or type) using a granular delta. Updates fields, appends/merges array elements, or inserts/deletes narrative steps. Answers with exactly what changed, and with every path the delta named that the write did not act on. Pass dryRun to be told what it would do without writing it.",
46002
+ description: "Update/patch an existing SDD specification (subsystem, component, interface, implementation, or type) using a granular delta. Updates fields, appends/merges array elements, or inserts/deletes narrative steps. Answers with exactly what changed, and with every path the delta named that the write did not act on. Type positions (the type of a param, a returns, the type of a field, the params and returns of a signature) are written in the neutral type grammar: an alias the delta writes (string[], T | null, boolean, Promise<T>) is stored canonical and listed under `respellings`, and a stored alias at a position the write keeps is respelled with it; a position the delta writes that has no canonical spelling (number \u2014 int or float?, an inline object or function type, a literal union, a union mixing in a primitive) is refused, naming the replacement, while one the stored spec already held is left alone. Pass dryRun to be told what it would do without writing it.",
44370
46003
  inputSchema: {
44371
46004
  kind: import_zod11.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).describe("The spec kind to update (system = the singleton L0 \u2014 vision, boundaries, globalRequirements, databases, and publicInterfaces: the project export table, each entry a re-export {from, component, interface, typeDef, as, name, type, details, audience: project|department|instance|partner|external} \u2014 from alone re-exports everything a subsystem exports, and a legacy {id, subsystem} reads as {as, from})"),
44372
46005
  id: import_zod11.z.string().describe("The ID of the spec to update (namespaced if needed)"),
@@ -44395,18 +46028,19 @@ ${testsBlock}` : ""}`, deletion);
44395
46028
  }
44396
46029
  },
44397
46030
  ({ subsystem, recursive }) => {
46031
+ const memberDepth = recursive === false ? 0 : void 0;
44398
46032
  const banner = pendingBanner().map((line2) => `\u26A0 TRANSACTION PENDING: ${line2}
44399
46033
  `).join("");
44400
46034
  try {
44401
46035
  let approvals;
44402
46036
  try {
44403
- approvals = familyApprovals(recursive === false ? 0 : void 0);
46037
+ approvals = familyApprovals(memberDepth);
44404
46038
  } catch {
44405
46039
  approvals = void 0;
44406
46040
  }
44407
46041
  const report3 = getStatusReport({
44408
46042
  subsystem,
44409
- recursive: recursive ?? true,
46043
+ memberDepth,
44410
46044
  approvals
44411
46045
  });
44412
46046
  const family = statusFamilyContext();
@@ -44623,7 +46257,7 @@ async function startMcpServer() {
44623
46257
  } catch {
44624
46258
  }
44625
46259
  }
44626
- var import_mcp, import_stdio, import_zod11, import_types12, fs30, path50, import_url, crypto10, TYPE_REF_GRAMMAR, staleServerOutput, externalPinsOutput, externalStatusesOutput, SPEC_KINDS, STATUS_VALUES, testsToRevisitOutput, specWriteReceiptOutput, specDeletionOutput, specChangeOutput, specChangeReportOutput, validationIssueOutput, memberCreationOutput, doctrineChangeOutput, impactTotalsOutput, packImpactOutput, validateTreeOutput, getSpecOutput, FINGERPRINT_VERSION, runningFingerprint, RECONNECT, assessedAsChanged, serverBuildStamps, SERVER_BUILD_STAMP, SPEC_WRITE_TOOLS, listChangedEmitters, statusInput, SKILL_RESOURCE_MIME, AGENT_BRIEF_SCHEME;
46260
+ var import_mcp, import_stdio, import_zod11, import_types12, fs30, path50, import_url, crypto10, TYPE_REF_GRAMMAR, staleServerOutput, externalPinsOutput, externalStatusesOutput, SPEC_KINDS, STATUS_VALUES, testsToRevisitOutput, typeRespellingOutput, specWriteReceiptOutput, specDeletionOutput, specChangeOutput, specChangeReportOutput, validationIssueOutput, memberCreationOutput, doctrineChangeOutput, impactTotalsOutput, packImpactOutput, validateTreeOutput, getSpecOutput, FINGERPRINT_VERSION, runningFingerprint, RECONNECT, assessedAsChanged, serverBuildStamps, SERVER_BUILD_STAMP, SPEC_WRITE_TOOLS, listChangedEmitters, statusInput, SKILL_RESOURCE_MIME, AGENT_BRIEF_SCHEME;
44627
46261
  var init_server = __esm({
44628
46262
  "src/mcp/server.ts"() {
44629
46263
  "use strict";
@@ -44652,7 +46286,7 @@ var init_server = __esm({
44652
46286
  init_project();
44653
46287
  init_validation_options();
44654
46288
  init_migrations3();
44655
- TYPE_REF_GRAMMAR = (what) => `${what}: a primitive/builtin or a defined type id (qualified across subsystems, e.g. "billing.Invoice" or "billing::Invoice"). Generics, arrays and UNIONS are all read, at any depth: "Invoice | null" (the commonest shape there is), "Invoice | undefined", "Invoice | Receipt", "Promise<Invoice | null>", "Map<string, Invoice | null>", "Invoice[] | null", "(Invoice | null)[]". Every identifier the string names must resolve \u2014 a union of two defined types means BOTH must exist. A union of string literals ("read" | "write") names no type and resolves to nothing.`;
46289
+ TYPE_REF_GRAMMAR = (what) => `${what}, in the neutral type grammar: a primitive (string, int, float, bool, bytes, date, datetime, duration, any; void only as a whole returns), a defined type id ("billing.Invoice", "alias::name"), list<T>, set<T>, map<K, V> (K is string, int or an enum), T? (T or no value), a union of NAMED types ("Invoice | Receipt"), and on a returns only, async T ("async void"). Every named type must resolve. TypeScript spellings are accepted and respelled \u2014 "Invoice[]" \u2192 list<Invoice>, "Invoice | null" \u2192 Invoice?, boolean \u2192 bool, Record<string, V> \u2192 map<string, V>, Promise<T> \u2192 async T \u2014 and the answer lists each respelling. Refused, naming the replacement: number ("int or float?"), inline object or function types (name a value-object or a signature type), string-literal unions (name an enum), and unions mixing in a primitive or a collection.`;
44656
46290
  staleServerOutput = {
44657
46291
  staleServer: import_zod11.z.boolean().optional().describe(
44658
46292
  "True when the wairon build on disk changed after this server started \u2014 the structured twin of the text answer's STALE SERVER banner. When the rebuild left the spec and tool schemas alone, writes still go through; reconnect to run the new code."
@@ -44702,6 +46336,13 @@ var init_server = __esm({
44702
46336
  })).describe(
44703
46337
  "The tests that encode a method this write changed or deleted, one entry per such method. Empty when the write touched no method, when the project declares no `rules.conformance.testRoots`, or when nothing references them. Read it before promising that existing tests still pass."
44704
46338
  );
46339
+ typeRespellingOutput = import_zod11.z.object({
46340
+ specId: import_zod11.z.string().describe("The interface or type holding the position."),
46341
+ kind: import_zod11.z.enum(["interface", "type"]).describe("interface | type."),
46342
+ path: import_zod11.z.string().describe("Where in the spec: methods.save.params.key, methods.save.returns, fields.createdAt, params.listener."),
46343
+ written: import_zod11.z.string().describe("The text the input (or the stored file) held."),
46344
+ stored: import_zod11.z.string().describe("The canonical spelling, which is what is stored and shown.")
46345
+ });
44705
46346
  specWriteReceiptOutput = {
44706
46347
  kind: import_zod11.z.enum(SPEC_KINDS).describe("The spec kind written."),
44707
46348
  id: import_zod11.z.string().describe('The id the spec is stored under; "system" for the L0 singleton.'),
@@ -44715,6 +46356,9 @@ var init_server = __esm({
44715
46356
  notices: import_zod11.z.array(import_zod11.z.string()).describe(
44716
46357
  "The notice lines the text answer lists, one per entry: what was carried forward, what a restatement removed, what an omission cleared, and every gate warning the write raised. Empty for a clean create."
44717
46358
  ),
46359
+ respellings: import_zod11.z.array(typeRespellingOutput).describe(
46360
+ "Each type position the write normalised: what the input wrote against the canonical spelling stored. Aliases are accepted, never refused, and this list is how an author learns the canonical form. Empty when every position was already canonical."
46361
+ ),
44718
46362
  testsToRevisit: testsToRevisitOutput,
44719
46363
  ...staleServerOutput
44720
46364
  };
@@ -44747,6 +46391,9 @@ var init_server = __esm({
44747
46391
  "Every path the delta named that the write did not act on, each with why. Read it: a nested typo the permissive delta cannot refuse shows up here and nowhere else."
44748
46392
  ),
44749
46393
  notices: import_zod11.z.array(import_zod11.z.string()).describe("Store placement notices, gate warnings and delta notices."),
46394
+ respellings: import_zod11.z.array(typeRespellingOutput).describe(
46395
+ "Each type position the write normalised (or, on a dry run, would): what the delta or the stored file wrote against the canonical spelling stored. Aliases are accepted and respelled, never refused \u2014 this is how the canonical form is taught. Empty when every position was already canonical."
46396
+ ),
44750
46397
  summary: import_zod11.z.string().describe("One line for people."),
44751
46398
  testsToRevisit: testsToRevisitOutput,
44752
46399
  strippedKeys: import_zod11.z.array(import_zod11.z.string()).optional().describe(
@@ -45147,7 +46794,7 @@ function planMcpInstall(options) {
45147
46794
  upToDate
45148
46795
  }));
45149
46796
  }
45150
- async function runMcpInstall(options = {}) {
46797
+ function runMcpInstall(options = {}) {
45151
46798
  for (const target of resolveInstallTargets(options)) {
45152
46799
  const agentLabel = target.backend === "gemini" ? "Antigravity" : "Claude";
45153
46800
  if (target.upToDate) {
@@ -45188,7 +46835,7 @@ function reportInstalled(target, agentLabel) {
45188
46835
  }
45189
46836
  logger.blank();
45190
46837
  }
45191
- async function runMcpStatus() {
46838
+ function runMcpStatus() {
45192
46839
  assertProjectInitialized();
45193
46840
  const projectConfig = loadProjectConfig();
45194
46841
  if (!projectConfig) throw new ProjectNotInitializedError();
@@ -45998,7 +47645,7 @@ async function executeInit(projectName, projectConfig, guidePlan) {
45998
47645
  if (targetTypes.includes("claude")) {
45999
47646
  try {
46000
47647
  const { runMcpInstall: runMcpInstall2 } = (init_mcp(), __toCommonJS(mcp_exports));
46001
- await runMcpInstall2({ global: false, backend: "claude" });
47648
+ runMcpInstall2({ global: false, backend: "claude" });
46002
47649
  } catch (err) {
46003
47650
  logger.warn(`Failed to automatically register MCP server for Claude: ${err}`);
46004
47651
  }
@@ -46006,7 +47653,7 @@ async function executeInit(projectName, projectConfig, guidePlan) {
46006
47653
  if (targetTypes.includes("gemini") || targetTypes.includes("agy")) {
46007
47654
  try {
46008
47655
  const { runMcpInstall: runMcpInstall2 } = (init_mcp(), __toCommonJS(mcp_exports));
46009
- await runMcpInstall2({ backend: "gemini", global: false });
47656
+ runMcpInstall2({ backend: "gemini", global: false });
46010
47657
  } catch (err) {
46011
47658
  logger.warn(`Failed to automatically register MCP server for Antigravity: ${err}`);
46012
47659
  }
@@ -46700,7 +48347,8 @@ async function runValidate(options = {}) {
46700
48347
  rules: projectConfig.rules,
46701
48348
  projectType: projectConfig.projectType,
46702
48349
  scopeSubsystem: options.subsystem,
46703
- recursive: options.recursive ?? true,
48350
+ // The flag at the edge: --no-recursive is a member depth of 0, the default every level.
48351
+ ...options.recursive === false ? { memberDepth: 0 } : {},
46704
48352
  family: options.family
46705
48353
  };
46706
48354
  const family = selectsFamily(sddOptions, declaredMembers(projectConfig).length > 0);
@@ -47234,16 +48882,12 @@ function refuseUnreadableTree(explanation) {
47234
48882
  function withoutTrailingNewline(text3) {
47235
48883
  return text3.endsWith("\n") ? text3.slice(0, -1) : text3;
47236
48884
  }
47237
- function depthOf2(recursive) {
47238
- if (recursive === false) return 0;
47239
- return typeof recursive === "number" ? recursive : void 0;
47240
- }
47241
48885
  async function runStatus(options = {}) {
47242
48886
  assertProjectInitialized();
47243
48887
  printPendingTransactions();
47244
48888
  let approvals;
47245
48889
  try {
47246
- approvals = familyApprovals(depthOf2(options.recursive));
48890
+ approvals = familyApprovals(options.memberDepth);
47247
48891
  } catch {
47248
48892
  approvals = void 0;
47249
48893
  }
@@ -47649,7 +49293,7 @@ async function runDomainsRemove(id) {
47649
49293
  var import_chalk11 = __toESM(require("chalk"));
47650
49294
  init_logger();
47651
49295
  init_paths();
47652
- async function runSkillsList() {
49296
+ function runSkillsList() {
47653
49297
  assertProjectInitialized();
47654
49298
  const names = listSkillNames();
47655
49299
  logger.header(`SDD Skills (${names.length})`);
@@ -47658,7 +49302,7 @@ async function runSkillsList() {
47658
49302
  console.log(` ${import_chalk11.default.bold(n)}`);
47659
49303
  }
47660
49304
  }
47661
- async function runSkillsInstall() {
49305
+ function runSkillsInstall() {
47662
49306
  assertProjectInitialized();
47663
49307
  const result = exportSddSkills();
47664
49308
  if (result.destinations.length === 0) {
@@ -47998,6 +49642,34 @@ async function runDoctor(options = {}) {
47998
49642
  } catch (e) {
47999
49643
  line(tally, "warn", `Could not plan the signature repair: ${e instanceof Error ? e.message : String(e)}`);
48000
49644
  }
49645
+ try {
49646
+ const spellings = repairTypeSpellings(false);
49647
+ const rewriting = spellings.filter((repair) => repair.rewritten.length > 0);
49648
+ const proposals = spellings.flatMap((repair) => repair.proposals);
49649
+ const authorNeeded = spellings.flatMap((repair) => repair.authorNeeded);
49650
+ if (spellings.length > 0) console.log(import_chalk12.default.bold("Type spellings"));
49651
+ if (rewriting.length > 0) {
49652
+ for (const repair of rewriting) {
49653
+ line(tally, "warn", `${repair.kind} ${repair.specId}: ${repair.rewritten.length} position(s) to respell (${describeRespellings(repair.rewritten)})`);
49654
+ }
49655
+ line(tally, "warn", "Run `wairon doctor --fix` to write them in their canonical spelling (any save of the spec does too).");
49656
+ }
49657
+ if (proposals.length > 0) {
49658
+ console.log(` ${import_chalk12.default.gray("int proposed \u2014 never written: confirm by writing int, or write float")}`);
49659
+ for (const proposal of proposals) {
49660
+ line(tally, "warn", `${proposal.kind} ${proposal.specId} ${proposal.path}: "${proposal.written}" \u2192 ${proposal.stored} (proposed)`);
49661
+ }
49662
+ }
49663
+ if (authorNeeded.length > 0) {
49664
+ console.log(` ${import_chalk12.default.gray("needs an author \u2014 no rewrite can settle these")}`);
49665
+ for (const problem of authorNeeded) {
49666
+ line(tally, "warn", `${problem.kind} ${problem.specId} ${problem.path ?? ""}: "${problem.written}" \u2014 ${problem.replacement ? `write ${problem.replacement}` : problem.detail}`);
49667
+ }
49668
+ }
49669
+ if (spellings.length > 0) logger.blank();
49670
+ } catch (e) {
49671
+ line(tally, "warn", `Could not plan the type-spelling repair: ${e instanceof Error ? e.message : String(e)}`);
49672
+ }
48001
49673
  try {
48002
49674
  const planned = plan5({ verb: "chaining", rehearse: false });
48003
49675
  const pending4 = planned.chaining;
@@ -48229,6 +49901,28 @@ async function applyFixes(options, tally) {
48229
49901
  } catch (e) {
48230
49902
  console.log(` ${icon("error")} Signature repair failed: ${e instanceof Error ? e.message : String(e)}`);
48231
49903
  }
49904
+ try {
49905
+ const spellings = repairTypeSpellings(true);
49906
+ const rewritten = spellings.filter((repair) => repair.rewritten.length > 0);
49907
+ if (rewritten.length > 0) {
49908
+ const positions = rewritten.reduce((n, repair) => n + repair.rewritten.length, 0);
49909
+ console.log(` ${icon("ok")} Rewrote ${positions} type position(s) in ${rewritten.length} spec(s) into their canonical spelling.`);
49910
+ }
49911
+ const proposals = spellings.flatMap((repair) => repair.proposals);
49912
+ if (proposals.length > 0) {
49913
+ console.log(` ${icon("warn")} ${proposals.length} number position(s) with int proposed \u2014 not written; confirm by writing int, or write float:`);
49914
+ for (const proposal of proposals) console.log(` ${proposal.kind} ${proposal.specId} ${proposal.path}: "${proposal.written}" \u2192 ${proposal.stored} (proposed)`);
49915
+ }
49916
+ const authorNeeded = spellings.flatMap((repair) => repair.authorNeeded);
49917
+ if (authorNeeded.length > 0) {
49918
+ console.log(` ${icon("warn")} ${authorNeeded.length} type position(s) need an author \u2014 no rewrite can settle them:`);
49919
+ for (const problem of authorNeeded) {
49920
+ console.log(` ${problem.kind} ${problem.specId} ${problem.path ?? ""}: "${problem.written}" \u2014 ${problem.replacement ? `write ${problem.replacement}` : problem.detail}`);
49921
+ }
49922
+ }
49923
+ } catch (e) {
49924
+ console.log(` ${icon("error")} Type-spelling repair failed: ${e instanceof Error ? e.message : String(e)}`);
49925
+ }
48232
49926
  await migrateChaining(options, tally);
48233
49927
  await repairMcpRegistration(options, targets);
48234
49928
  logger.blank();
@@ -48248,18 +49942,18 @@ async function repairMcpRegistration(options, targets) {
48248
49942
  continue;
48249
49943
  }
48250
49944
  if (writes.some((w) => w.outsideProject && !w.upToDate)) outside.push({ install, writes });
48251
- else await installMcp(install);
49945
+ else installMcp(install);
48252
49946
  }
48253
49947
  const plugin = gemini ? findLegacyPlugin() : null;
48254
49948
  if (outside.length === 0 && !plugin) return;
48255
49949
  printOutsideWrites(outside, plugin);
48256
49950
  if (!await confirmOutsideWrites(options)) return;
48257
- for (const planned of outside) await installMcp(planned.install);
49951
+ for (const planned of outside) installMcp(planned.install);
48258
49952
  if (plugin) retireLegacyPlugin();
48259
49953
  }
48260
- async function installMcp(install) {
49954
+ function installMcp(install) {
48261
49955
  try {
48262
- await runMcpInstall(install);
49956
+ runMcpInstall(install);
48263
49957
  } catch (e) {
48264
49958
  console.log(` ${icon("warn")} MCP install for ${install.backend} failed: ${e instanceof Error ? e.message : String(e)}`);
48265
49959
  }
@@ -48451,6 +50145,11 @@ function describeSignatureRepair(repair) {
48451
50145
  if (repair.dropped.length > 0) parts.push(`drops the restated signature of ${repair.dropped.join(", ")}`);
48452
50146
  return parts.join("; ");
48453
50147
  }
50148
+ function describeRespellings(respellings) {
50149
+ const shown2 = respellings.slice(0, 3).map((r) => `${r.written} \u2192 ${r.stored}`);
50150
+ const more = respellings.length - shown2.length;
50151
+ return more > 0 ? `${shown2.join("; ")}; and ${more} more` : shown2.join("; ");
50152
+ }
48454
50153
  function pendingCount(migration) {
48455
50154
  return migration.rewrites.length + migration.projects.reduce((n, p) => n + (p.idToWrite ? 1 : 0) + (p.createsSystem ? 1 : 0) + p.exports.length + p.externals.length + p.imports.length + p.pins.length + p.members.length + p.supersededPins.length + p.locations.length, 0);
48456
50155
  }
@@ -48689,7 +50388,7 @@ var import_chalk13 = __toESM(require("chalk"));
48689
50388
  init_logger();
48690
50389
  init_validation();
48691
50390
  init_core();
48692
- async function listRules3() {
50391
+ function listRules3() {
48693
50392
  let overrides = {};
48694
50393
  if (projectConfigExists()) {
48695
50394
  try {
@@ -48743,7 +50442,7 @@ SDD conformance rules (${active.length})
48743
50442
  var import_chalk14 = __toESM(require("chalk"));
48744
50443
  init_logger();
48745
50444
  init_core();
48746
- async function listPatterns() {
50445
+ function listPatterns() {
48747
50446
  const ext = loadProjectExtensions();
48748
50447
  console.log(import_chalk14.default.bold(`
48749
50448
  Reusable patterns (${ext.patterns.length})
@@ -48768,7 +50467,7 @@ Reusable patterns (${ext.patterns.length})
48768
50467
  var import_chalk15 = __toESM(require("chalk"));
48769
50468
  init_logger();
48770
50469
  init_core();
48771
- async function listVariants() {
50470
+ function listVariants() {
48772
50471
  const variants = loadProjectVariants();
48773
50472
  console.log(import_chalk15.default.bold(`
48774
50473
  Component variants (${variants.length})
@@ -48948,7 +50647,7 @@ async function addPackFromArchive(source, options) {
48948
50647
  }
48949
50648
  logger.info(`${describe2(probe2)} \u2014 commit .wai/ so CI and every clone enforce it.`);
48950
50649
  }
48951
- async function initPack(name, options = {}) {
50650
+ function initPack(name, options = {}) {
48952
50651
  const kind = options.kind === "code" ? "code" : "declarative";
48953
50652
  const targetDir = options.dir ?? `./${name}`;
48954
50653
  const request = { name, kind, targetDir };
@@ -48961,7 +50660,7 @@ async function initPack(name, options = {}) {
48961
50660
  logger.blank();
48962
50661
  logger.info(`Next: cd ${targetDir} && wairon pack build`);
48963
50662
  }
48964
- async function buildPack(source, options = {}) {
50663
+ function buildPack(source, options = {}) {
48965
50664
  const sourceDir = source && source.length > 0 ? source : ".";
48966
50665
  const result = (0, import_sdk.buildPack)(sourceDir);
48967
50666
  const outPath = options.out ?? result.suggestedFileName;
@@ -49108,7 +50807,7 @@ function extractArchiveToTemp(source) {
49108
50807
  return null;
49109
50808
  }
49110
50809
  }
49111
- async function uninstallStorePack(spec) {
50810
+ function uninstallStorePack(spec) {
49112
50811
  const { name, version } = parseNameAtVersion(spec);
49113
50812
  if (!uninstallPack(name, version)) {
49114
50813
  logger.error(`No pack "${spec}" is installed in the store (${packStoreDir()}).`);
@@ -49118,7 +50817,7 @@ async function uninstallStorePack(spec) {
49118
50817
  logger.success(`Uninstalled ${version ? `${name} v${version}` : `every version of ${name}`} from the pack store.`);
49119
50818
  logger.info("A project that still SELECTS it will now fail its gate with PACK_NOT_INSTALLED \u2014 reinstall it or drop the selection.");
49120
50819
  }
49121
- async function whichPack(spec) {
50820
+ function whichPack(spec) {
49122
50821
  const { name, version } = parseNameAtVersion(spec);
49123
50822
  const resolved = resolveInstalledPack(name, version);
49124
50823
  if (!resolved) {
@@ -49276,7 +50975,7 @@ function writeBundle(root, resolved) {
49276
50975
  fs37.cpSync(resolved.path, dest, { recursive: true });
49277
50976
  return dest;
49278
50977
  }
49279
- async function bundlePack(name, options = {}) {
50978
+ function bundlePack(name, options = {}) {
49280
50979
  if (!projectConfigExists()) {
49281
50980
  logger.error("Not inside a wairon project \u2014 run `wairon init` first.");
49282
50981
  process.exitCode = 1;
@@ -49341,7 +51040,7 @@ function parseNameAtVersion(spec) {
49341
51040
  if (at <= 0) return { name: spec };
49342
51041
  return { name: spec.slice(0, at), version: spec.slice(at + 1) };
49343
51042
  }
49344
- async function listPacks() {
51043
+ function listPacks() {
49345
51044
  const inProject = projectConfigExists();
49346
51045
  const config = inProject ? loadProjectConfig() : null;
49347
51046
  if (inProject && !config) throw new ProjectNotInitializedError();
@@ -49508,7 +51207,7 @@ function printImpact(impact) {
49508
51207
  After: ${totals3(impact.after)}`);
49509
51208
  logger.blank();
49510
51209
  }
49511
- async function impactPack(spec) {
51210
+ function impactPack(spec) {
49512
51211
  if (!projectConfigExists()) {
49513
51212
  logger.error("Not inside a wairon project \u2014 run `wairon init` first.");
49514
51213
  process.exitCode = 1;
@@ -56320,7 +58019,7 @@ function referencedTypeNames(methods) {
56320
58019
  const names = /* @__PURE__ */ new Set();
56321
58020
  for (const m of methods) {
56322
58021
  for (const ref of methodTypeRefs(m)) {
56323
- if (!BUILTIN_TYPES.has(ref.toLowerCase())) names.add(ref.split(/::|\./).pop());
58022
+ if (!isTypeVocabulary(ref)) names.add(ref.split(/::|\./).pop());
56324
58023
  }
56325
58024
  }
56326
58025
  return [...names];
@@ -56871,7 +58570,7 @@ function scopeOf2(grant) {
56871
58570
  if (!grant.projectId || grant.projectId === "*") return { scopeKind: "instance" };
56872
58571
  return { scopeKind: "project", scopeId: grant.projectId };
56873
58572
  }
56874
- function readRaw(file) {
58573
+ function readRaw2(file) {
56875
58574
  try {
56876
58575
  return JSON.parse(fs57.readFileSync(file, "utf8"));
56877
58576
  } catch {
@@ -56912,7 +58611,7 @@ function migratePermissionModel(dataDir, apply4) {
56912
58611
  const found = (area, detail) => {
56913
58612
  findings.push({ area, detail });
56914
58613
  };
56915
- const grid = readRaw(
58614
+ const grid = readRaw2(
56916
58615
  path79.join(dataDir, "permissions.json")
56917
58616
  ) ?? [];
56918
58617
  if (!getInstanceIdentity(dataDir)) {
@@ -56920,7 +58619,7 @@ function migratePermissionModel(dataDir, apply4) {
56920
58619
  if (apply4) ensureInstanceIdentity(dataDir);
56921
58620
  }
56922
58621
  const orgPath = path79.join(dataDir, "organization.json");
56923
- const org = readRaw(orgPath);
58622
+ const org = readRaw2(orgPath);
56924
58623
  if (org?.units?.length) {
56925
58624
  const units = org.units;
56926
58625
  const byId = new Map(units.map((u) => [u.id, u]));
@@ -56974,7 +58673,7 @@ function migratePermissionModel(dataDir, apply4) {
56974
58673
  }
56975
58674
  }
56976
58675
  const usersPath = path79.join(dataDir, "users.json");
56977
- const users = readRaw(usersPath);
58676
+ const users = readRaw2(usersPath);
56978
58677
  if (users) {
56979
58678
  let changed = false;
56980
58679
  for (const user of users) {
@@ -57001,7 +58700,7 @@ function migratePermissionModel(dataDir, apply4) {
57001
58700
  if (apply4 && changed) writeRaw(usersPath, users);
57002
58701
  }
57003
58702
  const keysPath = path79.join(dataDir, "auth", "credentials.json");
57004
- const keys = readRaw(keysPath);
58703
+ const keys = readRaw2(keysPath);
57005
58704
  if (keys) {
57006
58705
  let changed = false;
57007
58706
  for (const key of keys) {
@@ -57051,7 +58750,7 @@ function migratePermissionModel(dataDir, apply4) {
57051
58750
  if (apply4 && changed) writeRaw(keysPath, keys);
57052
58751
  }
57053
58752
  const sessionsPath = path79.join(dataDir, "web-sessions.json");
57054
- const sessions = readRaw(sessionsPath);
58753
+ const sessions = readRaw2(sessionsPath);
57055
58754
  if (sessions) {
57056
58755
  let changed = false;
57057
58756
  const kept = sessions.filter((s) => {
@@ -59270,9 +60969,9 @@ function serveMembershipTool(cfg, principal, binding, body) {
59270
60969
  return answer(relocationResult("adopt", adopt(cfg, principal, binding, String(call.args.alias ?? ""), String(call.args.path ?? ""), apply4), apply4));
59271
60970
  default: {
59272
60971
  const named2 = call.name === "sdd_rename_project" ? String(call.args.project ?? "") : call.name === "sdd_externalize_subsystem" ? String(call.args.subsystem ?? "") : String(call.args.alias ?? "");
59273
- const located = typeof call.args.source === "string" ? call.args.source : typeof call.args.path === "string" ? call.args.path : void 0;
60972
+ const located2 = typeof call.args.source === "string" ? call.args.source : typeof call.args.path === "string" ? call.args.path : void 0;
59274
60973
  const newId = call.name === "sdd_promote_member" && typeof call.args.id === "string" && call.args.id ? call.args.id : void 0;
59275
- const screened = screen(cfg.dataDir, binding, call.name, named2, located, newId);
60974
+ const screened = screen(cfg.dataDir, binding, call.name, named2, located2, newId);
59276
60975
  if (screened.refusal !== void 0) return answer({ content: [{ type: "text", text: screened.refusal }], isError: true });
59277
60976
  return { screen: screened };
59278
60977
  }
@@ -66016,7 +67715,7 @@ async function statusCommand(opts) {
66016
67715
  `);
66017
67716
  return;
66018
67717
  }
66019
- await runStatus({ subsystem: opts.subsystem, recursive: opts.recursive });
67718
+ await runStatus({ subsystem: opts.subsystem, ...opts.recursive === false ? { memberDepth: 0 } : {} });
66020
67719
  }
66021
67720
  async function lockCheckCommand(options) {
66022
67721
  const verdict = checkApproval(options.strict === true);
@@ -66041,34 +67740,34 @@ program.command("status").description("Show a hierarchical completeness graph of
66041
67740
  program.command("doctor").description("Health check: flags stale generated guides/skills, an unregistered MCP server, spec-tree issues and the chaining migration still pending").option("--fix", "regenerate stale in-project guides/context/skills, register the MCP server, then apply the chaining migration once confirmed").option("--report <section>", "print one section's report and nothing else, writing nothing (chaining | composed-validation); never combines with --fix").option("-y, --yes", "answer the chaining migration's confirmation (for a non-interactive --fix); a write outside the project root also needs --global").option("--global", "consent to the --fix writes outside the project root (a machine-wide MCP config, the legacy global plugin); with --yes it answers their confirmation, and each replaced file is backed up beside itself").action(async (opts) => {
66042
67741
  await runDoctor({ fix: opts.fix, report: opts.report, yes: opts.yes, global: opts.global === true });
66043
67742
  });
66044
- async function runRules() {
66045
- await listRules3();
67743
+ function runRules() {
67744
+ listRules3();
66046
67745
  }
66047
- async function runPatterns() {
66048
- await listPatterns();
67746
+ function runPatterns() {
67747
+ listPatterns();
66049
67748
  }
66050
- async function runVariants() {
66051
- await listVariants();
67749
+ function runVariants() {
67750
+ listVariants();
66052
67751
  }
66053
67752
  async function runPacks(action, arg, options = {}) {
66054
67753
  if (action === "add") await addPack(arg, options.global);
66055
- else if (action === "list") await listPacks();
67754
+ else if (action === "list") listPacks();
66056
67755
  else if (action === "remove") await removePack(arg, options.global);
66057
67756
  }
66058
67757
  async function runPack(action, arg, options = {}) {
66059
- if (action === "init") await initPack(arg, { kind: options.kind === "code" ? "code" : "declarative", dir: options.dir, skill: options.skill });
66060
- else if (action === "build") await buildPack(arg ?? ".", { out: options.out });
67758
+ if (action === "init") initPack(arg, { kind: options.kind === "code" ? "code" : "declarative", dir: options.dir, skill: options.skill });
67759
+ else if (action === "build") buildPack(arg ?? ".", { out: options.out });
66061
67760
  else if (action === "add") await addPack(arg, options.global, options.yes);
66062
- else if (action === "list") await listPacks();
67761
+ else if (action === "list") listPacks();
66063
67762
  else if (action === "remove") await removePack(arg, options.global, options.yes);
66064
67763
  else if (action === "install") await installPack(arg, options.yes);
66065
- else if (action === "uninstall") await uninstallStorePack(arg);
66066
- else if (action === "which") await whichPack(arg);
67764
+ else if (action === "uninstall") uninstallStorePack(arg);
67765
+ else if (action === "which") whichPack(arg);
66067
67766
  else if (action === "use") await usePack(arg, { source: options.source, bundle: options.bundle, pin: options.pin, yes: options.yes });
66068
67767
  else if (action === "unuse") await unusePack(arg, options.yes);
66069
- else if (action === "bundle") await bundlePack(arg, { all: options.all });
67768
+ else if (action === "bundle") bundlePack(arg, { all: options.all });
66070
67769
  else if (action === "sync") await syncPacks();
66071
- else if (action === "impact") await impactPack(arg);
67770
+ else if (action === "impact") impactPack(arg);
66072
67771
  else throw new WaironError("unknown pack action (expected init | build | install | uninstall | which | use | unuse | impact | bundle | sync | add | list | remove)");
66073
67772
  }
66074
67773
  var executionCmd = program.command("execution").description("Execution budgets: what each agent's work is like and the model/turn/tool allowance it earns");
@@ -66237,9 +67936,9 @@ var mcpCmd = program.command("mcp").description("MCP (Model Context Protocol) se
66237
67936
  mcpCmd.command("serve").description("Start the wairon MCP server (stdio transport \u2014 use in mcpServers config)").action(async () => {
66238
67937
  await runMcpServe();
66239
67938
  });
66240
- async function mcpInstallCommand(opts) {
67939
+ function mcpInstallCommand(opts) {
66241
67940
  const hostedToken = opts.hosted ? opts.token ?? storedCredentialFor(String(opts.hosted).replace(/\/+$/, "")) ?? void 0 : void 0;
66242
- await runMcpInstall({
67941
+ runMcpInstall({
66243
67942
  global: opts.global,
66244
67943
  configDir: opts.configDir,
66245
67944
  backend: opts.backend,
@@ -66249,8 +67948,8 @@ async function mcpInstallCommand(opts) {
66249
67948
  });
66250
67949
  }
66251
67950
  mcpCmd.command("install").description("Register the wairon MCP server in the Claude Code settings.json or Antigravity mcp_config.json").option("--global", "install into the global/home config instead of the project (respects CLAUDE_CONFIG_DIR / GEMINI_CONFIG_DIR)").option("--config-dir <path>", "explicit config dir to install into (validated for the agent; requires --backend). Reliable alternative to relying on the shell's CLAUDE_CONFIG_DIR").option("--backend <type>", "target AI assistant: claude | gemini (aliases: agy, antigravity \u2192 gemini)").option("--hosted <url>", "register a HOSTED entry against this instance instead of the local stdio server").option("--project <id>", "hosted: the project the agent should be bound to").option("--token <token>", "hosted: the bearer to carry (else the credential stored by `wairon login`)").action(mcpInstallCommand);
66252
- mcpCmd.command("status").description("Show whether the wairon MCP server is registered in Claude Code and Antigravity settings").action(async () => {
66253
- await runMcpStatus();
67951
+ mcpCmd.command("status").description("Show whether the wairon MCP server is registered in Claude Code and Antigravity settings").action(() => {
67952
+ runMcpStatus();
66254
67953
  });
66255
67954
  program.command("produce <target>").description("project the local project's specs to a producer target (notion | miro)").option("--page <id>", "parent page/board id in the target").option("--token <token>", "integration token (else env, else interactive prompt)").action(async (target, opts) => {
66256
67955
  await runProduce(target, { page: opts.page, token: opts.token });
@@ -66413,11 +68112,11 @@ domainsCmd.command("remove <id>").alias("rm").description("Remove a free-standin
66413
68112
  await runDomainsRemove(id);
66414
68113
  });
66415
68114
  var skillsCmd = program.command("skills").description("Manage the SDD skills installed into your AI tools");
66416
- skillsCmd.command("list").alias("ls").description("List the built-in SDD skills").action(async () => {
66417
- await runSkillsList();
68115
+ skillsCmd.command("list").alias("ls").description("List the built-in SDD skills").action(() => {
68116
+ runSkillsList();
66418
68117
  });
66419
- skillsCmd.command("install").alias("sync").description("Install/refresh the SDD skills into each active target tool").action(async () => {
66420
- await runSkillsInstall();
68118
+ skillsCmd.command("install").alias("sync").description("Install/refresh the SDD skills into each active target tool").action(() => {
68119
+ runSkillsInstall();
66421
68120
  });
66422
68121
  async function main() {
66423
68122
  try {