Commit graph CultLib/packages
Author SHA1 Message Date
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
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
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
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
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
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
Meta Vens
4b85a173e8 Drop the redundant complete-block short circuit from FEC recovery
A block with every data shard present passes through the code as a no-op
and yields no recovered records, so the early return in both recovery
functions did nothing the code did not already do. Mutation showed both
survive removal. The audio payload-length rule is now pinned by its
message, since the codec's own shard-size error would otherwise mask its
removal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:35:18 +02:00
Meta Vens
1da2a64fbc Pin the media FEC and impairment rules that mutation left standing
Adds the policy fields that must be non-zero, the inclusive shard limit at
the decoder for video and audio parity, admission statistics, seeded jitter
bounds, exact seeded loss, and arrival order for datagrams due together.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:26:49 +02:00
Meta Vens
6285a92841 Close the media FEC test gaps found before mutation
Pins the block-boundary chunk and last-chunk length refusals in video
recovery, the audio recovery refusals (packets one either side of the block,
another session, wrong length, parity from different blocks), the remaining
audio block identity rules (session, stream, timebase, duration), and the
data-parity alternation inside a block.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:20:06 +02:00
Meta Vens
858e6ec608 Add the cultnet_impair proxy example and a socketless media FEC loss matrix
cultnet_impair is a deterministic bidirectional UDP proxy for pressure
testing real CultNet RUDP sessions with seeded loss, burst, duplicate,
reorder, jitter and stall schedules. It is ported from the Odin donor
(80adfd3) as an example, not library surface. The decision policy (Profile,
Scheduler, seeded RNG) is split from the sockets into policy.rs, which the
proxy and the tests both compile, so a schedule has one owner. The ten
donor profiles live in tests/impairment/.

tests/impairment_policy.rs keeps the donor's six deterministic policy tests
(the burst test now pins the exact drops rather than a count) and adds
profile loading and rejection. tests/media_fec_loss.rs sends protected video
frames through those schedules with no sockets and asserts the guarantee: a
block recovers exactly when at most m of its records were lost, recovered
chunks equal what was sent, and a burst that the send order spreads under m
per block loses no frame. The recovered-frame ratios are printed, not
asserted; tests/impairment/loss-matrix.txt is that output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:17:46 +02:00
Meta Vens
f7c58ce5bc Add the media FEC codec, video parity v3 and audio parity records to cultnet-rs
The codec owns the erasure math for both ends: protect_video_frame and
protect_audio_block on the producer, recover_video_block and
recover_audio_block on the consumer. Each block is systematic Reed-Solomon
over GF(2^8) (reed-solomon-erasure 6, default features), named on every
parity record by the scheme id rs-gf256-v1. Any <= m erasures recover
exactly; more report BeyondRepair. Video frames split into ceil(n/16)
near-equal blocks with m = max(2, ceil(k/4)); audio is 4+2. A frame's
records come back in send order, rotating one record from each block so a
burst lands as one loss per block.

Deleted first: gamecult.media_video_parity_shard.v2 and its XOR-stripe
validator, and the reliable "audio" channel (profile entry, send-options
arm, constant, tests). Audio now rides "media" with parity.

Added: gamecult.media_video_parity_shard.v3 (block geometry, scheme id,
shard length), gamecult.media_audio_parity_shard.v1, the AudioParity wire
variant with validation, and the parity record key now names the block.
Every shard is bounded at MEDIA_FEC_MAX_SHARD_PAYLOAD_BYTES (720) so its
wrapped record fits MEDIA_FEC_MAX_WIRE_BYTES (1431), measured with
identifiers of up to 32 bytes.

Tests pin: exhaustive recovery for every shipped (k, m) and 4+2, m+1
refusal, known-answer parity bytes for both paths, the datagram bound on
every record kind, send order, deadline carriage and decoder validation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:08:54 +02:00
Meta Vens
9740551839 cultnet-ts: pin the operation service handler rejection path as a counted drop
The fragment-id-0 test is caught by the session-refusal branch, so a frame that
fails inside the handler needs its own test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:45:38 +02:00
Meta Vens
9da5b35f34 TypeScript and Python: a whole-store flush applies the header the file on disk carries
TS pushAll wrote the header its instance last read (v1 when fresh) without reading the
file. It now reads the on-disk header first, as push and delete do through pullAll. Python
resets its remembered header when the file is gone or empty, so the disk decides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:44:16 +02:00
Meta Vens
042dc75c82 cultnet-py: a server-mode transport is claimed only by a Connect; a refusing session ends
Server mode claimed the endpoint on the first datagram of any type with the
right connection id, so an orphan Ack made a later real client a stranger. Only
a Connect claims it now. A session that refuses a packet has already recorded
its reliable sequence, so the transport ends it: the session forgets the peer
(so the goodbye does not acknowledge the refused frame), a Disconnect goes to
the peer, disconnect_reason is set, and a server-mode transport forgets its
endpoint. CultNetRudpSession gains reset_peer_state and rejects an
initial_sequence of 0xFFFFFFFF.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:39:35 +02:00
Meta Vens
610b5c86c2 cultnet-ts: end a refusing session, survive operation-service rejections, cap its sessions
A session that refuses a packet has already recorded its reliable sequence, so
the socket transport now ends it: the session forgets the peer (so the goodbye
does not acknowledge the refused frame), sends a Disconnect, forgets the
endpoint in server mode, and emits disconnect and close. The operation service
did `void handleServerDatagram(...)`, so any rejection was an unhandled
rejection that ended Node; it now drops and counts the packet (packetsDropped),
ends a session that refuses a frame, and holds at most 64 sessions, dropping a
Connect from a new peer beyond that. CultNetRudpSession rejects an
initialSequence of 0xFFFFFFFF.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:37:05 +02:00
Meta Vens
1d0dc3aa57 cultnet-rs: repeated Connect has one primitive; the goodbye must not ack the refused frame
The Disconnect a refusing session sent carried the ack of the very frame it
refused, so the sender saw the frame acknowledged. The session forgets its
peer's sequences before building the goodbye, and a server-mode transport
forgets its endpoint so only a Connect can claim it again.

CultNetRudpSession::answer_repeated_connect is the one answer to a repeated
Connect for the hub and the document server: it records the sequence (the
document server's old accept_connect did, and a client that reconnects in one
session depends on it), applies acks, queues nothing, and returns the pending
Accept or an Ack.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:35:30 +02:00
Meta Vens
f9034eedb0 cultmesh-rs: a repeated Connect re-sends the pending Accept and queues nothing
The document server called accept_connect again for a Connect on an existing
key, queuing another reliable Accept each time: a resend storm, and the full
queue Err that the previous commit turned into a drop. The repeat now re-sends
the Accept still pending, or an Ack once acknowledged, as the hub does. A fresh
session's accept_connect cannot fail under validated options, so its error is
propagated again rather than counted as a drop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:33:30 +02:00
Meta Vens
f6dbbf77ef cultnet-rs: end the session of a peer whose packet the session refuses
CultNetRudpSession::receive records a reliable sequence before reassembly, so
keeping the session after it refuses a frame made the sender's retransmit be
acknowledged and the frame silently lost. The hub and the socket transport now
end that peer's session, send it a Disconnect, and surface the end to the
caller (a Disconnected event, disconnect_reason()). The hub and socket
constructors reject initial_sequence u32::MAX and max_pending_reliable_packets
Some(0), so the accept_connect drops are unreachable. pending_accept_for_resend
becomes public for the document server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:33:30 +02:00
Meta Vens
adb4047936 TypeScript, Python and Rust read the v3 header and keep it on rewrite
A store that can hold element ids declares cultcache.store.v3. These runtimes pass payload
bytes through, so they read it and write back the header they found; they do not decide
when a document carries ids. Vector: tests/vectors/document-variants-c2a/v3-base.msgpack.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 18:27:17 +02:00
Meta Vens
2ef7ae92c4 cultnet-rs: pin every RUDP stray drop site with an exact count
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 18:21:58 +02:00
Meta Vens
6b3da283b4 cultmesh-rs: pin each RUDP document server drop site with an exact count
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 18:12:09 +02:00
Meta Vens
dbe4f8a850 cultnet-py: RUDP socket transport drops and counts strays
A malformed frame, a foreign connection id or a refused packet raised out of
receive_once. They are now counted in stats.packets_dropped and receive_once
returns None. The id is checked before the peer endpoint is claimed, so a
stray arriving first can no longer become the server-mode peer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 17:48:10 +02:00
Meta Vens
ecb80b4fd8 cultnet-ts: RUDP socket transport drops and counts strays instead of emitting error
A foreign connection id, a malformed frame or a refused packet was emitted
as an error event; with no listener that throws and ends the process. They
are now counted in stats.packetsDropped. The id is checked before the peer
endpoint or session state is touched, and an unadmitted first packet in
server mode no longer claims the endpoint. Listener faults still surface as
error events.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 17:44:37 +02:00
Meta Vens
ba8466114d cultmesh-rs: RUDP document server drops refused Connects instead of dying
A Connect storm from a flow that never hears its Accept filled the session
reliable queue and accept_connect returned Err out of poll_once. A refused
Connect, a malformed frame and a packet for an unknown session are now
counted in packets_dropped() and poll_once keeps serving.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 17:38:20 +02:00
Meta Vens
7b7666f2c3 cultnet-rs: drop and count packets that belong to no session
A foreign connection id, a malformed frame, a sender no Connect admitted, a
Connect with the peer table full, and any packet the session refuses ended
the transport loop with an Err. Odin died twice this way after nginx reloads
moved its UDP flows (require_connection: connection id does not match).

The hub and the socket transport now drop such packets and count them in
CultNetTransportStats::packets_dropped. Only socket I/O errors leave the
loop. The socket transport checks the connection id before it touches the
peer endpoint or session, so a stray Connect can no longer move the endpoint
or reset the session.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 17:38:20 +02:00
Meta Vens
884602c708 Merge origin/main into variants-c2a 2026-09-29 16:35:00 +02:00
Meta Vens
640ad9a437 Interop: an element id crosses C#, TypeScript, Python and Rust
The C# cultcache interop peer gains an object-list document (a deck of marks).
C# writes it with cache-minted ids; TypeScript, Python and Rust read the ids as
ordinary members, and TypeScript-written ids survive a C# read.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 15:03:09 +02:00
Meta Vens
09dabd38c5 CultNet: witness element types carry ids; peers and fixtures follow
CultWitnessContractPin, CultWitnessArtifactEntry and CultWitnessTimingEntry get
an id slot. The JSON schema contract and the Python schema copy gain an optional
id per element, and the Python and TypeScript fixtures carry and assert ids.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 15:03:09 +02:00
Meta Vens
ded5436569 CultCache: derived element ids; the CDN chunk ref carries one
[CultElementId(nameof(Member))] declares an id derived from that member's
invariant text, never random, so content-addressed elements keep encoding to the
same bytes. CultMeshCdnChunkRef derives its id from Offset; PackArtifact sets
it, the Rust pack_content writes it, and the committed content vectors are
re-cut once so both runtimes carry it. Engine tests drive Problems and Assign
directly (InternalsVisibleTo the Caching tests).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 15:03:09 +02:00
Meta Vens
65311c9ad7 Merge the snoise seam fix (Asura Q-N1 a): upstream kernel r2=0.5, scale 105, permute +10
Soul: no defect; independent port of stegu 22434e04d7 matches (3D and 2D), goldens
confirmed independently, value jump 2.2e-6 over 20k crossings, gradient FD 9.7e-6,
every hand mutant killed. 2D follows upstream by ruling. Release follow-ups: rebuild
the Unity CultMath.dll, CHANGELOG entry, pin the two HLSL copies equal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:55:08 +02:00
Meta Vens
93fe1b9c8e Merge CultMath bounded least squares (locomotion Cut 2)
Closed after three Soul passes: the double iterate and progress stop hold over
~9000 cases against exact enumeration, zero allocation, deterministic; the stall
rule and fScale are now pinned (all six survivors killed). Ships in the next
CultMath release with Asura. Recorded, outside the documented range: 18/6000
gap misses at cond 1e5-3e5 with A scaled 1e-4..1e4 (solver or reference unknown).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:22:54 +02:00
Meta Vens
09507901d0 cultmath: BoundedLeastSquares doc states the cost gap is relative to |b|^2
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 13:14:21 +02:00
Meta Vens
c8227c6347 cultmath: BoundedLeastSquares tests drive stalled passes to the stall rule
Batches of small boxed problems whose passes end with the KKT test failing and
the cost unmoved (dependent free columns, releases the factor drops, weak-direction
noise-floor problems). They must report Converged, and land within the documented
gap of an exact active-set enumeration. Pins the stall comparison and the cost
scale in Objective, which no test reached before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 13:14:21 +02:00
x
0a5f4bf571 CultMath: regenerate snoise golden values for the upstream kernel
Seven values change: four snoise(float3) in HlslSemanticsTests and three snoise(float2)
in MathTests (the 2D (0,0) golden is 0 either way). Regenerated from the code after the
continuity probe passed. The tests no longer claim to match Unity.Mathematics, whose
snoise keeps the old constants.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 12:09:43 +02:00
x
641041822d CultMath: noise gradient tests carry only numbers the harness produces
Drops the unreproducible floors and detection-scale claims, and the seam-fluke
consistency filter (the spikes it hid were the snoise seam, now fixed and pinned by the
continuity probe). Tightens the tolerances to at least 5x the harness maximum (ridged
0.5 -> 0.05), corrects the point count in the bit-equality test, and caps the ridged
compounding loop so a mutant fails loudly instead of hanging.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 12:04:08 +02:00
Meta Vens
8703b44636 CultMath: snoise(float3) takes the upstream simplex-boundary fix
Kernel radius^2 0.6 -> 0.5, scale 42 -> 105, permute (34x+1)x -> (34x+10)x, per
stegu/webgl-noise 22434e04d7 (fix 21d9fe23d7, scale ff3b5d34ea, permute a3e6d57095).
snoise_grad, fbm_grad, ridged_grad follow. C#, both HLSL copies, design.md and
THIRD-PARTY-NOTICES.md change together. The 2D snoise shares the permute helper, so
its values change too. Golden values are regenerated in the next commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 11:13:34 +02:00
Meta Vens
4ca1cf388a CultMath: continuity probe for snoise across simplex-cell boundaries (red on the 0.6 kernel)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 11:10:18 +02:00
Meta Vens
468915b058 Merge remote-tracking branch 'origin/main' into hands/cultmath-snoise-grad 2026-09-29 11:09:07 +02:00
Meta Vens
6328f15345 cultmath: BoundedLeastSquares test drives the tolerance below the noise floor
With b along the smallest left singular vector, |A^T b| is tiny, so the KKT
tolerance (1e-6 of it) sits below the gradient's rounding noise at cond(A) >=
1e6 and can never be met. The solver must stop on the progress rule, in at most
six passes, never at the cap. This is the path the progress rule exists for;
the earlier fixtures reached it only by accident.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 10:52:39 +02:00
Meta Vens
854f01e4c7 cultmath: BoundedLeastSquares tests pin the tolerance scale and the zero gradient
Two tolerance behaviours had no observer: the scale taken at the box point
nearest the origin (a 6e-5 release on a 1000-scale problem, in either column
position), and the strict comparisons when the scale and the gradient are both
exactly zero (b = 0 at the origin takes no iteration).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 10:37:27 +02:00
Meta Vens
cbd3b4f3ce CultNet: a withdrawn hop subscription is refused with a typed error and its rows are withdrawn
Reconcile's withdrawal sent an untyped error with no subscription id and left the
peer holding the rows the subscription had delivered. It now sends removals for
those rows, then a variant_unsupported error whose details carry
field "subscriptionId" and the id (the existing details slots; no new wire key).
The subscribe and per-change paths use the same code. The error schema and the
Rust CultNetErrorCode accept variant_unsupported, so a Rust peer decodes it
instead of refusing an unknown code.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 10:12:20 +02:00
Meta Vens
0a80624930 cultmath: BoundedLeastSquares tests observe the warm-start clamp and the workspace
The full-step clamp repairs an out-of-box start on a free column, so the
warm-start clamp was invisible to the old test (Stryker: five survivors). The
new table runs one column with b beyond the box and a cap of 0, where the
solver returns the clamped start untouched, across finite, infinite and NaN
starts and finite and infinite bounds.

Workspace junk (including NaN doubles) is now checked on random boxed problems
that release bounds and use the tolerance, with bit-identical x and equal
iteration counts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 09:44:04 +02:00