Skip to content

ci: add a manual workflow for the frame-transaction measurement harnesses - #12738

Open
AnkushinDaniil wants to merge 5 commits into
masterfrom
daniil/frame-measurement-workflow
Open

ci: add a manual workflow for the frame-transaction measurement harnesses#12738
AnkushinDaniil wants to merge 5 commits into
masterfrom
daniil/frame-measurement-workflow

Conversation

@AnkushinDaniil

Copy link
Copy Markdown
Contributor

Changes

  • Add run-frame-tx-measurements.yml, a manual workflow that runs the frame-transaction measurement harnesses on the reproducible-benchmarks runner and publishes what they report

The two harnesses (FrameTxVerifyDosMeasurement and FrameTxMempoolDosMeasurement) are [Explicit] measurement fixtures on the frame-transaction branches, so they never run in regular CI. Sizing MAX_VERIFY_GAS needs their numbers from a machine that does not move underfoot, which is what the benchmark runner is for.

The workflow is dispatch-only and takes no part in PR or push CI. It exists on master only because workflow_dispatch is not offered for a workflow that is absent from the default branch; the harnesses themselves are not on master and the run fails with a clear message if the dispatched ref does not carry them.

Output goes to the run summary and to a frame-tx-measurements artifact, so a number quoted in a spec discussion has a run behind it.

Types of changes

What types of changes does your code introduce?

  • Bugfix (a non-breaking change that fixes an issue)
  • New feature (a non-breaking change that adds functionality)
  • Breaking change (a change that causes existing functionality not to work as expected)
  • Optimization
  • Refactoring
  • Documentation update
  • Build-related changes
  • Other: Description

Testing

Requires testing

  • Yes
  • No

If yes, did you write tests?

  • Yes
  • No

Notes on testing

The invocation was checked locally against the harness branch: dotnet Nethermind.Evm.Test.dll --filter FullyQualifiedName~FrameTxVerifyDosMeasurement runs all 12 cases and prints the RESULT lines the workflow captures. A filter that matches nothing exits non-zero, so a bad ref fails the run rather than reporting an empty success.

Documentation

Requires documentation update

  • Yes
  • No

Requires explanation in Release Notes

  • Yes
  • No

@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Claude finished @AnkushinDaniil's task in 4m 58s —— View job


Review: ci: add a manual workflow for the frame-transaction measurement harnesses

  • Read the PR diff
  • Check repo workflow conventions (.agents/rules/github-workflows.md, existing workflows, action versions)
  • Verify the harness-presence check and dotnet test invocation
  • Post review

Verdict: no Critical or High findings. Three Medium items worth addressing before merge, two Low. Details are in the inline comments; summary below.

What checks out

  • Action pins match repo convention exactly (checkout@v6, setup-dotnet@v5, upload-artifact@v7 are the dominant versions in .github/workflows), and bare setup-dotnet@v5 correctly picks up the SDK from global.json.
  • dotnet test --project X.csproj -c release -- --filter FullyQualifiedName~Name is the idiom documented in AGENTS.md:124 and .agents/rules/test-infrastructure.md:46.
  • runs-on: [self-hosted, reproducible-benchmarks] matches the label used by the other four benchmark workflows.
  • concurrency without -${{ github.ref }} and with cancel-in-progress: false deviates from the rule-file default, but deliberately and correctly: it serialises dispatches on a shared measurement runner instead of cancelling them. Worth a one-line comment so a future reader doesn't "fix" it back.
  • HARNESS is a choice input consumed via env: and quoted in [[ ]] — no expression-injection surface.
  • set -euo pipefail with tee propagates the test failure, and if-no-files-found: warn is the right choice given the if: always() upload.

Findings

# Severity Location Issue
1 Medium L44-47 -f "${project}/${fixture}.cs" assumes the harness sits at the project root under exactly that filename. Nethermind.Evm.Test has 31 .cs files in subdirectories — a nested or differently-named harness fails the run claiming the ref doesn't carry it.
2 Medium L55-62 With harness: both (default), set -e aborts after a failing verify harness, so mempool never runs. The artifact then holds partial results that look complete. Note the function currently returns the status of its trailing echo "::endgroup::", so it always reports success — that has to be fixed first for any ||-based collection to work.
3 Medium L20 No permissions: block. The job needs only contents: read; it executes arbitrary code from a dispatched ref on a persistent self-hosted runner, so bounding the token is cheap insurance. Both nethermind-tests.yml and the expb workflow declare theirs.
4 Low L48-52 ::endgroup:: is skipped when the test command fails, leaving the failing output collapsed. Also suggest MTP's --no-ansi so the captured RESULT lines land clean (per the ANSI caveat in AGENTS.md).
5 Low L69-77 Whole dotnet test log is appended to $GITHUB_STEP_SUMMARY (1 MiB cap). Overflow fails the step under set -e, worst exactly when a harness misbehaves. Filter to RESULT lines or bound with tail -c; the artifact keeps the full log.

Also folded into #2: the output_dir step output is written but never read — the path is spelled three separate times, and the later steps can't use the output anyway since they are if: always().

⚠️ One correction to my own inline suggestion on L48-52: it carries a trailing } that duplicates line 53. Apply it over L48-53, or drop that brace. Flagged inline too.

I have not pushed any changes — the GitHub App token can't modify files under .github/workflows. Fix these →
· branch daniil/frame-measurement-workflow

@AnkushinDaniil
AnkushinDaniil force-pushed the daniil/frame-measurement-workflow branch from 4a840d6 to 5cc0d10 Compare August 7, 2026 18:11
Comment thread .github/workflows/run-frame-tx-measurements.yml Outdated
Comment thread .github/workflows/run-frame-tx-measurements.yml
Comment thread .github/workflows/run-frame-tx-measurements.yml
Comment thread .github/workflows/run-frame-tx-measurements.yml Outdated
Comment thread .github/workflows/run-frame-tx-measurements.yml
Comment thread .github/workflows/run-frame-tx-measurements.yml Outdated
@AnkushinDaniil

Copy link
Copy Markdown
Contributor Author

@claude re-review

@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Claude finished @AnkushinDaniil's task in 2m 55s —— View job


Re-review: ci: add a manual workflow for the frame-transaction measurement harnesses

  • Read the updated file at 836429f
  • Verify each finding from the previous round
  • Look for anything introduced by the fixes
  • Post review

Verdict: all six items from the previous round are fixed correctly. One new Medium introduced by the GITHUB_ENV refactor, plus one Low nit. Everything else is clean.

Previous findings — all resolved

# Was Now
1 -f "${project}/${fixture}.cs" missed nested harnesses find … -print -quit (L50) — layout-independent, keeps the friendly message
2 harness: both skipped the second harness after a failure set -e dropped, run_harness returns the captured rc (L59), call sites collect into status (L64-72). The ordering matters and it's right: `
3 No permissions: contents: read (L20-21)
4 ::endgroup:: skipped on failure; ANSI in the report `
5 Whole log into a 1 MiB-capped summary grep '^RESULT' with an explicit fallback (L85)
6 Unused output_dir step output, path spelled 3× Exported once to GITHUB_ENV (L35), read by both consumers

On #6 — your reasoning is correct, not a workaround. Job-level env: can only see github, needs, strategy, matrix, vars, secrets, and inputs; runner is step-scope only, so GITHUB_ENV is the right mechanism. The comment on L32-33 records that, which is exactly the kind of why worth keeping.

Also verified: set -uo pipefail (L45) — pipefail is what makes dotnet test | tee || rc=$? report the test's status rather than tee's, so dropping -e doesn't create a silent-success path. Action pins still match repo convention (checkout@v6, setup-dotnet@v5, upload-artifact@v7). --project X/X.csproj -c release -- --filter is the form in AGENTS.md:124, and both Nethermind.Evm.Test and Nethermind.TxPool.Test resolve. --no-ansi is a Microsoft.Testing.Platform built-in, and tests.props:5 sets EnableNUnitRunner, so it's accepted.

New findings

# Severity Location Issue
1 Medium L29-35 Resolve the output directory now runs after checkout. If checkout fails it is skipped, and the if: always() upload at L95 interpolates an unset env.OUTPUT_DIR to the empty string — path: /, globbing the whole filesystem of a persistent self-hosted runner. Moving the step above the checkout closes it; it has no dependency on the checkout.
2 Low L85 ^RESULT bets on column 0, which NUnit-under-MTP output capture doesn't always give. Also L57 tees stdout only, so stderr is missing from the artifact. Both degrade gracefully thanks to the `

Finding 1 is a one-line reorder and is the only thing I'd hold the merge for.

One optional nit carried over: the deliberate concurrency deviation (no -${{ github.ref }}, cancel-in-progress: false, against the default in .agents/rules/github-workflows.md:8) is still uncommented. It's the correct choice for serialising dispatches on a shared measurement runner — a short comment would stop someone "fixing" it back.

Fix finding 1 →

I have not pushed any changes — the GitHub App token cannot modify files under .github/workflows.
· branch daniil/frame-measurement-workflow

Comment thread .github/workflows/run-frame-tx-measurements.yml Outdated
Comment thread .github/workflows/run-frame-tx-measurements.yml Outdated
@AnkushinDaniil

Copy link
Copy Markdown
Contributor Author

@claude re-review

@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Claude finished @AnkushinDaniil's task in 1m 49s —— View job


Re-review: ci: add a manual workflow for the frame-transaction measurement harnesses

  • Read the updated file at 96bb8d9
  • Verify the two findings from the last round
  • Check for anything new
  • Post review

Verdict: both findings from the last round are fixed, and no Critical/High/Medium remains. One Low residual, plus the still-uncommented concurrency nit. Good to merge as far as I'm concerned.

Previous findings — resolved

# Was Now
1 (Medium) Resolve the output directory ran after checkout; a checkout failure left OUTPUT_DIR unset and the if: always() upload globbed / Resolved first (L33-34), before anything that can fail. The comment on L29-32 records why the ordering matters, not just that it exists — that's the version worth keeping
2 (Low) ^RESULT anchored at column 0; the report captured stdout only grep 'RESULT' (L87) and 2>&1 | tee (L59)

Re-verified from the round before, since the file moved again: find … -print -quit presence check (L52) is still layout-independent; run_harness still returns the captured rc (L61) so || status=1 at L68/L71 actually collects, and harness: both runs both harnesses before exiting non-zero; permissions: contents: read (L20-21) is intact; --no-ansi (L58) is intact. set -uo pipefail without -e is still the right combination — pipefail is what makes the | tee report the test's status rather than tee's, and dropping -e is what lets the second harness run.

Also checked: action pins match the repo majority (checkout@v6 ×88, upload-artifact@v7 ×33), bare setup-dotnet@v5 matches nethermind-tests.yml:142 and picks the SDK up from global.json, and runs-on: [self-hosted, reproducible-benchmarks] matches the label .agents/rules/github-workflows.md names for benchmark jobs.

mkdir -p "${OUTPUT_DIR}" without a prior clean is fine — the runner empties RUNNER_TEMP at the start of each job, so a verify-only dispatch cannot pick up a stale mempool report from a previous run.

Remaining

# Severity Location Issue
1 Low L92-97 if: always() fires on cancellation too, so a dispatch cancelled before L34 still leaves env.OUTPUT_DIR empty and uploads path: /. A few-second window on a 120-minute job; if: always() && env.OUTPUT_DIR != '' closes it and keeps the invariant next to the step that depends on it.
2 Nit L16-18 The deliberate concurrency deviation — no -${{ github.ref }}, cancel-in-progress: false, against the default in .agents/rules/github-workflows.md:8 — is still uncommented. It is the right call for serialising dispatches on a shared measurement runner; one line would stop someone "fixing" it back to the PR-workflow default and having two measurement runs contend for the box.

Neither blocks the merge.

I have not pushed any changes — the GitHub App token cannot modify files under .github/workflows.
· branch daniil/frame-measurement-workflow

Comment thread .github/workflows/run-frame-tx-measurements.yml
The concurrency group intentionally omits the ref and keeps in-progress runs alive so dispatches queue for the single shared measurement runner instead of cancelling each other.
@AnkushinDaniil

Copy link
Copy Markdown
Contributor Author

Superseded by #12939, which re-bases this onto eip8141-frame-txs-devnet7 (the frame-tx family base). Same workflow file, no content change.

return 1
fi
echo "::group::${fixture}"
dotnet test --project "${project}/${project}.csproj" -c release -- \

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.

dotnet test builds in-job, which leaves MSBuild worker nodes and VBCSCompiler resident for their idle timeout — right through the measurement, on a box whose whole point is reproducible timing, and then into whatever job the runner takes next. Building first, running dotnet build-server shutdown, then testing with --no-build --no-restore keeps them out of that window. nethermind-tests.yml:161 already does the shutdown.

dotnet build "${project}/${project}.csproj" -c release || return 1
dotnet build-server shutdown || true
dotnet test --project "${project}/${project}.csproj" -c release --no-build --no-restore --output Detailed -- \
--no-ansi --filter "FullyQualifiedName~${fixture}" \
2>&1 | tee "${OUTPUT_DIR}/${fixture}.txt" || rc=$?

HARNESS: ${{ inputs.harness }}
run: |
set -uo pipefail
mkdir -p "${OUTPUT_DIR}"

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.

The output directory is a fixed path on a persistent runner and is only mkdir -p'd, never cleaned. If a report file survives an earlier dispatch, a harness: mempool run republishes the previous verify report in its summary and artifact as if it had just been measured — the summary loop globs every *.txt and the upload takes the whole directory. An rm -rf before the mkdir -p removes the possibility entirely.

set -uo pipefail
rm -rf "${OUTPUT_DIR}"
mkdir -p "${OUTPUT_DIR}"

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants