This is a playbook. Use it when a pull request is red and you want the next push to pass. It comes from current workflow files and from many CI-fix sessions in this repo.
Source of truth for status: gh pr checks, not gh run list.
gh run list misses required checks that are not GitHub Actions jobs.
CI repair note, 2026-09-30: CI was already red during the Null consensus release work (PR #3206). Compare failed checks with their base-branch logs and tested SHAs before choosing a fix. Existing failures, regressions, and transient infrastructure problems need different remedies, but none is a reason to ignore a failed release check.
Always finish a local pass with:
git diff --check
git status --shortDo not run scripts/fix_rust.sh during a normal CI loop. That script
creates commits.
Do this in order. Fix failures as they appear and keep watching the remaining jobs, including slow checks and infrastructure retries, until CI is green.
- Confirm GitHub is testing the commit you think it is. Local fmt/clippy that you did not push does not count.
- Run
scripts/preflight.sh(see §3). It scopes itself to the files you changed and stops at the cheap failures first. - Push the validated fix immediately after local preflight passes; do not wait for the rest of the remote suite to finish.
- Watch with fail-fast. When any check fails, inspect its logs and begin the fix immediately while other jobs continue. Restart the watcher after each push and repeat until the complete check set is green.
- Rerun only when the log is infra (429, GHCR pull/push, warp-proof, cancelled sibling).
- Do not push again just to "unstick" CI. A new push cancels in-flight Runtime Checks. Clone-upgrade and try-runtime then restart from zero (often 40+ minutes).
- Leave clone-upgrade and e2e to CI unless you already know they are
the failure. Clippy and
cargo testfor changed crates are part of the local gate.
gh pr view --json number,url,headRefName,headRefOid
gh pr checks --json name,bucket,state,workflow,link
gh pr checks --watch --fail-fast
# After a failure:
gh run view <run-id> --log-failed--fail-fast stops on the first failure. Other jobs may still be green
or still running. Re-read the full check set after every push. The set
can change.
gh pr checks is tab-separated. Names have spaces. To list all failures:
gh pr checks <N> | awk -F'\t' '$2=="fail"'A required check is a GitHub branch-protection name. If that name is missing or red, merge stays blocked.
A skipped job usually counts as passing. That is why path filters and labels exist. Do not add a new required check name without a skip-success path, or docs-only PRs get stuck on "Expected".
These names matter today:
| Check name | Workflow | Usual local match |
|---|---|---|
cargo fmt |
Check Rust | cargo fmt --check --all |
cargo clippy (default) |
Check Rust | SKIP_WASM_BUILD=1 cargo clippy --workspace --all-targets -- -D warnings |
cargo clippy (all) |
Check Rust | same + --all-features |
cargo test |
Check Rust | leave to CI |
bittensor-core wasm32 seam |
Check Rust | Linux/wasm; flake on GitHub 429 |
zepter feature propagation |
Check Rust | zepter run check |
SDK offline checks |
Runtime Checks | cd sdk/python && just check (almost) |
Docs and website build |
Runtime Checks | generate.py --check then website build |
spec_version newer than mainnet |
Runtime Checks | bump or label no-spec-version-bump |
Sudo-upgrade mainnet clone and test |
Runtime Checks | clone-upgrade fan-in; see below |
cargo test (eco-tests) |
Eco Tests | cd eco-tests && cargo test |
typescript-formatting |
Typescript E2E | cd ts-tests && pnpm run fmt |
typescript-e2e-zombienet_evm |
Typescript E2E | gate name; skip when EVM not selected |
typescript-e2e-zombienet_shield |
Typescript E2E | gate name; skip when shield not selected |
skeptic / auditor |
ai-review | required; often not a compile bug |
cargo fix leaves no diff and no cargo check warnings run on main /
manual, not on pull requests.
One command runs every gate below with CI's exact flags, scoped to the
files changed vs origin/main, and exits nonzero on any failure:
scripts/preflight.sh # full gate (builds the release node when runtime metadata may have changed)
scripts/preflight.sh --fast # skip the node / wasm builds
scripts/preflight.sh --all # every gate regardless of the diff
scripts/install-hooks.sh # pre-push hook: a red gate aborts the pushInstall the hook once per checkout. It gates the pushed commits in a
detached worktree (preflight.sh --rev SHA), so uncommitted fixes do
not count and git push origin other-branch validates other-branch.
git push --no-verify is forbidden for agents. Pushes go out as
unarbos; the gate verifies who owns the push credential for the
destination remote via api.github.com/user and prints the
git remote set-url fix when it is not unarbos. Build artifacts are
resolved from Cargo's target dir (--print-artifacts shows the paths);
scripts/test-preflight-rev.sh is the regression for the --rev path.
The rest of this section explains what each gate runs and how to fix it. Match fix-mode commands to the files you own.
cargo fmt --check --allIf it fails only on files you own:
cargo fmt --allThen inspect the diff. Typical hit: import order. rustfmt wants
use frame_support::storage::{TransactionOutcome, with_transaction}
on one line, or use super::*; above other imports.
just clippy is not the CI job. CI is:
SKIP_WASM_BUILD=1 cargo clippy --workspace --all-targets -- -D warnings
SKIP_WASM_BUILD=1 cargo clippy --workspace --all-targets --all-features -- -D warningsRoot just clippy only denies todo and unimplemented. CI denies
all warnings. Unused imports fail CI and pass just clippy.
scripts/preflight.sh runs both whenever a Rust path changed, plus
zepter run check and cargo test --all-features for the changed
crates (and runtime/tests/fee_baseline.rs + claim_root_weight.rs
when pallets/ or runtime/ changed).
cargo clippy -p pallet-subtensor --lib misses test-only denies.
A new test module usually needs the same crate-level allow as the
other pallet tests:
#![allow(clippy::expect_used, clippy::unwrap_used, clippy::indexing_slicing)]Do not touch a .rs file to "clear" a missing use. If Clippy
wants WeightInfo in scope, add the import.
From sdk/python, when the locked env exists:
uv run --no-sync ruff check .
uv run --no-sync ruff format --check .Use uv run ruff, not ruff on PATH. A system or stale .venv
binary will report lints (for example UP045) that CI's locked ruff
does not, or the reverse. CI pins ruff through uv sync --python 3.14.
Lint the whole SDK. Formatting one file (query.py) while CI
scans everything will bounce.
Fix only task-owned files:
uv run --no-sync ruff check . --fix
uv run --no-sync ruff format .CI SDK offline checks also runs (from sdk/python):
uv sync --python 3.14 --locked --all-extras --dev
uv run ruff check .
uv run ruff format --check .
uv run ty check --exit-zero-on-warning bittensor
uv run pytest
uv run python -m codegen.check --coverage
uv run python -m codegen.check --names
uv run python scripts/export_beta_baselines_rs.py --checkjust check in sdk/python also runs --units and --namespaces.
CI does not. A local just check failure on those two is not always
a CI failure.
From sdk/python:
uv run --no-sync python ../../website/apps/bittensor-website/scripts/generate.py --checkIf the drift is from this change:
uv run --no-sync python ../../website/apps/bittensor-website/scripts/generate.pyRepeat --check. Inspect the exit code. --check can print a
long file list and still exit 0. A list is not always a fail.
Order that avoids the fmt/regen race:
- Finish Rust and Python source.
cargo fmt --allgenerate.pythengenerate.py --check- Do not fmt again after regen. Line anchors in MDX and
reads.jsonwill move and the gate goes red again.
Do not hand-edit docs/tx/, docs/query/, docs/errors/, generated
hyperparameter pages, or website/apps/bittensor-website/public/catalog/.
If --check fails on tens of unrelated pages, stop. That is leftover
WIP, not your one-line change. Do not dump a 50-file regen into someone
else's PR.
MDX pages you do write by hand need string title and description
frontmatter. Missing those fail the website build after the drift gate.
From ts-tests, when the locked env exists:
pnpm run fmtFix: pnpm run fmt:fix.
Canonical files: precompiles/src/solidity/*.abi.
Vendored copies: sdk/python/bittensor/evm/abi/*.json.
From sdk/python:
uv run --no-sync pytest tests/unit/test_evm.py::TestVendoredAbiSync -qUpdate the vendored JSON only when the canonical ABI changed.
Each row is a failure we keep hitting. Symptom first. Then the fix.
Symptom: Diff in path/to/file.rs. Often import order or a long
iterator chain that rustfmt wants on one line.
Fix: cargo fmt --all. Commit only the rustfmt diff. Re-run
cargo fmt --check --all.
Traps:
- Do not edit
.rustfmt.toml. Almost every option is commented on purpose. Turning options on reformats hundreds of pallet files. vendor/frontierprintsimports_granularitywarnings. Stable rustfmt ignores them. They do not fail the job.- rustfmt can ICE and still exit 0. CI greps the output for
panicked at/internal compiler error. cargo fmt -p pallet-subtensoris not enough. CI is--all.
Symptom: unused import, clippy::expect_used, indexing, or a
compile error. -D warnings turns every warning into a hard fail.
Fix: smallest code change. Then run both Clippy jobs. Default
green does not mean --all-features is green.
Traps:
- One compile error in
pallet-subtensorturns Clippy,cargo test, Docker, e2e node builds, and clone-upgrade red at once. Fix the compile first. Do not chase the cascade. just clippywill lie to you. Use the CI flags.- Build-script
cargo:warninglines (missing benchmarks,freeze_struct) are not Clippy denials. Ignore them. - Pallet
--libClippy misses test modules. Add the crate-level allow shown above, or rewrite the lint.
Symptom: ruff format would reformat query.py (or similar);
codegen.check --names / --coverage fail; pytest fails.
Fix:
cd sdk/python
uv run ruff format .
uv run python -m codegen.check --coverage
uv run python -m codegen.check --names
# after a read-registry change:
uv run python -m codegen.emit_namespacesTraps:
- Do not hand-edit
sdk/python/bittensor/_generated/. Codegen headers have no trailing comma afterSpec version: N. A hand edit of the catalog will fail clone-upgrade even if ruff is clean. - Do not advertise storage that the runtime does not have. Reads for
TotalAlphaStakedor leftoverDeferredRootAlphaDividendsfailed when the chain had no such item. - Do not regen from a local
--dev/pow-faucetnode. That node addsSubtensorModule.faucet. CI's production release binary does not. You will then fail--coverage(raw-only entries no longer on chain: ['faucet']) or the reverse. - Do not use system
ruff. Useuv run ruff. - Do not let a local venv's Python leak into PyO3 / binding builds.
See the pyo3 trap under
cargo test.
Symptom: Docs drift gate (generated tx/query/errors pages) and
a list of stale files. Often docs/tx/claim-root.mdx or line anchors
in reads.json after a Rust edit that shifted source lines.
Fix: regenerate with generate.py, then --check. Commit the
generated pages.
Traps:
- Fmt then regen, or regen then fmt, can leave stale line anchors. Run generate after rustfmt is final. Do not fmt again.
--checkcan print many paths and still exit 0. Read the header and the exit code.- Broad unexpected drift that fails: stop and report. Do not "make CI green" by committing unrelated catalog churn.
- The website build (
yarn turbo run build --filter=@raofoundation/bittensor-website) is a second step. Drift can be clean and the Next build still fail on missing MDXtitle/description.
Symptom: local spec_version in runtime/src/lib.rs is less than
or equal to mainnet.
Fix (only if the user asked): bump to mainnet + 1, or add the
no-spec-version-bump label.
Check mainnet:
curl -sS -H 'Content-Type: application/json' \
-d '{"id":1,"jsonrpc":"2.0","method":"state_getRuntimeVersion","params":[]}' \
https://entrypoint-finney.opentensor.ai \
| jq -r '.result.specVersion'The file must contain exactly one spec_version: <n>, literal.
Trap: do not bump or label unless asked. A bump on a non-release PR fights every other open PR. Two in-flight PRs must not claim the same number. A missing bump on a release PR means the release train builds and then no-ops at every deploy guard.
Sudo-upgrade mainnet clone and test
What it is: CI builds a Linux release node, sudo-upgrades a copy of real mainnet state with your runtime, then runs clone regressions and (when relevant) SDK metadata drift against that upgraded chain.
Phases:
- Snapshot available:
pristineandremainingrun in parallel. - Live scrape or
fresh-mainnet-clonelabel: onecombinedjob. pristinedoes not run the SDK drift gate. It can pass while_generated/is stale.remaining(or the end ofcombined) is the metadata check.remaining/ end ofcombinedrunsuv run python -m codegen.check --drift ws://127.0.0.1:9944when the path classifier setssdk_drift.
The required name is Sudo-upgrade mainnet clone and test.
That is a fan-in job. If a sibling clone job is failure, this
gate dies in about 3 seconds with CLONE: failure. That is not a
second product bug. Open the inner clone-upgrade (remaining) log.
If clone is skipped because the build failed, the gate is red.
If clone is skipped because of skip-clone-upgrade or a docs-only
PR, the gate is green. GitHub treats skipped as a pass. That
label hides the check; it does not fix drift.
Common real failures:
-
Generated SDK drift. Committed
_generated/does not match the upgraded production runtime. Regen from the CI Linux release node, not a Mac--devnode:gh run download <run-id> -n 'node-subtensor-release-<sha>' -D /tmp/release-node docker run -d --name st-meta --platform linux/amd64 -p 9944:9944 \ -v /tmp/release-node:/node:ro ubuntu:24.04 \ bash -c 'apt-get update -qq && apt-get install -y -qq ca-certificates >/dev/null && \ exec /node/node-subtensor --chain local --tmp --alice --validator \ --rpc-external --rpc-cors all --rpc-methods unsafe --rpc-port 9944 \ --unsafe-force-node-key-generation' # Confirm specVersion == this PR before codegen. A stale # scripts/specs/local.json can still serve an OLD genesis. curl -sS -H 'Content-Type: application/json' \ -d '{"id":1,"jsonrpc":"2.0","method":"state_getRuntimeVersion","params":[]}' \ http://127.0.0.1:9944 | jq -r '.result.specVersion' cd sdk/python uv run python -m codegen ws://127.0.0.1:9944 uv run python scripts/record_golden.py ws://127.0.0.1:9944 uv run python -m codegen.check --drift ws://127.0.0.1:9944
--devis safe only if the binary is that CI release artifact (nopow-faucet). A local Mac--devbuild is thefaucettrap. -
Hand-edited
_generated/. Trailing commas, extra storage items, leftoverfaucet, or merge-conflict leftovers. Re-run codegen. Do not patch the generated files by eye. -
Regression assert. Behavior on real mainnet state changed (locks conviction after
transferStake, balancer, emission). Reproduce withdocs/internals/mainnet-clone.mdx. Run the named script inclones/js-tests(npm run test:locks-conviction, …). A cancelled run is not a pass. -
Cancelled sibling / 3-second gate.
--fail-fast, a cheap job, or a new push cancelled clone-upgrade. Fix the cheap job or wait for the new HEAD. Do not debug clone. -
Warp-sync infra. Log says
Bad warp prooforDownloading finality proofs, 0.00 MiB. Nightly snapshot is stale and live scrape failed. Rerun--failedup to three times. Do not applyskip-clone-upgradeunless asked.
Traps:
- Do not regenerate
_generated/by hand. You need a node on the new spec; the fullscripts/preflight.shbuilds one from this tree and regenerates for you. A MacSKIP_WASM_BUILD=1tree cannot produce that node. - Do not regen from
--dev/pow-faucet. That addsfaucet. - Do not add
skip-clone-upgradeto hide a runtime/SDK change. - Apple clang on this machine often cannot target wasm32. Leave full runtime wasm and the release node to CI.
Local clone steps (slow; only when clone is the real failure):
see docs/internals/mainnet-clone.mdx.
What it is: replay on_runtime_upgrade against a network state
snapshot. Cached snapshots are the default. Label
fresh-try-runtime-state to scrape live.
When it fails: a migration panics on real storage, or a new
migration has no pre_upgrade / post_upgrade. Unit tests will
not catch this. Put the migration in the runtime Migrations
tuple and add those hooks.
If all three networks go red together, look for a compile break first (a vendored crate), not three migration bugs.
Local Mac cannot build the try-runtime wasm. Leave it to CI.
What they are: required AI review jobs. skeptic is read-only.
auditor may run scripts/fix_rust.sh and push chore: auditor auto-fix
on non-fork PRs.
Read the verdict text, not the red X. Findings live in the PR
comments and the skeptic-output artifact. The job log is often
empty.
gh api repos/RaoFoundation/subtensor/issues/<N>/comments
gh run download <run-id> --repo RaoFoundation/subtensor -n skeptic-outputRecurring verdict: VULNERABLE on weight accounting. The bot
flags dispatchables whose weight does not pay for new loops or
storage growth (BasketClaimed, per-hotkey drains, dissolve
cursors, root_register walking every subnet).
When to fix:
- New unbounded walk or unpaid work this commit added.
- Real economic loss (for example recycled credits on eviction).
- Secrets inherited by
npm install, or unbounded PR archive extract. Do not waive those. Auto-merge stays stuck untilSAFE.
When not to thrash:
- Same HIGH weight findings already on the PR, unchanged. Docs regen and rustfmt do not create them.
- HTTP 406 / “PR exceeds 300 files” / merge-base missing: infra.
The workflow reads prefetch scripts from the base branch.
A
prefetch.shfix on the PR head does nothing until it is onmain. Do not rewrite pallet code for a 406. - Fork PRs fail on auto-trigger. A nucleus member must
gh workflow run ai-review.yml -f pr_number=<N>. - User said "green ignoring skeptic": keep fixing every other required check. Do not invent weights.
- Auditor 👎 “missing tests” or a docs wording nit is
informational unless that check itself is required. Skeptic
can be
SAFEwhile Auditor is 👎.
Do not invent WeightInfo numbers. CI measures on reference
hardware (threshold cited: 40%) and uploads a bench-patch
artifact. Label apply-benchmark-patch commits that patch, or
download it and apply it yourself. Label run-benchmarks turns
on validate-benchmarks (not required today).
Symptom: checkout download 429, or a real wasm compile break.
Fix: if the log is GitHub rate limit, gh run rerun <id> --failed.
If the log is a compile error in bittensor-core / bittensor-core-wasm,
fix the portable core. This job exists because Clippy on native does
not link the wasm target.
Mac: skip this locally. CI builds it on Linux.
Formatting: cd ts-tests && pnpm run fmt. Required even when e2e
shards skip.
E2E flakes (rerun once):
- GHCR pull
denied: requested access to the resource is denied - GHCR push
unknown blobafter a successful build - GitHub checkout
429 intent_multisig_cancelor stake-movement amounts that pass on siblings
gh run rerun <run-id> --failedIf the same test fails twice on this commit, it is a real regression.
Real e2e bugs (fix the test or the step):
NextKeyisNone~17–20s after the first block → wait/retry in the test. Not aLOCALNET_IMAGErename.find-e2e-testsexpected count drifted after a test was removed (for example 113 → 112). Update the count. Do not add a dummy test.- Workflow still calls a deleted Python e2e path
(
pytest tests/e2e/test_reads.pyfile not found). Delete or retarget the step. Keep the metadata drift gate.
Traps: ignore a LOCALNET_IMAGE / LOCALNET_IMAGE_NAME hint
when 98/100 tests already hit the chain. Read the real assertion.
Label skip-bittensor-e2e-tests skips the Rust SDK e2e suite.
Label skip-cargo-audit skips it. Do not add advisory ignores just
to go green. Do not refresh Cargo.lock unless the lockfile change
is the task.
Leave to CI unless you know the failing test name. Then:
SKIP_WASM_BUILD=1 cargo test -p pallet-subtensor <test_name>CI uses cargo nextest run --workspace --all-features plus
cargo test --doc --workspace --all-features.
py-sp-core / PyO3 traps:
-
undefined referencetoPy_*under--all-featuresis the crate featureextension-module. Remove that crate feature. Maturin already setsfeatures = ["pyo3/extension-module"]. Do not exclude the crate from--all-features. -
“Need
python3-dev” is often a misread. Fix the first compile error. py-sp-core may never have been reached. -
After
source .venv/bin/activate, cargo/pyo3 can cache the venv’spython3.10.deactivatein a subshell is not enough:unset VIRTUAL_ENV # take .venv off PATH, then: hash -r cargo clean -p pyo3-ffi
Rebuild with system Python. This is local. CI Linux is usually fine.
Use a label only when the user asks, or when the failure is the label's own gate.
| Label | Effect |
|---|---|
no-spec-version-bump |
Skip spec_version vs mainnet |
skip-clone-upgrade |
Skip clone-upgrade; required fan-in still passes |
fresh-mainnet-clone |
Live scrape; one combined job |
fresh-try-runtime-state |
Live RPC instead of cached try-runtime snap |
skip-cargo-audit |
Skip audit |
skip-bittensor-e2e-tests |
Skip Rust SDK e2e |
run-benchmarks |
Run validate-benchmarks on the PR |
apply-benchmark-patch |
Apply the bot's weights.rs patch |
check-node-compat |
Opt-in node compat harness |
mainnet-clone |
Public tunneled clone (non-fork, base main) |
auditor:run-node |
Auditor may start localnet (heavy) |
red-team, blue-team, runtime, breaking-change do not skip checks.
Rerun once when the log is one of these:
- GitHub
429on checkout (wasm32 seam, other jobs) - GHCR pull
denied/error pulling image configuration - GHCR push
unknown blobafter a green build Bad warp proof/Downloading finality proofs, 0.00 MiB- Job
cancelledbecause a cheaper sibling failed,--fail-fast, or a newer push - Skeptic HTTP 406 / “PR exceeds 300 files” / empty verdict JSON
gh run rerun <run-id> --failedIf it fails the same way on the same commit, treat it as real.
- Run
scripts/fix_rust.sh. It commits. - Push without running
scripts/preflight.sh, or push with--no-verify. - Regenerate
_generated/without a node on this spec. - Hand-edit generated docs or
_generated/. - Bump
spec_versionor apply labels without being asked. - Invent benchmark weights.
- Refresh a lockfile to "make CI pass".
- Merge latest main into the PR to hide an unrelated red check without checking that main is actually green for that check.
- Debug clone-upgrade when
cargo fmtis the first failure. - Treat
just clippyas CI Clippy. - Push fmt/clippy that you never committed.
- Push again to “unstick” CI (cancels 40+ min of clone/try-runtime).
- Dump a broad
generate.pyregen when--checkwas already stale from other WIP. - Use system
ruffinstead ofuv run ruff. - Regen
_generated/from a--dev/ faucet node. - Treat
pristinegreen as “SDK metadata is fine.” - Treat skeptic 406 as a pallet bug. Prefetch is read from base.
- Add
python3-devfor a compile error that never reached py-sp-core. - Exclude
py-sp-corefrom--all-featuresinstead of removing theextension-modulecrate feature. - Chase
LOCALNET_IMAGEwhen the container already ran tests. - Assume an admin can force-merge
main.enforce_admins: truestill needs reviews and required checks.
# Local gate (before every push; the pre-push hook runs it for you)
scripts/preflight.sh --fast
scripts/preflight.sh # full: node build + SDK drift + try-runtime when relevant
# Where are we?
gh pr view --json number,url,headRefOid,mergeStateStatus
gh pr checks --json name,bucket,state,link
# Watch
gh pr checks --watch --fail-fast
# Logs
gh run view <run-id> --log-failed
gh run download <run-id> -n skeptic-output
gh run download <run-id> -n 'node-subtensor-release-<sha>' -D /tmp/release-node
# Rerun infra (after the run has finished)
gh run rerun <run-id> --failed
# Fast Rust
cargo fmt --check --all
cargo fmt --all # only after check fails on owned files
# CI Clippy (when that job is red)
SKIP_WASM_BUILD=1 cargo clippy --workspace --all-targets -- -D warnings
SKIP_WASM_BUILD=1 cargo clippy --workspace --all-targets --all-features -- -D warnings
# Fast Python (sdk/python) — use uv run, not system ruff
uv run --no-sync ruff check .
uv run --no-sync ruff format --check .
uv run --no-sync python -m codegen.check --coverage --names
uv run --no-sync python ../../website/apps/bittensor-website/scripts/generate.py --check
# Regen bindings only after a Linux release node on THIS spec is up
# uv run python -m codegen ws://127.0.0.1:9944
# Fast TS (ts-tests)
pnpm run fmt
# Zepter (when that job is red)
zepter run check
# fix: zepter run default # inspect the diff; do not use fix_rust.shRuntime Checks uses .github/scripts/classify-runtime-changes.sh
on the trusted base SHA. Outputs: runtime, docs, python_sdk,
sdk_drift, snapshot_ci.
docs=true for docs/*, website/*, sdk/python/*, and
.github/workflows/runtime-checks.yml. That is why an SDK-only
change still runs the docs drift gate.
Check Rust uses .github/scripts/classify-rust-changes.sh and
.github/rust-ci-paths.txt. Non-Rust PRs get placeholder Clippy
jobs so the required names still exist.
If the classifier is missing or the PR file list fails, the workflows enable every check. That looks like "why is clone running on my docs typo?"
Local preflight sets SKIP_WASM_BUILD=1 (root justfile does this
too). CI builds real runtime wasm and a Linux node-subtensor on
self-hosted Linux.
On this machine, Apple clang often cannot target wasm32. That is environment, not your diff. Do not spend time on a local wasm runtime build to "match CI".
Clone-upgrade and try-runtime need that Linux artifact. Reproduce them only after the cheap gates are green and the log shows a real migration, regression, or metadata-drift failure.
For PR #3206, the owner explicitly chose a shared Null capacity of 2,500 UIDs (divided by emission mechanism count) and accepted Skeptic as the sole allowed non-green review check. Keep its result and findings visible; do not suppress the check or describe it as passed. All other CI checks and the mandatory local preflight/pre-push gate must pass. Further reward-accounting optimizations are deferred; payouts remain atomic. This exception does not supply measured weights or establish that deployment resource limits have passed.
Competitive registration mining refreshes the latest published block challenge on a 12-second cadence. Chain admission accepts work with age 1 through 5 blocks; transaction-pool longevity reflects the remaining lifetime. The miner must not restart or poll the chain after every small GPU batch. A found proof is checked against the latest difficulty before submission. Preserve key/subnet binding, replay rejection, disjoint GPU nonce ranges and bounded cancellation.
Verify the 12-second refresh with a deterministic clock, exact accepted/rejected age boundaries, difficulty changes during mining, and live GPU registration on the rebuilt runtime. Delay a broadcast beyond the buffer to exercise bounded fresh-proof recovery; do not replay included or uncertain transactions. Raising the CLI mining timeout alone does not change on-chain proof expiry.
If the local Python-binding Rust tests fail to link -lpython3.12, check that
the interpreter selected by PyO3 has its shared development library installed.
Select an installed managed interpreter with that library using
PYO3_PYTHON=/path/to/python3.12 scripts/preflight.sh, and preserve that setting
for the pre-push hook. This is a local toolchain issue; do not disable the tests
or change the binding code to hide it.