This is the per-check contract for cb-verify: what each check asserts, its tier, its
pass/warn/fail/skip conditions, and where its data comes from. It is meant to be read by both a
human and a CI/agent consumer of the verdict. Facts here are sourced from the code
(src/checks/*.rs, src/report.rs, src/main.rs); when in doubt, the code wins.
A run emits a VerificationReport (src/report.rs): an enclave, a timestamp, an
observation_window, an overall result, and a list of per-check CheckResults. Each
CheckResult (src/checks/mod.rs:26) carries an id, a tier (u8), a result (serialized
name of CheckStatus: PASS / FAIL / WARN / SKIP), a detail string, and a data object.
Tiers = severity contract:
| Tier | Meaning | Effect on exit code / overall result |
|---|---|---|
| 1 | must — a real pipeline invariant | A tier-1 FAIL fails the whole run |
| 2 | should — health signal | Never fails the run; annotative |
| 3 | informational | Never fails the run; annotative |
The crucial contract — only a tier-1 FAIL is fatal. report::exit_code
(src/report.rs:131-145) returns:
2if no tier-1 checks ran at all (setup/discovery failure),1if any tier-1 check isFAIL,0otherwise.
The overall report.result is computed the same way (src/main.rs:559-571): it is FAIL iff some
tier-1 check is FAIL, else PASS. WARN and SKIP are non-fatal and annotative — they never
change the exit code and never change the overall result, at any tier.
Consequence a consumer MUST internalize: several checks that exist specifically to catch an
anomaly report that anomaly as WARN, not FAIL (relay equivocation in payload_hash_match,
unverifiable routing in mux.routing, a best-bid shortfall in relay.best_bid). A run in which
those fire still exits 0. A CI job or agent that trusts only the process exit code will call a
misbehaving pipeline green. Parse the JSON and inspect each check's result field — do not gate
on the exit code alone. If you want any of these WARNs to be fatal, that is a policy decision that
has to be made explicitly (see the P3 notes and Known gaps below); today they are not.
One class of WARN is not an anomaly at all, it is a failure to measure. A Law 3 feature check
that arms a differential and then observes nothing has proved nothing about the feature, yet tier-1
WARN is non-fatal, so the scenario exits 0 and a sweep counts it as a win. Those checks now carry
"inconclusive": true in their JSON, and --require-feature-proof makes a tier-1 inconclusive
check exit 1.
The flag is off by default, so the contract above is unchanged for existing callers. Turn it on in sweeps. The three sites it covers:
| check | inconclusive when |
|---|---|
feature.<marker> |
enabled in CB config, ZERO proof markers in CB debug logs |
feature.skip_sigverify |
differential ARMED (wrong-pubkey relay url) but zero auction winners |
feature.min_bid |
floor set but ZERO bids rejected |
Deliberately not marked: feature.skip_sigverify in a plain (unpoisoned) scenario. That is a
negative codepath which emits nothing when it fires, so it is structurally unconfirmable rather than
unmeasured, and marking it would turn every scenario carrying skip_sigverify permanently red.
Relay equivocation in payload_hash_match also stays a plain WARN: it is a real observation, not
the absence of one.
One escalation exists: the cb_metrics matrix checks are authored at tier 2 but are promoted to
tier 1 when they FAIL (src/checks/cb_metrics.rs:636-641), because a 5xx from a relay is a real
pipeline failure. So a matrix 5xx does gate the exit code even though the check's nominal tier is 2.
Setup / preflight failures (bad args, discovery failure, no beacon node, dead services, chain
never reached the window) short-circuit before the check phase and produce a single synthetic tier-1
FAIL check (setup), exit code 2 (src/main.rs, make_error_report).
Checks split by where their evidence comes from, which determines how they behave when a service dies mid-run:
- CB-log-based / kurtosis-inspect-based — robust to relay death. Evidence is in the CB
container logs or
kurtosis enclave inspect, which survive a relay crash:cb_running,mux.routing, and the offered-bid half ofrelay.best_bid. - CB Prometheus metrics — robust to relay death, but usually absent.
cb_*_matrix,cb_v2_fallback,cb_relay_latency. These SKIP wholesale unless CB was configured to expose metrics; the default kurtosis PBS mode does not set metrics config, so they SKIP by default. - Relay data-API-based — fragile. If the relay dies before check time these
FAILorSKIP:relay.builder_blocks_received,relay.payloads_delivered_multi,relay.mev_delivery_rate,relay.validator_registrations,payload_hash_match, and the delivered-value half ofrelay.best_bid.run_relay_checkspings each relay first and emits a singleSKIPper downstream check when all relays are unreachable (rather than a pile of request errors). - Beacon-API-based.
chain_finality,sync_status,missed_slots, plus the on-chain half ofrelay.mev_delivery_rateandpayload_hash_match.
| id | tier | source | asserts |
|---|---|---|---|
chain_finality |
1 | beacon API | finalized epoch ≥ 2 (conditionally run) |
sync_status |
1 | beacon API | beacon node is done syncing |
cb_running |
1 | kurtosis inspect | ≥1 commit-boost service is running |
missed_slots |
2 | beacon API | missed-slot rate < 10% over the window |
relay.payloads_delivered_multi |
1 | relay data API | ≥1 payload delivered across relays |
relay.builder_blocks_received |
2 | relay data API | ≥1 builder block received by a relay |
relay.mev_delivery_rate |
2 | relay data API + beacon | MEV-delivered block fraction ≥ threshold (0.30) |
relay.validator_registrations |
3 | relay data API | validators are registered with the relay |
payload_hash_match |
1 | relay data API + beacon | relay-delivered hashes match on-chain, no cross-relay conflict |
relay.best_bid |
2 | CB logs + relay data API | CB delivered ≥ the best per-relay bid it was offered |
mux.routing |
1 | CB logs | every checked getHeader routed per the [[mux]] config |
feature.timing_games |
1 | CB logs | timing-games codepath fired (≥1 TG: debug line); config-gated |
feature.extra_validation |
1 | CB logs | extra-validation codepath fired (≥1 parent-block fetch); config-gated |
feature.min_bid |
1 | CB logs | the min_bid_eth floor dropped bids; FAIL if a winner is under it; config-gated |
feature.skip_sigverify |
1 | CB logs | skip-sigverify fired (differential: wrong-pubkey relay + auction winners); WARN in plain scenarios |
signer.pubkeys |
1 | CB signer API | the signer loaded the devnet's validator keys (JWT-authed count); config-gated |
cb_get_header_matrix |
2 → 1 on FAIL | CB Prometheus | get_header status-code distribution healthy |
cb_register_validator_matrix |
2 → 1 on FAIL | CB Prometheus | register_validator acceptance healthy |
cb_submit_blinded_block_matrix |
2 → 1 on FAIL | CB Prometheus | ≥1 blinded-block delivery (200/202) |
cb_status_matrix |
2 → 1 on FAIL | CB Prometheus | status endpoint answering 200 |
cb_relay_v2_unsupported |
2 → 1 on FAIL | CB Prometheus | no v2 submit_block lost to a relay 404ing the v2 route |
cb_v2_fallback |
2 | CB Prometheus | no v2→v1 submitBlindedBlock fallbacks |
cb_relay_latency |
2 | CB Prometheus | p95 relay latency < 500 ms |
Note: relay.validator_registrations (tier 3) is only added to the report when active validator
pubkeys were successfully fetched; if the pubkey fetch fails, the check is omitted entirely (it
does not even SKIP). cb_v2_fallback and cb_relay_latency are produced by the metrics phase but
were not part of the original catalog request; they are included here for completeness.
Asserts the beacon chain has finalized past epoch 2. Source: beacon.get_finalized_epoch().
- PASS — finalized epoch ≥ 2.
- FAIL — finalized epoch < 2, or the beacon query errored.
- SKIP — the surrounding
run_chain_health_checksskips this check (and injects a tier-1 SKIP) when--skip-finalization-checkis set, or when the observation window ends before slot 96 (epoch 3), because the justification cascade has not had time to finalize epoch 2 yet. - No WARN state.
Asserts the beacon node is not syncing. Source: beacon.is_syncing().
- PASS — node reports not syncing.
- FAIL — node still syncing, or the query errored.
- No WARN/SKIP.
Asserts commit-boost is actually up. Source: kurtosis enclave inspect <enclave>, grepped
case-insensitively for the service pattern commit-boost and the word running.
- PASS — ≥1 matching service line also contains
running. - FAIL — matching services exist but none are
running; or no matching services found; or thekurtosisCLI errored / returned non-zero. - No WARN/SKIP.
Asserts the miss rate over the window is under threshold. Source: beacon.get_header(slot) for each
slot in [start, end); a None header or an error counts as missed. Threshold is hardcoded to
0.10 by run_chain_health_checks.
- PASS — miss rate < 10%.
- WARN — miss rate ≥ 10%.
- SKIP — single-slot window (
start == end): no interior slots to measure, so it deliberately SKIPs rather than PASS on zero data. - FAIL — inverted range (
start > end), treated as nonsense input.
Asserts the MEV pipeline delivered at least one payload. Source: get_payloads_delivered(start,end)
across all live relays, unioned by slot.
- PASS — ≥1 delivered payload (across any relay).
- FAIL — zero delivered payloads across all relays.
- SKIP — all relays unreachable at check time (fragile-source SKIP from
run_relay_checks); OR an enforcingmin_bidfloor explains the zero delivery — whenfeature.min_bidPASSes (every bid rejected below the floor), zero delivery is the expected outcome, soreconcile_min_bid_deliverydowngrades this FAIL to SKIP. Keyed on positive evidence only: a dead pipeline leavesfeature.min_bidinconclusive, so a genuine delivery failure still FAILs.
Observation-window note: the delivery check measures over [start, end]. The window must span a full
epoch — a single-slot window (e.g. the orchestrator's old --min-epochs 0, [head, head]) makes this
check pass or fail on whether one payload happened to land in that exact slot. Use --target-epoch 1 --min-epochs 1 (or higher) for a meaningful window.
Asserts a relay received builder blocks. Source: get_builder_blocks_received(slot) sampled at ~10
slots across the window (the data API requires a filter param, so it samples). Aggregated across live
relays: PASS if any relay received blocks.
- PASS — ≥1 builder block received by at least one relay.
- FAIL — no builder blocks at any sampled slot on any relay.
- SKIP — all relays unreachable at check time.
Asserts a healthy fraction of on-chain blocks came from the relay. Source: delivered block hashes
from the first relay whose data API responds, intersected with on-chain block hashes
(beacon.get_block_hash(slot)) over the window. Threshold is --mev-threshold (default 0.30).
- PASS —
mev_blocks / total_blocks≥ threshold. - WARN — rate below threshold.
- FAIL — no proposed (on-chain) blocks found in the window (
total_blocks == 0). - SKIP — no relay supports the data API, or all relays unreachable.
Asserts validators are registered with the relay. Source: GET /relay/v1/data/validator_registration?pubkey=… for each active validator pubkey, per live relay,
aggregated to the worst status.
- PASS — all pubkeys registered.
- WARN — some registered, some missing.
- FAIL — none registered (
0/total) and the relay did not deliver payloads. - SKIP — pubkey list empty, or all relays unreachable (if the upstream pubkey fetch failed
entirely, the check is omitted rather than SKIPped); OR
0/totalfrom the data API but the relay delivered payloads (relay.payloads_delivered_multiPASS). A relay only delivers to registered proposers, so a0/totalwith confirmed delivery is a data-api reporting artifact (a submodule-builtdevelophelix answers this query from an unpopulated postgres while the in-memory cache the admission path uses is populated), not a registration failure.
Cross-checks each relay's delivered block_hash against the on-chain hash per slot, and detects
cross-relay disagreement. Source: per-(relay, slot) delivered hashes (kept un-deduped, on purpose) +
beacon.get_block_hash(slot).
- PASS — every observed slot has a relay hash matching the on-chain hash, and no slot has two relays reporting divergent hashes.
- WARN — any slot where no relay hash matched the on-chain hash (
mismatched > 0, possible reorg) or any slot with a cross-relay hash conflict (cross_relay_conflicts > 0, relay equivocation). The offending slots and relays are named indata. - SKIP — no delivered payloads to compare (this check explicitly defers the "was anything
delivered" signal to
relay.payloads_delivered_multirather than PASS on zero comparisons). missed(a delivered slot with no on-chain block) is counted but does not downgrade the verdict — informational only.
Asserts CB delivered at least the best bid it was actually offered across relays (aggregated
bidding). Offered bids come from CB's own received new header getHeader log events (relay_id,
slot, value_eth parsed decimal→wei exactly, no float) — the bids CB itself compared. Delivered
values come from the relay data API (the winning payload's value). Comparison is exact wei vs exact
wei.
- SKIP — fewer than 2 relays: no cross-relay aggregation is possible to verify.
- WARN (not exercised) — no slot had ≥2 distinct relays offering bids; aggregation never happened, so nothing is asserted.
- WARN (not verified) — competitive slots exist but none had a delivered payload to compare against (out of window / missed slot); the Law-3 guard against greening having compared nothing.
- WARN (suboptimal) — a verified competitive slot delivered less than the best offered bid
(value left on the table — late, rejected, or ineligible header). The slots are named in
data. - PASS — ≥1 verified competitive slot, and every one delivered ≥ its best offered bid.
Asserts CB routed each getHeader to the mux/relay the [[mux]] config specifies. Only runs when
--config was given and the CB config contains [[mux]] sections. Source: CB PBS container logs
(using mux config DEBUG events carry mux_id + validator; a routing decision is only "verified"
when a known pubkey is seen with its mux_id).
- PASS — ≥1 routing decision was actually checked and all checked decisions routed to the expected mux.
- FAIL — a checked decision routed a pubkey to the wrong mux/relay (misrouting). Details name the pubkey, the routed vs expected mux and relay.
- WARN (no events) — no mux-related log lines at all; routing could not be observed.
- WARN (nothing verified) — mux log events exist but zero routing decisions were verifiable
(
routing_decisions_verified == 0), typically because CB debug logging is off so there is nousing mux configline. Requires[logs.stdout] level = "debug". - SKIP — no
[[mux]]entries to verify. (A config that fails to parse produces a tier-1 FAIL frommain.rs, not a SKIP.)
One check per CB feature the --config enables, proving the feature's codepath actually fired at
runtime (not just that generic health passed). Source: CB PBS debug logs (the toggle scenarios set
[logs.stdout] level = "debug"). Detected by scanning the CB config template for <key> = true.
Each is emitted only when its feature is enabled — an off feature produces no check at all.
feature.timing_games(enable_timing_games) — PASS on ≥1TG:debug line (send_timed_get_header), else WARN (enabled but unobserved — maybe no getHeader in the window).feature.extra_validation(extra_validation_enabled) — PASS on ≥1fetched parent block/fetching parent blockline, else WARN.feature.skip_sigverify(skip_sigverify) — a negative codepath (sigverify simply not called; no success log or metric), indistinguishable from OFF on the happy path — so in plain scenarios it stays an honest WARN. The cb-sigverify-diff scenario arms a real differential: CB's[[relays]]url carries a valid-but-WRONG pubkey (a mnemonic validator key, not helix'sDEFAULT_MEV_PUBKEY), sovalidate_signaturewould reject every bid (PubkeyMismatch) — with the poison detected and ≥1 "auction winner" in CB logs (winners are post-validation), the check PASSes: bids winning is only possible if the skip fired.cb-sigverify-diff-control(same poison, skip OFF) is the expected-FAIL control arm;sim diffbetween the two runs shows the flip.
A marker feature enabled-but-unobserved is WARN, never FAIL (no-false-red — the same discipline as
mux.routing). All three are non-fatal (only a tier-1 FAIL fails the run).
Emitted only when the CB config sets min_bid_eth > 0. Counts CB's bid below minimum rejections
(ValidationError::BidTooLow) and the value_eth of every auction winner.
- FAIL — any auction winner's value is BELOW the floor. That can only happen if the floor was not
applied, and it is the definitive falsifier.
[pbs]has nodeny_unknown_fields(it must#[serde(flatten)]PbsConfig), so a renamed or misspelled key there is silently ignored rather than rejected - this check is the canary for that whole class. - PASS — ≥1 rejection and no sub-floor winner.
- WARN — nothing rejected: cannot distinguish "the key was ignored" from "every bid legitimately cleared the floor", so no false red.
The scenario must run with the builder subsidy OFF. With mev_builder_subsidy: 1 real bids land
near 1.04 ETH, and CB validates min_bid_wei < 1 ETH, so no LEGAL floor could ever reject one and the
scenario would silently prove nothing. cb-min-bid therefore sets subsidy 0 (bids ≈ 0.04 ETH of
spamoor MEV) against a 0.5 ETH floor.
Emitted only when discovery finds a cb-signer-* service. Mints an HS256 module JWT and calls
GET /signer/v1/get_pubkeys, asserting the KEY COUNT against the devnet's active validator set.
- PASS — the signer loaded every expected key and authenticated the module JWT.
- WARN — a partial load: CB warns and continues per keystore, so some were skipped.
- FAIL — zero keys, or a non-200 answer. Zero is this feature's signature failure: CB's
keystore loaders are
filter_map+warn!, so an unreadable mount yields a perfectly healthy signer holding nothing. The detail names the likely cause (the devnet'ssecrets/dir is mode 600 and root-owned; theteku-keys/teku-secretspair is the readable one).
Why not /status. It is Ok(StatusCode::OK) with no logic — 200 with zero keys loaded — and the
metrics server exposes a second unconditional /status, so probing the wrong port is an even
emptier green. The startup log's loaded_consensus=N is log-only (the signer registers exactly one
metric, signer_status_code_total, with no key-count gauge) and is ANSI-colored, so the field is not
a contiguous substring. One JWT-authed get_pubkeys subsumes liveness, module registration, auth and
key loading.
Four checks, one per endpoint, built from CB's status-code counters
cb_pbs_relay_status_code_total (codes CB received from relays, the source of truth) and
cb_pbs_beacon_node_status_code_total (codes CB returned to the CL, surfaced for cross-boundary
diagnosis). Codes bucket into 200 / 202 / 204 / 4xx / 5xx / timeout / transport / other. timeout is CB's
synthetic code 555 (TIMEOUT_ERROR_CODE) and transport is its code 556 (TRANSPORT_ERROR_CODE,
introduced with WS get_header streaming: connect refused / dns / tls / stream broke) — neither is a
relay-served status; 555 is CB cancelling its own
request at its deadline; it must never count as relay 5xx (live-confirmed 2026-08-03: timing-games
produced 42% 555s with ZERO real relay 5xx, and the old bucketing tier-1-failed the run). Metrics are
fetched over HTTP, falling back to kurtosis exec; if neither works (the usual case — default
kurtosis PBS mode sets no metrics config), all matrix checks plus cb_v2_fallback and
cb_relay_latency SKIP.
Shared rules across all four: relay-side 5xx FAILs when it exceeds 25% of COMPLETED responses
(timeouts excluded from the denominator, so a real error storm still fails amid heavy timeout
polling); at or below the rate it's a transient-warmup WARN, promoted to FAIL under --strict.
CB client-side codes (555 timeouts + 556 ws transport errors) above 25% combined → WARN, never FAIL
— not even under --strict (client-side
deadline policy, e.g. timing-games cancelling late polls by design, or a slow relay). Any matrix FAIL
is escalated from tier 2 to tier 1 so it gates the exit code. No samples for the endpoint → SKIP.
cb_get_header_matrix— PASS if any 200 (bids delivered, timeout count noted); WARN if only 204s (relay alive, no bid — promoted to FAIL under--strict); FAIL if only 4xx.cb_register_validator_matrix— PASS if 200s and zero 4xx (100% accepted); WARN if a mix of 200 and 4xx (some batches rejected — normal early on; the beacon-side 502 translation is surfaced); FAIL if only 4xx; SKIP if no registrations observed; FAIL on any 5xx.cb_submit_blinded_block_matrix— judged on the BEACON side (what CB returned to the CL), not the relay side. PASS if CB served any (200 + 202) to the beacon node; FAIL on a beacon-side 5xx (the proposer did not get its payload); WARN on zero deliveries. Relay-side codes are reported as context only. Why the exception: CB asks EVERY configured relay for the payload, but only the auction winner has it, so the losing relays answer 4xx/5xx by construction. On a 2-relay run with divergent subsidies (one relay wins every slot) that produced a 29.7% relay-side 5xx rate and FAILED a run which delivered 65/65 payloads with 100% MEV rate and 0 missed slots, while the beacon side was 220x 202 with zero failures. The discriminator still catches the real failure: on nethermind+prysm the beacon side was 26x 5xx (CB returning 502 to the CL). Falls back to relay-side logic when no beacon-side samples exist.cb_status_matrix— PASS if any 200; SKIP if no 200s; FAIL on any 5xx.
Reads pbs_submit_block_v2_unsupported_total{relay_id}, which CB increments when a relay 404s the
v2 submit_block route. CB deliberately does NOT downgrade to v1 there (in v2 the relay publishes
the block after an empty 202, so forwarding a v1 payload would be silently dropped by the beacon
node), so every affected submission is LOST and the slot is typically missed.
- PASS — counter zero or absent (Prometheus omits never-incremented families).
- FAIL (escalated to tier 1) — any nonzero count, naming the relay(s).
Read a FAIL as a relay CONFIG problem first. Found live on nethermind+prysm (2026-08-04): prysm
submits to /eth/v2/builder/blinded_blocks, helix 404'd it, and every builder block was lost (11
events, 11 missed slots) - but helix supports v2 fine; our generated
router_config.enabled_routes simply omitted its GetPayloadV2 route. Lighthouse never triggers this
because it submits via v1.
Asserts relays support submitBlindedBlockV2. Source:
cb_pbs_submit_block_v2_fallback_to_v1_total. A missing counter = never incremented = zero
fallbacks.
- PASS — total fallbacks == 0 (includes the missing-counter case).
- WARN — any v2→v1 fallback; a relay is behind on the builder-specs v2 upgrade. Never FAIL (v1
still works), unaffected by
--strict.
Asserts p95 relay latency under threshold. Source: the cb_pbs_relay_latency histogram, aggregated
across all {endpoint, relay_id} dimensions; standard histogram_quantile at q=0.95. Threshold is
hardcoded to 500 ms.
- PASS — p95 < 500 ms.
- WARN — p95 ≥ 500 ms.
- SKIP — histogram not exposed, zero observations, or degenerate buckets.
Three checks were rewritten (DESIGN Law 3 "a harness that lies green is worse than an ugly one") to kill a false green — a PASS reported while the check had verified nothing. Each now WARNs instead of passing-on-nothing:
mux.routing— the old pass-gate keyed ontotal_events(raw log lines) and PASSed whenever there were no violations, even with zero parseable routing decisions (CB debug logging off). It reported "all N routing decisions verified" where N was log lines, not decisions. Fixed to gate onrouting_decisions_verified; zero verified → WARN, and debug logging is required for mux scenarios.payload_hash_match— the old code was a first-wins union by slot (by_slot.entry(slot).or_insert(hash)) that kept only the first relay's hash and dropped cross-relay disagreement before the on-chain compare, making the verdict order-dependent (honest relay first → false PASS). Fixed to keep every (relay, slot) hash and detect divergence → WARN, naming the slot and relays.relay.best_bid— the old check was a first-wins union that counted distinct delivered slots and never compared bid values across relays, so one delivering relay scored identically to genuine two-relay aggregation. A first rewrite sourced offered bids fromget_builder_blocks_received, but adversarial review found that source unsound (it includes builder submissions that failed simulation and were never offered to the proposer — a false alarm on correct runs), so it was backed out. The shipped version sources offered bids from CB'sreceived new headerlog events (what CB actually compared) and WARNs whenever aggregation was not exercised or no competitive slot could be verified, rather than PASS.
All three land as WARN, which per the verdict model is non-fatal — see the consumer caveat in
the intro.
- Feature-fired assertions (Law 3) — mostly closed. DESIGN Law 3 wants every scenario to
positively assert its feature's codepath fired. Now five checks do:
mux.routing,relay.best_bid, and the config-gatedfeature.timing_games(≥1TG:debug line),feature.extra_validation(≥1 parent-block fetch log). The residual gap isskip_sigverify: it is a negative codepath (signature verification simply not called, with no success log or metric), indistinguishable from OFF on the happy path with a valid-signature mock relay.feature.skip_sigverifytherefore reports an honest WARN ("not runtime-confirmable") rather than a false green. Closing it fully needs a bad-signature-injecting relay in the helix mock (then: ON delivers the bad-sig bid, OFF rejects it). A marker feature that is enabled but unobserved WARNs (could be no getHeader in the window), never FAILs — the no-false-red discipline. relay.best_bidis inert or degenerate in common setups. It SKIPs any single-relay run (no aggregation to verify), and in mux mode — where each mux typically points a validator at exactly one relay — no slot sees ≥2 relays competing, so it WARNs "aggregation not exercised" rather than verifying anything. With an identical two-relay setup (e.g. two helix instances serving the same bid) the comparison is technically competitive but degenerate.- The
cb_*_matrixcounters are cumulative, not windowed. The H2 fix made the 5xx verdict rate-based (FAIL only above 25% of completed responses; below = transient-warmup WARN), and code 555 now buckets astimeout(WARN-only), so neither a warmup blip nor a designed deadline-cancellation fails a run anymore. But the counters still cover the container's whole life, not the observation window — a sustained pre-window error burst can still dominate the rate. True windowing (delta against a baseline scrape, as--live-metricsalready takes) remains future work. - A real anomaly can still exit 0. As stated in the intro: relay equivocation, unverifiable
routing, and best-bid shortfall are all
WARN. Gate on the JSONresultper check, not the exit code, if you care about these. - Metrics are usually absent. The default kurtosis PBS mode does not configure CB metrics, so the six Prometheus-based checks SKIP unless metrics are explicitly enabled.
payload_hash_matchleniency (left as-is under P3 scope). A delivered-but-not-on-chain slot (missed) is indistinguishable from a transient beacon error and never downgrades the verdict; a hash mismatch is WARN, not FAIL.