Contributing · 5. Quality gates and CI

**No human approval is required to merge into main, required_approving_review_count is 0, and the gate is eleven automated checks. But if you are contributing from a fork, a maintainer must approve your workflow run before any of those checks will start.** Both halves are true, and the second half is the one that matters to an outside contributor. This chapter names every check, tells you how to run each one on your machine before pushing, and states plainly where the model breaks.

5.0 The gate an outside contributor actually meets first

Every job in ci.yml runs on a self-hosted pool, runs-on: [self-hosted, ci], 24 of them (ci.yml), and no workflow carries a fork guard. GitHub does not run workflows on self-hosted runners for a pull request from a fork until a maintainer approves the run.

So the honest sequence for a fork contributor is:

StepWho actsWhat blocks
1. Open the PR from your forkyounothing
2. Workflow run awaits approvala maintainerevery check. None start.
3. The eleven checks runautomationthe checks themselves
4. Merge via the queueautomationALLGREEN

required_approving_review_count: 0 governs step 4, not step 2. Nobody has to review your code, but somebody has to let the checks run. If your PR sits with no check output at all, you are at step 2 and it is not something you can fix from your side, say so in the PR description and wait.

Two practical consequences. Run the checks locally before pushing (§5.4 below gives the commands for all eleven), because your first remote feedback may be hours away rather than minutes. And keep the first PR small: a maintainer approving a run is spending trust on an unknown contributor, and a 40-file diff is a harder thing to approve than a 3-file one.

Contributors with write access, and maintainers, skip step 2 entirely, which is why this constraint is easy to forget when you work here.

This is reference material. If you only want the command list, jump to 5.11.

5.1 Every workflow

Twenty workflow files live in .github/workflows/. All heavy jobs run on the self-hosted [self-hosted, ci] pool, except desktop-shell.yml, ci-fallback.yml, release.yml, version-sync.yml, docker.yml and the two SDK workflows.

Job names are status-check contexts. Required checks are bold.

FileWorkflow nameTriggersHas merge_group?Jobs
ci.ymlCIpush main, PR main, merge_groupYesDetect change scope, Lint, Test, Test (hosted-surfaces), MSRV (1.88.0), Coverage
agent-docs.ymlAgent docspush main, PR main, merge_groupYesVerify agent-doc references resolve
audit.ymlSecurity Auditpush main, PR main, merge_group, weekly cronYesCargo deny policy, Cargo audit, Licence check
docs.ymlDocumentationpush main, PR main, merge_groupYesBuild rustdoc, Deploy to GitHub Pages
semver.ymlSemver CheckPR main, merge_groupYesSemver Compatibility
desktop-shell.ymldesktop-shelldispatch, PR main, merge_groupYesDetect desktop change scope, lifecycle crate (std-only unit tests), desktop app compile gate (linux), desktop bundle (follow-up, non-blocking)
mutants.ymlMutation (trust core)nightly cron, dispatchYesshard <n>, merge + ratchet
mutants-diff.ymlMutation (PR diff)PR, path-filtered to trust-core cratesYescargo-mutants --in-diff
fuzz.ymlScheduled FuzzPR path-filtered, nightly cron, dispatchNoFuzz (<target>)
audit-vectors.ymlAudit bundle vectorspush main, PR mainNoVerify audit-bundle-v1 vectors
private-paths.ymlPrivate Paths Guardpush main, PR main including markdown-onlyNoNo private-monorepo refs
coverage-attestation.ymlCoverage Attestationpush main, weekly cron, dispatchNoCoverage attestation
buf.ymlProtopush and PR on proto/**, release publishedNoLint (advisory: flat proto layout), Breaking (wire format), Push to BSR
egress-probe.ymlSupply-chain egress probePR on workflow paths, dispatchNoProbe Sigstore + Trivy egress (self-hosted)
docker.ymlDockerpush main, v* tags, PR on Dockerfile paths, workflow_run on Release, dispatchNoBuild and Push Docker Image, Promote accepted release aliases
release.ymlReleasepush v* tagsNoBuild (<target>), Create Release, Combine provenance subjects, SLSA provenance, Update manifest
version-sync.ymlVersion syncpush v* tagsNoversion-matches-tag
sdk-python.ymlPython SDKtags, PR on sdks/python/**, dispatchNoBuild reproducibly, Publish to PyPI
sdk-typescript.ymlTypeScript SDKtags, PR on sdks/typescript/**, dispatchNoBuild package, Publish to npm
ci-fallback.ymlCI (fallback / ubuntu-latest)PR main on labeled and synchronize, gated on the ci:fallback labelNoLint (fallback), Test (fallback), MSRV (1.88.0, fallback)

Note what is not required: fuzzing, mutation testing, the audit-bundle vectors, the private-paths guard, the proto checks, Test (hosted-surfaces), and coverage attestation. They still turn red and are still worth fixing.

5.2 The eleven required checks on main

Queried live from gh api repos/CueCrux/Crux/branches/main/protection. These are exact context strings, a check whose name does not match character for character does not satisfy the rule.

#ContextWorkflowWhat it proves
1Lintci.ymlSix gates in order: cargo fmt --check, typos, licence headers, unwrap ratchet, release boundary, clippy -- -D warnings
2Testci.ymlcargo test --locked --workspace, plus a daemon smoke probe and the two offline tamper gates
3MSRV (1.88.0)ci.ymlThe workspace still builds on the declared minimum supported Rust version
4Coverageci.ymlcargo llvm-cov against a workspace floor, six per-crate floors and four per-file floors
5Build rustdocdocs.ymlcargo doc with RUSTDOCFLAGS: "-D warnings". A broken intra-doc link fails a required check
6Cargo deny policyaudit.ymlcargo deny check against deny.toml, licences, bans, sources, advisories
7Cargo auditaudit.ymlRustSec advisory database, cargo-audit 0.22.1
8Licence checkaudit.ymlcargo deny check licenses on its own
9Semver Compatibilitysemver.ymlcargo semver-checks. See the warning below
10Verify agent-doc references resolveagent-docs.ymlscripts/check-agent-docs.sh --exec: four assertions, see 2.6
11desktop app compile gate (linux)desktop-shell.ymlThe Tauri desktop shell still compiles, even though it is outside the workspace

Other protection settings on main:

SettingValueWhat it means for you
stricttrueYour branch must be up to date with main before merging. The merge queue is what makes this bearable
enforce_adminstrueMaintainers cannot bypass the checks either
required_approving_review_count0No review is required. Green checks are sufficient to merge
require_code_owner_reviewsfalseCODEOWNERS is advisory

Semver Compatibility is a required check that can never fail. semver.yml:28 runs cargo semver-checks and, on violations, writes them to the job summary with a warning annotation while the job stays green. The comment explains why: it is a required check, so a hard failure would wedge every PR. The plan is stated in the same comment, "Promote to enforcing (fail in the else-branch) after a clean run history." Do not read a green Semver Compatibility as proof that you did not break the API. Read the job summary.

.github/merge-queue-ruleset.README.md:70-72 lists only nine of these eleven. It omits Verify agent-doc references resolve and desktop app compile gate (linux), the latter was promoted on 2026-07-10 in PR #341 and the README was never updated. If you are reading that file, this table is the current one.

5.3 The merge queue

Status: active. Ruleset id 18213505, name main-merge-queue, enforcement active.

The versioned source of truth is .github/merge-queue-ruleset.json. It is not applied automatically; it is committed for review and reproducibility, and applied by hand.

ParameterValueWhy
merge_methodMERGEPreserve merge history. This is why the log contains Merge pull request #NNN from CueCrux/<branch> subjects
max_entries_to_build2The runner-safety knob. One code-change queue build fans out to about nine non-trivial self-hosted jobs, so 2 entries means up to about 18 concurrent jobs on top of in-flight PR CI
max_entries_to_merge3Batch up to three PRs per CI run
min_entries_to_merge1A lone PR never waits for a batch
min_entries_to_merge_wait_minutes5A brief coalescing window
grouping_strategyALLGREENA batch merges only if the whole group passes. GitHub bisects the group on failure
check_response_timeout_minutes60A never-reporting required check fails the entry rather than wedging the queue forever

Practically: you merge with gh pr merge --auto, your PR enters the queue, GitHub builds a speculative gh-readonly-queue/main/... ref, and the eleven checks run there before the merge lands.

Administering the ruleset, maintainer commands, listed here because section 5.9 depends on them:

gh api repos/CueCrux/Crux/rulesets --jq '.[] | {id, name, enforcement}'
gh api -X POST repos/CueCrux/Crux/rulesets --input .github/merge-queue-ruleset.json
gh api -X PUT repos/CueCrux/Crux/rulesets/18213505 \
  --input <(jq '.enforcement="disabled"' .github/merge-queue-ruleset.json)
gh api -X DELETE repos/CueCrux/Crux/rulesets/18213505

5.4 The merge_group rule, and the skip pattern

This is the single most important CI invariant in the repository, and it is stated in five separate workflow files.

Every required check must run on the merge_group event, or the queue hangs.

The merge queue evaluates required checks on the speculative gh-readonly-queue/main/... ref. A workflow with no merge_group: trigger never runs there, so its check never reports, so the entry waits forever. ci.yml:11, agent-docs.yml:9, audit.yml:6, docs.yml:6 and semver.yml:7 all carry a comment saying so. mutants.yml:26 carries the inverse warning: "Not a required check on main (and must not become one without a merge_group trigger, see merge-queue rule)."

The corollary is the part people get wrong: a path-filtered workflow cannot be a required check. A workflow that never runs never reports, and the queue hangs. The repository solves this the same way twice, and you must copy the pattern if you ever add a required check.

The ci.yml change-scope pattern. The workflow always triggers. A cheap job named Detect change scope (ci.yml:25) classifies the diff, the docs set is *.md, docs/, .agent/, and LICENSE*/LICENCE*, and every heavy job carries if: needs.changes.outputs.code == 'true'. GitHub counts a skipped required check as satisfied, so a docs-only PR merges cleanly without running a ten-minute build. This replaced paths-ignore, which left required checks stuck as "expected" forever; the first casualty was PR #167, a one-file markdown bump.

The desktop-shell.yml pattern is identical (desktop-shell.yml:36): always trigger on PR and merge_group, then let a Detect desktop change scope job gate the roughly ten-minute webkit build, so desktop app compile gate (linux) reports on every PR without running on most of them.

There is a subtle bug class encoded in the classifier at ci.yml:77. The step uses a herestring rather than piping echo into grep, because under pipefail a grep -q exiting at its first match can SIGPIPE the writer, and since the test is negated, a code PR would then be misclassified as docs-only and skip every heavy gate. If you edit CI shell, do not reintroduce that pipe.

5.5 The Lint job, gate by gate

Six commands, in this order (ci.yml:132):

cargo fmt --check
typos
bash scripts/check-licence-headers.sh
bash scripts/unwrap-ratchet.sh
bash scripts/assert-daemon-release-boundary.sh
cargo clippy --workspace -- -D warnings

cargo fmt

rustfmt.toml is five lines, and one of them surprises people:

edition = "2021"
max_width = 120
tab_spaces = 4
use_field_init_shorthand = true
use_try_shorthand = true

max_width = 120, not rustfmt's default of 100. If your editor reformats to 100 you will fight the gate on every save. cargo fmt --check is enforced twice, in the Lint job and again by scripts/check-agent-docs.sh --exec.

typos

typos-cli runs against _typos.toml. Two things it does:

SectionContents
[files] extend-exclude**/console-3d/vendor/: vendored three.js, minified upstream code
[default.extend-words]Domain vocabulary: ccxi, ccxseg, ccxs, corecrux, corecruxd, corecruxctl, fpr, pfordelta, COSE, LOD, thr, intoto, hel, metalness, plus tokenizer stemming test substrings ment, ful, runn
[default.extend-identifiers]unparseable and five function names containing it

If you introduce a new domain term, add it here or Lint goes red. This is one of the two most common first-PR failures and CONTRIBUTING.md does not mention it. Install the tool locally:

cargo install typos-cli
typos

The licence header

Every .rs file under crates/ must carry both the human-readable line and the SPDX identifier. scripts/check-licence-headers.sh:20 greps for two independent patterns and fails on either.

The canonical four-line header, copy it verbatim into every new file:

// Copyright (c) 2026 CueCrux Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0.
// See LICENSE in the repository root.

The script's own comment explains why both lines are needed: the human line is the long-standing header rule, and the SPDX line exists so that SBOM and compliance scanners get a parseable answer per file rather than relying on repository-level detection alone. See 8.5.

Self-check, the form AGENTS.md gives:

grep -rL "Licensed under" crates/**/*.rs

That must print nothing.

assert-daemon-release-boundary.sh

Runs PR-time as well as at release time. It asserts that twelve required distribution files exist, that the CUDA and GPU exclusion boundary holds, that the packaging script's artifact markers are present, and that the basename guard passes both its positive and its negative fixtures. Full detail in 9.3.

clippy

Run it exactly as CI does:

cargo clippy --locked --workspace -- -D warnings

The -D warnings is what makes section 5.6 matter.

5.6 Lint configuration in Cargo.toml

Cargo.toml:114 onwards. Read this block before you "fix" a style issue, because roughly 30 pedantic lints are deliberately allowed.

Rust lints (Cargo.toml:114):

LintLevel
unsafe_codeforbid, not deny. It cannot be overridden by an inner allow. There is no unsafe anywhere in this workspace and there cannot be
unused_imports, unused_variables, unused_mutdeny
unreachable_code, unreachable_patternsdeny

Clippy groups (Cargo.toml:122):

GroupLevel
correctness, suspiciousdeny
style, complexity, perf, pedanticwarn

Because CI runs clippy -- -D warnings, every warn is a hard failure on your PR anyway. The warn level buys the ability to build locally with warnings visible, nothing more.

Zero-tolerance denies: todo, unimplemented, dbg_macro, wildcard_imports, enum_glob_use. A todo!() will not compile past CI. A use foo::*; will not either.

Warned, as extracted-code debt: unwrap_used, expect_used, panic, print_stdout, print_stderr. The comment at Cargo.toml:112 explains the level: "extracted monorepo code contains hundreds of legitimate uses; new crates override to deny."

Around 30 pedantic allows (Cargo.toml:146) including module_name_repetitions, too_many_lines, all four cast_* lints, uninlined_format_args and doc_markdown. These are deliberate. Do not submit a PR that "fixes" them.

A new crate is expected to escalate the warn-level lints to deny at its crate root. corecruxd already does, see 5.7.

5.7 The unwrap ratchet

Two mechanisms, and you need both in your head.

The crate-level escalation. crates/corecruxd/src/main.rs:9 raises three workspace warns to denies for the daemon binary:

#![deny(clippy::unwrap_used)]
#![deny(clippy::expect_used)]
#![deny(clippy::panic)]

The comment above it states the reason: "The daemon must never panic on untrusted input." It also states the escape: "Individual call sites may #[allow] with a // SAFETY: justification if the unwrap is provably safe."

The policy, from docs/unwrap-triage.md: new code in corecruxd must not use unwrap(), expect() or panic!(). If a call site is provably safe, add #[allow(clippy::unwrap_used)] with a // SAFETY: comment explaining why. docs/unwrap-triage.md enumerates the eight surviving production call sites and the allowlisted metrics module, which carries about 250 sites as a Prometheus client-library constraint with a stated reduction plan of "None".

docs/unwrap-triage.md:3 quotes a single combined #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)]. The source has three separate attribute lines. Cosmetic, but it is a quoted claim about source, so it is wrong.

The ratchet. scripts/unwrap-ratchet.sh counts non-test unwrap() and expect() per crate and fails the Lint job if any crate exceeds its recorded count in scripts/unwrap-baseline.txt. This applies to every crate, not just corecruxd. Adding unwraps to a crate that is already at its baseline fails the build. The baseline is raised only deliberately and in its own commit.

bash scripts/unwrap-ratchet.sh

5.8 Coverage: the gate, the floors, the exclusion policy

The Coverage check runs cargo llvm-cov. To reproduce it exactly, same ignore regex, same flags:

RX='(.*/corecruxd/src/pool\.rs|.*/corecruxd/src/dataplane_store\.rs|.*/corecruxd/src/http/dataplane\.rs|.*/corecruxd/src/main\.rs|.*/corecruxctl/src/main\.rs|.*/crux-claude-hooks/src/main\.rs|.*/crux-claude-hooks/src/bin/crux_llm_shim\.rs|.*/crux-config-wizard/src/main\.rs|.*/crux-config-wizard/src/interactive\.rs)$'
cargo llvm-cov --workspace --ignore-filename-regex "$RX" --summary-only

You need cargo-llvm-cov, the llvm-tools-preview component, and a C toolchain. Remember section 1.5: this creates a third target/ tree.

The gate measures region coverage, column 4 of the TOTAL line, not line coverage. Reading the wrong column is a common source of confusion.

The floors, authoritative at ci.yml:429 onwards:

ScopeFloorSource
Workspace total86%ci.yml:429
corecrux-memory93, with 95 as the ratchet targetci.yml:446
crux-sync98ci.yml:446
crux-contrib99ci.yml:446
corecrux-receipts88ci.yml:447
corecrux-segment85ci.yml:447
corecrux-storage79ci.yml:447
corecruxd/src/config.rs90ci.yml:457
corecruxd/src/control.rs90ci.yml:457
corecruxd/src/structured_log.rs90ci.yml:457
corecruxd/src/problem.rs90ci.yml:457

Two properties worth understanding:

Floors are set at current-rounded-down. The comment at ci.yml:441 calls this "ratchet from reality". They prevent regression; they are raised as coverage improves. Raising corecrux-memory from 93 to 95 is a pre-declared, wanted change, see chapter 10.

The job prints the ungated total too. ci.yml:419 computes coverage with no exclusions at all and emits both numbers as a ::notice::, so the ignore regex can never quietly hide low-coverage code from review.

The exclusion policy is deliberately narrow. The ignore list covers only binary entry points (four main.rs files plus crux_llm_shim), the interactive config wizard, and the dataplane layer, which is an unconstructable typecheck stub in the CPU-only build, never constructed, with every method unreachable!(). Adding an exclusion is a reviewed decision, not a way to make your PR go green.

Keep the regex in lock-step. COVERAGE_IGNORE_REGEX is duplicated at ci.yml:382 and in .github/workflows/coverage-attestation.yml. Change both together.

One piece of history worth knowing, because it explains why the floors look conservative. Before 2026-06-17 the per-crate awk summed $1, the file path column, a string, which awk evaluates as 0, so every crate computed to "100.0" and the corecrux-memory, crux-sync and crux-contrib floors were inert no-ops that could never fail. The fix sums $2 and $3, and the no-match branch now yields 0.0, failing loudly, instead of 100.0. The comment at ci.yml:436 records the whole thing.

5.9 The operational limits, stated honestly

Three things here are genuinely awkward for an outside contributor. They are stated because finding them out at 3am is worse.

Every required check is self-hosted-only. All eleven run on the [self-hosted, ci] pool. When that pool is unhealthy, a missing C toolchain, broken passwordless sudo, a full data disk, every matrix job fails within 9 to 15 seconds and your PR cannot go green no matter what your code does. You cannot fix the runner. Section 5.10 is what a maintainer does.

The ci:fallback escape hatch exists, and it does not work in the queue. Labelling a PR ci:fallback makes ci-fallback.yml run Lint (fallback), Test (fallback) and MSRV (1.88.0, fallback) on GitHub-hosted ubuntu-latest instead. The rationale in ci-fallback.yml:6 is explicit: operators previously shipped urgent PRs by admin-merge when the runner broke, and this is the sanctioned override. Coverage thresholds are relaxed to warnings there, because caches are cold.

But the label cannot work inside the merge queue, queue entries are not pull requests and cannot be labelled. This is recorded as caveat OD-MQ-2 at merge-queue-ruleset.README.md:74. The documented recovery is to disable the ruleset entirely so PRs merge directly and the fallback path works, then re-enable it when the runners recover. That is a maintainer action using the gh api -X PUT command in section 5.3. If you are an outside contributor and the pool is down, the honest answer is that your PR waits.

A fallback run does not satisfy the required checks. Lint (fallback) is a different context string from Lint. The fallback workflow gives a maintainer signal and a path to an informed admin merge; it does not turn the eleven boxes green.

5.10 Self-hosted runner recovery

docs/self-hosted-runner.md is the operator guide. You will recognise the broken state by any of these:

SymptomMeans
error: linker 'cc' not foundThe runner has no C build toolchain
sudo: a password is required during taiki-e/install-actionThe runner user lacks passwordless sudo, so the action cannot install tools
error: failed to run custom build command for proc-macro2The same missing toolchain, surfacing through a build script
Every matrix job failing within 9 to 15 secondsAlmost always one of the above

The fix is at the OS level, on the runner host, and requires access you probably do not have:

cd /path/to/Crux
sudo bash scripts/provision-self-hosted-runner.sh
sudo systemctl restart actions-runner.gha-runner.service

The provisioning script installs build-essential pkg-config libssl-dev clang lld curl jq git cmake protobuf-compiler, writes a nopasswd sudoers fragment, and verifies both cc --version and sudo -n true.

Every cargo invocation in CI is wrapped by scripts/ci-cargo-with-fallback.sh; fuzzing uses scripts/ci-fuzz-with-fallback.sh.

Two claims in docs/self-hosted-runner.md are wrong. Line 3 says "the workspace builds 26 Rust crates"; it is 28. Line 45 names the preflight step Preflight: verify build toolchain and quotes the log line ::error::Self-hosted runner is missing C build toolchain (cc not found). Neither string exists. The step is Preflight (self-hosted runner toolchain) (ci.yml:113) and the emitted line is ::error::Self-hosted runner is missing required tooling:$MISSING (ci.yml:120). An operator grepping the documented text against a real log finds nothing. The real preflight also emits a third line pointing at the ci:fallback label, which the doc does not mention.

5.11 The pre-push checklist that actually matches CI

CONTRIBUTING.md lists three commands. The Lint job alone runs six, and there are eleven required checks. This is the real list.

cargo fmt --check
typos                                        # cargo install typos-cli
bash scripts/check-licence-headers.sh
bash scripts/unwrap-ratchet.sh
bash scripts/assert-daemon-release-boundary.sh
cargo clippy --locked --workspace -- -D warnings
cargo test --locked --workspace
bash scripts/check-no-private-paths.sh
bash scripts/check-agent-docs.sh --exec      # if you touched docs/agent, llms.txt, or any AGENTS.md
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps
cargo deny check                             # cargo install cargo-deny --locked --version 0.19.4
cargo audit                                  # cargo install cargo-audit --locked --version 0.22.1
./scripts/run-integration-tests.sh           # if you touched the daemon surface

Three of these are not mentioned anywhere in CONTRIBUTING.md or the pull-request template, and they are the three that most often surprise a newcomer:

CommandWhy it bites
typosA new domain term is a red Lint until it is in _typos.toml
bash scripts/unwrap-ratchet.shAdding one unwrap() to a crate at its baseline fails the build
RUSTDOCFLAGS="-D warnings" cargo docA broken intra-doc link is a required-check failure via Build rustdoc

scripts/check-no-private-paths.sh backs the Private Paths Guard workflow. It fails the build if any source file or user-facing document references PlanCrux/, the private planning monorepo. It is not a required check, but it exists because this is a public repository and a leaked internal path is a real problem.

5.12 Supply-chain policy: deny.toml and cargo audit

deny.toml drives two required checks: Cargo deny policy runs cargo deny check, and Licence check runs cargo deny check licenses separately. CARGO_DENY_VERSION is pinned to 0.19.4.

PolicySettingConsequence for a dependency you add
Allowed licences (deny.toml:18)MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, Zlib, Unicode-DFS-2016, Unicode-3.0, OpenSSL, BSL-1.0, CC0-1.0, Unlicense, CDLA-Permissive-2.0, bzip2-1.0.6A crate under GPL or AGPL will not pass. There is a [[licenses.clarify]] entry for ring
multiple-versions (deny.toml:54)warn, with a documented owner and expiryTwo RustCrypto generations coexist today for stated reasons. Do not add a third casually
wildcards (deny.toml:61)deny, with allow-wildcard-paths = trueA "*" registry requirement is rejected. Unversioned { path = "../x" } workspace deps are exempt
Sources (deny.toml:70)unknown-registry = "deny", unknown-git = "deny", allow-git = []No git dependencies are permitted at all

The wildcard exemption is why Cargo.toml:53 sets publish = false for the whole workspace. The comment there spells out the chain: the crates are Apache-2.0 licensed but not (yet) published to crates.io, so marking them non-publishable lets cargo-deny exempt the path wildcards while still denying registry wildcards.

scripts/check-deny-advisory-ignores.sh runs before cargo deny check and enforces that every entry in the [advisories] ignore list carries metadata. You cannot silently mute an advisory.

5.13 Review, CODEOWNERS and Dependabot

MechanismReality
Reviewrequired_approving_review_count: 0. The gate is the eleven checks. Merges go through the queue via gh pr merge --auto
.github/CODEOWNERS* @myles, plus explicit ownership of the storage, segment and frame crates, the retrieval and index crates, crux-mcp, and .github/. All the same owner. Code-owner review is not required, so the file is advisory today
.github/dependabot.ymlWeekly cargo updates, limit 10, label dependencies. Weekly github-actions updates, label ci
Labels availablebug, documentation, duplicate, enhancement, good first issue, help wanted, invalid, question, wontfix, and ci:fallback. Dependabot adds dependencies and ci

Because no human approval is required, your PR description and your commit body are the review. See chapter 7.

Sources