diff --git a/.gitignore b/.gitignore
index d639c709..2fdb5151 100644
--- a/.gitignore
+++ b/.gitignore
@@ -206,6 +206,17 @@ PublishScripts/
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
+!/packages/gamecult-geometry-rs/
+!/packages/gamecult-geometry-rs/**
+packages/gamecult-geometry-rs/target/
+.tools/
+!/packages/gamecult-geometry-ts/
+!/packages/gamecult-geometry-ts/**
+!/unity/org.gamecult.geometry/Samples~/
+!/unity/org.gamecult.geometry/Samples~/**
+!/unity/org.gamecult.geometry/Runtime/PlanetaryPatchMeshAdapter.cs.meta
+!/unity/org.gamecult.geometry/Runtime/PlanetaryPageUpload.cs.meta
+!/src/GameCult.Geometry/Shaders/*.hlsl.meta
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
@@ -241,6 +252,8 @@ _pkginfo.txt
ClientBin/
~$*
*~
+!/unity/org.gamecult.geometry/Samples~/
+!/unity/org.gamecult.geometry/Samples~/**
*.dbmdl
*.dbproj.schemaview
*.jfm
diff --git a/CultLib.sln b/CultLib.sln
index f6ae25e9..0319071d 100644
--- a/CultLib.sln
+++ b/CultLib.sln
@@ -29,6 +29,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Geometry", "src\Ga
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Geometry.Tests", "tests\GameCult.Geometry.Tests\GameCult.Geometry.Tests.csproj", "{C95A9A21-34FA-4D82-BE5B-88FBDA836D31}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Geometry.Planetary.Tests", "tests\GameCult.Geometry.Planetary.Tests\GameCult.Geometry.Planetary.Tests.csproj", "{CE447820-B407-48DE-8957-F373E032C4AE}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameCult.Mesh.Tests", "tests\GameCult.Mesh.Tests\GameCult.Mesh.Tests.csproj", "{F8A5060A-FB0B-4B8B-AE06-5147BB4CA127}"
EndProject
Global
@@ -173,6 +175,18 @@ Global
{C95A9A21-34FA-4D82-BE5B-88FBDA836D31}.Release|x64.Build.0 = Release|Any CPU
{C95A9A21-34FA-4D82-BE5B-88FBDA836D31}.Release|x86.ActiveCfg = Release|Any CPU
{C95A9A21-34FA-4D82-BE5B-88FBDA836D31}.Release|x86.Build.0 = Release|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Debug|x64.Build.0 = Debug|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Debug|x86.Build.0 = Debug|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Release|x64.ActiveCfg = Release|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Release|x64.Build.0 = Release|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Release|x86.ActiveCfg = Release|Any CPU
+ {CE447820-B407-48DE-8957-F373E032C4AE}.Release|x86.Build.0 = Release|Any CPU
{F8A5060A-FB0B-4B8B-AE06-5147BB4CA127}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8A5060A-FB0B-4B8B-AE06-5147BB4CA127}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8A5060A-FB0B-4B8B-AE06-5147BB4CA127}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -201,6 +215,7 @@ Global
{EB15435B-7147-4BD9-BB9C-B0A14318D3D5} = {39D316AB-ABB5-49A2-8E83-CAF4C75B48F4}
{94C086DA-3359-424B-9688-29A198806481} = {E733CBFF-42AE-4B6A-B5B9-0071ECFB45F1}
{C95A9A21-34FA-4D82-BE5B-88FBDA836D31} = {39D316AB-ABB5-49A2-8E83-CAF4C75B48F4}
+ {CE447820-B407-48DE-8957-F373E032C4AE} = {39D316AB-ABB5-49A2-8E83-CAF4C75B48F4}
{F8A5060A-FB0B-4B8B-AE06-5147BB4CA127} = {39D316AB-ABB5-49A2-8E83-CAF4C75B48F4}
EndGlobalSection
EndGlobal
diff --git a/Directory.Build.props b/Directory.Build.props
index 519e7a93..e38b66bc 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -15,6 +15,8 @@
© Meta Vens and contributors
true
1.0.3
+ $(MSBuildThisFileDirectory)..\CultMath\.tools\local-feed
+ $(RestoreAdditionalProjectSources);$(CultMathCandidateFeed)
true
diff --git a/README.md b/README.md
index d6e3778c..80dc90a5 100644
--- a/README.md
+++ b/README.md
@@ -79,7 +79,7 @@ The solution includes:
- `GameCult.Caching.NewtonsoftJson`: Newtonsoft.Json-backed persistence for the cache
- `GameCult.Caching.MessagePack.Generator`: source generator for MessagePack formatters for cache models
- `GameCult.Caching.MessagePack.Analyzers`: packaging project that delivers the generator to consuming projects
-- `GameCult.Geometry`: CultCache-native geometry domain, selected-cut, and chunk artifact documents for VibeGeometry/Fensalir-style pipelines
+- `GameCult.Geometry`: geometry, planetary mapping, CSG, LOD selection, mesh assembly, and CultCache-native geometry documents built on CultMath primitives
- `GameCult.Networking`: encrypted login/register/verify flows, schema-v0 contracts, transport adapters, and native RUDP sessions
- `GameCult.Mesh`: CultMesh package home for distributed realtime database, shard replication, client prediction, Verse discovery, and mesh witness consensus
- `GameCult.Caching.Tests`: NUnit tests for cache and backing-store behavior
diff --git a/docs/cultmath-gamecult-geometry-migration-plan.md b/docs/cultmath-gamecult-geometry-migration-plan.md
new file mode 100644
index 00000000..e8e961c4
--- /dev/null
+++ b/docs/cultmath-gamecult-geometry-migration-plan.md
@@ -0,0 +1,552 @@
+# CultMath and GameCult.Geometry Staged Migration Plan
+
+Status: proposed for execution
+
+Date: 2026-07-22
+
+## Decision
+
+`GameCult.Geometry` is the sole owner of geometric meaning and geometry
+machinery. It builds on CultMath numeric primitives and participates in
+CultCache, CultNet, and CultMesh through the normal CultLib integration paths.
+
+CultMath owns the HLSL-shaped numeric substrate: scalar operations, vectors,
+quaternions, and general numeric kernels that have no spatial domain authority.
+It does not own regions, topology, projections, surfaces, planetary fields,
+patches, geometry residency, meshes, or geometry workflow state.
+
+VibeGeometry and `vg-csg` are sources to mine. They are not surviving owners or
+dependencies.
+
+Source transfer does not erase provenance. CultMath is MPL-2.0 and CultLib is
+MIT-licensed; moved files require an explicit licensing decision and preserved
+notices before they enter a differently licensed package.
+
+## Objective
+
+Produce one coherent geometry stack in which:
+
+- consumers use `CultMath.float2`, `float3`, and `float4` as the canonical
+ floating-point vector primitives;
+- `GameCult.Geometry` owns reusable geometry, planetary geometry, CSG, mesh
+ production, spatial queries, and the typed documents that coordinate those
+ systems;
+- CultMath no longer publishes planetary or other geometry-owned APIs;
+- `CultVec2` and `CultVec3` no longer exist;
+- VibeGeometry can be archived after its useful implementation, evidence, and
+ doctrine have been absorbed;
+- persisted geometry remains readable through an explicit migration path when
+ a live persisted corpus requires one.
+
+## Authority Map
+
+| Organ | Owns | Does not own |
+| --- | --- | --- |
+| CultMath | `float2`, `float3`, `float4`, integer/double/bool vector primitives, quaternion algebra, scalar and component-wise intrinsics, coordinate-agnostic numeric kernels, CPU/HLSL parity for those primitives | Rectangles, spheres, topology, projections, planetary fields, geometry pages, meshes, CSG, persistence schemas, worker commands |
+| GameCult.Geometry | Spatial value types built from CultMath primitives, spatial queries, topology and projection, planetary field and page machinery, mesh and CSG implementation, geometry schema documents, stable geometry identities, geometry-specific HLSL and cross-runtime fixtures | Competing vector types, generic storage/network ownership, application gameplay consequences |
+| CultCache/CultNet/CultMesh | Typed persistence, schema resolution, transport, discovery, publication, commands, and interface projection | Geometry semantics or geometry algorithm ownership |
+| VibeGeometry / `vg-csg` | Nothing after migration | Any live runtime, schema, package, or compatibility authority |
+
+Authority sentence:
+
+> CultMath owns how numeric primitives behave; GameCult.Geometry owns what those
+> primitives mean in space.
+
+## Current Mechanism
+
+The live body is split across three places:
+
+1. CultMath publishes the canonical HLSL-shaped vector types, but also contains
+ `rect`, planetary topology/projection/field/page/residency systems, spherical
+ erosion, planetary HLSL, Unity adapters, web fixtures, and planetary tools.
+2. `GameCult.Geometry` contains duplicate `CultVec2` and `CultVec3` values,
+ geometric primitives, and four CultCache document roots. Its document
+ members still encode many vectors, bounds, transforms, vertices, normals,
+ and UVs as anonymous `float[]` values.
+3. VibeGeometry wraps the `vg-csg` Rust implementation for domain-tree intent,
+ LOD-aware selected cuts, CSG lowering, and triangle/collider output.
+
+CultCache's current managed SoA projection only admits primitive scalar members.
+It neither preserves arbitrary unmanaged structs as columns nor recursively
+splits them. A `float2` or `float3` member is therefore omitted from the SoA
+until that storage seam is repaired.
+
+## Invariants
+
+The migration must preserve these statements at every published boundary:
+
+1. There is one canonical vector vocabulary: CultMath's HLSL-shaped primitive
+ types.
+2. Geometry algorithms and geometry state have one owner: `GameCult.Geometry`.
+3. CultMath never depends on CultLib, CultCache, MessagePack, or
+ `GameCult.Geometry`.
+4. `GameCult.Geometry` may depend on CultMath and CultLib.
+5. Persistence decoration and formatters do not contaminate CultMath primitive
+ definitions.
+6. A geometry schema has one active writer. Legacy readers or offline
+ migrators may exist temporarily, but they cannot decide live behavior.
+7. Record keys and stable fingerprints are identical across supported runtimes
+ for the same semantic geometry value.
+8. SoA storage preserves each authored unmanaged member as one column of its
+ exact type. It never decomposes `float2`, `float3`, quaternion, or another
+ value type by reflection.
+9. CPU, HLSL, Unity, web, and Rust fixtures compare the same claimed layer of
+ geometry behavior.
+10. VibeGeometry is not retained as a wrapper, submodule, compatibility crate,
+ or second source of truth.
+
+## Target Package Shape
+
+The exact directory names may follow existing CultLib conventions, but the
+ownership should lower into these runtime surfaces:
+
+```text
+CultMath
+ C# and Unity numeric primitives
+ CultMath.hlsl numeric mirror
+ coordinate-agnostic native numeric kernels
+
+CultLib
+ src/GameCult.Geometry/
+ C# geometry API
+ planetary geometry
+ CultCache document contracts and formatters
+ geometry-specific HLSL packaged as content
+ packages/gamecult-geometry-rs/
+ Rust geometry, CSG, and cross-runtime fixtures
+ packages/gamecult-geometry-ts/ # only if the web runtime remains earned
+ web projection/tile helpers and parity fixtures
+ src/GameCult.Unity/Assets/Geometry/ # Unity-owned lowering/adapters
+ mesh/page upload and presentation adapters
+ Packages/org.gamecult.geometry/ # final location follows packaging audit
+ Unity package depending on CultMath and CultLib
+```
+
+The runtime directories are projections of one geometry authority. They are not
+independent products allowed to invent their own vector, topology, schema, or
+fingerprint semantics.
+
+Each stage is a commit/review boundary. Do not begin the next stage until the
+current gate passes, and never publish a state in which both repositories claim
+the same geometry authority.
+
+## Source Classification
+
+### Keep in CultMath
+
+- HLSL-shaped primitive types such as `float2`, `float3`, and `float4`.
+- Integer, double, and boolean vector primitives.
+- Quaternion algebra.
+- `math` scalar/component-wise operations and the core `CultMath.hlsl` mirror.
+- Numeric batch kernels whose contracts are independent of regions, topology,
+ surfaces, planets, meshes, CSG, rendering policy, and persistence.
+
+### Move from CultMath to GameCult.Geometry
+
+The initial move set is:
+
+- `rect` and other spatial-region values;
+- `PlanetaryTopology`;
+- `PlanetaryProjection`;
+- `PlanetaryQueries`;
+- `PlanetaryPatch`;
+- `PlanetaryPages`;
+- `PlanetaryGpuPages`;
+- `PlanetaryResidency`;
+- `PlanetaryRadialRefinement`;
+- `PlanetaryMapTileEncoding`;
+- `PlanetaryField`;
+- `AdvancedErosionFilter`;
+- `ErosionFrequencyBands`;
+- `SphericalErosion`;
+- `AdvancedErosionFilter.hlsl`;
+- `Planetary.hlsl`;
+- spherical-erosion and planetary-radial-refinement shader bodies currently
+ embedded in `CultMath.hlsl`;
+- the versioned `CMPT` planetary tile format and its TypeScript decoder;
+- planetary tests, docs, tools, Unity adapters, web fixtures, and samples.
+
+`AdvancedErosionFilter` and `ErosionFrequencyBands` move with planetary
+geometry. Their public contracts encode terrain-field policy, they are direct
+dependencies of the planetary field definition, and their HLSL is coupled to
+that field. Voronoi tone rendering and `native/cultmath-core` are outside this
+migration: the native crate currently owns only that tone kernel and has no
+planetary implementation. Audit those separately rather than dragging them
+across this boundary because their names look spatial.
+
+### Rebuild inside GameCult.Geometry
+
+- Make `CultRect`, `CultCircle`, and `CultSphere` the GameCult.Geometry-owned
+ spatial values, implemented over CultMath primitives, and delete
+ `CultMath.rect` at cutover.
+- Replace `CultVec2` and `CultVec3` with `float2` and `float3` at every typed
+ boundary.
+- Replace vector-shaped `float[]` document members with typed CultMath values or
+ intentional packed buffers.
+- Keep packed mesh buffers packed when they are genuinely bulk artifact storage;
+ do not turn every vertex buffer into a forest of object wrappers.
+- Put MessagePack/CultCache formatters for CultMath values in the integration
+ package or generated formatter output, not on the CultMath types.
+
+### Mine from VibeGeometry and `vg-csg`
+
+Evaluate and extract, in dependency order:
+
+- convex plane splitting and brush operations;
+- CSG tree and ordered operation semantics;
+- domain-tree DSL and lowering;
+- dirty-frontier and prefix-checkpoint work;
+- selected-cut and LOD planning;
+- triangle/collider assembly;
+- performance fixtures and parity evidence;
+- useful RealtimeCSG-derived behavioral fixtures;
+- runtime doctrine that still describes the adopted machine.
+
+Rewrite extracted code under GameCult.Geometry ownership. Preserve third-party
+license notices and source provenance. Do not retain a `vg-csg` forwarding
+crate or a VibeGeometry submodule after consumers have moved.
+
+## Staged Migration
+
+### Stage 0: Freeze and Baseline
+
+Purpose: establish the evidence needed to move authority without guessing.
+
+Work:
+
+- Freeze new planetary and geometry API additions in CultMath, VibeGeometry,
+ and the current duplicate primitive surface.
+- Do not publish an intermediate release containing duplicate live planetary
+ implementations. A coordinated working branch may hold source temporarily
+ for comparison, but only one owner may enter a package feed.
+- Inventory public symbols, package versions, HLSL entrypoints, Unity samples,
+ web fixtures, native exports, persisted schema IDs, record-key algorithms,
+ and known consumers.
+- Inventory exact contract constants: `FieldVersion`, enum ordinals, tile stable
+ keys, `CMPT` versions and bytes, MessagePack slots, GPU buffer layouts,
+ integer hashes, topology face/seam/corner rules, and shader include paths.
+- Search deployed and checked-in `.cc` stores for the four
+ `gamecult.geometry.*.v1` schemas.
+- Record whether any external consumer relies on CultMath planetary namespaces,
+ `CultVec*`, `CultMath.rect`, `vg-csg`, or the current HLSL function names.
+- Capture passing baselines for CultMath tests, GameCult.Geometry tests,
+ `vg-csg` tests/clippy, cross-runtime planetary fixtures, and package builds.
+- Add representative fixture hashes for projection, topology, field sampling,
+ patch selection, page encoding, CSG output, and geometry record keys.
+- Resolve licensing and provenance for MPL-2.0 CultMath sources and any
+ third-party-derived `vg-csg` behavior before copying source into CultLib.
+
+Gate:
+
+- Every published surface has a named consumer or is marked for deletion.
+- The persisted v1 corpus is known rather than assumed.
+- Baseline fixtures can be run without booting an entire daemon.
+
+### Stage 1: Establish the Destination and Dependency Direction
+
+Purpose: make `GameCult.Geometry` capable of receiving geometry without growing
+a second primitive vocabulary.
+
+Work:
+
+- Add the explicit `GameCult.Geometry -> CultMath` dependency.
+- Define the target namespaces and package content layout for C#, Rust, HLSL,
+ Unity, and any retained web surface.
+- Establish distribution before source transfer: an explicit NuGet dependency
+ and a dedicated `org.gamecult.geometry` Unity package depending on
+ `org.gamecult.cultmath` and `org.gamecult.cultlib`, unless package inspection
+ proves an existing surface already owns that role.
+- Retain `net8.0` and `netstandard2.1` targets and Unity 2021.3 compatibility.
+ The geometry asmdef references CultMath; the Unity lowering assembly
+ references both. CultMath's asmdef remains reference-free with
+ `noEngineReferences: true`.
+- Add GameCult.Geometry-owned MessagePack formatters for `float2`, `float3`,
+ `float4`, and quaternion with fixed positional layouts.
+- Add cross-runtime byte fixtures for those value encodings.
+- Repair CultCache's SoA member discovery so any direct unmanaged member is one
+ exact typed column. Add `Column` and `Column` tests and a
+ negative test proving no scalar decomposition occurs.
+- Ensure generated schema metadata describes the authored vector member rather
+ than synthetic x/y/z members.
+
+Gate:
+
+- CultMath has no new dependency.
+- `GameCult.Geometry` can serialize, deserialize, index, and expose intact
+ CultMath vectors.
+- CultCache hot-path access returns intact vector spans.
+- Built NuGet and UPM artifacts contain the expected dependency metadata,
+ formatters, shaders, and owned runtime assets.
+
+### Stage 2: Replace Duplicate Primitives and Repair Geometry Schemas
+
+Purpose: remove the duplicate vocabulary before moving larger geometry systems.
+
+Work:
+
+- Rewrite `CultRect`, `CultCircle`, and `CultSphere` with CultMath primitives.
+- Delete `CultVec2` and `CultVec3`; do not add aliases or conversion wrappers.
+- Replace typed references and opaque native-slice metadata using the obsolete
+ names.
+- Classify every vector-shaped `float[]` in `CultGeometryDocuments.cs`:
+ - `Translation`, `SupportCenter`, `SupportSize`, `CameraPosition`,
+ `FrustumMin`, `FrustumMax`, `BoundsMin`, and `BoundsMax` become `float3`;
+ - `RotationXyzw` becomes quaternion;
+ - mesh `Positions` and `Normals` become `float3[]`;
+ - mesh `Uvs` becomes `float2[]`;
+ - scalar and index buffers remain packed arrays;
+ - use a named geometry value when bounds/topology carries an invariant.
+- Define v2 schemas for every document whose field types change. CultCache
+ includes CLR type identity in the canonical schema and must reject an in-place
+ slot type mutation even when no durable v1 corpus is found.
+- If live v1 documents exist, add bounded v1 reader DTOs and an offline
+ v1-to-v2 migrator. Stop all v1 writers at cutover. If no live persisted corpus
+ exists, omit the migrator and delete v1 runtime support, but still publish the
+ corrected contract under v2 identity.
+- Reject malformed legacy vectors: fixed vectors must have the exact expected
+ component count, while packed mesh streams must be divisible by their element
+ width before conversion.
+- Recompute and cross-check stable fingerprints and record keys against Rust
+ fixtures before accepting the new schemas. Preserve an old record key only
+ when the typed representation reproduces the prior IEEE-bit canonicalization
+ exactly; otherwise version the identity deliberately.
+- Use CultMath's lowercase component names in v2 JSON inspection projections.
+ JSON is not the runtime transport, and uppercase `X/Y/Z` aliases must not keep
+ the deleted vector vocabulary alive. Any xenos-facing alternate casing is an
+ explicit boundary projection.
+
+Gate:
+
+- Repository-wide searches find no `CultVec2` or `CultVec3` outside historical
+ evidence.
+- Repository and package searches find no `CultMath.rect`.
+- No live writer emits vector-shaped anonymous arrays for semantic single
+ values.
+- The old schema cannot overwrite, repair, or become a second owner of v2
+ state.
+
+### Stage 3: Transfer Planetary Geometry out of CultMath
+
+Purpose: move planetary authority as one coherent vertical slice.
+
+Work:
+
+- Move the classified planetary C# implementation and tests into
+ `GameCult.Geometry`.
+- Move `Planetary.hlsl` and rename its include ownership and public entrypoints
+ to GameCult.Geometry. It may include CultMath numeric HLSL; CultMath must no
+ longer include planetary geometry. Remove the current implicit planetary
+ include from `CultMath.hlsl` only after consumers include the new owner.
+- Move `AdvancedErosionFilter.hlsl` plus the spherical-erosion and
+ planetary-radial-refinement bodies embedded in `CultMath.hlsl`. Publish one
+ geometry-owned umbrella include that imports CultMath first and geometry
+ organs afterward.
+- Move planetary documentation, fixture tools, web tile code, Unity page/mesh
+ adapters, and the planetary viewer sample to their consumer-owned runtime
+ locations.
+- Move ownership of the `CMPT` little-endian tile format and TypeScript decoder.
+ Preserve v1 bytes exactly or introduce a new format version; never reuse a
+ wire version after changing its bytes.
+- Rename public namespaces and symbols to their GameCult.Geometry authority.
+ Prefer a coordinated breaking release over permanent forwarding types.
+- Move spherical/terrain erosion components classified as geometry. Keep only
+ genuinely coordinate-agnostic numeric kernels in CultMath.
+- Run CPU/HLSL and cross-runtime fixtures from the new owner before deleting
+ the old files.
+- Preserve exact fixtures for field versions, enum ordinals, integer hashes,
+ tile keys/bytes, GPU buffer packing, MessagePack slots, topology seams and
+ corners, and record hashes. Use explicit tolerances only for floating
+ CPU/HLSL comparisons.
+- Delete the corresponding CultMath sources, package entries, and implicit
+ includes in the same authority-transfer stage after the destination passes.
+ Do not defer the actual ownership cut to consumer cleanup.
+
+Gate:
+
+- GameCult.Geometry passes the captured planetary baseline and owns the fixture
+ commands.
+- CultMath packages no `Planetary.hlsl`, planetary sample, planetary tool, or
+ planetary public namespace.
+- No source or shader include can select an old CultMath planetary path.
+- Package inspection finds no planetary API, Unity adapter, sample, tile
+ decoder, documentation claim, or implicit shader include in CultMath.
+
+### Stage 4: Mine and Absorb `vg-csg`
+
+Purpose: preserve useful CSG machinery without preserving the defunct owner.
+
+Work:
+
+- Import the earned Rust implementation into the GameCult.Geometry runtime
+ surface with source provenance and applicable license notices.
+- Replace `bevy_math`-shaped public contracts where they conflict with the
+ canonical cross-runtime primitive/layout contract. Internal use may remain
+ where it protects a measured invariant and does not leak a second public
+ vocabulary.
+- Connect domain trees, selected cuts, build requests, and emitted artifacts to
+ the GameCult.Geometry schema definitions.
+- Port useful fixtures before refactoring algorithms so observable behavior has
+ a witness.
+- Preserve the existing Rust v1 tuple MessagePack and stable-hash fixtures as
+ migration witnesses, then make the GameCult.Geometry-owned Rust package emit
+ the v2 contract.
+- Re-run performance fixtures and retain thresholds only where the fixture
+ represents a real workload.
+- Move surviving doctrine into GameCult.Geometry docs and discard historical
+ instructions that would recreate VibeGeometry ownership.
+
+Gate:
+
+- No production build or test depends on the VibeGeometry repository or
+ `vg-csg` submodule.
+- CSG output satisfies the adopted correctness and performance fixtures from
+ its new package.
+- The Rust runtime speaks the same schema IDs, vector layouts, fingerprints,
+ and record keys as C#.
+
+### Stage 5: Integrate Geometry Through CultCache, CultNet, and CultMesh
+
+Purpose: prove the new owner through the real typed-state path.
+
+Work:
+
+- Register the adopted geometry schemas and formatters through the generated
+ registry path.
+- Prove raw envelope interchange for geometry documents across C# and Rust,
+ adding TypeScript only if an actual web consumer remains.
+- Publish geometry worker state and commands through the provider-owned
+ CultMesh surface.
+- Ensure geometry build requests, selected cuts, chunk artifacts, and planetary
+ page state share one commit/derivation path for direct and programmatic use.
+- Add a development probe exposing schema version, owner, source record key,
+ selected cut/page, content hash, and served package version.
+
+Gate:
+
+- A pipeline smoke proves typed handoff from domain input through selected cut
+ or planetary page selection to a persisted and republished artifact.
+- The visible/debug projection and persisted state identify the same owner and
+ version.
+- No ad hoc JSON or parallel status model becomes load-bearing geometry truth.
+
+### Stage 6: Consumer Cutover and Coordinated Release
+
+Purpose: switch consumers without maintaining two live authorities.
+
+Release order:
+
+1. Build an unpublished CultMath candidate that contains the stable primitive
+ contract and removes geometry-owned APIs.
+2. Build an unpublished GameCult.Geometry candidate against that exact CultMath
+ version, containing adopted schemas, planetary geometry, and runtime packages.
+3. Update and test GameCult services, Unity projects, web consumers, and tools
+ against a local candidate feed.
+4. Publish the CultMath and GameCult.Geometry candidates in one coordinated
+ release window, with the primitives-only CultMath dependency entering the
+ feed first.
+5. Publish consumer updates after both owning packages are available.
+6. Convert persisted v1 geometry stores, if any, and remove migration tooling
+ after the retained-corpus policy is satisfied.
+
+Work:
+
+- Pin compatible package versions across the coordinated release.
+- Update examples and developer-facing entrypoints to use
+ `GameCult.Geometry` plus CultMath primitives directly.
+- Cut Fensalir's Engine/Contracts public planetary types and shader includes to
+ the new assembly before removing CultMath geometry. Its CPU/GPU parity tests
+ are release gates.
+- Add the geometry package to Aetheria/Unity, move its directly compiled
+ planetary sources to the new assembly, preserve moved Unity `.meta` GUIDs,
+ and regenerate Unity project files.
+- Verify served package, shader, schema, and migration versions in at least one
+ real consumer for each retained runtime.
+- Run the preserved parity corpus through known consumers including Fensalir,
+ Zyphos, Aetheria/Unity, the web tile decoder when retained, C#, Rust, and HLSL.
+- Refuse mixed old/new geometry writers rather than reconciling them later.
+
+Gate:
+
+- Every known consumer runs on the new owner.
+- No published package graph has `CultMath -> CultLib` or
+ `CultMath -> GameCult.Geometry`.
+- Old and new writers cannot target the same state surface.
+
+### Stage 7: Delete Obsolete Authorities
+
+Purpose: make the old ownership structurally unable to return.
+
+Work:
+
+- Confirm deletion of all CultMath planetary/geometry sources, tests, shaders,
+ samples, docs, tools, web surfaces, package content entries, and Unity
+ adapters; remove any residue found after consumer cutover.
+- Delete obsolete GameCult.Geometry duplicate primitive code and stale schema
+ paths.
+- Delete VibeGeometry workspace wiring and the `vg-csg` submodule dependency;
+ archive the repositories after provenance links and release tags are
+ recorded.
+- Remove compatibility aliases, forwarding namespaces, dual registries, and
+ temporary migration flags.
+- Update source indexes and repository memory so future agents retrieve the new
+ authority first.
+
+Gate:
+
+- Negative searches and package inspection prove the old paths are absent.
+- Building CultMath alone cannot access planetary or geometry APIs.
+- Building GameCult.Geometry cannot access `CultVec*` or VibeGeometry.
+- The old persisted schema path cannot write or override current geometry.
+
+## Schema Migration Policy
+
+Do not create a permanent compatibility model pre-emptively.
+
+- If no durable v1 corpus exists, change the schema once, update fixtures, and
+ publish the corrected contract as v2, then delete the old form without
+ building a migrator.
+- If a durable v1 corpus exists, create an offline migrator with explicit input
+ and output schema IDs, content hashes, counts, and a dry-run report.
+- A v1 reader may exist only inside migration/inspection tooling.
+- The runtime writes only the current schema after cutover.
+- Migration completes only when retained stores have receipts and no active
+ service advertises or writes v1.
+
+## Verification Matrix
+
+| Layer | Required evidence |
+| --- | --- |
+| CultMath primitives | C#/HLSL parity, unmanaged layout/size checks, package inspection |
+| Geometry values | Exact `float2`/`float3` field ownership, intersection/query unit tests, MessagePack byte fixtures |
+| CultCache SoA | Intact vector columns, no recursive flattening, add/update/remove/snapshot behavior |
+| Geometry schemas | Catalog IDs and slots, v1 inventory or migration receipts, stable record-key parity |
+| Planetary geometry | Projection round trips, topology adjacency, field/normal samples, radial refinement, page encoding/residency, CPU/HLSL parity |
+| CSG | Boolean/convex fixtures, dirty rebuild behavior, selected-cut correctness, triangle/collider receipts, performance fixtures |
+| Cross-runtime | C#/Rust payload and fingerprint parity; web parity only for retained web consumers |
+| Packaging | CultMath contains no geometry assets; GameCult.Geometry includes its owned HLSL/docs/runtime assets |
+| End to end | Domain or planetary input to persisted artifact through CultMesh with visible owner/version evidence |
+
+## Negative Completion Checks
+
+The migration is not complete until all are true:
+
+- `CultVec2` and `CultVec3` cannot appear in newly generated schemas or native
+ slice metadata.
+- CultMath cannot publish or execute planetary geometry through an old entrypoint.
+- VibeGeometry and `vg-csg` cannot influence a build, runtime, fixture, or
+ generated artifact.
+- VibeGeometry is absent from project references, package manifests,
+ submodules, CI, runtime identifiers, and authoritative documentation.
+- Legacy v1 state cannot override or repair current geometry after the fact.
+- CultCache cannot decompose a vector into independently owned scalar columns.
+- Manual, programmatic, imported, persisted, and network-received geometry use
+ the same schema and commit paths.
+- Passing unit tests are accompanied by package inspection and cross-runtime
+ evidence at the layer users actually consume.
+
+## Definition of Done
+
+The work is complete when `GameCult.Geometry` is the only geometry authority,
+CultMath is a clean numeric substrate, all retained runtime projections agree on
+typed layouts and stable identities, VibeGeometry has no live dependency path,
+and the old owners are structurally unable to emit current geometry state.
diff --git a/docs/cultmath-gamecult-geometry-migration-progress.md b/docs/cultmath-gamecult-geometry-migration-progress.md
new file mode 100644
index 00000000..131f85e1
--- /dev/null
+++ b/docs/cultmath-gamecult-geometry-migration-progress.md
@@ -0,0 +1,344 @@
+# CultMath and GameCult.Geometry Migration Progress
+
+Plan: `docs/cultmath-gamecult-geometry-migration-plan.md` (locked)
+
+Started: 2026-07-22
+
+Status: Stages 0-7 complete for source ownership, immutable Git/UPM delivery,
+consumer cutover, and verification. NuGet and Cargo registry mirrors remain an
+explicit credential-bound publication follow-up; release artifacts are public.
+
+## Objective
+
+Implement the locked migration so `GameCult.Geometry` becomes the sole owner of
+geometry, planetary geometry, CSG, geometry schemas, and runtime projections,
+while CultMath remains the HLSL-shaped numeric substrate and VibeGeometry loses
+all live authority.
+
+## Execution Authority Map
+
+- Owner: `GameCult.Geometry` owns geometry decisions and geometry state.
+- Inputs: CultMath numeric primitives, mined `vg-csg` implementation/evidence,
+ current geometry v1 documents, and named downstream consumer contracts.
+- Outputs: GameCult.Geometry C#/Rust/HLSL/Unity/web surfaces, v2 typed schemas,
+ persisted artifacts, and CultMesh projections.
+- Derived state: JSON inspection, UI/debug projections, caches, migration
+ receipts, and compatibility reads are non-authoritative.
+- Forbidden writers: CultMath planetary APIs, v1 geometry runtime writers,
+ `CultVec*`, VibeGeometry/`vg-csg`, legacy shader entrypoints, and anonymous
+ fixed-vector arrays.
+- Shared paths: direct API calls, persisted reloads, imports, network envelopes,
+ worker commands, Unity consumption, web tile reads, and Rust emitters must use
+ the same current schema/layout/fingerprint rules.
+- Deletion line: `CultVec2`, `CultVec3`, `CultMath.rect`, CultMath planetary and
+ erosion ownership, v1 writers, VibeGeometry wiring/submodule authority, and
+ stale package/docs/schema paths.
+
+Demotions:
+
+- Geometry v1 DTOs are no longer owners; when a durable corpus requires them,
+ they are bounded read-only migration inputs to v2.
+- JSON is no longer an owner; it is an inspection or xenos-boundary projection.
+- VibeGeometry is no longer an owner; it is a provenance-bearing source quarry.
+- CultMath planetary state is no longer an owner; it is source material pending
+ transfer and deletion in the same unpublished migration cut.
+
+## Worktree Safety
+
+- CultLib began with unrelated modified `.voidbot`, CultMesh, CultCache TS,
+ CultNet Rust, README, and test files plus scratch PID files. These changes are
+ outside the migration unless an exact overlap is required and reviewed.
+- CultMath began clean on `codex/rust-cultmath-core` at `cb481b4`.
+- VibeGeometry began clean on `main` at `85ac60a`.
+- The locked plan is not edited during implementation.
+
+## Stage Ledger
+
+| Stage | State | Evidence / next gate |
+| --- | --- | --- |
+| 0. Freeze and baseline | Complete | Source bodies, contracts, consumers, licenses, fixtures, and focused baselines mapped |
+| 1. Destination/dependency/formatters/SoA | Complete | NuGet and dedicated UPM artifacts prove dependency direction and owned vector integration |
+| 2. Primitive deletion and v2 schemas | Complete | C# and Geometry-owned Rust emit identical v2 bytes and record keys for all four roots |
+| 3. Planetary transfer | Complete | Managed, HLSL, CMPT, Unity, web, tool, docs, tests, and package authority moved; CultMath owners deleted |
+| 4. Mine `vg-csg` | Complete | Geometry-owned Rust kernel passes correctness, parity, performance, formatting, and warning-denied lint gates |
+| 5. CultCache/CultNet/CultMesh integration | Complete | One provider commit path persists, watches, replicates, and probes typed geometry state |
+| 6. Consumer cutover/release | Complete | Fensalir and Aetheria green; immutable Git/UPM tags and public release artifacts verified |
+| 7. Delete obsolete authorities/audit | Complete | VibeGeometry workspace authority archived; tracked negative audit is clean apart from retained provenance/witnesses |
+
+## Verification Receipts
+
+Receipts will be appended here with command, repository, commit, result, and
+relevant artifact/version. A passing narrow test is not accepted as evidence for
+a broader stage gate.
+
+### Stage 0 receipts
+
+- CultLib: `dotnet test tests/GameCult.Geometry.Tests/GameCult.Geometry.Tests.csproj --no-build --no-restore --disable-build-servers` passed 13/13. A full rebuild remains a Stage 1 gate; shared .NET build contention made attached build attempts exceed 60 seconds.
+- CultMath: `dotnet test CultMath.slnx --no-restore` passed 72/72.
+- CultMath native: `cargo test --locked` passed 1/1.
+- CultMath web tile unit: `node --test web/planetary-tile.test.mjs` passed 1/1.
+- CultMath pack and the combined web/C# cross-runtime script exceeded the
+ baseline time window and remain required package-transfer gates.
+- VibeGeometry: `cargo test` passed 56 unit and 9 integration tests; doc tests
+ passed; `cargo clippy --all-targets --all-features` passed.
+- Authoritative Rust quarry: VibeGeometry-pinned `vg-csg` submodule `a3197f4`.
+ Standalone `E:\Projects\vg-csg` is stale and excluded.
+- v1 witnesses: current C#/Rust slots and hash algorithms match; Rust expected
+ record keys include domain `02c9...ed95` and chunk `b94d...21af5`. Exact
+ checked-in MessagePack byte witnesses still need to be generated before v2.
+- Persisted corpus: targeted file inventory covered CultLib, CultMath,
+ VibeGeometry, Fensalir, and Aetheria. CultLib contains only the two checked-in
+ `vg-csg-ragnarok` payload fixtures. Binary scanning of Aetheria `.cc`,
+ `.msgpack`, and `.mpack` artifacts found no `gamecult.geometry.*.v1` catalog
+ identity. Indexed-source retrieval found definitions/tests/docs but no durable
+ geometry store. Execution therefore uses v2 plus retained test witnesses,
+ with no live v1 runtime migrator.
+
+### Stage 1 receipts
+
+- CultCache MessagePack now resolves external member values through a
+ document-owner assembly declaration. Resolver precedence is core CultCache,
+ owner-declared resolvers, then MessagePack standard fallback; the options are
+ cached per owner assembly and generated document codecs use that same path.
+ CultCache does not reference Geometry or CultMath.
+- CultCache SoA discovery now admits direct reference-free value types as one
+ exact typed column. It rejects managed-reference and byref-like values and
+ does not synthesize component columns from nested struct fields.
+- `dotnet test tests/GameCult.Caching.Tests/GameCult.Caching.Tests.csproj
+ --no-restore --filter
+ "FullyQualifiedName~DocumentAssemblyResolverTests|FullyQualifiedName~SoaValueColumnTests"
+ --verbosity minimal -m:1 -nodeReuse:false` passed 4/4 after a full rebuild.
+- The initial SoA reflection probe used `Type.IsFunctionPointer`, which is not
+ available on the library's `netstandard2.1` target. It was removed rather
+ than papered over; structural managed-reference inspection is the owning
+ eligibility rule.
+- CultMath candidate `0.1.0-geometry-migration.1` was packed from commit
+ `cb481b4c75444426a693781a0e5b35ade08a8938` with build servers disabled. The
+ 123,307-byte package SHA-256 is
+ `07C0CE79453AF9BA29910809F8779916E7B8F92DCA7ACEB5E2B8F9D3EBED3E93`;
+ a local-feed consumer restored, compiled, and executed `CultMath.float3`.
+- GameCult.Geometry consumes that candidate as an explicit NuGet dependency;
+ its sibling `.tools/local-feed` is only an optional restore source, not a
+ project-reference authority. Geometry owns strict positional formatters for
+ `float2`, `float3`, `float4`, and `quaternion`.
+- `dotnet test tests/GameCult.Geometry.Tests/GameCult.Geometry.Tests.csproj
+ --no-restore --filter FullyQualifiedName~CultMathValueIntegrationTests
+ --verbosity minimal --disable-build-servers -p:UseSharedCompilation=false
+ -m:1`
+ passed 6/6. Evidence covers exact bytes, round trips, malformed-width
+ rejection, actual `Column`/`Column` access, and absence of
+ synthetic component columns.
+- GameCult.Geometry candidate `0.1.0-geometry-migration.1` packed successfully;
+ its nuspec declares exact candidate dependencies on CultMath, CultCache, and
+ CultCache.MessagePack. The dedicated UPM artifact remains the open Stage 1
+ gate.
+- Authoritative `vg-csg` commit `8f070f4` now pins exact v1 MessagePack bytes
+ for representative domain/chunk tuples plus complete domain, request,
+ selected-cut, and chunk record keys. Focused Rust tests passed 4/4; formatting
+ and clippy passed. VibeGeometry commit `82b56db` advances its submodule to
+ that witness commit. Both migration branches are pushed.
+- `scripts/build-geometry-unity-package.ps1` staged
+ `org.gamecult.geometry@0.1.0` successfully. Its manifest depends explicitly
+ on `org.gamecult.cultmath@0.1.0` and `org.gamecult.cultlib@1.0.7`; the package
+ contains only its owned `GameCult.Geometry.dll`/symbols plus its asmdef and
+ documentation. It neither duplicates CultMath nor buries Geometry inside the
+ CultLib Unity package.
+
+## Decisions and Deviations
+
+- Licensing: no relicensing is assumed. Moved CultMath files retain MPL-2.0
+ notices and provenance; the GameCult.Geometry package will carry explicit
+ mixed-license package documentation rather than falsely stamping those files
+ MIT. `AdvancedErosionFilter` source and shader notices remain verbatim.
+- Serialization: Geometry-owned external value formatters require a declarative
+ per-document-assembly resolver seam in `GameCult.Caching.MessagePack`.
+ CultMath remains annotation-free and core caching does not reference Geometry.
+- v1 persistence: no durable corpus was found in the named repositories and
+ artifacts. v1 remains fixture evidence only; it will not survive as a runtime
+ writer or general compatibility model.
+- No deviations from the locked target architecture.
+- Stage 2 versions all four root geometry documents as one v2 cohort. Domain's
+ nested CLR layout changes are invisible to the current root-only schema
+ fingerprint, and leaving SelectedCut as the lone live v1 writer would retain
+ a split cohort even though its direct fields are unchanged.
+- Typed vectors preserve existing record keys by flattening components in the
+ same authored order into the existing IEEE-754 bit canonicalization. Wire
+ payloads still change—especially typed mesh arrays—so byte fixtures and v2
+ identities remain mandatory.
+
+### Stage 2 receipts
+
+- `CultVec2` and `CultVec3` definitions were deleted; `CultRect`, `CultCircle`,
+ and `CultSphere` now expose CultMath `float2`/`float3` directly. No alias or
+ conversion owner remains.
+- `dotnet test tests/GameCult.Geometry.Tests/GameCult.Geometry.Tests.csproj
+ --no-restore --verbosity minimal --disable-build-servers
+ -p:UseSharedCompilation=false -m:1` passed 19/19 for the primitive cut.
+- CultMesh native-slice metadata fixtures now name `CultMath.float2` and
+ `CultMath.float3` while retaining 8/12-byte element widths. Only those three
+ overlapping lines were staged from the already-dirty TypeScript test file.
+- All four root geometry schemas now form a v2 cohort with their original slot
+ numbers. Semantic single vectors use `float3`/`quaternion`; mesh vertices,
+ normals, and UVs use `float3[]`/`float2[]`. The runtime contains no v1 DTO,
+ reader, writer, or conversion authority.
+- Live v2 semantic SchemaIds are pinned as domain `sha256:e335...421d`, build
+ request `sha256:2de3...21d3`, selected cut `sha256:8644...9ff8`, and chunk
+ artifact `sha256:2d28...6156`. Full values live in the executable tests.
+- Typed stable fingerprints flatten components into the prior ordered IEEE-bit
+ stream. Domain/chunk round trips preserve record identity, while the checked-
+ in flat-mesh v1 payload is retained as negative evidence and rejected by the
+ v2 decoder. The Geometry suite passes 19/19 after the cohort cut.
+- `packages/gamecult-geometry-rs` is the Geometry-owned Rust v2 destination.
+ It has no VibeGeometry, `vg-csg`, `bevy_math`, CultCache, or CultNet
+ dependency and preserves the MIT provenance of the mined `vg-csg` contract.
+- C# and Rust pin identical exact MessagePack bytes and record keys for domain,
+ build request, selected cut, and chunk roots. C# decodes the Rust witnesses
+ and re-emits the same bytes. The Geometry suite passes 24/24; Rust passes 4/4,
+ doc tests, rustfmt, and warning-denied clippy.
+- The parity pass exposed an old split identity: C# request scalar floats used
+ round-trip decimal text while `vg-csg` used IEEE-bit hex. v2 deliberately
+ standardizes every float on IEEE bits. The request key therefore changes at
+ the v2 boundary; signed zero, a chosen NaN payload, and infinity are pinned
+ across MessagePack so this cannot quietly diverge again.
+
+### Stage 3 receipts
+
+- Thirteen planetary/erosion C# sources and thirteen xUnit suites now live
+ under GameCult.Geometry; `CultMath.rect`, its factories, and all original
+ planetary sources/tests are deleted. Destination planetary tests pass 41/41;
+ remaining CultMath tests pass 28/28 for both target frameworks.
+- Geometry owns five HLSL files behind `GameCult.Geometry.hlsl`; public geometry
+ symbols use `gamecult_geometry_*`. CultMath's umbrella and NuGet candidate
+ contain only numeric `CultMath.hlsl`. A DXC SPIR-V smoke compiled the joined
+ umbrella successfully to a 1,428-byte module.
+- The CMPT v1 tool and TypeScript decoder moved intact. The end-to-end fixture
+ is exactly 1,221 bytes with SHA-256
+ `8b2bc46e4123b8ac8936b43f8d08a34e3f2e74f38d0bb3f101cf3728cd201962`;
+ the C# emitter and TypeScript decoder pass together.
+- Unity adapters retain their original `.meta` GUIDs; the Planetary Viewer uses
+ the Geometry umbrella and owned shader symbols. The staged UPM artifact has
+ one owned DLL, both adapters, the sample, all five shaders, and explicit
+ MIT/MPL license and provenance documents.
+- Stale candidate cache was made impossible by rotating CultMath and Geometry
+ to `0.1.0-geometry-migration.2`. CultMath's candidate contains only its DLLs,
+ README, and numeric shader. Geometry's candidate declares the exact CultMath
+ `.2` dependency and contains all Geometry shaders/docs/notices under a
+ mixed-license file declaration rather than the inherited blanket MIT claim.
+- Full Geometry tests pass 27/27 after the joined shader/Unity integration;
+ planetary tests pass 41/41. Negative searches find obsolete CultMath geometry
+ names only inside tests that assert their absence or ignored generated output.
+
+### Stage 4 receipts
+
+- `packages/gamecult-geometry-rs` now owns the mined convex splitting, ordered
+ brush CSG, tree lowering, dirty frontiers, prefix checkpoints, domain/LOD
+ selection, feature lowering, and triangle/collider assembly kernels. No build
+ or runtime dependency points at VibeGeometry or `vg-csg`.
+- The public `GeometryBrushAssembler` facade accepts Geometry-owned
+ `Float3` values and returns `GeometryTriangleMesh`; `bevy_math` remains a
+ private implementation dependency and is not re-exported.
+- The duplicate quarry persistence authority (`DomainSpecDocument`,
+ `DomainNodeDocument`, `FeatureClaimDocument`, and its local schema version)
+ was deleted. Private specs are in-memory builders; the v2 `Geometry*`
+ documents remain the sole wire authority.
+- Nine RealtimeCSG behavioral fixtures and a bounded 512-distant-cutter
+ performance fixture moved under Geometry ownership. `cargo test --locked`
+ passes 67/67 plus doc tests; rustfmt and warning-denied Clippy pass.
+- The authority transfer commits are CultLib `756f5a33`, CultMath `6a16260`,
+ and the post-transfer label cleanup `99c893bd`. The Rust absorption commit is
+ CultLib `6b44f969`.
+
+### Stage 5 receipts
+
+- `CultGeometryWorkerProvider` owns the build invocation and output commit
+ sequence. Direct calls and the `gamecult.geometry.worker.build` CultMesh
+ operation share `BuildAsync`; compute is injected through the persistence-free
+ `ICultGeometryBuildPipeline` port.
+- Domain/request reads and selected-cut, artifact, and observational worker-state
+ writes use `CultNetDatabase` typed APIs. Watches and raw MessagePack envelopes
+ are consequences of that commit path rather than parallel publishers.
+- The development probe derives owner, v2 schema, request/cut keys, selected
+ nodes, artifact identities, content hashes, and served assembly version from
+ persisted documents. Mutating worker display state cannot overwrite geometry
+ outputs or forge the served version.
+- Worker state registers through the generated CultDocument registry. Focused
+ provider tests pass 4/4 and the full GameCult.Geometry suite passes 31/31.
+- Rust raw-envelope duplication was deliberately omitted: CultNet owns that
+ protocol. Existing C#/Rust exact v2 document bytes and record keys remain the
+ cross-runtime geometry witness.
+
+### Stage 6 receipts
+
+- Coordinated local candidates use
+ `scripts/build-geometry-candidate.ps1`. CultMath `.2` is primitives-only;
+ Geometry `.2` contains its managed assembly, notices/docs, and all five owned
+ shaders with exact support-package and CultMath candidate dependencies. A
+ clean net10 consumer restored and built; the Unity package staged one owned
+ assembly plus adapters, sample, and shaders. The Rust crate passed an
+ independent `cargo package --allow-dirty --no-verify` dry run.
+- Fensalir cutover commit `42778c1` adds direct Geometry dependencies, migrates
+ planetary C# consumers, compiler include roots, shaders, and parity harnesses
+ while retaining CultMath for numeric primitives. The solution builds with no
+ warnings/errors; fractal tests pass 170/170, erosion parity 1/1, topology
+ parity 19/19, and planetary page tests 16/16.
+- Aetheria's coordinated cutover ends at commit `3ec6222f`. It replaces
+ `CultVec*` fixtures with CultMath vectors under an exact legacy MessagePack
+ byte witness, moves spatial rectangles to `GameCult.Geometry.CultRect`, and
+ pins CultMath, CultLib, and Geometry to immutable Git/UPM tags. Geometry is an
+ explicit Unity testable dependency. The Freeze build and State verifier pass;
+ the verifier fingerprint is
+ `664afd7e...48b`, and the legacy float2/float3 bytes remain exact.
+- A disposable Aetheria import using only the published tags passed the owned
+ EditMode suite 3/3: primitive ownership, CultMath visibility, and planetary
+ Unity mesh projection. The resolved commits are CultMath `b43cf49`, Geometry
+ and CultLib `b65619ff`, and Eve `e5556c3`. No compatibility compiler errors
+ remain. Normal Unity project generation confirmed the core packages are
+ precompiled and require no generated CultMath or Geometry core project.
+ Aetheria commit `9c983a5e` deletes the stale generated `CultMath.csproj` and
+ its solution/project references; generated project metadata contains no
+ deleted `rect.cs` or planetary source paths.
+- Legacy D3DCompiler exposed that macro-expanded HLSL includes were not
+ portable. CultLib commit `cdeea474` gives the Geometry umbrella direct default
+ and Unity include branches; Geometry shader ownership tests pass 3/3 and the
+ downstream D3D12 corpus passes.
+- The coordinated release body is CultLib commit `b65619ff`, merging Geometry
+ ownership with the CultMesh reliability line. Caching passes 36/36, Mesh
+ 210/210, and Geometry 31/31. Its Unity package contains 24 managed assemblies,
+ owned binaries at file version `1.0.44.0`, the resolver declaration and body
+ read-lease API, and the native QUIC payload.
+- CultMath `0.1.0` and Geometry `0.1.0` are published as immutable Git/UPM tags.
+ Public GitHub releases carry the NuGet and Cargo artifacts at
+ `https://github.com/GameCult/CultMath/releases/tag/cultmath-v0.1.0` and
+ `https://github.com/GameCult/CultLib/releases/tag/gamecult-geometry-v0.1.0`.
+ The CultMath NuGet SHA-256 is `90dc30c0...d070`; Geometry NuGet is
+ `fdce63a3...6835`; the Rust crates are `a346dca1...d68` and
+ `ac0c45cb...9e0e` respectively.
+- NuGet and Cargo registry mirroring was not attempted: the authenticated GitHub
+ token lacks package scopes, no NuGet API key or Cargo registry token exists,
+ and GitHub Packages is not a Cargo registry. This is a distribution mirror,
+ not surviving source or package authority; the exact public artifacts and
+ immutable consumer paths above are the completed release contract.
+
+### Stage 7 receipts
+
+- Tracked negative audits across CultLib, CultMath, Fensalir, Aetheria, and
+ VibeGeometry find no live `CultVec*`, `CultMath.rect`, CultMath planetary
+ implementation, old planetary shader vocabulary, geometry v1 writer, or
+ VibeGeometry/`vg-csg` build dependency. Frozen fixtures, source attribution,
+ and negative ownership tests remain deliberately.
+- VibeGeometry commit `bc9604a` removes its tracked Cargo workspace, lockfile,
+ submodule declaration/gitlink, scratch/build wiring, and live mission claims.
+ Its remaining documents point to source witness `8f070f4f` and Geometry
+ absorption commit `6b44f969`; repository history preserves provenance.
+- CultLib commits `8a7eee71` and `b9f20a64` remove manual-registry guidance and
+ update the root package map. Generated CultDocument metadata remains the only
+ registration owner.
+
+## Publication Follow-up
+
+- Mirror the already-published NuGet artifacts to the chosen package feed when
+ a credential with package write scope is available.
+- Mirror the two Rust crates to crates.io or another real Cargo registry when a
+ registry token and registry choice are available.
+- These mirrors must publish the recorded bytes. Rebuilding them would create a
+ different release and requires a new version.
diff --git a/package.json b/package.json
index f1b995a2..a6803b6b 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,8 @@
"workspaces": [
"packages/cultcache-ts",
"packages/cultnet-ts",
- "packages/cultmesh-ts"
+ "packages/cultmesh-ts",
+ "packages/gamecult-geometry-ts"
],
"scripts": {
"build:ts": "npm --workspaces run build",
diff --git a/packages/cultmesh-ts/src/index.ts b/packages/cultmesh-ts/src/index.ts
index c61dfee1..687f9b8a 100644
--- a/packages/cultmesh-ts/src/index.ts
+++ b/packages/cultmesh-ts/src/index.ts
@@ -4088,11 +4088,25 @@ export interface CultMeshRudpDocumentPut {
};
}
+export interface CultMeshRudpDocumentReceipt {
+ binding: CultNetDocumentBinding;
+ recordKey: string;
+ value: CultCacheDocumentValue;
+ messageId?: string;
+ sourceRuntimeId?: string;
+ sourceAgentId?: string;
+ sourceRole?: string;
+ tags?: string[];
+}
+
export interface CultMeshRudpDocumentServerOptions extends CultMeshRudpSocketOptions {
documents: CultNetDocumentRegistry;
getCache?: () => Promise | CultCache;
onError?: (error: Error) => void;
- onDocumentPutRaw?: (document: CultMeshRudpDocumentPut) => void | Promise;
+ onDocumentPutRaw?: (document: CultMeshRudpDocumentPut) =>
+ | CultMeshRudpDocumentReceipt
+ | void
+ | Promise;
wireContract?: CultNetWireContract;
sessionTimeoutMs?: number;
}
@@ -4112,6 +4126,10 @@ export interface CultMeshRudpDocumentPublishOptions extends CultMeshRudpPeerOpti
flushTimeoutMs?: number;
}
+export interface CultMeshRudpDocumentReceiptOptions extends CultMeshRudpDocumentPublishOptions {
+ receiptTimeoutMs?: number;
+}
+
export interface CultMeshRudpEndpoint {
host: string;
port: number;
@@ -5638,7 +5656,18 @@ export class CultMesh {
return;
case "cultnet.document_put_raw.v0":
if (options.onDocumentPutRaw) {
- await options.onDocumentPutRaw(normalizeRudpDocumentPut(message, record.remote));
+ const receipt = await options.onDocumentPutRaw(normalizeRudpDocumentPut(message, record.remote));
+ if (receipt) {
+ sendSchemaMessage(record, options.documents.createRawDocumentPutMessage(
+ receipt.binding, receipt.messageId ?? message.messageId, receipt.recordKey, receipt.value,
+ {
+ sourceRuntimeId: receipt.sourceRuntimeId ?? runtimeId,
+ sourceAgentId: receipt.sourceAgentId,
+ sourceRole: receipt.sourceRole ?? "provider",
+ tags: receipt.tags ?? ["receipt"],
+ },
+ ));
+ }
}
return;
default:
@@ -5753,6 +5782,36 @@ export class CultMesh {
}
}
+ public static async publishRudpDocumentAndWaitForReceipt(
+ runtimeId: string, connectionId: number, endpoint: string | CultMeshRudpEndpoint,
+ binding: CultNetDocumentBinding, recordKey: string, value: CultCacheDocumentValue,
+ receiptBinding: CultNetDocumentBinding, options: CultMeshRudpDocumentReceiptOptions = {},
+ ): Promise> {
+ requireNonEmpty(runtimeId, "runtimeId");
+ requireNonEmpty(recordKey, "recordKey");
+ const messageId = options.messageId ?? `${runtimeId}:${binding.definition.type}:${recordKey}`;
+ const peer = await CultMesh.createRudpPeer(runtimeId, connectionId, endpoint, options);
+ try {
+ const receipt = new Promise>((resolve, reject) => {
+ const timeout = setTimeout(() => { cleanup(); reject(new Error(`Timed out waiting for RUDP application receipt for ${recordKey}.`)); }, options.receiptTimeoutMs ?? 5_000);
+ const onMessage = (message: CultNetMessage): void => {
+ if (message.schemaVersion !== "cultnet.document_put_raw.v0" || message.messageId !== messageId || message.document.schemaId !== receiptBinding.definition.schemaId) return;
+ try {
+ const decoded = decode(new Uint8Array(message.document.payload));
+ cleanup();
+ resolve(receiptBinding.definition.schema.parse(decoded) as CultCacheDocumentValue);
+ } catch (error) { cleanup(); reject(error); }
+ };
+ const cleanup = (): void => { clearTimeout(timeout); peer.off("message", onMessage); };
+ peer.on("message", onMessage);
+ });
+ peer.send(new CultNetDocumentRegistry([binding]).createRawDocumentPutMessage(binding, messageId, recordKey, value, {
+ sourceRuntimeId: options.sourceRuntimeId ?? runtimeId, sourceAgentId: options.sourceAgentId, sourceRole: options.sourceRole, tags: options.tags,
+ }));
+ return await receipt;
+ } finally { peer.close(); }
+ }
+
public static async createRudpServer(
runtimeId: string,
connectionId: number,
diff --git a/packages/cultmesh-ts/test/cultmesh.test.ts b/packages/cultmesh-ts/test/cultmesh.test.ts
index f4e8f9cc..d7f5e267 100644
--- a/packages/cultmesh-ts/test/cultmesh.test.ts
+++ b/packages/cultmesh-ts/test/cultmesh.test.ts
@@ -34,6 +34,13 @@ const noteAliasDocument = defineDocumentType({
name: "noteId",
});
+const noteReceiptDocument = defineDocumentType({
+ type: "cultmesh.note_receipt",
+ schemaId: "cultmesh.note_receipt.v0",
+ schema: z.object({ commandId: z.string(), state: z.literal("applied"), body: z.string() }),
+ name: "commandId",
+});
+
const foreignNoteDocument = defineDocumentType({
type: "runtime.generated.cultmesh.note",
schemaId: "runtime.generated.cultmesh.note.ui.42",
@@ -1447,7 +1454,7 @@ test("CultMesh TS describes typed surface catalogs for tools and generated bindi
"aetheria.zone.render",
"gamecult.aetheria.render_body.v1",
128,
- [CultMesh.nativeSliceColumn("position", "CultVec2", 8)],
+ [CultMesh.nativeSliceColumn("position", "CultMath.float2", 8)],
{ route: routeHint },
);
@@ -2143,7 +2150,7 @@ test("CultMesh TS exposes native slice descriptors", () => {
"gamecult.aetheria.zone_render.v1",
12,
[
- CultMesh.nativeSliceColumn("position", "CultVec3", 12),
+ CultMesh.nativeSliceColumn("position", "CultMath.float3", 12),
CultMesh.nativeSliceColumn("rotationRadians", "float32", 4),
CultMesh.nativeSliceColumn("renderGroupId", "uint32", 4),
],
@@ -2156,7 +2163,7 @@ test("CultMesh TS exposes native slice descriptors", () => {
assert.equal(CultMesh.denseRowStrideBytes(view), 20);
assert.deepEqual(CultMesh.findNativeSliceColumn(view, "position"), {
name: "position",
- valueType: "CultVec3",
+ valueType: "CultMath.float3",
elementSizeBytes: 12,
});
assert.equal(CultMesh.findNativeSliceColumn(view, "missing"), undefined);
@@ -2568,6 +2575,29 @@ test("CultMesh TS publishes one registered document to a RUDP catalog", async ()
}
});
+test("CultMesh TS returns a correlated provider receipt after a RUDP document mutation", async () => {
+ const connectionId = 0x10203050;
+ const commandBinding = defineCultNetDocumentBinding({ definition: noteDocument });
+ const receiptBinding = defineCultNetDocumentBinding({ definition: noteReceiptDocument });
+ const server = CultMesh.createRudpDocumentServer("cultmesh-ts-receipt-server", connectionId, {
+ documents: new CultNetDocumentRegistry([commandBinding, receiptBinding]), bindHost: "127.0.0.1", bindPort: 0,
+ resendDelayMs: 25, resendPollMs: 5,
+ onDocumentPutRaw: async (document) => ({
+ binding: receiptBinding, recordKey: document.recordKey,
+ value: { commandId: document.recordKey, state: "applied", body: (document.payload as { body: string }).body },
+ }),
+ });
+ try {
+ await server.start();
+ const receipt = await CultMesh.publishRudpDocumentAndWaitForReceipt(
+ "cultmesh-ts-receipt-client", connectionId, `rudp://127.0.0.1:${server.bind.port}`,
+ commandBinding, "command:note", { noteId: "command:note", body: "provider applied this" }, receiptBinding,
+ { messageId: "command:note", receiptTimeoutMs: 1_000, resendPollMs: 5 },
+ );
+ assert.deepEqual(receipt, { commandId: "command:note", state: "applied", body: "provider applied this" });
+ } finally { server.close(); }
+});
+
test("CultMesh TS reads remote RUDP snapshots through document handles", async () => {
const connectionId = 0x10203048;
const node = await CultMesh.startNode(
diff --git a/packages/gamecult-geometry-rs/Cargo.lock b/packages/gamecult-geometry-rs/Cargo.lock
new file mode 100644
index 00000000..986160f0
--- /dev/null
+++ b/packages/gamecult-geometry-rs/Cargo.lock
@@ -0,0 +1,969 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "approx"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "assert_type_match"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
+
+[[package]]
+name = "bevy_macro_utils"
+version = "0.17.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62d984f9f8bd0f0d9fb020492a955e641e30e7a425f3588bf346cb3e61fec3c3"
+dependencies = [
+ "parking_lot",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "toml_edit",
+]
+
+[[package]]
+name = "bevy_math"
+version = "0.17.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fa74ae5d968749cc073da991757d3c7e3504ac6dbaac5f8c2a54b9d19b0b7ed"
+dependencies = [
+ "approx",
+ "bevy_reflect",
+ "derive_more",
+ "glam",
+ "itertools",
+ "rand",
+ "rand_distr",
+ "serde",
+ "smallvec",
+ "thiserror",
+ "variadics_please",
+]
+
+[[package]]
+name = "bevy_platform"
+version = "0.17.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4691af6d7cfd1b5deb2fc926a43a180a546cdc3fe1e5a013fcee60db9bb2c81f"
+dependencies = [
+ "critical-section",
+ "foldhash",
+ "futures-channel",
+ "hashbrown 0.16.1",
+ "js-sys",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde",
+ "spin",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "bevy_ptr"
+version = "0.17.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17d24d7906c7de556033168b3485de36c59049fbaef0c2c44c715a23e0329b10"
+
+[[package]]
+name = "bevy_reflect"
+version = "0.17.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5472b91928c0f3e4e3988c0d036b00719f19520f53a0c3f8c2af72f00e693c5"
+dependencies = [
+ "assert_type_match",
+ "bevy_platform",
+ "bevy_ptr",
+ "bevy_reflect_derive",
+ "bevy_utils",
+ "derive_more",
+ "disqualified",
+ "downcast-rs",
+ "erased-serde",
+ "foldhash",
+ "glam",
+ "serde",
+ "smol_str",
+ "thiserror",
+ "uuid",
+ "variadics_please",
+ "wgpu-types",
+]
+
+[[package]]
+name = "bevy_reflect_derive"
+version = "0.17.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "083784255162fa39960aa3cf3c23af0e515db2daa7f2e796ae34df993f4d3f6c"
+dependencies = [
+ "bevy_macro_utils",
+ "indexmap",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "uuid",
+]
+
+[[package]]
+name = "bevy_utils"
+version = "0.17.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "789d04f88c764877a4552e07745b402dbc45f5d0545e6d102558f2f1752a1d89"
+dependencies = [
+ "bevy_platform",
+ "disqualified",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
+
+[[package]]
+name = "bytemuck"
+version = "1.25.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f65693059b6b9c588b9f62fed1cedbf0a8b805631457ea162d68f0de186f3de5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "critical-section"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "derive_more"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "disqualified"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd"
+
+[[package]]
+name = "downcast-rs"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc"
+
+[[package]]
+name = "either"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "erased-serde"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec"
+dependencies = [
+ "serde",
+ "serde_core",
+ "typeid",
+]
+
+[[package]]
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
+[[package]]
+name = "futures-channel"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
+
+[[package]]
+name = "futures-util"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "gamecult-geometry"
+version = "0.1.0"
+dependencies = [
+ "approx",
+ "bevy_math",
+ "rmp-serde",
+ "serde",
+ "sha2",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi 5.3.0",
+ "wasip2",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi 6.0.0",
+]
+
+[[package]]
+name = "glam"
+version = "0.30.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9"
+dependencies = [
+ "bytemuck",
+ "rand",
+ "serde_core",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+dependencies = [
+ "equivalent",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+
+[[package]]
+name = "indexmap"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.17.1",
+]
+
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.189"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
+
+[[package]]
+name = "libm"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
+
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
+
+[[package]]
+name = "memchr"
+version = "2.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-link",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "portable-atomic"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
+dependencies = [
+ "portable-atomic",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
+[[package]]
+name = "rand"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
+dependencies = [
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
+dependencies = [
+ "getrandom 0.3.4",
+]
+
+[[package]]
+name = "rand_distr"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463"
+dependencies = [
+ "num-traits",
+ "rand",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "rmp"
+version = "0.8.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "rmp-serde"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
+dependencies = [
+ "rmp",
+ "serde",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "semver"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
+
+[[package]]
+name = "serde"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
+[[package]]
+name = "smallvec"
+version = "1.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
+
+[[package]]
+name = "smol_str"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "spin"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3"
+dependencies = [
+ "portable-atomic",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.119"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.7.5+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.23.10+spec-1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "toml_parser",
+ "winnow 0.7.15",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.1.2+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
+dependencies = [
+ "winnow 1.0.4",
+]
+
+[[package]]
+name = "typeid"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
+
+[[package]]
+name = "typenum"
+version = "1.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "uuid"
+version = "1.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
+dependencies = [
+ "getrandom 0.4.3",
+ "js-sys",
+ "serde_core",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "variadics_please"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "wasip2"
+version = "1.0.4+wasi-0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "wgpu-types"
+version = "26.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eca7a8d8af57c18f57d393601a1fb159ace8b2328f1b6b5f80893f7d672c9ae2"
+dependencies = [
+ "bitflags",
+ "bytemuck",
+ "js-sys",
+ "log",
+ "serde",
+ "thiserror",
+ "web-sys",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "winnow"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "winnow"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
+
+[[package]]
+name = "wit-bindgen"
+version = "0.57.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
+
+[[package]]
+name = "zerocopy"
+version = "0.8.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
diff --git a/packages/gamecult-geometry-rs/Cargo.toml b/packages/gamecult-geometry-rs/Cargo.toml
new file mode 100644
index 00000000..b05c0246
--- /dev/null
+++ b/packages/gamecult-geometry-rs/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "gamecult-geometry"
+version = "0.1.0"
+edition = "2024"
+description = "GameCult.Geometry typed contracts and geometry runtime for Rust"
+license = "MIT"
+repository = "https://github.com/GameCult/CultLib"
+readme = "README.md"
+
+[dependencies]
+bevy_math = "0.17"
+rmp-serde = "1"
+serde = { version = "1", features = ["derive"] }
+sha2 = "0.10"
+
+[dev-dependencies]
+approx = "0.5"
diff --git a/packages/gamecult-geometry-rs/LICENSE b/packages/gamecult-geometry-rs/LICENSE
new file mode 100644
index 00000000..a431c3ab
--- /dev/null
+++ b/packages/gamecult-geometry-rs/LICENSE
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2026 GameCult
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/packages/gamecult-geometry-rs/README.md b/packages/gamecult-geometry-rs/README.md
new file mode 100644
index 00000000..0b3ec053
--- /dev/null
+++ b/packages/gamecult-geometry-rs/README.md
@@ -0,0 +1,11 @@
+# GameCult.Geometry for Rust
+
+Rust projection of the GameCult.Geometry v2 document contract. It owns the
+typed MessagePack layouts and stable geometry record identities used at the
+Rust boundary; it does not depend on VibeGeometry or `vg-csg`.
+
+The initial contract and stable-hash implementation were mined from the
+MIT-licensed GameCult `vg-csg` crate at commit `8f070f4`. The implementation
+was rewritten here under GameCult.Geometry ownership. The original copyright
+and MIT terms are preserved in [LICENSE](LICENSE).
+
diff --git a/packages/gamecult-geometry-rs/src/assembler.rs b/packages/gamecult-geometry-rs/src/assembler.rs
new file mode 100644
index 00000000..078a07b5
--- /dev/null
+++ b/packages/gamecult-geometry-rs/src/assembler.rs
@@ -0,0 +1,1509 @@
+#![allow(dead_code)]
+use std::cell::RefCell;
+
+use bevy_math::Vec3;
+
+use crate::{
+ Aabb, Brush, BrushId, BrushOp, ConvexPolygon, ConvexSolid, DirtyDemandFrontier, MaterialId,
+ PolygonRouteScratch, Primitive, TriangleMesh, append_cylinder_z, append_dome_cap_z,
+ append_floret_arm,
+ primitives::{DomeCapZSpec, FloretArmSpec},
+};
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum BuildWarning {
+ SubtractIgnoredForNonBox { brush: String },
+ IntersectIgnoredForNonBox { brush: String },
+}
+
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub struct BuildReport {
+ pub input_brushes: usize,
+ pub emitted_convex_fragments: usize,
+ pub operator_brushes: usize,
+ pub candidate_pairs: usize,
+ pub rejected_pairs: usize,
+ pub reused_mesh: bool,
+ pub used_checkpoint: bool,
+ pub checkpoint_rebuild_from: Option,
+ pub direct_dirty_rebuild: bool,
+ pub warnings: Vec,
+}
+
+#[derive(Clone, Debug)]
+pub struct BuildOutput {
+ pub mesh: TriangleMesh,
+ pub report: BuildReport,
+ pub generation: u64,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct Assembler {
+ brushes: Vec,
+ compiled: Vec,
+ cache: RefCell