From 6426943e2f5616d57069422d2163efd58c5b9053 Mon Sep 17 00:00:00 2001 From: Vignesh Date: Sun, 4 Oct 2026 02:11:37 +0530 Subject: [PATCH 1/2] chat: a bare number-and-period reply draws instead of vanishing (#1072) A model reply whose whole text was a number and a full stop (32., 1024.) parsed as a Markdown ordered list with one empty item. renderer.list drew the marker column, found no body, and popped the item as undrawn, taking the marker with it, so the reply rendered to zero rows and the deck skipped it. Draw the marker as literal text when the item body produced nothing, which is the only case a bare . reaches. --- .../1072-bare-number-reply-draws.md | 15 +++++++++++++ internal/tui2/prose/bare_number_test.go | 22 +++++++++++++++++++ internal/tui2/prose/render.go | 10 ++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 docs/changes/unreleased/1072-bare-number-reply-draws.md create mode 100644 internal/tui2/prose/bare_number_test.go diff --git a/docs/changes/unreleased/1072-bare-number-reply-draws.md b/docs/changes/unreleased/1072-bare-number-reply-draws.md new file mode 100644 index 0000000000..f2b3a207df --- /dev/null +++ b/docs/changes/unreleased/1072-bare-number-reply-draws.md @@ -0,0 +1,15 @@ +--- +kind: fixed +title: a reply that is only a number and a full stop draws instead of vanishing +pr: 1072 +surface: [chat] +invalidates: + - "A model reply whose whole text was a number and a full stop (`32.`, `1024.`) parsed as a Markdown ordered list with one empty item, so the list renderer drew the marker column, found no body, and discarded the marker too. The reply rendered to zero rows and the deck skipped it: the fold showed only the thought row though the transcript held the text. An ordered-list item with no body now draws its marker as the literal text the reader sent, so `32.` stands under the chip; `1. one` and multi-item lists still draw as lists." +--- + +Found while running the number-answer journey against `dev`: asking the chat for +"only the number followed by a period" produced `32.`, which the surface dropped. +`renderer.list` in `internal/tui2/prose` pushed the marker, rendered the empty +item, and popped it as undrawn, taking the marker with it. The fix draws the +marker when the item body produced nothing, which is the only case a bare +`.` reaches. diff --git a/internal/tui2/prose/bare_number_test.go b/internal/tui2/prose/bare_number_test.go new file mode 100644 index 0000000000..8c4546a7cb --- /dev/null +++ b/internal/tui2/prose/bare_number_test.go @@ -0,0 +1,22 @@ +package prose + +import ( + "strings" + "testing" +) + +// TestABareNumberedReplyStillDraws guards #1072: a reply that is only a number +// and a full stop (`32.`, `1024.`) is parsed by Markdown as an ordered list +// with one empty item. The list renderer must draw the marker as the literal +// text the reader sent, never discard it as an empty list and render nothing. +func TestABareNumberedReplyStillDraws(t *testing.T) { + for _, in := range []string{"32.", "1024."} { + if got := strings.TrimSpace(strings.Join(Render(in, Options{Width: 80}), "\n")); got == "" { + t.Errorf("%q rendered to nothing", in) + } + } + // A genuine ordered list with a body still draws as a list. + if got := strings.TrimSpace(strings.Join(Render("1. one", Options{Width: 80}), "\n")); got == "" { + t.Errorf("%q rendered to nothing", "1. one") + } +} diff --git a/internal/tui2/prose/render.go b/internal/tui2/prose/render.go index 45f2ab2ccf..be71628773 100644 --- a/internal/tui2/prose/render.go +++ b/internal/tui2/prose/render.go @@ -294,7 +294,15 @@ func (r *renderer) list(n *ast.List) { saved := r.push(first, indent) before := len(r.out) r.container(c, !n.IsTight) - r.pop(saved, len(r.out) > before) + drew := len(r.out) > before + r.pop(saved, drew) + // An item with no body - a line that was only a marker, like the whole + // reply `32.` - would otherwise render to nothing and the deck would + // drop the reply. Draw the marker as the literal text the reader sent so + // the answer still stands. + if !drew { + r.emit(first) + } } } From fddc4d0a47ee6f1da611767166f72199fd6e5e46 Mon Sep 17 00:00:00 2001 From: Vignesh Date: Thu, 8 Oct 2026 19:19:25 +0530 Subject: [PATCH 2/2] address review: PR number in change entry, exact test assertions - Rename change entry to 1746-... and set pr: 1746 (was the issue number). - Assert the exact rendered output ('32.', '1024.', '1. one') instead of just non-empty. --- ...ber-reply-draws.md => 1746-bare-number-reply-draws.md} | 2 +- internal/tui2/prose/bare_number_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename docs/changes/unreleased/{1072-bare-number-reply-draws.md => 1746-bare-number-reply-draws.md} (99%) diff --git a/docs/changes/unreleased/1072-bare-number-reply-draws.md b/docs/changes/unreleased/1746-bare-number-reply-draws.md similarity index 99% rename from docs/changes/unreleased/1072-bare-number-reply-draws.md rename to docs/changes/unreleased/1746-bare-number-reply-draws.md index f2b3a207df..d3d17b6d6e 100644 --- a/docs/changes/unreleased/1072-bare-number-reply-draws.md +++ b/docs/changes/unreleased/1746-bare-number-reply-draws.md @@ -1,7 +1,7 @@ --- kind: fixed title: a reply that is only a number and a full stop draws instead of vanishing -pr: 1072 +pr: 1746 surface: [chat] invalidates: - "A model reply whose whole text was a number and a full stop (`32.`, `1024.`) parsed as a Markdown ordered list with one empty item, so the list renderer drew the marker column, found no body, and discarded the marker too. The reply rendered to zero rows and the deck skipped it: the fold showed only the thought row though the transcript held the text. An ordered-list item with no body now draws its marker as the literal text the reader sent, so `32.` stands under the chip; `1. one` and multi-item lists still draw as lists." diff --git a/internal/tui2/prose/bare_number_test.go b/internal/tui2/prose/bare_number_test.go index 8c4546a7cb..3e079110f6 100644 --- a/internal/tui2/prose/bare_number_test.go +++ b/internal/tui2/prose/bare_number_test.go @@ -11,12 +11,12 @@ import ( // text the reader sent, never discard it as an empty list and render nothing. func TestABareNumberedReplyStillDraws(t *testing.T) { for _, in := range []string{"32.", "1024."} { - if got := strings.TrimSpace(strings.Join(Render(in, Options{Width: 80}), "\n")); got == "" { - t.Errorf("%q rendered to nothing", in) + if got := strings.TrimSpace(strings.Join(Render(in, Options{Width: 80}), "\n")); got != in { + t.Errorf("Render(%q) = %q, want %q", in, got, in) } } // A genuine ordered list with a body still draws as a list. - if got := strings.TrimSpace(strings.Join(Render("1. one", Options{Width: 80}), "\n")); got == "" { - t.Errorf("%q rendered to nothing", "1. one") + if got := strings.TrimSpace(strings.Join(Render("1. one", Options{Width: 80}), "\n")); got != "1. one" { + t.Errorf("Render(%q) = %q, want %q", "1. one", got, "1. one") } }