Skip to content

nightly: Step 6 surveys files that already have an open PR, so a 28-day rotation over an unmerged backlog re-derives its own prior PRs #1176

Description

@cargo-affected-bot

Nightly's Step 6 survey reads its file list straight from nightly_survey_files.py and reviews every path in it. Nothing between the script and the review checks whether a path already has an open PR against it. On a repo whose PRs merge, that costs nothing — the previous pass's PR is gone by the time the bucket comes round. On a repo with a backlog it costs a full review per aliased file, every cycle, forever.

The aliasing is exact, not approximate

The rotation is deterministic in both directions: the day picks the bucket (unix_day / 86400 % 28) and the path picks the bucket (cksum(path) % 28). So bucket N returns to the same file set every 28 days. Once a repo's default branch has been static longer than the cycle, every bucket whose last pass left an unmerged PR re-derives that PR's findings on the way round — not occasionally, but on the exact anniversary.

That threshold was crossed on max-sixty/cargo-affected: main static 31 days against a 28-day cycle, 36 open bot PRs, roughly a third of them survey-shaped.

What it cost on the run that tripped it

tend-nightly 34195750626 (2026-09-08, bucket 12/28) drew tests/functional/clean.rs and tests/functional/db_has_function_ranges.rs. It read both, found the unreachable || contains("no coverage data found") alternative and a pub → pub(crate) error in a module doc, branched, wrote both fixes, rustup component add llvm-tools, installed cargo-nextest, cargo build --tests, ran the full functional suite, clippy --all-targets and fmt --check — and then checked the queue and found max-sixty/cargo-affected#83, opened 2026-08-11 (28 days earlier, same bucket), touching the same two files with the same two fixes. The branch was discarded unpushed. Run cost $3.09 / 54 turns, most of it on that path.

Two earlier nightlies on the same repo landed the same way with less sunk cost — 33724452938 (09-03) found two of three survey defects already carried by open PRs #77 and #99, and 33950286183 (09-05) declined to file after its src/selection.rs finding turned out to be covered by #81.

Why the existing guards don't cover it

  • Step 8's orienting projection (gh issue list/gh pr list --json number,title) matches on titles. A survey PR's title describes the fix (tests: correct two inaccurate statements in surveyed scenarios), which a freshly re-derived finding has no reason to phrase the same way.
  • running-in-ci's "Fetch the prior rejection before re-deriving a fix" (added since 0.1.14, present on main) does search by path, and firing per finding before writing code would have saved the build and test cycle above. But it is downstream of the read: the survey has already spent its review turns on the file by the time any finding exists to search for. It also depends on the model choosing the right <symbol> for each finding, where the survey already holds the exact path.
  • The pre-gh pr create recheck is last by construction — it caught this one, which is why nothing wrong shipped, but only after everything was spent.

Suggested direction

Design is yours. The cheapest shape looks like one call at the top of Step 6, before any file is read:

gh pr list --state open --limit 200 --json number,files \
  --jq '[.[] | {n: .number, paths: [.files[].path]}]' > /tmp/open-pr-files.json

Then drop from today's bucket any path that appears in it, noting the skipped paths and the PRs that cover them in the Step 9 summary so the skip is visible rather than silent. A file with an open PR against it isn't a good survey target regardless of the aliasing: whatever the survey finds there is either already in that PR or belongs as a comment on it, and the PR is the place a maintainer is already looking.

This is a property of the rotation rather than of any one repo, so it will surface anywhere a backlog outlives the 28-day cycle — most visibly on repos where the bot is the only author.

Verification notes
  • Bucket confirmed by running the script in the same 24h window: nightly-survey-files.sh → bucket=12/28 files=4, listing the two test files plus two generated tend-*.yaml (skipped per the checklist).
  • gh pr view 83 --json files returns exactly tests/functional/clean.rs, tests/functional/db_has_function_ranges.rs; its diff makes the same two edits the nightly's Edit tool calls made, re-read from the session log at claude-session-logs-c508d87b.
  • fix(review): add explicit step to load running-in-ci skill #83 createdAt is 2026-08-11T06:41:20Z; the nightly ran 2026-09-08T06:41:08Z — 28 days and 12 seconds apart, which is the rotation, not a coincidence.
  • Step 6 on tend main (plugins/tend-ci-runner/skills/nightly/SKILL.md) carries no dedup between the script invocation and "Apply the review checklist below to each file in full".

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions