Skip to content

feat: add feature flag recomposition support#2853

Open
wilsonrivera wants to merge 2 commits into
mainfrom
wilson/eng-9541
Open

feat: add feature flag recomposition support#2853
wilsonrivera wants to merge 2 commits into
mainfrom
wilson/eng-9541

Conversation

@wilsonrivera
Copy link
Copy Markdown
Contributor

@wilsonrivera wilsonrivera commented May 11, 2026

Summary by CodeRabbit

  • New Features
    • Added a recompose feature-flag capability: CLI command plus platform API to trigger recomposition/redeployment with namespace, limit, validation and warning controls.
  • Tests
    • Added unit and integration tests covering success, error, warning and flag/namespace edge cases.
  • Chores
    • Updated RPC surface, message types, client bindings, server handler wiring, and audit logging to support the new operation.

Review Change Stack

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.
  • Documentation has been updated on https://github.com/wundergraph/docs-website.
  • I have read the Contributors Guide.

Open Source AI Manifesto

This project follows the principles of the Open Source AI Manifesto. Please ensure your contribution aligns with its principles.

The goal of this PR is to introduce support to recompose specific feature flags the same way federated-graph recompose and monograph recompose works

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 45e4c62f-d551-47b0-a540-d970607a2d9b

📥 Commits

Reviewing files that changed from the base of the PR and between aa7ff85 and 08b0321.

📒 Files selected for processing (1)
  • controlplane/test/feature-flag/recompose-feature-flag.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • controlplane/test/feature-flag/recompose-feature-flag.test.ts

Walkthrough

Adds RecomposeFeatureFlag end-to-end: proto messages and RPC, generated client bindings, server handler with validation and composition/deploy logic, router wiring and audit action, CLI command, and server/CLI tests.

Changes

Feature Flag Recomposition Workflow

Layer / File(s) Summary
Proto Contracts
proto/wg/cosmo/platform/v1/platform.proto
Adds RecomposeFeatureFlagRequest (name, namespace, optional limit, optional disable_resolvability_validation) and RecomposeFeatureFlagResponse (response, composition/deployment errors/warnings, errorCounts) plus rpc RecomposeFeatureFlag.
Generated Protocol Bindings
connect/src/wg/cosmo/platform/v1/platform_pb.ts, connect/src/wg/cosmo/platform/v1/platform_connect.ts, connect/src/wg/cosmo/platform/v1/platform-PlatformService_connectquery.ts
Generated protobuf message classes and Connect/Connect-Query RPC descriptors for the new recompose request/response and unary method.
Feature Flag Recomposition Handler
controlplane/src/core/bufservices/feature-flag/recomposeFeatureFlag.ts
Server handler: authentication, default namespace, RBAC and split-config-loading checks, namespace/flag existence verification, DB transaction invoking CompositionService.composeAndDeployFeatureFlag, audit log write, bounded result lists, and response status selection.
Service Routing & Audit
controlplane/src/core/bufservices/PlatformService.ts, controlplane/src/db/models.ts
Imports and wires recomposeFeatureFlag into PlatformService router; adds 'feature_flag.recomposed' to AuditLogFullAction.
Server Integration Tests
controlplane/test/feature-flag/recompose-feature-flag.test.ts
Integration tests: capability-disabled error, namespace not found, feature flag not found, successful recomposition (blob artifacts and composition count changes), and recomposition effects with contracts.
CLI Command Implementation
cli/src/commands/feature-flag/commands/recompose.ts, cli/src/commands/feature-flag/index.ts
New recompose subcommand with <name> and options (namespace, suppress-warnings, disable-resolvability-validation, fail-on-composition-error, fail-on-admission-webhook-error, limit); validates --limit, calls client.platform.recomposeFeatureFlag, handles error codes/messages, delegates composition/deployment result reporting, and manages exit codes.
CLI Test Utilities & Tests
cli/test/feature-flag/utils.ts, cli/test/feature-flag/recompose.test.ts
Test helpers (mock transport/client, runRecompose) and Vitest tests covering success, missing response, capability-disabled/not-found errors, composition/deployment failure behaviors with fail flags, and warning display/suppression.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • wundergraph/cosmo#2847: New RecomposeFeatureFlag RPC and CLI build on the CompositionService/feature-flag recomposition machinery refactored in this PR.
  • wundergraph/cosmo#2814: Both PRs integrate with the split-config-loading feature flag for feature-aware flows.
  • wundergraph/cosmo#2823: This PR depends on split-config-loading feature support and JWT/feature claim handling introduced in the referenced PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add feature flag recomposition support' directly and clearly summarizes the main change—adding recomposition capability for feature flags, which is the core objective across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 11, 2026

Codecov Report

❌ Patch coverage is 96.47577% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 40.93%. Comparing base (893ff72) to head (08b0321).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...li/src/commands/feature-flag/commands/recompose.ts 96.11% 4 Missing ⚠️
...e/bufservices/feature-flag/recomposeFeatureFlag.ts 96.61% 4 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #2853       +/-   ##
===========================================
- Coverage   65.01%   40.93%   -24.08%     
===========================================
  Files         573     1026      +453     
  Lines       71938   129306    +57368     
  Branches     4862     5997     +1135     
===========================================
+ Hits        46767    52927     +6160     
- Misses      23724    74635    +50911     
- Partials     1447     1744      +297     
Files with missing lines Coverage Δ
cli/src/commands/feature-flag/index.ts 95.83% <100.00%> (ø)
...ntrolplane/src/core/bufservices/PlatformService.ts 83.08% <100.00%> (+0.09%) ⬆️
controlplane/src/db/models.ts 100.00% <ø> (ø)
...li/src/commands/feature-flag/commands/recompose.ts 96.11% <96.11%> (ø)
...e/bufservices/feature-flag/recomposeFeatureFlag.ts 96.61% <96.61%> (ø)

... and 455 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 11, 2026

Router image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-cdf1518662dd885ace4ed3ceae0141f8ed4e130c

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
cli/test/feature-flag/recompose.test.ts (1)

52-60: ⚡ Quick win

Tighten thrown-error assertions to avoid false positives

At Line 53 and Line 67, .rejects.toThrow() is too broad. Because Line 12 mocks process.exit to throw 'process.exit', these tests can pass even if the command exits unexpectedly. Assert the expected error message/details (or assert process.exit was not called) so these cases validate the intended failure mode.

✅ Suggested test hardening
   test('that recompose fails fails with exit code 1 if config splitting is not enabled', async () => {
     await expect(
       runRecompose({
         response: { code: EnumStatusCode.ERR, details: 'Configuration splitting not enabled' },
         compositionErrors: [],
         compositionWarnings: [],
         deploymentErrors: [],
       }),
-    ).rejects.toThrow();
+    ).rejects.toThrow('Configuration splitting not enabled');

     expect(process.exitCode).toBeUndefined();
   });

   test('that recompose fails but does not return exit code 1 if the feature flag is not found', async () => {
     await expect(
       runRecompose({
         response: { code: EnumStatusCode.ERR_NOT_FOUND, details: 'The feature flag "feature-flag" was not found' },
         compositionErrors: [],
         compositionWarnings: [],
         deploymentErrors: [],
       }),
-    ).rejects.toThrow();
+    ).rejects.toThrow('was not found');

     expect(process.exitCode).toBeUndefined();
   });

Also applies to: 65-73

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/test/feature-flag/recompose.test.ts` around lines 52 - 60, The test using
runRecompose currently uses .rejects.toThrow() which is too broad and can pass
due to the mocked process.exit throwing 'process.exit'; update the assertions to
verify the specific failure from runRecompose (e.g.,
.rejects.toThrow('Configuration splitting not enabled') or similar exact message
coming from runRecompose) and/or assert that the mocked process.exit was not
called; locate the failing tests by the names "that recompose fails fails with
exit code 1 if config splitting is not enabled" and the other case around lines
65–73 and update their expectations to check the exact error message or add an
assertion on the process.exit mock instead of a generic .toThrow().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cli/src/commands/feature-flag/commands/recompose.ts`:
- Around line 44-55: Wrap the call to
opts.client.platform.recomposeFeatureFlag(...) in a try/catch to prevent
unhandled promise rejections: call recomposeFeatureFlag (the RPC) inside the
try, on success stop the spinner with spinner.succeed(...) and proceed, and in
the catch call spinner.fail(...) with an informative message including the
caught error, then surface/exit appropriately (e.g., throw or process.exit with
non-zero). Ensure you still pass the same payload
(disableResolvabilityValidation, limit, name, namespace) and headers from
getBaseHeaders(), and avoid leaving the spinner running on transport/protocol
failures.

In `@controlplane/test/feature-flag/recompose-feature-flag.test.ts`:
- Around line 18-23: The vi.mock call is targeting the wrong module specifier so
the real ClickHouse implementation can leak into tests; update the string
argument passed to vi.mock so it exactly matches the module specifier used by
the test's import (the same relative path the test imports), and keep the mocked
exports as shown (mock ClickHouseClient and its prototype.queryPromise) so
ClickHouseClient and queryPromise are used by the test instead of the real
module.

---

Nitpick comments:
In `@cli/test/feature-flag/recompose.test.ts`:
- Around line 52-60: The test using runRecompose currently uses
.rejects.toThrow() which is too broad and can pass due to the mocked
process.exit throwing 'process.exit'; update the assertions to verify the
specific failure from runRecompose (e.g., .rejects.toThrow('Configuration
splitting not enabled') or similar exact message coming from runRecompose)
and/or assert that the mocked process.exit was not called; locate the failing
tests by the names "that recompose fails fails with exit code 1 if config
splitting is not enabled" and the other case around lines 65–73 and update their
expectations to check the exact error message or add an assertion on the
process.exit mock instead of a generic .toThrow().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dd7db1d7-5d85-424e-abf1-7f4defbb79ff

📥 Commits

Reviewing files that changed from the base of the PR and between 1c7bc8c and aa7ff85.

⛔ Files ignored due to path filters (2)
  • connect-go/gen/proto/wg/cosmo/platform/v1/platform.pb.go is excluded by !**/*.pb.go, !**/gen/**
  • connect-go/gen/proto/wg/cosmo/platform/v1/platformv1connect/platform.connect.go is excluded by !**/gen/**
📒 Files selected for processing (12)
  • cli/src/commands/feature-flag/commands/recompose.ts
  • cli/src/commands/feature-flag/index.ts
  • cli/test/feature-flag/recompose.test.ts
  • cli/test/feature-flag/utils.ts
  • connect/src/wg/cosmo/platform/v1/platform-PlatformService_connectquery.ts
  • connect/src/wg/cosmo/platform/v1/platform_connect.ts
  • connect/src/wg/cosmo/platform/v1/platform_pb.ts
  • controlplane/src/core/bufservices/PlatformService.ts
  • controlplane/src/core/bufservices/feature-flag/recomposeFeatureFlag.ts
  • controlplane/src/db/models.ts
  • controlplane/test/feature-flag/recompose-feature-flag.test.ts
  • proto/wg/cosmo/platform/v1/platform.proto

Comment thread cli/src/commands/feature-flag/commands/recompose.ts
Comment thread controlplane/test/feature-flag/recompose-feature-flag.test.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant