Frames, Audiences and Lanes

Episode 4: before any coin flip, a filter stack decides which experiment a visitor can even enter: frames, conditions, audiences, lanes, and bots

Guide~3 min read

Copy this guide and your current settings as a prompt for your AI agent.

Preview agent promptSee the guide, code and settings included when you copy.

Three visitors, same page. One lands in the mobile experiment, one in the returning-visitor experiment, one matches nothing and is not measured at all. None of that was a coin flip. Before the hashes of episode 1 ever run, a filter stack decides which experiment a visitor can even enter. This episode is that stack.

Watch: 3m 55s — Runtime Walkthrough, episode 4: the entity hierarchy, the frame gate, the condition language, three-valued logic, audiences, lanes, and bots
On this page
  1. 01The hierarchy
  2. 02The frame gate
  3. 03The condition language
  4. 04Three-valued logic
  5. 05Audiences: first match wins
  6. 06Lanes: the claim
  7. 07Lanes: picking the winner
  8. 08Bots: two different levers
  9. 09In short
  10. 10Where in the code

01The hierarchy

Entity What it is
Surface A page: URL patterns (url, url_regex)
Element A region on a surface: a CSS or XPath selector
Frame The container you create: surfaces, elements, a goal metric, and its own holdout split
Experiment Attached to a frame; several can share one, typically one per audience

The payload ships this whole tree.

02The frame gate

Targeting starts with one filter, activeFrames: every frame whose activation condition passes for this URL and visitor. If that list is empty, getVariantsAndElements returns immediately. No frame, no global holdout, no measurement. The frame gate is the door to everything.

03The condition language

Conditions are one recursive type, ActivationCondition (sdk/shared/types/coframe.ts), evaluated by checkActivationCondition in sdk/shared/utils.ts. Two combinators, and and or, nest freely. The leaves come in three families:

Family Leaves
URL Exact match (normalized: protocol, www and trailing slashes ignored), base-URL match, regex, query parameters
Visitor Device, platform, language, country, region, city, first-time versus returning (via the visit count), your own user context
Mechanism Time windows, a seeded traffic slice (the same hash math as episode 1), an arbitrary JS function (CSP-safe executeJs; an error counts as false and logs js_eval_error)

04Three-valued logic

A condition does not just return true or false. It can return VALUE_UNKNOWN_YET: the URL is not resolved, the token is not minted, the JS runner is not available. The combinators respect it: in an and, false beats unknown but unknown beats true; in an or, true wins immediately. At the audience gate only a hard false blocks (activated !== false): unknown lets the visitor through.

05Audiences: first match wins

Within an active frame, experiments are tried in order. Each carries audience ids (saved conditions) and every one of them must pass: an AND-gate (checkAllAudiences). No audiences means catch-all. The first experiment whose audiences pass takes the frame and the loop breaks: one experiment per frame, per visitor. Different frames on the same page can all apply.

06Lanes: the claim

A lane is a named group of experiments that must never share a visitor. Membership lives in the payload. The visitor’s side is a claim, experiment id plus iteration id, one per lane (sdk/shared/lanes.ts), stored in localStorage always and in the coframe.lanes cookie once consent allows. Stale claims are dropped when the lane is gone, the experiment left, or its iteration rolled: release and reclaim, iteration-aware.

07Lanes: picking the winner

Per lane: if a valid claim exists, that experiment wins, so commitment survives. Otherwise a seeded tiebreak (pickWinner): sort the reachable members, hash the token with the lane’s seed, take the modulo. Stable per visitor, evenly split across members. Every reachable non-winner is excluded and its frame falls through to baseline. The winner’s claim is committed on entry, before the holdout flip, so even a control-bucketed visitor stays committed.

08Bots: two different levers

Lever Effect
Bot cloaking (x-is-bot with bot_cloaking_enabled) The visitor skips the coin flip and is forced onto the original page, so crawlers always index the canonical version. An SEO lever
Blocklisted IP Still sees variants (your own QA needs to) and is removed from analytics later, at query time

What bots see; what analytics counts. Two levers.

09In short

Frames gate the page. Conditions speak a three-valued language. Audiences AND-gate each experiment, first match wins. Lanes keep experiments apart with sticky, seeded claims. Bots are cloaked, not counted.

10Where in the code

File What to look for
sdk/shared/types/coframe.ts ActivationCondition
sdk/shared/utils.ts checkActivationCondition, normalizeUrl, VALUE_UNKNOWN_YET
sdk/shared/assignment.ts activeFrames, checkAllAudiences, lane reachability, the per-frame loop, bot cloaking
sdk/shared/lanes.ts Claims, validateClaims, pickWinner, resolveLaneWinners
sdk/sdk-js/src/init/modifyPage.ts persistLaneClaims

Related reading

Content and video lessons from Coframe documentation. Code examples are not executed on this page.

BACK TOHow Coframe Works

Documentation

Open Coframe App

Search documentation

Search all guides and references↑ ↓ Navigate · Esc Close