Consume the card:'read' result view (path, numbered lines, totalLines, lang)
the read backend PR added. ReadBlock (ui-primitives) draws a per-line gutter
with each line's own file number, shiki highlighting via a new highlightLines
returning per-line token arrays, a 显示 X / Y 行 window note, a height cap
matching TerminalBlock, and a copy control. read-card-model is the single
resultView derivation; a keyed ReadRow registers under read with the card
resident under its path-link summary. The generic fallback and the details
panel are read-aware. Fixture gains a windowed read turn for the built-boot
snapshot.
The answered-transcript golden captured the OS's own refusal of the denied
first attempt — "bash: notes.txt: Operation not permitted" on macOS against
"bash: line 1: notes.txt: Read-only file system" on Linux — so it passed
locally and failed the Linux snapshot lane.
The scenario now keeps one golden (the waiting panel, platform-neutral) and
asserts the answered state on the world instead: the decided outcome, the file
the escalated command actually wrote, DONE, the panel gone, and the composer
re-enabled. The file assertion is stronger evidence than the transcript dump
it replaces — it proves the grant reached the executor.
Replace the separate '已截断 · 共 N' pill with '显示 X / 共 N 处匹配 · K 个文件'
(and '显示 X / 共 N 个路径' for glob), mirroring the read card's '显示 X / Y 行',
so the retained count and the pre-cap total read as one clause instead of two
numbers that appear to disagree.
- .loadingFloat moved after the .status/.error block (its padding was
losing the same-specificity race) and re-anchored bottom-right: the
truncated/error rows own the bottom left and keep rendering through a
scan, so the pill can never cover them; confirmCreate's relist now
clears the stale failure text like every other scan launch.
- The close edge resets loading, so the slow-scan effect disarms while
hidden and a reopened dialog waits out a fresh silence window
(regression test added).
- The truncated note's survival through a scan is now asserted in the
slow-scan test; the wait-bound test moved to fake timers with the
200ms bound explicit.
- select()'s exemption from the one-frame rule and the constants' local
calibration premise are recorded in JSDoc and the capability-seam
Agent Note; the themed-scrollbars note's rebinding enumeration is
replaced by a pointer to the mechanical gate (it had drifted twice).
Both pairs re-recorded.
The loading pill's layer-2 background made the sheet an
elevated-surface painter, and the ui-theme scrollbar invariant rightly
flagged what was already latent: the dialog's columns scroll on an l2
card while the thumbs rendered in the base-surface pair. Rebind the
indirection on the card rule so it inherits to the scrolling columns.
Navigations keep the previous view rendering while scanning: target and
parent legs land as one two-pane frame when the parent leg settles within
a 200ms wait bound (past it the target lands alone and the late leg
upgrades in place; Escape inside the landing window withdraws the
navigation). The loading indicator floats over the content on the card
background and appears only once a scan outlives a 300ms silence window,
so navigation never shifts the columns or flashes an intermediate frame.
The truncated note now describes the on-screen panes instead of hiding
during scans.
Review round: the no-caption rule the Agent Note states applied only to the
success texts, leaving `permission · unknown permission preset "bogus" (…)`.
The error text now reads `unknown preset "bogus" (…)` and its exact wording
is pinned; the fixture mirror drops `JSON.stringify` for the host's own
quoting so the two cannot drift on a quoted argument.
The Note now states the line it draws: the rule bans a caption for the
command's own value, not the vocabulary, so `/plan`'s `Plan mode off.` and
`/goal`'s `Goal cleared.` conform as written — recorded with the broader
name-ban as a rejected alternative. The web row assertions are scoped to the
row so unrelated page text reading `permission` cannot satisfy them.
The re-exports for SearchResultView shift line numbers in packages/core/tools;
regenerate the generated docs the static gate checks (cordis catalog was already
regenerated with the feature commit).
The approval panel replaces the InputBar while a sandbox escalation waits,
and its justification and command are unbounded model text. With no height
cap, a long command grew the card until the refuse/allow row went under the
fold: at 900x700 the action row's bottom landed at y=749, so the user could
read the request and not answer it.
Justification and command now scroll in one region capped at the same height
as the composer's draft area, with the amber strip and the action row outside
it. The cap is one value with two consumers — declared as
--dsh-composer-text-max-height on ConversationRoot's .composerSeat, the
composer chain's only shared ancestor — so the seat cannot cap its two states
differently. The card rebinds the l2 scrollbar pair like every other scrolling
surface on an elevated background.
Covered by a new web e2e scenario that drives the real composition (read-only
session, denied write, the model's escalation retry, answer clicked through
the panel) and measures the live panel at two viewport heights against the
composer's own cap, read off the textarea rather than hardcoded.
Consume the card:'web' result view (structured sources + answer for search, the
URL and HTTP status for fetch) the web backend PR added. WebBlock
(ui-primitives) draws both kinds via the kind discriminant: a citation list of
safe external links (the MarkdownText protocol allowlist, title-or-hostname
label), a truncation indicator, a height cap matching TerminalBlock; a fetch
summary for the other kind. web-card-model is the single resultView derivation;
a keyed WebRow registers under web_search and web_fetch with the card resident
under its summary. The generic fallback and the details panel are web-aware.
Fixture gains web_search and web_fetch turns for the built-boot snapshot.