@wairon/cli 5.1.1-dev.38 → 5.1.1-dev.39

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
@@ -436,7 +436,7 @@ function activeTargetTypes(config) {
436
436
  const targets = config.targets;
437
437
  return targets.filter((t) => typeof t === "string" || t.enabled !== false).map((t) => typeof t === "string" ? t : t.type);
438
438
  }
439
- var import_zod3, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, PackSelectionSchema, ProfileSelectionSubjectSchema, ProjectProfileSelectionSchema, ProjectConfigSchema;
439
+ var import_zod3, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, ConformanceRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, PackSelectionSchema, ProfileSelectionSubjectSchema, ProjectProfileSelectionSchema, ProjectConfigSchema;
440
440
  var init_project = __esm({
441
441
  "src/models/project.ts"() {
442
442
  "use strict";
@@ -533,6 +533,30 @@ var init_project = __esm({
533
533
  */
534
534
  maxCognitiveLevel: import_zod3.z.string().optional()
535
535
  });
536
+ ConformanceRuleConfigSchema = import_zod3.z.object({
537
+ /**
538
+ * Project-relative paths holding this project's own source code; a
539
+ * directory is walked recursively, a file names itself. Absolute or
540
+ * parent-escaping entries are refused by containment, exactly as a
541
+ * sourcePath is.
542
+ */
543
+ sourceRoots: import_zod3.z.array(import_zod3.z.string()).optional(),
544
+ /**
545
+ * Paths inside a source root that are not this project's code to claim —
546
+ * vendored libraries, generated output, a chained subproject's own
547
+ * directory (the child claims its files in its own run). A file at, or
548
+ * under, one of these is never walked, so it is neither reported nor
549
+ * carried as debt.
550
+ */
551
+ exclude: import_zod3.z.array(import_zod3.z.string()).optional(),
552
+ /**
553
+ * The files under the source roots that no spec names yet, frozen. A listed
554
+ * file is not reported; an unlisted one is UNCLAIMED_SOURCE_FILE; an entry
555
+ * that is now claimed, proven a barrel, or no longer found is
556
+ * STALE_UNCLAIMED_ENTRY.
557
+ */
558
+ unclaimed: import_zod3.z.array(import_zod3.z.string()).optional()
559
+ });
536
560
  DesignDepthSchema = import_zod3.z.enum(["components", "interfaces", "implementations", "narratives"]);
537
561
  RulesConfigSchema = import_zod3.z.object({
538
562
  /**
@@ -583,6 +607,8 @@ var init_project = __esm({
583
607
  documentation: DocumentationRuleConfigSchema.optional(),
584
608
  /** Dynamic structural complexity caps (method limit, step limit, dependency limit) */
585
609
  complexity: ComplexityRuleConfigSchema.optional(),
610
+ /** Where this project's own source lives and what of it no spec claims yet (see ConformanceRuleConfigSchema) */
611
+ conformance: ConformanceRuleConfigSchema.optional(),
586
612
  /** Project-default design depth (see DesignDepthSchema); subsystems may override. */
587
613
  designDepth: DesignDepthSchema.optional()
588
614
  });
@@ -1113,7 +1139,7 @@ var init_defaults = __esm({
1113
1139
  copilot: ".github/prompts",
1114
1140
  codex: ".codex/agents"
1115
1141
  };
1116
- WAIRON_VERSION = "5.1.1-dev.38";
1142
+ WAIRON_VERSION = "5.1.1-dev.39";
1117
1143
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
1118
1144
  ARCHITECT_AGENT_ID = "agent-architect";
1119
1145
  ARCHITECT_TEMPLATE_ID = "architect";
@@ -1515,6 +1541,15 @@ function implementationSourceFiles(impl) {
1515
1541
  for (const method of impl.methods ?? []) add(method.sourcePath);
1516
1542
  return files;
1517
1543
  }
1544
+ function typeSourceFiles(type) {
1545
+ const files = [];
1546
+ const add = (file) => {
1547
+ if (file && !files.includes(file)) files.push(file);
1548
+ };
1549
+ add(type.sourcePath);
1550
+ for (const method of type.methods ?? []) add(method.sourcePath);
1551
+ return files;
1552
+ }
1518
1553
  var import_zod8, SpecIdSchema, SpecStatusSchema, BoundaryItemSchema, RequirementItemSchema, DatabaseSpecSchema, DiagramConfigSchema, SURFACE_AUDIENCES, SurfaceAudienceSchema, SystemPublicInterfaceSchema, SystemSpecSchema, PublicInterfaceTypeSchema, PublicInterfaceSchema, TrustedLinkSchema, LintAllowSchema, LintConfigSchema, ExtDataSchema, LifecycleEntrypointSchema, SubsystemSpecSchema, ComponentTypeSchema, PATTERN_TYPES, RETIRED_STEREOTYPES, PortalTypeSchema, DispatchBindingSchema, DurabilitySchema, DependencyClassSchema, PatternRefSchema, EventBindingSchema, ExternalLinkTypeSchema, ExternalLinkSchema, PortalAuthSchemeSchema, PortalAuthSchema, ComponentSpecSchema, BLOCK_NOUNS, HttpMethodSchema, TransportSchema, EndpointSchema, SEMANTIC_GUARANTEES, GuaranteeSchema, MethodParamSchema, FindingDeclarationSchema, MethodSignatureSchema, INTENT_FLOOR_MIN_CHARS, InterfaceSpecSchema, NarrativeStepTypeSchema, LoopKindSchema, SwitchCaseSchema, CatchClauseSchema, ParallelBranchSchema, NarrativeStepSchema, NarrativeDetailSchema, ConformanceTierSchema, MethodImplementationSchema, ImplementationSpecSchema, TypeKindSchema, TypeFieldSchema, TypeMethodSchema, InvariantSchema, TypeSpecSchema, SurfaceOriginSchema, SurfaceTypeDefSchema, SurfaceContractEntrySchema, SurfaceSnapshotSchema, NamedOpenApiSpecSchema, GroupSpecSchema;
1519
1554
  var init_specs = __esm({
1520
1555
  "src/models/specs.ts"() {
@@ -2141,7 +2176,22 @@ var init_specs = __esm({
2141
2176
  name: import_zod8.z.string(),
2142
2177
  signature: import_zod8.z.string(),
2143
2178
  returns: import_zod8.z.string(),
2144
- description: import_zod8.z.string().optional()
2179
+ description: import_zod8.z.string().optional(),
2180
+ /**
2181
+ * Source file realizing this method when it is not the type's own
2182
+ * sourcePath. A type's pure methods routinely live apart from its
2183
+ * declaration: the declaration is a struct or an interface, the methods are
2184
+ * free functions, and a language without methods-on-data has nowhere else
2185
+ * to put them.
2186
+ */
2187
+ sourcePath: import_zod8.z.string().optional(),
2188
+ /**
2189
+ * The code-level name realizing this method, when it legitimately differs
2190
+ * from the method name — e.g. `narrative_step.foreignFields` realized by
2191
+ * `narrativeStepForeignFields`, the free-function form a pure type method
2192
+ * takes in a language whose data carries no methods.
2193
+ */
2194
+ symbol: import_zod8.z.string().optional()
2145
2195
  });
2146
2196
  InvariantSchema = import_zod8.z.object({
2147
2197
  /** Stable invariant id, unique within the entity (referenced as "<type-id>.<invariant-id>"). */
@@ -2186,6 +2236,23 @@ var init_specs = __esm({
2186
2236
  * logical system entity type it maps to.
2187
2237
  */
2188
2238
  linkedEntity: import_zod8.z.string().optional(),
2239
+ /**
2240
+ * The source file holding this type's declaration, and the default for
2241
+ * every method that names no sourcePath of its own. Relative to the root of
2242
+ * the project that holds the spec.
2243
+ *
2244
+ * Naming one turns the type into a CLAIM on code, judged exactly as an
2245
+ * implementation's sourcePath is: the file must resolve, and the
2246
+ * declaration must be anchored in it (UNREALIZED_TYPE). A type that names
2247
+ * none claims nothing and is never reported.
2248
+ */
2249
+ sourcePath: import_zod8.z.string().optional(),
2250
+ /**
2251
+ * The code-level name realizing this type's declaration, when it
2252
+ * legitimately differs from `name` — a type named "Invoice Line" declared
2253
+ * as `InvoiceLine`.
2254
+ */
2255
+ symbol: import_zod8.z.string().optional(),
2189
2256
  /** Per-spec lint suppressions (see LintConfigSchema). */
2190
2257
  lint: LintConfigSchema.optional(),
2191
2258
  /** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
@@ -3635,11 +3702,11 @@ function buildCanvasModel(issues = []) {
3635
3702
  if (pi.component && pi.id) apiTagOf.set(pi.component, pi.id);
3636
3703
  }
3637
3704
  const patternTypes = /* @__PURE__ */ new Set(["Repository", "FeatureComponent", "RouterComponent"]);
3638
- const ownerOf = /* @__PURE__ */ new Map();
3705
+ const ownerOf2 = /* @__PURE__ */ new Map();
3639
3706
  for (const comp of components) {
3640
3707
  if (!patternTypes.has(comp.componentType)) continue;
3641
3708
  for (const memberId of comp.owns) {
3642
- if (componentIds.has(memberId)) ownerOf.set(memberId, comp.id);
3709
+ if (componentIds.has(memberId)) ownerOf2.set(memberId, comp.id);
3643
3710
  }
3644
3711
  }
3645
3712
  const modelComponents = components.map((comp) => {
@@ -3695,7 +3762,7 @@ function buildCanvasModel(issues = []) {
3695
3762
  ...comp.status ? { status: comp.status } : {},
3696
3763
  public: publicComponents.has(comp.id),
3697
3764
  ...apiTagOf.has(comp.id) ? { apiTag: apiTagOf.get(comp.id) } : {},
3698
- ...ownerOf.has(comp.id) ? { owner: ownerOf.get(comp.id) } : {},
3765
+ ...ownerOf2.has(comp.id) ? { owner: ownerOf2.get(comp.id) } : {},
3699
3766
  owns: comp.owns.filter((o) => componentIds.has(o)),
3700
3767
  dependsOn: comp.dependsOn,
3701
3768
  ...comp.externalLinks && comp.externalLinks.length ? { externalLinks: comp.externalLinks } : {},
@@ -13288,9 +13355,9 @@ var init_dependency_conformance = __esm({
13288
13355
  };
13289
13356
  const mappedImplsOf = (compId) => realization.implementationsOf(compId).filter((impl) => implementationSourceFiles(impl).some((f) => isExact(pathKey(f))));
13290
13357
  const dependsOrOwns = (from, toId) => from.dependsOn.includes(toId) || (from.owns ?? []).includes(toId);
13291
- const ownerOf = /* @__PURE__ */ new Map();
13358
+ const ownerOf2 = /* @__PURE__ */ new Map();
13292
13359
  for (const c of ctx.components) {
13293
- for (const member of c.owns ?? []) ownerOf.set(member, c);
13360
+ for (const member of c.owns ?? []) ownerOf2.set(member, c);
13294
13361
  }
13295
13362
  const declaresSurfaceEdge = (from, subsystemId) => {
13296
13363
  const published = ctx.publicSet.get(subsystemId);
@@ -13303,9 +13370,9 @@ var init_dependency_conformance = __esm({
13303
13370
  if (cf.id === cg.id) return true;
13304
13371
  if (dependsOrOwns(cf, cg.id)) return true;
13305
13372
  if (cf.subsystem === cg.subsystem && dependsOrOwns(cg, cf.id) && (cg.componentType === "Portal" || cg.componentType === "Observer")) return true;
13306
- const ownerG = ownerOf.get(cg.id);
13373
+ const ownerG = ownerOf2.get(cg.id);
13307
13374
  if (ownerG && (dependsOrOwns(cf, ownerG.id) || cf.id === ownerG.id)) return true;
13308
- const ownerF = ownerOf.get(cf.id);
13375
+ const ownerF = ownerOf2.get(cf.id);
13309
13376
  if (ownerF && (dependsOrOwns(ownerF, cg.id) || ownerF.id === cg.id)) return true;
13310
13377
  if (ownerF && ownerG && ownerF.id === ownerG.id) return true;
13311
13378
  if (cf.subsystem !== cg.subsystem && declaresSurfaceEdge(cf, cg.subsystem)) return true;
@@ -13559,6 +13626,150 @@ var init_integration_sim_coverage = __esm({
13559
13626
  }
13560
13627
  });
13561
13628
 
13629
+ // src/core/rules/conformance/type-realization.ts
13630
+ function ownerOf(type, file) {
13631
+ if (type.sourcePath && pathKey(type.sourcePath) === pathKey(file)) return `Type "${type.id}"`;
13632
+ const users = (type.methods ?? []).filter((m) => m.sourcePath && pathKey(m.sourcePath) === pathKey(file)).map((m) => m.name);
13633
+ if (users.length === 0) return `Type "${type.id}"`;
13634
+ return `Type "${type.id}" method${users.length === 1 ? "" : "s"} ${users.map((n) => `"${n}"`).join(", ")}`;
13635
+ }
13636
+ var FILE_PROBLEMS, typeRealizationRule;
13637
+ var init_type_realization = __esm({
13638
+ "src/core/rules/conformance/type-realization.ts"() {
13639
+ "use strict";
13640
+ init_models();
13641
+ FILE_PROBLEMS = {
13642
+ escaped: {
13643
+ code: "SOURCE_PATH_ESCAPES_ROOT",
13644
+ severity: "error",
13645
+ detail: "is absolute or escapes the project root \u2014 sourcePaths must stay inside the project."
13646
+ },
13647
+ missing: {
13648
+ code: "MISSING_SOURCE_FILE",
13649
+ severity: "error",
13650
+ detail: "does not resolve to a file \u2014 the spec names code that does not exist."
13651
+ },
13652
+ unreadable: {
13653
+ code: "CONFORMANCE_ANALYSIS_SKIPPED",
13654
+ severity: "warning",
13655
+ detail: "could not be analyzed (binary or unreadable) \u2014 what it would have realized was not checked."
13656
+ }
13657
+ };
13658
+ typeRealizationRule = {
13659
+ name: "type-realization",
13660
+ description: "Code\u2194spec Level 1 for the data model: a type that names a sourcePath is claiming code, so every file it names \u2014 its own and each method's \u2014 must resolve to a real, readable file inside the project root, its file must PUBLISH its declaration (an exported name at exact grade, the declaration tier below it, under its `symbol` when the code-level name differs from the type's name \u2014 and a pure re-export barrel publishes nothing of its own, so a claim on one is never realized), and each of its pure methods must appear in its own file (the method's sourcePath, else the type's) at the declaration tier, under the method's `symbol`, else the method's name. A type that names no sourcePath claims nothing and is never reported. Findings carry the analysis grade (exact AST | pattern table | generic scan) so weaker analysis is visible, a file that escapes the root, is missing or could not be analyzed is reported once and blocks only what it would have realized, and types under chained subsystems (projectPath) validate standalone in their own project run.",
13661
+ codes: [
13662
+ { code: "UNREALIZED_TYPE", defaultSeverity: "warning", summary: "A type names a sourcePath but its declaration is nowhere in that file \u2014 the claim points at code that does not hold it" },
13663
+ { code: "UNREALIZED_TYPE_METHOD", defaultSeverity: "warning", summary: "A pure method of a claimed type is nowhere in its own source file (the method's sourcePath, else the type's)" },
13664
+ { code: "MISSING_SOURCE_FILE", defaultSeverity: "error", summary: "A source file a type or one of its methods names does not resolve to a file on disk" },
13665
+ { code: "SOURCE_PATH_ESCAPES_ROOT", defaultSeverity: "error", summary: "A source file a type or one of its methods names is absolute or escapes the project root (containment refusal)" },
13666
+ { code: "CONFORMANCE_ANALYSIS_SKIPPED", defaultSeverity: "warning", summary: "A source file a type or one of its methods names could not be analyzed (binary/unreadable) \u2014 what it would have realized was not checked" }
13667
+ ],
13668
+ check(ctx) {
13669
+ const code = ctx.codeIndex();
13670
+ for (const type of ctx.types) {
13671
+ if (!type.sourcePath) continue;
13672
+ if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
13673
+ const blocked = /* @__PURE__ */ new Set();
13674
+ for (const file of typeSourceFiles(type)) {
13675
+ const key = pathKey(file);
13676
+ const facts = code.factsAt(key);
13677
+ if (!facts || facts.status === "analyzed") continue;
13678
+ blocked.add(key);
13679
+ const problem = FILE_PROBLEMS[facts.status];
13680
+ if (!problem) continue;
13681
+ ctx.addIssue(problem.severity, problem.code, `${ownerOf(type, file)} sourcePath "${file}" ${problem.detail}`, type.id);
13682
+ }
13683
+ const typeKey = pathKey(type.sourcePath);
13684
+ const typeFacts = code.factsAt(typeKey);
13685
+ if (typeFacts && !blocked.has(typeKey)) {
13686
+ const symbol = type.symbol ?? type.name;
13687
+ const published = typeFacts.analysisGrade === "exact" ? new Set(typeFacts.reexportOnly ? [] : typeFacts.exportedNames) : code.declarationsAt(typeKey);
13688
+ if (!published.has(symbol)) {
13689
+ const label = type.symbol ? `"${type.name}" (symbol "${symbol}")` : `"${type.name}"`;
13690
+ ctx.addIssue(
13691
+ "warning",
13692
+ "UNREALIZED_TYPE",
13693
+ `Type ${label} is not published by "${type.sourcePath}" (analysis grade: ${typeFacts.analysisGrade})${typeFacts.reexportOnly ? ", which is a pure re-export barrel and declares nothing of its own" : ""} \u2014 name the file the declaration lives in, or bind the code-level name with \`symbol\`.`,
13694
+ type.id
13695
+ );
13696
+ }
13697
+ }
13698
+ for (const method of type.methods ?? []) {
13699
+ const file = method.sourcePath ?? type.sourcePath;
13700
+ const key = pathKey(file);
13701
+ if (blocked.has(key)) continue;
13702
+ const facts = code.factsAt(key);
13703
+ if (!facts || facts.status !== "analyzed") continue;
13704
+ const symbol = method.symbol ?? method.name;
13705
+ if (code.declarationsAt(key).has(symbol)) continue;
13706
+ const label = method.symbol ? `"${method.name}" (symbol "${symbol}")` : `"${method.name}"`;
13707
+ ctx.addIssue(
13708
+ "warning",
13709
+ "UNREALIZED_TYPE_METHOD",
13710
+ `Method ${label} of type "${type.id}" is not declared in "${file}" (analysis grade: ${facts.analysisGrade}) \u2014 a pure type method is often a free function named after its type; bind that name with \`symbol\`, or name the file it lives in with the method's own \`sourcePath\`.`,
13711
+ type.id
13712
+ );
13713
+ }
13714
+ }
13715
+ }
13716
+ };
13717
+ }
13718
+ });
13719
+
13720
+ // src/core/rules/conformance/unclaimed-source.ts
13721
+ var unclaimedSourceRule;
13722
+ var init_unclaimed_source = __esm({
13723
+ "src/core/rules/conformance/unclaimed-source.ts"() {
13724
+ "use strict";
13725
+ init_models();
13726
+ unclaimedSourceRule = {
13727
+ name: "unclaimed-source",
13728
+ description: "Code\u2194spec Level 1, asked the other way round: every source file under the project's declared source roots must be named by some spec \u2014 an implementation's sourcePath, a method's, a simPath, or a type's \u2014 or be carried in the frozen `rules.conformance.unclaimed` list. A pure re-export barrel declares nothing of its own and only republishes other modules, so it has no code to claim and is exempt (exact grade only: a weaker grade cannot tell a barrel from a file it failed to parse). Declaring no source roots leaves the check silent, which is what makes it opt-in; the unclaimed list is a one-way debt register, so an entry the walk no longer finds unclaimed must be deleted and the list can only shrink.",
13729
+ codes: [
13730
+ { code: "UNCLAIMED_SOURCE_FILE", defaultSeverity: "warning", summary: "A source file under a declared source root that no spec names and the frozen unclaimed list does not carry \u2014 code nobody designed" },
13731
+ { code: "STALE_UNCLAIMED_ENTRY", defaultSeverity: "warning", summary: "An entry of the frozen unclaimed list names a file that is now claimed, or that the source-root walk no longer finds \u2014 delete it, the list only shrinks" }
13732
+ ],
13733
+ check(ctx) {
13734
+ const walked = ctx.codeModel.rootFiles;
13735
+ const listed = (ctx.rules?.conformance?.unclaimed ?? []).map(pathKey);
13736
+ if (walked.length === 0) return;
13737
+ const claimed = /* @__PURE__ */ new Set();
13738
+ for (const impl of ctx.implementations) {
13739
+ for (const file of implementationSourceFiles(impl)) claimed.add(pathKey(file));
13740
+ if (impl.simPath) claimed.add(pathKey(impl.simPath));
13741
+ }
13742
+ for (const type of ctx.types) {
13743
+ for (const file of typeSourceFiles(type)) claimed.add(pathKey(file));
13744
+ }
13745
+ const code = ctx.codeIndex();
13746
+ const isBarrel = (file) => code.factsAt(file)?.reexportOnly === true;
13747
+ const isLiveDebt = (file) => !claimed.has(file) && !isBarrel(file);
13748
+ const listedSet = new Set(listed);
13749
+ for (const file of walked) {
13750
+ if (!isLiveDebt(file)) continue;
13751
+ if (listedSet.has(file)) continue;
13752
+ ctx.addIssue(
13753
+ "warning",
13754
+ "UNCLAIMED_SOURCE_FILE",
13755
+ `Source file "${file}" is under a declared source root but no spec names it \u2014 give it to a spec as a sourcePath (an implementation's, a method's, or a type's), or carry it in \`rules.conformance.unclaimed\` until it is designed.`
13756
+ );
13757
+ }
13758
+ const walkedSet = new Set(walked);
13759
+ for (const entry of listed) {
13760
+ if (walkedSet.has(entry) && isLiveDebt(entry)) continue;
13761
+ const why = !walkedSet.has(entry) ? "the source-root walk no longer finds it (deleted, renamed, excluded, or outside the declared roots)" : isBarrel(entry) ? "it is a pure re-export barrel, which has nothing to claim" : "a spec now names it";
13762
+ ctx.addIssue(
13763
+ "warning",
13764
+ "STALE_UNCLAIMED_ENTRY",
13765
+ `"${entry}" is carried in \`rules.conformance.unclaimed\` but ${why} \u2014 delete the entry; the list only shrinks.`
13766
+ );
13767
+ }
13768
+ }
13769
+ };
13770
+ }
13771
+ });
13772
+
13562
13773
  // src/core/rules/heuristic/coupling-health.ts
13563
13774
  var DEFAULT_GOD_COMPONENT_THRESHOLD, couplingRule;
13564
13775
  var init_coupling_health = __esm({
@@ -13902,14 +14113,14 @@ var init_technology_boundaries = __esm({
13902
14113
  { code: "VENDOR_NAME_IN_CONTRACT", defaultSeverity: "warning", summary: "Technology name in L3 contract identifiers" }
13903
14114
  ],
13904
14115
  check(ctx) {
13905
- const ownerOf = /* @__PURE__ */ new Map();
13906
- for (const c of ctx.components) for (const m of c.owns) ownerOf.set(m, c.id);
14116
+ const ownerOf2 = /* @__PURE__ */ new Map();
14117
+ for (const c of ctx.components) for (const m of c.owns) ownerOf2.set(m, c.id);
13907
14118
  const ownershipRoot = (id) => {
13908
14119
  const seen = /* @__PURE__ */ new Set();
13909
14120
  let cur = id;
13910
- while (ownerOf.has(cur) && !seen.has(cur)) {
14121
+ while (ownerOf2.has(cur) && !seen.has(cur)) {
13911
14122
  seen.add(cur);
13912
- cur = ownerOf.get(cur);
14123
+ cur = ownerOf2.get(cur);
13913
14124
  }
13914
14125
  return cur;
13915
14126
  };
@@ -14802,6 +15013,8 @@ var init_repository = __esm({
14802
15013
  init_integration_sim_file();
14803
15014
  init_integration_sim_wiring();
14804
15015
  init_integration_sim_coverage();
15016
+ init_type_realization();
15017
+ init_unclaimed_source();
14805
15018
  init_coupling_health();
14806
15019
  init_signature_language_builtins();
14807
15020
  init_narrative_language_constructs();
@@ -14966,6 +15179,11 @@ var init_repository = __esm({
14966
15179
  integrationSimFileRule,
14967
15180
  integrationSimWiringRule,
14968
15181
  integrationSimCoverageRule,
15182
+ // The data model's own claim on code, and the question no spec can ask
15183
+ // from the spec side: which files are named by nothing at all. Last in
15184
+ // the family because the second one reads what all the others named.
15185
+ typeRealizationRule,
15186
+ unclaimedSourceRule,
14969
15187
  couplingRule,
14970
15188
  // Target-language fit in two questions: what a CONTRACT may name, and what
14971
15189
  // a NARRATIVE may describe.
@@ -14998,7 +15216,7 @@ var init_repository = __esm({
14998
15216
 
14999
15217
  // src/core/source-analysis.ts
15000
15218
  function emptyCodeModel() {
15001
- return { files: [], projectRoot: "" };
15219
+ return { files: [], projectRoot: "", rootFiles: [] };
15002
15220
  }
15003
15221
  function stripComments(text, patterns) {
15004
15222
  let out = text;
@@ -15238,7 +15456,8 @@ function walkExact(ts, sourceText, fileName) {
15238
15456
  ts.forEachChild(node, visit);
15239
15457
  };
15240
15458
  visit(sf);
15241
- return { declared, anchors, exported, imports, reexports, starExports, complexity, calls, mutableBindings };
15459
+ const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
15460
+ return { declared, anchors, exported, imports, reexports, starExports, complexity, calls, mutableBindings, reexportOnly };
15242
15461
  }
15243
15462
  function resolveRelativeModule(fromFile, specifier) {
15244
15463
  if (!specifier.startsWith(".")) return null;
@@ -15296,15 +15515,65 @@ function looksBinary(buffer) {
15296
15515
  const probe = buffer.subarray(0, Math.min(buffer.length, 4096));
15297
15516
  return probe.includes(0);
15298
15517
  }
15299
- function buildCodeModel(implementations, projectRoot) {
15518
+ function walkSourceRoots(sourceRoots, exclude, projectRoot) {
15519
+ const found = [];
15520
+ const seen = /* @__PURE__ */ new Set();
15521
+ const excluded = exclude.map(pathKey);
15522
+ const isExcluded = (key) => excluded.some((e) => key === e || key.startsWith(`${e}/`));
15523
+ const record = (absolute) => {
15524
+ const key = pathKey(path11.relative(projectRoot, absolute));
15525
+ if (seen.has(key) || isExcluded(key)) return;
15526
+ if (!EXTENSION_LANGUAGE[path11.extname(key).toLowerCase()]) return;
15527
+ seen.add(key);
15528
+ found.push(key);
15529
+ };
15530
+ const descend = (dir) => {
15531
+ let entries;
15532
+ try {
15533
+ entries = fs8.readdirSync(dir, { withFileTypes: true });
15534
+ } catch {
15535
+ return;
15536
+ }
15537
+ for (const entry of entries.slice().sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
15538
+ const child = path11.join(dir, entry.name);
15539
+ if (entry.isDirectory()) {
15540
+ if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
15541
+ if (isExcluded(pathKey(path11.relative(projectRoot, child)))) continue;
15542
+ descend(child);
15543
+ } else if (entry.isFile()) {
15544
+ record(child);
15545
+ }
15546
+ }
15547
+ };
15548
+ for (const root of sourceRoots) {
15549
+ const key = pathKey(root);
15550
+ if (path11.isAbsolute(key) || path11.normalize(key).split(path11.sep)[0] === "..") continue;
15551
+ const absolute = path11.resolve(projectRoot, key);
15552
+ if (path11.relative(projectRoot, absolute).startsWith("..")) continue;
15553
+ if (isExcluded(key)) continue;
15554
+ let stat;
15555
+ try {
15556
+ stat = fs8.statSync(absolute);
15557
+ } catch {
15558
+ continue;
15559
+ }
15560
+ if (stat.isDirectory()) descend(absolute);
15561
+ else if (stat.isFile()) record(absolute);
15562
+ }
15563
+ return found;
15564
+ }
15565
+ function buildCodeModel(implementations, types, projectRoot, sourceRoots = [], exclude = []) {
15300
15566
  const files = [];
15301
15567
  const seen = /* @__PURE__ */ new Set();
15302
15568
  const exactCache = /* @__PURE__ */ new Map();
15569
+ const rootFiles = walkSourceRoots(sourceRoots, exclude, projectRoot);
15303
15570
  const declaredPaths = [];
15304
15571
  for (const impl of implementations) {
15305
15572
  declaredPaths.push(...implementationSourceFiles(impl));
15306
15573
  if (impl.simPath) declaredPaths.push(impl.simPath);
15307
15574
  }
15575
+ for (const type of types) declaredPaths.push(...typeSourceFiles(type));
15576
+ declaredPaths.push(...rootFiles);
15308
15577
  for (const declared of declaredPaths) {
15309
15578
  const sourcePath = pathKey(declared);
15310
15579
  if (seen.has(sourcePath)) continue;
@@ -15353,7 +15622,8 @@ function buildCodeModel(implementations, projectRoot) {
15353
15622
  reexports: [...facts.reexports],
15354
15623
  functionComplexity: Object.fromEntries(facts.complexity),
15355
15624
  functionCalls: Object.fromEntries([...facts.calls].map(([k, v]) => [k, [...v]])),
15356
- topLevelMutableBindings: [...facts.mutableBindings]
15625
+ topLevelMutableBindings: [...facts.mutableBindings],
15626
+ reexportOnly: facts.reexportOnly
15357
15627
  };
15358
15628
  } catch {
15359
15629
  analyzed = analyzeGeneric(text);
@@ -15368,7 +15638,7 @@ function buildCodeModel(implementations, projectRoot) {
15368
15638
  }
15369
15639
  files.push({ path: sourcePath, status: "analyzed", language, ...analyzed });
15370
15640
  }
15371
- return { files, projectRoot };
15641
+ return { files, projectRoot, rootFiles };
15372
15642
  }
15373
15643
  var fs8, path11, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
15374
15644
  var init_source_analysis = __esm({
@@ -17221,7 +17491,14 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
17221
17491
  const implementations = loadImplementationSpecs();
17222
17492
  const types = loadTypeSpecs();
17223
17493
  const surfaceSnapshots = loadSurfaceSnapshots();
17224
- const codeModel = buildCodeModel(implementations, getProjectRoot());
17494
+ const conformance = rules?.conformance;
17495
+ const codeModel = buildCodeModel(
17496
+ implementations,
17497
+ types,
17498
+ getProjectRoot(),
17499
+ conformance?.sourceRoots ?? [],
17500
+ conformance?.exclude ?? []
17501
+ );
17225
17502
  const statusBearing = treatAllAsComplete ? [...subsystems, ...components, ...interfaces, ...implementations] : settledStatusBearing({ subsystems, components, interfaces, implementations });
17226
17503
  const statusSnapshot = statusBearing.map((s) => s.status);
17227
17504
  for (const s of statusBearing) s.status = "complete";
@@ -26212,6 +26489,7 @@ __export(src_exports, {
26212
26489
  ComponentSpecSchema: () => ComponentSpecSchema,
26213
26490
  ComponentTypeSchema: () => ComponentTypeSchema,
26214
26491
  ConfigValidationError: () => ConfigValidationError,
26492
+ ConformanceRuleConfigSchema: () => ConformanceRuleConfigSchema,
26215
26493
  ConformanceTierSchema: () => ConformanceTierSchema,
26216
26494
  CustomExporter: () => CustomExporter,
26217
26495
  CustomTargetConfigSchema: () => CustomTargetConfigSchema,
@@ -26593,6 +26871,7 @@ __export(src_exports, {
26593
26871
  toOpenApiSet: () => toOpenApiSet,
26594
26872
  typeGenericParameters: () => typeGenericParameters,
26595
26873
  typeMatchesRef: () => typeMatchesRef,
26874
+ typeSourceFiles: () => typeSourceFiles,
26596
26875
  uninstallPack: () => uninstallPack,
26597
26876
  updateSpec: () => updateSpec,
26598
26877
  upsertPackSelection: () => upsertPackSelection,
@@ -26940,6 +27219,7 @@ function loadProjectConfig4() {
26940
27219
  ComponentSpecSchema,
26941
27220
  ComponentTypeSchema,
26942
27221
  ConfigValidationError,
27222
+ ConformanceRuleConfigSchema,
26943
27223
  ConformanceTierSchema,
26944
27224
  CustomExporter,
26945
27225
  CustomTargetConfigSchema,
@@ -27321,6 +27601,7 @@ function loadProjectConfig4() {
27321
27601
  toOpenApiSet,
27322
27602
  typeGenericParameters,
27323
27603
  typeMatchesRef,
27604
+ typeSourceFiles,
27324
27605
  uninstallPack,
27325
27606
  updateSpec,
27326
27607
  upsertPackSelection,