Skip to content
Back to skills

To Spec

ASecurity

Capture load-bearing technical decisions into a standalone `.task/spec/<slug>.md` — Decision/Rationale/Constrains sections cited via `Spec:`.

  • 7 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added August 30, 2026
ai-agentsgobash

Security analysis

A100/100

Scanned October 4, 2026

npx -y skills add SpaiR/task-pipeline --skill to-spec --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of To Spec?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for To Spec
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/spair-to-spec/badge)](https://www.skillsdirectory.com/skills/spair-to-spec)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: to-spec
description: 'Capture load-bearing technical decisions into a standalone `.task/spec/<slug>.md` — Decision/Rationale/Constrains sections cited via `Spec:`.'
argument-hint: '[decision area]'
disable-model-invocation: true
user-invocable: true
allowed-tools: 'Bash(bash *skills/_lib/preflight.sh* *) Bash(bash *skills/_lib/write-task.sh* *) Bash(bash *skills/_lib/roadmap-items.sh* *) Bash(bash *skills/_lib/detect-project.sh* *) Bash(bash *skills/validate/validate.sh* *)'
---

Fix **load-bearing technical decisions** — a protocol, a cross-cutting data shape, a "we picked X over Y because…" whose reasoning wouldn't survive re-derivation — into `.task/spec/<slug>.md`. Unlike `to-task` / `to-roadmap`, a spec does not decompose work; it pins the decisions that work must honor. A task or roadmap references it via a `Spec: [<slug>](../spec/<slug>.md)` header, and the executing session reads it as a fixed anchor (per `.task/CLAUDE.md` → `## Executing a task`, which its `## Execution` pointer names). One spec may be cited by many tasks and roadmaps, and can be captured before any exist.

**Input:** `$ARGUMENTS` — a rough description of the decision area, or a reference back to a prior discussion in this conversation ("write a spec from what we settled").

**Format contract:** [docs/contract.md § Spec file format](../../docs/contract.md#spec-file-format-taskspecslugmd) is the single source of truth for the output structure. This file describes the authoring flow that produces it.

## Instructions

### Step 0: Setup gate

The entry state, gathered before this skill reached you — no tool call of your own:

!`bash "${CLAUDE_PLUGIN_ROOT}/skills/_lib/preflight.sh" capture`

[docs/contract.md § Helpers](../../docs/contract.md#helpers) owns that block's shape. Read it, then act:

1. `AI_DIR:` is the `.task` directory: `.task/spec/<slug>.md` below means `$AI_DIR/spec/<slug>.md`, **never a cwd-relative path** ([contract § Setup-gate categories](../../docs/contract.md#setup-gate-categories)).
2. **`CONFIG: absent`** → this skill is intake-capable: read `${CLAUDE_PLUGIN_ROOT}/skills/_lib/setup.md` and follow it — it owns the sub-steps and the `.task/CLAUDE.md` template — then continue.
3. **`CONFIG: present`** → leave the file untouched: never rewrite it, re-detect its values or "repair" a missing section ([contract § `.task/CLAUDE.md` format](../../docs/contract.md#taskclaudemd-format)).
4. `SPECS:` lists the specs that already exist — Step 1 matches structural style against them and avoids re-pinning a decision one of them already carries; Step 4's slug-collision check reads the same list. Neither needs a listing call of its own.

If that block arrived unexpanded — the command line itself rather than its output — the preprocessing did not fire: run that command yourself and continue exactly as above.

### Preconditions

- **No real decision to pin** — only behavioral outcomes, or details local to one task → **stop and suggest** `/task:to-task`. Say plainly that nothing was written, with the reason: "Nothing here is a cross-task technical anchor — these are outcomes local to one task. Nothing was written. `→ Next: \`/task:to-task\` to capture it as a task with a plan.`"
- **Only an initiative's technical shape** — which components it builds, how its items connect, a module layout — with no choice whose reasoning must survive → **stop and suggest** `/task:to-roadmap`. That is a roadmap's `## Architecture` section, not a spec; captured here it would restate the roadmap's items in technical dress. Say plainly that nothing was written: "This is the initiative's technical shape, not a decision with a rationale to pin. Nothing was written. `→ Next: \`/task:to-roadmap\` to capture the initiative with its architecture — or, for a roadmap that already exists, edit its \`## Architecture\` section by hand (or have any session do it) per docs/contract.md § Roadmap architecture section.`" When the discussion holds both, pin the real decisions here and name the rest in the digest as technical shape for a roadmap's `## Architecture`.

(The slug-collision check runs at save time, once the slug is derived — see Step 4.)

### Step 1: Load context

Read `.task/CLAUDE.md` (Language, conventions), the project's `CLAUDE.md` if present, and the `docs/` top level. Structural style comes from Step 0's `SPECS:` list — open one only when this decision area may overlap it, so a decision it already pins is not duplicated. Open source files only as far as stating a decision accurately requires: this is decision capture, not implementation.

### Step 2: Cold start or harvest

**Branch first**, since the file is written from decisions settled somewhere:

- **Harvest** — this conversation already settled concrete technical decisions. Tells: "write a spec from what we settled", or `$ARGUMENTS` reading as a handle for prior discussion. → Step 2H.
- **Cold start** — a rough decision area, no prior discussion. → Step 2C.

On the fence, prefer harvest: a false positive costs one recap the user skims, a false negative silently drops reasoning.

#### Step 2H: Harvest — Decision Inventory

Comb the prior conversation and print, as message text in your reply (chat-only, never written to a file; heading skeleton English, prose in the language from `.task/CLAUDE.md`):

```
## Spec — Decision Inventory

{Writing this spec from our discussion — here is every technical decision
I captured with its load-bearing reasoning. Say so if a line is wrong.}

### Decisions locked so far
1. {decision at full specificity} — because {the load-bearing reason}
2. {...}

### Open forks (not yet decided)
- {unresolved technical question}

### Coverage caveat
{Only if part of the discussion is out of context. Omit otherwise.}
```

It is a **recap** of decisions and their reasoning, already reached: print it, no confirmation chip. Open forks left → resolve them first, then draft. A misread decision or rationale arrives as chat: correct it and reprint before drafting.

#### Step 2C: Cold start — decide the forks

For a decision area with no prior discussion, work each fork with the user before drafting. For each load-bearing choice, lay out the real options with a recommendation:

```
## Spec — Round 1

### Decision area as I understand it
{2–4 sentences.}

### Fork: {the choice to make}
**A) {option}** — {sketch}. Pros / Cons: {...}
**B) {alternative}** — {sketch}. Pros / Cons: {...}

### My recommendation
{a real opinion, not a hedge}

### What I need from you
{One focused question on the most load-bearing fork.}
```

Two options per fork at least, or a stated reason why only one is viable. A round is **content dialogue, not a path fork**: print it as message text and close on the open question, because convention (c)'s chips would flatten the pros and cons the round exists to show. Iterate as `Round N` until the decisions are settled, then reprint the full list as message text — a recap, no confirmation chip. Topics the user said to skip stay skipped.

### Step 3: Draft the spec

Once the Step 2H inventory (or the Step 2C recap) is **printed** — no reply is awaited; a correction arrives as chat, and you reprint before drafting — draft per [contract § Spec file format](../../docs/contract.md#spec-file-format-taskspecslugmd), which owns the file shape. Each `## N. <title>` section carries three parts:

- **Decision:** what was chosen — concrete, technical, specific (naming real symbols/protocols/shapes is expected here, unlike a roadmap item).
- **Rationale:** the reasoning that must survive, so a later plan or executing session doesn't re-litigate it.
- **Constrains:** what this pins for consumers, and what it deliberately leaves free.

Each part starts its own paragraph, after a blank line: adjacent `**Decision:**` / `**Rationale:**` / `**Constrains:**` lines merge into one paragraph in a Markdown preview. The quoted intro under `# Spec: <Title>` is one sentence of your own on what this spec pins — never the contract's placeholder wording copied as-is.

Keep one decision per section. Before saving, a quick self-check, fixed inline:

1. Every decision is load-bearing: work would come out different if it were re-derived. No single-task details, no restated behavioral outcomes.
2. **Every section passes the decision test.** Its **Decision** names a concrete technical artifact — a type, a format, a protocol, a boundary rule — and its **Rationale** names at least one alternative that was rejected, with why. A section that fails it is not a decision: one that restates a roadmap item ("a module will handle X") or lays out components and how items connect belongs in that roadmap's `## Architecture` section, which `to-roadmap` writes — drop it here and name it in the digest.
3. Each `## N.` section stands alone — a reader who hasn't seen this chat understands the decision and why.
4. No placeholders (`TBD`, `TODO`, `???`, `fill in`).
5. Section numbers contiguous from 1 — the `**Spec references:** [<slug>](../spec/<slug>.md) §N` citations that other artifacts carry depend on stable numbering.
6. Each `**Decision:**` / `**Rationale:**` / `**Constrains:**` starts after a blank line, and the intro is a real sentence about this spec.

### Step 4: Save

Write the file directly — no in-chat preview, no confirmation prompt. Step 2's recap was the review, and Step 5's digest lets the user judge whether to open it.

1. Slug: kebab-case from the decision-area topic, ≤ 50 chars (`event-envelope`, `auth-token-model`), in English whatever `.task/CLAUDE.md` → Language says — it is a filename, a parser-stable string. Its own identity, independent of any roadmap.
2. **Slug collision.** If that slug is already in Step 0's `SPECS:` list → **stop** and pose an `AskUserQuestion` (**Overwrite** / **Pick different slug**). Never silently overwrite. That list is a snapshot taken before the rounds and no writer script guards this path, so when the slug is *absent* from it, confirm with a file read that nothing is there before writing.
3. Write `$AI_DIR/spec/<slug>.md` (creating the directory if needed), and nothing else — wiring a `Spec:` header into a task or roadmap is `to-task` / `to-roadmap`'s job when they reference this spec. If the write fails (unwritable `.task/spec`, full disk), report the failure plainly, skip Step 5's digest — it would name a path that does not exist — and close with `→ Next: fix the write failure (permissions or free space under \`$AI_DIR\`), then rerun \`/task:to-spec\`.` Stop there; there is nothing to validate.
4. Validate it: `bash "${CLAUDE_PLUGIN_ROOT}/skills/validate/validate.sh" spec <slug>` — surface any WARN/ERROR in the Step 5 digest; only a setup-precondition failure (exit 2) hard-stops. Report it and close with `→ Next: rerun \`/task:to-spec\` — its setup writes \`.task/CLAUDE.md\` first.`

### Step 5: Output — digest

Print the structural digest (convention (b)) as message text. A spec is read by the executing session as a **fixed anchor**, so list **every** pin — this is the user's one glance to catch a misstated decision:

```
Wrote `.task/spec/<slug>.md`
# Spec: {Title}

Pins:
1. {decision, one line}
2. {…}

Left for a roadmap's `## Architecture`: {technical shape dropped by the decision test, one line | omit when none}

validate: {OK — 0 errors, N warning(s) | the FAIL lines}
```

On a result that is **not** clean, append `re-check after editing: bash "${CLAUDE_PLUGIN_ROOT}/skills/validate/validate.sh" spec <slug>` — `validate` is not a slash command, so it is worth spelling out. Omit it on a clean result.

The file is already written — to change any pin, just say so. Then the handoff footer (convention (a)): `→ Next: \`/task:to-task\` for a task that leans on \`.task/spec/<slug>.md\` — or attach it by hand with the line \`Spec: [<slug>](../spec/<slug>.md)\`, above the \`---\` in a task or directly under a roadmap's \`# <Title>\`.`

## Forbidden

- Writing a `## Plan`, a step list, paths with line numbers, or implementation code. A spec pins decisions; it neither plans nor implements.
- Capturing behavioral outcomes or single-task details — those belong in a task's `**Outcomes**` / `**Acceptance criteria**`.
- Capturing a component map, a module layout, or how an initiative's items connect — that is a roadmap's `## Architecture` section, written by `to-roadmap`.
- Modifying any file but `.task/spec/<slug>.md`, or overwriting one silently. Stamping a `Spec:` header onto a task or roadmap is the referencing skill's job.
- Writing a filler spec when no load-bearing decision was settled — stop and redirect instead.
- Placeholders anywhere; re-raising topics the user asked to skip.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.