skills/code-quality/code-review-assistant/SKILL.md
Performs structured code review on a diff or file set, producing inline comments with severity levels and a summary. Checks correctness, error handling, security, and maintainability — in that priority order. Use when reviewing a pull request, when the user asks for a code review, when preparing code for merge, or when a second opinion is needed on a change.
npx skillsauth add santosomar/general-secure-coding-agent-skills code-review-assistantInstall 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 code as a senior engineer would: find the bug that will page someone at 3am, not the missing semicolon. Every comment should be one the author couldn't have found with a linter.
Stop after any tier that produces a Blocking finding. There is no value in reporting naming nits on code that deletes the wrong rows.
Read in this order:
If the PR description is empty or says "misc fixes" — your first comment is asking for a description. You cannot review intent you don't know.
For every changed function, hold the stated intent (from the PR description) against the implementation. Specific checks:
< vs <=, len-1 vs len, slice end-exclusive vs inclusive. If you see a loop boundary change in the diff, verify against one concrete example.if (!isValid || !isEnabled) — expand De Morgan's in your head and verify the truth table is what the author meant.return in the middle of a function that previously had a single exit → does it skip a close() / unlock() / commit() that used to run?await on a promise-returning call is a silent future bug. Every call to an async function should be awaited, explicitly voided, or collected for Promise.all.For each changed function, walk the inputs:
| Input shape | Ask | | ------------------ | ------------------------------------------------------------ | | Collection / array | What if it's empty? What if it has one element? | | Optional / nullable| Is it checked before first deref? Is there a test for the null path? | | String | Empty string? Whitespace-only? Longer than the DB column? | | Number | Zero? Negative? Larger than the downstream type can hold? | | External call | What if it throws? Times out? Returns a shape you don't expect? | | Map / dict lookup | What if the key is absent? |
Catch blocks deserve extra scrutiny. A catch that just logs and continues turns a loud failure into a silent data corruption. Ask: is the system in a valid state after this catch runs? If not → Blocking.
Not a full audit — just the things a reviewer spots in a diff:
exec, eval, system, child_process, subprocess, Runtime.execpickle.loads, yaml.load, ObjectInputStream, unserialize)For anything beyond a spot check, defer: "→ run static-vulnerability-detector on this path before merge."
Do not comment on code the PR didn't touch. If a function was already 200 lines and the PR adds 3 lines to it, the 200-line problem is pre-existing tech debt, not this author's responsibility. At most: one summary-level comment suggesting a follow-up, never inline.
On code the PR did introduce:
| Level | Meaning | Author's obligation | | -------------- | ----------------------------------------------------- | ------------------------ | | Blocking | Merge will cause a bug, security issue, or data loss | Must fix before merge | | Should-fix | Will cause pain later; fix is clear and scoped | Fix now or open a follow-up with a link | | Nit | Preference. Reasonable people disagree. | Author's call. No re-review needed. | | Question | You don't understand; might be fine, might not | Author answers; you decide severity from the answer |
Do not mark something Blocking to win a style argument. Blocking means "this will break production." If you're not confident it will, it's Should-fix at most.
## Summary
Adds retry-with-backoff to the payment client.
1 blocking (retries non-idempotent POST), 1 should-fix, 2 nits.
Recommend addressing the blocking finding before approval.
## Findings
### src/payments/client.ts:45 [Blocking]
Retry wraps `POST /charges`. That endpoint is not idempotent — a
transient 503 after the charge succeeded server-side will retry and
double-charge the customer.
→ Either: pass an Idempotency-Key header and have the server dedupe,
or only retry on errors that guarantee the request never reached the
server (connection refused, DNS failure).
### src/payments/client.ts:52 [Should-fix]
Backoff is 2^attempt seconds, uncapped. Attempt 10 = 17 minutes.
→ Cap at 30s: `Math.min(2 ** attempt, 30)`.
### src/payments/client.ts:38 [Nit]
`let` could be `const` — `delayMs` is never reassigned.
### test/payments.test.ts:140 [Question]
This test asserts 3 retries, but I don't see where the max is
configured. Is it hardcoded or am I missing a fixture?
Diff:
async function deleteUser(userId) {
- const user = await db.users.findById(userId);
- if (!user) throw new NotFoundError();
- await db.users.delete(userId);
+ await db.users.delete(userId);
+ await cache.invalidate(`user:${userId}`);
}
Review:
db.users.delete(nonexistentId) — what does it do? If it's a no-op that returns 0 rows affected, fine. If it throws, the error changed from NotFoundError to a DB error — API contract break. → Question.delete succeeds but cache.invalidate throws, the user is gone from the DB but the cache still serves them. Next read is a ghost. → Should-fix: invalidate first, or catch-and-log the cache failure since the DB is source of truth.development
Extracts human-readable pseudocode from a verified formal artifact (Dafny, Lean, TLA+) while preserving the verified properties as annotations, so the proof-carrying logic can be reimplemented in a production language. Use when porting verified code to an unverified target, when documenting what a formal spec actually does, or when handing a verified algorithm to an implementer.
development
Translates natural-language or pseudocode descriptions of concurrent and distributed systems into TLA+ specifications ready for the TLC model checker. Identifies state variables, actions, type invariants, safety properties, and liveness properties from the description. Use when formalizing a protocol, when the user describes a distributed algorithm to verify, when designing a consensus or locking scheme, or when starting formal verification of a concurrent system.
testing
Reduces a TLA+ model so TLC can actually check it — shrinks constants, adds state constraints, abstracts data, or applies symmetry — when the state space is too large to enumerate. Use when TLC runs out of memory, when checking takes hours, or when a spec works at N=2 and you need confidence at larger scale.
development
TLA+-specific instance of model-guided repair — reads a TLC error trace, identifies the enabling condition that should have been false, strengthens the corresponding action, and maps the fix to source code. Use when TLC reports an invariant violation or deadlock and you have the code-to-TLA+ mapping from extraction.