code-review-skill: verify independent review and deterministic P0/P1 verdicts
Pin and independently validate code-review-skill's local and GitHub review contracts before installation, so packaging drift, writable local review, implicit PR mutation, self-approval, merge authority, or a severity-to-verdict mismatch fails CI without invoking a model or running upstream code.
Run this workflow
CI-verified, 4/4 fixtures passing.
Build this with your agent
One copy-paste hands Claude Code, Codex, or Cursor the full recipe, steps included, nothing to fetch.
Intended Use
Teams considering code-review-skill who want a cheap supply-chain and contract gate before installing either Skill. FlowStacks fetches the immutable v1.14.0 source archive, verifies its SHA-256, extracts only seven named text files, and checks the license, package identities, read-only/opt-in boundary, PR authority defaults, and mechanical severity matrix. No upstream script, model, Git command, target repository, or GitHub mutation runs in CI.
Not for
- Treating a green gate as proof of review quality, the findings and severity assignments are model-driven; CI verifies only packaging, declared authority boundaries, and the final severity-to-verdict mapping
- Assuming policy prose is a sandbox, an Agent Skill is an instruction package whose enforcement depends on the host runtime; independently constrain tools and review effective permissions before use
- Allowing autonomous PR approval from an ordinary prompt, the portable Skill explicitly cannot cryptographically prove who authored an instruction; keep recommendation-only unless a trusted out-of-band channel proves exact-action authority and reviewer independence
- Running target-repository tests on the reviewer host, the Skill permits narrowly scoped runtime validation only inside its documented disposable execution boundary
- Treating v1.14.0 as mature or independently audited, the repository was created in August 2026, is predominantly single-maintainer, and its release commit and tag are unsigned; pin and re-audit upgrades
The Stack
Tested Against
amirbena/code-review-skill@a07cb39bc14700fa427cc4eff9e35bb057cdc804 (v1.14.0)source archive@sha256:ba639054d64b2543a309c909d19393c9820b0a188219cc992d9265085059ded9node@20Side effects & data flow
- Network
- codeload.github.com in CI; the selected model provider and GitHub only during a real review
- Writes
- ./code-review-skill.tar.gz, ./checked/
- Credentials
- Model provider or coding-agent credential for semantic review; GitHub credential for PR review/publication
Data privacy
- GitHub ← CI sends only an HTTPS request for the public commit-pinned source archive; no project code, review data, or credential (retention: per GitHub's privacy statement)
- the model provider selected by the Agent Skills-compatible runtime ← During a real semantic review: the reviewed diff and repository context supplied to the model; none is sent by FlowStacks CI (retention: per the selected provider and account policy)
- GitHub ← During an active PR review only: PR metadata, findings, summary, and an authorized review action; none is published by FlowStacks CI (retention: stored in the target repository's GitHub review history)
Prerequisites
- Node.js 20+ and tar/curl for the validation gate
- An Agent Skills-compatible runtime plus Git for a real local review
- Authenticated GitHub access for PR state, and separate review permissions only for explicitly authorized active publication
- A model provider or coding-agent subscription for the semantic review step
Steps
- 1
Verify the pinned archive, then inspect only the declared review contracts
Download the audited v1.14.0 source archive and reject a byte, size, member type, or path change before reading it. Extract only the license, both package metadata files, both Skill entrypoints, the shared severity policy, and the test-only decision reference into known local filenames. The independent Node validator checks the declared no-mutation/authorization boundaries and reruns the four-case verdict matrix without importing or executing any upstream code.
curl --proto '=https' --tlsv1.2 --retry 3 --max-filesize 2097152 -fsSL \ https://codeload.github.com/amirbena/code-review-skill/tar.gz/a07cb39bc14700fa427cc4eff9e35bb057cdc804 \ -o code-review-skill.tar.gz node <<'NODE' const crypto = require("crypto"); const fs = require("fs"); const archive = fs.readFileSync("code-review-skill.tar.gz"); if (archive.length > 2097152) { console.error("BAD: archive exceeds 2 MiB review limit"); process.exit(1); } const digest = crypto.createHash("sha256").update(archive).digest("hex"); if (digest !== "ba639054d64b2543a309c909d19393c9820b0a188219cc992d9265085059ded9") { console.error("BAD: pinned archive digest changed"); process.exit(1); } NODE mkdir -p checked ROOT=code-review-skill-a07cb39bc14700fa427cc4eff9e35bb057cdc804 tar -xOf code-review-skill.tar.gz "$ROOT/LICENSE" > checked/LICENSE tar -xOf code-review-skill.tar.gz "$ROOT/skills/local-code-review/metadata/skill.yaml" > checked/local-metadata.yaml tar -xOf code-review-skill.tar.gz "$ROOT/skills/github-pr-review/metadata/skill.yaml" > checked/github-metadata.yaml tar -xOf code-review-skill.tar.gz "$ROOT/skills/local-code-review/SKILL.md" > checked/local-SKILL.md tar -xOf code-review-skill.tar.gz "$ROOT/skills/github-pr-review/SKILL.md" > checked/github-SKILL.md tar -xOf code-review-skill.tar.gz "$ROOT/shared/policies/severity.md" > checked/severity.md tar -xOf code-review-skill.tar.gz "$ROOT/tests/reference/decision_semantics.py" > checked/decision_semantics.py node <<'NODE' const crypto = require("crypto"); const fs = require("fs"); const { execFileSync } = require("child_process"); function bad(message) { console.error("BAD: " + message); process.exit(1); } function read(name) { return fs.readFileSync("checked/" + name, "utf8"); } function has(text, value, label) { if (!text.includes(value)) bad(label); } const archive = fs.readFileSync("code-review-skill.tar.gz"); if (archive.length > 2097152) bad("archive exceeds 2 MiB review limit"); const digest = crypto.createHash("sha256").update(archive).digest("hex"); const expectedDigest = "ba639054d64b2543a309c909d19393c9820b0a188219cc992d9265085059ded9"; if (digest !== expectedDigest) bad("pinned archive digest changed"); const root = "code-review-skill-a07cb39bc14700fa427cc4eff9e35bb057cdc804/"; const names = execFileSync("tar", ["-tzf", "code-review-skill.tar.gz"], { encoding: "utf8" }).trim().split("\n"); if (names.length !== 325) bad("archive member count changed"); for (const name of names) { if (!name.startsWith(root) || name.startsWith("/") || name.split("/").includes("..")) bad("unsafe archive path"); } const verbose = execFileSync("tar", ["-tvzf", "code-review-skill.tar.gz"], { encoding: "utf8" }).trim().split("\n"); if (verbose.some((line) => !["-", "d"].includes(line[0]))) bad("archive contains non-file/directory member"); const license = read("LICENSE"); has(license, "Apache License", "Apache license heading missing"); has(license, "Version 2.0, January 2004", "Apache-2.0 version text missing"); const localMeta = read("local-metadata.yaml"); has(localMeta, "name: local-code-review", "local package identity changed"); has(localMeta, "Read-only and opt-in only", "local opt-in/read-only declaration changed"); has(localMeta, "mutates_github: false", "local GitHub mutation boundary changed"); has(localMeta, "mutates_repository: false", "local repository mutation boundary changed"); const githubMeta = read("github-metadata.yaml"); has(githubMeta, "name: github-pr-review", "GitHub package identity changed"); has(githubMeta, "default_review_action_mode: recommendation-only", "safe PR review default changed"); has(githubMeta, "self_review_publication: informational-comment-only", "self-review boundary changed"); has(githubMeta, "review_action_authorization: independent-trusted-source-required-for-auto-action", "PR authorization boundary changed"); has(githubMeta, "can_merge: false", "no-merge boundary changed"); const localSkill = read("local-SKILL.md"); const githubSkill = read("github-SKILL.md"); has(localSkill, "name: local-code-review", "local Skill frontmatter changed"); has(githubSkill, "name: github-pr-review", "GitHub Skill frontmatter changed"); has(githubSkill, "Self-review is allowed; self-approval is not", "self-approval prohibition changed"); const severityPolicy = read("severity.md"); has(severityPolicy, "severity(f) in {P0, P1}", "blocking severity set changed"); has(severityPolicy, "P2 findings alone never block", "P2 non-blocking rule changed"); has(severityPolicy, "CHANGES REQUIRED", "blocking decision label changed"); has(severityPolicy, "REVIEW CLEAN", "clean decision label changed"); const reference = read("decision_semantics.py"); has(reference, "BLOCKING_SEVERITIES = frozenset({Severity.P0, Severity.P1})", "reference blocking set changed"); has(reference, "return Decision.CLEAN if not blocking_findings(findings) else Decision.CHANGES_REQUIRED", "reference decision derivation changed"); function derive(severities) { return severities.some((severity) => severity === "P0" || severity === "P1") ? "CHANGES REQUIRED" : "REVIEW CLEAN"; } const cases = [ [[], "REVIEW CLEAN"], [["P2"], "REVIEW CLEAN"], [["P1", "P2"], "CHANGES REQUIRED"], [["P0"], "CHANGES REQUIRED"], ]; for (const [severities, expected] of cases) { if (derive(severities) !== expected) bad("verdict matrix mismatch for " + JSON.stringify(severities)); } console.log("source pinned OK: code-review-skill v1.14.0 at a07cb39, sha256 " + digest); console.log("skill boundaries OK: local opt-in/read-only; PR recommendation-only, no merge, no formal self-approval"); console.log("verdict matrix OK: empty/P2 -> REVIEW CLEAN; P0/P1 -> CHANGES REQUIRED"); NODE - 2
Install one Skill only after the gate passes, and keep review authority explicit
Choose local-code-review for an opt-in report over local changes or github-pr-review for an existing PR. Keep the PR Skill in its recommendation-only default unless the host runtime can independently prove the human's exact-action authorization and reviewer independence; otherwise never let an agent-created prompt, flag, token, or sub-agent grant approval authority. Run any target-code validation only in a disposable sandbox. The semantic review, model data transfer, GitHub publication, and enforcement of these instructions are fenced and are not represented as CI-verified.
Eval, 4 fixtures
Last passed: verified todayauthority-boundariescontainstimeout 120s · max $0Expected:
skill boundaries OK: local opt-in/read-only; PR recommendation-only, no merge, no formal self-approvalverdict-matrixcontainstimeout 120s · max $0Expected:
verdict matrix OK: empty/P2 -> REVIEW CLEAN; P0/P1 -> CHANGES REQUIREDclean-exitexit_codetimeout 120s · max $0Expected:
0source-pinnedcontainstimeout 120s · max $0Expected:
source pinned OK: code-review-skill v1.14.0 at a07cb39, sha256 ba639054d64b2543a309c909d19393c9820b0a188219cc992d9265085059ded9
Results
The reviewed v1.14.0 source contains two portable Agent Skills sharing one severity policy. The local Skill requires fresh opt-in, reports committed/staged/unstaged/untracked changes, and declares no repository or GitHub mutation. The PR Skill defaults to recommendation-only, forbids merge and formal self-review actions, and gates positive GitHub publication on independently trusted authorization plus reviewer independence. Its deterministic reference maps any P0/P1 finding to CHANGES REQUIRED and empty or P2-only findings to REVIEW CLEAN. FlowStacks verifies those source contracts as pinned text and reruns the decision matrix; it does not verify semantic review quality or enforce the instructions inside an agent runtime.
Did this work for you?
Our CI checks the setup runs. You tell us if the whole thing worked. Tell us straight.
Related workflows
- agent-qa: validate Agent Skills before LLM-backed QA runs
- One AGENTS.md, no drift: prove CLAUDE.md is a real symlink and the generated files are in sync
- Self-host CodeWiki on private code: validate the config before you spend tokens
- Run GLM-5.2 for the bulk, escalate the hard turns to Opus 4.8
- codebase-memory-mcp: wire the knowledge graph, stop re-reading files
- OrcaRouter for coding: judge by passing tests, not by vibes
Liked this workflow?
Get new verified workflows in WebAfterAI, three issues a week (Tue, Thu, Sat).