docs(specs): specify remote and virtual kinds in monolith/S05 and S07
Both specs deferred remote and virtual repositories to a later phase of themselves. S17 Phase 6 has since contracted most of that surface, so this amendment turns both deferrals into implementable contracts.
Both remain Approved with empty Open questions.
monolith/S07
Two delivery phases become three: Phase 1 hosted, Phase 2 remote, Phase 3 virtual, because the AR implementation behind the two non-hosted kinds sits at different readiness. Phase 1 is unaffected and raised no questions.
Specified: the three per-format remote settings inputs and their flat read type, the health-status enum, the upstream association mutations plus a set-order mutation, the test-connection mutation across create, edit, and detail, and the Ruby client extension.
monolith/S04 declares ArtifactRegistryRepository and the organization's
repositories connection, so S07 extends the type rather than introducing it,
replacing S04's non-null JSON settings with a typed, nullable, remote-only field.
Every field on that type is experiment, so the replacement is permitted before GA.
Attribution is stated as declaration rather than as deployment throughout, because a
declaration is a fact between two specs and does not go stale.
The client extension lives here rather than in monolith/S02, following the precedent
monolith/S05 set when it added client methods inside the consuming slice.
monolith/S05
Adds remote and virtual detail. A virtual repository's main content is an ordered upstream list, not an artifact table, so the two kinds are not variants of one view and ship as two pieces of delivery, remote first.
The slice also takes on cache eviction as its own write surface, which ends the
read-only property it claimed. The boundary that replaces it: repository lifecycle
writes stay monolith/S07's and are hosted here; artifact cache eviction is this
slice's own. AR's eviction routes answer 202 with no body and no per-entry outcome,
so the UI reports accepted, never completed, and never a count.
Gates
Both specs gate remote and virtual separately, and state each gate as a check on
api/openapi/v1.yaml rather than as an MR state, so a gate row cannot go stale. The
gates were re-read against origin/main before merge, and that check is what caught
three of them being out of date: the PATCH settings gate, the remote-create gate, and
the URL gate have all cleared. The whole remote phase is buildable now, except the
create-form test action.
Three gates remain. Virtual create is blocked because
CreateRepositoryRequest.kind says virtual returns 422. A create-time probe of a
supplied URL has no endpoint behind it at all, so it carries a Dependencies row and a
follow-up AR ask rather than an assumption. And AR's list route declares no search
parameter, which the Phase 3 upstream picker works around rather than waiting on
(#877); it
blocks nothing, so it earns no gate row.
One gate inverts the others and says so. They block work until AR declares
something; destructive breaks the merged Phase 1 delete the moment AR enforces it.
Its row names the monolith change as this spec's and says it must merge first, and a
criterion pins that the client sends the refusing value and never the cascading one.
Corrections to already-merged text
Called out because they change merged scope, not just the new phases.
- The hosted Source column is removed from the artifact table and from the Phase 2 deferral list. It has left the design.
- Row navigation is three-way, not two. Hosted rows become navigable when
monolith/S14lands, a sequencing gap; remote and virtual rows are non-navigable permanently, because AR serves artifact routes for hosted repositories only and answers404. The merged wording promised eventual navigability for all three kinds. - The setup entry point moves out of the kebab to a persistent header button on every kind, per work item 613241. The empty state is unchanged.
- Downloads renders on Maven only. npm and container have no writer, so the figure there is zero however much is pulled. The hosted sidebar previously presented it without qualification on every format. #757 carries the writer into S18.
- The Go-service S13 gate was never the gate:
internal/remote/andinternal/virtual/are present. The gate is the S17 Phase 6 management surface.
Two stale lines in sibling specs, not fixed here
Recorded at the point of citation rather than corrected silently, per the project's contradiction rule. Both belong to their own specs.
S17's S18 dependency row still callsMavenRepositoryStore#BumpRepoDownloadCounta no-op stub. It is not.monolith/S14's Out of scope still gates remote and virtual on Go-service S13.
Design sources
The Figma remote and virtual frames and the prototype's remote and virtual pages were consulted directly this round; an earlier revision of S07 recorded the Figma links as auth-gated and not consulted. The prototype was driven live in a browser, which is what separated drawn controls from working ones: every test-connection control is drawn in three places and implemented in none, so no source specified a result surface. This MR specifies one, splitting the payload by what it carries. The stored health fields update the connection indicator in place; the probe-only fields get their own surface per placement, inline on the forms and a toast on the detail page.
Where the two disagree, the specs follow Figma and say so at the point of disagreement. Known prototype defects are not treated as design intent: its remote empty state contradicts itself, its non-Maven remote pages drop the upstream URL's copy control, its virtual edit form offers Clear cache on hosted rows, and its bulk clear-all-caches link on the picker card never renders.
Reviewable size
2,918 insertions across three files, past the 500-LOC guidance in
development-model.md. Splitting was considered and
rejected: the two amendments cross-reference each other throughout, since S05 selects
the settings type, health enum, and upstream-list field that S07 declares, and the
eviction ownership and test-connection placement were settled across both. Split, each
MR would carry claims verifiable only by reading the other, and merging out of order
would leave contradictory statements on main.
By file: S05 1,769 insertions, S07 1,461, and 6 in docs/specs/README.md, whose S05
index row described the slice as read-only, hosted-only, and carrying the Source column
this MR removes.
Both files also took a conciseness pass, so the diff includes deletions that are not scope changes: S05 lost 198 lines and S07 141, mostly process narration, restated constraints, and counts of things that can change without the spec changing. Two of those counts had already gone wrong.
Scope the design does not cover
Three form behaviors are not specified here, because no design source draws them,
and each is a follow-up: change tracking on the edit form, which now sends the writable
settings fields rather than only those the user touched; the stored-credentials
indicator; and the control that clears a stored credential. Credential writes are
therefore two-state, absent meaning unchanged and an object meaning replace. The spec
calls this operation clearing everywhere, matching the contract: RemoteCredentialPair
and RemoteCredentialToken are nullable and define null as the clear.
The third is a capability gap rather than a refinement, and the follow-up says so: a user with stored credentials cannot clear them through this slice at all, only replace them. AR accepts the clearing write; the monolith offers no way to ask for it.
Separately, editing the upstream URL is now ungated. An earlier revision read
SettingsUpdate's missing url as a permanent refusal and removed the input, its
warning, and the credentials-cleared handling. The absence was sequencing: AR split the
settings PATCH so credential rotation would not wait for the eviction tail, and every
branch now declares url. The URL input, its destructive warning, the
credentials-cleared handling, the post-change cache eviction, and the warning's
accessibility rule are all specified and buildable.
Review round four
Sixteen threads, almost all one cause: each of round three's amendments landed at some sites and not others, so a criterion or a sibling section still carried the pre-amendment contract. Six commits.
The credential cut reached Forms, the settings table, Credential writes,
Resolutions, Security, and Follow-ups, and missed the acceptance criterion, which still
told a test-author to build the two cut controls. A test written from it failed against
the specified form. It now asserts their absence. S05's reason for hiding
has_credentials (the edit form needs it) was false once no form rendered it. Credential
writes gained the placeholder hazard, because a user reaching for a way to clear will try
one and convert a working anonymous pull into a 401. The missing indicator and clear
control are raised on
gitlab-org/gitlab#598033,
which owns this slice's CRUD design.
The detail toast's http_status reached the Connection-section prose only. S07's
placement table, its rationale, and its error-cases row all still stated the premise the
amendment reverses, and no criterion asserted the field. All five sites now agree.
Shipped-state claims are gone. The State column's other seven rows carry a spec
state, so the two reading Shipped were outliers, and the Summary and the table had
already drifted apart that way. No legend is needed now that the column reports one
thing.
Gate tags. A criterion asserted the update sends only changed fields, which Forms defers to a follow-up, under a gate that had already cleared. A criterion now names its gate and nothing else: the contract behind it lives in the Dependencies table, which is the single place tracking whether it has cleared. Three criteria that also cited their contract inline are where the stale claim came from.
The upstream picker. Its search box filters only what the client holds, because AR's list route declares no search parameter. Three defects followed and are now specified rather than left to the implementer: the filter is a standing predicate re-applied to every appended page, a page whose rows all drop triggers the next request instead of a zero-results line, and a namespace with no attachable source gets its own line. The missing AR parameter has an endpoint-table row and #877; it blocks nothing, so it earns no gate row.
Smaller fixes. The settings read type no longer claims the credentials-cleared
field, which only ever exists on an update response. The URL gate's clause structure is
restored and now says what an operator can do while it stands, which is nothing. The
destructive gate states plainly that its break can be user-visible first, since AR and
the monolith deploy on independent pipelines. S05's two sections no longer contradict
each other about the Test control on a settings-absent remote, with a criterion pinning
it and a follow-up asking AR to make the state unrepresentable.
Open with design
The frame 849-47899 draws the picker's no-attachable-sources state with no text. The
spec says its copy needs design rather than inventing it.
Review round five
Two threads, both blocking, both valid.
The contract moved under the spec. The branch carried a stale
api/openapi/v1.yaml, so the gate rows and every site quoting them described a contract
two AR merges out of date. A reader following the spec's own rule, grep the contract
document, would have found the opposite of the prose. Re-read against origin/main:
remote create and URL editing have cleared, virtual create has not, and the probe,
destructive, and list-search rows still stand. See the Gates section above. The branch
is rebased on main, so the contract a reader greps matches the prose.
Five criterion tags broke the rule round four wrote. Two criteria asserting the same verbs carried different gates; one tag duplicated the Dependencies table's row state inside itself; two named no gate despite no pending change; and the delete-refusal criterion gated two of its three conditions on a phase with no merge-order row, the uncheckable shape the Resolutions entry rejects. All 40 criteria now carry a tag.
Testing
Documentation only. No e2e scenario is affected; both specs remain behind the
disabled artifact_registry_ui flag and neither describes shipped behavior.
Next
/monolith-planner decomposes each phase. monolith/S07's remote plan is drafted and
names this MR as a hard predecessor.