@neocompose/cli 0.57.3 → 0.58.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.58.1] - 2026-09-24
4
+
5
+ - Push writes `@id` on list entries nested inside a new computed row, such as the items of a list on a new `new Event(...) { ... }` entry, so `neo status` is clean afterwards. Before, those entries stayed without ids and status kept reporting changes.
6
+ - A new row that names a stored row's `@id`, such as a row moved or copied under a new parent, is an error that names the `@id`, in `neo status` and `neo push --dry-run` too. Before, dry run passed and push either failed with a base-hash-conflict or committed a row that still referenced the deleted original. Drop the `@id` to create a new row.
7
+ - Source that places one stored row twice, such as a list entry copied with its `@id` into the same or another list or dictionary, is an error. Before, the push stored the same row at two positions.
8
+ - A negative number after `@id`, such as `[@id("…") -3]`, parses as the annotated entry, and a push of a new list with negative entries writes its ids. Before, `@id` could not precede `-3`, so the push failed after the server had applied it.
9
+ - A stored enum value may be written `Direction.North` as well as `.North`. Before, `neo status` failed with "Enum member … requires contextual .Option values."
10
+ - A push that deletes a row and creates a new one with the same localized text, such as re-creating a part under a new parent, updates source after it commits. Before, it failed with "Localizable value references missing localized text" after the server had already applied it.
11
+
12
+ ## [0.58.0] - 2026-09-24
13
+
14
+ - **Behavior change:** NeoScript cell queries the CLI evaluates, such as in `neo test`, no longer return an object that has no placement tiles. Before, such an object answered for its origin cell. This matches the Unity SDK (0.43.0) and the web editor: `PlacementTiles` are the cells an object occupies, and without them it occupies none.
15
+
3
16
  ## [0.57.3] - 2026-09-24
4
17
 
5
18
  - Status, pull, and push no longer copy a shared row passed to a constructor when they check a stored construction. Before, a constructor argument that named a shared row, such as a schedule event's `area`, copied that row's whole graph for every construction. Once an area grid held a few placed characters, this failed with "NeoScript constructed Session row limit of 4096 exceeded."
package/dist/neo.mjs CHANGED
@@ -3672,7 +3672,8 @@ var init_executable_parser = __esm({
3672
3672
  return {
3673
3673
  kind: "annotated",
3674
3674
  annotations,
3675
- expression: this.parsePostfix(),
3675
+ // Unary, so `@id("…") -3` annotates the negative entry.
3676
+ expression: this.parseUnary(),
3676
3677
  pos: at(t)
3677
3678
  };
3678
3679
  }
@@ -45372,17 +45373,12 @@ function lowerDictionaryKey(expression, keyEnum, path) {
45372
45373
  if (keyEnum === null) {
45373
45374
  throw new Error(`Dictionary ${path} requires string keys.`);
45374
45375
  }
45375
- let optionName;
45376
- if (expression.kind === "contextualEnum") {
45377
- optionName = expression.name;
45378
- } else if (expression.kind === "member" && expression.receiver.kind === "ident") {
45379
- if (expression.receiver.name !== keyEnum.name) {
45380
- throw new Error(
45381
- `Dictionary key at ${path} names enum ${JSON.stringify(expression.receiver.name)}, expected ${JSON.stringify(keyEnum.name)}.`
45382
- );
45383
- }
45384
- optionName = expression.name;
45385
- } else {
45376
+ const optionName = enumOptionSpelling(
45377
+ expression,
45378
+ keyEnum.name,
45379
+ `Dictionary key at ${path}`
45380
+ );
45381
+ if (optionName === null) {
45386
45382
  throw new Error(
45387
45383
  `Dictionary key at ${path} must be .Option, ${keyEnum.name}.Option, or a quoted option ID.`
45388
45384
  );
@@ -45395,6 +45391,17 @@ function lowerDictionaryKey(expression, keyEnum, path) {
45395
45391
  }
45396
45392
  return optionId;
45397
45393
  }
45394
+ function enumOptionSpelling(expression, enumName, subject) {
45395
+ if (expression.kind === "contextualEnum") return expression.name;
45396
+ if (expression.kind !== "member") return null;
45397
+ if (expression.receiver.kind !== "ident") return null;
45398
+ if (expression.receiver.name !== enumName) {
45399
+ throw new Error(
45400
+ `${subject} names enum ${JSON.stringify(expression.receiver.name)}, expected ${JSON.stringify(enumName)}.`
45401
+ );
45402
+ }
45403
+ return expression.name;
45404
+ }
45398
45405
  function emitDictionaryKey(key, options) {
45399
45406
  const name = options?.get(key)?.name;
45400
45407
  return name === void 0 ? quoteNeoString(key) : `.${name}`;
@@ -64310,9 +64317,9 @@ var init_dialogue_sources = __esm({
64310
64317
  if (typeof memberId !== "string") return null;
64311
64318
  const nameMember = this.members.get(memberId);
64312
64319
  if (nameMember?.kind !== 3) return null;
64313
- const childValueId = value.value[memberName];
64314
- if (typeof childValueId !== "string") return null;
64315
- const child = this.values.get(childValueId);
64320
+ const childValueId2 = value.value[memberName];
64321
+ if (typeof childValueId2 !== "string") return null;
64322
+ const child = this.values.get(childValueId2);
64316
64323
  if (typeof child?.value !== "string") return null;
64317
64324
  const localizedText = this.localizedTexts.get(child.value);
64318
64325
  const localeValues = isObjectRecord2(localizedText?.localeValues) ? localizedText.localeValues : null;
@@ -67652,13 +67659,13 @@ function resolveOwnerMembersForValues(document, targetValueIds, additionalRoots,
67652
67659
  const env = overlayStamp(instanceEnv, body.genericBindings);
67653
67660
  for (const entry of merged) {
67654
67661
  if (entry.memberId === null) continue;
67655
- const childValueId = body.value[entry.schemaKey];
67656
- if (childValueId === void 0) continue;
67662
+ const childValueId2 = body.value[entry.schemaKey];
67663
+ if (childValueId2 === void 0) continue;
67657
67664
  const childMember = membersById.get(entry.memberId);
67658
67665
  if (childMember === void 0) continue;
67659
67666
  const substituted = trySubstituteMember(childMember, env);
67660
67667
  if (substituted === null) continue;
67661
- searchRow(substituted, childValueId, rootOwner, env, rootKind);
67668
+ searchRow(substituted, childValueId2, rootOwner, env, rootKind);
67662
67669
  }
67663
67670
  return;
67664
67671
  }
@@ -67669,8 +67676,8 @@ function resolveOwnerMembersForValues(document, targetValueIds, additionalRoots,
67669
67676
  const entryEnv = overlayStamp(ambientEnv, body.genericBindings);
67670
67677
  const entryMember = trySubstituteMember(entryMemberRecord, entryEnv);
67671
67678
  if (entryMember === null) return;
67672
- for (const childValueId of Object.values(body.value)) {
67673
- searchRow(entryMember, childValueId, rootOwner, entryEnv, rootKind);
67679
+ for (const childValueId2 of Object.values(body.value)) {
67680
+ searchRow(entryMember, childValueId2, rootOwner, entryEnv, rootKind);
67674
67681
  }
67675
67682
  return;
67676
67683
  }
@@ -67688,9 +67695,9 @@ function resolveOwnerMembersForValues(document, targetValueIds, additionalRoots,
67688
67695
  return;
67689
67696
  }
67690
67697
  if (!Array.isArray(body.value)) return;
67691
- for (const childValueId of body.value) {
67692
- if (typeof childValueId !== "string") continue;
67693
- searchRow(entryMember, childValueId, rootOwner, entryEnv, rootKind);
67698
+ for (const childValueId2 of body.value) {
67699
+ if (typeof childValueId2 !== "string") continue;
67700
+ searchRow(entryMember, childValueId2, rootOwner, entryEnv, rootKind);
67694
67701
  }
67695
67702
  return;
67696
67703
  }
@@ -67811,14 +67818,14 @@ function findOwningMembersForValues(document, valueIds, additionalRoots, travers
67811
67818
  args.genericBindings
67812
67819
  );
67813
67820
  for (const entry of merged) {
67814
- const childValueId = args.record[entry.schemaKey];
67815
- if (childValueId === void 0) continue;
67821
+ const childValueId2 = args.record[entry.schemaKey];
67822
+ if (childValueId2 === void 0) continue;
67816
67823
  const childMember = membersById.get(entry.memberId);
67817
67824
  if (childMember === void 0) continue;
67818
67825
  queue.push({
67819
67826
  member: substituteMember(childMember, env, document.members),
67820
67827
  memberRecordId: childMember.id,
67821
- id: childValueId,
67828
+ id: childValueId2,
67822
67829
  parentValueId: args.parentValueId,
67823
67830
  env,
67824
67831
  // Descending through `Overrides` opens an override graph, and every
@@ -67978,11 +67985,11 @@ function findOwningMembersForValues(document, valueIds, additionalRoots, travers
67978
67985
  env,
67979
67986
  document.members
67980
67987
  );
67981
- for (const childValueId of Object.values(storedValue.value)) {
67988
+ for (const childValueId2 of Object.values(storedValue.value)) {
67982
67989
  queue.push({
67983
67990
  member: entryMember,
67984
67991
  memberRecordId: entryMemberRecord.id,
67985
- id: childValueId,
67992
+ id: childValueId2,
67986
67993
  // An entry inherits its collection's position: a Dictionary is not
67987
67994
  // itself a way into an override graph.
67988
67995
  insideAnimationOverrideGraph: current.insideAnimationOverrideGraph,
@@ -68002,11 +68009,11 @@ function findOwningMembersForValues(document, valueIds, additionalRoots, travers
68002
68009
  document.members
68003
68010
  );
68004
68011
  const childValueIds = listKindOf(current.member) === 1 /* Unordered */ ? (rowsByContainerId.get(storedValue.id) ?? []).map((row) => row.id) : isStringListValue(storedValue.value) ? storedValue.value : [];
68005
- for (const childValueId of childValueIds) {
68012
+ for (const childValueId2 of childValueIds) {
68006
68013
  queue.push({
68007
68014
  member: entryMember,
68008
68015
  memberRecordId: entryMemberRecord.id,
68009
- id: childValueId,
68016
+ id: childValueId2,
68010
68017
  // An entry inherits its collection's position: a List is not itself
68011
68018
  // a way into an override graph.
68012
68019
  insideAnimationOverrideGraph: current.insideAnimationOverrideGraph,
@@ -70414,7 +70421,6 @@ function buildIndex(source) {
70414
70421
  seen.add(key);
70415
70422
  }
70416
70423
  }
70417
- if (footprint.length === 0) footprint.push(cell);
70418
70424
  const placement = {
70419
70425
  valueId,
70420
70426
  isNeoObject: kinds.get(row.classId) === NeoWorldSystemClassKind.Object,
@@ -81751,16 +81757,16 @@ function createStoredValuePlacementTraversal(args, trackReferences = false) {
81751
81757
  if (settled === null || Object.hasOwn(value.value, settled.schemaKey)) {
81752
81758
  continue;
81753
81759
  }
81754
- const childValueId = value.constructorArgs[constructorActionParameterId(constructor2, index)];
81755
- if (typeof childValueId === "string") {
81756
- settledChildrenBySchemaKey.set(settled.schemaKey, childValueId);
81760
+ const childValueId2 = value.constructorArgs[constructorActionParameterId(constructor2, index)];
81761
+ if (typeof childValueId2 === "string") {
81762
+ settledChildrenBySchemaKey.set(settled.schemaKey, childValueId2);
81757
81763
  }
81758
81764
  }
81759
81765
  }
81760
81766
  for (const entry of storedSchema(classId)) {
81761
81767
  const childMember = membersById.get(entry.memberId);
81762
- const childValueId = value.value[entry.schemaKey] ?? settledChildrenBySchemaKey.get(entry.schemaKey);
81763
- if (childMember === void 0 || typeof childValueId !== "string") {
81768
+ const childValueId2 = value.value[entry.schemaKey] ?? settledChildrenBySchemaKey.get(entry.schemaKey);
81769
+ if (childMember === void 0 || typeof childValueId2 !== "string") {
81764
81770
  continue;
81765
81771
  }
81766
81772
  if (memberAccessOf(childMember) !== 0 /* Public */) continue;
@@ -81768,7 +81774,7 @@ function createStoredValuePlacementTraversal(args, trackReferences = false) {
81768
81774
  memberId: childMember.id,
81769
81775
  parentValueId: value.id,
81770
81776
  source: { kind: "classField", schemaKey: entry.schemaKey },
81771
- valueId: childValueId
81777
+ valueId: childValueId2
81772
81778
  });
81773
81779
  }
81774
81780
  continue;
@@ -81780,16 +81786,16 @@ function createStoredValuePlacementTraversal(args, trackReferences = false) {
81780
81786
  if (entryMember === void 0) continue;
81781
81787
  if (isMemberDictionaryBase(member)) {
81782
81788
  if (!isStringRecord4(value.value)) continue;
81783
- for (const [key, childValueId] of Object.entries(value.value)) {
81789
+ for (const [key, childValueId2] of Object.entries(value.value)) {
81784
81790
  queue.push({
81785
81791
  memberId: entryMember.id,
81786
81792
  parentValueId: value.id,
81787
81793
  source: {
81788
81794
  kind: "collectionEntry",
81789
81795
  containerMemberId: storedMember.id,
81790
- entry: { kind: "dict", key, valueId: childValueId }
81796
+ entry: { kind: "dict", key, valueId: childValueId2 }
81791
81797
  },
81792
- valueId: childValueId
81798
+ valueId: childValueId2
81793
81799
  });
81794
81800
  }
81795
81801
  continue;
@@ -81797,9 +81803,9 @@ function createStoredValuePlacementTraversal(args, trackReferences = false) {
81797
81803
  const entries = listKindOf(member) === 1 /* Unordered */ ? args.indexedRows === void 0 ? unorderedEntriesByContainerId.get(value.id) ?? [] : args.indexedRows.unorderedListEntryIds(value.id).flatMap((id2) => {
81798
81804
  const row = valuesById.get(id2);
81799
81805
  return row === void 0 ? [] : [row];
81800
- }) : Array.isArray(value.value) ? value.value.flatMap((childValueId) => {
81801
- if (typeof childValueId !== "string") return [];
81802
- const childValue = valuesById.get(childValueId);
81806
+ }) : Array.isArray(value.value) ? value.value.flatMap((childValueId2) => {
81807
+ if (typeof childValueId2 !== "string") return [];
81808
+ const childValue = valuesById.get(childValueId2);
81803
81809
  return childValue === void 0 ? [] : [childValue];
81804
81810
  }) : [];
81805
81811
  for (const [index, childValue] of entries.entries()) {
@@ -93980,8 +93986,8 @@ function findParentValues(valueId, values) {
93980
93986
  continue;
93981
93987
  }
93982
93988
  if (!isStringRecord6(value.value)) continue;
93983
- for (const [key, childValueId] of Object.entries(value.value)) {
93984
- if (childValueId === valueId) parents.push({ value, key });
93989
+ for (const [key, childValueId2] of Object.entries(value.value)) {
93990
+ if (childValueId2 === valueId) parents.push({ value, key });
93985
93991
  }
93986
93992
  }
93987
93993
  return parents;
@@ -96652,8 +96658,8 @@ function inspectClass(options, member) {
96652
96658
  options.index.members
96653
96659
  );
96654
96660
  if (!memberKindSupportsStorage(childMember.kind)) continue;
96655
- const childValueId = body[entry.schemaKey];
96656
- if (childValueId === void 0) {
96661
+ const childValueId2 = body[entry.schemaKey];
96662
+ if (childValueId2 === void 0) {
96657
96663
  if (isRequiredMember2(childMember) && !options.allowMissingRequiredClassFields) {
96658
96664
  fail(
96659
96665
  `${options.path} is missing required schema key "${entry.schemaKey}".`
@@ -96663,7 +96669,7 @@ function inspectClass(options, member) {
96663
96669
  }
96664
96670
  children.push({
96665
96671
  member: childMember,
96666
- valueId: childValueId,
96672
+ valueId: childValueId2,
96667
96673
  path: `${options.path}.${entry.schemaKey}`,
96668
96674
  genericEnv: environment,
96669
96675
  insideAnimationOverrideGraph: options.insideAnimationOverrideGraph || entersAnimationOverrideGraph(entry.memberId)
@@ -106732,10 +106738,10 @@ function repointedChildValueIds(document, change) {
106732
106738
  if (!isPlainRecord4(change.nextData)) return [];
106733
106739
  const after = change.nextData.value;
106734
106740
  if (!isPlainRecord4(after)) return [];
106735
- return Object.entries(before).flatMap(([schemaKey, childValueId]) => {
106736
- if (typeof childValueId !== "string") return [];
106737
- if (after[schemaKey] === childValueId) return [];
106738
- return [childValueId];
106741
+ return Object.entries(before).flatMap(([schemaKey, childValueId2]) => {
106742
+ if (typeof childValueId2 !== "string") return [];
106743
+ if (after[schemaKey] === childValueId2) return [];
106744
+ return [childValueId2];
106739
106745
  });
106740
106746
  }
106741
106747
  function authoredFieldsEqual(recordKind, base, next) {
@@ -115997,12 +116003,12 @@ function buildStoredValuePlacementIndexV4(records2) {
115997
116003
  const classId = typeof value.classId === "string" ? value.classId : null;
115998
116004
  const body = value.value;
115999
116005
  if (isObjectRecord2(body)) {
116000
- for (const [schemaKey, childValueId] of Object.entries(body)) {
116001
- if (typeof childValueId !== "string" || !values.has(childValueId)) {
116006
+ for (const [schemaKey, childValueId2] of Object.entries(body)) {
116007
+ if (typeof childValueId2 !== "string" || !values.has(childValueId2)) {
116002
116008
  continue;
116003
116009
  }
116004
116010
  add({
116005
- childValueId,
116011
+ childValueId: childValueId2,
116006
116012
  memberId: classId === null ? null : memberIdForSchemaKey(classId, schemaKey),
116007
116013
  parentValueId,
116008
116014
  schemaKey,
@@ -116030,8 +116036,8 @@ function buildStoredValuePlacementIndexV4(records2) {
116030
116036
  continue;
116031
116037
  }
116032
116038
  const parameterId = constructorActionParameterId(constructor2, index);
116033
- const childValueId = constructorArgs[parameterId];
116034
- if (typeof childValueId !== "string" || !values.has(childValueId) || !memberStoresAggregate(
116039
+ const childValueId2 = constructorArgs[parameterId];
116040
+ if (typeof childValueId2 !== "string" || !values.has(childValueId2) || !memberStoresAggregate(
116035
116041
  memberDataById.get(settled.memberId),
116036
116042
  value,
116037
116043
  memberDataById,
@@ -116042,7 +116048,7 @@ function buildStoredValuePlacementIndexV4(records2) {
116042
116048
  continue;
116043
116049
  }
116044
116050
  add({
116045
- childValueId,
116051
+ childValueId: childValueId2,
116046
116052
  memberId: settled.memberId,
116047
116053
  parentValueId,
116048
116054
  schemaKey: settled.schemaKey,
@@ -117767,16 +117773,7 @@ function computeWorkspaceStatus(workspace, options) {
117767
117773
  projectSources.push({ uri: relPath, source, kind: sourceKind });
117768
117774
  }
117769
117775
  if (conflictedFiles.length > 0) {
117770
- return {
117771
- changes: [],
117772
- conflictedFiles,
117773
- parseErrors,
117774
- parseWarnings,
117775
- reconstructed: /* @__PURE__ */ new Map(),
117776
- authoredValueSeeds: /* @__PURE__ */ new Map(),
117777
- binaryChanges: [],
117778
- binaryFiles: []
117779
- };
117776
+ return refusedStatus(conflictedFiles, parseErrors, parseWarnings);
117780
117777
  }
117781
117778
  const { records: loweringRecords, residency: packedResidency } = logicalValueBaseRecordsV4(loweringBaseRecords(workspace.state.records));
117782
117779
  const baseDocuments = schemaBaseDocuments(workspace);
@@ -117908,16 +117905,7 @@ function computeWorkspaceStatus(workspace, options) {
117908
117905
  1
117909
117906
  )
117910
117907
  );
117911
- return {
117912
- changes: [],
117913
- conflictedFiles,
117914
- parseErrors,
117915
- parseWarnings,
117916
- reconstructed: /* @__PURE__ */ new Map(),
117917
- authoredValueSeeds: /* @__PURE__ */ new Map(),
117918
- binaryChanges: [],
117919
- binaryFiles: []
117920
- };
117908
+ return refusedStatus(conflictedFiles, parseErrors, parseWarnings);
117921
117909
  }
117922
117910
  const sourceByKey = manifestRecordSources(manifest);
117923
117911
  const documents = projectSchemaManifestToDocuments(
@@ -118220,8 +118208,7 @@ function computeWorkspaceStatus(workspace, options) {
118220
118208
  if (reconstructed3.has(key)) continue;
118221
118209
  if (brokenFiles.has(recordFile)) continue;
118222
118210
  if (recordState.conflictServerHash === null) continue;
118223
- if (recordState.recordKind === "localized-text" && isLocalizedText(recordState.data) && recordState.data.memberSource !== void 0)
118224
- continue;
118211
+ if (isSharedMemberText(recordState)) continue;
118225
118212
  if (recordState.recordKind === "value") {
118226
118213
  deletedValueIds.add(recordState.recordId);
118227
118214
  }
@@ -118235,6 +118222,9 @@ function computeWorkspaceStatus(workspace, options) {
118235
118222
  casBaseHash: recordState.conflictServerHash !== void 0 ? recordState.conflictServerHash : recordState.contentHash
118236
118223
  });
118237
118224
  }
118225
+ const sourceTextByUri = new Map(
118226
+ sourceEntries.map((entry) => [entry.relPath, entry.source])
118227
+ );
118238
118228
  const referenceFailures = drainValueReferenceObligationsV4(
118239
118229
  prospectiveState,
118240
118230
  manifest,
@@ -118242,9 +118232,7 @@ function computeWorkspaceStatus(workspace, options) {
118242
118232
  registry: valueLowerRegistry,
118243
118233
  analysis: projectAnalysisV4,
118244
118234
  deletedValueIds,
118245
- sourceTextByUri: new Map(
118246
- sourceEntries.map((entry) => [entry.relPath, entry.source])
118247
- )
118235
+ sourceTextByUri
118248
118236
  }
118249
118237
  );
118250
118238
  for (const failure of referenceFailures) {
@@ -118261,16 +118249,7 @@ function computeWorkspaceStatus(workspace, options) {
118261
118249
  }
118262
118250
  reportPhase("record-diff-references", "Checking packed values\u2026");
118263
118251
  if (referenceFailures.length > 0) {
118264
- return {
118265
- changes: [],
118266
- conflictedFiles,
118267
- parseErrors,
118268
- parseWarnings,
118269
- reconstructed: /* @__PURE__ */ new Map(),
118270
- authoredValueSeeds: /* @__PURE__ */ new Map(),
118271
- binaryChanges: [],
118272
- binaryFiles: []
118273
- };
118252
+ return refusedStatus(conflictedFiles, parseErrors, parseWarnings);
118274
118253
  }
118275
118254
  const unanchoredValueDeletes = unanchoredValueDeleteIds({
118276
118255
  records: workspace.state.records,
@@ -118294,16 +118273,7 @@ function computeWorkspaceStatus(workspace, options) {
118294
118273
  "value-base-desync"
118295
118274
  )
118296
118275
  );
118297
- return {
118298
- changes: [],
118299
- conflictedFiles,
118300
- parseErrors,
118301
- parseWarnings,
118302
- reconstructed: /* @__PURE__ */ new Map(),
118303
- authoredValueSeeds: /* @__PURE__ */ new Map(),
118304
- binaryChanges: [],
118305
- binaryFiles: []
118306
- };
118276
+ return refusedStatus(conflictedFiles, parseErrors, parseWarnings);
118307
118277
  }
118308
118278
  parseWarnings.push(
118309
118279
  new SchemaSourceError(
@@ -118316,6 +118286,23 @@ function computeWorkspaceStatus(workspace, options) {
118316
118286
  )
118317
118287
  );
118318
118288
  }
118289
+ if (valueLowerRegistry.storedRowClaims.size > 0) {
118290
+ for (const claim of valueLowerRegistry.storedRowClaims.values()) {
118291
+ const position = composeReferenceSitePosition(
118292
+ claim.site,
118293
+ sourceTextByUri
118294
+ );
118295
+ parseErrors.push(
118296
+ new SchemaSourceError(
118297
+ claim.message,
118298
+ position.file,
118299
+ position.line,
118300
+ position.column
118301
+ )
118302
+ );
118303
+ }
118304
+ return refusedStatus(conflictedFiles, parseErrors, parseWarnings);
118305
+ }
118319
118306
  for (let index = changes.length - 1; index >= 0; index -= 1) {
118320
118307
  const change = changes[index];
118321
118308
  if (change === void 0 || change.kind !== "update") continue;
@@ -119502,6 +119489,21 @@ function summarizeFieldDiff(baseData3, nextData) {
119502
119489
  }
119503
119490
  return lines;
119504
119491
  }
119492
+ function isSharedMemberText(record6) {
119493
+ return record6.recordKind === "localized-text" && isLocalizedText(record6.data) && record6.data.memberSource !== void 0;
119494
+ }
119495
+ function refusedStatus(conflictedFiles, parseErrors, parseWarnings) {
119496
+ return {
119497
+ changes: [],
119498
+ conflictedFiles,
119499
+ parseErrors,
119500
+ parseWarnings,
119501
+ reconstructed: /* @__PURE__ */ new Map(),
119502
+ authoredValueSeeds: /* @__PURE__ */ new Map(),
119503
+ binaryChanges: [],
119504
+ binaryFiles: []
119505
+ };
119506
+ }
119505
119507
  var ANIMATION_DOCUMENT_RECORD_KINDS, ANIMATION_REPLAY_RECORD_KINDS, PROSPECTIVE_ANIMATION_RECORD_KINDS;
119506
119508
  var init_workspace_status_core = __esm({
119507
119509
  "src/project-source/workspace-status-core.ts"() {
@@ -121563,8 +121565,8 @@ function animationConstructorProjectionTargetValueIds(context, classId, value) {
121563
121565
  classId,
121564
121566
  projection.memberId
121565
121567
  );
121566
- const childValueId = schemaKey === null ? void 0 : value.value[schemaKey];
121567
- const childValue = typeof childValueId === "string" ? context.values.get(childValueId) : void 0;
121568
+ const childValueId2 = schemaKey === null ? void 0 : value.value[schemaKey];
121569
+ const childValue = typeof childValueId2 === "string" ? context.values.get(childValueId2) : void 0;
121568
121570
  const projectedMember = context.members.get(projection.memberId);
121569
121571
  if (childValue !== void 0 && projectedMember !== void 0 && numberField(projectedMember, "kind") === 9 /* Lookup */ && Array.isArray(childValue.value) && typeof childValue.value[0] === "string") {
121570
121572
  targetIds.push(childValue.value[0]);
@@ -121717,6 +121719,8 @@ function createValueLowerRegistryV4(options = {}) {
121717
121719
  referenceObligations: [],
121718
121720
  initAuthoredRowIds: /* @__PURE__ */ new Map(),
121719
121721
  loweringFailures: [],
121722
+ storedRowClaims: /* @__PURE__ */ new Map(),
121723
+ placedStoredRowIds: /* @__PURE__ */ new Set(),
121720
121724
  pendingValueIdentitySites: /* @__PURE__ */ new Map(),
121721
121725
  initializerReconciliations: /* @__PURE__ */ new Map()
121722
121726
  };
@@ -121849,6 +121853,8 @@ function buildValueLowerContext(state, manifest, options = {}) {
121849
121853
  ),
121850
121854
  referenceObligations: registry.referenceObligations,
121851
121855
  loweringFailures: registry.loweringFailures,
121856
+ storedRowClaims: registry.storedRowClaims,
121857
+ placedStoredRowIds: registry.placedStoredRowIds,
121852
121858
  pendingValueIdentitySites: registry.pendingValueIdentitySites,
121853
121859
  initializerReconciliations: registry.initializerReconciliations,
121854
121860
  materializedConstructorExpressions: registry.materializedConstructorExpressions,
@@ -121954,14 +121960,16 @@ function entrySliceRequiresEvaluation(entry, declaredConstructors2, entryClassNa
121954
121960
  }
121955
121961
  }
121956
121962
  function indexAuthoredRowIds(index, initSource, owner) {
121957
- const pattern = /@id\(\s*"((?:[^"\\]|\\.)*)"\s*\)/g;
121958
- let match = pattern.exec(initSource);
121959
- while (match !== null) {
121963
+ for (const authored of authoredRowIds(initSource)) {
121964
+ index.set(authored, owner);
121965
+ }
121966
+ }
121967
+ function* authoredRowIds(initSource) {
121968
+ for (const match of initSource.matchAll(
121969
+ /@id\(\s*"((?:[^"\\]|\\.)*)"\s*\)/g
121970
+ )) {
121960
121971
  const authored = match[1];
121961
- if (authored !== void 0 && authored.length > 0) {
121962
- index.set(authored, owner);
121963
- }
121964
- match = pattern.exec(initSource);
121972
+ if (authored !== void 0 && authored.length > 0) yield authored;
121965
121973
  }
121966
121974
  }
121967
121975
  function indexDeclaredInitializers(analysis) {
@@ -123538,7 +123546,8 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
123538
123546
  expressionPosition: {
123539
123547
  line: sourceExpression.pos.line,
123540
123548
  column: sourceExpression.pos.column
123541
- }
123549
+ },
123550
+ nestedListEntries: idlessNestedListEntries(context, expression)
123542
123551
  });
123543
123552
  }
123544
123553
  const code = initializerExpressionSlice(authoredSlice);
@@ -123654,9 +123663,9 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
123654
123663
  );
123655
123664
  }
123656
123665
  const childPath = `${path}.${schemaKey}`;
123657
- const childValueId = pendingNestedValueId(source, childPath);
123658
- if (rows.has(childValueId)) {
123659
- throw new Error(`Value construction reuses identity ${childValueId}.`);
123666
+ const childValueId2 = pendingNestedValueId(source, childPath);
123667
+ if (rows.has(childValueId2)) {
123668
+ throw new Error(`Value construction reuses identity ${childValueId2}.`);
123660
123669
  }
123661
123670
  if (projectedMember.kind === "lookup") {
123662
123671
  if (argument2.kind !== "litString") {
@@ -123854,6 +123863,13 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
123854
123863
  registerSeedRow(context, rows, row, source, sourceExpression);
123855
123864
  return row;
123856
123865
  }
123866
+ function* storedRowIdsRecreatedBy(context, row) {
123867
+ if (context.state[`value:${row.id}`] !== void 0) yield row.id;
123868
+ if (row.nodeType !== "initializer") return;
123869
+ for (const authored of authoredRowIds(row.code)) {
123870
+ if (context.state[`value:${authored}`] !== void 0) yield authored;
123871
+ }
123872
+ }
123857
123873
  function registerSeedRow(context, rows, row, source, expression) {
123858
123874
  if (rows.has(row.id)) {
123859
123875
  throw new Error(`Value construction reuses identity ${row.id}.`);
@@ -123866,6 +123882,15 @@ function registerSeedRow(context, rows, row, source, expression) {
123866
123882
  rows.set(row.id, row);
123867
123883
  return;
123868
123884
  }
123885
+ if (!context.structuralConstruction) {
123886
+ for (const storedId of storedRowIdsRecreatedBy(context, row)) {
123887
+ if (context.storedRowClaims.has(storedId)) continue;
123888
+ context.storedRowClaims.set(storedId, {
123889
+ message: `Value ${source.label} names existing row @id(${JSON.stringify(storedId)}) inside a new row. A row keeps its parent: drop the @id to create a new row.`,
123890
+ site: referenceSite(source, expression)
123891
+ });
123892
+ }
123893
+ }
123869
123894
  rows.set(row.id, row);
123870
123895
  context.pendingValues.set(row.id, row);
123871
123896
  if (row.containerId !== void 0) {
@@ -124782,8 +124807,8 @@ function lowerConstructorProjections(context, schemaClass2, expression, base, ba
124782
124807
  argument: argument2,
124783
124808
  argumentIndex
124784
124809
  } of resolved) {
124785
- const childValueId = baseBody[schemaKey];
124786
- if (typeof childValueId !== "string") {
124810
+ const childValueId2 = baseBody[schemaKey];
124811
+ if (typeof childValueId2 !== "string") {
124787
124812
  throw new Error(
124788
124813
  `Class value ${String(base.id)}.${schemaKey} has no existing structural row.`
124789
124814
  );
@@ -124794,27 +124819,27 @@ function lowerConstructorProjections(context, schemaClass2, expression, base, ba
124794
124819
  `Class value ${String(base.id)}.${schemaKey} requires a value-row id string literal.`
124795
124820
  );
124796
124821
  }
124797
- const projectedBase = valueBase(context, childValueId);
124822
+ const projectedBase = valueBase(context, childValueId2);
124798
124823
  addReconstructed(
124799
124824
  context,
124800
124825
  "value",
124801
- childValueId,
124826
+ childValueId2,
124802
124827
  { ...valueFileFields(projectedBase), value: [argument2.value] },
124803
124828
  source.source
124804
124829
  );
124805
- body[schemaKey] = childValueId;
124830
+ body[schemaKey] = childValueId2;
124806
124831
  continue;
124807
124832
  }
124808
124833
  lowerValueRow(
124809
124834
  context,
124810
124835
  projectedMember,
124811
124836
  argument2,
124812
- childValueId,
124837
+ childValueId2,
124813
124838
  source,
124814
124839
  environment,
124815
124840
  argumentSlices[argumentIndex]
124816
124841
  );
124817
- body[schemaKey] = childValueId;
124842
+ body[schemaKey] = childValueId2;
124818
124843
  }
124819
124844
  }
124820
124845
  function classInheritanceChain(classes, classId) {
@@ -125325,6 +125350,12 @@ function lowerListValue(context, member, expression, base, source, inheritedEnvi
125325
125350
  );
125326
125351
  continue;
125327
125352
  }
125353
+ recordRepeatedRow(
125354
+ context,
125355
+ itemId,
125356
+ `List ${member.name}`,
125357
+ referenceSite(source, element)
125358
+ );
125328
125359
  const child = valueBase(context, itemId);
125329
125360
  if (member.listKind === "unordered" && child.containerId !== base.id) {
125330
125361
  throw new Error(
@@ -125348,6 +125379,16 @@ function lowerListValue(context, member, expression, base, source, inheritedEnvi
125348
125379
  value: member.listKind === "unordered" ? [] : ids
125349
125380
  };
125350
125381
  }
125382
+ function recordRepeatedRow(context, rowId, collection, site) {
125383
+ if (!context.placedStoredRowIds.has(rowId)) {
125384
+ context.placedStoredRowIds.add(rowId);
125385
+ return;
125386
+ }
125387
+ context.loweringFailures.push({
125388
+ message: `${collection} places existing row @id(${JSON.stringify(rowId)}), which source already places elsewhere. A row has one position: drop the @id on the copy to create a new row.`,
125389
+ site
125390
+ });
125391
+ }
125351
125392
  function lowerPendingListItem(context, member, expression, source, path, containerId, environment, authoredSlice) {
125352
125393
  const rows = /* @__PURE__ */ new Map();
125353
125394
  const localizedTexts = /* @__PURE__ */ new Map();
@@ -125415,6 +125456,12 @@ function lowerDictionaryValue(context, member, expression, base, source, inherit
125415
125456
  `Dictionary ${member.name}[${JSON.stringify(key)}] has no existing row. New dictionary entries are not implemented by this slice.`
125416
125457
  );
125417
125458
  }
125459
+ recordRepeatedRow(
125460
+ context,
125461
+ valueId,
125462
+ `Dictionary ${member.name}`,
125463
+ referenceSite(source, entry.value)
125464
+ );
125418
125465
  body[key] = lowerValueRow(
125419
125466
  context,
125420
125467
  entryMember,
@@ -125486,14 +125533,20 @@ function lowerEnum2(context, member, expression) {
125486
125533
  throw new Error(`Enum member ${member.name} requires [...].`);
125487
125534
  const options = context.enumOptions.get(member.enumId);
125488
125535
  if (options === void 0) throw new Error(`Unknown enum ${member.enumId}.`);
125536
+ const enumName = context.enumNames.get(member.enumId);
125537
+ if (enumName === void 0) {
125538
+ throw new Error(`Enum ${member.enumId} has no manifest name.`);
125539
+ }
125540
+ const subject = `Enum member ${member.name}`;
125489
125541
  return elements.map((entry) => {
125490
- if (entry.kind !== "contextualEnum") {
125542
+ const optionName = enumOptionSpelling(entry, enumName, subject);
125543
+ if (optionName === null) {
125491
125544
  throw new Error(
125492
- `Enum member ${member.name} requires contextual .Option values.`
125545
+ `${subject} requires .Option or ${enumName}.Option values.`
125493
125546
  );
125494
125547
  }
125495
- const id2 = options.get(entry.name);
125496
- if (id2 === void 0) throw new Error(`Enum has no option ${entry.name}.`);
125548
+ const id2 = options.get(optionName);
125549
+ if (id2 === void 0) throw new Error(`Enum has no option ${optionName}.`);
125497
125550
  return id2;
125498
125551
  });
125499
125552
  }
@@ -126079,17 +126132,17 @@ function indexValuePlacements(state, options = {}) {
126079
126132
  }
126080
126133
  const body = isObjectRecord2(row.body) ? row.body : null;
126081
126134
  if (body === null) continue;
126082
- for (const [memberName, childValueId] of Object.entries(body)) {
126083
- if (typeof childValueId !== "string") continue;
126084
- const placementKey = `${memberName}:${childValueId}`;
126135
+ for (const [memberName, childValueId2] of Object.entries(body)) {
126136
+ if (typeof childValueId2 !== "string") continue;
126137
+ const placementKey = `${memberName}:${childValueId2}`;
126085
126138
  if (!containerBodyByPlacement.has(placementKey)) {
126086
126139
  containerBodyByPlacement.set(placementKey, body);
126087
126140
  }
126088
126141
  const placed = mutablePlacedValueIds.get(memberName) ?? /* @__PURE__ */ new Set();
126089
- placed.add(childValueId);
126142
+ placed.add(childValueId2);
126090
126143
  mutablePlacedValueIds.set(memberName, placed);
126091
126144
  const placement = {
126092
- valueId: childValueId,
126145
+ valueId: childValueId2,
126093
126146
  schemaKey: memberName,
126094
126147
  containerValueId: row.id,
126095
126148
  containerClassId: row.classId
@@ -126097,9 +126150,9 @@ function indexValuePlacements(state, options = {}) {
126097
126150
  const byKey = placementsBySchemaKey.get(memberName) ?? [];
126098
126151
  byKey.push(placement);
126099
126152
  placementsBySchemaKey.set(memberName, byKey);
126100
- const byValue = placementsByValueId.get(childValueId) ?? [];
126153
+ const byValue = placementsByValueId.get(childValueId2) ?? [];
126101
126154
  byValue.push(placement);
126102
- placementsByValueId.set(childValueId, byValue);
126155
+ placementsByValueId.set(childValueId2, byValue);
126103
126156
  }
126104
126157
  }
126105
126158
  const valueIdByMemberId = /* @__PURE__ */ new Map();
@@ -126570,6 +126623,68 @@ function fileTargetId(context, expression, registry) {
126570
126623
  }
126571
126624
  return null;
126572
126625
  }
126626
+ function idlessNestedListEntries(context, expression) {
126627
+ const entries = [];
126628
+ let spellable = true;
126629
+ const visit = (node, path) => {
126630
+ const { expression: unwrapped } = annotatedValue(node);
126631
+ if (unwrapped.kind === "new") {
126632
+ for (const field of unwrapped.initializer ?? []) {
126633
+ visit(field.value, [...path, field.name]);
126634
+ }
126635
+ return;
126636
+ }
126637
+ if (unwrapped.kind === "litDict") {
126638
+ for (const entry of unwrapped.entries) {
126639
+ if (entry.key.kind === "litString") {
126640
+ visit(entry.value, [...path, entry.key.value]);
126641
+ continue;
126642
+ }
126643
+ const before = entries.length;
126644
+ visit(entry.value, path);
126645
+ if (entries.length > before) spellable = false;
126646
+ }
126647
+ return;
126648
+ }
126649
+ if (unwrapped.kind !== "litList") return;
126650
+ unwrapped.elements.forEach((element, index) => {
126651
+ const entryPath = [...path, index];
126652
+ if (annotatedValue(element).id === null && sourceValueSymbol(context, element) === null) {
126653
+ const start = stampableEntryStart(element);
126654
+ if (start === null) spellable = false;
126655
+ else {
126656
+ entries.push({
126657
+ path: entryPath,
126658
+ expressionPosition: { line: start.line, column: start.column }
126659
+ });
126660
+ }
126661
+ }
126662
+ visit(element, entryPath);
126663
+ });
126664
+ };
126665
+ visit(expression, []);
126666
+ return spellable ? entries : null;
126667
+ }
126668
+ function stampableEntryStart(expression) {
126669
+ switch (expression.kind) {
126670
+ case "member":
126671
+ case "index":
126672
+ return stampableEntryStart(expression.receiver);
126673
+ case "call":
126674
+ return stampableEntryStart(expression.callee);
126675
+ case "force":
126676
+ return stampableEntryStart(expression.operand);
126677
+ case "binary":
126678
+ case "coalesce":
126679
+ case "conditional":
126680
+ case "is":
126681
+ case "lambda":
126682
+ case "annotated":
126683
+ return null;
126684
+ default:
126685
+ return expression.pos;
126686
+ }
126687
+ }
126573
126688
  function memberPath2(expression) {
126574
126689
  if (expression.kind === "ident") return expression.name;
126575
126690
  if (expression.kind !== "member") return null;
@@ -128006,10 +128121,10 @@ function constructorProjectionSource(context, classId, body, visited, environmen
128006
128121
  classId,
128007
128122
  projection.memberId
128008
128123
  );
128009
- const childValueId = schemaKey === null ? void 0 : body[schemaKey];
128010
- const childValue = typeof childValueId === "string" ? context.values.get(childValueId) : void 0;
128124
+ const childValueId2 = schemaKey === null ? void 0 : body[schemaKey];
128125
+ const childValue = typeof childValueId2 === "string" ? context.values.get(childValueId2) : void 0;
128011
128126
  const projectedMember = context.members.get(projection.memberId);
128012
- if (schemaKey === null || typeof childValueId !== "string" || childValue === void 0 || projectedMember === void 0) {
128127
+ if (schemaKey === null || typeof childValueId2 !== "string" || childValue === void 0 || projectedMember === void 0) {
128013
128128
  throw new Error(
128014
128129
  `Class ${context.manifestClasses.get(classId)?.name ?? classId} constructor projection ${projection.parameterName} has no stored projected value.`
128015
128130
  );
@@ -128024,7 +128139,7 @@ function constructorProjectionSource(context, classId, body, visited, environmen
128024
128139
  `Class ${context.manifestClasses.get(classId)?.name ?? classId} constructor projection ${projection.parameterName} requires exactly one referenced value.`
128025
128140
  );
128026
128141
  }
128027
- visited.add(childValueId);
128142
+ visited.add(childValueId2);
128028
128143
  targetValueIds.push(targetIds[0]);
128029
128144
  argumentsValue.push(
128030
128145
  `${projection.parameterName}: ${quote5(targetIds[0])}`
@@ -128035,7 +128150,7 @@ function constructorProjectionSource(context, classId, body, visited, environmen
128035
128150
  `${projection.parameterName}: ${emitValue(
128036
128151
  context,
128037
128152
  projectedMember,
128038
- childValueId,
128153
+ childValueId2,
128039
128154
  {
128040
128155
  definitionSite: true,
128041
128156
  writeIdentity: false,
@@ -133950,7 +134065,10 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
133950
134065
  workspace,
133951
134066
  replacements.exact,
133952
134067
  localStatus.pendingValueIdentitySites,
133953
- { sourceSnapshot: localStatus.sourceSnapshot }
134068
+ {
134069
+ sourceSnapshot: localStatus.sourceSnapshot,
134070
+ committedValueAtPath: committedValueAtPathReader(nextRecords)
134071
+ }
133954
134072
  );
133955
134073
  const localRecords = rewriteReconstructedRecords(
133956
134074
  localStatus.reconstructed,
@@ -133978,7 +134096,8 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
133978
134096
  for (const [key, state] of Object.entries(nextRecords)) {
133979
134097
  const prior = previousRecords[key];
133980
134098
  const local = localRecords.get(key);
133981
- if (local === void 0 && prior?.file !== void 0) continue;
134099
+ if (local === void 0 && prior?.file !== void 0 && !isSharedMemberText(state))
134100
+ continue;
133982
134101
  const submitted = submittedByKey.get(key);
133983
134102
  const emitData = local === void 0 ? state.data : changedKeys.has(key) && submitted !== void 0 ? mergeCommittedRecordWithLaterLocalEdits(
133984
134103
  state.recordKind,
@@ -134517,12 +134636,10 @@ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, p
134517
134636
  entry.identity.source.range.start.line,
134518
134637
  entry.identity.source.range.start.character
134519
134638
  );
134520
- const indentation = sourceIndentationAt(source, start);
134521
- const newline = source.includes("\r\n") ? "\r\n" : "\n";
134522
134639
  edits.push({
134523
134640
  start,
134524
134641
  end: start,
134525
- text: `@id(${JSON.stringify(assignedId)})${newline}${indentation}`
134642
+ text: idStampText(source, start, assignedId)
134526
134643
  });
134527
134644
  } else {
134528
134645
  const quotedPendingId = JSON.stringify(pendingId2);
@@ -134560,15 +134677,18 @@ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, p
134560
134677
  site.expressionPosition.column
134561
134678
  );
134562
134679
  if (expressionOffset === null) continue;
134563
- const insertion = initializerOffset + expressionOffset;
134564
- const indentation = sourceIndentationAt(source, insertion);
134565
- const newline = source.includes("\r\n") ? "\r\n" : "\n";
134566
- const edits = editsByUri.get(site.uri) ?? [];
134567
- edits.push({
134568
- start: insertion,
134569
- end: insertion,
134570
- text: `@id(${JSON.stringify(assignedId)})${newline}${indentation}`
134680
+ const stamp = (offset, id2) => ({
134681
+ start: offset,
134682
+ end: offset,
134683
+ text: idStampText(source, offset, id2)
134571
134684
  });
134685
+ const entryStamps = options.committedValueAtPath === void 0 ? [] : nestedListEntryStamps(site, assignedId, options.committedValueAtPath);
134686
+ if (entryStamps === null) continue;
134687
+ const edits = editsByUri.get(site.uri) ?? [];
134688
+ edits.push(stamp(initializerOffset + expressionOffset, assignedId));
134689
+ for (const entry of entryStamps) {
134690
+ edits.push(stamp(initializerOffset + entry.offset, entry.id));
134691
+ }
134572
134692
  editsByUri.set(site.uri, edits);
134573
134693
  const handled = handledByUri.get(site.uri) ?? /* @__PURE__ */ new Set();
134574
134694
  handled.add(pendingId2);
@@ -134602,6 +134722,49 @@ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, p
134602
134722
  }
134603
134723
  return preserved;
134604
134724
  }
134725
+ function nestedListEntryStamps(site, rowId, committedValueAtPath) {
134726
+ if (site.nestedListEntries === null) return null;
134727
+ const stamps = [];
134728
+ for (const entry of site.nestedListEntries) {
134729
+ const id2 = committedValueAtPath(rowId, entry.path);
134730
+ if (id2 === null) return null;
134731
+ const offset = sourceOffsetInsideInitializer(
134732
+ site.initializer,
134733
+ entry.expressionPosition.line,
134734
+ entry.expressionPosition.column
134735
+ );
134736
+ if (offset === null) return null;
134737
+ if (precedingNonWhitespace(site.initializer, offset) === "(") return null;
134738
+ stamps.push({ offset, id: id2 });
134739
+ }
134740
+ return stamps;
134741
+ }
134742
+ function precedingNonWhitespace(text, offset) {
134743
+ let index = offset - 1;
134744
+ while (index >= 0 && /\s/u.test(text[index] ?? "")) index -= 1;
134745
+ return text[index];
134746
+ }
134747
+ function committedValueAtPathReader(records2) {
134748
+ let rows;
134749
+ return (rootId, path) => {
134750
+ rows ??= storedValueRowsByIdV4(Object.values(records2));
134751
+ let id2 = rootId;
134752
+ for (const key of path) {
134753
+ const next = childValueId(rows.get(id2)?.value, key);
134754
+ if (next === null) return null;
134755
+ id2 = next;
134756
+ }
134757
+ return id2;
134758
+ };
134759
+ }
134760
+ function childValueId(body, key) {
134761
+ if (typeof key === "number") {
134762
+ const entry = Array.isArray(body) ? body[key] : void 0;
134763
+ return typeof entry === "string" ? entry : null;
134764
+ }
134765
+ const child = isObjectRecord2(body) ? body[key] : void 0;
134766
+ return typeof child === "string" ? child : null;
134767
+ }
134605
134768
  function sourceOffsetInsideInitializer(source, oneBasedLine, oneBasedColumn) {
134606
134769
  if (oneBasedLine < 1 || oneBasedColumn < 1) return null;
134607
134770
  let offset = 0;
@@ -134631,9 +134794,13 @@ function sourceOffsetAt2(source, line, character) {
134631
134794
  }
134632
134795
  return Math.min(offset + character, source.length);
134633
134796
  }
134634
- function sourceIndentationAt(source, offset) {
134797
+ function idStampText(source, offset, id2) {
134798
+ const annotation2 = `@id(${JSON.stringify(id2)})`;
134635
134799
  const lineStart = source.lastIndexOf("\n", Math.max(0, offset - 1)) + 1;
134636
- return /^[\t ]*/u.exec(source.slice(lineStart, offset))?.[0] ?? "";
134800
+ const prefix = source.slice(lineStart, offset);
134801
+ if (!/^[\t ]*$/u.test(prefix)) return `${annotation2} `;
134802
+ const newline = source.includes("\r\n") ? "\r\n" : "\n";
134803
+ return `${annotation2}${newline}${prefix}`;
134637
134804
  }
134638
134805
  async function buildPostPushCompileSchema(workspace, status) {
134639
134806
  const byKind = /* @__PURE__ */ new Map();
@@ -136575,8 +136742,7 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
136575
136742
  baseState.data,
136576
136743
  mainLocale
136577
136744
  );
136578
- const locallyDeleted = hasBaseline && baseState.file !== void 0 && local === void 0 && // Member-owned texts survive losing their last inline source reference.
136579
- !(serverRecord.recordKind === "localized-text" && isLocalizedText(baseState.data) && baseState.data.memberSource !== void 0);
136745
+ const locallyDeleted = hasBaseline && baseState.file !== void 0 && local === void 0 && !isSharedMemberText(baseState);
136580
136746
  if (!serverChanged) {
136581
136747
  plans.set(key, {
136582
136748
  emitData: locallyDeleted ? void 0 : local === void 0 ? serverRecord.data : mergeProjectDocumentRecord(
@@ -137304,7 +137470,6 @@ var init_pull = __esm({
137304
137470
  "src/commands/pull.ts"() {
137305
137471
  "use strict";
137306
137472
  init_push();
137307
- init_localization2();
137308
137473
  init_source_layout();
137309
137474
  init_project_read_base();
137310
137475
  init_document();
@@ -140445,7 +140610,7 @@ var init_registry2 = __esm({
140445
140610
  "schema-contract/registry.mjs"() {
140446
140611
  "use strict";
140447
140612
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
140448
- cliVersion: "0.57.3",
140613
+ cliVersion: "0.58.1",
140449
140614
  projectFileUploadBatchSize: 32,
140450
140615
  documentRecords: {
140451
140616
  member: {
@@ -144978,8 +145143,8 @@ function collectClassInstances(document, targetClassId, memberById2, valueById,
144978
145143
  const schema = schemaClass2 !== void 0 && isObjectRecord2(schemaClass2.schema) ? schemaClass2.schema : {};
144979
145144
  const instanceValue = valueRecord.value;
144980
145145
  if (isObjectRecord2(instanceValue)) {
144981
- for (const [key, childValueId] of Object.entries(instanceValue)) {
144982
- walk(schema[key], childValueId);
145146
+ for (const [key, childValueId2] of Object.entries(instanceValue)) {
145147
+ walk(schema[key], childValueId2);
144983
145148
  }
144984
145149
  }
144985
145150
  return;
@@ -144987,8 +145152,8 @@ function collectClassInstances(document, targetClassId, memberById2, valueById,
144987
145152
  if (memberKind === 6) {
144988
145153
  const entries = valueRecord.value;
144989
145154
  if (Array.isArray(entries)) {
144990
- for (const childValueId of entries) {
144991
- walk(member.entryMemberId, childValueId);
145155
+ for (const childValueId2 of entries) {
145156
+ walk(member.entryMemberId, childValueId2);
144992
145157
  }
144993
145158
  }
144994
145159
  return;
@@ -144996,8 +145161,8 @@ function collectClassInstances(document, targetClassId, memberById2, valueById,
144996
145161
  if (memberKind === 5) {
144997
145162
  const entries = valueRecord.value;
144998
145163
  if (isObjectRecord2(entries)) {
144999
- for (const childValueId of Object.values(entries)) {
145000
- walk(member.entryMemberId, childValueId);
145164
+ for (const childValueId2 of Object.values(entries)) {
145165
+ walk(member.entryMemberId, childValueId2);
145001
145166
  }
145002
145167
  }
145003
145168
  }
@@ -147013,10 +147178,10 @@ function dryrunOwnership(document, valueById, saveBindings = /* @__PURE__ */ new
147013
147178
  const childMemberByKey = new Map(
147014
147179
  storageResolver.mergedSchema(classId).map((entry) => [entry.schemaKey, entry.memberId])
147015
147180
  );
147016
- for (const [key, childValueId] of Object.entries(value.value)) {
147181
+ for (const [key, childValueId2] of Object.entries(value.value)) {
147017
147182
  const childMemberId = childMemberByKey.get(key);
147018
- if (childMemberId !== void 0 && typeof childValueId === "string") {
147019
- walkOwnedGraph([...memberPath3, childMemberId], childValueId);
147183
+ if (childMemberId !== void 0 && typeof childValueId2 === "string") {
147184
+ walkOwnedGraph([...memberPath3, childMemberId], childValueId2);
147020
147185
  }
147021
147186
  }
147022
147187
  return;
@@ -147025,9 +147190,9 @@ function dryrunOwnership(document, valueById, saveBindings = /* @__PURE__ */ new
147025
147190
  if (unorderedList && (Array.isArray(value.value) || includeHistoricalNullLists && membership.has(valueId))) {
147026
147191
  const entryMemberId = storageResolver.resolvedEntryMemberId(memberId);
147027
147192
  if (entryMemberId === void 0) return;
147028
- for (const childValueId of membership.get(valueId) ?? []) {
147029
- if (typeof childValueId === "string") {
147030
- walkOwnedGraph([...memberPath3, entryMemberId], childValueId);
147193
+ for (const childValueId2 of membership.get(valueId) ?? []) {
147194
+ if (typeof childValueId2 === "string") {
147195
+ walkOwnedGraph([...memberPath3, entryMemberId], childValueId2);
147031
147196
  }
147032
147197
  }
147033
147198
  return;
@@ -147036,9 +147201,9 @@ function dryrunOwnership(document, valueById, saveBindings = /* @__PURE__ */ new
147036
147201
  const entryMemberId = storageResolver.resolvedEntryMemberId(memberId);
147037
147202
  if (entryMemberId === void 0) return;
147038
147203
  const childIds = Array.isArray(value.value) ? value.value : Object.values(value.value);
147039
- for (const childValueId of childIds) {
147040
- if (typeof childValueId === "string") {
147041
- walkOwnedGraph([...memberPath3, entryMemberId], childValueId);
147204
+ for (const childValueId2 of childIds) {
147205
+ if (typeof childValueId2 === "string") {
147206
+ walkOwnedGraph([...memberPath3, entryMemberId], childValueId2);
147042
147207
  }
147043
147208
  }
147044
147209
  }
@@ -148266,7 +148431,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
148266
148431
  async function main() {
148267
148432
  const args = parseArgs(process.argv.slice(2));
148268
148433
  if (args.command === "--version") {
148269
- console.log("0.57.3");
148434
+ console.log("0.58.1");
148270
148435
  return;
148271
148436
  }
148272
148437
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.57.3",
3
+ "version": "0.58.1",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ description: >-
9
9
  `@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
10
10
  ---
11
11
 
12
- <!-- reviewed-through-cli: 0.57.3 -->
12
+ <!-- reviewed-through-cli: 0.58.1 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -63,6 +63,8 @@ opaque JSON or guessed syntax.
63
63
  values, files, or other records. `@system` is reserved for platform-owned
64
64
  system records; authoring it can make the record uneditable.
65
65
  - Preserve existing `@id` annotations through rename, reorder, and file moves.
66
+ A new row cannot reuse a stored row's `@id`, including inside a new parent:
67
+ author the new row without it.
66
68
  - Omit `@id` on new ordinary declarations and nested rows unless a same-push
67
69
  structural reference genuinely requires an explicit UUID. A successful push
68
70
  assigns the durable IDs and rewrites source.
@@ -71,7 +71,9 @@ initializer cannot assign them. `Cell` is the placement's writable field.
71
71
 
72
72
  Object `PlacementTiles` is a `List<NeoPlacementTile>`. Each entry marks a
73
73
  footprint cell with its own `Cell` member. It is independent of `NeoTile`. Rendered
74
- tile classes do not belong in this list.
74
+ tile classes do not belong in this list. An object with no placement tiles
75
+ occupies no cells. It doesn't block other placements, and cell queries don't
76
+ return it.
75
77
 
76
78
  Convert a writable placement without changing its row identity or cell:
77
79
 
@@ -97,7 +97,7 @@ wrappers.
97
97
  The marker near the top of `SKILL.md` must exactly match the package version:
98
98
 
99
99
  ```html
100
- <!-- reviewed-through-cli: 0.57.3 -->
100
+ <!-- reviewed-through-cli: 0.58.1 -->
101
101
  ```
102
102
 
103
103
  The quoted version above is checked too, so this instruction cannot go stale
@@ -57,9 +57,11 @@ keys as quoted IDs when their option has been deleted.
57
57
 
58
58
  ## Identity rules
59
59
 
60
- Preserve every existing ID through rename, reorder, and file moves. For a new
61
- ordinary record, omit `@id` and let a successful real push assign and insert
62
- it. Dry-run and failed pushes leave tracked source unchanged.
60
+ Preserve every existing ID through rename, reorder, and file moves. A new row
61
+ cannot reuse a stored row's `@id`, including inside a new parent, and source
62
+ cannot place one stored row in two collection positions. For a new ordinary record, omit
63
+ `@id` and let a successful real push assign and insert it. Dry-run and failed pushes
64
+ leave tracked source unchanged.
63
65
 
64
66
  Nested class/list rows remain independently addressable and keep inline IDs:
65
67