Imported from Evalir/dotfiles (
agents/skills/plan-spec/SKILL.md). Install upstream withnpx skills add Evalir/dotfiles --skill plan-spec. Copyright stays with the author.
plan-spec
The shared contract. Every plan-* skill loads this before doing anything.
Layout
Plans live in one private repo, Evalir/plans, cloned at ~/dev/evalir/plans. One
directory per code repo, named after it:
~/dev/evalir/plans/
README.md # what this repo is; hand-written
spellbook/
README.md # this repo's index; plan-status regenerates it
drafts/ # captured, not yet thought through
next/ # written and approved by you; ready to dispatch
open/ # a worktree exists; PR open or being built
done/ # PR merged
discarded/ # killed, with the reason kept
baseline/ …
Each code repo reaches its own tree through a symlink, ignored per clone, so every command in these skills works from inside the code repo unchanged:
<code repo>/.claude/plans -> ~/dev/evalir/plans/<repo>
Worktrees stay in the code repo at .claude/worktrees/<slug>/. A plan in open/ owns
exactly one worktree with the same slug.
The plans repo is committed and pushed — that is what lets another machine or a cloud
session read it. plan-status and plan-sync end by committing and pushing it.
Where you are
The plans repo is always Evalir/plans — git@github.com:Evalir/plans.git, cloned
at ~/dev/evalir/plans. Nothing else is ever the plans repo.
- Inside a code repo: paths are
.claude/plans/…;ghinfers the repo. Before any write, confirm the mount:readlink .claude/plansmust point into~/dev/evalir/plans. If it does not, stop and runplan-init. Never create.claude/plansas a plain directory — that makes a second tree nobody else can see. - Inside the plans repo — a cloud session opened on
Evalir/plans, or a machine without the code checked out: paths are<repo>/…, and everyghcall takes-R <owner/repo>from the plan'srepo:field.plan-statusandplan-syncwork here in full.plan-writeandplan-dispatchneed the code; run them from the code repo.
Filename
<scope>-<slug>.md, where <scope> is the conventional-commit scope you would put on
the commit — a crate, component, or surface name. No scope for a repo-wide change.
Example: auth-token-refresh.md.
Plan file
Frontmatter is machine-read by plan-status and plan-sync. Leave a field blank
rather than guessing.
---
repo: owner/name # the code repo this plan changes
scope: auth
status: draft | next | open | done | discarded
appetite: hours | days | a week | longer
worktree: # .claude/worktrees/<slug>; blank for a cloud dispatch
branch: # fix/<slug>, feat/<slug>, refactor/<slug>, ci/<slug>, chore/<slug>
pr: # 123
issue: # the tracking issue, when there is one
stacked-on: # PR number below this one in the stack
blocked-by: # owner/repo#N that must merge first, or a person
slack: # thread permalink
---
# <the commit subject this plan will produce>
## Problem
What is wrong, observed. Name where you saw it — a client, a log line, a host.
## Change
What you will do. Bullets, one per moving part, each naming the file or type it lands in.
## Checks
The exact commands, copied from the repo's own gate — see below.
## Verify live
How you will see it work outside the test suite. A green suite is not done.
## Not in this PR
The follow-ups you are deliberately leaving. Keeps scope from creeping.
## Log
Appended as work happens. Dates, PR numbers, what a review found.
The body sections are the PR body. plan-dispatch hands them over almost verbatim —
and for a cloud agent, the body is the task, so it must stand on its own.
Checks come from the repo
Never write a check from memory. In order: the repo's AGENTS.md or CLAUDE.md; its
Makefile or justfile (check, verify, lint); the CI workflow files, copying what
the jobs run; and only then the toolchain default — for Rust, cargo +nightly fmt --all --check, cargo clippy --workspace --all-targets --all-features --locked -- -D warnings,
cargo test --workspace. A suite that needs Docker, a cluster, or cloud credentials runs
only when the plan names it in ## Checks.
House rules
- Always start from an updated main.
git fetch originand rebase before any worktree, and again before every dispatch. This is the correction you have made most often; do not make it again. - Never force-push a branch you did not create. Teammates push to shared branches.
Check
git log --format='%an' origin/<branch> | sort -ufirst. - Plan before code. A plan is read and approved before a worktree exists. No skill
writes code during
plan-write. - Stack, don't pile.
gh stackis installed; use it wherever the host has stacked PRs enabled. Split anything over ~400 lines into a stack. - Tests earn their place. No test that exercises a dependency, the language, or coverage another test already has.
- Run the suite once, at the end. Not between every edit.
- Fan out only when the work is genuinely parallel, and cap it: at most 3 subagents, 1 per PR. Serial work with parallel agents wastes tokens.
- Shell snippets are fish-compatible. Use
env VAR=x cmd, notVAR=x cmd. - Succinct. Short sentences. No paragraph where a bullet works.
- Delete nothing you were not asked to delete.
Commit and PR format
- Match the repo's own log:
git log --oneline -20before the first commit. Work repos (the init4tech org) and some personal ones use conventional commits with a lowercase subject —fix(auth): refresh the token before it expires, not after; squash-merge appends(#NNN). Other personal repos use a plain imperative sentence, capitalized, no prefix —Refresh the token before it expires. The log decides. - Branch:
<type>/<slug>. PR bodies open with the ordering constraint when one exists ("Stacked on #N.", "Follow-up to #N.", "Merge only after …"). - Let the harness append its own attribution trailer, if it has one. Never hand-write a trailer naming a tool that did not do the work.