@sigloch/graph-api-core 5.4.0 → 5.5.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.
@@ -3,8 +3,6 @@ export declare class FormatECodec {
3
3
  private readonly ontology;
4
4
  private readonly edgeArrowToType;
5
5
  private readonly validNodeTypes;
6
- /** Trace-legality patterns (CR-GC-247) — the SSOT the parser validates edges against. */
7
- private readonly patterns;
8
6
  constructor(ontology: OntologyDescriptor);
9
7
  /** Extract a ```format-e block from LLM output. Returns null if not found. */
10
8
  extractFromLlm(llmOutput: string): string | null;
@@ -8,7 +8,6 @@
8
8
  * every foreign convention fail silently instead of loudly.
9
9
  */
10
10
  import { hydrateAttrValue, attributeTypeOf } from '@sigloch/contracts/se';
11
- import { isValidTrace, tracePatternsOf } from './types.js';
12
11
  // ---------------------------------------------------------------------------
13
12
  // Regex patterns
14
13
  // ---------------------------------------------------------------------------
@@ -27,8 +26,6 @@ export class FormatECodec {
27
26
  ontology;
28
27
  edgeArrowToType;
29
28
  validNodeTypes;
30
- /** Trace-legality patterns (CR-GC-247) — the SSOT the parser validates edges against. */
31
- patterns;
32
29
  constructor(ontology) {
33
30
  this.ontology = ontology;
34
31
  // Build arrow → edge type lookup
@@ -40,8 +37,6 @@ export class FormatECodec {
40
37
  }
41
38
  // Valid node type abbreviations
42
39
  this.validNodeTypes = new Set(Object.keys(ontology.nodeTypes));
43
- // Meta-model legality: descriptor.patterns (or derived from validPairs).
44
- this.patterns = tracePatternsOf(ontology);
45
40
  }
46
41
  /** Extract a ```format-e block from LLM output. Returns null if not found. */
47
42
  extractFromLlm(llmOutput) {
@@ -290,12 +285,9 @@ export class FormatECodec {
290
285
  errors.push(`Cannot resolve type of target "${targetId}" — not declared under a "### <TYPE>" section and no resolveType provided`);
291
286
  continue;
292
287
  }
293
- // Meta-model validation (CR-GC-247: single checker, patterns SSOThonors
294
- // '*' wildcards, unlike the old per-edgeType validPairs set-membership).
295
- if (!isValidTrace({ source: srcType, target: tgtType, type: edgeType }, this.patterns)) {
296
- errors.push(`Meta-model violation: ${srcType} -${edgeType}-> ${tgtType} is not valid`);
297
- continue;
298
- }
288
+ // CR-SM-324: no legality verdict while parsing the parser knows neither label nor
289
+ // the kinds of existing nodes. The write path judges with the one rule (gate R-18,
290
+ // GraphService.validateAndApplyEdge).
299
291
  const opType = action === 'remove'
300
292
  ? 'remove_edge'
301
293
  : action === 'strict_add'
@@ -199,6 +199,7 @@ export class GraphService {
199
199
  sourceId: op.sourceId,
200
200
  targetId: op.targetId,
201
201
  edgeType: op.edgeType,
202
+ label: op.attributes?.label,
202
203
  });
203
204
  const edge = {
204
205
  sourceId: op.sourceId,
@@ -249,6 +250,7 @@ export class GraphService {
249
250
  sourceId: op.sourceId,
250
251
  targetId: op.targetId,
251
252
  edgeType: op.edgeType,
253
+ label: op.attributes?.label,
252
254
  });
253
255
  const edge = {
254
256
  sourceId: op.sourceId,
@@ -299,6 +301,7 @@ export class GraphService {
299
301
  const { removed, added } = updateEdge(graph, { sourceId: op.sourceId, targetId: op.targetId, edgeType: op.edgeType }, op.set);
300
302
  await this.validateAndApplyEdge({
301
303
  sourceId: added.sourceId, targetId: added.targetId, edgeType: added.edgeType,
304
+ label: added.attributes?.label,
302
305
  });
303
306
  await this.storage.deleteEdges([{
304
307
  sourceId: removed.sourceId, targetId: removed.targetId, edgeType: removed.edgeType,
@@ -337,6 +340,7 @@ export class GraphService {
337
340
  for (const edge of addedEdges) {
338
341
  await this.validateAndApplyEdge({
339
342
  sourceId: edge.sourceId, targetId: edge.targetId, edgeType: edge.edgeType,
343
+ label: edge.attributes?.label,
340
344
  });
341
345
  }
342
346
  if (removedEdges.length > 0) {
@@ -565,9 +569,11 @@ export class GraphService {
565
569
  };
566
570
  }
567
571
  // CR-GC-247: one legality path for every ontology (SE + foreign). The edge type
568
- // must be declared (menu enumeration via edgeTypes), then the (source,target,type)
569
- // trace is checked against descriptor.patterns via the single isValidTrace —
570
- // no SE/foreign fork, no validPairs re-implementation.
572
+ // must be declared (menu enumeration via edgeTypes), then the trace is checked against
573
+ // descriptor.patterns. CR-SM-323: that checker delegates to contracts' isValidTrace — the
574
+ // routine R-18 runs fed label and endpoint kinds exactly as R-18 reads them. A foreign
575
+ // ontology's patterns (derived from validPairs) carry neither label nor where, so there
576
+ // the type pair alone decides.
571
577
  async validateAndApplyEdge(op) {
572
578
  if (!this.ontology.edgeTypes[op.edgeType]) {
573
579
  const known = Object.keys(this.ontology.edgeTypes).join(', ');
@@ -575,7 +581,14 @@ export class GraphService {
575
581
  }
576
582
  const src = await this.storage.getNode(op.sourceId);
577
583
  const tgt = await this.storage.getNode(op.targetId);
578
- if (src && tgt && !isValidTrace({ source: src.type, target: tgt.type, type: op.edgeType }, this.patterns)) {
584
+ if (src && tgt && !isValidTrace({
585
+ source: src.type,
586
+ target: tgt.type,
587
+ type: op.edgeType,
588
+ label: op.label,
589
+ sourceKinds: src.attributes?.kinds,
590
+ targetKinds: tgt.attributes?.kinds,
591
+ }, this.patterns)) {
579
592
  const allowed = this.patterns
580
593
  .filter(p => p.type === op.edgeType)
581
594
  .map(p => `${p.source}->${p.target}`)
@@ -12,7 +12,27 @@
12
12
  * Cypher) does not break consumers.
13
13
  */
14
14
  import { generateSchema } from './schema-generator.js';
15
- async function loadKuzu() {
15
+ /**
16
+ * Prozessweit genau EINE kuzu-Instanz (CR-SM-316).
17
+ *
18
+ * `kuzu.init()` ist nicht idempotent: jeder Aufruf legt eine NEUE Emscripten-Instanz
19
+ * mit eigenem Heap an (~186 MB) und haengt die alte ab. Zurueck gibt die niemand —
20
+ * WASM-Speicher schrumpft nicht, und weder `db.close()` noch der GC raeumen ihn ab.
21
+ * Vorher rief jedes `GraphCypherEngine.init()` erneut auf; im graphcode-Volllauf waren
22
+ * das 544 Aufrufe und 9641 MB Spitzenverbrauch, bis der Heap mit "memory access out of
23
+ * bounds" faultete und dabei traf, was gerade lief — in drei Laeufen drei verschiedene
24
+ * Testdateien.
25
+ *
26
+ * Der Cache haelt das PROMISE, nicht das Ergebnis: zwei Engines, die gleichzeitig
27
+ * initialisieren, warten damit auf denselben Ladevorgang statt zwei zu starten.
28
+ */
29
+ let kuzuModule = null;
30
+ function loadKuzu() {
31
+ if (!kuzuModule)
32
+ kuzuModule = initKuzu();
33
+ return kuzuModule;
34
+ }
35
+ async function initKuzu() {
16
36
  // Node path: sync nodejs variant. Detect Node by checking for `process.versions.node`.
17
37
  const isNode = typeof process !== 'undefined' && !!process.versions?.node;
18
38
  if (isNode) {
@@ -133,8 +133,14 @@ function liftAttributes(rest) {
133
133
  * otherwise the 114 pre-existing errors in graphcode's own graph would block every
134
134
  * write on debt the writer did not create. Promotion is a per-family decision, not a
135
135
  * side effect of being registered.
136
+ *
137
+ * CR-SM-309: IO is promoted (decision 2026-09-11). IO-02 ("a FLOW has exactly ONE
138
+ * producer", error) guards the one-FLOW-per-connection cut; without gate power a
139
+ * suggested merge put two producers back into one FLOW and passed the gate. The delta
140
+ * baseline keeps the switch-on safe: only NEW findings block, graphcode's own graph is
141
+ * at IO-02 = 0, and IO-01 shares the prefix but is a warning, which never blocks.
136
142
  */
137
- const GATING_PREFIXES = ['R-', 'RD-', 'MT-'];
143
+ const GATING_PREFIXES = ['R-', 'RD-', 'MT-', 'IO-'];
138
144
  const SE_RULE_DEFS = [
139
145
  ...V3_RULES,
140
146
  ...UC_RULES,
package/dist/types.d.ts CHANGED
@@ -1,7 +1,3 @@
1
- /**
2
- * Core types for graph-api-core — ontology-agnostic.
3
- * Domains register their node/edge types via OntologyDescriptor.
4
- */
5
1
  export interface GraphNode {
6
2
  uid: string;
7
3
  type: string;
@@ -135,21 +131,18 @@ export interface FormatEDiff {
135
131
  errors: string[];
136
132
  }
137
133
  /**
138
- * Structural trace-legality check. Ontology-agnostic: matches a trace against
139
- * TracePattern[] with '*' wildcards on source/target. Both graph-service and
140
- * format-e-codec route every check through this validPairs is no longer a
141
- * parallel re-implementation.
142
- *
143
- * A pattern's `label` is descriptive metadata (e.g. MS→MS relation 'depends-on'),
144
- * NOT a gate: label is trace data that isn't uniformly carried through the
145
- * Graph/codec pipeline, and the legality this replaces (validPairs) never keyed on
146
- * it. Enforcing label is a deliberate tightening (needs codec label round-tripping),
147
- * out of scope for the centralization.
134
+ * Trace legality for any ontology, decided by contracts' `isValidTrace` — the routine R-18
135
+ * runs. No logic of its own: `label` and endpoint `kinds` are part of the rule exactly as
136
+ * R-18 reads them. This signature only opens the SE-closed contracts types to the
137
+ * ontology-agnostic strings this package speaks; the runtime pattern shape is the same.
148
138
  */
149
139
  export declare function isValidTrace(trace: {
150
140
  source: string;
151
141
  target: string;
152
142
  type: string;
143
+ label?: string;
144
+ sourceKinds?: readonly string[];
145
+ targetKinds?: readonly string[];
153
146
  }, patterns: TracePattern[]): boolean;
154
147
  /**
155
148
  * The patterns a descriptor validates against: explicit `patterns` (SSOT) if set,
package/dist/types.js CHANGED
@@ -2,25 +2,18 @@
2
2
  * Core types for graph-api-core — ontology-agnostic.
3
3
  * Domains register their node/edge types via OntologyDescriptor.
4
4
  */
5
+ import { isValidTrace as contractsIsValidTrace } from '@sigloch/contracts/se';
5
6
  // ---------------------------------------------------------------------------
6
- // Trace legality — the ONE checker (CR-GC-247)
7
+ // Trace legality — ONE rule (CR-GC-247, CR-SM-323)
7
8
  // ---------------------------------------------------------------------------
8
9
  /**
9
- * Structural trace-legality check. Ontology-agnostic: matches a trace against
10
- * TracePattern[] with '*' wildcards on source/target. Both graph-service and
11
- * format-e-codec route every check through this validPairs is no longer a
12
- * parallel re-implementation.
13
- *
14
- * A pattern's `label` is descriptive metadata (e.g. MS→MS relation 'depends-on'),
15
- * NOT a gate: label is trace data that isn't uniformly carried through the
16
- * Graph/codec pipeline, and the legality this replaces (validPairs) never keyed on
17
- * it. Enforcing label is a deliberate tightening (needs codec label round-tripping),
18
- * out of scope for the centralization.
10
+ * Trace legality for any ontology, decided by contracts' `isValidTrace` — the routine R-18
11
+ * runs. No logic of its own: `label` and endpoint `kinds` are part of the rule exactly as
12
+ * R-18 reads them. This signature only opens the SE-closed contracts types to the
13
+ * ontology-agnostic strings this package speaks; the runtime pattern shape is the same.
19
14
  */
20
15
  export function isValidTrace(trace, patterns) {
21
- return patterns.some((p) => (p.source === '*' || p.source === trace.source) &&
22
- (p.target === '*' || p.target === trace.target) &&
23
- p.type === trace.type);
16
+ return contractsIsValidTrace(trace, patterns);
24
17
  }
25
18
  /**
26
19
  * The patterns a descriptor validates against: explicit `patterns` (SSOT) if set,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigloch/graph-api-core",
3
- "version": "5.4.0",
3
+ "version": "5.5.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",