From afd799ba8dff0ef590ff53bfd8354032e4ee5a7a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 21 Oct 2026 21:59:38 +0200 Subject: [PATCH] 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 --- src/GameCult.Caching/CultCache.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/GameCult.Caching/CultCache.cs b/src/GameCult.Caching/CultCache.cs index ea79e8ea..bc95ace9 100644 --- a/src/GameCult.Caching/CultCache.cs +++ b/src/GameCult.Caching/CultCache.cs @@ -393,7 +393,7 @@ namespace GameCult.Caching /// /// The schema id this record is durably stored under, as its backing store last read or wrote it. It differs from /// only for a record loaded under compatible schema drift that its - /// store has not rewritten since; a store records its rewrite with . + /// store has not rewritten since; a store records its rewrite of an entry it holds with . /// Conditional commit compares this id and with the disk. /// public string PersistedSchemaId => _persisted.SchemaId; @@ -3331,8 +3331,16 @@ namespace GameCult.Caching /// kept is not marked. A rewrite left unmarked makes conditional commit compare the schema id the record was read /// under, which fails closed until the next pull. /// - protected static void MarkWritten(CultStoredDocument entry) => - (entry ?? throw new ArgumentNullException(nameof(entry))).MarkWritten(); + /// is not the entry this store holds in + /// at its key: a store marks only its own rewrite. + protected void MarkWritten(CultStoredDocument entry) + { + if (entry == null) + throw new ArgumentNullException(nameof(entry)); + if (!Entries.TryGetValue(entry.Key.Value, out var held) || !ReferenceEquals(held, entry)) + throw new ArgumentException($"Record '{entry.Key.Value}' is not the entry this store holds; a store marks only what it wrote.", nameof(entry)); + entry.MarkWritten(); + } } public abstract class SingleFileBackingStore : CacheBackingStore