Skip to content

Add feature-flag system: schema, evaluation service, router gating, and admin UI - #152

Draft
Tim-Machine wants to merge 5 commits into
v2.2.xfrom
feature/feature-flags
Draft

Add feature-flag system: schema, evaluation service, router gating, and admin UI#152
Tim-Machine wants to merge 5 commits into
v2.2.xfrom
feature/feature-flags

Conversation

@Tim-Machine

@Tim-Machine Tim-Machine commented Jul 25, 2026

Copy link
Copy Markdown

Summary

Adds an end-to-end feature-flag system so features can be toggled per community without redeploying:

  • Migration 068: new feature_flags and feature_flag_audit tables (numbered SQL migration per the dual-schema convention).
  • FeatureFlagService in libs/flask_core (mirrored into services/ layout): evaluation helper for flag lookups with community-level overrides.
  • Router gating: processing/router_module checks feature flags before dispatching commands.
  • Hub portal admin surface: feature-flag management UI/API in admin/hub_module.
  • Also removes a stray EXIT: deploy-log file that was an invalid path on Windows.

Testing

  • Unit tests added: tests/unit/test_feature_flags.py and tests/unit/test_router_feature_flags.py.

🤖 Generated with Claude Code

Summary by Sourcery

Introduce a shared feature-flag system used by the router for command/interaction gating and by the hub admin portal for global and community-scoped flag management, backed by new database tables and Redis-based cache invalidation.

New Features:

  • Add a FeatureFlagService to flask_core (and mirrored core-community libs) for Redis-cached flag evaluation with community/platform scoping and rollout percentages.
  • Gate router command and interaction dispatch behind module-level feature flags, with identity/workflow modules explicitly exempt.
  • Provide superadmin APIs and UI to manage global feature flags and view an audit trail of changes.
  • Provide community-admin APIs and UI to manage per-community feature flag overrides that resolve consistently with router behavior.

Enhancements:

  • Wire the router app to initialize the FeatureFlagService, expose it via app config, and run a Redis pub/sub listener to invalidate cached flag decisions on reload events.
  • Extend admin and superadmin navigation/layouts to surface feature-flag management pages.
  • Update architecture documentation to record ownership and migration details for the new feature_flags and feature_flag_audit tables.

Build:

  • Add redis v4 as a dependency to the hub admin backend and introduce a shared, lazy Redis client for pub/sub signalling.

Tests:

  • Add unit tests for the FeatureFlagService resolution, caching, and reload behavior.
  • Add unit tests for the router’s feature-flag dispatch gate across command and interaction paths, including core-module bypass semantics.

Chores:

  • Remove an invalid EXIT deploy-log file from the repository.

zero and others added 5 commits June 24, 2026 08:14
Scoped flags: community_id NULL = global default, platform NULL = all
platforms, rollout_pct 0-100 for sticky percentage rollouts. Append-only
audit table. Verified against a live PostgreSQL 16 in a rolled-back
transaction; picked up automatically by the Alembic baseline glob.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Async Redis-cached flag resolution (most-specific scope wins), sticky
sha256 percentage bucketing on community_id, fail-open on errors, and
pub/sub invalidation via the feature_flags:reload channel. Mirrored into
the services/core-community flask_core copy per the dual-layout
convention. 20 stdlib-only unit tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New _dispatch_gate enforces core-module bypass (identity/workflow are
never blockable, now enforced at dispatch rather than only in the admin
API), the community module toggle, and a module.<name> feature-flag
check on both the command path and the interaction path - the latter
previously dispatched with no enable check at all. Adds a background
listener on feature_flags:reload for cache invalidation and threads
platform into flag evaluation. 11 unit tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Community admins manage per-community overrides (scoped CRUD, cannot
touch global or foreign rows); superadmins manage global flags and the
audit trail. Effective-state display replicates the router's
specificity resolution exactly (community scope outranks platform
scope). Every mutation writes an audit row in-transaction and publishes
feature_flags:reload via a new shared node-redis client. Redis ACLs
grant the hub user publish and the router user subscribe on that
channel - the router user previously had no channel permissions at all,
which also silently broke its existing command:reload subscription.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Implements a full feature-flag system with database schema, a shared evaluation service, router dispatch gating, Redis-based cache invalidation, and admin UI/API surfaces for superadmins and community admins, plus unit tests and minor cleanup.

Sequence diagram for feature-flag mutation and router cache invalidation

sequenceDiagram
    actor SuperAdmin
    participant SuperAdminFeatureFlags as SuperAdminFeatureFlagsUI
    participant HubBackend as featureFlagAdminController
    participant Postgres as PostgresDB
    participant Redis as RedisPubSub
    participant Router as RouterApp
    participant FlagService as FeatureFlagService

    SuperAdmin ->> SuperAdminFeatureFlags: superAdminApi.updateFeatureFlag(id, data)
    SuperAdminFeatureFlags ->> HubBackend: updateGlobalFlag(req)
    HubBackend ->> Postgres: SELECT * FROM feature_flags WHERE id = $1
    HubBackend ->> Postgres: UPDATE feature_flags SET ... WHERE id = $id
    HubBackend ->> Postgres: insertFlagAudit(client, { flagKey, communityId, platform, ... })
    HubBackend ->> Redis: publishReload(flagKey, null)

    Router ->> Redis: _feature_flag_reload_listener(feature_flag_service, REDIS_URL)
    Redis -->> Router: message on feature_flags:reload
    Router ->> FlagService: handle_reload(message)
    FlagService ->> Redis: _invalidate(flag_key)
    FlagService ->> Redis: DEL feature_flag:*:flag_key:*
    FlagService -->> Router: cache invalidated
Loading

Entity-relationship diagram for feature_flags and feature_flag_audit

erDiagram
    feature_flags {
        int id
        varchar flag_key
        int community_id
        varchar platform
        boolean is_enabled
        smallint rollout_pct
        text description
    }

    feature_flag_audit {
        int id
        varchar flag_key
        int community_id
        varchar platform
        varchar action
        jsonb old_value
        jsonb new_value
        varchar changed_by
        timestamp changed_at
    }

    feature_flags ||--o{ feature_flag_audit : audit_for_flag
Loading

File-Level Changes

Change Details Files
Add FeatureFlagService in flask_core and wire it into the router for flag evaluation and Redis-based cache invalidation.
  • Introduce FeatureFlagService with most-specific scope resolution, rollout percentage bucketing, Redis caching, and reload handling.
  • Expose FeatureFlagService and factory in flask_core init for both libs and services layouts.
  • Instantiate FeatureFlagService in router app startup using the shared Redis client and register it in app.config.
  • Implement a Redis pub/sub listener in the router to invalidate cached flag decisions on feature_flags:reload messages.
libs/flask_core/flask_core/feature_flags.py
services/core-community/libs/flask_core/flask_core/feature_flags.py
libs/flask_core/flask_core/__init__.py
services/core-community/libs/flask_core/flask_core/__init__.py
processing/router_module/app.py
Gate router command and interaction dispatch behind module enable toggles and feature flags, with core modules bypassing the gates.
  • Define CORE_MODULE_NAMES for non-disableable modules and document rationale.
  • Add feature_flag_service dependency to CommandProcessor and store platform from events.
  • Implement _dispatch_gate to apply module enable check and feature-flag evaluation, returning structured errors.
  • Use _dispatch_gate in execute_command and _process_interaction so both paths enforce the same gating logic.
processing/router_module/services/command_processor.py
Add PostgreSQL schema for feature_flags and feature_flag_audit tables with appropriate indexes and constraints, and document ownership.
  • Create migration 068_feature_flags.sql defining feature_flags with scoped uniqueness and rollout_pct, and feature_flag_audit as append-only history.
  • Add table ownership entries for feature_flags and feature_flag_audit to architecture docs.
config/postgres/migrations/068_feature_flags.sql
docs/architecture/table-ownership.md
Implement backend services and routes for global feature-flag management and audit viewing for superadmins.
  • Add featureFlagService.js with platform allowlist, key/rollout validators, specificity resolution helpers, audit insertion, and Redis reload publishing.
  • Introduce featureFlagAdminController.js for listing global flags with override counts, CRUD operations on global flags, and paginated audit listing.
  • Wire superadmin routes for feature-flag list/create/update/delete and audit endpoints, with body validation.
  • Add Redis config module using node-redis for pub/sub with soft-fail semantics and update backend dependencies to include redis.
admin/hub_module/backend/src/services/featureFlagService.js
admin/hub_module/backend/src/controllers/featureFlagAdminController.js
admin/hub_module/backend/src/routes/superadmin.js
admin/hub_module/backend/src/config/redis.js
admin/hub_module/backend/package.json
admin/hub_module/backend/package-lock.json
Implement backend services and routes for community-scoped feature-flag overrides and merged effective flag views.
  • Add featureFlagController.js to manage community overrides with strict community_id ownership checks, transactional audit logging, and reload publishing.
  • Add featureFlags.js routes under /admin requiring auth and community-admin, with request validation for overrides.
  • Expose community admin API endpoints for listing flags and CRUD on overrides in the frontend API service.
admin/hub_module/backend/src/controllers/featureFlagController.js
admin/hub_module/backend/src/routes/featureFlags.js
admin/hub_module/frontend/src/services/api.js
admin/hub_module/backend/src/routes/index.js
Provide admin and superadmin UI pages and navigation for feature-flag management and audit viewing.
  • Add AdminFeatureFlags page that shows effective state per flag/platform, reflects router resolution, and supports create/edit/revert community overrides.
  • Add SuperAdminFeatureFlags page with tabs for global flags (search, CRUD, override counts) and audit trail (filtering, pagination).
  • Wire new pages into App routes and add navigation entries with FlagIcon in AdminLayout and DashboardLayout for admin and superadmin menus.
admin/hub_module/frontend/src/pages/admin/AdminFeatureFlags.jsx
admin/hub_module/frontend/src/pages/superadmin/SuperAdminFeatureFlags.jsx
admin/hub_module/frontend/src/App.jsx
admin/hub_module/frontend/src/layouts/AdminLayout.jsx
admin/hub_module/frontend/src/layouts/DashboardLayout.jsx
Add unit tests for the feature-flag evaluation service and router gating behavior using lightweight fakes.
  • Add test_feature_flags.py to exercise FeatureFlagService resolution logic, caching, rollout bucketing, error handling, and reload behavior using fakes for Redis and DAL.
  • Add test_router_feature_flags.py to load CommandProcessor and FeatureFlagService via file paths with stubbed heavy dependencies, and test _dispatch_gate, execute_command, and _process_interaction for core vs non-core modules, flag presence, and platform scoping.
tests/unit/test_feature_flags.py
tests/unit/test_router_feature_flags.py
Minor cleanup: remove an invalid EXIT deploy-log file.
  • Delete EXIT: file that represented an invalid path on Windows.
EXIT:

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

* exported client is null, and if Redis is unreachable every operation fails
* soft (logged, never thrown). The app must run fine with Redis down or absent.
*/
import { createClient } from 'redis';

@PenguinzTech PenguinzTech Jul 25, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have nodejs do "backend" things would be a change of architecture.. we typically use Python3.12+ / Quart , Rust for clients, and GoLang for networking to handle "backend" things and simply use nextjs for a thin web layer.
Changing languages is not out of the question, but we should talk through it first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants