From d5330b9f0723296c57c81bc783419c42f7d5879c Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Sat, 21 Feb 2026 01:49:29 +0900 Subject: [PATCH] fix: shrink Telegram table width to 42 and suppress ASCII box-drawing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - markdownTableMaxWidth 90→42 to fit mobile monospace (~40 chars/line) - Add formatting rule to system prompt prohibiting ASCII art diagrams - Add strict table wrapping tests with exact expected output Co-Authored-By: Claude Opus 4.6 --- pkg/agent/context.go | 8 +++- pkg/channels/telegram.go | 2 +- pkg/channels/telegram_test.go | 84 +++++++++++++++++++++++++++++++++-- 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/pkg/agent/context.go b/pkg/agent/context.go index c09757571..e7d2f7170 100644 --- a/pkg/agent/context.go +++ b/pkg/agent/context.go @@ -103,7 +103,13 @@ Your workspace is at: %s ## Context - Keep each phase to 3-5 steps. Do NOT create plans without /plan. - - Always ask about build/test/lint commands during interview.`, + - Always ask about build/test/lint commands during interview. + +4. **Response Formatting** + - NEVER use ASCII box-drawing characters (┌─┐│└─┘╔═╗║╚═╝ etc.) or ASCII art diagrams. + - Use markdown headings, bold, lists, and indentation for structure. + - Keep lines short — most users read on mobile. + - For architecture/flow, use arrow text: CLI → Pipeline → Adapters`, now, runtime, workspacePath, workspacePath, workspacePath, workspacePath, toolsSection) } diff --git a/pkg/channels/telegram.go b/pkg/channels/telegram.go index 8228673c8..e0dfee7ab 100644 --- a/pkg/channels/telegram.go +++ b/pkg/channels/telegram.go @@ -47,7 +47,7 @@ func (c *thinkingCancel) Cancel() { } const telegramMaxMessageChars = 3900 -const markdownTableMaxWidth = 90 +const markdownTableMaxWidth = 42 const markdownTableMinColWidth = 6 var thinkBlockPattern = regexp.MustCompile(`(?is).*?`) diff --git a/pkg/channels/telegram_test.go b/pkg/channels/telegram_test.go index e4291e035..39b59d0e3 100644 --- a/pkg/channels/telegram_test.go +++ b/pkg/channels/telegram_test.go @@ -74,9 +74,87 @@ func TestMarkdownToTelegramHTML_TableWrapsLongCell(t *testing.T) { if !strings.Contains(got, "| short") { t.Fatalf("expected data row with short cell, got: %q", got) } - // Wrapped content should include a prefix from the long sentence. - if !strings.Contains(got, "this is a very") { - t.Fatalf("expected wrapped long cell content, got: %q", got) + // With markdownTableMaxWidth=42 the long cell MUST be wrapped into + // multiple visual lines. Verify the continuation line exists. + if !strings.Contains(got, "long cell that should wrap") { + t.Fatalf("expected wrapped continuation line, got: %q", got) + } + // The continuation row must have an empty first column (padding only). + if !strings.Contains(got, "| | long cell") { + t.Fatalf("expected continuation row with empty first col, got: %q", got) + } +} + +func TestFormatMarkdownTable_Width42(t *testing.T) { + lines := []string{ + "| col1 | col2 |", + "| --- | --- |", + "| short | this is a very very very very long cell that should wrap |", + } + got := formatMarkdownTable(lines) + + // Every line must fit within markdownTableMaxWidth (42). + for i, line := range strings.Split(got, "\n") { + w := displayWidth(line) + if w > markdownTableMaxWidth { + t.Errorf("line %d width %d > %d: %q", i, w, markdownTableMaxWidth, line) + } + } + + // Must produce more lines than a non-wrapped table (header + sep + 1 data = 3). + // With wrapping the data row becomes 2 visual lines → total 4. + lineCount := len(strings.Split(got, "\n")) + if lineCount < 4 { + t.Errorf("expected at least 4 lines (wrap must occur), got %d:\n%s", lineCount, got) + } + + // Verify continuation row has blank first column. + if !strings.Contains(got, "| | long cell that should wrap") { + t.Errorf("expected continuation row, got:\n%s", got) + } +} + +// TestFormatMarkdownTable_MultiColWrap verifies that a multi-column, +// multi-row table correctly wraps one long cell while leaving other +// rows and columns unaffected. +// With markdownTableMaxWidth=42, col widths shrink to [7, 5, 20]. +// Bob's "Needs more practice in writing" (30 chars) wraps at width 20. +func TestFormatMarkdownTable_MultiColWrap(t *testing.T) { + lines := []string{ + "| Name | Score | Comment |", + "| --- | --- | --- |", + "| Alice | 95 | Great job |", + "| Bob | 72 | Needs more practice in writing |", + "| Charlie | 88 | Good |", + } + got := formatMarkdownTable(lines) + rows := strings.Split(got, "\n") + + wantLines := []string{ + "| Name | Score | Comment |", + "| ------- | ----- | -------------------- |", + "| Alice | 95 | Great job |", + "| Bob | 72 | Needs more practice |", + "| | | in writing |", + "| Charlie | 88 | Good |", + } + + if len(rows) != len(wantLines) { + t.Fatalf("line count: got %d, want %d\nactual:\n%s", len(rows), len(wantLines), got) + } + + for i, want := range wantLines { + if rows[i] != want { + t.Errorf("line %d:\n got: %q\n want: %q", i, rows[i], want) + } + } + + // Every line must be exactly markdownTableMaxWidth. + for i, line := range rows { + w := displayWidth(line) + if w != markdownTableMaxWidth { + t.Errorf("line %d: displayWidth=%d, want %d: %q", i, w, markdownTableMaxWidth, line) + } } }