Bubbletea Code Review
/install bubbletea-code-review
BubbleTea Code Review
Hard gates (sequence)
Advance only when each pass condition is objectively true (reduces false positives on tea.Cmd and unsubstantiated blocking claims):
| Gate | Pass condition |
|---|---|
| G1 — Anti–false-positive | You skimmed NOT Issues below or read references/elm-architecture.md before recording a finding about tea.Cmd returns, value receivers on Update, or nested child Update. |
| G2 — Evidence for blocking / suspicious I/O | Each Critical/Major finding names file path + line (or a short quoted snippet) showing the blocking call, huh.Form.Run in the wrong place, or other asserted anti-pattern—not a hypothetical. |
| G3 — Verification | Before publishing review output, you applied beagle-go:review-verification-protocol to each proposed finding. |
Quick Reference
| Issue Type | Reference |
|---|---|
| Elm architecture, tea.Cmd as data | references/elm-architecture.md |
| Model state, message handling | references/model-update.md |
| View rendering, Lipgloss styling | references/view-styling.md |
| Component composition, Huh forms | references/composition.md |
| Bubbles components (list, table, etc.) | references/bubbles-components.md |
CRITICAL: Avoid False Positives
Read elm-architecture.md first! The most common review mistake is flagging correct patterns as bugs.
NOT Issues (Do NOT Flag These)
| Pattern | Why It's Correct |
|---|---|
return m, m.loadData() |
tea.Cmd is returned immediately; runtime executes async |
Value receiver on Update() |
Standard BubbleTea pattern; model returned by value |
Nested m.child, cmd = m.child.Update(msg) |
Normal component composition |
Helper functions returning tea.Cmd |
Creates command descriptor, no I/O in Update |
tea.Batch(cmd1, cmd2) |
Commands execute concurrently by runtime |
ACTUAL Issues (DO Flag These)
| Pattern | Why It's Wrong |
|---|---|
os.ReadFile() in Update |
Blocks UI thread |
http.Get() in Update |
Network I/O blocks |
time.Sleep() in Update |
Freezes UI |
\x3C-channel in Update (blocking) |
May block indefinitely |
huh.Form.Run() in Update |
Blocking call |
Review Checklist
Architecture
- No blocking I/O in Update() (file, network, sleep)
- Helper functions returning
tea.Cmdare NOT flagged as blocking - Commands used for all async operations
Model & Update
- Model is immutable (Update returns new model, not mutates)
- Init returns proper initial command (or nil)
- Update handles all expected message types
- WindowSizeMsg handled for responsive layout
- tea.Batch used for multiple commands
- tea.Quit used correctly for exit
View & Styling
- View is a pure function (no side effects)
- Lipgloss styles defined once, not in View
- Key bindings use key.Matches with help.KeyMap
Components
- Sub-component updates propagated correctly
- Bubbles components initialized with dimensions
- Huh forms embedded via Update loop (not Run())
Critical Patterns
Model Must Be Immutable
// BAD - mutates model
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.items = append(m.items, newItem) // mutation!
return m, nil
}
// GOOD - returns new model
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
newItems := make([]Item, len(m.items)+1)
copy(newItems, m.items)
newItems[len(m.items)] = newItem
m.items = newItems
return m, nil
}
Commands for Async/IO
// BAD - blocking in Update
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
data, _ := os.ReadFile("config.json") // blocks UI!
m.config = parse(data)
return m, nil
}
// GOOD - use commands
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, loadConfigCmd()
}
func loadConfigCmd() tea.Cmd {
return func() tea.Msg {
data, err := os.ReadFile("config.json")
if err != nil {
return errMsg{err}
}
return configLoadedMsg{parse(data)}
}
}
Styles Defined Once
// BAD - creates new style each render
func (m Model) View() string {
style := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("205"))
return style.Render("Hello")
}
// GOOD - define styles at package level or in model
var titleStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("205"))
func (m Model) View() string {
return titleStyle.Render("Hello")
}
When to Load References
- First time reviewing BubbleTea → elm-architecture.md (prevents false positives)
- Reviewing Update function logic → model-update.md
- Reviewing View function, styling → view-styling.md
- Reviewing component hierarchy → composition.md
- Using Bubbles components → bubbles-components.md
Review Questions
- Is Update() free of blocking I/O? (NOT: "is the cmd helper blocking?")
- Is the model immutable in Update?
- Are Lipgloss styles defined once, not in View?
- Is WindowSizeMsg handled for resizing?
- Are key bindings documented with help.KeyMap?
- Are Bubbles components sized correctly?
- Make sure OpenClaw is installed (local or Docker)
- Run the install command in chat:
/install bubbletea-code-review - After installation, invoke the skill by name or use
/bubbletea-code-review - Provide required inputs per the skill's parameter spec and get structured output
What is Bubbletea Code Review?
Reviews BubbleTea TUI code for proper Elm architecture, model/update/view patterns, and Lipgloss styling. Use when reviewing terminal UI code using charmbrac... It is an AI Agent Skill for Claude Code / OpenClaw, with 164 downloads so far.
How do I install Bubbletea Code Review?
Run "/install bubbletea-code-review" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.
Is Bubbletea Code Review free?
Yes, Bubbletea Code Review is completely free, licensed under MIT-0. You can download, install and use it at no cost.
Which platforms does Bubbletea Code Review support?
Bubbletea Code Review is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).
Who created Bubbletea Code Review?
It is built and maintained by Kevin Anderson (@anderskev); the current version is v2.3.1.