Add database-backed feature flags with super admin UI (#130) #144

Merged
ldraney merged 1 commit from 130-feature-flags into main 2026-06-07 02:35:16 +00:00
Owner

Summary

Implements a database-backed feature flag system that allows toggling features on/off in production without redeploying. Includes a super admin UI at /platform/feature_flags for managing flags, cached lookups with 1-minute TTL, and an idempotent rake task for registering flags on deploy.

Changes

  • db/migrate/20260606010000_create_feature_flags.rb -- migration for feature_flags table with unique name index
  • app/models/feature_flag.rb -- model with enabled?, enable, disable class methods and Rails.cache.fetch caching
  • app/controllers/platform/feature_flags_controller.rb -- index and toggle actions gated to super_admin role (returns 404 for non-super-admin)
  • app/views/platform/feature_flags/index.html.erb -- table UI with ON/OFF status badges and toggle buttons
  • app/controllers/application_controller.rb -- added feature_enabled? helper method exposed to views
  • config/routes.rb -- added platform namespace with feature_flags resource
  • app/views/layouts/application.html.erb -- Platform link under Person icon for super_admin users only
  • app/assets/stylesheets/application.css -- styles for PlatformNav and FlagsTable components (design tokens, component comments, no Tailwind)
  • lib/tasks/feature_flags.rake -- idempotent feature_flags:sync task with stripe and client_requests flags
  • spec/support/feature_flags.rb -- with_feature_disabled/with_feature_enabled test helpers and before(:each) hook
  • spec/models/feature_flag_spec.rb -- model validations, enabled?, enable, disable, cache behavior
  • spec/requests/platform/feature_flags_spec.rb -- super admin access, toggle, 404 for admin/member
  • spec/requests/feature_flag_gating_spec.rb -- demonstrates controller-level flag gating pattern

Test Plan

  • 32 new specs, 173 total pass locally
  • Model specs verify enabled? returns correct boolean, caching works with MemoryStore, enable/disable bust cache
  • Request specs verify super_admin can access and toggle flags, plain admin and member get 404, unauthenticated redirects to Keycloak
  • Gating spec demonstrates with_feature_disabled/with_feature_enabled block helpers work correctly
  • CI will run full suite on PR

Review Checklist

  • Feature flag needed? N/A -- this IS the feature flag system
  • Tests pass (173 examples, 0 failures)
  • CSS follows design token conventions (no Tailwind, no inline styles, no !important, no #id selectors)
  • No unrelated changes
  • docs/feature-flags.md not modified (implementation follows the doc as spec)
  • Architecture doc: docs/feature-flags.md (used as implementation guide, not modified)
  • Deploy initContainer update tracked in #140 (cross-repo, blocked on this)

Closes #130

  • Spike: #129
  • Deploy initContainer: #140 (blocked on this)
  • Stripe integration: #125
  • Client request UI: #123
## Summary Implements a database-backed feature flag system that allows toggling features on/off in production without redeploying. Includes a super admin UI at `/platform/feature_flags` for managing flags, cached lookups with 1-minute TTL, and an idempotent rake task for registering flags on deploy. ## Changes - `db/migrate/20260606010000_create_feature_flags.rb` -- migration for `feature_flags` table with unique name index - `app/models/feature_flag.rb` -- model with `enabled?`, `enable`, `disable` class methods and `Rails.cache.fetch` caching - `app/controllers/platform/feature_flags_controller.rb` -- index and toggle actions gated to `super_admin` role (returns 404 for non-super-admin) - `app/views/platform/feature_flags/index.html.erb` -- table UI with ON/OFF status badges and toggle buttons - `app/controllers/application_controller.rb` -- added `feature_enabled?` helper method exposed to views - `config/routes.rb` -- added `platform` namespace with `feature_flags` resource - `app/views/layouts/application.html.erb` -- Platform link under Person icon for super_admin users only - `app/assets/stylesheets/application.css` -- styles for PlatformNav and FlagsTable components (design tokens, component comments, no Tailwind) - `lib/tasks/feature_flags.rake` -- idempotent `feature_flags:sync` task with `stripe` and `client_requests` flags - `spec/support/feature_flags.rb` -- `with_feature_disabled`/`with_feature_enabled` test helpers and `before(:each)` hook - `spec/models/feature_flag_spec.rb` -- model validations, `enabled?`, `enable`, `disable`, cache behavior - `spec/requests/platform/feature_flags_spec.rb` -- super admin access, toggle, 404 for admin/member - `spec/requests/feature_flag_gating_spec.rb` -- demonstrates controller-level flag gating pattern ## Test Plan - 32 new specs, 173 total pass locally - Model specs verify `enabled?` returns correct boolean, caching works with MemoryStore, `enable`/`disable` bust cache - Request specs verify super_admin can access and toggle flags, plain admin and member get 404, unauthenticated redirects to Keycloak - Gating spec demonstrates `with_feature_disabled`/`with_feature_enabled` block helpers work correctly - CI will run full suite on PR ## Review Checklist - [x] Feature flag needed? N/A -- this IS the feature flag system - [x] Tests pass (173 examples, 0 failures) - [x] CSS follows design token conventions (no Tailwind, no inline styles, no `!important`, no `#id` selectors) - [x] No unrelated changes - [x] `docs/feature-flags.md` not modified (implementation follows the doc as spec) ## Related Notes - Architecture doc: `docs/feature-flags.md` (used as implementation guide, not modified) - Deploy initContainer update tracked in #140 (cross-repo, blocked on this) ## Related Closes #130 - Spike: #129 - Deploy initContainer: #140 (blocked on this) - Stripe integration: #125 - Client request UI: #123
Add database-backed feature flags with super admin UI (#130)
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
CI / scan_ruby (pull_request) Has been cancelled
CI / scan_js (pull_request) Has been cancelled
CI / lint (pull_request) Has been cancelled
f908d3ca4c
Implements runtime feature flag system for toggling features without
redeploying. Includes FeatureFlag model with cached lookups, idempotent
rake sync task, Platform::FeatureFlagsController gated to super_admin
role, and comprehensive test coverage (32 specs).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Owner

Self-Review Findings

Reviewed the diff against issue #130 spec and docs/feature-flags.md architecture doc.

Acceptance Criteria Checklist

  • Migration creates feature_flags table with correct columns, constraints, and unique index
  • FeatureFlag model with validations, enabled?, enable, disable class methods
  • enabled? uses Rails.cache.fetch with 1-minute TTL; enable/disable bust cache
  • Uses pick(:enabled) for minimal DB overhead
  • feature_enabled? helper in ApplicationController, exposed via helper_method
  • feature_flags:sync rake task with stripe and client_requests flags (disabled by default)
  • Task is idempotent via find_or_create_by!
  • Super admin UI at /platform/feature_flags with table and toggle buttons
  • require_super_admin returns 404 (not 403) for non-super-admin
  • Person icon shows Platform link for super_admin only
  • Test helpers with_feature_disabled and with_feature_enabled
  • before(:each) hook enables all flags and clears cache (with table_exists? guard)
  • Model spec covers all class methods and cache behavior
  • Request spec covers access control and toggle
  • Gating spec demonstrates the pattern

Notes

  1. Controller cache bust is intentional duplication: The update action manually calls Rails.cache.delete after flag.update! rather than delegating to FeatureFlag.enable/FeatureFlag.disable. This matches the architecture doc exactly -- the controller toggles via !flag.enabled? which the model class methods don't support (they're explicit enable/disable). Acceptable.

  2. Cache test uses MemoryStore swap: Test env uses NullStore, so the cache spec swaps in MemoryStore with proper cleanup via ensure. Correct approach.

  3. CSS follows conventions: Design tokens used throughout, component comments present, no Tailwind, no inline styles, no !important, no #id selectors.

  4. docs/feature-flags.md not modified: Confirmed.

  5. Schema version unchanged at 2026_06_07_011440: Correct -- the existing create_versions migration timestamp is higher than the new migration's.

No Issues Found

VERDICT: APPROVE

## Self-Review Findings Reviewed the diff against issue #130 spec and `docs/feature-flags.md` architecture doc. ### Acceptance Criteria Checklist - [x] Migration creates `feature_flags` table with correct columns, constraints, and unique index - [x] `FeatureFlag` model with validations, `enabled?`, `enable`, `disable` class methods - [x] `enabled?` uses `Rails.cache.fetch` with 1-minute TTL; `enable`/`disable` bust cache - [x] Uses `pick(:enabled)` for minimal DB overhead - [x] `feature_enabled?` helper in `ApplicationController`, exposed via `helper_method` - [x] `feature_flags:sync` rake task with `stripe` and `client_requests` flags (disabled by default) - [x] Task is idempotent via `find_or_create_by!` - [x] Super admin UI at `/platform/feature_flags` with table and toggle buttons - [x] `require_super_admin` returns 404 (not 403) for non-super-admin - [x] Person icon shows Platform link for super_admin only - [x] Test helpers `with_feature_disabled` and `with_feature_enabled` - [x] `before(:each)` hook enables all flags and clears cache (with `table_exists?` guard) - [x] Model spec covers all class methods and cache behavior - [x] Request spec covers access control and toggle - [x] Gating spec demonstrates the pattern ### Notes 1. **Controller cache bust is intentional duplication**: The `update` action manually calls `Rails.cache.delete` after `flag.update!` rather than delegating to `FeatureFlag.enable`/`FeatureFlag.disable`. This matches the architecture doc exactly -- the controller toggles via `!flag.enabled?` which the model class methods don't support (they're explicit enable/disable). Acceptable. 2. **Cache test uses MemoryStore swap**: Test env uses `NullStore`, so the cache spec swaps in `MemoryStore` with proper cleanup via `ensure`. Correct approach. 3. **CSS follows conventions**: Design tokens used throughout, component comments present, no Tailwind, no inline styles, no `!important`, no `#id` selectors. 4. **`docs/feature-flags.md` not modified**: Confirmed. 5. **Schema version unchanged at `2026_06_07_011440`**: Correct -- the existing `create_versions` migration timestamp is higher than the new migration's. ### No Issues Found VERDICT: APPROVE
Author
Owner

PR #144 Review

DOMAIN REVIEW

Stack: Ruby on Rails 8.1, RSpec, Hotwire/Turbo, plain CSS with design tokens. Reviewing against Rails conventions, OWASP top 10, the ~/ror-css-guide CSS conventions, and the docs/feature-flags.md architecture spec.

Model (app/models/feature_flag.rb)

  • Matches the architecture doc exactly: validates :name, presence: true, uniqueness: true, CACHE_TTL = 1.minute, Rails.cache.fetch with pick(:enabled), find_or_create_by! in enable/disable, cache busting on write. Clean.

Controller (app/controllers/platform/feature_flags_controller.rb)

  • Auth gate uses current_user_has_role?("super_admin") -- correct. This calls into the session hash via current_user[:roles].include?, not a method on a user object. Matches the AC requirement.
  • Returns head :not_found (404) for non-super-admin -- correct per spec. Does not leak information about the route's existence.
  • update action toggles via !flag.enabled?, busts cache, redirects with flash notice. Straightforward.
  • CSRF protection inherited from ApplicationController via ActionController::Base -- toggle is a PATCH with button_to, which generates a form with an authenticity token. Correct.

ApplicationController helper

  • feature_enabled? delegates to FeatureFlag.enabled? and is added to the helper_method list. Correct pattern -- available in both controllers and views.

Routes (config/routes.rb)

  • namespace :platform do resources :feature_flags, only: [:index, :update] end -- properly namespaced. Only the two needed actions are exposed. No extraneous routes.

Migration

  • Schema matches the doc: name (string, not null), enabled (boolean, not null, default false), description (string, nullable), timestamps, unique index on name. Clean.

Rake task (lib/tasks/feature_flags.rake)

  • feature_flags:sync with find_or_create_by! pattern. Registers stripe and client_requests flags with enabled: false. Idempotent -- existing flags keep their current enabled state. NOT in seeds. Matches spec.

Layout (app/views/layouts/application.html.erb)

  • Platform link appears under the Person icon (nav slot 3), gated by logged_in? && current_user_has_role?("super_admin"). Uses a gear/settings SVG icon. is-active class based on controller_name == 'feature_flags'. Correct placement per architecture doc.

View (app/views/platform/feature_flags/index.html.erb)

  • Table with Flag, Description, Status (ON/OFF badge), Action (toggle button). Uses button_to with method: :patch -- standard Turbo form, no JS needed. Output is properly escaped via ERB <%= %> tags (no raw or html_safe usage). No XSS risk.

CSS (application.css)

  • Component comments present: PlatformNav and FlagsTable with view paths. Follows ~/ror-css-guide pattern.
  • All colors use design tokens: var(--color-muted), var(--color-border), var(--color-success), var(--color-danger), etc.
  • All spacing uses tokens: var(--spacing-xs), var(--spacing-sm), var(--spacing-md).
  • Uses var(--radius) for border-radius.
  • One hardcoded value: color: white on hover states for .btn-flag-enable:hover and .btn-flag-disable:hover. This is a contrast-on-solid-bg pattern (white text on green/red background). There is no --color-surface-on-accent token defined, so white is the pragmatic choice here. Acceptable given the token system does not have a "text on colored background" token.
  • No Tailwind, no inline styles, no !important, no #id selectors. Compliant.
  • One magic number: border-radius: 1rem on .flag-status (pill shape) and border-radius: 4px on .flags-table code. The pill radius is intentional (large value = pill), but 4px on code should ideally use var(--radius) or a smaller token. Minor nit.

Test support (spec/support/feature_flags.rb)

  • with_feature_disabled / with_feature_enabled block helpers with ensure for cleanup. before(:each) hook with table_exists? guard enables all flags and clears cache. Matches the doc exactly.

Model spec (spec/models/feature_flag_spec.rb)

  • 14 examples covering: validations (presence, uniqueness, default), .enabled? (true/false/nonexistent/string names/caching), .enable (toggle/create/cache bust), .disable (toggle/create/cache bust), .cache_key. The caching test swaps in MemoryStore and restores in ensure -- thorough. Good coverage.

Request specs (spec/requests/platform/feature_flags_spec.rb)

  • Tests super_admin access (200, content, statuses, descriptions), plain admin 404, member 404, unauthenticated redirect. Toggle tests: enable, disable, flash notice, cache bust. Non-super-admin toggle returns 404 and does not modify the flag. Comprehensive.

Gating spec (spec/requests/feature_flag_gating_spec.rb)

  • Demonstrates the helper pattern. Tests with_feature_disabled and with_feature_enabled block behavior. Tests feature_enabled? for enabled/disabled/nonexistent flags. Good pattern documentation.

BLOCKERS

None.

NITS

  1. CSS magic number: .flags-table code uses border-radius: 4px instead of var(--radius) (which is 8px). If the intent is a smaller radius, consider defining a --radius-sm token. Very minor.

  2. with_feature_enabled ensure block: The ensure clause calls FeatureFlag.enable(name) -- this means it restores to "enabled" regardless of the flag's state before the block was entered. The comment says "Reset to default-on state" which is correct for the test environment (where before(:each) enables everything), but the asymmetry with with_feature_disabled (which re-enables, not "restores original") is worth noting. Not a bug given the test env contract, but could be surprising if used outside tests. Fine for now.

  3. with_keycloak duplication: The with_keycloak helper is defined inline in both feature_flags_spec.rb and feature_flag_gating_spec.rb (and already exists in crew_spec.rb). This is a pre-existing pattern in the codebase -- the PR is following the established convention, not introducing new duplication. Worth extracting to a shared helper in spec/support/ in a future cleanup ticket, but not a blocker for this PR.

  4. description column output escaping: The view uses <%= flag.description %> which is auto-escaped by ERB. Currently descriptions are only set via the rake task (developer-controlled strings), so there is no user-input XSS vector. If descriptions ever become editable via UI, the escaping is already correct. No issue.

SOP COMPLIANCE

  • Branch named after issue: 130-feature-flags (issue #130)
  • PR body has Summary, Changes, Test Plan, Related sections
  • Related section references the parent issue (#130) and related tickets (#129, #140, #125, #123)
  • No secrets committed -- no API keys, no .env files, no credentials in code
  • No unrelated changes -- all 14 files are directly related to the feature flag system
  • docs/feature-flags.md not modified (confirmed: not in the diff)
  • Commit messages are descriptive (PR title is clear)
  • Tests exist: 32 new specs reported, covering model, request, and gating patterns
  • CSS follows ~/ror-css-guide conventions (design tokens, component comments, no Tailwind, no inline styles, no !important, no #id selectors)
  • Auth uses current_user_has_role?("super_admin") -- not current_user&.super_admin?
  • 404 (not 403) for unauthorized access
  • Rake task uses find_or_create_by!, not seeds
  • pick(:enabled) for hot path
  • Rails.cache.fetch with 1-minute TTL
  • before(:each) with table_exists? guard in test support

PROCESS OBSERVATIONS

  • Clean implementation that tracks the architecture doc (docs/feature-flags.md) faithfully. The doc served as the spec and the code matches it line-for-line in the critical paths (model, controller, rake task, test support). This is how doc-first development should work.
  • The cross-repo dependency (#140 -- deploy initContainer update in pal-e-deployments) is correctly tracked as a separate issue and noted as blocked on this PR. Good sequencing.
  • 32 new specs for a 617-line addition is solid coverage density. The test strategy (default ON, test OFF explicitly) is well-documented and consistent with the architecture doc.
  • Change failure risk: LOW. The feature flag system is additive -- no existing behavior is modified. The only change to an existing file's behavior is adding feature_enabled? to the helper_method list, which has no effect until code actually calls it.

VERDICT: APPROVED

## PR #144 Review ### DOMAIN REVIEW **Stack**: Ruby on Rails 8.1, RSpec, Hotwire/Turbo, plain CSS with design tokens. Reviewing against Rails conventions, OWASP top 10, the `~/ror-css-guide` CSS conventions, and the `docs/feature-flags.md` architecture spec. **Model (`app/models/feature_flag.rb`)** - Matches the architecture doc exactly: `validates :name, presence: true, uniqueness: true`, `CACHE_TTL = 1.minute`, `Rails.cache.fetch` with `pick(:enabled)`, `find_or_create_by!` in `enable`/`disable`, cache busting on write. Clean. **Controller (`app/controllers/platform/feature_flags_controller.rb`)** - Auth gate uses `current_user_has_role?("super_admin")` -- correct. This calls into the session hash via `current_user[:roles].include?`, not a method on a user object. Matches the AC requirement. - Returns `head :not_found` (404) for non-super-admin -- correct per spec. Does not leak information about the route's existence. - `update` action toggles via `!flag.enabled?`, busts cache, redirects with flash notice. Straightforward. - CSRF protection inherited from `ApplicationController` via `ActionController::Base` -- toggle is a PATCH with `button_to`, which generates a form with an authenticity token. Correct. **ApplicationController helper** - `feature_enabled?` delegates to `FeatureFlag.enabled?` and is added to the `helper_method` list. Correct pattern -- available in both controllers and views. **Routes (`config/routes.rb`)** - `namespace :platform do resources :feature_flags, only: [:index, :update] end` -- properly namespaced. Only the two needed actions are exposed. No extraneous routes. **Migration** - Schema matches the doc: `name` (string, not null), `enabled` (boolean, not null, default false), `description` (string, nullable), timestamps, unique index on `name`. Clean. **Rake task (`lib/tasks/feature_flags.rake`)** - `feature_flags:sync` with `find_or_create_by!` pattern. Registers `stripe` and `client_requests` flags with `enabled: false`. Idempotent -- existing flags keep their current `enabled` state. NOT in seeds. Matches spec. **Layout (`app/views/layouts/application.html.erb`)** - Platform link appears under the Person icon (nav slot 3), gated by `logged_in? && current_user_has_role?("super_admin")`. Uses a gear/settings SVG icon. `is-active` class based on `controller_name == 'feature_flags'`. Correct placement per architecture doc. **View (`app/views/platform/feature_flags/index.html.erb`)** - Table with Flag, Description, Status (ON/OFF badge), Action (toggle button). Uses `button_to` with `method: :patch` -- standard Turbo form, no JS needed. Output is properly escaped via ERB `<%= %>` tags (no `raw` or `html_safe` usage). No XSS risk. **CSS (`application.css`)** - Component comments present: `PlatformNav` and `FlagsTable` with view paths. Follows `~/ror-css-guide` pattern. - All colors use design tokens: `var(--color-muted)`, `var(--color-border)`, `var(--color-success)`, `var(--color-danger)`, etc. - All spacing uses tokens: `var(--spacing-xs)`, `var(--spacing-sm)`, `var(--spacing-md)`. - Uses `var(--radius)` for border-radius. - One hardcoded value: `color: white` on hover states for `.btn-flag-enable:hover` and `.btn-flag-disable:hover`. This is a contrast-on-solid-bg pattern (white text on green/red background). There is no `--color-surface-on-accent` token defined, so `white` is the pragmatic choice here. Acceptable given the token system does not have a "text on colored background" token. - No Tailwind, no inline styles, no `!important`, no `#id` selectors. Compliant. - One magic number: `border-radius: 1rem` on `.flag-status` (pill shape) and `border-radius: 4px` on `.flags-table code`. The pill radius is intentional (large value = pill), but `4px` on `code` should ideally use `var(--radius)` or a smaller token. Minor nit. **Test support (`spec/support/feature_flags.rb`)** - `with_feature_disabled` / `with_feature_enabled` block helpers with `ensure` for cleanup. `before(:each)` hook with `table_exists?` guard enables all flags and clears cache. Matches the doc exactly. **Model spec (`spec/models/feature_flag_spec.rb`)** - 14 examples covering: validations (presence, uniqueness, default), `.enabled?` (true/false/nonexistent/string names/caching), `.enable` (toggle/create/cache bust), `.disable` (toggle/create/cache bust), `.cache_key`. The caching test swaps in `MemoryStore` and restores in `ensure` -- thorough. Good coverage. **Request specs (`spec/requests/platform/feature_flags_spec.rb`)** - Tests super_admin access (200, content, statuses, descriptions), plain admin 404, member 404, unauthenticated redirect. Toggle tests: enable, disable, flash notice, cache bust. Non-super-admin toggle returns 404 and does not modify the flag. Comprehensive. **Gating spec (`spec/requests/feature_flag_gating_spec.rb`)** - Demonstrates the helper pattern. Tests `with_feature_disabled` and `with_feature_enabled` block behavior. Tests `feature_enabled?` for enabled/disabled/nonexistent flags. Good pattern documentation. ### BLOCKERS None. ### NITS 1. **CSS magic number**: `.flags-table code` uses `border-radius: 4px` instead of `var(--radius)` (which is `8px`). If the intent is a smaller radius, consider defining a `--radius-sm` token. Very minor. 2. **`with_feature_enabled` ensure block**: The `ensure` clause calls `FeatureFlag.enable(name)` -- this means it restores to "enabled" regardless of the flag's state before the block was entered. The comment says "Reset to default-on state" which is correct for the test environment (where `before(:each)` enables everything), but the asymmetry with `with_feature_disabled` (which re-enables, not "restores original") is worth noting. Not a bug given the test env contract, but could be surprising if used outside tests. Fine for now. 3. **`with_keycloak` duplication**: The `with_keycloak` helper is defined inline in both `feature_flags_spec.rb` and `feature_flag_gating_spec.rb` (and already exists in `crew_spec.rb`). This is a pre-existing pattern in the codebase -- the PR is following the established convention, not introducing new duplication. Worth extracting to a shared helper in `spec/support/` in a future cleanup ticket, but not a blocker for this PR. 4. **`description` column output escaping**: The view uses `<%= flag.description %>` which is auto-escaped by ERB. Currently descriptions are only set via the rake task (developer-controlled strings), so there is no user-input XSS vector. If descriptions ever become editable via UI, the escaping is already correct. No issue. ### SOP COMPLIANCE - [x] Branch named after issue: `130-feature-flags` (issue #130) - [x] PR body has Summary, Changes, Test Plan, Related sections - [x] Related section references the parent issue (#130) and related tickets (#129, #140, #125, #123) - [x] No secrets committed -- no API keys, no `.env` files, no credentials in code - [x] No unrelated changes -- all 14 files are directly related to the feature flag system - [x] `docs/feature-flags.md` not modified (confirmed: not in the diff) - [x] Commit messages are descriptive (PR title is clear) - [x] Tests exist: 32 new specs reported, covering model, request, and gating patterns - [x] CSS follows `~/ror-css-guide` conventions (design tokens, component comments, no Tailwind, no inline styles, no `!important`, no `#id` selectors) - [x] Auth uses `current_user_has_role?("super_admin")` -- not `current_user&.super_admin?` - [x] 404 (not 403) for unauthorized access - [x] Rake task uses `find_or_create_by!`, not seeds - [x] `pick(:enabled)` for hot path - [x] `Rails.cache.fetch` with 1-minute TTL - [x] `before(:each)` with `table_exists?` guard in test support ### PROCESS OBSERVATIONS - Clean implementation that tracks the architecture doc (`docs/feature-flags.md`) faithfully. The doc served as the spec and the code matches it line-for-line in the critical paths (model, controller, rake task, test support). This is how doc-first development should work. - The cross-repo dependency (#140 -- deploy initContainer update in pal-e-deployments) is correctly tracked as a separate issue and noted as blocked on this PR. Good sequencing. - 32 new specs for a 617-line addition is solid coverage density. The test strategy (default ON, test OFF explicitly) is well-documented and consistent with the architecture doc. - Change failure risk: LOW. The feature flag system is additive -- no existing behavior is modified. The only change to an existing file's behavior is adding `feature_enabled?` to the helper_method list, which has no effect until code actually calls it. ### VERDICT: APPROVED
ldraney deleted branch 130-feature-flags 2026-06-07 02:35:16 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ldraney/landscaping-assistant!144
No description provided.