Single-file readers refuse duplicate keys in all four runtimes; c0 bad-canonical row #110

Merged
libby merged 20 commits from reader-dup-keys into main 2026-11-11 15:07:49 +00:00
Member

Closes variants:follow_up:reader-refuses-duplicate-keys and variants:follow_up:c0-bad-canonical-in-manifest.

Every runtime's single-file reader now refuses a store with two records under one key (ordinal string equality, schema not part of identity) as its unreadable-store error, instead of opening it last-wins and failing later writes raw. Five readability vectors (3 refuse, 2 distinct-key read: case, NFC/NFD) and a c0 manifest row (current-catalog-bad-canonical.msgpack, refuse x4; TS and Python no longer coerce a non-string catalog slot 4) are walked by all four runtimes plus the Mesh C# walk.

Writers: TS, Python and Rust writers replaced an arrived record only on (type, key), so they could write a store the new reader refuses; they now replace by key alone. Residual, not in this PR: TS/Py/Rs in-memory caches are still keyed by type+key (see leaf-key-identity-parity), so one batch carrying two types under one key can still write a duplicate-key file in TS and Python (Rust keeps the last).

Cut reports: cut-reader-dup-keys-{vectors,csharp,rust,ts,python}.h1, cut-c0-bad-canonical-row.h1/.h2.

🤖 Generated with Claude Code

Closes variants:follow_up:reader-refuses-duplicate-keys and variants:follow_up:c0-bad-canonical-in-manifest. Every runtime's single-file reader now refuses a store with two records under one key (ordinal string equality, schema not part of identity) as its unreadable-store error, instead of opening it last-wins and failing later writes raw. Five readability vectors (3 refuse, 2 distinct-key read: case, NFC/NFD) and a c0 manifest row (`current-catalog-bad-canonical.msgpack`, refuse x4; TS and Python no longer coerce a non-string catalog slot 4) are walked by all four runtimes plus the Mesh C# walk. Writers: TS, Python and Rust writers replaced an arrived record only on (type, key), so they could write a store the new reader refuses; they now replace by key alone. Residual, not in this PR: TS/Py/Rs in-memory caches are still keyed by type+key (see leaf-key-identity-parity), so one batch carrying two types under one key can still write a duplicate-key file in TS and Python (Rust keeps the last). Cut reports: cut-reader-dup-keys-{vectors,csharp,rust,ts,python}.h1, cut-c0-bad-canonical-row.h1/.h2. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Three dup-key vectors (same schema, other schema, different payload) refuse in all
four runtimes; case and NFC/NFD distinct-key vectors read. Runtimes not yet cut: the
dup rows are red until the runtime cuts land.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The reader owns the verdict; the writer replaces an arrived record by key alone so it cannot emit a store the reader refuses. Store-file tests no longer pin one key under two types.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
decode_store_snapshot refuses a repeated key (Undecodable). SingleFile push and push_all
replaced by (type,key), so a write of another type to an existing key left two records
under one key, which the reader now refuses; they now replace by key alone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/hands/reader-dup-keys-python' into reader-dup-keys
Some checks failed
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
0378b9160a
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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>
A single-file store holds one record per key. push and push_all now refuse (SchemaConflictError) a write that would put another type under a key, leaving file and cache unchanged, within one batch too; the legacy envelope-array decoder refuses a duplicate key like v1. Retyping is remove-then-add.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/hands/reader-dup-keys-fix-rs' into reader-dup-keys
Some checks failed
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
fa5903facf
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
write_all_unlocked now refuses any snapshot holding a key twice (SchemaConflictError for two types), so insert_entry_if_absent, the compare-and-swap family, compare_exchange and the snapshot exchanges cannot bypass it; push and push_all lose their private copies.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/hands/reader-dup-keys-fix-rs' into reader-dup-keys
All checks were successful
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Successful in 25m31s
7ecb887d47
libby merged commit 4246a37284 into main 2026-11-11 15:07:49 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
GameCult/CultLib!110
No description provided.