Summary
ebuild.lock is written after every package resolution and never read. Nothing calls Lockfile.load(), and _install_packages constructs the Lockfile only after PackageResolver.resolve() has already chosen versions.
Verified: grep -rn "\.load()" ebuild/ has no lockfile caller; resolve() takes no lock. An unpinned package therefore resolves to the newest recipe on every machine and every run, while an ebuild.lock sits in the project claiming to pin it. docs/architecture.md ("records exact resolved versions for reproducibility") and the CHANGELOG's "guaranteeing reproducible builds (§9.2)" describe a file that has no effect on resolution.
Fix
PR to follow: resolve(requested, lockfile=...) with precedence request → lock → newest; a locked version no recipe provides is an error naming ebuild.lock, not a silent fallback; a locked entry whose URL/checksum no longer matches the recipe of that version is refused; the CLI loads the lock before resolving and rewrites it after, as before.
Summary
ebuild.lockis written after every package resolution and never read. Nothing callsLockfile.load(), and_install_packagesconstructs theLockfileonly afterPackageResolver.resolve()has already chosen versions.Verified:
grep -rn "\.load()" ebuild/has no lockfile caller;resolve()takes no lock. An unpinned package therefore resolves to the newest recipe on every machine and every run, while anebuild.locksits in the project claiming to pin it.docs/architecture.md("records exact resolved versions for reproducibility") and the CHANGELOG's "guaranteeing reproducible builds (§9.2)" describe a file that has no effect on resolution.Fix
PR to follow:
resolve(requested, lockfile=...)with precedence request → lock → newest; a locked version no recipe provides is an error namingebuild.lock, not a silent fallback; a locked entry whose URL/checksum no longer matches the recipe of that version is refused; the CLI loads the lock before resolving and rewrites it after, as before.