A bare docker build . silently restores the cached-gate false green #39

Open
opened 2026-08-09 09:47:35 +02:00 by clawbot · 0 comments
Collaborator

#32 stopped script/cibuild and script/docker from serving cached gate layers, by interpolating a fresh CHECK_EPOCH into each gate RUN. That works — the reviewer proved it by holding the epoch constant and watching the 17-layer false green come straight back.

But CHECK_EPOCH has no default and nothing asserts it is non-empty (Dockerfile:24 and Dockerfile:64). So anyone who runs docker build . directly, rather than through the sanctioned entrypoints, gets the old behaviour with no warning at all. Measured by the reviewer: two bare docker build . runs in a row produce 17 CACHED steps with all three gates cached and exit 0.

Not a regression — both sanctioned paths pass the argument, and this is exactly how things behaved before #32. But the whole point of #32 is that a gate must not be able to report a green it did not earn, and right now the failure mode is one habit away. docker build . is what people type.

Definition of done

  1. Each gate stage asserts the epoch is present, e.g. RUN test -n "${CHECK_EPOCH}" || { echo "CHECK_EPOCH is unset; build via script/cibuild or script/docker" >&2; exit 1; }, so a bare docker build . fails loudly instead of quietly serving cache. Both stages, since ARG is per-stage.
  2. Verified by running docker build . directly and showing it fails with that message, and that script/cibuild and script/docker still succeed.
  3. Consider whether the epoch should be tightened to "$(date +%s)-$$". Second resolution alone means two builds starting in the same second share a value. The reviewer judged that non-blocking and gave the correct reason, which is worth recording because the original justification was wrong: it is safe not because builds take 50-80 seconds, but because a collision needs the same second and a byte-identical tree, in which case the reused result really was computed for that exact tree. Note date +%s%N is GNU-only and degrades silently to one-second resolution on macOS, which is a supported host class here — prefer -$$ if tightening.
  4. TODO.md:59 says twelve CACHED steps in steady state; the measured figure is thirteen (the extra is the lint stage's WORKDIR /src). Correct it.
  5. make check green, and make docker green with the gates demonstrably executing.
#32 stopped `script/cibuild` and `script/docker` from serving cached gate layers, by interpolating a fresh `CHECK_EPOCH` into each gate `RUN`. That works — the reviewer proved it by holding the epoch constant and watching the 17-layer false green come straight back. But `CHECK_EPOCH` has no default and nothing asserts it is non-empty (`Dockerfile:24` and `Dockerfile:64`). So anyone who runs `docker build .` directly, rather than through the sanctioned entrypoints, gets the old behaviour with no warning at all. Measured by the reviewer: two bare `docker build .` runs in a row produce 17 `CACHED` steps with all three gates cached and exit 0. Not a regression — both sanctioned paths pass the argument, and this is exactly how things behaved before #32. But the whole point of #32 is that a gate must not be able to report a green it did not earn, and right now the failure mode is one habit away. `docker build .` is what people type. ## Definition of done 1. Each gate stage asserts the epoch is present, e.g. `RUN test -n "${CHECK_EPOCH}" || { echo "CHECK_EPOCH is unset; build via script/cibuild or script/docker" >&2; exit 1; }`, so a bare `docker build .` fails loudly instead of quietly serving cache. Both stages, since `ARG` is per-stage. 2. Verified by running `docker build .` directly and showing it fails with that message, and that `script/cibuild` and `script/docker` still succeed. 3. Consider whether the epoch should be tightened to `"$(date +%s)-$$"`. Second resolution alone means two builds starting in the same second share a value. The reviewer judged that non-blocking and gave the correct reason, which is worth recording because the original justification was wrong: it is safe not because builds take 50-80 seconds, but because a collision needs the same second **and** a byte-identical tree, in which case the reused result really was computed for that exact tree. Note `date +%s%N` is GNU-only and degrades silently to one-second resolution on macOS, which is a supported host class here — prefer `-$$` if tightening. 4. `TODO.md:59` says twelve `CACHED` steps in steady state; the measured figure is thirteen (the extra is the lint stage's `WORKDIR /src`). Correct it. 5. `make check` green, and `make docker` green with the gates demonstrably executing.
clawbot added this to the 1.0.0 milestone 2026-08-09 09:47:35 +02:00
Sign in to join this conversation.