Why a Strong Coding Agent Failed a TUI Task: A Detailed Case Study
I asked a coding agent to design and build an operator TUI for film-pipeline-langgraph. The first result looked impressive in review and failed the actual product requirement.
This article is a single case study, not a benchmark of models. The claims below are tied to two repository commits: the design commit and the first implementation commit.
The task and the acceptance gap
The system already exposed projects, review gates, checkpoints, provider health, artifacts, and audit history. The task was to make those capabilities operable for a person spending hours in the terminal: persistent layout, keyboard navigation, drill-down, background work, and clear state.
The design commit added 31 documents and 6,142 lines. It specified personas, navigation, view models, a three-panel shell, page-level wireframes, async behavior, and end-to-end flows. The next commit added 1,255 lines across services, MCP tools, tests, and the console—but the UI itself was a 280-line numbered REPL built around input() and print().
def run(self) -> int:
self._output(title("LangGraph Film Studio"))
while True:
self._output(self._menu())
choice = self._input("> ").strip().lower()
if choice in {"q", "quit", "exit"}:
return 0
self._dispatch(choice)
The implementation was not worthless: it introduced a service boundary, gateway, typed request models, MCP exposure, and unit tests. But those were supporting components. Calling the result the requested TUI confused architectural progress with product acceptance.
| Feature | Analysis Scope | Delivered |
|---|---|---|
| Shell layout | 3-panel: projects sidebar + main workspace + context drawer | Single column, numbered menu |
| Navigation | Keyboard-driven, command palette (g d dashboard, g r review) | Type a number, press enter |
| Async refresh | Live polling, status indicators | None. Re-render on every input |
| Dashboard | Summary card, orchestrator summary, action board, risk board, activity log | One row in an ASCII table |
| Review workspace | Side-by-side artifact comparison, revision notes | Single text dump |
| Provider health | Color-coded status, inline warnings | Plain text table |
| Checkpoints | Diffs, rollback preview | Listed by ID |
| Framework | Textual (async, widgets, CSS layouts) | input() / print() |
| Project sidebar | Filter, status indicators, phase labels | Active project: none |
Five whys: why did review accept the wrong thing?
- Why was the UI incomplete? The implementation optimized for exposing operations, not reproducing the specified interaction model.
- Why could that pass? The task did not have an executable acceptance rubric tied to the wireframes.
- Why was there no rubric? Analysis and full implementation were requested as one large delivery.
- Why did the large delivery feel complete? Document volume and clean supporting architecture acted as proxies for working UX.
- Why were proxies enough? I reviewed artifacts, not a recorded human workflow through a real terminal.
The root cause I can support with evidence is an acceptance-system failure. Context exhaustion, training distribution, or model confidence may have contributed, but this one case cannot distinguish among them.
What happened next
The original article said the TUI had never been built. That is no longer true. The repository now contains a Textual application with screens, widgets, view models, in-process and MCP gateways, framework-harness tests, real-provider tests, and a tmux-driven smoke workflow.
It did not arrive in one corrective leap. The history shows separate commits for the Textual cockpit, approval confirmation, project creation, persistence, redesign, readable review output, MCP parity, asset behavior, real-mode intake, and simplification. That incremental sequence is the useful result: narrow changes made the UI reviewable and testable.
A better delivery contract
- Freeze acceptance criteria. Convert each required interaction into an observable pass/fail check.
- Build one vertical slice. Launch, create a project, show durable state, and approve one phase.
- Run the code. Require framework-native tests during implementation, not after prose review.
- Test the terminal boundary. Drive the packaged app through a PTY and capture the rendered state.
- Expand only after evidence. Add pages and workflows once the skeleton clears its gate.
Download the TUI evaluation checklist
What this case does—and does not—show
It shows that a strong design artifact can coexist with a delivery that misses the primary interface requirement. It also shows that agent-produced code can contribute useful service boundaries even when the surface fails acceptance.
It does not show that coding agents cannot build TUIs, that one framework is universally better, or that document length caused the failure. Those hypotheses need controlled comparisons across prompts, models, and tasks.
The practical lesson is smaller and stronger: never infer implementation quality from analysis quality. Define the user-visible evidence first, then make every coding iteration earn the next one.