skills/skimmable/SKILL.md
Enforce code readability and state minimisation before opening or updating a pull request. Use when code is functionally complete and needs a final simplification pass focused on skimmability: reducing arguments, removing optionality and overrides, collapsing unnecessary abstractions, preferring discriminated unions, adding assertions at boundaries, handling variants exhaustively, deleting incidental changes, and making the diff shorter, clearer, and easier to review.
npx skillsauth add ckorhonen/claude-skills skimmableInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Review the change as if a tired reviewer must understand it in one fast pass.
Prefer deleting code to explaining code.
Keep the pass narrow: improve readability, state modelling, and diff clarity without changing product behaviour unless the current behaviour is obviously dead, redundant, or inconsistent with types.
Apply these rules aggressively:
Follow this order:
Remove changes that do not help the main goal of the PR.
Delete:
If a simplification requires a separate behavioural decision, stop and call it out instead of smuggling it into the readability pass.
Reduce complexity in types before touching control flow.
Prefer:
Ask of every argument:
Make happy paths short and visible.
Prefer:
Avoid:
if pyramidselse branches for impossible statestry/catch for expected valuesAssert at boundaries.
Use assertions when:
After the boundary, write simple code that trusts the asserted shape.
When code depends on a variant, make the variant explicit and handle all cases.
Prefer a switch on a discriminant.
End with an exhaustive check or assertion if the language allows it.
Do not silently ignore unknown values.
Inline small one-use wrappers when they hide the real logic.
Keep helper functions only when they:
Do not split a 20-line readable function into five 4-line helpers just to look tidy.
Bad signs:
Prefer:
If a boolean changes behaviour, model the behaviour directly.
Use:
kind: "draft" | "published"source: "cache" | "network"status: "idle" | "loading" | "success" | "error"Avoid:
isDraftuseCacheisLoading plus error plus data on the same object unless the state machine truly permits all combinationsAt boundaries, assert once. Inside, trust the assertion.
Avoid repetitive checks like:
if (!user) returnif (!user.id) returnif (typeof user.id !== "string") returnwhen types and boundary validation already guarantee the shape.
If there is a default behaviour, encode it directly.
Do not add overrides like sortOrder = "desc", strategy = "auto", separator = ",", shouldLog = false unless callers truly need them.
Every option multiplies states.
Use these prompts during the pass:
Before:
function createReport(
userId: string,
includeDrafts = false,
sortBy: "date" | "name" = "date",
sortDirection: "asc" | "desc" = "desc",
limit?: number,
overrides?: { timezone?: string; now?: Date }
) {
const now = overrides?.now ?? new Date()
const timezone = overrides?.timezone ?? "UTC"
return buildReport({
userId,
includeDrafts,
sortBy,
sortDirection,
limit,
now,
timezone,
})
}
After:
type ReportScope =
| { kind: "published" }
| { kind: "all" }
function createReport(userId: string, scope: ReportScope) {
return buildReport({ userId, scope, now: new Date(), timezone: "UTC" })
}
Prefer a smaller API. Add configurability only when a real caller needs it.
Before:
type SaveState = {
isSaving: boolean
error?: string
receipt?: string
}
After:
type SaveState =
| { kind: "idle" }
| { kind: "saving" }
| { kind: "error"; message: string }
| { kind: "saved"; receipt: string }
The second version prevents impossible combinations.
Before:
function badgeColor(status: Status) {
if (status === "success") return "green"
if (status === "error") return "red"
return "gray"
}
After:
function badgeColor(status: Status) {
switch (status) {
case "success":
return "green"
case "error":
return "red"
case "pending":
return "gray"
default:
return assertNever(status)
}
}
Unknown variants should fail loudly during development.
Before:
async function loadAccount(accountId: string) {
const raw = await db.accounts.find(accountId)
if (!raw) {
throw new Error("Account not found")
}
if (typeof raw.email !== "string") {
throw new Error("Invalid account email")
}
return raw
}
function sendWelcomeEmail(account: any) {
if (!account) return
if (!account.email) return
mailer.send(account.email)
}
After:
async function loadAccount(accountId: string): Promise<Account> {
const raw = await db.accounts.find(accountId)
assert(raw, "Account not found")
assert(typeof raw.email === "string", "Invalid account email")
return raw as Account
}
function sendWelcomeEmail(account: Account) {
mailer.send(account.email)
}
Validate once. Keep the typed core simple.
Before:
function visibleItems(items: Item[], user?: User) {
if (user) {
if (user.isAdmin) {
return items
} else {
return items.filter((item) => !item.hidden)
}
} else {
return []
}
}
After:
function visibleItems(items: Item[], user?: User) {
if (!user) return []
if (user.isAdmin) return items
return items.filter((item) => !item.hidden)
}
Flatten the shape of the code.
Before:
function checkout(cart: Cart) {
validateCart(cart)
const subtotal = calculateSubtotal(cart)
const tax = calculateTax(subtotal)
const total = calculateTotal(subtotal, tax)
return finalizeCheckout(total)
}
function calculateTotal(subtotal: number, tax: number) {
return subtotal + tax
}
After:
function checkout(cart: Cart) {
assert(cart.items.length > 0, "Cart is empty")
const subtotal = sum(cart.items.map((item) => item.price))
const tax = subtotal * TAX_RATE
const total = subtotal + tax
return finalizeCheckout(total)
}
Keep trivial logic close to use.
Before opening or updating the PR, verify:
If a choice is between clever and obvious, choose obvious.
If a choice is between flexible and simple, choose simple.
If a choice is between adding code and deleting code, prefer deleting code.
documentation
Create or expand an Idea.md / IDEA.md file from a rough description, existing repo, conversation history, notes, or other early-stage product inputs. Use when the user asks to "write an Idea.md", "turn this into an idea file", "capture this product idea", "expand this concept", or wants a repo-grounded concept brief before validation, PRD, or implementation work.
development
Write structured implementation plans from specs or requirements before touching code. Use when given a spec, requirements doc, or feature description, when user says "plan this out", "write a plan for", "how should we implement", or before starting any multi-step coding task.
testing
Expert guidance for video editing with ffmpeg, encoding best practices, and quality optimization. Use when working with video files, transcoding, remuxing, encoding settings, color spaces, or troubleshooting video quality issues.
development
Opinionated constraints for building better interfaces with agents. Use when building UI components, implementing animations, designing layouts, reviewing frontend accessibility, or working with Tailwind CSS, motion/react, or accessible primitives like Radix/Base UI.