From 0c4b674aba7f2ec126eda1483f6dceb8e9d0b9b5 Mon Sep 17 00:00:00 2001 From: Pavel Date: Tue, 25 Aug 2026 16:06:13 +0300 Subject: [PATCH 1/7] Guard eager OpenFeature init and add detailed /ffe/evaluate for Go agentless configuration contract --- utils/build/docker/golang/parametric/ffe.go | 152 +++++++++++++++++-- utils/build/docker/golang/parametric/main.go | 39 ++++- 2 files changed, 175 insertions(+), 16 deletions(-) diff --git a/utils/build/docker/golang/parametric/ffe.go b/utils/build/docker/golang/parametric/ffe.go index 14ac1aa24ff..755636c85dd 100644 --- a/utils/build/docker/golang/parametric/ffe.go +++ b/utils/build/docker/golang/parametric/ffe.go @@ -3,12 +3,40 @@ package main import ( "encoding/json" "net/http" + "sync" + ddof "github.com/DataDog/dd-trace-go/v2/openfeature" of "github.com/open-feature/go-sdk/openfeature" ) -func (s *apmClientServer) ffeStart(http.ResponseWriter, *http.Request) { - return +var ffeStartOnce sync.Once + +func (s *apmClientServer) ffeStart(writer http.ResponseWriter, request *http.Request) { + var startErr error + ffeStartOnce.Do(func() { + provider, err := ddof.NewDatadogProvider(ddof.ProviderConfig{}) + if err != nil { + startErr = err + return + } + + if err := of.SetProvider(provider); err != nil { + startErr = err + return + } + + s.ddProvider = provider + s.ofClient = of.NewClient("system-tests-weblog-client") + }) + + if startErr != nil { + writer.WriteHeader(http.StatusInternalServerError) + _ = json.NewEncoder(writer).Encode(map[string]string{"error": startErr.Error()}) + return + } + + writer.WriteHeader(http.StatusOK) + _ = json.NewEncoder(writer).Encode(map[string]any{}) } func (s *apmClientServer) ffeEval(writer http.ResponseWriter, request *http.Request) { @@ -24,21 +52,127 @@ func (s *apmClientServer) ffeEval(writer http.ResponseWriter, request *http.Requ return } - ctx := of.NewEvaluationContext(body.TargetingKey, body.Attributes) + if s.ofClient == nil { + writer.WriteHeader(http.StatusInternalServerError) + _ = json.NewEncoder(writer).Encode(map[string]string{"error": "FFE provider not initialized"}) + return + } - if initer, ok := s.ddProvider.(of.StateHandler); ok { - initer.Init(ctx) + switch body.VariationType { + case "BOOLEAN", "STRING", "INTEGER", "NUMERIC", "JSON": + default: + http.Error(writer, "unknown variation type: "+body.VariationType, http.StatusBadRequest) + return } - val := s.ofClient.Object(request.Context(), body.Flag, body.DefaultValue, ctx) + ctx := of.NewEvaluationContext(body.TargetingKey, body.Attributes) - writer.WriteHeader(http.StatusOK) + value := body.DefaultValue + reason := string(of.DefaultReason) + var errorCode string + + evalCtx := request.Context() + func() { + defer func() { + if r := recover(); r != nil { + value = body.DefaultValue + reason = "ERROR" + } + }() + + switch body.VariationType { + case "BOOLEAN": + defaultValue, _ := body.DefaultValue.(bool) + details, err := s.ofClient.BooleanValueDetails(evalCtx, body.Flag, defaultValue, ctx) + if err != nil { + value = body.DefaultValue + reason = "ERROR" + return + } + value = details.Value + reason = string(details.Reason) + errorCode = string(details.ErrorCode) + case "STRING": + defaultValue, _ := body.DefaultValue.(string) + details, err := s.ofClient.StringValueDetails(evalCtx, body.Flag, defaultValue, ctx) + if err != nil { + value = body.DefaultValue + reason = "ERROR" + return + } + value = details.Value + reason = string(details.Reason) + errorCode = string(details.ErrorCode) + case "INTEGER": + defaultValue, _ := toInt64(body.DefaultValue) + details, err := s.ofClient.IntValueDetails(evalCtx, body.Flag, defaultValue, ctx) + if err != nil { + value = body.DefaultValue + reason = "ERROR" + return + } + value = details.Value + reason = string(details.Reason) + errorCode = string(details.ErrorCode) + case "NUMERIC": + defaultValue, _ := toFloat64(body.DefaultValue) + details, err := s.ofClient.FloatValueDetails(evalCtx, body.Flag, defaultValue, ctx) + if err != nil { + value = body.DefaultValue + reason = "ERROR" + return + } + value = details.Value + reason = string(details.Reason) + errorCode = string(details.ErrorCode) + case "JSON": + details, err := s.ofClient.ObjectValueDetails(evalCtx, body.Flag, body.DefaultValue, ctx) + if err != nil { + value = body.DefaultValue + reason = "ERROR" + return + } + value = details.Value + reason = string(details.Reason) + errorCode = string(details.ErrorCode) + } + }() + + writer.WriteHeader(http.StatusOK) response := struct { - Value any `json:"value"` - }{val} + Value any `json:"value"` + Reason string `json:"reason"` + ErrorCode string `json:"errorCode"` + }{value, reason, errorCode} if err := json.NewEncoder(writer).Encode(response); err != nil { http.Error(writer, "failed to encode response: "+err.Error(), http.StatusInternalServerError) } } + +func toInt64(v any) (int64, bool) { + switch n := v.(type) { + case int64: + return n, true + case int: + return int64(n), true + case float64: + return int64(n), true + default: + return 0, false + } +} + +func toFloat64(v any) (float64, bool) { + switch n := v.(type) { + case float64: + return n, true + case int: + return float64(n), true + case int64: + return float64(n), true + default: + return 0, false + } +} diff --git a/utils/build/docker/golang/parametric/main.go b/utils/build/docker/golang/parametric/main.go index 509c3fd172c..9c68360570a 100644 --- a/utils/build/docker/golang/parametric/main.go +++ b/utils/build/docker/golang/parametric/main.go @@ -58,19 +58,44 @@ func newServer() *apmClientServer { instruments: make(map[string]interface{}), } - s.ddProvider, err = ddof.NewDatadogProvider(ddof.ProviderConfig{}) - if err != nil { - log.Fatalf("failed to create Datadog OpenFeature provider: %v", err) - } + // The configuration-source contract requires lazy activation: no configuration + // delivery may happen before the provider is accessed through /ffe/start. When any + // Feature Flagging configuration variable is set, skip this eager initialization and + // leave provider setup to /ffe/start. Tests that predate that contract keep the + // original eager behavior. + if !ffeConfigurationEnvVarsSet() { + s.ddProvider, err = ddof.NewDatadogProvider(ddof.ProviderConfig{}) + if err != nil { + log.Fatalf("failed to create Datadog OpenFeature provider: %v", err) + } - if err := of.SetProvider(s.ddProvider); err != nil { - log.Fatalf("failed to set Datadog OpenFeature provider and wait for initialization: %v", err) + if err := of.SetProvider(s.ddProvider); err != nil { + log.Fatalf("failed to set Datadog OpenFeature provider and wait for initialization: %v", err) + } + + s.ofClient = of.NewClient("system-tests-weblog-client") } - s.ofClient = of.NewClient("system-tests-weblog-client") return s } +var ffeConfigurationEnvVars = []string{ + "DD_FEATURE_FLAGS_ENABLED", + "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE", + "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL", + "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS", + "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS", +} + +func ffeConfigurationEnvVarsSet() bool { + for _, name := range ffeConfigurationEnvVars { + if _, ok := os.LookupEnv(name); ok { + return true + } + } + return false +} + func main() { flag.String("Darg1", "", "Argument 1") flag.Parse() From 3d5729fb27b404e5e905bf683f6082890a234fd5 Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 11 Sep 2026 18:01:37 +0300 Subject: [PATCH 2/7] fix(golang): block /ffe/start on provider init and stop filtering RC polls by verb --- tests/parametric/test_ffe/test_configuration_sources.py | 5 ++++- utils/build/docker/golang/parametric/ffe.go | 5 ++++- utils/build/docker/golang/parametric/main.go | 6 +++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/parametric/test_ffe/test_configuration_sources.py b/tests/parametric/test_ffe/test_configuration_sources.py index 195edddd1ad..688f9fbdae9 100644 --- a/tests/parametric/test_ffe/test_configuration_sources.py +++ b/tests/parametric/test_ffe/test_configuration_sources.py @@ -151,8 +151,11 @@ def _assert_no_mock_requests(mock_ffe_agentless_backend: MockFFEAgentlessBackend def _remote_config_products(test_agent: TestAgentAPI) -> set[str]: + # Not post_only: golang polls /v0.7/config with GET, every other library with + # POST. The verb says nothing about which products the body advertises, and + # the capability assertion next to this one is already unfiltered. products: set[str] = set() - for request in test_agent.rc_requests(post_only=True): + for request in test_agent.rc_requests(): client = request["body"].get("client", {}) products.update(client.get("products", [])) return products diff --git a/utils/build/docker/golang/parametric/ffe.go b/utils/build/docker/golang/parametric/ffe.go index 755636c85dd..d17c115ff02 100644 --- a/utils/build/docker/golang/parametric/ffe.go +++ b/utils/build/docker/golang/parametric/ffe.go @@ -20,7 +20,10 @@ func (s *apmClientServer) ffeStart(writer http.ResponseWriter, request *http.Req return } - if err := of.SetProvider(provider); err != nil { + // AndWait: plain SetProvider returns before Init, so /ffe/start would + // answer 200 with no configuration and the next evaluation gets the + // default. Other SDKs block on initialize inside set_provider. + if err := of.SetProviderAndWait(provider); err != nil { startErr = err return } diff --git a/utils/build/docker/golang/parametric/main.go b/utils/build/docker/golang/parametric/main.go index 9c68360570a..040e58288b3 100644 --- a/utils/build/docker/golang/parametric/main.go +++ b/utils/build/docker/golang/parametric/main.go @@ -69,8 +69,12 @@ func newServer() *apmClientServer { log.Fatalf("failed to create Datadog OpenFeature provider: %v", err) } + // Async on purpose, unlike /ffe/start: this runs for every parametric + // test that sets no Feature Flagging variable, and waiting would add the + // 10s DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS to each + // container start. if err := of.SetProvider(s.ddProvider); err != nil { - log.Fatalf("failed to set Datadog OpenFeature provider and wait for initialization: %v", err) + log.Fatalf("failed to set Datadog OpenFeature provider: %v", err) } s.ofClient = of.NewClient("system-tests-weblog-client") From 136dd5ee92c1e8ed14441fe3d3a9f1e581c930b7 Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 11 Sep 2026 21:56:19 +0300 Subject: [PATCH 3/7] chore: re-run CI against the tracer fix branch From b9bbe44a6f741e8ed589c6c00c5519b7cdee2d9f Mon Sep 17 00:00:00 2001 From: Pavel Date: Mon, 14 Sep 2026 09:41:13 +0300 Subject: [PATCH 4/7] fix(golang/parametric): treat PROVIDER_NOT_READY as a successful /ffe/start --- utils/build/docker/golang/parametric/ffe.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/utils/build/docker/golang/parametric/ffe.go b/utils/build/docker/golang/parametric/ffe.go index d17c115ff02..a53dcacdd0f 100644 --- a/utils/build/docker/golang/parametric/ffe.go +++ b/utils/build/docker/golang/parametric/ffe.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "errors" "net/http" "sync" @@ -23,9 +24,15 @@ func (s *apmClientServer) ffeStart(writer http.ResponseWriter, request *http.Req // AndWait: plain SetProvider returns before Init, so /ffe/start would // answer 200 with no configuration and the next evaluation gets the // default. Other SDKs block on initialize inside set_provider. + // + // PROVIDER_NOT_READY is not a start failure: the provider is registered + // and evaluations return defaults until configuration arrives. if err := of.SetProviderAndWait(provider); err != nil { - startErr = err - return + var initErr *of.ProviderInitError + if !errors.As(err, &initErr) || initErr.ErrorCode != of.ProviderNotReadyCode { + startErr = err + return + } } s.ddProvider = provider From b56ca81cf2b0e3f7c1e7a5f8a7131a8841fc0768 Mon Sep 17 00:00:00 2001 From: Pavel Date: Mon, 14 Sep 2026 18:14:06 +0300 Subject: [PATCH 5/7] fix(golang/parametric): bound the /ffe/start wait with our own deadline --- utils/build/docker/golang/parametric/ffe.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/utils/build/docker/golang/parametric/ffe.go b/utils/build/docker/golang/parametric/ffe.go index a53dcacdd0f..5f6376397ed 100644 --- a/utils/build/docker/golang/parametric/ffe.go +++ b/utils/build/docker/golang/parametric/ffe.go @@ -1,15 +1,22 @@ package main import ( + "context" "encoding/json" "errors" "net/http" "sync" + "time" ddof "github.com/DataDog/dd-trace-go/v2/openfeature" of "github.com/open-feature/go-sdk/openfeature" ) +// ffeStartTimeout bounds the wait for the first configuration. Above the 10s +// DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS default, so the +// tracer's own timeout governs whenever it applies one. +const ffeStartTimeout = 15 * time.Second + var ffeStartOnce sync.Once func (s *apmClientServer) ffeStart(writer http.ResponseWriter, request *http.Request) { @@ -21,13 +28,19 @@ func (s *apmClientServer) ffeStart(writer http.ResponseWriter, request *http.Req return } - // AndWait: plain SetProvider returns before Init, so /ffe/start would + // Wait for Init: plain SetProvider returns before it, so /ffe/start would // answer 200 with no configuration and the next evaluation gets the // default. Other SDKs block on initialize inside set_provider. // + // The deadline is ours rather than SetProviderAndWait's background + // context, so tracers that only bound Init still cannot hang the suite. + // // PROVIDER_NOT_READY is not a start failure: the provider is registered // and evaluations return defaults until configuration arrives. - if err := of.SetProviderAndWait(provider); err != nil { + ctx, cancel := context.WithTimeout(context.Background(), ffeStartTimeout) + defer cancel() + + if err := of.SetProviderWithContextAndWait(ctx, provider); err != nil { var initErr *of.ProviderInitError if !errors.As(err, &initErr) || initErr.ErrorCode != of.ProviderNotReadyCode { startErr = err From d0f849de01e8b0d99bd99383b72d6d6d216ebe63 Mon Sep 17 00:00:00 2001 From: Pavel Date: Tue, 15 Sep 2026 10:21:57 +0300 Subject: [PATCH 6/7] fix(golang/parametric): keep evaluation details on error and allow /ffe/start to retry --- utils/build/docker/golang/parametric/ffe.go | 95 ++++++++++----------- 1 file changed, 44 insertions(+), 51 deletions(-) diff --git a/utils/build/docker/golang/parametric/ffe.go b/utils/build/docker/golang/parametric/ffe.go index 5f6376397ed..0a164c8b888 100644 --- a/utils/build/docker/golang/parametric/ffe.go +++ b/utils/build/docker/golang/parametric/ffe.go @@ -17,15 +17,25 @@ import ( // tracer's own timeout governs whenever it applies one. const ffeStartTimeout = 15 * time.Second -var ffeStartOnce sync.Once +// ffeStartMu guards ffeStarted, which records a *successful* start. sync.Once +// would burn its one shot on a failed attempt and then report 200 with no +// client, so a retry could never recover. +var ( + ffeStartMu sync.Mutex + ffeStarted bool +) func (s *apmClientServer) ffeStart(writer http.ResponseWriter, request *http.Request) { - var startErr error - ffeStartOnce.Do(func() { + startErr := func() error { + ffeStartMu.Lock() + defer ffeStartMu.Unlock() + if ffeStarted { + return nil + } + provider, err := ddof.NewDatadogProvider(ddof.ProviderConfig{}) if err != nil { - startErr = err - return + return err } // Wait for Init: plain SetProvider returns before it, so /ffe/start would @@ -43,14 +53,15 @@ func (s *apmClientServer) ffeStart(writer http.ResponseWriter, request *http.Req if err := of.SetProviderWithContextAndWait(ctx, provider); err != nil { var initErr *of.ProviderInitError if !errors.As(err, &initErr) || initErr.ErrorCode != of.ProviderNotReadyCode { - startErr = err - return + return err } } s.ddProvider = provider s.ofClient = of.NewClient("system-tests-weblog-client") - }) + ffeStarted = true + return nil + }() if startErr != nil { writer.WriteHeader(http.StatusInternalServerError) @@ -104,61 +115,43 @@ func (s *apmClientServer) ffeEval(writer http.ResponseWriter, request *http.Requ } }() + // The SDK returns evaluation details alongside an error, and the retry + // helper keys on errorCode PROVIDER_NOT_READY, so the details have to + // survive the error path rather than collapsing to a bare "ERROR". + var ( + details of.EvaluationDetails + err error + ) + switch body.VariationType { case "BOOLEAN": defaultValue, _ := body.DefaultValue.(bool) - details, err := s.ofClient.BooleanValueDetails(evalCtx, body.Flag, defaultValue, ctx) - if err != nil { - value = body.DefaultValue - reason = "ERROR" - return - } - value = details.Value - reason = string(details.Reason) - errorCode = string(details.ErrorCode) + d, e := s.ofClient.BooleanValueDetails(evalCtx, body.Flag, defaultValue, ctx) + value, details, err = d.Value, d.EvaluationDetails, e case "STRING": defaultValue, _ := body.DefaultValue.(string) - details, err := s.ofClient.StringValueDetails(evalCtx, body.Flag, defaultValue, ctx) - if err != nil { - value = body.DefaultValue - reason = "ERROR" - return - } - value = details.Value - reason = string(details.Reason) - errorCode = string(details.ErrorCode) + d, e := s.ofClient.StringValueDetails(evalCtx, body.Flag, defaultValue, ctx) + value, details, err = d.Value, d.EvaluationDetails, e case "INTEGER": defaultValue, _ := toInt64(body.DefaultValue) - details, err := s.ofClient.IntValueDetails(evalCtx, body.Flag, defaultValue, ctx) - if err != nil { - value = body.DefaultValue - reason = "ERROR" - return - } - value = details.Value - reason = string(details.Reason) - errorCode = string(details.ErrorCode) + d, e := s.ofClient.IntValueDetails(evalCtx, body.Flag, defaultValue, ctx) + value, details, err = d.Value, d.EvaluationDetails, e case "NUMERIC": defaultValue, _ := toFloat64(body.DefaultValue) - details, err := s.ofClient.FloatValueDetails(evalCtx, body.Flag, defaultValue, ctx) - if err != nil { - value = body.DefaultValue - reason = "ERROR" - return - } - value = details.Value - reason = string(details.Reason) - errorCode = string(details.ErrorCode) + d, e := s.ofClient.FloatValueDetails(evalCtx, body.Flag, defaultValue, ctx) + value, details, err = d.Value, d.EvaluationDetails, e case "JSON": - details, err := s.ofClient.ObjectValueDetails(evalCtx, body.Flag, body.DefaultValue, ctx) - if err != nil { - value = body.DefaultValue + d, e := s.ofClient.ObjectValueDetails(evalCtx, body.Flag, body.DefaultValue, ctx) + value, details, err = d.Value, d.EvaluationDetails, e + } + + reason = string(details.Reason) + errorCode = string(details.ErrorCode) + if err != nil { + value = body.DefaultValue + if reason == "" { reason = "ERROR" - return } - value = details.Value - reason = string(details.Reason) - errorCode = string(details.ErrorCode) } }() From a21571b58a8eeca743bf02ea713c78238165f567 Mon Sep 17 00:00:00 2001 From: Pavel Date: Tue, 15 Sep 2026 10:55:49 +0300 Subject: [PATCH 7/7] feat(golang): declare FFE agentless configuration-source support from v2.11.0-dev --- manifests/golang.yml | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/manifests/golang.yml b/manifests/golang.yml index 41b9806a673..cf56b613766 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -1327,7 +1327,7 @@ manifest: tests/debugger/test_debugger_symdb.py::Test_Debugger_SymDb::test_event_metadata: missing_feature (extended event schema not yet shipped) tests/debugger/test_debugger_telemetry.py::Test_Debugger_Telemetry: missing_feature tests/docker_ssi/test_docker_ssi_appsec.py::TestDockerSSIAppsecFeatures::test_telemetry_source_ssi: v2.0.0 - tests/ffe/test_agentless_configuration.py: missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks the system-tests contract) + tests/ffe/test_agentless_configuration.py: v2.11.0-dev tests/ffe/test_dynamic_evaluation.py::Test_FFE_Flag_Parse_Error_Isolation::test_valid_flag_unaffected: # TODO: a lower version might be supported - declaration: bug (FFL-2184) component_version: <2.10.0 @@ -1597,28 +1597,7 @@ manifest: tests/parametric/test_extract_behavior.py::Test_ExtractBehavior_Ignore: incomplete_test_app (The parametric test app does not apply DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT=ignore) tests/parametric/test_extract_behavior.py::Test_ExtractBehavior_Restart: incomplete_test_app (The parametric test app does not emit restart span links or preserve baggage) tests/parametric/test_extract_behavior.py::Test_ExtractBehavior_Restart_With_Extract_First: missing_feature (DD_TRACE_PROPAGATION_EXTRACT_FIRST=true is unsupported; dd-trace-go panics when extraction fails) - tests/parametric/test_ffe/test_configuration_sources.py: # TODO: a lower version might be supported - - declaration: missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) - component_version: <2.10.0 - tests/parametric/test_ffe/test_configuration_sources.py::Test_Feature_Flag_Configuration_Source_Cold_Failure_And_Recovery: missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) - tests/parametric/test_ffe/test_configuration_sources.py::Test_Feature_Flag_Configuration_Source_Poller_Concurrency: missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) - ? tests/parametric/test_ffe/test_configuration_sources.py::Test_Feature_Flag_Configuration_Source_Selection::test_default_agentless_positive - : missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) - ? tests/parametric/test_ffe/test_configuration_sources.py::Test_Feature_Flag_Configuration_Source_Selection::test_explicit_agentless_wins_over_legacy_true - : missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) - ? tests/parametric/test_ffe/test_configuration_sources.py::Test_Feature_Flag_Configuration_Source_Selection::test_explicit_remote_config_wins_over_legacy_false - : missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) - ? tests/parametric/test_ffe/test_configuration_sources.py::Test_Feature_Flag_Configuration_Source_Selection::test_legacy_true_preserves_remote_config - : missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) - ? tests/parametric/test_ffe/test_configuration_sources.py::Test_Feature_Flag_Configuration_Source_Selection::test_provider_kill_switch_overrides_legacy_true - : missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) - ? tests/parametric/test_ffe/test_configuration_sources.py::Test_Feature_Flag_Configuration_Source_Selection::test_remote_config_positive_ignores_agentless_env - : missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) - ? tests/parametric/test_ffe/test_configuration_sources.py::Test_Feature_Flag_Configuration_Source_Selection::test_remote_config_without_rc_does_not_fallback_to_agentless - : missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) - ? tests/parametric/test_ffe/test_configuration_sources.py::Test_Feature_Flag_Configuration_Source_Selection::test_stable_true_preserves_default_agentless - : missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) - tests/parametric/test_ffe/test_configuration_sources.py::Test_Feature_Flag_Configuration_Source_Warm_State_Preservation: missing_feature (FFL-2695 tracks Go agentless configuration-source implementation; FFL-2731 tracks system-tests configuration-source contract) + tests/parametric/test_ffe/test_configuration_sources.py: v2.11.0-dev tests/parametric/test_ffe/test_dynamic_evaluation.py::Test_Feature_Flag_Dynamic_Evaluation: v2.5.0-dev tests/parametric/test_ffe/test_span_enrichment.py: '>=2.10.0' # TODO: a lower version might be supported tests/parametric/test_ffe/test_span_enrichment.py::Test_Span_Enrichment_Child_Span_Propagation: missing_feature