agentleFS
DoryZiDoryZi/plan-html/SKILL.md

plan-html

Skill3 starsChanged 5 months agoMITView on GitHub

Interactive decision-deck planning. Same research/design thinking as /plan, but the plan is an interactive HTML deck the user works through in the browser — intent cards and decision cards, answered with buttons OR free text, with per-card questions to the agent — iterated in rounds until finalized. Every answer autosaves, so interruptions lose nothing. Use whenever the user wants to plan, review, or iterate on a plan interactively instead of reading markdown in the terminal.

1 other project uses a version of this.

Adopted by

Most recently taken up by

  • Other versions1 project uses a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 1

    1. exerias21/brainstorm-toolkit★ 0same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • In this repositoryOne other file it gives its agents.

The file

Show its rules and text

We split this skill into the rules it gives an agent

26 rules. About 0 are common, 9 are distinctive, and 17 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈9

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 17

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Plans persist: Write plan.json to <repo>/design/plans/<slug>/plan.json (gitignored — verify.

    line 38
  2. Very specific:

    Never lose user input: Before starting a round, check for an existing answers.json next to the plan — if present, the user has prior …

    line 43
  3. Distinctive:

    Ask only what you need to scope the exploration — a couple of questions max.

    line 53
  4. Distinctive:

    The loop must converge — balance looping against finishing: Every round should shrink the open set, not grow it sideways.

    line 92
  5. Very specific:

    Prefer this over spinning up a separate needs-you decision when the ambiguity is *about the intent itself*.

    line 111
  6. Very specific:

    Diagrams are REQUIRED: Every plan ships a plan-level diagramSvg (architecture/flow of what's being built).

    line 130
  7. Very specific:

    Inline only — NEVER as files: Do not write mock .html/.png files to disk, do not send mocks as attachments, do not link out.

    line 155
  8. Very specific:

    Every user action arrives as an ask with cardId:"__interview__":

    line 289
  9. Very specific:

    After each of these: bump rev, write plan.json.

    line 298
  10. Very specific:

    The rebuild happens on the next round, not in the popup: When a send-round arrives and plan.interview.state == "done" with appliedRound …

    line 309
  11. Very specific:

    Serve with a long timeout: when an interview is likely (--timeout 21600): the default 1800 s is a total wall-clock limit, not idle, and an …

    line 324
  12. Distinctive:

    ONLY reliable delivery. The server pushes the new plan over SSE and the page reconciles in place.

    line 378
  13. Very specific:

    Keep the Monitor armed until a send-round/finalize/timeout event.

    line 388
  14. Very specific:

    Struck-out cards are GONE — absent means removed: When the user strikes a card (the ✕) it is dropped from the round entirely: it will NOT …

    line 421
  15. Distinctive:

    When the answers come back, continue building.

    line 493
  16. Very specific:

    All prose fields render markdown (bold, italic, code, fenced blocks, bullet lists, links) — format the plan properly.

    line 594
  17. Distinctive:

    No mermaid, no CDN.

    line 604
  18. Very specific:

    Run the same understand → explore → design stages as /plan.

    line 609
  19. Very specific:

    Always include intents[] (one outcome + why + verify each) and a plan-level diagram.

    line 616
  20. Distinctive:

    Treat agreed boundaries as binding: if a later round would cross one, raise it as a new decision — don't silently widen scope.

    line 623
  21. Very specific:

    Keep each card's title/summary in sync with its content — when the meaning changes, rewrite the title.

    line 628
  22. Very specific:

    Start a cloudflared tunnel on demand (against the live port, no restart) when the user wants the deck public / on their phone.

    line 632
  23. Very specific:

    Use uv run --directory to launch serve_plan.py (per CLAUDE.md).

    line 634
  24. Distinctive:

    Write production/test code — that's the build phase's job.

    line 640
  25. Distinctive:

    Add external CDNs/scripts to the deck — it must open offline.

    line 643
  26. Distinctive:

    Write mock files to disk or send mocks as attachments/links — every visual renders inline in the deck (svg/html options, diagrams), full …

    line 644

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md
<!-- experiment-banner:start -->

🧪 This is an experiment — part of AI Will Replace You.

Stop wasting time on AI. I run practical experiments — real lessons you can use tomorrow, biweekly.

▶ Watch the demo on YouTube · 📖 Read the write-up · ✉️ Subscribe to the newsletter

More on @aiwillreplaceyou

<!-- experiment-banner:end -->

Plan (HTML / interactive, round-based)

Same goal as /plan — research a codebase and design an implementation plan interactively — but the review surface is the browser deck, not the terminal. The plan is delivered as cards the user answers in place: intent cards (what they want, in their words, editable) and decision cards (forks in the design). Free text counts as a full answer; every card can carry a question to the agent; rounds repeat until the user finalizes. The user can also reshape the plan directly: add their own intents, boundaries, decisions, or steps with a "+ Add" button per section, and strike out any card (✕) to remove it. Added cards come back tagged userAdded; struck cards simply vanish from the round.

HARD OUTPUT RULES — read first

  1. The deck IS the deliverable. Never print the plan as markdown in the terminal. No plan dumps, no "here's a summary of the plan" walls of text, no markdown decision lists. Everything the user needs to read goes into plan.json and is rendered by the deck.
  2. Terminal output is status lines only, a few short lines per round: what stage you're in, the deck URL, the round number, and (between rounds) a one-line note of what changed. Nothing else.
  3. Plans persist. Write plan.json to <repo>/design/plans/<slug>/plan.json (gitignored — verify; add design/plans/ to .gitignore if missing). Never /tmp: the sibling answers.json autosave must survive reboots and be findable after an interrupted session.
  4. Never lose user input. Before starting a round, check for an existing answers.json next to the plan — if present, the user has prior answers; the server preloads them automatically. On a timeout result, the autosaved answers are in the printed JSON (saved) — treat them as a partial round, don't discard them.

Workflow

Stage 1: Understand (chat, minimal)

Ask only what you need to scope the exploration — a couple of questions max. The deep interrogation does NOT happen in chat: it happens in the deck (Stage 3), where answers persist and the user can think. Do not proceed until the user confirms the task framing.

Stage 2: Explore (parallel agents)

Spawn explore agents in parallel (single message, multiple Agent calls), sized to scope (1 small → 3–4 large), each returning structured markdown with file paths and line numbers. Synthesize briefly (a few lines, not a report), confirm with the user, then move on. Detailed findings belong in the deck's card bodies, not the terminal.

Stage 3: Design → plan.json

Draft the plan directly as plan.json (schema below).

Every plan MUST have a goal, intents[] with a verify per intent, a plan-level diagramSvg, and grill cards — no exceptions. boundaries[] is optional: include it when the task has real scope risk (things that must not be touched, cost ceilings, "don't refactor X") and skip it for small contained tasks. The user can drop any boundary with its "Not needed" button.

  • goal — one sentence that shouts what we're building and why. Rendered as a highlighted banner at the top of the deck. If you can't write it in one sentence, the plan isn't understood yet.
  • GRILL THE USER — hard, but proportionate. The round-1 deck must interrogate the user: turn every real assumption, ambiguity, and unstated requirement into a needs-you decision card. Cover (where they genuinely apply): who/what uses this, edge cases, failure modes, cost/limits, security, what NOT to build, and what "done" looks like. Challenge the user's framing when you disagree — present the conflict as a card with your counter-position in building/tradeoffs. If an answer comes back vague, re-ask it sharper in the next round; don't build on a vague answer. Scale the grilling to scope and risk: a contained task earns a handful of sharp cards, not an inquisition — never manufacture questions to fill the deck.
  • The loop must converge — balance looping against finishing. Every round should shrink the open set, not grow it sideways. New cards in round N+1 are justified only by the user's answers or by re-exploration findings — not by second-guessing settled cards. When nothing real is left open, say so in the round-status line and let the user finalize; aim to be finalize-ready by round 2–3 for most plans. One round straight to Finalize is a success, not a shortcut.
  • intents[] — the spine of the plan. An intent is one outcome the user wants + why + how we prove it's done — written in the user's own words (first person works well: "I want my answers to survive an interruption"). It is NOT a feature list and NOT an implementation step; if you can't attach a binary verify (method + exact command + expected result) to it, it's not an intent yet — sharpen it. Intents replace acceptance criteria; they are user-editable in the deck, so write them to be edited.
    • Grill clarifying questions ON the intent (agentQuestions[]). When an intent is fuzzy, attach pointed clarifying questions directly to it — who uses it, what "done"/"revert"/"approve" concretely mean, edge cases — and the deck renders them as an in-card checklist the user answers inline. Prefer this over spinning up a separate needs-you decision when the ambiguity is about the intent itself; use decision cards for genuine forks in the design. Answers come back in the round under the card's grill[] (each {id, q, answer}) — read them and tighten the intent (and its verify) next round. Soft-gated: unanswered grill questions are surfaced and counted but do NOT block Finalize.
  • discovered[] — what exploring turned up (optional, shown after the intents). A finding from Stage 2 that changes or informs the plan: existing code that already does part of this, a constraint you didn't know about, an assumption that turned out wrong. Purely informational — no approve/reject, nothing to decide — so it never gates Finalize. Each card carries finding (what you found), and optionally impact (why it changes the plan) and source (where you found it, e.g. a file path). Only add one when it actually changes how the user should read the rest of the plan — not a running log of every file you opened.
  • boundaries[] — the fence (optional, shown after the intents). What's in scope, what's out of scope, and what must not be touched. One boundary per card, written as a testable statement. When present, boundaries gate Finalize — the user must agree to each, rewrite it, or drop it.
  • Diagrams are REQUIRED. Every plan ships a plan-level diagramSvg (architecture/flow of what's being built). Additionally, any intent or decision that describes structure, flow, or sequence gets its own per-card diagramSvg — if prose is explaining a shape, draw the shape instead.

    Diagram authoring rules (the deck styles these classes — use them):

    • class="node" boxes (bright blue). Color-code stages with the variants node alt1 (teal), node alt2 (amber), node alt3 (purple), node alt4 (rose) — e.g. inputs teal, processing blue, outputs amber.
    • Titles use class="node-label" (white, bold); secondary lines use class="node-sub" (light blue) — never invent your own fills/colors.
    • Edges: class="edge" with marker-end='url(#arrow)'; label them with class="edge-label" where the flow isn't obvious.
    • Size the viewBox to the content — ~20px of padding around the outermost shapes, no oceans of empty space. Nodes ~140×48 with ~60px horizontal gaps read well.
  • UI work gets mocks — always, no exceptions. Any decision about a screen, page, layout, component, or visual flow MUST present 2–3 alternative mocks as its options — the user picks a design by clicking a mock, not by imagining one from prose. Use option objects (see schema): {"label", "caption", "html", "width", "height"} for hi-fi HTML mocks (default — prefer these), or {"label", "caption", "viewBox", "svg"} for lo-fi SVG wireframes (fallback only). The deck renders html inline in a sandboxed, auto-scaled, zoomable iframe. Inline only — NEVER as files. Do not write mock .html/.png files to disk, do not send mocks as attachments, do not link out. If a visual is not inside the deck card, it does not count as delivered. Mock decisions automatically render in their own prominent "🎨 Design mocks" section near the top, expanded by default — you don't need to do anything beyond using option objects.

    • MOCKS MUST LOOK LIKE THE PRODUCT WE ALREADY HAVE — this is the rule that matters most. A mock that doesn't resemble the real app is worse than no mock: the user can't judge a layout that looks nothing like what they'll get. Before authoring any mock, the explore stage MUST have captured the app's actual look — its CSS custom properties / theme tokens (colors, radius, spacing), its real component classes (cards, tables, badges, buttons), and its font. Default to hi-fi HTML that pastes those real tokens into a self-contained <style> block and reuses the real class names and component shapes, so the mock is visually indistinguishable from a real screen of the app. If the app is dark, the mock is dark; if cards have a 1px --border and 8px radius, so does the mock. Populate with realistic sample data, not lorem/placeholders.
    • HTML mock authoring (the default path):
      • Self-contained <!doctype html> with an inline <style> — no external CSS, fonts, or scripts (the iframe is sandboxed and offline).
      • Copy the app's :root{--…} tokens verbatim and build with them; reuse the real component class names so the mock reads as the real UI.
      • Set width/height to a realistic screen size (e.g. 900×600); the deck scales it down to fit. Author at full size — don't pre-shrink.
      • Same width/height across the 2–3 alternatives so they compare like-for-like; vary only the layout being decided.
    • SVG wireframe fallback (only when there's no app look to match yet, e.g. a brand-new product with no styling): lo-fi and blocky on purpose — wf-frame for the screen/panel outline, wf-box for outlined regions, wf-fill/wf-accent for the regions being decided, wf-bar for text-line placeholders, wf-text for region labels, wf-note for small annotations. Only these classes — no custom colors. Sizing discipline is mandatory or text overflows the frame (the common failure): keep ALL shapes and text strictly inside the viewBox; leave 14px padding inside wf-frame; keep wf-text strings short (truncate, don't let them run past the box edge) and never start text near the left/right margin where it will clip. Same viewBox across the alternatives (0 0 200 140 for a screen, wider for dashboards).
    • Label every meaningful region so the differences between the 2–3 mocks are obvious at a glance; the caption states the tradeoff in one line ("denser, scales to 100 items" vs "calmer, 10 items max").
    • Mocks can sit alongside plain-string options on the same card (e.g. a third option "None of these — see my notes").
  • decisions[] — the forks. Any place the design could go more than one way: approach, library, boundary, what to defer, how much freedom the agent gets. Mark needs-you vs agent-call vs fyi. Use dependsOn to link a decision to the intents/decisions it hangs off — the deck shows the user their own answers on linked cards.
  • steps[] — numbered implementation steps, each tagged with the intent it serves. Steps are derived: when a round changes an intent or decision, update the steps to match — they reshape every round and only lock at finalize. Steps are now editable and deletable in the deck like any card: the user can rewrite a step's fields in place, strike it out (✕) to delete it, or add their own. The user's step edits are authoritative — an edited step comes back with userEdited: true and its full draft; keep the user's wording, don't silently regenerate or override it. A step struck out is gone (absent from the round). You may still re-derive other steps when an intent/decision changes, but leave the user's edited/added steps as they set them unless their own answers force a change.
  • finalVerify[] — the plan-level gate: for each intent, how the whole feature is proven end-to-end once built. Editable in the deck — the user can edit any row (intent / method / command / expected), add rows, or delete them. The edited rows ride back in the round as finalVerify[]; treat them as authoritative (keep the user's commands/expectations) and reconcile the plan's finalVerify to match. A new round reseeds the table from your updated plan.

Stage 4: Serve LIVE — answer questions in place, no restart

Default to live mode. The deck stays open and you stay in a loop beside it: the user can ask a question and get the answer streamed back into the same open page — no close, no re-serve. The plan file is the single source of truth; the page is a live view of it (SSE), and you keep it current.

  1. Serve in live mode — ALWAYS in the background:

    # ${CLAUDE_PLUGIN_ROOT} resolves to this skill's install dir (plugin install).
    # If you cloned manually instead, use that path, e.g. ~/.claude/skills/plan-html
    python3 "${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/skills/plan-html}/serve_plan.py" \
      --plan <abs-path>/design/plans/<slug>/plan.json --live

    Run it with run_in_background: true — never foreground. A foreground-blocking serve owns your turn, so the user can't type to you in the terminal until it yields. Background-serving keeps both channels open at once: the user types into the deck (autosaved + streamed to you) AND can still message you in the terminal. Poll the background server's output file for events between turns; don't block on it.

    Stable port for tunnels (--port). By default the server picks a random free port, so a cloudflared URL dies whenever the deck restarts. If the user is on a tunnel and you'll be restarting the deck (e.g. iterating on the template), pass --port <fixed> so the same tunnel URL keeps working across restarts. Otherwise omit it.

    Don't miss the final event. On Finalize/Send to agent the deck POSTs the round and the browser shows a "handed back to the agent — you can close this tab" page (it can't reliably close a user-opened tab, so the page IS the signal, not an auto-close). Because the server runs in the background, that round lands in the output file — actively pick it up. If a user says "I finalized but nothing happened," the round is in the server's stdout/log; read it and proceed.

    The deck opens; every user change autosaves to answers.json. The server prints a JSON line per event to stdout (it does NOT exit on the first one):

    • {"action":"ask","id","cardId","text"} — a live question (also queued in questions.json).
    • the full round object on Send to agent / Finalize plan.
    • {"action":"timeout",...} if the idle timeout hits.

    The interview (discovery only). Above the intents sits a panel with one button: "Interview me & redesign". It opens a popup in which you ask the user questions to understand what they are building and why, one at a time. The popup only collects; it never rebuilds. It is NOT a review of your own assumptions ("I assumed X, confirm?"), and it is NOT reading the cards back to them. Those are your problems to solve once you understand them; they become decision cards in the rebuilt deck.

    State lives on the plan and you are the only writer of it:

    "interview": {
      "state": "asking",          // idle | asking | done
      "max": 8,                   // rough cap shown to the user ("of about 8")
      "turns": [ {"id":"iv-1","q":"…","a":"…"} ],   // answered so far, in order
      "next": {"id":"iv-2","q":"…"},                 // the question on screen, or null
      "appliedRound": 2           // set when a round was rebuilt from these turns
    }

    Every user action arrives as an ask with cardId:"__interview__":

    textMeaningYou do
    [INTERVIEW START]user pressed the buttonset state:"asking", turns:[], write the first question into next
    anything else, with id = the current next.idthe answer to that questionappend {id,q,a} to turns; write the following question into next. When you could already write the goal in their words, make the next question a wrap-up ("Anything else I should know before I redesign?") rather than stopping on your own
    [INTERVIEW MORE]user wants more questions after doneset state:"asking" and write another next; keep turns
    [INTERVIEW DONE] (optionally followed by "Last answer (to: …)" and text)user closed the interviewappend the last answer if present, set next:null, state:"done", write. Do not rebuild now.

    After each of these: bump rev, write plan.json. The popup follows the plan over SSE; nothing else delivers your question.

    How to ask. One question at a time, plain words, each shaped by the previous answer. Dig into what they actually said; do not run a fixed list. Cover, in whatever order the conversation goes: what they want to be able to say or do when this is done; who it is for and who has to be convinced; what "done" and "wrong" look like; what they have already tried; what they will not spend (time, money, attention); what worries them. Around max questions, ask the wrap-up and let them press Done.

    The rebuild happens on the next round, not in the popup. When a send-round arrives and plan.interview.state == "done" with appliedRound missing or lower than the current round, regenerate the deck from the transcript plus the round's answers, not from the previous draft: rewrite goal and summary in their words; rewrite intents[] (each with a real verify); redraw boundaries[]; replace decisions[] with the forks the interview exposed (this is where your assumptions go, as needs-you cards); regenerate steps[], finalVerify[] and the plan-level diagram. Bump round. Give changed cards new ids so stale answers do not attach to them; keep ids only for cards that genuinely survived unchanged, and keep any user-edited or user-added card as the user set it. Keep interview.turns, set appliedRound to the new round. Say in the one-line round status that the plan was rebuilt from the interview.

    Serve with a long timeout when an interview is likely (--timeout 21600): the default 1800 s is a total wall-clock limit, not idle, and an interview can outlast it.

    Legacy: an ask with cardId:"__intents__" and [GRILL REQUEST], or cardId:"__plan__" and [REDESIGN REQUEST] (older decks), means: write pointed agentQuestions[] onto fuzzy intents, bump rev, write. No rebuild.

    Optional public tunnel (on demand). The deck binds 127.0.0.1 by default. If the user asks to share it / open it on their phone / "open a tunnel" while a deck is already running, start a cloudflared quick tunnel as a SEPARATE background process pointed at the live port — do NOT restart the server (that would not lose answers, but it's needless churn and changes the port):

    cloudflared tunnel --url http://localhost:<live-port> --no-autoupdate

    Grep its log for the https://<random>.trycloudflare.com URL and paste it to the user. The tunnel is ephemeral (URL changes each run, no account needed) and stays up until you stop the cloudflared process — stop it when the plan is done. (ngrok http <port> is an alternative if cloudflared isn't installed.)

  2. Arm a persistent Monitor the instant you serve — this is MANDATORY, not a poll. Every deck event (ask, clarification, finalize, send-round, timeout) arrives on the server's stdout the microsecond it happens. Do NOT poll the output file between turns — that is what makes a question sit "queued — no agent is watching." Instead, the moment the background server is up, attach a persistent Monitor to its output file so each event wakes you instantly, with zero polling — the same instant path finalize already rides:

    Monitor(persistent: true, command:
      tail -n0 -f <server-output-file> |
        grep -E --line-buffered '"action": ?"(ask|finalize|send-round|timeout)"')

    With the Monitor armed, asks, clarifications and finalize are all delivered one uniform way, instantly. The deck shows an "agent is replying…" spinner on Send; if no reply lands in ~25s it flips to "no agent is watching this deck right now" — with the Monitor running, that state should essentially never appear. If it does, your Monitor died: re-arm it.

    For each ask event — answer DURABLY (write the plan), then optionally paint fast (SSE push):

    • Find the card by cardId and append to its thread: {"role":"user","text":<their question>} then your {"role":"agent","text":<answer>}.
    • Apply any plan change the answer implies (revise options, edit an intent, add/remove a card, redraw a diagram, reorder).
    • Bump rev and write plan.json. This is the source of truth and the ONLY reliable delivery. The server pushes the new plan over SSE and the page reconciles in place; crucially, the server also re-sends the full plan on every SSE (re)connect — so an answer written to the plan survives a dropped mobile connection and reappears on reconnect. An answer that is not in the plan is lost if the user's stream blipped (common on phones/ tunnels). Never rely on the push alone.
    • Optional instant paint: you MAY also POST /answer {cardId, text} to stream the reply into the card a beat sooner. This is a nicety on top of the plan write, never a substitute — if you skip the plan write, a reconnect shows "still queued" forever even though you "answered."
    • Keep the Monitor armed until a send-round/finalize/timeout event.

    The user can keep working on other cards while you answer one — only the asked card shows a "thinking…" state. Don't block the whole plan on one question.

    If an ask lands while you're busy elsewhere: nothing is lost — it's queued in <plan-stem>.questions.json and the Monitor still fires. Pick it up; writing the plan delivers the answer (and heals any "queued" state) the moment the page next has the plan.

  3. On send-round / finalize / timeout — proceed exactly as the round rules below describe. (Live answers and rounds coexist: live handles "answer my question now"; a round handles "I've answered a batch, process them.")

Round handling (also used by live edits)

  1. Read the printed round ({"action", "round", "agentActions", "cards":[...], "finalVerify":[...]}). finalVerify carries the user's edited finish-line rows — reconcile your plan's finalVerify to match them.

    First check agentActions — the user's global requests for the next round:

    • reexplore: true — re-run the exploration agents before answering anything, then update affected cards (and say in one status line what changed in the code since last look).
    • note — free-text instruction for the next round; honor it.

    Cards arrive in the user's priority order (they drag-reorder by the ⠿ grip): cards[] is sorted top-first and each carries priority (0 = most important within its section). Build in that order; let it set what you tackle first.

    Struck-out cards are GONE — absent means removed. When the user strikes a card (the ✕) it is dropped from the round entirely: it will NOT appear in cards[]. So a card you sent last round that is missing this round was removed by the user — drop it from the plan, don't re-add it, don't re-ask it. (You lose the "why" — accept the user's call.)

    User-added cards arrive with userAdded: true and a draft. The user created this card in the browser and may have filled it in fully or partially (draft holds whatever they typed: title, intent/boundary text, verify, options, status, step fields). Promote it into the plan as a real card next round: keep the user's content, complete what's missing (write a proper verify for an added intent, draw a diagram if it has structure, normalize options for an added decision), and give it a stable id. Treat the user's draft as authoritative intent — fill gaps, don't override.

    Then, for each card:

    • choice — a button pick (approve / reject / agent / a custom option label / a mock label). null is fine if answer has text: free text is a full answer, treat it with the same weight as a button. (The user can pick any text choice straight from the collapsed card via inline quick-answer buttons — same choice either way.)
    • answer — the user's own words: detail, redirect, or a standalone answer. Always read it; it overrides/refines the button.
    • question — the user needs an answer before deciding. Answer it in the card's thread (see below), don't make them dig in the terminal.
    • thread — the card's Q&A history ([{role,text},…]), present only when the card was discussed. This is the reasoning behind the answer: the questions the user asked and the answers given. Read it before acting on choice/answer — a pick often only makes sense given the discussion, and on finalize this is the record of WHY each decision went the way it did.
    • edit — the user rewrote an intent or a boundary in place. Replace the card's text with their version (and adjust dependent decisions/steps/ verify if the rewrite changes scope). An edited boundary is the user re-drawing the fence — re-check every step still fits inside it.

    Keep the title in sync with the content. A card's title is just a short handle — when discussion, an edit, a question, or your own rework changes what a card is actually about, rewrite its title (and summary) to match. Don't leave a stale title describing the old meaning. This applies to intents, boundaries, decisions, and steps alike: if the content moved, the title moves with it. (The deck reconciles the new title live over SSE, so the user sees it update in place.)

  2. If action is "send-round" — iterate:
    • For every question, append to that card's thread: {"role":"user","text":<their question>} then {"role":"agent","text":<your answer>}.
    • Apply redirects: rejected decisions get reworked (new options or a changed design), edited intents get updated, ripple changes through steps and verify blocks.
    • Bump "round" by 1, write the updated plan.json, print one line saying what changed, and re-serve (step 1). The user's prior answers reload automatically; their delivered questions are cleared.
  3. If action is "finalize" — briefly restate (2–4 lines max) what was decided where it diverged from your defaults, then proceed to build (or hand to /dev-cycle). Do not re-dump the plan.
  4. If action is "timeout" (exit code 1) — the user stepped away. Their partial answers are in saved and on disk. Say so in one line and stop; on the next session, re-serve the same plan — nothing is lost.
  5. More questions after finalize? Re-open the deck — never ask in the terminal. Finalize locks the plan, but building often surfaces forks the plan didn't cover. When that happens: set "followUp": true, bump round, add ONLY the new needs-you cards (keep the locked sections for reference — don't re-open settled decisions), and re-serve. The deck shows a follow-up banner and the Finalize button becomes "Resume build" — in follow-up rounds only the new needs-you cards gate it; settled intents/boundaries stay locked and don't re-gate. (A follow-up intent or boundary can be marked "status": "needs-you" to gate again.) When the answers come back, continue building. Repeat as often as the build demands — a follow-up round is cheap; building on a guess isn't.

plan.json schema

{
  "title": "Feature title",
  "slug": "feature-slug",                       // stable id; also the dir name
  "task": "the original request",               // optional
  "date": "YYYY-MM-DD",                          // optional
  "round": 1,                                    // bump on every re-serve
  "rev": 0,                                       // bump on every LIVE write so the open deck reconciles
  "followUp": false,                             // true = post-finalize round: new questions from the build
  "summary": "1–2 sentence what-and-why (markdown ok)",
  "goal": "REQUIRED — one sentence: what we're building and why, shown as a banner",

  "boundaries": [                                // optional — the fence, shown first when present
    {
      "id": "boundary-scope",
      "title": "Only the skill files change",
      "boundary": "In scope: the three skill files. Out of scope: everything under `src/`. Do **not** add new dependencies. (markdown ok)",
      "thread": []                               // Q&A history, optional
    }
  ],

  "intents": [                                   // REQUIRED — the spine, replaces AC
    {
      "id": "intent-persist",
      "title": "Answers survive interruptions",  // short card title
      "intent": "I want every answer I give to survive a closed tab, a timeout, or a reboot. (markdown ok)",
      "verify": { "method": "e2e", "command": "…exact command…", "expected": "…observable result…" },
      "agentQuestions": [                          // optional — grill the user to sharpen this intent
        { "id": "scope", "q": "Does *every* answer include drag-order, or just card picks? (markdown ok)" }
      ],                                           // answers come back as grill:[{id,q,answer}] on the round
      "thread": [ {"role":"user","text":"…"}, {"role":"agent","text":"…"} ]  // Q&A history, optional
    }
  ],

  "discovered": [                                // optional — findings from exploring, shown after intents
    {
      "id": "found-ratelimiter",
      "title": "There's already a rate limiter",
      "summary": "middleware/throttle.py already does per-user limits",  // optional, shown on collapsed card
      "finding": "middleware/throttle.py already implements a token-bucket limiter per user. (markdown ok)",
      "impact": "The new endpoint should reuse it instead of adding a second one.",  // optional, markdown ok
      "source": "middleware/throttle.py",          // optional — where you found it
      "thread": []                                  // Q&A history, optional
    }
  ],

  "decisions": [
    {
      "id": "round-trip",
      "title": "How decisions return to the agent",
      "status": "needs-you",                     // needs-you | agent-call | fyi
      "summary": "one line shown on the collapsed card",
      "building": "what we're building for this decision (markdown ok)",
      "tradeoffs": ["**option A**: …", "**option B**: …"],   // optional, markdown ok
      "verify": "how we'll prove this was right",            // optional, string or {method,command,expected}
      "options": ["Blocking server", "Watched file"],        // optional; replaces Approve/Reject/Your-call
      // UI decisions: options are mock objects instead — the user clicks a mock to pick it.
      // These cards auto-render in the dedicated "Design mocks" section, expanded, zoomable.
      // PREFER hi-fi HTML that reuses the real app's theme tokens + component classes,
      // so the mock looks like the product. Same width/height across alternatives.
      // "options": [
      //   {"label":"Sidebar", "caption":"nav left, content right",
      //    "html":"<!doctype html><html><head><style>:root{/* paste app tokens */}…</style></head><body>…full mock reusing real classes…</body></html>",
      //    "width":900, "height":600},   // rendered inline, sandboxed, scaled, zoomable
      //   {"label":"Top tabs", "caption":"full-width content", "html":"…", "width":900, "height":600},
      //   // SVG wireframe is the lo-fi FALLBACK (only when there's no app look to match):
      //   {"label":"Lo-fi", "caption":"blocky wireframe", "viewBox":"0 0 200 140",
      //    "svg":"<rect class='wf-frame' x='4' y='4' width='192' height='132'/>…keep all text inside the box…"}
      // ],
      "dependsOn": ["intent-persist"],                       // optional; linked cards shown with the user's answers
      "thread": [],                                          // Q&A history, optional
      "diagramSvg": "<rect class='node' …/>…",               // optional per-card diagram (intents too)
      "diagramViewBox": "0 0 720 240"
    }
  ],

  "steps": [                                     // read-only reference
    { "title": "Step title", "description": "what it does (markdown ok)",
      "files": "path/to/file", "test": "e2e", "intent": "intent-persist" }
  ],

  "finalVerify": [                               // plan-level gate, one row per intent
    { "intent": "intent-persist", "method": "e2e",
      "command": "…exact command…", "expected": "…observable result…" }
  ],

  "diagramSvg": "<rect class='node' …/>…",       // REQUIRED — plan-level architecture/flow diagram
  "diagramViewBox": "0 0 720 240"                // optional
}

The deck also sends a global agentActions object with every round: {"reexplore": bool, "note": "free text"} — the user's standing panel for "re-explore the code before answering" and any other next-round instruction.

Notes:

  • All prose fields render markdown (bold, italic, code, fenced blocks, bullet lists, links) — format the plan properly; don't write flat text.
  • Boundary cards (when present), intent cards, and needs-you decisions gate Finalize; a card is satisfied by a button pick OR free text OR an in-place edit. Open questions also block Finalize (they must be answered in a round first). A boundary answered "Not needed — drop it" is removed from the next round's plan.
  • options reads better than Approve/Reject when the decision is a concrete pick between named approaches.
  • The diagram is hand-authored inline SVG (same node/edge classes as before). No mermaid, no CDN.

Rules

DO:

  • Run the same understand → explore → design stages as /plan; only the output surface differs.
  • Grill hard in round 1: every real assumption becomes a card; vague answers get re-asked sharper, never built on — but scale it to scope, and never invent questions to keep the loop alive.
  • Converge: each round shrinks the open set; when nothing real is left, recommend Finalize instead of another round.
  • Always include intents[] (one outcome + why + verify each) and a plan-level diagram; add boundaries[] whenever scope needs a fence.
  • Draw, don't describe: per-card diagrams for anything with structure, flow, or sequence — and 2–3 clickable wireframe mocks for every UI/layout decision, without being asked.
  • Honor agentActions every round — reexplore means re-run the explore agents before answering anything.
  • Treat agreed boundaries as binding: if a later round would cross one, raise it as a new decision — don't silently widen scope.
  • Surface every real fork as a decision with tradeoffs; link related cards with dependsOn.
  • Answer every user question in the card's thread on the next round.
  • Keep each card's title/summary in sync with its content — when the meaning changes, rewrite the title; never leave a stale handle.
  • Serve in the background (run_in_background: true) so the user can type to you in the terminal AND into the deck at the same time.
  • Start a cloudflared tunnel on demand (against the live port, no restart) when the user wants the deck public / on their phone.
  • Use uv run --directory to launch serve_plan.py (per CLAUDE.md).

DON'T:

  • Print the plan, findings, or decision lists as markdown in the terminal — the deck is the only plan surface.
  • Auto-advance stages without approval, or skip the understanding phase.
  • Write production/test code — that's the build phase's job.
  • Mark a real fork agent-call just to avoid asking — if it changes what you build and you have no clear default, it's needs-you.
  • Add external CDNs/scripts to the deck — it must open offline.
  • Write mock files to disk or send mocks as attachments/links — every visual renders inline in the deck (svg/html options, diagrams), full stop.
  • Discard or overwrite answers.json — it's the user's work.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.