@shapeshift-labs/frontier-lang-compiler 0.2.99 → 0.2.101

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.
Files changed (57) hide show
  1. package/dist/declarations/bidirectional-target-change-evidence.d.ts +299 -0
  2. package/dist/declarations/bidirectional-target-change.d.ts +19 -120
  3. package/dist/declarations/native-project-admission.d.ts +43 -22
  4. package/dist/declarations/semantic-edit-bundle.d.ts +13 -1
  5. package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +24 -0
  6. package/dist/declarations/semantic-edit-script.d.ts +53 -51
  7. package/dist/declarations/semantic-lineage.d.ts +62 -51
  8. package/dist/declarations/semantic-merge-candidates.d.ts +39 -0
  9. package/dist/declarations/semantic-patch-bundle.d.ts +13 -0
  10. package/dist/declarations/semantic-sidecar-admission.d.ts +14 -0
  11. package/dist/declarations/semantic-sidecar.d.ts +12 -14
  12. package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +200 -0
  13. package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +62 -17
  14. package/dist/internal/index-impl/createNativeSourcePreservation.js +16 -1
  15. package/dist/internal/index-impl/createProjectImportAdmissionRecord.js +151 -1
  16. package/dist/internal/index-impl/createSemanticImportSidecar.js +5 -0
  17. package/dist/internal/index-impl/createSemanticImportSidecarAdmission.js +29 -11
  18. package/dist/internal/index-impl/declarationRecord.js +2 -2
  19. package/dist/internal/index-impl/inferSemanticLineageEvents.js +8 -0
  20. package/dist/internal/index-impl/nativeChangeProjectionEndpoint.js +56 -16
  21. package/dist/internal/index-impl/projectImportAdmissionMergeScore.js +26 -74
  22. package/dist/internal/index-impl/projectImportAdmissionProjectionCoverage.js +74 -0
  23. package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +92 -74
  24. package/dist/internal/index-impl/replaySemanticEditProjection.js +114 -40
  25. package/dist/internal/index-impl/semanticEditBundleAdmission.js +95 -12
  26. package/dist/internal/index-impl/semanticEditBundleIndex.js +16 -10
  27. package/dist/internal/index-impl/semanticEditInsertionAnchors.js +8 -5
  28. package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +167 -0
  29. package/dist/internal/index-impl/semanticEditSourceRanges.js +283 -0
  30. package/dist/internal/index-impl/semanticHistoryLineageResolution.js +56 -3
  31. package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +2 -2
  32. package/dist/internal/index-impl/semanticLineageHashEvidence.js +97 -0
  33. package/dist/internal/index-impl/semanticLineageInferenceMatching.js +158 -13
  34. package/dist/internal/index-impl/semanticLineageResolutionRecords.js +46 -2
  35. package/dist/internal/index-impl/semanticMergeCandidateRecords.js +22 -2
  36. package/dist/internal/index-impl/semanticMergeCandidateScoreFacets.js +221 -0
  37. package/dist/internal/index-impl/semanticPatchBundleAdmission.js +122 -20
  38. package/dist/internal/index-impl/semanticPatchBundleLineageLinks.js +199 -0
  39. package/dist/internal/index-impl/semanticPatchBundleOverlaps.js +29 -3
  40. package/dist/internal/index-impl/semanticPatchBundleRecords.js +28 -104
  41. package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +127 -0
  42. package/dist/internal/index-impl/sourcePreservationFromProjectionContext.js +9 -2
  43. package/dist/internal/index-impl/sourceTextForSpan.js +4 -9
  44. package/dist/lightweight-dependency-relations.js +113 -7
  45. package/dist/native-import-language-profiles.js +10 -2
  46. package/dist/native-import-utils.js +15 -1
  47. package/dist/native-region-scanner-js-helpers.js +68 -18
  48. package/dist/native-region-scanner-js-imports.js +7 -0
  49. package/dist/native-region-scanner-js.js +16 -8
  50. package/dist/native-region-scanner.js +2 -1
  51. package/dist/semantic-import-regions.js +8 -6
  52. package/dist/semantic-import-sidecar-admission-types.d.ts +14 -0
  53. package/dist/semantic-import-sidecar-entry.js +151 -7
  54. package/dist/semantic-import-sidecar-types.d.ts +18 -13
  55. package/dist/semantic-import-source-preservation-utils.js +55 -0
  56. package/dist/semantic-import-source-preservation.js +98 -3
  57. package/package.json +1 -1
@@ -22,7 +22,8 @@ export function createProjectImportAdmissionRecord(projectResult,options={}){
22
22
  const importSummaries=projectAdmissionImports(imports,contract?.sources??[],mergeCandidates);
23
23
  const languages=admissionLanguages(importSummaries);
24
24
  const semanticEvidence=admissionSemanticEvidence(projectResult,imports,importSummaries);
25
- const sourcePreservation=admissionSourcePreservation(importSummaries,contract);
25
+ const sourceStaleness=admissionSourceStaleness(imports,importSummaries,contract);
26
+ const sourcePreservation=admissionSourcePreservationWithStaleness(admissionSourcePreservation(importSummaries,contract),sourceStaleness);
26
27
  const ownership=admissionOwnership(contract,mergeCandidates);
27
28
  const mergeCandidateRisk=admissionMergeCandidates(projectResult,imports,mergeCandidates,lossSummary);
28
29
  const readiness=maxSemanticMergeReadiness(
@@ -54,6 +55,7 @@ export function createProjectImportAdmissionRecord(projectResult,options={}){
54
55
  readiness,
55
56
  sourceCount:imports.length,
56
57
  semanticEvidence,
58
+ sourceStaleness,
57
59
  sourcePreservation,
58
60
  ownership,
59
61
  mergeCandidateRisk,
@@ -71,6 +73,7 @@ export function createProjectImportAdmissionRecord(projectResult,options={}){
71
73
  sourceCount:imports.length,
72
74
  languages,
73
75
  semanticEvidence,
76
+ sourceStaleness,
74
77
  sourcePreservation,
75
78
  ownership,
76
79
  mergeCandidates:mergeCandidateRisk,
@@ -89,3 +92,150 @@ export function createProjectImportAdmissionRecord(projectResult,options={}){
89
92
  }
90
93
  };
91
94
  }
95
+
96
+ function admissionSourceStaleness(imports,importSummaries,contract){
97
+ const total=Math.max(imports.length,importSummaries.length,contract?.sources?.length??0);
98
+ const records=[];
99
+ for(let index=0;index<total;index+=1){
100
+ const record=sourceStalenessRecord(imports[index],importSummaries[index],contract?.sources?.[index]);
101
+ if(record.reasonCodes.length) records.push(record);
102
+ }
103
+ const staleRecords=records.filter((record)=>record.staleByContentHash||record.staleByBaseHash);
104
+ const contentHashRecords=records.filter((record)=>record.staleByContentHash);
105
+ const baseHashRecords=records.filter((record)=>record.staleByBaseHash);
106
+ const dirtyWorkspaceRecords=records.filter((record)=>record.dirtyWorkspace);
107
+ const unverifiedRecords=records.filter((record)=>record.unverifiedSourceHash);
108
+ return {
109
+ total,
110
+ stale:staleRecords.length,
111
+ contentHashStale:contentHashRecords.length,
112
+ baseHashStale:baseHashRecords.length,
113
+ dirtyWorkspace:dirtyWorkspaceRecords.length,
114
+ unverified:unverifiedRecords.length,
115
+ staleSourcePaths:sourcePaths(staleRecords),
116
+ contentHashStaleSourcePaths:sourcePaths(contentHashRecords),
117
+ baseHashStaleSourcePaths:sourcePaths(baseHashRecords),
118
+ dirtyWorkspaceSourcePaths:sourcePaths(dirtyWorkspaceRecords),
119
+ unverifiedSourcePaths:sourcePaths(unverifiedRecords),
120
+ records
121
+ };
122
+ }
123
+
124
+ function sourceStalenessRecord(imported,summary,source){
125
+ const nativeSource=imported?.nativeSource;
126
+ const nativeAst=imported?.nativeAst??nativeSource?.ast;
127
+ const preservation=imported?.metadata?.sourcePreservation
128
+ ??nativeSource?.metadata?.sourcePreservation
129
+ ??nativeAst?.metadata?.sourcePreservation
130
+ ??imported?.universalAst?.metadata?.sourcePreservation;
131
+ const metadata=[
132
+ imported?.metadata,
133
+ nativeSource?.metadata,
134
+ nativeAst?.metadata,
135
+ preservation?.metadata,
136
+ imported?.universalAst?.metadata,
137
+ imported?.patch?.metadata
138
+ ].filter((entry)=>entry&&typeof entry==='object');
139
+ const lossReasons=(imported?.losses??nativeAst?.losses??[]).flatMap((loss)=>[
140
+ loss?.reason,
141
+ loss?.metadata?.reason,
142
+ ...(Array.isArray(loss?.reasonCodes)?loss.reasonCodes:[]),
143
+ ...(Array.isArray(loss?.metadata?.reasonCodes)?loss.metadata.reasonCodes:[])
144
+ ]);
145
+ const observedReasonCodes=uniqueStrings([
146
+ ...metadata.flatMap((entry)=>[
147
+ entry.reason,
148
+ ...(Array.isArray(entry.reasonCodes)?entry.reasonCodes:[]),
149
+ ...(Array.isArray(entry.reasons)?entry.reasons:[])
150
+ ]),
151
+ ...lossReasons
152
+ ]);
153
+ const sourcePath=firstString(summary?.sourcePath,source?.sourcePath,imported?.sourcePath,nativeSource?.sourcePath,nativeAst?.sourcePath,preservation?.sourcePath);
154
+ const sourceHash=firstString(summary?.sourceHash,source?.sourceHash,imported?.sourceHash,nativeSource?.sourceHash,nativeAst?.sourceHash,preservation?.sourceHash);
155
+ const declaredSourceHash=firstMetadataString(metadata,'declaredSourceHash');
156
+ const contentHash=firstMetadataString(metadata,'currentContentHash','actualContentHash','contentHash')??sourceHash;
157
+ const declaredContentHash=firstMetadataString(metadata,'declaredContentHash','expectedContentHash','expectedSourceHash')??declaredSourceHash;
158
+ const sourceHashMismatch=Boolean(declaredSourceHash&&sourceHash&&declaredSourceHash!==sourceHash);
159
+ const contentHashMismatch=Boolean(declaredContentHash&&contentHash&&declaredContentHash!==contentHash);
160
+ const preservationHashMismatch=Boolean(preservation?.sourceHash&&sourceHash&&preservation.sourceHash!==sourceHash);
161
+ const sourceHashVerifiedFalse=metadata.some((entry)=>entry.sourceHashVerified===false);
162
+ const staleByContentHash=sourceHashMismatch
163
+ ||contentHashMismatch
164
+ ||preservationHashMismatch
165
+ ||(sourceHashVerifiedFalse&&Boolean(declaredSourceHash||declaredContentHash));
166
+ const baseHash=firstString(imported?.baseHash,imported?.patch?.baseHash,firstMetadataString(metadata,'baseHash'));
167
+ const expectedBaseHash=firstMetadataString(metadata,'expectedBaseHash','declaredBaseHash','sourceBaseHash');
168
+ const currentBaseHash=firstMetadataString(metadata,'currentBaseHash','actualBaseHash','headBaseHash','workspaceBaseHash');
169
+ const staleByBaseHash=Boolean(
170
+ expectedBaseHash&&currentBaseHash&&expectedBaseHash!==currentBaseHash
171
+ )||metadata.some((entry)=>entry.baseHashVerified===false)||observedReasonCodes.includes('base-hash-mismatch');
172
+ const dirtyWorkspace=metadata.some((entry)=>
173
+ entry.dirtyWorkspace===true
174
+ ||entry.workspaceDirty===true
175
+ ||entry.worktreeDirty===true
176
+ ||entry.dirtyWorktree===true
177
+ )||observedReasonCodes.some((reason)=>reason==='dirty-workspace'||reason==='workspace-dirty'||reason==='dirty-worktree');
178
+ const unverifiedSourceHash=sourceHashVerifiedFalse&&!staleByContentHash;
179
+ const reasonCodes=uniqueStrings([
180
+ ...(sourceHashMismatch?['source-hash-mismatch']:[]),
181
+ ...(contentHashMismatch?['content-hash-mismatch']:[]),
182
+ ...(preservationHashMismatch?['source-preservation-hash-mismatch']:[]),
183
+ ...(staleByContentHash&&sourceHashVerifiedFalse?['source-hash-unverified']:[]),
184
+ ...(staleByBaseHash?['base-hash-mismatch']:[]),
185
+ ...(dirtyWorkspace?['dirty-workspace']:[]),
186
+ ...(unverifiedSourceHash?['source-hash-unverified']:[])
187
+ ]);
188
+ return {
189
+ ...(sourcePath?{sourcePath}:{}),
190
+ ...(sourceHash?{sourceHash}:{}),
191
+ ...(declaredSourceHash?{declaredSourceHash}:{}),
192
+ ...(contentHash?{contentHash}:{}),
193
+ ...(declaredContentHash?{declaredContentHash}:{}),
194
+ ...(baseHash?{baseHash}:{}),
195
+ ...(expectedBaseHash?{expectedBaseHash}:{}),
196
+ ...(currentBaseHash?{currentBaseHash}:{}),
197
+ staleByContentHash,
198
+ staleByBaseHash,
199
+ dirtyWorkspace,
200
+ unverifiedSourceHash,
201
+ reasonCodes
202
+ };
203
+ }
204
+
205
+ function admissionSourcePreservationWithStaleness(sourcePreservation,sourceStaleness){
206
+ const stale=sourceStaleness.stale;
207
+ const quality=stale>0?'stale':sourcePreservation.quality==='stale'
208
+ ? sourcePreservation.missing>0?'missing':sourcePreservation.lossy>0||sourcePreservation.truncated?'lossy':sourcePreservation.total===0?'empty':'exact'
209
+ : sourcePreservation.quality;
210
+ return {
211
+ ...sourcePreservation,
212
+ quality,
213
+ stale,
214
+ staleSourcePaths:sourceStaleness.staleSourcePaths,
215
+ contentHashStale:sourceStaleness.contentHashStale,
216
+ baseHashStale:sourceStaleness.baseHashStale,
217
+ dirtyWorkspace:sourceStaleness.dirtyWorkspace,
218
+ contentHashStaleSourcePaths:sourceStaleness.contentHashStaleSourcePaths,
219
+ baseHashStaleSourcePaths:sourceStaleness.baseHashStaleSourcePaths,
220
+ dirtyWorkspaceSourcePaths:sourceStaleness.dirtyWorkspaceSourcePaths
221
+ };
222
+ }
223
+
224
+ function sourcePaths(records){
225
+ return uniqueStrings(records.map((record)=>record.sourcePath).filter(Boolean));
226
+ }
227
+
228
+ function firstMetadataString(metadata,...keys){
229
+ for(const key of keys){
230
+ const value=firstString(...metadata.map((entry)=>entry?.[key]));
231
+ if(value) return value;
232
+ }
233
+ return undefined;
234
+ }
235
+
236
+ function firstString(...values){
237
+ for(const value of values){
238
+ if(value!==undefined&&value!==null&&String(value)) return String(value);
239
+ }
240
+ return undefined;
241
+ }
@@ -29,6 +29,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
29
29
  const patchHints = ownershipRegions.map((region) => semanticPatchHintForRegion(region, readiness, options));
30
30
  const quality = createSemanticImportSidecarQuality({
31
31
  expected: options.expected === true || options.semanticImportExpected === true,
32
+ expectedEmpty: options.expectedEmpty === true || options.semanticImportExpectedEmpty === true,
32
33
  importEntries,
33
34
  symbols,
34
35
  ownershipRegions,
@@ -130,8 +131,12 @@ export function createSemanticImportSidecar(importResult, options = {}) {
130
131
  patchHints: patchHints.length,
131
132
  evidenceWarnings: quality.emptyEvidenceWarnings.length,
132
133
  semanticImportExpected: quality.expected,
134
+ semanticImportExpectedEmpty: quality.expectedEmpty,
133
135
  semanticImportExpectedSatisfied: quality.expectedSatisfied,
134
136
  semanticImportExpectedMissingReasonCodes: quality.expectedMissingReasonCodes,
137
+ semanticImportRecordClassification: quality.record.classification,
138
+ semanticImportRecordReasonCode: quality.record.reasonCode,
139
+ semanticImportRecordAction: quality.record.action,
135
140
  readiness,
136
141
  emptySemanticIndex: symbols.length === 0
137
142
  },
@@ -1,3 +1,5 @@
1
+ import { semanticImportSidecarQualityRecord } from '../../semantic-import-sidecar-entry.js';
2
+
1
3
  function sidecarSourcePaths(importEntries) {
2
4
  return [...new Set(importEntries.map((entry) => entry.sourcePath).filter(Boolean))];
3
5
  }
@@ -15,10 +17,19 @@ function sidecarQualityWarning(code, message, action, sourcePaths) {
15
17
  export function createSemanticImportSidecarQuality(input) {
16
18
  const { importEntries, symbols, ownershipRegions, patchHints, proofSpec, evidence, readiness } = input;
17
19
  const expected = input.expected === true;
20
+ const expectedEmpty = input.expectedEmpty === true || input.semanticImportExpectedEmpty === true;
18
21
  const sourcePaths = sidecarSourcePaths(importEntries);
19
22
  const importCount = importEntries.length;
23
+ const record = semanticImportSidecarQualityRecord({
24
+ expected,
25
+ expectedEmpty,
26
+ importCount,
27
+ symbolCount: symbols.length,
28
+ sourcePaths
29
+ });
30
+ const expectedEmptySatisfied = record.classification === 'expected-empty';
20
31
  const warnings = [];
21
- if (expected && importCount === 0) warnings.push(sidecarQualityWarning(
32
+ if ((expected || expectedEmpty) && importCount === 0) warnings.push(sidecarQualityWarning(
22
33
  'expected-semantic-import-missing',
23
34
  'Semantic import was expected but no import entries were selected.',
24
35
  'check-semantic-import-include-globs-and-workspace-paths',
@@ -30,31 +41,31 @@ export function createSemanticImportSidecarQuality(input) {
30
41
  'run-native-import',
31
42
  sourcePaths
32
43
  ));
33
- if (expected && importCount > 0 && symbols.length === 0) warnings.push(sidecarQualityWarning(
44
+ if (!expectedEmptySatisfied && expected && importCount > 0 && symbols.length === 0) warnings.push(sidecarQualityWarning(
34
45
  'expected-semantic-import-empty',
35
46
  'Semantic import was expected but selected imports produced zero semantic symbols.',
36
47
  'rerun-importer-with-semantic-source-selection',
37
48
  sourcePaths
38
49
  ));
39
- if (importCount > 0 && symbols.length === 0) warnings.push(sidecarQualityWarning(
50
+ if (!expectedEmptySatisfied && importCount > 0 && symbols.length === 0) warnings.push(sidecarQualityWarning(
40
51
  'empty-semantic-index',
41
52
  'Semantic sidecar has import entries but no semantic symbols.',
42
53
  'rerun-importer-with-semantic-index',
43
54
  sourcePaths
44
55
  ));
45
- if (importCount > 0 && ownershipRegions.length === 0) warnings.push(sidecarQualityWarning(
56
+ if (!expectedEmptySatisfied && importCount > 0 && ownershipRegions.length === 0) warnings.push(sidecarQualityWarning(
46
57
  'missing-ownership-regions',
47
58
  'Semantic sidecar has no ownership regions for safe merge ownership.',
48
59
  'rerun-sidecar-generation-with-ownership-regions',
49
60
  sourcePaths
50
61
  ));
51
- if (importCount > 0 && patchHints.length === 0) warnings.push(sidecarQualityWarning(
62
+ if (!expectedEmptySatisfied && importCount > 0 && patchHints.length === 0) warnings.push(sidecarQualityWarning(
52
63
  'missing-patch-hints',
53
64
  'Semantic sidecar has no patch hints.',
54
65
  'generate-semantic-patch-hints',
55
66
  sourcePaths
56
67
  ));
57
- if (importCount > 0 && evidence.length === 0) warnings.push(sidecarQualityWarning(
68
+ if (!expectedEmptySatisfied && importCount > 0 && evidence.length === 0) warnings.push(sidecarQualityWarning(
58
69
  'empty-evidence',
59
70
  'Semantic sidecar has no evidence records.',
60
71
  'attach-semantic-import-evidence',
@@ -116,16 +127,18 @@ export function createSemanticImportSidecarQuality(input) {
116
127
  warning.code === 'missing-ownership-regions' ||
117
128
  warning.code === 'missing-patch-hints'
118
129
  ));
119
- const expectedMissingReasonCodes = expected
120
- ? emptyEvidenceWarnings.map((warning) => warning.code)
121
- : [];
122
- const expectedSatisfied = !expected || (
130
+ const expectedSatisfied = expectedEmpty
131
+ ? expectedEmptySatisfied
132
+ : !expected || (
123
133
  importCount > 0 &&
124
134
  symbols.length > 0 &&
125
135
  ownershipRegions.length > 0 &&
126
136
  patchHints.length > 0 &&
127
137
  evidence.length > 0
128
138
  );
139
+ const expectedMissingReasonCodes = (expected || expectedEmpty) && !expectedSatisfied
140
+ ? emptyEvidenceWarnings.map((warning) => warning.code)
141
+ : [];
129
142
  const proofSummary = {
130
143
  total: proofSpec.total,
131
144
  obligations: proofSpec.obligations,
@@ -143,12 +156,14 @@ export function createSemanticImportSidecarQuality(input) {
143
156
  return {
144
157
  schema: 'frontier.lang.semanticSidecarQuality.v1',
145
158
  expected,
159
+ expectedEmpty,
146
160
  expectedSatisfied,
147
161
  expectedMissingReasonCodes,
148
162
  selected: importCount > 0,
149
- eligible: importCount > 0 && emptyEvidenceWarnings.length === 0 && proofSpec.failed === 0 && readiness !== 'blocked',
163
+ eligible: record.classification === 'useful' && emptyEvidenceWarnings.length === 0 && proofSpec.failed === 0 && readiness !== 'blocked',
150
164
  imported: importCount > 0,
151
165
  importCount,
166
+ record,
152
167
  symbolCount: symbols.length,
153
168
  ownershipRegionCount: ownershipRegions.length,
154
169
  patchHintCount: patchHints.length,
@@ -164,6 +179,7 @@ export function createSemanticImportSidecarAdmission(quality, readiness) {
164
179
  return {
165
180
  schema: 'frontier.lang.semanticSidecarAdmission.v1',
166
181
  expected: quality.expected,
182
+ expectedEmpty: quality.expectedEmpty,
167
183
  expectedSatisfied: quality.expectedSatisfied,
168
184
  expectedMissingReasonCodes: quality.expectedMissingReasonCodes,
169
185
  selected: quality.selected,
@@ -172,6 +188,7 @@ export function createSemanticImportSidecarAdmission(quality, readiness) {
172
188
  importCount: quality.importCount,
173
189
  readiness,
174
190
  action: sidecarAdmissionAction(quality, readiness),
191
+ record: quality.record,
175
192
  counts: {
176
193
  symbols: quality.symbolCount,
177
194
  ownershipRegions: quality.ownershipRegionCount,
@@ -191,6 +208,7 @@ function sidecarAdmissionAction(quality, readiness) {
191
208
  if (!quality.imported) return 'reject-missing-imports';
192
209
  if (readiness === 'blocked') return 'reject-blocked';
193
210
  if (quality.proofSummary.failed > 0) return 'reject-failed-proof';
211
+ if (quality.record?.classification === 'expected-empty') return 'skip-expected-empty';
194
212
  if (quality.emptyEvidenceWarnings.length > 0) return 'reject-empty-evidence';
195
213
  if (!quality.eligible) return 'reject-quality';
196
214
  if (sidecarProofReviewObligations(quality.proofSummary) > 0) return 'review-proof-obligations';
@@ -1,11 +1,11 @@
1
- import{idFragment}from'../../native-import-utils.js';
1
+ import{idFragment,caseSensitiveIdFragment}from'../../native-import-utils.js';
2
2
  import{nativeNodeId}from'./nativeNodeId.js';
3
3
  export function declarationRecord(input, nativeNodeId, name, symbolKind, role = 'definition') {
4
4
  return {
5
5
  name: String(name),
6
6
  symbolKind,
7
7
  role,
8
- symbolId: `symbol:${input.language}:${role === 'import' ? 'import:' : ''}${idFragment(name)}`,
8
+ symbolId: `symbol:${input.language}:${role === 'import' ? 'import:' : ''}${caseSensitiveIdFragment(name)}`,
9
9
  nativeNodeId
10
10
  };
11
11
  }
@@ -74,6 +74,8 @@ export function inferSemanticLineageEvents(input = {}, options = {}) {
74
74
  inferredEvents: events.length,
75
75
  moved: events.filter((event) => event.eventKind === 'moved').length,
76
76
  renamed: events.filter((event) => event.eventKind === 'renamed').length,
77
+ split: events.filter((event) => event.eventKind === 'split').length,
78
+ recreated: events.filter((event) => event.eventKind === 'recreated').length,
77
79
  deleted: deleted.length,
78
80
  ambiguous: candidates.ambiguous.length,
79
81
  unmatchedAdded: candidates.unmatchedAfter.length,
@@ -169,6 +171,10 @@ function lineageInferenceEvidence(input) {
169
171
  language: input.language,
170
172
  unchangedAnchors: input.exact.matched.length,
171
173
  inferredEvents: input.events.length,
174
+ moved: input.events.filter((event) => event.eventKind === 'moved').length,
175
+ renamed: input.events.filter((event) => event.eventKind === 'renamed').length,
176
+ split: input.events.filter((event) => event.eventKind === 'split').length,
177
+ recreated: input.events.filter((event) => event.eventKind === 'recreated').length,
172
178
  deleted: input.deleted.length,
173
179
  ambiguous: input.candidates.ambiguous.length,
174
180
  unmatchedAdded: input.candidates.unmatchedAfter.length,
@@ -188,6 +194,8 @@ function inferenceReadiness(input, options) {
188
194
  function inferenceReasons(input) {
189
195
  return uniqueStrings([
190
196
  input.events.length ? 'semantic-lineage-inferred' : undefined,
197
+ input.events.some((event) => event.eventKind === 'split') ? 'split-anchor-lineage-inferred' : undefined,
198
+ input.events.some((event) => event.eventKind === 'recreated') ? 'recreated-anchor-lineage-inferred' : undefined,
191
199
  input.deleted.length ? 'deleted-anchor-lineage-inferred' : undefined,
192
200
  input.added.length ? 'unmatched-added-anchor-review' : undefined,
193
201
  input.ambiguous.length ? 'ambiguous-lineage-candidates' : undefined,
@@ -1,4 +1,4 @@
1
- import{nativeChangeMappingTouchesRegion}from'./nativeChangeMappingTouchesRegion.js';import{nativeImportSourcePreservationRecord}from'./nativeImportSourcePreservationRecord.js';
1
+ import{uniqueStrings}from'../../native-import-utils.js';import{nativeChangeMappingTouchesRegion}from'./nativeChangeMappingTouchesRegion.js';import{nativeImportSourcePreservationRecord}from'./nativeImportSourcePreservationRecord.js';
2
2
  export function nativeChangeProjectionEndpoint(imported, sidecar, region, side) {
3
3
  if (!imported && !region) return undefined;
4
4
  const preservation = nativeImportSourcePreservationRecord(imported);
@@ -6,23 +6,63 @@ export function nativeChangeProjectionEndpoint(imported, sidecar, region, side)
6
6
  const regionMappings = sourceMaps
7
7
  .flatMap((sourceMap) => (sourceMap?.mappings ?? []).map((mapping) => ({ sourceMap, mapping })))
8
8
  .filter(({ mapping }) => nativeChangeMappingTouchesRegion(mapping, region, []));
9
+ const sourceMapIds = uniqueStrings(sourceMaps.map((sourceMap) => sourceMap?.id));
10
+ const sourceMapMappingIds = uniqueStrings(regionMappings.map(({ mapping }) => mapping?.id));
11
+ const importId = imported?.id;
12
+ const nativeSourceId = imported?.nativeSource?.id;
13
+ const nativeAstId = imported?.nativeAst?.id;
14
+ const semanticIndexId = imported?.semanticIndex?.id;
15
+ const universalAstId = imported?.universalAst?.id;
16
+ const sourcePath = imported?.sourcePath ?? region?.sourcePath;
17
+ const sourceHash = imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash ?? region?.sourceHash;
18
+ const sourcePreservationId = preservation?.id;
19
+ const exactSourceAvailable = preservation?.summary?.exactSourceAvailable === true;
20
+ const ownershipRegionId = region?.id;
21
+ const ownershipKey = region?.key;
22
+ const ownershipRegionKind = region?.regionKind;
23
+ const sourceSpan = region?.sourceSpan;
24
+ const identity = compactRecord({
25
+ schema: 'frontier.lang.nativeChangeProjectionEndpointIdentity.v1',
26
+ version: 1,
27
+ side,
28
+ importId,
29
+ nativeSourceId,
30
+ nativeAstId,
31
+ semanticIndexId,
32
+ universalAstId,
33
+ sourcePath,
34
+ sourceHash,
35
+ sourcePreservationId,
36
+ exactSourceAvailable,
37
+ ownershipRegionId,
38
+ ownershipKey,
39
+ ownershipRegionKind,
40
+ sourceSpan,
41
+ sourceMapIds,
42
+ sourceMapMappingIds
43
+ });
9
44
  return {
10
45
  side,
11
- importId: imported?.id,
46
+ importId,
12
47
  sidecarId: sidecar?.id,
13
- nativeSourceId: imported?.nativeSource?.id,
14
- nativeAstId: imported?.nativeAst?.id,
15
- semanticIndexId: imported?.semanticIndex?.id,
16
- universalAstId: imported?.universalAst?.id,
17
- sourcePath: imported?.sourcePath ?? region?.sourcePath,
18
- sourceHash: imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash ?? region?.sourceHash,
19
- sourcePreservationId: preservation?.id,
20
- exactSourceAvailable: preservation?.summary?.exactSourceAvailable === true,
21
- ownershipRegionId: region?.id,
22
- ownershipKey: region?.key,
23
- ownershipRegionKind: region?.regionKind,
24
- sourceSpan: region?.sourceSpan,
25
- sourceMapIds: sourceMaps.map((sourceMap) => sourceMap?.id).filter(Boolean),
26
- sourceMapMappingIds: regionMappings.map(({ mapping }) => mapping?.id).filter(Boolean)
48
+ nativeSourceId,
49
+ nativeAstId,
50
+ semanticIndexId,
51
+ universalAstId,
52
+ sourcePath,
53
+ sourceHash,
54
+ sourcePreservationId,
55
+ exactSourceAvailable,
56
+ ownershipRegionId,
57
+ ownershipKey,
58
+ ownershipRegionKind,
59
+ sourceSpan,
60
+ sourceMapIds,
61
+ sourceMapMappingIds,
62
+ identity
27
63
  };
28
64
  }
65
+
66
+ function compactRecord(value) {
67
+ return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0)));
68
+ }
@@ -1,4 +1,5 @@
1
1
  import{uniqueRecordsById,uniqueStrings}from'../../native-import-utils.js';
2
+ import{targetProjectionCoverageSignals}from'./projectImportAdmissionProjectionCoverage.js';
2
3
 
3
4
  const scoreWeights=Object.freeze({
4
5
  semanticEvidence:22,
@@ -95,15 +96,35 @@ function sourcePreservationScore(input){
95
96
 
96
97
  function sourceFreshnessScore(input){
97
98
  const sourceCount=Math.max(input.sourceCount??0,input.sourcePreservation.total,1);
98
- const fresh=Math.max(0,sourceCount-input.sourcePreservation.stale);
99
+ const sourceStaleness=input.sourceStaleness??{};
100
+ const stale=sourceStaleness.stale??input.sourcePreservation.stale;
101
+ const contentHashStale=sourceStaleness.contentHashStale??input.sourcePreservation.contentHashStale??0;
102
+ const baseHashStale=sourceStaleness.baseHashStale??input.sourcePreservation.baseHashStale??0;
103
+ const dirtyWorkspace=sourceStaleness.dirtyWorkspace??input.sourcePreservation.dirtyWorkspace??0;
104
+ const fresh=Math.max(0,sourceCount-stale);
99
105
  const score=roundScore(fresh*100/sourceCount);
100
106
  return scoreComponent('sourceFreshness',score,[
101
- ...(input.sourcePreservation.stale?[
102
- `Project import has stale source hashes for ${input.sourcePreservation.stale} source(s).`
107
+ ...(contentHashStale?[
108
+ `Project import has stale content hashes for ${contentHashStale} source(s).`
109
+ ]:[]),
110
+ ...(baseHashStale?[
111
+ `Project import has stale base hashes for ${baseHashStale} source(s).`
112
+ ]:[]),
113
+ ...(!contentHashStale&&!baseHashStale&&stale?[
114
+ `Project import has stale source hashes for ${stale} source(s).`
115
+ ]:[]),
116
+ ...(dirtyWorkspace&&!stale?[
117
+ `Project workspace is marked dirty for ${dirtyWorkspace} source(s), but no content or base hash staleness was detected.`
103
118
  ]:[])
104
119
  ],{
105
- stale:input.sourcePreservation.stale,
106
- staleSourcePaths:input.sourcePreservation.staleSourcePaths,
120
+ stale,
121
+ contentHashStale,
122
+ baseHashStale,
123
+ dirtyWorkspace,
124
+ staleSourcePaths:sourceStaleness.staleSourcePaths??input.sourcePreservation.staleSourcePaths,
125
+ contentHashStaleSourcePaths:sourceStaleness.contentHashStaleSourcePaths??input.sourcePreservation.contentHashStaleSourcePaths??[],
126
+ baseHashStaleSourcePaths:sourceStaleness.baseHashStaleSourcePaths??input.sourcePreservation.baseHashStaleSourcePaths??[],
127
+ dirtyWorkspaceSourcePaths:sourceStaleness.dirtyWorkspaceSourcePaths??input.sourcePreservation.dirtyWorkspaceSourcePaths??[],
107
128
  checkedSources:sourceCount
108
129
  });
109
130
  }
@@ -186,75 +207,6 @@ function targetProjectionCoverageScore(input){
186
207
  ],coverage);
187
208
  }
188
209
 
189
- function targetProjectionCoverageSignals(input){
190
- const entries=targetProjectionEntries(input.projectResult,input.imports);
191
- const matrices=projectionMatrices(input.projectResult,input.imports);
192
- for(const matrix of matrices){
193
- for(const language of matrix?.languages??[]) entries.push(...(language?.targets??[]));
194
- }
195
- const sourceMapSummary=input.contract?.sourceMaps??{};
196
- const summary=matrices.reduce((current,matrix)=>{
197
- current.exactSourceProjection+=matrix?.summary?.exactSourceProjection??0;
198
- current.targetAdapterProjection+=matrix?.summary?.targetAdapterProjection??0;
199
- current.missingAdapters+=matrix?.summary?.missingAdapters??0;
200
- current.unsupportedTargetFeatures+=matrix?.summary?.unsupportedTargetFeatures??0;
201
- return current;
202
- },{exactSourceProjection:0,targetAdapterProjection:0,missingAdapters:0,unsupportedTargetFeatures:0});
203
- const targetEntries=entries.length;
204
- const supportedTargets=entries.filter((entry)=>entry?.supported===true).length;
205
- const adapterProjectionTargets=entries.filter((entry)=>
206
- entry?.lossClass==='targetAdapterProjection'||entry?.lossClass==='exactSourceProjection'||entry?.adapter||entry?.adapterKind==='targetProjection'
207
- ).length+summary.targetAdapterProjection+summary.exactSourceProjection;
208
- const readinessValues=entries.map((entry)=>readinessScore[entry?.readiness]??45);
209
- const readinessAverage=readinessValues.length?readinessValues.reduce((sum,value)=>sum+value,0)/readinessValues.length:0;
210
- return {
211
- targetEntries,
212
- supportedTargets,
213
- adapterProjectionTargets,
214
- exactSourceProjection:Math.max(summary.exactSourceProjection,input.sourcePreservation.exactSourceAvailable??0),
215
- targetAdapterProjection:summary.targetAdapterProjection,
216
- missingAdapters:summary.missingAdapters+entries.filter((entry)=>entry?.lossClass==='missingAdapter'||entry?.supported===false).length,
217
- unsupportedTargetFeatures:summary.unsupportedTargetFeatures+entries.filter((entry)=>entry?.lossClass==='unsupportedTargetFeatures').length,
218
- readinessScore:roundScore(readinessAverage),
219
- sourceMapMappings:sourceMapSummary.mappingCount??0,
220
- generatedRangeMappings:sourceMapSummary.generatedRangeMappings??0,
221
- targetPaths:sourceMapSummary.targetPaths?.length??0,
222
- adapterGeneratedRanges:input.contract?.adapterCoverage?.generatedRanges??0
223
- };
224
- }
225
-
226
- function targetProjectionEntries(projectResult,imports){
227
- return [
228
- projectResult?.targetCoverage,
229
- projectResult?.metadata?.targetCoverage,
230
- projectResult?.metadata?.targetProjectionCoverage,
231
- ...(projectResult?.targetCoverages??[]),
232
- ...(projectResult?.metadata?.targetCoverages??[]),
233
- ...(imports??[]).flatMap((imported)=>[
234
- imported?.targetCoverage,
235
- imported?.metadata?.targetCoverage,
236
- imported?.metadata?.targetProjectionCoverage,
237
- ...(imported?.targetCoverages??[]),
238
- ...(imported?.metadata?.targetCoverages??[])
239
- ])
240
- ].flatMap((entry)=>Array.isArray(entry)?entry:[entry]).filter((entry)=>entry&&typeof entry==='object'&&(entry.target||entry.lossClass||entry.supported!==undefined));
241
- }
242
-
243
- function projectionMatrices(projectResult,imports){
244
- return [
245
- projectResult?.projectionMatrix,
246
- projectResult?.metadata?.projectionMatrix,
247
- ...(projectResult?.projectionMatrices??[]),
248
- ...(projectResult?.metadata?.projectionMatrices??[]),
249
- ...(imports??[]).flatMap((imported)=>[
250
- imported?.projectionMatrix,
251
- imported?.metadata?.projectionMatrix,
252
- ...(imported?.projectionMatrices??[]),
253
- ...(imported?.metadata?.projectionMatrices??[])
254
- ])
255
- ].filter((matrix)=>matrix?.kind==='frontier.lang.projectionTargetLossMatrix'||Array.isArray(matrix?.languages));
256
- }
257
-
258
210
  function admissionEvidenceRecords(projectResult,imports){
259
211
  return uniqueRecordsById([
260
212
  ...(projectResult?.evidence??[]),
@@ -0,0 +1,74 @@
1
+ const readinessScore = Object.freeze({ ready: 100, 'ready-with-losses': 75, 'needs-review': 45, blocked: 0 });
2
+
3
+ export function targetProjectionCoverageSignals(input) {
4
+ const entries = targetProjectionEntries(input.projectResult, input.imports);
5
+ const matrices = projectionMatrices(input.projectResult, input.imports);
6
+ for (const matrix of matrices) {
7
+ for (const language of matrix?.languages ?? []) entries.push(...(language?.targets ?? []));
8
+ }
9
+ const sourceMapSummary = input.contract?.sourceMaps ?? {};
10
+ const summary = matrices.reduce((current, matrix) => {
11
+ current.exactSourceProjection += matrix?.summary?.exactSourceProjection ?? 0;
12
+ current.targetAdapterProjection += matrix?.summary?.targetAdapterProjection ?? 0;
13
+ current.missingAdapters += matrix?.summary?.missingAdapters ?? 0;
14
+ current.unsupportedTargetFeatures += matrix?.summary?.unsupportedTargetFeatures ?? 0;
15
+ return current;
16
+ }, { exactSourceProjection: 0, targetAdapterProjection: 0, missingAdapters: 0, unsupportedTargetFeatures: 0 });
17
+ const targetEntries = entries.length;
18
+ const supportedTargets = entries.filter((entry) => entry?.supported === true).length;
19
+ const adapterProjectionTargets = entries.filter((entry) =>
20
+ entry?.lossClass === 'targetAdapterProjection' || entry?.lossClass === 'exactSourceProjection' || entry?.adapter || entry?.adapterKind === 'targetProjection'
21
+ ).length + summary.targetAdapterProjection + summary.exactSourceProjection;
22
+ const readinessValues = entries.map((entry) => readinessScore[entry?.readiness] ?? 45);
23
+ const readinessAverage = readinessValues.length ? readinessValues.reduce((sum, value) => sum + value, 0) / readinessValues.length : 0;
24
+ return {
25
+ targetEntries,
26
+ supportedTargets,
27
+ adapterProjectionTargets,
28
+ exactSourceProjection: Math.max(summary.exactSourceProjection, input.sourcePreservation.exactSourceAvailable ?? 0),
29
+ targetAdapterProjection: summary.targetAdapterProjection,
30
+ missingAdapters: summary.missingAdapters + entries.filter((entry) => entry?.lossClass === 'missingAdapter' || entry?.supported === false).length,
31
+ unsupportedTargetFeatures: summary.unsupportedTargetFeatures + entries.filter((entry) => entry?.lossClass === 'unsupportedTargetFeatures').length,
32
+ readinessScore: roundScore(readinessAverage),
33
+ sourceMapMappings: sourceMapSummary.mappingCount ?? 0,
34
+ generatedRangeMappings: sourceMapSummary.generatedRangeMappings ?? 0,
35
+ targetPaths: sourceMapSummary.targetPaths?.length ?? 0,
36
+ adapterGeneratedRanges: input.contract?.adapterCoverage?.generatedRanges ?? 0
37
+ };
38
+ }
39
+
40
+ function targetProjectionEntries(projectResult, imports) {
41
+ return [
42
+ projectResult?.targetCoverage,
43
+ projectResult?.metadata?.targetCoverage,
44
+ projectResult?.metadata?.targetProjectionCoverage,
45
+ ...(projectResult?.targetCoverages ?? []),
46
+ ...(projectResult?.metadata?.targetCoverages ?? []),
47
+ ...(imports ?? []).flatMap((imported) => [
48
+ imported?.targetCoverage,
49
+ imported?.metadata?.targetCoverage,
50
+ imported?.metadata?.targetProjectionCoverage,
51
+ ...(imported?.targetCoverages ?? []),
52
+ ...(imported?.metadata?.targetCoverages ?? [])
53
+ ])
54
+ ].flatMap((entry) => Array.isArray(entry) ? entry : [entry]).filter((entry) => entry && typeof entry === 'object' && (entry.target || entry.lossClass || entry.supported !== undefined));
55
+ }
56
+
57
+ function projectionMatrices(projectResult, imports) {
58
+ return [
59
+ projectResult?.projectionMatrix,
60
+ projectResult?.metadata?.projectionMatrix,
61
+ ...(projectResult?.projectionMatrices ?? []),
62
+ ...(projectResult?.metadata?.projectionMatrices ?? []),
63
+ ...(imports ?? []).flatMap((imported) => [
64
+ imported?.projectionMatrix,
65
+ imported?.metadata?.projectionMatrix,
66
+ ...(imported?.projectionMatrices ?? []),
67
+ ...(imported?.metadata?.projectionMatrices ?? [])
68
+ ])
69
+ ].filter((matrix) => matrix?.kind === 'frontier.lang.projectionTargetLossMatrix' || Array.isArray(matrix?.languages));
70
+ }
71
+
72
+ function roundScore(value) {
73
+ return Math.round((Number.isFinite(value) ? value : 0) * 100) / 100;
74
+ }