Surfaced messages, round 3: 21 designs

Round 3 builds on the two round-2 favourites, B3 · Numbered pins and slot and M2 · Scrub strip, and adds five capabilities: a model-written recap of the journey, read and unread, importance feedback, replying to specific messages, and user-managed categories. Each design shows the same long turn six ways: desktop Live and Settled, phone Live and Settled, then the settings on desktop and phone. Phone screens are a fixed 760px tall, so vertical cost is honest. Decision bead: conduit-test-ahu8.8. With the feature off, every design falls back to today's view.

New in round 3 (all necessary)

1 · Recap the journeyA model, chosen in settings, describes the story of the updates so the developer is up to date. Every message it cites is a button that opens the real text.
2 · Read and unreadMessages can be marked read and unread.
3 · Importance feedbackMark a message more or less important than Jev said. Marks are written to a plain file that you, or an agent, can use to improve the categories.
4 · Reply to a messageA button on each message drops a reference into the composer that the agent understands. Several references can sit between free text before you press send.
5 · CategoriesAdjust each category's weight, add your own, retire and restore conduit's, and edit or delete yours.
ConsistencyDesktop and mobile may differ, but the same feature should look and work the same way where it can.

Carried from round 2 (still necessary)

CollapsibleSurfaced messages can be folded away.
Short threadsA long turn with many messages must not become a long thread.
One activity blockNever split a turn's activity into several blocks.
Easy to readLegible text, full text one tap away, clear type labels.
MobileReadable on a phone, without making the reply, activity or composer hard to read.
Dev navigationFast to find a specific message, especially the ones needing a reply.

Categories, weights and levels

The 9 built-in categories were mined from 500 mid-turn messages across the 20 largest conduit sessions. Long turns surface a median of 22 messages, p90 56, and about 150 in the worst turn found.

TypeSurface when the message…UrgencyShareExample from the logs
? Askasks the user for a choice, fact, approval or action1 · needs a reply4.8%"Want me to file that follow-up in Beads?"
✕ Blockedreports a failed gate, missing access or stop condition that prevents the next step2 · attention7.0%"Commit was blocked by the pre-commit hook (HEAD unchanged, files still staged)."
! Risknames an unresolved plausible harm, regression or hazard2 · attention8.8%"If that switch was ask→auto, a restart quietly turns on auto-approval."
± Scopesays the agreed deliverables must grow or shrink2 · attention1.4%"This story spans contracts → daemon → transport → stores…"
↻ Replanchanges method or order because of new evidence3 · consequential1.4%"That changes the design, and I want to make sure it doesn't cost you live streaming."
↶ Correctionretracts or materially revises an earlier claim3 · consequential3.4%"I owe you a correction: my "third derail" call was wrong."
◆ Decisioncommits to a consequential choice or trade-off on the user's behalf3 · consequential6.0%"Managed-policy keys render disabled with a "Locked by managed policy" label."
● Findingestablishes a new fact or root cause4 · informative15.2%"No ON DELETE CASCADE anywhere, and sessions.parent_id self-references sessions(id)."
✓ Milestonereports a completed deliverable or full gate4 · informative10.2%"All gates green: 523/523 unit test files, 6832 passed."
Check, Activitybounded check results; narration such as "Let me read the file"5 · stays in the log41.8%"Codex is still running. I'll pick this up when it reports back."

Each category has a weight (0–100). Prominence = weight × Jev p. The density level is a threshold: Key ≥ 70 · Balanced ≥ 40 (the default) · All · Final only (today's view). Default weights: Ask 100 · Blocked, Risk, Scope 80 · Replan, Correction, Decision 60 · Finding, Milestone 40. Feedback moves one message up or down one level and never silently changes a category's weight. Users can add categories (this scenario has one custom category, ⚡ Perf, weight 70), and retire or restore built-ins.

The scenario (identical in every design)

User: "Move the session list onto the typed streaming transport and delete the legacy session_switched path." One long turn. Live: 38m 12s in, 30 surfaced. The user read up to #21 and stepped away, so 9 are unread. They are drafting a reply that references #24 and #18. Settled: 44m 30s, 34 surfaced, just back, with 13 unread. Earlier the user marked #1 ▲ more important ("changed the plan"), so it shows at Balanced. By level, Live shows Key 6 · Balanced 14 · All 30, and Settled shows Key 6 · Balanced 16 · All 34. Numbers are stable everywhere, and the Ask is always #24.

All 34 messages: number, time, type, p · prominence, level, text (S = settled only)
#101:42● Findingp 0.93 · 37Balanced ▲ yousession_switched has three consumers, not one: MessageList history, the sidebar unread counter and the push deep link. The plan only lists the first.
#202:20● Findingp 0.71 · 28AllMessageList reads history through loadHistoryPage, not the store.
#303:10± Scopep 0.88 · 70KeyThat makes this bigger than planned: the unread counter and the deep-link handler both have to move to subscribeSessionDetail too, or they break when the legacy path goes.
#404:05● Findingp 0.66 · 26AllThe unread counter already dedupes by session id, so the move is mostly wiring.
#505:30✓ Milestonep 0.80 · 32AllContracts for subscribeSessionDetail compile.
#606:55◆ Decisionp 0.84 · 50BalancedI'm keeping session_switched behind CONDUIT_LEGACY_SWITCH=1 for one release instead of deleting it, so a bad rollout can be reverted without a deploy.
#708:10● Findingp 0.69 · 28AllPush deep links are built in push-routing.ts, not the service worker.
#809:20✕ Blockedp 0.95 · 76Keypnpm check fails at ws-rpc.ts:1368: the ViewSession schema lives on feat/ni8-transport, which isn't merged. I can't finish the sidebar without it.
#909:48↻ Replanp 0.79 · 47BalancedChanging order: I'll rebase onto feat/ni8-transport locally and build on top, rather than stubbing the schema and redoing it later.
#1011:02● Findingp 0.62 · 25Allfeat/ni8-transport rebases cleanly; 3 trivial conflicts in ws-rpc.ts.
#1112:45◆ Decisionp 0.74 · 44BalancedKeeping ViewSession field names identical to the legacy payload so stores don't change shape.
#1214:30↶ Correctionp 0.81 · 49BalancedCorrection: the deep link doesn't read session_switched directly. It goes through history-logic.ts:178, so there's one consumer fewer than I said at 01:42.
#1316:20✕ Blockedp 0.91 · 73Keypnpm lint fails on 4 unused imports left by the rebase. Fixing before continuing.
#1417:30● Findingp 0.60 · 24AllThe sidebar store re-renders on every summary event; it needs a keyed update.
#1519:05! Riskp 0.90 · 72KeyThe unread counter now subscribes per session. With 293 sessions that's 293 open streams per tab on reconnect, which could exhaust the daemon's socket budget.
#1620:10✓ Milestonep 0.82 · 33AllSidebar store is on the typed stream. Its unit tests pass.
#1721:05↶ Correctionp 0.78 · 47BalancedThe keyed update I mentioned isn't needed: Svelte already skips unchanged rows.
#1822:40◆ Decisionp 0.72 · 43BalancedI'm batching unread subscriptions into one subscribeSessionSummaries stream instead of adding a cap, because a cap would silently drop badges.
#1924:00● Findingp 0.68 · 27AllReconnect replays summaries in session order, so badges settle in under 200ms.
#2025:40! Riskp 0.89 · 71KeyTwo open tabs double the summary stream. Acceptable for now; noting it for later.
#2127:15✓ Milestonep 0.86 · 34AllSidebar and MessageList are on the typed stream. 523/523 unit files pass, 6,832 tests green.
#2228:30● Findingp 0.63 · 25AllThe 2px shift only appears on the phone sidebar, not on desktop.
#2329:50✓ Milestonep 0.81 · 32AllUnread badges migrated. pnpm check clean.
#2431:02? Askp 0.97 · 97KeyThe phone sidebar baseline shifts 2px because the unread badge now paints before the first frame. Recapture it, or keep the old paint order? I'll carry on with the deep link meanwhile.
#2532:15◆ Decisionp 0.73 · 44BalancedCarrying on with the deep link while I wait on your phone-sidebar answer.
#2633:40● Findingp 0.64 · 26AllThe push deep link opens the session before its history loads. That's why it flashed empty on iOS before this change too.
#2734:50● Findingp 0.61 · 24AlliOS Safari fires visibilitychange before pageshow, which is why the deep link raced.
#2835:30✓ Milestonep 0.84 · 34AllThe deep link now waits for history. The empty flash is gone in the iOS simulator.
#2936:10● Findingp 0.65 · 26AllThe sidebar's Storybook stories needed the typed fixture; 3 stories updated.
#3036:55✓ Milestonep 0.83 · 33AllDeep link migrated. pnpm acceptance:visual is green except phone-sidebar, which is waiting on your answer.
#3138:40● Findingp 0.67 · 27AllThe legacy handler has no callers left when the flag is off.
#3240:20◆ Decisionp 0.77 · 46BalancedLeaving phone-sidebar unrecaptured until you answer. Everything else is committed on feat/typed-session-list.
#3341:30◆ Decisionp 0.75 · 45BalancedDefaulting CONDUIT_LEGACY_SWITCH to off, since you want it deleted next release.
#3443:50✓ Milestonep 0.88 · 35AllBranch pushed. The legacy path is off by default and can be removed next release.

Best practice

Conventional patterns from mature products (mail, Slack, GitHub review, Linear, command palettes, AI summaries), applied to B3 and M2.

Best practice

B1 · Action row

Every message in the slot carries the same labelled row of verbs (↩ Reply · ▲ More · ▼ Less · ○ Mark unread · ⋯), the recap is page 0 of the slot behind a ✦ Story pin, and the categories live in one plain settings table.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running pnpm acceptance:visual↓ live
? #24 Ask Recapture it, the 2px shift is intended.
◆ #18 Decision Agree, but log the stream count on reconnect so we can see it.
Then carry on.
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
44m 30s · $6.80

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Phone · Live9 unread · slot on #25
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running pnpm acceptance:visual↓ live
? #24 Ask Recapture it, the 2px shift is intended.
◆ #18 Decision Agree, but log the stream count on reconnect so we can see it.
Then carry on.
opus-5
Phone · Settled13 unread · Story open
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
44m 30s · $6.80

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Desktop · SettingsSettings › Surfacing · Categories
Phone · SettingsSame page, cards instead of a table
9:415G ▮▮▮
‹Settings › Surfacing
Builds on
B3's pin lane, clustering, loupe and slot, unchanged in mechanics. Added: a permanent labelled action row at the foot of the slot, a ✦ Story pin in a gutter before the lane, unread dots on pins, and the level switch moved from the slot foot onto the axis line to pay for the new row.
Placement
All inside the one activity card. Unread count and "Mark all read" sit at the right of the head line. The pin lane (with ✦ Story first), strip and axis come next, then the slot, then the action row inside the slot. Final reply and composer follow the card as today.
Live
The slot follows the newest pin (#30) until you pick another. New pins arrive with an unread dot. The composer already holds the canonical two-reference draft.
Settled
The slot opens on page 0, the ✦ Story, scoped to "Since you last read · 13". The toggle switches it to the whole-turn recap. #1 carries a ▲ on its pin and a "▲ you" tag in the slot.
Recap
Page 0 of the slot, reached by the ✦ Story pin or ‹ from the first message. Generated on request (default) by sonnet-5 and cached per scope. Each #n chip jumps the slot and caret to that pin. If the message is below the current level it is shown with a dashed pin and a "Shown from All" tag, without changing the level. Settings › Surfacing › Recap has model, when, scope, length and cost.
Read-state
A message becomes read after 2s in the open slot (the "Unread" tag fills as a progress bar). Unread pins carry a filled white dot, groups carry one if any member is unread. "○ Mark unread" in the row puts it back and pauses the dwell for that message. The head shows "9 unread · Mark all read". Reading the Story does not mark messages read. It offers "✓ Mark 13 read" instead.
Importance-feedback
▲ More and ▼ Less toggle one level of override on the message. The pin gains a ▲ or ▼, the slot gets a "▲ you" tag, and the pin moves in or out of the current level at once. A toast confirms the line written to feedback.jsonl with Undo. ⋯ › Retype writes a retype line. No reason is asked for. The Feedback tab counts the marks, shows the file, and hands it to an agent on request.
Reply-to
↩ Reply drops a coloured .ref token at the composer's last caret position (or the end), with a space after it and the caret placed after that, so you just keep typing. Reply again elsewhere for a second token. Remove with × or Backspace. On send the agent gets the "Re #24 (Ask, 31:02): …" blocks from the scenario.
Categories
Settings › Surfacing › Categories is a table: glyph and name, the "Surface when the message…" definition, a weight slider with its number, matches this turn, and Retire (built-ins, plus Reset once moved) or Edit/Delete (custom). Retired built-ins drop to a Retired section with Restore. "+ New category" appends an editable row. Weights and retirement re-level the pins in the frames above live. The slot's ⋯ › Category settings jumps here and highlights the row.
Collapsible
▾ folds the slot to a one-line peek, which hides the action row too. The lane and strip stay, because they are the activity strip. Final hides pins and slot. Older turns open folded.
Short-session-threads
Desktop: head 1, lane, strip and axis 2, slot head 1, text 3, action row 1 ≈ 8 lines. That is one more than B3 (the row), with the levels folded into the axis to hold the rest. Phone ≈ 14 lines, because the row is 46px of icon+label buttons. Folded: 4. Always one card.
Easy-to-read
The full message at 13.5px, never clamped. Every verb has a word next to its glyph, so there is nothing to learn or hover. Tags are plain words ("Unread", "Waiting on you", "▲ you").
Mobile-friendly
The same row as five 46px icon-over-label buttons across the slot. The unread chip drops to its own line under the head, because the phone head has no room. Pins stay at B3's 24px, with the loupe for dense stretches. Level, pager and fold buttons are 32px.
Consistency
Identical: the verbs, their order, glyphs and behaviour, the Story pin, the dot, the tags and the settings tabs. Diverges only in layout: on phone the row stacks icons over short labels ("Unread" for "Mark unread", "Options" for ⋯), the unread chip wraps to its own line, and the settings table becomes one card per category. All three are width, not behaviour.
Dev-Navigation-Ease-And-Speed
#7 is at All, so type 7 ↵ (or tap its group, then the loupe): 1 to 2 actions. The Ask: the "? #24" chip in the slot head, or a: 1. Next unread has no dedicated key, which is this design's weak spot: tap the next dotted pin, or open the Story and tap a chip. 1 to 2 taps when visible.
At-150
The lane becomes about 20 group pills, dotted when they hold unread messages. The head count stays exact ("97 unread"). The Story still summarises only the unread stretch, with its footer saying how many it covers. References show just "#n Type", so they stay short. There is no "next unread" jump, so 150 hurts more here than in B2.
Build-cost
M. On top of B3: a read-state table (message id × user), a feedback.jsonl appender, one recap call per scope (cached), contenteditable tokens with serialisation, and a settings page over categories.json.
Trade-off
The row is the most discoverable of all the options, and it costs every turn a permanent line of chrome, even when you only want to read. Auto-read on a 2s dwell marks things read that you only paged past. Like B3, it reads one message at a time.
Collapsible●●●●○Short threads●●●○○Easy to read●●●●○Mobile●●●○○Consistency●●●●●Dev nav●●●○○Recap●●●●○Read state●●●○○Feedback loop●●●○○Reply-to●●●●○Categories●●●●○
Best practice

B2 · Keyboard triage

Surfaced messages are an inbox you clear with one hand: j/k to step, e to mark read and jump to the next unread, + and − to rank, r to reply, with swipes doing the same on a phone and a "✦ Catch up" recap before you start.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running pnpm acceptance:visual@ to cite
? #24 Ask Recapture it, the 2px shift is intended.
◆ #18 Decision Agree, but log the stream count on reconnect so we can see it.
Then carry on.
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
44m 30s · $6.80

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Phone · Live9 unread · triaging, swipe the card
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running acceptance:visual
? #24 Ask Recapture it, the 2px shift is intended.
◆ #18 Decision Agree, but log the stream count on reconnect so we can see it.
Then carry on.
opus-5
Phone · Settled13 unread · Catch up first
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
44m 30s · $6.80

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Desktop · SettingsSettings › Surfacing · Categories (focus the list, use the keys)
Phone · SettingsSame list, tap targets instead of keys
9:415G ▮▮▮
‹Settings › Surfacing
Builds on
B3's pin lane, clustering, loupe and slot. The slot's foot becomes a triage bar of keycap buttons, and the lane gains an "Unread only" filter that turns it into a queue. Read state is explicit, like Superhuman or Gmail: nothing is marked read just because you looked at it.
Placement
One activity card. The head's right edge holds "9 unread · ✦ Catch up on 9 · Next ⇥". The lane, strip and axis (with levels) come next, then the slot with the triage bar and the "Unread only" switch in its foot. The composer shows an "@ to cite" hint.
Live
The frame shows you mid-triage: Unread only is on, so the lane shows just the nine unread pins, and the slot sits on #24 (3 of 9). New surfaced messages join the queue at the end with a dot. They never steal the slot.
Settled
With 13 unread, the slot opens on the Catch-up page first. Press e (or "Start triage") to walk the 13. The lane stays at Balanced with every pin, so #1's pin and its group carry the ▲.
Recap
"✦ Catch up on N" is page 0 of the slot and appears before triage whenever three or more are unread. c toggles it. Citations jump the slot to that message without changing the filter (a dashed pin if it is outside the level). Scope switches between "since you last read" and the whole turn. It is on request by default, costs ~$0.006 on sonnet-5, and has full controls in Settings › Recap.
Read-state
Only explicit. e marks read and moves to the next unread, u toggles unread, and n skips to the next unread without marking. On phone, swipe right to mark read and move on, swipe left to keep it unread and move on. Pins lose their dot and dim once read. The head count updates as you go, and "Mark all read" lives on the Catch-up page.
Importance-feedback
+ / − nudge a message one level up or down. The pin gets ▲/▼, the slot gets "▲ you", the lane re-levels, and a toast confirms the feedback.jsonl line with Undo. t opens a retype picker with number keys 1 to 0. The Feedback tab counts the marks, opens the file, and hands it to an agent on request.
Reply-to
r puts a token for the open message at the composer caret and focuses it. Typing @24 (or @ alone, which lists unread messages and the Ask first) opens an autocomplete. ↑/↓ and Enter/Tab turn it into the same token. Tokens are removed with × or Backspace. Several can sit anywhere in the text.
Categories
A focusable list: ↑/↓ select, ←/→ or −/+ change the weight by 5, x retires or restores, ⌫ deletes a custom one, e edits, n adds. Every key also has a visible button. Weights are plain numbers with a bar. "Reset to defaults" restores all built-ins. Changes re-level the lanes above immediately.
Collapsible
▾ folds the slot to a one-line peek, and the lane stays as the activity strip. Final hides pins and slot. Older turns open folded, and keys still work on a folded slot (j/k unfold it).
Short-session-threads
Desktop is about 8 lines: head, lane and strip, axis, slot head, 3 lines of text, triage bar. Phone is about 15, because the five 46px swipe-alternative buttons and the switch take room. Folded is 4.
Easy-to-read
Full message text at 13.5px. Keycaps sit next to words on desktop ("e Done ›"), so the shortcuts teach themselves. On phone they turn into icon-over-label buttons. ? shows the whole map.
Mobile-friendly
The slot is a swipe card with green "✓ Read" and grey "Keep ○" underlays revealed as you drag. The same verbs are 46px buttons under it, so swiping is never the only way. "Unread only" is a full-width 32px switch.
Consistency
Same verbs, order and states. Desktop keys and phone swipes map one-to-one onto the visible buttons. Diverges: keycaps become icons, t Retype and ? sit behind the ⋯ in the phone bar, and swipe exists only on touch. So a phone user doesn't learn the keys, and the keyboard user doesn't need the swipe.
Dev-Navigation-Ease-And-Speed
#7 (All): type 7 ↵, 1 action. Ask: a or the ? tag, 1 action. Next unread: n or e, 1 key, which is this design's point. Getting through all 13 Settled unread takes 13 presses of e, or one "Mark 13 read".
At-150
This scales best of B1 to B3: Unread only reduces 150 to the handful you haven't seen, the pager counts them ("3 / 11"), and n never has to scrub. The lane still clusters for the full view.
Build-cost
M. B3 plus a read-state table, keybinding scope handling (focus-bound so it doesn't clash with the composer or browser), an @-autocomplete in the contenteditable composer, swipe gestures, and a list-editor for categories.json.
Trade-off
Powerful once learned, and invisible until then. The keycaps help, but most users never press ?. Explicit read state is accurate, but it becomes a chore when you don't want to triage, and a count that never goes down becomes noise. That's why the Catch-up page offers "Mark N read".
Collapsible●●●●○Short threads●●●○○Easy to read●●●○○Mobile●●●●○Consistency●●●○○Dev nav●●●●●Recap●●●●○Read state●●●●●Feedback loop●●●○○Reply-to●●●●●Categories●●●○○
Best practice

B3 · Pending review

Catching up on a turn works like reviewing a pull request: tick each note Viewed, raise or lower it with a reason, stage replies under the card and send them as one message, and read a Turn summary that closes the block like a PR description.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Ranpnpm test:unit1m 48s
Waiting for the visual run to finish before the Storybook run, so they don't compete for CPU.
Runningpnpm acceptance:visual 4m 10s
Working 38m 12s · Running pnpm acceptance:visual↓ live
Then carry on.
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
44m 30s · $6.80

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5
Phone · Live9 unread · following #25 · 2 pending
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s · Running pnpm acceptance:visual↓ live
Then carry on.
opus-5
Phone · Settled13 unread · Turn summary open
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
44m 30s

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5
Desktop · SettingsSettings › Surfacing · Categories as labels
Phone · SettingsSame page, stacked rows
9:415G ▮▮▮
‹Settings › Surfacing
Builds on
M2's scrub strip: one slider, pins in two rows with a dot fallback, a floating card that follows the thumb, and the density control on the axis. The card now works like a file in a PR review. Its header has a Viewed checkbox, its footer has ↩ Reply · ▲ Raise · ▼ Lower, and replies and reasons open inline under the text. Settled, the card opens in the flow under the axis instead of as an overlay, so it can grow. That meant dropping M2's hover preview, which would shift the page.
Placement
Everything sits inside the one activity block, in this order: the head (with "9 unread"), the card lane (live only), the pins and strip, and the axis (open Ask, viewed progress, density, now or end). Settled, the card sits under the axis, and the Turn summary closes the block. Pending replies sit in the composer above the text field. The final reply is untouched below the block.
Live
The card follows the newest pin at the current level. Scrubbing stops the following, and "now ⇥" resumes it. The desktop frame is scrubbed back to #24, the open Ask, which already has a pending reply. The phone frame follows #25. The summary is a one-line "✦ Summarize 9 new" button until you ask for it. "–" folds the card lane (about 130px) away.
Settled
You see the head, then the pins, strip and axis, then the Turn summary, open at "Since you last read · 13". The card stays closed until you click a pin or a citation, then opens under the axis. ✕ or Esc closes it. The head expands the step log as it does today.
Recap
sonnet-5 writes the Turn summary when the turn ends, like a PR description. It closes the activity block, with "Since you last read" and "Whole turn" scopes, ↻ Regenerate, and "✓ Mark 13 viewed". While live, "✦ Summarize 9 new" writes a "Summary so far" on request (~$0.005). A citation opens its card on the strip. If the message is below the current level (#26, #30 and #34 at Balanced), it gets a dashed pin and the card says "At All, below Balanced". Settings › Turn summary covers the model, when (as checkboxes, so B3 can run at turn end and on request), scope, length and cost.
Read-state
Marking is explicit, like GitHub's Viewed: opening a card doesn't mark it. You tick Viewed in the card header or press v, and untick it to make the message unread again. Two other actions also mark messages viewed: saving a pending reply marks its message, and "Mark 13 viewed" in the summary marks everything it covered. Unviewed pins are full colour with a white dot, and viewed pins dim to half. The axis shows "21/30 viewed ›" with a bar, and the head shows "9 unread". Both jump to the next unviewed message (so does n).
Importance-feedback
▲ Raise or ▼ Lower opens a one-line "Why?" row that states the move ("#26: All → Balanced") and offers reason chips. Raise offers "changed the plan" and "user-visible", Lower offers "noise" and "already knew", and both offer "no reason". Picking one commits the mark. The pin changes level and gets ▲ or ▼, the button stays lit, and the header reads "▲ you · changed the plan". Pressing the lit button again clears the mark. Each commit or clear appends one line (msg, Jev's type and p, mark, reason) to feedback.jsonl. You can see it under Settings › Feedback › Open file. Weights are never changed by marks.
Reply-to
↩ Reply opens a draft box under the card, headed by that message's reference token. "Add pending reply" (⌘↵) stages the reply. The pin gets a blue ↩, the card shows the pending text with Edit and Discard, and the composer lists the reply under "2 pending replies". "Send now" skips staging for a one-off reply. Free text stays in the composer's own field. "What the agent sees" previews the single message: each reply as Re #24 (Ask, 31:02): "…" plus your words, in list order (↑ reorders), then the free text. "Send all" sends it once, and a sent reply to the Ask clears its needs-reply ring. In the mockup, "Restore draft" undoes the send.
Categories
Laid out like GitHub labels. Each row has a coloured pill (glyph and name), the description, a this-turn count, a weight stepper (−/+ 5, or type a value) and actions. Built-ins get Retire, which moves them to a Retired box with Restore, and Reset once changed. Custom categories get Edit and Delete. Edit opens an inline form with a live label preview and fields for name, glyph, colour (↻ random or hex), description and weight. Delete asks for confirmation in the row. "New category" opens the same form at the top. Every change reflows the pins in all four frames at once.
Collapsible
Live, "–" folds the card lane. The summary folds to its one-line header. Final hides the pins and the card, leaving today's strip and a count. The pending list folds to its tokens. Each is one click.
Short-session-threads
A settled turn on desktop is about 10 lines: head 1, pins, strip and axis 3, and the open summary about 6. That drops to about 5 with the summary folded. On the phone it's about 15 lines open and 6 folded. There's one block per turn, nothing is inserted between steps, and the summary lives inside the block. Opening the summary by default makes settled turns taller than M2's.
Easy-to-read
The summary is a real paragraph (12.5px, 1.7 line height) with type-coloured citations, and it's the best read in the design. The card shows one message at a time at 12px (12.5px on the phone), clamped to 5 lines, so comparing two means stepping. Desktop pin labels are 10.5px, which is small. Pending replies show your own words, not quotes.
Mobile-friendly
The strip is a drag target about 55px tall, and drags are horizontal so they don't fight scrolling. Card buttons, the Viewed box, reason chips and pending-row buttons are all at least 32px tall. The pending list stays open in the dock (about 70px) and folds to tokens. The weak spot: 14 pins across about 340px turn several into dots, so you have to drag or step to reach them.
Consistency
These are identical on desktop and phone: the card and its buttons, the Viewed box and progress, the reason row, the inline draft, the summary, the pending list and the preview. Where they differ, it's for width or touch. The phone drops the axis's "00:00" and the summary's model label. The × moves out of each pending token into its own 32px ✕ button. The ⌘↵ hint is hidden. Pins need more spacing, so more of them fall back to dots.
Dev-Navigation-Ease-And-Speed
Open Ask: "? 1 open · #24" on the axis, or a with the strip focused, 1 tap either way. Next unviewed: n, the progress chip or the head's "9 unread", 1 tap. ←/→ steps, Home/End jump to the ends, r replies, v toggles Viewed, +/− raise or lower. #7 is a Finding at .69, so it only shows at All: switch to All, then aim at it or step (2 to 3 actions).
At-150
The strip degrades as it did in M2. About 70 Balanced pins turn mostly into dots, and Key stays usable. The since-last-read summary stays a paragraph. Progress reads "96/150 viewed", and "Mark N viewed" becomes the only sane way to clear it. Pending replies scale as a list, but the preview gets long, and there's no way to group replies.
Build-cost
M. Beyond M2's slider it needs: per-user viewed state per message; a pending-replies store per session that survives a reload; a summary call from the daemon at turn end (~$0.006) plus the on-request one; feedback.jsonl appends that include reasons; a message assembler that builds the "Re #n" block; and a labels page that writes categories.json.
Trade-off
Staging suits answering several things at once, and the preview removes the guesswork about what the agent gets. But a single reply is an extra step ("Send now" softens that), and free text can't sit between references; only their order can change. Explicit Viewed is precise, but it's a click per message, with the summary's "Mark 13 viewed" as the escape hatch. An open summary costs about 5 lines per settled turn.
Collapsible●●●●○Short threads●●●○○Easy to read●●●○○Mobile●●●○○Consistency●●●●○Dev nav●●●○○Recap●●●●○Read state●●●●○Feedback loop●●●●○Reply-to●●●●○Categories●●●●○
Best practice

B4 · Catch-up banner

Built around the return flow. A read horizon splits the scrub strip into what you've seen and what's new, and when you come back a one-line banner offers a recap ("9 new since 27:15 · ✦ Catch me up") that expands in place, with citations that scrub the thumb straight to the message.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
$Ranpnpm test:unit1m 48s
$Runningpnpm acceptance:visual 4m 10s
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
⌕Searchedsession_switched01:10
≡Readsrc/lib/frontend/stores/sidebar.svelte.ts01:31
$Ranpnpm acceptance:visual38:44
… 167 more tool steps · 41 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

44m 30s · $6.80
opus-5↑
Phone · LiveBanner · card · quoted reply
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
$Runningpnpm acceptance:visual 4m
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Phone · SettledBack after 6 min · catch-up open
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
$Ranpnpm acceptance:visual
… 169 more tool steps · 41 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

44m 30s · $6.80
opus-5↑
Desktop · SettingsSettings › Surfacing
Phone · SettingsSame sections, as tabs
9:415G ▮▮▮
‹Surfacing
Builds on
M2's scrub strip, kept whole: numbered pins in two rows with a dot fallback, one thumb that snaps from pin to pin, a card above the strip while live and dropped below it once settled, ←/→ Home End, and the Final/Key/Bal/All segment. Added: a read horizon drawn on the strip, unread dots on pins, the catch-up banner with its inline recap, and a footer on the card (❝ Quote, ⋯). M2's data is swapped for the canonical scenario (the Ask is #24, and #1 ▲ shows at Bal).
Placement
The banner is one line at the top of the activity block, above the strip, and the recap opens right under it. The card's ⋯ menu drops down from the button over the strip, and flips up when it would run under the composer. Nothing is inserted into the transcript: once you've read, the banner goes away and the block is M2's again.
Live
The thumb follows the newest Bal message (#25 here) and the card shows it. The mockup has the card's ⋯ menu open. The banner reads "● 9 new since 27:15 · 1 needs you". The dashed NEW line sits at 27:15, and pins to its right carry a white dot. Scrubbing stops following, and "now ⇥" resumes it. "–" folds the card lane.
Settled
You've just come back: "● 13 new since 27:15" with the catch-up already open (because you were away 5+ min, see settings), and #1 ▲ on its pin. The card is closed until you hover, click or drag the strip. The final reply stays where it was.
Recap
It's triggered by the banner (by default, away more than 5 min or 5+ unread) or c on the strip. It shows "✦ Catching you up on 13 updates with sonnet-5…", then 4 sentences with citations. Settled has a Since you last read / Whole turn switch. A citation scrubs the thumb to the message, opens its card (switching to All when needed) and pulses the pin. "✓ Mark 13 read" moves the horizon to the end and the banner goes away. Settings cover model, triggers, scope, length and cost.
Read-state
It's a single read horizon per turn, as in Slack: everything left of the NEW line is read, and the banner, dots and card "● unread" all come from it. Stepping onto the first unread and staying 1s moves the line past it, so reading in order works on its own. Jumping ahead (a citation, the Ask chip, scrubbing to #30) does not move it. That's the cost of a horizon: you can't be "read #30, not #26". Card ⋯ has "Mark unread from here" and "Mark read up to here".
Importance-feedback
Card ⋯ has ▲ More important and ▼ Less important, each showing the level change ("Bal → Key"), and "It's actually a… ›", a type list. The pin gets ▲/▼, the card gets "▲ you", and a toast confirms "appended to feedback.jsonl" with Undo. It's one menu step from reading, not zero: the card footer stays uncluttered.
Reply-to
❝ Quote on the card (or q on the strip) drops a .ref token at the composer's last caret position. Hovering the token shows the quoted text. Tokens are atomic: × or Backspace removes one, and you can type between them. The canonical draft is shown in both live composers.
Categories
Settings › Surfacing, like system preferences: sliders grouped by urgency tier (Needs a reply · Needs attention · Consequential · Informative · Yours), each with ↺ reset. Built-ins retire into a Retired group with Restore. Yours can be edited inline (glyph, name, colour, definition, tier) and deleted. "+ Add category" opens the same form. On desktop, Catch-up and the Feedback ledger sit in a second column.
Collapsible
The banner disappears once there's nothing new, and "Hide" closes the recap. The live card lane folds with "–" (138px is freed), the settled card closes with ✕ or Esc, and Final removes pins and cards, leaving today's strip.
Short-session-threads
Settled with nothing new: M2's 4 lines. Coming back: +1 line of banner, and the recap is about 6 lines more until you've read it. Live: +138px of card lane (170px on the phone) until folded. It's one block per turn, and nothing is inserted between steps.
Easy-to-read
The recap is the best on-ramp here: 13px, plain sentences, citations in type colour. The card is still one note at a time at 12px, clamped to 4 lines live, so reading a run of findings means stepping. Pin numbers stay at 10.5px on desktop.
Mobile-friendly
The banner wraps to two rows with 32px buttons. The strip (pins plus track, about 55px) is the drag target, and it doesn't fight vertical scroll. Card buttons are 32px. The ⋯ menu goes full width with 36px rows and flips up above the card. The weak spot is still M2's: 13 pins in 320px means dots, so the strip is for dragging, not precise tapping.
Consistency
Desktop and phone share the banner, recap, card, menu and horizon. Two differences: the menu is full width on the phone, and settings on the phone are three tabs instead of two columns. Keys (n q c u + −) are desktop-only extras.
Dev-Navigation-Ease-And-Speed
Focus the strip: n is the first unread, a the Ask, ←/→ step, Home/End, c catch up, q quote. #7 is All then about 6 steps, or a drag, because M2 has no jump-to-number. Recap citations are the fastest way to a specific message.
At-150
The banner and recap don't grow ("40 new since …" still gets a 4-sentence recap), and that's where this design shines on long turns. The strip doesn't cope: about 70 Bal pins become dots, and dragging turns into guessing. Key (about 25) stays usable, and n plus the recap become the main way through.
Build-cost
M. On top of M2: a per-user read horizon (one integer per turn), an away timer, one recap call through the daemon, a feedback.jsonl appender, a categories.json editor with tiers, client-side re-levelling, and a contenteditable composer with quote tokens.
Trade-off
It gets the "I just came back" moment right and costs nothing once you're caught up. But read state is coarse (a horizon, not per message), importance marks sit behind ⋯, and it inherits M2's one-note-at-a-time card and a strip that degrades on long turns.
Collapsible●●●●○Short threads●●●●○Easy to read●●●○○Mobile●●●○○Consistency●●●●○Dev nav●●●○○Recap●●●●●Read state●●●○○Feedback loop●●●○○Reply-to●●●●○Categories●●●●○
Best practice

B5 · Type chip popover

The type label on the slot is a button, like a Linear property chip: it opens one popover where you raise, lower or retype that message and drag its category's weight while the maths and the pins update live, so you fix the categorisation where you notice it.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Readsrc/lib/frontend/stores/sidebar.svelte.ts0.8s
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm acceptance:visual6m 10s
+ 165 more tool steps and 40 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

/recapopus-5↑
Phone · LiveBalanced · 9 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Phone · SettledRecap card · 13 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 51 runs · 37 edits
Ranpnpm acceptance:visual6m 10s
+ 169 more tool steps and 41 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

44m 30s · $6.80
/recapopus-5↑
Desktop · SettingsSettings › Surfacing › Categories
Phone · SettingsSame page, tabs on top
9:415G ▮▮▮
‹Surfacing
Builds on
B3, kept whole: numbered pins at the moment each message was said, merged groups, the loupe, the slot hanging from the selected pin, digits-then-Enter jumps. Pins you have marked never merge into a group (like the Ask), so #1 ▲ always shows. What changed: the slot's type label became a button (the property chip), pins and groups carry a white unread dot and a ▲/▼ when you've marked them, the head carries the unread count, and the slot foot gains ↩ Reply, Mark read/unread and ✦ Recap.
Placement
All inside the one activity block: head (with "9 unread"), pin lane, strip, axis, then the slot. The popover drops from the type chip over the slot text, never over the pins, so you can watch them move while you drag the weight. While it is open the composer stays docked at the bottom of the pane, so the draft stays visible. The recap card takes the slot's place until you dismiss it and is never written to the transcript. The final reply follows the block, then the composer.
Live
The slot follows the newest message at the level. The mockup shows the user at All, having tapped #26 and opened its chip: p .64 × weight 40 = 26 → All, with the next threshold ("Balanced at weight ≥ 63") and how many Findings show at this level. Dragging the slider re-levels every Finding in the turn and moves the pins while you drag. The message you're editing stays in the slot even if it drops below the level, until you close the popover.
Settled
The slot opens on the Ask #24 ("Waiting on you"). The mockup shows ✦ Recap invoked: the ephemeral card, since you last read, sits in the slot's place with a Since you last read / Whole turn switch. The pin lane stays visible above it, with #1 carrying its ▲ at Balanced.
Recap
On request only, from ✦ Recap in the slot foot, c, or by typing /recap and Enter in the composer (the command is stripped, nothing is sent). It shows "Recapping…" briefly, then the card with dashed edges: "Only you see this · not added to the transcript", ✕ or Esc to dismiss. A citation closes the card, opens that message in the slot (switching to All if it's below the level) and pulses its pin. Settings › Recap covers model, when, scope, length and cost. Choosing "When I come back" auto-opens the same card.
Read-state
Dwell to read: a message you open yourself (tap, step, jump, citation) becomes read after 1.5s in the unfolded slot, and a hairline under "● unread" fills as it counts. Following live never marks anything read, because you might be away. "○ Mark unread" / "✓ Mark read" in the slot foot, or u, toggles it. Unread shows as a white dot on pins and groups, "● unread" in the slot and the count in the head. Clicking the count jumps to the next unread. The recap card offers "Mark 13 read".
Importance-feedback
In the popover, ▲ More important and ▼ Less important move this one message a level. The pin gets ▲ or ▼, the slot shows "▲ you", and the popover reads "Jev: All · You: Balanced ▲". A line is written to feedback.jsonl ({at, session, turn, msg, text, jev:{type,p}, mark:"raise"}). A retype writes mark:"retype" with the new type. Marks never touch a weight. The weight slider sits beside them but is labelled as the whole category and saves to categories.json, with Undo. "Ask an agent to tune categories" (Settings › Feedback ledger) reads both files.
Reply-to
↩ Reply (or r) inserts a .ref token at the composer's last caret position, which is remembered while you click away, and leaves the caret after it so you can type straight on. Repeat it for another message, anywhere in the text. Tokens are atomic: × or Backspace removes one. On send each token expands into the agent's "Re #24 (Ask, 31:02): "…"" block, followed by the text you typed after it.
Categories
There are two surfaces for one model. The popover handles the common fix in context: weight with live maths, Retype to…, Retire this category (with Undo) and Manage categories…, which scrolls to the settings row and highlights it. Settings › Surfacing › Categories lists Built-in, Yours and Retired: a weight slider with a number and ↺ reset, Retire or Restore for built-ins, Edit or Delete for yours, and + New category (glyph, name, colour, definition, weight, "Preview on this turn · ~$0.02").
Collapsible
▾ folds the slot to a one-line peek, while the pins and strip stay because they are the activity strip. The Final level hides pins and slot. The recap card closes with ✕. The popover closes with Esc, ✕ or a click outside.
Short-session-threads
Settled desktop: head, then pins, strip and axis (about 3 lines), then the slot, about 6 lines, so about 9 lines in total. With the recap card open it's about 11. Phone: about 13. Folded: 4. It stays one activity block. The popover and recap overlay or replace the slot rather than adding rows.
Easy-to-read
The slot keeps B3's 13.5px full text with a big type label. The popover shows its maths as a sentence rather than a hidden formula. Weak spot: the popover is dense (two columns at 12px on desktop), and the chip's only affordance is its border and ▾.
Mobile-friendly
The popover goes full width and single-column, drops under the chip, and has 32px buttons. The slot moves its pager and levels to a foot row, with actions in a second row, all at 30px or more. Tokens' × have a 32px hit area. The loupe works as in B3. The recap card is the same card.
Consistency
The same on both: the chip, the popover's content and order, the slot, the recap card, the tokens and the settings list. Differences: the popover stacks into one column and always opens downward on the phone, because the body scrolls and there's no room beside it; the pager moves to the foot for thumb reach; there are no keyboard shortcuts on the phone; settings use top tabs instead of a side nav.
Dev-Navigation-Ease-And-Speed
Desktop: #7 is 7 ↵ (it switches to All), the Ask is a or the "? #24" chip, and the next unread is n or the head count, each one step. Phone: #7 is one drag through the loupe, or All then tap. The Ask chip and the unread count are one tap each.
At-150
The lane becomes about 20 group pills (as in B3), each dotted if any member is unread, and the unread count plus "next unread" becomes the main way in. The recap stays Short (4 sentences) and cites the handful that matter, and the popover's count ("58 Findings in this turn · 3 at Balanced") shows what a weight change would do before you commit. Reply tokens are per message, so nothing changes. Height doesn't grow.
Build-cost
M. On top of B3: per-user read state, a feedback.jsonl appender, categories.json read and write with client-side re-levelling (weight × stored p, no model call), one recap call through the daemon (~$0.006), a slash-command hook, and a contenteditable composer with atomic tokens that serialise to the Re-blocks.
Trade-off
Everything about a message's category lives behind one chip. That's fast once learned but invisible until then. Putting a whole-category weight slider next to a one-message ▲ invites people to reweight a category when they meant one message (the labels fight this, not always successfully). The ephemeral recap can't be scrolled back to, and dwell-to-read can mark something read that you merely paused on.
Collapsible●●●●○Short threads●●●●○Easy to read●●●●○Mobile●●●○○Consistency●●●●○Dev nav●●●●○Recap●●●○○Read state●●●○○Feedback loop●●●●○Reply-to●●●●○Categories●●●●●
Best practice

B6 · Command palette + action sheet

One command surface for every verb: ⌘K on desktop (or the slot's ⋯) opens a palette scoped to the selected message, a long-press on the phone opens an action sheet with the same verbs in the same order, and settings are a dev-first form beside the categories.json it writes.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Readsrc/lib/frontend/stores/sidebar.svelte.ts0.8s
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm acceptance:visual6m 10s
+ 165 more tool steps and 40 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Phone · LiveLong-press #24 · action sheet
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Phone · SettledRecap in the slot · 13 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 51 runs44m 30s
Ranpnpm acceptance:visual6m 10s
+ 169 more tool steps and 41 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Desktop · SettingsForm ⇄ categories.json, live
Phone · SettingsForm or JSON, one at a time
9:415G ▮▮▮
‹Surfacing
Builds on
B3, kept whole: numbered pins at their moment on the strip, merged groups, the loupe, the single slot, digits-then-Enter. Added: a ⋯ on the slot, ⌘K, a phone long-press, unread dots and ▲/▼ on pins, and the unread count right after the head's verb. Pins you've marked never merge, so #1 ▲ stays visible at Balanced. There's no new visible chrome beyond ⋯ and ↩ Reply: every other verb lives in the one command surface.
Placement
The activity block is B3's. The palette is a modal at the top of the conversation pane over a light scrim, and the composer stays above the scrim so the draft you're building is readable and editable. The phone sheet rises from above the dock, so the composer and its tokens stay in view. Recap renders inside the slot as a page, not as a new message.
Live
The slot follows the newest message at the level. The mockup shows ⌘K after the user tapped the Ask. The palette is scoped to #24: its first row is "Reply to #24", and typing filters ("wei" finds "Set Finding weight…" and "Set Ask weight…"). A bare number ("7") offers "Go to #7". ↑↓ ↵ Esc work, and ⌘K reopens it from anywhere in the pane.
Settled
The slot opens on the Ask. The mockup shows "Recap since last read" run from the palette: the slot turns into a ✦ Recap page with a Since you last read / Whole turn switch, working citations and "Mark 13 read". ✕ or Esc brings the Ask back. #1 ▲ is visible in the lane.
Recap
It lives in the slot, on request: palette "Recap since last read" / "Recap whole turn", c, or the sheet. It shows "Recapping 13 updates with sonnet-5…" and then the page. Citations open the message in the same slot (switching to All when it's below the level) and pulse its pin. The page is never written to the transcript. Settings › recap covers model, when, scope, length and cost.
Read-state
Read on leave: a message you picked (tap, step, jump, citation) counts as read when you move off it after at least 1s, which matches how you actually scan. "Next unread" (n, the head count, or the palette) marks the one you're leaving read and jumps. "Mark #n unread/read" (u) is in the palette and sheet. Following live never marks anything. There's a white dot on pins and groups, "● unread" in the slot, and the count in the head.
Importance-feedback
"Mark #24 more important" (+) and "less important" (−) move one message one level and put ▲/▼ on its pin and "▲ you" in the slot. "Retype #n as…" opens a type list inside the palette. Each appends a line to feedback.jsonl, shown in a toast with Undo. "Set Finding weight…" is a separate, category-wide command with the live p × weight = prominence → level maths. "Open feedback.jsonl" jumps to the ledger.
Reply-to
"Reply to #24" (r, ↩ Reply on the slot, or the sheet's first row) drops a .ref token at the composer's last caret position and leaves the caret after it. Tokens are atomic: × or Backspace removes one. "Preview what the agent receives" in the palette shows the exact serialised text ("Re #24 (Ask, 31:02): "…"" blocks) before you send.
Categories
The palette handles the quick changes: "Retire category Ask" (with Undo; its messages fold into the log), "Set Finding weight…", "Open categories.json". The settings tab categories.json is a form on the left and the file on the right, and they stay in sync live. Every weight slider rewrites the JSON as you drag. You can edit the JSON and press Apply: it is validated, and deleting a built-in is refused with "move it to retired". Built-ins retire, restore and ↺ reset. Yours can be edited inline and deleted. "+ Add category" appends a row. Preview on this turn costs about $0.02.
Collapsible
▾ folds the slot to a peek line. The Final level hides pins and slot. The recap page closes to the message. The palette and sheet dismiss with Esc, a scrim tap or Cancel.
Short-session-threads
It's B3's height: about 9 lines Settled on desktop, including the recap page, which reuses the slot. The phone is about 13 lines. The palette and sheet overlay the view and add no rows. Folded, it's 4 lines.
Easy-to-read
The slot is B3's 13.5px full text with a big type label. Palette rows are plain sentences with the message number in bold ("Mark #24 more important"). The cost: verbs aren't visible until you open the surface, so a new user sees only ↩ Reply and ⋯.
Mobile-friendly
Long-press (450ms, with a press-in scale) on any pin or the slot opens the sheet. ⋯ does the same, for people who don't know long-press. Rows are 40px, Cancel is 44px, the sheet scrolls, and a preview row shows which message it's scoped to. Long-press is not discoverable on its own, and the ⋯ is what makes it safe to depend on.
Consistency
The palette and the sheet are built from one command list, with the same verbs, order and scoping: rows, sections and sub-steps (Retype, Set weight, Preview) are identical. Differences: the desktop has a filter field and key hints, while the phone has neither and a preview row instead; settings on the phone show the form or the JSON, not side by side, because a split at 340px is unreadable.
Dev-Navigation-Ease-And-Speed
Desktop: #7 is 7↵ in the lane, or ⌘K "7" ↵ from anywhere. Ask is a. Next unread is n. Every verb is either a single key or ⌘K plus a couple of letters. Phone: #7 is a drag through the loupe, or All then tap. Next unread is the head count.
At-150
The lane collapses to about 20 group pills as in B3, and ⌘K "Go to #" plus "Next unread" becomes the main way in. The palette doesn't grow, because it's scoped to one message. The recap stays at 4 sentences with citations. Reply tokens and the JSON view are unaffected. Height doesn't change.
Build-cost
M. On top of B3: one command registry feeding both the palette and the sheet, a long-press recogniser, per-user read state with a leave timer, a feedback.jsonl appender, categories.json read/write with a schema validator for the split view, client-side re-levelling, one recap call through the daemon, and a contenteditable composer with serialisable tokens.
Trade-off
Fast and uniform for people who live on the keyboard, but it hides the feedback loop: ▲/▼ is two steps away (⋯ or ⌘K, then the row), so fewer marks get written and the tuning agent has less to go on. Long-press on the phone is invisible without ⋯. The JSON view is powerful, but it lets you make edits the form can't show, so it has to validate strictly.
Collapsible●●●●○Short threads●●●●○Easy to read●●●●○Mobile●●●○○Consistency●●●●○Dev nav●●●●●Recap●●●○○Read state●●●○○Feedback loop●●●○○Reply-to●●●●○Categories●●●●○
Best practice

B7 · Footnoted recap

The recap is the default read: settled turns open on a four-sentence summary whose superscript footnotes are the pin numbers, so each claim is one tap from the message that backs it, and the pins it cites turn half-filled as “read via recap”.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
00:0038:12 · now
Final only · 30 updates hidden · the recap and unread count stay in the bar
Working 38m 12s · Running pnpm acceptance:visual↓ live
? #24 Ask Recapture it, the 2px shift is intended. ◆ #18 Decision Agree, but log the stream count on reconnect so we can see it.
Then carry on.
opus-5
▸ What the agent receives
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Readsrc/lib/frontend/stores/sidebar.svelte.ts0.8s
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm test:unit1m 48s
Waiting for the visual run to finish before the Storybook run, so they don't compete for CPU.
Ranpnpm acceptance:visual6m 10s
+ 165 more tool steps and 39 narration lines
00:0044:30
Final only · 34 updates hidden · the recap and unread count stay in the bar

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
▸ What the agent receives
Phone · LiveRecap open · 9 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
00:0038:12 · now
Final only · 30 updates hidden · the recap and unread count stay in the bar
Working 38m 12s· Running pnpm acceptance:visual↓ live
? #24 Ask Recapture it, the 2px shift is intended. ◆ #18 Decision Agree, but log the stream count on reconnect so we can see it.
Then carry on.
opus-5
▸ What the agent receives
Phone · SettledRecap · 13 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Readsrc/lib/frontend/stores/sidebar.svelte.ts0.8s
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm test:unit1m 48s
Waiting for the visual run to finish before the Storybook run, so they don't compete for CPU.
Ranpnpm acceptance:visual6m 10s
+ 165 more tool steps and 39 narration lines
00:0044:30
Final only · 34 updates hidden · the recap and unread count stay in the bar

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
▸ What the agent receives
Desktop · SettingsSurfacing · categories with live preview
Categoriesprominence = weight × Jev p · Key ≥ 70 · Balanced ≥ 40
Live preview · this turn's 34 updates
00:00KeyBalancedAll only44:30
Retired
+ New category
NameGlyphColourSurfaces whenWeight

Adding a category adds one option to Jev's Choice question, so the cost barely moves. New categories apply to new messages.

Recapruns in the daemon
Model
When
Scope
Length

Cost: about $0.006 per recap on sonnet-5, $0.03 on opus-5. The daemon runs it; API keys stay server-side.

Feedback ledgerplain files · append-only
This week
23 marks · 14 ▲ · 9 ▼
Most raised
● Finding · 9 ▲
Most lowered
✓ Milestone · 6 ▼
~/.config/conduit/surfacing/feedback.jsonl
~/.config/conduit/surfacing/categories.json
Phone · SettingsTabs · Categories
9:415G ▮▮▮
‹Settings · Surfacing
Categoriesprominence = weight × Jev p · Key ≥ 70 · Balanced ≥ 40
Live preview · this turn's 34 updates
00:00KeyBalancedAll only44:30
Retired
+ New category
NameGlyphColourSurfaces whenWeight

Adding a category adds one option to Jev's Choice question, so the cost barely moves. New categories apply to new messages.

Builds on
B3's numbered pins and slot, plus the Perplexity and Notion AI pattern of a generated answer whose citations are the source numbers. The pin lane, loupe, scrub, pager and fold are B3's unchanged. What changes is what fills the slot by default.
Placement
Inside the one activity card: pin lane, strip and axis, then a bar (unread count, ✦ Recap, the Ask chip, level), then the slot. The slot holds either the recap or one message. Settled, the step log opens from the head as usual, and the final reply follows the card.
Live
The slot parks on the open Ask #24, tagged “Waiting on you” and “● unread”. The bar reads ● 9 unread and ✦ Recap · 9 new. Pressing Recap runs sonnet-5 for about 1.6s and swaps the slot to the cited summary. The desktop frame shows a text selection on #24 with its “❝ Reply to this” button, which is a static demo that clears on the first click. The phone frame shows the live recap open.
Settled
The slot opens on the recap, since you last read (27:15 → 44:30), with footnotes ?24 ●26 ✓30 ◆32 ✓34. Those five pins get a dashed ring and turn half-filled, and the bar reads 13 unread · ◐ 5 via recap. “Whole turn” swaps in the 13-footnote whole-turn recap. #1 keeps its ▲ badge on the lane, and its slot tag reads “▲ you · changed the plan”.
Recap
The recap is the default read of a settled turn. The footnote numbers are the pin numbers. Hovering or focusing a footnote lifts its pin, and clicking it opens that message in the slot with a “‹ Recap” chip to go back. A footnote you've followed turns dashed. The foot shows model, update count, time and cost (sonnet-5 · 13 updates · 1.8s · $0.006) plus “Next unread ⇥”. Model, trigger, scope and length are set in Settings.
Read-state
Three states on the pin itself: filled = unread, half-filled = read via recap (cited in a recap you opened), hollow = read. Opening a message by tap, pager, footnote or number marks it read. Scrubbing only reads the message you release on. A slot button toggles Mark read / Mark unread. The count excludes only fully read messages, so half-filled ones still count, with the breakdown beside it. “Next unread” visits uncited unread messages first.
Importance-feedback
▲/▼ in the slot foot move a message one level (Balanced → Key, or down through All into the log). The level is shown beside the buttons, a ▲/▼ badge appears on the pin, and the slot tag reads “▲ you”. If a message drops below the current filter it stays open, with “hidden at this level”. Each mark is appended to the ledger and never changes a category's weight.
Reply-to
Two grains. ↩ Reply inserts a whole-message token (? #24 Ask) at the caret. Select any words in a message or in the recap and a “❝ Reply to this” button quotes just that fragment (? #24 “Recapture it, or keep…”, or ✦ Recap “…”). The agent receives Re #24 (Ask, 31:02), the part: "…". Tokens sit inline with text, and × or Backspace removes them. “What the agent receives” previews the send.
Categories
Settings put a live preview of this turn's pin lane above the category list. There are 34 ticks, whose height is Key, Balanced or All-only. They are lit when shown at Balanced, and Key ticks carry their glyph. The ticks move while you drag a weight, and changed ticks flash. A readout (“Balanced shows 16 of 34 · Key 6”) updates with them. Built-ins can be retired (their ticks go grey), restored and reset. ⚡ Perf is custom and editable. New categories can be added and deleted, and there is a paid dry-run preview.
Collapsible
▾ folds the slot to a one-line peek of the recap or message. The lane and strip stay, because they are the activity strip. Final hides lane and slot but keeps the bar, so the unread count and Recap survive.
Short-session-threads
Desktop: head, lane, strip and axis (3 lines), bar (1), then a slot of at least 3 lines plus top and foot, about 10 lines. Folded: 5. For a 3-message turn the recap is overkill: the bar's Recap button still works, but the slot opens on the latest message instead.
Easy-to-read
The recap is 13.5px prose at 1.7 line height, and each footnote is a small chip coloured by type that carries its glyph. The number matches the pin and the slot's #24 · 31:02. The message view is B3's big type label with full text.
Mobile-friendly
Pins grow to 24px and merge within 28px (B3). Footnotes are 12px chips whose hit area extends to 32px by an invisible pad, and the recap line height rises to 1.85 so padded chips on adjacent lines don't overlap. The bar wraps to two lines. The pager moves into the slot top with 34×32 buttons, and tags drop to their own line. Selecting text uses the native handles, and the reply button is 32px tall.
Consistency
These are identical on desktop and phone: the lane, the pin states (filled, half, hollow, ringed), the bar and its order, the recap text and footnotes, the slot's top/body/foot, and both reply grains. The divergences are these. (1) Pins and chips grow and the merge distance widens, for touch. (2) The level segment says “Bal”, to fit. (3) Slot tags wrap to their own line, because the top row is narrow. (4) Footnotes get invisible 32px hit pads, because they are text-height. (5) The “select words” hint is hidden, because touch has no hover and selection is native. (6) Settings become three tabs.
Dev-Navigation-Ease-And-Speed
These are B3's keys: ←/→, a for the Ask, and digits plus Enter. B7 adds r to toggle the recap and u for the next unread. Footnotes are Tab-able buttons, and focusing one lifts its pin. The fastest catch-up is to read four sentences, then press u through the remaining ● pins.
At-150
The recap stays at four sentences with 4–6 footnotes (for example ?118 ✕131 ✓149). Footnote numbers are stable pin numbers, so #118 is findable by typing it. The unread count reads, say, “41 unread · ◐ 6 via recap”, and “Next unread” skips the half-filled ones. The lane falls back to B3's group pills with the loupe, and a group holding a cited pin carries the dashed ring, so every footnote still has a visible target. Following a footnote pulls its pin out of the group. Reply tokens stay short, because fragment quotes are clipped at 24 characters in the token and sent in full.
Build-cost
M+. B3's lane, plus a recap endpoint that returns sentences with message ids (constrained output), a per-message read state with three values persisted per user, a selection-to-fragment reply with a new token kind, and the settings preview (a pure function of the weights).
Trade-off
The recap becomes a second source of truth: if it misstates something, the footnote is the only guard. “Read via recap” is honest but adds a third state that people have to learn. Fragment reply needs a text selection, which is fiddly on phones.
Collapsible●●●●○Short threads●●●○○Easy to read●●●●○Mobile●●●○○Consistency●●●●○Dev nav●●●●○Recap●●●●●Read state●●●●○Feedback loop●●●○○Reply-to●●●●●Categories●●●●●

In between

A familiar base with one novel idea each.

In between

M1 · Scrub to read

M2's scrub strip, painted like a video's watched bar. The stretch you haven't read glows, and scrubbing across it greys it out. A three-line running brief (Goal · Done · Open) sits in the activity head, and every clause cites its pin. You throw the card at the composer to reply, or flick it up or down to rank it.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
✦ Running briefsonnet-5 · after #30
GoalSidebar, badges and deep link onto the typed stream ; legacy path flagged .
DoneSidebar and MessageList , badges , deep link .
OpenRecapture phone-sidebar or keep the paint order?
Working 38m 12s · Running pnpm acceptance:visual↓ live
? #24 Ask Recapture it, the 2px shift is intended. ◆ #18 Decision Agree, but log the stream count on reconnect so we can see it.
Then carry on.
opus-5
▸ What the agent receives
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Readsrc/lib/frontend/stores/sidebar.svelte.ts0.8s
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm test:unit1m 48s
Waiting for the visual run to finish before the Storybook run, so they don't compete for CPU.
Ranpnpm acceptance:visual6m 10s
+ 165 more tool steps and 39 narration lines
✦ Running briefsonnet-5 · final
GoalSidebar, badges and deep link onto the typed stream ; legacy path flagged .
DoneAll migrated , legacy off , branch pushed .
OpenRecapture phone-sidebar or keep the paint order? Held
Scrub to read. The glow is what you haven't seen; release or pause on a card and it greys out.

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
▸ What the agent receives
Phone · LiveCard on the Ask · 9 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
✦ Running briefsonnet-5 · after #30
GoalSidebar, badges and deep link onto the typed stream ; legacy path flagged .
DoneSidebar and MessageList , badges , deep link .
OpenRecapture phone-sidebar or keep the paint order?
Working 38m 12s· Running pnpm acceptance:visual↓ live
? #24 Ask Recapture it, the 2px shift is intended. ◆ #18 Decision Agree, but log the stream count on reconnect so we can see it.
Then carry on.
opus-5
▸ What the agent receives
Phone · SettledBrief · 13 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Readsrc/lib/frontend/stores/sidebar.svelte.ts0.8s
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm test:unit1m 48s
Waiting for the visual run to finish before the Storybook run, so they don't compete for CPU.
Ranpnpm acceptance:visual6m 10s
+ 165 more tool steps and 39 narration lines
✦ Running briefsonnet-5 · final
GoalSidebar, badges and deep link onto the typed stream ; legacy path flagged .
DoneAll migrated , legacy off , branch pushed .
OpenRecapture phone-sidebar or keep the paint order? Held
Scrub to read. The glow is what you haven't seen; release or pause on a card and it greys out.

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
▸ What the agent receives
Desktop · SettingsSurfacing · categories, brief and reading
Categoriesprominence = weight × Jev p · Key ≥ 70 · Balanced ≥ 40

Retired
+ New category
NameGlyphColourSurfaces whenWeight

Adding a category adds one option to Jev's Choice question, so the cost barely moves. New categories apply to new messages.

Brief & readingthe brief runs in the daemon
Brief model
Refresh the brief
Brief covers
Length
Scrubbing marks read
Card gestures

Cost: about $0.002 per refresh on sonnet-5. This turn refreshed 14 times, $0.03 in total (about $0.14 on opus-5). “Since you last read” is one extra call, about $0.006.

Feedback ledgerplain files · append-only
This week
23 marks · 14 ▲ · 9 ▼
Most raised
● Finding · 9 ▲
Most lowered
✓ Milestone · 6 ▼
~/.config/conduit/surfacing/feedback.jsonl
~/.config/conduit/surfacing/categories.json
Phone · SettingsTabs · Categories
9:415G ▮▮▮
‹Settings · Surfacing
Categoriesprominence = weight × Jev p · Key ≥ 70 · Balanced ≥ 40

Retired
+ New category
NameGlyphColourSurfaces whenWeight

Adding a category adds one option to Jev's Choice question, so the cost barely moves. New categories apply to new messages.

Builds on
M2's scrub strip: the pins in two rows, the one slider, the floating card with ‹ n/N ›, levels and Final. Three things change. The strip is painted with read state. A running brief sits in the head. The card itself is the handle for replying and ranking. The level control now only decides which pins get labels: every update stays on the strip as a dot you can scrub to, so the watched bar can always be finished.
Placement
In the activity card, in this order: head, then the brief (three lines), then the card lane, the pins, and the strip with its rail. Below that sits the axis: unread count, Ask chip, levels, and now/end. The card always sits above the strip in its own lane. When the lane grows or shrinks, the scroller moves by the same amount, so the strip stays under your finger mid-scrub, and nothing covers the brief, the reply or the composer. Settled, the step log opens from the head, and the final reply follows the card.
Live
The brief refreshes after each Balanced update (↻ after #30 · 38:05). The card is parked on the Ask #24 (“needs reply · ● unread”). 0–27:15 is grey and 28:30–now glows. The axis reads ● 9 unread, and the composer holds the draft with ? #24 Ask and ◆ #18 Decision. When the card is closed, following live shows the newest update without marking it read.
Settled
The strip is back to one line, with the brief in final form above it. The strip is grey to 27:15 and glows from 28:30 to 44:30. The axis reads ● 13 unread. #1 carries a ▲ over its pin, and one tap opens it tagged “▲ you · changed the plan”. On desktop, hovering the strip peeks at a card, and a hover that rests on a card for 350ms counts as reading it.
Recap
The running brief: Goal, Done and Open, one line each. Every clause cites its pins as type-glyph chips (●1 ±3 ◆6 · ✓21 ✓23 ✓30 · ?24 ◆32). Hovering a chip rings its pin, and clicking one opens the card. The toggle swaps in the canonical “Since you last read” recap (13 updates, with the same chips). ▾ folds the brief to just its Open line. Settings choose the model, the refresh rule, the scope, the length and the cost.
Read-state
It is painted on the strip like a watched bar. Unread stretches glow blue, with a lit rail underneath. Read stretches are desaturated and dimmed, with a grey rail. A stretch runs from one update to the next. Scrubbing past a pin marks it read if its card stays up for 350ms, and so does wherever you let go. A fast fling reads nothing. A newly read stretch fades from blue to grey. Pins match the strip: lit with a blue dot when unread, faded when read. The card has an explicit ✓ Mark read / Mark unread toggle, and a message marked unread stays lit until you open it again. The count button jumps to the next unread (u).
Importance-feedback
Flick the card up to raise it one level or down to lower it: a vertical flick of 26px or more, released within 380ms and not over the composer. The card bumps, and the foot line confirms “▲ Raised to Key · logged to feedback.jsonl”. ▲/▼ in the card foot do the same, and the level word sits beside them. Marks show as ▲/▼ over the pin and as a “▲ you” tag. Each mark is appended to the ledger and never changes a weight.
Reply-to
Drag the card into the composer. After 10px a token ghost follows the pointer, the composer lights up when you are over it, and the token drops at the caret position under the pointer (caretRangeFromPoint). ↩ Reply and r insert at the saved caret. Tokens sit inline with the text, and × or Backspace removes them. The agent receives Re #24 (Ask, 31:02): "…", and “What the agent receives” previews the send.
Categories
9 built-ins plus ⚡ Perf, each with a weight slider, a live number, a retire/restore toggle and a reset. Perf can be edited and deleted, and new categories can be added. A readout says how many pins this turn would label at Key and Balanced. The card also offers the ~$0.02 dry run. A second card, Brief & reading, adds the scrub-reading rule (350ms dwell, release only, or manual) and a switch for the gestures.
Collapsible
✕ closes the card, and the lane collapses to nothing. ▾ folds the brief to its Open line. Final hides the pins and the card but keeps the painted strip, the brief and the unread count. That leaves three levels of fold: full, then strip plus brief, then strip plus Open line.
Short-session-threads
With the card closed: head (1 line), brief (3), pins and strip (2), axis (1). That is about 7 lines, or 5 with the brief folded. A 3-update turn barely needs the brief. The daemon skips it under 4 updates, and the strip alone is the recap.
Easy-to-read
The brief uses fixed labels, so you read Open first (it is tinted like the Ask). The card is M2's type label with full text. Read state is carried by one visual rule, glow versus grey, that works at a glance with no numbers.
Mobile-friendly
The strip grows to 12px and the thumb to 23px. Pins use 12px type. The card spans the width, with 32px foot buttons, and ↩ loses its label. The brief's chips get 32px hit pads and a line height of 1.85, and its meta line wraps under the toggle. Dragging the card down to the dock works with a thumb. Flicking is the natural phone gesture, and the ▲/▼ buttons remain as fallbacks.
Consistency
These are identical on desktop and phone: the painted strip and rail, the pin states, the brief's rows and chips, the card's layout and foot, the drag-to-reply and flick-to-rank gestures, the 350ms rule, and the axis order. The divergences are these. (1) The strip, thumb, pins and buttons grow, for touch. (2) The card spans the width instead of following the thumb, because a phone is too narrow to float it; its arrow still points at the pin. (3) The brief's meta wraps under the toggle, and ↩ drops its label, for width. (4) There is no hover peek, because touch has no hover; dwell comes from scrubbing only. (5) Settings become three tabs.
Dev-Navigation-Ease-And-Speed
Focus the strip. ←/→ step through labelled pins, and Shift with an arrow steps through every update. u goes to the next unread, a to the Ask, m marks read or unread, +/- rank, r replies, and Esc closes the card. Brief chips are Tab-able. The fastest catch-up is to read the three lines, then press u repeatedly until the glow is gone.
At-150
The brief stays at three lines, citing the newest pins (?118 ✕131 ✓149). The count reads “● 41 unread”, and the glow shows where the 41 are. That is the main win at 150: you can see which stretch you missed without reading a number. Dots get dense (about 3px apart at 536px), so Shift+arrow and u become the precise tools and the scrub works best at Key. Reply tokens stay short (#118), whatever the turn length.
Build-cost
M. M2's strip, plus a per-message read state persisted per user, the paint overlay (a pure function of read state and times), a brief endpoint that refreshes on Balanced updates and returns clauses with ids, and two pointer gestures on one element.
Trade-off
“Read” means that a card stayed on screen for 350ms, not that you understood it, so a slow scrub can grey out things you skimmed. That is why there is an explicit Mark unread. Dragging and flicking the same card have to be told apart on release (over the composer, or a short fast vertical move). The gestures are invisible until the hint line teaches them, so the buttons have to stay.
Collapsible●●●●○Short threads●●●●○Easy to read●●●●○Mobile●●●○○Consistency●●●●○Dev nav●●●●○Recap●●●●○Read state●●●●●Feedback loop●●●○○Reply-to●●●●○Categories●●●●○
In between

M2 · Chapters

B3's pin lane under a row of named chapters: the recap model cuts the turn into 3–5 chapters with one cited sentence each, and tapping a chapter zooms the strip into it, so 150 updates read as five chapters of thirty.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Final only · 30 updates hidden · the chapters and unread count stay
Working 38m 12s · Running pnpm acceptance:visual↓ live
? #24 Ask Recapture it, the 2px shift is intended. ◆ #18 Decision Agree, but log the stream count on reconnect so we can see it.
Then carry on.
opus-5
▸ What the agent receives
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Readsrc/lib/frontend/stores/sidebar.svelte.ts0.8s
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm test:unit1m 48s
Waiting for the visual run to finish before the Storybook run, so they don't compete for CPU.
Ranpnpm acceptance:visual6m 10s
+ 165 more tool steps and 39 narration lines
Final only · 34 updates hidden · the chapters and unread count stay

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
▸ What the agent receives
Phone · LiveChapters · 9 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Final only · 30 updates hidden · the chapters and unread count stay
Working 38m 12s· Running pnpm acceptance:visual↓ live
? #24 Ask Recapture it, the 2px shift is intended. ◆ #18 Decision Agree, but log the stream count on reconnect so we can see it.
Then carry on.
opus-5
▸ What the agent receives
Phone · SettledZoomed into chapter 4 · 13 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Readsrc/lib/frontend/stores/sidebar.svelte.ts0.8s
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm test:unit1m 48s
Waiting for the visual run to finish before the Storybook run, so they don't compete for CPU.
Ranpnpm acceptance:visual6m 10s
+ 165 more tool steps and 39 narration lines
Final only · 34 updates hidden · the chapters and unread count stay

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
▸ What the agent receives
Desktop · SettingsSurfacing · categories, recap and chapters
Categoriesprominence = weight × Jev p · Key ≥ 70 · Balanced ≥ 40

Retired
+ New category
NameGlyphColourSurfaces whenWeight

Adding a category adds one option to Jev's Choice question, so the cost barely moves. New categories apply to new messages.

Recap & chaptersruns in the daemon
Model
When
Scope
Length
Chapters
Name chapters
Zooming into a chapter opens

Cost: about $0.006 per recap on sonnet-5, $0.03 on opus-5. The daemon runs it; API keys stay server-side.

Feedback ledgerplain files · append-only
This week
23 marks · 14 ▲ · 9 ▼
Most raised
● Finding · 9 ▲
Most lowered
✓ Milestone · 6 ▼
~/.config/conduit/surfacing/feedback.jsonl
~/.config/conduit/surfacing/categories.json
Phone · SettingsTabs · Categories
9:415G ▮▮▮
‹Settings · Surfacing
Categoriesprominence = weight × Jev p · Key ≥ 70 · Balanced ≥ 40

Retired
+ New category
NameGlyphColourSurfaces whenWeight

Adding a category adds one option to Jev's Choice question, so the cost barely moves. New categories apply to new messages.

Builds on
B3's numbered pins and slot, by way of B7's cited recap: the pin lane, strip, loupe, scrub, pager, fold, bar and footnote chips are B7's. There is one new layer, a row of chapter bands above the lane, and one new verb, zoom. The idea is the chapter list of a long video, or a PR split into commits.
Placement
Inside the one activity card, from the top: a breadcrumb (shown only while zoomed), the chapter bands, the pin lane with dashed chapter dividers, the strip and axis, the bar (unread count, ✦ N chapters, the Ask chip, level), then the slot. The slot holds either the chapter recap or one message. Settled, the final reply follows the card.
Live
There are four bands: Scope grows, Unblocking and Streams (all ✓), then Deep link & wait (● 9), whose right edge is dashed and open because the chapter is still being written. The desktop frame is zoomed into chapter 4. The crumb reads “Turn › 4 · Deep link & wait #22–30 · showing All”, the strip and axis span 27:15–38:12, and the slot parks on Ask #24. The phone frame is not zoomed and shows the chapter recap at Key, where chapters 1–3 are clamped to one line because they are read.
Settled
A fifth chapter, Hand-off #31–34, closes the turn, and chapter 4 closes at 38:40. The slot opens on the chapter list: 5 rows, 2 of them unread (● 9, ● 4), and the bar reads ● 13 unread · ✦ 5 chapters. #1 keeps its ▲ badge, and its slot tag reads “▲ you · changed the plan”. The phone frame is zoomed into chapter 4 on the Ask. The final reply is unchanged.
Recap
The recap is the chapter list: number, name, range, unread count, then one cited sentence per chapter (the canonical whole-turn recap, cut at its chapter breaks). Footnotes are the pin numbers, as in B7. A second tab, “Since you last read” (“Since 27:15” live), shows the canonical since-recap. The foot shows the model and update count, plus Show updates and Next unread ⇥. Tapping a chapter name zooms the strip into it. Tapping a footnote opens that message, with “‹ Chapters” to go back.
Read-state
These rules apply per message, as in B3: filled = unread, hollow = read. Opening a message by tap, pager, footnote or number marks it read, and scrubbing only reads the message you release on. A chapter is read when all of its messages are. Its band and row then show ✓, and the row clamps to one line (tap to expand). The row's ✓ button bulk-marks the whole chapter read, and pressing it again restores it. Zooming into a chapter in message mode opens and reads its first unread message (a setting). The unread count always counts messages, never chapters.
Importance-feedback
▲/▼ in the slot foot, as in B3/B7. The message moves one level, its pin carries a badge, the tag reads “▲ you”, and the mark goes to the ledger. There is no feedback on chapters, because their boundaries are the model's call and the ledger tunes categories, not cuts.
Reply-to
There are two grains. ↩ Reply inserts a message token (? #24 Ask). § on a chapter row or in the crumb inserts a chapter token (§ Deep link & wait, dashed). The agent receives Re #24 (Ask, 31:02): "…" for a message and Re chapter "Deep link & wait" (27:15–38:12, #22–#30): "…" for a chapter, with the chapter's sentence as the quote. Tokens sit inline with text, and × or Backspace removes them. “What the agent receives” previews the send.
Categories
These live in Settings, unchanged from the shared card: weights, retire and restore, and custom ⚡ Perf. The readout adds “Chapters are cut from all 34, whatever the level”, so tuning a weight never moves a chapter break. The Recap card gains three settings: Chapters (Auto, 3–5 per turn / One per 10 minutes / Off, one paragraph), Name chapters (At each break / When the turn settles), and what zooming opens.
Collapsible
▾ folds the slot to a one-line peek. Bands, lane and strip stay, because they are the activity strip. Final hides bands, lane, crumb and slot but keeps the bar, so the unread count and ✦ chapters survive. Zoom is itself a collapse along the time axis: ⤢ Out or Esc restores the whole turn.
Short-session-threads
With 3–10 messages, “Auto” produces one chapter, and a single band would just duplicate the strip. So under about 12 messages the bands hide and the design falls back to B7 exactly. The desktop card adds one 24px band row to B7's height, about 11 lines unfolded and 6 folded.
Easy-to-read
Chapter names are two or three words in 11–13px bold, and each has one sentence. Read chapters shrink to a line, so the eye lands on the ● rows. The bands and lane share an x axis, so a pin is visibly “under” its chapter, and a dashed divider runs down through the lane. Hovering a row lights its band, and hovering a footnote lifts its pin.
Mobile-friendly
Bands are 32px tall with 12px labels, which truncate with an ellipsis on narrow chapters but keep their number and ● count. Pins are B7's phone pins (24px, merging within 28px). Chapter rows drop the time range and the word “unread”, and their ✓ and § buttons are 32px. The crumb's actions wrap to their own right-aligned line. Zooming turns a crowded strip into 7–9 roomy pins, which is the main reason this concept helps on a phone.
Consistency
These are identical on desktop and phone: bands, dividers, lane, the pin states, bar order, chapter rows, the crumb's contents, both reply grains and the keys. The divergences are these. (1) Bands, pins and row buttons grow, and the merge distance widens, for touch. (2) Rows hide the range and the word “unread”, to fit one line. (3) The crumb actions wrap onto a second line, because the phone is too narrow for name plus five buttons. (4) The level segment says “Bal”. (5) Settings become three tabs (Categories, Chapters, Ledger).
Dev-Navigation-Ease-And-Speed
These are B7's keys: ←/→, a, r (recap), u (next unread) and digits plus Enter. M2 adds ]/[ for the next or previous chapter (zooming in), z to zoom into the current message's chapter or out again, and Esc to zoom out. Bands are buttons, so Tab reaches them. The fastest catch-up is to read the ● rows, press ] into the unread chapter, then press u through it.
At-150
This is the case chapters exist for. 150 updates become five chapters of about 30, and the recap stays five sentences. Zooming into a chapter shows about 30 messages across the full strip width. Because zoom adds one level of detail (Key shows Balanced, Balanced shows All), the pins inside stay readable without the merge pills B3 needs at 150. The unread count still counts messages (“41 unread”), and each band carries its own ● n, so you know which chapter to open. § tokens stay short however big the chapter is. If a turn outgrows 5 chapters, “Auto” groups them into parts, which is not drawn here.
Build-cost
M+. B7's lane and recap, plus a recap endpoint that returns chapter boundaries (message ids) with names and one cited sentence each. Boundaries must be stable while live (only the open chapter may move). There is also a zoomed x-scale (a window over the same layout function), chapter read-state derived from message read-state, and a chapter token kind.
Trade-off
Chapters are the model's cut, and a bad cut hides the thing you needed inside a chapter marked ✓. Chapter ✓ makes that worse, because it marks messages read that nobody opened. Zoom is one more mode to keep track of, although the crumb and Esc make it cheap to leave. On short turns the layer is pure overhead, so it has to switch itself off.
Collapsible●●●●○Short threads●●●●○Easy to read●●●●○Mobile●●●○○Consistency●●●●○Dev nav●●●●○Recap●●●●○Read state●●●●○Feedback loop●●●○○Reply-to●●●●○Categories●●●●○
In between

M3 · Composer lanes

B3's numbered pins and slot, where Reply opens a lane in the composer for that message: a tab with the message quoted on top and its own draft, while the General lane holds the outline of tokens and free text. The pins wear ✎ while a lane is open, and one Send compiles every lane into a single structured message you preview first.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Readsrc/lib/frontend/stores/sidebar.svelte.ts0.8s
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm test:unit1m 48s
Waiting for the visual run to finish before the Storybook run, so they don't compete for CPU.
Ranpnpm acceptance:visual6m 10s
+ 165 more tool steps and 39 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

Phone · Live9 unread · #1 open · 2 lanes
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Phone · Settled13 unread · ✦ recap open
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Readsrc/lib/frontend/stores/sidebar.svelte.ts
Ranpnpm acceptance:visual
+ 168 more tool steps and 41 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

Desktop · SettingsSurfacing › Categories · rank sets weight
Phone · Settings32px controls
9:415G ▮▮▮
‹Settings · Surfacing
Builds on
B3. The pins, clustering, loupe, slot, pager and keys are kept and rescoped. What changed:
  • Pins filter by prominence (weight × p) instead of urgency.
  • Pins carry an unread dot, a ✎ lane badge and a ▲/▼ mark.
  • A ✦ recap pin sits in a gutter at the right end of the lane.
  • The Ask chip, the unread chip and the level control moved from the slot footer to an axis line under the strip.
  • The new idea lives in the composer: lanes.
Placement
Everything about the messages stays inside the one activity card: the pins over the strip with ✦ at the right end, the axis line (? #24 · ● 9 unread · Final/Key/Bal/All), then the slot with the importance slider and ↩ Reply. The final reply follows the card. Reply lanes live in the composer, which is the only surface outside the card.
Live
New pins drop in at the right. The mockup shows the slot on #24, which you're answering. The composer is mid-draft in lane #18, with lane #24 filled in and the preview open. #24 and #18 both wear ✎.
Settled
With unread messages, a settled turn opens on the ✦ recap in the slot. Since you last read and Whole turn switch its scope. The composer is idle: with no lanes open, the tab row disappears and it's today's composer.
Recap
  • Opening it: tap ✦ at the right end of the lane. Its badge counts the unread messages it covers. The recap reads in the slot, and the caret points at ✦.
  • Citations: #24 chips open that message in the slot and flash its pin, switching to All if the message is below the current level. A "‹ ✦ Recap" pill takes you back.
  • Settings: Recap sets model, when, scope and length. With the default "On request", pressing ✦ generates the recap (about 1.6s) and caches it until new messages arrive. The mockup shows it already generated.
Read-state
  • What marks a message read: moving the slot off it (you saw it and moved on), or "Mark 13 read" under the recap. Opening the recap alone marks nothing.
  • How unread shows: a white dot on the pin or group, a "● Unread" pill in the slot, and "● 9 unread" on the axis.
  • Toggling: the pill (or u) switches a message between read and unread.
  • Next unread: the axis chip (or n) jumps there. It switches to All when needed, because 7 of the 9 unread are All-level.
Importance-feedback
  • The slider: it sits in the slot footer, with stops Fold · All · Bal · Key. ◇ is Jev's prominence on the scale and ● is yours, joined by a line when you disagree. #1 shows ◇ at 37 (All) and ● at Bal, with "changed the plan".
  • One level per mark: following the scenario rule, only the neighbouring stops are live. ▼/▲ step between them.
  • What changes: each move appends one line to feedback.jsonl, with an optional "why?" typed right there. The pin gains ▲ or ▼ and joins or leaves the current level at once. Category weights never change.
  • How the data is used: the ledger is summarised in Settings › Feedback ledger, for you or an agent to read.
Reply-to
  • Replying: ↩ Reply (or r) opens a lane, a composer tab such as "? #24". The message is quoted at the top of the lane, with its own draft below. It also drops a .ref token at the caret in the General lane.
  • General is the outline: tokens and free text, in the order they will be sent. You can type before, between or after the tokens.
  • Removing: × on a token or a tab, or Backspace over a token, closes both the token and its lane.
  • Sending: ↑ first opens a preview of exactly what the agent receives, in the canonical Re #24 (Ask, 31:02): "…" blocks. Send then sends it as one message.
Categories
Settings › Categories is a ranked list, where order sets weight.
  • Weight: drag ⠿, or use ↑/↓. Landing inside a band ties the row with it, for example 60 with Replan, Correction and Decision. Landing between two bands takes their midpoint (65, 75, 90). The number and bar update as you go.
  • Threshold lines: two lines explain rank. Rows above the Key line (≥ 70) can reach Key. Rows below 40 can only ever show at All.
  • Built-ins: Retire moves a row to Retired, where Restore brings it back. ↺ resets the weight.
  • Custom: ⚡ Perf has Edit (name, glyph, colour, definition) and Delete. "+ New category" adds a row at 50, already in edit mode.
Collapsible
  • Folding the slot: ▾ folds it to a one-line peek. The pins, strip and axis stay, which is 3 lines.
  • Final: hides the pins and the slot, leaving the strip and ✦.
  • The composer: the preview folds, and closing the last lane removes the tab row.
Short-session-threads
A settled turn with the recap open is about 11 lines on desktop (head 1, pins and strip 2, axis 1, slot 6 or 7) and about 15 on the phone. Folded, it's 5 lines on desktop and 6 on the phone. It's all one card, and lanes add nothing to the thread.
Easy-to-read
One message at a time at 13.5px, never clamped. The type label is large. The lane's quote header keeps the message you're answering in view while you type, which inline tokens can't do. The recap reads in the same slot, in the same type.
Mobile-friendly
  • Pins: 24px, and the whole 40px strip is a tap target that picks the nearest pin, with the loupe on drag (from B3).
  • Controls: lane tabs, slider arrows and footer buttons are 32px, and the tabs scroll horizontally.
  • What doesn't fit: the slider footer wraps to 2 rows. The Live composer, at about 150px with 2 lanes, is taller than today's.
Consistency
Identical on both: pins, ✦, the axis chips, the slot, the slider, lanes, the preview and the ranked list, including its ↑/↓ and drag. Divergences:
  • On the phone, the pager moves to the slot footer and the axis drops its 00:00/now labels, both for width.
  • The phone preview starts closed, because opening it pushes the thread off screen. Send still opens it before anything is sent.
  • On the phone, the ranked rows wrap to 3 lines.
Dev-Navigation-Ease-And-Speed
  • #7: type 7 ↵, which switches to All because #7 is All-level (28). On the phone, drag the loupe.
  • The Ask: a, or the "? #24" chip, which is one tap on both.
  • Next unread: n or the chip.
  • Other keys: r replies and u toggles unread.
At-150
  • Pins: they become group pills, as in B3.
  • Recap: ✦ is unchanged, and the recap still covers only what's unread ("✦ 41").
  • Unread: the count scales, and next-unread keeps triage linear.
  • Reply references: past about 4 lanes the tab row scrolls. The General outline and the preview are the check that the order is right.
Build-cost
L. It needs:
  • Per-message read state in the event store.
  • A feedback.jsonl writer.
  • A daemon-side recap call.
  • Rank-derived weights in categories.json.
  • A composer that keeps several drafts (persisted per session) and compiles them into one structured message.
Trade-off
  • Hidden drafts: lanes keep each answer focused and give it room, but the tabs hide the other lanes' text, so you can't see the whole message without Preview. Inline tokens get that for free.
  • Extra steps: a quick "yes" to #24 costs a lane plus the preview.
  • The slider: it explains Jev against you well, but it's a heavier control than two arrows for a one-level move.
  • Ranking: rank-as-weight is intuitive for order and clumsy for exact numbers.
Collapsible●●●●○Short threads●●●●○Easy to read●●●●○Mobile●●●○○Consistency●●●●○Dev nav●●●●○Recap●●●○○Read state●●●●○Feedback loop●●●○○Reply-to●●●●○Categories●●●○○
In between

M4 · Teach mode

M2's scrub strip, where every card has a "Disagree?" control with raise, lower or retype and a reason. Each mark is one line in a ledger you can read. From that ledger an agent proposes a diff to the category definitions and weights, which you accept hunk by hunk, alongside what the diff would have changed in this turn.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
Ranpnpm test:unit1m 48s
Runningpnpm acceptance:visual 1m 17s
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
Searchedsession_switched1.2s
Let me read the sidebar store.
Readsrc/lib/frontend/stores/sidebar.svelte.ts0.8s
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm test:unit1m 48s
Waiting for the visual run to finish before the Storybook run, so they don't compete for CPU.
Ranpnpm acceptance:visual6m 10s
+ 165 more tool steps and 39 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

Phone · Live9 unread · #1 marked ▲
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
Phone · Settled13 unread · recap open
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
Readsrc/lib/frontend/stores/sidebar.svelte.ts
Ranpnpm acceptance:visual
+ 168 more tool steps and 41 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

Desktop · SettingsSurfacing › Feedback ledger · proposal open
Phone · SettingsFeedback ledger
9:415G ▮▮▮
‹Settings · Surfacing
Builds on
M2. The scrub strip, snapping thumb, two-row pins, level switch and Ask chip are kept. What changed:
  • The card now sits in normal flow under the axis, in both live and settled turns. A long card or an open teach panel pushes the thread down instead of covering it.
  • Pins carry an unread dot and your ▲/▼/⇄ mark.
  • The axis gains "● 9 unread ⇥" and "✦ Recap".
  • Every card gets Disagree? in its footer.
Placement
Everything is in the activity card, under the strip. The ledger and proposals live in Settings › Surfacing › Feedback ledger, because tuning categories is a weekly job, not something done mid-turn.
Live
The desktop frame shows the dev, having jumped through unread (so the level is All), disagreeing with #26. Jev called it a routine ● Finding (26, All level), but it's the explanation for the iOS bug users saw. ▲ is picked, a reason is typed, and the exact JSON line is shown before it's saved.
Settled
With 13 unread, the card opens on the recap. The ▲ mark on #1 sits on its pin (●1▲), and the card for #1 carries a "▲ you · changed the plan" chip with Undo.
Recap
Conventional. "✦ Recap" on the axis opens a recap card with numbered citations, where Since you last read and Whole turn switch the scope. A citation moves the thumb and card to that message, switching to All if needed, and "‹ Recap" returns. It's generated by the daemon on request, using the Settings › Recap model.
Read-state
  • What marks a message read: moving the card away from it, or "Mark 13 read" on the recap.
  • How unread shows: a white dot on the pin, NEW on the card header (click it to toggle), and "● 9 unread ⇥" on the axis, which jumps to the next unread one.
Importance-feedback
The design centres on this. Disagree? offers ▲ / ▼ / ⇄ and opens the teach panel:
  • Jev said: the type, with the arithmetic (p .64 × 40 = 26 · All).
  • You say: one level up, one level down, or retype. Retyping recomputes the level from the new type's weight.
  • Why: free text, which autocompletes from reasons you've used before in the ledger.
  • Writes: the exact JSON line.
Save appends the line, so the pin and level move at once. Only this message moves. Weights change only through a proposal you accept in Settings.
Reply-to
Conventional. ↩ Reply on a card (or r) drops a .ref token at the caret in the composer. You can use several and type between them, and × or Backspace removes one. The agent receives the canonical Re #24 (Ask, 31:02): "…" blocks.
Categories
Two ways in:
  • Settings › Categories is a conventional list. Built-ins have a − / + weight stepper, a definition edit, Retire/Restore and ↺. Custom categories, such as ⚡ Perf, also get name, glyph, colour and Delete, and "+ Add category" adds one.
  • The ledger's Propose changes runs sonnet-5 over feedback.jsonl and categories.json and returns a diff. Each hunk comes with its evidence and what it would have changed in this turn. You accept or reject it, then apply. The applied edits show up in the Categories list.
Collapsible
The card folds to nothing (–), leaving 3 lines: head, pins and strip, axis. Final hides the pins too. The teach panel closes on Save or Cancel.
Short-session-threads
A settled turn with the recap is about 12 lines. With the card closed it's 3. The teach panel adds about 7 lines while it's open, which is the cost of doing it in place.
Easy-to-read
One card at 12.5px on desktop and 13px on phone, unclamped. The type label is on the card's coloured top edge. The teach panel is a labelled form (Jev said / You say / Why / Writes), so the dev reads Jev's reasoning as numbers, not vibes.
Mobile-friendly
  • Strip: the pins are 12px, and the 50px strip is one slider that snaps to the nearest pin. The axis wraps to 2 rows.
  • Teach panel: it stacks to one column with 30–32px options, and ▲ ▼ ⇄ in the card footer are 32px.
  • Downside: the ledger grid is dense on 390px, though it fits.
Consistency
The same on both: the card, the teach panel, the ledger, the proposal diff and the categories list. Divergences:
  • The phone teach panel stacks its labels above their fields.
  • The phone axis drops 00:00.
  • The phone's category rows wrap to 3 lines.
Dev-Navigation-Ease-And-Speed
  • #7: press All, then drag or press ← →. There's no direct number jump (inherited from M2).
  • The Ask: a or the "? #24" chip.
  • Next unread: n.
  • Disagree: d.
  • Reply: r.
Scrubbing is quick for finding something, and slow for landing exactly on #7 at All.
At-150
  • Pins: overflow drops to dots, as in M2, and the ledger and proposals don't change. Past about 40 pins at All, the strip turns into dots.
  • Unread: next-unread is the way through.
  • The ledger: more turns means more marks and better proposals. Teach mode improves with scale.
Build-cost
M/L. It needs:
  • The feedback writer and reader, plus the ledger aggregation (a small reduce over the JSONL).
  • A proposal run: one sonnet-5 call (about $0.01) that returns a JSON-patch, rendered as a diff, plus a replay of the turn's stored p values to show "what it would have changed".
  • Per-message read state.
  • The recap call.
Trade-off
It makes Jev's scoring legible and tunable, and it's the only design where your marks change future turns (with your consent, per hunk). Downsides:
  • The teach panel is heavier than B3's one-tap ▲. Most marks won't get a reason, so proposals rest on thinner evidence than the form implies.
  • Retype adds a third mark kind that the scenario didn't ask for.
  • Reply and recap are deliberately plain.
Collapsible●●●●○Short threads●●●●○Easy to read●●●●○Mobile●●●○○Consistency●●●●○Dev nav●●●○○Recap●●●○○Read state●●●●○Feedback loop●●●●●Reply-to●●●●○Categories●●●●●
In between

M5 · Ask the turn

B3's numbered pins and slot, plus one line under the slot where you type to the turn. ✦ Ask answers a question about what happened, with #n citations you can open, reply to or re-rank in place. ⌁ Tell takes a plain-words note about what matters and turns it into one previewed change: a mark, a weight or a category. Replies are inline tokens in today's composer.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Readsrc/lib/frontend/stores/sidebar.svelte.ts0.8s
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm test:unit1m 48s
Waiting for the visual run to finish before the Storybook run, so they don't compete for CPU.
Ranpnpm acceptance:visual6m 10s
+ 165 more tool steps and 39 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

Phone · Live9 unread · #25 open · 2 replies drafted
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Phone · Settled13 unread · ✦ recap open
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Readsrc/lib/frontend/stores/sidebar.svelte.ts
Ranpnpm acceptance:visual
+ 168 more tool steps and 41 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

Desktop · SettingsSurfacing › Categories · a Tell proposal open
Phone · Settings32px controls
9:415G ▮▮▮
‹Settings · Surfacing
Builds on
B3, through M3's build of it: the pins, clustering, loupe, slot, pager, keys, the axis line (Ask chip, unread chip, levels), the ✦ recap pin and the ±1 importance slider are all kept. What's new:
  • An ask line at the foot of the slot, with two modes: ✦ Ask and ⌁ Tell.
  • A third slot view, Answer, beside Message and Recap.
  • Citations in the recap and in answers open a small card instead of jumping.
  • M3's composer lanes are gone. Replies are inline .ref tokens in today's composer.
Placement
Everything stays in the activity card. The ask line sits under the slot, above the card's bottom edge, so it reads as "ask about this turn" rather than "message the agent". Answers and proposals replace the slot's contents, with the question in italics where the timestamp usually is. The composer below the card is unchanged apart from reply tokens.
Live
The slot is on the Ask #24, which you're answering. The draft in the composer holds ? #24 and ◆ #18 tokens with text around them. Both pins wear ✎. The ask line offers live questions: "What is it doing now?", "Anything risky since 27:15?", "What should I review first?". Answers use only what's surfaced plus the log so far, and say so.
Settled
The mockup shows a question already asked: "What happened to the legacy path?". The answer cites #6, #33, #31 and #34 and says what nothing in the turn covers (the flag-on path is untested). "Clear" returns to the recap, which is where a settled turn with unread messages opens.
Recap
  • Opening it: ✦ at the right end of the lane, as in M3. Its badge counts the unread messages it covers.
  • Asking beyond it: the recap is the default answer. The ask line is for the question it didn't answer. Settled suggestions are "What happened to the legacy path?", "Why isn't phone-sidebar recaptured?" and "What should I review first?".
  • Citations: #24 opens a card with the message's type, time, level and first three lines, plus Open, ↩ Reply, ▲ and ▼. Open puts it in the slot with a "‹ ✦ Recap" or "‹ ✦ Answer" pill back.
  • Settings: Recap sets model, when, scope and length, and can turn Ask off.
Read-state
As in M3.
  • What marks a message read: moving the slot off it, or "Mark 13 read" under the recap. Reading a recap or an answer marks nothing, and neither does opening a citation card.
  • How unread shows: a dot on the pin, a "● Unread" pill in the slot and "● 9 unread" on the axis.
  • Next unread: the axis chip or n.
Importance-feedback
  • Direct: the ±1 slider in the slot, or ▲/▼ on any citation card, without leaving the answer. Each writes one line to feedback.jsonl.
  • In words: ⌁ Tell with "#26 matters more" proposes a one-level raise for #26, with your words as the reason. Accept applies it and the pin gains ▲.
  • Category-level: "Milestones are noise" proposes Milestone 40 → 30 and shows what it changes in this turn (nothing: #34 and #21 were already All). Accept writes categories.json and Settings updates.
  • Nothing silent: every Tell ends in a card with Accept or Dismiss. Tell never edits without it.
Reply-to
  • Replying: ↩ Reply in the slot, ↩ Reply on a citation card, or r drops a ? #24 Ask token at the caret in the composer. You type around it.
  • Already in the draft: the button reads "✎ In draft" and puts the caret after the existing token instead of adding a second one.
  • Removing: × on the token.
  • Sending: today's Send. The tokens compile to Re #24 (Ask, 31:02): "…" blocks.
Categories
Settings › Categories, with ⌁ Tell at the top.
  • Tell: "Flaky or skipped tests are their own thing" proposes ≈ Flaky at weight 50 with a definition, and explains where 50 sits (Balanced, never Key). Accept adds the row, highlighted, with "⌁ from …" underneath so you can see where it came from.
  • When it already exists: "treat perf numbers as their own thing" answers that ⚡ Perf (70) already covers it, from your note on Sep 19, and changes nothing. The Perf row carries that provenance.
  • Retiring: "Stop surfacing corrections" proposes retiring ↶ Correction and lists this turn's Corrections that would fold. Ask can't be retired.
  • Manual: the conventional list is still there, with −/+ by 5, ↺ reset, Edit, Retire or Delete, and "+ Add category".
Collapsible
▾ folds the slot to a one-line peek and hides the ask line with it, so a folded card is 5 lines on desktop. Final hides pins, slot and ask line. Answers are one slot view, never stacked.
Short-session-threads
A settled turn with the recap open is about 13 lines on desktop (head 1, pins and strip 2, axis 1, slot 6, ask line 2) and about 17 on the phone. Folded, 5 and 6. Answers are not saved to the thread, so asking adds nothing to history.
Easy-to-read
One message, recap or answer at a time at 13.5px. Answers are short paragraphs or a numbered list, and they cite rather than quote, so the card stays small. The citation card shows three lines of the source without moving the slot off the answer.
Mobile-friendly
  • Pins: 24px, and the whole strip is a tap target with the loupe on drag.
  • Ask line: 32px mode buttons ("✦ Ask" and "⌁ Tell"), field and send. Suggestions scroll sideways on one row.
  • Citation card: 30px actions, clamped to the slot width.
  • Cost: the ask line adds about 70px to the card, which the phone feels most.
Consistency
Identical on both: pins, ✦, axis, slot, slider, ask line, answers, citation cards, reply tokens and the Tell box. Divergences:
  • The phone labels the mode "⌁ Tell" instead of "⌁ Tell conduit what matters", and hides the slot's type label in answer view.
  • Suggestions wrap on desktop and scroll on the phone.
  • The pager moves to the slot footer on the phone.
Dev-Navigation-Ease-And-Speed
  • #7: 7 ↵, switching to All if needed. On the phone, drag the loupe.
  • The Ask: a or the "? #24" chip.
  • Next unread: n.
  • New: / focuses the ask line. "Where did it decide the flag?" is often faster than scrubbing, and the answer's citation is one click from the message.
At-150
  • Pins: group pills, as in B3.
  • Ask: scales best of anything here. A question costs the same at 150 messages as at 30, and answers still cite a handful.
  • Recap: still scoped to unread.
  • Tell: unaffected by message count.
Build-cost
L. It needs:
  • Per-message read state in the event store.
  • A feedback.jsonl writer that also records Tell notes.
  • Daemon-side recap and ask calls (one sonnet-5 call each, over surfaced messages plus the log).
  • A Tell call that returns one structured change (mark, weight, add, retire, or none) against categories.json, plus the preview of its effect on the open turn.
  • Inline reply tokens in the composer, which are cheaper than M3's lanes.
Trade-off
  • Height: the ask line costs two lines on every expanded card, even when you never ask.
  • Parsing: Tell reads natural language, so it can misread. Every Tell ends in a proposal you accept, and it falls back to "I couldn't turn that into a change" with examples, but a wrong-but-plausible proposal is still possible.
  • Trust: answers can be wrong about the turn. They cite every claim so you can check, and they say when nothing covers the question.
  • Two ways in: importance can be changed three ways (slider, card, Tell), which is more to learn than M3.
Collapsible●●●●○Short threads●●●●○Easy to read●●●●○Mobile●●●○○Consistency●●●●○Dev nav●●●●○Recap●●●●○Read state●●●●○Feedback loop●●●●○Reply-to●●●●○Categories●●●●○
In between

M6 · Weighted lane

The pin lane becomes a bar lane: each surfaced message is a bar as tall as its prominence, standing against the Key 70 and Balanced 40 lines, so you drag a bar to re-rank one message and drag a category's column to watch all of its bars cross the lines.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Editedsrc/lib/frontend/utils/history-logic.ts4s
Ranpnpm acceptance:visual6m 10s
+ 166 more tool steps and 40 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Phone · Live30 surfaced · 9 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Phone · Settled34 surfaced · 13 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs
Ranpnpm acceptance:visual
+ 169 more tool steps and 41 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

44m 30s · $6.80
opus-5↑
Desktop · Settings › Surfacingdrag a column to reweight
Phone · Settingssame columns, same lines
9:415G ▮▮▮
‹Surfacing
Builds on
B3. The numbered pin lane becomes a bar lane on the same time axis: a bar per surfaced message, its height the prominence (weight × Jev p), against two horizontal lines, Key 70 and Balanced 40. Numbers ride on top of every bar at or above the chosen level. B3's slot, pager, caret, fold, level control and number-jump stay. B3's clustering goes: bars are 5px wide, so 34 fit without merging. The loupe survives, but only on the phone.
Placement
All inside the one activity card: head (with "9 unread ⇥"), bar lane, unread dots under the baseline, the duration strip, an axis row (level, ⚖ Weights, ✦ Recap, ? #24), then the slot. The final reply follows the card, and the composer sits under it as usual.
Live
New bars rise at the right edge, and the slot follows the newest (#30) until you pick another. The "9 unread" pill steps through the unread bars. The level lines show what Balanced would hide: bars below the lit line are drawn at a quarter opacity rather than removed, so you see what's being filtered.
Settled
The slot opens on the recap, "Since you last read" (13 updates). #1 carries a hatched cap from its own 37 up past the Balanced line, labelled "1▲": that's the "▲ you" mark, visible without a tap. The step log opens from the head.
Recap
Conventional. ✦ Recap on the axis turns the slot into the recap, with a scope switch (Since you last read, Whole turn; Live offers only the first). Each [#n] chip selects that bar and shows the message, and "‹ Recap" goes back. Footer: model, count, time, cost. Settings › Recap sets the model (sonnet-5 by default), when, scope and length.
Read-state
A message you open in the slot becomes read after 1.5s. "Mark unread" or m toggles it back, and the recap's "Mark 13 read" clears the lot. Unread shows as a white dot under the bar, a "● unread" chip in the slot, and the count in the head pill and figure caption, which update together.
Importance-feedback
Drag a bar up or down. It follows your pointer, clamped to one level, and on release it snaps to the next level: a hatched cap shows how far you lifted it above Jev's height, and a dashed ghost shows how far you pushed it down. ▲ More / ▼ Less in the slot, or ↑/↓ on the focused lane, do the same. A toast confirms "▲ #26 All → Balanced · appended to feedback.jsonl · Undo". Each mark writes one JSON line (msg, text, Jev's type and p, mark, reason). A mark never changes a weight.
Reply-to
↩ Reply (or r) drops a .ref token at the composer's caret, or at the end if the caret was never placed. Click another bar, Reply again, type between them. × or Backspace removes one. The desktop "Preview" shows exactly what the agent gets: a "Re #24 (Ask, 31:02): …" quote block before each typed answer.
Categories
Weights are columns. Each column shows its weight as a bar, with this turn's messages of that type as dots at weight × p, plotted against the 70 and 40 lines. Drag a column and the dots, and the lane's bars, cross the lines live, and the level counts update. It works in the card (⚖ Weights) and in Settings › Surfacing, where the rows add Retire/Restore and Reset for built-ins, Edit/Delete for customs (⚡ Perf), and "+ New category". Reweighting is free because p is stored. A new category needs Jev to rerun, so it offers a $0.02 dry run on this turn.
Collapsible
▾ folds the slot to a one-line peek. The lane stays, because it is the activity strip. Final (level control) removes the bars and the slot, leaving today's plain strip and "Final only · 34 hidden · Show". Older turns open folded.
Short-session-threads
Desktop: head, lane (about 4 lines tall with labels), strip and axis come to about 7 lines, and the slot about 5 more, so 12 in all, or 8 folded. Phone: about 16, or 10 folded. The lane is 2 lines taller than B3's pin lane. That's the cost of the height channel. It's all one activity card.
Easy-to-read
What matters is literally taller, and the lines say why it's shown. The slot reads at 13.5px, full text, with the arithmetic beside the actions ("p .83 × 40 = 33 → All") so no rank is a mystery. The weak spot is the numbers: at 10px on bar tops, some low bars lose their label to a neighbour. The slot and a hover title fill in.
Mobile-friendly
Bars shrink to 3px, which no finger can grab, so a touch on the lane opens a ×5 loupe above it. In the loupe the bars are 9px wide and every one is numbered. Drag sideways to scrub, and drag a bar vertically to re-rank it there. The slot's actions are four 32px buttons with labels. The composer keeps its full width, with tokens at 12px.
Consistency
Identical on both: the bar lane, lines, labels, unread dots, slot actions, recap-in-slot, toast, and the weight columns in settings. Divergence: the phone re-ranks inside the loupe rather than on the lane (a 3px bar can't be dragged), and it drops the Preview toggle and the in-card ⚖ Weights, leaving weights to settings. The phone's pager moves into a footer row with the arithmetic.
Dev-Navigation-Ease-And-Speed
#7: type 7 on the focused lane (or click its bar, dim at Balanced but still clickable). The Ask: a, or the ? #24 chip, one click. Its glowing bar is the tallest, so it's also easy to aim at. Next unread: u or the "9 unread ⇥" pill, one click each. Phone: the ? #24 and unread pills, one tap each. #7 takes a loupe touch and a tap.
At-150
At 150, 5px bars overlap on desktop (around 3.5px per message over 530px), so desktop also uses the loupe for any stretch denser than 6px per bar. The height channel still reads at a glance: the tall spikes are the Asks and blockers. Labels shrink to Key and Balanced only. The unread count, recap citations and reply tokens are unchanged: numbers are stable, and cites select the bar and open the loupe on it.
Build-cost
M. It needs B3's timestamps plus p stored per message (already there for levels), a canvas-or-DOM bar lane, vertical-drag handling, the loupe, and the weights editor wired to the lane. Storage: feedback.jsonl appends and categories.json. The recap needs one model call per request.
Trade-off
It's the most honest picture of why something surfaced. The cost: it's a chart in the transcript. Two extra lines per turn, bar labels that fight at density, and a gesture (vertical drag) that has to be discovered, though ▲/▼ sit beside it. The in-card weights editor is powerful, and also easy to misread as "just this turn": the toast says "all sessions".
Collapsible●●●●○Short threads●●●○○Easy to read●●●●○Mobile●●●○○Consistency●●●●○Dev nav●●●●○Recap●●●○○Read state●●●○○Feedback loop●●●●●Reply-to●●●●○Categories●●●●●
In between

M7 · Review mode

The turn stays one compact M2 strip. Catching up is a separate, deliberate mode: "Review 13 ▸" opens a focused reader with the recap and its citations beside the message card, triage keys, a read-progress bar, and a reply tray that drops into the composer when you leave.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Let me read the sidebar store.
Ranpnpm acceptance:visual6m 10s
+ 167 more tool steps and 40 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Phone · Live30 surfaced · 9 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Phone · Settled34 surfaced · 13 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs
Ranpnpm acceptance:visual
+ 169 more tool steps and 41 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

44m 30s · $6.80
opus-5↑
Desktop · Settings › Surfacinglist and detail
Phone · Settingslist, then pushed detail
9:415G ▮▮▮
‹Surfacing
Builds on
M2. It keeps the scrub strip (pins in two rows above the duration strip, a thumb, the Ask chip, levels) and its pop-up card, which gains ▲ ▼ ↩ ○. New here is a separate review mode: the catch-up happens in a reader you open, not in the transcript.
Placement
Transcript: one activity card holding the head (with "Review N ▸"), the strip, and a card above it when a pin is picked. Review on desktop is a two-pane panel docked under the final reply, standing in for the composer while open (the composer and its draft come back on exit). On phone it's a full-screen pager over the session.
Live
The strip grows and the card follows the newest (#30) until you pick a pin. "Review 9 ▸" counts the unread. Review works mid-turn: its queue is fixed when you open it, and new arrivals join at the end, so nothing reshuffles under you.
Settled
Review opens on "Since you last read": 13 in the queue, the recap on the left, #22 on the right. The strip above still shows #1 labelled "1▲" (your mark), and the Whole-turn queue lists #1 with its ▲. The final reply sits above the panel, unobstructed.
Recap
The recap is the first thing in review: the left pane on desktop, page 0 on phone. Scope switch: Since you last read, Whole turn. Each [#n] opens that message in the card pane (desktop) or jumps to its page (phone), and the cite for the open message stays highlighted, so the recap works as a table of contents. Footer: model, count, time, cost.
Read-state
Review is where reading happens, so progress is explicit: a bar and "4 / 13 read" in the review header. Moving past a card (j, e, ›, a swipe) marks it read. u or "○ Mark unread" undoes that. The transcript's pins carry a white unread dot, and the Review button and captions show the live count. A pin opened in the transcript card becomes read after 1.5s.
Importance-feedback
▲ More / ▼ Less on every card, in both the transcript and review (+ / − in review). A mark moves the message one level and writes a line to feedback.jsonl, and the toast has Undo. The card shows "Balanced · ▲ you → Key". A mark never changes a category weight.
Reply-to
In the transcript card, ↩ Reply drops a .ref at the composer's caret, the same as everywhere. In review, ↩ Reply (r) adds the message to a reply tray with a one-line field for your answer, so you can reply to five things without leaving. "Done · drop 2 replies into composer" (or Esc) exits and inserts each token and its text at the caret, ready to edit before sending. Nothing is sent automatically.
Categories
Conventional: Settings › Surfacing › Categories is a list with a detail pane. Selecting a category shows its weight slider, ticked at the Key 70 and Balanced 40 thresholds, with its effect on this turn ("1 of 12 Findings clear Balanced. Balanced shows 16 in all", updating as you drag), its definition, and Retire, or Restore once retired. The custom ⚡ Perf adds Edit and Delete. "+ New category" gives glyph, name, colour, definition and weight, and a $0.02 dry run on this turn. On phone the list pushes to the detail.
Collapsible
The card closes by tapping the pin again or with Esc, leaving just the strip (M2's compact form). Final (level) hides the pins and shows today's plain strip. Review mode is itself the "expanded" state and closes with Esc.
Short-session-threads
The shortest of mine. Closed, the turn is the head, two pin rows, the strip and the axis: about 5 lines on desktop, 6 on phone, with the card adding about 5 when open. Review adds nothing to the thread's length on phone (it's an overlay). On desktop it replaces the composer while you're in it.
Easy-to-read
Review reads like an inbox: one message at 13px (14.5px on phone), full text, its type and time, the recap beside it as context, and a queue with unread bold, read grey. The transcript card clamps nothing. What it gives up is the in-place scan: to read three messages without review you still scrub pins one at a time.
Mobile-friendly
The pager suits a phone well: one message per screen, swipe or ‹ › (36px), a 2×2 grid of 38px actions, reply fields inline under the card, and a sticky "Done ▸" with the tray count. The strip and card in the transcript use M2's phone sizes (12px pins, 32px card actions).
Consistency
The same model on both: a strip in the thread, review as a mode with recap first, the same card actions, the tray, and Done drops into the composer. The difference is form: two panes on desktop, pages on phone (the recap is page 0 rather than a side pane). Triage keys are desktop-only.
Dev-Navigation-Ease-And-Speed
#7: scrub to its pin, or in review switch to Whole turn and click its queue row. Two clicks, no typing. The Ask: the "? #24" chip on the strip, one click, and in review it's the first cite in the recap. Next unread: "Review ▸" then j / e, the fastest triage of the set once you're in. Phone: Review, then swipe. #7 on phone takes a scrub.
At-150
The strip is M2's, so at 150 the pins merge into dots and the card is the only way to read one. Review is what scales: a queue of 40 unread scrolls, triage keys stay one keystroke per message, and the recap cites become the way in. The tray and progress are unchanged. #n stays stable in cites, tokens and queue rows.
Build-cost
M. M2's strip plus a review panel and pager: queue state, a keyboard map, the tray (a local draft store per session, so the tray survives a reload), and one recap call per open. No new storage beyond feedback.jsonl and categories.json.
Trade-off
It separates reading from watching: you're never forced to triage in the transcript, and when you do triage it's fast and keyboard-first. The cost is a mode. Things you'd glance at get hidden behind a click, and the tray is a second draft place, which can surprise you ("where did my reply go?"). The Done label names the count to soften that. On desktop the panel displacing the composer is a real cost while the agent is live, so Live keeps the draft untouched.
Collapsible●●●●●Short threads●●●●●Easy to read●●●●○Mobile●●●●○Consistency●●●●○Dev nav●●●●○Recap●●●●○Read state●●●●●Feedback loop●●●○○Reply-to●●●○○Categories●●●○○

Off the wall

Deliberately unusual. Some are unshippable as they stand, but each hides an idea worth stealing.

Off the wall

W1 · Newspaper edition

The turn stays one compact block with a Stop Press ticker. The recap is printed as a front page: a masthead, a headline, the recap as the lede, and desk columns in which every story is a cited message. You rank a story by dragging it above or below the fold, and read ones go grey like visited links.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits · 51 runs44m 30s · $6.80
Searchedsession_switched1.2s
Ranpnpm acceptance:visual6m 10s
+ 168 more tool steps and 40 narration lines

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Phone · Live30 surfaced · 9 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Working38m 12s — Running pnpm acceptance:visual
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Phone · Settled34 surfaced · 13 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
›Worked· 18 searches · 64 reads · 37 edits
Ranpnpm acceptance:visual
+ 169 more tool steps

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

44m 30s · $6.80
opus-5↑
Desktop · Settings › Surfacingsections in column inches
Phone · Settingssections
9:415G ▮▮▮
‹Surfacing
Builds on
M2, compressed further. The turn keeps one strip with a tick per surfaced message (a white dot while unread). M2's pop-up card becomes a one-line Stop Press ticker with ‹ › ▲ ▼ ✉ ○. The new part is the edition, an opened mode that renders the recap and the surfaced messages as a front page.
Placement
Transcript: one activity card (head, strip, ticker, and an "Extra!" line that opens the edition). The edition unfolds as a sheet under the final reply and above the composer (on phone, at the end of the thread), then folds back to the Extra! line. Stories carry their own actions, so the composer is never covered.
Live
Stop Press shows the newest story (#30), and ‹ › steps through the others. "Extra! 9 unread since 27:15" opens a Late edition marked "still printing", which uses the live recap. The Full edition is disabled until the turn ends, because a front page for a running turn would re-lay itself out under you.
Settled
The Late edition, 44:30, is open. The masthead reads "The Typed Session List". The headline is "One question waits on you as the deep link ships". The lede is the since-you-last-read recap. The lead is #24 ("Waiting on you"), followed by the Decisions and Milestones desks, the fold, and "In brief". "Full" swaps to the whole-turn edition, where #1 sits on the Findings desk above the fold with "▲ you" in its kicker.
Recap
The recap is the lede, with a drop cap and a byline for model, count, time and cost. Every [#n] cites a story on the same page: clicking one scrolls to that story, flashes it and marks it read. Late means since you last read; Full means the whole turn. Settings › Editorial sets the writer model, the print run (when), the edition (scope), the length and the cost.
Read-state
Unread headlines are bold. Read ones turn grey and light, like visited links, so the page itself shows what's left. Opening a headline, following a cite, or stepping the ticker past a story marks it read. ○ marks it unread again. The count lives on the Extra! line and the captions. The strip ticks carry a white dot while unread.
Importance-feedback
Drag a story across the page: onto the lead (Key), above the fold (Balanced), into In brief (All), or onto Spiked (the log). ▲/▼ do the same one step at a time, and are the only way on touch. Each move re-lays the page, adds "▲ you" to the kicker, and appends a line to feedback.jsonl, with Undo in a toast. Section weights are never changed by a move; the ledger feeds the "Ask an agent to tune" flow.
Reply-to
✉ "Letter to the editor" on a story or the ticker clips a .ref to the composer caret, so several go in with your text between them. × or Backspace removes one. The agent receives the kit serialisation: Re #24 (Ask, 31:02): "…" blocks in place of each token.
Categories
Settings › Sections: each category is a newspaper section on a desk, and its weight is shown as column inches (weight ÷ 10). A story runs to inches × Jev's p: 4 in makes the front page, 7 in leads. Use − and + (0.5 in steps) or drag the ruler. Retiring a built-in moves it to Discontinued with Restore. "+ Launch a section" takes glyph, colour, name, desk, definition and inches, with a dry run. ⚡ Perf and any new section get Edit and Fold (delete).
Collapsible
"Fold ▴" closes the edition back to the Extra! line. The ticker can't be hidden, but it's one line. In brief items are one line until you open one. Spiked is a closed <details>.
Short-session-threads
Folded, a settled turn is 4 lines on desktop (head, strip, ticker, Extra!). On phone it's 6 (the ticker actions take a row) or the 5-line masthead teaser. The edition is a mode, not part of the thread.
Easy-to-read
This is where the design is strongest: serif headlines, desks, and a lead that is obviously the thing to answer. Read ones grey out, so you scan for the black. The weakness is that headlines are cut automatically at the first sentence break, so some read awkwardly (#6 is one long sentence), and a real build would need the recap model to write them.
Mobile-friendly
One column, with 32px action grids on each story and a 38px segmented toggle. HTML5 drag-and-drop doesn't work on touch, so on phone the fold is reached with ▲/▼ only, which loses the signature gesture. The edition is long on phone: about 3 screens for the Late edition.
Consistency
The masthead, zones, story anatomy, actions and toasts are identical on both. Only the layout differs: 3 desk columns on desktop, 1 on phone, with drag as a desktop extra and ▲/▼ everywhere.
Dev-Navigation-Ease-And-Speed
The open Ask is always the lead, so it's one click (Extra!) away. Next unread: scan for bold, or step through the ticker. #7 is weak: it's a Full-edition In brief line on the Findings desk, so it takes Extra!, then Full, then a scan (about 3 clicks). The page has no jump-to-number.
At-150
The front page doesn't grow much: the lead and above the fold hold only Key and Balanced stories (about 20 at 150). In brief becomes long, and the page needs a "Continued on page 2" split. Recap cites and reply refs are unchanged, because #n is stable. The unread count stays on the Extra! line.
Build-cost
M to L. It needs the M2 strip, a layout engine for the edition, DnD with a touch fallback, and a headline field on each message: either the recap call writes headlines (+1 structured output) or Jev emits them. Storage: feedback.jsonl and categories.json, with a desk field added.
Trade-off
It's the most readable recap in the set and makes importance physical ("it belongs above the fold"). The costs are a big layout for a small number of messages, a thin front page at default weights (only the Ask and 3 decisions make the Late edition's upper half), a metaphor that fights live updates, and column inches that are just weight in costume. Steal: visited-link read state, meaning bold for unread and grey for read in any list of surfaced messages, and the fold as a drag target for a single, legible importance gesture.
Collapsible●●●●○Short threads●●●●○Easy to read●●●●○Mobile●●●○○Consistency●●●○○Dev nav●●●○○Recap●●●●●Read state●●●●○Feedback loop●●●●○Reply-to●●●○○Categories●●●○○
Off the wall

W2 · Tier list

Jev's levels become an S/A/B/C tier list. Every pin wears its tier letter, the recap is read out one tier at a time, and you teach Jev by dragging a message tile to another row, or onto the composer to reply to it.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Phone · LiveS–A · #24 · 2 refs
9:415G ▮▮▮
‹Typed session list
Phone · SettledTier recap open
9:415G ▮▮▮
‹Typed session list
Desktop · SettingsSurfacing › Category tiers
Phone · SettingsCategory tiers
9:415G ▮▮▮
‹Surfacing
Builds on
B3's pin lane, clustering, loupe and slot, unchanged in behaviour. Each pin gets a tier letter above its number, the level control is relabelled as tier cut-offs (S · S–A · S–B · Final), and a second mode, ▦ Board, swaps nothing out but adds the tier list under the strip.
Placement
All inside the one activity card: pins over the strip, then a bar (Board, Tier recap, the ? #24 chip, the tier cut-off), then the recap and board when open, then the slot. The final reply and composer follow the card as today. Reply tokens land in the normal composer.
Live
New pins drop in at the right. The slot holds the open Ask #24, with ▲/▼ tier controls, Reply and a read sticker. "9 unread" sits in the head and every unread pin and tile carries a white dot. The composer shows the canonical draft with #24 and #18 tokens between text.
Settled
The desktop mockup opens with the tier recap and the board, so the whole turn is a 4-row list: S 6, A 10 (including #1 with ▲ you), B 18, C 0. The slot folds to a one-line peek of #24. "13 unread" is in the head.
Recap
"✦ Tier recap" on the bar, on request by default. sonnet-5 writes the canonical recap and conduit files each sentence under the tier of the message it cites, so it reads "S tier: one answer needed… A tier: … B tier: …". "Prose" shows the verbatim text. Settled adds a Whole turn scope, where #1's ▲ puts it in the A-tier sentence. Every [#n] is a button that selects the message, opens the slot and flashes its tile. Model, trigger, scope and length live in Settings › Recap.
Read-state
Leaving a message open for a second puts a silver sticker on it. Read tiles wear a sticker dot, and unread pins and tiles wear a white dot and a bold number. The slot's "Mark unread" button peels the sticker off. The head count updates live and is itself a button that jumps to the next unread.
Importance-feedback
Drag a tile to another row, or use ▲/▼ on the slot (one tier per press). The pin letter, the tile's row, the lane and the mark ("▲ you · Jev said B") all change together, and a reason row offers "changed the plan / user-visible / noise / already knew". Each move appends {"mark":"raise","from":"B","to":"A",…} to feedback.jsonl. It never changes a weight. Tier C means folded into the log.
Reply-to
↩ Reply on the slot inserts a .ref at the caret. On desktop you can also drag any tile out of the board and drop it between two words of the draft, and it lands at the drop point. × or Backspace removes a token. The agent gets the scenario's "Re #24 (Ask, 31:02): …" blocks in order.
Categories
Settings › Category tiers uses the same board: bands S 90–100, A 70–89, B 50–69 and C 1–49. Drag a category row into a band to set it to 100, 80, 60 or 40, or fine-tune with −/+ in steps of 5 (rows re-band as they cross a boundary). Retire drops a built-in into the Retired band with Restore. "+ Add category" appends a row with an inline editor, and custom rows get Edit and Delete.
Collapsible
▾ folds the slot to a peek line, and Board and Recap toggle off. With cut-off "Final" the lane disappears and the card is today's head plus strip. The head chevron opens the step log.
Short-session-threads
Default settled (board closed, slot folded): head, 2-line pin lane, strip, bar and peek, about 6 lines on desktop and 8 on phone. The board adds 4 rows (6 to 8 lines at 34, since tiles wrap). It is all one card.
Easy-to-read
The slot is B3's: full text at 13.5px with a large type label. Tier letters are greyscale (white S down to dark C), so they never compete with the type colours. The recap's tier letters give the gist order: read S, stop if busy.
Mobile-friendly
The same lane at 24px pins with GAP 28 clusters, the bar wraps to two rows, and tiles grow to 32px. All buttons are at least 32px. On phone you move tiles with the slot's ▲/▼, because a drag on a small board fights with scrolling. Tile drag still works there, but it isn't the taught path.
Consistency
Same lane, letters, board, sticker, recap and settings bands on both. Two differences: phone teaches ▲/▼ over drag, and on phone you reply with ↩ Reply rather than by dragging a tile to the composer, because the dock is below the fold of a tall board.
Dev-Navigation-Ease-And-Speed
#7 is a B-tier Finding. Pick S–B then its pin (2 clicks), or open Board and tap tile 7 (2), or type 7 Enter on the lane. The Ask: the ? #24 chip (1) or the a key, and it is always the first S tile. Next unread: tap the "9 unread" pill, or u. The lane also takes +/− (tier), r (reply) and b (board).
At-150
The lane clusters as B3 did, and the loupe spreads them. The board is where it strains: B holds about 110 tiles, so rows cap at two lines with "+84 more ›" (the tiles are drawn, the cap isn't). The recap still has one sentence per tier. The unread pill is a number, and tokens are unchanged.
Build-cost
M. It needs B3, a tier mapping over prominence (no new storage beyond read state and feedback.jsonl), one recap call per request plus a cheap sentence-to-tier split, and a pointer-drag layer for tiles and category rows.
Trade-off
Tiers are the same levels with a new vocabulary, and that is both the gain and the cost. "Drag to S" is instantly understood, but it hides the numeric prominence that makes levels predictable, and the board is a whole extra surface. Its drag-to-reply isn't discoverable, and the reorganised recap isn't the model's own sentence order. Steal: the recap sorted by importance tier (read the S line and stop), and one drag gesture that both reranks a message and teaches Jev.
Collapsible●●●●○Short threads●●●●○Easy to read●●●●○Mobile●●●○○Consistency●●●●○Dev nav●●●●○Recap●●●●○Read state●●●○○Feedback loop●●●●●Reply-to●●●●○Categories●●●●○
Off the wall

W3 · Radio briefing

The recap is something you listen to: a spoken briefing with a karaoke transcript whose phrases are cited. The M2 strip's thumb is the playhead, so hearing the briefing walks you through the pins, and anything you've heard counts as read. Answer by holding the mic while a message plays.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Phone · LiveBriefing ready · #24 · 2 refs
9:415G ▮▮▮
‹Typed session list
Phone · SettledBriefing + transcript
9:415G ▮▮▮
‹Typed session list
Desktop · SettingsSurfacing › Stations
Phone · SettingsStations
9:415G ▮▮▮
‹Surfacing
Builds on
M2's two-row pin rail over the strip, its scrub-to-pin thumb and its floating card. The thumb now doubles as the playhead of a spoken briefing, and the card, now in the flow and tied to the thumb by its caret, shows whatever message is playing.
Placement
Inside the activity card: the head (with "9 unread"), then the player bar (play, title, level), the transcript when open, the pins and strip, and the card. The final reply and composer follow as today. Nothing plays by itself.
Live
The player reads "Briefing since 27:15 · 9 new · about 1:05". Pressing play reads the canonical live recap, then a rundown of the other six unread, one sentence each. New pins keep arriving at the right. The card holds Ask #24 with ▲/▼, "Hold to talk", "⌨ Reply" and "Mark heard". The composer carries the canonical two-ref draft.
Settled
The transcript is open by default: the canonical "While you were away" recap plus a rundown of the other eight unread (13 in all). The final reply sits below. Pin #1 carries "▲" and its card says "▲ you · Jev: All · changed the plan". Scope: Since I read or Whole turn.
Recap
The canonical sonnet-5 text, cut into phrases at its citations. The phrase being read fills left to right (karaoke). Every [#n] is a cite button that seeks the audio there, moves the thumb to the pin and opens the card. Clicking any phrase seeks to it. Speed is 1×, 1.5× or 2×. Voice is on-device TTS by default, so it costs nothing beyond the text recap ($0.005).
Read-state
Heard means read. A phrase's cited messages flip to read when it finishes, their pins lose the white dot with a small bump, the strip shades what you've heard, and the head count drops. The card's "Mark heard"/"Mark unheard" is the manual override. Unread messages below the current level still show as hollow dots on the strip, so the rundown's position is never invisible.
Importance-feedback
▲/▼ on the card, or ↑/↓ on the keyboard, apply to whatever is playing, without pausing. That lets you rate while listening: ▲ moves All→Balanced→Key, and ▼ moves Key→Balanced→All→Log (log removes the pin; the dot stays). Each mark appends to feedback.jsonl and never changes a station's weights.
Reply-to
Hold the mic (pointer or Space) while a message plays: the briefing ducks, you speak, and on release the message's token is inserted at the caret, followed by your words transcribed on-device. The typed fallback, ⌨ Reply, inserts the token for you to type after. × or Backspace removes a token. The agent gets the canonical "Re #24 (Ask, 31:02): …" blocks.
Categories
Settings › Stations. A preset is a named mix of category weights: 88.1 Balanced mix (the scenario defaults), 94.7 Blockers first, 101.3 Everything, and 106.5 Night shift. Tuning any fader turns it into "Custom mix", which "Save as station" keeps (custom stations can be deleted). Below that are per-category faders with −/+ (steps of 5), Retire/Restore for built-ins, and "+ Add category" with Edit/Delete for customs, ⚡ Perf included.
Collapsible
The transcript toggles open and closed, the card folds to a one-line peek, and the head chevron shows the step log. With level "Final" the rail hides and only the player and strip remain.
Short-session-threads
Collapsed it is the head, a 44px player bar, a 2-row rail and the strip, about 5 lines. That is heavier than B3 for a 3-message turn, because the player bar is always there. A turn with fewer than 3 unread shows no player bar, just M2's rail.
Easy-to-read
The transcript is ordinary prose at 13px and readable without sound. The karaoke highlight is a faint fill, not a colour change. The card is M2's, at 13.5px.
Mobile-friendly
This is the natural phone case: walking, listening, holding the mic. The play button is 40px, the pins have 32px tap halos, the mic is a 32px press-and-hold, and the card is full width. Playback continues with the screen locked through the Media Session API, which the mockup can't show.
Consistency
Same rail, strip-as-playhead, card, transcript and settings on both. Phone adds lock-screen transport controls, and the mic is the primary reply there.
Dev-Navigation-Ease-And-Speed
#7 is below Balanced. Choose All, then tap its pin (2 taps), or scrub the thumb until the card says #7 (1 drag). The Ask: its pulsing pin, the first transcript phrase, or a. The rail takes ←/→, Home/End, a, Space (play or pause) and ↑/↓ (rate). There's no find-by-number in audio.
At-150
The rail thins to dots as M2 did. The briefing covers only since-last-read, with the rundown capped at 8 ("and 23 more findings, all green") so audio stays under about 90s. Tokens are unchanged.
Build-cost
M-L. It needs M2, one text recap call, on-device TTS (Web Speech) with phrase boundary events for karaoke, MediaSession for the lock screen, and on-device speech-to-text for the mic, with a server STT fallback. There is no new storage beyond read state.
Trade-off
Audio is slow: 1:05 to hear what you can skim in 10s, and a meeting-heavy office or open plan makes it unusable. The payoff is real on a phone away from a keyboard. Hold-to-talk puts the token in without looking, but voice replies to a coding agent need an edit pass. Steal: "heard/read" as a side effect of consuming the recap, and a rundown that walks the thumb through every unread pin in order.
Collapsible●●●●○Short threads●●●○○Easy to read●●●●○Mobile●●●●○Consistency●●●●○Dev nav●●●○○Recap●●●●●Read state●●●●○Feedback loop●●●●○Reply-to●●●○○Categories●●●●○
Off the wall

W4 · Metro map

The turn is a metro line and the surfaced messages are its stations. Decisions and replans are interchanges, with a dashed spur for the branch not taken. A pink "You are here" marks how far you've read, and unread stations are hollow. The recap reads as route directions.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Phone · LiveStation #24 open · 2 refs
9:415G ▮▮▮
‹Typed session list
Phone · SettledRoute recap open
9:415G ▮▮▮
‹Typed session list
Desktop · SettingsSurfacing › Lines
Phone · SettingsLines
9:415G ▮▮▮
‹Surfacing
Builds on
B3's idea of a lane of markers placed by time over the strip, redrawn as a transit diagram. The line is placed by time, and each track segment takes the colour of the station it leads to, so the whole route reads "Finding line to #1, Scope line to #3…". The pin lane is gone. "Lines" switches to a full map with one track per category, which the route snakes between.
Placement
Inside the activity card, under the head: the map (about 60px), then a bar (Lines, Route recap, the ? #24 chip, Next stop, level), then the recap and the station sign when open. The final reply and composer follow as today.
Live
The line ends in a dotted run to a blinking train at the live edge, labelled "running pnpm acceptance:visual". The pink "You are here" sits on #21, and #22–#30 are hollow (9 unread, in the head). The station sign for ? #24 is open with ▲ Enlarge, ▼ Shrink, "Message the driver" and "Read to here". The composer carries the canonical two-ref draft.
Settled
The line runs to a terminus at #34, and "13 unread" is in the head. The route recap is open with the since-you-read directions. Station #1 is drawn a size up with a ▲ badge ("▲ you"), one tap from its sign. Spurs show the branches not taken at #6, #9, #11, #18, #25, #32 and #33.
Recap
sonnet-5 writes the canonical recap, and conduit renders it as a route: one stop per cited message, in order. Each stop says which line you ride and where you change, with the canonical wording kept as each stop's text: "Change at ? #24 Ask: recapture phone-sidebar or keep the old paint order". Scope is Since I read or Whole turn (settled), and "Prose" shows the verbatim canonical paragraph. Every [#n] is a cite that opens the station sign and rings the station on the map.
Read-state
The read position is one marker. "You are here" sits on the last read station, and stations after it are hollow with bold numbers. "Next stop" or n moves you forward one station and fills it in. "Read to here" or "Unread from here" on a sign moves the marker directly. The count updates live. Marking one out-of-order message as read is not possible, by design.
Importance-feedback
Enlarge (▲) and Shrink (▼) on the sign change a station's size, which is its level: Key is the big roundel, Balanced medium, All small, and Log removes it from the map. The mark reads "▲ you · Jev: All". Each press logs to feedback.jsonl and never changes a line's weight.
Reply-to
Tap a station, then "Message the driver": the station's token goes into the composer at the caret. Repeat it for #18, typing in between. × or Backspace removes a token. The agent receives the canonical "Re #24 (Ask, 31:02): …" blocks.
Categories
Settings › Lines: every category is a line with a colour swatch, weight (−/+ by 5), definition and counts, drawn as a mini track. Built-ins can be retired (dashed line in a Retired section) and restored. "+ Add line" appends a custom line with an editor (name, glyph, definition, colour from 8 swatches). Custom lines, ⚡ Perf included, get Edit and Delete.
Collapsible
The sign and the recap close with ×. At level "Final" the map collapses to a 6px line with just the "You are here" dot and the Ask roundel. The head chevron shows the step log.
Short-session-threads
A 3-message turn is a short line with 3 stations and no bar clutter, about 3 lines under the head. That's lighter than the pin lanes, because the stations and the strip are one drawing.
Easy-to-read
The sign is plain 13.5px text under a platform-style header. The map is glanceable (big roundels are what mattered, pink is where you are) but it is not text, so it needs the sign or the recap to be read. The full Lines map is dense at 34 and more a novelty than a reading surface.
Mobile-friendly
The same line at phone width, with 32px invisible tap targets on every station, 32px buttons, and the sign full width. Labels at Balanced crowd between 01:42 and 04:05, so numbers step to a second row.
Consistency
Same map, sign, recap and settings on both. Phone hides the legend and uses 12px labels.
Dev-Navigation-Ease-And-Speed
#7 is a small All-level station. Pick All, then tap station 7 (2 taps). The station buttons are also in reading order for Tab, and ←/→ on the map moves between them. The Ask: its pulsing roundel, the ? #24 chip, or a. Next unread: Next stop, or n.
At-150
It strains here. At All, 150 stations in 560px is a comb, and the Lines map becomes unreadable. It holds up at Key (about 25 roundels) and Balanced, and the recap keeps to since-you-read. Station numbers thin to Key only above 40 stations. Tokens are unchanged.
Build-cost
M. An SVG renderer with a label-collision pass, a single read-position cursor (simpler than per-message read state), and the recap rendered from citations into stops. The branch-not-taken text needs one more field in Jev's output ("alternative": short phrase) for decisions and replans, and that costs tokens on every classification.
Trade-off
The metaphor is memorable and "You are here" is the clearest read-state in the set. But a single read cursor can't express "read #30 but not #26", branch-not-taken needs extra model output, and the Lines map is decoration past about 20 stations. Steal: the one-cursor "You are here" read position with Next stop, and showing decisions with the alternative they rejected.
Collapsible●●●●○Short threads●●●●●Easy to read●●●○○Mobile●●●○○Consistency●●●●○Dev nav●●●○○Recap●●●●○Read state●●●●○Feedback loop●●●○○Reply-to●●●●○Categories●●●●○
Off the wall

W5 · Swipe deck

The slot under the pin lane is the top card of a deck: flick it right if it mattered more than Jev thought, left if less, up if the type is right, down to retype it, and hold it to reply, while a running "you vs Jev" score and the ledger it writes make every read a training signal.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Phone · Livedeck on #24 · 9 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
Working 38m 12s· pnpm acceptance:visual↓ live
Phone · Settledrecap card on top · 13 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Desktop · Settings › Surfacingcategories + what your swipes suggest
Phone · Settings3 tabs · categories open
9:415G ▮▮▮
‹Settings · Surfacing
Builds on
B3. Its pin lane, clustering, loupe and drag-scrub are kept as they were. B3's slot becomes the top card of a deck, with two card edges showing beneath it. The pins still pick which card is on top. What's new is that the card itself takes gestures.
Placement
Everything sits inside the one activity block: head (with the unread count), pin lane, strip, deck bar, deck, and one ledger line. The final reply follows the block, then the composer. Reply tokens land in that composer. The recap is a card in the deck, not a separate panel.
Live
The deck is dealt in this order: the recap card, then the open Ask, then the other unread cards in time order, then the read ones. The Live mockup has the recap card put away and #24 on top. New messages join the unread part of the deck, and each new pin drops in at the right end of the strip.
Settled
The deck reopens on the recap card ("since you last read", 13 updates), with a scope switch for the whole-turn recap. Flicking it away deals #24.
Recap
The recap is the first card of the deck and comes back with "✦ Recap" in the deck bar. Every [#n] is a .cite button: it brings that message's card to the top, highlights its pin, and marks it read. The model, when it runs, scope and length are set in Settings (default sonnet-5, on request). In this design "on request" means dealing the deck while there are unread messages. The footer shows the model, the update count, the time and the cost.
Read-state
A card becomes read when you swipe it, act on it, or bring it to the top yourself (a pin, a citation, ‹ ›, Next unread). The card the deck happens to open on stays unread until you touch it, so the Live frame still shows #24 as unread. "○ Mark unread" (key u) puts it back. Unread cards carry an "unread" tag and their pins a white dot. The head shows the count ("9 unread").
Importance-feedback
Flick right (→, or "▲ More") to raise a card one level. Flick left (←) to lower it. Flick up (↑) to agree with Jev's type. Flick down (↓) to retype it from a picker. Swipes apply the change and deal the next card. Buttons apply it and leave the card where it is. The card shows "▲ you · Jev said All → now Balanced", the pin gains ▲, and the level counts in the bar change. Each mark or retype is appended to feedback.jsonl (the ledger line under the deck shows the exact line). Agreement swipes only feed the "you vs Jev" score (98 of 121 = 81%). Settings turns the ledger into a suggested weight per category, with a preview on this turn, which you apply yourself. No weight ever changes automatically.
Reply-to
Hold a card for about half a second (or press ↩ Reply, or r). A .ref token is inserted at the composer's last caret position and the caret is placed after it, so you type straight on. Hold another card and type again, or click into the text first to put the token somewhere else. × or Backspace removes a token. "what the agent gets" previews the send: each token becomes a Re #n (Type, time): "quote…" header, followed by the text you typed after it.
Categories
Settings › Surfacing shows a table of the 9 built-ins plus ⚡ Perf: definition, a weight stepper, "your swipes" (▲/▼ this week, and a suggested weight you can use), and Retire. Retired built-ins move to a Retired section with Restore. Custom categories get Edit and Delete. "+ New category" appends a row. Above the table, a preview panel spells out what the suggestions would change on this turn.
Collapsible
▾ on the card folds the deck to a single peek line, with the edges shrunk. The pin lane stays, because it is the activity strip. Tap a pin or the peek to reopen. At the Final level (in Settings) the lane and deck are hidden, which is today's view.
Short-session-threads
Desktop, settled: head, lane, strip and axis (3 lines), the deck bar (1), the card (about 7 to 9) and the ledger line: about 13 lines, and 5 folded. Phone: about 17, because the six action buttons form a 3×2 grid. It is always one activity block, and the deck shows one card whatever the count.
Easy-to-read
One message at a time at 13.5px, never clamped, with a big type label, #24 · 31:02, and the level with its prominence ("Key · 97"). The swipe stamp (▲ MORE, ▼ LESS, ✓ RIGHT TYPE, ↻ RETYPE) names the action before you let go. The weak point is the action row: six buttons under every card is a lot of chrome.
Mobile-friendly
This is where the design is at home: thumb flicks, a 3×2 grid of 34px buttons, 24px pins, and the B3 loupe on dense stretches. The card sets touch-action:none, so vertical flicks don't scroll the page. You scroll from outside the card, which people have to learn.
Consistency
The card, stamps, actions, deck order, recap card and tokens are identical on both. Divergences: desktop maps the swipes to arrow keys and shows key hints, while the phone drops the hints and arranges the actions as a grid. The phone's deck bar wraps to two rows. Hold-to-reply exists on both (mouse press-and-hold works), and the ↩ button is always shown, because a hold can't be discovered.
Dev-Navigation-Ease-And-Speed
The open Ask: one tap ("? #24" in the bar) or a. It is also second in the deck. Next unread: one tap ("Next unread ⇥") or n. #7: at All, tap its pin or group and loupe (2 taps). On desktop, type 7 ↵, which switches to All if needed.
At-150
The lane turns into group pills, as in B3, and the loupe does the work. The deck still shows one card, and its position reads "1 / 150". Triaging 150 cards one at a time is where the concept strains. The recap card and "Next unread" are the escape hatches. The unread count stays a single number, and the tokens are unaffected. The agreement score gets more reliable the more you swipe.
Build-cost
M–L: B3's lane, plus gesture handling (drag, fling thresholds, long-press), the deck ordering, per-message read state and marks (a small store per session), feedback.jsonl appends, a recap call (about $0.006), and the suggestion maths in Settings. No new surface outside the block.
Trade-off
A reading surface that doubles as training turns every glance into a judgement, and after a week that becomes a chore. Flicks are also easy to fire by accident (the ledger line, and flicking again, undo them). One card at a time can't show two messages side by side. Steal: the "you vs Jev" agreement score, and weight suggestions computed from the ledger with a preview on the current turn. Together they make the feedback loop visible and safe without the deck.
Collapsible●●●●○Short threads●●●○○Easy to read●●●○○Mobile●●●●○Consistency●●●●○Dev nav●●●○○Recap●●●●○Read state●●●○○Feedback loop●●●●●Reply-to●●●●○Categories●●●●○
Off the wall

W6 · Plain-text journal

Every surfaced message is appended to .conduit/journal/<session>.md and read in a less-style pager inside the activity block, where the recap is a generated ## Recap section, importance is a +1 reason line you add, reply is > #24, and categories are a surfacing.toml you edit with a live diff.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Phone · Livepager on #24 · extra-keys row
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
Working 38m 12s· pnpm acceptance:visual↓ live
Phone · Settledpager at ## Recap · 13 unread
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Desktop · Settings › surfacing.tomlin-app editor · live diff against the saved file
Phone · Settingssame file, diff below
9:415G ▮▮▮
‹surfacing.toml
Builds on
Nothing in B1–B7 or M1–M7. It borrows the pager from less and the journal from plain-text note tools. The only visual primitives it reuses are the shell's activity block, strip, .ref and .cite.
Placement
The one activity block contains the head (with the unread count and a "less"/"tail" toggle), a row of type-coloured ticks over the strip, and either a three-line tail of the journal (closed) or the pager (open). The pager has a filename bar, level filter, line-numbered view, inverse status line, command line and key row. The final reply and the composer come after it. The journal file itself is real: <project>/.conduit/journal/ses_c2d8cd52.md.
Live
Each newly surfaced message appends an entry, and the pager keeps its place, as less +F would if you had scrolled up. The status line shows the file position and "9 unread". The Live mockup has the pager open with the cursor on #24, which is also marked "waiting on you".
Settled
Conduit writes (or rewrites) a ## Recap section at the end of the file, and the pager opens at it. g goes back to the top, where #1 is.
Recap
The recap is a generated markdown section with [#n] links. Each link is a .cite that moves the cursor to that entry and marks it read. R, or :recap, (re)generates it: "since you last read" by default, :recap turn for the whole turn (settled only), which you can also switch from the header comment. A comment line records the model, update count, time and cost. It follows [recap] in surfacing.toml (sonnet-5, on request).
Read-state
Each heading holds a checkbox: ## [ ] #24 is unread, [x] is read. The box is in the file, so the file is the read state. An unread entry also gets a ● in the gutter and a white tick above the strip. Moving the cursor onto an entry (j/k, u, a click, a citation) ticks it. m, or clicking the box, toggles it. The head badge and status line keep the count.
Importance-feedback
+ prompts for "+1 reason" and appends a line such as +1 changed the plan under the entry. - appends -1 reason. The heading's level then reads All → Balanced. The same mark is appended to feedback.jsonl (the status line confirms this). Pressing the opposite key removes the annotation. A −1 at All folds the entry into the log, so it disappears at every level filter. Because the annotations are plain lines, you can also type them into the file by hand, and conduit picks them up.
Reply-to
r quotes the entry under the cursor into the composer as a > #24 Ask token, placed at the composer's last caret position, with the caret after it. Repeat for several entries and type between them. × or Backspace removes a token. "Preview send" shows the expanded Re #n (Type, time): "…" blocks the agent receives.
Categories
Categories are the [categories] and [custom] tables of ~/.config/conduit/surfacing.toml, as multi-line inline tables (TOML 1.1). The in-app editor has − / + on each weight, retire (which moves the entry into [retired], where restore is), and + add, edit and delete for custom entries. A diff pane shows every unsaved change against the saved file, with :w to write and :e! to revert. [recap] and [feedback] live in the same file.
Collapsible
q (or "tail" in the head) closes the pager to a three-line tail -n 3 of the journal, and "less" reopens it at the same place. The level filter (& in less terms: key, balanced, all) hides entries below it, and hidden line numbers stay skipped, as in less.
Short-session-threads
Closed, the block is head, ticks and strip, and 3 tail lines: about 5 lines. Open, the pager is a fixed 20 rows on desktop (18 on phone) whatever the message count. It is always one block.
Easy-to-read
Each entry is a bold heading (## [ ] #24 · 31:02 · ? Ask · p.97 · Key) followed by the unclamped message, with a blank line between entries. Line numbers and markdown sigils add noise, and the type colour is confined to the glyph and label. People who live in terminals will read it faster than cards. Everyone else will find it dense. That is the concept's honest cost.
Mobile-friendly
A Termux-style extra-keys block (two rows of seven 40px keys: j k u a g G R and / + − m r : q) stands in for the keyboard. The whole heading can be tapped to move the cursor. The level filter and checkboxes have enlarged hit areas, and the command line opens the soft keyboard only for reasons, search and :. It works, but it is a desktop idiom carried to the phone.
Consistency
The file, entries, keys, commands and tokens are identical on both. The only divergence is the key row: desktop shows inline key hints, the phone a 7×2 key grid.
Dev-Navigation-Ease-And-Speed
The open Ask: a, or one tap. Next unread: u, or one tap. #7: :7 ↵ (it switches the filter to all if needed), or /push-routing ↵. On the phone that is 2 taps plus typing. Top and bottom: g and G.
At-150
At 150 entries the file is about 450 lines, which a pager handles easily. The status line reads "lines 212–231/468 45%", search and :n scale, and the level filter keeps Balanced at around 60 entries. The ticks over the strip merge into a texture, and that is acceptable because they are decorative. Unread tracking stays exact because it lives in the checkboxes.
Build-cost
M: a journal writer in the daemon (append-only, plus in-place checkbox and annotation edits), a pager component (virtualised lines, key map, command line), a TOML parser and serialiser with a line diff, and the recap call. A file watcher lets hand edits flow back. It needs no new visual primitives.
Trade-off
It is the most portable and scriptable design here. You can grep, commit and hand the file to another agent. But it asks everyone to read in a terminal idiom, and read state stored in a markdown file will run into concurrent writes, for example the agent appending while you tick a box. Steal: write surfaced messages to a plain journal file anyway, even with a different UI. The +1 reason line is the most honest feedback format, because it is readable, editable, and already the training data.
Collapsible●●●●○Short threads●●●●○Easy to read●●○○○Mobile●●○○○Consistency●●●●●Dev nav●●●●○Recap●●●●○Read state●●●●○Feedback loop●●●●○Reply-to●●●●○Categories●●●●○
Off the wall

W7 · Quest log

The turn is a quest: a small tracker on the activity block shows the main quest, the one entry awaiting your answer and the active objective, and "Quest log" opens an RPG-style journal (objectives, side quests, lore, and a Chronicle recap) where you rate entries with stars, see "new" sparkles and pick "Respond" to quote an entry into your reply.

Desktop · Live38m 12s · 30 surfaced · 9 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
Working 38m 12s· Running pnpm acceptance:visual↓ live
opus-5
Desktop · Settled44m 30s · 34 surfaced · 13 unread
Move the session list onto the typed streaming transport and delete the legacy session_switched path.

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Phone · Livequest log open on the awaiting entry
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.
Working 38m 12s· pnpm acceptance:visual↓ live
Phone · Settledtracker only · Chronicle one tap away
9:415G ▮▮▮
‹Typed session list
Move the session list onto the typed streaming transport and delete the legacy session_switched path.

Done. The session list, unread badges and push deep links now run on the typed stream. session_switched survives behind CONDUIT_LEGACY_SWITCH=1 for one release. One open question: recapture phone-sidebar (a 2px shift), or keep the old paint order?

opus-5↑
Desktop · Settings › Questsquest types · chronicler · ratings ledger
Phone · Settingssame panels, stacked
9:415G ▮▮▮
‹Quests
Builds on
M2's settled strip (a plain strip, with no pins) and the shell's activity block. The idea of a quest tracker plus a journal comes from RPG HUDs. The quest log itself is new: none of B1–B7, M1–M7 or W1–W6 group messages by what they mean to the job (objective, obstacle, lore).
Placement
The activity block holds the shell head, the strip, and then either the tracker or the quest log. The tracker is 4 rows: the main quest with its progress, the glowing "Awaiting your answer" entry, the active objective, and "Quest log" and "Chronicle" buttons. The quest log opens inside the same block. On desktop it is two panes (the log on the left, a dialogue box on the right). On the phone it is one pane that switches between the log and the dialogue. The final reply and the composer come after the block.
Live
Each new message takes its place in the log by kind: a Milestone is a completed objective, a Risk, Blocked or Scope entry is a side quest, an Ask becomes "Awaiting your answer", and everything else is lore. New entries sparkle ✦. The active objective reads the agent's current step ("running pnpm acceptance:visual"). The Live mockups have the log open on #24.
Settled
The tracker's progress bar reads 7 of 8, and the one gap is the awaiting answer. The active objective becomes "Turn complete · branch pushed". Desktop opens the log on the Chronicle. The phone keeps the 4-row tracker, and the Chronicle is one tap away.
Recap
The Chronicle is the first entry in the log, and it is the recap: narrated text in the dialogue box, where every [#n] is a .cite that opens that entry and marks it read. You can switch it between since last read and the whole turn. The footer shows the model and cost, with "↻ Rewrite". In Live it is written on request ("Write the chronicle"), following Settings › Chronicler.
Read-state
Unread entries carry a ✦ sparkle in the log and "✦ new" in the dialogue box. Opening an entry, whether by clicking, j/k, "Next new" or a citation, marks it read. Dialogue option 2 ("Mark as new") reverses that. The count sits on the tracker's Quest log button ("✦ 9 new") and in the open log's header. The initial selection in each mockup is left unread on purpose, so the marker shows.
Importance-feedback
Importance is a 3-star rating: ★ All, ★★ Balanced, ★★★ Key, plus "✕ hide", which sends the entry to the log. Jev's rating shows as hollow outline stars next to your filled ones. Picking Jev's own level clears your rating. When your rating differs, the row shows a "you" tag, a "why?" field appears, and feedback.jsonl records a raise or lower with from, to and the reason. #1 shows Jev ★, you ★★, "changed the plan". Ratings apply live, so the entry moves in or out of the star filter.
Reply-to
Dialogue option 1, "Respond…" (or 1 / r), inserts a ↩ #24 Ask .ref token at the composer's last caret position. You can add several and type between them. × or Backspace removes a token. "Preview send" shows the Re #n (Type, time): "…" blocks the agent receives.
Categories
Settings › Quest types lists each type with its glyph, label, colour, quest kind (Awaiting, Objective, Side quest, Lore) and an XP weight (± 5). XP × Jev's confidence decides the stars (70 for ★★★, 40 for ★★). Built-ins can be retired to "Retired quest types", restored from there, and reset. Custom types (⚡ Perf) can be edited and deleted, and "+ New quest type" adds one, including its kind.
Collapsible
× (or Esc) closes the log to the 4-row tracker. The tracker never grows. Within the log, each section (Objectives, Side quests, Lore) folds on its header, and the star filter (★★★ / ★★+ / ★+) trims Side quests and Lore. The main quest and the awaiting entry are always shown.
Short-session-threads
The collapsed block is the head, the strip and 4 tracker rows, about 150px at any message count. The open log is a fixed 430px panel that scrolls inside. It is always one block.
Easy-to-read
The log is grouped by what an entry means to the job, which is easier to scan than a timeline. Each dialogue box is one message at 13.5px, with its type glyph and label in colour and the stars next to it. Gold and serif stay on headings, so the game framing doesn't touch the text itself. The risk is that some readers will find the framing twee.
Mobile-friendly
The log and the dialogue are pages in the same panel: tap a row to open its dialogue, and "‹ Quest log" goes back. Rows are 44px, options and stars 36–40px, and the tracker's awaiting row is a 44px glowing target. The phone has no hotkey row, because the numbered options are the touch path.
Consistency
The tracker, log sections, dialogue options, stars and tokens are the same on desktop and phone. There are two divergences: desktop shows log and dialogue side by side where the phone pages between them, and the keyboard hints (j/k/u/a/1–4/Esc) are desktop-only.
Dev-Navigation-Ease-And-Speed
The open Ask is one tap, on the glowing tracker row, whether the log is open or closed. Next unread is "Next new ✦" (u). #7 is under Lore, with ★ on: 2 taps, or j through the list. The log has no search or jump-to-number, so reaching an arbitrary number is slower than in W6.
At-150
The sections grow, but the panel stays at 430px and scrolls. At ★★+, Lore holds about 30 entries and Side quests about 12. Objectives fold by default above 8. The tracker is unchanged. Grouping by kind loses the chronology, which the times and #n on every row partly restore. With several Asks, "Awaiting your answer" becomes a short stack and the tracker shows the oldest.
Build-cost
M: the kind mapping (per type, set in settings), the side-quest "cleared by #n" links (these need the classifier to link a later message to an earlier one, which Jev doesn't do today, so the mock hard-codes them), the log and dialogue components, star ratings over the existing mark ledger, and the recap call. The active objective comes from the current step.
Trade-off
Grouping by meaning (what's done, what's in the way, what's waiting on you) is the best "where are we" view in the set, and the tracker is a small, glanceable summary. But the RPG framing will split the audience, and "cleared by" needs link inference Jev doesn't do yet. Steal: the 4-row tracker (main task · progress · the one thing awaiting you · the active step) as the collapsed state of any design, and stars as a clearer name for the three levels than Key, Balanced and All.
Collapsible●●●●●Short threads●●●●●Easy to read●●●○○Mobile●●●●○Consistency●●●●○Dev nav●●●○○Recap●●●●○Read state●●●●○Feedback loop●●●●○Reply-to●●●●○Categories●●●●○