The store chooses carried catalog slices by the entry object the read
produced. Keyed by schema id instead, a type whose id is unchanged but
whose entry changed (a compatible id added) would have the file's old
entry written over its own. The test stages a WsItem over a file whose
ItemId entry carries an unread slot and asserts the writer's encoding
lands, in both store kinds.
Closes variants:finding:cut-c2a-content-version.s3.catalog-writer-entry-unpinned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pins that a carried catalog entry keeps the store's slice, in the single-file and the directory store.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Delete RawBytes from CultPersistedRecord and CultSchemaCatalogEntry, with every
drop-on-assign hook and the catalog's content guard. A decoded record or snapshot
carries no stored bytes, so an in-place edit of its payload or overrides can never
be shadowed by cached bytes, and the public snapshot and record serializers encode
every record from its fields.
SingleFileBackingStore now reads through ReadStore, which returns the snapshot and,
parallel to it, the exact slice the file holds for each catalog entry and record. Its
durable index keeps, by record key, that slice and its SHA-256 (hashed when a
comparison asks), which is the record's version. A write re-encodes every record
the writer staged and copies every other record, and every catalog entry it keeps,
as the slice the file holds. The directory store keeps its manifest's catalog slices
the same way. SerializeSnapshot/DeserializeSnapshot hooks become
SerializeStore/ReadStore plus SerializeCatalogEntry.
A staged but unchanged non-canonical record is now re-encoded canonically and gets
a new version; an untouched one keeps its bytes and version.
Tests: in-place edits through the snapshot API (payload, override) and through a
held document (collection, nested object) are written as edited; an untouched
non-canonical record survives a neighbour's write byte for byte; a staged one is
re-encoded. The cost test counts record hashes.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The previous commit does not build: the widening helper read a byte string with an overload the test project cannot bind.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The foreign-record header test expected a marked header to drop while a record was copied
forward, which the header rule no longer does. A directory store loads a record from its
page, not the manifest, so its skew test makes both writers hold the record ahead instead of
editing the manifest.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A record's bytes cannot say whether it holds an element id (an id is an ordinary keyed
string), so the per-record fact decoded by whichever type loaded the record was a split
authority: a narrower type unmarked a v3 store over bytes holding ids. HeaderForWrite is now
a pure function of the durable header, the HoldsIds of the records the write encodes, and
whether it copies any record forward: a write that copies keeps a marked header, a write
that encodes every record decides by content. The directory store takes the same rule.
Also pins the three Low test findings deferred from c2a-write-set s3: the reload cause is
observed by type and message, the author's own Expect after a commit, and the directory
store's re-mint branches.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Ported from Soul's s2 probes: P5 skewed clock (equal storedAt re-mint, mint from the durable one),
D (directory store re-mint, as a store-kind parameter on the later-storedAt test), E (a disappeared
base refuses a variant write), P3 (foreign refusal whose reload fails carries the cause). New: a
foreign record a refusal reloaded is not seen as moved, and a writer that finds the file gone has
read nothing. ExpectOnAVariant is restored to pin that Expect tests only the stored delta: a stale
base reaches the write conflict, not a mismatch. Per-commit build not verified.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The merge tests become the refuse, reload, retry rule; new tests pin copied records that
cannot be decoded, the later storedAt over a replaced record, a batch that removes a staged
key, and a Mesh write into a variant store. The persistence and composition contracts state
the rule for the parity cuts.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
CULTLIB_REAL_STORE names a copy of a real store: one staged write leaves every other record and
catalog entry byte for byte. New: a copied record changed after the load keeps a marked header; what
this cache wrote counts as known id-free; a batch wins over a staged write of its key; a write is
judged on the staged edits plus the variants another writer left in the file.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The unconditional-commit last-writer-wins test now keeps another writer's record; the load-minted ids
stay for MintElementIds after an unconditional write; a detached store marks the variant it writes, not
the one it copies; a variant write supplies the indexed override; the conditional/unconditional
comparison uses one batch (a conditional commit refuses staged writes by design).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
New WriteSetTests (single-file and directory stores, flush and commit): another writer's write and
removal survive, a store that appeared after open is kept, an unstaged in-place change is not
written, a copied record keeps the catalog entry the store holds now, an entry's unknown trailing
fields survive byte for byte, conditional and unconditional commits write the same bytes, the
header drops only when every copied record is known id-free, v2 exactly with a variant. Mesh: a
write copies neighbours forward and replaces only its key. The lay-back and whole-view tests and
the flush-restamp tests are rewritten or removed with the rules they pinned.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>