@neocompose/cli 0.36.10 → 0.36.11

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,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.36.11] - 2026-08-21
4
+
5
+ ### Fixed
6
+
7
+ - Stop `neo status` reconstructing a referenced row twice. Retaining a stored
8
+ NeoScript construction followed reference members and `Reference(...)`
9
+ constructor arguments into rows it merely points at, so lowering order could
10
+ report a false "Source reconstructs value:… with different content" on a
11
+ healthy workspace. The retain sweep now stops at reference boundaries, and
12
+ an authored reconstruction always supersedes a retained copy.
13
+ - Re-lower against the server side of an unresolved conflict. A conflicted
14
+ record whose base body was stale (for example after resolving markers by
15
+ hand without `neo resolve`) made the identity walk miss rows the server
16
+ still holds and re-mint them; the lowering now adopts the server body while
17
+ the push plan keeps comparing against the local base.
18
+ - Refuse to plan value deletions the workspace base cannot explain. When a
19
+ planned delete's row is named by nothing in the base while the same push
20
+ mints replacement rows, `neo status`/`neo push` now report
21
+ `value-base-desync` and plan nothing instead of shipping deletes for live
22
+ server rows; the same shape with no re-mint is reported as a warning.
23
+
3
24
  ## [0.36.10] - 2026-08-21
4
25
 
5
26
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -32528,7 +32528,7 @@ function projectSchemaManifest(input, options = {}) {
32528
32528
  manifest.constructors ?? [],
32529
32529
  "ProjectSchemaManifest.constructors"
32530
32530
  );
32531
- const memberById = indexById(members, "ProjectSchemaManifest.members");
32531
+ const memberById2 = indexById(members, "ProjectSchemaManifest.members");
32532
32532
  const schemaClassById = indexById(classes, "ProjectSchemaManifest.classes");
32533
32533
  const interfaceById = indexById(
32534
32534
  interfaces,
@@ -32555,7 +32555,7 @@ function projectSchemaManifest(input, options = {}) {
32555
32555
  const rootMemberIds2 = options.rootMemberIds ?? inferRootMemberIds(members);
32556
32556
  const environment = {
32557
32557
  sourceUri: options.sourceUri ?? defaultSourceUri,
32558
- members: memberById,
32558
+ members: memberById2,
32559
32559
  classes: schemaClassById,
32560
32560
  interfaces: interfaceById,
32561
32561
  enums: indexById(enums, "ProjectSchemaManifest.enums"),
@@ -32765,10 +32765,10 @@ function inferRootMemberIds(members) {
32765
32765
  };
32766
32766
  }
32767
32767
  function effectiveMemberWritability(members, classes, roots) {
32768
- const memberById = /* @__PURE__ */ new Map();
32768
+ const memberById2 = /* @__PURE__ */ new Map();
32769
32769
  for (const member of members) {
32770
32770
  if (typeof member.id === "string") {
32771
- memberById.set(member.id, member);
32771
+ memberById2.set(member.id, member);
32772
32772
  }
32773
32773
  }
32774
32774
  const classById = /* @__PURE__ */ new Map();
@@ -32780,7 +32780,7 @@ function effectiveMemberWritability(members, classes, roots) {
32780
32780
  const declaredStorage = (memberId, seen = /* @__PURE__ */ new Set()) => {
32781
32781
  if (seen.has(memberId)) return null;
32782
32782
  seen.add(memberId);
32783
- const member = memberById.get(memberId);
32783
+ const member = memberById2.get(memberId);
32784
32784
  if (!member) return null;
32785
32785
  if (member.storage === "immutable" || member.storage === "save" || member.storage === "session") {
32786
32786
  return member.storage;
@@ -32849,7 +32849,7 @@ function effectiveMemberWritability(members, classes, roots) {
32849
32849
  if (typeof roots.session === "string") {
32850
32850
  rootStorage.set(roots.session, "session");
32851
32851
  }
32852
- for (const [memberId, member] of memberById) {
32852
+ for (const [memberId, member] of memberById2) {
32853
32853
  const root = rootStorage.get(memberId);
32854
32854
  const declared = declaredStorage(memberId);
32855
32855
  let storage = root ?? declared;
@@ -32866,7 +32866,7 @@ function effectiveMemberWritability(members, classes, roots) {
32866
32866
  let changed = true;
32867
32867
  while (changed) {
32868
32868
  changed = false;
32869
- for (const memberId of memberById.keys()) {
32869
+ for (const memberId of memberById2.keys()) {
32870
32870
  if (anchored.has(memberId)) continue;
32871
32871
  const next = new Set(effective.get(memberId) ?? []);
32872
32872
  for (const parentId of parents.get(memberId) ?? []) {
@@ -35199,6 +35199,19 @@ var init_names = __esm({
35199
35199
  function isObjectRecord2(value) {
35200
35200
  return typeof value === "object" && value !== null && !Array.isArray(value);
35201
35201
  }
35202
+ function effectiveRecordData(record3) {
35203
+ return record3.conflictServerHash !== void 0 && isObjectRecord2(record3.conflictServerData) ? record3.conflictServerData : record3.data;
35204
+ }
35205
+ function loweringBaseRecords(records2) {
35206
+ let normalized = null;
35207
+ for (const [key, record3] of Object.entries(records2)) {
35208
+ const effective = effectiveRecordData(record3);
35209
+ if (effective === record3.data) continue;
35210
+ if (normalized === null) normalized = { ...records2 };
35211
+ normalized[key] = { ...record3, data: effective };
35212
+ }
35213
+ return normalized ?? records2;
35214
+ }
35202
35215
  function splitRecordFields(data, serverOwnedFields) {
35203
35216
  const fileFields = {};
35204
35217
  const volatileValues = {};
@@ -54576,7 +54589,7 @@ function globalId(global, kind) {
54576
54589
  }
54577
54590
  function baseData(state, kind, id2) {
54578
54591
  const entry = state[`${kind}:${id2}`];
54579
- return entry?.conflictServerHash !== void 0 ? entry.conflictServerData : entry?.data;
54592
+ return entry === void 0 ? void 0 : effectiveRecordData(entry);
54580
54593
  }
54581
54594
  function optionalBase(value) {
54582
54595
  return isObjectRecord2(value) ? value : {};
@@ -64258,7 +64271,7 @@ function constructorActionParameterId(constructor2, index) {
64258
64271
  }
64259
64272
  return `__arg_${index}__`;
64260
64273
  }
64261
- function createTypedValueNormalizer(rows, document, childrenByContainerId, parameterTypesForRow, memberById, memberTypeInfo) {
64274
+ function createTypedValueNormalizer(rows, document, childrenByContainerId, parameterTypesForRow, memberById2, memberTypeInfo) {
64262
64275
  const visiting = /* @__PURE__ */ new Set();
64263
64276
  const normalizeLiteral = (value) => {
64264
64277
  if (Array.isArray(value)) return value.map(normalizeLiteral);
@@ -64341,7 +64354,7 @@ function createTypedValueNormalizer(rows, document, childrenByContainerId, param
64341
64354
  const childTypeBySchemaKey = /* @__PURE__ */ new Map();
64342
64355
  for (const entry of merged) {
64343
64356
  if (entry.memberId === null) continue;
64344
- const member = memberById(entry.memberId);
64357
+ const member = memberById2(entry.memberId);
64345
64358
  if (member === void 0) continue;
64346
64359
  const childType = memberTypeInfo(member, env);
64347
64360
  if (childType !== null) {
@@ -72261,7 +72274,7 @@ function buildConstructorInitializerIndexes(vm) {
72261
72274
  declaringClassIdsByMemberId.set(memberId, owners);
72262
72275
  }
72263
72276
  }
72264
- const memberById = new Map(vm.members.map((member) => [member.id, member]));
72277
+ const memberById2 = new Map(vm.members.map((member) => [member.id, member]));
72265
72278
  const containerMemberIdByEntryId = /* @__PURE__ */ new Map();
72266
72279
  for (const member of vm.members) {
72267
72280
  if (isMemberList(member) || isMemberDictionary(member)) {
@@ -72271,7 +72284,7 @@ function buildConstructorInitializerIndexes(vm) {
72271
72284
  return {
72272
72285
  initializerScopeMemberIds,
72273
72286
  declaringClassIdsByMemberId,
72274
- memberById,
72287
+ memberById: memberById2,
72275
72288
  containerMemberIdByEntryId
72276
72289
  };
72277
72290
  }
@@ -79912,7 +79925,7 @@ function baseData2(state, kind, id2) {
79912
79925
  return record3 ? effectiveData(record3) : {};
79913
79926
  }
79914
79927
  function effectiveData(record3) {
79915
- const data = record3.conflictServerHash !== void 0 ? record3.conflictServerData : record3.data;
79928
+ const data = effectiveRecordData(record3);
79916
79929
  return isObjectRecord2(data) ? data : {};
79917
79930
  }
79918
79931
  function requiredDestination(value, owner) {
@@ -81930,7 +81943,7 @@ function variantOwnershipRoots(args) {
81930
81943
  const folderById = new Map(
81931
81944
  (args.variantFolders ?? []).map((folder) => [folder.id, folder])
81932
81945
  );
81933
- const memberById = new Map(
81946
+ const memberById2 = new Map(
81934
81947
  (args.members ?? []).map((member) => [member.id, member])
81935
81948
  );
81936
81949
  const roots = [];
@@ -81971,7 +81984,7 @@ function variantOwnershipRoots(args) {
81971
81984
  })
81972
81985
  );
81973
81986
  const lookupParameterId = variantClass.genericParams?.[1]?.id;
81974
- const lookupCollection = lookupFolder?.binding == null ? void 0 : memberById.get(lookupFolder.binding.collectionMemberId);
81987
+ const lookupCollection = lookupFolder?.binding == null ? void 0 : memberById2.get(lookupFolder.binding.collectionMemberId);
81975
81988
  const lookupBinding = lookupParameterId !== void 0 && isMemberListBase(lookupCollection) ? {
81976
81989
  [lookupParameterId]: {
81977
81990
  kind: "member",
@@ -87398,7 +87411,7 @@ function validateCreatedMigrationGraph(args) {
87398
87411
  }
87399
87412
  createdById.set(row.id, row);
87400
87413
  }
87401
- const memberById = new Map(args.members.map((member) => [member.id, member]));
87414
+ const memberById2 = new Map(args.members.map((member) => [member.id, member]));
87402
87415
  const classById = new Map(
87403
87416
  args.classes.map((schemaClass2) => [schemaClass2.id, schemaClass2])
87404
87417
  );
@@ -87497,7 +87510,7 @@ function validateCreatedMigrationGraph(args) {
87497
87510
  );
87498
87511
  }
87499
87512
  const childMemberId = schema.get(schemaKey);
87500
- const childMember = childMemberId === void 0 ? void 0 : memberById.get(childMemberId);
87513
+ const childMember = childMemberId === void 0 ? void 0 : memberById2.get(childMemberId);
87501
87514
  if (childMember === void 0) {
87502
87515
  throw graphError(
87503
87516
  args,
@@ -87513,7 +87526,7 @@ function validateCreatedMigrationGraph(args) {
87513
87526
  return;
87514
87527
  }
87515
87528
  if (member !== void 0 && isMemberListBase(member)) {
87516
- const entryMember = memberById.get(member.entryMemberId);
87529
+ const entryMember = memberById2.get(member.entryMemberId);
87517
87530
  if (entryMember === void 0) {
87518
87531
  throw graphError(
87519
87532
  args,
@@ -87541,7 +87554,7 @@ function validateCreatedMigrationGraph(args) {
87541
87554
  return;
87542
87555
  }
87543
87556
  if (member !== void 0 && isMemberDictionaryBase(member)) {
87544
- const entryMember = memberById.get(member.entryMemberId);
87557
+ const entryMember = memberById2.get(member.entryMemberId);
87545
87558
  if (entryMember === void 0) {
87546
87559
  throw graphError(
87547
87560
  args,
@@ -88765,13 +88778,13 @@ function lookupTargetClassContractViolation(args) {
88765
88778
  }
88766
88779
  return null;
88767
88780
  }
88768
- function lookupEntryClassId(member, memberById) {
88769
- const collection = memberById.get(member.collectionMemberId);
88781
+ function lookupEntryClassId(member, memberById2) {
88782
+ const collection = memberById2.get(member.collectionMemberId);
88770
88783
  if (collection === void 0) return null;
88771
88784
  if (!isMemberListBase(collection) && !isMemberDictionaryBase(collection)) {
88772
88785
  return null;
88773
88786
  }
88774
- const entry = memberById.get(collection.entryMemberId);
88787
+ const entry = memberById2.get(collection.entryMemberId);
88775
88788
  if (entry === void 0) return null;
88776
88789
  return isMemberClassBase(entry) ? entry.classId : null;
88777
88790
  }
@@ -88795,8 +88808,8 @@ function classAssignableToClass(actualClassId, expectedClassId, classById) {
88795
88808
  function classDisplayName(classId, classById) {
88796
88809
  return classById.get(classId)?.name ?? classId;
88797
88810
  }
88798
- function collectionMemberName(member, memberById, classes) {
88799
- const collection = memberById.get(member.collectionMemberId);
88811
+ function collectionMemberName(member, memberById2, classes) {
88812
+ const collection = memberById2.get(member.collectionMemberId);
88800
88813
  if (collection === void 0) return member.collectionMemberId;
88801
88814
  const owner = classes.find(
88802
88815
  (schemaClass2) => Object.values(schemaClass2.schema).includes(member.collectionMemberId)
@@ -93412,7 +93425,7 @@ function reconcileVariantConstructorArgs(args) {
93412
93425
  if (variants.length === 0) return;
93413
93426
  const valueById = indexRecordsById(postDocument.values);
93414
93427
  const classById = indexRecordsById(postDocument.classes);
93415
- const memberById = indexRecordsById(postDocument.members);
93428
+ const memberById2 = indexRecordsById(postDocument.members);
93416
93429
  const constructorById = indexRecordsById(postDocument.constructors ?? []);
93417
93430
  const changeIndexByValueId = indexChangesByRecordId(args.prepared, "value");
93418
93431
  let baseHashByValueId = null;
@@ -93444,7 +93457,7 @@ function reconcileVariantConstructorArgs(args) {
93444
93457
  );
93445
93458
  if (schemaKey === void 0) continue;
93446
93459
  const memberId = schemaClass2.schema[schemaKey];
93447
- const member = memberId === void 0 ? void 0 : memberById.get(memberId);
93460
+ const member = memberId === void 0 ? void 0 : memberById2.get(memberId);
93448
93461
  if (member === void 0) continue;
93449
93462
  const childId = root.value[schemaKey];
93450
93463
  if (typeof childId !== "string") continue;
@@ -95236,6 +95249,52 @@ var init_project_version_whole_graph_validation = __esm({
95236
95249
  }
95237
95250
  });
95238
95251
 
95252
+ // src/project-source/value-base-desync.ts
95253
+ function unanchoredValueDeleteIds(args) {
95254
+ const { records: records2, deletedValueIds } = args;
95255
+ if (deletedValueIds.size === 0) return [];
95256
+ const anchored = /* @__PURE__ */ new Set();
95257
+ for (const [key, record3] of Object.entries(records2)) {
95258
+ collectAnchoredMentions(record3.data, key, deletedValueIds, anchored);
95259
+ }
95260
+ const unanchored = [];
95261
+ for (const id2 of deletedValueIds) {
95262
+ if (anchored.has(id2)) continue;
95263
+ const data = records2[recordStateKey("value", id2)]?.data;
95264
+ const containerId = isObjectRecord2(data) ? data.containerId : void 0;
95265
+ if (typeof containerId === "string" && records2[recordStateKey("value", containerId)] !== void 0) {
95266
+ continue;
95267
+ }
95268
+ unanchored.push(id2);
95269
+ }
95270
+ return unanchored;
95271
+ }
95272
+ function collectAnchoredMentions(value, mentioningKey, candidates, anchored) {
95273
+ if (typeof value === "string") {
95274
+ if (!candidates.has(value)) return;
95275
+ if (recordStateKey("value", value) === mentioningKey) return;
95276
+ anchored.add(value);
95277
+ return;
95278
+ }
95279
+ if (Array.isArray(value)) {
95280
+ for (const element of value) {
95281
+ collectAnchoredMentions(element, mentioningKey, candidates, anchored);
95282
+ }
95283
+ return;
95284
+ }
95285
+ if (!isObjectRecord2(value)) return;
95286
+ for (const nested of Object.values(value)) {
95287
+ collectAnchoredMentions(nested, mentioningKey, candidates, anchored);
95288
+ }
95289
+ }
95290
+ var init_value_base_desync = __esm({
95291
+ "src/project-source/value-base-desync.ts"() {
95292
+ "use strict";
95293
+ init_projection();
95294
+ init_workspace();
95295
+ }
95296
+ });
95297
+
95239
95298
  // src/project-source/workspace-status-core.ts
95240
95299
  function listVirtualProjectSourceFilesV4(files) {
95241
95300
  const selected = files.filter((file) => {
@@ -95305,6 +95364,7 @@ function computeWorkspaceStatus(workspace, options) {
95305
95364
  binaryFiles: []
95306
95365
  };
95307
95366
  }
95367
+ const loweringRecords = loweringBaseRecords(workspace.state.records);
95308
95368
  const baseDocuments = schemaBaseDocuments(workspace);
95309
95369
  const staticMemberOwnershipRecoveries = [];
95310
95370
  const baseManifest = baseDocuments.length === 0 ? void 0 : documentsToProjectSchemaManifest(baseDocuments, {
@@ -95372,7 +95432,7 @@ function computeWorkspaceStatus(workspace, options) {
95372
95432
  };
95373
95433
  }
95374
95434
  try {
95375
- validateProjectRootEnvelopeV4(workspace.state.records, analysis);
95435
+ validateProjectRootEnvelopeV4(loweringRecords, analysis);
95376
95436
  } catch (error) {
95377
95437
  const root = analysis.configuration.globals.find(
95378
95438
  (global) => global.type.name === "Root"
@@ -95397,20 +95457,15 @@ function computeWorkspaceStatus(workspace, options) {
95397
95457
  };
95398
95458
  }
95399
95459
  manifest = lowerProjectSchemaV4(baseManifest, analysis, {
95400
- staticValueIdsBySymbol: staticValueIdsBySymbol(workspace.state.records),
95401
- projectFileIdsBySymbol: projectFileIdsBySymbol(
95402
- workspace.state.records,
95403
- analysis
95404
- ),
95405
- rowBackedDefaultMemberIds: rowBackedDefaultMemberIdsV4(
95406
- workspace.state.records
95407
- ),
95460
+ staticValueIdsBySymbol: staticValueIdsBySymbol(loweringRecords),
95461
+ projectFileIdsBySymbol: projectFileIdsBySymbol(loweringRecords, analysis),
95462
+ rowBackedDefaultMemberIds: rowBackedDefaultMemberIdsV4(loweringRecords),
95408
95463
  rootValueTargetsByPath: rootValueTargetsByPath(
95409
- Object.values(workspace.state.records)
95464
+ Object.values(loweringRecords)
95410
95465
  )
95411
95466
  });
95412
95467
  memberDefaults = lowerMemberDefaultSourcesV4(
95413
- workspace.state.records,
95468
+ loweringRecords,
95414
95469
  analysis,
95415
95470
  manifest,
95416
95471
  { registry: valueLowerRegistry }
@@ -95488,7 +95543,7 @@ function computeWorkspaceStatus(workspace, options) {
95488
95543
  );
95489
95544
  }
95490
95545
  const staticValues = lowerStaticValueSourcesV4(
95491
- workspace.state.records,
95546
+ loweringRecords,
95492
95547
  projectAnalysisV4,
95493
95548
  manifest,
95494
95549
  { registry: valueLowerRegistry }
@@ -95499,7 +95554,7 @@ function computeWorkspaceStatus(workspace, options) {
95499
95554
  ]);
95500
95555
  staticMemberValueIds = staticValues.memberValueIds;
95501
95556
  const rootValues = lowerProjectRootSourceV4(
95502
- workspace.state.records,
95557
+ loweringRecords,
95503
95558
  projectAnalysisV4,
95504
95559
  manifest,
95505
95560
  { registry: valueLowerRegistry }
@@ -95507,7 +95562,7 @@ function computeWorkspaceStatus(workspace, options) {
95507
95562
  reportPhase("documents-static-root");
95508
95563
  authoredValueSeeds = new Map([...authoredValueSeeds, ...rootValues.seeds]);
95509
95564
  const rootPathResolutionState = overlayProspectiveSourceRecords(
95510
- workspace.state.records,
95565
+ loweringRecords,
95511
95566
  documents,
95512
95567
  [...staticValues.records, ...memberDefaults.records, ...rootValues.records],
95513
95568
  staticMemberValueIds
@@ -95635,18 +95690,18 @@ function computeWorkspaceStatus(workspace, options) {
95635
95690
  }
95636
95691
  const rootRecords = rootValues.records;
95637
95692
  const variantValues = lowerVariantValueSourcesV4(
95638
- workspace.state.records,
95693
+ loweringRecords,
95639
95694
  manifest,
95640
95695
  variantValueBindingsV4(manifest, projectAnalysisV4),
95641
95696
  { analysis: projectAnalysisV4, registry: valueLowerRegistry }
95642
95697
  );
95643
95698
  const supplementalRecords = options.lowerSupplementalRecords(
95644
- workspace.state.records,
95699
+ loweringRecords,
95645
95700
  projectAnalysisV4,
95646
95701
  options.trustedPendingProjectFiles
95647
95702
  );
95648
95703
  const prospectiveState = overlayProspectiveSourceRecords(
95649
- workspace.state.records,
95704
+ loweringRecords,
95650
95705
  documents,
95651
95706
  [
95652
95707
  ...staticValues.records,
@@ -95795,6 +95850,50 @@ function computeWorkspaceStatus(workspace, options) {
95795
95850
  binaryFiles: []
95796
95851
  };
95797
95852
  }
95853
+ const unanchoredValueDeletes = unanchoredValueDeleteIds({
95854
+ records: workspace.state.records,
95855
+ deletedValueIds
95856
+ });
95857
+ if (unanchoredValueDeletes.length > 0) {
95858
+ const mintedRowCount = valueLowerRegistry.pendingValues.size + authoredValueSeeds.size;
95859
+ const named = unanchoredValueDeletes.slice(0, 3).join(", ");
95860
+ const remainder = Math.max(0, unanchoredValueDeletes.length - 3);
95861
+ const sample = remainder === 0 ? named : `${named}, +${remainder} more`;
95862
+ const deleteFile = changes.find(
95863
+ (change) => change.kind === "delete" && change.recordKind === "value" && change.recordId === unanchoredValueDeletes[0]
95864
+ )?.file;
95865
+ if (mintedRowCount > 0) {
95866
+ parseErrors.push(
95867
+ new SchemaSourceError(
95868
+ `This push would delete ${unanchoredValueDeletes.length} value row(s) no record in the workspace base holds (${sample}) while minting ${mintedRowCount} replacement row(s): the workspace base disagrees with its own file projection \u2014 run "neo pull" before pushing.`,
95869
+ deleteFile ?? "<project>",
95870
+ 1,
95871
+ 1,
95872
+ "value-base-desync"
95873
+ )
95874
+ );
95875
+ return {
95876
+ changes: [],
95877
+ conflictedFiles,
95878
+ parseErrors,
95879
+ parseWarnings,
95880
+ reconstructed: /* @__PURE__ */ new Map(),
95881
+ authoredValueSeeds: /* @__PURE__ */ new Map(),
95882
+ binaryChanges: [],
95883
+ binaryFiles: []
95884
+ };
95885
+ }
95886
+ parseWarnings.push(
95887
+ new SchemaSourceError(
95888
+ `This push deletes ${unanchoredValueDeletes.length} value row(s) no record in the workspace base holds (${sample}): the workspace base disagrees with its own file projection \u2014 run "neo pull" before pushing.`,
95889
+ deleteFile ?? "<project>",
95890
+ 1,
95891
+ 1,
95892
+ "value-base-desync",
95893
+ "warning"
95894
+ )
95895
+ );
95896
+ }
95798
95897
  for (let index = changes.length - 1; index >= 0; index -= 1) {
95799
95898
  const change = changes[index];
95800
95899
  if (change === void 0 || change.kind !== "update") continue;
@@ -96137,7 +96236,7 @@ function replayAnimationDeclarationInitializersV4(records2, document, fallbackDo
96137
96236
  }
96138
96237
  function animationRecordsFromState(records2) {
96139
96238
  return Object.values(records2).flatMap((record3) => {
96140
- const data = record3.conflictServerHash !== void 0 && isObjectRecord2(record3.conflictServerData) ? record3.conflictServerData : record3.data;
96239
+ const data = effectiveRecordData(record3);
96141
96240
  return isObjectRecord2(data) ? [{ recordKind: record3.recordKind, data }] : [];
96142
96241
  });
96143
96242
  }
@@ -96280,7 +96379,7 @@ function recordLabel(data) {
96280
96379
  function prospectiveAnimationRecords(base, reconstructed3, changes, seeds) {
96281
96380
  const prospective = /* @__PURE__ */ new Map();
96282
96381
  for (const [key, state] of Object.entries(base)) {
96283
- const data = state.conflictServerHash !== void 0 && isObjectRecord2(state.conflictServerData) ? state.conflictServerData : state.data;
96382
+ const data = effectiveRecordData(state);
96284
96383
  if (isObjectRecord2(data)) {
96285
96384
  prospective.set(key, { recordKind: state.recordKind, data });
96286
96385
  }
@@ -96412,7 +96511,8 @@ function overlayProspectiveSourceRecords(base, schemaDocuments, authored, static
96412
96511
  for (const record3 of authored) {
96413
96512
  const key = recordStateKey(record3.recordKind, record3.recordId);
96414
96513
  const previous = result[key];
96415
- const previousData = previous?.conflictServerHash !== void 0 && isObjectRecord2(previous.conflictServerData) ? previous.conflictServerData : isObjectRecord2(previous?.data) ? previous.data : {};
96514
+ const effectivePrevious = previous === void 0 ? void 0 : effectiveRecordData(previous);
96515
+ const previousData = isObjectRecord2(effectivePrevious) ? effectivePrevious : {};
96416
96516
  put(record3.recordKind, record3.recordId, {
96417
96517
  ...previousData,
96418
96518
  ...record3.fileFields
@@ -96424,7 +96524,7 @@ function schemaBaseDocuments(workspace) {
96424
96524
  const records2 = [];
96425
96525
  for (const state of Object.values(workspace.state.records)) {
96426
96526
  if (!isSchemaRecordKindV4(state.recordKind)) continue;
96427
- const effectiveBase = state.conflictServerHash !== void 0 && isObjectRecord2(state.conflictServerData) ? state.conflictServerData : state.data;
96527
+ const effectiveBase = effectiveRecordData(state);
96428
96528
  if (!isObjectRecord2(effectiveBase)) {
96429
96529
  throw new Error(
96430
96530
  `Schema base ${state.recordKind}:${state.recordId} must contain an object document. Run \`neo pull --reset\` to repair the workspace state.`
@@ -96565,6 +96665,7 @@ var init_workspace_status_core = __esm({
96565
96665
  init_compile_ns_property();
96566
96666
  init_materialized_construction_cache();
96567
96667
  init_project_version_whole_graph_validation();
96668
+ init_value_base_desync();
96568
96669
  }
96569
96670
  });
96570
96671
 
@@ -98378,6 +98479,7 @@ function buildValueLowerContext(state, manifest, options = {}) {
98378
98479
  parsedInitializers,
98379
98480
  declaredInitializers: indexDeclaredInitializers(options.analysis),
98380
98481
  reconstructed: /* @__PURE__ */ new Map(),
98482
+ retainedReconstructedKeys: /* @__PURE__ */ new Set(),
98381
98483
  pendingValues: registry.pendingValues,
98382
98484
  pendingLocalizedTexts: registry.pendingLocalizedTexts,
98383
98485
  loweredMemberIdByValueId: /* @__PURE__ */ new Map(),
@@ -100852,10 +100954,16 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
100852
100954
  );
100853
100955
  }
100854
100956
  if (materializedConstruction !== "lower") {
100855
- for (const childId of Object.values(baseBody)) {
100856
- if (typeof childId === "string") {
100857
- retainStoredValueSubgraph(context, childId, source.source, /* @__PURE__ */ new Set());
100858
- }
100957
+ const storedMemberIds = storedClassSchemaMemberIds(context, currentClassId);
100958
+ for (const [schemaKey, childId] of Object.entries(baseBody)) {
100959
+ if (typeof childId !== "string") continue;
100960
+ retainStoredValueSubgraph(
100961
+ context,
100962
+ childId,
100963
+ source.source,
100964
+ /* @__PURE__ */ new Set(),
100965
+ memberById(context, storedMemberIds.get(schemaKey))
100966
+ );
100859
100967
  }
100860
100968
  if (materializedConstruction === "preserve" && isObjectRecord2(base.constructorArgs)) {
100861
100969
  const constructor2 = storedValueConstructor(
@@ -100863,12 +100971,25 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
100863
100971
  schemaClass2,
100864
100972
  base.constructorArgs
100865
100973
  );
100974
+ const authoredArguments = constructorArgumentExpressions(expression);
100866
100975
  for (let index = 0; index < constructor2.arguments.length; index += 1) {
100867
100976
  const argument2 = constructor2.arguments[index];
100977
+ if (argument2 === void 0) continue;
100868
100978
  const valueId = base.constructorArgs[`__arg_${index}__`];
100869
- if (argument2 !== void 0 && typeof valueId === "string" && constructorArgumentOwnsRow(argument2.type)) {
100870
- retainStoredValueSubgraph(context, valueId, source.source, /* @__PURE__ */ new Set());
100979
+ if (typeof valueId !== "string") continue;
100980
+ if (!constructorArgumentOwnsRow(
100981
+ argument2.type,
100982
+ authoredArguments.get(argument2.name)
100983
+ )) {
100984
+ continue;
100871
100985
  }
100986
+ retainStoredValueSubgraph(
100987
+ context,
100988
+ valueId,
100989
+ source.source,
100990
+ /* @__PURE__ */ new Set(),
100991
+ null
100992
+ );
100872
100993
  }
100873
100994
  }
100874
100995
  }
@@ -100878,45 +100999,106 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
100878
100999
  ...classId === currentClassId ? {} : { classId }
100879
101000
  };
100880
101001
  }
100881
- function constructorArgumentOwnsRow(type) {
100882
- return type.kind === "class" || type.kind === "interface" || type.kind === "generic" || type.kind === "list" || type.kind === "dictionary";
101002
+ function constructorArgumentOwnsRow(type, authoredArgument) {
101003
+ if (type.kind !== "class" && type.kind !== "interface" && type.kind !== "generic" && type.kind !== "list" && type.kind !== "dictionary") {
101004
+ return false;
101005
+ }
101006
+ return !namesExistingRow(authoredArgument);
100883
101007
  }
100884
- function retainStoredValueSubgraph(context, valueId, source, visited) {
101008
+ function constructorArgumentExpressions(expression) {
101009
+ const byName = /* @__PURE__ */ new Map();
101010
+ const names = expression.argumentNames ?? [];
101011
+ for (const [index, argument2] of expression.args.entries()) {
101012
+ const name = names[index];
101013
+ if (typeof name !== "string") continue;
101014
+ if (byName.has(name)) continue;
101015
+ byName.set(name, argument2);
101016
+ }
101017
+ return byName;
101018
+ }
101019
+ function namesExistingRow(expression) {
101020
+ if (expression === void 0) return false;
101021
+ const unwrapped = annotatedValue(expression).expression;
101022
+ if (unwrapped.kind !== "call") return false;
101023
+ if (unwrapped.callee.kind !== "ident") return false;
101024
+ return unwrapped.callee.name === "Reference";
101025
+ }
101026
+ function memberPointsAtForeignRows(member) {
101027
+ return member.kind === "lookup" || member.kind === "dialogueLookup";
101028
+ }
101029
+ function memberById(context, memberId) {
101030
+ if (memberId === void 0) return null;
101031
+ return context.members.get(memberId) ?? null;
101032
+ }
101033
+ function collectionEntryMember(context, member) {
101034
+ if (member === null) return null;
101035
+ if (member.kind !== "list" && member.kind !== "dictionary") return null;
101036
+ return memberById(context, member.entryMemberId);
101037
+ }
101038
+ function storedRowPlacementMember(context, valueId) {
101039
+ for (const placement of context.valuePlacements.placementsByValueId.get(
101040
+ valueId
101041
+ ) ?? []) {
101042
+ if (placement.containerClassId === null) continue;
101043
+ const memberId = storedClassSchemaMemberIds(
101044
+ context,
101045
+ placement.containerClassId
101046
+ ).get(placement.schemaKey);
101047
+ const member = memberById(context, memberId);
101048
+ if (member !== null) return member;
101049
+ }
101050
+ return null;
101051
+ }
101052
+ function retainStoredValueSubgraph(context, valueId, source, visited, member) {
100885
101053
  if (visited.has(valueId)) return;
100886
101054
  visited.add(valueId);
100887
101055
  const state = context.state[`value:${valueId}`];
100888
101056
  if (state === void 0 || !isObjectRecord2(state.data)) return;
100889
- const key = `value:${valueId}`;
100890
- if (!context.reconstructed.has(key)) {
100891
- addReconstructed(
100892
- context,
100893
- "value",
100894
- valueId,
100895
- valueFileFields(state.data),
100896
- source
100897
- );
100898
- }
100899
- const referenced = /* @__PURE__ */ new Set();
100900
- const collect = (value) => {
101057
+ addReconstructed(
101058
+ context,
101059
+ "value",
101060
+ valueId,
101061
+ valueFileFields(state.data),
101062
+ source,
101063
+ { retained: true }
101064
+ );
101065
+ const owningMember = member ?? storedRowPlacementMember(context, valueId);
101066
+ if (owningMember !== null && memberPointsAtForeignRows(owningMember)) return;
101067
+ const referenced = /* @__PURE__ */ new Map();
101068
+ const add = (id2, childMember) => {
101069
+ if (context.state[`value:${id2}`] === void 0) return;
101070
+ if (referenced.has(id2)) return;
101071
+ referenced.set(id2, childMember);
101072
+ };
101073
+ const collect = (value, childMember) => {
100901
101074
  if (typeof value === "string") {
100902
- if (context.state[`value:${value}`] !== void 0) referenced.add(value);
101075
+ add(value, childMember);
100903
101076
  return;
100904
101077
  }
100905
101078
  if (Array.isArray(value)) {
100906
- for (const entry of value) collect(entry);
101079
+ for (const entry of value) collect(entry, childMember);
100907
101080
  return;
100908
101081
  }
100909
101082
  if (!isObjectRecord2(value)) return;
100910
- for (const entry of Object.values(value)) collect(entry);
101083
+ for (const entry of Object.values(value)) collect(entry, childMember);
100911
101084
  };
100912
- collect(state.data.value);
101085
+ const body = state.data.value;
101086
+ const storedClassId = stringOrNull(state.data.classId);
101087
+ if (isObjectRecord2(body) && storedClassId !== null) {
101088
+ const storedMemberIds = storedClassSchemaMemberIds(context, storedClassId);
101089
+ for (const [schemaKey, child] of Object.entries(body)) {
101090
+ collect(child, memberById(context, storedMemberIds.get(schemaKey)));
101091
+ }
101092
+ } else {
101093
+ collect(body, collectionEntryMember(context, owningMember));
101094
+ }
100913
101095
  for (const childId of context.valuePlacements.valueIdsByContainerId.get(
100914
101096
  valueId
100915
101097
  ) ?? []) {
100916
- referenced.add(childId);
101098
+ add(childId, collectionEntryMember(context, owningMember));
100917
101099
  }
100918
- for (const childId of referenced) {
100919
- retainStoredValueSubgraph(context, childId, source, visited);
101100
+ for (const [childId, childMember] of referenced) {
101101
+ retainStoredValueSubgraph(context, childId, source, visited, childMember);
100920
101102
  }
100921
101103
  }
100922
101104
  function lowerConstructorProjections(context, schemaClass2, expression, base, baseBody, body, source, environment, authoredSlice) {
@@ -102371,7 +102553,7 @@ function dialogueTargetsBySymbol(analysis) {
102371
102553
  }
102372
102554
  function indexSourceStaticTargets(targets, state, analysis, manifest, parsedInitializers) {
102373
102555
  if (!analysis) return;
102374
- const memberById = new Map(
102556
+ const memberById2 = new Map(
102375
102557
  manifest.members.map((member) => [member.id, member])
102376
102558
  );
102377
102559
  for (let pass = 0; pass < 2; pass += 1) {
@@ -102388,7 +102570,7 @@ function indexSourceStaticTargets(targets, state, analysis, manifest, parsedInit
102388
102570
  if (declaration.kind !== "field" || declaration.initializer === null)
102389
102571
  continue;
102390
102572
  const memberId = sourceIdentityId(declaration, "member", symbol);
102391
- if (!memberById.has(memberId)) continue;
102573
+ if (!memberById2.has(memberId)) continue;
102392
102574
  const expression = parseCachedInitializer(
102393
102575
  parsedInitializers,
102394
102576
  declaration.initializer
@@ -102651,21 +102833,30 @@ function annotationId2(annotations) {
102651
102833
  }
102652
102834
  return value.value;
102653
102835
  }
102654
- function addReconstructed(context, recordKind, recordId, fileFields, source) {
102836
+ function addReconstructed(context, recordKind, recordId, fileFields, source, options = {}) {
102655
102837
  const key = `${recordKind}:${recordId}`;
102656
102838
  const existing = context.reconstructed.get(key);
102839
+ const line = source.range.start.line + 1;
102657
102840
  if (existing !== void 0) {
102658
- if (!canonicallyEqual(existing.fileFields, fileFields)) {
102659
- throw new Error(`Source reconstructs ${key} with different content.`);
102841
+ if (options.retained === true) return;
102842
+ if (context.retainedReconstructedKeys.has(key)) {
102843
+ context.retainedReconstructedKeys.delete(key);
102844
+ } else {
102845
+ if (!canonicallyEqual(existing.fileFields, fileFields)) {
102846
+ throw new Error(
102847
+ `Source reconstructs ${key} with different content: ${existing.file}:${existing.line} and ${source.uri}:${line} each lower this record from a different body.`
102848
+ );
102849
+ }
102850
+ return;
102660
102851
  }
102661
- return;
102662
102852
  }
102853
+ if (options.retained === true) context.retainedReconstructedKeys.add(key);
102663
102854
  context.reconstructed.set(key, {
102664
102855
  recordKind,
102665
102856
  recordId,
102666
102857
  fileFields,
102667
102858
  file: source.uri,
102668
- line: source.range.start.line + 1,
102859
+ line,
102669
102860
  sourceSpan: {
102670
102861
  path: source.uri,
102671
102862
  start: source.range.start,
@@ -109650,14 +109841,14 @@ function resolveNSFunctionReceiverValue(thisRef, target, document) {
109650
109841
  }
109651
109842
  function buildRootValue(document) {
109652
109843
  const rootValue = {};
109653
- const memberById = new Map(
109844
+ const memberById2 = new Map(
109654
109845
  document.members.map((entry) => [entry.id, entry])
109655
109846
  );
109656
109847
  const valueById = new Map(document.values.map((entry) => [entry.id, entry]));
109657
109848
  for (const { root, memberId } of projectRootMembersInDisplayOrder(
109658
109849
  document.project
109659
109850
  )) {
109660
- const member = memberById.get(memberId);
109851
+ const member = memberById2.get(memberId);
109661
109852
  const valueId = member !== void 0 && typeof member.valueId === "string" ? member.valueId : null;
109662
109853
  rootValue[root.key] = valueId !== null ? valueById.get(valueId)?.value ?? null : null;
109663
109854
  }
@@ -110377,7 +110568,7 @@ function neoScriptCheckErrorMessage(error) {
110377
110568
  function resolveLocalScriptMember(manifest, memberRef) {
110378
110569
  if (memberRef === null) return null;
110379
110570
  const scripted = manifest.members.filter(isLocalScriptMember);
110380
- const memberById = new Map(scripted.map((member2) => [member2.id, member2]));
110571
+ const memberById2 = new Map(scripted.map((member2) => [member2.id, member2]));
110381
110572
  const separator = memberRef.lastIndexOf(".");
110382
110573
  if (separator > 0 && separator < memberRef.length - 1) {
110383
110574
  const classRef = memberRef.slice(0, separator);
@@ -110400,7 +110591,7 @@ function resolveLocalScriptMember(manifest, memberRef) {
110400
110591
  while (!visited.has(current.id)) {
110401
110592
  visited.add(current.id);
110402
110593
  for (const [schemaKey, memberId] of Object.entries(current.schema)) {
110403
- const member2 = memberById.get(memberId);
110594
+ const member2 = memberById2.get(memberId);
110404
110595
  if (member2 !== void 0 && (schemaKey === memberName || localMemberMatchesMember(member2, memberName))) {
110405
110596
  return { member: member2, receiverClassId: receiver.id };
110406
110597
  }
@@ -114395,7 +114586,7 @@ var init_registry2 = __esm({
114395
114586
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
114396
114587
  formatVersion: 3,
114397
114588
  contractVersion: "3.14",
114398
- cliVersion: "0.36.10",
114589
+ cliVersion: "0.36.11",
114399
114590
  projectFileUploadBatchSize: 32,
114400
114591
  documentRecords: {
114401
114592
  member: {
@@ -118222,7 +118413,7 @@ async function runPendingMigrations(workspace, client, raw, migrations, onlyRef,
118222
118413
  }
118223
118414
  }
118224
118415
  }
118225
- const memberById = new Map(
118416
+ const memberById2 = new Map(
118226
118417
  document.members.map((member) => [member.id, member])
118227
118418
  );
118228
118419
  const valueById = new Map(
@@ -118293,7 +118484,7 @@ async function runPendingMigrations(workspace, client, raw, migrations, onlyRef,
118293
118484
  },
118294
118485
  null
118295
118486
  );
118296
- const instances = targetClassId === null ? [null] : collectClassInstances(document, targetClassId, memberById, valueById);
118487
+ const instances = targetClassId === null ? [null] : collectClassInstances(document, targetClassId, memberById2, valueById);
118297
118488
  let instanceEditCount = 0;
118298
118489
  for (const instance of instances) {
118299
118490
  let result;
@@ -118499,7 +118690,7 @@ async function runPendingMigrations(workspace, client, raw, migrations, onlyRef,
118499
118690
  `Applied ${pending.length} migration(s). Run "neo pull" to refresh the working copy.`
118500
118691
  );
118501
118692
  }
118502
- function collectClassInstances(document, targetClassId, memberById, valueById) {
118693
+ function collectClassInstances(document, targetClassId, memberById2, valueById) {
118503
118694
  const classById = new Map(
118504
118695
  document.classes.map((schemaClass2) => [schemaClass2.id, schemaClass2])
118505
118696
  );
@@ -118508,7 +118699,7 @@ function collectClassInstances(document, targetClassId, memberById, valueById) {
118508
118699
  const walk = (memberId, valueId) => {
118509
118700
  if (typeof valueId !== "string" || seen.has(valueId)) return;
118510
118701
  seen.add(valueId);
118511
- const member = typeof memberId === "string" ? memberById.get(memberId) : void 0;
118702
+ const member = typeof memberId === "string" ? memberById2.get(memberId) : void 0;
118512
118703
  const valueRecord = valueById.get(valueId);
118513
118704
  if (member === void 0 || valueRecord === void 0) return;
118514
118705
  const memberKind = member.kind;
@@ -119732,7 +119923,7 @@ function buildWorld(document, raw) {
119732
119923
  const valueById = new Map(
119733
119924
  document.values.map((value) => [String(value.id), value])
119734
119925
  );
119735
- const memberById = new Map(
119926
+ const memberById2 = new Map(
119736
119927
  document.members.map((member) => [String(member.id), member])
119737
119928
  );
119738
119929
  const saveOwned = /* @__PURE__ */ new Set();
@@ -119750,7 +119941,7 @@ function buildWorld(document, raw) {
119750
119941
  if (visitedPlacements.has(visitKey)) return;
119751
119942
  visitedPlacements.add(visitKey);
119752
119943
  const value = valueById.get(valueId);
119753
- const member = memberById.get(memberId);
119944
+ const member = memberById2.get(memberId);
119754
119945
  if (value === void 0 || member === void 0) return;
119755
119946
  const storage = storageResolver.effectiveStorageForPath(memberPath2);
119756
119947
  if (storage === "save" /* Save */) saveOwned.add(valueId);
@@ -119783,7 +119974,7 @@ function buildWorld(document, raw) {
119783
119974
  for (const { memberId } of projectRootMembersInDisplayOrder(
119784
119975
  document.project
119785
119976
  )) {
119786
- const member = memberById.get(memberId);
119977
+ const member = memberById2.get(memberId);
119787
119978
  if (member !== void 0 && typeof member.valueId === "string") {
119788
119979
  walkOwnedGraph([memberId], member.valueId);
119789
119980
  }
@@ -121014,7 +121205,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
121014
121205
  async function main() {
121015
121206
  const args = parseArgs(process.argv.slice(2));
121016
121207
  if (args.command === "--version") {
121017
- console.log("0.36.10");
121208
+ console.log("0.36.11");
121018
121209
  return;
121019
121210
  }
121020
121211
  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.36.10",
3
+ "version": "0.36.11",
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.36.10 -->
12
+ <!-- reviewed-through-cli: 0.36.11 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -210,10 +210,13 @@ Conflict source deliberately fails compilation. Edit the desired final source,
210
210
  or use `neo resolve --mine|--theirs` as a whole-side convenience. Then pull,
211
211
  review, dry-run, and push again.
212
212
 
213
+ Either `neo resolve` side adopts the server side as the new base for every
214
+ conflict it settles, so the next push plans against what the server holds:
215
+ `--theirs` reads clean afterwards, `--mine` reads as an ordinary local update.
213
216
  Server-derived compilation metadata is not authored source and does not create
214
- a pull conflict by itself. If an older CLI retained such a metadata-only
215
- conflict without writing source markers, either `neo resolve` side accepts the
216
- newer server metadata as the base while preserving any real local source edit.
217
+ a pull conflict by itself; if an older CLI retained such a metadata-only
218
+ conflict without writing source markers, either side settles it the same way
219
+ while preserving any real local source edit.
217
220
 
218
221
  `neo push` sends the authenticated semantic diff plus a deterministic source
219
222
  hash, while the server recompiles the changed NeoScript bodies and
@@ -83,7 +83,7 @@ wrappers.
83
83
  The marker near the top of `SKILL.md` must exactly match the package version:
84
84
 
85
85
  ```html
86
- <!-- reviewed-through-cli: 0.36.10 -->
86
+ <!-- reviewed-through-cli: 0.36.11 -->
87
87
  ```
88
88
 
89
89
  The quoted version above is checked too, so this instruction cannot go stale
@@ -80,9 +80,17 @@ Resolve source conflicts by editing the final intended source. Use
80
80
  choice. Pull, inspect, dry-run, and retry; there is no force-CAS path.
81
81
 
82
82
  Compiled initializer IR is server-owned and does not participate in authored
83
- three-way merge decisions. `neo resolve` also repairs metadata-only conflict
84
- bookkeeping left by an older pull when no source marker exists; accepting the
85
- newer server metadata does not overwrite the tracked source.
83
+ three-way merge decisions. Either `neo resolve` side adopts the server side as
84
+ the new base for every conflict it settles `--theirs` then reads clean, and
85
+ `--mine` keeps the local text as an ordinary update on top of that base. That
86
+ includes metadata-only conflict bookkeeping left by an older pull when no
87
+ source marker exists; adopting the server base never overwrites tracked
88
+ source.
89
+
90
+ A `value-base-desync` error means the workspace base no longer names value
91
+ rows its own file projection holds, so the plan would delete live server rows.
92
+ Nothing is planned in that state; run `neo pull` and retry. The same message
93
+ as a warning reports the inconsistency without blocking.
86
94
 
87
95
  For `base-hash-conflict`, pull and merge. For `version-bump-required`, inspect
88
96
  the classification and use `neo push --accept-bump` only when the bump is