diff --git a/SURFACE_LEVEL_PLAN.md b/SURFACE_LEVEL_PLAN.md index 19327db..9f87f01 100644 --- a/SURFACE_LEVEL_PLAN.md +++ b/SURFACE_LEVEL_PLAN.md @@ -1,6 +1,6 @@ # Surface Level — Implementation Attack Plan -**Status:** Proposed; send to Claude for audit before implementation. +**Status:** Audited; ready for implementation after this revision. **Scope:** Surface Level only. Do not change Angle, Edge, ruler, or billing in this stage. ## Outcome @@ -28,8 +28,13 @@ The user should be able to answer three questions at a glance or by sound: - Surface means the phone is intended to lie flat, screen-up, on the plane being measured. Its level-lock behavior is valid only in that geometry. - Numeric measurements are never hidden because the phone is steeply oriented. - Outside the ideal Surface geometry, show the actual angle and a quiet placement + Surface tilt magnitude remains authoritative from 0 to 180 degrees. Outside + the ideal face-up geometry, show the actual magnitude and a quiet placement hint; suppress level-lock feedback rather than replacing the reading with `—`. +- Above 80 degrees of tilt, de-emphasize/suppress Pitch and Roll because their + off-axis parameterization becomes noisy near vertical. Above 90 degrees, + identify the screen-down-side state rather than pretending the bullseye is a + useful placement tool. The magnitude remains visible in both cases. - The central visualization is a bullseye vial. It has a finite visual range: beyond that range the bubble rests at the rim, while numeric information remains full-range and authoritative. @@ -42,20 +47,30 @@ The user should be able to answer three questions at a glance or by sound: - Refactor Surface placement gating so it only controls the level-lock state, lock haptic, and geometry-specific guidance. -- Keep primary angle, pitch, and roll visible at 0-90 degrees. -- Add tests at flat, near level, 30 degrees, and near vertical proving numeric - readings remain present and correct; verify invalid placement never locks. +- Keep the primary tilt magnitude visible from 0-180 degrees. Keep Pitch and + Roll visible only where their meaning is stable (below 80 degrees tilt). +- Add tests at flat, near level, 30 degrees, 80 degrees, near vertical, and + screen-down. Verify magnitude stays correct, off-axis values are suppressed + in their ambiguous range, and invalid placement never locks. - Keep the existing lock tolerance semantics: a lock label states the tolerance rather than pretending any non-zero value is exactly flat. ### 2. Add the guided Surface calibration flow - Add a Surface Calibration route from the Level header. -- Explain that the phone may be calibrated on any firm, stable surface; the - surface itself does not need to be level. -- Step 1: settle, sample for a short stable window, capture. -- Step 2: rotate the phone exactly 180 degrees in the same plane, settle, capture. -- Reject/retry if readings indicate motion or inconsistent placement. +- Explain that the phone may be calibrated on any firm, stable, *roughly level* + surface; it need not be perfectly level. Warn and ask for a flatter surface + when the measured tilt exceeds 5 degrees. +- Capture the two samples from the uncalibrated sensor path. Never derive a new + calibration from readings that an existing calibration has already corrected. +- Step 1: settle, collect a 1.5-second stable window, capture. +- Step 2: rotate the phone exactly 180 degrees in the same plane, settle, then + collect and capture another 1.5-second stable window. +- Use the shared settling detector: enter Settling above 1.0 degrees/second of + two-axis plane movement; leave it only after movement is at or below 0.3 + degrees/second for 500 ms. Reject/retry windows that are not settled. +- Reject/retry a derived calibration bias above 3 degrees as evidence that the + device, setup, or turn was inconsistent. Suppress lock feedback during the flow. - Persist only the resulting Surface calibration; never alter Edge calibration. - State that the calibration applies to the current phone/case/contact setup. - On completion, show a concise success result and a way to clear/re-run it. @@ -67,10 +82,19 @@ The user should be able to answer three questions at a glance or by sound: - Glass, fluid, and meniscus effects must improve depth/readability; avoid a decorative or excessively glossy treatment. - Bubble target position derives directly from stable calibrated pitch/roll. -- Bubble movement uses a critically damped animation toward that real target: - no random drift, autonomous wobble, or animation that contradicts the number. +- Derive bubble target, high/low instruction, and rise/run direction from one + pure SurfaceGuidance function. Its hysteretic next-state input must prevent + quadrant labels from flapping at an axis boundary. Use an edge label when the + minor axis is under 25% of the dominant axis; otherwise use a corner label. +- Bubble movement uses a critically damped spring (damping ratio >= 1) toward + that real target: no overshoot, random drift, autonomous wobble, or animation + that contradicts the number. Clamp the target to the vial's visual range before + applying the spring so the bubble settles at the rim. +- Etched rings are real visual calibration marks, not decoration: map them to + explicit degree values (initially 1, 2, and 5 degrees) using the same pure + measurement-to-position mapping as the bubble. - Respect disabled system animations and the app's reduced-motion preference by - minimizing or removing the display spring. + direct-positioning the deadbanded bubble value instead of running the spring. - Show a large total tilt beside/below the vial, then Pitch and Roll below it. - At level lock, use the restrained lime state and an explicit text label. @@ -82,32 +106,42 @@ The user should be able to answer three questions at a glance or by sound: millimetres per metre and/or inches per foot based on the selected unit system. - Treat exact board/counter dimensions and calculated shim/foot height as a later Adjustment Assist feature, not a requirement of this stage. -- When the reading is moving, show a subtle `Settling` state and defer strong - correction guidance until the stable measurement is trustworthy. +- Use the shared settling detector from calibration. When it is settling, show a + subtle `Settling` state and defer strong correction guidance until the stable + measurement is trustworthy. +- Add a persisted units preference in core/settings for rise/run formatting; + the Tools settings UI remains deferred. ### 5. Add optional hands-free Audio Level Assist - Surface-only, off by default, explicitly user-enabled. - Use a soft sonar-like pulse with a gentle attack and naturally fading tail; never a harsh alarm or rapid smoke-detector cadence. -- Silent while settling or materially outside the usable placement geometry. -- Near level: occasional low-key proximity pulses. +- Silent while the shared settling detector is active or materially outside the + usable placement geometry. +- Near level: occasional low-key proximity pulses with hysteresis between audio + proximity bands, so band changes cannot chatter. - Locked: one distinct soft lock ping; optionally provide a sparse repeat-while- locked preference so a user adjusting a large plank can hear that it remains level. - Reuse the existing lock dwell/hysteresis/debounce so sound cannot chatter. -- Respect media volume, audio focus, system sound settings, app lifecycle, and - the user's Audio Assist preference. +- Use SoundPool with `USAGE_ASSISTANCE_SONIFICATION`; mix with media rather than + taking audio focus for intermittent pulses. Respect media volume, system sound + settings, app lifecycle, and the user's Audio Assist preference. Release audio + resources with the tool composable/flow collection. ## Surface acceptance criteria - A stable phone on a known level plane achieves lock without repeated feedback. -- A 180-degree calibration on a stable but non-level plane improves repeatability - without zeroing that plane's real slope. -- A screen-up phone at 30 and near 90 degrees still shows meaningful numbers; - it does not falsely claim Surface lock. -- Bubble direction agrees with Pitch/Roll and with the high/low instruction. -- Bubble visual remains physically believable but never leads or disagrees with - the stable numeric reading. +- A 180-degree calibration from uncalibrated stable samples on a stable, + roughly-level plane improves repeatability without zeroing that plane's real + slope; inconsistent/moving captures are rejected. +- A screen-up phone at 30, 80, near 90, and screen-down angles always shows a + correct magnitude. Pitch/Roll are suppressed at their documented ambiguous + range, and the Surface tool never falsely claims a lock. +- Bubble direction, high/low instruction, and rise/run direction agree in all + four quadrants through one pure, unit-tested guidance function. +- Bubble position is a pure mapping of stable measurement plus a no-overshoot + spring. Its etched rings match that mapping's documented degree marks. - Audio can guide a user to lock without viewing the screen and stays quiet when disabled, settling, backgrounded, or outside the Surface geometry. - Manual checks cover phone-with-case, phone-without-case, a firm tabletop, and