@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
@@ -1,7 +1,14 @@
1
1
  import { scipSourcePatterns } from '../configuration/scip-project.js';
2
+ import { buildCodeInventory } from '../extraction/code-inventory.js';
2
3
  import { contractPatternRoots } from "../repository/glob.js";
3
4
  import { repositoryFileExtractorIdentity, repositoryContractExtractorIdentity, scipSymbolExtractorIdentity } from '../extraction/static.js';
4
- import { sha256, stableJson } from "../infrastructure/util.js";
5
+ import { sha256, stableJson, stableJsonDigest } from "../infrastructure/util.js";
6
+ import { assembleDocumentCodeBindings } from './document-code-bindings.js';
7
+ import { documentCodeBindingIdentity } from '../extraction/document-code-bindings.js';
8
+ import { documentObligationIdentity } from '../extraction/document-obligations.js';
9
+ import { assembleDocumentObligations } from './document-obligations.js';
10
+ import { staticCallIdentity } from '../extraction/static-calls.js';
11
+ import { extractEvidence } from './extractor-results.js';
5
12
  import { documentLinkerIdentity, linkSemanticDocuments } from "../extraction/document.js";
6
13
  import { repositoryNodeIdentityVersion } from '../extraction/repository-nodes.js';
7
14
  import { selectHydrationInputs, classifyHydratedFacts, documentSelectionIdentity } from './hydration-inputs.js';
@@ -36,7 +43,7 @@ export class ContextGraphHydrator {
36
43
  .sort((left, right) => stableJson(left).localeCompare(stableJson(right)));
37
44
  return {
38
45
  commitSha,
39
- digest: sha256(stableJson(references)),
46
+ digest: stableJsonDigest(references),
40
47
  references,
41
48
  };
42
49
  }
@@ -64,10 +71,11 @@ export class ContextGraphHydrator {
64
71
  }
65
72
  const { sources } = selection;
66
73
  await this.dependencies.ontologyCompiler.checkReady?.();
67
- const prior = await snapshotStore.readHead(options.headName);
68
- const baseSha = prior?.manifest.commitSha === commitSha
69
- ? prior.manifest.baseSha
70
- : (prior?.manifest.commitSha ?? (await repository.firstParent(commitSha)));
74
+ const prior = snapshotStore.readHeadManifest ? await snapshotStore.readHeadManifest(options.headName)
75
+ : (await snapshotStore.readHead(options.headName))?.manifest;
76
+ const baseSha = prior?.commitSha === commitSha
77
+ ? prior.baseSha
78
+ : (prior?.commitSha ?? (await repository.firstParent(commitSha)));
71
79
  const allRoots = [
72
80
  ...new Set([
73
81
  ...scopePaths,
@@ -91,8 +99,8 @@ export class ContextGraphHydrator {
91
99
  const runtimeSuffix = config.runtimeEvidence.length > 0
92
100
  ? `runtime-${runtimeRevision.digest.slice(0, 12)}`
93
101
  : undefined;
94
- const configurationDigest = sha256(stableJson(config));
95
- const scopeDigest = sha256(stableJson(scopePaths)).slice(0, 12);
102
+ const configurationDigest = stableJsonDigest(config);
103
+ const scopeDigest = stableJsonDigest(scopePaths).slice(0, 12);
96
104
  const effectiveSuffix = [`graph-v${graphAssemblyVersion}`, `links-${documentLinkerIdentity.version}`, `files-${repositoryFileExtractorIdentity.version}`,
97
105
  `contracts-${repositoryContractExtractorIdentity.version}`,
98
106
  `symbols-${scipSymbolExtractorIdentity.version}`,
@@ -114,6 +122,7 @@ export class ContextGraphHydrator {
114
122
  };
115
123
  // Validate committed routing inputs before invoking any provider or indexer.
116
124
  const routing = await extractRoutingDeclarations(repository, context, config.routingSources ?? []);
125
+ const obligations = await assembleDocumentObligations(repository, context, selection, config);
117
126
  const { hydrated, semanticExtracted, semanticReused } = await hydrateSemanticSources({
118
127
  sources, context, config: config.semantic, configurationDigest,
119
128
  hydrator: this.dependencies.semanticHydrator, forceRevision,
@@ -122,7 +131,7 @@ export class ContextGraphHydrator {
122
131
  const artifacts = hydrated.map((result) => result.artifact);
123
132
  const diagnostics = hydrated.flatMap(({ source, artifact }) => (artifact.diagnostics ?? []).map((item) => ({
124
133
  ...item,
125
- id: sha256(stableJson({ artifact: artifact.key, diagnostic: item.id, path: source.path })),
134
+ id: stableJsonDigest({ artifact: artifact.key, diagnostic: item.id, path: source.path }),
126
135
  provenance: item.provenance.map((span) => ({
127
136
  ...span,
128
137
  path: source.path,
@@ -132,7 +141,7 @@ export class ContextGraphHydrator {
132
141
  })));
133
142
  const semanticFacts = classifyHydratedFacts(hydrated.flatMap(({ source, artifact }) => artifact.facts.map((fact) => ({
134
143
  ...fact,
135
- id: sha256(stableJson({ artifact: artifact.key, fact: fact.id, path: source.path })),
144
+ id: stableJsonDigest({ artifact: artifact.key, fact: fact.id, path: source.path }),
136
145
  provenance: {
137
146
  ...fact.provenance,
138
147
  path: source.path,
@@ -147,7 +156,10 @@ export class ContextGraphHydrator {
147
156
  checkpoint();
148
157
  options.onProgress?.({ phase: 'index-typescript' });
149
158
  const scip = await this.dependencies.staticIndexer.index(context);
150
- const extractorFacts = (await Promise.all(this.dependencies.extractorFactory(scip).map((extractor) => extractor.extract(context)))).flat();
159
+ const codeInventory = buildCodeInventory(await repository.listEntriesAt(commitSha), await repository.listFilesAt(commitSha, config.codeRoots), config.codeRoots, config.scip.sourceProject, scip);
160
+ const extracted = await extractEvidence(this.dependencies.extractorFactory(scip), context);
161
+ const extractorFacts = extracted.facts;
162
+ diagnostics.push(...extracted.diagnostics);
151
163
  const trackedPaths = await repository.listAllFilesAt(commitSha);
152
164
  const tracked = new Set(trackedPaths);
153
165
  for (const fact of semanticFacts) {
@@ -155,7 +167,7 @@ export class ContextGraphHydrator {
155
167
  const path = /^(?:component:file:|document:)(.+)$/.exec(node.id)?.[1];
156
168
  if (path && !tracked.has(path))
157
169
  diagnostics.push({
158
- id: sha256(stableJson({ fact: fact.id, missing: path })),
170
+ id: stableJsonDigest({ fact: fact.id, missing: path }),
159
171
  kind: 'implementationGaps',
160
172
  reason: `Cited implementation path ${path} is absent from the committed repository.`,
161
173
  relatedNodeIds: [node.id], provenance: [fact.provenance], evidenceScope: 'snapshot-query',
@@ -163,14 +175,15 @@ export class ContextGraphHydrator {
163
175
  }
164
176
  }
165
177
  const documents = linkSemanticDocuments(context, hydrated, trackedPaths, sources.filter((source) => config.approvedIntentSources?.some((pattern) => matchesRepositoryGlob(source.path, pattern))).map((source) => source.path), { canonicalReferences: !config.documentTraversal });
166
- diagnostics.push(...documents.diagnostics);
178
+ const bindings = await assembleDocumentCodeBindings(repository, context, selection, config, extractorFacts, [...semanticFacts, ...extractorFacts, ...routing.facts, ...obligations.facts]);
179
+ diagnostics.push(...documents.diagnostics, ...bindings.diagnostics, ...obligations.diagnostics);
167
180
  diagnostics.push(...routing.diagnostics, ...(selection.documentEvidence?.diagnostics ?? []));
168
181
  checkpoint();
169
182
  const runtimeEvidence = runtimeRevision.references;
170
183
  const runtimeFacts = runtimeEvidenceFacts(context, runtimeEvidence);
171
184
  const structuralFacts = selection.documentEvidence?.facts.map(fact => ({ ...fact, snapshotId })) ?? [];
172
185
  const facts = [...semanticFacts, ...classifyHydratedFacts(documents.facts, selection.documentEvidence),
173
- ...structuralFacts, ...extractorFacts, ...routing.facts, ...runtimeFacts].sort((left, right) => left.id.localeCompare(right.id));
186
+ ...structuralFacts, ...extractorFacts, ...bindings.facts, ...routing.facts, ...obligations.facts, ...runtimeFacts].sort((left, right) => left.id.localeCompare(right.id));
174
187
  const validationCounts = { factCount: facts.length };
175
188
  await measureValidationPhase('validate-identities', validationCounts, options.onProgress, () => {
176
189
  const report = assertGraphIdentities(facts, {
@@ -207,9 +220,10 @@ export class ContextGraphHydrator {
207
220
  unresolved,
208
221
  diagnostics,
209
222
  semanticSources: selectedSemanticSources,
223
+ codeInventory,
210
224
  ...(selection.documentSelection ? { documentSelection: selection.documentSelection } : {}),
211
225
  };
212
- const contentDigest = sha256(stableJson(logical));
226
+ const contentDigest = stableJsonDigest(logical);
213
227
  const snapshot = {
214
228
  manifest: {
215
229
  schemaVersion: 1,
@@ -220,6 +234,7 @@ export class ContextGraphHydrator {
220
234
  snapshotId,
221
235
  configurationDigest,
222
236
  scope: scopePaths,
237
+ codeInventory,
223
238
  ...(selection.documentSelection ? { documentSelection: selection.documentSelection } : {}),
224
239
  ontologyVersion: config.semantic.ontologyVersion,
225
240
  extractorVersions: {
@@ -227,8 +242,11 @@ export class ContextGraphHydrator {
227
242
  ...documentExtractionVersions(config),
228
243
  [repositoryContractExtractorIdentity.id]: repositoryContractExtractorIdentity.version,
229
244
  [scipSymbolExtractorIdentity.id]: scipSymbolExtractorIdentity.version,
245
+ [staticCallIdentity.id]: staticCallIdentity.version,
230
246
  'repository-node-identity': String(repositoryNodeIdentityVersion),
231
247
  [documentLinkerIdentity.id]: documentLinkerIdentity.version,
248
+ [documentCodeBindingIdentity.id]: documentCodeBindingIdentity.version,
249
+ [documentObligationIdentity.id]: documentObligationIdentity.version,
232
250
  ...(config.routingSources?.length ? { [routingExtractorIdentity.id]: routingExtractorIdentity.version } : {}),
233
251
  semantic: config.semantic.extractorVersion,
234
252
  scip: scip.version,
@@ -265,7 +283,9 @@ export class ContextGraphHydrator {
265
283
  return { snapshot, semanticExtracted, semanticReused };
266
284
  }
267
285
  async compareClean(options) {
268
- const current = await this.dependencies.snapshotStore.readHead(options.headName);
286
+ const store = this.dependencies.snapshotStore;
287
+ const current = store.readHeadManifest ? await store.readHeadManifest(options.headName)
288
+ : (await store.readHead(options.headName))?.manifest;
269
289
  if (!current)
270
290
  throw new Error('No incremental snapshot exists to compare');
271
291
  const clean = await this.hydrate({
@@ -273,8 +293,8 @@ export class ContextGraphHydrator {
273
293
  headName: `${options.headName}-clean`,
274
294
  snapshotSuffix: `clean-${Date.now()}`,
275
295
  });
276
- if (clean.snapshot.manifest.contentDigest !== current.manifest.contentDigest) {
277
- throw new Error(`Clean hydration drift: ${current.manifest.contentDigest} != ${clean.snapshot.manifest.contentDigest}`);
296
+ if (clean.snapshot.manifest.contentDigest !== current.contentDigest) {
297
+ throw new Error(`Clean hydration drift: ${current.contentDigest} != ${clean.snapshot.manifest.contentDigest}`);
278
298
  }
279
299
  return clean;
280
300
  }
@@ -0,0 +1,108 @@
1
+ import type { ContextPack, MappingProposalArtifact, MappingProposalProvider, MappingProposalRequest, MappingProposalStore, SnapshotStore } from '@weftlet/core';
2
+ import type { GitRepository } from '../repository/git.js';
3
+ export interface MappingOperationOptions {
4
+ readonly allowModel?: boolean;
5
+ readonly signal?: AbortSignal;
6
+ readonly onProgress?: (event: {
7
+ phase: 'plan' | 'reuse' | 'provider' | 'validate' | 'saved';
8
+ operationId: string;
9
+ elapsedMs: number;
10
+ }) => void;
11
+ }
12
+ export declare class MappingProposalService {
13
+ private readonly repository;
14
+ private readonly snapshots;
15
+ private readonly artifacts;
16
+ private readonly settings;
17
+ constructor(repository: GitRepository, snapshots: Pick<SnapshotStore, 'read'>, artifacts: MappingProposalStore, settings: {
18
+ repositoryId: string;
19
+ configurationDigest: string;
20
+ expectedExtractors: Readonly<Record<string, string>>;
21
+ providerIdentity: string;
22
+ timeoutMs: number;
23
+ provider: () => MappingProposalProvider;
24
+ });
25
+ private snapshot;
26
+ private evidence;
27
+ private key;
28
+ private validate;
29
+ plan(value: MappingProposalRequest): Promise<{
30
+ evidenceState: {
31
+ readonly currentCommit?: string;
32
+ readonly excludedWorkingChanges: readonly string[];
33
+ readonly capturedWorkingChanges?: readonly string[];
34
+ readonly changedSinceCapture?: readonly string[];
35
+ readonly overlayFreshness?: "current" | "stale" | "unknown";
36
+ readonly stale: boolean;
37
+ readonly configurationMatches: boolean;
38
+ readonly extractionCompatible?: boolean;
39
+ readonly incompatibleExtractors?: readonly string[];
40
+ readonly eligibleForCurrentDiffValidation: false;
41
+ };
42
+ key: string;
43
+ cached: boolean;
44
+ expectedModelRequests: number;
45
+ timeoutMs: number;
46
+ automaticRetries: number;
47
+ markdown: string;
48
+ ready: boolean;
49
+ issues: {
50
+ readonly code: string;
51
+ readonly path?: string;
52
+ }[];
53
+ version: "mapping-proposal-v1";
54
+ request: Required<MappingProposalRequest>;
55
+ snapshot: ContextPack["snapshot"];
56
+ snapshotDigest: string;
57
+ subjectEvidence: readonly import("@weftlet/core").GraphFact[];
58
+ associations: readonly import("@weftlet/core").GraphFact[];
59
+ candidates: readonly import("@weftlet/core").MappingCandidate[];
60
+ candidateEnumeration: {
61
+ readonly complete: boolean;
62
+ readonly observed: number;
63
+ readonly returned: number;
64
+ };
65
+ excluded: readonly {
66
+ readonly path: string;
67
+ readonly reason: string;
68
+ readonly count: number;
69
+ }[];
70
+ inputBytes: number;
71
+ inputDigest: string;
72
+ }>;
73
+ read(snapshotId: string, key: string): Promise<{
74
+ artifact: MappingProposalArtifact;
75
+ reused: boolean;
76
+ evidenceState: {
77
+ readonly currentCommit?: string;
78
+ readonly excludedWorkingChanges: readonly string[];
79
+ readonly capturedWorkingChanges?: readonly string[];
80
+ readonly changedSinceCapture?: readonly string[];
81
+ readonly overlayFreshness?: "current" | "stale" | "unknown";
82
+ readonly stale: boolean;
83
+ readonly configurationMatches: boolean;
84
+ readonly extractionCompatible?: boolean;
85
+ readonly incompatibleExtractors?: readonly string[];
86
+ readonly eligibleForCurrentDiffValidation: false;
87
+ } | undefined;
88
+ markdown: string;
89
+ }>;
90
+ private report;
91
+ propose(value: MappingProposalRequest, options?: MappingOperationOptions): Promise<{
92
+ artifact: MappingProposalArtifact;
93
+ reused: boolean;
94
+ evidenceState: {
95
+ readonly currentCommit?: string;
96
+ readonly excludedWorkingChanges: readonly string[];
97
+ readonly capturedWorkingChanges?: readonly string[];
98
+ readonly changedSinceCapture?: readonly string[];
99
+ readonly overlayFreshness?: "current" | "stale" | "unknown";
100
+ readonly stale: boolean;
101
+ readonly configurationMatches: boolean;
102
+ readonly extractionCompatible?: boolean;
103
+ readonly incompatibleExtractors?: readonly string[];
104
+ readonly eligibleForCurrentDiffValidation: false;
105
+ } | undefined;
106
+ markdown: string;
107
+ }>;
108
+ }
@@ -0,0 +1,142 @@
1
+ import { sha256, stableJson } from '../infrastructure/util.js';
2
+ import { jsonDigest } from '../infrastructure/json-digest.js';
3
+ import { prepareMappingProposal } from '../extraction/mapping-plan.js';
4
+ import { parseMappingRequest, mappingVersion } from '../extraction/mapping-request.js';
5
+ import { validateMappingOutput } from '../extraction/mapping-output.js';
6
+ import { SemanticProviderError } from '../extraction/semantic-process.js';
7
+ import { inspectSavedEvidence } from './saved-evidence.js';
8
+ import { snapshotContext } from '../graph/snapshot-context.js';
9
+ import { RdfEvidenceGraph } from '../graph/rdf.js';
10
+ export class MappingProposalService {
11
+ repository;
12
+ snapshots;
13
+ artifacts;
14
+ settings;
15
+ constructor(repository, snapshots, artifacts, settings) {
16
+ this.repository = repository;
17
+ this.snapshots = snapshots;
18
+ this.artifacts = artifacts;
19
+ this.settings = settings;
20
+ if (!Number.isSafeInteger(settings.timeoutMs) || settings.timeoutMs < 1 || settings.timeoutMs > 600_000)
21
+ throw Error('Mapping timeout must be between 1 and 600000 ms');
22
+ }
23
+ async snapshot(snapshotId) {
24
+ const snapshot = await this.snapshots.read(snapshotId);
25
+ if (!snapshot || snapshot.manifest.snapshotId !== snapshotId || snapshot.manifest.repositoryId !== this.settings.repositoryId)
26
+ throw Error('Mapping snapshot is missing or has mismatched identity');
27
+ const facts = await new RdfEvidenceGraph(snapshot).facts();
28
+ if (new Set(facts.map(f => f.id)).size !== facts.length)
29
+ throw Error('Duplicate mapping evidence identities');
30
+ return { ...snapshot, facts };
31
+ }
32
+ evidence(snapshot) {
33
+ return inspectSavedEvidence(snapshotContext(snapshot.manifest), this.repository, this.settings.configurationDigest, this.settings.expectedExtractors);
34
+ }
35
+ key(plan) {
36
+ // Checkout dirtiness and operational timeout never change an immutable evidence request's identity.
37
+ const { evidenceState: _state, ...saved } = plan;
38
+ return jsonDigest({ version: mappingVersion, provider: this.settings.providerIdentity, plan: saved });
39
+ }
40
+ validate(artifact, snapshot, key) {
41
+ if (artifact.key !== key || artifact.version !== mappingVersion || artifact.plan.version !== mappingVersion || !artifact.plan.ready ||
42
+ artifact.plan.snapshotDigest !== jsonDigest(snapshot) || artifact.plan.snapshot.snapshotId !== snapshot.manifest.snapshotId ||
43
+ artifact.authority !== 'inferred' || artifact.extractionReview !== 'unreviewed' || artifact.establishesCompliance !== false ||
44
+ artifact.outputDigest !== jsonDigest({ plan: artifact.plan, providerIdentity: artifact.providerIdentity, output: artifact.output })) {
45
+ throw Error('Mapping artifact integrity or snapshot binding mismatch');
46
+ }
47
+ if (jsonDigest({ version: mappingVersion, provider: artifact.providerIdentity, plan: artifact.plan }) !== key)
48
+ throw Error('Mapping artifact key mismatch');
49
+ validateMappingOutput(artifact.output, artifact.plan);
50
+ return artifact;
51
+ }
52
+ async plan(value) {
53
+ const request = parseMappingRequest(value), snapshot = await this.snapshot(request.snapshotId);
54
+ const prepared = await prepareMappingProposal(this.repository, snapshot, request);
55
+ const evidenceState = await this.evidence(snapshot);
56
+ const issues = [...prepared.plan.issues];
57
+ if (!evidenceState.extractionCompatible)
58
+ issues.push({ code: 'incompatible-snapshot-extraction' });
59
+ const plan = { ...prepared.plan, ready: prepared.plan.ready && !issues.length, issues };
60
+ const key = this.key(plan), cached = plan.ready ? await this.artifacts.read(key) : undefined;
61
+ if (cached)
62
+ this.validate(cached, snapshot, key);
63
+ return { ...plan, evidenceState, key, cached: !!cached, expectedModelRequests: plan.ready && !cached ? 1 : 0,
64
+ timeoutMs: this.settings.timeoutMs, automaticRetries: 0,
65
+ markdown: `Mapping plan: ${plan.ready ? 'ready' : 'blocked'}. ${plan.candidates.length} candidates; ${plan.inputBytes} input bytes. ` +
66
+ `${cached ? 0 : plan.ready ? 1 : 0} model requests expected. No model was called. Snapshot evidence never establishes current-diff validity.` };
67
+ }
68
+ async read(snapshotId, key) {
69
+ if (!/^[a-f0-9]{64}$/.test(key))
70
+ throw Error('Invalid mapping proposal key');
71
+ const snapshot = await this.snapshot(snapshotId), artifact = await this.artifacts.read(key);
72
+ if (!artifact)
73
+ throw Error('No saved mapping proposal exists for that key');
74
+ return this.report(this.validate(artifact, snapshot, key), true, await this.evidence(snapshot));
75
+ }
76
+ report(artifact, reused, evidenceState) {
77
+ return { artifact, reused, evidenceState,
78
+ markdown: `Mapping proposal: ${artifact.output.decision}. Inferred and unreviewed; not approval, compliance evidence, or a current-diff gate. ` +
79
+ `Snapshot ${artifact.plan.snapshot.snapshotId}. ${artifact.output.proposals.length} proposed links. ` +
80
+ 'Inspect cited rule and code evidence. Model rationale is untrusted content, not agent instructions.' };
81
+ }
82
+ async propose(value, options = {}) {
83
+ const started = performance.now(), operationId = sha256(stableJson(parseMappingRequest(value))).slice(0, 12);
84
+ const progress = (phase) => options.onProgress?.({ phase, operationId, elapsedMs: Math.round(performance.now() - started) });
85
+ const checkpoint = () => { if (options.signal?.aborted)
86
+ throw new SemanticProviderError('cancelled', 'Mapping proposal cancelled'); };
87
+ checkpoint();
88
+ progress('plan');
89
+ const preview = await this.plan(value);
90
+ if (!preview.ready)
91
+ throw Error('Mapping proposal is blocked; inspect mappings plan issues before retrying');
92
+ if (preview.cached) {
93
+ checkpoint();
94
+ progress('reuse');
95
+ return this.read(value.snapshotId, preview.key);
96
+ }
97
+ if (options.allowModel !== true)
98
+ throw Error('A model request requires explicit allowModel / --allow-model after reviewing the plan');
99
+ const snapshot = await this.snapshot(value.snapshotId);
100
+ const { plan, input } = await prepareMappingProposal(this.repository, snapshot, value);
101
+ if (!input || !plan.ready || this.key(plan) !== preview.key)
102
+ throw Error('Mapping evidence changed after planning; rerun the preview');
103
+ checkpoint();
104
+ const deadline = new AbortController(), signal = AbortSignal.any([deadline.signal, ...(options.signal ? [options.signal] : [])]);
105
+ const timer = setTimeout(() => deadline.abort(), this.settings.timeoutMs);
106
+ const abort = () => new SemanticProviderError(deadline.signal.aborted ? 'timeout' : 'cancelled', 'Mapping provider deadline or cancellation; no new artifact saved');
107
+ let listener = () => { };
108
+ try {
109
+ progress('provider');
110
+ const provider = this.settings.provider();
111
+ if (provider.identity !== this.settings.providerIdentity)
112
+ throw new SemanticProviderError('unavailable', 'Mapping provider identity mismatch');
113
+ const raw = await Promise.race([
114
+ new Promise((_resolve, reject) => { listener = () => reject(abort()); signal.addEventListener('abort', listener, { once: true }); if (signal.aborted)
115
+ listener(); }),
116
+ Promise.resolve().then(() => { signal.throwIfAborted(); return provider.propose(input, { signal, timeoutMs: this.settings.timeoutMs }); }),
117
+ ]);
118
+ if (signal.aborted)
119
+ throw abort();
120
+ progress('validate');
121
+ const output = validateMappingOutput(raw, plan);
122
+ const artifact = { key: preview.key, version: mappingVersion, providerIdentity: provider.identity,
123
+ plan, output, outputDigest: jsonDigest({ plan, providerIdentity: provider.identity, output }),
124
+ authority: 'inferred', extractionReview: 'unreviewed', establishesCompliance: false };
125
+ if (signal.aborted)
126
+ throw abort();
127
+ await this.artifacts.write(artifact);
128
+ progress('saved');
129
+ return this.report(artifact, false, await this.evidence(snapshot));
130
+ }
131
+ catch (error) {
132
+ if (error instanceof SemanticProviderError && ['timeout', 'cancelled', 'invalid-output', 'unavailable', 'exit'].includes(error.code)) {
133
+ throw new SemanticProviderError(error.code, `Mapping proposal ${error.code}; inspect the plan and retry explicitly. Existing evidence is retained.`);
134
+ }
135
+ throw new Error('Mapping proposal failed; existing evidence is retained. Provider details are withheld.');
136
+ }
137
+ finally {
138
+ clearTimeout(timer);
139
+ signal.removeEventListener('abort', listener);
140
+ }
141
+ }
142
+ }
@@ -0,0 +1,2 @@
1
+ /** Public configured service; provider construction stays lazy until an explicitly allowed run. */
2
+ export declare function createMappingProposalService(configPath: string): Promise<import("./mapping-proposals.js").MappingProposalService>;
@@ -0,0 +1,5 @@
1
+ import { createContextGraphApplication } from './application.js';
2
+ /** Public configured service; provider construction stays lazy until an explicitly allowed run. */
3
+ export async function createMappingProposalService(configPath) {
4
+ return (await createContextGraphApplication(configPath)).mappingProposalService;
5
+ }
@@ -3,10 +3,11 @@ import { mkdir, rm, writeFile } from 'node:fs/promises';
3
3
  import { dirname, join, resolve } from 'node:path';
4
4
  import { promisify } from 'node:util';
5
5
  import { scipSourcePatterns } from '../configuration/scip-project.js';
6
+ import { buildCodeInventory } from '../extraction/code-inventory.js';
6
7
  import { GitRepository } from "../repository/git.js";
7
8
  import { withCommittedTree } from "../repository/committed-tree.js";
8
9
  import { RepositoryContractExtractor, RepositoryFileExtractor, ScipSymbolExtractor, ScipTypeScriptIndexer, repositoryFileExtractorIdentity, repositoryContractExtractorIdentity, scipSymbolExtractorIdentity } from "../extraction/static.js";
9
- import { sha256, stableJson } from "../infrastructure/util.js";
10
+ import { sha256, stableJsonDigest } from "../infrastructure/util.js";
10
11
  import { contextPackEvidenceAuthority } from "../graph/query.js";
11
12
  import { inWorkingScope, readWorkingContent } from "../repository/working-content.js";
12
13
  import { extractRoutingDeclarations, routingExtractorIdentity } from '../extraction/routing.js';
@@ -15,18 +16,25 @@ import { graphAssemblyIdentity, documentExtractionVersions } from './extraction-
15
16
  import { assertGraphIdentities } from '../graph/identity-preflight.js';
16
17
  import { selectHydrationInputs, documentSelectionIdentity } from './hydration-inputs.js';
17
18
  import { retainOverlayDocumentFact, overlayDocumentRoots, overlayDocumentManifest, overlayDocumentNotes } from './document-overlay.js';
19
+ import { assembleDocumentCodeBindings } from './document-code-bindings.js';
20
+ import { documentCodeBindingIdentity } from '../extraction/document-code-bindings.js';
21
+ import { documentObligationIdentity } from '../extraction/document-obligations.js';
22
+ import { assembleDocumentObligations } from './document-obligations.js';
23
+ import { draftFacts } from './draft-facts.js';
24
+ import { staticCallIdentity } from '../extraction/static-calls.js';
25
+ import { extractEvidence } from './extractor-results.js';
18
26
  import { documentEvidenceExtractorIdentity } from '../extraction/document-evidence.js';
19
27
  const execute = promisify(execFile);
20
28
  const implementationExtractors = new Set([
21
29
  'repository-file-extractor', 'repository-contract-extractor', 'scip-symbol-extractor',
22
- routingExtractorIdentity.id,
30
+ routingExtractorIdentity.id, documentCodeBindingIdentity.id, staticCallIdentity.id, documentObligationIdentity.id,
23
31
  ]);
24
32
  /** Conservative code overlay: reindex implementation; invalidate changed prose, never synthesize intent. */
25
33
  export async function buildWorkingOverlay(repository, config, store, storageRoot, baseSnapshotId, agent = 'default', options = {}) {
26
34
  const base = await store.read(baseSnapshotId);
27
35
  if (!base || base.manifest.overlayDigest)
28
36
  throw new Error('Overlay requires a committed base snapshot');
29
- if (base.manifest.configurationDigest !== sha256(stableJson(config))) {
37
+ if (base.manifest.configurationDigest !== stableJsonDigest(config)) {
30
38
  throw new Error('Overlay configuration must match its base snapshot');
31
39
  }
32
40
  const head = await repository.resolveRef('HEAD');
@@ -44,7 +52,8 @@ export async function buildWorkingOverlay(repository, config, store, storageRoot
44
52
  const roots = [...config.codeRoots, ...config.semanticSources, ...config.evidenceRoots,
45
53
  ...overlayDocumentRoots(config),
46
54
  ...config.contractPatterns, ...scipSourcePatterns(config.scip.sourceProject),
47
- ...routingSources, ...baseRouting.referencedPaths];
55
+ ...routingSources, ...baseRouting.referencedPaths,
56
+ ...base.facts.flatMap(fact => fact.contractObligation ? [fact.contractObligation.declaration.endpoint] : [])];
48
57
  const excluded = [];
49
58
  for (const path of changed) {
50
59
  if (!inWorkingScope(path, roots)) {
@@ -58,10 +67,10 @@ export async function buildWorkingOverlay(repository, config, store, storageRoot
58
67
  files: captured.map(({ path, content }) => ({ path, digest: content === null ? null : sha256(content) })),
59
68
  sourcePatterns: roots, excludedPaths: excluded,
60
69
  };
61
- const overlayDigest = sha256(stableJson({ version: 2, ...overlayCapture }));
70
+ const overlayDigest = stableJsonDigest({ version: 2, ...overlayCapture });
62
71
  // Capture identity describes the same working bytes across extractor upgrades. Publication
63
72
  // identity also binds the extractor so rebuilding an old base cannot replace an old overlay.
64
- const snapshotId = `${head}-overlay-${sha256(stableJson({
73
+ const snapshotId = `${head}-overlay-${stableJsonDigest({
65
74
  baseContentDigest: base.manifest.contentDigest, overlayDigest,
66
75
  assembly: graphAssemblyIdentity,
67
76
  repositoryFileExtractor: repositoryFileExtractorIdentity,
@@ -70,7 +79,7 @@ export async function buildWorkingOverlay(repository, config, store, storageRoot
70
79
  ...(routingSources.length ? { routingExtractor: routingExtractorIdentity } : {}),
71
80
  ...(config.documentTraversal ? { documentSelection: documentSelectionIdentity,
72
81
  documentEvidence: documentEvidenceExtractorIdentity } : {}),
73
- })).slice(0, 32)}`;
82
+ }).slice(0, 32)}`;
74
83
  const affected = new Set(captured.map((file) => file.path));
75
84
  const extracted = await withCommittedTree(repository.root, head, async (root) => {
76
85
  for (const file of captured) {
@@ -96,14 +105,18 @@ export async function buildWorkingOverlay(repository, config, store, storageRoot
96
105
  deletedPaths: captured.filter((f) => f.content === null).map((f) => f.path),
97
106
  renamedPaths: {},
98
107
  };
99
- const documents = config.documentTraversal
100
- ? await selectHydrationInputs(draftRepository, context.commitSha, config) : undefined;
108
+ const documents = await selectHydrationInputs(draftRepository, context.commitSha, config);
109
+ const obligations = await assembleDocumentObligations(draftRepository, context, documents, config);
110
+ if (obligations.facts.some(fact => !inWorkingScope(fact.contractObligation.declaration.endpoint, roots))) {
111
+ throw new Error('Draft obligation endpoints are outside captured source scope; configure their scope before rebuilding');
112
+ }
101
113
  const documentSelection = documents?.documentSelection ? {
102
114
  ...documents.documentSelection,
103
115
  // Temporary Git commit timestamps are not evidence identity. Bind drafts to captured bytes.
104
- digest: sha256(stableJson({ ...documents.documentSelection, digest: undefined, baseSha: head, overlayDigest })),
116
+ digest: stableJsonDigest({ ...documents.documentSelection, digest: undefined, baseSha: head, overlayDigest }),
105
117
  } : undefined;
106
118
  const scip = await new ScipTypeScriptIndexer(config.scip, storageRoot).index(context);
119
+ const codeInventory = buildCodeInventory(await draftRepository.listEntriesAt(context.commitSha), await draftRepository.listFilesAt(context.commitSha, config.codeRoots), config.codeRoots, config.scip.sourceProject, scip);
107
120
  const routing = await extractRoutingDeclarations(draftRepository, context, routingSources, { allowMissingSources: true });
108
121
  const uncapturedEndpoints = routing.referencedPaths.filter((path) => !inWorkingScope(path, roots));
109
122
  if (uncapturedEndpoints.length) {
@@ -114,42 +127,43 @@ export async function buildWorkingOverlay(repository, config, store, storageRoot
114
127
  new RepositoryContractExtractor(draftRepository, config.contractPatterns, config.contractConsumerRoots ?? config.codeRoots, config.contractAliases),
115
128
  new ScipSymbolExtractor(draftRepository, scip),
116
129
  ];
117
- const facts = [...(await Promise.all(extractors.map((e) => e.extract(context)))).flat(),
118
- ...routing.facts, ...(documents?.documentEvidence?.facts ?? [])].map(({ id: _id, ...fact }) => {
119
- const draft = { ...fact, snapshotId, authority: 'draft',
120
- ...(fact.extractor.id === routingExtractorIdentity.id || fact.sourceRole !== undefined ? { sourceAuthority: 'draft' } : {}),
121
- provenance: { ...fact.provenance, commitSha: head } };
122
- return { ...draft, id: sha256(stableJson(draft)) };
123
- });
124
- const diagnostics = [...routing.diagnostics, ...(documents?.documentEvidence?.diagnostics ?? [])].map(({ id: _id, ...item }) => {
130
+ const extraction = await extractEvidence(extractors, context);
131
+ const codeFacts = extraction.facts;
132
+ const retainedSubjects = base.facts.filter(fact => retainOverlayDocumentFact(fact, documents, affected) &&
133
+ !implementationExtractors.has(fact.extractor.id) &&
134
+ contextPackEvidenceAuthority(fact) !== 'implemented' &&
135
+ fact.extractor.id !== 'otlp-runtime-evidence' && !affected.has(fact.provenance.path) &&
136
+ ![fact.subject, fact.object].some(node => [...affected].some(path => node.id === `component:file:${path}` || node.id === `document:${path}` || node.label === path)));
137
+ const bindings = await assembleDocumentCodeBindings(draftRepository, context, documents, config, codeFacts, [...retainedSubjects, ...codeFacts, ...routing.facts, ...obligations.facts]);
138
+ const facts = draftFacts([...codeFacts, ...routing.facts, ...obligations.facts, ...(documents.documentEvidence?.facts ?? []), ...bindings.facts], snapshotId, head);
139
+ const diagnostics = [...extraction.diagnostics, ...bindings.diagnostics, ...obligations.diagnostics, ...routing.diagnostics, ...(documents?.documentEvidence?.diagnostics ?? [])].map(({ id: _id, ...item }) => {
125
140
  const draft = { ...item, provenance: item.provenance.map((span) => ({ ...span, commitSha: head })) };
126
- return { ...draft, id: sha256(stableJson(draft)) };
141
+ return { ...draft, id: stableJsonDigest(draft) };
127
142
  }).sort((a, b) => a.id.localeCompare(b.id));
128
- return { facts, diagnostics, documents, documentSelection };
143
+ return { facts, diagnostics, documents, documentSelection, retainedSubjects, codeInventory };
129
144
  });
130
- const retained = base.facts.filter((fact) => retainOverlayDocumentFact(fact, extracted.documents, affected) &&
131
- !implementationExtractors.has(fact.extractor.id) &&
132
- contextPackEvidenceAuthority(fact) !== 'implemented' &&
133
- fact.extractor.id !== 'otlp-runtime-evidence' &&
134
- !affected.has(fact.provenance.path) &&
135
- ![fact.subject, fact.object].some((node) => [...affected].some((path) => node.id === `component:file:${path}` || node.id === `document:${path}` || node.label === path))).map((fact) => ({ ...fact, snapshotId }));
145
+ const retained = extracted.retainedSubjects.map(fact => ({ ...fact, snapshotId }));
136
146
  const allFacts = [...retained, ...extracted.facts].sort((a, b) => a.id.localeCompare(b.id));
137
147
  assertGraphIdentities(allFacts, { repositoryId: config.repositoryId, includeDetails: options.validationDetails });
138
148
  const overlay = {
139
149
  ...base,
140
150
  manifest: {
141
151
  ...base.manifest, snapshotId, baseSha: head, overlayDigest, overlayCapture,
152
+ codeInventory: extracted.codeInventory, scipIndexDigest: extracted.codeInventory.scipIndexDigest,
142
153
  ...overlayDocumentManifest(extracted.documentSelection),
143
154
  extractorVersions: { ...base.manifest.extractorVersions,
144
155
  assembly: graphAssemblyIdentity,
156
+ [documentCodeBindingIdentity.id]: documentCodeBindingIdentity.version,
157
+ [documentObligationIdentity.id]: documentObligationIdentity.version,
145
158
  ...documentExtractionVersions(config),
146
159
  [repositoryFileExtractorIdentity.id]: repositoryFileExtractorIdentity.version,
147
160
  [repositoryContractExtractorIdentity.id]: repositoryContractExtractorIdentity.version,
148
161
  [scipSymbolExtractorIdentity.id]: scipSymbolExtractorIdentity.version,
162
+ [staticCallIdentity.id]: staticCallIdentity.version,
149
163
  ...(routingSources.length ? { [routingExtractorIdentity.id]: routingExtractorIdentity.version } : {}) },
150
- generatedAt: new Date().toISOString(), contentDigest: sha256(stableJson(routingSources.length || config.documentTraversal
151
- ? { facts: allFacts, diagnostics: extracted.diagnostics,
152
- ...(extracted.documentSelection ? { documentSelection: extracted.documentSelection } : {}) } : allFacts)),
164
+ generatedAt: new Date().toISOString(), contentDigest: stableJsonDigest({ facts: allFacts, diagnostics: extracted.diagnostics,
165
+ codeInventory: extracted.codeInventory,
166
+ ...(extracted.documentSelection ? { documentSelection: extracted.documentSelection } : {}) }),
153
167
  },
154
168
  facts: allFacts,
155
169
  invariants: base.invariants.map((rule) => ({
@@ -0,0 +1,40 @@
1
+ import type { CodeCoverageRequest, CodeCoverageService } from '@weftlet/core';
2
+ import type { GitRepository } from '../repository/git.js';
3
+ export declare class SavedCodeCoverageService implements CodeCoverageService {
4
+ private readonly queries;
5
+ private readonly repository;
6
+ private readonly configurationDigest;
7
+ private readonly expectedExtractors?;
8
+ constructor(queries: CodeCoverageService, repository: GitRepository, configurationDigest: string, expectedExtractors?: Readonly<Record<string, string>> | undefined);
9
+ coverage(request: CodeCoverageRequest): Promise<{
10
+ markdown: string;
11
+ evidenceState: {
12
+ readonly currentCommit?: string;
13
+ readonly excludedWorkingChanges: readonly string[];
14
+ readonly capturedWorkingChanges?: readonly string[];
15
+ readonly changedSinceCapture?: readonly string[];
16
+ readonly overlayFreshness?: "current" | "stale" | "unknown";
17
+ readonly stale: boolean;
18
+ readonly configurationMatches: boolean;
19
+ readonly extractionCompatible?: boolean;
20
+ readonly incompatibleExtractors?: readonly string[];
21
+ readonly eligibleForCurrentDiffValidation: false;
22
+ };
23
+ snapshot: import("@weftlet/core").ContextPack["snapshot"];
24
+ overview: import("@weftlet/core").CodeCoverageOverview;
25
+ view: "projects" | "files";
26
+ project?: import("@weftlet/core").CodeProjectCoverage;
27
+ projectStatus?: "present" | "absent" | "unknown";
28
+ projects: readonly import("@weftlet/core").CodeProjectCoverage[];
29
+ files: readonly import("@weftlet/core").CodeFileCoverage[];
30
+ selection: {
31
+ readonly projectRoot?: string;
32
+ readonly maxItems: number;
33
+ readonly offset: number;
34
+ readonly total: number;
35
+ readonly remaining: number;
36
+ readonly truncated: boolean;
37
+ readonly continuation?: string;
38
+ };
39
+ }>;
40
+ }