Checks a whole repository: CI gates, dependencies, setup, and conventions. Use it for this input: repo. Report each check as PASS, FAIL, or N/A with file:line evidence. A FAIL carries the card's severity. Any blockFAIL means request changes. A should-fix or discussFAIL goes in the review but does not block.
FAIL [block] chg-expand-contract - src/a.py:42 - renames the column and drops the old one in one migration
FAIL [should-fix] chg-one-concern - src/b.py:7 - a logging rewrite rides along with the bug fix
PASS chg-breaking-change-taxonomy - src/c.py:15 - no signature or return shape changes
N/A chg-perf-claim-evidence - the description makes no performance claim
Checklist
CardSeverityConcernsRule
code-adrdiscussmaintainabilityprocessKeep a short written record for each significant design choice (datastore, framework, deviation from a pattern) that states the context, the decision, and its consequences.
code-build-hermeticshould-fixprocessBuild from declared inputs only, so that the same source revision gives the same artifact.
code-ci-gatesshould-fixprocessRequire review and passing build, tests, lint, and type checks on the default branch before merge, and leave nothing mechanical to reviewer vigilance.
code-consistency-driftdiscusscorrectnessmaintainabilityFor each recurring concern (errors, config, HTTP clients, logging), make one pattern the standard instead of leaving several as equal peers.
code-dead-codeshould-fixmaintainabilityRemove unreferenced functions, commented-out code, expired flags, and orphaned files; history keeps them.
code-dependency-licence-sbomshould-fixsecurityGenerate an SBOM, and scan dependencies for known vulnerabilities on every change or on a schedule.
code-dependency-pinningblocksecurityCommit a lockfile with hashes and install from it in CI.
code-perf-budget-in-cishould-fixperformanceprocessWrite the performance budget down as a CI check that fails the build when it is exceeded.
code-rule-has-enforcershould-fixmaintainabilityBack each rule that a type, lint or CI check could enforce with that check, not with a line in an instruction file or a repeated review comment.
code-secret-scanblocksecurityprocessScan for committed credentials, and treat any hit as leaked: revoke and replace it first.
Tool gate: what a linter or CI check could enforce instead of a reviewer.
Filters
Concerns
Topic tags
Topics
Cards
code-adr
discussstablemaintainabilityprocess
Applies when: Auditing whether the reasons behind significant design choices are recorded.
Rule. Keep a short written record for each significant design choice (datastore, framework, deviation from a pattern) that states the context, the decision, and its consequences.
Why. Without a record, the next reader reverses a decision blindly or cargo-cults it. Also list the options you ruled out and why: a record that weighs only the chosen design, its strengths or its flaws, is not a trade-off analysis.
FAIL
Repo uses Kafka for one 10-msg/day job. No record why; nobody dares remove it.
PASS
docs/adr/0007-queue.md: context, decision, consequences, status, and the 2 options ruled out.
Counterpoint. ADRs rot and become paperwork. Keep each to a page, write them only for hard-to-reverse choices, and mark superseded ones.
Applies when: The repository has a build, a packaging, or a release step.
Rule. Build from declared inputs only, so that the same source revision gives the same artifact.
Why. A build that reads whatever is installed on the machine gives different output on different machines, so a bug cannot be reproduced and a release cannot be checked against its source. Tools are inputs too: pin the compiler and toolchain versions, and do not call host binaries. Undeclared shared files also break builds at scale, for example two tasks that clean and use the same /tmp directory race each other.
FAIL
steps:
- run: make # uses whatever gcc, node and protoc the runner has
- run: date > BUILD_ID # the clock is an undeclared input
PASS
container:
image: ghcr.io/acme/builder@sha256:4b1e... # toolchain pinned by digest
steps:
- run: SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) make # timestamps come from the commit
Counterpoint. "Bit-identical builds are overkill." Full reproducibility is the ceiling. Declared inputs, pinned tools, and a recorded revision are the floor, and they cost little. code-dependency-pinning covers dependency pins.
Tool gate: Build twice in clean containers and compare checksums; diffoscope shows where two artifacts differ.
Applies when: Auditing which checks must pass before a change merges.
Rule. Require review and passing build, tests, lint, and type checks on the default branch before merge, and leave nothing mechanical to reviewer vigilance.
Why. Any rule that depends on someone remembering will be skipped. Components where changes merge unreviewed or without discussion tend to ship more post-release defects.
FAIL
Default branch: no protection. Force-push allowed. CI optional.
Counterpoint. Gates slow small teams, and flaky tests train people to override. Keep gates few and reliable, fix flakes instead of overriding them, and let the gates own the mechanical checks so review time goes to design.
Tool gate: Branch protection with required status checks.
Applies when: Auditing whether one concern is solved in several incompatible ways across the repo.
Rule. For each recurring concern (errors, config, HTTP clients, logging), make one pattern the standard instead of leaving several as equal peers.
Why. Each extra variant is another thing to learn, test, and secure, and drift accumulates through changes that each looked locally fine. Two limiters built two ways enforce the same quota differently. List the older variants and count them, so the migration shows progress and a new one shows up in review.
FAIL
Three HTTP clients (requests, urllib3, httpx) with three timeout conventions;
no document says which is current.
PASS
docs: "use httpx via net/client.py". Lint bans direct requests imports;
legacy sites listed in MIGRATION.md.
Counterpoint. "Forcing one way can block a better tool." Allow change through a proposal that updates the standard and the migration list, not through a quiet second pattern. Consistency breaks ties. It does not outrank a measured reason to switch.
Tool gate: Lint rules that ban the deprecated pattern once a standard is chosen.
Applies when: Auditing for code, flags and files that nothing uses.
Rule. Remove unreferenced functions, commented-out code, expired flags, and orphaned files; history keeps them.
Why. Dead code costs reading time, misleads searches, and gets "fixed" and tested for no benefit.
FAIL
# def old_export(): ... (commented out since 2022)
if FLAGS["legacy_ui"]: # flag permanently false since release 4
PASS
Code deleted; git log -S shows where it went. Detector reports 0 unused symbols
or each is allowlisted with a reason.
Counterpoint. Code may be called by reflection, plugins, or external users. Check with the detector's config and a grep of consumers. For public API, deprecate first (see chg-expand-contract).
Tool gate: Dead-code detectors (vulture, knip, deadcode) plus unused-export lint in CI.
Applies when: Auditing what third-party code the repo ships and under which terms.
Rule. Generate an SBOM, and scan dependencies for known vulnerabilities on every change or on a schedule.
Why. Without an inventory, a team cannot answer a disclosed vulnerability quickly. CI is the usual place for both steps: the SBOM then matches what was built, and the scan runs without anyone remembering it. Team policy: also fail the build on a dependency whose licence is not on the approved list.
FAIL
No SBOM anywhere, no scanner in CI, and dependency alerts turned off.
PASS
CI: generate SBOM, run vulnerability scanner, fail on high severity;
release artifacts attach the SBOM.
Counterpoint. SBOMs and scanners create noise that nobody triages. Tune severity thresholds and assign an owner, since an ignored report is worse than none. The licence list is team policy, not a universal rule. Drop it if your team has no licence constraints.
Tool gate: A vulnerability scanner such as OSV-Scanner or Dependabot alerts; an SBOM generator such as Syft.
Applies when: Auditing how the repo resolves third-party packages.
Rule. Commit a lockfile with hashes and install from it in CI.
Why. Unpinned versions make builds non-reproducible and let a compromised release flow straight into production. Updates should arrive as reviewed, staged changes, not silently. code-workflow-supply-chain covers CI action pins.
FAIL
requirements.txt: requests>=2 (no lockfile; CI runs pip install -U)
PASS
requirements.lock with hashes; CI: pip install --require-hashes -r requirements.lock
Update bot opens PRs; changes are rolled out in stages.
Counterpoint. Pinning delays security patches. A pin plus an update bot with a defined cadence beats floating versions, because the lock makes the update visible and revertable.
Tool gate: Lockfile-required install in CI; dependency-update bot config present.
Applies when: The repo has a performance target (bundle size, page metrics, latency under load, or a benchmark) that matters to users.
Rule. Write the performance budget down as a CI check that fails the build when it is exceeded.
Why. Performance regresses a few percent at a time, and no reviewer sees a 3% slowdown in a diff. A budget that only warns becomes a log line nobody reads.
FAIL
lighthouserc.json: "assertions": { "categories:performance": ["warn", { "minScore": 0.9 }] }
k6 script with no thresholds block
PASS
size-limit: [{ "path": "dist/app.js", "limit": "180 kB" }] # npm run size in CI
k6: thresholds: { http_req_duration: ["p(95)<300"] } # non-zero exit fails the job
Counterpoint. "Benchmarks in CI are noisy and flake." They are when a gate trusts one run. Gate on a stable budget, compare repeated runs (benchstat wants 10 to 20), and keep the threshold above the noise. On a site with much room to improve, a tight budget only turns every build red, so set it at today's level and lower it later.
Tool gate: Lighthouse CI assert, size-limit, k6 thresholds, Gatling assertions, or a benchstat comparison in CI.
Applies when: Auditing a repo's instruction files, review history or comments that state a rule.
Rule. Back each rule that a type, lint or CI check could enforce with that check, not with a line in an instruction file or a repeated review comment.
Why. A rule that relies on memory gets broken, and reviewers then repeat the same correction. An instruction file is advisory: a person or an agent can skip it. A lint message that names the fix teaches it at the moment of the mistake. A "do not remove" code comment has the same weakness as an instruction file line.
FAIL
CONTRIBUTING.md: "never call requests.get without a timeout" (no lint rule)
Review history: the same comment on 6 PRs
PASS
# pyproject.toml, checked by ruff in CI
[tool.ruff.lint]
extend-select = ["S113"] # request-without-timeout
Counterpoint. "Not every rule is checkable." Right, and judgement rules stay as prose. The FAIL is a checkable rule with no check. See code-ci-gates for where checks run, and chg-review-signal for what reviewers stop commenting on once a tool owns it.
Tool gate: The enforcer itself: a type, a lint rule or a CI check.
Applies when: Any repository, including its history, issues and pull requests.
Rule. Scan for committed credentials, and treat any hit as leaked: revoke and replace it first.
Why. A secret in history is readable by everyone with clone access, and forks and caches keep it after a rewrite. Rewriting history is slow and often unnecessary once the secret is dead.
FAIL
git log -p | rg 'AKIA' # a live cloud key in an old commit; "removed it in the next commit"
PASS
scanner runs on every push and covers history
key revoked and reissued the same day; new value comes from the secret store
Counterpoint. "Scanners give false positives." Tune with an allowlist of reviewed, non-secret strings. A missed live key costs far more than a triaged alert. sys-secrets-and-errors covers where secrets live at runtime.
Tool gate: Secret scanning on push (provider or pre-commit hook) with push protection on.
Applies when: Auditing CI workflow files and release pipelines.
Rule. Pin every CI action to a full commit SHA.
Why. The pipeline holds secrets and publish rights, so a compromised action or injected input owns the artifact. Also give workflows least-privilege tokens, keep untrusted event data out of run: and out of pull_request_target checkouts, and attach build provenance to releases. code-dependency-pinning covers dependency pins, and code-dependency-licence-sbom covers the inventory.
FAIL
steps:
- uses: actions/checkout@v4 # tag, can be moved to other code
- uses: some/action@main # branch, changes on every push
Counterpoint. "SHA pinning blocks automatic fixes and adds update churn." Let an update bot propose SHA bumps, so fixes still arrive as reviewed changes. Where the platform offers a lockfile for workflow dependencies, that lockfile is stronger than SHA pins and meets this check.
A draft card is not vetted. Read it for ideas, but do not use it for a verdict.
code-a11y-check-in-ci
should-fixdraftaccessibilitytesting
Applies when: The repo builds a web UI and has an automated test suite.
Rule. Run an automated accessibility check, such as axe on rendered pages, in the automated tests, and fail the test on a violation.
Why. A violation that nobody sees at review ships. A failing test catches the missing label or alt on the pull request that adds it, and run in CI on every change it blocks the merge. A JSX lint such as jsx-a11y catches some of the same problems earlier, in the editor. Either way it is a floor: tools find only part of the problems, so a person still tests with a keyboard.
FAIL
// e2e: axe runs, but nothing fails on what it finds
const results = await new AxeBuilder({ page }).analyze();
console.log(results.violations.length);
PASS
// e2e: fail on any axe violation
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
Counterpoint. "Tools only catch a minority, and we have a backlog, so a failing check would turn every build red." The check catches the cheap failures at no review cost, which frees the manual pass for what only a person can judge. For the backlog, exclude the known violations by name (disableRules, exclude, or a fingerprint of each known issue) and fail on anything new. Remove each exclusion once it is fixed.
Tool gate: This card is the gate: axe (through @axe-core/playwright or similar) against rendered pages in the test step; eslint-plugin-jsx-a11y in the lint step for JSX code.
Applies when: A pull request was written in whole or part by an AI tool.
Rule. Disclose AI assistance in the PR, and have a human author who can explain and defend every changed line without the tool.
Why. Reviewers calibrate by who wrote the code. An undisclosed generated change gets the wrong scrutiny, and an author who cannot explain it leaves the reviewer to do the author's job.
FAIL
PR description: "Add retry logic."
(500 generated lines; asked why a 3rd retry, the author says "the model chose it")
PASS
PR description: "Add retry logic. Drafted with <tool>; I read and tested every line.
Tests: test_retry.py. Not covered: jitter."
Trailer: Assisted-by: <tool>
Counterpoint. "Disclosure stigmatises tool use." The rule is about scrutiny, not blame: disclosed work is reviewed the same way and merged on the same terms. The agent never signs off or certifies origin; the human does.
Tool gate: PR template checkbox for AI assistance; commit trailer check.
Applies when: Auditing a repo's README or contribution guide.
Rule. Make every setup, build and run command in the README or contribution guide succeed on a clean checkout.
Why. A stale step costs each new contributor and each agent the same time, and they cannot tell a doc error from their own mistake.
FAIL
README: `make dev` -> make: *** No rule to make target 'dev'
PASS
CI job: fresh clone, run the README's commands in order, fail on any non-zero exit
Counterpoint. "Setup depends on the machine." Document the prerequisites, and run the commands in a clean container to prove them. Judge a miss by the damage it causes, not by its existence.
Tool gate: CI job that runs the documented setup on a clean checkout.
Applies when: Auditing a repo for files that have grown past comprehension.
Rule. Keep every hand-written source file under about 1,000 lines unless it has a listed exception, and split larger files by knowledge owned, not by execution order.
Why. Huge files hide unrelated decisions, attract merge conflicts, and cannot be understood in one read.
No hand-written file over 1,000 lines; generated files listed in an allowlist.
Counterpoint. Ousterhout favours deep modules and a long file can be coherent. Splitting for its own sake adds shallow pieces. 1,000 lines is a trigger for a look, not a verdict. The fix is to separate decisions (his information-leakage and temporal-decomposition flags).
Tool gate: CI check on lines per file (cloc, wc) with an allowlist.
Applies when: Auditing how the codebase handles tests that pass and fail on the same code.
Rule. Report every test that passes only on retry, move it to a quarantine with a named owner and a ticket, and return it to the merge gate only after it passes many reruns.
Why. A test that fails at random on unchanged code teaches people to rerun until green, and then a real regression looks like noise. Dropbox removes tests that fail too often from the main pool and files a bug against the owner. Its fix workflow then runs the test 100 or more times in CI before the fix lands. Shopify retries a failed test but tells developers about every pass after a retry. The usual causes are a fixed pause where the test should wait for an element (Shopify) and state left behind by an earlier test (Cabify, Dropbox).
FAIL
# .github/workflows/test.yml
- run: pytest --reruns 3 # a pass on retry counts as a pass, and nobody hears about it
PASS
# .github/workflows/test.yml
- run: pytest --reruns 3 -m "not quarantine" --junitxml=report.xml
- run: python tools/flaky_report.py report.xml # opens a ticket per pass after retry, owner from CODEOWNERS
# nightly.yml: a quarantined test rejoins the gate after 100 clean runs
- run: pytest -m quarantine --count 100
Counterpoint. "Retries keep the build green, and quarantine hides coverage." Shopify's data backs retries: a slightly slower build beats an engineer rerunning it by hand. Retries are fine when each pass after a retry reaches someone. A quarantined test still runs outside the gate, and its owner and ticket stop it from staying there for good. Skipping or deleting a flaky test in a diff is a separate FAIL. See chg-no-silenced-diagnostics. For the merge gate itself, see code-ci-gates.
Applies when: Auditing how a contributor checks a small change.
Rule. Document a command that checks one package or path, with readable output, so a small change does not need the full-repo run.
Why. If the only check takes twenty minutes or prints thousands of lines, people skip it and push to CI, and agents cannot tell which failure is theirs.
pytest tests/billing -q # about 20 s
ruff check src/billing
Counterpoint. "CI runs everything anyway." CI is the last check, not the loop. A scoped command lets the author find a failure in seconds instead of after a push.
Tool gate: A documented command that runs tests and lint for one path or package.