diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..f1fe478c4 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,81 @@ +# Aetheria Agent Instructions + +This repo is mid-migration toward CultMesh-native gameplay state. Treat access +simplicity as an architectural requirement, not a nice-to-have. + +## Canonical Typed State + +- Default to one canonical shared typed document for each gameplay state concept. + The daemon may own authority over that document, but it must not imply a + separate private daemon truth plus a copied client-facing state document. +- The shared runtime assembly defines the document. The daemon mutates/publishes + it. Unity, Electron, tools, tests, and later runtimes read the same document + type through CultMesh. +- Client input should feel like grabbing a managed typed state handle and either + reading it for display or writing/submitting through it according to authority + policy. Prediction, debounce, routing, quorum, reconciliation, and smoothing + are CultMesh responsibilities. + +Use projections only when the state shape is intentionally different: + +- hidden-information filtering; +- expensive or shared derived aggregation; +- viewport/windowed selection; +- SoA/native render or physics layout; +- lossy UI summaries; +- Eve/CultUI surface documents; +- compatibility mirrors with a named removal stage. + +"The client needs to see it" is not a projection reason. + +## Access Simplicity + +- A UI-only inline value should be derived from already accessible typed state. +- A shared UI read should usually be: define the typed document, then read it + with `client.State.Reactive()` or `client.State.Latest()`. +- A simulation feature should usually be: define the canonical typed document, + let daemon authority mutate/publish it, then read or modify that same managed + document from clients according to authority policy. +- Add named handles only when type alone cannot identify the state, such as + parameterized viewport/detail documents, multiple documents sharing one CLR + type, semantic query identity, operation policy, or native view ownership. + +## Heretek Shape + +The facade/projector/adapter/surface-builder chain is heretek now. Do not add a +stack of translation layers to recover one typed domain value. + +One adapter at a true boundary is allowed: + +- Unity GameObject presentation; +- Eve/CultUI lowering; +- legacy import; +- persistence; +- native view ownership. + +Two or more translation layers in a row mean the code is missing a CultMesh +primitive, generated typed handle, canonical document, query surface, operation +handle, state pointer, or native view descriptor. + +## Verifier Discipline + +The verifier should encode the desired architecture, not the current accidental +shape. When removing old access paths, add checks that reject their return. Do +not write verifier rules that require wrapper/session/facade/projector chains as +proof of correctness. + +Good verifier pressure: + +- client code owns `CultMeshReactiveDocument`; +- client code reads `.Current`; +- client code uses generic typed document access where possible; +- hot render paths use named SoA/native views; +- operations are typed and authority-aware; +- legacy adapters are quarantined and named for deletion. + +Bad verifier pressure: + +- requiring `LatestFoo()`/`ReactiveFoo()` wrappers for single fixed documents; +- requiring one-document session wrappers; +- requiring facade/projector/adapter/surface-builder chains; +- treating renderer-local facade indexes as gameplay-state APIs. diff --git a/Aetheria.State/docs/cultmesh-cross-runtime-primitives.md b/Aetheria.State/docs/cultmesh-cross-runtime-primitives.md index 193a060a1..553b4e075 100644 --- a/Aetheria.State/docs/cultmesh-cross-runtime-primitives.md +++ b/Aetheria.State/docs/cultmesh-cross-runtime-primitives.md @@ -32,6 +32,28 @@ compiles down to the fastest safe path the runtime can prove. 6. Native views expose zero-copy or near-zero-copy slices when safe, with the same state semantics as remote query/subscription fallbacks. 7. Schema evolution is part of the developer experience. Bindings and migration manifests come from one source instead of hand-maintained slot maps. +## Canonical State Before Projection + +CultMesh should make one canonical typed document feel native everywhere. If a +feature's game state is `AetheriaRuntimeFooDocument`, the daemon, Unity, +Electron, browser, tools, and future Rust clients should all hold managed +handles to that document type. Authority, prediction, debouncing, +reconciliation, quorum, locality, and transport are handle/runtime behavior, +not reasons for each application to create a private "truth" object and then a +client projection copy. + +A projection is a different state shape, not the normal visibility mechanism. +Use projected documents or query surfaces for hidden-information filtering, +derived aggregation, viewport/windowing, SoA/native memory layout, lossy UI +summaries, Eve/CultUI surfaces, or temporary compatibility bridges. Do not +project merely because a client needs to read daemon-owned state. + +This rule is cross-runtime. C#, Rust, TypeScript, Unity, browser/WASM, and tool +hosts should all express the same intent: grab a typed document/collection/query +handle, read it for display, or mutate/submit through it when authority policy +allows. CultMesh supplies the managed access conventions and generated handles +that make that simple. + ## Ownership Boundary | Belongs in CultMesh/CultLib | Belongs in Aetheria | diff --git a/Aetheria.State/docs/cultmesh-ergonomics-staged-migration-plan.md b/Aetheria.State/docs/cultmesh-ergonomics-staged-migration-plan.md index 8dc8a9a15..3134b6e3e 100644 --- a/Aetheria.State/docs/cultmesh-ergonomics-staged-migration-plan.md +++ b/Aetheria.State/docs/cultmesh-ergonomics-staged-migration-plan.md @@ -103,6 +103,16 @@ The project already has the first layer of primitive sugar: The remaining problem is that the sugar is not yet the only obvious path. There are still public or semi-public seams where clients can fall back into raw envelopes, record keys, ad hoc frame projection, manual state resolution, locality-specific wiring, and renderer-side facade adaptation. +The architectural correction is simple and strict: a normal gameplay feature has +one canonical typed document. The daemon may be the authority for that document, +but daemon authority is not a second state model. The shared runtime assembly +defines the document, the daemon mutates/publishes it, and every runtime grabs a +managed handle to that same type for display, input, prediction, or testing. +Projection documents exist only when the state shape is intentionally different: +hidden-information filtering, expensive aggregation, viewport/windowing, +SoA/native layout, lossy UI summaries, Eve/CultUI surfaces, or compatibility +bridges with a removal stage. + The worst current pattern is not one bad helper. It is a repeated protocol walk: 1. read one projected document; @@ -114,6 +124,12 @@ The worst current pattern is not one bad helper. It is a repeated protocol walk: That entire stack must collapse behind one typed CultMesh document, collection, query, or operation handle. If a client wants current docking, current inventory, current target, current stats, zone contacts, station refit state, or visible render objects, the caller should make one semantic CultMesh call and receive a typed reactive value. CultMesh owns cache hydration, routing, sync, invalidation, watch lifetimes, schema binding, derived-state execution, and locality choice. +The facade/projector/adapter/surface-builder chain is no longer an acceptable +style. A single adapter at a real boundary can be honest; a chain of translation +objects to recover one domain value is heretek architecture. Replace it with a +canonical document handle, a generated typed query/operation handle, a state +pointer, or a native view descriptor. + ## Migration Rules 1. Repeated Aetheria-specific glue becomes a generated Aetheria facade. @@ -125,6 +141,9 @@ That entire stack must collapse behind one typed CultMesh document, collection, 7. The browser/RTS client and Unity client must use the same semantic Verse facade, even when their locality/runtime routes differ. 8. Client code must not manually compose state access from multiple protocol layers. A semantic state read is one CultMesh call from the caller's point of view. 9. Renderer-local object facades are presentation caches. They are not the API for game state, inventory, docking, targeting, or station services. +10. Do not split "daemon truth" from "CultMesh typed state" for ordinary gameplay. Use one canonical document, then let authority policy decide who can mutate, predict, or only observe it. +11. Add projections only for a distinct derived/filter/window/native/UI shape. "A client needs to read it" is not a projection justification. +12. Do not add facade/projector/adapter/surface-builder chains. One real boundary adapter is allowed; stacked translation layers are a missing CultMesh primitive. ## Stage 0: Freeze The Vocabulary @@ -153,6 +172,11 @@ Gates: - No new public API may introduce `Apply(command, payload)` style call sites. - No new client API may require a caller to know transport record keys or raw schema slots. - No new client API may require a caller to join projected documents, frame rows, record keys, and renderer-local facades to obtain one domain value. +- No new feature guide, example, or verifier rule may describe separate daemon + truth and client typed state when one canonical document would work. +- No verifier may require a facade/projector/adapter/surface-builder chain as + proof of correctness. It should require the semantic typed handle and reject + the translation chain. - All new examples in docs must use the North Star shape. ## Stage 1: Harden Shared CultMesh Primitives @@ -235,7 +259,11 @@ npm run check:rts-bindings Status: partially started; local projection still leaks. -Goal: clients ask the daemon for typed projections. They do not reconstruct world truth from frames unless they are inside a generated/internal query executor. +Goal: clients ask CultMesh for typed query surfaces or projected documents when +the requested shape is derived, filtered, windowed, or native-layout-specific. +They do not reconstruct world truth from frames unless they are inside a +generated/internal query executor, and they do not add projections for canonical +state that a typed document handle can expose directly. Targets: diff --git a/Aetheria.State/docs/stage-7-thin-client-staged-implementation-plan.md b/Aetheria.State/docs/stage-7-thin-client-staged-implementation-plan.md index 6ae7be919..35eb82fab 100644 --- a/Aetheria.State/docs/stage-7-thin-client-staged-implementation-plan.md +++ b/Aetheria.State/docs/stage-7-thin-client-staged-implementation-plan.md @@ -143,9 +143,10 @@ Completed and usable: daemon delegates the temporary RUDP compatibility hook to it. - `AetheriaRuntimeStarbridgeProjection` owns the first S0 session-summary projection, and `AetheriaClient.StarbridgeSessionSummaryAsync` exposes it as - a named facade read without routing through map viewport state. Scenario and - session facts now have latest-record keys in local Verse, so clients can ask - the facade for the active Starbridge summary without passing documents around. + a semantic managed read without routing through map viewport state. Scenario + and session facts now have latest-record keys in local Verse, so clients can + ask the client state surface for the active Starbridge summary without passing + documents around. - The daemon boot path seeds the first Starbridge scenario/session facts from daemon-owned local state. A fresh once-mode daemon publishes `Frontier Fabricator Defense` as typed scenario/session documents, and the authority @@ -1147,7 +1148,7 @@ allowed to become Electron/RTS features. | 1 | Sector topology | `SectorMap`, `SectorRenderer`, and main-menu observed-game boot consume `SectorMapAsync`, `CurrentZoneAsync`, daemon zone contents, catalog, and player settings through explicit clients. `SectorMapAsync` carries tutorial mode, generation seed, and faction relationships so Unity can lower the temporary observed galaxy without a whole daemon frame. | `SectorMap` no longer reads `ActionGameManager.TryGetObservedGalaxy`, passes `GalaxyZone`, or owns a legacy faction graph. `MainMenu` may not call `LatestAuthoritativeRunFrameAsync` for observed-game boot. | `verify-stage7d-unity-parity.ps1`; scoped search for `TryGetObservedGalaxy`, `GalaxyZone`, `Dictionary(entityIndex); +using var docking = aetheria.State.Reactive(); +using var support = aetheria.State.Reactive(); -var stationRefit = await aetheria.Station.Refit.LatestAsync(); +var stationRefit = await aetheria.State.LatestAsync(); var visibleObjects = await aetheria.Zone(currentZoneId) .Objects .VisibleToCurrentPlayer() @@ -73,6 +82,13 @@ Verse, bind an Eve state pointer, or expose a native slab view. That decision is CultMesh infrastructure. The caller gets a typed domain value and diagnostics when it asks for them. +Client input follows the same rule. When a runtime has authority or prediction +rights, mutating the managed typed document is the ergonomic target; CultMesh +debounces, routes, records the prediction, and reconciles against canonical +state. When a runtime does not have direct write authority, it submits a typed +operation handle that the daemon validates and applies to the same canonical +document. + Unity should treat the client as its Aetheria-facing runtime surface. It can still use Burst, DOTS rendering, and native views for presentation, but the state authority boundary is the typed CultMesh contract. No Unity-facing code should diff --git a/docs/cultmesh-feature-implementation-guide.md b/docs/cultmesh-feature-implementation-guide.md index 7f7e0e51d..531ad4a80 100644 --- a/docs/cultmesh-feature-implementation-guide.md +++ b/docs/cultmesh-feature-implementation-guide.md @@ -5,13 +5,15 @@ Date: 2026-06-28 This guide describes the target developer experience for adding Aetheria state with CultMesh. If the workflow grows into a chain of facades, sessions, projectors, adapters, and surface builders, stop and simplify the state API. +That chain is heretek now: it means the feature is compensating for a missing +typed document, query, operation, pointer, native view, or generated handle. The rule is blunt: ```text small UI-only value: derive it inline from already accessible typed state named UI-only projection: define the projected document, then read it -simulation feature: define simulation state, publish typed state, consume it +simulation feature: define one canonical typed document, let daemon authority mutate it, consume the same document everywhere hot rendering/physics: use SoA, not object-graph UI documents ``` @@ -19,6 +21,36 @@ CultMesh owns document identity, sync, prediction, reconciliation, and reactive access. Aetheria code should read like it is using game state, not walking protocol layers. +## Canonical Document Rule + +The default shape is one shared typed document contract. + +```text +AetheriaRuntimeFooDocument + -> daemon authority mutates/publishes it + -> Unity/Electron/tools read the same document type + -> authorized clients write intent or predictions through the managed handle +``` + +Do not split a feature into "daemon truth" and a separate "CultMesh typed +state" unless the second shape is intentionally different. The assembly is +shared, so the document the daemon owns is also the document clients receive. +Across runtimes, callers should feel like they are grabbing a handle on typed +state and either reading it for display or writing to it for client input. + +A second document is justified only when it earns a distinct job: + +- hidden-information filtering; +- expensive or shared derived state; +- viewport/windowed selection; +- SoA/native render or physics layout; +- lossy presentation summaries; +- UI surface documents; +- compatibility mirrors with a named removal stage. + +Projection is not the normal way to make state client-visible. Projection is an +exception for a different shape. + ## The Ergonomic Bar UI-only state should take one or two steps. @@ -27,7 +59,7 @@ Use one step when the value is small and already derivable from state the caller has: ```csharp -var currentDocking = client.State.Current.ReactiveDocking(); +var currentDocking = client.State.Reactive(); var bayName = currentDocking.Current?.DockingBayName ?? ""; ``` @@ -35,16 +67,16 @@ Use two steps when the projection is shared, non-trivial, or should have a stable schema: ```text -1. Define the daemon-projected typed document. +1. Define the typed projected document. 2. Read it from Unity as a managed reactive typed document. ``` Simulation state should take two or three steps: ```text -1. Put the simulation data and rules in the daemon-owned domain model. -2. Publish the useful client-facing shape as typed CultMesh state. -3. Read that state from Unity and send typed operations back when input mutates truth. +1. Define the canonical typed document in the shared runtime package. +2. Let the daemon mutate/publish that document as the authority for the Verse. +3. Read or modify the same managed document from clients according to authority policy. ``` Anything beyond that needs a clear reason. Performance-sensitive entity @@ -86,7 +118,11 @@ daemon document -> session -> facade -> projector -> adapter -> surface builder ``` That shape preserves reconstruction-era scaffolding. It does not express the -game. +game. At most, one boundary adapter may exist at an actual boundary: Unity +GameObject presentation, Eve/CultUI lowering, legacy import, persistence, or +native view ownership. If two or more layers appear in a row just to obtain one +domain value, stop and move the missing primitive into CultMesh/CultLib or the +generated Aetheria handle surface. ## Choosing The State Shape @@ -105,18 +141,21 @@ Use a projected typed document when: - the result needs a stable schema or cross-runtime binding; - the projection should update reactively as daemon state changes. -Use daemon-owned simulation state when: +Use a canonical simulation document when: - the value affects game rules; - input can mutate it; - it needs authority, replay, persistence, or reconciliation; - the daemon must validate operations before publishing the next state. +Use a projected typed document when the caller needs a different state shape, +not merely because a client needs to see daemon-owned state. + Use SoA/native views when: - Unity jobs, Burst, Ymir, or rendering need large columnar data; - row count and per-frame access make object graphs the wrong format; -- the data is a hot view over daemon truth rather than a UI document. +- the data is a hot view over canonical simulation state rather than a UI document. ## One-Step UI Derivation @@ -140,7 +179,8 @@ public sealed class DockingBadge : IDisposable { get { - _docking ??= _client.State.Current.ReactiveDocking(); + _docking ??= _client.State + .Reactive(); var current = _docking.Current; return current?.IsDocked == true ? current.DockingBayName : ""; } @@ -162,6 +202,11 @@ document. Use this for shared presentation state such as player HUD, docking summary, commander wave status, station service summary, or Starbridge support alerts. +Before adding this projection, ask whether the UI can read the canonical +simulation document directly. If it can, do that. The projection exists only +when the UI shape is genuinely derived, filtered, windowed, or shared enough to +deserve its own schema. + ### Step 1: Define The Projected Document Put the document type in the shared runtime package so every runtime sees the @@ -195,20 +240,17 @@ public sealed class AetheriaRuntimeZoneDefenseStatusDocument } ``` -Then expose it as a named projection from `AetheriaClientState`: +Then make it available through generic managed state access: ```csharp -public CultMeshReactiveDocument - ReactiveZoneDefenseStatus() -{ - return Mesh.Reactive( - AetheriaRuntimeDaemonDocuments.ZoneDefenseStatus); -} +using var status = + client.State.Reactive(); ``` The exact registration/publishing code belongs in the runtime plumbing. It should be generated or centralized. The feature author should not have to write -five separate access classes for one projected state shape. +five separate access classes, wrapper methods, sessions, or facades for one +projected state shape. ### Step 2: Read It From Unity @@ -230,7 +272,8 @@ public sealed class ZoneDefenseHud : IDisposable public void Refresh() { - _status ??= _client.State.ReactiveZoneDefenseStatus(); + _status ??= _client.State + .Reactive(); var status = _status.Current; if (status == null) return; @@ -256,25 +299,10 @@ Use this when the feature changes gameplay: heat support, repair drones, station construction, scenario progression, commander infrastructure, or wave director state. -### Step 1: Put Truth In The Daemon +### Step 1: Define The Canonical Document -Simulation truth lives with the daemon domain model and tick/apply logic. - -```csharp -public sealed class StationSupportState -{ - public double CoolingReserve { get; set; } - public double RepairNaniteReserve { get; set; } - public int ActiveSupportDrones { get; set; } -} -``` - -The daemon mutates this state during ticks and command application. Unity does -not own gameplay truth. - -### Step 2: Publish Typed CultMesh State - -Expose the client-facing shape as a typed document. +Simulation truth is the shared typed document. The daemon owns authority over +that document, but the document contract is not daemon-private. ```csharp [MessagePackObject] @@ -292,21 +320,26 @@ public sealed class AetheriaRuntimeStationSupportDocument } ``` -The daemon publishes this document from its current truth. CultMesh handles sync. -If the client has prediction authority, local changes to the managed document -are predictions; reconciliation corrects them without requiring feature code to +### Step 2: Let The Daemon Own Authority + +The daemon validates operations, applies ticks, and publishes the canonical +document into the Verse. Unity does not own gameplay truth, but Unity does not +need a separate client-facing copy either. CultMesh handles sync. If the client +has prediction authority, local changes to the managed document are +predictions; reconciliation corrects them without requiring feature code to manually shuttle deltas. -### Step 3: Read State And Submit Typed Operations +### Step 3: Read Or Mutate Through The Managed Handle -Unity reads state through the managed typed document: +Unity reads the same document through the managed typed handle: ```csharp -var support = client.State.ReactiveStationSupport(); +var support = client.State.Reactive(); var cooling = support.Current?.CoolingReserve ?? 0; ``` -Unity asks the daemon to mutate truth through a typed operation: +When the client does not have direct simulation authority, it submits a typed +operation: ```csharp client.Operations.Submit(new AetheriaRuntimeDeploySupportDroneOperation @@ -320,6 +353,11 @@ client.Operations.Submit(new AetheriaRuntimeDeploySupportDroneOperation The daemon validates and applies the operation. The next published document is the authoritative result. +When the client has prediction or simulation authority, the ergonomic target is +even simpler: modifying the managed reactive document records a prediction, +debounced by the update frame, and CultMesh routes/reconciles it according to +the Verse authority policy. + ## SoA For Hot Paths Use SoA when Unity needs fast columnar access, not when a UI needs a label. @@ -340,9 +378,9 @@ Bad SoA candidates: - player settings; - station service summary. -SoA should be a named high-performance view over daemon truth. It should not -force ordinary presentation code to understand frame slabs, column handles, or -native buffer ownership. +SoA should be a named high-performance view over canonical simulation state. It +should not force ordinary presentation code to understand frame slabs, column +handles, or native buffer ownership. ## Reconnection @@ -373,13 +411,13 @@ For projected UI documents, test: - projection from representative daemon state; - empty/default state; - reactive update after source state changes; -- Unity caller reads the projected document directly. +- Unity caller reads the typed document directly. For simulation features, test: -- daemon tick/application mutates truth; +- daemon tick/application mutates the canonical typed document; - typed operation validation accepts and rejects correctly; -- published document reflects daemon truth; +- clients read the same shared document type the daemon publishes; - prediction/reconciliation behavior when the runtime supports local authority; - reconnect can reconstruct the required player state. @@ -401,8 +439,9 @@ Good verifier expectations: ```text Unity caller owns CultMeshReactiveDocument Unity caller reads .Current -Unity caller uses client.State.ReactiveFeatureName() +Unity caller uses client.State.Reactive() Unity caller submits typed operations for mutations +authorized caller mutates managed typed state as prediction/input hot renderer uses named SoA view ``` @@ -413,6 +452,7 @@ Unity caller owns AetheriaRuntime*Session for one document Unity caller calls ObserveFeatureName() wrapper only to read Current UI caller reads raw daemon frames and joins rows feature adds facade/projector/adapter/surface-builder chain +feature introduces multiple translation layers to get one typed value ``` The verifier should make the codebase harder to regress into ceremony, not @@ -425,8 +465,8 @@ Before calling a CultMesh feature ergonomic, answer these questions: - Can a UI-only inline value be implemented in one local derivation? - Can a shared UI projection be implemented by defining the document and reading it from Unity? -- Can a simulation feature be explained as daemon truth, typed state, typed - operation? +- Can a simulation feature be explained as one canonical typed document plus + authority policy and typed operations? - Does Unity code read domain state instead of protocol plumbing? - Is SoA reserved for hot paths? - Did the verifier protect the clean path instead of the old wrappers? diff --git a/docs/developer-navigation.md b/docs/developer-navigation.md index d6a4474d2..b86f499b5 100644 --- a/docs/developer-navigation.md +++ b/docs/developer-navigation.md @@ -10,8 +10,9 @@ ambushed by half-finished architecture. The short version: Unity is being reduced to a renderer, input surface, and editor environment. Durable game state and portable gameplay facts now live in typed CultCache/CultNet/CultMesh records under `Aetheria.State`. The daemon is -the simulation owner. Unity and the Electron RTS client are clients of the same -typed Verse state. +the authority over canonical shared documents, not a private truth source that +exports a second client copy. Unity and the Electron RTS client are clients of +the same typed Verse state. ## Start Here @@ -29,8 +30,9 @@ Read these in this order: 5. `docs/aetheria-verse-client-contract.md` Intended client contract: use typed Verse state, not daemon internals. 6. `docs/cultmesh-feature-implementation-guide.md` - Step-by-step guide for adding daemon-owned typed state, publishing it through - CultMesh, and consuming/interacting with it from Unity. + Step-by-step guide for adding canonical typed state once, letting daemon + authority own mutation, and consuming/interacting with the same document from + Unity and other clients. 7. `Aetheria.State/docs/verse-authority-implementation-plan.md` Authority and Starbridge staged implementation map. @@ -186,8 +188,12 @@ Expected recurring warnings: ### State Owner -Typed CultCache/CultNet/CultMesh documents are the state owner. The primary -world file is: +Typed CultCache/CultNet/CultMesh documents are the state owner. For normal +gameplay features, define one canonical shared document and let authority policy +decide who may write or predict it. Do not create "daemon truth" plus a separate +client-facing copy unless the second document is intentionally filtered, +aggregated, windowed, lossy, SoA/native, or a named compatibility bridge. The +primary world file is: ```text GameData/aetheria-world.cc @@ -220,14 +226,15 @@ Use `AetheriaClient` first: Packages/org.gamecult.aetheria.state/Runtime/AetheriaClient.cs ``` -It exposes named reads such as current entity, current docking, station/refit, -sector map, viewport, Starbridge summary, health, authority policy, and SoA -views. It also exposes typed controls through `AetheriaControl` and typed UI -surface helpers through `AetheriaUi`. +It exposes generic managed document reads such as `State.Latest()` +and `State.Reactive()`, parameterized handles for viewport/detail +documents, Starbridge seat handles, typed controls through `AetheriaControl`, +and typed UI surface helpers through `AetheriaUi`. -If a Unity panel or Electron surface needs a fact, prefer adding or extending a -named facade read. Avoid handing callers raw daemon frames unless the caller is -a transitional projection layer. +If a Unity panel or Electron surface needs a fact, prefer the generic managed +document call first. Add a named facade only when it carries semantic identity, +parameters, operation policy, or a distinct derived/native shape. Avoid handing +callers raw daemon frames unless the caller is a transitional projection layer. ### Unity Shell @@ -373,6 +380,12 @@ Work on Ymir/physics: Git preserves history. The migration should delete dead shims when the typed replacement exists and tests prove it. +8. Treat facade/projector/adapter/surface-builder chains as a design failure. + One adapter at a true boundary is fine. Stacked translation layers used to + recover one typed value mean the code is missing a CultMesh primitive, + generated handle, canonical document, query, operation, pointer, or native + view. + ## Known Transitional Debt These are not surprises; they are known pressure points. @@ -407,9 +420,13 @@ Use this checklist before writing code: Add or extend a MessagePack/CultNet document in the runtime package. Use explicit `[Key]` slots and stable names. -3. Add a facade read or operation. - Prefer `AetheriaClient`, `AetheriaControl`, or `AetheriaUi` over direct - low-level Verse calls in client code. +3. Add the simplest access path. + Prefer `AetheriaClient.State.Latest()` or + `AetheriaClient.State.Reactive()` for canonical documents. Add a + named handle only when type alone cannot identify the document, such as + parameterized viewport/detail state or multiple documents sharing one CLR + type. Use `AetheriaControl` or `AetheriaUi` for typed operations and UI + commands. 4. Lower Unity and Electron through the same semantics. Unity and RTS do not need identical UI, but they should observe the same @@ -498,8 +515,9 @@ The useful way to look at it is: When in doubt, follow the typed state: ```text -document -> facade -> client presentation -command document -> daemon apply -> projected result +canonical document -> managed CultMesh handle -> client presentation/input +typed operation -> daemon authority -> same canonical document +projection/native view -> only when the shape is intentionally different ``` If a path does not fit that shape, it is probably transitional debt or a bug.