Plan

SkillFiles & storage

Use after design to turn a validated spec into a lean implementation plan. Outputs the `## Plan` section of the task file — files, line numbers, class/method names, invariants, test strategy, order. Ends with a scope-creep / simpler-way check.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Plan skill

What this skill tells your AI

The instructions your AI receives, as published by btseytlin/ultrapack in skills/uplan/SKILL.md and read by ahel’s review.

Convert the approved Design into a lean, concrete implementation plan. Output fills the ## Plan section of docs/tasks/<slug>.md. The plan is the contract that up:uexecute will follow.

What a plan is

Plan answers: how do we get from where we are now to the Design's vision? It's the delta — the concrete path from current code to the approved spec. Design owns what it should be; Plan owns how we get there from here.

A plan is the minimum information an engineer needs to implement the Design without re-doing the thinking. That means:

  • Concrete locations: exact file paths, line number ranges, class/method/interface names. "Modify parser.py" is not enough — parser.py:120-160, Parser.tokenize() is.
  • Per-file change bullets: what changes in each file, by name. Not prose.
  • Interface signatures: new or changed function/method signatures. No bodies.
  • Phases with commits: ordered phases, one commit per phase minimum.
  • Test strategy: the behaviors to cover, not test code.
  • Snippets only where words fail: one regex, one tricky algorithm, one non-obvious API call. Never full implementations.

If an engineer could read your plan and implement the wrong thing, the plan is incomplete. If they need to re-derive the Design to understand the plan, the plan is bloated.

Scope is decided in design, not here

By the time you plan, the task's scope is fixed. If you discover a subsystem-split issue now, it means design missed it — stop planning, go back to the user, and decide whether to re-open design or keep the current scope.

Do not silently split the task here. Do not silently expand it either.

Output

Fill ## Plan in docs/tasks/<slug>.md. Do not create a separate plan file.

Brevity

Process

Required contents

Required always:

  • Approach: 1-2 sentences on the strategy and why it fits the Design
  • File structure: for each file, path/to/file.ext:lineA-lineB (create|modify) + affected class/method names
  • Per-file bullets: what changes, by name
  • New/changed interfaces: signatures only
  • IV/PC/AS referenced by ID: which Design entities each phase preserves or relies on

ID conventions in Plan

Plan introduces two entity types, numbered within the task file:

  • PH1, PH2, … — Phases. Each phase heading is ### PH<N> — <name>.
  • RK1, RK2, … — Risks. Each is one sentence.

References to Design entities use IDs (IV3, AS1, UK2) — never re-quote the full sentence.

Required when relevant (omit the subsection when it would say "single phase, no deps", "none", or similar):

  • Test strategy: behaviors to cover. If TDD: yes, list the failing tests to write first.
  • Order + dependencies: phases, what blocks what
  • Open questions / risks / rollback: what could go wrong, how to back out
  • Interfaces: declare cross-phase contracts when ≥2 phases share a signature / anchor / API shape. Omit for single-phase plans.
  • Interface graph: declare phase shape when ≥2 phases exist. Omit for single-phase plans.

Optional:

  • Code snippets: only for the most critical component per phase. If tempted to include full code, you're over-planning.

Format

## Plan

Approach: <1-2 sentences>

### PH1 — <name>

- **1.1** `path/to/file.ext:lineA-lineB` (create|modify)
  - `ClassName.method_name(arg: Type) -> Ret` — <what changes, by name>
  - Respects: IV2, AS1
- **1.2** ...
- Commit: `<message>`

### PH2 — <name>
...

### Test strategy   (optional — omit if no tests / doc-only)
<behavior list, per file or per phase>

### Order & dependencies   (optional — omit for single-phase or obviously-sequential plans)
<what blocks what, parallelizable phases>

### Risks / rollback   (optional — omit if none non-trivial)
- RK1 — <one-sentence risk and mitigation>
- RK2 — <...>

### Interfaces   (optional — omit for single-phase plans)
- IF1 — `<signature>` — <contract sentence>
- IF2 [blocks] — `<signature>` — <contract sentence> (consumer must wait for producer; see "Blocking interfaces" below)

### Interface graph   (optional — omit for single-phase plans)
- PH1              -> IF1, IF2   @ skills/foo/SKILL.md
- PH2  IF1         -> IF3        @ skills/bar/SKILL.md
- PH3  IF2, IF3 ->               @ agents/baz/AGENT.md

When to declare interfaces

  • An interface is a cross-phase contract: a function/method signature, a shared SKILL.md section anchor, or any API shape that one phase produces and another phase consumes.
  • The consume arrow (IF<N> ->) means runtime coupling — one phase's output is the other phase's input. Doc-only phases that merely reference a prior phase's plan text are sources, not consumers; leave the consume side empty (the line starts with PH<N> -> <produces>).
  • @ <paths> marks the filesystem boundary for each phase. Paths declared by phases in the same wave must be disjoint; up:uexecute uses this to detect boundary violations after each commit.
  • Waves are derived from the graph by topo-sort over [blocks] edges only — non-blocking IF edges (the default) do not create wave boundaries, since the IF declaration is sufficient context for the consumer. Phases linked only by non-blocking IFs land in the same wave and run in parallel. Do not hand-declare waves; declare the graph and the blocking flags, and let the executor derive them.
  • Omit both ### Interfaces and ### Interface graph for single-phase plans.

Blocking interfaces

By default, an IF is non-blocking: its declared signature is the contract, and a consumer can be implemented in parallel with the producer (the wiring check reconciles any drift after the wave). Mark an IF [blocks] when:

  • The consumer needs the producer's actual output, not just the signature — e.g. a generated config file, an applied DB migration, a fixture that must exist on disk, a doc anchor that must resolve at lint time.
  • The producer is large, risky, or critical enough that the planner does not want dependent work running concurrently — better to land it, verify it, then build on top. Planner/dispatcher discretion.

Use [blocks] sparingly — every block reduces parallelism. The default of non-blocking is correct for ordinary code interfaces (function signatures, class shapes, SKILL.md anchors) where the IF declaration carries the contract.

Self-review (inline, no subagent)

Fix issues inline. No re-review loop.

Backwards-compat check — restate the design's risks in plan terms

If the task is greenfield, skip this check explicitly in one line.

Default principles

Final check — scope creep, elegance, simpler way

  1. Scope creep: is every phase directly serving the Design, or have I snuck in "while I'm here" refactors, extra validation layers, speculative generality?
  2. Elegance: can any pair of phases merge? Are there accidental duplicate data paths? Does the plan introduce abstractions that aren't pulling their weight?
  3. Simpler way: is there a one-paragraph alternative plan that gets 90% of the value with 30% of the work? If yes, propose it to the user as an option.

If this check surfaces real simplifications, rewrite the plan. Don't stack warnings on top of a bloated plan.

Hands-off mode

See up:handsoff for the full contract. Stage-specific delta: skip the approval wait — present the plan highlights, log - uplan: plan auto-approved to ### Hands-off decisions, invoke up:uexecute directly. Self-review, scope-creep check, and backwards-compat restatement are unchanged. Ambiguities that would require a user call go to ### Deferred (needs user input) and the plan stops — do not guess around them.

Terminal state

Plan written, self-reviewed, scope-checked. Interactive: present highlights, wait for approval, invoke up:uexecute. Hands-off: present highlights, log auto-approval, invoke up:uexecute.

Signals

GitHub stars
47
Forks
12
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
uplan
Source
github.com/btseytlin/ultrapack