docs(publish): address review nits on the contract block

Four corrections from the review of #8, all in the files this PR already
touches:

- A local signal on the line that creates .publish-new-. The other four shapes
  each got a note at their producing line, which is the whole premise of #7 —
  someone renaming TMP_DST reads its own comment block and would never see the
  contract note 45 lines up at OLD.

- '.publish-old- is milder' understated it. Milder is true; bounded is not. A
  key whose branch is merged, deleted or renamed is never published again, so
  its rotated generation stays until something outside this repo takes it —
  which is the case gitdan#30 itself makes, two lines away.

- The drift check now says the prefix constants ci-cache-reclaim.sh DECLARES,
  not the ones it enumerates. Those are different numbers: collect_entries()
  globs .stage- and .evicting- only, because .reading- is read and never
  swept. Only the declared reading makes the five-against-five count work, and
  a countability check that needs a coin flip to count is not one.

- publish-snapshot.sh's own header had two stale names ten lines above the
  stale pointer this PR fixes: step 1 staged at .stage-<tag> (that is
  hardlink_clone_into's inner path; the staged snapshot is .publish-new-<tag>)
  and step 2 named .publish-old-<tag> without the key. Pre-existing and
  outside both ACs, but #6's thesis is that a plausible-looking wrong name is
  the worst kind, and these are in the file the PR is about.

Comments and docs only. With comments and blank lines stripped, all three
scripts hash identically to origin/main.
This commit is contained in:
2026-08-24 13:06:49 -05:00
parent 0118c28f01
commit e3869c5920
3 changed files with 25 additions and 13 deletions
+11 -5
View File
@@ -12,9 +12,11 @@
# The swap is two renames, not one, because POSIX rename() can only replace an
# EMPTY directory and a snapshot from a prior publish is not one:
#
# 1. stage the new snapshot at .stage-<tag> (copy time is off every
# consumer's hot path — nothing reads a staging path);
# 2. rename the current snapshot aside to .publish-old-<tag>, if present;
# 1. stage the new snapshot at .publish-new-<tag>, which hardlink_clone_into
# builds at its own .stage-<tag> and renames there (copy time is off
# every consumer's hot path — nothing reads either path);
# 2. rename the current snapshot aside to .publish-old-<key>-<tag>, if
# present;
# 3. rename the staged snapshot into place.
#
# Step 3 is a single atomic rename onto a path now guaranteed absent, so it
@@ -60,8 +62,10 @@ DST=$(snapshot_dir_for "$ROOT" "$OWN_KEY")
# per job per run, so a publisher killed between staging its snapshot and the
# swap below strands a full hardlink clone that no later run of this script
# will ever match. `.publish-old-` is milder — the sweep further down reclaims
# it on the next publish of the same key. Bringing both under the arbiter's
# enumeration is tracked as daniel/gitdan#30.
# it on the next publish of the same key, if there ever is one; a key whose
# branch is merged, deleted or renamed is never published again, and its
# rotated generation stays until something outside this repo takes it.
# Bringing both under the arbiter's enumeration is tracked as daniel/gitdan#30.
OLD="${ROOT}/.publish-old-${OWN_KEY}-${TAG}"
SNAP_NAME=$(basename "$DST")
GRACE="${CACHE_READ_GRACE_SECONDS}"
@@ -97,6 +101,8 @@ start=$(date +%s)
# the snapshot it just published — the same aliasing hazard the consume side
# closes, pointing the other way. The staging path is not the final name, so
# `hardlink_clone_into`'s rename lands on DST only after OLD is out of the way.
# `.publish-new-` is a contract name — see the note at OLD above before
# changing it.
TMP_DST="${ROOT}/.publish-new-${TAG}"
rm -rf "$TMP_DST"
hardlink_clone_into "$SRC" "$TMP_DST" "$TAG" || {