Use when the user asks to review CURRENT uncommitted changes (staged, unstaged, branch diff) before committing, or to synthesize JiT tests against the diff — QA + lead-engineer verdict on quality, architecture, cleanup. Needs a diff, not old commits.
61
77%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
Fix and improve this skill with Tessl
tessl review fix ./plugins/flowai/skills/review/SKILL.mdAct as QA engineer + lead engineer simultaneously. Review only the current changes (diff from the task branch or unstaged/staged changes) against the original request and plan. Verify task completion AND code quality in a single pass. In the same pass, interleave the JiT subset: synthesize ephemeral Catching JiTTests — pass on the parent revision, fail on the diff revision — probing behavioural regressions static review cannot see.
Input sources: git diff (unstaged / staged / <base>..HEAD); the original User Request (chat history); the Plan (task tool or task file via the tasks role in AGENTS.md); project conventions (AGENTS.md, linter/formatter configs); parent worktree (git worktree add <parent-sha> with a session-id'd path, or git show <parent-sha>:<file> fallback) — used by the JiT subset to verify pass-on-parent behaviour of synthesized catching tests.
<step_by_step>
Empty Diff Guard — git diff --stat, git diff --cached --stat, git status --short. STOP only when ALL THREE are empty. A ?? entry in git status --short IS a change: two empty git diff --stat outputs plus one untracked file mean a non-empty review, so proceed. System gitStatus snapshot can be stale (hooks / parallel processes); if live status shows unauthored files clean per snapshot, ask user before staging.
Pre-flight Project Check
deno.json → deno task check/test; package.json → check/lint/test script; Makefile check → make check; pyproject.toml → pytest/ruff check .; go.mod → go vet ./... && go test ./...) → else "No automated checks configured" in the report and JiT subset disables (Rule 10). Do NOT guess. "Declares it" means the instructions record an invocation — a command line, or a task or script name that a manifest in the repository defines. A section that only names an interface (a "Standard Interface" list saying a check command exists) while the sections meant for the actual commands are empty declares nothing; fall through to manifest detection.deno task check without deno.json, no npm test without package.json, no make check without a Makefile. This rule chooses the PROJECT CHECK only. It does not stop step 4b from running a located test module with the runner that module is written for — deno test utils_test.ts, python -m pytest tests/test_x.py, go test ./pkg — whether or not a manifest exists.deno.lock, node_modules/, __pycache__/, .pytest_cache/). A leftover gets swept into the commit.[critical] and continue review. A command that never ran has not failed: exit 127, command not found, or a missing task or script name means the project declares no runnable check. Report it as [skipped] <command> — not runnable: <error> under Automated Checks, say "No automated checks configured", disable the JiT subset (Rule 10), and raise no [critical] for it.HEAD; commit-range → <range-start>^). Prefer git worktree add <SCRATCH>/jit-parent-<sid> <parent-sha> (full runnable tree); use
git show <parent-sha>:<path> fallback ONLY if worktree-add fails. BEFORE any JiT synthesis, run the SAME project test/check command from 2a inside the parent worktree to verify baseline is green. Two things do NOT satisfy this and are the usual substitutes: the run of that command you already did on the DIFF side (it says nothing about the parent), and running only your synthesized JiT probes against the parent (they are not the project's suite). The evidence this step wants is one invocation of the project's own command whose working directory is the parent tree. Fallback path OR red baseline → "JiT disabled — parent baseline unavailable/red" in Degradation Notes; review continues without the JiT subset.Gather Context
SRS, SDS, and tasks from AGENTS.md; read the resolved SRS/SDS files if their content is not already in context. A required role missing → report it and continue only with review steps that do not depend on that role. AGENTS.md is the FIRST document you open, and no conventional location is probed before it. The doc layout you have seen most often is one project's convention, not the shape of every project; a speculative cat <guessed-path> 2>/dev/null batched alongside the AGENTS.md read is still reading the wrong file first, and discarding its output afterwards does not undo that. Projects that keep their SRS and SDS somewhere else entirely exist, and they are exactly the ones this step is for.git diff (unstaged), git diff --cached (staged), or git log --oneline <base>..HEAD + git diff <base>..HEAD for branch-based changes.git diff does NOT show them — read each untracked file from step 1's git status and include it in scope.tasks role / task list).git log -1 --pretty=%B <parent-sha>..HEAD (or commit messages of the range). Optionally gh pr view --json body IF gh is available AND the branch has a PR; on missing/error proceed silently — PR body is a bonus.Parallel Delegation (after gathering context):
git diff --stat plus git diff --cached --stat plus wc -l over every untracked file from step 1. Count the lines before deciding; git diff --stat alone reports 0 for an untracked-only change set, and file COUNT is not the threshold (one 173-line file is a large diff, not a small one).Task, Agent, task; on Codex the tool is spawn_agent — pass agent_type="console-expert" and fork_turns="none" (a full-history fork cannot carry an agent type), then collect with wait_agent), each returning its result to you. Batching several tool calls in your own thread is NOT delegation: the point is to keep the check output out of your context, which only a separate context achieves. Establish availability by LOOKING, never by assuming: list the agent directories (ls .claude/agents/ .cursor/agents/ .opencode/agent/ "${CODEX_HOME:-$HOME/.codex}/agents/" 2>/dev/null). A file there — .md for most hosts, <name>.toml under the Codex home — means that agent is installed and dispatchable — dispatch it by name. Running inside a sandbox, a test harness, or an automated run is NOT evidence of missing subagents and is never a reason to skip: decide from the listing, not from where you think you are. Run the listing BEFORE any inline check, and quote its output in ### Degradation Notes whenever you claim delegation was unavailable. The claim is a factual report about this machine, and without the command's output behind it you are stating something you did not check: a review that ran the checks inline and wrote Parallel delegation unavailable on top of an installed agent directory reported a fact that was false. Inline is permitted only when the listing came back empty AND no subagent tool is in your toolset — both verified, not assumed. Do not silently call a batch of your own tool calls a delegation. Speed is not one of the two conditions. "Faster than spinning up a subagent", "the file is only 181 lines", "I have already read it" — none of these reopen the shortcut, which is decided once, by the line count in step 1, and not revisited per task. The point of delegation is to keep the check output out of YOUR context; doing the work yourself because it is quick spends exactly what the step exists to save. And it is per task, not per review: SA1 and SA2 are two dispatches, so delegating one and running the other inline is a partial degradation that must be recorded as such — it is not delegation performed.
console-expert); return pass/fail + full output.TODO, FIXME, HACK, XXX, console.log, temp_*, *.tmp, *.bak, hardcoded-secret patterns. Same delegation; return findings list.run_in_background: false parameter or equivalent), set it. If you still get back only a handle, continue that agent by its id — send it a message or resume it and ask for its result. Do NOT feed an agent id to a background-task polling tool: the two id kinds are different, and the poll answers No task found with ID: <id> however long you give it. Never write a delegated finding you did not actually receive. And never end your turn while a dispatch is outstanding. Sitting idle until a notification arrives is not waiting — the turn closes, the session ends there, and the review produces no verdict, no report and no findings at all: strictly worse than the inline run you were avoiding. Collecting is an action you take, not an event you receive.### Degradation Notes. No hard dependency on delegation success.QA: Task Completion
[critical] Missing.[critical] Phantom completion.SRS / SDS / README). A doc still describing behaviour the diff removed or changed → [warning] Doc drift naming the doc file:line and the contradicting hunk. Drift alone is NEVER [critical] and never blocks the verdict — the commit phase owns Documentation Sync, so escalating here would stop the workflow before the step that fixes it. Do NOT edit the doc in this review.4a. FR Coverage Audit (blocking gate — see Requirements Lifecycle in AGENTS.md)
SRS role resolves from AGENTS.md AND that AGENTS.md mandates FR traceability (acceptance references, [REF:fr:<id>] code markers). Neither present → skip 4a entirely, record FR coverage gate not applicable — project declares no requirements lifecycle in ### Degradation Notes, and NEVER emit a missing code marker / no acceptance reference finding. Inventing the convention for a project that does not use it turns every clean diff into Request Changes. The SRS role resolves but its file does not exist → skip 4a the same way: record FR coverage gate not applicable — <SRS path> does not exist in ### Degradation Notes, and NEVER emit a [critical] (or any blocking) finding for the missing document or for an acceptance reference it would hold. Creating the requirements document is init's job, not this review's; the commit gate reports the same absence in one line and commits.implements:; (b) FR sections added/modified in the diff to SRS; (c) [REF:fr:<id>] SALP markers touched in the diff.**Acceptance:** with a runnable ref (test path::name, benchmark id, command, or manual — <reviewer>); missing/placeholder → [critical] no acceptance reference. (2) Run the evidence command (or deno run -A scripts/check-fr-coverage.ts FR-<ID>); non-zero / failing / manual without reviewer → [critical] acceptance fails. (3) FR claimed implemented but no [REF:fr:<id>] marker in changed source → [critical] missing code marker. (4) DoD [x] with no evidence run/cached pass → [critical] Phantom completion. (5) Only when the project's AGENTS.md declares the mandatory **Rationale:** field (a Rationale-as-gate rule or an equivalent sentence): every FR section the diff ADDS or MODIFIES in SRS — scope (b) only, never an FR the diff merely cites — carries a non-empty **Rationale:** line saying why the requirement exists; missing or empty → [critical] no rationale, naming the FR. Report it; do not write the rationale yourself.Approve while any FR-gate issue remains.4b. QA: Existing-Suite Execution (blocking gate — Rule 13)
exclude list and any path it is scoped to. Each one can surface coverage the other two miss, and skipping the last two is how a suite that exists gets reported as absent.exclude or a scoped path argument skips — is precisely what this gate exists to reach: it was excluded for cost, not because it stopped being coverage. Before deciding what to run, read AGENTS.md for a section naming where such tests live and how to run them, and check the test-runner config for exclude entries and for scoped paths in the check task. If either points at a suite covering your changed symbols, run it. "The project check passed" is not an answer to this step; name the modules you located and the command you ran on each.[critical]; verdict cannot be Approve. A pre-existing test that contradicts the diff is a finding under Rule 12, not a file to update. Use the project's scoped test command when it has one; otherwise run the module with the runner it is written for (deno test <file>, python -m pytest <file>, go test ./<pkg>). A missing manifest does not stop this run — the manifest rule of step 2 picks the project check only — and step 2's cleanup applies to whatever the run leaves behind.[warning].### Degradation Notes; never fabricate a pass. The gate stays unsatisfied, yet nothing in the change has been shown wrong, so the verdict is Needs Discussion — not Request Changes and not Approve: the human decides whether to merge without that run.QA: Hygiene (use SA2 result if available; else inline)
Hygiene: clean (N patterns scanned over the diff).print output and hardcoded secrets or localhost URLs.TODO/FIXME/HACK/XXX introduced in this diff (distinguish from pre-existing ones).[warning] Entire directory deleted — confirm intentional; ask the user to verify before proceeding.Code Review: Design & Architecture
Code Review: Implementation Quality
[warning] (Rule 6).any, untyped parameters, missing return types (where project conventions require).Code Review: Readability & Style
8a. Mutant + Catching Test Synthesis (JiT) (skip on pure-deletion diff or JiT-disabled flag)
8b. Dual-Run + Filter (JiT) (skip if Step 8a skipped)
Mutant kill-rate skipped — single test invocation exceeded 30 s threshold (recorded N s) in Degradation Notes so the lost signal stays visible.Run Automated Checks (collect from step 2 and/or SA1)
Review Report — verdict on first line. Include JiT sections (Intents, Catching Tests, Uncovered Risks, Degradation Notes) only when the JiT subset ran (or was disabled — Degradation Notes then explains why). Section order:
## Review: [Approve | Request Changes | Needs Discussion] <!-- count your [critical] findings BEFORE writing this line; zero criticals with no surviving catching test and no unsatisfied applicable gate => Approve -->
### Verdict (plain language) — 2–4 sentences a non-coder acts on, answering ALL four questions explicitly, each in words of its own: (1) is the task complete; (2) is the design sound — say so even with no concern ("the design is sound"); (3) the key risks, or that there are none; (4) the next step. A blocking finding never replaces the design answer: a change can be well designed and still not mergeable. Accept WITHOUT reading the diff. MUST come first.
### Intents (≤5)
### QA Findings — [severity] file:line — description
### Code Review Findings — [severity] file:line — description
### Catching Tests (pass on parent, fail on diff) — name, intent #, mutant killed?, failure, file:line
### Uncovered Risks — risk + reason no test (non-deterministic / I/O / etc.)
### Existing-Suite Check — pre-existing modules (incl. caller tests) + run result, or why not run
### Automated Checks — [pass|fail|skipped] command — summary
### Degradation Notes — which JiT step was skipped and why
### Summary — requirements X/Y; catching tests N; hygiene scan result (findings or `clean`); critical/warning/nit counts
### Diff (optional) — offer diff/details for optional inspection; verdict stands without it; never block (Model B). MUST be the report's last section.This report is the answer of the review. Step 11 is the only thing that follows it. When you made a scratch directory, the save / discard question is the last line of this answer: name the scratch path, ask, and end your turn. Delete or move nothing before the reply — the end of your turn is not a timeout, and default for timeout/ambiguous means an unclear reply, not a missing one.
Every later turn of this conversation stays inside the review. The author's reply to the report — an answer to a question it asked, "go ahead", a request to fix a finding, add tests, fill a missing field, or commit — does not start the next workflow. Reply in words only: what the answer changes in the verdict, or that it changes nothing; which workflow does the requested work (implement for a fix, commit or review-and-commit for a commit), started by name; and that the verdict stands until a fresh review. Edit no file, stage nothing, commit nothing, and do not offer to do any of these yourself — an offer the author accepts ends in the same edit or commit. Committing belongs to the commit workflow, which owns the documentation sync and atomic grouping this review does not run.
After the verdict the review is over — including when the author asks you to fix what you found. A reviewer who repairs the code then reports on their own work, and the finding that made the verdict disappears with the repair, so nobody can check it afterwards. Answer that the fix is the author's to make, or belongs to a separate implement run that the author starts by invoking that workflow by name. A reply in this conversation — "yes", "go ahead", "fix it", "add the tests", an answer to a question your report asked — is not that invocation, however explicit, and does not hand the fix to you. Leave the verdict where it stands. This holds for production files, for tests, for the requirement documents in the diff, AND for pre-existing tests: when a pre-existing test contradicts the diff, the contradiction IS the finding, and editing either side deletes the evidence rather than resolving it. Continuing to run the project's checks or to explain a fix in words is fine; writing the fix is not.
Verdict selection (decide in this order, first match wins): (1) Request Changes — ≥1 [critical] finding, ≥1 surviving catching test, or an APPLICABLE blocking gate (4a FR coverage where 4a applies at all, 4b existing-suite, Rule 13) still unsatisfied — except a 4b module that could not run locally, which goes to (2). (2) Needs Discussion — no blocker, but a design decision genuinely needs the human before merging, or a located pre-existing module could not run locally (step 4b). (3) Approve — everything else, and this branch is compulsory once reached. [warning] and [nit] findings NEVER force Request Changes on their own: list them, hand them to the author, approve. An Approve carrying warnings is the normal outcome, not a contradiction. Rank findings top-5 by severity × uniqueness. No issues AND zero catching tests → "Changes look good. All requirements covered, no issues found, no behavioural regressions detected." (last clause only when JiT actually ran).
save <name> / save all / discard all. On save: propose destination beside file-under-test, confirm, git mv, stage. On discard all (default for timeout/ambiguous): delete entire scratch directory, leave no stray files. A scratch directory you made is yours to remove, and the verdict is not the end of the run while one is still on disk. This step goes missing in the same way every time: the report reads finished, the catching test has already done its job, and the leftover directory is invisible to you and permanent to everyone else — a run that names its scratch path in the report and then leaves it there without asking has documented the mess rather than cleaned it. Whether you wait for the answer depends on the report's role, stated right after the report template. Removing the parent worktree is a different cleanup and does not cover this.</step_by_step>
korchasa/flowai-plugins
240d92b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.