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