Commit graph CultLib/tests/GameCult.Mesh.Tests
Author SHA1 Message Date
Meta Vens
7c4bc2bdc0 Make generated and reflective document members one rule
The generator and CultDocumentRegistry now discover members by the same
steps: most-derived declaration first, override chain collapsed to one
member, attributes read up the override chain, rejections in one order
with one text (GCC001 / InvalidOperationException).

- Accessors are named per document, so documents sharing a [CultName] or
  [CultIndex] base member no longer emit duplicate methods (CS0111).
- Duplicate [Key] slots are refused, including new-hidden fields and
  properties; a hidden persisted member with a different slot is refused.
- String [Key("x")] gets its own message instead of "has no [Key]".
- A persisted property whose set accessor is private, protected or init,
  or a readonly field, is refused instead of CS0272 in generated code.
- Overrides: MessagePack's DynamicObjectResolver reads the base
  declaration's [Key]/[IgnoreMember] (it throws "key is duplicated" for a
  re-keyed override and serializes an override marked [IgnoreMember]).
  An override that changes them is refused in both paths; an override
  that repeats or omits them is accepted and byte-pinned.
- Typed Serialize<T>/Deserialize<T> used static Options and skipped
  generated codecs and declared resolvers; deleted. CultNetDocumentBinding
  and CultMeshSnapshots route through the untyped path with their registry.

DocumentShapeSweepTests compiles each shape through the generator and
compares generated vs reflective schema id, catalog, name/index values
and payload against MessagePack's own bytes, or equal rejection text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:00:16 +02:00
Meta Vens
825b4f7d26 Mirror snapshots carry the cache Sequence they reflect
ObserveAsync, ReactiveAsync and RefreshAsync read a snapshot without a
Sequence, so an older change admitted before the read but delivered after it
overwrote the newer value.

CultCache.GetWithSequence(key) returns the document and the cache's Sequence,
read together under the gate. A cache-backed CultMesh handle passes it to its
mirrors; each mirror subscribes first, then adopts the sequenced read under its
own gate and takes that Sequence as applied, so a straggler at or below it
drops. RefreshAsync on both mirrors uses the same adoption path. Public Mesh
surface unchanged. The read is non-generic because the Mesh route needs the
untyped document for same-schema conversion.

Contract: only Watch/WatchRecord changes carry Sequence; OnUpdate-derived
CultNet streams have no ordering or stale protection; schema-alias handles and
removals are not stale-protected; the sequence constructor argument changed
CultCacheDocumentChange<T>'s binary signature.

Tests: ObserveAfterNewerSnapshotIgnoresStraggler and
RefreshIgnoresStragglerOlderThanSnapshot (observed and reactive cases) fail on
4562340; SequencedReadReflectsEveryChangeAtOrBelowItsSequence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 21:02:49 +02:00
Meta Vens
456234018f Carry order as data: per-cache change Sequence, no delivery scheduler
bcae483 made the cache a delivery scheduler (tickets, _deliveryGate turn
waits, per-thread delivery queues). It deadlocked inside CultLib: a load's
delivery reaching a CultNetDatabaseSubscriptionServer observer that needs
_lifecycleGate, while a subscribe's demand handler holding that gate writes
the cache and waits for the delivery's turn.

The cache now owns order as data. Every admitted change takes the next value
of a per-cache in-memory sequence under the gate, published as
CultCacheDocumentChange<T>.Sequence. Delivery is the 544c087/b653f68 rule:
each call publishes its own changes on its own thread after releasing the
gate; nothing waits on another call's delivery. Cross-thread delivery order is
not guaranteed.

Deleted: ticket counter, _delivered, _deliveryGate, Monitor.Wait turns,
per-thread delivery queues, per-ticket catch-all, and the save-order contract
text with its observer-must-not-block rule.

Consumer: a cache-backed CultMesh document handle hands its observed and
reactive mirrors each snapshot's Sequence; ApplyCanonicalSnapshot drops one
not newer than the last applied.

Tests: SequenceIncreasesInAdmissionOrder,
ObserverWritingTheSameCacheDoesNotWaitOnOtherDeliveries,
SubscriptionDemandHandlerWritingCacheDuringDeliveryDoesNotDeadlock,
WatchRecordIgnoresStaleSequence. The last three time out on bcae483.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 20:43:11 +02:00
Meta Vens
a3cc394241 Route each document type to one home store; attach hydrates; commit explicit batches
Cut 3 of the CultCache migration (Aetheria docs/cultcache-migration-cut.md, 2.1
and 2.2). One commit, because routing, the batch that replaces the ambient
transaction, and conditional commit share one admission path.

Deleted first:
- globals: InitializeGlobals, MaterializeMissingGlobals, the two-argument
  constructor, the initializeGlobals plumbing in CultCacheMessagePack,
  ContainsDurableRecord (cache and directory store)
- replication: the push-to-every-store loops and the attach-time push of
  existing records
- typeof(T) as the schema owner in AddAsync/UpsertAsync and in
  CultNetDatabase.PutAsync/PutPredictedAsync
- the ambient transaction: ExecuteTransactionAsync x2, CultCacheTransaction,
  CommitTransaction, VisibleStoredDocuments, the AsyncLocal, the SemaphoreSlim
  gate, and every ambient check
- CultCacheOpenOptions.PullOnOpen; OpenAsync is Task.FromResult(Create(...))
- DirectoryMessagePackBackingStore.ResolveLegacyUncataloguedRecordCatalog: the
  catalog precommit (fe55bff) is an ancestor of the v4 page format (eea8248),
  so no v4 manifest can reach it
- CultCache.DeleteAsync<T>; its one caller, CultNetDatabaseSubscriptionClient,
  called it by reflection and now calls Remove(key)

Added:
- AddBackingStore(store, params Type[] homes): attach validates (clean, not
  already attached, one untyped store, no claimed home type, no admitted
  record changes home) and then pulls under the gate
- Home(type): most derived routed home, else the untyped store
- Admit: every add and remove, from a write, a batch or a load, validates
  (home, read-only, loaded-from-home, global singleton), lands durably, then
  applies and publishes; a pull hands its whole delta over in one call, so a
  refused record leaves the store and the cache unchanged
- read-only stores (CacheBackingStore(readOnly), both store constructors,
  CultCacheOpenOptions.ReadOnly); flush skips them and writes only dirty stores
- assignable GetGlobal<T>/GetByName<T>/GetByIndex<T>/Watch<T>; more than one
  candidate throws naming them
- Commit(Action<CultCacheBatch>) returning bool, TryCommit returning
  CultCommitOutcome, CultCacheBatch (Upsert, Remove, Expect, ExpectUnchanged,
  sealed after commit), CultCommitRequest; CacheBackingStore.CommitBatch
  evaluates conditions under the store's exclusive lock
- single-file store: <path>.lock sidecar for CommitBatch and PushAll; a clean
  store commits onto the file as it is, so records it never loaded survive
- storedAt minted in one place and bumped a tick when not later than the record
  it replaces

Consumer adjustments in this repo: Mesh streaming tests drop PullOnOpen; the
Studio bridge stops reflecting on it; BackingStoreTests' global test creates
its global explicitly and its transaction tests are rewritten against Commit
(the two that proved ambient hydration rules are deleted); the pre-Cut 2
refusal test expects the attach to throw.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 17:57:58 +02:00
Meta Vens
fe7e5fbe37 Delete the parked CultCache SoA table
Removes CultSoaTable, CultSoaColumn, CultCacheSoaStore, CultCacheSoaTypeTable,
CultCacheSoaMember, CultCache.Soa<T>, the _soa field and its four hooks, and
the two tests that exercised them (with their TransformEntry and
MeshManagedPlayer fixtures). The last working form is at
parked/cultcache-soa.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 16:22:54 +02:00
Meta Vens
334e60f192 Recover authority-bound CultMesh and atomic generations 2026-08-26 19:01:24 +02:00
Meta Vens
4f6e95bb3d Carry editable field authority in CultMesh bindings 2026-08-22 00:05:57 +02:00
Meta Vens
3cec5463b2 Fence body commits by demand generation 2026-08-19 14:27:22 +02:00
Meta Vens
21fd4e07eb Version hot-body demand generations 2026-08-19 14:10:13 +02:00
Meta Vens
7a6f0be21f Bind CultMesh application ingress to session identity 2026-08-18 23:08:59 +02:00
Meta Vens
99458dbb57 Ship authenticated remote transports in Unity package 2026-08-18 20:23:27 +02:00
Meta Vens
c194bd4cc8 Harden CultMesh route fallback and secure onboarding 2026-08-18 19:41:03 +02:00
Meta Vens
f622ee1b24 Authenticate CultMesh authority routes and replay operations 2026-08-18 19:02:46 +02:00
Meta Vens
6c848bd6c0 Bind CultMesh authority identity to physical routes 2026-08-18 17:21:58 +02:00
Meta Vens
21163d83fa Make CultMesh identity and route recovery explicit 2026-08-18 10:42:33 +02:00
Meta Vens
c75051017d Make CultMesh session identity explicit 2026-08-18 07:26:55 +02:00
Meta Vens
b0a876a220 Add typed CultNet operation dispatch 2026-08-18 05:59:11 +02:00
Meta Vens
35d31ae908 Support mapped frame bodies on Unix 2026-08-18 04:18:46 +02:00
Meta Vens
f65352fa73 Measure mapped hot-body copy cost 2026-08-18 04:03:06 +02:00
Meta Vens
e49f93f56d Invoke typed operations through retained clients 2026-08-18 03:49:33 +02:00
Meta Vens
cb7f9e8fa2 Retain native clients across provider routes 2026-08-18 03:34:59 +02:00
Meta Vens
f38966b4b9 Bound retained session callback resources 2026-08-18 02:29:14 +02:00
Meta Vens
ba680020b9 Prove reactive scheduling scales with edits 2026-08-18 00:44:41 +02:00
Meta Vens
1fc68a4701 Route CultMesh sessions by stable Verse identity 2026-08-17 22:42:08 +02:00
Meta Vens
376c08a155 Make CultMesh client resources explicit 2026-08-17 20:39:24 +02:00
Meta Vens
98bec47d75 Make CultMesh document writes authority-explicit 2026-08-17 19:47:10 +02:00
Meta Vens
c1c1792dc5 Serve provider-owned content chunks directly 2026-07-20 03:05:28 +01:00
Meta Vens
ff89a5f007 Stream CDN artifacts in coarse durable windows 2026-07-19 20:29:07 +01:00
Meta Vens
c16163a82f Modularize CultMesh transport planes 2026-07-19 17:57:26 +01:00
Meta Vens
091200c09d Move hot bodies off snapshot state 2026-07-19 13:57:06 +01:00
Meta Vens
a1f25af2e0 Keep exact live subscriptions active before publication 2026-07-19 11:49:18 +01:00
Meta Vens
5587f5c6d1 Add ergonomic live body subscriptions 2026-07-19 10:55:27 +01:00
Meta Vens
4d2ddb5beb Default hot body subscriptions to live delivery 2026-07-19 09:20:40 +01:00
Meta Vens
743fa3889e Stream exact hot state over negotiated body planes 2026-07-19 05:54:24 +01:00
Meta Vens
242c8aaddd Stream mapped frame generations outside CultNet 2026-07-19 04:35:12 +01:00
Meta Vens
3e223ceecd Negotiate exact hot-body transport demand 2026-07-19 03:17:11 +01:00
Meta Vens
5c075bc212 Fence provider RUDP connection generations 2026-07-19 00:50:20 +01:00
Meta Vens
682a3e9f8d Bind mapped CDN bodies to verified paths 2026-07-16 14:08:13 +01:00
Meta Vens
25231429c1 Drain RUDP content traffic without per-packet sleeps 2026-07-16 02:36:48 +01:00
Meta Vens
262b5cc53d Add provider session wire transport 2026-07-14 23:47:30 +01:00
Meta Vens
a311cbe8cb Improve managed content throughput 2026-07-14 03:26:43 +01:00
Meta Vens
7d57140275 Add managed CultMesh content sessions 2026-07-14 01:13:23 +01:00
Meta Vens
1f40bbe411 Address CultMesh body publications by generation 2026-07-13 19:35:52 +01:00
Meta Vens
7d21bb0e1d Publish CultMesh network body bytes 2026-07-13 18:14:41 +01:00
Meta Vens
5e867bd92c Add typed CultMesh body publications 2026-07-13 17:48:42 +01:00
Meta Vens
dbe6a3f9de Publish cross-process CultMesh frame bodies 2026-07-13 17:38:20 +01:00
Meta Vens
581c5d0249 Read legacy CultMesh route records 2026-07-13 16:53:22 +01:00
Meta Vens
7e09668b7b Conform CultMesh body network fallback 2026-07-13 16:02:39 +01:00
Meta Vens
0a092d8c7a Own CultMesh SoA frame publication 2026-07-13 15:49:53 +01:00
Meta Vens
2727e325bb Map verified CultMesh CDN bodies without copies 2026-07-13 15:36:50 +01:00