@shapeshift-labs/frontier-lang-compiler 0.2.59 → 0.2.61

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
@@ -272,6 +272,7 @@ const sidecar = createSemanticImportSidecar(imported);
272
272
  console.log(sidecar.summary.emptySemanticIndex); // false when symbols were found
273
273
  console.log(sidecar.ownershipRegions[0].key); // source#src/runtime.ts#type#Runtime
274
274
  console.log(sidecar.patchHints[0].supportedOperations); // source-region patch operations
275
+ console.log(sidecar.semanticImpact.records[0].verificationPlan); // dependency/source-map/proof checks for a region
275
276
  console.log(sidecar.proofSpec.obligations); // proof/spec obligations when the import carries a universal AST proof layer
276
277
  console.log(sidecar.paradigmSemantics.hasLowering); // true when source import preserved lowering/paradigm records
277
278
  ```
@@ -0,0 +1,55 @@
1
+ import type { SemanticMergeReadiness, SourceSpan } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import type { NativeImportRegionTaxonomyKind } from './native-import-losses.js';
3
+
4
+ export type SemanticImportImpactRisk = 'low' | 'medium' | 'high' | string;
5
+ export type SemanticImportImpactConfidence = 'source-exact' | 'source-addressed' | 'estimated-source-region' | 'review-required' | string;
6
+
7
+ export interface SemanticImportImpactVerificationStep {
8
+ readonly kind: 'dependency-review' | 'source-map-review' | 'reject-or-reprove' | 'proof-review' | 'patch-admission' | 'evidence-review' | string;
9
+ readonly reason: string;
10
+ readonly required: boolean;
11
+ }
12
+
13
+ export interface SemanticImportImpactRecord {
14
+ readonly id: string;
15
+ readonly kind: 'ownership-region-impact' | string;
16
+ readonly ownershipRegionId: string;
17
+ readonly ownershipKey: string;
18
+ readonly regionKind?: NativeImportRegionTaxonomyKind;
19
+ readonly sourcePath?: string;
20
+ readonly sourceHash?: string;
21
+ readonly sourceSpan?: SourceSpan;
22
+ readonly symbolIds: readonly string[];
23
+ readonly symbolNames: readonly string[];
24
+ readonly dependencyRelationIds: readonly string[];
25
+ readonly dependencyPredicates: readonly string[];
26
+ readonly affectedSymbolIds: readonly string[];
27
+ readonly sourceMapMappingIds: readonly string[];
28
+ readonly sourcePreservationRecordIds: readonly string[];
29
+ readonly patchHintIds: readonly string[];
30
+ readonly proofSpecIds: readonly string[];
31
+ readonly proofObligationIds: readonly string[];
32
+ readonly failedProofObligationIds: readonly string[];
33
+ readonly openProofObligationIds: readonly string[];
34
+ readonly paradigmSemanticIds: readonly string[];
35
+ readonly loweringRecordIds: readonly string[];
36
+ readonly evidenceIds: readonly string[];
37
+ readonly verificationPlan: readonly SemanticImportImpactVerificationStep[];
38
+ readonly conflictKeys: readonly string[];
39
+ readonly readiness: SemanticMergeReadiness;
40
+ readonly risk: SemanticImportImpactRisk;
41
+ readonly confidence: SemanticImportImpactConfidence;
42
+ }
43
+
44
+ export interface SemanticImportImpactSummary {
45
+ readonly kind: 'frontier.lang.semanticImpact';
46
+ readonly version: 1;
47
+ readonly records: readonly SemanticImportImpactRecord[];
48
+ readonly summary: {
49
+ readonly total: number; readonly byRisk: Readonly<Record<string, number>>; readonly byReadiness: Readonly<Record<string, number>>;
50
+ readonly conflictKeys: readonly string[]; readonly dependencyRelations: number; readonly affectedSymbols: number;
51
+ readonly sourceMapMappings: number; readonly sourcePreservationRecords: number; readonly patchHints: number;
52
+ readonly proofObligations: number; readonly openProofObligations: number; readonly failedProofObligations: number; readonly paradigmSemantics: number; readonly loweringRecords: number;
53
+ readonly evidenceIds: number; readonly verificationPlans: readonly string[]; readonly requiredVerificationSteps: number;
54
+ };
55
+ }
@@ -14,6 +14,7 @@ export interface SemanticImportSidecarProofAdmissionSummary {
14
14
  readonly discharged: number;
15
15
  readonly pending?: number;
16
16
  readonly failed: number;
17
+ readonly stale?: number;
17
18
  readonly assumed?: number;
18
19
  readonly externalToolRequired?: number;
19
20
  readonly open: number;
@@ -26,6 +26,7 @@ import type {
26
26
  } from '@shapeshift-labs/frontier-lang-kernel';
27
27
  import type { SemanticImportSidecarAdmission, SemanticImportSidecarQuality } from './semantic-sidecar-admission.js';
28
28
  import type { SemanticMergeConflictClass, SemanticMergeConflictSummary } from './semantic-merge-conflicts.js';
29
+ import type { SemanticImportImpactSummary } from './semantic-impact.js';
29
30
  import type { Diagnostic } from '@shapeshift-labs/frontier-lang-checker';
30
31
  import type { EmitTypeScriptOptions, TypeScriptAstModule, TypeScriptDocumentSourceMapResult, TypeScriptGeneratedSourceMapResult } from '@shapeshift-labs/frontier-lang-typescript';
31
32
  import type { EmitJavaScriptOptions, EmitJavaScriptWithSourceMapResult, JavaScriptAstModule, JavaScriptSourceMapResult } from '@shapeshift-labs/frontier-lang-javascript';
@@ -263,7 +264,7 @@ export interface SemanticImportSidecar {
263
264
  readonly universalAstLayers: SemanticImportSidecarUniversalAstLayerSummary;
264
265
  readonly proofSpec: SemanticImportSidecarProofSpecSummary;
265
266
  readonly paradigmSemantics: SemanticImportSidecarParadigmSemanticsSummary;
266
- readonly dependencies: SemanticImportDependencySummary;
267
+ readonly dependencies: SemanticImportDependencySummary; readonly semanticImpact: SemanticImportImpactSummary;
267
268
  readonly patchHints: readonly SemanticImportPatchHint[];
268
269
  readonly quality: SemanticImportSidecarQuality;
269
270
  readonly admission: SemanticImportSidecarAdmission;
@@ -302,6 +303,7 @@ export interface SemanticImportSidecar {
302
303
  readonly paradigmSemanticsGroups: number;
303
304
  readonly paradigmSemanticsLoweringRecords: number;
304
305
  readonly dependencyRelations: number; readonly dependencyPredicates: readonly string[];
306
+ readonly semanticImpactRecords: number; readonly semanticImpactHighRiskRecords: number; readonly semanticImpactRequiredVerificationSteps: number;
305
307
  readonly patchHints: number;
306
308
  readonly evidenceWarnings: number;
307
309
  readonly semanticImportExpected: boolean; readonly semanticImportExpectedSatisfied: boolean; readonly semanticImportExpectedMissingReasonCodes: readonly string[];
@@ -310,10 +312,4 @@ export interface SemanticImportSidecar {
310
312
  readonly metadata?: Record<string, unknown>;
311
313
  }
312
314
 
313
- export interface SemanticImportSidecarOptions {
314
- readonly id?: string;
315
- readonly generatedAt?: number;
316
- readonly regionPrefix?: string;
317
- readonly targetPath?: string; readonly expected?: boolean; readonly semanticImportExpected?: boolean;
318
- readonly metadata?: Record<string, unknown>;
319
- }
315
+ export interface SemanticImportSidecarOptions { readonly id?: string; readonly generatedAt?: number; readonly regionPrefix?: string; readonly targetPath?: string; readonly expected?: boolean; readonly semanticImportExpected?: boolean; readonly metadata?: Record<string, unknown>; }
package/dist/index.d.ts CHANGED
@@ -16,6 +16,7 @@ export * from './declarations/semantic-sidecar-admission.js';
16
16
  export * from './declarations/semantic-merge-conflicts.js';
17
17
  export * from './declarations/semantic-history.js';
18
18
  export * from './declarations/semantic-patch-bundle.js';
19
+ export * from './declarations/semantic-impact.js';
19
20
  export * from './declarations/semantic-sidecar.js';
20
21
  export * from './declarations/native-diff.js';
21
22
  export * from './declarations/semantic-slice.js';
@@ -109,6 +109,18 @@ export function createLightweightNativeImport(input) {
109
109
  occurrences.push(...dependencies.occurrences);
110
110
  relations.push(...dependencies.relations);
111
111
  facts.push(...dependencies.facts);
112
+ for (const occurrence of dependencies.occurrences) {
113
+ mappings.push({
114
+ id: `map_${idFragment(occurrence.id)}`,
115
+ nativeAstNodeId: occurrence.nativeAstNodeId,
116
+ semanticSymbolId: occurrence.symbolId,
117
+ semanticOccurrenceId: occurrence.id,
118
+ sourceSpan: occurrence.span,
119
+ evidenceIds: [evidenceId],
120
+ lossIds: [],
121
+ precision: 'line'
122
+ });
123
+ }
112
124
  losses.push(...lightweightCoverageLosses(input, declarations, input.sourcePreservation));
113
125
 
114
126
  const semanticIndex = createSemanticIndexRecord({
@@ -48,7 +48,7 @@ export function createLightweightSemanticLayers(input) {
48
48
  })),
49
49
  evaluationModels: [evaluationModel(input, evidenceIds)],
50
50
  effectRegions: effectRegions(input, evidenceIds),
51
- loweringRecords: mappings.slice(0, 40).map((mapping) => ({
51
+ loweringRecords: mappings.map((mapping) => ({
52
52
  id: `lowering_${mapping.id}`,
53
53
  kind: 'nativeSourceToFrontierSemanticIndex',
54
54
  sourceMapId: mapping.sourceMapId,
@@ -1,4 +1,4 @@
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';
1
+ import{idFragment,maxSemanticMergeReadiness,uniqueRecordsById}from'../../native-import-utils.js';import{summarizeSemanticImportDependencies}from'../../semantic-import-dependencies.js';import{createSemanticImportImpact}from'../../semantic-import-impact.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 = {}) {
@@ -38,6 +38,22 @@ export function createSemanticImportSidecar(importResult, options = {}) {
38
38
  readiness
39
39
  });
40
40
  const admission = createSemanticImportSidecarAdmission(quality, readiness);
41
+ const semanticImpact = createSemanticImportImpact({
42
+ imports,
43
+ symbols,
44
+ ownershipRegions,
45
+ sourceMapMappings,
46
+ sourcePreservation,
47
+ dependencies,
48
+ patchHints,
49
+ proofSpec,
50
+ evidence: {
51
+ total: evidence.length,
52
+ failed: evidence.filter((record) => record.status === 'failed').map((record) => record.id),
53
+ ids: evidence.map((record) => record.id)
54
+ },
55
+ readiness
56
+ });
41
57
  return {
42
58
  kind: 'frontier.lang.semanticImportSidecar',
43
59
  version: 1,
@@ -58,6 +74,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
58
74
  proofSpec,
59
75
  paradigmSemantics,
60
76
  dependencies,
77
+ semanticImpact,
61
78
  patchHints,
62
79
  quality,
63
80
  admission,
@@ -101,6 +118,9 @@ export function createSemanticImportSidecar(importResult, options = {}) {
101
118
  paradigmSemanticsLoweringRecords: paradigmSemantics.loweringRecords,
102
119
  dependencyRelations: dependencies.total,
103
120
  dependencyPredicates: dependencies.predicates,
121
+ semanticImpactRecords: semanticImpact.summary.total,
122
+ semanticImpactHighRiskRecords: semanticImpact.summary.byRisk.high ?? 0,
123
+ semanticImpactRequiredVerificationSteps: semanticImpact.summary.requiredVerificationSteps,
104
124
  patchHints: patchHints.length,
105
125
  evidenceWarnings: quality.emptyEvidenceWarnings.length,
106
126
  semanticImportExpected: quality.expected,
@@ -72,6 +72,18 @@ export function createSemanticImportSidecarQuality(input) {
72
72
  'review-pending-proof-obligations',
73
73
  sourcePaths
74
74
  ));
75
+ if ((proofSpec.byReadinessStatus?.open ?? 0) > 0) warnings.push(sidecarQualityWarning(
76
+ 'open-proof-obligations',
77
+ 'Semantic sidecar has open proof obligations.',
78
+ 'review-open-proof-obligations',
79
+ sourcePaths
80
+ ));
81
+ if (proofSpec.stale > 0) warnings.push(sidecarQualityWarning(
82
+ 'stale-proof-obligations',
83
+ 'Semantic sidecar has stale proof obligations.',
84
+ 'rerun-stale-proof-obligations',
85
+ sourcePaths
86
+ ));
75
87
  if (proofSpec.assumed > 0) warnings.push(sidecarQualityWarning(
76
88
  'assumed-proof-obligations',
77
89
  'Semantic sidecar has assumed proof obligations.',
@@ -120,6 +132,7 @@ export function createSemanticImportSidecarQuality(input) {
120
132
  discharged: proofSpec.discharged,
121
133
  pending: proofSpec.pending,
122
134
  failed: proofSpec.failed,
135
+ stale: proofSpec.stale,
123
136
  assumed: proofSpec.assumed,
124
137
  externalToolRequired: proofSpec.externalToolRequired,
125
138
  open: proofSpec.open,
@@ -186,6 +199,6 @@ function sidecarAdmissionAction(quality, readiness) {
186
199
  }
187
200
 
188
201
  function sidecarProofReviewObligations(proofSummary) {
189
- return (proofSummary.pending ?? 0) + (proofSummary.assumed ?? 0) +
202
+ return (proofSummary.open ?? 0) + (proofSummary.stale ?? 0) + (proofSummary.assumed ?? 0) +
190
203
  (proofSummary.externalToolRequired ?? 0) + (proofSummary.unknown ?? 0);
191
204
  }
@@ -0,0 +1,223 @@
1
+ const ignoredIdentifiers = new Set([
2
+ 'abstract', 'alias', 'and', 'as', 'async', 'await', 'break', 'case', 'catch',
3
+ 'class', 'const', 'continue', 'def', 'default', 'defer', 'delete', 'do',
4
+ 'else', 'enum', 'export', 'extends', 'false', 'finally', 'fn', 'for', 'from',
5
+ 'func', 'function', 'if', 'impl', 'import', 'in', 'interface', 'let', 'match',
6
+ 'mod', 'module', 'namespace', 'new', 'nil', 'none', 'not', 'null', 'or',
7
+ 'package', 'pass', 'protocol', 'pub', 'public', 'return', 'self', 'static',
8
+ 'struct', 'super', 'switch', 'this', 'throw', 'trait', 'true', 'try', 'type',
9
+ 'undefined', 'use', 'using', 'var', 'void', 'while', 'with', 'yield'
10
+ ]);
11
+
12
+ export function dependencyIdentifiers(input, declaration, line) {
13
+ const identifiers = new Set();
14
+ addIdentifier(identifiers, declaration.name);
15
+ for (const part of splitIdentifierPath(declaration.name)) addIdentifier(identifiers, part);
16
+ for (const field of ['methodName', 'propertyName', 'owner']) addIdentifier(identifiers, declaration.fields?.[field]);
17
+ if (declaration.role === 'import') {
18
+ for (const identifier of importLineIdentifiers(input, line)) addIdentifier(identifiers, identifier);
19
+ for (const identifier of importPathIdentifiers(declaration.importPath ?? declaration.name)) addIdentifier(identifiers, identifier);
20
+ }
21
+ return [...identifiers];
22
+ }
23
+
24
+ export function addIdentifier(set, value) {
25
+ if (isDependencyIdentifier(value)) set.add(String(value));
26
+ }
27
+
28
+ export function isDependencyIdentifier(value) {
29
+ const text = String(value ?? '');
30
+ return /^[A-Za-z_$][\w$]*$/.test(text) && !ignoredIdentifiers.has(text.toLowerCase());
31
+ }
32
+
33
+ export function dependencyScanRanges(input, declarations, lines) {
34
+ const ordered = [...(declarations ?? [])].sort((left, right) => (left.span?.startLine ?? 0) - (right.span?.startLine ?? 0));
35
+ return ordered
36
+ .filter((declaration) => declaration?.symbolId && declaration.role !== 'import')
37
+ .map((declaration, index) => ({
38
+ declaration,
39
+ startLine: declaration.span?.startLine ?? 1,
40
+ endLine: dependencyScanEndLine(input, declaration, lines, ordered[index + 1]?.span?.startLine)
41
+ }));
42
+ }
43
+
44
+ export function maskDependencyLine(input, line, state) {
45
+ const language = String(input.language ?? '').toLowerCase();
46
+ const text = String(line ?? '');
47
+ let output = '';
48
+ let quote;
49
+ let escaped = false;
50
+ for (let index = 0; index < text.length; index += 1) {
51
+ const char = text[index];
52
+ const next = text[index + 1];
53
+ if (state.inBlockComment) {
54
+ output += ' ';
55
+ if (char === '*' && next === '/') {
56
+ output += ' ';
57
+ index += 1;
58
+ state.inBlockComment = false;
59
+ }
60
+ continue;
61
+ }
62
+ if (quote) {
63
+ output += ' ';
64
+ if (escaped) escaped = false;
65
+ else if (char === '\\') escaped = true;
66
+ else if (char === quote) quote = undefined;
67
+ continue;
68
+ }
69
+ if (char === '/' && next === '*') {
70
+ output += ' ';
71
+ index += 1;
72
+ state.inBlockComment = true;
73
+ continue;
74
+ }
75
+ if (startsLineComment(language, char, next, text, index)) break;
76
+ if (char === '\'' || char === '"' || char === '`') {
77
+ output += ' ';
78
+ quote = char;
79
+ continue;
80
+ }
81
+ output += char;
82
+ }
83
+ return output;
84
+ }
85
+
86
+ export function emptyDependencySummary() {
87
+ return { total: 0, calls: 0, uses: 0 };
88
+ }
89
+
90
+ function importLineIdentifiers(input, line) {
91
+ const language = String(input.language ?? '').toLowerCase();
92
+ const source = String(line ?? '').trim();
93
+ if (language === 'python') return pythonImportIdentifiers(source);
94
+ if (language === 'rust') return rustImportIdentifiers(source);
95
+ if (language === 'go') return aliasedPathImportIdentifiers(source);
96
+ if (language === 'javascript' || language === 'typescript') return jsImportIdentifiers(source);
97
+ if (language === 'haskell') return haskellImportIdentifiers(source);
98
+ return aliasedPathImportIdentifiers(source);
99
+ }
100
+
101
+ function jsImportIdentifiers(source) {
102
+ const identifiers = [];
103
+ let match = source.match(/^import\s+([A-Za-z_$][\w$]*)\s+from\b/);
104
+ if (match) identifiers.push(match[1]);
105
+ match = source.match(/^import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\b/);
106
+ if (match) identifiers.push(match[1]);
107
+ match = source.match(/^import\s+[^,{]+,\s*\{([^}]+)\}\s+from\b|^import\s+\{([^}]+)\}\s+from\b/);
108
+ if (match) identifiers.push(...namedImportIdentifiers(match[1] ?? match[2]));
109
+ return identifiers;
110
+ }
111
+
112
+ function pythonImportIdentifiers(source) {
113
+ const identifiers = [];
114
+ const importMatch = source.match(/^import\s+(.+)$/);
115
+ if (importMatch) identifiers.push(...commaParts(importMatch[1]).map((part) => part.split(/\s+as\s+/i).pop()));
116
+ const fromMatch = source.match(/^from\s+\S+\s+import\s+(.+)$/);
117
+ if (fromMatch) identifiers.push(...namedImportIdentifiers(fromMatch[1]));
118
+ return identifiers;
119
+ }
120
+
121
+ function rustImportIdentifiers(source) {
122
+ const match = source.match(/^use\s+(.+?);?$/);
123
+ if (!match) return [];
124
+ const raw = match[1].replace(/[{}]/g, ' ');
125
+ return raw.split(/::|,|\s+as\s+/i).map((part) => part.trim()).filter(Boolean);
126
+ }
127
+
128
+ function haskellImportIdentifiers(source) {
129
+ const match = source.match(/^import\s+(?:qualified\s+)?([A-Za-z_][\w.]*)(?:\s+as\s+([A-Za-z_]\w*))?/);
130
+ return match ? [match[2], ...splitIdentifierPath(match[1])].filter(Boolean) : [];
131
+ }
132
+
133
+ function aliasedPathImportIdentifiers(source) {
134
+ const alias = source.match(/^(?:import|using|use)\s+([A-Za-z_$][\w$]*)\s*=/)?.[1]
135
+ ?? source.match(/^(?:import|using|use)\s+([A-Za-z_$][\w$]*)\s+["']/)?.[1]
136
+ ?? source.match(/\bas\s+([A-Za-z_$][\w$]*)\b/)?.[1];
137
+ return alias ? [alias] : [];
138
+ }
139
+
140
+ function namedImportIdentifiers(raw) {
141
+ return commaParts(raw).map((part) => part.trim().split(/\s+as\s+/i).pop()?.trim()).filter(Boolean);
142
+ }
143
+
144
+ function importPathIdentifiers(value) {
145
+ return splitIdentifierPath(String(value ?? '').replace(/['";]/g, ''));
146
+ }
147
+
148
+ function splitIdentifierPath(value) {
149
+ return String(value ?? '').split(/[^A-Za-z0-9_$]+/).filter(isDependencyIdentifier);
150
+ }
151
+
152
+ function commaParts(raw) {
153
+ return String(raw ?? '').split(',').map((part) => part.trim()).filter(Boolean);
154
+ }
155
+
156
+ function dependencyScanEndLine(input, declaration, lines, nextStartLine) {
157
+ const startLine = declaration.span?.startLine ?? 1;
158
+ if (!declaration.metadata?.hasBody) return startLine;
159
+ if (usesIndentationBlocks(input)) {
160
+ return indentationRegionEndLine(lines, startLine);
161
+ }
162
+ const balancedEnd = balancedRegionEndLine(lines, startLine);
163
+ const fallbackEnd = nextStartLine ? nextStartLine - 1 : lines.length;
164
+ return Math.max(startLine, Math.min(balancedEnd > startLine ? balancedEnd : fallbackEnd, lines.length));
165
+ }
166
+
167
+ function usesIndentationBlocks(input) {
168
+ return ['python'].includes(String(input.language ?? '').toLowerCase());
169
+ }
170
+
171
+ function indentationRegionEndLine(lines, startLine) {
172
+ const header = lines[startLine - 1]?.line ?? '';
173
+ const baseIndent = indentationWidth(header);
174
+ let lastBodyLine = startLine;
175
+ let sawBodyLine = false;
176
+ for (let index = startLine; index < lines.length; index += 1) {
177
+ const line = lines[index]?.line ?? '';
178
+ if (!line.trim()) {
179
+ if (sawBodyLine) lastBodyLine = index + 1;
180
+ continue;
181
+ }
182
+ const indent = indentationWidth(line);
183
+ if (indent <= baseIndent) return Math.max(startLine, lastBodyLine);
184
+ sawBodyLine = true;
185
+ lastBodyLine = index + 1;
186
+ }
187
+ return lastBodyLine;
188
+ }
189
+
190
+ function indentationWidth(line) {
191
+ let width = 0;
192
+ for (const char of String(line ?? '')) {
193
+ if (char === ' ') width += 1;
194
+ else if (char === '\t') width += 4;
195
+ else break;
196
+ }
197
+ return width;
198
+ }
199
+
200
+ function balancedRegionEndLine(lines, startLine) {
201
+ const state = { inBlockComment: false };
202
+ let depth = 0;
203
+ let opened = false;
204
+ for (let index = Math.max(0, startLine - 1); index < lines.length; index += 1) {
205
+ for (const char of maskDependencyLine({ language: 'javascript' }, lines[index].line, state)) {
206
+ if (char === '{' || char === '[' || char === '(') {
207
+ depth += 1;
208
+ opened = true;
209
+ } else if (char === '}' || char === ']' || char === ')') {
210
+ depth -= 1;
211
+ }
212
+ }
213
+ if (opened && depth <= 0) return lines[index].number;
214
+ }
215
+ return startLine;
216
+ }
217
+
218
+ function startsLineComment(language, char, next, text, index) {
219
+ if (char === '/' && next === '/') return true;
220
+ if (char === '#' && !['csharp'].includes(language)) return true;
221
+ if (char === '-' && next === '-' && ['sql', 'haskell', 'lua'].includes(language)) return true;
222
+ return text.startsWith('rem ', index) && language === 'shell';
223
+ }
@@ -1,23 +1,20 @@
1
1
  import { idFragment } from './native-import-utils.js';
2
+ import {
3
+ addIdentifier,
4
+ dependencyIdentifiers,
5
+ dependencyScanRanges,
6
+ emptyDependencySummary,
7
+ isDependencyIdentifier,
8
+ maskDependencyLine
9
+ } from './lightweight-dependency-language.js';
2
10
  import { sourceLines } from './native-region-scanner-core.js';
3
11
 
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
12
  export function lightweightDependencyRelations(input, declarations, documentId) {
14
- if (!jsLikeLanguages.has(String(input.language ?? '').toLowerCase())) {
15
- return { relations: [], occurrences: [], facts: [], summary: emptySummary() };
16
- }
13
+ if (typeof input.sourceText !== 'string') return { relations: [], occurrences: [], facts: [], summary: emptyDependencySummary() };
17
14
  const lines = sourceLines(input.sourceText);
18
15
  const identifiers = declarationIdentifierMap(input, declarations, lines);
19
16
  const records = { relations: [], occurrences: [], facts: [], seen: new Set() };
20
- for (const scan of declarationScanRanges(declarations, lines)) {
17
+ for (const scan of dependencyScanRanges(input, declarations, lines)) {
21
18
  scanDeclarationDependencies(input, documentId, scan, identifiers, lines, records);
22
19
  }
23
20
  return {
@@ -52,101 +49,27 @@ function declarationIdentifierMap(input, declarations, lines) {
52
49
 
53
50
  function identifiersForDeclaration(input, declaration, lines) {
54
51
  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));
52
+ const line = lines[(declaration.span?.startLine ?? 1) - 1]?.line ?? '';
53
+ for (const identifier of dependencyIdentifiers(input, declaration, line)) {
54
+ addIdentifier(identifiers, identifier);
62
55
  }
63
56
  return [...identifiers];
64
57
  }
65
58
 
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
59
  function addIdentifierTarget(map, identifier, target) {
94
- if (!isIdentifier(identifier)) return;
60
+ if (!isDependencyIdentifier(identifier)) return;
95
61
  const existing = map.get(identifier) ?? [];
96
62
  if (!existing.some((entry) => entry.symbolId === target.symbolId)) existing.push(target);
97
63
  map.set(identifier, existing);
98
64
  }
99
65
 
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
66
  function scanDeclarationDependencies(input, documentId, scan, identifiers, lines, records) {
144
- const state = { inBlockComment: false, inTemplateString: false };
67
+ const state = { inBlockComment: false };
145
68
  for (let lineNumber = scan.startLine; lineNumber <= scan.endLine; lineNumber += 1) {
146
- const scanLine = maskReferenceLine(lines[lineNumber - 1]?.line ?? '', state);
69
+ const scanLine = maskDependencyLine(input, lines[lineNumber - 1]?.line ?? '', state);
147
70
  for (const match of scanLine.matchAll(/[A-Za-z_$][\w$]*/g)) {
148
71
  const name = match[0];
149
- if (ignoredIdentifiers.has(name) || !identifiers.has(name)) continue;
72
+ if (!isDependencyIdentifier(name) || !identifiers.has(name)) continue;
150
73
  const targets = identifiers.get(name).filter((target) => target.symbolId !== scan.declaration.symbolId);
151
74
  for (const target of targets) {
152
75
  addDependencyRecord(input, documentId, scan.declaration, target, {
@@ -197,7 +120,7 @@ function addDependencyRecord(input, documentId, caller, target, occurrence, reco
197
120
  nativeAstNodeId: caller.nodeId
198
121
  });
199
122
  records.facts.push({
200
- id: `fact_${idFragment(relationId)}_lightweight_dependency`,
123
+ id: `fact_${idFragment(relationId)}_${records.facts.length + 1}_lightweight_dependency`,
201
124
  predicate: 'lightweightDependency',
202
125
  subjectId: caller.symbolId,
203
126
  value: {
@@ -213,59 +136,3 @@ function addDependencyRecord(input, documentId, caller, target, occurrence, reco
213
136
  function isCallReference(line, afterIdentifierIndex) {
214
137
  return /^\s*\(/.test(String(line ?? '').slice(afterIdentifierIndex));
215
138
  }
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,67 @@
1
+ import type { SemanticMergeReadiness, SourceSpan } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import type { NativeImportRegionTaxonomyKind } from './index.js';
3
+
4
+ export type SemanticImportImpactRisk = 'low' | 'medium' | 'high' | string;
5
+ export type SemanticImportImpactConfidence = 'source-exact' | 'source-addressed' | 'estimated-source-region' | 'review-required' | string;
6
+
7
+ export interface SemanticImportImpactVerificationStep {
8
+ readonly kind: 'dependency-review' | 'source-map-review' | 'reject-or-reprove' | 'proof-review' | 'patch-admission' | 'evidence-review' | string;
9
+ readonly reason: string;
10
+ readonly required: boolean;
11
+ }
12
+
13
+ export interface SemanticImportImpactRecord {
14
+ readonly id: string;
15
+ readonly kind: 'ownership-region-impact' | string;
16
+ readonly ownershipRegionId: string;
17
+ readonly ownershipKey: string;
18
+ readonly regionKind?: NativeImportRegionTaxonomyKind;
19
+ readonly sourcePath?: string;
20
+ readonly sourceHash?: string;
21
+ readonly sourceSpan?: SourceSpan;
22
+ readonly symbolIds: readonly string[];
23
+ readonly symbolNames: readonly string[];
24
+ readonly dependencyRelationIds: readonly string[];
25
+ readonly dependencyPredicates: readonly string[];
26
+ readonly affectedSymbolIds: readonly string[];
27
+ readonly sourceMapMappingIds: readonly string[];
28
+ readonly sourcePreservationRecordIds: readonly string[];
29
+ readonly patchHintIds: readonly string[];
30
+ readonly proofSpecIds: readonly string[];
31
+ readonly proofObligationIds: readonly string[];
32
+ readonly failedProofObligationIds: readonly string[];
33
+ readonly openProofObligationIds: readonly string[];
34
+ readonly paradigmSemanticIds: readonly string[];
35
+ readonly loweringRecordIds: readonly string[];
36
+ readonly evidenceIds: readonly string[];
37
+ readonly verificationPlan: readonly SemanticImportImpactVerificationStep[];
38
+ readonly conflictKeys: readonly string[];
39
+ readonly readiness: SemanticMergeReadiness;
40
+ readonly risk: SemanticImportImpactRisk;
41
+ readonly confidence: SemanticImportImpactConfidence;
42
+ }
43
+
44
+ export interface SemanticImportImpactSummary {
45
+ readonly kind: 'frontier.lang.semanticImpact';
46
+ readonly version: 1;
47
+ readonly records: readonly SemanticImportImpactRecord[];
48
+ readonly summary: {
49
+ readonly total: number;
50
+ readonly byRisk: Readonly<Record<string, number>>;
51
+ readonly byReadiness: Readonly<Record<string, number>>;
52
+ readonly conflictKeys: readonly string[];
53
+ readonly dependencyRelations: number;
54
+ readonly affectedSymbols: number;
55
+ readonly sourceMapMappings: number;
56
+ readonly sourcePreservationRecords: number;
57
+ readonly patchHints: number;
58
+ readonly proofObligations: number;
59
+ readonly openProofObligations: number;
60
+ readonly failedProofObligations: number;
61
+ readonly paradigmSemantics: number;
62
+ readonly loweringRecords: number;
63
+ readonly evidenceIds: number;
64
+ readonly verificationPlans: readonly string[];
65
+ readonly requiredVerificationSteps: number;
66
+ };
67
+ }
@@ -0,0 +1,317 @@
1
+ import { countBy, idFragment, maxSemanticMergeReadiness, uniqueRecordsById, uniqueStrings } from './native-import-utils.js';
2
+ import { semanticDependencyPredicateKey } from './semantic-import-dependencies.js';
3
+
4
+ function createSemanticImportImpact(input) {
5
+ const relations = collectDependencyRelations(input.imports, input.dependencies);
6
+ const proofRecords = collectProofRecords(input.imports);
7
+ const paradigmRecords = collectParadigmRecords(input.imports);
8
+ const symbolsByRegion = groupBy(input.symbols, (symbol) => symbol.ownershipRegionId);
9
+ const symbolIds = new Set((input.symbols ?? []).map((symbol) => symbol.id).filter(Boolean));
10
+ const records = [];
11
+ for (const region of input.ownershipRegions ?? []) {
12
+ const regionSymbols = symbolsForRegion(region, symbolsByRegion);
13
+ const record = semanticImpactRecord({
14
+ region,
15
+ regionSymbols,
16
+ relations: relations.filter((relation) => relationTouchesSymbols(relation, regionSymbols, symbolIds)),
17
+ sourceMapMappings: sourceMapMappingsForSymbols(input.sourceMapMappings, regionSymbols),
18
+ sourcePreservationRecords: preservationRecordsForSymbols(input.sourcePreservation?.records, regionSymbols),
19
+ proofRecords: proofRecordsForSymbols(proofRecords, regionSymbols, region),
20
+ paradigmRecords: paradigmRecordsForSymbols(paradigmRecords, regionSymbols, region),
21
+ patchHints: (input.patchHints ?? []).filter((hint) => hint.ownershipRegionId === region.id || hint.ownershipKey === region.key),
22
+ allSymbolIds: symbolIds,
23
+ evidence: input.evidence ?? { failed: [], ids: [] },
24
+ readiness: input.readiness
25
+ });
26
+ records.push(record);
27
+ }
28
+ return {
29
+ kind: 'frontier.lang.semanticImpact',
30
+ version: 1,
31
+ records,
32
+ summary: summarizeSemanticImpactRecords(records)
33
+ };
34
+ }
35
+
36
+ function semanticImpactRecord(input) {
37
+ const symbolIds = uniqueStrings(input.regionSymbols.map((symbol) => symbol.id).filter(Boolean));
38
+ const dependencyPredicates = uniqueStrings(input.relations.map((relation) => semanticDependencyPredicateKey(relation.predicate)));
39
+ const proof = summarizeProofImpact(input.proofRecords);
40
+ const paradigm = summarizeParadigmImpact(input.paradigmRecords);
41
+ const evidenceIds = relatedEvidenceIds(input);
42
+ const readiness = input.regionSymbols.reduce(
43
+ (current, symbol) => maxSemanticMergeReadiness(current, symbol.readiness),
44
+ input.readiness
45
+ );
46
+ const verificationPlan = semanticImpactVerificationPlan({
47
+ dependencyPredicates,
48
+ proof,
49
+ paradigm,
50
+ evidenceIds,
51
+ sourcePreservationRecords: input.sourcePreservationRecords,
52
+ patchHints: input.patchHints,
53
+ readiness
54
+ });
55
+ const risk = semanticImpactRisk({
56
+ readiness,
57
+ proof,
58
+ failedEvidenceIds: input.evidence.failed ?? [],
59
+ sourcePreservationRecords: input.sourcePreservationRecords,
60
+ relationCount: input.relations.length,
61
+ verificationPlan
62
+ });
63
+ return {
64
+ id: `impact_${idFragment(input.region.id ?? input.region.key)}`,
65
+ kind: 'ownership-region-impact',
66
+ ownershipRegionId: input.region.id,
67
+ ownershipKey: input.region.key,
68
+ regionKind: input.region.regionKind,
69
+ sourcePath: input.region.sourcePath,
70
+ sourceHash: input.region.sourceHash,
71
+ sourceSpan: input.region.sourceSpan,
72
+ symbolIds,
73
+ symbolNames: uniqueStrings(input.regionSymbols.map((symbol) => symbol.name).filter(Boolean)),
74
+ dependencyRelationIds: input.relations.map((relation) => relation.id).filter(Boolean),
75
+ dependencyPredicates,
76
+ affectedSymbolIds: affectedSymbolIds(input.relations, symbolIds, input.allSymbolIds),
77
+ sourceMapMappingIds: uniqueStrings([
78
+ ...input.sourceMapMappings.map((mapping) => mapping.id),
79
+ ...input.sourcePreservationRecords.map((record) => record.sourceMapMappingId)
80
+ ].filter(Boolean)),
81
+ sourcePreservationRecordIds: input.sourcePreservationRecords.map((record) => record.id).filter(Boolean),
82
+ patchHintIds: input.patchHints.map((hint) => hint.id).filter(Boolean),
83
+ proofSpecIds: proof.specIds,
84
+ proofObligationIds: proof.obligationIds,
85
+ failedProofObligationIds: proof.failedObligationIds,
86
+ openProofObligationIds: proof.openObligationIds,
87
+ paradigmSemanticIds: paradigm.semanticIds,
88
+ loweringRecordIds: paradigm.loweringIds,
89
+ evidenceIds,
90
+ verificationPlan,
91
+ conflictKeys: semanticImpactConflictKeys(input.region, symbolIds, input.relations, dependencyPredicates),
92
+ readiness,
93
+ risk,
94
+ confidence: semanticImpactConfidence(input.region, input.sourceMapMappings, input.sourcePreservationRecords)
95
+ };
96
+ }
97
+
98
+ function collectDependencyRelations(imports, dependencies) {
99
+ const dependencyIds = new Set(dependencies?.ids ?? []);
100
+ return uniqueRecordsById((imports ?? [])
101
+ .flatMap((imported) => imported?.semanticIndex?.relations ?? imported?.universalAst?.semanticIndex?.relations ?? [])
102
+ .filter((relation) => dependencyIds.has(relation?.id)));
103
+ }
104
+
105
+ function collectProofRecords(imports) {
106
+ const records = [];
107
+ for (const imported of imports ?? []) {
108
+ const proof = imported?.universalAst?.proof ?? imported?.proof;
109
+ if (!proof) continue;
110
+ const proofGroups = [
111
+ ['proof-record', proof.contracts, proof.refinements, proof.invariants, proof.termination, proof.temporal],
112
+ ['obligation', proof.obligations],
113
+ ['proof-record', proof.artifacts, proof.assumptions]
114
+ ];
115
+ for (const [proofRecordKind, ...groups] of proofGroups) for (const record of groups.flatMap((group) => group ?? [])) records.push({ ...record, proofSpecId: proof.id, proofRecordKind });
116
+ }
117
+ return records.filter(Boolean);
118
+ }
119
+
120
+ const ParadigmImpactGroups = Object.freeze([
121
+ 'bindingScopes', 'bindings', 'patterns', 'typeConstraints', 'evaluationModels',
122
+ 'memoryLocations', 'effectRegions', 'controlRegions', 'logicPrograms',
123
+ 'actorSystems', 'stackEffects', 'arrayShapes', 'numericKernels',
124
+ 'dataflowNetworks', 'clockModels', 'objectModels', 'macroExpansions',
125
+ 'reflectionBoundaries', 'loweringRecords'
126
+ ]);
127
+
128
+ function collectParadigmRecords(imports) {
129
+ const records = [];
130
+ for (const imported of imports ?? []) {
131
+ const semantics = imported?.universalAst?.paradigmSemantics ?? imported?.paradigmSemantics;
132
+ if (!semantics) continue;
133
+ for (const group of ParadigmImpactGroups) {
134
+ for (const record of semantics[group] ?? []) records.push({ ...record, paradigmGroup: group, paradigmSpecId: semantics.id });
135
+ }
136
+ }
137
+ return records.filter(Boolean);
138
+ }
139
+
140
+ function proofRecordsForSymbols(proofRecords, symbols, region) {
141
+ const ids = new Set(symbols.map((symbol) => symbol.id).filter(Boolean));
142
+ const mappingIds = new Set(symbols.map((symbol) => symbol.sourceMapMappingId).filter(Boolean));
143
+ const nativeNodeIds = new Set(symbols.map((symbol) => symbol.nativeAstNodeId).filter(Boolean));
144
+ const regionIds = new Set([region.id, region.key].filter(Boolean));
145
+ return proofRecords.filter((record) => isImportWideProofRecord(record) || ids.has(record.subjectId) || ids.has(record.semanticSymbolId) || mappingIds.has(record.sourceMapMappingId) || nativeNodeIds.has(record.nativeAstNodeId) || regionIds.has(record.subjectId) || regionIds.has(record.ownershipRegionId));
146
+ }
147
+
148
+ function isImportWideProofRecord(record) {
149
+ const kind = String(record?.subjectKind ?? '').toLowerCase();
150
+ return (!record?.subjectId && !record?.semanticSymbolId && !record?.sourceMapMappingId && !record?.nativeAstNodeId && !record?.ownershipRegionId) || ['import', 'source', 'nativesource', 'document', 'proofspec'].includes(kind);
151
+ }
152
+
153
+ function paradigmRecordsForSymbols(records, symbols, region) {
154
+ const symbolIds = new Set(symbols.map((symbol) => symbol.id).filter(Boolean));
155
+ const mappingIds = new Set(symbols.map((symbol) => symbol.sourceMapMappingId).filter(Boolean));
156
+ const nativeNodeIds = new Set(symbols.map((symbol) => symbol.nativeAstNodeId).filter(Boolean));
157
+ const regionIds = new Set([region.id, region.key].filter(Boolean));
158
+ return records.filter((record) => symbolIds.has(record.semanticSymbolId)
159
+ || symbolIds.has(record.symbolId)
160
+ || symbolIds.has(record.subjectId)
161
+ || mappingIds.has(record.sourceMapMappingId)
162
+ || nativeNodeIds.has(record.nativeAstNodeId)
163
+ || regionIds.has(record.ownershipRegionId)
164
+ || regionIds.has(record.subjectId));
165
+ }
166
+
167
+ function summarizeProofImpact(records) {
168
+ const obligationRecords = records.filter((record) => record.proofRecordKind === 'obligation');
169
+ const failed = obligationRecords.filter((record) => proofReadinessStatus(record.status) === 'failed');
170
+ const open = obligationRecords.filter((record) => ['open', 'pending', 'unknown', 'stale', 'assumed', 'external-tool-required'].includes(proofReadinessStatus(record.status)));
171
+ return {
172
+ specIds: uniqueStrings(records.map((record) => record.proofSpecId).filter(Boolean)),
173
+ obligationIds: uniqueStrings(obligationRecords.map((record) => record.id).filter(Boolean)),
174
+ failedObligationIds: uniqueStrings(failed.map((record) => record.id).filter(Boolean)),
175
+ openObligationIds: uniqueStrings(open.map((record) => record.id).filter(Boolean)),
176
+ evidenceIds: uniqueStrings(records.flatMap((record) => record.evidenceIds ?? []).filter(Boolean))
177
+ };
178
+ }
179
+
180
+ function summarizeParadigmImpact(records) {
181
+ const lowering = records.filter((record) => record.paradigmGroup === 'loweringRecords');
182
+ const semantic = records.filter((record) => record.paradigmGroup !== 'loweringRecords');
183
+ return {
184
+ semanticIds: uniqueStrings(semantic.map((record) => record.id).filter(Boolean)),
185
+ loweringIds: uniqueStrings(lowering.map((record) => record.id).filter(Boolean)),
186
+ evidenceIds: uniqueStrings(records.flatMap((record) => record.evidenceIds ?? []).filter(Boolean)),
187
+ lossIds: uniqueStrings(records.flatMap((record) => record.lossIds ?? []).filter(Boolean))
188
+ };
189
+ }
190
+
191
+ function proofReadinessStatus(status) {
192
+ const value = String(status ?? 'unknown').trim().toLowerCase().replace(/[\s_]+/g, '-');
193
+ if (['accepted', 'checked', 'ok', 'passed', 'proved', 'qed', 'success', 'succeeded', 'valid', 'verified', 'discharged'].includes(value)) return 'discharged';
194
+ if (['counterexample', 'cex', 'error', 'failed', 'falsified', 'invalid', 'rejected', 'violated'].includes(value)) return 'failed';
195
+ if (['obsolete', 'outdated', 'stale'].includes(value)) return 'stale';
196
+ if (['admit', 'admitted', 'assume', 'assumed', 'axiom', 'trusted', 'unchecked'].includes(value)) return 'assumed';
197
+ if (['solver-required', 'prover-required', 'tool-required', 'external-tool-required'].includes(value)) return 'external-tool-required';
198
+ if (['pending', 'todo', 'unproved', 'unverified'].includes(value)) return 'pending';
199
+ if (value === 'open') return 'open';
200
+ return 'unknown';
201
+ }
202
+
203
+ function symbolsForRegion(region, symbolsByRegion) {
204
+ const symbols = [...(symbolsByRegion.get(region.id) ?? [])];
205
+ if (symbols.length === 0 && region.symbolId) {
206
+ symbols.push({ id: region.symbolId, name: region.symbolName, ownershipRegionId: region.id, readiness: 'needs-review' });
207
+ }
208
+ return symbols;
209
+ }
210
+
211
+ function relationTouchesSymbols(relation, symbols, allSymbolIds) {
212
+ const ids = new Set(symbols.map((symbol) => symbol.id).filter(Boolean));
213
+ if (ids.has(relation.sourceId) || ids.has(relation.targetId)) return true;
214
+ return allSymbolIds.has(relation.sourceId) && allSymbolIds.has(relation.targetId) && ids.size === 0;
215
+ }
216
+
217
+ function sourceMapMappingsForSymbols(mappings, symbols) {
218
+ const ids = new Set(symbols.map((symbol) => symbol.id).filter(Boolean));
219
+ const mappingIds = new Set(symbols.map((symbol) => symbol.sourceMapMappingId).filter(Boolean));
220
+ return uniqueRecordsById((mappings ?? []).filter((mapping) => ids.has(mapping.semanticSymbolId) || mappingIds.has(mapping.id)));
221
+ }
222
+
223
+ function preservationRecordsForSymbols(records, symbols) {
224
+ const ids = new Set(symbols.map((symbol) => symbol.id).filter(Boolean));
225
+ const mappingIds = new Set(symbols.map((symbol) => symbol.sourceMapMappingId).filter(Boolean));
226
+ return uniqueRecordsById((records ?? []).filter((record) => ids.has(record.semanticSymbolId) || mappingIds.has(record.sourceMapMappingId)));
227
+ }
228
+
229
+ function relatedEvidenceIds(input) {
230
+ return uniqueStrings([
231
+ ...input.proofRecords.flatMap((record) => record.evidenceIds ?? []),
232
+ ...input.paradigmRecords.flatMap((record) => record.evidenceIds ?? []),
233
+ ...input.sourcePreservationRecords.flatMap((record) => record.evidenceIds ?? []),
234
+ ...(input.evidence.failed ?? [])
235
+ ].filter(Boolean));
236
+ }
237
+
238
+ function semanticImpactVerificationPlan(input) {
239
+ const plan = [];
240
+ if (input.dependencyPredicates.length > 0) plan.push({ kind: 'dependency-review', reason: 'region touches semantic dependency relations', required: true });
241
+ if (input.sourcePreservationRecords.some((record) => record.level === 'estimated' || record.level === 'blocked')) plan.push({ kind: 'source-map-review', reason: 'source preservation is not exact', required: true });
242
+ if (input.proof.failedObligationIds.length > 0) plan.push({ kind: 'reject-or-reprove', reason: 'failed proof obligations are linked', required: true });
243
+ else if (input.proof.openObligationIds.length > 0) plan.push({ kind: 'proof-review', reason: 'open proof obligations are linked', required: true });
244
+ if (input.paradigm.loweringIds.length > 0) plan.push({ kind: 'semantic-lowering-review', reason: 'lowering records are linked to this region', required: input.readiness !== 'ready' });
245
+ if (input.patchHints.length > 0) plan.push({ kind: 'patch-admission', reason: 'patch hints exist for this region', required: input.readiness !== 'ready' });
246
+ if (input.evidenceIds.length === 0) plan.push({ kind: 'evidence-review', reason: 'no region-specific evidence ids were linked', required: input.readiness !== 'ready' });
247
+ return plan;
248
+ }
249
+
250
+ function semanticImpactRisk(input) {
251
+ if (input.readiness === 'blocked' || input.proof.failedObligationIds.length > 0 || input.sourcePreservationRecords.some((record) => record.level === 'blocked')) return 'high';
252
+ if (input.failedEvidenceIds.length > 0 || input.proof.openObligationIds.length > 0) return 'high';
253
+ if (input.relationCount > 0 || input.verificationPlan.some((step) => step.required)) return 'medium';
254
+ if (input.sourcePreservationRecords.some((record) => record.level === 'estimated')) return 'medium';
255
+ return 'low';
256
+ }
257
+
258
+ function semanticImpactConflictKeys(region, symbolIds, relations, predicates) {
259
+ return uniqueStrings([
260
+ region.id ? `region:${region.id}` : undefined,
261
+ region.key ? `ownership:${region.key}` : undefined,
262
+ region.sourcePath ? `source:${region.sourcePath}` : undefined,
263
+ ...symbolIds.map((id) => `symbol:${id}`),
264
+ ...relations.map((relation) => `dependency:${relation.sourceId}:${semanticDependencyPredicateKey(relation.predicate)}:${relation.targetId}`),
265
+ ...predicates.map((predicate) => `predicate:${predicate}`)
266
+ ].filter(Boolean));
267
+ }
268
+
269
+ function semanticImpactConfidence(region, mappings, sourcePreservationRecords) {
270
+ if (sourcePreservationRecords.some((record) => record.level === 'exact')) return 'source-exact';
271
+ if ((mappings ?? []).length > 0 || region.precision === 'declaration') return 'source-addressed';
272
+ if (region.precision === 'line' || region.precision === 'estimated') return 'estimated-source-region';
273
+ return 'review-required';
274
+ }
275
+
276
+ function affectedSymbolIds(relations, ownSymbolIds, allSymbolIds) {
277
+ const own = new Set(ownSymbolIds);
278
+ return uniqueStrings(relations
279
+ .flatMap((relation) => [relation.sourceId, relation.targetId])
280
+ .filter((id) => id && allSymbolIds.has(id) && !own.has(id)));
281
+ }
282
+
283
+ function summarizeSemanticImpactRecords(records) {
284
+ const verificationPlans = records.flatMap((record) => record.verificationPlan.map((step) => step.kind));
285
+ return {
286
+ total: records.length,
287
+ byRisk: countBy(records.map((record) => record.risk)),
288
+ byReadiness: countBy(records.map((record) => record.readiness)),
289
+ conflictKeys: uniqueStrings(records.flatMap((record) => record.conflictKeys)),
290
+ dependencyRelations: uniqueStrings(records.flatMap((record) => record.dependencyRelationIds)).length,
291
+ affectedSymbols: uniqueStrings(records.flatMap((record) => record.affectedSymbolIds)).length,
292
+ sourceMapMappings: uniqueStrings(records.flatMap((record) => record.sourceMapMappingIds)).length,
293
+ sourcePreservationRecords: uniqueStrings(records.flatMap((record) => record.sourcePreservationRecordIds)).length,
294
+ patchHints: uniqueStrings(records.flatMap((record) => record.patchHintIds)).length,
295
+ proofObligations: uniqueStrings(records.flatMap((record) => record.proofObligationIds)).length,
296
+ openProofObligations: uniqueStrings(records.flatMap((record) => record.openProofObligationIds)).length,
297
+ failedProofObligations: uniqueStrings(records.flatMap((record) => record.failedProofObligationIds)).length,
298
+ paradigmSemantics: uniqueStrings(records.flatMap((record) => record.paradigmSemanticIds)).length,
299
+ loweringRecords: uniqueStrings(records.flatMap((record) => record.loweringRecordIds)).length,
300
+ evidenceIds: uniqueStrings(records.flatMap((record) => record.evidenceIds)).length,
301
+ verificationPlans: uniqueStrings(verificationPlans),
302
+ requiredVerificationSteps: records.flatMap((record) => record.verificationPlan).filter((step) => step.required).length
303
+ };
304
+ }
305
+
306
+ function groupBy(values, keyFn) {
307
+ const grouped = new Map();
308
+ for (const value of values ?? []) {
309
+ const key = keyFn(value);
310
+ if (!key) continue;
311
+ if (!grouped.has(key)) grouped.set(key, []);
312
+ grouped.get(key).push(value);
313
+ }
314
+ return grouped;
315
+ }
316
+
317
+ export { createSemanticImportImpact, summarizeSemanticImpactRecords };
@@ -14,6 +14,7 @@ export interface SemanticImportSidecarProofAdmissionSummary {
14
14
  readonly discharged: number;
15
15
  readonly pending?: number;
16
16
  readonly failed: number;
17
+ readonly stale?: number;
17
18
  readonly assumed?: number;
18
19
  readonly externalToolRequired?: number;
19
20
  readonly open: number;
@@ -24,6 +25,9 @@ export interface SemanticImportSidecarProofAdmissionSummary {
24
25
 
25
26
  export interface SemanticImportSidecarQuality {
26
27
  readonly schema: 'frontier.lang.semanticSidecarQuality.v1';
28
+ readonly expected: boolean;
29
+ readonly expectedSatisfied: boolean;
30
+ readonly expectedMissingReasonCodes: readonly string[];
27
31
  readonly selected: boolean;
28
32
  readonly eligible: boolean;
29
33
  readonly imported: boolean;
@@ -40,6 +44,9 @@ export interface SemanticImportSidecarQuality {
40
44
 
41
45
  export interface SemanticImportSidecarAdmission {
42
46
  readonly schema: 'frontier.lang.semanticSidecarAdmission.v1';
47
+ readonly expected: boolean;
48
+ readonly expectedSatisfied: boolean;
49
+ readonly expectedMissingReasonCodes: readonly string[];
43
50
  readonly selected: boolean;
44
51
  readonly eligible: boolean;
45
52
  readonly imported: boolean;
@@ -6,6 +6,7 @@ import type {
6
6
  SourceSpan
7
7
  } from '@shapeshift-labs/frontier-lang-kernel';
8
8
  import type { NativeImportRegionTaxonomyKind, NativeImportTaxonomyKind } from './index.js';
9
+ import type { SemanticImportImpactSummary } from './semantic-import-impact-types.js';
9
10
  import type { SemanticImportSidecarAdmission, SemanticImportSidecarQuality } from './semantic-import-sidecar-admission-types.js';
10
11
  import type { SemanticMergeConflictClass, SemanticMergeConflictSummary } from './declarations/semantic-merge-conflicts.js';
11
12
 
@@ -237,6 +238,7 @@ export interface SemanticImportSidecar {
237
238
  readonly proofSpec: SemanticImportSidecarProofSpecSummary;
238
239
  readonly paradigmSemantics: SemanticImportSidecarParadigmSemanticsSummary;
239
240
  readonly dependencies: SemanticImportDependencySummary;
241
+ readonly semanticImpact: SemanticImportImpactSummary;
240
242
  readonly patchHints: readonly SemanticImportPatchHint[];
241
243
  readonly quality: SemanticImportSidecarQuality;
242
244
  readonly admission: SemanticImportSidecarAdmission;
@@ -280,6 +282,9 @@ export interface SemanticImportSidecar {
280
282
  readonly paradigmSemanticsLoweringRecords: number;
281
283
  readonly dependencyRelations: number;
282
284
  readonly dependencyPredicates: readonly string[];
285
+ readonly semanticImpactRecords: number;
286
+ readonly semanticImpactHighRiskRecords: number;
287
+ readonly semanticImpactRequiredVerificationSteps: number;
283
288
  readonly patchHints: number;
284
289
  readonly evidenceWarnings: number;
285
290
  readonly readiness: SemanticMergeReadiness;
@@ -293,5 +298,7 @@ export interface SemanticImportSidecarOptions {
293
298
  readonly generatedAt?: number;
294
299
  readonly regionPrefix?: string;
295
300
  readonly targetPath?: string;
301
+ readonly expected?: boolean;
302
+ readonly semanticImportExpected?: boolean;
296
303
  readonly metadata?: Record<string, unknown>;
297
304
  }
@@ -46,9 +46,9 @@ function createKernelSourcePreservationRecords(input) {
46
46
  }
47
47
 
48
48
  for (const sourceMap of input.sourceMaps ?? []) {
49
- for (const mapping of sourceMap.mappings ?? []) {
49
+ for (const [index, mapping] of (sourceMap.mappings ?? []).entries()) {
50
50
  records.push(explainSourcePreservation({
51
- id: `source_preservation_${idFragment(sourceMap.id)}_${idFragment(mapping.id)}`,
51
+ id: `source_preservation_${idFragment(sourceMap.id)}_${index + 1}_${idFragment(mapping.id)}`,
52
52
  sourceMap,
53
53
  mapping,
54
54
  level: mapping.preservation,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.59",
3
+ "version": "0.2.61",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",