@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.
- package/README.md +191 -18
- package/THIRD_PARTY_NOTICES.md +3 -1
- package/dist/application/application.d.ts +10 -0
- package/dist/application/application.js +20 -0
- package/dist/application/document-code-bindings.d.ts +11 -0
- package/dist/application/document-code-bindings.js +25 -0
- package/dist/application/document-obligations.d.ts +8 -0
- package/dist/application/document-obligations.js +11 -0
- package/dist/application/draft-facts.d.ts +3 -0
- package/dist/application/draft-facts.js +37 -0
- package/dist/application/extraction-identity.d.ts +2 -2
- package/dist/application/extraction-identity.js +7 -1
- package/dist/application/extractor-results.d.ts +6 -0
- package/dist/application/extractor-results.js +6 -0
- package/dist/application/hydrator.js +38 -18
- package/dist/application/mapping-proposals.d.ts +108 -0
- package/dist/application/mapping-proposals.js +142 -0
- package/dist/application/mapping-service.d.ts +2 -0
- package/dist/application/mapping-service.js +5 -0
- package/dist/application/overlay.js +43 -29
- package/dist/application/saved-code-coverage.d.ts +40 -0
- package/dist/application/saved-code-coverage.js +20 -0
- package/dist/application/saved-compatibility.d.ts +38 -0
- package/dist/application/saved-compatibility.js +20 -0
- package/dist/application/saved-evidence.d.ts +17 -11
- package/dist/application/saved-evidence.js +44 -40
- package/dist/application/saved-exploration.d.ts +57 -0
- package/dist/application/saved-exploration.js +21 -0
- package/dist/application/saved-paths.d.ts +58 -0
- package/dist/application/saved-paths.js +20 -0
- package/dist/application/semantic-run.d.ts +1 -1
- package/dist/extraction/call-syntax.d.ts +18 -0
- package/dist/extraction/call-syntax.js +61 -0
- package/dist/extraction/canonical-document-links.d.ts +5 -1
- package/dist/extraction/canonical-document-links.js +4 -4
- package/dist/extraction/code-inventory.d.ts +6 -0
- package/dist/extraction/code-inventory.js +26 -0
- package/dist/extraction/document-code-bindings.d.ts +14 -0
- package/dist/extraction/document-code-bindings.js +121 -0
- package/dist/extraction/document-obligations.d.ts +12 -0
- package/dist/extraction/document-obligations.js +112 -0
- package/dist/extraction/mapping-candidates.d.ts +13 -0
- package/dist/extraction/mapping-candidates.js +46 -0
- package/dist/extraction/mapping-output.d.ts +75 -0
- package/dist/extraction/mapping-output.js +53 -0
- package/dist/extraction/mapping-plan.d.ts +7 -0
- package/dist/extraction/mapping-plan.js +117 -0
- package/dist/extraction/mapping-prompt.d.ts +2 -0
- package/dist/extraction/mapping-prompt.js +17 -0
- package/dist/extraction/mapping-provider.d.ts +15 -0
- package/dist/extraction/mapping-provider.js +60 -0
- package/dist/extraction/mapping-request.d.ts +3 -0
- package/dist/extraction/mapping-request.js +32 -0
- package/dist/extraction/ontology.d.ts +2 -0
- package/dist/extraction/ontology.js +28 -13
- package/dist/extraction/routing.js +5 -20
- package/dist/extraction/scip-occurrences.d.ts +3 -0
- package/dist/extraction/scip-occurrences.js +102 -0
- package/dist/extraction/scip-ranges.d.ts +6 -0
- package/dist/extraction/scip-ranges.js +31 -0
- package/dist/extraction/scip-runtime.d.ts +39 -0
- package/dist/extraction/scip-runtime.js +8 -0
- package/dist/extraction/scip-symbols.d.ts +21 -0
- package/dist/extraction/scip-symbols.js +91 -0
- package/dist/extraction/semantic.d.ts +1 -1
- package/dist/extraction/semantic.js +5 -17
- package/dist/extraction/static-calls.d.ts +10 -0
- package/dist/extraction/static-calls.js +133 -0
- package/dist/extraction/static.d.ts +3 -20
- package/dist/extraction/static.js +8 -84
- package/dist/extraction/symbol-resolution.d.ts +12 -0
- package/dist/extraction/symbol-resolution.js +51 -0
- package/dist/extraction/symbol-selectors.d.ts +11 -0
- package/dist/extraction/symbol-selectors.js +51 -0
- package/dist/graph/binding-rdf.d.ts +7 -0
- package/dist/graph/binding-rdf.js +109 -0
- package/dist/graph/call-rdf.d.ts +4 -0
- package/dist/graph/call-rdf.js +94 -0
- package/dist/graph/call-shapes.d.ts +2 -0
- package/dist/graph/call-shapes.js +28 -0
- package/dist/graph/code-coverage-projection.d.ts +9 -0
- package/dist/graph/code-coverage-projection.js +117 -0
- package/dist/graph/code-coverage-renderer.d.ts +2 -0
- package/dist/graph/code-coverage-renderer.js +15 -0
- package/dist/graph/code-coverage-request.d.ts +6 -0
- package/dist/graph/code-coverage-request.js +19 -0
- package/dist/graph/code-coverage.d.ts +6 -0
- package/dist/graph/code-coverage.js +54 -0
- package/dist/graph/code-description.d.ts +4 -0
- package/dist/graph/code-description.js +24 -0
- package/dist/graph/code-rdf.d.ts +6 -0
- package/dist/graph/code-rdf.js +79 -0
- package/dist/graph/compatibility-context.d.ts +2 -0
- package/dist/graph/compatibility-context.js +18 -0
- package/dist/graph/compatibility-renderer.d.ts +2 -0
- package/dist/graph/compatibility-renderer.js +45 -0
- package/dist/graph/compatibility-request.d.ts +3 -0
- package/dist/graph/compatibility-request.js +18 -0
- package/dist/graph/compatibility.d.ts +7 -0
- package/dist/graph/compatibility.js +67 -0
- package/dist/graph/constraint-comparison.d.ts +7 -0
- package/dist/graph/constraint-comparison.js +49 -0
- package/dist/graph/coverage.d.ts +2 -2
- package/dist/graph/coverage.js +8 -3
- package/dist/graph/diagnostics.d.ts +1 -1
- package/dist/graph/diagnostics.js +10 -6
- package/dist/graph/evidence-description.d.ts +3 -0
- package/dist/graph/evidence-description.js +13 -0
- package/dist/graph/identity-preflight.js +1 -1
- package/dist/graph/neighborhood-renderer.d.ts +2 -0
- package/dist/graph/neighborhood-renderer.js +38 -0
- package/dist/graph/neighborhood-request.d.ts +9 -0
- package/dist/graph/neighborhood-request.js +37 -0
- package/dist/graph/neighborhood.d.ts +8 -0
- package/dist/graph/neighborhood.js +100 -0
- package/dist/graph/obligation-rdf.d.ts +5 -0
- package/dist/graph/obligation-rdf.js +67 -0
- package/dist/graph/obligation-shapes.d.ts +2 -0
- package/dist/graph/obligation-shapes.js +32 -0
- package/dist/graph/partitioned-shacl.d.ts +14 -0
- package/dist/graph/partitioned-shacl.js +66 -0
- package/dist/graph/path-request.d.ts +11 -0
- package/dist/graph/path-request.js +24 -0
- package/dist/graph/path-search.d.ts +45 -0
- package/dist/graph/path-search.js +101 -0
- package/dist/graph/paths-renderer.d.ts +2 -0
- package/dist/graph/paths-renderer.js +36 -0
- package/dist/graph/paths.d.ts +7 -0
- package/dist/graph/paths.js +74 -0
- package/dist/graph/query.js +23 -6
- package/dist/graph/rdf-partitions.d.ts +24 -0
- package/dist/graph/rdf-partitions.js +170 -0
- package/dist/graph/rdf-projection.d.ts +7 -0
- package/dist/graph/rdf-projection.js +27 -0
- package/dist/graph/rdf-records.d.ts +12 -0
- package/dist/graph/rdf-records.js +90 -0
- package/dist/graph/rdf.d.ts +10 -10
- package/dist/graph/rdf.js +84 -89
- package/dist/graph/shacl-shapes.js +70 -0
- package/dist/graph/snapshot-context.d.ts +3 -0
- package/dist/graph/snapshot-context.js +8 -0
- package/dist/graph/validation-progress.d.ts +4 -1
- package/dist/index.d.ts +23 -0
- package/dist/index.js +22 -0
- package/dist/infrastructure/json-digest.d.ts +3 -0
- package/dist/infrastructure/json-digest.js +3 -0
- package/dist/infrastructure/unique-json.d.ts +2 -0
- package/dist/infrastructure/unique-json.js +20 -0
- package/dist/interfaces/cli.js +33 -1
- package/dist/interfaces/code-coverage.d.ts +46 -0
- package/dist/interfaces/code-coverage.js +18 -0
- package/dist/interfaces/graph-compatibility.d.ts +35 -0
- package/dist/interfaces/graph-compatibility.js +21 -0
- package/dist/interfaces/graph-exploration.d.ts +59 -0
- package/dist/interfaces/graph-exploration.js +27 -0
- package/dist/interfaces/graph-paths.d.ts +77 -0
- package/dist/interfaces/graph-paths.js +31 -0
- package/dist/interfaces/graph-query-args.d.ts +2 -0
- package/dist/interfaces/graph-query-args.js +25 -0
- package/dist/interfaces/mapping-proposals.d.ts +204 -0
- package/dist/interfaces/mapping-proposals.js +74 -0
- package/dist/interfaces/mcp.js +31 -3
- package/dist/storage/mapping-proposals.d.ts +9 -0
- package/dist/storage/mapping-proposals.js +42 -0
- package/dist/storage/storage.d.ts +3 -0
- package/dist/storage/storage.js +37 -13
- package/dist/storage/streaming-json.d.ts +5 -0
- package/dist/storage/streaming-json.js +45 -0
- package/dist/visualization/evidence-sources.d.ts +6 -0
- package/dist/visualization/evidence-sources.js +19 -0
- package/dist/visualization/viewer-interaction.d.ts +5 -8
- package/dist/visualization/viewer-interaction.js +46 -44
- package/dist/visualization/visualization.js +14 -5
- package/docs/1.0-WORK-QUEUE.md +48 -0
- package/docs/ADR-029-exact-graph-neighborhoods.md +70 -0
- package/docs/ADR-030-bounded-evidence-paths.md +97 -0
- package/docs/ADR-031-code-extractor-selection.md +91 -0
- package/docs/ADR-032-scip-code-evidence.md +73 -0
- package/docs/ADR-033-document-symbol-bindings.md +83 -0
- package/docs/ADR-034-mapping-gaps.md +80 -0
- package/docs/ADR-035-static-call-evidence.md +78 -0
- package/docs/ADR-036-contract-obligation-declarations.md +124 -0
- package/docs/ADR-037-selected-contract-compatibility.md +93 -0
- package/docs/ADR-038-monorepo-code-coverage.md +105 -0
- package/docs/ADR-039-mapping-proposals.md +119 -0
- package/docs/ADR-040-bounded-graph-io.md +90 -0
- package/docs/GRAPHIFY-EVALUATION.md +21 -4
- package/docs/PRE-1.0-BASELINE.md +49 -2
- package/docs/RELEASE-1.0-PLAN.md +14 -2
- package/docs/RELEASING.md +3 -3
- package/docs/ROADMAP.md +27 -6
- package/docs/VALIDATION.md +521 -14
- package/docs/benchmarks/large-graph-2026-11-02/result.json +156 -0
- package/docs/extraction-manifest.json +266 -23
- package/package.json +10 -4
- 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:
|
|
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.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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 =
|
|
95
|
-
const scopeDigest =
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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.
|
|
277
|
-
throw new Error(`Clean hydration drift: ${current.
|
|
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,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,
|
|
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 !==
|
|
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 =
|
|
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-${
|
|
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
|
-
})
|
|
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
|
|
100
|
-
|
|
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:
|
|
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
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
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:
|
|
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 =
|
|
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:
|
|
151
|
-
|
|
152
|
-
|
|
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
|
+
}
|