Skip to content

Fix Bencher reporting permanently broken on pushes to main#3148

Open
alexeyr-ci2 wants to merge 1 commit intomainfrom
alexeyr/2546/Fix-benchmark-workflow
Open

Fix Bencher reporting permanently broken on pushes to main#3148
alexeyr-ci2 wants to merge 1 commit intomainfrom
alexeyr/2546/Fix-benchmark-workflow

Conversation

@alexeyr-ci2
Copy link
Copy Markdown
Collaborator

@alexeyr-ci2 alexeyr-ci2 commented Apr 15, 2026

Summary

The benchmark workflow passed --start-point main --start-point-hash <github.event.before> for push-to-main events. Since main IS the base branch, Bencher tried to look up a version of main at the "before" hash — which often didn't exist (e.g., docs-only commits skipped by paths-ignore). This caused a 404, the report was never stored, and subsequent pushes also failed because their "before" hash was also missing. This cascading failure meant no main data was stored between the first version (Jan 18) and the renaming of master to main (Mar 21), and then many more runs were missing.

Fix: don't pass --start-point args for pushes to main (thresholds are defined inline via --threshold-* args). For PRs/dispatch where the start-point hash may be missing, retry without --start-point-hash so the report still gets stored using the latest available baseline.

Pull Request checklist

- [ ] Add/update test to cover these changes
- [ ] Update documentation
- [ ] Update CHANGELOG file

Other Information

Rebuilt historical data on Bencher starting from a month ago (earlier data is not available; in principle it could be done by rerunning benchmarks on old commits, but it would take a long time and require re-deleting the current data).

Summary by CodeRabbit

  • Chores
    • Simplified benchmark workflow branching and start-point handling to reduce duplication and clarify behavior.
  • Bug Fixes
    • Added a targeted retry path when a pinned baseline is missing, improving reliability by falling back to the latest baseline and ensuring proper exit/code reporting.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d2011ded-e8d6-46ae-9eee-55bca74758b1

📥 Commits

Reviewing files that changed from the base of the PR and between b6c4ada and f4cc4c8.

📒 Files selected for processing (1)
  • .github/workflows/benchmark.yml

Walkthrough

GitHub Actions workflow updated to compute a single START_POINT_ARGS per event, extract Bencher invocation into run_bencher(), and replace 404-based suppression with conditional retry logic that strips --start-point-hash when a pinned baseline hash is not found.

Changes

Cohort / File(s) Summary
Workflow logic & retry behavior
.github/workflows/benchmark.yml
Consolidated start-point arguments into START_POINT_ARGS; introduced run_bencher() to centralize Bencher invocation and shared flags; removed prior 404-based exit-code override and added conditional retry that reruns Bencher without --start-point-hash when stderr matches the "Head Version ... not found" pattern; retained stderr printing and exporting of BENCHER_EXIT_CODE.

Sequence Diagram

sequenceDiagram
    participant GHA as GitHub Actions
    participant Bencher as Bencher CLI
    participant API as Git/GitHub API

    alt push to main
        Note over GHA: START_POINT_ARGS = (empty)
    else pull_request / workflow_dispatch
        Note over GHA: START_POINT_ARGS = --start-point ... --start-point-hash HASH ...
    end

    GHA->>Bencher: run_bencher(START_POINT_ARGS)
    Bencher->>API: request baseline (may include HASH)
    API-->>Bencher: response (baseline or not found)
    alt Baseline found
        Bencher-->>GHA: success (exit 0)
    else Baseline not found (stderr indicates pinned hash missing)
        Bencher-->>GHA: stderr captured, non-zero exit
        GHA->>GHA: compute retry args (strip --start-point-hash if present)
        alt retry args differ
            GHA->>Bencher: run_bencher(retry args)
            Bencher->>API: request baseline (without HASH)
            API-->>Bencher: response (latest baseline)
            Bencher-->>GHA: success/fail
        end
    end

    GHA->>GHA: print stderr, export BENCHER_EXIT_CODE
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A rabbit nibbles at the CI line,
Bundles flags into one tidy sign,
If a hash goes missing in the chase,
I strip it off and try the base. 🐇✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically addresses the main problem: Bencher reporting being broken on pushes to main, which is the core issue described in the PR objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch alexeyr/2546/Fix-benchmark-workflow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 15, 2026

Code Review

Overall: This is a well-targeted fix. The root cause (passing --start-point main --start-point-hash <before> for push-to-main, where <before> was often unbenchmarked) is correctly identified and removed. The function refactor reduces substantial duplication. A few observations below.


Correctness

Root cause fix — correct. For push events, main IS the base branch, so --start-point args serve no purpose and the <before> SHA will frequently be absent from Bencher (docs-only or skipped runs), causing cascading 404 failures. Removing them entirely is the right call.

Retry logic — good improvement over the previous "swallow 404" approach. Instead of silently ignoring failures, the PR now retries with a degraded-but-functional baseline. The guard "$RETRY_ARGS" != "$START_POINT_ARGS" correctly prevents a pointless retry when no hash was in the args to begin with.

workflow_dispatch behaviour change — the new code adds --start-point-clone-thresholds and --start-point-reset for dispatch runs on non-main branches, which the old code omitted. This is likely an intentional improvement (dispatch now matches PR behaviour), but it's worth explicitly confirming this is desired since it's a silent semantic change.


Concerns

1. Fragile error-message matching for retry trigger (line 647)

grep -q "Head Version" "$BENCHER_STDERR" && grep -q "not found" "$BENCHER_STDERR"

The retry only fires when Bencher's stderr contains exactly these two substrings. If Bencher ever changes its error format, the retry silently stops working and the old 404-swallowing behaviour is gone too — meaning the run just fails. Consider adding a comment citing the specific Bencher error string you observed (e.g. "Head Version '...' not found") so future maintainers can re-verify if the retry breaks.

2. Unquoted $sp_args (line 597)

$sp_args \

The # shellcheck disable=SC2086 suppression is correct — word-splitting is intentional here. However, this pattern is brittle: if any future arg value ever contains a space (e.g. a branch name with a space, though very unlikely), it will silently break. A brief inline note explaining why it's unquoted would help the next reader who sees the shellcheck disable.

3. sed stripping of --start-point-hash (line 648)

RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')

This works correctly for the current single-line string format and hex SHA values. It's worth noting it could leave a double space in the resulting string, but bencher's CLI treats that as equivalent to a single space so it's not a bug.


Minor

  • The trap 'rm -f "$BENCHER_STDERR"' EXIT and the explicit rm -f "$BENCHER_STDERR" at line 660 are both present — the explicit removal is redundant given the trap, but harmless.
  • The comment at line 592 (# shellcheck disable=SC2086) correctly documents the intentional suppression.

Summary: The fix is correct and the retry logic is a meaningful improvement. The main fragility is that the retry trigger depends on Bencher's error message text staying stable — worth a comment to future-proof it. Otherwise, ready to merge with that minor note addressed.

Comment thread .github/workflows/benchmark.yml Outdated
# Bencher (e.g., the base commit was a docs-only change skipped by
# paths-ignore), retry without --start-point-hash so the report
# still gets stored using the latest available baseline.
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version" "$BENCHER_STDERR" && grep -q "not found" "$BENCHER_STDERR"; then
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 retry condition depends on Bencher's exact stderr wording staying stable. If the error format changes, the retry silently stops working (and without the old 404-swallowing fallback, the workflow just fails).

Consider pinning the expected message in a comment, e.g.:

Suggested change
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version" "$BENCHER_STDERR" && grep -q "not found" "$BENCHER_STDERR"; then
# Bencher emits e.g. "Head Version '<sha>' not found" when the pinned hash
# isn't in its DB. Grep for both fragments so a message-format change
# surfaces clearly rather than silently skipping the retry.
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version" "$BENCHER_STDERR" && grep -q "not found" "$BENCHER_STDERR"; then

# Wrap bencher run in a function so we can retry with different
# start-point args if the pinned hash isn't found in Bencher.
run_bencher() {
local sp_args="$1"
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 # shellcheck disable=SC2086 on line 592 is correct — word-splitting $sp_args is intentional so the multi-word arg string is split into individual flags. It's worth adding a brief inline note so the next reader understands why it must be unquoted:

Suggested change
local sp_args="$1"
local sp_args="$1"
# $sp_args must be unquoted so the shell word-splits the multi-flag
# string into individual arguments (e.g. "--start-point main --start-point-hash abc123").
# shellcheck disable=SC2086

@alexeyr-ci2 alexeyr-ci2 force-pushed the alexeyr/2546/Fix-benchmark-workflow branch from a72ff8d to 8c45b36 Compare April 15, 2026 16:51
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 15, 2026

Code Review

Overview

This PR correctly identifies and fixes the root cause of cascading Bencher failures on main pushes: passing `--start-point main --start-point-hash ` when main is the base branch created a lookup for a hash that often did not exist (docs-only commits skipped by `paths-ignore`), causing a 404 and permanently breaking subsequent runs.

The refactoring to a `run_bencher()` function to eliminate the ~30-line command duplication is a clear improvement.


Issues

[Medium] Retry logic removes the old regression-masking guard

The old code explicitly prevented masking real regressions by checking for the absence of regression indicators before swallowing an error. The new retry block has no equivalent guard. While a regression alert and a "Head Version not found" error are unlikely to co-occur in a single Bencher run, the original defensive check was explicit about never retrying when regression indicators are present. Without it, if Bencher ever emits both in one run, the retry could produce an exit-0 result that masks a real regression.

Consider adding back the guard before the retry block:

&& ! grep -qiE "alert|threshold violation|boundary violation" "$BENCHER_STDERR"

[Low] `--start-point-reset` silently added to `workflow_dispatch` non-main branches

The old dispatch case had `EXTRA_ARGS=""` (no `--start-point-reset`); the new code includes it in both dispatch hash paths. This changes Bencher's behavior for manually-dispatched branch runs — if the branch already exists in Bencher, it will now be reset to main's state. If intentional (and it probably is correct), a brief comment explaining the reasoning would help.

[Low] `sed`-based hash stripping is fragile

`RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')`

This works for the current arg format but would silently fail to strip (and skip the retry) if the Bencher flag is ever renamed. A bash array (`declare -a SP_ARGS=(...)`) would make the manipulation explicit and avoid the word-splitting workaround entirely, at the cost of slightly more verbose construction code.


What looks good

  • Root cause analysis and fix are correct — main should not reference itself as a start-point
  • `run_bencher()` function eliminates duplication cleanly; the `shellcheck disable=SC2086` comment with explanation is appropriate for the intentional word-split
  • Using Bencher's actual error message (`Head Version ... not found`) rather than the HTTP status code (`404 Not Found`) is more semantically correct for triggering the retry
  • Retry produces a real stored report rather than the previous behavior of silently swallowing the error (exit 0)
  • The `trap 'rm -f "$BENCHER_STDERR"' EXIT` cleanup is cleaner than the manual `rm -f` that was removed

Comment thread .github/workflows/benchmark.yml Outdated
# Bencher emits: 'Head Version (..., Some(GitHash("<sha>"))) not found'
# We grep for both fragments so a format change surfaces clearly
# rather than silently skipping the retry.
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version" "$BENCHER_STDERR" && grep -q "not found" "$BENCHER_STDERR"; then
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 old error-handling block included an explicit guard to prevent retrying (and potentially masking) actual regression alerts:

# OLD
&& ! grep -qiE "alert|threshold violation|boundary violation" "$BENCHER_STDERR"

While it's probably not physically possible for Bencher to emit both a "Head Version not found" error and a regression alert in the same run (it wouldn't reach the comparison stage if the hash is missing), restoring this guard would make the safety property explicit again. Suggest:

Suggested change
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version" "$BENCHER_STDERR" && grep -q "not found" "$BENCHER_STDERR"; then
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version" "$BENCHER_STDERR" && grep -q "not found" "$BENCHER_STDERR" && ! grep -qiE "alert|threshold violation|boundary violation" "$BENCHER_STDERR"; then

# We grep for both fragments so a format change surfaces clearly
# rather than silently skipping the retry.
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version" "$BENCHER_STDERR" && grep -q "not found" "$BENCHER_STDERR"; then
RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')
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.

This sed approach works today, but if Bencher ever renames --start-point-hash the substitution silently no-ops, $RETRY_ARGS == $START_POINT_ARGS, and the retry is quietly skipped with no warning. The comment on line 652–653 already notes the concern about format changes surfacing clearly — worth applying the same logic here.

A small improvement: echo + sed can be replaced with bash parameter expansion which is both faster and avoids a subshell:

Suggested change
RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')
RETRY_ARGS="${START_POINT_ARGS/--start-point-hash [^ ]*/}"

[^ ]* isn't a POSIX extended regex here but rather a glob/pattern, so use ${var/pattern/replacement} — though note bash glob patterns don't support [^ ]. In practice the simpler approach is fine:

RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/ --start-point-hash [^ ]*//')

The leading space before --start-point-hash avoids leaving a double-space artefact in the retry args, which is cosmetically cleaner (no functional impact since the shell collapses whitespace during word-splitting).

START_POINT_HASH=$(gh api "repos/${{ github.repository }}/compare/main...$BRANCH" --jq '.merge_base_commit.sha' || true)
if [ -n "$START_POINT_HASH" ]; then
echo "Found merge-base via API: $START_POINT_HASH"
START_POINT_ARGS="--start-point main --start-point-hash $START_POINT_HASH --start-point-clone-thresholds --start-point-reset"
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.

This is a silent behavior change from the old code: the dispatch case previously had EXTRA_ARGS="" (no --start-point-reset), so manually-dispatched runs on a branch that already existed in Bencher would append to its history rather than resetting it. The new code adds --start-point-reset here (and on line 580 for the no-hash path), so now Bencher will reset the branch to main's state on every manual dispatch.

This is probably the correct behavior (consistent with how PRs work), but a one-line comment would make the intentionality clear:

Suggested change
START_POINT_ARGS="--start-point main --start-point-hash $START_POINT_HASH --start-point-clone-thresholds --start-point-reset"
START_POINT_ARGS="--start-point main --start-point-hash $START_POINT_HASH --start-point-clone-thresholds --start-point-reset" # reset so repeated dispatches don't accumulate stale history

@alexeyr-ci2 alexeyr-ci2 force-pushed the alexeyr/2546/Fix-benchmark-workflow branch from 8c45b36 to 8a321e2 Compare April 15, 2026 17:51
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 15, 2026

Code Review

Overview

This PR correctly fixes a cascading Bencher reporting failure on pushes to main. The root cause was clear: passing --start-point-hash <github.event.before> for main-push events caused a 404 when that commit wasn't in Bencher (e.g. a preceding docs-only push skipped by paths-ignore), and the 404 cascaded across future runs.

The refactor into run_bencher() + retry is a good structural improvement over the old approach of silently treating 404s as success.


Issues

1. Behavior change: --start-point-reset added to workflow_dispatch events

In the original code, EXTRA_ARGS="" for dispatch events — meaning --start-point-reset was never passed. The new code includes --start-point-reset in both dispatch sub-cases (hash found and not found). This means every workflow_dispatch run will reset the Bencher branch to main's latest state, discarding any accumulated history for long-running feature branches. If you benchmark the same branch via dispatch multiple times to build up a baseline, this would wipe it each run. Was this an intentional upgrade to dispatch behavior, or an accidental inclusion?

2. Two-grep "Head Version … not found" check is fragile

grep -q "Head Version" "$BENCHER_STDERR" && grep -q "not found" "$BENCHER_STDERR"

These are independent searches — they could match on completely different lines. If Bencher's output ever includes "Head Version" in a success message and "not found" anywhere else, the retry fires incorrectly. A combined pattern would be safer:

grep -qE "Head Version.*not found" "$BENCHER_STDERR"

3. Minor: sed strip may leave a double-space (harmless but noisy)

RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')

This leaves --start-point main --start-point-clone-thresholds (double space). Shell word-splitting treats this correctly (extra whitespace is swallowed), so it's not a functional bug, but a single sed 's/ \?--start-point-hash [^ ]*//' or sed 's/--start-point-hash [^ ]* //' would be cleaner.


What's Good

  • The core fix (no --start-point* args for main pushes) is exactly right and directly addresses the cascading failure.
  • Moving from "suppress exit code on 404" to "retry without the missing hash" is a much healthier failure mode — the report still gets stored and comparison still happens using the latest available baseline.
  • The run_bencher() function cleanly eliminates the massive duplication that would otherwise be needed for retry logic.
  • The shellcheck disable comment with a clear explanation is good hygiene.
  • The trap 'rm -f "$BENCHER_STDERR"' EXIT replacing the manual rm -f is a nice improvement.
  • Comments explaining why main doesn't need --start-point are helpful for future readers.

Comment thread .github/workflows/benchmark.yml Outdated
# Bencher emits: 'Head Version (..., Some(GitHash("<sha>"))) not found'
# We grep for both fragments so a format change surfaces clearly
# rather than silently skipping the retry.
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version" "$BENCHER_STDERR" && grep -q "not found" "$BENCHER_STDERR"; then
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.

These two independent grep calls could match on different lines — e.g., "Head Version" in one output line and "not found" in a completely unrelated error message. Consider combining them into a single pattern:

Suggested change
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version" "$BENCHER_STDERR" && grep -q "not found" "$BENCHER_STDERR"; then
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -qE "Head Version.*not found" "$BENCHER_STDERR"; then

Comment on lines +577 to +580
START_POINT_ARGS="--start-point main --start-point-hash $START_POINT_HASH --start-point-clone-thresholds --start-point-reset"
else
echo "⚠️ Could not find merge-base with main via GitHub API, continuing without hash"
START_POINT_ARGS="--start-point main --start-point-clone-thresholds --start-point-reset"
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.

Potential behavior change from original code: The original workflow_dispatch path had EXTRA_ARGS="", so --start-point-reset was never passed for dispatch events. Both new sub-cases here now include --start-point-reset, which tells Bencher to reset the branch's accumulated history to match main on every run. For a feature branch benchmarked multiple times via dispatch to build up a trending baseline, this would wipe it each time. Was this intentional? If not, --start-point-reset should be removed from these two lines.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, it's intentional and not worth commenting every flag.

@alexeyr-ci2 alexeyr-ci2 force-pushed the alexeyr/2546/Fix-benchmark-workflow branch 2 times, most recently from 9aa20e4 to b6c4ada Compare April 15, 2026 17:56
# Bencher emits: 'Head Version (..., Some(GitHash("<sha>"))) not found'
# We grep for both fragments so a format change surfaces clearly
# rather than silently skipping the retry.
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version.*not found" "$BENCHER_STDERR" && ! grep -qiE "alert|threshold violation|boundary violation" "$BENCHER_STDERR"; then
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 comment says two separate greps make format changes "surface clearly", but the real risk is the opposite: two independent greps can match on different lines, creating a false-positive retry. A combined pattern is both safer and still surfaces format changes:

Suggested change
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version.*not found" "$BENCHER_STDERR" && ! grep -qiE "alert|threshold violation|boundary violation" "$BENCHER_STDERR"; then
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -qE "Head Version.*not found" "$BENCHER_STDERR" && ! grep -qiE "alert|threshold violation|boundary violation" "$BENCHER_STDERR"; then

If Bencher's error format ever changes, the combined pattern still stops matching — you don't lose the diagnostic value, and you eliminate the false-positive risk.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The current version already has a single grep to prevent matching on different lines.

START_POINT_HASH=$(gh api "repos/${{ github.repository }}/compare/main...$BRANCH" --jq '.merge_base_commit.sha' || true)
if [ -n "$START_POINT_HASH" ]; then
echo "Found merge-base via API: $START_POINT_HASH"
START_POINT_ARGS="--start-point main --start-point-hash $START_POINT_HASH --start-point-clone-thresholds --start-point-reset"
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.

Behavior change from old code: the previous dispatch case used EXTRA_ARGS="", so --start-point-reset was never passed for workflow_dispatch runs. This now resets the Bencher branch state to main on every dispatch run. For a branch benchmarked multiple times via dispatch to build up history, that history gets wiped each run.

Is this intentional? If so, worth a brief comment here explaining why dispatch should match PR behavior.

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 15, 2026

Code Review

Overview

The fix is correct and well-reasoned. Passing --start-point main --start-point-hash <before-sha> on main-push events was the root cause: if that hash wasn't in Bencher (docs-only commit skipped by paths-ignore), the 404 cascaded to every subsequent run. Removing --start-point args for main pushes entirely is the right call since inline --threshold-* args define the comparison criteria, and main's own accumulated history is the baseline.

The run_bencher() function is a clean refactor that makes the retry logic straightforward rather than requiring a second copy of the 30-line command.


Issues

[Medium] --start-point-reset silently added to workflow_dispatch non-main branches

The old code used EXTRA_ARGS="" for dispatch events (no --start-point-reset). The new code includes --start-point-reset in both dispatch sub-cases (lines 577 and 580). This means every workflow_dispatch run on a non-main branch will reset that branch's Bencher history to main's latest state. If someone benchmarks the same feature branch repeatedly to build up a baseline, each dispatch run would wipe that history. Was this an intentional upgrade to dispatch behavior, or an accidental inclusion?

[Low] sed stripping leaves a double-space (harmless but noisy)

RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')

When --start-point-hash <sha> is in the middle of the string, the replacement leaves two consecutive spaces (e.g. --start-point main --start-point-clone-thresholds). Shell word-splitting handles it correctly, but sed 's/--start-point-hash [^ ]* //' (with a trailing space in the pattern) would be cleaner.


What's Good

  • Core fix is exactly right; the PR description clearly explains the cascading failure mechanism
  • Retry produces a real stored report (using latest baseline) instead of silently swallowing the error
  • The regression guard (! grep -qiE "alert|threshold violation|boundary violation") is correctly retained on the retry path
  • # shellcheck disable=SC2086 with a clear rationale comment is the right hygiene for the intentional word-split
  • trap 'rm -f "$BENCHER_STDERR"' EXIT is cleaner than the manual rm -f it replaces

START_POINT_HASH=$(gh api "repos/${{ github.repository }}/compare/main...$BRANCH" --jq '.merge_base_commit.sha' || true)
if [ -n "$START_POINT_HASH" ]; then
echo "Found merge-base via API: $START_POINT_HASH"
START_POINT_ARGS="--start-point main --start-point-hash $START_POINT_HASH --start-point-clone-thresholds --start-point-reset"
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.

Behavior change from old code: The old dispatch path had EXTRA_ARGS=""--start-point-reset was never passed for dispatch events. Adding it here means every workflow_dispatch run on a non-main branch will reset that branch's Bencher history to main's latest state on each invocation.

If the intent is to always start fresh from main for dispatch runs (consistent with PR behavior), this is correct and intentional. But if someone uses workflow_dispatch to benchmark a long-running branch iteratively, this will wipe their accumulated data each time. Worth a comment either way — e.g.:

Suggested change
START_POINT_ARGS="--start-point main --start-point-hash $START_POINT_HASH --start-point-clone-thresholds --start-point-reset"
START_POINT_ARGS="--start-point main --start-point-hash $START_POINT_HASH --start-point-clone-thresholds --start-point-reset"

(No code change needed if intentional — just add a comment confirming --start-point-reset is deliberate for dispatch.)

# We grep for both fragments so a format change surfaces clearly
# rather than silently skipping the retry.
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version.*not found" "$BENCHER_STDERR" && ! grep -qiE "alert|threshold violation|boundary violation" "$BENCHER_STDERR"; then
RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')
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.

When --start-point-hash <sha> appears in the middle of $START_POINT_ARGS, the substitution leaves a double-space (e.g. --start-point main --start-point-clone-thresholds). Not a functional bug — shell word-splitting collapses it — but a trailing space in the pattern is cleaner:

Suggested change
RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')
RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]* //')

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, it's intentional and already replied to.

@github-actions
Copy link
Copy Markdown
Contributor

⚠️ Bencher report chunks were too large to post as PR comments.

View the full report in the job summary.

ihabadham added a commit that referenced this pull request Apr 15, 2026
## Summary

- Applies `prettier --write` to five tracked docs files that are
currently out of compliance on `main`.
- No content changes — only whitespace/table-alignment fixes produced by
Prettier.

## Why this is needed

The formatting drift landed via a docs-only merge to `main`. The
`lint-js-and-ruby.yml` workflow is conditionally skipped for docs-only
commits on `main` (see the job condition at
[`.github/workflows/lint-js-and-ruby.yml:79-87`](https://github.com/shakacode/react_on_rails/blob/main/.github/workflows/lint-js-and-ruby.yml#L79-L87)),
so the check never ran on merge. It surfaces on every subsequent
code-touching PR because the lint job runs for those (e.g.
[#3151](#3151),
[#3148](#3148),
[#3142](#3142),
[#3097](#3097)).

Merging this unblocks the `build` → `Check formatting` step on those
PRs.

## Test plan

- [x] `pnpm start format.listDifferent` passes for the five files
- [x] Diff is purely whitespace — no link targets, code blocks, or prose
changed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Improved formatting across API reference, core concepts, migration,
and RSC docs: refined table layouts, adjusted column alignment, and
standardized emphasis for clearer warnings.
* Updated link text and phrasing for several guides for clearer
navigation.
* Reformatted code examples and JSX/Route snippets for more compact,
consistent presentation and improved readability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@alexeyr-ci2 alexeyr-ci2 marked this pull request as ready for review April 16, 2026 07:45
START_POINT="$GITHUB_BASE_REF"
START_POINT_HASH="${{ github.event.pull_request.base.sha }}"
EXTRA_ARGS="--start-point-reset"
START_POINT_ARGS="--start-point $GITHUB_BASE_REF --start-point-hash ${{ github.event.pull_request.base.sha }} --start-point-clone-thresholds --start-point-reset"
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 START_POINT_ARGS string for PR events embeds $GITHUB_BASE_REF and ${{ github.event.pull_request.base.sha }} directly into a space-delimited arg string. This is fine for typical branch names, but GITHUB_BASE_REF values like feature/my branch (with a space) would corrupt the arg list when word-split inside run_bencher. The same applies to the workflow_dispatch path with $BRANCH.

In practice, GitHub branch names almost never contain spaces and the SHA is always hex, so this is a very low risk. Just worth documenting.

Suggested change
START_POINT_ARGS="--start-point $GITHUB_BASE_REF --start-point-hash ${{ github.event.pull_request.base.sha }} --start-point-clone-thresholds --start-point-reset"
START_POINT_ARGS="--start-point $GITHUB_BASE_REF --start-point-hash ${{ github.event.pull_request.base.sha }} --start-point-clone-thresholds --start-point-reset"

(No code change needed unless you want to add a comment noting the branch-name assumption.)

# We grep for both fragments so a format change surfaces clearly
# rather than silently skipping the retry.
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version.*not found" "$BENCHER_STDERR" && ! grep -qiE "alert|threshold violation|boundary violation" "$BENCHER_STDERR"; then
RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')
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 sed strip leaves a double-space when --start-point-hash sits between two other flags:

"--start-point main --start-point-hash abc123 --start-point-clone-thresholds"
                                             ^ sed leaves two spaces here

This is harmless because the unquoted $sp_args expansion inside run_bencher collapses consecutive whitespace via normal word-splitting. Just noting it so future readers don't think there's a bug.

A slightly cleaner alternative if you ever want it:

Suggested change
RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')
RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*[[:space:]]*//')

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 16, 2026

Code Review

Overview

This PR correctly diagnoses and fixes a cascading failure in the Bencher reporting workflow: for push-to-main events, the old code passed --start-point main --start-point-hash <before-sha>, but since many commits are skipped by paths-ignore, Bencher often had no data at that hash — causing a 404 that corrupted the baseline for all subsequent runs. The fix is conceptually sound and well-executed.


What's Good

  • Root cause fix is correct. Main IS the base branch; passing --start-point main when running on main was the wrong thing to do. Removing start-point args entirely for main pushes is the right call.
  • Retry-instead-of-suppress is a genuine improvement. The old approach silently swallowed 404 errors and moved on with no data stored. The new approach retries without the pinned hash so a report is still stored against the latest available baseline.
  • run_bencher() function eliminates a 40-line duplication that was the only alternative to the retry pattern. Good refactor.
  • --start-point-clone-thresholds was previously applied on main pushes too (it was hardcoded in the bencher invocation, not in EXTRA_ARGS). Scoping it to feature branches only is correct.
  • --start-point-reset is now consistently applied to workflow_dispatch on non-main branches. Previously EXTRA_ARGS="" for dispatch events, meaning thresholds were never reset on re-runs. The new behavior is more correct.
  • # shellcheck disable=SC2086 with explanation for the intentional word-splitting is exactly the right way to handle this.

Issues / Suggestions

1. Retry is silently skipped if Bencher changes its error message format

The retry guard uses:

grep -q "Head Version.*not found" "$BENCHER_STDERR"

The comment says "we grep for both fragments so a format change surfaces clearly," but the only observable effect of a format change is that the retry is skipped and the run fails — not a very clear signal. Consider adding a fallback log line when the exit code is non-zero but the error pattern doesn't match, so it's obvious why the retry didn't fire:

elif [ $BENCHER_EXIT_CODE -ne 0 ]; then
  echo "ℹ️ bencher failed but error pattern didn't match retry criteria — not retrying"
fi

2. Double-space left by sed (cosmetic, harmless)
Noted in an inline comment on line 655. Word-splitting in $sp_args collapses it correctly; no functional impact.

3. Branch names with spaces in START_POINT_ARGS
Noted in an inline comment on line 566. Very low risk in practice since GitHub branch names with spaces are unusual, but worth being aware of.


Summary

Well-reasoned fix that addresses the root cause rather than papering over it. The behavioral changes (retry logic, correct scoping of --start-point-clone-thresholds, adding --start-point-reset for dispatch) are all improvements. The two inline issues are minor. The one actionable suggestion is improving visibility when the retry heuristic doesn't fire.

The benchmark workflow passed --start-point main --start-point-hash
<github.event.before> for push-to-main events. Since main IS the
base branch, Bencher tried to look up a version of main at the
"before" hash — which often didn't exist (e.g., docs-only commits
skipped by paths-ignore). This caused a 404, the report was never
stored, and subsequent pushes also failed because their "before" hash
was also missing. This cascading failure meant no main data was
stored after the first version (Jan 18).

Fix: don't pass --start-point args for pushes to main (thresholds are
defined inline via --threshold-* args). For PRs/dispatch where the
start-point hash may be missing, retry without --start-point-hash so
the report still gets stored using the latest available baseline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@alexeyr-ci2 alexeyr-ci2 force-pushed the alexeyr/2546/Fix-benchmark-workflow branch from b6c4ada to f4cc4c8 Compare April 16, 2026 07:48
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 16, 2026

Greptile Summary

This PR fixes a cascading Bencher reporting failure on pushes to main by removing the erroneous --start-point main --start-point-hash <before> args (since main is its own base branch, looking up an arbitrary "before" SHA caused 404s that then broke all subsequent runs). For PRs/dispatch, it replaces the old "swallow the 404 as a warning" logic with an active retry that strips --start-point-hash and falls back to the latest available baseline.

Confidence Score: 5/5

Safe to merge — the fix correctly eliminates the root cause of the cascading Bencher 404s and replaces error suppression with an active retry strategy.

All changes are confined to the CI benchmark workflow. The root cause analysis is accurate, the retry guard (checking for no regression alerts before retrying) prevents masking real regressions, and word-splitting on $sp_args is intentional and well-documented. No P0/P1 findings.

No files require special attention.

Important Files Changed

Filename Overview
.github/workflows/benchmark.yml Refactors Bencher invocation to fix push-to-main 404 cascade: removes start-point args for main, introduces run_bencher() helper and retry-without-hash logic for PRs/dispatch. Logic is sound; word-splitting is intentional and documented.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Benchmark step starts] --> B{github.event_name?}
    B -->|push| C[BRANCH=main\nSTART_POINT_ARGS=empty]
    B -->|pull_request| D[BRANCH=HEAD_REF\nSTART_POINT_ARGS=--start-point BASE_REF --start-point-hash BASE_SHA\n--start-point-clone-thresholds --start-point-reset]
    B -->|workflow_dispatch| E{BRANCH == main?}
    E -->|yes| F[START_POINT_ARGS=empty]
    E -->|no| G[Fetch merge-base SHA via GitHub API]
    G -->|found| H[START_POINT_ARGS=--start-point main --start-point-hash MERGE_BASE\n--start-point-clone-thresholds --start-point-reset]
    G -->|not found| I[START_POINT_ARGS=--start-point main\n--start-point-clone-thresholds --start-point-reset]
    C & F --> J[run_bencher with empty start-point args]
    D & H & I --> J
    J --> K{bencher exit code?}
    K -->|0| L[✅ Success — write HTML report]
    K -->|non-zero| M{Head Version not found\nin stderr AND no alerts?}
    M -->|no| N[❌ Fail workflow]
    M -->|yes| O{--start-point-hash\npresent in args?}
    O -->|no| N
    O -->|yes| P[Strip --start-point-hash\nfrom args, retry]
    P --> Q{Retry exit code?}
    Q -->|0| L
    Q -->|non-zero| N
Loading

Reviews (1): Last reviewed commit: "Fix Bencher reporting permanently broken..." | Re-trigger Greptile

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 16, 2026

Code Review: Fix Bencher reporting for pushes to main - The fix is correct and well-motivated. Root cause and fix are both clearly explained. See inline comments for specifics.

START_POINT_HASH=$(gh api "repos/${{ github.repository }}/compare/main...$BRANCH" --jq '.merge_base_commit.sha' || true)
if [ -n "$START_POINT_HASH" ]; then
echo "Found merge-base via API: $START_POINT_HASH"
START_POINT_ARGS="--start-point main --start-point-hash $START_POINT_HASH --start-point-clone-thresholds --start-point-reset"
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.

Behavior change for workflow_dispatch: The old code used EXTRA_ARGS="" for workflow_dispatch — no --start-point-reset. The new code adds --start-point-reset here and on line 580. This aligns workflow_dispatch with PR behavior, which is likely correct, but it isn't mentioned in the PR description. Could you confirm this is intentional?

# Bencher emits: 'Head Version (..., Some(GitHash("<sha>"))) not found'
# when the pinned hash isn't in its DB. A single combined pattern
# avoids false-positive matches across unrelated lines.
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version.*not found" "$BENCHER_STDERR" && ! grep -qiE "alert|threshold violation|boundary violation" "$BENCHER_STDERR"; then
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.

Fragile error-message coupling: The retry is gated on "Head Version.*not found", which is Bencher's current internal error string. If Bencher changes this message in a future release, the retry will silently stop working and the old cascading-404 failure will return — without any obvious signal that it regressed.

Suggestion: add a comment here with a link to the Bencher CLI source or changelog entry that documents this error format, so it's easy to re-check after a Bencher version bump.

# when the pinned hash isn't in its DB. A single combined pattern
# avoids false-positive matches across unrelated lines.
if [ $BENCHER_EXIT_CODE -ne 0 ] && grep -q "Head Version.*not found" "$BENCHER_STDERR" && ! grep -qiE "alert|threshold violation|boundary violation" "$BENCHER_STDERR"; then
RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')
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.

Minor: double space after removal. s/--start-point-hash [^ ]*/ removes the flag+value but leaves a double space in the result (e.g. "--start-point main --start-point-clone-thresholds ..."). Functionally harmless — word-splitting eats it and the RETRY_ARGS != START_POINT_ARGS guard still fires — but including the trailing space in the pattern keeps things tidy:

Suggested change
RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]*//')
RETRY_ARGS=$(echo "$START_POINT_ARGS" | sed 's/--start-point-hash [^ ]* //')

Comment on lines +590 to +601
run_bencher() {
local sp_args="$1"
# Intentional word-splitting: sp_args contains multiple flags
# (e.g. "--start-point main --start-point-hash abc123") that must
# be split into separate argv entries.
# shellcheck disable=SC2086
bencher run \
--project react-on-rails-t8a9ncxo \
--token '${{ secrets.BENCHER_API_TOKEN }}' \
--branch "$BRANCH" \
$sp_args \
--testbed github-actions \
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.

Suggestion: use an array instead of intentional word-splitting. The shellcheck disable=SC2086 escape hatch works for the current inputs (branch names and SHA hashes never contain spaces), but it's fragile for future changes and suppresses a useful lint rule broadly.

read -ra builds a real array and handles the empty-string case (push-to-main where START_POINT_ARGS="") correctly — an empty input produces a zero-element array, so "${sp_args[@]}" expands to nothing:

Suggested change
run_bencher() {
local sp_args="$1"
# Intentional word-splitting: sp_args contains multiple flags
# (e.g. "--start-point main --start-point-hash abc123") that must
# be split into separate argv entries.
# shellcheck disable=SC2086
bencher run \
--project react-on-rails-t8a9ncxo \
--token '${{ secrets.BENCHER_API_TOKEN }}' \
--branch "$BRANCH" \
$sp_args \
--testbed github-actions \
run_bencher() {
local -a sp_args
read -ra sp_args <<< "$1"
bencher run \
--project react-on-rails-t8a9ncxo \
--token '${{ secrets.BENCHER_API_TOKEN }}' \
--branch "$BRANCH" \
"${sp_args[@]}" \

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