Add missing `feature_category` metadata to RSpec files clearing RSpec/FeatureCategory todos

Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.

Summary

3,295 spec files are excluded from the RSpec/FeatureCategory cop via .rubocop_todo/rspec/feature_category.yml due to a missing feature_category: tag on their top-level RSpec.describe.

To kick off, give an AI agent the following prompt:

Work on #600670

Relates to: gitlab-org/quality#275 (closed)


Agent Guidelines

  • Batch size: 25 files per MR (max 30).
  • Only add missing values. Skip files that already have any feature_category: (even :shared); leave them in the rubocop_todo.
  • Valid categories: config/feature_categories.yml + tooling, test_platform, rails_platform. :shared is not valid.
  • Top-level only. Only modify the RSpec.describe line; never nested context/describe.

Determining the category (in order):

  1. Source file — feature_category :xxx in the corresponding class
  2. Sibling specs in the same directory that already have the tag
  3. Base (non-EE) equivalent spec
  4. CODEOWNERS group → team's primary category
  5. Module/class name

Learned patterns

RuboCop cop specs (spec/rubocop/)

Match category to what the cop enforces, not just the directory:

Cop type Category
cop/database/, ActiveRecord/ORM performance cops :database
cop/api/, cop/graphql/ :api
cop/scalability/, Sidekiq worker cops :scalability
cop/migration/ :database
cop/gitlab/feature_flag*, cop/feature_flag* :scalability
cop/gettext/ :internationalization
Security/file-safety cops :vulnerability_management
General code quality / tooling helpers :tooling
cop/qa/, QA helpers :test_platform
cop/usage_data/ :service_ping
cop/user_admin :system_access
cop/static_translation_definition :internationalization
Sidekiq/Redis cops (sidekiq_*, redis_*) :scalability

rails_platform is for Rails core framework gems only (rails, zeitwerk) — not for cops enforcing Rails patterns.

Important: Many spec/rubocop/cop/ files already have feature_category: :shared (set by the shared copconfig). Always check grep "^RSpec.describe" before editing — skip files that already have any feature_category: value.

spec/lib/gitlab/database/

All files in this directory tree → :database.

Running RuboCop locally

bundle exec rubocop may fail with Could not find gitlab_query_language (native gem missing in GDK setup). Use the system rubocop directly instead:

REVEAL_RUBOCOP_TODO=0 rubocop --only RSpec/FeatureCategory <files>

For commits/pushes, exclude the failing hooks:

LEFTHOOK_EXCLUDE=rubocop git commit ...
LEFTHOOK_EXCLUDE=openapi_docs,rubocop,danger,commit-message-linting git push origin <branch>

Instructions

Setup (once)

Resolve and reuse across all batches:

  • Current user ID: get_user (no args) → id
  • Current milestone ID: search gitlab-org group milestones for the lowest-version active milestone (e.g. 19.1) → id

Pre-flight (REQUIRED before every batch)

Step 0 — Pull master.

git checkout master && git pull origin master

Step 1 — Build an exclusion set of already-claimed files.

Use the GitLab MCP tool search (scope: merge_requests, project: gitlab-org/gitlab) to find all open MRs whose title contains "Add feature_category metadata". For each result, call list_merge_request_diffs and collect every new_path ending in _spec.rb. Also call list_merge_requests_related_to_issue (issue IID 600670) and repeat for any open ones. Union all paths into a single exclusion set.

Step 2 — Check for branch name collisions.

git ls-remote --heads origin '*add-feature-category-batch-*'

The naming convention is add-feature-category-batch-NNN. Pick the next unused number (e.g. if add-feature-category-batch-001 exists, use add-feature-category-batch-002).

Step 3 — Select files (spread across the YAML to avoid conflicts).

Read .rubocop_todo/rspec/feature_category.yml. Extract all entries (including those already excluded or tagged) to get the full list in YAML order. Call this list all_entries (length N_total).

Conflict-avoidance — this takes priority over everything else. The sole source of merge conflicts is .rubocop_todo/rspec/feature_category.yml. When multiple MRs are open simultaneously, their diff hunks must not overlap in that file. The critical insight: stride must be computed over YAML positions (the full entry list), not over the filtered candidate list. Striding over candidates fails when many consecutive YAML entries are all eligible — they map to adjacent YAML lines regardless of candidate-list distance.

  1. Let N_total = total entries in the YAML file (all entries, not just candidates), B = batch size (25).
  2. Compute stride S = N_total / B (round to nearest integer). With ~3100 entries this gives S ≈ 125.
  3. For i in 0..B-1: start at YAML index i * S. Advance forward until finding an entry that is (a) not in the exclusion set and (b) does not already have feature_category: on its RSpec.describe line. Select that entry. This guarantees each selected deletion is ~S YAML lines from the next — well beyond git's 3-line context window (which needs only 7 lines of separation to avoid conflicts).

Secondary goal — minimize distinct CODEOWNERS groups. After the stride selection, check whether the chosen files cluster into one or two CODEOWNERS groups. If swapping a few stride-selected files for nearby unclaimed ones (within ±S/4 positions in the YAML) would reduce the reviewer count without closing the gap between hunks below 20 lines, make that swap. Do not override the spread to achieve grouping — a few extra reviewers is cheaper than a conflict rebase.

Per batch

  1. Edit: add , feature_category: :symbol before do on the RSpec.describe line.
  2. Remove the file from .rubocop_todo/rspec/feature_category.yml.
  3. Verify:
    REVEAL_RUBOCOP_TODO=0 rubocop --only RSpec/FeatureCategory <files>
  4. Commit:
    Add feature_category metadata to batch-NNN specs
    
    Adds missing `feature_category:` metadata to N spec files and removes
    them from .rubocop_todo/rspec/feature_category.yml.
    
    Contributes to https://gitlab.com/gitlab-org/gitlab/-/issues/600670
    Use the full URL — the commit-msg linter rejects short references.
  5. Push:
    LEFTHOOK_EXCLUDE=openapi_docs,rubocop,danger,commit-message-linting git push origin <branch>
    (openapi_docs requires a dev database absent in standard GDK setups; rubocop/danger/commit-message-linting may fail if gitlab_query_language native gem is not installed.)
  6. Create MR via GitLab MCP:
    • title: Add feature_category metadata to batch-NNN specs
    • labels: pipeline::tier-1
    • squash: true, remove_source_branch: true
    • assignee_ids: current user ID, milestone_id: current milestone ID
    • description:
      ## Summary
      
      Adds missing `feature_category:` metadata to N spec files and removes
      them from `.rubocop_todo/rspec/feature_category.yml`.
      
      | File | `feature_category` | Rationale |
      |---|---|---|
      | `path/to/spec.rb` | `:category` | rationale |
      
      Contributes to https://gitlab.com/gitlab-org/gitlab/-/issues/600670
  7. Update this issue if new knowledge was gained during the batch — e.g. category assignment patterns, edge cases, or gotchas discovered — by editing the Agent Guidelines or adding to the Learned patterns section. This keeps future batches consistent.

Definition of Done

  • All files missing feature_category: have a valid tag
  • .rubocop_todo/rspec/feature_category.yml contains only pre-existing non-standard values or is empty
  • CI passes
  • Every assigned category exists in config/feature_categories.yml
Edited by 🤖 GitLab Bot 🤖