Skip to content

fix(session-ui): cohesive segments — label heuristic + blank-card guard #632

Description

@jeonghun-jj-lee

Context

Part of #628 — chat UI polish.

This slice strengthens the chunk-boundary heuristic in session-ui so that short labels stay with their content and blank bordered cards never render. Independent of the dot/rail/scroll work.

Acceptance Criteria

  • A short label (<40 chars ending with :) followed by any content stays in one card
  • Consecutive list items / blockquotes never split (existing guard preserved)
  • No blank/empty bordered cards render in the timeline

Key Decisions

Decision Rationale
40-char length gate on label heuristic Catches orphaned labels (common in LLM output like "Results:", "Summary:") without false-positiving on mid-sentence colons
Fires regardless of what follows (list, paragraph, quote) A label is a label whether it precedes a list or a paragraph
Blank-card suppression via trim guard If a chunk is all-whitespace after trimming, skip rendering — simplest fix, no upstream boundary logic change
Chunk splitting remains monotonic Once a boundary is emitted during streaming, it never un-emits — this invariant is preserved

Testing Decisions

Extend existing packages/session-ui/src/components/message-part.test.ts:

  • Label + list stays together: "Results:\n- item 1\n- item 2\n\nNext paragraph" — first chunk includes label and list
  • Label + paragraph stays together: "Summary:\nThis is the summary.\n\nAnother section" — first chunk includes both
  • Long line with colon still splits: "This is a complete sentence that happens to end with a colon:\n\nNext" (>40 chars) — normal split
  • All-whitespace chunk produces no DOM node: verify ChunkedStreamMarkdown skips rendering
  • Existing continuation guard preserved: consecutive list items across a blank line still do not split

Constraints & Invariants

  • Must not break fence-tracking state (code blocks never split)
  • Trailing blanks remain non-boundaries (streaming tail detection)
  • settledChunkBoundary return semantics unchanged (index past last boundary, 0 if none)

Prior Art

  • packages/session-ui/src/components/message-part-text.ts — current chunkBoundaries implementation with fence tracking and continuation guard
  • packages/session-ui/src/components/message-part.tsxChunkedStreamMarkdown renders chunks as data-prose-fragment divs
  • packages/session-ui/src/components/message-part.test.ts — existing chunk boundary unit tests

Metadata

Metadata

Labels

afkImplementable without human interactionarea:ui

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions