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
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.tsx — ChunkedStreamMarkdown renders chunks as data-prose-fragment divs
packages/session-ui/src/components/message-part.test.ts — existing chunk boundary unit tests
Context
Part of #628 — chat UI polish.
This slice strengthens the chunk-boundary heuristic in
session-uiso that short labels stay with their content and blank bordered cards never render. Independent of the dot/rail/scroll work.Acceptance Criteria
:) followed by any content stays in one cardKey Decisions
Testing Decisions
Extend existing
packages/session-ui/src/components/message-part.test.ts:"Results:\n- item 1\n- item 2\n\nNext paragraph"— first chunk includes label and list"Summary:\nThis is the summary.\n\nAnother section"— first chunk includes both"This is a complete sentence that happens to end with a colon:\n\nNext"(>40 chars) — normal splitChunkedStreamMarkdownskips renderingConstraints & Invariants
settledChunkBoundaryreturn semantics unchanged (index past last boundary, 0 if none)Prior Art
packages/session-ui/src/components/message-part-text.ts— currentchunkBoundariesimplementation with fence tracking and continuation guardpackages/session-ui/src/components/message-part.tsx—ChunkedStreamMarkdownrenders chunks asdata-prose-fragmentdivspackages/session-ui/src/components/message-part.test.ts— existing chunk boundary unit tests