poc: Mokka + AICR pre-silicon coverage sweep, measured twice - #744
poc: Mokka + AICR pre-silicon coverage sweep, measured twice#744ArangoGutierrez wants to merge 9 commits into
Conversation
Answers the question Mark Chmarny put on 2026-07-24: does Mokka + AICR materially reduce GB200 bring-up time, or does it merely prove the stack deploys against mocked APIs? Adds a re-runnable harness that runs the AICR recipe and validation suite against an nvml-mock cluster and classifies what each of AICR's 21 checks actually proves when no GPU is present. Everything is pinned: image ghcr.io/nvidia/nvml-mock:0.3.0, chart 0.3.0, AICR upstream/main @0752ea14. The harness is built so an incomplete run degrades to "not run" rather than to a favourable number. Seven invariants carry tests; the load-bearing one (a check absent from the run is never promoted to a pass) was mutation checked by flipping it and confirming two tests go red. Bucket (what a check would prove) and cause (what stopped this run) are kept as independent axes, because conflating a kind artifact with a Mokka gap is the easiest way to produce a misleading result. Result from the run on 2026-08-03: AICR dispatched 9 of 21 checks; 3 passed for real integration reasons, including check-nvidia-smi, which exercises device-plugin allocation and dlopen of libnvidia-ml.so.1 end to end. None of the 5 failures was caused by a Mokka capability gap. One real gap was found and is not yet fixed: the PCI bus ID reaches NVML consumers without its domain prefix, breaking dra.k8s.io/pcieRoot and crashing GFD. Supersedes the 2026-07-25 POC in NVIDIA#503, which predates v0.3.0. Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
…n without running Adds the hybrid cell asked for after the base sweep: 250 hollow KWOK GPU nodes alongside 2 real Mokka workers, 253 nodes advertising 2016 nvidia.com/gpu, same recipe, same AICR. Every AICR check reported PASS in 4.2s and 5.2s, against 9m0s on the pure-Mokka baseline, with no container ever executing. Four verified facts compose into it: validator Jobs tolerate every taint (empty key, operator Exists); KWOK nodes carry the full GPU label set and real nvidia.com/gpu capacity; the scheduler therefore places validators on them, 250 hollow against 2 real; and KWOK's pod-complete stage transitions any Job-owned pod to Succeeded. Container status shows an empty imageID with started false and startedAt equal to finishedAt, so the image was never pulled. The documented mitigation does not work. --node-selector reaches the snapshot agent's pod spec and stops there; deployer.go:66 passes it to inner workloads via AICR_NODE_SELECTOR and never applies it to the validator Job itself. Not a Mokka gap and not a KWOK bug. It is a composition hazard whose actionable half sits in AICR, and it is the architecture the fake-gpu-operator per-nodepool backend design produces. The 9 false passes are deliberately excluded from cells.yaml and the rollup, so they cannot raise the headline coverage number. Raw CTRF kept for audit. The recommendation panel dissented against running this, arguing it would document the obvious, and I agreed before being overruled. Both of us were wrong about the direction of the result; the dissent is recorded in DECISIONS.md D-015. Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
…ks added The previous wording said every check passed, which reads as all 21. Both runs dispatched the same 9 of 21; adding hollow KWOK nodes changed no selection, it changed the verdicts from 3 pass / 5 fail / 1 inconclusive to 9 pass / 0 fail. The other 12 stayed undispatched in both runs, so the KWOK run is not a coverage gain, it is the same 9 verdicts fabricated. Corrected in FINDINGS.md and HYBRID-KWOK-FINDING.md. Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
The v0.3.0 sweep was 23 days old and its headline gap, the PCI bus ID missing its domain, was fixed upstream by NVIDIA#672. Re-measuring was the only way to know whether the verdict moved. AICR is held at the same pin, 0752ea14, so Mokka is the only axis that changed. Verified rather than assumed: the base cell's generated recipe is byte-identical to the one recorded on 2026-08-03. Both runs regenerate byte-identically and neither overwrites the other. NVIDIA#672 landed and is real, and gpu-operator-health did NOT move. GFD now receives a well-formed BDF and still cannot read the vendor file: Mokka's rendered PCI tree uses relative symlinks and is not grafted onto GFD's /sys. D-014's K classification survives; its distinguishing cell is still the one to run. One verdict flipped. dra-support passes now because k8s-dra-driver-gpu v0.5.0 released altProcDevices on 2026-08-19, which D-009 had recorded as unreleased. Mokka's IMEX rendering already worked at v0.3.0; a released consumer was what was missing. Coverage went from 13 of 210 check-results reaching a verdict to 53, with the BUDGET and U blocks both to zero. Two cells the first run blocked as BUDGET and U are reclassified X: they declare intent=training, which resolves no recipe on the kind service for gb200. Also recorded: an injected uncorrectable ECC fault moves no verdict, and AICR's own snapshot agent is a static Go binary with no PT_INTERP, so library interception cannot reach it. The second was raised by Giulio Calzolari in review and verified here against the published image. Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Five documents carried 2026-08-03 figures with nothing marking them as a single dated run. A reader opening FINDINGS.md met "the one real gap this sweep found", which upstream fixed on 2026-08-19, and "nvidia.com/gpu: 8 per worker", which NVIDIA#727 changed to 4, with no signal that newer numbers exist. The measurements themselves are left alone. They were correct when taken, and rewriting them would destroy the first run's record. Each file gets a dated banner naming what has since changed and where to find the current figures. DECISIONS.md notes the two decisions the re-run bears on: D-009's cause-U block is released, and D-014's K classification survives with a changed diagnosis. Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
…VIDIA#503 PR NVIDIA#503 is superseded by this branch and is being closed. Three files existed only there: the preflight-workflow diagram pair and the diagrams index. The index lists every diagram by name, so porting it without preflight-workflow would leave a dangling row. The index gains a row for break-matrix, which this branch added and NVIDIA#503 never had. Verified both directions: every diagram named in the index has both a .mmd and a .svg on disk, and no diagram file is left unindexed. The rest of NVIDIA#503 is genuinely superseded. Its code landed on main through NVIDIA#541 and NVIDIA#542, and the IMEX renderer it introduced now lives at internal/imex after NVIDIA#729 relocated it. Its harness and 2026-07-25 results are left behind deliberately: that run predates v0.3.0, its harness will not exist in the tree, so its numbers could not be regenerated or checked. They stay reachable at pull/503/head. Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Rebasing onto main surfaced 8 lint findings that did not exist at the branch's old base: main enabled cyclop and perfsprint since the fork. CI would have gone red on a branch that was green locally before the rebase. perfsprint is fixed properly. Three fmt.Errorf calls carry no format verbs and become errors.New. cyclop is suppressed with the pattern already established on main (cmd/generate-bridge, cmd/nvml-mock-ctl, pkg/diagnostics all use "//nolint:cyclop // existing complexity; refactor deferred"). These five functions are branchy by nature: they map CTRF statuses onto outcomes, roll up buckets and causes, and render a table. Splitting them to chase a complexity number would scatter logic the tests pin precisely. nolintlint requires the suppression be specific and explained, and it is both. Verified behaviour-neutral: both recorded runs still regenerate byte-identical coverage.md and report.json after the edits. Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
…to be closed The next-steps list told a reader to close NVIDIA#498. Checking it rather than assuming: it was closed already. What remains true is that the imex note in the chart's values.yaml still says altProcDevices is in no release and still points at that issue, so the note outlived the issue and is wrong on both. Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
The Pages workflow runs `mkdocs build --strict`, which promotes an unresolvable link to an error. Five links failed. Reproduced locally with mkdocs 1.6.1 and `make docs-check-exclusion`, the same target CI runs. Three are in coverage.md and point at real, current files under tests/aicr-sweep/ that are simply not part of the docs site. They become absolute GitHub URLs, which mkdocs does not try to resolve and which are more useful to a reader on the published site anyway. Directory links are left alone: mkdocs reports those at INFO and does not fail them. Two came in with the diagram index carried over from NVIDIA#503, and the fix is larger than the links. That section cited tests/aicr-preflight/, the harness this branch deliberately does not carry, and quoted NVIDIA#503's bucket split (A 14, B 0, C 4, G 3, 66.7%, 81.0%). Those numbers are superseded twice over and contradicted the coverage-map SVG sitting beside them, which already carries the sweep's A 16, B 1, C 4, G 0 and the 76% ceiling. The section now cites tests/aicr-sweep/catalog.yaml, states the catalog's real counts, and points at the second run. Porting a file without auditing its body is what let the stale numbers in. Signed-off-by: Carlos Eduardo Arango Gutierrez <eduardoa@nvidia.com>
Greptile SummaryThis PR adds a reproducible AICR-on-Mokka coverage sweep, two recorded result sets, supporting evidence, and documentation explaining the pre-silicon coverage boundary.
Confidence Score: 4/5The PR should fix cell-ID validation before merging because duplicate IDs can reuse and double-count a single result directory; duplicate CTRF handling is additional non-blocking hardening. The harness trusts operator-supplied cell IDs as unique result identities, allowing repeated IDs to classify the same on-disk results more than once and corrupt the coverage roll-up. Files Needing Attention: tests/aicr-sweep/main.go, tests/aicr-sweep/classify.go, tests/aicr-sweep/ctrf.go Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
C[Catalog and cell matrix] --> R[Per-cell CTRF reports]
R --> P[Parse and merge reports]
P --> K[Classify each catalog check]
K --> U[Roll up checks across cells]
U --> J[report.json]
U --> M[coverage.md]
K --> N[Blocked and not-run remain in denominator]
K --> S[Simulated hardware-only passes marked suspect]
Reviews (1): Last reviewed commit: "fix(docs): unbreak the site build the ai..." | Re-trigger Greptile |
| results := make([]CellResult, 0, len(doc.Cells)) | ||
| for _, spec := range doc.Cells { | ||
| var run *RunResults | ||
| cause, why := spec.BlockedCause, spec.BlockedWhy | ||
|
|
||
| if cause == CauseNone && *resultsDir != "" { |
There was a problem hiding this comment.
| byName := map[string]RunTest{} | ||
| if run != nil { | ||
| for _, t := range run.Tests { | ||
| byName[t.Name] = t | ||
| } |
There was a problem hiding this comment.
When multiple phase or retained rerun reports contain the same check name, this assignment silently keeps whichever entry sorts last by filename. The generated verdict therefore depends on incidental file ordering and hides the other result; reject duplicates or define an explicit resolution rule.
Problem
The 2026-07-25 POC (#503) answered whether AICR runs against Mokka at all. It could not answer the
question the AICR maintainer actually put on 2026-07-24:
Answering that needs a permutation matrix and a way to say what each check proves when no GPU is
present, not a single recipe and a pass count.
Approach
A harness at
tests/aicr-sweep/classifies every AICR check into A (meaningful), B (trivial),C (hardware-dependent) or G (closable Mokka gap), and records a cause code for anything that did not
reach a verdict. Blocked cells stay in the denominator, so coverage cannot be inflated by deleting
the work that did not happen.
It is built so an incomplete run degrades to "not run" rather than to a favourable number. Seven
invariants each have a test that goes red if it regresses, and invariant 1 was mutation-checked:
flipping
OutcomeNotRuntoOutcomePassinClassifyturns two tests red.Two runs are recorded, and both regenerate byte-identically:
cells.yamlresults/v0.3.0cells-57ef016.yamlresults-57ef016/57ef01659AICR is pinned to
0752ea14in both, so Mokka is the only axis that moved between them. Verifiedrather than assumed: the base cell's generated recipe is byte-identical across the two runs.
What it found
The re-run's headline is negative, and that is the point. #672 fixed the PCI bus ID gap the first
run reported (
0000:01:00.0to00000000:01:00.0, confirmed by a differential probe of the twopublished images).
gpu-operator-healthdid not move. GFD now receives a well-formed BDF andstill cannot read the vendor file, because Mokka's rendered PCI tree uses relative symlinks and is
not grafted onto GFD's
/sys. Both statements are true at once, and AICR's check cannot tell themapart: it only sees
ClusterPolicy state=notReady.One verdict flipped.
dra-supportpasses now, because k8s-dra-driver-gpu v0.5.0 releasedaltProcDeviceson 2026-08-19. D-009 had recorded that flag as unreleased, which was correct whenwritten. Mokka's IMEX rendering already worked at
v0.3.0; a released consumer was what was missing.Coverage moved a long way. 13 of 210 check-results reached a verdict in the first run; 53 in the
second. The
BUDGETandUblocks are both zero. The largest single gain is the h100 cell, whereAICR dispatches 14 of 21 checks against 9 for gb200.
Two cells the first run blocked were misattributed, and this PR reclassifies them with CLI output
as evidence: both declare
intent=training, which resolves no recipe on thekindservice forgb200. That is
X, notBUDGET/U.Two consumers, one shape of problem. GFD is dynamically linked and fails on a sysfs path.
AICR's own snapshot agent is a static
ko-built Go binary with noPT_INTERP, so libraryinterception cannot reach it at all, and it fails on sysfs content. Raised by @giuliocalzo in
review and verified here against the published image. The common remedy is a filesystem graft, not
library interception.
An injected uncorrectable ECC fault moves no verdict. With the mock reporting 6 and 9 DRAM
Uncorrectable errors and all four GPUs still enumerating, every dispatched check is identical to the
base cell. That is a statement about which checks this recipe dispatches, not about Mokka.
Testing done
Build clean, tests pass, lint reports 0 issues, and both recorded runs regenerate byte-identical
coverage.mdandreport.json. Every number in the write-ups comes from that last command ratherthan from memory.
Known limits, stated rather than buried
simprovenance. Bucket C is non-empty byconstruction: 4 checks cannot be judged without hardware.
Chart.yamlon main is still0.3.0, sothe published OCI chart does not carry the 60 commits under test. The image is the published
sha-57ef016, so anyone can pull it by digest.K, notG. D-014 named the experiment that would settle it(run with
cdi.enabled=trueand the toolkit installed). It has not been run, the honest status is"not distinguished", and it is the single most valuable cell still outstanding.
dra-supportneeded--set nameOverride. v0.5.0's chart was renamed, so its default resourcenames no longer match what AICR looks for. AICR itself was not modified.
Breaking changes
None. This adds a test harness and documentation. No production code path changes.
Relationship to other work
Supersedes and replaces #503, now closed. Its code had already landed on main via #541 and #542, and
the IMEX renderer it introduced now lives at
internal/imex/after #729 relocated it. Thepreflight-workflowdiagram and the diagrams index are carried forward here; its harness and2026-07-25 results are deliberately left behind, because that harness will not be in the tree and
those numbers could not be regenerated or checked. They remain reachable at
pull/503/head.Related: #502 tracks the coverage gaps, and its body still carries the July figures that this PR
supersedes. Not closing it here, since correcting those numbers is a separate call.