feat(feature-flags)!: Evaluate ordered targeting rules by stanleyphu · Pull Request #1717 · workos/workos-node · GitHub
Skip to content

feat(feature-flags)!: Evaluate ordered targeting rules - #1717

Merged
stanleyphu merged 9 commits into
mainfrom
codex/auth-7097-node-rules
Oct 19, 2026
Merged

stanleyphu merged 9 commits into
mainfrom
codex/auth-7097-node-rules

Conversation

@stanleyphu

@stanleyphu stanleyphu commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Why

The runtime's enable-only evaluator cannot honor an ordered rule that serves false. The runtime now polls only the v2 payload and evaluates rules first-match-wins. Unknown rules/operators do not match; unparseable served values use the caller's default.

This is a breaking change for anyone reading flag configuration:

  • getFlag() and change-event snapshots return rules plus off_value instead of targets. isEnabled() keeps its existing signature.
  • The poll types are FlagPollEntry, FlagPollResponse ({ version: 2, flags }), FlagPollRule and FlagPollCondition. The v1 types are gone.
  • v1 bootstrapFlags data is ignored with a warning to the client's logger, and readiness still resolves. A v1 poll response is treated as malformed: the last good snapshot is kept and error is emitted. Disabled v1 targets have no v2 equivalent; they never affected evaluation.

The README describes the runtime in v2 terms.

Not covered by CI

  • Before the implementation, the new boundary tests failed because polling omitted payload_version=2 and a future bootstrap envelope was cached as two flags. Both pass now.
  • Exercised the built SDK's HTTP polling through a loopback relay to the already-authenticated local flags testing app, with the API on codex/auth-7096-poll-payloads. The relay forwarded live API payloads; credentials stayed inside the testing app.
  • Loaded all four built entry points (Node/worker, ESM/CJS) with a stubbed fetch. Each one evaluated a v2 bootstrap with a user exclusion ahead of an organization inclusion (user false, org true, user + org false), ignored a v1 bootstrap with one logged warning while still resolving readiness, polled /sdk/feature-flags?payload_version=2, and kept its snapshot and emitted one error when a poll returned a v1 payload.

Proof

The live poll requested /sdk/feature-flags?payload_version=2 and loaded 16 flags. For the disposable poll-payload-smoke flag, whose user exclusion precedes its organization inclusion:

no matching target        false
matching user             false
matching organization     true
matching user + org       false

The SDK on main reads the enable-only v1 projection and returns true for the same user + org. This branch preserves the exclusion.

Fixes AUTH-7097

@linear-code

linear-code Bot commented Sep 30, 2026

Copy link
Copy Markdown

@stanleyphu
stanleyphu marked this pull request as ready for review October 1, 2026 00:21
@stanleyphu
stanleyphu requested review from a team as code owners October 1, 2026 00:21
@stanleyphu
stanleyphu requested a review from mattolson October 1, 2026 00:21
@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge, with the documentation assessment depending on PR #1719.

Summary

The runtime moves to v2 flag payloads and evaluates rules in order. The first matching rule can serve either true or false.

  • Feature flags now use the first matching rule to choose their value.
  • The runtime polls version-2 flag data and keeps complete rule-based snapshots.

Previous thread context:

  • stanleyphu disputed the custom-target finding because the API reserves user and organization; Greptile withdrew it. The current code also removes legacy conversion.
  • stanleyphu accepted the response-type break as intentional for the major release. The current README describes FlagPollResponse as { version: 2, flags }.
  • stanleyphu accepted dropped disabled-target changes as intentional because they never affected evaluation. Greptile withdrew that finding; the current runtime rejects v1 data entirely.

All supplied previous threads were unnumbered, so none require numbered previous-finding entries.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Poll v2 payload] --> B{Payload passes checks?}
  B -->|No| C[Keep last good flags]
  B -->|Yes| D[Replace cached flags]
  D --> E{Flag enabled?}
  E -->|No| F[Serve off_value]
  E -->|Yes| G[Check rules in array order]
  G --> H{A rule matches?}
  H -->|Yes| I[Serve first matching value]
  H -->|No| J[Serve default_value]
  F --> K[Use caller default if value is not boolean]
  I --> K
  J --> K
Loading

Reviews (5) · Last reviewed commit: "fix(feature-flags): Explain unreadable p..." · Reviewed by Greptile

Comment thread src/feature-flags/payload.ts Outdated
Comment thread src/feature-flags/interfaces/flag-poll-response.interface.ts Outdated
Comment thread src/feature-flags/payload.ts Outdated
@Deborah-Digges
Deborah-Digges removed the request for review from mattolson October 1, 2026 16:06
Comment thread src/feature-flags/evaluator.ts
@Deborah-Digges

Deborah-Digges commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

How do we plan to roll this out? I imagine now that we have the v2 version of the SDK API shipped, we can just ship this without waiting for the full rollout of targeting rules.

disabled v1 targets get dropped since they don’t affect evaluation. We should call this out in the migration notes.

However, this would break existing targeting, so I'm wondering when the right order to ship this SDK change would be

BREAKING CHANGE: getFlag() and change-event snapshots now expose v2 rule entries with off_value and rules instead of legacy targets.
@stanleyphu
stanleyphu force-pushed the codex/auth-7097-node-rules branch from c0860ed to c9ac1cd Compare October 16, 2026 00:14
stanleyphu and others added 3 commits October 15, 2026 21:00
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@workos-shipyard

Copy link
Copy Markdown

⚓ Shipyard review suggestion

Suggested team: applied-ai, based on recent contributors to the changed files (@nicknisi).

Links open Shipyard for confirmation. Choosing a reviewer overrides availability; “Pick for me” skips known absences.
Comment /tag <team> for a team or /tag to infer one. Tag the same team again to reroll.

Shipyard Analysis

⏳ Generating analysis...

⚓ Explore the analysis in depth in Shipyard · 🔄 Re-run analysis

@workos-shipyard
workos-shipyard Bot requested a review from nicknisi October 16, 2026 23:42
@workos-shipyard

Copy link
Copy Markdown

⚓ Review requested from @nicknisi for team dx, requested by @stanleyphu.

Shipyard Analysis

⏳ Generating analysis...

⚓ Explore the analysis in depth in Shipyard · 🔄 Re-run analysis

@stanleyphu

Copy link
Copy Markdown
Contributor Author

@greptileai review

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/feature-flags/payload.ts
Comment thread src/feature-flags/payload.ts Outdated
Comment thread src/feature-flags/evaluator.spec.ts
Comment thread src/feature-flags/runtime-client.ts Outdated

@Deborah-Digges Deborah-Digges left a comment

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.

A couple questions and test suggestions but overall this looks great ⭐ :shipit:

…uator

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@stanleyphu

Copy link
Copy Markdown
Contributor Author

@greptileai review

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@stanleyphu

Copy link
Copy Markdown
Contributor Author

@greptileai review

stanleyphu and others added 2 commits October 19, 2026 13:01
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@stanleyphu

Copy link
Copy Markdown
Contributor Author

@stanleyphu
stanleyphu merged commit 1b305bd into main Oct 19, 2026
9 checks passed
@stanleyphu
stanleyphu deleted the codex/auth-7097-node-rules branch October 19, 2026 20:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants