Implement Organization Maintenance Mode
## Problem statement
We need to be able to make a given Organization unavailable on its source Cell while a migration (eg. to a Protocell) or isolation enablement is in progress. This is **not** the same as instance-wide Maintenance Mode: only the affected Organization is frozen, and every other Organization sharing the Cell stays fully operational.
The design is formalized in [ADR 010: Organization Maintenance Mode](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/organization/decisions/010_organization_maintenance_mode/). In summary:
* A per-Organization state on `Organizations::Stateful`: `active → maintenance_initialization → maintenance` (and back), with a `reason` recorded for audit (migration, isolation, incident, billing, legal). The reason is never surfaced to end users
* For this first iteration **all** requests are denied, reads included. HTTP method is not a reliable proxy for "no write": some `GET`s trigger DB writes or enqueue Sidekiq jobs. Full or partial reads may be reintroduced in a future iteration
* **No authentication exemption.** Sign-in, OAuth, JWT, and SAML/SSO callbacks are denied like any other request, because auth flows `INSERT` new `users` rows and update per-request timestamps that can cascade into org-owned state
* Enforcement keyed off `Current.organization` at every request surface: controllers, REST API (Grape), GraphQL, `Gitlab::GitAccess`, Container Registry, LFS, and Sidekiq. Deliberately no Rack middleware
* Org-scoped Sidekiq jobs already queued or in-flight **drain to completion**; cron workers skip Organizations in maintenance; BBMs are paused on the source Cell and re-enqueued on the destination Cell after cutover
* A cutover readiness contract (with a drain confirmation window and bounded wait then escalate) gates the data-copy step on a fully drained source Cell
* The default Organization is excluded, since freezing it would be equivalent to taking the whole instance offline
The POC (gitlab-org/gitlab!228743) validated the approach and will not be merged; production implementation builds on the merged unified state machine (gitlab-org/gitlab!230909).
## Exit criteria
* All requests (web UI, GraphQL queries and mutations, REST API, git pull and push, registry, LFS) to an Organization in `maintenance_initialization` or `maintenance` are denied with a structured error: `503` + `Retry-After` for time-bounded reasons, `403` otherwise
* Browser requests render a static maintenance page with generic copy that does not reveal the reason or any infrastructure detail (Cell, migration)
* Authentication is denied with no exemption; no new `users` rows are created for an Organization in maintenance
* Org-scoped Sidekiq jobs drain to completion, cron workers skip the Organization in maintenance and its projects and namespaces, and every skip or cancel emits a structured log with `organization_id`, `worker`, and `jid`
* Cell-wide cron workers that iterate org-owned data filter to active Organizations via a shared scope, with tests asserting non-`active` Organizations are not yielded
* A cutover readiness check reports when the source Cell is fully drained for the Organization, confirmed over at least two runs, escalating to an operator if it does not converge
* New pipelines are blocked and in-flight CI jobs are cancelled on entering maintenance
* Every entry into and exit from the maintenance states emits an Organization-level audit event
* The default Organization cannot be transitioned into maintenance
* Rollout is gated by a single Organization-scoped derisk feature flag that is removed once fully rolled out; no permanent ops flag is added
## Status 2026-08-20
*Block-all enforcement is merged behind the flag; rollout is queued behind a rename refactor.*
## 🕐 total hours spent this week by all contributors: 30
## 🎉 achievements
* ADR merged: https://gitlab.com/gitlab-com/content-sites/handbook/-/merge_requests/20741+ — **Organization Maintenance Mode** is now the agreed design, blocking every request for a frozen Organization with no auth exemption.
* Enforcement merged: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/250225+ blocks all requests, including GETs and GraphQL reads, and serves a 503 maintenance page. Closes https://gitlab.com/gitlab-org/gitlab/-/work_items/607966+.
* Both reverts of the now-redundant auth work merged: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/249739+ and https://gitlab.com/gitlab-org/gitlab/-/merge_requests/249855+.
## 🚫 blockers
* Rollout is blocked until the rename refactor https://gitlab.com/gitlab-org/gitlab/-/merge_requests/250667+ and the stacked registry cleanup https://gitlab.com/gitlab-org/gitlab/-/merge_requests/250986+ are merged. These are blocked on reviews by the container registry team.
## ▶️ next
* Merge https://gitlab.com/gitlab-org/gitlab/-/merge_requests/250667+ and https://gitlab.com/gitlab-org/gitlab/-/merge_requests/250986+.
* Enable the flag in non-production, then verify end-to-end against an internal Organization per https://gitlab.com/gitlab-org/gitlab/-/work_items/618032+.
_Copied from https://gitlab.com/groups/gitlab-org/-/epics/20404#note_3708316640_
epic