@wairon/cli 5.1.1-dev.63 → 5.1.1-dev.65

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.63";
68
+ WAIRON_VERSION = "5.1.1-dev.65";
69
69
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
70
70
  SUPPORTED_ALIASES = ["wai"];
71
71
  SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
@@ -2376,6 +2376,23 @@ var init_specs = __esm({
2376
2376
  * name — e.g. a store's `put` realized by `saveSnapshot`.
2377
2377
  */
2378
2378
  symbol: import_zod8.z.string().optional(),
2379
+ /**
2380
+ * The exported binding a consumer imports to REACH this method, when the
2381
+ * realization is published as a value that COMPOSES it rather than exported
2382
+ * in its own right — a rule module publishes `callConformanceRule`, and the
2383
+ * method is the `check` inside it.
2384
+ *
2385
+ * `symbol` cannot say this: it names the code-level name REALIZING the
2386
+ * contract method — the function itself — so a file whose whole published
2387
+ * surface is the composing value publishes a name no contract mentions, and
2388
+ * the surface check reads an unpromised export where the design has a
2389
+ * promised one. Naming the handle here is what closes that gap.
2390
+ *
2391
+ * A declaration, never a waiver: a handle the method's own source file
2392
+ * does not actually export is reported (UNREALIZED_EXPORT_HANDLE) and
2393
+ * allows nothing, so the field can never become a free-text suppression.
2394
+ */
2395
+ exportedVia: import_zod8.z.string().optional(),
2379
2396
  /** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
2380
2397
  ext: ExtDataSchema.optional()
2381
2398
  });
@@ -2406,6 +2423,24 @@ var init_specs = __esm({
2406
2423
  * stereotypes should bind tech directly (TECH_ON_LOGIC_COMPONENT).
2407
2424
  */
2408
2425
  technologies: import_zod8.z.array(import_zod8.z.string()).optional(),
2426
+ /**
2427
+ * The parameter names THIS realization takes BEFORE the ones its contract
2428
+ * declares — a config object, a data root, the transport handles a portal is
2429
+ * handed. Supplied by whatever wires the component up, never by the caller
2430
+ * the contract describes, which is why they belong to the realization and
2431
+ * not to the contract: another realization of the same contract may hold
2432
+ * them as fields instead.
2433
+ *
2434
+ * Declared rather than guessed, because a leading parameter the contract
2435
+ * does not name is otherwise indistinguishable from one it named under a
2436
+ * different name — `seed(config)` realized as `bootstrapInstance(cfg)` reads
2437
+ * as a dropped parameter to anything that infers. A leading parameter this
2438
+ * list does not name is a finding (UNDECLARED_PARAM), and only a LEADING run
2439
+ * is dropped: one of these names appearing after the contract's own
2440
+ * parameters is not wiring, it is an argument in the middle of the caller's
2441
+ * list.
2442
+ */
2443
+ injectedParams: import_zod8.z.array(import_zod8.z.string()).optional(),
2409
2444
  methods: import_zod8.z.array(MethodImplementationSchema).default([]).superRefine((methods, ctx) => {
2410
2445
  methods.forEach((m, i) => {
2411
2446
  if (!m.calls?.length || m.narrative.length === 0) return;
@@ -8861,7 +8896,7 @@ function buildCodeIndex(model) {
8861
8896
  const f = facts.get(path71);
8862
8897
  return f?.status === "analyzed" && f.analysisGrade === "exact" ? { path: path71, facts: f } : void 0;
8863
8898
  };
8864
- const originOf = (site, from) => {
8899
+ const originOf2 = (site, from) => {
8865
8900
  const resolved = scopeOf2(site, from);
8866
8901
  if (!resolved) return NO_ORIGIN;
8867
8902
  const { path: scope, facts: f } = resolved;
@@ -8882,7 +8917,7 @@ function buildCodeIndex(model) {
8882
8917
  return NO_ORIGIN;
8883
8918
  };
8884
8919
  const possibleOriginsOf = (site, from) => {
8885
- const proven = originOf(site, from);
8920
+ const proven = originOf2(site, from);
8886
8921
  if (!site.field && !site.constructed && !site.via) return proven;
8887
8922
  const resolved = scopeOf2(site, from);
8888
8923
  if (!resolved) return proven;
@@ -8906,7 +8941,7 @@ function buildCodeIndex(model) {
8906
8941
  declarationsAt,
8907
8942
  anchorsAt: (path71) => namesAt(anchors, pathKey(path71), (f) => /* @__PURE__ */ new Set([...f.declaredNames, ...f.exportedNames, ...f.anchoredNames])),
8908
8943
  findingAnchorsAt: (path71) => namesAt(findingAnchors, pathKey(path71), (f) => new Set(f.anchoredNames)),
8909
- originOf,
8944
+ originOf: originOf2,
8910
8945
  possibleOriginsOf
8911
8946
  };
8912
8947
  }
@@ -13962,6 +13997,274 @@ var init_type_realization = __esm({
13962
13997
  }
13963
13998
  });
13964
13999
 
14000
+ // src/core/rules/conformance/type-shape.ts
14001
+ function originOf(shape) {
14002
+ if (shape.origin === "derived") return "derived shape";
14003
+ return shape.inherited ? "declared shape (which extends another)" : "declared shape";
14004
+ }
14005
+ function absence(optional) {
14006
+ return optional ? "may be absent" : "is always there";
14007
+ }
14008
+ var typeShapeRule;
14009
+ var init_type_shape = __esm({
14010
+ "src/core/rules/conformance/type-shape.ts"() {
14011
+ "use strict";
14012
+ init_models();
14013
+ typeShapeRule = {
14014
+ name: "type-shape",
14015
+ description: "Code-to-contract for the DATA: a type spec's `fields` are compared, name by name, against the shape its `sourcePath` actually declares. `typeRealization` asks whether a type EXISTS in code; nothing asked whether it is the shape the spec claims, so a type spec could describe two fields of a six-field record \u2014 and call the two that are optional required \u2014 straight through a lock and a CI gate, while the ERD, the agent briefs and every implementer read it as truth. That is the worst of the code-to-spec gaps, because a wrong signature eventually breaks at a call site and a type spec that lies is only ever read by humans and agents. Two origins answer at exact grade: a DECLARED shape, whose members the file lists, and a DERIVED one, an alias followed one hop to the object literal its schema is built from, where the keys are the members. A shape that extends another is judged on what it shows and never on what it omits, since its inherited members are not in this file to count.",
14016
+ codes: [
14017
+ {
14018
+ code: "UNREALIZED_TYPE_FIELD",
14019
+ defaultSeverity: "warning",
14020
+ summary: "A type spec declares a field the shape at its sourcePath does not carry \u2014 the spec describes data the code does not have, and every reader of the ERD and the briefs has been told it exists",
14021
+ // Measured code↔spec drift, field by field, at a site the finding names
14022
+ // — which is why every one of them hands over `parts`.
14023
+ carryable: true
14024
+ },
14025
+ {
14026
+ code: "UNDECLARED_TYPE_FIELD",
14027
+ defaultSeverity: "warning",
14028
+ summary: "The shape at a type's sourcePath carries a field the type spec does not declare \u2014 data the design never described, so no diagram draws it and no brief hands it to an implementer",
14029
+ carryable: true
14030
+ },
14031
+ {
14032
+ code: "TYPE_FIELD_OPTIONALITY",
14033
+ defaultSeverity: "warning",
14034
+ summary: "A type spec and the shape at its sourcePath disagree about whether a field may be absent \u2014 one of them is telling a reader the data is guaranteed when it is not, or the reverse",
14035
+ carryable: true
14036
+ }
14037
+ ],
14038
+ check(ctx) {
14039
+ const code = ctx.codeIndex();
14040
+ for (const type of ctx.types) {
14041
+ if (!type.sourcePath) continue;
14042
+ if (type.subsystem && ctx.isInChainedSubproject(type.subsystem)) continue;
14043
+ const file = pathKey(type.sourcePath);
14044
+ const facts = code.factsAt(file);
14045
+ if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
14046
+ const symbol = type.symbol ?? type.name;
14047
+ const shapes = facts.typeShapes;
14048
+ if (!shapes || !Object.prototype.hasOwnProperty.call(shapes, symbol)) continue;
14049
+ const shape = shapes[symbol];
14050
+ const specFields = type.fields ?? [];
14051
+ const specFieldNames = new Set(specFields.map((field) => field.name));
14052
+ const specMethodNames = new Set((type.methods ?? []).map((method2) => method2.name));
14053
+ const codeFields = new Map(shape.fields.map((field) => [field.name, field.optional]));
14054
+ const codeMethodNames = new Set(shape.methods);
14055
+ const unrealized = shape.inherited ? [] : specFields.filter((field) => !codeFields.has(field.name) && !codeMethodNames.has(field.name)).map((field) => field.name);
14056
+ if (unrealized.length > 0) {
14057
+ ctx.addIssue(
14058
+ "warning",
14059
+ "UNREALIZED_TYPE_FIELD",
14060
+ `Type "${type.id}" declares ${unrealized.length} field(s) the ${originOf(shape)} "${symbol}" in "${file}" does not carry \u2014 ${unrealized.map((name) => `"${name}"`).join(", ")}. The spec is describing data the code does not have, and nothing breaks at a call site to correct it: the ERD draws those fields and every brief hands them to an implementer as if they were there. Add them to the code, or drop them from the type.`,
14061
+ type.id,
14062
+ void 0,
14063
+ void 0,
14064
+ { at: symbol, covers: unrealized }
14065
+ );
14066
+ }
14067
+ const undeclared = shape.inherited ? [] : shape.fields.filter((field) => !specFieldNames.has(field.name) && !specMethodNames.has(field.name)).map((field) => field.name);
14068
+ if (undeclared.length > 0) {
14069
+ ctx.addIssue(
14070
+ "warning",
14071
+ "UNDECLARED_TYPE_FIELD",
14072
+ `The ${originOf(shape)} "${symbol}" in "${file}" carries ${undeclared.length} field(s) type "${type.id}" does not declare \u2014 ${undeclared.map((name) => `"${name}"`).join(", ")}. Data the design never described is data no diagram draws and no brief hands to an implementer, which is how a record grows past the model of it without anyone deciding to. Model the field, or take it out of the shape.`,
14073
+ type.id,
14074
+ void 0,
14075
+ void 0,
14076
+ { at: symbol, covers: undeclared }
14077
+ );
14078
+ }
14079
+ const disagreed = specFields.filter((field) => {
14080
+ const absentable = codeFields.get(field.name);
14081
+ return absentable !== void 0 && absentable !== !!field.optional;
14082
+ });
14083
+ if (disagreed.length > 0) {
14084
+ const told = disagreed.map((field) => `"${field.name}" (the spec says it ${absence(!!field.optional)}, the code says it ${absence(!!codeFields.get(field.name))})`).join("; ");
14085
+ ctx.addIssue(
14086
+ "warning",
14087
+ "TYPE_FIELD_OPTIONALITY",
14088
+ `Type "${type.id}" and the ${originOf(shape)} "${symbol}" in "${file}" disagree about whether ${disagreed.length} field(s) may be absent \u2014 ${told}. One of them is telling a reader the data is guaranteed when it is not, or the reverse. Absent-able is the code's word for it \u2014 a wrapper that lets the key be missing \u2014 and a wrapper that FILLS a missing key leaves the field required in the value the type finally holds, which is the shape the spec describes.`,
14089
+ type.id,
14090
+ void 0,
14091
+ void 0,
14092
+ { at: symbol, covers: disagreed.map((field) => field.name) }
14093
+ );
14094
+ }
14095
+ }
14096
+ }
14097
+ };
14098
+ }
14099
+ });
14100
+
14101
+ // src/core/rules/conformance/param-conformance.ts
14102
+ function omittable(optional) {
14103
+ return optional ? "may be left out" : "is required";
14104
+ }
14105
+ function unitOf2(parameter, position) {
14106
+ return parameter.name ?? `#${position + 1}`;
14107
+ }
14108
+ function judge(declared, realized, injected, typeAgrees) {
14109
+ let wiring = 0;
14110
+ while (wiring < realized.length && realized[wiring].name !== void 0 && injected.has(realized[wiring].name)) wiring++;
14111
+ const taken = realized.slice(wiring);
14112
+ const matched = Math.min(declared.length, taken.length);
14113
+ const found = {
14114
+ unrealized: /* @__PURE__ */ new Map(),
14115
+ undeclared: /* @__PURE__ */ new Map(),
14116
+ renamed: /* @__PURE__ */ new Map(),
14117
+ optionality: /* @__PURE__ */ new Map()
14118
+ };
14119
+ for (const param of declared.slice(matched)) {
14120
+ found.unrealized.set(param.name, { unit: param.name, told: `"${param.name}"` });
14121
+ }
14122
+ taken.slice(matched).forEach((param, index) => {
14123
+ const unit = unitOf2(param, wiring + matched + index);
14124
+ found.undeclared.set(unit, { unit, told: `"${unit}"` });
14125
+ });
14126
+ for (let position = 0; position < matched; position++) {
14127
+ const param = declared[position];
14128
+ const at = taken[position];
14129
+ if (at.name !== void 0 && at.name !== param.name && typeAgrees(param, at)) {
14130
+ found.renamed.set(`${param.name}\u2192${at.name}`, {
14131
+ unit: param.name,
14132
+ told: `"${param.name}" (the code calls it "${at.name}")`
14133
+ });
14134
+ }
14135
+ if (!!param.optional !== at.optional) {
14136
+ found.optionality.set(`${param.name}:${at.optional}`, {
14137
+ unit: param.name,
14138
+ told: `"${param.name}" (the contract says it ${omittable(!!param.optional)}, the code says it ${omittable(at.optional)})`
14139
+ });
14140
+ }
14141
+ }
14142
+ return found;
14143
+ }
14144
+ function agreed(judgements, reading) {
14145
+ const [first, ...rest] = judgements;
14146
+ return [...first[reading]].filter(([fact]) => rest.every((other) => other[reading].has(fact))).map(([, finding]) => finding);
14147
+ }
14148
+ var paramConformanceRule;
14149
+ var init_param_conformance = __esm({
14150
+ "src/core/rules/conformance/param-conformance.ts"() {
14151
+ "use strict";
14152
+ init_models();
14153
+ paramConformanceRule = {
14154
+ name: "param-conformance",
14155
+ description: "Code-to-contract for the SIGNATURE, the last of the three readings a spec-driven gate never made: a contract declares `params`, and nothing ever compared them to the parameters of the function that realizes the method. A contract could promise an argument the code does not take, take one the contract never mentions \u2014 including a secret \u2014 or name the same argument two different things, and the brief handed to an implementer would carry the contract's version. Parameters are matched by POSITION against the tail of the realization's list, and the declared type is what tells a rename from a dropped argument: `seed(config: HostConfig)` realized as `bootstrapInstance(cfg: HostConfig)` is one parameter under two names, which anything matching on names alone reads as a parameter the code lost. What a realization takes BEFORE the contract's own parameters is wiring, and it is declared on the implementation as `injectedParams` rather than inferred, because an inferred prefix cannot be told from a renamed first argument. A method the named file only CALLS is left to `methodRealization`, which already reports that the body is not here.",
14156
+ codes: [
14157
+ {
14158
+ code: "UNREALIZED_PARAM",
14159
+ defaultSeverity: "warning",
14160
+ summary: "A contract declares a parameter the function realizing the method does not take \u2014 the contract promises an argument that would go nowhere, and every brief and caller built from it passes one",
14161
+ // Measured code↔spec drift, parameter by parameter, at a site the
14162
+ // finding names — which is why every one of them hands over `parts`.
14163
+ carryable: true
14164
+ },
14165
+ {
14166
+ code: "UNDECLARED_PARAM",
14167
+ defaultSeverity: "warning",
14168
+ summary: "The realizing function takes a parameter no contract parameter names and no declared injection accounts for \u2014 an argument a caller must supply that the design never mentions",
14169
+ carryable: true
14170
+ },
14171
+ {
14172
+ code: "PARAM_NAME_MISMATCH",
14173
+ defaultSeverity: "warning",
14174
+ summary: "A contract parameter and the one realizing it agree on position and type but not on name \u2014 the contract, the ERD and every brief say one word while the code says another",
14175
+ carryable: true
14176
+ },
14177
+ {
14178
+ code: "PARAM_OPTIONALITY",
14179
+ defaultSeverity: "warning",
14180
+ summary: "A contract and its realization disagree about whether a parameter may be left out \u2014 one of them is telling a caller an argument is required when it is not, or the reverse",
14181
+ carryable: true
14182
+ }
14183
+ ],
14184
+ check(ctx) {
14185
+ const code = ctx.codeIndex();
14186
+ const codeNameOf = /* @__PURE__ */ new Map();
14187
+ for (const type of ctx.types) {
14188
+ const named = type.symbol ?? type.name;
14189
+ codeNameOf.set(type.id, named);
14190
+ if (type.subsystem) codeNameOf.set(`${type.subsystem}.${type.id}`, named);
14191
+ }
14192
+ const normalize3 = (text2) => text2.replace(/\s+/g, " ").trim();
14193
+ const typeAgrees = (declared, realized) => {
14194
+ if (!realized.type) return false;
14195
+ const stated = normalize3(declared.type);
14196
+ return stated === realized.type || codeNameOf.get(stated) === realized.type;
14197
+ };
14198
+ for (const { implementation, method: method2, sourceFile, draftContext } of ctx.implementationMethods()) {
14199
+ const contract = ctx.interfaceMap.get(implementation.contract);
14200
+ const declared = contract?.methods.find((m) => m.name === method2.name)?.params ?? [];
14201
+ if (declared.length === 0 || !sourceFile) continue;
14202
+ const file = pathKey(sourceFile);
14203
+ const facts = code.factsAt(file);
14204
+ if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
14205
+ const symbol = method2.symbol ?? method2.name;
14206
+ const signatures = facts.functionParams;
14207
+ if (!signatures || !Object.prototype.hasOwnProperty.call(signatures, symbol)) continue;
14208
+ const candidates = signatures[symbol];
14209
+ if (candidates.length === 0) continue;
14210
+ const injected = new Set(implementation.injectedParams ?? []);
14211
+ const judgements = candidates.map((realized) => judge(declared, realized, injected, typeAgrees));
14212
+ const subject = candidates.length > 1 ? `every function called "${symbol}" in "${file}"` : `the function "${symbol}" in "${file}"`;
14213
+ const opening = subject.charAt(0).toUpperCase() + subject.slice(1);
14214
+ const unrealized = agreed(judgements, "unrealized");
14215
+ if (unrealized.length > 0) {
14216
+ ctx.addIssue(
14217
+ "warning",
14218
+ "UNREALIZED_PARAM",
14219
+ `Method "${method2.name}" of contract "${implementation.contract}" declares ${unrealized.length} parameter(s) ${subject} does not take \u2014 ${unrealized.map((found) => found.told).join(", ")}. The contract is promising an argument that would go nowhere, and nothing breaks at a call site to correct it: every brief and every caller built from the contract passes one. Take the parameter in the code, or drop it from the contract.`,
14220
+ implementation.id,
14221
+ draftContext,
14222
+ void 0,
14223
+ { at: method2.name, covers: unrealized.map((found) => found.unit) }
14224
+ );
14225
+ }
14226
+ const undeclared = agreed(judgements, "undeclared");
14227
+ if (undeclared.length > 0) {
14228
+ ctx.addIssue(
14229
+ "warning",
14230
+ "UNDECLARED_PARAM",
14231
+ `${opening} realizing method "${method2.name}" of contract "${implementation.contract}" takes ${undeclared.length} parameter(s) the contract does not declare and no declared injection accounts for \u2014 ${undeclared.map((found) => found.told).join(", ")}. An argument a caller must supply that the design never mentions is how a credential ends up in a signature nobody has read against its contract. Declare it on the contract, name it in the implementation's \`injectedParams\` when whatever wires this component up supplies it (a LEADING run only), or take it out of the signature.`,
14232
+ implementation.id,
14233
+ draftContext,
14234
+ void 0,
14235
+ { at: method2.name, covers: undeclared.map((found) => found.unit) }
14236
+ );
14237
+ }
14238
+ const renamed = agreed(judgements, "renamed");
14239
+ if (renamed.length > 0) {
14240
+ ctx.addIssue(
14241
+ "warning",
14242
+ "PARAM_NAME_MISMATCH",
14243
+ `Method "${method2.name}" of contract "${implementation.contract}" and ${subject} agree on position and type but not on name for ${renamed.length} parameter(s) \u2014 ${renamed.map((f) => f.told).join("; ")}. The contract, the ERD and every brief carry one word and the code answers to another, which is a rename nobody recorded rather than a different argument \u2014 the declared type agreeing is what says so. Rename one side to the other.`,
14244
+ implementation.id,
14245
+ draftContext,
14246
+ void 0,
14247
+ { at: method2.name, covers: renamed.map((found) => found.unit) }
14248
+ );
14249
+ }
14250
+ const disagreed = agreed(judgements, "optionality");
14251
+ if (disagreed.length > 0) {
14252
+ ctx.addIssue(
14253
+ "warning",
14254
+ "PARAM_OPTIONALITY",
14255
+ `Method "${method2.name}" of contract "${implementation.contract}" and ${subject} disagree about whether ${disagreed.length} parameter(s) may be left out \u2014 ${disagreed.map((f) => f.told).join("; ")}. One of them is telling a caller an argument is required when it is not, or the reverse. Omittable is the code's word for it: a default value and a rest parameter make an argument omittable exactly as a question mark does.`,
14256
+ implementation.id,
14257
+ draftContext,
14258
+ void 0,
14259
+ { at: method2.name, covers: disagreed.map((found) => found.unit) }
14260
+ );
14261
+ }
14262
+ }
14263
+ }
14264
+ };
14265
+ }
14266
+ });
14267
+
13965
14268
  // src/core/rules/conformance/unclaimed-source.ts
13966
14269
  var unclaimedSourceRule;
13967
14270
  var init_unclaimed_source = __esm({
@@ -14015,6 +14318,133 @@ var init_unclaimed_source = __esm({
14015
14318
  }
14016
14319
  });
14017
14320
 
14321
+ // src/core/rules/conformance/export-conformance.ts
14322
+ var NOTHING_PROMISED, exportConformanceRule;
14323
+ var init_export_conformance = __esm({
14324
+ "src/core/rules/conformance/export-conformance.ts"() {
14325
+ "use strict";
14326
+ init_models();
14327
+ NOTHING_PROMISED = /* @__PURE__ */ new Set();
14328
+ exportConformanceRule = {
14329
+ name: "export-conformance",
14330
+ description: "Code-to-contract for the SURFACE: does this file publish anything the components it realizes never promised, that another component then takes? The rest of the conformance set reads contract-to-code \u2014 it asks whether the code holds what a spec claims \u2014 so a file could export whatever it liked under a component's name and nothing looked. That is one half of the same hole as a contract promising a parameter it never passes. A method's `exportedVia` names the export a consumer imports to REACH it \u2014 the value that composes it, which `symbol` cannot name because `symbol` names the function inside \u2014 and a declared handle the file does not actually export is itself a finding, so naming one can never become a free-text suppression. Both questions are asked only of a file read at exact grade, and a taker counts only when its own import specifier resolves to the file it is accused of taking from: below that nothing separates an export from a mention, or a shared word from a shared module.",
14331
+ codes: [
14332
+ {
14333
+ code: "UNDECLARED_EXPORT",
14334
+ defaultSeverity: "warning",
14335
+ summary: "A file realizing a component exports a name no contract of that component declares, and a file realizing a DIFFERENT component imports it \u2014 a surface reached across a boundary that the design never promised",
14336
+ // Measured code-to-spec drift at a site the finding names, so the
14337
+ // debt register may carry it — which is why it hands over `parts`.
14338
+ carryable: true
14339
+ },
14340
+ {
14341
+ code: "UNREALIZED_EXPORT_HANDLE",
14342
+ defaultSeverity: "warning",
14343
+ summary: "An implementation method declares an `exportedVia` handle its own source file does not export \u2014 the spec names a published route to the method that nobody can import"
14344
+ // Deliberately NOT carryable, and so it hands over no `parts`: a wrong
14345
+ // handle is one string to correct, and a parking space for it could
14346
+ // only ever be a parking space for a lie.
14347
+ }
14348
+ ],
14349
+ check(ctx) {
14350
+ const code = ctx.codeIndex();
14351
+ const realization = ctx.realizationIndex();
14352
+ const promised = /* @__PURE__ */ new Map();
14353
+ const promise = (file, name) => {
14354
+ if (!name) return;
14355
+ const key = pathKey(file);
14356
+ let names = promised.get(key);
14357
+ if (!names) promised.set(key, names = /* @__PURE__ */ new Set());
14358
+ names.add(name);
14359
+ };
14360
+ for (const file of realization.paths) {
14361
+ for (const component of realization.componentsAt(file)) {
14362
+ for (const method2 of ctx.interfaceMethodsOf(component.id)) promise(file, method2.name);
14363
+ for (const impl of realization.implementationsOf(component.id)) {
14364
+ for (const method2 of impl.methods ?? []) {
14365
+ promise(file, method2.name);
14366
+ promise(file, method2.symbol);
14367
+ }
14368
+ }
14369
+ }
14370
+ }
14371
+ for (const { implementation, method: method2, sourceFile, draftContext } of ctx.implementationMethods()) {
14372
+ const handle = method2.exportedVia;
14373
+ if (!handle || !sourceFile) continue;
14374
+ const file = pathKey(sourceFile);
14375
+ const facts = code.factsAt(file);
14376
+ if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
14377
+ if (facts.exportedNames.includes(handle)) {
14378
+ promise(file, handle);
14379
+ continue;
14380
+ }
14381
+ ctx.addIssue(
14382
+ "warning",
14383
+ "UNREALIZED_EXPORT_HANDLE",
14384
+ `Method "${method2.name}" in implementation "${implementation.id}" declares exportedVia "${handle}", but "${file}" exports no such name. A handle names the export a consumer imports to REACH the method \u2014 the value that composes it, which \`symbol\` cannot name because \`symbol\` names the function inside \u2014 so one the file does not publish promises a route nobody can import. It allows nothing either: every export of this file is still read against the contracts. Name the binding the file actually exports, export it, or drop the handle.`,
14385
+ implementation.id,
14386
+ draftContext
14387
+ );
14388
+ }
14389
+ for (const type of ctx.types) {
14390
+ if (!type.sourcePath) continue;
14391
+ promise(type.sourcePath, type.symbol ?? type.name);
14392
+ for (const method2 of type.methods ?? []) {
14393
+ promise(method2.sourcePath ?? type.sourcePath, method2.symbol ?? method2.name);
14394
+ }
14395
+ }
14396
+ const takenFrom = /* @__PURE__ */ new Map();
14397
+ for (const consumer of realization.paths) {
14398
+ if (!code.exactPaths.has(consumer)) continue;
14399
+ for (const [local, binding] of Object.entries(code.factsAt(consumer)?.importBindings ?? {})) {
14400
+ if (binding.namespace) continue;
14401
+ const target = resolveImport(consumer, binding.from, code.paths);
14402
+ if (!target) continue;
14403
+ let names = takenFrom.get(target);
14404
+ if (!names) takenFrom.set(target, names = /* @__PURE__ */ new Map());
14405
+ for (const name of [local, binding.imported]) {
14406
+ if (!name) continue;
14407
+ let takers = names.get(name);
14408
+ if (!takers) names.set(name, takers = /* @__PURE__ */ new Set());
14409
+ takers.add(consumer);
14410
+ }
14411
+ }
14412
+ }
14413
+ for (const file of realization.paths) {
14414
+ const facts = code.factsAt(file);
14415
+ if (!facts || facts.status !== "analyzed" || facts.analysisGrade !== "exact") continue;
14416
+ const declared = promised.get(file) ?? NOTHING_PROMISED;
14417
+ const taken = takenFrom.get(file);
14418
+ const owners = new Set(realization.componentsAt(file).map((c) => c.id));
14419
+ const crossed = [];
14420
+ for (const name of [...new Set(facts.exportedNames)].sort()) {
14421
+ if (declared.has(name)) continue;
14422
+ const takers = taken?.get(name);
14423
+ if (!takers) continue;
14424
+ const crossing = [...takers].some(
14425
+ (taker) => taker !== file && !realization.componentsAt(taker).some((c) => owners.has(c.id))
14426
+ );
14427
+ if (crossing) crossed.push(name);
14428
+ }
14429
+ if (crossed.length === 0) continue;
14430
+ const [owner] = realization.implementationsAt(file).map((impl) => impl.id).sort();
14431
+ ctx.addIssue(
14432
+ "warning",
14433
+ "UNDECLARED_EXPORT",
14434
+ `Source file "${file}" publishes ${crossed.length} name(s) that no contract of the component(s) it realizes (${[...owners].sort().map((id) => `"${id}"`).join(", ")}) declares, and a file realizing NONE of those components imports each one FROM HERE \u2014 ${crossed.map((n) => `"${n}"`).join(", ")}. An export only this file's own components use is the module's private factoring and no concern of the design; one another component reaches for is a surface the design never promised. Declare it as a contract method, stop exporting it, or move it to the component that owns the name.`,
14435
+ owner,
14436
+ void 0,
14437
+ void 0,
14438
+ // The crossings themselves, named the way the message names them, so
14439
+ // a register entry written for today's set cannot carry tomorrow's.
14440
+ { at: file, covers: crossed }
14441
+ );
14442
+ }
14443
+ }
14444
+ };
14445
+ }
14446
+ });
14447
+
14018
14448
  // src/core/rules/conformance/carried-debt.ts
14019
14449
  var carriedDebtRule;
14020
14450
  var init_carried_debt = __esm({
@@ -15338,7 +15768,10 @@ var init_repository = __esm({
15338
15768
  init_integration_sim_wiring();
15339
15769
  init_integration_sim_coverage();
15340
15770
  init_type_realization();
15771
+ init_type_shape();
15772
+ init_param_conformance();
15341
15773
  init_unclaimed_source();
15774
+ init_export_conformance();
15342
15775
  init_carried_debt();
15343
15776
  init_coupling_health();
15344
15777
  init_signature_language_builtins();
@@ -15504,11 +15937,17 @@ var init_repository = __esm({
15504
15937
  integrationSimFileRule,
15505
15938
  integrationSimWiringRule,
15506
15939
  integrationSimCoverageRule,
15507
- // The data model's own claim on code, and the question no spec can ask
15508
- // from the spec side: which files are named by nothing at all. Last in
15509
- // the family because the second one reads what all the others named.
15940
+ // The data model's own claim on code — does the file hold the type, and is
15941
+ // it the SHAPE the spec claims — then the SIGNATURE, the third reading:
15942
+ // does the code take the arguments the contract promises. And then the
15943
+ // question no spec can ask from the spec side: which files are named by
15944
+ // nothing at all. Last in the family because that one reads what all the
15945
+ // others named.
15510
15946
  typeRealizationRule,
15947
+ typeShapeRule,
15948
+ paramConformanceRule,
15511
15949
  unclaimedSourceRule,
15950
+ exportConformanceRule,
15512
15951
  couplingRule,
15513
15952
  // Target-language fit in two questions: what a CONTRACT may name, and what
15514
15953
  // a NARRATIVE may describe.
@@ -15674,7 +16113,11 @@ function walkExact(ts, sourceText, fileName) {
15674
16113
  const typeOnlyBindings = /* @__PURE__ */ new Map();
15675
16114
  const fieldTypes = /* @__PURE__ */ new Map();
15676
16115
  const localTypes = /* @__PURE__ */ new Map();
16116
+ const typeShapes = /* @__PURE__ */ new Map();
16117
+ const functionParams = /* @__PURE__ */ new Map();
15677
16118
  const mutableBindings = /* @__PURE__ */ new Set();
16119
+ const schemaConstants = /* @__PURE__ */ new Map();
16120
+ const derivedAliases = [];
15678
16121
  const receiverKey = (site) => site.via ?? (site.field ? `this.${site.field}` : site.constructed ? `new ${site.constructed}` : "");
15679
16122
  const siteKey = (site) => `${site.member ? "m" : "b"}:${receiverKey(site)}:${site.name}`;
15680
16123
  const addBindingNames = (name) => {
@@ -15708,6 +16151,79 @@ function walkExact(ts, sourceText, fileName) {
15708
16151
  named.add(typeName);
15709
16152
  localTypes.set(name.text, named);
15710
16153
  };
16154
+ const recordDeclaredShape = (name, members, inherited) => {
16155
+ const fields = [];
16156
+ const methods = [];
16157
+ for (const member of members) {
16158
+ const memberName = member.name ? propertyNameText(member.name) : void 0;
16159
+ if (!memberName) continue;
16160
+ if (ts.isMethodSignature(member) || ts.isMethodDeclaration(member) || ts.isGetAccessorDeclaration(member)) {
16161
+ methods.push(memberName);
16162
+ } else if (ts.isPropertySignature(member) || ts.isPropertyDeclaration(member)) {
16163
+ fields.push({ name: memberName, optional: !!member.questionToken });
16164
+ }
16165
+ }
16166
+ const shape = { origin: "declared", fields, methods };
16167
+ if (inherited) shape.inherited = true;
16168
+ typeShapes.set(name, shape);
16169
+ };
16170
+ const inferredSchemaConstant = (type) => {
16171
+ if (!ts.isTypeReferenceNode(type)) return void 0;
16172
+ const reference = ts.isIdentifier(type.typeName) ? type.typeName.text : ts.isQualifiedName(type.typeName) ? type.typeName.right.text : void 0;
16173
+ if (reference !== "infer") return void 0;
16174
+ const [argument] = type.typeArguments ?? [];
16175
+ if (!argument || !ts.isTypeQueryNode(argument) || !ts.isIdentifier(argument.exprName)) return void 0;
16176
+ return argument.exprName.text;
16177
+ };
16178
+ const baseObjectLiteral = (expression) => {
16179
+ let node = expression;
16180
+ const walked = /* @__PURE__ */ new Set();
16181
+ while (node && !walked.has(node)) {
16182
+ walked.add(node);
16183
+ if (ts.isCallExpression(node)) {
16184
+ const callee = node.expression;
16185
+ if (ts.isPropertyAccessExpression(callee) && callee.name.text === "object") {
16186
+ const literal = node.arguments.find((argument) => ts.isObjectLiteralExpression(argument));
16187
+ if (literal) return literal;
16188
+ }
16189
+ node = callee;
16190
+ } else if (ts.isPropertyAccessExpression(node)) {
16191
+ node = node.expression;
16192
+ } else break;
16193
+ }
16194
+ return void 0;
16195
+ };
16196
+ const chainedCombinators = (expression) => {
16197
+ const applied = /* @__PURE__ */ new Set();
16198
+ let node = expression;
16199
+ const walked = /* @__PURE__ */ new Set();
16200
+ while (node && !walked.has(node)) {
16201
+ walked.add(node);
16202
+ if (ts.isCallExpression(node)) {
16203
+ const callee = node.expression;
16204
+ if (ts.isPropertyAccessExpression(callee)) applied.add(callee.name.text);
16205
+ node = callee;
16206
+ } else if (ts.isPropertyAccessExpression(node)) {
16207
+ node = node.expression;
16208
+ } else break;
16209
+ }
16210
+ return applied;
16211
+ };
16212
+ const schemaMembers = (literal) => {
16213
+ const fields = [];
16214
+ for (const property of literal.properties) {
16215
+ const memberName = property.name ? propertyNameText(property.name) : void 0;
16216
+ if (!memberName) continue;
16217
+ if (ts.isShorthandPropertyAssignment(property)) {
16218
+ fields.push({ name: memberName, optional: false });
16219
+ } else if (ts.isPropertyAssignment(property)) {
16220
+ const applied = chainedCombinators(property.initializer);
16221
+ const optional = (applied.has("optional") || applied.has("nullish")) && !applied.has("default");
16222
+ fields.push({ name: memberName, optional });
16223
+ }
16224
+ }
16225
+ return fields;
16226
+ };
15711
16227
  const hasExportModifier = (node) => {
15712
16228
  const mods = node.modifiers;
15713
16229
  return !!mods?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
@@ -15728,6 +16244,14 @@ function walkExact(ts, sourceText, fileName) {
15728
16244
  }
15729
16245
  return void 0;
15730
16246
  };
16247
+ const declaredParameters = (fn) => (fn.parameters ?? []).map((parameter) => {
16248
+ const fact = {
16249
+ optional: !!parameter.questionToken || !!parameter.initializer || !!parameter.dotDotDotToken
16250
+ };
16251
+ if (ts.isIdentifier(parameter.name)) fact.name = parameter.name.text;
16252
+ if (parameter.type) fact.type = parameter.type.getText(sf).replace(/\s+/g, " ").trim();
16253
+ return fact;
16254
+ });
15731
16255
  const collectFunctionFacts = (fn) => {
15732
16256
  let score = 1;
15733
16257
  const callees = /* @__PURE__ */ new Map();
@@ -15770,12 +16294,32 @@ function walkExact(ts, sourceText, fileName) {
15770
16294
  const sites = calls.get(fnName) ?? /* @__PURE__ */ new Map();
15771
16295
  for (const [key, site] of facts.callees) sites.set(key, site);
15772
16296
  calls.set(fnName, sites);
16297
+ const signatures = functionParams.get(fnName) ?? [];
16298
+ signatures.push(declaredParameters(node));
16299
+ functionParams.set(fnName, signatures);
15773
16300
  }
15774
16301
  if (ts.isPropertyDeclaration(node)) recordFieldType(node.name, node.type);
15775
16302
  else if (ts.isParameter(node) && node.parent && ts.isConstructorDeclaration(node.parent) && isParameterProperty(node)) {
15776
16303
  recordFieldType(node.name, node.type);
15777
16304
  }
15778
16305
  if (ts.isParameter(node) || ts.isVariableDeclaration(node)) recordLocalType(node.name, node.type);
16306
+ if (ts.isInterfaceDeclaration(node)) {
16307
+ recordDeclaredShape(node.name.text, node.members, (node.heritageClauses?.length ?? 0) > 0);
16308
+ } else if (ts.isClassDeclaration(node) && node.name) {
16309
+ const extendsOther = (node.heritageClauses ?? []).some(
16310
+ (clause) => clause.token === ts.SyntaxKind.ExtendsKeyword
16311
+ );
16312
+ recordDeclaredShape(node.name.text, node.members, extendsOther);
16313
+ } else if (ts.isTypeAliasDeclaration(node)) {
16314
+ if (ts.isTypeLiteralNode(node.type)) recordDeclaredShape(node.name.text, node.type.members, false);
16315
+ else {
16316
+ const constant = inferredSchemaConstant(node.type);
16317
+ if (constant) derivedAliases.push({ name: node.name.text, constant });
16318
+ }
16319
+ }
16320
+ if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && !schemaConstants.has(node.name.text)) {
16321
+ schemaConstants.set(node.name.text, node.initializer);
16322
+ }
15779
16323
  if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) {
15780
16324
  const name = node.name && ts.isIdentifier(node.name) ? node.name.text : void 0;
15781
16325
  if (name) {
@@ -15851,8 +16395,16 @@ function walkExact(ts, sourceText, fileName) {
15851
16395
  ts.forEachChild(node, visit);
15852
16396
  };
15853
16397
  visit(sf);
16398
+ for (const alias of derivedAliases) {
16399
+ if (typeShapes.has(alias.name)) continue;
16400
+ const initializer = schemaConstants.get(alias.constant);
16401
+ if (!initializer) continue;
16402
+ const literal = baseObjectLiteral(initializer);
16403
+ if (!literal) continue;
16404
+ typeShapes.set(alias.name, { origin: "derived", fields: schemaMembers(literal), methods: [] });
16405
+ }
15854
16406
  const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
15855
- return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, mutableBindings, reexportOnly };
16407
+ return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, mutableBindings, reexportOnly };
15856
16408
  }
15857
16409
  function resolveRelativeModule(fromFile, specifier) {
15858
16410
  if (!specifier.startsWith(".")) return null;
@@ -16034,6 +16586,8 @@ function buildCodeModel(implementations, types, projectRoot2, sourceRoots = [],
16034
16586
  typeOnlyBindings: Object.fromEntries(facts.typeOnlyBindings),
16035
16587
  fieldTypes: Object.fromEntries([...facts.fieldTypes].map(([k, v]) => [k, [...v]])),
16036
16588
  localTypes: Object.fromEntries([...facts.localTypes].map(([k, v]) => [k, [...v]])),
16589
+ typeShapes: Object.fromEntries(facts.typeShapes),
16590
+ functionParams: Object.fromEntries(facts.functionParams),
16037
16591
  topLevelMutableBindings: [...facts.mutableBindings],
16038
16592
  reexportOnly: facts.reexportOnly
16039
16593
  };
@@ -29639,6 +30193,7 @@ function createMcpServer(options = {}) {
29639
30193
  intent: import_zod11.z.string().optional().describe("detail: intent \u2014 behavioral prose (what it does and how it fails); substitute for a narrative"),
29640
30194
  conformance: conformanceEnum.optional().describe("Conformance tier for this method (overrides the spec default)"),
29641
30195
  symbol: import_zod11.z.string().optional().describe("Code-level name realizing this contract method in the sourcePath file, when it legitimately differs from the intent-language contract name (e.g. put realized by saveSnapshot)"),
30196
+ exportedVia: import_zod11.z.string().optional().describe("The exported binding a consumer imports to REACH this method, when the realization is published as a value that composes it rather than exported in its own right (a rule module publishes callConformanceRule, and the method is the check inside it). Not symbol, which names the function itself: this names the route to it, so the surface check reads that export as promised. A handle the method's own source file does not actually export is reported (UNREALIZED_EXPORT_HANDLE) and allows nothing"),
29642
30197
  ext: import_zod11.z.record(import_zod11.z.unknown()).optional().describe("Opaque pack/tool extension data for this method (namespaced keys) \u2014 preserved verbatim"),
29643
30198
  narrative: import_zod11.z.array(narrativeStepInput).optional(),
29644
30199
  calls: import_zod11.z.array(import_zod11.z.string().min(1)).optional().describe('The calls this method makes, when its narrative does not show them (detail: intent, or calls-only with no steps authored) \u2014 each as "<component>.<method>", the spelling the debt register and lint allows name a unit with. The narrative walk takes exactly these edges and no others, so an undeclared collaborator is NOT reached: this is what keeps unused-detection honest at a lower detail dial. Each entry is validated like a call step (the component resolves, this component declares it, the method is on its contract). Refused beside a non-empty narrative \u2014 add the call step there instead')
@@ -29652,6 +30207,7 @@ function createMcpServer(options = {}) {
29652
30207
  sourcePath: import_zod11.z.string().optional().describe("Optional: target source code file path relative to project root \u2014 for a chained subproject's implementation (qualified id), relative to that subproject's root; a path given relative to this root that lands inside the subproject is re-expressed for you"),
29653
30208
  simPath: import_zod11.z.string().optional().describe("Optional: the committed integration-sim harness file (project-relative; N:1 sharing allowed). The validator proves it exists and its import graph wires the REAL modules (this component + each direct dependency; technology adapters may stay faked) \u2014 running it is CI's job. Declaring the first simPath in a subsystem activates MISSING_INTEGRATION_SIM for its other complete non-leaf implementations"),
29654
30209
  technologies: import_zod11.z.array(import_zod11.z.string()).optional().describe(`External technologies this implementation binds to (e.g. ["mysql"]) \u2014 declares this component's ownership tree as the technology's home; references outside it are flagged (TECH_LEAKAGE) and contract identifiers must stay intent-language. Only for Adapter/Store/Registry/Index components.`),
30210
+ injectedParams: import_zod11.z.array(import_zod11.z.string()).optional().describe("The parameter names this realization takes BEFORE the ones its contract declares \u2014 a config object, a data root, the transport handles a portal is handed. Supplied by whatever wires the component up, never by the caller the contract describes, which is why they belong here and not to the contract: another realization may hold them as fields instead. Declared rather than guessed, because a leading parameter the contract does not name cannot be told from one it named under a different name (`seed(config)` realized as `bootstrapInstance(cfg)`); a leading parameter this list does not name is reported (UNDECLARED_PARAM). Only a LEADING run is dropped \u2014 one of these names appearing after the contract's own parameters is an argument in the middle of the caller's list, not wiring"),
29655
30211
  detail: detailEnum.optional().describe("Spec-level narrative detail default for all methods"),
29656
30212
  conformance: conformanceEnum.optional().describe("Spec-level conformance tier default: declared | anchored | off (omitted = stereotype default: Portal \u2192 anchored, else declared)"),
29657
30213
  methods: import_zod11.z.array(import_zod11.z.object(implMethodShape).strict()).optional().describe("Method implementations containing L5 narratives"),
@@ -29662,11 +30218,11 @@ function createMcpServer(options = {}) {
29662
30218
  server,
29663
30219
  "sdd_write_narrative",
29664
30220
  {
29665
- 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). A method whose narrative shows no steps declares the calls it makes in `calls` (one \"<component>.<method>\" each): the reachability walk takes exactly those edges and no others, so an intent-level method that reaches a collaborator must name it or that collaborator is reported unused. 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.",
30221
+ 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). A method whose narrative shows no steps declares the calls it makes in `calls` (one \"<component>.<method>\" each): the reachability walk takes exactly those edges and no others, so an intent-level method that reaches a collaborator must name it or that collaborator is reported unused. 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. Parameters the realization takes BEFORE its contract's own \u2014 a config object, a data root, a portal's transport handles \u2014 are wiring, and are declared once for the spec in injectedParams; a leading parameter it does not name is reported against the contract (UNDECLARED_PARAM). 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.",
29666
30222
  inputSchema: implInput,
29667
30223
  outputSchema: specWriteReceiptOutput
29668
30224
  },
29669
- ({ id, name, description, contract, sourcePath, simPath, technologies, detail, conformance, methods, status: status2 }) => {
30225
+ ({ id, name, description, contract, sourcePath, simPath, technologies, injectedParams, detail, conformance, methods, status: status2 }) => {
29670
30226
  try {
29671
30227
  const { loadInterfaceSpec: loadInterfaceSpec2, loadImplementationSpec: loadImplementationSpec2, saveImplementationSpec: saveImplementationSpec2 } = requireSpecs();
29672
30228
  const intf = loadInterfaceSpec2(contract);
@@ -29698,6 +30254,7 @@ function createMcpServer(options = {}) {
29698
30254
  sourcePath,
29699
30255
  simPath,
29700
30256
  technologies,
30257
+ injectedParams,
29701
30258
  detail,
29702
30259
  conformance,
29703
30260
  // Post-resolution every cases/catches entry has its numeric step —