The tooltip claimed 'Archivist begins compacting around 70%'. The
archivist does not compact. It reads journal/ and writes
system/synthesized/ - a different substrate from the conversation
entirely (core/archivist/mod.rs:5).
The real advisory tiers are 80/90/95 in ConsciousnessEngine::
on_response, and they never force compaction. Icon thresholds moved
from 85/70 to 90/80 to match.
0007 was fixing a live error: the server has carried named ContextPressure
fields since r463, so event.tokens was undefined and the pill assignment
failed on every turn. Applied and verified against the running shell.
0010 renders a halt in her own register rather than the tool's name.
0011 renames the reset command to /new, which is what clearMessages() does.
Verified against a running shell rather than a clean load, which immediately
found two faults every prior gate had called clean. Both controls dropped from
0008 recorded as decisions so a later session does not restore them.
Both were live in the running shell and invisible to every check that ran
before it. Found by reading the shell's log after Casey applied.
ThinkingCard: missing `import qs.modules.common.functions`
ReferenceError: ColorUtils is not defined, on every render of every
reasoning segment. My own defect, shipped through a lint that said clean.
The lint was blind because I staged an approximation of the composed tree by
hand instead of linting the composed tree itself. Against the real composed
tree qmllint reports it plainly: 1 ColorUtils diagnostic before, 0 after.
Lint the composed tree. A hand-built stand-in tests the stand-in.
Config: sidebar.ai.fontSize restored
MessageTextBlock (vendor) reads Config.options.sidebar.ai.fontSize and got
undefined, assigning undefined to font.pixelSize on every text render.
Pre-existing, not from 0008 — the vendor delegate used MessageTextBlock too.
Cause is structural: modules/common/Config.qml SHADOWS ii-base's Config
wholesale, so every key ii-base adds after the fork silently disappears. The
key even carries an ii-base comment explaining it was added deliberately as
a live theme binding. Nothing failed; a number was just quietly absent.
Restored as a live binding, never a literal and never null
(null-in-a-serialized-config, 2026-08-11).
Verified: shell alive 12s after the Config edit (the crash-loop window),
config.json 46 keys and zero nulls, both fault lines gone from the log.
Applied 0009 then 0008 (0008's re-synthesize control calls a function 0009
adds). Casey applied and reloaded; verified against a running shell rather
than from a clean load.
0009 Speech.qml
stop() could not stop. playProc ran `sh -c "mpv ... || ffplay ..."` — a
compound command, so sh does not exec-replace itself and SIGTERM killed the
wrapper while the player kept sounding as an orphan. Reproduced directly.
That is the back-to-back TTS slam: every stop left audio playing and the
next speak started a second player over it. Shell dropped; the pid held is
now the pid making noise. ffplay fallback deleted rather than repaired —
needing a fallback is what forced the wrapper that broke the kill.
resynthesize() added, bypassing the cache: the button existed for "that came
out wrong" and, being keyed on the same text, always replayed the identical
file. synthesizing/playing split out of one `speaking` boolean.
0008 AiChat.qml
One line: delegate AiMessage -> AgentMessage. The vendor block named 8 tools;
the registry holds 19. The 11 it could not see were exactly the interiority
surface — outfit, nickname, subagent, atmosphere, reach, consult, itinerary,
todo, schedule, halt, intrusive.
regenerate and edit dropped deliberately, per Casey: text regeneration is not
possible (Ai.regenerate() already returned advice) and there is no in-place
edit (the vendor wrote to a local array the server never sees). delete is
armed and states that it hides locally only. Do not restore them.
Queue entries removed here, per the directory's own discipline.
The queue section claimed 0001-0004 were all unapplied. Verified with
git apply --check in both directions: 0003 landed as 8c434df, 0004 as
23cebc5, and 0002 can no longer apply at all.
0002 patched inThinkBlock in services/Ai.qml. That variable no longer
exists - typed segments made the think-fence collision impossible by
construction rather than by escaping harder. Retired, not abandoned.
0001 and 0006 were on disk and undescribed. Both now carry their
reasoning here. 0007's block records the specific installed version it
waits on, since 'needs the server first' is not actionable a month
later.
A queue is not an archive; git keeps history.
0008 was blocked because the owned delegate carried none of the vendor's
seven message controls. df11bba carries five and drops two deliberately;
the header now records which and why rather than only lifting the warning.
0009 depends on nothing but is a prerequisite for 0008's re-synthesize
control doing anything real.
Vendor AiMessage exposed seven controls; the owned delegate had none, which
made patch 0008 a parity regression rather than a swap. Five are carried, two
are deliberately dropped:
regenerate — the conversation is forward-only and Ai.regenerate() is already
a no-op returning advice. A button whose only behaviour is to explain that
it does nothing is worse than no button.
edit — there is no in-place edit. The vendor's wrote to a local array the
server never sees, so the message read back was not the message held.
delete is kept but armed: removeMessage() splices two local arrays and leaves
the server transcript untouched, so it is a view filter wearing a delete icon.
The armed row says so in words and replaces the controls in place rather than
opening a modal, which on a layer-shell surface would fight for focus.
speak and copy are separated at the source. copy takes the whole content;
speak takes text segments only, so the synthesizer no longer reads reasoning
and tool payloads aloud. Legacy messages without segments fall back to full
content rather than to silence.
re-synthesize prefers Speech.resynthesize() when present and degrades to the
old stop+speak otherwise, because speak() opens with a cache check on the text
and re-synthesis is by definition the same text — the legacy path replays the
identical broken audio.
deploy.sh: name the hazard the composer already implements — a new file
plus a manifest line is inert until the next deploy, while editing a file
already in the manifest is live in the running shell immediately.
Registration is the dangerous act, not creation.
patches/README: 0008 swaps the message delegate to one that does not carry
the vendor control row (regenerate, speak, re-synthesize, copy, edit, raw,
delete). Applying it would remove all seven.
TASK-72 step 2. AgentMessage consumes the typed segment list and dispatches
to owned primitives; ThinkingCard draws reasoning as a segment kind rather
than as <think> punctuation inside a flattened string, which removes the
think-fence collision class by construction.
MessageTextBlock and MessageCodeBlock are deliberately reused from the vendor
snapshot: they are markdown renderers, not agent vocabulary.
Composed by MANIFEST, imported by nothing. The activating edit is queued as
patch 0008. Qt6 qmllint: zero syntax findings; remaining warnings are the
widget-resolution class the vendor AiMessage also produces (38 vs 46).
Not verified: nothing has loaded this. No real tool round has rendered here.
First step of TASK-72's owned destination. modules/souveraine/agent/ is
original Souveraine code; no further agent-surface work lands in ii-base.
ToolVocabulary transcribes src/ui/chat/tool_renderers.rs — the same event
stream, solved once already for the TUI. All 19 registered tools have a kind,
an icon and a summary. The vendor card knew 8: bash read write edit grep glob
list_dir memory. The 11 it could not name are the interiority verbs.
ToolCard groups by what the act is rather than which subsystem serves it, so
acts on the machine and acts on herself read as different families. Status is
legible without opening the payload; collapsed by default except running or
failed.
Composed by the manifest, imported by nothing. Registration is a later step.
Authored in a parallel session (Rook, Codex side) on 2026-08-12; committed
here because untracked is the fragile state, not because authorship is mine.
Finding stands on its own: StepUpAuth called souveraine-pam-auth, a binary
never written, and fell back to pkcheck against an action never shipped, so
every grant request was silently denied. Unapplied, unloaded.
Null-valued options serialised into config.json and segfaulted
JsonAdapter on the next launch, killing the shell after load.
Tristate strings (auto/on/off) with boolean fallback.
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.