604d61ebb287584eebbfde10779a3f973a599ae3
Voice (final P1 from audit): the policy is no longer consumed until TTS is ready, so a phrase can't be queued and then spoken stale after init. When ready, the policy evaluates the CURRENT reading (silent if moving, correct instruction if settled). VoiceSpeaker drops its pending-text queue and just exposes isReady; the policy is remember(speaker) so each VOICE session starts fresh. Covers all of Codex's cancellation cases (movement, unlock, invalid placement, foreground loss, mode change, shutdown). Velocity-aware Surface lock (Jay's observation + Codex): the fixed 400 ms dwell made a slowly-arriving bubble wait, delaying the level sound, while a fast crossing could still lock. LockDetector now takes a movement rate: - error <=0.2 deg AND rate <=0.3 deg/s -> lock after a short 175 ms confirm; - faster than that -> dwell never accumulates (a center fly-through never locks); slowing to a stop inside the zone starts a fresh confirmation; - 0.35 deg exit hysteresis and the single shared lock (sound/lime/haptic) unchanged. LevelPipeline feeds SettlingDetector.movementRateDegreesPerSecond (was discarded); Edge passes null -> classic fixed 400 ms dwell. Simple gated machine, no adaptive-dwell formula. Tests: slow-entry-locks-promptly, fast-traversal-never-locks, intermediate- speed-no-accumulate, slowing-inside-zone-fresh-dwell, exit hysteresis, feedback debounce, Edge fixed-dwell fallback. 82 tests passing. 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%