You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(migrate-eppo): align REST reference and fixtures with real Eppo OpenAPI schema
The prior REST reference and fake-server fixtures were modeled from
Eppo's high-level docs and were structurally close but field-naming
wrong almost everywhere. Diffed against the real OpenAPI 3.0 spec
(publicly served at https://eppo.cloud/api/docs/swagger-ui-init.js,
no auth required) and corrected:
* snake_case throughout: variation_type, is_archived, targeting_rules,
variation_weight, percent_exposure, is_default, environment_id, etc.
* Numeric IDs (Eppo Object IDs) instead of slug strings
* variation_weight is an array of {variation_id, weight}, not a map
keyed by variant_key
* Condition values are always arrays, even for single-value operators
* Default variation lives on the allocation with is_default: true,
not on the flag itself
* Environment status uses active + is_production, not enabled
* List pagination is offset + limit, not page + per_page
* List response is a bare array, not a {flags, has_more, total} wrapper
* Added include_archived, include_detailed_allocations query params
Surfaced two new BLOCKED cases the spec made visible:
* IS_NULL operator -> Confidence has no native null-check rule, so any
allocation using it is BLOCKED
* SWITCHBACK allocation type -> Eppo time-windowed experiments are not
modeled in Confidence; the whole flag is BLOCKED
Plus an audiences[]-references BLOCKED path (allocations that reference
reusable Eppo audience definitions via the IS_IN / IS_NOT_IN type
require fetching /audiences/{id} and inlining, which is non-trivial).
Test-fixture coverage updated to exercise every operator and
allocation type in the spec: 10 fixture flags covering MATCHES suffix,
waterfall + multivariant, NOT_ONE_OF + GTE + AND, special id
attribute, inactive-in-env, SemVer BLOCKED, regex BLOCKED, IS_NULL
BLOCKED, SWITCHBACK BLOCKED, and audience-reference BLOCKED.
Validated end-to-end against the OpenAPI required-fields and enums
via an automated schema-check script: zero violations across 10 flags
and 3 envs.
-`targeting_rules[]` — each rule is `{ conditions: [{ operator, attribute, values: [...] }] }`
185
+
-`variation_weight[]` — array of `{ variation_id, weight }` referencing variations by numeric `id`
186
+
-`audiences[]` — array of `{ audience_id, type }` where `type` is `IS_IN` or `IS_NOT_IN`
187
+
-`percent_exposure` (0–100) — fraction of matched subjects that enter the allocation
188
+
-`is_default` (boolean) — the default allocation sits at the bottom of the waterfall and supplies the "no match" variation
189
+
-`experiment` — the linked Eppo experiment object, or `null` for non-experiment allocations
190
+
-`environment_id` — only set on the env-scoped endpoint
191
+
-`environments[]` — per-environment state (`PublicApiFeatureFlagEnvironment`: `id`, `name`, `active`, `is_production`); allocations are NOT included here, only env status
192
+
193
+
The env-scoped endpoint (`GET /feature-flags/{id}/environments/{environmentId}`)
194
+
returns a `PublicApiFeatureFlagEnvironmentWithAllocation`: the env status
195
+
fields above PLUS `allocations[]` for that environment. This is the
196
+
canonical place to read the per-env waterfall.
197
+
198
+
**Default value lives on the allocation marked `is_default: true`**, not
199
+
on the flag. The default allocation has empty `targeting_rules[]` and
200
+
`audiences[]` and matches everyone; its `variation_weight[]` decides what
201
+
unmatched subjects see.
202
+
203
+
**Pagination.** Eppo uses `offset` + `limit` (both numbers), not cursors
204
+
and not page numbers. Loop:
205
+
206
+
```
207
+
offset = 0
208
+
LOOP:
209
+
items = GET /feature-flags?offset=<offset>&limit=50
210
+
process items
211
+
if len(items) < 50 OR items is empty → STOP
212
+
offset += 50 → continue LOOP
213
+
```
177
214
178
-
**Always paginate** until the response returns fewer items than `per_page` or
179
-
an empty page. Eppo's API uses page-based pagination, not cursors.
215
+
The list endpoint returns a **bare JSON array**, no wrapper object.
180
216
181
217
---
182
218
@@ -258,55 +294,61 @@ Set the step to `⏸ awaiting user` and wait for an explicit pick.
258
294
**Step 1b — list all flags. CRITICAL: paginate until exhausted.**
259
295
260
296
```
261
-
page = 1
297
+
offset = 0
262
298
LOOP:
263
-
response = curl GET /feature-flags?page=<page>&per_page=50
264
-
process response items
265
-
if response items < 50 OR response is empty → STOP
**Action:**[] Skip (no migrate option available until the block is resolved)
622
+
536
623
**MCP Commands:**
537
-
<createFlag, addFlagToClient, addTargetingRule (ONE per allocation, in order, with variant assignments and their split), resolveFlag with full parameters — positive AND negative case>
624
+
<createFlag (default value = is_default allocation's variation), addFlagToClient, addTargetingRule (ONE per non-default allocation, in order, with variant assignments and their split), resolveFlag with full parameters — positive AND negative case>
538
625
539
626
---
540
627
@@ -552,17 +639,23 @@ by `execute` — no implicit defaults.
552
639
(The core file defines the execute flow and the Flag Setup Sequence.
553
640
This section adds Eppo-specific guidance.)
554
641
555
-
**Disabled-in-environment handling.** If a flag is off in the source
556
-
Eppo environment, surface that during execute:
642
+
**Inactive-in-environment handling.** If a flag's `active` flag is
643
+
false in the source Eppo environment, surface that during execute:
557
644
558
-
> This flag is OFF in Eppo (<env>). I'll create it in Confidence but
559
-
> keep the rules at 0% rollout so it stays inactive until you turn it
560
-
> on intentionally. Continue?
645
+
> This flag is INACTIVE in Eppo (<env>). I'll create it in Confidence
646
+
> but keep the rules at 0% rollout so it stays off until you turn it on
647
+
> intentionally. Continue?
561
648
562
-
**Variation type → Confidence schema.** Use the Eppo `variationType`
563
-
(`STRING` / `BOOLEAN` / `NUMERIC` / `INTEGER` / `JSON`) as the
649
+
**Variation type → Confidence schema.** Use the Eppo `variation_type`
650
+
(`BOOLEAN` / `INTEGER` / `JSON` / `NUMERIC` / `STRING`) as the
564
651
Confidence schema type when calling `createFlag`. Include all Eppo
565
-
variations as Confidence variants.
652
+
variations (`variant_key` → `value`) as Confidence variants.
653
+
654
+
**Default value.** Take the variation referenced by the allocation
655
+
with `is_default: true` (its `variation_weight[0].variation_id`,
656
+
resolved against `variations[]`) and pass that variant's value as
657
+
`createFlag`'s default. Do NOT emit a targeting rule for the default
658
+
allocation.
566
659
567
660
**Waterfall verification.** Because Eppo flags often have multiple
568
661
allocations, the core file's Flag Setup Sequence Step 4 requires you to
0 commit comments