Reset existing dap_powered reviewer assignment strategy rows
Phase C. Blocked by #607677 (closed), and must not start until the Phase A trigger path is verified in production. This is the point rollback stops being a code revert.
Problem — risk corrected after code verification
Once the setting is removed there is no way to change reviewer_assignment_strategy off dap_powered, but rows still hold the value.
The original claim that stale rows would make "auto-assignment run with no strategy" after Phase D turns out to be wrong — the verified behaviour is a graceful no-op: Rails reads an unmapped enum value as nil (ActiveRecord::Enum::EnumType#deserialize → mapping.key(2) → nil; no raise on read), reviewer_auto_assignment_enabled? is then true (nil != 'disabled'), but StrategyFactory.build returns nil for unmapped names (strategy_factory.rb — STRATEGIES only maps code_owners) and AssignService returns skipped('No strategy available') as a success (assign_service.rb:18). Already covered by strategy_factory_spec.rb:32-40.
So the reset is not crash-prevention. It is still required because stale rows:
- silently lose the feature — the project owner chose DAP assignment and gets nothing, with no signal;
- waste work —
reviewer_auto_assignment_enabled?being true keeps enqueueingAutoAssignReviewersWorker(ee/.../update_service.rb:128,reload_merge_head_diff_service.rb:30) and marking the Redis flag (create_service.rb:29) just to no-op; - render dishonest UI — the settings checkbox shows unchecked (
reviewer_assignment_code_owners?false) while the row holds a live-looking value.
(The related hard-failure trap — NoMethodError from reviewer_assignment_dap_powered? once the enum value is gone — belongs to Phase D sequencing and is called out on #607679 (closed).)
Implementation
1. Audit first, split by whether the row ever worked
Count and list projects with project_settings.reviewer_assignment_strategy = 2. Because the REST API was never flag-gated (see #607677 (closed)), this set may be wider than the dap_powered_recommend_reviewers rollout. The worker's DAP branch only ever ran when Project#dap_powered_recommend_reviewers_available? held (feature flag with project actor + duo_foundational_flows_enabled + recommend_reviewers/v1 in enabled_flow_catalog_item_ids + StageCheck, ee/app/models/ee/project.rb:736-744) — rows that never satisfied that gate never executed DAP assignment and only need the reset, not migration. Console sketch:
rows = ProjectSetting.where(reviewer_assignment_strategy: 2).includes(:project)
functioning, inert = rows.partition { |ps| ps.project.dap_powered_recommend_reviewers_available? }2. Migrate the functioning projects to triggers
For each functioning project, enable the Recommend Reviewers flow and create a Merge request > Marked ready trigger, or notify the owners with instructions. Do this before resetting the value, so nobody silently loses the feature. Decide the consent posture explicitly: creating triggers inside customer projects is an action in their space — notify-with-instructions is the default unless product signs off on auto-creation. (These projects already have the flow enabled and a service account provisioned — that's part of the availability gate — so the missing piece is only the Ai::FlowTrigger row.)
3. Batched post-deploy migration
Validated approach — project_settings is table_size: medium, not on the high-traffic list (rubocop/rubocop-migrations.yml), so a regular post-deploy migration with each_batch is within guidelines (≤ 10 min); a batched background migration is not needed for a small-cardinality update (the BBM precedent on this table, ResetProjectSettingsDuoFoundationalFlowsEnabled, touched every row). Two facts shape the migration: the PK is project_id (not id), and there is no index on reviewer_assignment_strategy — so use the three-migration shape from db/post_migrate/20260717205311..13 (service desk settings): temporary partial index → batched update → drop index.
# 1) add_concurrent_index :project_settings, :project_id,
# name: 'tmp_idx_project_settings_reviewer_strategy_dap',
# where: 'reviewer_assignment_strategy = 2'
# 2) the reset
class ResetDapPoweredReviewerAssignmentStrategy < Gitlab::Database::Migration[2.3]
restrict_gitlab_migration gitlab_schema: :gitlab_main_org
disable_ddl_transaction!
milestone '19.5'
BATCH_SIZE = 500
def up
scope = ->(model) { model.where(reviewer_assignment_strategy: 2) }
each_batch(:project_settings, scope: scope, of: BATCH_SIZE) do |batch, _model|
batch.update_all(reviewer_assignment_strategy: 0)
end
end
def down
# no-op: the previous value is not recoverable; restore from the audit list if needed
end
end
# 3) remove_concurrent_index by nameThe audit list from step 1 is the de-facto rollback artifact — attach it to this issue before the migration ships.
Reset to disabled, not code_owners
Resetting to code_owners would silently start assigning every code owner on projects that deliberately chose the narrower DAP strategy. disabled is the honest default; owners who want code owners can opt back in.
Acceptance criteria
- Affected project count and list recorded on this issue, split into functioning vs never-functioning rows
- Every functioning project either has a trigger configured or has been notified
- Post-deploy migration (tmp index → batched reset to
0→ drop index) shipped - No rows with value
2remain before the Phase D enum removal ships