Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/tasks/47-auto-brightness-anchors.md

3.5 KiB

TASK 47 — Auto-brightness: the design is settled, none of it is built

Status: open, raised 2026-07-29. Size: one session for the Action + hysteresis; a second for anchors and their persistence. Repo: souveraine (sessiond), then souveraine (settings surface).

The gap

DEVICE-STATE-MACHINE.md §12 reads as finished work. It settles where light belongs in the chain, that auto-brightness must be an Action and not an eighth blind actor, how it interacts with Dim/Restore, that it needs hysteresis, and — in a subsection marked "settled 2026-07-27" — that manual correction produces anchors, not a global bias.

Measured against the code on 2026-07-29:

§12 claim in code
lux enters through sensor_input like proximity yesSensorSource::Light, fed by souveraine-sensord
light health is tracked yes — live on the phone, "light": "live"
light_changing carries evidence weight yes — §4 arithmetic
auto-brightness leaves as an Action no
suspended between Dim and Restore no
hysteresis / ramp on lux no
manual corrections stored as (lux, brightness) anchors no
anchors persisted next to other settings no

grep -rn "anchor" src/ returns nothing. Action is Dim | Restore | Blank | Lock — there is no brightness verb at all. The evidence half of §12 is live; the actuation half does not exist.

This is the failure mode a settled-looking doc creates. Nothing in §12 says "unbuilt", and the section that sounds most finished — anchors, with the iOS patent reasoning worked through — is the part with zero lines behind it.

What exists to build on

  • sensord already reports light with the asymmetric debounce taken from this device's own Android RRO (config_autoBrightnessBrighteningLightDebounce=2000, DarkeningLightDebounce=4000). That is the hysteresis input, already smoothed.
  • Action::Dim / Action::Restore are the model to copy: computed in tick(), executed by the executor table, one writer, one trail.
  • note_brightness_before_dim / take_brightness_before_dim already establish that sessiond owns the panel value and that a second writer makes the captured value a lie. Auto-brightness must go through the same door.

Acceptance

  1. Action::SetBrightness(u32) exists, is computed in tick(), and is executed by the same table as Dim. No second process writes brightnessctl.
  2. It proposes only while undimmed and not Locked, per §12. Between Dim and Restore it is suspended, not rate-limited — and Restore re-evaluates from current lux rather than replaying the captured number.
  3. A sustained lux delta is required before any move, and the move ramps. The thresholds come off the trail, the way PROXIMITY_NEAR_DEBOUNCE did — not out of §12 and not out of this file.
  4. A manual brightness change while auto-brightness is on writes an anchor at the current lux. Two to four anchors, interpolated between, 0%/100% pinned. Pinned at manual maximum, further ambient rise moves nothing.
  5. Anchors persist across a reboot and can be deleted to get the old behaviour back.
  6. The settings page shows the current lux, the computed target, and the anchors — TASK-19 owes the readout, and this is one of the things it reads.

Connects to

DEVICE-STATE-MACHINE.md §12 (the whole design), TASK-40 (lux must reach the machine without widening what the raw stream exposes — that task is the gate), TASK-19 (the readout), TASK-08 (the machine itself).