Two builds of one pin make two libraries, and every snapshot pairs with only one of them #162

Closed
opened 2026-08-21 08:22:28 +02:00 by Claude · 1 comment
Collaborator

make-sample-media.sh stamps every file it generates with the moment the image
was built, and nothing puts them back. So two builds of the same pinned commit
produce libraries that differ in nothing but their timestamps — and a snapshot
cut against one of them no longer pairs with the other.

That is not hypothetical. Two images are on this machine right now, both of which
cargo xtask reference build --check is happy with:

image media mtime
jellyfin-rs/upstream:pinned (0e5cf3e7) 2026-08-16
ephyra/upstream:pinned (298b062a) 2026-08-20

The committed snapshots were cut against the first. Running
cargo xtask reference up --fresh re-exports the library from the second, and
ephyra-app::scan_ingest::a_row_carries_the_time_its_own_file_was_written then
reports 12 of 20 rows disagreeing about their dates — upstream 2026-08-16 17:08:19, ours 2026-08-20 20:27:33. Nothing in the failure says the media was
re-exported; it reads as the scan having got the dates wrong.

Why it bites

up calls export_media, which docker cps the baked library onto the host
over whatever was there. Any up --fresh, bootstrap, or down --wipe followed
by up does it. So a rebuild of the image silently invalidates every snapshot on
disk, and the only symptom is a date comparison in a crate that has nothing to do
with either.

crates/ephyra-app/corpus/README.md already warns about the neighbouring trap —
cp -r into a worktree stamping every file with the moment it ran, twelve rows
then disagreeing about their dates and nothing saying why. This is the same
failure with the image as the cause.

What would fix it

Make the generated library a function of the pin rather than of the clock: have
make-sample-media.sh touch -d every file it writes to a fixed timestamp — the
pinned commit's own date is the obvious one, and it is already in pin.env
territory. Then two builds of one pin produce one library, a snapshot survives an
image rebuild, and build --check's promise that the image "carries the library
this tree describes" becomes true of the timestamps as well as of the bytes.

The cheap half, if the above is not wanted: export_media warns when the media
it is about to overwrite has different timestamps from the media it is exporting,
and reference/README.md says a rebuild invalidates the snapshots.

How to know it is done

Build the image twice from a clean cache and diff the two exports with
find … -printf '%T@ %p\n': the two agree. Then up --fresh against a committed
snapshot and a_row_carries_the_time_its_own_file_was_written passes.

`make-sample-media.sh` stamps every file it generates with the moment the image was built, and nothing puts them back. So two builds of the *same* pinned commit produce libraries that differ in nothing but their timestamps — and a snapshot cut against one of them no longer pairs with the other. That is not hypothetical. Two images are on this machine right now, both of which `cargo xtask reference build --check` is happy with: | image | media mtime | |---|---| | `jellyfin-rs/upstream:pinned` (`0e5cf3e7`) | 2026-08-16 | | `ephyra/upstream:pinned` (`298b062a`) | 2026-08-20 | The committed snapshots were cut against the first. Running `cargo xtask reference up --fresh` re-exports the library from the second, and `ephyra-app::scan_ingest::a_row_carries_the_time_its_own_file_was_written` then reports **12 of 20 rows disagreeing about their dates** — upstream `2026-08-16 17:08:19`, ours `2026-08-20 20:27:33`. Nothing in the failure says the media was re-exported; it reads as the scan having got the dates wrong. ## Why it bites `up` calls `export_media`, which `docker cp`s the baked library onto the host over whatever was there. Any `up --fresh`, `bootstrap`, or `down --wipe` followed by `up` does it. So a rebuild of the image silently invalidates every snapshot on disk, and the only symptom is a date comparison in a crate that has nothing to do with either. `crates/ephyra-app/corpus/README.md` already warns about the neighbouring trap — `cp -r` into a worktree stamping every file with the moment it ran, twelve rows then disagreeing about their dates and nothing saying why. This is the same failure with the image as the cause. ## What would fix it Make the generated library a function of the pin rather than of the clock: have `make-sample-media.sh` `touch -d` every file it writes to a fixed timestamp — the pinned commit's own date is the obvious one, and it is already in `pin.env` territory. Then two builds of one pin produce one library, a snapshot survives an image rebuild, and `build --check`'s promise that the image "carries the library this tree describes" becomes true of the timestamps as well as of the bytes. The cheap half, if the above is not wanted: `export_media` warns when the media it is about to overwrite has different timestamps from the media it is exporting, and `reference/README.md` says a rebuild invalidates the snapshots. ## How to know it is done Build the image twice from a clean cache and diff the two exports with `find … -printf '%T@ %p\n'`: the two agree. Then `up --fresh` against a committed snapshot and `a_row_carries_the_time_its_own_file_was_written` passes.
Author
Collaborator

Fixed in feea61c.

The issue named the timestamps; the bytes were the other half of it. Comparing the two images on this machine showed three files differing byte for byte at the same length — Night Signal (2022).mkv, Test Pattern S01E03.mkv, Test Pattern S02E02.mkv — which is Matroska's random SegmentUID, its DateUTC and the CRC-32 elements over them. So the library was a function of the clock in two ways at once.

What landed in make-sample-media.sh:

  • every muxer is run bit-exactly, in the output position where the flag reaches the output format context rather than the inputs (it does nothing where I first put it, which is worth knowing);
  • the library is dated from a fixed epoch in one pass at the end, in sorted path order rather than in generation order, so a run that found half of it already there dates it the same as a run that wrote all of it. One second apart rather than one moment, because a listing ordered by date has to have an order to be in.

Measured, which is what the issue asked for: generating the library twice with the patched script gives 21 files, BYTES IDENTICAL and MTIMES IDENTICAL. Before the change the same comparison reported the three mkvs differing.

Beside it, so the fix cannot be undone by accident: up leaves a library already on the host where it is, and --fresh discards the server's state without it — the media is a fixture every snapshot was scanned against, not state the server wrote. Verified: cargo xtask reference up --fresh now logs library already at … and a_row_carries_the_time_its_own_file_was_written passes, where before it reported 12 of 20 rows disagreeing.

What remains, and where it belongs. The library on this machine was laid down by a build from before any of this, so it does not match what the image would now write, and the committed snapshots are cut against it. Nothing breaks — the export guard is what keeps the pair together — but the next image rebuild has to re-lay the media and rebuild the snapshots with it. reference/README.md says so where it says how to grow the fixture, and that rebuild is the one #148 and #117 are already waiting on.

Fixed in `feea61c`. The issue named the timestamps; the bytes were the other half of it. Comparing the two images on this machine showed three files differing byte for byte at the same length — `Night Signal (2022).mkv`, `Test Pattern S01E03.mkv`, `Test Pattern S02E02.mkv` — which is Matroska's random `SegmentUID`, its `DateUTC` and the CRC-32 elements over them. So the library was a function of the clock in two ways at once. **What landed in `make-sample-media.sh`:** - every muxer is run bit-exactly, in the output position where the flag reaches the output format context rather than the inputs (it does nothing where I first put it, which is worth knowing); - the library is dated from a fixed epoch in one pass at the end, in sorted path order rather than in generation order, so a run that found half of it already there dates it the same as a run that wrote all of it. One second apart rather than one moment, because a listing ordered by date has to have an order to be in. **Measured**, which is what the issue asked for: generating the library twice with the patched script gives 21 files, `BYTES IDENTICAL` and `MTIMES IDENTICAL`. Before the change the same comparison reported the three mkvs differing. **Beside it**, so the fix cannot be undone by accident: `up` leaves a library already on the host where it is, and `--fresh` discards the server's state without it — the media is a fixture every snapshot was scanned against, not state the server wrote. Verified: `cargo xtask reference up --fresh` now logs `library already at …` and `a_row_carries_the_time_its_own_file_was_written` passes, where before it reported 12 of 20 rows disagreeing. **What remains, and where it belongs.** The library on this machine was laid down by a build from before any of this, so it does not match what the image would now write, and the committed snapshots are cut against it. Nothing breaks — the export guard is what keeps the pair together — but the next image rebuild has to re-lay the media and rebuild the snapshots with it. `reference/README.md` says so where it says how to grow the fixture, and that rebuild is the one #148 and #117 are already waiting on.
Sign in to join this conversation.
No description provided.