Consult the Policy Store at CD deployment gates
What does this MR do and why?
⚠️ Stacked MR: targets the branch of !248000 (closed) (which itself stacks on !247753 (merged)) and shows only its own commit. GitLab retargets it automatically as the base MRs merge.
Implements KR8 of the Policy Store experiment epic (https://gitlab.com/groups/gitlab-org/-/epics/22027): the CD deploy driver can consult the Policy Store at a deployment gate and act on the verdict (allow / deny / require_approval).
Govern::Deployments::EvaluateService — the consultation. A stand-in for the Rego Policy Engine (GOVERN-009): it fetches the organization's active deployment-trigger policies through the gitlab-policy-store facade's organization-scoped evaluation read (list_for_evaluation, added in the base MR) — every policy access goes through the gem port, derives each policy's requested action from its actions data, and applies mode semantics — only enforce policies contribute to the verdict; audit and warn are recorded but never block, so policies can roll out non-disruptively. The overall verdict resolves by precedence deny > require_approval > allow. Every evaluation upserts a govern_policy_enforcements row keyed on the table's unique index, so re-evaluated gates update in place.
Cd::Rollouts::PolicyGateService — acting on the verdict. Mirrors the existing Cd::Rollouts::ResolveGateService mechanics: journal-only writes to Cd::RolloutTransition, which the workflow (AutoFlow/KAS) reads to resume or stop the rollout.
| Verdict | Action |
|---|---|
allow |
journal approve as principal policy_store |
deny |
journal reject, with the denying policy names as the reason |
require_approval |
no journal write — the rollout stays paused for the existing human approval flow |
The whole path is inert unless the Policy Store experiment is enabled (policy_store_experiment_enabled instance setting + security_policies_v2 feature flag checked against the organization). When disabled, the gate is transparently allowed without consulting or recording anything.
Evaluation reads are organization-scoped while policy CRUD stays group-scoped through the gem port — deliberate, per GOVERN-006: the CD domain is organization-anchored (no group associations), and engine evaluation is defined org-wide with scope predicates filtering within. Scope predicates and real Rego evaluation land with the engine; until then every active deployment policy of the organization applies.
Out of scope (deliberately): the API endpoint the deploy driver will call, UI, GraphQL, the Rego engine, and the per-policy-type context builders (vulnerability data, external change requests).
Verification
47 examples, 0 failures (3 pre-existing pending jsonb-validation TODOs): verdict precedence across modes and actions, enforcement upsert idempotency (state and last_evaluated_at update in place, created_at preserved), org/trigger/lifecycle filtering, journal writes per verdict, and the disabled-experiment and non-paused guard paths.
No migrations. The govern_policy_enforcements writes use a single-row upsert keyed on unique_govern_policy_enforcements_org_policy_and_entity.