Project

General

Profile

User Story #68 » 0006-feat-ui-render-role-aware-render-safeguard-anchors-s.patch

knight8241, 08/07/2026 18:33

View differences:

androidApp/src/main/java/com/aether/mofe/ui/AppShell.kt
import com.aether.mofe.ui.design.ScreenHeader
import com.aether.mofe.ui.design.StatusPill
import com.aether.mofe.ui.design.ToastHost
import com.aether.mofe.engine.render.MeshDisplayPolicy
import com.aether.mofe.engine.render.MeshSurface
import com.aether.mofe.ui.design.ToastTone
import com.aether.mofe.ui.design.meshLabel
import com.aether.mofe.ui.design.meshTone
......
val constellationFrame by app.mofeEngineHost.constellationFrame.collectAsState()
val geometryReport by app.mofeEngineHost.geometryReport.collectAsState()
// Role-aware startup surface (safeguard). A CLIENT is protected from unsolved locations
// pre-quorum (the scene holds); an ANCHOR/ROOT is instead SHOWN that state — coming up
// auto-joining its last mesh, it defaults straight to the diagnostic panel. The rule lives
// in engine.render.MeshDisplayPolicy (pure + unit-tested); here we only supply role + readiness.
val selfIsAnchorOrRoot = shared.nodes
.firstOrNull { it.nodeId == selfId }
?.meshRoles?.any { it.equals("ANCHOR", true) || it.equals("ROOT", true) } == true
val startRoute = when (
MeshDisplayPolicy.startupSurface(
isAnchorOrRoot = selfIsAnchorOrRoot,
readiness = renderReadiness,
autoJoinBringup = true, // first composition = app bringup into the last mesh
)
) {
MeshSurface.DIAGNOSTICS -> Routes.Calibrate
else -> Routes.Visualizer
}
val realizerScope = rememberCoroutineScope()
val realizerVm = remember { PredicateRealizerViewModel(app.meshRepository, realizerScope) }
......
},
) {
Box(Modifier.fillMaxSize()) {
NavHost(navController = navController, startDestination = Routes.Visualizer) {
NavHost(navController = navController, startDestination = startRoute) {
// HOME is the Filament scene (3b promotion). Its own top-left hamburger opens the drawer.
composable(Routes.Visualizer) {
val anchorIds = shared.nodes
common/src/commonMain/kotlin/com/aether/mofe/engine/render/MeshDisplayPolicy.kt
package com.aether.mofe.engine.render
/**
* Role-aware surface selection — the second half of the render-readiness safeguard.
*
* [MeshRenderReadiness] answers the objective question *"may solved locations be drawn
* yet?"*. It does NOT know this device's mesh role, and the field rule depends on it:
*
* - A **client** (a plain member / observer) is *protected*. Before the mesh is
* [RenderReadinessState.READY] it must not be shown unsolved locations — the raw,
* spatially-wrong, jittery view is indistinguishable from a working one — so it
* HOLDS with a status. Once READY it renders the spatial scene.
* - An **anchor / root** is the opposite party. The pre-quorum mesh state (links,
* quorum progress, geometry) is exactly what its operator is there to watch, so it
* is shown the DIAGNOSTICS surface rather than a client hold. And when the app comes
* up auto-joining its last mesh *as* an anchor/root, DIAGNOSTICS is the default
* surface it lands on — building/monitoring the mesh is the job in front of them.
*
* Pure and platform-agnostic: the host supplies the role + readiness + bringup signal;
* the UI maps the resulting [MeshSurface] to its own navigation destination / content.
*/
enum class MeshSurface {
/** Render the 3-D / FPV spatial scene (solved node positions + FPV camera). */
SPATIAL_SCENE,
/** Hold with a status label — a client that must not draw unsolved locations yet. */
HOLDING,
/** The diagnostic panel: mesh health, quorum progress, inter-anchor links, calibration. */
DIAGNOSTICS,
}
/** The chosen [surface] plus a one-line [reason] for the dev diagnostic / logs. */
data class MeshDisplayDecision(val surface: MeshSurface, val reason: String)
object MeshDisplayPolicy {
/**
* Decide what this device should display.
*
* @param isAnchorOrRoot this device backs the mesh frame — it holds an ANCHOR or ROOT
* mesh role (as opposed to a plain client / member). The host computes it from
* the self node's mesh roles (or the current Raft root).
* @param readiness objective spatial readiness from [MeshRenderReadiness.evaluate].
* @param autoJoinBringup the app has just launched and is auto-joining its last mesh
* (i.e. this is the startup surface decision, not a mid-session re-evaluation).
*/
fun decide(
isAnchorOrRoot: Boolean,
readiness: MeshRenderReadiness,
autoJoinBringup: Boolean = false,
): MeshDisplayDecision = when {
// Anchor/root auto-joining its last mesh → default straight to diagnostics,
// regardless of readiness: establishing/monitoring the mesh is its job and the
// pre-quorum state is its to see. The operator can navigate to the scene.
isAnchorOrRoot && autoJoinBringup ->
MeshDisplayDecision(MeshSurface.DIAGNOSTICS, "anchor/root auto-join bringup")
// Anchor/root before the mesh is solvable → diagnostics, NOT a client hold: this is
// the device where the unsolved / quorum-building state is prevalent and wanted.
isAnchorOrRoot && !readiness.canRenderSpatial ->
MeshDisplayDecision(MeshSurface.DIAGNOSTICS, "anchor/root pre-quorum (${readiness.detail})")
// Everyone else falls under the client spatial gate: render the scene only when
// READY, otherwise HOLD so a client never shows unsolved locations.
readiness.canRenderSpatial ->
MeshDisplayDecision(MeshSurface.SPATIAL_SCENE, "ready")
else ->
MeshDisplayDecision(MeshSurface.HOLDING, readiness.label)
}
/** The app's start destination on launch — [decide] projected to its [MeshSurface]. */
fun startupSurface(
isAnchorOrRoot: Boolean,
readiness: MeshRenderReadiness,
autoJoinBringup: Boolean,
): MeshSurface = decide(isAnchorOrRoot, readiness, autoJoinBringup).surface
}
common/src/commonTest/kotlin/com/aether/mofe/engine/render/MeshDisplayPolicyTest.kt
package com.aether.mofe.engine.render
import kotlin.test.Test
import kotlin.test.assertEquals
/**
* The role-aware surface safeguard ([MeshDisplayPolicy]).
*
* Clients are protected from unsolved locations pre-quorum (HOLD); anchors/root are
* instead shown the DIAGNOSTICS surface for that same state, and default to it on
* auto-join bringup. Readiness alone (role-blind) can't express this — that's why the
* policy sits on top of [MeshRenderReadiness].
*/
class MeshDisplayPolicyTest {
private val ready = MeshRenderReadiness.evaluate(engineStarted = true, meshSolved = true, referenceCount = 4, selfLocalized = true)
private val acquiringMesh = MeshRenderReadiness.evaluate(engineStarted = true, meshSolved = false, referenceCount = 0, selfLocalized = false)
private val acquiringSelf = MeshRenderReadiness.evaluate(engineStarted = true, meshSolved = true, referenceCount = 4, selfLocalized = false)
private fun surface(isAnchorOrRoot: Boolean, r: MeshRenderReadiness, autoJoin: Boolean = false) =
MeshDisplayPolicy.decide(isAnchorOrRoot, r, autoJoin).surface
// ─────────────────────────── CLIENT: protected ───────────────────────────
@Test
fun client_holds_before_the_mesh_is_ready() {
assertEquals(MeshSurface.HOLDING, surface(isAnchorOrRoot = false, acquiringMesh))
assertEquals(MeshSurface.HOLDING, surface(isAnchorOrRoot = false, acquiringSelf))
}
@Test
fun client_renders_the_scene_only_when_ready() {
assertEquals(MeshSurface.SPATIAL_SCENE, surface(isAnchorOrRoot = false, ready))
}
@Test
fun client_auto_join_is_still_gated_never_shown_diagnostics_or_unsolved_locations() {
// autoJoin must not change the client rule — it is not an anchor/root.
assertEquals(MeshSurface.HOLDING, surface(isAnchorOrRoot = false, acquiringMesh, autoJoin = true))
assertEquals(MeshSurface.SPATIAL_SCENE, surface(isAnchorOrRoot = false, ready, autoJoin = true))
}
// ─────────────────────── ANCHOR / ROOT: informed ─────────────────────────
@Test
fun anchor_root_auto_join_bringup_defaults_to_the_diagnostic_panel() {
// Regardless of readiness — the whole point of the bringup default.
assertEquals(MeshSurface.DIAGNOSTICS, surface(isAnchorOrRoot = true, acquiringMesh, autoJoin = true))
assertEquals(MeshSurface.DIAGNOSTICS, surface(isAnchorOrRoot = true, ready, autoJoin = true))
}
@Test
fun anchor_root_sees_diagnostics_pre_quorum_not_a_client_hold() {
assertEquals(MeshSurface.DIAGNOSTICS, surface(isAnchorOrRoot = true, acquiringMesh))
assertEquals(MeshSurface.DIAGNOSTICS, surface(isAnchorOrRoot = true, acquiringSelf))
}
@Test
fun anchor_root_not_auto_joining_falls_through_to_the_scene_once_ready() {
assertEquals(MeshSurface.SPATIAL_SCENE, surface(isAnchorOrRoot = true, ready))
}
// ─────────────────────────────── contract ────────────────────────────────
@Test
fun startupSurface_matches_decide() {
for (anchor in listOf(false, true))
for (r in listOf(ready, acquiringMesh, acquiringSelf))
for (auto in listOf(false, true))
assertEquals(
MeshDisplayPolicy.decide(anchor, r, auto).surface,
MeshDisplayPolicy.startupSurface(anchor, r, auto),
"startupSurface must project decide().surface for anchor=$anchor auto=$auto state=${r.state}",
)
}
@Test
fun a_client_is_the_only_role_that_can_be_held() {
// Safeguard invariant: HOLDING is reachable only for a non-anchor (client). An
// anchor/root is always either shown the scene (READY) or diagnostics — never a
// bland hold that would deny the operator the state they need.
for (r in listOf(ready, acquiringMesh, acquiringSelf))
for (auto in listOf(false, true))
assertEquals(
false, surface(isAnchorOrRoot = true, r, auto) == MeshSurface.HOLDING,
"anchor/root must never be put in HOLDING (state=${r.state} auto=$auto)",
)
}
}
    (1-1/1)