Contract and changelog: unions refuse an unknown arm and an unnamed subtype

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-10 21:34:57 +02:00
commit ee227bb782
2 changed files with 32 additions and 0 deletions

View file

@ -73,6 +73,27 @@ Typed lookups and watches (`Get<T>`, `GetAll<T>`, `GetByName<T>`,
type is assignable to `T`. A single-result lookup with several candidates
throws.
## Unions
A union is an abstract class or interface carrying `[Union(key, typeof(Arm))]`
attributes on itself (a union whose arms are also a union has its own key set;
attributes are not inherited). A union value is `nil` for a null reference, and
otherwise an arm: the two-element array `[key, armSlots]`, where `armSlots` is the
arm's own slot array (a unit arm is `[key, []]`). Keys start anywhere and may
have gaps.
- A key is the identity of an arm. It is never reused, and a retired arm's key
stays a gap.
- An arm key the reader's union does not declare is refused on read, in a field,
a list or an arm, with an error naming the union type and the key. The
payload is never echoed, and the value is never read back as `null`: a runtime
that cannot name an arm cannot re-encode it.
- A value whose runtime type is not exactly a declared arm is refused on write,
naming the union and the type. A subtype of an arm is not the arm.
- A store that holds a refused arm fails to load, and the error names the record
key and its schema id.
- Arm slots follow the slot rules below; this rule does not change them.
## Soft-migratable drift
CultCache accepts compatible drift only when the local reader can still map the

View file

@ -2,6 +2,17 @@
All notable changes to this package are documented in this file.
## [Unreleased]
### Breaking
- CultCache refuses an unknown union arm on read and an unnamed union subtype on
write. A payload whose arm key the union does not declare used to decode to
`null` (and the next save wrote the loss back); a value whose type the union
does not name used to be written as `nil`. Both now throw, naming the union and
the key or type, and a store pull names the record key and schema id. Known arms
encode to the same bytes as before.
## [1.0.60]
### Changed