@shapeshift-labs/frontier-lang-compiler 0.2.33 → 0.2.35

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.js CHANGED
@@ -6,7 +6,9 @@ import {
6
6
  createSemanticIndexRecord,
7
7
  createSemanticMergeCandidateRecord,
8
8
  createSourceMapRecord,
9
+ createSourcePreservationRecord,
9
10
  createUniversalAstEnvelope,
11
+ explainSourcePreservation,
10
12
  hashDocumentBase,
11
13
  hashSemanticValue,
12
14
  nativeSourceNode,
@@ -277,6 +279,24 @@ export const ProjectionTargetLossClasses = Object.freeze([
277
279
  'missingAdapter'
278
280
  ]);
279
281
 
282
+ export const NativeParserFeatureCategories = Object.freeze([
283
+ 'syntax',
284
+ 'semantic',
285
+ 'type',
286
+ 'controlFlow',
287
+ 'macroMetaprogramming',
288
+ 'sourcePreservation'
289
+ ]);
290
+
291
+ export const NativeParserFeatureCoverageStatuses = Object.freeze([
292
+ 'full',
293
+ 'partial',
294
+ 'evidence-required',
295
+ 'missing',
296
+ 'blocked',
297
+ 'not-applicable'
298
+ ]);
299
+
280
300
  export const NativeImportLanguageProfiles = Object.freeze([
281
301
  nativeImportLanguageProfile('javascript', {
282
302
  aliases: ['js', 'mjs', 'cjs', 'jsx'],
@@ -2369,6 +2389,78 @@ export function createNativeParserAstFormatMatrix(input = {}) {
2369
2389
  };
2370
2390
  }
2371
2391
 
2392
+ export function createNativeParserFeatureMatrix(input = {}) {
2393
+ const imports = input.imports ?? [];
2394
+ const adapters = input.adapters ?? [];
2395
+ const profiles = mergeNativeImportProfiles(input.languages ?? NativeImportLanguageProfiles, imports, adapters);
2396
+ const parsers = nativeParserFeatureRowsForProfiles(profiles, {
2397
+ imports,
2398
+ adapters,
2399
+ requiredFeatures: input.requiredFeatures,
2400
+ minimumReadiness: input.minimumReadiness,
2401
+ includeEmptyParsers: input.includeEmptyParsers
2402
+ });
2403
+ const summary = nativeParserFeatureMatrixSummary(parsers);
2404
+ return {
2405
+ kind: 'frontier.lang.nativeParserFeatureMatrix',
2406
+ version: 1,
2407
+ generatedAt: input.generatedAt ?? Date.now(),
2408
+ parsers,
2409
+ languages: summarizeNativeParserFeatureLanguages(profiles, parsers),
2410
+ summary,
2411
+ metadata: {
2412
+ categories: [...NativeParserFeatureCategories],
2413
+ statuses: [...NativeParserFeatureCoverageStatuses],
2414
+ requiredFeatures: normalizeNativeParserRequiredFeatures(input.requiredFeatures),
2415
+ minimumReadiness: normalizeSemanticMergeReadiness(input.minimumReadiness ?? 'ready'),
2416
+ note: 'Native parser feature coverage is admission evidence per language/parser. It does not promote lightweight scans or host adapters beyond their declared and observed capabilities.'
2417
+ }
2418
+ };
2419
+ }
2420
+
2421
+ export function queryNativeParserFeatureMatrix(matrixOrInput = {}, query = {}) {
2422
+ const matrix = matrixOrInput?.kind === 'frontier.lang.nativeParserFeatureMatrix'
2423
+ ? matrixOrInput
2424
+ : createNativeParserFeatureMatrix(matrixOrInput);
2425
+ const language = normalizeNativeLanguageId(query.language);
2426
+ const parser = query.parser === undefined ? undefined : parserAstFormatIdForParser(query.parser);
2427
+ const requiredFeatures = normalizeNativeParserRequiredFeatures(query.requiredFeatures ?? matrix.metadata?.requiredFeatures);
2428
+ const minimumReadiness = normalizeSemanticMergeReadiness(query.minimumReadiness ?? matrix.metadata?.minimumReadiness ?? 'ready');
2429
+ const row = matrix.parsers.find((entry) => {
2430
+ if (language && normalizeNativeLanguageId(entry.language) !== language && !(entry.aliases ?? []).map(normalizeNativeLanguageId).includes(language)) {
2431
+ return false;
2432
+ }
2433
+ if (!parser) return true;
2434
+ const parserIds = [
2435
+ entry.parser,
2436
+ entry.parserFormat,
2437
+ ...(entry.parserAliases ?? []),
2438
+ ...(entry.parserAdapters ?? [])
2439
+ ].map(parserAstFormatIdForParser);
2440
+ return parserIds.includes(parser);
2441
+ });
2442
+ const merge = row
2443
+ ? nativeParserFeatureMergeAssessment(row, { requiredFeatures, minimumReadiness })
2444
+ : {
2445
+ mergeReady: false,
2446
+ readiness: 'blocked',
2447
+ requiredFeatures,
2448
+ minimumReadiness,
2449
+ blockingFeatures: requiredFeatures,
2450
+ reviewFeatures: [],
2451
+ reasons: [`No native parser feature coverage row matched language=${query.language ?? '*'} parser=${query.parser ?? '*'}.`]
2452
+ };
2453
+ return {
2454
+ kind: 'frontier.lang.nativeParserFeatureQuery',
2455
+ version: 1,
2456
+ found: Boolean(row),
2457
+ language: row?.language ?? language,
2458
+ parser: row?.parser ?? parser,
2459
+ row,
2460
+ merge
2461
+ };
2462
+ }
2463
+
2372
2464
  export function createProjectionTargetLossMatrix(input = {}) {
2373
2465
  const imports = input.imports ?? [];
2374
2466
  const adapters = input.adapters ?? [];
@@ -2472,6 +2564,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
2472
2564
  const mergeCandidates = imports.flatMap((imported) => imported?.mergeCandidates ?? []);
2473
2565
  const lossSummary = summarizeNativeImportLosses(losses, { evidence });
2474
2566
  const regionTaxonomy = summarizeSemanticImportRegionTaxonomy(ownershipRegions);
2567
+ const sourcePreservation = summarizeKernelSourcePreservation(importResult, imports);
2475
2568
  const readiness = mergeCandidates.reduce(
2476
2569
  (current, candidate) => maxSemanticMergeReadiness(current, candidate.readiness),
2477
2570
  lossSummary.semanticMergeReadiness
@@ -2492,6 +2585,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
2492
2585
  mappings: sourceMapMappings.length,
2493
2586
  ids: sourceMaps.map((sourceMap) => sourceMap.id).filter(Boolean)
2494
2587
  },
2588
+ sourcePreservation,
2495
2589
  patchHints,
2496
2590
  mergeCandidates: mergeCandidates.map((candidate) => ({
2497
2591
  id: candidate.id,
@@ -2520,6 +2614,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
2520
2614
  ownershipRegions: ownershipRegions.length,
2521
2615
  regionKinds: regionTaxonomy.presentKinds.length,
2522
2616
  sourceMapMappings: sourceMapMappings.length,
2617
+ sourcePreservationRecords: sourcePreservation.total,
2523
2618
  readiness,
2524
2619
  emptySemanticIndex: symbols.length === 0
2525
2620
  },
@@ -3719,6 +3814,7 @@ export function importNativeSource(input) {
3719
3814
  nativeSource,
3720
3815
  evidence,
3721
3816
  losses,
3817
+ sourcePreservation,
3722
3818
  target: input.target,
3723
3819
  targetPath,
3724
3820
  targetHash
@@ -3753,6 +3849,7 @@ export function importNativeSource(input) {
3753
3849
  semanticIndex,
3754
3850
  evidence,
3755
3851
  losses,
3852
+ sourcePreservation,
3756
3853
  target: input.target,
3757
3854
  targetPath: inferredTargetPath,
3758
3855
  targetHash,
@@ -3760,6 +3857,20 @@ export function importNativeSource(input) {
3760
3857
  sourceHash,
3761
3858
  defaultSourceMapId: `source_map_${importIdPart}`
3762
3859
  });
3860
+ const sourcePreservationRecords = createKernelSourcePreservationRecords({
3861
+ idPart: importIdPart,
3862
+ language,
3863
+ sourcePath,
3864
+ sourceHash,
3865
+ sourcePreservation,
3866
+ sourceMaps,
3867
+ losses,
3868
+ evidence,
3869
+ nativeSource,
3870
+ nativeAst,
3871
+ semanticIndex
3872
+ });
3873
+ const kernelSourcePreservationSummary = summarizeKernelSourcePreservationRecords(sourcePreservationRecords);
3763
3874
  const resultSourceMapMappings = sourceMaps.flatMap((sourceMap) => sourceMap.mappings ?? []);
3764
3875
  const universalAst = createUniversalAstEnvelope({
3765
3876
  id: input.universalAstId ?? `universal_ast_${importIdPart}`,
@@ -3778,6 +3889,11 @@ export function importNativeSource(input) {
3778
3889
  sourcePreservationId: sourcePreservation.id,
3779
3890
  sourcePreservation
3780
3891
  } : {}),
3892
+ ...(sourcePreservationRecords.length ? {
3893
+ sourcePreservationRecords,
3894
+ kernelSourcePreservationRecords: sourcePreservationRecords,
3895
+ kernelSourcePreservationSummary
3896
+ } : {}),
3781
3897
  ...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
3782
3898
  declaredSourceHash,
3783
3899
  sourceHashVerified: false
@@ -3805,6 +3921,10 @@ export function importNativeSource(input) {
3805
3921
  sourcePreservationId: sourcePreservation.id,
3806
3922
  sourcePreservationSummary: sourcePreservation.summary
3807
3923
  } : {}),
3924
+ ...(sourcePreservationRecords.length ? {
3925
+ kernelSourcePreservationRecordIds: sourcePreservationRecords.map((record) => record.id),
3926
+ kernelSourcePreservationSummary
3927
+ } : {}),
3808
3928
  ...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
3809
3929
  declaredSourceHash,
3810
3930
  sourceHashVerified: false
@@ -3835,6 +3955,11 @@ export function importNativeSource(input) {
3835
3955
  sourcePreservationId: sourcePreservation.id,
3836
3956
  sourcePreservation
3837
3957
  } : {}),
3958
+ ...(sourcePreservationRecords.length ? {
3959
+ sourcePreservationRecords,
3960
+ kernelSourcePreservationRecords: sourcePreservationRecords,
3961
+ kernelSourcePreservationSummary
3962
+ } : {}),
3838
3963
  ...(declaredSourceHash && declaredSourceHash !== sourceHash ? {
3839
3964
  declaredSourceHash,
3840
3965
  sourceHashVerified: false
@@ -6481,6 +6606,8 @@ function normalizeSourceMapMappings(mappings, context) {
6481
6606
  const sourceSpan = mapping.sourceSpan ?? occurrence?.span ?? nativeNode?.span;
6482
6607
  const target = mapping.target ?? mapping.generatedSpan?.target ?? context.target;
6483
6608
  const generatedSpan = normalizeGeneratedSpan(mapping.generatedSpan, target, context.targetPath, context.targetHash);
6609
+ const mappingLossIds = normalizeReferenceIds(mapping.lossIds, lossIdsForNativeNode(context.losses ?? nativeAst?.losses ?? [], nativeAstNodeId));
6610
+ const precision = normalizeSourceMapPrecision(mapping.precision, sourceSpan, generatedSpan);
6484
6611
  if (
6485
6612
  !nativeAstNodeId &&
6486
6613
  !mapping.semanticNodeId &&
@@ -6503,11 +6630,17 @@ function normalizeSourceMapMappings(mappings, context) {
6503
6630
  generatedSpan,
6504
6631
  target,
6505
6632
  evidenceIds: normalizeReferenceIds(mapping.evidenceIds, evidenceIds),
6506
- lossIds: normalizeReferenceIds(mapping.lossIds, lossIdsForNativeNode(context.losses ?? nativeAst?.losses ?? [], nativeAstNodeId)),
6633
+ lossIds: mappingLossIds,
6507
6634
  ownershipRegionId: mapping.ownershipRegionId ?? symbol?.metadata?.ownershipRegionId,
6508
6635
  ownershipRegionKey: mapping.ownershipRegionKey ?? symbol?.metadata?.ownershipRegionKey,
6509
6636
  ownershipRegionKind: mapping.ownershipRegionKind ?? symbol?.metadata?.ownershipRegionKind,
6510
- precision: normalizeSourceMapPrecision(mapping.precision, sourceSpan, generatedSpan)
6637
+ precision,
6638
+ preservation: normalizeSourcePreservationLevel(mapping.preservation, {
6639
+ precision,
6640
+ lossIds: mappingLossIds,
6641
+ losses: context.losses ?? nativeAst?.losses ?? [],
6642
+ sourcePreservation: context.sourcePreservation
6643
+ })
6511
6644
  };
6512
6645
  return {
6513
6646
  ...normalizedMapping,
@@ -6588,10 +6721,19 @@ function normalizeSourceMapPrecision(value, sourceSpan, generatedSpan) {
6588
6721
  const explicit = value === undefined || value === null ? '' : String(value).trim();
6589
6722
  if (explicit) {
6590
6723
  const normalized = explicit.toLowerCase();
6591
- if (normalized === 'exact' || normalized === 'declaration' || normalized === 'line' || normalized === 'estimated' || normalized === 'unknown') return normalized;
6724
+ if (normalized === 'exact') {
6725
+ return hasExactSpan(sourceSpan) && hasExactSpan(generatedSpan)
6726
+ ? 'exact'
6727
+ : inferSourceMapPrecisionFromSpans(sourceSpan, generatedSpan);
6728
+ }
6729
+ if (normalized === 'declaration' || normalized === 'line' || normalized === 'estimated' || normalized === 'unknown') return normalized;
6592
6730
  if (normalized === 'estimate' || normalized === 'approx' || normalized === 'approximate' || normalized === 'approximated') return 'estimated';
6593
6731
  return explicit;
6594
6732
  }
6733
+ return inferSourceMapPrecisionFromSpans(sourceSpan, generatedSpan);
6734
+ }
6735
+
6736
+ function inferSourceMapPrecisionFromSpans(sourceSpan, generatedSpan) {
6595
6737
  if (hasExactSpan(sourceSpan) && hasExactSpan(generatedSpan)) return 'exact';
6596
6738
  if (sourceSpan?.startLine && generatedSpan?.startLine) return 'line';
6597
6739
  if (sourceSpan?.startLine) return 'declaration';
@@ -6599,6 +6741,25 @@ function normalizeSourceMapPrecision(value, sourceSpan, generatedSpan) {
6599
6741
  return 'unknown';
6600
6742
  }
6601
6743
 
6744
+ function normalizeSourcePreservationLevel(value, context = {}) {
6745
+ const explicit = value === undefined || value === null ? '' : String(value).trim();
6746
+ if (explicit) {
6747
+ const normalized = explicit.toLowerCase();
6748
+ if (normalized === 'exact' || normalized === 'declaration' || normalized === 'estimated' || normalized === 'blocked') return normalized;
6749
+ if (normalized === 'estimate' || normalized === 'approx' || normalized === 'approximate' || normalized === 'approximated' || normalized === 'line') return 'estimated';
6750
+ return explicit;
6751
+ }
6752
+
6753
+ const lossIds = new Set(context.lossIds ?? []);
6754
+ const linkedLosses = (context.losses ?? []).filter((loss) => lossIds.has(loss.id));
6755
+ if (linkedLosses.some((loss) => loss.severity === 'error')) return 'blocked';
6756
+ if (context.precision === 'exact') return 'exact';
6757
+ if (context.precision === 'declaration') return 'declaration';
6758
+ if (context.precision === 'line' || context.precision === 'estimated' || context.precision === 'unknown') return 'estimated';
6759
+ if (context.sourcePreservation?.summary?.exactSourceAvailable === true) return 'estimated';
6760
+ return 'estimated';
6761
+ }
6762
+
6602
6763
  function hasExactSpan(span) {
6603
6764
  return Boolean(span && (
6604
6765
  (typeof span.start === 'number' && typeof span.end === 'number') ||
@@ -7312,6 +7473,12 @@ function nativeSourceCompilePreservedMappings(input) {
7312
7473
  ownershipRegionKey: mapping.ownershipRegionKey,
7313
7474
  ownershipRegionKind: mapping.ownershipRegionKind,
7314
7475
  precision: exact ? 'exact' : mapping.precision === 'exact' ? 'line' : mapping.precision ?? 'line',
7476
+ preservation: exact ? 'exact' : normalizeSourcePreservationLevel(mapping.preservation, {
7477
+ precision: mapping.precision === 'exact' ? 'line' : mapping.precision ?? 'line',
7478
+ lossIds: mapping.lossIds,
7479
+ losses: input.losses ?? [],
7480
+ sourcePreservation: input.importResult.metadata?.sourcePreservation
7481
+ }),
7315
7482
  metadata: {
7316
7483
  ...mapping.metadata,
7317
7484
  compileResultId: input.compileResultId,
@@ -7339,6 +7506,7 @@ function nativeSourceCompileDeclarationMappings(input) {
7339
7506
  ownershipRegionKey: declaration.metadata?.ownershipRegionKey,
7340
7507
  ownershipRegionKind: declaration.metadata?.ownershipRegionKind,
7341
7508
  precision: generated.exactName ? 'declaration' : 'estimated',
7509
+ preservation: generated.exactName ? 'declaration' : 'estimated',
7342
7510
  metadata: {
7343
7511
  ...declaration.metadata,
7344
7512
  compileResultId: input.compileResultId,
@@ -7361,6 +7529,7 @@ function nativeSourceCompileFileMapping(input) {
7361
7529
  target: input.target,
7362
7530
  evidenceIds: (input.evidence ?? []).map((record) => record.id).filter(Boolean),
7363
7531
  precision: input.projection.mode === 'preserved-source' && input.outputHash === input.projection.sourceHash ? 'line' : 'estimated',
7532
+ preservation: input.losses?.some((loss) => loss.severity === 'error') ? 'blocked' : 'estimated',
7364
7533
  metadata: {
7365
7534
  compileResultId: input.compileResultId,
7366
7535
  sourceMapOrigin: 'file-fallback'
@@ -7523,6 +7692,557 @@ function normalizeParserAstFormatId(format) {
7523
7692
  return String(format ?? '').trim().toLowerCase().replace(/[_\s]+/g, '-');
7524
7693
  }
7525
7694
 
7695
+ const nativeParserMacroMetaprogrammingLossKinds = new Set([
7696
+ 'macroExpansion',
7697
+ 'macroHygiene',
7698
+ 'preprocessor',
7699
+ 'conditionalCompilation',
7700
+ 'metaprogramming',
7701
+ 'reflection',
7702
+ 'generatedCode'
7703
+ ]);
7704
+
7705
+ const nativeParserTypeCoverageLossKinds = new Set([
7706
+ 'typeInference',
7707
+ 'overloadResolution',
7708
+ 'overloadTypeInference',
7709
+ 'unsupportedSemantic'
7710
+ ]);
7711
+
7712
+ function nativeParserFeatureRowsForProfiles(profiles, context) {
7713
+ const rows = [];
7714
+ for (const profile of profiles) {
7715
+ const matchingImports = nativeParserFeatureImportsForProfile(profile, context.imports);
7716
+ const matchingAdapters = nativeParserFeatureAdapterSummariesForProfile(profile, context.adapters);
7717
+ for (const parser of nativeParserFeatureParserSlots(profile, matchingImports, matchingAdapters)) {
7718
+ const row = nativeParserFeatureRowForParser(profile, parser, {
7719
+ ...context,
7720
+ imports: matchingImports.filter((imported) => nativeParserFeatureParserMatches(nativeParserParserForImport(imported), parser)),
7721
+ adapters: matchingAdapters.filter((adapter) => nativeParserFeatureParserMatches(adapter.parser, parser))
7722
+ });
7723
+ if (context.includeEmptyParsers === false && row.imports.total === 0 && row.adapters.total === 0) continue;
7724
+ rows.push(row);
7725
+ }
7726
+ }
7727
+ return rows.sort((left, right) => {
7728
+ const languageOrder = left.language.localeCompare(right.language);
7729
+ return languageOrder || left.parser.localeCompare(right.parser);
7730
+ });
7731
+ }
7732
+
7733
+ function nativeParserFeatureRowForParser(profile, parser, context) {
7734
+ const imports = context.imports ?? [];
7735
+ const adapters = context.adapters ?? [];
7736
+ const parserFormat = parserAstFormatIdForParser(parser);
7737
+ const parserProfile = getNativeParserAstFormatProfile(parserFormat);
7738
+ const adapterCoverage = summarizeNativeImporterAdapterCoverageEntries([
7739
+ ...imports.map((imported) => nativeImporterAdapterCoverageEntryFromImport(imported)).filter(Boolean),
7740
+ ...adapters.map((adapter) => ({
7741
+ adapterId: adapter.id,
7742
+ language: adapter.language,
7743
+ parser: adapter.parser,
7744
+ coverage: adapter.coverage
7745
+ }))
7746
+ ]);
7747
+ const losses = imports.flatMap((imported) => imported?.losses ?? imported?.nativeAst?.losses ?? []);
7748
+ const evidence = imports.flatMap((imported) => imported?.evidence ?? []);
7749
+ const lossSummary = summarizeNativeImportLosses(losses, { evidence, parser });
7750
+ const semanticEvidence = nativeParserFeatureSemanticEvidence(imports);
7751
+ const sourceMaps = imports.flatMap((imported) => imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? []);
7752
+ const sourceMapMappings = sourceMaps.reduce((sum, sourceMap) => sum + (sourceMap?.mappings?.length ?? 0), 0);
7753
+ const sourcePreservation = summarizeImportSourcePreservation(undefined, imports);
7754
+ const nativeAstNodes = imports.reduce((sum, imported) => sum + Object.keys(imported?.nativeAst?.nodes ?? imported?.nativeSource?.ast?.nodes ?? {}).length, 0);
7755
+ const featureContext = {
7756
+ profile,
7757
+ parser,
7758
+ parserFormat,
7759
+ parserProfile,
7760
+ imports,
7761
+ adapters,
7762
+ adapterCoverage,
7763
+ losses,
7764
+ evidence,
7765
+ lossSummary,
7766
+ semanticEvidence,
7767
+ sourceMaps,
7768
+ sourceMapMappings,
7769
+ sourcePreservation,
7770
+ nativeAstNodes
7771
+ };
7772
+ const features = {
7773
+ syntax: nativeParserSyntaxFeature(featureContext),
7774
+ semantic: nativeParserSemanticFeature(featureContext),
7775
+ type: nativeParserTypeFeature(featureContext),
7776
+ controlFlow: nativeParserControlFlowFeature(featureContext),
7777
+ macroMetaprogramming: nativeParserMacroMetaprogrammingFeature(featureContext),
7778
+ sourcePreservation: nativeParserSourcePreservationFeature(featureContext)
7779
+ };
7780
+ const importReadiness = imports.length
7781
+ ? lossSummary.semanticMergeReadiness
7782
+ : adapters.length ? 'needs-review' : normalizeSemanticMergeReadiness(profile.defaultReadiness) ?? 'needs-review';
7783
+ const row = {
7784
+ language: profile.language,
7785
+ aliases: profile.aliases,
7786
+ parser,
7787
+ parserFormat,
7788
+ parserAliases: uniqueStrings([...(parserProfile?.aliases ?? []), ...(parserProfile?.parserAdapters ?? [])]),
7789
+ parserAdapters: uniqueStrings([parser, ...(parserProfile?.parserAdapters ?? [])]),
7790
+ extensions: profile.extensions,
7791
+ supportsLightweightScan: profile.supportsLightweightScan,
7792
+ projectionTargets: profile.projectionTargets,
7793
+ knownLossKinds: uniqueStrings([...(profile.knownLossKinds ?? []), ...Object.keys(lossSummary.byKind)]),
7794
+ defaultReadiness: profile.defaultReadiness,
7795
+ notes: uniqueStrings([...(profile.notes ?? []), ...(parserProfile?.notes ?? [])]),
7796
+ adapters: {
7797
+ total: adapters.length,
7798
+ ids: adapters.map((adapter) => adapter.id),
7799
+ versions: uniqueStrings(adapters.map((adapter) => adapter.version).filter(Boolean)),
7800
+ exactness: uniqueStrings(adapters.map((adapter) => adapter.coverage?.exactness).filter(Boolean)),
7801
+ coverage: adapterCoverage
7802
+ },
7803
+ imports: {
7804
+ total: imports.length,
7805
+ sourcePaths: uniqueStrings(imports.map((imported) => imported?.sourcePath ?? imported?.nativeSource?.sourcePath ?? imported?.nativeAst?.sourcePath).filter(Boolean)),
7806
+ readiness: importReadiness,
7807
+ readinessReasons: imports.length ? lossSummary.readinessReasons : nativeImportCoverageReasons(profile),
7808
+ nativeAstNodes,
7809
+ symbols: semanticEvidence.symbols,
7810
+ references: semanticEvidence.references,
7811
+ types: semanticEvidence.types,
7812
+ controlFlow: semanticEvidence.controlFlow,
7813
+ sourceMaps: sourceMaps.length,
7814
+ sourceMapMappings,
7815
+ losses: lossSummary.total,
7816
+ lossKinds: lossSummary.byKind,
7817
+ lossCategories: lossSummary.categories,
7818
+ sourcePreservation
7819
+ },
7820
+ features
7821
+ };
7822
+ return {
7823
+ ...row,
7824
+ merge: nativeParserFeatureMergeAssessment(row, {
7825
+ requiredFeatures: context.requiredFeatures,
7826
+ minimumReadiness: context.minimumReadiness
7827
+ })
7828
+ };
7829
+ }
7830
+
7831
+ function nativeParserSyntaxFeature(context) {
7832
+ const blockingSyntaxLosses = context.losses.filter((loss) => loss.severity === 'error' && (loss.kind === 'unsupportedSyntax' || loss.kind === 'parserDiagnostic'));
7833
+ const exactAst = context.adapterCoverage.effective.exactAst ?? 0;
7834
+ const sourceRanges = context.adapterCoverage.effective.sourceRanges ?? 0;
7835
+ const parserDiagnostics = context.adapterCoverage.effective.parserDiagnostics ?? 0;
7836
+ let status = 'missing';
7837
+ const reasons = [];
7838
+ if (blockingSyntaxLosses.length) {
7839
+ status = 'blocked';
7840
+ reasons.push('Parser diagnostics or unsupported syntax errors block syntax coverage.');
7841
+ } else if (exactAst > 0 && (sourceRanges > 0 || context.sourceMapMappings > 0)) {
7842
+ status = 'full';
7843
+ reasons.push('Exact parser AST and source-range evidence are available.');
7844
+ } else if (exactAst > 0 || sourceRanges > 0 || context.nativeAstNodes > 1 || context.sourceMapMappings > 0) {
7845
+ status = 'partial';
7846
+ reasons.push('Syntax evidence exists, but exact AST/source-range coverage is incomplete.');
7847
+ } else if (context.adapters.length || context.parserProfile) {
7848
+ status = 'evidence-required';
7849
+ reasons.push('Parser slot is declared, but no observed syntax import evidence is attached.');
7850
+ } else {
7851
+ reasons.push('No syntax parser coverage is declared or observed.');
7852
+ }
7853
+ return nativeParserFeatureCoverage('syntax', status, {
7854
+ capabilities: {
7855
+ exactAst,
7856
+ sourceRanges,
7857
+ parserDiagnostics,
7858
+ nativeAstNodes: context.nativeAstNodes,
7859
+ sourceMapMappings: context.sourceMapMappings
7860
+ },
7861
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['exactAst', 'sourceRanges', 'parserDiagnostics']),
7862
+ lossKinds: nativeParserFeatureLossKindCounts(context.losses, ['unsupportedSyntax', 'parserDiagnostic']),
7863
+ reasons,
7864
+ notes: ['Syntax coverage covers parser AST/CST structure, diagnostics, source ranges, and source-map anchors.']
7865
+ });
7866
+ }
7867
+
7868
+ function nativeParserSemanticFeature(context) {
7869
+ const declarations = (context.adapterCoverage.effective.semanticDeclarations ?? 0) + context.semanticEvidence.declarations;
7870
+ const symbols = (context.adapterCoverage.effective.semanticSymbols ?? 0) + context.semanticEvidence.symbols;
7871
+ let status = 'missing';
7872
+ const reasons = [];
7873
+ if (symbols > 0 && declarations > 0) {
7874
+ status = 'full';
7875
+ reasons.push('Declaration and symbol evidence are available.');
7876
+ } else if (symbols > 0 || declarations > 0 || context.nativeAstNodes > 1) {
7877
+ status = 'partial';
7878
+ reasons.push('Semantic evidence is present, but declaration/symbol coverage is incomplete.');
7879
+ } else if (context.adapters.length || context.imports.length) {
7880
+ status = 'evidence-required';
7881
+ reasons.push('Import evidence exists, but no semantic declarations or symbols were observed.');
7882
+ } else {
7883
+ reasons.push('No semantic index evidence is declared or observed.');
7884
+ }
7885
+ return nativeParserFeatureCoverage('semantic', status, {
7886
+ capabilities: {
7887
+ declarations,
7888
+ symbols,
7889
+ semanticIndexLevel: nativeParserFeatureSemanticLevel(context.adapterCoverage, context.semanticEvidence)
7890
+ },
7891
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['semanticDeclarations', 'semanticSymbols']),
7892
+ lossKinds: nativeParserFeatureLossKindCounts(context.losses, ['partialSemanticIndex', 'unsupportedSemantic']),
7893
+ reasons,
7894
+ notes: ['Semantic coverage covers declaration and symbol evidence. References, types, and control flow are reported separately.']
7895
+ });
7896
+ }
7897
+
7898
+ function nativeParserTypeFeature(context) {
7899
+ const types = (context.adapterCoverage.effective.types ?? 0) + context.semanticEvidence.types;
7900
+ const typeLossKinds = nativeParserFeaturePresentLossKinds(context, nativeParserTypeCoverageLossKinds);
7901
+ let status = 'missing';
7902
+ const reasons = [];
7903
+ if (types > 0) {
7904
+ status = 'full';
7905
+ reasons.push('Resolved or declared type evidence is available.');
7906
+ } else if (typeLossKinds.length > 0 || context.semanticEvidence.symbols > 0) {
7907
+ status = 'evidence-required';
7908
+ reasons.push('Type-sensitive coverage needs compiler or language-server evidence.');
7909
+ } else {
7910
+ reasons.push('No type evidence is declared or observed.');
7911
+ }
7912
+ return nativeParserFeatureCoverage('type', status, {
7913
+ capabilities: { types },
7914
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['types']),
7915
+ lossKinds: nativeParserFeatureLossKindCounts(context.losses, [...nativeParserTypeCoverageLossKinds]),
7916
+ reasons,
7917
+ notes: ['Type coverage covers declared/inferred type facts and overload or inference evidence.']
7918
+ });
7919
+ }
7920
+
7921
+ function nativeParserControlFlowFeature(context) {
7922
+ const controlFlow = (context.adapterCoverage.effective.controlFlow ?? 0) + context.semanticEvidence.controlFlow;
7923
+ let status = 'missing';
7924
+ const reasons = [];
7925
+ if (controlFlow > 0) {
7926
+ status = 'full';
7927
+ reasons.push('Control-flow or CFG evidence is available.');
7928
+ } else if (context.imports.length || context.adapters.length) {
7929
+ status = 'evidence-required';
7930
+ reasons.push('Control-flow evidence was not observed for this parser row.');
7931
+ } else {
7932
+ reasons.push('No control-flow evidence is declared or observed.');
7933
+ }
7934
+ return nativeParserFeatureCoverage('controlFlow', status, {
7935
+ capabilities: { controlFlow },
7936
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['controlFlow']),
7937
+ lossKinds: {},
7938
+ reasons,
7939
+ notes: ['Control-flow coverage covers call/branch/CFG facts supplied by host parsers or semantic indexers.']
7940
+ });
7941
+ }
7942
+
7943
+ function nativeParserMacroMetaprogrammingFeature(context) {
7944
+ const macroLossKinds = nativeParserFeaturePresentLossKinds(context, nativeParserMacroMetaprogrammingLossKinds);
7945
+ const macroLosses = context.losses.filter((loss) => nativeParserMacroMetaprogrammingLossKinds.has(loss.kind));
7946
+ const featureEvidence = summarizeNativeImportFeatureEvidence(macroLosses, { evidence: context.evidence });
7947
+ const generatedRanges = context.adapterCoverage.effective.generatedRanges ?? 0;
7948
+ let status = 'not-applicable';
7949
+ const reasons = [];
7950
+ if (!macroLossKinds.length) {
7951
+ reasons.push('No macro, preprocessor, generator, or metaprogramming coverage risk is declared for this parser row.');
7952
+ } else if (macroLosses.some((loss) => loss.severity === 'error')) {
7953
+ status = 'blocked';
7954
+ reasons.push('Macro or metaprogramming evidence includes blocking loss records.');
7955
+ } else if (featureEvidence.missingRequiredEvidence.length > 0 || generatedRanges === 0) {
7956
+ status = 'evidence-required';
7957
+ reasons.push('Macro/metaprogramming coverage requires generated-range and policy evidence before merge admission.');
7958
+ } else {
7959
+ status = 'partial';
7960
+ reasons.push('Macro/metaprogramming risk has attached evidence, but this facade still treats generated behavior as review-required.');
7961
+ }
7962
+ return nativeParserFeatureCoverage('macroMetaprogramming', status, {
7963
+ capabilities: {
7964
+ generatedRanges,
7965
+ policyKinds: featureEvidence.policyKinds,
7966
+ highestRisk: featureEvidence.highestRisk
7967
+ },
7968
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['generatedRanges']),
7969
+ lossKinds: nativeParserFeatureLossKindCounts(context.losses, [...nativeParserMacroMetaprogrammingLossKinds]),
7970
+ reasons: uniqueStrings([...reasons, ...featureEvidence.reasons]),
7971
+ notes: ['Macro/metaprogramming coverage covers macros, preprocessors, generated code, reflection, and conditional compilation evidence.']
7972
+ });
7973
+ }
7974
+
7975
+ function nativeParserSourcePreservationFeature(context) {
7976
+ const exactSource = context.sourcePreservation.exactSourceAvailable;
7977
+ const tokens = context.sourcePreservation.tokens + (context.adapterCoverage.effective.tokens ?? 0);
7978
+ const trivia = context.sourcePreservation.trivia + (context.adapterCoverage.effective.trivia ?? 0);
7979
+ const sourceRanges = context.adapterCoverage.effective.sourceRanges ?? 0;
7980
+ let status = 'missing';
7981
+ const reasons = [];
7982
+ if (exactSource > 0 && (tokens > 0 || trivia > 0 || sourceRanges > 0)) {
7983
+ status = 'full';
7984
+ reasons.push('Exact source text and token/trivia or source-range evidence are available.');
7985
+ } else if (exactSource > 0 || tokens > 0 || trivia > 0 || sourceRanges > 0) {
7986
+ status = 'partial';
7987
+ reasons.push('Source-preservation evidence exists, but exact source, tokens, trivia, or ranges are incomplete.');
7988
+ } else if (context.imports.length || context.adapters.length) {
7989
+ status = 'evidence-required';
7990
+ reasons.push('Import or adapter evidence exists, but no exact source-preservation record was observed.');
7991
+ } else {
7992
+ reasons.push('No source-preservation evidence is declared or observed.');
7993
+ }
7994
+ return nativeParserFeatureCoverage('sourcePreservation', status, {
7995
+ capabilities: {
7996
+ exactSourceAvailable: exactSource,
7997
+ tokens,
7998
+ trivia,
7999
+ comments: context.sourcePreservation.comments,
8000
+ whitespace: context.sourcePreservation.whitespace,
8001
+ directives: context.sourcePreservation.directives,
8002
+ sourceRanges
8003
+ },
8004
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['tokens', 'trivia', 'sourceRanges']),
8005
+ lossKinds: nativeParserFeatureLossKindCounts(context.losses, ['sourcePreservation', 'commentsTrivia', 'sourceMapApproximation']),
8006
+ reasons,
8007
+ notes: ['Source-preservation coverage covers exact source text, token/trivia retention, comments, whitespace, directives, and source ranges.']
8008
+ });
8009
+ }
8010
+
8011
+ function nativeParserFeatureCoverage(category, status, input = {}) {
8012
+ const normalizedStatus = NativeParserFeatureCoverageStatuses.includes(status) ? status : 'missing';
8013
+ return Object.freeze({
8014
+ category,
8015
+ status: normalizedStatus,
8016
+ readiness: nativeParserFeatureReadinessForStatus(normalizedStatus),
8017
+ mergeReady: nativeParserFeatureStatusMergeReady(normalizedStatus),
8018
+ supported: normalizedStatus === 'full' || normalizedStatus === 'partial' || normalizedStatus === 'not-applicable',
8019
+ capabilities: Object.freeze(input.capabilities ?? {}),
8020
+ gaps: Object.freeze(uniqueStrings(input.gaps ?? [])),
8021
+ lossKinds: Object.freeze(input.lossKinds ?? {}),
8022
+ reasons: Object.freeze(uniqueStrings(input.reasons ?? [])),
8023
+ notes: Object.freeze(uniqueStrings(input.notes ?? []))
8024
+ });
8025
+ }
8026
+
8027
+ function nativeParserFeatureReadinessForStatus(status) {
8028
+ if (status === 'full' || status === 'not-applicable') return 'ready';
8029
+ if (status === 'partial') return 'ready-with-losses';
8030
+ if (status === 'blocked') return 'blocked';
8031
+ return 'needs-review';
8032
+ }
8033
+
8034
+ function nativeParserFeatureStatusMergeReady(status) {
8035
+ return status === 'full' || status === 'not-applicable';
8036
+ }
8037
+
8038
+ function nativeParserFeatureMergeAssessment(row, input = {}) {
8039
+ const requiredFeatures = normalizeNativeParserRequiredFeatures(input.requiredFeatures);
8040
+ const minimumReadiness = normalizeSemanticMergeReadiness(input.minimumReadiness ?? 'ready') ?? 'ready';
8041
+ const featureReadiness = requiredFeatures.reduce(
8042
+ (current, category) => maxSemanticMergeReadiness(current, row.features?.[category]?.readiness ?? 'blocked'),
8043
+ 'ready'
8044
+ );
8045
+ const readiness = maxSemanticMergeReadiness(row.imports?.readiness ?? 'needs-review', featureReadiness);
8046
+ const blockingFeatures = requiredFeatures.filter((category) => !nativeParserFeatureStatusMergeReady(row.features?.[category]?.status));
8047
+ const reviewFeatures = requiredFeatures.filter((category) => {
8048
+ const featureReadiness = row.features?.[category]?.readiness ?? 'blocked';
8049
+ return semanticMergeReadinessRank[featureReadiness] > semanticMergeReadinessRank.ready
8050
+ && semanticMergeReadinessRank[featureReadiness] <= semanticMergeReadinessRank['needs-review'];
8051
+ });
8052
+ const reasons = [];
8053
+ if ((row.imports?.total ?? 0) === 0) reasons.push('No native import evidence matched this language/parser row.');
8054
+ for (const category of blockingFeatures) {
8055
+ const feature = row.features?.[category];
8056
+ reasons.push(`${category} coverage is ${feature?.status ?? 'missing'}: ${(feature?.reasons ?? []).join(' ')}`);
8057
+ }
8058
+ if (semanticMergeReadinessRank[readiness] > semanticMergeReadinessRank[minimumReadiness]) {
8059
+ reasons.push(`Readiness ${readiness} is weaker than required threshold ${minimumReadiness}.`);
8060
+ }
8061
+ const mergeReady = (row.imports?.total ?? 0) > 0
8062
+ && blockingFeatures.length === 0
8063
+ && semanticMergeReadinessRank[readiness] <= semanticMergeReadinessRank[minimumReadiness];
8064
+ if (mergeReady) reasons.push(`Native import is merge-ready for required features: ${requiredFeatures.join(', ')}.`);
8065
+ return Object.freeze({
8066
+ mergeReady,
8067
+ readiness,
8068
+ requiredFeatures,
8069
+ minimumReadiness,
8070
+ blockingFeatures,
8071
+ reviewFeatures,
8072
+ reasons: uniqueStrings(reasons)
8073
+ });
8074
+ }
8075
+
8076
+ function normalizeNativeParserRequiredFeatures(value) {
8077
+ const requested = normalizeStringList(value);
8078
+ const features = requested.length ? requested : ['syntax', 'semantic', 'sourcePreservation'];
8079
+ return uniqueStrings(features.map(normalizeNativeParserFeatureCategory).filter((feature) => NativeParserFeatureCategories.includes(feature)));
8080
+ }
8081
+
8082
+ function normalizeNativeParserFeatureCategory(value) {
8083
+ const normalized = String(value ?? '').trim().replace(/[-_\s]+([a-zA-Z])/g, (_, letter) => letter.toUpperCase());
8084
+ if (normalized.toLowerCase() === 'macrometaprogramming' || normalized.toLowerCase() === 'macro') return 'macroMetaprogramming';
8085
+ if (normalized.toLowerCase() === 'controlflow' || normalized.toLowerCase() === 'cfg') return 'controlFlow';
8086
+ if (normalized.toLowerCase() === 'sourcepreservation' || normalized.toLowerCase() === 'source') return 'sourcePreservation';
8087
+ if (normalized.toLowerCase() === 'types') return 'type';
8088
+ return normalized;
8089
+ }
8090
+
8091
+ function nativeParserFeatureMatrixSummary(rows) {
8092
+ const summary = {
8093
+ languages: new Set(),
8094
+ parsers: rows.length,
8095
+ imports: 0,
8096
+ adapters: 0,
8097
+ mergeReady: 0,
8098
+ byReadiness: {},
8099
+ byFeatureStatus: {},
8100
+ byFeatureReadiness: {}
8101
+ };
8102
+ for (const row of rows) {
8103
+ summary.languages.add(row.language);
8104
+ summary.imports += row.imports.total;
8105
+ summary.adapters += row.adapters.total;
8106
+ if (row.merge.mergeReady) summary.mergeReady += 1;
8107
+ summary.byReadiness[row.merge.readiness] = (summary.byReadiness[row.merge.readiness] ?? 0) + 1;
8108
+ for (const [category, feature] of Object.entries(row.features)) {
8109
+ summary.byFeatureStatus[category] ??= {};
8110
+ summary.byFeatureReadiness[category] ??= {};
8111
+ summary.byFeatureStatus[category][feature.status] = (summary.byFeatureStatus[category][feature.status] ?? 0) + 1;
8112
+ summary.byFeatureReadiness[category][feature.readiness] = (summary.byFeatureReadiness[category][feature.readiness] ?? 0) + 1;
8113
+ }
8114
+ }
8115
+ return {
8116
+ ...summary,
8117
+ languages: summary.languages.size
8118
+ };
8119
+ }
8120
+
8121
+ function summarizeNativeParserFeatureLanguages(profiles, rows) {
8122
+ return profiles.map((profile) => {
8123
+ const languageRows = rows.filter((row) => row.language === profile.language);
8124
+ return {
8125
+ language: profile.language,
8126
+ aliases: profile.aliases,
8127
+ parserRows: languageRows.length,
8128
+ parsers: languageRows.map((row) => row.parser),
8129
+ imports: languageRows.reduce((sum, row) => sum + row.imports.total, 0),
8130
+ adapters: languageRows.reduce((sum, row) => sum + row.adapters.total, 0),
8131
+ mergeReadyParsers: languageRows.filter((row) => row.merge.mergeReady).map((row) => row.parser),
8132
+ readiness: languageRows.reduce((current, row) => maxSemanticMergeReadiness(current, row.merge.readiness), 'ready')
8133
+ };
8134
+ });
8135
+ }
8136
+
8137
+ function nativeParserFeatureParserSlots(profile, imports, adapters) {
8138
+ const slots = [
8139
+ ...(profile.parserAdapters ?? []),
8140
+ ...adapters.map((adapter) => adapter.parser),
8141
+ ...imports.map(nativeParserParserForImport)
8142
+ ].filter(Boolean);
8143
+ if (!slots.length && profile.supportsLightweightScan) slots.push(`${profile.language}.lightweight-declaration-scan`);
8144
+ const seen = new Set();
8145
+ const result = [];
8146
+ for (const slot of slots) {
8147
+ const key = `${normalizeParserAstFormatId(slot)}#${parserAstFormatIdForParser(slot)}`;
8148
+ if (seen.has(key)) continue;
8149
+ seen.add(key);
8150
+ result.push(String(slot));
8151
+ }
8152
+ return result;
8153
+ }
8154
+
8155
+ function nativeParserFeatureImportsForProfile(profile, imports = []) {
8156
+ const languages = nativeParserFeatureLanguageSet(profile);
8157
+ return imports.filter((imported) => languages.has(normalizeNativeLanguageId(imported?.language ?? imported?.nativeAst?.language ?? imported?.nativeSource?.language)));
8158
+ }
8159
+
8160
+ function nativeParserFeatureAdapterSummariesForProfile(profile, adapters = []) {
8161
+ const languages = nativeParserFeatureLanguageSet(profile);
8162
+ return adapters
8163
+ .map((adapter) => safeNativeImporterAdapterSummary(adapter))
8164
+ .filter(Boolean)
8165
+ .filter((adapter) => languages.has(normalizeNativeLanguageId(adapter.language)));
8166
+ }
8167
+
8168
+ function nativeParserFeatureLanguageSet(profile) {
8169
+ return new Set([profile.language, ...(profile.aliases ?? [])].map(normalizeNativeLanguageId).filter(Boolean));
8170
+ }
8171
+
8172
+ function nativeParserParserForImport(imported) {
8173
+ return imported?.adapter?.parser
8174
+ ?? imported?.metadata?.adapterCoverage?.parser
8175
+ ?? imported?.nativeAst?.parser
8176
+ ?? imported?.nativeSource?.parser
8177
+ ?? imported?.parser
8178
+ ?? imported?.metadata?.parser;
8179
+ }
8180
+
8181
+ function nativeParserFeatureParserMatches(candidateParser, rowParser) {
8182
+ if (!candidateParser || !rowParser) return false;
8183
+ const candidate = normalizeParserAstFormatId(candidateParser);
8184
+ const row = normalizeParserAstFormatId(rowParser);
8185
+ return candidate === row || parserAstFormatIdForParser(candidateParser) === parserAstFormatIdForParser(rowParser);
8186
+ }
8187
+
8188
+ function nativeParserFeatureSemanticEvidence(imports) {
8189
+ const totals = {
8190
+ declarations: 0,
8191
+ symbols: 0,
8192
+ references: 0,
8193
+ types: 0,
8194
+ controlFlow: 0
8195
+ };
8196
+ for (const imported of imports) {
8197
+ const semanticIndex = imported?.semanticIndex ?? imported?.universalAst?.semanticIndex;
8198
+ const evidence = observeNativeImporterSemanticEvidence(semanticIndex);
8199
+ totals.declarations += evidence.declarations;
8200
+ totals.symbols += semanticIndex?.symbols?.length ?? 0;
8201
+ totals.references += evidence.references;
8202
+ totals.types += evidence.types;
8203
+ totals.controlFlow += evidence.controlFlow;
8204
+ }
8205
+ return totals;
8206
+ }
8207
+
8208
+ function nativeParserFeatureSemanticLevel(adapterCoverage, semanticEvidence) {
8209
+ if ((adapterCoverage.effective.types ?? 0) > 0 || (adapterCoverage.effective.controlFlow ?? 0) > 0 || semanticEvidence.types > 0 || semanticEvidence.controlFlow > 0 || semanticEvidence.references > 0) {
8210
+ return 'semantic-index';
8211
+ }
8212
+ if ((adapterCoverage.effective.semanticDeclarations ?? 0) > 0 || (adapterCoverage.effective.semanticSymbols ?? 0) > 0 || semanticEvidence.declarations > 0 || semanticEvidence.symbols > 0) {
8213
+ return 'declaration-index';
8214
+ }
8215
+ return 'native-ast';
8216
+ }
8217
+
8218
+ function nativeParserFeatureCapabilityGaps(adapterCoverage, capabilities) {
8219
+ const gaps = new Set();
8220
+ for (const capability of capabilities) {
8221
+ if ((adapterCoverage.effective?.[capability] ?? 0) === 0) gaps.add(capability);
8222
+ }
8223
+ for (const capability of Object.keys(adapterCoverage.gaps ?? {})) {
8224
+ if (capabilities.includes(capability)) gaps.add(capability);
8225
+ }
8226
+ return [...gaps];
8227
+ }
8228
+
8229
+ function nativeParserFeatureLossKindCounts(losses, kinds) {
8230
+ const wanted = new Set(kinds);
8231
+ const counts = {};
8232
+ for (const loss of losses) {
8233
+ if (!wanted.has(loss?.kind)) continue;
8234
+ counts[loss.kind] = (counts[loss.kind] ?? 0) + 1;
8235
+ }
8236
+ return counts;
8237
+ }
8238
+
8239
+ function nativeParserFeaturePresentLossKinds(context, kindSet) {
8240
+ return uniqueStrings([
8241
+ ...(context.profile.knownLossKinds ?? []),
8242
+ ...Object.keys(context.lossSummary.byKind ?? {})
8243
+ ].filter((kind) => kindSet.has(kind)));
8244
+ }
8245
+
7526
8246
  function mergeNativeParserAstFormatProfiles(profiles, imports, adapters) {
7527
8247
  const byId = new Map((profiles ?? []).map((profile) => [normalizeParserAstFormatId(profile.id ?? profile), nativeParserAstFormatProfile(normalizeParserAstFormatId(profile.id ?? profile), profile)]));
7528
8248
  for (const adapter of adapters ?? []) {
@@ -7856,6 +8576,7 @@ function semanticImportSidecarEntry(imported, index, options) {
7856
8576
  const nativeAst = imported?.nativeAst ?? imported?.nativeSource?.ast;
7857
8577
  const sourceMaps = imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? [];
7858
8578
  const sourceMapMappings = sourceMaps.flatMap((sourceMap) => sourceMap?.mappings ?? []);
8579
+ const sourcePreservationRecords = collectKernelSourcePreservationFromImport(imported);
7859
8580
  const mappingsBySymbolId = new Map();
7860
8581
  for (const mapping of sourceMapMappings) {
7861
8582
  if (mapping.semanticSymbolId && !mappingsBySymbolId.has(mapping.semanticSymbolId)) {
@@ -7900,6 +8621,8 @@ function semanticImportSidecarEntry(imported, index, options) {
7900
8621
  symbolCount: symbols.length,
7901
8622
  sourceMapCount: sourceMaps.length,
7902
8623
  sourceMapMappingCount: sourceMapMappings.length,
8624
+ sourcePreservationRecordCount: sourcePreservationRecords.length,
8625
+ sourcePreservationLevels: uniqueStrings(sourcePreservationRecords.map((record) => record.level).filter(Boolean)),
7903
8626
  readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness ?? 'needs-review',
7904
8627
  emptySemanticIndex: symbols.length === 0,
7905
8628
  regionTaxonomy,
@@ -8294,6 +9017,127 @@ function summarizeImportRegions(importResult, imports, options = {}) {
8294
9017
  };
8295
9018
  }
8296
9019
 
9020
+ function createKernelSourcePreservationRecords(input) {
9021
+ const records = [];
9022
+ if (input.sourcePreservation) {
9023
+ const exactSource = input.sourcePreservation.summary?.exactSourceAvailable === true &&
9024
+ (!input.sourceHash || input.sourcePreservation.sourceHash === input.sourceHash);
9025
+ const hashMismatch = input.sourceHash &&
9026
+ input.sourcePreservation.sourceHash &&
9027
+ input.sourcePreservation.sourceHash !== input.sourceHash;
9028
+ records.push(createSourcePreservationRecord({
9029
+ id: `source_preservation_${idFragment(input.idPart ?? input.sourcePath ?? input.language)}_file`,
9030
+ level: hashMismatch ? 'blocked' : exactSource ? 'exact' : 'estimated',
9031
+ precision: exactSource ? 'exact' : 'estimated',
9032
+ nativeSourceId: input.nativeSource?.id,
9033
+ nativeAstNodeId: input.nativeAst?.rootId,
9034
+ sourceSpan: {
9035
+ sourceId: input.sourceHash,
9036
+ path: input.sourcePath,
9037
+ startLine: 1,
9038
+ startColumn: 1
9039
+ },
9040
+ lossIds: (input.losses ?? []).filter((loss) => loss.kind === 'sourcePreservation' || loss.sourceMapId).map((loss) => loss.id),
9041
+ evidenceIds: (input.evidence ?? []).map((record) => record.id).filter(Boolean),
9042
+ losses: input.losses ?? [],
9043
+ evidence: input.evidence ?? [],
9044
+ reasons: hashMismatch
9045
+ ? [`Preserved source hash ${input.sourcePreservation.sourceHash} does not match import hash ${input.sourceHash}.`]
9046
+ : exactSource
9047
+ ? ['Exact native source text is preserved for source-level replay and semantic merge review.']
9048
+ : ['Native source preservation metadata exists, but exact source text is unavailable or unverified.'],
9049
+ metadata: {
9050
+ compilerRecord: 'nativeSourcePreservation',
9051
+ nativeSourcePreservationId: input.sourcePreservation.id,
9052
+ sourceBytes: input.sourcePreservation.sourceBytes,
9053
+ lineCount: input.sourcePreservation.lineCount,
9054
+ tokens: input.sourcePreservation.summary?.tokens ?? 0,
9055
+ trivia: input.sourcePreservation.summary?.trivia ?? 0,
9056
+ directives: input.sourcePreservation.summary?.directives ?? 0,
9057
+ comments: input.sourcePreservation.summary?.comments ?? 0,
9058
+ whitespace: input.sourcePreservation.summary?.whitespace ?? 0,
9059
+ truncated: input.sourcePreservation.summary?.truncated === true
9060
+ }
9061
+ }));
9062
+ }
9063
+
9064
+ for (const sourceMap of input.sourceMaps ?? []) {
9065
+ for (const mapping of sourceMap.mappings ?? []) {
9066
+ records.push(explainSourcePreservation({
9067
+ id: `source_preservation_${idFragment(sourceMap.id)}_${idFragment(mapping.id)}`,
9068
+ sourceMap,
9069
+ mapping,
9070
+ level: mapping.preservation,
9071
+ losses: input.losses ?? [],
9072
+ evidence: uniqueRecordsById([...(input.evidence ?? []), ...(sourceMap.evidence ?? [])]),
9073
+ metadata: {
9074
+ compilerRecord: 'sourceMapMapping',
9075
+ language: input.language,
9076
+ semanticIndexId: input.semanticIndex?.id,
9077
+ sourceMapId: sourceMap.id,
9078
+ sourceMapMappingId: mapping.id
9079
+ }
9080
+ }));
9081
+ }
9082
+ }
9083
+
9084
+ return uniqueRecordsById(records);
9085
+ }
9086
+
9087
+ function summarizeKernelSourcePreservationRecords(records) {
9088
+ const compactRecords = records.map(compactKernelSourcePreservationRecord);
9089
+ const byLevel = countBy(compactRecords.map((record) => record.level ?? 'unknown'));
9090
+ return {
9091
+ total: compactRecords.length,
9092
+ ids: compactRecords.map((record) => record.id).filter(Boolean),
9093
+ byLevel,
9094
+ exact: byLevel.exact ?? 0,
9095
+ declaration: byLevel.declaration ?? 0,
9096
+ estimated: byLevel.estimated ?? 0,
9097
+ blocked: byLevel.blocked ?? 0,
9098
+ sourcePaths: uniqueStrings(compactRecords.map((record) => record.sourcePath).filter(Boolean)),
9099
+ sourceMapIds: uniqueStrings(compactRecords.map((record) => record.sourceMapId).filter(Boolean)),
9100
+ sourceMapMappingIds: uniqueStrings(compactRecords.map((record) => record.sourceMapMappingId).filter(Boolean)),
9101
+ records: compactRecords
9102
+ };
9103
+ }
9104
+
9105
+ function collectKernelSourcePreservationFromImport(imported) {
9106
+ return uniqueRecordsById([
9107
+ ...(imported?.metadata?.kernelSourcePreservationRecords ?? []),
9108
+ ...(imported?.metadata?.sourcePreservationRecords ?? []),
9109
+ ...(imported?.universalAst?.metadata?.kernelSourcePreservationRecords ?? []),
9110
+ ...(imported?.universalAst?.metadata?.sourcePreservationRecords ?? [])
9111
+ ].filter((record) => record?.kind === 'frontier.lang.sourcePreservation'));
9112
+ }
9113
+
9114
+ function summarizeKernelSourcePreservation(importResult, imports) {
9115
+ return summarizeKernelSourcePreservationRecords(uniqueRecordsById([
9116
+ ...collectKernelSourcePreservationFromImport(importResult),
9117
+ ...imports.flatMap((imported) => collectKernelSourcePreservationFromImport(imported))
9118
+ ]));
9119
+ }
9120
+
9121
+ function compactKernelSourcePreservationRecord(record) {
9122
+ return {
9123
+ id: record.id,
9124
+ level: record.level,
9125
+ precision: record.precision,
9126
+ sourceMapId: record.sourceMapId,
9127
+ sourceMapMappingId: record.sourceMapMappingId,
9128
+ semanticNodeId: record.semanticNodeId,
9129
+ nativeSourceId: record.nativeSourceId,
9130
+ nativeAstNodeId: record.nativeAstNodeId,
9131
+ semanticSymbolId: record.semanticSymbolId,
9132
+ semanticOccurrenceId: record.semanticOccurrenceId,
9133
+ sourcePath: record.sourceSpan?.path,
9134
+ generatedPath: record.generatedSpan?.path ?? record.generatedSpan?.targetPath,
9135
+ lossIds: record.lossIds ?? [],
9136
+ evidenceIds: record.evidenceIds ?? [],
9137
+ reasons: record.reasons ?? []
9138
+ };
9139
+ }
9140
+
8297
9141
  function summarizeImportSourcePreservation(importResult, imports) {
8298
9142
  const records = uniqueSourcePreservationRecords([
8299
9143
  ...collectSourcePreservationFromImport(importResult),