Manual mutants survived: the record key and schema id were only asserted through the
cause chain, and an inner built from StandardResolver alone passed every test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A guard ahead of MessagePack-CSharp's union formatter decides both; the store
decode site names the record and schema.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ARefusalLeavesStagedWhatItDidNotRefuse re-staged the refused record and
expected the flush to refuse it; since b636a69f lists it foreign, that
re-staging is refused at once. The partial case now stages two records before
another writer makes one foreign.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A removal of a key the cache does not hold writes nothing, so it never reached
the apply; it was checked against the foreign list of the last load, and a
foreign record that arrived since was reported removed. Each store now reads
its durable state for such a removal (RefuseRemovalOfUndeclared) and refuses
through the apply's own RefuseUndeclaredOnDisk, which reloads and lists the
record. The load-time check (RefuseRemovalOfForeign) is deleted: the durable
read refuses everything it refused, and it could not save that read. A store
kind that cannot read its durable state keeps the view check as its default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refuse reloads a refused key as a pull loads it, and now that includes the
store's foreign list: a record another writer moved to an id this cache cannot
read is listed in ForeignRecords after the refusal, so the next write of that
key is refused at staging instead of again at the flush. Both store kinds give
Refuse the durable record's foreign entry (ForeignAt); the variant refusal too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A raw entry that lists the typed record's id compatible may replace it at the
original key, which is the only path left to the opaque-payload header rule.
Replaces RawPayloadWriteOverAMarkedFileKeepsItMarked, whose second-key case
duplicated the last step of the test above it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The persistence contract's rule 3 now says the store refuses, under its lock, a
write or removal at a key whose durable record is under an undeclared id, for
the store's own write API and for CultMesh single-file writes, which declare
through the catalog entry they write. The sentence that a declaring type stores
the record under its own id is corrected: Declares is registry-wide, so the
record lands under the writing type's id. The late-registration test is renamed
to match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A record another writer moved to an undeclared id after the load, a staged
removal of one, and one that appeared at a written key are refused at the apply
and reloaded read-only. A direct store Push, Delete and CommitBatch of a
read-only record are refused and never relabel it. Removing a foreign record is
refused typed. A CultMesh write over a record its entry does not declare is
refused, and one whose entry lists the id lands under its own id.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ForeignOnDisk and the two RefuseForeign calls in PushAll and CommitBatchCore are
gone. ApplyWriteSet refuses first, over every key it writes or removes, by
Registry.Declares for a cache and by the catalog entry it writes for a cacheless
CultMesh write. Both refusals reload through one DurableAt; the directory store
reloads through LoadPage, the per-record body of LoadRecordPages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The held-only mutant of RefuseUndeclared survived the suite, because the
apply-time refusal still caught the write at flush. This pins the staging
half for foreign records in both store kinds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The relabel tests become refusals: ARecordReadByNameIsReadOnly (name branch)
and AnOlderVersionCannotWriteANewerRecordItReadsThroughTheNewerEntry (a
persisted entry's list). New: every write path refuses before staging,
declaring the id claims the record, a type registered after load claims it,
MintElementIds is refused by a read-only record, the undeclared dropped-slot
case, and a variant of a read-only base lands. The copy test also checks the
write beside the read-only record landed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
The C# peer gains edit-in-place (payload and override through the public snapshot
API; collection and nested object through the held document), and the four-peer
test has Rust and Python read each edit.
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>
ebd66e6f and cc8604ae do not build: the re-mint named MintStoredAt without its class.
Two tests pin the staleness rule for a write that would add a variant to a plain store.
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>
_unowned with its rebuild, Owns, Publisher, WholeViewWrite, WholeView, SameStoredBytes and
WroteWholeView are deleted before the write-set apply that replaces them; PushAll and
CommitBatchCore still call them, so this commit does not build. The contract states the
write-set rule that the next commit implements.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A Starfire run showed the junction helper left its parent directory behind, which the "write went
through the link" assertion mistook for a write. The race test sees nothing-at-the-path windows on
Windows because File.Replace is not atomic to a lock-free reader; that is the store's replace and
is reported as a fork, so the test skips there with that reason.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Behavioural tests through Mesh and CultCache for each rule the cut owns, and the persistence
contract's single-readability paragraph states that one runtime has one reader, lock and replace
and that only nothing at the path is missing. Not yet run: verification follows in one job.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Deletes CultMesh's ReadSingleFileSnapshot, HasOlderCatalogLayout, ReadLegacySchemaCatalogEntry,
WriteFileAtomically and the File.Exists gate, ReadStore's entry-reader delegate, and the three tests
of the older catalog layout. This commit does not build: the Mesh read and write sites still name
the deleted reader; the next commit routes them through the CultCache single-file store.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Absent writes, a store it reads is left byte for byte, and anything the reader refuses (an unreadable file, a dangling link) is refused. The readability manifest walk now runs a soft flush over every vector, so TypeScript and Rust are pinned by the same files.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Opening a junction whose target is gone fails with UnauthorizedAccessException, not the
IOException the dangling-link rule promises. ReadDisk now turns that into the same
IOException. The dangling-link tests build a directory junction (mklink /J), which needs
no privilege, and the file-symlink cases skip where the account cannot create one.
The fixture removes links before its recursive delete.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The manifest held only three-byte and whole-store trailers, so a Rust reader that
tolerated a single trailing byte passed every suite while C#, TypeScript and Python
refused the file.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
store_header is the only reader of a store file's leading bytes. It returns the header slot (a MessagePack str only, never bin, as in C#, TS and Python) and separately the proof that the bytes are one whole array, so a newer store cut short after its header is still named unsupported. leading_header, read_array_header and read_string are deleted. decode_store_file takes the header the reader already found.
Vectors bin-header-v1/v3/v9 join the readability manifest as refuses x4. Tests: a rename fault step (WriteStep::Rename) shows a failed rename is NotReplaced, the store intact and no staging file left; every merging writer refuses through a dangling link; the unsupported-format refusal names v1 and v3 and echoes only ASCII-digit headers.
Not yet compiled or run at this commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CultMesh's single-file reader had its own zero-length-means-empty branch, so a Mesh write
replaced a zero-byte file the cache refuses (merge-r1-precedence). It now asks the cache's
snapshot reader and gets its verdict. The auto-merged test that read a same-schema instance
through MeshNoteAliasDocument cannot be built under retire-schema-alias; it now states the
ownership rule it protected (a database serves only what a shard owns) with the one type.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>