@neocompose/cli 0.61.2 → 0.61.3

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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.61.3] - 2026-10-01
4
+
5
+ - Resolve `collectionValue: Reference(root.Assets.Quests.Daily)` through construction defaults, including classes and empty collections declared in the same push. Preparation creates the required rows and associations together; no preliminary push or manually assigned value IDs are needed.
6
+ - Preserve generated collection identities through source verification and subsequent pulls. Report one source diagnostic when a collection path cannot resolve.
7
+ - Capture root-path reads through construction defaults as executable initializers, including nested object fields. Preserve sparse receiver identities during read-only construction replay so local and web preparation can resolve those reads.
8
+
3
9
  ## [0.61.2] - 2026-09-30
4
10
 
5
11
  - Delete hidden collection entry members when their owning class or collection declaration is removed or changed to a scalar, while preserving entries still used by surviving declarations.
package/dist/neo.mjs CHANGED
@@ -63084,6 +63084,7 @@ function isSourceMemberValueRead(context, expression, ownerClass) {
63084
63084
  while (current.kind === "annotated") current = current.expression;
63085
63085
  const path = expressionMemberPath(current);
63086
63086
  if (path === null) return false;
63087
+ if (path.startsWith("root.")) return true;
63087
63088
  const parts = path.split(".");
63088
63089
  let classId;
63089
63090
  let memberName;
@@ -75760,7 +75761,7 @@ function withEvaluationRuntime(ctx, writes) {
75760
75761
  ctx.__runtimeSessionValues
75761
75762
  );
75762
75763
  const referencesAlreadyRemapped = ctx.__valueOverlay !== void 0 && ctx.__runtimeReferenceRemap !== void 0;
75763
- const remap = (value) => referencesAlreadyRemapped ? value : remapRuntimeReferences(
75764
+ const remap = (value) => referencesAlreadyRemapped || ctx.storedConstructionReplay === true ? value : remapRuntimeReferences(
75764
75765
  value,
75765
75766
  overlay,
75766
75767
  referenceRemap,
@@ -85753,14 +85754,15 @@ function claimVirtualIdentity(args) {
85753
85754
  virtualId: args.fixedVirtualId
85754
85755
  };
85755
85756
  }
85756
- const derivedId = virtualValueId(args.instanceRootId, args.rawSourceIdentity);
85757
+ const valueId = (sourceIdentity4) => args.instanceRootId.startsWith("__pending__:") || hasPendingVirtualSourceIdentity(sourceIdentity4) ? pendingVirtualValueId(args.instanceRootId, sourceIdentity4) : virtualValueId(args.instanceRootId, sourceIdentity4);
85758
+ const derivedId = valueId(args.rawSourceIdentity);
85757
85759
  const claimedPath = args.claimedVirtualIds.get(derivedId);
85758
85760
  if (claimedPath === void 0 || claimedPath === args.pathKey) {
85759
85761
  args.claimedVirtualIds.set(derivedId, args.pathKey);
85760
85762
  return { sourceIdentity: args.rawSourceIdentity, virtualId: derivedId };
85761
85763
  }
85762
85764
  const sourceIdentity3 = `${args.rawSourceIdentity}:${args.pathKey}`;
85763
- const virtualId = virtualValueId(args.instanceRootId, sourceIdentity3);
85765
+ const virtualId = valueId(sourceIdentity3);
85764
85766
  args.claimedVirtualIds.set(virtualId, args.pathKey);
85765
85767
  return { sourceIdentity: sourceIdentity3, virtualId };
85766
85768
  }
@@ -88141,6 +88143,8 @@ function createHeadlessVirtualInstanceResolver(args) {
88141
88143
  const remember = (graph, rows) => {
88142
88144
  graphRowsByRootId.set(graph.instanceRootId, rows);
88143
88145
  for (const [id2, row] of graph.rowsById) {
88146
+ if (id2 !== graph.instanceRootId && graphByValueId.get(id2)?.instanceRootId === id2)
88147
+ continue;
88144
88148
  expandedRowById.set(id2, row);
88145
88149
  graphByValueId.set(id2, graph);
88146
88150
  if (typeof row.value === "object" && row.value !== null) {
@@ -88148,6 +88152,8 @@ function createHeadlessVirtualInstanceResolver(args) {
88148
88152
  }
88149
88153
  }
88150
88154
  for (const id2 of graph.locationsById.keys()) {
88155
+ if (id2 !== graph.instanceRootId && graphByValueId.get(id2)?.instanceRootId === id2)
88156
+ continue;
88151
88157
  graphByValueId.set(id2, graph);
88152
88158
  }
88153
88159
  graphByValueId.set(graph.instanceRootId, graph);
@@ -88266,7 +88272,10 @@ function createHeadlessVirtualInstanceResolver(args) {
88266
88272
  rowForValueReference: (value) => expandedRowByRecord.get(value) ?? null,
88267
88273
  virtualInstanceGraphForValue: (valueId) => {
88268
88274
  const known = graphByValueId.get(valueId);
88269
- if (known !== void 0) return known;
88275
+ if (known?.instanceRootId === valueId) return known;
88276
+ const stored = rawById.get(valueId);
88277
+ if (known !== void 0 && (stored === void 0 || !isStoredClassResolutionRoot(stored)))
88278
+ return known;
88270
88279
  virtualInstanceRowsForValue(valueId);
88271
88280
  return graphByValueId.get(valueId) ?? null;
88272
88281
  },
@@ -98336,6 +98345,389 @@ var init_project_commit_preparation_scoped_loader = __esm({
98336
98345
  }
98337
98346
  });
98338
98347
 
98348
+ // ../src/runtime/neoscript/index.ts
98349
+ var init_neoscript2 = __esm({
98350
+ "../src/runtime/neoscript/index.ts"() {
98351
+ "use strict";
98352
+ init_NSGetterRuntimeError();
98353
+ init_evaluateNSGetter();
98354
+ init_evaluateInitializer();
98355
+ init_initializer_context();
98356
+ }
98357
+ });
98358
+
98359
+ // ../src/database/headless-virtual-instance-lookups.ts
98360
+ function headlessVirtualInstanceLookups(document, options = {}) {
98361
+ const members = document.members;
98362
+ const expandedDocument = expandPackedDocumentValues(document);
98363
+ const values = expandedDocument.values;
98364
+ const indexed = makeEvaluatorLookups(
98365
+ members,
98366
+ values,
98367
+ options.includeValueGraphIndexes === true ? {
98368
+ includeValueGraphIndexes: true,
98369
+ constructorInitializerDocument: {
98370
+ classes: document.classes,
98371
+ members,
98372
+ values
98373
+ }
98374
+ } : {}
98375
+ );
98376
+ const virtual = createHeadlessVirtualInstanceResolver2({
98377
+ document: {
98378
+ ...expandedDocument,
98379
+ ...options.initializerCompiler === void 0 ? {} : { initializerCompiler: options.initializerCompiler }
98380
+ },
98381
+ // A thunk: the resolver resolves nested reads through the lookups being
98382
+ // built around it.
98383
+ resolverLookups: () => lookups
98384
+ });
98385
+ const lookups = {
98386
+ ...indexed,
98387
+ // Raw first, then virtual: a materialized row always wins over the
98388
+ // expansion that would otherwise answer for it.
98389
+ valueById: (id2) => indexed.valueById(id2) ?? virtual.valueById(id2),
98390
+ rowForValueReference: (value) => indexed.rowForValueReference(value) ?? virtual.rowForValueReference(value),
98391
+ virtualInstanceRowsForValue: (receiverValueId) => virtual.virtualInstanceRowsForValue(receiverValueId),
98392
+ // Stored-construction replay evaluates `ToVariant`, and production's
98393
+ // variant-constructed placements collapse through exactly this path.
98394
+ resolveVariantInstanceGraph: (args) => resolveVariantInstanceGraphForDocument2({
98395
+ document,
98396
+ resolverLookups: lookups,
98397
+ ...args
98398
+ }),
98399
+ // A write to a virtual key must MATERIALIZE it (a pin row at the
98400
+ // deterministic virtual id plus the sparse spine); the planner needs the
98401
+ // owning graph, not just its rows.
98402
+ virtualInstanceGraphForValue: (valueId) => virtual.virtualInstanceGraphForValue(valueId)
98403
+ };
98404
+ return lookups;
98405
+ }
98406
+ var init_headless_virtual_instance_lookups = __esm({
98407
+ "../src/database/headless-virtual-instance-lookups.ts"() {
98408
+ "use strict";
98409
+ init_virtual_instance_values();
98410
+ init_packed_value_encoding();
98411
+ init_neoscript2();
98412
+ }
98413
+ });
98414
+
98415
+ // ../src/database/collection-binding-materialization.ts
98416
+ function mergeBodies(left, right) {
98417
+ if (right === void 0) return left;
98418
+ if (typeof left === "object" && left !== null && !Array.isArray(left) && typeof right === "object" && right !== null && !Array.isArray(right)) {
98419
+ return { ...left, ...right };
98420
+ }
98421
+ return right;
98422
+ }
98423
+ var CollectionBindingMaterializer;
98424
+ var init_collection_binding_materialization = __esm({
98425
+ "../src/database/collection-binding-materialization.ts"() {
98426
+ "use strict";
98427
+ init_members();
98428
+ init_virtual_instance_values();
98429
+ init_headless_virtual_instance_lookups();
98430
+ init_document_initializer_compiler();
98431
+ CollectionBindingMaterializer = class {
98432
+ constructor(document) {
98433
+ this.document = document;
98434
+ this.stored = new Map(document.values.map((row) => [row.id, row]));
98435
+ this.lookups = headlessVirtualInstanceLookups(document, {
98436
+ initializerCompiler: documentInitializerCompiler(document)
98437
+ });
98438
+ }
98439
+ document;
98440
+ rows = /* @__PURE__ */ new Map();
98441
+ pathRowsByRootId = /* @__PURE__ */ new Map();
98442
+ stored;
98443
+ lookups;
98444
+ resolvePath(rootId, segments) {
98445
+ let id2 = rootId;
98446
+ const pathRows = this.pathRowsByRootId.get(rootId) ?? /* @__PURE__ */ new Set();
98447
+ this.pathRowsByRootId.set(rootId, pathRows);
98448
+ for (const segment of segments) {
98449
+ const graph = this.lookups.virtualInstanceGraphForValue(id2);
98450
+ const row = graph?.rowsById.get(id2) ?? this.stored.get(id2);
98451
+ if (row === void 0 || !isLiteralValueContent(row)) return null;
98452
+ const body = row.value;
98453
+ if (typeof body !== "object" || body === null || Array.isArray(body))
98454
+ return null;
98455
+ const next = Reflect.get(body, segment);
98456
+ if (typeof next !== "string") return null;
98457
+ id2 = next;
98458
+ pathRows.add(id2);
98459
+ }
98460
+ return this.materialize(id2, pathRows) ? id2 : null;
98461
+ }
98462
+ /** Discover opaque virtual IDs from web selections against this candidate's roots. */
98463
+ materializeTargets(ids) {
98464
+ const missing = new Set([...ids].filter((id2) => !this.stored.has(id2)));
98465
+ if (missing.size === 0) return;
98466
+ const visited = /* @__PURE__ */ new Set();
98467
+ for (const row of this.document.values) {
98468
+ if (typeof row.classId !== "string") continue;
98469
+ const graph = this.lookups.virtualInstanceGraphForValue(row.id);
98470
+ if (graph === null || visited.has(graph.instanceRootId)) continue;
98471
+ visited.add(graph.instanceRootId);
98472
+ for (const id2 of graph.rowsById.keys()) {
98473
+ if (!missing.has(id2)) continue;
98474
+ if (this.materialize(id2)) missing.delete(id2);
98475
+ }
98476
+ if (missing.size === 0) return;
98477
+ }
98478
+ if (missing.size > 0) {
98479
+ throw new Error(
98480
+ `Collection binding targets missing values: ${[...missing].join(", ")}.`
98481
+ );
98482
+ }
98483
+ }
98484
+ locationForValue(id2) {
98485
+ return this.lookups.virtualInstanceGraphForValue(id2)?.locationsById.get(id2);
98486
+ }
98487
+ rowForValue(id2) {
98488
+ return this.rows.get(id2) ?? this.stored.get(id2);
98489
+ }
98490
+ materialize(id2, pathRows) {
98491
+ if (this.stored.has(id2) || this.rows.has(id2)) return true;
98492
+ const graph = this.lookups.virtualInstanceGraphForValue(id2);
98493
+ const row = graph?.rowsById.get(id2);
98494
+ if (graph === null || row === void 0 || !isLiteralValueContent(row))
98495
+ return false;
98496
+ const plan = planVirtualOverrideWrite({
98497
+ graph,
98498
+ valueId: id2,
98499
+ value: row.value
98500
+ });
98501
+ for (const create of plan.creates) {
98502
+ pathRows?.add(create.id);
98503
+ const { valuePlacement: _placement, ...fields } = create;
98504
+ const previous = this.rows.get(create.id);
98505
+ this.rows.set(create.id, {
98506
+ ...fields,
98507
+ value: mergeBodies(fields.value, previous?.value),
98508
+ projectId: this.document.project.id,
98509
+ createdAt: 0,
98510
+ updatedAt: 0
98511
+ });
98512
+ }
98513
+ for (const update of plan.updates) {
98514
+ pathRows?.add(update.valueId);
98515
+ const previous = this.rows.get(update.valueId) ?? this.stored.get(update.valueId);
98516
+ if (previous === void 0)
98517
+ throw new Error(
98518
+ `Collection binding has no ancestor ${update.valueId}.`
98519
+ );
98520
+ this.rows.set(update.valueId, {
98521
+ ...previous,
98522
+ value: mergeBodies(previous.value, update.value)
98523
+ });
98524
+ }
98525
+ return true;
98526
+ }
98527
+ };
98528
+ }
98529
+ });
98530
+
98531
+ // ../src/database/project-version-write-changes.ts
98532
+ function applyProjectVersionWriteChanges(document, changes) {
98533
+ return projectVersionDocumentWithChanges(document, changes, false);
98534
+ }
98535
+ function projectVersionDocumentWithChanges(document, changes, reuseUnchangedBuckets) {
98536
+ const projectRows = (rows, kind) => {
98537
+ const relevant = changes.filter((change) => change.recordKind === kind);
98538
+ if (reuseUnchangedBuckets && relevant.length === 0) return rows;
98539
+ return applyArrayChanges(rows, relevant);
98540
+ };
98541
+ const hasGranularDialogueRecords = document.dialogueRecords !== void 0 || changes.some((change) => change.recordKind === "dialogue");
98542
+ const hasGranularDialogueNodes = document.dialogueNodes !== void 0 || changes.some((change) => change.recordKind === "dialogue-node");
98543
+ const dialogueRecords = hasGranularDialogueRecords ? projectRows(document.dialogueRecords ?? [], "dialogue") : void 0;
98544
+ const dialogueNodes = hasGranularDialogueNodes ? projectRows(document.dialogueNodes ?? [], "dialogue-node") : void 0;
98545
+ const projected = {
98546
+ localizedTexts: projectRows(
98547
+ document.localizedTexts ?? [],
98548
+ "localized-text"
98549
+ ),
98550
+ localizationConfig: applySingletonChange(
98551
+ document.localizationConfig,
98552
+ changes,
98553
+ "localization-config"
98554
+ ),
98555
+ project: applySingletonChange(document.project, changes, "project"),
98556
+ members: projectRows(document.members, "member"),
98557
+ classes: projectRows(document.classes, "class"),
98558
+ // P43 §6. Declared constructors participate in the post-transaction
98559
+ // document so the whole-graph validator and every body compile see the
98560
+ // same constructor surface the transaction is about to persist.
98561
+ constructors: projectRows(document.constructors ?? [], "constructor"),
98562
+ // P67 §2.1. Same reason as constructors: without these buckets the
98563
+ // whole-graph validator, the variant ownership roots, and every body
98564
+ // compile would grade the transaction against the pre-transaction
98565
+ // variant set — so a newly pushed variant's closures would compile
98566
+ // against a document that does not contain it.
98567
+ variants: projectRows(document.variants ?? [], "variant"),
98568
+ variantFolders: projectRows(
98569
+ document.variantFolders ?? [],
98570
+ "variant-folder"
98571
+ ),
98572
+ values: projectRows(document.values ?? [], "value"),
98573
+ // Project-file staging finalizes pending uploads before schema
98574
+ // preparation. Defaults and authored instance graphs may reference a file
98575
+ // created by the same source transaction, so every construction pass must
98576
+ // see that finalized row in its post-write document.
98577
+ projectFiles: projectRows(document.projectFiles ?? [], "project-file"),
98578
+ // Import-setting templates are part of the same file graph. A new file
98579
+ // may select a new template in one source transaction, and default-value
98580
+ // materialization validates that relationship immediately against this
98581
+ // post-write document.
98582
+ textureTemplates: projectRows(
98583
+ document.textureTemplates ?? [],
98584
+ "unity-texture-template"
98585
+ ),
98586
+ audioClipTemplates: projectRows(
98587
+ document.audioClipTemplates ?? [],
98588
+ "unity-audio-clip-template"
98589
+ ),
98590
+ internalRecordRelations: projectRows(
98591
+ document.internalRecordRelations ?? [],
98592
+ "internal-record-relation"
98593
+ ),
98594
+ enums: projectRows(document.enums, "enum"),
98595
+ interfaces: projectRows(document.interfaces, "interface"),
98596
+ dialogues: dialogueRecords === void 0 || dialogueNodes === void 0 || dialogueRecords === document.dialogueRecords && dialogueNodes === document.dialogueNodes ? document.dialogues : materializeDialogues(
98597
+ dialogueRecords,
98598
+ dialogueNodes
98599
+ ),
98600
+ dialogueGroups: projectRows(
98601
+ document.dialogueGroups ?? [],
98602
+ "dialogue-group"
98603
+ ),
98604
+ migrations: projectRows(document.migrations ?? [], "migration")
98605
+ };
98606
+ return {
98607
+ ...document,
98608
+ ...projected,
98609
+ ...dialogueRecords === void 0 ? {} : { dialogueRecords },
98610
+ ...dialogueNodes === void 0 ? {} : { dialogueNodes }
98611
+ // The document spread above carries every bucket the caller's view declares
98612
+ // beyond the projected ones, so the result is the same view; TypeScript
98613
+ // cannot prove that of an object literal built over a type parameter.
98614
+ };
98615
+ }
98616
+ function applySingletonChange(current, changes, recordKind) {
98617
+ const change = changes.find(
98618
+ (candidate) => candidate.recordKind === recordKind
98619
+ );
98620
+ if (change === void 0) return current;
98621
+ if (change.operation === "delete" || change.nextData === void 0) {
98622
+ throw new Error(`Schema commit cannot delete the ${recordKind} record.`);
98623
+ }
98624
+ return change.nextData;
98625
+ }
98626
+ function applyArrayChanges(current, changes) {
98627
+ const byId = new Map(current.map((record6) => [record6.id, record6]));
98628
+ for (const change of changes) {
98629
+ if (change.operation === "delete") {
98630
+ byId.delete(change.recordId);
98631
+ continue;
98632
+ }
98633
+ if (change.nextData !== void 0) {
98634
+ byId.set(change.recordId, change.nextData);
98635
+ }
98636
+ }
98637
+ return [...byId.values()];
98638
+ }
98639
+ var init_project_version_write_changes = __esm({
98640
+ "../src/database/project-version-write-changes.ts"() {
98641
+ "use strict";
98642
+ init_projectDocumentDialogueMaterialization();
98643
+ }
98644
+ });
98645
+
98646
+ // ../src/database/prepared-collection-bindings.ts
98647
+ function materializePreparedCollectionBindings(args) {
98648
+ const targets = /* @__PURE__ */ new Set();
98649
+ for (const change of args.prepared) {
98650
+ if (change.operation === "delete") continue;
98651
+ if (change.recordKind !== "member" && change.recordKind !== "variant-folder" && change.recordKind !== "dialogue-group")
98652
+ continue;
98653
+ const data = change.nextData;
98654
+ if (typeof data !== "object" || data === null) continue;
98655
+ const binding = change.recordKind === "variant-folder" ? Reflect.get(data, "binding") : data;
98656
+ if (typeof binding !== "object" || binding === null) continue;
98657
+ const id2 = Reflect.get(binding, "collectionValueId");
98658
+ if (typeof id2 === "string") targets.add(id2);
98659
+ }
98660
+ if (targets.size === 0) return;
98661
+ const deletedTargets = /* @__PURE__ */ new Set();
98662
+ for (const change of args.prepared) {
98663
+ if (change.recordKind !== "value" || !targets.has(change.recordId))
98664
+ continue;
98665
+ if (change.operation === "delete") deletedTargets.add(change.recordId);
98666
+ else targets.delete(change.recordId);
98667
+ }
98668
+ if (targets.size === 0) return;
98669
+ for (const row of args.document.values) {
98670
+ if (!deletedTargets.has(row.id)) targets.delete(row.id);
98671
+ if (targets.size === 0) return;
98672
+ }
98673
+ const document = projectVersionDocumentWithChanges(
98674
+ args.document,
98675
+ args.prepared,
98676
+ true
98677
+ );
98678
+ const existingIds = new Set(document.values.map((row) => row.id));
98679
+ const materializer = new CollectionBindingMaterializer(document);
98680
+ materializer.materializeTargets(targets);
98681
+ const indexes2 = /* @__PURE__ */ new Map();
98682
+ args.prepared.forEach((change, index) => {
98683
+ if (change.recordKind === "value") indexes2.set(change.recordId, index);
98684
+ });
98685
+ const hashes = new Map(
98686
+ args.contentHashHeads.filter((head) => head.recordKind === "value").map((head) => [head.recordId, head.contentHash])
98687
+ );
98688
+ for (const row of materializer.rows.values()) {
98689
+ const index = indexes2.get(row.id);
98690
+ if (index !== void 0) {
98691
+ const previous = args.prepared[index];
98692
+ args.prepared[index] = {
98693
+ ...previous,
98694
+ operation: previous.operation === "delete" ? "update" : previous.operation,
98695
+ deleted: false,
98696
+ nextData: row,
98697
+ intent: previous.operation === "delete" ? createProjectVersionIntent("value.update", {
98698
+ source: "collection-binding-materialization"
98699
+ }) : previous.intent
98700
+ };
98701
+ continue;
98702
+ }
98703
+ const contentHash = hashes.get(row.id);
98704
+ if (existingIds.has(row.id) && contentHash === void 0) {
98705
+ throw new Error(
98706
+ `Collection binding ancestor "${row.id}" has no content hash.`
98707
+ );
98708
+ }
98709
+ args.prepared.push({
98710
+ recordKind: "value",
98711
+ recordId: row.id,
98712
+ operation: existingIds.has(row.id) ? "update" : "create",
98713
+ nextData: row,
98714
+ expectedBaseContentHash: contentHash ?? null,
98715
+ intent: createProjectVersionIntent(
98716
+ existingIds.has(row.id) ? "value.update" : "value.create",
98717
+ { source: "collection-binding-materialization" }
98718
+ )
98719
+ });
98720
+ }
98721
+ }
98722
+ var init_prepared_collection_bindings = __esm({
98723
+ "../src/database/prepared-collection-bindings.ts"() {
98724
+ "use strict";
98725
+ init_project_version_intents();
98726
+ init_collection_binding_materialization();
98727
+ init_project_version_write_changes();
98728
+ }
98729
+ });
98730
+
98339
98731
  // ../src/models/project/world-content-sidecar-validation.ts
98340
98732
  function cachedWorldLayerTarget(context, args) {
98341
98733
  let target = context.linkTargetsByClassId.get(args.sourceClassId);
@@ -98991,17 +99383,6 @@ var init_world_content_sidecar_validation = __esm({
98991
99383
  }
98992
99384
  });
98993
99385
 
98994
- // ../src/runtime/neoscript/index.ts
98995
- var init_neoscript2 = __esm({
98996
- "../src/runtime/neoscript/index.ts"() {
98997
- "use strict";
98998
- init_NSGetterRuntimeError();
98999
- init_evaluateNSGetter();
99000
- init_evaluateInitializer();
99001
- init_initializer_context();
99002
- }
99003
- });
99004
-
99005
99386
  // ../src/database/dialogue-node-index.ts
99006
99387
  function indexDialogueNodesByDialogue(nodes, dialogueId) {
99007
99388
  const result = /* @__PURE__ */ new Map();
@@ -100300,62 +100681,6 @@ var init_general_function_call_ir_source_recompile = __esm({
100300
100681
  }
100301
100682
  });
100302
100683
 
100303
- // ../src/database/headless-virtual-instance-lookups.ts
100304
- function headlessVirtualInstanceLookups(document, options = {}) {
100305
- const members = document.members;
100306
- const expandedDocument = expandPackedDocumentValues(document);
100307
- const values = expandedDocument.values;
100308
- const indexed = makeEvaluatorLookups(
100309
- members,
100310
- values,
100311
- options.includeValueGraphIndexes === true ? {
100312
- includeValueGraphIndexes: true,
100313
- constructorInitializerDocument: {
100314
- classes: document.classes,
100315
- members,
100316
- values
100317
- }
100318
- } : {}
100319
- );
100320
- const virtual = createHeadlessVirtualInstanceResolver2({
100321
- document: {
100322
- ...expandedDocument,
100323
- ...options.initializerCompiler === void 0 ? {} : { initializerCompiler: options.initializerCompiler }
100324
- },
100325
- // A thunk: the resolver resolves nested reads through the lookups being
100326
- // built around it.
100327
- resolverLookups: () => lookups
100328
- });
100329
- const lookups = {
100330
- ...indexed,
100331
- // Raw first, then virtual: a materialized row always wins over the
100332
- // expansion that would otherwise answer for it.
100333
- valueById: (id2) => indexed.valueById(id2) ?? virtual.valueById(id2),
100334
- rowForValueReference: (value) => indexed.rowForValueReference(value) ?? virtual.rowForValueReference(value),
100335
- virtualInstanceRowsForValue: (receiverValueId) => virtual.virtualInstanceRowsForValue(receiverValueId),
100336
- // Stored-construction replay evaluates `ToVariant`, and production's
100337
- // variant-constructed placements collapse through exactly this path.
100338
- resolveVariantInstanceGraph: (args) => resolveVariantInstanceGraphForDocument2({
100339
- document,
100340
- resolverLookups: lookups,
100341
- ...args
100342
- }),
100343
- // A write to a virtual key must MATERIALIZE it (a pin row at the
100344
- // deterministic virtual id plus the sparse spine); the planner needs the
100345
- // owning graph, not just its rows.
100346
- virtualInstanceGraphForValue: (valueId) => virtual.virtualInstanceGraphForValue(valueId)
100347
- };
100348
- return lookups;
100349
- }
100350
- var init_headless_virtual_instance_lookups = __esm({
100351
- "../src/database/headless-virtual-instance-lookups.ts"() {
100352
- "use strict";
100353
- init_virtual_instance_values();
100354
- init_packed_value_encoding();
100355
- init_neoscript2();
100356
- }
100357
- });
100358
-
100359
100684
  // ../src/database/localized-instance-override-writes.ts
100360
100685
  function collapseEditedLocalizedInstanceOverrides(args) {
100361
100686
  const edits = args.changes.filter(
@@ -100787,121 +101112,6 @@ var init_member_access_modifier_validation = __esm({
100787
101112
  }
100788
101113
  });
100789
101114
 
100790
- // ../src/database/project-version-write-changes.ts
100791
- function applyProjectVersionWriteChanges(document, changes) {
100792
- return projectVersionDocumentWithChanges(document, changes, false);
100793
- }
100794
- function projectVersionDocumentWithChanges(document, changes, reuseUnchangedBuckets) {
100795
- const projectRows = (rows, kind) => {
100796
- const relevant = changes.filter((change) => change.recordKind === kind);
100797
- if (reuseUnchangedBuckets && relevant.length === 0) return rows;
100798
- return applyArrayChanges(rows, relevant);
100799
- };
100800
- const hasGranularDialogueRecords = document.dialogueRecords !== void 0 || changes.some((change) => change.recordKind === "dialogue");
100801
- const hasGranularDialogueNodes = document.dialogueNodes !== void 0 || changes.some((change) => change.recordKind === "dialogue-node");
100802
- const dialogueRecords = hasGranularDialogueRecords ? projectRows(document.dialogueRecords ?? [], "dialogue") : void 0;
100803
- const dialogueNodes = hasGranularDialogueNodes ? projectRows(document.dialogueNodes ?? [], "dialogue-node") : void 0;
100804
- const projected = {
100805
- localizedTexts: projectRows(
100806
- document.localizedTexts ?? [],
100807
- "localized-text"
100808
- ),
100809
- localizationConfig: applySingletonChange(
100810
- document.localizationConfig,
100811
- changes,
100812
- "localization-config"
100813
- ),
100814
- project: applySingletonChange(document.project, changes, "project"),
100815
- members: projectRows(document.members, "member"),
100816
- classes: projectRows(document.classes, "class"),
100817
- // P43 §6. Declared constructors participate in the post-transaction
100818
- // document so the whole-graph validator and every body compile see the
100819
- // same constructor surface the transaction is about to persist.
100820
- constructors: projectRows(document.constructors ?? [], "constructor"),
100821
- // P67 §2.1. Same reason as constructors: without these buckets the
100822
- // whole-graph validator, the variant ownership roots, and every body
100823
- // compile would grade the transaction against the pre-transaction
100824
- // variant set — so a newly pushed variant's closures would compile
100825
- // against a document that does not contain it.
100826
- variants: projectRows(document.variants ?? [], "variant"),
100827
- variantFolders: projectRows(
100828
- document.variantFolders ?? [],
100829
- "variant-folder"
100830
- ),
100831
- values: projectRows(document.values ?? [], "value"),
100832
- // Project-file staging finalizes pending uploads before schema
100833
- // preparation. Defaults and authored instance graphs may reference a file
100834
- // created by the same source transaction, so every construction pass must
100835
- // see that finalized row in its post-write document.
100836
- projectFiles: projectRows(document.projectFiles ?? [], "project-file"),
100837
- // Import-setting templates are part of the same file graph. A new file
100838
- // may select a new template in one source transaction, and default-value
100839
- // materialization validates that relationship immediately against this
100840
- // post-write document.
100841
- textureTemplates: projectRows(
100842
- document.textureTemplates ?? [],
100843
- "unity-texture-template"
100844
- ),
100845
- audioClipTemplates: projectRows(
100846
- document.audioClipTemplates ?? [],
100847
- "unity-audio-clip-template"
100848
- ),
100849
- internalRecordRelations: projectRows(
100850
- document.internalRecordRelations ?? [],
100851
- "internal-record-relation"
100852
- ),
100853
- enums: projectRows(document.enums, "enum"),
100854
- interfaces: projectRows(document.interfaces, "interface"),
100855
- dialogues: dialogueRecords === void 0 || dialogueNodes === void 0 || dialogueRecords === document.dialogueRecords && dialogueNodes === document.dialogueNodes ? document.dialogues : materializeDialogues(
100856
- dialogueRecords,
100857
- dialogueNodes
100858
- ),
100859
- dialogueGroups: projectRows(
100860
- document.dialogueGroups ?? [],
100861
- "dialogue-group"
100862
- ),
100863
- migrations: projectRows(document.migrations ?? [], "migration")
100864
- };
100865
- return {
100866
- ...document,
100867
- ...projected,
100868
- ...dialogueRecords === void 0 ? {} : { dialogueRecords },
100869
- ...dialogueNodes === void 0 ? {} : { dialogueNodes }
100870
- // The document spread above carries every bucket the caller's view declares
100871
- // beyond the projected ones, so the result is the same view; TypeScript
100872
- // cannot prove that of an object literal built over a type parameter.
100873
- };
100874
- }
100875
- function applySingletonChange(current, changes, recordKind) {
100876
- const change = changes.find(
100877
- (candidate) => candidate.recordKind === recordKind
100878
- );
100879
- if (change === void 0) return current;
100880
- if (change.operation === "delete" || change.nextData === void 0) {
100881
- throw new Error(`Schema commit cannot delete the ${recordKind} record.`);
100882
- }
100883
- return change.nextData;
100884
- }
100885
- function applyArrayChanges(current, changes) {
100886
- const byId = new Map(current.map((record6) => [record6.id, record6]));
100887
- for (const change of changes) {
100888
- if (change.operation === "delete") {
100889
- byId.delete(change.recordId);
100890
- continue;
100891
- }
100892
- if (change.nextData !== void 0) {
100893
- byId.set(change.recordId, change.nextData);
100894
- }
100895
- }
100896
- return [...byId.values()];
100897
- }
100898
- var init_project_version_write_changes = __esm({
100899
- "../src/database/project-version-write-changes.ts"() {
100900
- "use strict";
100901
- init_projectDocumentDialogueMaterialization();
100902
- }
100903
- });
100904
-
100905
101115
  // ../src/database/member-localization-normalization.ts
100906
101116
  function planMemberLocalizationNormalization(input, now = 0) {
100907
101117
  const before = expandPackedDocumentValues(input);
@@ -103350,6 +103560,11 @@ function* prepareServerOwnedSchemaCommitPasses(args) {
103350
103560
  prepared,
103351
103561
  contentHashHeads: args.contentHashHeads
103352
103562
  });
103563
+ materializePreparedCollectionBindings({
103564
+ document: args.document,
103565
+ prepared,
103566
+ contentHashHeads: args.contentHashHeads
103567
+ });
103353
103568
  drainConstructedGraphValidations({
103354
103569
  document: args.document,
103355
103570
  prepared,
@@ -106840,6 +107055,7 @@ var SERVER_COMPILATION_PROJECT_CACHE_LIMIT, serverCompilationProjectCache, READ_
106840
107055
  var init_project_version_schema_commit = __esm({
106841
107056
  "../src/database/project-version-schema-commit.ts"() {
106842
107057
  "use strict";
107058
+ init_prepared_collection_bindings();
106843
107059
  init_document_initializer_compiler();
106844
107060
  init_effective_value_comparison();
106845
107061
  init_classes();
@@ -120807,7 +121023,8 @@ function resolveRootPathCollectionValuesV4(args) {
120807
121023
  args.registry,
120808
121024
  placements,
120809
121025
  rootValues,
120810
- fail2
121026
+ fail2,
121027
+ args.resolveVirtualPath
120811
121028
  );
120812
121029
  if (target !== null) resolved.set(member.id, target);
120813
121030
  }
@@ -120845,7 +121062,8 @@ function resolveRootPathVariantFolderCollectionValuesV4(args) {
120845
121062
  args.registry,
120846
121063
  placements,
120847
121064
  rootValues,
120848
- fail2
121065
+ fail2,
121066
+ args.resolveVirtualPath
120849
121067
  );
120850
121068
  if (target !== null) resolved.set(folder.id, target);
120851
121069
  }
@@ -120882,7 +121100,8 @@ function resolveRootPathDialogueGroupCollectionValuesV4(args) {
120882
121100
  expression: null,
120883
121101
  label: String(record6.fileFields.name)
120884
121102
  }
120885
- })
121103
+ }),
121104
+ args.resolveVirtualPath
120886
121105
  );
120887
121106
  if (target !== null) record6.fileFields.collectionValueId = target;
120888
121107
  }
@@ -120902,7 +121121,7 @@ function rootSlotValueIds(state) {
120902
121121
  });
120903
121122
  return values;
120904
121123
  }
120905
- function walkRootValuePath(path, state, registry, placements, rootValues, fail2) {
121124
+ function walkRootValuePath(path, state, registry, placements, rootValues, fail2, resolveVirtualPath) {
120906
121125
  const segments = path.split(".");
120907
121126
  const slotName = segments[1];
120908
121127
  if (segments[0] !== "root" || slotName === void 0) {
@@ -120945,10 +121164,17 @@ function walkRootValuePath(path, state, registry, placements, rootValues, fail2)
120945
121164
  }
120946
121165
  const next = placements.childrenByParentValueId.get(cursor)?.get(segment)?.childValueId;
120947
121166
  if (typeof next !== "string") {
120948
- const cursorData = state[`value:${cursor}`]?.data;
120949
- const collapsed = isObjectRecord2(cursorData) && isVirtualInstanceRootShape(cursorData);
121167
+ try {
121168
+ const resolved = resolveVirtualPath?.(slotValueId, segments.slice(2));
121169
+ if (resolved != null) return resolved;
121170
+ } catch (error) {
121171
+ fail2(
121172
+ `collectionValue path ${path} could not resolve its construction: ${error instanceof Error ? error.message : String(error)}`
121173
+ );
121174
+ return null;
121175
+ }
120950
121176
  fail2(
120951
- collapsed ? `collectionValue path ${path} reaches segment ${segment} on a collapsed instance "${cursor}": the value exists in that instance's construction but has no stored row to bind, and this push cannot materialize one. Name the row with Reference(id: ...) instead.` : `collectionValue path ${path} has no value at segment ${segment} after this push.`
121177
+ `collectionValue path ${path} has no value at segment ${segment} after this push.`
120952
121178
  );
120953
121179
  return null;
120954
121180
  }
@@ -121018,7 +121244,6 @@ var init_root_source = __esm({
121018
121244
  "use strict";
121019
121245
  init_src();
121020
121246
  init_src2();
121021
- init_instance_provenance();
121022
121247
  init_value_sources();
121023
121248
  init_source_format();
121024
121249
  init_stored_value_placements();
@@ -122636,9 +122861,35 @@ function computeWorkspaceStatus(workspace, options) {
122636
122861
  [...staticValues.records, ...memberDefaults.records, ...rootValues.records],
122637
122862
  staticMemberValueIds
122638
122863
  );
122864
+ let collectionBindings;
122865
+ const resolveVirtualPath = (rootId, segments) => {
122866
+ if (collectionBindings === void 0) {
122867
+ const candidate = /* @__PURE__ */ new Map();
122868
+ for (const [key, record6] of Object.entries(rootPathResolutionState)) {
122869
+ if (record6.file !== void 0 && isSchemaRecordKindV4(record6.recordKind) && !documentsByKey.has(key))
122870
+ continue;
122871
+ candidate.set(key, { ...record6, deleted: false });
122872
+ }
122873
+ for (const row of valueLowerRegistry.pendingValues.values()) {
122874
+ candidate.set(`value:${row.id}`, {
122875
+ recordKind: "value",
122876
+ recordId: row.id,
122877
+ contentHash: "",
122878
+ deleted: false,
122879
+ data: { id: row.id, ...encodeStoredValueRowFields(row) }
122880
+ });
122881
+ }
122882
+ const document = readPulledProjectDocumentV4(candidate);
122883
+ compilePulledProjectDocumentForEvaluationV4(document);
122884
+ collectionBindings = new CollectionBindingMaterializer(document);
122885
+ }
122886
+ const resolved = collectionBindings.resolvePath(rootId, segments);
122887
+ return resolved;
122888
+ };
122639
122889
  const resolvedCollectionPaths = resolveRootPathCollectionValuesV4({
122640
122890
  manifest,
122641
122891
  state: rootPathResolutionState,
122892
+ resolveVirtualPath,
122642
122893
  registry: valueLowerRegistry,
122643
122894
  siteForMember: (memberId) => {
122644
122895
  const span2 = sourceByKey.get(recordStateKey("member", memberId))?.span;
@@ -122667,6 +122918,7 @@ function computeWorkspaceStatus(workspace, options) {
122667
122918
  const resolvedVariantFolderPaths = resolveRootPathVariantFolderCollectionValuesV4({
122668
122919
  manifest,
122669
122920
  state: rootPathResolutionState,
122921
+ resolveVirtualPath,
122670
122922
  registry: valueLowerRegistry,
122671
122923
  siteForFolder: (folderId) => {
122672
122924
  const span2 = sourceByKey.get(
@@ -122753,7 +123005,7 @@ function computeWorkspaceStatus(workspace, options) {
122753
123005
  }
122754
123006
  }
122755
123007
  }
122756
- const rootRecords = rootValues.records;
123008
+ const rootRecords = [...rootValues.records];
122757
123009
  const variantValues = lowerVariantValueSourcesV4(
122758
123010
  loweringRecords,
122759
123011
  manifest,
@@ -122769,8 +123021,71 @@ function computeWorkspaceStatus(workspace, options) {
122769
123021
  records: supplementalRecords,
122770
123022
  manifest,
122771
123023
  state: rootPathResolutionState,
123024
+ resolveVirtualPath,
122772
123025
  registry: valueLowerRegistry
122773
123026
  });
123027
+ if (collectionBindings !== void 0) {
123028
+ const bindingSeeds = new Map(authoredValueSeeds);
123029
+ const rootRecordsById = new Map(
123030
+ rootRecords.filter((record6) => record6.recordKind === "value").map((record6) => [record6.recordId, record6])
123031
+ );
123032
+ const rootMembers = /* @__PURE__ */ new Map();
123033
+ for (const record6 of Object.values(rootPathResolutionState)) {
123034
+ if (record6.recordKind === "member" && isObjectRecord2(record6.data) && typeof record6.data.valueId === "string")
123035
+ rootMembers.set(record6.data.valueId, record6.recordId);
123036
+ }
123037
+ for (const row of collectionBindings.rows.values()) {
123038
+ if (rootPathResolutionState[`value:${row.id}`] === void 0) continue;
123039
+ const existing = rootRecordsById.get(row.id);
123040
+ if (existing !== void 0) Object.assign(existing.fileFields, row);
123041
+ else
123042
+ rootRecords.push({
123043
+ recordKind: "value",
123044
+ recordId: row.id,
123045
+ file: "Root.neo",
123046
+ line: 1,
123047
+ fileFields: { ...row }
123048
+ });
123049
+ }
123050
+ for (const [rootId, rowIds] of collectionBindings.pathRowsByRootId) {
123051
+ const memberId = rootMembers.get(rootId);
123052
+ const root = collectionBindings.rowForValue(rootId);
123053
+ if (memberId === void 0 || root === void 0) continue;
123054
+ const seed = authoredValueSeeds.get(memberId);
123055
+ const rows = new Map((seed?.values ?? []).map((row) => [row.id, row]));
123056
+ for (const id2 of rowIds) {
123057
+ if (id2 === rootId) continue;
123058
+ const row = collectionBindings.rowForValue(id2);
123059
+ const location3 = collectionBindings.locationForValue(id2);
123060
+ if (row === void 0 || !isLiteralValueContent(row) || location3 === void 0)
123061
+ throw new Error(`Collection binding row ${id2} has no member.`);
123062
+ const isNew = rootPathResolutionState[`value:${id2}`] === void 0;
123063
+ const classId = row.classId ?? (isMemberClassBase(location3.member) ? location3.member.classId : null);
123064
+ const authored = staticValueSeedRow(
123065
+ {
123066
+ ...row,
123067
+ classId,
123068
+ ...isNew && classId !== null ? {
123069
+ constructorArgs: row.constructorArgs ?? {},
123070
+ instanceConstructorId: row.instanceConstructorId ?? null
123071
+ } : {}
123072
+ },
123073
+ location3.memberId
123074
+ );
123075
+ rows.set(id2, authored);
123076
+ if (isNew) valueLowerRegistry.pendingValues.set(id2, authored);
123077
+ }
123078
+ bindingSeeds.set(memberId, {
123079
+ ...seed,
123080
+ nodeType: "literal",
123081
+ valueId: rootId,
123082
+ value: root.value,
123083
+ classId: root.classId ?? null,
123084
+ values: [...rows.values()]
123085
+ });
123086
+ }
123087
+ authoredValueSeeds = bindingSeeds;
123088
+ }
122774
123089
  const prospectiveState = overlayProspectiveSourceRecords(
122775
123090
  loweringRecords,
122776
123091
  documents,
@@ -124153,6 +124468,8 @@ var ANIMATION_DOCUMENT_RECORD_KINDS, ANIMATION_REPLAY_RECORD_KINDS, PROSPECTIVE_
124153
124468
  var init_workspace_status_core = __esm({
124154
124469
  "src/project-source/workspace-status-core.ts"() {
124155
124470
  "use strict";
124471
+ init_members();
124472
+ init_collection_binding_materialization();
124156
124473
  init_localizable_member_value_writes();
124157
124474
  init_localized_instance_override_writes();
124158
124475
  init_localization2();
@@ -126824,6 +127141,7 @@ function collectionValueObligations(manifest) {
126824
127141
  for (const member of manifest.members) {
126825
127142
  if (member.kind !== "lookup") continue;
126826
127143
  if (typeof member.collectionValueId !== "string") continue;
127144
+ if (member.collectionValueId.startsWith("__pending__:root-path:")) continue;
126827
127145
  obligations.push({
126828
127146
  kind: "collectionValue",
126829
127147
  site: {
@@ -127208,6 +127526,7 @@ function memberInitializerRequiresEvaluation(context, member, expression, source
127208
127526
  function isSchemaMemberValueRead(context, expression, source) {
127209
127527
  const path = memberPath2(annotatedValue(expression).expression);
127210
127528
  if (path === null) return false;
127529
+ if (path.startsWith("root.")) return true;
127211
127530
  const parts = path.split(".");
127212
127531
  const name = parts.at(-1);
127213
127532
  if (name === void 0) return false;
@@ -131452,10 +131771,9 @@ function classValueBody(context, member, value, visited, targetTyped, outerEnvir
131452
131771
  `${key} = ${emitValue(context, emittedChildMember, childId, {
131453
131772
  environment,
131454
131773
  definitionSite: !context.symbolsByValueId.has(childId),
131455
- // A class slot recovers this row from the owning value and schema key.
131456
- // P61 creation metadata and localization back-pointers must not turn
131457
- // that structural child into an explicitly identified source value.
131458
- writeIdentity: false,
131774
+ // Existing slots recover identity from storage. A newly pinned virtual
131775
+ // slot must keep its derived pending identity through repeat lowering.
131776
+ writeIdentity: pendingVirtualValueIdentity(childId) !== null,
131459
131777
  visited
131460
131778
  })}`
131461
131779
  );
@@ -133121,6 +133439,7 @@ var INFERRED_GENERIC_CLASS_PREFIX, MEMBER_KIND_DICTIONARY2, MEMBER_KIND_LIST2, M
133121
133439
  var init_value_sources = __esm({
133122
133440
  "src/project-source/value-sources.ts"() {
133123
133441
  "use strict";
133442
+ init_member_value_id();
133124
133443
  init_effective_value_comparison();
133125
133444
  init_member_declared_type_info();
133126
133445
  init_constructor_argument_ownership();
@@ -145703,7 +146022,7 @@ var init_registry2 = __esm({
145703
146022
  "../packages/manifest/schema-contract/registry.mjs"() {
145704
146023
  "use strict";
145705
146024
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
145706
- cliVersion: "0.61.2",
146025
+ cliVersion: "0.61.3",
145707
146026
  projectFileUploadBatchSize: 32,
145708
146027
  documentRecords: {
145709
146028
  member: {
@@ -154383,7 +154702,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
154383
154702
  async function main() {
154384
154703
  const args = parseArgs(process.argv.slice(2));
154385
154704
  if (args.command === "--version") {
154386
- console.log("0.61.2");
154705
+ console.log("0.61.3");
154387
154706
  return;
154388
154707
  }
154389
154708
  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.61.2",
3
+ "version": "0.61.3",
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.61.2 -->
12
+ <!-- reviewed-through-cli: 0.61.3 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -110,7 +110,7 @@ wrappers.
110
110
  The marker near the top of `SKILL.md` must exactly match the package version:
111
111
 
112
112
  ```html
113
- <!-- reviewed-through-cli: 0.61.2 -->
113
+ <!-- reviewed-through-cli: 0.61.3 -->
114
114
  ```
115
115
 
116
116
  The quoted version above is checked too, so this instruction cannot go stale
@@ -219,6 +219,10 @@ qualified static, or reached through a stable root path. For example,
219
219
  `protected int Slots = FeatureFlags.StartInventorySize;` evaluates the current
220
220
  static field or getter whenever a new instance is constructed; it is not baked
221
221
  into a persisted literal during push.
222
+ Root paths such as `root.Save.World.Time.Year` remain executable even when
223
+ `World` and `Time` exist only through construction defaults. This also applies
224
+ inside `new() { Year = root.Save.World.Time.Year }`: the child field retains
225
+ its initializer and reads the root when the instance is constructed.
222
226
 
223
227
  Declare overloadable constructors as members when each constructor body owns
224
228
  its parameter scope:
@@ -130,7 +130,11 @@ the edit is not a target, and lookup/dialogue references must still satisfy
130
130
  assignability, collection membership, multiplicity, and group constraints.
131
131
 
132
132
  `collectionValue` references follow the same post-push resolution and
133
- membership rules as member defaults.
133
+ membership rules as member defaults. Name the collection itself, for example
134
+ `Reference(root.Assets.Quests.Daily)`. A path may traverse construction defaults
135
+ and declarations created in the same push. Preparation materializes the target
136
+ collection and its required ancestor rows together, preserving the virtual
137
+ identities. No preliminary push or manual value ID is needed.
134
138
 
135
139
  ## Defaults and localization
136
140