@neocompose/cli 0.28.0 → 0.29.0

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,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.29.0] - 2026-08-12
4
+
5
+ ### Added
6
+
7
+ - `Reference<T>(id: "...", withProvenance: true)` for executable NeoScript.
8
+ The opt-in flag resolves an authored row id to the nearest clone carrying it
9
+ as `sourceValueId` in the lexical receiver's ownership graph. It defaults to
10
+ `false`, preserving exact-ID behavior for every existing reference.
11
+
12
+ ### Changed
13
+
14
+ - Animation child selectors created by the world UI and the legacy
15
+ `Child`-to-`Selector` reconciler now opt into provenance so clips authored
16
+ against a class default select each placement's cloned child.
17
+ - Unity export schema version is 19 and NeoScript compiler revision is 9.
18
+ Older SDKs reject the export rather than silently ignoring provenance and
19
+ applying an override to the shared authored row.
20
+
3
21
  ## [0.28.0] - 2026-08-12
4
22
 
5
23
  ### Added
package/dist/neo.mjs CHANGED
@@ -7891,7 +7891,7 @@ var NEOSCRIPT_COMPILER_REVISION;
7891
7891
  var init_strict_ir = __esm({
7892
7892
  "../packages/neoscript-language/src/strict-ir.ts"() {
7893
7893
  "use strict";
7894
- NEOSCRIPT_COMPILER_REVISION = 8;
7894
+ NEOSCRIPT_COMPILER_REVISION = 9;
7895
7895
  }
7896
7896
  });
7897
7897
 
@@ -10597,9 +10597,34 @@ var init_strict_resolver = __esm({
10597
10597
  expression.pos
10598
10598
  );
10599
10599
  }
10600
- if (expression.args.length !== 1 || expression.argumentNames?.[0] !== "id" || expression.args[0]?.kind !== "litString" || expression.args[0].value.length === 0) {
10600
+ const hasProvenanceArgument = expression.args.length === 2;
10601
+ if (expression.args.length < 1 || expression.args.length > 2) {
10601
10602
  throw new CompileError(
10602
- "Reference<T> requires exactly one named id: string argument.",
10603
+ "Reference<T> requires id: string and optionally withProvenance: bool.",
10604
+ expression.pos
10605
+ );
10606
+ }
10607
+ if (expression.argumentNames?.[0] !== "id") {
10608
+ throw new CompileError(
10609
+ "Reference<T> requires id: as its first named argument.",
10610
+ expression.pos
10611
+ );
10612
+ }
10613
+ if (expression.args[0]?.kind !== "litString" || expression.args[0].value.length === 0) {
10614
+ throw new CompileError(
10615
+ "Reference<T> id must be a non-empty string literal.",
10616
+ expression.pos
10617
+ );
10618
+ }
10619
+ if (hasProvenanceArgument && expression.argumentNames?.[1] !== "withProvenance") {
10620
+ throw new CompileError(
10621
+ "Reference<T>'s second named argument must be withProvenance: bool.",
10622
+ expression.pos
10623
+ );
10624
+ }
10625
+ if (hasProvenanceArgument && expression.args[1]?.kind !== "litBool") {
10626
+ throw new CompileError(
10627
+ "Reference<T> withProvenance must be a boolean literal.",
10603
10628
  expression.pos
10604
10629
  );
10605
10630
  }
@@ -10613,7 +10638,8 @@ var init_strict_resolver = __esm({
10613
10638
  return {
10614
10639
  pointer: {
10615
10640
  type: "reference" /* Reference */,
10616
- valueId: expression.args[0].value
10641
+ valueId: expression.args[0].value,
10642
+ ...expression.args[1]?.kind === "litBool" && expression.args[1].value ? { withProvenance: true } : {}
10617
10643
  },
10618
10644
  type: referencedType
10619
10645
  };
@@ -21834,7 +21860,9 @@ function validateReferenceExpression(expression, expected, environment, uri, ran
21834
21860
  const typeArguments = expression.typeArguments ?? [];
21835
21861
  const names = expression.argumentNames ?? [];
21836
21862
  const idIndex = names.findIndex((name) => name === "id");
21863
+ const provenanceIndex = names.findIndex((name) => name === "withProvenance");
21837
21864
  const hasId = idIndex >= 0;
21865
+ const hasProvenance = provenanceIndex >= 0;
21838
21866
  const argument2 = expression.args[hasId ? idIndex : 0];
21839
21867
  const malformed = (message) => pushDiagnostic(
21840
21868
  diagnostics,
@@ -21878,9 +21906,11 @@ function validateReferenceExpression(expression, expected, environment, uri, ran
21878
21906
  }
21879
21907
  return;
21880
21908
  }
21881
- if (expression.args.length !== 1 || hasId && (idIndex !== 0 || names.some((name) => name !== "id")) || !hasId && names.some((name) => name !== null)) {
21909
+ const validIdShape = hasId && idIndex === 0 && expression.args.length === (hasProvenance ? 2 : 1) && (!hasProvenance || provenanceIndex === 1) && names.every((name) => name === "id" || name === "withProvenance");
21910
+ const validSymbolShape = !hasId && !hasProvenance && expression.args.length === 1 && names.every((name) => name === null);
21911
+ if (!validIdShape && !validSymbolShape) {
21882
21912
  malformed(
21883
- "Reference requires exactly one symbol argument or one named id: string argument."
21913
+ "Reference requires one symbol argument, or id: string followed by optional withProvenance: bool."
21884
21914
  );
21885
21915
  return;
21886
21916
  }
@@ -21890,6 +21920,10 @@ function validateReferenceExpression(expression, expected, environment, uri, ran
21890
21920
  malformed("Reference id must be one non-empty string literal.");
21891
21921
  return;
21892
21922
  }
21923
+ if (hasProvenance && expression.args[provenanceIndex]?.kind !== "litBool") {
21924
+ malformed("Reference withProvenance must be a boolean literal.");
21925
+ return;
21926
+ }
21893
21927
  if (!explicit && expected?.name !== "string") {
21894
21928
  malformed("Reference(id: ...) requires an explicit generic target type.");
21895
21929
  return;
@@ -26596,7 +26630,11 @@ function projectSignatureHelp(analysis, document, position) {
26596
26630
  return signatureResult(`@${callee.text}`, parameters, activeParameter);
26597
26631
  }
26598
26632
  if (callee.text === "Reference") {
26599
- return signatureResult("Reference", ["symbol", "id"], activeParameter);
26633
+ return signatureResult(
26634
+ "Reference",
26635
+ ["symbol or id", "withProvenance = false"],
26636
+ activeParameter
26637
+ );
26600
26638
  }
26601
26639
  if (callee.text === "Pause") {
26602
26640
  return signatureResult(
@@ -38455,7 +38493,9 @@ function isNSKeyOf(value) {
38455
38493
  }
38456
38494
  function isNSPointerReference(value) {
38457
38495
  const v = value;
38458
- return v?.type === "reference" /* reference */ && typeof v?.valueId === "string";
38496
+ if (v?.type !== "reference" /* reference */) return false;
38497
+ if (typeof v.valueId !== "string") return false;
38498
+ return v.withProvenance === void 0 || typeof v.withProvenance === "boolean";
38459
38499
  }
38460
38500
  function isNSPointerVariable(value) {
38461
38501
  const v = value;
@@ -51301,9 +51341,14 @@ function collectFunctions(args) {
51301
51341
  for (const action of node.actions) {
51302
51342
  if (!isObjectRecord2(action) || action.type !== 0 || !isObjectRecord2(action.logic))
51303
51343
  continue;
51304
- if (action.logic.sourceInline === true || action.logic.type === 0 && isCompleteUIAction(action.logic.action))
51305
- continue;
51306
51344
  const useId = stringField(action, "id");
51345
+ if (action.logic.sourceInline === true) continue;
51346
+ if (action.logic.type === 0) {
51347
+ if (isCompleteUIAction(action.logic.action)) continue;
51348
+ throw new Error(
51349
+ `Dialogue action "${useId}" on actions node "${nodeId}" is incomplete. Complete or remove the action in Neo Compose before pulling.`
51350
+ );
51351
+ }
51307
51352
  add(action.logic, `action:${useId}`, "void");
51308
51353
  }
51309
51354
  }
@@ -54332,6 +54377,16 @@ function lowerExpressionValue(context, expression, declaredExpected, ownerClass,
54332
54377
  if (expression.callee.kind === "ident" && expression.callee.name === "Reference") {
54333
54378
  const index = expression.argumentNames?.findIndex((name) => name === "id") ?? -1;
54334
54379
  if (index >= 0) {
54380
+ if (expression.argumentNames?.includes("withProvenance") === true) {
54381
+ throw new Error(
54382
+ `${path} writes Reference(id: ..., withProvenance: ...) as a persisted member value. Provenance-aware references are executable NeoScript values and must be used inside a function or delegate closure.`
54383
+ );
54384
+ }
54385
+ if (expression.args.length !== 1 || expression.argumentNames?.some((name) => name !== "id")) {
54386
+ throw new Error(
54387
+ `${path} writes Reference(id: ...) with unsupported arguments.`
54388
+ );
54389
+ }
54335
54390
  const idArgument = expression.args[index];
54336
54391
  if (idArgument?.kind === "litString") return idArgument.value;
54337
54392
  throw new Error(
@@ -59911,13 +59966,14 @@ function evaluatorOwnershipDistances(rowId, indexes) {
59911
59966
  indexes.ownershipDistancesByRowId.set(rowId, distances);
59912
59967
  return distances;
59913
59968
  }
59914
- function resolveRuntimeReferenceRow(sourceValueId, ctx) {
59969
+ function resolveRuntimeReferenceRow(sourceValueId, ctx, withProvenance) {
59915
59970
  const direct = evalValueById(
59916
59971
  ctx,
59917
59972
  sourceValueId,
59918
59973
  ctx.__runtimeSessionValues,
59919
59974
  ctx.__valueOverlay
59920
59975
  );
59976
+ if (!withProvenance) return direct;
59921
59977
  const receiver = trackedRowForValueReference(ctx.thisValue, ctx);
59922
59978
  if (receiver === null) return direct;
59923
59979
  const indexes = evaluatorIndexes(ctx);
@@ -62128,7 +62184,11 @@ function evalPointer(pointer, scope, ctx) {
62128
62184
  return scope.get(pointer.variableId);
62129
62185
  }
62130
62186
  case "reference" /* reference */: {
62131
- const row = resolveRuntimeReferenceRow(pointer.valueId, ctx);
62187
+ const row = resolveRuntimeReferenceRow(
62188
+ pointer.valueId,
62189
+ ctx,
62190
+ pointer.withProvenance === true
62191
+ );
62132
62192
  if (!row) {
62133
62193
  throw new NSGetterRuntimeError(
62134
62194
  `Missing value reference: ${pointer.valueId}`
@@ -104470,7 +104530,7 @@ var init_registry2 = __esm({
104470
104530
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
104471
104531
  formatVersion: 3,
104472
104532
  contractVersion: "3.10",
104473
- cliVersion: "0.28.0",
104533
+ cliVersion: "0.29.0",
104474
104534
  projectFileUploadBatchSize: 32,
104475
104535
  documentRecords: {
104476
104536
  member: {
@@ -111036,7 +111096,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
111036
111096
  async function main() {
111037
111097
  const args = parseArgs(process.argv.slice(2));
111038
111098
  if (args.command === "--version") {
111039
- console.log("0.28.0");
111099
+ console.log("0.29.0");
111040
111100
  return;
111041
111101
  }
111042
111102
  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.28.0",
3
+ "version": "0.29.0",
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.28.0 -->
12
+ <!-- reviewed-through-cli: 0.29.0 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -109,12 +109,14 @@ override replaces the whole leaf.
109
109
  Frame overrides may change `Enabled`, `FlipX`, and `SortingOrder`; they may
110
110
  not add, remove, or reorder `Children`.
111
111
 
112
- Address direct children through selector delegates. A selector should implement
113
- the project's own stable identity contract, such as an immutable `Name`, slug,
114
- or semantic position; do not turn a mutable object-row ID back into identity by
115
- returning `Reference<T>(id: "...")`. Missing optional child slots are skipped
116
- with diagnostics when the authored slot is absent, while stale pre-provenance
117
- placements may still fail closed.
112
+ Address direct children through selector delegates. Prefer the project's own
113
+ stable identity contract, such as an immutable `Name`, slug, or semantic
114
+ position. When a selector means one specific authored child slot whose
115
+ placement clones carry `sourceValueId`, return
116
+ `Reference<T>(id: "<authored-child-id>", withProvenance: true)`. The flag is
117
+ opt-in and defaults to `false`; without it the ID is matched exactly. Missing
118
+ optional child slots are skipped with diagnostics when the authored slot is
119
+ absent, while stale pre-provenance placements may still fail closed.
118
120
 
119
121
  A placement row carries its `assetClassId` binding and optional `assetValueId`
120
122
  override beside its schema keys. They are row provenance, not declared schema:
@@ -176,11 +178,11 @@ class LegPart : NeoObject {
176
178
  ```
177
179
 
178
180
  Selectors may also be compatible inline closures. Prefer a named function when
179
- the identity rule is reused or deserves an explicit failure message. Never use
180
- a selector merely to wrap `Reference<T>(id: "...")`; that recreates the
181
- row-ID coupling selectors were introduced to remove. For new track rows, omit
182
- `@id` and let the successful push assign it. Preserve IDs already present on
183
- pulled rows.
181
+ the identity rule is reused or deserves an explicit failure message. An exact
182
+ `Reference<T>(id: "...")` recreates the row-ID coupling selectors were
183
+ introduced to remove; use `withProvenance: true` only for a deliberately
184
+ authored slot identity. For new track rows, omit `@id` and let the successful
185
+ push assign it. Preserve IDs already present on pulled rows.
184
186
 
185
187
  A selector argument such as `this.SelectPants` is evaluated in the lexical
186
188
  scope of the class that declares the clip. Here `this` is the `LegPart`, not
@@ -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.28.0 -->
86
+ <!-- reviewed-through-cli: 0.29.0 -->
87
87
  ```
88
88
 
89
89
  The quoted version above is checked too, so this instruction cannot go stale
@@ -83,6 +83,7 @@ supports:
83
83
  Reference(Assets.Capitol)
84
84
  Reference(root.Assets.Cosmetics.Pants)
85
85
  Reference<Outpost>(id: "capitol-value-id")
86
+ Reference<NeoSpriteObject>(id: "authored-child-id", withProvenance: true)
86
87
  Reference<PantsAsset>(key: "pants.long")
87
88
  Reference<PantsAsset>(key: "pants.long", index: Slug)
88
89
  Reference<Dialogue>(id: "capitol-dialogue-id")
@@ -95,14 +96,20 @@ Reference<Dialogue>(id: "capitol-dialogue-id")
95
96
  - Use the generic `id:` form when the ID is the only target information. A
96
97
  `Reference` call whose argument is neither `id:` nor `key:` is rejected by
97
98
  name rather than read as an ID.
99
+ - Inside executable NeoScript, add `withProvenance: true` when the ID names an
100
+ authored row and the reference must select its nearest `sourceValueId` clone
101
+ in the lexical receiver's ownership graph. The parameter defaults to `false`;
102
+ absent or explicit `false` keeps exact-ID behavior. This option is not a
103
+ persisted member-default form.
98
104
  - Both forms are legal in a member's declaration default, not only inside a
99
105
  value graph. A key written there resolves after every pass has run, so it may
100
106
  name a row the same push creates.
101
107
  - Keep genuine structural references ID-based when their collection has no
102
108
  stable symbol, path, or key surface. Animation track and frame-override
103
109
  targets are selectors, not structural references: encode a stable
104
- project-owned identity such as `Name` or slug in the selector instead of
105
- wrapping a row ID in `Reference<T>(id: "...")`.
110
+ project-owned identity such as `Name` or slug. The exception is an authored
111
+ child slot whose placement clones retain `sourceValueId`: select that slot
112
+ with `Reference<T>(id: "...", withProvenance: true)`.
106
113
 
107
114
  Declaration and file order do not affect resolution within one push. Cyclic
108
115
  identity references are legal because they resolve IDs rather than evaluate a