Design
SkillFiles & storageUse before any creative work — features, components, behavior changes. Turns an idea into a validated spec with explicit tradeoffs and unknowns, and splits scope into multiple tasks if it's too large. Output is the `## Context` and `## Design` sections of the task file.
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 Design skill
What this skill tells your AI
The instructions your AI receives, as published by btseytlin/ultrapack in skills/udesign/SKILL.md and read by ahel’s review.
Turn an idea into a validated spec through collaborative dialogue. Output lives in docs/tasks/<slug>.md — ## Context, ## Design, ### Invariants (IV), ### Principles (PC), ### Assumptions (AS), ### Unknowns (UK) — with a TDD decision recorded. Nothing is planned or written until the user approves.
What design is
Design answers: given what we want, how should it work? Reason forward from the goal — if this were built right, what would it look like? — then distill into invariants and principles.
Explore how it works now and what's been tried (step 1) to inform that answer, never to constrain it. "We already have X, so our options are…" is banned — it anchors the ideal to the accident of what exists. Getting from today's code to the design is the Plan's job.
Context — the observations that motivate the design
## Context comes before ## Design in the task file and holds a short list of observations about how things are today: what exists, how it behaves, what hurts. It is the evidence; Design is the response to it. A reader should be able to look at the list and see why this task exists without having been in the room.
Distil it from the step-1 exploration. Each bullet is one observation, one sentence, checkable against the repo or the user's stated experience. Aim for 3–6; if a bullet doesn't push toward some part of the design, cut it.
State facts, not solutions — no "we should", no chosen approach. Anything you can't confirm is an assumption (AS) or an unknown (UK), not a context bullet.
The Goal — definition of done
Every task has one Goal: the observable end state that means the work is finished. Write it to the **Goal:** header and confirm it with the user as part of design approval. It is the definition of done — the task is not done until the workflow's goal-validation step confirms it, not when code merely verifies and reviews clean.
State it as an outcome, not an activity: "training runs end-to-end on the full converted dataset and loss matches the old format", not "convert the dataset". If confirming the Goal needs a step beyond the diff — a run at full scale, an expensive / remote job, or an outcome only visible in the user's environment — say so in the Goal, so verify and the done-gate know a local proxy isn't the real thing.
When to invoke
Before any creative work: new features, component builds, behavior changes, architectural moves. Even "simple" tasks — five minutes of design prevents hours of rework. Skip only when the task is trivial (typo, one-line fix) and the user has confirmed the skip.
Process
- Explore project context — how it works now, what's been tried, existing patterns, recent commits. Inform the ideal; don't let current state constrain it. No exceptions. Record incidental code smells you pass — if one is in scope or an easy win, note it for the plan to fix, else add it to
## Code smells(see_principles.md→ Incidental code smells). - Scope check — split into multiple tasks now if the ask is too large.
- Ask clarifying questions, one at a time. Prefer multiple choice.
- Propose 2–3 approaches. Each with explicit tradeoffs and unknowns.
- Backwards-compat check — flag anything that could break already-running or already-used systems. Ask the user how to resolve before proceeding.
- Present the design in sections. Get per-section approval.
- Identify invariants (IV), principles (PC), assumptions (AS), and unknowns (UK).
- Decide TDD — yes or no, with reason. Use
up:test-driven-development's applicability rule. - Write to task file — set the
**Goal:**header (the definition of done — see below), then## Context,## Design,### Invariants,### Principles,### Assumptions,### Unknowns. - Self-review for placeholders, contradictions, scope, ambiguity. Fix inline.
- Wait for user approval before invoking
up:uplan.
Scope check — split before planning
If the ask spans multiple independent subsystems, stop and propose a split. Each piece gets its own task file. We work on one in this dialogue; the rest wait.
Agent: "That's three independent tasks. I'd split into:
docs/tasks/add-auth.mddocs/tasks/add-billing.mddocs/tasks/add-admin-dashboard.md
Each is a separate task file, designed and built in its own session. Which one should we start with?"
A good test: can a plan for this piece produce working, testable software on its own? If no, it's too big.
Proposing approaches — tradeoffs and unknowns, always
For every option, state:
- What it is: 1-2 sentences
- Tradeoffs: what you gain, what you give up (cost, complexity, flexibility, reversibility)
- Unknowns: what you can't answer without more info or experiment
End with a recommendation and why. Don't hedge on the recommendation — if the tradeoffs don't settle it cleanly, say that explicitly and ask the user to weigh in.
Option B: In-memory token bucket per pod.
- Tradeoff: zero new infra, simpler code. Loses limits on pod restart; uneven limits across horizontally-scaled pods.
- Unknown: how often pods cycle — if it's every 10 minutes, users see limit resets.
Option C: Postgres-backed counter with short TTL.
- Tradeoff: uses existing DB; no new infra. DB write per request is expensive at our RPS.
- Unknown: whether our DB can absorb the extra write load — need a napkin calc.
Recommendation: B for now, revisit when we outgrow it. The redis latency unknown (A) and write-load unknown (C) both need measurement work before committing, and B is cheap to replace."
Backwards compatibility — flag breaks loudly
If the task is greenfield (no existing consumers), say so in one line and move on. Don't invent risks.
ID conventions — define once, reference by ID
Entities in the task file are assigned short IDs so later sections (Plan, Verify, Conclusion) can reference them without re-quoting full sentences.
Design owns four entity types:
- IV1, IV2, … — Invariants
- PC1, PC2, … — Principles
- AS1, AS2, … — Assumptions
- UK1, UK2, … — Unknowns
Rules:
- Defined once with a full sentence at first appearance; later mentions are ID-only.
- Max one sentence per definition.
- Numbering is scoped to the task file. The same ID can recur across tasks with different meanings.
- IDs are for persisted artifacts (task file, commit messages, agent-to-agent prompts). When talking to the user in chat, expand the ID — write out the invariant, principle, or assumption in plain English. "IV3 was violated" is fine in the task file; to the user say "the invariant that Dataset must not import from training/ was violated". Mentioning the ID alongside is OK; replacing the content with just the ID is not.
Plan owns PH (phases) and RK (risks). Verify owns CK (checks). Those are introduced in their stage's skill.
Identifying invariants, principles, assumptions, unknowns
Not principles: "prefer composition" (too vague without "over inheritance"). "Be consistent." "Write clean code."
Assumptions vs invariants: an IV is something the code guarantees. An AS is something the world is assumed to give you. If the code can enforce it, it's an IV; if it depends on something outside your control, it's an AS.
TDD decision
Invoke the applicability rule from up:test-driven-development. Record in Design:
TDD: yes
or
TDD: no (reason: one-off migration script; no reusable logic)
Task-file output shape
## Context
- <observation about how things are now that motivates the design>
- <...>
## Design
<purpose, scope, chosen approach, key decisions, tradeoffs that settled it>
<TDD: yes|no (reason)>
### Invariants
- IV1 — <specific thing that must hold>
- IV2 — <...>
### Principles
- PC1 — <softer guidance — concrete enough to check>
- PC2 — <...>
### Assumptions
- AS1 — <unverified premise the design rests on>
- AS2 — <...>
### Unknowns
- UK1 — <open question left to plan / execute>
- UK2 — <...>
Rules
- One question per message. No batching. (Hands-off: ask only when genuinely blocking; prefer conservative defaults logged in the task file.)
- YAGNI ruthlessly. Cut anything not needed for the stated goal.
- Follow existing patterns. Targeted improvements only if they serve this task.
- Isolation. Units with one clear purpose; interfaces understandable without reading internals.
- No code yet. Design's output is words, not code.
- Omit empty subsections.
## Context,### Invariants,### Principles,### Assumptions,### Unknownsare pre-seeded by the/up:maketemplate. Delete any that end up with no entries — never leave a placeholder like<empty>, "none", or "n/a". See_brevity.mdprinciple 1.
Hands-off mode
See up:handsoff for the full contract. Stage-specific delta: Design is still the one interactive stage — run the full process. The only relaxation is "one question per message" → "ask only when genuinely blocking; prefer a conservative default". Log each defaulted answer as - udesign: <what> — <rationale> in ### Hands-off decisions; log no-default gaps under ### Deferred (needs user input).
Terminal state
User has approved the Context and Design sections and the **Goal:** header (interactive) or both have been written and self-reviewed (hands-off) → invoke up:uplan. Do not write code. Do not invoke any other skill.
Signals
- GitHub stars
- 47
- Forks
- 12
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
udesign- Source
- github.com/btseytlin/ultrapack