Skip to content

feat: Add multi-selector support to flagd#1898

Open
Nilushiya wants to merge 15 commits intoopen-feature:mainfrom
Nilushiya:multi-tenant-merge
Open

feat: Add multi-selector support to flagd#1898
Nilushiya wants to merge 15 commits intoopen-feature:mainfrom
Nilushiya:multi-tenant-merge

Conversation

@Nilushiya
Copy link
Copy Markdown

This PR
Adds multi-selector support for flag evaluation and sync by allowing clients to pass an ordered list of selectors and merging results so later selectors override earlier ones.
Adds HTTP header support for multi-selectors via the new flag-selector header (comma-separated), with fallback to the existing Flagd-Selector header.
Implements ordered multi-merge for ResolveAll by evaluating per selector in sequence and merging by flag key.
Related Issues
Fixes #1234523

Notes
New header: flag-selector (preferred). Example: flag-selector: flagSetId=base,flagSetId=tenantA,flagSetId=tenantB
Backward compatible: existing Flagd-Selector still works (single selector), and also works with comma-separated selector lists after this change.
Merge semantics: selectors are applied left-to-right; later selector results override earlier ones for the same FlagKey.
Key code changes:
flagd/pkg/service/constants.go: adds FLAG_SELECTOR_HEADER = "flag-selector"
flagd/pkg/service/selector.go: helper functions to read selector expression from HTTP headers and gRPC metadata (prefers flag-selector)
flagd/pkg/service/flag-evaluation/selector_merge.go: adds ResolveAllWithSelectorMerge to evaluate selectors in order and merge results
flagd/pkg/service/flag-evaluation/flag_evaluator_v1.go and flagd/pkg/service/flag-evaluation/flag_evaluator.go: ResolveAll now uses ResolveAllWithSelectorMerge
flagd/pkg/service/flag-sync/handler.go: adds ordered selector parsing + merge for sync fetch/stream
Tests added/updated:
flagd/pkg/service/selector_test.go
flagd/pkg/service/flag-sync/handler_test.go
flagd/pkg/service/flag-evaluation/ofrep/handler_test.go
Follow-up Tasks
Extend multi-selector behavior consistently to other evaluation endpoints (non-bulk Resolve* calls) if required.
Align streaming/watch behavior for multi-selectors across all services (if stricter per-selector watch semantics are needed).
How to test
Build and run:
docker build -f flagd/build.Dockerfile -t flagd:multi-selector .
docker compose up -d
Postman (ResolveAll):
POST http://localhost:8013/flagd.evaluation.v1.Service/ResolveAll
Headers:
Content-Type: application/json
flag-selector: flagSetId=base,flagSetId=tenantA (order matters)
Body: {} or {"context":{"Hospital":"A1"}}
Expect: flags from tenantA override flags from base for overlapping keys.
refer this repo : https://github.com/Nilushiya/Multi-tenat-with-multi-selector.git

@Nilushiya Nilushiya requested review from a team as code owners March 10, 2026 11:12
@netlify
Copy link
Copy Markdown

netlify bot commented Mar 10, 2026

Deploy Preview for polite-licorice-3db33c canceled.

Name Link
🔨 Latest commit f59c005
🔍 Latest deploy log https://app.netlify.com/projects/polite-licorice-3db33c/deploys/69bcaf7d6382c30008a049a7

@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Mar 10, 2026
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances flagd's capability to manage feature flags in multi-tenant or hierarchical environments. By introducing multi-selector support, clients can now specify multiple flag sets to be evaluated in a defined order, enabling more granular control and dynamic configuration overrides. This change provides greater flexibility for developers to implement sophisticated flag management strategies, improving the adaptability of applications.

Highlights

  • Multi-selector Support: Introduced the ability to pass an ordered list of selectors for flag evaluation and synchronization, allowing for complex, layered flag configurations.
  • HTTP Header Integration: Added support for multi-selectors via a new "flag-selector" HTTP header (comma-separated), with backward compatibility for the existing "Flagd-Selector" header.
  • Ordered Merge Logic: Implemented ResolveAllWithSelectorMerge to evaluate flags based on multiple selectors in sequence, ensuring that results from later selectors override earlier ones for the same flag key.
Changelog
  • flagd/pkg/service/constants.go
    • Added a new constant FLAG_SELECTOR_HEADER for the preferred multi-selector HTTP header.
  • flagd/pkg/service/flag-evaluation/flag_evaluator.go
    • Updated ResolveAll to utilize the new ResolveAllWithSelectorMerge function, enabling multi-selector evaluation.
  • flagd/pkg/service/flag-evaluation/flag_evaluator_v1.go
    • Modified ResolveAll to integrate ResolveAllWithSelectorMerge for multi-selector support in the v1 evaluation service.
  • flagd/pkg/service/flag-evaluation/ofrep/handler.go
    • Changed HandleFlagEvaluation and HandleBulkEvaluation to use the new SelectorExpressionFromHTTPHeaders and ResolveAllWithSelectorMerge for processing multiple selectors.
  • flagd/pkg/service/flag-evaluation/ofrep/handler_test.go
    • Added necessary imports and a new test case to verify that HandleBulkEvaluation correctly processes the flag-selector header for ordered evaluation.
  • flagd/pkg/service/flag-evaluation/selector_merge.go
    • Introduced a new file containing ResolveAllWithSelectorMerge and splitSelectorExpression functions, which handle the logic for evaluating multiple selectors and merging their results in a defined order.
  • flagd/pkg/service/flag-sync/handler.go
    • Refactored SyncFlags and FetchAllFlags to support multi-selectors, including new helper functions parseSelectorList and fetchMergedFlags for processing and merging flags from multiple sources.
  • flagd/pkg/service/flag-sync/handler_test.go
    • Added a mockMergeStore and new test cases to validate the multi-selector override logic in FetchAllFlags and fetchMergedFlags.
  • flagd/pkg/service/selector.go
    • Created a new file with utility functions SelectorExpressionFromHTTPHeaders and SelectorExpressionFromGRPCMetadata to extract selector expressions from HTTP headers and gRPC metadata, prioritizing the new flag-selector header.
  • flagd/pkg/service/selector_test.go
    • Added new test cases to verify the correct parsing of selector expressions from HTTP headers and gRPC metadata.
Activity
  • No human activity has occurred on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces multi-selector support for flag evaluation and synchronization. However, the current implementation has critical security concerns, notably a vulnerability to Denial of Service (DoS) attacks due to the lack of limits on the number of selectors processed in a single request, affecting both evaluation and synchronization services. There's also a logic error in the OFREP single evaluation endpoint that could lead to broken access control by incorrectly parsing multi-selector headers. Additionally, inconsistencies in the flag merging logic for the sync service and redundant code in the gRPC header parsing require attention to ensure overall correctness and consistency.

Comment on lines +195 to +232
func (s syncHandler) fetchMergedFlags(ctx context.Context, selectors []store.Selector) ([]model.Flag, error) {
switch len(selectors) {
case 0:
flags, _, err := s.store.GetAll(ctx, nil)
return flags, err
case 1:
flags, _, err := s.store.GetAll(ctx, &selectors[0])
return flags, err
default:
type flagIdentifier struct {
flagSetID string
key string
}

merged := map[flagIdentifier]model.Flag{}
for _, selector := range selectors {
flags, _, err := s.store.GetAll(ctx, &selector)
if err != nil {
return nil, err
}
for _, flag := range flags {
merged[flagIdentifier{flagSetID: flag.FlagSetId, key: flag.Key}] = flag
}
}

out := make([]model.Flag, 0, len(merged))
for _, flag := range merged {
out = append(out, flag)
}
sort.Slice(out, func(i, j int) bool {
if out[i].FlagSetId != out[j].FlagSetId {
return out[i].FlagSetId < out[j].FlagSetId
}
return out[i].Key < out[j].Key
})
return out, nil
}
}
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.

security-high high

The fetchMergedFlags function, used by both SyncFlags and FetchAllFlags, is vulnerable to a Denial of Service (DoS) attack. It iterates over an unbounded list of selectors and performs a store query for each, which is an expensive operation triggered on every flag update when multiple selectors are used. This loop lacks a limit on the number of selectors and does not check for context cancellation. Additionally, the merging logic uses a composite key of (flagSetID, key), which is inconsistent with the requirement to override flags based on the FlagKey alone. This will not perform an override if two flags have the same key but different FlagSetIds. To ensure correct override behavior and mitigate the DoS vulnerability, implement a limit on the number of selectors in parseSelectorList, ensure the merge loop respects context cancellation, and use only the flag key for merging.

type flagIdentifier struct {
	key string
}

merged := map[string]model.Flag{}
for _, selector := range selectors {
	flags, _, err := s.store.GetAll(ctx, &selector)
	if err != nil {
		return nil, err
	}
	for _, flag := range flags {
		merged[flag.Key] = flag
	}

Comment on lines +13 to +62
func ResolveAllWithSelectorMerge(
ctx context.Context,
reqID string,
eval evaluator.IEvaluator,
evaluationContext map[string]any,
selectorExpression string,
) ([]evaluator.AnyValue, model.Metadata, error) {
selectors := splitSelectorExpression(selectorExpression)

switch len(selectors) {
case 0:
return eval.ResolveAllValues(ctx, reqID, evaluationContext)
case 1:
selector := store.NewSelector(selectors[0])
selectorCtx := context.WithValue(ctx, store.SelectorContextKey{}, selector)
return eval.ResolveAllValues(selectorCtx, reqID, evaluationContext)
default:
mergedValues := map[string]evaluator.AnyValue{}
mergedMetadata := model.Metadata{}

for _, selectorExpression := range selectors {
selector := store.NewSelector(selectorExpression)
selectorCtx := context.WithValue(ctx, store.SelectorContextKey{}, selector)
values, metadata, err := eval.ResolveAllValues(selectorCtx, reqID, evaluationContext)
if err != nil {
return nil, nil, err
}

for key, value := range metadata {
mergedMetadata[key] = value
}
for _, value := range values {
mergedValues[value.FlagKey] = value
}
}

keys := make([]string, 0, len(mergedValues))
for key := range mergedValues {
keys = append(keys, key)
}
sort.Strings(keys)

resolutions := make([]evaluator.AnyValue, 0, len(keys))
for _, key := range keys {
resolutions = append(resolutions, mergedValues[key])
}

return resolutions, mergedMetadata, nil
}
}
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.

security-high high

The ResolveAllWithSelectorMerge function is vulnerable to a Denial of Service (DoS) attack. It splits the selectorExpression by commas and iterates over the resulting slice without any limit on the number of selectors. An attacker can provide a large number of selectors in the request headers, causing the server to perform a full flag evaluation for each one. This process consumes significant CPU and memory, and the loop does not check for context cancellation (ctx.Done()), meaning it will run to completion even if the client disconnects.

Remediation: Implement a limit on the maximum number of selectors allowed (e.g., 10) and check the context status at the beginning of each iteration.

Comment on lines +97 to 98
selectorExpression := service.SelectorExpressionFromHTTPHeaders(r.Header)
selector := store.NewSelector(selectorExpression)
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.

security-medium medium

In HandleFlagEvaluation, the selectorExpression (which can now be a comma-separated list of multiple selectors) is passed directly to store.NewSelector. This is a critical logic error as store.NewSelector is designed for a single selector, leading to incorrect parsing or an empty selector. This can cause the evaluator to fall back to the highest priority flag across all sources, potentially bypassing intended isolation between flag sets (e.g., tenant isolation), which is a broken access control vulnerability. This behavior is inconsistent with HandleBulkEvaluation. Update this endpoint to correctly handle multi-selectors, for example by splitting the expression and using the first valid selector if multi-merge is not yet supported for single evaluations.

Comment on lines +17 to +28
func SelectorExpressionFromGRPCMetadata(md metadata.MD) string {
if selectors := strings.TrimSpace(strings.Join(md.Get(strings.ToLower(FLAG_SELECTOR_HEADER)), ",")); selectors != "" {
return selectors
}
if selectors := strings.TrimSpace(strings.Join(md.Get(strings.ToLower(FLAGD_SELECTOR_HEADER)), ",")); selectors != "" {
return selectors
}
if selectors := strings.TrimSpace(strings.Join(md.Get(FLAG_SELECTOR_HEADER), ",")); selectors != "" {
return selectors
}
return strings.TrimSpace(strings.Join(md.Get(FLAGD_SELECTOR_HEADER), ","))
}
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.

medium

The logic for reading gRPC metadata is a bit complex and contains redundancy. Specifically, FLAG_SELECTOR_HEADER is checked twice. Also, gRPC metadata keys are typically normalized to lowercase, so checking for the original case of headers is often unnecessary and can be confusing.

Simplifying this function would improve readability and maintainability while retaining the correct behavior of prioritizing the new flag-selector header.

func SelectorExpressionFromGRPCMetadata(md metadata.MD) string {
	// gRPC metadata keys are conventionally lowercase.
	if selectors := strings.TrimSpace(strings.Join(md.Get(strings.ToLower(FLAG_SELECTOR_HEADER)), ",")); selectors != "" {
		return selectors
	}

	// Fallback to the old header.
	return strings.TrimSpace(strings.Join(md.Get(strings.ToLower(FLAGD_SELECTOR_HEADER)), ","))
}

@Nilushiya Nilushiya changed the title Feature: Add multi-selector support to flagd feat: Add multi-selector support to flagd Mar 16, 2026
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
@Nilushiya Nilushiya force-pushed the multi-tenant-merge branch from e62006b to af7a261 Compare March 16, 2026 04:44
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Mar 17, 2026
cupofcat and others added 10 commits March 17, 2026 06:43
This PR introduces a new Architecture Decision Record (ADR) establishing
a Tiered Support Model for flagd language providers.

Motivation
As the flagd ecosystem grows, the maturity and feature parity of
different language providers have varied. This inconsistency can confuse
adopters about which SDKs are "production-ready" versus experimental.

This policy aims to:

- Set clear expectations for users regarding stability, feature parity,
and SLAs.
- Define a clear path for community contributions to graduate from
"Incubation" to "Core Supported."
- Formalize governance roles (Core Maintainers vs. Language
Maintainers).
- Set a clear expectations for releases

Related issues:
open-feature#1750

---------

Signed-off-by: Maks Osowski <maks@google.com>
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
See title.

Go tests can often contain some duplication, and sonar seems to be
particularly annoyed with this. Or Go tests seem perfectly idiomatic for
me. We use tables in most cases, but sonar still seems to think there's
too much duplication in tests.

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
See title

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
…eature#1892)

Adds configuration (and defaults) for:
- max request body size
- max header size

---------

Signed-off-by: marcozabel <marco.zabel@dynatrace.com>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
…e#1897)

Update vulnerable OTel deps and update to Go 1.25.

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
🤖 I have created a release *beep* *boop*
---

<details><summary>flagd: 0.14.2</summary>

##
[0.14.2](open-feature/flagd@flagd/v0.14.1...flagd/v0.14.2)
(2026-03-09)

### 🐛 Bug Fixes

* **security:** update otel deps, minimum core Go version
([open-feature#1897](open-feature#1897))
([6b79bf8](open-feature@6b79bf8))

### ✨ New Features

* make max header and body size configurable, add default
([open-feature#1892](open-feature#1892))
([25c5fd7](open-feature@25c5fd7))

### 🧹 Chore

* reduce duplication in some tests
([open-feature#1895](open-feature#1895))
([4a82812](open-feature@4a82812))
</details>

<details><summary>flagd-proxy: 0.9.1</summary>

##
[0.9.1](open-feature/flagd@flagd-proxy/v0.9.0...flagd-proxy/v0.9.1)
(2026-03-09)

### 🐛 Bug Fixes

* **security:** update otel deps, minimum core Go version
([open-feature#1897](open-feature#1897))
([6b79bf8](open-feature@6b79bf8))
</details>

<details><summary>core: 0.14.1</summary>

##
[0.14.1](open-feature/flagd@core/v0.14.0...core/v0.14.1)
(2026-03-09)

### 🐛 Bug Fixes

* **security:** update otel deps, minimum core Go version
([open-feature#1897](open-feature#1897))
([6b79bf8](open-feature@6b79bf8))

### ✨ New Features

* make max header and body size configurable, add default
([open-feature#1892](open-feature#1892))
([25c5fd7](open-feature@25c5fd7))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Signed-off-by: OpenFeature Bot <109696520+openfeaturebot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
…r header (open-feature#1900)

## Summary

- Replaces the inline `cors.Options` in the OFREP service with the
shared CORS middleware from `pkg/service/middleware/cors/`
- This adds `AllowedHeaders: []string{"*"}` to OFREP preflight
responses, unblocking the `Flagd-Selector` header from browser clients

## Problem

The OFREP service created its own `cors.Options` at
`ofrep_service.go:40` without setting `AllowedHeaders`. The `rs/cors`
library defaults to only allowing `accept`, `content-type`, and
`x-requested-with` — so browser preflight `OPTIONS` requests with
`Access-Control-Request-Headers: flagd-selector` were being rejected.

The handler explicitly reads this header at `handler.go:99` and
`handler.go:124`, so it's expected to be sent from browser clients.
Meanwhile, the Connect service already uses the shared CORS middleware
which sets `AllowedHeaders: []string{"*"}`.

Reported in [this Slack
thread](https://cloud-native.slack.com/archives/C066A48LK35/p1773065350366859).

## Fix

Swapped the inline `cors.New(cors.Options{...})` for
`corsmw.New(origins)` from the shared middleware package. This brings
OFREP in line with the Connect service and prevents future CORS config
divergence between the two.

### Related Issues

Fixes open-feature#1899

---------

Signed-off-by: Norris <jonathan.norris@dynatrace.com>
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
🤖 I have created a release *beep* *boop*
---

<details><summary>flagd: 0.14.3</summary>

##
[0.14.3](open-feature/flagd@flagd/v0.14.2...flagd/v0.14.3)
(2026-03-10)

### 🐛 Bug Fixes

* OFREP service CORS missing AllowedHeaders - blocks Flagd-Selector
header ([open-feature#1900](open-feature#1900))
([08f0429](open-feature@08f0429))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Signed-off-by: OpenFeature Bot <109696520+openfeaturebot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
Add docs about graceful defaulting.

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
Proposes a "rollout operator". Facilitates linear, progressive rollouts
as a fundamental feature flag use case: gradually shifting traffic from
one variant to another over time.

See ADR for justification and full proposal.

See also:
- draft implementation **WITH DEMO** in flagd:
open-feature#1868
  - run demo with: `make build-flagd && ./demo-rollout.sh`
- draft schema changes:
open-feature/flagd-schemas#205

![output](https://github.com/user-attachments/assets/7eaeafea-bc60-40db-a1ac-65d9c15f82a4)

---------

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
@Nilushiya Nilushiya force-pushed the multi-tenant-merge branch from 05b427e to 4055d8f Compare March 17, 2026 01:14
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. size:XXL This PR changes 1000+ lines, ignoring generated files. labels Mar 17, 2026
Nilushiya.K added 2 commits March 17, 2026 07:03
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
Signed-off-by: Nilushiya.K <Nilushiya.K@cloudsolutions.com.sa>
@Nilushiya Nilushiya force-pushed the multi-tenant-merge branch from 780fc63 to 70c56fd Compare March 17, 2026 01:33
@dosubot dosubot bot removed the size:XL This PR changes 500-999 lines, ignoring generated files. label Mar 17, 2026
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Mar 17, 2026
@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:XXL This PR changes 1000+ lines, ignoring generated files. labels Mar 17, 2026
@sonarqubecloud
Copy link
Copy Markdown

@toddbaert
Copy link
Copy Markdown
Member

Hey @Nilushiya - we intentionally did not implement this so far. Hypothetically it's possible (as well as other interesting options, such as selecting flags with particular metadata or other characteristics) but I don't think we understand our use-cases well-enough now to accept this work.

I would start with opening an issue, or an enhancement to this architecture document to discuss justifications and options.

@Nilushiya
Copy link
Copy Markdown
Author

Nilushiya commented Mar 25, 2026 via email

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

Labels

size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants