Uh oh!
There was an error while loading. Please reload this page.
fix(web): unify activity logs and composer banners - #221
Conversation
Adapted for Pylon. Upstream replaces the bespoke .chat-composer-* CSS with ComposerSurface/ComposerBanner primitives; Pylon's fork-only surfaces move onto the same primitives rather than being dropped: - ComposerTasksBadge keeps waitingOn, per-turn dismiss and delegated-agent counts on top of upstream's banner shape, and keeps Pylon's task segments and status indicator so a waiting step still names who it waits on; - the timeline working row and its workingStepLabel stay, which #8734 also keeps; - BranchToolbar keeps the branch selector in the left run with Usage opposite, per the comment already there, and adopts ComposerSurface.ContextStrip; - ChatComposer keeps ThreadHandoffTab, the quick-question and session-resource dialogs, the attachment control, externalDrawerAttached and the collapsed mobile stash toggle; - session-logic keeps Pylon's activity skip list and adds turn.plan.updated. Upstream's simpler work grouping is adopted: it excludes agent-spawn and error rows from grouping outright, which preserves the #7893 and #7999 findings by construction, so MAX_VISIBLE_WORK_LOG_ENTRIES and onlyToolEntries go. The inline per-turn plan chip goes with it. Upstream folds plans into the composer tasks badge, which Pylon keeps and extends with waitingOn, so the transcript chip would duplicate it; deriveTurnPlans, TurnPlanTimelineRow and the turn-plan row kind are removed. (cherry picked from commit 30175a8afc42d2e8cb4e0b3a0e2b1c0aa96a0e2e)
(cherry picked from commit 9842518c9a0af14fb3a3e90993692a6e34032682)
Adapted for Pylon: upstream also strips workingStepLabel so the timeline timer shows only elapsed time. Pylon keeps it, so the row still says what the agent is doing and not merely how long it has been doing it. Everything else in the commit is taken as-is. (cherry picked from commit 3f62e6fa65c2a2a91a367be4ee95da1b50007bde)
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Adversarial review found the cherry-pick had left features wired to nothing and
two blocks rendered twice. Typecheck stayed green throughout because none of it
is a type error.
Rendered twice: the collapsed-mobile prompt row, and a second
data-chat-composer-main-surface wrapper nested inside ComposerSurface.Main
repeating composerFrameClassName - which in ultrathink mode ran the infinite
rainbow animation on two nested elements. ComposerSurface.Main already carries
that attribute, so the wrapper was pure duplication.
Wired to nothing: per-turn tasks dismiss reached no surface, so dismissTasks and
the dismissal snapshot were dead; delegated-agent counts reached only the inline
badge; and #8855's removal of workingStepLabel applied through a non-conflicting
hunk despite being declined, leaving the prop and memo dep behind while the row
stopped rendering the step.
Also: the dock and inline stash badges were no longer mutually exclusive; the
inline stash badge wrapped itself in a glass banner inside a compact toolbar;
the drawer-inset fallback was dropped, and since the custom property went with
the deleted CSS, parseFloat("") gave NaN and a NaN never settles; the composer's
activity item was prepended, tying with background liveness and tucking its Stop
button - the only stop affordance for a settled turn - behind the peek;
dismissing a failed update also wrote the permanent version-mismatch dismissal;
the tasks aria-label hardcoded "Current task" for waiting, next and completed
plans, and the per-step status moved after the step text; and groups of two or
more non-tool rows collapsed to "Received N updates", hiding text that is the
whole point of a runtime warning.
Restores tests for ComposerBannerStack and ComposerStashBadge, deleted by the
same change that rewrote them, including one that pins the ordering above.rynfar
commented
Aug 31, 2026
Adversarial review found thirteen defects, and it was right to be blunt: several features this PR claimed to keep were wired to nothing. Typecheck stayed green throughout, because none of it is a type error. All fixed on the branch. Claims in the original description that were false
All three are now wired through every surface, and the task surfaces gate on the dismissal-aware values rather than the raw ones. Rendered twice
Other defects fixed
Also removed dead imports and a dead Re-verified: The review's closing point stands and I'm acting on it: findings 1, 2, 4 and 5 are exactly what a single real-client pass catches. Doing that next before this merges. |
rynfar
commented
Aug 31, 2026
Real-client pass done. Ran the branch against an isolated dev environment ( The structural defects the review found are gone:
Also verified live:
Two honest gaps in this pass:
|
Closes DEF-9 from the upstream review ledger.
Pylon's composer grew a set of bespoke
.chat-composer-*CSS rules that everynotice, badge and drawer positioned itself against by hand. Upstream replaced
them with
ComposerSurface/ComposerBannerprimitives and deleted 524 lines ofthat CSS. This adopts the primitives and moves Pylon's fork-only surfaces onto
them rather than dropping them.
Adopted from upstream
pingdotgg/t3code:#8734(30175a8af),#8850(
9842518c9),#8855(3f62e6fa6).Why three commits and not four
The ledger recorded this as
#8693. Upstream reverted#8693twelve hours aftermerging it (
#8733), then re-landed a substantially different version as#8734— 979 insertions of difference across 18 files, including keeping thetimeline working row that
#8693had deleted.#8733restores the pre-#8693tree exactly, so the revert/re-land pair cancels and
#8734is a clean re-landonto a tree shaped like Pylon's. Porting
#8693would have adopted a versionupstream itself abandoned.
What Pylon keeps
ComposerTasksBadgetakes upstream's banner shape but keepswaitingOn("Needs your input" / "Waiting on agents" / "Waiting on external system"),
per-turn dismiss with its
turnId+step:waitingOnsnapshot, delegated-agentcounts, and Pylon's
TaskProgressSegments/TaskStatusIndicator. Upstream'sbadge has none of these. Segments hide while the list is expanded, since the
list already shows every step.
workingStepLabel.#8734keeps the row;#8855strips the step label so the timer shows only elapsed time. That onehunk is declined — the row still says what the agent is doing, not merely how
long.
BranchToolbaradoptsComposerSurface.ContextStripwhile keeping thebranch selector in the left run with Usage opposite, per the comment already
there. Upstream's right-hand copy would have rendered it twice.
ChatComposerkeepsThreadHandoffTab, the quick-question andsession-resource dialogs, the attachment control,
externalDrawerAttachedandthe collapsed-mobile stash toggle. The last two were dropped silently by the
cherry-pick in non-conflicting hunks; typecheck caught them.
session-logickeeps Pylon's activity skip list and addsturn.plan.updated.What Pylon gives up
MAX_VISIBLE_WORK_LOG_ENTRIESandonlyToolEntriesare gone in favour of upstream's simpler grouping, whichexcludes agent-spawn and error rows from grouping outright. That preserves the
#7893and#7999findings by construction — a spawn row can no longer beinside a group, so it can never hide behind a "+N tool calls" toggle — and
hasFailurekeys offlatestToolEntry, which is exactly the "#7893 retriedsuccessfully is not a failure" rule.
deriveTurnPlans,TurnPlanTimelineRowandthe
turn-planrow kind are removed. Upstream folds plans into the composertasks badge, which Pylon keeps and extends with
waitingOn, so thetranscript chip would show the same plan twice. Reversible if you disagree —
it needs the row kind restored into upstream's rewritten grouping.
One behaviour note worth a look: a missing-response notice now renders as a
work-togglerather than a plainworkrow. It stays outside the fold and alone non-tool entry surfaces its own label as the summary, so the message text is
still what the user reads.
Verified:
vp run -F @t3tools/web typecheck(exit 0, no errors);vp test run --dir apps/web apps/web/src/components/chat apps/web/src/session-logic.test.ts apps/web/src/components/ChatView.logic.test.ts apps/web/src/versionSkew.test.ts(637 passed);
vp fmt --check apps/web/srcclean. Also checked that no JSXstill references a removed
.chat-composer-*class.Not exercised in a browser. This restructures the composer's banner, drawer
and shoulder-tab layout, which unit tests cannot judge — it wants a real-client
pass before merge.
Reviewed and integrated with Claude Opus 5 in Claude Code.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.