docs(specs): add the monolith/S15 permission-gated controls spec
What
Adds monolith/S15, the spec for gating Artifact Registry (AR) UI controls on the permission verdicts AR embeds in its management API responses. This revision carries three things: the spec, fixes from review round 1, and a repair to monolith/S07.
The slice consumes the merged contract rather than proposing one: include_permissions on the repository detail read, the repository list read, and the namespace details read, with verdicts arriving as a permissions object on the resource. The closed action sets are NamespacePermissions (seven actions) and RepositoryPermissions (nine).
The rule worth reviewing first
Repository-scoped verdicts are sufficient for per-row controls on nested lists, because the repository block rides whichever repository read the page already performs, not only the repository-detail page's. A deep link to a versions route resolves organization, then repository, then artifact, then versions, so the repository read is unavoidable and its verdicts gate every row at no extra call.
The rule depends on a constraint the spec states explicitly: no page renders nested rows without resolving the repository they belong to. If that ever stops holding, the rule stops holding with it. The create route is the one exception, and the spec names it as such.
State of the AR side
AR parses include_permissions on all three reads (internal/managementapi/detail.go, namespace_detail.go, list.go), api/openapi/v1.yaml references the parameter on three operations, and RepositoryList is now an object with a required repositories property, so the list envelope has flipped. The spec names the first release carrying each parse rather than asserting a moment, because two of its state claims went stale during review.
Fixes from review round 1
Four blocking items:
- The namespace connection test claim had gone stale. AR contracts and serves it, and
S17binds it tocreate_repository, which the namespace block already carries. The create form's Test connection control is not shipped in the monolith, so it is filed as gated on arrival. - The rule that a verdict rides a read the page already performs did not hold for the create route, whose namespace details read exists only to carry the verdict. The rule now names that route as its one exception.
- The Boolean-block shape cited the Virtual Registry per-object permission block as precedent. Virtual Registry has no permission field; it authorizes at the type. The citation now points at the real convention.
- The rule that an all-
falseblock from an absentpermissionsobject is not a durable answer had no acceptance criterion. One now pins it.
Two of the five questions changed the contract:
monolith/S03's cached client reuses the client instance and the credential, not the response, so the claim of request-scoped read de-duplication was wrong and is removed. Any future memo must key on whetherinclude_permissionswas sent, or a permission-free body reaches a caller that asked for permissions, the fail-closed rule hides every control, and nothing looks broken.- Gating rides the AR UI's existing shared flag rather than a new one.
A repair to monolith/S07
Two stale statements in that Approved spec, in their own commit:
- Its cross-spec dependency row recorded the AR-to-GLAZ batched check as "In design", although the design merged on 2026-07-30 and the Go service implements it.
- Its "UI pre-gating of affordances" follow-up entry linked no issue, which the monolith spec template forbids for an Approved spec. The entry now points at
monolith/S15and atgitlab-org/gitlab#627317.
The repair lands here rather than in a tracking issue, per the in-place rule in docs/dev/development-model.md. monolith/S07 has eight other follow-up entries with no linked issue; this MR deliberately does not touch them.
Out of scope, and why
Two items on a virtual repository's upstream row, Clear cache and Edit repository, ask about the upstream, which no read this slice performs returns verdicts for. That is a cross-resource question, not a nesting one, so no verdict on a nested operation reaches it. AR agreed the shape that answers it, and #1174 carries it. Both items render ungated until it lands. Remove repository, the third item on that kebab, asks about the virtual repository and is gated here.
Related to gitlab-org/gitlab#627317