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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2959,7 +2959,6 @@ var require_dist = __commonJS({
2959
2959
  "# languages:",
2960
2960
  "# rust:",
2961
2961
  "# unsupportedFlow: {}",
2962
- "# foreignBuiltins: []",
2963
2962
  "languages: {}",
2964
2963
  "",
2965
2964
  "# Reusable, versioned architecture patterns:",
@@ -3186,12 +3185,12 @@ var require_dist = __commonJS({
3186
3185
  const suggestedFileName = `${sealed.name}-${sealed.version}.wpack`;
3187
3186
  return { archive: archiveBytes, info, suggestedFileName };
3188
3187
  }
3189
- function inspectArchive(archiveBytes) {
3190
- const entries = listEntries(archiveBytes);
3188
+ function inspectArchive(archive) {
3189
+ const entries = listEntries(archive);
3191
3190
  if (!entries.some((entry) => entry.path === ENVELOPE_FILENAME)) {
3192
3191
  throw new Error("not a .wpack archive (missing wairon-pack.yaml envelope)");
3193
3192
  }
3194
- const envelopeBytes = inflateEntry(archiveBytes, ENVELOPE_FILENAME);
3193
+ const envelopeBytes = inflateEntry(archive, ENVELOPE_FILENAME);
3195
3194
  const manifest = parseManifest(decode(envelopeBytes));
3196
3195
  const compatible = checkCompatibility(manifest, SDK_VERSION);
3197
3196
  const fileEntries = entries.filter((entry) => entry.kind === "file");
@@ -3209,13 +3208,13 @@ var require_dist = __commonJS({
3209
3208
  if (manifest.minWaironVersion !== void 0) info.minWaironVersion = manifest.minWaironVersion;
3210
3209
  return info;
3211
3210
  }
3212
- function extractPack(archiveBytes, destDir, limits) {
3211
+ function extractPack(archive, destDir, limits) {
3213
3212
  const effectiveLimits = limits === void 0 ? defaultLimits() : limits;
3214
- const entries = listEntries(archiveBytes);
3213
+ const entries = listEntries(archive);
3215
3214
  const plan6 = planExtraction(entries, effectiveLimits);
3216
3215
  const files = [];
3217
3216
  for (const approvedPath of plan6.paths) {
3218
- const contents = inflateEntry(archiveBytes, approvedPath);
3217
+ const contents = inflateEntry(archive, approvedPath);
3219
3218
  files.push({ path: approvedPath, contents });
3220
3219
  }
3221
3220
  const envelope = files.find((file) => file.path === ENVELOPE_FILENAME);
@@ -3272,28 +3271,28 @@ var require_dist = __commonJS({
3272
3271
  const suggestedFileName = `${slugify(projectName)}.waitree`;
3273
3272
  return { archive: archiveBytes, manifest: sealed, suggestedFileName, fileCount: files.length };
3274
3273
  }
3275
- function inspectTreeArchive3(archiveBytes) {
3276
- const entries = listEntries(archiveBytes);
3274
+ function inspectTreeArchive3(archive) {
3275
+ const entries = listEntries(archive);
3277
3276
  if (!entries.some((entry) => entry.path === TREE_ENVELOPE_FILENAME)) {
3278
3277
  throw new Error("not a .waitree archive (missing wairon-tree.yaml envelope)");
3279
3278
  }
3280
- const envelopeBytes = inflateEntry(archiveBytes, TREE_ENVELOPE_FILENAME);
3279
+ const envelopeBytes = inflateEntry(archive, TREE_ENVELOPE_FILENAME);
3281
3280
  const manifest = parseTreeManifest(decode(envelopeBytes));
3282
3281
  const compatible = checkTreeCompatibility(manifest, SDK_VERSION);
3283
3282
  const fileEntries = entries.filter((entry) => entry.kind === "file");
3284
3283
  const totalUncompressedBytes = entries.reduce((sum, entry) => sum + entry.uncompressedSize, 0);
3285
3284
  return { manifest, entryCount: fileEntries.length, totalUncompressedBytes, compatible };
3286
3285
  }
3287
- function extractTreeArchive3(archiveBytes, destDir, limits, refuseExecutableEntries) {
3286
+ function extractTreeArchive3(archive, destDir, limits, refuseExecutableEntries) {
3288
3287
  const effectiveLimits = limits === void 0 ? defaultTreeLimits() : limits;
3289
- const entries = listEntries(archiveBytes);
3288
+ const entries = listEntries(archive);
3290
3289
  const plan6 = planTreeExtraction(entries, effectiveLimits);
3291
3290
  if (refuseExecutableEntries === true) {
3292
3291
  assertPortableTreePaths(plan6.paths);
3293
3292
  }
3294
3293
  const files = [];
3295
3294
  for (const approvedPath of plan6.paths) {
3296
- const contents = inflateEntry(archiveBytes, approvedPath);
3295
+ const contents = inflateEntry(archive, approvedPath);
3297
3296
  files.push({ path: approvedPath, contents });
3298
3297
  }
3299
3298
  const envelope = files.find((file) => file.path === TREE_ENVELOPE_FILENAME);
@@ -3326,9 +3325,9 @@ var require_dist = __commonJS({
3326
3325
  }
3327
3326
  function readInnerIdentity(files, manifest) {
3328
3327
  const innerPath = manifest.kind === "code" ? "package.json" : manifest.entry;
3329
- const inner = files.find((file) => file.path === innerPath);
3330
- if (!inner) throw new Error(`pack build: inner pack manifest "${innerPath}" is missing`);
3331
- const parsed = manifest.kind === "code" ? JSON.parse(decode(inner.contents)) : (0, import_js_yaml3.load)(decode(inner.contents));
3328
+ const inner2 = files.find((file) => file.path === innerPath);
3329
+ if (!inner2) throw new Error(`pack build: inner pack manifest "${innerPath}" is missing`);
3330
+ const parsed = manifest.kind === "code" ? JSON.parse(decode(inner2.contents)) : (0, import_js_yaml3.load)(decode(inner2.contents));
3332
3331
  const name = parsed?.name;
3333
3332
  const version = parsed?.version;
3334
3333
  if (typeof name !== "string" || typeof version !== "string") {
@@ -3394,7 +3393,6 @@ __export(src_exports, {
3394
3393
  AgentTemplateSchema: () => AgentTemplateSchema,
3395
3394
  BACKEND_COMMANDS: () => BACKEND_COMMANDS,
3396
3395
  BUILTIN_PROFILES: () => BUILTIN_PROFILES,
3397
- BUILTIN_TYPES: () => BUILTIN_TYPES,
3398
3396
  BoundaryItemSchema: () => BoundaryItemSchema,
3399
3397
  BuiltinTargetConfigSchema: () => BuiltinTargetConfigSchema,
3400
3398
  BuiltinTargetSchema: () => BuiltinTargetSchema,
@@ -3428,6 +3426,7 @@ __export(src_exports, {
3428
3426
  DurabilitySchema: () => DurabilitySchema,
3429
3427
  EXTERNAL_ALIAS_RE: () => EXTERNAL_ALIAS_RE,
3430
3428
  EndpointSchema: () => EndpointSchema,
3429
+ EnumValueSchema: () => EnumValueSchema,
3431
3430
  EventBindingSchema: () => EventBindingSchema,
3432
3431
  ExtDataSchema: () => ExtDataSchema,
3433
3432
  ExternalDeclarationSchema: () => ExternalDeclarationSchema,
@@ -3442,6 +3441,7 @@ __export(src_exports, {
3442
3441
  GeminiExporter: () => GeminiExporter,
3443
3442
  GroupSpecSchema: () => GroupSpecSchema,
3444
3443
  GuaranteeSchema: () => GuaranteeSchema,
3444
+ HOLDABLE_PRIMITIVES: () => HOLDABLE_PRIMITIVES,
3445
3445
  HttpMethodSchema: () => HttpMethodSchema,
3446
3446
  ImplementationSpecSchema: () => ImplementationSpecSchema,
3447
3447
  InterfaceSpecSchema: () => InterfaceSpecSchema,
@@ -3461,6 +3461,7 @@ __export(src_exports, {
3461
3461
  NarrativeStepTypeSchema: () => NarrativeStepTypeSchema,
3462
3462
  OutputTargetSchema: () => OutputTargetSchema,
3463
3463
  PATTERN_TYPES: () => PATTERN_TYPES,
3464
+ PRIMITIVE_TYPES: () => PRIMITIVE_TYPES,
3464
3465
  PROJECT_ID_RE: () => PROJECT_ID_RE,
3465
3466
  PROJECT_KINDS: () => PROJECT_KINDS,
3466
3467
  PUBLIC_NAME_RE: () => PUBLIC_NAME_RE,
@@ -3548,6 +3549,7 @@ __export(src_exports, {
3548
3549
  buildRuleContext: () => buildRuleContext,
3549
3550
  callSitesOf: () => callSitesOf,
3550
3551
  canonicalTypeRef: () => canonicalTypeRef,
3552
+ canonicalTypeText: () => canonicalTypeText,
3551
3553
  captureApprovedSpecs: () => captureApprovedSpecs,
3552
3554
  checkDeclarativePack: () => checkDeclarativePack,
3553
3555
  clearLoaderIssues: () => clearLoaderIssues,
@@ -3592,6 +3594,7 @@ __export(src_exports, {
3592
3594
  detectDomainCandidates: () => detectDomainCandidates,
3593
3595
  diagnoseProjectPacks: () => diagnoseProjectPacks,
3594
3596
  diagramSetIndex: () => diagramSetIndex,
3597
+ dialectOf: () => dialectOf,
3595
3598
  diffAgainstApproval: () => diffAgainstApproval,
3596
3599
  diffSize: () => diffSize,
3597
3600
  discoverPacks: () => discoverPacks,
@@ -3599,6 +3602,7 @@ __export(src_exports, {
3599
3602
  effectiveDetail: () => effectiveDetail,
3600
3603
  effectiveProjectId: () => effectiveProjectId,
3601
3604
  emptyExtensions: () => emptyExtensions,
3605
+ emptyTypeSpellingFacts: () => emptyTypeSpellingFacts,
3602
3606
  ensureDir: () => ensureDir,
3603
3607
  ensureProjectInitialized: () => ensureProjectInitialized,
3604
3608
  excerptParent: () => excerpt,
@@ -3645,6 +3649,7 @@ __export(src_exports, {
3645
3649
  importSpecTree: () => importSpecTree,
3646
3650
  injectGuide: () => injectGuide,
3647
3651
  installPackFromDirectory: () => installPackFromDirectory,
3652
+ interfaceCanonicalTypes: () => interfaceCanonicalTypes,
3648
3653
  interfaceGenericParameters: () => interfaceGenericParameters,
3649
3654
  internalizeMember: () => internalizeMember,
3650
3655
  isDraftSubsystem: () => isDraftSubsystem,
@@ -3656,9 +3661,11 @@ __export(src_exports, {
3656
3661
  isProjectInitialized: () => isProjectInitialized,
3657
3662
  isProvidedBy: () => isProvidedBy,
3658
3663
  isRetired: () => isRetired,
3664
+ isTypeVocabulary: () => isTypeVocabulary,
3659
3665
  judgesCode: () => judgesCode,
3660
3666
  keyIn: () => keyIn,
3661
3667
  landReference: () => landReference,
3668
+ languageOfSourcePath: () => languageOfSourcePath,
3662
3669
  listDirectChainedSubprojects: () => listDirectChainedSubprojects,
3663
3670
  listFiles: () => listFiles,
3664
3671
  listFilesRecursive: () => listFilesRecursive,
@@ -3701,6 +3708,7 @@ __export(src_exports, {
3701
3708
  moveMethods: () => moveMethods,
3702
3709
  moveMountToMembers: () => moveMountToMembers,
3703
3710
  nameKey: () => nameKey,
3711
+ namesAnInteger: () => namesAnInteger,
3704
3712
  narrativeStepForeignFields: () => narrativeStepForeignFields,
3705
3713
  normalizeReferences: () => normalizeReferences2,
3706
3714
  ownerOf: () => ownerOf,
@@ -3712,6 +3720,8 @@ __export(src_exports, {
3712
3720
  parseDeclarativePack: () => parseDeclarativePack,
3713
3721
  parseDeclaredCall: () => parseDeclaredCall,
3714
3722
  parseMemberSource: () => parseMemberSource,
3723
+ parseTypeExpression: () => parseTypeExpression,
3724
+ parseTypePosition: () => parseTypePosition,
3715
3725
  parseYaml: () => parseYaml,
3716
3726
  passesIntentFloor: () => passesIntentFloor,
3717
3727
  pathExists: () => pathExists,
@@ -3725,6 +3735,7 @@ __export(src_exports, {
3725
3735
  provisionProject: () => provisionProject,
3726
3736
  qualifiedTypeId: () => qualifiedTypeId,
3727
3737
  rangeProblem: () => rangeProblem,
3738
+ readCodeAnnotation: () => readCodeAnnotation,
3728
3739
  readFileOrNull: () => readFileOrNull,
3729
3740
  readJsonFile: () => readJsonFile,
3730
3741
  readLockRecord: () => readLockRecord,
@@ -3747,6 +3758,7 @@ __export(src_exports, {
3747
3758
  renderDiagram: () => renderDiagram,
3748
3759
  repairForeignStepFields: () => repairForeignStepFields,
3749
3760
  repairSignatures: () => repairSignatures,
3761
+ repairTypeSpellings: () => repairTypeSpellings,
3750
3762
  repointExternal: () => repointExternal,
3751
3763
  requiredPolicies: () => requiredPolicies,
3752
3764
  resolveAgentTopology: () => resolveAgentTopology,
@@ -3758,7 +3770,7 @@ __export(src_exports, {
3758
3770
  resolveImport: () => resolveImport,
3759
3771
  resolveInstalledPack: () => resolveInstalledPack,
3760
3772
  resolveProjectExports: () => resolveProjectExports,
3761
- resolveSignatures: () => resolveTree,
3773
+ resolveSignatures: () => resolveSignatures,
3762
3774
  resolveSubprojectForNamespace: () => resolveSubprojectForNamespace,
3763
3775
  resolveSubsystemExports: () => resolveSubsystemExports,
3764
3776
  resolveVariantGuidance: () => resolveVariantGuidance,
@@ -3794,9 +3806,15 @@ __export(src_exports, {
3794
3806
  technologyTokens: () => technologyTokens,
3795
3807
  toMarkdown: () => toMarkdown,
3796
3808
  typeBindingOf: () => typeBindingOf,
3809
+ typeCanonicalTypes: () => typeCanonicalTypes,
3810
+ typeDialectFor: () => typeDialectFor,
3797
3811
  typeGenericParameters: () => typeGenericParameters,
3812
+ typeIsMany: () => typeIsMany,
3798
3813
  typeMatchesRef: () => typeMatchesRef,
3814
+ typeNamedRefs: () => typeNamedRefs,
3815
+ typeProblemIntProposal: () => typeProblemIntProposal,
3799
3816
  typeSourceFiles: () => typeSourceFiles,
3817
+ typeSpellingFacts: () => typeSpellingFacts,
3800
3818
  uninstallPack: () => uninstallPack,
3801
3819
  updateMember: () => updateMember,
3802
3820
  updateSpec: () => updateSpec,
@@ -3811,7 +3829,8 @@ __export(src_exports, {
3811
3829
  writeReachDenials: () => writeReachDenials,
3812
3830
  writeReachPermits: () => writeReachPermits,
3813
3831
  writeRootGuideDelegator: () => writeRootGuideDelegator,
3814
- writeYamlFile: () => writeYamlFile
3832
+ writeYamlFile: () => writeYamlFile,
3833
+ writtenTypeRefs: () => writtenTypeRefs
3815
3834
  });
3816
3835
  module.exports = __toCommonJS(src_exports);
3817
3836
 
@@ -4260,6 +4279,14 @@ var AgentBriefSchema = import_zod2.z.object({
4260
4279
  instructions: import_zod2.z.string(),
4261
4280
  /** Rendered variant guidance, also folded into instructions */
4262
4281
  variantGuidance: import_zod2.z.string().optional(),
4282
+ /**
4283
+ * How the neutral types of the contracts this agent implements are spelled
4284
+ * in its language: the write table (type_dialect.mappingLines) of the dialect
4285
+ * for the language its implementations are written in, also folded into
4286
+ * instructions. Absent when no dialect is shipped for that language or the
4287
+ * agent implements nothing.
4288
+ */
4289
+ typeMapping: import_zod2.z.array(import_zod2.z.string()).optional(),
4263
4290
  /**
4264
4291
  * Set only on a brief for a member's agent (an id qualified `<alias>::`): the
4265
4292
  * member's directory relative to the root the brief was asked from. The
@@ -5272,14 +5299,16 @@ function locateScalar(tokens, path49) {
5272
5299
  const base = Math.min(...region.map((t) => t.col));
5273
5300
  const last = k === path49.length - 1;
5274
5301
  let at;
5275
- if (typeof segment === "string") {
5302
+ const items = region.map((t, i) => ({ t, i })).filter(({ t }) => t.kind === "item" && t.col === base);
5303
+ if (items.length === 0) {
5276
5304
  at = region.findIndex((t) => t.kind === "key" && t.col === base && t.name === segment);
5277
5305
  if (at < 0) return `no block key "${segment}" at ${path49.slice(0, k).join(".") || "the document root"}`;
5278
5306
  if (last) return region[at].valueStart !== void 0 ? region[at] : `"${segment}" holds no inline scalar`;
5279
5307
  } else {
5280
- const items = region.map((t, i) => ({ t, i })).filter(({ t }) => t.kind === "item" && t.col === base);
5281
- if (segment >= items.length) return `no block sequence item ${segment} at ${path49.slice(0, k).join(".")}`;
5282
- at = items[segment].i;
5308
+ if (!/^\d+$/.test(segment)) return `"${segment}" is not a sequence index at ${path49.slice(0, k).join(".") || "the document root"}`;
5309
+ const index = Number(segment);
5310
+ if (index >= items.length) return `no block sequence item ${segment} at ${path49.slice(0, k).join(".")}`;
5311
+ at = items[index].i;
5283
5312
  if (last) {
5284
5313
  const value = region[at + 1];
5285
5314
  return value && value.kind === "scalar" && value.line === region[at].line ? value : `item ${segment} holds no inline scalar`;
@@ -5751,7 +5780,7 @@ function registryOver(store, root) {
5751
5780
  const at = typeof entry.at === "string" ? entry.at : void 0;
5752
5781
  const covers = Array.isArray(entry.covers) ? entry.covers.filter((u) => typeof u === "string") : void 0;
5753
5782
  const next = rekeyAnchor(rename, { spec: entry.spec, at, covers });
5754
- const base = ["rules", "conformance", "carried", g, "findings", f];
5783
+ const base = ["rules", "conformance", "carried", String(g), "findings", String(f)];
5755
5784
  const named2 = { code: String(entry.code), spec: entry.spec, at: at ?? "" };
5756
5785
  const change = (path49, field, from, to) => {
5757
5786
  if (from === to) return;
@@ -5760,7 +5789,7 @@ function registryOver(store, root) {
5760
5789
  };
5761
5790
  change(["spec"], "spec", entry.spec, next.spec);
5762
5791
  if (at !== void 0) change(["at"], "at", at, next.at);
5763
- (covers ?? []).forEach((unit, u) => change(["covers", u], "covers", unit, next.covers[u]));
5792
+ (covers ?? []).forEach((unit, u) => change(["covers", String(u)], "covers", unit, next.covers[u]));
5764
5793
  });
5765
5794
  });
5766
5795
  store.rewriteScalars(edits, dryRun);
@@ -7087,11 +7116,10 @@ var GuaranteeSchema = import_zod8.z.string().min(1);
7087
7116
  var MethodParamSchema = import_zod8.z.object({
7088
7117
  name: import_zod8.z.string(),
7089
7118
  /**
7090
- * A primitive/builtin or a defined type id (qualified across subsystems, e.g.
7091
- * "billing.Invoice"), on its own or inside a generic, an array or a UNION:
7092
- * `Invoice | null`, `Promise<Invoice | null>`, `Invoice[] | null`. Every
7093
- * identifier the string names has to resolve — see the grammar on
7094
- * src/models/type-references.ts.
7119
+ * A type expression in the neutral type grammar (src/models/type-grammar.ts):
7120
+ * a primitive, a named type (`billing.Invoice`, `alias::name`), `list<T>`,
7121
+ * `set<T>`, `map<K, V>`, `T?`, a union of named types. Stored canonical; an
7122
+ * alias (`Invoice[]`, `Invoice | null`, `boolean`) is normalised on write.
7095
7123
  */
7096
7124
  type: import_zod8.z.string(),
7097
7125
  description: import_zod8.z.string().optional(),
@@ -7110,8 +7138,9 @@ var MethodSignatureSchema = import_zod8.z.object({
7110
7138
  description: import_zod8.z.string(),
7111
7139
  signature: import_zod8.z.string().optional(),
7112
7140
  // e.g. "save(key: string, data: Buffer): Promise<void>"
7113
- // e.g. "Promise<void>", or a union: "Invoice | null" — the commonest shape in
7114
- // any real tree. See the grammar on src/models/type-references.ts.
7141
+ // A type expression in the neutral grammar, at the returns position — the only
7142
+ // one `async T` and `void` may stand in: "async void", "Invoice?". Stored
7143
+ // canonical; see src/models/type-grammar.ts.
7115
7144
  returns: import_zod8.z.string().optional(),
7116
7145
  /** Structured parameters (authoritative for type checking when present). */
7117
7146
  params: import_zod8.z.array(MethodParamSchema).optional(),
@@ -7133,11 +7162,16 @@ var MethodSignatureSchema = import_zod8.z.object({
7133
7162
  */
7134
7163
  guarantees: import_zod8.z.array(GuaranteeSchema).optional(),
7135
7164
  /**
7136
- * State-effect direction of this method on its component's held state. Required on a
7137
- * durable Store's contract methods so the durability round-trip rule can pair external
7138
- * writes with hydration read-backs (MISSING_HYDRATION); optional elsewhere.
7165
+ * What this method does to its component's held state: `read` observes it, `write`
7166
+ * modifies an entity's domain fields, and `lifecycle` creates, destroys, or
7167
+ * (un)registers an entity's existence or membership without modifying its fields —
7168
+ * closed under composition (a lifecycle method calls only read and lifecycle methods,
7169
+ * LIFECYCLE_CALLS_WRITE). Required on a durable Store's contract methods so the
7170
+ * durability round-trip rule can pair mutations with hydration read-backs
7171
+ * (MISSING_HYDRATION); a Supervisor may call a data component it does not own only
7172
+ * through read and lifecycle methods (SUPERVISOR_WRITE_SHORTCUT). Optional elsewhere.
7139
7173
  */
7140
- effect: import_zod8.z.enum(["read", "write"]).optional(),
7174
+ effect: import_zod8.z.enum(["read", "write", "lifecycle"]).optional(),
7141
7175
  /**
7142
7176
  * Typed acknowledgment of a real caller OUTSIDE the modeled narrative graph
7143
7177
  * (runtime timer/hook, external system, sibling subsystem). Unused-detection
@@ -7587,12 +7621,12 @@ function implementationSourceFiles(impl) {
7587
7621
  for (const method of impl.methods ?? []) add2(method.sourcePath);
7588
7622
  return files;
7589
7623
  }
7590
- var TypeKindSchema = import_zod8.z.enum(["entity", "value-object", "signature"]);
7624
+ var TypeKindSchema = import_zod8.z.enum(["entity", "value-object", "signature", "enum"]);
7591
7625
  var TypeFieldSchema = import_zod8.z.object({
7592
7626
  name: import_zod8.z.string(),
7593
- // A primitive, or another type id (qualified across subsystems, e.g.
7594
- // "billing.Invoice") — on its own or inside a generic, an array or a union
7595
- // ("Invoice[] | null"). See the grammar on src/models/type-references.ts.
7627
+ // A type expression in the neutral grammar (src/models/type-grammar.ts): a
7628
+ // primitive, a named type ("billing.Invoice"), list/set/map, `T?`, a union
7629
+ // of named types. Stored canonical.
7596
7630
  type: import_zod8.z.string(),
7597
7631
  description: import_zod8.z.string().optional(),
7598
7632
  optional: import_zod8.z.boolean().default(false),
@@ -7646,9 +7680,13 @@ var InvariantSchema = import_zod8.z.object({
7646
7680
  /** The property that must hold, stated precisely enough to test against. */
7647
7681
  description: import_zod8.z.string().min(1)
7648
7682
  });
7683
+ var EnumValueSchema = import_zod8.z.object({
7684
+ name: import_zod8.z.string().min(1),
7685
+ description: import_zod8.z.string().optional()
7686
+ });
7649
7687
  var TypeSpecSchema = import_zod8.z.object({
7650
7688
  kind: TypeKindSchema,
7651
- // discriminator — entity | value-object | signature
7689
+ // discriminator — entity | value-object | signature | enum
7652
7690
  id: SpecIdSchema,
7653
7691
  name: import_zod8.z.string(),
7654
7692
  description: import_zod8.z.string().optional(),
@@ -7711,6 +7749,20 @@ var TypeSpecSchema = import_zod8.z.object({
7711
7749
  params: import_zod8.z.array(MethodParamSchema).optional(),
7712
7750
  /** A signature's one output type; required on kind signature and only there (SIGNATURE_TYPE_MEMBERS). */
7713
7751
  returns: import_zod8.z.string().optional(),
7752
+ /**
7753
+ * An enum's values, in declared order (the order is part of the design),
7754
+ * unique by nameKey; required and non-empty on kind enum and only there
7755
+ * (ENUM_MEMBERS).
7756
+ */
7757
+ values: import_zod8.z.array(EnumValueSchema).optional(),
7758
+ /**
7759
+ * A named scalar's one primitive (string, int, float, bool, bytes, date,
7760
+ * datetime or duration), in place of fields: a value-object that is a
7761
+ * newtype or type alias in every language. Read as a type position (the
7762
+ * grammar's `holds` position); only on a value-object and never beside
7763
+ * fields (NAMED_SCALAR_MEMBERS).
7764
+ */
7765
+ holds: import_zod8.z.string().optional(),
7714
7766
  createdAt: import_zod8.z.string().datetime(),
7715
7767
  updatedAt: import_zod8.z.string().datetime()
7716
7768
  });
@@ -7753,7 +7805,11 @@ var SurfaceTypeDefSchema = import_zod8.z.object({
7753
7805
  optional: import_zod8.z.boolean().optional()
7754
7806
  })).optional(),
7755
7807
  /** A signature's one output type; only on kind signature. */
7756
- returns: import_zod8.z.string().optional()
7808
+ returns: import_zod8.z.string().optional(),
7809
+ /** An enum's values in declared order; only on kind enum. */
7810
+ values: import_zod8.z.array(EnumValueSchema).optional(),
7811
+ /** A named scalar's one primitive, in place of fields; only on a value-object that declares it. */
7812
+ holds: import_zod8.z.string().optional()
7757
7813
  });
7758
7814
  var SurfaceTypeExportSchema = import_zod8.z.object({
7759
7815
  /** The type's public name in the producer's export table. */
@@ -7862,80 +7918,767 @@ var GroupSpecSchema = import_zod8.z.object({
7862
7918
  updatedAt: import_zod8.z.string().datetime()
7863
7919
  });
7864
7920
 
7865
- // src/models/type-references.ts
7866
- var BUILTIN_TYPES = /* @__PURE__ */ new Set([
7867
- "string",
7868
- "str",
7869
- "number",
7870
- "boolean",
7871
- "bool",
7872
- "float",
7873
- "double",
7874
- "int",
7875
- "integer",
7876
- "u8",
7877
- "u16",
7878
- "u32",
7879
- "u64",
7880
- "u128",
7881
- "usize",
7882
- "i8",
7883
- "i16",
7884
- "i32",
7885
- "i64",
7886
- "i128",
7887
- "isize",
7888
- "f32",
7889
- "f64",
7890
- "char",
7891
- "byte",
7921
+ // src/models/type-grammar.ts
7922
+ var PRIMITIVE_TYPES = ["string", "int", "float", "bool", "bytes", "date", "datetime", "duration", "void", "any"];
7923
+ var PRIMITIVES = new Set(PRIMITIVE_TYPES);
7924
+ var HOLDABLE_PRIMITIVES = PRIMITIVE_TYPES.filter((p) => p !== "void" && p !== "any");
7925
+ var PRIMITIVE_ALIASES = /* @__PURE__ */ new Map([
7926
+ ["boolean", "bool"],
7927
+ ["integer", "int"],
7928
+ ["long", "int"],
7929
+ ["i8", "int"],
7930
+ ["i16", "int"],
7931
+ ["i32", "int"],
7932
+ ["i64", "int"],
7933
+ ["i128", "int"],
7934
+ ["isize", "int"],
7935
+ ["u8", "int"],
7936
+ ["u16", "int"],
7937
+ ["u32", "int"],
7938
+ ["u64", "int"],
7939
+ ["u128", "int"],
7940
+ ["usize", "int"],
7941
+ ["double", "float"],
7942
+ ["f32", "float"],
7943
+ ["f64", "float"],
7944
+ ["Buffer", "bytes"],
7945
+ ["Uint8Array", "bytes"],
7946
+ ["Date", "datetime"],
7947
+ ["timestamp", "datetime"],
7948
+ ["object", "any"],
7949
+ ["unknown", "any"],
7950
+ ["json", "any"],
7951
+ ["Json", "any"],
7952
+ ["str", "string"]
7953
+ ]);
7954
+ var GENERIC_ALIASES = /* @__PURE__ */ new Map([
7955
+ ["list", { form: "list", arity: 1 }],
7956
+ ["Array", { form: "list", arity: 1 }],
7957
+ ["ReadonlyArray", { form: "list", arity: 1 }],
7958
+ ["List", { form: "list", arity: 1 }],
7959
+ ["vec", { form: "list", arity: 1 }],
7960
+ ["vector", { form: "list", arity: 1 }],
7961
+ ["set", { form: "set", arity: 1 }],
7962
+ ["Set", { form: "set", arity: 1 }],
7963
+ ["ReadonlySet", { form: "set", arity: 1 }],
7964
+ ["map", { form: "map", arity: 2 }],
7965
+ ["Map", { form: "map", arity: 2 }],
7966
+ ["Record", { form: "map", arity: 2 }],
7967
+ ["ReadonlyMap", { form: "map", arity: 2 }],
7968
+ ["dict", { form: "map", arity: 2 }],
7969
+ ["dictionary", { form: "map", arity: 2 }],
7970
+ ["HashMap", { form: "map", arity: 2 }],
7971
+ ["Option", { form: "optional", arity: 1 }],
7972
+ ["Optional", { form: "optional", arity: 1 }],
7973
+ ["Promise", { form: "async", arity: 1 }]
7974
+ ]);
7975
+ var NOT_NEUTRAL = /* @__PURE__ */ new Map([
7976
+ ["uuid", "a named value-object holding a string (e.g. order_id)"],
7977
+ ["decimal", "a named value-object (e.g. money), or float"],
7978
+ ["char", "string"],
7979
+ ["byte", "int, or bytes for binary data"],
7980
+ ["time", "datetime for an instant, or duration for an elapsed time"],
7981
+ ["tuple", "a named value-object"],
7982
+ ["result", "the success type as the returns (failures as findings or throws), or a named value-object"],
7983
+ ["error", "a named value-object"],
7984
+ ["never", "void"],
7985
+ ["box", "the inner type (ownership is an implementation detail)"],
7986
+ ["arc", "the inner type (ownership is an implementation detail)"],
7987
+ ["rc", "the inner type (ownership is an implementation detail)"],
7988
+ ["ref", "the inner type (ownership is an implementation detail)"],
7989
+ ["cell", "the inner type (ownership is an implementation detail)"],
7990
+ ["refcell", "the inner type (ownership is an implementation detail)"],
7991
+ ["mutex", "the inner type (synchronisation is an implementation detail)"],
7992
+ ["rwlock", "the inner type (synchronisation is an implementation detail)"],
7993
+ ["std", "a named type"],
7994
+ ["mcpserver", "a named type"]
7995
+ ]);
7996
+ var UTILITY_TYPES = /* @__PURE__ */ new Set([
7997
+ "Partial",
7998
+ "Required",
7999
+ "Readonly",
8000
+ "Pick",
8001
+ "Omit",
8002
+ "Exclude",
8003
+ "Extract",
8004
+ "NonNullable",
8005
+ "ReturnType",
8006
+ "Parameters",
8007
+ "InstanceType",
8008
+ "Awaited",
8009
+ "ConstructorParameters",
8010
+ "ThisType",
8011
+ "Uppercase",
8012
+ "Lowercase"
8013
+ ]);
8014
+ var NONES = /* @__PURE__ */ new Set(["null", "undefined"]);
8015
+ var LOWER_PRIMITIVE = new Map([
8016
+ ...PRIMITIVE_TYPES.map((p) => [p, p]),
8017
+ ...[...PRIMITIVE_ALIASES].map(([k, v]) => [k.toLowerCase(), v])
8018
+ ]);
8019
+ var LOWER_GENERIC = new Map([...GENERIC_ALIASES].map(([k, v]) => [k.toLowerCase(), v]));
8020
+ function isTypeVocabulary(name) {
8021
+ const lower = name.toLowerCase();
8022
+ return LOWER_PRIMITIVE.has(lower) || LOWER_GENERIC.has(lower) || NOT_NEUTRAL.has(lower) || NONES.has(lower) || lower === "true" || lower === "false" || lower === "number";
8023
+ }
8024
+ function primitiveOf(name) {
8025
+ if (PRIMITIVES.has(name)) return name;
8026
+ const exact = PRIMITIVE_ALIASES.get(name);
8027
+ if (exact) return exact;
8028
+ return LOWER_PRIMITIVE.get(name.toLowerCase());
8029
+ }
8030
+ function genericOf(name) {
8031
+ return GENERIC_ALIASES.get(name) ?? LOWER_GENERIC.get(name.toLowerCase());
8032
+ }
8033
+ var TypeSyntaxError = class extends Error {
8034
+ };
8035
+ var PUNCTUATION = ["...", "::", "=>", "<", ">", ",", "|", "?", "(", ")", "[", "]", "{", "}", ":", "&", ";", "."];
8036
+ function tokenize2(text3) {
8037
+ const out = [];
8038
+ let i = 0;
8039
+ while (i < text3.length) {
8040
+ const c = text3[i];
8041
+ if (/\s/.test(c)) {
8042
+ i++;
8043
+ continue;
8044
+ }
8045
+ if (/[A-Za-z_$]/.test(c)) {
8046
+ let j = i + 1;
8047
+ while (j < text3.length && /[A-Za-z0-9_$-]/.test(text3[j])) j++;
8048
+ while (text3[j - 1] === "-") j--;
8049
+ out.push({ t: "id", v: text3.slice(i, j) });
8050
+ i = j;
8051
+ continue;
8052
+ }
8053
+ if (/[0-9]/.test(c)) {
8054
+ let j = i + 1;
8055
+ while (j < text3.length && /[0-9.]/.test(text3[j])) j++;
8056
+ out.push({ t: "num", v: text3.slice(i, j) });
8057
+ i = j;
8058
+ continue;
8059
+ }
8060
+ if (c === "'" || c === '"' || c === "`") {
8061
+ let j = i + 1;
8062
+ while (j < text3.length && text3[j] !== c) j += text3[j] === "\\" ? 2 : 1;
8063
+ if (j >= text3.length) throw new TypeSyntaxError(`an unterminated string literal at "${text3.slice(i)}"`);
8064
+ out.push({ t: "str", v: text3.slice(i, j + 1) });
8065
+ i = j + 1;
8066
+ continue;
8067
+ }
8068
+ const punct = PUNCTUATION.find((p) => text3.startsWith(p, i));
8069
+ if (!punct) throw new TypeSyntaxError(`"${c}" is not part of any type spelling`);
8070
+ out.push({ t: "p", v: punct });
8071
+ i += punct.length;
8072
+ }
8073
+ return out;
8074
+ }
8075
+ var RawParser = class {
8076
+ constructor(tokens) {
8077
+ this.tokens = tokens;
8078
+ this.at = 0;
8079
+ }
8080
+ /** type-position: an optional `async` prefix, one type, and nothing after it. */
8081
+ position() {
8082
+ if (this.tokens.length === 0) throw new TypeSyntaxError("the position is empty");
8083
+ const node = this.isAsyncPrefix() ? (this.at++, { k: "async", inner: this.type() }) : this.type();
8084
+ if (this.at < this.tokens.length) throw new TypeSyntaxError(`"${this.rest()}" is left over after the type`);
8085
+ return node;
8086
+ }
8087
+ isAsyncPrefix() {
8088
+ const first = this.tokens[0];
8089
+ const next = this.tokens[1];
8090
+ if (!first || first.t !== "id" || first.v !== "async" || !next) return false;
8091
+ return next.t !== "p" || ["(", "{", "[", "::"].includes(next.v);
8092
+ }
8093
+ rest() {
8094
+ return this.tokens.slice(this.at).map((t) => t.v).join(" ");
8095
+ }
8096
+ peek(offset = 0) {
8097
+ return this.tokens[this.at + offset];
8098
+ }
8099
+ isP(v, offset = 0) {
8100
+ const t = this.peek(offset);
8101
+ return !!t && t.t === "p" && t.v === v;
8102
+ }
8103
+ expect(v) {
8104
+ if (!this.isP(v)) {
8105
+ const got = this.peek();
8106
+ throw new TypeSyntaxError(got ? `expected "${v}" but found "${got.v}"` : `expected "${v}" but the text ended`);
8107
+ }
8108
+ this.at++;
8109
+ }
8110
+ type() {
8111
+ if (this.isP("|")) this.at++;
8112
+ const members = [this.intersection()];
8113
+ while (this.isP("|")) {
8114
+ this.at++;
8115
+ members.push(this.intersection());
8116
+ }
8117
+ return members.length === 1 ? members[0] : { k: "union", members };
8118
+ }
8119
+ intersection() {
8120
+ const members = [this.postfix()];
8121
+ while (this.isP("&")) {
8122
+ this.at++;
8123
+ members.push(this.postfix());
8124
+ }
8125
+ return members.length === 1 ? members[0] : { k: "intersection", members };
8126
+ }
8127
+ postfix() {
8128
+ let node = this.primary();
8129
+ for (; ; ) {
8130
+ if (this.isP("[") && this.isP("]", 1)) {
8131
+ this.at += 2;
8132
+ node = { k: "array", elem: node };
8133
+ } else if (this.isP("?")) {
8134
+ this.at++;
8135
+ node = { k: "opt", inner: node };
8136
+ } else {
8137
+ return node;
8138
+ }
8139
+ }
8140
+ }
8141
+ primary() {
8142
+ const tok = this.peek();
8143
+ if (!tok) throw new TypeSyntaxError("a type was expected but the text ended");
8144
+ if (tok.t === "str" || tok.t === "num") {
8145
+ this.at++;
8146
+ return { k: "literal", text: tok.v };
8147
+ }
8148
+ if (tok.t === "id" && (tok.v === "keyof" || tok.v === "typeof" || tok.v === "unique") && this.peek(1)?.t === "id") {
8149
+ this.at++;
8150
+ return { k: "operator", op: tok.v, inner: this.postfix() };
8151
+ }
8152
+ const next = this.peek(1);
8153
+ if (tok.t === "id" && tok.v === "readonly" && next && (next.t === "id" || next.t === "p" && (next.v === "(" || next.v === "["))) {
8154
+ this.at++;
8155
+ return this.postfix();
8156
+ }
8157
+ if (tok.t === "id" || tok.t === "p" && tok.v === "::") return this.named();
8158
+ if (tok.t === "p" && tok.v === "(") return this.parenOrFunction();
8159
+ if (tok.t === "p" && tok.v === "{") return this.objectShape();
8160
+ if (tok.t === "p" && tok.v === "[") return this.tuple();
8161
+ throw new TypeSyntaxError(`"${tok.v}" cannot start a type`);
8162
+ }
8163
+ ident() {
8164
+ const tok = this.peek();
8165
+ if (!tok || tok.t !== "id") throw new TypeSyntaxError(tok ? `expected a name but found "${tok.v}"` : "expected a name but the text ended");
8166
+ this.at++;
8167
+ return tok.v;
8168
+ }
8169
+ named() {
8170
+ let name = "";
8171
+ if (this.isP("::")) {
8172
+ this.at++;
8173
+ name = "::";
8174
+ }
8175
+ name += this.ident();
8176
+ while (this.isP("::") || this.isP(".")) {
8177
+ const sep14 = this.peek().v;
8178
+ this.at++;
8179
+ name += sep14 + this.ident();
8180
+ }
8181
+ if (!this.isP("<")) return { k: "name", name };
8182
+ this.at++;
8183
+ const args = [this.type()];
8184
+ while (this.isP(",")) {
8185
+ this.at++;
8186
+ args.push(this.type());
8187
+ }
8188
+ this.expect(">");
8189
+ return { k: "name", name, args };
8190
+ }
8191
+ /** The index of the `)` closing the `(` at the cursor, or -1. */
8192
+ closingParen() {
8193
+ let depth = 0;
8194
+ for (let i = this.at; i < this.tokens.length; i++) {
8195
+ const t = this.tokens[i];
8196
+ if (t.t !== "p") continue;
8197
+ if (t.v === "(") depth++;
8198
+ else if (t.v === ")" && --depth === 0) return i;
8199
+ }
8200
+ return -1;
8201
+ }
8202
+ parenOrFunction() {
8203
+ const close2 = this.closingParen();
8204
+ const after = close2 === -1 ? void 0 : this.tokens[close2 + 1];
8205
+ if (!after || after.t !== "p" || after.v !== "=>") {
8206
+ this.expect("(");
8207
+ const inner2 = this.type();
8208
+ this.expect(")");
8209
+ return { k: "paren", inner: inner2 };
8210
+ }
8211
+ this.expect("(");
8212
+ const params = [];
8213
+ while (!this.isP(")")) {
8214
+ if (this.isP("...")) this.at++;
8215
+ this.ident();
8216
+ if (this.isP("?")) this.at++;
8217
+ if (this.isP(":")) {
8218
+ this.at++;
8219
+ params.push(this.type());
8220
+ }
8221
+ if (!this.isP(",")) break;
8222
+ this.at++;
8223
+ }
8224
+ this.expect(")");
8225
+ this.expect("=>");
8226
+ return { k: "function", params, ret: this.type() };
8227
+ }
8228
+ objectShape() {
8229
+ this.expect("{");
8230
+ const members = [];
8231
+ while (!this.isP("}")) {
8232
+ if (this.isP("[")) {
8233
+ this.at++;
8234
+ this.ident();
8235
+ this.expect(":");
8236
+ members.push(this.type());
8237
+ this.expect("]");
8238
+ } else {
8239
+ if (this.peek()?.t === "id" && this.peek().v === "readonly" && this.peek(1)?.t === "id") this.at++;
8240
+ const key = this.peek();
8241
+ if (!key || key.t !== "id" && key.t !== "str" && key.t !== "num") throw new TypeSyntaxError("an inline object member has no name");
8242
+ this.at++;
8243
+ if (this.isP("?")) this.at++;
8244
+ if (this.isP("(")) {
8245
+ members.push(this.parenOrFunctionMember());
8246
+ this.skipSeparator();
8247
+ continue;
8248
+ }
8249
+ }
8250
+ this.expect(":");
8251
+ members.push(this.type());
8252
+ this.skipSeparator();
8253
+ }
8254
+ this.expect("}");
8255
+ return { k: "object", members };
8256
+ }
8257
+ /** A method member of an inline object: (params): returns. */
8258
+ parenOrFunctionMember() {
8259
+ this.expect("(");
8260
+ const params = [];
8261
+ while (!this.isP(")")) {
8262
+ if (this.isP("...")) this.at++;
8263
+ this.ident();
8264
+ if (this.isP("?")) this.at++;
8265
+ if (this.isP(":")) {
8266
+ this.at++;
8267
+ params.push(this.type());
8268
+ }
8269
+ if (!this.isP(",")) break;
8270
+ this.at++;
8271
+ }
8272
+ this.expect(")");
8273
+ this.expect(":");
8274
+ return { k: "function", params, ret: this.type() };
8275
+ }
8276
+ skipSeparator() {
8277
+ if (this.isP(";") || this.isP(",")) this.at++;
8278
+ }
8279
+ tuple() {
8280
+ this.expect("[");
8281
+ const elems = [];
8282
+ while (!this.isP("]")) {
8283
+ elems.push(this.type());
8284
+ if (!this.isP(",")) break;
8285
+ this.at++;
8286
+ }
8287
+ this.expect("]");
8288
+ return { k: "tuple", elems };
8289
+ }
8290
+ };
8291
+ function readRaw(text3) {
8292
+ return new RawParser(tokenize2(text3)).position();
8293
+ }
8294
+ var TypeProblemError = class extends Error {
8295
+ constructor(code, detail, replacement) {
8296
+ super(detail);
8297
+ this.code = code;
8298
+ this.detail = detail;
8299
+ this.replacement = replacement;
8300
+ }
8301
+ };
8302
+ var RETURNS_POSITIONS = /* @__PURE__ */ new Set(["returns", "type-method-returns", "signature-returns"]);
8303
+ var LOOSE_NUMBER = "number";
8304
+ function primitive(name) {
8305
+ return { form: "primitive", name, args: [] };
8306
+ }
8307
+ function optionalOf(inner2) {
8308
+ if (inner2.form === "primitive" && inner2.name === "any") return inner2;
8309
+ if (inner2.form === "optional") throw new TypeProblemError("TYPE_POSITION_INVALID", "a type is made optional twice (T??)", "T?");
8310
+ if (inner2.form === "primitive" && inner2.name === "void") throw new TypeProblemError("TYPE_POSITION_INVALID", "void cannot be optional (void?)", "void");
8311
+ return { form: "optional", args: [inner2] };
8312
+ }
8313
+ function inner(ctx) {
8314
+ return { ...ctx, top: false, voidAllowed: false, omittable: false };
8315
+ }
8316
+ function normalise(node, ctx, notes) {
8317
+ switch (node.k) {
8318
+ case "paren":
8319
+ return normalise(node.inner, ctx, notes);
8320
+ case "async":
8321
+ return asyncOf(node.inner, ctx, notes, "async");
8322
+ case "array":
8323
+ return { form: "list", args: [normalise(node.elem, inner(ctx), notes)] };
8324
+ case "opt":
8325
+ return optionalOf(normalise(node.inner, inner(ctx), notes));
8326
+ case "union":
8327
+ return unionOf(node.members, ctx, notes);
8328
+ case "name":
8329
+ return node.args === void 0 ? bareName(node.name, ctx, notes) : appliedName(node.name, node.args, ctx, notes);
8330
+ case "literal":
8331
+ 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");
8332
+ case "object":
8333
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "an inline object shape is not in the grammar", "a named value-object");
8334
+ case "function":
8335
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "an inline function type is not in the grammar", "a signature type");
8336
+ case "intersection":
8337
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "an intersection (A & B) is not in the grammar", "a named type");
8338
+ case "tuple":
8339
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "a tuple is not in the grammar", "a named value-object");
8340
+ case "operator":
8341
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", `"${node.op}" is TypeScript's type algebra, not in the grammar`, "a named type");
8342
+ }
8343
+ }
8344
+ function asyncOf(innerNode, ctx, notes, spelled) {
8345
+ if (!ctx.top || !RETURNS_POSITIONS.has(ctx.position)) {
8346
+ throw new TypeProblemError("TYPE_POSITION_INVALID", `${spelled} may stand only at the top of a returns, not in a ${ctx.position}`);
8347
+ }
8348
+ const read2 = normalise(innerNode, { ...inner(ctx), voidAllowed: true }, notes);
8349
+ return { form: "async", args: [read2] };
8350
+ }
8351
+ function bareName(name, ctx, notes) {
8352
+ const lower = name.toLowerCase();
8353
+ if (NONES.has(name)) {
8354
+ 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");
8355
+ }
8356
+ if (lower === "true" || lower === "false") {
8357
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", `${name} is a literal value, not a type`, "bool, or an enum type naming the values");
8358
+ }
8359
+ if (name === "number" || lower === "number") {
8360
+ if (ctx.looseNumber) return primitive(LOOSE_NUMBER);
8361
+ notes.notNeutral ??= { detail: '"number" does not say whether it holds an integer \u2014 int or float?', replacement: "int or float" };
8362
+ return primitive("float");
8363
+ }
8364
+ const prim = primitiveOf(name);
8365
+ if (prim !== void 0) {
8366
+ if (prim === "void" && !ctx.voidAllowed) {
8367
+ 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"}`);
8368
+ }
8369
+ return primitive(prim);
8370
+ }
8371
+ if (genericOf(name) !== void 0) {
8372
+ throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" needs its type arguments`, `${genericOf(name)?.form ?? "list"}<...>`);
8373
+ }
8374
+ const legacy = NOT_NEUTRAL.get(lower);
8375
+ if (legacy !== void 0) {
8376
+ notes.notNeutral ??= { detail: `"${name}" has no neutral meaning`, replacement: legacy };
8377
+ return primitive("any");
8378
+ }
8379
+ return { form: "named", name, args: [] };
8380
+ }
8381
+ function appliedName(name, args, ctx, notes) {
8382
+ const alias = genericOf(name);
8383
+ if (alias !== void 0) {
8384
+ if (args.length !== alias.arity) {
8385
+ throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" takes ${alias.arity} type argument${alias.arity === 1 ? "" : "s"}, ${args.length} given`);
8386
+ }
8387
+ if (alias.form === "async") return asyncOf(args[0], ctx, notes, `${name}<...>`);
8388
+ if (alias.form === "optional") return optionalOf(normalise(args[0], inner(ctx), notes));
8389
+ const read2 = args.map((a) => normalise(a, inner(ctx), notes));
8390
+ if (alias.form === "map") assertMapKey(read2[0], args[0]);
8391
+ return { form: alias.form, args: read2 };
8392
+ }
8393
+ if (UTILITY_TYPES.has(name)) {
8394
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", `"${name}<...>" is a TypeScript utility type, not in the grammar`, "a named type");
8395
+ }
8396
+ if (primitiveOf(name) !== void 0 || NONES.has(name)) {
8397
+ throw new TypeProblemError("TYPE_EXPRESSION_INVALID", `"${name}" takes no type arguments`);
8398
+ }
8399
+ const legacy = NOT_NEUTRAL.get(name.toLowerCase());
8400
+ if (legacy !== void 0) {
8401
+ args.forEach((a) => normalise(a, { ...inner(ctx), voidAllowed: true }, notes));
8402
+ notes.notNeutral ??= { detail: `"${name}<...>" has no neutral meaning`, replacement: legacy };
8403
+ return primitive("any");
8404
+ }
8405
+ return { form: "applied", name, args: args.map((a) => normalise(a, inner(ctx), notes)) };
8406
+ }
8407
+ function assertHoldable(expression) {
8408
+ if (expression.form === "primitive" && HOLDABLE_PRIMITIVES.includes(expression.name ?? "")) return;
8409
+ throw new TypeProblemError(
8410
+ "TYPE_POSITION_INVALID",
8411
+ `a named scalar holds one primitive (${HOLDABLE_PRIMITIVES.join(", ")}), not ${canonicalTypeText(expression)}`,
8412
+ "one of those primitives, or fields for a value-object that holds more than one value"
8413
+ );
8414
+ }
8415
+ function assertMapKey(key, written) {
8416
+ if (key.form === "named") return;
8417
+ if (key.form === "primitive" && (key.name === "string" || key.name === "int" || key.name === LOOSE_NUMBER)) return;
8418
+ if (key.form === "primitive" && key.name === "float" && written.k === "name" && written.name.toLowerCase() === "number") return;
8419
+ throw new TypeProblemError("TYPE_POSITION_INVALID", "a map key must be string, int or an enum", "map<string, V>");
8420
+ }
8421
+ function unionOf(members, ctx, notes) {
8422
+ const nones = members.filter((m) => m.k === "name" && m.args === void 0 && NONES.has(m.name));
8423
+ const rest = members.filter((m) => !(m.k === "name" && m.args === void 0 && NONES.has(m.name)));
8424
+ if (rest.length === 0) throw new TypeProblemError("TYPE_EXPRESSION_INVALID", "a union of nones names no type");
8425
+ if (rest.some((m) => m.k === "literal" || m.k === "name" && m.args === void 0 && /^(true|false)$/i.test(m.name))) {
8426
+ throw new TypeProblemError("TYPE_FORM_UNSUPPORTED", "a union of literal values is not in the grammar", "an enum type naming the values");
8427
+ }
8428
+ const memberCtx = inner(ctx);
8429
+ let hasNone = nones.length > 0;
8430
+ const read2 = rest.map((m) => {
8431
+ const e = normalise(m, memberCtx, notes);
8432
+ if (e.form !== "optional") return e;
8433
+ hasNone = true;
8434
+ return e.args[0];
8435
+ });
8436
+ let base;
8437
+ if (read2.length === 1) {
8438
+ base = read2[0];
8439
+ } else {
8440
+ const loose = read2.find((e) => e.form !== "named" && e.form !== "applied");
8441
+ if (loose) {
8442
+ 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");
8443
+ }
8444
+ base = { form: "union", args: read2 };
8445
+ }
8446
+ if (!hasNone) return base;
8447
+ const onlyUndefined = nones.length > 0 && nones.every((n) => n.name === "undefined") && read2.length === rest.length && !rest.some((m) => m.k === "opt");
8448
+ if (ctx.omittable && ctx.top && onlyUndefined) return base;
8449
+ return optionalOf(base);
8450
+ }
8451
+ function canonicalTypeText(expr) {
8452
+ switch (expr.form) {
8453
+ case "primitive":
8454
+ case "named":
8455
+ return expr.name ?? "";
8456
+ case "list":
8457
+ case "set":
8458
+ return `${expr.form}<${canonicalTypeText(expr.args[0])}>`;
8459
+ case "map":
8460
+ return `map<${canonicalTypeText(expr.args[0])}, ${canonicalTypeText(expr.args[1])}>`;
8461
+ case "optional": {
8462
+ const innerText = canonicalTypeText(expr.args[0]);
8463
+ return expr.args[0].form === "union" ? `(${innerText})?` : `${innerText}?`;
8464
+ }
8465
+ case "union":
8466
+ return expr.args.map(canonicalTypeText).join(" | ");
8467
+ case "async":
8468
+ return `async ${canonicalTypeText(expr.args[0])}`;
8469
+ case "applied":
8470
+ return `${expr.name}<${expr.args.map(canonicalTypeText).join(", ")}>`;
8471
+ }
8472
+ }
8473
+ function typeNamedRefs(expr) {
8474
+ if (expr.form === "named") return [expr.name];
8475
+ const own = expr.form === "applied" ? [expr.name] : [];
8476
+ return [...own, ...expr.args.flatMap(typeNamedRefs)];
8477
+ }
8478
+ function typeIsMany(expr) {
8479
+ if (expr.form === "list" || expr.form === "set" || expr.form === "map") return true;
8480
+ if (expr.form === "optional" || expr.form === "async") return typeIsMany(expr.args[0]);
8481
+ return false;
8482
+ }
8483
+ function contextFor(position, omittable2, looseNumber = false) {
8484
+ return { position, top: true, voidAllowed: RETURNS_POSITIONS.has(position), omittable: omittable2, looseNumber };
8485
+ }
8486
+ function parseTypePosition(text3, position, omittable2 = false) {
8487
+ let raw;
8488
+ try {
8489
+ raw = readRaw(text3);
8490
+ } catch (e) {
8491
+ if (!(e instanceof TypeSyntaxError)) throw e;
8492
+ return { expression: null, canonical: text3, problem: { code: "TYPE_EXPRESSION_INVALID", written: text3, detail: `"${text3}" does not parse as a type: ${e.message}` } };
8493
+ }
8494
+ const notes = {};
8495
+ try {
8496
+ const expression = normalise(raw, contextFor(position, omittable2), notes);
8497
+ if (position === "holds") assertHoldable(expression);
8498
+ const problem = notes.notNeutral ? { code: "TYPE_NOT_NEUTRAL", written: text3, detail: notes.notNeutral.detail, replacement: notes.notNeutral.replacement } : null;
8499
+ return { expression, canonical: canonicalTypeText(expression), problem };
8500
+ } catch (e) {
8501
+ if (!(e instanceof TypeProblemError)) throw e;
8502
+ return {
8503
+ expression: null,
8504
+ canonical: text3,
8505
+ problem: { code: e.code, written: text3, detail: `"${text3}": ${e.detail}`, ...e.replacement ? { replacement: e.replacement } : {} }
8506
+ };
8507
+ }
8508
+ }
8509
+ function parseTypeExpression(text3, position) {
8510
+ return parseTypePosition(text3, position);
8511
+ }
8512
+ function readCodeAnnotation(annotation, looseNumber) {
8513
+ let raw;
8514
+ try {
8515
+ raw = readRaw(annotation);
8516
+ } catch (e) {
8517
+ if (e instanceof TypeSyntaxError) return null;
8518
+ throw e;
8519
+ }
8520
+ try {
8521
+ return normalise(raw, contextFor("returns", false, looseNumber), {});
8522
+ } catch (e) {
8523
+ if (e instanceof TypeProblemError) return null;
8524
+ throw e;
8525
+ }
8526
+ }
8527
+ function writtenTypeRefs(text3, position) {
8528
+ const parse = parseTypePosition(text3, position);
8529
+ if (parse.expression && !parse.problem) return typeNamedRefs(parse.expression);
8530
+ try {
8531
+ return rawRefs(readRaw(text3));
8532
+ } catch (e) {
8533
+ if (e instanceof TypeSyntaxError) return [];
8534
+ throw e;
8535
+ }
8536
+ }
8537
+ function rawRefs(node) {
8538
+ switch (node.k) {
8539
+ case "name": {
8540
+ const own = isTypeVocabulary(node.name) || UTILITY_TYPES.has(node.name) ? [] : [node.name];
8541
+ return [...own, ...(node.args ?? []).flatMap(rawRefs)];
8542
+ }
8543
+ case "array":
8544
+ return rawRefs(node.elem);
8545
+ case "opt":
8546
+ case "paren":
8547
+ case "async":
8548
+ case "operator":
8549
+ return rawRefs(node.inner);
8550
+ case "union":
8551
+ case "intersection":
8552
+ case "object":
8553
+ return node.members.flatMap(rawRefs);
8554
+ case "tuple":
8555
+ return node.elems.flatMap(rawRefs);
8556
+ case "function":
8557
+ return [...node.params.flatMap(rawRefs), ...rawRefs(node.ret)];
8558
+ case "literal":
8559
+ return [];
8560
+ }
8561
+ }
8562
+ var Canonicalizer = class {
8563
+ constructor(specId, kind) {
8564
+ this.specId = specId;
8565
+ this.kind = kind;
8566
+ this.respellings = [];
8567
+ this.problems = [];
8568
+ }
8569
+ /** One position: its canonical text when it has one, else the text as written, recorded either way. */
8570
+ read(text3, position, path49, omittable2 = false) {
8571
+ const parse = parseTypePosition(text3, position, omittable2);
8572
+ if (parse.problem) {
8573
+ this.problems.push({ ...parse.problem, specId: this.specId, kind: this.kind, path: path49 });
8574
+ return text3;
8575
+ }
8576
+ if (parse.canonical !== text3) this.respellings.push({ specId: this.specId, kind: this.kind, path: path49, written: text3, stored: parse.canonical });
8577
+ return parse.canonical;
8578
+ }
8579
+ params(params, position, prefix) {
8580
+ return params.map((p) => ({ ...p, type: this.read(p.type, position, `${prefix}.${p.name}`, !!p.optional) }));
8581
+ }
8582
+ };
8583
+ function interfaceCanonicalTypes(intf) {
8584
+ const reader = new Canonicalizer(intf.id, "interface");
8585
+ const methods = (intf.methods ?? []).map((m) => {
8586
+ if (m.signatureFrom !== void 0) return m;
8587
+ const out = { ...m };
8588
+ if (m.params !== void 0) out.params = reader.params(m.params, "param", `methods.${m.name}.params`);
8589
+ if (typeof m.returns === "string") out.returns = reader.read(m.returns, "returns", `methods.${m.name}.returns`);
8590
+ return out;
8591
+ });
8592
+ return { spec: { ...intf, methods }, respellings: reader.respellings, problems: reader.problems };
8593
+ }
8594
+ function typeCanonicalTypes(type) {
8595
+ const reader = new Canonicalizer(type.id, "type");
8596
+ const out = { ...type };
8597
+ if (Array.isArray(type.fields)) {
8598
+ out.fields = type.fields.map((f) => ({ ...f, type: reader.read(f.type, "field", `fields.${f.name}`, !!f.optional) }));
8599
+ }
8600
+ if (Array.isArray(type.methods)) {
8601
+ out.methods = type.methods.map((m) => {
8602
+ const method = { ...m };
8603
+ if (m.params !== void 0) method.params = reader.params(m.params, "type-method-param", `methods.${m.name}.params`);
8604
+ if (typeof m.returns === "string") method.returns = reader.read(m.returns, "type-method-returns", `methods.${m.name}.returns`);
8605
+ return method;
8606
+ });
8607
+ }
8608
+ if (type.params !== void 0) out.params = reader.params(type.params, "signature-param", "params");
8609
+ if (typeof type.holds === "string") out.holds = reader.read(type.holds, "holds", "holds");
8610
+ if (typeof type.returns === "string") out.returns = reader.read(type.returns, "signature-returns", "returns");
8611
+ return { spec: out, respellings: reader.respellings, problems: reader.problems };
8612
+ }
8613
+ var INTEGER_WORDS = /* @__PURE__ */ new Set([
8614
+ "count",
8615
+ "counts",
8616
+ "size",
8617
+ "sizes",
8618
+ "length",
8619
+ "len",
8620
+ "limit",
8621
+ "limits",
8622
+ "port",
8623
+ "step",
8624
+ "steps",
8625
+ "index",
8626
+ "idx",
8627
+ "depth",
8628
+ "level",
8629
+ "levels",
8630
+ "days",
8631
+ "day",
8632
+ "hours",
8633
+ "minutes",
8634
+ "seconds",
8635
+ "ms",
8636
+ "millis",
8637
+ "milliseconds",
7892
8638
  "bytes",
7893
- "any",
7894
- "void",
7895
- "null",
7896
- "undefined",
7897
- "object",
7898
- "date",
7899
- "datetime",
7900
- "time",
7901
- "timestamp",
7902
- "duration",
7903
- "uuid",
7904
- "decimal",
7905
- "json",
7906
- "true",
7907
- "false",
7908
- "list",
7909
- "vector",
7910
- "vec",
7911
- "array",
7912
- "map",
7913
- "set",
7914
- "dict",
7915
- "dictionary",
7916
- "hashmap",
7917
- "tuple",
7918
- "result",
7919
- "option",
7920
- "box",
7921
- "arc",
7922
- "rc",
7923
- "ref",
7924
- "cell",
7925
- "refcell",
7926
- "mutex",
7927
- "rwlock",
7928
- "std",
7929
- "promise",
7930
- "record",
7931
- "json",
7932
- "unknown",
7933
- "never",
7934
- "error",
7935
- "mcpserver"
8639
+ "version",
8640
+ "total",
8641
+ "offset",
8642
+ "page",
8643
+ "pages",
8644
+ "retries",
8645
+ "attempts",
8646
+ "number",
8647
+ "num",
8648
+ "line",
8649
+ "lines",
8650
+ "column",
8651
+ "columns",
8652
+ "max",
8653
+ "min",
8654
+ "width",
8655
+ "height"
7936
8656
  ]);
8657
+ function namesAnInteger(name) {
8658
+ const words2 = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[\s_\-.]+/).filter(Boolean);
8659
+ return words2.some((word) => INTEGER_WORDS.has(word));
8660
+ }
8661
+ function positionNameOf(path49) {
8662
+ const segments = path49.split(".");
8663
+ const last = segments[segments.length - 1];
8664
+ return last === "returns" && segments.length >= 3 ? segments[segments.length - 2] : last;
8665
+ }
8666
+ var NUMBER_NAME = /(?<![\w:.-])number(?![\w:.-])/g;
8667
+ function typeProblemIntProposal(problem) {
8668
+ if (problem.code !== "TYPE_NOT_NEUTRAL" || problem.replacement !== "int or float") return null;
8669
+ if (problem.specId === void 0 || problem.kind === void 0 || problem.path === void 0) return null;
8670
+ if (!namesAnInteger(positionNameOf(problem.path))) return null;
8671
+ const parse = parseTypePosition(problem.written.replace(NUMBER_NAME, "int"), "returns");
8672
+ if (!parse.expression || parse.problem) return null;
8673
+ return { specId: problem.specId, kind: problem.kind, path: problem.path, written: problem.written, stored: parse.canonical };
8674
+ }
8675
+ function emptyTypeSpellingFacts() {
8676
+ return { respellings: [], problems: [] };
8677
+ }
8678
+
8679
+ // src/models/type-references.ts
7937
8680
  function extractTypeIdentifiers(typeStr) {
7938
- let cleaned = typeStr.replace(/\/\/.*$/gm, "").replace(/#.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
8681
+ let cleaned = typeStr.replace(/^\s*async\s+(?=\S)/, "").replace(/\/\/.*$/gm, "").replace(/#.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
7939
8682
  cleaned = cleaned.replace(/(?<=\S)\s*\([^)]*\)\s*$/, "");
7940
8683
  cleaned = cleaned.replace(/(?<=\S)\s+[-—:]\s+[a-z\s_-]+$/, "");
7941
8684
  cleaned = cleaned.replace(/(["'`])(?:\\.|[^\\])*?\1/g, " ");
@@ -8021,16 +8764,19 @@ function methodTypeRefs(m) {
8021
8764
  if (m.params && m.params.length > 0) {
8022
8765
  const refs = [];
8023
8766
  for (const p of m.params) {
8024
- refs.push(...extractTypeIdentifiers(p.type));
8767
+ refs.push(...writtenTypeRefs(p.type, "param"));
8025
8768
  }
8026
- refs.push(...extractTypeIdentifiers(m.returns ?? ""));
8769
+ if (m.returns !== void 0) refs.push(...writtenTypeRefs(m.returns, "returns"));
8027
8770
  return Array.from(new Set(refs));
8028
8771
  }
8029
8772
  return extractTypesFromSignature(m.signature ?? "", m.returns ?? "");
8030
8773
  }
8031
8774
  function signatureTypeRefs(type) {
8032
8775
  if (type.kind !== "signature") return [];
8033
- const refs = [...(type.params ?? []).flatMap((p) => extractTypeIdentifiers(p.type)), ...extractTypeIdentifiers(type.returns ?? "")];
8776
+ const refs = [
8777
+ ...(type.params ?? []).flatMap((p) => writtenTypeRefs(p.type, "signature-param")),
8778
+ ...type.returns !== void 0 ? writtenTypeRefs(type.returns, "signature-returns") : []
8779
+ ];
8034
8780
  return Array.from(new Set(refs));
8035
8781
  }
8036
8782
  function methodGenericParameters(method) {
@@ -8065,12 +8811,108 @@ function typeGenericParameters(type) {
8065
8811
  }
8066
8812
  function fieldTypeRefs(type, fieldType) {
8067
8813
  const generics = new Set(Array.from(typeGenericParameters(type)).map((g) => g.toLowerCase()));
8068
- return extractTypeIdentifiers(fieldType).filter((ref) => !generics.has(ref.toLowerCase()));
8814
+ return writtenTypeRefs(fieldType, "field").filter((ref) => !generics.has(ref.toLowerCase()));
8069
8815
  }
8070
8816
  function interfaceGenericParameters(intf) {
8071
8817
  return extractTypeGenerics(intf.name);
8072
8818
  }
8073
8819
 
8820
+ // src/models/type-dialects.ts
8821
+ function lastSegment(name) {
8822
+ return name.split(/::|\./).filter(Boolean).pop() ?? name;
8823
+ }
8824
+ function sameType(code, spec, codeNames) {
8825
+ if (code.form === "primitive" && code.name === "number") return spec.form === "primitive" && (spec.name === "int" || spec.name === "float");
8826
+ if (code.form === "named" && (spec.form === "named" || spec.form === "applied")) {
8827
+ if (spec.form === "applied") return false;
8828
+ return namesAgree(code.name, spec.name, codeNames);
8829
+ }
8830
+ if (code.form !== spec.form) return false;
8831
+ if (code.form === "primitive") return code.name === spec.name;
8832
+ if (code.form === "applied" && !namesAgree(code.name, spec.name, codeNames)) return false;
8833
+ if (code.args.length !== spec.args.length) return false;
8834
+ return code.args.every((arg, i) => sameType(arg, spec.args[i], codeNames));
8835
+ }
8836
+ function namesAgree(codeName, specName, codeNames) {
8837
+ if (codeName === specName) return true;
8838
+ const mapped = codeNames.get(specName) ?? codeNames.get(lastSegment(specName));
8839
+ return mapped === codeName || mapped === lastSegment(codeName);
8840
+ }
8841
+ function writeTypeScript(expr) {
8842
+ switch (expr.form) {
8843
+ case "primitive":
8844
+ return TS_PRIMITIVES[expr.name ?? ""] ?? expr.name ?? "unknown";
8845
+ case "named":
8846
+ return expr.name ?? "unknown";
8847
+ case "list": {
8848
+ const elem = writeTypeScript(expr.args[0]);
8849
+ return /[|\s]/.test(elem) ? `(${elem})[]` : `${elem}[]`;
8850
+ }
8851
+ case "set":
8852
+ return `Set<${writeTypeScript(expr.args[0])}>`;
8853
+ case "map":
8854
+ return `Record<${writeTypeScript(expr.args[0])}, ${writeTypeScript(expr.args[1])}>`;
8855
+ case "optional":
8856
+ return `${writeTypeScript(expr.args[0])} | null`;
8857
+ case "union":
8858
+ return expr.args.map(writeTypeScript).join(" | ");
8859
+ case "async":
8860
+ return `Promise<${writeTypeScript(expr.args[0])}>`;
8861
+ case "applied":
8862
+ return `${expr.name}<${expr.args.map(writeTypeScript).join(", ")}>`;
8863
+ }
8864
+ }
8865
+ var TS_PRIMITIVES = {
8866
+ string: "string",
8867
+ int: "number",
8868
+ float: "number",
8869
+ bool: "boolean",
8870
+ bytes: "Uint8Array",
8871
+ date: "string",
8872
+ datetime: "string",
8873
+ duration: "string",
8874
+ void: "void",
8875
+ any: "unknown"
8876
+ };
8877
+ var typescriptDialect = {
8878
+ language: "typescript",
8879
+ read(annotation) {
8880
+ return readCodeAnnotation(annotation, false);
8881
+ },
8882
+ agrees(annotation, expression, codeNames) {
8883
+ const code = readCodeAnnotation(annotation, true);
8884
+ return code !== null && sameType(code, expression, codeNames);
8885
+ },
8886
+ write(expression) {
8887
+ return writeTypeScript(expression);
8888
+ },
8889
+ mappingLines() {
8890
+ return [
8891
+ "string \u2192 string",
8892
+ "int, float \u2192 number",
8893
+ "bool \u2192 boolean",
8894
+ "bytes \u2192 Uint8Array",
8895
+ "date, datetime, duration \u2192 string (ISO 8601)",
8896
+ "any \u2192 unknown",
8897
+ "list<T> \u2192 T[]",
8898
+ "set<T> \u2192 Set<T>",
8899
+ "map<K, V> \u2192 Record<K, V>",
8900
+ "T? \u2192 T | null",
8901
+ "A | B \u2192 A | B",
8902
+ "async T \u2192 Promise<T>",
8903
+ "an enum E \u2192 type E = 'a' | 'b' (a string-literal union alias)",
8904
+ "a named scalar E holding P \u2192 type E = P (an alias of the primitive)"
8905
+ ];
8906
+ }
8907
+ };
8908
+ var DIALECTS = /* @__PURE__ */ new Map([
8909
+ ["typescript", typescriptDialect],
8910
+ ["javascript", typescriptDialect]
8911
+ ]);
8912
+ function typeDialectFor(language) {
8913
+ return DIALECTS.get(language.toLowerCase()) ?? null;
8914
+ }
8915
+
8074
8916
  // src/models/step-graph.ts
8075
8917
  function stepGraph(method) {
8076
8918
  const steps = method.narrative;
@@ -8365,6 +9207,36 @@ function callSitesOf(facts, fn) {
8365
9207
  function hasFunctionBody(facts, symbol) {
8366
9208
  return callSitesOf(facts, symbol) !== void 0;
8367
9209
  }
9210
+ var EXTENSION_LANGUAGE = {
9211
+ ".ts": "typescript",
9212
+ ".tsx": "typescript",
9213
+ ".mts": "typescript",
9214
+ ".cts": "typescript",
9215
+ ".js": "javascript",
9216
+ ".jsx": "javascript",
9217
+ ".mjs": "javascript",
9218
+ ".cjs": "javascript",
9219
+ ".py": "python",
9220
+ ".rs": "rust",
9221
+ ".go": "go",
9222
+ ".cs": "csharp",
9223
+ ".java": "java",
9224
+ ".c": "c",
9225
+ ".h": "c",
9226
+ ".cpp": "cpp",
9227
+ ".cc": "cpp",
9228
+ ".hpp": "cpp",
9229
+ ".rb": "ruby",
9230
+ ".php": "php",
9231
+ ".kt": "kotlin",
9232
+ ".swift": "swift"
9233
+ };
9234
+ function languageOfSourcePath(sourcePath) {
9235
+ return EXTENSION_LANGUAGE[path9.extname(sourcePath).toLowerCase()];
9236
+ }
9237
+ function dialectOf(facts) {
9238
+ return facts.language ? typeDialectFor(facts.language) : null;
9239
+ }
8368
9240
  function resolveImport(fromFile, specifier, knownPaths) {
8369
9241
  if (!specifier.startsWith(".")) return void 0;
8370
9242
  const joined = pathKey(path9.posix.normalize(path9.posix.join(path9.posix.dirname(fromFile), specifier)));
@@ -8410,11 +9282,18 @@ function closureTypeOf(snapshot, identifier) {
8410
9282
  if (matches.length === 1) return matches[0];
8411
9283
  return matches.find((def) => nameKey(def.id) === nameKey(ref));
8412
9284
  }
9285
+ function renamedRefs(expr, rename) {
9286
+ const args = expr.args.map((a) => renamedRefs(a, rename));
9287
+ return expr.form === "named" || expr.form === "applied" ? { ...expr, name: rename(expr.name), args } : { ...expr, args };
9288
+ }
8413
9289
  function canonicalTypeRef(snapshot, typeExpr) {
9290
+ const rename = (identifier) => closureTypeOf(snapshot, identifier)?.id ?? identifier;
9291
+ const parse = parseTypeExpression(typeExpr, "returns");
9292
+ if (parse.expression) return canonicalTypeText(renamedRefs(parse.expression, rename));
8414
9293
  return typeExpr.replace(TYPE_IDENTIFIER, (identifier) => {
8415
9294
  if (!/[A-Za-z]/.test(identifier)) return identifier;
8416
- if (BUILTIN_TYPES.has(identifier.toLowerCase())) return identifier.toLowerCase();
8417
- return closureTypeOf(snapshot, identifier)?.id ?? identifier;
9295
+ if (isTypeVocabulary(identifier)) return identifier.toLowerCase();
9296
+ return rename(identifier);
8418
9297
  });
8419
9298
  }
8420
9299
  function typeShape(snapshot, def) {
@@ -8426,7 +9305,9 @@ function typeShape(snapshot, def) {
8426
9305
  ...def.kind === "signature" ? {
8427
9306
  params: (def.params ?? []).map((p) => ({ type: canonicalTypeRef(snapshot, p.type), optional: p.optional === true })),
8428
9307
  returns: canonicalTypeRef(snapshot, def.returns ?? "unknown")
8429
- } : {}
9308
+ } : {},
9309
+ ...def.kind === "enum" ? { values: (def.values ?? []).map((v) => v.name) } : {},
9310
+ ...def.holds !== void 0 ? { holds: canonicalTypeRef(snapshot, def.holds) } : {}
8430
9311
  };
8431
9312
  }
8432
9313
  function typeDefExprs(def) {
@@ -8694,7 +9575,7 @@ function shortestPath(from, to, members, edges, rank) {
8694
9575
  // src/models/validation-options.ts
8695
9576
  function selectsFamily(options, declaresMembers) {
8696
9577
  if (options?.family) return true;
8697
- return declaresMembers && options?.recursive !== false;
9578
+ return declaresMembers && options?.memberDepth !== 0;
8698
9579
  }
8699
9580
 
8700
9581
  // src/core/narrative-labels.ts
@@ -9423,17 +10304,23 @@ function buildCanvasModel(issues = [], relations) {
9423
10304
  })),
9424
10305
  methods: t.methods.map((m) => ({ name: m.name, signature: m.signature, returns: m.returns, ...m.description ? { description: m.description } : {} })),
9425
10306
  ...t.kind === "signature" ? { signature: deriveTypeSignature(t) } : {},
10307
+ ...t.kind === "enum" ? { values: (t.values ?? []).map((v) => ({ name: v.name, ...v.description ? { description: v.description } : {} })) } : {},
10308
+ ...t.holds !== void 0 ? { holds: t.holds } : {},
9426
10309
  usedBy: usedByFor(t),
9427
10310
  ...t.componentClass ? { componentClass: t.componentClass } : {},
9428
10311
  ...t.database ? { database: t.database } : {},
9429
10312
  ...t.table ? { table: t.table } : {},
9430
10313
  ...t.linkedEntity ? { linkedEntity: t.linkedEntity } : {}
9431
10314
  }));
9432
- const MANY_SHAPE = /\[\s*\]|Array<|Vec<|Set<|List<|Map<|Record<|HashMap</i;
10315
+ const multiplicity = (field) => {
10316
+ const expression = parseTypeExpression(field.type, "field").expression;
10317
+ if (expression && typeIsMany(expression)) return "*";
10318
+ return field.optional || expression?.form === "optional" ? "0..1" : "1";
10319
+ };
9433
10320
  const typeEdges = [];
9434
10321
  for (const t of typeSpecs) {
9435
10322
  for (const field of t.fields) {
9436
- const refs = new Set(extractTypeIdentifiers(field.type));
10323
+ const refs = new Set(writtenTypeRefs(field.type, "field"));
9437
10324
  if (field.references) {
9438
10325
  const refStr = field.references;
9439
10326
  refs.add(refStr);
@@ -9448,8 +10335,8 @@ function buildCanvasModel(issues = [], relations) {
9448
10335
  const qualified = other.subsystem && !other.id.startsWith(`${other.subsystem}::`) ? `${other.subsystem}::${other.id}` : other.id;
9449
10336
  return matchTypeRef(ref, qualified);
9450
10337
  });
9451
- if (target && target.id !== t.id) {
9452
- const card = MANY_SHAPE.test(field.type) ? "*" : field.optional ? "0..1" : "1";
10338
+ if (target && target.id !== t.id && target.kind !== "enum" && target.holds === void 0) {
10339
+ const card = multiplicity(field);
9453
10340
  if (!typeEdges.some((e) => e.from === t.id && e.to === target.id && e.field === field.name)) {
9454
10341
  typeEdges.push({ from: t.id, to: target.id, field: field.name, card });
9455
10342
  }
@@ -11114,7 +12001,7 @@ var MODEL = __MODEL_JSON__;
11114
12001
  return '';
11115
12002
  }
11116
12003
  function visibleFields(t) {
11117
- if (det === 'names') return [];
12004
+ if (det === 'names' || t.values || t.holds) return [];
11118
12005
  if (det === 'keys') return t.fields.filter(function (f) { return markerOf(t, f) !== ''; });
11119
12006
  return t.fields;
11120
12007
  }
@@ -11159,15 +12046,19 @@ var MODEL = __MODEL_JSON__;
11159
12046
  var meths = det === 'full' ? t.methods : [];
11160
12047
  // A signature type is drawn with its derived text in place of a field list.
11161
12048
  var sig = t.signature && det !== 'names' ? t.signature : '';
11162
- var head = t.name + ' \\u00AB' + t.kind + '\\u00BB';
11163
- var rows = (sig ? [sig] : []).concat(fields.map(function (f) { return rowText(t, f); }))
12049
+ // An enum is drawn with its values in place of a field list.
12050
+ var vals = t.values && det !== 'names' ? t.values : [];
12051
+ // A named scalar is drawn compactly: its name and the primitive it holds.
12052
+ var head = t.name + (t.holds ? ' = ' + t.holds : '') + ' \\u00AB' + t.kind + '\\u00BB';
12053
+ var rows = (sig ? [sig] : []).concat(vals.map(function (v) { return '\\u2022 ' + v.name; }))
12054
+ .concat(fields.map(function (f) { return rowText(t, f); }))
11164
12055
  .concat(meths.map(function (m) { return '\\u0192 ' + m.name + '(): ' + m.returns; }));
11165
12056
  var longest = head.length + 4;
11166
12057
  rows.forEach(function (r) { if (r.length > longest) longest = r.length; });
11167
12058
  var plain = rows.length === 0;
11168
12059
  var pw = Math.max(170, head.length * 6.8 + 26);
11169
12060
  var W = Math.max(210, Math.min(400, longest * 6.6 + 30));
11170
- 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 };
12061
+ 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 };
11171
12062
  }
11172
12063
 
11173
12064
  // Emit one type table with its top-left at (ax, ay); returns its size.
@@ -11198,6 +12089,13 @@ var MODEL = __MODEL_JSON__;
11198
12089
  });
11199
12090
  ry += ROW_H;
11200
12091
  }
12092
+ sh.vals.forEach(function (v, vi) {
12093
+ eles.push({
12094
+ data: { id: 'TV~' + t.id + '~' + vi, parent: 'T~' + t.id, label: '\\u2022 ' + v.name, w: sh.w, h: ROW_H, tw: sh.w - 14 },
12095
+ position: { x: ax + sh.w / 2, y: ry + ROW_H / 2 }, classes: 'typeRow' + (dim ? ' dimmed' : ''), grabbable: false,
12096
+ });
12097
+ ry += ROW_H;
12098
+ });
11201
12099
  sh.fields.forEach(function (f) {
11202
12100
  var rid = 'TF~' + t.id + '~' + f.name;
11203
12101
  rowIds[rid] = 1;
@@ -13731,6 +14629,17 @@ var MODEL = __MODEL_JSON__;
13731
14629
  : '<span class="desc">no fields</span>';
13732
14630
  if (ty.signature) {
13733
14631
  body += section('Signature', 1, '<div class="method"><code>' + esc(ty.signature) + '</code></div>', true);
14632
+ } else if (ty.holds) {
14633
+ body += section('Holds', 1, '<div class="method"><code>' + esc(ty.holds) + '</code>'
14634
+ + '<div class="mdesc">A named scalar: one ' + esc(ty.holds) + ' under this name.</div></div>', true);
14635
+ } else if (ty.values) {
14636
+ body += section('Values', ty.values.length, ty.values.length
14637
+ ? ty.values.map(function (v) {
14638
+ return '<div class="method"><div class="mname">' + esc(v.name) + '</div>'
14639
+ + (v.description ? '<div class="mdesc">' + esc(v.description) + '</div>' : '')
14640
+ + '</div>';
14641
+ }).join('')
14642
+ : '<span class="desc">no values</span>', true);
13734
14643
  } else {
13735
14644
  body += section('Fields', ty.fields.length, fieldsInner, true);
13736
14645
  }
@@ -14056,7 +14965,7 @@ function computeTypeClosure(entries, types, exported = []) {
14056
14965
  queue.push(key);
14057
14966
  };
14058
14967
  const enqueueRef = (ref) => {
14059
- if (BUILTIN_TYPES.has(ref.toLowerCase())) return;
14968
+ if (isTypeVocabulary(ref)) return;
14060
14969
  for (const spec of types) {
14061
14970
  if (matchTypeRef(ref, qualifiedTypeId2(spec))) include(spec);
14062
14971
  }
@@ -14070,13 +14979,13 @@ function computeTypeClosure(entries, types, exported = []) {
14070
14979
  while (queue.length) {
14071
14980
  const spec = included.get(queue.shift());
14072
14981
  for (const field of spec.fields) {
14073
- for (const ref of extractTypeIdentifiers(field.type)) enqueueRef(ref);
14982
+ for (const ref of writtenTypeRefs(field.type, "field")) enqueueRef(ref);
14074
14983
  }
14075
14984
  for (const param of spec.params ?? []) {
14076
- for (const ref of extractTypeIdentifiers(param.type)) enqueueRef(ref);
14985
+ for (const ref of writtenTypeRefs(param.type, "signature-param")) enqueueRef(ref);
14077
14986
  }
14078
14987
  if (spec.returns) {
14079
- for (const ref of extractTypeIdentifiers(spec.returns)) enqueueRef(ref);
14988
+ for (const ref of writtenTypeRefs(spec.returns, "signature-returns")) enqueueRef(ref);
14080
14989
  }
14081
14990
  }
14082
14991
  const usedIds = /* @__PURE__ */ new Set();
@@ -14095,7 +15004,11 @@ function computeTypeClosure(entries, types, exported = []) {
14095
15004
  kind: t.kind,
14096
15005
  fields: t.fields.map(describe2),
14097
15006
  // A signature type travels complete: its params and returns with it.
14098
- ...t.kind === "signature" ? { params: (t.params ?? []).map(describe2), returns: t.returns ?? "unknown" } : {}
15007
+ ...t.kind === "signature" ? { params: (t.params ?? []).map(describe2), returns: t.returns ?? "any" } : {},
15008
+ // An enum travels with its values, in declared order.
15009
+ ...t.kind === "enum" ? { values: (t.values ?? []).map((v) => ({ name: v.name, ...v.description ? { description: v.description } : {} })) } : {},
15010
+ // A named scalar travels with the primitive it holds.
15011
+ ...t.holds !== void 0 ? { holds: t.holds } : {}
14099
15012
  };
14100
15013
  });
14101
15014
  }
@@ -14714,7 +15627,7 @@ function depthOf(family, node) {
14714
15627
  return depth;
14715
15628
  }
14716
15629
  function selectProjects(family, options) {
14717
- const limit = typeof options.recursive === "number" ? options.recursive : options.recursive === false ? 0 : Infinity;
15630
+ const limit = options.memberDepth ?? Infinity;
14718
15631
  const nodes = family.nodes.filter((n) => depthOf(family, n) <= limit);
14719
15632
  const scope = options.scopeSubsystem;
14720
15633
  if (!scope) return nodes.map((node) => ({ node }));
@@ -15242,7 +16155,7 @@ function defaultTargetConfig(type) {
15242
16155
  enabled: true
15243
16156
  };
15244
16157
  }
15245
- var WAIRON_VERSION = "5.1.1-dev.95";
16158
+ var WAIRON_VERSION = "5.1.1-dev.97";
15246
16159
  var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
15247
16160
  var ARCHITECT_AGENT_ID = "agent-architect";
15248
16161
  var ARCHITECT_TEMPLATE_ID = "architect";
@@ -15469,7 +16382,7 @@ var ProfileDefSchema = import_zod9.z.object({
15469
16382
  });
15470
16383
  var LanguagePackDefSchema = import_zod9.z.object({
15471
16384
  unsupportedFlow: import_zod9.z.record(import_zod9.z.string()).default({}),
15472
- foreignBuiltins: import_zod9.z.array(import_zod9.z.string()).default([])
16385
+ foreignBuiltins: import_zod9.z.array(import_zod9.z.string()).optional()
15473
16386
  });
15474
16387
  var PackSkillSchema = import_zod9.z.object({
15475
16388
  /** A NEW skill, installed namespaced as `<pack-id>-<id>`. Mutually exclusive with `extends`. */
@@ -15603,8 +16516,9 @@ function globalPacksEnabled(config) {
15603
16516
  return config.extensions?.useGlobalPacks ?? GLOBAL_PACKS_DEFAULT;
15604
16517
  }
15605
16518
  function emptyExtensions() {
15606
- return { packNames: [], packs: [], rules: [], profiles: {}, languages: {}, skills: [], patterns: [], guarantees: [], assertions: [], instructions: [], errors: [], selectionFailures: [] };
16519
+ return { packNames: [], packs: [], rules: [], profiles: {}, languages: {}, skills: [], patterns: [], guarantees: [], assertions: [], instructions: [], errors: [], selectionFailures: [], deprecations: [] };
15607
16520
  }
16521
+ var 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";
15608
16522
  function globalPacksDir() {
15609
16523
  return process.env.WAIRON_PACKS_DIR ?? path17.join(os3.homedir(), ".wairon", "packs");
15610
16524
  }
@@ -15665,11 +16579,11 @@ function mergePack(out, pack, ref, scope, packDir) {
15665
16579
  Object.assign(out.profiles, pack.profiles);
15666
16580
  for (const [lang, def] of Object.entries(pack.languages)) {
15667
16581
  const key = lang.toLowerCase();
16582
+ if (def.foreignBuiltins !== void 0) {
16583
+ out.deprecations.push(`${pack.name}: languages.${lang}.foreignBuiltins \u2014 ${FOREIGN_BUILTINS_RETIRED}`);
16584
+ }
15668
16585
  const existing = out.languages[key];
15669
- out.languages[key] = existing ? {
15670
- unsupportedFlow: { ...existing.unsupportedFlow, ...def.unsupportedFlow },
15671
- foreignBuiltins: [.../* @__PURE__ */ new Set([...existing.foreignBuiltins, ...def.foreignBuiltins])]
15672
- } : def;
16586
+ out.languages[key] = { unsupportedFlow: { ...existing?.unsupportedFlow, ...def.unsupportedFlow } };
15673
16587
  }
15674
16588
  for (const s of pack.skills) out.skills.push({ ...s, pack: pack.name, packVersion: pack.version, sourcePath: path17.resolve(packDir, s.source) });
15675
16589
  for (const p of pack.patterns) out.patterns.push({ ...p, pack: pack.name });
@@ -16217,7 +17131,7 @@ function buildImportGraph(index, universe) {
16217
17131
  function buildOwnershipIndex(ctx) {
16218
17132
  const ownedBy2 = /* @__PURE__ */ new Map();
16219
17133
  for (const comp of ctx.components) {
16220
- if (isRetired(comp) || !isPattern(comp)) continue;
17134
+ if (isRetired(comp) || !(isPattern(comp) || comp.componentType === "Supervisor")) continue;
16221
17135
  for (const memberId of comp.owns) {
16222
17136
  const member = ctx.componentMap.get(memberId);
16223
17137
  if (!member || isPattern(member)) continue;
@@ -16275,7 +17189,7 @@ function buildImplementationMethods(ctx) {
16275
17189
  const component = ctx.componentMap.get(contract.component);
16276
17190
  if (!component) continue;
16277
17191
  if (ctx.isInChainedSubproject(component.subsystem)) continue;
16278
- const draftContext3 = ctx.isImplementationDraft(impl);
17192
+ const draftContext4 = ctx.isImplementationDraft(impl);
16279
17193
  for (const method of impl.methods) {
16280
17194
  const sourceFile = methodSourceFile(method, impl.sourcePath);
16281
17195
  out.push({
@@ -16283,7 +17197,7 @@ function buildImplementationMethods(ctx) {
16283
17197
  method,
16284
17198
  component,
16285
17199
  ...sourceFile !== void 0 ? { sourceFile } : {},
16286
- draftContext: draftContext3
17200
+ draftContext: draftContext4
16287
17201
  });
16288
17202
  }
16289
17203
  }
@@ -16521,7 +17435,7 @@ var roundtripRule = {
16521
17435
  var typeDeclarationsRule = {
16522
17436
  name: "type-declarations",
16523
17437
  judges: "design",
16524
- 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.",
17438
+ 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.",
16525
17439
  codes: [
16526
17440
  { code: "INVALID_SUBSYSTEM_REFERENCE", defaultSeverity: "error", summary: "Type references a non-existent owning subsystem" },
16527
17441
  { code: "HOLLOW_TYPE", defaultSeverity: "warning", summary: "Type declares no fields and no methods \u2014 a placeholder that informs neither implementers nor the ERD" }
@@ -16539,7 +17453,8 @@ var typeDeclarationsRule = {
16539
17453
  isDraftCtx
16540
17454
  );
16541
17455
  }
16542
- if ((!t.fields || t.fields.length === 0) && (!t.methods || t.methods.length === 0)) {
17456
+ 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;
17457
+ if (!models) {
16543
17458
  ctx.addIssue(
16544
17459
  "warning",
16545
17460
  "HOLLOW_TYPE",
@@ -16776,6 +17691,7 @@ var signatureTypesRule = {
16776
17691
  );
16777
17692
  continue;
16778
17693
  }
17694
+ if (t.kind === "enum") continue;
16779
17695
  const stated = [...t.params !== void 0 ? ["params"] : [], ...t.returns !== void 0 ? ["returns"] : []];
16780
17696
  if (stated.length === 0) continue;
16781
17697
  ctx.addIssue(
@@ -16822,6 +17738,184 @@ var signatureTextRule = {
16822
17738
  }
16823
17739
  };
16824
17740
 
17741
+ // src/core/rules/integrity/type-expressions.ts
17742
+ function draftContext3(ctx, specId, kind) {
17743
+ if (kind === "interface") {
17744
+ const intf = ctx.interfaceMap.get(specId);
17745
+ return !!intf && (ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design");
17746
+ }
17747
+ const type = ctx.types.find((t) => t.id === specId);
17748
+ const sub = type?.subsystem ? ctx.subsystems.find((s) => s.id === type.subsystem) : void 0;
17749
+ return !!sub && (sub.status === "draft" || sub.status === "design");
17750
+ }
17751
+ function siteOf(path49) {
17752
+ const [head2, name] = path49.split(".");
17753
+ return (head2 === "methods" || head2 === "fields") && name ? name : path49;
17754
+ }
17755
+ function located(kind, specId, path49) {
17756
+ return `${path49 ? `"${path49}" of ` : ""}${kind ?? "spec"} "${specId}"`;
17757
+ }
17758
+ function problemMessage(problem, specId) {
17759
+ const where = located(problem.kind, specId, problem.path);
17760
+ const replacement = problem.replacement ? ` Write ${problem.replacement} instead.` : "";
17761
+ switch (problem.code) {
17762
+ case "TYPE_EXPRESSION_INVALID":
17763
+ 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.`;
17764
+ case "TYPE_POSITION_INVALID":
17765
+ return `The type at ${where} breaks a position rule: ${problem.detail}.${replacement}`;
17766
+ case "TYPE_FORM_UNSUPPORTED":
17767
+ 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.`;
17768
+ case "TYPE_NOT_NEUTRAL": {
17769
+ if (problem.replacement === "int or float") {
17770
+ 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.";
17771
+ 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.`;
17772
+ }
17773
+ return `The type at ${where} is "${problem.written}": ${problem.detail}, so it is read as any.${replacement}`;
17774
+ }
17775
+ }
17776
+ }
17777
+ var typeExpressionsRule = {
17778
+ name: "type-expressions",
17779
+ judges: "design",
17780
+ 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).",
17781
+ codes: [
17782
+ { code: "TYPE_EXPRESSION_INVALID", defaultSeverity: "error", summary: "A structured type position does not parse under the type grammar" },
17783
+ { 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??" },
17784
+ { 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" },
17785
+ { 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" },
17786
+ { 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" }
17787
+ ],
17788
+ check(ctx) {
17789
+ const facts = ctx.typeSpellingFacts;
17790
+ if (!facts) return;
17791
+ for (const problem of facts.problems) {
17792
+ if (problem.specId === void 0 || !ctx.isSpecInScope(problem.specId)) continue;
17793
+ const severity = problem.code === "TYPE_EXPRESSION_INVALID" || problem.code === "TYPE_POSITION_INVALID" ? "error" : "warning";
17794
+ ctx.addIssue(
17795
+ severity,
17796
+ problem.code,
17797
+ problemMessage(problem, problem.specId),
17798
+ problem.specId,
17799
+ draftContext3(ctx, problem.specId, problem.kind),
17800
+ void 0,
17801
+ problem.path ? { at: siteOf(problem.path) } : void 0
17802
+ );
17803
+ }
17804
+ for (const respelling of facts.respellings) {
17805
+ if (!ctx.isSpecInScope(respelling.specId)) continue;
17806
+ ctx.addIssue(
17807
+ "warning",
17808
+ "TYPE_SPELLING_STALE",
17809
+ `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.`,
17810
+ respelling.specId,
17811
+ draftContext3(ctx, respelling.specId, respelling.kind),
17812
+ void 0,
17813
+ { at: siteOf(respelling.path) }
17814
+ );
17815
+ }
17816
+ }
17817
+ };
17818
+
17819
+ // src/core/rules/integrity/enum-types.ts
17820
+ var enumTypesRule = {
17821
+ name: "enum-types",
17822
+ judges: "design",
17823
+ 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.",
17824
+ codes: [
17825
+ { 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" }
17826
+ ],
17827
+ check(ctx) {
17828
+ for (const t of ctx.types) {
17829
+ if (!ctx.isSpecInScope(t.id)) continue;
17830
+ const sub = t.subsystem ? ctx.subsystems.find((s) => s.id === t.subsystem) : void 0;
17831
+ const isDraft = !!sub && (sub.status === "draft" || sub.status === "design");
17832
+ if (t.kind === "enum") {
17833
+ const values = t.values ?? [];
17834
+ const problems = [];
17835
+ if (values.length === 0) problems.push("lists no values");
17836
+ const seen = /* @__PURE__ */ new Map();
17837
+ for (const value of values) {
17838
+ const key = nameKey(value.name);
17839
+ const earlier = seen.get(key);
17840
+ if (earlier !== void 0) problems.push(`lists "${earlier}" and "${value.name}", which every language derives one identifier from`);
17841
+ else seen.set(key, value.name);
17842
+ }
17843
+ const members = [
17844
+ ...t.fields.length > 0 ? ["fields"] : [],
17845
+ ...t.params !== void 0 ? ["params"] : [],
17846
+ ...t.returns !== void 0 ? ["returns"] : [],
17847
+ ...(t.invariants?.length ?? 0) > 0 ? ["invariants"] : [],
17848
+ ...t.componentClass !== void 0 ? ["componentClass"] : [],
17849
+ ...t.database !== void 0 ? ["database"] : [],
17850
+ ...t.table !== void 0 ? ["table"] : [],
17851
+ ...t.linkedEntity !== void 0 ? ["linkedEntity"] : []
17852
+ ];
17853
+ if (members.length > 0) problems.push(`carries ${members.join(", ")}`);
17854
+ if (problems.length === 0) continue;
17855
+ ctx.addIssue(
17856
+ "error",
17857
+ "ENUM_MEMBERS",
17858
+ `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.`,
17859
+ t.id,
17860
+ isDraft
17861
+ );
17862
+ continue;
17863
+ }
17864
+ if (t.values === void 0) continue;
17865
+ ctx.addIssue(
17866
+ "error",
17867
+ "ENUM_MEMBERS",
17868
+ `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.`,
17869
+ t.id,
17870
+ isDraft
17871
+ );
17872
+ }
17873
+ }
17874
+ };
17875
+
17876
+ // src/core/rules/integrity/named-scalar-types.ts
17877
+ var namedScalarTypesRule = {
17878
+ name: "named-scalar-types",
17879
+ judges: "design",
17880
+ 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.",
17881
+ codes: [
17882
+ { 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" }
17883
+ ],
17884
+ check(ctx) {
17885
+ for (const t of ctx.types) {
17886
+ if (!ctx.isSpecInScope(t.id)) continue;
17887
+ if (t.holds === void 0) continue;
17888
+ const sub = t.subsystem ? ctx.subsystems.find((s) => s.id === t.subsystem) : void 0;
17889
+ const isDraft = !!sub && (sub.status === "draft" || sub.status === "design");
17890
+ if (t.kind !== "value-object") {
17891
+ ctx.addIssue(
17892
+ "error",
17893
+ "NAMED_SCALAR_MEMBERS",
17894
+ `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.`,
17895
+ t.id,
17896
+ isDraft
17897
+ );
17898
+ continue;
17899
+ }
17900
+ const members = [
17901
+ ...t.fields.length > 0 ? ["fields"] : [],
17902
+ ...t.componentClass !== void 0 ? ["componentClass"] : [],
17903
+ ...t.database !== void 0 ? ["database"] : [],
17904
+ ...t.table !== void 0 ? ["table"] : [],
17905
+ ...t.linkedEntity !== void 0 ? ["linkedEntity"] : []
17906
+ ];
17907
+ if (members.length === 0) continue;
17908
+ ctx.addIssue(
17909
+ "error",
17910
+ "NAMED_SCALAR_MEMBERS",
17911
+ `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.`,
17912
+ t.id,
17913
+ isDraft
17914
+ );
17915
+ }
17916
+ }
17917
+ };
17918
+
16825
17919
  // src/core/rules/integrity/public-surface-binding.ts
16826
17920
  var publicSurfaceBindingRule = {
16827
17921
  name: "public-surface-binding",
@@ -17334,10 +18428,10 @@ var projectBoundariesRule = {
17334
18428
  var lintAllowsRule = {
17335
18429
  name: "lint-allows",
17336
18430
  judges: "design",
17337
- description: "Per-spec lint suppressions (lint.allow) must name real issue codes and actually suppress a finding \u2014 unknown codes and stale allows are flagged. An allow covers exactly the occurrence it names: a finding that reports a site is silenced only by an allow whose `at` is that site, a finding that reports none only by an allow that names none, and an aggregating finding only by an allow whose `covers` lists every unit it reports \u2014 a unit nobody listed is named back as new instead of inheriting a decision taken about its neighbours. So a coarse allow left on a rule that names sites, and an allow whose site the run no longer reports, are both UNUSED_LINT_ALLOW, and the finding names the sites that did fire. Allows silence warnings and notices; errors always surface.",
18431
+ description: "Per-spec lint suppressions (lint.allow) must name real issue codes and actually suppress a finding \u2014 unknown codes and stale allows are flagged. An allow covers exactly the occurrence it names: a finding that reports a site is silenced only by an allow whose `at` is that site, a finding that reports none only by an allow that names none, and an aggregating finding only by an allow whose `covers` lists every unit it reports \u2014 a unit nobody listed is named back as new instead of inheriting a decision taken about its neighbours. So a coarse allow left on a rule that names sites, and an allow whose site the run no longer reports, are both UNUSED_LINT_ALLOW, and the finding names the sites that did fire. Allows silence warnings and notices; errors always surface \u2014 so an allow naming a code that is an error on its spec (it fired there as one, or its resolved severity is error) covers nothing, and the finding says plainly that an error cannot be allowed rather than that the code never fired.",
17338
18432
  codes: [
17339
18433
  { code: "UNKNOWN_LINT_ALLOW_CODE", defaultSeverity: "warning", summary: "lint.allow names an issue code no registered rule emits" },
17340
- { code: "UNUSED_LINT_ALLOW", defaultSeverity: "warning", summary: "lint.allow entry matched no finding this run \u2014 the code never fired, or it fired at sites this allow does not name" }
18434
+ { code: "UNUSED_LINT_ALLOW", defaultSeverity: "warning", summary: "lint.allow entry covers nothing this run \u2014 the code never fired, it fired at sites this allow does not name, or it is an error, which no allow can cover" }
17341
18435
  ],
17342
18436
  check(ctx) {
17343
18437
  for (const a of ctx.lintAllows) {
@@ -17350,9 +18444,20 @@ var lintAllowsRule = {
17350
18444
  );
17351
18445
  continue;
17352
18446
  }
17353
- if (a.used) continue;
17354
18447
  const reported = ctx.sitesReported(a.specId, a.code);
17355
18448
  const at = a.at ? ` at "${a.at}"` : "";
18449
+ const fired = reported.unsited || reported.sites.length > 0;
18450
+ const isError = reported.errored || !fired && ctx.severityOf(a.code, a.specId) === "error";
18451
+ if (isError) {
18452
+ ctx.addIssue(
18453
+ "warning",
18454
+ "UNUSED_LINT_ALLOW",
18455
+ `Spec "${a.specId}" allows "${a.code}"${at} (reason: ${a.reason}), but "${a.code}" is an error${fired ? " and fired here as one" : ""}, and an error cannot be allowed \u2014 lint.allow silences warnings and notices only. Fix what the finding names, or remove the allow (a project may re-tune the code's severity in rules.sddRuleSeverity).`,
18456
+ a.specId
18457
+ );
18458
+ continue;
18459
+ }
18460
+ if (a.used) continue;
17356
18461
  let why;
17357
18462
  if (a.at && reported.unsited && reported.sites.length === 0) {
17358
18463
  why = `findings of "${a.code}" on this spec name no site at all, so this allow must not name one \u2014 drop the \`at\``;
@@ -18250,11 +19355,11 @@ var detailSufficiencyRule = {
18250
19355
  const contract = ctx.interfaceMap.get(impl.contract);
18251
19356
  if (!contract) continue;
18252
19357
  const component = ctx.componentMap.get(contract.component);
18253
- const draftContext3 = ctx.isImplementationDraft(impl);
19358
+ const draftContext4 = ctx.isImplementationDraft(impl);
18254
19359
  for (const method of impl.methods) {
18255
19360
  const detail = effectiveDetail(method, impl, component);
18256
19361
  if (contract.methods.some((m) => m.name === method.name) && method.narrative.length === 0 && detail.level !== "full") {
18257
- dialedDown.push({ implementation: impl, method, component, detail, draftContext: draftContext3 });
19362
+ dialedDown.push({ implementation: impl, method, component, detail, draftContext: draftContext4 });
18258
19363
  }
18259
19364
  }
18260
19365
  }
@@ -18875,18 +19980,47 @@ var dataBlockDepsRule = {
18875
19980
  };
18876
19981
 
18877
19982
  // src/core/rules/doctrine/entrypoint-dependencies.ts
19983
+ function maintainedRegistries(ctx) {
19984
+ const maintained = /* @__PURE__ */ new Map();
19985
+ const add2 = (supervisorId, registryId) => {
19986
+ let set = maintained.get(supervisorId);
19987
+ if (!set) maintained.set(supervisorId, set = /* @__PURE__ */ new Set());
19988
+ set.add(registryId);
19989
+ };
19990
+ for (const comp of ctx.components) {
19991
+ if (comp.componentType !== "Supervisor" || isRetired(comp)) continue;
19992
+ for (const memberId of comp.owns) {
19993
+ if (ctx.componentMap.get(memberId)?.componentType === "Registry") add2(comp.id, memberId);
19994
+ }
19995
+ }
19996
+ for (const impl of ctx.implementations) {
19997
+ const contract = ctx.interfaceMap.get(impl.contract);
19998
+ const supervisor = contract ? ctx.componentMap.get(contract.component) : void 0;
19999
+ if (!supervisor || supervisor.componentType !== "Supervisor" || isRetired(supervisor)) continue;
20000
+ for (const implMethod of impl.methods) {
20001
+ for (const step of implMethod.narrative) {
20002
+ if (step.type !== "call" || !step.targetComponent || !step.targetMethod) continue;
20003
+ if (ctx.componentMap.get(step.targetComponent)?.componentType !== "Registry") continue;
20004
+ const called = ctx.interfaceMethodsOf(step.targetComponent).find((m) => m.name === step.targetMethod);
20005
+ if (called?.effect === "lifecycle") add2(supervisor.id, step.targetComponent);
20006
+ }
20007
+ }
20008
+ }
20009
+ return new Map([...maintained].map(([id, set]) => [id, [...set]]));
20010
+ }
18878
20011
  var entrypointDepsRule = {
18879
20012
  name: "entrypoint-dependencies",
18880
20013
  judges: "design",
18881
- description: "Judges the edges at the system's entry points and its process layer. Portals and Observers are top-level entry points and subscribers, so nothing may depend on them \u2014 and that is the edge's one finding, which is why no other matrix rule judges it. Downward, a Portal dispatches to Orchestrators and may READ through Indexes and Repository facades but never reaches Store/Registry/Query or an Adapter, while an Observer forwards to one Orchestrator or Supervisor over a message-bus Adapter. A View stays a passive presenter. A Supervisor reaches data only through workflows, and a component depending on a live Actor must also depend on a Supervisor that supervises it.",
20014
+ description: "Judges the edges at the system's entry points and its process layer. Portals and Observers are top-level entry points and subscribers, so nothing may depend on them \u2014 and that is the edge's one finding, which is why no other matrix rule judges it. Downward, a Portal dispatches to Orchestrators and may READ through Indexes and Repository facades but never reaches Store/Registry/Query or an Adapter, while an Observer forwards to one Orchestrator or Supervisor over a message-bus Adapter. A View stays a passive presenter. A Supervisor stays out of presentation; what it may do to the data it reaches is judged per call (supervisor-shared-data), and what it owns as its supervision state by the ownership rules. A component depending on a live Actor reaches it through the Actor's supervision: by depending on a Supervisor that supervises it, or on a Registry such a Supervisor maintains (owns, or calls with lifecycle-effect methods) \u2014 the lookup hop callers really take.",
18882
20015
  codes: [
18883
20016
  { code: "ARCHITECTURE_VIOLATION_PORTAL_DEP", defaultSeverity: "error", summary: "Component depending on a Portal/Observer" },
18884
20017
  { code: "ARCHITECTURE_VIOLATION_PORTAL_FORBIDDEN_DEP", defaultSeverity: "error", summary: "Portal/Observer reaching the data layer directly" },
18885
20018
  { code: "ARCHITECTURE_VIOLATION_VIEW_DEP", defaultSeverity: "error", summary: "View depending on persistence layers or on logic that is not pure" },
18886
- { code: "ARCHITECTURE_VIOLATION_SUPERVISOR_DEP", defaultSeverity: "error", summary: "Supervisor depending on anything but Actors, Orchestrators, Adapters or other Supervisors \u2014 it reaches data only through workflows" },
18887
- { code: "ACTOR_REACHED_WITHOUT_SUPERVISOR", defaultSeverity: "error", summary: "Component depending on a live Actor it does not supervise, without also depending on a Supervisor that supervises it \u2014 a live Actor is reached by id through its Supervisor" }
20019
+ { code: "ARCHITECTURE_VIOLATION_SUPERVISOR_DEP", defaultSeverity: "error", summary: "Supervisor depending on a presentation block (View, FeatureComponent, RouterComponent) \u2014 a Supervisor manages live processes; the data it reaches is judged per call by supervisor-shared-data" },
20020
+ { code: "ACTOR_REACHED_WITHOUT_SUPERVISOR", defaultSeverity: "error", summary: "Component depending on a live Actor it does not supervise, reaching it neither through a Supervisor that supervises it nor through a Registry such a Supervisor maintains \u2014 model the real lookup hop" }
18888
20021
  ],
18889
20022
  check(ctx) {
20023
+ const lookups = maintainedRegistries(ctx);
18890
20024
  for (const edge of ctx.dependencyEdges().matrix) {
18891
20025
  const comp = edge.from;
18892
20026
  const depComp = edge.to;
@@ -18922,27 +20056,32 @@ var entrypointDepsRule = {
18922
20056
  edge.draftContext
18923
20057
  );
18924
20058
  }
18925
- if (comp.componentType === "Supervisor" && ["Store", "Registry", "Repository", "Index", "Query", "View", "FeatureComponent", "RouterComponent"].includes(depComp.componentType)) {
20059
+ if (comp.componentType === "Supervisor" && ["View", "FeatureComponent", "RouterComponent"].includes(depComp.componentType)) {
18926
20060
  ctx.addIssue(
18927
20061
  "error",
18928
20062
  "ARCHITECTURE_VIOLATION_SUPERVISOR_DEP",
18929
- `Architectural violation: Supervisor "${comp.id}" cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". A Supervisor reaches data only through workflows \u2014 depend on the Orchestrator, Actor or Adapter that does that work instead.` + storeHint,
20063
+ `Architectural violation: Supervisor "${comp.id}" cannot depend on ${stereotypeOf(depComp)} "${depComp.id}". A Supervisor manages live processes and stays out of presentation \u2014 the UI reaches the system through its Portals and workflows, never through a Supervisor's dependencies.`,
18930
20064
  comp.id,
18931
20065
  edge.draftContext
18932
20066
  );
18933
20067
  }
18934
20068
  if (depComp.componentType === "Actor" && comp.componentType !== "Supervisor") {
18935
- const reachedThroughSupervisor = comp.dependsOn.some((id) => {
18936
- const supervisor = ctx.componentMap.get(id);
18937
- return supervisor?.componentType === "Supervisor" && supervisor.dependsOn.includes(depComp.id);
18938
- });
18939
- if (!reachedThroughSupervisor) {
18940
- const supervisors = ctx.components.filter((c) => c.componentType === "Supervisor" && c.dependsOn.includes(depComp.id)).map((c) => `"${c.id}"`);
18941
- const remedy = supervisors.length > 0 ? `also depend on ${supervisors.length === 1 ? "its Supervisor" : "one of its Supervisors"} (${supervisors.join(", ")})` : `no Supervisor depends on "${depComp.id}" yet, so give the Actor a Supervisor that depends on it and depend on that Supervisor`;
20069
+ const supervisors = ctx.components.filter((c) => c.componentType === "Supervisor" && c.dependsOn.includes(depComp.id));
20070
+ const registries = supervisors.flatMap((s) => (lookups.get(s.id) ?? []).map((registry) => ({ registry, supervisor: s.id })));
20071
+ const reached = supervisors.some((s) => comp.dependsOn.includes(s.id)) || registries.some((r) => comp.dependsOn.includes(r.registry));
20072
+ if (!reached) {
20073
+ let remedy;
20074
+ if (registries.length > 0) {
20075
+ remedy = `depend on ${registries.length === 1 ? "the Registry" : "one of the Registries"} its Supervisor maintains (${registries.map((r) => `"${r.registry}", kept by "${r.supervisor}"`).join("; ")}) and look the Actor up by id there`;
20076
+ } else if (supervisors.length > 0) {
20077
+ remedy = `${supervisors.length === 1 ? "its Supervisor" : "its Supervisors"} (${supervisors.map((s) => `"${s.id}"`).join(", ")}) maintain no Registry of its live handles yet: model that lookup as a Registry the Supervisor keeps through lifecycle-effect methods (registering and unregistering a live handle), and depend on that Registry`;
20078
+ } else {
20079
+ remedy = `no Supervisor depends on "${depComp.id}" yet: give it a Supervisor that depends on it and maintains a Registry of its live handles through lifecycle-effect methods, and depend on that Registry`;
20080
+ }
18942
20081
  ctx.addIssue(
18943
20082
  "error",
18944
20083
  "ACTOR_REACHED_WITHOUT_SUPERVISOR",
18945
- `Architectural violation: ${stereotypeOf(comp)} "${comp.id}" depends on the live Actor "${depComp.id}" without a Supervisor that supervises it. A live Actor is reached by id through a Supervisor that supervises it \u2014 ${remedy}.`,
20084
+ `Architectural violation: ${stereotypeOf(comp)} "${comp.id}" depends on the live Actor "${depComp.id}" without reaching it through its supervision. A live Actor is found by id through a Registry its Supervisor maintains, or messaged through that Supervisor \u2014 ${remedy}.`,
18946
20085
  comp.id,
18947
20086
  edge.draftContext
18948
20087
  );
@@ -18956,9 +20095,9 @@ var entrypointDepsRule = {
18956
20095
  var portalWriteShortcutRule = {
18957
20096
  name: "portal-write-shortcut",
18958
20097
  judges: "design",
18959
- description: "A Portal narrative call step or dispatch-table binding that reaches a write-effect method on a Repository or Index directly is the write shortcut: the Portal\u2192data-facade edge is licensed for reads only, and a write routes through an Orchestrator that owns the workflow. A dispatch step reaches its server only through a table binding, so judging every binding judges each dispatch step that takes it, once, where the route is declared. Methods that carry no effect tag are not judged.",
20098
+ description: "A Portal narrative call step or dispatch-table binding that reaches a write- or lifecycle-effect method on a Repository or Index directly is the write shortcut: the Portal\u2192data-facade edge is licensed for reads only, and a mutation \u2014 a write, or a lifecycle change to what exists \u2014 routes through an Orchestrator that owns the workflow. A dispatch step reaches its server only through a table binding, so judging every binding judges each dispatch step that takes it, once, where the route is declared. Methods that carry no effect tag are not judged.",
18960
20099
  codes: [
18961
- { code: "PORTAL_WRITE_SHORTCUT", defaultSeverity: "error", summary: "Portal narrative call or dispatch-table binding reaches a write-effect method on a Repository/Index directly \u2014 reads may shortcut, writes route through an Orchestrator (judged on effect-tagged facade methods; untagged methods are not yet judged)" }
20100
+ { code: "PORTAL_WRITE_SHORTCUT", defaultSeverity: "error", summary: "Portal narrative call or dispatch-table binding reaches a write- or lifecycle-effect method on a Repository/Index directly \u2014 reads may shortcut, mutations route through an Orchestrator (judged on effect-tagged facade methods; untagged methods are not yet judged)" }
18962
20101
  ],
18963
20102
  check(ctx) {
18964
20103
  for (const impl of ctx.implementations) {
@@ -18972,11 +20111,11 @@ var portalWriteShortcutRule = {
18972
20111
  const target = ctx.componentMap.get(step.targetComponent);
18973
20112
  if (!target || target.componentType !== "Repository" && target.componentType !== "Index") continue;
18974
20113
  const targetMethod = ctx.interfaceMethodsOf(target.id).find((m) => m.name === step.targetMethod);
18975
- if (targetMethod?.effect !== "write") continue;
20114
+ if (targetMethod?.effect !== "write" && targetMethod?.effect !== "lifecycle") continue;
18976
20115
  ctx.addIssue(
18977
20116
  "error",
18978
20117
  "PORTAL_WRITE_SHORTCUT",
18979
- `Portal "${component.id}": step ${step.stepNumber} of "${implMethod.name}" calls write-effect method ${target.id}.${step.targetMethod} directly. The Portal\u2192${target.componentType} shortcut is licensed for READS only \u2014 route the write through an Orchestrator that owns the workflow.`,
20118
+ `Portal "${component.id}": step ${step.stepNumber} of "${implMethod.name}" calls ${targetMethod.effect}-effect method ${target.id}.${step.targetMethod} directly. The Portal\u2192${target.componentType} shortcut is licensed for READS only \u2014 route the ${targetMethod.effect === "write" ? "write" : "lifecycle change"} through an Orchestrator that owns the workflow.`,
18980
20119
  impl.id,
18981
20120
  isDraftCtx || ctx.isComponentDraft(target.id)
18982
20121
  );
@@ -18990,11 +20129,11 @@ var portalWriteShortcutRule = {
18990
20129
  const target = ctx.componentMap.get(binding.component);
18991
20130
  if (!target || target.componentType !== "Repository" && target.componentType !== "Index") continue;
18992
20131
  const targetMethod = ctx.interfaceMethodsOf(target.id).find((m) => m.name === binding.method);
18993
- if (targetMethod?.effect !== "write") continue;
20132
+ if (targetMethod?.effect !== "write" && targetMethod?.effect !== "lifecycle") continue;
18994
20133
  ctx.addIssue(
18995
20134
  "error",
18996
20135
  "PORTAL_WRITE_SHORTCUT",
18997
- `Portal "${comp.id}": dispatch binding "${binding.capability}" routes to write-effect method ${target.id}.${binding.method} directly. The Portal\u2192${target.componentType} shortcut is licensed for READS only \u2014 route the write through an Orchestrator that owns the workflow.`,
20136
+ `Portal "${comp.id}": dispatch binding "${binding.capability}" routes to ${targetMethod.effect}-effect method ${target.id}.${binding.method} directly. The Portal\u2192${target.componentType} shortcut is licensed for READS only \u2014 route the ${targetMethod.effect === "write" ? "write" : "lifecycle change"} through an Orchestrator that owns the workflow.`,
18998
20137
  comp.id,
18999
20138
  isDraftCtx || ctx.isComponentDraft(target.id)
19000
20139
  );
@@ -19003,28 +20142,101 @@ var portalWriteShortcutRule = {
19003
20142
  }
19004
20143
  };
19005
20144
 
20145
+ // src/core/rules/doctrine/supervisor-shared-data.ts
20146
+ var DATA_COMPONENTS = /* @__PURE__ */ new Set(["Store", "Registry", "Repository", "Index", "Query"]);
20147
+ var supervisorSharedDataRule = {
20148
+ name: "supervisor-shared-data",
20149
+ judges: "design",
20150
+ description: "A Supervisor keeps its own supervision state (the Stores and Registries it owns) with full read and write, but data it does not own is shared: a Supervisor narrative call step that reaches a method of a Store, Registry, Repository, Index or Query the Supervisor does not own is legal only when that method declares effect read or lifecycle. A write \u2014 or a method that declares no effect \u2014 goes through a workflow: the Supervisor depends on the Orchestrator that does it. A read on behalf of a request still passes; in practice the paired write is what forces the workflow out, and the read moves with it.",
20151
+ codes: [
20152
+ { code: "SUPERVISOR_WRITE_SHORTCUT", defaultSeverity: "error", summary: "Supervisor narrative call reaches a method of a data component it does not own whose declared effect is neither read nor lifecycle \u2014 writes to shared data route through an Orchestrator" }
20153
+ ],
20154
+ check(ctx) {
20155
+ const ownership = ctx.ownershipIndex();
20156
+ for (const impl of ctx.implementations) {
20157
+ const contract = ctx.interfaceMap.get(impl.contract);
20158
+ const supervisor = contract ? ctx.componentMap.get(contract.component) : void 0;
20159
+ if (!supervisor || supervisor.componentType !== "Supervisor" || isRetired(supervisor)) continue;
20160
+ const isDraftCtx = ctx.isImplementationDraft(impl);
20161
+ for (const implMethod of impl.methods) {
20162
+ for (const step of implMethod.narrative) {
20163
+ if (step.type !== "call" || !step.targetComponent || !step.targetMethod) continue;
20164
+ const target = ctx.componentMap.get(step.targetComponent);
20165
+ if (!target || !DATA_COMPONENTS.has(target.componentType)) continue;
20166
+ if (ownership.ownerOf(target.id) === supervisor.id) continue;
20167
+ const called = ctx.interfaceMethodsOf(target.id).find((m) => m.name === step.targetMethod);
20168
+ if (!called || called.effect === "read" || called.effect === "lifecycle") continue;
20169
+ const what = called.effect === "write" ? "a write-effect method" : "a method that declares no effect";
20170
+ const untaggedHint = called.effect === "write" ? "" : ` If ${target.id}.${called.name} only reads, or only changes what exists, declare its effect (read or lifecycle).`;
20171
+ ctx.addIssue(
20172
+ "error",
20173
+ "SUPERVISOR_WRITE_SHORTCUT",
20174
+ `Supervisor "${supervisor.id}": step ${step.stepNumber} of "${implMethod.name}" calls ${target.id}.${called.name}, ${what} on ${target.componentType} "${target.id}", which the Supervisor does not own. A Supervisor reads shared data and changes what exists in it (read and lifecycle effects); a write to its fields goes through a workflow \u2014 depend on the Orchestrator that does it and call that instead.${untaggedHint}`,
20175
+ impl.id,
20176
+ isDraftCtx || ctx.isComponentDraft(target.id)
20177
+ );
20178
+ }
20179
+ }
20180
+ }
20181
+ }
20182
+ };
20183
+
20184
+ // src/core/rules/doctrine/lifecycle-effect-closure.ts
20185
+ var lifecycleEffectClosureRule = {
20186
+ name: "lifecycle-effect-closure",
20187
+ judges: "design",
20188
+ description: "A lifecycle-effect method may create, destroy, or (un)register an entity's existence or membership, never modify its domain fields \u2014 and the effect is closed under composition: its narrative may call only read- and lifecycle-effect methods besides its construction and local steps. A call step to a write-effect method from a lifecycle-effect method is a declaration error: the method is a write, or the write belongs to a workflow beside it. Called methods that declare no effect are not judged.",
20189
+ codes: [
20190
+ { code: "LIFECYCLE_CALLS_WRITE", defaultSeverity: "error", summary: "Lifecycle-effect method whose narrative calls a write-effect method \u2014 the lifecycle effect is closed under composition" }
20191
+ ],
20192
+ check(ctx) {
20193
+ for (const impl of ctx.implementations) {
20194
+ const contract = ctx.interfaceMap.get(impl.contract);
20195
+ if (!contract) continue;
20196
+ const declared = ctx.interfaceMethodsOf(contract.component);
20197
+ const isDraftCtx = ctx.isImplementationDraft(impl);
20198
+ for (const implMethod of impl.methods) {
20199
+ if (declared.find((m) => m.name === implMethod.name)?.effect !== "lifecycle") continue;
20200
+ for (const step of implMethod.narrative) {
20201
+ if (step.type !== "call" || !step.targetComponent || !step.targetMethod) continue;
20202
+ const called = ctx.interfaceMethodsOf(step.targetComponent).find((m) => m.name === step.targetMethod);
20203
+ if (called?.effect !== "write") continue;
20204
+ ctx.addIssue(
20205
+ "error",
20206
+ "LIFECYCLE_CALLS_WRITE",
20207
+ `"${contract.component}.${implMethod.name}" declares effect lifecycle, but step ${step.stepNumber} calls write-effect method ${step.targetComponent}.${step.targetMethod}. A lifecycle method creates, destroys or (un)registers what exists and never modifies domain fields, and the effect is closed under composition \u2014 it may call only read and lifecycle methods. Declare the method a write, or move the write to a workflow beside it.`,
20208
+ impl.id,
20209
+ isDraftCtx
20210
+ );
20211
+ }
20212
+ }
20213
+ }
20214
+ }
20215
+ };
20216
+
19006
20217
  // src/core/rules/doctrine/pattern-membership.ts
19007
20218
  var patternMembershipRule = {
19008
20219
  name: "pattern-membership",
19009
20220
  judges: "design",
19010
- description: "Only patterns (Repository/FeatureComponent/RouterComponent) own member blocks, and every pattern owns at least one. Each claim must name a component that exists and is itself a building block \u2014 patterns compose at the subsystem (L1) level, never by owning one another \u2014 and a block has exactly one owner, the first pattern to claim it.",
20221
+ description: "Only patterns (Repository/FeatureComponent/RouterComponent) own member blocks, and every pattern owns at least one. The one building block that may own is a Supervisor, which owns its supervision state and may own nothing at all; what it may own is pattern-containment's question. Each claim must name a component that exists and is itself a building block \u2014 patterns compose at the subsystem (L1) level, never by owning one another \u2014 and a block has exactly one owner, the first pattern or Supervisor to claim it.",
19011
20222
  codes: [
19012
20223
  { code: "EMPTY_PATTERN", defaultSeverity: "error", summary: "Pattern with no owned member blocks" },
19013
- { code: "BLOCK_OWNS_MEMBERS", defaultSeverity: "error", summary: "Building block using owns" },
20224
+ { code: "BLOCK_OWNS_MEMBERS", defaultSeverity: "error", summary: "Building block other than a Supervisor using owns" },
19014
20225
  { code: "INVALID_OWNED_MEMBER", defaultSeverity: "error", summary: "owns names a non-existent component" },
19015
20226
  { code: "PATTERN_OWNS_PATTERN", defaultSeverity: "error", summary: "Pattern owning another pattern" },
19016
- { code: "SHARED_OWNED_MEMBER", defaultSeverity: "error", summary: "Block owned by two patterns" }
20227
+ { code: "SHARED_OWNED_MEMBER", defaultSeverity: "error", summary: "Block owned by two owners (patterns or Supervisors)" }
19017
20228
  ],
19018
20229
  check(ctx) {
19019
20230
  for (const comp of ctx.components) {
19020
20231
  if (isRetired(comp)) continue;
19021
20232
  const isDraftCtx = ctx.isComponentDraft(comp.id);
19022
20233
  const pattern = isPattern(comp);
20234
+ const supervisor = comp.componentType === "Supervisor";
19023
20235
  if (pattern && comp.owns.length === 0) {
19024
20236
  ctx.addIssue("error", "EMPTY_PATTERN", `Pattern "${comp.id}" (${comp.componentType}) must own member blocks via "owns".`, comp.id, isDraftCtx);
19025
20237
  }
19026
- if (!pattern && comp.owns.length > 0) {
19027
- ctx.addIssue("error", "BLOCK_OWNS_MEMBERS", `Building block "${comp.id}" (${comp.componentType}) cannot own members; only patterns (${Array.from(PATTERN_TYPES).join("/")}) use "owns".`, comp.id, isDraftCtx);
20238
+ if (!pattern && !supervisor && comp.owns.length > 0) {
20239
+ ctx.addIssue("error", "BLOCK_OWNS_MEMBERS", `Building block "${comp.id}" (${comp.componentType}) cannot own members; only patterns (${Array.from(PATTERN_TYPES).join("/")}) use "owns", and a Supervisor for its supervision state.`, comp.id, isDraftCtx);
19028
20240
  }
19029
20241
  }
19030
20242
  const ownership = ctx.ownershipIndex();
@@ -19032,15 +20244,18 @@ var patternMembershipRule = {
19032
20244
  if (isRetired(comp)) continue;
19033
20245
  const isDraftCtx = ctx.isComponentDraft(comp.id);
19034
20246
  const pattern = isPattern(comp);
20247
+ const supervisor = comp.componentType === "Supervisor";
19035
20248
  for (const memberId of comp.owns) {
19036
20249
  const member = ctx.componentMap.get(memberId);
19037
20250
  if (!member) {
19038
20251
  ctx.addIssue("error", "INVALID_OWNED_MEMBER", `Component "${comp.id}" owns "${memberId}" which does not exist.`, comp.id, isDraftCtx);
19039
20252
  continue;
19040
20253
  }
19041
- if (!pattern) continue;
20254
+ if (!pattern && !supervisor) continue;
19042
20255
  if (isPattern(member)) {
19043
- ctx.addIssue("error", "PATTERN_OWNS_PATTERN", `Pattern "${comp.id}" owns "${memberId}", which is itself a pattern. Patterns own only building blocks \u2014 compose patterns at the subsystem (L1) level.`, comp.id, isDraftCtx);
20256
+ if (pattern) {
20257
+ ctx.addIssue("error", "PATTERN_OWNS_PATTERN", `Pattern "${comp.id}" owns "${memberId}", which is itself a pattern. Patterns own only building blocks \u2014 compose patterns at the subsystem (L1) level.`, comp.id, isDraftCtx);
20258
+ }
19044
20259
  continue;
19045
20260
  }
19046
20261
  const firstOwner = ownership.ownerOf(memberId);
@@ -19054,13 +20269,15 @@ var patternMembershipRule = {
19054
20269
 
19055
20270
  // src/core/rules/doctrine/pattern-containment.ts
19056
20271
  var REPOSITORY_MEMBERS = /* @__PURE__ */ new Set(["Store", "Registry", "Index", "Query", "Adapter"]);
20272
+ var SUPERVISION_STATE = /* @__PURE__ */ new Set(["Store", "Registry"]);
19057
20273
  var patternContainmentRule = {
19058
20274
  name: "pattern-containment",
19059
20275
  judges: "design",
19060
- description: "Holds each pattern to the containment its definition prescribes. A Repository may own only Store, Registry, Index, Query and (optionally) Adapter, judged member by member. A FeatureComponent owns exactly one Orchestrator (the logic side) and one or more Views (its faces \u2014 list, detail, form \u2014 sharing the one logic component), and nothing else. A RouterComponent owns exactly one Portal as its facade and at least one other child to route to. A counting pattern that still owns a retired member is not judged until that member is migrated: its counts change with the migration, and STEREOTYPE_RETIRED is the one finding.",
20276
+ description: "Holds each owner to the containment its definition prescribes. A Repository may own only Store, Registry, Index, Query and (optionally) Adapter, judged member by member. A Supervisor may own only its supervision state \u2014 Stores and Registries that are its own, one hop \u2014 judged member by member too. A FeatureComponent owns exactly one Orchestrator (the logic side) and one or more Views (its faces \u2014 list, detail, form \u2014 sharing the one logic component), and nothing else. A RouterComponent owns exactly one Portal as its facade and at least one other child to route to. A counting pattern that still owns a retired member is not judged until that member is migrated: its counts change with the migration, and STEREOTYPE_RETIRED is the one finding.",
19061
20277
  codes: [
19062
20278
  { code: "REPOSITORY_CONTAINMENT", defaultSeverity: "error", summary: "Repository owning a non Store/Registry/Index/Query/Adapter member" },
19063
20279
  { code: "FEATURE_COMPONENT_CONTAINMENT", defaultSeverity: "error", summary: "FeatureComponent not owning exactly one Orchestrator + one or more Views" },
20280
+ { code: "SUPERVISOR_CONTAINMENT", defaultSeverity: "error", summary: "Supervisor owning anything but a Store or Registry \u2014 a Supervisor owns only its supervision state" },
19064
20281
  { code: "ROUTER_COMPONENT_CONTAINMENT", defaultSeverity: "error", summary: "RouterComponent not owning exactly one Portal facade, or owning no children to route to" }
19065
20282
  ],
19066
20283
  check(ctx) {
@@ -19111,6 +20328,15 @@ var patternContainmentRule = {
19111
20328
  }
19112
20329
  }
19113
20330
  }
20331
+ for (const comp of ctx.components) {
20332
+ if (comp.componentType !== "Supervisor" || isRetired(comp)) continue;
20333
+ const isDraftCtx = ctx.isComponentDraft(comp.id);
20334
+ for (const memberId of comp.owns) {
20335
+ const member = ctx.componentMap.get(memberId);
20336
+ if (!member || isRetired(member) || SUPERVISION_STATE.has(member.componentType)) continue;
20337
+ ctx.addIssue("error", "SUPERVISOR_CONTAINMENT", `Supervisor "${comp.id}" owns "${memberId}" of type ${member.componentType}; a Supervisor owns only its supervision state \u2014 Stores and Registries that are its own. Depend on "${memberId}" as a collaborator instead.`, comp.id, isDraftCtx);
20338
+ }
20339
+ }
19114
20340
  }
19115
20341
  };
19116
20342
 
@@ -19118,7 +20344,7 @@ var patternContainmentRule = {
19118
20344
  var unownedBlocksRule = {
19119
20345
  name: "unowned-blocks",
19120
20346
  judges: "design",
19121
- description: `Judges the data blocks no pattern owns. A Store may stand alone deliberately \u2014 the sanctioned lightweight form for genuinely simple state, acknowledged with a lint.allow \u2014 while the recommended shape stays a Repository; what must never happen is folding the state into a consuming component, where no spec, diagram or conformance check can see it again. A Registry standing alone with no Store to write to is either mistyped (the "file-backed Registry" idiom, a fused persistent store that belongs typed Store) or orphaned. A Query has no standalone form at all: it computes reads over its own Repository's Store.`,
20347
+ description: `Judges the data blocks no owner (a pattern, or a Supervisor keeping its supervision state) owns. A Store may stand alone deliberately \u2014 the sanctioned lightweight form for genuinely simple state, acknowledged with a lint.allow \u2014 while the recommended shape stays a Repository; what must never happen is folding the state into a consuming component, where no spec, diagram or conformance check can see it again. A Registry standing alone with no Store to write to is either mistyped (the "file-backed Registry" idiom, a fused persistent store that belongs typed Store) or orphaned. A Query has no standalone form at all: it computes reads over its own Repository's Store.`,
19122
20348
  codes: [
19123
20349
  { code: "UNOWNED_STORE", defaultSeverity: "warning", summary: "Store not owned by any pattern \u2014 recommended shape is a Repository; a deliberate standalone Store needs a lint.allow" },
19124
20350
  { code: "REGISTRY_WITHOUT_STORE", defaultSeverity: "warning", summary: "Standalone Registry with no Store to write to \u2014 either mistyped (a fused file-backed store belongs typed Store) or orphaned" },
@@ -19164,9 +20390,10 @@ var unownedBlocksRule = {
19164
20390
  var memberVisibilityRule = {
19165
20391
  name: "member-visibility",
19166
20392
  judges: "design",
19167
- description: "A component may depend on a block within its own group (it is the owning pattern, or a sibling member of the same pattern), on any pattern facade, or on a standalone block \u2014 never on a block privately owned by ANOTHER pattern, which must be reached through that pattern's facade. A retired depending component is skipped, as everywhere in this family.",
20393
+ description: "A component may depend on a block within its own group (it is the owner, or a sibling member of the same owner), on any pattern facade, or on a standalone block \u2014 never on a block privately owned by ANOTHER pattern, which must be reached through that pattern's facade. A Store or Registry a Supervisor owns is that Supervisor's supervision state and nobody else depends on it: a component that does is the intruder, and the finding is reported on it, naming the Supervisor and the state. A retired depending component is skipped, as everywhere in this family.",
19168
20394
  codes: [
19169
- { code: "VISIBILITY_VIOLATION", defaultSeverity: "error", summary: "Dependency on a block privately owned by another pattern" }
20395
+ { code: "VISIBILITY_VIOLATION", defaultSeverity: "error", summary: "Dependency on a block privately owned by another pattern" },
20396
+ { code: "SUPERVISION_STATE_INTRUSION", defaultSeverity: "error", summary: "Dependency on a Store or Registry a Supervisor owns as its supervision state \u2014 reported on the intruder, naming the Supervisor and the state" }
19170
20397
  ],
19171
20398
  check(ctx) {
19172
20399
  const ownership = ctx.ownershipIndex();
@@ -19177,7 +20404,12 @@ var memberVisibilityRule = {
19177
20404
  if (!owner) continue;
19178
20405
  if (owner === comp.id) continue;
19179
20406
  if (ownership.ownerOf(comp.id) === owner) continue;
19180
- ctx.addIssue("error", "VISIBILITY_VIOLATION", `Component "${comp.id}" depends on "${depId}", which is privately owned by pattern "${owner}". Depend on the facade "${owner}" instead.`, comp.id, ctx.isComponentDraft(comp.id) || ctx.isComponentDraft(depId));
20407
+ const isDraftCtx = ctx.isComponentDraft(comp.id) || ctx.isComponentDraft(depId);
20408
+ if (ctx.componentMap.get(owner)?.componentType === "Supervisor") {
20409
+ ctx.addIssue("error", "SUPERVISION_STATE_INTRUSION", `"${comp.id}" depends on "${depId}", which Supervisor "${owner}" owns as its supervision state. Supervision state is private to its Supervisor \u2014 nobody else depends on it. If "${comp.id}" needs this data it is shared data: move "${depId}" out of "${owner}"'s owns into a data component both depend on (the Supervisor keeping it through read and lifecycle calls), or reach what "${comp.id}" needs through "${owner}".`, comp.id, isDraftCtx);
20410
+ continue;
20411
+ }
20412
+ ctx.addIssue("error", "VISIBILITY_VIOLATION", `Component "${comp.id}" depends on "${depId}", which is privately owned by pattern "${owner}". Depend on the facade "${owner}" instead.`, comp.id, isDraftCtx);
19181
20413
  }
19182
20414
  }
19183
20415
  }
@@ -19679,6 +20911,25 @@ var packRequirementsRule = {
19679
20911
  }
19680
20912
  };
19681
20913
 
20914
+ // src/core/rules/extension/pack-deprecations.ts
20915
+ var packDeprecationsRule = {
20916
+ name: "pack-deprecations",
20917
+ judges: "design",
20918
+ 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.",
20919
+ codes: [
20920
+ { code: "PACK_FIELD_DEPRECATED", defaultSeverity: "notice", summary: "A loaded pack declares a deprecated field, accepted and ignored for one release" }
20921
+ ],
20922
+ check(ctx) {
20923
+ for (const deprecation of ctx.ext.deprecations) {
20924
+ ctx.addIssue(
20925
+ "notice",
20926
+ "PACK_FIELD_DEPRECATED",
20927
+ `Deprecated pack field \u2014 ${deprecation}. It is accepted and ignored in this release and removed in the next; take it out of the pack.`
20928
+ );
20929
+ }
20930
+ }
20931
+ };
20932
+
19682
20933
  // src/core/rules/wiring/dependency-cycles.ts
19683
20934
  var cyclesRule = {
19684
20935
  name: "dependency-cycles",
@@ -20291,9 +21542,9 @@ var durabilityRule = {
20291
21542
  // Stage 8: its verdict needs the whole system's specs, so a part judged alone skips it.
20292
21543
  needsWholeTree: true,
20293
21544
  judges: "design",
20294
- description: "A durable Store (persisted RAM projection) must carry effect-tagged contract methods, and its writes require a hydration read-back reachable from a lifecycle init entrypoint. read-through is exempt (every read IS the read-back), as are ram-projection (rebuilt not restored) and cache (evictable, loss-safe). The flagship semantic check is opt-out by declaration, never silently absent \u2014 the declaration itself is enforced by durability-declaration.",
21545
+ description: "A durable Store (persisted RAM projection) must carry effect-tagged contract methods, and its mutations \u2014 write-effect methods, and lifecycle-effect methods, which change what exists \u2014 require a hydration read-back reachable from a lifecycle init entrypoint. read-through is exempt (every read IS the read-back), as are ram-projection (rebuilt not restored) and cache (evictable, loss-safe). The flagship semantic check is opt-out by declaration, never silently absent \u2014 the declaration itself is enforced by durability-declaration.",
20295
21546
  codes: [
20296
- { code: "MISSING_EFFECT_TAG", defaultSeverity: "warning", summary: "Durable Store contract method lacks an effect: read | write tag" },
21547
+ { code: "MISSING_EFFECT_TAG", defaultSeverity: "warning", summary: "Durable Store contract method lacks an effect: read | write | lifecycle tag" },
20297
21548
  { code: "MISSING_HYDRATION", defaultSeverity: "error", summary: "Durable Store is written but no read-back is reachable from any lifecycle init entrypoint" }
20298
21549
  ],
20299
21550
  check(ctx) {
@@ -20321,7 +21572,7 @@ var durabilityRule = {
20321
21572
  isDraftCtx
20322
21573
  );
20323
21574
  }
20324
- const writes = methods.filter((method) => method.effect === "write");
21575
+ const writes = methods.filter((method) => method.effect === "write" || method.effect === "lifecycle");
20325
21576
  const reads = methods.filter((method) => method.effect === "read");
20326
21577
  if (writes.length === 0) continue;
20327
21578
  const hydrated = initReach !== null && reads.some((method) => initReach.reachesMethod(comp.id, method.name));
@@ -20340,14 +21591,22 @@ var durabilityRule = {
20340
21591
  };
20341
21592
 
20342
21593
  // src/core/rules/wiring/untyped-seams.ts
20343
- var BARE_SEAM_TYPES = /* @__PURE__ */ new Set(["json", "any", "unknown", "object"]);
20344
- function namesBareType(refs) {
20345
- return refs.some((ref) => BARE_SEAM_TYPES.has(ref.toLowerCase()));
21594
+ var UNTYPED_SPELLINGS = /* @__PURE__ */ new Set(["any", "json", "unknown", "object"]);
21595
+ function holdsAny(expr) {
21596
+ return expr.form === "primitive" && expr.name === "any" || expr.args.some(holdsAny);
21597
+ }
21598
+ function tokensUntyped(text3) {
21599
+ return extractTypeIdentifiers(text3).some((ref) => UNTYPED_SPELLINGS.has(ref.toLowerCase()));
21600
+ }
21601
+ function positionUntyped(text3, position) {
21602
+ const parse = parseTypePosition(text3, position);
21603
+ if (parse.expression && !parse.problem) return holdsAny(parse.expression);
21604
+ return tokensUntyped(text3);
20346
21605
  }
20347
21606
  var untypedSeamRule = {
20348
21607
  name: "untyped-seams",
20349
21608
  judges: "design",
20350
- 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.",
21609
+ 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.",
20351
21610
  codes: [
20352
21611
  { code: "UNTYPED_SEAM", defaultSeverity: "warning", summary: "Bare Json/any/unknown/object parameter or return crossing a subsystem public surface" }
20353
21612
  ],
@@ -20365,14 +21624,14 @@ var untypedSeamRule = {
20365
21624
  const offenders = [];
20366
21625
  if (m.params && m.params.length > 0) {
20367
21626
  for (const p of m.params) {
20368
- if (namesBareType(extractTypeIdentifiers(p.type))) {
21627
+ if (positionUntyped(p.type, "param")) {
20369
21628
  offenders.push(`param "${p.name}: ${p.type}"`);
20370
21629
  }
20371
21630
  }
20372
- if (namesBareType(extractTypeIdentifiers(m.returns ?? ""))) {
21631
+ if (m.returns && positionUntyped(m.returns, "returns")) {
20373
21632
  offenders.push(`return "${m.returns}"`);
20374
21633
  }
20375
- } else if (namesBareType(methodTypeRefs(m))) {
21634
+ } else if (methodTypeRefs(m).some((ref) => UNTYPED_SPELLINGS.has(ref.toLowerCase()))) {
20376
21635
  offenders.push(`signature "${m.signature}"`);
20377
21636
  }
20378
21637
  if (offenders.length) {
@@ -21616,10 +22875,37 @@ function originOf(shape) {
21616
22875
  function absence(optional) {
21617
22876
  return optional ? "may be absent" : "is always there";
21618
22877
  }
22878
+ function judgeNamedScalar(ctx, type, file, facts) {
22879
+ if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") return;
22880
+ const dialect = dialectOf(facts);
22881
+ const holds = parseTypePosition(type.holds, "holds").expression;
22882
+ if (!dialect || !holds) return;
22883
+ const symbol = type.symbol ?? type.name;
22884
+ const own = (record) => !!record && Object.prototype.hasOwnProperty.call(record, symbol);
22885
+ let declared;
22886
+ if (own(facts.aliasTypes)) {
22887
+ const annotation = facts.aliasTypes[symbol];
22888
+ if (dialect.agrees(annotation, holds, /* @__PURE__ */ new Map())) return;
22889
+ declared = `an alias of \`${annotation}\``;
22890
+ } else if (own(facts.typeShapes)) {
22891
+ declared = "a record (an object shape)";
22892
+ } else {
22893
+ return;
22894
+ }
22895
+ ctx.addIssue(
22896
+ "warning",
22897
+ "TYPE_HOLDS_MISMATCH",
22898
+ `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.`,
22899
+ type.id,
22900
+ void 0,
22901
+ void 0,
22902
+ { at: symbol }
22903
+ );
22904
+ }
21619
22905
  var typeShapeRule = {
21620
22906
  name: "type-shape",
21621
22907
  judges: "code",
21622
- 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.",
22908
+ 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.",
21623
22909
  codes: [
21624
22910
  {
21625
22911
  code: "UNREALIZED_TYPE_FIELD",
@@ -21635,6 +22921,12 @@ var typeShapeRule = {
21635
22921
  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",
21636
22922
  carryable: true
21637
22923
  },
22924
+ {
22925
+ code: "TYPE_HOLDS_MISMATCH",
22926
+ defaultSeverity: "warning",
22927
+ 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",
22928
+ carryable: true
22929
+ },
21638
22930
  {
21639
22931
  code: "TYPE_FIELD_OPTIONALITY",
21640
22932
  defaultSeverity: "warning",
@@ -21649,6 +22941,10 @@ var typeShapeRule = {
21649
22941
  if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
21650
22942
  const file = pathKey(type.sourcePath);
21651
22943
  const facts = code.factsAt(file);
22944
+ if (type.holds !== void 0) {
22945
+ judgeNamedScalar(ctx, type, file, facts);
22946
+ continue;
22947
+ }
21652
22948
  if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
21653
22949
  const symbol = type.symbol ?? type.name;
21654
22950
  const shapes = facts.typeShapes;
@@ -21703,6 +22999,67 @@ var typeShapeRule = {
21703
22999
  }
21704
23000
  };
21705
23001
 
23002
+ // src/core/rules/conformance/enum-values.ts
23003
+ var enumValuesRule = {
23004
+ name: "enum-values",
23005
+ judges: "code",
23006
+ 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.",
23007
+ codes: [
23008
+ {
23009
+ code: "UNREALIZED_ENUM_VALUE",
23010
+ defaultSeverity: "warning",
23011
+ 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",
23012
+ carryable: true
23013
+ },
23014
+ {
23015
+ code: "UNDECLARED_ENUM_VALUE",
23016
+ defaultSeverity: "warning",
23017
+ summary: "The declaration at an enum type's sourcePath holds a value the enum does not list \u2014 a value the design never described",
23018
+ carryable: true
23019
+ }
23020
+ ],
23021
+ check(ctx) {
23022
+ const code = ctx.codeIndex();
23023
+ for (const type of ctx.types) {
23024
+ if (type.kind !== "enum" || !type.sourcePath) continue;
23025
+ if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
23026
+ const file = pathKey(type.sourcePath);
23027
+ const facts = code.factsAt(file);
23028
+ const symbol = type.symbol ?? type.name;
23029
+ if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
23030
+ const declared = facts.enumValues;
23031
+ if (!declared || !Object.prototype.hasOwnProperty.call(declared, symbol)) continue;
23032
+ const held = new Set(declared[symbol]);
23033
+ const listed = (type.values ?? []).map((value) => value.name);
23034
+ const listedSet = new Set(listed);
23035
+ const unrealized = listed.filter((value) => !held.has(value));
23036
+ if (unrealized.length > 0) {
23037
+ ctx.addIssue(
23038
+ "warning",
23039
+ "UNREALIZED_ENUM_VALUE",
23040
+ `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).`,
23041
+ type.id,
23042
+ void 0,
23043
+ void 0,
23044
+ { at: symbol, covers: unrealized }
23045
+ );
23046
+ }
23047
+ const undeclared = declared[symbol].filter((value) => !listedSet.has(value));
23048
+ if (undeclared.length > 0) {
23049
+ ctx.addIssue(
23050
+ "warning",
23051
+ "UNDECLARED_ENUM_VALUE",
23052
+ `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.`,
23053
+ type.id,
23054
+ void 0,
23055
+ void 0,
23056
+ { at: symbol, covers: undeclared }
23057
+ );
23058
+ }
23059
+ }
23060
+ }
23061
+ };
23062
+
21706
23063
  // src/core/rules/conformance/param-conformance.ts
21707
23064
  function omittable(optional) {
21708
23065
  return optional ? "may be left out" : "is required";
@@ -21753,7 +23110,7 @@ function agreed(judgements, reading) {
21753
23110
  var paramConformanceRule = {
21754
23111
  name: "param-conformance",
21755
23112
  judges: "code",
21756
- 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.",
23113
+ 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.",
21757
23114
  codes: [
21758
23115
  {
21759
23116
  code: "UNREALIZED_PARAM",
@@ -21790,13 +23147,13 @@ var paramConformanceRule = {
21790
23147
  codeNameOf.set(type.id, named2);
21791
23148
  if (type.subsystem) codeNameOf.set(`${type.subsystem}.${type.id}`, named2);
21792
23149
  }
21793
- const normalize3 = (text3) => text3.replace(/\s+/g, " ").trim();
21794
- const typeAgrees = (declared, realized) => {
21795
- if (!realized.type) return false;
21796
- const stated = normalize3(declared.type);
21797
- return stated === realized.type || codeNameOf.get(stated) === realized.type;
23150
+ const typeAgreesIn = (dialect) => (declared, realized) => {
23151
+ if (!realized.type || !dialect) return false;
23152
+ const stated = parseTypePosition(declared.type, "param", !!declared.optional);
23153
+ if (!stated.expression) return false;
23154
+ return dialect.agrees(realized.type, stated.expression, codeNameOf);
21798
23155
  };
21799
- for (const { implementation, method, sourceFile, draftContext: draftContext3 } of ctx.implementationMethods()) {
23156
+ for (const { implementation, method, sourceFile, draftContext: draftContext4 } of ctx.implementationMethods()) {
21800
23157
  const contract = ctx.interfaceMap.get(implementation.contract);
21801
23158
  const declared = contract?.methods.find((m) => m.name === method.name)?.params ?? [];
21802
23159
  if (declared.length === 0 || !sourceFile) continue;
@@ -21809,6 +23166,7 @@ var paramConformanceRule = {
21809
23166
  const candidates = signatures[symbol];
21810
23167
  if (candidates.length === 0) continue;
21811
23168
  const injected = new Set(implementation.injectedParams ?? []);
23169
+ const typeAgrees = typeAgreesIn(dialectOf(facts));
21812
23170
  const judgements = candidates.map((realized) => judge(declared, realized, injected, typeAgrees));
21813
23171
  const subject = candidates.length > 1 ? `every function called "${symbol}" in "${file}"` : `the function "${symbol}" in "${file}"`;
21814
23172
  const opening = subject.charAt(0).toUpperCase() + subject.slice(1);
@@ -21819,7 +23177,7 @@ var paramConformanceRule = {
21819
23177
  "UNREALIZED_PARAM",
21820
23178
  `Method "${method.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.`,
21821
23179
  implementation.id,
21822
- draftContext3,
23180
+ draftContext4,
21823
23181
  void 0,
21824
23182
  { at: method.name, covers: unrealized.map((found) => found.unit) }
21825
23183
  );
@@ -21831,7 +23189,7 @@ var paramConformanceRule = {
21831
23189
  "UNDECLARED_PARAM",
21832
23190
  `${opening} realizing method "${method.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.`,
21833
23191
  implementation.id,
21834
- draftContext3,
23192
+ draftContext4,
21835
23193
  void 0,
21836
23194
  { at: method.name, covers: undeclared.map((found) => found.unit) }
21837
23195
  );
@@ -21843,7 +23201,7 @@ var paramConformanceRule = {
21843
23201
  "PARAM_NAME_MISMATCH",
21844
23202
  `Method "${method.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.`,
21845
23203
  implementation.id,
21846
- draftContext3,
23204
+ draftContext4,
21847
23205
  void 0,
21848
23206
  { at: method.name, covers: renamed.map((found) => found.unit) }
21849
23207
  );
@@ -21855,7 +23213,7 @@ var paramConformanceRule = {
21855
23213
  "PARAM_OPTIONALITY",
21856
23214
  `Method "${method.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.`,
21857
23215
  implementation.id,
21858
- draftContext3,
23216
+ draftContext4,
21859
23217
  void 0,
21860
23218
  { at: method.name, covers: disagreed.map((found) => found.unit) }
21861
23219
  );
@@ -21864,6 +23222,62 @@ var paramConformanceRule = {
21864
23222
  }
21865
23223
  };
21866
23224
 
23225
+ // src/core/rules/conformance/async-conformance.ts
23226
+ var asyncConformanceRule = {
23227
+ name: "async-conformance",
23228
+ judges: "code",
23229
+ 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.",
23230
+ codes: [
23231
+ {
23232
+ code: "ASYNC_MISMATCH",
23233
+ defaultSeverity: "warning",
23234
+ 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",
23235
+ carryable: true
23236
+ }
23237
+ ],
23238
+ check(ctx) {
23239
+ const code = ctx.codeIndex();
23240
+ for (const { implementation, method, sourceFile, draftContext: draftContext4 } of ctx.implementationMethods()) {
23241
+ const contractMethod = ctx.interfaceMap.get(implementation.contract)?.methods.find((m) => m.name === method.name);
23242
+ if (!contractMethod || typeof contractMethod.returns !== "string" || !sourceFile) continue;
23243
+ const file = pathKey(sourceFile);
23244
+ const facts = code.factsAt(file);
23245
+ if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
23246
+ const symbol = method.symbol ?? method.name;
23247
+ const signatures = facts.functionParams;
23248
+ if (!signatures || !Object.prototype.hasOwnProperty.call(signatures, symbol)) continue;
23249
+ const bodies = signatures[symbol].length;
23250
+ if (bodies === 0) continue;
23251
+ const returns = parseTypeExpression(contractMethod.returns, "returns");
23252
+ if (!returns.expression) continue;
23253
+ const contractAsync = returns.expression.form === "async";
23254
+ const asyncBodies = (facts.asyncFunctions ?? []).filter((name) => name === symbol).length;
23255
+ const subject = bodies > 1 ? `every function called "${symbol}" in "${file}"` : `the function "${symbol}" in "${file}"`;
23256
+ if (contractAsync && asyncBodies === 0) {
23257
+ ctx.addIssue(
23258
+ "warning",
23259
+ "ASYNC_MISMATCH",
23260
+ `Method "${method.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.`,
23261
+ implementation.id,
23262
+ draftContext4,
23263
+ void 0,
23264
+ { at: method.name }
23265
+ );
23266
+ } else if (!contractAsync && asyncBodies === bodies) {
23267
+ ctx.addIssue(
23268
+ "warning",
23269
+ "ASYNC_MISMATCH",
23270
+ `Method "${method.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.`,
23271
+ implementation.id,
23272
+ draftContext4,
23273
+ void 0,
23274
+ { at: method.name }
23275
+ );
23276
+ }
23277
+ }
23278
+ }
23279
+ };
23280
+
21867
23281
  // src/core/rules/conformance/route-coverage.ts
21868
23282
  function liesUnder2(path49, prefix) {
21869
23283
  return path49 === prefix || path49.startsWith(prefix + "/");
@@ -21932,7 +23346,7 @@ var routeCoverageRule = {
21932
23346
  const routes = code.factsAt(file).functionRoutes;
21933
23347
  return routes && Object.prototype.hasOwnProperty.call(routes, via) ? routes[via] : [];
21934
23348
  });
21935
- const draftContext3 = ctx.isComponentDraft(listener.id) || ctx.isComponentDraft(portal.id);
23349
+ const draftContext4 = ctx.isComponentDraft(listener.id) || ctx.isComponentDraft(portal.id);
21936
23350
  const where = holders.map((file) => `"${file}"`).join(", ");
21937
23351
  if (read2.length === 0) {
21938
23352
  ctx.addIssue(
@@ -21940,7 +23354,7 @@ var routeCoverageRule = {
21940
23354
  "UNREADABLE_ROUTER",
21941
23355
  `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.`,
21942
23356
  anchor,
21943
- draftContext3,
23357
+ draftContext4,
21944
23358
  void 0,
21945
23359
  { at: via }
21946
23360
  );
@@ -21965,7 +23379,7 @@ var routeCoverageRule = {
21965
23379
  "UNDECLARED_ROUTE",
21966
23380
  `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.`,
21967
23381
  anchor,
21968
- draftContext3,
23382
+ draftContext4,
21969
23383
  void 0,
21970
23384
  { at: via, covers: undeclared }
21971
23385
  );
@@ -21977,7 +23391,7 @@ var routeCoverageRule = {
21977
23391
  "UNROUTED_ENDPOINT",
21978
23392
  `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.`,
21979
23393
  anchor,
21980
- draftContext3,
23394
+ draftContext4,
21981
23395
  void 0,
21982
23396
  { at: via, covers: unrouted }
21983
23397
  );
@@ -22080,7 +23494,7 @@ var exportConformanceRule = {
22080
23494
  }
22081
23495
  }
22082
23496
  }
22083
- for (const { implementation, method, sourceFile, draftContext: draftContext3 } of ctx.implementationMethods()) {
23497
+ for (const { implementation, method, sourceFile, draftContext: draftContext4 } of ctx.implementationMethods()) {
22084
23498
  const handle = method.exportedVia;
22085
23499
  if (!handle || !sourceFile) continue;
22086
23500
  const file = pathKey(sourceFile);
@@ -22095,7 +23509,7 @@ var exportConformanceRule = {
22095
23509
  "UNREALIZED_EXPORT_HANDLE",
22096
23510
  `Method "${method.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.`,
22097
23511
  implementation.id,
22098
- draftContext3
23512
+ draftContext4
22099
23513
  );
22100
23514
  }
22101
23515
  for (const listener of ctx.components) {
@@ -22468,82 +23882,6 @@ var couplingRule = {
22468
23882
  }
22469
23883
  };
22470
23884
 
22471
- // src/core/rules/heuristic/signature-language-builtins.ts
22472
- var LANGUAGE_MARKERS = {
22473
- rust: /* @__PURE__ */ new Set([
22474
- "u8",
22475
- "u16",
22476
- "u32",
22477
- "u64",
22478
- "u128",
22479
- "usize",
22480
- "i8",
22481
- "i16",
22482
- "i32",
22483
- "i64",
22484
- "i128",
22485
- "isize",
22486
- "f32",
22487
- "f64",
22488
- "vec",
22489
- "box",
22490
- "arc",
22491
- "rc",
22492
- "refcell",
22493
- "cell",
22494
- "mutex",
22495
- "rwlock",
22496
- "str"
22497
- ]),
22498
- typescript: /* @__PURE__ */ new Set(["any", "unknown", "never", "undefined", "promise", "record"]),
22499
- javascript: /* @__PURE__ */ new Set(["promise", "undefined"]),
22500
- python: /* @__PURE__ */ new Set(["dict", "tuple"]),
22501
- csharp: /* @__PURE__ */ new Set(["task"]),
22502
- go: /* @__PURE__ */ new Set(["chan", "rune"])
22503
- };
22504
- var signatureLanguageBuiltinsRule = {
22505
- name: "signature-language-builtins",
22506
- judges: "design",
22507
- 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).",
22508
- codes: [
22509
- { code: "LANGUAGE_FOREIGN_BUILTIN", defaultSeverity: "warning", summary: "Signature uses a builtin from a different language family" }
22510
- ],
22511
- check(ctx) {
22512
- const markersFor = (family) => {
22513
- const base = LANGUAGE_MARKERS[family];
22514
- const extra = ctx.ext.languages[family]?.foreignBuiltins;
22515
- if (!extra?.length) return base;
22516
- return /* @__PURE__ */ new Set([...base ?? [], ...extra.map((s) => s.toLowerCase())]);
22517
- };
22518
- const families = [.../* @__PURE__ */ new Set([...Object.keys(LANGUAGE_MARKERS), ...Object.keys(ctx.ext.languages)])];
22519
- const scans = [];
22520
- for (const intf of ctx.interfaces) {
22521
- const comp = ctx.componentMap.get(intf.component);
22522
- const lang = ctx.targetLanguageFor(comp?.subsystem);
22523
- if (!lang) continue;
22524
- const ownMarkers = markersFor(lang);
22525
- if (!ownMarkers || ownMarkers.size === 0) continue;
22526
- const draft = ctx.isComponentDraft(intf.component) || intf.status === "draft" || intf.status === "design";
22527
- for (const method of intf.methods) {
22528
- for (const ref of methodTypeRefs(method)) scans.push({ intf, method, ref, lang, ownMarkers, draft });
22529
- }
22530
- }
22531
- for (const scan of scans) {
22532
- const refLower = scan.ref.toLowerCase();
22533
- if (scan.ownMarkers.has(refLower)) continue;
22534
- const foreign = families.find((family) => family !== scan.lang && markersFor(family)?.has(refLower));
22535
- if (!foreign) continue;
22536
- ctx.addIssue(
22537
- "warning",
22538
- "LANGUAGE_FOREIGN_BUILTIN",
22539
- `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.`,
22540
- scan.intf.id,
22541
- scan.draft
22542
- );
22543
- }
22544
- }
22545
- };
22546
-
22547
23885
  // src/core/rules/heuristic/narrative-language-constructs.ts
22548
23886
  var UNSUPPORTED_FLOW = {
22549
23887
  rust: {
@@ -23469,6 +24807,11 @@ var SDD_RULES = [
23469
24807
  signatureSourcesRule,
23470
24808
  signatureTypesRule,
23471
24809
  signatureTextRule,
24810
+ // The type grammar beside the signatures: how each stored type position is
24811
+ // spelled, then what an enum and a named scalar may hold.
24812
+ typeExpressionsRule,
24813
+ enumTypesRule,
24814
+ namedScalarTypesRule,
23472
24815
  // Contracts and the targets narratives name, in four questions with one
23473
24816
  // owner each: does the implementation mirror its contract, does a target
23474
24817
  // inside this tree resolve, does a target that leaves it pin to exactly one
@@ -23522,6 +24865,10 @@ var SDD_RULES = [
23522
24865
  dataBlockDepsRule,
23523
24866
  entrypointDepsRule,
23524
24867
  portalWriteShortcutRule,
24868
+ // The process layer's data reach and the lifecycle effect's closure, both
24869
+ // read from narratives against the callee's declared effect.
24870
+ supervisorSharedDataRule,
24871
+ lifecycleEffectClosureRule,
23525
24872
  // Patterns in four questions: who may own and what a claim must name,
23526
24873
  // what each pattern must contain, which data blocks are left standing
23527
24874
  // alone, and who may see a private member.
@@ -23633,14 +24980,17 @@ var SDD_RULES = [
23633
24980
  // others named.
23634
24981
  typeRealizationRule,
23635
24982
  typeShapeRule,
24983
+ // An enum's values beside the data shape, and whether the call completes
24984
+ // later beside the signature: the same readings, of their own facts.
24985
+ enumValuesRule,
23636
24986
  paramConformanceRule,
24987
+ asyncConformanceRule,
23637
24988
  routeCoverageRule,
23638
24989
  unclaimedSourceRule,
23639
24990
  exportConformanceRule,
23640
24991
  couplingRule,
23641
- // Target-language fit in two questions: what a CONTRACT may name, and what
23642
- // a NARRATIVE may describe.
23643
- signatureLanguageBuiltinsRule,
24992
+ // Target-language fit: what a NARRATIVE may describe. What a contract may
24993
+ // name is the neutral type grammar's (type-expressions), not a language's.
23644
24994
  narrativeLanguageConstructsRule,
23645
24995
  // The declaration before the consequences: which stereotype may bind a
23646
24996
  // technology at all, then where its name may appear.
@@ -23664,6 +25014,9 @@ var SDD_RULES = [
23664
25014
  // stage): configuration too, and the family run cannot judge a member against
23665
25015
  // a requirement that does not parse.
23666
25016
  packRequirementsRule,
25017
+ // The fields a loaded pack still declares that wairon has deprecated: about
25018
+ // the packs' configuration too, and only ever a notice.
25019
+ packDeprecationsRule,
23667
25020
  // MUST run last, in this order: each audits what the earlier rules did
23668
25021
  // with a declared exception. The debt register first (which carried
23669
25022
  // findings the conformance family actually matched), then the allows
@@ -23706,30 +25059,6 @@ var import_module2 = require("module");
23706
25059
  function emptyCodeModel() {
23707
25060
  return { files: [], projectRoot: "", rootFiles: [] };
23708
25061
  }
23709
- var EXTENSION_LANGUAGE = {
23710
- ".ts": "typescript",
23711
- ".tsx": "typescript",
23712
- ".mts": "typescript",
23713
- ".cts": "typescript",
23714
- ".js": "javascript",
23715
- ".jsx": "javascript",
23716
- ".mjs": "javascript",
23717
- ".cjs": "javascript",
23718
- ".py": "python",
23719
- ".rs": "rust",
23720
- ".go": "go",
23721
- ".cs": "csharp",
23722
- ".java": "java",
23723
- ".c": "c",
23724
- ".h": "c",
23725
- ".cpp": "cpp",
23726
- ".cc": "cpp",
23727
- ".hpp": "cpp",
23728
- ".rb": "ruby",
23729
- ".php": "php",
23730
- ".kt": "kotlin",
23731
- ".swift": "swift"
23732
- };
23733
25062
  var C_FAMILY_COMMENTS = { lineComments: ["//"], blockComments: [["/*", "*/"]] };
23734
25063
  var NAMED_IMPORT_BINDINGS_RE = /\bimport\s*\{([^}]*)\}/g;
23735
25064
  var JS_PATTERNS = {
@@ -23962,6 +25291,9 @@ function walkExact(ts, sourceText, fileName) {
23962
25291
  const typeShapes = /* @__PURE__ */ new Map();
23963
25292
  const functionParams = /* @__PURE__ */ new Map();
23964
25293
  const functionRoutes = /* @__PURE__ */ new Map();
25294
+ const asyncFunctions = [];
25295
+ const enumValues = /* @__PURE__ */ new Map();
25296
+ const aliasTypes = /* @__PURE__ */ new Map();
23965
25297
  const mutableBindings = /* @__PURE__ */ new Set();
23966
25298
  const schemaConstants = /* @__PURE__ */ new Map();
23967
25299
  const derivedAliases = [];
@@ -24176,6 +25508,48 @@ function walkExact(ts, sourceText, fileName) {
24176
25508
  if (fn.body) walk2(fn.body, []);
24177
25509
  return routes;
24178
25510
  };
25511
+ const completesLater = (fn) => {
25512
+ const mods = fn.modifiers;
25513
+ if (mods?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword)) return true;
25514
+ const returned = fn.type;
25515
+ return !!returned && ts.isTypeReferenceNode(returned) && ts.isIdentifier(returned.typeName) && returned.typeName.text === "Promise";
25516
+ };
25517
+ const stringLiteralUnion = (type) => {
25518
+ const members = ts.isUnionTypeNode(type) ? [...type.types] : [type];
25519
+ const values = [];
25520
+ for (const member of members) {
25521
+ if (!ts.isLiteralTypeNode(member) || !ts.isStringLiteral(member.literal)) return void 0;
25522
+ values.push(member.literal.text);
25523
+ }
25524
+ return values;
25525
+ };
25526
+ const zodEnumValues = (expression) => {
25527
+ const strings = (array, hops) => {
25528
+ let node2 = array;
25529
+ while (node2 && (ts.isAsExpression(node2) || ts.isParenthesizedExpression(node2) || ts.isSatisfiesExpression(node2))) node2 = node2.expression;
25530
+ if (node2 && ts.isIdentifier(node2) && hops > 0) return strings(schemaConstants.get(node2.text), hops - 1);
25531
+ if (!node2 || !ts.isArrayLiteralExpression(node2) || node2.elements.length === 0) return void 0;
25532
+ const values = [];
25533
+ for (const element of node2.elements) {
25534
+ if (!ts.isStringLiteral(element) && !ts.isNoSubstitutionTemplateLiteral(element)) return void 0;
25535
+ values.push(element.text);
25536
+ }
25537
+ return values;
25538
+ };
25539
+ let node = expression;
25540
+ const walked = /* @__PURE__ */ new Set();
25541
+ while (node && !walked.has(node)) {
25542
+ walked.add(node);
25543
+ if (ts.isCallExpression(node)) {
25544
+ const callee = node.expression;
25545
+ if (ts.isPropertyAccessExpression(callee) && callee.name.text === "enum") return strings(node.arguments[0], 1);
25546
+ node = callee;
25547
+ } else if (ts.isPropertyAccessExpression(node)) {
25548
+ node = node.expression;
25549
+ } else break;
25550
+ }
25551
+ return void 0;
25552
+ };
24179
25553
  const visit = (node) => {
24180
25554
  const fnName = namedFunctionName(node);
24181
25555
  if (fnName && node.body) {
@@ -24187,6 +25561,7 @@ function walkExact(ts, sourceText, fileName) {
24187
25561
  const signatures = functionParams.get(fnName) ?? [];
24188
25562
  signatures.push(declaredParameters(node));
24189
25563
  functionParams.set(fnName, signatures);
25564
+ if (completesLater(node)) asyncFunctions.push(fnName);
24190
25565
  const routes = collectRoutes(node);
24191
25566
  if (routes.size > 0) {
24192
25567
  const known = functionRoutes.get(fnName) ?? /* @__PURE__ */ new Map();
@@ -24209,10 +25584,17 @@ function walkExact(ts, sourceText, fileName) {
24209
25584
  } else if (ts.isTypeAliasDeclaration(node)) {
24210
25585
  if (ts.isTypeLiteralNode(node.type)) recordDeclaredShape(node.name.text, node.type.members, false);
24211
25586
  else {
25587
+ if (!aliasTypes.has(node.name.text)) aliasTypes.set(node.name.text, node.type.getText(sf));
24212
25588
  const constant = inferredSchemaConstant(node.type);
24213
25589
  if (constant) derivedAliases.push({ name: node.name.text, constant });
25590
+ const literals = stringLiteralUnion(node.type);
25591
+ if (literals) enumValues.set(node.name.text, literals);
24214
25592
  }
24215
25593
  }
25594
+ if (ts.isEnumDeclaration(node)) {
25595
+ const values = node.members.map((member) => member.initializer && (ts.isStringLiteral(member.initializer) || ts.isNoSubstitutionTemplateLiteral(member.initializer)) ? member.initializer.text : void 0);
25596
+ if (values.length > 0 && values.every((v) => v !== void 0)) enumValues.set(node.name.text, values);
25597
+ }
24216
25598
  if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && !schemaConstants.has(node.name.text)) {
24217
25599
  schemaConstants.set(node.name.text, node.initializer);
24218
25600
  }
@@ -24334,8 +25716,17 @@ function walkExact(ts, sourceText, fileName) {
24334
25716
  if (!fields) continue;
24335
25717
  typeShapes.set(alias.name, { origin: "derived", fields, methods: [] });
24336
25718
  }
25719
+ for (const [name, initializer] of schemaConstants) {
25720
+ const values = zodEnumValues(initializer);
25721
+ if (values) enumValues.set(name, values);
25722
+ }
25723
+ for (const alias of derivedAliases) {
25724
+ if (enumValues.has(alias.name)) continue;
25725
+ const values = enumValues.get(alias.constant);
25726
+ if (values && schemaConstants.has(alias.constant)) enumValues.set(alias.name, values);
25727
+ }
24337
25728
  const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
24338
- return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, functionRoutes, mutableBindings, reexportOnly };
25729
+ return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, functionRoutes, asyncFunctions, enumValues, aliasTypes, mutableBindings, reexportOnly };
24339
25730
  }
24340
25731
  function resolveRelativeModule(fromFile, specifier) {
24341
25732
  if (!specifier.startsWith(".")) return null;
@@ -24414,7 +25805,7 @@ function walkDeclaredRoots(roots, exclude, projectRoot) {
24414
25805
  const record = (absolute) => {
24415
25806
  const key = pathKey(path18.relative(projectRoot, absolute));
24416
25807
  if (seen.has(key) || isExcluded(key)) return;
24417
- if (!EXTENSION_LANGUAGE[path18.extname(key).toLowerCase()]) return;
25808
+ if (!languageOfSourcePath(key)) return;
24418
25809
  seen.add(key);
24419
25810
  found.push(key);
24420
25811
  };
@@ -24496,7 +25887,7 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
24496
25887
  continue;
24497
25888
  }
24498
25889
  const text3 = buffer.toString("utf8");
24499
- const language = EXTENSION_LANGUAGE[path18.extname(sourcePath).toLowerCase()];
25890
+ const language = languageOfSourcePath(sourcePath);
24500
25891
  let analyzed;
24501
25892
  if (language === "typescript" || language === "javascript") {
24502
25893
  const ts = resolveTypeScript(projectRoot);
@@ -24524,6 +25915,9 @@ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], e
24524
25915
  typeShapes: Object.fromEntries(facts.typeShapes),
24525
25916
  functionParams: Object.fromEntries(facts.functionParams),
24526
25917
  functionRoutes: Object.fromEntries([...facts.functionRoutes].map(([k, v]) => [k, [...v.values()]])),
25918
+ asyncFunctions: [...facts.asyncFunctions],
25919
+ enumValues: Object.fromEntries(facts.enumValues),
25920
+ aliasTypes: Object.fromEntries(facts.aliasTypes),
24527
25921
  topLevelMutableBindings: [...facts.mutableBindings],
24528
25922
  reexportOnly: facts.reexportOnly
24529
25923
  };
@@ -24953,7 +26347,7 @@ function buildRuleContext(opts) {
24953
26347
  const authoredTypeRefs = new Set((family?.authoredReferences ?? []).filter((r) => r.position === "type" && r.binding !== "local" && memberOf(r.specId) === void 0).map((r) => r.authored));
24954
26348
  const isTypeResolved = (ref, generics) => {
24955
26349
  const refLower = ref.toLowerCase();
24956
- if (BUILTIN_TYPES.has(refLower)) return true;
26350
+ if (isTypeVocabulary(refLower)) return true;
24957
26351
  if (generics.has(refLower)) return true;
24958
26352
  if (ownTypes.some((spec) => typeMatchesRef(spec, ref))) return true;
24959
26353
  const [first, ...rest] = ref.split(/::|\./);
@@ -25167,7 +26561,7 @@ function buildRuleContext(opts) {
25167
26561
  }
25168
26562
  return projectNaming;
25169
26563
  };
25170
- const isBuiltinType = (ref) => BUILTIN_TYPES.has(ref.toLowerCase());
26564
+ const isBuiltinType = (ref) => isTypeVocabulary(ref);
25171
26565
  const getRuleSeverity = (ruleCode, defaultSeverity, isDraftContext, subsystemId) => {
25172
26566
  if (rules?.sddRuleSeverity?.[ruleCode]) {
25173
26567
  return rules.sddRuleSeverity[ruleCode];
@@ -25198,7 +26592,16 @@ function buildRuleContext(opts) {
25198
26592
  const sitesSeen = /* @__PURE__ */ new Map();
25199
26593
  const sitesReported = (specId, code) => {
25200
26594
  const seen = sitesSeen.get(`${specId}\0${code}`);
25201
- return { sites: [...seen?.sites ?? []], unsited: seen?.unsited ?? false };
26595
+ return { sites: [...seen?.sites ?? []], unsited: seen?.unsited ?? false, errored: seen?.errored ?? false };
26596
+ };
26597
+ const codeDefaults = new Map(opts.issueCodeSeverities ?? []);
26598
+ for (const a of opts.extensions?.assertions ?? []) {
26599
+ if (!codeDefaults.has(a.fullCode)) codeDefaults.set(a.fullCode, a.severity);
26600
+ }
26601
+ const severityOf = (code, specId) => {
26602
+ const defaultSeverity = codeDefaults.get(code);
26603
+ if (!defaultSeverity) return void 0;
26604
+ return getRuleSeverity(code, defaultSeverity, false, subsystemOfSpec.get(specId));
25202
26605
  };
25203
26606
  for (const s of subsystems) collectAllows(s.id, s.lint);
25204
26607
  for (const c of components) collectAllows(c.id, c.lint);
@@ -25241,9 +26644,10 @@ function buildRuleContext(opts) {
25241
26644
  if (specId) {
25242
26645
  const key = `${specId}\0${code}`;
25243
26646
  let seen = sitesSeen.get(key);
25244
- if (!seen) sitesSeen.set(key, seen = { sites: /* @__PURE__ */ new Set(), unsited: false });
26647
+ if (!seen) sitesSeen.set(key, seen = { sites: /* @__PURE__ */ new Set(), unsited: false, errored: false });
25245
26648
  if (parts) seen.sites.add(parts.at);
25246
26649
  else seen.unsited = true;
26650
+ if (severity === "error") seen.errored = true;
25247
26651
  }
25248
26652
  let allowClaimed = false;
25249
26653
  if (specId) {
@@ -25345,7 +26749,7 @@ function buildRuleContext(opts) {
25345
26749
  documentationConfigFor,
25346
26750
  namingConfigFor,
25347
26751
  isBuiltinType,
25348
- 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 ?? [] },
26752
+ 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 ?? [] },
25349
26753
  variants: opts.variants ?? [],
25350
26754
  surfaceSnapshots,
25351
26755
  ...opts.projectIdentity ? { projectIdentity: opts.projectIdentity } : {},
@@ -25354,11 +26758,13 @@ function buildRuleContext(opts) {
25354
26758
  ...opts.projectFamily ? { projectFamily: opts.projectFamily } : {},
25355
26759
  ...opts.exportUsages ? { exportUsages: opts.exportUsages } : {},
25356
26760
  ...opts.signatureFacts ? { signatureFacts: opts.signatureFacts } : {},
26761
+ ...opts.typeSpellingFacts ? { typeSpellingFacts: opts.typeSpellingFacts } : {},
25357
26762
  pinnedExternals,
25358
26763
  codeModel: opts.codeModel ?? emptyCodeModel(),
25359
26764
  roundTripIssues: opts.roundTripIssues,
25360
26765
  lintAllows: lintAllows2,
25361
26766
  sitesReported,
26767
+ severityOf,
25362
26768
  knownIssueCodes: opts.knownIssueCodes,
25363
26769
  carriedFindings,
25364
26770
  carryableIssueCodes: opts.carryableIssueCodes ?? /* @__PURE__ */ new Set(),
@@ -25708,7 +27114,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
25708
27114
  let extensions;
25709
27115
  let treatAllAsComplete = false;
25710
27116
  let packSelections;
25711
- 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)) {
27117
+ 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)) {
25712
27118
  const opts = rulesOrOptions;
25713
27119
  rules = opts.rules;
25714
27120
  projectType = opts.projectType ?? "backend";
@@ -25718,7 +27124,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
25718
27124
  packSelections = opts.packSelections;
25719
27125
  }
25720
27126
  extensions ??= loadProjectExtensions();
25721
- scanAllSpecs({ recursive: true });
27127
+ scanAllSpecs();
25722
27128
  const issues = [];
25723
27129
  clearLoaderIssues();
25724
27130
  const boundConfig = boundConfiguration();
@@ -25791,11 +27197,13 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
25791
27197
  const carryableCodes = new Set(
25792
27198
  knownIssueCodes().filter((rc) => rc.carryable).map((rc) => rc.code)
25793
27199
  );
27200
+ const codeSeverities = new Map(knownIssueCodes().map((rc) => [rc.code, rc.defaultSeverity]));
25794
27201
  const family = graph();
25795
27202
  const memberTables = family.nodes.filter((n) => n.namespace !== "").map((n) => resolveProjectExports(n.namespace));
25796
27203
  const producers = new Set(family.references.filter((r) => r.consumer === "").map((r) => r.producer));
25797
27204
  const exportUsages = [...producers].map((producer) => exportUsage("", producer));
25798
27205
  const signatures = signatureFacts();
27206
+ const typeSpellings = typeSpellingFacts();
25799
27207
  const ctx = buildRuleContext({
25800
27208
  system,
25801
27209
  subsystems,
@@ -25818,6 +27226,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
25818
27226
  projectFamily: family,
25819
27227
  exportUsages,
25820
27228
  signatureFacts: signatures,
27229
+ typeSpellingFacts: typeSpellings,
25821
27230
  pinnedExternals,
25822
27231
  // By-name selections only: a legacy path ref pins nothing to check. A dry
25823
27232
  // run supplies its candidate's; otherwise the stored ones.
@@ -25831,6 +27240,7 @@ function runOwnersGate(rulesOrOptions, projectType = "backend") {
25831
27240
  codeModel,
25832
27241
  roundTripIssues,
25833
27242
  knownIssueCodes: knownCodes,
27243
+ issueCodeSeverities: codeSeverities,
25834
27244
  carryableIssueCodes: carryableCodes,
25835
27245
  issues
25836
27246
  });
@@ -25938,7 +27348,12 @@ function judgePartAlone(config, scopeSubsystem) {
25938
27348
  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));
25939
27349
  }
25940
27350
  const ownSignatures = signatureFacts();
25941
- const resolved = resolveTree(
27351
+ const ownTypeSpellings = typeSpellingFacts();
27352
+ const partTypeSpellings = {
27353
+ respellings: ownTypeSpellings.respellings.filter((r) => isOwn(r.specId)),
27354
+ problems: ownTypeSpellings.problems.filter((p) => p.specId === void 0 || isOwn(p.specId))
27355
+ };
27356
+ const resolved = resolveSignatures(
25942
27357
  [...own.interfaces, ...context.interfaces],
25943
27358
  [...own.components, ...context.components],
25944
27359
  [...own.types, ...context.types]
@@ -25977,6 +27392,7 @@ function judgePartAlone(config, scopeSubsystem) {
25977
27392
  projectFamily: graph(),
25978
27393
  exportUsages: [],
25979
27394
  signatureFacts: partSignatures,
27395
+ typeSpellingFacts: partTypeSpellings,
25980
27396
  pinnedExternals: [],
25981
27397
  packSelections: packs.filter((p) => typeof p !== "string"),
25982
27398
  packRequirements: [],
@@ -26032,7 +27448,7 @@ function builtinProjectKinds() {
26032
27448
  return [...PROJECT_KINDS];
26033
27449
  }
26034
27450
  function computeGateStateId() {
26035
- scanAllSpecs({ recursive: true });
27451
+ scanAllSpecs();
26036
27452
  const content = computeOwnStateId();
26037
27453
  const extensions = loadProjectExtensions();
26038
27454
  let gate = {};
@@ -26684,16 +28100,16 @@ function bindReferencedMembers(node, root) {
26684
28100
  out.push({ ...base, sourceKind: "hosted", hosted: member.source.hosted });
26685
28101
  continue;
26686
28102
  }
26687
- const located = root.referenced.find((r) => r.alias === member.alias);
28103
+ const located2 = root.referenced.find((r) => r.alias === member.alias);
26688
28104
  const sourceKind = member.storage === "git" ? "git" : "path";
26689
- const directory = located?.directory ?? (member.storage === "path" ? path20.resolve(node.directory, member.source.path ?? "") : void 0);
26690
- const commit2 = located?.commit ?? member.source.commit;
28105
+ const directory = located2?.directory ?? (member.storage === "path" ? path20.resolve(node.directory, member.source.path ?? "") : void 0);
28106
+ const commit2 = located2?.commit ?? member.source.commit;
26691
28107
  out.push({
26692
28108
  ...base,
26693
28109
  sourceKind,
26694
28110
  ...directory !== void 0 ? { directory } : {},
26695
28111
  ...commit2 !== void 0 ? { commit: commit2 } : {},
26696
- ...located?.availability === "unavailable" ? { problem: located.reason } : {}
28112
+ ...located2?.availability === "unavailable" ? { problem: located2.reason } : {}
26697
28113
  });
26698
28114
  }
26699
28115
  return out;
@@ -27517,7 +28933,8 @@ function emptyIndex() {
27517
28933
  type: {},
27518
28934
  group: {}
27519
28935
  },
27520
- signatures: { sources: [], staleTexts: [] }
28936
+ signatures: { sources: [], staleTexts: [] },
28937
+ typeSpellings: emptyTypeSpellingFacts()
27521
28938
  };
27522
28939
  }
27523
28940
  function isWithin(dir, file) {
@@ -27874,7 +29291,7 @@ function bareRawReferences(kind, spec) {
27874
29291
  const out = [];
27875
29292
  const seen = /* @__PURE__ */ new Set();
27876
29293
  const add2 = (position, value, generics = /* @__PURE__ */ new Set()) => {
27877
- if (!value || value.includes("::") || BUILTIN_TYPES.has(value.toLowerCase()) || generics.has(value.toLowerCase())) return;
29294
+ if (!value || value.includes("::") || isTypeVocabulary(value) || generics.has(value.toLowerCase())) return;
27878
29295
  const k = `${position}|${value}`;
27879
29296
  if (seen.has(k)) return;
27880
29297
  seen.add(k);
@@ -28294,6 +29711,49 @@ function bindImport(raw, suppliers, p) {
28294
29711
  }
28295
29712
  };
28296
29713
  }
29714
+ function typePositionText(spec, at) {
29715
+ const [head2, name, member, param] = at.split(".");
29716
+ const named2 = (list, key) => Array.isArray(list) ? list.find((x) => x?.name === key) : void 0;
29717
+ if (head2 === "returns") return spec.returns;
29718
+ if (head2 === "params") return named2(spec.params, name)?.type;
29719
+ if (head2 === "fields") return named2(spec.fields, name)?.type;
29720
+ if (head2 !== "methods") return void 0;
29721
+ const method = named2(spec.methods, name);
29722
+ return member === "returns" ? method?.returns : named2(method?.params, param)?.type;
29723
+ }
29724
+ function writtenSignatureTexts(raws) {
29725
+ const texts = /* @__PURE__ */ new Map();
29726
+ const record = (kind, specId, methods) => {
29727
+ for (const m of methods) {
29728
+ if (m.signatureFrom !== void 0) continue;
29729
+ const derived = deriveMethodSignature(m);
29730
+ if (derived !== void 0) texts.set(`${kind}|${specId}|${m.name}`, derived);
29731
+ }
29732
+ };
29733
+ for (const raw of raws) {
29734
+ for (const intf of raw.index.interfaces) record("interface", intf.id, intf.methods ?? []);
29735
+ for (const type of raw.index.types) record("type", type.id, type.methods ?? []);
29736
+ }
29737
+ return texts;
29738
+ }
29739
+ function canonicalizeRootTypes(raws) {
29740
+ const facts = emptyTypeSpellingFacts();
29741
+ for (const raw of raws) {
29742
+ raw.index.interfaces = raw.index.interfaces.map((intf) => {
29743
+ const read2 = interfaceCanonicalTypes(intf);
29744
+ facts.respellings.push(...read2.respellings);
29745
+ facts.problems.push(...read2.problems);
29746
+ return read2.spec;
29747
+ });
29748
+ raw.index.types = raw.index.types.map((type) => {
29749
+ const read2 = typeCanonicalTypes(type);
29750
+ facts.respellings.push(...read2.respellings);
29751
+ facts.problems.push(...read2.problems);
29752
+ return read2.spec;
29753
+ });
29754
+ }
29755
+ return facts;
29756
+ }
28297
29757
  function bindRootReferences(raw, raws, readables) {
28298
29758
  const bound2 = /* @__PURE__ */ new Map();
28299
29759
  for (const p of raw.pending) {
@@ -28388,7 +29848,8 @@ function specKind(raw) {
28388
29848
  if ("component" in raw && Array.isArray(raw.methods)) return "interface";
28389
29849
  if ("contract" in raw && Array.isArray(raw.methods)) return "implementation";
28390
29850
  if ("kind" in raw && Array.isArray(raw.fields)) return "type";
28391
- if (raw.kind === "signature") return "type";
29851
+ if (raw.kind === "signature" || raw.kind === "enum") return "type";
29852
+ if ("kind" in raw && typeof raw.holds === "string") return "type";
28392
29853
  return void 0;
28393
29854
  }
28394
29855
  function rewriteSpecRefs(raw, remap) {
@@ -28686,12 +30147,12 @@ function pairSteps(before, after) {
28686
30147
  const gapBefore = before.slice(oi + 1, oj).map((step, k) => ({ step, at: oi + 1 + k }));
28687
30148
  const gapAfter = after.slice(ni + 1, nj).map((step, k) => ({ step, at: ni + 1 + k }));
28688
30149
  const similar = commonRun(gapBefore, gapAfter, (x, y) => recognisablySame(x.step, y.step)).map(([x, y]) => [gapBefore[x].at, gapAfter[y].at]);
28689
- const inner = [[oi, ni], ...similar, [oj, nj]];
28690
- for (let k = 0; k < inner.length - 1; k++) {
28691
- const [a0, b0] = inner[k];
28692
- const [a1, b1] = inner[k + 1];
30150
+ const inner2 = [[oi, ni], ...similar, [oj, nj]];
30151
+ for (let k = 0; k < inner2.length - 1; k++) {
30152
+ const [a0, b0] = inner2[k];
30153
+ const [a1, b1] = inner2[k + 1];
28693
30154
  if (a1 - a0 === b1 - b0) for (let d = 1; d < a1 - a0; d++) pairs.push([a0 + d, b0 + d]);
28694
- if (k < inner.length - 2) pairs.push(inner[k + 1]);
30155
+ if (k < inner2.length - 2) pairs.push(inner2[k + 1]);
28695
30156
  }
28696
30157
  if (g < bounds.length - 2) pairs.push(bounds[g + 1]);
28697
30158
  }
@@ -28898,7 +30359,7 @@ function ineffectiveDeltaPaths(delta, merged, stored) {
28898
30359
  var SpecWorkspace = class {
28899
30360
  constructor(rootDir) {
28900
30361
  this.cachedIndex = null;
28901
- this.cachedRecursive = null;
30362
+ this.cachedDepth = null;
28902
30363
  this.cachedSpecDirs = [];
28903
30364
  this.cachedSignature = null;
28904
30365
  this.lastSignatureCheckMs = 0;
@@ -28927,7 +30388,7 @@ var SpecWorkspace = class {
28927
30388
  }
28928
30389
  invalidate() {
28929
30390
  this.cachedIndex = null;
28930
- this.cachedRecursive = null;
30391
+ this.cachedDepth = null;
28931
30392
  this.cachedSpecDirs = [];
28932
30393
  this.cachedConfigFiles = [];
28933
30394
  this.cachedRoots = [];
@@ -28942,8 +30403,8 @@ var SpecWorkspace = class {
28942
30403
  // Scanning
28943
30404
  // -------------------------------------------------------------------------
28944
30405
  scanAll(options) {
28945
- const recursive = options?.recursive ?? true;
28946
- if (this.cachedIndex && this.cachedRecursive === recursive) {
30406
+ const maxDepth = options?.memberDepth ?? Infinity;
30407
+ if (this.cachedIndex && this.cachedDepth === maxDepth) {
28947
30408
  const now = Date.now();
28948
30409
  if (now - this.lastSignatureCheckMs <= SIGNATURE_TTL_MS) return this.cachedIndex;
28949
30410
  this.lastSignatureCheckMs = now;
@@ -28951,10 +30412,9 @@ var SpecWorkspace = class {
28951
30412
  this.invalidate();
28952
30413
  }
28953
30414
  this.loaderIssues = [];
28954
- this.cachedRecursive = recursive;
30415
+ this.cachedDepth = maxDepth;
28955
30416
  this.scanVisitedSpecDirs = [];
28956
30417
  this.scanVisitedConfigFiles = [];
28957
- const maxDepth = typeof recursive === "number" ? recursive : recursive ? Infinity : 0;
28958
30418
  const scan = this.scanFamily(maxDepth);
28959
30419
  this.cachedIndex = scan.index;
28960
30420
  this.cachedRoots = scan.raws.map((raw) => raw.record);
@@ -28995,6 +30455,8 @@ var SpecWorkspace = class {
28995
30455
  raws.forEach((raw) => this.bindAliases(raw, raws));
28996
30456
  const readables = raws.map((raw) => readableOf(raw));
28997
30457
  resolveFamilyTables(raws);
30458
+ const writtenTexts = writtenSignatureTexts(raws);
30459
+ const typeSpellings = canonicalizeRootTypes(raws);
28998
30460
  raws.forEach((raw) => bindRootReferences(raw, raws, readables));
28999
30461
  const index = emptyIndex();
29000
30462
  for (const raw of raws) {
@@ -29009,7 +30471,11 @@ var SpecWorkspace = class {
29009
30471
  const signatures = resolveTree(index.interfaces, index.components, index.types, raws.flatMap((r) => r.record.authoredReferences));
29010
30472
  index.interfaces = signatures.interfaces;
29011
30473
  index.types = signatures.types;
29012
- index.signatures = signatures.facts;
30474
+ index.signatures = {
30475
+ ...signatures.facts,
30476
+ staleTexts: signatures.facts.staleTexts.filter((stale) => writtenTexts.get(`${stale.kind}|${stale.specId}|${stale.method}`) !== stale.stored)
30477
+ };
30478
+ index.typeSpellings = typeSpellings;
29013
30479
  return { index, raws, readables };
29014
30480
  }
29015
30481
  /**
@@ -29155,26 +30621,26 @@ var SpecWorkspace = class {
29155
30621
  decl.kind = "project";
29156
30622
  return;
29157
30623
  }
29158
- const located = this.memberRoot(raw, decl);
29159
- if (located === null) return;
29160
- if ("unavailable" in located) {
30624
+ const located2 = this.memberRoot(raw, decl);
30625
+ if (located2 === null) return;
30626
+ if ("unavailable" in located2) {
29161
30627
  if (asserted === "project" || pinnedAsMember(raw.dir, decl.alias)) {
29162
30628
  decl.kind = "project";
29163
- raw.record.referenced.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason: located.unavailable });
30629
+ raw.record.referenced.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason: located2.unavailable });
29164
30630
  return;
29165
30631
  }
29166
30632
  decl.kind = "part";
29167
- this.memberProblem(raw, "part-unavailable", decl.alias, located.unavailable);
29168
- raw.record.parts.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason: located.unavailable });
30633
+ this.memberProblem(raw, "part-unavailable", decl.alias, located2.unavailable);
30634
+ raw.record.parts.push({ alias: decl.alias, storage: decl.storage, partOf: null, subsystems: [], specIds: [], availability: "unavailable", reason: located2.unavailable });
29169
30635
  return;
29170
30636
  }
29171
- const kind = projectContentAt(located.dir) ? "project" : "part";
30637
+ const kind = projectContentAt(located2.dir) ? "project" : "part";
29172
30638
  decl.kind = kind;
29173
30639
  if (asserted !== void 0 && asserted !== kind) {
29174
30640
  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`"}`);
29175
30641
  }
29176
- if (kind === "part") this.readPart(raw, decl, located.dir, located.commit);
29177
- else if (decl.storage !== "contained") this.recordReferenced(raw, decl, located.dir, located.commit);
30642
+ if (kind === "part") this.readPart(raw, decl, located2.dir, located2.commit);
30643
+ else if (decl.storage !== "contained") this.recordReferenced(raw, decl, located2.dir, located2.commit);
29178
30644
  }
29179
30645
  /**
29180
30646
  * A referenced project member (stage 8): where its root is — the sibling
@@ -30073,7 +31539,8 @@ var SpecWorkspace = class {
30073
31539
  * the owning project.
30074
31540
  */
30075
31541
  prepareInterfaceForWrite(spec) {
30076
- const stored = { ...spec, methods: (spec.methods ?? []).map((m) => storedMethodSignature(m)) };
31542
+ const canonical3 = interfaceCanonicalTypes(spec).spec;
31543
+ const stored = { ...canonical3, methods: (canonical3.methods ?? []).map((m) => storedMethodSignature(m)) };
30077
31544
  return this.relativizeSpec("interface", stored);
30078
31545
  }
30079
31546
  prepareImplementationForWrite(spec, carry = true) {
@@ -30084,9 +31551,14 @@ var SpecWorkspace = class {
30084
31551
  const partDir = this.partHolding(this.getImplementationPath(spec.id, spec.contract))?.part.directory;
30085
31552
  return partDir ? partRelativeFilePaths(relative19, this.rootDir, partDir) : relative19;
30086
31553
  }
30087
- /** A type written in its stored form — every params-bearing method's text derived — relative to the owning project. */
31554
+ /**
31555
+ * A type written in its stored form — every type position canonical (one
31556
+ * that is not canonical written as it stands), every params-bearing method's
31557
+ * text derived from them — relative to the owning project.
31558
+ */
30088
31559
  prepareTypeForWrite(spec) {
30089
- const stored = { ...spec, methods: (spec.methods ?? []).map((m) => storedTypeMethod(m)) };
31560
+ const canonical3 = typeCanonicalTypes(spec).spec;
31561
+ const stored = { ...canonical3, methods: (canonical3.methods ?? []).map((m) => storedTypeMethod(m)) };
30090
31562
  return this.relativizeSpec("type", stored);
30091
31563
  }
30092
31564
  prepareGroupForWrite(spec) {
@@ -30297,7 +31769,7 @@ var SpecWorkspace = class {
30297
31769
  if (!pathExists(p)) return null;
30298
31770
  try {
30299
31771
  const raw = readSpecFile(p);
30300
- return resolveTree([InterfaceSpecSchema.parse(raw)], index.components, index.types).interfaces[0];
31772
+ return resolveTree([interfaceCanonicalTypes(InterfaceSpecSchema.parse(raw)).spec], index.components, index.types).interfaces[0];
30301
31773
  } catch (e) {
30302
31774
  this.loaderIssues.push({
30303
31775
  severity: "error",
@@ -30873,6 +32345,33 @@ var SpecWorkspace = class {
30873
32345
  index.listProjectRoots();
30874
32346
  return this.cachedIndex.signatures;
30875
32347
  }
32348
+ /**
32349
+ * A delta's merged spec with every type position canonical, IN PLACE (an
32350
+ * interface or a type; nothing else holds type positions). Answers the
32351
+ * respellings the write applies: each alias the merge holds — written by the
32352
+ * delta — and each alias the stored file still holds at a position the merge
32353
+ * kept, which the scan recorded and the save rewrites with it. A position
32354
+ * that is not canonical stays as written.
32355
+ */
32356
+ canonicalizeMerged(kind, merged) {
32357
+ if (kind !== "interface" && kind !== "type") return [];
32358
+ const read2 = kind === "interface" ? interfaceCanonicalTypes(merged) : typeCanonicalTypes(merged);
32359
+ Object.assign(merged, read2.spec);
32360
+ const respelled = new Set(read2.respellings.map((r) => r.path));
32361
+ 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);
32362
+ return [...read2.respellings, ...fromFile];
32363
+ }
32364
+ /**
32365
+ * ispec_index.typeSpellingFacts — what the current scan's type
32366
+ * canonicalisation recorded: every stored type position that is an alias of
32367
+ * its canonical spelling, and every one that is not canonical at all. Read
32368
+ * from the cached scan, rescanning first when the tree changed.
32369
+ */
32370
+ typeSpellingFacts() {
32371
+ const index = this;
32372
+ index.listProjectRoots();
32373
+ return this.cachedIndex.typeSpellings;
32374
+ }
30876
32375
  snapshotSpecFiles() {
30877
32376
  const index = this.scanAll();
30878
32377
  const snapshot = /* @__PURE__ */ new Map();
@@ -31483,6 +32982,7 @@ var SpecWorkspace = class {
31483
32982
  const qualifiedDelta = qualifyDeltaRefs(mergeableDelta);
31484
32983
  const mergedResult = mergeDelta(result, qualifiedDelta);
31485
32984
  for (const field of unsetFields) delete mergedResult[field];
32985
+ const respellings = this.canonicalizeMerged(kind, mergedResult);
31486
32986
  deriveMergedTexts(kind, mergedResult);
31487
32987
  const withDefaults = deltaSchema.safeParse(mergedResult);
31488
32988
  if (withDefaults.success) {
@@ -31523,6 +33023,7 @@ var SpecWorkspace = class {
31523
33023
  changes,
31524
33024
  ineffective,
31525
33025
  notices,
33026
+ respellings: [],
31526
33027
  testsToRevisit: [],
31527
33028
  summary: `No change to ${kind} "${id}" \u2014 the delta matches what is stored, so nothing ${dryRun ? "would be" : "was"} written.`
31528
33029
  };
@@ -31539,6 +33040,7 @@ var SpecWorkspace = class {
31539
33040
  changes,
31540
33041
  ineffective,
31541
33042
  notices,
33043
+ respellings,
31542
33044
  testsToRevisit: [],
31543
33045
  ...stripped.length ? { strippedKeys: stripped } : {},
31544
33046
  summary: `Dry run on ${kind} "${id}": ${changes.length} change${changes.length === 1 ? "" : "s"} would be made. Nothing was written.`
@@ -31557,6 +33059,7 @@ var SpecWorkspace = class {
31557
33059
  changes,
31558
33060
  ineffective,
31559
33061
  notices,
33062
+ respellings,
31560
33063
  // The store never searches for tests: it holds the tree and knows
31561
33064
  // nothing about the rule engine that reads code. The GATED write above
31562
33065
  // it fills this in, which is also the only write path a human drives.
@@ -32111,6 +33614,7 @@ function moveChangeReports(edits, dryRun) {
32111
33614
  changes,
32112
33615
  ineffective: [],
32113
33616
  notices: [],
33617
+ respellings: [],
32114
33618
  testsToRevisit: [],
32115
33619
  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"}.`
32116
33620
  }));
@@ -32409,6 +33913,9 @@ function snapshotSpecFiles() {
32409
33913
  function signatureFacts() {
32410
33914
  return current().signatureFacts();
32411
33915
  }
33916
+ function typeSpellingFacts() {
33917
+ return current().typeSpellingFacts();
33918
+ }
32412
33919
  function findLegacySpecFiles() {
32413
33920
  return current().findLegacySpecFiles();
32414
33921
  }
@@ -35230,6 +36737,19 @@ function composeAgentBrief(agentId) {
35230
36737
  ## Project guidance
35231
36738
 
35232
36739
  ${guidance.trim()}
36740
+ `;
36741
+ }
36742
+ const language = implementationLanguage(record);
36743
+ const dialect = language ? typeDialectFor(language) : null;
36744
+ const typeMapping = dialect ? dialect.mappingLines() : void 0;
36745
+ if (typeMapping) {
36746
+ instructions = `${instructions.trimEnd()}
36747
+
36748
+ ## Types in ${language}
36749
+
36750
+ Contracts speak wairon's neutral type grammar; write each type in ${language} as:
36751
+
36752
+ ${typeMapping.map((line) => `- ${line}`).join("\n")}
35233
36753
  `;
35234
36754
  }
35235
36755
  const profile = deriveExecutionProfile(record);
@@ -35243,10 +36763,28 @@ ${guidance.trim()}
35243
36763
  readPaths: record.readPaths,
35244
36764
  instructions,
35245
36765
  variantGuidance: record.variantGuidance || void 0,
36766
+ ...typeMapping ? { typeMapping } : {},
35246
36767
  profile: budget ? profile : void 0,
35247
36768
  budget
35248
36769
  };
35249
36770
  }
36771
+ function implementationLanguage(record) {
36772
+ const owned = new Set(record.ownedPaths);
36773
+ const implementations = loadImplementationSpecs().filter((impl) => implementationSourceFiles(impl).some((file) => owned.has(file)));
36774
+ for (const impl of implementations) {
36775
+ for (const technology of impl.technologies ?? []) {
36776
+ const name = typeof technology === "string" ? technology : technology.name;
36777
+ if (typeDialectFor(name)) return name.toLowerCase();
36778
+ }
36779
+ }
36780
+ for (const impl of implementations) {
36781
+ for (const file of implementationSourceFiles(impl)) {
36782
+ const language = languageOfSourcePath(file);
36783
+ if (language) return language;
36784
+ }
36785
+ }
36786
+ return void 0;
36787
+ }
35250
36788
  function memberHop(agentId) {
35251
36789
  const at = agentId.indexOf("::");
35252
36790
  if (at < 0) return null;
@@ -35824,6 +37362,52 @@ function repairSignatures(apply3) {
35824
37362
  return planned;
35825
37363
  }
35826
37364
 
37365
+ // src/core/type-spelling-repair.ts
37366
+ function repairTypeSpellings(apply3) {
37367
+ const facts = typeSpellingFacts();
37368
+ const held = new Set(signatureFacts().sources.filter((f) => f.outcome === "restated" && f.differs).map((f) => f.interfaceId));
37369
+ const repairs = /* @__PURE__ */ new Map();
37370
+ const repairOf = (specId, kind) => {
37371
+ const key = `${kind}:${specId}`;
37372
+ let repair = repairs.get(key);
37373
+ if (!repair) {
37374
+ repair = { specId, kind, rewritten: [], proposals: [], authorNeeded: [] };
37375
+ repairs.set(key, repair);
37376
+ }
37377
+ return repair;
37378
+ };
37379
+ const own = (specId) => !specId.includes("::");
37380
+ for (const respelling of facts.respellings) {
37381
+ if (!own(respelling.specId)) continue;
37382
+ if (respelling.kind === "interface" && held.has(respelling.specId)) continue;
37383
+ repairOf(respelling.specId, respelling.kind).rewritten.push(respelling);
37384
+ }
37385
+ for (const problem of facts.problems) {
37386
+ if (problem.specId === void 0 || problem.kind === void 0 || !own(problem.specId)) continue;
37387
+ const repair = repairOf(problem.specId, problem.kind);
37388
+ const proposal = typeProblemIntProposal(problem);
37389
+ if (proposal) repair.proposals.push(proposal);
37390
+ else repair.authorNeeded.push(problem);
37391
+ }
37392
+ const planned = [...repairs.values()];
37393
+ const rewriting = planned.filter((repair) => repair.rewritten.length > 0);
37394
+ if (apply3 && rewriting.length > 0) {
37395
+ const interfaces = new Map(loadInterfaceSpecs().map((i) => [i.id, i]));
37396
+ const types = new Map(loadTypeSpecs().map((t) => [t.id, t]));
37397
+ for (const repair of rewriting) {
37398
+ if (repair.kind === "interface") {
37399
+ const intf = interfaces.get(repair.specId);
37400
+ if (intf) saveInterfaceSpec(intf);
37401
+ continue;
37402
+ }
37403
+ const type = types.get(repair.specId);
37404
+ if (type) saveSpec("type", type);
37405
+ }
37406
+ invalidateSpecCache();
37407
+ }
37408
+ return planned;
37409
+ }
37410
+
35827
37411
  // src/core/approval.ts
35828
37412
  var crypto7 = __toESM(require("crypto"));
35829
37413
  var path35 = __toESM(require("path"));
@@ -36342,7 +37926,7 @@ function parentPinState(parent) {
36342
37926
  }
36343
37927
  function getStatusReport(options = {}, decor) {
36344
37928
  const mark = fillDecor(decor);
36345
- scanAllSpecs({ recursive: options.recursive ?? true });
37929
+ scanAllSpecs({ memberDepth: options.memberDepth });
36346
37930
  const system = loadSystemSpec();
36347
37931
  const loaderErrors = getLoaderIssues();
36348
37932
  if (loaderErrors.length > 0) {
@@ -36532,6 +38116,11 @@ function getStatusReport(options = {}, decor) {
36532
38116
  }
36533
38117
 
36534
38118
  // src/core/index.ts
38119
+ function resolveSignatures(interfaces, components, types) {
38120
+ const canonicalInterfaces = interfaces.map((intf) => interfaceCanonicalTypes(intf).spec);
38121
+ const canonicalTypes = types.map((type) => typeCanonicalTypes(type).spec);
38122
+ return resolveTree(canonicalInterfaces, components, canonicalTypes);
38123
+ }
36535
38124
  function resolveDomains2() {
36536
38125
  return resolveDomains();
36537
38126
  }
@@ -36847,12 +38436,14 @@ function noticesFrom(verdict) {
36847
38436
  ...verdict.notices.map((n) => `${n.code} (notice): ${n.message}`)
36848
38437
  ];
36849
38438
  }
36850
- function componentCandidateGate(options = candidateOptions(), storedOwner) {
38439
+ function componentCandidateGate(options = candidateOptions(), storedOwner, stored) {
36851
38440
  return {
36852
38441
  gate: (kind, merged) => {
36853
38442
  refuseUnknownOwner(kind, merged, storedOwner);
36854
38443
  const restated = kind === "interface" ? restatedSources(merged.methods) : [];
36855
38444
  if (restated.length) throw new Error(`${restatedSourceRefusal(String(merged.id), restated)} Nothing was written.`);
38445
+ const written = deltaWrittenTypeProblems(kind, merged, stored ?? null);
38446
+ if (written.length) throw new Error(`${typeProblemRefusal(kind, String(merged.id), written)} Nothing was written.`);
36856
38447
  if (kind !== "component") return;
36857
38448
  const verdict = validateComponentCandidate(merged, options);
36858
38449
  if (verdict.errors.length) throw new Error(formatCandidateRefusal(verdict));
@@ -36860,6 +38451,22 @@ function componentCandidateGate(options = candidateOptions(), storedOwner) {
36860
38451
  }
36861
38452
  };
36862
38453
  }
38454
+ function canonicalTypesOf(kind, spec) {
38455
+ if (!spec || typeof spec !== "object") return null;
38456
+ if (kind === "interface") return interfaceCanonicalTypes(spec);
38457
+ if (kind === "type") return typeCanonicalTypes(spec);
38458
+ return null;
38459
+ }
38460
+ function deltaWrittenTypeProblems(kind, merged, stored) {
38461
+ const now = canonicalTypesOf(kind, merged)?.problems ?? [];
38462
+ if (now.length === 0) return [];
38463
+ const before = new Set((canonicalTypesOf(kind, stored)?.problems ?? []).map((p) => `${p.path}|${p.written}`));
38464
+ return now.filter((p) => !before.has(`${p.path}|${p.written}`));
38465
+ }
38466
+ function typeProblemRefusal(kind, id, problems) {
38467
+ const lines = problems.map((p) => `${p.code} at ${p.path}: ${p.detail}${p.replacement ? ` \u2014 write ${p.replacement} instead` : ""}.`);
38468
+ 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.`;
38469
+ }
36863
38470
  function restatedSources(methods) {
36864
38471
  if (!Array.isArray(methods)) return [];
36865
38472
  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));
@@ -36965,6 +38572,11 @@ function applyRestatement(restatement, loaded, parent) {
36965
38572
  `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.`
36966
38573
  );
36967
38574
  }
38575
+ const types = canonicalTypesOf(restatement.kind, candidate);
38576
+ if (types && types.problems.length) {
38577
+ return refused(restatement, replacedExisting, `${typeProblemRefusal(restatement.kind, String(candidate.id), types.problems)} Nothing was written.`);
38578
+ }
38579
+ if (types) Object.assign(candidate, types.spec);
36968
38580
  const derivedNotices = deriveStatedTexts(restatement.kind, candidate);
36969
38581
  const labelErrors = resolveLabelsOf(restatement.kind, candidate);
36970
38582
  if (labelErrors.length) {
@@ -36978,7 +38590,8 @@ function applyRestatement(restatement, loaded, parent) {
36978
38590
  ...status.status ? { status: status.status } : {},
36979
38591
  replacedExisting,
36980
38592
  notices: [...existing ? rewriteNotices(restatement.kind, existing, candidate, carried, cleared) : [], ...derivedNotices],
36981
- changedMethods: changedMethodsOf(restatement.kind, existing, candidate)
38593
+ changedMethods: changedMethodsOf(restatement.kind, existing, candidate),
38594
+ respellings: types?.respellings ?? []
36982
38595
  };
36983
38596
  }
36984
38597
  var SPEC_SCHEMA = {
@@ -37017,7 +38630,7 @@ function parseCandidate(kind, candidate) {
37017
38630
  };
37018
38631
  }
37019
38632
  function refused(restatement, replacedExisting, refusal2) {
37020
- return { spec: restatement.spec, refusal: refusal2, replacedExisting, notices: [], changedMethods: [] };
38633
+ return { spec: restatement.spec, refusal: refusal2, replacedExisting, notices: [], changedMethods: [], respellings: [] };
37021
38634
  }
37022
38635
  function statusForCreate(restatement, existing) {
37023
38636
  if (!STATUSED_KINDS.has(restatement.kind)) return {};
@@ -37162,6 +38775,7 @@ function writeSpec(restatement) {
37162
38775
  replacedExisting: application.replacedExisting,
37163
38776
  ...application.status ? { status: application.status } : {},
37164
38777
  notices: [...gateNotices, ...persisted.notices, ...application.notices],
38778
+ respellings: application.respellings,
37165
38779
  testsToRevisit
37166
38780
  };
37167
38781
  }
@@ -37217,7 +38831,7 @@ function updateSpecGated(kind, id, delta, dryRun) {
37217
38831
  const bound2 = candidateOptions();
37218
38832
  const testRoots = bound2.rules?.conformance?.testRoots ?? [];
37219
38833
  const stored = loadSpec(kind, id);
37220
- const gate = componentCandidateGate(bound2, stored?.subsystem);
38834
+ const gate = componentCandidateGate(bound2, stored?.subsystem, stored);
37221
38835
  const report2 = updateSpec(kind, id, delta, gate, dryRun);
37222
38836
  const changed = changedMethods(report2);
37223
38837
  if (changed.length > 0 && testRoots.length > 0) {
@@ -40641,7 +42255,7 @@ function errText(message) {
40641
42255
  function errMessage(e) {
40642
42256
  return errText(e instanceof Error ? e.message : String(e));
40643
42257
  }
40644
- var 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.`;
42258
+ var 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.`;
40645
42259
  function structured(content, data) {
40646
42260
  return { content: [{ type: "text", text: content }], structuredContent: data };
40647
42261
  }
@@ -40736,6 +42350,7 @@ function renderChangeReport(report2) {
40736
42350
  if (report2.strippedKeys?.length) {
40737
42351
  lines.push("", `DROPPED FROM THE STORED FILE (${report2.dryRun ? "would be " : ""}removed \u2014 the schema does not know them):`, ...report2.strippedKeys.map((k) => `- ${k}`));
40738
42352
  }
42353
+ lines.push(...renderRespellings(report2.respellings));
40739
42354
  lines.push(...renderTestsToRevisit(report2.testsToRevisit));
40740
42355
  return lines.join("\n");
40741
42356
  }
@@ -40763,6 +42378,17 @@ function renderTestsToRevisit(entries) {
40763
42378
  }
40764
42379
  return lines;
40765
42380
  }
42381
+ var typeRespellingOutput = import_zod11.z.object({
42382
+ specId: import_zod11.z.string().describe("The interface or type holding the position."),
42383
+ kind: import_zod11.z.enum(["interface", "type"]).describe("interface | type."),
42384
+ path: import_zod11.z.string().describe("Where in the spec: methods.save.params.key, methods.save.returns, fields.createdAt, params.listener."),
42385
+ written: import_zod11.z.string().describe("The text the input (or the stored file) held."),
42386
+ stored: import_zod11.z.string().describe("The canonical spelling, which is what is stored and shown.")
42387
+ });
42388
+ function renderRespellings(entries) {
42389
+ if (!entries.length) return [];
42390
+ return ["", "RESPELLED (stored canonical):", ...entries.map((r) => `- ${r.path}: "${r.written}" \u2192 "${r.stored}"`)];
42391
+ }
40766
42392
  var specWriteReceiptOutput = {
40767
42393
  kind: import_zod11.z.enum(SPEC_KINDS).describe("The spec kind written."),
40768
42394
  id: import_zod11.z.string().describe('The id the spec is stored under; "system" for the L0 singleton.'),
@@ -40776,6 +42402,9 @@ var specWriteReceiptOutput = {
40776
42402
  notices: import_zod11.z.array(import_zod11.z.string()).describe(
40777
42403
  "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."
40778
42404
  ),
42405
+ respellings: import_zod11.z.array(typeRespellingOutput).describe(
42406
+ "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."
42407
+ ),
40779
42408
  testsToRevisit: testsToRevisitOutput,
40780
42409
  ...staleServerOutput
40781
42410
  };
@@ -40784,8 +42413,10 @@ function writeReceipt(sentence, receipt) {
40784
42413
 
40785
42414
  NOTICE:
40786
42415
  - ${receipt.notices.join("\n- ")}` : "";
42416
+ const respelledBlock = renderRespellings(receipt.respellings).join("\n");
40787
42417
  const testsBlock = renderTestsToRevisit(receipt.testsToRevisit).join("\n");
40788
- return structured(`${sentence}${noticeBlock}${testsBlock ? `
42418
+ return structured(`${sentence}${noticeBlock}${respelledBlock ? `
42419
+ ${respelledBlock}` : ""}${testsBlock ? `
40789
42420
  ${testsBlock}` : ""}`, receipt);
40790
42421
  }
40791
42422
  var specDeletionOutput = {
@@ -40817,6 +42448,9 @@ var specChangeReportOutput = {
40817
42448
  "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."
40818
42449
  ),
40819
42450
  notices: import_zod11.z.array(import_zod11.z.string()).describe("Store placement notices, gate warnings and delta notices."),
42451
+ respellings: import_zod11.z.array(typeRespellingOutput).describe(
42452
+ "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."
42453
+ ),
40820
42454
  summary: import_zod11.z.string().describe("One line for people."),
40821
42455
  testsToRevisit: testsToRevisitOutput,
40822
42456
  strippedKeys: import_zod11.z.array(import_zod11.z.string()).optional().describe(
@@ -41016,46 +42650,46 @@ function schemaShape(schema, seen = /* @__PURE__ */ new Map()) {
41016
42650
  const known = seen.get(schema);
41017
42651
  if (known !== void 0) return { ref: known };
41018
42652
  seen.set(schema, seen.size);
41019
- const inner = (s) => schemaShape(s, seen);
42653
+ const inner2 = (s) => schemaShape(s, seen);
41020
42654
  const kind = String(def.typeName);
41021
42655
  switch (kind) {
41022
42656
  case "ZodObject": {
41023
42657
  const shape = typeof def.shape === "function" ? def.shape() : def.shape;
41024
42658
  return {
41025
42659
  kind,
41026
- keys: Object.keys(shape).sort().map((key) => [key, inner(shape[key])]),
42660
+ keys: Object.keys(shape).sort().map((key) => [key, inner2(shape[key])]),
41027
42661
  unknownKeys: def.unknownKeys,
41028
- catchall: inner(def.catchall)
42662
+ catchall: inner2(def.catchall)
41029
42663
  };
41030
42664
  }
41031
42665
  case "ZodArray":
41032
- return { kind, of: inner(def.type), min: def.minLength?.value, max: def.maxLength?.value };
42666
+ return { kind, of: inner2(def.type), min: def.minLength?.value, max: def.maxLength?.value };
41033
42667
  case "ZodOptional":
41034
42668
  case "ZodNullable":
41035
42669
  case "ZodReadonly":
41036
42670
  case "ZodBranded":
41037
42671
  case "ZodCatch":
41038
- return { kind, of: inner(def.innerType ?? def.type) };
42672
+ return { kind, of: inner2(def.innerType ?? def.type) };
41039
42673
  case "ZodDefault":
41040
- return { kind, of: inner(def.innerType), default: JSON.stringify(def.defaultValue?.()) };
42674
+ return { kind, of: inner2(def.innerType), default: JSON.stringify(def.defaultValue?.()) };
41041
42675
  case "ZodEffects":
41042
- return { kind, effect: def.effect?.type, of: inner(def.schema) };
42676
+ return { kind, effect: def.effect?.type, of: inner2(def.schema) };
41043
42677
  case "ZodLazy":
41044
- return { kind, of: inner(def.getter()) };
42678
+ return { kind, of: inner2(def.getter()) };
41045
42679
  case "ZodUnion":
41046
42680
  case "ZodDiscriminatedUnion": {
41047
42681
  const options = def.options instanceof Map ? [...def.options.values()] : def.options;
41048
- return { kind, discriminator: def.discriminator, of: options.map(inner) };
42682
+ return { kind, discriminator: def.discriminator, of: options.map(inner2) };
41049
42683
  }
41050
42684
  case "ZodIntersection":
41051
- return { kind, left: inner(def.left), right: inner(def.right) };
42685
+ return { kind, left: inner2(def.left), right: inner2(def.right) };
41052
42686
  case "ZodTuple":
41053
- return { kind, of: def.items.map(inner), rest: inner(def.rest) };
42687
+ return { kind, of: def.items.map(inner2), rest: inner2(def.rest) };
41054
42688
  case "ZodRecord":
41055
42689
  case "ZodMap":
41056
- return { kind, key: inner(def.keyType), value: inner(def.valueType) };
42690
+ return { kind, key: inner2(def.keyType), value: inner2(def.valueType) };
41057
42691
  case "ZodSet":
41058
- return { kind, of: inner(def.valueType) };
42692
+ return { kind, of: inner2(def.valueType) };
41059
42693
  case "ZodEnum":
41060
42694
  return { kind, values: def.values };
41061
42695
  case "ZodNativeEnum":
@@ -41063,7 +42697,7 @@ function schemaShape(schema, seen = /* @__PURE__ */ new Map()) {
41063
42697
  case "ZodLiteral":
41064
42698
  return { kind, value: def.value };
41065
42699
  case "ZodPipeline":
41066
- return { kind, in: inner(def.in), out: inner(def.out) };
42700
+ return { kind, in: inner2(def.in), out: inner2(def.out) };
41067
42701
  default:
41068
42702
  return { kind, checks: def.checks?.map((c) => [c.kind, c.value ?? null]) ?? [] };
41069
42703
  }
@@ -41791,7 +43425,7 @@ ${renderChangeReport(report2)}`,
41791
43425
  type: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The parameter's type")),
41792
43426
  description: import_zod11.z.string().optional(),
41793
43427
  optional: import_zod11.z.boolean().optional().describe(
41794
- '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.'
43428
+ '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).'
41795
43429
  )
41796
43430
  }).strict();
41797
43431
  const interfaceMethodShape = {
@@ -41806,7 +43440,7 @@ ${renderChangeReport(report2)}`,
41806
43440
  ),
41807
43441
  params: import_zod11.z.array(methodParamItem).optional().describe("Structured parameters \u2014 authoritative for type checking, and the signature text is derived from them. Strongly preferred."),
41808
43442
  guarantees: import_zod11.z.array(import_zod11.z.string().min(1)).optional().describe("Semantic guarantees the method promises (combinable); any guarantee a narrative step asserts must be declared here. Builtin tokens: idempotent | atomic | transactional | exactly-once; extension packs may declare more (any other token is UNKNOWN_GUARANTEE)"),
41809
- effect: import_zod11.z.enum(["read", "write"]).optional().describe("State-effect direction on the component's held state \u2014 required on a durable Store's contract methods so the durability round-trip rule can pair writes with hydration read-backs"),
43443
+ effect: import_zod11.z.enum(["read", "write", "lifecycle"]).optional().describe("What the method does to the component's held state: read observes it; write modifies an entity's domain fields; lifecycle creates, destroys, or (un)registers an entity's existence or membership without modifying its fields, and calls only read and lifecycle methods (LIFECYCLE_CALLS_WRITE). Required on a durable Store's contract methods so the durability round-trip rule can pair mutations with hydration read-backs; a Supervisor may call a data component it does not own only through read and lifecycle methods (SUPERVISOR_WRITE_SHORTCUT)"),
41810
43444
  invokedBy: import_zod11.z.object({
41811
43445
  kind: import_zod11.z.enum(["runtime", "external", "sibling-subsystem"]).describe("Who owns the out-of-graph invocation: runtime (timer/signal/shutdown hook), external (a system outside this project), sibling-subsystem (a modeled sibling whose edge is not narrated here)"),
41812
43446
  caller: import_zod11.z.string().optional().describe("WHO invokes it and when, as reviewable prose \u2014 missing or placeholder-thin prose is INVOKED_BY_UNDESCRIBED")
@@ -42040,7 +43674,7 @@ ${renderChangeReport(report2)}`,
42040
43674
  }
42041
43675
  );
42042
43676
  const typeInput = {
42043
- 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"),
43677
+ 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"),
42044
43678
  id: import_zod11.z.string().describe("Lowercase identifier"),
42045
43679
  name: import_zod11.z.string().describe("Human-readable name"),
42046
43680
  description: import_zod11.z.string().optional(),
@@ -42074,18 +43708,23 @@ ${renderChangeReport(report2)}`,
42074
43708
  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)"),
42075
43709
  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'),
42076
43710
  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)"),
42077
- returns: import_zod11.z.string().optional().describe(TYPE_REF_GRAMMAR("kind signature only, and required there: the function type's one output"))
43711
+ returns: import_zod11.z.string().optional().describe(TYPE_REF_GRAMMAR("kind signature only, and required there: the function type's one output")),
43712
+ values: import_zod11.z.array(import_zod11.z.object({
43713
+ name: import_zod11.z.string().min(1).describe(`The value's name, exactly as data carries it ("stable", "local_only")`),
43714
+ description: import_zod11.z.string().optional().describe("What the value means")
43715
+ }).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"),
43716
+ 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?")')
42078
43717
  };
42079
43718
  const typeInputFields = Object.keys(typeInput);
42080
43719
  reg(
42081
43720
  server,
42082
43721
  "sdd_add_type",
42083
43722
  {
42084
- 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.",
43723
+ 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.",
42085
43724
  inputSchema: typeInput,
42086
43725
  outputSchema: specWriteReceiptOutput
42087
43726
  },
42088
- ({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol, params, returns }) => {
43727
+ ({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol, params, returns, values, holds }) => {
42089
43728
  try {
42090
43729
  const spec = {
42091
43730
  kind,
@@ -42111,7 +43750,9 @@ ${renderChangeReport(report2)}`,
42111
43750
  ...sourcePath ? { sourcePath } : {},
42112
43751
  ...symbol ? { symbol } : {},
42113
43752
  ...params ? { params } : {},
42114
- ...returns ? { returns } : {}
43753
+ ...returns ? { returns } : {},
43754
+ ...values ? { values } : {},
43755
+ ...holds ? { holds } : {}
42115
43756
  };
42116
43757
  const receipt = writeSpec({ kind: "type", spec, fields: typeInputFields });
42117
43758
  return writeReceipt(
@@ -42144,7 +43785,8 @@ ${renderChangeReport(report2)}`,
42144
43785
  rules: config.rules,
42145
43786
  projectType: config.projectType,
42146
43787
  scopeSubsystem: subsystem,
42147
- recursive: recursive ?? true,
43788
+ // The tool's flag at the edge: recursive false is a member depth of 0.
43789
+ ...recursive === false ? { memberDepth: 0 } : {},
42148
43790
  family
42149
43791
  };
42150
43792
  const result = selectsFamily(options2, declaredMembers(config).length > 0) ? validateFamily(options2) : validateProject(options2);
@@ -42279,7 +43921,7 @@ ${testsBlock}` : ""}`, deletion);
42279
43921
  server,
42280
43922
  "sdd_update_spec",
42281
43923
  {
42282
- 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.",
43924
+ 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.",
42283
43925
  inputSchema: {
42284
43926
  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})"),
42285
43927
  id: import_zod11.z.string().describe("The ID of the spec to update (namespaced if needed)"),
@@ -42308,18 +43950,19 @@ ${testsBlock}` : ""}`, deletion);
42308
43950
  }
42309
43951
  },
42310
43952
  ({ subsystem, recursive }) => {
43953
+ const memberDepth = recursive === false ? 0 : void 0;
42311
43954
  const banner = pendingBanner().map((line) => `\u26A0 TRANSACTION PENDING: ${line}
42312
43955
  `).join("");
42313
43956
  try {
42314
43957
  let approvals;
42315
43958
  try {
42316
- approvals = familyApprovals(recursive === false ? 0 : void 0);
43959
+ approvals = familyApprovals(memberDepth);
42317
43960
  } catch {
42318
43961
  approvals = void 0;
42319
43962
  }
42320
43963
  const report2 = getStatusReport({
42321
43964
  subsystem,
42322
- recursive: recursive ?? true,
43965
+ memberDepth,
42323
43966
  approvals
42324
43967
  });
42325
43968
  const family = statusFamilyContext();
@@ -42414,7 +44057,6 @@ function loadProjectConfig3() {
42414
44057
  AgentTemplateSchema,
42415
44058
  BACKEND_COMMANDS,
42416
44059
  BUILTIN_PROFILES,
42417
- BUILTIN_TYPES,
42418
44060
  BoundaryItemSchema,
42419
44061
  BuiltinTargetConfigSchema,
42420
44062
  BuiltinTargetSchema,
@@ -42448,6 +44090,7 @@ function loadProjectConfig3() {
42448
44090
  DurabilitySchema,
42449
44091
  EXTERNAL_ALIAS_RE,
42450
44092
  EndpointSchema,
44093
+ EnumValueSchema,
42451
44094
  EventBindingSchema,
42452
44095
  ExtDataSchema,
42453
44096
  ExternalDeclarationSchema,
@@ -42462,6 +44105,7 @@ function loadProjectConfig3() {
42462
44105
  GeminiExporter,
42463
44106
  GroupSpecSchema,
42464
44107
  GuaranteeSchema,
44108
+ HOLDABLE_PRIMITIVES,
42465
44109
  HttpMethodSchema,
42466
44110
  ImplementationSpecSchema,
42467
44111
  InterfaceSpecSchema,
@@ -42481,6 +44125,7 @@ function loadProjectConfig3() {
42481
44125
  NarrativeStepTypeSchema,
42482
44126
  OutputTargetSchema,
42483
44127
  PATTERN_TYPES,
44128
+ PRIMITIVE_TYPES,
42484
44129
  PROJECT_ID_RE,
42485
44130
  PROJECT_KINDS,
42486
44131
  PUBLIC_NAME_RE,
@@ -42568,6 +44213,7 @@ function loadProjectConfig3() {
42568
44213
  buildRuleContext,
42569
44214
  callSitesOf,
42570
44215
  canonicalTypeRef,
44216
+ canonicalTypeText,
42571
44217
  captureApprovedSpecs,
42572
44218
  checkDeclarativePack,
42573
44219
  clearLoaderIssues,
@@ -42612,6 +44258,7 @@ function loadProjectConfig3() {
42612
44258
  detectDomainCandidates,
42613
44259
  diagnoseProjectPacks,
42614
44260
  diagramSetIndex,
44261
+ dialectOf,
42615
44262
  diffAgainstApproval,
42616
44263
  diffSize,
42617
44264
  discoverPacks,
@@ -42619,6 +44266,7 @@ function loadProjectConfig3() {
42619
44266
  effectiveDetail,
42620
44267
  effectiveProjectId,
42621
44268
  emptyExtensions,
44269
+ emptyTypeSpellingFacts,
42622
44270
  ensureDir,
42623
44271
  ensureProjectInitialized,
42624
44272
  excerptParent,
@@ -42665,6 +44313,7 @@ function loadProjectConfig3() {
42665
44313
  importSpecTree,
42666
44314
  injectGuide,
42667
44315
  installPackFromDirectory,
44316
+ interfaceCanonicalTypes,
42668
44317
  interfaceGenericParameters,
42669
44318
  internalizeMember,
42670
44319
  isDraftSubsystem,
@@ -42676,9 +44325,11 @@ function loadProjectConfig3() {
42676
44325
  isProjectInitialized,
42677
44326
  isProvidedBy,
42678
44327
  isRetired,
44328
+ isTypeVocabulary,
42679
44329
  judgesCode,
42680
44330
  keyIn,
42681
44331
  landReference,
44332
+ languageOfSourcePath,
42682
44333
  listDirectChainedSubprojects,
42683
44334
  listFiles,
42684
44335
  listFilesRecursive,
@@ -42721,6 +44372,7 @@ function loadProjectConfig3() {
42721
44372
  moveMethods,
42722
44373
  moveMountToMembers,
42723
44374
  nameKey,
44375
+ namesAnInteger,
42724
44376
  narrativeStepForeignFields,
42725
44377
  normalizeReferences,
42726
44378
  ownerOf,
@@ -42732,6 +44384,8 @@ function loadProjectConfig3() {
42732
44384
  parseDeclarativePack,
42733
44385
  parseDeclaredCall,
42734
44386
  parseMemberSource,
44387
+ parseTypeExpression,
44388
+ parseTypePosition,
42735
44389
  parseYaml,
42736
44390
  passesIntentFloor,
42737
44391
  pathExists,
@@ -42745,6 +44399,7 @@ function loadProjectConfig3() {
42745
44399
  provisionProject,
42746
44400
  qualifiedTypeId,
42747
44401
  rangeProblem,
44402
+ readCodeAnnotation,
42748
44403
  readFileOrNull,
42749
44404
  readJsonFile,
42750
44405
  readLockRecord,
@@ -42767,6 +44422,7 @@ function loadProjectConfig3() {
42767
44422
  renderDiagram,
42768
44423
  repairForeignStepFields,
42769
44424
  repairSignatures,
44425
+ repairTypeSpellings,
42770
44426
  repointExternal,
42771
44427
  requiredPolicies,
42772
44428
  resolveAgentTopology,
@@ -42814,9 +44470,15 @@ function loadProjectConfig3() {
42814
44470
  technologyTokens,
42815
44471
  toMarkdown,
42816
44472
  typeBindingOf,
44473
+ typeCanonicalTypes,
44474
+ typeDialectFor,
42817
44475
  typeGenericParameters,
44476
+ typeIsMany,
42818
44477
  typeMatchesRef,
44478
+ typeNamedRefs,
44479
+ typeProblemIntProposal,
42819
44480
  typeSourceFiles,
44481
+ typeSpellingFacts,
42820
44482
  uninstallPack,
42821
44483
  updateMember,
42822
44484
  updateSpec,
@@ -42831,6 +44493,7 @@ function loadProjectConfig3() {
42831
44493
  writeReachDenials,
42832
44494
  writeReachPermits,
42833
44495
  writeRootGuideDelegator,
42834
- writeYamlFile
44496
+ writeYamlFile,
44497
+ writtenTypeRefs
42835
44498
  });
42836
44499
  //# sourceMappingURL=index.js.map