@shapeshift-labs/frontier-lang-compiler 0.2.84 → 0.2.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -933,7 +933,6 @@ The published Frontier package family is generated from one shared package catal
933
933
  - [`@shapeshift-labs/frontier-realtime-server`](https://www.npmjs.com/package/@shapeshift-labs/frontier-realtime-server): Authoritative realtime room, tick, command validation, rate-limit, session, and snapshot-history runtime.
934
934
  - [`@shapeshift-labs/frontier-realtime-websocket`](https://www.npmjs.com/package/@shapeshift-labs/frontier-realtime-websocket): WebSocket client, wire, and Node room-server transport for Frontier realtime.
935
935
  - [`@shapeshift-labs/frontier-game`](https://www.npmjs.com/package/@shapeshift-labs/frontier-game): Game-facing entity, component, player, room, ownership, spatial interest, rollback, physics, and replication helpers above realtime.
936
- - [`@shapeshift-labs/loom`](https://www.npmjs.com/package/@shapeshift-labs/loom): Repo-level semantic collaboration CLI for .loom workspaces, including init, scan, status, graph snapshots, projection plans, Frontier Lang delegation, Frontier Swarm delegation, and Frontier Framework delegation.
937
936
 
938
937
  Package source repositories:
939
938
 
@@ -1027,4 +1026,3 @@ Package source repositories:
1027
1026
  - [`siliconjungle/-shapeshift-labs-frontier-realtime-server`](https://github.com/siliconjungle/-shapeshift-labs-frontier-realtime-server)
1028
1027
  - [`siliconjungle/-shapeshift-labs-frontier-realtime-websocket`](https://github.com/siliconjungle/-shapeshift-labs-frontier-realtime-websocket)
1029
1028
  - [`siliconjungle/-shapeshift-labs-frontier-game`](https://github.com/siliconjungle/-shapeshift-labs-frontier-game)
1030
- - [`siliconjungle/-shapeshift-labs-loom`](https://github.com/siliconjungle/-shapeshift-labs-loom)
@@ -40,6 +40,10 @@ export interface SemanticEditScriptOperation {
40
40
  readonly symbolKind?: string;
41
41
  readonly sourceSpan?: SourceSpan;
42
42
  };
43
+ readonly semanticKey?: string;
44
+ readonly semanticIdentityHash?: string;
45
+ readonly sourceIdentityHash?: string;
46
+ readonly operationContentHash?: string;
43
47
  readonly spans?: {
44
48
  readonly base?: SourceSpan;
45
49
  readonly worker?: SourceSpan;
@@ -62,6 +66,9 @@ export interface SemanticEditScriptOperation {
62
66
  readonly reanchor?: {
63
67
  readonly fromAnchorKey?: string;
64
68
  readonly toAnchorKey?: string;
69
+ readonly toSourcePath?: string;
70
+ readonly toSymbolName?: string;
71
+ readonly toSymbolKind?: string;
65
72
  readonly lineageStatus?: string;
66
73
  readonly traversedEventIds?: readonly string[];
67
74
  };
@@ -84,6 +91,10 @@ export interface SemanticEditScriptSummary {
84
91
  readonly blocked: number;
85
92
  readonly candidates: number;
86
93
  readonly autoMergeCandidates: number;
94
+ readonly semanticKeys?: readonly string[];
95
+ readonly semanticIdentityHashes?: readonly string[];
96
+ readonly sourceIdentityHashes?: readonly string[];
97
+ readonly operationContentHashes?: readonly string[];
87
98
  }
88
99
 
89
100
  export interface SemanticEditScriptAdmission {
@@ -130,12 +141,18 @@ export interface SemanticEditProjectionEdit {
130
141
  readonly regionId?: string;
131
142
  readonly regionKind?: string;
132
143
  readonly sourcePath?: string;
144
+ readonly originalSourcePath?: string;
145
+ readonly targetAnchorKey?: string;
146
+ readonly targetSourcePath?: string;
147
+ readonly targetSymbolName?: string;
148
+ readonly targetSymbolKind?: string;
133
149
  readonly symbolId?: string;
134
150
  readonly symbolName?: string;
135
151
  readonly symbolKind?: string;
136
152
  readonly semanticKey?: string;
137
153
  readonly semanticIdentityHash?: string;
138
154
  readonly sourceIdentityHash?: string;
155
+ readonly operationContentHash?: string;
139
156
  readonly editContentHash?: string;
140
157
  readonly headStart: number;
141
158
  readonly headEnd: number;
@@ -179,6 +196,7 @@ export interface ProjectSemanticEditScriptToSourceOptions {
179
196
  readonly script: SemanticEditScript;
180
197
  readonly workerSourceText: string;
181
198
  readonly headSourceText: string;
199
+ readonly headSourcePath?: string;
182
200
  readonly metadata?: Record<string, unknown>;
183
201
  }
184
202
 
@@ -198,6 +216,7 @@ export interface CreateSemanticEditScriptOptions {
198
216
  readonly workerSourceText?: string;
199
217
  readonly afterSourceText?: string;
200
218
  readonly headSourceText?: string;
219
+ readonly headSourcePath?: string;
201
220
  readonly currentSourceText?: string;
202
221
  readonly baseSourceHash?: string;
203
222
  readonly workerSourceHash?: string;
@@ -1,5 +1,6 @@
1
1
  import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
2
  import { idFragment, uniqueStrings } from '../../native-import-utils.js';
3
+ import { semanticEditIdentityFields } from './semanticEditIdentityRecords.js';
3
4
 
4
5
  export function projectSemanticEditScriptToSource(input = {}) {
5
6
  const script = input.script;
@@ -24,7 +25,7 @@ export function projectSemanticEditScriptToSource(input = {}) {
24
25
  id: input.id ?? `semantic_edit_projection_${idFragment(script.id ?? script.hash ?? 'script')}`,
25
26
  scriptId: script.id,
26
27
  status: blocked ? 'blocked' : 'projected',
27
- sourcePath: script.sourcePath,
28
+ sourcePath: input.headSourcePath ?? projectedSourcePath(script, edits),
28
29
  language: script.language,
29
30
  baseHash: script.baseHash,
30
31
  workerHash: script.workerHash,
@@ -53,8 +54,9 @@ export function projectSemanticEditScriptToSource(input = {}) {
53
54
  }
54
55
 
55
56
  function sourceEditForOperation(operation, workerSourceText, headSourceText) {
57
+ const identity = projectionIdentity(operation);
56
58
  if (operation.status === 'already-applied') {
57
- return { ok: true, value: { ...semanticEditIdentity(operation), operationId: operation.id, start: 0, end: 0, replacement: '', current: '', alreadyApplied: true } };
59
+ return { ok: true, value: { ...identity, operationId: operation.id, start: 0, end: 0, replacement: '', current: '', alreadyApplied: true } };
58
60
  }
59
61
  if (operation.status !== 'portable') return { ok: false, reasonCodes: [`operation-not-portable:${operation.id}`] };
60
62
  const workerOffsets = spanOffsets(workerSourceText, operation.spans?.worker);
@@ -77,7 +79,7 @@ function sourceEditForOperation(operation, workerSourceText, headSourceText) {
77
79
  ok: true,
78
80
  value: {
79
81
  operationId: operation.id,
80
- ...semanticEditIdentity(operation),
82
+ ...identity,
81
83
  start: headOffsets.start,
82
84
  end: headOffsets.end,
83
85
  workerStart: workerOffsets.start,
@@ -88,12 +90,15 @@ function sourceEditForOperation(operation, workerSourceText, headSourceText) {
88
90
  };
89
91
  }
90
92
 
93
+ function projectionIdentity(operation) {
94
+ const identity = semanticEditIdentity(operation);
95
+ return { ...identity, sourcePath: operation.reanchor?.toSourcePath ?? identity.sourcePath };
96
+ }
97
+
91
98
  function projectionEditRecord(edit) {
92
99
  const deletedTextHash = hashSemanticValue(edit.current);
93
100
  const replacementTextHash = hashSemanticValue(edit.replacement);
94
- const semanticKey = semanticEditKey(edit);
95
- const semanticIdentityHash = hashSemanticValue(semanticIdentityRecord(edit, semanticKey));
96
- const sourceIdentityHash = hashSemanticValue(sourceIdentityRecord(edit));
101
+ const identity = semanticEditIdentityFields(edit);
97
102
  return compactRecord({
98
103
  operationId: edit.operationId,
99
104
  status: edit.alreadyApplied ? 'already-applied' : 'applied',
@@ -104,14 +109,18 @@ function projectionEditRecord(edit) {
104
109
  regionId: edit.regionId,
105
110
  regionKind: edit.regionKind,
106
111
  sourcePath: edit.sourcePath,
112
+ originalSourcePath: edit.originalSourcePath,
113
+ targetAnchorKey: edit.targetAnchorKey,
114
+ targetSourcePath: edit.targetSourcePath,
115
+ targetSymbolName: edit.targetSymbolName,
116
+ targetSymbolKind: edit.targetSymbolKind,
107
117
  symbolId: edit.symbolId,
108
118
  symbolName: edit.symbolName,
109
119
  symbolKind: edit.symbolKind,
110
- semanticKey,
111
- semanticIdentityHash,
112
- sourceIdentityHash,
120
+ ...identity,
121
+ operationContentHash: edit.operationContentHash,
113
122
  editContentHash: hashSemanticValue(compactRecord({
114
- semanticIdentityHash,
123
+ semanticIdentityHash: identity.semanticIdentityHash,
115
124
  deletedTextHash,
116
125
  replacementTextHash,
117
126
  status: edit.alreadyApplied ? 'already-applied' : 'applied'
@@ -128,35 +137,6 @@ function projectionEditRecord(edit) {
128
137
  });
129
138
  }
130
139
 
131
- function semanticEditKey(edit) {
132
- const scope = edit.symbolName
133
- ? `${edit.symbolKind ?? 'symbol'}:${edit.symbolName}`
134
- : edit.anchorKey ?? edit.regionId ?? edit.operationId;
135
- return ['semantic-edit', edit.kind ?? 'region', edit.changeKind ?? 'change', scope].filter(Boolean).join(':');
136
- }
137
-
138
- function semanticIdentityRecord(edit, semanticKey) {
139
- return compactRecord({
140
- semanticKey,
141
- kind: edit.kind,
142
- changeKind: edit.changeKind,
143
- regionKind: edit.regionKind,
144
- symbolName: edit.symbolName,
145
- symbolKind: edit.symbolKind
146
- });
147
- }
148
-
149
- function sourceIdentityRecord(edit) {
150
- return compactRecord({
151
- anchorKey: edit.anchorKey,
152
- conflictKey: edit.conflictKey,
153
- regionId: edit.regionId,
154
- sourcePath: edit.sourcePath,
155
- symbolId: edit.symbolId,
156
- semanticKey: semanticEditKey(edit)
157
- });
158
- }
159
-
160
140
  function semanticEditIdentity(operation) {
161
141
  const anchor = operation.anchor ?? {};
162
142
  return compactRecord({
@@ -167,9 +147,18 @@ function semanticEditIdentity(operation) {
167
147
  regionId: anchor.regionId,
168
148
  regionKind: anchor.regionKind,
169
149
  sourcePath: anchor.sourcePath,
150
+ originalSourcePath: operation.reanchor?.toSourcePath ? anchor.sourcePath : undefined,
151
+ targetAnchorKey: operation.reanchor?.toAnchorKey,
152
+ targetSourcePath: operation.reanchor?.toSourcePath,
153
+ targetSymbolName: operation.reanchor?.toSymbolName,
154
+ targetSymbolKind: operation.reanchor?.toSymbolKind,
170
155
  symbolId: anchor.symbolId,
171
156
  symbolName: anchor.symbolName,
172
- symbolKind: anchor.symbolKind
157
+ symbolKind: anchor.symbolKind,
158
+ semanticKey: operation.semanticKey,
159
+ semanticIdentityHash: operation.semanticIdentityHash,
160
+ sourceIdentityHash: operation.sourceIdentityHash,
161
+ operationContentHash: operation.operationContentHash
173
162
  });
174
163
  }
175
164
 
@@ -179,6 +168,10 @@ function applySourceEdits(sourceText, edits) {
179
168
  .reduce((text, edit) => text.slice(0, edit.start) + edit.replacement + text.slice(edit.end), sourceText);
180
169
  }
181
170
 
171
+ function projectedSourcePath(script, edits) {
172
+ return edits.map((edit) => edit.sourcePath).find(Boolean) ?? script.sourcePath;
173
+ }
174
+
182
175
  function spanOffsets(sourceText, span) {
183
176
  if (typeof sourceText !== 'string' || !span) return undefined;
184
177
  if (typeof span.start === 'number' && typeof span.end === 'number' && span.end >= span.start) return { start: span.start, end: span.end };
@@ -0,0 +1,54 @@
1
+ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+
3
+ export function semanticEditIdentityFields(record) {
4
+ const semanticKey = record.semanticKey ?? semanticEditKey(record);
5
+ return compactRecord({
6
+ semanticKey,
7
+ semanticIdentityHash: record.semanticIdentityHash ?? hashSemanticValue(semanticIdentityRecord(record, semanticKey)),
8
+ sourceIdentityHash: record.sourceIdentityHash ?? hashSemanticValue(sourceIdentityRecord(record, semanticKey))
9
+ });
10
+ }
11
+
12
+ export function semanticEditOperationContentHash(record) {
13
+ const semanticIdentityHash = record.semanticIdentityHash ?? semanticEditIdentityFields(record).semanticIdentityHash;
14
+ return hashSemanticValue(compactRecord({
15
+ semanticIdentityHash,
16
+ baseTextHash: record.baseTextHash,
17
+ workerTextHash: record.workerTextHash,
18
+ headTextHash: record.headTextHash,
19
+ status: record.status
20
+ }));
21
+ }
22
+
23
+ export function semanticEditKey(record) {
24
+ const scope = record.symbolName
25
+ ? `${record.symbolKind ?? 'symbol'}:${record.symbolName}`
26
+ : record.anchorKey ?? record.regionId ?? record.operationId ?? record.id;
27
+ return ['semantic-edit', record.kind ?? 'region', record.changeKind ?? 'change', scope].filter(Boolean).join(':');
28
+ }
29
+
30
+ function semanticIdentityRecord(record, semanticKey) {
31
+ return compactRecord({
32
+ semanticKey,
33
+ kind: record.kind,
34
+ changeKind: record.changeKind,
35
+ regionKind: record.regionKind,
36
+ symbolName: record.symbolName,
37
+ symbolKind: record.symbolKind
38
+ });
39
+ }
40
+
41
+ function sourceIdentityRecord(record, semanticKey) {
42
+ return compactRecord({
43
+ anchorKey: record.anchorKey,
44
+ conflictKey: record.conflictKey,
45
+ regionId: record.regionId,
46
+ sourcePath: record.sourcePath,
47
+ symbolId: record.symbolId,
48
+ semanticKey
49
+ });
50
+ }
51
+
52
+ function compactRecord(value) {
53
+ return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0)));
54
+ }
@@ -44,7 +44,11 @@ export function summarizeSemanticEditOperations(operations) {
44
44
  stale: byStatus.stale ?? 0,
45
45
  blocked: byStatus.blocked ?? 0,
46
46
  candidates: byStatus.candidate ?? 0,
47
- autoMergeCandidates: (byStatus.portable ?? 0) + (byStatus['already-applied'] ?? 0)
47
+ autoMergeCandidates: (byStatus.portable ?? 0) + (byStatus['already-applied'] ?? 0),
48
+ semanticKeys: uniqueStrings(operations.map((operation) => operation.semanticKey).filter(Boolean)),
49
+ semanticIdentityHashes: uniqueStrings(operations.map((operation) => operation.semanticIdentityHash).filter(Boolean)),
50
+ sourceIdentityHashes: uniqueStrings(operations.map((operation) => operation.sourceIdentityHash).filter(Boolean)),
51
+ operationContentHashes: uniqueStrings(operations.map((operation) => operation.operationContentHash).filter(Boolean))
48
52
  };
49
53
  }
50
54
 
@@ -88,12 +92,15 @@ function classifyMissingHeadAnchor(input) {
88
92
  ? resolveSemanticLineage(input.context.headLineage.lineageMap, { anchorKey: input.anchorKey })
89
93
  : undefined;
90
94
  if (resolved?.status === 'resolved' && resolved.currentAnchors.length === 1) {
91
- return editStatus('needs-port', 'needs-review', 0.72, ['anchor-moved-or-renamed'], {
92
- fromAnchorKey: input.anchorKey,
93
- toAnchorKey: resolved.currentAnchors[0].key,
94
- lineageStatus: resolved.status,
95
- traversedEventIds: resolved.traversedEventIds
96
- }, resolved.evidenceIds);
95
+ const target = resolved.currentAnchors[0];
96
+ const reanchor = reanchorRecord(input.anchorKey, target, resolved);
97
+ if (target.bodyHash && input.baseSymbol?.spanHash && target.bodyHash === input.baseSymbol.spanHash) {
98
+ return editStatus('portable', 'ready', 0.86, ['anchor-reanchored-head-matches-base'], reanchor, resolved.evidenceIds);
99
+ }
100
+ if (target.bodyHash && input.workerSymbol?.spanHash && target.bodyHash === input.workerSymbol.spanHash) {
101
+ return editStatus('already-applied', 'ready', 0.87, ['anchor-reanchored-head-matches-worker'], reanchor, resolved.evidenceIds);
102
+ }
103
+ return editStatus('needs-port', 'needs-review', 0.72, ['anchor-moved-or-renamed'], reanchor, resolved.evidenceIds);
97
104
  }
98
105
  if (resolved?.status === 'ambiguous') {
99
106
  return editStatus('blocked', 'blocked', 0.1, ['anchor-lineage-ambiguous'], { fromAnchorKey: input.anchorKey, lineageStatus: resolved.status }, resolved.evidenceIds);
@@ -108,6 +115,18 @@ function editStatus(status, readiness, confidence, reasonCodes, reanchor, eviden
108
115
  return { status, readiness, confidence, reasonCodes, reanchor, evidenceIds };
109
116
  }
110
117
 
118
+ function reanchorRecord(fromAnchorKey, target, resolved) {
119
+ return {
120
+ fromAnchorKey,
121
+ toAnchorKey: target.key,
122
+ toSourcePath: target.sourcePath,
123
+ toSymbolName: target.symbolName,
124
+ toSymbolKind: target.kind,
125
+ lineageStatus: resolved.status,
126
+ traversedEventIds: resolved.traversedEventIds
127
+ };
128
+ }
129
+
111
130
  function admissionStatus(summary) {
112
131
  if (summary.operations === 0) return 'evidence-only';
113
132
  if (summary.blocked > 0) return 'blocked';
@@ -13,6 +13,7 @@ import {
13
13
  summarizeSemanticEditOperations
14
14
  } from './semanticEditScriptClassification.js';
15
15
  import { sourceTextForSpan } from './sourceTextForSpan.js';
16
+ import { semanticEditIdentityFields, semanticEditOperationContentHash } from './semanticEditIdentityRecords.js';
16
17
 
17
18
  export { SemanticEditScriptAdmissionStatuses };
18
19
 
@@ -131,48 +132,55 @@ function semanticEditOperation(region, index, context, input) {
131
132
  const anchorKey = region.key ?? region.conflictKey ?? region.id;
132
133
  const baseSymbol = context.baseSymbols.get(anchorKey);
133
134
  const workerSymbol = context.workerSymbols.get(anchorKey);
134
- const headSymbol = context.headSymbols.get(anchorKey);
135
- const classification = classifySemanticEdit({ region, anchorKey, baseSymbol, workerSymbol, headSymbol, context });
135
+ const directHeadSymbol = context.headSymbols.get(anchorKey);
136
+ const classification = classifySemanticEdit({ region, anchorKey, baseSymbol, workerSymbol, headSymbol: directHeadSymbol, context });
137
+ const headSymbol = directHeadSymbol ?? reanchoredHeadSymbol(context, classification);
136
138
  const kind = semanticEditOperationKind(region);
137
139
  const baseText = spanText(context.base, baseSymbol?.sourceSpan ?? region.metadata?.changedRegionProjection?.before?.sourceSpan ?? region.sourceSpan);
138
140
  const workerText = spanText(context.worker, workerSymbol?.sourceSpan ?? region.metadata?.changedRegionProjection?.after?.sourceSpan ?? region.sourceSpan);
139
141
  const headText = spanText(context.head, headSymbol?.sourceSpan ?? region.metadata?.changedRegionProjection?.head?.sourceSpan ?? baseSymbol?.sourceSpan);
142
+ const anchor = compactRecord({
143
+ key: anchorKey,
144
+ conflictKey: region.conflictKey ?? `region:${anchorKey}`,
145
+ regionId: region.id,
146
+ regionKind: region.regionKind,
147
+ granularity: region.granularity,
148
+ language: region.language ?? context.workerChangeSet.language,
149
+ sourcePath: region.sourcePath ?? context.workerChangeSet.sourcePath,
150
+ symbolId: region.symbolId ?? workerSymbol?.id ?? baseSymbol?.id,
151
+ symbolName: region.symbolName ?? workerSymbol?.name ?? baseSymbol?.name,
152
+ symbolKind: region.symbolKind ?? workerSymbol?.kind ?? baseSymbol?.kind,
153
+ sourceSpan: workerSymbol?.sourceSpan ?? region.sourceSpan
154
+ });
155
+ const hashes = compactRecord({
156
+ baseSourceHash: context.workerChangeSet.beforeHash,
157
+ workerSourceHash: context.workerChangeSet.afterHash,
158
+ headSourceHash: context.headChangeSet?.afterHash,
159
+ baseSpanHash: baseSymbol?.spanHash,
160
+ workerSpanHash: workerSymbol?.spanHash,
161
+ headSpanHash: headSymbol?.spanHash,
162
+ baseTextHash: baseText === undefined ? undefined : hashSemanticValue(baseText),
163
+ workerTextHash: workerText === undefined ? undefined : hashSemanticValue(workerText),
164
+ headTextHash: headText === undefined ? undefined : hashSemanticValue(headText),
165
+ beforeSignatureHash: workerSymbol?.beforeSignatureHash ?? baseSymbol?.signatureHash,
166
+ afterSignatureHash: workerSymbol?.afterSignatureHash ?? workerSymbol?.signatureHash
167
+ });
168
+ const identityRecord = semanticEditIdentityRecord({ kind, region, anchor });
169
+ const identity = semanticEditIdentityFields(identityRecord);
140
170
  return compactRecord({
141
171
  id: `semantic_edit_op_${idFragment(firstString(input.id, anchorKey, index))}`,
142
172
  kind,
143
173
  changeKind: region.changeKind,
144
- anchor: compactRecord({
145
- key: anchorKey,
146
- conflictKey: region.conflictKey ?? `region:${anchorKey}`,
147
- regionId: region.id,
148
- regionKind: region.regionKind,
149
- granularity: region.granularity,
150
- language: region.language ?? context.workerChangeSet.language,
151
- sourcePath: region.sourcePath ?? context.workerChangeSet.sourcePath,
152
- symbolId: region.symbolId ?? workerSymbol?.id ?? baseSymbol?.id,
153
- symbolName: region.symbolName ?? workerSymbol?.name ?? baseSymbol?.name,
154
- symbolKind: region.symbolKind ?? workerSymbol?.kind ?? baseSymbol?.kind,
155
- sourceSpan: workerSymbol?.sourceSpan ?? region.sourceSpan
156
- }),
174
+ anchor,
175
+ ...identity,
157
176
  spans: compactRecord({
158
177
  base: baseSymbol?.sourceSpan ?? region.metadata?.changedRegionProjection?.before?.sourceSpan,
159
178
  worker: workerSymbol?.sourceSpan ?? region.metadata?.changedRegionProjection?.after?.sourceSpan ?? region.sourceSpan,
160
179
  head: headSymbol?.sourceSpan
161
180
  }),
162
- hashes: compactRecord({
163
- baseSourceHash: context.workerChangeSet.beforeHash,
164
- workerSourceHash: context.workerChangeSet.afterHash,
165
- headSourceHash: context.headChangeSet?.afterHash,
166
- baseSpanHash: baseSymbol?.spanHash,
167
- workerSpanHash: workerSymbol?.spanHash,
168
- headSpanHash: headSymbol?.spanHash,
169
- baseTextHash: baseText === undefined ? undefined : hashSemanticValue(baseText),
170
- workerTextHash: workerText === undefined ? undefined : hashSemanticValue(workerText),
171
- headTextHash: headText === undefined ? undefined : hashSemanticValue(headText),
172
- beforeSignatureHash: workerSymbol?.beforeSignatureHash ?? baseSymbol?.signatureHash,
173
- afterSignatureHash: workerSymbol?.afterSignatureHash ?? workerSymbol?.signatureHash
174
- }),
181
+ hashes,
175
182
  status: classification.status,
183
+ operationContentHash: semanticEditOperationContentHash({ ...identityRecord, ...identity, ...hashes, status: classification.status }),
176
184
  reanchor: classification.reanchor,
177
185
  readiness: classification.readiness,
178
186
  confidence: classification.confidence,
@@ -185,6 +193,26 @@ function semanticEditOperation(region, index, context, input) {
185
193
  });
186
194
  }
187
195
 
196
+ function reanchoredHeadSymbol(context, classification) {
197
+ const targetKey = classification.reanchor?.toAnchorKey;
198
+ return targetKey ? context.headSymbols.get(targetKey) : undefined;
199
+ }
200
+
201
+ function semanticEditIdentityRecord(input) {
202
+ return {
203
+ kind: input.kind,
204
+ changeKind: input.region.changeKind,
205
+ anchorKey: input.anchor.key,
206
+ conflictKey: input.anchor.conflictKey,
207
+ regionId: input.anchor.regionId,
208
+ regionKind: input.anchor.regionKind,
209
+ sourcePath: input.anchor.sourcePath,
210
+ symbolId: input.anchor.symbolId,
211
+ symbolName: input.anchor.symbolName,
212
+ symbolKind: input.anchor.symbolKind
213
+ };
214
+ }
215
+
188
216
  function semanticEditOperationKind(region) {
189
217
  const prefix = region.changeKind === 'added' ? 'add' : region.changeKind === 'removed' ? 'remove' : 'replace';
190
218
  const kind = String(region.regionKind ?? region.granularity ?? 'region');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.84",
3
+ "version": "0.2.86",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",