PlanGate — terminal-native plan-review loop
Batch the decisions into a reviewable plan file instead of forcing the user through
one-by-one chat. The user reviews in vim; their :w is the signal; you resolve and
revise; no code until it's agreed. Full background & prior art: see the repo README.
Review markers (the contract). Everything the user reviews is markdown blockquotes:
> Q:— a question / decision / anything needing the user's input. Whenever you write a> Q:line, put an empty> A:line directly under it so the user jumps in and types the answer with no marker typing (their vim has]ato jump to the next empty> A:and start typing).> A:— the answer. The user fills the empty slot; you fill it when replying to a> Q:the user raised.- An open item = an empty
> A:slot (^\s*> A:\s*$), plus any> Q:the user added with no> A:under it. The loop is done when every> Q:has a non-empty> A:beneath it. - Blockquotes keep the plan valid markdown (renders fine in Obsidian/GitHub) and — with the bundled
vim reading setup (see README) — highlight
> Q:vs> A:, with]q/[qto page between open questions.
Helper scripts live beside this file:
open-plan.sh <file>— open/focus the plan in a right-side vim pane (its own pane; never hijacks the user's working vim).wait-save.sh <file> [max_s]— block until the user saves; run it in the BACKGROUND so their:wre-invokes you.nudge-reload.sh— after you edit the plan, refresh the user's vim (:checktime+ autoread) to show your> A:answers.prune-plans.sh <plans-dir> [days]— delete plan files untouched for N days (default 7). Run it once per plan, at step 2.
When to fire
- 2+ open decisions/tradeoffs, or multiple implementation steps → propose the loop.
- One simple/single-answer question → just answer, no plan.
- If the user says "just answer" / "skip the plan" → drop the loop for that task.
Loop
Scope it. Feature comes from the
/plangate <description>argument if given, else infer it from the conversation and confirm your one-line summary before writing.Pick the path, then prune. In a git repo →
.plans/<slug>.mdat the repo root; otherwise~/.plans/<slug>.md.<slug>is a short kebab-case name. First time you create a plan in a git repo, add.plans/to its.gitignore(and mention it). Then runprune-plans.sh <plans-dir>before writing the new plan — it deletes plans untouched for 7 days (finished or abandoned; an in-review plan's mtime is bumped by every:w, so it survives). Mention anything it pruned. Override withPLAN_RETENTION_DAYS.Write the plan — a design doc and a decision surface, with decisions up front. The reader asked for this plan minutes ago and reviews it fresh — they already know the goal, so don't re-explain the ask or pad with background. Spend words on what they can't already know: the decisions, the open questions, and non-obvious design findings.
- Order:
Goal (≤3 lines) → Decisions → Open questions → Approach → Steps → Risks / unknowns. Keep a terse Goal on top for orientation, then hit the decisions immediately — don't bury them under a long exposition. Approach / Steps / Risks still carry the full design below. - Context budget: restating what the user asked for = cut. A background fact earns its place only if it's non-obvious and bears on a decision — put it inside that decision block, not a standalone exposition section. Findings that shape the whole design can live in Approach.
- Open the file with a short header telling the user how to review (markers below).
- Pose every real choice as a decision block (the checkbox / "input" pattern):
Options are one-line tradeoffs, not paragraphs; always give a recommendation and leave the empty### Decision: <title> - [ ] Option A — one-line tradeoff - [ ] Option B — one-line tradeoff > Q: pick one (check the box) or comment. Recommend A because… > A:> A:line so the user jumps straight to typing. - Genuinely leave decisions open — don't pre-resolve to avoid review.
- Order:
Open it: run
open-plan.sh <path>.Wait for review: launch
wait-save.sh <path>in the BACKGROUND. The user's:wwill fire it and re-invoke you. (Typing "reviewed" is a manual fallback.)Resolve (each round). On the save signal, re-read the file and find open items —
grep -nE '^\s*> A:\s*$'(empty answer slots) plus any> Q:the user added with no> A:below:- The user's
> A:text answers your questions; their> Q:lines are new comments/instructions (> Q: DELETE …= cut that scope,> Q: REPLACE: …= proposed change). Apply ticked[x]decisions. - Revise the plan inline. Fill every
> A:you owe (answering a user> Q:), and for anything still needing the user, add a fresh> Q:+ empty> A:pair. Leave the user's answered> Q:/> A:exchanges in place as the record. - Run
nudge-reload.shso their vim shows your changes. - Post a short chat summary of what changed — inline edits are invisible in chat.
- Re-launch
wait-save.shin the background for the next round. Repeat until no empty> A:slots remain, every> Q:has an answer, and no decisions are unticked.
- The user's
Gate. Do NOT write code or make implementation edits until BOTH: no open items (every
> Q:has a non-empty> A:, no empty slots) AND the user explicitly approves (e.g. "approved", "go build it", "lgtm"). If the user wants to bypass planning for a quick change, honor "just answer".Build. Once gated through, implement against the agreed plan.
Notes
- The
> Q:/> A:markers and decision blocks are the whole contract — keep them consistent, and always pair a> Q:with an empty> A:beneath it. - The save-signal means the user has no unsaved changes at resolve time, so
nudge-reload.shrefreshes safely;:checktimewon't clobber if they've started editing again. - If
wait-save.shreportsTIMEOUT, the review is just still open — re-launch it or check in. - Portability: the scripts are best in tmux + vim/nvim on macOS or Linux. Outside tmux,
open-plan.shprints the path for you to open manually andnudge-reload.shis a silent no-op;wait-save.shusesentrwhen installed (event-driven) and otherwise falls back to mtime polling — either way it works in any editor that writes to disk.