@shapeshift-labs/frontier-lang-compiler 0.2.10 → 0.2.12

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/index.d.ts CHANGED
@@ -112,11 +112,23 @@ export type NativeImportKnownLossKind =
112
112
  | 'parserDiagnostic'
113
113
  | 'unsupportedSyntax'
114
114
  | 'unsupportedSemantic'
115
+ | 'unverifiedNativeAst'
115
116
  | 'partialSemanticIndex'
116
117
  | 'sourceMapApproximation'
117
118
  | 'targetProjectionLoss'
118
119
  | string;
119
120
 
121
+ export type NativeImportRegionTaxonomyKind =
122
+ | 'symbol'
123
+ | 'declaration'
124
+ | 'import'
125
+ | 'body'
126
+ | 'call'
127
+ | 'type'
128
+ | 'effect'
129
+ | 'generatedOutput'
130
+ | string;
131
+
120
132
  export interface NativeImportLossSummaryOptions {
121
133
  readonly exactAst?: boolean;
122
134
  readonly evidence?: readonly EvidenceRecord[];
@@ -216,6 +228,167 @@ export interface NativeImportCoverageMatrixOptions {
216
228
  readonly generatedAt?: number;
217
229
  }
218
230
 
231
+ export interface NativeImportContractSource {
232
+ readonly id: string;
233
+ readonly language?: FrontierSourceLanguage | string;
234
+ readonly sourcePath?: string;
235
+ readonly sourceHash?: string;
236
+ readonly parser?: string;
237
+ readonly nativeSourceId?: string;
238
+ readonly nativeAstId?: string;
239
+ readonly semanticIndexId?: string;
240
+ readonly universalAstId?: string;
241
+ readonly patchId?: string;
242
+ readonly sourceMapIds: readonly string[];
243
+ readonly sourceMapMappings: number;
244
+ readonly symbolCount: number;
245
+ readonly lossCount: number;
246
+ readonly evidenceCount: number;
247
+ readonly readiness?: SemanticMergeReadiness;
248
+ }
249
+
250
+ export interface NativeImportSourcePreservationRecordSummary {
251
+ readonly id?: string;
252
+ readonly language?: FrontierSourceLanguage | string;
253
+ readonly sourcePath?: string;
254
+ readonly sourceHash?: string;
255
+ readonly sourceBytes?: number;
256
+ readonly lineCount?: number;
257
+ readonly newline?: string;
258
+ readonly encoding?: string;
259
+ readonly exactSourceAvailable: boolean;
260
+ readonly tokens: number;
261
+ readonly trivia: number;
262
+ readonly directives: number;
263
+ readonly comments: number;
264
+ readonly whitespace: number;
265
+ readonly truncated: boolean;
266
+ }
267
+
268
+ export interface NativeImportSourcePreservationContract {
269
+ readonly total: number;
270
+ readonly ids: readonly string[];
271
+ readonly sourcePaths: readonly string[];
272
+ readonly sourceHashes: readonly string[];
273
+ readonly exactSourceAvailable: number;
274
+ readonly sourceBytes: number;
275
+ readonly lineCount: number;
276
+ readonly tokens: number;
277
+ readonly trivia: number;
278
+ readonly directives: number;
279
+ readonly comments: number;
280
+ readonly whitespace: number;
281
+ readonly truncated: boolean;
282
+ readonly records: readonly NativeImportSourcePreservationRecordSummary[];
283
+ }
284
+
285
+ export interface NativeImportAdapterCoverageRecordSummary {
286
+ readonly adapterId?: string;
287
+ readonly adapterVersion?: string;
288
+ readonly parser?: string;
289
+ readonly capabilities: readonly string[];
290
+ readonly supportedExtensions: readonly string[];
291
+ readonly exactness?: NativeImporterAdapterExactness;
292
+ readonly exactAst: boolean;
293
+ readonly tokens: boolean;
294
+ readonly trivia: boolean;
295
+ readonly diagnostics: boolean;
296
+ readonly sourceRanges: boolean;
297
+ readonly generatedRanges: boolean;
298
+ readonly semanticCoverage?: NativeImporterAdapterSemanticCoverage;
299
+ readonly observed?: NativeImporterAdapterCoverageObserved;
300
+ readonly notes: readonly string[];
301
+ }
302
+
303
+ export interface NativeImportAdapterCoverageContract {
304
+ readonly total: number;
305
+ readonly adapterIds: readonly string[];
306
+ readonly parsers: readonly string[];
307
+ readonly exactness: readonly string[];
308
+ readonly exactAst: number;
309
+ readonly tokens: number;
310
+ readonly trivia: number;
311
+ readonly diagnostics: number;
312
+ readonly sourceRanges: number;
313
+ readonly generatedRanges: number;
314
+ readonly semanticCoverageLevels: readonly string[];
315
+ readonly observed: NativeImporterAdapterCoverageObserved;
316
+ readonly records: readonly NativeImportAdapterCoverageRecordSummary[];
317
+ }
318
+
319
+ export interface NativeImportRegionSummary {
320
+ readonly total: number;
321
+ readonly ids: readonly string[];
322
+ readonly keys: readonly string[];
323
+ readonly sourcePaths: readonly string[];
324
+ readonly byKind: Readonly<Record<string, number>>;
325
+ readonly byGranularity: Readonly<Record<string, number>>;
326
+ readonly byPrecision: Readonly<Record<string, number>>;
327
+ readonly byLanguage: Readonly<Record<string, number>>;
328
+ readonly symbolIds: readonly string[];
329
+ readonly taxonomy: SemanticImportRegionTaxonomySummary;
330
+ }
331
+
332
+ export interface NativeImportSourceMapSummary {
333
+ readonly total: number;
334
+ readonly ids: readonly string[];
335
+ readonly mappingCount: number;
336
+ readonly sourcePaths: readonly string[];
337
+ readonly targetPaths: readonly string[];
338
+ readonly byPrecision: Readonly<Record<string, number>>;
339
+ readonly sourceRangeMappings: number;
340
+ readonly generatedRangeMappings: number;
341
+ }
342
+
343
+ export interface NativeImportReadinessContract {
344
+ readonly semanticMergeReadiness: SemanticMergeReadiness;
345
+ readonly severityReadiness: SemanticMergeReadiness;
346
+ readonly reasons: readonly string[];
347
+ readonly failedEvidenceIds: readonly string[];
348
+ readonly blockingLossIds: readonly string[];
349
+ readonly reviewLossIds: readonly string[];
350
+ readonly informationalLossIds: readonly string[];
351
+ }
352
+
353
+ export interface NativeImportResultContract {
354
+ readonly kind: 'frontier.lang.nativeImportResultContract';
355
+ readonly version: 1;
356
+ readonly importResultId?: string;
357
+ readonly language?: FrontierSourceLanguage | 'mixed' | string;
358
+ readonly sourcePath?: string;
359
+ readonly sourceHash?: string;
360
+ readonly sourceCount: number;
361
+ readonly sources: readonly NativeImportContractSource[];
362
+ readonly ids: {
363
+ readonly nativeSourceId?: string;
364
+ readonly nativeAstId?: string;
365
+ readonly semanticIndexId?: string;
366
+ readonly universalAstId?: string;
367
+ readonly patchId?: string;
368
+ readonly sourceMapIds: readonly string[];
369
+ readonly semanticSidecarIds: readonly string[];
370
+ };
371
+ readonly sourcePreservation: NativeImportSourcePreservationContract;
372
+ readonly adapterCoverage: NativeImportAdapterCoverageContract;
373
+ readonly lossSummary: NativeImportLossSummary;
374
+ readonly regions: NativeImportRegionSummary;
375
+ readonly sourceMaps: NativeImportSourceMapSummary;
376
+ readonly readiness: NativeImportReadinessContract;
377
+ readonly evidence: {
378
+ readonly total: number;
379
+ readonly failed: readonly string[];
380
+ readonly ids: readonly string[];
381
+ };
382
+ readonly metadata: Record<string, unknown>;
383
+ }
384
+
385
+ export interface NativeImportResultContractOptions extends SemanticImportSidecarOptions {
386
+ readonly lossSummary?: NativeImportLossSummary;
387
+ readonly semanticSidecarIds?: readonly string[] | string;
388
+ readonly sidecarIds?: readonly string[] | string;
389
+ readonly sidecarId?: string;
390
+ }
391
+
219
392
  export type NativeSourceTokenKind =
220
393
  | 'identifier'
221
394
  | 'keyword'
@@ -295,8 +468,10 @@ export interface CreateNativeSourcePreservationOptions {
295
468
  export interface SemanticImportOwnershipRegion {
296
469
  readonly id: string;
297
470
  readonly key: string;
471
+ readonly regionKind?: NativeImportRegionTaxonomyKind;
298
472
  readonly granularity: 'symbol' | string;
299
473
  readonly language?: FrontierSourceLanguage | string;
474
+ readonly documentId?: string;
300
475
  readonly sourcePath?: string;
301
476
  readonly sourceHash?: string;
302
477
  readonly symbolId?: string;
@@ -306,6 +481,7 @@ export interface SemanticImportOwnershipRegion {
306
481
  readonly sourceSpan?: SourceSpan;
307
482
  readonly precision?: 'exact' | 'declaration' | 'line' | 'estimated' | 'unknown' | string;
308
483
  readonly mergePolicy?: string;
484
+ readonly metadata?: Record<string, unknown>;
309
485
  }
310
486
 
311
487
  export interface SemanticImportSidecarSymbol {
@@ -320,9 +496,18 @@ export interface SemanticImportSidecarSymbol {
320
496
  readonly signatureHash?: string;
321
497
  readonly ownershipRegionId: string;
322
498
  readonly ownershipKey: string;
499
+ readonly ownershipRegionKind?: NativeImportRegionTaxonomyKind;
323
500
  readonly readiness: SemanticMergeReadiness;
324
501
  }
325
502
 
503
+ export interface SemanticImportRegionTaxonomySummary {
504
+ readonly kinds: readonly NativeImportRegionTaxonomyKind[];
505
+ readonly presentKinds: readonly NativeImportRegionTaxonomyKind[];
506
+ readonly byKind: Readonly<Record<string, number>>;
507
+ readonly keys: readonly string[];
508
+ readonly keysByKind: Readonly<Record<string, readonly string[]>>;
509
+ }
510
+
326
511
  export interface SemanticImportPatchHint {
327
512
  readonly id: string;
328
513
  readonly kind: 'source-region-patch' | string;
@@ -356,6 +541,7 @@ export interface SemanticImportSidecarImportEntry {
356
541
  readonly sourceMapMappingCount: number;
357
542
  readonly readiness: SemanticMergeReadiness;
358
543
  readonly emptySemanticIndex: boolean;
544
+ readonly regionTaxonomy?: SemanticImportRegionTaxonomySummary;
359
545
  }
360
546
 
361
547
  export interface SemanticImportSidecar {
@@ -389,6 +575,7 @@ export interface SemanticImportSidecar {
389
575
  readonly blockingLossIds: readonly string[];
390
576
  readonly reviewLossIds: readonly string[];
391
577
  };
578
+ readonly regionTaxonomy: SemanticImportRegionTaxonomySummary;
392
579
  readonly evidence: {
393
580
  readonly total: number;
394
581
  readonly failed: readonly string[];
@@ -398,6 +585,7 @@ export interface SemanticImportSidecar {
398
585
  readonly imports: number;
399
586
  readonly symbols: number;
400
587
  readonly ownershipRegions: number;
588
+ readonly regionKinds: number;
401
589
  readonly sourceMapMappings: number;
402
590
  readonly readiness: SemanticMergeReadiness;
403
591
  readonly emptySemanticIndex: boolean;
@@ -511,6 +699,7 @@ export interface ImportNativeSourceOptions {
511
699
  readonly sourceMaps?: readonly SourceMapRecord[];
512
700
  readonly universalAstId?: string;
513
701
  readonly universalAstMetadata?: Record<string, unknown>;
702
+ readonly exactAst?: boolean;
514
703
  readonly metadata?: Record<string, unknown>;
515
704
  }
516
705
 
@@ -724,9 +913,65 @@ export interface NativeSourceProjectionResult {
724
913
  readonly metadata: Record<string, unknown>;
725
914
  }
726
915
 
916
+ export type NativeImportRoundtripReadinessStatus =
917
+ | 'exact'
918
+ | 'preserved-source'
919
+ | 'stub-only'
920
+ | 'blocked'
921
+ | 'needs-review';
922
+
923
+ export interface NativeImportRoundtripReadinessOptions extends ProjectNativeImportToSourceOptions {
924
+ readonly projection?: NativeSourceProjectionResult;
925
+ }
926
+
927
+ export interface NativeImportRoundtripReadinessClassification {
928
+ readonly kind: 'frontier.lang.nativeImportRoundtripReadiness';
929
+ readonly version: 1;
930
+ readonly status: NativeImportRoundtripReadinessStatus;
931
+ readonly semanticMergeReadiness: SemanticMergeReadiness;
932
+ readonly reasons: readonly string[];
933
+ readonly importReadiness: NativeImportReadinessClassification;
934
+ readonly projectionReadiness: NativeImportReadinessClassification;
935
+ readonly projectionMode: NativeSourceProjectionMode;
936
+ readonly checks: {
937
+ readonly nativeImport: {
938
+ readonly imports: number;
939
+ readonly exactAst: boolean;
940
+ readonly losses: number;
941
+ readonly readiness: SemanticMergeReadiness;
942
+ };
943
+ readonly universalAst: {
944
+ readonly present: boolean;
945
+ readonly valid: boolean;
946
+ readonly issues: readonly string[];
947
+ readonly nativeSources: number;
948
+ readonly semanticSymbols: number;
949
+ readonly sourceMaps: number;
950
+ readonly sourceMapMappings: number;
951
+ };
952
+ readonly projectedSource: {
953
+ readonly mode: NativeSourceProjectionMode;
954
+ readonly outputHash: string;
955
+ readonly expectedSourceHash?: string;
956
+ readonly sourceHashVerified: boolean;
957
+ readonly declarations: number;
958
+ readonly losses: number;
959
+ readonly readiness: SemanticMergeReadiness;
960
+ };
961
+ };
962
+ readonly evidence: {
963
+ readonly importEvidenceIds: readonly string[];
964
+ readonly projectionEvidenceIds: readonly string[];
965
+ readonly failedEvidenceIds: readonly string[];
966
+ };
967
+ readonly metadata: Record<string, unknown>;
968
+ }
969
+
727
970
  export declare const FrontierCompileTargets: readonly FrontierCompileTarget[];
971
+ export declare const NativeImportRoundtripReadinessStatuses: readonly NativeImportRoundtripReadinessStatus[];
728
972
  export declare const NativeImportTaxonomyKinds: readonly NativeImportTaxonomyKind[];
729
973
  export declare const NativeImportLossKinds: readonly NativeImportKnownLossKind[];
974
+ export declare const NativeImportRegionTaxonomyKinds: readonly NativeImportRegionTaxonomyKind[];
730
975
  export declare const NativeImportReadinessBySeverity: Readonly<Record<NativeImportLossSummary['highestSeverity'], SemanticMergeReadiness>>;
731
976
  export declare const NativeImportLanguageProfiles: readonly NativeImportLanguageProfile[];
732
977
  export declare function normalizeCompileTarget(target?: string): FrontierCompileTarget;
@@ -737,9 +982,11 @@ export declare function renderTargetAst(ast: FrontierTargetAst, target?: Frontie
737
982
  export declare function resolveCapabilityAdapters(document: FrontierLangDocument, target?: FrontierCompileOptions['target'], options?: { readonly platform?: string }): readonly CapabilityResolution[];
738
983
  export declare function summarizeNativeImportLosses(losses?: readonly NativeAstLossRecord[], options?: NativeImportLossSummaryOptions): NativeImportLossSummary;
739
984
  export declare function classifyNativeImportReadiness(losses?: readonly NativeAstLossRecord[], options?: NativeImportLossSummaryOptions): NativeImportReadinessClassification;
985
+ export declare function classifyNativeImportRoundtripReadiness(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: NativeImportRoundtripReadinessOptions): NativeImportRoundtripReadinessClassification;
740
986
  export declare function createNativeImportCoverageMatrix(options?: NativeImportCoverageMatrixOptions): NativeImportCoverageMatrix;
741
987
  export declare function createNativeSourcePreservation(options: CreateNativeSourcePreservationOptions): NativeSourcePreservation;
742
988
  export declare function createSemanticImportSidecar(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: SemanticImportSidecarOptions): SemanticImportSidecar;
989
+ export declare function createNativeImportResultContract(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: NativeImportResultContractOptions): NativeImportResultContract;
743
990
  export declare function createEstreeNativeImporterAdapter(options?: JavaScriptNativeImporterAdapterOptions): NativeImporterAdapter;
744
991
  export declare function createBabelNativeImporterAdapter(options?: JavaScriptNativeImporterAdapterOptions): NativeImporterAdapter;
745
992
  export declare function createTypeScriptCompilerNativeImporterAdapter(options?: TypeScriptCompilerNativeImporterAdapterOptions): NativeImporterAdapter;
package/dist/index.js CHANGED
@@ -67,6 +67,14 @@ const semanticMergeReadinessRank = Object.freeze({
67
67
  blocked: 3
68
68
  });
69
69
 
70
+ export const NativeImportRoundtripReadinessStatuses = Object.freeze([
71
+ 'exact',
72
+ 'preserved-source',
73
+ 'stub-only',
74
+ 'blocked',
75
+ 'needs-review'
76
+ ]);
77
+
70
78
  export const NativeImportTaxonomyKinds = Object.freeze([
71
79
  'exactAstImport',
72
80
  'declarationsOnly',
@@ -106,6 +114,7 @@ export const NativeImportLossKinds = Object.freeze([
106
114
  'parserDiagnostic',
107
115
  'unsupportedSyntax',
108
116
  'unsupportedSemantic',
117
+ 'unverifiedNativeAst',
109
118
  'partialSemanticIndex',
110
119
  'sourceMapApproximation',
111
120
  'targetProjectionLoss'
@@ -118,6 +127,17 @@ export const NativeImportReadinessBySeverity = Object.freeze({
118
127
  error: 'blocked'
119
128
  });
120
129
 
130
+ export const NativeImportRegionTaxonomyKinds = Object.freeze([
131
+ 'symbol',
132
+ 'declaration',
133
+ 'import',
134
+ 'body',
135
+ 'call',
136
+ 'type',
137
+ 'effect',
138
+ 'generatedOutput'
139
+ ]);
140
+
121
141
  export const NativeImportLanguageProfiles = Object.freeze([
122
142
  nativeImportLanguageProfile('javascript', {
123
143
  aliases: ['js', 'mjs', 'cjs', 'jsx'],
@@ -327,6 +347,134 @@ export function classifyNativeImportReadiness(losses = [], options = {}) {
327
347
  };
328
348
  }
329
349
 
350
+ export function classifyNativeImportRoundtripReadiness(importResult, options = {}) {
351
+ if (!importResult || typeof importResult !== 'object') {
352
+ throw new Error('classifyNativeImportRoundtripReadiness requires a native import result');
353
+ }
354
+ const imports = nativeImportEntries(importResult);
355
+ const importLosses = uniqueByLossId([
356
+ ...(importResult.losses ?? []),
357
+ ...imports.flatMap((imported) => imported?.losses ?? [])
358
+ ]);
359
+ const importEvidence = uniqueByEvidenceId([
360
+ ...(importResult.evidence ?? []),
361
+ ...imports.flatMap((imported) => imported?.evidence ?? [])
362
+ ]);
363
+ const exactAst = imports.length > 0 && imports.every((imported) => nativeImportHasExactAstCoverage(imported));
364
+ const importReadiness = classifyNativeImportReadiness(importLosses, {
365
+ exactAst,
366
+ evidence: importEvidence,
367
+ parser: nativeImportRoundtripParser(importResult, imports),
368
+ scanKind: importResult.metadata?.nativeImportLossSummary?.scanKind,
369
+ semanticStatus: importResult.metadata?.semanticStatus ?? importResult.universalAst?.metadata?.semanticStatus
370
+ });
371
+ const projection = options.projection ?? projectNativeImportToSource(importResult, options);
372
+ const projectionReadiness = projection.readiness ?? classifyNativeImportReadiness(projection.losses ?? [], {
373
+ evidence: projection.evidence,
374
+ parser: projection.metadata?.nativeImportLossSummary?.parser,
375
+ scanKind: 'native-source-projection'
376
+ });
377
+ const universalAst = importResult.universalAst;
378
+ const universalAstIssues = universalAst
379
+ ? validateUniversalAstEnvelope(universalAst)
380
+ : ['missing-universal-ast'];
381
+ const universalAstNativeSources = universalAst?.nativeSources?.length ?? importResult.nativeSources?.length ?? (importResult.nativeSource ? 1 : 0);
382
+ const semanticIndex = importResult.semanticIndex ?? universalAst?.semanticIndex;
383
+ const semanticSymbols = semanticIndex?.symbols?.length ?? 0;
384
+ const sourceMaps = importResult.sourceMaps ?? universalAst?.sourceMaps ?? [];
385
+ const sourceMapMappings = sourceMaps.reduce((sum, sourceMap) => sum + (sourceMap?.mappings?.length ?? 0), 0);
386
+ const projectionMatchesSourceHash = Boolean(projection.sourceHash && projection.outputHash === projection.sourceHash);
387
+ const preservedSource = projection.mode === 'preserved-source';
388
+ const failedEvidenceIds = uniqueStrings([
389
+ ...importEvidence.filter((record) => record?.status === 'failed').map((record) => record.id),
390
+ ...(projection.evidence ?? []).filter((record) => record?.status === 'failed').map((record) => record.id)
391
+ ]);
392
+ const blockingReasons = [
393
+ ...(importReadiness.readiness === 'blocked' ? importReadiness.reasons : []),
394
+ ...(projectionReadiness.readiness === 'blocked' ? projectionReadiness.reasons : []),
395
+ ...(failedEvidenceIds.length ? [`Failed evidence prevents native roundtrip readiness: ${failedEvidenceIds.join(', ')}`] : []),
396
+ ...(universalAstIssues.length ? [`Universal AST validation failed: ${universalAstIssues.join('; ')}`] : [])
397
+ ];
398
+ const reviewReasons = [
399
+ ...(semanticSymbols === 0 ? ['Universal AST semantic index has no symbols for source projection review.'] : []),
400
+ ...(sourceMapMappings === 0 ? ['Universal AST has no native source-map mappings for roundtrip review.'] : []),
401
+ ...(preservedSource && !projectionMatchesSourceHash ? ['Projected source was preserved without a verified import source hash match.'] : []),
402
+ ...importReadiness.reasons.filter((reason) => importReadiness.readiness !== 'ready' || !exactAst),
403
+ ...projectionReadiness.reasons.filter((reason) => projectionReadiness.readiness !== 'ready')
404
+ ];
405
+ let status;
406
+ if (blockingReasons.length) {
407
+ status = 'blocked';
408
+ } else if (projection.mode === 'native-source-stubs') {
409
+ status = 'stub-only';
410
+ } else if (reviewReasons.some((reason) => reason.startsWith('Universal AST')) || (preservedSource && !projectionMatchesSourceHash)) {
411
+ status = 'needs-review';
412
+ } else if (exactAst && preservedSource && projectionMatchesSourceHash && projectionReadiness.readiness === 'ready') {
413
+ status = 'exact';
414
+ } else if (preservedSource && projectionMatchesSourceHash) {
415
+ status = 'preserved-source';
416
+ } else {
417
+ status = 'needs-review';
418
+ }
419
+ const reasons = nativeImportRoundtripReasons(status, {
420
+ blockingReasons,
421
+ reviewReasons,
422
+ projection,
423
+ importReadiness,
424
+ projectionReadiness
425
+ });
426
+ return {
427
+ kind: 'frontier.lang.nativeImportRoundtripReadiness',
428
+ version: 1,
429
+ status,
430
+ semanticMergeReadiness: maxSemanticMergeReadiness(importReadiness.readiness, projectionReadiness.readiness),
431
+ reasons,
432
+ importReadiness,
433
+ projectionReadiness,
434
+ projectionMode: projection.mode,
435
+ checks: {
436
+ nativeImport: {
437
+ imports: imports.length,
438
+ exactAst,
439
+ losses: importReadiness.summary.total,
440
+ readiness: importReadiness.readiness
441
+ },
442
+ universalAst: {
443
+ present: Boolean(universalAst),
444
+ valid: universalAstIssues.length === 0,
445
+ issues: universalAstIssues,
446
+ nativeSources: universalAstNativeSources,
447
+ semanticSymbols,
448
+ sourceMaps: sourceMaps.length,
449
+ sourceMapMappings
450
+ },
451
+ projectedSource: {
452
+ mode: projection.mode,
453
+ outputHash: projection.outputHash,
454
+ expectedSourceHash: projection.sourceHash,
455
+ sourceHashVerified: projectionMatchesSourceHash,
456
+ declarations: projection.declarations?.length ?? 0,
457
+ losses: projection.lossSummary?.total ?? projection.losses?.length ?? 0,
458
+ readiness: projectionReadiness.readiness
459
+ }
460
+ },
461
+ evidence: {
462
+ importEvidenceIds: importEvidence.map((record) => record.id).filter(Boolean),
463
+ projectionEvidenceIds: (projection.evidence ?? []).map((record) => record.id).filter(Boolean),
464
+ failedEvidenceIds
465
+ },
466
+ metadata: {
467
+ nativeImportId: importResult.id,
468
+ universalAstId: universalAst?.id,
469
+ projectionId: projection.id,
470
+ sourcePath: projection.sourcePath ?? importResult.sourcePath,
471
+ language: projection.language ?? importResult.language,
472
+ sourcePreservationId: projection.metadata?.sourcePreservationId,
473
+ ...options.metadata
474
+ }
475
+ };
476
+ }
477
+
330
478
  export function createNativeImportCoverageMatrix(input = {}) {
331
479
  const imports = input.imports ?? [];
332
480
  const adapters = input.adapters ?? [];
@@ -446,6 +594,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
446
594
  const evidence = uniqueRecordsById(imports.flatMap((imported) => imported?.evidence ?? []));
447
595
  const mergeCandidates = imports.flatMap((imported) => imported?.mergeCandidates ?? []);
448
596
  const lossSummary = summarizeNativeImportLosses(losses, { evidence });
597
+ const regionTaxonomy = summarizeSemanticImportRegionTaxonomy(ownershipRegions);
449
598
  const readiness = mergeCandidates.reduce(
450
599
  (current, candidate) => maxSemanticMergeReadiness(current, candidate.readiness),
451
600
  lossSummary.semanticMergeReadiness
@@ -482,6 +631,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
482
631
  blockingLossIds: lossSummary.blockingLossIds,
483
632
  reviewLossIds: lossSummary.reviewLossIds
484
633
  },
634
+ regionTaxonomy,
485
635
  evidence: {
486
636
  total: evidence.length,
487
637
  failed: evidence.filter((record) => record.status === 'failed').map((record) => record.id),
@@ -491,6 +641,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
491
641
  imports: imports.length,
492
642
  symbols: symbols.length,
493
643
  ownershipRegions: ownershipRegions.length,
644
+ regionKinds: regionTaxonomy.presentKinds.length,
494
645
  sourceMapMappings: sourceMapMappings.length,
495
646
  readiness,
496
647
  emptySemanticIndex: symbols.length === 0
@@ -502,6 +653,83 @@ export function createSemanticImportSidecar(importResult, options = {}) {
502
653
  };
503
654
  }
504
655
 
656
+ export function createNativeImportResultContract(importResult, options = {}) {
657
+ if (!importResult || typeof importResult !== 'object') {
658
+ throw new Error('createNativeImportResultContract requires a native import result');
659
+ }
660
+ const imports = nativeImportEntries(importResult);
661
+ const evidence = uniqueByEvidenceId([
662
+ ...(importResult.evidence ?? []),
663
+ ...imports.flatMap((imported) => imported?.evidence ?? [])
664
+ ]);
665
+ const losses = uniqueByLossId([
666
+ ...(importResult.losses ?? []),
667
+ ...imports.flatMap((imported) => imported?.losses ?? imported?.nativeAst?.losses ?? [])
668
+ ]);
669
+ const sourceMaps = collectImportSourceMaps(importResult, imports);
670
+ const regionSummary = summarizeImportRegions(importResult, imports, options);
671
+ const sourceMapSummary = summarizeImportSourceMaps(sourceMaps);
672
+ const sourcePreservation = summarizeImportSourcePreservation(importResult, imports);
673
+ const adapterCoverage = summarizeImportAdapterCoverage(importResult, imports);
674
+ const primary = imports[0] ?? importResult;
675
+ const nativeSource = importResult.nativeSource ?? importResult.nativeSources?.[0] ?? primary?.nativeSource;
676
+ const nativeAst = importResult.nativeAst ?? nativeSource?.ast ?? primary?.nativeAst ?? primary?.nativeSource?.ast;
677
+ const semanticIndex = importResult.semanticIndex ?? importResult.universalAst?.semanticIndex ?? primary?.semanticIndex ?? primary?.universalAst?.semanticIndex;
678
+ const lossSummary = options.lossSummary ?? importResult.metadata?.nativeImportLossSummary ?? summarizeNativeImportLosses(losses, {
679
+ evidence,
680
+ parser: nativeAst?.parser,
681
+ scanKind: importResult.metadata?.scanKind,
682
+ semanticStatus: importResult.metadata?.semanticStatus ?? importResult.universalAst?.metadata?.semanticStatus
683
+ });
684
+ const mergeCandidates = [
685
+ ...(importResult.mergeCandidates ?? []),
686
+ ...imports.flatMap((imported) => imported?.mergeCandidates ?? [])
687
+ ];
688
+ const readiness = summarizeImportContractReadiness(importResult, mergeCandidates, lossSummary);
689
+ const defaultSidecarId = defaultSemanticImportSidecarId(importResult, imports);
690
+ return {
691
+ kind: 'frontier.lang.nativeImportResultContract',
692
+ version: 1,
693
+ importResultId: importResult.id,
694
+ language: importResult.language ?? (imports.length === 1 ? imports[0]?.language : 'mixed'),
695
+ sourcePath: importResult.sourcePath ?? primary?.sourcePath ?? nativeSource?.sourcePath ?? nativeAst?.sourcePath,
696
+ sourceHash: nativeSource?.sourceHash ?? nativeAst?.sourceHash,
697
+ sourceCount: imports.length,
698
+ sources: imports.map((imported, index) => compactImportContractSource(imported, index)),
699
+ ids: {
700
+ nativeSourceId: nativeSource?.id,
701
+ nativeAstId: nativeAst?.id,
702
+ semanticIndexId: semanticIndex?.id,
703
+ universalAstId: importResult.universalAst?.id ?? primary?.universalAst?.id,
704
+ patchId: importResult.patch?.id,
705
+ sourceMapIds: sourceMapSummary.ids,
706
+ semanticSidecarIds: uniqueStrings([
707
+ ...normalizeStringList(options.semanticSidecarIds ?? options.sidecarIds ?? options.sidecarId),
708
+ defaultSidecarId
709
+ ])
710
+ },
711
+ sourcePreservation,
712
+ adapterCoverage,
713
+ lossSummary,
714
+ regions: regionSummary,
715
+ sourceMaps: sourceMapSummary,
716
+ readiness,
717
+ evidence: {
718
+ total: evidence.length,
719
+ failed: evidence.filter((record) => record?.status === 'failed').map((record) => record.id).filter(Boolean),
720
+ ids: evidence.map((record) => record.id).filter(Boolean)
721
+ },
722
+ metadata: {
723
+ parser: nativeAst?.parser,
724
+ semanticStatus: importResult.metadata?.semanticStatus ?? importResult.universalAst?.metadata?.semanticStatus,
725
+ projectRoot: importResult.projectRoot,
726
+ defaultSemanticSidecarId: defaultSidecarId,
727
+ note: 'Contract summarizes import-result evidence for merge admission; it does not upgrade lightweight scans into exact semantic imports.',
728
+ ...options.metadata
729
+ }
730
+ };
731
+ }
732
+
505
733
  export function createEstreeNativeImporterAdapter(options = {}) {
506
734
  return createJavaScriptSyntaxImporterAdapter({
507
735
  id: 'frontier.estree-native-importer',
@@ -917,7 +1145,17 @@ export function importNativeSource(input) {
917
1145
  const frontierNodeIds = input.frontierNodeIds ?? input.semanticNodes?.map((node) => node.id) ?? [];
918
1146
  const semanticNodes = input.semanticNodes ?? [];
919
1147
  const semanticStatus = input.semanticStatus ?? (semanticNodes.length ? 'mapped' : 'native-only');
920
- const losses = normalizeNativeLossRecords(input.losses ?? nativeAst.losses ?? lightweight?.losses ?? []);
1148
+ const nativeAstExact = hasNativeExactAstEvidence(input, nativeAst, lightweight);
1149
+ const baseLosses = normalizeNativeLossRecords(input.losses ?? nativeAst.losses ?? lightweight?.losses ?? []);
1150
+ const losses = normalizeNativeLossRecords([
1151
+ ...baseLosses,
1152
+ ...unverifiedNativeAstLosses(input, nativeAst, {
1153
+ importIdPart,
1154
+ exactAst: nativeAstExact,
1155
+ hasLosses: baseLosses.length > 0,
1156
+ lightweight
1157
+ })
1158
+ ]);
921
1159
  const nativeSource = nativeSourceNode({
922
1160
  id: input.nativeSourceId ?? `native_source_${importIdPart}`,
923
1161
  name: input.name ?? sourcePath?.split(/[\\/]/).filter(Boolean).at(-1) ?? `${language}NativeSource`,
@@ -978,7 +1216,7 @@ export function importNativeSource(input) {
978
1216
  }
979
1217
  }];
980
1218
  const lossSummary = summarizeNativeImportLosses(losses, {
981
- exactAst: Boolean(input.nativeAst || input.nodes),
1219
+ exactAst: nativeAstExact,
982
1220
  evidence: baseEvidence,
983
1221
  parser: nativeAst.parser,
984
1222
  scanKind: lightweight?.metadata?.scanKind,
@@ -1164,7 +1402,8 @@ function createLightweightNativeImport(input) {
1164
1402
  metadata: {
1165
1403
  ...declaration.metadata,
1166
1404
  ownershipRegionId: ownershipRegion.id,
1167
- ownershipRegionKey: ownershipRegion.key
1405
+ ownershipRegionKey: ownershipRegion.key,
1406
+ ownershipRegionKind: ownershipRegion.regionKind
1168
1407
  }
1169
1408
  };
1170
1409
  if (declaration.symbolId) {
@@ -1180,7 +1419,8 @@ function createLightweightNativeImport(input) {
1180
1419
  definitionSpan: declaration.span,
1181
1420
  metadata: {
1182
1421
  ownershipRegionId: ownershipRegion.id,
1183
- ownershipRegionKey: ownershipRegion.key
1422
+ ownershipRegionKey: ownershipRegion.key,
1423
+ ownershipRegionKind: ownershipRegion.regionKind
1184
1424
  }
1185
1425
  });
1186
1426
  occurrences.push({
@@ -1207,6 +1447,15 @@ function createLightweightNativeImport(input) {
1207
1447
  predicate: 'semanticOwnershipRegion',
1208
1448
  subjectId: declaration.symbolId,
1209
1449
  value: ownershipRegion
1450
+ }, {
1451
+ id: `fact_${idFragment(declaration.nodeId)}_ownership_region_taxonomy`,
1452
+ predicate: 'semanticOwnershipRegionTaxonomy',
1453
+ subjectId: declaration.symbolId,
1454
+ value: {
1455
+ regionKind: ownershipRegion.regionKind,
1456
+ granularity: ownershipRegion.granularity,
1457
+ key: ownershipRegion.key
1458
+ }
1210
1459
  });
1211
1460
  mappings.push({
1212
1461
  id: `map_${idFragment(declaration.nodeId)}`,
@@ -1217,6 +1466,8 @@ function createLightweightNativeImport(input) {
1217
1466
  evidenceIds: [evidenceId],
1218
1467
  lossIds: declaration.loss ? [declaration.loss.id] : [],
1219
1468
  ownershipRegionId: ownershipRegion.id,
1469
+ ownershipRegionKey: ownershipRegion.key,
1470
+ ownershipRegionKind: ownershipRegion.regionKind,
1220
1471
  precision: 'declaration'
1221
1472
  });
1222
1473
  }
@@ -2827,6 +3078,9 @@ function inferSourceMapMappings(input) {
2827
3078
  sourceSpan: occurrence.span ?? nativeNode?.span,
2828
3079
  evidenceIds,
2829
3080
  lossIds: lossIdsForNativeNode(input.losses ?? nativeAst?.losses ?? [], occurrence.nativeAstNodeId),
3081
+ ownershipRegionId: symbol?.metadata?.ownershipRegionId,
3082
+ ownershipRegionKey: symbol?.metadata?.ownershipRegionKey,
3083
+ ownershipRegionKind: symbol?.metadata?.ownershipRegionKind,
2830
3084
  precision: occurrence.span || nativeNode?.span ? 'declaration' : 'unknown'
2831
3085
  };
2832
3086
  });
@@ -2896,6 +3150,9 @@ function normalizeSourceMapMappings(mappings, context) {
2896
3150
  target,
2897
3151
  evidenceIds: normalizeReferenceIds(mapping.evidenceIds, evidenceIds),
2898
3152
  lossIds: normalizeReferenceIds(mapping.lossIds, lossIdsForNativeNode(context.losses ?? nativeAst?.losses ?? [], nativeAstNodeId)),
3153
+ ownershipRegionId: mapping.ownershipRegionId ?? symbol?.metadata?.ownershipRegionId,
3154
+ ownershipRegionKey: mapping.ownershipRegionKey ?? symbol?.metadata?.ownershipRegionKey,
3155
+ ownershipRegionKind: mapping.ownershipRegionKind ?? symbol?.metadata?.ownershipRegionKind,
2899
3156
  precision: normalizeSourceMapPrecision(mapping.precision, sourceSpan, generatedSpan)
2900
3157
  };
2901
3158
  return {
@@ -3106,7 +3363,7 @@ function nativeImportCategoryForLossKind(kind) {
3106
3363
  if (kind === 'sourcePreservation' || kind === 'commentsTrivia' || kind === 'nonRoundTrippable') return 'sourcePreservation';
3107
3364
  if (kind === 'parserDiagnostic') return 'parserDiagnostics';
3108
3365
  if (kind === 'unsupportedSyntax' || kind === 'unsupportedSemantic') return 'unsupportedSyntax';
3109
- if (kind === 'partialSemanticIndex') return 'partialSemanticIndex';
3366
+ if (kind === 'partialSemanticIndex' || kind === 'unverifiedNativeAst') return 'partialSemanticIndex';
3110
3367
  if (kind === 'sourceMapApproximation') return 'sourceMapApproximation';
3111
3368
  if (kind === 'targetProjectionLoss') return 'targetProjectionLoss';
3112
3369
  return String(kind ?? 'opaqueNative');
@@ -3238,9 +3495,12 @@ function semanticImportSidecarEntry(imported, index, options) {
3238
3495
  signatureHash: symbol.signatureHash,
3239
3496
  ownershipRegionId: region.id,
3240
3497
  ownershipKey: region.key,
3498
+ ownershipRegionKind: region.regionKind,
3241
3499
  readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness ?? 'needs-review'
3242
3500
  });
3243
3501
  }
3502
+ const ownershipRegions = uniqueRecordsById(regions);
3503
+ const regionTaxonomy = summarizeSemanticImportRegionTaxonomy(ownershipRegions);
3244
3504
  return {
3245
3505
  id: imported?.id ?? `import_${index + 1}`,
3246
3506
  language: imported?.language,
@@ -3256,8 +3516,9 @@ function semanticImportSidecarEntry(imported, index, options) {
3256
3516
  sourceMapMappingCount: sourceMapMappings.length,
3257
3517
  readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness ?? 'needs-review',
3258
3518
  emptySemanticIndex: symbols.length === 0,
3519
+ regionTaxonomy,
3259
3520
  symbols,
3260
- ownershipRegions: uniqueRecordsById(regions)
3521
+ ownershipRegions
3261
3522
  };
3262
3523
  }
3263
3524
 
@@ -3265,15 +3526,17 @@ function semanticOwnershipRegionForSymbol(imported, symbol, mapping, nativeNode,
3265
3526
  const sourcePath = mapping?.sourceSpan?.path ?? symbol.definitionSpan?.path ?? nativeNode?.span?.path ?? imported?.sourcePath ?? imported?.nativeSource?.sourcePath ?? imported?.nativeAst?.sourcePath;
3266
3527
  const language = symbol.language ?? imported?.language ?? imported?.nativeAst?.language ?? imported?.nativeSource?.language;
3267
3528
  const sourceSpan = mapping?.sourceSpan ?? symbol.definitionSpan ?? nativeNode?.span;
3529
+ const regionKind = semanticRegionKindForSymbol(symbol, mapping, nativeNode);
3268
3530
  const key = [
3269
3531
  options.regionPrefix ?? 'source',
3270
3532
  sourcePath ?? `${language}:memory`,
3271
- symbol.kind ?? 'symbol',
3533
+ regionKind,
3272
3534
  symbol.name ?? symbol.id
3273
3535
  ].map((part) => String(part).replace(/\s+/g, ' ').trim()).join('#');
3274
3536
  return {
3275
3537
  id: `region_${idFragment(key)}`,
3276
3538
  key,
3539
+ regionKind,
3277
3540
  granularity: 'symbol',
3278
3541
  language,
3279
3542
  sourcePath,
@@ -3284,7 +3547,10 @@ function semanticOwnershipRegionForSymbol(imported, symbol, mapping, nativeNode,
3284
3547
  nativeAstNodeId: symbol.nativeAstNodeId ?? nativeNode?.id,
3285
3548
  sourceSpan,
3286
3549
  precision: mapping?.precision ?? (sourceSpan ? 'declaration' : 'unknown'),
3287
- mergePolicy: 'single-writer-review-required'
3550
+ mergePolicy: semanticRegionMergePolicy(regionKind),
3551
+ metadata: {
3552
+ semanticRegionTaxonomy: true
3553
+ }
3288
3554
  };
3289
3555
  }
3290
3556
 
@@ -3292,10 +3558,12 @@ function semanticOwnershipRegionForDeclaration(input, declaration, documentId) {
3292
3558
  const name = declaration.name ?? declaration.importPath ?? declaration.nodeId ?? declaration.nativeNode?.id;
3293
3559
  const kind = declaration.symbolKind ?? declaration.kind ?? declaration.nativeNode?.kind ?? 'symbol';
3294
3560
  const sourcePath = declaration.span?.path ?? declaration.nativeNode?.span?.path ?? input.sourcePath ?? `${input.language}:memory`;
3295
- const key = ['source', sourcePath, kind, name].map((part) => String(part).replace(/\s+/g, ' ').trim()).join('#');
3561
+ const regionKind = semanticRegionKindForDeclaration(declaration);
3562
+ const key = ['source', sourcePath, regionKind, name].map((part) => String(part).replace(/\s+/g, ' ').trim()).join('#');
3296
3563
  return {
3297
3564
  id: `region_${idFragment(key)}`,
3298
3565
  key,
3566
+ regionKind,
3299
3567
  granularity: 'symbol',
3300
3568
  language: input.language,
3301
3569
  documentId,
@@ -3307,7 +3575,10 @@ function semanticOwnershipRegionForDeclaration(input, declaration, documentId) {
3307
3575
  nativeAstNodeId: declaration.nodeId ?? declaration.nativeNode?.id,
3308
3576
  sourceSpan: declaration.span ?? declaration.nativeNode?.span,
3309
3577
  precision: declaration.span || declaration.nativeNode?.span ? 'declaration' : 'unknown',
3310
- mergePolicy: 'single-writer-review-required'
3578
+ mergePolicy: semanticRegionMergePolicy(regionKind),
3579
+ metadata: {
3580
+ semanticRegionTaxonomy: true
3581
+ }
3311
3582
  };
3312
3583
  }
3313
3584
 
@@ -3322,7 +3593,7 @@ function semanticPatchHintForRegion(region, readiness, options = {}) {
3322
3593
  sourceSpan: region.sourceSpan,
3323
3594
  readiness,
3324
3595
  precision: region.precision,
3325
- supportedOperations: ['replace-region', 'insert-before-region', 'insert-after-region'],
3596
+ supportedOperations: semanticRegionSupportedOperations(region),
3326
3597
  projection: {
3327
3598
  sourceLanguage: region.language,
3328
3599
  targetPath: options.targetPath ?? region.sourcePath,
@@ -3331,6 +3602,79 @@ function semanticPatchHintForRegion(region, readiness, options = {}) {
3331
3602
  };
3332
3603
  }
3333
3604
 
3605
+ function semanticRegionKindForDeclaration(declaration) {
3606
+ if (declaration.role === 'import' || declaration.importPath) return 'import';
3607
+ const kind = declaration.symbolKind ?? declaration.kind ?? declaration.nativeNode?.kind;
3608
+ if (semanticKindIsType(kind)) return 'type';
3609
+ if (semanticKindCanOwnBody(kind, declaration.span ?? declaration.nativeNode?.span)) return 'body';
3610
+ return 'declaration';
3611
+ }
3612
+
3613
+ function semanticRegionKindForSymbol(symbol, mapping, nativeNode) {
3614
+ if (mapping?.generatedSpan || mapping?.generatedName || mapping?.target?.emitPath) return 'generatedOutput';
3615
+ if (symbol?.metadata?.ownershipRegionKind) return normalizeNativeImportRegionKind(symbol.metadata.ownershipRegionKind);
3616
+ if (String(symbol?.id ?? '').includes(':import:') || symbol?.metadata?.role === 'import') return 'import';
3617
+ if (semanticKindIsType(symbol?.kind ?? nativeNode?.kind)) return 'type';
3618
+ if (semanticKindCanOwnBody(symbol?.kind ?? nativeNode?.kind, nativeNode?.span ?? symbol?.definitionSpan)) return 'body';
3619
+ return 'declaration';
3620
+ }
3621
+
3622
+ function semanticKindIsType(kind) {
3623
+ return ['type', 'class', 'interface', 'trait', 'protocol', 'struct', 'enum', 'record'].includes(String(kind ?? '').toLowerCase());
3624
+ }
3625
+
3626
+ function semanticKindCanOwnBody(kind, span) {
3627
+ const text = String(kind ?? '').toLowerCase();
3628
+ if (/function|method|class|implementation|module|namespace|package|action|effect|capability/.test(text)) return true;
3629
+ return typeof span?.startLine === 'number' && typeof span?.endLine === 'number' && span.endLine > span.startLine;
3630
+ }
3631
+
3632
+ function semanticRegionMergePolicy(regionKind) {
3633
+ if (regionKind === 'import') return 'module-edge-review-required';
3634
+ if (regionKind === 'body') return 'implementation-single-writer-review-required';
3635
+ if (regionKind === 'call') return 'callsite-overlap-review-required';
3636
+ if (regionKind === 'type') return 'type-surface-review-required';
3637
+ if (regionKind === 'effect') return 'effect-boundary-review-required';
3638
+ if (regionKind === 'generatedOutput') return 'generated-output-source-map-review-required';
3639
+ return 'single-writer-review-required';
3640
+ }
3641
+
3642
+ function semanticRegionSupportedOperations(region) {
3643
+ if (region.regionKind === 'import') return ['replace-import', 'insert-import-before', 'insert-import-after', 'replace-region'];
3644
+ if (region.regionKind === 'body') return ['replace-body', 'insert-before-body', 'insert-after-body'];
3645
+ if (region.regionKind === 'call') return ['replace-callsite', 'review-callsite'];
3646
+ if (region.regionKind === 'type') return ['replace-type-declaration', 'merge-type-members', 'replace-region'];
3647
+ if (region.regionKind === 'effect') return ['route-effect', 'replace-effect-boundary', 'review-effect-policy'];
3648
+ if (region.regionKind === 'generatedOutput') return ['replace-generated-output', 'attach-generated-source-map', 'review-generator-input'];
3649
+ return ['replace-region', 'insert-before-region', 'insert-after-region'];
3650
+ }
3651
+
3652
+ function normalizeNativeImportRegionKind(value) {
3653
+ const text = String(value ?? 'symbol').trim();
3654
+ if (text === 'generated' || text === 'generated-output' || text === 'generated_output') return 'generatedOutput';
3655
+ if (NativeImportRegionTaxonomyKinds.includes(text)) return text;
3656
+ return text || 'symbol';
3657
+ }
3658
+
3659
+ function summarizeSemanticImportRegionTaxonomy(regions) {
3660
+ const byKind = {};
3661
+ const keysByKind = {};
3662
+ const keys = [];
3663
+ for (const region of regions ?? []) {
3664
+ const kind = normalizeNativeImportRegionKind(region.regionKind ?? region.granularity);
3665
+ byKind[kind] = (byKind[kind] ?? 0) + 1;
3666
+ keysByKind[kind] = [...(keysByKind[kind] ?? []), region.key].filter(Boolean);
3667
+ if (region.key) keys.push(region.key);
3668
+ }
3669
+ return {
3670
+ kinds: [...NativeImportRegionTaxonomyKinds],
3671
+ presentKinds: uniqueStrings(Object.keys(byKind)),
3672
+ byKind,
3673
+ keys,
3674
+ keysByKind
3675
+ };
3676
+ }
3677
+
3334
3678
  function nativeImportCoverageReasons(profile) {
3335
3679
  if (!profile.supportsLightweightScan) return ['No built-in scanner coverage profile; host must provide an exact adapter or mark unsupported.'];
3336
3680
  return ['Built-in coverage is declaration-level only; use injected parser adapters for exact AST/CST, tokens, trivia, type resolution, and round-trip evidence.'];
@@ -3392,6 +3736,36 @@ function attachNativeImportLossSummary(evidence, lossSummary) {
3392
3736
  }));
3393
3737
  }
3394
3738
 
3739
+ function hasNativeExactAstEvidence(input, nativeAst, lightweight) {
3740
+ if (lightweight) return false;
3741
+ if (!(input?.nativeAst || input?.nodes)) return false;
3742
+ if (input.exactAst === true || input.metadata?.exactAst === true || input.nativeAstMetadata?.exactAst === true) return true;
3743
+ const coverage = input.metadata?.adapterCoverage
3744
+ ?? input.nativeAstMetadata?.adapterCoverage
3745
+ ?? input.nativeSourceMetadata?.adapterCoverage
3746
+ ?? nativeAst?.metadata?.adapterCoverage;
3747
+ if (coverage?.exactAst !== true) return false;
3748
+ const observedNodes = coverage.observed?.nativeAstNodes;
3749
+ return observedNodes === undefined || observedNodes > 0;
3750
+ }
3751
+
3752
+ function unverifiedNativeAstLosses(input, nativeAst, context) {
3753
+ if (context.lightweight || context.exactAst || context.hasLosses) return [];
3754
+ if (!(input?.nativeAst || input?.nodes)) return [];
3755
+ return [{
3756
+ id: `loss_${context.importIdPart}_unverified_native_ast`,
3757
+ severity: 'warning',
3758
+ kind: 'unverifiedNativeAst',
3759
+ nodeId: nativeAst?.rootId,
3760
+ message: 'Caller supplied native AST nodes without explicit exactAst or adapter coverage evidence.',
3761
+ metadata: {
3762
+ reason: 'missing-exact-ast-evidence',
3763
+ nativeAstId: nativeAst?.id,
3764
+ parser: nativeAst?.parser
3765
+ }
3766
+ }];
3767
+ }
3768
+
3395
3769
  function withNativeImportReadiness(importResult, lossSummary) {
3396
3770
  const mergeCandidates = (importResult.mergeCandidates ?? []).map((candidate) => {
3397
3771
  const readiness = maxSemanticMergeReadiness(candidate.readiness, lossSummary.semanticMergeReadiness);
@@ -3413,13 +3787,30 @@ function withNativeImportReadiness(importResult, lossSummary) {
3413
3787
  }
3414
3788
  };
3415
3789
  });
3790
+ const semanticMergeReadiness = mergeCandidates[0]?.readiness ?? lossSummary.semanticMergeReadiness;
3791
+ const contractInput = {
3792
+ ...importResult,
3793
+ mergeCandidates,
3794
+ metadata: {
3795
+ ...importResult.metadata,
3796
+ nativeImportLossSummary: lossSummary,
3797
+ semanticMergeReadiness
3798
+ }
3799
+ };
3800
+ const importResultContract = createNativeImportResultContract(contractInput, { lossSummary });
3416
3801
  return {
3417
3802
  ...importResult,
3418
3803
  mergeCandidates,
3419
3804
  metadata: {
3420
3805
  ...importResult.metadata,
3806
+ importResultContract,
3421
3807
  nativeImportLossSummary: lossSummary,
3422
- semanticMergeReadiness: mergeCandidates[0]?.readiness ?? lossSummary.semanticMergeReadiness,
3808
+ semanticMergeReadiness,
3809
+ readinessReasons: importResultContract.readiness.reasons,
3810
+ sourcePreservationSummary: importResultContract.sourcePreservation,
3811
+ adapterCoverageSummary: importResultContract.adapterCoverage,
3812
+ regionSummary: importResultContract.regions,
3813
+ sourceMapSummary: importResultContract.sourceMaps,
3423
3814
  lossCategories: lossSummary.categories,
3424
3815
  lossSeverityCounts: lossSummary.bySeverity,
3425
3816
  lossKindCounts: lossSummary.byKind
@@ -3427,6 +3818,300 @@ function withNativeImportReadiness(importResult, lossSummary) {
3427
3818
  };
3428
3819
  }
3429
3820
 
3821
+ function nativeImportEntries(importResult) {
3822
+ if (Array.isArray(importResult?.imports)) return importResult.imports.filter(Boolean);
3823
+ return [importResult].filter(Boolean);
3824
+ }
3825
+
3826
+ function nativeImportHasExactAstCoverage(imported) {
3827
+ if (imported?.metadata?.nativeImportLossSummary?.exactAst === true) return true;
3828
+ if (imported?.adapter?.coverage?.exactAst === true && !(imported?.losses?.length)) return true;
3829
+ return false;
3830
+ }
3831
+
3832
+ function nativeImportRoundtripParser(importResult, imports) {
3833
+ const parsers = uniqueStrings([
3834
+ importResult.nativeAst?.parser,
3835
+ importResult.nativeSource?.parser,
3836
+ importResult.metadata?.parser,
3837
+ ...imports.map((imported) => imported?.nativeAst?.parser ?? imported?.nativeSource?.parser ?? imported?.metadata?.parser)
3838
+ ].filter(Boolean));
3839
+ return parsers.length === 1 ? parsers[0] : parsers.length ? parsers.join(',') : undefined;
3840
+ }
3841
+
3842
+ function nativeImportRoundtripReasons(status, input) {
3843
+ if (status === 'blocked') return uniqueStrings(input.blockingReasons);
3844
+ if (status === 'stub-only') {
3845
+ return uniqueStrings([
3846
+ `Native source projection emitted declaration stubs in ${input.projection.mode} mode.`,
3847
+ ...input.projectionReadiness.reasons,
3848
+ ...input.importReadiness.reasons.filter((reason) => input.importReadiness.readiness !== 'ready')
3849
+ ]);
3850
+ }
3851
+ if (status === 'needs-review') return uniqueStrings(input.reviewReasons);
3852
+ if (status === 'exact') {
3853
+ return ['Exact native AST import and verified preserved source projection are available.'];
3854
+ }
3855
+ if (status === 'preserved-source') {
3856
+ return uniqueStrings([
3857
+ 'Verified native source text is preserved for projection; semantic import evidence may still require review.',
3858
+ ...input.importReadiness.reasons.filter((reason) => input.importReadiness.readiness !== 'ready')
3859
+ ]);
3860
+ }
3861
+ return ['Native import roundtrip readiness requires review.'];
3862
+ }
3863
+
3864
+ function collectImportSourceMaps(importResult, imports) {
3865
+ return uniqueRecordsById([
3866
+ ...(importResult?.sourceMaps ?? importResult?.universalAst?.sourceMaps ?? []),
3867
+ ...imports.flatMap((imported) => imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? [])
3868
+ ]);
3869
+ }
3870
+
3871
+ function summarizeImportSourceMaps(sourceMaps) {
3872
+ const mappings = sourceMaps.flatMap((sourceMap) => sourceMap?.mappings ?? []);
3873
+ return {
3874
+ total: sourceMaps.length,
3875
+ ids: sourceMaps.map((sourceMap) => sourceMap.id).filter(Boolean),
3876
+ mappingCount: mappings.length,
3877
+ sourcePaths: uniqueStrings([
3878
+ ...sourceMaps.map((sourceMap) => sourceMap.sourcePath),
3879
+ ...mappings.map((mapping) => mapping.sourceSpan?.path)
3880
+ ].filter(Boolean)),
3881
+ targetPaths: uniqueStrings([
3882
+ ...sourceMaps.map((sourceMap) => sourceMap.targetPath ?? sourceMap.target?.emitPath),
3883
+ ...mappings.map((mapping) => mapping.generatedSpan?.targetPath ?? mapping.target?.emitPath)
3884
+ ].filter(Boolean)),
3885
+ byPrecision: countBy(mappings.map((mapping) => mapping.precision ?? 'unknown')),
3886
+ sourceRangeMappings: mappings.filter((mapping) => mapping.sourceSpan).length,
3887
+ generatedRangeMappings: mappings.filter((mapping) => mapping.generatedSpan).length
3888
+ };
3889
+ }
3890
+
3891
+ function summarizeImportRegions(importResult, imports, options = {}) {
3892
+ const entries = imports.map((imported, index) => semanticImportSidecarEntry(imported, index, options));
3893
+ const regions = uniqueRecordsById(entries.flatMap((entry) => entry.ownershipRegions ?? []));
3894
+ const taxonomy = summarizeSemanticImportRegionTaxonomy(regions);
3895
+ return {
3896
+ total: regions.length,
3897
+ ids: regions.map((region) => region.id),
3898
+ keys: regions.map((region) => region.key),
3899
+ sourcePaths: uniqueStrings(regions.map((region) => region.sourcePath).filter(Boolean)),
3900
+ byKind: taxonomy.byKind,
3901
+ byGranularity: countBy(regions.map((region) => region.granularity ?? 'unknown')),
3902
+ byPrecision: countBy(regions.map((region) => region.precision ?? 'unknown')),
3903
+ byLanguage: countBy(regions.map((region) => region.language ?? importResult?.language ?? 'unknown')),
3904
+ symbolIds: uniqueStrings(regions.map((region) => region.symbolId).filter(Boolean)),
3905
+ taxonomy
3906
+ };
3907
+ }
3908
+
3909
+ function summarizeImportSourcePreservation(importResult, imports) {
3910
+ const records = uniqueSourcePreservationRecords([
3911
+ ...collectSourcePreservationFromImport(importResult),
3912
+ ...imports.flatMap((imported) => collectSourcePreservationFromImport(imported))
3913
+ ]);
3914
+ const compactRecords = records.map(compactSourcePreservationRecord);
3915
+ return {
3916
+ total: compactRecords.length,
3917
+ ids: compactRecords.map((record) => record.id).filter(Boolean),
3918
+ sourcePaths: uniqueStrings(compactRecords.map((record) => record.sourcePath).filter(Boolean)),
3919
+ sourceHashes: uniqueStrings(compactRecords.map((record) => record.sourceHash).filter(Boolean)),
3920
+ exactSourceAvailable: compactRecords.filter((record) => record.exactSourceAvailable).length,
3921
+ sourceBytes: compactRecords.reduce((sum, record) => sum + (record.sourceBytes ?? 0), 0),
3922
+ lineCount: compactRecords.reduce((sum, record) => sum + (record.lineCount ?? 0), 0),
3923
+ tokens: compactRecords.reduce((sum, record) => sum + (record.tokens ?? 0), 0),
3924
+ trivia: compactRecords.reduce((sum, record) => sum + (record.trivia ?? 0), 0),
3925
+ directives: compactRecords.reduce((sum, record) => sum + (record.directives ?? 0), 0),
3926
+ comments: compactRecords.reduce((sum, record) => sum + (record.comments ?? 0), 0),
3927
+ whitespace: compactRecords.reduce((sum, record) => sum + (record.whitespace ?? 0), 0),
3928
+ truncated: compactRecords.some((record) => record.truncated),
3929
+ records: compactRecords
3930
+ };
3931
+ }
3932
+
3933
+ function collectSourcePreservationFromImport(imported) {
3934
+ const nativeAst = imported?.nativeAst ?? imported?.nativeSource?.ast;
3935
+ return [
3936
+ imported?.metadata?.sourcePreservation,
3937
+ imported?.nativeSource?.metadata?.sourcePreservation,
3938
+ nativeAst?.metadata?.sourcePreservation,
3939
+ imported?.universalAst?.metadata?.sourcePreservation,
3940
+ ...(imported?.nativeSources ?? []).map((nativeSource) => nativeSource?.metadata?.sourcePreservation ?? nativeSource?.ast?.metadata?.sourcePreservation)
3941
+ ].filter(Boolean);
3942
+ }
3943
+
3944
+ function uniqueSourcePreservationRecords(records) {
3945
+ const seen = new Set();
3946
+ const result = [];
3947
+ for (const record of records) {
3948
+ const key = record.id ?? `${record.sourcePath ?? 'source'}#${record.sourceHash ?? result.length}`;
3949
+ if (seen.has(key)) continue;
3950
+ seen.add(key);
3951
+ result.push(record);
3952
+ }
3953
+ return result;
3954
+ }
3955
+
3956
+ function compactSourcePreservationRecord(record) {
3957
+ return {
3958
+ id: record.id,
3959
+ language: record.language,
3960
+ sourcePath: record.sourcePath,
3961
+ sourceHash: record.sourceHash,
3962
+ sourceBytes: record.sourceBytes,
3963
+ lineCount: record.lineCount,
3964
+ newline: record.newline,
3965
+ encoding: record.encoding,
3966
+ exactSourceAvailable: record.summary?.exactSourceAvailable === true,
3967
+ tokens: record.summary?.tokens ?? record.tokens?.length ?? 0,
3968
+ trivia: record.summary?.trivia ?? record.trivia?.length ?? 0,
3969
+ directives: record.summary?.directives ?? record.directives?.length ?? 0,
3970
+ comments: record.summary?.comments ?? 0,
3971
+ whitespace: record.summary?.whitespace ?? 0,
3972
+ truncated: record.summary?.truncated === true
3973
+ };
3974
+ }
3975
+
3976
+ function summarizeImportAdapterCoverage(importResult, imports) {
3977
+ const records = uniqueAdapterCoverageRecords([
3978
+ compactAdapterCoverageRecord(importResult),
3979
+ ...imports.map((imported) => compactAdapterCoverageRecord(imported))
3980
+ ].filter(Boolean));
3981
+ const observed = records.reduce((totals, record) => {
3982
+ for (const key of ['diagnostics', 'losses', 'nativeAstNodes', 'semanticSymbols', 'sourceMapMappings']) {
3983
+ totals[key] += record.observed?.[key] ?? 0;
3984
+ }
3985
+ totals.sourceRanges = totals.sourceRanges || record.observed?.sourceRanges === true;
3986
+ totals.generatedRanges = totals.generatedRanges || record.observed?.generatedRanges === true;
3987
+ return totals;
3988
+ }, {
3989
+ diagnostics: 0,
3990
+ losses: 0,
3991
+ nativeAstNodes: 0,
3992
+ semanticSymbols: 0,
3993
+ sourceMapMappings: 0,
3994
+ sourceRanges: false,
3995
+ generatedRanges: false
3996
+ });
3997
+ return {
3998
+ total: records.length,
3999
+ adapterIds: uniqueStrings(records.map((record) => record.adapterId).filter(Boolean)),
4000
+ parsers: uniqueStrings(records.map((record) => record.parser).filter(Boolean)),
4001
+ exactness: uniqueStrings(records.map((record) => record.exactness).filter(Boolean)),
4002
+ exactAst: records.filter((record) => record.exactAst).length,
4003
+ tokens: records.filter((record) => record.tokens).length,
4004
+ trivia: records.filter((record) => record.trivia).length,
4005
+ diagnostics: records.filter((record) => record.diagnostics).length,
4006
+ sourceRanges: records.filter((record) => record.sourceRanges).length,
4007
+ generatedRanges: records.filter((record) => record.generatedRanges).length,
4008
+ semanticCoverageLevels: uniqueStrings(records.map((record) => record.semanticCoverage?.level).filter(Boolean)),
4009
+ observed,
4010
+ records
4011
+ };
4012
+ }
4013
+
4014
+ function compactAdapterCoverageRecord(imported) {
4015
+ const nativeAst = imported?.nativeAst ?? imported?.nativeSource?.ast;
4016
+ const nativeSource = imported?.nativeSource;
4017
+ const coverage = imported?.adapter?.coverage
4018
+ ?? imported?.metadata?.adapterCoverage
4019
+ ?? nativeAst?.metadata?.adapterCoverage
4020
+ ?? nativeSource?.metadata?.adapterCoverage;
4021
+ if (!coverage) return undefined;
4022
+ return {
4023
+ adapterId: imported?.adapter?.id ?? imported?.metadata?.adapterId ?? nativeAst?.metadata?.adapterId ?? nativeSource?.metadata?.adapterId,
4024
+ adapterVersion: imported?.adapter?.version ?? imported?.metadata?.adapterVersion ?? nativeAst?.metadata?.adapterVersion ?? nativeSource?.metadata?.adapterVersion,
4025
+ parser: imported?.adapter?.parser ?? nativeAst?.parser ?? nativeSource?.parser ?? imported?.metadata?.parser,
4026
+ capabilities: uniqueStrings(imported?.adapter?.capabilities ?? imported?.metadata?.adapterCapabilities ?? []),
4027
+ supportedExtensions: uniqueStrings(imported?.adapter?.supportedExtensions ?? imported?.metadata?.supportedExtensions ?? []),
4028
+ exactness: coverage.exactness,
4029
+ exactAst: Boolean(coverage.exactAst),
4030
+ tokens: Boolean(coverage.tokens),
4031
+ trivia: Boolean(coverage.trivia),
4032
+ diagnostics: Boolean(coverage.diagnostics),
4033
+ sourceRanges: Boolean(coverage.sourceRanges),
4034
+ generatedRanges: Boolean(coverage.generatedRanges),
4035
+ semanticCoverage: coverage.semanticCoverage,
4036
+ observed: coverage.observed,
4037
+ notes: uniqueStrings(coverage.notes ?? [])
4038
+ };
4039
+ }
4040
+
4041
+ function uniqueAdapterCoverageRecords(records) {
4042
+ const seen = new Set();
4043
+ const result = [];
4044
+ for (const record of records) {
4045
+ const key = [record.adapterId, record.adapterVersion, record.parser, record.exactness].join('#');
4046
+ if (seen.has(key)) continue;
4047
+ seen.add(key);
4048
+ result.push(record);
4049
+ }
4050
+ return result;
4051
+ }
4052
+
4053
+ function compactImportContractSource(imported, index) {
4054
+ const nativeAst = imported?.nativeAst ?? imported?.nativeSource?.ast;
4055
+ const nativeSource = imported?.nativeSource;
4056
+ const semanticIndex = imported?.semanticIndex ?? imported?.universalAst?.semanticIndex;
4057
+ const sourceMaps = collectImportSourceMaps(imported, [imported].filter(Boolean));
4058
+ return {
4059
+ id: imported?.id ?? `import_${index + 1}`,
4060
+ language: imported?.language ?? nativeSource?.language ?? nativeAst?.language,
4061
+ sourcePath: imported?.sourcePath ?? nativeSource?.sourcePath ?? nativeAst?.sourcePath,
4062
+ sourceHash: nativeSource?.sourceHash ?? nativeAst?.sourceHash,
4063
+ parser: nativeAst?.parser ?? nativeSource?.parser,
4064
+ nativeSourceId: nativeSource?.id,
4065
+ nativeAstId: nativeAst?.id,
4066
+ semanticIndexId: semanticIndex?.id,
4067
+ universalAstId: imported?.universalAst?.id,
4068
+ patchId: imported?.patch?.id,
4069
+ sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id).filter(Boolean),
4070
+ sourceMapMappings: sourceMaps.reduce((sum, sourceMap) => sum + (sourceMap.mappings?.length ?? 0), 0),
4071
+ symbolCount: semanticIndex?.symbols?.length ?? 0,
4072
+ lossCount: imported?.losses?.length ?? nativeAst?.losses?.length ?? 0,
4073
+ evidenceCount: imported?.evidence?.length ?? 0,
4074
+ readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness
4075
+ };
4076
+ }
4077
+
4078
+ function summarizeImportContractReadiness(importResult, mergeCandidates, lossSummary) {
4079
+ const candidateReadiness = mergeCandidates.reduce(
4080
+ (current, candidate) => maxSemanticMergeReadiness(current, candidate.readiness),
4081
+ lossSummary.semanticMergeReadiness
4082
+ );
4083
+ const semanticMergeReadiness = maxSemanticMergeReadiness(
4084
+ importResult?.metadata?.semanticMergeReadiness ?? lossSummary.semanticMergeReadiness,
4085
+ candidateReadiness
4086
+ );
4087
+ return {
4088
+ semanticMergeReadiness,
4089
+ severityReadiness: lossSummary.semanticMergeReadiness,
4090
+ reasons: uniqueStrings([
4091
+ ...(lossSummary.readinessReasons ?? []),
4092
+ ...mergeCandidates.flatMap((candidate) => candidate?.reasons ?? []),
4093
+ ...normalizeStringList(importResult?.metadata?.readinessReasons)
4094
+ ]),
4095
+ failedEvidenceIds: lossSummary.failedEvidenceIds,
4096
+ blockingLossIds: lossSummary.blockingLossIds,
4097
+ reviewLossIds: lossSummary.reviewLossIds,
4098
+ informationalLossIds: lossSummary.informationalLossIds
4099
+ };
4100
+ }
4101
+
4102
+ function defaultSemanticImportSidecarId(importResult, imports = []) {
4103
+ return `semantic_import_${idFragment(importResult?.id ?? importResult?.projectRoot ?? imports[0]?.sourcePath ?? imports[0]?.language ?? 'source')}`;
4104
+ }
4105
+
4106
+ function countBy(values) {
4107
+ const counts = {};
4108
+ for (const value of values ?? []) {
4109
+ const key = String(value ?? 'unknown');
4110
+ counts[key] = (counts[key] ?? 0) + 1;
4111
+ }
4112
+ return counts;
4113
+ }
4114
+
3430
4115
  function maxSemanticMergeReadiness(left, right) {
3431
4116
  const leftRank = semanticMergeReadinessRank[left] ?? semanticMergeReadinessRank['needs-review'];
3432
4117
  const rightRank = semanticMergeReadinessRank[right] ?? semanticMergeReadinessRank['needs-review'];
@@ -3822,7 +4507,8 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
3822
4507
  declaration.nativeNode.metadata = {
3823
4508
  ...declaration.nativeNode.metadata,
3824
4509
  ownershipRegionId: ownershipRegion.id,
3825
- ownershipRegionKey: ownershipRegion.key
4510
+ ownershipRegionKey: ownershipRegion.key,
4511
+ ownershipRegionKind: ownershipRegion.regionKind
3826
4512
  };
3827
4513
  symbols.push({
3828
4514
  id: symbolId,
@@ -3835,7 +4521,8 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
3835
4521
  definitionSpan: declaration.nativeNode.span,
3836
4522
  metadata: {
3837
4523
  ownershipRegionId: ownershipRegion.id,
3838
- ownershipRegionKey: ownershipRegion.key
4524
+ ownershipRegionKey: ownershipRegion.key,
4525
+ ownershipRegionKind: ownershipRegion.regionKind
3839
4526
  }
3840
4527
  });
3841
4528
  occurrences.push({
@@ -3862,6 +4549,15 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
3862
4549
  predicate: 'semanticOwnershipRegion',
3863
4550
  subjectId: symbolId,
3864
4551
  value: ownershipRegion
4552
+ }, {
4553
+ id: `fact_${idFragment(declaration.nativeNode.id)}_ownership_region_taxonomy`,
4554
+ predicate: 'semanticOwnershipRegionTaxonomy',
4555
+ subjectId: symbolId,
4556
+ value: {
4557
+ regionKind: ownershipRegion.regionKind,
4558
+ granularity: ownershipRegion.granularity,
4559
+ key: ownershipRegion.key
4560
+ }
3865
4561
  });
3866
4562
  mappings.push({
3867
4563
  id: `map_${idFragment(declaration.nativeNode.id)}`,
@@ -3872,6 +4568,8 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
3872
4568
  evidenceIds: [evidenceId],
3873
4569
  lossIds: [],
3874
4570
  ownershipRegionId: ownershipRegion.id,
4571
+ ownershipRegionKey: ownershipRegion.key,
4572
+ ownershipRegionKind: ownershipRegion.regionKind,
3875
4573
  precision: declaration.nativeNode.span ? 'declaration' : 'unknown'
3876
4574
  });
3877
4575
  }
@@ -3991,7 +4689,7 @@ function createNativeProjectImportResult(input, imports) {
3991
4689
  sourcePreservationSummary
3992
4690
  }
3993
4691
  });
3994
- return {
4692
+ const projectResult = {
3995
4693
  kind: 'frontier.lang.projectImportResult',
3996
4694
  version: 1,
3997
4695
  id: input.id ?? `project_import_${idPart}`,
@@ -4015,6 +4713,21 @@ function createNativeProjectImportResult(input, imports) {
4015
4713
  ...input.metadata
4016
4714
  }
4017
4715
  };
4716
+ const importResultContract = createNativeImportResultContract(projectResult, {
4717
+ lossSummary: nativeImportLossSummary
4718
+ });
4719
+ return {
4720
+ ...projectResult,
4721
+ metadata: {
4722
+ ...projectResult.metadata,
4723
+ importResultContract,
4724
+ semanticMergeReadiness: importResultContract.readiness.semanticMergeReadiness,
4725
+ readinessReasons: importResultContract.readiness.reasons,
4726
+ regionSummary: importResultContract.regions,
4727
+ sourceMapSummary: importResultContract.sourceMaps,
4728
+ adapterCoverageSummary: importResultContract.adapterCoverage
4729
+ }
4730
+ };
4018
4731
  }
4019
4732
 
4020
4733
  function summarizeProjectSourcePreservation(imports) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",