@neocompose/cli 0.89.13 → 0.89.14

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,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.89.14
4
+
5
+ ### Patch Changes
6
+
7
+ - f1b9697: Reduce local export and initializer-checking time by sharing dependency-tracked constructor frames, avoiding eager expansion of unrelated values, and skipping whole-project placement indexing for temporary rows. Track cached placement dependencies so relevant edits still invalidate retained work.
8
+ - f89cf70: Fix animation validation rejecting an existing override member after its inherited method signature changes. Recompile the retained body against the new signature without changing the member ID.
9
+
10
+ Fix diagnostics and completion for `root.Assets` in nested `foreach` collection expressions, including `.spec.neo` files and parenthesized receivers.
11
+
3
12
  ## 0.89.13
4
13
 
5
14
  ### Patch Changes
@@ -19377,7 +19377,7 @@ var init_registry = __esm({
19377
19377
  "../packages/manifest/schema-contract/registry.mjs"() {
19378
19378
  "use strict";
19379
19379
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
19380
- cliVersion: "0.89.13",
19380
+ cliVersion: "0.89.14",
19381
19381
  projectFileUploadBatchSize: 32,
19382
19382
  documentRecords: {
19383
19383
  member: {
@@ -36221,7 +36221,7 @@ function initializerEvaluatorCacheRevision(document) {
36221
36221
  function initializerReadDependenciesEqual(left, right) {
36222
36222
  if (left === right) return true;
36223
36223
  if (left === null || right === null) return false;
36224
- return left.hasUnattributableValueRead === right.hasUnattributableValueRead && left.hasValuePlacementRead === right.hasValuePlacementRead && setsEqual(left.valueIds, right.valueIds) && setsEqual(left.localizedTextIds, right.localizedTextIds) && setsEqual(left.variantIds, right.variantIds) && setsEqual(left.containerIds, right.containerIds) && setsEqual(left.staticMemberIds, right.staticMemberIds) && left.worldQueries.equals(right.worldQueries);
36224
+ return left.hasUnattributableValueRead === right.hasUnattributableValueRead && left.hasValuePlacementRead === right.hasValuePlacementRead && left.hasUnnamedValuePlacementRead === right.hasUnnamedValuePlacementRead && setsEqual(left.placementIds, right.placementIds) && setsEqual(left.valueIds, right.valueIds) && setsEqual(left.localizedTextIds, right.localizedTextIds) && setsEqual(left.variantIds, right.variantIds) && setsEqual(left.containerIds, right.containerIds) && setsEqual(left.staticMemberIds, right.staticMemberIds) && left.worldQueries.equals(right.worldQueries);
36225
36225
  }
36226
36226
  var InitializerReadSet;
36227
36227
  var init_initializer_context = __esm({
@@ -36238,6 +36238,8 @@ var init_initializer_context = __esm({
36238
36238
  staticMemberIds = /* @__PURE__ */ new Set();
36239
36239
  worldQueries = new WorldGridQueryReadSet();
36240
36240
  hasValuePlacementRead = false;
36241
+ hasUnnamedValuePlacementRead = false;
36242
+ placementIds = /* @__PURE__ */ new Set();
36241
36243
  hasUnattributableValueRead = false;
36242
36244
  /**
36243
36245
  * The unattributable reads by scope, for a fork of the same document to
@@ -36261,8 +36263,10 @@ var init_initializer_context = __esm({
36261
36263
  return;
36262
36264
  }
36263
36265
  };
36264
- recordValuePlacementRead = () => {
36266
+ recordValuePlacementRead = (valueId) => {
36265
36267
  this.hasValuePlacementRead = true;
36268
+ if (valueId === void 0) this.hasUnnamedValuePlacementRead = true;
36269
+ else this.placementIds.add(valueId);
36266
36270
  };
36267
36271
  };
36268
36272
  }
@@ -36886,6 +36890,7 @@ var init_getter_subscriptions = __esm({
36886
36890
  const reads = new InitializerReadSet();
36887
36891
  for (const key of [
36888
36892
  "valueIds",
36893
+ "placementIds",
36889
36894
  "localizedTextIds",
36890
36895
  "variantIds",
36891
36896
  "containerIds",
@@ -36897,6 +36902,7 @@ var init_getter_subscriptions = __esm({
36897
36902
  for (const read of source.reads.worldQueries)
36898
36903
  reads.worldQueries.add(read);
36899
36904
  reads.hasValuePlacementRead = source.reads.hasValuePlacementRead;
36905
+ reads.hasUnnamedValuePlacementRead = source.reads.hasUnnamedValuePlacementRead;
36900
36906
  reads.hasUnattributableValueRead = source.reads.hasUnattributableValueRead;
36901
36907
  cloned.setReads(copy, reads);
36902
36908
  }
@@ -58757,7 +58763,9 @@ function materializationReadTracking(recorder) {
58757
58763
  for (const read of readTracking.worldQueries) {
58758
58764
  recorder.recordWorldQueryRead?.(read);
58759
58765
  }
58760
- if (readTracking.hasValuePlacementRead) {
58766
+ for (const id2 of readTracking.placementIds)
58767
+ recorder.recordValuePlacementRead(id2);
58768
+ if (readTracking.hasUnnamedValuePlacementRead) {
58761
58769
  recorder.recordValuePlacementRead();
58762
58770
  }
58763
58771
  for (const containerId of readTracking.containerEntryReadIds) {
@@ -62902,6 +62910,10 @@ function createHeadlessVirtualInstanceResolver(args) {
62902
62910
  if (valueId !== void 0) {
62903
62911
  for (const active of activeReads) active.placementIds.add(valueId);
62904
62912
  ownReads()?.placementIds.add(valueId);
62913
+ } else {
62914
+ for (const active of activeReads) active.global = true;
62915
+ const own = ownReads();
62916
+ if (own !== void 0) own.global = true;
62905
62917
  }
62906
62918
  args.readRecorder?.recordValuePlacementRead(valueId);
62907
62919
  },
@@ -63139,7 +63151,9 @@ function createHeadlessVirtualInstanceResolver(args) {
63139
63151
  for (const id2 of reads.variantIds) external.recordVariantRead(id2);
63140
63152
  for (const id2 of reads.staticMemberIds)
63141
63153
  external.recordStaticMemberRead(id2);
63142
- if (reads.placementIds.size > 0) external.recordValuePlacementRead();
63154
+ for (const id2 of reads.placementIds)
63155
+ external.recordValuePlacementRead(id2);
63156
+ if (reads.global) external.recordGlobalRead("virtual-instance-expansion");
63143
63157
  for (const reason of reads.corpusIndexReads)
63144
63158
  external.recordGlobalRead(reason);
63145
63159
  }
@@ -63334,7 +63348,8 @@ function createHeadlessVirtualInstanceResolver(args) {
63334
63348
  share.containerChanged
63335
63349
  );
63336
63350
  if (shared === null) return null;
63337
- recorder.recordValuePlacementRead();
63351
+ for (const id2 of shared.reads.placementIds)
63352
+ recorder.recordValuePlacementRead(id2);
63338
63353
  for (const id2 of shared.reads.valueIds) recorder.recordValueRead(id2);
63339
63354
  for (const id2 of shared.reads.containerIds)
63340
63355
  recorder.recordContainerMembershipRead(id2);
@@ -63369,12 +63384,10 @@ function createHeadlessVirtualInstanceResolver(args) {
63369
63384
  const virtualInstanceRowsForValue = (receiverValueId) => {
63370
63385
  const adopted = adoptSharedExpansion(receiverValueId);
63371
63386
  if (adopted !== null) return adopted;
63372
- recorder.recordValuePlacementRead();
63373
63387
  recorder.recordValueRead(receiverValueId);
63374
63388
  const readPlacement = (valueId) => {
63375
- for (const active of activeReads) active.placementIds.add(valueId);
63376
- ownReads()?.placementIds.add(valueId);
63377
- return placements().get(valueId) ?? null;
63389
+ recorder.recordValuePlacementRead(valueId);
63390
+ return rawById.has(valueId) ? placements().get(valueId) ?? null : null;
63378
63391
  };
63379
63392
  let placement = readPlacement(receiverValueId);
63380
63393
  const roots = [];
@@ -63411,10 +63424,19 @@ function createHeadlessVirtualInstanceResolver(args) {
63411
63424
  observeRoot(known.instanceRootId);
63412
63425
  observeOwner(valueId, true);
63413
63426
  }
63414
- if (known?.instanceRootId === valueId) return known;
63427
+ const readKnown = (graph2) => {
63428
+ const result = resultByRootId.get(graph2.instanceRootId);
63429
+ if (result !== void 0) {
63430
+ settledRows(result);
63431
+ forwardReads(result.reads);
63432
+ }
63433
+ observeOwner(valueId, true);
63434
+ return graph2;
63435
+ };
63436
+ if (known?.instanceRootId === valueId) return readKnown(known);
63415
63437
  const stored = rawById.get(valueId);
63416
63438
  if (known !== void 0 && (stored === void 0 || !isStoredClassResolutionRoot(stored) || isMaterializedSpine(known.locationsById.get(valueId))))
63417
- return known;
63439
+ return readKnown(known);
63418
63440
  virtualInstanceRowsForValue(valueId);
63419
63441
  if (frames.length === 0) return graphByValueId.get(valueId) ?? null;
63420
63442
  const graph = observeSettled(valueId);
@@ -63514,7 +63536,7 @@ function createHeadlessVirtualInstanceResolver(args) {
63514
63536
  readRecorder: recorder,
63515
63537
  recordReads: (target, read) => {
63516
63538
  let reads = callerReads.get(target);
63517
- if (reads === void 0) {
63539
+ if (reads === void 0 || reads.valueIds !== target.valueIds || reads.containerIds !== target.containerIds || reads.localizedTextIds !== target.localizedTextIds || reads.variantIds !== target.variantIds || reads.staticMemberIds !== target.staticMemberIds) {
63518
63540
  reads = {
63519
63541
  valueIds: target.valueIds,
63520
63542
  containerIds: target.containerIds,
@@ -63538,6 +63560,9 @@ function createHeadlessVirtualInstanceResolver(args) {
63538
63560
  return read();
63539
63561
  } finally {
63540
63562
  activeReads.pop();
63563
+ for (const id2 of reads.placementIds)
63564
+ target.recordValuePlacementRead(id2);
63565
+ reads.placementIds.clear();
63541
63566
  if (reads.global)
63542
63567
  target.recordUnattributableValueRead("virtual-instance-expansion");
63543
63568
  for (const reason of reads.corpusIndexReads)
@@ -63740,9 +63765,23 @@ function sharedHeadlessResolver(overlay2) {
63740
63765
  containerChanged: (id2) => overlay2.changedContainerIds.has(id2)
63741
63766
  };
63742
63767
  }
63743
- function initializerStoredClassGraph(document, rootId, member) {
63768
+ function initializerStoredClassGraph(document, rootId, member, readTracking) {
63744
63769
  initializerEvaluatorLookups(document);
63745
- return headlessResolversByLookupKey.get(initializerBehaviorCacheKey(document))?.storedClassGraph(rootId, member) ?? null;
63770
+ const resolver = headlessResolversByLookupKey.get(
63771
+ initializerBehaviorCacheKey(document)
63772
+ );
63773
+ if (resolver === void 0) return null;
63774
+ const read = () => resolver.storedClassGraph(rootId, member);
63775
+ return readTracking === void 0 ? read() : resolver.recordReads(readTracking, read);
63776
+ }
63777
+ function initializerContainingClassGraph(document, valueId, readTracking) {
63778
+ initializerEvaluatorLookups(document);
63779
+ const resolver = headlessResolversByLookupKey.get(
63780
+ initializerBehaviorCacheKey(document)
63781
+ );
63782
+ if (resolver === void 0) return null;
63783
+ const read = () => resolver.virtualInstanceGraphForValue(valueId);
63784
+ return readTracking === void 0 ? read() : resolver.recordReads(readTracking, read);
63746
63785
  }
63747
63786
  function initializerEvaluatorLookups(document, readTracking) {
63748
63787
  const trackedHeadless = readTracking !== void 0 && document.databaseVM === void 0;
@@ -63997,7 +64036,9 @@ function createInitializerMaterializationBatch(authoredDocument, pendingInitiali
63997
64036
  },
63998
64037
  // Resolver placement walks record every ancestor id. Upserts below also
63999
64038
  // invalidate old/new ownership targets, including newly attached rows.
64000
- recordValuePlacementRead: () => {
64039
+ recordValuePlacementRead: (id2) => {
64040
+ if (id2 !== void 0) recordRead(id2);
64041
+ else for (const entry of activeReads) entry.global = true;
64001
64042
  }
64002
64043
  };
64003
64044
  const forget = (entry) => {
@@ -64021,10 +64062,11 @@ function createInitializerMaterializationBatch(authoredDocument, pendingInitiali
64021
64062
  entry.invalidated();
64022
64063
  };
64023
64064
  const watchReads = (reads, invalidated) => {
64024
- const global = reads.worldQueries.size > 0 || reads.hasValuePlacementRead || reads.corpusReadReasons.size > 0;
64065
+ const global = reads.worldQueries.size > 0 || reads.hasUnnamedValuePlacementRead || reads.corpusReadReasons.size > 0;
64025
64066
  const entry = {
64026
64067
  reads: /* @__PURE__ */ new Set([
64027
64068
  ...reads.valueIds,
64069
+ ...reads.placementIds,
64028
64070
  ...reads.containerIds,
64029
64071
  ...reads.containerEntryReadIds
64030
64072
  ]),
@@ -123439,9 +123481,18 @@ var init_effective_value_reader = __esm({
123439
123481
  if (position === void 0) break;
123440
123482
  const parentMember = this.memberForId(position.memberId);
123441
123483
  if (parentMember !== null && isClassSlotMember(parentMember)) {
123442
- const containingRows = this.resolveGraph(parentId, parentMember);
123443
- if (isMaterializedSpine(this.locations.get(valueId)))
123444
- return containingRows;
123484
+ const canShareFrame = this.context.saveStaticBindings === void 0 && this.context.sessionStaticBindings === void 0 && parentMember.payload !== 1 /* Partial */ && isStoredClassResolutionRoot(this.rawRow(parentId));
123485
+ const frame = canShareFrame ? initializerContainingClassGraph(
123486
+ this.readDocument,
123487
+ parentId,
123488
+ this.context.readTracking
123489
+ ) : null;
123490
+ const location3 = frame?.locationsById.get(valueId);
123491
+ if (frame === null || isMaterializedSpine(location3)) {
123492
+ const containingRows = this.resolveGraph(parentId, parentMember);
123493
+ if (isMaterializedSpine(this.locations.get(valueId)))
123494
+ return containingRows;
123495
+ }
123445
123496
  break;
123446
123497
  }
123447
123498
  parentId = position.parentValueId;
@@ -123456,7 +123507,12 @@ var init_effective_value_reader = __esm({
123456
123507
  materializeInitializerValue,
123457
123508
  materializeMemberDefaultValue: materializeMemberDefaultValue2,
123458
123509
  materializedIndex: this.materializedIndex,
123459
- resolveClassGraph: this.context.readTracking === void 0 && this.context.saveStaticBindings === void 0 && this.context.sessionStaticBindings === void 0 ? (root2, member) => initializerStoredClassGraph(this.readDocument, root2.id, member) : void 0,
123510
+ resolveClassGraph: this.context.saveStaticBindings === void 0 && this.context.sessionStaticBindings === void 0 ? (root2, member) => initializerStoredClassGraph(
123511
+ this.readDocument,
123512
+ root2.id,
123513
+ member,
123514
+ this.context.readTracking
123515
+ ) : void 0,
123460
123516
  onResolvedListenerDefaults: (ownerId, entry) => {
123461
123517
  if (entry === void 0) this.listenerDefaults.delete(ownerId);
123462
123518
  else this.listenerDefaults.set(ownerId, entry);
@@ -144949,7 +145005,7 @@ function createScheduledConstructionReplayComparer(args) {
144949
145005
  get worldQueries() {
144950
145006
  return created.reads.worldQueries;
144951
145007
  },
144952
- recordValuePlacementRead: () => created.reads.recordValuePlacementRead(),
145008
+ recordValuePlacementRead: (valueId) => created.reads.recordValuePlacementRead(valueId),
144953
145009
  recordUnattributableValueRead: (reason) => created.reads.recordUnattributableValueRead(reason)
144954
145010
  };
144955
145011
  const reader = schemaReader.withIndexedSnapshot(document2).withRowOverrides(input.replayRows, { readTracking: tracking });
@@ -163615,10 +163671,10 @@ function buildValueEmitIndexesV4(records3, manifest, localizedTextDependencies,
163615
163671
  storedPlacements,
163616
163672
  rootPathsByValueId
163617
163673
  ),
163618
- readUnstoredSlots: () => unstoredSlots ??= unstoredSlotParents(
163674
+ unstoredSlotParent: (id2) => (unstoredSlots ??= unstoredSlotParents(
163619
163675
  readDocument(),
163620
- readCompilationProject()
163621
- ),
163676
+ readCompilationProject
163677
+ ))(id2),
163622
163678
  collectionBindingValueIds: /* @__PURE__ */ new Set([
163623
163679
  ...manifest.members.flatMap(
163624
163680
  (member) => member.kind === "lookup" && typeof member.collectionValueId === "string" ? [member.collectionValueId] : []
@@ -174647,7 +174703,7 @@ function slotRootPath(context, targetId) {
174647
174703
  id2 = edge.parentValueId;
174648
174704
  continue;
174649
174705
  }
174650
- const slot = context.readUnstoredSlots().get(id2);
174706
+ const slot = context.unstoredSlotParent(id2);
174651
174707
  if (slot === void 0) return null;
174652
174708
  segments.push(slot.schemaKey);
174653
174709
  id2 = slot.parentId;
@@ -174655,9 +174711,6 @@ function slotRootPath(context, targetId) {
174655
174711
  return null;
174656
174712
  }
174657
174713
  function unstoredSlotParents(document, project) {
174658
- const lookups = headlessVirtualInstanceLookups(document, {
174659
- initializerCompiler: documentInitializerCompiler(document, project)
174660
- });
174661
174714
  const classIndex = new Map(document.classes.map((c) => [c.id, c]));
174662
174715
  const memberIndex = new Map(document.members.map((m) => [m.id, m]));
174663
174716
  const slots = /* @__PURE__ */ new Map();
@@ -174676,24 +174729,36 @@ function unstoredSlotParents(document, project) {
174676
174729
  });
174677
174730
  }
174678
174731
  };
174679
- const visited = /* @__PURE__ */ new Set();
174680
- for (const row of document.values) {
174681
- addDerived(row);
174682
- if (typeof row.classId !== "string") continue;
174683
- const graph = lookups.virtualInstanceGraphForValue(row.id);
174684
- if (graph === null || visited.has(graph.instanceRootId)) continue;
174685
- visited.add(graph.instanceRootId);
174686
- for (const [id2, location3] of graph.locationsById) {
174687
- addDerived(location3.virtualRow);
174688
- if (location3.parentId == null) continue;
174689
- if (location3.parentSegment?.kind !== "class") continue;
174690
- slots.set(id2, {
174691
- parentId: location3.parentId,
174692
- schemaKey: location3.parentSegment.schemaKey
174693
- });
174732
+ for (const row of document.values) addDerived(row);
174733
+ function* expandSlots() {
174734
+ const lookups = headlessVirtualInstanceLookups(document, {
174735
+ initializerCompiler: documentInitializerCompiler(document, project())
174736
+ });
174737
+ const visited = /* @__PURE__ */ new Set();
174738
+ for (const row of document.values) {
174739
+ if (typeof row.classId !== "string") continue;
174740
+ const graph = lookups.virtualInstanceGraphForValue(row.id);
174741
+ if (graph === null || visited.has(graph.instanceRootId)) continue;
174742
+ visited.add(graph.instanceRootId);
174743
+ for (const [id2, location3] of graph.locationsById) {
174744
+ addDerived(location3.virtualRow);
174745
+ if (location3.parentId == null) continue;
174746
+ if (location3.parentSegment?.kind !== "class") continue;
174747
+ slots.set(id2, {
174748
+ parentId: location3.parentId,
174749
+ schemaKey: location3.parentSegment.schemaKey
174750
+ });
174751
+ }
174752
+ yield;
174694
174753
  }
174695
174754
  }
174696
- return slots;
174755
+ const expansion = expandSlots();
174756
+ return (id2) => {
174757
+ while (!slots.has(id2)) {
174758
+ if (expansion.next().done) break;
174759
+ }
174760
+ return slots.get(id2);
174761
+ };
174697
174762
  }
174698
174763
  function memberReferenceType(context, member) {
174699
174764
  if (typeof member.classId === "string") {
@@ -185652,12 +185717,15 @@ function hydrateProspectiveMemberOverride(data, members2) {
185652
185717
  return hydrated;
185653
185718
  }
185654
185719
  function validProspectiveMember(member) {
185655
- if (!isMember(member)) {
185656
- throw new Error(
185657
- `Prospective animation validation received an invalid member record ${recordLabel(member)}.`
185658
- );
185659
- }
185660
- return member;
185720
+ if (isMember(member)) return member;
185721
+ const authored = readWithoutClientDerivedBodies(
185722
+ ProjectRecordKind.Member,
185723
+ member
185724
+ );
185725
+ if (isMember(authored)) return authored;
185726
+ throw new Error(
185727
+ `Prospective animation validation received an invalid member record ${recordLabel(member)}.`
185728
+ );
185661
185729
  }
185662
185730
  function closeInheritedCallableSignature(member, classes, members2) {
185663
185731
  if (typeof member.id !== "string") return member;
@@ -186081,6 +186149,7 @@ var sourceSchemaValidation, pulledAnimationValues, animationCompilationProject,
186081
186149
  var init_workspace_status_core = __esm({
186082
186150
  "src/project-source/workspace-status-core.ts"() {
186083
186151
  "use strict";
186152
+ init_neo_script_derived_bodies();
186084
186153
  init_identity_rewriter();
186085
186154
  init_initializer_replay();
186086
186155
  init_project_version_whole_graph_validation();
@@ -194430,6 +194499,10 @@ function expressionTokensBefore(tokens, endExclusive) {
194430
194499
  start = name;
194431
194500
  continue;
194432
194501
  }
194502
+ if (depth === 0 && token.kind === "keyword" && token.text === "in" && ![".", "?."].includes(tokens[start - 1]?.text ?? "")) {
194503
+ const opener = receiverEnclosingOpeners(tokens)[start] ?? -1;
194504
+ if (tokens[opener - 1]?.text === "foreach") break;
194505
+ }
194433
194506
  if (depth === 0 && token.text === "when" && start < endExclusive - 1 && ![".", "?.", "!", "[", "<"].includes(tokens[start + 1]?.text ?? "") && followsSwitchPattern(tokens, start))
194434
194507
  break;
194435
194508
  if (depth === 0 && (token.kind === "punctuation" && [";", "{", "}", ",", ":", "?"].includes(token.text) || // Only `?.` and `!` continue a receiver; every other operator
@@ -243713,7 +243786,7 @@ var releaseWorkspaceLock = () => void 0;
243713
243786
  async function main() {
243714
243787
  const args = parseArgs(process.argv.slice(2));
243715
243788
  if (args.command === "--version") {
243716
- console.log("0.89.13");
243789
+ console.log("0.89.14");
243717
243790
  return;
243718
243791
  }
243719
243792
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
@@ -22186,6 +22186,8 @@ var InitializerReadSet = class {
22186
22186
  staticMemberIds = /* @__PURE__ */ new Set();
22187
22187
  worldQueries = new WorldGridQueryReadSet();
22188
22188
  hasValuePlacementRead = false;
22189
+ hasUnnamedValuePlacementRead = false;
22190
+ placementIds = /* @__PURE__ */ new Set();
22189
22191
  hasUnattributableValueRead = false;
22190
22192
  /**
22191
22193
  * The unattributable reads by scope, for a fork of the same document to
@@ -22209,8 +22211,10 @@ var InitializerReadSet = class {
22209
22211
  return;
22210
22212
  }
22211
22213
  };
22212
- recordValuePlacementRead = () => {
22214
+ recordValuePlacementRead = (valueId) => {
22213
22215
  this.hasValuePlacementRead = true;
22216
+ if (valueId === void 0) this.hasUnnamedValuePlacementRead = true;
22217
+ else this.placementIds.add(valueId);
22214
22218
  };
22215
22219
  };
22216
22220
 
@@ -22815,6 +22819,7 @@ var NeoScriptGetterSubscriptions = class _NeoScriptGetterSubscriptions {
22815
22819
  const reads = new InitializerReadSet();
22816
22820
  for (const key of [
22817
22821
  "valueIds",
22822
+ "placementIds",
22818
22823
  "localizedTextIds",
22819
22824
  "variantIds",
22820
22825
  "containerIds",
@@ -22826,6 +22831,7 @@ var NeoScriptGetterSubscriptions = class _NeoScriptGetterSubscriptions {
22826
22831
  for (const read of source.reads.worldQueries)
22827
22832
  reads.worldQueries.add(read);
22828
22833
  reads.hasValuePlacementRead = source.reads.hasValuePlacementRead;
22834
+ reads.hasUnnamedValuePlacementRead = source.reads.hasUnnamedValuePlacementRead;
22829
22835
  reads.hasUnattributableValueRead = source.reads.hasUnattributableValueRead;
22830
22836
  cloned.setReads(copy, reads);
22831
22837
  }
@@ -46965,7 +46971,9 @@ function materializationReadTracking(recorder) {
46965
46971
  for (const read of readTracking.worldQueries) {
46966
46972
  recorder.recordWorldQueryRead?.(read);
46967
46973
  }
46968
- if (readTracking.hasValuePlacementRead) {
46974
+ for (const id of readTracking.placementIds)
46975
+ recorder.recordValuePlacementRead(id);
46976
+ if (readTracking.hasUnnamedValuePlacementRead) {
46969
46977
  recorder.recordValuePlacementRead();
46970
46978
  }
46971
46979
  for (const containerId of readTracking.containerEntryReadIds) {
@@ -50204,6 +50212,10 @@ function createHeadlessVirtualInstanceResolver(args) {
50204
50212
  if (valueId !== void 0) {
50205
50213
  for (const active of activeReads) active.placementIds.add(valueId);
50206
50214
  ownReads()?.placementIds.add(valueId);
50215
+ } else {
50216
+ for (const active of activeReads) active.global = true;
50217
+ const own = ownReads();
50218
+ if (own !== void 0) own.global = true;
50207
50219
  }
50208
50220
  args.readRecorder?.recordValuePlacementRead(valueId);
50209
50221
  },
@@ -50441,7 +50453,9 @@ function createHeadlessVirtualInstanceResolver(args) {
50441
50453
  for (const id of reads.variantIds) external.recordVariantRead(id);
50442
50454
  for (const id of reads.staticMemberIds)
50443
50455
  external.recordStaticMemberRead(id);
50444
- if (reads.placementIds.size > 0) external.recordValuePlacementRead();
50456
+ for (const id of reads.placementIds)
50457
+ external.recordValuePlacementRead(id);
50458
+ if (reads.global) external.recordGlobalRead("virtual-instance-expansion");
50445
50459
  for (const reason of reads.corpusIndexReads)
50446
50460
  external.recordGlobalRead(reason);
50447
50461
  }
@@ -50636,7 +50650,8 @@ function createHeadlessVirtualInstanceResolver(args) {
50636
50650
  share.containerChanged
50637
50651
  );
50638
50652
  if (shared === null) return null;
50639
- recorder.recordValuePlacementRead();
50653
+ for (const id of shared.reads.placementIds)
50654
+ recorder.recordValuePlacementRead(id);
50640
50655
  for (const id of shared.reads.valueIds) recorder.recordValueRead(id);
50641
50656
  for (const id of shared.reads.containerIds)
50642
50657
  recorder.recordContainerMembershipRead(id);
@@ -50671,12 +50686,10 @@ function createHeadlessVirtualInstanceResolver(args) {
50671
50686
  const virtualInstanceRowsForValue = (receiverValueId) => {
50672
50687
  const adopted = adoptSharedExpansion(receiverValueId);
50673
50688
  if (adopted !== null) return adopted;
50674
- recorder.recordValuePlacementRead();
50675
50689
  recorder.recordValueRead(receiverValueId);
50676
50690
  const readPlacement = (valueId) => {
50677
- for (const active of activeReads) active.placementIds.add(valueId);
50678
- ownReads()?.placementIds.add(valueId);
50679
- return placements().get(valueId) ?? null;
50691
+ recorder.recordValuePlacementRead(valueId);
50692
+ return rawById.has(valueId) ? placements().get(valueId) ?? null : null;
50680
50693
  };
50681
50694
  let placement = readPlacement(receiverValueId);
50682
50695
  const roots = [];
@@ -50713,10 +50726,19 @@ function createHeadlessVirtualInstanceResolver(args) {
50713
50726
  observeRoot(known.instanceRootId);
50714
50727
  observeOwner(valueId, true);
50715
50728
  }
50716
- if (known?.instanceRootId === valueId) return known;
50729
+ const readKnown = (graph2) => {
50730
+ const result = resultByRootId.get(graph2.instanceRootId);
50731
+ if (result !== void 0) {
50732
+ settledRows(result);
50733
+ forwardReads(result.reads);
50734
+ }
50735
+ observeOwner(valueId, true);
50736
+ return graph2;
50737
+ };
50738
+ if (known?.instanceRootId === valueId) return readKnown(known);
50717
50739
  const stored = rawById.get(valueId);
50718
50740
  if (known !== void 0 && (stored === void 0 || !isStoredClassResolutionRoot(stored) || isMaterializedSpine(known.locationsById.get(valueId))))
50719
- return known;
50741
+ return readKnown(known);
50720
50742
  virtualInstanceRowsForValue(valueId);
50721
50743
  if (frames.length === 0) return graphByValueId.get(valueId) ?? null;
50722
50744
  const graph = observeSettled(valueId);
@@ -50816,7 +50838,7 @@ function createHeadlessVirtualInstanceResolver(args) {
50816
50838
  readRecorder: recorder,
50817
50839
  recordReads: (target, read) => {
50818
50840
  let reads = callerReads.get(target);
50819
- if (reads === void 0) {
50841
+ if (reads === void 0 || reads.valueIds !== target.valueIds || reads.containerIds !== target.containerIds || reads.localizedTextIds !== target.localizedTextIds || reads.variantIds !== target.variantIds || reads.staticMemberIds !== target.staticMemberIds) {
50820
50842
  reads = {
50821
50843
  valueIds: target.valueIds,
50822
50844
  containerIds: target.containerIds,
@@ -50840,6 +50862,9 @@ function createHeadlessVirtualInstanceResolver(args) {
50840
50862
  return read();
50841
50863
  } finally {
50842
50864
  activeReads.pop();
50865
+ for (const id of reads.placementIds)
50866
+ target.recordValuePlacementRead(id);
50867
+ reads.placementIds.clear();
50843
50868
  if (reads.global)
50844
50869
  target.recordUnattributableValueRead("virtual-instance-expansion");
50845
50870
  for (const reason of reads.corpusIndexReads)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.89.13",
3
+ "version": "0.89.14",
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.89.13 -->
12
+ <!-- reviewed-through-cli: 0.89.14 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -143,7 +143,7 @@ The marker near the top of `SKILL.md` must exactly match the package version,
143
143
  and this quoted copy is checked too:
144
144
 
145
145
  ```html
146
- <!-- reviewed-through-cli: 0.89.13 -->
146
+ <!-- reviewed-through-cli: 0.89.14 -->
147
147
  ```
148
148
 
149
149
  Keep this skill on current behavior. Upgrade steps, rollout prerequisites,
@@ -28,7 +28,8 @@ production source stops the run. It skips the extra source round trip that
28
28
  `neo push --dry-run` performs.
29
29
 
30
30
  - Specs live in colocated `*.spec.neo` files that never sync. Object
31
- initializers work there as in bodies.
31
+ initializers work there as in bodies. The editor resolves project roots in
32
+ loop sources, including nested `foreach` loops over `root.Assets` collections.
32
33
  - Files run in parallel on up to four CPU workers. Set `test.maxWorkers` in
33
34
  `neo.json`, pass `--maxWorkers <n>`, or `--runInBand`. Hook and test order
34
35
  within a file is preserved; fixtures and mocks stay isolated.
@@ -7,7 +7,9 @@ and generated C# under `<generatedTypesDirectory>/Generated/`.
7
7
  `Library/NeoCompose/export.json` is written last for Unity to ingest.
8
8
 
9
9
  - Only changed files are written. Codegen is skipped when schema inputs are
10
- unchanged, but content is still validated. Repeated exports, undos, and
10
+ unchanged, but content is still validated. Initializer checks share settled
11
+ dependencies and resolve independent children without expanding every sibling.
12
+ Repeated exports, undos, and
11
13
  reverts reuse cached results after verifying output bytes.
12
14
  - The Unity project comes from `unityConfigPath`. Without it, an interactive
13
15
  export offers to link the one config asset naming this project; scripts get
@@ -22,7 +24,9 @@ and generated C# under `<generatedTypesDirectory>/Generated/`.
22
24
  owner or one of its constructed base types.
23
25
 
24
26
  Status and export stay clean when animation defaults use initializers or
25
- constructor arguments select collection rows through Lookups.
27
+ constructor arguments select collection rows through Lookups. Changing an
28
+ override method signature keeps its member ID; animation validation recompiles
29
+ a retained body against the new inherited signature.
26
30
 
27
31
  ## Local exports
28
32