@neocompose/cli 0.75.2 → 0.76.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.75.2",
3
+ "version": "0.76.1",
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.75.2 -->
12
+ <!-- reviewed-through-cli: 0.76.1 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -211,8 +211,12 @@ copies, use the editor's duplicate action or copy the initializer without IDs.
211
211
  Scalars may have several owners. If those values diverge in web, pull keeps
212
212
  the different server values by inlining the differing uses. Initializer
213
213
  aliases are local source organization, not runtime globals or server records.
214
- Use their names as complete value initializers; executable expressions such as
215
- `BASE + 1` cannot access these local declarations.
214
+ Complete member and collection entries compose owned values, including inside
215
+ constructor-backed initializers. A class local used as a constructor argument or
216
+ executable operand refers to its single stored instance. Nested targets need an
217
+ existing `@id` or an authored UUID-v4; a direct static owner supports pending IDs.
218
+ Scalar executable expressions such as `BASE + 1` still require inlining the
219
+ scalar expression. Locals are unavailable inside executable closures.
216
220
  The shared editor offers extract-to-file on a selected complete initializer,
217
221
  inline on its sole use, and duplicate at a use. Move the created file freely.
218
222
 
@@ -66,8 +66,9 @@ new GlassFloorTile { Cell = new(9, -4) }
66
66
 
67
67
  Layer-link `Tiles` lists contain these values directly. `NeoTileInstance` and
68
68
  `@tile(...)` are removed. A tile's `Name`, `Sprite`, and `SmartTile` are
69
- `readonly` declaration members. Override them on a subclass; a placement
70
- initializer cannot assign them. `Cell` is the placement's writable field.
69
+ class-level `readonly` members: tiles render per class, so override them on a
70
+ subclass. Push rejects a placement that sets them. `Cell` is the placement's
71
+ writable field.
71
72
 
72
73
  Object `PlacementTiles` is a `List<NeoPlacementTile>`. Each entry marks a
73
74
  footprint cell with its own `Cell` member. It is independent of `NeoTile`. Rendered
@@ -129,7 +129,7 @@ wrappers.
129
129
  The marker near the top of `SKILL.md` must exactly match the package version:
130
130
 
131
131
  ```html
132
- <!-- reviewed-through-cli: 0.75.2 -->
132
+ <!-- reviewed-through-cli: 0.76.1 -->
133
133
  ```
134
134
 
135
135
  The quoted version above is checked too, so this instruction cannot go stale
@@ -166,6 +166,30 @@ a User class reads only `root.User`. Other code reads `root.User` but can't
166
166
  assign, mutate, construct, or invoke User data. Lookups and listeners don't
167
167
  cross between User and Save data; Session lookups may target `root.User`.
168
168
 
169
+ `readonly` is init-only, as in C#, and independent of storage. A field
170
+ initializer, a constructor body, an object initializer block, or an authored
171
+ value sets it; nothing writes it after construction. It is valid on Save,
172
+ Session, Immutable, and User classes, but not with Writable storage. It may
173
+ read a header parameter:
174
+
175
+ ```neo
176
+ class MixerGroup(string name) {
177
+ public readonly string Name = name;
178
+ public readonly List<MixerChannel> Children = [];
179
+ public float Volume = 1;
180
+ }
181
+ ```
182
+
183
+ Readonly is shallow. A readonly collection's membership is fixed, while its
184
+ entries' members follow their own storage and mutability. User class members
185
+ still declare no storage.
186
+
187
+ A readonly member declared as a literal (no initializer or constructor call)
188
+ that reads neither `this` nor a constructor argument and holds nothing
189
+ writable is a constant. Instances share its declared value and store nothing
190
+ until a constructor or initializer block writes it. A readonly collection or
191
+ initializer is still built per instance.
192
+
169
193
  `@index(member: Field, kind: .Unique)` and
170
194
  `@column(member: Field, width: ..., visibility: ..., pin: ..., overflow: ...)`
171
195
  are repeatable List annotations. `member:` is a bare field name on the entry
@@ -369,8 +393,8 @@ Accept settlement only from:
369
393
 
370
394
  1. The member's own initializer expression.
371
395
  2. Assignment on every reachable path in `init` or a constructor body.
372
- 3. A call-site initializer entry when using an otherwise valid construction
373
- path.
396
+ 3. A call-site initializer entry, in value source and NeoScript bodies alike,
397
+ when using an otherwise valid construction path.
374
398
 
375
399
  Do not infer settlement from a side effect inside another function. Require
376
400
  full `if`/`else` coverage when an assignment is conditional.
@@ -434,7 +458,22 @@ class PlacedItem<
434
458
  }
435
459
  ```
436
460
 
437
- Executable object initializer blocks can set immutable stored members on the newly constructed instance, including nested `new` expressions. Ordinary assignments after construction remain subject to storage restrictions. `readonly` declarations still change only through class defaults.
461
+ Object initializer blocks can set `readonly` members of the newly constructed
462
+ instance, including nested `new` expressions. Ordinary assignments after
463
+ construction remain subject to storage and `readonly` restrictions.
464
+
465
+ Runtime construction never supplies an Immutable member: an initializer block,
466
+ constructor body, base-clause argument, or generated C# factory cannot set one.
467
+ The value is never stored with the instance. When the declaration, including
468
+ every row it owns, reads neither `this` nor a constructor argument, and no
469
+ value of its type holds a writable member, the member is a constant and every
470
+ instance shares one value. Otherwise each instance evaluates the declared
471
+ initializer with its own `this` and constructor arguments, again after a
472
+ reload. A method group such as `selector: SelectTorso` binds `this`.
473
+ `Clone()` shares an authored Immutable value or a constant and re-evaluates a
474
+ per-instance one. A class with a required Immutable member and
475
+ no initializer cannot be constructed at runtime; only authored values construct
476
+ it.
438
477
 
439
478
  Populated unordered member defaults, such as `@settings(kind: .Unordered)` with
440
479
  `List<Entry> Entries = [new Entry()]`, are constructed by an initializer for
@@ -154,9 +154,10 @@ Object initializers are available in method bodies and `.spec.neo` tests:
154
154
  var tile = new DirtWetTile { Cell = Vector2Int(3, -2) };
155
155
  ```
156
156
 
157
- Construction runs first. Each initializer entry then uses ordinary assignment
158
- rules in source order, including property setters, access checks, and readonly
159
- restrictions. Initializer expressions read the caller's scope.
157
+ An entry that settles a required member is evaluated with construction, as the
158
+ constructor argument it stands in for. The remaining entries then use ordinary
159
+ assignment rules in source order, including property setters, access checks,
160
+ and readonly restrictions. Initializer expressions read the caller's scope.
160
161
 
161
162
  ## Nullability and ownership
162
163
 
@@ -90,6 +90,45 @@ formatting moves its ID above the declaration. Do not put an ID in both places.
90
90
  A local initializer such as `Delivery DeliveryRow = new()` retains `Delivery`
91
91
  when consumed by a base-class member. Aliases retain that same concrete type.
92
92
 
93
+ Complete local member and collection initializers also work inside constructions
94
+ that execute a constructor. Each local composes its owned subtree and preserves
95
+ its concrete class:
96
+
97
+ ```neo
98
+ AudioFolder SFX_FOLDER = new("Sound effects") {
99
+ Children = [UI_VOLUME, GAMEPLAY_VOLUME]
100
+ };
101
+ AudioSettings AUDIO_SETTINGS = new() { Main = SFX_FOLDER };
102
+ ```
103
+
104
+ In constructor arguments and executable operands, a class local names its one
105
+ stored instance instead of constructing a copy:
106
+
107
+ ```neo
108
+ @id("86ac7c7d-8d3b-4642-9def-cdf32643bdc5")
109
+ DeliveryQuest HARD_AS_STONE = new();
110
+ // The core quest list owns HARD_AS_STONE.
111
+ WorkflowStep UNLOCK = new QuestUnlockStep(HARD_AS_STONE);
112
+ ```
113
+
114
+ The referenced local must have exactly one owning placement. A nested target
115
+ needs its existing `@id`. A structurally owned list entry can also use an unused
116
+ UUID-v4 in the same push. For a local nested inside an evaluated constructor,
117
+ leave it idless on the first push; push assigns its actual identity before it can
118
+ be referenced. Do not assign a fresh UUID to that nested construction. A direct static owning member can supply an idless local's pending identity.
119
+ Aliases of that static local work too. A member default is a construction template,
120
+ so its local does not identify a single stored instance. Unowned locals, ambiguous
121
+ placements, and cycles are errors at source. Constructor argument type checking
122
+ still applies. Canonical construction arguments may use `Reference<Type>(id: "…")`.
123
+
124
+ Owned locals inside evaluated initializers need directly addressable fields, list
125
+ entries, or literal dictionary keys; inline the construction when a computed key
126
+ or other executable expression prevents tracking its owning path.
127
+
128
+ Scalar locals remain complete-initializer bindings; inline their expressions when
129
+ using them in arithmetic or constructor arguments. Closure bodies cannot capture
130
+ project authoring locals.
131
+
93
132
  Nested class/list rows remain independently addressable and keep inline IDs:
94
133
 
95
134
  ```neo