Inspiration
When a robot policy fails in simulation, the first question is the hardest: what broke? It could be a one-line bug in the client code (a flipped image, a wrong normalisation file), a gap in the training data (the policy never saw this lighting), or the simulator itself. Each needs a completely different fix, and guessing wrong is expensive: a fine-tuning run spent on a one-line bug, or an afternoon of patching code when the data was the problem. I wanted a system that answers "what broke" before it spends money on a fix.
What it does
Triage has two parts:
- A deterministic router. It runs contract checks on the policy's real inputs and outputs and feeds the results into a fixed decision table. The checks look at image orientation, channel order and value range; config against the training spec; and action delay. No model decides the route. A test enforces that the decision code imports no model layer. If evidence is missing, the router says
undeterminedand names what is missing, instead of guessing. - hagent, a two-model repair agent for code bugs. It runs on Nebius Token Factory:
Nemotron-3_5-Lightningdrives almost every step, andNemotron-3-Ultra-550b-a55bis consulted rarely, for planning, when the agent is stuck, and for the final review. It has hard caps on steps, cost and big-model calls.
The policy under test is NVIDIA GR00T N1.6 in Isaac Lab Arena (GR1 robot, put an item in the fridge and close the door).
How I built it
- Router and verification ladder (Python, about 835 tests against mocks): Stage 1 checks, counterfactual replays, the decision table, and Rung 0/1/2 verification of patches.
- hagent: a bash-only coding agent with no dependencies beyond the standard library. It talks to Token Factory's OpenAI-compatible endpoint and has a loop detector and history compaction.
- The simulation side: an Isaac Lab Arena backend, instrumentation on a GR00T client wrapper, and a ledger of 14 injected bugs, each with a patch, a symptom and the check that should catch it.
- A real GPU session on an RTX PRO 6000 Blackwell, which was a Spot VM preempted several times. Over it, I ran clean episodes, injected bugs, the router checks on real frames, automatic repair and a lighting sweep.
Success rates use Wilson intervals and Fisher tests. Repair success uses the unbiased pass@k estimator:
$$\text{pass@}k = 1 - \frac{\binom{n-c}{k}}{\binom{n}{k}}$$
Challenges I ran into
- Blackwell (sm_120): GR00T's shipped torch had no kernels for the GPU, so I had to install
torch 2.7.1+cu128. Isaac Sim crashed at camera init until I installed the NVIDIA Vulkan driver package. - Spot preemptions wiped running jobs and
/tmpseveral times. I rebuilt the bugged repos deterministically from the ledger and bundled results to my laptop between runs. - Isaac Lab Mimic would not start (a render-graph error on both CPU and GPU). That blocked the reference controller my router needs to rule out simulator faults.
- Honest scoring: my first scoring of one bug was wrong, and I had to rescore it with the router's own config check.
Accomplishments that I'm proud of
- Clean policy: 32/40. With the 180° image-rotation bug: 0/30. After hagent's fix: 28/40, within noise of clean; the fixed repo is byte-identical to the clean one.
- The router flagged 6 of 7 injected bugs on real data, including 4 that barely change success, which a success-rate monitor would miss. It had 0 of 3 false positives on held-out clean runs.
- Automatic repair with no hand-written hint: 4/5 attempts on the rotation bug (about $0.09 per attempt), 2/5 on a normalisation-stats bug, and 1/5 on a bug given only as a symptom.
- I found a real vision failure with no code change: dimming the dome light took success from 17/20 to 0/20.
- The router refused to guess. Without the reference replay it returned
undetermined, exactly as designed.
What I learned
- Detection is not routing. The observation check also fires on the dark-lighting scene, so contract evidence alone cannot tell a vision gap from a code bug. Only a counterfactual replay can, which is why the design has them.
- Localization matters more than model size. hagent fixed located bugs far more often than the unlocated one. On my SWE-rebench-V2 benchmark, adding the big model did not beat the small one (13/39 vs 14/39).
- Small samples make it easy to fool yourself, so I report per-attempt rates and intervals, not best cases.
What's next for Triage: Autonomous VLA Bug Repair
After my end-semester exams:
- Get a reference controller working, either by fixing Mimic or using the stock Arena client as a stand-in. Then run the one-factor-restore replay, so the router can actually separate the dark-lighting case from a code bug.
- Run the full incident loop end to end on the simulator, with Rung 1 as a selector over repair attempts.
- Calibrate thresholds on no-fault runs, and run the remaining ledger bugs with more episodes.
- Close the vision half: a short fine-tune on the dark-lighting failure and a held-out evaluation, compared with a fixed fine-tuning recipe on cost.