plan-a-feature
SkillMediaBuilds a feature specification from scratch through a relentless, evidence-based interview that walks the design tree decision-by-decision, resolving dependencies as it goes. Use when the user wants to plan, design, scope, specify, or flesh out a new feature, capability, or system behavior before implementation. Produces a feature specification focused on system behaviors, not implementation detail. Does not refine or stress-test an existing plan — use iterative-plan-review. Does not document already-built features — use project-documentation. Does not design the contract for an interface — use design-an-api. Does not research open-ended options before there is a feature to specify — use research.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the plan-a-feature skill
What this skill tells your AI
The instructions your AI receives, as published by testdouble/han in han-planning/skills/plan-a-feature/SKILL.md and read by ahel’s review.
Project Context
- CLAUDE.md: !
find . -maxdepth 1 -name "CLAUDE.md" -type f - project-discovery.md: !
find . -maxdepth 3 -name "project-discovery.md" -type f - personal config directory: !
bash "${CLAUDE_PLUGIN_ROOT}/scripts/han-config-dir.sh" 2>/dev/null || echo "$HOME/.claude" - project .han/config.md: !
cat .han/config.md 2>/dev/null || echo ""
As your first action, use the Read tool on .han/config.md inside the personal config directory path above. A read
that returns no file is no personal configuration: continue silently. When that file or the project .han/config.md
probe supplies content, apply it per config-rule.md, which governs precedence
between the two files, relative-path resolution, and what to do with a file that reads but cannot be used.
Operating Principles
- Interview relentlessly, but explore first. If a question can be answered by reading the codebase, project docs, coding standards, ADRs, or existing feature specs — or by querying a read-only tool already available to this session that authoritatively answers it (for example a connected schema or data-source tool) — explore instead of asking. Only surface questions that genuinely require the user's judgment. The connected-tool path is gated on availability, not on a fresh judgment: use it only when such a read-only tool is actually permitted to this skill; if none is available, ask the user as today (see Step 4).
- Walk the design tree. Decisions have dependencies. Resolve foundational decisions first (what the feature does, who uses it, what outcome it produces). Then descend into dependent decisions (flow, states, edge cases, coordination points). Never ask a dependent question before its parent is settled.
- Recommend, then ask. For every question surfaced to the user, provide a recommended answer with rationale grounded in evidence (code, docs, conventions, or stated goals). The user can accept, redirect, or provide a nuanced response.
- Behavior, not implementation, in the spec. The specification captures WHAT the feature does, for WHOM, and WHY —
at a level a reader who has never opened the codebase can understand. Language primitives, file/line references,
function or class names, library mechanics, implementation patterns, and internal env/flag names DO NOT appear in
feature-specification.md. Product-level subsystem names ("events processing system", "backend service"), user-facing UI vocabulary (popover, modal, toast), URL paths, behavioral verbs, and user-observable states DO. Technology brand names generalize one level up (NATS → "events processing system"; PostgreSQL → "database"; Redis → "cache"). This rule is language-agnostic — it applies equally to Go, Rails, Node, Python, Swift, Kotlin, and frontend JavaScript code. Any examples given in references or templates are illustrative, not an exhaustive deny-list. - Load-bearing mechanics go in
feature-technical-notes.md, not the spec. When a mechanic is load-bearing for a behavior — meaning the behavioral commitment in the spec is only correct because of that mechanic (ordering, durability, consistency, visibility timing) — the behavioral consequence goes in the spec sentence, and the mechanic goes in aT#note linked inline from that sentence. The tech-notes file is LAZILY created — it exists only when at least one load-bearing mechanic qualified. Mechanics that are discoverable from the code repo (an existing pattern, an in-use library, a documented convention) do NOT belong in the tech-notes file either —plan-implementationwill find them from the code. Mechanics that do not affect observable behavior are pure implementation and belong in the implementation plan, not here. - YAGNI is a first-class operating principle. Apply the evidence-based YAGNI rule in
yagni-rule.md to every commitment the spec carries. An item with no accepted
evidence is demoted to
## Deferred (YAGNI)with its reopening trigger, never silently dropped and never silently kept. An item with evidence gets the simpler-version test. - Evidence quality is the companion principle. Apply evidence-rule.md alongside YAGNI. YAGNI gates inclusion; this one characterizes the quality of what each commitment rests on, through trust classes, the corroboration gate on web claims, and a distinct label for no evidence at any tier.
- The run stays inside the boundary it descends from. The skill records the work item's stated scope and exclusions before the interview, per planning-boundary-rule.md. Every commitment is checked against it, and anything the boundary excludes lands in a visible cut list, per scope-justification-rule.md.
- Visual material the user supplies is kept, and reaches every reviewer. Persist it beside the spec as it arrives, never at document-write time, and pass its paths in every reviewer's brief. The session context is the only copy until it reaches disk, and a compaction destroys it. The boundary rule owns the convention.
- Questions to the user arrive one at a time, led by the consequence. Per operator-escalation-rule.md. The opening confirmation turn is the one exception, and the one turn that carries more than one ask.
Plan a Feature
Step 1: Capture the Feature Request and Output Location
Read the user's argument and conversation context to extract the feature being planned. If the request is too thin to start (e.g., just "plan a feature"), ask the user for a one-to-two-sentence description of what the feature does and what outcome it produces — nothing else yet.
Resolve the output location:
- If the user specified a folder path, use it.
- Otherwise, propose a folder name of 3 to 5 words in kebab-case (e.g.,
docs/features/user-invite-flow/,docs/plans/bulk-export-jobs/). Prefer placing it under an existing documentation root discovered via CLAUDE.md's## Project Discoverysection,project-discovery.md, or Glob fallbacks (docs/features/,docs/plans/,docs/). - Confirm the folder name with the user before creating files. If the folder does not exist, create it.
Up to four files will be written. The primary spec lives at the root of {folder}/; the companion artifacts live in
{folder}/artifacts/ to keep the planning folder uncluttered:
{folder}/feature-specification.md— the primary behavioral spec. Always written.{folder}/artifacts/decision-log.md— the full decision history with rationale, evidence, and rejected alternatives. Always written.{folder}/artifacts/team-findings.md— review-team findings and how each was resolved. Always written.{folder}/artifacts/feature-technical-notes.md— load-bearing mechanics that were captured because they were needed to correctly specify a behavior. Lazily created — written only if at least oneT#qualifies during the interview (Step 4) or finding resolution (Step 7). If noT#qualifies, the file is never created and the spec contains noT#links.{folder}/artifacts/scope-boundary.md— the boundary record. Always written, by Step 1.5.
One more folder appears when the user supplies visual material:
{folder}/ui-designs/— the visual material itself, one file per item, named for the state it depicts.
Create the artifacts/ subfolder before writing the companion files if it does not already exist.
The files cross-reference each other. The main spec cites decisions with inline parenthetical links like
([D4](artifacts/decision-log.md#d4-invite-expiration-window)) and cites technical notes (when the file exists) with
inline parenthetical links like ([T3](artifacts/feature-technical-notes.md#t3-ack-ordering)). The decision log,
findings log, and tech-notes file (all siblings inside artifacts/) cross-link through Driven by findings: /
Linked technical notes: / Affected decisions: / Affected tech-notes: / Supports decisions: fields, and all
reference back into the spec with ../feature-specification.md paths.
Step 1.5: Read and Record the Scope Boundary
Read ../../references/planning-boundary-rule.md for the record's name, its sections, and the accepted visual-material file set. Establish the boundary before you discover anything or ask anything.
A record already exists at {folder}/artifacts/scope-boundary.md. Read it and use it. Do not re-ask anything it
answers, including the direction-of-travel question: a recorded answer of any kind is never re-asked.
No record exists. Identify the work item this feature descends from — a ticket, an issue, a pull request, or a written request the user typed — read it, and record its stated scope and exclusions word for word. When no work item exists, record that explicitly, along with the statement that the user's request is the only boundary this run has. The read does not traverse outward: a linked, sibling, or closed item is not scope evidence for the item in hand. There is no tool here that reads a tracker, so you will often be recording the user's own words; record which it was.
Then take one confirmation turn before Step 2 begins. It restates the recorded boundary in the user's own terms, names any visual material you kept, and asks the direction-of-travel question with its subjects named from the work item: are the specific things it named being deprecated, replaced, or migrated away from? This turn is a confirmation rather than an escalation, and the one turn that carries more than one ask. When the user hands you a work item that conflicts with the recorded one, surface the conflict here and ask which governs, rather than silently overwriting or trusting the record.
Persist every piece of visual material the user supplies into {folder}/ui-designs/ as it arrives, named for the state
each one depicts, and note each item into the record's Visual Material Received section as you keep it. When the host
never made an item reachable as a file, name which items you could not keep and ask for them through the single stop,
while they are still recoverable.
Source the explanation standard by invoking han-communication:explanation-guidance before you write the confirmation
turn, and again before any escalation or stop later in the run.
Step 2: Discover Before Asking
Before asking the user anything beyond the initial framing, explore the codebase and project documentation to gather context that will answer as many design-tree questions as possible. Use Glob and Grep to find:
- CLAUDE.md, AGENTS.md, and any
project-discovery.md— tech stack, constraints, conventions. - ADRs in
docs/adr/ordocs/architecture/decisions/— prior architectural decisions the feature must respect. - Coding standards in
docs/coding-standards/or.github/CODING_STANDARDS.md— rules the feature's design must align with. - Existing feature specifications or PRDs — tone, structure, level of detail the team expects.
- Code adjacent to what the feature touches — current behaviors, patterns, integration points.
A connected read-only tool that authoritatively answers a design-tree question counts as a source here, on the same terms the operating principles set: read-only, never writing or changing state.
Record what was found (file paths) and what was not found. Missing standards are themselves findings that inform the feature spec.
Step 3: Build the Design Tree
Enumerate the decisions the feature needs in dependency order. A decision is a question whose answer shapes behavior. Group them into tiers:
- Foundational — What is the feature? Who uses it? What outcome does it produce? What triggers it? What does "done" look like?
- Behavioral — What are the primary and alternate flows? What states does the feature move through? What coordinations between actors, services, or subsystems are involved?
- Boundary — What edge cases, failure modes, and rollback behaviors must be specified? What is explicitly out of scope? What does the system do when inputs are malformed, missing, or adversarial?
- Interaction — If there is a user interface or API surface, what is the interaction model? What affordances, feedback, and error states must exist?
Do not pre-populate the tree with implementation detail. Keep each node as a behavioral question with a candidate answer.
Step 4: Interview Loop — One Branch at a Time
For each decision in dependency order:
- Try to resolve it from evidence. Re-check the codebase, docs, standards, ADRs, and already-settled decisions. If a read-only tool that authoritatively answers the question is available to this session (a connected schema, data-source, or similar read-only tool) and permitted to this skill, query it before surfacing the question — the same answerable-from-a-source discipline already applied to static sources, extended to connected ones. Gate it on availability, not judgment: if such a tool is available, use it; if none is available (including because it is not permitted to this skill), ask the user as today. Keep it read-only — no writes, no state changes. If the answer is clear from evidence, record it in the spec with the evidence citation and move on — do not ask.
- If evidence is insufficient, draft a recommended answer. Ground the recommendation in whatever evidence is available (prior decisions, conventions, stated goals, user's framing). State the recommendation, the rationale, and the alternatives considered.
- Apply the YAGNI evidence test before surfacing. A decision that exists only for "completeness", "for future
flexibility", "we might want to", "best practice", or symmetry with another feature is a YAGNI candidate per
../../references/yagni-rule.md. When no accepted evidence (user-described need,
named direct dependency, existing code path, applicable regulation, documented incident/metric) supports the
decision, the recommended answer is "defer this to the spec's
## Deferred (YAGNI)section with the reopening trigger named" — surfaced to the user with rationale like any other recommendation. When evidence does support the decision, apply the simpler-version test: is there a strictly simpler behavior that satisfies the same evidence? If yes, recommend the simpler behavior. - Surface to the user only if the decision genuinely needs their judgment. Present the recommendation, rationale, and alternatives. Allow the user to accept, amend, or redirect. Capture their answer verbatim in the spec.
- Descend. Once a decision is settled, evaluate whether any dependent decisions are now resolvable from evidence (they often are). Repeat.
Keep the interview moving — do not stall on questions the evidence can answer. Do not batch every question upfront; ask as the tree unfolds, because later answers often resolve earlier uncertainties.
Routing implementation-level details
When settling a decision surfaces an implementation mechanic, classify it BEFORE writing the spec sentence and route
it per mechanic-routing.md: a mechanic that changes observable behavior becomes a
T# candidate, one already discoverable in the repo is cited as evidence on the D#, and anything else belongs to
plan-implementation and is not settled here.
T-note capture discipline (in-message accumulator)
The feature-technical-notes.md file is not written during Step 4 — it is flushed during Step 5 (or first written
during Step 7 if finding resolution produces the first qualifying note). During the interview, track candidates
in-message by stating them plainly as they are identified:
T-note candidate captured — T(pending #N): {short title}. Supports D{n}; section {spec section}; mechanic: {one-line summary}.
This makes the accumulator visible in the conversation history and gives the user a chance to redirect ("that's discoverable from code" / "not load-bearing") before the note is written. If the user redirects, drop the candidate from further consideration.
Candidates that later become irrelevant (e.g., a review specialist in Step 6 proves the mechanic is discoverable from code) do not reach disk — Step 5 re-validates every candidate against the routing rules before writing.
Step 5: Draft the Initial Feature Specification
Before drafting, invoke han-communication:readability-guidance to source the shared readability standard into your
context, then apply it as you write the prose sections, holding the named audience: the stakeholder or reviewer who reads
the spec for approval. The frame governs how a fact is said, never whether a required fact appears — keep the behavioral
precision the spec depends on.
Write the files. The primary spec goes at the root of {folder}/; the companion artifacts go in {folder}/artifacts/
(create that subfolder if it does not already exist):
-
{folder}/feature-specification.md— use feature-specification-template.md. This is the primary behavioral spec covering: The template defines every section and carries the rule for what may and may not appear in the file. Three of its sections have behavior the template cannot express:- Visual Reference — write it only when the run received visual material.
plan-work-itemsreads this table and the inline embed placements as its mapping source, so the exact heading text and the embed paths are a contract rather than a formatting choice. - Cut for Scope and Deferred (YAGNI) — both are lazily created. Omit either entirely when nothing qualifies. They sit adjacent and are the same shape, so each opens with one line saying what it is not. A cut carries no reopening trigger; a deferral does.
For every behavior that embodies a non-obvious decision, append an inline parenthetical link to the decision in
artifacts/decision-log.md, e.g.([D4](artifacts/decision-log.md#d4-invite-expiration-window)). Link only non-obvious behaviors — not every sentence. "Non-obvious" means a reader would reasonably ask "why this and not something else?"For every spec sentence whose correct behavior relies on a captured
T#note, append an inline parenthetical link to the note, e.g.([T3](artifacts/feature-technical-notes.md#t3-ack-ordering)). Link only sentences where the mechanic changes observable behavior — never as a gratuitous "see also" link.Apply the spec-content rule from the operating principles to every sentence before writing it. If a draft sentence names a language primitive, file/line, function or class, library mechanic, implementation pattern, or internal flag, rewrite it behaviorally before it reaches disk. Route the implementation detail to the appropriate home per Step 4's routing rules.
- Visual Reference — write it only when the run received visual material.
-
{folder}/artifacts/decision-log.md— use decision-log-template.md. Do not classify decisions as full or trivial yet. Write every decision with the full structured fields, under## Full decisions, and classify the whole set once in Step 8 after the review round returns. Two of the promotion signals, a driving finding and a linked technical note, cannot exist at draft time, so classifying now guarantees re-classification later. The D# counter is assigned here and stays stable through classification, so every spec inline link keeps resolving. TheDriven by findings:field is—in this draft; it is populated in Step 7 when review findings reshape decisions. -
{folder}/artifacts/team-findings.md— use team-findings-template.md. Write the header block; leave the findings list empty.F#entries are added in Step 7 after the review team returns. -
{folder}/artifacts/feature-technical-notes.md— use feature-technical-notes-template.md. This file is LAZILY created — write it only if at least one capturedT#candidate qualifies.Flush the in-message accumulator from Step 4:
- Review every T-note candidate captured during the interview.
- Re-validate each against the routing rules: load-bearing (affects observable behavior), not discoverable in the code repo.
- Drop candidates the user redirected or that no longer qualify after later evidence.
- Assign
T1..Tnin the order captured (not the order validated). - Write one entry per qualifying candidate with
Title,Context,Technical detail,Supports decisions:(D# IDs),Driven by findings:(—during initial draft), andReferenced in spec:(spec section headings). - For every D# whose behavior a T# supports, populate the D#'s
Linked technical notes:field with the T# IDs. - Add inline
([T#](artifacts/feature-technical-notes.md#t#-slug))links to the spec sentences each note supports.
If zero candidates qualify, do not create this file. The artifacts folder does not gain an empty or stub file. Every reference to
feature-technical-notes.mdin the other artifacts should be absent in this case.
Technical details (specific files, libraries, data shapes) appear only under Evidence: in
artifacts/decision-log.md or in Technical detail: entries in artifacts/feature-technical-notes.md — never as
behavioral statements in feature-specification.md.
Step 5.5: Classify Feature Size
Before dispatching the review team, classify the feature. Default to small. Start the classification at small and only escalate to medium or large when the signals below clearly require it. When a signal is borderline, stay at the smaller band. Use the signals already in the draft spec:
- Small (default) — single subsystem, no cross-service integration, no auth/PII surface, no data migration, behavioral surface fits in one tab/page or one API call.
- Medium — two to three subsystems, optional integration, may touch UX or rollout, may have a small auth surface.
- Large — cross-service, security-sensitive, data ownership shifts, multiple new coordinations, or the user explicitly requests full team review.
This size drives the team-size cap in Step 6:
| Size | Team cap | Rationale |
|---|---|---|
| Small | 2 (han-core:junior-developer + 1 chosen specialist) | Limited surface area; one domain specialist is usually enough. |
| Medium | 3 to 4 | Typical default; the historical cap. |
| Large | 4 to 5 | Reserved for plans where missed coverage is expensive. |
Size override. A non-empty $size wins: a band value skips the signal-based classification above, while dynamic
forces it even when a config sets a default band. When $size is empty and a config supplies default-swarm-size (per
config-rule.md), use that band and skip the classification. The team cap scales to
whichever size wins. State the chosen size, the recommended specialists, and the reason in one short message before
launching agents, naming which of the two config files supplied a band. If the user disagrees, accept their override of
the size, the specialists, or both.
Step 6: Dispatch the Review Team
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 263
- Forks
- 23
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
plan-a-feature- Source
- github.com/testdouble/han