AI News · Tools
Matt Pocock's /prototype:
throwaway code against a real question.
DE Auf Deutsch lesen
/prototype is meant to answer a design or state question through short,
deliberately throwaway code instead of discussing it. This skill also comes from Matt
Pocock's collection, just like /handoff.
Instead of making up a task, I looked at real PlantWiz code until a real, unresolved state
question turned up, and triggered /prototype right there.
skills/engineering/prototype.
Tested in an isolated Git worktree of PlantWiz, a production Vue 3 application with a Node
backend, so ongoing work in the actual repository stayed untouched. The question was real and
still open, not constructed for this post. One run, carried out and assessed by me.
What the skill actually prescribes
The idea behind /prototype is right there in the first line of the skill file: "A
prototype is throwaway code that answers a question. The question decides the shape." Unlike
/handoff, this file has no disable-model-invocation: true in its
frontmatter, so in theory the agent could also load it on its own when a design question comes
up in conversation. In practice that stays a footnote, because the frontmatter description is
narrowly scoped: "sanity-check whether a state model or logic feels right, or explore what a UI
should look like."
Before any code gets written, the skill splits into exactly two directions. The main file,
SKILL.md, doesn't spell out the details itself; it points to two separate files in
the same folder, LOGIC.md and UI.md, each of which prescribes the
full process for its branch:
- LOGIC branch (
LOGIC.md) for "does this state model feel right?": a single, self-contained HTML file with buttons that pushes a state machine or reducer through cases that are hard to reason about on paper. Built so a non-developer (designer, PM, domain expert) can click through it too. - UI branch (
UI.md) for "what should this look like?": several structurally different layout variants on the same route, switchable via a?variant=parameter and a floating bottom bar with arrow-key navigation.
The same six rules apply to both branches, summarized straight from the skill file:
- Throwaway from day one, but clearly marked as such, and located close to the code it concerns.
- Trivial to start: one command for UI prototypes, a double-click for the HTML logic demo.
- No persistence by default. State lives in memory, unless the question is explicitly about persistence.
- Skip the polish. No tests, no error handling beyond what's needed to run, no abstractions.
- Surface the state, after every action or every variant switch.
- Capture it when done: fold the validated decision into the real code, commit the prototype itself to a throwaway branch, with a pointer to it on the ticket. Main keeps only the decision, not the prototype.
Why not just try it directly in the real code?
The obvious alternative would be to rework the logic in question directly inside the real
component and keep testing until it fits. Rules one and three exist specifically to prevent
that: no test setup, no database, no existing state store in the way. A UI prototype does
attach to a real route (sub-shape A, the preferred case per UI.md), but only to be
judged against real data and real density, not to be production-ready. The decisive difference
from a direct implementation: a prototype is allowed to be wrong without anyone having to
surgically remove it later, because it never intended to land in main.
The test case: a real, unresolved state question
In TodoView.vue, nextDue() computes the next due date for recurring
garden to-dos from completedAt + interval. isCompleted(), however,
checks completedAt ≥ nextDue, even though nextDue was itself just
derived from completedAt. With a positive interval, that condition is arithmetically
never true. A recurring to-do therefore never lands in the "done" list: click the checkbox and
it immediately reappears under "open," with a new, further-future due date, with no visible
confirmation at all. The field comment in the data model, completedAt // ISO datetime of
the last check-off, shows the original author was aware that information gets lost here,
without a decision ever having been made for or against a history.
The design question in one sentence: should checking off a recurring to-do get some visible
confirmation or history, or is the invisible push-forward to the next due date actually the
intended behavior? That's clearly a state question, not a layout problem, so the LOGIC branch
from LOGIC.md.
The prototype
Per LOGIC.md, the question itself opens the demo, as a paragraph, not a comment.
After that comes a pure reducer with no DOM access, liftable into the real
code, and above it a page with domain-language buttons instead of reducer jargon. Claude Code
built two models side by side, under my direction, switchable with one click:
- Model A is a 1:1 reproduction of the logic in
TodoView.vue, bug included. - Model B is an alternative proposal with
lastCompletedAtand an explicithistory[]list instead of self-reference.
Plus, as prescribed, free-play (create a to-do, check it off, reset) and three guided walkthroughs as tabs: the happy path with a one-off to-do, a recurring to-do checked off three times in a row, and a double-click case right after checking off. Every step is a real button that triggers the matching action and advances to the next step.
| Result in the browser | Model A (as coded) | Model B (with history) |
|---|---|---|
| Recurring to-do checked off once | stays under "open," no checkmark visible | visibly moves to "done," with checkmark |
| History | none, just the last timestamp | counter "done 1×," grows with each pass |
| Undo possible? | de facto yes, but only because "done" never triggers | yes, via the same button, as long as it's the same day |
The prototype is a single, self-contained HTML file, exactly as LOGIC.md
prescribes. No build, no server needed, clickable directly in the browser.
What the prototype surfaced along the way
The real payoff didn't show up first when clicking through it, but already while building Model
A: to faithfully reproduce the existing logic, Claude Code had to transcribe
nextDue() and isCompleted() line by line into the reducer. That's
exactly where the
self-reference becomes obvious, whereas in the real code it's spread across several computed
properties and a 200-line component, which makes it less noticeable. The prototype didn't go
looking for the bug; it surfaced it as a byproduct of faithfully reproducing the logic. In the
browser you can watch live that "done" stays empty after the click while "open" only changes its
date.
The wrap-up the skill prescribes: capture it instead of leaving it lying around
Step six in SKILL.md requires committing the prototype to its own throwaway branch
after the decision, with the question and verdict in the commit message, instead of just
leaving it in the working directory. I followed that: branch prototype-skill-test,
commit message with the question, the trigger (the bug in TodoView.vue), and the
verdict (Model B answers the question more cleanly and surfaces the bug; recommendation for the
implementation: replace completedAt with lastCompletedAt plus
history[]). The prototype itself stays on that branch, not in main.
UI.md, with its
several layout variants and the floating switcher bar, stayed untested, because the question I
found was clearly a state question. Whether the variant switching works as smoothly in a real
Vue route as described in the LOGIC.md branch would need a second run against a
real UI question to show.
Direct comparison to the obvious alternatives
| /prototype | Testing directly in the real code | Just describing/planning | |
|---|---|---|---|
| Trigger | manual (or theoretically automatic, rarely per the skill) | manual, ad hoc | manual, in words |
| Can be wrong at no cleanup cost | yes, throwaway by definition | no, has to be surgically removed later | yes, but yields no insight |
| Understandable for non-developers | yes, domain-language buttons instead of code | no | partially, without interaction |
| Surfaces distributed logic bugs | yes, as a byproduct of reproduction | possible, but hidden within the existing scope | no |
| Leaves a record | yes, on a throwaway branch with a verdict | only the final diff | only if written down |
When it's worth it
A state or UI question you can't play out in your head
A state model with several transitions, a data model with unclear edge cases, or several plausible layouts for the same page. Exactly the case from this test.
A non-developer needs to weigh in on the decision
The HTML logic demo needs no setup and speaks domain language instead of code. Where only developers are involved, a conversation over the existing code is often enough.
The answer is already obvious
Skip/prototype. For a clear, uncontroversial change, the throwaway detour is
pure overhead.
Choosing skills for your team?
I set up Claude Code in developer teams, including the question of which skills actually pay off and which just cost tokens. 30-minute intro call, free of charge.