Skip to the content.

ADR 0010: Use Charm-Ruby (bubbletea + lipgloss) for the TUI layer

Status: Superseded by ADR-0013 (2026-05-03). Bubbletea-ruby was a workshop-driven choice; once the workshop concluded, ADR-0013 restored Rooibos as the TUI framework. Date: 2026-05-02 Supersedes: ADR-0008 — but see ADR-0013, which reverses this supersession.

Context

ADR-0008 chose Rooibos (on ratatui_ruby) as the TUI framework. That decision was made on 2026-04-27, before bubbletea-ruby (Marco Roth’s Ruby bindings to Charm’s Bubble Tea) existed. Bubbletea-ruby was first published on 2025-12-26 and is now at 0.1.4 (March 2026); the companion styling library, lipgloss-ruby, is at 0.2.2.

Stage 3 (the TUI work, issue #2) has not started, so the cost of changing this decision is at its lifetime minimum.

Decision

Use bubbletea-ruby as the TUI framework, with lipgloss-ruby for styling. This replaces Rooibos and removes ratatui_ruby from the dependency tree.

Reasoning

Idiomatic Ruby surface. Bubbletea-ruby’s API is plain Ruby — class Foo; include Bubbletea::Model; def init; def update(msg); def view; end. Rooibos uses lambda-as-constants (Init = ->, View = ->), which ADR-0008 itself called out as a real onboarding cost for Rails developers. For Blue Ridge Ruby 2026, lower onboarding cost matters.

Battle-tested core. The Go bubbletea library is the dominant TUI framework in the Go ecosystem — used by gh, glow, the Charm CLI suite, and dozens of other production tools. Bubbletea-ruby is a young binding (0.1.x), but the rendering and event-loop semantics it wraps are mature in a way that no pure-Ruby alternative is.

Workshop transferability. “You’re learning the Ruby flavor of Bubble Tea” is a stronger pitch than “you’re learning Rooibos.” MVU is the transferable mental model; Bubble Tea is the largest MVU-TUI ecosystem to walk into afterward.

Install friction is acceptable on workshop hardware. Both frameworks ship precompiled binaries via the standard Ruby gem-platform mechanism. On a typical Mac/Linux workshop laptop, gem install pulls a binary; no Rust or Go toolchain on the user’s machine. Verified on rubygems.org:

Platform ratatui_ruby (1.5.0) bubbletea (0.1.4) / lipgloss (0.2.2)
macOS arm64 precompiled precompiled
macOS x86_64 source build precompiled
Linux x86_64 gnu precompiled precompiled
Linux x86_64 musl source build precompiled
Linux arm64 (gnu/musl) source build precompiled
Windows precompiled source build

Bubbletea wins broadly on Mac+Linux; ratatui_ruby wins on Windows. For a regional Ruby conference, that asymmetry favors bubbletea.

Companion ecosystem. Lipgloss-ruby provides idiomatic styling; the Charm-Ruby umbrella (charm-ruby.dev) signals an ongoing port effort, not a one-off binding.

Tradeoffs accepted

Project-shaped Command primitives are gone. Rooibos provides Command.system, Command.http, Command.wait, Command.cancel as first-class primitives matching this project’s exact verbs. Bubbletea-ruby follows the Go idiom: a Command is a closure that returns a message. We write thin helpers — http_command(url, envelope:), system_command(argv, envelope:), wait_command(seconds, envelope:) — once, and use them throughout. This is the only meaningful piece of Rooibos value we reproduce ourselves.

Test helpers unverified. Rooibos shipped pure-function-Update testing + snapshot helpers + headless terminal style assertions. Bubbletea-ruby’s testing story is not yet documented in its README. Pre-merge of Stage 3, verify what bubbletea-ruby ships and either use it, port teatest patterns from Go’s bubbletea, or build a minimal snapshot harness. Acceptable risk because pure-function update is testable on its own.

Younger Ruby surface. Bubbletea-ruby is at 0.1.4; Rooibos was at 0.7/0.8. Both are pre-1.0 with API-change risk; bubbletea-ruby has had less time to settle. Mitigation: pin bubbletea-ruby and lipgloss to known-good versions; bump deliberately with verification.

Two native gems vs one. Bubbletea-ruby and lipgloss-ruby are both Go-built native gems; Rooibos was pure Ruby on top of one native gem (ratatui_ruby). The user-visible difference is marginal — both bundle install to a precompiled binary on common platforms.

Maintainer alignment. Rooibos and ratatui_ruby were both Kerrick Long: one mind, one ecosystem. Charm-Ruby splits maintainership across charmbracelet (Go upstream) and Marco Roth (Ruby bindings, plus many other projects). When Go upstream bumps an API, Marco’s lag determines our exposure. Mitigation: pin to a known-good version; bump deliberately.

Windows attendees compile. No precompiled bubbletea binary for Windows. Document the source-build path in docs/archive/workshop.md for any Windows attendee.

Alternatives considered

Consequences

On dependencies: remove rooibos from the gemspec. Add bubbletea (~> 0.1.4) and lipgloss (~> 0.2.2). Drop the ratatui_ruby Rust-toolchain warning from docs/archive/workshop.md.

On docs/design.md: the TUI-layer section needs reworking — fragments become bubbletea models composed by routing, lambda-as-constant examples are replaced with idiomatic class-with-mixin examples, the Command list (Command.http, etc.) is replaced with the wrapper helpers defined above.

On CLAUDE.md: the working-agreement bullet “Async work is always a Rooibos Command” becomes “Async work is always a bubbletea Command.” The package-pinning note about Rooibos is replaced with bubbletea/lipgloss pins.

On ADR-0008: marked Superseded by ADR-0010.

On issue #2: rewritten to point at bubbletea-ruby + lipgloss-ruby instead of Rooibos.

On the workshop: attendees still learn MVU, but the framing is “Charm-style TUI in Ruby” — the same pattern as gh, glow, and the Charm CLIs they may already know.

On testing: before Stage 3 lands, verify bubbletea-ruby’s test helpers. If absent, port teatest patterns or build a minimal snapshot harness. Pure-function update testability is independent of the framework choice.

On the maintainer relationship: reach out to Marco Roth before the workshop to mention “we’re building a workshop project on bubbletea-ruby for Blue Ridge Ruby 2026” — same etiquette ADR-0008 prescribed for Kerrick Long.

What this doesn’t change