diff --git a/.gitlab/ci/gitlab-gems.gitlab-ci.yml b/.gitlab/ci/gitlab-gems.gitlab-ci.yml
index f188e1f97f8ad4fc128821281695eeec33715d88..c996f96b823b227c864df64ef19a245e651e9ba6 100644
--- a/.gitlab/ci/gitlab-gems.gitlab-ci.yml
+++ b/.gitlab/ci/gitlab-gems.gitlab-ci.yml
@@ -4,16 +4,19 @@ include:
       gem_name: "activerecord-gitlab"
   - local: .gitlab/ci/templates/gem.gitlab-ci.yml
     inputs:
-      gem_name: "gitlab-rspec"
+      gem_name: "click_house-client"
   - local: .gitlab/ci/templates/gem.gitlab-ci.yml
     inputs:
-      gem_name: "gitlab-utils"
+      gem_name: "gitlab-ipynbdiff"
   - local: .gitlab/ci/templates/gem.gitlab-ci.yml
     inputs:
-      gem_name: "click_house-client"
+      gem_name: "gitlab-rspec"
   - local: .gitlab/ci/templates/gem.gitlab-ci.yml
     inputs:
       gem_name: "gitlab-schema-validation"
   - local: .gitlab/ci/templates/gem.gitlab-ci.yml
     inputs:
-      gem_name: "gitlab-ipynbdiff"
+      gem_name: "gitlab-utils"
+  - local: .gitlab/ci/templates/gem.gitlab-ci.yml
+    inputs:
+      gem_name: "rspec_flaky"
diff --git a/.rubocop.yml b/.rubocop.yml
index 9c0fb76f68f0c44bd425c493df9a4519148cbcbb..f6d0625a5301bf922b1a758e6ced7b8590d59890 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -395,7 +395,6 @@ Gitlab/Json:
   Exclude:
     - 'qa/**/*'
     - 'scripts/**/*'
-    - 'tooling/rspec_flaky/**/*'
     - 'lib/quality/**/*'
     - 'tooling/danger/**/*'
 
diff --git a/.rubocop_todo/fips/md5.yml b/.rubocop_todo/fips/md5.yml
index d50aaf7950256e973e851cf821dbe8104eacb327..4a1994635b7b99cf151c2c3ede232b002ed11339 100644
--- a/.rubocop_todo/fips/md5.yml
+++ b/.rubocop_todo/fips/md5.yml
@@ -17,5 +17,3 @@ Fips/MD5:
     - 'spec/lib/gitlab/ci/trace/remote_checksum_spec.rb'
     - 'spec/models/concerns/checksummable_spec.rb'
     - 'spec/services/gravatar_service_spec.rb'
-    - 'spec/tooling/rspec_flaky/example_spec.rb'
-    - 'tooling/rspec_flaky/example.rb'
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml
index 72f43c7779e8ad1849b6d8bfd7a01b65d02b9246..0ffb5e3bdd858d1ab47566d2d9d84731062b1457 100644
--- a/.rubocop_todo/layout/line_length.yml
+++ b/.rubocop_todo/layout/line_length.yml
@@ -3103,7 +3103,6 @@ Layout/LineLength:
     - 'scripts/api/cancel_pipeline.rb'
     - 'scripts/api/get_job_id.rb'
     - 'scripts/changed-feature-flags'
-    - 'scripts/flaky_examples/prune-old-flaky-examples'
     - 'scripts/lint_templates_bash.rb'
     - 'scripts/no-dir-check'
     - 'scripts/perf/query_limiting_report.rb'
@@ -5067,9 +5066,6 @@ Layout/LineLength:
     - 'spec/tooling/lib/tooling/kubernetes_client_spec.rb'
     - 'spec/tooling/lib/tooling/test_map_generator_spec.rb'
     - 'spec/tooling/quality/test_level_spec.rb'
-    - 'spec/tooling/rspec_flaky/config_spec.rb'
-    - 'spec/tooling/rspec_flaky/flaky_examples_collection_spec.rb'
-    - 'spec/tooling/rspec_flaky/report_spec.rb'
     - 'spec/uploaders/ci/pipeline_artifact_uploader_spec.rb'
     - 'spec/uploaders/file_mover_spec.rb'
     - 'spec/uploaders/file_uploader_spec.rb'
@@ -5180,5 +5176,3 @@ Layout/LineLength:
     - 'tooling/lib/tooling/kubernetes_client.rb'
     - 'tooling/merge_request_rspec_failure_rake_task.rb'
     - 'tooling/quality/test_level.rb'
-    - 'tooling/rspec_flaky/listener.rb'
-    - 'tooling/rspec_flaky/report.rb'
diff --git a/.rubocop_todo/lint/non_atomic_file_operation.yml b/.rubocop_todo/lint/non_atomic_file_operation.yml
index cd700870448f68e5a147d6771cb4173e1e5c2591..d9d415de9231cc737e366a34adeec9ded6066ef3 100644
--- a/.rubocop_todo/lint/non_atomic_file_operation.yml
+++ b/.rubocop_todo/lint/non_atomic_file_operation.yml
@@ -41,5 +41,3 @@ Lint/NonAtomicFileOperation:
     - 'spec/services/bulk_imports/repository_bundle_export_service_spec.rb'
     - 'spec/services/bulk_imports/uploads_export_service_spec.rb'
     - 'spec/support/database/query_recorder.rb'
-    - 'spec/tooling/rspec_flaky/report_spec.rb'
-    - 'tooling/rspec_flaky/report.rb'
diff --git a/.rubocop_todo/lint/redundant_cop_disable_directive.yml b/.rubocop_todo/lint/redundant_cop_disable_directive.yml
index b35f876ed503676c8073b140bfaddcba36146806..7a07242ab29cf93aa985d243ab9251f700dc9c31 100644
--- a/.rubocop_todo/lint/redundant_cop_disable_directive.yml
+++ b/.rubocop_todo/lint/redundant_cop_disable_directive.yml
@@ -316,4 +316,3 @@ Lint/RedundantCopDisableDirective:
     - 'tooling/lib/tooling/helm3_client.rb'
     - 'tooling/lib/tooling/kubernetes_client.rb'
     - 'tooling/quality/test_level.rb'
-    - 'tooling/rspec_flaky/listener.rb'
diff --git a/.rubocop_todo/lint/unused_block_argument.yml b/.rubocop_todo/lint/unused_block_argument.yml
index 01510a8443d9b2ab7d9184fdc9cb47d2113ab12b..05e30a7f9add646e9212bf2006d1347fdf0da40b 100644
--- a/.rubocop_todo/lint/unused_block_argument.yml
+++ b/.rubocop_todo/lint/unused_block_argument.yml
@@ -426,6 +426,5 @@ Lint/UnusedBlockArgument:
     - 'spec/support/shared_examples/uploaders/object_storage_shared_examples.rb'
     - 'spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb'
     - 'spec/tooling/lib/tooling/find_codeowners_spec.rb'
-    - 'spec/tooling/rspec_flaky/config_spec.rb'
     - 'spec/workers/projects/git_garbage_collect_worker_spec.rb'
     - 'tooling/lib/tooling/find_codeowners.rb'
diff --git a/.rubocop_todo/rspec/expect_in_hook.yml b/.rubocop_todo/rspec/expect_in_hook.yml
index 3ae4d9ee7f4db491e937d88ba2e4e2e8977ff1ba..b0369d5180d28b3ec584827948b5cd7001db8223 100644
--- a/.rubocop_todo/rspec/expect_in_hook.yml
+++ b/.rubocop_todo/rspec/expect_in_hook.yml
@@ -466,7 +466,6 @@ RSpec/ExpectInHook:
     - 'spec/tasks/gitlab/praefect_rake_spec.rb'
     - 'spec/lib/gitlab/task_helpers_spec.rb'
     - 'spec/tooling/danger/feature_flag_spec.rb'
-    - 'spec/tooling/rspec_flaky/listener_spec.rb'
     - 'spec/uploaders/file_mover_spec.rb'
     - 'spec/uploaders/gitlab_uploader_spec.rb'
     - 'spec/uploaders/object_storage_spec.rb'
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index bc050a40a737f57362e2972d5680e6c864ea1564..806226149289daec857d71e424dc46f09889d123 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -5618,12 +5618,6 @@ RSpec/MissingFeatureCategory:
     - 'spec/tooling/lib/tooling/test_map_generator_spec.rb'
     - 'spec/tooling/lib/tooling/test_map_packer_spec.rb'
     - 'spec/tooling/merge_request_spec.rb'
-    - 'spec/tooling/rspec_flaky/config_spec.rb'
-    - 'spec/tooling/rspec_flaky/example_spec.rb'
-    - 'spec/tooling/rspec_flaky/flaky_example_spec.rb'
-    - 'spec/tooling/rspec_flaky/flaky_examples_collection_spec.rb'
-    - 'spec/tooling/rspec_flaky/listener_spec.rb'
-    - 'spec/tooling/rspec_flaky/report_spec.rb'
     - 'spec/uploaders/attachment_uploader_spec.rb'
     - 'spec/uploaders/avatar_uploader_spec.rb'
     - 'spec/uploaders/ci/pipeline_artifact_uploader_spec.rb'
diff --git a/.rubocop_todo/rspec/verified_doubles.yml b/.rubocop_todo/rspec/verified_doubles.yml
index 285dd769e251074185cf7dac264f384367d45565..0d66bf5604b5fd04d206f95e23f4e91e8b86c616 100644
--- a/.rubocop_todo/rspec/verified_doubles.yml
+++ b/.rubocop_todo/rspec/verified_doubles.yml
@@ -983,8 +983,6 @@ RSpec/VerifiedDoubles:
     - 'spec/tooling/danger/project_helper_spec.rb'
     - 'spec/tooling/lib/tooling/helm3_client_spec.rb'
     - 'spec/tooling/lib/tooling/kubernetes_client_spec.rb'
-    - 'spec/tooling/rspec_flaky/example_spec.rb'
-    - 'spec/tooling/rspec_flaky/listener_spec.rb'
     - 'spec/uploaders/file_uploader_spec.rb'
     - 'spec/uploaders/object_storage_spec.rb'
     - 'spec/uploaders/personal_file_uploader_spec.rb'
diff --git a/.rubocop_todo/style/guard_clause.yml b/.rubocop_todo/style/guard_clause.yml
index abd7fe7af98c1fb63058b9677686d338b3fa16c7..f00f5bed9d048c3abc00ef557dd9be9da4703b9a 100644
--- a/.rubocop_todo/style/guard_clause.yml
+++ b/.rubocop_todo/style/guard_clause.yml
@@ -640,5 +640,3 @@ Style/GuardClause:
     - 'tooling/lib/tooling/helm3_client.rb'
     - 'tooling/lib/tooling/image.rb'
     - 'tooling/lib/tooling/kubernetes_client.rb'
-    - 'tooling/rspec_flaky/flaky_example.rb'
-    - 'tooling/rspec_flaky/listener.rb'
diff --git a/.rubocop_todo/style/if_unless_modifier.yml b/.rubocop_todo/style/if_unless_modifier.yml
index 09707e6864836e9dbcbc9aa526acf85307d32419..a1f516a8abec71448e36020fcacea60d61b5e60c 100644
--- a/.rubocop_todo/style/if_unless_modifier.yml
+++ b/.rubocop_todo/style/if_unless_modifier.yml
@@ -1074,5 +1074,3 @@ Style/IfUnlessModifier:
     - 'tooling/lib/tooling/find_codeowners.rb'
     - 'tooling/lib/tooling/image.rb'
     - 'tooling/lib/tooling/test_map_packer.rb'
-    - 'tooling/rspec_flaky/flaky_example.rb'
-    - 'tooling/rspec_flaky/flaky_examples_collection.rb'
diff --git a/.rubocop_todo/style/redundant_interpolation.yml b/.rubocop_todo/style/redundant_interpolation.yml
index a618632f5525d0f838d40558ef2d3c504ec693ae..27cd954f506f748640f09169561f092bb6dbb8a7 100644
--- a/.rubocop_todo/style/redundant_interpolation.yml
+++ b/.rubocop_todo/style/redundant_interpolation.yml
@@ -35,4 +35,3 @@ Style/RedundantInterpolation:
     - 'qa/qa/tools/generate_perf_testdata.rb'
     - 'scripts/perf/gc/print_gc_stats.rb'
     - 'scripts/qa/testcases-check'
-    - 'tooling/rspec_flaky/flaky_example.rb'
diff --git a/.rubocop_todo/style/symbol_proc.yml b/.rubocop_todo/style/symbol_proc.yml
index 0e4602f9f317e18f156fd9470cb19f84f2c19170..67229a8b704ee766362b448bf1021ff90ea130a9 100644
--- a/.rubocop_todo/style/symbol_proc.yml
+++ b/.rubocop_todo/style/symbol_proc.yml
@@ -241,4 +241,3 @@ Style/SymbolProc:
     - 'spec/views/layouts/_published_experiments.html.haml_spec.rb'
     - 'spec/workers/snippets/schedule_bulk_repository_shard_moves_worker_spec.rb'
     - 'tooling/lib/tooling/test_map_generator.rb'
-    - 'tooling/rspec_flaky/flaky_examples_collection.rb'
diff --git a/Gemfile b/Gemfile
index 3a6ce1553f7fcf4cb2adce1e672b49d9e99831cd..125f8bf1fe3a820a97cf5ca2bff679c5b0c93cff 100644
--- a/Gemfile
+++ b/Gemfile
@@ -462,6 +462,7 @@ end
 # Gems required in various pipelines
 group :development, :test, :monorepo do
   gem 'gitlab-rspec', path: 'gems/gitlab-rspec'
+  gem 'rspec_flaky', path: 'gems/rspec_flaky'
 end
 
 group :test do
diff --git a/Gemfile.lock b/Gemfile.lock
index adbca941778c13dab16b7465f55bdff01e6fc28d..acfce81552ae69141df5f056cbb4dedd257d3066 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -41,6 +41,13 @@ PATH
       diffy (~> 3.4)
       oj (~> 3.13.16)
 
+PATH
+  remote: gems/rspec_flaky
+  specs:
+    rspec_flaky (0.1.0)
+      activesupport (>= 6.1, < 7.1)
+      rspec (~> 3.0)
+
 PATH
   remote: vendor/gems/attr_encrypted
   specs:
@@ -1961,6 +1968,7 @@ DEPENDENCIES
   rspec-parameterized (~> 1.0)
   rspec-rails (~> 6.0.1)
   rspec-retry (~> 0.6.2)
+  rspec_flaky!
   rspec_junit_formatter
   rspec_profiling (~> 0.0.6)
   rubocop
diff --git a/doc/development/gems.md b/doc/development/gems.md
index a10fb86e88338288e4b16bfae509afabf4e1dbf6..0779cc5daa38d6f8f9b2f250f619b49bc82be00b 100644
--- a/doc/development/gems.md
+++ b/doc/development/gems.md
@@ -80,20 +80,29 @@ You can see example adding a new gem: [!121676](https://gitlab.com/gitlab-org/gi
 1. Edit `gems/<name-of-gem>/<name-of-gem>.gemspec` and fill the details about the Gem as in the following example:
 
    ```ruby
+   # frozen_string_literal: true
+
+   require_relative "lib/name/of/gem/version"
+
    Gem::Specification.new do |spec|
      spec.name = "<name-of-gem>"
-     spec.version = Gitlab::NameOfGem::VERSION
+     spec.version = Name::Of::Gem::Version::VERSION
      spec.authors = ["group::tenant-scale"]
      spec.email = ["engineering@gitlab.com"]
 
-     spec.summary = "GitLab's RSpec extensions"
-     spec.description = "A set of useful helpers to configure RSpec with various stubs and CI configs."
+     spec.summary = "Gem summary"
+     spec.description = "A more descriptive text about what the gem is doing."
      spec.homepage = "https://gitlab.com/gitlab-org/gitlab/-/tree/master/gems/<name-of-gem>"
-     spec.required_ruby_version = ">= 2.7"
+     spec.license = "MIT"
+     spec.required_ruby_version = ">= 3.0"
+     spec.metadata["rubygems_mfa_required"] = "true"
+
+     spec.files = Dir['lib/**/*.rb']
+     spec.require_paths = ["lib"]
    end
    ```
 
-1. Update `gems/<name-of-gem>/.rubocop` with:
+1. Update `gems/<name-of-gem>/.rubocop.yml` with:
 
    ```yaml
    inherit_from:
diff --git a/doc/development/testing_guide/flaky_tests.md b/doc/development/testing_guide/flaky_tests.md
index d111021f4863aedaa9fb8856cbe722b9bc77e8d5..ad3ce0da88168e4f7b7dbdcf14231742cc14bfec 100644
--- a/doc/development/testing_guide/flaky_tests.md
+++ b/doc/development/testing_guide/flaky_tests.md
@@ -210,10 +210,10 @@ Once a test is in quarantine, there are 3 choices:
 
 ## Automatic retries and flaky tests detection
 
-On our CI, we use [RSpec::Retry](https://github.com/NoRedInk/rspec-retry) to automatically retry a failing example a few
+On our CI, we use [`RSpec::Retry`](https://github.com/NoRedInk/rspec-retry) to automatically retry a failing example a few
 times (see [`spec/spec_helper.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/spec_helper.rb) for the precise retries count).
 
-We also use a custom [`RspecFlaky::Listener`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/tooling/rspec_flaky/listener.rb).
+We also use a custom [`RspecFlaky::Listener`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/gems/rspec_flaky/lib/rspec_flaky/listener.rb).
 This listener runs in the `update-tests-metadata` job in `maintenance` scheduled pipelines
 on the `master` branch, and saves flaky examples to `rspec/flaky/report-suite.json`.
 The report file is then retrieved by the `retrieve-tests-metadata` job in all pipelines.
diff --git a/gems/activerecord-gitlab/.rubocop.yml b/gems/activerecord-gitlab/.rubocop.yml
index 8c99d7c47d18b8ea2f188ad9c7deef0161e3acb4..8c670b439d3eb02d2ce5f3847696991962de7614 100644
--- a/gems/activerecord-gitlab/.rubocop.yml
+++ b/gems/activerecord-gitlab/.rubocop.yml
@@ -1,6 +1,2 @@
 inherit_from:
   - ../config/rubocop.yml
-
-# FIXME
-Gitlab/RSpec/AvoidSetup:
-  Enabled: false
diff --git a/gems/config/rubocop.yml b/gems/config/rubocop.yml
index 5422348ebeff7815fb8bfa06d6fb22a5fcf160d1..f591cde534fa5f28ef0fa1a503614a90d8035bff 100644
--- a/gems/config/rubocop.yml
+++ b/gems/config/rubocop.yml
@@ -43,6 +43,10 @@ Gitlab/DocUrl:
 Gitlab/NamespacedClass:
   Enabled: false
 
+# This cop doesn't make sense in the context of gems
+Gitlab/RSpec/AvoidSetup:
+  Enabled: false
+
 # This cop doesn't make sense in the context of gems
 Graphql/AuthorizeTypes:
   Enabled: false
diff --git a/gems/gitlab-rspec/.rubocop.yml b/gems/gitlab-rspec/.rubocop.yml
index a2bdc5735c4d9ae2a8aea956777aa1fdc8d95b4b..38c0c592dad64319078ce50b7cac174b2c8ecb36 100644
--- a/gems/gitlab-rspec/.rubocop.yml
+++ b/gems/gitlab-rspec/.rubocop.yml
@@ -8,7 +8,3 @@ RSpec/InstanceVariable:
 Gitlab/ChangeTimezone:
   Exclude:
     - spec/gitlab/rspec/time_travel_spec.rb
-
-# FIXME
-Gitlab/RSpec/AvoidSetup:
-  Enabled: false
diff --git a/gems/gitlab-utils/.rubocop.yml b/gems/gitlab-utils/.rubocop.yml
index caaa0787be2dd89aebf2c2e60088ed14381118b0..eeafd850c9bb25552f7c72c86c0e68393f2d36c3 100644
--- a/gems/gitlab-utils/.rubocop.yml
+++ b/gems/gitlab-utils/.rubocop.yml
@@ -14,10 +14,6 @@ Lint/BinaryOperatorWithIdenticalOperands:
 Style/NumericPredicate:
   EnforcedStyle: comparison
 
-# FIXME
-Gitlab/RSpec/AvoidSetup:
-  Enabled: false
-
 # FIXME: When enabled, there's a spec failure in ee/spec/requests/api/graphql/mutations/merge_requests/update_approval_rule_spec.rb:51,
 # due to the `default_value` of `remove_hidden_groups` set to `[]`, most probably instead of `false`, in ee/app/graphql/mutations/merge_requests/update_approval_rule.rb.
 # The problem is that `Object#=~` exists (even though it's deprecated), hence calling it on an `Array` doesn't blow up, but `Array#match?` doesn't exist.
diff --git a/gems/gitlab-utils/Gemfile.lock b/gems/gitlab-utils/Gemfile.lock
index 2a571c95517150b9d9787b76d73634838c40373e..971d90ce146d016eb4f539aab40ed541c17e9626 100644
--- a/gems/gitlab-utils/Gemfile.lock
+++ b/gems/gitlab-utils/Gemfile.lock
@@ -2,6 +2,7 @@ PATH
   remote: ../gitlab-rspec
   specs:
     gitlab-rspec (0.1.0)
+      activesupport (>= 6.1, < 7.1)
       rspec (~> 3.0)
 
 PATH
diff --git a/gems/rspec_flaky/.gitignore b/gems/rspec_flaky/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b04a8c840df1a534cfd67449e31919721b410986
--- /dev/null
+++ b/gems/rspec_flaky/.gitignore
@@ -0,0 +1,11 @@
+/.bundle/
+/.yardoc
+/_yardoc/
+/coverage/
+/doc/
+/pkg/
+/spec/reports/
+/tmp/
+
+# rspec failure tracking
+.rspec_status
diff --git a/gems/rspec_flaky/.gitlab-ci.yml b/gems/rspec_flaky/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..41fac86e7a52e0a93de7e1f8125e0991f77232cc
--- /dev/null
+++ b/gems/rspec_flaky/.gitlab-ci.yml
@@ -0,0 +1,4 @@
+include:
+  - local: gems/gem.gitlab-ci.yml
+    inputs:
+      gem_name: "rspec_flaky"
diff --git a/gems/rspec_flaky/.rspec b/gems/rspec_flaky/.rspec
new file mode 100644
index 0000000000000000000000000000000000000000..34c5164d9b56c7d528f061c97f2d2fe02c834bdd
--- /dev/null
+++ b/gems/rspec_flaky/.rspec
@@ -0,0 +1,3 @@
+--format documentation
+--color
+--require spec_helper
diff --git a/gems/rspec_flaky/.rubocop.yml b/gems/rspec_flaky/.rubocop.yml
new file mode 100644
index 0000000000000000000000000000000000000000..62cb8a982c5d4b52d763ab36118a64fa6f76412f
--- /dev/null
+++ b/gems/rspec_flaky/.rubocop.yml
@@ -0,0 +1,13 @@
+inherit_from:
+  - ../config/rubocop.yml
+
+# FIXME once Gitlab::Json is in a gem
+Gitlab/Json:
+  Enabled: false
+
+# FIXME
+RSpec/MultipleMemoizedHelpers:
+  Enabled: false
+
+RSpec/VerifiedDoubles:
+  Enabled: false
diff --git a/gems/rspec_flaky/Gemfile b/gems/rspec_flaky/Gemfile
new file mode 100644
index 0000000000000000000000000000000000000000..90bf29fb6478586484484d5ab46256b9c26228ee
--- /dev/null
+++ b/gems/rspec_flaky/Gemfile
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+# Specify your gem's dependencies in rspec_flaky.gemspec
+gemspec
+
+gem "gitlab-rspec", "~> 0.1", path: "../gitlab-rspec"
diff --git a/gems/rspec_flaky/Gemfile.lock b/gems/rspec_flaky/Gemfile.lock
new file mode 100644
index 0000000000000000000000000000000000000000..547dc24e3756a95105b73a3581c349799c7cf3b3
--- /dev/null
+++ b/gems/rspec_flaky/Gemfile.lock
@@ -0,0 +1,126 @@
+PATH
+  remote: ../gitlab-rspec
+  specs:
+    gitlab-rspec (0.1.0)
+      activesupport (>= 6.1, < 7.1)
+      rspec (~> 3.0)
+
+PATH
+  remote: .
+  specs:
+    rspec_flaky (0.1.0)
+      activesupport (>= 6.1, < 7.1)
+      rspec (~> 3.0)
+
+GEM
+  remote: https://rubygems.org/
+  specs:
+    activesupport (7.0.6)
+      concurrent-ruby (~> 1.0, >= 1.0.2)
+      i18n (>= 1.6, < 2)
+      minitest (>= 5.1)
+      tzinfo (~> 2.0)
+    ast (2.4.2)
+    binding_of_caller (1.0.0)
+      debug_inspector (>= 0.0.1)
+    coderay (1.1.3)
+    concurrent-ruby (1.2.2)
+    debug_inspector (1.1.0)
+    diff-lcs (1.5.0)
+    gitlab-styles (10.1.0)
+      rubocop (~> 1.50.2)
+      rubocop-graphql (~> 0.18)
+      rubocop-performance (~> 1.15)
+      rubocop-rails (~> 2.17)
+      rubocop-rspec (~> 2.22)
+    i18n (1.14.1)
+      concurrent-ruby (~> 1.0)
+    json (2.6.3)
+    minitest (5.18.1)
+    parallel (1.23.0)
+    parser (3.2.2.3)
+      ast (~> 2.4.1)
+      racc
+    proc_to_ast (0.1.0)
+      coderay
+      parser
+      unparser
+    racc (1.7.1)
+    rack (3.0.8)
+    rainbow (3.1.1)
+    regexp_parser (2.8.1)
+    rexml (3.2.5)
+    rspec (3.12.0)
+      rspec-core (~> 3.12.0)
+      rspec-expectations (~> 3.12.0)
+      rspec-mocks (~> 3.12.0)
+    rspec-core (3.12.2)
+      rspec-support (~> 3.12.0)
+    rspec-expectations (3.12.3)
+      diff-lcs (>= 1.2.0, < 2.0)
+      rspec-support (~> 3.12.0)
+    rspec-mocks (3.12.5)
+      diff-lcs (>= 1.2.0, < 2.0)
+      rspec-support (~> 3.12.0)
+    rspec-parameterized (1.0.0)
+      rspec-parameterized-core (< 2)
+      rspec-parameterized-table_syntax (< 2)
+    rspec-parameterized-core (1.0.0)
+      parser
+      proc_to_ast
+      rspec (>= 2.13, < 4)
+      unparser
+    rspec-parameterized-table_syntax (1.0.0)
+      binding_of_caller
+      rspec-parameterized-core (< 2)
+    rspec-support (3.12.1)
+    rubocop (1.50.2)
+      json (~> 2.3)
+      parallel (~> 1.10)
+      parser (>= 3.2.0.0)
+      rainbow (>= 2.2.2, < 4.0)
+      regexp_parser (>= 1.8, < 3.0)
+      rexml (>= 3.2.5, < 4.0)
+      rubocop-ast (>= 1.28.0, < 2.0)
+      ruby-progressbar (~> 1.7)
+      unicode-display_width (>= 2.4.0, < 3.0)
+    rubocop-ast (1.29.0)
+      parser (>= 3.2.1.0)
+    rubocop-capybara (2.18.0)
+      rubocop (~> 1.41)
+    rubocop-factory_bot (2.23.1)
+      rubocop (~> 1.33)
+    rubocop-graphql (0.19.0)
+      rubocop (>= 0.87, < 2)
+    rubocop-performance (1.18.0)
+      rubocop (>= 1.7.0, < 2.0)
+      rubocop-ast (>= 0.4.0)
+    rubocop-rails (2.20.2)
+      activesupport (>= 4.2.0)
+      rack (>= 1.1)
+      rubocop (>= 1.33.0, < 2.0)
+    rubocop-rspec (2.22.0)
+      rubocop (~> 1.33)
+      rubocop-capybara (~> 2.17)
+      rubocop-factory_bot (~> 2.22)
+    ruby-progressbar (1.13.0)
+    tzinfo (2.0.6)
+      concurrent-ruby (~> 1.0)
+    unicode-display_width (2.4.2)
+    unparser (0.6.8)
+      diff-lcs (~> 1.3)
+      parser (>= 3.2.0)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  gitlab-rspec (~> 0.1)!
+  gitlab-styles (~> 10.1.0)
+  rspec-parameterized (~> 1.0)
+  rspec_flaky!
+  rubocop (~> 1.50)
+  rubocop-rspec (~> 2.22)
+
+BUNDLED WITH
+   2.4.14
diff --git a/gems/rspec_flaky/lib/rspec_flaky.rb b/gems/rspec_flaky/lib/rspec_flaky.rb
new file mode 100644
index 0000000000000000000000000000000000000000..90fc6b1dc490bbda65a16114529a4cb0163e4ef5
--- /dev/null
+++ b/gems/rspec_flaky/lib/rspec_flaky.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+require "rspec"
+require_relative "rspec_flaky/config"
+require_relative "rspec_flaky/listener"
+require_relative "rspec_flaky/version"
diff --git a/gems/rspec_flaky/lib/rspec_flaky/config.rb b/gems/rspec_flaky/lib/rspec_flaky/config.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ca57d1e08be3ea7d5ed233474fe596a8d044753e
--- /dev/null
+++ b/gems/rspec_flaky/lib/rspec_flaky/config.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module RspecFlaky
+  class Config
+    def self.generate_report?
+      !!(ENV['FLAKY_RSPEC_GENERATE_REPORT'] =~ /1|true/)
+    end
+
+    def self.suite_flaky_examples_report_path
+      ENV['FLAKY_RSPEC_SUITE_REPORT_PATH'] || "rspec/flaky/suite-report.json"
+    end
+
+    def self.flaky_examples_report_path
+      ENV['FLAKY_RSPEC_REPORT_PATH'] || "rspec/flaky/report.json"
+    end
+
+    def self.new_flaky_examples_report_path
+      ENV['NEW_FLAKY_RSPEC_REPORT_PATH'] || "rspec/flaky/new-report.json"
+    end
+  end
+end
diff --git a/tooling/rspec_flaky/example.rb b/gems/rspec_flaky/lib/rspec_flaky/example.rb
similarity index 92%
rename from tooling/rspec_flaky/example.rb
rename to gems/rspec_flaky/lib/rspec_flaky/example.rb
index 8f369c99c5b14d10dd9d828e4f43e51d85bf09a4..4a128a151dc3224ecb25f6b156057f03ac5d0f59 100644
--- a/tooling/rspec_flaky/example.rb
+++ b/gems/rspec_flaky/lib/rspec_flaky/example.rb
@@ -15,7 +15,7 @@ def initialize(rspec_example)
     end
 
     def uid
-      @uid ||= Digest::MD5.hexdigest("#{description}-#{file}")
+      @uid ||= Digest::MD5.hexdigest("#{description}-#{file}") # rubocop:disable Fips/MD5
     end
 
     def example_id
diff --git a/tooling/rspec_flaky/flaky_example.rb b/gems/rspec_flaky/lib/rspec_flaky/flaky_example.rb
similarity index 79%
rename from tooling/rspec_flaky/flaky_example.rb
rename to gems/rspec_flaky/lib/rspec_flaky/flaky_example.rb
index 3ce48ce1cd3ffa22106f5d39a2c1536f77b1dc80..35d1f34d2a28185ae812460690d9bc267a4cc64f 100644
--- a/tooling/rspec_flaky/flaky_example.rb
+++ b/gems/rspec_flaky/lib/rspec_flaky/flaky_example.rb
@@ -3,21 +3,21 @@
 require 'ostruct'
 
 module RspecFlaky
-  ALLOWED_ATTRIBUTES = %i[
-    example_id
-    file
-    line
-    description
-    first_flaky_at
-    last_flaky_at
-    last_flaky_job
-    last_attempts_count
-    flaky_reports
-    feature_category
-  ].freeze
-
   # This represents a flaky RSpec example and is mainly meant to be saved in a JSON file
   class FlakyExample
+    ALLOWED_ATTRIBUTES = %i[
+      example_id
+      file
+      line
+      description
+      first_flaky_at
+      last_flaky_at
+      last_flaky_job
+      last_attempts_count
+      flaky_reports
+      feature_category
+    ].freeze
+
     def initialize(example_hash)
       @attributes = {
         first_flaky_at: Time.now,
@@ -43,9 +43,9 @@ def update!(example_hash)
       attributes[:feature_category] = example_hash[:feature_category]
       attributes[:last_attempts_count] = example_hash[:last_attempts_count] if example_hash[:last_attempts_count]
 
-      if ENV['CI_JOB_URL']
-        attributes[:last_flaky_job] = "#{ENV['CI_JOB_URL']}"
-      end
+      return unless ENV['CI_JOB_URL']
+
+      attributes[:last_flaky_job] = (ENV['CI_JOB_URL']).to_s
     end
 
     def to_h
diff --git a/tooling/rspec_flaky/flaky_examples_collection.rb b/gems/rspec_flaky/lib/rspec_flaky/flaky_examples_collection.rb
similarity index 57%
rename from tooling/rspec_flaky/flaky_examples_collection.rb
rename to gems/rspec_flaky/lib/rspec_flaky/flaky_examples_collection.rb
index 019ebf703da20ecc7e0d6ae1fddbeb63eed267b4..f03fe63d11b509efca48d9d96b6ef301dea29408 100644
--- a/tooling/rspec_flaky/flaky_examples_collection.rb
+++ b/gems/rspec_flaky/lib/rspec_flaky/flaky_examples_collection.rb
@@ -8,15 +8,13 @@
 module RspecFlaky
   class FlakyExamplesCollection < SimpleDelegator
     def initialize(collection = {})
-      unless collection.is_a?(Hash)
-        raise ArgumentError, "`collection` must be a Hash, #{collection.class} given!"
-      end
+      raise ArgumentError, "`collection` must be a Hash, #{collection.class} given!" unless collection.is_a?(Hash)
 
       collection_of_flaky_examples =
         collection.map do |uid, example|
           [
             uid,
-            RspecFlaky::FlakyExample.new(example.to_h.symbolize_keys)
+            FlakyExample.new(example.to_h.symbolize_keys)
           ]
         end
 
@@ -24,13 +22,11 @@ def initialize(collection = {})
     end
 
     def to_h
-      transform_values { |example| example.to_h }.deep_symbolize_keys
+      transform_values(&:to_h).deep_symbolize_keys
     end
 
     def -(other)
-      unless other.respond_to?(:key)
-        raise ArgumentError, "`other` must respond to `#key?`, #{other.class} does not!"
-      end
+      raise ArgumentError, "`other` must respond to `#key?`, #{other.class} does not!" unless other.respond_to?(:key)
 
       self.class.new(reject { |uid, _| other.key?(uid) })
     end
diff --git a/tooling/rspec_flaky/listener.rb b/gems/rspec_flaky/lib/rspec_flaky/listener.rb
similarity index 56%
rename from tooling/rspec_flaky/listener.rb
rename to gems/rspec_flaky/lib/rspec_flaky/listener.rb
index 3431d814a8a4e8b9a6f28c678a8c4fabdce8997a..c2deb1c327a64766e0cc62d36c3ad16427b85489 100644
--- a/tooling/rspec_flaky/listener.rb
+++ b/gems/rspec_flaky/lib/rspec_flaky/listener.rb
@@ -17,32 +17,32 @@ class Listener
     attr_reader :suite_flaky_examples, :flaky_examples
 
     def initialize(suite_flaky_examples_json = nil)
-      @flaky_examples = RspecFlaky::FlakyExamplesCollection.new
+      @flaky_examples = FlakyExamplesCollection.new
       @suite_flaky_examples = init_suite_flaky_examples(suite_flaky_examples_json)
     end
 
     def example_passed(notification)
-      current_example = RspecFlaky::Example.new(notification.example)
+      current_example = Example.new(notification.example)
 
       return unless current_example.attempts > 1
 
-      flaky_example = suite_flaky_examples.fetch(current_example.uid) { RspecFlaky::FlakyExample.new(current_example.to_h) }
+      flaky_example = suite_flaky_examples.fetch(current_example.uid) do
+        FlakyExample.new(current_example.to_h)
+      end
       flaky_example.update!(current_example.to_h)
 
       flaky_examples[current_example.uid] = flaky_example
     end
 
     def dump_summary(_)
-      RspecFlaky::Report.new(flaky_examples).write(RspecFlaky::Config.flaky_examples_report_path)
-      # write_report_file(flaky_examples, RspecFlaky::Config.flaky_examples_report_path)
+      Report.new(flaky_examples).write(Config.flaky_examples_report_path)
 
-      if new_flaky_examples.any?
-        rails_logger_warn("\nNew flaky examples detected:\n")
-        rails_logger_warn(JSON.pretty_generate(new_flaky_examples.to_h)) # rubocop:disable Gitlab/Json
+      return unless new_flaky_examples.any?
 
-        RspecFlaky::Report.new(new_flaky_examples).write(RspecFlaky::Config.new_flaky_examples_report_path)
-        # write_report_file(new_flaky_examples, RspecFlaky::Config.new_flaky_examples_report_path)
-      end
+      rails_logger_warn("\nNew flaky examples detected:\n")
+      rails_logger_warn(JSON.pretty_generate(new_flaky_examples.to_h))
+
+      Report.new(new_flaky_examples).write(Config.new_flaky_examples_report_path)
     end
 
     private
@@ -53,11 +53,11 @@ def new_flaky_examples
 
     def init_suite_flaky_examples(suite_flaky_examples_json = nil)
       if suite_flaky_examples_json
-        RspecFlaky::Report.load_json(suite_flaky_examples_json).flaky_examples
+        Report.load_json(suite_flaky_examples_json).flaky_examples
       else
-        return {} unless File.exist?(RspecFlaky::Config.suite_flaky_examples_report_path)
+        return {} unless File.exist?(Config.suite_flaky_examples_report_path)
 
-        RspecFlaky::Report.load(RspecFlaky::Config.suite_flaky_examples_report_path).flaky_examples
+        Report.load(Config.suite_flaky_examples_report_path).flaky_examples
       end
     end
 
diff --git a/tooling/rspec_flaky/report.rb b/gems/rspec_flaky/lib/rspec_flaky/report.rb
similarity index 72%
rename from tooling/rspec_flaky/report.rb
rename to gems/rspec_flaky/lib/rspec_flaky/report.rb
index 17dbb277446a52ef7eddab253a4f38d074b1be0b..3c8e1c933e1199b2a422924e93c37fa44fe4edba 100644
--- a/tooling/rspec_flaky/report.rb
+++ b/gems/rspec_flaky/lib/rspec_flaky/report.rb
@@ -19,12 +19,13 @@ def self.load(file_path)
     end
 
     def self.load_json(json)
-      new(RspecFlaky::FlakyExamplesCollection.new(JSON.parse(json)))
+      new(FlakyExamplesCollection.new(JSON.parse(json)))
     end
 
     def initialize(flaky_examples)
-      unless flaky_examples.is_a?(RspecFlaky::FlakyExamplesCollection)
-        raise ArgumentError, "`flaky_examples` must be a RspecFlaky::FlakyExamplesCollection, #{flaky_examples.class} given!"
+      unless flaky_examples.is_a?(FlakyExamplesCollection)
+        raise ArgumentError,
+          "`flaky_examples` must be a RspecFlaky::FlakyExamplesCollection, #{flaky_examples.class} given!"
       end
 
       @flaky_examples = flaky_examples
@@ -32,13 +33,13 @@ def initialize(flaky_examples)
     end
 
     def write(file_path)
-      unless RspecFlaky::Config.generate_report?
+      unless Config.generate_report?
         Kernel.warn "! Generating reports is disabled. To enable it, please set the `FLAKY_RSPEC_GENERATE_REPORT=1` !"
         return
       end
 
       report_path_dir = File.dirname(file_path)
-      FileUtils.mkdir_p(report_path_dir) unless Dir.exist?(report_path_dir)
+      FileUtils.mkdir_p(report_path_dir)
 
       File.write(file_path, JSON.pretty_generate(flaky_examples.to_h))
     end
@@ -51,7 +52,7 @@ def prune_outdated(days: OUTDATED_DAYS_THRESHOLD)
           last_flaky_at && last_flaky_at.to_i < outdated_date_threshold.to_i
         end
 
-      self.class.new(RspecFlaky::FlakyExamplesCollection.new(recent_flaky_examples))
+      self.class.new(FlakyExamplesCollection.new(recent_flaky_examples))
     end
   end
 end
diff --git a/gems/rspec_flaky/lib/rspec_flaky/version.rb b/gems/rspec_flaky/lib/rspec_flaky/version.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ec507d734c873e78ca2342c820ac7b5a49a4a0fa
--- /dev/null
+++ b/gems/rspec_flaky/lib/rspec_flaky/version.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module RspecFlaky
+  module Version
+    VERSION = "0.1.0"
+  end
+end
diff --git a/gems/rspec_flaky/rspec_flaky.gemspec b/gems/rspec_flaky/rspec_flaky.gemspec
new file mode 100644
index 0000000000000000000000000000000000000000..5c0a434218f1d8f06cd49cef89f2135bd5431755
--- /dev/null
+++ b/gems/rspec_flaky/rspec_flaky.gemspec
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require_relative "lib/rspec_flaky/version"
+
+Gem::Specification.new do |spec|
+  spec.name = "rspec_flaky"
+  spec.version = RspecFlaky::Version::VERSION
+  spec.authors = ["Engineering Productivity"]
+  spec.email = ["quality@gitlab.com"]
+
+  spec.summary = "GitLab's RSpec Flaky test detector"
+  spec.description =
+    "This gem provide an RSpec listener that allows to detect flaky examples. See " \
+    "https://docs.gitlab.com/ee/development/testing_guide/flaky_tests.html#automatic-retries-and-flaky-tests-detection."
+  spec.homepage = "https://gitlab.com/gitlab-org/gitlab/-/tree/master/gems/rspec_flaky"
+  spec.license = "MIT"
+  spec.required_ruby_version = ">= 3.0"
+  spec.metadata["rubygems_mfa_required"] = "true"
+
+  spec.files = Dir["lib/**/*.rb"]
+  spec.require_paths = ["lib"]
+
+  spec.add_runtime_dependency "activesupport", ">= 6.1", "< 7.1"
+  spec.add_runtime_dependency "rspec", "~> 3.0"
+
+  spec.add_development_dependency "gitlab-styles", "~> 10.1.0"
+  spec.add_development_dependency "rspec-parameterized", "~> 1.0"
+  spec.add_development_dependency "rubocop", "~> 1.50"
+  spec.add_development_dependency "rubocop-rspec", "~> 2.22"
+end
diff --git a/spec/tooling/rspec_flaky/config_spec.rb b/gems/rspec_flaky/spec/rspec_flaky/config_spec.rb
similarity index 88%
rename from spec/tooling/rspec_flaky/config_spec.rb
rename to gems/rspec_flaky/spec/rspec_flaky/config_spec.rb
index 8ba4dd1640e9071a44d84c62643f54123f8b4d02..827249efefa027488f25e2341e9242644e1dd609 100644
--- a/spec/tooling/rspec_flaky/config_spec.rb
+++ b/gems/rspec_flaky/spec/rspec_flaky/config_spec.rb
@@ -1,9 +1,6 @@
 # frozen_string_literal: true
 
-require 'rspec-parameterized'
-require 'gitlab/rspec/all'
-
-require_relative '../../../tooling/rspec_flaky/config'
+require 'rspec_flaky/config'
 
 RSpec.describe RspecFlaky::Config, :aggregate_failures do
   include StubENV
@@ -14,10 +11,6 @@
     stub_env('FLAKY_RSPEC_SUITE_REPORT_PATH', nil)
     stub_env('FLAKY_RSPEC_REPORT_PATH', nil)
     stub_env('NEW_FLAKY_RSPEC_REPORT_PATH', nil)
-    # Ensure the behavior is the same locally and on CI (where Rails is defined since we run this test as part of the whole suite), i.e. Rails isn't defined
-    allow(described_class).to receive(:rails_path).and_wrap_original do |method, path|
-      path
-    end
   end
 
   describe '.generate_report?' do
diff --git a/spec/tooling/rspec_flaky/example_spec.rb b/gems/rspec_flaky/spec/rspec_flaky/example_spec.rb
similarity index 96%
rename from spec/tooling/rspec_flaky/example_spec.rb
rename to gems/rspec_flaky/spec/rspec_flaky/example_spec.rb
index d001ed324440f233ca8067dfc34d4847bfd637e0..64d65c0e1705ca4d6bf55eac51ccd0ba374c2d10 100644
--- a/spec/tooling/rspec_flaky/example_spec.rb
+++ b/gems/rspec_flaky/spec/rspec_flaky/example_spec.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require_relative '../../../tooling/rspec_flaky/example'
+require 'rspec_flaky/example'
 
 RSpec.describe RspecFlaky::Example do
   let(:example_attrs) do
@@ -45,7 +45,7 @@
 
   describe '#uid' do
     it 'returns a hash of the full description' do
-      expect(subject.uid).to eq(Digest::MD5.hexdigest("#{subject.description}-#{subject.file}"))
+      expect(subject.uid).to eq(Digest::MD5.hexdigest("#{subject.description}-#{subject.file}")) # rubocop:disable Fips/MD5
     end
   end
 
diff --git a/spec/tooling/rspec_flaky/flaky_example_spec.rb b/gems/rspec_flaky/spec/rspec_flaky/flaky_example_spec.rb
similarity index 95%
rename from spec/tooling/rspec_flaky/flaky_example_spec.rb
rename to gems/rspec_flaky/spec/rspec_flaky/flaky_example_spec.rb
index c100756d7342665586ba11522840ccfd9b0adefe..9a26d11d30cf1c5e234625cc919b97e185833e63 100644
--- a/spec/tooling/rspec_flaky/flaky_example_spec.rb
+++ b/gems/rspec_flaky/spec/rspec_flaky/flaky_example_spec.rb
@@ -1,11 +1,8 @@
 # frozen_string_literal: true
 
-require 'gitlab/rspec/all'
-
-require_relative '../../../tooling/rspec_flaky/flaky_example'
+require 'rspec_flaky/flaky_example'
 
 RSpec.describe RspecFlaky::FlakyExample, :aggregate_failures do
-  include ActiveSupport::Testing::TimeHelpers
   include StubENV
 
   let(:example_attrs) do
@@ -87,7 +84,7 @@
       end
 
       it 'updates the flaky_reports' do
-        expected_flaky_reports = flaky_example.to_h[:first_flaky_at] ? flaky_example.to_h[:flaky_reports] + 1 : 1
+        expected_flaky_reports = flaky_example.to_h[:first_flaky_at] ? flaky_example.to_h[:flaky_reports] + 1 : 1 # rubocop:disable RSpec/AvoidConditionalStatements
 
         expect { flaky_example.update!(example_attrs) }.to change { flaky_example.to_h[:flaky_reports] }.by(1)
         expect(flaky_example.to_h[:flaky_reports]).to eq(expected_flaky_reports)
diff --git a/spec/tooling/rspec_flaky/flaky_examples_collection_spec.rb b/gems/rspec_flaky/spec/rspec_flaky/flaky_examples_collection_spec.rb
similarity index 85%
rename from spec/tooling/rspec_flaky/flaky_examples_collection_spec.rb
rename to gems/rspec_flaky/spec/rspec_flaky/flaky_examples_collection_spec.rb
index 8304bcb426ea7ef10ee306ecbc4c6482cf413daa..260ebc7219248608bd00a765efd89b4756d0b4d0 100644
--- a/spec/tooling/rspec_flaky/flaky_examples_collection_spec.rb
+++ b/gems/rspec_flaky/spec/rspec_flaky/flaky_examples_collection_spec.rb
@@ -1,8 +1,6 @@
 # frozen_string_literal: true
 
-require 'gitlab/rspec/all'
-
-require_relative '../../../tooling/rspec_flaky/flaky_examples_collection'
+require 'rspec_flaky/flaky_examples_collection'
 
 RSpec.describe RspecFlaky::FlakyExamplesCollection, :aggregate_failures, :freeze_time do
   let(:collection_hash) do
@@ -45,7 +43,9 @@
     end
 
     it 'does not accept anything else' do
-      expect { described_class.new([1, 2, 3]) }.to raise_error(ArgumentError, "`collection` must be a Hash, Array given!")
+      expect do
+        described_class.new([1, 2, 3])
+      end.to raise_error(ArgumentError, "`collection` must be a Hash, Array given!")
     end
   end
 
@@ -79,7 +79,9 @@
     it 'fails if the given collection does not respond to `#key?`' do
       collection = described_class.new(collection_hash)
 
-      expect { collection - [1, 2, 3] }.to raise_error(ArgumentError, "`other` must respond to `#key?`, Array does not!")
+      expect do
+        collection - [1, 2, 3]
+      end.to raise_error(ArgumentError, "`other` must respond to `#key?`, Array does not!")
     end
   end
 end
diff --git a/spec/tooling/rspec_flaky/listener_spec.rb b/gems/rspec_flaky/spec/rspec_flaky/listener_spec.rb
similarity index 90%
rename from spec/tooling/rspec_flaky/listener_spec.rb
rename to gems/rspec_flaky/spec/rspec_flaky/listener_spec.rb
index d1ed84dad76415e3a47cd72cea67178988af16ca..cbc5422a76329b5731cb0f156b2b712186373b55 100644
--- a/spec/tooling/rspec_flaky/listener_spec.rb
+++ b/gems/rspec_flaky/spec/rspec_flaky/listener_spec.rb
@@ -1,11 +1,8 @@
 # frozen_string_literal: true
 
-require 'gitlab/rspec/all'
-
-require_relative '../../../tooling/rspec_flaky/listener'
+require 'rspec_flaky/listener'
 
 RSpec.describe RspecFlaky::Listener, :aggregate_failures do
-  include ActiveSupport::Testing::TimeHelpers
   include StubENV
 
   let(:already_flaky_example_uid) { '6e869794f4cfd2badd93eb68719371d1' }
@@ -37,7 +34,10 @@
     }
   end
 
-  let(:already_flaky_example) { RspecFlaky::FlakyExample.new(suite_flaky_example_report[already_flaky_example_uid]) }
+  let(:already_flaky_example) do
+    RspecFlaky::FlakyExample.new(suite_flaky_example_report[already_flaky_example_uid])
+  end
+
   let(:new_example_attrs) do
     {
       id: 'spec/foo/baz_spec.rb:3',
@@ -79,22 +79,23 @@
         stub_env('FLAKY_RSPEC_SUITE_REPORT_PATH', report_file_path)
       end
 
-      context 'and report file exists' do
+      context 'when report file exists' do
         before do
-          expect(File).to receive(:exist?).with(report_file_path).and_return(true)
+          allow(File).to receive(:exist?).with(report_file_path).and_return(true)
         end
 
         it 'delegates the load to RspecFlaky::Report' do
-          report = RspecFlaky::Report.new(RspecFlaky::FlakyExamplesCollection.new(suite_flaky_example_report))
+          report = RspecFlaky::Report
+            .new(RspecFlaky::FlakyExamplesCollection.new(suite_flaky_example_report))
 
           expect(RspecFlaky::Report).to receive(:load).with(report_file_path).and_return(report)
           expect(described_class.new.suite_flaky_examples.to_h).to eq(report.flaky_examples.to_h)
         end
       end
 
-      context 'and report file does not exist' do
+      context 'when report file does not exist' do
         before do
-          expect(File).to receive(:exist?).with(report_file_path).and_return(false)
+          allow(File).to receive(:exist?).with(report_file_path).and_return(false)
         end
 
         it 'return an empty hash' do
@@ -217,7 +218,8 @@
         expect(RspecFlaky::Report).to receive(:new).with(listener.flaky_examples).and_return(report1)
         expect(report1).to receive(:write).with(RspecFlaky::Config.flaky_examples_report_path)
 
-        expect(RspecFlaky::Report).to receive(:new).with(listener.__send__(:new_flaky_examples)).and_return(report2)
+        expect(RspecFlaky::Report)
+          .to receive(:new).with(listener.__send__(:new_flaky_examples)).and_return(report2)
         expect(report2).to receive(:write).with(RspecFlaky::Config.new_flaky_examples_report_path)
 
         listener.dump_summary(nil)
diff --git a/spec/tooling/rspec_flaky/report_spec.rb b/gems/rspec_flaky/spec/rspec_flaky/report_spec.rb
similarity index 84%
rename from spec/tooling/rspec_flaky/report_spec.rb
rename to gems/rspec_flaky/spec/rspec_flaky/report_spec.rb
index 543a69cd8ee1379f476eb0e4e93edfa4dd7b2ff9..e1e9bd6a7b1819efc25550035a1d338e088f92ae 100644
--- a/spec/tooling/rspec_flaky/report_spec.rb
+++ b/gems/rspec_flaky/spec/rspec_flaky/report_spec.rb
@@ -1,16 +1,16 @@
 # frozen_string_literal: true
 
 require 'tempfile'
-require 'gitlab/rspec/all'
 
-require_relative '../../../tooling/rspec_flaky/report'
+require 'rspec_flaky/report'
 
 RSpec.describe RspecFlaky::Report, :aggregate_failures, :freeze_time do
   let(:thirty_one_days) { 3600 * 24 * 31 }
   let(:collection_hash) do
     {
       a: { example_id: 'spec/foo/bar_spec.rb:2' },
-      b: { example_id: 'spec/foo/baz_spec.rb:3', first_flaky_at: (Time.now - thirty_one_days).to_s, last_flaky_at: (Time.now - thirty_one_days).to_s }
+      b: { example_id: 'spec/foo/baz_spec.rb:3', first_flaky_at: (Time.now - thirty_one_days).to_s,
+           last_flaky_at: (Time.now - thirty_one_days).to_s }
     }
   end
 
@@ -41,7 +41,7 @@
   describe '.load' do
     let!(:report_file) do
       Tempfile.new(%w[rspec_flaky_report .json]).tap do |f|
-        f.write(JSON.pretty_generate(suite_flaky_example_report)) # rubocop:disable Gitlab/Json
+        f.write(JSON.pretty_generate(suite_flaky_example_report))
         f.rewind
       end
     end
@@ -58,7 +58,7 @@
 
   describe '.load_json' do
     let(:report_json) do
-      JSON.pretty_generate(suite_flaky_example_report) # rubocop:disable Gitlab/Json
+      JSON.pretty_generate(suite_flaky_example_report)
     end
 
     it 'loads the report file' do
@@ -72,7 +72,11 @@
     end
 
     it 'does not accept anything else' do
-      expect { described_class.new([1, 2, 3]) }.to raise_error(ArgumentError, "`flaky_examples` must be a RspecFlaky::FlakyExamplesCollection, Array given!")
+      expect do
+        described_class.new([1, 2,
+          3])
+      end.to raise_error(ArgumentError,
+        "`flaky_examples` must be a RspecFlaky::FlakyExamplesCollection, Array given!")
     end
   end
 
@@ -84,11 +88,11 @@
     let(:report_file_path) { File.join('tmp', 'rspec_flaky_report.json') }
 
     before do
-      FileUtils.rm(report_file_path) if File.exist?(report_file_path)
+      FileUtils.rm_f(report_file_path)
     end
 
     after do
-      FileUtils.rm(report_file_path) if File.exist?(report_file_path)
+      FileUtils.rm_f(report_file_path)
     end
 
     context 'when RspecFlaky::Config.generate_report? is false' do
@@ -113,7 +117,7 @@
 
         expect(File.exist?(report_file_path)).to be(true)
         expect(File.read(report_file_path))
-          .to eq(JSON.pretty_generate(report.flaky_examples.to_h)) # rubocop:disable Gitlab/Json
+          .to eq(JSON.pretty_generate(report.flaky_examples.to_h))
       end
     end
   end
diff --git a/gems/rspec_flaky/spec/spec_helper.rb b/gems/rspec_flaky/spec/spec_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..72d48ee6e6384c8469c806a0d402bbb9f665c482
--- /dev/null
+++ b/gems/rspec_flaky/spec/spec_helper.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require "rspec-parameterized"
+require "gitlab/rspec/all"
+require "rspec_flaky"
+
+RSpec.configure do |config|
+  # Enable flags like --only-failures and --next-failure
+  config.example_status_persistence_file_path = ".rspec_status"
+
+  # Disable RSpec exposing methods globally on `Module` and `main`
+  config.disable_monkey_patching!
+
+  config.expect_with :rspec do |c|
+    c.syntax = :expect
+  end
+end
diff --git a/scripts/flaky_examples/prune-old-flaky-examples b/scripts/flaky_examples/prune-old-flaky-examples
index a5b50a7e8eac22d3415289d9f830155fd1f07834..24964cbf4dfaf524fbc74d3136a0785e9ba54189 100755
--- a/scripts/flaky_examples/prune-old-flaky-examples
+++ b/scripts/flaky_examples/prune-old-flaky-examples
@@ -1,15 +1,15 @@
 #!/usr/bin/env ruby
 # frozen_string_literal: true
 
-# tooling/rspec_flaky/flaky_examples_collection.rb is requiring
-# `active_support/hash_with_indifferent_access`, and we install the `activesupport`
-# gem manually on the CI
-require 'rubygems'
-require_relative '../../tooling/rspec_flaky/report'
+require 'bundler/inline'
+
+gemfile do
+  gem 'rspec_flaky', path: 'gems/rspec_flaky'
+end
 
 report_file = ARGV.shift
 unless report_file
-  puts 'usage: prune-old-flaky-specs <report-file> <new-report-file>'
+  puts "usage: #{__FILE__} <report-file> <new-report-file>"
   exit 1
 end
 
@@ -20,5 +20,6 @@ puts "Current report has #{report.size} entries."
 
 new_report = report.prune_outdated
 
-puts "New report has #{new_report.size} entries: #{report.size - new_report.size} entries older than #{RspecFlaky::Report::OUTDATED_DAYS_THRESHOLD} days were removed."
+puts "New report has #{new_report.size} entries: #{report.size - new_report.size} entries older than " \
+     "#{RspecFlaky::Report::OUTDATED_DAYS_THRESHOLD} days were removed."
 puts "Saved #{new_report_file}." if new_report.write(new_report_file)
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 18dba7e68ad382cade8f5f37d41c3f14b05b4e4d..eb7796cb55abe916acb3227ca3513d53912dc536 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -38,6 +38,8 @@
 require 'parslet/rig/rspec'
 require 'axe-rspec'
 
+require 'rspec_flaky'
+
 rspec_profiling_is_configured =
   ENV['RSPEC_PROFILING_POSTGRES_URL'].present? ||
   ENV['RSPEC_PROFILING']
@@ -228,11 +230,7 @@
     config.exceptions_to_hard_fail = [DeprecationToolkitEnv::DeprecationBehaviors::SelectiveRaise::RaiseDisallowedDeprecation]
   end
 
-  require_relative '../tooling/rspec_flaky/config'
-
   if RspecFlaky::Config.generate_report?
-    require_relative '../tooling/rspec_flaky/listener'
-
     config.reporter.register_listener(
       RspecFlaky::Listener.new,
       :example_passed,
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 0f91ff5f12cf903699559853a8c08401e9080718..d45595d5fc86a851770dc529f3947050357ca790 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -9748,12 +9748,6 @@
 - './spec/tooling/lib/tooling/test_map_packer_spec.rb'
 - './spec/tooling/merge_request_spec.rb'
 - './spec/tooling/quality/test_level_spec.rb'
-- './spec/tooling/rspec_flaky/config_spec.rb'
-- './spec/tooling/rspec_flaky/example_spec.rb'
-- './spec/tooling/rspec_flaky/flaky_examples_collection_spec.rb'
-- './spec/tooling/rspec_flaky/flaky_example_spec.rb'
-- './spec/tooling/rspec_flaky/listener_spec.rb'
-- './spec/tooling/rspec_flaky/report_spec.rb'
 - './spec/uploaders/attachment_uploader_spec.rb'
 - './spec/uploaders/avatar_uploader_spec.rb'
 - './spec/uploaders/ci/pipeline_artifact_uploader_spec.rb'
diff --git a/tooling/rspec_flaky/config.rb b/tooling/rspec_flaky/config.rb
deleted file mode 100644
index 0e36e985aad59ebb9b6907642b6092c8134c7ede..0000000000000000000000000000000000000000
--- a/tooling/rspec_flaky/config.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-module RspecFlaky
-  class Config
-    def self.generate_report?
-      !!(ENV['FLAKY_RSPEC_GENERATE_REPORT'] =~ /1|true/)
-    end
-
-    def self.suite_flaky_examples_report_path
-      ENV['FLAKY_RSPEC_SUITE_REPORT_PATH'] || rails_path("rspec/flaky/suite-report.json")
-    end
-
-    def self.flaky_examples_report_path
-      ENV['FLAKY_RSPEC_REPORT_PATH'] || rails_path("rspec/flaky/report.json")
-    end
-
-    def self.new_flaky_examples_report_path
-      ENV['NEW_FLAKY_RSPEC_REPORT_PATH'] || rails_path("rspec/flaky/new-report.json")
-    end
-
-    def self.rails_path(path)
-      return path unless defined?(Rails)
-
-      Rails.root.join(path)
-    end
-  end
-end