@weftlet/toolkit 0.6.1 → 0.7.1

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.
Files changed (196) hide show
  1. package/README.md +191 -18
  2. package/THIRD_PARTY_NOTICES.md +3 -1
  3. package/dist/application/application.d.ts +10 -0
  4. package/dist/application/application.js +20 -0
  5. package/dist/application/document-code-bindings.d.ts +11 -0
  6. package/dist/application/document-code-bindings.js +25 -0
  7. package/dist/application/document-obligations.d.ts +8 -0
  8. package/dist/application/document-obligations.js +11 -0
  9. package/dist/application/draft-facts.d.ts +3 -0
  10. package/dist/application/draft-facts.js +37 -0
  11. package/dist/application/extraction-identity.d.ts +2 -2
  12. package/dist/application/extraction-identity.js +7 -1
  13. package/dist/application/extractor-results.d.ts +6 -0
  14. package/dist/application/extractor-results.js +6 -0
  15. package/dist/application/hydrator.js +38 -18
  16. package/dist/application/mapping-proposals.d.ts +108 -0
  17. package/dist/application/mapping-proposals.js +142 -0
  18. package/dist/application/mapping-service.d.ts +2 -0
  19. package/dist/application/mapping-service.js +5 -0
  20. package/dist/application/overlay.js +43 -29
  21. package/dist/application/saved-code-coverage.d.ts +40 -0
  22. package/dist/application/saved-code-coverage.js +20 -0
  23. package/dist/application/saved-compatibility.d.ts +38 -0
  24. package/dist/application/saved-compatibility.js +20 -0
  25. package/dist/application/saved-evidence.d.ts +17 -11
  26. package/dist/application/saved-evidence.js +44 -40
  27. package/dist/application/saved-exploration.d.ts +57 -0
  28. package/dist/application/saved-exploration.js +21 -0
  29. package/dist/application/saved-paths.d.ts +58 -0
  30. package/dist/application/saved-paths.js +20 -0
  31. package/dist/application/semantic-run.d.ts +1 -1
  32. package/dist/extraction/call-syntax.d.ts +18 -0
  33. package/dist/extraction/call-syntax.js +61 -0
  34. package/dist/extraction/canonical-document-links.d.ts +5 -1
  35. package/dist/extraction/canonical-document-links.js +4 -4
  36. package/dist/extraction/code-inventory.d.ts +6 -0
  37. package/dist/extraction/code-inventory.js +26 -0
  38. package/dist/extraction/document-code-bindings.d.ts +14 -0
  39. package/dist/extraction/document-code-bindings.js +121 -0
  40. package/dist/extraction/document-obligations.d.ts +12 -0
  41. package/dist/extraction/document-obligations.js +112 -0
  42. package/dist/extraction/mapping-candidates.d.ts +13 -0
  43. package/dist/extraction/mapping-candidates.js +46 -0
  44. package/dist/extraction/mapping-output.d.ts +75 -0
  45. package/dist/extraction/mapping-output.js +53 -0
  46. package/dist/extraction/mapping-plan.d.ts +7 -0
  47. package/dist/extraction/mapping-plan.js +117 -0
  48. package/dist/extraction/mapping-prompt.d.ts +2 -0
  49. package/dist/extraction/mapping-prompt.js +17 -0
  50. package/dist/extraction/mapping-provider.d.ts +15 -0
  51. package/dist/extraction/mapping-provider.js +60 -0
  52. package/dist/extraction/mapping-request.d.ts +3 -0
  53. package/dist/extraction/mapping-request.js +32 -0
  54. package/dist/extraction/ontology.d.ts +2 -0
  55. package/dist/extraction/ontology.js +28 -13
  56. package/dist/extraction/routing.js +5 -20
  57. package/dist/extraction/scip-occurrences.d.ts +3 -0
  58. package/dist/extraction/scip-occurrences.js +102 -0
  59. package/dist/extraction/scip-ranges.d.ts +6 -0
  60. package/dist/extraction/scip-ranges.js +31 -0
  61. package/dist/extraction/scip-runtime.d.ts +39 -0
  62. package/dist/extraction/scip-runtime.js +8 -0
  63. package/dist/extraction/scip-symbols.d.ts +21 -0
  64. package/dist/extraction/scip-symbols.js +91 -0
  65. package/dist/extraction/semantic.d.ts +1 -1
  66. package/dist/extraction/semantic.js +5 -17
  67. package/dist/extraction/static-calls.d.ts +10 -0
  68. package/dist/extraction/static-calls.js +133 -0
  69. package/dist/extraction/static.d.ts +3 -20
  70. package/dist/extraction/static.js +8 -84
  71. package/dist/extraction/symbol-resolution.d.ts +12 -0
  72. package/dist/extraction/symbol-resolution.js +51 -0
  73. package/dist/extraction/symbol-selectors.d.ts +11 -0
  74. package/dist/extraction/symbol-selectors.js +51 -0
  75. package/dist/graph/binding-rdf.d.ts +7 -0
  76. package/dist/graph/binding-rdf.js +109 -0
  77. package/dist/graph/call-rdf.d.ts +4 -0
  78. package/dist/graph/call-rdf.js +94 -0
  79. package/dist/graph/call-shapes.d.ts +2 -0
  80. package/dist/graph/call-shapes.js +28 -0
  81. package/dist/graph/code-coverage-projection.d.ts +9 -0
  82. package/dist/graph/code-coverage-projection.js +117 -0
  83. package/dist/graph/code-coverage-renderer.d.ts +2 -0
  84. package/dist/graph/code-coverage-renderer.js +15 -0
  85. package/dist/graph/code-coverage-request.d.ts +6 -0
  86. package/dist/graph/code-coverage-request.js +19 -0
  87. package/dist/graph/code-coverage.d.ts +6 -0
  88. package/dist/graph/code-coverage.js +54 -0
  89. package/dist/graph/code-description.d.ts +4 -0
  90. package/dist/graph/code-description.js +24 -0
  91. package/dist/graph/code-rdf.d.ts +6 -0
  92. package/dist/graph/code-rdf.js +79 -0
  93. package/dist/graph/compatibility-context.d.ts +2 -0
  94. package/dist/graph/compatibility-context.js +18 -0
  95. package/dist/graph/compatibility-renderer.d.ts +2 -0
  96. package/dist/graph/compatibility-renderer.js +45 -0
  97. package/dist/graph/compatibility-request.d.ts +3 -0
  98. package/dist/graph/compatibility-request.js +18 -0
  99. package/dist/graph/compatibility.d.ts +7 -0
  100. package/dist/graph/compatibility.js +67 -0
  101. package/dist/graph/constraint-comparison.d.ts +7 -0
  102. package/dist/graph/constraint-comparison.js +49 -0
  103. package/dist/graph/coverage.d.ts +2 -2
  104. package/dist/graph/coverage.js +8 -3
  105. package/dist/graph/diagnostics.d.ts +1 -1
  106. package/dist/graph/diagnostics.js +10 -6
  107. package/dist/graph/evidence-description.d.ts +3 -0
  108. package/dist/graph/evidence-description.js +13 -0
  109. package/dist/graph/identity-preflight.js +1 -1
  110. package/dist/graph/neighborhood-renderer.d.ts +2 -0
  111. package/dist/graph/neighborhood-renderer.js +38 -0
  112. package/dist/graph/neighborhood-request.d.ts +9 -0
  113. package/dist/graph/neighborhood-request.js +37 -0
  114. package/dist/graph/neighborhood.d.ts +8 -0
  115. package/dist/graph/neighborhood.js +100 -0
  116. package/dist/graph/obligation-rdf.d.ts +5 -0
  117. package/dist/graph/obligation-rdf.js +67 -0
  118. package/dist/graph/obligation-shapes.d.ts +2 -0
  119. package/dist/graph/obligation-shapes.js +32 -0
  120. package/dist/graph/partitioned-shacl.d.ts +14 -0
  121. package/dist/graph/partitioned-shacl.js +66 -0
  122. package/dist/graph/path-request.d.ts +11 -0
  123. package/dist/graph/path-request.js +24 -0
  124. package/dist/graph/path-search.d.ts +45 -0
  125. package/dist/graph/path-search.js +101 -0
  126. package/dist/graph/paths-renderer.d.ts +2 -0
  127. package/dist/graph/paths-renderer.js +36 -0
  128. package/dist/graph/paths.d.ts +7 -0
  129. package/dist/graph/paths.js +74 -0
  130. package/dist/graph/query.js +23 -6
  131. package/dist/graph/rdf-partitions.d.ts +24 -0
  132. package/dist/graph/rdf-partitions.js +170 -0
  133. package/dist/graph/rdf-projection.d.ts +7 -0
  134. package/dist/graph/rdf-projection.js +27 -0
  135. package/dist/graph/rdf-records.d.ts +12 -0
  136. package/dist/graph/rdf-records.js +90 -0
  137. package/dist/graph/rdf.d.ts +10 -10
  138. package/dist/graph/rdf.js +84 -89
  139. package/dist/graph/shacl-shapes.js +70 -0
  140. package/dist/graph/snapshot-context.d.ts +3 -0
  141. package/dist/graph/snapshot-context.js +8 -0
  142. package/dist/graph/validation-progress.d.ts +4 -1
  143. package/dist/index.d.ts +23 -0
  144. package/dist/index.js +22 -0
  145. package/dist/infrastructure/json-digest.d.ts +3 -0
  146. package/dist/infrastructure/json-digest.js +3 -0
  147. package/dist/infrastructure/unique-json.d.ts +2 -0
  148. package/dist/infrastructure/unique-json.js +20 -0
  149. package/dist/interfaces/cli.js +33 -1
  150. package/dist/interfaces/code-coverage.d.ts +46 -0
  151. package/dist/interfaces/code-coverage.js +18 -0
  152. package/dist/interfaces/graph-compatibility.d.ts +35 -0
  153. package/dist/interfaces/graph-compatibility.js +21 -0
  154. package/dist/interfaces/graph-exploration.d.ts +59 -0
  155. package/dist/interfaces/graph-exploration.js +27 -0
  156. package/dist/interfaces/graph-paths.d.ts +77 -0
  157. package/dist/interfaces/graph-paths.js +31 -0
  158. package/dist/interfaces/graph-query-args.d.ts +2 -0
  159. package/dist/interfaces/graph-query-args.js +25 -0
  160. package/dist/interfaces/mapping-proposals.d.ts +204 -0
  161. package/dist/interfaces/mapping-proposals.js +74 -0
  162. package/dist/interfaces/mcp.js +31 -3
  163. package/dist/storage/mapping-proposals.d.ts +9 -0
  164. package/dist/storage/mapping-proposals.js +42 -0
  165. package/dist/storage/storage.d.ts +3 -0
  166. package/dist/storage/storage.js +37 -13
  167. package/dist/storage/streaming-json.d.ts +5 -0
  168. package/dist/storage/streaming-json.js +45 -0
  169. package/dist/visualization/evidence-sources.d.ts +6 -0
  170. package/dist/visualization/evidence-sources.js +19 -0
  171. package/dist/visualization/viewer-interaction.d.ts +5 -8
  172. package/dist/visualization/viewer-interaction.js +46 -44
  173. package/dist/visualization/visualization.js +14 -5
  174. package/docs/1.0-WORK-QUEUE.md +48 -0
  175. package/docs/ADR-029-exact-graph-neighborhoods.md +70 -0
  176. package/docs/ADR-030-bounded-evidence-paths.md +97 -0
  177. package/docs/ADR-031-code-extractor-selection.md +91 -0
  178. package/docs/ADR-032-scip-code-evidence.md +73 -0
  179. package/docs/ADR-033-document-symbol-bindings.md +83 -0
  180. package/docs/ADR-034-mapping-gaps.md +80 -0
  181. package/docs/ADR-035-static-call-evidence.md +78 -0
  182. package/docs/ADR-036-contract-obligation-declarations.md +124 -0
  183. package/docs/ADR-037-selected-contract-compatibility.md +93 -0
  184. package/docs/ADR-038-monorepo-code-coverage.md +105 -0
  185. package/docs/ADR-039-mapping-proposals.md +119 -0
  186. package/docs/ADR-040-bounded-graph-io.md +90 -0
  187. package/docs/GRAPHIFY-EVALUATION.md +21 -4
  188. package/docs/PRE-1.0-BASELINE.md +49 -2
  189. package/docs/RELEASE-1.0-PLAN.md +14 -2
  190. package/docs/RELEASING.md +3 -3
  191. package/docs/ROADMAP.md +27 -6
  192. package/docs/VALIDATION.md +521 -14
  193. package/docs/benchmarks/large-graph-2026-11-02/result.json +156 -0
  194. package/docs/extraction-manifest.json +266 -23
  195. package/package.json +10 -4
  196. package/templates/context-graph-development/SKILL.md +30 -1
@@ -0,0 +1,2 @@
1
+ /** Included in the shared named-dataset/union shape set. */
2
+ export declare const callShapes = "\ncg:CodeCallShape a sh:NodeShape; sh:targetClass cg:CodeCall;\n sh:property [ sh:path rdf:predicate; sh:hasValue cg:calls ];\n sh:property [ sh:path rdf:object; sh:class cg:CodeSymbol ];\n sh:property [ sh:path cg:callVersion; sh:minCount 1; sh:maxCount 1; sh:hasValue 1 ];\n sh:property [ sh:path cg:callMethod; sh:minCount 1; sh:maxCount 1; sh:hasValue \"typescript-ast+scip\" ];\n sh:property [ sh:path cg:callSyntax; sh:minCount 1; sh:maxCount 1; sh:in (\"call\" \"construct\") ];\n sh:property [ sh:path cg:callDispatch; sh:minCount 1; sh:maxCount 1; sh:in (\"lexical\" \"member\" \"construct\") ];\n sh:property [ sh:path cg:callOptional; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:boolean ];\n sh:property [ sh:path cg:callCaller; sh:minCount 1; sh:maxCount 1; sh:in (\"indexed-callable\" \"file-scope\") ];\n sh:property [ sh:path cg:callTarget; sh:minCount 1; sh:maxCount 1; sh:in (\"indexed-body\" \"indexed-class\" \"declaration-only\" \"external\") ];\n sh:property [ sh:path cg:callRuntime; sh:minCount 1; sh:maxCount 1; sh:hasValue \"not-observed\" ];\n sh:property [ sh:path cg:columnEncoding; sh:minCount 1; sh:hasValue \"utf-16\" ];\n sh:property [ sh:path cg:authority; sh:in (\"implemented\" \"draft\") ];\n sh:property [ sh:path cg:extractionReview; sh:minCount 1; sh:hasValue \"deterministic\" ];\n sh:property [ sh:path cg:callOccurrence; sh:minCount 1; sh:class cg:CallProof ];\n sh:property [ sh:path cg:callCallerDefinition; sh:class cg:CallProof ];\n sh:property [ sh:path cg:callTargetDefinition; sh:class cg:CallProof ].\ncg:CallProofShape a sh:NodeShape; sh:targetClass cg:CallProof;\n sh:property [ sh:path cg:callFact; sh:minCount 1; sh:maxCount 1; sh:class rdf:Statement ];\n sh:property [ sh:path cg:path; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:string; sh:minLength 1 ];\n sh:property [ sh:path cg:digest; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:string; sh:minLength 1 ];\n sh:property [ sh:path cg:commitSha; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:string; sh:minLength 1 ];\n sh:property [ sh:path cg:startLine; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:integer; sh:minInclusive 1 ];\n sh:property [ sh:path cg:endLine; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:integer; sh:minInclusive 1 ];\n sh:property [ sh:path cg:columnEncoding; sh:minCount 1; sh:hasValue \"utf-16\" ].\n";
@@ -0,0 +1,28 @@
1
+ /** Included in the shared named-dataset/union shape set. */
2
+ export const callShapes = `
3
+ cg:CodeCallShape a sh:NodeShape; sh:targetClass cg:CodeCall;
4
+ sh:property [ sh:path rdf:predicate; sh:hasValue cg:calls ];
5
+ sh:property [ sh:path rdf:object; sh:class cg:CodeSymbol ];
6
+ sh:property [ sh:path cg:callVersion; sh:minCount 1; sh:maxCount 1; sh:hasValue 1 ];
7
+ sh:property [ sh:path cg:callMethod; sh:minCount 1; sh:maxCount 1; sh:hasValue "typescript-ast+scip" ];
8
+ sh:property [ sh:path cg:callSyntax; sh:minCount 1; sh:maxCount 1; sh:in ("call" "construct") ];
9
+ sh:property [ sh:path cg:callDispatch; sh:minCount 1; sh:maxCount 1; sh:in ("lexical" "member" "construct") ];
10
+ sh:property [ sh:path cg:callOptional; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:boolean ];
11
+ sh:property [ sh:path cg:callCaller; sh:minCount 1; sh:maxCount 1; sh:in ("indexed-callable" "file-scope") ];
12
+ sh:property [ sh:path cg:callTarget; sh:minCount 1; sh:maxCount 1; sh:in ("indexed-body" "indexed-class" "declaration-only" "external") ];
13
+ sh:property [ sh:path cg:callRuntime; sh:minCount 1; sh:maxCount 1; sh:hasValue "not-observed" ];
14
+ sh:property [ sh:path cg:columnEncoding; sh:minCount 1; sh:hasValue "utf-16" ];
15
+ sh:property [ sh:path cg:authority; sh:in ("implemented" "draft") ];
16
+ sh:property [ sh:path cg:extractionReview; sh:minCount 1; sh:hasValue "deterministic" ];
17
+ sh:property [ sh:path cg:callOccurrence; sh:minCount 1; sh:class cg:CallProof ];
18
+ sh:property [ sh:path cg:callCallerDefinition; sh:class cg:CallProof ];
19
+ sh:property [ sh:path cg:callTargetDefinition; sh:class cg:CallProof ].
20
+ cg:CallProofShape a sh:NodeShape; sh:targetClass cg:CallProof;
21
+ sh:property [ sh:path cg:callFact; sh:minCount 1; sh:maxCount 1; sh:class rdf:Statement ];
22
+ sh:property [ sh:path cg:path; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:string; sh:minLength 1 ];
23
+ sh:property [ sh:path cg:digest; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:string; sh:minLength 1 ];
24
+ sh:property [ sh:path cg:commitSha; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:string; sh:minLength 1 ];
25
+ sh:property [ sh:path cg:startLine; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:integer; sh:minInclusive 1 ];
26
+ sh:property [ sh:path cg:endLine; sh:minCount 1; sh:maxCount 1; sh:datatype xsd:integer; sh:minInclusive 1 ];
27
+ sh:property [ sh:path cg:columnEncoding; sh:minCount 1; sh:hasValue "utf-16" ].
28
+ `;
@@ -0,0 +1,9 @@
1
+ import { type CodeCoverageOverview, type CodeFileCoverage, type CodeProjectCoverage, type GraphSnapshot } from '@weftlet/core';
2
+ export declare const codeCoverageLimits: readonly ["Inventory covers regular committed TypeScript-family files and nearest package.json boundaries, not compiler projects or verified workspace membership.", "Code roots, requested entry files and observed SCIP documents are separate measurements. Neither selection nor an import alone proves a file was indexed.", "Connections count retained indexed code references across package boundaries, not asynchronous product links, runtime delivery or business impact.", "Zero observed connections or unresolved references does not prove isolation or complete resolution. Other languages, dynamic targets and absent evidence remain outside this measurement.", "Saved or draft coverage never establishes current-diff validity; use graph.neighbors on file/symbol IDs to inspect cited implementation evidence."];
3
+ /** Derived only from saved inventory and retained occurrence facts; does not invent graph edges. */
4
+ export declare function projectCodeCoverage(snapshot: GraphSnapshot): {
5
+ overview: CodeCoverageOverview;
6
+ projects: readonly CodeProjectCoverage[];
7
+ files: readonly CodeFileCoverage[];
8
+ };
9
+ export declare function codeCoverageDescription(code?: CodeCoverageOverview): string;
@@ -0,0 +1,117 @@
1
+ import { repositoryFileNode } from '@weftlet/core';
2
+ import { sha256, stableJson } from '../infrastructure/util.js';
3
+ export const codeCoverageLimits = [
4
+ 'Inventory covers regular committed TypeScript-family files and nearest package.json boundaries, not compiler projects or verified workspace membership.',
5
+ 'Code roots, requested entry files and observed SCIP documents are separate measurements. Neither selection nor an import alone proves a file was indexed.',
6
+ 'Connections count retained indexed code references across package boundaries, not asynchronous product links, runtime delivery or business impact.',
7
+ 'Zero observed connections or unresolved references does not prove isolation or complete resolution. Other languages, dynamic targets and absent evidence remain outside this measurement.',
8
+ 'Saved or draft coverage never establishes current-diff validity; use graph.neighbors on file/symbol IDs to inspect cited implementation evidence.',
9
+ ];
10
+ function validInventory(snapshot) {
11
+ const inventory = snapshot.manifest.codeInventory;
12
+ if (!inventory || inventory.version !== 'committed-code-inventory-v1' || inventory.scipIndexDigest !== snapshot.manifest.scipIndexDigest)
13
+ return;
14
+ const { digest, ...plain } = inventory;
15
+ if (sha256(stableJson(plain)) !== digest)
16
+ return;
17
+ return inventory;
18
+ }
19
+ function totals(files) {
20
+ const count = (test) => files.filter(test).length;
21
+ const sum = (key) => files.reduce((n, file) => n + file[key], 0);
22
+ return { files: files.length, codeRootSelected: count(file => file.codeRootSelected),
23
+ requested: count(file => file.requested === 'included'), excludedFromRequest: count(file => file.requested === 'excluded'),
24
+ requestUnknown: count(file => file.requested === 'unknown'), indexed: count(file => file.indexed === true),
25
+ indexUnknown: count(file => file.indexed === undefined), requestedNotIndexed: count(file => file.requested === 'included' && file.indexed === false),
26
+ indexedOutsideRequest: count(file => file.requested === 'excluded' && file.indexed === true),
27
+ references: sum('references'), unresolvedReferences: sum('unresolvedReferences'), declarationOnlyReferences: sum('declarationOnlyReferences'),
28
+ implementationGaps: sum('implementationGaps') };
29
+ }
30
+ function definitionProjects(facts, byPath) {
31
+ const definitions = new Map();
32
+ for (const fact of facts)
33
+ if (fact.codeOccurrence?.role === 'definition') {
34
+ const owner = byPath.get(fact.provenance.path)?.projectRoot;
35
+ if (owner === undefined)
36
+ continue;
37
+ const roots = definitions.get(fact.subject.id) ?? new Set();
38
+ roots.add(owner);
39
+ definitions.set(fact.subject.id, roots);
40
+ }
41
+ return definitions;
42
+ }
43
+ function measureReferences(facts, byPath) {
44
+ const definitions = definitionProjects(facts, byPath);
45
+ const connections = new Map();
46
+ const project = (root) => {
47
+ if (!connections.has(root))
48
+ connections.set(root, { peers: new Set(), incoming: new Set(), outgoing: new Set() });
49
+ return connections.get(root);
50
+ };
51
+ const crossReferences = new Set(), seen = new Set();
52
+ for (const fact of facts) {
53
+ if (fact.codeOccurrence?.role !== 'reference' || seen.has(fact.id))
54
+ continue;
55
+ seen.add(fact.id);
56
+ const file = byPath.get(fact.provenance.path);
57
+ if (!file)
58
+ continue;
59
+ file.references++;
60
+ const targets = definitions.get(fact.object.id);
61
+ if (!targets?.size || fact.object.codeSymbol?.resolution === 'no-indexed-definition')
62
+ file.unresolvedReferences++;
63
+ if (fact.object.codeSymbol?.resolution === 'declaration-only')
64
+ file.declarationOnlyReferences++;
65
+ for (const root of targets ?? [])
66
+ if (root !== file.projectRoot) {
67
+ crossReferences.add(fact.id);
68
+ project(file.projectRoot).peers.add(root);
69
+ project(file.projectRoot).outgoing.add(fact.id);
70
+ project(root).peers.add(file.projectRoot);
71
+ project(root).incoming.add(fact.id);
72
+ }
73
+ }
74
+ return { project, crossReferences };
75
+ }
76
+ /** Derived only from saved inventory and retained occurrence facts; does not invent graph edges. */
77
+ export function projectCodeCoverage(snapshot) {
78
+ const inventory = validInventory(snapshot);
79
+ if (!inventory)
80
+ return { overview: { scope: 'saved-snapshot', status: snapshot.manifest.codeInventory ? 'invalid-inventory' : 'unavailable',
81
+ limitations: codeCoverageLimits }, projects: [], files: [] };
82
+ const files = inventory.files.map(file => ({ ...file, nodeId: repositoryFileNode(file.path).id,
83
+ references: 0, unresolvedReferences: 0, declarationOnlyReferences: 0, implementationGaps: 0 }));
84
+ const byPath = new Map(files.map(file => [file.path, file]));
85
+ const { project, crossReferences } = measureReferences(snapshot.facts, byPath);
86
+ for (const gap of snapshot.diagnostics ?? [])
87
+ if (gap.kind === 'implementationGaps') {
88
+ for (const path of new Set(gap.provenance.map(span => span.path))) {
89
+ const file = byPath.get(path);
90
+ if (file)
91
+ file.implementationGaps++;
92
+ }
93
+ }
94
+ const groups = new Map();
95
+ for (const file of files) {
96
+ const group = groups.get(file.projectRoot) ?? [];
97
+ group.push(file);
98
+ groups.set(file.projectRoot, group);
99
+ }
100
+ const projects = inventory.projects.map(item => ({ root: item.root, ...(item.manifest ? { manifestPath: item.manifest.path } : {}),
101
+ counts: totals(groups.get(item.root) ?? []), connectedProjects: project(item.root).peers.size,
102
+ outgoingReferences: project(item.root).outgoing.size, incomingReferences: project(item.root).incoming.size }));
103
+ return { overview: { scope: 'saved-snapshot', status: 'recorded', inventoryDigest: inventory.digest, selection: inventory.selection,
104
+ indexObservation: inventory.indexObservation, projects: projects.length, totals: totals(files),
105
+ crossProjectReferences: crossReferences.size, otherIndexedFiles: inventory.otherIndexedFiles, limitations: codeCoverageLimits }, projects, files };
106
+ }
107
+ export function codeCoverageDescription(code) {
108
+ if (!code || code.status !== 'recorded' || !code.totals)
109
+ return `Saved repository code coverage: ${code?.status ?? 'unavailable'}; absence is not zero coverage.`;
110
+ const n = code.totals;
111
+ return `Saved repository code coverage (TypeScript-family files): ${n.files} inventoried; ${n.codeRootSelected} selected by code roots; ` +
112
+ `${n.requested} requested entries, ${n.excludedFromRequest} outside entries, ${n.requestUnknown} selection unknown; ` +
113
+ `${n.indexed} observed in SCIP, ${n.indexUnknown} index observation unknown, ${n.requestedNotIndexed} requested but not observed. ` +
114
+ `${code.crossProjectReferences} observed cross-package references; ${n.unresolvedReferences} references lack an indexed definition; ` +
115
+ `${n.declarationOnlyReferences} reference declaration-only targets; ${n.implementationGaps} cited implementation gaps. ` +
116
+ 'These are saved-snapshot measurements, not completeness or no-impact claims. Use graph.coverage for package/file pages.';
117
+ }
@@ -0,0 +1,2 @@
1
+ import type { CodeCoverageReport } from '@weftlet/core';
2
+ export declare function renderCodeCoverage(report: Omit<CodeCoverageReport, 'markdown'>): string;
@@ -0,0 +1,15 @@
1
+ import { codeCoverageDescription } from './code-coverage-projection.js';
2
+ const quoted = (value) => JSON.stringify(value).replace(/[\\`*_{}\[\]()<>!|]/g, '\\$&');
3
+ export function renderCodeCoverage(report) {
4
+ const { snapshot, evidenceState, selection, overview } = report;
5
+ return ['# Saved repository code coverage', `Snapshot: ${quoted(snapshot.snapshotId)} (${snapshot.commitSha}); configuration ${snapshot.configurationDigest ?? 'unknown'}`,
6
+ codeCoverageDescription(overview),
7
+ `View: ${report.view}; project: ${quoted(selection.projectRoot ?? '(all boundaries)')}; ${report.projectStatus ?? 'all'}; offset ${selection.offset}; ${selection.remaining}/${selection.total} items remain.`,
8
+ ...(selection.continuation ? [`Continuation (same request): ${selection.continuation}`] : []),
9
+ ...(evidenceState ? [`Stale: ${evidenceState.stale}; configuration matches: ${evidenceState.configurationMatches}; extraction compatible: ${evidenceState.extractionCompatible ?? 'unknown'}; overlay freshness: ${evidenceState.overlayFreshness ?? 'not an overlay'}`,
10
+ `Excluded working changes: ${evidenceState.excludedWorkingChanges.map(quoted).join(', ') || '(none)'}`] : []),
11
+ '', ...report.projects.map(p => `- ${quoted(p.root)}: ${p.counts.files} files; ${p.counts.requested} requested; ${p.counts.indexed} indexed; ${p.counts.indexUnknown} index unknown; ${p.connectedProjects} connected package boundaries. Use this exact projectRoot to page files.`),
12
+ ...report.files.map(f => `- ${quoted(f.path)} (${quoted(f.nodeId)}): code roots ${f.codeRootSelected}; requested ${f.requested}; indexed ${f.indexed ?? 'unknown'}; ${f.unresolvedReferences} unresolved references; ${f.declarationOnlyReferences} declaration-only references; ${f.implementationGaps} cited gaps. Inspect graph.neighbors at this file ID for retained evidence.`),
13
+ '', '## Measurement limits', ...overview.limitations.map(item => `- ${item}`),
14
+ ].join('\n');
15
+ }
@@ -0,0 +1,6 @@
1
+ export declare function parseCodeCoverageRequest(value: unknown): {
2
+ continuation?: string | undefined;
3
+ projectRoot?: string | undefined;
4
+ snapshotId: string;
5
+ maxItems: number;
6
+ };
@@ -0,0 +1,19 @@
1
+ export function parseCodeCoverageRequest(value) {
2
+ if (!value || typeof value !== 'object' || Array.isArray(value))
3
+ throw new Error('graph.coverage arguments must be an object');
4
+ const item = value;
5
+ if (Object.keys(item).some(key => !['snapshotId', 'projectRoot', 'maxItems', 'continuation'].includes(key)))
6
+ throw new Error('Unknown graph.coverage field');
7
+ const text = (key, max) => {
8
+ const s = item[key];
9
+ if (typeof s !== 'string' || !s.trim() || s.length > max)
10
+ throw new Error(`${key} must be a nonempty string of at most ${max} characters`);
11
+ return s;
12
+ };
13
+ const maxItems = item.maxItems === undefined ? 50 : item.maxItems;
14
+ if (!Number.isInteger(maxItems) || Number(maxItems) < 1 || Number(maxItems) > 200)
15
+ throw new Error('maxItems must be an integer between 1 and 200');
16
+ return { snapshotId: text('snapshotId', 256), maxItems: Number(maxItems),
17
+ ...(item.projectRoot === undefined ? {} : { projectRoot: text('projectRoot', 8192) }),
18
+ ...(item.continuation === undefined ? {} : { continuation: text('continuation', 2048) }) };
19
+ }
@@ -0,0 +1,6 @@
1
+ import type { CodeCoverageReport, CodeCoverageRequest, CodeCoverageService, SnapshotStore } from '@weftlet/core';
2
+ export declare class SnapshotCodeCoverageService implements CodeCoverageService {
3
+ private readonly store;
4
+ constructor(store: Pick<SnapshotStore, 'read'>);
5
+ coverage(input: CodeCoverageRequest): Promise<CodeCoverageReport>;
6
+ }
@@ -0,0 +1,54 @@
1
+ import { stableJsonDigest } from '../infrastructure/util.js';
2
+ import { projectCodeCoverage } from './code-coverage-projection.js';
3
+ import { parseCodeCoverageRequest } from './code-coverage-request.js';
4
+ import { renderCodeCoverage } from './code-coverage-renderer.js';
5
+ import { RdfEvidenceGraph } from './rdf.js';
6
+ import { snapshotContext } from './snapshot-context.js';
7
+ const version = 'code-coverage-query-v1';
8
+ function pageOffset(token, binding, length, pageSize) {
9
+ if (token === undefined)
10
+ return 0;
11
+ try {
12
+ if (!/^[A-Za-z0-9_-]+$/.test(token))
13
+ throw Error();
14
+ const item = JSON.parse(Buffer.from(token, 'base64url').toString('utf8'));
15
+ if (Object.keys(item).sort().join(',') !== 'binding,offset,version' || item.version !== version || item.binding !== binding ||
16
+ !Number.isSafeInteger(item.offset) || item.offset < 1 || item.offset >= length || item.offset % pageSize)
17
+ throw Error();
18
+ return item.offset;
19
+ }
20
+ catch {
21
+ throw Error('Invalid graph.coverage continuation or snapshot/query mismatch; restart graph.coverage');
22
+ }
23
+ }
24
+ export class SnapshotCodeCoverageService {
25
+ store;
26
+ constructor(store) {
27
+ this.store = store;
28
+ }
29
+ async coverage(input) {
30
+ const request = parseCodeCoverageRequest(input), saved = await this.store.read(request.snapshotId);
31
+ if (!saved)
32
+ throw Error('No graph snapshot is available for that identifier');
33
+ if (saved.manifest.snapshotId !== request.snapshotId)
34
+ throw Error('Saved snapshot identity does not match the requested identifier');
35
+ const facts = await new RdfEvidenceGraph(saved).facts();
36
+ if (new Set(facts.map(fact => fact.id)).size !== facts.length)
37
+ throw Error('Duplicate fact identities prevent stable code coverage');
38
+ const projected = projectCodeCoverage({ ...saved, facts });
39
+ const { continuation, ...query } = request;
40
+ const binding = stableJsonDigest({ version, query, manifest: saved.manifest, facts, diagnostics: saved.diagnostics });
41
+ const fileView = request.projectRoot !== undefined;
42
+ const files = fileView ? projected.files.filter(f => f.projectRoot === request.projectRoot) : [];
43
+ const length = fileView ? files.length : projected.projects.length;
44
+ const offset = pageOffset(continuation, binding, length, request.maxItems), end = Math.min(length, offset + request.maxItems);
45
+ const project = projected.projects.find(p => p.root === request.projectRoot);
46
+ const next = end < length ? Buffer.from(JSON.stringify({ version, binding, offset: end })).toString('base64url') : undefined;
47
+ const plain = { snapshot: snapshotContext(saved.manifest), overview: projected.overview,
48
+ view: fileView ? 'files' : 'projects', ...(fileView ? { project, projectStatus: projected.overview.status !== 'recorded' ? 'unknown' : project ? 'present' : 'absent' } : {}),
49
+ projects: fileView ? [] : projected.projects.slice(offset, end), files: fileView ? files.slice(offset, end) : [],
50
+ selection: { ...(fileView ? { projectRoot: request.projectRoot } : {}), maxItems: request.maxItems, offset, total: length,
51
+ remaining: length - end, truncated: !!next, ...(next ? { continuation: next } : {}) } };
52
+ return { ...plain, markdown: renderCodeCoverage(plain) };
53
+ }
54
+ }
@@ -0,0 +1,4 @@
1
+ import type { GraphFact, ContextPack } from '@weftlet/core';
2
+ /** Human-readable labels for the same typed evidence returned to CLI/MCP clients. */
3
+ export declare function codeEvidenceDescription(fact: GraphFact): string | undefined;
4
+ export declare function unexpandedCodeDescription(exploration: ContextPack['exploration']): readonly string[];
@@ -0,0 +1,24 @@
1
+ /** Human-readable labels for the same typed evidence returned to CLI/MCP clients. */
2
+ export function codeEvidenceDescription(fact) {
3
+ if (fact.codeCall) {
4
+ const call = fact.codeCall;
5
+ return `static ${call.syntax}; ${call.dispatch} selection; ${call.target}; ${call.caller}; ` +
6
+ `${call.optional ? 'optional invocation; ' : ''}runtime dispatch/value mutation not evaluated; execution not observed`;
7
+ }
8
+ const binding = fact.codeBinding;
9
+ if (binding) {
10
+ const sites = binding.definitions.map(proof => `${proof.provenance.path}:${proof.provenance.startLine}:${proof.provenance.startColumn ?? 1}@${proof.provenance.commitSha}`).join(', ');
11
+ const selected = binding.selectedOccurrences.map(proof => `${proof.provenance.path}:${proof.provenance.startLine}:${proof.provenance.startColumn ?? 1}@${proof.provenance.commitSha}`).join(', ');
12
+ return `explicit mapping declaration; target ${binding.selector.symbol}; selected ${selected}; definitions ${sites}; not proof of fulfillment or test execution`;
13
+ }
14
+ const code = fact.codeOccurrence;
15
+ if (!code)
16
+ return undefined;
17
+ const symbol = code.role === 'definition' ? fact.subject : fact.object;
18
+ return `static ${code.role}; ${symbol.codeSymbol?.resolution ?? 'unknown resolution'}; ` +
19
+ `${code.enclosure} lexical enclosure; ${code.locationKind}; UTF-16 columns`;
20
+ }
21
+ export function unexpandedCodeDescription(exploration) {
22
+ const count = exploration?.unexpandedCodeTargetCount ?? 0;
23
+ return count ? [`Unresolved code targets left unexpanded: ${count}; up to 100 example IDs are included in exploration metadata. Use graph.neighbors with their exact IDs to inspect shared references; no implementation or call is established.`] : [];
24
+ }
@@ -0,0 +1,6 @@
1
+ import { type Store, type NamedNode } from 'n3';
2
+ import { type GraphFact, type GraphNode, type SourceSpan } from '@weftlet/core';
3
+ export declare function addCodeEvidence(dataset: Store, record: NamedNode, fact: GraphFact, nodeIri: (id: string) => string, includeNodes?: boolean): void;
4
+ export declare function addCodeNodeEvidence(dataset: Store, node: GraphNode, nodeIri: (id: string) => string): void;
5
+ export declare function invalidCodeRange(range: Pick<SourceSpan, 'startLine' | 'endLine' | 'startColumn' | 'endColumn' | 'columnEncoding'>): boolean;
6
+ export declare function codeEvidenceIssues(fact: GraphFact): readonly string[];
@@ -0,0 +1,79 @@
1
+ import { DataFactory } from 'n3';
2
+ import { repositoryFileNode } from '@weftlet/core';
3
+ import { CG, RDF, XSD } from './shacl-shapes.js';
4
+ const { namedNode: iri, literal } = DataFactory;
5
+ export function addCodeEvidence(dataset, record, fact, nodeIri, includeNodes = true) {
6
+ const property = (subject, key, value) => {
7
+ if (value === undefined)
8
+ return;
9
+ dataset.addQuad(subject, iri(CG + key), literal(String(value), iri(XSD + (typeof value === 'number' ? 'integer' : 'string'))));
10
+ };
11
+ for (const key of ['startColumn', 'endColumn', 'columnEncoding'])
12
+ property(record, key, fact.provenance[key]);
13
+ const code = fact.codeOccurrence;
14
+ if (code !== undefined) {
15
+ dataset.addQuad(record, iri(RDF + 'type'), iri(CG + 'CodeOccurrence'));
16
+ for (const [key, value] of Object.entries({ codeVersion: code.schemaVersion, codeRole: code.role, codeLocationKind: code.locationKind,
17
+ codeNativeRoles: code.nativeRoles, codeEnclosure: code.enclosure }))
18
+ property(record, key, value);
19
+ if (code.enclosingSymbolId !== undefined)
20
+ dataset.addQuad(record, iri(CG + 'enclosingSymbol'), iri(nodeIri(code.enclosingSymbolId)));
21
+ if (code.declarationRange !== undefined) {
22
+ const range = iri(record.value + ':declaration-range');
23
+ dataset.addQuad(record, iri(CG + 'declarationRange'), range);
24
+ dataset.addQuad(range, iri(RDF + 'type'), iri(CG + 'CodeRange'));
25
+ for (const [key, value] of Object.entries(code.declarationRange))
26
+ property(range, key, value);
27
+ }
28
+ }
29
+ if (includeNodes)
30
+ for (const node of [fact.subject, fact.object])
31
+ addCodeNodeEvidence(dataset, node, nodeIri);
32
+ }
33
+ export function addCodeNodeEvidence(dataset, node, nodeIri) {
34
+ const property = (subject, key, value) => {
35
+ if (value !== undefined)
36
+ dataset.addQuad(subject, iri(CG + key), literal(String(value), iri(XSD + (typeof value === 'number' ? 'integer' : 'string'))));
37
+ };
38
+ if (node.kind !== 'CodeSymbol' && node.codeSymbol === undefined)
39
+ return;
40
+ const term = iri(nodeIri(node.id));
41
+ dataset.addQuad(term, iri(RDF + 'type'), iri(CG + 'CodeSymbol'));
42
+ const value = node.codeSymbol;
43
+ if (value === undefined)
44
+ return; // The shape reports missing required metadata.
45
+ for (const [key, item] of Object.entries({ codeVersion: value.schemaVersion, codeProvider: value.provider,
46
+ codeRepository: value.repositoryId, nativeSymbol: value.nativeSymbol, codeIdentityScope: value.identityScope,
47
+ codeDocument: value.documentPath, codeIndex: value.indexDigest, codeResolution: value.resolution }))
48
+ property(term, key, item);
49
+ }
50
+ export function invalidCodeRange(range) {
51
+ if (range.startColumn === undefined && range.endColumn === undefined && range.columnEncoding === undefined)
52
+ return false;
53
+ return !Number.isSafeInteger(range.startColumn) || !Number.isSafeInteger(range.endColumn) ||
54
+ range.startColumn < 1 || range.endColumn < 1 || range.columnEncoding !== 'utf-16' ||
55
+ range.endLine < range.startLine || (range.endLine === range.startLine && range.endColumn < range.startColumn);
56
+ }
57
+ export function codeEvidenceIssues(fact) {
58
+ const code = fact.codeOccurrence;
59
+ if (!code)
60
+ return [];
61
+ const issues = [];
62
+ const file = repositoryFileNode(fact.provenance.path);
63
+ if ((code.nativeRoles & 1) !== (code.role === 'definition' ? 1 : 0))
64
+ issues.push('Code role disagrees with native definition flag');
65
+ if (code.role === 'definition' && (fact.object.id !== file.id || fact.object.kind !== file.kind)) {
66
+ issues.push('Code definition must identify its canonical cited physical file');
67
+ }
68
+ if (code.role === 'reference') {
69
+ if (code.declarationRange !== undefined)
70
+ issues.push('A reference cannot declare a definition range');
71
+ const sourceId = code.enclosure === 'native-range' ? code.enclosingSymbolId : file.id;
72
+ if (fact.subject.id !== sourceId)
73
+ issues.push('Reference source disagrees with lexical enclosure');
74
+ }
75
+ const zeroWidth = fact.provenance.startLine === fact.provenance.endLine && fact.provenance.startColumn === fact.provenance.endColumn;
76
+ if (zeroWidth !== (code.locationKind === 'zero-width-anchor'))
77
+ issues.push('Code location kind disagrees with source span');
78
+ return issues;
79
+ }
@@ -0,0 +1,2 @@
1
+ import type { ContractCompatibilityReport, GraphFact } from '@weftlet/core';
2
+ export declare function compatibilityContext(all: readonly GraphFact[], selected: readonly GraphFact[]): ContractCompatibilityReport['context'];
@@ -0,0 +1,18 @@
1
+ /** Other declarations of the same endpoint/property remain visible; never choose a winning assertion. */
2
+ function competes(selected, other) {
3
+ const left = selected.contractObligation?.declaration, right = other.contractObligation?.declaration;
4
+ if (!left || !right || other.id === selected.id || selected.object.id !== other.object.id)
5
+ return false;
6
+ return left.role === right.role && left.endpoint === right.endpoint && left.domain === right.domain &&
7
+ left.stage === right.stage && left.constraint?.metric === right.constraint?.metric;
8
+ }
9
+ export function compatibilityContext(all, selected) {
10
+ const contractNodeIds = [...new Set(selected.filter(fact => fact.contractObligation).map(fact => fact.object.id))].sort();
11
+ const endpointNodeIds = [...new Set(selected.filter(fact => fact.contractObligation).map(fact => fact.subject.id))].sort();
12
+ const selectedIds = new Set(selected.map(fact => fact.id));
13
+ const competing = [...new Set(all.filter(other => !selectedIds.has(other.id) && selected.some(fact => competes(fact, other))).map(fact => fact.id))].sort();
14
+ return { contractNodeIds, endpointNodeIds,
15
+ otherIncidentFacts: all.filter(fact => !selectedIds.has(fact.id) &&
16
+ [fact.subject.id, fact.object.id].some(id => contractNodeIds.includes(id))).length,
17
+ competingFactIds: competing.slice(0, 20), competingFacts: competing.length, omittedCompetingFacts: Math.max(0, competing.length - 20) };
18
+ }
@@ -0,0 +1,2 @@
1
+ import { type ContractCompatibilityReport } from '@weftlet/core';
2
+ export declare function renderContractCompatibility(report: Omit<ContractCompatibilityReport, 'markdown'>): string;
@@ -0,0 +1,45 @@
1
+ import { sourceLocation } from '@weftlet/core';
2
+ import { factEvidenceDescription } from './evidence-description.js';
3
+ import { diagnosticSummary } from './diagnostics.js';
4
+ import { coverageDescription } from './coverage.js';
5
+ const quoted = (value) => JSON.stringify(value).replace(/[\\`*_{}\[\]()<>!|]/g, '\\$&');
6
+ const explanations = {
7
+ 'missing-assertion': 'One or both exact assertion IDs are absent from this snapshot.',
8
+ 'invalid-evidence': 'The selected records lack valid obligation metadata or matching snapshot/revision provenance.',
9
+ 'wrong-roles': 'Select a producer guarantee first and a consumer requirement second.',
10
+ 'different-contracts': 'The assertions do not identify the same exact repository/scoped/transport/versioned contract.',
11
+ 'unknown-version': 'A contract version is unknown; no version equivalence is assumed.',
12
+ 'missing-context': 'Documented domain, stage, numeric constraint or unit is missing.',
13
+ 'different-domain': 'The documented domains differ.',
14
+ 'different-metric': 'The documented metrics differ; no semantic equivalence is inferred.',
15
+ 'different-unit': 'The units differ; no unit conversion is inferred.',
16
+ 'different-stage': 'The measurement stages differ; no elapsed-time or transformation model is available.',
17
+ 'unsupported-number': 'A numeric limit is nonfinite or outside the supported safe numeric magnitude.',
18
+ 'competing-declarations': 'Other assertions describe this endpoint/property; no winning declaration was selected.',
19
+ 'extraction-gaps': 'Relevant source/extraction gaps remain in the saved evidence.',
20
+ subset: 'At the same declared stage, every value allowed by the producer constraint is allowed by the consumer constraint.',
21
+ 'overlap-only': 'Some allowed producer values do not satisfy the consumer constraint. This alone is not a contradiction.',
22
+ disjoint: 'At the same declared stage, no value satisfies both constraints. This is a documented range incompatibility for an untransformed accepted value.',
23
+ 'consumer-revalidation': 'The consumer explicitly requires revalidation. Verify its check and rejection behavior; the declaration alone does not prove them.',
24
+ };
25
+ export function renderContractCompatibility(report) {
26
+ const { snapshot, assessment, context, evidenceState } = report;
27
+ return ['# Selected contract constraint assessment',
28
+ `Snapshot: ${quoted(snapshot.snapshotId)} (${snapshot.commitSha}); configuration ${snapshot.configurationDigest ?? 'unknown'}`,
29
+ `Scope: ${snapshot.scope?.map(quoted).join(', ') ?? 'unknown'}; selected assertions: ${quoted(report.selection.producerFactId)}, ${quoted(report.selection.consumerFactId)}`,
30
+ `Result: ${assessment.status}; authority: ${assessment.authority}; method: ${assessment.version}; CI gate eligible: false; fulfillment: not evaluated`,
31
+ ...assessment.reasons.map(reason => `- ${explanations[reason]}`),
32
+ ...(evidenceState ? [`Stale: ${evidenceState.stale}; configuration matches: ${evidenceState.configurationMatches}; extraction compatible: ${evidenceState.extractionCompatible ?? 'unknown'}`,
33
+ `Excluded working changes: ${evidenceState.excludedWorkingChanges.map(quoted).join(', ') || '(none)'}; overlay freshness: ${evidenceState.overlayFreshness ?? 'not an overlay'}`] : []),
34
+ '', '## Cited declarations', ...report.facts.map(fact => `- ${quoted(fact.id)} [${fact.authority ?? 'unspecified'}; source ${fact.sourceAuthority ?? 'unspecified'}; review ${fact.extractionReview ?? 'unspecified'}] ` +
35
+ `${quoted(sourceLocation(fact.provenance))}–${fact.provenance.endLine}@${fact.provenance.commitSha}: ${quoted(factEvidenceDescription(fact) ?? 'Not a structured obligation')}`),
36
+ '', '## Assessment limits', ...assessment.conditions.map(condition => `- ${condition}`),
37
+ coverageDescription(report.coverage), ...report.coverage.gaps.map(quoted),
38
+ '', '## Further evidence',
39
+ `Other contract-incident assertions: ${context.otherIncidentFacts}; competing declarations: ${context.competingFacts}; omitted competing IDs: ${context.omittedCompetingFacts}.`,
40
+ ...context.competingFactIds.map(id => `- Competing assertion: ${quoted(id)}`),
41
+ ...context.contractNodeIds.map(id => `- Use graph.neighbors with contract ${quoted(id)} and the same snapshot for all declarations and continuation.`),
42
+ ...context.endpointNodeIds.map(id => `- Use graph.neighbors with endpoint ${quoted(id)} for indexed definitions, mappings and tests; file existence alone proves no fulfillment.`),
43
+ '', '## Scoped diagnostics', ...diagnosticSummary(report).map(quoted),
44
+ ].join('\n');
45
+ }
@@ -0,0 +1,3 @@
1
+ import type { ContractCompatibilityRequest } from '@weftlet/core';
2
+ /** No search, implicit current head, policy mutation or caller-supplied authority. */
3
+ export declare function parseContractCompatibilityRequest(value: unknown): ContractCompatibilityRequest;
@@ -0,0 +1,18 @@
1
+ /** No search, implicit current head, policy mutation or caller-supplied authority. */
2
+ export function parseContractCompatibilityRequest(value) {
3
+ if (!value || typeof value !== 'object' || Array.isArray(value))
4
+ throw new Error('graph.compatibility arguments must be an object');
5
+ const item = value, fields = ['snapshotId', 'producerFactId', 'consumerFactId'];
6
+ if (Object.keys(item).some(key => !fields.includes(key)))
7
+ throw new Error('Unknown graph.compatibility field');
8
+ const identifier = (key, maximum) => {
9
+ const value = item[key];
10
+ if (typeof value !== 'string' || !value.trim() || value.length > maximum)
11
+ throw new Error(`${key} must be a nonempty string of at most ${maximum} characters`);
12
+ return value;
13
+ };
14
+ const request = { snapshotId: identifier('snapshotId', 256), producerFactId: identifier('producerFactId', 8192), consumerFactId: identifier('consumerFactId', 8192) };
15
+ if (request.producerFactId === request.consumerFactId)
16
+ throw new Error('Select distinct producer and consumer assertion IDs');
17
+ return request;
18
+ }
@@ -0,0 +1,7 @@
1
+ import type { ContractCompatibilityReport, ContractCompatibilityRequest, ContractCompatibilityService, SnapshotStore } from '@weftlet/core';
2
+ /** Read-only exact pair assessment over the same lossless RDF facts used by exploration and visualization. */
3
+ export declare class SnapshotContractCompatibilityService implements ContractCompatibilityService {
4
+ private readonly store;
5
+ constructor(store: Pick<SnapshotStore, 'read'>);
6
+ assess(input: ContractCompatibilityRequest): Promise<ContractCompatibilityReport>;
7
+ }
@@ -0,0 +1,67 @@
1
+ import { RdfEvidenceGraph } from './rdf.js';
2
+ import { snapshotContext } from './snapshot-context.js';
3
+ import { selectDiagnostics } from './diagnostics.js';
4
+ import { parseContractCompatibilityRequest } from './compatibility-request.js';
5
+ import { compareDeclaredConstraints } from './constraint-comparison.js';
6
+ import { compatibilityContext } from './compatibility-context.js';
7
+ import { renderContractCompatibility } from './compatibility-renderer.js';
8
+ import { coverageForFacts } from './coverage.js';
9
+ const conditions = [
10
+ 'Only the two selected numeric declarations are compared, within this saved scope; other product obligations may exist.',
11
+ 'Matching metric/unit names assume the same meaning. No unit conversion, transformations or elapsed-time model is inferred.',
12
+ 'Source approval and assertion review stay on the cited facts. This assessment supplies no policy approval or CI pass/fail.',
13
+ 'Implementation correctness, runtime delivery, consumer rejection/availability and current-diff validity are not established.',
14
+ ];
15
+ async function assessSelected(snapshot, facts, context, diagnostics) {
16
+ const base = { version: 'declared-constraint-comparison-v1', scope: 'selected-assertion-pair',
17
+ authority: facts.some(fact => fact.authority === 'draft') ? 'draft' : 'inferred',
18
+ gateEligible: false, fulfillment: 'not-evaluated', conditions };
19
+ const unknown = (...reasons) => ({ ...base, status: 'unknown', reasons });
20
+ if (facts.length !== 2)
21
+ return unknown('missing-assertion');
22
+ if (facts.some(fact => !fact.contractObligation || fact.snapshotId !== snapshot.manifest.snapshotId ||
23
+ fact.provenance.commitSha !== snapshot.manifest.commitSha))
24
+ return unknown('invalid-evidence');
25
+ // Bound structural validation to these two records. Never revalidate the complete saved graph on a read query.
26
+ if (!(await new RdfEvidenceGraph({ ...snapshot, facts }).validate()).conforms)
27
+ return unknown('invalid-evidence');
28
+ const [producer, consumer] = facts;
29
+ if (producer.object.id !== consumer.object.id)
30
+ return unknown('different-contracts');
31
+ const gaps = [];
32
+ if (context.competingFacts)
33
+ gaps.push('competing-declarations');
34
+ if (['missingIntent', 'ambiguousIntent', 'implementationGaps'].some(key => {
35
+ const section = diagnostics[key];
36
+ return section.items.length || section.omittedRelevant;
37
+ }))
38
+ gaps.push('extraction-gaps');
39
+ if (gaps.length)
40
+ return unknown(...gaps);
41
+ return { ...base, ...compareDeclaredConstraints(producer.contractObligation.declaration, consumer.contractObligation.declaration) };
42
+ }
43
+ /** Read-only exact pair assessment over the same lossless RDF facts used by exploration and visualization. */
44
+ export class SnapshotContractCompatibilityService {
45
+ store;
46
+ constructor(store) {
47
+ this.store = store;
48
+ }
49
+ async assess(input) {
50
+ const request = parseContractCompatibilityRequest(input), snapshot = await this.store.read(request.snapshotId);
51
+ if (!snapshot)
52
+ throw new Error('No graph snapshot is available for that identifier');
53
+ if (snapshot.manifest.snapshotId !== request.snapshotId)
54
+ throw new Error('Saved snapshot identity does not match the requested identifier');
55
+ const all = await new RdfEvidenceGraph(snapshot).facts();
56
+ const byId = new Map(all.map(fact => [fact.id, fact]));
57
+ if (byId.size !== all.length)
58
+ throw new Error('Duplicate fact identities prevent exact compatibility selection');
59
+ const facts = [request.producerFactId, request.consumerFactId].flatMap(id => byId.has(id) ? [byId.get(id)] : []);
60
+ const context = compatibilityContext(all, facts);
61
+ const diagnostics = selectDiagnostics(snapshot, {}, facts, [], [], new Set(context.contractNodeIds));
62
+ const assessment = await assessSelected(snapshot, facts, context, diagnostics);
63
+ const plain = { snapshot: snapshotContext(snapshot.manifest), selection: request, facts,
64
+ coverage: coverageForFacts(facts, snapshot), assessment, context, diagnostics };
65
+ return { ...plain, markdown: renderContractCompatibility(plain) };
66
+ }
67
+ }
@@ -0,0 +1,7 @@
1
+ import type { CompatibilityReason, ContractCompatibilityStatus, ObligationDeclaration } from '@weftlet/core';
2
+ export interface ConstraintComparison {
3
+ readonly status: ContractCompatibilityStatus;
4
+ readonly reasons: readonly CompatibilityReason[];
5
+ }
6
+ /** Arithmetic on declared numeric sets only. No unit conversion, policy approval or implementation inference. */
7
+ export declare function compareDeclaredConstraints(producer: ObligationDeclaration, consumer: ObligationDeclaration): ConstraintComparison;