> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cfosilvia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# EXPERIMENTS

# A/B experiments

How to add a PostHog experiment to Silvia-Web with the shared primitive.
Required reading before any experiment PR, human or agent.

## The contract

Two files carry every experiment:

* `lib/experiments/registry.ts` declares it: flag key, variants (control
  first), launch time, audience, surface, goal event, owner.
* `hooks/use-experiment.ts` resolves it: `useExperiment(EXPERIMENTS.name)`
  returns `{ variant, isReady, isEligible }`.

The hook owns exposure. It fires `$feature_flag_called` exactly once per
user per browser session, before the state update that lets the treatment
mount, so impressions and goal events always land after exposure. Do not
call `getFeatureFlag` for the experiment anywhere else.

Control is the default for every failure mode: flag missing, flag not yet
started, PostHog absent, user outside the audience, flags not loaded. A
merged experiment PR is inert until someone starts the experiment in
PostHog.

## Adding one

1. Add the registry entry. Key convention is `<surface>-<change>-vNN`.
   `launchedAt` is the planned start time in UTC and is the eligibility
   cutoff for `new-users` audiences.
2. At exactly one surface, read the hook and branch on `variant`. Gate
   any treatment-only rendering on `isReady` so users never flash control.
3. Fire the goal event where the user converts, if it does not exist yet.
   Prefer an event the funnel already emits (see `pricing_intent_*`,
   `onboarding_*`, `upgrade_cta_clicked`).
4. Add a unit test for the surface: control renders the old UI, test
   renders the new one, neither renders before `isReady`.
5. Create the experiment in PostHog in draft with the same key and
   variants. The growth lane in llm-platform does this through the API.
   Starting it is a human action after the code is in production.

QA on previews: append `?<key_with_underscores>_variant=test` to force a
variant. Production ignores the override.

## Retiring one

When the readout picks a winner: flip the flag to 100 percent on the
winner in PostHog, then open a PR that hardcodes the winner, deletes the
registry entry, and removes the branch. Delete the flag after that PR is
in production.

## Rules for agent-authored experiment PRs

* One experiment per PR, one surface per experiment, control-first.
* Copy, layout, ordering, timing, and visibility of existing elements
  only. No changes to Stripe or checkout API logic, auth, account
  connections, or anything that sends a message to users.
* Keep the diff small. Around 150 lines excluding tests is the ceiling.
* Never touch `app/themes.css`, `app/globals.css`, or `components/ui/**`.
  Variant-only styling goes through `data-theme-variant` per
  `docs/THEMING.md`.
* Check `docs/SILVIA-SKILLS-SYNC.md`. A variant that renames or moves
  something the assistant explains to users needs a skills PR too.

## History

Older experiments each carry their own hook
(`use-sidebar-cta-location-experiment`,
`use-account-connection-security-experiment`, `use-cost-basis-line-flag`).
They keep working and are the source of the rules above. New experiments
use the shared hook. Migrating the old ones is optional cleanup.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.