Watch
1
0
Fork
You've already forked SouveraineOS
0

tasks: index 36-40, correct TASK-34's invalid test, add inference tiering task

This commit is contained in:
Fimeg 2026-07-27 15:49:09 -04:00
commit 4287d8765d
3 changed files with 221 additions and 3 deletions

View file

@ -1,6 +1,31 @@
# TASK 34 — SLPI bring-up sequencing regression
**Status:** open, raised 2026-07-26. **Size:** one session, mostly bisection.
**Status:** NEEDS RE-DIAGNOSIS — the test below is invalid (2026-07-27).
**Size:** one session, mostly bisection.
> **2026-07-27:** measured on the device, `blueline-hexagonrpcd-sdsp` is
> **active**, and the sensors work: `monitor-sensor --light` returns live,
> changing lux (`0.665771`, `unit: lux`) and iio-sensor-proxy answers
> `HasProximity`/`HasAmbientLight`/`HasAccelerometer` = true.
>
> The symptom below — "no ssc-accel, ssc-proximity, ssc-light in
> `/sys/bus/iio/devices`" — **is not a valid test and was never going to be.**
> The SSC sensors are not IIO devices. `90-iio-sensor-proxy-ssc.rules` tags
> them onto the FastRPC *misc* nodes:
>
> ```
> SUBSYSTEM=="misc", KERNEL=="fastrpc-sdsp*", ENV{IIO_SENSOR_PROXY_TYPE}+="ssc-accel ssc-proximity ssc-light"
> ```
>
> so iio-sensor-proxy reaches them over libssc/FastRPC and they never appear
> under `/sys/bus/iio/devices` — healthy or not. That directory holds exactly
> two entries on a working device, both PMIC ADCs, which is what it holds
> today.
>
> Whatever happened on 07-26 was real (the service did hit its start limit),
> but "no SSC sensor reached userspace" was inferred from a test that cannot
> show that. Re-diagnose from the service journal and a live `monitor-sensor`,
> not from the IIO device list. **TASK-13 is not blocked on this.**
**Repo:** `~/Projects/Pixel3Arch` (`rootfs-overlay/etc/systemd/system`).
## Symptom

View file

@ -0,0 +1,188 @@
# TASK 40 — Inference tiering for the sensor surfaces
**Status:** open, gating. Raised 2026-07-27 (Casey, explicitly: the "based on
what they might infer" lens). **Size:** decisions first, then small code.
**Repos:** `souveraine` (sensord, sessiond, dial QML), `Pixel3Arch`
(blueline-edge-sense).
## The lens, in its own words
`SECURITY-AUDIT.md` **P1 — inference-first tiering**:
> Permission classes are defined by **what a capable agent could infer from a
> stream, not by what the field literally is.** A contact count, an app-launch
> timing log, an ambient noise level, a raw accelerometer reading — each is
> "just data" per-field, and each is an inference substrate for a sufficiently
> capable model.
>
> AOSP retrofitted the 200Hz sensor cap and `HIGH_SAMPLING_RATE_SENSORS`
> *after* realizing motion sensors infer keystrokes, gait, identity — it was a
> gap, then a patch, not first-class design. We model inference as first-class.
>
> **Application:** every tier assignment must be justified against the
> inference lens. "Looks innocent" is not a defense.
Three things landed or moved in the last day that this has never been applied
to. Two are about to start emitting for the first time, which is exactly the
moment P1 says to do the work — before the stream flows, not after.
---
## 1. The sensor stream is about to exist for the first time
`souveraine-sensord` has been `enabled` and **dead from every boot** (its unit
hung off `graphical-session.target`, which nothing on this device starts —
fixed 2026-07-27). The forensic trail proves the consequence: 2093 wake, 138
transition, 121 decision, 29 error, and **not one sensor entry**. So the
device state machine — and through doctrine §13, the agent — has never had
sensor evidence at all.
Turning it on is therefore not "restoring" a stream. It is opening one.
### What the current shape gets right, apparently by accident
`sensord` reports **edges, not levels** (`de3a332`, "sensord: report edges, not
every sample"):
- light → `{"changing": bool}`, never lux
- accel → `{"moving": bool}`, derived from iio-sensor-proxy *orientation*
changing, not from raw acceleration
- proximity → near/far, debounced
That is close to the minimum an inference-first design would have chosen. The
agent learns *that* something changed, not the waveform. Accel in particular
is orientation-only, which is the difference between "the phone was picked up"
and the 200 Hz stream AOSP had to cap.
**This should be written down as a property to preserve, not left as a
side-effect of an unrelated commit.** Nothing currently stops a future change
from passing lux through "because the machine needs it".
### Which is exactly what auto-brightness proposes to do
`DEVICE-STATE-MACHINE.md` §12 (auto-brightness) requires lux to enter as
`sensor_input` so brightness can leave as an `Action`. That converts light
from a boolean edge into a **continuous level**, and P1 says that is a tier
change requiring justification, not an implementation detail.
Ambient light over time is the sleeper in this set. Individually it looks like
the most innocent value on the device; as a series with timestamps it carries
sleep and wake times, indoor/outdoor transitions, commute shape, and whether
the house is occupied. Combined with proximity and orientation edges it is a
timeline of a day — P1's "intersection attack" line, verbatim.
**The decision this task owes:** lux may be needed by the *machine* without
being readable by the *agent*. Those are different consumers and the code does
not currently distinguish them — `sensor_evidence` is on the snapshot and
doctrine §13 makes snapshots agent-readable. Splitting "what `tick()` computes
on" from "what `describe`/snapshot exposes" is the concrete work.
---
## 2. Grip is the sharpest case on the device
`elmyra_raw` is **six strain gauges at 100 Hz** (libssc patched to take the
fastest advertised rate, TASK-13). Against P1 this is not a near-miss of the
AOSP precedent — it is the precedent:
- **Keystrokes.** Every tap flexes the chassis. A 100 Hz six-channel strain
array on the frame is a typing side-channel, which is the literal thing the
`HIGH_SAMPLING_RATE_SENSORS` permission was retrofitted for.
- **Identity.** Where and how hard a person holds a phone is a biometric.
Google's own factory calibration (`edgesense_roller_cal`) exists because the
gauges differ per unit and per grip — that sensitivity is the feature and the
leak.
- **Health.** Hand tremor is in that band.
TASK-13 has been, correctly, a bring-up task. It contains **zero tier
analysis**, and its own producer is the leaky shape: `blueline-edge-sense`
opens `elmyra_raw` and decodes six floats per sample **into AP userspace at
100 Hz**, which is the highest-inference stream we have ever run.
### The architecture that makes it work is also the architecture that fixes it
This is the part worth not missing. TASK-36 established that squeeze cannot be
configured from the AP at all: the AFE control surface
(`ashConfigureElmyraFilters`) is reachable only from inside a CHRE nanoapp, and
`elmyra.so` is that nanoapp. It consumes the raw gauges **on the SLPI** and
emits a gesture.
So the correct implementation is also the private one:
| | raw stream | what crosses to the AP |
|---|---|---|
| `blueline-edge-sense` (today, diagnostic) | 100 Hz × 6 channels, in AP userspace | everything |
| `elmyra.so` nanoapp (TASK-36) | stays on the SLPI | "a squeeze happened" |
That is the tiering argument made in hardware. **The rule this task should
set: the raw `elmyra_raw` stream is a bring-up instrument, not a product
surface.** `blueline-edge-sense` keeps existing as a diagnostic, gated and
not running by default; the shipping path is the nanoapp's gesture event.
Doing it the other way — shipping the raw reader because it works sooner —
would be P1's "gap, then a patch" repeated knowingly.
---
## 3. The dial enumerates capabilities, and enumeration is not gated
`TASK-31` already says the dial "does not appear over a locked screen with
anything personal on it". That governs **painting**. It does not govern the
IPC, and the IPC is the agent-facing surface.
`modules/souveraine/dial/DialHost.qml` today:
```qml
IpcHandler {
target: "dial"
function open(): void { scope.dialOpen = true; ... }
function close(): void { ... }
function toggle(): void { ... }
function entries(): string { /* label, icon, enabled, reason for every entry */ }
}
```
None of the four checks lock state, and `GlobalShortcut { name: "dialToggle" }`
does not either. Two distinct problems:
- **`entries()` is reconnaissance without exercise.** P3 says capability *use*
is visible, not just the grant — but enumeration is neither a grant nor a
use, so it is invisible. The returned `reason` strings are designed to be
explanatory ("no focused window to close"), which makes the refusal itself an
information channel: it answers "is an app open right now" to any caller, at
any lock state, leaving no trace.
- **The contents are the leak, not the ring.** Today's entries are generic
(lock, screenshot, kill window, health, agent, keyboard). TASK-31's whole
design is that entries come from the verb tables (TASK-30) — at which point
the dial's contents become "every mutating verb currently permitted", which
is precisely a capability map, and a per-contact or per-app entry would carry
personal data into the ring.
**The decision this task owes:** `entries()` must answer at the caller's tier,
not the device's — an ambient caller sees ambient verbs. That has to be settled
*before* TASK-30 feeds the dial, because afterwards it is a filter retrofitted
onto a list, which is the shape P1 warns about.
---
## Acceptance
- The edges-not-levels property of `sensord` is documented as a requirement
with a rationale, so a later change has to argue with it.
- Lux reaches the state machine for auto-brightness **without** widening what
the agent can read, or the widening is explicitly justified and recorded.
- `elmyra_raw` is designated a bring-up instrument; the shipping squeeze path
is the nanoapp gesture (TASK-36), and `blueline-edge-sense` does not run by
default.
- `dial entries()` and `dial open()` answer at the caller's tier, and the
refusal `reason` strings are checked for what they disclose.
- Each of these is justified in writing against P1, per its own "Application"
clause — "looks innocent" is not a defense.
## Connects to
`SECURITY-AUDIT.md` P1/P2/P3, `TRUST-BOUNDARY-MATRIX.md`,
`SESSION-AUTHORITY-DOCTRINE.md` §9 (evidence not fact) and §13 (agent-readable
context), `DEVICE-STATE-MACHINE.md` §4 and §12, TASK-13 (grip), TASK-36 (the
nanoapp that makes the tiering physical), TASK-30 (verb tables — the thing that
will feed the dial), TASK-31 (the dial).

View file

@ -18,7 +18,7 @@ plus open threads from the 2026-07-17 session.
| 8 | [Device state manager / power profiles](08-device-state-manager.md) | actuating 07-25 | clock + actuators; lock-before-blank is an invariant and hypridle's listeners are deleted; (b) charge real, (c)(d) open, **(e) lease still missing**, **(f) `sensors_degraded` reaches no surface**, **(g) the confidence gates need a decision before code** |
| 9 | [Suspend-resume FTS calibration race](09-suspend-resume-fts.md) | interim-fix | kernel-side; cold-boot only |
| 12 | [Face auth as a capability factor (Gaze reference)](12-gaze-reference-face-auth.md) | open | per-target: phone needs front-cam bring-up |
| 13 | [Active Edge (squeeze) as a sensor input](13-active-edge-squeeze.md) | open, wanted | rail is UP and held from boot (07-26); step 2 now blocked on TASK-34 (SLPI doorbell) |
| 13 | [Active Edge (squeeze) as a sensor input](13-active-edge-squeeze.md) | open, wanted | rail is a DT gpio-hog now (07-27); NOT blocked on 34; real path is TASK-36, and the raw stream needs TASK-40 first |
| 14 | [Overview/app grid + Auxo multitasking](14-overview-appgrid.md) | in progress | shell-side; Home path + app cards |
| 15 | [Idle coordinator: freeze, doze, scheduled wakeups](15-idle-coordinator.md) | blocked | freezing `app.slice` would freeze sessiond+shell+secrets; needs slice separation first (measured 07-25) |
| 16 | [culver: usable messenger + contacts as Personal-class store](16-culver-messenger-contacts.md) | in progress | A/B/D landed; C contact card next; F is the folded-in TASK-01 |
@ -37,8 +37,13 @@ plus open threads from the 2026-07-17 session.
| 31 | [The radial dial](31-radial-dial.md) | open | component before contents; entries come from the verb tables, not a list in QML |
| 32 | [The pill and the keyboard, as owned apps](32-pill-and-osk-as-apps.md) | open | both erroneous daily; neither has an owner or a shipping story |
| 33 | [Battery and charging belong to the device state machine](33-battery-as-device-state.md) | open | last raw feed going straight to the glass; the charge ceiling is writable today |
| 34 | [SLPI bring-up sequencing regression](34-slpi-bringup-sequencing.md) | open | old sequencing error; doorbell dies on SSR and stays dead; blocks TASK-13 step 2 |
| 34 | [SLPI bring-up sequencing regression](34-slpi-bringup-sequencing.md) | **needs re-diagnosis 07-27** | doorbell is ACTIVE and sensors work (live lux measured); its `/sys/bus/iio/devices` test is not valid — SSC sensors never appear there |
| 35 | [sessiond answers nothing: handler threads park forever](35-sessiond-handler-deadlock.md) | **open, live** | 347 threads in futex_wait, 691/1024 fds; accept loop fine, no request ever answered |
| 36 | [CHRE nanoapp host for the SLPI](36-chre-nanoapp-host.md) | open | chrecd already runs CHRE with zero nanoapps; method table recovered 07-27, three methods to add |
| 37 | ["Back" as a first-class verb](37-navigation-back-verb.md) | open | our Gestures.qml is already the compositor half; `can_go_back` is the dial's enabled/reason |
| 38 | [Pill swipe-to-max swaps the keyboard](38-pill-swipe-osk-swap.md) | open | micro; the ONLY route to a terminal keyboard since stevia dropped its layout |
| 39 | [Audio levels as tracked state, and sound roles](39-audio-levels-as-state.md) | open | check WirePlumber restores first; roles before a number |
| 40 | [Inference tiering for the sensor surfaces](40-inference-tiering-sensors.md) | **open, gating** | P1 applied to sensord/grip/dial; blocks TASK-13's raw stream and gates TASK-31's IPC |
| — | [Unify the shell trees](unify-shell-trees-laptop-phone.md) | open | ~900-file `ii` drift; blocks durable shell fixes reaching the phone |
Archived (see `archive/`): 05 crash reporter — done 2026-07-21,