Add authoritative v1 product & technical brief (Codex)

This commit is contained in:
Jay
2026-07-11 18:25:38 -04:00
parent d29d9336f4
commit bcda2856f7
+185
View File
@@ -0,0 +1,185 @@
# On the Level — v1 Product & Technical Brief
**Status:** Ready for final Claude review before implementation
**Date:** 2026-07-11
**Companion audit:** [`AUDIT.md`](AUDIT.md)
## Product decision
On the Level is an offline-first Android pocket-tool app for quick, trustworthy
level and angle work. It should feel like a responsive physical instrument—not
a novelty simulation—while keeping displayed measurements stable and honest.
v1 is deliberately small: a level, an angle meter, a calibrated screen ruler,
and the settings necessary to make them dependable. There is no account,
backend, advertising, camera/AR measurement, cloud sync, analytics dependency,
or subscription in v1.
## Audience and promise
For everyday DIY, hanging pictures, shelving, small home jobs, and quick rough
measurements. The app is not a substitute for a certified or safety-critical
instrument. Screen-ruler copy must state that it is for short, rough
measurements only.
## Navigation and layout
Use a simple three-destination bottom navigation: **Level**, **Angle**, and
**Tools**. The app is portrait-locked in v1. The UI is edge-to-edge and must
correctly handle system insets and back gestures.
### 1. Level — default destination
- Header: `LEVEL`, live/calibrated status, calibration/settings action.
- Explicit mode selector: **Surface** | **Edge**. Do not auto-switch modes in
v1; unexpected switching undermines confidence while positioning the phone.
- **Surface:** phone lying screen-up on a surface; use a two-axis cross-vial
visualization with meaningful pitch and roll readings.
- **Edge:** phone held upright on its long edge against a wall, shelf, or
picture frame; use a single, prominent vial/readout for that plane.
- The central visual is tactile: its bubble has restrained spring/damping,
settles naturally, and responds immediately to device movement.
- A locked state is a brief, accessible success reaction with a debounced
system-respecting haptic/audio cue. It is not the persistent normal UI.
- Keep the screen on while this destination is visible.
### 2. Angle
- Large primary angle readout.
- Secondary pitch, roll, and percent-grade readings.
- **Hold to zero** sets a relative reference and is always free.
- Percent grade is `tan(pitch) × 100`; define a sensible cap or infinity
presentation near a vertical surface.
- Target-angle alerts and saved named references are Pro features.
### 3. Tools
- **Screen Ruler**: a Pro tool with a clear preview and calm, intentional
paywall—not an interruption in the free level/angle flow.
- Calibration entry point.
- Units, haptic/audio, and motion/accessibility preferences.
## Visual and interaction direction
Use the concept board's deep graphite base, warm amber bubble, and lime level
lock as direction rather than a literal spec. Numeric measurements remain
near-white and must be readable in bright conditions. Never convey accuracy,
state, or purchase status by color alone.
The visual vial may be expressive, but it must never contradict the numeric
reading. Provide an in-app reduced-motion option and respect disabled system
animations. Respect device haptic settings; do not force vibration.
## Measurement modes and calibration
Only promise calibration for the two physical orientations explicitly supported
in v1:
1. **Surface** — screen-up, flat contact plane.
2. **Long Edge** — upright, long-edge contact plane.
Store a reference orientation for each supported mode—not a scalar angle offset.
The calibration flow uses two samples: place the device, record; rotate it 180°
on the same plane, record; derive the reference/bias from both samples. This
reduces fixed device bias without requiring the user to start on a known-level
surface. Calibration UI must give exact, mode-specific placement instructions.
Screen-ruler calibration is separate from sensor calibration. Offer calibration
against a standard credit-card width (85.60 mm) and a conventional ruler option.
Store scale with the active display/configuration characteristics and request
recalibration whenever a material display change makes the prior scale suspect.
## Sensor and measurement architecture
Use a small native Android/Kotlin Compose application. Keep one app module for
v1, with a manual application container rather than premature modularization or
Hilt.
```text
app/
core/design/ theme, typography, tactile animation primitives
core/sensors/ sensor source, device-axis mapping, measurement math
core/settings/ DataStore preferences and calibration persistence
core/billing/ entitlement abstraction and Play Billing implementation
feature/level/ state and UI for Surface / Edge level modes
feature/angle/ relative-zero logic and UI
feature/ruler/ ruler calibration and UI
feature/tools/ tools and settings UI
```
Require an accelerometer in the manifest. Select sensors in this order:
1. `TYPE_GAME_ROTATION_VECTOR`
2. `TYPE_GRAVITY`
3. A low-pass-filtered `TYPE_ACCELEROMETER`
The app does not need north/heading. Map device-frame sensor values explicitly
for the portrait-locked UI and for each measurement mode. Handle unavailable
sensors with a clear unsupported-device state.
Maintain three distinct values:
1. **Raw sensor value** — received from the selected sensor source.
2. **Stable calibrated measurement** — reference-orientation calibration plus
a defined EMA/low-pass filter. This is the sole input to numeric readouts
and level-lock detection.
3. **Animated display value** — a critically damped spring derived from the
stable value and used only to move the vial/bubble.
Readouts use 0.1° resolution and a display deadband to prevent flickering. Lock
behavior starts at no more than 0.2°, exits at at least 0.35°, has a short dwell
time, and has a multi-second haptic debounce. Exact constants remain tunable,
but lock and readout must always use the same stable calibrated measurement.
Register sensors only while a relevant screen is foregrounded; unregister them
when backgrounded.
## Monetization
Use one permanent, non-consumable **On the Level Pro** Google Play product. Do
not use a subscription. The free product is complete:
- Surface and Edge level modes
- Angle measurement, pitch/roll, degree/grade display
- Hold-to-zero
- Calibration and basic feedback/unit preferences
Pro adds deliberate, additive measurement tools:
- Calibrated Screen Ruler
- Target-angle haptic/audio alerts
- Saved and named angle references
Expose entitlement as `StateFlow<Entitlement>` from `core/billing`. Billing
initialization must be asynchronous/lazy: free tools render immediately and
never block on Play services. Cache a previously Play-confirmed entitlement for
offline job-site use, refresh owned purchases when possible (including resume),
and never grant from an arbitrary preference value or a pending purchase.
## Testability and release gates
Keep all math—device-axis mapping, calibration, smoothing, hysteresis, grade,
and lock transitions—in pure Kotlin. Place sensor I/O behind an injected
`SensorSource` so tests can use synthetic and recorded real-device traces.
Before release, verify:
- Surface and Edge readings across supported orientations and screen rotation
conditions.
- Two-sample calibration improves repeatability and remains isolated per mode.
- Numeric readings, vial animation, and lock feedback stay consistent.
- Haptic debounce, system haptic settings, reduced motion, and bright-light
readability.
- Sensor absence, app backgrounding, and screen wake behavior.
- Pro purchase, restore, pending purchase, offline cached entitlement, and
billing-unavailable behavior without degrading free tools.
- Ruler calibration, display/configuration changes, and honest rough-measure
wording.
## Explicitly out of scope for v1
- Camera-assisted or AR distance measurement
- Accounts, server verification, cloud storage, sharing, or collaboration
- Ads, subscription plans, consumable credits, or trial metering
- Automatic flat/edge mode switching
- Support claims for every device edge, case, or external/foldable display