feat: render chat summaries as a headline plus bullets - #28013
Conversation
The whole-chat summary shown in the Summary tab rendered as a single prose blob. Three layers each independently prevented structure: the generation prompt banned all markdown, the normalizer collapsed every newline into a space, and the panel rendered the result into one plain <p>. Validation was also looser than the prompt asked, permitting up to 1000 runes and six sentences in a 448px column. Generation now returns a structured headline plus 2-4 bullets, which a shared serializer renders to markdown stored in the existing summary column. No migration is needed. Both summary producers go through formatChatSummaryMarkdown so the subagent path cannot bypass the format. Subagent summaries are extracted from an existing report rather than generated, so they pass no bullets and the serializer returns their snippet unchanged. Validation moved onto the struct, before serialization. Applying the old sentence cap to serialized markdown would have silently stopped working, since bullets routinely omit trailing punctuation. Per-field normalization also preserves backticks, which the shared normalizeShortTextOutput strips, so a headline ending in an inline code span keeps a balanced pair. The panel renders the markdown through InlineMarkdown with a narrow allowlist, bounded to max-h-48 with a Show more toggle. Legacy prose summaries continue to render; summaries regenerate after three completed turns, so both shapes coexist without a backfill.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ede189b76a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Observe overflow on an inner unclamped element rather than the clamped box. The clamped box stops growing at its max height, so a summary that arrives via an in-place cache update while the box is already pinned there resized nothing and stayed clipped with no toggle. Drop useCallback, which site/AGENTS.md prohibits under src/pages/AgentsPage since React Compiler already memoizes callbacks there. The measurement closure now lives inside the effect. Render link text without an anchor. Clipping below the collapsed bound is visual only, so a mounted anchor stayed reachable by keyboard and screen readers while invisible.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 634f89079a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
generateChatSummary now returns a headline plus bullets, but its doc comment still described a 1-3 sentence summary. The subagent snippet bound in chatd.go referenced the same obsolete contract.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dff5f061b7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ummaries Summaries preserve identifiers verbatim and wrap them in backticks, so a single token can be wider than the panel with no natural break opportunity. The summary box sized itself to that token, escaping its column, and the clipped remainder was unreachable because the toggle only responds to vertical overflow.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9af0084547
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…rom summary story FE10 prohibits DOM-geometry assertions because pixel values vary with font rendering across environments. The long-identifier story keeps its narrow column as a visual regression fixture and asserts semantically instead.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 90f83d6752
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…surement Dragging the right panel resized the summary every frame, and each observer callback read layout and set state even though the overflow verdict is a boolean that flips at most a couple of times per drag. Waiting for the resize to settle skips those reads entirely while a drag is in flight.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 32cd37b6e0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…r chat Switching chats swaps the panel's props instead of remounting it. An uncached chat happened to reset the expanded state, because the panel renders nothing while loading and that unmounts the summary, but a cached chat resolves synchronously and kept the previous chat's expanded state, offering to collapse a summary that may not overflow at all.
|
@codex review |
|
Codex Review: Didn't find any major issues. 🚀 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
… disclosure toggle The summary panel is already a scroll container, so clamping the summary to a fixed height put a second, worse overflow mechanism in front of content the reader could already reach by scrolling. Generated summaries are capped at 600 runes server-side and rarely reach the clamp, so the toggle mostly never appeared. Removing it also removes the overflow measurement it required: the ResizeObserver, the settle debounce, the two refs, and the per-chat key that existed only to reset the expanded state.
Requiring at least two bullets gave a trivial chat two bad options: the model pads the summary with filler, or validation rejects it and the panel falls back to "No summary yet". The rejection was silent, logged only at Debug. Drop the lower bound and keep the cap at four, and tell the model to leave the bullets field empty when the headline already covers the whole chat. The serializer already renders a bare headline for empty bullets, which is the same path subagent summaries take.
# Conflicts: # coderd/x/chatd/quickgen.go
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
…s dead Normalization runs before validation and serialization, so several downstream defensive checks could never fire: - normalizeSummaryField guarded the empty case that strings.Fields already handles. - formatChatSummaryMarkdown re-filtered blank bullets that normalizeSummaryBullets had already dropped, and built the output with a Builder plus a trailing-newline trim where a Join does. - validateGeneratedChatSummary rejected bullets containing newlines, which strings.Fields collapses before validation sees them. Record the normalization precondition on formatChatSummaryMarkdown so the invariant is documented rather than re-checked, and drop the tests that only covered the dead branches. Blank-bullet and newline handling stay covered by TestNormalizeSummaryBullets and TestNormalizeSummaryField at the layer that actually performs them. Also drop the duplicated LongSummary panel story; it repeated the ChatSummary story verbatim while asserting nothing about the panel.
Shorten comments added in this PR to the rationale they carry, drop the ones that restated the code, and inline the shared Tailwind list classes at each call site instead of routing them through a constant.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 91a04ced7c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 99ef133e18
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2749c3d8ba
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2d6a39b97a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b8c059d5d3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f29f74fae
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Merge origin/main, preserve both database and frontend changes, and renumber the summary migration to 000591.\n\nGenerated by Coder Agents.
Adopts the structured-object approach from #28013: the model returns headline and bullets fields that the server serializes to markdown, so validation can check each field instead of parsing the rendered string. Keeps the capability-first headline wording, enforces the 20-word headline limit, and renders through InlineMarkdown so backticked identifiers become inline code.
Render whole-chat summaries in the Summary tab as a concise headline with up to four short bullets, omitting bullets when the headline is sufficient. Preserve inline code for identifiers and support existing prose summaries and subagent report snippets.
The panel renders constrained Markdown and scrolls naturally without a Show more toggle. Chats without a summary show a neutral empty state, and persisted summary and cost data refresh after reconnecting.
This uses the existing summary storage and summary-change events, with no database migration, durable generation-status tracking, or backfill. Sidebar turn summaries, compaction summaries, and chat titles are unchanged.