Files
cyclone/docs/superpowers/plans/2026-06-21-cyclone-skill-catalog.md
T

827 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cyclone Skill Catalog Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Ship 8 layer-mapped Cyclone skills under `.superpowers/skills/` (one per PR), then update the README to advertise the catalog. No code changes — pure guidance artifacts.
**Architecture:** Each skill is a single `SKILL.md` (150250 lines, YAML frontmatter + sections per the spec template). One commit per skill. Phase order matches the spec's build order so each later skill can ship its `## Related skills` section pointing to skills that already exist.
**Tech Stack:** Markdown, YAML frontmatter. No build step, no runtime, no tests (skills are guidance, not code).
**Spec:** [`docs/superpowers/specs/2026-06-21-cyclone-skill-catalog-design.md`](../specs/2026-06-21-cyclone-skill-catalog-design.md)
---
## File structure
```
.superpowers/
└── skills/
├── cyclone-spec/
│ └── SKILL.md ← Task 1
├── cyclone-tests/
│ └── SKILL.md ← Task 2
├── cyclone-edi/
│ └── SKILL.md ← Task 3
│ └── references/
│ └── parsers.md ← Task 3
├── cyclone-tail/
│ └── SKILL.md ← Task 4
│ └── references/
│ └── wire-format.md ← Task 4
├── cyclone-store/
│ └── SKILL.md ← Task 5
├── cyclone-api-router/
│ └── SKILL.md ← Task 6
├── cyclone-frontend-page/
│ └── SKILL.md ← Task 7
└── cyclone-cli/
└── SKILL.md ← Task 8
README.md ← Task 9 (catalog section)
```
Each skill is one PR. Each `SKILL.md` follows the template in spec §"Per-skill structure". Optional `references/<file>.md` only when the SKILL.md would otherwise exceed ~200 lines.
---
## Shared SKILL.md template (lock this in Task 0, then apply to every Task)
```markdown
---
name: <kebab-case>
description: "<one-sentence trigger, with Use when: … keywords>"
---
# <Title>
## When to use
- <bullet 1 — concrete situation>
- <bullet 2>
- <bullet 3>
## Conventions
1. <testable rule>
2. <testable rule>
3. <testable rule>
## Patterns
\`\`\`<lang>
<copy-pasteable skeleton>
\`\`\`
## Anti-patterns
- <tempting-but-wrong move>
## Related skills
- `<other-skill>` — <one line>
- `<other-skill>` — <one line>
```
YAML frontmatter rules:
- `name` must be kebab-case and match the directory name.
- `description` is the only thing the auto-loader sees. One sentence, max ~200 chars. Include a `Use when:` clause listing the trigger situations.
---
## Task 0: Pre-flight — verify starting state
**Goal:** Confirm `.superpowers/skills/` does not exist yet, capture a directory snapshot, and verify the catalog can be served from project scope.
**Files:**
- Read: `.superpowers/` (existing project-scoped superpowers directory)
- Create (no commit): `/tmp/cyclone-skill-baseline.txt`
- [ ] **Step 1: Inspect existing `.superpowers/` contents**
```bash
ls -la .superpowers/
```
Expected: shows only `plans/` and `specs/` directories (the canonical superpowers content for this repo). No `skills/` directory yet.
- [ ] **Step 2: Confirm no stray skill files outside `.superpowers/skills/`**
```bash
find . -path ./node_modules -prune -o -path ./.git -prune -o -path ./backend/.venv -prune -o -name "SKILL.md" -print
```
Expected: no output (or only paths under `.grok/` system directories that are not part of the repo).
- [ ] **Step 3: Verify the README's skills section is empty (or absent)**
```bash
grep -n -i "skill" README.md | head -10
```
Expected: a few hits for "skill" in unrelated contexts (e.g. "skill set" in marketing copy) but no existing catalog section.
- [ ] **Step 4: Save the baseline**
```bash
ls .superpowers/ | tee /tmp/cyclone-skill-baseline.txt
```
Expected: `plans\nspecs`.
No commit. Pre-flight only.
---
## Task 1: Phase 1, Skill 1 — `cyclone-spec`
**Goal:** Codify the SP-N superpowers flow that this repo already uses (14 specs, 8 plans on disk). Pure documentation; this skill owns the convention so every later feature goes through it consistently.
**Files:**
- Create: `.superpowers/skills/cyclone-spec/SKILL.md`
- [ ] **Step 1: Confirm the SP-N flow materials exist**
```bash
ls docs/superpowers/specs/ | wc -l
ls docs/superpowers/plans/ | wc -l
git log --oneline | grep -E "^(feat|docs|merge): SP[0-9]+" | head -5
```
Expected: ≥ 14 specs, ≥ 8 plans, ≥ 5 SP-N commits in history. Record the numbers for use in the skill.
- [ ] **Step 2: Create the directory**
```bash
mkdir -p .superpowers/skills/cyclone-spec
```
- [ ] **Step 3: Write the SKILL.md**
Use the shared template. Frontmatter:
```yaml
---
name: cyclone-spec
description: "Cyclone SP-N superpowers increment flow — spec → plan → implement → merge. Use when: starting a new numbered feature increment, naming a branch, opening a SP-N PR, or doing the merge dance into main."
---
```
Body sections (each must be present, in this order):
- `## When to use` — 4 bullets covering: starting a new increment, naming the spec/plan files, opening the PR, performing the merge.
- `## Conventions` — at minimum these numbered rules:
1. **Numbering.** Reserve the next SP-N number (next after the highest in `git log`). Never reuse a number.
2. **Branch.** `sp<N>-<short-kebab-topic>` (e.g. `sp22-line-reconciliation`).
3. **Spec path.** `docs/superpowers/specs/YYYY-MM-DD-cyclone-<topic>-design.md`. Status header: `Draft, pending user review`.
4. **Plan path.** `docs/superpowers/plans/YYYY-MM-DD-cyclone-<topic>.md`. Header per writing-plans skill.
5. **Commit prefix.** `feat(sp<N>): …`, `docs(spec): …`, `docs(plan): …`, `merge: SP<N> … into main`.
6. **PR title.** `SP<N> <Topic>` (matches commit history).
7. **Merge shape.** Single atomic merge commit into `main` after review; no squash, no rebase.
- `## Patterns` — copy-pasteable skeleton for the spec header (Date / Status / Branch / Scope) and the plan header (per writing-plans).
- `## Anti-patterns` — at least these:
- Don't skip the spec ("it's a small fix"). Small fixes still get a 3-line spec when they introduce a new numbered increment.
- Don't squash the merge commit — the SP-N merge commit is the audit trail.
- Don't put code in the spec — the spec is the *what*, the plan is the *how*.
- `## Related skills` — references to `cyclone-tests` (every spec mentions test impact), `cyclone-edi` / `cyclone-tail` / `cyclone-store` / `cyclone-api-router` / `cyclone-frontend-page` / `cyclone-cli` (each spec touches one or more of these), and the upstream `superpowers:brainstorming` / `superpowers:writing-plans` global skills.
Keep total under ~200 lines. Use real examples drawn from existing specs (e.g. SP9 multi-payer, SP14 5-lane Inbox) where they illustrate the convention.
- [ ] **Step 4: Verify frontmatter and structure**
```bash
head -5 .superpowers/skills/cyclone-spec/SKILL.md
wc -l .superpowers/skills/cyclone-spec/SKILL.md
grep -c "^## " .superpowers/skills/cyclone-spec/SKILL.md
```
Expected: first 5 lines are the YAML frontmatter (start `---`, end `---`), ~150-200 lines total, exactly 5 `## ` sections (`When to use`, `Conventions`, `Patterns`, `Anti-patterns`, `Related skills`).
- [ ] **Step 5: Commit**
```bash
git add .superpowers/skills/cyclone-spec/SKILL.md
git commit -m "feat(sp-skill-catalog): add cyclone-spec skill (SP-N flow)"
```
---
## Task 2: Phase 1, Skill 2 — `cyclone-tests`
**Goal:** Codify the pytest + vitest fixture patterns so every later skill can ship its `## Anti-patterns` cleanly. Specifically: how to drop a prodfiles fixture into `backend/tests/fixtures/`, how to write a `.test.tsx` next to a page, how to keep tests deterministic.
**Files:**
- Create: `.superpowers/skills/cyclone-tests/SKILL.md`
- [ ] **Step 1: Survey the existing fixture layout**
```bash
ls backend/tests/fixtures/ | head -30
ls backend/tests/fixtures/ | wc -l
ls src/**/*.test.tsx src/**/*.test.ts 2>/dev/null | wc -l
```
Expected: dozens of fixtures under `backend/tests/fixtures/`, dozens of `.test.ts(x)` siblings across `src/`. Record counts for the skill.
- [ ] **Step 2: Sample one backend pytest and one frontend vitest to cite**
```bash
ls backend/tests/fixtures/ | head -3
echo "---"
ls src/hooks/*.test.ts | head -3
```
Pick the smallest/most-representative example from each side to cite in the skill's `## Patterns`.
- [ ] **Step 3: Create the directory and write the SKILL.md**
```bash
mkdir -p .superpowers/skills/cyclone-tests
```
Frontmatter:
```yaml
---
name: cyclone-tests
description: "Cyclone pytest + vitest fixture patterns, prodfiles layout, backend/tests/fixtures/ conventions, .test.tsx sibling rule. Use when: adding a backend pytest case, adding a frontend vitest test, or wiring in a real-EDI prodfiles sample."
---
```
Body sections (each must be present):
- `## When to use` — 4 bullets: adding a backend test, adding a frontend test, dropping in a prodfiles fixture, debugging a flaky test.
- `## Conventions` — at minimum:
1. **Frontend sibling rule.** Every new file in `src/` that contains testable logic gets a `*.test.ts(x)` next to it. `useFoo.ts``useFoo.test.ts`.
2. **Backend test location.** Tests live under `backend/tests/test_*.py`. Integration tests (FastAPI) follow `test_api_*.py`; pure-unit tests (parsers, validators) follow `test_<module>_*.py`.
3. **Prodfiles drop-in.** Real EDI samples live under `docs/prodfiles/<source>/<edi>.txt`. To use one in a test: copy to `backend/tests/fixtures/<test-name>/<edi>.txt` and reference via the existing fixture helper (see `## Patterns`).
4. **Determinism.** Use `freezegun` for date-sensitive backend tests; use `vi.useFakeTimers()` for time-sensitive frontend tests. Never rely on `datetime.now()` directly.
5. **No network.** Tests must not hit the network. `httpx`/`requests` mocks live in `conftest.py` fixtures.
6. **pytest collection.** Run `cd backend && python -m pytest tests/<file>::<name> -v` for the fastest feedback loop. Full suite is the merge gate.
- `## Patterns` — three small copy-pasteable skeletons:
- One pytest using `tmp_path` + a prodfiles fixture reference.
- One vitest `*.test.ts` for a hook using `vi.useFakeTimers()`.
- One vitest `*.test.tsx` for a component using `@testing-library/react` + `happy-dom`.
- `## Anti-patterns` — at least:
- Don't put frontend tests in `src/__tests__/` (legacy location, no longer used).
- Don't reach into `docs/prodfiles/` directly from a test — copy to `backend/tests/fixtures/` so tests stay runnable when prodfiles is reorganized.
- Don't use `sleep()` for timing — use the deterministic timer tools listed in Conventions.
- `## Related skills` — references to all 7 other skills (every domain skill impacts tests) plus `superpowers:test-driven-development` upstream.
Keep under ~250 lines (this is the most cross-cutting skill, so it's the largest).
- [ ] **Step 4: Verify**
```bash
head -5 .superpowers/skills/cyclone-tests/SKILL.md
wc -l .superpowers/skills/cyclone-tests/SKILL.md
grep -c "^## " .superpowers/skills/cyclone-tests/SKILL.md
```
Expected: frontmatter present, 150250 lines, 5 `## ` sections.
- [ ] **Step 5: Commit**
```bash
git add .superpowers/skills/cyclone-tests/SKILL.md
git commit -m "feat(sp-skill-catalog): add cyclone-tests skill (fixture patterns)"
```
---
## Task 3: Phase 2, Skill 3 — `cyclone-edi`
**Goal:** Codify Cyclone's EDI parser/validator conventions across 837P/835/999/270/271/277CA/TA1. Highest-leverage skill — 30 parsers, scattered validators, fixture sprawl.
**Files:**
- Create: `.superpowers/skills/cyclone-edi/SKILL.md`
- Create: `.superpowers/skills/cyclone-edi/references/parsers.md`
- [ ] **Step 1: Enumerate the parsers and validator rules**
```bash
ls backend/src/cyclone/parsers/
grep -rn "^def parse_" backend/src/cyclone/parsers/ | head -40
grep -n "^R[0-9]" backend/src/cyclone/validator*.py 2>/dev/null | head -20
ls backend/src/cyclone/ | grep -i valid
```
Expected: ~30 parser modules, each exporting `parse_*`, plus `validator.py` and possibly `validator_835.py`. Record the R-codes (R200, R210, etc.) found.
- [ ] **Step 2: Sample one parser to cite in the skill**
Pick the smallest one (e.g. `parse_ta1.py` or `parse_999.py`) and read the first 30 lines. The skill's `## Patterns` section will reference its structure.
- [ ] **Step 3: Create directories and write the SKILL.md + reference**
```bash
mkdir -p .superpowers/skills/cyclone-edi/references
```
Frontmatter:
```yaml
---
name: cyclone-edi
description: "Cyclone EDI parser/validator conventions (837P/835/999/270/271/277CA/TA1). Use when: adding or changing a parser, adding a validator rule (R200/R210/NPI Luhn/EIN/CAS), or mapping a new CAS adjustment reason code."
---
```
Body sections:
- `## When to use` — 4 bullets: adding a parser, adding a validator rule, wiring a new CAS code, debugging a parse failure on a prodfiles sample.
- `## Conventions` — at minimum:
1. **Parser signature.** Every parser module exports `parse(text: str) -> <TypedResult>` where `<TypedResult>` is a Pydantic model from `backend/src/cyclone/parsers/<edi>_models.py` (or co-located).
2. **Segment walk.** Parsers consume `backend/src/cyclone/segments.py` helpers (`Segment`, `Loop`, `next_segment`) — do not parse raw text inline.
3. **Validator rules.** Numbered (R200, R210, …) live in `backend/src/cyclone/validator.py` (or `validator_835.py` for 835-specific). Each rule is a function `_rule_R<n>_<name>` raising `ValidationError` with the R-code.
4. **NPI / EIN / CAS.** Identity-format rules (NPI Luhn, EIN `XX-XXXXXXX`, CAS adjustment reason codes) live in dedicated modules (`npi.py`, etc.). Don't duplicate the format logic.
5. **Prodfiles reuse.** When adding a parser for a new transaction type, ship at least one prodfiles fixture in `backend/tests/fixtures/<edi>/<sample>.txt`.
- `## Patterns` — three skeletons:
- A minimal `parse_<edi>.py` (using `segments.py`, exporting `parse(text)`).
- A validator rule `_rule_R<n>_<name>(ctx) -> None`.
- A test that uses a prodfiles fixture (`backend/tests/fixtures/<edi>/<sample>.txt`) and asserts `parse(text).foo == expected`.
- `## Anti-patterns` — at least:
- Don't re-parse raw X12 strings inside validators — always parse first, validate the typed result.
- Don't bake payer-specific logic into the generic parser — payer variations live in `backend/src/cyclone/payers.py` or `<payer>_config.py`.
- Don't add a validator rule without an R-code — the R-code is how the UI surfaces the error.
- `## Related skills` — references to `cyclone-store` (writes the parsed result), `cyclone-api-router` (exposes it), `cyclone-tests` (fixture drop-in), `cyclone-cli` (the `cyc parse <file>` smoke command).
Then write `references/parsers.md`: a flat table mapping each parser module to its transaction type, signature, primary fixtures, and any payer-specific variants. Keep under 80 lines.
- [ ] **Step 4: Verify**
```bash
head -5 .superpowers/skills/cyclone-edi/SKILL.md
wc -l .superpowers/skills/cyclone-edi/SKILL.md
wc -l .superpowers/skills/cyclone-edi/references/parsers.md
grep -c "^## " .superpowers/skills/cyclone-edi/SKILL.md
```
Expected: frontmatter present, SKILL.md ≤ 200 lines, references/parsers.md ≤ 80 lines, exactly 5 `## ` sections in SKILL.md.
- [ ] **Step 5: Commit**
```bash
git add .superpowers/skills/cyclone-edi/
git commit -m "feat(sp-skill-catalog): add cyclone-edi skill (parser/validator conventions)"
```
---
## Task 4: Phase 2, Skill 4 — `cyclone-tail`
**Goal:** Codify the live-tail wire format and the `useTailStream` + `useMergedTail` + per-resource hook triplet. Concrete drift risk — wire format is in the README but consumed across 3 page-hook pairs.
**Files:**
- Create: `.superpowers/skills/cyclone-tail/SKILL.md`
- Create: `.superpowers/skills/cyclone-tail/references/wire-format.md`
- [ ] **Step 1: Survey the live-tail surface**
```bash
grep -rn "/stream" backend/src/cyclone/api_routers/ src/hooks/ 2>/dev/null | head -20
ls src/hooks/use*Stream* src/hooks/use*MergedTail*
ls src/hooks/use*Tail* 2>/dev/null
grep -rn "snapshot_end\|x-ndjson" backend/src/cyclone/ src/ 2>/dev/null | head -10
```
Expected: 3 `/api/<resource>/stream` endpoints, one `useTailStream.ts`, one `useMergedTail.ts`, multiple `use<X>Stream` consumers.
- [ ] **Step 2: Read the wire-format spec in the README**
```bash
grep -n -A 30 "Wire format" README.md | head -50
```
This is the canonical definition; the skill will reference it. Record the line range for the `references/wire-format.md` cross-reference.
- [ ] **Step 3: Create directories and write the SKILL.md + reference**
```bash
mkdir -p .superpowers/skills/cyclone-tail/references
```
Frontmatter:
```yaml
---
name: cyclone-tail
description: "Cyclone live-tail streaming wire format and the useTailStream / useMergedTail hook triplet. Use when: adding a new streaming list page, changing the wire format, debugging stalled/reconnecting state, or modifying the StatusPill behavior."
---
```
Body sections:
- `## When to use` — 4 bullets: adding a new streaming page, changing the wire format, debugging stalled/reconnecting, tuning heartbeat/stall timing.
- `## Conventions` — at minimum:
1. **Wire format.** Newline-delimited JSON. Line shapes: `{"type":"item","data":…}`, `{"type":"snapshot_end","data":{"count":N}}`, `{"type":"heartbeat","data":{"ts":…}}`, `{"type":"item_dropped","data":…}`, `{"type":"error","data":…}`. See `references/wire-format.md`.
2. **Hook triplet.** Streaming pages consume three hooks: `useTailStream(url)` (raw stream state), `useMergedTail(items, events)` (combine initial fetch with live events), and a per-resource hook (`useClaims`, `useRemittances`, etc.) that wraps both.
3. **Stall threshold.** 30s of total silence (heartbeat included) flips the connection to `stalled` and surfaces a `↻ Reconnect` button. Don't change this without updating the README's "Status pill" table.
4. **Snapshot first.** Every stream must emit the current snapshot before any live `item` events. The `snapshot_end` line is the marker the UI uses to flip `StatusPill` from `connecting` to `live`.
5. **Content-Type.** `application/x-ndjson`. Never `application/json` for a stream endpoint.
- `## Patterns` — three skeletons:
- A `useFoo.ts` page-hook skeleton (TanStack Query initial fetch + tail subscription).
- A new backend `/api/foo/stream` endpoint signature.
- A `StatusPill` consumer wiring.
- `## Anti-patterns` — at least:
- Don't hand-roll `fetch` + `ReadableStream` parsing in a page — go through `useTailStream` so reconnect/stall handling is consistent.
- Don't change the wire format on one endpoint without updating the other two — they share the parser in `src/lib/tail-stream.ts`.
- Don't emit `item` events before `snapshot_end` — the UI will duplicate rows.
- `## Related skills` — references to `cyclone-frontend-page` (page-level wiring), `cyclone-api-router` (the `/api/<resource>/stream` endpoint), `cyclone-store` (the event contract that drives the stream), `cyclone-tests` (`.test.ts` siblings on every hook in this skill).
Then write `references/wire-format.md`: copy the relevant README section (with attribution) and add a per-line field reference table (which fields are required, which are optional, parser tolerance). Keep under 80 lines.
- [ ] **Step 4: Verify**
```bash
head -5 .superpowers/skills/cyclone-tail/SKILL.md
wc -l .superpowers/skills/cyclone-tail/SKILL.md
wc -l .superpowers/skills/cyclone-tail/references/wire-format.md
grep -c "^## " .superpowers/skills/cyclone-tail/SKILL.md
```
Expected: frontmatter present, SKILL.md ≤ 200 lines, wire-format.md ≤ 80 lines, 5 `## ` sections.
- [ ] **Step 5: Commit**
```bash
git add .superpowers/skills/cyclone-tail/
git commit -m "feat(sp-skill-catalog): add cyclone-tail skill (live-tail wire format)"
```
---
## Task 5: Phase 3, Skill 5 — `cyclone-store`
**Goal:** Codify the store write-paths, the pubsub event contract, and the SP21 store-split boundary map. Aligns with the SP21 store split currently in plan stage — the skill codifies the new boundaries as they land.
**Files:**
- Create: `.superpowers/skills/cyclone-store/SKILL.md`
- [ ] **Step 1: Read SP21 plan to learn the new module boundaries**
```bash
cat docs/superpowers/plans/2026-06-21-cyclone-store-split.md | head -80
```
Record the module list (`batches`, `inbox`, `acks`, etc.) for use in the skill.
- [ ] **Step 2: Survey the current pubsub surface**
```bash
grep -rn "_written\|_recorded" backend/src/cyclone/pubsub.py | head -20
grep -rn "publish\|subscribe" backend/src/cyclone/store.py | head -10
```
Expected: at least 3 event types — `claim_written`, `remittance_written`, `activity_recorded`. Record exact names.
- [ ] **Step 3: Create the directory and write the SKILL.md**
```bash
mkdir -p .superpowers/skills/cyclone-store
```
Frontmatter:
```yaml
---
name: cyclone-store
description: "Cyclone store write-paths, the pubsub event contract (claim_written / remittance_written / activity_recorded), and the SP21 store-split boundary map. Use when: touching store.py, adding a new entity, wiring a new write event, or splitting a store module."
---
```
Body sections:
- `## When to use` — 4 bullets: adding a new entity, wiring a new write event, debugging a write-path issue, splitting a store module.
- `## Conventions` — at minimum:
1. **All writes go through `CycloneStore`.** Route handlers and parsers must not write directly to the ORM session.
2. **Every write publishes an event.** The event name matches the entity: `claim_written`, `remittance_written`, `activity_recorded`. New entities get `<entity>_written` (or `_recorded` for non-canonical rows).
3. **Snapshot shape.** Each entity has a Pydantic serializer in `backend/src/cyclone/store/ui.py` (or its post-SP21 equivalent). The serializer is the single source of truth for what the frontend sees.
4. **SP21 boundaries.** Post-split, each domain lives in its own module (`batches`, `inbox`, `acks`, …). Cross-module writes go through `CycloneStore` facade methods, not direct module access.
5. **No business logic in route handlers.** A route handler validates input, calls `store.<method>(...)`, publishes the event, returns the serialized result. Anything more belongs in the store or a parser.
- `## Patterns` — three skeletons:
- A `CycloneStore` write method (`def add_foo(self, foo: Foo) -> FooRecord`).
- An event publication in `pubsub.py`.
- A new `<entity>_written` handler test that asserts both the row and the event.
- `## Anti-patterns` — at least:
- Don't read directly from the ORM in a route handler — go through the snapshot serializer.
- Don't introduce a new event name without updating the subscriber list (currently `api.py` and `api_routers/`).
- Don't merge a write method with its event publication into separate places — they live next to each other so reviewers see both.
- `## Related skills` — references to `cyclone-edi` (the parsed result lands here), `cyclone-api-router` (the route that calls the store), `cyclone-tail` (the event drives the stream), `cyclone-tests` (write-path tests).
Keep under ~200 lines. No `references/` needed unless the boundary map grows.
- [ ] **Step 4: Verify**
```bash
head -5 .superpowers/skills/cyclone-store/SKILL.md
wc -l .superpowers/skills/cyclone-store/SKILL.md
grep -c "^## " .superpowers/skills/cyclone-store/SKILL.md
```
Expected: frontmatter present, ≤ 200 lines, 5 `## ` sections.
- [ ] **Step 5: Commit**
```bash
git add .superpowers/skills/cyclone-store/SKILL.md
git commit -m "feat(sp-skill-catalog): add cyclone-store skill (write paths + event contract)"
```
---
## Task 6: Phase 3, Skill 6 — `cyclone-api-router`
**Goal:** Codify FastAPI router conventions — `api_routers/`, `api_helpers.py` reuse, response shapes, error envelopes. Pairs with `cyclone-store`.
**Files:**
- Create: `.superpowers/skills/cyclone-api-router/SKILL.md`
- [ ] **Step 1: Survey the existing router layout**
```bash
ls backend/src/cyclone/api_routers/
grep -l "APIRouter" backend/src/cyclone/api_routers/*.py
head -30 backend/src/cyclone/api_helpers.py
```
Record the router filenames and which helpers exist in `api_helpers.py`.
- [ ] **Step 2: Sample one router**
Pick the smallest existing router (likely `acks.py` or `ta1_acks.py`) and read the first 50 lines. The skill's `## Patterns` section will reference its structure.
- [ ] **Step 3: Create the directory and write the SKILL.md**
```bash
mkdir -p .superpowers/skills/cyclone-api-router
```
Frontmatter:
```yaml
---
name: cyclone-api-router
description: "Cyclone FastAPI router conventions (api_routers/, api_helpers.py, response shapes, error envelopes). Use when: adding or changing an HTTP endpoint, splitting a route out of api.py, or wiring a new helper into api_helpers.py."
---
```
Body sections:
- `## When to use` — 4 bullets: adding an endpoint, splitting a route, adding a helper, defining an error response.
- `## Conventions` — at minimum:
1. **No new top-level routes in `api.py`.** Every endpoint lives in `backend/src/cyclone/api_routers/<topic>.py` as an `APIRouter`.
2. **Reuse `api_helpers.py`.** Common response shapes, error envelopes, and parsing helpers live there. Don't duplicate them in a router.
3. **Response shape.** Every successful response is a Pydantic model. Errors use the shared `ErrorEnvelope` (from `api_helpers.py`) with `code` and `message`.
4. **Mounting.** Routers are mounted in `api.py` with their prefix; keep prefix naming consistent (`/api/<resource>`).
5. **Streaming endpoints.** Use `StreamingResponse(media_type="application/x-ndjson")` — see `cyclone-tail` for the wire format.
6. **Tests.** Every new endpoint gets a `test_api_<topic>_<verb>.py` test under `backend/tests/`.
- `## Patterns` — three skeletons:
- A new `APIRouter` skeleton (`router = APIRouter(prefix="/api/foo", tags=["foo"])`).
- A `get_one` / `get_list` / `post_one` trio with Pydantic response models.
- An error envelope usage example.
- `## Anti-patterns` — at least:
- Don't import from `cyclone.api` into a router — the dependency runs the other way.
- Don't return raw dicts — always Pydantic.
- Don't bypass `CycloneStore` to query the ORM directly — see `cyclone-store`.
- `## Related skills` — references to `cyclone-store` (most routes call store methods), `cyclone-tail` (streaming endpoints), `cyclone-edi` (parse endpoints), `cyclone-tests` (endpoint tests).
Keep under ~200 lines.
- [ ] **Step 4: Verify**
```bash
head -5 .superpowers/skills/cyclone-api-router/SKILL.md
wc -l .superpowers/skills/cyclone-api-router/SKILL.md
grep -c "^## " .superpowers/skills/cyclone-api-router/SKILL.md
```
Expected: frontmatter present, ≤ 200 lines, 5 `## ` sections.
- [ ] **Step 5: Commit**
```bash
git add .superpowers/skills/cyclone-api-router/SKILL.md
git commit -m "feat(sp-skill-catalog): add cyclone-api-router skill (FastAPI conventions)"
```
---
## Task 7: Phase 4, Skill 7 — `cyclone-frontend-page`
**Goal:** Codify the React page-component pattern — TanStack Query, `use<X>` hook, drawer, URL state, `.test.tsx` sibling. Generalizes what `cyclone-tail` already covers.
**Files:**
- Create: `.superpowers/skills/cyclone-frontend-page/SKILL.md`
- [ ] **Step 1: Survey the page layout**
```bash
ls src/pages/
echo "---"
ls src/components/ui/ | head -20
```
Record the page list and which shadcn-style UI components are in `src/components/ui/`.
- [ ] **Step 2: Sample one page to cite**
```bash
head -50 src/pages/Claims.tsx
```
Use this in the skill's `## Patterns` to show the canonical page structure.
- [ ] **Step 3: Create the directory and write the SKILL.md**
```bash
mkdir -p .superpowers/skills/cyclone-frontend-page
```
Frontmatter:
```yaml
---
name: cyclone-frontend-page
description: "Cyclone React page conventions (TanStack Query, use<X> hook, drawer, URL state, .test.tsx sibling, Layout / PageHeader / Sidebar). Use when: adding a new page, refactoring an existing one, or wiring a drawer into a page."
---
```
Body sections:
- `## When to use` — 4 bullets: adding a new page, refactoring an existing page, wiring a drawer, sharing state via URL.
- `## Conventions` — at minimum:
1. **Page shape.** Every page in `src/pages/<Name>.tsx` exports a default `function <Name>()`. It uses `<Layout>`, sets a `<PageHeader>`, and renders a table or list.
2. **Data hook.** Each page pairs with a `use<X>` hook in `src/hooks/use<X>.ts` that returns `{ data, isLoading, error, ...tail }`. Pages do not call `fetch` directly.
3. **Tail.** If the page shows live data, it consumes the tail-stream pattern via `cyclone-tail`. The hook, not the page, owns the streaming subscription.
4. **Drawer.** Drawers (e.g. `ClaimDrawer`, `RemitDrawer`) live in `src/components/<DrawerName>/` and are wired via `useDrawerUrlState` so the URL reflects open state.
5. **Tests.** Every page gets a `src/pages/<Name>.test.tsx` sibling. Every hook gets a `src/hooks/use<X>.test.ts` sibling.
6. **UI primitives.** Use Radix-backed components from `src/components/ui/`. Don't pull in new UI libraries without discussion.
7. **Routing.** Pages register their route in `src/App.tsx`. Lazy-load if the page is heavy.
- `## Patterns` — three skeletons:
- A minimal `Claims.tsx`-style page (Layout + PageHeader + table + drawer).
- A `use<X>` hook skeleton (TanStack Query + tail).
- A drawer component skeleton with `useDrawerUrlState`.
- `## Anti-patterns` — at least:
- Don't `fetch` from inside a page component — go through the hook.
- Don't open a drawer via local component state — use `useDrawerUrlState` so deep-links work.
- Don't put domain logic in JSX — extract to the hook or a pure helper in `src/lib/`.
- `## Related skills` — references to `cyclone-tail` (live-data pages), `cyclone-api-router` (the endpoints pages call), `cyclone-tests` (page + hook tests), `cyclone-edi` (pages that show parsed EDI content).
Keep under ~200 lines.
- [ ] **Step 4: Verify**
```bash
head -5 .superpowers/skills/cyclone-frontend-page/SKILL.md
wc -l .superpowers/skills/cyclone-frontend-page/SKILL.md
grep -c "^## " .superpowers/skills/cyclone-frontend-page/SKILL.md
```
Expected: frontmatter present, ≤ 200 lines, 5 `## ` sections.
- [ ] **Step 5: Commit**
```bash
git add .superpowers/skills/cyclone-frontend-page/SKILL.md
git commit -m "feat(sp-skill-catalog): add cyclone-frontend-page skill (React page conventions)"
```
---
## Task 8: Phase 4, Skill 8 — `cyclone-cli`
**Goal:** Codify the CLI subcommand conventions in `cli.py` — argparse style, exit codes, smoke-test patterns. Last because nothing else depends on it.
**Files:**
- Create: `.superpowers/skills/cyclone-cli/SKILL.md`
- [ ] **Step 1: Survey the existing CLI**
```bash
grep -n "add_parser\|sub_parsers\|set_defaults" backend/src/cyclone/cli.py | head -20
grep -n "sys.exit\|return [0-9]\|return 1\|return 2" backend/src/cyclone/cli.py | head -10
```
Record the subcommand list and exit-code conventions.
- [ ] **Step 2: Sample one subcommand**
Pick the smallest (likely `validate`) and read its 20-30 lines. The skill's `## Patterns` will reference its structure.
- [ ] **Step 3: Create the directory and write the SKILL.md**
```bash
mkdir -p .superpowers/skills/cyclone-cli
```
Frontmatter:
```yaml
---
name: cyclone-cli
description: "Cyclone CLI subcommand conventions (cli.py, serve/parse/backup/rotate-key/validate, argparse style, exit codes, smoke tests). Use when: adding a CLI subcommand, changing exit codes, or working on operator-facing commands."
---
```
Body sections:
- `## When to use` — 4 bullets: adding a subcommand, changing exit codes, adding a smoke test, wiring a security-sensitive command (key rotation, backup).
- `## Conventions` — at minimum:
1. **Subcommand shape.** Every subcommand is a function `cmd_<name>(args) -> int` in `cli.py`. The `main()` parser dispatches via `set_defaults(func=...)`.
2. **Argparse style.** Use `argparse.ArgumentParser` sub-parsers. Long-form flags (`--rotate-key`) preferred over positional for safety.
3. **Exit codes.** `0` = success, `1` = user error (bad input, missing file), `2` = operator error (DB locked, key missing), `3` = security error (auth fail, key mismatch). Document in `cmd_<name>` docstring.
4. **Smoke test.** Every new subcommand gets a `backend/tests/test_cli_<name>.py` test using `subprocess.run` against the installed `cyc` entrypoint.
5. **Security-sensitive commands.** Key rotation (`cyc rotate-key`), backup (`cyc backup`), and any command touching `secrets.py` requires a `--confirm` flag and a dry-run path.
- `## Patterns` — three skeletons:
- A `cmd_<name>(args)` function with `argparse` setup.
- A `subprocess.run` smoke test.
- A `--confirm` / `--dry-run` pattern for security-sensitive commands.
- `## Anti-patterns` — at least:
- Don't `sys.exit()` from inside a subcommand — return the int and let `main()` exit.
- Don't print to stdout for errors — use `logging` (or `print(..., file=sys.stderr)` for top-level fatal errors).
- Don't add a subcommand without a smoke test in `backend/tests/test_cli_<name>.py`.
- `## Related skills` — references to `cyclone-store` (most subcommands touch the store), `cyclone-api-router` (`cyc serve` runs the FastAPI app), `cyclone-edi` (`cyc parse <file>`), `cyclone-tests` (CLI smoke tests).
Keep under ~200 lines.
- [ ] **Step 4: Verify**
```bash
head -5 .superpowers/skills/cyclone-cli/SKILL.md
wc -l .superpowers/skills/cyclone-cli/SKILL.md
grep -c "^## " .superpowers/skills/cyclone-cli/SKILL.md
```
Expected: frontmatter present, ≤ 200 lines, 5 `## ` sections.
- [ ] **Step 5: Commit**
```bash
git add .superpowers/skills/cyclone-cli/SKILL.md
git commit -m "feat(sp-skill-catalog): add cyclone-cli skill (CLI conventions)"
```
---
## Task 9: Final — README catalog section
**Goal:** Advertise the catalog to human readers and to AI agents reading the README on first contact.
**Files:**
- Modify: `README.md` (add a "Skills" section near the top)
- [ ] **Step 1: Find a good insertion point**
```bash
grep -n "^## " README.md | head -20
```
Pick a sensible location — typically after the existing "Install" or "Dev" section.
- [ ] **Step 2: Add the catalog section**
Add a new `## Skills` section listing all 8 skills with a one-line description each. Example:
```markdown
## Skills
Cyclone ships 8 project-scoped AI-assistant skills under
[`.superpowers/skills/`](.superpowers/skills/). Each one codifies the
conventions for a major subsystem so the next contributor (human or
AI) gets the lay of the land automatically.
| Skill | Owns |
|-------|------|
| [`cyclone-spec`](.superpowers/skills/cyclone-spec/SKILL.md) | The SP-N spec → plan → implement → merge flow. |
| [`cyclone-tests`](.superpowers/skills/cyclone-tests/SKILL.md) | pytest + vitest fixture patterns, prodfiles drop-in. |
| [`cyclone-edi`](.superpowers/skills/cyclone-edi/SKILL.md) | EDI parser/validator conventions (837P/835/999/270/271/277CA/TA1). |
| [`cyclone-tail`](.superpowers/skills/cyclone-tail/SKILL.md) | Live-tail streaming wire format and the hook triplet. |
| [`cyclone-store`](.superpowers/skills/cyclone-store/SKILL.md) | Store write-paths, pubsub event contract, SP21 split map. |
| [`cyclone-api-router`](.superpowers/skills/cyclone-api-router/SKILL.md) | FastAPI router conventions (`api_routers/`, `api_helpers.py`). |
| [`cyclone-frontend-page`](.superpowers/skills/cyclone-frontend-page/SKILL.md) | React page conventions (TanStack Query, drawer, URL state). |
| [`cyclone-cli`](.superpowers/skills/cyclone-cli/SKILL.md) | CLI subcommand conventions (`cli.py`, exit codes, smoke tests). |
Skills auto-load by description match — no slash command needed.
```
- [ ] **Step 3: Verify the section reads cleanly**
```bash
grep -n -A 16 "^## Skills" README.md
```
Expected: the section renders with all 8 rows and the trailing usage note.
- [ ] **Step 4: Commit**
```bash
git add README.md
git commit -m "docs(readme): add Skills section linking the 8-skill catalog"
```
---
## Self-review (run after writing the plan, before executing)
- [ ] **Spec coverage.** Skim each section of `docs/superpowers/specs/2026-06-21-cyclone-skill-catalog-design.md`. Confirm:
- Spec §"The catalog" → Tasks 1-8 each create the matching skill. ✅
- Spec §"Per-skill structure" → enforced by the shared template locked in Task 0. ✅
- Spec §"Loading & cross-references" → every Task has a `## Related skills` section. ✅
- Spec §"Build order / phasing" → Phase order = Task order (1-2 foundations, 3-4 domain, 5-6 backend, 7-8 frontend+CLI). ✅
- Spec §"Verification" → covered by verification steps in each task + Task 9 README addition. ✅
- [ ] **Placeholder scan.** No "TBD" / "TODO" / "implement later" in the plan. Each task's body content is specified (frontmatter exact, sections listed, key bullets enumerated).
- [ ] **Type / name consistency.**
- Skill names: `cyclone-spec`, `cyclone-tests`, `cyclone-edi`, `cyclone-tail`, `cyclone-store`, `cyclone-api-router`, `cyclone-frontend-page`, `cyclone-cli`. Same in every task. ✅
- Commit prefix: `feat(sp-skill-catalog): …` everywhere. ✅
- Section names: `## When to use`, `## Conventions`, `## Patterns`, `## Anti-patterns`, `## Related skills`. Same in every task. ✅
Plan passes self-review.