Record the legacy global shim, re-entry rule, and Rust push_all shape

Operator decisions after Soul's check of the sibling locks: a legacy-key global
loads as a read-only compatibility shim that the first write replaces under
__global__ and removes; re-entrant mutation is forbidden in TS and Python; a
Rust store keeps receiving its full view and the default push_all fails closed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-14 12:16:01 +02:00
commit eae25a0aa8

View file

@ -1271,7 +1271,7 @@ the behavior change.
become single calls; `_all_specific_stores` (`:304-311`) deleted;
`pull_all_backing_stores` (`:111`) iterates the distinct stores. Tests:
`test_second_generic_store_rejected`, `test_type_claimed_twice_rejected`,
`test_types_route_to_home_store`. Command: `python -m unittest discover -s tests` in `packages\cultcache-py` with `PYTHONPATH=src` (pytest is not installed; the suite is unittest). Soul, Cut 5: the siblings did not enforce C#'s "a record's home cannot change after admission" or refuse a record loaded from a store that is not its home, and TS and Rust rejected zero-store writes the contract treats as in-memory; all three are brought to the C# reference in a follow-up commit on the same branch, and Python's `add_backing_store(store, [])` becomes a generic store. A later Soul pass over the sibling series found TS and Python write paths touching the store before validation and accessors ran, TS open to async interleaving, Python accepting a global under any key, and Rust `load_soa` non-atomic. Operator decisions (2026-09-14, all option A): a TS cache serializes attach, pull, writes and registrations through one per-cache queue (its own mutations only, not observer delivery); TS and Python store a global only under `__global__`; Rust deletes the uncalled `load_envelope` and makes `load_soa` atomic with the home check; cross-runtime error wording stays as is. Every sibling write validates everything before touching a store, and a refused write changes neither store nor cache.
`test_types_route_to_home_store`. Command: `python -m unittest discover -s tests` in `packages\cultcache-py` with `PYTHONPATH=src` (pytest is not installed; the suite is unittest). Soul, Cut 5: the siblings did not enforce C#'s "a record's home cannot change after admission" or refuse a record loaded from a store that is not its home, and TS and Rust rejected zero-store writes the contract treats as in-memory; all three are brought to the C# reference in a follow-up commit on the same branch, and Python's `add_backing_store(store, [])` becomes a generic store. A later Soul pass over the sibling series found TS and Python write paths touching the store before validation and accessors ran, TS open to async interleaving, Python accepting a global under any key, and Rust `load_soa` non-atomic. Operator decisions (2026-09-14, all option A): a TS cache serializes attach, pull, writes and registrations through one per-cache queue (its own mutations only, not observer delivery); TS and Python store a global only under `__global__`; Rust deletes the uncalled `load_envelope` and makes `load_soa` atomic with the home check; cross-runtime error wording stays as is. Every sibling write validates everything before touching a store, and a refused write changes neither store nor cache. A further Soul pass (after CultLib `15c6a5d`) found the TS queue could deadlock when a store awaited the same cache and swallowed rejections of unawaited calls, Python's reentrant lock let a nested write corrupt a load or registration, a Python global stored under a legacy key made the cache unloadable after one `put_global`, and the Rust batch was all-or-nothing only for stores with an atomic `push_all`. Operator decisions (2026-09-14): (B) a global stored under a key other than `__global__` loads as a compatibility shim that never writes; the first write to that global stores it under `__global__` and deletes the legacy record in the same validated write, and the shim is removed once no store holds a legacy-key global; two globals of one type on disk are still refused. (A) Re-entrant mutation from inside a store, decoder, accessor or updater is forbidden in TS and Python with a clear error. (A) A Rust store keeps receiving its full current view on a batch, and the default `push_all` fails closed instead of wiping rows partway.
Version `0.2.0` -> `0.3.0`.
- Soul: the nine tests; `rg -n "mirror" packages\cultcache-ts\src packages\cultcache-rs\src packages\cultcache-py\src`
empty; `npm test` green including the routed-file reads.