@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 +1 -0
- package/dist/declarations/semantic-impact.d.ts +55 -0
- package/dist/declarations/semantic-sidecar-admission.d.ts +1 -0
- package/dist/declarations/semantic-sidecar.d.ts +4 -8
- package/dist/index.d.ts +1 -0
- package/dist/internal/index-impl/createLightweightNativeImport.js +12 -0
- package/dist/internal/index-impl/createLightweightSemanticLayers.js +1 -1
- package/dist/internal/index-impl/createSemanticImportSidecar.js +21 -1
- package/dist/internal/index-impl/createSemanticImportSidecarAdmission.js +14 -1
- package/dist/lightweight-dependency-language.js +223 -0
- package/dist/lightweight-dependency-relations.js +18 -151
- package/dist/semantic-import-impact-types.d.ts +67 -0
- package/dist/semantic-import-impact.js +317 -0
- package/dist/semantic-import-sidecar-admission-types.d.ts +7 -0
- package/dist/semantic-import-sidecar-types.d.ts +7 -0
- package/dist/semantic-import-source-preservation.js +2 -2
- package/package.json +1 -1
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.
|
|
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.
|
|
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 (
|
|
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
|
|
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
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
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 (!
|
|
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
|
|
67
|
+
const state = { inBlockComment: false };
|
|
145
68
|
for (let lineNumber = scan.startLine; lineNumber <= scan.endLine; lineNumber += 1) {
|
|
146
|
-
const scanLine =
|
|
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 (
|
|
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