The merge staged the `--ours` consistency records before the merged prose was
re-recorded, so the committed hashes still describe the pre-merge content and
`verify-translation-pairing` rejects the pair. Re-record all three against
what actually merged.
The composer ring, percentage, and `~used / capacity` header read
`contextPressure.pressureTokens`, which moves only when a request reports
usage. Compaction reports none — compact-basic summarizes through a direct
`ctx.llm.stream()` call and appends only its own `compact/*` records plus the
replacement `user/message` — so the meter was frozen across the one action
taken to change it. Driving a real `compactNow` through the agent loop:
BEFORE compact: ring=4% header=~4227/100000 rows=[18, 0, 4365]
AFTER compact: ring=4% header=~4227/100000 rows=[18, 0, 286]
The composition rows fell 93%; the ring did not move, and would not until an
entire further turn completed. The panel then contradicted itself by more than
an order of magnitude at exactly the moment a reader opens it.
`contextPressure` now also publishes `projectedTokens`: the provider sample
plus the heuristic repricing of everything the surface gained or lost since
that sample, clamped at zero, folded through the shared `surface-fold.ts`. The
sample is stamped before the same event joins the surface, so an
`assistant/message` anchors against the surface its own request carried. Only
the delta is estimated, so the figure stays provider-anchored — the estimator's
CJK and JSON-schema underpricing stays out of the occupancy number — while
reacting the moment content lands or a span is shadowed. Same run after:
BEFORE compact: ring=4% header=~4323/100000 (pressure=4227, projected=4323)
AFTER compact: ring=0% header=~ 244/100000 (pressure=4227, projected= 244)
`contextOccupancy` prefers the projected figure and falls back to the bare
sample, so a projection restored from a pre-field checkpoint degrades to the
old behavior rather than disappearing. `stateVersion` moves to 3.
Assistant footers and the stats line gain TTFT/tok-per-second readings
folded from step timings; context occupancy moves off the stats line onto
a composer ring whose panel shows a heuristic system/tools/messages
breakdown from the new token-meter contextBreakdown session projection.
The composer seat is one node laid out against two different edges: in Chat it
is a sticky child of the column's scroller and rides its content box, which a
space-consuming scrollbar shortens, while a view declaring a composer overlay
gets an absolutely positioned seat against the padding box, which no bar
reduces. With the transcript scrolling the two tabs disagreed by the bar's
width, so the centred input card moved 4px sideways on every switch — and the
same 4px inside Chat when a growing transcript began to scroll.
The column now reserves its scrollbar gutter unconditionally and states the
overlay branch a scroll container on the same axes, so both states measure
against the same width.
The READMEs of ui-primitives and ui-conversation still documented the
removed maxSources prop, DEFAULT_WEB_MAX_SOURCES, CHAT_WEB_MAX_SOURCES,
and the head/tail collapse; the 2026-07-30 frontend note described the
same as current behavior. Restate all of them as the fixed-height scroll
container and cross-link the two notes both ways.
Bound the model-visible/card-visible correspondence: spill-policy's
tools/post-execute replaces an oversized result's content while leaving
presentationMeta whole, so the card's contract is the view it receives,
not the model's context.
Narrow the unit test's name and header comment to what jsdom establishes
(one <ol>, no expand control); jsdom does not resolve CSS Modules layout,
so the scroll geometry belongs to an assembled browser case.
Address review of the ToolRow card refactor:
- ToolRow renders a visually-hidden run-state label (row.running/failed/stopped
locale keys) so a running/failed/interrupted row is not a colour-only signal;
the StateDot and the sweep are both aria-hidden. Restores the stopped-state
text assertion in diff-card.spec at the ToolRow layer.
- The file-link button stops Enter/Space from bubbling to DisclosureRow's
whole-row keydown, which would preventDefault the key and toggle expand
instead of opening the file (the keyboard analogue of the click stopPropagation).
- search-row passes output={model.output} unconditionally, matching the other
three card rows (ToolRow renders the card over the output when present).
- Assert locale: 'conversation' in the search/web/diff registration tests.
- Add the read render-intent paragraph to the ui-conversation README and a
ReadBlock atom entry + Read section to ui-primitives (both languages).
- Update the grep fixture sample lines to the post-refactor SearchRow shape.
Refresh the code-mode-round, steering, and search-card goldens for the added
status text and updated sample lines.