F7 retires schema aliasing (one type presented as another for the same schema). The Rust and Kotlin rows called their snapshot hydration "same-schema alias" hydration. Both decode a raw record by its schema id into the one type the receiving runtime registered for that schema; neither has a handle aliasing API. The rows now say that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
139 lines
12 KiB
Markdown
139 lines
12 KiB
Markdown
# Runtime Parity Scope
|
||
|
||
This is the claim CultLib can make without turning evidence into incense.
|
||
Parity means the targeted runtimes speak the same wire language and expose
|
||
similar developer-facing entrypoints for the roles they own. It does not mean
|
||
every runtime clones the C# production server, LiteNetLib history, or hot-loop
|
||
memory layout.
|
||
|
||
## Objective
|
||
|
||
Cross-runtime parity has three layers:
|
||
|
||
- Wire parity: shared CultCache file/state contracts, schema-v0 MessagePack
|
||
documents, transport profiles, and RUDP packets must cross runtime boundaries
|
||
without translation folklore.
|
||
Every store reader refuses, by name, a header or record slot it does not
|
||
understand (vectors: `tests/vectors/document-variants-c0`).
|
||
Element ids: a store that can hold them declares `cultcache.store.v3`; TypeScript,
|
||
Python and Rust read it and keep the header, and readers older than ids refuse it
|
||
(vectors: `tests/vectors/document-variants-c2a`).
|
||
Document variants: C# resolves them (`cultcache.store.v2`); every other runtime,
|
||
CultMesh single-file reads, the directory store and CultNet refuse a variant by name
|
||
(vector written by C#: `tests/vectors/document-variants-c1`).
|
||
- Feature parity: each runtime must expose the CultCache/CultNet/CultMesh
|
||
surfaces needed for its intended role, with unsupported production-server
|
||
ownership called out plainly.
|
||
- Ergonomic parity: callers should find a branded `CultMesh` entrypoint,
|
||
typed catalogs, discovery helpers, authority checks, stream surfaces, and
|
||
transport helpers instead of assembling raw maps and channel strings.
|
||
|
||
SoA performance parity is deliberately not a cross-runtime requirement. C#
|
||
owns the shared-memory streaming body path. The C# CultCache SoA table (ECS-style
|
||
structure-of-arrays columns over cached documents) is parked at the tag
|
||
`parked/cultcache-soa`: it was built for hot-loop performance, had no consumer
|
||
outside tests, and is restorable from that tag (`docs/parked-features.md`).
|
||
It is not a supported surface in the current tree. Other runtimes may add native hot paths when their
|
||
runtime role earns them, but they do not need to mimic Unity/C# memory layout
|
||
to speak CultNet or participate in CultMesh.
|
||
|
||
## Current Targeted Runtimes
|
||
|
||
| Runtime | Role | Claimed parity | Not claimed |
|
||
| --- | --- | --- | --- |
|
||
| C# | Reference implementation and production game/server substrate | CultCache typed documents, CultNet schema-v0, LiteNetLib production path with adapter profile, shared transport profiles, shared RUDP socket transport, C# multi-peer RUDP listener and schema service host, C# interop RUDP serve/dial path, connected RUDP CultMesh client helpers, RUDP-backed schema-client selection for `rudp://` service endpoints, RUDP reconnect policy/controller and reconnect-loop helper, LiteNetLib client reconnect-controller adoption, schema-client transport port for service request/response clients, CultMesh node/session/catalog/stream/simulation/RUDP surfaces, authorized peer lookup | No need to abandon LiteNetLib before the CultNet adapter fully owns production service adoption |
|
||
| Python | Package runtime, local daemon endpoint, raw interop peer, typed state participant | CultCache/CultNet/CultMesh package surface, branded CultMesh node/schema/shard/Verse/peer/authority/stream/RUDP ergonomics, authorized peer lookup, shard catalogs/logs, schema-transport factory for raw client request/subscription flows, local TCP and RUDP CultMesh server listeners, Python interop RUDP serve/dial path, session/prediction/simulation fact helpers, RUDP codec/session/socket transport, RUDP reconnect policy/controller and reconnect-loop helper, schema-v0 MessagePack over RUDP evidence, benchmark evidence against C# cache paths | Universal C# throughput, full C# daemon/server stack, and RUDP as the only public service pipe |
|
||
| TypeScript | Node/browser-adjacent projection and interop runtime | CultCache parity tests, CultNet schema-v0 peer, advertised schema/shard catalog wire contracts plus request/sync helpers, transport profiles, TCP-framed transport port and peer helper, shared RUDP socket transport, RUDP interop serve/dial path, RUDP reconnect policy/controller, RUDP reconnect-loop helper, branded CultMesh node/schema/shard/Verse/peer/authority/stream/RUDP ergonomics, authorized peer lookup | Full C# game session/server stack |
|
||
| Rust | Low-level cache/transport/runtime substrate | CultCache/CultNet interop lane, schema and shard catalog helpers, typed hydration of raw snapshots into the receiver's own registered type for each schema id, transport profiles, TCP-framed port, shared RUDP codec/session/socket transport, Rust interop RUDP serve/dial path, connected RUDP CultMesh client helpers, RUDP reconnect policy/controller and reconnect-loop helper, schema-v0 MessagePack over RUDP evidence, small CultMesh facade for schema registry, shard catalog, peer cards, authority leases, authorized peer lookup, RUDP endpoint parsing, and peer-card/authorized-peer RUDP client construction | Full CultMesh game-session/server facade or production game-session server. `packages/cultmesh-rs` now holds a Rust stream and session surface -- node, document sets, RUDP document publication, stream descriptors, negotiation, stream catalog, and shared-memory frame rings -- but no interop lane exercises it, so it carries no cross-runtime parity claim |
|
||
| Kotlin | JVM/Android client substrate | Typed MessagePack cache, one-call typed snapshot document sync into the receiver's own registered type for the schema, built-in schema catalog advertisement, schema/shard catalogs with shared schema-message transport fetch/sync helpers, authority leases, authorized peer lookup, stream catalog, branded `CultMesh` facade, WebSocket transport profile/connection helper, RUDP client/server sugar, connected RUDP CultMesh client helpers, Kotlin interop RUDP serve/dial path, RUDP reconnect policy/controller and reconnect-loop helper, schema-v0 MessagePack over RUDP evidence | C# SoA store (parked at `parked/cultcache-soa`), full C# server daemon, or Unity hot-loop memory layout |
|
||
|
||
## Evidence Gates
|
||
|
||
- `.github/workflows/cultnet-interop.yml` is the hosted CI witness for the
|
||
cross-runtime wire claim. It builds the TypeScript workspace, Kotlin interop
|
||
peer, Rust/C# interop peers through the TypeScript harness, installs the
|
||
Python package dependencies, then runs named RUDP lanes for Python, Rust, C#,
|
||
and Kotlin, the RUDP impairment matrix, and the schema-v0 discovery lane.
|
||
- `packages/cultnet-ts/test/interop/cultnet-interop.test.ts` is the main live
|
||
wire harness. It covers schema-v0 interop and RUDP exchange across C#,
|
||
TypeScript, Rust, Python, and Kotlin, including loss, reordering,
|
||
fragmentation, disconnect reason propagation, ping/pong, and schema-v0
|
||
MessagePack over RUDP for C#, Rust, Python, and Kotlin peers against
|
||
TypeScript. Those RUDP schema-message tests also assert the advertised
|
||
`reconnectPolicy` profile field from C#, Rust, Python, and Kotlin peers.
|
||
Runtime-local tests cover the deterministic delay helper and reconnect
|
||
controller in C#, TypeScript, Rust, Python, and Kotlin.
|
||
- `docs/python-runtime-parity.md` is the Python-specific audit ledger. Its
|
||
"Still Not Claimed" section is part of the contract, not an apology.
|
||
- `docs/cultnet-transport-parity.md` is the transport ownership map. It tracks
|
||
the remaining work to lower older LiteNetLib/WebSocket bodies behind the
|
||
shared transport port and expand RUDP adoption beyond the core proof.
|
||
- `packages/cultmesh-kotlin/README.md` records Kotlin's runtime role and
|
||
ergonomic surface, including the explicit non-claim around C# SoA storage.
|
||
- CultNet typed selection (`cultnet.snapshot_response_raw.v1`, C#/Rust only;
|
||
docs/cultnet-selection-cut.md, R-AY): byte parity for the
|
||
`snapshot_response_raw.v1` envelope and its nested record types, verified at
|
||
populated and absent optional values, plus semantic parity over the named
|
||
field subset. `packages/cultnet-rs/tests/selection_wire_parity.rs` and
|
||
`tests/GameCult.Networking.Tests/SelectionWireParityTests.cs` carry the byte
|
||
vectors; `packages/cultnet-rs/tests/selection.rs`'s
|
||
`write_selection_vectors_for_the_reference` and
|
||
`tests/GameCult.Networking.Tests/SelectionParityVectorTests.cs` carry the
|
||
semantic ones. A single rows-to-bytes path driving both halves in one call
|
||
remains its own cut (R-AR) and is not part of this claim.
|
||
- CultMesh content plane (`cultmesh.content_chunk_request.v1` and `.response.v1`
|
||
plus the CDN manifest, C#/Rust only): byte parity in both directions (manifest metadata keeps its wire key order and a
|
||
duplicate key is refused, as the reference refuses it), and the reference's answer
|
||
to a chunk request, including its failure spellings: every bad request is
|
||
answered `found: false`, never refused at decode.
|
||
`packages/cultnet-rs/tests/content.rs` and
|
||
`tests/GameCult.Mesh.Tests/CultMeshContentVectorTests.cs` judge
|
||
`contracts/cultmesh/content-vectors.cs-written.json` and
|
||
`content-vectors.rs-written.json`. Rust carries these chunks only on the
|
||
session the client already holds (see `src/GameCult.Mesh/docs/transport-planes.md`);
|
||
it has no authenticated content connector, so it claims no TCP+TLS or QUIC
|
||
content path.
|
||
Recorded limits: a caller that passes `max_bytes = u64::MAX` to `fetch_content` gets an unbounded
|
||
body, because the reference refuses artifacts over `int.MaxValue` bytes and Rust bounds them only by the
|
||
caller's cap.
|
||
`normalize_hash` matches the `sha256:` prefix ASCII-case-insensitively while C# `OrdinalIgnoreCase` may
|
||
also match non-ASCII forms (for example `ſha256:`); unprobed, and it affects hostile input only.
|
||
|
||
## Expansion Work Outside The Current Parity Claim
|
||
|
||
The current claim is role-scoped feature, wire, and ergonomic parity across the
|
||
targeted runtimes. The stronger claim that every production daemon/service body
|
||
has already moved to native RUDP, or that every runtime clones the C# production
|
||
server body, is deliberately not made.
|
||
|
||
- Broaden production service adoption so remaining daemon paths use the shared
|
||
transport port consistently. The C# built-in LiteNetLib service wrappers now
|
||
register transport-aware `CultNetServerPeer` listeners instead of requiring
|
||
`NetPeer` callbacks, and TypeScript TCP peer creation has a transport-first
|
||
helper. C# Verse discovery, peer exchange, shard-log fetch, shard-snapshot
|
||
fetch, and shard write forwarding clients now consume a schema-client
|
||
transport port and select the C# RUDP schema client for `rudp://` endpoints
|
||
while keeping LiteNetLib for `cultnet://` endpoints. Broader daemon adoption
|
||
remains the unclaimed layer.
|
||
- Extend RUDP adoption through daemon/socket reconnect loops. RUDP profiles now
|
||
advertise the portable reconnect policy, runtime-local tests prove the shared
|
||
controller across the targeted runtimes, and the C# LiteNetLib client plus
|
||
C#/TypeScript/Rust/Python/Kotlin RUDP reconnect loops use that controller;
|
||
broader service adoption remains a separate claim.
|
||
- Keep Kotlin ergonomic parity moving with Android/JVM-shaped APIs instead of
|
||
backfilling C# server responsibilities it should not own. Kotlin WebSocket
|
||
and RUDP adapters now share one `CultNetSchemaMessageTransport` helper shape
|
||
for schema/shard catalog request and sync flows.
|
||
- Bring `packages/cultmesh-rs` under the wire claim. It arrived from a consumer repo where it grew
|
||
outside any conformance harness, and its stream, negotiation, and shared-memory frame-ring
|
||
surfaces have never been checked against the browser, Kotlin, and TypeScript CultMesh bodies
|
||
they must interoperate with. Runtime-local tests pass; that is not a parity claim.
|
||
- Carry the RUDP receive-path bounds to the other runtimes. Rust now caps tracked ordered
|
||
and sequenced channels, caps the out-of-order hold buffer by frames and bytes, refuses a
|
||
packet far ahead of the highest received so a forged sequence cannot starve a session, and
|
||
reports reliable sequence exhaustion instead of aborting. C#, TypeScript, Python and Kotlin
|
||
still accept all of it. These are refusals rather than wire-format changes, so the interop
|
||
lanes stay green either way -- which is exactly why the gap will not announce itself.
|
||
- Keep performance claims evidence-bound: C# owns SoA; Python has benchmark
|
||
comparison gates; other runtimes should advertise measured native hot paths
|
||
only when those paths exist.
|