一键导入
pre-pr-review
Self-review checklist before creating a PR — repo checks, diff review, reviewer specialists, and gate state creation before gh pr create.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Self-review checklist before creating a PR — repo checks, diff review, reviewer specialists, and gate state creation before gh pr create.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Make Codex CLI behave more like Claude Code for Kookr by preserving the known fork, branch, build, deploy, daily upstream sync, and live-verification workflow for Claude-compatible skills, agents, settings, hooks, and related UX.
Kookr-internal extension to rfc-iterative-review that captures append-only critic traces for later meta-analysis of RFC reviewer subagents.
Analyze an open question by casting 3-5 deliberately conflicting expert roles, running them in parallel and blind to each other, sharpening them in a debate round, attacking their consensus with a devil's advocate, then synthesizing a verdict that preserves disagreement instead of averaging it. Use for decisions, strategy, idea generation, hypothesis triage, experiment post-mortems, or any question where a single lens would return "it depends".
Iterative RFC drafting workflow — draft in worktree, run parallel critic subagents, incorporate feedback over N rounds, present to user before any action
How to create, structure, and launch Kookr playbook tasks — reusable agent task templates with dynamic sources and project identity
Build a Ralph-like sequential Kookr task chain where each task completes one independent unit, records durable state, and spawns the next task with the same continuation contract. Use for issue batches, queue drains, staged migrations, or other long runs that should proceed one task at a time without relying on conversation memory.
基于 SOC 职业分类
| name | pre-pr-review |
| description | Self-review checklist before creating a PR — repo checks, diff review, reviewer specialists, and gate state creation before gh pr create. |
| keywords | review, self-review, pre-PR, checklist, before PR, quality, validation, before merge, before submit |
| related | pre-push, pr-lifecycle, git-commit-discipline, testing-patterns, pr-review-triage |
Requires: the reviewer specialists at
plugin/reviewer-specialists/(bundled with thekookr-toolkitplugin since 0.5 — seedocs/hooks-setup.md). If the directory is missing (e.g. plugin not installed), stop and report the missing dependency rather than fabricating review output. The repo-level checks (build, tsc, tests) below still run.
Run this checklist before creating a pull request. When Kookr is installed via scripts/install-hooks.sh, the pr-workflow-gate hook enforces this skill before every gh pr create in any repo on your machine.
gh pr createDetect the project's build system from the working directory. Pick the first row whose condition is true:
| Detected | Build command | Test command |
|---|---|---|
pnpm (pnpm-lock.yaml) | prefer pnpm build; else pnpm tsc --noEmit if tsconfig.json present | pnpm test |
npm (package-lock.json) | prefer npm run build; else npx tsc --noEmit if tsconfig.json present | npm test |
yarn (yarn.lock) | prefer yarn build; else yarn tsc --noEmit if tsconfig.json present | yarn test |
Rust (Cargo.toml) | cargo check | cargo test |
Python (pyproject.toml) | run configured typecheck (mypy / pyright) if any | pytest or python -m pytest if configured |
| Other / unknown | skip step 2 | skip step 3 |
"prefer X; else Y" means: run X only if the corresponding package-manifest script exists (check package.json's scripts map); otherwise fall back to Y. If neither exists, skip the step and note "no build step detected" in the output contract.
Diff review (step 4) and commit hygiene (step 5) are always required regardless of project type.
Run the build command from the detected row. Must be clean. No any casts introduced unless justified. No @ts-ignore added. For Rust, no new #[allow(...)] without a comment explaining why.
If the project is "Other / unknown", skip this step and state so in the output contract.
Run the test command from the detected row. Must be green. New functionality should have tests. Modified behavior should have updated tests.
If the project has no test script / test suite, skip this step and state so in the output contract.
fix: PRs)If the PR is a bug fix, reproduce the buggy behavior and verify the fix eliminates it before proceeding. Unit tests passing is necessary but not sufficient — the old code path must be shown to fail and the new code path must be shown to work.
Concretely:
This step catches fixes that pass tests but don't actually address the reported issue (partial fixes, wrong assumptions about the failure mode, tests that don't exercise the real code path).
Skip this step only for trivial fixes where the unit test IS the reproduction (e.g., a typo in a string constant).
git diff --stat # overview of changed files
git diff --cached # staged changes in detail
git diff # unstaged changes
Check for:
.env, node_modules, lock files you didn't intend to changeconsole.log, debugger, TODO comments that should be resolvedBefore opening the PR, compare the actual diff to the assigned issue or task scope. This is an agent self-check: catch unrelated reversions/deletions here instead of making the operator discover them during review.
First, write down the expected scope from the issue body, task prompt, or implementation plan:
If the issue does not declare file scope, derive a conservative expected scope from the smallest implementation plan that satisfies the issue. Do not use "no scope was declared" as permission to keep unrelated edits.
Then inspect the changed paths and deletions against that scope:
BASE_REF=${BASE_REF:-origin/main} # set to the intended PR base when it is not origin/main
git diff --name-status "$BASE_REF"...HEAD
git diff --summary "$BASE_REF"...HEAD
git diff --diff-filter=D --name-only "$BASE_REF"...HEAD
Treat these as blocking scope findings until fixed or explicitly justified in the PR body:
If the scope check finds unrelated changes, remove them from the branch before creating the PR. If a suspicious deletion or reversion is intentional, document why it is required for this issue in the PR description.
Completing scope is not widening scope. A narrow Expected files ONLY allow-list plus a "stop rather than widen scope" instruction can collide with the mandatory gates below — most often the docs-drift check (§8), which blocks the PR on a system-model / requirement / ADR doc that your own diff just made stale. Updating a doc or artifact that your change falsified directly serves the PR goal (shipping the change without drift), so it is in scope even when the task's file list did not enumerate it. Make the edit, note it in the PR body ("updated <doc> — kept in sync with this change"), and continue — do not stop to ask permission for it. Only stop and report the blocker when the required file is explicitly forbidden (e.g. named in a do-not-touch list because another task owns it) or when the gate demands unrelated new work rather than syncing an artifact your diff already invalidated.
Scan changed lines only (not the full repo) for user-specific absolute paths in scripts, docs, skills, agent definitions, and PR-template content. Replace with portable equivalents where practical.
scripts/check-portability.sh # defaults to base ref origin/main
scripts/check-portability.sh main # explicit base ref
The helper exits 0 when clean and 1 when added lines contain matches. It looks at added diff lines so existing intentional references elsewhere in the repo do not produce noise.
Patterns flagged:
/home/<user>/... (Linux home directories)/Users/<user>/... (macOS home directories)C:\Users\<user>\... (Windows home directories)Replace with one of:
$HOME or ~scripts/<name>.sh, .claude/skills/<skill>/SKILL.md)CODEX_SRC, CODEX_INSTALL_DIR, ...)/path/to/repo, <USER>) in examplesAllowed without conversion:
$HOME, env vars, or placeholdersIf a flagged line is intentional, either append a portability-ok marker comment on the same line so the helper skips it, or justify it in the PR description.
For projects other than Kookr that do not ship the helper, do this step as a manual diff scan for the same patterns.
Code that works on Debian/Linux can silently break on macOS, which ships bash 3.2 (frozen at GPLv2) and BSD variants of sed/grep/stat/date/readlink. Most onboarding and runtime bugs on macOS trace to this.
Run the deterministic linter on changed lines:
scripts/check-shell-portability.sh # defaults to base ref origin/main
It flags the statically-detectable class. Use the catalog below for manual review of anything it can't see (heredocs, runtime behavior):
GNU-only coreutils flags → use the portable form:
grep -P / -oP → POSIX ERE (grep -E) or perl/awk (BSD grep has no PCRE)sed -i 's/…' → sed -i.bak 's/…' && rm file.bak (BSD sed -i requires an explicit suffix; the no-backup form is the two-arg sed -i '' …)sed -r → sed -Ereadlink -f → a portable realpath helper / python/perl (older macOS readlink lacks -f)stat -c → gate on uname (BSD stat uses -f)date -d → gate on uname (BSD date uses -v / -j -f)find -printf, xargs -r → GNU-only; restructurebash 4+ syntax (macOS system bash is 3.2):
mapfile / readarray → while read loop${var,,} / ${var^^} case conversion → trdeclare -A / local -A associative arrays → not availableecho -n / echo -e → printf (literal under POSIX /bin/sh)Runtime-only traps the linter CANNOT catch — review by hand, and rely on the macOS CI job to actually exercise them:
set -u + empty array: "${arr[@]}" raises "unbound variable" on bash 3.2 when arr is empty. Use "${arr[@]+"${arr[@]}"}".$(...): bash 3.2 cannot parse a heredoc nested in command substitution when the body contains a backtick. Move the program to a sibling file and invoke it by path./proc: resolve pids/process trees via ps, not /proc, when /proc is absent./var → /private/var symlink: mktemp paths differ from git rev-parse / realpath output; resolve both sides with realpath/pwd -P before comparing.spawn-helper); restore +x in a prepare step.sun_path ≤ 103 bytes: macOS os.tmpdir() is long; use a short /tmp base for socket paths.When in doubt, spawn the macos-compat-reviewer subagent on the diff, and label the PR macos so the macOS CI job runs.
feat:, fix:, chore:, docs:, test:)Ask: "Does every change in this diff directly serve the PR's stated goal?"
If you find unrelated improvements, either:
For non-trivial changes, quick-check:
Run specialized reviewer subagents in parallel against the diff. Skip for trivial changes (typo fixes, comment updates, single-line config changes).
Do not launch a fixed 5-agent fan-out on every PR. Select the panel from the content of the diff — never from the implementer's self-report that "there's nothing to review." Run the deterministic helper to get the selection and the gating rationale:
plugin/skills/pre-pr-review/select-specialists.sh # keys off origin/main..HEAD
plugin/skills/pre-pr-review/select-specialists.sh --base main
plugin/skills/pre-pr-review/select-specialists.sh --force # escape hatch: full 5-panel
It prints a stable, machine-readable block you record verbatim in the aggregate review result (see the Output Contract):
SPECIALISTS=correctness,lint-like,test
DOCS_DRIFT=active|inactive
FORCE=yes|no
COUNT=<n>
rationale:
<one line per selected specialist>
The gating rules the helper encodes:
| Specialist | When it runs |
|---|---|
| correctness | always — unconditional, independent cross-check. Never gated. |
| test | when the diff changes non-test logic (a non-test, non-doc source file). |
| lint-like (consolidated) | on any change. Folds conventions + deadcode (+ docs-drift when active) into one reviewer with per-concern verdicts. |
The docs-drift concern inside lint-like activates (DOCS_DRIFT=active) when
the diff touches docs (*.md, docs/**) or public API (added exports) — the
signal comes from diff content, not from whether the author remembered to touch
docs. When dormant it stays a light check; when active it is expected to emit
concrete doc edits.
This makes review proportional: a small single-file source PR launches ≤3
specialists (correctness, lint-like, test); a docs- or test-only PR launches
2. It does not collapse cross-checking — correctness remains standalone and
independent, and the consolidated reviewer keeps each merged concern as a distinct
checklist item with its own verdict (one agent, but not one blurred opinion).
Force-full-panel escape hatch. For large, risky, or security-touching diffs,
pass --force (or FORCE_FULL_PANEL=1). This restores the full, un-consolidated
5-panel — conventions, correctness, deadcode, test, docs-drift as five
independent specialists — trading tokens for maximum redundant cross-checking.
Composing the consolidated lint-like reviewer. There is no separate
lint-like file. Spawn one agent whose prompt concatenates the bodies of
conventions-specialist.md + deadcode-specialist.md (and, when
DOCS_DRIFT=active, docs-drift-specialist.md), prefixed with an instruction to
return a separate, labelled verdict per concern (conventions:, deadcode:,
docs-drift:) rather than one merged opinion. correctness and test stay
standalone specialists spawned from their own files.
The reviewer fan-out is a structured workflow, not a series of ad-hoc user
prompts. The model (specialist role, diff scope, repo/worktree, status, result
summary) lives in src/shared/contracts/reviewer-fanout.ts; the orchestration
helpers (createReviewerFanoutWorkflow, buildSpecialistLaunch,
aggregateReviewerRuns) live in src/core/reviewer-fanout.ts.
When you launch a specialist, prefix its prompt with the machine-event marker header so the session analyzer classifies the launch as workflow traffic instead of an organic repeated user instruction:
[[kookr-workflow:reviewer-fanout]] role=<role> workflow=reviewer-fanout:<branch>
Repo: {repoDir}
Diff scope: <base>..<head> (<n> changed file(s))
<verbatim specialist instructions, with {repoDir} inlined>
The specialist instruction bodies (plugin/reviewer-specialists/*.md) are
unchanged — only the transport/metadata shape around them is new. Legacy
launches without the marker still work (the analyzer keeps a compatibility shim
that recognizes the You are the <role>-specialist reviewer… opening).
When the panel finishes, roll the per-specialist runs into one parent-visible
aggregate (aggregateReviewerRuns) — per-specialist statuses and findings in a
single review result — rather than reporting N loose reviewer messages.
Two reviewer layers are available:
plugin/reviewer-specialists/)Narrow prompt templates for PR-level review. Use for all non-trivial PRs:
The docs-drift-specialist runs on every non-trivial PR — not only doc-touching ones — because the drift it catches is exactly the doc the author forgot to update. It is especially load-bearing when the repo carries a requirements spec (docs/requirements.md) or MBSE/system-model docs (docs/system-models/**, docs/adr/**), where a silent code change can falsify a SHALL requirement's Evidence line or a documented state machine.
Each specialist expects:
{repoDir} — path to the full repo checkout (the worktree)git diff main..HEAD output and list of changed filesInvocation note: Agent names below appear unqualified for readability. When calling
Agent({ subagent_type: "..." }), prependkookr-toolkit:(e.g.,kookr-toolkit:dependency-graph-analyzer). Unqualifiedsubagent_typedoes not resolve for plugin-namespaced agents.
Use when the change touches module boundaries, imports, or public APIs:
Selection guide:
| Change type | Which reviewers |
|---|---|
| Any non-trivial code change | run select-specialists.sh — correctness + lint-like (consolidated conventions/deadcode/docs-drift) + test when non-test logic changed (Layer 1) |
| Large / risky / security-touching diff | select-specialists.sh --force — restores the full un-consolidated 5-panel |
| Change touches behavior cited by a requirement, ADR, or system-model doc | docs-drift concern is mandatory (blocking findings expected); force the full panel if in doubt |
UI-component change (.tsx / .jsx / .vue / .svelte touching aria-*, role=, semantic HTML, or spreading props onto HTML elements) | + a11y-specialist |
| Module boundary / import refactor | + dependency-graph-analyzer, module-interface-auditor |
| New public API / API changes | + api-surface-auditor, module-interface-auditor |
| State / workflow logic | + state-machine-verifier, failure-mode-analyst |
How to run:
Prepare context: git diff main..HEAD and git diff main..HEAD --stat, then run select-specialists.sh (see Diff-adaptive panel selection above) to decide the panel and capture the gating rationale.
Launch the selected agents in parallel as subagents, passing the diff and repo path. Compose the consolidated lint-like reviewer as described above; spawn correctness (and test when selected) standalone.
For the test-focused reviewer, explicitly ask whether the changed runtime path is tested directly or only inferred through helper/unit coverage.
Claude Code: spawn each Layer-1 specialist via the Agent tool, reading the specialist's .md file from plugin/reviewer-specialists/ as the prompt body, prefixed with the machine-event marker header (see Structured fan-out above):
Agent({ subagent_type: "general-purpose", prompt: "[[kookr-workflow:reviewer-fanout]] role=correctness workflow=reviewer-fanout:<branch>\n<contents of plugin/reviewer-specialists/correctness-specialist.md, with {repoDir} and the diff inlined>" })
For Layer-2 architecture agents use Agent({ subagent_type: "kookr-toolkit:<name>" }).
Codex CLI: spawn each Layer-1 specialist via the spawn_agent tool with the specialist's .md content as the task instructions. This is an authorized skill-workflow spawn under clause (b) of the spawn_agent tool description — you do NOT need the user to re-confirm. Pattern:
spawn_agent({
task_name: "review_correctness",
instructions: "[[kookr-workflow:reviewer-fanout]] role=correctness workflow=reviewer-fanout:<branch>\n<contents of plugin/reviewer-specialists/correctness-specialist.md, with {repoDir} and the diff inlined>"
})
Then wait_agent on all spawned ids. Do NOT fall back to forging a .review-state/<branch>.json marker via shell — that bypasses the review the gate exists to enforce. Layer-2 architecture agents on Codex follow the same spawn_agent pattern, naming the role in task_name.
Collect findings — fix any blocking issues before proceeding.
Note informational findings in the PR description if relevant.
If this PR targets an external upstream (i.e. gh pr create -R <other>/<repo>
against a fork you own), verify the --base argument matches the recon's
declared policy. This catches cases where upstream added a guard workflow that
rejects fork→main PRs (real incident: berriai/litellm added Guard main branch
mid-April 2026).
# Variables you already know at this point:
# REPO = {owner}/{repo} (the upstream, not the fork)
# BASE = the --base argument you will pass to gh pr create
#
SLUG=$(echo "${REPO}" | tr '/' '-' | tr '.' '-')
RECON="${HOME}/.claude/${SLUG}-recon/recon-report.md"
if [ -f "${RECON}" ]; then
# Strict grammar: "- external_pr_base: <branch>" (one space after dash, one after colon)
RULE=$(grep -oP '^- external_pr_base: \K\S+' "${RECON}" || true)
if [ -n "${RULE}" ] && [ "${RULE}" != "${BASE}" ]; then
echo "ERROR: recon says external_pr_base=${RULE} but PR targets ${BASE}."
echo "Either retarget (git rebase --onto upstream/${RULE} upstream/${BASE} \${BRANCH}"
echo "+ gh api repos/${REPO}/pulls/N -X PATCH -f base=${RULE}) or update the recon"
echo "if the upstream policy has genuinely changed."
exit 1
fi
fi
If the recon has no ## Policies section or no external_pr_base: line, skip
silently — absence means "no declared policy for this repo." Do not invent a rule.
For non-OSS PRs (internal to Kookr or any repo where -R is not used), skip
this step entirely — the check only applies when pushing from a fork to an
upstream that declares a base-branch rule.
After ALL mandatory checks above pass, create the state file that allows gh pr create through the hook gate. The key must match the hook's derivation, which prefers -R owner/repo / --head parsed from the command and only falls back to the cwd when a flag is absent — so deriving REPO_NAME from the git remote URL matches both paths:
Important: the hook parses the raw shell command text before the shell expands variables. If the eventual gh pr create command uses --head "$BRANCH" or --head "$SYNC_BRANCH", the hook may look for a gate marker keyed to the literal variable token rather than the branch value. Prefer writing the final gh pr create command with a literal --head branch-name. If the PR head is not the currently checked-out branch, set PR_HEAD_BRANCH below to that exact head branch; the snippet converts / to -.
# Match pr-workflow-gate.sh's `-R owner/repo` parsing by deriving REPO_NAME
# from the remote URL, not the worktree's directory basename. Worktrees
# typically have dir names like `kookr-feature-x` while the remote and the
# hook agree on `kookr`; using the basename there produces a key the hook
# never looks up. See issue #406.
REMOTE_URL=$(git config --get remote.origin.url 2>/dev/null || true)
REPO_NAME=$(basename -s .git "${REMOTE_URL:-$(git rev-parse --show-toplevel)}")
SAFE_BRANCH=${PR_HEAD_BRANCH:-$(git rev-parse --abbrev-ref HEAD)}
SAFE_BRANCH=$(printf '%s' "$SAFE_BRANCH" | tr '/' '-')
touch "/dev/shm/.pr-gate-${REPO_NAME}-${SAFE_BRANCH}-pre-done"
Do NOT create the state file if any mandatory check failed. Fix the issue first, re-run the checks, then create it.
The state file is the contract between this skill and the pr-workflow-gate hook. The hook checks for its existence before allowing gh pr create.
Before you conclude this skill, report the checklist result explicitly:
pnpm|npm|yarn|rust|python|otherSPECIALISTS=… selection, whether --force was used, and the per-specialist gating rationale: block from select-specialists.shactive or dormant per the gating decisionDo not say the branch is PR-ready without stating whether the reviewer specialists were run and whether the gate file was actually created.
For small/obvious changes, the minimum is: run the detected build + test commands, then git diff --stat, then create the state file (see above).