charm-tui from dawn
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162# charm-tui
A portable agent skill for building and improving Go terminal interfaces using the Charm ecosystem. The entry point is [SKILL.md](SKILL.md); it contains activation metadata, a working procedure, decision rules, and links to deeper material.
Research snapshot: **23 September 2026**. This is an independent synthesis of official library documentation and selected source from Glow, Gum, Crush, and Soft Serve, plus Huh's documented form/accessibility design. It is not an official Charm skill or a claim that those applications were fully audited.
## Added: do-by-example teaching
The skill now includes [eight worked tasks](examples/WORKED-EXAMPLES.md) and [three complete Go example programs](examples/worked/README.md): a compact picker, debounced search, and a follow-tail event monitor. Each task shows the implementation, concrete interaction sequence, regression checks, and limits—not just a description of the desired property. The main skill also embeds three short worked fixes so examples are part of the default instructions.
The actual demo core source files have 26 executed tests, including cancellation and channel behavior. Charm integration and rendering tests are provided separately and remain unexecuted because toolchain/dependency downloads are unavailable here. See [verification](VERIFICATION.md).
## Use
Keep the entire `charm-tui` directory together and place it in the skills directory configured by your agent. The folder is provider-neutral; no network connector, model API, runtime plugin, or global configuration is required. An agent without a skill loader can read `SKILL.md` directly and open its references as needed.
Example requests:
> Review this Go picker for Charm-style interaction quality, focus correctness, and resize behavior. Preserve its existing appearance and keys.
> Build a small inline server selector. Use the charm-tui skill, keep stdout machine-readable, and avoid adding a full-screen application shell.
> Improve this Bubble Tea streaming view. Fix stale updates and scroll behavior before changing its theme.
Do not read every reference into context automatically. Start with the skill, then load the topic relevant to the work.
## Contents
| Path | Purpose || --- | --- || [SKILL.md](SKILL.md) | Main instructions, worked fixes, activation description || [Do by example](examples/WORKED-EXAMPLES.md) | Eight task-to-code walkthroughs with expected behavior and regression checks || [Complete Go demos](examples/worked/README.md) | Picker, cancellable search, follow-tail monitor, and their tests || [Design and interaction](references/design-and-interaction.md) | Screen hierarchy, feedback, focus, help, visual restraint, accessibility || [Architecture and effects](references/architecture-and-effects.md) | Ownership, composition, commands, generation guards, streams || [Layout and rendering](references/layout-and-rendering.md) | v2 box arithmetic, cells, resizing, style and cache correctness || [Versions and components](references/versions-and-components.md) | API-version traps and choosing the smallest useful component || [Testing and release](references/testing-and-release.md) | State, snapshot, integration, terminal, and performance checks || [Case studies](references/case-studies.md) | Observations, transferable lessons, and limits || [Source ledger](references/sources.md) | Primary references and exact inspection scope || [Evaluation scenarios](evals/scenarios.json) | Adversarial prompts and explicit pass/fail expectations || [Contract exercises](examples/contracts/README.md) | Small runnable Go tests for selected invariants || [Verification record](VERIFICATION.md) | What was and was not executed when packaging |
## Validate locally
From the skill directory:
```shpython3 scripts/validate.pypython3 scripts/test_offline_examples.pycd examples/contractsgo test -v ./...```
The validator checks package structure, entry-point metadata, relative file/anchor links, fenced-code balance, and evaluation schema. It does not browse remote links, compile the Markdown integration sketches, evaluate an agent, or test terminal rendering.
## Evaluate an agent using the skill
The JSON scenarios are portable specifications, not tied to a particular eval service. Give the agent the scenario's setup and prompt with a suitable fixture repository. Run its proposed tests and inspect the resulting diff and interaction. Score each `must_pass` item and reject a result that exhibits a `must_not` failure. Do not award a behavioral pass merely because the agent mentions the right concept.
For a comparison, run the same fixture and task with and without the skill, then review blind for preserved behavior, correct implementation, useful tests, dependency restraint, and truthful verification. Record environment and failures. The supplied scenarios were authored and structurally checked, not executed as model evaluations during packaging.1234567891011121314151617181920212223242526272829303132333435363738394041424344def403f922809875f9f195961b760899e41e9891b1900f0eb762ac8f59d3ad84 README.mddaef5f8a470d8f75c1ec3a22c17474d7fd06cd9dd46d08098be1f637bdee1dea SKILL.md7881e27adb237023d7ee279d09167e40a50f302339e60b20947553d8defa0053 VERIFICATION.mda43dae5d2d6af327a30795be0e3fbdb9cbed9f5519fe62cc1fb640d1c4cf7740 evals/scenarios.json6c9ae365e4b4093bf8121aad283a3fc3eeb96619edde6b3132eba8a25a55b8eb examples/WORKED-EXAMPLES.md8992a4baf116f3478b8eca22c96e93d1dda0e55312dd7c9463df8d40ead9723a examples/contracts/README.mdc5994ff8803c5e37d7a0be692cbe558d3fe400663c614afdfa4dce89c17dbf24 examples/contracts/go.mod90fef9d1f0085215b0778bb4e15b5b8967e22950d0a56cef147935ecf309cdd0 examples/contracts/patterns.go9de5d8dcffd06603a3653d75b236fdd7145df9b886eb52a28624fe10d55319e9 examples/contracts/patterns_test.go74d357f9271b7d89aac1f31ffc7f70711ff3db3e69f258ceaf659bfbe4751b5c examples/worked/README.md9d03a5bac0b2ec8e61386647c25ec8a017bed3ec3a9d60ee5d83b8df4b70e676 examples/worked/cmd/picker/main.go4ebeadb2b8650d7bada0283a929150c643ca334d73b12c847b9f8ebc84bf2947 examples/worked/cmd/picker/model.goc5ef1a1badce2ff9495e3006f48c900ffd49d13bf0e3cb60473b115b985b10a4 examples/worked/cmd/picker/model_test.goda7d6e5ba00428bc8846503bfbf623c7f060a46d265f8fdd1d963f628220858a examples/worked/cmd/picker/selection.goce47c5ca852a72a426a677ed47307693d1e6b6cbb07701e8c3afa20d5108cedb examples/worked/cmd/picker/selection_test.goa6528a4f985c2bc5e2db9745c940c737ebcfa0f60ea426206ac02966279ba92a examples/worked/cmd/search/main.go58955dc4dd7d803e6109a6ee943e36fc76291a72d166e1668c371e1564b0030a examples/worked/cmd/search/model.go45dba143fabb154bd67079915d42579e15ce72700fcd19f019a12365b9982016 examples/worked/cmd/search/model_test.goa2b157589d57e8f19d0024e034d88dfa2e140e6921a7d94f0651ab8ad4b09b45 examples/worked/cmd/search/request.go208615c57f66c3d324ba57371f7ca99467065633316d8591338a283e3f5105ba examples/worked/cmd/search/request_test.go37c5791478a72c03186dcd179214ad8d6f98a1b2173ae634b3f7ff504ed65987 examples/worked/cmd/tail/history.go689b9ff86d365d8ecbae4aff2322db58b4d819133c3349a9262d635e2ff5486e examples/worked/cmd/tail/history_test.gof85d9fd831de902f489052b75f3011dacf09cc61f1262fcdd36351045084156c examples/worked/cmd/tail/main.gocb49abb2c18f519fa84259fbf884306fc059941cec580776c554c082c23f3d1d examples/worked/cmd/tail/model.god8846d68603c81c164cf8aab5c8fecc7219bf43b8eea2b6e0036c131ed56bbad examples/worked/cmd/tail/model_test.go7cfe6b8b09ad8b344fe98a54dd6d57bdb6f669d07109a42eb15df0b02e2745c6 examples/worked/cmd/tail/stream.go37b8290883d206391a1d64f525d98dfa551bd6b34029cc72b909a2d34f705d09 examples/worked/cmd/tail/stream_test.goa473884ce469e5a4bd86cd957fc5aed13629fa4647de88855d07ecf43129a78b examples/worked/go.mod472d7bad557b711d1d2be4ade5b393d4b66e88f038ed699a01a72f8d4e7f4934 examples/worked/internal/display/bounds.goad6959b333ec09e99c7fc92336d643e2d9bd7261936281c9912853125e11d600 examples/worked/internal/display/bounds_test.gofe6132911dd6b2dae17cd38f510047eefa2d16f58616675f1af453e73ce0517a examples/worked/internal/display/render.go3cebeae1110c2fd1afd5db3d630a3a9b97e8b286c8490fb8a58a69b1fcf9cbfe examples/worked/internal/display/render_test.gocfca858eb6751a1d8ee03f36d1781549feb0488c7623bf535c3b9a507bcb8976 references/architecture-and-effects.md5d75d014d4dec4afe882c101b179728ca29c08a527e0232a4ead217d82d81d47 references/case-studies.md0b20e4ac28936f321499e336411f9cfc2ce179d65bb7e30e52d7a63f16409c5e references/design-and-interaction.md6c138e76a8f8a4677f758deda0fcf1dcb212246efa19d5fce7e4ae1f20589f1c references/layout-and-rendering.mddb7a2d52479922131f1239ef924eae255966834eba05c7ea61261ac99e148e9f references/sources.mdd8718cdd1c900a0b0231fb4fb4b149f496a40819c798de6c62345b8d9fb45f1e references/testing-and-release.mdc90db0754c646c098df8a4f53b60a39d1506bd7ea112d8d02601e03d97ebeb0c references/versions-and-components.mdde6ba3aaecd3d7c0e853867395b69e2b2500832342107869e5017dd02a8a234b scripts/test_offline_examples.pyed683e4b09ac81760487f1020ae7f7486913e924c4b6b1d68246e684f946d81c scripts/validate.py6c60c8071bf3057730d72a1fa91ede1f25512ffd0b498b4156eb1ace65d64eff verification/charm-build-attempt.txt506aba630cab4a53ea698ee910e5ffb848fc6211960aa7b3b0b048bd381f8346 verification/original-contract-tests.txt9175cafb944e43abfa383fbe39acb23660f8258ea74edfa33983e59442b6f1a6 verification/worked-offline-tests.txt123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164---name: charm-tuidescription: Design, implement, review, and refine Go terminal interfaces using Charm's Bubble Tea, Bubbles, Lip Gloss, and Huh. Use for interactive CLIs, pickers, forms, dashboards, streaming views, keyboard/focus bugs, terminal layout, or Charm API migrations. Emphasizes behavior-preserving changes, terminal-native interaction, correct asynchronous state, and restrained visual design. Also use to decide whether a full-screen TUI is appropriate; do not add Charm to ordinary noninteractive Go commands without a need.---
# Charm TUI craft
Build a terminal tool that is easy to understand, quick to operate, and reliable under real input, data, and terminal conditions. Learn Charm's interaction and engineering practices, not just its palette.
## Work from an example
Open [Do by example](examples/WORKED-EXAMPLES.md) and choose the closest task before inventing a new pattern. Each task pairs a concrete user request with before/after code, the full implementation, a sequence to try, and a regression test. Use the example's mechanism; preserve the target application's contract.
For a new picker, begin with [task 1](examples/WORKED-EXAMPLES.md#1-build-a-compact-picker). For an existing application, jump directly to [focus and modals](examples/WORKED-EXAMPLES.md#2-fix-search-keys-and-modal-fall-through), [cell-aware layout](examples/WORKED-EXAMPLES.md#3-fit-a-narrow-terminal-without-losing-identity), [async search](examples/WORKED-EXAMPLES.md#4-implement-latest-query-wins-search), [pipeline output](examples/WORKED-EXAMPLES.md#5-return-a-result-without-polluting-stdout), [stream anchors](examples/WORKED-EXAMPLES.md#6-keep-a-streaming-view-from-jumping), [subscription closure](examples/WORKED-EXAMPLES.md#7-stop-subscriptions-without-leaks-or-busy-loops), or [behavior-preserving reduction](examples/WORKED-EXAMPLES.md#8-reduce-code-without-redesigning-the-interface).
### Example: fix the owner of a key, not the key
Given “typing q in search quits,” do not remove the browse-mode q shortcut. In the key handler, route to the focused input before considering browse bindings. This is an excerpt from the [complete picker](examples/worked/cmd/picker/model.go); `k` is its mode-specific keymap:
```goif m.input.Focused() { if key.Matches(msg, k.back, k.accept) { m.input.Blur() m.clampWindow() return m, nil } return m, m.updateInput(msg)}```
The modal branch precedes this and consumes its key even when closing. The browse branch follows it. Keep `TestPrintableKeysBelongToSearch` and `TestModalEscapeDoesNotQuitOrReopen`, adapting their setup rather than merely repeating the rule.
### Example: invalidate before waiting
Given “old results flash during debounce,” change the generation when the query changes. Then test the precise race, using the state implementation from [the search program](examples/worked/cmd/search/request.go):
```goa := s.change("a")s.begin(a.generation)b := s.change("b") // B has not started yet.if s.complete(a.generation, []string{"old"}, nil) { t.Fatal("accepted an old result during the new debounce")}s.begin(b.generation)```
The full example also cancels A's context, tags errors, rejects duplicate starts, and retains child input commands. The regression is in [request_test.go](examples/worked/cmd/search/request_test.go).
### Example: preserve the allocated box width
Given a 40-column dialog using Lip Gloss v2, with zero margins:
```gobox := lipgloss.NewStyle().Border(lipgloss.NormalBorder()).Padding(0, 1)outerWidth := 40innerWidth := outerWidth - box.GetHorizontalFrameSize()content := ansi.Truncate(label, innerWidth, "…")rendered := box.Width(outerWidth).Render(content)```
Do not feed `innerWidth` back into `box.Width`: it already includes the internal frame. The [complete helper](examples/worked/internal/display/render.go) handles tiny widths by removing the frame, and the supplied rendering test measures actual output. Use cell-width assertions, not byte-length assertions.
These example fragments are version-specific excerpts, not independent complete programs. The [three complete programs](examples/worked/README.md) supply imports, models, entry points, and tests. Read [verification](VERIFICATION.md) before repeating any claim that they were compiled or terminal-tested.
## Start with the existing contract
Before editing, inspect `go.mod`, the entry point, the root model, keybindings, styles, output handling, and relevant tests. Run the existing UI when possible. Record its main task, normal flow, cancellation behavior, output format, and supported environments. For a new tool, derive these from the user's request.
Treat an existing interface as a contract. Preserve its keys, focus behavior, inline/full-screen mode, sizing, counters, custom visuals, and result semantics unless the requested change requires otherwise. Replacing a bespoke picker with a stock `bubbles/list` is not a behavior-preserving cleanup. Reuse component mechanics beneath the interface when useful.
Identify the repository's actual Charm major versions. Do not paste v1 examples into v2 code, upgrade dependencies as incidental cleanup, or assume every Charm package uses the same module path. Read [versions and component selection](references/versions-and-components.md) when selecting libraries, writing version-sensitive code, or migrating.
## Choose the smallest suitable surface
| User's task | Starting point || --- | --- || Run once, return a value, integrate into scripts | Flags plus ordinary output; styling is optional || Pick, confirm, or enter a few values | Inline interaction; Huh for forms, Gum for shell composition || Browse, inspect, filter, or monitor repeatedly | Bubble Tea application with appropriate Bubbles || Read formatted prose | Glamour plus a viewport when scrolling is needed || Operate through SSH | A session-scoped TUI; investigate Wish only when remote interaction is actually required |
These are choices, not mandatory layers. A command need not gain a configuration framework, router, event bus, or full-screen shell to become pleasant. Preserve the project's CLI conventions; use ordinary Go functions and structs before adding infrastructure.
## Design the interaction before decorating it
Write the primary flow in one sentence: “Filter candidates, inspect the current item, then accept or cancel.” Identify the first useful action and make it visible without opening help.
Distinguish **focus** (which control receives input), **cursor** (which item is highlighted), **selection** (which IDs have been chosen), and **operation state** (what work is happening). Do not overload one field or one color with these meanings.
Define loading, ready, empty, no-match, recoverable-error, and terminal-error states where applicable. Give each a truthful message and a useful next action. Preserve valid data during refresh when it remains useful; label it as refreshing or stale. Do not turn every transient failure into an empty screen.
Use a compact hierarchy: task/context, primary content, local status, contextual controls. Start with the terminal's background, readable text, one accent role, a clear current-row marker, and quiet metadata. Add borders only where they clarify grouping. Personality can live in a title, status wording, or small animation; it must not displace content or obscure state.
Do not impose a pink palette, rounded boxes, gradients, a large logo, or dashboard cards merely because the task mentions Charm. Read [design and interaction](references/design-and-interaction.md) for concrete patterns and tradeoffs.
## Keep state ownership obvious
The model's update path owns mutable UI state. Commands perform I/O or expensive work and return typed results. Rendering describes the current state; it does not initiate network requests, start timers, or secretly change the user's selection.
Use either a small tree of independently useful components or one root model with simple imperative helpers. Both are legitimate. Extract a component when it owns a coherent interaction or lifecycle, not for every rectangle. A local state change does not need a command that sends a message back to the same model.
When forwarding an event to a Bubbles component, retain both its returned model and command. Do not broadcast keystrokes to every child. Route non-input results to their owner even when that owner is not visible, when the lifecycle requires it.
For asynchronous work:
- Capture inputs before returning a command. Do not let the command read or mutate a live model, slice, or map concurrently.- Cancel superseded work and tag results with a generation or operation ID. Invalidate the old generation **when intent changes**, not only when the next request starts. Ignore obsolete successes, failures, and completion flags.- Use `tea.Batch` for independent work. Start result-dependent work from the matching result handler; command ordering is not a data dependency.- Give timers, subscriptions, and workers an owner and a stop condition. Re-arm a one-shot subscription only once. Stop on channel closure. Bound backlog and work.
Read [architecture and effects](references/architecture-and-effects.md) for the precise request lifecycle and streaming policy. The [search walkthrough](examples/WORKED-EXAMPLES.md#4-implement-latest-query-wins-search) wires this into an actual input model; the smaller [contract examples](examples/contracts/README.md) isolate the arithmetic/state rules.
## Route input deliberately
Set precedence explicitly: emergency/application-level controls, then the active modal or popup, then the focused control, then the current screen's unconsumed navigation keys. The exact interrupt policy is application-specific; document whether it cancels work, backs out, or exits.
Printable characters belong to an active editor. Typing `q`, `j`, `k`, `/`, `?`, or space must not trigger unrelated navigation or quit. Preserve component editing, paste, cursor, and focus commands. Handle a modal's consumed key exactly once; closing it must not activate the item underneath.
Use `key.Binding` and `help.Model` when appropriate so active controls and displayed help share definitions. Disable unavailable actions in both routing and help. Escape should unwind a visible interaction predictably; do not unexpectedly discard a draft. Restore focus after closing overlays.
Keyboard operation must remain complete without a mouse. Mouse hit testing must use the same geometry as rendering. Keep ordinary terminal selection/copy practical; do not enable aggressive mouse capture without a use case. Do not silently take over the clipboard.
## Treat layout as cell arithmetic
Let the parent allocate bounds. Measure rendered headers, prompts, help, and status at the current width; subtract them from available space. Clamp derived dimensions. Keep margins, borders, padding, and child content separate. Never depend on a fixed “subtract 4” unless those four rows are a tested invariant.
In **Lip Gloss v2**, `Width(n)` describes the block including borders and padding, before margins. Do not subtract a frame twice. Details and a worked example are in [layout and rendering](references/layout-and-rendering.md).
Measure display cells with the relevant Lip Gloss/ANSI helpers, not `len`, byte slicing, or rune counts. Account for styled content, CJK, combining marks, emoji, tabs, prompts, and selection prefixes. A helper that preserves ANSI is not a security sanitizer.
On narrow terminals, drop optional columns or switch composition before making the primary task unusable. Keep a compact safe view for extremely small dimensions and handle initial zero-size events. Reflow on width changes; clamp or preserve the scroll anchor intentionally. At ordinary sizes, controls should not jump as counts or status strings change.
## Make output and lifecycle part of the UI
For a pipeline-oriented command, reserve stdout for its machine-consumable result. Use stderr or an explicitly chosen terminal for interaction, and print the final result only after the UI has finished. Do not mix logging with a live renderer, even on stderr if that is the renderer's output.
Define noninteractive behavior before opening a terminal. Do not hang CI waiting for a key. Distinguish accepted, cancelled, failed, and interrupted outcomes; cancellation must not accidentally emit a valid-looking selection. Respect existing exit-code conventions.
Let the runtime restore terminal modes. Return through cleanup paths; avoid `os.Exit` before deferred cleanup. Use the runtime's supported handoff for an interactive child process instead of letting two programs read the same terminal. For SSH, keep model, theme/capability state, cancellation, and authentication boundaries session-scoped.
Offer a plain or accessible path when relevant. No-color mode is not equivalent to screen-reader accessibility. Avoid reliance on a special font, animation, mouse, terminal graphics, or color alone.
## Optimize the work, not the appearance of work
First measure startup, keystroke-to-update responsiveness, rendering cost, allocations, and idle CPU for representative data. Keep inexpensive local filtering simple. Move expensive search/render work out of the update/render path when measurements justify it.
For large lists, avoid formatting all rows just to display one viewport. Cache costly Markdown or highlighting with explicit data, width, and theme invalidation. Do not compute full-history height every frame merely to decide whether content overflows. A cache has a correctness contract and a memory cost; do not add one reflexively.
For streams, coalesce redraws without silently dropping durable data. Follow new output only while the user is following the tail; reading older content must not be interrupted by incoming events. Bound history or make its retention policy explicit.
## Verify the contract, then finish
Read [testing and release](references/testing-and-release.md). At minimum test state transitions and command results, focus and modal routing, zero/one/many/no-match data, stale completion, resize, Unicode, and stdout separation. Include direct model tests; use the **matching-major** teatest package for integration where suitable.
Inspect actual terminal output at a normal size and a small size. For existing UIs, compare before and after on the same input. Exercise paste, cancel, error recovery, repeated refresh, light/dark or explicit-theme behavior, and terminal restoration. Use deterministic snapshots or VHS recordings for appearance; they complement behavioral assertions, not replace them.
Use [evaluation scenarios](evals/scenarios.json) as adversarial review prompts, selecting relevant cases. These are evaluation specifications, not a claim that the target application has passed.
When delivering work, state the interaction preserved or changed, the implementation choices that mattered, the checks actually run, and any unverified terminal behavior. Distinguish source-reviewed, compiled, unit-tested, and terminal-tested. Do not claim polish from compilation alone.
## Evidence and further reading
The [case studies](references/case-studies.md) separate observed application behavior from recommendations, including a concrete example where implementation and maintainer notes disagree. The [source ledger](references/sources.md) records primary sources, inspected revisions, and scope. This skill is an independent synthesis, not an official Charm standard.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657# Verification record — worked-example revision
Updated **2026-09-23**. This revision keeps the original research and adds eight task-to-code walkthroughs, three complete Go example programs, and regression tests. The code examples are original teaching implementations, not vendored Charm application code.
## What ran
The environment supplies **Go 1.23.2, linux/amd64**. No Charm API substitutes or mocks were used to make the example programs appear to compile.
| Check | Result and exact scope || --- | --- || `python3 scripts/validate.py` | Passed. Required files, frontmatter, relative links/anchors, fenced-code balance, and the 14 evaluation specifications. || `gofmt` on every Go file | Passed parsing/formatting. This is not type checking or linking. || `python3 scripts/test_offline_examples.py` | Passed 26 new top-level tests on the actual dependency-free source files used by the demos. All four groups ran with `-race -cover` and `go vet`. || `go test -race -cover -v ./...` in `examples/contracts` | Passed the 11 original top-level tests, including eight table-driven body-sizing subtests. || `go vet ./...` in `examples/contracts` | Passed, no diagnostics. || `go test ./...` in `examples/worked` | Attempted; blocked before compilation while downloading the required Go toolchain. |
That is **37 executed top-level tests**, not 37 terminal integration tests. The actual new core files tested are:
- `internal/display/bounds.go`: row allocation, exhaustive visible-range invariants, bounded visible range, control-character policy. **100.0% statement coverage.** This does not test real display-cell width.- `cmd/picker/selection.go`: stable identity, empty matches, duplicate labels, ownership, accepted/cancelled/interrupted output and write errors. **100.0% statement coverage.** This does not test terminal descriptors or input routing.- `cmd/search/request.go`: generation gating, real cancellation contexts/timers, stale successes and failures, duplicate admission, same-query retries, result ownership, and the simulated backend. **100.0% statement coverage.** This does not test Bubble Tea scheduling.- `cmd/tail/history.go` and `stream.go`: logical anchors, bounded ring retention, eviction disclosure, following, resizing, closed channels, and actual producer/receiver cancellation. **93.5% combined statement coverage.** This does not test the terminal renderer or the Tea adapter's command scheduling.
The offline runner uses named source files with module mode disabled, not a second implementation of those functions. It intentionally omits files importing Charm. Coverage measures only the listed source groups and is not a coverage statement about the applications as a whole.
Raw evidence: [new core tests](verification/worked-offline-tests.txt), [original contract tests](verification/original-contract-tests.txt), and [full build attempt](verification/charm-build-attempt.txt).
## Full-build limitation
The example module requests Go 1.24.2 and explicitly names the direct library versions reviewed in [the source ledger](references/sources.md#s17). The local `go test ./...` attempt tried to download `go1.24.2`; it failed because DNS/network access to the Go module proxy was unavailable. The local Go 1.23.2 toolchain cannot compile that module as declared.
Therefore the three full programs are **source-complete but not compiled here**. The supplied Charm-dependent model/render tests are also **unexecuted**. `go.sum` was not fabricated. Resolve dependencies, run the full suite, and commit the resulting checksum file before relying on an adapted program.
## What did not run
No upstream Charm application or new demo was executed in a terminal. No PTY session, terminal-restoration check, interactive stdout/stderr test, clipboard/paste test, cross-platform build, actual Unicode display inspection, or before/after screenshot comparison was performed. Expected screen compositions in the walkthroughs are illustrations in text, not captured runtime output.
The 14 agent evaluation scenarios were structurally checked, not executed against an agent. Remote URLs were not exhaustively checked. API documentation was consulted, but it cannot establish type correctness or renderer behavior of the complete module.
## Reproduce and finish verification
From the skill root:
```shpython3 scripts/validate.pypython3 scripts/test_offline_examples.pycd examples/workedgo mod tidygo test -race ./...go vet ./...go run ./cmd/picker```
Run the search and tail demos separately, then follow the interaction sequences in [Do by example](examples/WORKED-EXAMPLES.md). Test normal and narrow terminals, search typing and paste, modal Escape, cancellation, redirecting stdout, stale result order, stream closure, retention eviction, and restoration of terminal modes.
Do not turn an authored test into a passed test, a parser check into compilation, or a core test into terminal verification.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211{ "skill": "charm-tui", "format_version": 1, "status": "Specifications authored; agent evaluations not executed.", "scenarios": [ { "id": "preserve-v1", "setup": "Existing Go app pins Bubble Tea v1 and uses a custom picker.", "prompt": "Fix the resize bug without changing the UI or upgrading dependencies.", "must_pass": [ "Inspect existing imports and layout before editing.", "Keep the v1 API unless a necessary change is explained.", "Test the reported resize and preserve key/output behavior." ], "must_not": [ "An incidental v2 migration.", "Replacing the picker with stock list behavior." ] }, { "id": "preserve-custom-design", "setup": "Existing picker has an animated mascot, custom counters, two themes, and a bespoke header.", "prompt": "Reduce duplicated code without removing features or changing how this picker looks.", "must_pass": [ "Record current visible and interaction contracts.", "Simplify repeated plumbing beneath those contracts.", "Compare before and after on fixed fixtures." ], "must_not": [ "Removing the mascot or counters as cleanup.", "Claiming fewer lines proves equivalence." ] }, { "id": "focus-and-modal", "setup": "Picker has normal navigation, a search editor, and a confirmation overlay.", "prompt": "Typing q in search quits the app, and closing a dialog sometimes accepts the row beneath it. Fix both.", "must_pass": [ "Focused input receives printable text.", "The modal consumes the closing event exactly once.", "Restore focus and retain any required component commands.", "Add regression tests for both bugs." ], "must_not": [ "Broadcasting all key events to every child.", "Disabling q everywhere instead of resolving context." ] }, { "id": "stale-before-debounce", "setup": "Async search A is in flight; user types B; B has a 150 ms debounce. Backend can complete out of order.", "prompt": "Ensure the UI always represents the latest query, including errors and loading state.", "must_pass": [ "Invalidate identity at the query change, before the new debounce fires.", "Cancel superseded work and pass context to the backend.", "Reject stale rows, stale errors, and stale completion flags.", "Use deterministic out-of-order completion tests." ], "must_not": [ "Only incrementing generation when B starts.", "Using Batch or sleeps to assume order." ] }, { "id": "v2-box-width", "setup": "Lip Gloss v2 container has a one-cell border and two padding cells on each horizontal side; no margins. Outer allocation is 40 cells.", "prompt": "Fix a child that wraps two characters before the right edge and document the sizing rule.", "must_pass": [ "Recognize an inner content width of 34 cells for this style.", "Set the outer style width to 40, not 34.", "Test actual display width and handle allocations smaller than the frame." ], "must_not": [ "Treating Width as content width.", "Ignoring external margins when generalizing the calculation." ] }, { "id": "small-unicode", "setup": "Data contains CJK, combining marks, emoji, ANSI styles, and long paths. The footer wraps at narrow widths.", "prompt": "Make this UI safe at 40x12 and at initial zero size without breaking Unicode.", "must_pass": [ "Measure terminal cells and actual rendered chrome.", "Clamp allocations and provide a safe too-small fallback.", "Preserve identity and selection across layout changes.", "Test styled and multilingual data." ], "must_not": [ "Byte slicing rendered strings.", "Using rune count as universal display width.", "Claiming zero-sized content makes an oversized frame fit." ] }, { "id": "pipeline-picker", "setup": "Command accepts newline-separated candidates on stdin and must return one stable ID for another program.", "prompt": "Add an inline selector that works in pipelines and cancels cleanly.", "must_pass": [ "Separate candidate input from interactive terminal input.", "Reserve stdout for final machine output after UI completion.", "Define non-TTY, cancelled, failed, and interrupted behavior.", "Verify no logs or escape sequences contaminate the result." ], "must_not": [ "Writing renderer/debug output to result stdout.", "Assuming stderr can always be safely logged to while it is the UI sink.", "Returning a selection on cancel." ] }, { "id": "streaming-history", "setup": "Long-running log view appends events while the user reads older output. Syntax rendering is expensive.", "prompt": "Keep the view responsive and stop it jumping back to the bottom.", "must_pass": [ "Separate follow-tail state from scrolling.", "Preserve the reading anchor while new data arrives.", "Bound retained data or specify a retention policy.", "Cache expensive rendering with complete invalidation keys and measure costs.", "Avoid repeated exact whole-history measurement for an overflow check." ], "must_not": [ "Unconditionally scrolling to bottom on append.", "Silently dropping durable log events while claiming full retention.", "Rendering every historical item on each tick." ] }, { "id": "accessible-form", "setup": "Small form collects project name and two options; users include assistive-technology users and CI jobs.", "prompt": "Choose and implement an appropriate Charm interface with an accessible path.", "must_pass": [ "Consider Huh or an equally small prompt flow rather than a full-screen framework.", "Provide genuine plain/accessible or noninteractive behavior.", "Explain validation and preserve entered values on errors." ], "must_not": [ "Equating NO_COLOR with complete screen-reader accessibility.", "Hanging noninteractive execution for input." ] }, { "id": "ssh-isolation", "setup": "Two SSH clients use the same TUI service with different terminal sizes and backgrounds.", "prompt": "Fix cross-session theme and cursor interference.", "must_pass": [ "Make model and mutable terminal/theme state session-scoped.", "Bind subscriptions and cancellation to session lifetime.", "Test two concurrent sessions with different capabilities." ], "must_not": [ "Using process-global active model or mutable terminal capability state.", "Claiming a UI fix validates SSH authentication security." ] }, { "id": "matching-test-major", "setup": "Application imports charm.land/bubbletea/v2 but a tutorial uses unversioned x/exp/teatest.", "prompt": "Add integration tests without introducing a major-version mismatch.", "must_pass": [ "Inspect the helper module and choose teatest/v2 or an appropriate v2-compatible harness.", "Verify toolchain and dependency compatibility in the target module graph.", "Use bounded waits and check final outcome." ], "must_not": [ "Assuming unversioned teatest is the v2 helper.", "Claiming helper existence proves compatibility with every later release." ] }, { "id": "no-tui-needed", "setup": "A Go command converts input files to JSON; no interactive task exists.", "prompt": "Make this command pleasant to use and review whether Charm would help.", "must_pass": [ "Prefer clear flags, errors, help, and machine-readable output.", "Decline unnecessary TUI infrastructure while offering task-appropriate improvements." ], "must_not": [ "Adding Bubble Tea solely because the skill was loaded.", "Replacing scriptable output with a full-screen view." ] }, { "id": "write-cancellation", "setup": "A confirmation dialog submits a remote delete operation; the server may commit before cancellation arrives.", "prompt": "Let Escape cancel safely and avoid duplicate deletion requests.", "must_pass": [ "Distinguish dismissing a dialog from proving server-side cancellation.", "Track operation identity and reconcile completion.", "Prevent duplicate active submissions and explain retry semantics." ], "must_not": [ "Applying latest-search-wins logic to silently forget a committed write.", "Reporting cancelled solely because the dialog disappeared." ] }, { "id": "conflicting-source-notes", "setup": "Maintainer notes say no list-level cache; implementation at the same revision contains one.", "prompt": "Study the list and recommend performance improvements.", "must_pass": [ "Read the implementation and report the documentation discrepancy.", "Base cache-existence claims on code, not the stale note.", "Measure before proposing another cache." ], "must_not": [ "Repeating the stale note as fact.", "Claiming an entire application was benchmarked from a source read." ] } ]}