Engineering

The key that did nothing.

By Albert 5 min read

Three releases ago le grew per-field actions in its detail pane: open this port rather than whichever one sorts first, reveal the binary as distinct from the folder, act on the row under the cursor. It shipped working. It also shipped reachable only by pressing tab — and nothing on the screen has ever said tab. So the key a person actually presses on a row they have selected, , did nothing at all.

That is the whole bug, and it is not a bug any test catches. Every test passed. The feature worked exactly as designed for anyone who knew the undocumented key.

A feature nobody can find is a feature you didn't ship

Look at what the footer advertises: j/k move, z group, / filter, x stop, o open, c copy, f pin, ? help, q quit. Nine keys. tab is not among them, and never was. The ? overlay mentioned it, on a line most people meet after they have already formed a mental model of the app.

So the pane below the table read as a readout — a place where facts about the selection are displayed — rather than as the place where you act on it. That reading is reasonable. It is also the opposite of the menu-bar app, where a row simply is its actions and you open them by clicking it. The two halves of the same product disagreed about what a selected row means, and the terminal half lost.

What 0.1.22 does

on a row now moves focus into the pane and lands on its first field — the same door tab opens, which keeps working. From there j/k step between fields, runs the focused one, and tab or esc backs out:

│ › ports  5000, 7000   pid 788   owner macos
j/k field  ⏎ opens :5000   tab/esc back

On a group header still folds the group — that branch is checked first and is untouched — and , and space stay group-only. Nothing that used to fold now does something else.

The new test pins two things: that lands on the first field rather than merely flipping a flag, and that esc still gets you out. A key that opens a mode you cannot leave is worse than a key that does nothing.

The part that made this safe to change

There is a detail here worth more than the feature. Earlier this year our randomised key-sequence test opened nineteen browser tabs per run while never pressing the key that opens a browser. o was not in the key list. But tab entered pane focus and ran the focused field, and those two navigation keys composed into the launcher. Reading the key list would never have found it.

The fix at the time was not to stub the browser call at its call site. It was to neuter every launcher hook — browser, Finder, Terminal — for the entire test binary, in an init() inside a _test.go file that no new test can opt out of by not knowing it exists.

Which is exactly why this release cost nothing to make safe. Adding as a second door shortens the path from the key list to the browser launcher: it used to be tab+, and it is now +. If the guard lived at the call sites, that shortening would have been a live hazard discovered by a developer's desktop filling with tabs. Because the guard lives at the boundary, the change required no test edits at all.

The generalisation: guard where the process touches the world, not where you happen to call it from. Reachability changes with every keybinding you add. The boundary does not.

Testing a TUI honestly, and the trap under it

Unit tests drive the model's update function directly. That proves the state machine and proves nothing about whether a real terminal, a real scan, and the real renderer agree — so before tagging we live-fire the actual binary: start a real listener, run le under a pty, send real keystrokes, read the frame a human would see.

The first version of that harness reported a clean pass. It was verifying nothing.

le under a bare pty emitted twelve bytes and stopped. Two of its dependencies ask the terminal questions before rendering: lipgloss wants the background colour (OSC 11, \e]11;?) so it can pick light or dark styling, and bubbletea wants the cursor position (DSR, \e[6n). A real terminal answers both. A raw pty answers neither, and both calls block. The captured frame was empty — so every --expect matched against an empty string, found nothing to contradict it, and the harness cheerfully declared success on a TUI that never drew a single row.

The harness now answers both queries, and the lesson went into the bug journal: assert on a positive marker you have actually seen in a real frame, never on the absence of a symptom, and treat an empty capture as a harness failure rather than a quiet pass. A green check on nothing is worse than a red one, because you stop looking.

Also in this release

Nothing else — this one is a single change, verified end to end: the full gate (build, vet, gofmt, go test -race, golangci-lint), the live-fire above, and then the same live-fire re-run against the brew-installed binary rather than the local build, because the thing you shipped and the thing you tested are not the same artifact until you check.

brew upgrade alikatgh/tap/le
le --version   # 0.1.22

It's free and it's MIT.

macOS and Linux, one static binary. brew install alikatgh/tap/le