@shapeshift-labs/frontier-lang-compiler 0.2.38 → 0.2.40

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
@@ -2488,6 +2488,62 @@ export function createProjectionTargetLossMatrix(input = {}) {
2488
2488
  };
2489
2489
  }
2490
2490
 
2491
+ export function createUniversalCapabilityMatrix(input = {}) {
2492
+ const generatedAt = input.generatedAt ?? Date.now();
2493
+ const imports = input.imports ?? [];
2494
+ const adapters = input.adapters ?? [];
2495
+ const targetAdapters = input.targetAdapters ?? [];
2496
+ const languages = input.languages ?? NativeImportLanguageProfiles;
2497
+ const targets = input.targets ?? FrontierCompileTargets;
2498
+ const importCoverage = createNativeImportCoverageMatrix({ languages, imports, adapters, generatedAt });
2499
+ const parserFormats = createNativeParserAstFormatMatrix({
2500
+ formats: input.formats,
2501
+ imports,
2502
+ adapters,
2503
+ generatedAt
2504
+ });
2505
+ const parserFeatures = createNativeParserFeatureMatrix({
2506
+ languages,
2507
+ imports,
2508
+ adapters,
2509
+ requiredFeatures: input.requiredFeatures,
2510
+ minimumReadiness: input.minimumReadiness,
2511
+ includeEmptyParsers: input.includeEmptyParsers,
2512
+ generatedAt
2513
+ });
2514
+ const projectionTargets = createProjectionTargetLossMatrix({
2515
+ languages,
2516
+ imports,
2517
+ adapters,
2518
+ targetAdapters,
2519
+ targets,
2520
+ generatedAt
2521
+ });
2522
+ const rows = importCoverage.languages.map((entry) => universalCapabilityLanguageRow(entry, {
2523
+ parserFeatures,
2524
+ projectionTargets
2525
+ }));
2526
+ return {
2527
+ kind: 'frontier.lang.universalCapabilityMatrix',
2528
+ version: 1,
2529
+ generatedAt,
2530
+ languages: rows,
2531
+ summary: universalCapabilityMatrixSummary(rows),
2532
+ matrices: {
2533
+ importCoverage,
2534
+ parserFormats,
2535
+ parserFeatures,
2536
+ projectionTargets
2537
+ },
2538
+ metadata: {
2539
+ requiredFeatures: parserFeatures.metadata.requiredFeatures,
2540
+ minimumReadiness: parserFeatures.metadata.minimumReadiness,
2541
+ compileTargets: projectionTargets.metadata.compileTargets,
2542
+ note: 'Universal capability coverage composes import, parser, source-preservation, and projection evidence. It identifies gaps; it is not a proof that every language feature is losslessly portable.'
2543
+ }
2544
+ };
2545
+ }
2546
+
2491
2547
  export function createNativeSourcePreservation(options) {
2492
2548
  if (!options || typeof options.sourceText !== 'string') {
2493
2549
  throw new Error('createNativeSourcePreservation requires sourceText');
@@ -4155,6 +4211,867 @@ export function diffNativeSourceImports(input) {
4155
4211
  };
4156
4212
  }
4157
4213
 
4214
+ export function createSemanticSlice(input, options = {}) {
4215
+ const context = semanticSliceContext(input, options);
4216
+ const sidecar = context.sidecar ?? (context.importResult ? createSemanticImportSidecar(context.importResult, {
4217
+ generatedAt: options.generatedAt,
4218
+ regionPrefix: options.regionPrefix
4219
+ }) : undefined);
4220
+ const records = semanticSliceRecords(context, sidecar);
4221
+ const entryRefs = uniqueStrings([
4222
+ ...readStringArray(options.entryRefs),
4223
+ ...readStringArray(options.semanticRefs),
4224
+ ...readStringArray(options.refs),
4225
+ ...(options.symbol ? [`symbol:${options.symbol}`] : []),
4226
+ ...(options.region ? [`region:${options.region}`] : []),
4227
+ ...(options.nativeNodeId ? [`native:${options.nativeNodeId}`] : []),
4228
+ ...(options.sourcePath ? [`path:${options.sourcePath}`] : [])
4229
+ ]);
4230
+ const selection = selectSemanticSliceRecords(records, {
4231
+ entryRefs,
4232
+ includeDependencies: options.includeDependencies !== false,
4233
+ maxDependencyDepth: Number.isFinite(options.maxDependencyDepth) ? Math.max(0, Math.floor(options.maxDependencyDepth)) : 2
4234
+ });
4235
+ const sourceSpans = semanticSliceSourceSpans(selection);
4236
+ const sourceFiles = semanticSliceSourceFiles(sourceSpans, context, options);
4237
+ const sourceMapLinks = semanticSliceSourceMapLinks(selection);
4238
+ const conflictKeys = uniqueStrings([
4239
+ ...selection.regions.map((region) => region.conflictKey),
4240
+ ...selection.regions.map((region) => region.key ? `region:${region.key}` : region.id ? `region:${region.id}` : undefined),
4241
+ ...selection.symbols.map((symbol) => symbol.metadata?.ownershipRegionKey ? `region:${symbol.metadata.ownershipRegionKey}` : undefined),
4242
+ ...selection.symbols.map((symbol) => symbol.id ? `symbol:${symbol.id}` : undefined),
4243
+ ...sourceFiles.map((file) => file.path ? `source:${file.path}` : undefined)
4244
+ ].filter(Boolean));
4245
+ const unresolvedEntryRefs = entryRefs.filter((entryRef) => !selection.matchedEntryRefs.includes(entryRef));
4246
+ const readiness = semanticSliceReadiness(context, selection, unresolvedEntryRefs);
4247
+ const reasons = semanticSliceReasons(context, selection, unresolvedEntryRefs, readiness);
4248
+ const idPart = idFragment(options.id ?? entryRefs.join('_') ?? context.sourcePath ?? context.language ?? context.importResult?.id ?? context.universalAst?.id ?? 'semantic_slice');
4249
+ const evidence = [{
4250
+ id: options.evidenceId ?? `evidence_${idPart}_semantic_slice`,
4251
+ kind: 'semantic-slice',
4252
+ status: readiness === 'blocked' ? 'failed' : 'passed',
4253
+ path: context.sourcePath,
4254
+ summary: `Created semantic slice with ${selection.symbols.length} symbol(s), ${selection.regions.length} ownership region(s), and ${sourceMapLinks.length} source-map link(s).`,
4255
+ metadata: {
4256
+ entryRefs,
4257
+ unresolvedEntryRefs,
4258
+ importId: context.importResult?.id,
4259
+ universalAstId: context.universalAst?.id,
4260
+ semanticIndexId: context.semanticIndex?.id,
4261
+ sidecarId: sidecar?.id
4262
+ }
4263
+ }];
4264
+ const mergeCandidate = createSemanticMergeCandidateRecord({
4265
+ id: options.mergeCandidateId ?? `merge_candidate_${idPart}_semantic_slice`,
4266
+ importResultId: context.importResult?.id,
4267
+ language: context.language,
4268
+ sourcePath: context.sourcePath,
4269
+ touchedSymbols: selection.symbols.map(semanticSliceTouchedSymbol),
4270
+ touchedSemanticNodes: [],
4271
+ nativeSpans: sourceSpans,
4272
+ conflictKeys,
4273
+ readiness,
4274
+ reasons,
4275
+ evidence,
4276
+ metadata: {
4277
+ kind: 'semantic-slice',
4278
+ sidecarId: sidecar?.id,
4279
+ sourceMapLinks: sourceMapLinks.length,
4280
+ nativeNodeIds: selection.nativeNodes.map((node) => node.id),
4281
+ dependencyRelationIds: selection.relations.map((relation) => relation.id).filter(Boolean),
4282
+ autoMergeClaim: false
4283
+ }
4284
+ });
4285
+ return {
4286
+ kind: 'frontier.lang.semanticSlice',
4287
+ version: 1,
4288
+ id: options.id ?? `semantic_slice_${idPart}`,
4289
+ generatedAt: options.generatedAt ?? Date.now(),
4290
+ language: context.language,
4291
+ sourcePath: context.sourcePath,
4292
+ importId: context.importResult?.id,
4293
+ universalAstId: context.universalAst?.id,
4294
+ semanticIndexId: context.semanticIndex?.id,
4295
+ sidecarId: sidecar?.id,
4296
+ entryRefs,
4297
+ matchedEntryRefs: selection.matchedEntryRefs,
4298
+ unresolvedEntryRefs,
4299
+ symbols: selection.symbols,
4300
+ ownershipRegions: selection.regions,
4301
+ nativeNodes: selection.nativeNodes,
4302
+ relations: selection.relations,
4303
+ occurrences: selection.occurrences,
4304
+ sourceMapLinks,
4305
+ sourceSpans,
4306
+ sourceFiles,
4307
+ losses: selection.losses,
4308
+ evidence,
4309
+ mergeCandidate,
4310
+ verification: {
4311
+ focusedCommands: readStringArray(options.focusedCommands),
4312
+ fixtureHints: readStringArray(options.fixtureHints),
4313
+ expectedAssertions: semanticSliceExpectedAssertions(selection, unresolvedEntryRefs)
4314
+ },
4315
+ summary: {
4316
+ symbols: selection.symbols.length,
4317
+ ownershipRegions: selection.regions.length,
4318
+ nativeNodes: selection.nativeNodes.length,
4319
+ relations: selection.relations.length,
4320
+ occurrences: selection.occurrences.length,
4321
+ sourceMapLinks: sourceMapLinks.length,
4322
+ sourceFiles: sourceFiles.length,
4323
+ losses: selection.losses.length,
4324
+ conflictKeys: conflictKeys.length,
4325
+ readiness,
4326
+ unresolvedEntryRefs: unresolvedEntryRefs.length,
4327
+ sourceTextAvailable: sourceFiles.some((file) => file.excerptCount > 0)
4328
+ },
4329
+ mergeAdmission: {
4330
+ autoMergeClaim: false,
4331
+ reviewRequired: readiness !== 'ready',
4332
+ readiness,
4333
+ reasons,
4334
+ conflictKeys,
4335
+ ownershipKeys: uniqueStrings(selection.regions.map((region) => region.key).filter(Boolean)),
4336
+ sourceHashes: sourceFiles.map((file) => ({ path: file.path, sourceHash: file.sourceHash })).filter((file) => file.path || file.sourceHash),
4337
+ staleCheck: {
4338
+ mode: 'source-hash',
4339
+ requiresCurrentSource: sourceFiles.some((file) => file.sourceHash),
4340
+ sourceFiles: sourceFiles.length
4341
+ }
4342
+ },
4343
+ metadata: {
4344
+ note: 'Semantic slices are focused source-addressable evidence for agent isolation and merge admission; they are not correctness proofs.',
4345
+ ...options.metadata
4346
+ }
4347
+ };
4348
+ }
4349
+
4350
+ export function testSemanticSlice(slice, options = {}) {
4351
+ const assertions = [];
4352
+ assertions.push(semanticSliceAssertion('kind', slice?.kind === 'frontier.lang.semanticSlice', 'Input is a Frontier semantic slice.'));
4353
+ assertions.push(semanticSliceAssertion('entryRefsResolved', (slice?.unresolvedEntryRefs?.length ?? 0) === 0, 'All requested semantic entry refs resolved.', {
4354
+ unresolvedEntryRefs: slice?.unresolvedEntryRefs ?? []
4355
+ }));
4356
+ assertions.push(semanticSliceAssertion('nonEmptySelection', (slice?.symbols?.length ?? 0) + (slice?.ownershipRegions?.length ?? 0) + (slice?.nativeNodes?.length ?? 0) > 0, 'Slice selected at least one symbol, ownership region, or native node.'));
4357
+ assertions.push(semanticSliceAssertion('sourceMapLinks', options.requireSourceMapLinks === false || (slice?.sourceMapLinks?.length ?? 0) > 0, 'Slice has source-map links or the requirement was disabled.'));
4358
+ assertions.push(semanticSliceAssertion('conflictKeys', (slice?.mergeAdmission?.conflictKeys?.length ?? 0) > 0, 'Slice exposes merge-admission conflict keys.'));
4359
+ const sourceHashAssertions = semanticSliceSourceHashAssertions(slice, options.currentSources);
4360
+ assertions.push(...sourceHashAssertions);
4361
+ const failed = assertions.filter((assertion) => assertion.status === 'failed');
4362
+ const warnings = assertions.filter((assertion) => assertion.status === 'warning');
4363
+ const readiness = failed.length
4364
+ ? 'blocked'
4365
+ : maxSemanticMergeReadiness(slice?.mergeAdmission?.readiness ?? slice?.summary?.readiness ?? 'ready', warnings.length ? 'needs-review' : 'ready');
4366
+ return {
4367
+ kind: 'frontier.lang.semanticSliceTestResult',
4368
+ version: 1,
4369
+ id: options.id ?? `semantic_slice_test_${idFragment(slice?.id ?? 'slice')}`,
4370
+ generatedAt: options.generatedAt ?? Date.now(),
4371
+ sliceId: slice?.id,
4372
+ status: failed.length ? 'failed' : warnings.length ? 'needs-review' : 'passed',
4373
+ readiness,
4374
+ assertions,
4375
+ summary: {
4376
+ assertions: assertions.length,
4377
+ passed: assertions.filter((assertion) => assertion.status === 'passed').length,
4378
+ warnings: warnings.length,
4379
+ failed: failed.length,
4380
+ sourceHashChecks: sourceHashAssertions.length,
4381
+ symbols: slice?.summary?.symbols ?? slice?.symbols?.length ?? 0,
4382
+ ownershipRegions: slice?.summary?.ownershipRegions ?? slice?.ownershipRegions?.length ?? 0,
4383
+ sourceMapLinks: slice?.summary?.sourceMapLinks ?? slice?.sourceMapLinks?.length ?? 0
4384
+ },
4385
+ metadata: {
4386
+ sliceReadiness: slice?.mergeAdmission?.readiness ?? slice?.summary?.readiness,
4387
+ ...options.metadata
4388
+ }
4389
+ };
4390
+ }
4391
+
4392
+ export function readSemanticSliceJson(source) {
4393
+ const slice = JSON.parse(source);
4394
+ if (slice?.kind !== 'frontier.lang.semanticSlice') {
4395
+ throw new Error('Invalid Frontier semantic slice JSON: expected kind frontier.lang.semanticSlice');
4396
+ }
4397
+ return slice;
4398
+ }
4399
+
4400
+ export function writeSemanticSliceJson(slice) {
4401
+ if (slice?.kind !== 'frontier.lang.semanticSlice') {
4402
+ throw new Error('Invalid Frontier semantic slice: expected kind frontier.lang.semanticSlice');
4403
+ }
4404
+ return stableUniversalAstJson(slice);
4405
+ }
4406
+
4407
+ function semanticSliceContext(input, options = {}) {
4408
+ const value = input?.importResult ?? input?.import ?? input?.source ?? input;
4409
+ const imported = semanticSliceImportResult(value, options);
4410
+ const universalAst = imported?.universalAst ?? (value?.kind === 'frontier.lang.universalAst' ? value : value?.universalAst);
4411
+ const sidecar = value?.kind === 'frontier.lang.semanticImportSidecar' ? value : input?.sidecar ?? options.sidecar;
4412
+ const nativeSource = imported?.nativeSource ?? universalAst?.nativeSources?.[0];
4413
+ const nativeAst = imported?.nativeAst ?? nativeSource?.ast;
4414
+ const semanticIndex = imported?.semanticIndex ?? universalAst?.semanticIndex;
4415
+ const language = options.language ?? imported?.language ?? universalAst?.metadata?.sourceLanguage ?? nativeSource?.language ?? sidecar?.language;
4416
+ const sourcePath = options.sourcePath ?? imported?.sourcePath ?? universalAst?.metadata?.sourcePath ?? nativeSource?.sourcePath ?? semanticIndex?.documents?.[0]?.path;
4417
+ return {
4418
+ importResult: imported,
4419
+ universalAst,
4420
+ sidecar,
4421
+ nativeSource,
4422
+ nativeAst,
4423
+ semanticIndex,
4424
+ language,
4425
+ sourcePath,
4426
+ sourceTexts: semanticSliceSourceTextMap(imported, universalAst, value, options),
4427
+ sourceHashes: semanticSliceSourceHashMap(imported, universalAst, value, options)
4428
+ };
4429
+ }
4430
+
4431
+ function semanticSliceImportResult(value, options = {}) {
4432
+ if (!value) return undefined;
4433
+ if (value.kind === 'frontier.lang.importResult') return value;
4434
+ if (value.kind === 'frontier.lang.universalAst') {
4435
+ const nativeSource = value.nativeSources?.[0];
4436
+ return {
4437
+ kind: 'frontier.lang.importResult',
4438
+ version: 1,
4439
+ id: value.metadata?.nativeImportId ?? `import_${idFragment(value.id)}`,
4440
+ language: value.metadata?.sourceLanguage ?? nativeSource?.language ?? options.language,
4441
+ sourcePath: value.metadata?.sourcePath ?? nativeSource?.sourcePath ?? options.sourcePath,
4442
+ document: value.document,
4443
+ nativeSource,
4444
+ nativeAst: nativeSource?.ast,
4445
+ semanticIndex: value.semanticIndex,
4446
+ universalAst: value,
4447
+ sourceMaps: value.sourceMaps ?? [],
4448
+ losses: value.losses ?? [],
4449
+ evidence: value.evidence ?? [],
4450
+ mergeCandidates: value.mergeCandidates ?? [],
4451
+ metadata: value.metadata ?? {}
4452
+ };
4453
+ }
4454
+ if (value.kind === 'frontier.lang.semanticImportSidecar') return undefined;
4455
+ if (value.universalAst || value.semanticIndex || value.nativeSource || value.nativeAst) return value;
4456
+ if (value.sourceText || value.nodes || value.nativeAst) return importNativeSource(value);
4457
+ return undefined;
4458
+ }
4459
+
4460
+ function semanticSliceRecords(context, sidecar) {
4461
+ const imports = context.importResult ? nativeImportEntries(context.importResult) : [];
4462
+ const universalAst = context.universalAst;
4463
+ const semanticIndexes = [
4464
+ context.semanticIndex,
4465
+ universalAst?.semanticIndex,
4466
+ ...imports.map((imported) => imported?.semanticIndex ?? imported?.universalAst?.semanticIndex)
4467
+ ].filter(Boolean);
4468
+ const sourceMaps = uniqueRecordsById([
4469
+ ...(context.importResult?.sourceMaps ?? []),
4470
+ ...(universalAst?.sourceMaps ?? []),
4471
+ ...imports.flatMap((imported) => imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? [])
4472
+ ]);
4473
+ const symbols = mergeSemanticSliceSymbols([
4474
+ ...(sidecar?.symbols ?? []),
4475
+ ...semanticIndexes.flatMap((index) => index.symbols ?? [])
4476
+ ]);
4477
+ const regions = uniqueSemanticSliceRegions([
4478
+ ...(sidecar?.ownershipRegions ?? []),
4479
+ ...symbols.map((symbol) => semanticSliceRegionFromSymbol(symbol, context)).filter(Boolean),
4480
+ ...sourceMaps.flatMap((sourceMap) => (sourceMap.mappings ?? []).map((mapping) => semanticSliceRegionFromMapping(mapping, context)).filter(Boolean))
4481
+ ]);
4482
+ const nativeNodes = uniqueSemanticSliceNativeNodes([
4483
+ ...imports.flatMap((imported) => nativeAstNodes(imported?.nativeAst ?? imported?.nativeSource?.ast)),
4484
+ ...nativeAstNodes(context.nativeAst),
4485
+ ...((universalAst?.nativeSources ?? []).flatMap((source) => nativeAstNodes(source?.ast)))
4486
+ ]);
4487
+ const mappings = sourceMaps.flatMap((sourceMap) => (sourceMap.mappings ?? []).map((mapping) => ({
4488
+ ...mapping,
4489
+ sourceMapId: mapping.sourceMapId ?? sourceMap.id,
4490
+ sourceMapSourcePath: sourceMap.sourcePath,
4491
+ sourceMapSourceHash: sourceMap.sourceHash,
4492
+ sourceMapTargetPath: sourceMap.targetPath,
4493
+ sourceMapTargetHash: sourceMap.targetHash
4494
+ })));
4495
+ const losses = uniqueByLossId([
4496
+ ...(context.importResult?.losses ?? []),
4497
+ ...(universalAst?.losses ?? []),
4498
+ ...imports.flatMap((imported) => imported?.losses ?? imported?.nativeAst?.losses ?? [])
4499
+ ]);
4500
+ const evidence = uniqueByEvidenceId([
4501
+ ...(context.importResult?.evidence ?? []),
4502
+ ...(universalAst?.evidence ?? []),
4503
+ ...imports.flatMap((imported) => imported?.evidence ?? [])
4504
+ ]);
4505
+ return {
4506
+ symbols,
4507
+ symbolById: new Map(symbols.map((symbol) => [symbol.id, symbol])),
4508
+ regions,
4509
+ nativeNodes,
4510
+ nativeNodeById: new Map(nativeNodes.map((node) => [node.id, node])),
4511
+ mappings,
4512
+ sourceMaps,
4513
+ relations: uniqueRecordsById(semanticIndexes.flatMap((index) => index.relations ?? [])),
4514
+ occurrences: uniqueRecordsById(semanticIndexes.flatMap((index) => index.occurrences ?? [])),
4515
+ losses,
4516
+ evidence,
4517
+ sidecar
4518
+ };
4519
+ }
4520
+
4521
+ function selectSemanticSliceRecords(records, options) {
4522
+ const selectedSymbols = new Set();
4523
+ const selectedRegions = new Set();
4524
+ const selectedNativeNodes = new Set();
4525
+ const selectedMappings = new Set();
4526
+ const selectedRelations = new Set();
4527
+ const selectedOccurrences = new Set();
4528
+ const matchedEntryRefs = [];
4529
+ const entryRefs = options.entryRefs ?? [];
4530
+ if (entryRefs.length === 0) {
4531
+ for (const symbol of records.symbols) selectedSymbols.add(symbol.id);
4532
+ for (const region of records.regions) selectedRegions.add(region.id);
4533
+ for (const node of records.nativeNodes) selectedNativeNodes.add(node.id);
4534
+ for (const mapping of records.mappings) selectedMappings.add(mapping.id);
4535
+ } else {
4536
+ for (const ref of entryRefs) {
4537
+ let matched = false;
4538
+ for (const symbol of records.symbols) {
4539
+ if (!semanticSliceSymbolMatchesRef(symbol, ref)) continue;
4540
+ selectedSymbols.add(symbol.id);
4541
+ matched = true;
4542
+ }
4543
+ for (const region of records.regions) {
4544
+ if (!semanticSliceRegionMatchesRef(region, ref)) continue;
4545
+ selectedRegions.add(region.id);
4546
+ matched = true;
4547
+ }
4548
+ for (const node of records.nativeNodes) {
4549
+ if (!semanticSliceNativeNodeMatchesRef(node, ref)) continue;
4550
+ selectedNativeNodes.add(node.id);
4551
+ matched = true;
4552
+ }
4553
+ for (const mapping of records.mappings) {
4554
+ if (!semanticSliceMappingMatchesRef(mapping, ref)) continue;
4555
+ selectedMappings.add(mapping.id);
4556
+ matched = true;
4557
+ }
4558
+ if (matched) matchedEntryRefs.push(ref);
4559
+ }
4560
+ }
4561
+ expandSemanticSliceSelection(records, {
4562
+ selectedSymbols,
4563
+ selectedRegions,
4564
+ selectedNativeNodes,
4565
+ selectedMappings,
4566
+ selectedRelations,
4567
+ selectedOccurrences,
4568
+ includeDependencies: options.includeDependencies,
4569
+ maxDependencyDepth: options.maxDependencyDepth
4570
+ });
4571
+ const symbols = records.symbols.filter((symbol) => selectedSymbols.has(symbol.id));
4572
+ const regions = records.regions.filter((region) => selectedRegions.has(region.id));
4573
+ const nativeNodes = records.nativeNodes.filter((node) => selectedNativeNodes.has(node.id));
4574
+ const mappings = records.mappings.filter((mapping) => selectedMappings.has(mapping.id));
4575
+ const relations = records.relations.filter((relation) => selectedRelations.has(relation.id));
4576
+ const occurrences = records.occurrences.filter((occurrence) => selectedOccurrences.has(occurrence.id));
4577
+ const selectedNodeIds = new Set(nativeNodes.map((node) => node.id));
4578
+ const losses = records.losses.filter((loss) => !loss.nodeId || selectedNodeIds.has(loss.nodeId) || semanticSliceSpanTouchesSelection(loss.span, mappings, regions));
4579
+ return {
4580
+ matchedEntryRefs,
4581
+ symbols,
4582
+ regions,
4583
+ nativeNodes,
4584
+ mappings,
4585
+ relations,
4586
+ occurrences,
4587
+ losses
4588
+ };
4589
+ }
4590
+
4591
+ function expandSemanticSliceSelection(records, selection) {
4592
+ let changed = true;
4593
+ let depth = 0;
4594
+ const childToParent = semanticSliceNativeParentMap(records.nativeNodes);
4595
+ while (changed && depth <= selection.maxDependencyDepth) {
4596
+ changed = false;
4597
+ for (const mapping of records.mappings) {
4598
+ if (!semanticSliceMappingTouchesSets(mapping, selection)) continue;
4599
+ changed = addSet(selection.selectedMappings, mapping.id) || changed;
4600
+ if (mapping.semanticSymbolId) changed = addSet(selection.selectedSymbols, mapping.semanticSymbolId) || changed;
4601
+ if (mapping.nativeAstNodeId) changed = addSet(selection.selectedNativeNodes, mapping.nativeAstNodeId) || changed;
4602
+ if (mapping.ownershipRegionId) changed = addSet(selection.selectedRegions, mapping.ownershipRegionId) || changed;
4603
+ }
4604
+ for (const symbol of records.symbols) {
4605
+ if (!semanticSliceSymbolTouchesSets(symbol, selection)) continue;
4606
+ changed = addSet(selection.selectedSymbols, symbol.id) || changed;
4607
+ if (symbol.nativeAstNodeId) changed = addSet(selection.selectedNativeNodes, symbol.nativeAstNodeId) || changed;
4608
+ const regionId = symbol.ownershipRegionId ?? symbol.metadata?.ownershipRegionId;
4609
+ if (regionId) changed = addSet(selection.selectedRegions, regionId) || changed;
4610
+ }
4611
+ for (const region of records.regions) {
4612
+ if (!semanticSliceRegionTouchesSets(region, selection)) continue;
4613
+ changed = addSet(selection.selectedRegions, region.id) || changed;
4614
+ if (region.symbolId) changed = addSet(selection.selectedSymbols, region.symbolId) || changed;
4615
+ if (region.nativeAstNodeId) changed = addSet(selection.selectedNativeNodes, region.nativeAstNodeId) || changed;
4616
+ }
4617
+ for (const occurrence of records.occurrences) {
4618
+ if (!selection.selectedSymbols.has(occurrence.symbolId) && !selection.selectedNativeNodes.has(occurrence.nativeAstNodeId)) continue;
4619
+ changed = addSet(selection.selectedOccurrences, occurrence.id) || changed;
4620
+ if (occurrence.symbolId) changed = addSet(selection.selectedSymbols, occurrence.symbolId) || changed;
4621
+ if (occurrence.nativeAstNodeId) changed = addSet(selection.selectedNativeNodes, occurrence.nativeAstNodeId) || changed;
4622
+ }
4623
+ if (selection.includeDependencies) {
4624
+ for (const relation of records.relations) {
4625
+ const touches = selection.selectedSymbols.has(relation.sourceId) || selection.selectedSymbols.has(relation.targetId);
4626
+ if (!touches) continue;
4627
+ changed = addSet(selection.selectedRelations, relation.id) || changed;
4628
+ if (relation.sourceId && records.symbolById.has(relation.sourceId)) changed = addSet(selection.selectedSymbols, relation.sourceId) || changed;
4629
+ if (relation.targetId && records.symbolById.has(relation.targetId)) changed = addSet(selection.selectedSymbols, relation.targetId) || changed;
4630
+ }
4631
+ for (const node of records.nativeNodes) {
4632
+ if (!selection.selectedNativeNodes.has(node.id)) continue;
4633
+ for (const child of node.children ?? []) changed = addSet(selection.selectedNativeNodes, child) || changed;
4634
+ const parent = childToParent.get(node.id);
4635
+ if (parent) changed = addSet(selection.selectedNativeNodes, parent) || changed;
4636
+ }
4637
+ }
4638
+ depth += 1;
4639
+ }
4640
+ }
4641
+
4642
+ function semanticSliceSourceSpans(selection) {
4643
+ return uniqueSemanticSliceSpans([
4644
+ ...selection.symbols.map(semanticSliceSymbolSpan),
4645
+ ...selection.regions.map((region) => region.sourceSpan),
4646
+ ...selection.nativeNodes.map((node) => node.span ?? node.sourceSpan),
4647
+ ...selection.mappings.map((mapping) => mapping.sourceSpan)
4648
+ ].filter(Boolean));
4649
+ }
4650
+
4651
+ function semanticSliceSourceFiles(spans, context, options = {}) {
4652
+ const byPath = new Map();
4653
+ const includeExcerpts = options.includeSourceText !== false;
4654
+ const maxExcerptBytes = Number.isFinite(options.maxExcerptBytes) ? Math.max(0, Math.floor(options.maxExcerptBytes)) : 4000;
4655
+ for (const span of spans) {
4656
+ const path = span.path ?? span.sourceId ?? context.sourcePath ?? 'unknown';
4657
+ const record = byPath.get(path) ?? {
4658
+ path,
4659
+ sourceHash: context.sourceHashes.get(path) ?? context.sourceHashes.get('') ?? span.sourceHash,
4660
+ spans: [],
4661
+ excerpts: []
4662
+ };
4663
+ record.spans.push(span);
4664
+ const sourceText = context.sourceTexts.get(path) ?? context.sourceTexts.get('');
4665
+ const excerpt = includeExcerpts ? sourceTextForSpan(sourceText, span) : undefined;
4666
+ if (typeof excerpt === 'string') {
4667
+ const clipped = excerpt.length > maxExcerptBytes ? excerpt.slice(0, maxExcerptBytes) : excerpt;
4668
+ record.excerpts.push({
4669
+ span,
4670
+ text: clipped,
4671
+ textHash: hashSemanticValue(excerpt),
4672
+ truncated: clipped.length !== excerpt.length
4673
+ });
4674
+ }
4675
+ byPath.set(path, record);
4676
+ }
4677
+ return [...byPath.values()].map((file) => ({
4678
+ ...file,
4679
+ spanCount: file.spans.length,
4680
+ excerptCount: file.excerpts.length,
4681
+ sourceTextAvailable: file.excerpts.length > 0
4682
+ }));
4683
+ }
4684
+
4685
+ function semanticSliceSourceMapLinks(selection) {
4686
+ return selection.mappings.map((mapping) => ({
4687
+ id: mapping.id,
4688
+ sourceMapId: mapping.sourceMapId,
4689
+ sourcePath: mapping.sourceSpan?.path ?? mapping.sourceMapSourcePath,
4690
+ sourceHash: mapping.sourceMapSourceHash,
4691
+ targetPath: mapping.generatedSpan?.targetPath ?? mapping.sourceMapTargetPath,
4692
+ targetHash: mapping.generatedSpan?.targetHash ?? mapping.sourceMapTargetHash,
4693
+ semanticSymbolId: mapping.semanticSymbolId,
4694
+ semanticOccurrenceId: mapping.semanticOccurrenceId,
4695
+ semanticNodeId: mapping.semanticNodeId,
4696
+ nativeSourceId: mapping.nativeSourceId,
4697
+ nativeAstNodeId: mapping.nativeAstNodeId,
4698
+ precision: mapping.precision,
4699
+ sourceSpan: mapping.sourceSpan,
4700
+ generatedSpan: mapping.generatedSpan,
4701
+ ownershipRegionId: mapping.ownershipRegionId,
4702
+ ownershipRegionKey: mapping.ownershipRegionKey,
4703
+ ownershipRegionKind: mapping.ownershipRegionKind
4704
+ }));
4705
+ }
4706
+
4707
+ function semanticSliceReadiness(context, selection, unresolvedEntryRefs) {
4708
+ if (unresolvedEntryRefs.length) return 'blocked';
4709
+ if (selection.symbols.length + selection.regions.length + selection.nativeNodes.length === 0) return 'blocked';
4710
+ const lossReadiness = summarizeNativeImportLosses(selection.losses, { evidence: context.importResult?.evidence }).semanticMergeReadiness;
4711
+ return [
4712
+ context.importResult?.metadata?.semanticMergeReadiness,
4713
+ context.importResult?.metadata?.nativeImportLossSummary?.semanticMergeReadiness,
4714
+ context.sidecar?.summary?.readiness,
4715
+ ...(context.importResult?.mergeCandidates ?? []).map((candidate) => candidate.readiness),
4716
+ lossReadiness
4717
+ ].reduce((current, value) => maxSemanticMergeReadiness(current, value ?? 'ready'), 'ready');
4718
+ }
4719
+
4720
+ function semanticSliceReasons(context, selection, unresolvedEntryRefs, readiness) {
4721
+ return uniqueStrings([
4722
+ unresolvedEntryRefs.length ? `Unresolved semantic slice entry refs: ${unresolvedEntryRefs.join(', ')}` : undefined,
4723
+ selection.symbols.length + selection.regions.length + selection.nativeNodes.length === 0 ? 'Semantic slice selected no reviewable records.' : undefined,
4724
+ selection.mappings.length === 0 ? 'Semantic slice has no source-map links; source review may need file-level fallback.' : undefined,
4725
+ selection.losses.length ? `Semantic slice carries ${selection.losses.length} native import loss record(s).` : undefined,
4726
+ readiness !== 'ready' ? `Semantic slice readiness is ${readiness}.` : undefined,
4727
+ context.importResult?.metadata?.nativeImportLossSummary?.semanticMergeReadiness === 'needs-review' ? 'Parent native import requires review.' : undefined
4728
+ ].filter(Boolean));
4729
+ }
4730
+
4731
+ function semanticSliceExpectedAssertions(selection, unresolvedEntryRefs) {
4732
+ return [
4733
+ { id: 'entryRefsResolved', expected: unresolvedEntryRefs.length === 0 },
4734
+ { id: 'nonEmptySelection', expected: selection.symbols.length + selection.regions.length + selection.nativeNodes.length > 0 },
4735
+ { id: 'sourceMapLinks', expected: selection.mappings.length > 0 },
4736
+ { id: 'conflictKeys', expected: true }
4737
+ ];
4738
+ }
4739
+
4740
+ function semanticSliceTouchedSymbol(symbol) {
4741
+ return {
4742
+ id: symbol.id,
4743
+ name: symbol.name,
4744
+ kind: symbol.kind,
4745
+ nativeAstNodeId: symbol.nativeAstNodeId,
4746
+ span: semanticSliceSymbolSpan(symbol),
4747
+ conflictKey: symbol.metadata?.ownershipRegionKey ? `region:${symbol.metadata.ownershipRegionKey}` : `symbol:${symbol.id}`,
4748
+ metadata: {
4749
+ ownershipRegionId: symbol.ownershipRegionId ?? symbol.metadata?.ownershipRegionId,
4750
+ ownershipRegionKind: symbol.ownershipRegionKind ?? symbol.metadata?.ownershipRegionKind,
4751
+ signatureHash: symbol.signatureHash
4752
+ }
4753
+ };
4754
+ }
4755
+
4756
+ function semanticSliceAssertion(id, ok, summary, metadata = {}) {
4757
+ return {
4758
+ id,
4759
+ status: ok ? 'passed' : 'failed',
4760
+ summary,
4761
+ metadata
4762
+ };
4763
+ }
4764
+
4765
+ function semanticSliceSourceHashAssertions(slice, currentSources) {
4766
+ if (!currentSources) return [];
4767
+ const assertions = [];
4768
+ for (const file of slice?.sourceFiles ?? []) {
4769
+ if (!file.sourceHash || !file.path) continue;
4770
+ const currentSource = semanticSliceCurrentSource(currentSources, file.path);
4771
+ if (typeof currentSource !== 'string') {
4772
+ assertions.push({
4773
+ id: `sourceHash:${file.path}`,
4774
+ status: 'warning',
4775
+ summary: `Current source text was not supplied for ${file.path}.`,
4776
+ metadata: { path: file.path, expectedSourceHash: file.sourceHash }
4777
+ });
4778
+ continue;
4779
+ }
4780
+ const actualSourceHash = hashSemanticValue(currentSource);
4781
+ assertions.push({
4782
+ id: `sourceHash:${file.path}`,
4783
+ status: actualSourceHash === file.sourceHash ? 'passed' : 'failed',
4784
+ summary: actualSourceHash === file.sourceHash ? `Source hash matched for ${file.path}.` : `Source hash changed for ${file.path}.`,
4785
+ metadata: { path: file.path, expectedSourceHash: file.sourceHash, actualSourceHash }
4786
+ });
4787
+ }
4788
+ return assertions;
4789
+ }
4790
+
4791
+ function semanticSliceCurrentSource(currentSources, path) {
4792
+ if (currentSources instanceof Map) return currentSources.get(path);
4793
+ return currentSources[path] ?? currentSources[`./${path}`];
4794
+ }
4795
+
4796
+ function semanticSliceSourceTextMap(imported, universalAst, value, options) {
4797
+ const entries = new Map();
4798
+ const add = (path, sourceText) => {
4799
+ if (typeof sourceText !== 'string') return;
4800
+ entries.set(path ?? '', sourceText);
4801
+ };
4802
+ add(options.sourcePath, options.sourceText);
4803
+ add(value?.sourcePath, value?.sourceText);
4804
+ for (const item of [imported, ...(imported ? nativeImportEntries(imported) : [])]) {
4805
+ add(item?.sourcePath, nativeImportSourceText(item));
4806
+ add(item?.nativeSource?.sourcePath, item?.nativeSource?.metadata?.sourcePreservation?.sourceText);
4807
+ }
4808
+ for (const nativeSource of universalAst?.nativeSources ?? []) {
4809
+ add(nativeSource?.sourcePath, nativeSource?.metadata?.sourcePreservation?.sourceText);
4810
+ add(nativeSource?.sourcePath, nativeSource?.ast?.metadata?.sourcePreservation?.sourceText);
4811
+ }
4812
+ return entries;
4813
+ }
4814
+
4815
+ function semanticSliceSourceHashMap(imported, universalAst, value, options) {
4816
+ const entries = new Map();
4817
+ const add = (path, sourceHash) => {
4818
+ if (!sourceHash) return;
4819
+ entries.set(path ?? '', sourceHash);
4820
+ };
4821
+ add(options.sourcePath, options.sourceHash);
4822
+ add(value?.sourcePath, value?.sourceHash);
4823
+ for (const item of [imported, ...(imported ? nativeImportEntries(imported) : [])]) {
4824
+ add(item?.sourcePath, item?.nativeSource?.sourceHash ?? item?.nativeAst?.sourceHash ?? item?.sourceHash);
4825
+ }
4826
+ for (const nativeSource of universalAst?.nativeSources ?? []) {
4827
+ add(nativeSource?.sourcePath, nativeSource?.sourceHash ?? nativeSource?.ast?.sourceHash);
4828
+ }
4829
+ for (const sourceMap of universalAst?.sourceMaps ?? []) add(sourceMap.sourcePath, sourceMap.sourceHash);
4830
+ return entries;
4831
+ }
4832
+
4833
+ function mergeSemanticSliceSymbols(symbols) {
4834
+ const byId = new Map();
4835
+ for (const symbol of symbols ?? []) {
4836
+ if (!symbol?.id) continue;
4837
+ const existing = byId.get(symbol.id);
4838
+ byId.set(symbol.id, {
4839
+ ...(existing ?? {}),
4840
+ ...symbol,
4841
+ metadata: {
4842
+ ...(existing?.metadata ?? {}),
4843
+ ...(symbol.metadata ?? {})
4844
+ }
4845
+ });
4846
+ }
4847
+ return [...byId.values()];
4848
+ }
4849
+
4850
+ function uniqueSemanticSliceRegions(regions) {
4851
+ const seen = new Set();
4852
+ const result = [];
4853
+ for (const region of regions ?? []) {
4854
+ if (!region) continue;
4855
+ const id = region.id ?? `region_${idFragment(region.key ?? region.sourcePath ?? region.symbolId ?? result.length)}`;
4856
+ const key = region.key ?? id;
4857
+ const dedupeKey = `${id}:${key}`;
4858
+ if (seen.has(dedupeKey)) continue;
4859
+ seen.add(dedupeKey);
4860
+ result.push({ ...region, id, key });
4861
+ }
4862
+ return result;
4863
+ }
4864
+
4865
+ function uniqueSemanticSliceNativeNodes(nodes) {
4866
+ const seen = new Set();
4867
+ const result = [];
4868
+ for (const node of nodes ?? []) {
4869
+ const id = node?.id ?? node?.nodeId;
4870
+ if (!id || seen.has(id)) continue;
4871
+ seen.add(id);
4872
+ result.push({ ...node, id });
4873
+ }
4874
+ return result;
4875
+ }
4876
+
4877
+ function uniqueSemanticSliceSpans(spans) {
4878
+ const seen = new Set();
4879
+ const result = [];
4880
+ for (const span of spans ?? []) {
4881
+ if (!span) continue;
4882
+ const key = [
4883
+ span.path,
4884
+ span.sourceId,
4885
+ span.start,
4886
+ span.end,
4887
+ span.startLine,
4888
+ span.startColumn,
4889
+ span.endLine,
4890
+ span.endColumn
4891
+ ].join(':');
4892
+ if (seen.has(key)) continue;
4893
+ seen.add(key);
4894
+ result.push(span);
4895
+ }
4896
+ return result;
4897
+ }
4898
+
4899
+ function nativeAstNodes(nativeAst) {
4900
+ if (!nativeAst?.nodes) return [];
4901
+ return Object.values(nativeAst.nodes);
4902
+ }
4903
+
4904
+ function semanticSliceRegionFromSymbol(symbol, context) {
4905
+ const regionId = symbol.ownershipRegionId ?? symbol.metadata?.ownershipRegionId;
4906
+ const regionKey = symbol.ownershipKey ?? symbol.metadata?.ownershipRegionKey;
4907
+ if (!regionId && !regionKey) return undefined;
4908
+ return {
4909
+ id: regionId,
4910
+ key: regionKey,
4911
+ regionKind: symbol.ownershipRegionKind ?? symbol.metadata?.ownershipRegionKind ?? symbol.kind,
4912
+ granularity: 'symbol',
4913
+ language: symbol.language ?? context.language,
4914
+ sourcePath: semanticSliceSymbolSpan(symbol)?.path ?? context.sourcePath,
4915
+ sourceSpan: semanticSliceSymbolSpan(symbol),
4916
+ symbolId: symbol.id,
4917
+ symbolName: symbol.name,
4918
+ symbolKind: symbol.kind,
4919
+ nativeAstNodeId: symbol.nativeAstNodeId,
4920
+ precision: 'symbol',
4921
+ mergePolicy: 'semantic-region-review'
4922
+ };
4923
+ }
4924
+
4925
+ function semanticSliceRegionFromMapping(mapping, context) {
4926
+ if (!mapping.ownershipRegionId && !mapping.ownershipRegionKey) return undefined;
4927
+ return {
4928
+ id: mapping.ownershipRegionId,
4929
+ key: mapping.ownershipRegionKey,
4930
+ regionKind: mapping.ownershipRegionKind,
4931
+ granularity: 'source-map',
4932
+ language: context.language,
4933
+ sourcePath: mapping.sourceSpan?.path ?? context.sourcePath,
4934
+ sourceSpan: mapping.sourceSpan,
4935
+ symbolId: mapping.semanticSymbolId,
4936
+ nativeAstNodeId: mapping.nativeAstNodeId,
4937
+ precision: mapping.precision,
4938
+ mergePolicy: 'source-map-region-review'
4939
+ };
4940
+ }
4941
+
4942
+ function semanticSliceSymbolSpan(symbol) {
4943
+ return symbol.sourceSpan ?? symbol.definitionSpan ?? symbol.span;
4944
+ }
4945
+
4946
+ function semanticSliceSymbolMatchesRef(symbol, ref) {
4947
+ const normalized = normalizeSemanticSliceRef(ref, 'symbol');
4948
+ return [
4949
+ symbol.id,
4950
+ symbol.name,
4951
+ symbol.displayName,
4952
+ symbol.signature,
4953
+ symbol.nativeAstNodeId,
4954
+ symbol.metadata?.ownershipRegionId,
4955
+ symbol.metadata?.ownershipRegionKey
4956
+ ].filter(Boolean).some((value) => semanticSliceValueMatches(value, normalized));
4957
+ }
4958
+
4959
+ function semanticSliceRegionMatchesRef(region, ref) {
4960
+ const normalized = normalizeSemanticSliceRef(ref, 'region');
4961
+ return [
4962
+ region.id,
4963
+ region.key,
4964
+ region.symbolId,
4965
+ region.symbolName,
4966
+ region.nativeAstNodeId,
4967
+ region.sourcePath
4968
+ ].filter(Boolean).some((value) => semanticSliceValueMatches(value, normalized));
4969
+ }
4970
+
4971
+ function semanticSliceNativeNodeMatchesRef(node, ref) {
4972
+ const normalized = normalizeSemanticSliceRef(ref, 'native');
4973
+ return [
4974
+ node.id,
4975
+ node.nodeId,
4976
+ node.name,
4977
+ node.symbolId,
4978
+ node.kind,
4979
+ node.languageKind
4980
+ ].filter(Boolean).some((value) => semanticSliceValueMatches(value, normalized));
4981
+ }
4982
+
4983
+ function semanticSliceMappingMatchesRef(mapping, ref) {
4984
+ const pathRef = normalizeSemanticSliceRef(ref, 'path');
4985
+ const normalized = normalizeSemanticSliceRef(ref, 'mapping');
4986
+ return [
4987
+ mapping.id,
4988
+ mapping.semanticSymbolId,
4989
+ mapping.semanticOccurrenceId,
4990
+ mapping.semanticNodeId,
4991
+ mapping.nativeAstNodeId,
4992
+ mapping.ownershipRegionId,
4993
+ mapping.ownershipRegionKey
4994
+ ].filter(Boolean).some((value) => semanticSliceValueMatches(value, normalized))
4995
+ || semanticSliceValueMatches(mapping.sourceSpan?.path ?? mapping.sourceMapSourcePath, pathRef);
4996
+ }
4997
+
4998
+ function normalizeSemanticSliceRef(ref, prefix) {
4999
+ const text = String(ref ?? '');
5000
+ return text.startsWith(`${prefix}:`) ? text.slice(prefix.length + 1) : text;
5001
+ }
5002
+
5003
+ function semanticSliceValueMatches(value, ref) {
5004
+ if (!value || !ref) return false;
5005
+ const left = String(value);
5006
+ const right = String(ref);
5007
+ return left === right || left.endsWith(`:${right}`) || left.includes(right);
5008
+ }
5009
+
5010
+ function semanticSliceMappingTouchesSets(mapping, selection) {
5011
+ return selection.selectedMappings.has(mapping.id)
5012
+ || selection.selectedSymbols.has(mapping.semanticSymbolId)
5013
+ || selection.selectedNativeNodes.has(mapping.nativeAstNodeId)
5014
+ || selection.selectedRegions.has(mapping.ownershipRegionId)
5015
+ || selection.selectedRegions.has(mapping.ownershipRegionKey);
5016
+ }
5017
+
5018
+ function semanticSliceSymbolTouchesSets(symbol, selection) {
5019
+ return selection.selectedSymbols.has(symbol.id)
5020
+ || selection.selectedNativeNodes.has(symbol.nativeAstNodeId)
5021
+ || selection.selectedRegions.has(symbol.ownershipRegionId)
5022
+ || selection.selectedRegions.has(symbol.metadata?.ownershipRegionId)
5023
+ || selection.selectedRegions.has(symbol.metadata?.ownershipRegionKey);
5024
+ }
5025
+
5026
+ function semanticSliceRegionTouchesSets(region, selection) {
5027
+ return selection.selectedRegions.has(region.id)
5028
+ || selection.selectedRegions.has(region.key)
5029
+ || selection.selectedSymbols.has(region.symbolId)
5030
+ || selection.selectedNativeNodes.has(region.nativeAstNodeId);
5031
+ }
5032
+
5033
+ function semanticSliceSpanTouchesSelection(span, mappings, regions) {
5034
+ if (!span) return false;
5035
+ return mappings.some((mapping) => mapping.sourceSpan === span || semanticSliceSpansOverlap(mapping.sourceSpan, span))
5036
+ || regions.some((region) => semanticSliceSpansOverlap(region.sourceSpan, span));
5037
+ }
5038
+
5039
+ function semanticSliceSpansOverlap(left, right) {
5040
+ if (!left || !right) return false;
5041
+ const leftPath = left.path ?? left.sourceId;
5042
+ const rightPath = right.path ?? right.sourceId;
5043
+ if (leftPath && rightPath && leftPath !== rightPath) return false;
5044
+ if (typeof left.start === 'number' && typeof left.end === 'number' && typeof right.start === 'number' && typeof right.end === 'number') {
5045
+ return left.start <= right.end && right.start <= left.end;
5046
+ }
5047
+ if (typeof left.startLine === 'number' && typeof right.startLine === 'number') {
5048
+ const leftEnd = left.endLine ?? left.startLine;
5049
+ const rightEnd = right.endLine ?? right.startLine;
5050
+ return left.startLine <= rightEnd && right.startLine <= leftEnd;
5051
+ }
5052
+ return false;
5053
+ }
5054
+
5055
+ function semanticSliceNativeParentMap(nativeNodes) {
5056
+ const parents = new Map();
5057
+ for (const node of nativeNodes ?? []) {
5058
+ for (const child of node.children ?? []) parents.set(child, node.id);
5059
+ }
5060
+ return parents;
5061
+ }
5062
+
5063
+ function addSet(set, value) {
5064
+ if (!value || set.has(value)) return false;
5065
+ set.add(value);
5066
+ return true;
5067
+ }
5068
+
5069
+ function readStringArray(value) {
5070
+ if (value === undefined || value === null) return [];
5071
+ if (Array.isArray(value)) return value.flatMap((entry) => readStringArray(entry));
5072
+ return String(value).split(',').map((entry) => entry.trim()).filter(Boolean);
5073
+ }
5074
+
4158
5075
  function normalizeNativeDiffImport(value, input, side) {
4159
5076
  if (!value) return undefined;
4160
5077
  if (value.kind === 'frontier.lang.importResult' && value.nativeSource) return value;
@@ -7191,6 +8108,211 @@ function projectionTargetLossMatrixSummary(languages) {
7191
8108
  };
7192
8109
  }
7193
8110
 
8111
+ function universalCapabilityLanguageRow(importCoverage, context) {
8112
+ const languageIds = universalLanguageIds(importCoverage);
8113
+ const parserRows = (context.parserFeatures.parsers ?? []).filter((row) => universalLanguageIds(row).some((id) => languageIds.includes(id)));
8114
+ const parserLanguage = (context.parserFeatures.languages ?? []).find((row) => universalLanguageIds(row).some((id) => languageIds.includes(id)));
8115
+ const projection = (context.projectionTargets.languages ?? []).find((row) => universalLanguageIds(row).some((id) => languageIds.includes(id)));
8116
+ const parserReadiness = parserRows.length
8117
+ ? parserRows.reduce((current, row) => maxSemanticMergeReadiness(current, row.merge?.readiness ?? row.imports?.readiness ?? 'blocked'), 'ready')
8118
+ : 'blocked';
8119
+ const sourceProjectionReadiness = [projection?.sourceProjection?.exactSource, projection?.sourceProjection?.stubs]
8120
+ .filter(Boolean)
8121
+ .reduce((current, entry) => maxSemanticMergeReadiness(current, entry.readiness ?? 'blocked'), 'ready');
8122
+ const targetReadiness = (projection?.targets ?? []).length
8123
+ ? projection.targets.reduce((current, entry) => maxSemanticMergeReadiness(current, entry.readiness ?? 'blocked'), 'ready')
8124
+ : 'blocked';
8125
+ const projectionReadiness = maxSemanticMergeReadiness(sourceProjectionReadiness, targetReadiness);
8126
+ const readiness = maxSemanticMergeReadiness(importCoverage.imports.readiness, maxSemanticMergeReadiness(parserReadiness, projectionReadiness));
8127
+ const parserBlockingFeatures = uniqueStrings(parserRows.flatMap((row) => row.merge?.blockingFeatures ?? []));
8128
+ const parserReviewFeatures = uniqueStrings(parserRows.flatMap((row) => row.merge?.reviewFeatures ?? []));
8129
+ const missingTargets = (projection?.targets ?? []).filter((entry) => entry.lossClass === 'missingAdapter').map((entry) => entry.target);
8130
+ const unsupportedTargets = (projection?.targets ?? []).filter((entry) => entry.lossClass === 'unsupportedTargetFeatures').map((entry) => entry.target);
8131
+ const blockers = universalCapabilityBlockers({
8132
+ importCoverage,
8133
+ parserRows,
8134
+ parserReadiness,
8135
+ parserBlockingFeatures,
8136
+ projection,
8137
+ missingTargets,
8138
+ readiness
8139
+ });
8140
+ const review = universalCapabilityReviewReasons({
8141
+ importCoverage,
8142
+ parserRows,
8143
+ parserReviewFeatures,
8144
+ unsupportedTargets,
8145
+ readiness
8146
+ });
8147
+ return {
8148
+ language: importCoverage.language,
8149
+ aliases: importCoverage.aliases,
8150
+ extensions: importCoverage.extensions,
8151
+ readiness,
8152
+ imports: {
8153
+ total: importCoverage.imports.total,
8154
+ readiness: importCoverage.imports.readiness,
8155
+ symbols: importCoverage.imports.symbols,
8156
+ sourceMaps: importCoverage.imports.sourceMaps,
8157
+ sourceMapMappings: importCoverage.imports.sourceMapMappings,
8158
+ losses: importCoverage.imports.losses,
8159
+ lossKinds: importCoverage.imports.lossKinds,
8160
+ readinessReasons: importCoverage.imports.readinessReasons
8161
+ },
8162
+ parser: {
8163
+ readiness: parserReadiness,
8164
+ rows: parserRows.length,
8165
+ parsers: parserRows.map((row) => row.parser),
8166
+ mergeReadyParsers: parserRows.filter((row) => row.merge?.mergeReady).map((row) => row.parser),
8167
+ blockingFeatures: parserBlockingFeatures,
8168
+ reviewFeatures: parserReviewFeatures,
8169
+ languageSummary: parserLanguage
8170
+ },
8171
+ projection: {
8172
+ readiness: projectionReadiness,
8173
+ sourceProjection: projection?.sourceProjection,
8174
+ targets: projection?.targets ?? [],
8175
+ summary: projection?.summary ?? {
8176
+ imports: 0,
8177
+ parserAdapters: 0,
8178
+ targetEntries: 0,
8179
+ byLossClass: {},
8180
+ exactSourceImports: 0,
8181
+ stubDeclarationImports: 0
8182
+ },
8183
+ missingTargets,
8184
+ unsupportedTargets
8185
+ },
8186
+ evidence: {
8187
+ parserAdapters: importCoverage.parserAdapters.length,
8188
+ adapterCoverageSummaries: importCoverage.adapterCoverage.total,
8189
+ adapterCoverageGaps: importCoverage.adapterCoverage.gaps,
8190
+ knownLossKinds: importCoverage.knownLossKinds,
8191
+ sourceMapMappings: importCoverage.imports.sourceMapMappings
8192
+ },
8193
+ blockers,
8194
+ review
8195
+ };
8196
+ }
8197
+
8198
+ function universalCapabilityBlockers(input) {
8199
+ const blockers = [];
8200
+ if ((input.importCoverage.imports.total ?? 0) === 0) {
8201
+ blockers.push('No native import evidence observed for this language.');
8202
+ }
8203
+ if (!input.parserRows.length) {
8204
+ blockers.push('No parser feature row matched this language.');
8205
+ }
8206
+ if (input.parserReadiness === 'blocked') {
8207
+ blockers.push('Parser feature readiness is blocked.');
8208
+ }
8209
+ for (const feature of input.parserBlockingFeatures) {
8210
+ blockers.push(`Required parser feature is not merge-ready: ${feature}.`);
8211
+ }
8212
+ if (!input.projection) {
8213
+ blockers.push('No projection coverage row matched this language.');
8214
+ }
8215
+ for (const target of input.missingTargets) {
8216
+ blockers.push(`Missing native-to-target projection adapter for ${target}.`);
8217
+ }
8218
+ if (input.readiness === 'blocked' && blockers.length === 0) {
8219
+ blockers.push('Combined universal capability readiness is blocked.');
8220
+ }
8221
+ return uniqueStrings(blockers);
8222
+ }
8223
+
8224
+ function universalCapabilityReviewReasons(input) {
8225
+ const review = [];
8226
+ if ((input.importCoverage.imports.losses ?? 0) > 0) {
8227
+ review.push(`Native import evidence carries ${input.importCoverage.imports.losses} loss record(s).`);
8228
+ }
8229
+ for (const reason of input.importCoverage.imports.readinessReasons ?? []) {
8230
+ review.push(reason);
8231
+ }
8232
+ for (const feature of input.parserReviewFeatures) {
8233
+ review.push(`Parser feature needs review: ${feature}.`);
8234
+ }
8235
+ for (const target of input.unsupportedTargets) {
8236
+ review.push(`Target projection has unsupported feature losses for ${target}.`);
8237
+ }
8238
+ if (input.readiness === 'needs-review') {
8239
+ review.push('Combined universal capability readiness requires review.');
8240
+ } else if (input.readiness === 'ready-with-losses') {
8241
+ review.push('Combined universal capability readiness is ready with disclosed losses.');
8242
+ }
8243
+ return uniqueStrings(review);
8244
+ }
8245
+
8246
+ function universalCapabilityMatrixSummary(rows) {
8247
+ const byReadiness = {};
8248
+ const byImportReadiness = {};
8249
+ const byParserReadiness = {};
8250
+ const byProjectionReadiness = {};
8251
+ let imports = 0;
8252
+ let symbols = 0;
8253
+ let sourceMapMappings = 0;
8254
+ let losses = 0;
8255
+ let parserRows = 0;
8256
+ let parserMergeReady = 0;
8257
+ let targetEntries = 0;
8258
+ let missingAdapters = 0;
8259
+ let unsupportedTargetFeatures = 0;
8260
+ let exactSourceProjection = 0;
8261
+ let nativeSourceStubs = 0;
8262
+ let blockers = 0;
8263
+ let reviewReasons = 0;
8264
+ for (const row of rows) {
8265
+ byReadiness[row.readiness] = (byReadiness[row.readiness] ?? 0) + 1;
8266
+ byImportReadiness[row.imports.readiness] = (byImportReadiness[row.imports.readiness] ?? 0) + 1;
8267
+ byParserReadiness[row.parser.readiness] = (byParserReadiness[row.parser.readiness] ?? 0) + 1;
8268
+ byProjectionReadiness[row.projection.readiness] = (byProjectionReadiness[row.projection.readiness] ?? 0) + 1;
8269
+ imports += row.imports.total;
8270
+ symbols += row.imports.symbols;
8271
+ sourceMapMappings += row.imports.sourceMapMappings;
8272
+ losses += row.imports.losses;
8273
+ parserRows += row.parser.rows;
8274
+ parserMergeReady += row.parser.mergeReadyParsers.length;
8275
+ targetEntries += row.projection.targets.length;
8276
+ missingAdapters += row.projection.missingTargets.length;
8277
+ unsupportedTargetFeatures += row.projection.unsupportedTargets.length;
8278
+ exactSourceProjection += row.projection.summary.byLossClass?.exactSourceProjection ?? 0;
8279
+ nativeSourceStubs += row.projection.summary.byLossClass?.nativeSourceStubs ?? 0;
8280
+ blockers += row.blockers.length;
8281
+ reviewReasons += row.review.length;
8282
+ }
8283
+ return {
8284
+ languages: rows.length,
8285
+ imports,
8286
+ symbols,
8287
+ sourceMapMappings,
8288
+ losses,
8289
+ parserRows,
8290
+ parserMergeReady,
8291
+ targetEntries,
8292
+ missingAdapters,
8293
+ unsupportedTargetFeatures,
8294
+ exactSourceProjection,
8295
+ nativeSourceStubs,
8296
+ blockers,
8297
+ reviewReasons,
8298
+ readyLanguages: rows.filter((row) => row.readiness === 'ready').length,
8299
+ readyWithLossesLanguages: rows.filter((row) => row.readiness === 'ready-with-losses').length,
8300
+ reviewLanguages: rows.filter((row) => row.readiness === 'needs-review').length,
8301
+ blockedLanguages: rows.filter((row) => row.readiness === 'blocked').length,
8302
+ byReadiness,
8303
+ byImportReadiness,
8304
+ byParserReadiness,
8305
+ byProjectionReadiness
8306
+ };
8307
+ }
8308
+
8309
+ function universalLanguageIds(entry) {
8310
+ return uniqueStrings([
8311
+ entry?.language,
8312
+ ...(entry?.aliases ?? [])
8313
+ ].map(normalizeNativeLanguageId).filter(Boolean));
8314
+ }
8315
+
7194
8316
  function countProjectionLossClasses(entries) {
7195
8317
  const counts = {};
7196
8318
  for (const entry of entries ?? []) {