You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Three layout/polish defects on the Workspace client surface, all in the agent page Overview and the shared avatar:
The two-column split is conditional on data. In PortalAgentPage.vue the Overview grid is <div class="mb-6 grid gap-6" :class="{ 'lg:grid-cols-2': asks.length }"> — the second column only exists when the agent has open asks. An agent with nothing waiting collapses to a single stacked column, so the page reflows into a different shape depending on whether a transient operator-queue item happens to be open. The structure should be stable; only the content should vary.
Wrong things are sharing the row, in the wrong order. Today the row is Waiting on you (left) | Recent work (right), with the activity chart sitting alone above in a max-w-2xl section. The intended layout is: activity chart and recent work share one 50/50 row, and asks ("Waiting on you") move below that row, full width, rendered only when there are asks.
The agent avatar has no edge.PortalAvatar.vue renders a bare rounded-full span with no border. Against the sidebar and chat backgrounds — and especially for image avatars whose own edges are light — the avatar bleeds into the surface. It needs a thin border in both places it appears (sidebar roster and chat rows), which is one change in the shared component.
Context
Follow-up to #2161, which restructured this Overview (moved asks into the grid, reused the shared chart). The layout that landed keys its column count off asks.length, which is exactly the instability being reported here. Same file family, same reviewer context.
Target layout:
┌───────────────────────────┬───────────────────────────┐
│ Activity · last {window} │ Recent work │ <- always two columns
└───────────────────────────┴───────────────────────────┘
┌───────────────────────────────────────────────────────┐
│ Waiting on you │ <- only when asks.length
└───────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────┐
│ Your chats with {agent} │
└───────────────────────────────────────────────────────┘
Note that the target layout resolves the stability complaint by construction: both columns of the top row (activity, recent work) always exist and already have their own empty states ("No activity in this window." / "Nothing yet."), so the split never has to collapse.
Acceptance Criteria
The Overview top row is a 50/50 two-column grid holding activity chart (left) and Recent work (right); the column count does not depend on asks.length or on either section having data
Both columns render their existing empty states in place rather than collapsing the grid — an agent with no executions and no activity still shows two columns
"Waiting on you" moves below that row, full width, and is rendered only when asks.length > 0 (no empty-state placeholder — an agent with nothing waiting should not advertise the section)
The activity section's max-w-2xl constraint is removed or re-scoped so the chart fills its column
The grid collapses to a single stacked column below the xl breakpoint (narrow viewports keep today's stacking behaviour) — layout stability applies within a breakpoint, not across them
Amended from lg to xl during planning, on measurement.lg was written from intent, not arithmetic. At 1024px, dropping the activity section's max-w-2xl and splitting 50/50 takes the chart from 672px to 332px (sidebar w-72 + px-6 + gap-6) — the 30-day x-axis truncates to nothing and the 9-bucket legend wraps to 3–5 lines. StackedBarChart is pure CSS/flexbox, so the theme criterion below would still pass while the left column became unreadable. Recorded here so a later reader does not "fix" it back to lg.
PortalAvatar.vue renders a thin border on every instance, visible in both light and dark themes, using semantic tokens (no raw palette classes, no hardcoded hex)
The border does not change the component's outer footprint or clip the image fill — verify the avatar still aligns in the sidebar roster rows and the chat rows at every size it is used with
Verified in the browser on the agent page in both themes: with asks and without, with executions and without
Technical Notes
src/frontend/src/components/portal/PortalAgentPage.vue — the Overview <template v-else-if="tab === 'overview'"> block holds the activity <section class="mb-6 max-w-2xl">, the conditional grid, and the chats section
src/frontend/src/components/portal/PortalAvatar.vue — single shared component; both the sidebar (PortalSidebar.vue) and chat (PortalChatRow.vue / PortalConversation.vue) consume it, so the border is one edit, not three
Read docs/memory/design-system-contract.md before touching either file (semantic tokens only, both themes first-class, layout stability)
No backend change; no new endpoint. Pure frontend layout.
Planning decision — mobile ask-priority (recorded, reversible)
Moving "Waiting on you" below the row applies at every breakpoint, so on a stacked narrow viewport the asks sit under activity + recent work. This was raised as a blocking objection during plan review and is implemented as specified — the layout was an explicit instruction and an acceptance criterion is not the planner's to overrule.
Mitigations already present: the section is conditional (absent when there are no asks) and the Overview tab carries a count badge. The costed alternative (a ~3-line mobile-only order swap) is noted in the PR body so it can be reversed in one commit if the stacked ordering proves wrong in use.
Summary
Three layout/polish defects on the Workspace client surface, all in the agent page Overview and the shared avatar:
The two-column split is conditional on data. In
PortalAgentPage.vuethe Overview grid is<div class="mb-6 grid gap-6" :class="{ 'lg:grid-cols-2': asks.length }">— the second column only exists when the agent has open asks. An agent with nothing waiting collapses to a single stacked column, so the page reflows into a different shape depending on whether a transient operator-queue item happens to be open. The structure should be stable; only the content should vary.Wrong things are sharing the row, in the wrong order. Today the row is
Waiting on you(left) |Recent work(right), with the activity chart sitting alone above in amax-w-2xlsection. The intended layout is: activity chart and recent work share one 50/50 row, and asks ("Waiting on you") move below that row, full width, rendered only when there are asks.The agent avatar has no edge.
PortalAvatar.vuerenders a barerounded-fullspan with no border. Against the sidebar and chat backgrounds — and especially for image avatars whose own edges are light — the avatar bleeds into the surface. It needs a thin border in both places it appears (sidebar roster and chat rows), which is one change in the shared component.Context
Follow-up to #2161, which restructured this Overview (moved asks into the grid, reused the shared chart). The layout that landed keys its column count off
asks.length, which is exactly the instability being reported here. Same file family, same reviewer context.Target layout:
Note that the target layout resolves the stability complaint by construction: both columns of the top row (activity, recent work) always exist and already have their own empty states ("No activity in this window." / "Nothing yet."), so the split never has to collapse.
Acceptance Criteria
The Overview top row is a 50/50 two-column grid holding activity chart (left) and Recent work (right); the column count does not depend on
asks.lengthor on either section having dataBoth columns render their existing empty states in place rather than collapsing the grid — an agent with no executions and no activity still shows two columns
"Waiting on you" moves below that row, full width, and is rendered only when
asks.length > 0(no empty-state placeholder — an agent with nothing waiting should not advertise the section)The activity section's
max-w-2xlconstraint is removed or re-scoped so the chart fills its columnThe grid collapses to a single stacked column below the
xlbreakpoint (narrow viewports keep today's stacking behaviour) — layout stability applies within a breakpoint, not across themPortalAvatar.vuerenders a thin border on every instance, visible in both light and dark themes, using semantic tokens (no raw palette classes, no hardcoded hex)The border does not change the component's outer footprint or clip the image fill — verify the avatar still aligns in the sidebar roster rows and the chat rows at every size it is used with
Verified in the browser on the agent page in both themes: with asks and without, with executions and without
Technical Notes
src/frontend/src/components/portal/PortalAgentPage.vue— the Overview<template v-else-if="tab === 'overview'">block holds the activity<section class="mb-6 max-w-2xl">, the conditional grid, and the chats sectionsrc/frontend/src/components/portal/PortalAvatar.vue— single shared component; both the sidebar (PortalSidebar.vue) and chat (PortalChatRow.vue/PortalConversation.vue) consume it, so the border is one edit, not threedocs/memory/design-system-contract.mdbefore touching either file (semantic tokens only, both themes first-class, layout stability)Planning decision — mobile ask-priority (recorded, reversible)
Moving "Waiting on you" below the row applies at every breakpoint, so on a stacked narrow viewport the asks sit under activity + recent work. This was raised as a blocking objection during plan review and is implemented as specified — the layout was an explicit instruction and an acceptance criterion is not the planner's to overrule.
Mitigations already present: the section is conditional (absent when there are no asks) and the Overview tab carries a count badge. The costed alternative (a ~3-line mobile-only order swap) is noted in the PR body so it can be reversed in one commit if the stacked ordering proves wrong in use.