feat: redesign message templates with professional branding#33
feat: redesign message templates with professional branding#33
Conversation
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughThe changes update the triage response formatting to use markdown tables and collapsible blocks for better organization, add comprehensive unit tests for response building logic, modify the undo message styling, and remove initialization of the Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/steps/command_handler.go (1)
43-45:⚠️ Potential issue | 🔴 CriticalMarker mismatch will break self-detection and loop prevention.
The code checks for
"🤖 Simili Triage Report"(with emoji) at lines 43, 100, and 134, butresponse_builder.goline 55 now generates"### Simili Triage Report"(no emoji). This will cause:
- Bot may process its own triage reports (line 43 check fails)
- Loop prevention won't detect previous transfers (lines 100, 134)
🐛 Proposed fix - update markers to match new format
// Self-prevention: Ignore bot's own reports even if using PAT -if strings.Contains(body, "🤖 Simili Triage Report") { +if strings.Contains(body, "### Simili Triage Report") { return pipeline.ErrSkipPipeline }// Check for previous transfers (hot-potato loop prevention) -if strings.Contains(body, "🤖 Simili Triage Report") && strings.Contains(body, "Transferred from") { +if strings.Contains(body, "### Simili Triage Report") && strings.Contains(body, "Transferred from") {-if strings.Contains(body, "🤖 Simili Triage Report") && strings.Contains(body, "Transferred from") { +if strings.Contains(body, "### Simili Triage Report") && strings.Contains(body, "Transferred from") {Alternatively, use a more robust marker like
"Simili Triage Report"without the prefix to handle both old and new formats during transition.Also applies to: 100-100, 134-134
🤖 Fix all issues with AI agents
In `@internal/steps/response_builder.go`:
- Around line 236-241: The initial assignment to status is ineffectual because
you immediately overwrite it; remove "status := similar.State" and instead
initialize status only within the conditional (e.g., declare "var status string"
then set status = "Closed" if similar.State == "closed" else status = "Open"),
or simply assign directly with the conditional so the variable is only set once;
update the code that uses status accordingly (referencing the "status" variable
and "similar.State").
- Around line 144-147: The badge label encoding currently only replaces spaces
and dashes (see encodedLabel handling) but misses escaping underscores and other
reserved URL characters; update the encoding for encodedLabel (used before
appending to badges in the badges construction) to: first escape literal
underscores by replacing "_" with "__" per Shields.io rules, then percent-encode
remaining reserved characters (use net/url.QueryEscape or equivalent) so
characters like %, #, ?, &, /, + are encoded; ensure the final string is what
you insert into fmt.Sprintf("",
encodedLabel, color).
🧹 Nitpick comments (2)
.claude/sessions/2026-02-05-1853.md (1)
5-5: Branch name inconsistency.The session document references branch
feature/hybrid-index-learn, but this PR is forupdate/design-new-chat-message-templates. Consider updating the session document to reflect the correct branch context, or verify this file was intended for this PR.internal/steps/response_builder_test.go (1)
107-108: Error message doesn't match assertion.The error message says
"Expected 'Issue belongs in this repository' message"but the actual check is for"✅ To check". Update the error message for clarity when debugging failures.🔧 Proposed fix
if !strings.Contains(row, "✅ To check **similigh/simili-bot**") { - t.Errorf("Expected 'Issue belongs in this repository' message, got: %s", row) + t.Errorf("Expected '✅ To check **similigh/simili-bot**' message, got: %s", row) }
| // URL encode label | ||
| encodedLabel := strings.ReplaceAll(label, " ", "%20") | ||
| encodedLabel = strings.ReplaceAll(encodedLabel, "-", "--") // Shields.io escaping | ||
| badges = append(badges, fmt.Sprintf("", encodedLabel, color)) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Shields.io badge URL encoding requirements special characters
💡 Result:
For Shields.io badge text (label/message) you generally need normal URL encoding (percent-encoding) for anything that isn’t URL-safe—especially in path-based badges like https://img.shields.io/badge/<label>-<message>-<color>. [1]
1) Use standard URL percent-encoding for reserved/special characters
Encode characters like: space, %, #, ?, &, /, +, etc. (e.g., % → %25, + → %2B). Shields explicitly notes that “URL-Encoding [is] needed for spaces or special characters” in text overrides like ?label=.... [2]
2) Shields “static badge” path segment rules (very important)
Because the dash - is the field separator in /badge/..., Shields defines extra escaping rules for common characters in the badgeContent path: [1]
_or%20in the URL → space in the rendered badge__in the URL → literal underscore_in the rendered badge--in the URL → literal dash-in the rendered badge
So if your label/message must contain a literal -, write it as -- in the /badge/... path. If you need a literal _, write __.
Examples
- Space in message:
.../badge/just%20the%20message-8A2BE2[1] - Literal dash in text: use
--(e.g.,hello--worldrenders ashello-world) [1] - Percent sign in text: encode as
%25(example shown in Shields docs:95%25) [1]
Sources: Shields.io “Static Badge” docs for the path escaping table and examples [1], and Shields.io badge docs noting URL-encoding for spaces/special characters in label overrides [2].
[1] https://shields.io/badges/static-badge
[2] https://shields.io/badges/git-hub-labels
🏁 Script executed:
# First, let's examine the actual code at the specified lines
cat -n internal/steps/response_builder.go | sed -n '140,150p'Repository: similigh/simili-bot
Length of output: 702
Incomplete URL encoding for Shields.io badges—underscores not escaped.
Labels containing underscores will render incorrectly. Per Shields.io escaping rules, literal underscores must be escaped as __ in the badge URL. The current code handles spaces and dashes correctly but misses underscore escaping. Additionally, if labels contain reserved characters (%, #, ?, &, /, +), they should be percent-encoded.
🔧 Proposed fix
// URL encode label
encodedLabel := strings.ReplaceAll(label, " ", "%20")
+ encodedLabel = strings.ReplaceAll(encodedLabel, "_", "__")
encodedLabel = strings.ReplaceAll(encodedLabel, "-", "--") // Shields.io escaping
badges = append(badges, fmt.Sprintf("", encodedLabel, color))For more comprehensive escaping of reserved characters like %, #, ?, consider using net/url.QueryEscape or a similar approach.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // URL encode label | |
| encodedLabel := strings.ReplaceAll(label, " ", "%20") | |
| encodedLabel = strings.ReplaceAll(encodedLabel, "-", "--") // Shields.io escaping | |
| badges = append(badges, fmt.Sprintf("", encodedLabel, color)) | |
| // URL encode label | |
| encodedLabel := strings.ReplaceAll(label, " ", "%20") | |
| encodedLabel = strings.ReplaceAll(encodedLabel, "_", "__") | |
| encodedLabel = strings.ReplaceAll(encodedLabel, "-", "--") // Shields.io escaping | |
| badges = append(badges, fmt.Sprintf("", encodedLabel, color)) |
🤖 Prompt for AI Agents
In `@internal/steps/response_builder.go` around lines 144 - 147, The badge label
encoding currently only replaces spaces and dashes (see encodedLabel handling)
but misses escaping underscores and other reserved URL characters; update the
encoding for encodedLabel (used before appending to badges in the badges
construction) to: first escape literal underscores by replacing "_" with "__"
per Shields.io rules, then percent-encode remaining reserved characters (use
net/url.QueryEscape or equivalent) so characters like %, #, ?, &, /, + are
encoded; ensure the final string is what you insert into
fmt.Sprintf("", encodedLabel, color).
There was a problem hiding this comment.
Pull request overview
This pull request redesigns the bot's message templates to adopt a more professional appearance using GitHub Flavored Markdown features. The changes modernize the triage report output with alerts, badges, and collapsible sections, replacing the previous emoji-heavy format with a cleaner, more structured presentation.
Changes:
- Updated triage report to use GFM alerts (NOTE, WARNING, TIP) instead of text-based indicators
- Converted labels and transfer information from standalone sections to a structured classification table with Shields.io badges
- Made quality improvements and similar issues sections collapsible using HTML
<details>elements - Updated undo command response to use professional alert formatting
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| internal/steps/response_builder.go | Core redesign of all message templates including header change, GFM alerts, classification table with badges, and collapsible sections |
| internal/steps/response_builder_test.go | New unit tests validating the updated template formats and output structure |
| internal/steps/command_handler.go | Updated undo command response to use GFM alert style |
| .claude/sessions/2026-02-05-1853.md | Development session log (unrelated to PR purpose, appears accidentally included) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Development Session - 2026-02-05 18:53 | ||
|
|
||
| ## Session Overview | ||
| - **Start Time:** 2026-02-05 18:53 | ||
| - **Branch:** feature/hybrid-index-learn | ||
| - **Status:** Active | ||
|
|
||
| ## Goals | ||
| _Awaiting user input..._ | ||
|
|
||
| ## Progress | ||
|
|
||
| ### Session Started | ||
| - Initialized new development session | ||
| - Current branch: `feature/hybrid-index-learn` | ||
|
|
||
| --- | ||
| _Session updates will be logged below_ |
There was a problem hiding this comment.
This session file appears to be unrelated to the current PR's purpose. The session references branch "feature/hybrid-index-learn" but this PR is about message template redesign. This file seems to have been accidentally included in this pull request and should be removed or committed separately in the appropriate branch/PR.
| encodedLabel := strings.ReplaceAll(label, " ", "%20") | ||
| encodedLabel = strings.ReplaceAll(encodedLabel, "-", "--") // Shields.io escaping |
There was a problem hiding this comment.
The label encoding logic only handles spaces and hyphens, but labels can contain other special characters that need URL encoding (e.g., slashes, underscores, parentheses). Additionally, the current logic first replaces spaces with %20, then replaces hyphens with --. However, if a label already contains "%20" from a previous step or other URL-encoded characters, the hyphen replacement could affect those. Consider using a more comprehensive URL encoding function like url.QueryEscape() from the net/url package, or apply the Shields.io escaping rules more carefully (hyphens should be escaped to -- before other encoding).
| if len(title) > 50 { | ||
| title = title[:47] + "..." |
There was a problem hiding this comment.
The title truncation logic has a potential issue with multibyte UTF-8 characters. Using title[:47] could slice in the middle of a multibyte character, resulting in invalid UTF-8 and potential display issues. Consider using the utf8 package or []rune conversion to safely truncate at character boundaries: runes := []rune(title); if len(runes) > 50 { title = string(runes[:47]) + "..." }.
| if len(title) > 50 { | |
| title = title[:47] + "..." | |
| runes := []rune(title) | |
| if len(runes) > 50 { | |
| title = string(runes[:47]) + "..." |
| // Heuristic: 'urgent' or 'bug' gets orange, others get black | ||
| color := "000000" // Black | ||
| if strings.Contains(strings.ToLower(label), "urgent") || strings.Contains(strings.ToLower(label), "high") || strings.Contains(strings.ToLower(label), "bug") { | ||
| color = "ff7300" // Orange |
There was a problem hiding this comment.
The color assignment heuristic using string matching (lines 141-142) is fragile and may not work well with all label types. Consider making the color mapping configurable or using label metadata if available. For example, labels might have a color property from GitHub that could be used, or the configuration could include a mapping of label patterns to badge colors. This would make the system more flexible and maintainable.
| value = fmt.Sprintf("🔄 Transferred from **%s** %s", sourceRepo, confBadge) | ||
| } else if targetRepo == currentRepo { | ||
| // Best match is current repo | ||
| value = fmt.Sprintf("✅ To check **%s** %s", targetRepo, confBadge) |
There was a problem hiding this comment.
The message "To check" appears to be grammatically incorrect or unclear. When the issue belongs in the current repository, the message should probably say "Belongs in" or "Correctly placed in" instead of "To check". The phrase "To check" suggests an action that needs to be taken, which doesn't match the semantic meaning of confirming the issue is in the right place.
| value = fmt.Sprintf("✅ To check **%s** %s", targetRepo, confBadge) | |
| value = fmt.Sprintf("✅ Belongs in **%s** %s", targetRepo, confBadge) |
|
|
||
| row := builder.buildTransferRow(ctx) | ||
|
|
||
| if !strings.Contains(row, "✅ To check **similigh/simili-bot**") { |
There was a problem hiding this comment.
The test expectation doesn't match the implementation. The test expects "To check" but this wording is unclear. The test should verify the actual message that will be displayed, which should clearly indicate the issue belongs in the current repository (e.g., "Belongs in" or "Correctly placed in").
| if !strings.Contains(row, "✅ To check **similigh/simili-bot**") { | |
| if !strings.Contains(row, "✅ Belongs in **similigh/simili-bot**") { |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@internal/steps/response_builder.go`:
- Around line 243-250: The similar issue title isn't escaped before embedding in
the Markdown table, so sanitize similar.Title by first replacing
newlines/carriage returns with spaces and then escaping table-sensitive
characters (|, [, ]) using strings.NewReplacer, assign the escaped value to
title (or titleEscaped) and perform truncation on the escaped string (or ensure
truncation preserves escapes), then use that escaped/truncated title in the
fmt.Sprintf that appends to parts (referencing the variables similar.Title,
title, parts and the fmt.Sprintf call).
| // Truncate title if too long | ||
| title := similar.Title | ||
| if len(title) > 50 { | ||
| title = title[:47] + "..." | ||
| } | ||
|
|
||
| parts = append(parts, fmt.Sprintf("| %.0f%% | [#%d %s](%s) | %s |", | ||
| similar.Similarity*100, similar.Number, title, similar.URL, status)) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
head -n 260 internal/steps/response_builder.go | tail -n 30Repository: similigh/simili-bot
Length of output: 965
🏁 Script executed:
cat -n internal/steps/response_builder.go | sed -n '240,255p'Repository: similigh/simili-bot
Length of output: 600
🏁 Script executed:
cat -n internal/steps/response_builder.go | sed -n '200,280p'Repository: similigh/simili-bot
Length of output: 2978
🏁 Script executed:
head -n 30 internal/steps/response_builder.goRepository: similigh/simili-bot
Length of output: 748
🏁 Script executed:
rg -n "append.*parts.*fmt\.Sprintf" internal/Repository: similigh/simili-bot
Length of output: 913
🏁 Script executed:
rg -n "Markdown|markdown|table|Table" internal/steps/response_builder.goRepository: similigh/simili-bot
Length of output: 250
🌐 Web query:
Markdown table pipe character escaping |
💡 Result:
In Markdown tables, | is the column separator. To include a literal pipe inside a cell:
| Method | What to write in the cell | Notes |
|---|---|---|
| Backslash escape | a | b |
Works in CommonMark/GitHub Flavored Markdown because ` |
| HTML entity | a | b or a | b |
Useful if a renderer mishandles | in tables (some do). (designcise.com) |
Example:
| Expr | Meaning |
|---|---|
| a \| b | OR |
| a | b | OR |Citations:
- 1: https://github.github.io/gfm/?utm_source=openai
- 2: https://www.designcise.com/web/tutorial/how-to-escape-the-pipe-character-in-a-markdown-table?utm_source=openai
🏁 Script executed:
# Check if there are other places in the file building tables or formatted Markdown with user data
rg -n "similar\.|Reasoning|Title|State" internal/steps/response_builder.go | head -20Repository: similigh/simili-bot
Length of output: 332
Escape table-sensitive characters in similar issue titles.
The title is embedded in both a Markdown table cell and link text. Raw titles containing |, [, ], or newlines will break table parsing. For example, a title like "foo | bar" renders as | ... | [#123 foo | bar](...) | ..., breaking the column structure.
Sanitize by replacing newlines/carriage returns with spaces, then escaping |, [, and ] with backslashes using strings.NewReplacer().
🔧 Proposed fix
- title := similar.Title
+ title := strings.ReplaceAll(similar.Title, "\n", " ")
+ title = strings.ReplaceAll(title, "\r", " ")
if len(title) > 50 {
title = title[:47] + "..."
}
+ title = strings.NewReplacer("|", "\\|", "[", "\\[", "]", "\\]").Replace(title)
parts = append(parts, fmt.Sprintf("| %.0f%% | [#%d %s](%s) | %s |",
similar.Similarity*100, similar.Number, title, similar.URL, status))🤖 Prompt for AI Agents
In `@internal/steps/response_builder.go` around lines 243 - 250, The similar issue
title isn't escaped before embedding in the Markdown table, so sanitize
similar.Title by first replacing newlines/carriage returns with spaces and then
escaping table-sensitive characters (|, [, ]) using strings.NewReplacer, assign
the escaped value to title (or titleEscaped) and perform truncation on the
escaped string (or ensure truncation preserves escapes), then use that
escaped/truncated title in the fmt.Sprintf that appends to parts (referencing
the variables similar.Title, title, parts and the fmt.Sprintf call).
- Remove accidentally committed .claude session file - Improve label encoding to handle underscores and special chars - Fix UTF-8 safe title truncation for similar issues - Update 'To check' to 'Belongs in' for clarity - Update test expectations to match
Summary
Redesigns the bot's message templates to be more professional, cleaner, and brand-consistent.
Changes
Summary by CodeRabbit
New Features
Tests