Commit graph CultLib/src/GameCult.Caching
Author SHA1 Message Date
Meta Vens
19f3a5edbb Cacheless single-file API: WriteDocument encodes the declared type, the registry is the call's argument, DecodeDocument moves to CultCodec, ReadStoreSnapshot's gate is pinned
SerializePayload takes the declared type. A call attaches its registry under the store's gate and restores the previous one, so no call leaves a registry on the store. Not yet run; the next commit set is verified on Yggdrasil.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 01:38:16 +02:00
Meta Vens
bf01212f9a Docs: cacheless single-file access names WriteDocument and DecodeDocument; CultElementIds is not public
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 00:15:28 +02:00
Meta Vens
729754278a Caching: SingleFileBackingStore.WriteDocument and DecodeDocument own element-id planning; CultElementIds is internal again
Mesh's typed single-file read and write call the store's typed members instead of planning ids through a public CultElementIds. Builds.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 00:15:27 +02:00
Meta Vens
bc6d524bdf Contracts: cacheless single-file access; never reuse a dropped slot
Store-composition gains the Cacheless single-file access section; schema-compatibility states the slot-reuse consequence per variants:ruling:slot-reuse-documented. Owns follow_up slot-reuse-contract-doc.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 23:57:38 +02:00
Meta Vens
73ffa6f8d0 Caching: CultElementIds.Plan, Holds and IdPlan are public, the third internal CultMesh reached
CultMeshSingleFileDocuments mints, checks and counts element ids through CultElementIds, which the deleted GameCult.Mesh InternalsVisibleTo also granted; without the grant Mesh does not build. A cacheless typed writer has to plan ids before it serializes, so the three members it needs are public and nothing else of the class is.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 23:54:54 +02:00
Meta Vens
26bf0986d0 Caching: public ReadStoreSnapshot and WriteRecord replace Mesh's InternalsVisibleTo reach
SingleFileBackingStore gains the cacheless single-file read and write CultMesh used through internals; CultMeshSingleFileDocuments is now a caller of the public API only, and the GameCult.Mesh InternalsVisibleTo grant (with the duplicate Tests/MessagePack grants) is deleted. Owns finding mesh-ivt-moved-not-removed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 23:52:33 +02:00
Meta Vens
5baa02fa54 Merge origin/main (CultLib 2.0.1) into the C2a landing branch
CHANGELOG: C2a's Unreleased above main's [2.0.1], verbatim.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 23:46:31 +02:00
Meta Vens
972e7b2ed4 Caching: a reference gained on an otherwise identical slot is compatible drift (1.0.x ref-list stores open); 2.0.1
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 22:16:14 +02:00
Meta Vens
f2b3569822 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>
2026-10-22 20:35:33 +02:00
Meta Vens
4a14f17270 Public DeserializeSnapshot parses any stamp as in 1.0.61; stores judge the v1/v2 stamp in their own read paths; flush prose states the exact rule; the I/O refusal test asserts no write was reached
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 18:34:15 +02:00
Meta Vens
39e8ab5efd Single-file and directory snapshot reads judge the format stamp before parsing the body; a flush heals an empty or truncated file
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 17:30:57 +02:00
Meta Vens
0bf11ddc94 Caching: SingleFileBackingStore.PushAll owns the flush guard; it reads the disk as attach and commit do, ReadFormatVersion is deleted
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 17:00:10 +02:00
Meta Vens
afd4858b9a Caching: qualify FormatNumber at the single-file flush guard
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 16:43:37 +02:00
Meta Vens
d44188637d Caching: a plain flush and a directory write refuse a store another process stamped newer since attach
Single-file PushAll reads the format stamp under the lock and refuses cultcache.store.v<N> with N > 2;
the directory store throws NotSupportedException for N > 4 from the manifest parse every write already runs.
An unreadable stamp keeps today's behaviour.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 16:43:11 +02:00
Meta Vens
8b47a2e8d9 Caching: PersistedSchemaId and MarkWritten are internal for 2.0; Mesh single-file write guard in the CHANGELOG
C2a's StoredVersion owns the same identity and deletes both. No consumer outside CultLib uses them,
so they do not ship public in 2.0.0 and C2a's release is not a major.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 15:57:01 +02:00
Meta Vens
4a1c2df698 Store-composition contract: MarkWritten guards accidental marks; a mark is the store's own claim
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 22:10:26 +02:00
Meta Vens
22eaaabeb2 Caching docs: MarkWritten refuses an entry the store does not hold (contract, CHANGELOG)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:59:38 +02:00
Meta Vens
afd799ba8d Caching: CacheBackingStore.MarkWritten is an instance method that refuses an entry the store does not hold
A protected static mark let any subclass mark another store's or cache's
entry. The instance form throws ArgumentException unless the entry is the
one this store holds in its own Entries at that key. Both library stores
already mark only their own entries, so their calls are unchanged.

Finding cultlib-gaps:finding:cut-expect-persisted-schema.s2.markwritten-unscoped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:59:38 +02:00
Meta Vens
73d438dbcf CultCache: every backing store marks the records it rewrites, through a protected CacheBackingStore.MarkWritten
The directory store's write of a generation marks each page it wrote once the
manifest names it, so a Push+PushAll of a loaded drifted entry no longer leaves
its persisted schema id stale. The single-file store marks through the same
member, and a store outside GameCult.Caching now has a supported way to mark
its own rewrite. The getter's doc names how a store records a rewrite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:41:53 +02:00
Meta Vens
c681af39d9 store-composition contract: identity is the persisted schema id plus storedAt
The claim that every write mints a later storedAt is false for a whole-file write, which keeps storedAt and rewrites a drifted record under the writer's schema. State what the identity is and what a rewrite does to another cache's Expect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:05:29 +02:00
Meta Vens
3f6dda8b36 CultCache: conditional commit compares the schema id a record is persisted under
The six reads that took Descriptor.SchemaId as a record's durable identity (ConditionsHold, Expect, the store-less commit, both pull filters, JudgeMerge) now read CultStoredDocument.PersistedSchemaId. The store sets it from the record it read, a stamped entry starts at its descriptor's id, and a completed whole-file write (single-file PushAll, whole-view CommitBatchCore) resets it under the hold. A variant and its resolved view share it. An unchanged record loaded under compatible drift now satisfies Expect and ExpectUnchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:05:23 +02:00
Meta Vens
ed432b59da cultcache-ts 0.14.2: version, CHANGELOG, README persistence model, store-composition contract
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:54:14 +02:00
Meta Vens
0c797ec3f7 Element ids: string keys read from the members MessagePack writes; AllowPrivate, inherited-formatter and unwritten-string-key pins; README states the whole-value exception
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 02:16:32 +02:00
Meta Vens
19963293ae Contracts: whole-value shapes, closed list of owned shapes, door only verifies
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 00:54:39 +02:00
Meta Vens
19f475ca92 Element ids part 2: one recogniser; shapes the walk does not own are whole values
Delete Hidden, HiddenProblem, UnreadKeyedMembers/UnreadMembers, Settle's
[MessagePackFormatter] refusals and the door's written-as-object branch.
Classify gains Leaf for attribute-formatted, [DataContract] and string-keyed
types; MembersOf honours the most derived AllowPrivate; the door refuses a
null StandardResolver formatter with the throwing path's text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 00:54:37 +02:00
Meta Vens
57fdf70c21 Contracts: the codec door refuses only a proven family mismatch
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 20:52:37 +02:00
Meta Vens
48d3f667e6 Element ids: a keyed map counts as enumerating objects; family check tests assert the exact refusal
EnumeratesObjects took ElementTypeOf first, which for a dictionary is the
KeyValuePair (a leaf), so a keyed Dictionary subclass never reached the path; the
map's value type is asked first. The unreachable null-standard guard in
WrittenAsWalked is removed. Tests pin the direction and wording of the refusal, the
attribute-formatter collection case and the unwrapped inner exception.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 03:09:39 +02:00
Meta Vens
744f2f78b1 Contracts: the written-member rule, readable ids and the codec's family check
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 02:58:10 +02:00
Meta Vens
5157c647af Codec: confirm MessagePack writes every id-path type the way the walk reads it
CultElementIds is the walk's model of a type; MessagePack's StandardResolver owns how
a type is written, and nothing checked that the two agree. ResolverClaim now asks,
for each id-path type, whether StandardResolver's formatter is the object or union
resolver's (a type read by key) or a collection resolver's (a collection), and refuses
the document at first encode or decode when it is not, naming the type. A keyed type
that enumerates objects goes on the path so a keyed List subclass is caught. A type
MessagePack cannot build a formatter for, and a resolver ahead that throws, are typed
refusals; the AllowPrivate catch is pinned (s4: keyed-enumerable-misclassified,
allowprivate-catch-unpinned).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 02:53:28 +02:00
Meta Vens
ee3e262064 CultElementIds: the walk reads the members MessagePack writes, and an id it cannot read back is refused
MembersOf is now MessagePack's StandardResolver write set: public fields and
properties with a public getter, none of them [IgnoreMember]. UnreadKeyedMembers
(renamed from NonPublicKeyedMembers) is every other [Key] member, including a
public property whose getter is private. IdProblem takes id candidates from every
instance member up the base chain, refuses an id MessagePack does not write, and
refuses a property id with a non-public setter, which MessagePack writes but does
not read back (s4: walk-member-set-differs, private-setter-id-dropped-on-read).
An oracle test pins the member set to MessagePack's ObjectSerializationInfo.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 02:51:24 +02:00
Meta Vens
9fd5ae1f09 CultElementIds: a keyed type is read by key before it is read as a sequence
Classify tested IEnumerable<T> before [MessagePackObject], so a keyed class that
enumerates itself recursed until the stack overflowed during registration (s4,
keyed-enumerable-misclassified), and a keyed class that merely enumerates had its
keyed lists neither judged nor minted. Keyed first: MessagePack writes both by key.
VisitMemberType also stops at a type already on its own through chain, which ends
an unkeyed Node : IEnumerable<Node>.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 02:49:18 +02:00
Meta Vens
70ffa2af9a Element ids: Classify keeps only the string special case; tests pin nullable structs, unkeyed structs, inheritance and an AllowPrivate formatter that cannot be built
Stryker found the primitive, enum and object tests in Classify redundant with the value-type and unkeyed
branches, so they are gone. The new tests kill the survivors that were behaviour: a nullable keyed struct
walked as its struct, an unannotated struct treated as a value, [MessagePackObject] reaching a subclass,
and a claim on a type StandardResolverAllowPrivate cannot format.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 01:32:25 +02:00
Meta Vens
8bbe56f555 Contracts: the id walk follows declared types; keyed structs, every map interface and AllowPrivate named
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 01:24:35 +02:00
Meta Vens
143e4f7db2 Element ids: a non-public [Key] member on the way to an object list is refused at registration
MessagePack writes a non-public [Key] member only under AllowPrivate, and the element-id walk reads
public members, so the ids such a list holds could never be checked. Graph entries carry Hidden; a hidden
list or a hidden edge whose child reaches adds one problem and nothing to the id path.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 01:22:47 +02:00
Meta Vens
1e7b3b25da Element ids: one Classify of the declared type; registration, Plan and Holds walk declared types
IsLeaf, IsObjectType and DictionaryValueType are deleted. Classify(declared) says Leaf, Map
(IDictionary<,> or IReadOnlyDictionary<,>), Sequence or Owner; a keyed struct is walked as an owner and
is never an element. Plan and Holds take the declared root type and follow declared member types, so a
member declared object or a subclass's extra members under a base-typed member are neither minted into
nor read. Minted path text is unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 01:21:22 +02:00
Meta Vens
b0cc36bc7e ResolverClaim: a resolver returning MessagePack's standard formatter for an id-path type claims nothing
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 00:26:53 +02:00
Meta Vens
73b4b47451 CultElementIds: collection types join the id path, member attributes read through overrides, each unkeyed element type refused once
Graph edges and lists carry the collection types between member and child; Settle adds them to the path so
[MessagePackFormatter] on a collection class and a resolver claim on List<Elem> are judged. Declares replaces the four
MemberInfo.IsDefined member reads, which ignore inherit for properties. Survey.Judged dedupes the unkeyed-element refusal.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 00:26:52 +02:00
Meta Vens
328e9b37e1 HeaderForWrite comment: only a reader with the record's type can tell from its bytes whether it holds an element id
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 00:23:25 +02:00
Meta Vens
fa90c6cc1e CopiedHoldIds: the copied.Length == 0 early-out is deleted; an empty copy runs the loop zero times
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 00:23:25 +02:00
Meta Vens
fb065f0de9 CultCache: a write decides from the copied records' bytes whether an unmarked store holds an element id; the per-instance read map is deleted
_readHoldingIds and the pull's Holds walk are removed in the same commit that adds
SingleFileBackingStore.CopiedHoldIds: under the store's lock, while the header on disk is
unmarked, every plain copied record the writer's registry claims and whose type reaches an
object list is decoded, and the first holding an id marks the store. Positive-only, never
refuses a write. CultMesh's typed write attaches the registry its payload was encoded with;
the raw write attaches none. Contract text states the rule and its residual.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 23:35:31 +02:00
Meta Vens
e5736e49f3 CultCache: a write that copies a record it read holding an element id marks the store
The single-file store records, per pull, which plain records decoded holding a non-empty
element id and at what version. A write whose copied record is still at that version
marks the header (v3). The record is positive-only and version-gated, so nothing a load
records can unmark a store. The directory store passes false: only C# writes it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 21:52:36 +02:00
Meta Vens
b091d21f81 CultCache: list elements are written by their [Key] layout; registration and the codec refuse otherwise
Registration: a concrete element type of an object list that is not keyed, and
[MessagePackFormatter] on any type or member on the id path or on the id member, are
refused. The id path is a fixed point over the walk's edges. The codec refuses, once per
document type and on every encode and decode path, a resolver ahead of StandardResolver
that formats a type on that path. UnionArmGuardTests moves StubArm out of the list union.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 21:52:36 +02:00
Meta Vens
e3f497197d Caching: README section on element ids; the hex refusal names the derived form for readable ids
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 20:31:44 +02:00
Meta Vens
c1962181a2 Merge remote-tracking branch 'origin/hands/variants-c2a' into hands/variants-c2a-main 2026-10-14 04:38:07 +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
7be966b485 Contracts: every writable store judges a batch's checks; a store kind lists foreign records at the version it read
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 02:07:21 +02:00
Meta Vens
1893410fe1 Every writable store judges a batch's unheld removals under its own lock; a foreign record's version comes through Foreign
CacheBackingStore.JudgeChecks (default: the view; single-file and directory stores read under their lock) replaces Land's view-only loop over non-home stores. Foreign takes the version the store read; CultForeignRecord.StoredVersion is get-only and set by the ctor, and every assignment after Foreign is deleted. One commit carries both spec commits 1 and 2 (the edits share hunks); unverified until the final job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 02:07:19 +02:00
Meta Vens
cec09806b0 Caching: an empty direct flush still applies as before; only a write that checked keys and has nothing else to write writes no file. Tests: the delisting writer declares the widget; the checked-key flush also writes
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 05:06:19 +02:00
Meta Vens
2e3a5b8e16 Contracts: an unheld removal is a check-only key judged under the store's lock; ExpectUnchanged compares foreign records
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 04:03:42 +02:00
Meta Vens
3a18f01a83 Caching: a removal of a key the cache does not hold is a check-only key judged under the store's lock; ExpectUnchanged compares foreign records too
Remove and Commit of an unheld key no longer read the store at the call: the key travels as CultCommitRequest.Checks (commit) or a staged check (flush),
and each writable store judges it in RefuseUndeclaredOnDisk with the keys it writes, never removing it. A batch that only checks writes no file.
CultForeignRecord.StoredVersion records the version a store read, and the one ConditionsHold takes the store's foreign records.
Folds spec commits 1 and 2 (their hunks share the same methods). The src projects build; tests/GameCult.Caching.Tests does not at this commit
(CultCommitRequest gained a parameter; the next commit updates the test).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 04:03:41 +02:00