@sigloch/contracts 0.7.0 → 0.8.0

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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * RC conformance rules (CR-GC-253) — graph↔code resolution, the RESOLUTION
3
- * twins of the presence rules R-20 (codeRef) / R-19 (testRef).
3
+ * twins of the presence rules R-20 (FUNC realRef) / R-19 (testRef).
4
4
  *
5
5
  * Rules stay in THIS library (one rule base per onto set — no rule definitions
6
6
  * in executor codebases). They are pure functions over (graph, facts): all
@@ -8,7 +8,7 @@
8
8
  * from the repo), so this module stays I/O-free and browser-bundlable.
9
9
  *
10
10
  * CodeFacts semantics: `files` is keyed by repo-relative path and MUST contain
11
- * an entry for every file referenced by a codeRef/testRef the extractor saw.
11
+ * an entry for every file referenced by a realRef/testRef the extractor saw.
12
12
  * A MISSING key is treated like `exists:false` — an extractor gap must surface
13
13
  * loudly as a violation, never as a silent pass.
14
14
  *
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * RC conformance rules (CR-GC-253) — graph↔code resolution, the RESOLUTION
3
- * twins of the presence rules R-20 (codeRef) / R-19 (testRef).
3
+ * twins of the presence rules R-20 (FUNC realRef) / R-19 (testRef).
4
4
  *
5
5
  * Rules stay in THIS library (one rule base per onto set — no rule definitions
6
6
  * in executor codebases). They are pure functions over (graph, facts): all
@@ -8,14 +8,14 @@
8
8
  * from the repo), so this module stays I/O-free and browser-bundlable.
9
9
  *
10
10
  * CodeFacts semantics: `files` is keyed by repo-relative path and MUST contain
11
- * an entry for every file referenced by a codeRef/testRef the extractor saw.
11
+ * an entry for every file referenced by a realRef/testRef the extractor saw.
12
12
  * A MISSING key is treated like `exists:false` — an extractor gap must surface
13
13
  * loudly as a violation, never as a silent pass.
14
14
  *
15
15
  * @sigloch/contracts/se — single source of truth for SE validation rules.
16
16
  */
17
17
  import { z } from 'zod/v4';
18
- import { CodeRefSchema, TestRefSchema, SchemaRefSchema } from './ontology.js';
18
+ import { RealRefSchema, TestRefSchema } from './ontology.js';
19
19
  /** Parser facts about one source file (extracted by the executor). */
20
20
  export const FileFactsSchema = z.object({
21
21
  /** File exists on disk (repo-relative path). */
@@ -50,10 +50,11 @@ export const CodeFactsSchema = z.object({
50
50
  importEdges: z.array(ImportEdgeSchema).optional(),
51
51
  });
52
52
  const missingFile = (facts, file) => facts.files[file]?.exists !== true;
53
- // RC-01: every valid codeRef must resolve — file on disk, symbol declared in it.
54
- // Presence is R-20's concern (incl. concept/external/decomposition-parent
53
+ // RC-01: every valid FUNC realRef must resolve — file on disk, symbol declared in
54
+ // it. Presence is R-20's concern (incl. concept/external/decomposition-parent
55
55
  // exemptions, CR-GC-244); RC-01 only judges bindings that exist. `lang:'prompt'`
56
- // is realized by a skill file — file-exists is the whole binding.
56
+ // is realized by a skill file — file-exists is the whole binding. A realRef with
57
+ // no `symbol` (CR-228) is also file-only: resolution stops at file-exists.
57
58
  function codeRefMustResolve(graph, facts) {
58
59
  const violations = [];
59
60
  for (const el of graph.elements) {
@@ -61,7 +62,7 @@ function codeRefMustResolve(graph, facts) {
61
62
  continue;
62
63
  if (el.attributes?.concept === true || el.attributes?.external === true)
63
64
  continue;
64
- const parsed = CodeRefSchema.safeParse(el.attributes?.codeRef);
65
+ const parsed = RealRefSchema.safeParse(el.attributes?.realRef);
65
66
  if (!parsed.success)
66
67
  continue; // no/invalid binding → R-20 territory
67
68
  const ref = parsed.data;
@@ -70,20 +71,20 @@ function codeRefMustResolve(graph, facts) {
70
71
  rule_id: 'RC-01',
71
72
  severity: 'error',
72
73
  element_id: el.id,
73
- message: `${el.id} codeRef.file '${ref.file}' does not exist on disk`,
74
+ message: `${el.id} realRef.file '${ref.file}' does not exist on disk`,
74
75
  fix_hint: 'Re-realize the FUNC (graph_realize) against the current source tree, or fix the moved/renamed file path',
75
76
  context: { element_type: el.type, element_name: el.name },
76
77
  });
77
78
  continue;
78
79
  }
79
- if (ref.lang === 'prompt')
80
- continue; // skill file existing IS the binding
80
+ if (ref.lang === 'prompt' || ref.symbol === undefined)
81
+ continue; // skill/file-only binding IS the binding
81
82
  if (!facts.files[ref.file].declaredSymbols.includes(ref.symbol)) {
82
83
  violations.push({
83
84
  rule_id: 'RC-01',
84
85
  severity: 'error',
85
86
  element_id: el.id,
86
- message: `${el.id} codeRef.symbol '${ref.symbol}' is not declared in '${ref.file}'`,
87
+ message: `${el.id} realRef.symbol '${ref.symbol}' is not declared in '${ref.file}'`,
87
88
  fix_hint: 'The symbol was renamed or removed — re-realize the FUNC against the current code',
88
89
  context: { element_type: el.type, element_name: el.name },
89
90
  });
@@ -131,11 +132,11 @@ function testRefMustResolve(graph, facts) {
131
132
  }
132
133
  return violations;
133
134
  }
134
- // RC-03: every valid SCHEMA schemaRef must resolve — file on disk, symbol declared
135
- // in it (CR-211). Presence (a SCHEMA with NO schemaRef) is the concern of the
136
- // R-26 presence rule, not RC-03; concept/external SCHEMAs are exempt there and
137
- // here. Severity error, like RC-01: a bound-but-broken schema IS a defect. Fires
138
- // until schemaRefs exist (it only judges SCHEMAs that carry one).
135
+ // RC-03: every valid SCHEMA realRef must resolve — file on disk, symbol declared
136
+ // in it (CR-211, unified CR-228). Presence (a SCHEMA with NO realRef) is the
137
+ // concern of the R-26 presence rule, not RC-03; concept/external SCHEMAs are exempt
138
+ // there and here. Severity error, like RC-01: a bound-but-broken schema IS a defect.
139
+ // A symbol-less realRef stops at file-exists. Fires until realRefs exist.
139
140
  function schemaRefMustResolve(graph, facts) {
140
141
  const violations = [];
141
142
  for (const el of graph.elements) {
@@ -143,7 +144,7 @@ function schemaRefMustResolve(graph, facts) {
143
144
  continue;
144
145
  if (el.attributes?.concept === true || el.attributes?.external === true)
145
146
  continue;
146
- const parsed = SchemaRefSchema.safeParse(el.attributes?.schemaRef);
147
+ const parsed = RealRefSchema.safeParse(el.attributes?.realRef);
147
148
  if (!parsed.success)
148
149
  continue; // no/invalid binding → R-26 territory
149
150
  const ref = parsed.data;
@@ -152,18 +153,20 @@ function schemaRefMustResolve(graph, facts) {
152
153
  rule_id: 'RC-03',
153
154
  severity: 'error',
154
155
  element_id: el.id,
155
- message: `${el.id} schemaRef.file '${ref.file}' does not exist on disk`,
156
+ message: `${el.id} realRef.file '${ref.file}' does not exist on disk`,
156
157
  fix_hint: 'Re-bind the SCHEMA (graph_realize) to the current source tree, or fix the moved/renamed file path',
157
158
  context: { element_type: el.type, element_name: el.name },
158
159
  });
159
160
  continue;
160
161
  }
162
+ if (ref.symbol === undefined)
163
+ continue; // file-only binding
161
164
  if (!facts.files[ref.file].declaredSymbols.includes(ref.symbol)) {
162
165
  violations.push({
163
166
  rule_id: 'RC-03',
164
167
  severity: 'error',
165
168
  element_id: el.id,
166
- message: `${el.id} schemaRef.symbol '${ref.symbol}' is not a declared export in '${ref.file}'`,
169
+ message: `${el.id} realRef.symbol '${ref.symbol}' is not a declared export in '${ref.file}'`,
167
170
  fix_hint: 'The schema export was renamed or removed — re-bind the SCHEMA against the current code',
168
171
  context: { element_type: el.type, element_name: el.name },
169
172
  });
@@ -174,11 +177,11 @@ function schemaRefMustResolve(graph, facts) {
174
177
  // RC-04: a bound SCHEMA that the graph says is realized at an interface must
175
178
  // actually be parsed there (CR-211). The graph gives the check LOCATIONS: FUNCs
176
179
  // io-connected to a FLOW whose data format IS this SCHEMA (FUNC ─io→ FLOW
177
- // ─relation→ SCHEMA) and that carry a resolvable codeRef. If ≥1 such FUNC exists
178
- // but NONE of their codeRef files import AND parse (`.parse`/`.safeParse`) the
180
+ // ─relation→ SCHEMA) and that carry a resolvable realRef. If ≥1 such FUNC exists
181
+ // but NONE of their realRef files import AND parse (`.parse`/`.safeParse`) the
179
182
  // schema symbol, the modelled validation is missing. Severity warn (the parse may
180
183
  // legitimately sit in a framework layer, not the FUNC's own file). Skips when the
181
- // SCHEMA has no schemaRef, or no io-connected realized FUNC to check against.
184
+ // SCHEMA has no realRef (or a symbol-less one), or no io-connected realized FUNC.
182
185
  function schemaRefMustBeUsed(graph, facts) {
183
186
  const typeOf = new Map(graph.elements.map(e => [e.id, e.type]));
184
187
  const violations = [];
@@ -187,8 +190,8 @@ function schemaRefMustBeUsed(graph, facts) {
187
190
  continue;
188
191
  if (el.attributes?.concept === true || el.attributes?.external === true)
189
192
  continue;
190
- const parsed = SchemaRefSchema.safeParse(el.attributes?.schemaRef);
191
- if (!parsed.success)
193
+ const parsed = RealRefSchema.safeParse(el.attributes?.realRef);
194
+ if (!parsed.success || parsed.data.symbol === undefined)
192
195
  continue;
193
196
  const ref = parsed.data;
194
197
  // The FLOWs whose data format is this SCHEMA (FLOW ─relation→ SCHEMA).
@@ -203,11 +206,11 @@ function schemaRefMustBeUsed(graph, facts) {
203
206
  ((flowIds.has(t.target) && typeOf.get(t.source) === 'FUNC') ||
204
207
  (flowIds.has(t.source) && typeOf.get(t.target) === 'FUNC')))
205
208
  .map(t => (flowIds.has(t.target) ? t.source : t.target)));
206
- // Realized FUNCs among them: a resolvable codeRef whose file exists.
209
+ // Realized FUNCs among them: a resolvable realRef whose file exists.
207
210
  const realizedFiles = [];
208
211
  for (const fnId of funcIds) {
209
212
  const fn = graph.elements.find(e => e.id === fnId);
210
- const cr = CodeRefSchema.safeParse(fn?.attributes?.codeRef);
213
+ const cr = RealRefSchema.safeParse(fn?.attributes?.realRef);
211
214
  if (cr.success && !missingFile(facts, cr.data.file))
212
215
  realizedFiles.push(cr.data.file);
213
216
  }
@@ -255,12 +258,12 @@ function importDriftConformance(graph, facts) {
255
258
  const modIds = graph.elements.filter(e => e.type === 'MOD').map(e => e.id);
256
259
  if (modIds.length === 0)
257
260
  return [];
258
- // 1. file → MOD. Direct codeRef bindings first.
261
+ // 1. file → MOD. Direct realRef bindings first.
259
262
  const fileToMod = new Map();
260
263
  for (const el of graph.elements) {
261
264
  if (el.type !== 'FUNC')
262
265
  continue;
263
- const cr = CodeRefSchema.safeParse(el.attributes?.codeRef);
266
+ const cr = RealRefSchema.safeParse(el.attributes?.realRef);
264
267
  if (!cr.success)
265
268
  continue;
266
269
  const modTrace = graph.traces.find(t => t.source === el.id && t.type === 'allocate' && typeOf.get(t.target) === 'MOD');
@@ -352,10 +355,10 @@ function importDriftConformance(graph, facts) {
352
355
  }
353
356
  /** All RC conformance rules — evaluated by executors that can supply CodeFacts. */
354
357
  export const CODE_CONFORMANCE_RULES = [
355
- { id: 'RC-01', name: 'codeRef resolves to a declared symbol', severity: 'error', evaluate: codeRefMustResolve },
358
+ { id: 'RC-01', name: 'FUNC realRef resolves to a declared symbol', severity: 'error', evaluate: codeRefMustResolve },
356
359
  { id: 'RC-02', name: 'testRef resolves to a runnable test', severity: 'error', evaluate: testRefMustResolve },
357
- { id: 'RC-03', name: 'schemaRef resolves to a declared export', severity: 'error', evaluate: schemaRefMustResolve },
358
- { id: 'RC-04', name: 'schemaRef is parsed at its interface', severity: 'warning', evaluate: schemaRefMustBeUsed },
360
+ { id: 'RC-03', name: 'SCHEMA realRef resolves to a declared export', severity: 'error', evaluate: schemaRefMustResolve },
361
+ { id: 'RC-04', name: 'SCHEMA realRef is parsed at its interface', severity: 'warning', evaluate: schemaRefMustBeUsed },
359
362
  { id: 'RC-05', name: 'cross-module import drift', severity: 'warning', evaluate: importDriftConformance },
360
363
  ];
361
364
  /** Run all RC rules against a graph + extracted code facts. */
@@ -3,9 +3,9 @@
3
3
  * Single source of truth for SE ontology schemas across all projects.
4
4
  */
5
5
  /** Ontology schema version (element types + trace types). */
6
- export declare const ONTOLOGY_VERSION = "3.8.0";
6
+ export declare const ONTOLOGY_VERSION = "3.9.0";
7
7
  /** Rules engine version (validation rules incl. RC conformance). */
8
- export declare const RULES_VERSION = "2.17.0";
8
+ export declare const RULES_VERSION = "2.18.0";
9
9
  /** Meta-model version (trace pattern constraints + format-e parser). */
10
10
  export declare const META_MODEL_VERSION = "1.4.0";
11
11
  export * from './ontology.js';
package/dist/se/index.js CHANGED
@@ -3,9 +3,9 @@
3
3
  * Single source of truth for SE ontology schemas across all projects.
4
4
  */
5
5
  /** Ontology schema version (element types + trace types). */
6
- export const ONTOLOGY_VERSION = '3.8.0'; // +RepoRelativePathSchema on testRef/codeRef/schemaRef .file — no absolute/`..` paths (CR-GC-255)
6
+ export const ONTOLOGY_VERSION = '3.9.0'; // realRef unifies codeRef+schemaRef (+physical-MOD CAD ref), symbol optional; testRef stays separate (CR-228 C); +RepoRelativePathSchema on testRef/realRef .file — no absolute/`..` paths (CR-GC-255)
7
7
  /** Rules engine version (validation rules incl. RC conformance). */
8
- export const RULES_VERSION = '2.17.0'; // -R-24/R-25 REQ→MOD allocation rules deleted (CR-228 A: REQ→MOD allocate no longer a valid pattern, R-18 flags residual edges); NFR-01 budget target split physical→MOD / behavioral→FCHAIN; RULE_TO_DIMENSION completeness (R-18..R-23/R-26/MS-03/CR-R04 mapped, no advisory fall-through) (CR-228 B/D); +BQ-01/02/04/06/07 base-quality rules promoted from aimpro (K2-b, completes rule consolidation); +ND-01/02 near-duplicate + AO-D01/D03/CR-01/RT-01/PH-01/CA-01/IO-01 architecture rules promoted from aimpro (K2-b); +CR-R01..04/MS-03 change-request rules promoted from aimpro (K2-b); +FM-01..03/NFR-01 FMEA + VR-01/CL-01 view rules promoted from aimpro (K2-b); +MT-01..03 architecture metrics promoted from aimpro (K2-b); +SC-01..03/UC-01..06/FC-01..03 quality rules promoted from aimpro (K2-a); +RC-05 cross-module import drift (CR-212); +R-26/RC-03/RC-04 schemaRef (CR-211); +R-22..R-25/R-10/R-20 (CR-201/202/208/209/210)
8
+ export const RULES_VERSION = '2.18.0'; // R-20/R-26/RC-01/RC-03/RC-04 read realRef (unified codeRef+schemaRef); +R-27 physical-MOD realRef presence (CR-228 C); -R-24/R-25 REQ→MOD allocation rules deleted (CR-228 A: REQ→MOD allocate no longer a valid pattern, R-18 flags residual edges); NFR-01 budget target split physical→MOD / behavioral→FCHAIN; RULE_TO_DIMENSION completeness (R-18..R-23/R-26/MS-03/CR-R04 mapped, no advisory fall-through) (CR-228 B/D); +BQ-01/02/04/06/07 base-quality rules promoted from aimpro (K2-b, completes rule consolidation); +ND-01/02 near-duplicate + AO-D01/D03/CR-01/RT-01/PH-01/CA-01/IO-01 architecture rules promoted from aimpro (K2-b); +CR-R01..04/MS-03 change-request rules promoted from aimpro (K2-b); +FM-01..03/NFR-01 FMEA + VR-01/CL-01 view rules promoted from aimpro (K2-b); +MT-01..03 architecture metrics promoted from aimpro (K2-b); +SC-01..03/UC-01..06/FC-01..03 quality rules promoted from aimpro (K2-a); +RC-05 cross-module import drift (CR-212); +R-26/RC-03/RC-04 schemaRef (CR-211); +R-22..R-25/R-10/R-20 (CR-201/202/208/209/210)
9
9
  /** Meta-model version (trace pattern constraints + format-e parser). */
10
10
  export const META_MODEL_VERSION = '1.4.0'; // -REQ→MOD allocate pattern removed (CR-228 A); +FUNC→FUNC compose (blackbox function decomposition)
11
11
  export * from './ontology.js';
@@ -8,7 +8,7 @@ export const TRACE_PATTERNS = [
8
8
  { source: 'UC', target: 'FCHAIN', type: 'compose', cardinality: '1..*', description: 'UC behavioral scenarios' },
9
9
  { source: 'UC', target: 'REQ', type: 'compose', cardinality: '1..*', description: 'UC functional/non-functional requirements' },
10
10
  { source: 'FCHAIN', target: 'FUNC', type: 'compose', cardinality: '1..*', description: 'Functions participating in chain' },
11
- { source: 'FUNC', target: 'FUNC', type: 'compose', cardinality: '0..*', description: 'Function (blackbox architecture block) decomposes into sub-functions one level deeper; leaf FUNCs carry codeRef, the parent is realized by its children' },
11
+ { source: 'FUNC', target: 'FUNC', type: 'compose', cardinality: '0..*', description: 'Function (blackbox architecture block) decomposes into sub-functions one level deeper; leaf FUNCs carry realRef, the parent is realized by its children' },
12
12
  { source: 'REQ', target: 'REQ', type: 'compose', cardinality: '0..*', description: 'Requirement decomposes into sub-requirements (incl. mitigation)' },
13
13
  // ── io (data exchange, always through FLOW) ──
14
14
  { source: 'ACTOR', target: 'FLOW', type: 'io', description: 'Actor triggers/receives via flow' },
@@ -110,40 +110,27 @@ export declare const TestRefSchema: z.ZodObject<{
110
110
  }, z.core.$strip>;
111
111
  export type TestRef = z.infer<typeof TestRefSchema>;
112
112
  /**
113
- * Code binding for a FUNC element (CR-GC-205 Item 5, ontology bump 3.6.0).
114
- * Resolves a FUNC node to the concrete code symbol that realizes it, enabling
115
- * graph<->code conformance: every non-concept/non-external FUNC must resolve to a
116
- * real symbol in its allocated MOD's file, and (via the consumer's LSP-backed
117
- * check) every cross-module-called symbol must itself be a FUNC node.
118
- * - `file` : implementation file path, e.g. `src/harness.ts` (the symbol's home).
119
- * - `symbol` : the exported symbol (function/method/class) name realizing the FUNC.
120
- * - `lang` : optional language id (default `ts`) — selects the LSP/engine the
121
- * conformance check drives, so the binding is language-agnostic.
122
- * Stored under `OntologyElement.attributes.codeRef` (additive, opt-in validation).
113
+ * Realization binding for an element (CR-228, ontology bump 3.9.0) — unifies the
114
+ * byte-identical former `codeRef` (FUNC code symbol) and `schemaRef` (SCHEMA
115
+ * Zod export), and extends to MOD (physical part → CAD/geometry artefact). The
116
+ * *type* of the pointing element disambiguates what kind of realization it is:
117
+ * FUNC→code, SCHEMA→Zod-def, physical MOD→CAD. TEST keeps its own `testRef`
118
+ * (a TEST is not just located but *executed* — the runner is the extra value).
119
+ * - `file` : realization file path, e.g. `src/harness.ts`, `part.step`.
120
+ * - `symbol` : the realizing symbol (function/class/Zod export). Optional a
121
+ * geometry artefact (CAD) has no symbol; file-exists is the binding.
122
+ * - `lang` : optional language/format id (default `ts`) selects the
123
+ * conformance engine, so the binding is language-agnostic.
124
+ * Stored under `OntologyElement.attributes.realRef` (additive, opt-in validation).
125
+ * The free-text SCHEMA `contract` attribute stays a human description, not the
126
+ * conformance basis: RC-01/RC-03 resolve this binding, RC-04 checks it is parsed.
123
127
  */
124
- export declare const CodeRefSchema: z.ZodObject<{
128
+ export declare const RealRefSchema: z.ZodObject<{
125
129
  file: z.ZodString;
126
- symbol: z.ZodString;
130
+ symbol: z.ZodOptional<z.ZodString>;
127
131
  lang: z.ZodOptional<z.ZodString>;
128
132
  }, z.core.$strip>;
129
- export type CodeRef = z.infer<typeof CodeRefSchema>;
130
- /**
131
- * Binding of a SCHEMA node to the Zod schema that defines it (CR-211), analogous
132
- * to CodeRefSchema for FUNC:
133
- * - `file` : the source file declaring the Zod schema, e.g. `src/se/ontology.ts`.
134
- * - `symbol` : the exported schema symbol, e.g. `CodeRefSchema`.
135
- * - `lang` : optional language id (default `ts`).
136
- * Stored under `OntologyElement.attributes.schemaRef`. The free-text `contract`
137
- * attribute stays as a human description but is no longer the conformance basis:
138
- * RC-03 resolves this binding to a declared export, RC-04 checks it is parsed at
139
- * the modelled interface.
140
- */
141
- export declare const SchemaRefSchema: z.ZodObject<{
142
- file: z.ZodString;
143
- symbol: z.ZodString;
144
- lang: z.ZodOptional<z.ZodString>;
145
- }, z.core.$strip>;
146
- export type SchemaRef = z.infer<typeof SchemaRefSchema>;
133
+ export type RealRef = z.infer<typeof RealRefSchema>;
147
134
  /**
148
135
  * An element (node) in the SE ontology graph.
149
136
  * `attributes` holds type-specific properties (e.g. FUNC.safety_relevant).
@@ -72,36 +72,24 @@ export const TestRefSchema = z.object({
72
72
  level: z.string().optional(),
73
73
  });
74
74
  /**
75
- * Code binding for a FUNC element (CR-GC-205 Item 5, ontology bump 3.6.0).
76
- * Resolves a FUNC node to the concrete code symbol that realizes it, enabling
77
- * graph<->code conformance: every non-concept/non-external FUNC must resolve to a
78
- * real symbol in its allocated MOD's file, and (via the consumer's LSP-backed
79
- * check) every cross-module-called symbol must itself be a FUNC node.
80
- * - `file` : implementation file path, e.g. `src/harness.ts` (the symbol's home).
81
- * - `symbol` : the exported symbol (function/method/class) name realizing the FUNC.
82
- * - `lang` : optional language id (default `ts`) — selects the LSP/engine the
83
- * conformance check drives, so the binding is language-agnostic.
84
- * Stored under `OntologyElement.attributes.codeRef` (additive, opt-in validation).
75
+ * Realization binding for an element (CR-228, ontology bump 3.9.0) — unifies the
76
+ * byte-identical former `codeRef` (FUNC code symbol) and `schemaRef` (SCHEMA
77
+ * Zod export), and extends to MOD (physical part → CAD/geometry artefact). The
78
+ * *type* of the pointing element disambiguates what kind of realization it is:
79
+ * FUNC→code, SCHEMA→Zod-def, physical MOD→CAD. TEST keeps its own `testRef`
80
+ * (a TEST is not just located but *executed* — the runner is the extra value).
81
+ * - `file` : realization file path, e.g. `src/harness.ts`, `part.step`.
82
+ * - `symbol` : the realizing symbol (function/class/Zod export). Optional a
83
+ * geometry artefact (CAD) has no symbol; file-exists is the binding.
84
+ * - `lang` : optional language/format id (default `ts`) selects the
85
+ * conformance engine, so the binding is language-agnostic.
86
+ * Stored under `OntologyElement.attributes.realRef` (additive, opt-in validation).
87
+ * The free-text SCHEMA `contract` attribute stays a human description, not the
88
+ * conformance basis: RC-01/RC-03 resolve this binding, RC-04 checks it is parsed.
85
89
  */
86
- export const CodeRefSchema = z.object({
90
+ export const RealRefSchema = z.object({
87
91
  file: RepoRelativePathSchema,
88
- symbol: z.string(),
89
- lang: z.string().optional(),
90
- });
91
- /**
92
- * Binding of a SCHEMA node to the Zod schema that defines it (CR-211), analogous
93
- * to CodeRefSchema for FUNC:
94
- * - `file` : the source file declaring the Zod schema, e.g. `src/se/ontology.ts`.
95
- * - `symbol` : the exported schema symbol, e.g. `CodeRefSchema`.
96
- * - `lang` : optional language id (default `ts`).
97
- * Stored under `OntologyElement.attributes.schemaRef`. The free-text `contract`
98
- * attribute stays as a human description but is no longer the conformance basis:
99
- * RC-03 resolves this binding to a declared export, RC-04 checks it is parsed at
100
- * the modelled interface.
101
- */
102
- export const SchemaRefSchema = z.object({
103
- file: RepoRelativePathSchema,
104
- symbol: z.string(),
92
+ symbol: z.string().optional(),
105
93
  lang: z.string().optional(),
106
94
  });
107
95
  /**
@@ -188,8 +176,8 @@ export const ELEMENT_ATTRIBUTES = {
188
176
  { key: 'timingBudgetMs', type: 'number', description: 'NFR timing budget in ms' },
189
177
  { key: 'measuredMs', type: 'number', description: 'Measured execution time in ms' },
190
178
  { key: 'sourceFile', type: 'string', description: 'Implementation source file' },
191
- { key: 'codeRef', type: 'object', description: 'Code binding {file, symbol, lang?} — the symbol that realizes this FUNC; see CodeRefSchema (CR-GC-205)' },
192
- { key: 'external', type: 'boolean', description: 'Externally-realized FUNC (e.g. a renderer in another package): exempt from the R-20 codeRef-binding requirement (CR-GC-205)' },
179
+ { key: 'realRef', type: 'object', description: 'Realization binding {file, symbol?, lang?} — the code symbol that realizes this FUNC; see RealRefSchema (CR-228, was codeRef)' },
180
+ { key: 'external', type: 'boolean', description: 'Externally-realized FUNC (e.g. a renderer in another package): exempt from the R-20 realRef-binding requirement (CR-GC-205)' },
193
181
  { key: 'concept', type: 'boolean', description: 'Concept-only FUNC: specified, no implementation yet; exempt from R-20 (CR-GC-205)' },
194
182
  ],
195
183
  UC: [
@@ -206,11 +194,15 @@ export const ELEMENT_ATTRIBUTES = {
206
194
  ],
207
195
  MOD: [
208
196
  { key: 'path', type: 'string', description: 'Source file or glob the module owns, e.g. src/harness.ts — anchors the MOD<->file mapping for graph<->code conformance (CR-GC-205)' },
197
+ { key: 'kind', type: 'string', description: "Module kind, e.g. 'physical' for a machine part (Bauteil) — physical MODs are realized by a CAD/geometry realRef (R-27), logical MODs by their FUNCs' code (CR-191/228)" },
198
+ { key: 'realRef', type: 'object', description: 'Realization binding {file, symbol?, lang?} — CAD/geometry artefact realizing a physical MOD; see RealRefSchema (CR-228, symbol optional)' },
199
+ { key: 'external', type: 'boolean', description: 'Externally-realized MOD: exempt from the R-27 realRef-presence requirement (CR-228)' },
200
+ { key: 'concept', type: 'boolean', description: 'Concept-only MOD: modelled, no realization yet; exempt from R-27 (CR-228)' },
209
201
  ],
210
202
  SCHEMA: [
211
203
  { key: 'contract', type: 'string', description: 'Free-text contract description, e.g. "@sigloch/contracts LiveUpdateEventSchema" — human hint only, not the conformance basis (CR-211)' },
212
- { key: 'schemaRef', type: 'object', description: 'Schema binding {file, symbol, lang?} — the Zod export defining this SCHEMA; see SchemaRefSchema (CR-211)' },
213
- { key: 'external', type: 'boolean', description: 'Externally-defined SCHEMA (e.g. a foreign-API contract): exempt from the schemaRef-presence requirement (CR-211)' },
214
- { key: 'concept', type: 'boolean', description: 'Concept-only SCHEMA: modelled, no Zod export yet; exempt from the schemaRef-presence requirement (CR-211)' },
204
+ { key: 'realRef', type: 'object', description: 'Realization binding {file, symbol?, lang?} — the Zod export defining this SCHEMA; see RealRefSchema (CR-228, was schemaRef)' },
205
+ { key: 'external', type: 'boolean', description: 'Externally-defined SCHEMA (e.g. a foreign-API contract): exempt from the realRef-presence requirement (CR-211)' },
206
+ { key: 'concept', type: 'boolean', description: 'Concept-only SCHEMA: modelled, no Zod export yet; exempt from the realRef-presence requirement (CR-211)' },
215
207
  ],
216
208
  };
@@ -39,7 +39,7 @@ export const RULE_TO_DIMENSION = {
39
39
  'RD-01': 'req', 'RD-02': 'req', 'RD-03': 'req',
40
40
  // trace/realization/allocation completeness rules (CR-228 D: previously unmapped → advisory fall-through)
41
41
  'R-18': 'arch', 'R-19': 'ver', 'R-20': 'arch', 'R-21': 'ver',
42
- 'R-22': 'alloc', 'R-23': 'alloc', 'R-26': 'schema',
42
+ 'R-22': 'alloc', 'R-23': 'alloc', 'R-26': 'schema', 'R-27': 'arch',
43
43
  // uc
44
44
  'UC-01': 'uc', 'UC-02': 'uc', 'UC-03': 'uc', 'UC-04': 'uc',
45
45
  'UC-05': 'uc', 'UC-06': 'uc',
package/dist/se/rules.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * @sigloch/contracts/se — single source of truth for SE validation rules.
5
5
  */
6
6
  import { z } from 'zod/v4';
7
- import { ElementType, TraceType, TestRefSchema, CodeRefSchema, SchemaRefSchema } from './ontology.js';
7
+ import { ElementType, TraceType, TestRefSchema, RealRefSchema } from './ontology.js';
8
8
  import { isValidTrace } from './meta-model.js';
9
9
  export const RuleSeverity = z.enum(['error', 'warning', 'info']);
10
10
  /** Candidate target for resolving a violation (e.g. a REQ to satisfy, a TEST to link). */
@@ -581,10 +581,10 @@ function testMustHaveRunnableBinding(graph) {
581
581
  }));
582
582
  }
583
583
  // ---------------------------------------------------------------------------
584
- // R-20: FUNC code binding (CR-GC-205 Item 5, extended by CR-210) a FUNC that
585
- // is not explicitly concept-only (attributes.concept === true) or externally
586
- // realized (attributes.external === true) counts as BOUND when it EITHER carries
587
- // a valid codeRef {file, symbol} OR is a blackbox parent whose compose→FUNC
584
+ // R-20: FUNC realRef binding (CR-GC-205 Item 5, extended by CR-210, unified CR-228)
585
+ // — a FUNC that is not explicitly concept-only (attributes.concept === true) or
586
+ // externally realized (attributes.external === true) counts as BOUND when it EITHER
587
+ // carries a valid realRef {file, symbol?} OR is a blackbox parent whose compose→FUNC
588
588
  // children are ALL (recursively) bound — the parent is realized by its children
589
589
  // (e.g. FUNC-gesture-capture, gve CR-GVE-150). A partially-bound parent fires
590
590
  // R-20 naming the still-unbound leaves in the fix_hint, so drift on a child is
@@ -598,7 +598,7 @@ function funcMustHaveCodeBinding(graph) {
598
598
  const composeFuncChildren = (id) => graph.traces
599
599
  .filter(t => t.source === id && t.type === 'compose' && funcById.has(t.target))
600
600
  .map(t => t.target);
601
- const hasCodeRef = (el) => CodeRefSchema.safeParse(el.attributes?.codeRef).success;
601
+ const hasCodeRef = (el) => RealRefSchema.safeParse(el.attributes?.realRef).success;
602
602
  const isExempt = (el) => el.attributes?.concept === true || el.attributes?.external === true;
603
603
  // A FUNC is bound iff it is exempt, has a codeRef, or is a parent whose
604
604
  // compose→FUNC children are all bound (recursive, cycle-safe).
@@ -662,8 +662,8 @@ function funcMustHaveCodeBinding(graph) {
662
662
  rule_id: 'R-20',
663
663
  severity: 'warning',
664
664
  element_id: fn.id,
665
- message: `${fn.id} is a FUNC without a valid codeRef binding`,
666
- fix_hint: 'Add attributes.codeRef {file, symbol, lang?}, or set attributes.concept:true (spec-only) / attributes.external:true (realized in another package)',
665
+ message: `${fn.id} is a FUNC without a valid realRef binding`,
666
+ fix_hint: 'Add attributes.realRef {file, symbol, lang?}, or set attributes.concept:true (spec-only) / attributes.external:true (realized in another package)',
667
667
  context: { element_type: fn.type, element_name: fn.name },
668
668
  });
669
669
  }
@@ -800,28 +800,52 @@ function modMustHaveAllocatedFunc(graph) {
800
800
  // MOD→satisfy→REQ, behavioral NFRs FCHAIN→satisfy→REQ. R-18 now flags any residual
801
801
  // REQ→MOD allocate edge as an invalid trace pattern.
802
802
  // ---------------------------------------------------------------------------
803
- // R-26: SCHEMA schemaRef presence (CR-211) — a SCHEMA that is not explicitly
804
- // concept-only or external should carry a valid schemaRef {file, symbol} so its
805
- // Zod definition is machine-resolvable (RC-03 then checks it resolves, RC-04 that
806
- // it is parsed at the interface). WARNING, not error: the 9 currently-unbound
807
- // SCHEMAs on the reference model must not turn readiness red before any binding
808
- // exists — the presence signal mirrors R-20 (codeRef) / R-19 (testRef). Symbol
809
- // RESOLUTION is out of scope here (pure, no I/O — that is RC-03's job).
803
+ // R-26: SCHEMA realRef presence (CR-211, unified CR-228) — a SCHEMA that is not
804
+ // explicitly concept-only or external should carry a valid realRef {file, symbol?}
805
+ // so its Zod definition is machine-resolvable (RC-03 then checks it resolves, RC-04
806
+ // that it is parsed at the interface). WARNING, not error: currently-unbound SCHEMAs
807
+ // on the reference model must not turn readiness red before any binding exists — the
808
+ // presence signal mirrors R-20 (FUNC realRef) / R-19 (testRef). Symbol RESOLUTION is
809
+ // out of scope here (pure, no I/O — that is RC-03's job).
810
810
  // ---------------------------------------------------------------------------
811
811
  function schemaMustHaveSchemaRef(graph) {
812
812
  return graph.elements
813
813
  .filter(e => e.type === 'SCHEMA')
814
814
  .filter(e => e.attributes?.concept !== true && e.attributes?.external !== true)
815
- .filter(e => !SchemaRefSchema.safeParse(e.attributes?.schemaRef).success)
815
+ .filter(e => !RealRefSchema.safeParse(e.attributes?.realRef).success)
816
816
  .map(sc => ({
817
817
  rule_id: 'R-26',
818
818
  severity: 'warning',
819
819
  element_id: sc.id,
820
- message: `${sc.id} is a SCHEMA without a valid schemaRef binding`,
821
- fix_hint: 'Add attributes.schemaRef {file, symbol, lang?}, or set attributes.concept:true (spec-only) / attributes.external:true (foreign-API contract)',
820
+ message: `${sc.id} is a SCHEMA without a valid realRef binding`,
821
+ fix_hint: 'Add attributes.realRef {file, symbol, lang?}, or set attributes.concept:true (spec-only) / attributes.external:true (foreign-API contract)',
822
822
  context: { element_type: sc.type, element_name: sc.name },
823
823
  }));
824
824
  }
825
+ // ---------------------------------------------------------------------------
826
+ // R-27: physical MOD realRef presence (CR-228) — the MOD arm of the unified
827
+ // "element must have a realRef" rule (FUNC=R-20, SCHEMA=R-26, physical MOD=R-27).
828
+ // A physical MOD (kind='physical') is a Bauteil realized by a CAD/geometry
829
+ // artefact, not by code (RT-01: FUNCs never allocate directly to it). Logical/SW
830
+ // MODs are realized through their FUNCs' code (R-20) and are NOT in scope here.
831
+ // WARNING (presence signal like R-20/R-26); concept/external MODs are exempt.
832
+ // Symbol is optional on a realRef (a geometry file has none) — presence = a valid
833
+ // realRef with a file. RESOLUTION (file on disk) is a consumer/RC concern.
834
+ // ---------------------------------------------------------------------------
835
+ function physicalModMustHaveRealRef(graph) {
836
+ return graph.elements
837
+ .filter(e => e.type === 'MOD' && e.attributes?.kind === 'physical')
838
+ .filter(e => e.attributes?.concept !== true && e.attributes?.external !== true)
839
+ .filter(e => !RealRefSchema.safeParse(e.attributes?.realRef).success)
840
+ .map(mod => ({
841
+ rule_id: 'R-27',
842
+ severity: 'warning',
843
+ element_id: mod.id,
844
+ message: `${mod.id} is a physical MOD without a valid realRef (CAD/geometry) binding`,
845
+ fix_hint: 'Add attributes.realRef {file, symbol?, lang?} pointing at the CAD/geometry artefact, or set attributes.concept:true / attributes.external:true',
846
+ context: { element_type: mod.type, element_name: mod.name },
847
+ }));
848
+ }
825
849
  export const V3_RULES = [
826
850
  { id: 'R-01', name: 'REQ must have verification', severity: 'error', evaluate: reqMustHaveVerification },
827
851
  { id: 'R-02', name: 'FUNC must satisfy REQ', severity: 'warning', evaluate: funcMustSatisfyReq },
@@ -837,11 +861,12 @@ export const V3_RULES = [
837
861
  { id: 'R-12', name: 'No circular dependencies', severity: 'warning', evaluate: noDirectCircular },
838
862
  { id: 'R-18', name: 'Valid trace pattern', severity: 'error', evaluate: validTracePattern },
839
863
  { id: 'R-19', name: 'Runnable TEST binding', severity: 'warning', evaluate: testMustHaveRunnableBinding },
840
- { id: 'R-20', name: 'FUNC code binding', severity: 'warning', evaluate: funcMustHaveCodeBinding },
864
+ { id: 'R-20', name: 'FUNC realRef binding', severity: 'warning', evaluate: funcMustHaveCodeBinding },
841
865
  { id: 'R-21', name: 'FUNC↔FUNC connection needs integration test', severity: 'warning', evaluate: fchainMustHaveIntegrationTest },
842
866
  { id: 'R-22', name: 'FUNC must be allocated to MOD', severity: 'warning', evaluate: funcMustBeAllocated },
843
867
  { id: 'R-23', name: 'MOD must have allocated FUNC', severity: 'warning', evaluate: modMustHaveAllocatedFunc },
844
- { id: 'R-26', name: 'SCHEMA must have schemaRef', severity: 'warning', evaluate: schemaMustHaveSchemaRef },
868
+ { id: 'R-26', name: 'SCHEMA must have realRef', severity: 'warning', evaluate: schemaMustHaveSchemaRef },
869
+ { id: 'R-27', name: 'Physical MOD must have realRef', severity: 'warning', evaluate: physicalModMustHaveRealRef },
845
870
  { id: 'RD-01', name: 'Unresolved requirement', severity: 'warning', evaluate: unresolvedRequirement },
846
871
  { id: 'RD-02', name: 'Decomposition consistency', severity: 'warning', evaluate: decompositionConsistency },
847
872
  { id: 'RD-03', name: 'No premature decomposition', severity: 'info', evaluate: noPrematureDecomposition },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigloch/contracts",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",