From 4dc7f3a1fb27e8a75cbff30daa6f72c9be0c3cfb Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 14 Sep 2026 11:58:18 +0200 Subject: [PATCH] Record write atomicity and __global__-only globals in the store contract Adds the row "Refused write changes neither store nor cache" with each runtime's actual guarantee, including that Python serializes nothing across threads. Corrects the TypeScript home-move cell (attach runs in the serial queue), the TypeScript and Python globals cells (any key but __global__ is refused before a store is touched), the Rust refused-at-load cell (load_soa covered, load_envelope gone) and the Rust and Python batch descriptions. Co-Authored-By: Claude Opus 5 --- .../Contracts/cultcache-store-composition.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/GameCult.Caching/Contracts/cultcache-store-composition.md b/src/GameCult.Caching/Contracts/cultcache-store-composition.md index 030668a1..e0521079 100644 --- a/src/GameCult.Caching/Contracts/cultcache-store-composition.md +++ b/src/GameCult.Caching/Contracts/cultcache-store-composition.md @@ -239,13 +239,14 @@ no serialization; a CultMath encoding, if added, uses this shape. |---|---|---|---|---| | One home store, no mirrors | implemented | implemented (exact type ids; `add_backing_store` returns `Err` on a second generic store or a claimed type; an empty type list is the generic store) | implemented (exact type ids; `addBackingStore` throws on a second generic store or a claimed type; no types is the generic store) | implemented (exact type ids; `add_backing_store`/`add_generic_store` raise on a claimed type or a second generic store; an empty type list is the generic store) | | Zero stores is in memory | implemented | implemented | implemented | implemented | -| Home cannot move after admission | implemented | implemented (`add_backing_store` returns `Err` and attaches nothing) | implemented (`addBackingStore` throws and attaches nothing) | implemented (`add_backing_store`/`add_generic_store` raise and attach nothing) | -| Refused at load when not home | implemented | implemented (`pull_all_backing_stores` returns `Err` naming key and both stores; admits nothing) | implemented (`pullAllBackingStores` throws naming key and both stores; admits nothing) | implemented (`pull_all_backing_stores` raises naming key and both stores; admits nothing) | +| Home cannot move after admission | implemented | implemented (`add_backing_store` returns `Err` and attaches nothing) | implemented (`addBackingStore` rejects and attaches nothing; it runs in the cache's serial queue, so it cannot move a write that is in flight) | implemented (`add_backing_store`/`add_generic_store` raise and attach nothing) | +| Refused at load when not home | implemented | implemented (`pull_all_backing_stores` returns `Err` naming key and both stores; `load_soa` returns `Err` for a type with no home once a store is attached; both admit nothing and leave the prior view intact) | implemented (`pullAllBackingStores` throws naming key and both stores; admits nothing) | implemented (`pull_all_backing_stores` raises naming key and both stores; admits nothing) | +| Refused write changes neither store nor cache | implemented | implemented (`put`, `put_envelope`, `put_raw_envelope`, `put_prepared_batch` and `delete` validate before the store call and apply infallibly after it; there are no user accessors) | implemented (home, `__global__` key and name and index accessors run before the store call; overwrite and delete use lookup values stored at admission; attach, pull, registration, writes and deletes run through one per-cache promise queue) | implemented for one caller at a time (same validation order and stored lookup keys as TypeScript; the cache takes no lock, so concurrent threads are not serialized) | | Attach hydrates; loading never writes | implemented | attach does not read (call `pull_all_backing_stores`); pull loads without writing | attach does not read (call `pullAllBackingStores`); pull loads without writing | attach does not read (call `pull_all_backing_stores`); pull loads without writing | | Runtime type decides schema | implemented | n/a (explicit type ids) | n/a | n/a | | Assignable lookups and watches | implemented | exact type ids | exact type ids | exact type ids | -| Globals never invented, singleton | implemented | no globals | single `__global__`; a write with a new key replaces it; refused only on pull; invents nothing | single `__global__`, invents nothing | -| Explicit batch commit | implemented | `put_prepared_batch`, one store, all-or-nothing | not implemented | `put_envelopes`, one type per call, not all-or-nothing | +| Globals never invented, singleton | implemented | no globals | single `__global__`; a write under any other key is refused before any store is touched; replacing writes `__global__` in place; a second persisted global is refused on pull; invents nothing | single `__global__`; `put`, `put_envelope` and `put_envelopes` refuse any other key before any store is touched; a second persisted global is refused on pull; invents nothing | +| Explicit batch commit | implemented | `put_prepared_batch`: staged records must share one home; that store receives one `push_all` of its own held records plus the batch; empty key or `stored_at` refused first; all-or-nothing | not implemented | `put_envelopes`, one type per call; every record validated before one `push_all`, applied only after the store accepts; atomicity on disk is the store's `push_all` | | Conditional commit | implemented | store-level `compare_exchange` family | not implemented | not implemented | | Per-assembly options, value-type arrays | contract | n/a | n/a | n/a |