What happened
The v0.24.0 release PR recorded the immutable archive digests for the new
docset in docs/site/src/data/archive-lock.yaml. #799 then merged, changing
docs/site/src/content/** before the candidate was dispatched. The candidate
failed at archive:snapshot --verify-lock, because the built docset no longer
matched the lock.
There is no supported repair once the entry exists:
assertArchiveLockImmutable rejects a change to an existing entry,
addArchiveLockEntry throws already exists, and isLockBackedDocset has no
carve-out for availability: candidate. The release had to revert #799
(d1a6c7b92), publish, and re-land it (#806).
Why CI did not catch it
The docs-archives job runs check:archives, which is the gate that would have
failed. But needs.changes.outputs.docs_archives is computed in
.github/scripts/ci_changes.py from an exact allow-list: ci.yml,
astro.config.mjs, package.json, package-lock.json, a fixed set of
docs/site/scripts/*.mjs, and src/data/{archive-lock,docsets,repo-docs}.yaml.
A pure prose change matches none of them, so the job skips. That is visible on
#806 right now: Immutable docs archives: skipping.
The comment above the filter explains the intent, and the intent is sound for
the steady state: "Publication workflows and this classifier do not alter
archived bytes; their focused tests cover those contracts without replaying
every historical docset."
The gap is a window, not a permanent hole
The assumption behind the filter, that content changes cannot move archived
bytes, holds once a docset is published, because assemble-archives.mjs
restores a published bundle rather than rebuilding it. Note that the pinned
source tag does not provide this: currentSourceGeneratedArtifacts binds
only docs/site/src/content/docs/reference/cli and
docs/site/src/data/generated/cli-reference.json to the tag. Prose is built from
the checked-out tree.
So the assumption is false for exactly one window: between the commit that
records a lock entry and the publication of that docset's bundle. In that
window any docs/site/src/content/** change silently breaks the candidate, and
CI stays green.
Possible fixes
Not proposing a specific one, since this is a design call:
- Make
docs_archives true when docs/site/src/content/** changes and the
lock contains an entry whose bundle is not yet published. Narrow, but needs
the classifier to know publication state.
- Add a cheap standalone check, not the full
check:archives replay, that fails
a PR touching docs/site/src/content/** while an unpublished lock entry
exists.
- Leave the classifier alone and encode the ordering rule in
release/OPERATIONS.md: merge the release PR last, dispatch the candidate
immediately, and freeze docs/site/src/content/** until the release
publishes.
Option 3 alone is what the process relies on today, and it is unwritten.
What happened
The v0.24.0 release PR recorded the immutable archive digests for the new
docset in
docs/site/src/data/archive-lock.yaml. #799 then merged, changingdocs/site/src/content/**before the candidate was dispatched. The candidatefailed at
archive:snapshot --verify-lock, because the built docset no longermatched the lock.
There is no supported repair once the entry exists:
assertArchiveLockImmutablerejects a change to an existing entry,addArchiveLockEntrythrowsalready exists, andisLockBackedDocsethas nocarve-out for
availability: candidate. The release had to revert #799(
d1a6c7b92), publish, and re-land it (#806).Why CI did not catch it
The
docs-archivesjob runscheck:archives, which is the gate that would havefailed. But
needs.changes.outputs.docs_archivesis computed in.github/scripts/ci_changes.pyfrom an exact allow-list:ci.yml,astro.config.mjs,package.json,package-lock.json, a fixed set ofdocs/site/scripts/*.mjs, andsrc/data/{archive-lock,docsets,repo-docs}.yaml.A pure prose change matches none of them, so the job skips. That is visible on
#806 right now:
Immutable docs archives: skipping.The comment above the filter explains the intent, and the intent is sound for
the steady state: "Publication workflows and this classifier do not alter
archived bytes; their focused tests cover those contracts without replaying
every historical docset."
The gap is a window, not a permanent hole
The assumption behind the filter, that content changes cannot move archived
bytes, holds once a docset is published, because
assemble-archives.mjsrestores a published bundle rather than rebuilding it. Note that the pinned
source tag does not provide this:
currentSourceGeneratedArtifactsbindsonly
docs/site/src/content/docs/reference/clianddocs/site/src/data/generated/cli-reference.jsonto the tag. Prose is built fromthe checked-out tree.
So the assumption is false for exactly one window: between the commit that
records a lock entry and the publication of that docset's bundle. In that
window any
docs/site/src/content/**change silently breaks the candidate, andCI stays green.
Possible fixes
Not proposing a specific one, since this is a design call:
docs_archivestrue whendocs/site/src/content/**changes and thelock contains an entry whose bundle is not yet published. Narrow, but needs
the classifier to know publication state.
check:archivesreplay, that failsa PR touching
docs/site/src/content/**while an unpublished lock entryexists.
release/OPERATIONS.md: merge the release PR last, dispatch the candidateimmediately, and freeze
docs/site/src/content/**until the releasepublishes.
Option 3 alone is what the process relies on today, and it is unwritten.