registrystack / registrystack/registry-stack
A docs content change between recording an archive lock and publishing it breaks the candidate, and CI cannot see it
- Dominant language
- Rust
- Stars
- 2
- Forks
- 0
- Avg merge
- 2h 55m
- Merged PRs (30d)
- 130
Description
## 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:
1. 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.
2. 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.
3. 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.
Contributor guide
Research direction
Start with .github/scripts/ci_changes.py and the docs archive checks, then read the archive-lock handling and relevant focused tests. Compare the three proposed approaches and inspect release/OPERATIONS.md for the current ordering assumptions. Done means the selected policy is documented and covered by a regression check for content changes during the unpublished-lock window.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, python
- Domain
- ci-cd, documentation, release
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100