Regression System¶
RoboCI regression comparison answers:
Did the current run get worse than a baseline run?
Files¶
Main implementation:
roboci/regression/comparator.py
roboci/regression/gates.py
roboci/regression/baseline.py
Dashboard display:
frontend/components/RegressionDiff.tsx
frontend/app/regressions/page.tsx
Gates vs Regression Comparison¶
RoboCI has two related concepts:
Gates¶
Gates are absolute thresholds.
Example:
gates:
min_success_rate: 0.95
max_collisions: 0
min_safety_score: 80
If a run violates a gate, the run can fail even without a baseline.
Regression Comparison¶
Regression comparison is relative to a baseline.
Example:
roboci baseline create smoke --from runs/latest --description "Known-good smoke run"
roboci baseline promote runs/latest smoke
roboci compare --current runs/latest --baseline-name smoke
Named baselines are stored under baselines/<name>/. Each baseline is a copied run directory with a baseline.json metadata file:
{
"name": "smoke",
"source_run_id": "2026-06-14_10-00-00",
"source_run_dir": "/path/to/workspace/runs/2026-06-14_10-00-00",
"created_at": "2026-06-14T10:05:00+00:00",
"description": "Known-good smoke run",
"path": "baselines/smoke"
}
roboci baseline promote <run-dir> <name> is a positional baseline-promotion shortcut for the same named baseline flow. Baseline names are unique unless roboci baseline create <name> --from <run> --overwrite or roboci baseline promote <run> <name> --overwrite is used. --overwrite replaces the named baseline from a different source run; RoboCI rejects attempts to overwrite a baseline from its own directory so the source is not deleted. roboci baseline list and GET /baselines only include directories that contain baseline.json, so stray folders under baselines/ are ignored.
Path-based comparisons remain supported with roboci compare --current runs/latest --baseline baselines/smoke.
This writes metric deltas to:
runs/<current>/regression.json
Metric Direction¶
The comparator needs to know whether higher or lower is better.
Lower is better:
collision_countoffroad_counttrajectory_adetrajectory_fdemiss_ratecontrol_l1_errorprediction_latency_ms
Higher is better:
success_ratesafety_scoreroute_completion_percent
regression.json¶
Example:
{
"status": "fail",
"failures": [
"safety_score regressed by 8.10%"
],
"deltas": {
"safety_score": {
"current": 91.9,
"baseline": 100.0,
"delta": -8.1,
"percent": -8.1,
"direction": "higher_is_better",
"regressed": true
}
}
}
When a numeric baseline is 0 and the current value is nonzero, percent is null because a percent change from zero is undefined. Regression detection handles that case explicitly with a minimum absolute delta instead of reporting a fake 100% change.
Why Regression Diff May Be Empty¶
Normal run commands write regression.json with empty deltas:
{
"status": "pass",
"failures": [],
"deltas": {}
}
That means no baseline comparison has been run yet.
To populate deltas:
roboci baseline create smoke --from runs/<baseline-run-id>
roboci compare --current runs/latest --baseline-name smoke
Dashboard Behavior¶
The API enriches run summaries with:
regression_statusregression_countregression_delta_count
The dashboard uses those fields for overview cards and run tables.
The Regression Explorer reads the full regression.json for runs with regression artifacts and groups real artifact data into:
- New regressions.
- Resolved regressions.
- Recurring regressions.
- Top degraded scenarios.
- Top improved scenarios.
- Critical safety regressions.
Closed-loop and open-loop signals are shown in separate sections. Metric cards show the previous value, current value, delta, severity, and affected run or sample scope. Scenario cards show the affected scenario, safety score delta when present, and status transitions when the scenario exists only on one side of the comparison.
Replay links are shown only when a current closed-loop scenario exists. Failure links are shown only when a matching failure artifact exists for the card's scenario or sample; the explorer does not fall back to unrelated failures.
Closed-loop comparisons also backfill failure lifecycle references in the compared artifacts. Resolved baseline failures receive the current run id in fixed_in_run_id, while current-run failures on previously successful scenarios receive the current run id in regressed_in_run_id. RoboCI preserves manual terminal decisions such as verified and wont_fix.
The detailed diff is loaded from:
GET /runs/{run_id}
which includes:
{
"regression": {
"status": "pass",
"failures": [],
"deltas": {}
}
}
Adding New Regression Metrics¶
- Ensure the metric is written to
summary.json,metrics.json, oropenloop/aggregate_metrics.json. - Add the metric to
LOWER_IS_BETTERorHIGHER_IS_BETTERinroboci/regression/comparator.py. - Add tests for the new direction.
- Confirm the dashboard can render the delta.