0cb315fdccd651db8d2aae21a07f1d08217715b2
Three-way consensus (Jay/Codex/Claude): one sound, split timing not identity. The bullseye sound now means 'centered right now' and fires the instant you're aligned - even a fast pass over center - so the zone that needs the most help locating always announces itself. It no longer waits on the velocity+dwell lock, which was the delay near center. - SurfaceTickPolicy: outside the center zone, proximity ticks (unchanged anchor scheduler). Inside, emit ALIGNED every frame -> caller loops level.wav. Entry is immediate (no dwell, no velocity gate). Alignment uses SPATIAL hysteresis (enter <=0.2, rearm only after leaving >=0.35) - not a time debounce, so a legitimate quick re-crossing still announces. Ticks resume immediately on exit. - Alignment error = hypot(stable pitch, stable roll) - the SAME axes that drive the bubble, so the sound can't lag the visual (Codex's implementation note). - Player: level.wav loops while aligned (ensureLevelLooping, idempotent), stopped before every restart and on exit; pending-load queue retained. - The held-level confirmation (persistent lime, label, haptic) stays on the velocity-aware LockDetector; the locating sound is fully decoupled from it. Tests: immediate cue on fast crossing, one start / ticks-suppressed while inside, stop+resume-ticks on exit, spatial-hysteresis rearm, tick reactivity. 81 tests passing; assembleDebug clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
On the Level
Offline-first Android pocket toolset: a trustworthy level, angle meter, and calibrated screen ruler (Pro). Kotlin + Jetpack Compose, single module, no backend.
- BRIEF.md — the authoritative v1 product & technical brief
- AUDIT.md — audit of the brief; the amendments it lists are incorporated
- resources/ — concept board (visual direction)
Building
Open in Android Studio, or:
./gradlew assembleDebug # build
./gradlew testDebugUnitTest # measurement-math unit tests
Scaffold status
The project skeleton follows BRIEF.md §Sensor and measurement architecture. Implemented and tested:
core/sensors— sensor selection (game rotation vector → gravity → low-passed accelerometer), device-frame gravity stream, and the pure measurement math: orientation mapping, two-sample per-mode calibration (derived in angle space, applied in vector space so it stays correct away from zero), EMA smoothing, lock hysteresis/dwell/debounce, display deadband, placement validity, and the sensor-vector contract (all sources emit device-frame world-up; flat = +9.81 on Z, pinned bySensorContractTest). All unit-tested.core/settings— DataStore preferences and per-mode calibration persistence.core/billing— entitlement interface with a stub (free-tier) implementation.feature/level,feature/angle— live numeric scaffolds wired to the real sensor pipeline, with manual Surface|Edge selection, placement-validity hints, lock state (locked label states the tolerance so it can never contradict the rounded readout), and directional hold-to-zero (vector reference, not magnitude subtraction).feature/ruler,feature/tools— static placeholders.
Not yet implemented (deliberately — see TODO markers):
- The tactile vial visuals and the spring-animated display value (value 3 of 3 in the brief); scaffold screens render the deadbanded stable value directly.
- Guided calibration flow UI (math and persistence are done).
- Edge-mode readout counter-rotation on the portrait-locked activity.
- Play Billing implementation of
ProEntitlementRepository. - Screen ruler measurement UI and per-display scale persistence.
- Audio cue, reduced-motion behaviors, preferences UI.
Description
Languages
Kotlin
83.9%
HTML
16.1%