@dennisrongo/skills 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/package.json +1 -1
- package/skills/api-contract-review/SKILL.md +67 -0
- package/skills/e2e-verify/SKILL.md +88 -0
- package/skills/migration-safety/SKILL.md +59 -0
- package/skills/safe-refactor/SKILL.md +62 -0
- package/skills/security-review/SKILL.md +65 -0
- package/skills/upgrade-deps/SKILL.md +60 -0
- package/skills/write-tests/SKILL.md +68 -0
package/README.md
CHANGED
|
@@ -92,24 +92,32 @@ node bin/claude-skills.js list
|
|
|
92
92
|
|
|
93
93
|
| Skill | What it does |
|
|
94
94
|
|---|---|
|
|
95
|
+
| [`api-contract-review`](./skills/api-contract-review/SKILL.md) | Review an API's contract as a **promise to consumers you can't see** — HTTP endpoints, webhooks, published events, SDK-facing types. Two evidence rules do the work: a **breaking** verdict requires a before/after diff of a consumer-visible element (`git show` the old DTO/spec vs. the working tree — removed/renamed fields, type changes, requiredness tightened on requests, status-code or enum semantics changed under the same name), and an **inconsistency** finding requires citing the in-repo precedent being violated (error envelope, naming, pagination style, auth placement — grep the siblings first or don't flag it). Also checks the day-one invariants that can't be retrofitted: collections paginate from the start, retryable writes are idempotent, errors carry a machine-usable `code`, no ORM entities serialized wholesale, timestamps/money carry explicit units. Reports **Breaking / Design / Questions**, ranked — zero findings is a valid outcome. Distinct from [`code-review`](./skills/code-review/SKILL.md) (implementation quality): this reviews the *surface*. Triggers on "review this API", "is this a breaking change", "check backward compatibility", "review the contract", or `/api-contract-review`. |
|
|
95
96
|
| [`code-review`](./skills/code-review/SKILL.md) | Production-readiness review of **uncommitted** working-tree changes (staged + unstaged). Hunts DRY violations, dead code, leaky abstractions, premature abstraction, magic values, missing error handling, debug residue, missing migrations / flags / logs, and other common best-practice gaps — prioritized correctness → DRY/design → tests → security → performance → production-readiness → readability. On non-trivial diffs, convenes a **lens council**: parallel `Explore` sub-agents reviewing through distinct lenses (correctness / design / security / tests / production-readiness), followed by an **adversarial critique round** that challenges each lens's blocking flags against context from the others — false positives demoted, contradictions surfaced for the user, multi-lens findings promoted. Small diffs skip the council. Auto-detects and runs the project's tests and build (`npm`, `pytest`, `dotnet`, `go`, `cargo`, `Makefile`, monorepo orchestrators) and gates the verdict on them being green. **Never edits code unprompted** — produces a categorized report (`blocking` / `suggestion` / `nit` / `praise`) with `file:line` citations, then asks per-finding before fixing. Distinct from [`pr-review`](./skills/pr-review/SKILL.md), which scopes to committed branch work. Triggers on "code review", "review the diff", "is this production ready", "DRY check", or `/code-review`. |
|
|
96
97
|
| [`codebase-explainer`](./skills/codebase-explainer/SKILL.md) | Produce a durable onboarding artifact for a repo — writes `ONBOARDING.md` (or `docs/ONBOARDING.md` if `docs/` exists) with a tight **read this first** minimum, system overview, dependency map (top-level prod deps + how each is actually used in *this* codebase, with key call site), startup flow (entry point → bootstrap → config), auth flow (or an explicit "no auth detected" when there isn't one), and the 5–15 important files — every claim backed by a `file:line` citation. Walks the repo with **parallel Explore sub-agents** to stay context-safe on big projects, refreshes an existing `ONBOARDING.md` in place instead of rewriting from scratch, and is opinionated about what *not* to include (no dev/test/types deps, no 40-file "important files" lists, no fabricated auth flows, no dates that rot). Built for revisiting a project after months away — and for new teammates landing in an unfamiliar repo. Composes with [`improve-codebase-architecture`](./skills/improve-codebase-architecture/SKILL.md) when shallow-module clusters surface and [`handoff`](./skills/handoff/SKILL.md) for session-level state. Triggers on "explain this codebase", "onboard me", "give me a tour", "where do I start", "I haven't looked at this in months", or `/codebase-explainer`. |
|
|
97
98
|
| [`conventional-commits`](./skills/conventional-commits/SKILL.md) | Write git commit messages that follow the [Conventional Commits](https://www.conventionalcommits.org/) spec (`feat`, `fix`, `chore`, `docs`, …), auto-prefixed with the ticket number from the current branch (e.g. `feature/12345-...` → `#12345`) and a project tag (`API` / `CLIENT` / `CONSOLE` / `DB`) when detectable from the diff. Both prefixes are omitted when they don't apply. Triggers on commit-message requests. |
|
|
98
99
|
| [`diagnose`](./skills/diagnose/SKILL.md) | Disciplined diagnosis loop for hard bugs and performance regressions — reproduce → minimise → hypothesise → instrument → fix → regression-test. Forces a fast, deterministic feedback loop before any guessing. On non-trivial cases, Phase 3 convenes a **hypothesis council**: parallel `Explore` sub-agents — one per candidate hypothesis — each defending its case with falsifiable predictions and `file:line` evidence from the actual codebase, followed by a cross-examination round that drops defenders who couldn't find supporting evidence and ranks the survivors. Trivial bugs skip the council and use 1–2 inline hypotheses. Uses tagged `[DEBUG-...]` instrumentation that's trivially cleaned up, and ends with a post-mortem on what would have prevented the bug. Triggers on "diagnose this" / "debug this" / bug reports / flaky tests / perf regressions. |
|
|
99
100
|
| [`dotnet-onion-api`](./skills/dotnet-onion-api/SKILL.md) | Scaffold a new .NET solution (Web API + Worker microservices) using ONION architecture and EF Core, codifying battle-tested layered patterns and explicitly removing common legacy pitfalls (sproc-centric repos with reflection, EF6 on netstandard2.1, polling console workers, mutable base-service state, missing `CancellationToken`). Three modes — full solution scaffold, add-a-feature slice, add-a-worker microservice. Resolves TFM and NuGet versions at scaffold time (not hard-coded). |
|
|
101
|
+
| [`e2e-verify`](./skills/e2e-verify/SKILL.md) | Verify a change end-to-end in a real browser, routed by one question: **who needs this check to run again?** Nobody → **ephemeral**: run [Expect](https://github.com/millionco/expect) if installed (explicit non-prod `--url`, `--no-cookies` by default) or have Claude drive the browser directly — walking the flows the *diff* touches, verifying via DOM/accessibility state, and checking the console + network log after each flow (a page that looks right while logging exceptions is a finding). CI-forever (money/auth/signup/checkout/deletion) → **durable**: Playwright tests committed to the repo under [`write-tests`](./skills/write-tests/SKILL.md) discipline — extend the repo's existing e2e setup, `getByRole`/`getByTestId` selectors never CSS chains, no sleeps, API-seeded independent tests, every test **proven red-capable** with both runs quoted. The evidence rule that holds across every engine: an AI-walked flow yields *"no issues found in the paths walked"* with paths enumerated and observations quoted — never "e2e passes"; unobserved behavior is "not verified", never "works". Hard gate: no production targets, no real user cookies. Triggers on "verify this in the browser", "test it end to end", "e2e test this", "write playwright tests", "run expect", "smoke test the UI", or `/e2e-verify`. |
|
|
100
102
|
| [`grill-with-docs`](./skills/grill-with-docs/SKILL.md) | Interview-driven design review. Stress-tests a plan, RFC, or feature idea against the project's existing domain model and documented decisions — one question at a time with `AskUserQuestion`, sharpening fuzzy terminology and surfacing contradictions with the codebase. Updates `CONTEXT.md` (glossary) inline as terms resolve and writes ADRs to `docs/adr/` only when the decision is hard to reverse, non-obvious, and had real trade-offs. Writes no production code — composes with [`plan-and-build`](./skills/plan-and-build/SKILL.md) for the implementation handoff. Triggers on "grill me", "stress-test this plan", "challenge this design", "/grill-with-docs", or a pasted RFC. |
|
|
101
103
|
| [`handoff`](./skills/handoff/SKILL.md) | Capture a session hand-off before context runs out — writes a dated `.claude/handoffs/*.md` (objective, progress, decisions, files, open issues, ready-to-paste next-session prompt) plus a lightweight memory pointer so a fresh Claude session can resume cleanly. |
|
|
102
104
|
| [`improve-codebase-architecture`](./skills/improve-codebase-architecture/SKILL.md) | Surface architectural friction and propose **deepening opportunities** — refactors that collapse clusters of shallow modules into one deep module with a real seam. Walks the codebase with an Explore sub-agent, applies the **deletion test** to suspected pass-throughs, presents numbered candidates (files / problem / solution / benefits) using `CONTEXT.md` for the domain and a strict architecture glossary (module / interface / seam / depth / leverage / locality) for the structure, then drops into a grilling loop with optional parallel sub-agent interface design ("Design It Twice"). Updates `CONTEXT.md` inline as new concepts get named and offers an ADR only when a rejection is load-bearing. Composes with [`grill-with-docs`](./skills/grill-with-docs/SKILL.md) for glossary + ADR discipline and [`plan-and-build`](./skills/plan-and-build/SKILL.md) for the implementation handoff. Writes no production code. Triggers on "improve architecture", "architecture review", "find refactoring / deepening opportunities", "find shallow modules", "make this more testable", or `/improve-codebase-architecture`. |
|
|
105
|
+
| [`migration-safety`](./skills/migration-safety/SKILL.md) | Review a schema migration for **production safety under live traffic** — the three failure axes are locks, deploy ordering, and irreversibility. Destructive ops (drops, renames — a rename IS a drop+add to running old code — type narrowing, `NOT NULL` tightening) are blockers unless an **expand → migrate readers → contract-in-a-later-release** plan is stated. Lock findings must name the engine, the specific lock, and its duration driver (no "might be slow" — and no flagging what's actually free, like a defaulted nullable add on modern PG/SQL Server). Checks the deploy-order contract **both ways** (old code on new schema during rollout, new-code data on old schema during rollback), separates backfills from DDL, and demands an honest rollback verdict per migration (an auto-generated down that drops a column does *not* restore its data). **Never executes** migrations or any SQL. Distinct from [`sql-review`](./skills/sql-review/SKILL.md) (T-SQL antipatterns in procs): this judges schema changes against the deploy timeline. Triggers on "review this migration", "is this migration safe", "will this lock the table", "zero-downtime migration", or `/migration-safety`. |
|
|
103
106
|
| [`nextjs-app-router`](./skills/nextjs-app-router/SKILL.md) | Scaffold a new Next.js (App Router) **fullstack** app — TypeScript, **NextAuth (Auth.js v5)**, **Prisma + PostgreSQL**, Route Handlers as the backend, Redux Toolkit + RTK Query, Tailwind + shadcn/ui (Radix), React Hook Form + Zod. **API-driven by deliberate choice**: pages are `'use client'`, all data flows UI → RTK Query → `/api/**` Route Handlers → Prisma. No `fetch()` in server components, no Server Actions, no async `page.tsx`. Confirms the database (Postgres + Prisma) and NextAuth providers with the user before writing files. Forbids the usual pitfalls (custom JWT cookies alongside NextAuth, multiple `createApi`/`PrismaClient` instances, `serializableCheck: false`, `@ts-ignore`, mixed `moment`/`date-fns`, `styled-components` alongside Tailwind, case-sensitive folder dupes, `dangerouslySetInnerHTML` without sanitization, Route Handlers that skip `requireSession()` or trust client-sent user IDs, `prisma db push` in CI). Three modes — full project scaffold, add-a-feature slice (page + form + Route Handler + Zod schema + RTK Query endpoints + Prisma model), add-an-API-slice. Resolves package versions at scaffold time (not hard-coded). |
|
|
104
107
|
| [`plan-and-build`](./skills/plan-and-build/SKILL.md) | Plan-first feature builder. Grills the user about the feature (à la `grill-with-docs`) until the design is unambiguous, detects the project's stack and conventions, presents a plan, and gates on `ExitPlanMode` approval before writing any code. When Phase 3 hits a genuine design fork (service-layer vs. CQRS, new table vs. nullable columns, sync vs. background job, etc.) and Phase 2 didn't settle it, runs **Design It Twice**: two parallel sub-agents each draft the BEST plan for one side of the named axis, then an inline debate names three wins for each and recommends one — the user sees a single recommended plan with a one-line "considered alternative" note so they can redirect with one sentence. Skipped when an existing pattern in the repo already dictates the approach. Builds TDD-first with NUnit when a .NET API changes — appending to the matching test class if one already exists rather than forking a parallel one — reuses existing patterns, keeps comments minimal, and generates EF Core / migration files **without ever** running `dotnet ef database update` or any DDL/SQL against the user's database. Triggers on "build/add/implement a feature", "/plan-and-build", or a pasted feature spec. |
|
|
105
108
|
| [`pr-review`](./skills/pr-review/SKILL.md) | Structured review of a local branch, **grouped per `#NNN` task** referenced in commit messages, prioritized correctness → design → tests → security → performance → readability, with categorized feedback (`blocking` / `suggestion` / `question` / `nit` / `praise`). On non-trivial tasks, convenes a **per-task lens council**: parallel `Explore` sub-agents through distinct lenses (correctness / design / security / tests) on that task's diff only, followed by an **adversarial critique round** that demotes false-positive blockers when another lens defuses them, surfaces contradictions as `question` items for the user, and promotes findings raised by multiple lenses independently. Small tasks skip the council. The council never crosses task boundaries — each `#NNN` gets its own verdict. Triggers on "review my PR", "review the diff", "review my branch", or `/pr-review`. |
|
|
109
|
+
| [`safe-refactor`](./skills/safe-refactor/SKILL.md) | Execute a behavior-preserving refactor as a **sequence of proofs, not a rewrite**. Writes the behavior contract first (public surface, side effects, error types — the falsifiable definition of "preserved"), audits the safety net (every contract item mapped to a covering test, characterization tests written via [`write-tests`](./skills/write-tests/SKILL.md) for the gaps — **no net, no refactor**, and "the change is simple" is not an exemption), locks a quoted-green baseline, then moves in single mechanical transformations with the suite green between steps. Renames are **grep-verified repo-wide** (strings, routes, reflection, and serialized names don't compile-check). The **assertion-change tripwire**: if fixing a red step means changing a test's expected value, stop — that's a behavior change wearing a refactor's clothes; surface it, never silently absorb it. Ends with an adversarial diff read hunting the smuggled change (reordered side effects, dropped `await`, widened catch). Completes the chain: [`improve-codebase-architecture`](./skills/improve-codebase-architecture/SKILL.md) names the target, this makes the move. Triggers on "refactor this", "clean this up without changing behavior", "extract/inline/split", "rename across the codebase", or `/safe-refactor`. |
|
|
110
|
+
| [`security-review`](./skills/security-review/SKILL.md) | Attacker's-eye security audit of a diff, branch, or module — maps trust boundaries, then walks a fixed catalog: missing authn **and object-level authz** (IDOR is the most common real miss), client-sent identity trusted in queries, injection (SQL / command / path traversal / XSS), secrets in code or logs (a committed secret means *rotate*, not delete-the-line), SSRF, open redirects, insecure deserialization, mass assignment, crypto misuse, dependency CVEs (only via an actual audit-tool run with output quoted — otherwise `dependencies: not checked`). The gate that keeps it honest: every finding must state a one-sentence **attack path** (*who* does X → gains Y) or be demoted to hardening advice; every finding is re-derived by tracing input to sink (untraced pattern-matches are labeled `unconfirmed`); the report never claims "secure" — only "nothing found in the classes checked", plus the explicit unchecked list. Zero findings is a valid outcome. Never edits code unprompted. Distinct from the security *lens* in [`code-review`](./skills/code-review/SKILL.md) / [`pr-review`](./skills/pr-review/SKILL.md): this is the dedicated deep pass for trust-boundary changes. Triggers on "security review", "is this secure", "check for vulnerabilities", "audit the auth", "threat model this", or `/security-review`. |
|
|
106
111
|
| [`ship-it`](./skills/ship-it/SKILL.md) | Pre-launch **operational-readiness** gate for a feature, release, or branch — the complement to [`code-review`](./skills/code-review/SKILL.md). Walks a fixed 10-category checklist (logging, error handling, telemetry, feature flags, migrations, rollback strategy, secrets, local-first storage, auth, update strategy) against the named scope and produces a structured report with PASS / GAP / N/A per item — every PASS backed by a `file:line` citation, every GAP labelled `no evidence found at <path>`, every N/A justified in one line. The final verdict groups findings as **Blocking** (secrets in code, missing authz on a new endpoint, destructive migration without rollback, no way to disable the change in prod) / **Should-fix** / **N/A with reason** / **Passing**, then asks per-blocker whether to draft a fix. **Never edits code unprompted** and **forces scope before auditing** — a PR, a flag, a release tag, or a module — so the output stays actionable. Distinct from `code-review` (diff quality) and `pr-review` (branch / per-task review): `ship-it` is the operational gate that catches what diff-level reviews don't surface. Triggers on "is this ready to ship?", "ship-it check", "production checklist", "pre-launch checklist", "release readiness", or `/ship-it`. |
|
|
107
112
|
| [`sql-review`](./skills/sql-review/SKILL.md) | Pre-commit SQL code review for uncommitted `.sql` changes (staged + unstaged). Detects 17 antipattern classes that map to **real production incident causes** — `sp_send_dbmail` in CATCH blocks (masks the real exception as a misleading permission denial), broken retry patterns (`@retry` declared without a surrounding `WHILE` loop), swallowing CATCH blocks (no `THROW`/`RAISERROR`/log), **new tables created without a primary key or any index** (the silent perf-then-deadlock killer), parameter-vs-column type mismatches (8152 truncation risk), `EXEC()` string concatenation without `sp_executesql` parameters (SQL injection), `NOLOCK` inside transactional write paths, `UPDATE`/`DELETE` without `WHERE`, cursors without `READ_ONLY FORWARD_ONLY LOCAL`, hardcoded environment values (emails, server names, paths, linked servers), cross-DB references like `msdb.dbo.*`, missing `SET NOCOUNT ON`, missing `GRANT EXECUTE` on `CREATE PROC`, `BEGIN TRANSACTION` outside `TRY`/`CATCH` with `XACT_STATE` handling, and vestigial control-flow comments hinting at refactor leftovers (e.g. `-- end while loop` with no `WHILE`). **Scope-aware** — full-file scan for new files, diff-only scan for modified files so legacy antipatterns in untouched parts of a large SP don't flood the report. Categorizes findings as `BLOCKER` / `WARN` / `INFO` with `file:line` citations and per-finding fix recommendations. **Never edits SQL unprompted** — produces the report, then asks per fix. Distinct from [`code-review`](./skills/code-review/SKILL.md), which carries the general best-practice catalog without SQL-specific patterns. Triggers on `/sql-review`, "review my SQL", "review the SQL diff", "lint the SQL", "check my SQL changes", "SQL pre-commit check", or "audit my stored proc". |
|
|
108
113
|
| [`task-executor`](./skills/task-executor/SKILL.md) | Disciplined execution loop for a single, already-defined task — Understand → Inspect → Plan → Execute incrementally → Validate after every change → Track assumptions → Update progress. Forces a strict per-turn output format with six fixed sections (**Goal** / **Current understanding** / **Files to inspect** / **Plan** / **Progress** / **Risks** / **Assumptions**) so the work stays legible and resumable instead of devolving into ad-hoc edits across turns. When the inspection working set spans multiple layers (controller + service + persistence + tests), Phase 2 convenes an **inspection council**: parallel `Explore` sub-agents — one per layer slice — each map their area and return `file:line`-cited findings on existing patterns, wiring points, and sibling test classes; the main session aggregates the results into `Current understanding` so the working context window stays free for the strict per-turn output the executional phase keeps emitting. Small inspection sets (≤ ~5 files, one layer) skip the council and read inline. Enters Plan Mode after inspection and gates on `ExitPlanMode` approval before writing any code; every plan step is then one logical change followed by an immediate validation (test, build, type-check, curl, page load) before the next checkbox ticks. Assumptions are tracked explicitly until confirmed by code or the user — and promoted into `Current understanding` or killed, never silently carried. Composes downward from [`plan-and-build`](./skills/plan-and-build/SKILL.md) (which interviews the user to design the feature) and routes back to it when the user invokes this skill on a fuzzy spec. Triggers on `/task-executor`, "Work on task: …", or any concrete, already-defined task handed to Claude for execution. |
|
|
109
114
|
| [`tauri-2-app`](./skills/tauri-2-app/SKILL.md) | Scaffold a new Tauri 2 desktop app (Rust backend + TypeScript/React frontend) using a thin-frontend / rich-Rust-backend architecture with modular `commands/`, `state/`, `storage/`, `platform/` traits, `error/` macros, single-instance + updater plugins wired correctly, capability JSON per window, encrypted secrets at rest, `spawn_blocking` for sync work, and typed frontend command hooks — while forbidding common pitfalls (committed `.backup`/`.orig`/`.temp` files, plaintext API keys in `settings.json`, tokens in `localStorage`, `cfg!(target_os)` in command bodies instead of trait-based platform code, hand-rolled date math instead of `chrono`, raw `std::fs` bypassing capability checks, blocking I/O inside async commands, missing `windows_subsystem = "windows"` in `main.rs`, `devtools: true` in release, hardcoded bundle identifiers / updater pubkeys / CDN URLs). Three modes — full project scaffold, add-a-command end-to-end, add-a-Rust-module slice. Resolves Cargo + npm versions at scaffold time (not hard-coded). |
|
|
110
115
|
| [`think-like-fable`](./skills/think-like-fable/SKILL.md) | An operating manual for rigorous reasoning, written as a senior operator handing their craft to a sharp junior — applied to whatever task is at hand rather than replacing it. Eight disciplines, each with the procedure, a worked example, and the failure it prevents: read the need behind the literal ask, decompose into **independently checkable** pieces, spend effort where the risk lives (not where the work is easy or interesting), verify load-bearing claims by **re-deriving** them instead of recognizing them ("a claim about code you haven't opened is a hypothesis"), tag every claim **verified / inferred / assumed** in the text, attack your own conclusion before handing it over, and communicate answer → reasoning → risk in that order. Names the seven mistakes that look like competence and aren't (thoroughness theater, fluent overclaiming, premature agreement, complexity as signal, silent scope repair, momentum completion, deference to your own prior output) and ends with a five-question self-test to run on every answer before sending. Triggers on "think like fable", "/think-like-fable", "be rigorous", "are you sure?", "don't guess", or any high-stakes analysis where a confident wrong answer is worse than a slow right one. |
|
|
116
|
+
| [`upgrade-deps`](./skills/upgrade-deps/SKILL.md) | Upgrade dependencies as a **verification exercise, not a version edit**. Inventory comes from the tool (`npm outdated` / `dotnet list package --outdated` / `pip list --outdated` / `cargo outdated` — never from training-data memory of "latest"), baseline suite quoted green before anything moves, patch/minor bumps batched, **majors strictly one at a time**: read the actual changelog across every crossed version (a breaking-change claim without a citation is a hypothesis), grep the repo for each breaking API (cite the call sites, or state the negative: "no usage — grep for `X` returned nothing"), list behavioral changes that don't grep (changed defaults, stricter parsing) as named **runtime risks** with the test that would catch each — or the admission that none would. Red bump → one informed retry → revert, mark **blocked** with the quoted error, move on. Never silences peer conflicts with `--force`/`--legacy-peer-deps` without surfacing the override. Reports a per-package table (from → to, breaking changes affecting *this* repo, evidence, result) plus the honest residue. Triggers on "upgrade dependencies", "update packages", "bump X", "is it safe to upgrade", "fix the npm audit", "handle the dependabot PRs", or `/upgrade-deps`. |
|
|
111
117
|
| [`write-a-skill`](./skills/write-a-skill/SKILL.md) | Author a new Claude Code skill — interview-driven scaffolding that produces a properly-structured `SKILL.md` (trigger-rich YAML description, "When to use", workflow, examples, anti-patterns), drops it in the right location (library `skills/`, project `./.claude/skills/`, or global `~/.claude/skills/`), updates the README skills table when extending this library, and runs a review checklist focused on the failure mode that matters most — under-triggering descriptions. Triggers on "create/write/add a skill", "/write-a-skill", or a pasted SKILL.md URL with "one like this". |
|
|
112
118
|
|
|
119
|
+
| [`write-tests`](./skills/write-tests/SKILL.md) | Author tests whose only job is to **fail when the behavior breaks** — everything else (coverage %, test count, green checkmarks) is a gameable proxy. Ranks WHAT to test by risk (error paths, money, auth, deletion, retry/idempotency, date math outrank happy paths), checks for existing coverage first (**zero new tests is a valid outcome** — cite the existing test), asserts observable behavior only (return values, persisted state, real-boundary calls — never internal call order), and **proves every new test can fail**: TDD-red first, or mutate the behavior → confirm red → revert → confirm green, both runs quoted. A test never seen red is decoration; a green claim without a quoted run is `tests: not run`. Mocks only boundaries you don't own (network, clock, fs, db) using the repo's existing double convention. For untested legacy code: **characterization tests** that pin actual behavior — bugs included, labeled — before anything changes ([`safe-refactor`](./skills/safe-refactor/SKILL.md) depends on this). Test pain gets reported as design feedback, not mocked through. Triggers on "write tests", "add tests / coverage", "test this", "add a regression test", "TDD this", "characterization tests", or `/write-tests`. |
|
|
120
|
+
|
|
113
121
|
Run `skills list` to see this list with install status, or browse [`skills/`](./skills) directly.
|
|
114
122
|
|
|
115
123
|
## How Claude Code finds these skills
|
package/package.json
CHANGED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-contract-review
|
|
3
|
+
description: Review an API's contract as a promise to consumers — detects breaking changes by diffing the before/after surface (removed/renamed fields, type changes, tightened requiredness, status-code changes), and judges design by the repo's OWN precedent (error envelope, naming, pagination, auth placement) with every consistency finding citing the in-repo convention being violated. Covers versioning, idempotency on retryable writes, pagination on collections, and status-code semantics. Use this skill whenever the user says "review this API", "review the endpoint", "API design review", "is this a breaking change", "check backward compatibility", "review the contract", "review this OpenAPI/swagger spec", or "/api-contract-review" — even if they don't name the skill. Distinct from code-review (implementation quality); this reviews the SURFACE consumers depend on.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# API Contract Review
|
|
7
|
+
|
|
8
|
+
An API contract is a promise made to code you can't see and can't fix. This skill reviews the promise, not the implementation: what a consumer can observe — paths, methods, fields, types, requiredness, status codes, error shapes, headers, ordering and pagination semantics — and whether this change keeps, extends, or breaks it. Two evidence rules do the work: **breaking** requires a before/after diff of a consumer-visible element, and **inconsistent** requires a citation of the in-repo precedent being violated.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- The user says "review this API", "API design review", "is this a breaking change", "check backward compat", "review the contract", "review this OpenAPI spec", "/api-contract-review".
|
|
13
|
+
- A new endpoint, GraphQL type, gRPC service, webhook payload, or event schema is being added or changed.
|
|
14
|
+
- `plan-and-build` is designing an endpoint and the contract deserves its own pass before implementation.
|
|
15
|
+
|
|
16
|
+
Do **not** auto-trigger for internal function signatures or module interfaces (that's `code-review` / `improve-codebase-architecture` territory) — this skill is for surfaces crossed by consumers who deploy independently: HTTP APIs, published events, webhooks, SDK-facing types.
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
1. **Establish the before and the after.** For a change: the old contract is `git show` of the previous handler/spec/DTO, the new one is the working tree — read both; a breaking-change verdict without the before-state in hand is a hypothesis. For a brand-new endpoint there is no "before", so the compat section reduces to forward-compat design (step 4). Identify the consumers if discoverable (other repos, mobile apps, webhook subscribers, "unknown external") — unknown consumers raise the cost of every breaking change and the report should say so.
|
|
21
|
+
2. **Diff the consumer-visible surface for breaking changes.** Breaking = an existing valid consumer interaction stops working or changes meaning. The checklist, each judged by before/after citation:
|
|
22
|
+
- Removed or renamed: path, method, field, enum value, header.
|
|
23
|
+
- Type changes (string→int, scalar→object, nullable→non-null in responses).
|
|
24
|
+
- Requiredness tightened on **requests** (new required field/param, stricter validation rejecting previously-valid input).
|
|
25
|
+
- Semantics changed under the same name: status code for the same condition, default value, sort order consumers observe, pagination behavior, error `code` values, ID format.
|
|
26
|
+
- Response fields **removed or now-sometimes-absent** (additive response fields are non-breaking for tolerant readers — but check the repo's serializer isn't strict).
|
|
27
|
+
- ❌ "Changing this field feels risky." — no before/after, not a verdict.
|
|
28
|
+
- ✅ "Breaking: `status` response field was `\"active\"|\"disabled\"` (git show `UserDto.cs:14`), now adds `\"suspended\"` — consumers with exhaustive enum handling will throw. New enum values in responses are breaking unless the contract documents open enums; nothing in the spec says so."
|
|
29
|
+
3. **Judge design by local precedent, not by taste.** Before flagging anything as inconsistent, grep the sibling endpoints and read at least two. Then check the new surface against what THIS repo does: error envelope shape (find the canonical one; new endpoint must return it, not a fresh ad-hoc `{message}` — cite both), naming (`camelCase` vs `snake_case`, plural collections, ID field naming), auth placement (same middleware/guard pattern as siblings — compose with `security-review` if it's absent entirely), pagination style (cursor vs offset, envelope keys), timestamp format, route casing. A consistency finding without the cited precedent is an opinion; drop it or label it one.
|
|
30
|
+
4. **Check the design invariants that hurt later.** These apply even with zero consumers today, because they're near-impossible to retrofit:
|
|
31
|
+
- **Collections paginate from day one** — an unpaginated list endpoint is a time bomb; adding pagination later breaks every consumer.
|
|
32
|
+
- **Retryable writes are idempotent** — POSTs that create money-adjacent or non-deduplicable resources need an idempotency key or a natural dedup constraint; name which.
|
|
33
|
+
- **Status codes carry their standard meaning** — 200-with-error-body, 404 for authz denials (unless deliberate resource-hiding, then say so), 500 for validation failures: each flagged with the observed code and the condition.
|
|
34
|
+
- **Errors are machine-usable** — a stable `code` field, not prose-only messages consumers will regex.
|
|
35
|
+
- **Nothing leaks that can't be unshipped** — internal IDs, stack traces, ORM entity fields serialized wholesale (grep: does the handler return the entity type or a DTO?). Every response field is a permanent promise; flag fields with no evident consumer need.
|
|
36
|
+
- **Timestamps and money have explicit units/zones/currency** — `amount: 4200` with no currency or minor-unit convention is a production incident on layaway.
|
|
37
|
+
5. **Report in three ranked sections.** (a) **Breaking** — each with before/after citations and the migration path (version bump per the repo's existing strategy, additive alternative, deprecation window); (b) **Design** — invariant violations and precedent-cited inconsistencies; (c) **Questions** — semantics you couldn't determine from code (is this enum open? is this endpoint consumer-facing at all?). Zero findings is a valid outcome — an additive, precedent-matching endpoint should get a short pass, not invented nits. Never edit code or specs unprompted.
|
|
38
|
+
|
|
39
|
+
## Examples
|
|
40
|
+
|
|
41
|
+
### Example 1: "is this a breaking change?"
|
|
42
|
+
|
|
43
|
+
**User:** "I'm renaming `customer_id` to `customerId` in the orders response — breaking?"
|
|
44
|
+
|
|
45
|
+
**Claude:** Diffs before/after (breaking: field rename is remove+add for every existing reader), greps the repo — every other response uses `snake_case`, so the rename also breaks local convention rather than fixing it (cited: 9 sibling DTOs). Recommends: keep `customer_id`; if the target is a casing migration, that's an API-version-level change, not a field edit.
|
|
46
|
+
|
|
47
|
+
### Example 2: new endpoint pass
|
|
48
|
+
|
|
49
|
+
**User:** "Review the contract for the new `/api/reports` endpoints before I build them."
|
|
50
|
+
|
|
51
|
+
**Claude:** No before-state → forward-compat pass: flags the unpaginated `GET /reports` (day-one invariant), flags `POST /reports/generate` returning 200 with the result inline when generation takes minutes (should be 202 + status resource — cites the repo's existing export endpoint doing exactly that), confirms error envelope and naming match siblings, asks one question (open or closed enum for `format`?).
|
|
52
|
+
|
|
53
|
+
## Anti-patterns
|
|
54
|
+
|
|
55
|
+
- ❌ Declaring "breaking" or "safe" without reading the before-state — the diff of the surface is the entire evidence base.
|
|
56
|
+
- ❌ Imposing REST doctrine the repo doesn't follow ("should be HATEOAS", "must be plural nouns") when siblings consistently do otherwise — local consistency beats global convention; flag the doctrine mismatch once as a question, at most.
|
|
57
|
+
- ❌ Consistency findings with no cited precedent — grep the siblings first or don't flag it.
|
|
58
|
+
- ❌ Treating additive response fields as breaking, or new required request fields as safe — the asymmetry (requests: consumers write them; responses: consumers read them) is the whole compat model.
|
|
59
|
+
- ❌ Passing an unpaginated collection endpoint because "there won't be much data" — volume assumptions don't survive; retrofitting pagination breaks every consumer.
|
|
60
|
+
- ❌ Reviewing the handler's implementation quality (naming, DRY, perf) — that's `code-review`; scope discipline keeps this report actionable.
|
|
61
|
+
- ✅ Before/after-cited breaking verdicts, precedent-cited consistency findings, day-one invariants checked, questions kept separate from findings.
|
|
62
|
+
|
|
63
|
+
## Notes
|
|
64
|
+
|
|
65
|
+
- Spec-first repos (OpenAPI/proto/GraphQL SDL): review the spec diff as the contract and verify the implementation actually matches it (spot-check one handler against its spec entry — drift between the two is itself a finding). Code-first repos: the serialized DTOs + routes are the contract.
|
|
66
|
+
- Deprecation over deletion: when a breaking change is genuinely wanted, the recommendation is the repo's existing versioning/deprecation mechanism if one exists (grep for it) — inventing a versioning strategy is a `grill-with-docs` conversation, not a review finding.
|
|
67
|
+
- Apply `think-like-fable`: the risk lives in the unknown consumers, so compat verdicts get the re-derivation effort; "safe" claims are labeled by what was actually diffed; the report leads with the one change the user must not merge as-is.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: e2e-verify
|
|
3
|
+
description: Verify a change end-to-end in a real browser, routed by one question — who needs this check to run again? Ephemeral checks run via Expect (millionco/expect) if installed or Claude driving the browser directly; behaviors that must never silently regress (auth, money, signup, checkout, deletion) get durable Playwright tests committed under write-tests discipline. Evidence rule either way — an AI-walked flow yields "no issues found in the paths walked" with observations quoted, never "e2e passes"; unobserved behavior is "not verified", never "works". Safety gate — never against production or with real user cookies. Use this skill whenever the user says "verify this in the browser", "test it end to end", "e2e test this", "write playwright tests", "run expect", "smoke test the UI", "does the feature actually work", "check it in the browser", or "/e2e-verify" — even if they don't name the skill. Not for unit/integration authoring (write-tests) or debugging a failing e2e test (diagnose).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# E2E Verify
|
|
7
|
+
|
|
8
|
+
Close the loop between "the diff looks right" and "a user can actually do the thing." Every route through this skill drives a real browser against running code; what differs is durability. The routing question is not which tool — it's **who needs this check to run again?** Nobody (one-off confidence in this change) → ephemeral. CI, forever (a behavior whose silent regression is expensive) → durable Playwright test in the repo. Often both: smoke now, durable test as the deliverable.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- The user says "verify this in the browser", "test it end to end", "e2e test this", "write playwright tests", "run expect", "smoke test the UI", "does it actually work", "/e2e-verify".
|
|
13
|
+
- A feature from `plan-and-build` / `task-executor` is done and needs proof beyond unit tests; `code-review` or `ship-it` wants runtime evidence.
|
|
14
|
+
|
|
15
|
+
Do **not** auto-trigger for unit/integration authoring (`write-tests`), debugging an already-failing e2e test (`diagnose`), or general browser automation that isn't testing.
|
|
16
|
+
|
|
17
|
+
## The safety gate — before any browser opens
|
|
18
|
+
|
|
19
|
+
1. **Confirm the target URL is local or staging.** State it in the report. If the only available URL is production, stop and ask — never assume.
|
|
20
|
+
2. **No real user cookies or accounts.** Expect extracts system-browser cookies by default — pass `--no-cookies` unless the user explicitly opts in for a non-prod target. Flows that mutate data (payments, deletion, invites that email people) run only against seeded/throwaway data; if none exists, that's a blocker to surface, not a reason to "carefully" test on real data.
|
|
21
|
+
3. **Confirm something is listening.** Load the base URL and observe a real response before walking any flow. If nothing answers, start the dev server the repo's own way (its dev script / launch config) and confirm it's up — a dead port produces "every flow is broken", which is a false report about the app. Distinguish "the app failed" from "the app wasn't running" in everything you report.
|
|
22
|
+
|
|
23
|
+
## Route the request
|
|
24
|
+
|
|
25
|
+
| Signal | Route |
|
|
26
|
+
|---|---|
|
|
27
|
+
| "Does my change work?", pre-merge confidence, exploratory | **A: Ephemeral** |
|
|
28
|
+
| The flow is money/auth/signup/checkout/data-loss, or user says "add e2e tests", or the same flow has now been manually re-verified twice | **B: Durable** |
|
|
29
|
+
| Feature just built and it's a critical flow | **A now, then B** — the smoke run's steps become the test's spec |
|
|
30
|
+
|
|
31
|
+
## Route A: ephemeral verification
|
|
32
|
+
|
|
33
|
+
Pick the engine by what's installed — the discipline is identical across engines:
|
|
34
|
+
|
|
35
|
+
1. **Expect** (`expect-cli` / `/expect` on PATH): run with an explicit `--url` (the confirmed non-prod target), `--no-cookies` by default, `--target` matching the change scope. Its report is input, not verdict — extract which flows its subagents walked and what they observed.
|
|
36
|
+
2. **Claude drives the browser** (Playwright MCP, claude-in-chrome, preview tools — whatever this session has): enumerate the user-visible flows the diff touches (from the diff, not imagination), walk each one as a user would, and at each step verify via DOM/accessibility state for text and behavior (screenshots only for layout questions). After each flow: check the browser console for errors and the network log for failed requests — a page that looks right while logging exceptions is a finding, not a pass.
|
|
37
|
+
3. **Neither available — offer the install, never dead-end and never install silently.** One `AskUserQuestion` with the real options: (a) install Playwright locally (`npm init playwright@latest` — standard tooling, Claude then drives it), (b) install Expect (state plainly what its init does: runs a third-party script that adds a skill + hooks into the agent — third-party init scripts require an explicit yes), or (c) skip. Only if the user skips, report `e2e: not verified — no browser tooling` and stop. Never substitute code-reading for observation, and never report "verified" after a skipped install.
|
|
38
|
+
|
|
39
|
+
**Reporting rule (the core of the skill):** an AI-simulated user is a fallible verifier — same epistemics as any model output. The honest claim shape:
|
|
40
|
+
- ❌ "Ran Expect — e2e tests pass ✅"
|
|
41
|
+
- ✅ "Walked 3 flows against localhost:3000 (seeded account): login → dashboard (observed: redirect + username rendered), create invoice (observed: row appears, total 107.10, console clean), delete invoice (observed: 409 on double-delete — **finding**, quoted below). Not walked: mobile viewport, payment path (no test Stripe key). "
|
|
42
|
+
Every flow walked is enumerated; every "works" is backed by a named observation; everything not walked is listed. Zero findings across enumerated flows is a valid outcome.
|
|
43
|
+
|
|
44
|
+
## Route B: durable Playwright tests
|
|
45
|
+
|
|
46
|
+
`write-tests` discipline applied to the browser — plus the e2e-specific rules that keep suites from rotting:
|
|
47
|
+
|
|
48
|
+
1. **Match the repo first.** Grep for `playwright.config`, `cypress.config`, an `e2e/` dir. Extend the existing setup and imitate its strongest test; only scaffold fresh (`npm init playwright@latest`) if nothing exists, and say so.
|
|
49
|
+
2. **Ration e2e tests by risk.** Each one is 100× a unit test's cost in time and flake surface. Test through the UI only what is *about* the flow: the user journey, the wiring, the redirect. Business-logic variations (12 discount cases) belong in unit tests — write the one journey here and note the push-down. A handful of journeys is a suite; fifty is a liability.
|
|
50
|
+
3. **Selectors users would recognize:** `getByRole`, `getByLabel`, `getByTestId` — never CSS chains or `nth-child`, which weld the test to today's DOM.
|
|
51
|
+
- ❌ `page.locator('div.card > button:nth-child(2)')` — breaks on any restyle, catches nothing a user cares about.
|
|
52
|
+
- ✅ `page.getByRole('button', { name: 'Submit order' })` — breaks only when the user's world breaks.
|
|
53
|
+
4. **No sleeps.** Web-first assertions (`await expect(locator).toBeVisible()`) auto-wait; `waitForTimeout` is flake by construction. If a step genuinely needs polling, poll a condition.
|
|
54
|
+
5. **Independent tests, cheap setup.** Each test arranges its own world — seed state via API calls or fixtures, not by clicking through the UI (UI setup means test 3 fails because of the flow test 1 owns). Auth via a `storageState` fixture created once, not a login walk per test.
|
|
55
|
+
6. **Prove red-capable** (non-negotiable, per `write-tests`): break the behavior or the assertion target, run, quote the red; revert, quote the green. An e2e test never seen red is the most expensive decoration there is.
|
|
56
|
+
7. **Run the suite and quote the summary line.** A result you didn't observe is `not run`, never "passed".
|
|
57
|
+
|
|
58
|
+
## Examples
|
|
59
|
+
|
|
60
|
+
### Example 1: post-feature smoke → durable
|
|
61
|
+
|
|
62
|
+
**User:** "I just finished the invoice feature — verify it end to end."
|
|
63
|
+
|
|
64
|
+
**Claude:** Confirms target (localhost:3000, seeded user), no Expect installed → drives the browser: walks create/edit/void flows from the diff's touched routes, quotes observations per flow, catches a console 500 on void (finding). After the fix: "void is a money path — want the durable test?" → writes one Playwright journey extending the existing `e2e/` config, `getByRole` selectors, proves it red against the reverted fix, quotes both runs.
|
|
65
|
+
|
|
66
|
+
### Example 2: "run expect"
|
|
67
|
+
|
|
68
|
+
**User:** "Run expect on my branch."
|
|
69
|
+
|
|
70
|
+
**Claude:** Confirms the dev URL, runs `expect --target branch --url http://localhost:3000 --no-cookies`, then translates its output into the claim shape: flows walked + observations + findings + not-walked list — and flags that the checkout flow wasn't in Expect's generated plan despite being in the diff, walking it manually to close the gap.
|
|
71
|
+
|
|
72
|
+
## Anti-patterns
|
|
73
|
+
|
|
74
|
+
- ❌ Reporting an Expect or browser-walk green as "e2e tests pass" — the verifier is a model; the claim is "no issues found in the paths walked", paths listed.
|
|
75
|
+
- ❌ Trusting the generated test plan to cover the diff — diff-to-plan is lossy; check the plan against the diff's touched routes and walk what it missed.
|
|
76
|
+
- ❌ Verifying against production, or letting Expect's default cookie extraction ride along silently.
|
|
77
|
+
- ❌ Visual-only verification — a rendered page with console exceptions and failed XHRs is a finding.
|
|
78
|
+
- ❌ Writing an e2e test for every case a unit test could cover — ration by journey, push logic down the pyramid.
|
|
79
|
+
- ❌ Shipping an e2e test never seen red, or "fixing" a flaky one by adding `waitForTimeout`.
|
|
80
|
+
- ❌ Scaffolding a fresh Playwright setup when the repo already has one (or has Cypress — extend what's there).
|
|
81
|
+
- ❌ Running Expect's init script (or any third-party installer) without an explicit yes — offering the install is required, running it unasked is not consent.
|
|
82
|
+
- ✅ Route by who-needs-it-again → safety gate → observations quoted per flow → durable tests proven red-capable → not-walked list always present.
|
|
83
|
+
|
|
84
|
+
## Notes
|
|
85
|
+
|
|
86
|
+
- Engines are swappable; the discipline isn't. If Expect changes licensing (FSL, hosted version coming) or a better ephemeral tool appears, only Route A's engine list changes.
|
|
87
|
+
- Manual re-verification of the same flow twice is the signal to graduate it to Route B — the third time is a test.
|
|
88
|
+
- Apply `think-like-fable`: flows chosen by risk, every "works" re-derived by observation, the not-walked list is the labeled-assumption discipline, and the report leads with findings, not the tour.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: migration-safety
|
|
3
|
+
description: Review a schema migration for production safety under live traffic — destructive operations (dropped/renamed columns or tables, type narrowing) gated behind expand-contract plans, lock-taking DDL flagged with the specific lock and its duration driver, the deploy-order contract checked both ways (old code on new schema during rollout, new code on old schema during rollback), backfills separated from DDL and batched, and a rollback path stated per migration. Never executes migrations or DDL. Use this skill whenever the user says "review this migration", "is this migration safe", "will this lock the table", "zero-downtime migration", "check the schema change", "expand and contract", "review the EF migration / alembic / prisma migrate diff", or "/migration-safety" — even if they don't name the skill. Distinct from sql-review (T-SQL antipatterns in procs); this reviews SCHEMA CHANGES against live traffic and deploys.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migration Safety
|
|
7
|
+
|
|
8
|
+
A migration runs once, against the production database, usually mid-deploy, while old and new application code overlap. This skill reviews it for the three ways that goes wrong: **locks** (DDL that blocks traffic), **ordering** (schema and code versions that can't coexist), and **irreversibility** (data destroyed with no path back). The core discipline: every migration is judged against the deploy timeline, not against an empty dev database where everything is instant and nothing is watching.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- The user says "review this migration", "is this migration safe", "will this lock the table", "zero-downtime", "expand and contract", "check the schema change", "/migration-safety".
|
|
13
|
+
- A migration file (EF Core, Prisma, alembic, Rails, Flyway, raw DDL) sits in the diff — including ones generated by `plan-and-build`, which writes migrations but never runs them.
|
|
14
|
+
- `ship-it` flags a migration and it needs the dedicated pass.
|
|
15
|
+
|
|
16
|
+
Do **not** auto-trigger for stored-procedure or query changes (`sql-review`) or for greenfield schemas with no production data — on an empty database most of this catalog is N/A, and the report should say that in one line instead of performing the checklist. This skill **never executes** migrations, DDL, or any SQL against a database — it reads files and reports.
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
1. **Establish the context the file doesn't show.** Which engine (Postgres/MySQL/SQL Server/SQLite — lock behavior differs by engine and version, and a lock claim that doesn't name the engine is a guess)? Is there production data, and are the touched tables large or hot? Get evidence where possible (the user, a row-count comment, table names like `events`/`audit_log` that are large by nature) — otherwise label the assumption out loud: "assuming `orders` is large and hot; if it's small, findings 2–3 downgrade to noise." How do migrations deploy relative to code (before app deploy? in the same release?) — this determines step 4's ordering checks.
|
|
21
|
+
2. **Walk the destructive-operation gate.** `DROP COLUMN`/`DROP TABLE`, column/table **renames** (a rename IS a drop+add to running code), type **narrowing** (`varchar(500)→(50)`, `bigint→int`, nullable→`NOT NULL`), truncates, and `DELETE`/`UPDATE` data mutations. Each is a **blocker unless** an expand-contract plan is stated: expand (add the new thing, dual-write or backfill), migrate readers, contract (drop the old thing **in a later release**, after old code is provably gone). Drop-in-the-same-release-as-the-code-change fails the rollback test by construction.
|
|
22
|
+
- ❌ "Renamed `users.name` to `users.full_name` in one migration — the ORM was updated too, so it's fine."
|
|
23
|
+
- ✅ "Rename is drop+add to the old code still running during rollout: add `full_name`, backfill, deploy code reading `full_name` (writing both), then drop `name` in release N+2. Or, if the deploy has real downtime, say so and the one-step rename is fine — name the assumption."
|
|
24
|
+
3. **Flag lock-taking DDL with the specific mechanism.** Name the lock and what drives its duration — "might be slow" is not a finding. The high-yield catalog: index creation without `CONCURRENTLY` (Postgres: blocks writes for the whole build) or `ONLINE = ON` (SQL Server, edition-permitting); `NOT NULL` added without engine-appropriate staging (Postgres: `ADD CONSTRAINT ... NOT VALID` then `VALIDATE`; adding a column `NOT NULL` **with** a constant default is metadata-only on modern PG/SQL Server — don't flag what's actually free, that's severity inflation); full-table-rewrite type changes; adding an FK without `NOT VALID`+`VALIDATE`; MySQL DDL without an online strategy on big tables. For each: the operation, the lock, the duration driver (table size, write rate), and the non-blocking alternative.
|
|
25
|
+
4. **Check the deploy-order contract both ways.** During rollout, **old code runs against the new schema** — every added `NOT NULL`-without-default, dropped column old code still selects, or renamed anything breaks it. During rollback, **new-ish data meets old schema expectations** — rows written by new code must not violate what old code assumes. Trace both directions against the actual code in the diff (grep for the column names old code uses). Also: does anything in the release *require* the migration to have run (new code reading a column that doesn't exist yet) — if so the migrate-then-deploy ordering is load-bearing; say it.
|
|
26
|
+
5. **Separate backfills from DDL.** Data backfills inside the schema migration (same transaction as DDL, or a single `UPDATE table SET ...` over millions of rows) are findings: long transactions hold locks, bloat undo/WAL, and can't checkpoint. Recommendation shape: DDL migration adds the nullable column; a separate batched job (with progress + resumability) backfills; a later migration adds the constraint. Small-table backfills are fine — say why (the size evidence or assumption from step 1).
|
|
27
|
+
6. **Demand the rollback path, per migration.** Three honest answers: (a) reversible — the down-migration exists and actually restores (auto-generated downs that drop a column do NOT restore its data — a down that loses data is not a rollback, label it); (b) roll-forward-only — irreversible by nature, so the plan is a fixing migration, and the deploy gate should know; (c) backup-gated — destructive step requires a named backup/snapshot beforehand. "It has a down file" without reading it is not an answer.
|
|
28
|
+
7. **Report.** Findings ranked **blocker** (data loss without a stated plan, traffic-blocking lock on a hot table, rollout/rollback contradiction) / **should-fix** / **note**, each with `file:line`, the failure scenario in one sentence (deploy does X → traffic sees Y), and the safe alternative. State the assumptions from step 1 that findings hang on. A green report on an additive, defaulted, indexed-concurrently migration is a valid outcome — two lines, not a performed checklist. Never edit or execute anything unprompted; ask per-finding whether to draft the fix.
|
|
29
|
+
|
|
30
|
+
## Examples
|
|
31
|
+
|
|
32
|
+
### Example 1: the innocent-looking rename
|
|
33
|
+
|
|
34
|
+
**User:** "Review this EF migration before I merge — just renames a column and adds an index."
|
|
35
|
+
|
|
36
|
+
**Claude:** Flags the rename as drop+add against live old code (blocker, expand-contract plan sketched across 3 releases), checks the index: `CreateIndex` on `Orders` — SQL Server without `ONLINE = ON`; asks/flags edition support, cites the line. Checks the down: `RenameColumn` back — actually reversible, noted. One assumption stated: `Orders` presumed hot.
|
|
37
|
+
|
|
38
|
+
### Example 2: honest green
|
|
39
|
+
|
|
40
|
+
**User:** "Is this migration safe? Adds a nullable `preferences` jsonb column and a partial index CONCURRENTLY."
|
|
41
|
+
|
|
42
|
+
**Claude:** Nullable add = metadata-only; `CONCURRENTLY` = non-blocking (notes it must run outside a transaction — verifies the tool's migration wrapper handles that, cites how); old code unaffected (column unreferenced by it), rollback trivial. Report: no findings, one note about the transaction wrapper. Done in four lines.
|
|
43
|
+
|
|
44
|
+
## Anti-patterns
|
|
45
|
+
|
|
46
|
+
- ❌ Reviewing the migration against an empty dev database mentally — every finding is judged against size, traffic, and the deploy overlap, or labeled with the assumption.
|
|
47
|
+
- ❌ Lock claims without the engine — `ADD COLUMN NOT NULL DEFAULT` is free on modern Postgres and was a rewrite on old versions; flagging the free case is severity inflation that erodes trust in the real findings.
|
|
48
|
+
- ❌ Accepting a rename or drop because "the code was updated in the same PR" — the whole problem is the minutes-to-hours when old code and new schema coexist.
|
|
49
|
+
- ❌ Trusting an auto-generated down-migration as a rollback without reading whether it restores data or just shape.
|
|
50
|
+
- ❌ Letting a million-row backfill ride inside the DDL transaction because the migration tool put it there.
|
|
51
|
+
- ❌ Executing the migration, `dotnet ef database update`, or ANY SQL "to check" — this skill reads and reports, full stop.
|
|
52
|
+
- ❌ Performing the full checklist on a pre-production empty schema — one line of N/A beats a page of theater.
|
|
53
|
+
- ✅ Engine-named lock findings, both-direction deploy-order trace, data-preserving rollback verdicts, assumptions stated, blockers with one-sentence failure scenarios.
|
|
54
|
+
|
|
55
|
+
## Notes
|
|
56
|
+
|
|
57
|
+
- Tool wrappers matter: some runners wrap every migration in a transaction (breaking `CREATE INDEX CONCURRENTLY`), some apply timeouts, some (SQL Server) do transactional DDL. Find the runner's config in the repo before asserting behavior — a claim about the wrapper you haven't opened is a hypothesis.
|
|
58
|
+
- Recommend `lock_timeout`/`statement_timeout` guards (Postgres) on DDL touching hot tables where the repo's runner supports it — a migration that fails fast beats one that queues behind a long transaction and blocks everything behind *it*.
|
|
59
|
+
- Apply `think-like-fable`: the risk lives in the destructive ops and the deploy overlap, so they get the effort; every lock/rollback claim is re-derived from the engine + the actual file; the report leads with the one migration that must not run as-is.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: safe-refactor
|
|
3
|
+
description: Execute a behavior-preserving refactor with a proof of preservation — establishes a safety net first (existing tests or new characterization tests over every touched path), locks a baseline green run, then moves in small always-green steps where each step is one mechanical transformation, and treats any needed assertion change as a smuggled behavior change to surface, not fix. Use this skill whenever the user says "refactor this", "clean this up without changing behavior", "extract this into", "restructure this module", "rename this across the codebase", "inline this", "split this function/class", or "/safe-refactor" — even if they don't explicitly say "refactoring skill". Do not use for choosing WHAT to refactor (use improve-codebase-architecture) or for changes that are supposed to alter behavior (use plan-and-build).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Safe Refactor
|
|
7
|
+
|
|
8
|
+
Execute a refactor as a sequence of proofs, not a rewrite. "Behavior-preserving" is a falsifiable claim: the observable behavior before and after must be identical, and this skill's job is to make that claim checkable at every step instead of asserting it once at the end. The completion of the chain: `improve-codebase-architecture` names the target, `write-tests` builds the net, this skill makes the move.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- The user says "refactor this", "clean this up", "extract X into Y", "restructure without changing behavior", "rename across the codebase", "split this up", "inline this", "/safe-refactor".
|
|
13
|
+
- `improve-codebase-architecture` produced an approved deepening candidate and the user says "do it".
|
|
14
|
+
- A feature task requires restructuring existing code before the new behavior can land (do the refactor as its own phase, this skill governs that phase only).
|
|
15
|
+
|
|
16
|
+
Do **not** auto-trigger for changes meant to alter behavior — bug fixes, feature work, "improve this error message" — those are `plan-and-build` / `task-executor` territory. If the user's "refactor" turns out to include behavior changes, split the work: refactor first under this skill, behavior change after, never interleaved.
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
1. **Write the behavior contract.** One short list: the observable surface of the code being moved — public functions/endpoints and their outputs, side effects (writes, emits, logs that anything consumes), error types thrown, and performance characteristics if anything depends on them. This list is what "preserved" means; everything not on it is fair game. If you can't write the list, you haven't read enough code yet — a claim about code you haven't opened is a hypothesis.
|
|
21
|
+
2. **Audit the safety net.** For each item on the contract, find the test that would fail if it broke — grep the test tree, cite `file:line`. Contract items with no covering test get characterization tests **before any restructuring begins** (per `write-tests`: pin actual behavior, prove each test can go red). No net, no refactor — this gate is not skippable, and "the change is simple" is not an exemption; simple changes with no net are how behavior drifts silently.
|
|
22
|
+
3. **Lock the baseline.** Run the full suite (single-run mode, never watch mode) and build; quote both summary lines. A result you didn't observe is "not run", never "passed". If anything is red before you start, stop and surface it — you cannot distinguish your breakage from pre-existing breakage on a red baseline.
|
|
23
|
+
4. **Plan the move as mechanical steps.** Each step is ONE named transformation — extract function, move file, rename symbol, inline variable, introduce parameter, replace conditional with polymorphism — small enough that if the suite goes red, the cause is unambiguous. Order steps so each leaves the code compiling and the suite green. Present the step list before executing.
|
|
24
|
+
5. **Execute step → verify → step.** After every step: build + run the affected tests (full suite at least at every commit point), quote the result. Green → proceed. Red → the step is wrong; revert or fix *the step*, never the test. Each step (or small coherent group) is a rollback point — ask once, up front, whether to commit at these points; committing without that yes is not authorized by this skill.
|
|
25
|
+
6. **Renames are grep-verified, not assumed.** After renaming or moving a symbol, grep for the old name repo-wide — including strings, configs, reflection sites, docs, and serialized names (JSON keys, DB column mappings, API routes). Zero hits or each remaining hit justified in one line. Dynamic-dispatch and reflection usages don't break the build; the grep is the only net they have.
|
|
26
|
+
- ❌ "Renamed `getUser` to `fetchUser`; build is green, done." — the build can't see the `"getUser"` string in the route table.
|
|
27
|
+
- ✅ "Renamed; repo grep for `getUser` returns 2 hits: a CHANGELOG entry (historical, left) and `routes.json:14` (updated)."
|
|
28
|
+
7. **The assertion-change tripwire.** If any step makes an existing test fail and the "fix" would be changing that test's assertion, expected value, or setup — **stop**. That is the definition of a behavior change wearing a refactor's clothes. Surface it: quote the old and new behavior, ask whether the change is intended. Only test changes allowed without asking: mechanical renames mirroring step 6, and import/path updates.
|
|
29
|
+
- ❌ Silently updating `expect(total).toBe(107.10)` to `109.31` mid-refactor because "the new rounding is more correct."
|
|
30
|
+
- ✅ "Step 4 changes the rounding point, which alters totals on multi-line invoices (old: 107.10, new: 109.31). That's a behavior change, not a refactor — proceed with old behavior preserved, or split this into a follow-up?"
|
|
31
|
+
8. **Final proof.** Full suite + build, summaries quoted. Then re-read the complete diff once, adversarially, hunting for the behavior change you smuggled in without noticing — reordered side effects, a dropped `await`, a widened catch, changed default parameter, iteration-order dependence, log lines something might parse. Report: contract (each item → covering test), steps taken, grep results for renames, and anything intentionally left un-refactored with a one-line why.
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
### Example 1: extract a module
|
|
36
|
+
|
|
37
|
+
**User:** "Pull the pricing logic out of `OrderService` into its own `PricingEngine`."
|
|
38
|
+
|
|
39
|
+
**Claude:** Writes the contract (3 public methods, the discount side effect, 2 error types), finds tests covering 2 of 3 methods, writes characterization tests for the third and proves them red-capable, locks baseline (`112 passed`, build green), then: extract class → move methods one at a time → re-point callers → delete originals, suite quoted green after each, old-name grep clean, final adversarial diff read confirms no reordered side effects.
|
|
40
|
+
|
|
41
|
+
### Example 2: the smuggled behavior change
|
|
42
|
+
|
|
43
|
+
**User:** "Refactor this date-handling mess to use one formatter."
|
|
44
|
+
|
|
45
|
+
**Claude:** Mid-sequence, unifying formatters makes a test expecting `"3/5/2026"` fail because the shared formatter emits `"03/05/2026"`. Stops at the tripwire, shows both outputs, asks: preserve the legacy format via a format parameter (pure refactor), or adopt the new format (behavior change, separate commit, may affect consumers)?
|
|
46
|
+
|
|
47
|
+
## Anti-patterns
|
|
48
|
+
|
|
49
|
+
- ❌ Refactoring untested code because "it's a small change" — the net gate (step 2) exists precisely for the changes that feel too small to break anything.
|
|
50
|
+
- ❌ The big-bang rewrite: restructure everything, then see what's red. When five things break you can't attribute any of them.
|
|
51
|
+
- ❌ Fixing a red step by editing the test — the tripwire (step 7) exists because this is the single most common way behavior changes ship inside "refactors".
|
|
52
|
+
- ❌ Interleaving "while I'm in here" behavior improvements with the restructuring — one diff, two intents, zero reviewability. Queue them for after.
|
|
53
|
+
- ❌ Trusting the compiler to catch all breakage from a rename — strings, reflection, routes, and serialization don't compile-check.
|
|
54
|
+
- ❌ Reporting the refactor complete without quoting the final suite run — "not run" is the only honest label for an unobserved result.
|
|
55
|
+
- ❌ Refactoring toward a pattern the repo doesn't use because it's considered best practice — match the codebase's existing idiom; consistency is a feature.
|
|
56
|
+
- ✅ Contract → net → baseline → small proven steps → grep-verified renames → adversarial final read.
|
|
57
|
+
|
|
58
|
+
## Notes
|
|
59
|
+
|
|
60
|
+
- Commit hygiene: keep refactor commits free of behavior changes so `git bisect` stays useful and reviewers can verify "no behavior change" structurally. Use `conventional-commits` (`refactor:` type) when committing.
|
|
61
|
+
- If the safety-net phase reveals the code is nearly untestable, that finding may reorder the plan — sometimes the first refactor step must be the minimal seam that makes testing possible (extract the untestable I/O, pin the rest). Say so rather than silently expanding scope.
|
|
62
|
+
- Apply `think-like-fable` throughout: the contract is the decomposition into independently checkable pieces, every green claim is re-derived by running (never recognized), and the final adversarial read is attacking your own conclusion before handing it over.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-review
|
|
3
|
+
description: Attacker's-eye security review of a diff, branch, or module — walks a fixed vulnerability catalog (missing authz on new endpoints, injection, secrets in code/logs, trusting client-sent identity, SSRF, path traversal, insecure deserialization, mass assignment, broken crypto, unsafe redirects, dependency CVEs) where every finding must name a concrete attack path (attacker does X → gains Y) or be demoted to hardening advice. Never claims "secure", only "nothing found in the classes checked". Use this skill whenever the user says "security review", "is this secure", "check for vulnerabilities", "audit the auth", "threat model this", "pentest mindset", "check for injection", or "/security-review" — even if they don't name the skill. Distinct from code-review/pr-review (security is one lens there); this is the dedicated deep pass.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Security Review
|
|
7
|
+
|
|
8
|
+
A defensive security audit of code you're shipping, run with an attacker's questions: what does this trust that it shouldn't, and what can I reach that I shouldn't. The output discipline is what makes it useful — every finding carries an attack path, every "pass" carries the evidence that was checked, and the report never claims more than it verified. "No vulnerabilities" is not a claim this skill can make; "none found in these classes, with these checks, and here's what wasn't checked" is.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- The user says "security review", "is this secure", "check for vulnerabilities", "audit the auth", "threat model this", "check for injection", "/security-review".
|
|
13
|
+
- New endpoints, auth changes, file handling, payment paths, or user-generated-content rendering are about to ship.
|
|
14
|
+
- `ship-it` or `code-review` surfaces a security concern that needs a dedicated pass.
|
|
15
|
+
|
|
16
|
+
Do **not** auto-trigger on every diff — `code-review` carries a security lens for routine work; this skill is the deep pass when the change touches trust boundaries or the user asks. This skill reviews and reports; it never writes exploit tooling, and it never edits code unprompted.
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
1. **Fix the scope first.** A diff, a branch, a module, or an endpoint list — force the user to name it if ambiguous (one `AskUserQuestion`). Then map the trust boundaries inside that scope: every place data crosses from less-trusted to more-trusted (HTTP input, file upload, queue message, webhook, env/config, DB values rendered back out). Effort follows boundaries — a 500-line diff with one new endpoint gets most scrutiny on the endpoint.
|
|
21
|
+
2. **Walk the catalog against the scope.** For each class, the check is named — run it, don't vibe it:
|
|
22
|
+
- **Missing authn/authz** — for every new/changed route or handler, locate the auth check (middleware registration, guard attribute, explicit session call) and cite `file:line`. Then check *object-level* authz: does the handler verify the caller may touch **this** record, or only that they're logged in? Absence of either is a finding. Compare against how sibling endpoints in the repo do it.
|
|
23
|
+
- **Trusting client-sent identity** — grep handlers in scope for user/account/tenant IDs read from body, query, or headers and used in queries or writes. The ID must come from the session/token.
|
|
24
|
+
- **Injection** — SQL/query built by string concatenation with external input (must be parameterized); shell commands from input; path traversal (user input joined into filesystem paths without normalization + prefix check); XSS (external input rendered without the framework's escaping — grep for `dangerouslySetInnerHTML`, `innerHTML`, `v-html`, `Html.Raw`, raw template filters).
|
|
25
|
+
- **Secrets** — grep the scope for key/token/password-shaped literals and connection strings; check that secrets aren't logged (grep log calls near auth/config code) or committed in config. A finding is the **value** in the repo, not the reference: `process.env.STRIPE_KEY` / a secret-manager call is the correct pattern, never a finding; `sk_live_4eC39...` in code or a committed `.env` is. If a real secret is already committed, rotation is the fix — deleting the line doesn't un-leak it; git history keeps it.
|
|
26
|
+
- **SSRF & redirects** — any outbound request whose URL contains external input; any redirect target taken from a parameter without an allowlist.
|
|
27
|
+
- **Insecure deserialization / mass assignment** — deserializing external input into types with dangerous side effects; binding request bodies directly to DB entities so a caller can set `isAdmin`/`role`/`price` (check for an explicit DTO/allowlist between input and model).
|
|
28
|
+
- **Crypto misuse** — hand-rolled hashing/encryption, fast hashes (MD5/SHA-x) for passwords instead of bcrypt/argon2/scrypt, `Math.random()`-class RNG for tokens, comparing secrets with `==` instead of constant-time compare.
|
|
29
|
+
- **Dependency CVEs** — only via an actual tool run (`npm audit`, `dotnet list package --vulnerable`, `pip-audit`, `cargo audit`); quote the output. If no tool ran, the report says `dependencies: not checked` — never "dependencies look fine".
|
|
30
|
+
3. **Evidence-gate every finding.** A finding must state, in one sentence, the attack path: *who* (unauthenticated user / any logged-in user / tenant B / insider) does *what* → gains *what*. No constructible path → demote to **hardening** (still reported, clearly separated). Severity from the path itself: **critical** = unauthenticated or cross-tenant data access/mutation, RCE, secret exposure; **high** = authenticated privilege escalation or injection with real reachable input; **medium** = requires unusual preconditions; **hardening** = defense-in-depth with no current path.
|
|
31
|
+
- ❌ "The `userId` parameter could be dangerous." — no actor, no gain, not a finding.
|
|
32
|
+
- ✅ "`GET /api/invoices/{id}` checks login but not ownership (`InvoiceController.cs:41` — no tenant filter in the query): any logged-in user who increments `id` reads other customers' invoices. Critical (IDOR)."
|
|
33
|
+
4. **Verify before reporting.** For each finding, re-derive it: open the file, trace the input to the sink, confirm no sanitizer/guard sits between them (search the call chain, not just the hunk — the guard may live in middleware or a base class). A pattern-matched finding you didn't trace is labeled **unconfirmed** in the report, never stated in the same register as a traced one. Zero findings is a valid outcome and must be reported without padding.
|
|
34
|
+
5. **Report.** Findings ranked by severity, each with `file:line`, attack path, and a recommended fix direction (not applied). Then the coverage statement: which catalog classes were checked with what evidence, and which were **not** checked (e.g. "infra/deployment config out of scope, dependencies not audited — no lockfile in scope"). End by asking per-finding whether to draft the fix — never edit unprompted.
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
### Example 1: new endpoint pass
|
|
39
|
+
|
|
40
|
+
**User:** "Security review the payments branch before I merge."
|
|
41
|
+
|
|
42
|
+
**Claude:** Scopes to the branch diff, maps boundaries (2 new endpoints, 1 webhook), finds the webhook skips signature verification (traced: raw body straight to the handler, `webhooks.ts:23`) — critical, forged payment confirmations; finds sibling endpoints use `requireSession` but the new refund route registers outside the auth router — high; runs `npm audit` (quotes: 0 high). Coverage note: XSS class N/A (no rendering in scope), crypto N/A.
|
|
43
|
+
|
|
44
|
+
### Example 2: honest zero
|
|
45
|
+
|
|
46
|
+
**User:** "Check this date-formatting utility PR for vulnerabilities."
|
|
47
|
+
|
|
48
|
+
**Claude:** Walks the catalog; no external input reaches a sink (verified: all call sites pass server-generated dates). Report: zero findings, one hardening note (no input-length guard), explicit "not checked: dependencies (no manifest change)". No invented findings to look thorough.
|
|
49
|
+
|
|
50
|
+
## Anti-patterns
|
|
51
|
+
|
|
52
|
+
- ❌ Findings without an attack path — severity theater. If you can't say who gains what, it's hardening advice.
|
|
53
|
+
- ❌ Claiming "this code is secure" — the only honest claim is "nothing found in the classes checked", plus the unchecked list.
|
|
54
|
+
- ❌ Reporting a pattern-match ("string concatenation near SQL") without tracing whether external input actually reaches it — half of these have a parameterizer or validator one frame up.
|
|
55
|
+
- ❌ Marking everything critical. The severity ladder is defined by the attack path; a finding needing admin access plus a misconfigured proxy is not critical.
|
|
56
|
+
- ❌ "Fixing" a committed secret by deleting the line — history retains it; the finding is "rotate this credential".
|
|
57
|
+
- ❌ Skipping object-level authz because authentication exists — IDOR is the most common real-world miss, and it lives exactly in that gap.
|
|
58
|
+
- ❌ Editing code or adding "quick fixes" unprompted — report, ask, then fix.
|
|
59
|
+
- ✅ Scope → boundaries → named checks with citations → attack-path-gated findings → coverage statement including what was NOT checked.
|
|
60
|
+
|
|
61
|
+
## Notes
|
|
62
|
+
|
|
63
|
+
- This skill is defensive: it reviews code the user owns or is authorized to audit. It doesn't produce working exploits — a one-sentence attack path is the proof standard, a PoC payload is not required and not offered beyond what's needed to demonstrate the flaw to the developer.
|
|
64
|
+
- Language/framework specifics come from the repo: find how THIS codebase does auth, validation, and escaping (grep for the middleware/guards), then hunt for the places in scope that deviate from it — deviation from the local safe pattern is the highest-yield query.
|
|
65
|
+
- Apply `think-like-fable`: effort at the boundaries (§3), every finding re-derived not recognized (§4), unconfirmed labeled out loud (§5), and attack your own report — the finding you're most confident in is the one to re-trace.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upgrade-deps
|
|
3
|
+
description: Upgrade project dependencies safely — inventories current→target versions from the actual manifest/lockfile, reads the real changelog/release notes for every major bump (a breaking-change claim without a changelog citation is a hypothesis), greps the codebase for each breaking API before declaring it safe, upgrades one major at a time with tests+build quoted green between batches, and reports a per-package table of what changed and what evidence backs "safe". Use this skill whenever the user says "upgrade dependencies", "update packages", "bump <package>", "is it safe to upgrade", "update to the latest", "fix the npm audit", "dependabot PR review", or "/upgrade-deps" — even if they don't explicitly say "dependency skill". Do not use for adding a NEW dependency (that's a design decision — plan-and-build) or diagnosing a breakage after an upgrade already happened (use diagnose).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Upgrade Deps
|
|
7
|
+
|
|
8
|
+
Upgrade dependencies as a verification exercise, not a version-number edit. The failure mode this skill exists to prevent: bump everything, build goes green, ship — then the runtime break surfaces in production because the breaking change lived in behavior, not in types. Every "safe to upgrade" claim here is backed by a named check: the changelog actually read, the breaking API actually grepped for, the suite actually run.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- The user says "upgrade dependencies", "update packages", "bump X to latest", "is it safe to upgrade X", "handle the dependabot/renovate PRs", "fix the audit warnings", "/upgrade-deps".
|
|
13
|
+
- A CVE or audit finding requires moving a package (composes with `security-review`).
|
|
14
|
+
- A framework/runtime upgrade (Node LTS, .NET major, Next.js major) that drags dependencies with it.
|
|
15
|
+
|
|
16
|
+
Do **not** auto-trigger for adding a new dependency (a design choice — route to `plan-and-build` / `grill-with-docs`) or for debugging an already-broken upgrade (`diagnose`). If the repo has no tests at all, say up front that upgrade safety cannot be verified beyond compile — and ask whether to proceed anyway or pin a minimal net first (`write-tests`).
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
1. **Inventory from the tools, not from memory.** Run the ecosystem's outdated-check (`npm outdated`, `dotnet list package --outdated`, `pip list --outdated`, `cargo outdated`, `go list -u -m all`) and quote the output. Training-data version knowledge is stale by construction — never state a "latest version" you didn't just read from a tool or registry. Note which entries are direct vs transitive: transitives usually move by bumping the parent or the lockfile, not the manifest.
|
|
21
|
+
2. **Confirm scope.** Everything? Security-only? One package? If the user didn't say and the outdated list is long, ask once (`AskUserQuestion`): the plan for "patch the CVE" and "get current across the board" are different sizes. Classify the in-scope list: **patch/minor** (batchable) vs **major** (one at a time, each with its own evidence).
|
|
22
|
+
3. **Lock the baseline.** Full test suite (single-run mode, never watch mode) + build, summaries quoted. A result you didn't observe is "not run", never "passed". Red baseline → stop and surface: you can't attribute post-upgrade failures on a pre-broken repo.
|
|
23
|
+
4. **For every major bump: read the actual release notes.** Fetch the changelog / GitHub releases / migration guide for the versions being crossed (all of them — v2→v4 means reading v3's breaking changes too). Extract the breaking-change list. No changelog found → say so and downgrade confidence explicitly ("no changelog located; upgrading on test coverage alone").
|
|
24
|
+
5. **Grep before declaring unaffected.** For each breaking change, search the repo for the removed/renamed API, changed config key, or altered default. Cite hits (each becomes a migration edit) or state the negative concretely: "no usage — repo grep for `<symbol>` returned nothing". Behavioral breaking changes (changed defaults, stricter parsing, timezone/locale changes) don't grep well — list them separately as **runtime risks** and name which test would catch each, or that none would.
|
|
25
|
+
- ❌ "v5 has breaking changes but they probably don't affect us."
|
|
26
|
+
- ✅ "v5 removes `parser.lenient` (changelog §2). Grep: 2 call sites — `config.ts:12`, `import.ts:88` — both need the new `strict:false` option. Also v5 changes default timezone handling (runtime risk: covered by `date-roundtrip` tests)."
|
|
27
|
+
6. **Upgrade in evidence-sized batches.** Patch/minor: one batch, then tests+build, quoted. Majors: strictly one package at a time — apply the bump plus its migration edits from step 5, run tests+build, quote, commit (`conventional-commits`, `chore:` or `fix:`), next. When a batch goes red: read the error, change exactly one thing, retry once; still red → revert that bump, mark the package **blocked** with the quoted error, and continue with the rest. Two failures on the same package is a report item, not a loop.
|
|
28
|
+
7. **Lockfile and peer hygiene.** Regenerate the lockfile with the ecosystem's tool and commit it with the manifest — never hand-edit it. Never silence resolution errors with `--force` / `--legacy-peer-deps` without surfacing what conflict is being overridden and getting a yes. For a transitive CVE with no direct bump available, prefer the ecosystem's override mechanism (`overrides` / `resolutions` / central package management) and label it as a pin to revisit.
|
|
29
|
+
8. **Report the table + the honest residue.** Per package: from → to, breaking changes affecting THIS repo (with the grep/changelog evidence), migration edits made, test result. Then the residue, stated plainly: blocked packages and why, runtime risks no test covers, anything held back deliberately (e.g. "next stayed on 14 — the 15 migration is its own task"). Zero-affected upgrades are a valid, common outcome — a clean table is the report, not a reason to pad.
|
|
30
|
+
|
|
31
|
+
## Examples
|
|
32
|
+
|
|
33
|
+
### Example 1: the routine sweep
|
|
34
|
+
|
|
35
|
+
**User:** "Update this project's dependencies."
|
|
36
|
+
|
|
37
|
+
**Claude:** Runs `npm outdated` (quotes it: 14 minor, 3 major), confirms scope = all, baseline green (quoted), batches the 14 minors → suite green → commit; then majors one at a time: reads `zod` v4 release notes, greps the 3 renamed APIs (7 call sites migrated), suite green, commit; `eslint` v9 flat-config migration done the same way; third major's changelog reveals a Node engine bump beyond the repo's CI version → marked **blocked** with the evidence, not forced. Final table + residue.
|
|
38
|
+
|
|
39
|
+
### Example 2: CVE-driven, transitive
|
|
40
|
+
|
|
41
|
+
**User:** "npm audit is flagging lodash under some build plugin, fix it."
|
|
42
|
+
|
|
43
|
+
**Claude:** Runs `npm audit` (quotes the advisory + path), finds it transitive under a plugin whose latest still pins the bad range, adds an `overrides` entry, regenerates the lockfile, re-runs audit (quotes: 0), runs the build to prove the override didn't break resolution, labels the override as a revisit-when-upstream-fixes pin in the report.
|
|
44
|
+
|
|
45
|
+
## Anti-patterns
|
|
46
|
+
|
|
47
|
+
- ❌ Stating a package's latest version or breaking changes from memory — versions move; only tool output and fetched changelogs count.
|
|
48
|
+
- ❌ Bumping all majors at once — when the suite goes red you can't attribute the failure, and the debug costs more than the sequencing saved.
|
|
49
|
+
- ❌ "Build passes, so the upgrade is safe" — compilation proves the typed surface only; changed runtime defaults, dynamic access, and reflection sail through. Name the runtime risks instead of implying their absence.
|
|
50
|
+
- ❌ `--force` / `--legacy-peer-deps` / `--ignore-engines` applied silently to make the error go away — that's deferring the break to runtime and hiding the decision from the user.
|
|
51
|
+
- ❌ Hand-editing the lockfile, or committing the manifest without regenerating it.
|
|
52
|
+
- ❌ Retrying a red upgrade in a loop with mutations until something compiles — one informed retry, then revert, block, and report.
|
|
53
|
+
- ❌ Sneaking unrelated "while I'm here" refactors into upgrade commits — the diff should be bump + its migration edits, nothing else.
|
|
54
|
+
- ✅ Tool-derived inventory → changelogs actually read → breaking APIs grepped with citations → one major per commit, suite quoted between → table + honest residue.
|
|
55
|
+
|
|
56
|
+
## Notes
|
|
57
|
+
|
|
58
|
+
- Framework majors with published codemods (Next.js, React, MUI, AngularJS→ng) — check for an official codemod before hand-migrating; run it, then diff-review its output like any other change.
|
|
59
|
+
- Version-pinning policy belongs to the repo (exact pins vs ranges, Renovate config) — match what's there; don't impose a policy mid-upgrade.
|
|
60
|
+
- Apply `think-like-fable`: the risk lives in the majors and the runtime-behavior changes, so that's where the effort goes; every "unaffected" is a re-derived negative (grep quoted), never a vibe; the report leads with what the user must decide (blocked items, risks), not the chronology.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: write-tests
|
|
3
|
+
description: Author tests that actually catch regressions — picks WHAT to test by risk (branch points, boundaries, error paths, money/auth/concurrency), proves every new test can fail (a test never seen red is decoration), mocks only boundaries you don't own, and pins untested legacy code with labeled characterization tests before anything else touches it. Use this skill whenever the user says "write tests", "add tests", "test this", "add coverage", "unit test this function", "increase coverage", "add a regression test", "characterization tests", "TDD this", or "/write-tests" — even if they don't explicitly say "test skill". Do not use for diagnosing a failing test (use diagnose) or reviewing existing tests as part of a diff (use code-review / pr-review).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Write Tests
|
|
7
|
+
|
|
8
|
+
Author tests whose only job is to fail when the behavior breaks. Everything else — coverage numbers, green checkmarks, test count — is a proxy, and every proxy here can be gamed by accident. This skill makes the non-gameable thing the deliverable: each test is proven capable of failing, targets a behavior chosen by risk, and asserts what the caller can observe.
|
|
9
|
+
|
|
10
|
+
## When to use this skill
|
|
11
|
+
|
|
12
|
+
- The user says "write tests", "add tests", "test this", "add coverage", "unit test this", "add a regression test", "TDD this", "characterization tests", or "/write-tests".
|
|
13
|
+
- A feature just landed without tests, or plan-and-build / task-executor hands off a test-writing step.
|
|
14
|
+
- The user wants to change untested legacy code safely (pin first, then change — composes with `safe-refactor`).
|
|
15
|
+
|
|
16
|
+
Do **not** auto-trigger when the user is debugging a failing test (`diagnose`) or reviewing test quality in a diff (`code-review`, `pr-review`).
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
1. **Establish the baseline.** Detect the test runner (`package.json` scripts, `*.csproj`, `pytest.ini`, `go.mod`, `Cargo.toml`, `Makefile`) and run the existing suite **in non-interactive mode** — a `test` script that launches watch mode never exits and never yields a result; use the runner's single-run form (`CI=true`, `--run`, `--watchAll=false`, or the repo's `test:ci` script). Quote its summary line in your report. A result you didn't observe is `tests: not run`, never "passing". If the suite is already red, stop and surface it — new tests on a red suite are unverifiable.
|
|
21
|
+
2. **Read the whole unit under test**, not just the part the user pointed at. A claim about code you haven't opened is a hypothesis. Note every branch, early return, thrown/returned error, and boundary (empty, zero, max, null, duplicate, concurrent).
|
|
22
|
+
3. **Enumerate candidate behaviors and rank by risk.** Risk = likelihood of being wrong × cost of being wrong. Error paths, money, auth, deletion, retry/idempotency, and date/timezone logic outrank happy paths. Write the ranked list down before writing any test — it is the spec for step 5.
|
|
23
|
+
- ❌ One happy-path test per public method, top to bottom.
|
|
24
|
+
- ✅ Three tests on the refund-calculation branch and its rounding boundary, one on the happy path. The refund bug costs money; the happy path is exercised by every caller already.
|
|
25
|
+
4. **Check for existing coverage first.** Grep the test directory for the unit's name and read what's already asserted. Zero new tests is a valid outcome — if the ranked behaviors are covered, cite the existing test `file:line` and stop. Duplicating coverage is negative value: two tests break on every refactor instead of one.
|
|
26
|
+
5. **Write each test to a fixed shape.** Arrange-act-assert, one behavior per test, name = scenario + expected outcome (`refund_over_balance_is_rejected`, not `test_refund_2`). Assert observable behavior — return values, thrown errors, persisted state, emitted calls across a real boundary — never internal call order or private state.
|
|
27
|
+
6. **Prove each test can fail.** This is the step that separates a test from decoration, and it is not skippable:
|
|
28
|
+
- TDD path: write the test before the fix/feature, run it, quote the failure. Then make it pass.
|
|
29
|
+
- After-the-fact path: temporarily break the behavior under test (invert the condition, return the wrong value), run, confirm red, revert, confirm green. Quote both runs. **Mutation hygiene:** before reporting, run `git diff` on the production sources and confirm it contains none of your mutations — only the new test files. A leftover mutation is worse than no test; the green suite can't catch the one you made it agree with.
|
|
30
|
+
- A test that stays green against broken code is tautological. Fix its assertion or delete it — never ship it.
|
|
31
|
+
7. **Mock only boundaries you don't own** — network, clock, filesystem, database, message bus, external SDKs. Use the project's existing test-double convention (grep sibling tests for the fixture/mocking pattern and match it). Never mock the unit's own collaborators just to assert they were called: that welds the test to the implementation, and the test now fails on refactors and passes on bugs — the exact inversion of its job.
|
|
32
|
+
- ❌ `expect(userService.formatName).toHaveBeenCalledWith(user)` — asserts plumbing; survives `formatName` returning garbage.
|
|
33
|
+
- ✅ `expect(response.displayName).toBe("Rongo, Dennis")` — asserts the observable result; survives any internal restructure that keeps behavior.
|
|
34
|
+
8. **For untested legacy code: characterization tests first.** Capture what the code *does* (run it, record actual outputs for the ranked inputs), not what you believe it should do. If an output looks like a bug, pin it anyway with a comment: `// characterization: pins current (possibly wrong) behavior — see #issue`. Report the suspected bug separately; changing it is a behavior change, not a test.
|
|
35
|
+
9. **Final gate.** Run the full suite once more and quote the summary. Report: behaviors covered (with test names), behaviors deliberately not covered and why, and any suspected bugs found while reading. Rank the report — the two tests that matter, not fifteen equal bullets.
|
|
36
|
+
|
|
37
|
+
## Examples
|
|
38
|
+
|
|
39
|
+
### Example 1: "add tests for this service"
|
|
40
|
+
|
|
41
|
+
**User:** "Add tests for `InvoiceService.applyCredit`."
|
|
42
|
+
|
|
43
|
+
**Claude:** Runs the suite (quotes `47 passed`), reads the whole method plus the two private helpers it calls, ranks behaviors (credit > balance, negative credit, concurrent double-apply, happy path), finds the happy path already covered in `InvoiceServiceTests.cs:88`, writes the three missing tests, breaks the guard clause to prove each goes red, restores, quotes final green run.
|
|
44
|
+
|
|
45
|
+
### Example 2: pinning legacy code before a refactor
|
|
46
|
+
|
|
47
|
+
**User:** "I need to restructure this 400-line pricing function but there are no tests."
|
|
48
|
+
|
|
49
|
+
**Claude:** Enumerates its branches, runs the function against boundary inputs to record actual outputs, writes characterization tests pinning them (including one output that looks off, labeled and reported separately), proves they fail against a mutated copy, then hands off to `safe-refactor`.
|
|
50
|
+
|
|
51
|
+
## Anti-patterns
|
|
52
|
+
|
|
53
|
+
- ❌ Reporting "tests pass" without quoting an observed run — a result you didn't observe is "not run".
|
|
54
|
+
- ❌ Shipping a test you never saw red. Green-only tests are the largest source of fake coverage.
|
|
55
|
+
- ❌ Asserting a mock was called with the arguments you configured it with — the test proves the test.
|
|
56
|
+
- ❌ Testing private methods directly or reaching into internal state — test through the public surface; if a private method needs its own tests, that's a signal it wants to be its own module (note it, don't force it).
|
|
57
|
+
- ❌ Snapshot-testing large structures as a substitute for choosing an assertion — a 200-line snapshot fails on everything and tells the reader nothing.
|
|
58
|
+
- ❌ `sleep()`-based waits for async behavior — poll a condition or inject the clock; time-based tests are flaky by construction.
|
|
59
|
+
- ❌ Sharing mutable fixtures across tests so ordering matters — each test arranges its own world.
|
|
60
|
+
- ❌ Chasing a coverage percentage — coverage measures execution, not assertion. A line executed by a tautological test is worse than uncovered, because it looks protected.
|
|
61
|
+
- ❌ "Fixing" a legitimately-failing existing test by weakening its assertion to get to green — that's deleting a regression alarm; surface it instead.
|
|
62
|
+
- ✅ Ranked behavior list → match project conventions → observable assertions → every test proven red-capable → summary lines quoted.
|
|
63
|
+
|
|
64
|
+
## Notes
|
|
65
|
+
|
|
66
|
+
- Framework specifics (NUnit conventions for .NET APIs, RTL queries for React, etc.) come from the repo, not from memory — grep sibling tests and imitate the strongest existing example, matching its fixture, naming, and assertion style.
|
|
67
|
+
- When the unit is hard to test (needs six mocks, giant setup), say so in the report: test pain is design feedback. Offer `improve-codebase-architecture` rather than heroically mocking through it.
|
|
68
|
+
- Apply `think-like-fable` discipline throughout: effort follows risk (step 3), claims are re-derived not recognized (steps 1, 6), and the report leads with what matters.
|