The island had never been load-tested. All three were fatal or noisy and
none were visible by reading.
StyledToolTip takes `text`, not `content`. Two occurrences, each one a
hard "Cannot assign to non-existent property" that failed the entire
module chain up to shell.qml — the bar would not have come back.
The provider-health tooltip could never have shown even once fixed: a
MaterialSymbol has no `hovered`, and our own StyledToolTip fix treats a
non-hoverable parent as never-show. That block exists specifically so an
unavailable provider is not mistaken for "no sessions", so a tooltip was
the wrong carrier. Stated visibly now.
Island read root.QsWindow.window through a non-optional access; inside a
Loader the attached object is not resolved at construction and it threw.
Revealer takes implicitHeight from childrenRect, so anchoring its child
to its own centre closes a cycle once it sits in a Loader. ii-base hid it
by letting the layout drive the Revealer height directly.
Verified: qs -c souveraine reaches "Configuration Loaded" with no warning
naming any of these files.
ii-base and ii-phone each carried a 357-line BarContent to express eight
differences, all of them either "is this widget shown" or "which slot is
it in". No behaviour differed. Every bar edit had to be made twice or
diverge silently against the pin.
Widgets are now declared once as Components in a registry; each slot is a
Repeater over a list of widget names, so placement and order are data.
Config names a slot or a device profile picks it. "auto" derives the
profile from the same cramped-ness test behind useShortenedForm, so the
phone keeps its arrangement with no config file. Loaders are active only
when placed, so an unplaced widget is never constructed.
The agent island becomes a placeable widget rather than a mount smuggled
into UtilButtons: it reaches every device bar from one declaration.
ClockWidget is unified for the same reason. Its phone fork existed solely
to add customFormat, which BarContent now sets on every device.
bar.agentSessions was landing under background.widgets, not bar, so
Config.options.bar.agentSessions was always undefined and only the ?? true
fallback made the island appear wired. Moved, and merged into the existing
bar.indicators rather than declaring a second one.
Collector emits a single JSON envelope for every agent session (Souveraine,
Claude Code, Codex) and always exits 0; a failing provider reports
available:false rather than taking the envelope down.
AgentSessions.qml owns the cadence and projects the envelope. Transport is not
the contract: when the TASK-69 daemon lands, only the source changes.
Island is a morph host for the bar - dot on a cramped bar, pill where there is
room, derived from the same threshold BarContent uses.
Inert until registered in services/qmldir and mounted; see patches/0005.
Keep assistant, reasoning, tool-call, and tool-return frames structured through the sidebar delegate. Tool cards bind returns by wire id and fold their output by default.
Reasoned from the SSE contract and existing TUI renderer. Diff check passed. Untested against a running shell: Casey applies and reloads staged patches.
A tool_call/tool_return arriving while a <think> block was open emitted its
own self-closing fence, whose </think> closed the outer block early. Every
reasoning token after that point rendered as prose, and the stray close left
the trailing assistant text mis-segmented. Append inside the open fence
instead; only open one when arriving in prose.
The composed config is symlinked into the repo and quickshell reloads on
write, so an agent editing QML edits the live shell mid-turn. Stage changes
as patches instead; the human applies and reloads on their own beat.
First in the queue: 0001-resume-offer — the agent-established hook offers
the latest thread instead of silently attaching it. Explicit resume paths
and the e4e6594 amnesia fix are untouched. Untested against a running
shell.
The reload killer: FileView under a QtObject root — QtObject has no
default property, so the whole service tree failed to compile. Every
working service singleton roots in Quickshell's Singleton type.
The vault card lives where notifications were — header with sync pill
(clean/ahead/behind/dirty), today's note entry, five freshest notes,
cold start and push rows — over the notification list. Lens.qml owns
souveraine-lens the way Face owns the rig: one socket, event lines up,
no vault access of its own. Kiyoshi's slash grammar and tool registry
are noted for the fuller app; the micro view only opens and plants.
PersistentProperties is in-process, so it carries the surface choice
through a reload and not through a fresh shell. sessiond locks before the
shell exists, so a greetd-started shell adopts a live lock with
Config.ready still false and binds the desktop surface — and
WlSessionLock.surfaceComponent cannot change once active. A lock screen
that will not take your PIN, on the iPhone 7 tonight.
Reads the environment, which answers at construction. Unset behaves
exactly as before, so nothing changes on blueline until it sets it.
Power is the one irreversible transition and it bypassed the authority
entirely: both menus called Session.poweroff() straight into systemctl, so
there was no Action, no trail entry and no refusal path.
The daemon gains the verb and the shell gains a transport for it. The
request rides its own short-lived connection, never the heartbeat, because
suspend can hold the daemon's handler until resume while the heartbeat
must stay free to carry locked_ack and the EOF that means shell death. A
disconnect after the request was sent reports outcome_unknown rather than
guessing.
Session.qml keeps the legacy executor until this is exercised on hardware.
Unbuilt and unrun; CI is the first reader.
78d6743 committed PowerMenu's half and never added the file, so the
singleton in qmldir resolved to nothing and the shell would not load on a
tree that had not been hand-linked.
HidController streams to usb-hid-inject and acknowledges every accepted
report, closes both endpoint fds on the busy edge before usb-signaller
tears the functions down, and refuses non-ASCII on the glass instead of
clearing a field whose first report was never written.
The face reports the agent and host results back to the page, and
deploy.sh stages the tracked face assets before the QML switch so a new
control cannot arrive without the page that implements it.
ZoneTransition owns the card rect, the four overview verbs, the end-target
table and the gesture clock. ZoneOverview and the rail read it; neither
derives a geometry.
Deletes poseActiveZone/clearPose/_posed, zoneCard.scale and
zonePullProgress. The card's ColumnLayout went too — its margin was a second
opinion about where a window sits in its zone; panes are the compositor's
tiling scaled.
The swipe felt wrong because the release committed where the thumb left it.
settleTo() travels to the destination first and commits on arrival, with
quickstep's numbers (350ms cap, min(1/0.7, 1/0.3)). Abandoned half-swipes
settle to last_zone instead of snapping. Dwell gate restored — quickstep
gates the same way and "ever paused" is a latch.
Card box is measured from the surface, not assumed from the screen: the
overview panel respects exclusive zones and its height moves.
Strand repro passes on hardware.
Two fixes to the same seam. columnLayout was still gated on
missionControlOpen, so a peek raised a panel whose contents were
invisible — the same line that had been left behind once before, with
its own comment saying so.
The card scaled 0.6 -> 1.0 while pose runs 1.0 -> 0.6 against the same
progress, so at the handoff the window was at 0.6 of the glass and the
card arrived near full-bleed. Same curve now, and the 26/12 inset is
gone: scale is the inset, and stacking both squashed the aspect because
the bottom label margin has no counterpart at the top.
Going home from a pull left the app scaled down until the pill was
dragged back to clear it. clearPose walked ViewtopControl.windows, which
is the 2 s poll's list, so a window that list no longer named never got
its 1.0 back. Track the ids actually posed and clear those too.
The overview loader was only active once missionControlOpen was already
true, so nothing existed during the pull and ZoneOverview.progress
following the rail was dead mid-drag. The cards appeared at release and
animated on their own clock — two motions, which is the seam TASK-60's
acceptance names.
missionPeek gates presentation only: visibility, mask, which body the
loader builds. Focus, dismissal and every commit path still read
missionControlOpen, so a preview draws and decides nothing. Masked to a
zero-size item while peeking so the in-flight gesture stays the rail's.
Dwell rather than any pull, so a flick to home never flashes the blur,
and it drops above the multitasking detent so the preview never shows a
destination the release would not commit.
A partial slide left the app scaled small until another pull brought it
back (DUMP-bugs 8). Every path anyone checked did call endPull, so the
hunt for the one that didn't was the wrong shape: while restoring is a
step, some path skips it. It is now a function of `dragging`.
Release gates on speed and length as one projected number instead of a
distance test with a flick promotion bolted on. That promotion had never
fired anyway — it read rail.tapSlop, which is undefined, so the test was
`travel >= undefined`. Every commit on this rail has been distance-only.
The drawer clears oskOpen on close and this did not, so the OSK outlived the
surface holding the only text field it was serving — and with nothing focused
there was no obvious way to put it away.
On the state, not on a visibility handler: the panel's `visible` is a binding
to that flag, so reacting to visibility would be reacting to our own effect.
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.