S17 Phase 6: virtual and remote repository CRUD
Summary
CRUD for remote and virtual repositories on the management API, per ADR-009: the non-hosted polymorphic settings (remote upstream URL, auth, cache TTLs; virtual upstream associations), upstream management routes, and connection test. Phase 1 shipped the shared repository CRUD with hosted-only rows; this phase lifts the non-hosted rejection.
Gated on
- S13 Virtual and remote foundation and the format slices: S14-S16 (remote), S30-S32 (virtual).
This phase patches the S17 spec and follows the repo gate: spec MR, plan MR, implementation. Part of &22341.
Workstream status (2026-08-25)
The graph is a ledger of work in progress and work to be done: a step's node leaves the graph when its merge request merges, and the Merge request status table below keeps the full step-to-MR record, merged rows included. Reading the colors, green nodes have an open merge request and link to it, amber nodes are being implemented but their merge requests have not opened yet, grey nodes are planned, red nodes are blocking follow-ups, and blue nodes are slices outside this issue's ownership whose schema migrations gate the virtual steps. Edges are the plan's dependency graph among the remaining nodes, plus the four slice edges, so every edge shown is an unsatisfied dependency. Blocking follow-ups join the map when they are filed and carry no plan edges, so the step each one holds is named in the external gates below. A follow-up leaves the map when the step it held merges, and joins the follow-up list until it closes.
Dependency graph
graph TD
S36["Step 36: Virtual create and delete: Maven"]
S37["Step 37: Virtual create and delete: npm"]
S38["Step 38: Virtual create and delete: container"]
S39["Step 39: Maven virtual upstream store and the delete-side guard"]
S40["Step 40: Upstream association routes: Maven"]
S41["Step 41: Upstream associations: npm"]
S42["Step 42: Upstream associations: container"]
EXTS30["S30 Maven virtual slice (#289)"]
EXTS31["S31 npm virtual slice (#290)"]
EXTS32["S32 container virtual slice (#291)"]
S36 --> S40
S37 --> S41
S38 --> S42
S39 --> S40
S40 --> S41
S40 --> S42
EXTS30 --> S36
EXTS30 --> S39
EXTS31 --> S37
EXTS32 --> S38
click EXTS30 "https://gitlab.com/gitlab-org/ops/artifact-registry/-/issues/289"
click EXTS31 "https://gitlab.com/gitlab-org/ops/artifact-registry/-/issues/290"
click EXTS32 "https://gitlab.com/gitlab-org/ops/artifact-registry/-/issues/291"
classDef stOpen fill:#c8e6c9,stroke:#2e7d32,color:#1b5e20
classDef stInBuild fill:#ffecb3,stroke:#ff8f00,color:#ff6f00
classDef stPlanned fill:#f5f5f5,stroke:#9e9e9e,color:#424242
classDef stBlocking fill:#ffcdd2,stroke:#c62828,color:#b71c1c
classDef stExternal fill:#d6e4ff,stroke:#3f51b5,color:#1a237e
class S36,S37,S38,S39,S40,S41,S42 stPlanned
class EXTS30,EXTS31,EXTS32 stExternalBeyond the four slice edges the diagram carries, the plan names these external gates per step:
- Step 33 has merged, feat(managementapi): container remote bulk pass... (!1805 - merged) • Hayley Swimelar • 19.4. It landed ahead of the deploy-ordering decision under the merge-throughput ruling, so both of its blocking follow-ups outlived it. Neither holds an unmerged step now, so both have left the map for the follow-up list. While Deploy-ordering gate for the remote bulk passes (#755) • Hayley Swimelar • 19.4 is open the deploy-ordering question is unresolved on the record, and the issue closes on that answer rather than on a merge. All three remote bulk passes have now merged and none of them satisfies the
delete_allcontract, which defines completion as emptiness at acceptance time, so delete_all contract defines completion by accep... (#738) • Hayley Swimelar • 19.4 stays open as well. The fail-closed worker fix those passes needed is on main as fix(managementapi): fail closed on bulk-worker ... (!1736 - merged) • Hayley Swimelar • 19.4. - Step 34 has merged, feat(managementapi): serve the url change on th... (!1841 - merged) • Hayley Swimelar • 19.4. It validates the update's
urlat exact parity with create through the shared decode and parse path and adds no fetch-time covers, so the spec self-conflict AC #83's create-time coverage is fetch-only: up... (#715) • Hayley Swimelar • 19.4 records holds no step and sits on the non-blocking list below. What its merge opened was a deploy-order window on main: while the container remote bulk pass was unmerged, a docker or oci URL change reached a worker arm that was not deployed, failed closed, and parked on River retries. That window closed when the bulk pass merged, feat(managementapi): container remote bulk pass... (!1805 - merged) • Hayley Swimelar • 19.4. - Step 35 was stacked rather than gated. It merged into step 20's branch, feat(managementapi): serve the container remote... (!1900 - merged) • Hayley Swimelar • 19.4, and reached main when that branch did, feat(managementapi): serve the container remote... (!1748 - merged) • Hayley Swimelar • 19.4. The stack is how its plan dependency on step 20 was satisfied.
- Steps 36-39 wait on their format's virtual schema migration, which ships in the slice the diagram now links. The three slices sit at different stages. S31's spec is merged as
docs/specs/S31-npm-virtual.md, S32's is in review as docs(specs): add S32 container virtual reposito... (!972 - merged) • Radamanthus Batnag • 19.4, and S30 has no spec at all, because S30: Maven virtual spec (#284) • Moaz Khalifa • 19.4 is still open. No migration from any of the three is on main. - Steps 40, 41, and 42 inherit those gates through the virtual create steps they follow.
- Builders for Steps 36-39 all exited without an artifact and returned the same two options, ship the slice or amend the plan to drop the schema dependency. Those four steps hold until that ruling lands.
Non-blocking follow-ups filed against this phase carry no graph membership: Remote repository destructive delete (202 tombs... (#697) • Unassigned, Unify the container remote downloaded-at setter... (#698) • Unassigned, Container remote token cache is not evicted on ... (#706) • Unassigned, Visibility changes on hosted and virtual reposi... (#707) • Unassigned, AC #83's create-time coverage is fetch-only: up... (#715) • Hayley Swimelar • 19.4, Container remote read-store comments still desc... (#733) • Unassigned, Repository path segment reaches FindByName unsc... (#734 - closed) • Unassigned, Sweep: remote read stores carry identifiers in ... (#735) • Unassigned, delete_all contract defines completion by accep... (#738) • Hayley Swimelar • 19.4, DeleteIfUnreferenced takes no FOR UPDATE on blo... (#742) • Unassigned, ADR-012 deleted-event row reads as covering evi... (#743) • Unassigned, logAndWriteInternalError swallows context cance... (#744) • Unassigned, wire_oci_remote and wire_npm_remote build store... (#754 - closed) • Dzmitry (Dima) Meshcharakou, and Deploy-ordering gate for the remote bulk passes (#755) • Hayley Swimelar • 19.4.
Steps 1 through 35 are on main at f60ca64e3, and no step merge request is open. The seven that remain, steps 36 through 42, all hold on the virtual schema gate, directly or through the create step each one follows.