From 9256d4bf5ace6c9112741beecb04e69cfb807fa6 Mon Sep 17 00:00:00 2001 From: Leo Wilkin Date: Sun, 25 Oct 2026 19:22:25 +0000 Subject: [PATCH 1/3] replace manual-verification flag actors with a computed flipper group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit opening a case enabled a stored flipper actor per identity, which hit flipper's 100-actor cap and 500'd case creation (Flipper::Adapters::ActorLimit::LimitExceeded). the actor list was a hand-synced mirror of "identities with an open case", so membership is now the :has_open_verification_case group, computed from the cases table — nothing per-actor to enable, revoke, or leak. a data migration enables the group once and drops the stored actors. --- .../backend/verification_cases_controller.rb | 4 ++-- app/models/verification_case.rb | 19 +++++------------- config/flipper_features.yml | 2 +- config/initializers/flipper.rb | 9 +++++++++ ...case_group_for_manual_verification_call.rb | 19 ++++++++++++++++++ spec/models/verification_case_spec.rb | 8 +++++--- .../backend/verification_cases_spec.rb | 7 +++++-- spec/requests/manual_verifications_spec.rb | 20 +++++++++---------- 8 files changed, 56 insertions(+), 32 deletions(-) create mode 100644 db/migrate/20261025000001_enable_open_case_group_for_manual_verification_call.rb diff --git a/app/controllers/backend/verification_cases_controller.rb b/app/controllers/backend/verification_cases_controller.rb index 71b33020..f9673825 100644 --- a/app/controllers/backend/verification_cases_controller.rb +++ b/app/controllers/backend/verification_cases_controller.rb @@ -57,7 +57,8 @@ def show end # staff entry point: user emailed identity@, staff opens a case. - # enables the flipper flag + sends the single-use link. + # the open case itself grants the flipper flag (group gate), so all + # that's left is sending the single-use link. def create authorize VerificationCase @@ -73,7 +74,6 @@ def create opened_by: current_user, skip_persona: params[:skip_persona] == "1" ) - @case.enable_flag! deliver_link! @case.log_event!(:case_opened, actor: current_user, request: request, diff --git a/app/models/verification_case.rb b/app/models/verification_case.rb index 26cee00f..af9cc323 100644 --- a/app/models/verification_case.rb +++ b/app/models/verification_case.rb @@ -127,20 +127,15 @@ class VerificationCase < ApplicationRecord event :approve do transitions from: :call_held, to: :approved - after { close_out! } end event :deny do transitions from: :call_held, to: :denied - after { close_out! } end event :withdraw do transitions from: [ :requested, :link_sent, :docs_submitted, :call_scheduled, :call_held ], to: :withdrawn - after do - close_out! - invalidate_access_token! - end + after { invalidate_access_token! } end end @@ -161,8 +156,10 @@ def decided? = approved? || denied? # -- feature flag ------------------------------------------------------ - def enable_flag! = Flipper.enable(FLIPPER_FLAG, identity) - def revoke_flag! = Flipper.disable(FLIPPER_FLAG, identity) + # access to the flow is computed, not stored: FLIPPER_FLAG is enabled for + # the :has_open_verification_case group (config/initializers/flipper.rb), + # which matches any identity with an open case — so opening a case grants + # the flow and closing it (approve / deny / withdraw) revokes it. # -- single-use access link (mirrors Identity::V2LoginCode) ------------- @@ -305,10 +302,4 @@ def log_event!(key, actor: nil, data: {}, request: nil) def reset_capture! update!(persona_inquiry_id: nil, persona_session_token: nil) end - - private - - # decision time: drop the flag. documents are retained (break-glass - # gated) — the ManualVerificationCall itself is created by the decision flow. - def close_out! = revoke_flag! end diff --git a/config/flipper_features.yml b/config/flipper_features.yml index 1d434abe..7d30030b 100644 --- a/config/flipper_features.yml +++ b/config/flipper_features.yml @@ -16,4 +16,4 @@ shared: disable_slack_invites_2025_12_08: suppresses Slack SCIM provisioning for an actor dev_force_manual_review_2026_05_27: forces verifications for an actor into manual review dev_force_deny_verification_2026_05_27: auto-denies verifications for an actor - manual_verification_call_2026_07_03: enables the manual verification call flow for an actor with an open case + manual_verification_call_2026_07_03: enables the manual verification call flow — granted via the has_open_verification_case group to any identity with an open case diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb index 8ef5626c..39fbcf3a 100644 --- a/config/initializers/flipper.rb +++ b/config/initializers/flipper.rb @@ -1,3 +1,12 @@ +# the actor side of manual_verification_call_* is computed, not stored: +# membership follows the open case, so opening a case grants the flow and +# closing it revokes it — nothing per-actor to enable, revoke, or leak. +# (storing one actor per case hit flipper's 100-actor limit, and every +# enabled? check loads the whole stored set.) +Flipper.register(:has_open_verification_case) do |actor| + actor.respond_to?(:verification_cases) && actor.verification_cases.open_cases.exists? +end + Rails.application.configure do config.flipper.memoize = true config.flipper.preload = true diff --git a/db/migrate/20261025000001_enable_open_case_group_for_manual_verification_call.rb b/db/migrate/20261025000001_enable_open_case_group_for_manual_verification_call.rb new file mode 100644 index 00000000..b9c80523 --- /dev/null +++ b/db/migrate/20261025000001_enable_open_case_group_for_manual_verification_call.rb @@ -0,0 +1,19 @@ +# the manual-verification flag kept one stored actor per open case and hit +# flipper's 100-actor cap on case creation (ActorLimit::LimitExceeded). +# membership is now computed by the :has_open_verification_case group +# (config/initializers/flipper.rb): enable it once here, and drop the stored +# actors — they're redundant, and every enabled? check loads the whole set. +class EnableOpenCaseGroupForManualVerificationCall < ActiveRecord::Migration[8.0] + FLAG = "manual_verification_call_2026_07_03" + + def up + Flipper.enable_group(FLAG, :has_open_verification_case) + execute("DELETE FROM flipper_gates WHERE feature_key = #{connection.quote(FLAG)} AND key = 'actors'") + end + + def down + # the deleted actor rows can't be restored — rolling back means + # re-enabling actors by hand for identities with open cases + Flipper.disable_group(FLAG, :has_open_verification_case) + end +end diff --git a/spec/models/verification_case_spec.rb b/spec/models/verification_case_spec.rb index ddf0dcfb..c9d845f8 100644 --- a/spec/models/verification_case_spec.rb +++ b/spec/models/verification_case_spec.rb @@ -52,10 +52,11 @@ expect { kase.deny! }.to raise_error(AASM::InvalidTransition) end - it "revokes the flag on decision and keeps documents" do + it "loses the flag on decision (the group follows the open case) and keeps documents" do kase = create(:verification_case, :call_held) doc = create(:verification_case_document, verification_case: kase) - Flipper.enable(described_class::FLIPPER_FLAG, kase.identity) + Flipper.enable_group(described_class::FLIPPER_FLAG, :has_open_verification_case) + expect(Flipper.enabled?(described_class::FLIPPER_FLAG, kase.identity)).to be(true) kase.approve! @@ -66,9 +67,10 @@ describe "#withdraw" do it "closes from any open state, drops the flag, and kills the link" do + Flipper.enable_group(described_class::FLIPPER_FLAG, :has_open_verification_case) %i[requested link_sent docs_submitted call_scheduled call_held].each do |state| kase = create(:verification_case, state == :requested ? nil : state, access_token: "tok", access_token_expires_at: 1.day.from_now) - Flipper.enable(described_class::FLIPPER_FLAG, kase.identity) + expect(Flipper.enabled?(described_class::FLIPPER_FLAG, kase.identity)).to be(true) kase.withdraw! diff --git a/spec/requests/backend/verification_cases_spec.rb b/spec/requests/backend/verification_cases_spec.rb index e4763b81..6b70c239 100644 --- a/spec/requests/backend/verification_cases_spec.rb +++ b/spec/requests/backend/verification_cases_spec.rb @@ -12,8 +12,10 @@ after { Flipper.disable(VerificationCase::FLIPPER_FLAG) } describe "POST /backend/verification_cases" do - it "opens a case, enables the flag, and emails the single-use link" do + it "opens a case (which grants the flag via the group) and emails the single-use link" do + Flipper.enable_group(VerificationCase::FLIPPER_FLAG, :has_open_verification_case) target = create(:identity) + expect(Flipper.enabled?(VerificationCase::FLIPPER_FLAG, target)).to be(false) expect { post backend_verification_cases_path, params: { identity_id: target.public_id } @@ -318,7 +320,8 @@ def guardian_pings = enqueued_jobs.select { |j| j[:job] == Slack::NotifyGuardian it "closes the case, revokes the flag, invalidates the link, and logs the reason" do kase = create(:verification_case, :call_scheduled) token = kase.access_token - Flipper.enable(VerificationCase::FLIPPER_FLAG, kase.identity) + Flipper.enable_group(VerificationCase::FLIPPER_FLAG, :has_open_verification_case) + expect(Flipper.enabled?(VerificationCase::FLIPPER_FLAG, kase.identity)).to be(true) post withdraw_backend_verification_case_path(kase), params: { reason: "opened on the wrong account" } diff --git a/spec/requests/manual_verifications_spec.rb b/spec/requests/manual_verifications_spec.rb index 0530d96e..9dfebe39 100644 --- a/spec/requests/manual_verifications_spec.rb +++ b/spec/requests/manual_verifications_spec.rb @@ -13,17 +13,24 @@ allow_any_instance_of(ApplicationController).to receive(:current_identity).and_return(identity) allow_any_instance_of(ApplicationController).to receive(:current_session).and_return(session) allow_any_instance_of(ApplicationController).to receive(:identity_signed_in?).and_return(true) + + # production state: the feature is on for the has_open_verification_case + # group, so having an open case is what grants access + Flipper.enable_group(VerificationCase::FLIPPER_FLAG, :has_open_verification_case) end after { Flipper.disable(VerificationCase::FLIPPER_FLAG) } describe "gating" do - it "bounces users without the flipper flag" do + it "bounces users when the feature is off, open case or not" do + create(:verification_case, identity: identity, status: :link_sent, access_token_used_at: Time.current) + Flipper.disable(VerificationCase::FLIPPER_FLAG) + get manual_verification_path expect(response).to redirect_to(root_path) end - it "bounces flagged users with no open case" do + it "bounces users with no open case, even manually flagged ones" do Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) get manual_verification_path expect(response).to redirect_to(root_path) @@ -32,7 +39,6 @@ it "requires the single-use token on first visit" do kase = create(:verification_case, identity: identity, status: :link_sent) kase.generate_access_token! - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) get manual_verification_path expect(response).to have_http_status(:forbidden) @@ -41,7 +47,6 @@ it "consumes a valid token then allows session access" do kase = create(:verification_case, identity: identity, status: :link_sent) token = kase.generate_access_token! - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) get manual_verification_path(token: token) expect(response).to redirect_to(manual_verification_path) @@ -56,7 +61,6 @@ it "shows the invalid-link page to a user following the old emailed link" do kase = create(:verification_case, identity: identity, status: :link_sent) token = kase.generate_access_token! - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) kase.withdraw! @@ -78,7 +82,6 @@ it "puts the user back at document submission, needing the new link first" do kase = create(:verification_case, identity: identity, status: :docs_submitted, document_class: "government_id", access_token_used_at: 1.hour.ago) - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) kase.request_redo! token = kase.rotate_access_link!(reopen_gate: true) @@ -101,10 +104,7 @@ access_token_used_at: Time.current) end - before do - kase - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) - end + before { kase } it "shows document class selection first" do get manual_verification_path From f07caa02b4e6878af5ede371a766671d104c45bb Mon Sep 17 00:00:00 2001 From: Leo Wilkin Date: Sun, 25 Oct 2026 19:38:16 +0000 Subject: [PATCH 2/3] drop flipper from the manual verification flow entirely per review: the flag was only ever a hand-synced mirror of "identity has an open case", so gate directly on that instead. the migration now removes the feature and its ~100 stored actors (which had hit flipper's 100-actor cap and 500'd case creation). --- app/controllers/application_controller.rb | 1 - .../manual_verifications_controller.rb | 7 +------ app/models/verification_case.rb | 8 -------- config/flipper_features.yml | 3 +-- config/initializers/flipper.rb | 9 --------- ...case_group_for_manual_verification_call.rb | 19 ------------------- ...e_manual_verification_call_flipper_flag.rb | 14 ++++++++++++++ spec/models/verification_case_spec.rb | 10 ++-------- .../backend/verification_cases_spec.rb | 12 ++---------- spec/requests/manual_verifications_spec.rb | 17 +---------------- 10 files changed, 21 insertions(+), 79 deletions(-) delete mode 100644 db/migrate/20261025000001_enable_open_case_group_for_manual_verification_call.rb create mode 100644 db/migrate/20261025000001_remove_manual_verification_call_flipper_flag.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9a8eac9a..ad36517e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -48,7 +48,6 @@ def authenticate_identity! # verification flows — controllers serving those flows use this as a # before_action so users land on their case instead of a legacy page def redirect_to_open_manual_case - return unless Flipper.enabled?(VerificationCase::FLIPPER_FLAG, current_identity) return unless current_identity.verification_cases.open_cases.exists? redirect_to manual_verification_path diff --git a/app/controllers/manual_verifications_controller.rb b/app/controllers/manual_verifications_controller.rb index c9ca4201..fcfe4709 100644 --- a/app/controllers/manual_verifications_controller.rb +++ b/app/controllers/manual_verifications_controller.rb @@ -1,5 +1,5 @@ # the user side of a manual verification call case. gated three ways: -# logged in (ApplicationController), flipper flag on the identity, and +# logged in (ApplicationController), an open case on the identity, and # a single-use emailed link consumed on first visit. class ManualVerificationsController < ApplicationController before_action :set_case @@ -175,11 +175,6 @@ def acknowledge_call_capture private def set_case - unless Flipper.enabled?(VerificationCase::FLIPPER_FLAG, current_identity) - render(:link_invalid, status: :forbidden) and return if params[:token].present? - redirect_to root_path and return - end - @case = current_identity.verification_cases.open_cases.order(created_at: :desc).first return if @case diff --git a/app/models/verification_case.rb b/app/models/verification_case.rb index af9cc323..4902ac44 100644 --- a/app/models/verification_case.rb +++ b/app/models/verification_case.rb @@ -14,7 +14,6 @@ class VerificationCase < ApplicationRecord include PublicIdentifiable set_public_id_prefix "vcase" - FLIPPER_FLAG = :manual_verification_call_2026_07_03 ACCESS_TOKEN_TTL = 7.days belongs_to :identity @@ -154,13 +153,6 @@ def destroy_fully! end def decided? = approved? || denied? - # -- feature flag ------------------------------------------------------ - - # access to the flow is computed, not stored: FLIPPER_FLAG is enabled for - # the :has_open_verification_case group (config/initializers/flipper.rb), - # which matches any identity with an open case — so opening a case grants - # the flow and closing it (approve / deny / withdraw) revokes it. - # -- single-use access link (mirrors Identity::V2LoginCode) ------------- # once the link has been opened, the authenticated session is what gets diff --git a/config/flipper_features.yml b/config/flipper_features.yml index 7d30030b..f2a87f1d 100644 --- a/config/flipper_features.yml +++ b/config/flipper_features.yml @@ -15,5 +15,4 @@ shared: dev_allow_manual_flow_2026_05_27: enables the manual document verification flow for an actor disable_slack_invites_2025_12_08: suppresses Slack SCIM provisioning for an actor dev_force_manual_review_2026_05_27: forces verifications for an actor into manual review - dev_force_deny_verification_2026_05_27: auto-denies verifications for an actor - manual_verification_call_2026_07_03: enables the manual verification call flow — granted via the has_open_verification_case group to any identity with an open case + dev_force_deny_verification_2026_05_27: auto-denies verifications for an actor \ No newline at end of file diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb index 39fbcf3a..8ef5626c 100644 --- a/config/initializers/flipper.rb +++ b/config/initializers/flipper.rb @@ -1,12 +1,3 @@ -# the actor side of manual_verification_call_* is computed, not stored: -# membership follows the open case, so opening a case grants the flow and -# closing it revokes it — nothing per-actor to enable, revoke, or leak. -# (storing one actor per case hit flipper's 100-actor limit, and every -# enabled? check loads the whole stored set.) -Flipper.register(:has_open_verification_case) do |actor| - actor.respond_to?(:verification_cases) && actor.verification_cases.open_cases.exists? -end - Rails.application.configure do config.flipper.memoize = true config.flipper.preload = true diff --git a/db/migrate/20261025000001_enable_open_case_group_for_manual_verification_call.rb b/db/migrate/20261025000001_enable_open_case_group_for_manual_verification_call.rb deleted file mode 100644 index b9c80523..00000000 --- a/db/migrate/20261025000001_enable_open_case_group_for_manual_verification_call.rb +++ /dev/null @@ -1,19 +0,0 @@ -# the manual-verification flag kept one stored actor per open case and hit -# flipper's 100-actor cap on case creation (ActorLimit::LimitExceeded). -# membership is now computed by the :has_open_verification_case group -# (config/initializers/flipper.rb): enable it once here, and drop the stored -# actors — they're redundant, and every enabled? check loads the whole set. -class EnableOpenCaseGroupForManualVerificationCall < ActiveRecord::Migration[8.0] - FLAG = "manual_verification_call_2026_07_03" - - def up - Flipper.enable_group(FLAG, :has_open_verification_case) - execute("DELETE FROM flipper_gates WHERE feature_key = #{connection.quote(FLAG)} AND key = 'actors'") - end - - def down - # the deleted actor rows can't be restored — rolling back means - # re-enabling actors by hand for identities with open cases - Flipper.disable_group(FLAG, :has_open_verification_case) - end -end diff --git a/db/migrate/20261025000001_remove_manual_verification_call_flipper_flag.rb b/db/migrate/20261025000001_remove_manual_verification_call_flipper_flag.rb new file mode 100644 index 00000000..d0c147eb --- /dev/null +++ b/db/migrate/20261025000001_remove_manual_verification_call_flipper_flag.rb @@ -0,0 +1,14 @@ +# the manual verification flow is no longer flipper-gated — access is simply +# "this identity has an open case", checked in code. the feature kept one +# stored actor per open case, which hit flipper's 100-actor cap and 500'd +# case creation (ActorLimit::LimitExceeded), and the actor list was only +# ever a hand-synced mirror of the cases table anyway. +class RemoveManualVerificationCallFlipperFlag < ActiveRecord::Migration[8.0] + def up + Flipper.remove("manual_verification_call_2026_07_03") + end + + def down + # nothing to restore — the flow is no longer flipper-gated + end +end diff --git a/spec/models/verification_case_spec.rb b/spec/models/verification_case_spec.rb index c9d845f8..bf675dca 100644 --- a/spec/models/verification_case_spec.rb +++ b/spec/models/verification_case_spec.rb @@ -52,25 +52,20 @@ expect { kase.deny! }.to raise_error(AASM::InvalidTransition) end - it "loses the flag on decision (the group follows the open case) and keeps documents" do + it "keeps documents on decision" do kase = create(:verification_case, :call_held) doc = create(:verification_case_document, verification_case: kase) - Flipper.enable_group(described_class::FLIPPER_FLAG, :has_open_verification_case) - expect(Flipper.enabled?(described_class::FLIPPER_FLAG, kase.identity)).to be(true) kase.approve! - expect(Flipper.enabled?(described_class::FLIPPER_FLAG, kase.identity)).to be(false) expect(doc.reload.file).to be_attached end end describe "#withdraw" do - it "closes from any open state, drops the flag, and kills the link" do - Flipper.enable_group(described_class::FLIPPER_FLAG, :has_open_verification_case) + it "closes from any open state and kills the link" do %i[requested link_sent docs_submitted call_scheduled call_held].each do |state| kase = create(:verification_case, state == :requested ? nil : state, access_token: "tok", access_token_expires_at: 1.day.from_now) - expect(Flipper.enabled?(described_class::FLIPPER_FLAG, kase.identity)).to be(true) kase.withdraw! @@ -80,7 +75,6 @@ expect(kase).not_to be_decided expect(kase.access_token).to be_nil expect(kase.consume_access_token!("tok")).to be(false) - expect(Flipper.enabled?(described_class::FLIPPER_FLAG, kase.identity)).to be(false) end end diff --git a/spec/requests/backend/verification_cases_spec.rb b/spec/requests/backend/verification_cases_spec.rb index 6b70c239..4630f758 100644 --- a/spec/requests/backend/verification_cases_spec.rb +++ b/spec/requests/backend/verification_cases_spec.rb @@ -9,13 +9,9 @@ allow_any_instance_of(Backend::ApplicationController).to receive(:require_2fa!).and_return(true) end - after { Flipper.disable(VerificationCase::FLIPPER_FLAG) } - describe "POST /backend/verification_cases" do - it "opens a case (which grants the flag via the group) and emails the single-use link" do - Flipper.enable_group(VerificationCase::FLIPPER_FLAG, :has_open_verification_case) + it "opens a case and emails the single-use link" do target = create(:identity) - expect(Flipper.enabled?(VerificationCase::FLIPPER_FLAG, target)).to be(false) expect { post backend_verification_cases_path, params: { identity_id: target.public_id } @@ -24,7 +20,6 @@ kase = target.verification_cases.sole expect(kase).to be_link_sent expect(kase.skip_persona).to be(false) - expect(Flipper.enabled?(VerificationCase::FLIPPER_FLAG, target)).to be(true) end it "opens a skip-persona case when asked" do @@ -317,11 +312,9 @@ def guardian_pings = enqueued_jobs.select { |j| j[:job] == Slack::NotifyGuardian end describe "POST /backend/verification_cases/:id/withdraw" do - it "closes the case, revokes the flag, invalidates the link, and logs the reason" do + it "closes the case, invalidates the link, and logs the reason" do kase = create(:verification_case, :call_scheduled) token = kase.access_token - Flipper.enable_group(VerificationCase::FLIPPER_FLAG, :has_open_verification_case) - expect(Flipper.enabled?(VerificationCase::FLIPPER_FLAG, kase.identity)).to be(true) post withdraw_backend_verification_case_path(kase), params: { reason: "opened on the wrong account" } @@ -330,7 +323,6 @@ def guardian_pings = enqueued_jobs.select { |j| j[:job] == Slack::NotifyGuardian expect(kase.verification).to be_nil expect(kase.access_token).to be_nil expect(kase.consume_access_token!(token)).to be(false) - expect(Flipper.enabled?(VerificationCase::FLIPPER_FLAG, kase.identity)).to be(false) event = kase.events.find_by(key: "case_withdrawn") expect(event.actor).to eq(verifier) diff --git a/spec/requests/manual_verifications_spec.rb b/spec/requests/manual_verifications_spec.rb index 9dfebe39..5bb19afa 100644 --- a/spec/requests/manual_verifications_spec.rb +++ b/spec/requests/manual_verifications_spec.rb @@ -13,25 +13,10 @@ allow_any_instance_of(ApplicationController).to receive(:current_identity).and_return(identity) allow_any_instance_of(ApplicationController).to receive(:current_session).and_return(session) allow_any_instance_of(ApplicationController).to receive(:identity_signed_in?).and_return(true) - - # production state: the feature is on for the has_open_verification_case - # group, so having an open case is what grants access - Flipper.enable_group(VerificationCase::FLIPPER_FLAG, :has_open_verification_case) end - after { Flipper.disable(VerificationCase::FLIPPER_FLAG) } - describe "gating" do - it "bounces users when the feature is off, open case or not" do - create(:verification_case, identity: identity, status: :link_sent, access_token_used_at: Time.current) - Flipper.disable(VerificationCase::FLIPPER_FLAG) - - get manual_verification_path - expect(response).to redirect_to(root_path) - end - - it "bounces users with no open case, even manually flagged ones" do - Flipper.enable(VerificationCase::FLIPPER_FLAG, identity) + it "bounces users with no open case" do get manual_verification_path expect(response).to redirect_to(root_path) end From 9c93cbbe3619c796234364ccc72526bbe7c92040 Mon Sep 17 00:00:00 2001 From: Leo Wilkin Date: Sun, 25 Oct 2026 19:53:28 +0000 Subject: [PATCH 3/3] drop the flipper cleanup migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit per review: flipper state is ops data, not schema — the dead feature and its stored actors get deleted from the flipper UI instead. --- ...remove_manual_verification_call_flipper_flag.rb | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 db/migrate/20261025000001_remove_manual_verification_call_flipper_flag.rb diff --git a/db/migrate/20261025000001_remove_manual_verification_call_flipper_flag.rb b/db/migrate/20261025000001_remove_manual_verification_call_flipper_flag.rb deleted file mode 100644 index d0c147eb..00000000 --- a/db/migrate/20261025000001_remove_manual_verification_call_flipper_flag.rb +++ /dev/null @@ -1,14 +0,0 @@ -# the manual verification flow is no longer flipper-gated — access is simply -# "this identity has an open case", checked in code. the feature kept one -# stored actor per open case, which hit flipper's 100-actor cap and 500'd -# case creation (ActorLimit::LimitExceeded), and the actor list was only -# ever a hand-synced mirror of the cases table anyway. -class RemoveManualVerificationCallFlipperFlag < ActiveRecord::Migration[8.0] - def up - Flipper.remove("manual_verification_call_2026_07_03") - end - - def down - # nothing to restore — the flow is no longer flipper-gated - end -end