T3's sidebar reshuffles when agents change state. A and B keep every row in its slot.
Four threads changed state and 8 of T3's 15 rows landed in a different slot. In both variations, none did. B's rows did not move a pixel. Both also make the working state move. My pick is variation B, then port A's answers on the row. Even with the answers, that is about a third of A's new code. I built A and B; I did not build B with answers.
Four state changes moved 8 of 15 rows in T3 and 0 of 15 in A and B
T3 today
Variation A
Variation B
measured Slots read from the DOM of the running app on each branch, with every section unfolded, before and 3 seconds after these changes: Rate limiting working to done, Audit backfill done to working, Flaky retry test failed to working, Class migration working to approval. Threads are made up.
On screen, the four tracked rows moved up to 886px in T3, 28px in A and 0px in B. A's 28px is its Waiting on you list gaining a row. A real request would also open answers on the row, about 48px more for the rows below it. The fake socket did not supply that request in this test, so the 28px is a floor.
Slots as a table
| Thread | T3 before | T3 after | A before | A after | B before | B after |
|---|---|---|---|---|---|---|
| Chart colors | 1 | 1 | 1 | 1 | 1 | 1 |
| Audit backfill | 2 | 9 | 7 | 7 | 6 | 6 |
| Key rotation | 3 | 4 | 8 | 8 | 7 | 7 |
| Invoice rounding | 4 | 5 | 9 | 9 | 8 | 8 |
| Billing page | 5 | 6 | 3 | 3 | 3 | 3 |
| Flaky retry test | 6 | 10 | 10 | 10 | 9 | 9 |
| Network module | 7 | 7 | 13 | 13 | 11 | 11 |
| Preview env | 8 | 8 | 2 | 2 | 2 | 2 |
| Rate limiting | 9 | 2 | 6 | 6 | 5 | 5 |
| Disk alerts | 10 | 11 | 12 | 12 | 10 | 10 |
| Class migration | 11 | 3 | 4 | 4 | 4 | 4 |
| SSO flow | 12 | 12 | 5 | 5 | 12 | 12 |
| Staging DNS | 13 | 13 | 14 | 14 | 13 | 13 |
| Certificates | 14 | 14 | 15 | 15 | 14 | 14 |
| Nightly job | 15 | 15 | 11 | 11 | 15 | 15 |
The review put hierarchy last, and the sidebar is why
I ran the design review on the running default dark app before designing anything: thread working, waiting on approval, asking a question, parent with workers and settings at 1440x900, plus 390px and 200% zoom. Type, color tokens and keyboard reach are sound. The harm sits in one place. The list sorts by state and dims what runs, so the thing that needs you is not the loudest thing on screen.
The findings below set the scope of the whole-app changes. I left settings, spacing outside the sidebar and the composer alone, because nothing in the review argued for touching them. I did not re-score the variations, so the scores describe T3 today only.
Overall 6 of 10; hierarchy is the low score at 5
| Typography | 7 | |
| Hierarchy | 5 | |
| Spacing and density | 6 | |
| Color and contrast | 6 | |
| Iconography | 7 | |
| Consistency and polish | 7 | |
| Clarity | 6 | |
| Overall | 6 |
Scores out of 10, from the review skill's scale, where 5 to 6 means works with visible problems. My judgment, not a metric.
What each change answers
| Finding in T3 today | T3 today | A | B | Change |
|---|---|---|---|---|
| Running threads fold into a shelf at the bottom | 1 of 3 in view at 1440x900, and only because it is open | 3 of 3 | 3 of 3 | Rows stay in project groups |
| A thread blocked on you reads quieter than a finished one | Title 10.36:1, against 18.9:1 for Done | 18.9:1 | 18.9:1 | Approval never recedes |
| Working row metadata fails AA at 12px | Branch 3.55:1, timer 4.19:1 | 6.49:1, 7.95:1 | 6.49:1 | Drop opacity-70 on working rows |
| Allow and Deny are small text links; Stop is the loudest control | 29x16px links | 45x24px buttons | 45x24px buttons | Same T3 component, its composer form |
| "Working for 1m 34s" looks like a finished row | Only a ticking number | Shimmer | Shimmer | T3's own overlay, always on for this row |
| 82px card rows hold 3 lines | 78px surface | 50px, 98px with answers | 36px | New row layouts |
| Titles cut short in a narrow row | Long titles show 31 to 34 characters | 28 to 29 | 19 to 26: worse | Not fixed. See the pick |
| To answer an agent you must open its thread | Open the thread | Allow, Deny and options on the row | Open the thread, until A's answers are ported | New |
| Workers show only in the details panel | Panel only | Nested, with state bar | Count on the row | New |
| Same provider logo on every row with one provider | 1 per row | None | None | Show only with 2 or more providers |
measured Contrast computed from composed colors on #000. Button sizes from the DOM. Character counts are from the row captures below, for three long titles. T3's shelf says Working 4: three running and one waiting on its own work. Left alone: a row costs 3 tab stops, and Unpin is a 12x12px target.
Assumption: "thread handling" means threads moving on their own
You did not say exactly what annoys you, so this is my reading, and everything about placement depends on it. T3's docs describe the behavior in so many words:
"Threads that are working or waiting on work they will resume from move into a collapsed Working section below the active list. [...] A thread returns to the top of the active list when it finishes, fails, or needs an approval or answer."
T3 docs,docs/user/thread-sidebar.md, "Fold working threads (beta)"
Worth trying before you patch anything. That section is on by default with an off switch at Settings, General, Working section. T3's docs on that switch: "While this is on, the active list is ordered by when each thread last came back to you [...] Your saved order returns when you turn it off." Boring sidebar in Settings, Labs, gives project folders and turns the Working section off. Neither adds the motion or fixes the row, and I measured neither on its own, but both are free.
If what annoys you is something else, such as Settle or Snooze, the motion and contrast work still stands, and this placement work may not be the part you want.
Each state, row by row
A marks state with a glyph in the left column, a word, and a bar with a tint when something needs you. B keeps the mark and drops the word for working and finished rows. That gives titles more room than T3's own Boring rows, but B still shows the shortest titles of the three, because folder rows indent 36px and hold a time or a word on one line.
| State | T3 today capture | A capture | B capture |
|---|---|---|---|
| Working, long title | |||
| Setting up | |||
| Waiting on approval | |||
| Asking a question | |||
| Done, unseen | |||
| Failed, unseen | |||
| Usage limit | |||
| Waiting on its own work | |||
| Snoozed | |||
| Settled | |||
| Parent with workers | |||
| Pinned, done and seen | |||
| Open thread, working |
Where a parent's workers live
T3: thread details panel
A: nested, with a state bar
B: a count on the row
T3 already raises a worker's approval to the parent row as "Approval". A's bar comes from pitwall's fleet.go: one segment per worker in chip order, and a worker that asks keeps the list open. B's count turns amber when one needs you, and T3's details panel still holds the list.
The full window, before and after
Flip the right-hand frame between A and B, or press 1 and 2 to flip every pair. Click any capture to see it at full size.
T3 today, thread working
captureAfter, thread working
captureThe right panel and composer did not change. "Working for" now carries T3's shimmer. In a still it shows only as a brighter patch of text, so see Motion.
T3 today, waiting on approval
captureAfter, waiting on approval
captureAllow and Deny go from 29x16px links to 45x24px buttons with one prop on T3's own component. T3's red Stop button is still the largest control there; I did not change it.
The working state, moving
Stills can't show your main complaint, so this section runs. Your read holds for the sidebar and the "Working for" row: a fixed icon and a ticking number. One thing I found that you did not mention: T3 already shines the live tool line and "Thinking" in the chat, not only worktree setup and compaction. That shine is live-tool-shine, and it comes up under paint cost.
Your system asks for reduced motion, so the recreations below hold still, as the shipped code does, and the recordings do not autoplay.
One working row in each build live recreation
T3 today
Fixed CircleDashed, row at opacity-70. Only the timer changes.
A: row shimmer and dot
Sweep over the whole row, pitwall's 3s ease-in-out, plus T3's status-pulse dot.
B: mark and title
Mark turns, title carries T3's own overlay. The row itself holds still.
| What moves | Property | Timing |
|---|---|---|
| A row sweep | transform | 3s, eased, 45 held steps |
| A dot | opacity | T3's 2s, steps(6) |
| B mark | transform | 2.2s, steps(30) |
| B title, chat row | transform, mask | T3's 2.2s, steps(30) |
Chat, T3 today live recreation
Chat, A and B live recreation
HTML and CSS on this page, using T3's default dark tokens and the keyframes and timings on the branches. Your window's font is what you see here; T3's stack starts with -apple-system. All of it pauses off screen, through T3's observeVisibleAnimation in the app and a matching observer here. With reduced motion on, the app's list of running animations was empty on all three branches.
The real app reacting to three state changes, recorded from each branch capture
T3 today
Variation A
Variation B
- 2.5sRate limiting: working to done
- 5sAudit backfill: done to working
- 7.5sClass migration: working to approval
Screencast frames from headless Chromium, cropped to the sidebar. The recording encoder softens A's 6% sweep, so judge the sweep from the recreation above. T3's Working shelf is open here, or its rows would be hidden.
Row motion added no paints, but it did add main-thread time
Your brief points at T3 #28113, where a shimmer that redrew every frame cost battery on iOS. I took that from you and did not read the issue. So every animation here runs on transform, opacity or T3's existing mask, in held steps at about 14 to 15 frames a second, and pauses off screen. Over 10 idle seconds, A and B paint what T3 paints with its Working shelf open, give or take 2. Those paints line up with the row timers ticking once a second, so the sweep and the spin add none. They are not free: main-thread task time rose from about 33ms to about 75ms in A and 66ms in B, and style recalcs rose from 0 to about 65 and 55.
One line of T3's own tool shine costs more paint than all the sidebar motion in A or B
| 10 seconds, nothing happening | Paints | Layouts | Style recalcs | Task ms | Script ms | Animations running |
|---|---|---|---|---|---|---|
| T3, finished thread open, Working shelf folded | 0 | 0 | 0 | 10 to 41 | 2 to 33 | None |
| T3, finished thread open, Working shelf open | 20 | 10 | 0 | 32 to 67 | 11 to 49 | None |
| A, finished thread open | 20 to 21 | 10 | 64 to 65 | 75 to 139 | 13 to 58 | 5 row sweeps, 6 dots |
| B, finished thread open | 20 to 22 | 10 | 54 to 55 | 65 to 120 | 11 to 44 | 3 marks, 3 title overlays |
| T3, running thread open, live tool line on screen | 286 | 10 | 612 | 251 | 9 | 1 live-tool-shine |
| A, running thread open | 310 | 11 | 599 | 564 | 55 | Sweeps, dots, chat overlay, tool shine |
| B, running thread open | 310 | 10 | 615 | 471 | 12 | Marks, overlays, chat overlay, tool shine |
measured Chromium trace at 1000x1500, headless, on this machine. Rows on a finished thread show the range over three runs; the high task and script figures come from one slow run in each set, and the prose uses the other two. Rows on a running thread are one run each, so read them as rough. This does not measure battery, GPU work or iOS, which is what the issue was about.
The 286 is T3's live-tool-shine, which animates background-position, so the browser repaints it at each step. T3's own live-activity-focus does the same look with a transform and a mask. Swapping the tool line onto it looks like the cheapest battery win in this study. I did not make that change or measure it.
Pick variation B, then port A's answers on the row
Both fix what you asked about. B does it inside the shape T3 already maintains, and A's best part should move over on its own. I built and measured A and B. I did not build, capture or measure B with answers, so that part of this pick is a plan.
B costs about a third of A's new code, answers included
B alone adds 160 new lines against A's 851. Adding RowAnswers.tsx brings it to 308, about 36%, plus a hook I have not written. In Sidebar.tsx, the file you counted 79 changes to in 30 days, B touches +9 −5 and A touches +69 −33, replacing the part of the list that renders folders.
B fits a screen and holds to the pixel
All 11 thread rows sit in view at 1440x900 with no scrolling, and they moved 0px through the state changes. A's list runs 1190px, needs 434px of scroll, and shifted 28px or more. With many agents going, I would expect that gap to grow.
B reads as T3
Its motion is T3's own overlay, and its layout is T3's Boring folders. A is closer to pitwall, which is what you asked to see, and on T3 it reads as a second design language: tints, bars, chips and buttons on one row.
A's answers are the part worth taking
Allow, Deny and question options sent T3's real runtime-request.respond from the sidebar without leaving the open thread. RowAnswers.tsx is 148 lines and has no tie to A's layout. A B row has no room for it today, so it would add height while a row asks and push the rows below it, about 48px if it matches A. That gives up B's pixel stillness for those rows only.
Pick A instead if
You answer approvals all day and want workers in the list. A shows them, and its row sweep reads from further away than B's title sweep. You would pay in scroll and in carrying cost.
Where B falls short
It shows the shortest titles of the three: 19 to 26 characters on the long titles, and "Ship the billing…" next to a count and "Approval". It ignores T3's multi-line folder setting. Snoozed and Settled still pull a row away from its folder, and T3 settles idle threads after 3 days by default. To keep a row in its folder through both, you need A's grouping.
What each change costs to carry
Three kinds of change, from cheapest to most exposed: a new file, which T3 cannot conflict with; a hook, lines added in T3's file that leave what its code already does alone; and an edit to T3 logic, which changes what T3's own code does. Line counts show how big each one is. Lines come from git diff --numstat and hunks from git diff -U0 on each branch.
| Change | File | Kind | Lines | Hunks |
|---|---|---|---|---|
| Whole-app fixes, on both branches | ||||
| Chat working row shimmer | chat/TimelineWorkGroupRows.tsx | hook | +2 −3 | 3 |
| Allow and Deny as buttons, timeline | chat/TimelinePendingApprovalRow.tsx | hook | +1 −4 | 2 |
| Allow and Deny as buttons, child approval | chat/ChildThreadApprovals.tsx | hook | +1 −1 | 1 |
| AA on working rows, hide a lone provider logo | sidebar/SidebarThreadRow.tsx | hook | +1 −3 | 2 |
| Approval never recedes | Sidebar.logic.ts | T3 logic | +2 −1 | 2 |
| Variation A | ||||
| Groups replace the folder list; every project keeps a folder | Sidebar.tsx | T3 logic | +69 −33 | 7 |
| Row hands its logic to the new layout | sidebar/SidebarThreadRow.tsx | hook | +108 −1 | 3 |
| Groups, Waiting on you, worker strip | sidebar/stable/StableSidebarGroups.tsx | new file | +320 | 1 |
| Row layout | sidebar/stable/StableThreadRow.tsx | new file | +150 | 1 |
| Answers on the row | sidebar/stable/RowAnswers.tsx | new file | +148 | 1 |
| Motion, tint, bar | sidebar/stable/stableSidebar.css | new file | +130 | 1 |
| Worker states, stable order, a test | sidebar/stable/workers.ts, stableGroups.ts, stableGroups.test.ts | new file | +103 | 3 |
| A in total, without the whole-app fixes | 851 lines in new files | +1028 −34 | 17 | |
| Variation B | ||||
| Folder rows use the new layout, pinned rows go slim, worker counts | Sidebar.tsx | T3 logic | +9 −5 | 5 |
| Row hands its logic to the new layout | sidebar/SidebarThreadRow.tsx | hook | +88 −1 | 3 |
| Row layout and spinning mark | sidebar/slim/SlimThreadRow.tsx | new file | +118 | 1 |
| Worker states | sidebar/slim/workers.ts | new file | +42 | 1 |
| B in total, without the whole-app fixes | 160 lines in new files | +257 −6 | 10 | |
The pick, not built: B plus RowAnswers.tsx | 308 lines in new files, plus a hook not yet written | +405 −6 | 11+ | |
| The pick with the whole-app fixes | +412 −18 | 21+ | ||
measured Counted between study-base and each variation, leaving out the one-line harness change. To ship either, the default of boringSidebarEnabled also has to flip; the study harness sets it at boot, so the branches do not carry that line. The 79 changes in 30 days is your count. My shallow clone can't confirm it.
B's +88 in SidebarThreadRow.tsx is one early return that passes the row's computed state to the new file, and it is a big hook. Most of it is a list of props, so I would expect it to conflict when T3 renames what it passes. A's +108 has the same shape. A's exposure is the Sidebar.tsx list render, where T3's own folder loop had to go.
How this was made, and what it doesn't show
T3 ran from a shallow clone at 337c5858, in a sealed environment with its own home folder and none of this machine's secrets or logins. The real web app and its real stores ran with the socket swapped for an in-process fake adapted from T3's own apps/web/test/fakeEnvironment.ts. No server, no agent, no sign-in. I set T3CODE_TELEMETRY_ENABLED=false, and the captures made no request outside the local machine. Default dark is T3's real default: no stored theme and a dark system preference, which paints #000.
Branches in the clone, none pushed: study-before, study-base, variation-a, variation-b. All typecheck with T3's effect-tsgo, and T3's 81 sidebar logic tests pass on both variations.
- Every image is a capture. None is recreated. The motion recreations are labeled, and they are the only drawn parts.
- One visual change for fairness. The harness reports the stage as Beta, because a dev build paints a blue banner your builds never show.
- Fonts. T3's stack starts with
-apple-system, so these Linux captures use a fallback and not SF Pro. Text widths will differ on a Mac. - Answers not end to end. In A, clicking Allow and an option dispatched the right command to the fake socket. No server processed it, so the row did not clear.
- The pick is not built. B with
RowAnswers.tsxhas no branch, capture or measurement. Its line count is addition, and its 48px is A's figure. - Untested. Desktop shell, mobile app, multiple environments, drag, keyboard list navigation on the new rows, screen readers, light theme. Dragging is already off in Boring mode.
- A's settled counts cover loaded threads only. T3 pages older settled threads, and A does not load more yet.
- Not surveyed. The recommendation is my judgment from these numbers, with one user's complaint. Nobody tried either variation at work.
- Review scope. One default theme, seeded data, and my scores. The review did not re-score A or B.
Unofficial design study, not made by or endorsed by T3. T3 Code is MIT licensed, commit 337c5858. Private: not published, not sent upstream. All projects and threads shown are made up.