Watch
1
0
Fork
You've already forked SouveraineOS
0

tasks: 30 agent reach, 31 radial dial, 32 pill+keyboard; 08 gains the two unsurfaced findings

This commit is contained in:
Fimeg 2026-07-26 16:07:20 -04:00
commit 3e4373bbc5
5 changed files with 251 additions and 1 deletions

View file

@ -93,6 +93,27 @@ Wants (from handoff doc item 12):
race the idle lock again.
- The conclusions want to land in `power-profiles-daemon` as the hint layer.
## Two things the machine knows and nothing says (open, 2026-07-26)
**(f) `sensors_degraded` reaches no surface.** Source health landed
(`DEVICE-STATE-MACHINE.md` §10): a source that reported and went silent for 90 s
is `down`, not quiet, and every forensic snapshot carries `sensors_degraded`.
The trail knows. No screen does. The crash reporter structurally cannot help —
all three of its sources key on something *failing*, and the SLPI outage exited
status 0 (archive/05). This is the surfacing half: an ambient indicator when a
source that used to work has stopped, legible on the lock surface, saying which
and since when. `device_state` already ships `sensor_health` over IPC.
**(g) The confidence gates are computed, logged, and never branched on.** §4's
threshold table (<0.3 / 0.30.6 / 0.60.8 / >0.8) and the cross-sensor
disagreement rules are written to the trail and ignored. **This needs a decision
before code**, and the decision moved on 2026-07-26: the table was written when
proximity suppressed DPMS wake and blanked locked screens, and proximity now
does neither — it vetoes tap-to-wake and is otherwise evidence. So the question
is no longer "what should confidence gate" but "is anything left for it to
gate". The honest candidate is the doze promotion rate and possibly nothing
else. Answer that, then wire it or delete the table and record why.
## Sub-tasks (do separately)
1. State machine + pub/sub (mce/datapipe shape), device-flag gating.

View file

@ -0,0 +1,72 @@
# TASK 30 — Agent reach: verb tables across the surfaces
**Status:** half built 2026-07-26 (sessiond). **Size:** one session per surface.
**Repo:** `~/Projects/souveraine` (`surfaces/quickshell`).
## Goal
The agent owns the device 60/40 (doctrine §13) and cannot currently compose a
single intent across two surfaces. sessiond can now be enumerated and its
refusals branched on; the shell's surfaces cannot. Until they can, "full phone
control" is control of the device and not of the things on it.
## What exists after 2026-07-26
sessiond has the pattern, working, in `src/sessiond/protocol.rs`:
- `describe` returns a verb table — every op, whether it mutates, the refusal
codes it can return, and a request line that parses. Knowing an op exists is
not enough to call it; a test round-trips every advertised example through
the real parser, so a table that lies fails the build.
- Refusals carry a code beside the prose: `unsupported_op`,
`invalid_argument`, `refused_by_state`, `not_permitted`, `unavailable`.
Not-now, never, and wrong-arguments are three different next moves.
- Requests carry an optional `intent`, stamped onto every trail entry the
request produces, so a chain's leaves join back to one decision.
## What to build
The same three things on each shell surface, in this order — smallest blast
radius first, which is the order the dock manifest work already argued for:
1. **`dock.*`** — the manifest exists; give it `describe` and coded refusals.
2. **`apps.*`** — `app-inventory-manifest.md` (archive/from-substrate) scoped
the inventory; it is a read surface, so it is the cheapest to make legible.
3. **`shell.*`** — surfaces and state.
4. **`settings.*`** — the largest, and the one with a real policy layer:
`SETTINGS-AUTHORITY.md` already specifies risk tiers and propose-vs-set.
Its refusals map onto the same five codes; do not invent a sixth.
Quickshell drops `var` returns, so every result is a JSON string. That bug cost
the `{ok, reason}` contract its first six months — every new surface inherits
the convention or it cannot be chained.
## Then: chaining itself
With the vocabulary in place, what remains is failure semantics. A chain that
refuses at step three is not the same as one that never started, and whether
the earlier steps roll back is per-chain and must be **stated, not
discovered**. Decide it when the first real chain exists, not before.
## Acceptance
- `describe` on every surface, with examples that parse, enforced by a test.
- Every refusal carries a code from the five; none carries only prose.
- A chain of four verbs across two surfaces records one intent in the trail,
and reconstruction reads the decision rather than four orphans.
- A refusal mid-chain is legible enough for the agent to pick another path
without being told what went wrong out of band.
## Do not
- Build a new toolcall integration. Settled 2026-07-13: the agent reaches
these through its existing harness.
- Let a surface invent its own refusal vocabulary.
- Inline the teaching. The surface exposes; the Souveraine School teaches.
## Connects to
`SESSION-AUTHORITY-DOCTRINE` §13 (the mandate), `SHELL-ECOSYSTEM.md`
(manifests + guarded methods), `FIRST-CLASS-APPS.md` §6/§7 (legibility and
agency are two of the nine), `SETTINGS-AUTHORITY.md`, TASK-31 (the dial is the
human rendering of the same vocabulary).

View file

@ -0,0 +1,74 @@
# TASK 31 — The radial dial
**Status:** open, wanted soon (Casey, 2026-07-26). **Size:** component first,
then invocations, then contents. **Repo:** `~/Projects/souveraine`
(`surfaces/quickshell`).
## Goal
A radial dial of options, summoned by a gesture, reachable by thumb without
looking. The phone's fastest path from "I want a thing" to the thing.
## The idea worth getting right first
**The dial and the agent's verb table are the same vocabulary.** One rendered
for a thumb, one for a composer. A dial whose entries are a hand-written list
is a third place device capabilities are enumerated — after the verb table and
the manifests — and it will drift from both within a month.
So the dial's contents come from the surfaces' `describe` (TASK-30): each entry
is a verb that mutates, is permitted in the current state, and has somewhere to
say what it does. A verb the agent cannot reach should not appear on the dial
either, and vice versa. That is the check that keeps them one system.
This also settles what happens on a refused entry: the dial shows the refusal's
*code* as its disabled reason (`refused_by_state` → "not while locked"),
because the surface already had to say so.
## Priority order — the component before the contents
TASK-18 learned this the hard way and wrote it down: *"the stevia keyboard
shipped glitchy because the surface work was rushed."* A dial whose actions are
stubs is fine. A janky dial is not.
1. **The component.** Geometry, anchor, open/close animation, thumb reach,
what happens when it opens near a screen edge, how it dismisses. Built from
Souveraine-owned AGPL primitives — this is a first user of the primitive set
`souveraine-lock-primitives` (archive/from-substrate) specifies, and a good
reason to build them, since a dial needs shapes and ripples that ii's
widgets would otherwise supply.
2. **Invocation.** Squeeze is the natural one (TASK-13) and is not the only
one — long-press and an edge gesture want to reach it too. Every path
registers through the gesture/keyboard routing system, never a hardcoded
binding (INTERFACE-ARCHITECTURE §4).
3. **Contents.** From the verb tables, per above.
## Constraints inherited, not invented
- **Layer and state.** The dial declares its layer and lets shell state gate
it (`FIRST-CLASS-APPS` §4). It must not paint over a locked screen, fight the
OSK, or survive an overview.
- **Lock and tier.** Ambient entries may appear on the lock surface; personal
ones and any mutation gate on `screenLockSecure` plus a fresh grant
(`FIRST-CLASS-APPS` §5). A dial is exactly the shape of thing that leaks by
being convenient.
- **Update management.** It ships as part of the shell surfaces, which are
**not packaged today** — they live in `$HOME` and arrive by rsync. TASK-25
names this; the dial does not get an exception, and "how does this reach a
device" is part of its acceptance, not an afterthought.
## Acceptance
- Opens under the thumb from any app, at any screen edge, without clipping.
- Dismisses by the same gesture, by tapping away, and by pressing back.
- Entries are derived from a surface's verb table, not a list in the QML.
- A disabled entry says why, in the words the refusal already used.
- Does not appear over a locked screen with anything personal on it.
- Reaches a device by a mechanism that survives the next update.
## Connects to
TASK-13 (squeeze — the first invocation), TASK-30 (the vocabulary),
TASK-18 (selection menu — the other summoned surface; they should not become
two frameworks), `FIRST-CLASS-APPS.md`, `INTERFACE-ARCHITECTURE.md` §4,
`souveraine-lock-primitives.md` (the owned widget set this needs).

View file

@ -0,0 +1,80 @@
# TASK 32 — The pill and the keyboard, as owned apps
**Status:** open. Both are erroneous in daily use (Casey, 2026-07-26).
**Size:** two sessions, one each. **Repo:** `~/Projects/souveraine`,
`~/Projects/Pixel3Arch/pkgs/stevia`.
## Why one task for two surfaces
They fail the same way and neither has an owner. Each is a thing the user
touches constantly, each was built by patching symptoms, and neither has a
section that says how it ships or who decides its behaviour.
`FIRST-CLASS-APPS.md` lists nine requirements; both fail §1, §6, §7 and §8, and
the keyboard has failed §8 hardest — four pkgrels of it existed only on the
phone before any of it came back to the repo.
Treat each as an app: what it is, what it owns, how it is configured, **how it
updates**, and what it refuses. Not a bag of fixes.
## The keyboard
**Current state:** stevia 0.56.0-7, patched for Hyprland, with a dictation mic
key. TASK-24 scoped the layout rework and it has not happened: backspace is on
the wrong row, the bottom row has seven keys against the reference's six, and
the layout matches no validated design. TASK-17 records the other live defect —
completion is on by default but stevia asks hunspell for `en-us` while the
dictionary is `en_US`, so it may still show nothing.
**The rule TASK-24 already wrote and that must hold:** start from a validated
reference (squeekboard `us+apple`, gmankab `us-mobile`), not from first
principles. No layout change ships without a screenshot of the rendered
keyboard on the device.
**Update management.** stevia is a Pixel3Arch package built on archdev and
hand-carried; nothing publishes it (TASK-27). Upstream ALARM ships a
`squeekboard` that declares `conflicts=stevia`, so the phone currently pins
squeekboard to keep both — an interim recorded in `rootfs-overlay/etc/
pacman.conf` that must come off when our own squeekboard ships at a version
outranking theirs. The osk-switch toggle is the runtime arbiter and is staged,
not functional.
## The pill
**Current state:** `SystemGestureRail`, always-on overlay, one MouseArea
recognising double-tap, swipe-up and swipe-down. The interaction layer is
sound. What is missing is ownership: the pill's z-order guarantee was
documented but not enforced until a layerrule was added, the OSK lift was a
stale hardcoded 315 px that stacked it over the keys, and single-tap-as-Home is
specified in TASK-14 but lives nowhere as a contract.
**What it should own:** the gesture vocabulary and nothing else. It forwards to
named IPC targets and makes no app decisions (INTERFACE-ARCHITECTURE §1).
Every new gesture is a row in the table, not a branch inline — and with
TASK-31, one of those rows is the radial dial.
**Update management.** Ships with the shell surfaces, which are unpackaged and
live in `$HOME`. Same gap as everything else in `surfaces/quickshell`; TASK-25
owns the fix.
## Acceptance
- Each has a section in `docs/` naming what it owns, what it refuses, and how
it reaches a device — not a list of fixed bugs.
- Keyboard: layout matches a validated reference, screenshot-verified at every
level; completion actually completes.
- Pill: the gesture table is single-sourced; adding a gesture touches one row.
- Both ship by a mechanism that survives an update, or the gap is named with
the task that owns it.
## Do not
- Patch another keyboard symptom without a reference layout in hand.
- Add pill gestures while z-order is enforced by documentation rather than by
a rule.
## Connects to
TASK-17, TASK-24 (the keyboard's history — read both before touching a
layout), TASK-14 (single-tap Home), TASK-31 (the dial is a pill gesture),
TASK-25/27 (how either of them ships), `FIRST-CLASS-APPS.md`,
`INTERFACE-ARCHITECTURE.md`.

View file

@ -15,7 +15,7 @@ plus open threads from the 2026-07-17 session.
| 2 | [Lockscreen-as-Rust-system: glance + swipe](02-lockscreen-rust-system.md) | in progress | stopgap shipped 2026-07-17 |
| 3 | [Boot timing: fade splash → lock, no extra fixes](03-boot-timing-splash-to-lock.md) | diagnosed | EBUSY handoff race is the last timing bug; splash text killed, holder added |
| 7 | [WCD9340 mic: functional-pinned; unpin + fidelity](07-mic-wcd9340-slim-tx.md) | functional 07-20 | defect: power pin; kernel `mic-race-fix` |
| 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 — a shell deploy over a locked session still crashes the lockscreen** |
| 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 | substrate up; bring-up unit not drafted |
@ -33,6 +33,9 @@ plus open threads from the 2026-07-17 session.
| 27 | [Gitea pipeline audit](27-gitea-pipeline-audit.md) | open | overlay is not a package; no `primary` branch; poisoned build sysroot |
| 28 | [Handover on upgrade](28-upgrade-handover.md) | open | half-upgrades move authority silently; blocked-by TASK-27 packaging |
| 29 | [SouveraineOS Updater](29-souveraine-updater.md) | started 07-25 | rust rewrite of pachub; doesn't build yet; first repo to build *into* the pipeline |
| 30 | [Agent reach: verb tables across the surfaces](30-agent-reach-verb-tables.md) | half built 07-26 | sessiond has describe + refusal codes + intent; dock/apps/shell/settings do not |
| 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 |
| — | [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,