Skip to content

fix(arithmetization): fix weekly metrics job - #3722

Open
OlivierBBB wants to merge 7 commits into
mainfrom
3698-fix-weekly-metrics-split-jobs
Open

fix(arithmetization): fix weekly metrics job#3722
OlivierBBB wants to merge 7 commits into
mainfrom
3698-fix-weekly-metrics-split-jobs

Conversation

@OlivierBBB

Copy link
Copy Markdown
Contributor

No description provided.

OlivierBBB and others added 6 commits August 5, 2026 00:24
…rate jobs

The workflow failed on both of its first two runs, in two different ways, and both
have the same root cause: everything happened in one job and nothing was persisted
until the end.

Run 30951692211: the timeout-budget guard failed, which skipped `zkc compile
--stats` — the one step without `if: always()`. The guard compared the step caps
against a job cap that was never exposed as an input, so raising check-timeout-min
past ~290 made it unsatisfiable from the dispatch form. Cancelling then did not
work either: `if: always()` on the heavy steps opts out of cancellation, which
GitHub's own documentation warns about.

Run 30952623752: steps 1-12 all succeeded — stats ok, guest built, trace ok in
4m08s — and then the runner vanished 16 minutes into the check. Steps 14-18 never
ran despite `if: always()`, and the job's log was never finalised, so the runner
was killed rather than a step failing; the ARC pod annotations point at the cluster
reclaiming the pod. Two completed measurements were destroyed, and the fallback job
wrote a row of dashes in their place.

Each measurement now runs in its own job and uploads its result as soon as it
finishes:

  prepare      guest ELF + JSON, resolve the zkc ref to a full SHA
  stats        compile --stats                    15m cap
  trace        trace --stats                      45m cap
  trace-check  trace --stats --check             120m cap
  report       assembles whatever artifacts exist, commits
  notify       Slack on failure or cancellation, main only

So a lost runner costs one measurement rather than all of them, the three tasks
appear separately in the Actions UI with their own durations, and the fast ones no
longer wait on the slow one. The renderer needed no change: it already treats an
absent step directory as "skipped", verified against a run directory with the check
results withheld.

The guard is deleted rather than fixed. Each job now carries its own literal cap
and the input is clamped in-step to stay just below it, so the cap always fires
where the outcome can be recorded. Job-level `timeout-minutes` has no precedent in
this repo for expressions and cannot read `env` anyway, hence literals.

`always()` becomes `!cancelled()` throughout, so cancelling works. The check cap
drops from 240 to 90: on infrastructure that reclaims pods, "did not finish in 90m"
is a more reliable weekly datum than an occasional heroic completion.

The zkc binary deliberately does not travel between jobs as an artifact —
upload-artifact drops the executable bit and nothing in this repo does
download-then-execute. Instead prepare exports the resolved SHA as a job output and
each measurement builds that exact commit via install-zkc-to. Only data moves
through artifacts.

record-lost-run is removed. It existed to prove a week had been attempted, but with
per-job artifacts a partially lost run now yields a real row, and its only remaining
case is covered by the new notification. That deletes a duplicated commit-back block
and the bug that made its own commit fail. The five shared setup steps move into a
composite action, so the split costs 44 lines of workflow rather than triple the
preamble.

Signed-off-by: Olivier Bégassat <olivier.begassat@consensys.net>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Olivier Bégassat <olivier.begassat.cours@gmail.com>
…cheduled run

Pre-review audit of the split, plus the review comments on #3714. One finding is
serious and was already latent on main.

On a cron run the `inputs` context is empty, and GitHub coerces both null and false
to 0 when comparing across types — so `inputs.run-heavy != false` evaluates FALSE
every Wednesday. The scheduled run would have skipped the trace and trace+check
work entirely and, through the same expression on commit-results, never committed a
row. This repo's only other schedule+dispatch workflow guards it explicitly for the
same reason (slack-notify-e2e-runtime-report.yml:35). Both conditions now key off
`github.event_name` instead.

The remaining fixes concern what happens when a job does not complete:

  - stats now runs whenever prepare pinned a compiler, rather than being skipped by
    a failed prepare. A guest-build failure must not cost the cheapest and
    most-trended measurement.
  - trace and trace-check require prepare to have succeeded. Without the explicit
    result check they would still start — an `if:` overrides the implicit
    skip-on-failed-need — and then die on a missing artifact.
  - the guest JSON upload is gated on the build step actually succeeding, so
    `if-no-files-found: error` cannot pile a second, confusing failure on the first.
  - the guest JSON download is best-effort, and the measurement skips outright when
    the file is absent, so the renderer reads it as "skipped".
  - report is gated on prepare having produced provenance, so a run that died before
    recording anything writes no row of dashes.
  - the report checkout is no longer shallow: the commit step rebases onto
    origin/<branch>, which a depth-1 clone cannot do.

Verified by simulating every path against the renderer: a full run, the check job
evicted, a failed guest build, a failed compile, and run-heavy off. All five produce
a usable row, and the two that previously destroyed completed work now preserve it.

CodeQL's "checkout of untrusted code" alert on the measured checkout is documented
rather than silenced: the workflow has no pull_request trigger, so there is no fork
head to check out, and monorepo-ref can only be set by a dispatcher who already has
write access. The structural mitigation is recorded next to the code — the jobs that
build the measured tree never mint the App token; only report holds it, and it checks
out the tooling ref alone.

Signed-off-by: Olivier Bégassat <olivier.begassat@consensys.net>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Olivier Bégassat <olivier.begassat.cours@gmail.com>
…ure conditions

Review flagged that `report` could be skipped when a measurement job fails unless
its condition includes always(). My reading is that any explicit `if:` replaces the
implicit success() check, so `!cancelled()` alone already suffices and is what
GitHub documents as the replacement for always().

I cannot demonstrate that from a run, though, and the repo has ten job-level
precedents for `always() && !cancelled()` and none for the bare form. Since
always() is always true the two are identical, so adopting the paired form costs
nothing and removes the ambiguity for a reader. Step-level conditions keep the bare
form, which does have precedent here.

Signed-off-by: Olivier Bégassat <olivier.begassat@consensys.net>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Olivier Bégassat <olivier.begassat.cours@gmail.com>
…zkc that got built

Two review findings, both reachable and both worth fixing.

prepare publishes its zkc_ref output before it records metadata, so an eviction
between the two satisfies the report job's condition while leaving no meta.env. The
renderer treats a missing metadata file as an empty map, so it would have appended
`| – | – | – | – | – | – | – |` — a permanent row with no week, runner or refs. The
report now fails if meta.env is absent or empty: notify reports it and the ledger
stays clean. This is the same reasoning that removed record-lost-run; a row with no
identity is worse than no row.

arithmetization/Makefile's checkout-zkc-repo ends its fetch and checkout with
`|| true`. A failed fetch therefore leaves the clone on the default branch from the
initial `git clone --depth=1`, and the build silently measures a different compiler
than the one the row will claim — precisely the confusion this workflow exists to
prevent. The composite action now compares the built binary's stamped vcs.revision
against the requested SHA and fails on a mismatch, warning only when nothing was
stamped. Verified against a binary built through that very target from the Makefile's
shallow detached clone: vcs.revision matches `git rev-parse v1.2.25` exactly, so the
check has a real signal to work with.

Not fixed here: the `|| true` itself. That Makefile is shared with several workflows
that may rely on the tolerant behaviour, so tightening it belongs in its own change.

Signed-off-by: Olivier Bégassat <olivier.begassat@consensys.net>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Olivier Bégassat <olivier.begassat.cours@gmail.com>
…disable errexit

Run 30959512082 failed both measurement jobs with exit 127 while prepare succeeded
entirely. Two bugs, the second worse than the first.

GNU time is not on the runner image. setup-arithmetization-riscv apt-installs it
(line 53, alongside binutils-riscv64-unknown-elf), and the measurement jobs
deliberately skip that heavy action to avoid pulling in Zig and Rust they do not
need. Every measurement wraps its command in `/usr/bin/time -v` to capture peak RSS,
so with the binary absent the step died before running anything — hence 127, and
hence prepare being the only job that worked. The composite now installs it, guarded
so it is a no-op when already present.

The 127 then exposed the real problem. GitHub's default shell is `bash -e`, and
`set -uo pipefail` does not clear errexit — verified locally: under `bash -e`, a
block using that form exits at the failing command and writes no rc, no wall_s and
no timedout marker, whereas the same block with `set +e` records rc=124, the wall
clock and the marker. So every rc capture in this workflow was dead code on failure,
which means a timed-out trace or check recorded nothing at all. That is the outcome
the whole design exists to capture, and it could never have worked. The merged
version has the same defect.

Signed-off-by: Olivier Bégassat <olivier.begassat@consensys.net>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Olivier Bégassat <olivier.begassat.cours@gmail.com>
…path did not run

Run 30960567540 was green end to end — prepare, stats and report all succeeded and
the row rendered with full provenance — but it carried a red annotation: "Artifact
not found for name: zkc-metrics-guest". With run-heavy off, prepare never builds the
guest, so that artifact does not exist. continue-on-error meant the report still
succeeded, but an error annotation on a green run is exactly the kind of noise that
makes people stop trusting the output.

The download is now gated the same way the trace and trace-check downloads are.

Signed-off-by: Olivier Bégassat <olivier.begassat@consensys.net>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Olivier Bégassat <olivier.begassat.cours@gmail.com>
Copilot AI lite review requested due to automatic review settings August 5, 2026 09:06
Signed-off-by: Olivier Bégassat <38285177+OlivierBBB@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the weekly arithmetization “zkc metrics” automation by restructuring the GitHub Actions workflow so each measurement runs in its own job, improving resilience to runner loss/timeouts and making scheduled runs correctly execute the “heavy” measurements.

Changes:

  • Split the weekly metrics workflow into prepare, stats, trace, trace-check, and report jobs, persisting intermediate results via artifacts.
  • Add a reusable composite action (setup-zkc-measurement) to share the measurement-job preamble (checkout measured ref, install GNU time + Go, build pinned zkc).
  • Update the accompanying documentation to reflect the new execution model and failure semantics (“skipped”/“TIMEOUT” instead of “run lost” placeholder rows).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
arithmetization/src/test/scripts/README-weekly-metrics.md Updates operator-facing docs to match the new multi-job + artifact-based workflow behavior.
arithmetization/docs/metrics/zkc-weekly-metrics.md Updates ledger semantics to explain skipped/TIMEOUT interpretation and where to find failure reasons.
.github/workflows/arithmetization-weekly-zkc-metrics.yml Reworks the weekly metrics workflow into separate jobs with artifact handoff, corrected schedule-safe gating, and a reporting/notifier stage.
.github/actions/setup-zkc-measurement/action.yml Introduces a shared composite setup action to keep measurement jobs consistent and reduce duplication.
Suppressed comments (1)

.github/workflows/arithmetization-weekly-zkc-metrics.yml:474

  • trace-check is a job id containing a dash, so needs.trace-check.result is parsed as an expression (subtraction) rather than a context lookup. This condition will not evaluate as intended and can break the workflow step gating.
      - name: Download trace-check measurement
        if: ${{ needs.trace-check.result != 'skipped' }}

Copilot AI review requested due to automatic review settings August 5, 2026 09:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@OlivierBBB OlivierBBB self-assigned this Aug 5, 2026

@letypequividelespoubelles letypequividelespoubelles left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blind approval

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants