Review gate
How to run the whiteboard repo's per-diff change-review gate — args, return contract, and the externalized-criteria mechanism for review.workflow.mjs. Use when running a standalone review gate (pre-merge, pre-un-draft, ad-hoc) or when authoring/tuning the review workflow that dev-loop composes.From its SKILL.md
npx -y skills add kamiazya/whiteboard --skill review-gateAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.8 KB, as published. Nobody here has run it
Review Gate (whiteboard)
review.workflow.mjs is the generic per-diff change-review gate: parallel
multi-dimension review + adversarial verify + QA smoke, returning one
consolidated report. It is the most-invoked workflow in the repo — every
dev-loop composes it as its review phase — but is also runnable standalone
against any git range.
Run it
Workflow({ scriptPath: '.claude/workflows/review.workflow.mjs',
args: { range?, files?, cwd?, dimensions?, qaScenarios?,
dogfood?, appUrl?, dogfoodPersonas?, codex? } })
- range: a git range understood by
git diff/git log. Default'origin/main...HEAD'. - files: optional explicit scope (array of paths). When omitted, each
reviewer agent discovers changed files itself via
git diff --name-only <range>. - cwd: optional worktree path so a composed parent (
dev-loop) can point review at an isolated worktree. When set, agents run git asgit -C <cwd> ...and Read files at that absolute path. - dimensions: defaults to
['correctness', 'contract', 'boundary', 'test-coverage'](a 4-dimension subset of the 6 documented in.claude/agents/reviewer-dimension.md;dead-codeandauthare opt-in extras — pass them explicitly when relevant).securityalways runs in addition, unconditionally. Each entry is either a legacy plain string (criteria stays embedded inreviewer-dimension.md) or{name, content}wherecontentis authoritative externalized criteria injected into the reviewer prompt (see below). A malformed non-string entry (e.g. missingname) throws instead of silently propagatingundefined. - qaScenarios: defaults to
['smoke', 'error-recovery', 'startup']. - dogfood: optional live persona pass over the touched flow. Default
false— requires a running app (appUrl). - appUrl: the running app URL for the dogfood phase.
- dogfoodPersonas: number of persona passes when
dogfoodis on. Default1. - codex: adds a Codex second-opinion review lane. Default
false. If the Codex runtime is unavailable, the lane returnsnulland is reported viacodexUnavailable— it never counts as a failed partition dimension and never blocks the gate.
Return contract
{
range: string,
files: string[] | null,
summary: {
rawFindings: number,
confirmed: number,
critical: number,
high: number,
qaFail: number,
dogfoodFriction: number,
failedDimensions: number,
},
failedDimensions: string[], // partition dimensions whose agent returned nothing
notApplicable: string[], // dimensions that explicitly opted out (notApplicable:true, no findings)
codexUnavailable?: boolean, // present ONLY when args.codex was true
confirmedFindings: Array<Finding & { dimension: string, verdict: {...} }>,
qa: Array<{ scenario: string, status: 'pass'|'fail'|'skip', notes?: string }>,
dogfood: Array<{ persona?: string, goalAchieved: 'yes'|'partial'|'no'|'skip', summary: string, friction: [...] }>,
}
persona is optional — the workflow's DOGFOOD_SCHEMA does not list it in
required (only goalAchieved, summary, friction are), so a
dogfood-persona agent run can legitimately omit it. Destructure with a
fallback (run.persona ?? 'unlabeled') rather than assuming it is always
present.
Coverage-honesty fields mirror audit-triage: check failedDimensions
before trusting a clean run — a failed mandatory partition lane (e.g.
security, correctness) is synthesized into confirmedFindings as a HIGH
mandatory review lane failed: <key> finding rather than silently degrading
to zero findings, so the gate can never read "clean" just because a lane
never ran. notApplicable lists dimensions an agent explicitly opted out of
for this diff (e.g. a docs-only change has nothing for auth to review).
Externalized criteria (resources/*.md)
Like audit-triage, each review dimension's detailed criteria can live in
.claude/skills/review-gate/resources/<dimension>.md (Title-Case # <Name>
heading + ## Criteria + numbered checks) instead of only the embedded
summary in .claude/agents/reviewer-dimension.md. To run with the
authoritative externalized criteria:
Glob('.claude/skills/review-gate/resources/*.md').Readeach file; derivenamefrom the filename (kebab-case, minus.md), not the Title-Case#heading, sonamematches the canonical dimension identifiers used elsewhere (reviewer-dimension.md's## Dimensionslist, the defaultdimensionsarray,failedDimensions,notApplicable). Use the full file body ascontent.- Pass
args.dimensions = [{name, content}, ...]— a subset is fine, narrow to the dimensions relevant to this run.
Workflow scripts have no filesystem access — the workflow cannot
glob/read resources/*.md itself. The launching session (this skill's
caller) must do the glob+read and pass content through args; that is why
the mechanism accepts {name, content} objects rather than a directory path.
Do not shortcut step 2 by passing a PATH as content. The reviewing
agent does have Read, so "read resources/<name>.md and apply it" looks
equivalent and saves inlining hundreds of lines into args. Measured on a
real run, it is not: of four lanes given a path-only pointer, two never
opened the file and reviewed from the pointer sentence alone. The two that
did read it were the ones whose pointer carried an emphatic, specific
instruction — which is exactly the kind of difference you cannot rely on.
Inlining the body is what makes the criteria unconditionally present in the
prompt, and a lane that silently reviewed against nothing is indistinguishable
in the report from one that found no issues.
.claude/agents/reviewer-dimension.md still carries its own embedded ## Dimensions summary (contract, boundary, dead-code, test-coverage, auth,
correctness) as the legacy fallback — used when a caller passes plain
dimension strings, or omits dimensions entirely. This is deliberate
duplication, the same pattern audit-triage documents: resources/*.md is
authoritative when content is supplied; the agent's embedded text is only
the safety-net default.
Discipline
- One level of nesting only:
reviewmust NOT callworkflow()(its dogfood phase is an inline agent + Playwright pass, not a nested workflow) — see theworkflow-authoringskill's one-level-nesting gotcha. securityis always-on and failable, exactly like any requested dimension — it is not optional and is included in the partition whose failure gates the run.- The optional
codexlane sits outside the partition entirely: it is a second opinion, surfaced only viacodexUnavailable, and never counted as a failed dimension.
Reject guards that cannot fail
A recurring defect class in this repo: an assertion that reads as coverage while being satisfied by its own declaration. It is worse than no assertion, because it stops anyone from writing the real one. Every review must check new guards for this shape, and the answer must come from running the test, not from reading it.
Observed forms, each of which shipped here at least once:
- Self-satisfying scan. A test greps the whole file for an identifier
that the file's own
export constprovides. It passes no matter what the guarded code does. - Absence of something that never existed. Asserting a name is not registered when no such name was ever registered. It can never fail, and it implies to a future reader that the thing once existed and is being kept out.
- Unreachable exhaustiveness. A
switchover a hand-written array already typed as the union it claims to check — theneverbranch is unreachable by construction, so the test only fails when the type-level pin beside it fails first. - Vacuous iteration. A
for/everyover a collection that can be empty, with no assertion that it is non-empty. An empty scan set is a green run. Build-time source scans (import.meta.glob) are the usual offender. - Proxy indicator. Asserting a stand-in (a count, a flag, a log line) that stays true when the real behavior breaks.
The mechanical check is a mutation: break the thing the guard protects
and confirm the guard goes red. Restore the deleted tool, reintroduce the
banned import, empty the scanned set. A guard that stays green under its own
mutation does not ship. When mutating, confirm the red is for the intended
reason — a ReferenceError from a missing import is not a real red, and has
been mistaken for one here.