| layout | default | |||||
|---|---|---|---|---|---|---|
| title | Code review module | |||||
| nav_order | 10 | |||||
| permalink | /modules/code-review/ | |||||
| keywords |
|
|||||
| audience |
|
|||||
| expertise_level |
|
specfact code review run executes the governed review pipeline end to end.
The bundle run guide at Code review run lists the same public flags in a compact table, documents invalid flag combinations, and matches the Typer surface. This section keeps module-level detail (tooling behavior, exit codes, examples).
specfact code review run [OPTIONS] [FILES...]Options (aligned with specfact code review run --help):
--scope changed|full: choose changed-only or full-repository auto-discovery when positional files are omitted--path PATH: repeatable repo-relative subtree filter for auto-discovered review targets--include-tests/--exclude-tests: include or exclude changed test files when review scope is auto-detected fromgit diff--focus: repeatable facet filter applied after scope resolution; values aresource(non-test, non-docs Python),tests(paths with atests/segment), anddocs(Python under adocs/directory segment). Multiple--focusvalues union their file sets, then intersect with the resolved scope. When any--focusis set,--include-testsand--exclude-testsare rejected (use focus alone to express test intent)--mode shadow|enforce: enforce (default) keeps today’s non-zero process exit when the governed report says the run failed; shadow still runs the full toolchain and preservesoverall_verdictin JSON, but forcesci_exit_codeand the process exit code to0so CI or hooks can log signal without blocking--level error|warning: filter findings before scoring so JSON, tables, score, verdict, andci_exit_codematch the filtered list:errorkeeps errors only (warnings and info dropped);warningkeeps errors and warnings (info dropped). Omit to keep all severities--bug-hunt: enable the bug-hunt pass (CrossHair uses longer budgets: per-path timeout 10s, subprocess budget 120s; other tools keep normal speed)--include-noise/--suppress-noise: include or suppress known low-signal findings such as test-scope contract noise--json: emit aReviewReportJSON file (defaults toreview-report.jsonin the working directory when--outis omitted)--out PATH: JSON output path (only valid together with--json)--score-only: print only the integerreward_delta--no-tests: skip the targeted TDD gate--fix: apply Ruff autofixes and re-run the review before printing results--interactive: ask whether changed test files should be included before auto-detected review runs
The command rejects incompatible mixes (same rules as the bundle run guide): Typer raises BadParameter while validating review flags (--include-tests with --exclude-tests, or --focus with --include-tests / --exclude-tests). Request validation and conflicting targeting styles rules in the run pipeline enforce the remaining pairings (output modes and positional files vs --scope / --path), still surfaced as typer.BadParameter.
- Positional
FILES...with--scopeor--path: choose explicit files or auto-scope controls, not both. --focuswith--include-testsor--exclude-tests: use--focusor the include/exclude flags, not both.--include-testswith--exclude-tests: pick at most one test inclusion mode.--outwithout--json:--outis accepted only when--jsonis also set.--jsonwith--score-only: pick one, not both (--jsoncannot be used with--score-only).
When FILES is omitted, the command falls back to:
git diff HEAD --name-only
git ls-files --others --exclude-standardOnly existing Python files from tracked or untracked workspace changes are
reviewed. Test files under tests/ are excluded by default for auto-detected
review scope unless you pass --include-tests or answer yes in
--interactive mode. Explicit --path tests/... filters count as intentional
targeting and keep matching tests in scope even with the default test
exclusion.
Use --scope full to review the governed repository file set instead of just
current changes:
specfact code review run --scope fullUse repeatable --path filters to limit either scope to one package or a
focused source-plus-test slice:
specfact code review run --scope full --path packages/specfact-code-review
specfact code review run --scope changed --path packages/specfact-code-review --path tests/unit/specfact_code_reviewCopy-pastable recipes for shadow mode, JSON --out, --focus
(source / tests / docs Python only), noise flags, and interactive
test prompts live in the Code review run bundle
guide (same Typer surface as this section).
Positional FILES... cannot be mixed with --scope or --path (see
Invalid combinations above).
With default noise suppression, the review also hides known low-signal test findings such as:
contract_runner MISSING_ICONTRACTon test functions- selected
basedpyrightimport/attribute findings in tests pylint W0212when tests intentionally exercise internal helperspylint R0801duplicate-code reports that are better treated as advisory
0:PASSorPASS_WITH_ADVISORY, or any outcome under--mode shadow(shadow forces success at the process level even whenoverall_verdictisFAIL)1:FAILunder default enforce semantics2: invalid CLI usage, such as a missing file path or incompatible options
Default output renders findings grouped by category, then prints verdict, CI exit code, score, reward delta, and the review summary.
During long-running reviews, the CLI now emits step progress to stderr so users
can see the current phase without breaking stdout-oriented contracts like the
final --json output path.
With --json, the ReviewReport envelope is written to review-report.json
in the working directory by default; use --out only together with
--json to pick another path.
specfact code review run --json --out /tmp/review-report.json packages/specfact-code-review/src/specfact_code_review/run/commands.py
specfact code review ledger update --from /tmp/review-report.jsonspecfact code review run --json packages/specfact-code-review/src/specfact_code_review/run/commands.py
specfact code review ledger update --from review-report.jsonThe second block writes JSON next to your cwd as review-report.json; pass
that same path into ledger update --from.
--score-only is intended for lightweight CI integration:
specfact code review run --score-only packages/specfact-code-review/src/specfact_code_review/run/commands.py--fix applies Ruff-backed autofixes before running the final review report:
specfact code review run --fix packages/specfact-code-review/src/specfact_code_review/run/commands.pyThe specfact-code-review bundle now includes internal runners that translate tool
output into governed ReviewFinding records.
specfact_code_review.tools.ruff_runner.run_ruff(files) executes:
ruff check --output-format json <files...>Rule prefixes are mapped as follows:
| Ruff rule prefix | ReviewFinding category |
|---|---|
S* |
security |
C9* |
clean_code |
E* |
style |
F* |
style |
I* |
style |
W* |
style |
Additional behavior:
- only the provided file list is considered
- unsupported Ruff rule families are skipped instead of being mislabeled
- a non-empty Ruff
fixpayload setsfixable=True - malformed output or a missing Ruff executable yields a single
tool_errorfinding
specfact_code_review.tools.radon_runner.run_radon(files) executes:
radon cc -j <files...>Cyclomatic complexity thresholds:
| Complexity | Severity | Category |
|---|---|---|
<= 12 |
no finding | n/a |
13..15 |
warning |
clean_code |
> 15 |
error |
clean_code |
Additional behavior:
- the default Hatch environment installs
radon, so the runner is usable in standard repo workflows - only the provided file list is considered
- malformed output or a missing Radon executable yields a single
tool_errorfinding
specfact_code_review.tools.basedpyright_runner.run_basedpyright(files) executes:
basedpyright --outputjson --project . <files...>Diagnostic mapping:
| basedpyright severity | ReviewFinding severity | ReviewFinding category |
|---|---|---|
error |
error |
type_safety |
warning |
warning |
type_safety |
information |
info |
type_safety |
Additional behavior:
- basedpyright runs with project context and then filters findings back to the provided changed-file list
- the runner preserves per-diagnostic line numbers and falls back to rule
basedpyrightwhen the payload omits a specific rule id - malformed output or a missing basedpyright executable yields a single
tool_errorfinding
specfact_code_review.tools.pylint_runner.run_pylint(files) executes:
pylint --output-format json <files...>Governance-oriented message mapping:
| Pylint message id | ReviewFinding category |
|---|---|
W0702 |
architecture |
W0703 |
architecture |
T201 |
architecture |
W1505 |
architecture |
| other ids | style |
Additional behavior:
- only the provided changed-file list is reported back, even when pylint emits findings for other files
- severity is derived from the message id prefix (
E*/F*->error,I*->info, otherwisewarning) - malformed output or a missing pylint executable yields a single
tool_errorfinding
specfact_code_review.tools.semgrep_runner.run_semgrep(files) executes:
semgrep --config packages/specfact-code-review/.semgrep/clean_code.yaml --json <files...>Custom rule mapping:
| Semgrep rule | ReviewFinding category |
|---|---|
banned-generic-public-names |
naming |
swallowed-exception-pattern |
clean_code |
get-modify-same-method |
clean_code |
unguarded-nested-access |
clean_code |
cross-layer-call |
architecture |
module-level-network |
architecture |
print-in-src |
architecture |
Representative anti-patterns covered by the ruleset:
- methods that both read state and mutate it
- public symbols that use banned generic names like
dataorprocess - swallowed exceptions that hide an underlying failure path
- direct nested attribute access like
obj.config.value - repository and HTTP client calls in the same function
- module-level network client instantiation
print(...)in source code
Additional behavior:
- only the provided file list is considered
- semgrep rule IDs emitted with path prefixes are normalized back to the governed rule IDs above
- malformed output, a missing
resultslist, or a missing Semgrep executable yields a singletool_errorfinding
After the clean-code Semgrep pass, the orchestrator runs
specfact_code_review.tools.semgrep_runner.run_semgrep_bugs(files), which uses
packages/specfact-code-review/.semgrep/bugs.yaml when present. Findings are
mapped to security or correctness. If the config file is missing, the pass
is skipped with no error.
specfact_code_review.tools.contract_runner.run_contract_check(files, *, bug_hunt=False) combines two
contract-oriented checks:
- an AST scan for public functions missing
@require/@ensure - a CrossHair fast pass
AST scan behavior:
- only public module-level and class-level functions are checked
- functions prefixed with
_are treated as private and skipped - the AST scan for
MISSING_ICONTRACTruns only when a batch-level package/repo scan root importsicontract(from icontract …orimport icontract). Reviewed files in a package that uses icontract are scanned even when the changed file itself does not import icontract - missing icontract decorators become
contractsfindings with ruleMISSING_ICONTRACTwhen the scan runs - unreadable or invalid Python files degrade to a single
tool_errorfinding instead of raising
CrossHair behavior:
crosshair check --per_path_timeout 2 <files...> # default
crosshair check --per_path_timeout 10 <files...> # with CLI --bug-hunt- CrossHair counterexamples map to
contractswarnings with toolcrosshair - timeouts are skipped so the AST scan can still complete
- missing CrossHair binaries degrade to a single
tool_errorfinding - with
--bug-hunt, the per-path timeout is 10 seconds and the subprocess budget is 120 seconds instead of 2 / 30
Operational note:
- the current development environment includes CrossHair, but installed-user environments still need that executable available for the fast pass to run
The specfact-code-review bundle now includes a reward ledger that persists
review outcomes and exposes a small CLI surface under specfact code review ledger.
Use the governed review report as the canonical input for ledger updates:
specfact code review run --json --out /tmp/review-report.json
specfact code review ledger update --from /tmp/review-report.json
specfact code review ledger status
specfact code review ledger reset --confirm- When
SUPABASE_URLandSUPABASE_KEYare present, the ledger writes review runs toai_sync.review_runsand appends ledger snapshots toai_sync.reward_ledger. - The reviewed DDL lives with the bundle at
packages/specfact-code-review/src/specfact_code_review/resources/supabase/review_ledger_ddl.sqlinstead of a repo-root infrastructure folder. - When Supabase is unavailable or not configured, the bundle falls back to a
local JSON ledger. In a repository checkout it prefers
.specfact/ledger.json; otherwise it falls back to~/.specfact/ledger.json. ledger statusprints coins, pass/block streaks, the last verdict, and the top three violation rules seen so far.ledger resetonly clears the local JSON fallback and requires--confirm.
If the CLI warns that bundled modules are missing or outdated while you are testing local bundle changes, refresh the project-scope modules first:
specfact module init --scope projectThen rerun the ledger command from the same repository checkout.
The specfact-code-review bundle can derive a compact house-rules skill from the
reward ledger and keep it small enough for AI session context injection. The
default charter now encodes the clean-code principles directly:
- Naming: use intention-revealing names instead of placeholders.
- KISS: keep functions small, shallow, and narrow in parameters.
- YAGNI: remove unused private helpers and speculative layers.
- DRY: extract repeated function shapes once duplication appears.
- SOLID: keep transport and persistence responsibilities separate.
- TDD + contracts: keep test-first and icontract discipline in the baseline skill.
specfact code review rules init
specfact code review rules show
specfact code review rules updaterules initcreatesskills/specfact-code-review/SKILL.mdin the current project from the bundled package template (or default content if bundled is unavailable), does not touchCLAUDE.md, and can optionally install one canonical IDE target via--ide <claude|codex|cursor|vibe>.- Cursor (special case):
.cursor/rules/house_rules.mdc— rules format for auto-attachment when--ide cursoris chosen. - SKILL.md compatible IDEs:
.claude/skills/specfact-code-review/SKILL.md,.codex/skills/specfact-code-review/SKILL.md,.vibe/skills/specfact-code-review/SKILL.mdwhen their matching--idevalue is chosen. rules showprints the currentSKILL.mdcontent and suggestsrules initwhen the file is missing.rules updatereads the last 20 ledger runs, surfaces rule IDs with at least three hits in the auto-managedTOP VIOLATIONSsection, prunes stale entries that have disappeared for 10 runs, and increments the skill version header.- The updater preserves the curated
DOandDON'Tsections, updates theUpdated:timestamp, and enforces a 35-line hard cap by removing the lowest-frequency violation entries first. rules updaterefreshes only canonical IDE targets already installed in the project, or a single explicit target when--ideis supplied.
specfact_code_review.run.runner.run_review( files, no_tests=False, include_noise=False, progress_callback=None, bug_hunt=False, review_level=None, review_mode="enforce", ) orchestrates the bundle runners in this order:
- Ruff
- Radon
- Semgrep (clean-code ruleset)
- Semgrep bug rules (
.semgrep/bugs.yaml, skipped if absent) - AST clean-code checks
- basedpyright
- pylint
- contract runner (AST + CrossHair; optional bug-hunt timeouts)
- TDD gate, unless
no_tests=True
When SPECFACT_CODE_REVIEW_PR_MODE=1 is present, the runner also evaluates a
bundle-local advisory PR checklist from SPECFACT_CODE_REVIEW_PR_TITLE,
SPECFACT_CODE_REVIEW_PR_BODY, and SPECFACT_CODE_REVIEW_PR_PROPOSAL without
adding a new CLI flag.
The merged findings are then scored into a governed ReviewReport.
Representative programmatic use:
from pathlib import Path
from specfact_code_review.run.runner import run_review
report = run_review(
[Path("src/app.py")],
no_tests=False,
include_noise=False,
bug_hunt=True,
review_level="error",
review_mode="shadow",
)The bundle now ships specfact/clean-code-principles as a resource payload at:
packages/specfact-code-review/resources/policy-packs/specfact/clean-code-principles.yamlpackages/specfact-code-review/src/specfact_code_review/resources/policy-packs/specfact/clean-code-principles.yaml
The manifest exposes the clean-code rule IDs directly so downstream policy code can apply advisory, mixed, or hard modes without a second review-specific severity schema.
specfact_code_review.run.runner.run_tdd_gate(files) enforces a bundle-local
test-before-code rule for changed files under
packages/specfact-code-review/src/specfact_code_review/....
For each changed source file, the runner expects a matching unit test under
tests/unit/specfact_code_review/... using the test_<module>.py naming pattern.
Current behavior:
- missing expected test file ->
TEST_FILE_MISSING,testing,error - targeted pytest failure ->
TEST_FAILURE,testing,error - coverage below 80% ->
TEST_COVERAGE_LOW,testing,warning no_tests=Trueskips the TDD gate entirely