Summoning on onPressed meant any gesture that merely began over the search
field raised the keyboard — including the swipe that scrolls the app grid,
which starts at the top of the drawer more often than not. The press now only
remembers where it landed and the release decides, with a generous slop: a
thumb reaching the top of a 540px panel is not steady, and a few pixels of
drift while tapping a text field is a tap.
`posture()` called window.live2dMotion, which does not exist — measured
undefined on the device. Nine states advertised to her in the prompt and
wired to nothing. The bundle exposes two levers: #live_talk, a motion slot it
reads every frame, and its own head/body hit test. The nine collapse onto
those rather than pretending: speaking talks, idle rests, and the rest are a
look — head for attention, body for effort.
#live_talk starts at "0". "1" means start a talk motion, so she came up
mouthing words with no voice behind them.
The gaze socket used onConnectedChanged; Quickshell's Socket emits
onConnectionStateChanged, so the subscribe was never sent — a handler for a
signal that does not exist.
The handler already existed and already did this — a second one in the same
MouseArea is "Property value set multiple times", which failed the whole
module and took the shell down. The pill's stranded-pose symptom is
something else; the cancel path was never the hole.
onReleased is the only thing that clears the pull's pose, and a MouseArea
whose grab is taken away never gets one — it gets onCanceled, which had no
handler. So a cancelled climb left `dragging` true, the pull progress set and
the app posed at 0.6: shrunken, with nothing on screen to say why, and no way
back except pulling the pill again to force a fresh press and release
through.
Never a commit. A gesture nobody finished only has to put the glass back.
44px buttons with 35px icons read small on a 540px panel, and a stack's
members were 12px inside a 35px box — unreadable, which is the whole job of
the collapsed form. 56/44 now, and the member icons give back the border and
padding that were being subtracted from them as well as the grid spacing.
Both numbers live in Config so this is a setting rather than a rebuild, with
spin boxes in Settings > Dock. Two numbers and not one ratio: the button is
the row's height and the icon is what you see, and fixing the ratio would
mean either cramped icons in a tall row or icons overflowing a short one.
ZoneOverview took its width from the Column it sits in, and a Column is as
wide as its widest child. The drawer's search widget supplies that; mission
control is the cards alone and has no search, so the only child left was the
loader — whose width came from the column, whose width came from the loader.
The cycle resolves to zero.
Full-height zero-width cards draw nothing, which is why multitasking was a
blurred backdrop and nothing else while every other signal read healthy:
zones 2, windows 1, subscribed true, loader active, item present, opacity 1,
progress 1. Bound to the panel's width instead, which is what this surface
covers anyway.
Also adds `overview missionControl` and `overview state` — the surface that
had been reported broken was the one nothing but a pill swipe could raise,
and the state readout is what located this.
Multitasking was raised only by the pill's first-stage swipe, so the one
surface that has been reported broken was the one neither an agent nor a test
could reach. state() reports what the cards are drawn from.
Used it immediately: with a window open the model is not empty — zones 2,
windows 1, subscribed true — so the missing cards are not missing data.
Gated on screenLockSecure, the compositor's ack, not on screenLocked. The
request drifts: measured on the phone, `session lock` answered already-locked
while logind reported LockedHint=no and the phone was in use. That is a
shadow copy of state the protocol owns, and a face gated on it would have
been permanently dismissed with nothing on screen to explain why.
No return on unlock. You come back to the clock and summon her if you want
her, rather than finding whatever was left on.
Subscribes to viewtop's gaze feed and dispatches a synthetic pointer move on
the canvas, so live2d.js's own mousemove path drives head angle and eye
direction with its existing damping. Synthetic is the point: she is
deliberately not being given those contacts, and this lets her watch them
anyway.
Screen coordinates in, her window's coordinates out — the origin comes from
the compositor's furniture report rather than assumed, because the layout
decides it and it moves with the zone.
Entries may carry a hold verb beside verb; the ring fills while it charges so
the commitment is visible while it is still cancellable, and sliding off
cancels. Holding the camera starts a recording, tapping or holding it again
stops it — the same seat, because the dial is muscle memory.
No wf-recorder or wl-screenrec on the device, so it is grim piped into
ffmpeg. Half scale: at 1080x2160 grim manages about 3.5 fps and a recording
that captures a third of its claimed frames plays back at triple speed.
Its own process group, and SIGINT to the group: only ffmpeg can finish the
file, so killing the shell alone would orphan a screen capture with no way to
stop it.
Reachable as a verb too, not only by thumb.
The rig never drew. live2d.js is not only the Cubism runtime — it carries the
reference's character layer, which reads #live_talk every update as its
motion-state slot. Our page omitted the element, so it threw on the first
frame after the model and all four textures had loaded: assets 200, WebGL
live, no error reaching Rust, blank canvas.
--app-id was a no-op. set_prgname ran after GTK had already created the
surface, so the compositor saw souveraine-web and window rules could only
match the title. It has to run before the event loop.
Press and hold her to speak: pw-record at 16k mono s16, release transcribes
through souveraine-stt --file and goes out over Souveraine.send, so the
sidebar logs it. Whisper wraps with newlines and the transcript arrives
through a SplitParser, so it is collapsed here or one utterance becomes four
turns. Double tap dismisses; the clock and quote fade out when she is up and
the clock stops being a target, because a double tap on an invisible object
is not a way back.
The page reports its silhouette and the host installs it as the input region.
Claiming the GL context for preserveDrawingBuffer must happen after the
canvas is sized — before it, the context is born 300x150 and she renders as a
thumbnail in the corner.
Console and errors are forwarded to the host's stdout. Without that a rig
that fails to draw is silent in every direction, which is what cost this
the afternoon.
hyprsunset does not exist under viewtop and execDetached cannot fail loudly,
so the brightness slider and the night light have done nothing since the
move. Same public surface, pointed at viewtop's gamma verb. TASK-61.
Keeps the upstream name: every consumer in the tree spells it, and renaming
across the base tree and the phone overlay is its own sweep.
import Quickshell was missing, which made the Singleton type unavailable and
failed the whole qs.services module — 29 singletons, 28 had it. The phone's
shell was in a restart loop with a 26-line cascade whose only real line was
the last one.
Two gaps in the owner-change handling, both found on device.
The push at adopt time can lose a race. osk-switch sleeps 0.3s before
its deliberate SetVisible(true), and stevia self-shows once it has
finished coming up - both land after the shell has spoken. Measured:
adopt+hide 07:37:51, Visible=true 07:37:52, leaving oskOpen=false with a
keyboard on the screen. Re-assert when the settle window closes so the
last speaker is not the winner.
And the oskSwap gesture never recorded that a swap means the user wants
a keyboard, so osk-switch's re-show and the shell's stale false were
racing with nothing to arbitrate them. Declare the intent first.
sm.puri.OSK0 is a name, not a process. Boot, a crash-restart and an
osk-switch swap each put a fresh keyboard behind it, and the visibility
monitor mirrored the newcomer's opening claim into oskOpen as though the
user had asked for a keyboard. Measured: shell up 03:03:38, self-showed
at 03:03:43, nobody near the phone.
Track the bus owner - gdbus already prints it and the 'Visible' filter
was throwing it away - and on a change push oskOpen at the new owner
instead of pulling state out of it. A process that just started has no
history; the shell is the continuity of intent across the keyboard's
lifetime.
The credential file is shared with `claude` itself and its refresh token is
one-time-use, so whichever process refreshes first rotates the other's copy to
spent. On a failed refresh the provider kept the stale token and 400d every
turn until the service was restarted by hand — measured 2026-08-05, invalid_grant
from 00:03 onward while the file on disk already held a valid pair written at
00:08.
Re-read the file on refresh failure and adopt the token when it is different and
still live. oauth/refresh.rs already does this for the Codex path; the Claude
path never got it.
exclusionMode was ExclusionMode.Ignore, which tells the compositor to
disregard the exclusive zone outright — so the expression below it computed
a height that was thrown away and every window laid out underneath the bar.
The bar is not app space while it is visible.
Auto-hide still yields 0, so a bar configured to hide gives its space back;
the mode only says "honour what I claim".
Not deployed: the phone locked itself on session start, which is
lock-on-startup firing for the first time under viewtop (80cb4b2), and
deploy.sh refuses a live deploy into a locked session.
FileView loads async, so text() was still empty when onReadyChanged read it
once to decide whether this is a new session. It fell through to the unset
env var and persisted "" — the same bug, reached by a different route.
Verified on device: the stored signature now matches viewtop.instance.
HYPRLAND_INSTANCE_SIGNATURE is unset under viewtop, so this compared "" to
"" and isNewHyprlandInstance was false on every start. It gates
lock.launchOnStartup, which has therefore never fired once under viewtop,
and both Idle.qml copies read it as well.
viewtop publishes viewtop.instance beside its control socket — pid plus
startup nanos, different every start. Read that first, fall back to the env
var so a Hyprland session (the laptop is still one) keeps working, and
treat "neither" as the session continuing, which re-locks rather than
assuming a fresh boot.
Measured on the phone, idle, screen on, same session, one flag apart:
monitor-sensor --proximity --light --accel iio-sensor-proxy 15.3%
monitor-sensor --proximity --light iio-sensor-proxy 1.1%
Claiming the accelerometer makes iio-sensor-proxy poll the IIO device
continuously; nothing else here does. Fourteen points of a core, forever,
for the reading §4 weights least (+0.3) and calls a weak signal — and one
the keepalive had to actively decay, because monitor-sensor speaks only on
orientation change, so a stationary phone paid the full poll cost to
report nothing.
The parser, the motion decay and MOTION_WINDOW go with it; all three
existed only to turn orientation into a motion edge. The 2026-07-27
finding behind the decay is kept in comment — 165 Moving(true) against 8
Moving(false) on one boot — because whatever reports motion next owes a
decay too.
This does not say accel is unwanted. It says a subprocess holding a
continuous claim is the wrong way to get it; TASK-36's SLPI batching is
the right one. Restore the source together with that, or the cost returns.
Action::Overview shells out to `ipc call overview toggle`. The only handler
here was `search`, so that call reached nothing and the three-finger tap
did nothing, without an error anyone would see.
It has been invisible from the device because the shipped sessiond has no
`gesture` verb yet and refuses the op long before the executor runs, so
only the first half of the break was observable.
This makes the existing binding reach the surface it already names. What
the tap should raise is TASK-55 Q1 and is not decided here.
The panel, its mask and the loader all moved to overviewOpen ||
missionControlOpen when mission control landed; the Column holding them
did not. So the second-stage pill swipe raised a panel whose entire
contents were invisible — WindowOverview instantiated, laid out, and never
shown.
Search stays overview-only: mission control takes no keyboard focus on
purpose, so a field there is one you can see and cannot use. Outside-tap
now clears both states, because with no focus grab that tap is mission
control's only way back.
Drop temperature for models that removed sampling params (400 otherwise).
Preserve signed thinking blocks across tool rounds — Anthropic rejects a
tool_use whose preceding thinking block was dropped. Raise default
max_tokens to 32k for thinking-by-default models and request summarized
display so the reasoning pane is fed.