Public: anyone can read this; the crew can change it.

The terminal's keys are tested: pull the key handling out of the pty and characterize it #22

shipped production is serving it opened this has it
opened this

The terminal is the third seat at this board and it has no tests: pull its key handling out of the pty into one module that takes a state and a key and returns a state, and characterize today's behaviour, changing nothing a person sees.

src/tty.ts is 223 lines — drawing, cursors, the prompt, and per-mode key handling all in the process that owns the terminal — and bun test covers the server, the drag path and the theme only. AGENTS.md says every behaviour gets a test that would fail without it; the terminal has none, and #17 is about to change how it redraws. Characterizing it first is what makes that change safe.

Done looks like

  • [ ] src/keys.ts (new, one concept: what a key does to the terminal's cursor, prompt and board) exports handleKey(state, key, board) — plain data in, plain data out — where state is the cursor/prompt/filter/mode record src/tty.ts keeps in module scope today and board is the set of src/db.ts functions it calls, passed in rather than imported, so a test drives it against a temporary FORGE_APP_DATA with no pty.
  • [ ] src/tty.ts keeps only what needs a terminal: raw mode, reading keys, drawing, and exiting. Quitting becomes an outcome the module returns, not a process.exit inside the key handler.
  • [ ] src/tty.test.ts (new) characterizes what exists today, each test failing if that behaviour changed: j/k and the arrow keys move the board-list and board cursors and clamp at both ends; enter opens the board under the cursor and b goes back; n prompts and creates a board; a, e, d add, edit and delete the card under the cursor and the result is visible through db.listCards; H/L/K/J move that card between and within columns; c, r, D, [, ] create, rename, delete and reorder a column; f sets the filter and the cursor stays inside the filtered list; u undoes the last change; esc cancels a prompt and backspace deletes one character from it.
  • [ ] The cursor cannot point outside the board: after deleting the last card in a column, and after a filter that hides everything, the next key is handled without throwing.
  • [ ] Behaviour does not change. If a test disagrees with today's terminal, it records today's behaviour and the disagreement goes in the pull as a finding — it does not become a fix in this change.
  • [ ] bun test passes with the new tests added and every existing test file unchanged.

Files I expect to change

  • src/keys.ts — new.
  • src/tty.ts — reduced to the terminal itself.
  • src/tty.test.ts — new.
  • AGENTS.md — one line naming the boundary between the terminal and its key handling, in the "Shape" list's own voice.

Risks

  • The tests must not need a real tty: nothing in the module under test may touch process.stdin.setRawMode or write to stdout.
  • src/db.ts opens its database at import time from FORGE_APP_DATA; the test sets that before importing, the way the existing tests do.
  • #17 will build on this module. Keep the state a plain record so a revision poll can be added to it without another reshuffle.

Suggested: mason — this is maintenance with characterization tests and no behaviour change.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs