Agent skills
The issue body
The schema rd-plan writes, rd-issues files, rd-build treats as done-or-not, and rd-retro scores against.
One schema, used by four skills. rd-plan writes it, rd-issues files it,
rd-build reads the acceptance lines as the definition of done, and rd-retro
scores a run against it. It is markdown, so it renders in the app, in radial show, and in a diff.
Use the smallest version that carries the contract. A body padded with empty headings teaches readers to skim, and the acceptance lines are the part nobody can afford to skim.
A parent
Three header lines first, nothing above them:
**Type:** feature
**Agent:** Claude Code
**Session ID:** 9f2c1a44-0c3e-4a5d-8b21-6f0d7e2b1c88
**Goal:** One sentence a stakeholder would recognise, describing the world
after this ships.
**Scope:**
- In: everything this tree covers.
- Out: what a reasonable reader would expect and will not get, and why.
**Plan:** How the children fit, the decisions already made, and the reason for
each. This is where a reader learns why it is three children and not one.
**Acceptance:**
- [boot] The repo's gate is green.
- [behavioral] A named person can do a named thing.
**Tags:** [ui, api]Type is feature, fix, chore, refactor or docs. Session ID makes
the planning conversation findable a month later, which is its only job;
unknown is an acceptable value and a missing line is not.
A child
**Goal:** One sentence. What exists after this child merges.
**Scope:**
- In: …
- Out: …
**Plan:** The approach, the key decision, the trap to avoid. Two to six
sentences. Name the pattern the repo already uses, so the builder follows it
instead of inventing a second one.
**Files:**
create: src/lib/skills/manifest.ts, src/lib/skills/manifest.test.ts
modify: src/app/developers/page.tsx
**UX:** The journey a person takes, including empty, loading and error states.
No human-visible surface? Write `n/a (no UI surface)` rather than deleting the
line, so a reader knows it was considered.
**Acceptance:**
- [integration] With the manifest unreachable, the page renders the degraded
hook table and no 500.
- [integration] A page whose frontmatter omits `section` fails the content
build, naming the file.
**Browse checks:**
1. Open `/docs/skills` → the sidebar shows the skills in two groups → click
"Install and update" → a Claude Code section and a Codex section appear,
each with a copy button.
2. At 390px wide → the sidebar is a disclosure above the article and nothing
overflows horizontally.
**Tags:** [ui]The rules that actually matter
An acceptance criterion is observable. It names an input and a result someone can see: a rendered string, a status code, a row in the database, an exit code. Never a function you intend to call. The test is whether a reviewer who has not read your diff could check the line. If not, rewrite it.
A bracket tag says how it is checked. [boot] the process starts and the
gate passes. [integration] two real parts meet. [behavioral] a person does
something and sees something. [perf] a number with a bound.
Browse checks are walkthroughs, not intentions. Each starts where the
reader starts, lists the clicks, and ends on a condition that is either on
screen or not. rd-build runs them verbatim before a child may move to in
review, so vagueness here becomes a false "done" later, which is the specific
failure the format exists to prevent.
The Out list is load-bearing. It records what you decided not to do, so
nobody re-litigates it in review and rd-retro can tell a deliberate omission
from a dropped ball. A plan that excludes nothing has not been thought about.
Write it for the reader who arrives late. The body is read by a teammate in six months and by an agent with no memory of the conversation you are having now. Neither has your context.
Note
rd-plan writes every body of one plan into a single file,
.radial/plans/<slug>.md, and rd-issues files each with
radial create --description-file. -d is inline text: passing it a path
stores the literal path as the description, which nobody notices until review.