tasks 47/48: auto-brightness is unbuilt; the shell dies on a lock-owing reload
This commit is contained in:
parent
e421975646
commit
85b172aaf1
3 changed files with 158 additions and 1 deletions
69
docs/tasks/47-auto-brightness-anchors.md
Normal file
69
docs/tasks/47-auto-brightness-anchors.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# 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 | **yes** — `SensorSource::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).
|
||||
86
docs/tasks/48-lock-surface-reload-fatal.md
Normal file
86
docs/tasks/48-lock-surface-reload-fatal.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# TASK 48 — The shell dies on any scene reload that owes a lock
|
||||
|
||||
**Status:** open, raised 2026-07-29, fully diagnosed and reproducible.
|
||||
**Size:** one session, but it is on the lock path — read TASK-43 first.
|
||||
**Repo:** `souveraine` (`modules/common/panels/lock/LockScreen.qml`).
|
||||
|
||||
## Symptom
|
||||
|
||||
```
|
||||
FATAL: Tried to show lockscreen surfaces without active lock
|
||||
ERROR: Quickshell has crashed under pid <n>
|
||||
ERROR: Quickshell has been restarted.
|
||||
```
|
||||
|
||||
Eleven crash directories under `~/.cache/quickshell/crashes/` going back to
|
||||
2026-07-17, so this is long-standing and roughly every second or third day. Two
|
||||
on 2026-07-29 alone (09:14:37, 10:41:11), both immediately after a scene reload.
|
||||
|
||||
## Mechanism
|
||||
|
||||
`LockScreen.qml:104` is
|
||||
|
||||
```qml
|
||||
WlSessionLock {
|
||||
locked: GlobalStates.screenLocked
|
||||
surface: root.sessionLockSurface
|
||||
}
|
||||
```
|
||||
|
||||
On a **scene reload** the whole QML tree is rebuilt while the outgoing instance
|
||||
is still alive and still holds the compositor's ext-session-lock. In the new
|
||||
tree `GlobalStates.screenLocked` starts `false`. Then `SessiondBridge`
|
||||
registers, sessiond answers `must_lock=true` (the session *is* locked), and
|
||||
`registerWithAuthority()` sets `GlobalStates.screenLocked = true`.
|
||||
|
||||
The binding fires, quickshell asks for the lock, **the compositor denies it —
|
||||
another client holds it** — and the surface component is instantiated anyway.
|
||||
That is the FATAL.
|
||||
|
||||
The file already knows about this denial path. `onLockStateChanged` carries a
|
||||
comment about ext-session-lock `finished` "denied because another client held
|
||||
it", and resyncs `screenLocked` when it happens. But that handler runs *after*
|
||||
surfaces were attempted, so the abort beats the guard to it every time.
|
||||
|
||||
## Why it surfaced now
|
||||
|
||||
It did not — it was always there. It was partly *masked*: before
|
||||
souveraine `45fbbea`, a reload's `shell_ready` was refused ("already
|
||||
registered") and the shell gave up permanently, so `must_lock` was never
|
||||
applied and `screenLocked` was never set. The reload stayed silently
|
||||
unregistered instead of crashing. Fixing the registration made the reload reach
|
||||
the lock path, which is correct behaviour meeting a real bug.
|
||||
|
||||
So: **the fix for TASK-48 is not to undo `45fbbea`.** An unregistered shell is
|
||||
strictly worse — sessiond believed there was no shell for 90 minutes.
|
||||
|
||||
## What the fix has to respect
|
||||
|
||||
- **Do not gate the surface on `secure`.** `secure` is the compositor's ack that
|
||||
a surface is up; using it as the precondition for putting one up is circular.
|
||||
- **Adopting beats re-requesting.** The phone and laptop both run
|
||||
`misc:allow_session_lock_restore = true`, which is what lets a relaunched
|
||||
`qs -c souveraine` take over an abandoned lock. A reload should land on that
|
||||
same path rather than racing a second acquire against a live holder.
|
||||
- **Never open the panel to close the race.** Failing closed here means staying
|
||||
locked and crashing loudly, which is what it does today. Any fix that trades
|
||||
the crash for a briefly unlocked panel is worse than the crash.
|
||||
- TASK-43's note applies verbatim: *the two-client lock handoff is the thing not
|
||||
to get wrong.* Another session is building viewtop's ext-session-lock
|
||||
admission gate — sync with it before touching this.
|
||||
|
||||
## Acceptance
|
||||
|
||||
1. `touch` any file in the composed tree while the session is locked. The shell
|
||||
reloads, re-registers, sends `locked_ack`, and does **not** crash.
|
||||
2. `~/.cache/quickshell/crashes/` gains no new directory across ten such reloads.
|
||||
3. The panel never becomes visible during the reload — verify with `grim`, not
|
||||
by looking, since the window is under a second.
|
||||
4. `{"op":"device_state"}` reports `shell_alive: true` and `phase: released`
|
||||
afterwards.
|
||||
|
||||
## Connects to
|
||||
|
||||
TASK-43 (viewtop's lock admission gate — the other half of two-client lock),
|
||||
TASK-02 (lockscreen as a Rust system), `LOCK-DPMS-LESSONS.md` §1,
|
||||
souveraine `45fbbea` (the registration fix that unmasked this).
|
||||
|
|
@ -58,7 +58,9 @@ Four that are cheap relative to what they unblock:
|
|||
|
||||
| # | Task | What's left |
|
||||
|---|------|-------------|
|
||||
| 46 | [Camera: everything but the DT](46-camera-imx363.md) | CAMSS + imx363/imx355 drivers are in-tree and configured; blueline's DT declares no camera. Beryllium is the same-sensor reference. |
|
||||
| 48 | [Shell dies on any reload that owes a lock](48-lock-surface-reload-fatal.md) | Fully diagnosed 07-29, 11 crashes since 07-17. A reload re-requests ext-session-lock while the outgoing instance still holds it → `FATAL: Tried to show lockscreen surfaces without active lock`. **Sync with TASK-43 before touching the lock path.** |
|
||||
| 47 | [Auto-brightness: anchors, none of it built](47-auto-brightness-anchors.md) | §12 reads as settled and has **zero** code behind its actuation half — no brightness `Action`, no anchors, `grep anchor src/` is empty. The evidence half (lux in, health tracked) is live. |
|
||||
| 46 | [Camera: the rear sensor was the gap](46-camera-imx363.md) | Front IMX355 pair was already declared; rear IMX363 added 07-28 (kernel `c7784b716`, branch `mic-race-fix`). Not yet built or run — no camera has ever produced a frame on this device. |
|
||||
| 44 | [Fingerprint (FPC1020)](44-fingerprint-fpc1020.md) | Piece 1 (wake source / input) is a session — DT + shim already extracted. Piece 2 (biometrics) is a TrustZone question, answer it before scheduling it. |
|
||||
| 45 | [Haptics (CS40L20)](45-haptics-cs40l20.md) | **First: which chip actually drives the motor** — `pmi8998_haptics` is what the dial's detents use today. Then port `cs40l2x` from Kirisakura; firmware blobs are committed. |
|
||||
| 41 | [Producers must be attested](41-attested-producers.md) | Design first. Demonstrated live 07-28 — a squeeze and a dial-open both accepted from an SSH shell. |
|
||||
|
|
|
|||
Loading…
Reference in a new issue