Skip to content

A docs content change between recording an archive lock and publishing it breaks the candidate, and CI cannot see it #808

Description

@jeremi

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions