Skip to content

Commit e06d23f

Browse files
authored
feat(corroborate): add cross-version combined dashboard view (#1771)
Signed-off-by: Nathan Hensley <nhensley@nvidia.com>
1 parent 22c35b3 commit e06d23f

6 files changed

Lines changed: 242 additions & 41 deletions

File tree

pkg/corroborate/doc.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@
3131
// FAILING, UNTESTED. A verified-but-unallowlisted signer is admitted as a
3232
// zero-weight "reported" dot that can never reach CONFIRMED on its own.
3333
//
34+
// Each recipe's consensus is baked two ways. The strict per-version grids
35+
// (Tab.Versions, newest-first) count agreement only among runs at the SAME AICR
36+
// version, because a re-run against a different tool release is not a reproduction
37+
// of the same result. The relaxed cross-version grid (Tab.Combined) folds each
38+
// distinct signer's single latest run, version-blind, into one grid — the
39+
// dashboard's default "all versions" view, which surfaces every source that has
40+
// attested the recipe (including sources whose latest run predates the newest
41+
// release). Selecting a specific AICR version switches the renderer to that
42+
// version's strict grid.
43+
//
3444
// The generator (Generate) reads the source-keyed GCS layout (Contract 3:
3545
// results/<group>/<dashboard>/<tab>/<signer-id-hash>/<run-id>/{meta.json,
3646
// ctrf/<phase>.json}) from a local directory, derives each recipe's coordinate

pkg/corroborate/generate.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,10 +556,28 @@ func buildRecipe(agg *recipeAgg, sources map[string]Source) (Tab, Series, int) {
556556
})
557557
}
558558

559+
// Cross-version "all versions" grid: fold each signer's single latest run
560+
// (latestAny, version-blind) into one consensus grid. This is the dashboard's
561+
// default view — it includes every source that has attested this recipe, even
562+
// sources whose latest run predates the newest release (which the newest strict
563+
// grid, tabVersions[0], omits — such a source stays visible in its own version
564+
// grid). A throwaway grid-signer set keeps the series column set (built from the
565+
// per-version grids above) unchanged: a signer's latest run also lands in its
566+
// own version grid, so this adds no series column the per-version pass missed.
567+
// Consensus here counts agreement ACROSS versions — weaker than same-version
568+
// reproduction — which the renderer surfaces explicitly.
569+
combinedRows, combinedStates := computeGrid(unionRows(latestAny), signerIDs, latestAny, map[string]struct{}{})
570+
combined := &TabVersion{
571+
AICRVer: "",
572+
PhaseRollup: phaseRollup(combinedStates),
573+
Tests: combinedRows,
574+
}
575+
559576
tab := Tab{
560577
Recipe: agg.name,
561578
Coord: coordMap(agg.criteria),
562579
Versions: tabVersions,
580+
Combined: combined,
563581
}
564582
// The time-series spans every build/version, so its union test set must come
565583
// from ALL runs (not just each signer's newest), or a test that ran only in an

pkg/corroborate/generate_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,23 @@ func findRowAtVersion(t *testing.T, idx Index, recipe, aicrVer, phase, name stri
100100
return Row{}
101101
}
102102

103+
// findCombinedRow returns a row from the recipe's cross-version Combined grid
104+
// (the dashboard's default "all versions" view).
105+
func findCombinedRow(t *testing.T, idx Index, recipe, phase, name string) Row {
106+
t.Helper()
107+
tab := findTab(t, idx, recipe)
108+
if tab.Combined == nil {
109+
t.Fatalf("recipe %s has no combined grid", recipe)
110+
}
111+
for _, r := range tab.Combined.Tests {
112+
if r.Phase == phase && r.Name == name {
113+
return r
114+
}
115+
}
116+
t.Fatalf("row not found in combined grid: %s %s/%s", recipe, phase, name)
117+
return Row{}
118+
}
119+
103120
func findTab(t *testing.T, idx Index, recipe string) Tab {
104121
t.Helper()
105122
for _, g := range idx.Groups {
@@ -213,6 +230,23 @@ func TestGenerateEndToEnd(t *testing.T) {
213230
t.Errorf("operator-health @ v0.14.0 = %q, want FAILING (nvidia's isolated older run)", got)
214231
}
215232

233+
// Combined ("all versions") grid: each source's single latest run folded into
234+
// one grid, with an empty AICRVer because it spans versions. In this fixture
235+
// every source's newest run is v1.0.0, so combined matches the newest grid —
236+
// operator-health CONFIRMED with the rogue reported dot. (The cross-version
237+
// case where combined diverges from the newest grid is in
238+
// TestGenerateCombinedCrossVersion.)
239+
if tabA.Combined == nil {
240+
t.Fatal("recipeA has no combined grid")
241+
}
242+
if tabA.Combined.AICRVer != "" {
243+
t.Errorf("combined AICRVer = %q, want empty (spans versions)", tabA.Combined.AICRVer)
244+
}
245+
coh := findCombinedRow(t, idx, recipeA, "deployment", "operator-health")
246+
if coh.Consensus != string(StateConfirmed) || coh.Reported != 1 {
247+
t.Errorf("combined operator-health = %q reported=%d, want CONFIRMED reported=1", coh.Consensus, coh.Reported)
248+
}
249+
216250
// Recipe B: FAILING + SINGLE + bare-intent (empty platform).
217251
const recipeB = "h100-gke-cos-training"
218252
if got := findRow(t, idx, recipeB, "deployment", "operator-health").Consensus; got != string(StateFailing) {
@@ -340,6 +374,76 @@ func TestGenerateConsensusKeyedByVerifiedIdentityNotIDHash(t *testing.T) {
340374
}
341375
}
342376

377+
func TestGenerateCombinedCrossVersion(t *testing.T) {
378+
// The combined ("all versions") grid folds each source's single latest run
379+
// across versions, so two allowlisted sources whose latest runs are at
380+
// DIFFERENT AICR versions still corroborate (CONFIRMED). The strict newest
381+
// per-version grid does not: at the newest version only the source that ran it
382+
// appears (SINGLE). This is the behavior the dashboard's default view relies on.
383+
const issuer = "https://token.actions.githubusercontent.com"
384+
dir := t.TempDir()
385+
writeRun := func(identity, idHash, runID, aicrVer, when string) {
386+
t.Helper()
387+
runDir := filepath.Join(dir, "results", "eks", "h100-ubuntu", "training", idHash, runID)
388+
if err := os.MkdirAll(filepath.Join(runDir, "ctrf"), 0o755); err != nil {
389+
t.Fatal(err)
390+
}
391+
meta := fmt.Sprintf(`{"schemaVersion":"aicr-corroboration-meta/v1",`+
392+
`"coordinate":{"group":"eks","dashboard":"h100-ubuntu","tab":"training"},`+
393+
`"recipe":"h100-eks-ubuntu-training",`+
394+
`"signer":{"idHash":%q,"identity":%q,"issuer":%q,"class":"community","allowlisted":true},`+
395+
`"runId":%q,"aicrVersion":%q,"attestedAt":%q}`, idHash, identity, issuer, runID, aicrVer, when)
396+
if err := os.WriteFile(filepath.Join(runDir, "meta.json"), []byte(meta), 0o600); err != nil {
397+
t.Fatal(err)
398+
}
399+
ctrf := `{"reportFormat":"CTRF","results":{"tool":{"name":"aicr"},"summary":{},` +
400+
`"tests":[{"name":"operator-health","status":"passed"}]}}`
401+
if err := os.WriteFile(filepath.Join(runDir, "ctrf", "deployment.json"), []byte(ctrf), 0o600); err != nil {
402+
t.Fatal(err)
403+
}
404+
}
405+
// Source A's latest run is an OLDER release; source B's latest is the newest.
406+
// Two DISTINCT verified identities, so they are two distinct signers.
407+
writeRun("https://github.com/org-a/attest/.github/workflows/a.yaml@refs/heads/main", "aaa", "run-a", "v0.14.0", "2026-06-10T01:00:00Z")
408+
writeRun("https://github.com/org-b/attest/.github/workflows/b.yaml@refs/heads/main", "bbb", "run-b", "v1.0.0", "2026-06-20T01:00:00Z")
409+
410+
out := t.TempDir()
411+
if _, err := Generate(context.Background(), Options{InputDir: dir, OutputDir: out}); err != nil {
412+
t.Fatalf("Generate: %v", err)
413+
}
414+
idx := readIndex(t, filepath.Join(out, "data", "index.json"))
415+
const recipe = "h100-eks-ubuntu-training"
416+
417+
// Newest version grid is v1.0.0; only source B ran it -> SINGLE.
418+
tab := findTab(t, idx, recipe)
419+
if len(tab.Versions) == 0 || tab.Versions[0].AICRVer != "v1.0.0" {
420+
t.Fatalf("newest version = %+v, want v1.0.0 leading", tab.Versions)
421+
}
422+
if got := findRow(t, idx, recipe, "deployment", "operator-health").Consensus; got != string(StateSingle) {
423+
t.Errorf("newest-version operator-health = %q, want SINGLE (only source B at v1.0.0)", got)
424+
}
425+
426+
// Combined grid: both sources' latest runs pass -> CONFIRMED across versions,
427+
// with both sources listed and their real per-run versions preserved.
428+
if tab.Combined == nil || tab.Combined.AICRVer != "" {
429+
t.Fatalf("combined grid = %+v, want present with empty AICRVer", tab.Combined)
430+
}
431+
comb := findCombinedRow(t, idx, recipe, "deployment", "operator-health")
432+
if comb.Consensus != string(StateConfirmed) {
433+
t.Errorf("combined operator-health = %q, want CONFIRMED (two sources, latest runs across versions)", comb.Consensus)
434+
}
435+
if len(comb.Signers) != 2 {
436+
t.Fatalf("combined signers = %d, want 2 (both sources' latest runs)", len(comb.Signers))
437+
}
438+
gotVers := map[string]bool{}
439+
for _, s := range comb.Signers {
440+
gotVers[s.AICRVer] = true
441+
}
442+
if !gotVers["v0.14.0"] || !gotVers["v1.0.0"] {
443+
t.Errorf("combined signer versions = %v, want both v0.14.0 and v1.0.0 (per-source run versions preserved)", gotVers)
444+
}
445+
}
446+
343447
func TestGenerateContextCanceled(t *testing.T) {
344448
// An already-canceled context stops the walk/collect before any output is
345449
// written and surfaces as an error rather than a partial dashboard.

pkg/corroborate/model.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,14 @@ type Dashboard struct {
116116
Tabs []Tab `json:"tabs"`
117117
}
118118

119-
// Tab is one recipe (intent[-platform]). Its consensus is split per AICR
120-
// version so that corroboration only counts agreement at the SAME version
121-
// (cross-version agreement is not reproduction). Versions are newest-first;
122-
// the overview/landing summarize the newest (Versions[0]).
119+
// Tab is one recipe (intent[-platform]). Its consensus is baked two ways: the
120+
// strict per-version Versions grids (corroboration only counts agreement at the
121+
// SAME version, because cross-version agreement is not reproduction) and the
122+
// relaxed Combined grid (each source's single latest run, version-blind). The
123+
// renderer defaults to Combined ("all versions") so every source that has ever
124+
// attested the recipe is visible, and switches to a per-version grid when a
125+
// specific AICR version is selected. Versions are newest-first; a per-version
126+
// overview summarizes the newest (Versions[0]).
123127
type Tab struct {
124128
// Recipe is the overlay metadata.name (the series-file slug).
125129
Recipe string `json:"recipe"`
@@ -131,11 +135,25 @@ type Tab struct {
131135
// Versions holds one baked consensus grid per AICR version present in the
132136
// evidence, newest-first.
133137
Versions []TabVersion `json:"versions"`
138+
139+
// Combined is the cross-version "all versions" consensus grid: each distinct
140+
// signer's single latest run (version-blind) folded into one grid. It is the
141+
// dashboard's default (non-strict) view — it surfaces every source that has
142+
// attested the recipe, including sources whose latest run predates the newest
143+
// release (which the newest strict grid, Versions[0], omits — such a source
144+
// stays visible in its own version's Versions grid). Its own AICRVer is empty
145+
// because it spans versions; each row's per-signer Latest.AICRVer still carries
146+
// that source's real run version. Consensus here counts agreement ACROSS
147+
// versions, which is weaker than same-version reproduction — the renderer makes
148+
// that trade-off explicit and the strict Versions grids remain available.
149+
Combined *TabVersion `json:"combined,omitempty"`
134150
}
135151

136-
// TabVersion is one recipe's baked consensus grid for a single AICR version.
152+
// TabVersion is one recipe's baked consensus grid for a single AICR version, or
153+
// (when it is a Tab's Combined grid) the cross-version fold with an empty AICRVer.
137154
type TabVersion struct {
138-
// AICRVer is the AICR version this grid's consensus was computed at.
155+
// AICRVer is the AICR version this grid's consensus was computed at, or empty
156+
// for a cross-version Combined grid.
139157
AICRVer string `json:"aicrVer"`
140158

141159
// PhaseRollup maps each phase to its worst-first rollup state.

0 commit comments

Comments
 (0)