@neocompose/cli 0.53.5 → 0.54.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,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.54.0] - 2026-09-22
4
+
5
+ - `NeoSortingGroup` has a `SortPoint`: where the group sorts along the transparency sort axis, as a cell offset from the object's origin-cell corner. It defaults to `(0.5, 0)`, the bottom center of the origin cell. Generated Unity classes implement `INeoSortingGroup.SortPoint`, which requires Neo Compose SDK 0.40.0.
6
+
7
+ ## [0.53.7] - 2026-09-22
8
+
9
+ - Adding a value under a row that stores packed children, such as a new list on an item whose sprite is packed, keeps those children readable in `neo test`, `neo status`, and `neo push --dry-run`. A constructed instance replayed from that row used to receive the child's id instead of its value and fail with "NSFunction argument 1 does not match its declared runtime type."
10
+ - Editing a function's or property's source drops its stale compiled body before local verification. Changing a function's parameters while keeping its `@id` no longer fails with 'Convex project document field "members" has an invalid shape'.
11
+
12
+ ## [0.53.6] - 2026-09-19
13
+
14
+ - Full project reads, including initial pulls and resyncs, fetch published snapshots directly in bounded pages, reducing Convex requests and duplicate database reads. Normal incremental pulls keep their existing delta transport.
15
+
3
16
  ## [0.53.5] - 2026-09-19
4
17
 
5
18
  - `neo test` and `neo push --dry-run` compile pending member initializers and their newly authored callable dependencies when a deletion replays a surviving constructed instance. Adding a computed default and deleting an owned row in one push no longer fails with "has no compiled body".
package/dist/neo.mjs CHANGED
@@ -38465,16 +38465,26 @@ function composeDocumentRecord(recordKind, recordId, authoredFields, baseRecord)
38465
38465
  }
38466
38466
  assertKnownDocumentFields(recordKind, baseRecord.data);
38467
38467
  const base = partitionDocumentFields(recordKind, baseRecord.data);
38468
- const invalidatesDerivedConstructor = recordKind === "constructor" && !schemaDocumentAuthoredSemanticallyEqual(
38468
+ const derived = Object.entries(base.derived);
38469
+ const authoredEdited = derived.length > 0 && !schemaDocumentAuthoredSemanticallyEqual(
38469
38470
  recordKind,
38470
38471
  baseRecord.data,
38471
38472
  normalizedAuthored
38472
38473
  );
38473
- if (!invalidatesDerivedConstructor) Object.assign(data, base.derived);
38474
+ for (const [field, value] of derived) {
38475
+ if (authoredEdited && compiledFromAuthoredBody(recordKind, field)) {
38476
+ continue;
38477
+ }
38478
+ data[field] = value;
38479
+ }
38474
38480
  Object.assign(data, base.volatile);
38475
38481
  }
38476
38482
  return { recordKind, recordId, data };
38477
38483
  }
38484
+ function compiledFromAuthoredBody(recordKind, field) {
38485
+ if (recordKind === "constructor") return true;
38486
+ return recordKind === "member" && field !== "valueId";
38487
+ }
38478
38488
  function assertOnlyAuthoredFields(recordKind, fields) {
38479
38489
  for (const field of Object.keys(fields)) {
38480
38490
  const disposition = fieldDisposition(recordKind, field);
@@ -50022,6 +50032,14 @@ var init_world_system_classes_generated = __esm({
50022
50032
  docsText: "Sort this group against the scene root, ignoring any enclosing sorting group. Maps to SortingGroup.sortAtRoot.",
50023
50033
  requirement: 1 /* Required */,
50024
50034
  schemaKey: "SortAtRoot"
50035
+ },
50036
+ {
50037
+ memberId: "system_c5d39776-03d6-4673-84bb-16ba81ae9e0a",
50038
+ memberKind: "vector2",
50039
+ defaultValue: { x: 0.5, y: 0 },
50040
+ docsText: "Where this group sorts along the camera's transparency sort axis, as an offset from the object's origin-cell corner in cells, like NeoCollider.Offset. It is the group's pivot: put it where the art meets the ground. Moving it never moves the art.",
50041
+ requirement: 1 /* Required */,
50042
+ schemaKey: "SortPoint"
50025
50043
  }
50026
50044
  ],
50027
50045
  worldKind: "sortingGroup"
@@ -50083,7 +50101,7 @@ var init_world_system_classes_generated = __esm({
50083
50101
  memberId: "system_6f32f1f2-83ea-42fc-8647-34ed0946b7f1",
50084
50102
  memberKind: "int",
50085
50103
  defaultValue: null,
50086
- docsText: "Nudges this sprite's draw order within its object. Added to the order derived from the object's layer group \u2014 it does not replace it. Leave unset for the default order.",
50104
+ docsText: "Added to the object layer's sorting order; it does not replace it. The result competes with every renderer in the sorting layer, or in the enclosing sorting group, and among equal orders the renderer lower on screen draws in front. Leave unset to use the layer's order.",
50087
50105
  minValue: -32768,
50088
50106
  maxValue: 32767,
50089
50107
  schemaKey: "SortingOrder"
@@ -86205,8 +86223,8 @@ __export(project_document_read_exports, {
86205
86223
  readArrayField: () => readArrayField,
86206
86224
  readProjectDocument: () => readProjectDocument,
86207
86225
  readProjectDocumentContentHashHeads: () => readProjectDocumentContentHashHeads,
86208
- readProjectDocumentManifestPage: () => readProjectDocumentManifestPage,
86209
86226
  readProjectDocumentManifestPageRecords: () => readProjectDocumentManifestPageRecords,
86227
+ readProjectDocumentPage: () => readProjectDocumentPage,
86210
86228
  readProjectDocumentRevisionMarker: () => readProjectDocumentRevisionMarker,
86211
86229
  withStoredValueBase: () => withStoredValueBase
86212
86230
  });
@@ -86224,7 +86242,7 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86224
86242
  "Chunked project document read received an empty partition key."
86225
86243
  );
86226
86244
  }
86227
- const firstPageResult = await runner.fetchManifestPage(
86245
+ const firstPageResult = await runner.fetchPage(
86228
86246
  {
86229
86247
  numItems: DOCUMENT_MANIFEST_PAGE_SIZE,
86230
86248
  cursor: null
@@ -86232,7 +86250,7 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86232
86250
  firstMapKey
86233
86251
  );
86234
86252
  if (firstPageResult === null) return null;
86235
- const firstPage = readProjectDocumentManifestPage(firstPageResult);
86253
+ const firstPage = readProjectDocumentPage(firstPageResult);
86236
86254
  if (firstPage.metadata === null) {
86237
86255
  throw new Error(
86238
86256
  "Convex project document manifest first page is missing version metadata."
@@ -86244,18 +86262,20 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86244
86262
  const snapshots = createProjectDocumentSnapshotLoader(
86245
86263
  (ids) => fetchScopedSnapshotBatch(runner, ids, readBase)
86246
86264
  );
86247
- const rememberRecords = async (records2) => {
86265
+ const rememberRecords = async (page) => {
86266
+ if (page.snapshots !== void 0) snapshots.seed(page.snapshots);
86267
+ const records2 = page.records;
86248
86268
  for (const record6 of records2) {
86249
86269
  recordsByKey.set(`${record6.recordKind} ${record6.recordId}`, record6);
86250
86270
  }
86251
86271
  await snapshots.enqueue(records2.map((record6) => record6.snapshotId));
86252
86272
  };
86253
86273
  try {
86254
- await rememberRecords(firstPage.records);
86274
+ await rememberRecords(firstPage);
86255
86275
  const fetchRemainingPages = async (mapKey, initialCursor) => {
86256
86276
  let cursor = initialCursor;
86257
86277
  while (cursor !== null) {
86258
- const pageResult = await runner.fetchManifestPage(
86278
+ const pageResult = await runner.fetchPage(
86259
86279
  {
86260
86280
  numItems: DOCUMENT_MANIFEST_PAGE_SIZE,
86261
86281
  cursor
@@ -86268,15 +86288,15 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86268
86288
  "Convex project document manifest returned null for a continuation page."
86269
86289
  );
86270
86290
  }
86271
- const page = readProjectDocumentManifestPage(pageResult);
86291
+ const page = readProjectDocumentPage(pageResult);
86272
86292
  assertProjectReadBase(readBase, page.readBase);
86273
- await rememberRecords(page.records);
86293
+ await rememberRecords(page);
86274
86294
  cursor = page.continueCursor;
86275
86295
  }
86276
86296
  };
86277
86297
  await fetchRemainingPages(firstMapKey, firstPage.continueCursor);
86278
86298
  for (const mapKey of mapKeys.slice(1)) {
86279
- const pageResult = await runner.fetchManifestPage(
86299
+ const pageResult = await runner.fetchPage(
86280
86300
  { numItems: DOCUMENT_MANIFEST_PAGE_SIZE, cursor: null },
86281
86301
  mapKey,
86282
86302
  readBase,
@@ -86287,9 +86307,9 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86287
86307
  `Convex project document manifest returned null for partition "${mapKey ?? "all"}".`
86288
86308
  );
86289
86309
  }
86290
- const page = readProjectDocumentManifestPage(pageResult);
86310
+ const page = readProjectDocumentPage(pageResult);
86291
86311
  assertProjectReadBase(readBase, page.readBase);
86292
- await rememberRecords(page.records);
86312
+ await rememberRecords(page);
86293
86313
  await fetchRemainingPages(mapKey, page.continueCursor);
86294
86314
  }
86295
86315
  const snapshotsById = await snapshots.finish();
@@ -86320,7 +86340,8 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86320
86340
  }
86321
86341
  }
86322
86342
  const records2 = [...recordsByKey.values()];
86323
- await runner.validateReadBase(readBase);
86343
+ if (options.revisionValidation !== "live-subscription")
86344
+ await runner.validateReadBase(readBase);
86324
86345
  return assembleProjectDocumentRaw({
86325
86346
  version: metadata.version,
86326
86347
  versions: metadata.versions,
@@ -86352,7 +86373,7 @@ async function fetchProjectDocumentPartitionRecords(runner, mapKey, readBase) {
86352
86373
  try {
86353
86374
  let cursor = null;
86354
86375
  do {
86355
- const pageResult = await runner.fetchManifestPage(
86376
+ const pageResult = await runner.fetchPage(
86356
86377
  { numItems: DOCUMENT_MANIFEST_PAGE_SIZE, cursor },
86357
86378
  mapKey,
86358
86379
  readBase,
@@ -86363,11 +86384,12 @@ async function fetchProjectDocumentPartitionRecords(runner, mapKey, readBase) {
86363
86384
  `Convex project document manifest returned null for partition "${mapKey}".`
86364
86385
  );
86365
86386
  }
86366
- const page = readProjectDocumentManifestPage(pageResult);
86387
+ const page = readProjectDocumentPage(pageResult);
86367
86388
  assertProjectReadBase(readBase, page.readBase);
86368
86389
  for (const record6 of page.records) {
86369
86390
  recordsByKey.set(`${record6.recordKind} ${record6.recordId}`, record6);
86370
86391
  }
86392
+ if (page.snapshots !== void 0) snapshots.seed(page.snapshots);
86371
86393
  await snapshots.enqueue(page.records.map((record6) => record6.snapshotId));
86372
86394
  cursor = page.continueCursor;
86373
86395
  } while (cursor !== null);
@@ -86393,42 +86415,30 @@ async function fetchProjectDocumentPartitionRecords(runner, mapKey, readBase) {
86393
86415
  }
86394
86416
  }
86395
86417
  async function fetchProjectDocumentValueRecords(runner, valueIds, readBase) {
86396
- if (runner.fetchValueHeadsPage === void 0) {
86418
+ if (runner.fetchValueSnapshotsPage === void 0) {
86397
86419
  throw new Error(
86398
- "Partition-scoped document reads require a value-head reader for member defaults."
86420
+ "Partition-scoped document reads require a value-snapshot reader for member defaults."
86399
86421
  );
86400
86422
  }
86401
86423
  const values = /* @__PURE__ */ new Map();
86402
86424
  let cursor = null;
86403
86425
  do {
86404
- const page = await runner.fetchValueHeadsPage(valueIds, cursor, readBase);
86405
- if (!isObject4(page)) throw new Error("Value-head page must be an object.");
86406
- const revision = readProjectDocumentRevisionMarker({
86407
- documentRevision: page.documentRevision
86408
- });
86409
- if (revision === null)
86410
- throw new Error("Value-head page has no document revision.");
86411
- assertProjectReadBase(readBase, revision.readBase);
86412
- if (!Array.isArray(page.snapshotIds) || !page.snapshotIds.every((id2) => typeof id2 === "string"))
86413
- throw new Error(
86414
- "Value-head page snapshotIds must be an array of strings."
86415
- );
86416
- if (page.continueCursor !== null && typeof page.continueCursor !== "string")
86417
- throw new Error(
86418
- "Value-head page continueCursor must be a string or null."
86419
- );
86420
- const loaded = await fetchProjectDocumentSnapshots(
86421
- (ids) => fetchScopedSnapshotBatch(runner, ids, readBase),
86422
- page.snapshotIds
86423
- );
86424
- for (const snapshot of loaded.values()) {
86426
+ const page = readProjectDocumentPage(
86427
+ await runner.fetchValueSnapshotsPage(valueIds, cursor, readBase)
86428
+ );
86429
+ assertProjectReadBase(readBase, page.readBase);
86430
+ if (page.snapshots === void 0)
86431
+ throw new Error("Value snapshot page is missing snapshots.");
86432
+ if (page.continueCursor !== null && page.continueCursor === cursor)
86433
+ throw new Error("Value snapshot page cursor made no progress.");
86434
+ for (const snapshot of page.snapshots) {
86425
86435
  if (snapshot.recordKind !== "value")
86426
- throw new Error("Value-head page returned a non-value snapshot.");
86436
+ throw new Error("Value snapshot page returned a non-value snapshot.");
86427
86437
  if (snapshot.deleted)
86428
- throw new Error("Value-head page returned a deleted snapshot.");
86438
+ throw new Error("Value snapshot page returned a deleted snapshot.");
86429
86439
  if (!valueIds.includes(snapshot.recordId))
86430
86440
  throw new Error(
86431
- `Value-head page returned unrequested value "${snapshot.recordId}".`
86441
+ `Value snapshot page returned unrequested value "${snapshot.recordId}".`
86432
86442
  );
86433
86443
  values.set(snapshot.recordId, snapshot);
86434
86444
  }
@@ -86446,10 +86456,23 @@ async function fetchScopedSnapshotBatch(runner, ids, readBase) {
86446
86456
  assertProjectReadBase(readBase, readProjectReadBase(result.readBase));
86447
86457
  return result;
86448
86458
  }
86449
- function readProjectDocumentManifestPage(value) {
86459
+ function readProjectDocumentPage(value) {
86450
86460
  if (!isObject4(value)) {
86451
86461
  throw new Error("Convex project document manifest page must be an object.");
86452
86462
  }
86463
+ let snapshots;
86464
+ if (value.snapshots !== void 0) {
86465
+ if (!Array.isArray(value.snapshots))
86466
+ throw new Error("Snapshot page snapshots must be an array.");
86467
+ const ids = value.snapshots.map((snapshot) => {
86468
+ if (!isObject4(snapshot))
86469
+ throw new Error("Snapshot page entry must be an object.");
86470
+ if (typeof snapshot.id !== "string")
86471
+ throw new Error("Snapshot page entry is missing an id.");
86472
+ return snapshot.id;
86473
+ });
86474
+ snapshots = ids.length === 0 ? [] : readProjectDocumentSnapshotBatch(value, ids);
86475
+ }
86453
86476
  const continueCursor = value.continueCursor;
86454
86477
  if (continueCursor !== null && typeof continueCursor !== "string") {
86455
86478
  throw new Error(
@@ -86472,7 +86495,12 @@ function readProjectDocumentManifestPage(value) {
86472
86495
  releaseChannels: metadata.releaseChannels,
86473
86496
  documentRevision: metadata.documentRevision
86474
86497
  },
86475
- records: readProjectDocumentManifestRecords(value.records),
86498
+ ...snapshots === void 0 ? {} : { snapshots },
86499
+ records: snapshots === void 0 ? readProjectDocumentManifestRecords(value.records) : snapshots.map((snapshot) => ({
86500
+ recordKind: snapshot.recordKind,
86501
+ recordId: snapshot.recordId,
86502
+ snapshotId: snapshot.id
86503
+ })),
86476
86504
  continueCursor
86477
86505
  };
86478
86506
  }
@@ -86520,7 +86548,7 @@ function readProjectDocumentManifestPageRecords(value) {
86520
86548
  if (!isObject4(value)) {
86521
86549
  throw new Error("Convex project document manifest page must be an object.");
86522
86550
  }
86523
- return readProjectDocumentManifestRecords(value.records);
86551
+ return readProjectDocumentPage(value).records;
86524
86552
  }
86525
86553
  async function fetchProjectDocumentSnapshots(fetchBatch, snapshotIds) {
86526
86554
  const loader = createProjectDocumentSnapshotLoader(fetchBatch);
@@ -86566,6 +86594,12 @@ function createProjectDocumentSnapshotLoader(fetchBatch) {
86566
86594
  active.add(work);
86567
86595
  };
86568
86596
  return {
86597
+ seed(snapshots) {
86598
+ for (const snapshot of snapshots) {
86599
+ seen.add(snapshot.id);
86600
+ snapshotsById.set(snapshot.id, snapshot);
86601
+ }
86602
+ },
86569
86603
  async enqueue(snapshotIds) {
86570
86604
  checkFailure();
86571
86605
  for (const id2 of snapshotIds) {
@@ -116045,36 +116079,59 @@ function prospectiveInitializerRecordsV4(args) {
116045
116079
  }
116046
116080
  for (const seed of args.authoredValueSeeds) {
116047
116081
  if (seed.valueId === void 0) continue;
116048
- records2.set(`value:${seed.valueId}`, {
116049
- recordKind: "value",
116050
- recordId: seed.valueId,
116051
- contentHash: "local-seed",
116052
- deleted: false,
116053
- data: {
116054
- id: seed.valueId,
116082
+ setSeedValueRecord(records2, {
116083
+ id: seed.valueId,
116084
+ projectId: args.workspace.config.projectId,
116085
+ ...encodeStoredValueNode(seed),
116086
+ createdAt: 0,
116087
+ updatedAt: 0
116088
+ });
116089
+ for (const row of seed.values ?? []) {
116090
+ setSeedValueRecord(records2, {
116091
+ id: row.id,
116055
116092
  projectId: args.workspace.config.projectId,
116056
- ...encodeStoredValueNode(seed),
116093
+ ...encodeStoredValueRowFields(row),
116057
116094
  createdAt: 0,
116058
116095
  updatedAt: 0
116059
- }
116060
- });
116061
- for (const row of seed.values ?? []) {
116062
- records2.set(`value:${row.id}`, {
116096
+ });
116097
+ }
116098
+ }
116099
+ return records2;
116100
+ }
116101
+ function setSeedValueRecord(records2, data) {
116102
+ const key = `value:${data.id}`;
116103
+ const physical = records2.get(key)?.data;
116104
+ if (isObjectRecord2(physical) && rowCarriesPackedValueContent(physical)) {
116105
+ const row = {
116106
+ projectId: data.projectId,
116107
+ createdAt: 0,
116108
+ updatedAt: 0,
116109
+ ...physical
116110
+ };
116111
+ if (!isMemberValue(row)) {
116112
+ throw new PackedValueEncodingError(
116113
+ `Seeded row "${key}" replaces a row that packs a child but is not a well-formed value row, so its packed children cannot be kept.`
116114
+ );
116115
+ }
116116
+ for (const child of decodePackedValueRow(row).children) {
116117
+ const childKey = `value:${child.id}`;
116118
+ if (records2.has(childKey)) continue;
116119
+ records2.set(childKey, {
116063
116120
  recordKind: "value",
116064
- recordId: row.id,
116121
+ recordId: child.id,
116065
116122
  contentHash: "local-seed",
116066
116123
  deleted: false,
116067
- data: {
116068
- id: row.id,
116069
- projectId: args.workspace.config.projectId,
116070
- ...encodeStoredValueRowFields(row),
116071
- createdAt: 0,
116072
- updatedAt: 0
116073
- }
116124
+ data: child
116074
116125
  });
116075
116126
  }
116076
116127
  }
116077
- return records2;
116128
+ records2.set(key, {
116129
+ recordKind: "value",
116130
+ recordId: data.id,
116131
+ contentHash: "local-seed",
116132
+ deleted: false,
116133
+ data
116134
+ });
116078
116135
  }
116079
116136
  function initializerCode(records2, valueId) {
116080
116137
  const row = records2.get(`value:${valueId}`)?.data;
@@ -133618,7 +133675,7 @@ function readRawSingleton(raw, key) {
133618
133675
  async function fetchProjectDocument(workspace) {
133619
133676
  const convex = await createConvexClient(workspace);
133620
133677
  const raw = await fetchProjectDocumentRawChunked2({
133621
- fetchManifestPage: (paginationOpts, mapKey, readBase) => convex.query(api2.projectDocuments.getDocumentManifestPage, {
133678
+ fetchPage: (paginationOpts, mapKey, readBase) => convex.query(api2.projectDocuments.getDocumentSnapshotPage, {
133622
133679
  projectId: workspace.config.projectId,
133623
133680
  versionId: workspace.config.versionId,
133624
133681
  paginationOpts,
@@ -138787,7 +138844,7 @@ var init_registry2 = __esm({
138787
138844
  "schema-contract/registry.mjs"() {
138788
138845
  "use strict";
138789
138846
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
138790
- cliVersion: "0.53.5",
138847
+ cliVersion: "0.54.0",
138791
138848
  projectFileUploadBatchSize: 32,
138792
138849
  documentRecords: {
138793
138850
  member: {
@@ -146608,7 +146665,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
146608
146665
  async function main() {
146609
146666
  const args = parseArgs(process.argv.slice(2));
146610
146667
  if (args.command === "--version") {
146611
- console.log("0.53.5");
146668
+ console.log("0.54.0");
146612
146669
  return;
146613
146670
  }
146614
146671
  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.53.5",
3
+ "version": "0.54.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.53.5 -->
12
+ <!-- reviewed-through-cli: 0.54.0 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -209,11 +209,16 @@ class Tree : NeoObject {
209
209
  ```
210
210
 
211
211
  Presence of a non-null group makes the object and descendants sort as one unit.
212
- `SortAtRoot` hoists a nested group against the scene root.
213
-
214
- `NeoSpriteObject` exposes `FlipX`, `FlipY`, `MaskInteraction`, and optional
215
- `SortingOrder`. Treat `SortingOrder` as an offset added to the order derived
216
- from layer, placement, and composition; it is not an absolute replacement.
212
+ `SortAtRoot` hoists a nested group against the scene root. `SortPoint` is the
213
+ point the group Y-sorts at, in cells from the object's origin-cell corner
214
+ (default `(0.5, 0)`, the origin cell's bottom center). Moving it never moves
215
+ art. Set it per class, variant, or animation frame, like any other value.
216
+
217
+ Objects sort by sorting layer, then order in layer, then sort-point Y (lower
218
+ draws in front), then document order. `NeoSpriteObject` exposes `FlipX`,
219
+ `FlipY`, `MaskInteraction`, and optional `SortingOrder`. Treat `SortingOrder`
220
+ as an offset added to the layer's sorting order; it is not an absolute
221
+ replacement.
217
222
 
218
223
  Use `NeoObjectBase.Enabled` to hide an object and its entire subtree without
219
224
  removing its authored `Children` row. A disabled object is not rendered or
@@ -97,7 +97,7 @@ wrappers.
97
97
  The marker near the top of `SKILL.md` must exactly match the package version:
98
98
 
99
99
  ```html
100
- <!-- reviewed-through-cli: 0.53.5 -->
100
+ <!-- reviewed-through-cli: 0.54.0 -->
101
101
  ```
102
102
 
103
103
  The quoted version above is checked too, so this instruction cannot go stale