@shapeshift-labs/frontier-lang-compiler 0.2.52 → 0.2.54

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/README.md CHANGED
@@ -183,6 +183,7 @@ console.log(pythonToRust.mergeScore.value); // sortable merge-review score, not
183
183
  const conversionArtifacts = createUniversalConversionArtifacts(conversionPlan);
184
184
  console.log(conversionArtifacts.historyRecords[0].kind); // "frontier.lang.semanticHistoryRecord"
185
185
  console.log(conversionArtifacts.patchBundleRecords[0].admission.autoMergeClaim); // false
186
+ console.log(conversionArtifacts.admissionRecords[0].admissionBucket); // "blocked", "needs-evidence", "merge-ready", etc.
186
187
  console.log(conversionArtifacts.index.semanticOperationKinds); // sourcePreservation/projection/merge
187
188
  console.log(conversionArtifacts.routeArtifacts[0].semanticOperations.summary.conflictKeys);
188
189
  ```
@@ -199,7 +200,7 @@ The projection target matrix separates five runtime/API classes:
199
200
 
200
201
  `createUniversalConversionPlan` turns that capability evidence into coordinator tasks: preserve exact source, run a target adapter, emit stubs, attach semantic-index evidence, or block the route until missing parser/adapter/proof evidence exists. Every route carries `autoMergeClaim: false`, `semanticEquivalenceClaim: false`, missing evidence, task hints, and a `frontier.lang.semanticMergeScore.v1` score for swarm merge admission.
201
202
 
202
- `createUniversalConversionArtifacts` materializes those route refs into compact `SemanticHistoryRecord`, `SemanticPatchBundleRecord`, and semantic-operation artifacts that swarm collectors can index by route, history ID, patch-bundle ID, operation kind, source path, ownership key, conflict key, evidence, proof, readiness, and admission status. It is still review evidence, not target-code proof: blocked and semantic-index-only routes stay blocked/needs-review, and every artifact keeps `autoMergeClaim: false` plus `semanticEquivalenceClaim: false`.
203
+ `createUniversalConversionArtifacts` materializes those route refs into compact `SemanticHistoryRecord`, `SemanticPatchBundleRecord`, semantic operation sets, and `UniversalConversionAdmissionRecord` rows that swarm collectors can index by route, history ID, patch-bundle ID, admission-record ID, admission bucket, risk, operation kind, source path, ownership key, conflict key, evidence, proof, readiness, and admission status. It is still review evidence, not target-code proof: blocked and semantic-index-only routes stay blocked/needs-review, and every artifact keeps `autoMergeClaim: false` plus `semanticEquivalenceClaim: false`.
203
204
 
204
205
  Preserve exact native source text, token/trivia hashes, comments, whitespace, and source directives as evidence. This does not claim full semantic understanding; it keeps round-trip material available while exact parser adapters catch up:
205
206
 
@@ -95,6 +95,22 @@ export function measureNativeTransformations(nativeImportResults) {
95
95
  conversionArtifacts: conversionArtifacts.summary.routes,
96
96
  conversionArtifactHistories: conversionArtifacts.summary.histories,
97
97
  conversionArtifactPatchBundles: conversionArtifacts.summary.patchBundles,
98
+ conversionArtifactAdmissionRecords: conversionArtifacts.summary.admissionRecords,
99
+ conversionArtifactMergeReady: conversionArtifacts.summary.mergeReady,
100
+ conversionArtifactNeedsEvidence: conversionArtifacts.summary.needsEvidence,
101
+ conversionArtifactNeedsAdapter: conversionArtifacts.summary.needsAdapter,
102
+ conversionArtifactNeedsReview: conversionArtifacts.summary.needsReview,
103
+ conversionArtifactAdmissionBlocked: conversionArtifacts.summary.admissionBlocked,
104
+ conversionArtifactLowRisk: conversionArtifacts.summary.lowRisk,
105
+ conversionArtifactMediumRisk: conversionArtifacts.summary.mediumRisk,
106
+ conversionArtifactHighRisk: conversionArtifacts.summary.highRisk,
107
+ conversionArtifactQueued: conversionArtifacts.summary.queued,
108
+ conversionArtifactAdmissionReasonCodes: conversionArtifacts.summary.reasonCodes,
109
+ conversionArtifactAdmissionMissingEvidence: conversionArtifacts.summary.missingEvidence,
110
+ conversionArtifactAdmissionBlockers: conversionArtifacts.summary.blockers,
111
+ conversionArtifactAdmissionReviewReasons: conversionArtifacts.summary.reviewReasons,
112
+ conversionArtifactAdmissionEvidenceIds: conversionArtifacts.summary.evidenceIds,
113
+ conversionArtifactAdmissionProofIds: conversionArtifacts.summary.proofIds,
98
114
  conversionArtifactsDurationMs,
99
115
  featureEvidencePolicyMatches,
100
116
  featureEvidenceDurationMs,
package/bench/smoke.mjs CHANGED
@@ -63,6 +63,22 @@ console.log(JSON.stringify({
63
63
  conversionArtifacts: transformMetrics.conversionArtifacts,
64
64
  conversionArtifactHistories: transformMetrics.conversionArtifactHistories,
65
65
  conversionArtifactPatchBundles: transformMetrics.conversionArtifactPatchBundles,
66
+ conversionArtifactAdmissionRecords: transformMetrics.conversionArtifactAdmissionRecords,
67
+ conversionArtifactMergeReady: transformMetrics.conversionArtifactMergeReady,
68
+ conversionArtifactNeedsEvidence: transformMetrics.conversionArtifactNeedsEvidence,
69
+ conversionArtifactNeedsAdapter: transformMetrics.conversionArtifactNeedsAdapter,
70
+ conversionArtifactNeedsReview: transformMetrics.conversionArtifactNeedsReview,
71
+ conversionArtifactAdmissionBlocked: transformMetrics.conversionArtifactAdmissionBlocked,
72
+ conversionArtifactLowRisk: transformMetrics.conversionArtifactLowRisk,
73
+ conversionArtifactMediumRisk: transformMetrics.conversionArtifactMediumRisk,
74
+ conversionArtifactHighRisk: transformMetrics.conversionArtifactHighRisk,
75
+ conversionArtifactQueued: transformMetrics.conversionArtifactQueued,
76
+ conversionArtifactAdmissionReasonCodes: transformMetrics.conversionArtifactAdmissionReasonCodes,
77
+ conversionArtifactAdmissionMissingEvidence: transformMetrics.conversionArtifactAdmissionMissingEvidence,
78
+ conversionArtifactAdmissionBlockers: transformMetrics.conversionArtifactAdmissionBlockers,
79
+ conversionArtifactAdmissionReviewReasons: transformMetrics.conversionArtifactAdmissionReviewReasons,
80
+ conversionArtifactAdmissionEvidenceIds: transformMetrics.conversionArtifactAdmissionEvidenceIds,
81
+ conversionArtifactAdmissionProofIds: transformMetrics.conversionArtifactAdmissionProofIds,
66
82
  conversionArtifactsDurationMs: Number(transformMetrics.conversionArtifactsDurationMs.toFixed(2)),
67
83
  featureEvidencePolicyMatches: transformMetrics.featureEvidencePolicyMatches,
68
84
  featureEvidenceDurationMs: Number(transformMetrics.featureEvidenceDurationMs.toFixed(2)),
@@ -81,6 +97,7 @@ console.log(JSON.stringify({
81
97
  nativeTargetAdapterDurationMs: Number(transformMetrics.nativeTargetAdapterDurationMs.toFixed(2)),
82
98
  regionScanImports: sourceChangeMetrics.regionScanImports,
83
99
  regionScanSymbols: sourceChangeMetrics.regionScanSymbols,
100
+ regionScanDependencyRelations: sourceChangeMetrics.regionScanDependencyRelations,
84
101
  regionScanOwnershipRegions: sourceChangeMetrics.regionScanOwnershipRegions,
85
102
  regionScanDurationMs: Number(sourceChangeMetrics.regionScanDurationMs.toFixed(2)),
86
103
  changeProjectionSets: sourceChangeMetrics.changeProjectionSets,
@@ -22,6 +22,7 @@ function measureRegionScan() {
22
22
  language: 'typescript',
23
23
  sourcePath: `src/regions-${index}.ts`,
24
24
  sourceText: `
25
+ export function normalizeCount${index}(value) { return value; }
25
26
  export const appRoutes${index} = [
26
27
  { path: "/${index}", component: Screen${index} },
27
28
  { path: "/${index}/settings", component: Settings${index} }
@@ -31,7 +32,7 @@ function measureRegionScan() {
31
32
  legal: { title: "Legal ${index}" }
32
33
  };
33
34
  export const runtimeConfig${index} = {
34
- limits: { count: ${index} },
35
+ limits: { count: normalizeCount${index}(${index}) },
35
36
  resolve(id) { return id; }
36
37
  };
37
38
  export const helpers${index} = {
@@ -45,6 +46,7 @@ function measureRegionScan() {
45
46
  return {
46
47
  regionScanImports: regionScanImports.length,
47
48
  regionScanSymbols: regionScanImports.reduce((sum, entry) => sum + entry.imported.semanticIndex.symbols.length, 0),
49
+ regionScanDependencyRelations: regionScanImports.reduce((sum, entry) => sum + entry.sidecar.dependencies.total, 0),
48
50
  regionScanOwnershipRegions: regionScanImports.reduce((sum, entry) => sum + entry.sidecar.ownershipRegions.length, 0),
49
51
  regionScanDurationMs
50
52
  };
@@ -132,6 +132,7 @@ export interface SemanticImportSidecarImportEntry {
132
132
  readonly universalAstLayerIds: readonly string[];
133
133
  readonly proofSpec: SemanticImportSidecarProofSpecSummary;
134
134
  readonly paradigmSemantics: SemanticImportSidecarParadigmSemanticsSummary;
135
+ readonly dependencyRelationCount: number; readonly dependencyPredicates: readonly string[];
135
136
  readonly readiness: SemanticMergeReadiness;
136
137
  readonly emptySemanticIndex: boolean;
137
138
  readonly regionTaxonomy?: SemanticImportRegionTaxonomySummary;
@@ -228,6 +229,13 @@ export interface SemanticImportSidecarParadigmSemanticsSummary {
228
229
  readonly empty: boolean;
229
230
  }
230
231
 
232
+ export interface SemanticImportDependencySummary {
233
+ readonly total: number; readonly calls: number; readonly uses: number; readonly references: number;
234
+ readonly imports: number; readonly extends: number; readonly implements: number; readonly includes: number; readonly requires: number;
235
+ readonly byPredicate: Readonly<Record<string, number>>; readonly predicates: readonly string[];
236
+ readonly ids: readonly string[]; readonly sourceSymbolIds: readonly string[]; readonly targetSymbolIds: readonly string[];
237
+ }
238
+
231
239
  export interface SemanticImportSidecar {
232
240
  readonly kind: 'frontier.lang.semanticImportSidecar';
233
241
  readonly version: 1;
@@ -238,11 +246,7 @@ export interface SemanticImportSidecar {
238
246
  readonly imports: readonly SemanticImportSidecarImportEntry[];
239
247
  readonly symbols: readonly SemanticImportSidecarSymbol[];
240
248
  readonly ownershipRegions: readonly SemanticImportOwnershipRegion[];
241
- readonly sourceMaps: {
242
- readonly total: number;
243
- readonly mappings: number;
244
- readonly ids: readonly string[];
245
- };
249
+ readonly sourceMaps: { readonly total: number; readonly mappings: number; readonly ids: readonly string[] };
246
250
  readonly sourcePreservation: {
247
251
  readonly total: number;
248
252
  readonly ids: readonly string[];
@@ -259,6 +263,7 @@ export interface SemanticImportSidecar {
259
263
  readonly universalAstLayers: SemanticImportSidecarUniversalAstLayerSummary;
260
264
  readonly proofSpec: SemanticImportSidecarProofSpecSummary;
261
265
  readonly paradigmSemantics: SemanticImportSidecarParadigmSemanticsSummary;
266
+ readonly dependencies: SemanticImportDependencySummary;
262
267
  readonly patchHints: readonly SemanticImportPatchHint[];
263
268
  readonly quality: SemanticImportSidecarQuality;
264
269
  readonly admission: SemanticImportSidecarAdmission;
@@ -280,11 +285,7 @@ export interface SemanticImportSidecar {
280
285
  readonly reviewLossIds: readonly string[];
281
286
  };
282
287
  readonly regionTaxonomy: SemanticImportRegionTaxonomySummary;
283
- readonly evidence: {
284
- readonly total: number;
285
- readonly failed: readonly string[];
286
- readonly ids: readonly string[];
287
- };
288
+ readonly evidence: { readonly total: number; readonly failed: readonly string[]; readonly ids: readonly string[] };
288
289
  readonly summary: {
289
290
  readonly imports: number;
290
291
  readonly symbols: number;
@@ -300,6 +301,7 @@ export interface SemanticImportSidecar {
300
301
  readonly paradigmSemanticsRecords: number;
301
302
  readonly paradigmSemanticsGroups: number;
302
303
  readonly paradigmSemanticsLoweringRecords: number;
304
+ readonly dependencyRelations: number; readonly dependencyPredicates: readonly string[];
303
305
  readonly patchHints: number;
304
306
  readonly evidenceWarnings: number;
305
307
  readonly readiness: SemanticMergeReadiness;
@@ -14,10 +14,18 @@ import type {
14
14
  UniversalConversionRoute,
15
15
  UniversalConversionRouteAction,
16
16
  UniversalConversionRouteMode,
17
- UniversalConversionMergeScore
17
+ UniversalConversionMergeScore,
18
+ UniversalConversionRisk
18
19
  } from './universal-conversion-plan.js';
19
20
 
20
21
  export type UniversalConversionArtifactAdmissionStatus = 'queued' | 'needs-review' | 'blocked' | string;
22
+ export type UniversalConversionArtifactAdmissionBucket =
23
+ | 'merge-ready'
24
+ | 'needs-evidence'
25
+ | 'needs-adapter'
26
+ | 'needs-review'
27
+ | 'blocked'
28
+ | string;
21
29
  export type UniversalConversionArtifactMaterializationStatus = 'materialized' | 'planned-only' | 'missing' | 'failed' | string;
22
30
 
23
31
  export interface UniversalConversionArtifactMaterialization {
@@ -33,6 +41,69 @@ export interface UniversalConversionArtifactMaterialization {
33
41
  readonly semanticEquivalenceClaim: false;
34
42
  }
35
43
 
44
+ export interface UniversalConversionAdmissionRecord {
45
+ readonly kind: 'frontier.lang.universalConversionAdmissionRecord';
46
+ readonly version: 1;
47
+ readonly schema: 'frontier.lang.universalConversionAdmissionRecord.v1';
48
+ readonly id: string;
49
+ readonly routeId: string;
50
+ readonly planId?: string;
51
+ readonly sourceLanguage?: FrontierSourceLanguage | string;
52
+ readonly target?: FrontierCompileTarget | string;
53
+ readonly mode: UniversalConversionRouteMode;
54
+ readonly routeAction: UniversalConversionRouteAction;
55
+ readonly admissionStatus: UniversalConversionArtifactAdmissionStatus;
56
+ readonly admissionAction: UniversalConversionAdmissionAction;
57
+ readonly admissionBucket: UniversalConversionArtifactAdmissionBucket;
58
+ readonly reviewRequired: true;
59
+ readonly readiness: SemanticMergeReadiness | string;
60
+ readonly risk: UniversalConversionRisk | string;
61
+ readonly score: {
62
+ readonly value: number;
63
+ readonly uncappedValue: number;
64
+ readonly sortKey: number;
65
+ readonly higherIsBetter: true;
66
+ readonly componentStatuses: Record<string, string>;
67
+ readonly penalties: readonly string[];
68
+ };
69
+ readonly ids: {
70
+ readonly historyId?: string;
71
+ readonly patchBundleId?: string;
72
+ readonly semanticOperationIds: readonly string[];
73
+ readonly sourceMapLinkIds: readonly string[];
74
+ readonly evidenceIds: readonly string[];
75
+ readonly proofIds: readonly string[];
76
+ };
77
+ readonly semanticOperations: {
78
+ readonly total: number;
79
+ readonly byKind: Record<string, number>;
80
+ readonly kinds: readonly string[];
81
+ readonly dynamic: readonly string[];
82
+ readonly opaque: readonly string[];
83
+ };
84
+ readonly ownership: {
85
+ readonly keys: readonly string[];
86
+ readonly conflictKeys: readonly string[];
87
+ };
88
+ readonly source: {
89
+ readonly paths: readonly string[];
90
+ readonly hashes: readonly string[];
91
+ readonly baseHashes: readonly string[];
92
+ readonly targetHashes: readonly string[];
93
+ };
94
+ readonly evidence: {
95
+ readonly total: number;
96
+ readonly proofArtifacts: number;
97
+ readonly missing: readonly string[];
98
+ readonly blockers: readonly string[];
99
+ readonly review: readonly string[];
100
+ };
101
+ readonly reasons: readonly string[];
102
+ readonly autoMergeClaim: false;
103
+ readonly semanticEquivalenceClaim: false;
104
+ readonly metadata: Record<string, unknown>;
105
+ }
106
+
36
107
  export interface UniversalConversionRouteArtifact {
37
108
  readonly kind: 'frontier.lang.universalConversionRouteArtifact';
38
109
  readonly version: 1;
@@ -50,9 +121,11 @@ export interface UniversalConversionRouteArtifact {
50
121
  readonly reviewRequired: true;
51
122
  readonly history: SemanticHistoryRecord;
52
123
  readonly patchBundle: SemanticPatchBundleRecord;
124
+ readonly admissionRecord: UniversalConversionAdmissionRecord;
53
125
  readonly semanticOperations: SemanticOperationSet;
54
126
  readonly materialization: UniversalConversionArtifactMaterialization;
55
127
  readonly mergeScore?: UniversalConversionMergeScore;
128
+ readonly admissionBucket: UniversalConversionArtifactAdmissionBucket;
56
129
  readonly autoMergeClaim: false;
57
130
  readonly semanticEquivalenceClaim: false;
58
131
  readonly metadata: Record<string, unknown>;
@@ -62,11 +135,14 @@ export interface UniversalConversionArtifactIndex {
62
135
  readonly routeIds: readonly string[];
63
136
  readonly historyIds: readonly string[];
64
137
  readonly patchBundleIds: readonly string[];
138
+ readonly admissionRecordIds: readonly string[];
65
139
  readonly languages: readonly string[];
66
140
  readonly targets: readonly string[];
67
141
  readonly modes: readonly string[];
68
142
  readonly readinesses: readonly string[];
69
143
  readonly admissionStatuses: readonly string[];
144
+ readonly admissionBuckets: readonly string[];
145
+ readonly admissionRisks: readonly string[];
70
146
  readonly sourcePaths: readonly string[];
71
147
  readonly sourceHashes: readonly string[];
72
148
  readonly ownershipKeys: readonly string[];
@@ -87,14 +163,31 @@ export interface UniversalConversionArtifacts {
87
163
  readonly routeArtifacts: readonly UniversalConversionRouteArtifact[];
88
164
  readonly historyRecords: readonly SemanticHistoryRecord[];
89
165
  readonly patchBundleRecords: readonly SemanticPatchBundleRecord[];
166
+ readonly admissionRecords: readonly UniversalConversionAdmissionRecord[];
90
167
  readonly index: UniversalConversionArtifactIndex;
91
168
  readonly summary: {
92
169
  readonly routes: number;
93
170
  readonly histories: number;
94
171
  readonly patchBundles: number;
172
+ readonly admissionRecords: number;
95
173
  readonly semanticOperations: number;
174
+ readonly mergeReady: number;
175
+ readonly needsEvidence: number;
176
+ readonly needsAdapter: number;
177
+ readonly needsReview: number;
178
+ readonly admissionBlocked: number;
179
+ readonly lowRisk: number;
180
+ readonly mediumRisk: number;
181
+ readonly highRisk: number;
182
+ readonly queued: number;
96
183
  readonly reviewRequired: number;
97
184
  readonly blocked: number;
185
+ readonly reasonCodes: number;
186
+ readonly missingEvidence: number;
187
+ readonly blockers: number;
188
+ readonly reviewReasons: number;
189
+ readonly evidenceIds: number;
190
+ readonly proofIds: number;
98
191
  readonly autoMergeClaims: 0;
99
192
  readonly semanticEquivalenceClaims: 0;
100
193
  };
@@ -128,12 +221,15 @@ export interface UniversalConversionArtifactQuery {
128
221
  readonly routeId?: string | readonly string[];
129
222
  readonly historyId?: string | readonly string[];
130
223
  readonly patchBundleId?: string | readonly string[];
224
+ readonly admissionRecordId?: string | readonly string[];
131
225
  readonly sourceLanguage?: FrontierSourceLanguage | string | readonly string[];
132
226
  readonly target?: FrontierCompileTarget | string | readonly string[];
133
227
  readonly mode?: UniversalConversionRouteMode | readonly string[];
134
228
  readonly readiness?: SemanticMergeReadiness | string | readonly string[];
135
229
  readonly admissionAction?: UniversalConversionAdmissionAction | readonly string[];
136
230
  readonly admissionStatus?: UniversalConversionArtifactAdmissionStatus | readonly string[];
231
+ readonly admissionBucket?: UniversalConversionArtifactAdmissionBucket | readonly string[];
232
+ readonly risk?: UniversalConversionRisk | string | readonly string[];
137
233
  readonly priority?: UniversalConversionPriority | readonly string[];
138
234
  readonly routeAction?: UniversalConversionRouteAction | readonly string[];
139
235
  readonly sourcePath?: string | readonly string[];
@@ -1,4 +1,4 @@
1
- import{idFragment}from'../../native-import-utils.js';import{lightweightCoverageLosses,scanNativeDeclarations}from'../../native-region-scanner.js';import{semanticOwnershipRegionForDeclaration}from'../../semantic-import-regions.js';import{createSemanticIndexRecord,hashSemanticValue}from'@shapeshift-labs/frontier-lang-kernel';
1
+ import{idFragment}from'../../native-import-utils.js';import{lightweightDependencyRelations}from'../../lightweight-dependency-relations.js';import{lightweightCoverageLosses,scanNativeDeclarations}from'../../native-region-scanner.js';import{semanticOwnershipRegionForDeclaration}from'../../semantic-import-regions.js';import{createSemanticIndexRecord,hashSemanticValue}from'@shapeshift-labs/frontier-lang-kernel';
2
2
  export function createLightweightNativeImport(input) {
3
3
  const parser = input.parser ?? `${input.language}.lightweight-declaration-scan`;
4
4
  const rootId = 'native_root';
@@ -20,6 +20,7 @@ export function createLightweightNativeImport(input) {
20
20
  const facts = [];
21
21
  const mappings = [];
22
22
  const evidenceId = `evidence_${idFragment(input.sourcePath ?? input.language)}_lightweight_scan`;
23
+ const dependencies = lightweightDependencyRelations(input, declarations, documentId);
23
24
 
24
25
  for (const declaration of declarations) {
25
26
  const ownershipRegion = semanticOwnershipRegionForDeclaration(input, declaration, documentId);
@@ -105,6 +106,9 @@ export function createLightweightNativeImport(input) {
105
106
  }
106
107
  if (declaration.loss) losses.push(declaration.loss);
107
108
  }
109
+ occurrences.push(...dependencies.occurrences);
110
+ relations.push(...dependencies.relations);
111
+ facts.push(...dependencies.facts);
108
112
  losses.push(...lightweightCoverageLosses(input, declarations, input.sourcePreservation));
109
113
 
110
114
  const semanticIndex = createSemanticIndexRecord({
@@ -124,12 +128,13 @@ export function createLightweightNativeImport(input) {
124
128
  kind: 'import',
125
129
  status: 'passed',
126
130
  path: input.sourcePath,
127
- summary: `Lightweight declaration scan found ${symbols.length} symbol(s).`,
128
- metadata: { parser }
131
+ summary: `Lightweight declaration scan found ${symbols.length} symbol(s) and ${dependencies.summary.total} dependency edge(s).`,
132
+ metadata: { parser, dependencyRelations: dependencies.summary.total }
129
133
  }],
130
134
  metadata: {
131
135
  parser,
132
136
  coverage: 'declarations-only',
137
+ dependencyRelations: dependencies.summary,
133
138
  unsupported: ['full expression AST', 'type checking', 'control flow', 'comments and formatting preservation']
134
139
  }
135
140
  });
@@ -145,6 +150,8 @@ export function createLightweightNativeImport(input) {
145
150
  parser,
146
151
  scanKind: 'lightweight-declaration-scan',
147
152
  declarationCount: declarations.length,
153
+ dependencyRelationCount: dependencies.summary.total,
154
+ dependencyOccurrenceCount: dependencies.occurrences.length,
148
155
  ...(input.sourcePreservation ? {
149
156
  sourcePreservationId: input.sourcePreservation.id,
150
157
  sourcePreservationSummary: input.sourcePreservation.summary
@@ -1,4 +1,4 @@
1
- import{idFragment,maxSemanticMergeReadiness,uniqueRecordsById}from'../../native-import-utils.js';import{summarizeSemanticImportSidecarParadigmSemantics,summarizeSemanticImportSidecarProofSpec,summarizeSemanticImportSidecarUniversalAstLayers}from'../../semantic-import-layers.js';import{semanticPatchHintForRegion,summarizeSemanticImportRegionTaxonomy}from'../../semantic-import-regions.js';import{semanticImportSidecarEntry}from'../../semantic-import-sidecar-entry.js';import{summarizeKernelSourcePreservation}from'../../semantic-import-source-preservation.js';
1
+ import{idFragment,maxSemanticMergeReadiness,uniqueRecordsById}from'../../native-import-utils.js';import{summarizeSemanticImportDependencies}from'../../semantic-import-dependencies.js';import{summarizeSemanticImportSidecarParadigmSemantics,summarizeSemanticImportSidecarProofSpec,summarizeSemanticImportSidecarUniversalAstLayers}from'../../semantic-import-layers.js';import{semanticPatchHintForRegion,summarizeSemanticImportRegionTaxonomy}from'../../semantic-import-regions.js';import{semanticImportSidecarEntry}from'../../semantic-import-sidecar-entry.js';import{summarizeKernelSourcePreservation}from'../../semantic-import-source-preservation.js';
2
2
  import{createSemanticImportSidecarAdmission,createSemanticImportSidecarQuality}from'./createSemanticImportSidecarAdmission.js';
3
3
  import{summarizeNativeImportLosses}from'./summarizeNativeImportLosses.js';
4
4
  export function createSemanticImportSidecar(importResult, options = {}) {
@@ -17,6 +17,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
17
17
  const universalAstLayers = summarizeSemanticImportSidecarUniversalAstLayers(importEntries);
18
18
  const proofSpec = summarizeSemanticImportSidecarProofSpec(importEntries);
19
19
  const paradigmSemantics = summarizeSemanticImportSidecarParadigmSemantics(importEntries);
20
+ const dependencies = summarizeSemanticImportDependencies(imports);
20
21
  const readiness = mergeCandidates.reduce(
21
22
  (current, candidate) => maxSemanticMergeReadiness(current, candidate.readiness),
22
23
  lossSummary.semanticMergeReadiness
@@ -51,6 +52,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
51
52
  universalAstLayers,
52
53
  proofSpec,
53
54
  paradigmSemantics,
55
+ dependencies,
54
56
  patchHints,
55
57
  quality,
56
58
  admission,
@@ -92,6 +94,8 @@ export function createSemanticImportSidecar(importResult, options = {}) {
92
94
  paradigmSemanticsRecords: paradigmSemantics.total,
93
95
  paradigmSemanticsGroups: paradigmSemantics.groups.length,
94
96
  paradigmSemanticsLoweringRecords: paradigmSemantics.loweringRecords,
97
+ dependencyRelations: dependencies.total,
98
+ dependencyPredicates: dependencies.predicates,
95
99
  patchHints: patchHints.length,
96
100
  evidenceWarnings: quality.emptyEvidenceWarnings.length,
97
101
  readiness,
@@ -0,0 +1,271 @@
1
+ import { idFragment } from './native-import-utils.js';
2
+ import { sourceLines } from './native-region-scanner-core.js';
3
+
4
+ const jsLikeLanguages = new Set(['javascript', 'typescript']);
5
+ const ignoredIdentifiers = new Set([
6
+ 'async', 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'default',
7
+ 'delete', 'do', 'else', 'export', 'extends', 'false', 'finally', 'for', 'from',
8
+ 'function', 'if', 'import', 'in', 'instanceof', 'let', 'new', 'null', 'of',
9
+ 'return', 'static', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof',
10
+ 'undefined', 'var', 'void', 'while', 'with', 'yield'
11
+ ]);
12
+
13
+ export function lightweightDependencyRelations(input, declarations, documentId) {
14
+ if (!jsLikeLanguages.has(String(input.language ?? '').toLowerCase())) {
15
+ return { relations: [], occurrences: [], facts: [], summary: emptySummary() };
16
+ }
17
+ const lines = sourceLines(input.sourceText);
18
+ const identifiers = declarationIdentifierMap(input, declarations, lines);
19
+ const records = { relations: [], occurrences: [], facts: [], seen: new Set() };
20
+ for (const scan of declarationScanRanges(declarations, lines)) {
21
+ scanDeclarationDependencies(input, documentId, scan, identifiers, lines, records);
22
+ }
23
+ return {
24
+ relations: records.relations,
25
+ occurrences: records.occurrences,
26
+ facts: records.facts,
27
+ summary: {
28
+ total: records.relations.length,
29
+ calls: records.relations.filter((relation) => relation.predicate === 'calls').length,
30
+ uses: records.relations.filter((relation) => relation.predicate === 'uses').length
31
+ }
32
+ };
33
+ }
34
+
35
+ function declarationIdentifierMap(input, declarations, lines) {
36
+ const identifiers = new Map();
37
+ for (const declaration of declarations ?? []) {
38
+ if (!declaration?.symbolId) continue;
39
+ const target = {
40
+ name: declaration.name,
41
+ symbolId: declaration.symbolId,
42
+ symbolKind: declaration.symbolKind,
43
+ nodeId: declaration.nodeId,
44
+ role: declaration.role
45
+ };
46
+ for (const identifier of identifiersForDeclaration(input, declaration, lines)) {
47
+ addIdentifierTarget(identifiers, identifier, target);
48
+ }
49
+ }
50
+ return identifiers;
51
+ }
52
+
53
+ function identifiersForDeclaration(input, declaration, lines) {
54
+ const identifiers = new Set();
55
+ addIdentifier(identifiers, declaration.name);
56
+ const parts = String(declaration.name ?? '').split('.');
57
+ addIdentifier(identifiers, parts[parts.length - 1]);
58
+ if (declaration.role === 'import') {
59
+ const line = lines[(declaration.span?.startLine ?? 1) - 1]?.line ?? '';
60
+ for (const identifier of importLocalIdentifiers(line)) addIdentifier(identifiers, identifier);
61
+ addIdentifier(identifiers, packageIdentifier(declaration.importPath ?? declaration.name));
62
+ }
63
+ return [...identifiers];
64
+ }
65
+
66
+ function importLocalIdentifiers(line) {
67
+ const source = String(line ?? '');
68
+ const identifiers = [];
69
+ let match = source.match(/^import\s+([A-Za-z_$][\w$]*)\s+from\b/);
70
+ if (match) identifiers.push(match[1]);
71
+ match = source.match(/^import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\b/);
72
+ if (match) identifiers.push(match[1]);
73
+ match = source.match(/^import\s+[^,{]+,\s*\{([^}]+)\}\s+from\b/);
74
+ if (match) identifiers.push(...namedImportIdentifiers(match[1]));
75
+ match = source.match(/^import\s+\{([^}]+)\}\s+from\b/);
76
+ if (match) identifiers.push(...namedImportIdentifiers(match[1]));
77
+ return identifiers;
78
+ }
79
+
80
+ function namedImportIdentifiers(raw) {
81
+ return String(raw ?? '')
82
+ .split(',')
83
+ .map((part) => part.trim().split(/\s+as\s+/i).pop()?.trim())
84
+ .filter(Boolean);
85
+ }
86
+
87
+ function packageIdentifier(value) {
88
+ const parts = String(value ?? '').split('/').filter(Boolean);
89
+ const last = parts[parts.length - 1] ?? value;
90
+ return String(last).replace(/[^A-Za-z0-9_$]/g, '');
91
+ }
92
+
93
+ function addIdentifierTarget(map, identifier, target) {
94
+ if (!isIdentifier(identifier)) return;
95
+ const existing = map.get(identifier) ?? [];
96
+ if (!existing.some((entry) => entry.symbolId === target.symbolId)) existing.push(target);
97
+ map.set(identifier, existing);
98
+ }
99
+
100
+ function addIdentifier(set, value) {
101
+ if (isIdentifier(value)) set.add(value);
102
+ }
103
+
104
+ function isIdentifier(value) {
105
+ const text = String(value ?? '');
106
+ return /^[A-Za-z_$][\w$]*$/.test(text) && !ignoredIdentifiers.has(text);
107
+ }
108
+
109
+ function declarationScanRanges(declarations, lines) {
110
+ return (declarations ?? [])
111
+ .filter((declaration) => declaration?.symbolId && declaration.role !== 'import')
112
+ .map((declaration) => ({
113
+ declaration,
114
+ startLine: declaration.span?.startLine ?? 1,
115
+ endLine: declarationScanEndLine(declaration, lines)
116
+ }));
117
+ }
118
+
119
+ function declarationScanEndLine(declaration, lines) {
120
+ const startLine = declaration.span?.startLine ?? 1;
121
+ if (!declaration.metadata?.hasBody || declaration.kind === 'ClassDeclaration') return startLine;
122
+ return balancedRegionEndLine(lines, startLine);
123
+ }
124
+
125
+ function balancedRegionEndLine(lines, startLine) {
126
+ const state = { inBlockComment: false, inTemplateString: false };
127
+ let depth = 0;
128
+ let opened = false;
129
+ for (let index = Math.max(0, startLine - 1); index < lines.length; index += 1) {
130
+ for (const char of maskReferenceLine(lines[index].line, state)) {
131
+ if (char === '{' || char === '[' || char === '(') {
132
+ depth += 1;
133
+ opened = true;
134
+ } else if (char === '}' || char === ']' || char === ')') {
135
+ depth -= 1;
136
+ }
137
+ }
138
+ if (opened && depth <= 0) return lines[index].number;
139
+ }
140
+ return startLine;
141
+ }
142
+
143
+ function scanDeclarationDependencies(input, documentId, scan, identifiers, lines, records) {
144
+ const state = { inBlockComment: false, inTemplateString: false };
145
+ for (let lineNumber = scan.startLine; lineNumber <= scan.endLine; lineNumber += 1) {
146
+ const scanLine = maskReferenceLine(lines[lineNumber - 1]?.line ?? '', state);
147
+ for (const match of scanLine.matchAll(/[A-Za-z_$][\w$]*/g)) {
148
+ const name = match[0];
149
+ if (ignoredIdentifiers.has(name) || !identifiers.has(name)) continue;
150
+ const targets = identifiers.get(name).filter((target) => target.symbolId !== scan.declaration.symbolId);
151
+ for (const target of targets) {
152
+ addDependencyRecord(input, documentId, scan.declaration, target, {
153
+ line: scanLine,
154
+ lineNumber,
155
+ startColumn: match.index + 1,
156
+ name
157
+ }, records);
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ function addDependencyRecord(input, documentId, caller, target, occurrence, records) {
164
+ const predicate = isCallReference(occurrence.line, occurrence.startColumn + occurrence.name.length - 1) ? 'calls' : 'uses';
165
+ const key = `${caller.symbolId}|${predicate}|${target.symbolId}|${occurrence.lineNumber}|${occurrence.startColumn}`;
166
+ if (records.seen.has(key)) return;
167
+ records.seen.add(key);
168
+ const relationId = `rel_${idFragment(caller.symbolId)}_${predicate}_${idFragment(target.symbolId)}_${occurrence.lineNumber}_${occurrence.startColumn}`;
169
+ const occurrenceId = `occ_${idFragment(caller.nodeId)}_${predicate}_${idFragment(target.symbolId)}_${occurrence.lineNumber}_${occurrence.startColumn}`;
170
+ const span = {
171
+ sourceId: input.sourceHash,
172
+ path: input.sourcePath,
173
+ startLine: occurrence.lineNumber,
174
+ endLine: occurrence.lineNumber,
175
+ startColumn: occurrence.startColumn,
176
+ endColumn: occurrence.startColumn + occurrence.name.length
177
+ };
178
+ records.relations.push({
179
+ id: relationId,
180
+ sourceId: caller.symbolId,
181
+ predicate,
182
+ targetId: target.symbolId,
183
+ metadata: {
184
+ scan: 'lightweight-dependency',
185
+ confidence: 'lexical-reference',
186
+ sourceDocumentId: documentId,
187
+ sourceName: caller.name,
188
+ targetName: target.name
189
+ }
190
+ });
191
+ records.occurrences.push({
192
+ id: occurrenceId,
193
+ documentId,
194
+ symbolId: target.symbolId,
195
+ role: 'reference',
196
+ span,
197
+ nativeAstNodeId: caller.nodeId
198
+ });
199
+ records.facts.push({
200
+ id: `fact_${idFragment(relationId)}_lightweight_dependency`,
201
+ predicate: 'lightweightDependency',
202
+ subjectId: caller.symbolId,
203
+ value: {
204
+ relationId,
205
+ predicate,
206
+ targetId: target.symbolId,
207
+ line: occurrence.lineNumber,
208
+ confidence: 'lexical-reference'
209
+ }
210
+ });
211
+ }
212
+
213
+ function isCallReference(line, afterIdentifierIndex) {
214
+ return /^\s*\(/.test(String(line ?? '').slice(afterIdentifierIndex));
215
+ }
216
+
217
+ function maskReferenceLine(line, state) {
218
+ const text = String(line ?? '');
219
+ let output = '';
220
+ let quote;
221
+ let escaped = false;
222
+ for (let index = 0; index < text.length; index += 1) {
223
+ const char = text[index];
224
+ const next = text[index + 1];
225
+ if (state.inBlockComment) {
226
+ output += ' ';
227
+ if (char === '*' && next === '/') {
228
+ output += ' ';
229
+ index += 1;
230
+ state.inBlockComment = false;
231
+ }
232
+ continue;
233
+ }
234
+ if (state.inTemplateString) {
235
+ output += ' ';
236
+ if (char === '`' && !escaped) state.inTemplateString = false;
237
+ escaped = char === '\\' && !escaped;
238
+ continue;
239
+ }
240
+ if (quote) {
241
+ output += ' ';
242
+ if (escaped) escaped = false;
243
+ else if (char === '\\') escaped = true;
244
+ else if (char === quote) quote = undefined;
245
+ continue;
246
+ }
247
+ if (char === '/' && next === '/') break;
248
+ if (char === '/' && next === '*') {
249
+ output += ' ';
250
+ index += 1;
251
+ state.inBlockComment = true;
252
+ continue;
253
+ }
254
+ if (char === '\'' || char === '"') {
255
+ output += ' ';
256
+ quote = char;
257
+ continue;
258
+ }
259
+ if (char === '`') {
260
+ output += ' ';
261
+ state.inTemplateString = true;
262
+ continue;
263
+ }
264
+ output += char;
265
+ }
266
+ return output;
267
+ }
268
+
269
+ function emptySummary() {
270
+ return { total: 0, calls: 0, uses: 0 };
271
+ }
@@ -0,0 +1,62 @@
1
+ import { countBy, uniqueRecordsById, uniqueStrings } from './native-import-utils.js';
2
+
3
+ function summarizeSemanticImportDependencies(imports) {
4
+ return summarizeSemanticImportDependencyRelations((imports ?? [])
5
+ .flatMap((imported) => imported?.semanticIndex?.relations ?? imported?.universalAst?.semanticIndex?.relations ?? []));
6
+ }
7
+
8
+ function summarizeSemanticImportDependencyRelations(relations) {
9
+ const dependencyRelations = uniqueRecordsById((relations ?? []).filter(isDependencyRelation));
10
+ const predicateKeys = dependencyRelations.map((relation) => semanticDependencyPredicateKey(relation.predicate));
11
+ const byPredicate = countBy(predicateKeys);
12
+ return {
13
+ total: dependencyRelations.length,
14
+ calls: byPredicate.calls ?? 0,
15
+ uses: byPredicate.uses ?? 0,
16
+ references: byPredicate.references ?? 0,
17
+ imports: byPredicate.imports ?? 0,
18
+ extends: byPredicate.extends ?? 0,
19
+ implements: byPredicate.implements ?? 0,
20
+ includes: byPredicate.includes ?? 0,
21
+ requires: byPredicate.requires ?? 0,
22
+ byPredicate,
23
+ predicates: uniqueStrings(predicateKeys),
24
+ ids: dependencyRelations.map((relation) => relation.id).filter(Boolean),
25
+ sourceSymbolIds: uniqueStrings(dependencyRelations.map((relation) => relation.sourceId).filter(Boolean)),
26
+ targetSymbolIds: uniqueStrings(dependencyRelations.map((relation) => relation.targetId).filter(Boolean))
27
+ };
28
+ }
29
+
30
+ function isDependencyRelation(relation) {
31
+ const predicate = String(relation?.predicate ?? '').toLowerCase();
32
+ if (!predicate || predicate === 'defines' || predicate === 'definitionof') return false;
33
+ return predicate === 'imports'
34
+ || predicate === 'calls'
35
+ || predicate === 'uses'
36
+ || predicate.includes('reference')
37
+ || predicate.includes('depend')
38
+ || predicate.includes('require')
39
+ || predicate.includes('include')
40
+ || predicate.includes('extend')
41
+ || predicate.includes('implement');
42
+ }
43
+
44
+ function semanticDependencyPredicateKey(predicate) {
45
+ const value = String(predicate ?? 'unknown').toLowerCase();
46
+ if (value.includes('call')) return 'calls';
47
+ if (value.includes('reference')) return 'references';
48
+ if (value.includes('import')) return 'imports';
49
+ if (value.includes('depend')) return 'depends';
50
+ if (value.includes('require')) return 'requires';
51
+ if (value.includes('include')) return 'includes';
52
+ if (value.includes('extend')) return 'extends';
53
+ if (value.includes('implement')) return 'implements';
54
+ if (value === 'uses') return 'uses';
55
+ return value || 'unknown';
56
+ }
57
+
58
+ export {
59
+ semanticDependencyPredicateKey,
60
+ summarizeSemanticImportDependencies,
61
+ summarizeSemanticImportDependencyRelations
62
+ };
@@ -1,4 +1,5 @@
1
1
  import { uniqueRecordsById, uniqueStrings } from './native-import-utils.js';
2
+ import { summarizeSemanticImportDependencyRelations } from './semantic-import-dependencies.js';
2
3
  import { semanticOwnershipRegionForSymbol, summarizeSemanticImportRegionTaxonomy } from './semantic-import-regions.js';
3
4
  import { collectKernelSourcePreservationFromImport } from './semantic-import-source-preservation.js';
4
5
  import {
@@ -16,6 +17,7 @@ function semanticImportSidecarEntry(imported, index, options) {
16
17
  const universalAstLayers = summarizeUniversalAstLayers(imported?.universalAst);
17
18
  const proofSpec = summarizeProofSpecLayer(imported?.universalAst?.proof ?? imported?.proof);
18
19
  const paradigmSemantics = summarizeParadigmSemanticsLayer(imported?.universalAst?.paradigmSemantics ?? imported?.paradigmSemantics);
20
+ const dependencies = summarizeSemanticImportDependencyRelations(semanticIndex?.relations ?? []);
19
21
  const mappingsBySymbolId = new Map();
20
22
  for (const mapping of sourceMapMappings) {
21
23
  if (mapping.semanticSymbolId && !mappingsBySymbolId.has(mapping.semanticSymbolId)) {
@@ -67,6 +69,8 @@ function semanticImportSidecarEntry(imported, index, options) {
67
69
  universalAstLayerIds: universalAstLayers.ids,
68
70
  proofSpec,
69
71
  paradigmSemantics,
72
+ dependencyRelationCount: dependencies.total,
73
+ dependencyPredicates: dependencies.predicates,
70
74
  readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness ?? 'needs-review',
71
75
  emptySemanticIndex: symbols.length === 0,
72
76
  regionTaxonomy,
@@ -90,6 +90,8 @@ export interface SemanticImportSidecarImportEntry {
90
90
  readonly universalAstLayerIds: readonly string[];
91
91
  readonly proofSpec: SemanticImportSidecarProofSpecSummary;
92
92
  readonly paradigmSemantics: SemanticImportSidecarParadigmSemanticsSummary;
93
+ readonly dependencyRelationCount: number;
94
+ readonly dependencyPredicates: readonly string[];
93
95
  readonly readiness: SemanticMergeReadiness;
94
96
  readonly emptySemanticIndex: boolean;
95
97
  readonly regionTaxonomy?: SemanticImportRegionTaxonomySummary;
@@ -186,6 +188,23 @@ export interface SemanticImportSidecarParadigmSemanticsSummary {
186
188
  readonly empty: boolean;
187
189
  }
188
190
 
191
+ export interface SemanticImportDependencySummary {
192
+ readonly total: number;
193
+ readonly calls: number;
194
+ readonly uses: number;
195
+ readonly references: number;
196
+ readonly imports: number;
197
+ readonly extends: number;
198
+ readonly implements: number;
199
+ readonly includes: number;
200
+ readonly requires: number;
201
+ readonly byPredicate: Readonly<Record<string, number>>;
202
+ readonly predicates: readonly string[];
203
+ readonly ids: readonly string[];
204
+ readonly sourceSymbolIds: readonly string[];
205
+ readonly targetSymbolIds: readonly string[];
206
+ }
207
+
189
208
  export interface SemanticImportSidecar {
190
209
  readonly kind: 'frontier.lang.semanticImportSidecar';
191
210
  readonly version: 1;
@@ -217,6 +236,7 @@ export interface SemanticImportSidecar {
217
236
  readonly universalAstLayers: SemanticImportSidecarUniversalAstLayerSummary;
218
237
  readonly proofSpec: SemanticImportSidecarProofSpecSummary;
219
238
  readonly paradigmSemantics: SemanticImportSidecarParadigmSemanticsSummary;
239
+ readonly dependencies: SemanticImportDependencySummary;
220
240
  readonly patchHints: readonly SemanticImportPatchHint[];
221
241
  readonly quality: SemanticImportSidecarQuality;
222
242
  readonly admission: SemanticImportSidecarAdmission;
@@ -258,6 +278,8 @@ export interface SemanticImportSidecar {
258
278
  readonly paradigmSemanticsRecords: number;
259
279
  readonly paradigmSemanticsGroups: number;
260
280
  readonly paradigmSemanticsLoweringRecords: number;
281
+ readonly dependencyRelations: number;
282
+ readonly dependencyPredicates: readonly string[];
261
283
  readonly patchHints: number;
262
284
  readonly evidenceWarnings: number;
263
285
  readonly readiness: SemanticMergeReadiness;
@@ -0,0 +1,122 @@
1
+ import { countBy, idFragment, uniqueStrings } from './native-import-utils.js';
2
+
3
+ export function createUniversalConversionAdmissionRecord(input) {
4
+ const route = input.route;
5
+ const operations = input.semanticOperations?.operations ?? [];
6
+ const operationKinds = uniqueStrings(operations.map((operation) => operation.operationKind));
7
+ const sourceRecords = input.patchBundle?.sources ?? input.history?.sources ?? [];
8
+ const evidenceIds = uniqueStrings([
9
+ ...(input.history?.evidenceIds ?? []),
10
+ ...(input.patchBundle?.evidenceIds ?? []),
11
+ ...(input.materialization?.evidenceIds ?? [])
12
+ ]);
13
+ const proofIds = uniqueStrings([
14
+ ...(input.history?.proofIds ?? []),
15
+ ...(input.patchBundle?.proofIds ?? []),
16
+ ...(input.materialization?.proofIds ?? [])
17
+ ]);
18
+ const score = route.mergeScore ?? {};
19
+ const missingEvidence = route.missingEvidence ?? [];
20
+ const blockers = route.blockers ?? [];
21
+ const bucket = admissionBucket({
22
+ action: score.action ?? route.admissionAction,
23
+ blockers,
24
+ missingEvidence,
25
+ mode: route.mode,
26
+ operationKinds,
27
+ opaqueOperations: operations.filter((operation) => operation.opaque).length,
28
+ scoreValue: score.value,
29
+ status: input.admissionStatus
30
+ });
31
+ return {
32
+ kind: 'frontier.lang.universalConversionAdmissionRecord',
33
+ version: 1,
34
+ schema: 'frontier.lang.universalConversionAdmissionRecord.v1',
35
+ id: `admission_${idFragment(route.id)}`,
36
+ routeId: route.id,
37
+ planId: input.planId,
38
+ sourceLanguage: route.sourceLanguage,
39
+ target: route.target,
40
+ mode: route.mode,
41
+ routeAction: route.routeAction,
42
+ admissionStatus: input.admissionStatus,
43
+ admissionAction: score.action ?? route.admissionAction,
44
+ admissionBucket: bucket,
45
+ reviewRequired: true,
46
+ readiness: route.readiness,
47
+ risk: score.risk ?? riskForRoute(route, input.admissionStatus),
48
+ score: {
49
+ value: Number(score.value ?? 0),
50
+ uncappedValue: Number(score.uncappedValue ?? score.value ?? 0),
51
+ sortKey: Number(score.sortKey ?? score.value ?? 0),
52
+ higherIsBetter: true,
53
+ componentStatuses: componentStatuses(score.components),
54
+ penalties: score.penalties ?? []
55
+ },
56
+ ids: {
57
+ historyId: input.history?.id,
58
+ patchBundleId: input.patchBundle?.id,
59
+ semanticOperationIds: operations.map((operation) => operation.id),
60
+ sourceMapLinkIds: input.materialization?.sourceMapLinkIds ?? [],
61
+ evidenceIds,
62
+ proofIds
63
+ },
64
+ semanticOperations: {
65
+ total: operations.length,
66
+ byKind: countBy(operations.map((operation) => operation.operationKind)),
67
+ kinds: operationKinds,
68
+ dynamic: operations.filter((operation) => operation.dynamic).map((operation) => operation.id),
69
+ opaque: operations.filter((operation) => operation.opaque).map((operation) => operation.id)
70
+ },
71
+ ownership: {
72
+ keys: uniqueStrings(input.history?.index?.ownershipKeys ?? input.patchBundle?.index?.ownershipKeys ?? []),
73
+ conflictKeys: uniqueStrings(input.history?.index?.conflictKeys ?? input.patchBundle?.index?.conflictKeys ?? [])
74
+ },
75
+ source: {
76
+ paths: uniqueStrings(sourceRecords.map((source) => source.sourcePath)),
77
+ hashes: uniqueStrings(sourceRecords.map((source) => source.sourceHash)),
78
+ baseHashes: uniqueStrings(sourceRecords.map((source) => source.baseHash)),
79
+ targetHashes: uniqueStrings(sourceRecords.map((source) => source.targetHash))
80
+ },
81
+ evidence: {
82
+ total: evidenceIds.length,
83
+ proofArtifacts: proofIds.length,
84
+ missing: missingEvidence,
85
+ blockers,
86
+ review: route.review ?? []
87
+ },
88
+ reasons: uniqueStrings([
89
+ ...(input.reasonCodes ?? []),
90
+ ...missingEvidence.map((reason) => `missing:${reason}`),
91
+ ...blockers.map((reason) => `blocker:${reason}`),
92
+ ...(route.review ?? []).map((reason) => `review:${reason}`),
93
+ ...(score.penalties ?? [])
94
+ ]),
95
+ autoMergeClaim: false,
96
+ semanticEquivalenceClaim: false,
97
+ metadata: {
98
+ routeId: route.id,
99
+ planId: input.planId,
100
+ mergeScoreSchema: score.schema,
101
+ note: 'Admission records are sortable merge-review evidence, not proof of target semantic equivalence.'
102
+ }
103
+ };
104
+ }
105
+
106
+ function admissionBucket(input) {
107
+ if (input.status === 'blocked' || input.action === 'reject' || input.blockers.length) return 'blocked';
108
+ if (input.mode === 'semantic-index-only' || input.missingEvidence.includes('target-adapter')) return 'needs-adapter';
109
+ if (input.missingEvidence.length || input.opaqueOperations || input.operationKinds.includes('merge')) return 'needs-evidence';
110
+ if (Number(input.scoreValue ?? 0) >= 80) return 'merge-ready';
111
+ return 'needs-review';
112
+ }
113
+
114
+ function riskForRoute(route, status) {
115
+ if (status === 'blocked' || route.readiness === 'blocked') return 'high';
116
+ if (route.readiness === 'needs-review' || route.mode === 'stub-only') return 'medium';
117
+ return 'low';
118
+ }
119
+
120
+ function componentStatuses(components = {}) {
121
+ return Object.fromEntries(Object.entries(components).map(([key, component]) => [key, component.status]));
122
+ }
@@ -12,11 +12,14 @@ export function artifactIndex(routeArtifacts) {
12
12
  routeIds: uniqueStrings(routeArtifacts.map((artifact) => artifact.routeId)),
13
13
  historyIds: uniqueStrings(routeArtifacts.map((artifact) => artifact.history.id)),
14
14
  patchBundleIds: uniqueStrings(routeArtifacts.map((artifact) => artifact.patchBundle.id)),
15
+ admissionRecordIds: uniqueStrings(routeArtifacts.map((artifact) => artifact.admissionRecord.id)),
15
16
  languages: uniqueStrings(routeArtifacts.map((artifact) => artifact.sourceLanguage)),
16
17
  targets: uniqueStrings(routeArtifacts.map((artifact) => artifact.target)),
17
18
  modes: uniqueStrings(routeArtifacts.map((artifact) => artifact.mode)),
18
19
  readinesses: uniqueStrings(routeArtifacts.map((artifact) => artifact.readiness)),
19
20
  admissionStatuses: uniqueStrings(routeArtifacts.map((artifact) => artifact.admissionStatus)),
21
+ admissionBuckets: uniqueStrings(routeArtifacts.map((artifact) => artifact.admissionRecord.admissionBucket)),
22
+ admissionRisks: uniqueStrings(routeArtifacts.map((artifact) => artifact.admissionRecord.risk)),
20
23
  sourcePaths: uniqueStrings(routeArtifacts.flatMap((artifact) => artifact.history.index.sourcePaths)),
21
24
  sourceHashes: uniqueStrings(routeArtifacts.flatMap((artifact) => artifact.history.index.sourceHashes)),
22
25
  ownershipKeys: uniqueStrings(routeArtifacts.flatMap((artifact) => artifact.history.index.ownershipKeys)),
@@ -39,6 +42,7 @@ function matchesArtifact(record, query) {
39
42
  return match(query.routeId, [record.routeId])
40
43
  && match(query.historyId, [record.history.id])
41
44
  && match(query.patchBundleId, [record.patchBundle.id])
45
+ && match(query.admissionRecordId, [record.admissionRecord.id])
42
46
  && match(query.sourceLanguage, [record.sourceLanguage])
43
47
  && match(query.target, [record.target])
44
48
  && match(query.mode, [record.mode])
@@ -46,6 +50,8 @@ function matchesArtifact(record, query) {
46
50
  && match(query.priority, [record.priority])
47
51
  && match(query.readiness, [record.readiness])
48
52
  && match(query.admissionStatus, [record.admissionStatus])
53
+ && match(query.admissionBucket, [record.admissionRecord.admissionBucket])
54
+ && match(query.risk, [record.admissionRecord.risk])
49
55
  && match(query.sourcePath, record.history.index.sourcePaths)
50
56
  && match(query.sourceHash, record.history.index.sourceHashes)
51
57
  && match(query.ownershipKey, record.history.index.ownershipKeys)
@@ -0,0 +1,37 @@
1
+ export function universalConversionArtifactSummary(routeArtifacts, records) {
2
+ const admissionRecords = records.admissionRecords;
3
+ return {
4
+ routes: routeArtifacts.length,
5
+ histories: records.historyRecords.length,
6
+ patchBundles: records.patchBundleRecords.length,
7
+ admissionRecords: admissionRecords.length,
8
+ semanticOperations: routeArtifacts.reduce((sum, artifact) => sum + artifact.semanticOperations.operations.length, 0),
9
+ mergeReady: countAdmissionBucket(admissionRecords, 'merge-ready'),
10
+ needsEvidence: countAdmissionBucket(admissionRecords, 'needs-evidence'),
11
+ needsAdapter: countAdmissionBucket(admissionRecords, 'needs-adapter'),
12
+ needsReview: countAdmissionBucket(admissionRecords, 'needs-review'),
13
+ admissionBlocked: countAdmissionBucket(admissionRecords, 'blocked'),
14
+ lowRisk: countAdmissionRisk(admissionRecords, 'low'),
15
+ mediumRisk: countAdmissionRisk(admissionRecords, 'medium'),
16
+ highRisk: countAdmissionRisk(admissionRecords, 'high'),
17
+ queued: routeArtifacts.filter((artifact) => artifact.admissionStatus === 'queued').length,
18
+ reviewRequired: routeArtifacts.filter((artifact) => artifact.reviewRequired).length,
19
+ blocked: routeArtifacts.filter((artifact) => artifact.admissionStatus === 'blocked').length,
20
+ reasonCodes: admissionRecords.reduce((sum, record) => sum + record.reasons.length, 0),
21
+ missingEvidence: admissionRecords.reduce((sum, record) => sum + record.evidence.missing.length, 0),
22
+ blockers: admissionRecords.reduce((sum, record) => sum + record.evidence.blockers.length, 0),
23
+ reviewReasons: admissionRecords.reduce((sum, record) => sum + record.evidence.review.length, 0),
24
+ evidenceIds: admissionRecords.reduce((sum, record) => sum + record.ids.evidenceIds.length, 0),
25
+ proofIds: admissionRecords.reduce((sum, record) => sum + record.ids.proofIds.length, 0),
26
+ autoMergeClaims: 0,
27
+ semanticEquivalenceClaims: 0
28
+ };
29
+ }
30
+
31
+ function countAdmissionBucket(records, bucket) {
32
+ return records.filter((record) => record.admissionBucket === bucket).length;
33
+ }
34
+
35
+ function countAdmissionRisk(records, risk) {
36
+ return records.filter((record) => record.risk === risk).length;
37
+ }
@@ -1,6 +1,8 @@
1
1
  import { idFragment, uniqueStrings } from './native-import-utils.js';
2
+ import { universalConversionArtifactSummary } from './universal-conversion-artifact-summary.js';
2
3
  import { createUniversalConversionPlan } from './universal-conversion-plan.js';
3
4
  import { artifactIndex } from './universal-conversion-artifact-query.js';
5
+ import { createUniversalConversionAdmissionRecord } from './universal-conversion-admission-record.js';
4
6
  import { routeSemanticOperations } from './universal-conversion-route-operations.js';
5
7
  import { createSemanticHistoryRecord } from './internal/index-impl/semanticHistoryRecords.js';
6
8
  import { createSemanticPatchBundleRecord } from './internal/index-impl/semanticPatchBundleRecords.js';
@@ -23,6 +25,7 @@ export function createUniversalConversionArtifacts(input = {}, options = {}) {
23
25
  }));
24
26
  const historyRecords = routeArtifacts.map((artifact) => artifact.history);
25
27
  const patchBundleRecords = routeArtifacts.map((artifact) => artifact.patchBundle);
28
+ const admissionRecords = routeArtifacts.map((artifact) => artifact.admissionRecord);
26
29
  const index = artifactIndex(routeArtifacts);
27
30
  return {
28
31
  kind: 'frontier.lang.universalConversionArtifacts',
@@ -34,17 +37,13 @@ export function createUniversalConversionArtifacts(input = {}, options = {}) {
34
37
  routeArtifacts,
35
38
  historyRecords,
36
39
  patchBundleRecords,
40
+ admissionRecords,
37
41
  index,
38
- summary: {
39
- routes: routeArtifacts.length,
40
- histories: historyRecords.length,
41
- patchBundles: patchBundleRecords.length,
42
- semanticOperations: routeArtifacts.reduce((sum, artifact) => sum + artifact.semanticOperations.operations.length, 0),
43
- reviewRequired: routeArtifacts.filter((artifact) => artifact.reviewRequired).length,
44
- blocked: routeArtifacts.filter((artifact) => artifact.admissionStatus === 'blocked').length,
45
- autoMergeClaims: 0,
46
- semanticEquivalenceClaims: 0
47
- },
42
+ summary: universalConversionArtifactSummary(routeArtifacts, {
43
+ historyRecords,
44
+ patchBundleRecords,
45
+ admissionRecords
46
+ }),
48
47
  metadata: {
49
48
  autoMergeClaim: false,
50
49
  semanticEquivalenceClaim: false,
@@ -121,6 +120,16 @@ function createRouteArtifact(route, options) {
121
120
  autoMergeClaim: false,
122
121
  semanticEquivalenceClaim: false
123
122
  };
123
+ const admissionRecord = createUniversalConversionAdmissionRecord({
124
+ route,
125
+ planId,
126
+ admissionStatus,
127
+ reasonCodes,
128
+ history,
129
+ patchBundle,
130
+ semanticOperations,
131
+ materialization
132
+ });
124
133
  return {
125
134
  kind: 'frontier.lang.universalConversionRouteArtifact',
126
135
  version: 1,
@@ -138,9 +147,11 @@ function createRouteArtifact(route, options) {
138
147
  reviewRequired: true,
139
148
  history,
140
149
  patchBundle,
150
+ admissionRecord,
141
151
  semanticOperations,
142
152
  materialization,
143
153
  mergeScore: route.mergeScore,
154
+ admissionBucket: admissionRecord.admissionBucket,
144
155
  autoMergeClaim: false,
145
156
  semanticEquivalenceClaim: false,
146
157
  metadata: { ...routeRecordMetadata(route, planId, options.metadata), materialization }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.52",
3
+ "version": "0.2.54",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",