feat(migrate-eppo): phase 2 code-migration — client SDKs, struct keys, resolve modes#18
Open
fabriziodemaria wants to merge 7 commits into
Open
feat(migrate-eppo): phase 2 code-migration — client SDKs, struct keys, resolve modes#18fabriziodemaria wants to merge 7 commits into
fabriziodemaria wants to merge 7 commits into
Conversation
Phase 2 code migration previously only modeled server SDKs. Add: - client vs server SDK classification (ambient vs per-call context) - struct/dot-notation resolve paths with a Phase-1->Phase-2 property handoff (BOOLEAN -> .enabled, other scalars -> .value) - legacy get_assignment detection (inverted arg order, untyped return) - removal of Eppo-side readiness scaffolding (e.g. Android postDelayed) - surfacing flags referenced in code but never migrated in Phase 1 Shared core gains the two Confidence-wide transform truths so PostHog benefits too. Co-authored-by: Cursor <cursoragent@cursor.com>
Honor the "prefer local resolve" policy operationally: Phase 2 Step 2 now picks local resolve (getLocalResolveIntegrationGuide) for backend Java/Go/JS/Rust targets and remote resolve otherwise, instead of always fetching the remote provider guide. Also compare source vs target resolve mode and surface a notice when the migration shifts evaluation between in-process (local) and service-call (remote) — it changes latency, caching/freshness, and offline behavior. Eppo declares its source mode as local (in-process UFC eval), so Eppo -> Confidence remote (client SDKs, or Python/Ruby/.NET backends) now warns local -> remote. Co-authored-by: Cursor <cursoragent@cursor.com>
Mobile/web client SDKs are cached-client (backend resolves, device caches resolved values for fast offline reads) - not a network call per read. JS splits by surface: Node server can be in-process WASM or remote, browser is cached-client (the WASM provider is server-only). Replace the local/remote binary with three target modes (in-process, cached client, remote) and rewrite the change-signal to describe what actually shifts per transition (where eval happens, per-read latency, freshness/refetch, cold-start defaults, ruleset exposure). Eppo now declares per-surface source modes: backend = in-process, client = on-device eval. Co-authored-by: Cursor <cursoragent@cursor.com>
Add a server-precomputed resolve mode and a React/Next.js (RSC) transform branch so the Eppo precompute pattern (server getPrecomputedConfiguration + client offlinePrecomputedInit) maps onto Confidence's local-resolve ConfidenceProvider + useFlag flow. - Scan: match JS method-name variants (getBoolAssignment, getJsonAssignment) and detect the precomputed pattern (getPrecomputedConfiguration, offlinePrecomputedInit, getPrecomputedInstance) - Transform: map server init -> createConfidenceServerProvider, precompute string -> ConfidenceProvider context, client reads -> useFlag, and drop the assignment-logger/custom-event exposure bridge - Resolve mode: classify server-precomputed and signal it as preserved (no local<->remote change) when migrating to the React provider Co-authored-by: Cursor <cursoragent@cursor.com>
Validated the code migration against the Eppo Go and Java server relays and closed the gaps that surfaced: - Scan: case-insensitive grep + a value-type-to-spelling table so we catch Go PascalCase (GetBoolAssignment), Java getDoubleAssignment (numeric) and getJSONStringAssignment (JSON-as-string), not just the JS/Python forms - Transform: document the per-language accessor shape (Go ctx-first/no-get prefix; Java getDoubleValue/getObjectValue) and drop the gson re-parse for JSON since getObjectValue returns a structured value - Readiness scaffolding removal now covers server init too (Go Initialized() channel wait, Java blocking buildAndInit()) - Bandits (getBanditAction/BanditResult) flagged as BLOCKED with no Confidence equivalent Co-authored-by: Cursor <cursoragent@cursor.com>
Validated the code migration against the Eppo Python server relay (the only remote target — Confidence has no local resolve for Python) and closed the gaps that surfaced: - Add Python to the value-type-to-accessor table; numeric maps to get_float_value (not getNumberValue/getDoubleValue), JSON to get_object_value, all snake_case get_<type>_value - Document the remote init shape: api.set_provider( ConfidenceOpenFeatureProvider(Confidence(client_secret=...))) with NO set_provider_and_wait, and delete Eppo's wait_for_initialization() This exercises the in-process -> remote resolve-mode change signal, which the core already models for backend Python/Ruby/.NET targets. Co-authored-by: Cursor <cursoragent@cursor.com>
Add the migrated (post-Confidence) output for each validated resolve mode, plus a README mapping every fixture to its upstream Eppo source repo, the Confidence SDK it targets, and how it was verified: - go-server / java-server — in-process local resolve (Java compiled clean) - python-server — remote target (in-process -> remote signal) - nextjs-precomputed — server-precomputed React/RSC (tsc + lint clean) Source-only (no build artifacts); these pin expected transform output so regressions in the skill's mapping tables are easy to catch. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #17 (Phase 1). Hardens the Phase 2 (code migration) half of the Eppo→Confidence skill after test-running it against real Eppo example apps across all four resolve modes. The detection front-end already worked; these commits fix the transform half and add migrated fixtures.
What changed
setEvaluationContext/setEvaluationContextAndWait, no per-call context arg. SDK side is classified from the detected Eppo package.flag.property. Phase 1 now records a deterministic resolve path (BOOLEAN→.enabled, other scalars→.value, JSON→struct shape) on each flag's plan entry, which Phase 2 consumes verbatim.get_assignmentdetection — added to the scan grep, with handling for its inverted(subjectKey, flagKey)arg order and untyped return.Handler.postDelayed, GoInitialized()channel, JavabuildAndInit(), Pythonwait_for_initialization()), and surface flags referenced in code but never migrated in Phase 1 instead of inventing a path.getLocalResolveIntegrationGuidefor backend Java/Go/JS/Rust targets, else the remote provider guide.Validation against real Eppo apps (per resolve mode)
Each example was migrated following the skill; gaps found were fixed and re-verified. Migrated output is committed under
skills/migrate-eppo/test-fixtures/phase2-examples/(source-only, no build artifacts).android-sdk-kotlin-example(Kotlin)nextjs-precomputed-client(Next.js/React)openfeature-server-provider-local(react-server/react-client)ConfidenceProvider+useFlag,getBoolAssignmentgrep, RSC awareness, drop exposure bridgetsc --noEmit+next lintcleansdk-test-dataGo + Java relaysGetBoolAssignment, JavagetDoubleAssignment/getJSONStringAssignment, per-lang accessor shapes, bandits BLOCKED./gradlew compileJavaBUILD SUCCESSFUL; Go doc-verifiedsdk-test-dataPython relayspotify-confidence-sdkOpenFeature providerget_float_value, remote init (api.set_provider, no wait), in-process→remote signalpy_compile+ API existence vsspotify-confidence-sdk==3.0.1Rust was considered but Eppo ships no Rust SDK, so there is no source to migrate from.
Test plan
tsc+lintclean against provider0.14.1.com.spotify.confidence:openfeature-provider-local:0.15.0; Go doc-verified.spotify-confidence-sdk.Made with Cursor