LeanGuard (Use Guide)
Emit legacy traces and run legacy or exact-executor Lean checkers.
Scope: Days AGO and Legacy Days. This guide builds the Lean checkers, emits Legacy Days traces, and runs the checkers for both engines.
Prerequisites
- Rust and Cargo.
- Lean 4 through
elan, includinglake.
The repository pins Lean in lean/lean-toolchain. Build the executor proof library and every
checker from the repository root:
cd lean
lake build DaysExecutor \
dcqcn_check pfc_check cubic_check tcp_check drr_check wfq_check sp_check \
aqm_check aqm_dcqcn_check \
p10c_mechanisms_check p10c_aqm_check p10c_dcqcn_check p10c_collective_check \
p10c_roce_check
cd ..Emit legacy traces
The legacy engine emits protocol traces with Cargo feature lean. It writes them beneath
log_path and records the non-empty files in log_path/traces.json.
CSV output must be enabled. csv_logging defaults to true; if a config sets
csv_logging = false, the engine creates neither the log directory nor the trace manifest.
The committed CI fixtures provide small deterministic runs:
# DCQCN and AQM (with the PFC-capable link path enabled)
RUST_LOG=info cargo run -p days-legacy --features dcqcn,lean,l2_pfc --bin days-legacy -- \
configs/ci/leanguard_dcqcn.toml
# PFC
RUST_LOG=info cargo run -p days-legacy --features lean,l2_pfc --bin days-legacy -- \
configs/ci/leanguard_pfc.toml
# TCP CUBIC
RUST_LOG=info cargo run -p days-legacy --features lean --bin days-legacy -- \
configs/ci/leanguard_cubic.toml
# DRR
RUST_LOG=info cargo run -p days-legacy --features lean --bin days-legacy -- \
configs/ci/leanguard_drr.toml
# WFQ
RUST_LOG=info cargo run -p days-legacy --features lean --bin days-legacy -- \
configs/ci/leanguard_wfq.toml
# AQM
RUST_LOG=info cargo run -p days-legacy --features lean --bin days-legacy -- \
configs/ci/leanguard_aqm.tomlThe fixture's log_path names the output directory. A trace is useful only when its configured
mechanism executes; the examples above select the matching flow or scheduler.
Simulate and check legacy traces
leanguard-run runs the separately built days-legacy binary, discovers traces from traces.json (or a
known-filename fallback), and invokes the matching checkers:
cargo build -p days-legacy --features dcqcn,lean,l2_pfc --bin days-legacy
cargo run --bin leanguard-run -- \
--config configs/ci/leanguard_dcqcn.toml \
--legacy-runner target/debug/days-legacy \
--checker-dir lean/.lake/build/binRe-check the existing log_path without rerunning the simulator:
cargo run --bin leanguard-run -- \
--config configs/ci/leanguard_dcqcn.toml \
--mode check-only \
--checker-dir lean/.lake/build/binBy default, simulate-and-check refuses threading = "multiple"; use
--allow-nondeterministic only when unordered legacy emission is intentional.
Run checkers directly
After the build above, run these commands from lean/.
Legacy certificate checkers:
./.lake/build/bin/dcqcn_check <path/to/dcqcn_events.csv>
./.lake/build/bin/pfc_check <path/to/pfc_events.csv>
./.lake/build/bin/cubic_check <path/to/cubic_events.csv>
./.lake/build/bin/drr_check <path/to/drr_events.csv>
./.lake/build/bin/wfq_check <path/to/wfq_events.csv>
./.lake/build/bin/aqm_check <path/to/aqm_events.csv>
./.lake/build/bin/aqm_dcqcn_check <path/to/aqm_events.csv> <path/to/dcqcn_events.csv>Days AGO checkers:
./.lake/build/bin/tcp_check <path/to/tcp_events.csv>
./.lake/build/bin/sp_check <path/to/sp_events.csv>
./.lake/build/bin/p10c_mechanisms_check rate <path/to/rate_events.csv>
./.lake/build/bin/p10c_mechanisms_check pfc <path/to/pfc_events.csv>
./.lake/build/bin/p10c_mechanisms_check drr <path/to/drr_events.csv>
./.lake/build/bin/p10c_mechanisms_check wrr <path/to/wrr_events.csv>
./.lake/build/bin/p10c_mechanisms_check wfq <path/to/wfq_events.csv> [<path/to/pfc_events.csv>]
./.lake/build/bin/p10c_aqm_check <path/to/aqm_events.csv>
./.lake/build/bin/p10c_dcqcn_check <path/to/dcqcn_events.csv>
./.lake/build/bin/p10c_dcqcn_check trace <path/to/dcqcn_events.csv> <path/to/dcqcn_cnp_arrivals.csv>
./.lake/build/bin/p10c_collective_check <path/to/collective_events.csv>
./.lake/build/bin/p10c_roce_check receiver <path/to/roce_receiver.csv>
./.lake/build/bin/p10c_roce_check sender <path/to/roce_sender.csv> <path/to/dcqcn.csv> \
--pfc <path/to/pfc.csv> [--horizon-ns <ns>] [<stop_time_ns>]
./.lake/build/bin/p10c_roce_check trace <path/to/roce_sender.csv> <path/to/roce_receiver.csv> \
<path/to/dcqcn.csv> --pfc <path/to/pfc.csv> [--horizon-ns <ns>] \
[--collective <path/to/collective.csv>] [<stop_time_ns>]p10c_roce_check reads one sender format: the full P16 sender schema, every column the
executor's roce_sender_transitions_csv writes (class_paused, data_class, the window columns,
maximum_rate_bps and initial_rate_bps included). It rejects a log that lacks any of them and
names the missing columns. No older sender layout is read; the committed fixtures are all in this
format.
p10c_roce_check certifies RoCE queue pairs from the executor's roce_receiver_transitions_csv,
roce_sender_transitions_csv and dcqcn_transitions_csv of one full-observation run:
-
receiverchecks each data arrival: the notification point, the in-order frontier, the ACK cadence, duplicates, and the NACK rate limit. -
senderchecks each pacing tick, ACK, NACK and timeout. It needs the DCQCN log of the same run, because the controller's rate decides the sender's status. A queue pair's controller has no events of its own: each of its transitions happens inside a sender transition, and its DCQCN row carries that sender row's event key. The checker requires a DCQCN row at a sender row exactly when the row is an ACK or NACK withinput_ce_echo = 1on a pair that is not yet complete (afeedbackrow), the ACK that completes the pair (anadvancerow withfrozen = 1; the controller then changes no more), or a row before whose bound a rate increase or cut is due (anadvancerow). A tick'srate_bpsmust equal the controller's rate after every instant due beforetime_ns + 1, and a window-blocked tick (window_blocked = 1, the pair's unacknowledged bytes at or above its window) sends nothing, adds no credit and parks. It also checks that every pending pacing tick and timeout fires before any later event of the pair. -
Host-link PFC (schema Amendments 1-3). A tick with
class_paused = 1found the pair's data class (data_class) paused on its host: it must send nothing, add no credit and park the pacer. Aresumerow records a PFC RESUME restarting a pacer that a paused tick parked, on its next grid point strictly after the RESUME (orstoppedbeyond the stop time). The rows of one RESUME share its event key, for distinct flows of one node in increasingflow_idorder; no other rows may share a key. Every sender log carriesdata_class, so--pfcwith the run'spfc_transitions_csvis required (header-only for a run without PFC), and the pauses and resumes are checked against the host PAUSE and RESUME records in it:- a paused tick finds its class paused at its host at its event key, and an unpaused tick finds it not paused;
- every
resumerow shares the key of the host RESUME that unpaused its class at its node; - every host RESUME that unpauses class
pat nodenrestarts, inresumerows at its key, every pair of classpatnthat a paused tick parked and that can restart (work remaining, not complete). A missingresumerow is caught at the RESUME, even when a later ACK, NACK or timeout restart would mask it.
What this certifies, and what it trusts: it certifies the transition mechanics of a pause and a resume and their warrant and completeness against the host PFC records. It trusts the
data_classvalue each row carries, the host PFC records themselves (checked by the P10c PFC rules for internal consistency, not against the switch threshold records that emitted the frames), and that a parked pair that cannot restart writes noresumerow. -
A queue pair whose first ticks find its class paused writes no rate until its first crediting tick. Until that tick, or the pair's first DCQCN row, reveals the rate, the checker compares every field exactly except a status that depends on the rate; each such status bounds the rate, and the revealed rate must lie within the bounds. While a pair's controller is pristine (no DCQCN row yet), every rate its rows show must equal the sender log's
initial_rate_bps. -
--horizon-ns <ns>checks a prefix of a run: the logs must hold exactly the run's events withtime_ns < ns. Pending events at or after the horizon need not have fired; stop decisions are still checked against the run's stop time. -
--collective <path>(trace mode) joins the run'scollective_transitions_csvto the queue-pair logs. A released RoCE collective stage's pair runs its first pacing tick at the release instant, from the armed status the release row records; a logged stage that is never released has no queue-pair rows; and a local completion caused by a RoCE pair shares its event key with that pair's ACK that brings its acknowledgment to the pair's total.
p10c_collective_check replays each stage's inbound rows by the transport that sent them: TCP's
in-order frontier, which absorbs buffered out-of-order segments, or a RoCE queue pair's Go-back-N
frontier, which advances only on the packet at the frontier and requires each packet to be the
pair's packet at its PSN. A RoCE stage's rows carry its MTU; a compute stage after a RoCE
collective carries the predecessor's MTU and pacing interval, which must equal a logged
predecessor's. The checker knows the five Days AGO algorithms (lean/LeanGuard/P10c/Collective/):
each ring's stage positions and owner recurrence per channel, with every channel required to be one
ring over the collective's ranks; UniformFloor message sizes; an all-to-all's n (n - 1) pairs
of floor(S / n) bytes, or, for a seeded one, the per-pair bytes re-derived from the certificate's
seeded_matrix parameters by the same integer construction (SeededMatrix.lean); and a
SendRecv's one message. Dependencies are counted joins: a stage's local and inbound predecessor
lists are checked against its counts and against the operations the stage follows (a stage that
follows a collective must wait for that collective's whole completion at its rank, a compute
predecessor must sit on the same host), and each credit is tied to a logged completion or a replayed
frontier reaching the predecessor's total. A stage notify's release arms its lead's timer at the
sender, and its delivery lands exactly its recorded delay after the release, as one whole chunk.
Every inbound claim, arrival and local completion is bound to the delivery host its row records
(target_node, cause_target_node), and the rows one cause produces must agree on that host.
traceruns both, then the cross-role checks. Every data arrival must be a packet the sender emitted. Every ACK or NACK the sender applies must have been sent earlier by the receiver, with the same acknowledgment and the same ECN echo (feedback_ce_echoon the receiver row,input_ce_echoon the sender row); a queue pair sends and receives no CNP.<stop_time_ns>is the image'sstop_time_ns. No CSV records it, and it decides whether a pacer's next tick is armed or stopped. With it, each decision is checked exactly, no row may lie after it, and no pending event at or before it may be left unfired at the end of the log. Without it, the checker requires one stop time consistent with every armed and stopped decision in the log, and checks unfired events against the latest time the log shows to be before the stop.
Committed Days AGO certificate fixtures can be checked directly, for example:
./.lake/build/bin/p10c_aqm_check fixtures/p10c/aqm_executor_accept.csv
./.lake/build/bin/p10c_dcqcn_check fixtures/p10c/dcqcn_executor_trace_accept.csv
./.lake/build/bin/p10c_dcqcn_check trace fixtures/p10c/dcqcn_cnp_join_t26_accept.dcqcn.csv \
fixtures/p10c/dcqcn_cnp_join_t26_accept.cnp.csv
./.lake/build/bin/p10c_mechanisms_check drr fixtures/p10c/drr_executor_accept.csv
./.lake/build/bin/p10c_collective_check fixtures/p10c/collective_allgather_executor_accept.csv
./.lake/build/bin/p10c_roce_check trace fixtures/p10c/roce_trace_timeout_executor_accept.sender.csv \
fixtures/p10c/roce_trace_timeout_executor_accept.receiver.csv \
fixtures/p10c/roce_trace_timeout_executor_accept.dcqcn.csv \
--pfc fixtures/p10c/roce_trace_timeout_executor_accept.pfc.csv \
"$(cat fixtures/p10c/roce_trace_timeout_executor_accept.stop_time_ns)"
./.lake/build/bin/sp_check fixtures/sp/sp_priority_and_fifo_accept.csv
./.lake/build/bin/sp_check fixtures/sp/sp_compiled_executor_accept.csv
./.lake/build/bin/p10c_mechanisms_check wfq fixtures/p10c/wfq_compiled_executor_accept.csv
./.lake/build/bin/tcp_check fixtures/tcp/reno-compiled-tcp-events.csvThe legacy checkers, tcp_check, and sp_check also accept
--coverage-out <path>. leanguard-run --coverage uses that option and aggregates returned
coverpoints. The p10c_* executables do not expose the coverage flag.
Days AGO AQM rows
p10c_aqm_check replays aqm_transitions_csv: one row per admission decision at a switch queue
with an ECN ramp (Lean rules: the Aqm namespace of lean/LeanGuard/P10c/Semantics.lean and
lean/LeanGuard/P10c/AqmEventLog.lean). Each row names the packet's kind (packet_kind: data,
tcp_data, roce_data, tcp_ack, roce_ack, roce_nack, dcqcn_cnp, feedback, pfc,
roce_pacing_timer or stage_notify), which the writer takes from the run's observed packets, its
byte depth before admission and its size, the run's seed, and the queue's ramp
(capacity_bytes, kmin_bytes, kmax_bytes, pmax_numerator, pmax_denominator).
- The checker recomputes every decision. It derives the draw from the seed, the queue
(
node_id,queue_id) and the payload, exactly as the executor does (the switch configuration), and applies the ramp in integer arithmetic. It also checks that the policy is well formed, that each queue's policy and the run's seed stay constant, and that the ECN bit changes exactly on a mark. - Days AGO marks only data packets (
data,tcp_data,roce_data). Any other packet is admitted unmarked at any depth below the byte capacity, so an ACK inside or above the ramp must showenqueueand an unchanged ECN bit. - A packet's kind is the same on every row of its payload (one row per queue it crosses); a kind that changes between rows is rejected.
- What remains trusted is the kind itself: a packet relabelled consistently on all of its rows
cannot be told from a packet of that kind, as RoCE senders'
data_classand WFQ's PFC classes are trusted.aqm_roce_acks_whole_packet_relabel_trusted_limit_accept.csvpins this limit: a marked data packet relabelledroce_ackon all of its rows, its mark removed, is accepted.
The CI fixtures aqm_roce_acks_compiled_executor_accept.csv (configs/leanguard/aqm_roce_acks.toml,
a byte step) and aqm_roce_acks_ramp_compiled_executor_accept.csv (aqm_roce_acks_ramp.toml, a
ramp with pmax = 4/5) have RoCE ACKs admitted unmarked in a queue that marks data, and the ramp
fixture has data both marked and unmarked between its thresholds. Their mutations must be rejected:
a data packet left unmarked, a marked ACK, a relabel that keeps a mark, a draw flipped either way, a
changed seed, and a kind changed on one row of a packet. The AQM differential's reference decides
independently: it writes SplitMix64 out and applies the ramp in its rational form, with no high
multiply.
Days AGO WFQ and SP rows
p10c_mechanisms_check wfq replays wfq_transitions_csv: one row per transition of a WFQ switch
egress queue, at the event key of the event that performs it (Lean rules: the Wfq namespace of
lean/LeanGuard/P10c/Semantics.lean and WfqLog in MechanismEventLog.lean). Virtual time and
finish tags are exact rationals, written numerator/denominator in lowest terms and normalized by
the egress link's rate: a packet's service is size_bytes * 8 / weight, and virtual time advances
by elapsed_ns * rate_bps / (10^9 * W) over the active classes' weight sum W. A packet's class is
flow_id % class_count.
enqueue(phase 0): an admitted arrival. An idle scheduler resets virtual time and every class's finish tag to zero; otherwise virtual time advances.virtual_startmust be the later of virtual time and the class's last finish tag, andfinish_tagthe start plus the packet's service.select(phase 2): a service start. The scheduler state is unchanged.queued_packetsmust be exactly the packets waiting (served one included), each with its enqueue's finish tag and its PFC class, and the served packet must have the least finish tag, the earliest enqueue among equal tags, among the packets whose class is not inpaused_priorities.complete(phase 1): a service completion. Virtual time advances over the active set that still holds the packet; the class then loses it, and a scheduler left idle resets its virtual time and that class's finish tag.
A queue that does not start empty and idle (a run resumed from a checkpoint) opens with an
initial row whose event-key columns are empty. It gives the starting scheduler state, the waiting
packets in queue order with their finish tags and PFC classes, the in-service packet (payload,
finish_tag, pfc_priority; empty when there is none) and the starting paused priorities. Their
counts by class must equal the starting active counts. Other queues start from the empty state.
Each row's before state must equal the queue's replayed state, and its after state the
reference update. The executor records each transition's after state from its own scheduler,
after the event; wfq_transitions_csv writes before as the queue's state after its previous row
(its starting state before the first) and a completion's finish tag as its packet's at selection,
or in the starting state. A change to the scheduler between two rows would therefore show in the
second row's after, which the checker recomputes. These are the rules of the legacy wfq_check
(Demers, Keshav and Shenker, with the same idle resets) up to the rate normalization; wfq_check
still certifies days-legacy.
PFC classes and pauses:
- A packet's PFC class is fixed for its lifetime: its
enqueuerow (pfc_priority) or theinitialrow gives it, and everyselectlisting must repeat it. pfc_monitor(on every row) says whether the queue has a PFC monitor. Without one, every class is zero and noselectmay list a paused priority.- With one, the checker needs the run's PFC certificate (
pfc_transitions_csv) as its second argument, checks it with the P10c PFC rules, and requires eachselect'spaused_prioritiesto be the queue's pause state at the select's event key: the controller sets of the queue'scontrolrows before that key. A priority theinitialrow starts paused must have a non-empty controller set before its firstcontrolrow; any other, an empty one. - What remains trusted is each packet's class itself (its flow's packet priority, written at
enqueue), as RoCE senders'
data_classis.
The checker does not certify timing: a completion's time is not checked against the packet's serialization time on the link, and service starts are not checked for work conservation. Virtual time depends on those times, so the certificate holds for the event times the run recorded.
sp_check reads sp_transitions_csv, the same schema as its hand-written fixtures: enqueue
(phase 0), schedule (phase 2, with the transmission's completion time) and depart (phase 1).
It keys a scheduler by one number, so a Days AGO SP queue writes scheduler_id = node_id * 2^32 + queue_id and repeats node_id and queue_id. A queue that starts non-empty opens with an
initial_in_service row for the packet it is transmitting (with the time of its pending
TxComplete) and an initial_queued row per waiting packet in queue order; their event-key
columns are empty, and sp_check applies them before the events. sp_check has no pause model, so it certifies SP
only without PFC: under PFC, a Days AGO SP queue serves the first packet whose class is not paused,
which sp_check rejects as an SP order violation.
Days AGO DCQCN rows
p10c_dcqcn_check replays dcqcn_transitions_csv: one row per controller transition of an
unreliable DCQCN flow or a RoCE queue pair (schema: days-gpu/plans/briefs/p16/dcqcn-schema.md;
Lean rules: lean/LeanGuard/P10c/Dcqcn/Semantics.lean and DcqcnEventLog.lean). The
Mellanox-form controller owns no events, so each row sits at the event key of the flow transition
that applied its timers, and bound_ns is the exclusive bound of the instants applied: the
event's time at a feedback arrival (phase 0), one nanosecond later at a pacing tick or timeout
(phase 1). The arithmetic is exact: a 63-bit fixed-point alpha, integer rates, one floor per
update, a cut of R - ceil(R * alpha / 2^64), and an average of floor(R_C / 2) + floor(R_T / 2).
feedback: a CNP arrival at an unreliable flow, or an ACK or NACK with its ECN echo set at a queue pair that is not complete after it.aftermust equalon_feedback(before, time_ns).tick: every pacing tick of an unreliable flow, emitting or not.aftermust equalmaterialize(before, time_ns + 1), andafter_current_rate_bpsis the rate the tick read.advance: any other transition that applied a rate increase or a cut, or the ACK that completes a queue pair.
A row with frozen = 1 (the completing ACK of a queue pair, or the tick that emits an unreliable
flow's last byte) is replayed by settle, which applies the instants due before the bound and
stops the controller; the flow has no later row.
Each row's before must equal the flow's previous after; the first row's before is the pristine
controller of the row's configuration. Rows must arrive in strictly increasing EventKey order.
In trace mode the checker also reads dcqcn_cnp_arrivals_csv (time_ns,flow_id,payload, one
row per CNP delivered to a reaction point): a CNP at or before its unreliable flow's freeze must
be exactly one feedback row of that flow at its time; every feedback row of an unreliable flow
must be such a CNP; and a CNP addressed to a flow without tick rows is rejected, because a queue
pair receives no CNP. A queue pair's ticks have no tick row: p10c_roce_check ties each tick's
rate_bps to the controller instead, as described above.
Continuous integration
.github/workflows/leanguard.yml has two jobs.
lean-fixtures builds the checkers, audits the DaysExecutor axioms
(lean/scripts/audit-days-executor-axioms.sh), and runs these scripts under lean/scripts/, each
on committed fixtures: accept fixtures, plus mutations each checker must reject with a named
message.
- Days AGO certificates:
- the P10c collective, DCQCN and RoCE campaigns (
run-p10c-collective-campaign.sh,run-p10c-dcqcn-campaign.sh,run-p10c-roce-campaign.sh); - the P10c switch-mechanism campaign (
run-p10c-mechanism-campaign.sh): rate, switch-link PFC, DRR, WRR and WFQ; - Static Priority (
run-sp-campaign.sh, with coverage); - TCP Reno and CUBIC (
run-tcp-campaign.sh lean/fixtures/tcp, which builds its mutations); - the collective differential and budget and the AQM differential (below). The P10c AQM
campaign (
run-p10c-aqm-campaign.sh) runs in CI only inside the AQM differential, through the differential checker.
- the P10c collective, DCQCN and RoCE campaigns (
- Legacy certificates: the AQM, CUBIC, DRR and WFQ fixture and coverage scripts
(
run-aqm-fixtures.sh,run-aqm-coverage-fixtures.sh,run-cubic-fixtures.sh,run-drr-fixtures.sh,run-drr-coverage-fixtures.sh,run-wfq-fixtures.sh,run-wfq-coverage-fixtures.sh), on hand-written legacy-schema fixtures.
protocol-traces runs the legacy engine (days-legacy) on the configs/ci/ fixtures and the
legacy checkers on its traces. It certifies Legacy Days only.
Where the Days AGO certificates come from
Every Days AGO certificate is the full-observation output of a Scalar run; device runs return no
diagnostic planes, so no certificate comes from Metal or CUDA, and device identity rests on the
byte-identity tests, not on certificates. Many of the committed accept fixtures are regenerated by
a Rust test in CI's test job, which fails on any difference:
tests/leanguard_certificates.rslowers the scenarios inconfigs/leanguard/through the Days AGO compiler (compile_config), writes the Scalar certificate, and requires a two-worker CPU run to write the same bytes: TCP Reno and CUBIC (tcp_reno.toml,tcp_cubic.toml: fan-in losses, fast recovery and timeouts), switch-link PFC (pfc_switch.toml), DRR, WRR, WFQ and SP (sched_*.toml), and WFQ under switch-link PFC (wfq_pfc.toml). It also resumessched_wfq.tomlandsched_sp.tomlfrom a checkpoint with packets queued and in service, and the WFQ incast (wfq_incast.toml) from a checkpoint taken while a switch queue is paused. A WFQ certificate under PFC has its run's PFC certificate beside it (<name>.pfc.csv), which the campaign passes to the checker. It also writes the two AQM certificates ofaqm_roce_acks.toml(an ECN step on a queue that RoCE ACKs share with data) andaqm_roce_acks_ramp.toml(the ramp), which pin that ACKs stay unmarked and that every data packet's decision follows the draw. The fixtures arelean/fixtures/tcp/*-compiled-*.csv,lean/fixtures/p10c/*_compiled_executor_accept.csv, the*_resumed_executor_accept.csvfiles andlean/fixtures/sp/sp_compiled_executor_accept.csv.- Executor tests build images by hand for cases a scenario cannot reach directly: the adversarial
TCP recovery, timeout and saturation traces (
executor/tests/tcp_semantics.rs); therate_executor_accept,pfc_executor_accept,drr_executor_acceptandwrr_executor_acceptcertificates (rate_source.rs,pfc_semantics.rs,scalar_switch_schedulers.rs); and theaqm_executor_accept,aqm_ramp_executor_acceptandaqm_ramp_byte_overflow_executor_acceptAQM certificates (aqm_semantics.rs). - The collective certificates are the Scalar traces of the scenarios in
tests/collective_certificates.rs(the four P14 fixtures),tests/p16_collops.rs(collective_collops_*),tests/p16_collops_rail.rs(the rail fixtures among them) andtests/p16_hostafter.rs(collective_hostafter_*);tests/dcqcn_lowering.rsregeneratesdcqcn_executor_trace_accept.csv.
The remaining accept fixtures are committed outputs that no Rust test regenerates: the
roce_trace_*_executor_accept logs (Scalar runs of configs/p15/ and configs/p16/ scenarios,
written by days-gpu tooling), the four collective_roce_*_executor_accept P15 traces, and the
DCQCN queue-pair, tick-grid, coincident-prefix and CNP-join fixtures. The hand-written fixtures are wrr_wrap_after_saturated_class_accept.csv, the
SP fixtures without compiled in their names, the roce_sender_* and roce_receiver_* fixtures,
and the *_hand_* collective fixtures.
DAYS_UPDATE_LEANGUARD_FIXTURES=1 rewrites the TCP and compiled-scenario fixtures,
DAYS_UPDATE_COLLECTIVE_TRACE_FIXTURES=1 the collective certificates, and
DAYS_UPDATE_DCQCN_TRACE_FIXTURE=1 the DCQCN trace certificate.
Coverage by mechanism
The matrix lists what each Days AGO checker certifies, which certificates CI checks and where
they come from, and the recorded limits. One limit is common to every row: a checker reads a
certificate without the scenario, so a certificate that consistently rewrites every record of a
run describes a different scenario and is accepted. Every CI step below is in
.github/workflows/leanguard.yml and runs a script under lean/scripts/.
| Mechanism | What is certified (checker; CI step) | Certificates checked in CI | Documented limits |
|---|---|---|---|
| DCQCN, Mellanox form | Every controller transition of an unreliable flow or a queue pair: feedback, tick and advance rows against the exact integer reference (63-bit alpha, floored rates, freeze at completion); in trace mode, each CNP of the arrivals log is exactly one feedback row of its flow and no CNP reaches a queue pair (p10c_dcqcn_check; run-p10c-dcqcn-campaign.sh). | Scalar: dcqcn_executor_trace_accept.csv (regenerated by tests/dcqcn_lowering.rs); the queue-pair, tick-grid, coincident-prefix and CNP-join fixtures (committed, not regenerated by a Rust test). | A queue pair's ticks have no tick row; their rates are tied to the controller only through p10c_roce_check. The configuration is checked for continuity between rows, not against the scenario. |
| RoCE queue pairs, with host-link PFC at the pacer | Each data arrival at the receiver (notification point, frontier, ACK cadence, duplicates, NACK rate limit); each pacing tick, ACK, NACK and timeout at the sender, with the tick's rate tied to the DCQCN log; window-blocked and paused ticks; resume rows against the host PAUSE and RESUME records; the cross-role join of sent packets and applied feedback (p10c_roce_check; run-p10c-roce-campaign.sh). | Scalar: the roce_trace_*_executor_accept logs of configs/p15/ scenarios (timeout, NACK-only, host-PFC prefix, window prefix, RoCE DAG prefix, mixed RoCE and TCP), not regenerated by a Rust test; hand-written sender and receiver fixtures with their mutations. | Trusts each row's data_class and the host PFC records themselves; a parked pair that cannot restart is trusted to write no resume row; a prefix certificate leaves pending events after its horizon unchecked. |
| PFC | Switch-link PFC: each PAUSE and RESUME against the per-link thresholds and the controller sets of its queue (p10c_mechanisms_check pfc; run-p10c-mechanism-campaign.sh). Host-link PFC: the pause and resume of a host's pacers, in the RoCE row above, and the paused priorities of a WFQ queue, in the WFQ row. | Scalar, with the two-worker CPU run required to match: pfc_compiled_executor_accept.csv from configs/leanguard/pfc_switch.toml. Scalar: pfc_executor_accept.csv from a hand-built image (executor/tests/pfc_semantics.rs). | A forged control row that names a controller and link the run does not have is accepted (the rules check internal consistency, not the switch threshold records that emitted the frame). The pause state that a resumed queue starts with is trusted when no later control row names its priority. |
| ECN marking (the Kmin/Kmax/Pmax ramp) | Each queue's frozen ramp parameters and byte capacity; every admission decision recomputed from the queue's byte depth, the packet's kind and the stateless draw (the mark or drop the row records must equal the recomputed one); non-data packets (ACKs, NACKs, CNPs, PFC frames) never marked; each payload's kind constant across its rows (p10c_aqm_check; in CI inside run-p10c-aqm-differential.sh, which runs the whole AQM campaign through the shipped checker and its list-scan reference, plus generated mutants). | Scalar, with the two-worker CPU run required to match: aqm_roce_acks.toml (a step; RoCE ACKs share a marking queue with data) and aqm_roce_acks_ramp.toml (configs/leanguard/, tests/leanguard_certificates.rs). Scalar: aqm_executor_accept, aqm_ramp_executor_accept and aqm_ramp_byte_overflow_executor_accept from hand-built images (executor/tests/aqm_semantics.rs). | The packet kind is writer-trusted, as RoCE data_class is: a consistent relabel of all of a packet's rows is accepted (pinned by aqm_roce_acks_whole_packet_relabel_trusted_limit_accept.csv); binding the kind to the RoCE certificates through flow_id is not done. A seed rewritten consistently on every row is accepted. |
| WFQ | Every enqueue, service start and completion of a WFQ queue in exact rationals: virtual time, start and finish tags, the least-tag choice among unpaused classes, each packet's fixed PFC class, and each select's paused set against the run's PFC certificate; resumed queues from their initial row (p10c_mechanisms_check wfq; run-p10c-mechanism-campaign.sh). | Scalar, with the two-worker CPU run required to match: sched_wfq.toml fresh and resumed, wfq_pfc.toml with its PFC certificate, wfq_incast.toml resumed while paused with its PFC certificate. | Service timing is not certified: a completion's time is not checked against the packet's serialization time and service starts are not checked for work conservation, so the certificate holds for the event times the run recorded. The packet's class at enqueue is trusted. |
| Static Priority | Every enqueue, schedule and departure of an SP queue: larger priority first, FIFO within a priority, the scheduled completion time; resumed queues from their initial_* rows (sp_check; run-sp-campaign.sh, with coverage). | Scalar, with the two-worker CPU run required to match: sched_sp.toml fresh and resumed; hand-written fixtures. | No pause model: SP under PFC is not certified (a valid Days AGO run under PFC is rejected as an order violation), so the SP scenario has no PFC monitor. |
| DRR | Each selection against the per-class quanta and deficits; state continuity between rows (p10c_mechanisms_check drr; run-p10c-mechanism-campaign.sh). | Scalar, with the two-worker CPU run required to match: sched_drr.toml; Scalar: a hand-built image (executor/tests/scalar_switch_schedulers.rs). | None recorded beyond the common one. |
| WRR | Each selection against the per-class packet counts; state continuity (p10c_mechanisms_check wrr; run-p10c-mechanism-campaign.sh). | Scalar, with the two-worker CPU run required to match: sched_wrr.toml; Scalar: a hand-built image (scalar_switch_schedulers.rs); the hand-written wrr_wrap_after_saturated_class_accept.csv. | None recorded beyond the common one. |
| TCP Reno and CUBIC | Every congestion-control transition: new and duplicate ACKs, fast recovery entry, inflation, partial and full ACKs, timeouts, CUBIC's K and W_max (tcp_check; run-tcp-campaign.sh lean/fixtures/tcp, which builds its mutations). | Scalar, with the two-worker CPU run required to match: tcp_reno.toml and tcp_cubic.toml fan-ins; Scalar: the recovery, timeout and saturation traces of hand-built images (executor/tests/tcp_semantics.rs). | None recorded beyond the common one. |
Collectives and stage joins (rings, ReduceScatter, channels, compute stages, counted joins, stage notifies) | Each stage's progress rows: local completion at the acknowledging ACK or timer, inbound progress replayed by transport, the release at the event that completes the counts, each stage's predecessor lists against the operations it follows and its collective's whole completion at its rank, chunk sizes and positions per algorithm and channel, a notify's lead timer and its whole-chunk delivery exactly its delay after release (p10c_collective_check; run-p10c-collective-campaign.sh, plus the differential and heartbeat-budget steps). | Scalar: the P14 fixtures of tests/collective_certificates.rs, the collective_collops_* fixtures of tests/p16_collops.rs and tests/p16_collops_rail.rs, and the collective_hostafter_* fixtures of tests/p16_hostafter.rs (all regenerated); the four P15 collective_roce_*_executor_accept traces (committed outputs); hand-written RoCE fixtures. | An ungated collective's absolute start and each notify's delay value are not certified (like an unlogged TCP or RoCE root), so a notify that ignored initial_delay would still certify. An ungated root notify's collective label is writer-trusted. NVLink delays are accepted as given. The checker does not check which groups a stage follows beyond its predecessor lists: the after lists are not in the certificate, so dropping a whole cross-family join is accepted. |
| Send/Recv | The message's one stage, its local completion at the sender and its delivery at the receiver, both bound to the recorded delivery host: a claim at another host, a claim by another group, or a moved claim rejects (p10c_collective_check; the sendrecv-* and ungated-sendrecv-* cases of run-p10c-collective-campaign.sh). | Scalar: collective_collops_sendrecv, collective_hostafter_sendrecv_across_stages and collective_hostafter_sendrecv_ungated (regenerated). | For a message that starts a collective with no predecessor (an ungated root): its source host is not recorded, so a stage falsely claiming to have sent it is accepted; and when it has no sender-side successor, a claim-only rewrite of its receiver's rows is accepted. A consistent rewrite of every record describes a different scenario. |
| All-to-all, uniform and seeded | Each pair's stage and floor(S / n) bytes, or the seeded per-pair bytes re-derived from seeded_matrix; the completion join of a rank's sends and arrivals; a zero-send rank's release at its gate and arrivals, counted once (p10c_collective_check; the all-to-all, seeded and zero-row cases of run-p10c-collective-campaign.sh). | Scalar: the collective_collops_a2a_* fixtures (uniform TCP and RoCE, seeded, sparse, isolated rank, zero-row gate, diamond, redundant chain, slow gate) and the rail all-to-alls (regenerated). | Ungated all-to-all pairs log no rows, so they share the ungated-root limits of the row above. The after lists are not in the certificate, so a coherent rewrite that drops a zero-send rank's gate is accepted. |
| Rate sources | Each emission against the rate state (p10c_mechanisms_check rate; run-p10c-mechanism-campaign.sh). | Scalar: rate_executor_accept.csv from a hand-built image (executor/tests/rate_source.rs). | No scenario lowers a standalone rate generator (the compiler builds one only inside a DCQCN flow, whose rows the DCQCN row certifies). |
| FIFO scheduling | Not certified: no checker on either engine. | none | |
| Constant (packet-distribution) traffic | Not certified: no checker on either engine. | none |
Remaining gaps:
- FIFO scheduling and constant traffic have no checker on either engine.
- Static Priority under PFC:
sp_checkhas no pause model (see Days AGO WFQ and SP rows). - AQM packet kind:
p10c_aqm_checktrusts each packet's kind; only a kind that changes between a packet's rows is rejected (see Days AGO AQM rows). - WFQ timing: the
wfqmode does not check completion times against serialization or service starts for work conservation (no link-busy model, no transmission time); it takes the run's event times as given, so the certificate holds only if they are correct. - Two inputs the PFC rules and the WFQ PFC join do not check: a made-up PFC
controlrow that names a controller and link the run does not have (the rules check the certificate's internal consistency, as the existing PFC rules always did), and a resumed queue's starting pause on a priority that no later control row names (inherent to certifying from a checkpoint). - Rate sources: no scenario lowers a standalone rate generator (the compiler builds one only inside
a DCQCN flow, whose rows
p10c_dcqcn_checkcertifies), so the rate certificate comes from a hand-built image. - Backends: certificates come from Scalar runs only (the
configs/leanguard/ones also require a two-worker CPU run to write the same bytes), because device runs return no diagnostic planes. - Ungated roots in the collective certificate: see the Send/Recv and all-to-all rows above.
- The AQM certificate's packet kind is writer-trusted: see the ECN row above.
The P10c collective, DCQCN, RoCE and AQM checkers are linear in the row count, except the
collective checker's TCP replay, which keeps a per-flow frontier in an ordered map and is
n log n: per-flow, per-queue and per-stage state lives in hash maps, and no check searches
earlier rows per row.
Where P16 replaced such a search, the old list-scan functions stayed in a test-only module
(lean/LeanGuard/P10c/Test/), and three lean-fixtures steps keep the two in agreement:
run-p10c-collective-differential.shandrun-p10c-aqm-differential.shrun the shipped checker and the reference on every campaign fixture and on generated single-row mutants (p10c_collective_diff,p10c_aqm_diffinlean/lakefile.lean), and fail on any case where the two results differ;run-p10c-collective-budget.shcounts the shipped collective checker's small allocations (Lean heartbeats) across a size step of its fixtures and fails when the count grows faster than the rows allow, or when the shipped source contains a whole-trace list scan.
Exit codes and diagnostics
0:ACCEPT1:REJECT: ..., including malformed certificate data2: invalid command-line usage
Legacy checkers report failures in their canonical replay order, which may differ from physical CSV
order. Days AGO diagnostics use the complete EventKey; p10c_dcqcn_check and p10c_roce_check
additionally require the input rows to arrive in strictly increasing key order. A
p10c_roce_check diagnostic names the log it refers to (sender:, receiver: or dcqcn:) in
sender and trace modes.
Common rejection causes include malformed or out-of-domain integers, duplicate keys, parameter or state discontinuity, an illegal queue/protocol action, unmatched paired events, and a post-state that does not equal the executable reference update.