Files
skills/deprecated/prototype/LOGIC.md
2026-08-26 17:01:23 -04:00

5.8 KiB

Logic Prototype

A tiny interactive terminal app that lets the user drive a state model by hand. Use this when the question is about business logic, state transitions, or data shape — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases.

When this is the right shape

  • "I'm not sure if this state machine handles the edge case where X then Y."
  • "Does this data model actually let me represent the case where..."
  • "I want to feel out what the API should look like before writing it."
  • Anything where the user wants to press buttons and watch state change.

If the question is "what should this look like" — wrong branch. Use UI.md.

Process

1. State the question

Before writing code, write down what state model and what question you're prototyping. Use one paragraph in the prototype's README or a comment at the top of the file. A logic prototype that answers the wrong question is pure waste, so make the question explicit enough to check later whether the user is watching now or returning to it AFK.

Done when the prototype states one concrete logic question and the model being tested.

2. Pick the language

Use whatever the host project uses. If the project has no obvious runtime (e.g. a docs repo), ask.

Match the project's existing conventions for tooling. Don't add a new package manager or runtime just for the prototype.

Done when the prototype has a runnable host-project language and toolchain without introducing a new runtime convention.

3. Isolate the logic in a portable module

Put the actual logic — the bit that's answering the question — behind a small, pure interface that could be lifted out and dropped into the real codebase later. The TUI around it is throwaway; the logic module shouldn't be.

The right shape depends on the question:

  • A pure reducer — (state, action) => state. Good when actions are discrete events and state is a single value.
  • A state machine — explicit states and transitions. Good when "which actions are even legal right now" is part of the question.
  • A small set of pure functions over a plain data type. Good when there's no implicit current state — just transformations.
  • A class or module with a clear method surface when the logic genuinely owns ongoing internal state.

Pick whichever shape best fits the question being asked, not whichever is easiest to wire to a TUI. Keep it pure: no I/O, no terminal code, no console.log for control flow. The TUI imports it and calls into it; nothing flows the other direction.

This is what makes the prototype useful past its own lifetime. When the question is answered, the validated reducer, machine, or function set can be lifted into the real module on its own.

Done when all tested logic lives behind one portable, pure interface and the TUI depends on it in only one direction.

4. Build the smallest TUI that exposes the state

Build it as a lightweight TUI — on every tick, clear the screen (console.clear() / print("\033[2J\033[H") / equivalent) and re-render the whole frame. The user should always see one stable view, not an ever-growing scrollback.

Each frame has two parts, in this order:

  1. Current state, pretty-printed and diff-friendly (one field per line, or formatted JSON). Use bold for field names or section headers and dim for less important context (timestamps, IDs, derived values). Native ANSI escape codes are fine — \x1b[1m bold, \x1b[2m dim, \x1b[0m reset. No need to pull in a styling library unless one is already in the project.
  2. Keyboard shortcuts, listed at the bottom: [a] add user [d] delete user [t] tick clock [q] quit. Bold the key, dim the description, or vice-versa — whatever reads cleanly.

Behaviour:

  1. Initialise state — a single in-memory object/struct. Render the first frame on start.
  2. Read one keystroke (or one line) at a time, dispatch to a handler that mutates state.
  3. Re-render the full frame after every action — don't append, replace.
  4. Loop until quit.

The whole frame should fit on one screen.

Done when every available action re-renders a complete one-screen view of the current state and shortcuts.

5. Make it runnable in one command

Add a script to the project's existing task runner (package.json scripts, Makefile, justfile, pyproject.toml). The user should run pnpm run <prototype-name> or equivalent — never need to remember a path.

If the host project has no task runner, put the command at the top of the prototype's README.

Done when a fresh user can launch the prototype with one documented command.

6. Evaluate it

In HITL mode, give the user the run command. They drive it themselves. The interesting moments are when they say "wait, that shouldn't be possible" or "huh, I assumed X would be different" because those expose bugs in the idea. Add actions when the feedback needs them.

In AFK mode, run the command yourself and drive the hard cases that answer the stated question. Record the observations that support the verdict.

Done when the evaluator can exercise the model and the prototype exposes every state transition needed to reach a verdict.

Production mapping

When the shared SKILL permits production work, lift the validated reducer, machine, or function set into the real module. Keep the TUI shell on the throwaway branch.

Anti-patterns

  • Don't generalise. No "what if we wanted to support X later." The prototype answers one question.
  • Don't blur the logic and the TUI together. If the reducer / state machine references console.log, prompts, or terminal escape codes, it's no longer portable. Keep the TUI as a thin shell over a pure module.
  • Don't ship the TUI shell into production. The shell is optimised for being driven by hand from a terminal. The logic module behind it is the bit worth keeping.