Merge main at cultlib-unity-v2.0.0 (42dce361) into C2a: one owner per collision

Identity: C2a's StoredSchemaId/StoredVersion owns; main's PersistedSchemaId, PersistedIdentity and both MarkWritten are deleted. Stamp rule: ReadStore judges the stamp before the body. Flush over damage refuses (merge-r1-precedence). Mesh single-file writes route through SingleFileMessagePackBackingStore. TS: store-file.ts commits, C2a's header handling kept. Rust: C2a's readable_store_format in front of the grown decoder. Build not yet verified at this commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-22 20:21:31 +02:00
commit f2b3569822
210 changed files with 27332 additions and 899 deletions

View file

@ -535,10 +535,13 @@ public sealed class DirectoryMessagePackBackingStore : CacheBackingStore
}, new Dictionary<CultSchemaCatalogEntry, byte[]>());
}
// The stamp is judged before the manifest body is parsed, so a store stamped newer is refused whatever its body. Every
// write path parses the manifest under its commit lease before it writes a page or the manifest, so a store another
// process has since stamped newer is refused there too, untouched.
(CultPersistedStoreSnapshot Snapshot, byte[][] CatalogBytes, byte[][] RecordBytes) read;
try
{
read = CultDocumentMessagePackSerialization.ReadStore(manifestBytes);
read = CultDocumentMessagePackSerialization.ReadStore(manifestBytes, RequireReadableStamp);
}
catch (CultStoreUnreadableException ex) when (ex.Path == null)
{
@ -546,20 +549,24 @@ public sealed class DirectoryMessagePackBackingStore : CacheBackingStore
}
var snapshot = read.Snapshot;
if (!string.Equals(snapshot.FormatVersion, IndexedFormatVersion, StringComparison.Ordinal) &&
!string.Equals(snapshot.FormatVersion, IndexedFormatVersionWithIds, StringComparison.Ordinal))
{
throw new CultStoreUnreadableException(
$"Directory store {_manifestFile.FullName} is {CultDocumentMessagePackSerialization.DescribeHeader(snapshot.FormatVersion)}; only {IndexedFormatVersion} and {IndexedFormatVersionWithIds} are readable.",
_manifestFile.FullName);
}
var catalogBytes = new Dictionary<CultSchemaCatalogEntry, byte[]>();
for (var index = 0; index < snapshot.SchemaCatalog.Length; index++)
catalogBytes[snapshot.SchemaCatalog[index]] = read.CatalogBytes[index];
return (snapshot, catalogBytes);
}
// The one verdict on a directory manifest's stamp: this runtime reads v4 and v5.
private void RequireReadableStamp(string stamp)
{
if (!string.Equals(stamp, IndexedFormatVersion, StringComparison.Ordinal) &&
!string.Equals(stamp, IndexedFormatVersionWithIds, StringComparison.Ordinal))
{
throw new CultStoreUnreadableException(
$"Directory store {_manifestFile.FullName} is {CultDocumentMessagePackSerialization.DescribeHeader(stamp)}; only {IndexedFormatVersion} and {IndexedFormatVersionWithIds} are readable.",
_manifestFile.FullName);
}
}
private string ContentAddressedRecordPath(CultPersistedRecord metadata)
{
if (metadata.Payload == null || metadata.Payload.Length != 32)