Commit graph CultLib/packages/cultcache-py
Author SHA1 Message Date
Libby
3379191b00 cultcache-py: refuse a legacy duplicate key; refuse a write that would retype a key
The legacy envelope-array decoder refuses a duplicate key as v1 does. The single-file writer
refuses (SchemaConflictError, C# Retyped wording) a put/push_all that would land a second type
under a key the store holds or the batch already names; file unchanged. Replace-by-key stays
for the same type. Retyping is remove-then-add.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-11 14:45:00 +00:00
Libby
9f869adb60 cultcache-py: refuse duplicate record keys and non-string canonical schema; writer replaces by key
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-11 14:05:55 +00:00
7c2e514941 Unique-index vector: three-holder normal-form and astral-alias rows 34-37; walk counts raised in all four runtimes (#107)
Some checks failed
CultMesh QUIC native / Build win32-x64 (push) Has been skipped
CultMesh QUIC native / Build linux-x64 (push) Has been skipped
CultMesh QUIC native / Release native binaries (push) Has been skipped
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (push) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (push) Has been cancelled
2026-11-11 13:41:10 +00:00
3deb270458 cultcache-py: a unique-index refusal names the least violation of the resulting state; walk refusals.msgpack (#101)
Some checks failed
CultMesh QUIC native / Build win32-x64 (push) Has been skipped
CultMesh QUIC native / Build linux-x64 (push) Has been skipped
CultMesh QUIC native / Release native binaries (push) Has been skipped
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (push) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (push) Has been cancelled
2026-11-11 11:48:38 +00:00
7002c252ed Element ids: the existing-member route and one-way v3/v5 rollback, documented (#98)
Some checks failed
CultMesh QUIC native / Build win32-x64 (push) Has been skipped
CultMesh QUIC native / Build linux-x64 (push) Has been skipped
CultMesh QUIC native / Release native binaries (push) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (push) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (push) Has been cancelled
2026-11-11 08:53:13 +00:00
447b310b4d cultcache-py: load order, role-alias register_index refusal, C# two-change commit vector (#96)
Some checks failed
CultMesh QUIC native / Build win32-x64 (push) Has been skipped
CultMesh QUIC native / Build linux-x64 (push) Has been skipped
CultMesh QUIC native / Release native binaries (push) Has been skipped
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (push) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (push) Has been cancelled
2026-11-11 08:05:39 +00:00
ba25b43369 cultcache-py, cultnet-py: indexes means unique, roles names selection aliases; commitId/commitCount on change messages (#94)
Some checks failed
CultMesh QUIC native / Build win32-x64 (push) Has been skipped
CultMesh QUIC native / Build linux-x64 (push) Has been skipped
CultMesh QUIC native / Release native binaries (push) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (push) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (push) Has been cancelled
2026-11-11 06:59:08 +00:00
44149afb7b cultcache-ts: opening a legacy store never rewrites it (#85)
Some checks failed
CultMesh QUIC native / Build win32-x64 (push) Has been skipped
CultMesh QUIC native / Build linux-x64 (push) Has been skipped
CultMesh QUIC native / Release native binaries (push) Has been skipped
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (push) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (push) Has been cancelled
2026-11-10 23:56:57 +00:00
Libby
00ecdcf9e6 cultcache-py: read the slot-nil vector; changelog for the store lock and msgpack
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh portability and performance / ubuntu-latest one-command multiplayer onboarding (pull_request) Has been cancelled
CultMesh portability and performance / windows-latest one-command multiplayer onboarding (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
CultMesh portability and performance / windows-latest reactive semantics and leases (pull_request) Has been cancelled
CultNet cross-runtime interop / C# / TypeScript / Rust / Python / Kotlin (pull_request) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
tests/vectors/slot-nil was read by TS only. The Python reader accepts the same 15 rows
and refuses the same 2 as the C# reference and rewrites the reference's bytes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 20:50:10 +00:00
Libby
b2434fc7fc cultcache-py: single-file store takes the cross-process lock, stages uniquely and fsyncs
push, push_all and delete hold <path>.lock (flock; LockFileEx over the whole file on
Windows, as cultcache-ts store-file.ts) across the read and the replace. The staged file
is <path>.tmp-<pid>-<uuid>, fsynced before os.replace, with a directory fsync after off
Windows; a failed write removes its staged file and leaves the store.

Tests fail when the rule breaks: a write waits while a flock holder (Rust's lock) and the
TypeScript runtime's withStoreWriteLock hold the lock, three processes lose no writes,
fsync precedes replace, staged names differ, a failed replace leaves no staged file.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 20:49:32 +00:00
Libby
9548d51a71 cultcache-py: msgpack is a hard dependency; drop the optional-import fallbacks
pyproject (and cultnet-py) already declare msgpack as required; the code treated it
as optional behind a cultcache-py[msgpack] extra that does not exist.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 20:47:07 +00:00
Libby
533b492af2 docs/start: tested Rust and Python CultCache first calls
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh portability and performance / ubuntu-latest one-command multiplayer onboarding (pull_request) Has been cancelled
CultMesh portability and performance / windows-latest one-command multiplayer onboarding (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
CultMesh portability and performance / windows-latest reactive semantics and leases (pull_request) Has been cancelled
CultNet cross-runtime interop / C# / TypeScript / Rust / Python / Kotlin (pull_request) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
Each test file carries the snippet between markers; the docs show it verbatim and a second test in the file fails if the README block drifts. Matrix cells for Rust and Python CultCache now link them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-04 20:20:33 +00:00
Meta Vens
8b780a27bc cultcache-py CHANGELOG: registration refusals are SchemaConflictError, one owner per schema id, records resolve by schema id first
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:17:32 +02:00
Meta Vens
388e2eaa25 CHANGELOGs: every public break since cultlib-unity-v2.0.0, the unity package releases as 3.0.0
Owns finding c2a-public-breaks-not-in-changelog. Leaf changelogs gain v3 read and write and element ids carried; cultcache-rs gets its first changelog (the whole-store writer fix is not on main, so no 0.3.1 section).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 23:57:37 +02:00
Meta Vens
2c9043357e cultnet-ts: chunk references carry the element id (slot 4), as the reference reads and writes it; changelogs for the flush-over-damage refusal
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 21:55:15 +02:00
Meta Vens
6afa8ed9c4 cultcache-py, -rs and -ts: unit-arm tests assert against the bytes the C# reference wrote
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 20:33:24 +02:00
Meta Vens
c05c7919d0 cultcache-ts and cultcache-py tests: golden models carry element ids; python-written store asserts them
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 19:44:28 +02:00
Meta Vens
e0747912c6 cultcache-rs and cultcache-py union parity tests: golden models carry element ids
Wrap and the Outcome arms gain id slots (unit arms become one-slot id records);
the Python unit-arm codec behaviour is kept on a separate unit union.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 19:43:28 +02:00
Meta Vens
3b83cb6c08 Merge origin/main into hands/variants-c2a-main (main-into-c2a)
Sixteen conflicted files resolved by the spec's rule: main owns payload
ownership, bounds and the Clock seam; C2a owns persistence semantics and exact
binding. MintStoredAt is internal static (previous, now); the cache and the
store pass their cache's Clock. Package versions are main's. The cultmesh-ts
payload-parse acceptance and its test are deleted under retire-schema-alias;
peer snapshot reads take the schema's own id, else an id the definition lists
as compatible. The held firstRecordAtKey fallback is removed: main's handles
take definitions only, so nothing could reach it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 04:06:41 +02:00
Meta Vens
10521f3598 cultcache-py test: the any-row comment cites the ruling in force, any-rewrite-binds-kind-and-value
Comment only. Soul finding cut-py-typed-leaves.s2.stale-superseded-ruling-comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:28:26 +02:00
Meta Vens
100088520e cultcache-py test: the any-row comparison tells bool, int and float apart; README points at the kind-and-value ruling
Ruling any-rewrite-binds-kind-and-value: kind-strict, exact value, recursive, numeric width free.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 02:01:47 +02:00
Meta Vens
eb01c414a6 cultcache-py: one number path for the float leaves (no write/read flag); tests pin the refusal texts
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 01:24:35 +02:00
Meta Vens
ed30592ea7 cultcache-py: an extension is reported as an extension (a namedtuple was read as an array); boundary tests for the leaves
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 01:17:28 +02:00
Meta Vens
8ebe43fe7a cultcache-py test: the leaf-record mirror is one function
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 01:07:52 +02:00
Meta Vens
58ee315d11 cultcache-py: typed leaves own each slot's C# type name, write check and read check; the catalog derives from them
leaves.py (string, boolean, bytes, int32, int64, float32, float64, any, list, array, nullable, lazy) held to the
numeric-leaves vectors. Removed: DatabaseEntryField.many, callable codecs and _resolve, the System.Object literal,
the present-nil default, the union's nil pass-through, interop.py's hand-written catalog. A float32 keeps its width
through _pack_tree. Nil is refused unless wrapped in nullable; a default fills only a slot past the payload's end.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 01:06:24 +02:00
Meta Vens
7f6649f9a1 cultcache-py: the payload depth count takes every mapping, sequence and set; a codec's RecursionError is no longer relabelled as a depth refusal
The tree walk counted only list, tuple and dict, so a codec whose tree used deque,
mappingproxy or another abc container was unbounded. Every Mapping, Sequence and Set
that is not text or bytes now counts. RecursionError from a user encode or decode,
and from the JSON decoder, reaches the caller as itself with its traceback; only
msgpack's StackError (raised by depth alone) is mapped to the depth refusal.
All commits build.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 17:47:55 +02:00
Meta Vens
4328ddb40e cultcache-py: a scalar payload of any type has no depth (integer and nil roots pinned)
Every commit builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:24:22 +02:00
Meta Vens
99b0ff4e1a cultcache-py: depth walk starts only from a container root; refusals of a stack-exhausting codec and the validator's arguments pinned
Removes the equivalent continue in the walk (the stack only ever holds containers).
Pins the exact refusal text on the RecursionError and StackError paths, both sides,
and that the validator receives the written and the decoded value. Every commit builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:20:35 +02:00
Meta Vens
5d4753e2f0 cultcache-py: slot keys capped at 65535 at definition; a short record reads with nil as the reference does; bool arm key pinned
DatabaseEntryField validates its key in one place (0..MAX_SLOT_KEY, no bool), so
database_entry_field, direct construction and field tuples share the rule. A record
shorter than its declared slots no longer refuses a missing slot: the slot is nil
and its default applies, as the C# reference reads it. define_union refuses True
and False as arm keys, each pinned alone. Every commit builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:12:24 +02:00
Meta Vens
44da432e96 cultcache-py: one 256 nesting bound on read and write in DocumentDefinition, and the arm-key and nil-slot refusals, held to the C# bounds vectors
Iterative container count over the decoded and the encoded tree (MAX_PAYLOAD_DEPTH,
CultCachePayloadDepthError); msgpack StackError and RecursionError map to the same
refusal. Union decode names an out-of-Int32, non-integer or nil-slots arm as the
reference does. Fixture arms Wrap, Floor, Ceiling; loop over bounds-outcomes.msgpack.
Pins the bool arm-key refusal's callers in the vector loop only; every commit builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:12:17 +02:00
Meta Vens
36aa1a44bf cultcache-py: a directly constructed DatabaseEntryField requires its default; test corrected
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:55:55 +02:00
Meta Vens
7cbe8049ff cultcache-py: pin the defaults of a directly constructed DatabaseEntryField
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:54:36 +02:00
Meta Vens
eae7207df5 cultcache-py: the record decode applies a slot default in one place; plain field helper, SlotCodec and field immutability pinned
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:47:52 +02:00
Meta Vens
ff1138ca4c cultcache-py: union arm keys bounded to Int32 at definition; Missing slot typed Node; nil list elements, unit-arm extra slots and UnionArm frozen-ness pinned
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:40:33 +02:00
Meta Vens
27e3f0f721 cultcache-py: pin the union and record refusal messages and the record defaults, member catalog
Tests only; the code is unchanged. All commits build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 04:11:36 +02:00
Meta Vens
91c8352ec5 cultcache-py: union codec and nested record codec, held to the C# union-parity golden
define_union and define_record_codec share the slot logic of define_database_entry_type
(one record codec); a field takes a codec, many, or a thunk for recursion. Unknown arm
keys are refused naming union and key, never the payload. All commits build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 03:49:47 +02:00
Meta Vens
7b815e1ca8 TS and Python: pin the refusal text (v3 named, ASCII digits only); TS comments say a zero-byte file is refused
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:05:38 +02:00
Meta Vens
e5f06a2a49 Python: fix the payload byte literal in the refusal-text test (the previous commit did not parse)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:04:16 +02:00
Meta Vens
0bd1bac963 Python: pin what the unreadable-format and extra-slot refusals say and what the runtime reads
Mutation survivors on the merge's refusal text (the two readable formats, the
unrecognised-header description, the record-slot refusal) had no test pinning them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:02:15 +02:00
Meta Vens
bad511f5ac Merge CultCache R1 (f789c8d1) into C2a: one reader per runtime, R1's failure taxonomy
R1's rules win where both sides decided the same question: a zero-byte file is
not a store (refused as unreadable in every runtime), only nothing at the path is
empty, a dangling link is an I/O error, refusals never echo stored values and echo
a header only as cultcache.store.v<digits>, writes report Rejected, NotReplaced
and ReplacedNotDurable. C2a's rules win on what it added: the v3 element-id
header, variants, whole-file proof, typed unreadable errors in C#, TypeScript and
Python. Rust keeps R1's CultCacheStoreUnreadable{path, kind}; C2a's
StoreUnreadableError is deleted. The readability manifest's zero-byte row is
refuses x4.

Not yet built or tested: verification follows in the next commits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:41:35 +02:00
Meta Vens
549b745ee6 cultcache-py: a record refusal names only a string key; pin link writes for R3
The five-slot refusal echoed a non-string key or schema id with repr, quoting
stored bytes. A string is named; anything else is described by its type.

A push through a live link replaces the link with a regular file. A test
named for R3 pins that until R3 decides whether writes resolve or refuse
links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 19:54:51 +02:00
Meta Vens
53f6b54e15 cultcache-py: refusals echo a header only in its known shape; a dangling link is not absent
An unsupported header is echoed only as cultcache.store.v<digits>; any other
is described by its byte length, since the bytes are the store's.

A read that found nothing through a dangling symbolic link returned an empty
store, and the writer then replaced the link. Both stores now share one read
rule: only nothing at the path is absent. JsonLinesBackingStore also drops
`Path.exists()`, which reported a symlink loop or a file-as-parent as absent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 18:35:19 +02:00
Meta Vens
202c4bbd63 cultcache-py: only a missing store reads as empty; an empty file is refused
`Path.exists()` returns False for ELOOP and ENOTDIR, so a symlink loop or a
file where the parent directory should be read as an empty store. The read
now catches FileNotFoundError only. An existing zero-byte file is refused
with ValueError, like any other undecodable store, matching the Rust and C#
runtimes. `[0x90]` stays an empty legacy store.

Refusal text no longer points at a variant-aware runtime: an unsupported
header names the format found, and a v1 record with a fifth slot says the
file is not a valid store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 17:04:39 +02:00
Meta Vens
84246b685a Python: a document listing an id registers beside the one owning it; registration refuses typed
Owner beats lister, as in C#. A document owns its schema id; others may list it
as compatible beside its owner, in either registration order (batch 9 refused
any overlap, which forbade keeping v1 beside a v2 that declares v1's id; Soul
F8). A record resolves to the document owning its id, else to the one document
listing it. Several listers and no owner name no single document, and the load
is refused with SchemaConflictError naming the id, the record key and the
listers: refusing at registration would depend on whether the owner had
registered yet.

Every registration refusal is now SchemaConflictError (Soul F10): a second owner
of one schema id, a second document with one schema name, and a second
definition of one document type. Each names the new document's id and both
schema names and document types. Before, the last two raised CultCacheError.

Tests: an owner and a lister register in both orders and each reads its own
records, and a lister alone reads the owner's id; two listers register in both
orders, a record under their id is refused typed without its owner and is the
owner's with it, registered before or after them; two owners, one schema name
and one document type twice are each refused as a schema conflict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:31:25 +02:00
Meta Vens
abb922cd57 Python: one registered document carries each schema id; a second claimant is refused typed
register_document_type refuses a document whose own or compatible ids overlap
another registered document's, raising SchemaConflictError (empty record key,
both schema names, a message naming both document types), as TypeScript does.
Before, the record resolved to whichever document the dict held first (Soul F4).
With one carrier per id, resolution by id is a single pass. SchemaConflictError
takes an optional message for this use.

The test that pinned owner-over-lister resolution inside one cache becomes a
refusal test in both orders; shared compatible ids and a shared own id (neither
declaring its own) are refused in both orders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:35:52 +02:00
Meta Vens
5926804e2b Python test: a document that owns an id is resolved before one registered earlier that lists it
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 11:14:38 +02:00
Meta Vens
ebe15ab8c9 Python: a record resolves to its document by schema id, and the type check runs across tiers
A record now resolves to the document that owns its schema id, else one that lists it as
compatible, before the catalog name is tried; a schema renamed under a stable id opens, and an id
that names one document is not overruled by a name that names another. The name resolves a record
only when no local document has the id.

The writer refuses a write that would change which type an existing record resolves to, unless the
entries of both the existing records and the written ones own the id (a rename, the registered
descriptor wins).

Tests pin the arrived-overwrites-registered guard in both id orders, the content-hash tie-break
against the compatible-list order, sorted id processing, the cross-tier refusal and the rename, and
a cache whose document does not list the id a loaded record sits under.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 11:04:07 +02:00
Meta Vens
acbf031c5b Tests: the arrived entries' hashes sort before the registered one's, so a writer that ignores the tiers picks the arrived one
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 10:16:53 +02:00
Meta Vens
ca0b62023d Writers: drop the same-tier dedupe refusal (unreachable), and pin tie order and the registered lister
Two entries of one tier that share an own id are refused when the id is carried, so a refusal at dedupe could never fire.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 10:14:33 +02:00
Meta Vens
7a5b64ad88 TypeScript and Python writers: records are compared for type within a tier only
A read-back record's type is its schema's name and a supplied one's is the document type, so records of different tiers are never
compared; two supplied (or two arrived) records of different types under one id still refuse the write.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 10:13:44 +02:00