@shapeshift-labs/frontier-lang-compiler 0.2.100 → 0.2.101
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/dist/declarations/bidirectional-target-change-evidence.d.ts +299 -0
- package/dist/declarations/bidirectional-target-change.d.ts +19 -120
- package/dist/declarations/native-project-admission.d.ts +43 -22
- package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +24 -0
- package/dist/declarations/semantic-edit-script.d.ts +20 -15
- package/dist/declarations/semantic-lineage.d.ts +3 -21
- package/dist/declarations/semantic-merge-candidates.d.ts +39 -0
- package/dist/declarations/semantic-sidecar-admission.d.ts +14 -0
- package/dist/declarations/semantic-sidecar.d.ts +12 -14
- package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +200 -0
- package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +62 -17
- package/dist/internal/index-impl/createNativeSourcePreservation.js +16 -1
- package/dist/internal/index-impl/createProjectImportAdmissionRecord.js +151 -1
- package/dist/internal/index-impl/createSemanticImportSidecar.js +5 -0
- package/dist/internal/index-impl/createSemanticImportSidecarAdmission.js +29 -11
- package/dist/internal/index-impl/nativeChangeProjectionEndpoint.js +56 -16
- package/dist/internal/index-impl/projectImportAdmissionMergeScore.js +26 -74
- package/dist/internal/index-impl/projectImportAdmissionProjectionCoverage.js +74 -0
- package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +39 -13
- package/dist/internal/index-impl/replaySemanticEditProjection.js +65 -23
- package/dist/internal/index-impl/semanticEditInsertionAnchors.js +8 -5
- package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +167 -0
- package/dist/internal/index-impl/semanticEditSourceRanges.js +94 -15
- package/dist/internal/index-impl/semanticHistoryLineageResolution.js +21 -2
- package/dist/internal/index-impl/semanticLineageHashEvidence.js +97 -0
- package/dist/internal/index-impl/semanticLineageInferenceMatching.js +8 -0
- package/dist/internal/index-impl/semanticLineageResolutionRecords.js +18 -1
- package/dist/internal/index-impl/semanticMergeCandidateRecords.js +22 -2
- package/dist/internal/index-impl/semanticMergeCandidateScoreFacets.js +221 -0
- package/dist/internal/index-impl/semanticPatchBundleOverlaps.js +23 -1
- package/dist/internal/index-impl/sourcePreservationFromProjectionContext.js +9 -2
- package/dist/native-import-language-profiles.js +10 -2
- package/dist/native-region-scanner-js-helpers.js +8 -2
- package/dist/native-region-scanner-js-imports.js +7 -0
- package/dist/native-region-scanner-js.js +4 -4
- package/dist/native-region-scanner.js +2 -1
- package/dist/semantic-import-regions.js +6 -4
- package/dist/semantic-import-sidecar-admission-types.d.ts +14 -0
- package/dist/semantic-import-sidecar-entry.js +151 -7
- package/dist/semantic-import-sidecar-types.d.ts +18 -13
- package/dist/semantic-import-source-preservation-utils.js +55 -0
- package/dist/semantic-import-source-preservation.js +98 -3
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { idFragment, uniqueStrings } from '../../native-import-utils.js';
|
|
2
|
+
import { addIdentityHashEvidence, addSourceHashEvidence, hashEvidenceSummary } from './semanticLineageHashEvidence.js';
|
|
2
3
|
import { createSemanticLineageEvent } from './semanticLineageRecords.js';
|
|
3
4
|
|
|
4
5
|
export function matchExactAnchors(beforeSymbols, afterSymbols) {
|
|
@@ -88,6 +89,9 @@ export function symbolSummary(symbol) {
|
|
|
88
89
|
language: symbol.language,
|
|
89
90
|
sourcePath: symbol.anchor.sourcePath,
|
|
90
91
|
sourceHash: symbol.anchor.sourceHash,
|
|
92
|
+
identityHash: firstString(symbol.identityHash, symbol.anchor.metadata?.identityHash),
|
|
93
|
+
semanticIdentityHash: firstString(symbol.semanticIdentityHash, symbol.anchor.metadata?.semanticIdentityHash),
|
|
94
|
+
sourceIdentityHash: firstString(symbol.sourceIdentityHash, symbol.anchor.metadata?.sourceIdentityHash),
|
|
91
95
|
sourceSpan: symbol.anchor.sourceSpan,
|
|
92
96
|
signatureHash: symbol.signatureHash,
|
|
93
97
|
bodyHash: symbol.spanHash,
|
|
@@ -144,6 +148,7 @@ function inferredEvent(before, after, score, input) {
|
|
|
144
148
|
inferred: true,
|
|
145
149
|
algorithm: 'frontier.semantic-lineage-inference.v1',
|
|
146
150
|
reasonCodes: score.reasons,
|
|
151
|
+
hashEvidence: hashEvidenceSummary(score.reasons),
|
|
147
152
|
moved: pathChanged || spanMoved,
|
|
148
153
|
renamed: nameChanged,
|
|
149
154
|
recreated,
|
|
@@ -190,6 +195,7 @@ function inferredSplitEvent(before, candidates, input) {
|
|
|
190
195
|
inferred: true,
|
|
191
196
|
algorithm: 'frontier.semantic-lineage-inference.v1',
|
|
192
197
|
reasonCodes: reasons,
|
|
198
|
+
hashEvidence: hashEvidenceSummary(reasons),
|
|
193
199
|
split: true,
|
|
194
200
|
targetCount: targets.length,
|
|
195
201
|
candidateConfidences: candidates.map((candidate) => candidate.score.confidence),
|
|
@@ -210,10 +216,12 @@ function scoreLineagePair(before, after) {
|
|
|
210
216
|
if (before.anchor.key && before.anchor.key === after.anchor.key) add(0.4, 'anchor-key-match');
|
|
211
217
|
if (before.name && before.name === after.name) add(0.28, 'symbol-name-match');
|
|
212
218
|
if (before.kind && before.kind === after.kind) add(0.12, 'symbol-kind-match');
|
|
219
|
+
addIdentityHashEvidence(before, after, add, note);
|
|
213
220
|
if (before.signatureHash && before.signatureHash === after.signatureHash) add(0.52, 'signature-hash-match');
|
|
214
221
|
if (before.spanHash && before.spanHash === after.spanHash) add(0.22, 'body-hash-match');
|
|
215
222
|
if (before.anchor.kind && before.anchor.kind === after.anchor.kind) add(0.06, 'anchor-kind-match');
|
|
216
223
|
if (before.anchor.sourcePath && before.anchor.sourcePath === after.anchor.sourcePath) add(0.04, 'source-path-match');
|
|
224
|
+
addSourceHashEvidence(before, after, add, note, reasons, sameSymbolSurface);
|
|
217
225
|
if (sourceSpanRangeSame(before.anchor.sourceSpan, after.anchor.sourceSpan)) add(0.18, 'source-span-range-match');
|
|
218
226
|
if (before.ownershipRegionKind && before.ownershipRegionKind === after.ownershipRegionKind) add(0.04, 'ownership-kind-match');
|
|
219
227
|
if (before.nativeAstNodeId && before.nativeAstNodeId === after.nativeAstNodeId) add(0.06, 'native-node-id-match');
|
|
@@ -36,7 +36,12 @@ export function resolveSemanticLineage(eventsOrMap = [], query = {}, options = {
|
|
|
36
36
|
state.status = 'max-depth';
|
|
37
37
|
state.reasonCodes.push('max-depth');
|
|
38
38
|
}
|
|
39
|
-
if (!state.cycle && !state.maxDepthHit)
|
|
39
|
+
if (!state.cycle && !state.maxDepthHit) {
|
|
40
|
+
state.status = classifyResolutionStatus(state);
|
|
41
|
+
if (state.status === 'ambiguous' && state.terminal.length > 0 && state.current.length > 0) {
|
|
42
|
+
state.reasonCodes.push('inactive-anchor-has-active-candidates');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
40
45
|
return buildResolutionRecord(state, start, maxDepth, resolutionQuery, options);
|
|
41
46
|
}
|
|
42
47
|
|
|
@@ -105,6 +110,7 @@ function applyLineageEvent(state, event, visitedEvents) {
|
|
|
105
110
|
state.operationIds.push(event.crdt?.operationId);
|
|
106
111
|
state.heads.push(...(event.crdt?.heads ?? []));
|
|
107
112
|
state.eventKinds.push(event.eventKind);
|
|
113
|
+
state.reasonCodes.push(...lineageEventReasonCodes(event));
|
|
108
114
|
state.sourcePaths.push(event.from?.sourcePath, ...event.to.map((anchor) => anchor.sourcePath));
|
|
109
115
|
if (event.confidence !== undefined) state.confidence = state.confidence === undefined ? event.confidence : Math.min(state.confidence, event.confidence);
|
|
110
116
|
const matched = state.current.filter((anchor) => anchorsMatch(anchor, event.from));
|
|
@@ -142,6 +148,7 @@ function nextLineageEvents(anchors, byFromKey, byFromId) {
|
|
|
142
148
|
|
|
143
149
|
function classifyResolutionStatus(state) {
|
|
144
150
|
if (state.current.length === 0 && state.traversed.length > 0) return 'deleted';
|
|
151
|
+
if (state.terminal.length > 0 && state.current.length > 0) return 'ambiguous';
|
|
145
152
|
if (state.eventKinds.includes('recreated')) return 'recreated';
|
|
146
153
|
if (state.current.length > 1 || state.eventKinds.includes('split') || state.eventKinds.includes('merged')) return 'ambiguous';
|
|
147
154
|
if (state.traversed.length > 0) return 'resolved';
|
|
@@ -205,6 +212,16 @@ function lineageSourcePaths(state, start, query, anchors = []) {
|
|
|
205
212
|
...array(anchors).flatMap((anchor) => anchor?.lineageSourcePaths ?? [])
|
|
206
213
|
]);
|
|
207
214
|
}
|
|
215
|
+
function lineageEventReasonCodes(event) {
|
|
216
|
+
return uniqueStrings([
|
|
217
|
+
...array(event.reasonCodes),
|
|
218
|
+
...array(event.metadata?.reasonCodes),
|
|
219
|
+
event.evidence?.signatureHashMatch ? 'signature-hash-match' : undefined,
|
|
220
|
+
event.evidence?.bodyHashMatch ? 'body-hash-match' : undefined,
|
|
221
|
+
event.evidence?.pathMatch ? 'source-path-match' : undefined,
|
|
222
|
+
event.evidence?.sourceSpanMoved ? 'source-span-moved' : undefined
|
|
223
|
+
]);
|
|
224
|
+
}
|
|
208
225
|
function positiveInteger(value, fallback) {
|
|
209
226
|
const number = Number(value);
|
|
210
227
|
return Number.isFinite(number) && number > 0 ? Math.floor(number) : fallback;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import{idFragment,normalizeSemanticMergeReadiness,uniqueStrings}from'../../native-import-utils.js';
|
|
2
2
|
import{semanticMergeConflictRiskScore}from'./semanticMergeConflicts.js';
|
|
3
3
|
import{inferProjectionRisk,internalOverlaps,normalizeChangedSemanticRegions,normalizeProjectionRisk,projectionRiskRank,queryAdmissionOverlaps,summarizeOverlaps}from'./semanticMergeCandidateRecordInternals.js';
|
|
4
|
+
import{semanticMergeCandidateScoreFacets}from'./semanticMergeCandidateScoreFacets.js';
|
|
4
5
|
|
|
5
6
|
export const SemanticMergeCandidateProjectionRisks=Object.freeze(['low','medium','high','unknown']);
|
|
6
7
|
|
|
@@ -74,6 +75,21 @@ export function createSemanticMergeCandidateAdmissionRecord(input={},options={})
|
|
|
74
75
|
const projectionRisk=normalizeProjectionRisk(options.projectionRisk??candidate.projectionRisk??candidate.risk)
|
|
75
76
|
??inferProjectionRisk({readiness,candidate,patch,changedSemanticRegions,conflictKeys});
|
|
76
77
|
const overlaps=internalOverlaps(changedSemanticRegions);
|
|
78
|
+
const conflictRiskScore=semanticMergeConflictRiskScore(candidate);
|
|
79
|
+
const scoreFacets=semanticMergeCandidateScoreFacets({
|
|
80
|
+
source,
|
|
81
|
+
candidate,
|
|
82
|
+
patch,
|
|
83
|
+
readiness,
|
|
84
|
+
projectionRisk,
|
|
85
|
+
evidenceRecords,
|
|
86
|
+
evidenceIds,
|
|
87
|
+
proofIds,
|
|
88
|
+
changedSemanticRegions,
|
|
89
|
+
conflictKeys,
|
|
90
|
+
overlaps,
|
|
91
|
+
conflictRiskScore
|
|
92
|
+
});
|
|
77
93
|
const readinessSortKey=semanticMergeCandidateReadinessSortKey({
|
|
78
94
|
readiness,
|
|
79
95
|
projectionRisk,
|
|
@@ -106,11 +122,13 @@ export function createSemanticMergeCandidateAdmissionRecord(input={},options={})
|
|
|
106
122
|
evidenceIds,
|
|
107
123
|
proofIds,
|
|
108
124
|
overlapSummary:summarizeOverlaps(overlaps),
|
|
125
|
+
scoreFacets,
|
|
109
126
|
admission:{
|
|
110
127
|
readiness,
|
|
111
128
|
reviewRequired:readiness!=='ready'||projectionRisk!=='low'||overlaps.length>0,
|
|
112
129
|
action:admissionAction({readiness,projectionRisk,overlaps}),
|
|
113
130
|
sortKey:readinessSortKey,
|
|
131
|
+
scoreFacets,
|
|
114
132
|
reasonCodes:uniqueStrings([
|
|
115
133
|
...strings(options.reasonCodes),
|
|
116
134
|
...strings(source.reasons),
|
|
@@ -145,11 +163,12 @@ export function createSemanticMergeCandidateAdmissionRecord(input={},options={})
|
|
|
145
163
|
overlaps:overlaps.length,
|
|
146
164
|
readiness,
|
|
147
165
|
projectionRisk,
|
|
148
|
-
reviewRequired:readiness!=='ready'||projectionRisk!=='low'||overlaps.length>0
|
|
166
|
+
reviewRequired:readiness!=='ready'||projectionRisk!=='low'||overlaps.length>0,
|
|
167
|
+
scoreFacets:scoreFacets.summary
|
|
149
168
|
},
|
|
150
169
|
metadata:compactRecord({
|
|
151
170
|
sourceChangeSetId:source.kind==='frontier.lang.nativeSourceChangeSet'?source.id:undefined,
|
|
152
|
-
conflictRiskScore
|
|
171
|
+
conflictRiskScore,
|
|
153
172
|
conflictSummary:candidate.conflictSummary??candidate.metadata?.conflictSummary??source.metadata?.semanticMergeConflictSummary,
|
|
154
173
|
changedRegionProjectionSummary:source.metadata?.changedRegionProjectionSummary??candidate.metadata?.changedRegionProjectionSummary,
|
|
155
174
|
compact:true,
|
|
@@ -170,6 +189,7 @@ export function decorateSemanticMergeCandidateForAdmission(input={},options={}){
|
|
|
170
189
|
proofIds:admissionRecord.proofIds,
|
|
171
190
|
projectionRisk:admissionRecord.projectionRisk,
|
|
172
191
|
readinessSortKey:admissionRecord.readinessSortKey,
|
|
192
|
+
scoreFacets:admissionRecord.scoreFacets,
|
|
173
193
|
mergeAdmission:admissionRecord
|
|
174
194
|
};
|
|
175
195
|
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { uniqueStrings } from '../../native-import-utils.js';
|
|
2
|
+
|
|
3
|
+
const scoreFacetWeights = Object.freeze({ ownership: 18, staleStatus: 20, testEvidence: 18, overlap: 18, size: 10, semanticSidecarQuality: 16 });
|
|
4
|
+
|
|
5
|
+
export function semanticMergeCandidateScoreFacets(input) {
|
|
6
|
+
const components = {
|
|
7
|
+
ownership: ownershipFacet(input),
|
|
8
|
+
staleStatus: staleStatusFacet(input),
|
|
9
|
+
testEvidence: testEvidenceFacet(input),
|
|
10
|
+
overlap: overlapFacet(input),
|
|
11
|
+
size: sizeFacet(input),
|
|
12
|
+
semanticSidecarQuality: semanticSidecarQualityFacet(input)
|
|
13
|
+
};
|
|
14
|
+
const weightedTotal = Object.values(components).reduce((sum, component) => sum + component.weightedScore, 0);
|
|
15
|
+
const weightTotal = Object.values(components).reduce((sum, component) => sum + component.weight, 0);
|
|
16
|
+
const value = roundScore(weightTotal ? weightedTotal * 100 / weightTotal : 0);
|
|
17
|
+
const weakFacets = Object.values(components).filter((component) => component.status === 'weak').map((component) => component.key);
|
|
18
|
+
const blockedFacets = Object.values(components).filter((component) => component.status === 'blocked').map((component) => component.key);
|
|
19
|
+
const lowestScore = Math.min(...Object.values(components).map((component) => component.score));
|
|
20
|
+
return {
|
|
21
|
+
schema: 'frontier.lang.semanticMergeCandidateScoreFacets.v1',
|
|
22
|
+
version: 1,
|
|
23
|
+
higherIsBetter: true,
|
|
24
|
+
value,
|
|
25
|
+
risk: value < 50 || blockedFacets.length ? 'high' : value < 80 || weakFacets.length ? 'medium' : 'low',
|
|
26
|
+
components,
|
|
27
|
+
summary: {
|
|
28
|
+
value,
|
|
29
|
+
risk: value < 50 || blockedFacets.length ? 'high' : value < 80 || weakFacets.length ? 'medium' : 'low',
|
|
30
|
+
lowestScore,
|
|
31
|
+
weakFacets,
|
|
32
|
+
blockedFacets,
|
|
33
|
+
availableFacets: Object.values(components).filter((component) => component.signals.available !== false).map((component) => component.key)
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function ownershipFacet(input) {
|
|
39
|
+
const regions = input.changedSemanticRegions;
|
|
40
|
+
const keyedRegions = regions.filter((region) => region.key || region.conflictKey);
|
|
41
|
+
const kindedRegions = regions.filter((region) => region.regionKind);
|
|
42
|
+
const spannedRegions = regions.filter((region) => region.sourceSpan);
|
|
43
|
+
const score = regions.length
|
|
44
|
+
? roundScore(keyedRegions.length * 70 / regions.length + kindedRegions.length * 15 / regions.length + spannedRegions.length * 15 / regions.length)
|
|
45
|
+
: input.conflictKeys.length ? 45 : 70;
|
|
46
|
+
return scoreFacet('ownership', score, [
|
|
47
|
+
...(regions.length === 0 ? ['missing-changed-semantic-regions'] : []),
|
|
48
|
+
...(regions.length && keyedRegions.length < regions.length ? ['missing-ownership-keys'] : [])
|
|
49
|
+
], {
|
|
50
|
+
changedSemanticRegions: regions.length,
|
|
51
|
+
keyedRegions: keyedRegions.length,
|
|
52
|
+
regionKinds: uniqueStrings(regions.map((region) => region.regionKind)),
|
|
53
|
+
conflictKeys: input.conflictKeys,
|
|
54
|
+
ownershipKeys: uniqueStrings(regions.map((region) => region.key))
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function staleStatusFacet(input) {
|
|
59
|
+
const staleEvidence = input.evidenceRecords.filter((record) => evidenceStatus(record) === 'stale' || record?.metadata?.stale === true);
|
|
60
|
+
const sourceHashVerified = [
|
|
61
|
+
input.source?.metadata?.sourceHashVerified,
|
|
62
|
+
input.source?.before?.metadata?.sourceHashVerified,
|
|
63
|
+
input.source?.after?.metadata?.sourceHashVerified,
|
|
64
|
+
input.candidate?.metadata?.sourceHashVerified,
|
|
65
|
+
input.patch?.metadata?.sourceHashVerified
|
|
66
|
+
].filter((value) => value !== undefined);
|
|
67
|
+
const staleProofs = semanticSidecarQuality(input)?.proofSummary?.stale ?? 0;
|
|
68
|
+
const sourceHashStale = sourceHashVerified.includes(false);
|
|
69
|
+
const score = sourceHashStale ? 0 : staleEvidence.length ? 35 : staleProofs > 0 ? 55 : 100;
|
|
70
|
+
return scoreFacet('staleStatus', score, [
|
|
71
|
+
...(sourceHashStale ? ['stale-source-hash'] : []),
|
|
72
|
+
...(staleEvidence.length ? ['stale-evidence'] : []),
|
|
73
|
+
...(staleProofs > 0 ? ['stale-sidecar-proof-obligations'] : [])
|
|
74
|
+
], {
|
|
75
|
+
staleEvidenceIds: staleEvidence.map((record) => record.id).filter(Boolean),
|
|
76
|
+
staleProofObligations: staleProofs,
|
|
77
|
+
sourceHashVerified
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function testEvidenceFacet(input) {
|
|
82
|
+
const failed = input.evidenceRecords.filter((record) => evidenceStatus(record) === 'failed');
|
|
83
|
+
const stale = input.evidenceRecords.filter((record) => evidenceStatus(record) === 'stale');
|
|
84
|
+
const pending = input.evidenceRecords.filter((record) => ['pending', 'assumed', 'unknown'].includes(evidenceStatus(record)));
|
|
85
|
+
const passed = input.evidenceRecords.filter((record) => ['passed', 'ok', 'success'].includes(evidenceStatus(record)));
|
|
86
|
+
const score = failed.length ? 0 : stale.length ? 45 : pending.length ? 65 : input.proofIds.length ? 100 : input.evidenceIds.length ? 82 : 35;
|
|
87
|
+
return scoreFacet('testEvidence', score, [
|
|
88
|
+
...(failed.length ? ['failed-evidence'] : []),
|
|
89
|
+
...(stale.length ? ['stale-evidence'] : []),
|
|
90
|
+
...(pending.length ? ['pending-evidence'] : []),
|
|
91
|
+
...(!input.evidenceIds.length && !input.proofIds.length ? ['missing-evidence'] : [])
|
|
92
|
+
], {
|
|
93
|
+
evidenceIds: input.evidenceIds,
|
|
94
|
+
proofIds: input.proofIds,
|
|
95
|
+
evidenceRecords: input.evidenceRecords.length,
|
|
96
|
+
passedEvidenceIds: passed.map((record) => record.id).filter(Boolean),
|
|
97
|
+
failedEvidenceIds: failed.map((record) => record.id).filter(Boolean),
|
|
98
|
+
staleEvidenceIds: stale.map((record) => record.id).filter(Boolean),
|
|
99
|
+
pendingEvidenceIds: pending.map((record) => record.id).filter(Boolean)
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function overlapFacet(input) {
|
|
104
|
+
const high = input.overlaps.filter((overlap) => overlap.risk === 'high').length;
|
|
105
|
+
const medium = input.overlaps.filter((overlap) => overlap.risk === 'medium').length;
|
|
106
|
+
const score = clampScore(100 - high * 45 - medium * 25 - Math.max(0, input.overlaps.length - high - medium) * 15);
|
|
107
|
+
return scoreFacet('overlap', score, [
|
|
108
|
+
...(input.overlaps.length ? ['overlapping-semantic-regions'] : [])
|
|
109
|
+
], {
|
|
110
|
+
overlaps: input.overlaps.length,
|
|
111
|
+
highRiskOverlaps: high,
|
|
112
|
+
mediumRiskOverlaps: medium,
|
|
113
|
+
conflictKeys: uniqueStrings(input.overlaps.flatMap((overlap) => overlap.conflictKeys ?? [])),
|
|
114
|
+
byKind: countBy(input.overlaps.map((overlap) => overlap.overlapKind))
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function sizeFacet(input) {
|
|
119
|
+
const operationCount = array(input.candidate?.operations ?? input.patch?.operations).length;
|
|
120
|
+
const changedSemanticRegions = input.changedSemanticRegions.length;
|
|
121
|
+
const conflictKeys = input.conflictKeys.length;
|
|
122
|
+
const score = clampScore(100 - Math.max(0, changedSemanticRegions - 3) * 8 - Math.max(0, operationCount - 3) * 4 - Math.max(0, conflictKeys - 4) * 3);
|
|
123
|
+
return scoreFacet('size', score, [
|
|
124
|
+
...(changedSemanticRegions > 3 ? ['large-changed-region-set'] : []),
|
|
125
|
+
...(operationCount > 3 ? ['large-operation-set'] : []),
|
|
126
|
+
...(conflictKeys > 4 ? ['many-conflict-keys'] : [])
|
|
127
|
+
], { changedSemanticRegions, operationCount, conflictKeys });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function semanticSidecarQualityFacet(input) {
|
|
131
|
+
const quality = semanticSidecarQuality(input);
|
|
132
|
+
if (!quality) return scoreFacet('semanticSidecarQuality', 100, [], { available: false });
|
|
133
|
+
const proofSummary = quality.proofSummary ?? {};
|
|
134
|
+
const warningCodes = uniqueStrings([...(quality.warnings ?? []).map((warning) => warning.code), ...strings(quality.expectedMissingReasonCodes)]);
|
|
135
|
+
const reviewProofs = (proofSummary.open ?? 0) + (proofSummary.stale ?? 0) + (proofSummary.assumed ?? 0) + (proofSummary.externalToolRequired ?? 0) + (proofSummary.unknown ?? 0);
|
|
136
|
+
const score = clampScore(
|
|
137
|
+
(quality.imported === false || quality.selected === false ? 25 : 100)
|
|
138
|
+
- (quality.eligible === false ? 35 : 0)
|
|
139
|
+
- Math.min(35, (quality.warningCount ?? warningCodes.length ?? 0) * 7)
|
|
140
|
+
- Math.min(40, (proofSummary.failed ?? 0) * 20)
|
|
141
|
+
- Math.min(25, reviewProofs * 5)
|
|
142
|
+
);
|
|
143
|
+
return scoreFacet('semanticSidecarQuality', score, [
|
|
144
|
+
...(quality.imported === false || quality.selected === false ? ['semantic-sidecar-not-imported'] : []),
|
|
145
|
+
...(quality.eligible === false ? ['semantic-sidecar-not-eligible'] : []),
|
|
146
|
+
...warningCodes
|
|
147
|
+
], {
|
|
148
|
+
available: true,
|
|
149
|
+
expected: quality.expected,
|
|
150
|
+
expectedSatisfied: quality.expectedSatisfied,
|
|
151
|
+
selected: quality.selected,
|
|
152
|
+
imported: quality.imported,
|
|
153
|
+
eligible: quality.eligible,
|
|
154
|
+
importCount: quality.importCount,
|
|
155
|
+
symbolCount: quality.symbolCount,
|
|
156
|
+
ownershipRegionCount: quality.ownershipRegionCount,
|
|
157
|
+
patchHintCount: quality.patchHintCount,
|
|
158
|
+
evidenceCount: quality.evidenceCount,
|
|
159
|
+
warningCount: quality.warningCount ?? warningCodes.length,
|
|
160
|
+
warningCodes,
|
|
161
|
+
proofSummary
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function semanticSidecarQuality(input) {
|
|
166
|
+
return [
|
|
167
|
+
input.candidate?.semanticSidecarQuality,
|
|
168
|
+
input.candidate?.sidecarQuality,
|
|
169
|
+
input.candidate?.semanticSidecar?.quality,
|
|
170
|
+
input.candidate?.sidecar?.quality,
|
|
171
|
+
input.candidate?.metadata?.semanticSidecarQuality,
|
|
172
|
+
input.candidate?.metadata?.sidecarQuality,
|
|
173
|
+
input.source?.semanticSidecarQuality,
|
|
174
|
+
input.source?.sidecarQuality,
|
|
175
|
+
input.source?.semanticSidecar?.quality,
|
|
176
|
+
input.source?.sidecar?.quality,
|
|
177
|
+
input.source?.metadata?.semanticSidecarQuality,
|
|
178
|
+
input.source?.metadata?.sidecarQuality,
|
|
179
|
+
input.patch?.semanticSidecarQuality,
|
|
180
|
+
input.patch?.metadata?.semanticSidecarQuality
|
|
181
|
+
].find(looksLikeSemanticSidecarQuality);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function looksLikeSemanticSidecarQuality(value) {
|
|
185
|
+
return value && typeof value === 'object' && (
|
|
186
|
+
value.schema === 'frontier.lang.semanticSidecarQuality.v1'
|
|
187
|
+
|| value.eligible !== undefined
|
|
188
|
+
|| value.imported !== undefined
|
|
189
|
+
|| value.proofSummary !== undefined
|
|
190
|
+
|| value.warningCount !== undefined
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function scoreFacet(key, score, reasonCodes, signals) {
|
|
195
|
+
const normalizedScore = clampScore(score);
|
|
196
|
+
const weight = scoreFacetWeights[key] ?? 1;
|
|
197
|
+
return {
|
|
198
|
+
key,
|
|
199
|
+
score: normalizedScore,
|
|
200
|
+
weight,
|
|
201
|
+
weightedScore: roundScore(normalizedScore * weight / 100),
|
|
202
|
+
status: facetStatus(normalizedScore),
|
|
203
|
+
reasonCodes: uniqueStrings(reasonCodes),
|
|
204
|
+
signals: compactRecord(signals)
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function facetStatus(score) {
|
|
209
|
+
if (score <= 0) return 'blocked';
|
|
210
|
+
if (score < 50) return 'weak';
|
|
211
|
+
if (score < 80) return 'partial';
|
|
212
|
+
return 'strong';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function evidenceStatus(record) { return String(record?.status ?? record?.metadata?.status ?? '').toLowerCase(); }
|
|
216
|
+
function countBy(values) { const counts = {}; for (const value of values ?? []) { const key = String(value ?? 'unknown'); counts[key] = (counts[key] ?? 0) + 1; } return counts; }
|
|
217
|
+
function clampScore(value) { return Math.max(0, Math.min(100, roundScore(value))); }
|
|
218
|
+
function roundScore(value) { return Math.round((Number.isFinite(value) ? value : 0) * 100) / 100; }
|
|
219
|
+
function array(value) { if (value === undefined || value === null) return []; return Array.isArray(value) ? value : [value]; }
|
|
220
|
+
function strings(value) { return array(value).map((entry) => String(entry ?? '')).filter(Boolean); }
|
|
221
|
+
function compactRecord(value) { return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0))); }
|
|
@@ -90,7 +90,7 @@ export function querySemanticPatchBundleOverlaps(records,query={}){
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
function sharedIndex(left,right,options){
|
|
93
|
-
|
|
93
|
+
const shared={
|
|
94
94
|
operationContentHashes:intersect(left.operationContentHashes,right.operationContentHashes),
|
|
95
95
|
editContentHashes:intersect(left.editContentHashes,right.editContentHashes),
|
|
96
96
|
semanticEditKeys:intersect(left.semanticEditKeys,right.semanticEditKeys),
|
|
@@ -110,6 +110,28 @@ function sharedIndex(left,right,options){
|
|
|
110
110
|
baseHashes:intersect(left.baseHashes,right.baseHashes),
|
|
111
111
|
targetHashes:intersect(left.targetHashes,right.targetHashes)
|
|
112
112
|
};
|
|
113
|
+
const scopedEdit=hasSharedEditScope(shared);
|
|
114
|
+
const scopedSource=hasSharedSourceScope(shared);
|
|
115
|
+
return{
|
|
116
|
+
...shared,
|
|
117
|
+
operationContentHashes:scopedEdit?shared.operationContentHashes:[],
|
|
118
|
+
editContentHashes:scopedEdit?shared.editContentHashes:[],
|
|
119
|
+
semanticEditKeys:scopedSource?shared.semanticEditKeys:[],
|
|
120
|
+
semanticIdentityHashes:scopedSource?shared.semanticIdentityHashes:[],
|
|
121
|
+
semanticEditReplayCurrentHashes:scopedSource?shared.semanticEditReplayCurrentHashes:[],
|
|
122
|
+
semanticEditReplayOutputHashes:scopedEdit?shared.semanticEditReplayOutputHashes:[],
|
|
123
|
+
semanticTransformContentHashes:shared.projectionIdentityHashes.length?shared.semanticTransformContentHashes:[],
|
|
124
|
+
semanticTransformIdentityHashes:shared.projectionIdentityHashes.length?shared.semanticTransformIdentityHashes:[]
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function hasSharedEditScope(shared){
|
|
129
|
+
return Boolean(shared.regionKeys.length||shared.conflictKeys.length||shared.sourceIdentityHashes.length
|
|
130
|
+
||(shared.sourcePaths.length&&(shared.semanticEditKeys.length||shared.semanticIdentityHashes.length)));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function hasSharedSourceScope(shared){
|
|
134
|
+
return Boolean(shared.sourcePaths.length||shared.regionKeys.length||shared.conflictKeys.length||shared.sourceIdentityHashes.length);
|
|
113
135
|
}
|
|
114
136
|
|
|
115
137
|
function overlapAdmission(shared,{leftIndex,rightIndex,options}){
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export function sourcePreservationFromProjectionContext(context) {
|
|
2
|
-
return context.
|
|
2
|
+
return context.sourcePreservation
|
|
3
|
+
?? context.metadata?.sourcePreservation
|
|
4
|
+
?? context.importResult?.metadata?.sourcePreservation
|
|
5
|
+
?? context.importResult?.nativeSource?.metadata?.sourcePreservation
|
|
6
|
+
?? context.importResult?.nativeAst?.metadata?.sourcePreservation
|
|
7
|
+
?? context.nativeSource?.metadata?.sourcePreservation
|
|
3
8
|
?? context.nativeAst?.metadata?.sourcePreservation
|
|
4
|
-
?? context.nativeSource?.ast?.metadata?.sourcePreservation
|
|
9
|
+
?? context.nativeSource?.ast?.metadata?.sourcePreservation
|
|
10
|
+
?? context.universalAst?.metadata?.sourcePreservation
|
|
11
|
+
?? context.importResult?.universalAst?.metadata?.sourcePreservation;
|
|
5
12
|
}
|
|
@@ -17,13 +17,21 @@ export const NativeImportLanguageProfiles = Object.freeze([
|
|
|
17
17
|
aliases: ['js', 'mjs', 'cjs', 'jsx'],
|
|
18
18
|
extensions: ['.js', '.mjs', '.cjs', '.jsx'],
|
|
19
19
|
parserAdapters: ['estree', 'babel', 'tree-sitter'],
|
|
20
|
-
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'dynamicRuntime']
|
|
20
|
+
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'dynamicRuntime'],
|
|
21
|
+
notes: [
|
|
22
|
+
'lightweight scanner records declarations only; exact parser adapters must be injected by the host',
|
|
23
|
+
'.jsx sources are classified as javascript for declaration scanning; JSX element trees remain opaque without host parser evidence'
|
|
24
|
+
]
|
|
21
25
|
}),
|
|
22
26
|
nativeImportLanguageProfile('typescript', {
|
|
23
27
|
aliases: ['ts', 'tsx'],
|
|
24
28
|
extensions: ['.ts', '.tsx'],
|
|
25
29
|
parserAdapters: ['typescript-compiler-api', 'babel', 'tree-sitter'],
|
|
26
|
-
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'unsupportedSyntax']
|
|
30
|
+
lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'unsupportedSyntax'],
|
|
31
|
+
notes: [
|
|
32
|
+
'lightweight scanner records declarations only; exact parser adapters must be injected by the host',
|
|
33
|
+
'.tsx sources are classified as typescript for declaration scanning; JSX element trees remain opaque without host parser evidence'
|
|
34
|
+
]
|
|
27
35
|
}),
|
|
28
36
|
nativeImportLanguageProfile('python', {
|
|
29
37
|
aliases: ['py'],
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
splitParameters
|
|
5
5
|
} from './native-region-scanner-core.js';
|
|
6
6
|
import { jsImportDeclarations } from './native-region-scanner-js-imports.js';
|
|
7
|
-
|
|
8
7
|
function jsCommentOnlyLine(trimmed) {
|
|
9
8
|
return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*');
|
|
10
9
|
}
|
|
@@ -128,7 +127,7 @@ function jsExportedContainerDeclaration(input, lineNumber, trimmed) {
|
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
function jsExportedFunctionWrapperDeclaration(input, lineNumber, trimmed) {
|
|
131
|
-
const match = trimmed.match(/^export\s+default\s+((?:React\.)?(?:forwardRef|memo|lazy|observer))\s*(?:<[^>]+>)?\s*\(\s*(.+)$/);
|
|
130
|
+
const match = trimmed.match(/^export\s+default\s+((?:React\.)?(?:forwardRef|memo|lazy|observer)|Object\.freeze)\s*(?:<[^>]+>)?\s*\(\s*(.+)$/);
|
|
132
131
|
if (!match) return undefined;
|
|
133
132
|
const wrapper = match[1];
|
|
134
133
|
const argument = match[2].trim();
|
|
@@ -148,6 +147,13 @@ function jsExportedFunctionWrapperDeclaration(input, lineNumber, trimmed) {
|
|
|
148
147
|
parameters: splitParameters(functionMatch[1] ?? functionMatch[2])
|
|
149
148
|
}, true);
|
|
150
149
|
}
|
|
150
|
+
const classMatch = argument.match(/^(?:abstract\s+)?class\b(?:\s+(?!(?:extends|implements)\b)([A-Za-z_$][\w$]*))?/);
|
|
151
|
+
if (classMatch) {
|
|
152
|
+
return nativeDeclaration(input, lineNumber, 'ExportDefaultClassWrapperDeclaration', 'class', classMatch[1] ?? 'default', {
|
|
153
|
+
exportDefault: true,
|
|
154
|
+
wrapper
|
|
155
|
+
}, true);
|
|
156
|
+
}
|
|
151
157
|
const aliasMatch = argument.match(/^([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*(?:[,)]|$)/);
|
|
152
158
|
if (!aliasMatch) return undefined;
|
|
153
159
|
return nativeDeclaration(input, lineNumber, 'ExportDefaultWrappedAlias', 'variable', 'default', {
|
|
@@ -44,6 +44,13 @@ export function jsImportDeclarations(input, lineNumber, trimmed) {
|
|
|
44
44
|
importedName: 'default',
|
|
45
45
|
importKind: trimmed.includes('import') ? 'dynamic-import-binding' : 'commonjs-require'
|
|
46
46
|
}]);
|
|
47
|
+
const sideEffectRequireMatch = trimmed.match(/^require\s*\(\s*(['"])([^'"]+)\1\s*\)\s*;?$/);
|
|
48
|
+
if (sideEffectRequireMatch) {
|
|
49
|
+
return jsImportModuleDeclarations(input, lineNumber, sideEffectRequireMatch[2], 'CommonJsSideEffectRequireDeclaration', [], {
|
|
50
|
+
sideEffectOnly: true,
|
|
51
|
+
importKind: 'commonjs-require'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
47
54
|
return [];
|
|
48
55
|
}
|
|
49
56
|
|
|
@@ -126,18 +126,18 @@ function scanJavaScriptLike(input) {
|
|
|
126
126
|
} else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=/))) {
|
|
127
127
|
const regionKind = jsRegionKindForDeclarationName(match[1], trimmed);
|
|
128
128
|
pushDeclaration(nativeDeclaration(input, number, 'CommonJsExport', 'variable', match[1], { export: 'commonjs' }, false, { regionKind }));
|
|
129
|
-
} else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
|
|
129
|
+
} else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?(#?[A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
|
|
130
130
|
pushDeclaration(nativeDeclaration(input, number, 'MethodDefinition', 'method', `${currentClass}.${match[1]}`, {
|
|
131
131
|
methodName: match[1],
|
|
132
132
|
owner: currentClass,
|
|
133
133
|
parameters: splitParameters(match[2])
|
|
134
134
|
}, declarationLine.includes('{') || declarationLine.includes('=>')));
|
|
135
|
-
} else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*([A-Za-z_$][\w$]*)[?!]?\s*(?::\s*([^=;{]+))?(?:[=;]|$)/))) {
|
|
135
|
+
} else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*(#?[A-Za-z_$][\w$]*)[?!]?\s*(?::\s*([^=;{]+))?(?:[=;]|$)/))) {
|
|
136
136
|
pushDeclaration(nativeDeclaration(input, number, 'PropertyDefinition', 'property', `${currentClass}.${match[1]}`, {
|
|
137
137
|
propertyName: match[1],
|
|
138
138
|
owner: currentClass,
|
|
139
139
|
valueType: match[2]?.trim()
|
|
140
|
-
}, false));
|
|
140
|
+
}, false, { regionKind: 'property' }));
|
|
141
141
|
}
|
|
142
142
|
if (currentClass) {
|
|
143
143
|
classDepth += braceDelta(trimmed);
|
|
@@ -271,7 +271,7 @@ function jsInlineClassMemberDeclarations(input, lineNumber, declarationLine, cla
|
|
|
271
271
|
if (open < 0 || close <= open) return [];
|
|
272
272
|
const body = declarationLine.slice(open + 1, close);
|
|
273
273
|
const declarations = [];
|
|
274
|
-
for (const match of body.matchAll(/(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={;]+)?\s*(?:\{|=>)/g)) {
|
|
274
|
+
for (const match of body.matchAll(/(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?(#?[A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={;]+)?\s*(?:\{|=>)/g)) {
|
|
275
275
|
if (jsControlKeyword(match[1])) continue;
|
|
276
276
|
declarations.push(nativeDeclaration(input, lineNumber, 'MethodDefinition', 'method', `${className}.${match[1]}`, {
|
|
277
277
|
methodName: match[1],
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
scanHaskell,
|
|
27
27
|
scanR
|
|
28
28
|
} from './native-region-scanner-functional.js';
|
|
29
|
+
import { normalizeNativeLanguageId } from './native-import-utils.js';
|
|
29
30
|
export { lightweightCoverageLosses } from './native-region-scanner-core.js';
|
|
30
31
|
export {
|
|
31
32
|
detectNewlineStyle,
|
|
@@ -34,7 +35,7 @@ export {
|
|
|
34
35
|
} from './native-source-preservation-scanner.js';
|
|
35
36
|
|
|
36
37
|
function scanNativeDeclarations(input) {
|
|
37
|
-
const language = String(input.language).toLowerCase();
|
|
38
|
+
const language = normalizeNativeLanguageId(input.language) || String(input.language).toLowerCase();
|
|
38
39
|
if (language === 'javascript' || language === 'typescript') return scanJavaScriptLike(input);
|
|
39
40
|
if (language === 'python') return scanPython(input);
|
|
40
41
|
if (language === 'rust') return scanRust(input);
|
|
@@ -20,20 +20,20 @@ function semanticOwnershipRegionForSymbol(imported, symbol, mapping, nativeNode,
|
|
|
20
20
|
const language = symbol.language ?? imported?.language ?? imported?.nativeAst?.language ?? imported?.nativeSource?.language;
|
|
21
21
|
const sourceSpan = mapping?.sourceSpan ?? symbol.definitionSpan ?? nativeNode?.span;
|
|
22
22
|
const regionKind = semanticRegionKindForSymbol(symbol, mapping, nativeNode);
|
|
23
|
-
const key = [
|
|
23
|
+
const key = symbol?.metadata?.ownershipRegionKey ?? [
|
|
24
24
|
options.regionPrefix ?? 'source',
|
|
25
25
|
sourcePath ?? `${language}:memory`,
|
|
26
26
|
regionKind,
|
|
27
27
|
symbol.name ?? symbol.id
|
|
28
28
|
].map((part) => String(part).replace(/\s+/g, ' ').trim()).join('#');
|
|
29
29
|
return {
|
|
30
|
-
id: `region_${caseSensitiveIdFragment(key)}`,
|
|
30
|
+
id: symbol?.metadata?.ownershipRegionId ?? `region_${caseSensitiveIdFragment(key)}`,
|
|
31
31
|
key,
|
|
32
32
|
regionKind,
|
|
33
33
|
granularity: 'symbol',
|
|
34
34
|
language,
|
|
35
35
|
sourcePath,
|
|
36
|
-
sourceHash: imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash,
|
|
36
|
+
sourceHash: imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash ?? imported?.sourceHash,
|
|
37
37
|
symbolId: symbol.id,
|
|
38
38
|
symbolName: symbol.name,
|
|
39
39
|
symbolKind: symbol.kind,
|
|
@@ -76,17 +76,19 @@ function semanticOwnershipRegionForDeclaration(input, declaration, documentId) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function semanticPatchHintForRegion(region, readiness, options = {}) {
|
|
79
|
+
const supportedOperations = semanticRegionSupportedOperations(region);
|
|
79
80
|
return {
|
|
80
81
|
id: `hint_${idFragment(region.id)}`,
|
|
81
82
|
kind: 'source-region-patch',
|
|
82
83
|
ownershipRegionId: region.id,
|
|
83
84
|
ownershipKey: region.key,
|
|
85
|
+
operation: supportedOperations[0] ?? 'replace-region',
|
|
84
86
|
sourcePath: region.sourcePath,
|
|
85
87
|
sourceHash: region.sourceHash,
|
|
86
88
|
sourceSpan: region.sourceSpan,
|
|
87
89
|
readiness,
|
|
88
90
|
precision: region.precision,
|
|
89
|
-
supportedOperations
|
|
91
|
+
supportedOperations,
|
|
90
92
|
projection: {
|
|
91
93
|
sourceLanguage: region.language,
|
|
92
94
|
targetPath: options.targetPath ?? region.sourcePath,
|
|
@@ -8,6 +8,16 @@ export interface SemanticImportSidecarQualityWarning {
|
|
|
8
8
|
readonly sourcePaths: readonly string[];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export type SemanticImportSidecarRecordClassification = 'useful' | 'expected-empty' | 'unexpectedly-empty' | 'missing' | string;
|
|
12
|
+
|
|
13
|
+
export interface SemanticImportSidecarQualityRecord {
|
|
14
|
+
readonly classification: SemanticImportSidecarRecordClassification;
|
|
15
|
+
readonly reasonCode: string;
|
|
16
|
+
readonly message: string;
|
|
17
|
+
readonly action: string;
|
|
18
|
+
readonly sourcePaths: readonly string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
export interface SemanticImportSidecarProofAdmissionSummary {
|
|
12
22
|
readonly total: number;
|
|
13
23
|
readonly obligations: number;
|
|
@@ -26,12 +36,14 @@ export interface SemanticImportSidecarProofAdmissionSummary {
|
|
|
26
36
|
export interface SemanticImportSidecarQuality {
|
|
27
37
|
readonly schema: 'frontier.lang.semanticSidecarQuality.v1';
|
|
28
38
|
readonly expected: boolean;
|
|
39
|
+
readonly expectedEmpty: boolean;
|
|
29
40
|
readonly expectedSatisfied: boolean;
|
|
30
41
|
readonly expectedMissingReasonCodes: readonly string[];
|
|
31
42
|
readonly selected: boolean;
|
|
32
43
|
readonly eligible: boolean;
|
|
33
44
|
readonly imported: boolean;
|
|
34
45
|
readonly importCount: number;
|
|
46
|
+
readonly record: SemanticImportSidecarQualityRecord;
|
|
35
47
|
readonly symbolCount: number;
|
|
36
48
|
readonly ownershipRegionCount: number;
|
|
37
49
|
readonly patchHintCount: number;
|
|
@@ -45,6 +57,7 @@ export interface SemanticImportSidecarQuality {
|
|
|
45
57
|
export interface SemanticImportSidecarAdmission {
|
|
46
58
|
readonly schema: 'frontier.lang.semanticSidecarAdmission.v1';
|
|
47
59
|
readonly expected: boolean;
|
|
60
|
+
readonly expectedEmpty: boolean;
|
|
48
61
|
readonly expectedSatisfied: boolean;
|
|
49
62
|
readonly expectedMissingReasonCodes: readonly string[];
|
|
50
63
|
readonly selected: boolean;
|
|
@@ -53,6 +66,7 @@ export interface SemanticImportSidecarAdmission {
|
|
|
53
66
|
readonly importCount: number;
|
|
54
67
|
readonly readiness: SemanticMergeReadiness;
|
|
55
68
|
readonly action: string;
|
|
69
|
+
readonly record: SemanticImportSidecarQualityRecord;
|
|
56
70
|
readonly counts: {
|
|
57
71
|
readonly symbols: number;
|
|
58
72
|
readonly ownershipRegions: number;
|