@shapeshift-labs/frontier-lang-compiler 0.2.65 → 0.2.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +37 -8
  2. package/bench/smoke.mjs +15 -1
  3. package/bench/universal-fixture-suite.mjs +183 -0
  4. package/dist/declarations/import-adapter-core.d.ts +3 -0
  5. package/dist/declarations/native-project-admission.d.ts +133 -0
  6. package/dist/declarations/roundtrip-audit.d.ts +177 -0
  7. package/dist/declarations/roundtrip.d.ts +2 -53
  8. package/dist/declarations/semantic-history-records.d.ts +277 -0
  9. package/dist/declarations/semantic-history.d.ts +45 -92
  10. package/dist/declarations/semantic-merge-candidates.d.ts +200 -0
  11. package/dist/declarations/semantic-merge-conflicts.d.ts +12 -0
  12. package/dist/declarations/semantic-sidecar.d.ts +8 -3
  13. package/dist/declarations/semantic-slice-admission.d.ts +111 -0
  14. package/dist/declarations/semantic-slice.d.ts +36 -1
  15. package/dist/declarations/universal-conversion-plan.d.ts +59 -0
  16. package/dist/declarations/universal-runtime-capabilities.d.ts +171 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +2 -0
  19. package/dist/internal/index-impl/attachExternalOwnership.js +18 -10
  20. package/dist/internal/index-impl/createNativeRoundtripEvidence.js +54 -49
  21. package/dist/internal/index-impl/createSemanticImportSidecar.js +6 -0
  22. package/dist/internal/index-impl/createSemanticSlice.js +4 -3
  23. package/dist/internal/index-impl/createSemanticSliceAdmissionRecord.js +10 -1
  24. package/dist/internal/index-impl/diffNativeSourceImports.js +3 -2
  25. package/dist/internal/index-impl/expandSemanticSliceSelection.js +0 -1
  26. package/dist/internal/index-impl/externalSemanticBase.js +1 -0
  27. package/dist/internal/index-impl/importExternalSemanticIndex.js +4 -0
  28. package/dist/internal/index-impl/nativeRoundtripAudit.js +217 -0
  29. package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +160 -0
  30. package/dist/internal/index-impl/projectImportAdmissionLanguageSummaries.js +247 -0
  31. package/dist/internal/index-impl/projectImportAdmissionRanks.js +52 -0
  32. package/dist/internal/index-impl/projectImportAdmissionSummaries.js +77 -117
  33. package/dist/internal/index-impl/projectImportAdmissionTasks.js +239 -0
  34. package/dist/internal/index-impl/semanticHistoryRecordNormalizers.js +151 -0
  35. package/dist/internal/index-impl/semanticHistoryRecordOverlaps.js +113 -0
  36. package/dist/internal/index-impl/semanticHistoryRecords.js +210 -149
  37. package/dist/internal/index-impl/semanticMergeCandidateRecordInternals.js +314 -0
  38. package/dist/internal/index-impl/semanticMergeCandidateRecords.js +241 -0
  39. package/dist/internal/index-impl/semanticSliceAdmissionSurface.js +142 -0
  40. package/dist/internal/index-impl/semanticSliceExpectationAssertions.js +100 -0
  41. package/dist/internal/index-impl/semanticSliceExpectationRecords.js +75 -0
  42. package/dist/internal/index-impl/semanticSliceExpectedAssertions.js +5 -2
  43. package/dist/internal/index-impl/testSemanticSlice.js +4 -1
  44. package/dist/internal/index-impl/withExternalEmptyLoss.js +1 -0
  45. package/dist/language-adapter-package-contracts.js +12 -57
  46. package/dist/language-adapter-package-rows.js +116 -0
  47. package/dist/lightweight-dependency-language.js +8 -0
  48. package/dist/native-region-scanner-core.js +42 -10
  49. package/dist/native-region-scanner-js-helpers.js +2 -0
  50. package/dist/native-region-scanner-js-imports.js +111 -0
  51. package/dist/native-region-scanner-js.js +111 -28
  52. package/dist/universal-conversion-plan-summary.js +42 -0
  53. package/dist/universal-conversion-plan.js +46 -40
  54. package/dist/universal-runtime-capabilities.js +92 -0
  55. package/dist/universal-runtime-host-selectors.js +192 -0
  56. package/dist/universal-runtime-profiles.js +109 -0
  57. package/dist/universal-runtime-route-records.js +162 -0
  58. package/examples/js-frontier-rust-workbench-adapters.mjs +89 -0
  59. package/examples/js-frontier-rust-workbench-bounds.mjs +4 -3
  60. package/examples/js-frontier-rust-workbench-client.mjs +135 -59
  61. package/examples/js-frontier-rust-workbench-convert.mjs +161 -0
  62. package/examples/js-frontier-rust-workbench-html.mjs +20 -13
  63. package/examples/js-frontier-rust-workbench-route-styles.mjs +126 -0
  64. package/examples/js-frontier-rust-workbench-route.mjs +190 -0
  65. package/examples/js-frontier-rust-workbench-styles.mjs +12 -54
  66. package/examples/js-frontier-rust-workbench.mjs +54 -214
  67. package/package.json +1 -1
@@ -1,7 +1,10 @@
1
1
  import{countBy,maxSemanticMergeReadiness,uniqueRecordsById,uniqueStrings}from'../../native-import-utils.js';
2
+ import{createSemanticMergeCandidateAdmissionRecord,querySemanticMergeCandidateAdmissionOverlaps,sortSemanticMergeCandidateAdmissionRecords}from'./semanticMergeCandidateRecords.js';
3
+ import{compactAdmissionSource,importLosses,sourceLossClasses,summarizeImportPreservation,summarizeParserEvidence}from'./projectImportAdmissionImportEvidence.js';
4
+ import{sourceMissingEvidence,sourceMissingTasks,sourceSemanticMergeScore}from'./projectImportAdmissionTasks.js';
5
+ import{candidateRisk,maxPreservationQuality,maxRisk,normalizeRisk}from'./projectImportAdmissionRanks.js';
2
6
 
3
- const riskRank=Object.freeze({low:0,medium:1,unknown:2,high:3});
4
- const preservationRank=Object.freeze({exact:0,lossy:1,missing:2,stale:3,empty:4});
7
+ export{admissionLanguages}from'./projectImportAdmissionLanguageSummaries.js';
5
8
 
6
9
  export function projectAdmissionImports(imports,sourceRows,mergeCandidates){
7
10
  return imports.map((imported,index)=>{
@@ -17,49 +20,59 @@ export function projectAdmissionImports(imports,sourceRows,mergeCandidates){
17
20
  relations:semanticIndex?.relations?.length??0,
18
21
  facts:semanticIndex?.facts?.length??0
19
22
  };
23
+ const readiness=source.readiness??imported?.metadata?.semanticMergeReadiness??candidates[0]?.readiness??'ready';
24
+ const emptySemanticEvidence=Object.values(semanticCounts).reduce((sum,value)=>sum+value,0)===0;
25
+ const sourcePreservation=summarizeImportPreservation(imported,source);
26
+ const losses=importLosses(imported);
27
+ const lossClasses=sourceLossClasses(imported,losses);
28
+ const parserEvidence=summarizeParserEvidence(imported,source,losses);
29
+ const missingEvidence=sourceMissingEvidence({
30
+ imported,
31
+ source,
32
+ losses,
33
+ semanticCounts,
34
+ emptySemanticEvidence,
35
+ sourcePreservation,
36
+ parserEvidence
37
+ });
38
+ const nextMissingTasks=sourceMissingTasks({
39
+ source,
40
+ readiness,
41
+ semanticCounts,
42
+ emptySemanticEvidence,
43
+ sourcePreservation,
44
+ parserEvidence,
45
+ lossClasses,
46
+ losses,
47
+ missingEvidence,
48
+ candidates
49
+ });
20
50
  return {
21
51
  id:source.id??imported?.id,
22
52
  language:source.language??imported?.language??'unknown',
23
53
  sourcePath,
24
54
  sourceHash:source.sourceHash,
25
- readiness:source.readiness??imported?.metadata?.semanticMergeReadiness??candidates[0]?.readiness??'ready',
55
+ readiness,
26
56
  semanticCounts,
27
- emptySemanticEvidence:Object.values(semanticCounts).reduce((sum,value)=>sum+value,0)===0,
28
- sourcePreservation:summarizeImportPreservation(imported,source),
57
+ emptySemanticEvidence,
58
+ parserEvidence,
59
+ lossClasses,
60
+ missingEvidence,
61
+ nextMissingTasks,
62
+ semanticMergeScore:sourceSemanticMergeScore({
63
+ readiness,
64
+ emptySemanticEvidence,
65
+ sourcePreservation,
66
+ parserEvidence,
67
+ missingEvidence,
68
+ candidates
69
+ }),
70
+ sourcePreservation,
29
71
  mergeCandidates:candidates
30
72
  };
31
73
  });
32
74
  }
33
75
 
34
- export function admissionLanguages(importSummaries){
35
- const grouped=new Map();
36
- for(const entry of importSummaries){
37
- const key=entry.language??'unknown';
38
- if(!grouped.has(key)) grouped.set(key,emptyLanguageRow(key));
39
- const row=grouped.get(key);
40
- row.sourceCount+=1;
41
- row.sourcePaths.push(entry.sourcePath);
42
- row.readiness=maxSemanticMergeReadiness(row.readiness,entry.readiness);
43
- row.semanticSymbols+=entry.semanticCounts.symbols;
44
- if(entry.emptySemanticEvidence) row.emptySemanticEvidenceSources+=1;
45
- row.sourcePreservationQuality=maxPreservationQuality(row.sourcePreservationQuality,entry.sourcePreservation.quality);
46
- if(entry.sourcePreservation.stale&&entry.sourcePath) row.staleSourcePaths.push(entry.sourcePath);
47
- row.mergeCandidates+=entry.mergeCandidates.length;
48
- row.highestRisk=maxRisk(row.highestRisk,entry.mergeCandidates.reduce((current,candidate)=>maxRisk(current,candidateRisk(candidate)),'low'));
49
- }
50
- const rows=[...grouped.values()].map((row)=>({
51
- ...row,
52
- sourcePaths:uniqueStrings(row.sourcePaths.filter(Boolean)),
53
- staleSourcePaths:uniqueStrings(row.staleSourcePaths.filter(Boolean))
54
- })).sort((left,right)=>left.language.localeCompare(right.language));
55
- return {
56
- total:rows.length,
57
- byReadiness:countBy(rows.map((row)=>row.readiness)),
58
- bySourcePreservationQuality:countBy(rows.map((row)=>row.sourcePreservationQuality)),
59
- rows
60
- };
61
- }
62
-
63
76
  export function admissionSemanticEvidence(projectResult,imports,importSummaries){
64
77
  const projectIndex=projectResult?.semanticIndex??projectResult?.universalAst?.semanticIndex;
65
78
  const indexes=projectIndex?[projectIndex]:imports.flatMap((imported)=>[
@@ -155,98 +168,45 @@ export function admissionMergeCandidates(projectResult,imports,mergeCandidates,l
155
168
  projectResult?.patch,
156
169
  ...imports.map((imported)=>imported?.patch)
157
170
  ].filter((patch)=>patch?.id).map((patch)=>[patch.id,patch]));
158
- const risks=mergeCandidates.map((candidate)=>candidateRisk(candidate,patchById.get(candidate.patchId)));
171
+ const records=sortSemanticMergeCandidateAdmissionRecords(mergeCandidates.map((candidate)=>createSemanticMergeCandidateAdmissionRecord(candidate,{patch:patchById.get(candidate.patchId)})));
172
+ const recordByCandidateId=new Map(records.map((record)=>[record.candidateId,record]));
173
+ const overlaps=querySemanticMergeCandidateAdmissionOverlaps(records);
174
+ const risks=mergeCandidates.map((candidate)=>{
175
+ const record=recordByCandidateId.get(candidate.id);
176
+ return maxRisk(candidateRisk(candidate,patchById.get(candidate.patchId)),record?.projectionRisk??'low');
177
+ });
159
178
  const readiness=mergeCandidates.reduce(
160
- (current,candidate)=>maxSemanticMergeReadiness(current,candidate.readiness),
179
+ (current,candidate)=>maxSemanticMergeReadiness(current,recordByCandidateId.get(candidate.id)?.readiness??candidate.readiness),
161
180
  lossSummary?.semanticMergeReadiness??'ready'
162
181
  );
182
+ const overlapCandidateIds=uniqueStrings(overlaps.flatMap((overlap)=>overlap.candidateIds??[]));
163
183
  return {
164
184
  total:mergeCandidates.length,
165
185
  readiness,
166
186
  highestRisk:risks.reduce(maxRisk,'low'),
187
+ projectionRisk:records.reduce((current,record)=>maxRisk(current,record.projectionRisk??'unknown'),'low'),
167
188
  byRisk:countBy(risks),
168
- byReadiness:countBy(mergeCandidates.map((candidate)=>candidate.readiness??'needs-review')),
189
+ byReadiness:countBy(records.map((record)=>record.readiness??'needs-review')),
190
+ byProjectionRisk:countBy(records.map((record)=>record.projectionRisk??'unknown')),
169
191
  highRiskCandidateIds:mergeCandidates.filter((candidate,index)=>risks[index]==='high').map((candidate)=>candidate.id).filter(Boolean),
170
- reviewCandidateIds:mergeCandidates.filter((candidate)=>candidate.readiness==='needs-review').map((candidate)=>candidate.id).filter(Boolean),
171
- blockedCandidateIds:mergeCandidates.filter((candidate)=>candidate.readiness==='blocked').map((candidate)=>candidate.id).filter(Boolean),
172
- conflictKeys:uniqueStrings(mergeCandidates.flatMap((candidate)=>candidate.conflictKeys??[])),
192
+ reviewCandidateIds:records.filter((record)=>record.readiness==='needs-review'||record.admission.reviewRequired).map((record)=>record.candidateId).filter(Boolean),
193
+ blockedCandidateIds:records.filter((record)=>record.readiness==='blocked'||record.admission.action==='block').map((record)=>record.candidateId).filter(Boolean),
194
+ highProjectionRiskCandidateIds:records.filter((record)=>record.projectionRisk==='high').map((record)=>record.candidateId).filter(Boolean),
195
+ conflictKeys:uniqueStrings(records.flatMap((record)=>record.conflictKeys??[])),
196
+ readinessOrderCandidateIds:records.map((record)=>record.candidateId).filter(Boolean),
197
+ changedSemanticRegions:{
198
+ total:records.reduce((sum,record)=>sum+record.changedSemanticRegions.length,0),
199
+ byKind:countBy(records.flatMap((record)=>record.changedSemanticRegions.map((region)=>region.regionKind??'unknown'))),
200
+ conflictKeys:uniqueStrings(records.flatMap((record)=>record.changedSemanticRegions.map((region)=>region.conflictKey)))
201
+ },
202
+ overlaps:{
203
+ total:overlaps.length,
204
+ candidateIds:overlapCandidateIds,
205
+ conflictKeys:uniqueStrings(overlaps.flatMap((overlap)=>overlap.conflictKeys??[])),
206
+ sourcePaths:uniqueStrings(overlaps.map((overlap)=>overlap.sourcePath).filter(Boolean)),
207
+ pairs:overlaps
208
+ },
209
+ records,
173
210
  patchRisk:normalizeRisk(projectResult?.patch?.risk)
174
211
  };
175
212
  }
176
-
177
- export function candidateRisk(candidate,patch){
178
- return normalizeRisk(candidate?.risk)??normalizeRisk(patch?.risk)??readinessRisk(candidate?.readiness);
179
- }
180
-
181
- export function maxRisk(left,right){
182
- return riskRank[left]>=riskRank[right]?left:right;
183
- }
184
-
185
- export function maxPreservationQuality(left,right){
186
- return preservationRank[left]>=preservationRank[right]?left:right;
187
- }
188
-
189
- function compactAdmissionSource(imported,index){
190
- const semanticIndex=imported?.semanticIndex??imported?.universalAst?.semanticIndex;
191
- const nativeAst=imported?.nativeAst??imported?.nativeSource?.ast;
192
- const sourceMaps=imported?.sourceMaps??imported?.universalAst?.sourceMaps??[];
193
- return {
194
- id:imported?.id??`import_${index+1}`,
195
- language:imported?.language??imported?.nativeSource?.language??nativeAst?.language,
196
- sourcePath:imported?.sourcePath??imported?.nativeSource?.sourcePath??nativeAst?.sourcePath,
197
- sourceHash:imported?.nativeSource?.sourceHash??nativeAst?.sourceHash,
198
- parser:nativeAst?.parser??imported?.nativeSource?.parser,
199
- sourceMapIds:sourceMaps.map((sourceMap)=>sourceMap.id).filter(Boolean),
200
- sourceMapMappings:sourceMaps.reduce((sum,sourceMap)=>sum+(sourceMap.mappings?.length??0),0),
201
- symbolCount:semanticIndex?.symbols?.length??0,
202
- lossCount:imported?.losses?.length??nativeAst?.losses?.length??0,
203
- evidenceCount:imported?.evidence?.length??0,
204
- readiness:imported?.metadata?.semanticMergeReadiness??imported?.mergeCandidates?.[0]?.readiness
205
- };
206
- }
207
-
208
- function summarizeImportPreservation(imported,source){
209
- const nativeAst=imported?.nativeAst??imported?.nativeSource?.ast;
210
- const record=imported?.metadata?.sourcePreservation
211
- ??imported?.nativeSource?.metadata?.sourcePreservation
212
- ??nativeAst?.metadata?.sourcePreservation
213
- ??imported?.universalAst?.metadata?.sourcePreservation;
214
- const sourceHash=source?.sourceHash??imported?.nativeSource?.sourceHash??nativeAst?.sourceHash;
215
- const sourcePreservationLosses=(imported?.losses??nativeAst?.losses??[]).filter((loss)=>loss.kind==='sourcePreservation');
216
- const stale=imported?.metadata?.sourceHashVerified===false
217
- ||imported?.nativeSource?.metadata?.sourceHashVerified===false
218
- ||nativeAst?.metadata?.sourceHashVerified===false
219
- ||record?.metadata?.sourceHashVerified===false
220
- ||Boolean(record?.sourceHash&&sourceHash&&record.sourceHash!==sourceHash);
221
- const missing=!record;
222
- const truncated=record?.summary?.truncated===true;
223
- const exactSourceAvailable=record?.summary?.exactSourceAvailable===true;
224
- const quality=stale?'stale':missing?'missing':truncated||!exactSourceAvailable||sourcePreservationLosses.length?'lossy':'exact';
225
- return {quality,missing,stale,truncated,exactSourceAvailable,lossCount:sourcePreservationLosses.length,id:record?.id};
226
- }
227
-
228
- function emptyLanguageRow(language){
229
- return {
230
- language,
231
- sourceCount:0,
232
- sourcePaths:[],
233
- readiness:'ready',
234
- semanticSymbols:0,
235
- emptySemanticEvidenceSources:0,
236
- sourcePreservationQuality:'exact',
237
- staleSourcePaths:[],
238
- mergeCandidates:0,
239
- highestRisk:'low'
240
- };
241
- }
242
-
243
- function readinessRisk(readiness){
244
- if(readiness==='blocked') return 'high';
245
- if(readiness==='needs-review'||readiness==='ready-with-losses') return 'medium';
246
- return 'low';
247
- }
248
-
249
- function normalizeRisk(value){
250
- const risk=String(value??'').toLowerCase();
251
- return Object.prototype.hasOwnProperty.call(riskRank,risk)?risk:undefined;
252
- }
@@ -0,0 +1,239 @@
1
+ import{maxSemanticMergeReadiness,uniqueStrings}from'../../native-import-utils.js';
2
+ import{nativeImportCategoryForLossKind}from'./nativeImportCategoryForLossKind.js';
3
+ import{candidateRisk,clampScore,maxRisk,maxTaskPriority,readinessSort,sourceScoreByReadiness,taskPriorityForReadiness,taskPriorityRank}from'./projectImportAdmissionRanks.js';
4
+
5
+ export function sourceMissingEvidence(input){
6
+ const lossSummary=input.imported?.metadata?.nativeImportLossSummary
7
+ ??input.imported?.nativeSource?.metadata?.nativeImportLossSummary
8
+ ??input.imported?.nativeAst?.metadata?.nativeImportLossSummary
9
+ ??input.imported?.universalAst?.metadata?.nativeImportLossSummary;
10
+ const sourcePath=input.source?.sourcePath??input.imported?.sourcePath;
11
+ const language=input.source?.language??input.imported?.language??'unknown';
12
+ const hints=[];
13
+ for(const missing of lossSummary?.featureEvidence?.missingRequiredEvidence??[]){
14
+ const lossClass=nativeImportCategoryForLossKind(missing.kind??missing.policyKind);
15
+ hints.push(evidenceHint({
16
+ evidenceKey:missing.evidenceKey,
17
+ task:`Add ${missing.evidenceKey} evidence for ${missing.kind??missing.policyKind}.`,
18
+ sourcePath,
19
+ language,
20
+ lossId:missing.lossId,
21
+ lossKind:missing.kind??missing.policyKind,
22
+ lossClass,
23
+ readiness:'needs-review'
24
+ }));
25
+ }
26
+ if(input.emptySemanticEvidence){
27
+ hints.push(evidenceHint({
28
+ evidenceKey:'semantic-index',
29
+ task:'Import semantic symbols for sources with empty semantic indexes.',
30
+ sourcePath,
31
+ language,
32
+ lossKind:'partialSemanticIndex',
33
+ lossClass:'partialSemanticIndex',
34
+ readiness:'blocked'
35
+ }));
36
+ }
37
+ if(input.parserEvidence.missing){
38
+ hints.push(evidenceHint({
39
+ evidenceKey:'parser-result',
40
+ task:'Attach a native parser AST instead of missing-parser fallback evidence.',
41
+ sourcePath,
42
+ language,
43
+ lossKind:'unsupportedSyntax',
44
+ lossClass:'parserDiagnostics',
45
+ readiness:'blocked'
46
+ }));
47
+ }
48
+ if(input.sourcePreservation.stale){
49
+ hints.push(evidenceHint({
50
+ evidenceKey:'source-hash-verification',
51
+ task:'Refresh source hashes or re-import the stale source text.',
52
+ sourcePath,
53
+ language,
54
+ lossKind:'sourcePreservation',
55
+ lossClass:'sourcePreservation',
56
+ readiness:'blocked'
57
+ }));
58
+ }else if(input.sourcePreservation.quality==='missing'||input.sourcePreservation.quality==='lossy'){
59
+ hints.push(evidenceHint({
60
+ evidenceKey:'source-preservation',
61
+ task:'Attach exact source-preservation evidence for lossy or missing source text.',
62
+ sourcePath,
63
+ language,
64
+ lossKind:'sourcePreservation',
65
+ lossClass:'sourcePreservation',
66
+ readiness:'needs-review'
67
+ }));
68
+ }
69
+ return uniqueEvidenceHints(hints);
70
+ }
71
+
72
+ export function sourceMissingTasks(input){
73
+ const sourcePath=input.source?.sourcePath;
74
+ const language=input.source?.language??'unknown';
75
+ const tasks=[];
76
+ for(const hint of input.missingEvidence??[]){
77
+ tasks.push(taskHint({
78
+ id:`evidence:${hint.evidenceKey}`,
79
+ task:hint.task,
80
+ reason:'missing-evidence',
81
+ priority:taskPriorityForReadiness(hint.readiness),
82
+ readiness:hint.readiness,
83
+ sourcePath,
84
+ language,
85
+ lossClasses:hint.lossClasses,
86
+ evidenceKeys:[hint.evidenceKey]
87
+ }));
88
+ }
89
+ for(const lossClass of input.lossClasses??[]){
90
+ const task=taskForLossClass(lossClass);
91
+ if(task){
92
+ tasks.push(taskHint({
93
+ ...task,
94
+ sourcePath,
95
+ language,
96
+ readiness:input.readiness,
97
+ lossClasses:[lossClass]
98
+ }));
99
+ }
100
+ }
101
+ const reviewCandidates=(input.candidates??[]).filter((candidate)=>
102
+ candidate.readiness==='needs-review'
103
+ || candidate.readiness==='blocked'
104
+ || candidateRisk(candidate)!=='low'
105
+ );
106
+ if(reviewCandidates.length){
107
+ tasks.push(taskHint({
108
+ id:'semantic-merge-review',
109
+ task:'Review semantic merge candidates that carry elevated risk or review readiness.',
110
+ reason:'merge-candidate-risk',
111
+ priority:reviewCandidates.some((candidate)=>candidateRisk(candidate)==='high'||candidate.readiness==='blocked')?'critical':'high',
112
+ readiness:reviewCandidates.reduce((current,candidate)=>maxSemanticMergeReadiness(current,candidate.readiness??'ready'),input.readiness),
113
+ sourcePath,
114
+ language,
115
+ lossClasses:[],
116
+ evidenceKeys:[]
117
+ }));
118
+ }
119
+ if(!tasks.length&&input.readiness!=='ready'){
120
+ tasks.push(taskHint({
121
+ id:'native-import-review',
122
+ task:'Review native import losses before admitting this source.',
123
+ reason:'readiness-review',
124
+ priority:taskPriorityForReadiness(input.readiness),
125
+ readiness:input.readiness,
126
+ sourcePath,
127
+ language,
128
+ lossClasses:input.lossClasses,
129
+ evidenceKeys:[]
130
+ }));
131
+ }
132
+ return uniqueTaskHints(tasks);
133
+ }
134
+
135
+ function taskForLossClass(lossClass){
136
+ if(lossClass==='exactAstImport'||lossClass==='none') return undefined;
137
+ if(lossClass==='partialSemanticIndex'){
138
+ return {id:'semantic-index-completeness',task:'Complete semantic index coverage for partial imports.',reason:'partial-semantic-index',priority:'high',evidenceKeys:['semantic-index']};
139
+ }
140
+ if(lossClass==='declarationsOnly'||lossClass==='opaqueBodies'){
141
+ return {id:'body-semantics',task:'Import body-level semantic evidence beyond declaration-only coverage.',reason:'declaration-only-or-opaque-body',priority:'normal',evidenceKeys:['body-semantics']};
142
+ }
143
+ if(lossClass==='sourceMapApproximation'){
144
+ return {id:'source-map-ranges',task:'Attach precise source-map ranges for approximated native mappings.',reason:'source-map-approximation',priority:'normal',evidenceKeys:['source-map']};
145
+ }
146
+ if(lossClass==='sourcePreservation'||lossClass==='commentsTrivia'){
147
+ return {id:'source-preservation',task:'Attach exact source-preservation evidence for lossy or missing source text.',reason:'source-preservation-loss',priority:'high',evidenceKeys:['source-preservation']};
148
+ }
149
+ if(lossClass==='parserDiagnostics'||lossClass==='unsupportedSyntax'){
150
+ return {id:'parser-evidence',task:'Resolve parser diagnostics or attach exact parser evidence.',reason:'parser-loss',priority:'blocker',evidenceKeys:['parser-result']};
151
+ }
152
+ if(lossClass==='unsupportedSyntax'||lossClass==='unsupportedSemantic'){
153
+ return {id:'unsupported-language-feature',task:'Add host semantic evidence for unsupported native language features.',reason:'unsupported-feature-loss',priority:'high',evidenceKeys:['host-semantics']};
154
+ }
155
+ if(lossClass==='targetProjectionLoss'||lossClass==='missingAdapter'||lossClass==='unsupportedTargetFeatures'||lossClass==='nativeSourceStubs'){
156
+ return {id:'target-projection-coverage',task:'Add target projection adapter coverage or mark unsupported target features.',reason:'target-projection-loss',priority:'high',evidenceKeys:['target-projection']};
157
+ }
158
+ if(lossClass==='macroExpansion'||lossClass==='preprocessor'||lossClass==='conditionalCompilation'||lossClass==='metaprogramming'||lossClass==='overloadTypeInference'||lossClass==='generatedCode'){
159
+ return {id:`host-evidence:${lossClass}`,task:`Attach host ${lossClass} evidence for native import losses.`,reason:'host-evidence-loss',priority:'high',evidenceKeys:[lossClass]};
160
+ }
161
+ return {id:`loss-class:${lossClass}`,task:`Review ${lossClass} native import loss evidence.`,reason:'native-loss-class',priority:'normal',evidenceKeys:[lossClass]};
162
+ }
163
+
164
+ function evidenceHint(input){
165
+ return {
166
+ evidenceKey:input.evidenceKey,
167
+ task:input.task,
168
+ count:1,
169
+ sourcePaths:uniqueStrings([input.sourcePath].filter(Boolean)),
170
+ languages:uniqueStrings([input.language].filter(Boolean)),
171
+ lossIds:uniqueStrings([input.lossId].filter(Boolean)),
172
+ lossKinds:uniqueStrings([input.lossKind].filter(Boolean)),
173
+ lossClasses:uniqueStrings([input.lossClass].filter(Boolean)),
174
+ readiness:input.readiness??'needs-review'
175
+ };
176
+ }
177
+
178
+ function taskHint(input){
179
+ return {
180
+ id:input.id,
181
+ task:input.task,
182
+ reason:input.reason,
183
+ priority:input.priority??'normal',
184
+ readiness:input.readiness??'needs-review',
185
+ count:1,
186
+ sourcePaths:uniqueStrings([input.sourcePath].filter(Boolean)),
187
+ languages:uniqueStrings([input.language].filter(Boolean)),
188
+ lossClasses:uniqueStrings(input.lossClasses??[]),
189
+ evidenceKeys:uniqueStrings(input.evidenceKeys??[])
190
+ };
191
+ }
192
+
193
+ function uniqueEvidenceHints(hints){
194
+ const grouped=new Map();
195
+ for(const hint of hints){
196
+ const key=`${hint.evidenceKey}\u0000${hint.lossIds.join('|')}\u0000${hint.sourcePaths.join('|')}`;
197
+ if(!grouped.has(key)) grouped.set(key,hint);
198
+ }
199
+ return [...grouped.values()];
200
+ }
201
+
202
+ function uniqueTaskHints(tasks){
203
+ const grouped=new Map();
204
+ for(const task of tasks){
205
+ const key=task.id??task.task;
206
+ if(!grouped.has(key)){
207
+ grouped.set(key,{...task});
208
+ continue;
209
+ }
210
+ const entry=grouped.get(key);
211
+ entry.count+=task.count??1;
212
+ entry.priority=maxTaskPriority(entry.priority,task.priority);
213
+ entry.readiness=maxSemanticMergeReadiness(entry.readiness,task.readiness);
214
+ entry.sourcePaths=uniqueStrings([...entry.sourcePaths,...task.sourcePaths]);
215
+ entry.languages=uniqueStrings([...entry.languages,...task.languages]);
216
+ entry.lossClasses=uniqueStrings([...entry.lossClasses,...task.lossClasses]);
217
+ entry.evidenceKeys=uniqueStrings([...entry.evidenceKeys,...task.evidenceKeys]);
218
+ }
219
+ return [...grouped.values()].sort((left,right)=>
220
+ taskPriorityRank[right.priority]-taskPriorityRank[left.priority]
221
+ || readinessSort(right.readiness)-readinessSort(left.readiness)
222
+ || String(left.task).localeCompare(String(right.task))
223
+ );
224
+ }
225
+
226
+ export function sourceSemanticMergeScore(input){
227
+ const readinessScore=sourceScoreByReadiness[input.readiness]??sourceScoreByReadiness['needs-review'];
228
+ const preservationPenalty=input.sourcePreservation.quality==='stale'?35
229
+ :input.sourcePreservation.quality==='missing'?26
230
+ :input.sourcePreservation.quality==='lossy'?12
231
+ :input.sourcePreservation.quality==='empty'?18
232
+ :0;
233
+ const risk=(input.candidates??[]).reduce((current,candidate)=>maxRisk(current,candidateRisk(candidate)),'low');
234
+ const riskPenalty=risk==='high'?24:risk==='medium'?10:risk==='unknown'?8:0;
235
+ const parserPenalty=input.parserEvidence.missing?25:Math.min(16,(input.parserEvidence.diagnosticCount??0)*4);
236
+ const missingEvidencePenalty=Math.min(24,(input.missingEvidence?.length??0)*6);
237
+ const semanticBonus=input.emptySemanticEvidence?-25:5;
238
+ return clampScore(readinessScore+semanticBonus-preservationPenalty-riskPenalty-parserPenalty-missingEvidencePenalty);
239
+ }
@@ -0,0 +1,151 @@
1
+ import{hashSemanticValue}from'@shapeshift-labs/frontier-lang-kernel';
2
+ import{idFragment,normalizeSemanticMergeReadiness,uniqueStrings as uniqueRawStrings}from'../../native-import-utils.js';
3
+
4
+ export function normalizeSources(input,imported){
5
+ const entries=[...(array(input.sources)),...(array(input.sourceRefs)),...(array(input.source)),...(array(imported))];
6
+ if(entries.length===0&&(input.sourcePath||input.sourceHash||input.baseHash||input.targetHash||input.sourceId||input.importId)){
7
+ entries.push(input);
8
+ }
9
+ return entries.map((source,index)=>compactRecord({
10
+ id:source?.id??source?.sourceId,
11
+ importId:source?.importId??source?.importResultId??(source?.kind==='frontier.lang.importResult'?source.id:undefined),
12
+ language:source?.language??input.language,
13
+ sourcePath:source?.sourcePath??source?.path??input.sourcePath,
14
+ sourceHash:source?.sourceHash??source?.hash,
15
+ baseHash:source?.baseHash??source?.beforeHash??input.baseHash??input.beforeHash,
16
+ targetHash:source?.targetHash??source?.afterHash??input.targetHash??input.afterHash,
17
+ metadata:source?.metadata,
18
+ ordinal:index
19
+ })).filter((source)=>source.id||source.importId||source.sourcePath||source.sourceHash||source.baseHash||source.targetHash);
20
+ }
21
+
22
+ export function normalizeOwnershipRegions(regions){
23
+ const seen=new Set();
24
+ const normalized=[];
25
+ for(const region of regions){
26
+ const key=firstString(region?.key,region?.ownershipKey,region?.conflictKey,region?.id);
27
+ if(!key||seen.has(key))continue;
28
+ seen.add(key);
29
+ normalized.push(compactRecord({
30
+ id:region?.id,
31
+ key,
32
+ regionKind:region?.regionKind??region?.ownershipRegionKind,
33
+ granularity:region?.granularity??'semantic',
34
+ language:region?.language,
35
+ sourcePath:region?.sourcePath,
36
+ sourceHash:region?.sourceHash,
37
+ symbolId:region?.symbolId,
38
+ symbolName:region?.symbolName??region?.name,
39
+ sourceSpan:region?.sourceSpan??region?.span,
40
+ metadata:region?.metadata
41
+ }));
42
+ }
43
+ return normalized;
44
+ }
45
+
46
+ export function normalizeSemanticCandidates(candidates){
47
+ return candidates.filter(Boolean).map((candidate,index)=>compactRecord({
48
+ id:candidate.id??candidate.candidateId??`semantic_candidate_${index+1}`,
49
+ importResultId:candidate.importResultId,
50
+ patchId:candidate.patchId,
51
+ sourcePath:candidate.sourcePath,
52
+ baseHash:candidate.baseHash,
53
+ targetHash:candidate.targetHash,
54
+ readiness:normalizeSemanticMergeReadiness(candidate.readiness)??candidate.readiness,
55
+ conflictKeys:uniqueStrings([...(strings(candidate.conflictKeys)),...(array(candidate.touchedSymbols)).map((entry)=>entry?.conflictKey),...(array(candidate.touchedSemanticNodes)).map((entry)=>entry?.conflictKey),...(array(candidate.nativeSpans)).map((entry)=>entry?.conflictKey),...(array(candidate.conflictClasses??candidate.metadata?.conflictClasses)).flatMap((entry)=>entry?.conflictKeys??[]),...(candidate.conflictSummary?.conflictKeys??candidate.metadata?.conflictSummary?.conflictKeys??[])]),
56
+ ownershipKeys:uniqueStrings([...(strings(candidate.ownershipKeys)),...(strings(candidate.regionKeys))]),
57
+ evidenceIds:uniqueStrings([...(strings(candidate.evidenceIds)),...(array(candidate.evidence)).map((record)=>record?.id),...(array(candidate.conflictClasses??candidate.metadata?.conflictClasses)).flatMap((entry)=>entry?.evidenceIds??[])]),
58
+ proofIds:uniqueStrings([...(strings(candidate.proofIds)),...(array(candidate.evidence)).filter((record)=>record?.kind==='proof').map((record)=>record?.id)]),
59
+ replayIds:uniqueStrings([...(strings(candidate.replayIds)),...(array(candidate.evidence)).filter((record)=>record?.kind==='replay').map((record)=>record?.id)]),
60
+ metadata:compactRecord({risk:candidate.risk,reasons:candidate.reasons,...candidate.metadata})
61
+ }));
62
+ }
63
+
64
+ export function normalizeSemanticClaims(claims,defaults){
65
+ return claims.filter(Boolean).map((claim,index)=>{
66
+ const text=typeof claim==='string'?claim:undefined;
67
+ const source=typeof claim==='string'?{}:claim;
68
+ const record=compactRecord({
69
+ kind:'frontier.lang.semanticHistoryClaim',
70
+ version:1,
71
+ claimKind:source.claimKind??source.semanticClaimKind??source.kind??defaults.claimKind,
72
+ status:source.status??defaults.status,
73
+ subject:firstString(source.subject,source.symbolId,source.semanticNodeId,source.conflictKey,source.key,text),
74
+ predicate:source.predicate??source.relation,
75
+ object:source.object??source.value??source.expected,
76
+ text,
77
+ language:source.language??defaults.language,
78
+ sourcePath:source.sourcePath??defaults.sourcePath,
79
+ sourceHash:source.sourceHash??defaults.sourceHash,
80
+ baseHash:source.baseHash??defaults.baseHash,
81
+ targetHash:source.targetHash??defaults.targetHash,
82
+ conflictKeys:uniqueStrings([...(strings(source.conflictKeys)),source.conflictKey,source.key]),
83
+ evidenceIds:uniqueStrings(source.evidenceIds),
84
+ proofIds:uniqueStrings(source.proofIds),
85
+ replayIds:uniqueStrings(source.replayIds),
86
+ actor:normalizeActor(source.actor)??defaults.actor,
87
+ recordSource:normalizeRecordSource(source.recordSource??source.historySource??defaults.recordSource,defaults),
88
+ metadata:source.metadata
89
+ });
90
+ return withStableSubrecordIdentity(defaults.prefix??'semantic_claim',record,source,index);
91
+ });
92
+ }
93
+
94
+ export function semanticClaimsByStatus(input,status){
95
+ return [...array(input.semanticClaims),...array(input.claims)].filter((claim)=>{
96
+ const claimStatus=String(claim?.status??'').toLowerCase();
97
+ if(claimStatus===status)return true;
98
+ if(status==='accepted'&&claim?.accepted===true)return true;
99
+ if(status==='rejected'&&(claim?.rejected===true||claim?.status===false))return true;
100
+ return false;
101
+ });
102
+ }
103
+ function withStableSubrecordIdentity(prefix,record,source,index){
104
+ const hash=source.hash??hashSemanticValue(record);
105
+ return{
106
+ ...record,
107
+ id:source.id??`${prefix}_${idFragment(firstString(record.subject,record.evidenceId,record.proofId,record.patchId,record.decision,hash,index+1))}`,
108
+ hash
109
+ };
110
+ }
111
+
112
+ function normalizeActor(value){
113
+ const actor=typeof value==='string'?{id:value}:value;
114
+ const normalized=compactRecord({
115
+ id:actor?.id??actor?.actorId,
116
+ kind:actor?.kind??actor?.actorKind??actor?.type,
117
+ role:actor?.role,
118
+ displayName:actor?.displayName??actor?.name,
119
+ runId:actor?.runId,
120
+ lane:actor?.lane,
121
+ taskId:actor?.taskId,
122
+ metadata:actor?.metadata
123
+ });
124
+ return nonEmptyRecord(normalized);
125
+ }
126
+
127
+ function normalizeRecordSource(value,context={}){
128
+ const source=typeof value==='string'?{id:value}:value;
129
+ const normalized=compactRecord({
130
+ id:source?.id??source?.sourceId??context.recordSourceId??context.historySourceId,
131
+ sourceId:source?.sourceId??source?.id??context.recordSourceId??context.historySourceId,
132
+ sourceKind:source?.sourceKind??source?.kind??source?.type,
133
+ sourcePath:source?.sourcePath??source?.path,
134
+ sourceHash:source?.sourceHash??source?.hash,
135
+ href:source?.href??source?.url,
136
+ importId:source?.importId??context.importId??context.importResultId,
137
+ runId:source?.runId??context.runId,
138
+ jobId:source?.jobId??context.jobId,
139
+ lane:source?.lane??context.lane,
140
+ taskId:source?.taskId??context.taskId,
141
+ metadata:source?.metadata
142
+ });
143
+ return nonEmptyRecord(normalized);
144
+ }
145
+
146
+ function array(value){if(value===undefined||value===null)return[];return Array.isArray(value)?value:[value];}
147
+ function strings(value){return array(value).map((entry)=>String(entry??'')).filter(Boolean);}
148
+ function uniqueStrings(values){return uniqueRawStrings((values??[]).filter((entry)=>entry!==undefined&&entry!==null&&String(entry)!==''));}
149
+ function firstString(...values){return values.map((value)=>value===undefined||value===null?'':String(value)).find(Boolean);}
150
+ function compactRecord(value){return Object.fromEntries(Object.entries(value??{}).filter(([,entry])=>entry!==undefined&&(!Array.isArray(entry)||entry.length>0)));}
151
+ function nonEmptyRecord(value){return Object.keys(value??{}).length?value:undefined;}