@wairon/cli 5.1.1-dev.37 → 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/cli/index.js CHANGED
@@ -65,7 +65,7 @@ var init_defaults = __esm({
65
65
  copilot: ".github/prompts",
66
66
  codex: ".codex/agents"
67
67
  };
68
- WAIRON_VERSION = "5.1.1-dev.37";
68
+ WAIRON_VERSION = "5.1.1-dev.39";
69
69
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
70
70
  SUPPORTED_ALIASES = ["wai"];
71
71
  SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
@@ -770,7 +770,7 @@ function activeTargetTypes(config) {
770
770
  const targets = config.targets;
771
771
  return targets.filter((t) => typeof t === "string" || t.enabled !== false).map((t) => typeof t === "string" ? t : t.type);
772
772
  }
773
- var import_zod4, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, PackSelectionSchema, ProfileSelectionSubjectSchema, ProjectProfileSelectionSchema, ProjectConfigSchema;
773
+ var import_zod4, BuiltinTargetConfigSchema, CustomTargetConfigSchema, TargetConfigSchema, NamingRuleConfigSchema, DocumentationRuleConfigSchema, ComplexityRuleConfigSchema, ConformanceRuleConfigSchema, DesignDepthSchema, RulesConfigSchema, PathsConfigSchema, PackSelectionSchema, ProfileSelectionSubjectSchema, ProjectProfileSelectionSchema, ProjectConfigSchema;
774
774
  var init_project = __esm({
775
775
  "src/models/project.ts"() {
776
776
  "use strict";
@@ -867,6 +867,30 @@ var init_project = __esm({
867
867
  */
868
868
  maxCognitiveLevel: import_zod4.z.string().optional()
869
869
  });
870
+ ConformanceRuleConfigSchema = import_zod4.z.object({
871
+ /**
872
+ * Project-relative paths holding this project's own source code; a
873
+ * directory is walked recursively, a file names itself. Absolute or
874
+ * parent-escaping entries are refused by containment, exactly as a
875
+ * sourcePath is.
876
+ */
877
+ sourceRoots: import_zod4.z.array(import_zod4.z.string()).optional(),
878
+ /**
879
+ * Paths inside a source root that are not this project's code to claim —
880
+ * vendored libraries, generated output, a chained subproject's own
881
+ * directory (the child claims its files in its own run). A file at, or
882
+ * under, one of these is never walked, so it is neither reported nor
883
+ * carried as debt.
884
+ */
885
+ exclude: import_zod4.z.array(import_zod4.z.string()).optional(),
886
+ /**
887
+ * The files under the source roots that no spec names yet, frozen. A listed
888
+ * file is not reported; an unlisted one is UNCLAIMED_SOURCE_FILE; an entry
889
+ * that is now claimed, proven a barrel, or no longer found is
890
+ * STALE_UNCLAIMED_ENTRY.
891
+ */
892
+ unclaimed: import_zod4.z.array(import_zod4.z.string()).optional()
893
+ });
870
894
  DesignDepthSchema = import_zod4.z.enum(["components", "interfaces", "implementations", "narratives"]);
871
895
  RulesConfigSchema = import_zod4.z.object({
872
896
  /**
@@ -917,6 +941,8 @@ var init_project = __esm({
917
941
  documentation: DocumentationRuleConfigSchema.optional(),
918
942
  /** Dynamic structural complexity caps (method limit, step limit, dependency limit) */
919
943
  complexity: ComplexityRuleConfigSchema.optional(),
944
+ /** Where this project's own source lives and what of it no spec claims yet (see ConformanceRuleConfigSchema) */
945
+ conformance: ConformanceRuleConfigSchema.optional(),
920
946
  /** Project-default design depth (see DesignDepthSchema); subsystems may override. */
921
947
  designDepth: DesignDepthSchema.optional()
922
948
  });
@@ -1228,6 +1254,15 @@ function implementationSourceFiles(impl) {
1228
1254
  for (const method2 of impl.methods ?? []) add(method2.sourcePath);
1229
1255
  return files;
1230
1256
  }
1257
+ function typeSourceFiles(type) {
1258
+ const files = [];
1259
+ const add = (file) => {
1260
+ if (file && !files.includes(file)) files.push(file);
1261
+ };
1262
+ add(type.sourcePath);
1263
+ for (const method2 of type.methods ?? []) add(method2.sourcePath);
1264
+ return files;
1265
+ }
1231
1266
  var import_zod7, 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;
1232
1267
  var init_specs = __esm({
1233
1268
  "src/models/specs.ts"() {
@@ -1536,7 +1571,13 @@ var init_specs = __esm({
1536
1571
  GuaranteeSchema = import_zod7.z.string().min(1);
1537
1572
  MethodParamSchema = import_zod7.z.object({
1538
1573
  name: import_zod7.z.string(),
1539
- /** A primitive/builtin or a defined type id (qualified across subsystems, e.g. "billing.Invoice"). */
1574
+ /**
1575
+ * A primitive/builtin or a defined type id (qualified across subsystems, e.g.
1576
+ * "billing.Invoice"), on its own or inside a generic, an array or a UNION:
1577
+ * `Invoice | null`, `Promise<Invoice | null>`, `Invoice[] | null`. Every
1578
+ * identifier the string names has to resolve — see the grammar on
1579
+ * src/models/type-references.ts.
1580
+ */
1540
1581
  type: import_zod7.z.string(),
1541
1582
  description: import_zod7.z.string().optional(),
1542
1583
  optional: import_zod7.z.boolean().optional()
@@ -1554,8 +1595,9 @@ var init_specs = __esm({
1554
1595
  description: import_zod7.z.string(),
1555
1596
  signature: import_zod7.z.string(),
1556
1597
  // e.g. "save(key: string, data: Buffer): Promise<void>"
1598
+ // e.g. "Promise<void>", or a union: "Invoice | null" — the commonest shape in
1599
+ // any real tree. See the grammar on src/models/type-references.ts.
1557
1600
  returns: import_zod7.z.string(),
1558
- // e.g. "Promise<void>"
1559
1601
  /** Structured parameters (authoritative for type checking when present). */
1560
1602
  params: import_zod7.z.array(MethodParamSchema).optional(),
1561
1603
  /** Concrete wire binding for this method when its component is a Portal (set via sdd_set_endpoints). */
@@ -1824,8 +1866,10 @@ var init_specs = __esm({
1824
1866
  TypeKindSchema = import_zod7.z.enum(["entity", "value-object"]);
1825
1867
  TypeFieldSchema = import_zod7.z.object({
1826
1868
  name: import_zod7.z.string(),
1869
+ // A primitive, or another type id (qualified across subsystems, e.g.
1870
+ // "billing.Invoice") — on its own or inside a generic, an array or a union
1871
+ // ("Invoice[] | null"). See the grammar on src/models/type-references.ts.
1827
1872
  type: import_zod7.z.string(),
1828
- // a primitive, or another type id (qualified across subsystems, e.g. "billing.Invoice")
1829
1873
  description: import_zod7.z.string().optional(),
1830
1874
  optional: import_zod7.z.boolean().default(false),
1831
1875
  /**
@@ -1845,7 +1889,22 @@ var init_specs = __esm({
1845
1889
  name: import_zod7.z.string(),
1846
1890
  signature: import_zod7.z.string(),
1847
1891
  returns: import_zod7.z.string(),
1848
- description: import_zod7.z.string().optional()
1892
+ description: import_zod7.z.string().optional(),
1893
+ /**
1894
+ * Source file realizing this method when it is not the type's own
1895
+ * sourcePath. A type's pure methods routinely live apart from its
1896
+ * declaration: the declaration is a struct or an interface, the methods are
1897
+ * free functions, and a language without methods-on-data has nowhere else
1898
+ * to put them.
1899
+ */
1900
+ sourcePath: import_zod7.z.string().optional(),
1901
+ /**
1902
+ * The code-level name realizing this method, when it legitimately differs
1903
+ * from the method name — e.g. `narrative_step.foreignFields` realized by
1904
+ * `narrativeStepForeignFields`, the free-function form a pure type method
1905
+ * takes in a language whose data carries no methods.
1906
+ */
1907
+ symbol: import_zod7.z.string().optional()
1849
1908
  });
1850
1909
  InvariantSchema = import_zod7.z.object({
1851
1910
  /** Stable invariant id, unique within the entity (referenced as "<type-id>.<invariant-id>"). */
@@ -1890,6 +1949,23 @@ var init_specs = __esm({
1890
1949
  * logical system entity type it maps to.
1891
1950
  */
1892
1951
  linkedEntity: import_zod7.z.string().optional(),
1952
+ /**
1953
+ * The source file holding this type's declaration, and the default for
1954
+ * every method that names no sourcePath of its own. Relative to the root of
1955
+ * the project that holds the spec.
1956
+ *
1957
+ * Naming one turns the type into a CLAIM on code, judged exactly as an
1958
+ * implementation's sourcePath is: the file must resolve, and the
1959
+ * declaration must be anchored in it (UNREALIZED_TYPE). A type that names
1960
+ * none claims nothing and is never reported.
1961
+ */
1962
+ sourcePath: import_zod7.z.string().optional(),
1963
+ /**
1964
+ * The code-level name realizing this type's declaration, when it
1965
+ * legitimately differs from `name` — a type named "Invoice Line" declared
1966
+ * as `InvoiceLine`.
1967
+ */
1968
+ symbol: import_zod7.z.string().optional(),
1893
1969
  /** Per-spec lint suppressions (see LintConfigSchema). */
1894
1970
  lint: LintConfigSchema.optional(),
1895
1971
  /** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
@@ -3683,11 +3759,11 @@ function buildCanvasModel(issues = []) {
3683
3759
  if (pi.component && pi.id) apiTagOf.set(pi.component, pi.id);
3684
3760
  }
3685
3761
  const patternTypes = /* @__PURE__ */ new Set(["Repository", "FeatureComponent", "RouterComponent"]);
3686
- const ownerOf = /* @__PURE__ */ new Map();
3762
+ const ownerOf2 = /* @__PURE__ */ new Map();
3687
3763
  for (const comp of components) {
3688
3764
  if (!patternTypes.has(comp.componentType)) continue;
3689
3765
  for (const memberId of comp.owns) {
3690
- if (componentIds.has(memberId)) ownerOf.set(memberId, comp.id);
3766
+ if (componentIds.has(memberId)) ownerOf2.set(memberId, comp.id);
3691
3767
  }
3692
3768
  }
3693
3769
  const modelComponents = components.map((comp) => {
@@ -3743,7 +3819,7 @@ function buildCanvasModel(issues = []) {
3743
3819
  ...comp.status ? { status: comp.status } : {},
3744
3820
  public: publicComponents.has(comp.id),
3745
3821
  ...apiTagOf.has(comp.id) ? { apiTag: apiTagOf.get(comp.id) } : {},
3746
- ...ownerOf.has(comp.id) ? { owner: ownerOf.get(comp.id) } : {},
3822
+ ...ownerOf2.has(comp.id) ? { owner: ownerOf2.get(comp.id) } : {},
3747
3823
  owns: comp.owns.filter((o) => componentIds.has(o)),
3748
3824
  dependsOn: comp.dependsOn,
3749
3825
  ...comp.externalLinks && comp.externalLinks.length ? { externalLinks: comp.externalLinks } : {},
@@ -13333,9 +13409,9 @@ var init_dependency_conformance = __esm({
13333
13409
  };
13334
13410
  const mappedImplsOf = (compId) => realization.implementationsOf(compId).filter((impl) => implementationSourceFiles(impl).some((f) => isExact(pathKey(f))));
13335
13411
  const dependsOrOwns = (from, toId) => from.dependsOn.includes(toId) || (from.owns ?? []).includes(toId);
13336
- const ownerOf = /* @__PURE__ */ new Map();
13412
+ const ownerOf2 = /* @__PURE__ */ new Map();
13337
13413
  for (const c of ctx.components) {
13338
- for (const member of c.owns ?? []) ownerOf.set(member, c);
13414
+ for (const member of c.owns ?? []) ownerOf2.set(member, c);
13339
13415
  }
13340
13416
  const declaresSurfaceEdge = (from, subsystemId) => {
13341
13417
  const published = ctx.publicSet.get(subsystemId);
@@ -13348,9 +13424,9 @@ var init_dependency_conformance = __esm({
13348
13424
  if (cf.id === cg.id) return true;
13349
13425
  if (dependsOrOwns(cf, cg.id)) return true;
13350
13426
  if (cf.subsystem === cg.subsystem && dependsOrOwns(cg, cf.id) && (cg.componentType === "Portal" || cg.componentType === "Observer")) return true;
13351
- const ownerG = ownerOf.get(cg.id);
13427
+ const ownerG = ownerOf2.get(cg.id);
13352
13428
  if (ownerG && (dependsOrOwns(cf, ownerG.id) || cf.id === ownerG.id)) return true;
13353
- const ownerF = ownerOf.get(cf.id);
13429
+ const ownerF = ownerOf2.get(cf.id);
13354
13430
  if (ownerF && (dependsOrOwns(ownerF, cg.id) || ownerF.id === cg.id)) return true;
13355
13431
  if (ownerF && ownerG && ownerF.id === ownerG.id) return true;
13356
13432
  if (cf.subsystem !== cg.subsystem && declaresSurfaceEdge(cf, cg.subsystem)) return true;
@@ -13604,6 +13680,150 @@ var init_integration_sim_coverage = __esm({
13604
13680
  }
13605
13681
  });
13606
13682
 
13683
+ // src/core/rules/conformance/type-realization.ts
13684
+ function ownerOf(type, file) {
13685
+ if (type.sourcePath && pathKey(type.sourcePath) === pathKey(file)) return `Type "${type.id}"`;
13686
+ const users = (type.methods ?? []).filter((m) => m.sourcePath && pathKey(m.sourcePath) === pathKey(file)).map((m) => m.name);
13687
+ if (users.length === 0) return `Type "${type.id}"`;
13688
+ return `Type "${type.id}" method${users.length === 1 ? "" : "s"} ${users.map((n) => `"${n}"`).join(", ")}`;
13689
+ }
13690
+ var FILE_PROBLEMS, typeRealizationRule;
13691
+ var init_type_realization = __esm({
13692
+ "src/core/rules/conformance/type-realization.ts"() {
13693
+ "use strict";
13694
+ init_models();
13695
+ FILE_PROBLEMS = {
13696
+ escaped: {
13697
+ code: "SOURCE_PATH_ESCAPES_ROOT",
13698
+ severity: "error",
13699
+ detail: "is absolute or escapes the project root \u2014 sourcePaths must stay inside the project."
13700
+ },
13701
+ missing: {
13702
+ code: "MISSING_SOURCE_FILE",
13703
+ severity: "error",
13704
+ detail: "does not resolve to a file \u2014 the spec names code that does not exist."
13705
+ },
13706
+ unreadable: {
13707
+ code: "CONFORMANCE_ANALYSIS_SKIPPED",
13708
+ severity: "warning",
13709
+ detail: "could not be analyzed (binary or unreadable) \u2014 what it would have realized was not checked."
13710
+ }
13711
+ };
13712
+ typeRealizationRule = {
13713
+ name: "type-realization",
13714
+ 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.",
13715
+ codes: [
13716
+ { 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" },
13717
+ { 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)" },
13718
+ { 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" },
13719
+ { 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)" },
13720
+ { 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" }
13721
+ ],
13722
+ check(ctx) {
13723
+ const code = ctx.codeIndex();
13724
+ for (const type of ctx.types) {
13725
+ if (!type.sourcePath) continue;
13726
+ if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
13727
+ const blocked = /* @__PURE__ */ new Set();
13728
+ for (const file of typeSourceFiles(type)) {
13729
+ const key = pathKey(file);
13730
+ const facts = code.factsAt(key);
13731
+ if (!facts || facts.status === "analyzed") continue;
13732
+ blocked.add(key);
13733
+ const problem = FILE_PROBLEMS[facts.status];
13734
+ if (!problem) continue;
13735
+ ctx.addIssue(problem.severity, problem.code, `${ownerOf(type, file)} sourcePath "${file}" ${problem.detail}`, type.id);
13736
+ }
13737
+ const typeKey = pathKey(type.sourcePath);
13738
+ const typeFacts = code.factsAt(typeKey);
13739
+ if (typeFacts && !blocked.has(typeKey)) {
13740
+ const symbol = type.symbol ?? type.name;
13741
+ const published = typeFacts.analysisGrade === "exact" ? new Set(typeFacts.reexportOnly ? [] : typeFacts.exportedNames) : code.declarationsAt(typeKey);
13742
+ if (!published.has(symbol)) {
13743
+ const label = type.symbol ? `"${type.name}" (symbol "${symbol}")` : `"${type.name}"`;
13744
+ ctx.addIssue(
13745
+ "warning",
13746
+ "UNREALIZED_TYPE",
13747
+ `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\`.`,
13748
+ type.id
13749
+ );
13750
+ }
13751
+ }
13752
+ for (const method2 of type.methods ?? []) {
13753
+ const file = method2.sourcePath ?? type.sourcePath;
13754
+ const key = pathKey(file);
13755
+ if (blocked.has(key)) continue;
13756
+ const facts = code.factsAt(key);
13757
+ if (!facts || facts.status !== "analyzed") continue;
13758
+ const symbol = method2.symbol ?? method2.name;
13759
+ if (code.declarationsAt(key).has(symbol)) continue;
13760
+ const label = method2.symbol ? `"${method2.name}" (symbol "${symbol}")` : `"${method2.name}"`;
13761
+ ctx.addIssue(
13762
+ "warning",
13763
+ "UNREALIZED_TYPE_METHOD",
13764
+ `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\`.`,
13765
+ type.id
13766
+ );
13767
+ }
13768
+ }
13769
+ }
13770
+ };
13771
+ }
13772
+ });
13773
+
13774
+ // src/core/rules/conformance/unclaimed-source.ts
13775
+ var unclaimedSourceRule;
13776
+ var init_unclaimed_source = __esm({
13777
+ "src/core/rules/conformance/unclaimed-source.ts"() {
13778
+ "use strict";
13779
+ init_models();
13780
+ unclaimedSourceRule = {
13781
+ name: "unclaimed-source",
13782
+ 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.",
13783
+ codes: [
13784
+ { 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" },
13785
+ { 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" }
13786
+ ],
13787
+ check(ctx) {
13788
+ const walked = ctx.codeModel.rootFiles;
13789
+ const listed = (ctx.rules?.conformance?.unclaimed ?? []).map(pathKey);
13790
+ if (walked.length === 0) return;
13791
+ const claimed = /* @__PURE__ */ new Set();
13792
+ for (const impl of ctx.implementations) {
13793
+ for (const file of implementationSourceFiles(impl)) claimed.add(pathKey(file));
13794
+ if (impl.simPath) claimed.add(pathKey(impl.simPath));
13795
+ }
13796
+ for (const type of ctx.types) {
13797
+ for (const file of typeSourceFiles(type)) claimed.add(pathKey(file));
13798
+ }
13799
+ const code = ctx.codeIndex();
13800
+ const isBarrel = (file) => code.factsAt(file)?.reexportOnly === true;
13801
+ const isLiveDebt = (file) => !claimed.has(file) && !isBarrel(file);
13802
+ const listedSet = new Set(listed);
13803
+ for (const file of walked) {
13804
+ if (!isLiveDebt(file)) continue;
13805
+ if (listedSet.has(file)) continue;
13806
+ ctx.addIssue(
13807
+ "warning",
13808
+ "UNCLAIMED_SOURCE_FILE",
13809
+ `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.`
13810
+ );
13811
+ }
13812
+ const walkedSet = new Set(walked);
13813
+ for (const entry of listed) {
13814
+ if (walkedSet.has(entry) && isLiveDebt(entry)) continue;
13815
+ 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";
13816
+ ctx.addIssue(
13817
+ "warning",
13818
+ "STALE_UNCLAIMED_ENTRY",
13819
+ `"${entry}" is carried in \`rules.conformance.unclaimed\` but ${why} \u2014 delete the entry; the list only shrinks.`
13820
+ );
13821
+ }
13822
+ }
13823
+ };
13824
+ }
13825
+ });
13826
+
13607
13827
  // src/core/rules/heuristic/coupling-health.ts
13608
13828
  var DEFAULT_GOD_COMPONENT_THRESHOLD, couplingRule;
13609
13829
  var init_coupling_health = __esm({
@@ -13947,14 +14167,14 @@ var init_technology_boundaries = __esm({
13947
14167
  { code: "VENDOR_NAME_IN_CONTRACT", defaultSeverity: "warning", summary: "Technology name in L3 contract identifiers" }
13948
14168
  ],
13949
14169
  check(ctx) {
13950
- const ownerOf = /* @__PURE__ */ new Map();
13951
- for (const c of ctx.components) for (const m of c.owns) ownerOf.set(m, c.id);
14170
+ const ownerOf2 = /* @__PURE__ */ new Map();
14171
+ for (const c of ctx.components) for (const m of c.owns) ownerOf2.set(m, c.id);
13952
14172
  const ownershipRoot = (id) => {
13953
14173
  const seen = /* @__PURE__ */ new Set();
13954
14174
  let cur = id;
13955
- while (ownerOf.has(cur) && !seen.has(cur)) {
14175
+ while (ownerOf2.has(cur) && !seen.has(cur)) {
13956
14176
  seen.add(cur);
13957
- cur = ownerOf.get(cur);
14177
+ cur = ownerOf2.get(cur);
13958
14178
  }
13959
14179
  return cur;
13960
14180
  };
@@ -14847,6 +15067,8 @@ var init_repository = __esm({
14847
15067
  init_integration_sim_file();
14848
15068
  init_integration_sim_wiring();
14849
15069
  init_integration_sim_coverage();
15070
+ init_type_realization();
15071
+ init_unclaimed_source();
14850
15072
  init_coupling_health();
14851
15073
  init_signature_language_builtins();
14852
15074
  init_narrative_language_constructs();
@@ -15011,6 +15233,11 @@ var init_repository = __esm({
15011
15233
  integrationSimFileRule,
15012
15234
  integrationSimWiringRule,
15013
15235
  integrationSimCoverageRule,
15236
+ // The data model's own claim on code, and the question no spec can ask
15237
+ // from the spec side: which files are named by nothing at all. Last in
15238
+ // the family because the second one reads what all the others named.
15239
+ typeRealizationRule,
15240
+ unclaimedSourceRule,
15014
15241
  couplingRule,
15015
15242
  // Target-language fit in two questions: what a CONTRACT may name, and what
15016
15243
  // a NARRATIVE may describe.
@@ -15043,7 +15270,7 @@ var init_repository = __esm({
15043
15270
 
15044
15271
  // src/core/source-analysis.ts
15045
15272
  function emptyCodeModel() {
15046
- return { files: [], projectRoot: "" };
15273
+ return { files: [], projectRoot: "", rootFiles: [] };
15047
15274
  }
15048
15275
  function stripComments(text2, patterns) {
15049
15276
  let out = text2;
@@ -15283,7 +15510,8 @@ function walkExact(ts, sourceText, fileName) {
15283
15510
  ts.forEachChild(node, visit);
15284
15511
  };
15285
15512
  visit(sf);
15286
- return { declared, anchors, exported, imports, reexports, starExports, complexity, calls, mutableBindings };
15513
+ const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
15514
+ return { declared, anchors, exported, imports, reexports, starExports, complexity, calls, mutableBindings, reexportOnly };
15287
15515
  }
15288
15516
  function resolveRelativeModule(fromFile, specifier) {
15289
15517
  if (!specifier.startsWith(".")) return null;
@@ -15341,15 +15569,65 @@ function looksBinary(buffer) {
15341
15569
  const probe2 = buffer.subarray(0, Math.min(buffer.length, 4096));
15342
15570
  return probe2.includes(0);
15343
15571
  }
15344
- function buildCodeModel(implementations, projectRoot2) {
15572
+ function walkSourceRoots(sourceRoots, exclude, projectRoot2) {
15573
+ const found = [];
15574
+ const seen = /* @__PURE__ */ new Set();
15575
+ const excluded = exclude.map(pathKey);
15576
+ const isExcluded = (key) => excluded.some((e) => key === e || key.startsWith(`${e}/`));
15577
+ const record2 = (absolute) => {
15578
+ const key = pathKey(path13.relative(projectRoot2, absolute));
15579
+ if (seen.has(key) || isExcluded(key)) return;
15580
+ if (!EXTENSION_LANGUAGE[path13.extname(key).toLowerCase()]) return;
15581
+ seen.add(key);
15582
+ found.push(key);
15583
+ };
15584
+ const descend = (dir) => {
15585
+ let entries;
15586
+ try {
15587
+ entries = fs10.readdirSync(dir, { withFileTypes: true });
15588
+ } catch {
15589
+ return;
15590
+ }
15591
+ for (const entry of entries.slice().sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
15592
+ const child = path13.join(dir, entry.name);
15593
+ if (entry.isDirectory()) {
15594
+ if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
15595
+ if (isExcluded(pathKey(path13.relative(projectRoot2, child)))) continue;
15596
+ descend(child);
15597
+ } else if (entry.isFile()) {
15598
+ record2(child);
15599
+ }
15600
+ }
15601
+ };
15602
+ for (const root of sourceRoots) {
15603
+ const key = pathKey(root);
15604
+ if (path13.isAbsolute(key) || path13.normalize(key).split(path13.sep)[0] === "..") continue;
15605
+ const absolute = path13.resolve(projectRoot2, key);
15606
+ if (path13.relative(projectRoot2, absolute).startsWith("..")) continue;
15607
+ if (isExcluded(key)) continue;
15608
+ let stat;
15609
+ try {
15610
+ stat = fs10.statSync(absolute);
15611
+ } catch {
15612
+ continue;
15613
+ }
15614
+ if (stat.isDirectory()) descend(absolute);
15615
+ else if (stat.isFile()) record2(absolute);
15616
+ }
15617
+ return found;
15618
+ }
15619
+ function buildCodeModel(implementations, types, projectRoot2, sourceRoots = [], exclude = []) {
15345
15620
  const files = [];
15346
15621
  const seen = /* @__PURE__ */ new Set();
15347
15622
  const exactCache = /* @__PURE__ */ new Map();
15623
+ const rootFiles = walkSourceRoots(sourceRoots, exclude, projectRoot2);
15348
15624
  const declaredPaths = [];
15349
15625
  for (const impl of implementations) {
15350
15626
  declaredPaths.push(...implementationSourceFiles(impl));
15351
15627
  if (impl.simPath) declaredPaths.push(impl.simPath);
15352
15628
  }
15629
+ for (const type of types) declaredPaths.push(...typeSourceFiles(type));
15630
+ declaredPaths.push(...rootFiles);
15353
15631
  for (const declared of declaredPaths) {
15354
15632
  const sourcePath = pathKey(declared);
15355
15633
  if (seen.has(sourcePath)) continue;
@@ -15398,7 +15676,8 @@ function buildCodeModel(implementations, projectRoot2) {
15398
15676
  reexports: [...facts.reexports],
15399
15677
  functionComplexity: Object.fromEntries(facts.complexity),
15400
15678
  functionCalls: Object.fromEntries([...facts.calls].map(([k, v]) => [k, [...v]])),
15401
- topLevelMutableBindings: [...facts.mutableBindings]
15679
+ topLevelMutableBindings: [...facts.mutableBindings],
15680
+ reexportOnly: facts.reexportOnly
15402
15681
  };
15403
15682
  } catch {
15404
15683
  analyzed = analyzeGeneric(text2);
@@ -15413,7 +15692,7 @@ function buildCodeModel(implementations, projectRoot2) {
15413
15692
  }
15414
15693
  files.push({ path: sourcePath, status: "analyzed", language, ...analyzed });
15415
15694
  }
15416
- return { files, projectRoot: projectRoot2 };
15695
+ return { files, projectRoot: projectRoot2, rootFiles };
15417
15696
  }
15418
15697
  var fs10, path13, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
15419
15698
  var init_source_analysis = __esm({
@@ -22396,7 +22675,14 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
22396
22675
  const implementations = loadImplementationSpecs();
22397
22676
  const types = loadTypeSpecs();
22398
22677
  const surfaceSnapshots = loadSurfaceSnapshots();
22399
- const codeModel = buildCodeModel(implementations, getProjectRoot());
22678
+ const conformance = rules?.conformance;
22679
+ const codeModel = buildCodeModel(
22680
+ implementations,
22681
+ types,
22682
+ getProjectRoot(),
22683
+ conformance?.sourceRoots ?? [],
22684
+ conformance?.exclude ?? []
22685
+ );
22400
22686
  const statusBearing = treatAllAsComplete ? [...subsystems, ...components, ...interfaces, ...implementations] : settledStatusBearing({ subsystems, components, interfaces, implementations });
22401
22687
  const statusSnapshot = statusBearing.map((s) => s.status);
22402
22688
  for (const s of statusBearing) s.status = "complete";
@@ -26827,6 +27113,7 @@ __export(server_exports, {
26827
27113
  captureBuildStamp: () => captureBuildStamp,
26828
27114
  createMcpServer: () => createMcpServer,
26829
27115
  isBuildStale: () => isBuildStale,
27116
+ markStale: () => markStale,
26830
27117
  startMcpServer: () => startMcpServer,
26831
27118
  statusFamilyContext: () => statusFamilyContext
26832
27119
  });
@@ -26907,6 +27194,12 @@ function json(value) {
26907
27194
  function errText(message) {
26908
27195
  return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
26909
27196
  }
27197
+ function structured(content, data) {
27198
+ return { content: [{ type: "text", text: content }], structuredContent: data };
27199
+ }
27200
+ function jsonStructured(value) {
27201
+ return structured(JSON.stringify(value, null, 2), value);
27202
+ }
26910
27203
  function renderChangeReport(report2) {
26911
27204
  const lines = [report2.dryRun ? `DRY RUN \u2014 nothing was written. ${report2.summary}` : report2.summary];
26912
27205
  for (const change of report2.changes) {
@@ -26920,6 +27213,13 @@ function renderChangeReport(report2) {
26920
27213
  if (report2.notices.length) lines.push("", "NOTICE:", ...report2.notices.map((n) => `- ${n}`));
26921
27214
  return lines.join("\n");
26922
27215
  }
27216
+ function writeReceipt(sentence, receipt) {
27217
+ const noticeBlock = receipt.notices.length ? `
27218
+
27219
+ NOTICE:
27220
+ - ${receipt.notices.join("\n- ")}` : "";
27221
+ return structured(`${sentence}${noticeBlock}`, receipt);
27222
+ }
26923
27223
  function captureBuildStamp(entryPath) {
26924
27224
  try {
26925
27225
  const s = fs20.statSync(entryPath);
@@ -26937,16 +27237,19 @@ function isBuildStale(stamp) {
26937
27237
  return false;
26938
27238
  }
26939
27239
  }
26940
- function withStaleWarning(result) {
26941
- if (!isBuildStale(SERVER_BUILD_STAMP)) return result;
26942
- const first = result.content?.[0];
27240
+ function markStale(result) {
27241
+ const marked = result.structuredContent ? { ...result, structuredContent: { ...result.structuredContent, staleServer: true } } : result;
27242
+ const first = marked.content?.[0];
26943
27243
  if (first && first.type === "text") {
26944
27244
  return {
26945
- ...result,
26946
- content: [{ ...first, text: `${first.text}${STALE_SERVER_WARNING}` }, ...result.content.slice(1)]
27245
+ ...marked,
27246
+ content: [{ ...first, text: `${first.text}${STALE_SERVER_WARNING}` }, ...marked.content.slice(1)]
26947
27247
  };
26948
27248
  }
26949
- return result;
27249
+ return marked;
27250
+ }
27251
+ function withStaleWarning(result) {
27252
+ return isBuildStale(SERVER_BUILD_STAMP) ? markStale(result) : result;
26950
27253
  }
26951
27254
  function reg(server, name, config, cb) {
26952
27255
  const guarded = (args) => {
@@ -26958,8 +27261,9 @@ function reg(server, name, config, cb) {
26958
27261
  server.registerTool(name, strictConfig, guarded);
26959
27262
  }
26960
27263
  function statusForCreate(label, stated, existing) {
26961
- if (stated === void 0) return { status: "draft" };
26962
- const held = existing?.status ?? void 0;
27264
+ const stored = existing?.status;
27265
+ const held = stored !== void 0 && STATUS_ORDER.includes(stored) ? stored : void 0;
27266
+ if (stated === void 0) return { status: held ?? "draft" };
26963
27267
  if (held === void 0) return { status: stated };
26964
27268
  const rank = (s) => STATUS_ORDER.indexOf(s);
26965
27269
  if (rank(stated) < rank(held)) {
@@ -27275,8 +27579,9 @@ function createMcpServer(options = {}) {
27275
27579
  server,
27276
27580
  "sdd_initialize_system",
27277
27581
  {
27278
- description: "Initialize the L0 System Specification (system.yaml). Re-running it on an existing system RE-AUTHORS it: the fields above are replaced, and everything this tool cannot express (databases, the project gateway publicInterfaces, diagram defaults) is carried forward.",
27279
- inputSchema: systemInput
27582
+ description: "Initialize the L0 System Specification (system.yaml). Re-running it on an existing system RE-AUTHORS it: the fields above are replaced, and everything this tool cannot express (databases, the project gateway publicInterfaces, diagram defaults) is carried forward. The answer carries a write receipt as structured content beside the sentence \u2014 what was written, and whether a system spec already existed \u2014 so a caller never has to read English to find out.",
27583
+ inputSchema: systemInput,
27584
+ outputSchema: specWriteReceiptOutput
27280
27585
  },
27281
27586
  ({ name, vision, boundaries, globalRequirements, targetLanguage }) => {
27282
27587
  try {
@@ -27298,11 +27603,10 @@ function createMcpServer(options = {}) {
27298
27603
  next.updatedAt = now;
27299
27604
  saveSystemSpec2(next);
27300
27605
  const notices = existing ? rewriteNotices({ label: `System spec "${existing.name}"`, carried, cleared, removed: [] }) : [];
27301
- const noticeBlock = notices.length ? `
27302
-
27303
- NOTICE:
27304
- - ${notices.join("\n- ")}` : "";
27305
- return text(`Successfully ${existing ? "re-authored" : "initialized"} L0 System Spec for "${name}".${noticeBlock}`);
27606
+ return writeReceipt(
27607
+ `Successfully ${existing ? "re-authored" : "initialized"} L0 System Spec for "${name}".`,
27608
+ { kind: "system", id: "system", name, replacedExisting: Boolean(existing), notices }
27609
+ );
27306
27610
  } catch (e) {
27307
27611
  return errText(String(e));
27308
27612
  }
@@ -27339,8 +27643,9 @@ NOTICE:
27339
27643
  server,
27340
27644
  "sdd_add_subsystem",
27341
27645
  {
27342
- description: "Add an L1 Subsystem / Service under the system boundary. publicInterfaces should bind each entry to the component that realizes it (the subsystem's published surface); if components do not exist yet, add them later with sdd_set_public_interfaces. Re-running it on an existing id RE-AUTHORS it: the fields above are replaced, lint/ext are carried forward, and the stored status is kept unless this input states a higher one.",
27343
- inputSchema: subsystemInput
27646
+ description: "Add an L1 Subsystem / Service under the system boundary. publicInterfaces should bind each entry to the component that realizes it (the subsystem's published surface); if components do not exist yet, add them later with sdd_set_public_interfaces. Re-running it on an existing id RE-AUTHORS it: the fields above are replaced, lint/ext are carried forward, and the stored status is kept unless this input states a higher one. The answer carries a write receipt as structured content beside the sentence \u2014 the status written, whether a spec already held the id, and the child project directory a chained subsystem scaffolded.",
27647
+ inputSchema: subsystemInput,
27648
+ outputSchema: specWriteReceiptOutput
27344
27649
  },
27345
27650
  ({ id, name, description, publicInterfaces, projectPath, targetLanguage, profile, designDepth, trustedLinks, lifecycle, status: status2 }) => {
27346
27651
  try {
@@ -27371,17 +27676,24 @@ NOTICE:
27371
27676
  const cleared = clearedByOmission(existing, spec, subsystemInputFields);
27372
27677
  if (existing) spec.createdAt = existing.createdAt;
27373
27678
  const notices = existing ? rewriteNotices({ label: `Subsystem "${id}"`, carried: ["createdAt", ...carried], cleared, removed: [] }) : [];
27374
- const noticeBlock = notices.length ? `
27375
-
27376
- NOTICE:
27377
- - ${notices.join("\n- ")}` : "";
27679
+ const receipt = {
27680
+ kind: "subsystem",
27681
+ id,
27682
+ name,
27683
+ replacedExisting: Boolean(existing),
27684
+ status: resolved.status,
27685
+ notices
27686
+ };
27378
27687
  if (projectPath && projectPath.trim() !== "") {
27379
27688
  const { createChainedSubsystem: createChainedSubsystem3 } = requireProvision();
27380
27689
  createChainedSubsystem3(spec, name);
27381
- return text(`Successfully added external subsystem "${name}" (${id}) and scaffolded its child project at ${projectPath}.${noticeBlock}`);
27690
+ return writeReceipt(
27691
+ `Successfully added external subsystem "${name}" (${id}) and scaffolded its child project at ${projectPath}.`,
27692
+ { ...receipt, scaffoldedProjectPath: projectPath }
27693
+ );
27382
27694
  }
27383
27695
  saveSubsystemSpec2(spec);
27384
- return text(`Successfully added L1 Subsystem Spec "${name}" (${id}).${noticeBlock}`);
27696
+ return writeReceipt(`Successfully added L1 Subsystem Spec "${name}" (${id}).`, receipt);
27385
27697
  } catch (e) {
27386
27698
  return errText(String(e));
27387
27699
  }
@@ -27577,8 +27889,9 @@ NOTICE:
27577
27889
  server,
27578
27890
  "sdd_add_component",
27579
27891
  {
27580
- description: `Add an L2 Component under a subsystem. componentType is a building block (Portal, Orchestrator, Supervisor, Actor, Store, Index, Query, Registry, Adapter, Observer) or the pattern Repository. Specialist and Gateway are retired (STEREOTYPE_RETIRED) and cannot be authored: logic is an Orchestrator with a dependencyClass (pure | read; unset = a workflow), and a gateway is a Portal with the gateway variant (set variant with sdd_update_spec). Patterns set "owns" (their private member blocks); all components set "dependsOn" (collaborators \u2014 facades or standalone blocks). Held/persisted state (configs, permissions, sessions, caches): model the Repository recipe \u2014 a Store + Registry (write) + Index (read), plus a Query for computed reads over the Store, owned by a Repository facade consumers depend on; a deliberately standalone Store is the sanctioned lightweight form (workflow-layer consumers + lint.allow on UNOWNED_STORE). Never hold state as fields inside an Orchestrator because a Store link was refused. Re-running it on an existing id RE-AUTHORS it: the fields above are replaced (an omitted array is CLEARED), while lint.allow, a Portal's auth, variant, patterns and externalLinks are carried forward \u2014 edit those with sdd_update_spec. The stored status is kept unless this input states a higher one.`,
27581
- inputSchema: componentInput
27892
+ description: `Add an L2 Component under a subsystem. componentType is a building block (Portal, Orchestrator, Supervisor, Actor, Store, Index, Query, Registry, Adapter, Observer) or the pattern Repository. Specialist and Gateway are retired (STEREOTYPE_RETIRED) and cannot be authored: logic is an Orchestrator with a dependencyClass (pure | read; unset = a workflow), and a gateway is a Portal with the gateway variant (set variant with sdd_update_spec). Patterns set "owns" (their private member blocks); all components set "dependsOn" (collaborators \u2014 facades or standalone blocks). Held/persisted state (configs, permissions, sessions, caches): model the Repository recipe \u2014 a Store + Registry (write) + Index (read), plus a Query for computed reads over the Store, owned by a Repository facade consumers depend on; a deliberately standalone Store is the sanctioned lightweight form (workflow-layer consumers + lint.allow on UNOWNED_STORE). Never hold state as fields inside an Orchestrator because a Store link was refused. Re-running it on an existing id RE-AUTHORS it: the fields above are replaced (an omitted array is CLEARED), while lint.allow, a Portal's auth, variant, patterns and externalLinks are carried forward \u2014 edit those with sdd_update_spec. The stored status is kept unless this input states a higher one. The answer carries a write receipt as structured content beside the sentence \u2014 the status written, whether a spec already held the id, and every gate notice the write raised, each as its own entry.`,
27893
+ inputSchema: componentInput,
27894
+ outputSchema: specWriteReceiptOutput
27582
27895
  },
27583
27896
  ({ id, name, description, subsystem, componentType, owns, dependsOn, portalType, basePath, dispatch, durability, dependencyClass, emits, subscribesTo, ext, status: status2 }) => {
27584
27897
  try {
@@ -27621,11 +27934,10 @@ NOTICE:
27621
27934
  removed: []
27622
27935
  }));
27623
27936
  }
27624
- const noticeBlock = notices.length ? `
27625
-
27626
- NOTICE:
27627
- - ${notices.join("\n- ")}` : "";
27628
- return text(`Successfully ${existing ? "re-authored" : "added"} L2 Component Spec "${name}" (${id}, ${componentType}).${noticeBlock}`);
27937
+ return writeReceipt(
27938
+ `Successfully ${existing ? "re-authored" : "added"} L2 Component Spec "${name}" (${id}, ${componentType}).`,
27939
+ { kind: "component", id, name, replacedExisting: Boolean(existing), status: resolved.status, notices }
27940
+ );
27629
27941
  } catch (e) {
27630
27942
  return errText(String(e));
27631
27943
  }
@@ -27635,12 +27947,14 @@ NOTICE:
27635
27947
  name: import_zod11.z.string(),
27636
27948
  description: import_zod11.z.string(),
27637
27949
  signature: import_zod11.z.string(),
27638
- returns: import_zod11.z.string(),
27950
+ returns: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The type the method answers with")),
27639
27951
  params: import_zod11.z.array(import_zod11.z.object({
27640
27952
  name: import_zod11.z.string(),
27641
- type: import_zod11.z.string().describe('A primitive/builtin or a defined type id (e.g. "billing.Invoice")'),
27953
+ type: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The parameter's type")),
27642
27954
  description: import_zod11.z.string().optional(),
27643
- optional: import_zod11.z.boolean().optional()
27955
+ optional: import_zod11.z.boolean().optional().describe(
27956
+ '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.'
27957
+ )
27644
27958
  }).strict()).optional().describe("Structured parameters \u2014 authoritative for type checking (the prose signature becomes display-only). Strongly preferred."),
27645
27959
  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)"),
27646
27960
  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"),
@@ -27669,8 +27983,9 @@ NOTICE:
27669
27983
  server,
27670
27984
  "sdd_define_interface",
27671
27985
  {
27672
- description: "Define an L3 Contract / Interface with method signatures for a component. Prefer supplying structured `params` per method \u2014 they are the authoritative source for type checking (the free-form signature string then becomes display-only and is never heuristically parsed). A method declares the finding codes it reports in `findings` ({code, severity, summary}); each code must be anchored in the method's source file, as a string literal or a property-access name (UNREALIZED_FINDING). Re-defining an existing id REPLACES the method list: a method left out of the input is REMOVED (and reported); spec-level lint/ext and each method's endpoint binding are carried forward, and the stored status is kept unless this input states a higher one.",
27673
- inputSchema: interfaceInput
27986
+ description: "Define an L3 Contract / Interface with method signatures for a component. Prefer supplying structured `params` per method \u2014 they are the authoritative source for type checking (the free-form signature string then becomes display-only and is never heuristically parsed). A method declares the finding codes it reports in `findings` ({code, severity, summary}); each code must be anchored in the method's source file, as a string literal or a property-access name (UNREALIZED_FINDING). Re-defining an existing id REPLACES the method list: a method left out of the input is REMOVED (and reported); spec-level lint/ext and each method's endpoint binding are carried forward, and the stored status is kept unless this input states a higher one. The answer carries a write receipt as structured content beside the sentence \u2014 the status written, whether a spec already held the id, and the notices a restatement raised, each as its own entry.",
27987
+ inputSchema: interfaceInput,
27988
+ outputSchema: specWriteReceiptOutput
27674
27989
  },
27675
27990
  ({ id, name, description, component, methods, status: status2 }) => {
27676
27991
  try {
@@ -27713,11 +28028,10 @@ NOTICE:
27713
28028
  removedSuffix: " (endpoint bindings included)"
27714
28029
  }));
27715
28030
  }
27716
- const noticeBlock = notices.length ? `
27717
-
27718
- NOTICE:
27719
- - ${notices.join("\n- ")}` : "";
27720
- return text(`Successfully ${existing ? "re-authored" : "defined"} L3 Interface Contract "${name}" (${id}).${noticeBlock}`);
28031
+ return writeReceipt(
28032
+ `Successfully ${existing ? "re-authored" : "defined"} L3 Interface Contract "${name}" (${id}).`,
28033
+ { kind: "interface", id, name, replacedExisting: Boolean(existing), status: resolved.status, notices }
28034
+ );
27721
28035
  } catch (e) {
27722
28036
  return errText(String(e));
27723
28037
  }
@@ -27862,8 +28176,9 @@ NOTICE:
27862
28176
  server,
27863
28177
  "sdd_write_narrative",
27864
28178
  {
27865
- description: "Write L4 Concrete Implementation spec containing L5 method narratives. Narratives are a FLAT ordered step list; flow steps (branch/switch/loop/try/parallel/jump/return/throw) jump by step number \u2014 blocks are just skipped regions. Steps may declare a `label` anchor, and every jump field has a *Label twin (toLabel, onTrueLabel, endLabel, \u2026) resolved to step numbers at write time \u2014 prefer labels over hand-counted numbers; an unresolvable label rejects the write. Detail dial per method: full (narrative required) | calls-only (call choreography suffices) | intent (prose instead of steps); omitted = stereotype default (Portal/Observer/Adapter: calls-only, Store/Index/Registry: intent, else full). Conformance dial per method or spec: declared | anchored | off \u2014 how strictly structural conformance requires contract methods to be realized in their source file (omitted = Portal: anchored, else declared). A method whose body lives in its own file names it in the method's sourcePath; the implementation's sourcePath is the default for every method that names none. Re-authoring an existing id REPLACES the method list: a method left out of the input is REMOVED together with its narrative (and reported); spec-level lint/ext are carried forward, and the stored status is kept unless this input states a higher one.",
27866
- inputSchema: implInput
28179
+ description: "Write L4 Concrete Implementation spec containing L5 method narratives. Narratives are a FLAT ordered step list; flow steps (branch/switch/loop/try/parallel/jump/return/throw) jump by step number \u2014 blocks are just skipped regions. Steps may declare a `label` anchor, and every jump field has a *Label twin (toLabel, onTrueLabel, endLabel, \u2026) resolved to step numbers at write time \u2014 prefer labels over hand-counted numbers; an unresolvable label rejects the write. Detail dial per method: full (narrative required) | calls-only (call choreography suffices) | intent (prose instead of steps); omitted = stereotype default (Portal/Observer/Adapter: calls-only, Store/Index/Registry: intent, else full). Conformance dial per method or spec: declared | anchored | off \u2014 how strictly structural conformance requires contract methods to be realized in their source file (omitted = Portal: anchored, else declared). A method whose body lives in its own file names it in the method's sourcePath; the implementation's sourcePath is the default for every method that names none. Re-authoring an existing id REPLACES the method list: a method left out of the input is REMOVED together with its narrative (and reported); spec-level lint/ext are carried forward, and the stored status is kept unless this input states a higher one. The answer carries a write receipt as structured content beside the sentence \u2014 the status written, whether a spec already held the id, and the notices a restatement raised, each as its own entry.",
28180
+ inputSchema: implInput,
28181
+ outputSchema: specWriteReceiptOutput
27867
28182
  },
27868
28183
  ({ id, name, description, contract, sourcePath, simPath, technologies, detail, conformance, methods, status: status2 }) => {
27869
28184
  try {
@@ -27919,11 +28234,10 @@ NOTICE:
27919
28234
  removedSuffix: " (L5 narratives included)"
27920
28235
  }));
27921
28236
  }
27922
- const noticeBlock = notices.length ? `
27923
-
27924
- NOTICE:
27925
- - ${notices.join("\n- ")}` : "";
27926
- return text(`Successfully saved L4 Implementation Spec "${name}" (${id}) with method narratives.${noticeBlock}`);
28237
+ return writeReceipt(
28238
+ `Successfully saved L4 Implementation Spec "${name}" (${id}) with method narratives.`,
28239
+ { kind: "implementation", id, name, replacedExisting: Boolean(existing), status: resolved.status, notices }
28240
+ );
27927
28241
  } catch (e) {
27928
28242
  return errText(String(e));
27929
28243
  }
@@ -27938,13 +28252,20 @@ NOTICE:
27938
28252
  group: import_zod11.z.string().optional().describe("Optional logical group ID to organize this type in subfolders"),
27939
28253
  fields: import_zod11.z.array(import_zod11.z.object({
27940
28254
  name: import_zod11.z.string(),
27941
- type: import_zod11.z.string(),
28255
+ type: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The field's type")),
27942
28256
  description: import_zod11.z.string().optional(),
27943
28257
  optional: import_zod11.z.boolean().optional(),
27944
28258
  key: import_zod11.z.enum(["primary", "unique", "foreign"]).optional().describe("Identity marker (PK/unique/FK) for ERD and database schema derivation"),
27945
28259
  references: import_zod11.z.string().optional().describe('For foreign keys, the referenced type/table id and optional field, e.g. "invoice.id"')
27946
28260
  }).strict()).optional().describe('Data fields (type is a primitive or a qualified type id, e.g. "billing.Invoice")'),
27947
- methods: import_zod11.z.array(import_zod11.z.object({ name: import_zod11.z.string(), signature: import_zod11.z.string(), returns: import_zod11.z.string(), description: import_zod11.z.string().optional() }).strict()).optional().describe("Pure intrinsic methods only"),
28261
+ methods: import_zod11.z.array(import_zod11.z.object({
28262
+ name: import_zod11.z.string(),
28263
+ signature: import_zod11.z.string(),
28264
+ returns: import_zod11.z.string().describe(TYPE_REF_GRAMMAR("The type the method answers with")),
28265
+ description: import_zod11.z.string().optional(),
28266
+ sourcePath: import_zod11.z.string().optional().describe("Source file realizing this method when the type's own sourcePath does not hold it \u2014 a pure type method often lives apart from the declaration"),
28267
+ symbol: import_zod11.z.string().optional().describe("Code-level name realizing this method when it differs from the method name, e.g. narrative_step.foreignFields realized by narrativeStepForeignFields")
28268
+ }).strict()).optional().describe("Pure intrinsic methods only"),
27948
28269
  componentClass: import_zod11.z.string().optional().describe("Optional component id that implements or owns this logical entity"),
27949
28270
  invariants: import_zod11.z.array(import_zod11.z.object({
27950
28271
  id: import_zod11.z.string().describe("Stable invariant id, unique within the entity"),
@@ -27952,17 +28273,20 @@ NOTICE:
27952
28273
  }).strict()).optional().describe("Declared domain invariants (entities): every write-effect method of the componentClass must carry a narrative step asserting each (assertsInvariants) \u2014 declarations checked, enforcement never proven"),
27953
28274
  database: import_zod11.z.string().optional().describe("Optional database id for table-schema types"),
27954
28275
  table: import_zod11.z.string().optional().describe("Optional database table name for table-schema types"),
27955
- linkedEntity: import_zod11.z.string().optional().describe("Optional logical entity id represented by this table-schema type")
28276
+ linkedEntity: import_zod11.z.string().optional().describe("Optional logical entity id represented by this table-schema type"),
28277
+ 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)"),
28278
+ 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')
27956
28279
  };
27957
28280
  const typeInputFields = Object.keys(typeInput);
27958
28281
  reg(
27959
28282
  server,
27960
28283
  "sdd_add_type",
27961
28284
  {
27962
- description: "Define an entity or value-object type (the data components operate on). Entities are owned by a subsystem; shared value objects 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. Re-defining an existing id REPLACES fields/methods/invariants (an omitted list is CLEARED, and a dropped member is reported); lint/ext are carried forward.",
27963
- inputSchema: typeInput
28285
+ description: "Define an entity or value-object type (the data components operate on). Entities are owned by a subsystem; shared value objects 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. 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. 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.",
28286
+ inputSchema: typeInput,
28287
+ outputSchema: specWriteReceiptOutput
27964
28288
  },
27965
- ({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity }) => {
28289
+ ({ kind, id, name, description, subsystem, group, fields, methods, componentClass, invariants, database, table, linkedEntity, sourcePath, symbol }) => {
27966
28290
  try {
27967
28291
  const { loadTypeSpec: loadTypeSpec2, saveTypeSpec: saveTypeSpec2 } = requireSpecs();
27968
28292
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -27988,6 +28312,8 @@ NOTICE:
27988
28312
  ...database ? { database } : {},
27989
28313
  ...table ? { table } : {},
27990
28314
  ...linkedEntity ? { linkedEntity } : {},
28315
+ ...sourcePath ? { sourcePath } : {},
28316
+ ...symbol ? { symbol } : {},
27991
28317
  createdAt: now,
27992
28318
  updatedAt: now
27993
28319
  };
@@ -28007,11 +28333,10 @@ NOTICE:
28007
28333
  removedNoun: "member"
28008
28334
  }));
28009
28335
  }
28010
- const noticeBlock = notices.length ? `
28011
-
28012
- NOTICE:
28013
- - ${notices.join("\n- ")}` : "";
28014
- return text(`Successfully ${existing ? "re-authored" : "defined"} ${kind} type "${name}" (${id}).${noticeBlock}`);
28336
+ return writeReceipt(
28337
+ `Successfully ${existing ? "re-authored" : "defined"} ${kind} type "${name}" (${id}).`,
28338
+ { kind: "type", id, name, replacedExisting: Boolean(existing), notices }
28339
+ );
28015
28340
  } catch (e) {
28016
28341
  return errText(String(e));
28017
28342
  }
@@ -28021,11 +28346,12 @@ NOTICE:
28021
28346
  server,
28022
28347
  "sdd_validate_tree",
28023
28348
  {
28024
- description: "Validate the SDD spec tree, checking parent references, contract compatibility, narratives, and component type boundaries. Supports scoping and recursion controls.",
28349
+ description: "Validate the SDD spec tree, checking parent references, contract compatibility, narratives, and component type boundaries. Supports scoping and recursion controls. Findings come back as structured content too, under the schema this tool declares \u2014 errors and warnings already split, each with its code, severity, message and the spec it concerns \u2014 so a caller filters them as objects instead of parsing the JSON text block and hoping its shape holds.",
28025
28350
  inputSchema: {
28026
28351
  subsystem: import_zod11.z.string().optional().describe("Only validate the specified subsystem (granular)"),
28027
28352
  recursive: import_zod11.z.boolean().optional().describe("Whether to recursively validate subprojects (default: true)")
28028
- }
28353
+ },
28354
+ outputSchema: validateTreeOutput
28029
28355
  },
28030
28356
  ({ subsystem, recursive }) => {
28031
28357
  try {
@@ -28038,7 +28364,7 @@ NOTICE:
28038
28364
  scopeSubsystem: subsystem,
28039
28365
  recursive: recursive ?? true
28040
28366
  });
28041
- return json({
28367
+ return jsonStructured({
28042
28368
  valid: result.valid,
28043
28369
  errors: result.issues.filter((i) => i.severity === "error"),
28044
28370
  warnings: result.issues.filter((i) => i.severity === "warning"),
@@ -28053,17 +28379,19 @@ NOTICE:
28053
28379
  server,
28054
28380
  "sdd_get_spec",
28055
28381
  {
28056
- description: `Get/read the parsed JSON contents of a specific spec from the spec tree. Returns structural contents without file system path searching. Pass "methods" to read only the named methods of a contract, an implementation or a type \u2014 a 45-method spec fetched whole to look at one of them is the read side of the same waste a restatement is on the write side; the answer then carries a "partialResult" marker naming what was left out, and must never be re-authored from. For a variant-tagged COMPONENT the result also carries a derived, read-only "variantGuidance" (the variant's base, its implementation guidance, and the same-variant sibling components to implement alike) \u2014 it is resolved from the variant registry, not part of the spec, so never write it back.`,
28382
+ description: `Get/read the parsed JSON contents of a specific spec from the spec tree. Returns structural contents without file system path searching. Pass "methods" to read only the named methods of a contract, an implementation or a type \u2014 a 45-method spec fetched whole to look at one of them is the read side of the same waste a restatement is on the write side; the answer then carries a "partialResult" marker naming what was left out, and must never be re-authored from. For a variant-tagged COMPONENT the result also carries a derived, read-only "variantGuidance" (the variant's base, its implementation guidance, and the same-variant sibling components to implement alike) \u2014 it is resolved from the variant registry, not part of the spec, so never write it back. The structured content carries the same answer with the two derived markers KEPT SEPARATE from the stored spec ({kind, id, spec, partialResult?, variantGuidance?}), so nothing derived can be mistaken for something stored; the text block folds them in as it always has.`,
28057
28383
  inputSchema: {
28058
28384
  kind: import_zod11.z.enum(["system", "subsystem", "component", "interface", "implementation", "type"]).describe("The kind of specification"),
28059
28385
  id: import_zod11.z.string().describe('The identifier of the spec to fetch (the L0 system spec is a singleton \u2014 pass the system name or "system")'),
28060
28386
  methods: import_zod11.z.array(import_zod11.z.string().min(1)).optional().describe("Return only these methods by name; every other field of the spec comes back unchanged. Only an interface, an implementation or a type declares methods \u2014 asking for one elsewhere is refused, as is a name the spec does not declare (the answer names the ones it does). Omit it for the whole spec.")
28061
- }
28387
+ },
28388
+ outputSchema: getSpecOutput
28062
28389
  },
28063
28390
  ({ kind, id, methods }) => {
28064
28391
  try {
28065
28392
  const specs = requireSpecs();
28066
28393
  let result = null;
28394
+ let partial = null;
28067
28395
  switch (kind) {
28068
28396
  case "system":
28069
28397
  result = specs.loadSystemSpec();
@@ -28100,21 +28428,26 @@ NOTICE:
28100
28428
  );
28101
28429
  }
28102
28430
  const kept = declared.filter((m) => methods.includes(String(m?.name)));
28103
- result = {
28104
- ...result,
28105
- methods: kept,
28106
- partialResult: {
28107
- shown: kept.map((m) => String(m?.name)),
28108
- omitted: names.length - kept.length,
28109
- warning: `PARTIAL: ${names.length - kept.length} of this spec's ${names.length} methods are not in this answer. Never re-author from it \u2014 sdd_define_interface and sdd_write_narrative REPLACE the method list, so every method missing here would be removed from the spec.`
28110
- }
28431
+ partial = {
28432
+ shown: kept.map((m) => String(m?.name)),
28433
+ omitted: names.length - kept.length,
28434
+ warning: `PARTIAL: ${names.length - kept.length} of this spec's ${names.length} methods are not in this answer. Never re-author from it \u2014 sdd_define_interface and sdd_write_narrative REPLACE the method list, so every method missing here would be removed from the spec.`
28111
28435
  };
28436
+ result = { ...result, methods: kept };
28112
28437
  }
28113
- if (kind === "component") {
28114
- const guidance = resolveComponentVariantGuidance(result);
28115
- if (guidance) return json({ ...result, variantGuidance: guidance });
28116
- }
28117
- return json(result);
28438
+ const guidance = kind === "component" ? resolveComponentVariantGuidance(result) : null;
28439
+ const folded = {
28440
+ ...result,
28441
+ ...partial ? { partialResult: partial } : {},
28442
+ ...guidance ? { variantGuidance: guidance } : {}
28443
+ };
28444
+ return structured(JSON.stringify(folded, null, 2), {
28445
+ kind,
28446
+ id,
28447
+ spec: result,
28448
+ ...partial ? { partialResult: partial } : {},
28449
+ ...guidance ? { variantGuidance: guidance } : {}
28450
+ });
28118
28451
  } catch (e) {
28119
28452
  return errText(String(e));
28120
28453
  }
@@ -28168,11 +28501,13 @@ NOTICE:
28168
28501
  id: import_zod11.z.string().describe("The ID of the spec to update (namespaced if needed)"),
28169
28502
  delta: import_zod11.z.record(import_zod11.z.any()).describe(`The partial fields to merge into the spec. ARRAYS UPSERT, they do not replace: an array whose elements carry an identity is merged element-by-element, so a delta naming ONE element leaves the others intact. Identity is "name" or "id" by default, and per field: dispatch by "capability", lifecycle by phase+component+method, emits/subscribesTo by topic+event, trustedLinks by "subsystem", invariants and patterns by "id", lint.allow by "code", an interface method's findings by "code", boundaries by "name", globalRequirements by "description", switch cases by "value", try catches by "error". Identity merging applies at EVERY depth, including an array INSIDE an element (a method's params, a step's catches). Add "action: 'delete'" (or "remove: true") alongside that identity to REMOVE an element \u2014 including a stale lint allow. Arrays of plain STRINGS (owns, dependsOn, guarantees) carry no per-element identity and are replaced wholesale; pass [] to clear any array outright. To REMOVE an optional field entirely, list it in "unset": e.g. {"unset": ["basePath", "variant"]} \u2014 passing null/undefined means "no change" (they are skipped), and writing "" would leave the field present but empty, which is a different and usually wrong spec. Unsetting a required field is refused by schema validation, which names it. For narrative steps, match by "stepNumber" and use "action: 'insert'" (shifts subsequent steps up) or "action: 'delete'" (shifts subsequent steps down and removes it). Step entries apply in ASCENDING stepNumber order, each against the numbering the earlier entries of the SAME delta left behind \u2014 delete step 3 and step 7 becomes step 6 \u2014 so prefer labels, and restate the step's "label" or "description" on a delete to have it checked against the step actually addressed. Renumbering RELOCATES every flow jump field (onTrueStep/onFalseStep/cases.step/defaultStep/endStep/catches.step/finallyStep/toStep) in the same narrative. A delete is REJECTED when the narrative has no such step, when a jump still targets it (retarget the referrers first), when a restated label/description does not match, or when it is a loop/try/parallel header whose body would be left standing (retype the header first to dissolve the region, then delete it). Changing a step's "type" REBUILDS it for the new type: its description and label are kept and every field the new type cannot carry is dropped (returned as a NOTICE); a delta that retypes AND sets such a field is refused. A step delta is also refused when it carries a marker the merge does not recognise: a non-boolean "remove", an "action" that is neither "insert" nor "delete", a "captureJumps" outside an insert, or no "stepNumber" to address. Inserting AT a jump target relocates those jumps past the inserted step by default (a NOTICE is returned) \u2014 add "captureJumps": true on the inserted step to retarget entry jumps onto it (loop/try endStep region tails always relocate with the body and are never captured). Every jump field has a "*Label" twin (toLabel, onFalseLabel, endLabel, \u2026, and "label" on a cases/catches entry) resolved against step labels AFTER the merge, so a delta may anchor on a label only pre-existing steps carry; a label the delta supplies REPLACES the stored number it twins, while setting the number and its label together in one delta is refused as a contradiction. Reference ids in deltas may use LOCAL names \u2014 they are qualified against the spec's namespace exactly as the loader would. Per-spec lint suppression: set "lint: { allow: [{ code, reason }] }" to silence a WARNING code on this spec only (errors always surface; stale allows are flagged). This delta is deliberately OPEN below its top level \u2014 the shapes nest further than a schema here should restate \u2014 so a key that is not a field at its depth is not refused, it is NAMED BACK under NO EFFECT in the answer, together with any value the spec already held and any "unset" that removed nothing. Read that list: it is where a nested typo shows up.`),
28170
28503
  dryRun: import_zod11.z.boolean().optional().describe("Ask what this delta WOULD do instead of doing it. The whole write runs, the candidate gate included, and the answer is the change report it would have produced \u2014 marked DRY RUN, with nothing stamped and not one byte of the stored file moved. Use it before a delta that renumbers a long narrative.")
28171
- }
28504
+ },
28505
+ outputSchema: specChangeReportOutput
28172
28506
  },
28173
28507
  ({ kind, id, delta, dryRun }) => {
28174
28508
  try {
28175
- return text(renderChangeReport(updateSpecGated(kind, id, delta, dryRun)));
28509
+ const report2 = updateSpecGated(kind, id, delta, dryRun);
28510
+ return structured(renderChangeReport(report2), report2);
28176
28511
  } catch (e) {
28177
28512
  return errText(String(e));
28178
28513
  }
@@ -28376,7 +28711,7 @@ async function startMcpServer() {
28376
28711
  } catch {
28377
28712
  }
28378
28713
  }
28379
- var import_mcp, import_stdio, import_zod11, import_types3, fs20, path27, import_url, SERVER_BUILD_STAMP, STALE_SERVER_WARNING, SPEC_WRITE_TOOLS, listChangedEmitters, STORE_MANAGED_FIELDS, STATUS_ORDER, statusInput, ALWAYS_CARRIED_FIELDS, SKILL_RESOURCE_MIME, AGENT_BRIEF_SCHEME;
28714
+ var import_mcp, import_stdio, import_zod11, import_types3, fs20, path27, import_url, TYPE_REF_GRAMMAR, staleServerOutput, SPEC_KINDS, STATUS_ORDER, specWriteReceiptOutput, specChangeOutput, specChangeReportOutput, validationIssueOutput, validateTreeOutput, getSpecOutput, SERVER_BUILD_STAMP, STALE_SERVER_WARNING, SPEC_WRITE_TOOLS, listChangedEmitters, STORE_MANAGED_FIELDS, statusInput, ALWAYS_CARRIED_FIELDS, SKILL_RESOURCE_MIME, AGENT_BRIEF_SCHEME;
28380
28715
  var init_server = __esm({
28381
28716
  "src/mcp/server.ts"() {
28382
28717
  "use strict";
@@ -28407,6 +28742,105 @@ var init_server = __esm({
28407
28742
  init_agent_resolver();
28408
28743
  init_budget_policy();
28409
28744
  init_surfaces();
28745
+ 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.`;
28746
+ staleServerOutput = {
28747
+ staleServer: import_zod11.z.boolean().optional().describe(
28748
+ "True when the wairon build on disk changed after this server started \u2014 the structured twin of the text answer's STALE SERVER banner. Writes through a stale process can silently drop fields a newer schema introduced: restart the MCP session before editing further."
28749
+ )
28750
+ };
28751
+ SPEC_KINDS = ["system", "subsystem", "component", "interface", "implementation", "type"];
28752
+ STATUS_ORDER = ["draft", "design", "complete"];
28753
+ specWriteReceiptOutput = {
28754
+ kind: import_zod11.z.enum(SPEC_KINDS).describe("The spec kind written."),
28755
+ id: import_zod11.z.string().describe('The id the spec is stored under; "system" for the L0 singleton.'),
28756
+ name: import_zod11.z.string().describe("The spec's display name as written."),
28757
+ replacedExisting: import_zod11.z.boolean().describe(
28758
+ "True when a spec already held this id and the call re-authored it in place rather than adding one. The notices then say what was carried forward, removed or cleared by the restatement."
28759
+ ),
28760
+ status: import_zod11.z.enum(STATUS_ORDER).optional().describe(
28761
+ "The lifecycle status written. Absent for a type, which carries none."
28762
+ ),
28763
+ notices: import_zod11.z.array(import_zod11.z.string()).describe(
28764
+ "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."
28765
+ ),
28766
+ scaffoldedProjectPath: import_zod11.z.string().optional().describe(
28767
+ "The child project directory a chained subsystem's create scaffolded; absent for every other write."
28768
+ ),
28769
+ ...staleServerOutput
28770
+ };
28771
+ specChangeOutput = {
28772
+ path: import_zod11.z.string().describe("The dotted path the change addresses, with names and indexes."),
28773
+ change: import_zod11.z.enum(["set", "added", "removed", "cleared"]).describe("What happened at that path."),
28774
+ before: import_zod11.z.string().optional().describe("The previous value, summarized; absent when there was none."),
28775
+ after: import_zod11.z.string().optional().describe("The new value, summarized; absent when there is none.")
28776
+ };
28777
+ specChangeReportOutput = {
28778
+ kind: import_zod11.z.enum(SPEC_KINDS).describe("The spec kind written."),
28779
+ id: import_zod11.z.string().describe("The stored spec's id."),
28780
+ written: import_zod11.z.boolean().describe("False when the merged spec equals what is stored: nothing reached disk."),
28781
+ dryRun: import_zod11.z.boolean().describe(
28782
+ "True when the caller asked what the delta WOULD do. `written` is false for a dry run exactly as it is for a delta that changed nothing, and only this field tells the two apart."
28783
+ ),
28784
+ changes: import_zod11.z.array(import_zod11.z.object(specChangeOutput)).describe(
28785
+ "Every change the write made; empty exactly when `written` is false."
28786
+ ),
28787
+ ineffective: import_zod11.z.array(import_zod11.z.string()).describe(
28788
+ "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."
28789
+ ),
28790
+ notices: import_zod11.z.array(import_zod11.z.string()).describe("Store placement notices, gate warnings and delta notices."),
28791
+ summary: import_zod11.z.string().describe("One line for people."),
28792
+ ...staleServerOutput
28793
+ };
28794
+ validationIssueOutput = {
28795
+ severity: import_zod11.z.enum(["error", "warning"]).describe("The finding's severity after project overrides."),
28796
+ code: import_zod11.z.string().describe("The rule code, UPPER_SNAKE \u2014 the stable handle to filter and suppress by."),
28797
+ message: import_zod11.z.string().describe("What is wrong, named."),
28798
+ agentId: import_zod11.z.string().optional().describe("The agent the finding concerns, when it concerns one."),
28799
+ specId: import_zod11.z.string().optional().describe("The spec the finding concerns, when it concerns one."),
28800
+ draftContext: import_zod11.z.boolean().optional().describe(
28801
+ "True when the finding was raised against a draft/design spec \u2014 what the --ci gate waives."
28802
+ ),
28803
+ surfaceResolved: import_zod11.z.boolean().optional().describe(
28804
+ "True when the finding was verified against a vendored surface snapshot, so it is a contract verdict rather than a resolution failure."
28805
+ )
28806
+ };
28807
+ validateTreeOutput = {
28808
+ valid: import_zod11.z.boolean().describe("False when the tree holds at least one error."),
28809
+ errors: import_zod11.z.array(import_zod11.z.object(validationIssueOutput)).describe("Every finding of severity error."),
28810
+ warnings: import_zod11.z.array(import_zod11.z.object(validationIssueOutput)).describe("Every finding of severity warning."),
28811
+ resolvedThrough: import_zod11.z.object({
28812
+ root: import_zod11.z.string().describe("The top root that was validated."),
28813
+ scope: import_zod11.z.string().describe("The mount chain the verdict was scoped to.")
28814
+ }).optional().describe(
28815
+ "Present when a chained subproject's verdict was resolved through its parent; absent when the tree was validated on its own."
28816
+ ),
28817
+ ...staleServerOutput
28818
+ };
28819
+ getSpecOutput = {
28820
+ kind: import_zod11.z.enum(SPEC_KINDS).describe("The kind of spec read."),
28821
+ id: import_zod11.z.string().describe("The id it was read by."),
28822
+ spec: import_zod11.z.record(import_zod11.z.unknown()).describe(
28823
+ "The stored spec, exactly as the text block renders it \u2014 minus the two derived markers below, which the text folds in and this answer keeps separate so nothing derived can be mistaken for stored."
28824
+ ),
28825
+ partialResult: import_zod11.z.object({
28826
+ shown: import_zod11.z.array(import_zod11.z.string()).describe("The method names this answer carries."),
28827
+ omitted: import_zod11.z.number().describe("How many of the spec's methods are NOT in it."),
28828
+ warning: import_zod11.z.string().describe("Why it must never be re-authored from.")
28829
+ }).optional().describe(
28830
+ "Present only when `methods` filtered the read. A filtered spec is a partial one: sdd_define_interface and sdd_write_narrative REPLACE the method list, so re-authoring from it would delete every method left out."
28831
+ ),
28832
+ variantGuidance: import_zod11.z.object({
28833
+ variant: import_zod11.z.string().describe("The variant id the component declares."),
28834
+ base: import_zod11.z.string().describe("The core stereotype it specializes."),
28835
+ guidance: import_zod11.z.string().describe("How to implement a component of this variant."),
28836
+ siblings: import_zod11.z.array(import_zod11.z.string()).describe("Other components of the same variant, to implement alike."),
28837
+ target: import_zod11.z.string().optional().describe("The target language the variant scopes itself to, if any."),
28838
+ profile: import_zod11.z.string().optional().describe("The architectural profile it scopes itself to, if any.")
28839
+ }).optional().describe(
28840
+ "Derived, read-only guidance for a variant-tagged component \u2014 resolved from the variant registry, not part of the spec. Never write it back."
28841
+ ),
28842
+ ...staleServerOutput
28843
+ };
28410
28844
  SERVER_BUILD_STAMP = captureBuildStamp(__filename);
28411
28845
  STALE_SERVER_WARNING = "\n\n\u26A0 STALE SERVER: the wairon build on disk changed after this MCP server started. Restart the MCP session (e.g. /mcp reconnect) before further spec edits \u2014 writes through a stale server can silently drop fields introduced by newer schemas.";
28412
28846
  SPEC_WRITE_TOOLS = /* @__PURE__ */ new Set([
@@ -28429,7 +28863,6 @@ var init_server = __esm({
28429
28863
  ]);
28430
28864
  listChangedEmitters = /* @__PURE__ */ new WeakMap();
28431
28865
  STORE_MANAGED_FIELDS = /* @__PURE__ */ new Set(["status", "updatedAt"]);
28432
- STATUS_ORDER = ["draft", "design", "complete"];
28433
28866
  statusInput = import_zod11.z.enum(STATUS_ORDER).optional().describe(
28434
28867
  "The spec's lifecycle status. Omitted means draft for a NEW spec and the status already stored for a re-authoring, so a restatement never reopens a frozen spec. State it to author straight at design or complete instead of promoting afterwards. A status that would LOWER the stored one is refused \u2014 reopening a spec for revision is sdd_update_spec's job, which sets the demotion deliberately."
28435
28868
  );