docs(plans): record the two filed handbook amendment MRs

What this does

Five plan corrections, all fallout from the reviews of the two handbook amendment MRs this plan books.

  1. Records both amendment MRs, now that both are filed, and corrects how the plan describes the ADR-009 one.
  2. Rewrites ### Schema facts, which no longer described the repository.
  3. Adds a cross-plan note to Step 2 about its position check, and what to copy from the npm migration that now precedes it.
  4. Names the two docs(specs) MRs that close the same claims on the spec side.
  5. Names the open referrers/:digest error-code decision on Step 13, which is the step that writes the route.

No code ships here, and no step's scope, acceptance, or sizing changes.

Amendment MR
ADR-007, container virtual tables gitlab-com/content-sites/handbook!20928 (merged)
ADR-009, virtual container read routes gitlab-com/content-sites/handbook!20929 (merged)
ADR-009, in-use refusal on repository delete gitlab-com/content-sites/handbook!20941 (merged)
S32 spec, deferrable precedent !2126 (closed)
S32 spec, referrers client split !2127 (closed) (Draft)

Decisions a reviewer should check first

### Schema facts was wrong before this branch, not only stale. It claimed grep -ic virtual over structure.sql returns 0 and that no *_virtual_* table exists. S31's first table was already merged at this branch's own base, so the count was never 0 here. All three npm virtual tables are on main now, and the count is 1818 at ddfb0f545. The section is rewritten with the commit named beside the measurement, so the next reader can tell a stale figure from a wrong command.

The same paragraph asked which of S31 and S32 would land first and set the deferrable-constraint precedent. S31 did. structure.sql carries 65 DEFERRABLE lines from e739e728b — the parent constraint plus its 64 partition clones — so Step 2 copies 20260828102654_create_npm_virtual_repository_upstreams.sql rather than deriving the shape.

The spec still carries the pre-S31 reading, and the plan says so rather than correcting it silently. ### Schema facts names the divergence and points at !2126 (closed). That follows the AGENTS.md rule for a plan whose research contradicts its spec: a reader cross-referencing the two documents can otherwise not tell a deliberate correction from a transcription error.

Step 2 gains one precedent to copy and one not to. The npm migration's disallowed-unique-constraint ignore transfers, because neither half of the rule's own remedy runs on a hash-partitioned parent — PostgreSQL refuses CREATE UNIQUE INDEX CONCURRENTLY there, and refuses ALTER TABLE ... ADD CONSTRAINT ... UNIQUE USING INDEX too. Its prefer-bigint-over-int ignore does not transfer: that one justifies itself with "positions are unique and ordered but not contiguous", which holds for the npm column and is false for this one. Carried over wholesale, the directive argues the opposite of what this table needs, and nothing fails when it does.

The ADR-009 entry counted three widening items. There are four. The amendment also had to scope its account of the referrers tag-schema fallback to the clients that take it: crane reads no error code and falls back, while oras and Notation read a 404 NAME_UNKNOWN as a missing repository and return an error. Measured against go-containerregistry v0.20.6 and oras-go v2.5.0 and v2.6.0. The spec states the fallback without that qualifier in four places, and !2127 (closed) corrects them.

Step 2's position check diverges from the npm sibling's, and one S17 write surface serves both. This table takes CHECK (position >= 1), 1-based and contiguous, per the spec. The npm virtual table takes position >= 0 and permits gaps. S17 Phase 6 writes both tables from one surface — Step 39's junction store and Step 40's handler — which its Step 41 extends with the npm arm and its Step 42 with the container arm.

The two checks are asymmetric rather than incompatible. S17 fixes one rule over that surface: position is 1-based and contiguous, and a position below 1 returns 422. Values written under that rule satisfy both checks, so no S17 write path meets two constraints. Only a 0-based helper written against the npm table alone fails against the container one.

ADR-007 fixes no base or contiguity rule for either table. !20928 therefore states S32's constraint on the container table, leaves the npm table untouched, and leaves the divergence open for the S17 or S13 DRI — arbitrating would change the npm table, which is outside what the spec asked for. Nothing in Step 2 changes.

Step 13 and the open error code

Step 13's Scope writes 404 NAME_UNKNOWN on referrers/:digest, and whether that is the right code is open in #1019 (closed). NAME_UNKNOWN is what stops oras and Notation taking the referrers tag-schema fallback, and that fallback is the spec's stated reason for preferring a 404 over an empty 200.

The entry now carries an Open decision this step settles first line. The Scope keeps NAME_UNKNOWN as its assumption and says what changes if #1019 (closed) lands on another code, so the step reads correctly either way. tags/list is unaffected, because neither client library has a tag-listing fallback at any status.

### ADR findings also had the crane half of the split compressed to the point of being wrong. It reads no error code, but it does not fall back on everything: CheckError admits only 200, 404, 400, and 406, so any other status returns an error and never reaches the fallback arm. The entry now carries the predicate the code implements, and attributes Notation's behavior to oras-go rather than listing it beside two libraries it is not one of.

Size

101 insertions, 15 deletions, one file, documentation only. Well inside the reviewable-source ceiling; no justification owed.

Overlap

!2069 (merged) also writes this plan file. Its hunks sit at old lines 787-792 and 1340-1352; this branch's at 910, 1020, and 1612, so the two merge in either order. Whichever lands second should still be rebased before merge.

Checks

pre-commit clean, lychee included. The hook needs env LYCHEE_VERSION=0.24.2 locally, because git commit exports GIT_DIR and the hook's own tag detection then resolves against this repository instead of its cached lychee clone. That is an environment gap rather than anything in this diff, and the variable short-circuits the detection while still running the link check.

Edited by Radamanthus Batnag

Merge request reports

Loading
Loading