Project

General

Profile

User Story #80 » 0005-test-engine-multi-layout-mesh-convergence-gate-70-ag.patch

knight8241, 08/07/2026 18:33

View differences:

common/src/commonTest/kotlin/com/aether/mofe/engine/MeshConvergenceGateTest.kt
package com.aether.mofe.engine
import com.aether.mofe.integration.MofeTestHarness
import com.aether.mofe.model.ConstellationFrameState
import com.aether.mofe.model.DeviceId
import com.aether.mofe.model.MeshOperatingMode
import com.aether.mofe.model.MofeConfig
import com.aether.mofe.model.RawRangingMeasurement
import com.aether.mofe.model.Vector3D
import kotlin.math.abs
import kotlin.math.atan2
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
/**
* #70 — MESH-CONVERGENCE ACCEPTANCE GATE (agreement clauses).
*
* Given the delivered convergence stack — #61 (non-root followers ADOPT the root's constellation and
* DEFER their own solve, `defersConstellationToRoot()`), #69 (the root's over-determined self-solve),
* and :498 (a non-leader adopts a leader-replicated ANCHOR position into its engine frame) — a
* HEALTHY self-solved mesh must satisfy, across MANY valid anchor layouts, the AGREEMENT criteria:
*
* (1) every device reports the SAME constellation — pairwise inter-anchor distances identical
* across devices within an epsilon (the capture4 failure was each anchor showing a DIFFERENT
* shape);
* (2) the frame reads LOCKED (not perpetual CONVERGING) once solved/adopted;
* (3) the mesh operating mode is a converged, non-DEGRADED state and never churns THROUGH
* DEGRADED while healthy (see the scoping note on [meshModeIsStableAndConverged]);
* (4) a DEGENERATE layout is DETECTED + REPORTED by the #47 placement classifier
* ([constellationGeometryReport] / [GeometryQuality]) and is NOT silently locked wrong.
*
* Plus an ACCURACY probe: the solved SHAPE (its pairwise inter-anchor distances) matches truth
* within [PROVISIONAL_TOLERANCE_M].
*
* These are PURE-ENGINE assertions over existing public APIs — they mirror the mechanics proven in
* `ConstellationFrameLockTest` (root self-solve → LOCK) and `ConstellationAdoptionTest` (follower
* adopt + defer), and extend them to an explicit CROSS-DEVICE agreement check over varied layouts.
* No engine (`commonMain`) source is modified.
*/
class MeshConvergenceGateTest {
// ─────────────────────────────────────────────────────────────────────────────────────────────
// Tolerances
// ─────────────────────────────────────────────────────────────────────────────────────────────
/**
* How closely the SOLVED constellation shape must match ground truth (max abs difference over the
* sorted pairwise inter-anchor distances, metres).
*
* PROVISIONAL — pending dev tolerance decision (#70). Deliberately LOOSE: with the perfect
* synthetic inter-anchor ranges fed here the current engine recovers the shape to well under a
* millimetre, so 5 cm is generous headroom — it exists so a future, real accuracy bar (measured
* against noisy on-device captures) is a ONE-LINE edit, not a suite rewrite. Tightening it is the
* dev's call; do NOT read this value as the shipping accuracy target.
*/
private companion object {
private const val PROVISIONAL_TOLERANCE_M = 0.05 // PROVISIONAL — pending dev tolerance decision (#70)
/**
* Cross-device agreement epsilon (metres). Under #61/:498 every follower ADOPTS the root's
* exact positions, so their pairwise-distance signatures equal the root's to floating-point
* noise — hence a tight bound. The clause is not a tautology: each follower below is first fed
* a COMPLETE but CONFLICTING local range set that, absent the defer gate, WOULD re-solve a
* divergent shape (the field failure). The bound proves the gate held.
*/
private const val AGREEMENT_EPSILON_M = 1e-6
}
// ─────────────────────────────────────────────────────────────────────────────────────────────
// Layouts under test — each VALID: ≥4 anchors, a complete inter-anchor graph, non-degenerate,
// and exactly one clearly-RAISED anchor so the orientation solve (and thus the LOCK) can engage.
// ─────────────────────────────────────────────────────────────────────────────────────────────
/** A valid anchor layout: [truth] positions (first id = root at ORIGIN) and the [raised] anchor. */
private class Layout(
val name: String,
val truth: Map<DeviceId, Vector3D>,
val raised: DeviceId,
) {
val root: DeviceId get() = truth.keys.first()
val peers: List<DeviceId> get() = truth.keys.drop(1)
val ids: List<DeviceId> get() = truth.keys.toList()
}
private fun dev(s: String) = DeviceId(s)
/** 4-anchor SQUARE footprint (2.4 m) with one corner lifted onto a shelf (+0.7 m). */
private fun squareRaised() = Layout(
name = "square-raised(4)",
truth = linkedMapOf(
dev("A1") to Vector3D(0.0, 0.0, 0.0),
dev("A2") to Vector3D(2.4, 0.0, 0.0),
dev("A3") to Vector3D(2.4, 2.4, 0.0),
dev("A4") to Vector3D(0.0, 2.4, 0.7),
),
raised = dev("A4"),
)
/** 4-anchor TETRAHEDRON with genuine elevation (apex +1.7 m). */
private fun tetrahedron() = Layout(
name = "tetrahedron(4)",
truth = linkedMapOf(
dev("A1") to Vector3D(0.0, 0.0, 0.0),
dev("A2") to Vector3D(2.6, 0.0, 0.0),
dev("A3") to Vector3D(1.3, 2.3, 0.0),
dev("A4") to Vector3D(1.3, 0.8, 1.7),
),
raised = dev("A4"),
)
/** 5-anchor L-SHAPED room, one anchor raised (+0.9 m). */
private fun lShape() = Layout(
name = "l-shape(5)",
truth = linkedMapOf(
dev("A1") to Vector3D(0.0, 0.0, 0.0),
dev("A2") to Vector3D(3.0, 0.0, 0.0),
dev("A3") to Vector3D(3.0, 1.4, 0.0),
dev("A4") to Vector3D(1.4, 1.4, 0.0),
dev("A5") to Vector3D(1.4, 3.4, 0.9),
),
raised = dev("A5"),
)
/** 5-anchor wide room, four flat corners + a central raised anchor (+1.4 m). */
private fun fiveAnchor() = Layout(
name = "wide-5(5)",
truth = linkedMapOf(
dev("A1") to Vector3D(0.0, 0.0, 0.0),
dev("A2") to Vector3D(4.5, 0.0, 0.0),
dev("A3") to Vector3D(0.0, 4.5, 0.0),
dev("A4") to Vector3D(4.5, 4.5, 0.0),
dev("A5") to Vector3D(2.0, 2.5, 1.4),
),
raised = dev("A5"),
)
// ─────────────────────────────────────────────────────────────────────────────────────────────
// Shared drivers (mirror ConstellationFrameLockTest / ConstellationAdoptionTest mechanics)
// ─────────────────────────────────────────────────────────────────────────────────────────────
/** Sorted multiset of all pairwise distances — a rigid-motion-invariant SHAPE signature (the same
* invariant the engine's own lock-stability gate uses). Ids sorted for a stable pairing order. */
private fun signature(positions: Map<DeviceId, Vector3D>): DoubleArray {
val pts = positions.entries.sortedBy { it.key.value }.map { it.value }
val out = ArrayList<Double>(pts.size * (pts.size - 1) / 2)
for (i in pts.indices) for (j in i + 1 until pts.size) out.add(pts[i].distanceTo(pts[j]))
return out.toDoubleArray().also { it.sort() }
}
private fun maxAbsDiff(a: DoubleArray, b: DoubleArray): Double {
require(a.size == b.size) { "signatures differ in size: ${a.size} vs ${b.size}" }
var m = 0.0
for (i in a.indices) m = maxOf(m, abs(a[i] - b[i]))
return m
}
/** Feed every inter-anchor edge of [positions] into the engine ([skip] omits one, modelling a
* permanently-obstructed link). EMA-smoothed inside the engine, so several reps settle it. */
private fun feedInterAnchor(
h: MofeTestHarness,
ids: List<DeviceId>,
positions: Map<DeviceId, Vector3D>,
reps: Int = 20,
skip: Pair<DeviceId, DeviceId>? = null,
) {
for (i in ids.indices) for (j in i + 1 until ids.size) {
val a = ids[i]; val b = ids[j]
if (skip != null && (a to b == skip || b to a == skip)) continue
val d = positions.getValue(a).distanceTo(positions.getValue(b))
repeat(reps) { h.engine.processInterAnchorRanging(a, b, d) }
}
}
/** Feed the ROOT's own AoA so the orientation solve has ≥2 azimuth bearings and can ORDINALLY
* rank the raised anchor (it reads a clearly-higher elevation than the coplanar base). */
private fun feedSelfAoa(h: MofeTestHarness, layout: Layout) {
val rootPos = layout.truth.getValue(layout.root)
for (peer in layout.peers) {
val p = layout.truth.getValue(peer)
val az = atan2(p.y, p.x)
val el = if (peer == layout.raised) 0.6 else 0.0
repeat(4) {
h.clock.advance(10_000)
h.engine.processRanging(
RawRangingMeasurement(
anchorId = layout.root, targetId = peer, timestamp = h.clock.now(),
distance = rootPos.distanceTo(p),
azimuth = az, elevation = el, signalQuality = 1.0, rssi = -55.0,
),
)
}
}
}
/** Drive maintenance until the trust gate locks the frame (or a tick cap is hit). */
private fun driveUntilLocked(h: MofeTestHarness, maxTicks: Int = 25): Boolean {
repeat(maxTicks) {
if (h.engine.constellationFrameState() == ConstellationFrameState.LOCKED) return true
h.engine.bootstrapReferenceConstellation()
}
return h.engine.constellationFrameState() == ConstellationFrameState.LOCKED
}
/**
* Build the ROOT device and drive it to a LOCKED, oriented self-solve of [layout]. Peers are
* seated as reference ANCHORS (so the mesh reaches QUORUM, not just PRE_QUORUM) at truth — exactly
* as `ConstellationFrameLockTest` seeds — then the bootstrap re-solves from the inter-anchor ranges
* and must EARN the lock through its own residual/stability/orientation trust gate.
*/
private fun buildLockedRoot(layout: Layout): MofeTestHarness {
val h = MofeTestHarness(MofeConfig()).build()
h.engine.setSelfId(layout.root) // self == origin ⇒ the root SOLVES (does not defer)
h.engine.initializeAsRoot(layout.root)
for (peer in layout.peers) h.engine.registerReferenceAnchor(peer, layout.truth.getValue(peer))
feedSelfAoa(h, layout)
feedInterAnchor(h, layout.ids, layout.truth)
assertTrue(
driveUntilLocked(h),
"[${layout.name}] the root must LOCK its self-solved constellation " +
"(state=${h.engine.constellationFrameState()})",
)
return h
}
// ─────────────────────────────────────────────────────────────────────────────────────────────
// Clause (3) reachability note
// ─────────────────────────────────────────────────────────────────────────────────────────────
//
// The task's "quorum stable / no DEGRADED churn" is ultimately a RAFT/registry property owned by
// MeshCoordinator (the consensus quorum), which a pure-engine test does not spin up. What IS
// reachable here is the engine's own MeshOperatingMode ([CoordinateFrameManager.operatingMode],
// surfaced via the frame manager) and its transition log ([RecordingListener.modeChanges]).
// MeshOperatingMode includes DEGRADED (QUORUM lost), so the engine-level analogue of the clause IS
// expressible and asserted below: a healthy self-solved mesh settles in QUORUM and never emits a
// transition INTO DEGRADED. The Raft-quorum-churn dimension proper (leader failover, voter loss)
// lives in the messaging/ClusterHarness suites and is out of scope for this engine-only gate.
private fun meshModeIsStableAndConverged(h: MofeTestHarness, layout: String) {
assertTrue(
h.listener.modeChanges.none { (_, to) -> to == MeshOperatingMode.DEGRADED },
"[$layout] a healthy self-solve must never churn THROUGH DEGRADED; " +
"transitions=${h.listener.modeChanges}",
)
assertEquals(
MeshOperatingMode.QUORUM, h.frameManager.operatingMode,
"[$layout] a ≥4-anchor healthy mesh must settle in QUORUM",
)
}
// ═════════════════════════════════════════════════════════════════════════════════════════════
// Clauses (1)+(2)+(3) + accuracy, run across every valid layout
// ═════════════════════════════════════════════════════════════════════════════════════════════
private fun runAgreementGate(layout: Layout) {
// ── ROOT self-solves the authoritative constellation ──────────────────────────────────────
val rootH = buildLockedRoot(layout)
// (2) root frame is LOCKED, not CONVERGING.
assertEquals(
ConstellationFrameState.LOCKED, rootH.engine.constellationFrameState(),
"[${layout.name}] root frame must read LOCKED once solved",
)
// (3) mesh mode converged + no DEGRADED churn.
meshModeIsStableAndConverged(rootH, layout.name)
// The root's authoritative constellation (root at ORIGIN + solved peers).
val authoritative = rootH.engine.referencePointPositions().associate { it.first to it.second }
assertEquals(
layout.truth.keys, authoritative.keys,
"[${layout.name}] the solved constellation must hold every anchor",
)
// ── ACCURACY probe: solved SHAPE vs truth SHAPE (orientation-invariant pairwise distances) ──
val truthSig = signature(layout.truth)
val solvedSig = signature(authoritative)
val shapeErr = maxAbsDiff(truthSig, solvedSig)
assertTrue(
shapeErr < PROVISIONAL_TOLERANCE_M,
"[${layout.name}] solved shape must match truth within the provisional bar " +
"($PROVISIONAL_TOLERANCE_M m); max pairwise error=$shapeErr m",
)
// ── FOLLOWERS adopt the root frame, DEFER their own (conflicting) solve, and must AGREE ─────
// Absent #61's defer gate, each follower — here fed a COMPLETE but deliberately CONFLICTING
// inter-anchor range set (truth ×1.4) — would re-solve a DIVERGENT constellation. The gate
// makes every device hold the root's ONE frame; that is what clause (1) asserts.
val deviceSignatures = linkedMapOf<DeviceId, DoubleArray>(layout.root to solvedSig)
val rogue = layout.truth.mapValues { (_, p) -> p * 1.4 }
for (follower in layout.peers) {
val fH = MofeTestHarness(MofeConfig()).build()
fH.engine.initializeAsRoot(layout.root) // frame rooted at the ELECTED root…
fH.engine.setSelfId(follower) // …but THIS device is a NON-root follower
// :498 adoption path — seat the leader/root-replicated positions as reference anchors
// (registering the origin itself is a no-op, mirroring the on-device guard).
for ((id, pos) in authoritative) fH.engine.registerReferenceAnchor(id, pos)
// (2) an adopted follower advertises LOCKED (shared frame), not perpetual CONVERGING.
assertEquals(
ConstellationFrameState.LOCKED, fH.engine.constellationFrameState(),
"[${layout.name}] follower ${follower.value} on the adopted frame must read LOCKED",
)
// #61 defer — feed the follower complete, solvable, CONFLICTING ranges; it must NOT
// re-solve or refine its own frame.
feedInterAnchor(fH, layout.ids, rogue, reps = 12)
assertEquals(
0, fH.engine.refineAnchorConstellation(),
"[${layout.name}] follower ${follower.value} must not refine its own frame",
)
repeat(5) {
assertEquals(
0, fH.engine.bootstrapReferenceConstellation(),
"[${layout.name}] follower ${follower.value} must not re-solve despite complete local ranges",
)
}
assertEquals(
ConstellationFrameState.LOCKED, fH.engine.constellationFrameState(),
"[${layout.name}] follower ${follower.value} must stay LOCKED after the conflicting feed",
)
// Every adopted position stays byte-for-byte the root's (not the rogue local solve).
val followerRefs = fH.engine.referencePointPositions().associate { it.first to it.second }
for ((id, pos) in authoritative) {
assertTrue(
followerRefs.getValue(id).distanceTo(pos) < 1e-9,
"[${layout.name}] follower ${follower.value} must hold the root's $id, not its rogue solve",
)
}
deviceSignatures[follower] = signature(followerRefs)
}
// (1) AGREEMENT — every device reports the SAME constellation: identical pairwise inter-anchor
// distances across the whole mesh, within epsilon.
val reference = deviceSignatures.getValue(layout.root)
for ((device, sig) in deviceSignatures) {
assertEquals(
reference.size, sig.size,
"[${layout.name}] ${device.value} must report the same number of inter-anchor edges",
)
val err = maxAbsDiff(reference, sig)
assertTrue(
err < AGREEMENT_EPSILON_M,
"[${layout.name}] ${device.value} must agree with the mesh-wide constellation; " +
"max pairwise divergence=$err m",
)
}
}
@Test fun agreement_gate_square_raised() = runAgreementGate(squareRaised())
@Test fun agreement_gate_tetrahedron() = runAgreementGate(tetrahedron())
@Test fun agreement_gate_l_shape() = runAgreementGate(lShape())
@Test fun agreement_gate_five_anchor() = runAgreementGate(fiveAnchor())
// ═════════════════════════════════════════════════════════════════════════════════════════════
// Clause (4) — DEGENERATE layouts are DETECTED by the #47 classifier and NOT silently locked
// ═════════════════════════════════════════════════════════════════════════════════════════════
private fun degenerateHarness(seeded: Map<DeviceId, Vector3D>): MofeTestHarness {
val h = MofeTestHarness(MofeConfig()).build()
val root = seeded.keys.first()
h.engine.setSelfId(root)
h.engine.initializeAsRoot(root)
for ((id, pos) in seeded) if (id != root) h.engine.registerReferenceAnchor(id, pos)
return h
}
/**
* COLLAPSED constellation: 4 anchors, a complete inter-anchor graph (so it is rigid by edge count),
* but two anchors sit ~2 cm apart — below [MofeConfig.anchorMinSeparationMeters]. The #47 classifier
* must call this DEGENERATE, and the lock trust gate must never latch it.
*/
@Test
fun degenerate_collapsed_is_reported_and_never_locks() {
val root = dev("A1")
val collapsed = linkedMapOf(
root to Vector3D(0.0, 0.0, 0.0),
dev("A2") to Vector3D(2.0, 0.0, 0.0),
dev("A3") to Vector3D(0.0, 2.0, 0.0),
dev("A4") to Vector3D(0.02, 0.0, 0.0), // ~2 cm from the root → collapsed pair
)
val h = degenerateHarness(collapsed)
feedInterAnchor(h, collapsed.keys.toList(), collapsed, reps = 20)
val report = assertNotNull(
h.engine.constellationGeometryReport(),
"a topology exists, so the classifier must return a report",
)
assertEquals(
GeometryQuality.DEGENERATE, report.quality,
"a collapsed (near-overlapping) constellation must be classified DEGENERATE; report=$report",
)
assertTrue(report.issues.isNotEmpty(), "a DEGENERATE report must carry operator-facing guidance")
repeat(25) { h.engine.bootstrapReferenceConstellation() }
assertTrue(
h.engine.constellationFrameState() != ConstellationFrameState.LOCKED,
"a degenerate (collapsed) layout must NEVER lock; state=${h.engine.constellationFrameState()}",
)
}
/**
* UNDER-RANGED constellation ("too few constraints"): 4 well-separated non-coplanar anchors but a
* permanently-missing inter-anchor edge, so the distance graph is not rigid (5 of 6 edges). The #47
* classifier must flag this DEGENERATE, and the incomplete-graph solve (prior-folded fallback) must
* never lock — mirroring `ConstellationFrameLockTest.never_locks_an_incomplete_graph_solve`.
*/
@Test
fun degenerate_under_ranged_is_reported_and_never_locks() {
val layout = tetrahedron()
val h = degenerateHarness(layout.truth)
feedSelfAoa(h, layout)
feedInterAnchor(h, layout.ids, layout.truth, skip = dev("A2") to dev("A4"))
val report = assertNotNull(
h.engine.constellationGeometryReport(),
"a topology exists, so the classifier must return a report",
)
assertEquals(
GeometryQuality.DEGENERATE, report.quality,
"an under-ranged (non-rigid) constellation must be classified DEGENERATE; report=$report",
)
assertTrue(
report.edgeCount < report.edgesForRigidity,
"the report must show the missing constraint (edges ${report.edgeCount} < ${report.edgesForRigidity})",
)
repeat(25) { h.engine.bootstrapReferenceConstellation() }
assertTrue(
h.engine.constellationFrameState() != ConstellationFrameState.LOCKED,
"an under-ranged (incomplete-graph) solve must NEVER lock; state=${h.engine.constellationFrameState()}",
)
}
/**
* COLLINEAR anchors — 4 anchors on a single line, fully ranged.
*
* Honest scoping of the #47 classifier: its DEGENERATE verdict is driven by anchor COUNT, edge
* RIGIDITY, and minimum SEPARATION — NOT by the rank/collinearity of the point set. A fully-ranged
* collinear line is therefore rigid-by-edge-count and reads MARGINAL (its only complaint is "no
* clearly-raised anchor", because a line has no base plane), not DEGENERATE. The guarantee that
* actually matters — "NOT silently locked wrong" — is upheld by the ORIENTATION + trust gate: a
* collinear set has no solvable up-axis, so orientation never engages and the frame never locks.
* This test asserts both facts precisely (rather than pretending #47 escalates collinearity).
*/
@Test
fun collinear_is_flagged_marginal_and_never_locks() {
val root = dev("A1")
val collinear = linkedMapOf(
root to Vector3D(0.0, 0.0, 0.0),
dev("A2") to Vector3D(1.0, 0.0, 0.0),
dev("A3") to Vector3D(2.0, 0.0, 0.0),
dev("A4") to Vector3D(3.0, 0.0, 0.0),
)
val h = degenerateHarness(collinear)
// Give it every chance to lock: feed AoA + a complete edge set, then drive hard.
for (peer in listOf(dev("A2"), dev("A3"), dev("A4"))) {
val p = collinear.getValue(peer)
repeat(4) {
h.clock.advance(10_000)
h.engine.processRanging(
RawRangingMeasurement(
anchorId = root, targetId = peer, timestamp = h.clock.now(),
distance = p.magnitude, azimuth = 0.0, elevation = 0.0,
signalQuality = 1.0, rssi = -55.0,
),
)
}
}
feedInterAnchor(h, collinear.keys.toList(), collinear, reps = 20)
val report = assertNotNull(
h.engine.constellationGeometryReport(),
"a topology exists, so the classifier must return a report",
)
assertEquals(
GeometryQuality.MARGINAL, report.quality,
"a fully-ranged collinear line is rigid-by-edge-count but has no raised anchor → MARGINAL; report=$report",
)
assertTrue(report.issues.isNotEmpty(), "the classifier must still surface a placement complaint")
repeat(25) { h.engine.bootstrapReferenceConstellation() }
assertTrue(
h.engine.constellationFrameState() != ConstellationFrameState.LOCKED,
"a collinear (unorientable) layout must NEVER lock; state=${h.engine.constellationFrameState()}",
)
}
}
    (1-1/1)