Skip to content

install: only re-resolve rows a package still owns when overrides or catalogs change - #38849

Open
robobun wants to merge 6 commits into
mainfrom
farm/46e99f0c/skip-orphaned-rows-on-override-change
Open

install: only re-resolve rows a package still owns when overrides or catalogs change#38849
robobun wants to merge 6 commits into
mainfrom
farm/46e99f0c/skip-orphaned-rows-on-override-change

Conversation

@robobun

@robobun robobun commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • A project whose root package.json has "x": "file:../x" installs fine with an override for x, but once that override is removed (or, for "x": "catalog:", once the catalog changes) every following bun install fails until the lockfile is deleted:
    error: Could not find package.json for "file:../x" dependency "x"
    
    Reproduces on bun 1.4.0 and main, with bun.lock and bun.lockb alike; a fresh install of the same package.json works.
  • The same defect also mis-resolves registry dependencies: with "no-deps": "~1.0.0" plus an override, removing the override while changing the range to ^1.0.0 installs no-deps 1.0.1 (the best match of the range that was just removed) instead of 1.1.0.
  • Cause: when overrides or catalogs changed, install_with_manager appends the freshly parsed root rows to buffers.dependencies and points package 0 at them (src/install/PackageManager/install_with_manager.rs:353), then re-resolves every row in buffers.dependencies whose name is overridden, or that is a catalog: row (the two loops at :531 and :555 before this change). The root's previous rows are still in the buffer, owned by no package, so they get re-resolved too, ahead of the live rows.
  • file: case: for an unowned row Lockfile::is_workspace_dependency is false, so the Folder arm of get_or_put_resolved_package (src/install/PackageManager/PackageManagerEnqueue.rs:2656) treats it as a transitive folder dependency and rejects a path that leaves the package directory unless the name is still overridden. The live row resolves fine; the error comes from the dead one.
  • npm case: the dead ~1.0.0 row appends no-deps@1.0.1, and the live ^1.0.0 row is then deduped onto that package by the satisfies fallback in Lockfile::get_package_id (src/install/lockfile.rs:2034), which accepts a same-major package appended earlier in the session.
  • Peer case: a root peerDependencies entry declared through the catalog (or overridden to catalog:) is bound twice when the catalog entry stops matching the installed version, so warn: incorrect peer dependency is printed twice.
  • Every other dead row naming an overridden package (git, tarball, in-project folders, ...) was resolved a second time for nothing; Lockfile::clean drops whatever that produced.

Fix

  • Both loops now go through reresolve_owned_rows, which builds a bitset of the rows covered by some package's dependency list and only invalidates and re-resolves those (still skipping the rows a bare bun update pinned). The selection predicates are unchanged.
  • Correct because a row only means something through its owner: the resolver decides how far to trust a file: / workspace: target from the owning package, locked_version_of_invoking_workspace_row only honors rows in the root's current list, and once resolution is done nothing reads an unowned row (Lockfile::clean and the tree builder both walk package lists). A row no package owns therefore has no owner to be resolved under and nothing to contribute; its live replacement is in the buffer and is visited by the same pass.
  • The dead rows had no side effect worth keeping. The only way their resolution reached a live row is the get_package_id dedupe described above, which is the 1.0.1-instead-of-1.1.0 bug: it let a range that is no longer in package.json pick the version. The locked-version helpers (locked_version_in_lockfile only looks at lockfile-loaded packages) and manifest fetches (the same manifest is fetched for the live row) are unaffected by whether a dead row was resolved.
  • This is the rule the newer passes already follow (UpdateScope::walkable_rows, update_transitive::set_rows_of, audit_fix::live_edge); these two loops predate it.
  • Out of scope: rows that are owned but get visited twice by these passes for other reasons (a root row the differ left unmapped, which the add/update pass re-enqueues as well; the rows of a workspace the add/update pass is about to re-read; a catalog: row whose name is also overridden when both changed). Those only repeat a peer warning and are a separate change on top of this one (see the comments below).
  • Verified with:
    • test/cli/install/nested-overrides.test.ts, "removing a flat rule re-resolves only the root's current rows": the npm range case above, a file: dependency outside the project whose override is removed, and one removed together with its override.
    • test/cli/install/catalogs.test.ts: "changing the entry of a catalog: dependency pointing outside the project", and "a root peer whose catalog range stops matching the installed version is checked once" (declared through the catalog, overridden to catalog:, plus the inline declaration as the baseline that already warned once).
    • The six non-baseline tests fail on the released binary (the file: ones with the error above, the npm one with 1.0.1, the peer ones with a wrong warning count) and pass with this change; the nested-overrides ones 3 of 3 runs each way.
    • bun bd test on nested-overrides, catalogs, bun-update-transitive, frozen-lockfile-pruned, bun-lock and bun-install: no new failures (the bun-install failures in this sandbox are the tests that need network access).

Background

  • buffers.dependencies / buffers.resolutions are flat, parallel arrays of dependency rows; each package owns a contiguous (off, len) slice of them. A row is "owned" when some package's slice covers its index.
  • The differ (Diff::generate) compares the root package.json against the lockfile's root. When anything relevant changed, the root gets a new slice appended at the end of the buffers rather than having its old rows rewritten, so the old rows stay in the buffer, unowned, until Lockfile::clean rebuilds the lockfile after resolution.
  • Folder (file:) trust: a file: target on a root or workspace row is user-written and may point anywhere; the same target declared by a transitive package is rejected when it escapes that package's directory, except for names listed in the root's overrides, whose values are also user-written. Ownership is how the resolver tells the two apart.
  • Lockfile::get_package_id dedupe: when an npm row resolves, a package of that name already in the lockfile that satisfies the row's range is reused instead of appending the manifest's best match; for packages appended during the current install this is only refused across majors. Resolution order therefore decides which of two overlapping ranges wins, which is why a dead row resolved first could pick for the live one.
  • pinned_rows is the set of rows a bare bun update already re-resolved to a chosen version just before these loops run; it was already excluded and still is.

…catalogs change

When package.json's overrides or catalogs differ from the lockfile, the
differ replaces the root's dependency rows and then re-resolves every row
in buffers.dependencies that the change could affect. That walk also hit
the root's previous rows, which no package owns any more. The resolver
treats an unowned row as a transitive dependency, so a root file: path
outside the project failed with MissingPackageJSON once its override or
catalog entry changed, and every other stale row was resolved again for
nothing. Both passes now go through one helper that skips rows outside
every package's dependency list.
@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1b643c61-918f-4e19-bf12-5fc7b79f9f85

📥 Commits

Reviewing files that changed from the base of the PR and between 39fb3c1 and 2701ae8.

📒 Files selected for processing (3)
  • src/install/PackageManager/install_with_manager.rs
  • test/cli/install/catalogs.test.ts
  • test/cli/install/nested-overrides.test.ts

Walkthrough

The installer now uses one traversal for catalog and override dependency invalidation. New tests cover catalog file changes, peer warnings, override removal, file dependencies, lockfiles, and frozen installs.

Changes

Dependency re-resolution

Layer / File(s) Summary
Shared invalidation traversal
src/install/PackageManager/install_with_manager.rs
Catalog and override changes use reresolve_owned_rows to re-resolve affected unpinned dependencies and report errors.
Catalog validation
test/cli/install/catalogs.test.ts
Tests cover external file: catalog changes, lockfile updates, frozen installs, and single peer warnings.
Override removal regressions
test/cli/install/nested-overrides.test.ts
Tests cover resolution ordering, changed ranges, external file: dependencies, lockfile contents, and frozen installs.

Possibly related PRs

Suggested reviewers: jarred-sumner

Merge Risk: ⚪ Minimal · up to 2701a

The change is localized to dependency re-resolution behavior and includes targeted regression coverage; no actionable merge-blocking risk remains beyond normal checks and review.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the ownership-based re-resolution change for overrides and catalogs.
Description check ✅ Passed The description explains the problem, fix, scope, and verification results, including the required implementation and testing details.
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.

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

@robobun

robobun commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

Status: fix is up in this PR and ready for review. CI (build 97304): 178 of 179 jobs passed and the new tests passed on every lane, macOS included. The one failed job is a macOS 14 aarch64 shard where test/js/third_party/grpc-js/test-tonic.test.ts fails with 1 CANCELLED: Call cancelled, which this PR does not touch (reported separately); everything else the build lists is flakes that passed on retry.

Reproduced on bun 1.4.0 three ways, all from the same cause: a root "x": "file:../x" dependency installed once with an override (or a catalog: entry) for x makes every later bun install fail with error: Could not find package.json for "file:../x" dependency "x" once that override or catalog entry is removed or changed (bun.lock and bun.lockb alike); a root "no-deps": "~1.0.0" whose override is removed while the range changes to ^1.0.0 installs 1.0.1 instead of 1.1.0; and a root peer declared through a catalog entry that stops matching warns twice. The new tests in test/cli/install/nested-overrides.test.ts and test/cli/install/catalogs.test.ts fail on the released binary and pass with this change.

Out of scope here, tracked in the comments below: owned rows these passes still visit twice (unmapped root rows, workspaces about to be re-read, and rows both passes select), which only repeat a peer warning.

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

I reviewed this and didn't find any bugs. The refactor is clean and the ownership-gated re-resolve matches the existing UpdateScope::walkable_rows pattern, but since it narrows what gets re-resolved on the core install path, a human look at the lockfile/differ invariants would still be worthwhile.

Checked: the catalogs-loop predicate is a correct De Morgan of the old continue guard (empty catalog_overridden still short-circuits via binary_search().is_ok() == false); owned_rows.bit_length() equals the snapshotted buffers.dependencies.len() so the iteration bound and buffer-growth safety are preserved; kept_pruned workspace rows land inside package 0's new (off, len) slice so they remain owned and visited. Tests are hermetic (tempDir, no registry), assert installed versions + lockfile snapshots + a frozen-lockfile pass, and cover both the overrides-removed and catalog-changed variants.

Extended reasoning...

Overview

This PR fixes a bug where bun install fails with "Could not find package.json for file:../x" after removing an override (or changing a catalog entry) for a root file: dependency that points outside the project. The fix extracts the two nearly-identical re-resolve loops in install_with_manager.rs (overrides-changed and catalogs-changed) into a shared reresolve_owned_rows helper that additionally skips rows no package's dependency slice covers — the root's stale rows the differ just replaced. Three regression tests are added across nested-overrides.test.ts and catalogs.test.ts.

Security risks

None. This is package-resolution bookkeeping; no user-facing input parsing, auth, or filesystem-trust boundaries change. The file: trust rule itself (is_workspace_dependency / Folder arm) is untouched — the fix only stops feeding it dead rows.

Level of scrutiny

High. This runs on every bun install where overrides or catalogs changed relative to the lockfile, and it narrows the set of rows that get invalidated and re-enqueued. A missed row here would leave a stale resolution in place. The PR's argument that unowned rows contribute nothing (because Lockfile::clean rebuilds from package lists and the tree builder walks package lists) is sound and matches the precedent of UpdateScope::walkable_rows, update_transitive::set_rows_of, and audit_fix::live_edge — but confirming there's no path where re-resolving a dead row had a load-bearing side effect (e.g. seeding a manifest cache the live row's resolution then reuses in a way that changes outcomes) is the kind of invariant a maintainer who owns this subsystem should sign off on.

Other factors

  • The bitset construction is byte-for-byte the same shape as walkable_rows (src/install/update_scope.rs:162), including the slice.len == 0 skip and Range { begin, end } bounds, so no new edge cases in the ownership computation itself.
  • The catalogs predicate rewrite is a straight De Morgan; verified the empty-catalog_overridden case still evaluates to "only Catalog-tagged rows".
  • Iteration bound moved from dependencies_len to owned_rows.bit_length(), which is set from buffers.dependencies.len() at construction — same snapshot semantics, same protection against the buffer growing during enqueue_dependency_with_main.
  • The new ? propagates DynamicBitSet::init_empty's alloc error up through install_with_manager; consistent with surrounding OOM handling.
  • Tests follow harness conventions (tempDir, normalizeBunSnapshot, test.concurrent, no network), assert the strongest observable (installed version + full lockfile snapshot + --frozen-lockfile re-read), and the PR states they fail on the released binary.

Comment thread src/install/PackageManager/install_with_manager.rs Outdated
Comment thread src/install/PackageManager/install_with_manager.rs Outdated
@robobun

robobun commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 10:05 AM PT - Aug 15th, 2026

@robobun, your commit 2701ae8 has some failures in Build #97917 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 38849

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

bun-38849 --bun

@robobun

robobun commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

I was working on a neighbouring symptom of the same dead rows: when a root peerDependencies entry is declared through a catalog entry and that entry changes so the installed version no longer satisfies it, bun install prints warn: incorrect peer dependency "no-deps@1.0.0" twice (the old root row and the live one are both queued as peers and each binding warns). Built this PR's head (c87bf3e) and checked: that case, the same thing through a changed flat override, and an override valued catalog: all print the warning once now, so this PR covers it. It has no test for the duplicate warning itself, though; the ones on my branch below are free to take.

Three more ways the same two passes visit a row twice survive this change. All of them still print the warning twice with c87bf3e, and the rows involved are owned, so the ownership bitset does not see them:

  1. A root row the differ left unmapped (mapping[i] == invalid_package_id: its literal changed, or it is a named/bare bun update target) is re-resolved here and then again by the add/update pass right below. Repro: root peer catalog: moved to catalog:peers while that catalog is added in the same edit.
  2. A workspace: (or file: / link:) root row that is unchanged but unmapped, because the workspace's own dependencies changed or because of bun update, gets re-read from disk by the add/update pass, which replaces the package in place (folder_resolver.rs, packages.set(existing_id, ..)). When this pass runs, the package's current rows are still owned, so they are re-resolved, and the fresh rows from the re-read are resolved again afterwards. Repro: change the catalog and packages/lib/package.json in one install, or run bun update after a catalog change in a workspace repo.
  3. When both overrides and catalogs changed, a catalog: row whose name is also overridden is selected by both calls.

Branch with the variant I had ready, in case it is useful to fold in: main...farm/1fea27be/skip-replaced-rows-on-override-catalog-change (commit 2621eb9). It keeps one bitset across both passes (the one enqueue_transitive returns), seeds it with the replaced root slice, the unmapped new root rows and the dependency slices of the packages the differ knows it will re-read (a new DiffSummary::reread_in_place, filled at the two places in Diff::generate_inner where an unchanged root row is deliberately left unmapped), and has the overrides pass mark the rows it re-enqueued so the catalogs pass skips them. Tests: catalogs.test.ts "a peer whose catalog range stops matching the installed version is checked once" (seven cases; the four behind points 1 to 3 fail on c87bf3e with 2 warnings each, the inline one is the baseline) and nested-overrides.test.ts "the rows a changed rule re-resolves" (peer warning count, and the file:../outside removal this PR fixes).

Not opening a separate PR since this one owns the fix; happy for any of the above to be lifted into it, or it can follow as a small follow-up once this lands.

@robobun

robobun commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

On the one open question from the review, whether re-resolving the dead rows had any side effect the live rows relied on: I went through the channels and the only one that exists is harmful, so it is now covered by a test.

  • Lockfile::get_package_id (src/install/lockfile.rs, the try_satisfies_dedupe closure) reuses a same-major package appended earlier in the session when it satisfies the row being resolved. Dead rows come first in the buffer, so a dead row's range could pick the version for the live row. Concretely, with "no-deps": "~1.0.0" plus a flat override, removing the override while changing the range to ^1.0.0 installs 1.0.1 on the released binary (the dead ~1.0.0 row appends 1.0.1, the live ^1.0.0 row dedupes onto it) and 1.1.0 with this change. Added as "a range changed in the same edit resolves on its own" in nested-overrides.test.ts (e040163); fails 3/3 on the released binary, passes 3/3 here.
  • Manifest fetches: the live row requests the same manifest, so whether a dead row asked first only changes timing.
  • The locked-version helpers in PackageManagerEnqueue.rs are already ownership-aware: locked_version_of_invoking_workspace_row requires the row to be in the root's current list, and locked_version_in_lockfile only considers packages below loaded_package_count, so packages appended for a dead row never count as locked.
  • After resolution, Lockfile::clean and the tree builder walk package lists, so a dead row's own resolution slot and anything only it pointed at are discarded.

The PR description now includes the npm case. The two flagged comments were shortened in d1e28ff; the src change is otherwise the same as the first push.

@robobun

robobun commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, that matches what I see. Re-checked the two dead-row peer cases against this branch and lifted them into catalogs.test.ts as "a root peer whose catalog range stops matching the installed version is checked once" (bd02bc7: declared through the catalog, overridden to catalog:, inline as the baseline). They fail without the src change and pass with it, so the duplicate warning for dead rows is covered here now.

I am keeping this PR to the unowned rows, so points 1 to 3 stay open: those rows are owned at the time the passes run, and skipping them rests on a different argument (the add/update pass is about to handle them), which needs the differ plumbing from your branch. That is a reasonable change on its own, but it is a separate one from "rows nobody owns resolve as transitive", and the failing install / wrong version here should not wait on it. The description now lists those three cases as out of scope and points at this thread.

For the follow-up on top of this branch: the replaced root slice no longer needs seeding (reresolve_owned_rows derives that from the package lists), so what remains is the unmapped new root rows, reread_in_place, and having the overrides call mark what it enqueued so the catalogs call skips it. All three fit as extra bits in the bitset that helper takes, and your four remaining test cases apply unchanged.

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

I reviewed this PR and didn't find any bugs. Since it changes resolution semantics in bun install (which rows get re-resolved when overrides/catalogs change), a human look would still be worthwhile before merging.

What was reviewed:

  • reresolve_owned_rows mirrors the established UpdateScope::walkable_rows pattern; the bitset length correctly bounds the walk to rows that existed before resolution appends more.
  • The catalogs-changed predicate is semantically identical to the old inverted continue guard (binary_search on an empty vec is always Err, so the dropped is_empty() short-circuit was only an optimization).
  • When both passes run, the second call rebuilds owned_rows and picks up rows appended by the first — matching the old per-loop dependencies_len snapshot for owned rows.
Extended reasoning...

Overview

This PR extracts two near-identical re-resolution loops in install_with_manager.rs (one for overrides_changed, one for catalogs_changed) into a shared reresolve_owned_rows helper. The helper adds one filter: it builds a bitset of dependency rows currently covered by some package's (off, len) slice and skips rows no package owns. Those unowned rows are the root's stale entries from the loaded lockfile, orphaned when the differ appended fresh root rows and repointed package 0's slice. Re-resolving the orphans caused three user-visible bugs (a hard file:../x install failure, a wrong npm version via get_package_id dedupe, and a duplicate peer warning), each now covered by a test in nested-overrides.test.ts or catalogs.test.ts.

Security risks

None. No auth, crypto, network parsing, or path-traversal surface is touched; the change only narrows which already-parsed lockfile rows are re-enqueued for resolution.

Level of scrutiny

Medium-high. The diff itself is small (~50 net src lines) and copies the exact bitset-construction idiom from UpdateScope::walkable_rows, so pattern risk is low. But it sits on the bun install resolution path and is a semantic narrowing rather than a mechanical refactor: correctness rests on the argument that unowned rows contribute nothing useful to resolution. The PR description and follow-up comments walk that argument through every channel (get_package_id dedupe, locked-version helpers, manifest fetches, Lockfile::clean), and the one channel that did leak (dedupe) is the bug being fixed and now has a dedicated test. That analysis reads sound to me, but it is exactly the kind of invariant reasoning a maintainer familiar with the resolver should confirm.

Other factors

  • The two comment-cop bot flags were addressed in d1e28ff (comments shortened) and both threads are resolved.
  • A second robobun thread proposed folding in three additional owned-row double-visit cases; the author scoped those out with a clear rationale (they need differ plumbing and only affect a warning count), and the description records them as follow-up. That scoping decision seems reasonable but is worth a maintainer's nod.
  • Tests follow harness conventions (tempDir, test.concurrent, VerdaccioRegistry, frozen-lockfile round-trips) and include a baseline case ("inline") that already passed on the released binary.

…one pass

Walking the flat buffer without the root's dead rows moved the root's
live rows (appended last by the differ) behind every transitive row, so
with an unchanged root range they deduped onto whatever same-major
version a transitive row had just appended. Walking each package's
current list in package order resolves the root first again, as the
dead rows used to and as a fresh install does, and leaves the dead rows
out without a bitset. The overrides and catalogs selections are folded
into one walk so a row matching both is re-resolved once.
Comment on lines +1549 to +1553
/// Re-resolves the rows `selects`, walking each package's current dependency list in package order. The root's
/// list (just rebuilt by the differ) goes first because a later row dedupes onto what an earlier one appended
/// (`Lockfile::get_package_id`); the root's loaded rows, now in no list, would resolve as nobody's and are not
/// walked, nor are `pinned_rows`, which the update plan just resolved. A workspace the add/update pass is about
/// to re-read still holds its loaded list here and is walked like any other package.

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.

If you need a paragraph-long comment to justify why the workaround is OK, the code is wrong — fix the code

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

I reviewed this PR and didn't find any bugs. Because it changes resolution ordering in the package manager's overrides/catalogs invalidation pass (buffer order → package-list order, two loops merged into one), a human look would still be worthwhile.

What was reviewed:

  • reresolve_owned_rows: DependencySlice::begin()/end() return u32 so dep_id matches DependencyID; the lists snapshot bounds the walk against rows/packages appended mid-resolve.
  • Merged predicate vs. the two old guards: all_name_hashes is empty unless overrides changed and catalog_overridden is only filled when catalogs_changed, so the union under if invalidates_rows selects the same rows minus the unowned ones.
  • Package-order iteration puts the root's new slice first (package 0 was repointed at (off, len)); the "root's row resolves before a dependency's row" test pins that.
  • Note: comment-cop still has an open flag on the reresolve_owned_rows doc comment (line 1553).
Extended reasoning...

Overview

The PR replaces two flat 0..dependencies_len loops in install_with_manager.rs (one for overrides_changed, one for catalogs_changed) with a single reresolve_owned_rows helper that iterates packages.items_dependencies() and applies a merged selection predicate. The effect is that the root's previous dependency rows — orphaned when the differ appended fresh ones and repointed package 0 — are no longer re-resolved, and the root's current rows are visited before every other package's. Tests are added to nested-overrides.test.ts (npm range dedupe, file:../ outside the project, removed together with its rule) and catalogs.test.ts (catalog file:../ change, duplicate peer warning).

Security risks

None identified. This is internal resolution bookkeeping; no new inputs are parsed and the file: trust check itself is unchanged (the fix stops feeding it rows with no owner, which is a strictly narrower set of resolves).

Level of scrutiny

High. This is the package manager's core resolve loop, where iteration order is load-bearing (Lockfile::get_package_id dedupes onto packages appended earlier in the session) and row ownership determines whether a file: path escaping the project is accepted. The change is small in line count but alters both which rows are visited and in what order. The PR description argues carefully that dead-row resolution had no beneficial side effects (only the harmful dedupe), and that argument checks out against the code I read, but it is exactly the kind of invariant a maintainer familiar with this subsystem should confirm.

Other factors

  • CI is green on the earlier build (178/179, unrelated grpc flake); the latest commit's build (#97917) was still running at review time.
  • The candidate finding that "the root's row resolves before a dependency's row" might pass on `USE_SYSTEM_BUN=1" was examined and refuted by verifiers.
  • There is an unresolved comment-cop flag on the current HEAD (line 1553) about the doc comment length on reresolve_owned_rows. The author previously shortened comments in d1e28ff in response to two earlier flags, but the bot fired again after 2701ae8. This is stylistic, not a correctness concern, but it is technically an open inline comment.
  • The PR thread includes a detailed follow-up plan for owned rows that are still visited twice (unmapped root rows, workspaces about to be re-read, rows selected by both criteria in the old two-loop shape); the author has scoped this PR to unowned rows only, which is a reasonable boundary for a maintainer to sign off on.

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