From 393383df2bd735b9b9f6d44871dc69c13d1649b9 Mon Sep 17 00:00:00 2001 From: Ciprian Goea Date: Wed, 12 Aug 2026 11:52:58 +0300 Subject: [PATCH 1/7] fix: _MATRIX_JOB_RE should be lower/uppercase agnostic --- .../tests/therock_workflow_registry_test.py | 1 - .../therock_update_status_json.py | 33 ++++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/scripts/receive_therock/tests/therock_workflow_registry_test.py b/scripts/receive_therock/tests/therock_workflow_registry_test.py index a17637b7..6ef55175 100644 --- a/scripts/receive_therock/tests/therock_workflow_registry_test.py +++ b/scripts/receive_therock/tests/therock_workflow_registry_test.py @@ -79,7 +79,6 @@ "publish_no_rocm_image_ubuntu24_04_rocgdb.yml", # Upstream test scaffolding (not Quartz-reported). "test_artifacts_structure.yml", - "test_component.yml", "test_jax_dockerfile.yml", # Quartz's own local plumbing (not a rock producer workflow). "sync_develop_to_main.yml", diff --git a/scripts/receive_therock/therock_update_status_json.py b/scripts/receive_therock/therock_update_status_json.py index 1e004eb2..60ad7696 100644 --- a/scripts/receive_therock/therock_update_status_json.py +++ b/scripts/receive_therock/therock_update_status_json.py @@ -326,8 +326,14 @@ def _update_document_metadata( # Matrix-cell job name for fan-out builds, e.g. TheRock's # "Build | py 3.12 | torch release/2.10" (pytorch) # "Build | py 3.12 | jax rocm-jaxlib-v0.9" (jax) -# `.search` (not fullmatch) so a reusable-workflow prefix/suffix still matches. -_MATRIX_JOB_RE = re.compile(r"py\s+(?P\S+)\s*\|\s*(?:torch|jax)\s+(?P\S+)") +# Case-insensitive: a calling orchestrator's own composite job name can wrap +# this in a differently-cased ancestor segment, e.g. rockrel's +# "Release | py 3.12 | JAX 0.11.0 / Build | py 3.12 | jax rocm-jaxlib-v0.11.0" +# -- the nested Test sub-job under that same cell has no (py, ref) of its own +# and relies entirely on that ancestor segment to be recognized. +_MATRIX_JOB_RE = re.compile( + r"py\s+(?P\S+)\s*\|\s*(?:torch|jax)\s+(?P\S+)", re.IGNORECASE +) # pipeline_type -> the matrix axis key used in the variant (reference schema: # pytorch cells key the ref as "torch", jax cells as "jax_ref"). @@ -366,9 +372,17 @@ def _variants_from_jobs( cells: dict[tuple[str, str], list[WorkflowJobRecord]] = {} order: list[tuple[str, str]] = [] for j in jobs: - match = _MATRIX_JOB_RE.search(j.name) - if not match: + # Take the *last* match, not the first: a nested job's composite name + # is "ancestor segment(s) / ... / own segment", and the own segment + # (closest to the actual job) is the authoritative (py, ref) -- e.g. + # a build job's own tail carries the full ref, while an orchestrator + # ancestor segment upstream of it may carry a shorter/looser one. A + # job with no segment of its own (a nested test sub-job) falls back + # to whichever ancestor segment matched. + matches = list(_MATRIX_JOB_RE.finditer(j.name)) + if not matches: continue + match = matches[-1] key = (match.group("py"), match.group("ref")) if key not in cells: cells[key] = [] @@ -477,6 +491,14 @@ def _refresh_same_run_fanout_tests( leaves while some matrix cells are still in progress; the final top-level completion is classified as the build phase, so it would otherwise leave those same-run test leaves stale. + + `leaf.status` is the *build* run's own top-level GitHub conclusion, which + is not necessarily the worst-of its `variants` (e.g. a matrix cell whose + nested test job failed/cancelled does not always flip the run's own + conclusion). Projected test leaves must not inherit that raw status + verbatim -- they re-derive their status from the same variants they are + given, exactly like `_merge_variant_leaf` does for every other + variant-carrying leaf. """ cls = workflow_run.classification if ( @@ -488,6 +510,7 @@ def _refresh_same_run_fanout_tests( ): return False + projected_status = Variant.rollup_status(leaf.variants, leaf.status) pipeline = getattr(doc.pipelines, cls.pipeline_type) wrote = False for phase_map in (pipeline.test, pipeline.test_full): @@ -499,7 +522,7 @@ def _refresh_same_run_fanout_tests( continue if not existing.should_replace(leaf): continue - existing.status = leaf.status + existing.status = projected_status existing.completed_at = leaf.completed_at existing.variants = leaf.variants wrote = True From 927d15ceefa146164c7d60118bf7c8504deb5163 Mon Sep 17 00:00:00 2001 From: Ciprian Goea Date: Wed, 12 Aug 2026 13:15:38 +0300 Subject: [PATCH 2/7] add regression tests for matrix-job fixes --- .../tests/therock_update_status_json_test.py | 82 +++++++++++++++++++ .../tests/therock_workflow_registry_test.py | 3 + 2 files changed, 85 insertions(+) diff --git a/scripts/receive_therock/tests/therock_update_status_json_test.py b/scripts/receive_therock/tests/therock_update_status_json_test.py index c1f4b920..8f2f363f 100644 --- a/scripts/receive_therock/tests/therock_update_status_json_test.py +++ b/scripts/receive_therock/tests/therock_update_status_json_test.py @@ -1599,3 +1599,85 @@ def test_completed_fanout_build_refreshes_same_run_test_leaves() -> None: assert leaf.completed_at == "2026-06-19T15:18:00Z" assert leaf.variants is not None assert leaf.variants[0].status is Status.success + + +def test_fanout_projection_uses_variant_rollup_not_raw_run_conclusion() -> None: + # The build run's own top-level GitHub conclusion is not necessarily the + # worst-of its matrix cells (e.g. a cell whose nested test job failed does + # not always flip the run's own conclusion). A projected test leaf must + # take the worst-of its own variants, not the raw run conclusion. + doc = StatusDocument() + stale_test = _variant_run( + pipeline_type="pytorch", + pipeline_phase="test", + architectures=["gfx110X-all"], + run_id=902, + conclusion=None, + jobs=[ + _job("Build | py 3.12 | torch release/2.10 / Build"), + _job( + "Build | py 3.12 | torch release/2.10 / Test | gfx110X-all", + conclusion=None, + completed=None, + ), + ], + ) + doc.upsert_leaf( + "linux", "gfx110X-all", "pytorch", "test", tusj._create_leaf(stale_test) + ) + + # The run's own conclusion reports success even though its nested test + # job for this cell failed. + completed_build = _variant_run( + pipeline_type="pytorch", + pipeline_phase="build", + run_id=902, + conclusion="success", + jobs=[ + _job("Build | py 3.12 | torch release/2.10 / Build"), + _job( + "Build | py 3.12 | torch release/2.10 / Test | gfx110X-all", + conclusion="failure", + ), + ], + ) + completed_build.classification.platform = "windows" + tusj._merge_run_into_document( + doc, completed_build, tusj._create_leaf(completed_build) + ) + + leaf = doc.pipelines.pytorch.test["linux"]["gfx110X-all"] + assert leaf.status is Status.failure + assert leaf.variants is not None + assert leaf.variants[0].status is Status.failure + + +def test_variant_job_name_matches_uppercase_ancestor_segment() -> None: + # A calling orchestrator (e.g. rockrel) can wrap TheRock's own build job + # in a differently-cased ancestor segment, e.g. + # "Release | py 3.12 | JAX 0.11.0 / Build | py 3.12 | jax rocm-jaxlib-v0.11.0" + # The build job's own tail (lowercase, full ref) must still win over that + # ancestor. A nested test sub-job has no (py, ref) segment of its own and + # must fall back to the uppercase ancestor instead of being dropped. + run = _variant_run( + pipeline_type="jax", + pipeline_phase="build", + jobs=[ + _job( + "build_jax_wheels / Release | py 3.12 | JAX 0.11.0 / " + "Build | py 3.12 | jax rocm-jaxlib-v0.11.0" + ), + _job( + "build_jax_wheels / Release | py 3.13 | JAX 0.11.0 / " + "Test | gfx94X-dcgpu | linux-gfx942-1gpu-ccs-csp-ossci-rocm / " + "Test JAX | gfx94X-dcgpu", + conclusion="cancelled", + ), + ], + ) + variants = tusj._derive_variants(run) + by_py = {v.matrix["py"]: v for v in variants} + assert by_py["3.12"].matrix["jax_ref"] == "rocm-jaxlib-v0.11.0" + assert by_py["3.12"].status is Status.success + assert by_py["3.13"].matrix["jax_ref"] == "0.11.0" + assert by_py["3.13"].status is Status.cancelled diff --git a/scripts/receive_therock/tests/therock_workflow_registry_test.py b/scripts/receive_therock/tests/therock_workflow_registry_test.py index 6ef55175..7e4d3b63 100644 --- a/scripts/receive_therock/tests/therock_workflow_registry_test.py +++ b/scripts/receive_therock/tests/therock_workflow_registry_test.py @@ -79,6 +79,9 @@ "publish_no_rocm_image_ubuntu24_04_rocgdb.yml", # Upstream test scaffolding (not Quartz-reported). "test_artifacts_structure.yml", + # Registered separately in users/cgoea/fix_test_artifacts; excluded + # here so this branch stays green standalone. + "test_component.yml", "test_jax_dockerfile.yml", # Quartz's own local plumbing (not a rock producer workflow). "sync_develop_to_main.yml", From 6f2438a17469ee2cb7cae00c50453d57f9fb56c8 Mon Sep 17 00:00:00 2001 From: Ciprian Goea Date: Thu, 13 Aug 2026 15:51:29 +0300 Subject: [PATCH 3/7] fix: fold run conclusion into fanout test rollup; drop stale comment Same-run test-leaf projection ignored the build run's own terminal conclusion unless variants were empty, so a top-level cancellation/ failure could be masked by whatever individual matrix cells happened to report. Fold leaf.status into the rollup instead of only using it as an empty-variants fallback. Also drop a stale comment left over from a rebase referencing an unrelated branch, and clarify the same-run fanout docstring (fix a reference to a function that doesn't exist, and explain why build and test jobs share one job list for these delegated PyTorch/JAX runs). --- .../tests/therock_update_status_json_test.py | 46 +++++++++++++++++++ .../tests/therock_workflow_registry_test.py | 2 - .../therock_update_status_json.py | 41 +++++++++++------ 3 files changed, 73 insertions(+), 16 deletions(-) diff --git a/scripts/receive_therock/tests/therock_update_status_json_test.py b/scripts/receive_therock/tests/therock_update_status_json_test.py index 8f2f363f..6ff58dd0 100644 --- a/scripts/receive_therock/tests/therock_update_status_json_test.py +++ b/scripts/receive_therock/tests/therock_update_status_json_test.py @@ -1652,6 +1652,52 @@ def test_fanout_projection_uses_variant_rollup_not_raw_run_conclusion() -> None: assert leaf.variants[0].status is Status.failure +def test_fanout_projection_folds_raw_run_conclusion_into_rollup() -> None: + # The inverse of the case above: every reported cell looks clean, but the + # run itself was cancelled (e.g. a cell whose job never even started, so + # it never shows up in `variants` at all). The projected test leaf must + # still surface that cancellation rather than reporting the variants' + # all-success rollup verbatim. + doc = StatusDocument() + stale_test = _variant_run( + pipeline_type="pytorch", + pipeline_phase="test", + architectures=["gfx110X-all"], + run_id=903, + conclusion=None, + jobs=[ + _job( + "Build | py 3.12 | torch release/2.10 / Test | gfx110X-all", + conclusion=None, + completed=None, + ), + ], + ) + doc.upsert_leaf( + "linux", "gfx110X-all", "pytorch", "test", tusj._create_leaf(stale_test) + ) + + cancelled_build = _variant_run( + pipeline_type="pytorch", + pipeline_phase="build", + run_id=903, + conclusion="cancelled", + jobs=[ + _job( + "Build | py 3.12 | torch release/2.10 / Test | gfx110X-all", + conclusion="success", + ), + ], + ) + cancelled_build.classification.platform = "windows" + tusj._merge_run_into_document( + doc, cancelled_build, tusj._create_leaf(cancelled_build) + ) + + leaf = doc.pipelines.pytorch.test["linux"]["gfx110X-all"] + assert leaf.status is Status.cancelled + + def test_variant_job_name_matches_uppercase_ancestor_segment() -> None: # A calling orchestrator (e.g. rockrel) can wrap TheRock's own build job # in a differently-cased ancestor segment, e.g. diff --git a/scripts/receive_therock/tests/therock_workflow_registry_test.py b/scripts/receive_therock/tests/therock_workflow_registry_test.py index 7e4d3b63..a17637b7 100644 --- a/scripts/receive_therock/tests/therock_workflow_registry_test.py +++ b/scripts/receive_therock/tests/therock_workflow_registry_test.py @@ -79,8 +79,6 @@ "publish_no_rocm_image_ubuntu24_04_rocgdb.yml", # Upstream test scaffolding (not Quartz-reported). "test_artifacts_structure.yml", - # Registered separately in users/cgoea/fix_test_artifacts; excluded - # here so this branch stays green standalone. "test_component.yml", "test_jax_dockerfile.yml", # Quartz's own local plumbing (not a rock producer workflow). diff --git a/scripts/receive_therock/therock_update_status_json.py b/scripts/receive_therock/therock_update_status_json.py index 60ad7696..b335ca68 100644 --- a/scripts/receive_therock/therock_update_status_json.py +++ b/scripts/receive_therock/therock_update_status_json.py @@ -486,19 +486,30 @@ def _refresh_same_run_fanout_tests( ) -> bool: """Refresh same-run test leaves from a completed fan-out workflow snapshot. - Delegated PyTorch/JAX release workflows report the shared entry run id. - Early notifications can project the run's job list into per-arch test - leaves while some matrix cells are still in progress; the final top-level - completion is classified as the build phase, so it would otherwise leave - those same-run test leaves stale. - - `leaf.status` is the *build* run's own top-level GitHub conclusion, which - is not necessarily the worst-of its `variants` (e.g. a matrix cell whose - nested test job failed/cancelled does not always flip the run's own - conclusion). Projected test leaves must not inherit that raw status - verbatim -- they re-derive their status from the same variants they are - given, exactly like `_merge_variant_leaf` does for every other - variant-carrying leaf. + PyTorch/JAX test coverage (`test_pytorch_wheels.yml` / `test_linux_jax_wheels.yml`) + is invoked as a reusable `workflow_call` nested inside the delegated release + workflow -- not dispatched as its own top-level run -- so its jobs land in + the *same* run id, job list, and webhook notifications as the entry build. + There is no job-name-level split between "build" and "test" jobs: the + registry classifies the whole run as `pipeline_type`/`pipeline_phase="build"` + (see `WORKFLOW_SPECS`), and `_variants_from_jobs` already groups every job + sharing a (py, ref) cell -- build and nested test alike -- into one + `Variant` per cell (see `test_reusable_matrix_nested_jobs_collapse_to_one_variant_per_cell`). + Early notifications can project that job-list snapshot into per-arch test + leaves (keyed by the same run id) while some cells are still in progress; + the final notification is still classified as the build phase, so without + this function those same-run test leaves would go stale once the build + itself is done. + + `leaf.status` is this run's own top-level GitHub conclusion. It is not + necessarily the worst-of its `variants` (e.g. a matrix cell whose nested + test job failed/cancelled does not always flip the run's own conclusion, + or a cell can be entirely missing from `variants` if its job never + started). Fold it into the rollup rather than only using it as an + empty-variants fallback, so a terminal failure/cancellation at the run + level cannot be masked by whatever the individual cells happened to report + -- mirroring what `_merge_matrix_build_leaf` does for the build leaf + itself. """ cls = workflow_run.classification if ( @@ -510,7 +521,9 @@ def _refresh_same_run_fanout_tests( ): return False - projected_status = Variant.rollup_status(leaf.variants, leaf.status) + projected_status = rollup_statuses( + (*(v.status for v in leaf.variants), leaf.status), leaf.status + ) pipeline = getattr(doc.pipelines, cls.pipeline_type) wrote = False for phase_map in (pipeline.test, pipeline.test_full): From c19a9e17ab4a0c25501c1538f1c39fa2428b4a7f Mon Sep 17 00:00:00 2001 From: Ciprian Goea Date: Thu, 13 Aug 2026 16:03:35 +0300 Subject: [PATCH 4/7] address comments from reviewer --- .../receive_therock/therock_update_status_json.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/scripts/receive_therock/therock_update_status_json.py b/scripts/receive_therock/therock_update_status_json.py index b335ca68..aa85bb08 100644 --- a/scripts/receive_therock/therock_update_status_json.py +++ b/scripts/receive_therock/therock_update_status_json.py @@ -486,21 +486,6 @@ def _refresh_same_run_fanout_tests( ) -> bool: """Refresh same-run test leaves from a completed fan-out workflow snapshot. - PyTorch/JAX test coverage (`test_pytorch_wheels.yml` / `test_linux_jax_wheels.yml`) - is invoked as a reusable `workflow_call` nested inside the delegated release - workflow -- not dispatched as its own top-level run -- so its jobs land in - the *same* run id, job list, and webhook notifications as the entry build. - There is no job-name-level split between "build" and "test" jobs: the - registry classifies the whole run as `pipeline_type`/`pipeline_phase="build"` - (see `WORKFLOW_SPECS`), and `_variants_from_jobs` already groups every job - sharing a (py, ref) cell -- build and nested test alike -- into one - `Variant` per cell (see `test_reusable_matrix_nested_jobs_collapse_to_one_variant_per_cell`). - Early notifications can project that job-list snapshot into per-arch test - leaves (keyed by the same run id) while some cells are still in progress; - the final notification is still classified as the build phase, so without - this function those same-run test leaves would go stale once the build - itself is done. - `leaf.status` is this run's own top-level GitHub conclusion. It is not necessarily the worst-of its `variants` (e.g. a matrix cell whose nested test job failed/cancelled does not always flip the run's own conclusion, From d9ecc9916f5243b752b26017eaf1c96ee8c8027a Mon Sep 17 00:00:00 2001 From: Ciprian Goea Date: Thu, 13 Aug 2026 16:10:56 +0300 Subject: [PATCH 5/7] make comment more clear --- .../receive_therock/therock_update_status_json.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/receive_therock/therock_update_status_json.py b/scripts/receive_therock/therock_update_status_json.py index aa85bb08..b335ca68 100644 --- a/scripts/receive_therock/therock_update_status_json.py +++ b/scripts/receive_therock/therock_update_status_json.py @@ -486,6 +486,21 @@ def _refresh_same_run_fanout_tests( ) -> bool: """Refresh same-run test leaves from a completed fan-out workflow snapshot. + PyTorch/JAX test coverage (`test_pytorch_wheels.yml` / `test_linux_jax_wheels.yml`) + is invoked as a reusable `workflow_call` nested inside the delegated release + workflow -- not dispatched as its own top-level run -- so its jobs land in + the *same* run id, job list, and webhook notifications as the entry build. + There is no job-name-level split between "build" and "test" jobs: the + registry classifies the whole run as `pipeline_type`/`pipeline_phase="build"` + (see `WORKFLOW_SPECS`), and `_variants_from_jobs` already groups every job + sharing a (py, ref) cell -- build and nested test alike -- into one + `Variant` per cell (see `test_reusable_matrix_nested_jobs_collapse_to_one_variant_per_cell`). + Early notifications can project that job-list snapshot into per-arch test + leaves (keyed by the same run id) while some cells are still in progress; + the final notification is still classified as the build phase, so without + this function those same-run test leaves would go stale once the build + itself is done. + `leaf.status` is this run's own top-level GitHub conclusion. It is not necessarily the worst-of its `variants` (e.g. a matrix cell whose nested test job failed/cancelled does not always flip the run's own conclusion, From ca6495d67da034fa617bec86501c761d017891cd Mon Sep 17 00:00:00 2001 From: Ciprian Goea Date: Thu, 13 Aug 2026 18:36:05 +0300 Subject: [PATCH 6/7] test: drop unnecessary platform mismatch, clarify the deliberate one test_fanout_projection_folds_raw_run_conclusion_into_rollup doesn't need a platform mismatch to make its point (that's already covered by test_completed_fanout_build_refreshes_same_run_test_leaves), and it read as if a windows run could leak into a linux leaf. Drop it there, and add a comment on the older test explaining why the mismatch is intentional where it remains. --- .../tests/therock_update_status_json_test.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/receive_therock/tests/therock_update_status_json_test.py b/scripts/receive_therock/tests/therock_update_status_json_test.py index 6ff58dd0..eaf7251a 100644 --- a/scripts/receive_therock/tests/therock_update_status_json_test.py +++ b/scripts/receive_therock/tests/therock_update_status_json_test.py @@ -1589,6 +1589,12 @@ def test_completed_fanout_build_refreshes_same_run_test_leaves() -> None: _job("Build | py 3.12 | torch release/2.10 / Test | gfx110X-all"), ], ) + # Deliberately mismatched vs. the linux leaf above: this proves the match + # is keyed on (run_id, run_attempt) alone, not platform. That's safe in + # practice -- GitHub's run_id is unique per repository across every + # workflow/platform, so a real linux and windows run can never collide on + # one -- but it's an isolation technique, not a model of real data; don't + # read it as "a windows run can update a linux leaf" in production. completed_build.classification.platform = "windows" tusj._merge_run_into_document( doc, completed_build, tusj._create_leaf(completed_build) @@ -1677,6 +1683,11 @@ def test_fanout_projection_folds_raw_run_conclusion_into_rollup() -> None: "linux", "gfx110X-all", "pytorch", "test", tusj._create_leaf(stale_test) ) + # Same platform as the stale leaf above: this test is about the + # cancellation-folding logic, not about the (run_id, run_attempt)-only + # matching (already covered by + # test_completed_fanout_build_refreshes_same_run_test_leaves), so it + # doesn't need a platform mismatch to make its point. cancelled_build = _variant_run( pipeline_type="pytorch", pipeline_phase="build", @@ -1689,7 +1700,6 @@ def test_fanout_projection_folds_raw_run_conclusion_into_rollup() -> None: ), ], ) - cancelled_build.classification.platform = "windows" tusj._merge_run_into_document( doc, cancelled_build, tusj._create_leaf(cancelled_build) ) From 7e45584ac970d79c1c13f3310b94c57ea2c3d2ea Mon Sep 17 00:00:00 2001 From: Ciprian Goea Date: Thu, 13 Aug 2026 18:36:05 +0300 Subject: [PATCH 7/7] test: clarify the deliberate platform mismatch --- .../tests/therock_update_status_json_test.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/receive_therock/tests/therock_update_status_json_test.py b/scripts/receive_therock/tests/therock_update_status_json_test.py index 6ff58dd0..eaf7251a 100644 --- a/scripts/receive_therock/tests/therock_update_status_json_test.py +++ b/scripts/receive_therock/tests/therock_update_status_json_test.py @@ -1589,6 +1589,12 @@ def test_completed_fanout_build_refreshes_same_run_test_leaves() -> None: _job("Build | py 3.12 | torch release/2.10 / Test | gfx110X-all"), ], ) + # Deliberately mismatched vs. the linux leaf above: this proves the match + # is keyed on (run_id, run_attempt) alone, not platform. That's safe in + # practice -- GitHub's run_id is unique per repository across every + # workflow/platform, so a real linux and windows run can never collide on + # one -- but it's an isolation technique, not a model of real data; don't + # read it as "a windows run can update a linux leaf" in production. completed_build.classification.platform = "windows" tusj._merge_run_into_document( doc, completed_build, tusj._create_leaf(completed_build) @@ -1677,6 +1683,11 @@ def test_fanout_projection_folds_raw_run_conclusion_into_rollup() -> None: "linux", "gfx110X-all", "pytorch", "test", tusj._create_leaf(stale_test) ) + # Same platform as the stale leaf above: this test is about the + # cancellation-folding logic, not about the (run_id, run_attempt)-only + # matching (already covered by + # test_completed_fanout_build_refreshes_same_run_test_leaves), so it + # doesn't need a platform mismatch to make its point. cancelled_build = _variant_run( pipeline_type="pytorch", pipeline_phase="build", @@ -1689,7 +1700,6 @@ def test_fanout_projection_folds_raw_run_conclusion_into_rollup() -> None: ), ], ) - cancelled_build.classification.platform = "windows" tusj._merge_run_into_document( doc, cancelled_build, tusj._create_leaf(cancelled_build) )