Skip to content

[FFL-1694] feat(core): evaluate variant-key flag dependencies - #414

Draft
typotter wants to merge 1 commit into
mainfrom
dependent-flags-variant-key-evaluator
Draft

typotter wants to merge 1 commit into
mainfrom
dependent-flags-variant-key-evaluator

Conversation

@typotter

@typotter typotter commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

Motivation

The JavaScript rules evaluator currently resolves conditions only from request attributes. UFC can now reference another flag's variant key, so the evaluator needs bounded graph traversal with deterministic telemetry and the Node provider must publish prerequisite events through its normal channels.

Linear: FFL-1694 — [SDK][JS] Support Dependent Flags

System context

flowchart LR
    UFC["UFC JSON or protobuf"] --> Core["Flagging core evaluator"]
    Core --> Session["Lazy dependency evaluation session"]
    Session --> Result["Root resolution"]
    Session --> Telemetry["Evaluation and exposure candidates"]
    Telemetry --> Provider["Node OpenFeature provider"]
    Provider --> Channels["Diagnostics and exposure channels"]

    classDef changed fill:#e36209,color:#fff,stroke:#8a3800,stroke-width:3px
    class Core,Session,Telemetry,Provider changed
Loading

This PR changes the highlighted evaluator and provider path. Precomputed assignments remain unchanged.

Changes

  • Parse key-only flagEvaluation conditions for JSON and protobuf UFC.
  • Support dependent ONE_OF and NOT_ONE_OF comparisons against prerequisite variant keys.
  • Create a dependency session lazily when evaluation reaches its first dependent condition, avoiding session overhead for ordinary flags.
  • Memoize each prerequisite once per root evaluation.
  • Track the active traversal stack to detect cycles.
  • Apply an inclusive maximum dependency depth, defaulting to 10 and accepting overrides from 0 through 255.
  • Propagate missing, malformed, cyclic, and over-depth prerequisite failures to the root.
  • Emit prerequisite evaluation telemetry as each node completes or receives a propagated error.
  • Buffer only prerequisite exposure candidates and publish them only after the root succeeds.
  • Publish successful dependency exposures depth-first before the root through the Node provider's existing exposure handling and deduplication.
  • Add an optional dependency-evaluation diagnostics channel to the Node provider.
  • Pin the canonical dependent-flags fixture revision and run its root evaluation result cases through the existing shared fixture runners.
  • Retain focused tests for dependency telemetry, memoization, transactional exposures, protobuf parity, and Node provider plumbing that the shared fixture contract does not express.

Evaluation flow implemented by this PR

sequenceDiagram
    participant Caller
    participant Root as Root evaluator
    participant Condition as Dependent condition
    participant Session as Lazy dependency session
    participant Prereq as Prerequisite evaluator
    participant Eval as Evaluation telemetry
    participant Exposures as Exposure buffer
    participant Provider as Node provider

    Caller->>Root: evaluate root flag
    Root->>Condition: evaluate conditions in declaration order
    alt no dependent condition is reached
        Condition-->>Root: ordinary attribute result
    else dependent condition is reached
        Condition->>Session: create on first dependency
        Session->>Session: check depth, active stack, and memo
        Session->>Prereq: evaluate prerequisite
        Prereq-->>Session: variant key or error
        Session->>Eval: emit completed evaluation immediately
        alt prerequisite succeeds
            Session->>Exposures: append candidate
            Session-->>Condition: prerequisite variant key
        else prerequisite fails
            Session-->>Root: propagate graph error
            Root-->>Caller: caller default and normalized error
            Note over Exposures: discard all candidates
        end
    end
    Root-->>Caller: root result
    opt root succeeds after dependencies
        Root->>Provider: commit dependency exposures
        Provider->>Provider: deduplicate and publish dependencies
        Provider->>Provider: publish root exposure last
    end
Loading

Test instructions

Run the focused core and provider suites, then the repository checks:

yarn workspace @datadog/flagging-core test flags-v1.spec.ts
yarn workspace @datadog/flagging-core test dependent-flags.spec.ts
yarn workspace @datadog/openfeature-node-server test dependent-flags.spec.ts
yarn lint
yarn build

The shared fixture runner covers root evaluation results for these behaviors:

  • Direct and transitive JSON dependencies
  • ONE_OF and NOT_ONE_OF
  • Declaration-order short-circuiting
  • Missing prerequisite propagation
  • Cycle detection
  • Inclusive depth enforcement and invalid depth parsing

Retain focused handwritten tests where the shared fixture contract cannot provide coverage:

  • Per-root memoization
  • Immediate evaluation telemetry
  • Atomic exposure rollback and depth-first dependency exposures before the root
  • Lazy session creation and no dependency-session overhead for ordinary flags
  • Protobuf decoding and traversal parity
  • Node provider diagnostics-channel, exposure-publication, and deduplication plumbing

Compatibility and risk

  • Existing public evaluation calls remain source-compatible because the new callbacks and provider diagnostics channel are optional.
  • Ordinary flags do not allocate a dependency session.
  • Precomputed evaluation is unchanged.
  • The highest-risk area is telemetry ordering and rollback when a later dependency fails; focused evaluator and provider tests pin that behavior.
  • Delivery must not send dependent UFC to SDK versions that do not support it.
  • Reverting this PR restores the previous evaluator behavior; dependent UFC must remain gated during rollback.

Checklist

  • Updated documentation for the new UFC shape
  • Tested core evaluation locally
  • Tested Node provider integration locally
  • Retained focused JSON/protobuf and telemetry tests not expressible through the shared fixture contract
  • Added provider telemetry tests
  • Added or updated cross-SDK fixture coverage
  • Tested on staging — not applicable until backend producer and capability gating are available

Add bounded dependent-flag traversal for JSON and protobuf UFC with propagated graph failures and transactional prerequisite exposures.

Environment: Datadog workspace

Co-Authored-By: Codex GPT-5 <noreply@localhost>
@linear-code

linear-code Bot commented Oct 9, 2026

Copy link
Copy Markdown

FFL-1694

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

OpenFeature Browser Provider Bundle Sizes

Measured from the Vite production output after installing packed @datadog/flagging-core and @datadog/openfeature-browser tarballs.

Both scenarios initialize an OpenFeature provider, evaluate a boolean flag, change context, and evaluate again. Telemetry is disabled for DatadogProvider; no tracking hooks are registered for DatadogCoreProvider. DatadogProvider fetches precomputed assignments for each context; DatadogCoreProvider receives rules from fetchRulesConfiguration once and evaluates locally.

Sizes include OpenFeature and the same small scenario harness. Configuration responses are supplied by Playwright and are not bundled. These are complete scenario JS sizes, not configuration payload sizes or isolated provider/Protobuf costs; the difference between rows is not a decoder-only delta.

Scenario HTML JS Assets Raw JS Gzip JS Protobuf Markers
DatadogProvider (precomputed fetching) provider.html 19 82.9 KiB 31.4 KiB no
DatadogCoreProvider + fetchRulesConfiguration core-provider.html 14 154.6 KiB 48.1 KiB yes

Dependency checks passed: no Protobuf markers in default/precomputed scenarios; markers present in rules-based scenarios.

OpenFeature Browser Tracking Hook Bundle Sizes

Synthetic entrypoints import and call tracking hook factories from the packed @datadog/openfeature-browser/rules-based ESM package. Deltas are measured against the no-hook baseline from the same Vite production build.

Scenario HTML JS Assets Raw JS Raw Δ Gzip JS Gzip Δ
tracking hook baseline tracking-baseline.html 6 2.6 KiB 0 B 1.6 KiB 0 B
exposure logging hook tracking-exposure.html 14 56.9 KiB +54.3 KiB 22.1 KiB +20.5 KiB
evaluation logging hook tracking-evaluation.html 14 53.2 KiB +50.6 KiB 20.8 KiB +19.2 KiB
RUM tracking hook tracking-rum.html 10 5.1 KiB +2.5 KiB 3.0 KiB +1.4 KiB
all tracking hooks tracking-all.html 17 61.2 KiB +58.6 KiB 24.1 KiB +22.5 KiB

OpenFeature Node Server Bundle Sizes

Independent Webpack production builds of the installed core and Node SDK tarballs, targeting Node 18 with ESM (module export condition). Node built-ins remain external. OpenFeature, dd-trace, and configuration fixtures are not bundled.

Scenario Raw JS Gzip JS Protobuf Modules
Node provider only 41.9 KiB 12.8 KiB no
SSR helpers only (fetch client rules + serialize) 80.6 KiB 23.2 KiB yes
Node provider + SSR helpers 122.1 KiB 35.2 KiB yes

Adding SSR helpers to the provider adds 80.1 KiB raw / 22.4 KiB gzip in this build. This includes fetching, parsing, and serialization, not just the decoder. SSR helpers fetch rules for the browser, not for configuring that provider (which uses its existing JSON API).

These are bundler sizes, not npm install sizes, unbundled Node require/import costs, payload sizes, or latency. Gzip compares compressed artifacts. The browser scenarios include OpenFeature and a different harness, so their sizes are not directly comparable.

Dependency checks passed: no Protobuf runtime or generated schema modules in the provider-only bundle; present in both SSR bundles (checked using Webpack module statistics).

This report shows current PR artifact sizes only; it does not compare against the base branch.

This branch has not been deployed

No deployments
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.

1 participant