Commit graph

1,458 commits

Author SHA1 Message Date
Meta Vens
5d1fc496d7 Caching tests: register the shared valid store's record type
The open verdict for v3-base.msgpack needs a cache that knows vectors.item.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 02:30:30 +02:00
Meta Vens
4b3c76e418 Caching tests: the open verdict is asserted where the store is attached
AddBackingStore pulls, so a file the reader refuses throws on attach, outside
the assertion. Wrap attach and pull together.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 02:11:40 +02:00
Meta Vens
e1ff5b2278 Mesh tests: raw and typed writes share one readability vector test
The raw-write refusal test spelled the old header-only verdict (NotSupported for
a first slot that is a string, a legacy array replaced). The shared vector test
now drives both write paths; the old test keeps only the marked-file case.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 02:05:56 +02:00
Meta Vens
4028205ff1 Store readability: one reader decides open, flush, commit and CultMesh writes
A rewrite replaces a file only when the runtime's own reader opens it. C#
drops ReadStoreHeader (a second, header-only verdict that let a flush replace
files the reader refuses) and asks DeserializeSnapshot from open, flush and
commit; the reader now requires exactly one array, so C# refuses bytes after
the store on read as TS and Python do. A zero-byte file is an empty store for
all three paths. CultMesh.WriteSingleFileDocument reads the file it replaces
through the Mesh reader before writing. Rust reads and rewrites through one
classification (trailing bytes refused on read; a non-store first slot must be a
legacy envelope array the reader accepts). TypeScript checks a legacy array with
pullAll's own decoder before replacing it. Python already conformed.

Shared byte vectors and per-runtime verdicts live in
tests/vectors/document-variants-c2a/readability; every runtime's test reads
them. Holds gains second-dictionary-value and second-list-item cases. The
persistence format contract now marks v3 by content and states the readability
rule.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 01:59:28 +02:00
Meta Vens
76e449f33e Merge origin/main into hands/variants-c2a 2026-09-30 01:52:26 +02:00
Meta Vens
15c6a7e2d4 Merge CultMath Phacelle (Asura 2a-ii-b)
phacelle(p, side, offset, normalization) -> CultPhasor with exact
gradient and exact pruning, mirrored in HLSL. Soul: no correctness
defect; the MPL-2.0 port now lives in its own MPL-headed files and the
NuGet package is MIT AND MPL-2.0 with notices packed (0acc3728).
Release (Unity DLL rebuild, changelog) is 2a-ii-c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:54:19 +02:00
Meta Vens
9b8877ced7 Ack map: record Q-A7 A, Q-A8 A, Q-A9 B
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:45:41 +02:00
Meta Vens
03b1df7cf5 Ack map pass 3: FORWARD-TSN (Cut F), receipts port, Cut 1c ruled, Cut K, Cut D
Maps the four 2026-09-30 rulings. Cut 2 merges only with Cut F (Soul:
expiring reliable sets pin the bound). Adds Cut D (a failed send to one
peer no longer aborts a document server poll) and C# Cut 2c. Opens
Q-A7, Q-A8, Q-A9; Q-A7 and Q-A9 block Cut F.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:42:27 +02:00
Meta Vens
0acc3728ed CultMath: Phacelle port moves into MPL-2.0 files; prune cut pinned by boundary points
MPL-2.0 is file-level and the Phacelle port sat inside MIT files. phacelle and
CultPhasor now live in math.Phacelle.cs and CultPhasor.cs, and cultmath_phacelle
in CultMath.Phacelle.hlsl (included by CultMath.hlsl, both tracked copies), each
with the MPL header and Johansen / Luke Mitchell attribution. math stays one
partial class, so math.phacelle is called exactly as before. The GameCult.Math
package declares MIT AND MPL-2.0 and packs THIRD-PARTY-NOTICES.md and the new
include; the Unity LICENSE.md points at CultLib; build-unity-package.ps1
requires the new include.

The random sweeps never met a cell whose box bound sits just under the 2.25 cut
while its weight is still nonzero, so cuts of 2.245, 2.24 and 2.2 survived.
Six hard-coded points (found by an offline search) sit next to such cells; the
C# pruned-vs-unpruned test and the HLSL mirror test both use them, with a
witness check that re-derives the property from the hash.

Docs: the prune keeps about 45 of 64 cells (14 is the nonzero-weight count),
the support radius is 1.49998 (d^2 = 2.24995), normalization's range and
saturation, W = 0 only for adversarial hashes, and the support-edge gradient
seam size.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 00:34:27 +02:00
Meta Vens
fd1cf22ffe Pin Holds on every list shape, and drop C#'s redundant first-slot check
CultElementIds.Holds is the one fact a store marks its header by; a test now
drives it directly over a dictionary value, a list of lists, a list under an
unset element and an element, and over unset ids that hold nothing, so a
mutant that skips any of those branches fails.

C# ReadStoreHeader refused a first-slot string without the cultcache.store.
prefix and then ran RequireSingleFileFormat, which refuses every string that
is not v1, v2 or v3 anyway. The prefix check is gone; a non-header string is
refused as a format this runtime does not read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:11:13 +02:00
Meta Vens
0be33c1198 Rust: a zero-byte store file is rewritten as an empty v1 store
Soul's mutant deleting the empty-file arm of write_all_unlocked survived: no
test rewrote a zero-byte file. Without the arm the empty file reaches
store_header and is refused as truncated. The test pushes onto a zero-byte
file and expects a v1 store holding the pushed record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:11:12 +02:00
Meta Vens
ea1bd6a797 Every runtime rewrites only a file that is exactly one store
A rewrite read the header of the file it replaces but not whether the file was
a store: C# ReadStoreHeader skipped one value and ignored bytes after it, and
took a non-array value or an array whose first slot is a string that is not a
store header for a headerless legacy file (so did the C# Mesh raw write, which
uses it); TypeScript's pushAll took any non-array value or non-header first slot
for v1; Rust ignored bytes after the array.

The rule, one per runtime: a file is a store on rewrite only when it is exactly
one complete MessagePack array. Bytes after it, a value that is not an array,
and a first slot that is a string but not a header this runtime reads are
refused, and the file is left untouched. An empty file and a legacy envelope
array (first slot not a string, empty included) carry no header and are still
rewritten.

- C#: ReadStoreHeader refuses non-array values, trailing bytes and a non-header
  first string, with a NotSupportedException naming which.
- TypeScript: storeHeader (store-format.ts) decides the rewrite header;
  @msgpack/msgpack's decode already refuses trailing bytes.
- Rust: store_header(bytes, whole) walks with a cursor; the rewrite path asks
  for the whole file, the read paths keep reading as before.
- Python: push_all already reads the whole file (unpackb refuses trailing
  bytes, the legacy decoder refuses anything but a list of maps); pinned by a
  test.

Tests in each runtime run the same file matrix (truncated, trailing, scalar,
map, string first; empty, empty array, legacy), plus a C# unconditional commit
over a store with trailing bytes and the C# Mesh raw write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:01:58 +02:00
Meta Vens
2a3cab1bd7 CultMath tests: a dense sweep pins cultmath_phacelle against C# bit for bit
The generic mirror comparison passes about a hundred inputs through the shader and
missed a mirror whose prune cut was 2.0 instead of 2.25, because only a rare cell has
a nonzero weight under that cut. Twenty thousand random arguments compare the shader
text and math.phacelle field by field and kill that mutant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:58:43 +02:00
Meta Vens
e9469cb3a4 Ack map: record Q-A3 B, Q-A4 A, Q-A5 A and Q-A6 A
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:53:21 +02:00
Meta Vens
4f742d05c3 C#: a flush refuses to overwrite a store file whose header it cannot read
SingleFileBackingStore.PushAll replaced the file without reading it, so a
truncated store or one in a format this runtime does not read was clobbered,
where Rust, TypeScript and Python refuse. PushAll now reads the header of a
non-empty file under the lock before it writes; a file it cannot read throws
and is left as it was. An empty file and a legacy envelope array carry no
header and are still replaced.

The header is read by the store's codec: SingleFileBackingStore gains a
protected virtual ReadStoreHeader (default: read the whole snapshot), and the
MessagePack store overrides it with
CultDocumentMessagePackSerialization.ReadStoreHeader. An unconditional commit
reads the file it rewrites through the same check.

Doc text: last-writer-wins now says it holds only over a file the writer can
read (CultCache.cs PushAll comment, cultcache-persistence-format.md
"Conditional commit is the only safe multi-process write" and writer rule 4,
cultcache-store-composition.md "A plain flush and an unconditional commit are
last-writer-wins").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:51:47 +02:00
Meta Vens
9f18a432cb CultMath: document phacelle and record Johansen's MPL-2.0 provenance
design.md names CultPhasor as the second struct-return consumer and describes
phacelle (stripe wave vector, weight, normalization, exact gradient and its two
discontinuity classes, exact pruning). THIRD-PARTY-NOTICES.md, in both the package
root and the Unity package, cites Rune Skovbo Johansen's Phacelle Noise (MPL-2.0)
and records that the 3D-cell generalisation, the caller-supplied side vector, the
exact gradient and the exact pruning are new.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:51:04 +02:00
Meta Vens
7904e15ed3 CultMath: phacelle, Johansen's Phacelle noise on 3D cells with an exact gradient
Cut 2a-ii-b. phacelle(float3 p, float3 side, float offset, float normalization)
returns CultPhasor { float4 cos; float4 sin }, each float4(gradient.xyz, value.w).

- 4x4x4 cells, pcg3d-hashed jitter in [-0.5, 0.5), weight max(0, exp(-2 d^2) - 0.01111),
  normalization as upstream (lpmitchell/AdvancedTerrainErosion PhacelleNoise). The
  caller supplies the stripe wave vector; offset is in cycles.
- The gradient is exact: weight derivatives and the normalization are included, not
  upstream's grad(cos) ~ -sin * side.
- Cells whose per-axis lower bound is at least 2.25 have weight exactly 0 and are
  skipped. An internal overload switches the prune off so the test can pin bit
  equality without a second copy of the loop.
- cultmath_phacelle and CultPhasor mirror it in both tracked CultMath.hlsl copies
  (the same blob). The mirror test compares the struct field by field; it now covers
  34 functions.
- PhacelleTests: value against an independent double-precision reference, quarter-cycle
  phase rotation, gradient against central differences on both sides of the
  normalization threshold (excluding the threshold and each cell's weight-support
  edge, where the exact gradient is discontinuous), stripes along the flow on the unit
  sphere (0.215 against the 0.25 bound), step-difference continuity across cell planes,
  and pruned == unpruned bit for bit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:51:02 +02:00
Meta Vens
ce0d61fc71 Ack map pass 2: re-map on 3bf1c0c, split session generation into Cuts 1b and 1c
Applies the Q-A1/Q-A2 rulings (one public meaning: Acknowledged means
delivered). Adds Cut 1b (every session ending drops what it owed; flush is
bound to its session) and Cut 1c (how a new generation is recognised,
blocked on new Q-A6). Probes at 3bf1c0c reproduce G1, G1b, G2 and G3 in
all four runtimes. Kotlin trails as Cut K pending Q-A5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:47:14 +02:00
Meta Vens
619e089fa6 C#: HoldsIds is read off the stored form each time a store writes
A record's HoldsIds was decided by its admission and trusted afterwards, so a
document changed after it was committed, or loaded and changed in place,
flushed under the header its admission-time content needed (Soul P7, P10).

HoldsIds now has one owner, the record's stored form, read when asked:
- a plain record: CultElementIds.Holds over its document, a scan that reads
  and refuses nothing;
- a variant: the same scan over each override value, decoded with the codec
  the record was built with (the cache's, or the attached cache's at load);
- a variant built with no codec (a store that loaded it with no cache) cannot
  see its values and counts as holding an id, so a store never writes an
  unmarked header over ids it could not see.

Cut: the admission path that set HoldsIds on plain records, on variants from
their resolved copies, and on flattened records; the internal setter and its
backing flag; IdPlan.HeldIds and IdPlan.HoldsIds. The Mesh typed write reads
Holds off the document it writes. Admission keeps its own id counting
(IdPlan.Count) for IdsInMemoryOnly.

Tests: an id added to a committed or loaded deck after admission marks a
PushAll v3, and taking it away unmarks it; a variant read without a codec
counts as holding ids while the same record read with one does not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:46:50 +02:00
Meta Vens
af7209a2c8 Map publication Cut 4-6 on the journal (Imagination pass 4) and record Q-P5 A
Adds the pass 4 probes, the journal-floor rule, and the re-taken Cuts 4-6, salvage and ledger.
Records Q-P5 A: a primary that cannot log a committed change throws CultNetShardLogException.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:34:42 +02:00
Meta Vens
cf16cb7c1f Merge origin/main: CultCache publication Cuts 1-3 and the RUDP hardening
One conflict, at the end of Admit in CultCache.cs. The branch had added the
IdsInMemoryOnly marking of a load's minted ids before Apply; main had turned
the thread's hold into a Hold carrying Changes and Failures and run the
journals right after Apply. Both are kept, in that order: the marking touches
only the records and runs no user code, then Apply mints the Sequences, the
hold records the changes, and the journals run under the gate.

Id planning and Project do not collide. Ids are planned and applied first,
Resolve (variants, then Project, then the name and index rules) reads the
filled documents, land runs, and only then Apply mints. A Project getter that
throws is caught by the same handler that undoes the id plans, so a refused
admission changes no document and mints nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:33:11 +02:00
Meta Vens
3616d0617c Rust: the header is read and the file proven whole in one pass
rmp_serde refuses a sequence visitor that stops early, so the prefix-only header read the
previous commit introduced could not decode any store. store_header is now one visitor: the
first slot is the header (None when it is not a string, the legacy envelope array), the rest
is walked, and a truncated file is an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:58:38 +02:00
Meta Vens
9c62187562 Test: the directory store keeps a marked manifest on a partial flush, so only the single file drops back
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:56:26 +02:00
Meta Vens
aa6b11006f CultMesh: a raw write refuses a file whose header it cannot read
ExistingHeader read the whole snapshot and swallowed every failure into "no header": a
truncated v3 file got a v1 raw write and an unknown v9 file was overwritten, where
TypeScript, Python and Rust refuse. CultDocumentMessagePackSerialization.ReadStoreHeader
reads the prefix header, refuses a file that is not one complete MessagePack value or whose
format this runtime cannot read, and the raw write keeps a whole marked file marked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:53:13 +02:00
Meta Vens
c4fbb53a3b C#: HoldsIds means what the stored form persists, wherever the record came from
A CultStoredDocument built by a caller and handed straight to a store never passed an
admission, so HoldsIds stayed false and its ids landed under a v1/v4 header. An admission
still decides it (it counts the ids it fills); a record that never had one reads it off the
document it stores. CacheBackingStore.HeaderFor documents the rule for third-party stores.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:53:13 +02:00
Meta Vens
8272b40645 Rust: a store cut short is refused by a rewrite, never taken for a legacy file
store_header returned None for any bytes it could not walk, so push_all read a truncated
variant store as legacy and overwrote it v1. It now proves the file is one complete
MessagePack value first (an error otherwise, the rewrite refuses and leaves the file), then
reads the header from the prefix; the reader's own header read no longer walks to the end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:53:12 +02:00
Meta Vens
3bf1c0cea5 Merge RUDP stray-packet hardening across all four runtimes
A stray, malformed, foreign-id or refused datagram is dropped and counted,
never fatal; a refused reliable frame ends the peer's session instead of
being acked and lost; flush fails once its session ends; one reliable
Accept per session; the TS operation service expires idle sessions and
survives a throwing handler. Four Soul passes; session-generation and
reconnect semantics move to the ack cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:05:30 +02:00
Meta Vens
b0d4103e22 cultnet-ts: a timeout ends the session generation, and every non-repeated Connect ends a live one
Also adds a swept-after-handler test for the operation service and states
the Rust receipt comment exactly: only a peer Disconnect invalidates receipts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:01:43 +02:00
Meta Vens
d29e2a7d81 Ack map: the ending paths disagree about owed writes (Cut 1 scope)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:59:18 +02:00
Meta Vens
e9a5a60dbf TypeScript and Python: pin pushAll/push_all onto a store whose header the runtime cannot read
The behaviour was already a refusal that leaves the file untouched; now a test fails if a rewrite
ever reads the disk header less strictly than a load does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:56:08 +02:00
Meta Vens
7ea1aa6a9a Rust: a rewrite reads the header with the snapshot reader, not a hand-written prefix parser
push_all reads the file and reuses store_header and readable_store_format, the same pair a load
uses. The 96-byte prefix parser it replaces misread a str16 header and any str8 header past the
prefix as v1 and overwrote a store it could not read. Pins both encodings refused with the file
untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:56:07 +02:00
Meta Vens
41d0dd9bfb C# header: one function decides it for every writer; a store's own ids are the only source
CacheBackingStore.HeaderFor(holdsIds, existingHeader, wholeStore, directoryStore) replaces FormatFor,
DirectoryFormatFor and the writers' private marker logic. A writer that rewrites the whole store
decides by content; a writer that touches part of it keeps a header already marked. Flush,
unconditional and conditional commit, the directory store and CultMesh's single-file write all call
it. A typed CultMesh write replaces the file with a document it can see, so it decides by content;
a raw payload is opaque and keeps a marked file marked.

A record's HoldsIds is what its stored form persists: a load keeps a variant's delta as read, so
the ids a load mints in an override exist only in memory and no longer mark the store. A
conditional commit onto the file no longer reads this cache's view of what the file held, so a
stale view cannot over-mark it. IdPlan.HoldsIds is derived from HeldIds and the fills.

Reverts InternalsVisibleTo GameCult.Caching.MessagePack and the public DirectoryFormatV4/V5
constants: the directory store reaches the decision through the protected member it inherits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:54:01 +02:00
Meta Vens
36e052b33a cultnet-ts: the replaced-peer test acknowledges A's Accept so a repeated-Connect answer is distinguishable from an Accept
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:46:27 +02:00
Meta Vens
c412eed066 cultnet-rs: the reconnect test expects the Accept to have acknowledged the new Connect
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:37:24 +02:00
Meta Vens
29f8bf575e Tests: a repeated Connect acknowledges what it carries (TS, Python, C#); fix the TS replaced-peer test
The repeated-Connect answer applies its acknowledgement field in every runtime;
no test failed when a runtime stopped doing so. The TS replaced-peer test now
lets client A receive its Accept before A sends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:35:44 +02:00
Meta Vens
5f1e883d88 cultnet-rs: a peer's Disconnect ends its session's receipts and waits for good
A Disconnect left the session's pending writes and scope alone, and the
transport's Invalidated status leaned on disconnect_reason, which connect()
clears. After a reconnect the receipt returned to Pending and could become
Acknowledged in the new session. The session now rotates its scope and drops
what it still owed on Disconnect, so the session is the one owner of
Invalidated and the transport override is gone.

A flush records the session scope it started in and fails when a Connect that
replaces the peer rotates it, instead of resolving on the emptied queue.

Tests: Disconnect-then-reconnect receipt, stale end reason after a new Connect
is accepted, flush across a replaced peer, and a repeated Connect's ack field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:35:44 +02:00
Meta Vens
756da48943 cultnet-ts: a Connect from another endpoint replaces the peer; a flush is bound to its session; a busy operation session is not idle
A server-mode transport treated a Connect from a new endpoint as a repeated
Connect once the session was connected: the new client got an Ack and never an
Accept, and the old session never ended. Only a Connect from the accepted
endpoint repeats; another endpoint resets the session and gets its Accept.

A flush recorded nothing about the session it started in, so a disconnect
listener that reconnected cleared the end reason before the 5 ms poll saw it.
Sessions now carry a generation; a flush fails if its generation ended.

The operation service's idle sweep deleted a session whose handler was still
running, losing the response. A session with a handler running, or one that
finished a handler within the idle window, is not idle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:32:05 +02:00
Meta Vens
ecfb4d6509 Ack map: record Q-A1 and Q-A2, one meaning for Acknowledged
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:29:59 +02:00
Meta Vens
9321c98a38 Map the RUDP ack cut: Received and Delivered receipts
Imagination pass 1. Probes found acknowledged ordered frames lost in live
sessions in every runtime; the map derives Delivered at the sender from a
correct receiver watermark. Operator questions Q-A1..Q-A5 open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:24:52 +02:00
Meta Vens
591eeff70d Rust: a whole-store flush reads the on-disk header first and refuses one it cannot read
push_all overwrote a v9, variant-v2 or variant store with a v1 file, dropping its variants; TypeScript and Python
refuse. The rewrite header now comes from the file's own prefix and shares one readable-format rule with the
decoder. TypeScript pullAll resets its header when the file is gone, empty or legacy, as Python does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:20:11 +02:00
Meta Vens
6a7d17bdda C2a fix: a flattened record is marked by the document it stores; every writer decides its header by content
A flattened variant's HoldsIds is read off the document re-resolved against the post-batch base, not carried from the
variant or from the document the flatten was planned against. The conditional commit onto a file decides by the same
rule as the unconditional commit (ids in the records the file will hold, ids only this cache minted excepted, a marked
file staying marked). The directory manifest's header choice moves beside FormatFor. Pins the three-batch flatten
probe on the commit and flush paths, the conditional commit, the last id leaving, an id-bearing override that is not
the last, and a failed CultMesh write giving its ids back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:20:01 +02:00
Meta Vens
4252ce65aa Map: C2a Soul pass 3 and the C0-with-C2a release ruling
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:16:34 +02:00
Meta Vens
d85877a7be cultnet-rs: the session no longer keeps an initial sequence nothing reads
Reset used to reissue from it; issued sequences now survive a reset, so the
field was dead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:54:03 +02:00
Meta Vens
9475e3653d cultnet-rs, cultnet-ts: tests that fail when the fix is removed
The hub test counts replaced sessions, since a fresh session repeats the same
Accept sequence and the distinct-sequence count could not see it. The TS test
requires an Ack, not a new Accept, once the Accept was acknowledged. A peer
Disconnect is pinned as an ended session for flush and receipts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:47:53 +02:00
Meta Vens
ae8576d856 Merge CultCache publication Cut 3: the journal
AddJournal runs under the gate right after Apply, once per admission that
changed something, before observers. Soul: merge with follow-ups; the
dispose and catch-path fixes landed. Cut 4 must decide what a throwing
journal means for replicas and whether a journal needs the change origin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:45:30 +02:00
Meta Vens
afd52e97fa GameCult.Networking: the replaced-peer reason belongs to the listener that reports it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:44:32 +02:00
Meta Vens
ca18dfac55 GameCult.Networking: an ended session fails its flush; a repeated Connect owes one Accept
FlushReliable throws for writes an ended session forgot. A server-mode
transport and the listener answer a Connect from the peer they already hold
with AnswerRepeatedConnect: the Accept still owed, or an Ack, queueing
nothing. The listener replaced the peer session on every Connect, reusing
sequences and never reporting PeerDisconnected; it now replaces only on a
different connect payload and reports the old peer, as the Rust hub does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:41:23 +02:00
Meta Vens
4814aea8b0 cultnet-ts: an ended session fails its flush; abandoned operation sessions idle out; a repeated Connect owes one Accept
flush now rejects for writes an ended session forgot. acceptConnect on a
connected session delegates to the new answerRepeatedConnect, so a repeated
Connect re-sends the Accept still owed, or an Ack once it was acknowledged,
and queues nothing; the operation service no longer builds a session per
Connect. The operation service dropped sessions never, so 64 abandoned
clients locked every new one out: a session idle for sessionIdleTimeoutMs
(default 30 s, the document server's session_idle_timeout) is now dropped.
A request whose handler rejects ends its session like a transport refusal,
so the invoker learns promptly and a retransmit is not acknowledged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:41:22 +02:00
Meta Vens
34054db03a cultnet-py: an ended session fails its flush; a repeated Connect owes one Accept
flush_reliable raised nothing for writes the ended session had forgotten; it
now raises ConnectionError with the reason. Server mode answered every
repeated Connect by queueing a fresh reliable Accept; a Connect from the peer
already accepted is now answered with the Accept still owed and queues
nothing, as the Rust hub does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:37:56 +02:00
Meta Vens
c7a61018da cultnet-rs, cultmesh-rs: an ended session fails its flush; issued sequences survive a reset; the doc server says goodbye
A session that refused a packet, or was disconnected, forgot its
unacknowledged writes, so flush_reliable returned success for writes the peer
never acknowledged. The transport now fails the flush with the reason and
reads such writes as Invalidated. Reset no longer reissues sequence numbers or
fragment ids: a late duplicate Connect in server mode reused sequences the
peer had seen. end_refused_session owns the reset-then-goodbye ordering for
the hub, the transport and the document server, which used to end a refusing
session silently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:36:58 +02:00