Refactor feature flags to use functional availability rules - #3166
Refactor feature flags to use functional availability rules#3166SamMorrowDrums wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Refactors feature gating around typed functional rules and shared request-scoped resolution.
Changes:
- Introduces
FeatureFlag,FeatureRule, and cached resolution state. - Migrates inventory items and GitHub tools from legacy flag fields.
- Updates HTTP/stdio integration, tests, generators, and documentation.
Show a summary per file
| File | Description |
|---|---|
script/print-mcp-diff-configs/main.go |
Uses the header-compatible flag accessor. |
pkg/inventory/server_tool.go |
Replaces legacy tool gates with FeatureRule. |
pkg/inventory/resources.go |
Adds resource feature rules. |
pkg/inventory/registry.go |
Collects and pre-resolves required features. |
pkg/inventory/registry_test.go |
Migrates inventory feature tests. |
pkg/inventory/prompts.go |
Adds prompt feature rules. |
pkg/inventory/filters.go |
Evaluates functional rules during filtering. |
pkg/inventory/features.go |
Implements typed rules and resolution state. |
pkg/inventory/features_test.go |
Tests predicates and caching. |
pkg/inventory/builder.go |
Removes the legacy feature filter. |
pkg/http/server.go |
Adapts HTTP feature resolution to typed flags. |
pkg/http/server_test.go |
Updates HTTP checker tests. |
pkg/http/handler.go |
Seeds request-owned feature state. |
pkg/http/handler_test.go |
Migrates handler feature tests. |
pkg/github/ui_tools.go |
Migrates the UI tool gate. |
pkg/github/ui_tools_test.go |
Verifies the UI feature rule. |
pkg/github/ui_capability_test.go |
Uses typed UI flags. |
pkg/github/tools.go |
Types granular flags and converts header flags. |
pkg/github/tools_validation_test.go |
Updates gated-duplicate validation. |
pkg/github/server.go |
Updates feature configuration documentation. |
pkg/github/server_test.go |
Updates dependency stubs. |
pkg/github/repositories.go |
Migrates file-blame gating. |
pkg/github/repositories_test.go |
Verifies file-blame rules. |
pkg/github/pullrequests.go |
Migrates consolidated PR rules. |
pkg/github/pullrequests_granular.go |
Migrates granular PR rules. |
pkg/github/issues.go |
Migrates consolidated issue rules. |
pkg/github/issues_test.go |
Updates issue-rule assertions. |
pkg/github/issues_granular.go |
Migrates granular issue rules. |
pkg/github/issue_dependencies.go |
Migrates dependency-tool gates. |
pkg/github/issue_dependencies_test.go |
Updates dependency gate tests. |
pkg/github/granular_tools_test.go |
Tests granular functional rules. |
pkg/github/find_duplicate.go |
Migrates duplicate-detection gating. |
pkg/github/find_duplicate_test.go |
Updates duplicate gate tests. |
pkg/github/feature_flags.go |
Types flags and defines reusable rules. |
pkg/github/feature_flags_test.go |
Migrates feature-resolution tests. |
pkg/github/dependencies.go |
Shares resolution through dependencies. |
pkg/github/dependencies_test.go |
Updates dependency checker tests. |
pkg/github/csv_output_test.go |
Migrates CSV rule fixtures. |
pkg/github/context_tools_test.go |
Uses typed IFC flags. |
pkg/github/actions_test.go |
Updates functional-rule terminology. |
internal/ghmcp/server.go |
Adapts stdio feature checking. |
docs/insiders-features.md |
Documents shared functional resolution. |
docs/feature-flags.md |
Documents availability rules. |
cmd/github-mcp-server/generate_docs.go |
Updates default documentation checker. |
cmd/github-mcp-server/feature_flag_docs.go |
Types feature documentation generation. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 45/45 changed files
- Comments generated: 4
- Review effort level: Balanced
IrynaKulakova
left a comment
There was a problem hiding this comment.
A few notes on the new feature-rule machinery. Overall direction looks right — replacing the three annotation fields with one declared predicate is a clear improvement, and the fail-closed semantics of the old featureFlagAllowed are preserved. Comments below are about the resolution state, not the rule model itself.
9a34d96 to
397f5eb
Compare
Resolve declared inventory features once per request and share the request-owned cache with in-handler feature checks. BREAKING CHANGE: Inventory items now use FeatureRule instead of FeatureFlagEnable, FeatureFlagEnableAll, and FeatureFlagDisable; FeatureFlagChecker now accepts FeatureFlag. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1e4a1ca6-53f7-4158-af22-35d2448d0b13
Keep legacy string APIs compatible, seed feature state from each inventory's checker, persist caching for stdio calls, and fail closed for empty undeclared flags. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1e4a1ca6-53f7-4158-af22-35d2448d0b13
Make feature checks reentrant and single-flight, validate rule declarations across short-circuit paths, cache inventory feature metadata, and codify the HTTP context boundary used by the remote server. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1e4a1ca6-53f7-4158-af22-35d2448d0b13
Resolve the feature-query test against the final string-compatible flag API. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1e4a1ca6-53f7-4158-af22-35d2448d0b13
397f5eb to
de349a5
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Cached metadata lacks enforced ownership, and an additional public API break is undocumented.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Balanced
Findings: 2
New issues introduced by this change (2)
| Severity | Finding |
|---|---|
pkg/github/dependencies.go — This introduces another public source-breaking change that is not listed in the PR's Breaking… |
|
pkg/inventory/registry.go — These caches assume immutable inventory contents, but Builder.SetTools, SetResources, and… |
|
|
||
| // IsFeatureEnabled checks if a feature flag is enabled. | ||
| IsFeatureEnabled(ctx context.Context, flagName string) bool | ||
| IsFeatureEnabled(ctx context.Context, flag inventory.FeatureFlag) bool |
| // Feature metadata is derived once from the immutable inventory contents. | ||
| toolFeatures []FeatureFlag | ||
| resourceTemplateFeatures []FeatureFlag | ||
| promptFeatures []FeatureFlag | ||
| requiredFeatures []FeatureFlag | ||
| usesMCPApps bool |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Feature resolution can deadlock or incorrectly enable cyclic flags, and cached metadata is not protected from caller mutation.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Balanced
Findings: 4
New issues introduced by this change (3)
| Severity | Finding |
|---|---|
pkg/inventory/features.go — Cycle detection does not necessarily fail the resolved flag closed. For a checker that computes… |
|
pkg/inventory/features.go — This wait can deadlock on a concurrent cross-feature cycle. If one goroutine owns A and its… |
|
pkg/inventory/builder.go — The metadata cache assumes immutable inventory contents, but Build retains the slices supplied to… |
Pre-existing issues (2)
| Severity | Finding |
|---|---|
pkg/inventory/registry.go — These caches assume immutable inventory contents, but Builder.SetTools, SetResources, and… View comment |
|
pkg/github/dependencies.go — This introduces another public source-breaking change that is not listed in the PR's Breaking… View comment |
Suppressed comments (1)
pkg/github/dependencies.go:97
- This changes the public
ToolDependenciesinterface, so external implementations and callers using string variables no longer compile. The PR's breaking-change section lists the inventory fields andFeatureFlagChecker, but not this interface change. Either preserve the string boundary and convert internally, or explicitly documentToolDependencies.IsFeatureEnabledas another breaking change.
IsFeatureEnabled(ctx context.Context, flag inventory.FeatureFlag) bool
| for current := resolvingFeatureFromContext(ctx); current != nil; current = current.parent { | ||
| if current.flag == feature { | ||
| fmt.Fprintf(os.Stderr, "Feature flag resolution cycle detected for %q\n", feature) | ||
| return false |
| if found { | ||
| select { | ||
| case <-result.ready: | ||
| return result.enabled | ||
| case <-ctx.Done(): | ||
| return false | ||
| } |
| } | ||
| } | ||
|
|
||
| r.cacheFeatureMetadata() |


Summary
FeatureFlagEnable,FeatureFlagEnableAll, andFeatureFlagDisablewith typed functionalFeatureRulepredicatesinventory.FeatureFlagandFeatureResolvertypes for local and remote server consumersToolDependencies.IsFeatureEnabledAllowedFeatureFlags/header allowlistBreaking change
Inventory items now expose
FeatureRuleinstead of the three legacy feature-gate fields, andFeatureFlagCheckeracceptsinventory.FeatureFlaginstead ofstring.Validation
script/lintscript/testscript/generate-docs