skills/generate-spec/SKILL.md
--- name: generate-spec description: Generate a spec-driven-development spec for a feature or whole product. Two modes, fresh (interview-driven greenfield, vault-first when no repo exists) or from-codebase (analyses an existing repo). Outputs versioned markdown with EARS-format requirements, mermaid diagrams, and inline ADRs. Specs are born in a vault and graduate into a repo's docs/specs/ once implementation starts. Optional PDF render. Use when starting a new project, capturing the current sta
npx skillsauth add RonanCodes/ronan-skills skills/generate-specInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Produces a single spec.md per generation, versioned by date and mode. The spec is the executable contract: stories use EARS format, decisions are inline ADRs, and the plan slots straight into /ralph.
Sister skills: /compare-specs (diff two specs) and /compare-codebase-to-spec (drift audit). Render to PDF via the llm-wiki /generate pdf skill.
Specs go through three phases. Storage moves with the phase.
| Phase | Where the spec lives | Status field | Trigger |
|---|---|---|---|
| 1. Genesis | Vault: vaults/llm-wiki-<vault>/wiki/specs/<name>-spec-v1-fresh-YYYY-MM-DD.md | draft then accepted | /generate-spec --mode fresh --vault <name> <project> |
| 2. Graduation | Repo: docs/specs/spec-v1-fresh-YYYY-MM-DD.md (copy of vault canonical) | vault entry: graduated-to-repo, repo entry: accepted | Repo scaffolded from spec; copy + cross-link both sides |
| 3. Living spec | Repo: docs/specs/spec-v<N>-from-codebase-YYYY-MM-DD.md | each version: draft → accepted → superseded | /generate-spec --mode from-codebase after each phase ships |
A spec born inside an existing repo (no vault step) jumps straight to phase 3 storage.
# Phase 1, vault genesis (no repo yet)
/generate-spec --mode fresh --vault <vault-short> <name>
# Phase 3, in an existing repo (cwd is the repo)
/generate-spec --mode from-codebase <name>
/generate-spec --mode from-codebase --repo <path> <name>
# Both at once: write canonical to vault, mirror to repo's docs/specs/
/generate-spec --mode fresh --vault <vault-short> --repo <path> <name>
# Render PDF alongside markdown
/generate-spec --pdf ...
<name> is kebab-case. Defaults to repo's package.json name or directory name when --repo is set; otherwise the user must supply it.
Pick the destination by which flags are set:
| --vault | --repo (or cwd is a repo) | Canonical path | Mirror? |
|---|---|---|---|
| set | not set | vaults/llm-wiki-<vault>/wiki/specs/<name>-spec-v<N>-<mode>-YYYY-MM-DD.md | none |
| not set | set | <repo>/docs/specs/spec-v<N>-<mode>-YYYY-MM-DD.md | none |
| set | set | <repo>/docs/specs/spec-v<N>-<mode>-YYYY-MM-DD.md | summary stub in vault with cross-vault link to repo path |
| not set | not set | error: ask the user where to write |
In all cases:
<N> auto-increments by counting existing spec-v*.md files in the canonical directory.If --pdf, also render the canonical markdown via /generate pdf and write next to it (same directory, .pdf extension). Print both paths.
Interview the user via AskUserQuestion. Ask one question at a time. Adapt based on answers. Cover all 10 sections of the template.
Question order:
Then write the spec using the template below. Show it to the user. Ask: "Look right? Anything to revise before we save?"
Read the repo and infer each section. Do not invent. Mark inferred items with a (inferred) tag in the spec so the user can validate.
Inference order (use grep, glob, and Read):
package.json, pnpm-lock.yaml, Cargo.toml, pyproject.toml, go.mod, wrangler.jsonc. Read README.md, ARCHITECTURE.md, TECH_CHOICES.md if present.package.json description.TECH_CHOICES.md, DESIGN_SYSTEM.md, ARCHITECTURE.md. Treat each "we chose X because Y" as an inline ADR.src/routes/**, src/pages/**, src/components/**, exposed CLI commands, public API endpoints.drizzle/, prisma/, migrations/, models/..ralph/prd.json if present, otherwise leave a single milestone "v1 (current)" and list shipped stories.tests/, e2e/, CI config. Each story → linked test or "no test (gap)".wrangler.jsonc runtime, package.json engines, env var requirements, DPA/compliance docs in docs/.gh is available.If --vault <name> is set, read vaults/llm-wiki-<name>/wiki/entities/project-<repo-name>.md, the launch plan, and recent session notes. Treat vault notes as supplementary context, codebase as primary.
---
title: <Project> Spec
version: v<N>
date: YYYY-MM-DD
mode: fresh | from-codebase
status: draft | reviewed | accepted | graduated-to-repo | superseded
repo: <path or git url, or empty string for vault-genesis specs>
related-vault: <vault-short or empty>
graduated-from-vault: <vault-short and path, set when phase moves to repo>
supersedes: <previous version path or empty>
---
# <Project> Spec, v<N>
> One-sentence summary of what this spec covers.
## 1. Constitution
Non-negotiable principles. If a future change violates one of these, the principle wins or the constitution is amended explicitly.
- **<Principle>** — <one-line rationale>
## 2. Outcomes
What success looks like.
| Outcome | Audience | How we measure |
|---|---|---|
| <user-visible outcome> | <user role> | <metric or signal> |
## 3. Scope
| In scope (v<N>) | Out of scope |
|---|---|
| <feature> | <feature> |
## 4. Constraints
| Type | Constraint | Source |
|---|---|---|
| Tech | <e.g. runs on Cloudflare Workers> | <wrangler.jsonc> |
| Business | <e.g. zero-cost hosting> | <decision> |
| Regulatory | <e.g. EU data residency> | <client requirement> |
| Time | <e.g. ship by YYYY-MM-DD> | <commitment> |
## 5. User Stories
EARS format: `WHEN <trigger> THE system SHALL <behaviour>`. One row per acceptance criterion so each is independently testable.
### Web-app baseline checklist (must-have stories)
Any spec for an app that has a web UI, an authenticated user, or an HTTP API MUST include the following stories. If you skip one, the gap will surface mid-build as a manual fix-up. Mark "N/A — <reason>" inline if a story genuinely doesn't apply (e.g. CLI-only tool: skip auth UI; no public API: skip API-docs).
| Story | Why it's mandatory |
|---|---|
| **US-000 Bootstrap** | Repo scaffold, quality stack, first green deploy. Tracer bullet that every later story rides. Must include CI secrets bootstrap AND a runtime health-probe assertion against the deployed URL, not just a green-CI check. |
| **Auth UI (sign-in + sign-out)** | Explicit acceptance criterion: "WHEN the user clicks the sign-out control on any authenticated page THE system SHALL end the session and redirect to /". Don't bury sign-out under "auth wired up"; it's a user-visible feature with its own e2e. |
| **Lazy auth mirror** | If auth uses a webhook to sync users into your DB (Clerk, Auth0, etc.), include a story for the cold-start path: "WHEN an authenticated request arrives before the user.created webhook has fired THE system SHALL mirror the user on demand and proceed." Otherwise sign-up users hit "user not found" until the webhook lands. |
| **API discoverability** | OpenAPI 3.1 doc served at `/api/openapi/json` (or equivalent) AND a rendered viewer (Scalar / Swagger UI / Redoc) at `/api-docs`. Acceptance: both routes return 200 in production. |
| **API client collection** | Bruno (or Postman) collection committed in the repo, one request per public route, env files for local + prod. Acceptance: at minimum the health/openapi requests run green in CLI-mode. |
| **Integration test layer** | Tests that exercise route handlers + DB with in-memory or test-container DB (not mocks). Acceptance: at least one integration test per data-mutating endpoint. Don't rely on e2e to cover data-layer correctness. |
| **CI env injection** | The CI workflow MUST materialise the dev-server's env file (`.dev.vars`, `.env.local`, etc.) from CI secrets before any job runs the dev server. Document each required key explicitly. Skipping this is the #1 cause of "works locally, red in CI" with frameworks like TanStack Start, Next.js, Wrangler. |
| **Deployment health probe** | Each shipping story's DoD includes: "the deployed URL returns 200 from `/api/health` AND the new route is reachable." Don't trust green CI alone; verify against the actual deployed instance. |
| **Lazy reconciliation for any external source of truth** | If your app caches state from an external service (Nango / Stripe / Clerk / Auth0 / GitHub), the read endpoint for that state SHALL reconcile from the source on every request, not just rely on webhooks. Webhook is the fast path; reconcile-on-read is the correct path. Same shape as lazy auth-mirror. Otherwise a misconfigured webhook URL strands users in stale state. |
| **Onboarding checklist (when relevant)** | If the app has an empty-state Home that just lists "do this, do that," replace it with a gamified onboarding checklist driven by server-observed events. See `/onboarding-flow` skill + `[[onboarding-checklist-ux]]` canon. Persistent dashboard widget, event-driven auto-tick, free-roam steps, server-side state in the app DB. |
| **Share assets (favicon, app icons, OG image, Twitter card)** | Every link a customer pastes into X / Reddit / LinkedIn / WhatsApp / iMessage / Slack triggers an OpenGraph fetch. Default Vite/Next.js favicons + missing OG previews look broken. Spec MUST include a story for: favicon set (.ico + .svg + apple-touch), PWA manifest icons (192/512/maskable), static OG image fallback, dynamic per-URL OG route, OpenGraph + Twitter card meta tags, JSON-LD application schema. See `/share-assets` skill. |
| **Worker bundle-size budget** | If the app deploys to Cloudflare Workers, every story SHALL include "wrangler deploy --dry-run target=production passes" in its DoD. The free-tier ceiling is 3 MiB (gzipped) for the worker bundle; the paid tier is 10 MiB. Stories that add wasm (resvg, satori, wasm-bindgen artefacts) or inlined font/image bytes are most likely to bust the budget. Today most CI pipelines deploy post-merge so a bad story poisons main and forces a revert PR. The dry-run check moves the failure pre-merge. Source: Dataforce US-333 dynamic OG hit the 3 MiB ceiling because resvg-wasm (2.4 MiB) + Inter fonts (290 KiB) + Scalar modal (4.8 MiB unminified) compounded. Fix: extract heavy routes to a dedicated Worker, upgrade plan, or trim upstream deps. |
These exist because every greenfield web-app spec we ship rediscovers them. Inline them by default; remove only on explicit justification.
### US-001 — <short title>
**As a** <role>, **I want** <capability>, **so that** <benefit>.
| # | Acceptance criterion (EARS) | Verified by |
|---|---|---|
| 1 | WHEN <trigger> THE system SHALL <behaviour> | <test path or demo> |
(Repeat for US-002, US-003, ...)
## 6. Architecture
### Container diagram
```mermaid
graph TB
user[<User>] --> app[<App>]
app --> store[(<Data store>)]
app --> ext[<External service>]
classDef user fill:#e0af40,stroke:#333,color:#000
classDef engine fill:#5bbcd6,stroke:#333,color:#000
classDef output fill:#7dcea0,stroke:#333,color:#000
class user user
class app engine
class store,ext output
```
### Key sequence — <flow name>
```mermaid
sequenceDiagram
participant U as User
participant A as App
participant D as Data
U->>A: <action>
A->>D: <query>
D-->>A: <result>
A-->>U: <response>
```
### Data model
| Entity | Fields | Notes |
|---|---|---|
| <name> | <fields> | <constraints> |
## 7. Decisions (inline ADRs)
| # | Decision | Chose | Over | Why |
|---|---|---|---|---|
| ADR-001 | <topic> | <X> | <Y, Z> | <one-line reason> |
## 8. Plan
Milestones → stories. Tracer bullet first (thinnest end-to-end slice). Stories map 1:1 to entries here, ordered by build sequence.
| Milestone | Goal | Stories | Status |
|---|---|---|---|
| M1 — Tracer | <thinnest demoable slice> | US-001 | <not started \| in progress \| shipped> |
| M2 — <name> | <goal> | US-002, US-003 | <status> |
## 9. Verification
How we accept each story. Tests, demos, or metrics — pick one per story.
| Story | Method | Location | Status |
|---|---|---|---|
| US-001 | <unit test \| e2e test \| manual demo \| metric> | <path or dashboard> | <pending \| passing \| gap> |
## 10. Open Questions
- [ ] <question> — owner: <who> — by: <when>
## Sources
For from-codebase mode: list every file read during inference.
- `<path>` — used for <section>
For fresh mode: list AskUserQuestion answers as session memory.
#e0af40 for user/sources, cyan #5bbcd6 for engine, green #7dcea0 for outputs).on cache miss, on error, if signed in, not miss, err, auth. A reader should understand the edge without knowing the system.(inferred) so reviewers know to validate.<N>. Never overwrite an existing file.supersedes frontmatter field links to the previous version (use the canonical path at the time of writing).draft → accepted → (graduated-to-repo if vault genesis) → superseded (when a newer version replaces it).When the user is ready to scaffold the repo from an accepted vault spec, the manual hand-off is:
/new-tanstack-app or by hand).<repo>/docs/specs/spec-v<N>-fresh-YYYY-MM-DD.md (preserve the original date and mode in the filename; this is the genesis spec graduating, not a new generation).repo: <repo-url>, graduated-from-vault: <vault-short>:<original vault path>, status: accepted.status: graduated-to-repo and add a cross-vault link to the repo file.A future skill (/spec-to-repo) will automate steps 2-5; for now do it by hand.
--pdf, run /generate pdf <output-path>. Print the PDF path.--vault, write the summary stub and print its path./compare-codebase-to-spec after the next phase to see drift."development
Close the loop on a Linear ticket when its work ships - move the status and post a deploy comment with the PR link, what shipped, and a try-it link, mentioning the collaborator. Used as the tail of /ro:linear-nightshift for every merged mirror, or manually after an ad-hoc build. Triggers on "linear update", "update the linear ticket", "mark NUT-x done", "tell eoin it shipped", "/ro:linear-update".
devops
Run a night-shift against a collaborator's Linear board. Pulls the team's Grilled tickets (/ro:linear-grill moves a ticket to Grilled once its questions are answered), VERIFIES the questions were actually answered (unanswered → bounce the ticket to the "Question for <name>" state), mirrors verified tickets to ephemeral GitHub issues with ready-for-agent, then runs the standard /ro:night-shift machinery on GitHub. Tail-calls /ro:linear-update for everything that merged + deployed. Triggers on "linear nightshift", "nightshift linear", "drain the linear board", "run the shift off linear", "/ro:linear-nightshift".
development
Grill a collaborator's Linear tickets and move every processed ticket to where it belongs. Resolves the board from the repo's .ro-linear.json, reads the collaborator's Backlog / Ready-for-agent issues, then per ticket either posts 3-5 decision-extracting questions (state moves to "Question for <name>") or confirms it build-ready (state moves to "Grilled", the gate /ro:linear-nightshift consumes); shipped-and-confirmed tickets close as Done. The async-collaborator counterpart of /ro:day-shift for people who never touch GitHub. Triggers on "grill linear", "grill eoin's tickets", "linear grill", "add questions to the linear tickets", "/ro:linear-grill".
development
--- name: about-page description: Add a standard About page to any web app, what it is, the tech stack, and an FAQ, wired into a footer link with a sticky footer. Built with Spartan + Tailwind (the canonical component layer) and falls back to semantic HTML so it ships reliably. Use whenever building, polishing, or shipping an app, every app should have one. Triggers on "add an about page", "about page", "footer about link", or as a standard step in app build/polish. category: frontend argument-h