Skip to content

ci(parallel-allowlist): carry forward previous excludeFiles on regen - #36868

Closed
robobun wants to merge 6 commits into
mainfrom
farm/5d22f22d/allowlist-carry-forward
Closed

robobun wants to merge 6 commits into
mainfrom
farm/5d22f22d/allowlist-carry-forward

Conversation

@robobun

@robobun robobun commented Aug 4, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

scripts/update-parallel-allowlist.mjs decides a file is safe for the bun test --parallel batch when it has zero flaky/failed annotations in the scanned builds. But a file already in excludeFiles never runs in the parallel batch, so it never produces a parallel-batch annotation. On the next regen the generator sees zero flakes and drops the exclusion; the file runs in the batch, flakes, and the regen after that re-adds it. Ping-pong.

Observed: #36585 (30032c6) regenerated from builds 85133-86444. js/bun/util/inspect-error-leak.test.js was in excludeFiles (via #36175) throughout that window and had zero annotations, so the regen dropped it; it flaked on almost every build from 86456 onward:

test/js/bun/util/inspect-error-leak.test.js - code 1 on debian 13 x64-asan
RSS increased by 346 MB
✗ Printing errors does not leak [12434.88ms]

42 entries were dropped this way (dir still listed, file no longer excluded). Every RSS / heapStats test currently in excludeFiles is exposed to the same thing on a future regen whose window post-dates its last batch flake.

Cause

isGood(file) only has negative evidence (flake annotations). It has no positive evidence that a previously-excluded file ran in the batch without flaking, so "zero flakes for an excluded file" cannot be distinguished from "this file is batch-safe".

Fix

Read the existing excludeFiles before overwriting and carry those entries forward. An exclusion leaves the list only when the file is deleted, its dir drops out of dirs, or someone removes the entry by hand; the next regen then has real batch data for the file and re-excludes only if it flakes again. The _meta.rule text is updated to say so.

The decision logic is now a pure computeAllowlist({files, flaky, durations, previousExcludes, ...}) export; the CLI body is guarded by process.argv[1] === __filename so test/internal/parallel-allowlist.test.ts can unit-test the carry-forward rule without hitting the Buildkite API.

Also re-adds the two in-process-RSS tests that #36585 dropped and are currently running in the batch:

The other 40 dropped entries are left alone; they have been running in the batch since 86456, so the next regen (with this fix applied) has real data for them.

Verification

$ bun bd test test/internal/parallel-allowlist.test.ts
(pass) test/parallel-allowlist.json has the shape the runner reads
(pass) excludeFiles are real files inside listed dirs
(pass) scripts/update-parallel-allowlist.mjs > carries forward previous excludeFiles even with zero flakes in the scan window
(pass) scripts/update-parallel-allowlist.mjs > a file removed from excludeFiles by hand is re-evaluated on fresh data
(pass) scripts/update-parallel-allowlist.mjs > previous exclude whose dir drops below the threshold is not emitted
(pass) scripts/update-parallel-allowlist.mjs > stress-named and docker-prefixed files never qualify
(pass) scripts/update-parallel-allowlist.mjs > importing the script does not run its CLI (no token check, no network)
 7 pass  0 fail

node scripts/update-parallel-allowlist.mjs still runs as before (enters the CLI block, reads the previous table, proceeds to the build scan).

Runner predicate with the updated table:

js/bun/util/inspect-error-leak.test.js   -> in batch: false
js/node/vm/sourcetextmodule-leak.test.ts -> in batch: false

[stamp-90s] gate passed · iteration 0 · 3 files touched

passes on PR (with fix)
Test-only change.

Debug/ASAN (expected pass):
$ bun bd test 'test/internal/parallel-allowlist.test.ts'
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test test/internal/parallel-allowlist.test.ts
bun test v1.4.0 (791138603)

test/internal/parallel-allowlist.test.ts:
(pass) test/parallel-allowlist.json has the shape the runner reads [185.39ms]
(pass) excludeFiles are real files inside listed dirs [33.85ms]
(pass) scripts/update-parallel-allowlist.mjs > carries forward previous excludeFiles even with zero flakes in the scan window [19.47ms]
(pass) scripts/update-parallel-allowlist.mjs > a file removed from excludeFiles by hand is re-evaluated on fresh data [7.75ms]
(pass) scripts/update-parallel-allowlist.mjs > carried excludes do not count against the dir's 2/3 threshold [4.09ms]
(pass) scripts/update-parallel-allowlist.mjs > a dir still drops when its fresh files fall below the threshold [3.41ms]
(pass) scripts/update-parallel-allowlist.mjs > stress-named and docker-prefixed files never qualify [6.41ms]
(pass) scripts/update-parallel-allowlist.mjs > importing the script does not run its CLI (no token check, no network) [1494.73ms]

 8 pass
 0 fail
 1150 expect() calls
Ran 8 tests across 1 file. [4.04s]
Exit: 0
diff hotspot
scripts/update-parallel-allowlist.mjs    | 307 ++++++++++++++++++-------------
 test/internal/parallel-allowlist.test.ts |  98 +++++++++-
 test/parallel-allowlist.json             |   2 +
 3 files changed, 275 insertions(+), 132 deletions(-)

gate history · 4 passed · 0 rejected · iteration 0

evidence per changed file
file                                      reads  edits  tests
scripts/update-parallel-allowlist.mjs         6      7      0
test/internal/parallel-allowlist.test.ts      4      7      0
test/parallel-allowlist.json                  2      2      0

A file in excludeFiles never runs in the --parallel batch, so the
annotation scan can't observe it flaking there; "zero flakes" for it
means nothing. The generator now reads the existing table before
overwriting it and keeps those entries. An exclusion leaves the list
only when the file is deleted, its dir drops out of dirs, or someone
removes the entry by hand (at which point the next regen has real batch
data for it).

Also re-adds inspect-error-leak.test.js and sourcetextmodule-leak.test.ts
to excludeFiles; both measure in-process RSS and were dropped by #36585
for this reason.

computeAllowlist is now a pure export so the carry-forward rule is
covered by test/internal/parallel-allowlist.test.ts without hitting the
Buildkite API.
@robobun

robobun commented Aug 4, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: diff touches scripts/ + test/ only (no src/). bun bd test test/internal/parallel-allowlist.test.ts passes (8/8). All review threads resolved at 7911386. Supersedes #36867.

CI on build 88675: test/internal/parallel-allowlist.test.ts passed on every lane that ran. The annotation failures are all [flaky] (passed on retry) on unrelated files (bun-install-patch, test-fastutf8stream-destroy, v8-heap-snapshot, junit, two napi node-gyp EBUSY on Windows aarch64 parallel batch), and the three darwin test lanes expired waiting for an agent. None touch this diff. Ready for a maintainer.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The update extracts pure allowlist computation, preserves prior exclusions, isolates CLI execution, adds focused tests, and updates the generated parallel test exclusion list.

Changes

Parallel allowlist update

Layer / File(s) Summary
Allowlist computation and validation
scripts/update-parallel-allowlist.mjs, test/internal/parallel-allowlist.test.ts
computeAllowlist filters Docker-prefixed, stress-named, flaky, slow, and previously excluded files. It applies the directory threshold and returns computed statistics. Tests cover exclusion carry-forward, reevaluation, threshold handling, and filtering.
CLI orchestration and output wiring
scripts/update-parallel-allowlist.mjs, test/internal/parallel-allowlist.test.ts, test/parallel-allowlist.json
CLI setup and Buildkite access run only during direct execution. Existing exclusions are loaded and validated before computation. Output metadata and directory totals use the helper results. The tests verify import safety, and two test files are added to the exclusion list.

Possibly related PRs

  • oven-sh/bun#33622: Both changes modify the parallel-test allowlisting workflow and its duration and Docker-prefix inputs.
  • oven-sh/bun#36175: Both changes modify and test the same scripts/update-parallel-allowlist.mjs logic.
  • oven-sh/bun#36867: Both changes update exclusion handling for js/bun/util/inspect-error-leak.test.js.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: preserving previous excludeFiles entries during parallel allowlist regeneration.
Description check ✅ Passed The description explains the problem, fix, scope, and verification results, including targeted tests and expected behavior.

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/update-parallel-allowlist.mjs`:
- Around line 165-168: Update the previousExcludes loading in the allowlist
regeneration flow to treat only ENOENT as an empty initial allowlist; for
malformed JSON, permission errors, or other read failures, emit an actionable
error and stop before writing output. Validate that parsed excludeFiles is an
array of strings, preserve the existing file unchanged on any invalid input, and
add a regression test covering corrupted JSON.
- Around line 75-85: Validate the parsed CLI values in the entrypoint before any
build scanning or network access: require builds to be a strict, finite, safe
integer of at least 50, and fast-ms to be a strict, finite, safe non-negative
integer (including 0 as valid). Reject empty or partially parsed strings rather
than relying on parseInt, and report each invalid value with its constraint and
correction guidance.

In `@test/internal/parallel-allowlist.test.ts`:
- Around line 75-86: Update the test "stress-named and docker-prefixed files
never qualify" to assert that the computed dirs result does not contain
"js/sql", ensuring the Docker-prefix production guard is exercised in addition
to the existing excludeFiles assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 47de4f7b-78a9-45bd-8f5b-165a393bb51d

📥 Commits

Reviewing files that changed from the base of the PR and between d6302e9 and 5e3c4ad.

📒 Files selected for processing (3)
  • scripts/update-parallel-allowlist.mjs
  • test/internal/parallel-allowlist.test.ts
  • test/parallel-allowlist.json

Comment thread scripts/update-parallel-allowlist.mjs Resolved
Comment thread scripts/update-parallel-allowlist.mjs Resolved

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/update-parallel-allowlist.mjs`:
- Around line 167-173: Update the previous-exclusions loading logic around
previousExcludes to validate every excludeFiles entry is a string before
constructing the Set. On invalid entries, fail with an error containing the
output path, rejected value or index, expected string-array shape, and repair
action; add a regression test covering a non-string entry and ensure valid
exclusions remain preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 171f6a66-fbea-4a71-8e86-75c871a6c85a

📥 Commits

Reviewing files that changed from the base of the PR and between 5e3c4ad and 1515e8a.

📒 Files selected for processing (2)
  • scripts/update-parallel-allowlist.mjs
  • test/internal/parallel-allowlist.test.ts

Comment thread scripts/update-parallel-allowlist.mjs Resolved
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. ci: check parallel-denylist.txt against the allowlist; denylist three more in-process leak tests #36867 - Adds the identical "js/bun/util/inspect-error-leak.test.js" entry to excludeFiles in test/parallel-allowlist.json (same hunk, same root cause); this PR states it supersedes it.

🤖 Generated with Claude Code

Comment thread scripts/update-parallel-allowlist.mjs Resolved
Comment thread scripts/update-parallel-allowlist.mjs Resolved

@claude claude Bot 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.

LGTM — all three nits from my earlier pass are addressed (fail-loud on unreadable previous allowlist, dirs assertion in the docker test, realpathSync on the entry-point guard).

What was reviewed:

  • computeAllowlist extraction is behavior-preserving vs. the inline original; only new logic is the previousExcludes.has(file) carry-forward, which is the point of the PR and is unit-tested.
  • Previous-allowlist read now matches the sibling durations read: ENOENT → empty set, everything else exits before writing.
  • Confirmed process.argv[1] under bun test is the test file's absolute path, so realpathSync on it succeeds and the CLI block stays skipped on import.
  • The two re-added excludeFiles entries are in listed dirs and exist on disk (covered by the existing shape test).
Extended reasoning...

Overview

This PR touches CI tooling only: scripts/update-parallel-allowlist.mjs (the Buildkite-driven regenerator for test/parallel-allowlist.json), its unit test at test/internal/parallel-allowlist.test.ts, and two entries re-added to test/parallel-allowlist.json. No src/ changes; nothing shipped to users.

The functional change is one line — if (previousExcludes.has(file)) return false inside isGood() — plus the plumbing to read the previous excludeFiles before overwriting. The rest is a refactor that hoists the decision logic into an exported pure function and gates the CLI body behind an entry-point check so the test file can import it without triggering the token check / network scan.

Security risks

None. The script already read BUILDKITE_API_TOKEN and hit the Buildkite API; that code moved verbatim inside the guard. No new external inputs, no new writes outside test/parallel-allowlist.json, no shell interpolation.

Level of scrutiny

Low. This is internal maintenance tooling whose output is a checked-in JSON file that goes through PR review before landing, and the two allowlist edits are strictly conservative (excluding a file from the parallel batch is always safe — it still runs serially). A regression here manifests as a wrong regen diff in a future PR, not as a user-visible bug or CI break.

Other factors

  • All prior review threads (mine and CodeRabbit's) are resolved. The one declined CodeRabbit suggestion (per-entry string validation of excludeFiles) is reasonably declined: the checked-in file is validated per-entry by test/internal/parallel-allowlist.test.ts on every CI run, and a non-string in previousExcludes cannot match a real file path so cannot drop a real exclusion.
  • I verified that under bun test, process.argv[1] is the test file's absolute path, so realpathSync(process.argv[1]) in 25d2902 succeeds and does not match the script's __filename; the process.argv[1] && prefix guards the undefined case.
  • The refactored computeAllowlist reproduces the original per-dir threshold, sorting, and excludeFiles emission exactly; totalDirs replaces the removed byDir.size reference in the final log line.
  • New tests cover carry-forward, hand-removal re-evaluation, dir-below-threshold interaction, stress/docker exclusion (now asserting on dirs), and import-without-CLI.

@claude claude Bot 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.

LGTM — all three nits from the previous pass are addressed (ENOENT-only catch, realpathSync guard, dirs assertion in the docker test). The one remaining inline comment is test hygiene on a placeholder assertion and not merge-blocking.

Extended reasoning...

Overview

Internal CI tooling only — no src/ changes. Three files:

  • scripts/update-parallel-allowlist.mjs: extracts the existing decision logic into a pure exported computeAllowlist({...}), adds a previousExcludes.has(file) check so exclusions carry forward across regens, and moves the CLI body verbatim into a realpathSync(process.argv[1]) === __filename guard so the test file can import it. The previousExcludes read fails loud on anything but ENOENT.
  • test/internal/parallel-allowlist.test.ts: adds five unit tests for computeAllowlist, including a direct test of the carry-forward rule (the PR's actual fix).
  • test/parallel-allowlist.json: manually re-adds two RSS-leak tests that #36585 dropped via the ping-pong bug this PR fixes.

Security risks

None. Maintainer-run script that reads Buildkite annotations and writes a checked-in JSON consumed only by the CI test runner. No user-facing surface.

Level of scrutiny

Low. This is test-infrastructure tooling whose output goes through PR review before commit. The refactor is a mechanical extraction — I diffed the moved CLI body against the original and the only semantic change is the new previousExcludes input, which is exactly the fix. The two re-added excludeFiles entries exist on disk and their parent dirs are already listed, so the existing "excludeFiles are real files inside listed dirs" test covers them. _meta.stats.excluded is now stale (160 vs 162 actual), but nothing reads it and the next regen corrects it.

Other factors

All prior review feedback (mine and CodeRabbit's) is resolved in 1515e8a and 25d2902. The one nit posted this run — the vacuous typeof computeAllowlist === "function" assertion — is real but does not gate correctness: the top-level import at line 4 already provides the load-bearing coverage for "import doesn't run the CLI", and the carry-forward rule itself is properly covered by the first test in the describe block.

@robobun

robobun commented Aug 4, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 2:11 AM PT - Aug 4th, 2026

✅ @robobun, your commit 7911386035df2cf69db8b38906102c868fb8b5d3 passed in Build #88675! 🎉


🧪   To try this PR locally:

bunx bun-pr 36868

That installs a local version of the PR into your bun-36868 executable, so you can run:

bun-36868 --bun

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/internal/parallel-allowlist.test.ts`:
- Around line 92-97: Update the Bun.spawn invocation in the parallel allowlist
test to pipe stdout, then consume proc.stdout concurrently with proc.stderr and
proc.exited. Assert that the collected stdout is empty before asserting the
child exitCode, preserving the existing stderr and exit-status checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b6741c68-dd2c-4ca0-a34c-c68fc0f0b3a6

📥 Commits

Reviewing files that changed from the base of the PR and between 25d2902 and c269d13.

📒 Files selected for processing (1)
  • test/internal/parallel-allowlist.test.ts

Comment thread scripts/update-parallel-allowlist.mjs Resolved
…n't drop the dir

A carried exclude has no batch data in the scan window; letting it count
against DIR_MIN_FRACTION can push a marginal dir below threshold, which
drops the dir and all its carried excludes, recreating the ping-pong one
level up. js/node/vm is at margin=0 (7 files, 2 carried) on the table
this PR writes.

@claude claude Bot 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.

LGTM — internal CI tooling change with all prior review feedback addressed.

What was reviewed:

  • computeAllowlist carry-forward logic and the fresh-files-only threshold fix from 7911386 — carried excludes no longer count against dir eligibility, and the fresh.length === 0 edge falls through to 0 < max(1, 0) and drops as before.
  • The realpathSync(argv[1]) === __filename guard: argv[1] is checked for truthiness first, and the subprocess test in bun -e mode won't match __filename even if BUILDKITE_API_TOKEN is inherited via bunEnv.
  • Previous-allowlist read: ENOENT is the only silent path; malformed JSON / non-array excludeFiles exits loudly.
  • Two re-added excludeFiles entries pass the existing "real files inside listed dirs" invariant test.
Extended reasoning...

Overview

This PR touches three files, none of which ship in the Bun runtime: scripts/update-parallel-allowlist.mjs (a maintainer-run script that scans BuildKite annotations and regenerates test/parallel-allowlist.json), test/internal/parallel-allowlist.test.ts (unit tests for that script and shape checks on the checked-in JSON), and test/parallel-allowlist.json (two entries re-added). The functional change is: (a) extract the decision logic into a pure exported computeAllowlist so it can be unit-tested without hitting BuildKite, (b) carry forward the previous excludeFiles set so files that never ran in the batch aren't spuriously re-admitted on zero evidence, (c) compute the 2/3 dir threshold over fresh (non-carried) files only so carry-forward can't push a marginal dir out and recreate the ping-pong one level up, and (d) guard the CLI body behind realpathSync(argv[1]) === __filename so importing the module for tests doesn't hit the network.

Security risks

None. This is an internal maintenance script invoked by a maintainer or a scheduled BuildKite pipeline; its output is a checked-in JSON file that goes through PR review. It reads a BuildKite API token from the environment (pre-existing behavior, unchanged), makes read-only API calls, and writes a local file. No user-facing surface, no auth/crypto/permissions.

Level of scrutiny

Low-to-medium. This is CI plumbing whose worst-case failure mode is "some tests flake in the parallel batch and get re-excluded on the next regen" — not runtime breakage. The output file lands via a reviewed PR, so a bad regen is caught before it affects CI. That said, I gave it four rounds of inline review on earlier commits (bare catch {} on the previous-allowlist read, symlinked-checkout argv[1] mismatch, a vacuous test assertion, and the dir-level ping-pong from carried excludes counting against the threshold); all four were addressed in follow-up commits and every thread is resolved.

Other factors

  • No CODEOWNERS entry covers scripts/, test/internal/, or test/parallel-allowlist.json.
  • 8 unit tests pass on the debug build per the PR verification block; the new tests exercise the carry-forward rule, the fresh-threshold rule, the hand-removal path, dir-drop, stress/docker exclusion, and the import-doesn't-run-CLI guard via a spawned subprocess with piped and drained stdout/stderr.
  • The one candidate the finders raised this run (bunEnv passes BUILDKITE_API_TOKEN through to the subprocess test) was verified as a non-issue: in bun -e mode process.argv[1] cannot equal the script's realpath, so the CLI block is unreachable regardless of the token.
  • The bulk of the diff is indentation from wrapping the pre-existing CLI body in the if (argv[1] === __filename) block; the actual logic delta is small and now covered by pure-function unit tests it previously lacked.

@robobun

robobun commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Heads up: #39373 landed a different fix for the same oscillation, a persistent test/parallel-denylist.txt that the generator always honours (plus --denylist-from-annotations to grow it from batch annotations). That covers the carry-forward this PR adds, so it probably wants closing or rebasing into something narrower. #36867 has been reshaped on top of it (a denylist/allowlist consistency check plus three pre-window entries).

@robobun

robobun commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Closing as superseded by #39373.

@robobun robobun closed this Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants