Final design: harness, account and checkout in the composer

2026-10-11 · implementer spec. Combines P2 (harness tabs, account cards), W1 (words: what on the left, where on the right) and I2 (icons: t3code two-row footer, ⋯ on phone). Frames are drawn at real app size (12px root, 760px max composer, 404px picker) and scaled to fit. Toggle light/dark top right.

1The header loses its checkout cell and its account pill. The checkout moves into the composer in every theme and width; the account becomes a badge on the harness logo.
2The picker separates harness from account. Harness is a segmented control (2–3) or a scrolling row (4+). The account, or OpenCode instance, is a card grid (2–3) or a scrolling row (4+). A section with one option is hidden.
3Words theme: model words on the left, then project / branch and context % on the right, on every width including phone.
4Icons theme: desktop gets a two-row box with a labelled footer; phone is one line with the logo and a ⋯ sheet.
5One checkout cell everywhere. GitIdentity's trigger (icon project / branch, dirty dot, ↑↓, rebase/merged, warning) is moved, not redrawn. It keeps its menu, including Move to worktree.

The segmented chip keeps Skills, Side threads and Settle. The checkout segment and the account pill (or OpenCode instance badge) are removed. Struck-through hatching marks what goes.

B · Shared parts: account badge and checkout cell

Both appear in the words row, the icons footer, the ⋯ sheet and the picker. Build each once.

C · Picker: every state

Desktop is the drop-up from the harness control; phone is the sheet. The content is identical apart from 50px rows on the phone. Model, Context, Effort and Agent rows are today's, unchanged.

D · Words composer: every state

Each word is a button that opens the picker at its own section. The checkout cell opens the checkout menu. Desktop width is measured on the composer, so opening the files pane can push it into the narrow tier.

E · Icons composer: every state

F · Implementer notes

TierTriggerWords rowIcons composer
Widenot compact, composer ≥ 600pxbadge · account · model · window · effort · mode | checkout · %two rows; footer has labels
Narrownot compact, composer < 600pxdrops the account word (the badge stays)two rows; harness, effort and mode lose their labels
PhonesessionViewState.compact (≤ 767px viewport)badge · short model · effort · mode | checkout · %one line: logo, ⋯, send
  1. Header (SessionBar.svelte identityBlock): remove <GitIdentity>, <SessionAccountPill> and <InstanceBadgeMenu> while a session is open. The composer is mounted for every open session, so there is no state where the header still needs them. SessionAccountPill.svelte then has no caller; delete it.
  2. Checkout cell: move GitIdentity.svelte under input/ and add presentation: "words" | "control" | "row". The trigger markup, truncation floors and menu stay as they are. Workspace hint and mismatch use the menu path the phone already has (warning icon in the cell, Move and Dismiss in the menu) at every width, which retires the desktop-only inline hint buttons. Test IDs: composer-checkout (trigger) and composer-checkout-menu, replacing session-bar-identity and session-bar-checkout.
  3. Drafts are unchanged. There is no checkout yet, so the composer renders no checkout cell and NewSessionContext stays where it is (above the composer on a phone, below it on desktop).
  4. Account badge: extend the driverIcon snippet in InstanceModelPicker.svelte to take the instance and draw the letter badge, replacing the isCustom accent badge. The colour comes from AccountDot's hash, so export its class lookup rather than copying it. Show the badge only when the driver has 2 or more instances. Gotcha: a session that never switched runs on the daemon's default claude instance, which is missing from the configured list. Treat it as an account card named "Claude" (the same fallback SessionAccountPill uses).
  5. Picker: delete the harness view and the picker-row-harness drill-in. The root gets two sections above the existing rows.
    Harness: SegmentedControl for 2–3 drivers, a scroll-snap row of 104px tabs for 4+. Test IDs picker-harness-{driver}.
    Account or Instance: a card grid for 2–3, a scroll-snap row of 108px cards for 4+. Test IDs picker-account-{instanceId}.
    Both rows scroll the selected item into view when the picker opens, and fade the right edge while more is hidden.
  6. Card behaviour. In a draft, a tap selects at once; that is today's handleInstanceSelect plus the project binding. In an open Claude session, a tap on another account runs AccountSwitch's flow: quotas are read fresh when the picker opens, then previewContinuationRpc → HandoffDialog → switchSessionAccount. Pull that flow out of AccountSwitch's menu so the cards can call it. Limited accounts and the current one can't be picked; an account whose quota can't be read can. In an open OpenCode session, instance cards are locked, as instances are today.
  7. Harness lock: in an open session, the other harness tabs show a lock and are aria-disabled, with today's copy "Harness is fixed for this session".
  8. Icons theme: on desktop, split the trigger into a harness control (composer-harness-trigger, which opens the picker at the top) and the model control (model-picker-trigger, which opens it at Model). Effort opens it at Effort. Mode is today's PermissionModeSelector. On a phone, ⋯ is composer-more and opens composer-more-sheet. ⋯ carries the amber dot while the checkout has a workspace hint or mismatch. A mode other than the default stays visible beside ⋯ as a tinted shield.
  9. Words theme: add composer-word-account after the badge (wide tier only), then the checkout cell, then the existing composer-word-context-usage at the far right. On a phone the model shortens (Sonnet 5 → S5) and the window word drops; 1M still shows, because it isn't the default.
  10. Verification: the composer is covered by features/*.feature, so run pnpm acceptance:visual, then recapture and review the baselines. Add a scenario for each of: 4+ accounts scroll, 4+ harnesses scroll, phone words row shows the project, phone ⋯ sheet, and a header with no checkout. Remember that new feature files must be added to the list in scripts/acceptance-visual.sh.