Skip to content

Cargo.lock: #1029's scrypt pins restored, and the host job resolves --locked - #1034

Merged
Japabu merged 1 commit into
mainfrom
lock-drift
Oct 23, 2026
Merged

Japabu merged 1 commit into
mainfrom
lock-drift

Conversation

@Japabu

@Japabu Japabu commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

main's Cargo.lock at 63bc8a3 (#1026) is not what cargo resolves: any build rewrites it, adding scrypt and its closure at fresh versions.

Cause. The merge queue merged #1026 onto #1029 as a clean text merge. #1029 added scrypt to toyos-crypto. Its closure (scrypt 0.12.0-rc.10, pbkdf2 0.13.0-rc.9, hmac 0.13.0-rc.5, sha2 0.11.0-rc.5, salsa20 0.11.0, cipher 0.5.1, inout 0.2.2) sat beside blocks that #1026 deleted along with russh. The merge kept toyos-crypto's "scrypt" edge and lost the packages. git merge-tree --write-tree c184a8451 2b38b4f06 reproduces main's tree exactly (ade71e07). So #1026's own dependency change was not the cause by itself; the problem was how its lock merged with #1029's.

Why CI passed. The host job's cargo run -- --ci host resolved and rewrote the lock before the driver started. Merge-group run 39872408561 on 63bc8a3 logs Adding scrypt v0.12.0 (excerpt). Every --locked inside the driver (the licences' cargo metadata, the kernel's cargo tree) then read the rewritten lock.

Change

  • Cargo.lock: main's lock with toyos-crypto: scrypt (RFC 7914) and PBKDF2, bounded by the caller's limits #1029's own pins restored. Every package version in it was already locked by one of the merge parents. A fresh resolution would also have bumped scrypt to 0.12.0 and digest, crypto-common, hmac, pbkdf2, sha2, cipher and inout (that diff). That would be a version change no resolution requires.
  • ci.yml's host job runs cargo run --locked -- --ci host. The check has to sit on that cargo, not in build/ci.rs: the outer cargo resolves before any driver code runs, so a check inside the driver only ever sees a lock that cargo has already rewritten. --locked does not change any unit's fingerprint, so the host cache's provenance is unaffected. Only the required host job carries it, because it gates every merge group, including documentation-only ones.

Other lockfiles

  • sdk/libc, tests/guest/rust-tests and its four tls-* crates: cargo metadata --locked --offline exits 0 for each.
  • The rust/ fork's library/Cargo.lock at the gitlink 9d8bb4c: the licence step resolves it --locked inside the host run below, and that step is green.
  • Fork pins: 11 of the 12 git sources are locked at their branch tip. mio's toyos branch is one commit ahead (5c122cf, "watch a stream's receive bell for its readable readiness"). The open net: a stream is shared by its holders across processes (socket sharing S2) #1012 pins that commit, so this is a pending consumer, not drift.

Evidence (head 283b6e7, macOS host)

  • cargo run --locked -- --ci host: exit 0, 101 steps green, tree clean afterwards (log)
  • cargo run --locked -- --build-only: exit 0, tree clean afterwards (log)
  • Negative control: with the fix reversed (lock equal to 63bc8a3's), cargo run --locked -- --ci host exits 101 with "cannot update the lock file", then the fix is restored (log)
  • Before the fix: cargo metadata --locked exits 101 (log); README

Risks

  • A local cargo run -- --ci host still rewrites a stale lock silently. Only the merge-group host job refuses one.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q

… --locked

#1026's merge onto #1029 was clean as text and wrong as a lock: #1029
added `scrypt` to toyos-crypto and its closure (scrypt 0.12.0-rc.10,
pbkdf2 0.13.0-rc.9, hmac 0.13.0-rc.5, sha2 0.11.0-rc.5, salsa20 0.11.0,
cipher 0.5.1, inout 0.2.2) beside blocks #1026 deleted with russh, so the
merge kept the `"scrypt"` edge and lost the packages. `git merge-tree
c184a84 2b38b4f` reproduces main's tree exactly.

The lock is main's with #1029's own pins restored: every package version
in it is one a merge parent already locked. A fresh resolution would
instead have taken scrypt 0.12.0 and bumped digest, crypto-common, hmac,
pbkdf2, sha2, cipher and inout.

CI could not see it: the host job's `cargo run` resolved and rewrote the
lock before the driver ran, so every `--locked` inside it (the licences'
`cargo metadata`, the kernel's `cargo tree`) read the rewritten one.
`cargo run --locked` refuses that rewrite where it happens.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
@Japabu

Japabu commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

Review of #1034, round 1 (head 283b6e7)

Architectural assessment

The defect is a lock that git merged cleanly as text but that cargo does not accept as a resolution. The fix puts the gate in the right place, and in the only place that works. The outer cargo run of the host job resolves before any driver code runs, so a check inside build/ci.rs would only ever see a lock cargo has already rewritten. --locked on that one invocation is the whole mechanism: one flag, with no new driver code or workflow step. The rejected alternative is a custom gate that diffs the lock in the driver. It would be more machinery, and it would be blind for the reason above. The lock change restores the merge parents' own blocks instead of re-resolving, so no unrelated versions move. Net: Cargo.lock +86/-11 (seven package blocks restored, disambiguation suffixes on existing edges), ci.yml +4/-1. No tests were added; none are needed. The claim is about one cargo flag, and the negative control covers it.

Verification (mine, not the implementer's)

  • Lock is exactly cargo's resolution. I extracted 283b6e7 with git archive into a scratch directory and ran cargo metadata --locked --offline --format-version 1: exit 0, and Cargo.lock is byte-identical afterwards.

  • Negative control (independent). In the same tree with 63bc8a3's lock, the command exits 101 with "cannot update the lock file ... because --locked was passed". The implementer's control-stale.log shows the same failure from the actual workflow command cargo run --locked -- --ci host.

  • No unneeded bumps. I compared every [[package]] block in the head lock against c184a84 (toyos-crypto: scrypt (RFC 7914) and PBKDF2, bounded by the caller's limits #1029) and 2b38b4f (sshserver on toyos-ssh with std threads; russh, tokio and rsa are gone (SSH stage 2) #1026's head). No (name, version) pair is absent from both parents, and no package of main's lock is dropped (524 vs 517 packages, +7). Where a block differs from both parents, the difference is one of two kinds:

    • Version suffixes on edges ("cipher 0.4.4", "sha2 0.10.9", "hmac 0.12.1", "inout 0.1.4"). These are required now that two versions coexist.
    • Feature-unification edges that are cargo's own output, confirmed by the --locked resolution above:
      • digest 0.11.1 gains ctutils (hmac 0.13's mac feature) and has no const-oid (rsa is gone);
      • inout 0.2.2 has no block-padding (russh's cbc is gone);
      • scrypt and toyos-crypto differ only in edge suffixes.

    pbkdf2 0.12.2 from toyos-crypto: scrypt (RFC 7914) and PBKDF2, bounded by the caller's limits #1029 is correctly absent, since only russh needed it. CI's own re-resolution shows the counterfactual: run 39872408561 added cipher 0.5.2, hmac 0.13.0, scrypt 0.12.0, sha2 0.11.1 and bumped digest and crypto-common. This fix avoids all of those.

  • Cause. 63bc8a3's parents are c184a84 and 2b38b4f, as stated. The scrypt edge in toyos-crypto survives in main while its closure is missing, which the per-revision version listing confirms.

Coverage of --locked across a merge group

  • host is the job that reaches the root lock first and gates every merge group, including documentation-only ones (scope skips only toolchain and image). A stale root lock now fails the required check. scope, toolchain and image run cargo run without --locked on their own runners. A rewrite there cannot reach the merge commit, which is fixed, and the group already fails on host. Adding the flag to those jobs would only fail them sooner. It is not needed for soundness.
  • Inner cargo invocations. The licence gate resolves sdk/libc and the fork's library/Cargo.lock with --locked (build/licence.rs:1887, :1903), and build/build.rs:2825 runs cargo tree --locked. With the outer cargo now --locked, the root lock reaching those calls is the committed one. The implementer's host run on 283b6e7 ended with a clean tree, so the driver rewrote no other lockfile on that run.
  • Cache provenance. cicache::carried keys on the driver's path (target/ci-driver), not on the command text, and --locked changes no unit fingerprint. build/CLAUDE.md's host-cache contract (no extra commands, no environment overrides) is respected. ci.rs's workflow tests check only the trigger and job shape and runner labels, so they are unaffected.
  • Nightly. nightly's host (the cache writer) still runs without --locked. It runs on main, which can now only advance through a group whose host resolved --locked, so it is covered transitively.

Local --ci host

I agree with leaving it as is. Cargo has no configuration key equivalent to --locked. The driver cannot tell a stale lock from one it has already rewritten, and it cannot tell either from a deliberate local dependency edit. A local rewrite shows up in git status and gets committed with the change. The merge queue is where a lock that is clean as text but stale as a lock arises, and that is where it is now refused. The PR's Risks section states this accurately.

Findings

No blockers.

  • SUGGESTION, tests/guest/rust-tests/**/Cargo.lock: no merge-group step resolves these five lockfiles --locked. Only nightly's guest run builds them, and it does so without the flag, so a future stale lock there would be rewritten silently in nightly. They are clean today (the implementer's cargo metadata --locked --offline on each). This is outside this PR's defect. Record it only if the orchestrator thinks it is worth an issue.
  • SUGGESTION, .github/workflows/ci.yml:45-47: the comment is accurate and worth keeping, because it stops someone from "simplifying" the flag into the driver. Its second sentence could be dropped without loss. Optional.
  • Note for the orchestrator, not the code: the handoff reports a read-only git submodule status in the worktree, which CLAUDE.md forbids. Nothing in this PR depends on it.
  • Nit, commit message: the merge-tree command it quotes omits --write-tree. The PR body and the evidence README include it.

The PR description is accurate. It presents no agent design as an owner decision.

LAND

@Japabu
Japabu marked this pull request as ready for review October 23, 2026 01:27
@Japabu
Japabu added this pull request to the merge queue Oct 23, 2026
Merged via the queue into main with commit 89138e3 Oct 23, 2026
8 checks passed
@Japabu
Japabu deleted the lock-drift branch October 23, 2026 02:06
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.

1 participant