@onlyworlds/sdk 4.8.0 → 4.9.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,434 +1,459 @@
1
- # Changelog
2
-
3
- All notable changes to `@onlyworlds/sdk`. Maintained from 3.1.0 onward;
4
- earlier history lives in git log only.
5
-
6
- ## [Unreleased]
7
-
8
- ## [4.8.0] — 2026-10-10
9
-
10
- ### Added
11
- - **`removeImage(keyOrUrl, { ticket? })`**: removes one uploaded image. A removal ticket from
12
- keel (`POST /media/remove-ticket`, keel D91), then the removal at the ticket's `remove_url`
13
- with the ticket only, never the API key. Takes the object key or the whole `image_url`. The
14
- image's uploader or the world's owner key may remove it; the bytes go back to the uploader's
15
- storage. Elements keep their `image_url`: clear it yourself.
16
- - **`createRemovalTicket(key)`** and the types **`OwRemovalTicket`** (`ticket`, `remove_url`,
17
- `exp` in unix seconds, `key`, `referenced`: how many elements still show the image) and
18
- **`OwRemovedImage`** (`removed`, `bytes`, `cache`). Refusals arrive as `OwApiError`: keel's
19
- `permission_error`, `not_found`, `removal_in_flight` and `upload_pending` (both 409 with
20
- Retry-After); the edge's `ticket_expired`, `ticket_used`, `ticket_invalid`, `not_removable`,
21
- `remove_failed`.
22
-
23
- ## [4.7.3] — 2026-10-10
24
-
25
- ### Changed
26
- - **Schema repinned `v0.31.0-dist.18` → `v0.31.0-dist.19`** (canonical unchanged, 00.31.00).
27
- Two generated values move: `ELEMENT_ICONS.zone` `architecture` → `pentagon`,
28
- `ELEMENT_ICONS.marker` `location_on` → `polyline` (Material Symbols names; Marker no longer
29
- shares the pin glyph). No field, type or `FIELD_SCHEMA` entry changed. The vendored tree grows
30
- from 31 to 69 files (dist.19 adds `schema.json` and an example world); 69/69 hashed against
31
- the pinned manifest.
32
- - **`AGENTS.md` re-read against the new pin** (its gate fired on the repin, as designed); its
33
- wire facts hold, its "Current as of" line moves.
34
-
35
- ## [4.7.2] — 2026-10-09
36
-
37
- ### Changed
38
- - **README first run** finds Admiral Fluffington by name (the demo world's default list order moves with every edit, so "the first three" could not be promised); the npm page shows the corrected output.
39
- - **Schema repinned `v0.30.2-dist.16` → `v0.31.0-dist.18`** (canonical **00.31.00**).
40
- `ONLYWORLDS_VERSION` is now `'00.31.00'`. The World gains three optional free-text settings,
41
- `length_unit`, `mass_unit` and `distance_unit`; the nine element fields that promised "world
42
- LENGTH / MASS / DISTANCE units" now name them in their descriptions (Ability `range`, Character
43
- and Creature `height` and `weight`, Creature `speed`, Location `elevation`, Object and Species
44
- `weight`). No element field shape and no `FIELD_SCHEMA` entry changed. The world body is not
45
- typed per field (`OwWorldMeta` is open), so `getWorld` returns the three keys and `patchWorld`
46
- sends them with no code change; a test says so.
47
-
48
- ## [4.7.1] — 2026-10-08
49
-
50
- ### Changed
51
- - **README rewritten**: install, a first run against the public demo world (its output shown,
52
- run as written), your own world with an agent seat for writes, then the reference. It no
53
- longer calls a `world` field in a payload an error: the API ignores it.
54
- - Comments, the RFC and pin-file prose no longer name people or internal places.
55
- - **Schema repinned `v0.30.1-dist.15` → `v0.30.2-dist.16`** (canonical **00.30.02**).
56
- `ONLYWORLDS_VERSION` is now `'00.30.02'`. Field descriptions move with the schema: Location's
57
- `rivalries` and `partnerships`, Marker's `order`, and Pin's `element` pair. No field shape and
58
- no `FIELD_SCHEMA` entry changed: canonical now requires only `name` on every type, which is
59
- what `FIELD_SCHEMA` already said, so its hand-made exception for the old Pin and Marker
60
- lists is gone. If the schema lists more than `name` again, codegen stops and asks for a
61
- ruling instead of dropping the list. 31/31 file hashes recomputed from the tag.
62
-
63
- ## [4.7.0] — 2026-10-08
64
-
65
- ### Added
66
- - **`OwApiError.isResyncRequired`**: 409 `resync_required` from `/changes`. Guests can follow the
67
- feed since keel D79; when a guest's view or a key's role changes, its stored cursor is refused. Drop
68
- it, walk from zero, and replace the local copy rather than merging.
69
-
70
- ### Documented
71
- - `uploadImage` accepts webp, png, jpeg and avif. 4.6.0's JSDoc and README also listed gif, which
72
- the edge refuses (415 `unsupported_type`); checked against the Worker's type table.
73
- - `changes()`: the old rewind rule compared a stored cursor with `head`, which an opaque cursor
74
- cannot do; replaced with the reset rule (`isResyncRequired`).
75
- - The doc comments of `OwClientConfig`, `create` and `uploadImage` are written for users: internal
76
- ruling numbers and test-harness notes are gone, and each opens with what the option or method does.
77
- The docs site's TypeScript reference is generated from these comments.
78
-
79
- ## [4.6.0] — 2026-10-06
80
-
81
- Published from `e198298` (tag `v4.6.0`); the tarball was checked against a build of that commit.
82
-
83
- Includes 4.5.1's documentation fixes: 4.5.1 was staged and never published.
84
-
85
- ### Added
86
- - **`uploadImage(image, { key?, ticket? })`**: upload one image (webp, png, jpeg, avif;
87
- never SVG; 4.6.0's docs also said gif, which the edge refuses with 415) and get its permanent public URL. Two requests: a ticket from keel
88
- (`POST /media/ticket`), then the bytes straight to the edge at the ticket's `upload_url`
89
- (keel never sees them). Only the ticket goes to the edge, never the API key or PIN. An image
90
- larger than the ticket's `max_bytes` throws 413 `too_large` before anything is sent; the edge's
91
- refusals (`ticket_used`, `ticket_expired`, `bad_key`, `exists`, `unsupported_type`, ...) arrive
92
- as `OwApiError` with their code. Set the returned `url` as an element's `image_url` yourself.
93
- Checked live on 2026-10-06: one upload through keel's ticket to the edge (201), and the same
94
- ticket again refused with 401 `ticket_used`.
95
- - **`createMediaTicket()`**: the first step alone, for callers that upload themselves (progress
96
- bars, say). Types `OwMediaTicket` and `OwUploadedImage`.
97
-
98
- ### Documented
99
- - `ListParams.filter` lists what keel accepts now: exact `name`, and `characters=<id>` on every
100
- type with a `characters` link (keel D76d).
101
-
102
- ## [4.5.1] — 2026-10-05 (never published; shipped in 4.6.0)
103
-
104
- Docs only: no code path changes.
105
-
106
- ### Documented
107
- - **`isOwnerOnly`'s JSDoc in 4.5.0 was wrong about who sees `owner_only`.** It said owners and
108
- co-builders never see it. A member key, a co-builder's too, gets it on `PATCH /world`; only the
109
- owner's key is exempt (keel spec section 9; confirmed from keel's `patch_world`,
110
- 2026-10-04).
111
- - **`world_gone` is reserved.** `OwAuthErrorCode` and `isAuthError` still carry it, but keel never
112
- emits it: a deleted world's keys are deleted with it, so the key answers 401 `invalid_credentials`
113
- (2026-10-04; keel spec section 2). Found by the wire gate reading the SDK's own
114
- built file for the codes it keys on.
115
-
116
- ## [4.5.0] — 2026-10-04
117
-
118
- What the staging write probes found on the wire (Python package, 2026-10-04): one new getter, and
119
- three behaviours the server has always had that no client doc said. Additive; nothing is removed.
120
-
121
- ### Added
122
- - **`OwApiError.isOwnerOnly`**: 403 `owner_only`, a contributor attempting a world-level change
123
- (`patchWorld`, publishing a show page). Owners and co-builders never see it. keel's OpenAPI
124
- document does not name the code yet (the schema side has that to close); the wire returns it.
125
-
126
- ### Documented
127
- - **An atomic `/bulk` rollback still returns 201 slots** for the items that would have
128
- succeeded, with the ids and timestamps they would have had (keel spec: "counterfactual 201s").
129
- After `atomic: true` with `errors: true`, nothing was written. On `bulk()`, `OwBulkItemResult`
130
- and the README.
131
- - **`name` is required as a key only**: `''` and `null` are accepted and stored as `''` (keel
132
- ruling 2026-09-28, spec §6; nameless Markers exist). On `create()` and the README.
133
-
134
- ## [4.4.0] — 2026-10-04
135
-
136
- Membership, as far as the server has shipped it: who created an element, and the refusal a
137
- contributor meets on someone else's. Additive; nothing is removed or narrows.
138
-
139
- ### Added
140
- - **`created_by`** on `OwElementBase` (so on every element type): `string | null`, the id of the
141
- membership that created the element, `null` for the world's owner and for anything made
142
- before memberships existed (keel D72 phase 2, live 2026-09-28; keel spec §4). Read-only. The
143
- schema does not carry it (like `created_at` it is server bookkeeping), so the generator adds
144
- it to the wire base under the declared mapping. A write body that carries it has it dropped
145
- by the server, so the client does not strip it and a read body still round-trips.
146
- - **`OwApiError.isNotAuthor`**: 403 `not_author`, a contributor changing, replacing, relinking
147
- or deleting an element someone else created. `/bulk` answers 200 and reports it in the item
148
- slot, so check the slot (documented on `OwBulkItemResult`).
149
-
150
- ### Fixed
151
- - **`FIELD_SCHEMA` no longer marks `marker.map/zone/x/y/order` and `pin.map/element_type/element_id/x/y`
152
- as `required: true`.** The wire requires `name` and nothing else (rulings.yaml `nullable-by-default`,
153
- ruling 2026-07-28; keel's OpenAPI write schemas say `required: [name]`), but the schema files still
154
- list those fields and the generator copied the lists, so a form built from the table demanded
155
- coordinates the server never did. Found by comparing the SDK with keel's OpenAPI document (the
156
- new `verify-sdk-wire` gate). **If you read `required` from this table, marker and pin
157
- fields now read as optional**, which is what the server accepts. The generator notes the lists it
158
- ignores on every run and says when canonical stops carrying them.
159
-
160
- ### Not yet
161
- - `me()` and `members()` wait for the server routes (keel's M2b). Nothing in this release
162
- guesses at their names.
163
-
164
- ## [4.3.0] — 2026-09-28
165
-
166
- Errors you can act on: the two 409s told apart, and the busy server's `Retry-After`, in Node and
167
- browsers. One getter narrows (see Fixed); nothing is removed.
168
-
169
- ### Fixed
170
- - **`OwApiError.isIdempotencyConflict` no longer claims every 409.** keel sends two: 409
171
- `idempotency_error` (a key reused with a different body) and 409 `id_conflict` (the id is
172
- taken: a create with an existing id since D39, or a PUT / bulk id held by another world since
173
- D70). The getter was true for both, so an id conflict read as a key problem, and retrying with
174
- a fresh key could never help. It now matches `idempotency_error` only, and the new
175
- **`isIdConflict`** names the other. A caller who relied on the old catch-all for `id_conflict`
176
- should switch to `isIdConflict`. A 409 with no code (a proxy, a non-JSON body) now reads
177
- false for both. `/bulk` is unaffected: it answers 200 and reports a per-item 409 in the slot.
178
-
179
- ### Added
180
- - **`OwApiError.retryAfter`** (seconds, or null) is parsed from `Retry-After`, as a delay or an
181
- HTTP date, and **`isBusy`** marks keel's 503 `server_busy` (admission control, keel D71: six
182
- requests at once per worker, up to 10 s of queueing, then 503 with `Retry-After: 5`). The client
183
- still never retries on its own; it gives callers what they need to back off. A malformed
184
- `Retry-After` is null, never "retry now". **Works in browsers too** since keel `4949406`
185
- (2026-09-28): every response exposes `Retry-After`, `Idempotent-Replay` and
186
- `X-OW-Schema-Version` over CORS (probed from an allowed origin), and keel reports that the
187
- admission gate's 503 now carries CORS headers, so a busy server reads as busy, not as
188
- `OwNetworkError` (keel's word; the 503 can't be triggered on demand to probe). Six tests; the
189
- four core ones watched failing on the previous `errors.ts`.
190
-
191
- ## [4.2.0] — 2026-09-28
192
-
193
- Ids, honest filter docs, and the schema repin. Nothing is removed; no call needs to change.
194
-
195
- ### Changed
196
- - **`create()` now mints an RFC 9562 UUIDv7** when the element has no id (was v4). The first
197
- 48 bits are the creation millisecond, so ids this client mints a millisecond or more apart
198
- sort by creation (while the clock does not step backwards), which gives databases better
199
- index locality. **This holds within one client only**: worlds also hold v4 ids and legacy
200
- v1-server ids (`06x…`, which also carry version nibble 7 but put seconds first), so never
201
- order elements by id. For creation order use `created_at`; `change_seq` is last-write
202
- order. This is a **default, not a
203
- requirement**: a v4 or v7 id a caller supplies is still accepted as-is, and
204
- every id already stored stays valid. Keel mints v7 server-side too (ruling,
205
- 2026-09-28). Pinned by six tests: version and variant, the big-endian timestamp above
206
- bit 32, fractional and pre-1970 clocks floored consistently, creation order across 50
207
- consecutive milliseconds, 1,000 distinct ids inside one millisecond, and the no-`crypto`
208
- fallback (asserting `Math.random` is actually used). Five injected defects (v4 nibble,
209
- 32-bit truncation, a dropped random fill, a dropped variant mask, an unfloored clock)
210
- each fail the suite on every run, on Node 20 and 22. An independent reviewer's decoder agreed on 20,000 random timestamps and the 48-bit
211
- edges, and keel's `uuid7()` agrees on timestamp, version and variant for a pinned clock.
212
- - **`elementColor()` on an unknown type now throws a `TypeError` that names the type.** It
213
- used to crash with `Cannot read properties of undefined (reading 'dark')`. Found while
214
- building a colour gate: atlas's own copy falls back to the `world` family for an
215
- unknown type, so swapping it for this export is not a pure re-export for that input, and the
216
- docstring now says so. Whether the package should fall back is left to its consumers.
217
- - The generated field-schema comment no longer calls `maximum:` an open question or counts
218
- its occurrences (the count was 41; since 00.30.01 it is 15). The question was ruled on
219
- 2026-07-29.
220
- - **`ListParams.filter` JSDoc names what the server actually accepts**: `name__icontains`,
221
- `supertype`, `subtype`. It used to list `__in`, `__gte`, `__lte` and `__isnull`, copied
222
- from keel's spec, which described them but never built them. Probed live 2026-09-28: the
223
- three accepted keys answer 200; every other one, and `?ordering=`, answers 422.
224
- - **Schema repinned `v0.30.1-dist.13` → `v0.30.1-dist.15`** (canonical unchanged, 00.30.01).
225
- Two generated values move: `ELEMENT_ICONS.institution` `business` → `account_balance`,
226
- `ELEMENT_ICONS.marker` `place` → `location_on` (both Material Symbols names). dist.15
227
- also adds `minimum: 0` to `ability.potency`; the walk does not surface bounds, so nothing
228
- generated changes for it. 31/31 file hashes recomputed from a fresh download.
229
- - **`AGENTS.md` re-read against the new pin** (its gate fired on the repin, as designed).
230
- It still called the v1 client "frozen legacy" in this package; v1 was removed at 4.0.0.
231
- It now also names the wire behaviours keel deployed on 2026-09-28 (keel D70): the 64 KB
232
- extension cap, the three built filters and the `?ordering=` 422, 409 `id_conflict` for a
233
- PUT or bulk id that belongs to another world, and the 422 for unpaired surrogates. None of
234
- them needs client code.
235
-
236
- ### Also in this release (staged earlier)
237
- - **`SCHEMA.md` now opens with its own provenance** (dist tag, canonical version, publish
238
- date — rendered from `schema-pin.json`, never the wall clock). It ships in the tarball
239
- and is read cold by agents outside this repo, where the pin file is not present; a
240
- generated reference that cannot name its source was the one remaining self-dating gap.
241
- - **`AGENTS.md` carries a "Current as of" line, asserted by `codegen:check`** — the tag it
242
- names must match the pin or CI fails. A hand-maintained agent doc with an ungated date
243
- is how this repo's previous agent doc went a major version stale. Gate watched firing
244
- (tampered tag → exit 1, restored → 0).
245
-
246
- ## [4.1.0] — 2026-07-29
247
-
248
- Public-surface hygiene. Nothing breaks; one member is now marked for removal, and one
249
- piece of long-standing speculation is retired by measurement.
250
-
251
- ⚑ **4.0.2 was tagged in git and superseded before it reached npm.** Everything in it ships
252
- here — the tag stays as a record rather than being moved or deleted.
253
-
254
- ### Deprecated
255
- - **`FieldType.integer_max` and `FieldInfo.max`** — removal scheduled for **5.0.0**. No
256
- `FIELD_SCHEMA` entry has ever carried either, in this repository's entire history. They
257
- existed to surface the schema's `maximum:` constraint, and that constraint is **advisory**:
258
- keel declares no `MaxValueValidator`, and a `charisma: 9999` write against a `maximum: 100`
259
- field returns 201 and stores it verbatim. The canonical schema walk therefore stays silent
260
- on bounds permanently. There is no source to wire them to and no promise they could keep —
261
- and a public type member meaning "hint the wire ignores" is one consumers read as
262
- validation. Deprecating now rather than at the major so the signal arrives early; the
263
- `@deprecated` tags surface in editors via the shipped `.d.ts`.
264
-
265
- ### Changed
266
- - **`TokenResource` is confirmed staying.** RFC-001 §5 asked the platform owner whether the
267
- token routes were carried long-term or deprecated wire-side, and the question was never
268
- answered in writing — so this package's own barrel carried "wire fate under review; may be
269
- removed in a later 4.x" for months, on nobody's authority. Probed against production:
270
- `GET /api/v2/tokens/status/` and `/tokens/encryption-info/` both return **200**, with a 404
271
- control on a nonexistent route proving the check meant something. The wire carries it. The
272
- speculation is retired and the comment now records the evidence instead.
273
-
274
- ## [4.0.2] — 2026-07-29
275
-
276
- Re-pinned to `v0.30.1-dist.13` (canonical **00.30.01**). No field shape changed and no
277
- `FIELD_SCHEMA` entry changed — the schema walk is byte-identical between the two pins, so
278
- nothing about decoding moved. Three things changed and nothing else.
279
-
280
- ### Fixed
281
- - **`construct.relations` and `event.languages` shipped with no description at all** — in
282
- `types.generated.ts` and in `SCHEMA.md`. Their descriptions were nested one level too deep
283
- inside `items:` in the canonical YAML, which made them invisible to every consumer that reads
284
- field descriptions, this package included. `SCHEMA.md` is the package's AI-legibility artifact,
285
- so the gap landed where it did the most harm.
286
- - **Five description typos** corrected in published JSDoc and `SCHEMA.md`: `beapplied`,
287
- `phyiscal`, `relating the`, `object grant`, `eventuated`. The rendered docs site had already
288
- fixed all five by hand — the downstream copy was the correct one, and nobody noticed because
289
- the fix went where it was visible rather than where it was true.
290
-
291
- ### Changed
292
- - `ONLYWORLDS_VERSION` `'00.30.00'` → `'00.30.01'`. It is a public `as const`, so its **literal
293
- type** changes. Depending on that literal is pathological, but it is a type-level change and
294
- should not be discovered rather than announced.
295
- - The pin now carries canonical's numeric-bounds correction: the `maximum: 0` sentinel is gone
296
- from 26 fields, 8 fields gained `minimum: 0`, and `rulings.yaml` carries the numeric-bounds
297
- row. Nothing in this package consumes bounds — `maximum:` is **advisory** (keel does not
298
- enforce it; a `charisma: 9999` write returns 201 and stores verbatim) and the walk stays
299
- silent on bounds permanently. `integer_max` / `max` therefore remain declared no-ops here.
300
-
301
- ## [4.0.1] — 2026-07-29
302
-
303
- **Metadata correction release.** No wire-path change: the client's reads and writes never
304
- consulted `FIELD_SCHEMA`, and the generated interfaces carried the correct targets throughout.
305
- The exposure is anything that builds UI or validation by **iterating `FIELD_SCHEMA`**.
306
-
307
- ⚑ **One way this can surface as a compile error**: `relation.relations` is removed, so
308
- `FIELD_SCHEMA.relation.relations` is now a TypeScript error rather than a value. That is the
309
- intended outcome — the field does not exist in the standard and the API rejects it — but it can
310
- break a build rather than only a behaviour.
311
-
312
- ### Fixed
313
- - **`FIELD_SCHEMA.collective.equipment` targeted `construct`; the standard says `object`.**
314
- This is the founding case of the schema ruling table
315
- (`collective-equipment-target`, ruled 2026-07-23): the v1 implementation used
316
- Construct, v1 is decommissioned, and keel serves per YAML. The generated code path
317
- in this repo was corrected the same week. The hand-maintained `FIELD_SCHEMA` copy of
318
- the same fact, in the same package, kept shipping the decommissioned value on
319
- `latest` — the fix went where someone happened to be looking.
320
- - **`FIELD_SCHEMA.relation.relations` removed** — a `multi_link` to `relation` that does
321
- not exist in `relation.yaml`. A phantom field, publicly exported, that consumers
322
- building forms from this table would have sent to an API that 422s unknown keys.
323
-
324
- ### Changed
325
- - **`FIELD_SCHEMA` is now GENERATED** from the pinned schema distribution and gated by
326
- `codegen:check`, joining `ELEMENT_ICONS` / `ELEMENT_SECTIONS` / `ELEMENT_FAMILIES`. It
327
- was ~650 hand-maintained lines whose test compared nothing to the schema. Regenerating
328
- it changed exactly 2 of 467 entries — the two above. Runtime shape and the deeply
329
- readonly public types (`as const`) are unchanged.
330
- Two declared deviations from a naive schema read are now stated in the generated file:
331
- `pin.element` (a `generic-link`) splits into `element_type` + `element_id`, as the wire
332
- serves it; and `integer_max` / `max` remain in the `FieldType` union unused, because
333
- the walk does not surface the schema's `maximum:` constraint (41 across 17 types).
334
- - `codegen/generate_types.py` imports the vendored schema walk instead of carrying its
335
- own copy of it. Output byte-identical.
336
-
337
- ## [4.0.0] — 2026-07-23
338
-
339
- **v2-native only.** See `docs/migrating-3-to-4.md`. 3.x stays published forever for
340
- pinned consumers.
341
-
342
- ### Removed (BREAKING)
343
- - The v1 client (`OnlyWorldsClient`), the v1 `ElementType` enum, `icon-utils`.
344
- `ElementType` at the root is now THE v2 slug union (was `V2ElementType` alias in 3.x).
345
- - CJS build — the package is ESM-only with a sealed `exports` map (`"type": "module"`,
346
- `sideEffects: false`).
347
-
348
- ### Changed
349
- - Metadata tables (`ELEMENT_ICONS`, `ELEMENT_LABELS`, `ELEMENT_SECTIONS`, `FIELD_SCHEMA`,
350
- `getElementIcon`, `getElementLabel`) ported onto the v2 slug union — same export names,
351
- same shapes, contents byte-identical EXCEPT: `FIELD_SCHEMA` `type: 'number'` →
352
- `'integer'` (70 entries; 'number' dropped from `FieldType`). Migration: consumers
353
- switching on `'number'` switch on `'integer'`.
354
- - `TokenResource` now takes a structural `TokenTransport` (`{ request<T>(method, path,
355
- body?) }`) instead of the removed v1 client. Token surface retained pending the
356
- wire-fate ruling (RFC-001 §5) — may be removed in a later 4.x.
357
-
358
- ### Added (post-alpha.0, same night)
359
- - **`ELEMENT_ICONS` GENERATED** from keel's `icon:` wrapper key (keel `56c124a`).
360
- - **`ELEMENT_SECTIONS` DERIVED** from the canonical schema's own document structure
361
- (ruling: sections are the standard tier's property groups; document order is
362
- display order). Divergence check vs the old hand table found and fixed three fossils:
363
- creature "Behaviour"→"Behavior", pin's triple-listed generic link → `element`,
364
- relation "Involves" listing a nonexistent `relations` field. Canonical is truth.
365
- - **`SCHEMA.md`** — full generated schema reference (every field with its canonical
366
- description, link directions, families, icons, sections), ships in the tarball;
367
- covered by the codegen drift guard. AGENTS.md points agents at it first.
368
- - **`OwV2Client.request()` is public** (typed escape hatch; does not sanitize) and
369
- structurally satisfies `TokenTransport` — `new TokenResource(client)` just works.
370
- Token ruling: keel keeps `/tokens/*` long-term; ported, not deleted.
371
- - README rewritten v2-native (v1 sections, branded types, and the encrypted-key
372
- walkthrough removed; bulk partial-failure and idempotency-key hygiene promoted).
373
-
374
- ### alpha.1 — consumer-pin fixes (atlas review, same night)
375
- - **`parseEnvelope` → `parseErrorEnvelope`** (it parses the platform *error* envelope;
376
- renamed before the 4.0 name freeze to avoid collision with the world-export envelope).
377
- - The never-whitelist LAW now lives as a comment on `READ_ONLY_FIELDS` itself.
378
- - Stale v1 example in the shipped d.ts fixed (TokenResource JSDoc).
379
-
380
- ### Release gates — ALL GREEN (2026-07-23 night)
381
- - **Gate 3 (wire-log v1-traffic query)**: GREEN — no unknown v1-SDK consumer on
382
- the wire; observed v1 traffic is pinned deployed builds (council proxy, legacy plugin
383
- walks) that an npm major cannot touch. The v1 API dialect stays served regardless.
384
- - **`/api/v2/tokens/` live on prod + staging** (keel `e181689`; three-base byte
385
- agreement test-pinned). `TokenResource` annotates a 404 on token routes as
386
- "older server", not "no tokens". Requirement noted in the migration guide.
387
- - **`ONLYWORLDS_VERSION` now GENERATED** from canonical's `VERSION` file (carried
388
- into keel `schema/` by the refresh script, keel `492168c`) — the last hand-synced
389
- constant is dead. Consumer soak: the atlas app pinned the alpha against its full
390
- static surface — strict-clean compile, correct runtime, package shape approved.
391
- - **No `npm deprecate` on 3.x** (landscape research; 3.x stays plainly supported).
392
- - 4.x wishlist: account client (list/mint/create — atlas first consumer), world-export
393
- envelope reader (§8c), per-status error classes, richer codegen JSDoc, retry/backoff
394
- parity audit, FIELD_SCHEMA generation with `required:` from YAML.
395
-
396
- ## [3.1.0] — 2026-07-23
397
-
398
- ### Added
399
- - **Canonical element colour palette** (`src/v2/palette.ts`, exported from root):
400
- `ELEMENT_FAMILIES` (type → family), `FAMILY_COLORS` (family → `{light, dark}` hex),
401
- `familyOf(type)`, `elementColor(type, mode)`, `FAMILY_ORDER`, type `ElementFamily`.
402
- Four semantic families (agents / world / abstract / temporal) ruled 2026-07-22 after
403
- CVD-validated measurement.
404
- Colour carries the FAMILY; `ELEMENT_ICONS` carries the TYPE. Keyed on the v2 slug
405
- union. First proven live in atlas and council, whose local copies become re-exports.
406
- **`ELEMENT_FAMILIES` is GENERATED** from the `family:` key in keel's schema YAML
407
- (keel `c69366b`, a keel presentation-wrapper key — not part of the council-governed
408
- OnlyWorlds standard); hexes are hand-authored design constants beside it. Membership
409
- and hex invariants test-gated (`test/palette.test.mjs`); light hexes render-proven on
410
- the ruled OnlyWorlds light mode (ow-house-light) 2026-07-23.
411
- - **Codegen drift guard**: `python codegen/generate_types.py --check` fails if
412
- `types.generated.ts` doesn't match the schema YAMLs (release gate; mirrors keel's
413
- schema CI job). Codegen also hard-fails on missing/invalid `family:` keys — the
414
- canary for a canonical-refresh that overwrote keel's wrapper layer.
415
- - **CI**: GitHub Action building + running the full suite against the compiled bundle.
416
- - **AGENTS.md** shipped in the tarball — usage guide for AI agents reading the package
417
- locally, including the SDK-vs-MCP division of labor and the wire gotchas.
418
- - README: worked create→link→read-back round-trip, schema-verified field by field
419
- (also fixes the old example patching `'character'` with a location's id); canonical
420
- colour section; SDK-vs-MCP guidance.
421
- - This CHANGELOG.
422
-
423
- ### Fixed
424
- - `FieldType` union now includes `'number'` — the legacy alias that 70 `FIELD_SCHEMA`
425
- entries actually carry (consumers switch on it; data normalization deferred to 4.0).
426
- - `generate_types.py` exits with a clear message when the sibling keel checkout is
427
- absent instead of a bare traceback.
428
-
429
- ## [3.0.0] — 2026-07-18
430
-
431
- - v2-native client (`OwV2Client`) absorbed from an earlier v2 client (ow-v2-client v0.9.0),
432
- wire-corrected against live staging fixtures (S22). Generated element types from the
433
- canonical keel schema YAML (`src/v2/types.generated.ts`), validated against all 1,086
434
- live W11 elements. v1 surface unchanged and frozen.
1
+ # Changelog
2
+
3
+ All notable changes to `@onlyworlds/sdk`. Maintained from 3.1.0 onward;
4
+ earlier history lives in git log only.
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [4.9.0] — 2026-10-11
9
+
10
+ ### Added
11
+ - **Only if unchanged (keel #210).** `patch(type, id, partial, { ifMatch })` and
12
+ `editLinks(type, id, field, edit, { ifMatch })` send `If-Match: "<change_seq>"`: the write
13
+ lands only if the element is still at that `change_seq`. When it has moved, nothing is
14
+ written and the call throws **`OwPreconditionFailedError`** (412 `precondition_failed`), an
15
+ `OwApiError` whose `current` is the element as it is now, so the client's re-read is free.
16
+ `OwApiError.isPreconditionFailed` names the case. The server never merges; the client
17
+ compares the copy it edited from with `current`. An `ifMatch` that is not a whole number
18
+ throws `TypeError` before anything is sent. Without `ifMatch`, nothing changes.
19
+ Checked live on keel-staging: a matching token lands, a stale one is refused with
20
+ `current` at the live `change_seq`, the refused write changes nothing, and the links
21
+ route behaves the same.
22
+
23
+ ## [4.8.1] — 2026-10-11
24
+
25
+ ### Changed
26
+ - **Schema repinned `v0.31.0-dist.19` → `v0.32.0-dist.20`** (canonical 00.31.00 → 00.32.00).
27
+ The standard's World gains `public_read`, the walk's first boolean field (`scalar_bool`).
28
+ `OwWorldMeta` already carried `public_read` from `GET /world`; it stays read-only, since
29
+ `PATCH /world` refuses it (flipping a world public is an owner-level act on
30
+ onlyworlds.com). `ONLYWORLDS_VERSION` is now `'00.32.00'`. No element type, field or
31
+ `FIELD_SCHEMA` entry changed.
32
+
33
+ ## [4.8.0] — 2026-10-10
34
+
35
+ ### Added
36
+ - **`removeImage(keyOrUrl, { ticket? })`**: removes one uploaded image. A removal ticket from
37
+ keel (`POST /media/remove-ticket`, keel D91), then the removal at the ticket's `remove_url`
38
+ with the ticket only, never the API key. Takes the object key or the whole `image_url`. The
39
+ image's uploader or the world's owner key may remove it; the bytes go back to the uploader's
40
+ storage. Elements keep their `image_url`: clear it yourself.
41
+ - **`createRemovalTicket(key)`** and the types **`OwRemovalTicket`** (`ticket`, `remove_url`,
42
+ `exp` in unix seconds, `key`, `referenced`: how many elements still show the image) and
43
+ **`OwRemovedImage`** (`removed`, `bytes`, `cache`). Refusals arrive as `OwApiError`: keel's
44
+ `permission_error`, `not_found`, `removal_in_flight` and `upload_pending` (both 409 with
45
+ Retry-After); the edge's `ticket_expired`, `ticket_used`, `ticket_invalid`, `not_removable`,
46
+ `remove_failed`.
47
+
48
+ ## [4.7.3] — 2026-10-10
49
+
50
+ ### Changed
51
+ - **Schema repinned `v0.31.0-dist.18` → `v0.31.0-dist.19`** (canonical unchanged, 00.31.00).
52
+ Two generated values move: `ELEMENT_ICONS.zone` `architecture` → `pentagon`,
53
+ `ELEMENT_ICONS.marker` `location_on` → `polyline` (Material Symbols names; Marker no longer
54
+ shares the pin glyph). No field, type or `FIELD_SCHEMA` entry changed. The vendored tree grows
55
+ from 31 to 69 files (dist.19 adds `schema.json` and an example world); 69/69 hashed against
56
+ the pinned manifest.
57
+ - **`AGENTS.md` re-read against the new pin** (its gate fired on the repin, as designed); its
58
+ wire facts hold, its "Current as of" line moves.
59
+
60
+ ## [4.7.2] — 2026-10-09
61
+
62
+ ### Changed
63
+ - **README first run** finds Admiral Fluffington by name (the demo world's default list order moves with every edit, so "the first three" could not be promised); the npm page shows the corrected output.
64
+ - **Schema repinned `v0.30.2-dist.16` → `v0.31.0-dist.18`** (canonical **00.31.00**).
65
+ `ONLYWORLDS_VERSION` is now `'00.31.00'`. The World gains three optional free-text settings,
66
+ `length_unit`, `mass_unit` and `distance_unit`; the nine element fields that promised "world
67
+ LENGTH / MASS / DISTANCE units" now name them in their descriptions (Ability `range`, Character
68
+ and Creature `height` and `weight`, Creature `speed`, Location `elevation`, Object and Species
69
+ `weight`). No element field shape and no `FIELD_SCHEMA` entry changed. The world body is not
70
+ typed per field (`OwWorldMeta` is open), so `getWorld` returns the three keys and `patchWorld`
71
+ sends them with no code change; a test says so.
72
+
73
+ ## [4.7.1] — 2026-10-08
74
+
75
+ ### Changed
76
+ - **README rewritten**: install, a first run against the public demo world (its output shown,
77
+ run as written), your own world with an agent seat for writes, then the reference. It no
78
+ longer calls a `world` field in a payload an error: the API ignores it.
79
+ - Comments, the RFC and pin-file prose no longer name people or internal places.
80
+ - **Schema repinned `v0.30.1-dist.15` → `v0.30.2-dist.16`** (canonical **00.30.02**).
81
+ `ONLYWORLDS_VERSION` is now `'00.30.02'`. Field descriptions move with the schema: Location's
82
+ `rivalries` and `partnerships`, Marker's `order`, and Pin's `element` pair. No field shape and
83
+ no `FIELD_SCHEMA` entry changed: canonical now requires only `name` on every type, which is
84
+ what `FIELD_SCHEMA` already said, so its hand-made exception for the old Pin and Marker
85
+ lists is gone. If the schema lists more than `name` again, codegen stops and asks for a
86
+ ruling instead of dropping the list. 31/31 file hashes recomputed from the tag.
87
+
88
+ ## [4.7.0] — 2026-10-08
89
+
90
+ ### Added
91
+ - **`OwApiError.isResyncRequired`**: 409 `resync_required` from `/changes`. Guests can follow the
92
+ feed since keel D79; when a guest's view or a key's role changes, its stored cursor is refused. Drop
93
+ it, walk from zero, and replace the local copy rather than merging.
94
+
95
+ ### Documented
96
+ - `uploadImage` accepts webp, png, jpeg and avif. 4.6.0's JSDoc and README also listed gif, which
97
+ the edge refuses (415 `unsupported_type`); checked against the Worker's type table.
98
+ - `changes()`: the old rewind rule compared a stored cursor with `head`, which an opaque cursor
99
+ cannot do; replaced with the reset rule (`isResyncRequired`).
100
+ - The doc comments of `OwClientConfig`, `create` and `uploadImage` are written for users: internal
101
+ ruling numbers and test-harness notes are gone, and each opens with what the option or method does.
102
+ The docs site's TypeScript reference is generated from these comments.
103
+
104
+ ## [4.6.0] — 2026-10-06
105
+
106
+ Published from `e198298` (tag `v4.6.0`); the tarball was checked against a build of that commit.
107
+
108
+ Includes 4.5.1's documentation fixes: 4.5.1 was staged and never published.
109
+
110
+ ### Added
111
+ - **`uploadImage(image, { key?, ticket? })`**: upload one image (webp, png, jpeg, avif;
112
+ never SVG; 4.6.0's docs also said gif, which the edge refuses with 415) and get its permanent public URL. Two requests: a ticket from keel
113
+ (`POST /media/ticket`), then the bytes straight to the edge at the ticket's `upload_url`
114
+ (keel never sees them). Only the ticket goes to the edge, never the API key or PIN. An image
115
+ larger than the ticket's `max_bytes` throws 413 `too_large` before anything is sent; the edge's
116
+ refusals (`ticket_used`, `ticket_expired`, `bad_key`, `exists`, `unsupported_type`, ...) arrive
117
+ as `OwApiError` with their code. Set the returned `url` as an element's `image_url` yourself.
118
+ Checked live on 2026-10-06: one upload through keel's ticket to the edge (201), and the same
119
+ ticket again refused with 401 `ticket_used`.
120
+ - **`createMediaTicket()`**: the first step alone, for callers that upload themselves (progress
121
+ bars, say). Types `OwMediaTicket` and `OwUploadedImage`.
122
+
123
+ ### Documented
124
+ - `ListParams.filter` lists what keel accepts now: exact `name`, and `characters=<id>` on every
125
+ type with a `characters` link (keel D76d).
126
+
127
+ ## [4.5.1] — 2026-10-05 (never published; shipped in 4.6.0)
128
+
129
+ Docs only: no code path changes.
130
+
131
+ ### Documented
132
+ - **`isOwnerOnly`'s JSDoc in 4.5.0 was wrong about who sees `owner_only`.** It said owners and
133
+ co-builders never see it. A member key, a co-builder's too, gets it on `PATCH /world`; only the
134
+ owner's key is exempt (keel spec section 9; confirmed from keel's `patch_world`,
135
+ 2026-10-04).
136
+ - **`world_gone` is reserved.** `OwAuthErrorCode` and `isAuthError` still carry it, but keel never
137
+ emits it: a deleted world's keys are deleted with it, so the key answers 401 `invalid_credentials`
138
+ (2026-10-04; keel spec section 2). Found by the wire gate reading the SDK's own
139
+ built file for the codes it keys on.
140
+
141
+ ## [4.5.0] — 2026-10-04
142
+
143
+ What the staging write probes found on the wire (Python package, 2026-10-04): one new getter, and
144
+ three behaviours the server has always had that no client doc said. Additive; nothing is removed.
145
+
146
+ ### Added
147
+ - **`OwApiError.isOwnerOnly`**: 403 `owner_only`, a contributor attempting a world-level change
148
+ (`patchWorld`, publishing a show page). Owners and co-builders never see it. keel's OpenAPI
149
+ document does not name the code yet (the schema side has that to close); the wire returns it.
150
+
151
+ ### Documented
152
+ - **An atomic `/bulk` rollback still returns 201 slots** for the items that would have
153
+ succeeded, with the ids and timestamps they would have had (keel spec: "counterfactual 201s").
154
+ After `atomic: true` with `errors: true`, nothing was written. On `bulk()`, `OwBulkItemResult`
155
+ and the README.
156
+ - **`name` is required as a key only**: `''` and `null` are accepted and stored as `''` (keel
157
+ ruling 2026-09-28, spec §6; nameless Markers exist). On `create()` and the README.
158
+
159
+ ## [4.4.0] — 2026-10-04
160
+
161
+ Membership, as far as the server has shipped it: who created an element, and the refusal a
162
+ contributor meets on someone else's. Additive; nothing is removed or narrows.
163
+
164
+ ### Added
165
+ - **`created_by`** on `OwElementBase` (so on every element type): `string | null`, the id of the
166
+ membership that created the element, `null` for the world's owner and for anything made
167
+ before memberships existed (keel D72 phase 2, live 2026-09-28; keel spec §4). Read-only. The
168
+ schema does not carry it (like `created_at` it is server bookkeeping), so the generator adds
169
+ it to the wire base under the declared mapping. A write body that carries it has it dropped
170
+ by the server, so the client does not strip it and a read body still round-trips.
171
+ - **`OwApiError.isNotAuthor`**: 403 `not_author`, a contributor changing, replacing, relinking
172
+ or deleting an element someone else created. `/bulk` answers 200 and reports it in the item
173
+ slot, so check the slot (documented on `OwBulkItemResult`).
174
+
175
+ ### Fixed
176
+ - **`FIELD_SCHEMA` no longer marks `marker.map/zone/x/y/order` and `pin.map/element_type/element_id/x/y`
177
+ as `required: true`.** The wire requires `name` and nothing else (rulings.yaml `nullable-by-default`,
178
+ ruling 2026-07-28; keel's OpenAPI write schemas say `required: [name]`), but the schema files still
179
+ list those fields and the generator copied the lists, so a form built from the table demanded
180
+ coordinates the server never did. Found by comparing the SDK with keel's OpenAPI document (the
181
+ new `verify-sdk-wire` gate). **If you read `required` from this table, marker and pin
182
+ fields now read as optional**, which is what the server accepts. The generator notes the lists it
183
+ ignores on every run and says when canonical stops carrying them.
184
+
185
+ ### Not yet
186
+ - `me()` and `members()` wait for the server routes (keel's M2b). Nothing in this release
187
+ guesses at their names.
188
+
189
+ ## [4.3.0] — 2026-09-28
190
+
191
+ Errors you can act on: the two 409s told apart, and the busy server's `Retry-After`, in Node and
192
+ browsers. One getter narrows (see Fixed); nothing is removed.
193
+
194
+ ### Fixed
195
+ - **`OwApiError.isIdempotencyConflict` no longer claims every 409.** keel sends two: 409
196
+ `idempotency_error` (a key reused with a different body) and 409 `id_conflict` (the id is
197
+ taken: a create with an existing id since D39, or a PUT / bulk id held by another world since
198
+ D70). The getter was true for both, so an id conflict read as a key problem, and retrying with
199
+ a fresh key could never help. It now matches `idempotency_error` only, and the new
200
+ **`isIdConflict`** names the other. A caller who relied on the old catch-all for `id_conflict`
201
+ should switch to `isIdConflict`. A 409 with no code (a proxy, a non-JSON body) now reads
202
+ false for both. `/bulk` is unaffected: it answers 200 and reports a per-item 409 in the slot.
203
+
204
+ ### Added
205
+ - **`OwApiError.retryAfter`** (seconds, or null) is parsed from `Retry-After`, as a delay or an
206
+ HTTP date, and **`isBusy`** marks keel's 503 `server_busy` (admission control, keel D71: six
207
+ requests at once per worker, up to 10 s of queueing, then 503 with `Retry-After: 5`). The client
208
+ still never retries on its own; it gives callers what they need to back off. A malformed
209
+ `Retry-After` is null, never "retry now". **Works in browsers too** since keel `4949406`
210
+ (2026-09-28): every response exposes `Retry-After`, `Idempotent-Replay` and
211
+ `X-OW-Schema-Version` over CORS (probed from an allowed origin), and keel reports that the
212
+ admission gate's 503 now carries CORS headers, so a busy server reads as busy, not as
213
+ `OwNetworkError` (keel's word; the 503 can't be triggered on demand to probe). Six tests; the
214
+ four core ones watched failing on the previous `errors.ts`.
215
+
216
+ ## [4.2.0] — 2026-09-28
217
+
218
+ Ids, honest filter docs, and the schema repin. Nothing is removed; no call needs to change.
219
+
220
+ ### Changed
221
+ - **`create()` now mints an RFC 9562 UUIDv7** when the element has no id (was v4). The first
222
+ 48 bits are the creation millisecond, so ids this client mints a millisecond or more apart
223
+ sort by creation (while the clock does not step backwards), which gives databases better
224
+ index locality. **This holds within one client only**: worlds also hold v4 ids and legacy
225
+ v1-server ids (`06x…`, which also carry version nibble 7 but put seconds first), so never
226
+ order elements by id. For creation order use `created_at`; `change_seq` is last-write
227
+ order. This is a **default, not a
228
+ requirement**: a v4 or v7 id a caller supplies is still accepted as-is, and
229
+ every id already stored stays valid. Keel mints v7 server-side too (ruling,
230
+ 2026-09-28). Pinned by six tests: version and variant, the big-endian timestamp above
231
+ bit 32, fractional and pre-1970 clocks floored consistently, creation order across 50
232
+ consecutive milliseconds, 1,000 distinct ids inside one millisecond, and the no-`crypto`
233
+ fallback (asserting `Math.random` is actually used). Five injected defects (v4 nibble,
234
+ 32-bit truncation, a dropped random fill, a dropped variant mask, an unfloored clock)
235
+ each fail the suite on every run, on Node 20 and 22. An independent reviewer's decoder agreed on 20,000 random timestamps and the 48-bit
236
+ edges, and keel's `uuid7()` agrees on timestamp, version and variant for a pinned clock.
237
+ - **`elementColor()` on an unknown type now throws a `TypeError` that names the type.** It
238
+ used to crash with `Cannot read properties of undefined (reading 'dark')`. Found while
239
+ building a colour gate: atlas's own copy falls back to the `world` family for an
240
+ unknown type, so swapping it for this export is not a pure re-export for that input, and the
241
+ docstring now says so. Whether the package should fall back is left to its consumers.
242
+ - The generated field-schema comment no longer calls `maximum:` an open question or counts
243
+ its occurrences (the count was 41; since 00.30.01 it is 15). The question was ruled on
244
+ 2026-07-29.
245
+ - **`ListParams.filter` JSDoc names what the server actually accepts**: `name__icontains`,
246
+ `supertype`, `subtype`. It used to list `__in`, `__gte`, `__lte` and `__isnull`, copied
247
+ from keel's spec, which described them but never built them. Probed live 2026-09-28: the
248
+ three accepted keys answer 200; every other one, and `?ordering=`, answers 422.
249
+ - **Schema repinned `v0.30.1-dist.13` → `v0.30.1-dist.15`** (canonical unchanged, 00.30.01).
250
+ Two generated values move: `ELEMENT_ICONS.institution` `business` → `account_balance`,
251
+ `ELEMENT_ICONS.marker` `place` → `location_on` (both Material Symbols names). dist.15
252
+ also adds `minimum: 0` to `ability.potency`; the walk does not surface bounds, so nothing
253
+ generated changes for it. 31/31 file hashes recomputed from a fresh download.
254
+ - **`AGENTS.md` re-read against the new pin** (its gate fired on the repin, as designed).
255
+ It still called the v1 client "frozen legacy" in this package; v1 was removed at 4.0.0.
256
+ It now also names the wire behaviours keel deployed on 2026-09-28 (keel D70): the 64 KB
257
+ extension cap, the three built filters and the `?ordering=` 422, 409 `id_conflict` for a
258
+ PUT or bulk id that belongs to another world, and the 422 for unpaired surrogates. None of
259
+ them needs client code.
260
+
261
+ ### Also in this release (staged earlier)
262
+ - **`SCHEMA.md` now opens with its own provenance** (dist tag, canonical version, publish
263
+ date — rendered from `schema-pin.json`, never the wall clock). It ships in the tarball
264
+ and is read cold by agents outside this repo, where the pin file is not present; a
265
+ generated reference that cannot name its source was the one remaining self-dating gap.
266
+ - **`AGENTS.md` carries a "Current as of" line, asserted by `codegen:check`** — the tag it
267
+ names must match the pin or CI fails. A hand-maintained agent doc with an ungated date
268
+ is how this repo's previous agent doc went a major version stale. Gate watched firing
269
+ (tampered tag → exit 1, restored → 0).
270
+
271
+ ## [4.1.0] — 2026-07-29
272
+
273
+ Public-surface hygiene. Nothing breaks; one member is now marked for removal, and one
274
+ piece of long-standing speculation is retired by measurement.
275
+
276
+ ⚑ **4.0.2 was tagged in git and superseded before it reached npm.** Everything in it ships
277
+ here — the tag stays as a record rather than being moved or deleted.
278
+
279
+ ### Deprecated
280
+ - **`FieldType.integer_max` and `FieldInfo.max`** — removal scheduled for **5.0.0**. No
281
+ `FIELD_SCHEMA` entry has ever carried either, in this repository's entire history. They
282
+ existed to surface the schema's `maximum:` constraint, and that constraint is **advisory**:
283
+ keel declares no `MaxValueValidator`, and a `charisma: 9999` write against a `maximum: 100`
284
+ field returns 201 and stores it verbatim. The canonical schema walk therefore stays silent
285
+ on bounds permanently. There is no source to wire them to and no promise they could keep —
286
+ and a public type member meaning "hint the wire ignores" is one consumers read as
287
+ validation. Deprecating now rather than at the major so the signal arrives early; the
288
+ `@deprecated` tags surface in editors via the shipped `.d.ts`.
289
+
290
+ ### Changed
291
+ - **`TokenResource` is confirmed staying.** RFC-001 §5 asked the platform owner whether the
292
+ token routes were carried long-term or deprecated wire-side, and the question was never
293
+ answered in writing — so this package's own barrel carried "wire fate under review; may be
294
+ removed in a later 4.x" for months, on nobody's authority. Probed against production:
295
+ `GET /api/v2/tokens/status/` and `/tokens/encryption-info/` both return **200**, with a 404
296
+ control on a nonexistent route proving the check meant something. The wire carries it. The
297
+ speculation is retired and the comment now records the evidence instead.
298
+
299
+ ## [4.0.2] — 2026-07-29
300
+
301
+ Re-pinned to `v0.30.1-dist.13` (canonical **00.30.01**). No field shape changed and no
302
+ `FIELD_SCHEMA` entry changed — the schema walk is byte-identical between the two pins, so
303
+ nothing about decoding moved. Three things changed and nothing else.
304
+
305
+ ### Fixed
306
+ - **`construct.relations` and `event.languages` shipped with no description at all** — in
307
+ `types.generated.ts` and in `SCHEMA.md`. Their descriptions were nested one level too deep
308
+ inside `items:` in the canonical YAML, which made them invisible to every consumer that reads
309
+ field descriptions, this package included. `SCHEMA.md` is the package's AI-legibility artifact,
310
+ so the gap landed where it did the most harm.
311
+ - **Five description typos** corrected in published JSDoc and `SCHEMA.md`: `beapplied`,
312
+ `phyiscal`, `relating the`, `object grant`, `eventuated`. The rendered docs site had already
313
+ fixed all five by hand — the downstream copy was the correct one, and nobody noticed because
314
+ the fix went where it was visible rather than where it was true.
315
+
316
+ ### Changed
317
+ - `ONLYWORLDS_VERSION` `'00.30.00'` → `'00.30.01'`. It is a public `as const`, so its **literal
318
+ type** changes. Depending on that literal is pathological, but it is a type-level change and
319
+ should not be discovered rather than announced.
320
+ - The pin now carries canonical's numeric-bounds correction: the `maximum: 0` sentinel is gone
321
+ from 26 fields, 8 fields gained `minimum: 0`, and `rulings.yaml` carries the numeric-bounds
322
+ row. Nothing in this package consumes bounds — `maximum:` is **advisory** (keel does not
323
+ enforce it; a `charisma: 9999` write returns 201 and stores verbatim) and the walk stays
324
+ silent on bounds permanently. `integer_max` / `max` therefore remain declared no-ops here.
325
+
326
+ ## [4.0.1] — 2026-07-29
327
+
328
+ **Metadata correction release.** No wire-path change: the client's reads and writes never
329
+ consulted `FIELD_SCHEMA`, and the generated interfaces carried the correct targets throughout.
330
+ The exposure is anything that builds UI or validation by **iterating `FIELD_SCHEMA`**.
331
+
332
+ ⚑ **One way this can surface as a compile error**: `relation.relations` is removed, so
333
+ `FIELD_SCHEMA.relation.relations` is now a TypeScript error rather than a value. That is the
334
+ intended outcome — the field does not exist in the standard and the API rejects it — but it can
335
+ break a build rather than only a behaviour.
336
+
337
+ ### Fixed
338
+ - **`FIELD_SCHEMA.collective.equipment` targeted `construct`; the standard says `object`.**
339
+ This is the founding case of the schema ruling table
340
+ (`collective-equipment-target`, ruled 2026-07-23): the v1 implementation used
341
+ Construct, v1 is decommissioned, and keel serves per YAML. The generated code path
342
+ in this repo was corrected the same week. The hand-maintained `FIELD_SCHEMA` copy of
343
+ the same fact, in the same package, kept shipping the decommissioned value on
344
+ `latest` — the fix went where someone happened to be looking.
345
+ - **`FIELD_SCHEMA.relation.relations` removed** — a `multi_link` to `relation` that does
346
+ not exist in `relation.yaml`. A phantom field, publicly exported, that consumers
347
+ building forms from this table would have sent to an API that 422s unknown keys.
348
+
349
+ ### Changed
350
+ - **`FIELD_SCHEMA` is now GENERATED** from the pinned schema distribution and gated by
351
+ `codegen:check`, joining `ELEMENT_ICONS` / `ELEMENT_SECTIONS` / `ELEMENT_FAMILIES`. It
352
+ was ~650 hand-maintained lines whose test compared nothing to the schema. Regenerating
353
+ it changed exactly 2 of 467 entries — the two above. Runtime shape and the deeply
354
+ readonly public types (`as const`) are unchanged.
355
+ Two declared deviations from a naive schema read are now stated in the generated file:
356
+ `pin.element` (a `generic-link`) splits into `element_type` + `element_id`, as the wire
357
+ serves it; and `integer_max` / `max` remain in the `FieldType` union unused, because
358
+ the walk does not surface the schema's `maximum:` constraint (41 across 17 types).
359
+ - `codegen/generate_types.py` imports the vendored schema walk instead of carrying its
360
+ own copy of it. Output byte-identical.
361
+
362
+ ## [4.0.0] — 2026-07-23
363
+
364
+ **v2-native only.** See `docs/migrating-3-to-4.md`. 3.x stays published forever for
365
+ pinned consumers.
366
+
367
+ ### Removed (BREAKING)
368
+ - The v1 client (`OnlyWorldsClient`), the v1 `ElementType` enum, `icon-utils`.
369
+ `ElementType` at the root is now THE v2 slug union (was `V2ElementType` alias in 3.x).
370
+ - CJS build — the package is ESM-only with a sealed `exports` map (`"type": "module"`,
371
+ `sideEffects: false`).
372
+
373
+ ### Changed
374
+ - Metadata tables (`ELEMENT_ICONS`, `ELEMENT_LABELS`, `ELEMENT_SECTIONS`, `FIELD_SCHEMA`,
375
+ `getElementIcon`, `getElementLabel`) ported onto the v2 slug union — same export names,
376
+ same shapes, contents byte-identical EXCEPT: `FIELD_SCHEMA` `type: 'number'` →
377
+ `'integer'` (70 entries; 'number' dropped from `FieldType`). Migration: consumers
378
+ switching on `'number'` switch on `'integer'`.
379
+ - `TokenResource` now takes a structural `TokenTransport` (`{ request<T>(method, path,
380
+ body?) }`) instead of the removed v1 client. Token surface retained pending the
381
+ wire-fate ruling (RFC-001 §5) — may be removed in a later 4.x.
382
+
383
+ ### Added (post-alpha.0, same night)
384
+ - **`ELEMENT_ICONS` GENERATED** from keel's `icon:` wrapper key (keel `56c124a`).
385
+ - **`ELEMENT_SECTIONS` DERIVED** from the canonical schema's own document structure
386
+ (ruling: sections are the standard tier's property groups; document order is
387
+ display order). Divergence check vs the old hand table found and fixed three fossils:
388
+ creature "Behaviour"→"Behavior", pin's triple-listed generic link → `element`,
389
+ relation "Involves" listing a nonexistent `relations` field. Canonical is truth.
390
+ - **`SCHEMA.md`** — full generated schema reference (every field with its canonical
391
+ description, link directions, families, icons, sections), ships in the tarball;
392
+ covered by the codegen drift guard. AGENTS.md points agents at it first.
393
+ - **`OwV2Client.request()` is public** (typed escape hatch; does not sanitize) and
394
+ structurally satisfies `TokenTransport` — `new TokenResource(client)` just works.
395
+ Token ruling: keel keeps `/tokens/*` long-term; ported, not deleted.
396
+ - README rewritten v2-native (v1 sections, branded types, and the encrypted-key
397
+ walkthrough removed; bulk partial-failure and idempotency-key hygiene promoted).
398
+
399
+ ### alpha.1 — consumer-pin fixes (atlas review, same night)
400
+ - **`parseEnvelope` → `parseErrorEnvelope`** (it parses the platform *error* envelope;
401
+ renamed before the 4.0 name freeze to avoid collision with the world-export envelope).
402
+ - The never-whitelist LAW now lives as a comment on `READ_ONLY_FIELDS` itself.
403
+ - Stale v1 example in the shipped d.ts fixed (TokenResource JSDoc).
404
+
405
+ ### Release gates — ALL GREEN (2026-07-23 night)
406
+ - **Gate 3 (wire-log v1-traffic query)**: GREEN — no unknown v1-SDK consumer on
407
+ the wire; observed v1 traffic is pinned deployed builds (council proxy, legacy plugin
408
+ walks) that an npm major cannot touch. The v1 API dialect stays served regardless.
409
+ - **`/api/v2/tokens/` live on prod + staging** (keel `e181689`; three-base byte
410
+ agreement test-pinned). `TokenResource` annotates a 404 on token routes as
411
+ "older server", not "no tokens". Requirement noted in the migration guide.
412
+ - **`ONLYWORLDS_VERSION` now GENERATED** from canonical's `VERSION` file (carried
413
+ into keel `schema/` by the refresh script, keel `492168c`) — the last hand-synced
414
+ constant is dead. Consumer soak: the atlas app pinned the alpha against its full
415
+ static surface — strict-clean compile, correct runtime, package shape approved.
416
+ - **No `npm deprecate` on 3.x** (landscape research; 3.x stays plainly supported).
417
+ - 4.x wishlist: account client (list/mint/create — atlas first consumer), world-export
418
+ envelope reader (§8c), per-status error classes, richer codegen JSDoc, retry/backoff
419
+ parity audit, FIELD_SCHEMA generation with `required:` from YAML.
420
+
421
+ ## [3.1.0] — 2026-07-23
422
+
423
+ ### Added
424
+ - **Canonical element colour palette** (`src/v2/palette.ts`, exported from root):
425
+ `ELEMENT_FAMILIES` (type → family), `FAMILY_COLORS` (family → `{light, dark}` hex),
426
+ `familyOf(type)`, `elementColor(type, mode)`, `FAMILY_ORDER`, type `ElementFamily`.
427
+ Four semantic families (agents / world / abstract / temporal) ruled 2026-07-22 after
428
+ CVD-validated measurement.
429
+ Colour carries the FAMILY; `ELEMENT_ICONS` carries the TYPE. Keyed on the v2 slug
430
+ union. First proven live in atlas and council, whose local copies become re-exports.
431
+ **`ELEMENT_FAMILIES` is GENERATED** from the `family:` key in keel's schema YAML
432
+ (keel `c69366b`, a keel presentation-wrapper key — not part of the council-governed
433
+ OnlyWorlds standard); hexes are hand-authored design constants beside it. Membership
434
+ and hex invariants test-gated (`test/palette.test.mjs`); light hexes render-proven on
435
+ the ruled OnlyWorlds light mode (ow-house-light) 2026-07-23.
436
+ - **Codegen drift guard**: `python codegen/generate_types.py --check` fails if
437
+ `types.generated.ts` doesn't match the schema YAMLs (release gate; mirrors keel's
438
+ schema CI job). Codegen also hard-fails on missing/invalid `family:` keys — the
439
+ canary for a canonical-refresh that overwrote keel's wrapper layer.
440
+ - **CI**: GitHub Action building + running the full suite against the compiled bundle.
441
+ - **AGENTS.md** shipped in the tarball — usage guide for AI agents reading the package
442
+ locally, including the SDK-vs-MCP division of labor and the wire gotchas.
443
+ - README: worked create→link→read-back round-trip, schema-verified field by field
444
+ (also fixes the old example patching `'character'` with a location's id); canonical
445
+ colour section; SDK-vs-MCP guidance.
446
+ - This CHANGELOG.
447
+
448
+ ### Fixed
449
+ - `FieldType` union now includes `'number'` — the legacy alias that 70 `FIELD_SCHEMA`
450
+ entries actually carry (consumers switch on it; data normalization deferred to 4.0).
451
+ - `generate_types.py` exits with a clear message when the sibling keel checkout is
452
+ absent instead of a bare traceback.
453
+
454
+ ## [3.0.0] — 2026-07-18
455
+
456
+ - v2-native client (`OwV2Client`) absorbed from an earlier v2 client (ow-v2-client v0.9.0),
457
+ wire-corrected against live staging fixtures (S22). Generated element types from the
458
+ canonical keel schema YAML (`src/v2/types.generated.ts`), validated against all 1,086
459
+ live W11 elements. v1 surface unchanged and frozen.