Skip to content

Commit 2ee566c

Browse files
committed
[CRCR] Add CRCR results to main HUD grid with monsterization
Integrates CRCR downstream CI results (L2/L3) into the main HUD commit grid as native columns. CRCR jobs appear under a "CRCR" group alongside existing groups (Linux, Windows, etc.) and support all existing HUD features: grouped view, hide-green, hide-always-skipped, job filter, tooltips, and monsterization. The approach mirrors the AI advisor verdicts pattern — a parallel ClickHouse query fetches CRCR results by SHA, maps them into JobData, and merges them into each row's nameToJobs before grouping runs. This means zero new UI components; existing JobCell, HudGroupedCell, and JobConclusion handle rendering and monsterization automatically. For monsterization, failed CRCR jobs use failed_tests_json (when available) or a synthetic string (downstream_repo/job_name) as the failure line hash input, giving each unique failure type its own monster sprite. Authored with an AI assistant.
1 parent 168b0ed commit 2ee566c

4 files changed

Lines changed: 141 additions & 4 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"params": {
3+
"shas": "Array(String)"
4+
},
5+
"tests": [
6+
{
7+
"shas": "[\"abc123\"]"
8+
}
9+
]
10+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
SELECT
2+
pytorch_head_sha,
3+
downstream_repo,
4+
downstream_repo_level,
5+
workflow_name,
6+
job_name,
7+
check_run_id,
8+
run_id,
9+
run_attempt,
10+
status,
11+
conclusion,
12+
workflow_run_url,
13+
duration_seconds,
14+
failed_tests_json
15+
FROM
16+
default.crcr_workflow_job FINAL
17+
WHERE
18+
pytorch_head_sha IN {shas: Array(String)}
19+
ORDER BY
20+
pytorch_head_sha, downstream_repo, job_name

torchci/lib/JobClassifierUtil.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,15 @@ const GROUP_LIBTORCH = "Libtorch";
7373
const GROUP_OTHER_VIABLE_STRICT_BLOCKING = "Other viable/strict blocking";
7474
const GROUP_XPU = "XPU";
7575
const GROUP_VLLM = "vLLM";
76+
const GROUP_CRCR = "CRCR";
7677
const GROUP_OTHER = "other";
7778

7879
// Jobs will be grouped with the first regex they match in this list
7980
export const groups = [
81+
{
82+
regex: /^CRCR /,
83+
name: GROUP_CRCR,
84+
},
8085
{
8186
regex: /vllm/i,
8287
name: GROUP_VLLM,
@@ -235,6 +240,7 @@ const HUD_GROUP_SORTING = [
235240
GROUP_BINARY_WINDOWS,
236241
GROUP_MEMORY_LEAK_CHECK,
237242
GROUP_RERUN_DISABLED_TESTS,
243+
GROUP_CRCR,
238244
// These two groups should always be at the end
239245
GROUP_OTHER,
240246
GROUP_UNSTABLE,

torchci/pages/hud/[repoOwner]/[repoName]/[branch]/[[...page]].tsx

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,65 @@ function CopyPermanentLink({
748748
return <CopyLink textToCopy={url} compressed={false} style={style} />;
749749
}
750750

751+
interface CrcrHudRow {
752+
pytorch_head_sha: string;
753+
downstream_repo: string;
754+
downstream_repo_level: string;
755+
workflow_name: string;
756+
job_name: string;
757+
check_run_id: string;
758+
run_id: string;
759+
run_attempt: number;
760+
status: string;
761+
conclusion: string;
762+
workflow_run_url: string;
763+
duration_seconds: number;
764+
failed_tests_json: string;
765+
}
766+
767+
function crcrToJobData(row: CrcrHudRow): JobData {
768+
let conclusion: string | undefined;
769+
if (row.status === "completed") {
770+
conclusion = row.conclusion || "failure";
771+
} else if (row.status === "in_progress") {
772+
conclusion = "pending";
773+
} else {
774+
conclusion = undefined;
775+
}
776+
777+
const failureLines: string[] = [];
778+
if (conclusion === "failure") {
779+
if (row.failed_tests_json) {
780+
try {
781+
const tests = JSON.parse(row.failed_tests_json);
782+
if (Array.isArray(tests) && tests.length > 0) {
783+
failureLines.push(
784+
tests.map((t: any) => t.name || t.classname || "").join("; ")
785+
);
786+
}
787+
} catch {
788+
// fall through to synthetic failure line
789+
}
790+
}
791+
if (failureLines.length === 0) {
792+
failureLines.push(`${row.downstream_repo}/${row.job_name}`);
793+
}
794+
}
795+
796+
return {
797+
name: `CRCR ${row.downstream_repo} / ${row.job_name}`,
798+
conclusion,
799+
htmlUrl: row.workflow_run_url,
800+
durationS: row.duration_seconds || undefined,
801+
failureLines: failureLines.length > 0 ? failureLines : undefined,
802+
id: row.check_run_id,
803+
sha: row.pytorch_head_sha,
804+
workflowName: `CRCR ${row.downstream_repo}`,
805+
jobName: row.job_name,
806+
runAttempt: row.run_attempt,
807+
};
808+
}
809+
751810
function GroupedHudTable({ params }: { params: HudParams }) {
752811
const router = useRouter();
753812
const { data, isLoading, error } = useHudData(params);
@@ -759,9 +818,6 @@ function GroupedHudTable({ params }: { params: HudParams }) {
759818
refreshInterval: 300 * 1000, // refresh every 5 minutes
760819
}
761820
);
762-
const jobNames = new Set(
763-
data?.flatMap((row) => Array.from(row.nameToJobs.keys()))
764-
);
765821

766822
// Lazy-load AI advisor verdicts for commits on screen
767823
const shas = useMemo(() => data?.map((row) => row.sha) ?? [], [data]);
@@ -778,6 +834,51 @@ function GroupedHudTable({ params }: { params: HudParams }) {
778834
return buildVerdictsBySha(deduplicateVerdicts(advisorRows));
779835
}, [advisorRows]);
780836

837+
// Lazy-load CRCR results for commits on screen (pytorch/pytorch only)
838+
const isPyTorch = isPyTorchPyTorchRepo(params);
839+
const { data: crcrRows } = useClickHouseAPIImmutable<CrcrHudRow>(
840+
"crcr_hud_results",
841+
{ shas },
842+
isPyTorch && shas.length > 0
843+
);
844+
845+
// Merge CRCR results into each row's nameToJobs so they appear as
846+
// regular grid columns (with grouping, filtering, monsterization).
847+
const dataWithCrcr = useMemo(() => {
848+
if (!data) return data;
849+
if (!crcrRows || crcrRows.length === 0) return data;
850+
851+
const crcrBySha = new Map<string, CrcrHudRow[]>();
852+
for (const row of crcrRows) {
853+
const list = crcrBySha.get(row.pytorch_head_sha) ?? [];
854+
list.push(row);
855+
crcrBySha.set(row.pytorch_head_sha, list);
856+
}
857+
858+
return data.map((row) => {
859+
const crcrForSha = crcrBySha.get(row.sha);
860+
if (!crcrForSha) return row;
861+
862+
const merged = new Map(row.nameToJobs);
863+
for (const cr of crcrForSha) {
864+
const jobData = crcrToJobData(cr);
865+
const existing = merged.get(jobData.name!);
866+
if (
867+
!existing ||
868+
!existing.id ||
869+
cr.run_attempt > (existing.runAttempt ?? 0)
870+
) {
871+
merged.set(jobData.name!, jobData);
872+
}
873+
}
874+
return { ...row, nameToJobs: merged };
875+
});
876+
}, [data, crcrRows]);
877+
878+
const jobNames = new Set(
879+
dataWithCrcr?.flatMap((row) => Array.from(row.nameToJobs.keys()))
880+
);
881+
781882
const [hideUnstable] = usePreference("hideUnstable");
782883
const [hideGreenColumns] = useHideGreenColumnsPreference();
783884
const [hideNonViableStrict] = useHideNonViableStrictPreference();
@@ -794,7 +895,7 @@ function GroupedHudTable({ params }: { params: HudParams }) {
794895
jobsViableStrictBlocking,
795896
groupsViableStrictBlocking,
796897
} = getGroupingData(
797-
data ?? [],
898+
dataWithCrcr ?? [],
798899
jobNames,
799900
(!useGrouping && hideUnstable) || (useGrouping && !hideUnstable),
800901
unstableIssuesData ?? [],

0 commit comments

Comments
 (0)