Skip to content

fix(op): sort standalone-test dependencies so the test rootfs is deterministic - #1180

Merged
twitchyliquid64 merged 2 commits into
mainfrom
bryan/standalone-test-rootfs-order
Aug 6, 2026
Merged

fix(op): sort standalone-test dependencies so the test rootfs is deterministic#1180
twitchyliquid64 merged 2 commits into
mainfrom
bryan/standalone-test-rootfs-order

Conversation

@bryan-minimal

@bryan-minimal bryan-minimal commented Aug 5, 2026

Copy link
Copy Markdown
Member

Summary

Same class of issue as #1178, on the standalone-test path.

StandaloneTest::dependencies collects into a HashSet<SandboxMapped>, fed by an unsorted
HashMap iteration. The sandbox hardlinks these first-writer-wins, so when two
dependencies install the same path the winner is decided by iteration order, which comes
from a per-process RandomState seed. A test therefore ran against a different rootfs from
one invocation to the next.

Change

Collect into a Vec sorted by spec hash, deduplicating by path and keeping the first
occurrence. One file.

Why this one is worth doing

Unlike a build, a test that composes its rootfs differently can silently report the wrong
result rather than failing.

Concretely: packages/gawk's matching test has test_deps = [base-bootstrap], and that
closure contains gawk-bootstrap 5.3.2, which installs usr/bin/gawk, usr/bin/awk and
usr/lib/gawk/*.so — the same paths gawk 5.4.0 installs. sandbox2 also symlinks
/bin → usr/bin, so command lookup resolves to whichever won. On an unlucky iteration
minimal check gawk asserts against the 5.3.2 binary and reports a pass for 5.4.0; it
would report a pass even if 5.4.0 could not start. packages/mtools has the same shape
with bash.

This PR makes that outcome consistent rather than random. It does not decide which
package ought to win — as with #1178, the overlapping paths are the underlying problem and
belong in the specs. Filing this separately so the variance is gone while that is sorted
out.

Note

The queue builder does not run standalone tests, so no published artifact depends on these
verdicts today.

Note

Sort standalone-test rootfs dependencies deterministically using BTreeSet

  • Replaces HashSet<SandboxMapped> with BTreeSet<SandboxMapped> in both Config.rootfs and StandaloneTest.dependencies to give deterministic iteration order.
  • Adds PartialOrd/Ord implementations for SandboxMapped, ordering by variant (File < Dir < TempDir < FileCopy) then by associated path.
  • Behavioral Change: anything iterating over Config.rootfs now sees a stable, sorted order instead of hash-determined order.

Macroscope summarized afdbc22.

…rministic

`StandaloneTest::dependencies` collected into a `HashSet<SandboxMapped>` fed by an
unsorted `HashMap` iteration. The sandbox hardlinks these first-writer-wins, so when two
dependencies install the same path the winner was decided by a per-process `RandomState`
seed and varied between runs.

Collect into a `Vec` sorted by spec hash, deduplicating by path and keeping the first
occurrence, so a given test assembles the same rootfs every time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@bryan-minimal
bryan-minimal requested a review from a team as a code owner August 5, 2026 21:26
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0c673c87-6b36-4c30-ba68-3a90ee64b2f5

📥 Commits

Reviewing files that changed from the base of the PR and between a3eaa8d and afdbc22.

📒 Files selected for processing (2)
  • crates/op/src/standalone_test.rs
  • crates/sandbox2/src/config.rs

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

@twitchyliquid64 twitchyliquid64 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Theres no need to sort and/or remove dupes if the upstream collection of paths into a rootfs is a BTreeSet - it will be deterministic order, and first write will win. A lot of this diff seems superfluous (tho s/HashSet/BTreeSet seems fine)

@bryan-minimal

Copy link
Copy Markdown
Member Author

Done in bbab0ff — took it one level further per your note: Config::rootfs itself is now a BTreeSet (with an Ord on SandboxMapped mirroring its manual Eq/Hash key), so assembly is deterministic for every producer, not just this call site. The op-layer sort/dedup is reverted; the standalone-test path keeps the plain HashSetBTreeSet swap.

Sandbox::new hardlinks Config::rootfs entries first-writer-wins by
iterating the collection directly, so a HashSet let a per-process
RandomState seed decide which entry provided a contended path. Make the
collection itself ordered: SandboxMapped gets an Ord mirroring its
manual Eq/Hash key (variant tag, path) and Config::rootfs becomes a
BTreeSet. This fixes every rootfs producer at once; the op-layer
sort/dedup this replaces was only treating one call site's symptom.

The standalone-test path keeps the plain HashSet->BTreeSet swap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@twitchyliquid64
twitchyliquid64 merged commit f5a9a98 into main Aug 6, 2026
30 checks passed
@twitchyliquid64
twitchyliquid64 deleted the bryan/standalone-test-rootfs-order branch August 6, 2026 01:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants