Merge master 0fdadce5189dcf13a00c2ed8293be95b9bb29c2c into the reviewed
Session V4 integration base. Preserve its V4 persistence and direct-catalog
subagent navigation while carrying forward current UI controls, decorative
icons, Desktop signing/runtime changes, and upstream tests.
Resolve lineage conflicts by retaining interaction-time catalog authority,
known-leaf handling and row order together with the shared status icons.
Keep both Desktop shell-routing regressions and one production route; master
now includes the route repair previously carried by this integration branch.
The full build, 41 documentation gates, two Desktop shell tests and 34 focused
Client lineage tests pass. Further built Web and packaging checks precede
publication. Preserve this pinned merge checkpoint before taking a newer
master tip, and propagate it through the reorganized feature stack.
Merge master 62b700bba7bb71e66d2d5a3b850ba8f4dd0fd6b4 into release/session-log-v4 at 4825bc01a999f564e79172a6af28e34fc757c540. Preserve this pinned integration checkpoint before any later upstream movement so the dependent session-format repairs have an immutable rebase base.
Keep release projection-backed child catalogs, independent Session references, exact fork behavior, V4 writer, and existing migration data. Adapt the incoming subagent sidebar chat to refreshProjections rather than the removed dedicated catalog API, preserve title projections in complete projection fixtures, and retain both sidebar actions and release lineage/status behavior. Remove the duplicate SessionProjectionMap import introduced by automatic merging.
Preserve both disjoint archive seals without changing any existing hash. Regenerate current Cordis and documentation catalogs from the combined source, retaining release session methods plus master slot factories. The frozen V0-V3 runtime paths are unchanged. Initial validation passed the full build, 93 focused Client tests, and all 1905 archived artifact seals; remaining integration and snapshot checks follow on this checkpoint.
- The model seat decides before consuming: a focused control that is not a row
(a retry button) keeps the browser's traversal, and Tab enters the list only
from the trigger.
- A launcher-opened menu is a fresh intent, so the dismissal that closed the
same token earlier no longer silences it.
- Comments and JSDoc now match the implementation: the near-end entry resumes
from the walk's last index, Shift+Tab leaves whenever the menu is open, and
the anchor hand-back names the control that opened the menu.
- The e2e focus assertions all go through `expect.poll`.
- Regression coverage for a rowless pane (the trigger keeps the keyboard) and
for a focused retry keeping its Tab, in the seat and the popup shell.
- The PR records the two public-surface changes (`SessionInput.focus()` in,
`bindComposerFocus` out) and the row-fill focus decision.
Review follow-ups on the hand-offs this branch added:
- Menu gives the keyboard back to the control that opened it (an anchor may
wrap several, as a split button does), falls back to a disabled-free anchor
button, leaves a Tab it cannot use to the browser, and no longer moves focus
while the owner keeps the list open.
- The model seat settles only real rows with Tab and keeps the keyboard on its
trigger when a pane's rows are disabled or absent.
- The popup shell leaves Tab to the browser when nothing can be settled (the
error strip's retry button stays reachable), and a dismissal caused by a
stale catalog returns the keyboard like the other paths.
- A frozen-tier track is the submit gesture's own bookkeeping and no longer
re-arms a menu the user dismissed.
- The launcher reuses the shared caret-preserving focus helper; the option
contract records that `active` is the row an accept gesture confirms; the
conversation README documents the composer's key and focus behavior.
The dialog/menu Escape layering this branch also carried is out of scope here
and has been removed again.
The two split the other way around: the composer's triggers keep the focus
ring their design gives them (a 2px `--dsw-alias-border-l3` ring), while a
walked menu row shows the hover fill alone, without the browser's default
outline doubling it.
The popup select panel and the model seat hold focus while open, and their Tab
fell through to the browser's traversal, leaving an open surface that only a
pointer press clears. Both now settle on Enter or Tab, leave on Escape or
Shift+Tab, walk with the arrows, and park on the value in use: the panel's
highlight on the row its options mark current, the seat's drilled pane on its
checked row, with the keyboard handed back to the cell that drilled in when the
pane is left.
Dismissal rides the conversation's own focus(), so the caret returns where the
draft left it instead of at the start.
Takes the merged slash-command localization (#3557): its zh description
copy replaces this branch's, its contribution description() callback and
HOST_DESCRIPTION_KEYS are superseded by the label/description/icon
contribution face and presentation.ts, and its zh menu golden and
locale-switch test are refreshed for the sectioned menu.
Dislike and a bare /feedback open the Session's feedback dialog, which
collects one of seven fixed categories and an optional description and
records through the messageFeedback put or the new sessionFeedback
Remote; Like records at once. A recorded rating shows the filled glyph
and every submission is acknowledged by a toast. feedback/record and
MessageFeedbackItem carry the optional category; the note popover, its
layout scenario, and the clearNote/clear verbs are removed. ui-commands
gains the action command kind the /feedback decoration runs as.
Closes#3515
The + button and a typed / open one menu with an Add section (File, Goal,
Plan, Feedback) and a Commands section (Compact, Permission, Model, Export)
in usage order; rows carry a glyph, a localized title and description, and
the command name as an alias where the localized title differs. Built-in
Host rows get their face from the ui-commands dictionaries when the catalog
description is the canonical English text. A menu pick fills the locale's
claim token (/计划 ) while the submission executes the catalog name, and a
typed localized token resolves on Space and Enter in every locale. The File
row is a new action contribution kind that opens the composer's file picker
through the scoped slash/input-pick-files event; the paperclip button is
removed and the + button reads "Add files or run commands".
StatsPills buttons carry explicit aria-labels separating segments with
' · '; a log with no timed figure renders the counts pill as a static
reading instead of a button opening an empty dialog; the pills row owns
one exclusive open slot so sibling dialogs never stack. ContextMeter
restores the '~' approximation prefix (usedTokens prefers heuristic
projectedTokens). ModelSelect's jscpd:ignore now names the real gap
(useAnchoredPosition places from the left edge only). The pills Agent
Note's stale facts (locale keys, context occupancy, golden ownership)
are rewritten in place, en+zh. plan-review.e2e.ts parks the pointer
after Approve so the ContextMeter tooltip cannot leak into the aria
captures; affected goldens refreshed.
Master archived three Agent Notes this branch had fact-updated (archived
copies restored to their frozen master content; the pills note now links
the archived path), extended the steer-all placeholder to continuable
children (spec and subagent e2e take master's behavior with this branch's
reworded copy), and dropped the session-sharing sentence from feedback
acks (goldens re-refreshed with the new placeholder).
The A/B winner over the single-line StatsLine: a gauge pill (counts + TPS,
opening the session-stats dialog) and a database pill (billed total +
cache-hit share, opening the exact token-usage dialog), sharing a new
stat-dialog module. StatsLine and its dead locale keys are deleted, the
gauge icon is optically centered, and the composer keeps its 4px clearance
through the data-composer-stats contract. Agent Note added; stale
StatsLine references across implemented notes updated in place.
ModelSelect renders its dropdown through a body portal with viewport-clamped
fixed placement, so the panel keeps its full width instead of being cut by the
sidebar edge. The preset seat passes Menu an anchor class with an icon-only
min-width floor and moves its label ellipsis onto a dedicated span, keeping the
chip's overflow degradation breakpoint-free. Toasts move to the viewport top
and shrink to fit their text.
The composer hairline moves one step up from the menus' l1; the seven
menu-fill dropdown cards grow from 16px to 20px corners. Notes and the
token comment record the new layering.
Apply global visual polish across the web client: corner-shape:
superellipse(1.5) with corner-shape: round pairing for full circles,
elevation tokens that draw 0.5px stroke outlines inside box-shadow for
floating surfaces, 0.5px hairline borders and divider lines for
neutral-token strokes, and tuned stroke contrast plus larger radii for
menus, settings panels, and cards. Stylesheet-scan specs in ui-theme
reject unpaired circles, border+shadow mixes, and 1px neutral hairlines
repo-wide.
Closes#3287