feat: add a session summary panel to the chat side panel - #3169
Conversation
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSAdvisory design-level review of Design-Verdict: PASS Real gap (backend from #2855 had no consumer), solved at the right layer with the closest precedent, the WS envelope fix in the same commit, and spec updated. [DESIGN-REVIEWED] 0e790d5 |
UX Review (Fable 5) — 🟡 CONCERNSAdvisory UX-level review of UX-Verdict: CONCERNS Solid, state-complete panel — but its freshness warning is muted in the footer while the triage block above makes the claims that go stale. Watch
Suggestions
[UX-REVIEWED] 0e790d5 |
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsI've verified the single candidate. The No findings. [OPUS-REVIEWED] 0e790d5 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
9d751b0 to
c7cab57
Compare
c7cab57 to
7026bbb
Compare
Dispositions for
|
7026bbb to
c90ff9f
Compare
Dispositions for
|
c90ff9f to
37c2760
Compare
5c4cd05 to
a74368a
Compare
|
Rebased onto The rebase#1636 registers a new panel tab in the same seven registries this branch touches, so 16 files conflicted:
E2E: correcting my own earlier diagnosisI previously suggested sourcing The actual cause was the group's React key being derived from its contents ( Proven non-inert: reverting the key to
Findings1 — launcher left on the pre-reshape shape. FIXED. 2 — This was visible in 3, 4 — comment policy. FIXED. Dropped 5 — a comment stating something false. FIXED. The spec claimed the summary route "ships in a separate change, so on this branch's gateway it does not exist yet". It is registered at the base commit ( 6 — helper only reshaped, not re-typed. FIXED. 7 — orphaned doc comment. FIXED. Moved onto 8 — raw Verification
Two caveats, stated rather than buried:
|
a74368a to
4d7bd62
Compare
E2E: root cause and fix (
|
Renders the intent-level session summary as a side-panel tab: what each
thread of the session was trying to do, where it landed, and what still
needs the reader. Open items are hoisted into a triage block at the top,
each naming the intent it came from so lifting it out does not sever the
context that makes it decidable. Intent cards are collapsible with the
most recently touched one open, and the durable project facts are pinned
outside the scroll region so they do not have to be hunted for.
The panel does not poll. Freshness comes from the session_summary
websocket event, invalidating the per-slot query on regeneration. A panel
on an interval would reward the refresh habit the feature exists to
remove, and would cost a request per tick for data that changes once a
turn.
Reads the summary through GET /api/chat/slots/{slot}/summary. Off by
default: with the flag disabled the endpoint reports disabled and the
panel explains itself rather than looking broken. The settings toggle
ships separately, so the off state is reachable before it can be turned
on and has to stand on its own.
Every user-facing string is translated across the 12 shipped catalogs.
The needs-you count uses i18next plural selection rather than a
plural-neutral phrasing, so its base key is registered in pluralKeys.json
and Russian carries the few and many forms its rules require.
Adds 34 unit tests and 9 Playwright specs, and raises
MIN_EXECUTED_SPECS accordingly. The specs stub the summary route: a real
summary costs a model call the credential-less CI gateway cannot make,
and stubbing keeps them about the panel rather than about model output.
Screenshots under temp-screenshots/session-summary-panel/ are dark-theme
only. The capture harness has no gateway, so the custom color theme's
variables are never injected and a light capture would be half-themed
rather than accurate. The component holds no color literals; every color
is a theme token.
4d7bd62 to
0e790d5
Compare
E2E round 2: the helper fix landed, two spec bugs behind it (
|
Dispositions for UX Review 🟡 CONCERNS on
|
Renders the intent-level session summary as a side-panel tab: what each
thread of the session was trying to do, where it landed, and what still
needs the reader. Open items are hoisted into a triage block at the top,
each naming the intent it came from so lifting it out does not sever the
context that makes it decidable. Intent cards are collapsible with the
most recently touched one open, and the durable project facts are pinned
outside the scroll region so they do not have to be hunted for.
The panel does not poll. Freshness comes from the session_summary
websocket event, invalidating the per-slot query on regeneration. A panel
on an interval would reward the refresh habit the feature exists to
remove, and would cost a request per tick for data that changes once a
turn.
Reads the summary through GET /api/chat/slots/{slot}/summary. Off by
default: with the flag disabled the endpoint reports disabled and the
panel explains itself rather than looking broken. The settings toggle
ships separately, so the off state is reachable before it can be turned
on and has to stand on its own.
Every user-facing string is translated across the 12 shipped catalogs.
The needs-you count uses i18next plural selection rather than a
plural-neutral phrasing, so its base key is registered in pluralKeys.json
and Russian carries the few and many forms its rules require.
Adds 34 unit tests and 9 Playwright specs, and raises
MIN_EXECUTED_SPECS accordingly. The specs stub the summary route: a real
summary costs a model call the credential-less CI gateway cannot make,
and stubbing keeps them about the panel rather than about model output.
Screenshots under temp-screenshots/session-summary-panel/ are dark-theme
only. The capture harness has no gateway, so the custom color theme's
variables are never injected and a light capture would be half-themed
rather than accurate. The component holds no color literals; every color
is a theme token.
# Conflicts:
# website/src/i18n/locales/bn.json
# website/src/i18n/locales/de.json
# website/src/i18n/locales/en-XA.json
# website/src/i18n/locales/en.manual.json
# website/src/i18n/locales/es.json
# website/src/i18n/locales/fr.json
# website/src/i18n/locales/hi.json
# website/src/i18n/locales/it.json
# website/src/i18n/locales/ja.json
# website/src/i18n/locales/ko.json
# website/src/i18n/locales/pt.json
# website/src/i18n/locales/ru.json
# website/src/i18n/locales/zh-CN.json
# website/src/pages/chat/SidePanel.tsx
# website/src/test/sidePanelAddMenu.test.tsx
Problem
A long session gives you no way to see what it was about. To recover the thread of a hundred-turn conversation — what you were trying to do, which threads finished, what is still waiting on you — you scroll back and re-read.
#2855 added the backend that derives that summary. Nothing renders it.
Why it matters
The summary is only useful if a reader can see it at the moment they return to a session. Without a surface it is a sidecar file on disk that nothing consumes.
Fix (symptom → root cause → change)
Symptom: no way to read a session's summary.
Root cause: the panel surface does not exist;
GET /api/chat/slots/{slot}/summaryhas no consumer.Change: a new
Summarytab in the chat side panel.session_summarywebsocket event invalidating the per-slot query. A panel on an interval would reward the refresh habit this feature exists to remove, and would cost a request per tick for data that changes once a turn.One backend change rides along, and it is the reason that last point is true. #2855 added
push_session_summary, but_broadcasthad no typed WS branch for the event, so it fell through to the genericnotificationenvelope — and because the client dispatches on the outertype, the panel'scase 'session_summary'was unreachable. The summary only appeared after a manual reload, and the payload also landed in the bell feed as ats-less notification._broadcastnow emits{"type": "session_summary", "data": {"key": ...}}, mirroring theartifact_updatebranch and the reason its comment gives. This is the only non-frontend code in the PR.Registered across the seven places a side-panel view has to be declared (
ViewKind,VIEW_TITLE_KEY,KIND_ICON, the twoNEW_MENU_*maps,NEW_MENU_GROUPS,VIEW_KINDS), plusActivityViewer's view union, its dispatch, and the segmented-control guard. It followsContextBreakdownTab— the closest sibling, and the right precedent: a per-slot, read-only view of derived data, taking onlyslotand fetching its own.Empty and error states
Three states a reader will hit before this feature is fully rolled out, each of which has to explain itself rather than look broken:
refetch. Without it the failure branch returned before the header rendered, so the one control that could recover the panel disappeared exactly when it was needed.Tests
sessionSummaryTab.test.tsxcovers all four states, ordering, triage hoisting with provenance, disclosure persistence across remount for both the intent cards and the triage items, the pinned notes, the plural chip, and that the panel fetches once and does not poll. Several assert recovery or reveal rather than presence: clicking Retry / Reload actually loads content, and expanding a collapsed triage item actually surfaces its reasoning and source intent — a collapsed-by-default element is easy to test into a state where it is merely absent.sessionSummaryHelpers.test.ts— the pure helpers,collectTriageordering especially (needs-you before merely-recent, dropped intents never hoisted, no double-count across the two passes).sidePanelAddMenu.test.tsxfixtures updated for the new menu entry; that suite pins that every entry appears exactly once across the groups.website/playwright/session-summary.spec.ts),MIN_EXECUTED_SPECSraised 210 → 219. They stub the summary route withpage.route: a real summary costs a model call the credential-less CI gateway cannot make, and stubbing keeps the specs about the panel rather than about model output.tsc -b,eslint, and all 16i18n:checkgates pass.i18n
30 keys across the 12 shipped catalogs. The needs-you count uses i18next plural selection rather than a plural-neutral phrasing, so its base key is registered in
pluralKeys.jsonand every locale carries exactly the CLDR categories it selects: Russian_few/_many, Spanish/French/Portuguese/Italian_many(these four select it for large and compact numbers), and zh-CN/ja/ko_otheronly.catalogParity.test.tsenforces both directions — a missing category and a category the language never selects.Manual verification
Verified against a real session in an isolated dev gateway with the feature enabled — the panel rendered a live summary (3 intents from 6 user turns), not fixture data.
This depended on #3167 — the fix for the backend never storing a summary at all — which merged on 2026-08-13, so there is no longer a sequencing constraint.
Screenshots
Seven states plus the expanded interaction, captured by rendering the real component (dark theme; the harness has no gateway, so the custom color theme's variables are never injected and a light capture would be half-themed rather than accurate — the component holds no color literals, which is the property a light capture would have tested).
Remaining states
Known gaps, deliberately left
POSTendpoint and reverses feat: generate intent-level session summaries behind a flag #2855's deliberate read-only stance. Its own PR.