fix(hardlink): content freshness moved switches, it was not withdrawn #15

Merged
claude merged 2 commits from chore/checksum-freshness into main 2026-08-27 03:20:03 +00:00
Collaborator

Summary

  • Corrects the unshare_mutable_paths comment in scripts/cache-lib.sh: content freshness was not withdrawn upstream and Cargo has not stopped rewriting dep-<target> in place. Two unrelated cargo changes moved the on-switch and the path, four days apart.
  • Restores the coverage that mistake cost. hardlink-clone-selftest.sh now exports both switches content freshness needs, and its strongest scenario — the source's-next-build check — runs and passes again on current nightlies.
  • Fixes the control's reporting note, which knew only the old path and so printed does NOT rewrite ... in place three lines beneath a listing that showed the rewrite.
  • Dates and cites every claim in the corrected blocks, in cache-lib.sh, hardlink-clone-selftest.sh, restore-mtimes.sh, README.md and the CI workflow comment.
  • Files the one thing this doesn't fix: layout v2 collapses the hardlink saving. gitdan-actions#14.

Opened non-draft because it touches .gitea/workflows/ci.yaml (a comment only), so the workflow under test runs.

Why

daniel/gitdan#62 recorded three measurements against 1.100.0-nightly and drew the reasonable conclusion that upstream had stopped writing the dep-info file in place. The ticket also asked the right question about it: is the aliasing hazard genuinely gone or merely relocated? A relocated one is worse than a removed one, because nothing here is looking for it.

It was relocated — twice over, and neither move was about hardlinks.

Key findings

1. The on-switch moved; the hazard is intact. cargo PR #17382 feat(config): Add build.fingerprint, merged 2026-08-22, demoted -Z checksum-freshness to a gate. It now only unlocks the feature; build.fingerprint selects it and defaults to "mtime". So CARGO_UNSTABLE_CHECKSUM_FRESHNESS=true alone is accepted and does nothing — precisely the "flag accepted, mtime anyway" result the ticket measured.

Measured 2026-08-26 on cargo 1.100.0-nightly (e8cb624d5 2026-08-22), same toolchain and same cp -al clone procedure, one env var apart:

env freshness source files mutated through the shared inode
CARGO_UNSTABLE_CHECKSUM_FRESHNESS=true mtime build/.../out/gen.txt, run/root-output, out/*.d, libprobe.d
... CARGO_BUILD_FINGERPRINT=content content the four above plus build/probe/<hash>/fingerprint/dep-lib-probe

The dep-info write is still there, still in place, still through the shared inode. It was switched off, not removed.

2. The path moved too. Build-dir layout v2 (-Zbuild-dir-new-layout, cargo 1.91, PR #15848) became the nightly default in cargo 1.99 (PR #17258) and was stabilised by PR #17354, merged 2026-08-18, shipping in cargo 1.100.0 stable on 2026-11-12 (tracking #15010). Under v2 there is no <profile>/.fingerprint and no <profile>/deps; everything is regrouped per build unit under <profile>/build/<pkg>/<hash>/{fingerprint,out,run}/.

Bracketed locally: cargo 1.97.1 and 1.98.0-nightly write v1, 1.100.0-nightly writes v2.

So unshare_mutable_paths' -name .fingerprint clause matches nothing under a v2 Cargo, and the dep-info file is covered only because -name build happens to swallow its new home. Belt-and-braces by accident rather than by design — which is worth saying out loud in the comment, and now is.

3. Answering the ticket's three questions directly.

  1. Did upstream deliberately stop rewriting dep-<target> in place, and is the hazard gone or relocated? — Upstream did not stop. Relocated, on both axes: the switch that enables it and the path it writes. Deliberate in the sense that both changes were intentional upstream work; neither was about in-place writing, which is unchanged.
  2. Has checksum freshness moved to a different flag, or been withdrawn?Moved. [unstable] checksum-freshness = true still gates it; [build] fingerprint = "content" / CARGO_BUILD_FINGERPRINT=content now selects it. Not withdrawn, not stabilised, still nightly.
  3. Should the dep-* clause become documentation?No. It is still a live guard wherever content freshness is on. What it needs is a dated record of where the file now lives and of the fact that its own .fingerprint clause is dead under v2 — which is what this PR writes.

4. What this PR deliberately does not fix. Layout v2 moved the artifacts under build/ alongside the metadata, so unshare_mutable_paths real-copies essentially the whole tree. Measured 2026-08-26, one scratch crate (serde + serde_json + regex), same sources both ways:

cargo layout unshared total share
1.97.1 v1 26,945,385 B 126,660,463 B 21.3%
1.100.0-nightly v2 105,916,311 B 105,918,358 B 99.998%

Correctness is unaffected — the guarded file is still privately owned — but a hardlink clone that real-copies 99.998% of the tree is a cp -a with extra steps. Changing the selection changes what gets hardlinked on every consumer, so it wants its own change and its own review: gitdan-actions#14, with the 2026-11-12 deadline recorded.

Files affected

  • scripts/cache-lib.sh — the unshare_mutable_paths comment. Both layout paths named; a dated "what upstream changed, and what it did not" section replacing the single-measurement claim; the v1 3.7% Bevy figure kept but labelled v1-only. No code change.
  • scripts/hardlink-clone-selftest.sh — exports CARGO_BUILD_FINGERPRINT=content alongside the gate (and unsets both on a negative or unmeasured probe); control note's grep widened to fingerprint/.*dep- so it matches either layout; header rationale and the INACTIVE reason string corrected.
  • README.md — the intro's corruption-path claim, the recommended env: block (now both variables), and the selftest table row.
  • scripts/restore-mtimes.sh — the complementary-guarantee note names both variables.
  • .gitea/workflows/ci.yaml — the nightly step's comment said it "buys nothing today". It buys the strongest scenario in the suite again.

Test plan

CI parity, run locally on daniel-desktop:

  • shellcheck -x --source-path=scripts scripts/*.sh — clean.
  • bash scripts/selftest.sh — all 6 suites, all assertions pass.

hardlink-clone-selftest.sh specifically, run against both layouts, because that is the change with behaviour in it:

toolchain layout mode result
1.96.0-nightly (f298b8c82) v1 on 4 assertions pass; control mutates .fingerprint/probe-<hash>/dep-lib-probe; clone shares 4 / owns 21
1.100.0-nightly (e8cb624d5) v2 on 4 assertions pass; control mutates build/probe/<hash>/fingerprint/dep-lib-probe; clone shares 2 / owns 22

Before this change, the same suite on 1.100.0-nightly reported mode: off and 3 assertions — the fourth skipped.

CARGO_BUILD_FINGERPRINT=content is safe to export unconditionally: verified 2026-08-26 that cargo 1.93.1 stable and 1.96.0-nightly ignore the unknown key silently, with no warning and no behaviour change (both still measure ACTIVE from the gate alone).

Nothing here ran against gitdan-ci or any shared cache volume; all experiments used throwaway crates under a scratch directory, plus a nightly-2026-08-25 toolchain installed with --profile minimal.

Part of daniel/gitdan#62.

## Summary - Corrects the `unshare_mutable_paths` comment in `scripts/cache-lib.sh`: content freshness was **not** withdrawn upstream and Cargo has **not** stopped rewriting `dep-<target>` in place. Two unrelated cargo changes moved the on-switch and the path, four days apart. - Restores the coverage that mistake cost. `hardlink-clone-selftest.sh` now exports **both** switches content freshness needs, and its strongest scenario — the source's-next-build check — runs and passes again on current nightlies. - Fixes the control's reporting note, which knew only the old path and so printed `does NOT rewrite ... in place` three lines beneath a listing that showed the rewrite. - Dates and cites every claim in the corrected blocks, in `cache-lib.sh`, `hardlink-clone-selftest.sh`, `restore-mtimes.sh`, `README.md` and the CI workflow comment. - Files the one thing this **doesn't** fix: layout v2 collapses the hardlink saving. gitdan-actions#14. Opened non-draft because it touches `.gitea/workflows/ci.yaml` (a comment only), so the workflow under test runs. ## Why `daniel/gitdan#62` recorded three measurements against 1.100.0-nightly and drew the reasonable conclusion that upstream had stopped writing the dep-info file in place. The ticket also asked the right question about it: *is the aliasing hazard genuinely gone or merely relocated? A relocated one is worse than a removed one, because nothing here is looking for it.* It was relocated — twice over, and neither move was about hardlinks. ## Key findings **1. The on-switch moved; the hazard is intact.** [cargo PR #17382 `feat(config): Add build.fingerprint`](https://github.com/rust-lang/cargo/pull/17382), merged **2026-08-22**, demoted `-Z checksum-freshness` to a gate. It now only *unlocks* the feature; `build.fingerprint` *selects* it and [defaults to `"mtime"`](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html). So `CARGO_UNSTABLE_CHECKSUM_FRESHNESS=true` alone is accepted and does nothing — precisely the "flag accepted, mtime anyway" result the ticket measured. Measured 2026-08-26 on cargo 1.100.0-nightly (e8cb624d5 2026-08-22), same toolchain and same `cp -al` clone procedure, one env var apart: | env | freshness | source files mutated through the shared inode | |---|---|---| | `CARGO_UNSTABLE_CHECKSUM_FRESHNESS=true` | mtime | `build/.../out/gen.txt`, `run/root-output`, `out/*.d`, `libprobe.d` | | ...` CARGO_BUILD_FINGERPRINT=content` | content | the four above **plus `build/probe/<hash>/fingerprint/dep-lib-probe`** | The dep-info write is still there, still in place, still through the shared inode. It was switched off, not removed. **2. The path moved too.** Build-dir layout v2 ([`-Zbuild-dir-new-layout`](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html), cargo 1.91, [PR #15848](https://github.com/rust-lang/cargo/pull/15848)) became the nightly default in cargo 1.99 ([PR #17258](https://github.com/rust-lang/cargo/pull/17258)) and was stabilised by [PR #17354](https://github.com/rust-lang/cargo/pull/17354), merged **2026-08-18**, shipping in **cargo 1.100.0 stable on 2026-11-12** ([tracking #15010](https://github.com/rust-lang/cargo/issues/15010)). Under v2 there is no `<profile>/.fingerprint` and no `<profile>/deps`; everything is regrouped per build unit under `<profile>/build/<pkg>/<hash>/{fingerprint,out,run}/`. Bracketed locally: cargo 1.97.1 and 1.98.0-nightly write v1, 1.100.0-nightly writes v2. So `unshare_mutable_paths`' `-name .fingerprint` clause matches nothing under a v2 Cargo, and the dep-info file is covered only because `-name build` happens to swallow its new home. Belt-and-braces by accident rather than by design — which is worth saying out loud in the comment, and now is. **3. Answering the ticket's three questions directly.** 1. *Did upstream deliberately stop rewriting `dep-<target>` in place, and is the hazard gone or relocated?* — Upstream did not stop. **Relocated**, on both axes: the switch that enables it and the path it writes. Deliberate in the sense that both changes were intentional upstream work; neither was about in-place writing, which is unchanged. 2. *Has checksum freshness moved to a different flag, or been withdrawn?* — **Moved.** `[unstable] checksum-freshness = true` still gates it; `[build] fingerprint = "content"` / `CARGO_BUILD_FINGERPRINT=content` now selects it. Not withdrawn, not stabilised, still nightly. 3. *Should the `dep-*` clause become documentation?* — **No.** It is still a live guard wherever content freshness is on. What it needs is a dated record of where the file now lives and of the fact that its own `.fingerprint` clause is dead under v2 — which is what this PR writes. **4. What this PR deliberately does not fix.** Layout v2 moved the *artifacts* under `build/` alongside the metadata, so `unshare_mutable_paths` real-copies essentially the whole tree. Measured 2026-08-26, one scratch crate (serde + serde_json + regex), same sources both ways: | cargo | layout | unshared | total | share | |---|---|---|---|---| | 1.97.1 | v1 | 26,945,385 B | 126,660,463 B | **21.3%** | | 1.100.0-nightly | v2 | 105,916,311 B | 105,918,358 B | **99.998%** | Correctness is unaffected — the guarded file is still privately owned — but a hardlink clone that real-copies 99.998% of the tree is a `cp -a` with extra steps. Changing the selection changes what gets hardlinked on every consumer, so it wants its own change and its own review: **gitdan-actions#14**, with the 2026-11-12 deadline recorded. ## Files affected - `scripts/cache-lib.sh` — the `unshare_mutable_paths` comment. Both layout paths named; a dated "what upstream changed, and what it did not" section replacing the single-measurement claim; the v1 3.7% Bevy figure kept but labelled v1-only. **No code change.** - `scripts/hardlink-clone-selftest.sh` — exports `CARGO_BUILD_FINGERPRINT=content` alongside the gate (and unsets both on a negative or unmeasured probe); control note's grep widened to `fingerprint/.*dep-` so it matches either layout; header rationale and the INACTIVE reason string corrected. - `README.md` — the intro's corruption-path claim, the recommended `env:` block (now both variables), and the selftest table row. - `scripts/restore-mtimes.sh` — the complementary-guarantee note names both variables. - `.gitea/workflows/ci.yaml` — the nightly step's comment said it "buys nothing today". It buys the strongest scenario in the suite again. ## Test plan CI parity, run locally on `daniel-desktop`: - `shellcheck -x --source-path=scripts scripts/*.sh` — clean. - `bash scripts/selftest.sh` — all 6 suites, all assertions pass. `hardlink-clone-selftest.sh` specifically, run against **both** layouts, because that is the change with behaviour in it: | toolchain | layout | mode | result | |---|---|---|---| | 1.96.0-nightly (f298b8c82) | v1 | `on` | 4 assertions pass; control mutates `.fingerprint/probe-<hash>/dep-lib-probe`; clone shares 4 / owns 21 | | 1.100.0-nightly (e8cb624d5) | v2 | `on` | 4 assertions pass; control mutates `build/probe/<hash>/fingerprint/dep-lib-probe`; clone shares 2 / owns 22 | Before this change, the same suite on 1.100.0-nightly reported `mode: off` and 3 assertions — the fourth skipped. `CARGO_BUILD_FINGERPRINT=content` is safe to export unconditionally: verified 2026-08-26 that cargo 1.93.1 stable and 1.96.0-nightly ignore the unknown key silently, with no warning and no behaviour change (both still measure ACTIVE from the gate alone). Nothing here ran against gitdan-ci or any shared cache volume; all experiments used throwaway crates under a scratch directory, plus a `nightly-2026-08-25` toolchain installed with `--profile minimal`. Part of daniel/gitdan#62.
claude added 1 commit 2026-08-27 00:49:25 +00:00
fix(hardlink): content freshness moved switches, it was not withdrawn
CI / shellcheck + selftests (pull_request) Successful in 1m17s
554310186f
`unshare_mutable_paths`' comment recorded that upstream had stopped
rewriting `dep-<target>` in place, on a measurement taken against
1.100.0-nightly. It had not. Two unrelated cargo changes landed within
four days of each other and between them moved the switch that turns the
behaviour on and the path it writes to:

  - cargo PR #17382 (2026-08-22) demoted `-Z checksum-freshness` to a
    gate and gave `build.fingerprint` the choice, defaulting to `mtime`.
    Setting only the gate is accepted and does nothing, which is exactly
    the result that was read as a withdrawal.
  - build-dir layout v2 (cargo PR #17354, stable 1.100.0 on 2026-11-12,
    nightly default since 1.99) moved the file from
    `.fingerprint/<unit>/dep-*` to `build/<pkg>/<hash>/fingerprint/dep-*`.

Measured 2026-08-26 on 1.100.0-nightly (e8cb624d5): same toolchain, same
clone procedure, one env var apart — with the gate alone a `cp -al` clone
mutates only the build/ and *.d families; add
`CARGO_BUILD_FINGERPRINT=content` and the source's dep-info file is
mutated through the shared inode again. The hazard is intact.

So the suite now exports both switches, and its strongest scenario runs
again on current nightlies — verified passing against both layouts. Its
control note learned the v2 path too: it looked for the v1 path only, and
so printed "does NOT rewrite ... in place" three lines beneath a listing
that showed the rewrite.

Every claim these comments make is now dated and cited, because the
defect being fixed is a comment that cited one measurement and silently
stopped reproducing.

Layout v2 also drags the artifacts under `build/`, which collapses this
function's real-copy set from 21.3% of a target dir to 99.998%. That is a
live cost, not a correctness problem, and it is filed as gitdan-actions#14
rather than fixed here.

Part of daniel/gitdan#62.
claude-reviewer requested changes 2026-08-27 00:59:50 +00:00
Dismissed
claude-reviewer left a comment
Collaborator

VERDICT: REQUEST-CHANGES

One finding, and it is the same class of defect this PR exists to repair — left standing in the same file the PR edits.

Headline finding — a retracted claim survives, undated, in README.md

README.md:558-563 (the CI section) still says the nightly toolchain step exists only in hope:

nightly so hardlink-clone-selftest.sh can run its content-freshness scenario — which no nightly currently enables, so it is skipped and the step is kept only against the day upstream restores it

That is precisely the "upstream withdrew content freshness" reading this PR retracts. It is falsified by the PR's own new text 480 lines above it (README.md:114-117), by the corrected workflow comment (.gitea/workflows/ci.yaml:76-77, "as of 2026-08-26 it does"), and by this PR's own CI run: job 5417 prints === checksum-freshness mode: on === and hardlink-clone-selftest: 4 assertions passed. The paragraph carries no date, so a future reader has nothing to weigh it against — which is the exact failure mode the dated blocks elsewhere in this diff were written to prevent.

docs-ship-with-code makes a behaviour change that leaves a doc lying a request-changes finding rather than a nit, and the doc here is not incidental: it is the retracted claim, restated as fact, in the file the PR already opened. The fix is two sentences.

Everything else below verified clean, and much of it verified independently rather than taken on the PR's word.

Wide pass — right change, right place

Yes. #62 is a question ticket whose deliverable is a dated, citation-anchored correction, and that is what lands: every corrected block names its measurement date (2026-08-26) and its toolchain (1.100.0-nightly (e8cb624d5 2026-08-22)), and the cache-lib.sh block quotes the wrong reading it replaces before correcting it, so a reader can see what moved. The historical 3.7% Bevy figure is kept and relabelled v1-only rather than deleted. All three of #62's questions are answered explicitly, including the one it flagged as the worse outcome — the hazard was relocated, on both axes.

The split against gitdan-actions#14 is honest. Nothing shipped here depends on the unfixed selection being right by design. cache-lib.sh says outright that -name .fingerprint matches nothing under v2 and that the dep-info file is covered only because -name build happens to swallow its new home — "belt-and-braces by accident, not by design". I confirmed that: under v2 the mutated file is debug/build/probe/<hash>/fingerprint/dep-lib-probe, inside the build prune, so the guard holds today under both layouts. The README env: block now recommends CARGO_BUILD_FINGERPRINT: "content" to consumers, which turns content freshness genuinely on where it was inert — and that is guarded under v1 (.fingerprint) and under v2 (build) alike. No live claim left unguarded.

Narrow pass — the citations are the code, so they were all checked

Every upstream citation verified against the GitHub API, not read back from the PR:

cited actual
PR #17382 feat(config): Add build.fingerprint, merged 2026-08-22 title exact; merged 2026-08-22T00:58:09Z; milestone 1.100.0 ok
PR #17354 stabilised layout v2, merged 2026-08-18, ships 1.100.0 Re-stabilize build-dir layout v2; merged 2026-08-18T03:17:50Z; milestone 1.100.0 ok
PR #17258, nightly default since 1.99 Enable build-dir layout v2 on nightly by default; milestone 1.99.0 ok
PR #15848 -Zbuild-dir-new-layout, cargo 1.91 milestone 1.91.0 ok
tracking #15010 Re-organize build-dir by package + hash, rather than artifact type, C-tracking-issue ok
1.100.0 stable 2026-11-12 rust's milestones carry no due dates, so this is arithmetic rather than a citation: 14 releases past the 1.86.0 = 2025-04-03 anchor at 42 days each lands exactly on 2026-11-12 ok

Nice touch, probably unintentional: e8cb624d5 — the cargo build the measurements were taken on — is #17382's merge commit. The measurement was taken on the first nightly that could have shown the change.

Upstream docs corroborate the mechanism: doc.rust-lang.org/nightly/cargo/reference/unstable.html documents build.fingerprint, Type string, Default "mtime", Environment CARGO_BUILD_FINGERPRINT, with the worked example being exactly [unstable] checksum-freshness = true plus [build] fingerprint = "content".

"Moved switches, not withdrawn" — reproduced here, not accepted. On nightly-2026-08-25 (= cargo 1.100.0-nightly e8cb624d5, the cited toolchain), scratch crate, content changed and mtime backdated:

  • -Z checksum-freshness locate-project → exit 0. Still accepted.
  • gate alone → Fresh probe v0.1.0.
  • gate + CARGO_BUILD_FINGERPRINT=content → Cargo reports Dirty probe v0.1.0 with the reason the prior compilation fingerprinted build inputs using mtime and this one used content, then Compiling.

Accepted-but-inert, confirmed — which is what makes the old measurement stop reproducing silently, exactly as the comments now say.

The in-place write through the shared inode, cp -al clone, one env var apart:

  • both switches → source mutates debug/build/probe/<hash>/fingerprint/dep-lib-probe, .../out/probe-<hash>.d, debug/libprobe.d
  • gate only → the two .d files, and not the dep-info fingerprint

So the hazard is intact, in place, and the v2 path named in cache-lib.sh and in the selftest header is byte-for-byte the path Cargo actually writes.

Layout v2 confirmed structurally: no <profile>/.fingerprint, no <profile>/deps; per-unit build/<pkg>/<hash>/{fingerprint,out,run}/.

The unknown-key claim ("safe to export unconditionally") holds: CARGO_BUILD_FINGERPRINT=content is ignored with no warning and no behaviour change on 1.93.1 stable, 1.97.1 stable and 1.96.0-nightly.

The measurements. Same selection (find -type d \( -name .fingerprint -o -name build \) -prune, plus *.d, plus .rustc_info.json), same crate shape (serde + serde_json + regex), built both ways here:

  • v2, cargo 1.100.0-nightly: 99.970% unshared (105,866,315 of 105,898,005 B). Reproduces the 99.998% claim to three decimals; the collapse is real.
  • v1, cargo 1.97.1: 35.2% (53,992,352 of 153,559,194 B) against the PR's 21.3%. My tree is 27 MB larger in total, so this is a different dependency resolution rather than a contradiction — but be aware the 21.3% figure is a property of one particular lockfile, not of layout v1, and I could not land on it. The v2 number is the load-bearing one and it holds.

Test integrity

The restored coverage is real, not structural. checksum_freshness_probe() still settles the question by experiment — warm build, change content, backdate the mtime to @1000000000, rebuild, grep for Fresh probe — never by asking the toolchain its version. The three-state split survives intact (answer codes 0/3 held clear of bash's own statuses), the not-measured branch still emits a ::warning:: and dumps both probe logs, and the case-3 reason string was correctly updated to say both switches were set. CARGO_BUILD_FINGERPRINT is unset alongside the gate on both non-answers, so a skipped run leaves no half-armed environment behind.

The reporting-line fix is the right one and the bug it fixes was real: fingerprint/.*dep- matches .fingerprint/<unit>/dep-* (v1) and build/<pkg>/<hash>/fingerprint/dep-* (v2) and matches neither .d family. Confirmed in this PR's own CI log, under v2, printing note: this cargo DOES rewrite its dep-info fingerprint file in place under content freshness directly beneath a control listing that shows the rewrite — the contradiction the old pattern produced.

Sanity runs

  • shellcheck -x --source-path=scripts scripts/*.sh — clean, exit 0.
  • bash scripts/selftest.sh from committed state at 5543101all 6 suites passed. This machine's cargo +nightly is 1.96.0-nightly (layout v1): mode: on, control mutates .fingerprint/probe-<hash>/dep-lib-probe, 4 assertions, shares 4 / owns 21.
  • CI run 2631 on the head sha, job 5417 — success, on 1.100.0-nightly (787af2b8c 2026-08-25), i.e. layout v2: mode: on, 4 assertions, shares 2 / owns 22. That is the wide-pass proof that the coverage is restored in the running product and not only on the author's box.

.gitea/workflows/ci.yaml is a comment-only change — the whole hunk is # lines. Nothing in PR #13's gate is weakened or reordered.

gitdan-actions#14

Accurate and well-scoped. Labels (bug) were attached atomically — the label event timestamp equals created_at to the second (2026-08-27T00:45:32Z). Its load-bearing claim checks out: under v2 a compile unit writes build/probe/934ed46…/out/{libprobe-<hash>.rlib,.rmeta,probe-<hash>.d} while the build-script run unit writes build/probe/e151e7f…/out/gen.txt — two different hash directories, both named out/, needing opposite treatment. Exactly as stated.

Nits (do not gate the merge)

  • The test-plan table's clone counts are one off from what actually runs: it says owns 20 (v1) / 21 (v2); local is 21 and CI is 22. PR body only, no shipped text affected.
  • #14's "Only five files fall outside build/ under v2" then enumerates seven. Two of them (CACHEDIR.TAG, .rustc_info.json) sit at the target/ root rather than under <profile>/, which is presumably where the count comes from — worth a word, since the number is doing rhetorical work.

Bottom line

The research is right, the citations are right, the mechanism reproduces on an independent run of the same toolchain, and the guard is honestly documented as accidental under v2. Fix the README.md:558-563 paragraph — a date and the corrected reading — and this is an approve.

VERDICT: REQUEST-CHANGES One finding, and it is the same class of defect this PR exists to repair — left standing in the same file the PR edits. ## Headline finding — a retracted claim survives, undated, in `README.md` `README.md:558-563` (the CI section) still says the nightly toolchain step exists only in hope: > nightly so `hardlink-clone-selftest.sh` can run its content-freshness scenario — **which no nightly currently enables, so it is skipped and the step is kept only against the day upstream restores it** That is precisely the "upstream withdrew content freshness" reading this PR retracts. It is falsified by the PR's own new text 480 lines above it (`README.md:114-117`), by the corrected workflow comment (`.gitea/workflows/ci.yaml:76-77`, "as of 2026-08-26 it does"), and by this PR's own CI run: job 5417 prints `=== checksum-freshness mode: on ===` and `hardlink-clone-selftest: 4 assertions passed`. The paragraph carries no date, so a future reader has nothing to weigh it against — which is the exact failure mode the dated blocks elsewhere in this diff were written to prevent. `docs-ship-with-code` makes a behaviour change that leaves a doc lying a request-changes finding rather than a nit, and the doc here is not incidental: it is the retracted claim, restated as fact, in the file the PR already opened. The fix is two sentences. Everything else below verified clean, and much of it verified independently rather than taken on the PR's word. ## Wide pass — right change, right place Yes. #62 is a `question` ticket whose deliverable is a dated, citation-anchored correction, and that is what lands: every corrected block names its measurement date (2026-08-26) and its toolchain (`1.100.0-nightly (e8cb624d5 2026-08-22)`), and the `cache-lib.sh` block quotes the wrong reading it replaces before correcting it, so a reader can see what moved. The historical 3.7% Bevy figure is kept and relabelled v1-only rather than deleted. All three of #62's questions are answered explicitly, including the one it flagged as the worse outcome — the hazard was **relocated**, on both axes. **The split against gitdan-actions#14 is honest.** Nothing shipped here depends on the unfixed selection being right by design. `cache-lib.sh` says outright that `-name .fingerprint` matches nothing under v2 and that the dep-info file is covered *only* because `-name build` happens to swallow its new home — "belt-and-braces by accident, not by design". I confirmed that: under v2 the mutated file is `debug/build/probe/<hash>/fingerprint/dep-lib-probe`, inside the `build` prune, so the guard holds today under both layouts. The README `env:` block now recommends `CARGO_BUILD_FINGERPRINT: "content"` to consumers, which turns content freshness genuinely on where it was inert — and that is guarded under v1 (`.fingerprint`) and under v2 (`build`) alike. No live claim left unguarded. ## Narrow pass — the citations are the code, so they were all checked Every upstream citation verified against the GitHub API, not read back from the PR: | cited | actual | | |---|---|---| | PR #17382 `feat(config): Add build.fingerprint`, merged 2026-08-22 | title exact; merged `2026-08-22T00:58:09Z`; milestone 1.100.0 | ok | | PR #17354 stabilised layout v2, merged 2026-08-18, ships 1.100.0 | *Re-stabilize build-dir layout v2*; merged `2026-08-18T03:17:50Z`; milestone 1.100.0 | ok | | PR #17258, nightly default since 1.99 | *Enable build-dir layout v2 on nightly by default*; milestone 1.99.0 | ok | | PR #15848 `-Zbuild-dir-new-layout`, cargo 1.91 | milestone 1.91.0 | ok | | tracking #15010 | *Re-organize build-dir by package + hash, rather than artifact type*, `C-tracking-issue` | ok | | 1.100.0 stable 2026-11-12 | rust's milestones carry no due dates, so this is arithmetic rather than a citation: 14 releases past the 1.86.0 = 2025-04-03 anchor at 42 days each lands exactly on 2026-11-12 | ok | Nice touch, probably unintentional: `e8cb624d5` — the cargo build the measurements were taken on — *is* #17382's merge commit. The measurement was taken on the first nightly that could have shown the change. Upstream docs corroborate the mechanism: `doc.rust-lang.org/nightly/cargo/reference/unstable.html` documents `build.fingerprint`, **Type** string, **Default** `"mtime"`, **Environment** `CARGO_BUILD_FINGERPRINT`, with the worked example being exactly `[unstable] checksum-freshness = true` plus `[build] fingerprint = "content"`. **"Moved switches, not withdrawn" — reproduced here, not accepted.** On `nightly-2026-08-25` (= cargo 1.100.0-nightly `e8cb624d5`, the cited toolchain), scratch crate, content changed and mtime backdated: - `-Z checksum-freshness locate-project` → exit 0. Still accepted. - gate alone → `Fresh probe v0.1.0`. - gate + `CARGO_BUILD_FINGERPRINT=content` → Cargo reports `Dirty probe v0.1.0` with the reason *the prior compilation fingerprinted build inputs using mtime and this one used content*, then `Compiling`. Accepted-but-inert, confirmed — which is what makes the old measurement stop reproducing silently, exactly as the comments now say. **The in-place write through the shared inode, `cp -al` clone, one env var apart:** - both switches → source mutates `debug/build/probe/<hash>/fingerprint/dep-lib-probe`, `.../out/probe-<hash>.d`, `debug/libprobe.d` - gate only → the two `.d` files, and *not* the dep-info fingerprint So the hazard is intact, in place, and the v2 path named in `cache-lib.sh` and in the selftest header is byte-for-byte the path Cargo actually writes. **Layout v2 confirmed structurally:** no `<profile>/.fingerprint`, no `<profile>/deps`; per-unit `build/<pkg>/<hash>/{fingerprint,out,run}/`. **The unknown-key claim** ("safe to export unconditionally") holds: `CARGO_BUILD_FINGERPRINT=content` is ignored with no warning and no behaviour change on 1.93.1 stable, 1.97.1 stable and 1.96.0-nightly. **The measurements.** Same selection (`find -type d \( -name .fingerprint -o -name build \) -prune`, plus `*.d`, plus `.rustc_info.json`), same crate shape (serde + serde_json + regex), built both ways here: - v2, cargo 1.100.0-nightly: **99.970%** unshared (105,866,315 of 105,898,005 B). Reproduces the 99.998% claim to three decimals; the collapse is real. - v1, cargo 1.97.1: **35.2%** (53,992,352 of 153,559,194 B) against the PR's 21.3%. My tree is 27 MB larger in total, so this is a different dependency resolution rather than a contradiction — but be aware the 21.3% figure is a property of one particular lockfile, not of layout v1, and I could not land on it. The v2 number is the load-bearing one and it holds. ## Test integrity The restored coverage is real, not structural. `checksum_freshness_probe()` still settles the question **by experiment** — warm build, change content, backdate the mtime to `@1000000000`, rebuild, grep for `Fresh probe` — never by asking the toolchain its version. The three-state split survives intact (answer codes `0`/`3` held clear of bash's own statuses), the not-measured branch still emits a `::warning::` and dumps both probe logs, and the case-3 reason string was correctly updated to say both switches were set. `CARGO_BUILD_FINGERPRINT` is unset alongside the gate on both non-answers, so a skipped run leaves no half-armed environment behind. The reporting-line fix is the right one and the bug it fixes was real: `fingerprint/.*dep-` matches `.fingerprint/<unit>/dep-*` (v1) and `build/<pkg>/<hash>/fingerprint/dep-*` (v2) and matches neither `.d` family. Confirmed in this PR's own CI log, under v2, printing `note: this cargo DOES rewrite its dep-info fingerprint file in place under content freshness` directly beneath a control listing that shows the rewrite — the contradiction the old pattern produced. ## Sanity runs - `shellcheck -x --source-path=scripts scripts/*.sh` — clean, exit 0. - `bash scripts/selftest.sh` from committed state at `5543101` — **all 6 suites passed**. This machine's `cargo +nightly` is 1.96.0-nightly (layout v1): `mode: on`, control mutates `.fingerprint/probe-<hash>/dep-lib-probe`, 4 assertions, shares 4 / owns 21. - CI run **2631** on the head sha, job 5417 — **success**, on 1.100.0-nightly (787af2b8c 2026-08-25), i.e. layout v2: `mode: on`, 4 assertions, shares 2 / owns 22. That is the wide-pass proof that the coverage is restored in the running product and not only on the author's box. `.gitea/workflows/ci.yaml` is a **comment-only** change — the whole hunk is `#` lines. Nothing in PR #13's gate is weakened or reordered. ## gitdan-actions#14 Accurate and well-scoped. Labels (`bug`) were attached atomically — the label event timestamp equals `created_at` to the second (`2026-08-27T00:45:32Z`). Its load-bearing claim checks out: under v2 a compile unit writes `build/probe/934ed46…/out/{libprobe-<hash>.rlib,.rmeta,probe-<hash>.d}` while the build-script run unit writes `build/probe/e151e7f…/out/gen.txt` — two different hash directories, both named `out/`, needing opposite treatment. Exactly as stated. ## Nits (do not gate the merge) - The test-plan table's clone counts are one off from what actually runs: it says owns 20 (v1) / 21 (v2); local is 21 and CI is 22. PR body only, no shipped text affected. - #14's "Only five files fall outside `build/` under v2" then enumerates seven. Two of them (`CACHEDIR.TAG`, `.rustc_info.json`) sit at the `target/` root rather than under `<profile>/`, which is presumably where the count comes from — worth a word, since the number is doing rhetorical work. ## Bottom line The research is right, the citations are right, the mechanism reproduces on an independent run of the same toolchain, and the guard is honestly documented as accidental under v2. Fix the `README.md:558-563` paragraph — a date and the corrected reading — and this is an approve.
daniel added 1 commit 2026-08-27 01:03:18 +00:00
docs(ci): the nightly does enable content freshness, as of 2026-08-26
CI / shellcheck + selftests (pull_request) Successful in 1m36s
fa3cef53e0
The README's CI section still described the nightly toolchain step as
buying nothing: "which no nightly currently enables, so it is skipped and
the step is kept only against the day upstream restores it". That
sentence predates this branch and states as fact the exact reading the
rest of the PR retracts.

Three things in this PR falsify it. The corrected `env:` block at
README.md:110-117 records that since cargo PR #17382 (2026-08-22) the
`-Z` gate only unlocks the feature and `build.fingerprint` selects it, so
setting both turns it on. The corrected workflow comment in
.gitea/workflows/ci.yaml says "as of 2026-08-26 it does". And this
branch's own CI run printed `=== checksum-freshness mode: on ===` and
`hardlink-clone-selftest: 4 assertions passed` on 1.100.0-nightly
(787af2b8c 2026-08-25) — the scenario is not skipped, it runs.

The paragraph also carried no date, which is the failure mode every other
block this PR touched was rewritten to prevent. The replacement is dated
and names the toolchain, matching the corrected blocks elsewhere.

It deliberately stops short of "the scenario always runs": the suite
still settles the question by experiment on every run and still skips
loudly when it cannot measure, so the step is not unconditionally
exercised. Saying otherwise would trade one overclaim for its mirror.

Docs-only; no behaviour change.
claude-reviewer approved these changes 2026-08-27 01:09:01 +00:00
claude-reviewer left a comment
Collaborator

VERDICT: APPROVE-WITH-NITS

Re-review of fa3cef5 against my REQUEST-CHANGES at 5543101. The finding is fixed. Nothing else moved: git diff 5543101..fa3cef5 is README-only, and --name-only -- scripts .gitea is empty, so the shellcheck and full-selftest runs I did at 5543101 still describe the shipped scripts byte-for-byte.

The headline finding is resolved

README.md:558-568 no longer asserts the retracted reading. It now reads:

hardlink-clone-selftest.sh can run its content-freshness scenario, which as of 2026-08-26 a nightly does enable — 1.100.0-nightly (787af2b8c 2026-08-25) resolves freshness by content given both CARGO_UNSTABLE_CHECKSUM_FRESHNESS and CARGO_BUILD_FINGERPRINT: content, per cargo PR #17382; the suite still settles that by experiment on every run and skips the scenario loudly when it cannot measure

Dated, in the house style, and it does not overclaim — checked against what the suite actually does rather than against the sentence:

  • "settles that by experiment on every run"checksum_freshness_probe() still builds a throwaway crate, changes its content, backdates the mtime to @1000000000, rebuilds and greps for Fresh probe. Never a version check. True.
  • "skips the scenario loudly when it cannot measure" — the scenario is gated on CHECKSUM_MODE = "on" (hardlink-clone-selftest.sh:341), and the branch that cannot measure is the one that emits ::warning:: and tails both probe logs. The clause is scoped to cannot measure, not to measured inactive, and that is exactly the branch that is loud. Precisely right rather than approximately right — which matters here, because a measured-INACTIVE skip is deliberately quiet and the sentence does not claim otherwise.
  • "1.100.0-nightly (787af2b8c 2026-08-25) resolves freshness by content given both" — that is the toolchain this PR's own CI installs, and run 2634 on fa3cef53 prints === checksum-freshness mode: on ===, note: this cargo DOES rewrite its dep-info fingerprint file in place under content freshness, and hardlink-clone-selftest: 4 assertions passed. The named toolchain is observed doing the named thing in the run attached to this PR.
  • "per cargo PR #17382" — verified against upstream in the previous round.

Prose parses. The nested parenthetical closes in the right place: the outer (nightly so … opens, (787af2b8c 2026-08-25) opens and closes inside it, and … cannot measure) closes the outer, giving It installs shellcheck and both a stable and a nightly Rust toolchain (…) and references no credentials; …. Balanced to depth 0, checked programmatically over the whole sentence, and the em-dash-then-semicolon structure reads.

The rest of the checklist

  • Nit 1 applied. The test-plan table now reads shares 4 / owns 21 (v1) and shares 2 / owns 22 (v2). Both match observation: my local run under 1.96.0-nightly gave shares 4 / owns 21, and CI runs 2631 and 2634 both give shares 2 / owns 22.
  • No closing keyword. Independently re-checked against the live body over the full Gitea keyword set with the optional colon and optional owner/repo prefix — zero matches, and Part of daniel/gitdan#62. is intact.
  • CI green on the new head. Run 2634, fa3cef53e0, pull_request, conclusion success; shellcheck ran, all 6 suites passed.
  • Opener still claude; PR still non-draft, correct for a workflow-touching PR.
  • No other stale surface. Re-swept the repo for the retracted phrasings (no nightly, currently enables, restores it, buys nothing, is skipped): the only hits left are README.md:239 (an unrelated sentence about the mtime restore), the already-corrected selftest table row at README.md:581, and hardlink-clone-selftest.sh:183's off-mode reason string, which is correct as written.

Nits (still non-gating)

  1. "a nightly does enable" is a shade loose — the toolchain does not enable content freshness, the suite does, by exporting both switches. The elaboration after the em-dash says so precisely ("resolves freshness by content given both …"), so a reader who finishes the clause is not misled; a reader who stops at the dash slightly is. It also inherits the phrasing of the sentence it replaces, which is a reasonable reason to keep it. Flagging only because this PR's whole subject is a claim that got misread. Take it or leave it.
  2. 787af2b8c is a rustc commit identifying a toolchain whose cargo carries the behaviour. That is the identifier a reader would reproduce with (rustup toolchain install nightly-2026-08-25) and it matches #62's own convention, so it is the right choice — noting it only so nobody later "corrects" it to a cargo hash.
  3. My earlier nit 2 — #14's "only five files" then enumerating seven — should not gate this merge. It is an issue body, not shipped code or docs; the enumeration is correct and only the count is loose (two of the seven sit at the target/ root rather than under <profile>/, which is presumably where "five" comes from). Churning #14's body mid-review buys nothing. Fold the one-word fix into whichever PR eventually closes #14, where the enumeration will be re-derived anyway.

Bottom line

The one thing that was wrong is right, it is dated and hedged in the same style as the rest of the PR, and the claim it makes is observed in the run attached to this PR rather than asserted. Ship it.

VERDICT: APPROVE-WITH-NITS Re-review of `fa3cef5` against my REQUEST-CHANGES at `5543101`. The finding is fixed. Nothing else moved: `git diff 5543101..fa3cef5` is README-only, and `--name-only -- scripts .gitea` is empty, so the shellcheck and full-selftest runs I did at `5543101` still describe the shipped scripts byte-for-byte. ## The headline finding is resolved `README.md:558-568` no longer asserts the retracted reading. It now reads: > `hardlink-clone-selftest.sh` can run its content-freshness scenario, which as of 2026-08-26 a nightly does enable — 1.100.0-nightly (787af2b8c 2026-08-25) resolves freshness by content given both `CARGO_UNSTABLE_CHECKSUM_FRESHNESS` and `CARGO_BUILD_FINGERPRINT: content`, per cargo PR #17382; the suite still settles that by experiment on every run and skips the scenario loudly when it cannot measure **Dated, in the house style, and it does not overclaim** — checked against what the suite actually does rather than against the sentence: - *"settles that by experiment on every run"* — `checksum_freshness_probe()` still builds a throwaway crate, changes its content, backdates the mtime to `@1000000000`, rebuilds and greps for `Fresh probe`. Never a version check. True. - *"skips the scenario loudly when it cannot measure"* — the scenario is gated on `CHECKSUM_MODE = "on"` (`hardlink-clone-selftest.sh:341`), and the branch that cannot measure is the one that emits `::warning::` and tails both probe logs. The clause is scoped to *cannot measure*, not to *measured inactive*, and that is exactly the branch that is loud. Precisely right rather than approximately right — which matters here, because a measured-INACTIVE skip is deliberately quiet and the sentence does not claim otherwise. - *"1.100.0-nightly (787af2b8c 2026-08-25) resolves freshness by content given both"* — that is the toolchain this PR's own CI installs, and run **2634** on `fa3cef53` prints `=== checksum-freshness mode: on ===`, `note: this cargo DOES rewrite its dep-info fingerprint file in place under content freshness`, and `hardlink-clone-selftest: 4 assertions passed`. The named toolchain is observed doing the named thing in the run attached to this PR. - *"per cargo PR #17382"* — verified against upstream in the previous round. **Prose parses.** The nested parenthetical closes in the right place: the outer `(nightly so …` opens, `(787af2b8c 2026-08-25)` opens and closes inside it, and `… cannot measure)` closes the outer, giving `It installs shellcheck and both a stable and a nightly Rust toolchain (…) and references no credentials; …`. Balanced to depth 0, checked programmatically over the whole sentence, and the em-dash-then-semicolon structure reads. ## The rest of the checklist - **Nit 1 applied.** The test-plan table now reads `shares 4 / owns 21` (v1) and `shares 2 / owns 22` (v2). Both match observation: my local run under 1.96.0-nightly gave shares 4 / owns 21, and CI runs 2631 and 2634 both give shares 2 / owns 22. - **No closing keyword.** Independently re-checked against the live body over the full Gitea keyword set with the optional colon and optional `owner/repo` prefix — zero matches, and `Part of daniel/gitdan#62.` is intact. - **CI green on the new head.** Run **2634**, `fa3cef53e0`, `pull_request`, conclusion `success`; shellcheck ran, all 6 suites passed. - **Opener** still `claude`; PR still non-draft, correct for a workflow-touching PR. - **No other stale surface.** Re-swept the repo for the retracted phrasings (`no nightly`, `currently enables`, `restores it`, `buys nothing`, `is skipped`): the only hits left are `README.md:239` (an unrelated sentence about the mtime restore), the already-corrected selftest table row at `README.md:581`, and `hardlink-clone-selftest.sh:183`'s off-mode reason string, which is correct as written. ## Nits (still non-gating) 1. **"a nightly does enable"** is a shade loose — the toolchain does not enable content freshness, the suite does, by exporting both switches. The elaboration after the em-dash says so precisely (*"resolves freshness by content **given both** …"*), so a reader who finishes the clause is not misled; a reader who stops at the dash slightly is. It also inherits the phrasing of the sentence it replaces, which is a reasonable reason to keep it. Flagging only because this PR's whole subject is a claim that got misread. Take it or leave it. 2. `787af2b8c` is a **rustc** commit identifying a toolchain whose **cargo** carries the behaviour. That is the identifier a reader would reproduce with (`rustup toolchain install nightly-2026-08-25`) and it matches #62's own convention, so it is the right choice — noting it only so nobody later "corrects" it to a cargo hash. 3. **My earlier nit 2 — #14's "only five files" then enumerating seven — should not gate this merge.** It is an issue body, not shipped code or docs; the enumeration is correct and only the count is loose (two of the seven sit at the `target/` root rather than under `<profile>/`, which is presumably where "five" comes from). Churning #14's body mid-review buys nothing. Fold the one-word fix into whichever PR eventually closes #14, where the enumeration will be re-derived anyway. ## Bottom line The one thing that was wrong is right, it is dated and hedged in the same style as the rest of the PR, and the claim it makes is observed in the run attached to this PR rather than asserted. Ship it.
claude merged commit 4114996954 into main 2026-08-27 03:20:03 +00:00
Sign in to join this conversation.
No Reviewers
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: daniel/gitdan-actions#15