Four rounds of structural rework on the conversation surface, converging
on one registration model for the whole client:
- Review fixes: open() leaves the inject factory (SessionsService owns
the semantic); ConversationService mounts via ctx.plugin(); the
bespoke view registry retires into the 'conversation.view' list slot.
- Ring alignment: createChatView factory retired (components get
everything through checkable shares at the register call site); the
hand-rolled t/i18n threading is deleted wholesale — a future
framework-level i18n will supply t as a standard prop keyed by slot
name, so no interim manual channel.
- Toolview dissolution: ToolViewRegistry / ToolViewResolver /
ToolViewOutlet / ctx.toolviews retire. Tool rows are entries of the
'conversation.chat.toolview' keyed slot (scope: session) declared by
the chat entry; ToolRowOwnerProps is the unified owner payload;
GenericToolCard becomes the call-site fallback; registrants are plain
plugins (inject ['slots','conversation'] as the load-order seam);
session-dimension dispatch moves into components (useSessions reads
parentId); trajectory/waterfall gain same-shape slots the day they
render tool rows (RendersCheck rejects empty declarations). Slot
names mirror the composition path (<domain>.<entry>.<hole>).
- Staging follows current: cell()/binding() are pure resolution
(render-safe); the constructor subscribes to the list store and
followCurrent opens the event window when the current session
changes — staging IS the open signal, business verbs are the timing,
React render/commit is decoupled from window lifecycle. A masked
current (projection gap) keeps the stage untouched so deferred
teardown semantics survive reconnects.
Agent Note: .agents/notes/implemented/architecture/
2026-07-23-toolview-dissolution.md (bilingual pair) records the
decision, the four rejected alternatives, and the accepted semantic
changes; the web client architecture note and packages/client/AGENTS.md
carry the current-state narrative.
Verified: typecheck 0, duplication 0 clones (478 files), full coverage
run 6190 passed with zero threshold errors, knip 0, doc-sync 24/24,
client aggregate tsc 0, render-count checks (one commit per chunk, zero
row re-renders under streaming) green.
Fixed sidebar width (never concedes), figma session-row rail (16px twist +
status slots, triangle arrows, mount fade), exact brand wordmark svg with
tooltip'd rail controls, form controls inheriting the app font stack, and
inlined the twist button reset since the tsdown CSS pipeline drops composes.
The cwd-variants test asserted footer text right after setup()'s single
tick. On slow Windows runners the TUI has only written the color-scheme
query and hideCursor by then — no first frame — so the /opt assertion
failed (PR #498, windows coverage lane). Wait for each first-frame
assertion with vi.waitFor, the pattern 7fddeb446 and df38095a1 already
use in this file.
demo:web and the README Web UI instructions ran only build:web (the Vite
frontend shell), never the root build that emits each web-client plugin's
lib/client.js. On a clean checkout every /plugins/<id>/client.js 404s and
the client loader shows "Failed to load plugins".
Run pnpm run build before build:web in both the demo:web script and the
README instructions for the installed ~/.dsh/source checkout.