AI Code Reviewer
SkillAI & modelsGraph-evidence AI code review (codexqa-code-reviewer) for ANY language repo using ONLY the CodexQA CLI symbol graph (call chains, classes, methods, configs, blast radius, test edges), then an order-16 Agent LLM judgment pass by the host agent's embedded model with deterministic dedupe/merge against heuristic findings. Use when the user asks for codexqa-code-reviewer (former name ai-code-reviewer), code review, PR review, 代码评审, impact analysis, 影响面, regression scope, test gaps, full-repo health review, 全仓评审, CodexQA evidence-pack, 证据包, graph-backed review, or LLM semantic CR on a pack. Requires codexqa CLI for every language; never embeds CodexQA source; never substitutes git-diff-only analysis. Not SAST+agent scan reports (that is codexqa-defect-analyzer), not structure/impact mapping alone (that is codexqa-code-analyzer), and not exception RCA (that is codexqa-rootcause-analyzer).
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the AI Code Reviewer skill
What this skill tells your AI
The instructions your AI receives, as published by openqa-cn/codexqa in skills/codexqa-code-reviewer/SKILL.md and read by ahel’s review.
Graph-first code review via CodexQA CLI only. Collect a JSON evidence pack, then reason from those artifacts. Applies to every language / polyglot monorepo.
CLI: {baseDir}/scripts/collect-pr-evidence.sh (and full-repo / adhoc variants).
Runtime pack: <repo>/.codexqa-review/<run-id>/ → review-conclusion.json +
REVIEW-REPORT.html.
Install: Prefer npx skills add openqa-cn/codexqa --skill codexqa-code-reviewer.
Do not copy into a skills library manually until the user names the install target.
README.md / README.zh-CN.md / HOW_IT_WORKS.md / KNOWN_LIMITATIONS.md
(and their .zh-CN twins) are human-facing. Do not load them at runtime.
Boundaries
| Need | Skill |
|---|---|
Graph-evidence pack → bilingual HTML CR (REVIEW-REPORT.html) | this skill (codexqa-code-reviewer) |
SAST + Agent LLM Detection → report_scan.* | codexqa-defect-analyzer |
| Symbol-graph change impact, callers, test gaps | codexqa-code-analyzer |
| Exception RCA from stacks/logs on top of CLI analysis | codexqa-rootcause-analyzer |
Prerequisites
| Dependency | Why |
|---|---|
codexqa (Node ≥ 18) | Sole primary analysis backend. Resolve it with the Preflight gate below, not with command -v on the default PATH. npm i -g @openqa-cn/codexqa only when that probe prints nothing. |
jq | Evidence JSON / HTML render |
bash 3.2+ | Collect / validate / render scripts (macOS OK) |
Python 3.10+ (scripts/acr-python) | Local derive-* helpers / validate-skill gate |
| Semgrep, Bandit, gosec, gitleaks, osv-scanner, ruff, eslint | Deterministic SAST. If any binary is missing, install it before collect (scripts/lib/install-sast-tools.sh) |
Local skill gate (Eval substitute when skill-up is missing):
./scripts/validate-skill.sh
Preflight gate
Run this once in the current shell before any later step. Collect, SAST install, index, validate, review, merge, and render stay blocked until codexqa_cli_path has actually executed here and its stdout is known. Intending to preflight later does not open those steps. A second probe is needed only after an install that can change PATH.
A bare command -v codexqa or which codexqa is not this gate. The CLI is an npm global. Its bin directory comes from prefix in ~/.npmrc, $npm_config_prefix, and npm prefix -g, and that directory is often missing from the default PATH. A failed lookup means this shell has not been probed, not that the CLI is absent. Installing from that failure reinstalls a CLI that is already there.
source scripts/lib/codexqa-preflight.sh
codexqa_cli_path
command -v jq >/dev/null
codexqa --version
Keep source and codexqa_cli_path in this shell. $(codexqa_cli_path) drops the PATH update. Do not hardcode the prefix.
- Printed path: the CLI is installed. Do not run
npm i -g. Record the path and the version. If the user asked for the latest release, compare that version withnpm view @openqa-cn/codexqa versiononly after this probe, and upgrade only when they differ. Source the preflight again after an upgrade. - Empty stdout: the CLI is absent. Only then
npm i -g @openqa-cn/codexqa(Node ≥ 18). Source the preflight again. Ifcodexqa_cli_pathis still empty, stop withmissing_gate: missing_codexqa_engine. jqmissing stops the same way. Do not switch the engine to grep or a language-native SAST.
Quick start
Task progress:
- [ ] 1. Preflight gate in this shell (source + codexqa_cli_path). Steps 1b–6 stay blocked until this has run once.
- [ ] 1a+1b. After the path is printed, run resolve-pr-checkout.sh and install-sast-tools.sh in parallel. Do not git fetch or git clone before resolve returns. SAST install stays mandatory.
- [ ] 2. Collect. Stdout is the primary language, the pack path, and validate-evidence. Traces stay in commands.log. status=ok ends the incremental-index question; do not open the collector or commands.log.
- [ ] 3. From 31-model-brief.json, jq only still_open, open_suspects, test_oracle_open, chain_dimensions, and output. Do not print methods. still_open is one row per shape. chain_dimensions.chains is every CodexQA call chain; judge each chain once against its rules. Do not restate look_for, do_not_report, or closed_lines. Do not open rule-construction or review-conclusion.json.
- [ ] 4. If those three lists are empty, render immediately. Do not read methods, judgment-work, 29, or the repository. If any list is non-empty, judge each shape once and read only its hosts. A different shape is a separate finding. Do not compare it with a closed line to decide they are one defect.
- [ ] 5. Render writes the sealed review-conclusion.json. Summarize from that file. Do not reclassify a sealed card by opening the repository.
# Step 1 — required before every command below. See Preflight gate.
source scripts/lib/codexqa-preflight.sh
codexqa_cli_path
# Steps 1a and 1b run in parallel after codexqa_cli_path has printed a path.
# 1a fetches at most one URL. An empty HTTP reply gets one HTTP/1.1 downgrade, then stop.
./scripts/resolve-pr-checkout.sh --pr <url-or-owner/repo#N> --search-root <workspace>
# status=local means do not fetch. Use the printed repo and diff_base.
./scripts/lib/install-sast-tools.sh
# PR / diff (default). derive-sast.sh runs the installer again before the scan.
./scripts/collect-pr-evidence.sh --repo /path/to/repo --diff-base origin/main
# Full-repo (optional)
./scripts/collect-fullrepo-evidence.sh --repo /path/to/repo
# Adhoc / single-file (no PR diff-base)
./scripts/collect-adhoc-evidence.sh --file /path/to/Foo.java
# After review reasoning:
./scripts/render-review-html.sh --dir <OUT_DIR>
Default OUT_DIR: <repo>/.codexqa-review/<run-id>/ with runtime manifest.json
(+ 09-language-profile.json). templates/evidence-manifest.json is schema-only —
never written by collectors.
Hard constraints (CodexQA mandate)
- CLI only — call
codexqaafter the Preflight gate, which builds PATH. Never vendor / unzip / import@openqa-cn/codexqa. A default-PATH miss is not a missing CLI. - Evidence files first — the CodexQA evidence pack is a hard prerequisite (前置必要条件). Impact, callers, entries, coverage must cite artifact fields.
- No invented graph — missing facts →
confidence: UNKNOWN. Never fake green fromtest/paths. - PR gates — need code identity (
REPO) + reviewable change (--diff-base). Elsestatus: blocked. - Human merge decision — actionable review only; never auto-approve.
- No alternate primary backend — forbid git-diff-only, grep-only “call graph”, or language SAST
(SpotBugs/ESLint/mypy/…) as the sole engine.
derive-sast.shmay run Semgrep, Bandit, gosec, gitleaks, osv-scanner, ruff, and eslint as a secondary deterministic pass (23-sast-signals.json). Missing/invalid pack →missing_gate: missing_codexqa_engine(or specific gate). - Primary language gate — read
09-language-profile.json/manifest.primary_language/review_language_focusbefore findings; apply references/review-dimensions.md (references/language-profile.md). Override with--primary-langonly when detection is wrong. Label uncertainty for reflection / dynamic dispatch / cross-language FFI — do not leave CodexQA.
Modes
| Mode | When | Script |
|---|---|---|
| PR/diff (default) | Branch/PR vs base | scripts/collect-pr-evidence.sh |
| Full-repo (optional) | Health / architecture / hotspots | scripts/collect-fullrepo-evidence.sh |
| Adhoc (single-file) | Upload one/few files without PR | scripts/collect-adhoc-evidence.sh |
Full-repo deliverables: hotspot modules (ranked by edges-in, not from_count), layering drift (入口 → 应用 → 领域 → 存储),
entry concentration, hardening backlog P0/P1/P2. Never invent PR change_status.
No product scorecard / 产品评测打分.
Adhoc: bootstraps a mini git repo when --repo is omitted so CodexQA index gates pass. An empty root commit is the diff base, then build-review-digest.py writes 26–31 while the source is still on disk. Validate with --mode adhoc. Judgment reads 31-model-brief.json once. 29 stays for seal.
Capability → pack map
| Capability | Pack evidence |
|---|---|
| Change localization | 03-change-groups / 05-changed-symbols / diffs/*.diff.json |
| PR review digest | 26-review-digest.json (commits behind/ahead, three-dot file classes vs two-dot drift, deduped disposition: report lines). The judgment pass reads 31-model-brief.json instead. |
| Model brief | 31-model-brief.json (the only file the judgment pass opens: closed shapes, candidate hits, still-open shapes, oracle flags, and method source). Short callees used by a kept method are included. An empty open_suspects[].source with source_ref points at methods[].source for that method, or at judgment-work/ when source_ref.where is judgment-work. |
| Judgment packet | 29-judgment-packet.json stays for seal. A review of at most 2000 pending lines, including one file of about 800 lines, does not create judgment-work/. Scripted suspects are closed in judgment-seed.json before that pass. Question fan-out starts only when the source left for the model exceeds 2000 lines. That question fan-out writes at most four judgment-work/group-*.json files, each holding only methods that own a suspect, a business rule, or a lock-order pair. One-line getters stay out. magic_number and rate_literal are seeded as conventions in judgment-seed.json and are not re-judged. Report rows are closed by seal. Group findings are a union. Past that, whole methods pack into chunks of about 800 lines, at most four concurrent judgment-work/group-*.json files. A method is cut only when it is longer than the chunk. Each chunk carries field lines and the lock-order summary. Rule text stays once in shared.json. Extra agents on the same change are security, correctness, and quality passes over the full change, not line windows, and the packet does not emit them by default. A suspect with slice_ref points at the method slice and does not repeat the source. identical_to_base matches the base tip and is not a defect. A csv/markdown/txt keyword hit does not force T0. Confirmed magic numbers seal as conventions, not P1. 30-conclusion-skeleton.json is sealed into the conclusion at render. |
| Design fit | 10-design-fit-signals.json (path + package/import layers, import_cross_layer, dead_nested_symbols confirmed via empty edges-in; full: imports/ + on-disk fallback) |
| Complexity | 11-complexity-signals.json (method LOC / decisions / nesting / YAGNI hints) |
| Dependencies | 12-dependency-signals.json (manifest/lock SNAPSHOT, lock drift, license clues, local audit) |
| Privacy | 13-privacy-signals.json (PII fields, log exposure, retention gaps, consent/transfer clues) |
| Resilience | 14-resilience-signals.json (timeout, retry, swallow, partial fail, idempotency/compensation) — signal hits → findings hard gate |
| Change / rollout | 15-rollout-signals.json (migration, dual-write, flags, compat window, breaking announce, rollback) |
| Observability | 16-observability-signals.json (catch without log/metric/trace) |
| Contract | 17-contract-signals.json (breaking hints, XSS/HTML sinks, public-sig volume) |
| Maintainability | 18-maintainability-signals.json (TODO/FIXME, magic numbers, long files) |
| Performance | 21-performance-signals.json (hot path, N+1, unbounded allocation) |
| Agent LLM judgment | 22-llm-judgment.json (host-agent semantic CR + dedupe merge vs heuristic findings) |
| Deterministic SAST | 23-sast-signals.json (per hit disposition: report / drop / suspect; class policy allow / suppress_obvious / dedupe_loci) |
| Annotation callbacks | 19-annotation-edges.json (Spring/Resilience4j synthetic callers when edges-in empty) |
| Risk tier (blast-radius triage) | 20-risk-tier.json (T0–T3 from paths + tags + sensitive + rollout surfaces; auth/pay/migration/IaC → T0) |
| Blast radius | impact/*/edges-in.json / reach-in.json (PR + full-repo top hotspots) |
| Entry / flow | 07-tags.json + impact/*/paths/ |
| Test gaps | tested_count + tests-reach.json (not test directory / test path names) |
| Sensitive paths | 06-sensitive-hits.json + callers |
| Hot-but-thin | 08-hot-but-thin.json |
| Full-repo architecture | stats / summary / imports/ + Design fit signals |
| Primary language | 09-language-profile.json + manifest stamps |
Workflow detail
1. Preflight
This step is the Preflight gate. In a shell where codexqa_cli_path has not yet been executed, stop. Do not start 1b, collect, index, or review from a command -v miss.
source scripts/lib/codexqa-preflight.sh
codexqa_cli_path
command -v jq >/dev/null
codexqa --version
SAST install is step 1b and starts only after that probe has printed a path (or an install from empty stdout has been probed again):
./scripts/lib/install-sast-tools.sh
Per-tool commands (the installer runs these only when that binary is missing):
| Tool | Install command |
|---|---|
| semgrep | python3 -m pip install --user --break-system-packages 'semgrep>=1.80' |
| bandit | python3 -m pip install --user --break-system-packages bandit |
| ruff | python3 -m pip install --user --break-system-packages ruff |
| eslint | npm install -g eslint |
| gitleaks | go install github.com/gitleaks/gitleaks/v8@latest |
| gosec | go install github.com/securego/gosec/v2/cmd/gosec@latest |
| osv-scanner | go install github.com/google/osv-scanner/cmd/osv-scanner@latest |
codexqa-preflight.sh, install-sast-tools.sh, and derive-sast.sh all source scripts/lib/sast-tool-path.sh and call sast_refresh_path. That is the only PATH policy. It prepends a directory when the directory contains the CodexQA CLI, a SAST binary, or the runtime that installs it. Do not hardcode install prefixes. codexqa_cli_path prints the resolved CLI. npm bins (codexqa, eslint) come from the prefix in ~/.npmrc, $npm_config_prefix, and npm prefix -g (<prefix>/bin on Unix; the prefix directory itself on Windows, where the file is eslint.cmd). pip bins (semgrep, bandit, ruff) come from each Python's sysconfig scripts path (bin on Unix, Scripts on Windows). Go bins (gitleaks, gosec, osv-scanner) come from $GOBIN, $GOPATH, and go env (Windows lists split on ;); if go is not on PATH it is found with brew --prefix, asdf where, or a depth-capped search for go or go.exe, then go env supplies the bin dir. Lookup also accepts .exe, .cmd, and .bat. Node shims come from $NVM_DIR, $VOLTA_HOME, $FNM_DIR, and $ASDF_DATA_DIR. A gitleaks / gosec / osv-scanner file that fails --version is moved aside so a truncated download is not treated as installed. The GitHub release download runs only when no go binary runs, or go install still leaves that tool missing.
Do not set CODEXQA_SAST_SKIP_INSTALL=1 on a real review.
PR: REPO + DIFF_BASE. Full-repo: REPO only. Prefer absolute repo paths.
2. Collect
Blocked until the Preflight gate has run once in this shell. Collectors append command traces to commands.log and print the primary language, the pack path, and validate-evidence status. Shared helpers: scripts/lib/codexqa-preflight.sh.
Options: --full, --github-pr owner/repo#N, --primary-lang <Lang>, --skip-index, --skip-validate, --out DIR.
A changed --diff-base misses the index cache and forces --full. An incremental index that parses 0 files while the three-dot diff or the GitHub PR file list is non-empty is re-run with --full.
PR collect writes 26-review-digest.json after the signal files. Judgment reads that digest for commits behind/ahead, file-class counts, report rows, and dimension cards. Full path lists stay in 26-review-digest-detail.json. Do not recompute the split with git or open every signal file for the dimension verdict. Residual reading opens each 24-coverage-ledger.json read_groups entry once and still writes one closure row per pending symbol. Non-source files are not residual symbols. Byte-identical copies are scanned once; findings keep every path. Files whose bytes differ are both scanned.
3. Validate
./scripts/validate-evidence.sh --dir <OUT_DIR> --mode pr # or --mode full
Fails: missing CodexQA provenance; empty change-groups; all change_status=default;
lang_stats present but primary_language null. Legacy packs may WARN and still pass.
stubs≥20 (numeric or {total:N}) → cap edge/reach findings at UNKNOWN; do not treat from_count as real fan-in — prefer edges-in callers.
4. Review from artifacts
- When
31-model-brief.jsonis absent, read prompts/pr-diff-review.md or prompts/full-repo-review.md. When it exists, do not open those prompts. - Confirm
manifest.engineiscodexqa(or legacy codexqa in commands). Else blocked. - Lock language from
manifest.json+09-language-profile.json. - For top risks:
diffs/,impact/<id>/,paths/, then tags / hot-but-thin / sensitive. - Mermaid from references/mermaid-evidence.md.
- Detection rules live on the owner dimension card (index:
references/dimension-registry.md).
Build and extend them only with
references/rule-construction.md:
a rule is a relation plus role-shaped variants, one hit does not close
the family, and a hard-gate row is a visible finding. Pattern-class
defects with
disposition: reportare filed from23-sast-signals.json.dropis discarded. Onlysuspects[]go to the SAST suspect channel.allowrecords a scanner gap and does not rescan that class. CodexQA stays the primary engine. - Agent LLM judgment (order 16): when
31-model-brief.jsonexists, jqstill_open,open_suspects,test_oracle_open, andoutputonly. Findings injudgment.jsonare already copied fromcandidate_hits. Do not rewritetitle,risk,fix,line, orseverity. If those three lists are empty, do not add a finding, do not read methods orjudgment-work, and render. If a list is non-empty, judge each shape once and read only its hosts. Do not restatelook_for,do_not_report, orclosed_lines. Do not open rule-construction orreview-conclusion.json. A different shape is a separate finding. Copypresetunderoracle, judge onlyquestions, and leave a preset key unchanged.boundary_missedstays false unlesspresetis true.title,risk, andfixare Chinese; leave the English fields empty. An id already insuspect_hitsis closed. A failed render names the sentence to edit. Do not grep seal or validate scripts for fields. Do not walk24, regroup closed rows, or open templates, examples, dimension docs, orseal-conclusion.py. When31is absent, follow prompts/llm-judgment-pass.md. On that legacy path, SAST suspects, business logic, and semantic candidates are separate prompts. The residual read visits everypendingsymbol in24-coverage-ledger.json; scanner hits do not dequeue it. The host embedded model reviews those packets, thenscripts/lib/merge-llm-findings.pydedupesp0/p1/p2against heuristic findings (22-llm-judgment.json).
5. Deliver
- Optional chat notes: templates/review-report.md
- Required:
review-conclusion.jsonis already in the pack. Do not replace it. - Required:
./scripts/render-review-html.sh --dir <OUT_DIR>→REVIEW-REPORT.htmlandreview-comments.json(same defect id, no score). Render runsscripts/lib/seal-conclusion.pybeforescripts/lib/validate-conclusion.py. The seal fills report-row cards,span_hash,test_gaps, rule shapes, default oracle skips, and unconfirmed per-line skips from the pack. The model writesjudgment.jsononly. The gate still refuses HTML when any check fails after that fill:- Every behavioral
disposition: reportrow is the primarylineof a finding, or analso_linesentry withsame_fix: true. A line number written only in prose does not close the row. One finding cannot close tworule_id,pattern_class, orkindvalues. Magic numbers, long files, and stale imports close inconventions, not in P0/P1/P2. A sentence that says another card covers a defect must name a line that a finding lists. - Each
test_oracle_inventoryrow hasoracle.unsafe_pass,oracle.boundary_missed, andoracle.branch_uncovered, plus that row's extra questions. Skip is legal only when every flag is false. - Production symbols with
tested_count == 0are intest_gaps.symbolsorwaived_symbolsinsidereview-conclusion.json. The HTML report does not render that table. A static initializer, a type or constructor, a get/set/is accessor, or a private helper goes towaived_symbols. A rule whose look-for has several shapes lists every shape; the first hit does not close the rest.
- Every behavioral
Cover: 页头四块(能否合入、最高严重级别、行为缺陷数与证据行数、必测三条路径)、一张卡一个失败场景、规范项(不计缺陷)、回归必测清单、敏感路径。有问题的维度和调用链默认折叠,排在发现项之后。ok/none 维度不进报告。seal-conclusion.py 在渲染前用信号文件补上结论里空着的维度(风险分档、架构契合、复杂度、依赖、韧性、隐私、变更发布、性能、模型语义评审),所以判定稿不写维度长文时,HTML 仍会展示有信号的维度。
不渲染: 测试缺口表、建议修复顺序、残留风险与假设、独立影响面示意。test_gaps 仍写入 review-conclusion.json 供闭合校验,不进 HTML。
render-review-html.sh 会过滤干净维度;仍须在 review-conclusion.json 写全评估结果与
dimensions_covered。Final findings must already be dedupe-merged (no duplicate
heuristic + LLM cards for the same defect).
High-severity findings cite: symbol id/file/lines, callers or entry path,
tested_count / tests-reach, confidence (high|medium|low|UNKNOWN).
Human-facing prose (dimension hotspots, finding risk/evidence, summary) must
explain risks in plain language — see references/review-dimensions.md
Reader prose.
Card voice (scanner cards in scripts/lib/seal-conclusion.py, model cards in
prompts/llm-judgment-pass.md):
title: SARIFshortDescription,规则名,例如SQL 注入。不写规则编号,不写这一行不是…。risk: Semgrep / SARIFmessage。在第 N 行检测到 \代码`。接一条影响。不写不会/不是/而不是`。fix: SARIFfix/ Sonar recommendation。将第 N 行 \代码` 改为:接安全写法。不写不用/不要/而不是`。call_chain:谁会走到这一行. No recorded caller stays未记录调用方, not没有入边and not a dead function. Do not add that the index is thin, that stubs capped confidence at UNKNOWN, or that real edges may be missing.- Stub counts and the UNKNOWN confidence cap stay in the validate log and
manifest.index_quality. Do not put them inREVIEW-REPORT.htmlor in the user-facing summary. - PR cards only. A file outside the three-dot diff is a branch-drift skip, not
既有代码.
Bilingual HTML: Write primary prose in Chinese (summary, intent, scope,
dimension risk/yagni/evidence, finding title/risk/evidence/fix,
call_chain.title, regression/test-gap notes, sensitive). On judgment.json
findings, leave title_en, risk_en, and fix_en empty. seal-conclusion.py
copies the Chinese text into those English fields at render. The HTML
toolbar switches data-zh/data-en; a hand-written English card is not required
for delivery.
Blocked result
### Code Review Blocked
- status: blocked
- missing_gate: missing_codexqa_engine | missing_code_identity | missing_reviewable_change | ...
- supplied: ...
- required: ...
- next_commands: ...
Do not emit P0/P1/P2 or merge advice when blocked.
Examples
End-to-end walkthrough: examples/pr-review-walkthrough.md
Progressive disclosure
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 60
- Forks
- 2
- Last commit
- Sep 2026
ahel review
K1binfo
installs-packagesK6low
bundled executables the agent is told to run
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Item type
- skill
- Key
codexqa-code-reviewer- Source
- github.com/openqa-cn/codexqa