Part of #2009
GET /api/tree/<tree_name>/<branch>/builds/compare?commit_a=&commit_b=&origin=&filter_*=...
Route uses <path:git_branch> and category-before-compare so it can't be confused with the per-commit <commit_hash>/builds route (no registration-order dependency).
Fetch builds for both commits (reuse FilterParams, create_checkouts_where_clauses, get_tree_data pattern). Map raw status → PASS / FAIL / INCONCLUSIVE via group_status (all non-PASS/FAIL statuses collapse to INCONCLUSIVE). Join on config_name + architecture + compiler. Return rows where grouped status differs; one-sided rows use null.
Open question: identity key may not be unique per commit — decide collision handling (group to one status per key before comparing).
Response: array of { config_name, architecture, compiler, status_a, status_b }.
Done when: route + OpenAPI; diff tests (flip, one-sided null, same grouped bucket → no diff).
Part of #2009
GET /api/tree/<tree_name>/<branch>/builds/compare?commit_a=&commit_b=&origin=&filter_*=...Route uses
<path:git_branch>and category-before-compareso it can't be confused with the per-commit<commit_hash>/buildsroute (no registration-order dependency).Fetch builds for both commits (reuse
FilterParams,create_checkouts_where_clauses,get_tree_datapattern). Map raw status →PASS/FAIL/INCONCLUSIVEviagroup_status(all non-PASS/FAIL statuses collapse toINCONCLUSIVE). Join onconfig_name+architecture+compiler. Return rows where grouped status differs; one-sided rows usenull.Open question: identity key may not be unique per commit — decide collision handling (group to one status per key before comparing).
Response: array of
{ config_name, architecture, compiler, status_a, status_b }.Done when: route + OpenAPI; diff tests (flip, one-sided null, same grouped bucket → no diff).