@shapeshift-labs/frontier-lang-compiler 0.2.98 → 0.2.100

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 (30) hide show
  1. package/dist/declarations/semantic-edit-bundle.d.ts +90 -0
  2. package/dist/declarations/semantic-edit-script.d.ts +34 -37
  3. package/dist/declarations/semantic-lineage.d.ts +63 -34
  4. package/dist/declarations/semantic-patch-bundle-index.d.ts +3 -0
  5. package/dist/declarations/semantic-patch-bundle.d.ts +23 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +1 -0
  8. package/dist/internal/index-impl/declarationRecord.js +2 -2
  9. package/dist/internal/index-impl/inferSemanticLineageEvents.js +8 -0
  10. package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +56 -64
  11. package/dist/internal/index-impl/replaySemanticEditProjection.js +54 -22
  12. package/dist/internal/index-impl/semanticEditBundleAdmission.js +220 -0
  13. package/dist/internal/index-impl/semanticEditBundleIndex.js +16 -10
  14. package/dist/internal/index-impl/semanticEditSourceRanges.js +204 -0
  15. package/dist/internal/index-impl/semanticHistoryLineageResolution.js +35 -1
  16. package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +2 -2
  17. package/dist/internal/index-impl/semanticLineageInferenceMatching.js +150 -13
  18. package/dist/internal/index-impl/semanticLineageResolutionRecords.js +28 -1
  19. package/dist/internal/index-impl/semanticPatchBundleAdmission.js +130 -11
  20. package/dist/internal/index-impl/semanticPatchBundleLineageLinks.js +199 -0
  21. package/dist/internal/index-impl/semanticPatchBundleOverlaps.js +6 -2
  22. package/dist/internal/index-impl/semanticPatchBundleRecords.js +65 -126
  23. package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +127 -0
  24. package/dist/internal/index-impl/sourceTextForSpan.js +4 -9
  25. package/dist/lightweight-dependency-relations.js +113 -7
  26. package/dist/native-import-utils.js +15 -1
  27. package/dist/native-region-scanner-js-helpers.js +61 -17
  28. package/dist/native-region-scanner-js.js +12 -4
  29. package/dist/semantic-import-regions.js +3 -3
  30. package/package.json +1 -1
@@ -2,27 +2,43 @@ import { normalizeSemanticMergeReadiness, uniqueStrings } from '../../native-imp
2
2
 
3
3
  export function createSemanticPatchBundleAdmission(input = {}, context = {}) {
4
4
  const transformAdmission = semanticTransformAdmission(context);
5
- const fallbackReadiness = transformAdmission.readiness === 'ready' ? 'ready' : context.readiness;
6
- const readiness = normalizeSemanticMergeReadiness(input.readiness ?? fallbackReadiness) ?? input.readiness ?? fallbackReadiness;
7
- const status = input.status ?? admissionStatusForReadiness(readiness, transformAdmission);
8
- const autoApplyCandidate = input.autoApplyCandidate ?? (status === 'admitted' && transformAdmission.action === 'admit');
5
+ const semanticEditAdmission = context.semanticEditAdmission ?? { status: 'none', action: 'none', readiness: 'needs-review', reasonCodes: [] };
6
+ const evidenceAdmission = autoMergeEvidenceAdmission(context, { transformAdmission, semanticEditAdmission });
7
+ const fallbackReadiness = fallbackAdmissionReadiness(transformAdmission, semanticEditAdmission, evidenceAdmission, context.readiness);
8
+ const inputReadiness = normalizeSemanticMergeReadiness(input.readiness ?? fallbackReadiness) ?? input.readiness ?? fallbackReadiness;
9
+ const readiness = hasPositiveApplyAction(transformAdmission, semanticEditAdmission) && evidenceAdmission.action !== 'admit'
10
+ ? evidenceAdmission.readiness
11
+ : inputReadiness;
12
+ const computedStatus = admissionStatusForReadiness(readiness, transformAdmission, semanticEditAdmission, evidenceAdmission);
13
+ const status = input.status === 'admitted' && computedStatus !== 'admitted' ? computedStatus : input.status ?? computedStatus;
14
+ const computedAutoApplyCandidate = status === 'admitted' &&
15
+ hasPositiveApplyAction(transformAdmission, semanticEditAdmission) &&
16
+ evidenceAdmission.action === 'admit';
17
+ const autoApplyCandidate = input.autoApplyCandidate === true ? computedAutoApplyCandidate : input.autoApplyCandidate ?? computedAutoApplyCandidate;
18
+ const admittedWithoutPositiveProof = status === 'admitted' &&
19
+ hasPositiveApplyAction(transformAdmission, semanticEditAdmission) &&
20
+ evidenceAdmission.action !== 'admit';
9
21
  return compactRecord({
10
22
  status,
11
23
  readiness,
12
- reviewRequired: input.reviewRequired ?? status !== 'admitted',
24
+ reviewRequired: input.reviewRequired ?? (status !== 'admitted' || admittedWithoutPositiveProof),
13
25
  autoMergeClaim: false,
14
26
  autoApplyCandidate,
15
27
  transformAdmission,
28
+ semanticEditAdmission,
29
+ evidenceAdmission,
16
30
  reasonCodes: uniqueStrings([
17
31
  ...strings(input.reasonCodes),
18
32
  ...strings(context.source?.reasons),
19
33
  ...strings(context.mergeCandidate?.reasons),
20
- ...transformAdmission.reasonCodes
21
- ]),
34
+ ...transformAdmission.reasonCodes,
35
+ ...strings(semanticEditAdmission.reasonCodes),
36
+ ...strings(evidenceAdmission.reasonCodes)
37
+ ].filter(Boolean)),
22
38
  conflictKeys: uniqueStrings([...strings(input.conflictKeys), ...context.conflictKeys]),
23
39
  admittedAt: input.admittedAt,
24
40
  reviewerId: input.reviewerId,
25
- evidenceIds: uniqueStrings([...strings(input.evidenceIds), ...strings(transformAdmission.evidenceIds)]),
41
+ evidenceIds: uniqueStrings([...strings(input.evidenceIds), ...strings(transformAdmission.evidenceIds), ...strings(evidenceAdmission.evidenceIds)]),
26
42
  metadata: input.metadata
27
43
  });
28
44
  }
@@ -68,7 +84,7 @@ function transformReasonCodes(input) {
68
84
  !input.complete ? 'transform-evidence-incomplete' : undefined,
69
85
  input.ready ? 'transform-auto-apply-candidate' : undefined,
70
86
  ...input.readinesses.map((readiness) => `transform-readiness:${readiness}`)
71
- ]);
87
+ ].filter(Boolean));
72
88
  }
73
89
 
74
90
  function transformReadiness(value) {
@@ -81,17 +97,120 @@ function transformReadiness(value) {
81
97
  return undefined;
82
98
  }
83
99
 
84
- function admissionStatusForReadiness(readiness, transformAdmission) {
100
+ function autoMergeEvidenceAdmission(context, admissions) {
101
+ const evidence = uniqueEvidenceRecords([
102
+ ...array(context.evidenceRecords),
103
+ ...array(context.evidence),
104
+ ...array(context.source?.evidence),
105
+ ...array(context.source?.patch?.evidence),
106
+ ...array(context.source?.semanticPatch?.evidence),
107
+ ...array(context.mergeCandidate?.evidence)
108
+ ]);
109
+ const positiveApply = hasPositiveApplyAttempt(admissions.transformAdmission, admissions.semanticEditAdmission);
110
+ if (!positiveApply) return { status: 'none', action: 'none', readiness: 'needs-review', reasonCodes: [], evidenceIds: evidenceIds(evidence) };
111
+ const summary = summarizeAutoMergeEvidence(evidence);
112
+ const blocked = summary.failed > 0 || summary.conflict > 0;
113
+ const ready = !blocked && summary.stale === 0 && summary.passed > 0;
114
+ const status = blocked ? 'blocked' : summary.stale > 0 ? 'stale' : ready ? 'ready' : 'needs-review';
115
+ return compactRecord({
116
+ status,
117
+ action: blocked ? 'block' : status === 'stale' ? 'rerun-semantic-import' : ready ? 'admit' : 'review',
118
+ readiness: blocked ? 'blocked' : ready ? 'ready' : 'needs-review',
119
+ reasonCodes: autoMergeEvidenceReasonCodes(summary, status),
120
+ evidenceIds: summary.evidenceIds,
121
+ passed: summary.passed,
122
+ failed: summary.failed,
123
+ conflict: summary.conflict,
124
+ stale: summary.stale
125
+ });
126
+ }
127
+
128
+ function summarizeAutoMergeEvidence(evidence) {
129
+ const testLike = evidence.filter(isAutoMergeTestEvidence);
130
+ const failed = testLike.filter((record) => evidenceStatus(record, ['failed', 'failure', 'error', 'blocked', 'rejected']));
131
+ const passed = testLike.filter((record) => evidenceStatus(record, ['passed', 'ok', 'success', 'succeeded', 'accepted', 'verified']));
132
+ const conflict = evidence.filter((record) => evidenceStatus(record, ['conflict', 'conflicted']) || record?.metadata?.conflict === true || strings(record?.reasonCodes ?? record?.reasons).some((reason) => reason.toLowerCase().includes('conflict')));
133
+ const stale = evidence.filter((record) => evidenceStatus(record, ['stale']) || record?.metadata?.stale === true || strings(record?.reasonCodes ?? record?.reasons).some((reason) => reason.toLowerCase().includes('stale')));
134
+ return {
135
+ evidenceIds: evidenceIds(evidence),
136
+ passed: passed.length,
137
+ failed: failed.length,
138
+ conflict: conflict.length,
139
+ stale: stale.length
140
+ };
141
+ }
142
+
143
+ function autoMergeEvidenceReasonCodes(summary, status) {
144
+ return uniqueStrings([
145
+ summary.passed ? 'auto-merge-tests-passed' : undefined,
146
+ summary.passed === 0 ? 'auto-merge-tests-passed-evidence-missing' : undefined,
147
+ summary.failed ? 'auto-merge-tests-failed' : undefined,
148
+ summary.conflict ? 'auto-merge-conflict-evidence' : undefined,
149
+ summary.stale ? 'auto-merge-stale-evidence' : undefined,
150
+ status === 'ready' ? 'auto-merge-positive-proof' : undefined
151
+ ].filter(Boolean));
152
+ }
153
+
154
+ function fallbackAdmissionReadiness(transformAdmission, semanticEditAdmission, evidenceAdmission, fallback) {
155
+ if ([transformAdmission.readiness, semanticEditAdmission.readiness, evidenceAdmission.readiness].includes('blocked')) return 'blocked';
156
+ if (hasSkipReadyAction(semanticEditAdmission)) return 'ready';
157
+ if (hasPositiveApplyAction(transformAdmission, semanticEditAdmission)) return evidenceAdmission.action === 'admit' ? 'ready' : evidenceAdmission.readiness;
158
+ return fallback;
159
+ }
160
+
161
+ function admissionStatusForReadiness(readiness, transformAdmission, semanticEditAdmission, evidenceAdmission) {
85
162
  if (readiness === 'blocked') return 'blocked';
86
- if (transformAdmission.action === 'admit' && readiness === 'ready') return 'admitted';
163
+ if (hasAdmissibleReadyAction(transformAdmission, semanticEditAdmission, evidenceAdmission) && readiness === 'ready') return 'admitted';
87
164
  return readiness === 'needs-review' ? 'needs-review' : 'proposed';
88
165
  }
89
166
 
167
+ function hasAdmissibleReadyAction(transformAdmission, semanticEditAdmission, evidenceAdmission) {
168
+ return hasSkipReadyAction(semanticEditAdmission) ||
169
+ (hasPositiveApplyAction(transformAdmission, semanticEditAdmission) && evidenceAdmission.action === 'admit');
170
+ }
171
+
172
+ function hasPositiveApplyAction(transformAdmission, semanticEditAdmission) {
173
+ return [transformAdmission.action, semanticEditAdmission.action].includes('admit');
174
+ }
175
+
176
+ function hasPositiveApplyAttempt(transformAdmission, semanticEditAdmission) {
177
+ return hasPositiveApplyAction(transformAdmission, semanticEditAdmission) ||
178
+ Number(semanticEditAdmission.summary?.acceptedClean ?? 0) > 0 ||
179
+ strings(transformAdmission.reasonCodes).includes('transform-readiness:auto-merge-candidate');
180
+ }
181
+
182
+ function hasSkipReadyAction(semanticEditAdmission) {
183
+ return semanticEditAdmission.action === 'skip' && semanticEditAdmission.readiness === 'ready' && semanticEditAdmission.reviewRequired === false;
184
+ }
185
+
90
186
  function hasCrossLanguageTransform(index) {
91
187
  const source = new Set(strings(index.transformSourceLanguages));
92
188
  return strings(index.transformTargetLanguages).some((target) => !source.has(target));
93
189
  }
94
190
 
191
+ function isAutoMergeTestEvidence(record) {
192
+ const kind = String(record?.kind ?? record?.type ?? '').toLowerCase();
193
+ return ['test', 'tests', 'proof', 'gate', 'verification', 'check'].includes(kind);
194
+ }
195
+
196
+ function evidenceStatus(record, statuses) {
197
+ const status = String(record?.status ?? record?.outcome ?? '').toLowerCase();
198
+ return statuses.includes(status);
199
+ }
200
+
201
+ function uniqueEvidenceRecords(records) {
202
+ const seen = new Set();
203
+ const result = [];
204
+ for (const record of records.filter(Boolean)) {
205
+ const key = record.id ?? JSON.stringify(record);
206
+ if (seen.has(key)) continue;
207
+ seen.add(key);
208
+ result.push(record);
209
+ }
210
+ return result;
211
+ }
212
+
213
+ function evidenceIds(evidence) { return uniqueStrings(evidence.map((record) => record.id)); }
95
214
  function array(value) { if (value === undefined || value === null) return []; return Array.isArray(value) ? value : [value]; }
96
215
  function strings(value) { return array(value).map((entry) => String(entry ?? '')).filter(Boolean); }
97
216
  function compactRecord(value) { return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0))); }
@@ -0,0 +1,199 @@
1
+ import { uniqueStrings } from '../../native-import-utils.js';
2
+
3
+ export function lineageLinkInputs(source, options, changedRegions, targetPortability) {
4
+ return [
5
+ ...array(options.lineageResolutionLinks ?? options.semanticLineageResolutionLinks),
6
+ ...array(options.lineageResolutions ?? options.semanticLineageResolutions),
7
+ ...array(source.lineageResolutionLinks ?? source.semanticLineageResolutionLinks),
8
+ ...array(source.lineageResolutions ?? source.semanticLineageResolutions),
9
+ ...array(source.metadata?.semanticLineageResolutionLinks),
10
+ ...array(source.metadata?.semanticHistoryLineageResolution),
11
+ ...array(source.metadata?.semanticLineageResolution),
12
+ ...array(source.metadata?.targetPortability?.lineageResolutionLinks),
13
+ ...array(targetPortability?.lineageResolutionLinks),
14
+ ...array(targetPortability?.lineageResolutions),
15
+ ...changedRegions.flatMap((region) => lineageLinksFromRegion(region))
16
+ ];
17
+ }
18
+
19
+ export function normalizeLineageResolutionLinks(entries) {
20
+ const seen = new Set();
21
+ const result = [];
22
+ for (const entry of lineageResolutionEntries(entries)) {
23
+ const currentAnchors = array(entry.currentAnchors);
24
+ const summary = entry.summary ?? {};
25
+ const anchorSummary = entry.anchorSummary ?? {};
26
+ const ids = uniqueStrings([
27
+ entry.id,
28
+ entry.resolutionId,
29
+ entry.lineageResolutionId,
30
+ ...strings(entry.lineageResolutionIds),
31
+ ...strings(entry.semanticLineageResolutionIds),
32
+ ...strings(summary.lineageResolutionIds)
33
+ ]);
34
+ const link = compactRecord({
35
+ id: firstString(entry.id, entry.resolutionId, entry.lineageResolutionId, ids[0]),
36
+ lineageResolutionIds: ids,
37
+ status: entry.status,
38
+ readiness: entry.readiness ?? entry.admission?.readiness,
39
+ action: entry.action ?? entry.admission?.action,
40
+ anchorKeys: uniqueStrings([
41
+ entry.anchorKey,
42
+ entry.query?.anchorKey,
43
+ entry.startAnchor?.key,
44
+ ...strings(entry.anchorKeys),
45
+ ...strings(anchorSummary.activeAnchorKeys),
46
+ ...strings(anchorSummary.candidateAnchorKeys),
47
+ ...strings(anchorSummary.blockedAnchorKeys),
48
+ ...strings(summary.activeAnchorKeys),
49
+ ...strings(summary.candidateAnchorKeys),
50
+ ...strings(summary.blockedAnchorKeys),
51
+ ...currentAnchors.map((anchor) => anchor.key)
52
+ ]),
53
+ sourcePaths: uniqueStrings([
54
+ entry.sourcePath,
55
+ entry.query?.sourcePath,
56
+ entry.startAnchor?.sourcePath,
57
+ ...strings(entry.sourcePaths),
58
+ ...strings(entry.lineageSourcePaths),
59
+ ...strings(summary.sourcePaths),
60
+ ...currentAnchors.flatMap((anchor) => [anchor.sourcePath, ...strings(anchor.lineageSourcePaths)])
61
+ ]),
62
+ evidenceIds: uniqueStrings([
63
+ ...strings(entry.evidenceIds),
64
+ ...strings(entry.lineageEvidenceIds),
65
+ ...strings(summary.evidenceIds),
66
+ ...currentAnchors.flatMap((anchor) => strings(anchor.evidenceIds))
67
+ ]),
68
+ proofIds: uniqueStrings([
69
+ ...strings(entry.proofIds),
70
+ ...strings(entry.lineageProofIds),
71
+ ...strings(summary.proofIds),
72
+ ...currentAnchors.flatMap((anchor) => strings(anchor.proofIds))
73
+ ]),
74
+ lineageEventIds: uniqueStrings([
75
+ ...strings(entry.lineageEventIds),
76
+ ...strings(entry.traversedEventIds),
77
+ ...strings(summary.lineageEventIds),
78
+ ...strings(summary.traversedEventIds),
79
+ ...currentAnchors.flatMap((anchor) => strings(anchor.lineageEventIds))
80
+ ]),
81
+ terminalEventIds: uniqueStrings([
82
+ ...strings(entry.terminalEventIds),
83
+ ...strings(summary.terminalEventIds),
84
+ ...currentAnchors.flatMap((anchor) => strings(anchor.terminalLineageEventIds))
85
+ ]),
86
+ crdtOperationIds: uniqueStrings([
87
+ ...strings(entry.crdtOperationIds),
88
+ ...strings(summary.crdtOperationIds),
89
+ ...currentAnchors.flatMap((anchor) => strings(anchor.crdtOperationIds))
90
+ ]),
91
+ crdtHeads: uniqueStrings([
92
+ ...strings(entry.crdtHeads),
93
+ ...strings(summary.crdtHeads),
94
+ ...currentAnchors.flatMap((anchor) => strings(anchor.crdtHeads))
95
+ ]),
96
+ reasonCodes: uniqueStrings([
97
+ ...strings(entry.reasonCodes),
98
+ ...strings(entry.lineageReasonCodes),
99
+ ...strings(summary.reasonCodes),
100
+ ...currentAnchors.flatMap((anchor) => strings(anchor.lineageReasonCodes))
101
+ ])
102
+ });
103
+ const key = firstString(link.id, ...strings(link.lineageResolutionIds), ...strings(link.lineageEventIds), ...strings(link.evidenceIds), ...strings(link.sourcePaths));
104
+ if (!key || seen.has(key)) continue;
105
+ seen.add(key);
106
+ result.push(link);
107
+ }
108
+ return result;
109
+ }
110
+
111
+ export function semanticLineageLinkIndex(links = [], changedRegions = [], targetPortability, admission) {
112
+ const records = normalizeLineageResolutionLinks([
113
+ ...array(links),
114
+ ...array(targetPortability?.lineageResolutionLinks),
115
+ ...array(targetPortability?.lineageResolutions),
116
+ ...array(admission?.metadata?.semanticLineageResolutionLinks),
117
+ ...array(changedRegions).flatMap((region) => lineageLinksFromRegion(region))
118
+ ]);
119
+ return {
120
+ lineageResolutionIds: uniqueStrings(records.flatMap((record) => [record.id, ...strings(record.lineageResolutionIds)])),
121
+ lineageEventIds: uniqueStrings(records.flatMap((record) => record.lineageEventIds)),
122
+ sourcePaths: uniqueStrings(records.flatMap((record) => record.sourcePaths)),
123
+ evidenceIds: uniqueStrings(records.flatMap((record) => record.evidenceIds)),
124
+ proofIds: uniqueStrings(records.flatMap((record) => record.proofIds)),
125
+ reasonCodes: uniqueStrings(records.flatMap((record) => record.reasonCodes)),
126
+ terminalEventIds: uniqueStrings(records.flatMap((record) => record.terminalEventIds)),
127
+ crdtOperationIds: uniqueStrings(records.flatMap((record) => record.crdtOperationIds)),
128
+ crdtHeads: uniqueStrings(records.flatMap((record) => record.crdtHeads))
129
+ };
130
+ }
131
+
132
+ export function linkAdmissionLineage(admission, lineageLinks) {
133
+ if (lineageLinks.length === 0) return admission;
134
+ const lineageIndex = semanticLineageLinkIndex(lineageLinks);
135
+ return {
136
+ ...admission,
137
+ evidenceIds: uniqueStrings([...strings(admission.evidenceIds), ...lineageIndex.evidenceIds]),
138
+ reasonCodes: uniqueStrings([...strings(admission.reasonCodes), 'semantic-lineage-resolution-linked', ...lineageIndex.reasonCodes]),
139
+ metadata: compactRecord({
140
+ ...(admission.metadata ?? {}),
141
+ semanticLineageResolutionLinks: lineageLinks
142
+ })
143
+ };
144
+ }
145
+
146
+ function lineageLinksFromRegion(region = {}) {
147
+ const metadata = region.metadata ?? {};
148
+ const bidirectional = metadata.bidirectionalTargetChange ?? {};
149
+ const history = metadata.semanticHistoryLineageResolution ?? {};
150
+ const lineage = metadata.semanticLineageResolution ?? {};
151
+ return [
152
+ ...array(region.lineageResolutionLinks),
153
+ ...array(region.lineageResolutions),
154
+ compactRecord({
155
+ lineageResolutionIds: region.lineageResolutionIds,
156
+ lineageEventIds: region.lineageEventIds,
157
+ sourcePaths: region.lineageSourcePaths,
158
+ evidenceIds: region.lineageEvidenceIds,
159
+ proofIds: region.lineageProofIds,
160
+ reasonCodes: region.lineageReasonCodes
161
+ }),
162
+ bidirectional,
163
+ history,
164
+ lineage,
165
+ ...array(bidirectional.lineageResolutionLinks),
166
+ ...array(history.lineageResolutionLinks),
167
+ ...array(lineage.lineageResolutionLinks)
168
+ ].filter((entry) => Object.keys(entry ?? {}).length > 0);
169
+ }
170
+
171
+ function lineageResolutionEntries(entries) {
172
+ const result = [];
173
+ for (const entry of array(entries).filter(Boolean)) {
174
+ result.push(entry);
175
+ result.push(...array(entry.resolutions));
176
+ result.push(...array(entry.lineageResolutions));
177
+ result.push(...array(entry.semanticLineageResolutions));
178
+ result.push(...array(entry.lineageResolutionLinks));
179
+ result.push(...array(entry.semanticLineageResolutionLinks));
180
+ }
181
+ return result;
182
+ }
183
+
184
+ function array(value) {
185
+ if (value === undefined || value === null) return [];
186
+ return Array.isArray(value) ? value : [value];
187
+ }
188
+
189
+ function strings(value) {
190
+ return array(value).map((entry) => String(entry ?? '')).filter(Boolean);
191
+ }
192
+
193
+ function firstString(...values) {
194
+ return values.map((value) => value === undefined || value === null ? '' : String(value)).find(Boolean);
195
+ }
196
+
197
+ function compactRecord(value) {
198
+ return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0)));
199
+ }
@@ -113,8 +113,12 @@ function sharedIndex(left,right,options){
113
113
  }
114
114
 
115
115
  function overlapAdmission(shared,{leftIndex,rightIndex,options}){
116
- const duplicate=shared.operationContentHashes.length||shared.editContentHashes.length||shared.semanticTransformContentHashes.length||shared.semanticEditReplayIds.length||shared.semanticEditReplayOutputHashes.length;
117
- const semantic=shared.semanticEditKeys.length||shared.semanticIdentityHashes.length||shared.sourceIdentityHashes.length||shared.semanticTransformIdentityHashes.length||shared.projectionIdentityHashes.length;
116
+ const hashMismatch=disjointNonEmpty(leftIndex.baseHashes,rightIndex.baseHashes)||disjointNonEmpty(leftIndex.targetHashes,rightIndex.targetHashes);
117
+ const sourceRelated=shared.sourcePaths.length||shared.regionKeys.length||shared.conflictKeys.length||shared.sourceIdentityHashes.length;
118
+ const editContent=shared.operationContentHashes.length||shared.editContentHashes.length||shared.semanticEditReplayOutputHashes.length;
119
+ const transformContent=shared.semanticTransformContentHashes.length&&shared.projectionIdentityHashes.length;
120
+ const duplicate=(transformContent||shared.semanticEditReplayIds.length||(editContent&&sourceRelated))&&!hashMismatch;
121
+ const semantic=editContent||shared.semanticEditKeys.length||shared.semanticIdentityHashes.length||shared.sourceIdentityHashes.length||shared.semanticTransformIdentityHashes.length||shared.projectionIdentityHashes.length;
118
122
  const source=shared.regionKeys.length||shared.conflictKeys.length||shared.sourcePaths.length||shared.semanticEditReplayCurrentHashes.length;
119
123
  const status=duplicate?'duplicate':semantic?'semantic-overlap':source?'source-overlap':'independent';
120
124
  const reasonCodes=uniqueStrings([