Featurevisor
You are helping the user with Featurevisor — a Git-based feature management tool. A Featurevisor project is a repository of YAML (default) or JSON definitions that compile into static JSON datafiles, which applications evaluate locally through SDKs. There are two sides to every task:
- Project side — authoring/querying the definitions repo (features, segments, attributes, tests) and running the
featurevisor CLI.
- Application side — consuming datafiles via SDKs (
@featurevisor/sdk, React, Vue, or other languages).
This skill covers both. The compact documentation index is at https://featurevisor.com/llms.txt and the complete feed at https://featurevisor.com/llms-full.txt — fetch on demand if a topic isn't covered in this skill's references.
Know your audience
Featurevisor is used by engineers, product managers, marketers, and people who describe what they want without knowing the YAML. Calibrate:
- Not sure of the vocabulary? Someone asking to "turn on the banner for 10% of Dutch users" wants a rollout rule — don't make them learn the words
segment, bucketBy, or percentage first. Do the mapping for them, then show the result in their language: what changed, who is affected, what happens next.
- Safe vs. risky changes. Ramping a percentage up, adding a new rule, adding a feature, force-enabling for QA — routine; do them confidently. Renaming rule keys, changing
bucketBy, resizing group slots, decreasing percentages — these silently re-bucket users; warn plainly ("some users would lose the feature mid-session") before proceeding.
- Always close the loop. After any change, say in one or two sentences what will happen when it ships (e.g. "once this merges and CI deploys the datafile, ~10% of users in NL will see the banner; the rest see nothing").
- For anyone who wants to see the project, offer the Catalog — a browsable read-only UI with live reload (see Visual review with Catalog).
Orient yourself first
No project yet? Interview, then scaffold
If there is no featurevisor.config.js anywhere in the working tree, there is no Featurevisor project yet. If the user is in an application repo consuming Featurevisor, this is SDK work — see Application integration; author definitions in the project repo, not here. If they want a new project, ask a few setup questions before scaffolding — these choices shape every file written afterwards and are annoying to retrofit:
- Environments? Classic
staging + production (recommended default), a custom list, or none (single-environment tools, internal apps — rules become direct lists instead of per-env maps).
- Sets? Default no — one tree is right for most projects. Offer sets only if they want independent trees: release lanes with promotion gates (dev → staging → production) or fully separate surfaces (storefront/admin). See sets-promotions.md for the trade-off; sets add real structure overhead.
- Consumers → tags and targets? Who loads datafiles: one app (single
all tag + one all target is fine) or several surfaces (a tag per surface — web, ios, android — and a target per datafile they'll load)?
- File format? YAML (default) or JSON.
- What identifies a user?
userId, deviceId, or both ({or: [userId, deviceId]}) — this becomes defaultBucketBy and the first attributes.
Then scaffold in an empty directory — a separate repo from application code (review flags like code, deploy datafiles independently — that separation is the point of the tool):
npx @featurevisor/cli init # yml default; --example=json | no-environments | sets | test-environments (release lanes) | toml | namespace-slash
npm install
Pick the --example closest to their answers, then adjust featurevisor.config.js to match exactly. templates/example-project/ is an alternative lint-clean starting point.
Existing project? Detect the setup before touching anything
Always run these once at the start:
npx featurevisor config --json --pretty
npx featurevisor info
Four config values change the shape of everything you write — get them wrong and files won't lint or, worse, will mean something else:
sets — if true, every path moves under sets/<set>/… and you must author in the right set and scope commands with --set. Read sets-promotions.md before doing anything in such a project.
environments — if present, rules, force, and expose are maps keyed by env; if omitted, they are direct lists with no env level at all.
parser — if "json" author in JSON; otherwise YAML.
namespaceCharacter — separator for directory-namespaced keys. Default . (features/checkout/promo.yml → checkout.promo); some projects use / (→ checkout/promo). Every key you write — in segments: of rules, required:, test specs, SDK calls — must use the project's separator. Confirm against reality, not assumption: npx featurevisor list --segments --json shows keys exactly as the project spells them.
Also note: tags (must include any tag you put on a feature), defaultBucketBy (default userId), and directory-path overrides (featuresDirectoryPath, etc.).
Still on v2? npm ls @featurevisor/cli tells you. v2-only shapes such as scopes: in the config, an environments/ definition directory, splitByEnvironment, slash-separated namespaced keys, or createInstance in app code mean this skill's guidance won't match what lints. Point that out and offer the upgrade: upgrading-to-v3.md.
Then read one or two existing entities (a feature, a segment) to match local style — indentation, quoting, comment density, key ordering — before adding new ones.
When to load which reference
This file is loaded eagerly. The files below are loaded only when relevant — read them in full before authoring or debugging in that area, don't rely on the summary in this file.
| Task |
Read |
| Create or edit a feature (flags, variations, variables, etc.) |
features.md |
| Write or change segment conditions |
segments.md |
| Look up a condition operator |
operators.md |
| Define or change an attribute |
attributes.md |
Variables, JSON-Schema-ish types, reusable schemas/ |
variables-schemas.md |
Define or evaluate independent global variables under variables/ |
global-variables.md |
Variable overrides with deep merge (mutations) |
variables-schemas.md |
Mutually-exclusive experiments via groups/ |
groups.md |
Bucketing, bucketBy, state files, sticky |
bucketing.md |
| Tags: feature and global variable metadata used by targets |
tags.md |
| Targets — generated datafile definitions |
targets.md |
| Namespaces — directory-based feature/segment key prefixes |
namespaces.md |
featurevisor.config.js, environments, directory overrides |
configuration.md |
| Sets (independent trees: release lanes, surfaces) and promotions between them |
sets-promotions.md |
| JSON / TOML / other format projects |
custom-parsers.md |
| Build datafiles, deploy to CDN, CI pipeline |
building-datafiles.md |
Write a .spec.yml test, run featurevisor test |
testing.md |
Any CLI invocation, flags, list/find-usage/evaluate |
cli.md |
| Answer "what's enabled where / who uses X"; browse via Catalog |
querying.md |
| Use the SDK in an app — JS/TS/Node/browser/edge, context, refresh, server-side |
sdk-javascript.md |
React or React Native integration (useFlag etc.) |
sdk-react.md |
| Vue integration |
sdk-vue.md |
| Go, Python, Ruby, Java, Kotlin, Swift, PHP, and running your specs through them |
sdk-other-languages.md |
OpenFeature providers (@featurevisor/openfeature-provider-*, other languages) |
openfeature.md |
| Code generation (typed TS bindings) |
code-generation.md |
| Analytics activation modules (GA4 / Segment / etc.) |
tracking.md |
| Upgrading v2 → v3 — project or application |
upgrading-to-v3.md |
Custom CLI commands (plugins), the datasource API, custom adapters |
plugins-datasource.md |
| Common patterns — A/B, multivariate, entitlements, kill switches, scheduled releases, staged rollouts, version gating, migrations, testing-in-prod, deprecation/cleanup, microfrontends, ownership, trunk-based dev |
recipes.md |
| Terminology refresher |
glossary.md |
Per-entity templates live in templates/ — copy and adapt rather than writing from memory.
A complete end-to-end mini project lives in templates/example-project/. It passes lint and test as-is — use it as the source of truth for "show me how a realistic Featurevisor project hangs together" requests.
Core authoring rules
These apply to every change. Internalize them; the references add depth, they do not override these.
1. Bucketing and rule keys are append-only contracts
bucketBy is what keeps a user's experience consistent as percentages ramp. Pick once, never silently change it.
- Default for signed-in users:
userId. For anonymous users: deviceId (or whatever the project calls it). The attribute names may differ in this project — read the attributes/ directory and ask the user which to use if it isn't obvious. Do not invent attribute names.
- Combine attributes with a list (
bucketBy: [orgId, userId]) or fall back via bucketBy: {or: [userId, deviceId]}.
- Each rule's
key must be unique within its environment and must not change once users are bucketed against it. Renaming a rule key re-buckets users — the same user can lose or gain the feature. Add a new rule rather than renaming.
- Increasing
percentage over time is safe only if the rule key stays stable.
2. First matching rule wins
Rules are evaluated top-to-bottom per environment. Put narrow targeting (e.g. country-specific rollouts at 100%) before the catch-all segments: '*' rule.
3. Don't author rules that target unknown segments or attributes
Before referencing segments: foo in a rule, confirm the segment exists (or create it). Same for attributes referenced inside segment conditions. Run npx featurevisor lint after edits — it catches dangling refs, percentage-sum errors in groups and variations, and schema mismatches.
4. Variations weights sum to 100; group slot percentages sum to 100
And a feature in a group cannot use a rollout percentage higher than its slot's percentage.
5. Conditions must stay portable across SDKs
Definitions are evaluated by SDKs in many languages, so lint enforces a portable subset — worth knowing before writing conditions:
- Dates (
before/after) need a full ISO 8601 timestamp with timezone, quoted: "2026-11-27T00:00:00Z". Date-only or zone-less values fail lint.
- Regex (
matches/notMatches) allows only g, i, m, s flags and rejects every (?…) construct — including non-capturing (?:…), which is the usual surprise — plus backreferences and possessive quantifiers.
- Operators are checked against the attribute's declared type (numeric operators need
integer/double, includes needs an array, etc.).
Details and lint messages: operators.md.
6. After any edit, lint
npx featurevisor lint
If you wrote or changed a test spec, also run:
npx featurevisor test --keyPattern=<theKey>
CLI: run freely
All featurevisor CLI commands are local and safe to run without confirmation, with two caveats:
- Bare
build is a CI command — it increments .featurevisor/REVISION and updates state files, which only CI should commit. For local builds (yours and the user's), default to --no-state-files: same datafiles, same success/failure confirmation, no state side effects.
promote --apply (sets projects) writes definition files — preview first and treat applying like any other edit (sets-promotions.md).
npx featurevisor build --no-state-files
The most useful commands for an authoring agent (full reference in cli.md):
| Command |
Purpose |
npx featurevisor config --json --pretty |
Project configuration |
npx featurevisor info |
Counts of features / segments / attributes / tests |
npx featurevisor lint |
Validate definitions (run after every edit) |
npx featurevisor list --features --json [--filters…] |
Find features by tag, env, variable, archived, etc. |
npx featurevisor list --datafiles --json |
List generated datafile paths |
npx featurevisor list --segments --json |
List segments |
npx featurevisor list --attributes --json |
List attributes |
npx featurevisor list --variables --json |
List global variables |
npx featurevisor list --groups --json |
List exclusion groups |
npx featurevisor list --schemas --json |
List reusable schemas |
npx featurevisor list --targets --json |
List targets |
npx featurevisor find-usage --segment=<key> |
Where a segment is used |
npx featurevisor find-usage --attribute=<key> |
Where an attribute is used |
npx featurevisor find-usage --feature=<key> |
Feature usage details |
npx featurevisor find-usage --variable=<key> |
What a global variable depends on |
npx featurevisor find-usage --unusedSegments |
Dead segments |
npx featurevisor find-usage --unusedAttributes |
Dead attributes |
npx featurevisor find-duplicate-segments |
Segments with identical conditions |
npx featurevisor evaluate --environment=<e> --feature=<k> --context='{…}' |
Why a feature evaluates the way it does (debug) |
npx featurevisor evaluate --environment=<e> --variable=<k> --context='{…}' |
Why a global variable has its value |
npx featurevisor assess-distribution --environment=<e> --feature=<k> --context='{…}' --populateUuid=userId --n=1000 |
Simulate rollout distribution |
npx featurevisor test [--keyPattern=…] [--assertionPattern=…] |
Run test specs |
npx featurevisor build --no-state-files |
Build datafiles without touching local revision/state |
npx featurevisor catalog |
Browsable read-only UI of the whole project |
Prefer the CLI over grepping when answering questions like "what features use segment X?", "which features are enabled in production?", or "why does feature F evaluate to disabled for this context?". The CLI's --json output is parseable and authoritative.
Use optional, repeatable --target=<target> selection with build, test, evaluate, benchmark, assess-distribution, list --features, and info when the question concerns deployed target datafiles. Runtime commands process each selected target independently. build --json and build --print accept only one target because they emit one datafile.
Changes ship through Git
Featurevisor is GitOps: nothing you write takes effect until it travels the pipeline —
edit → PR review → merge → CI (lint, test, build) → datafile deployed to CDN → each app's next datafile refresh.
Practical consequences:
- Don't commit or push unless asked. Editing files and running the CLI is your job; landing the change is the user's (or their CI's).
- Keep one logical change per branch/PR (a rollout bump, a new feature, a cleanup) — flags get reviewed like code, and small diffs get approved fast.
- Update or add the matching
.spec.yml in the same change when behavior expectations shift.
- When the user asks "when will this be live?", walk that pipeline: after merge, CI deploys the datafile, and apps pick it up on their next refresh (an app polling every 5 minutes lags up to 5 minutes). For emergency paths, see the kill-switch recipe in recipes.md.
Common authoring flows
Starting a brand-new project
- Run the setup interview from Orient yourself first — environments, sets or not, tags/targets, format, bucketing identity.
- Scaffold in an empty directory (a new repo, separate from app code) with the closest
init --example=…, then npm install.
- Adjust
featurevisor.config.js until it matches the interview answers exactly (environments, tags, sets, defaultBucketBy, parser).
- Replace the scaffolded example entities with the user's first real attribute → segment → feature, in that order (features reference segments; segments reference attributes), and matching targets.
npx featurevisor lint && npx featurevisor test && npx featurevisor build --no-state-files to prove the pipeline.
- Offer the CI/CDN deployment setup from building-datafiles.md when they're ready to ship — and
npx featurevisor catalog so they can see what they built.
Adding a new feature flag
- Read the existing
features/ directory to match conventions (file naming, comment style).
- Confirm the attribute used for
bucketBy exists in attributes/; ask the user which to use if multiple plausible options exist (e.g. userId vs deviceId).
- Create
features/<key>.yml from templates/feature.yml.
- If targeting specific segments, ensure each referenced segment exists in
segments/ — create it from templates/segment.yml if not.
- Run
npx featurevisor lint.
- Offer (don't force): "I can add a
tests/features/<key>.spec.yml covering this — want me to?" If yes, use templates/test-feature.spec.yml.
Adding variations (A/B test)
Read features.md on variations, then use templates/feature-with-variations.yml. Remind the user that weights sum to 100 and the control/treatment names are conventional only. If they ask how results get measured, that's tracking.md.
Adding variables (remote config)
Read variables-schemas.md — covers all variable types, the inline JSON-Schema-ish form, reusable schemas/, variation-level variables, rule-level variables: and variableOverrides:, and the mutations feature for deep-merge overrides. Use templates/feature-with-variables.yml as the starting shape.
Adding a global variable (config no single feature owns)
Ask this first whenever someone wants a configurable value: is the value owned by a feature, or does it stand on its own? A support address, a retry limit, a CDN base URL, a rate limit shared by four services. None of these is an attribute of any one flag, and hanging them off an unrelated feature is the mistake to head off. Those belong in variables/<key>.yml as global variables.
Read global-variables.md and start from templates/variable.yml. The differences from feature variables that catch people out:
- Never bucketed. No
bucketBy, no percentage, no variations. The first matching override wins for everyone it matches, so the value is deterministic for a given context. There is no way to roll one out to 10% of users; if that is what they want, they want a feature.
requiredFeatures instead of a parent. Gate the value on one or more features ({ feature, enabled?, variation? }, enabled defaults to true). When unmet the SDK returns disabledValue, or defaultValue with useDefaultWhenDisabled: true, and reports reason: required_features_unmet.
- Overrides may nest, unlike the flat overrides on feature rules and variations. They follow the same environment rule as
rules and force: a map keyed by environment when the project declares environments, a direct list when it does not. Give every override a key and keep those keys unique across the whole tree for one environment.
- Read them with the two-argument SDK call,
getVariable(variableKey, context), not the three-argument feature form. Go, Rust, and Elixir cannot dispatch on that, so they expose GetGlobalVariable / get_global_variable instead (sdk-other-languages.md).
Before changing or deleting one, run npx featurevisor find-usage --variable=<key> for what it depends on; consuming applications are found by searching app code, not the project (querying.md).
Complex targeting (and/or/not)
Both segment conditions and feature rule segments support and, or, not with nesting. See segments.md and templates/segment-complex.yml.
Important not rule: multiple direct children are treated as an implicit AND and then negated. So not: [A, B] means not (A and B), not not A and not B. For "none of these match", wrap them in or: not: [{ or: [A, B] }].
Mutual-exclusion experiments
Read groups.md. Plan slot percentages before adding rules — once users are bucketed in a group, changing slot percentages re-buckets them. Use templates/group.yml.
Force-enabling for QA / a specific user
Use force: on the feature (per-environment), not rules. No key/percentage needed. See features.md.
Promoting between sets ("move X to staging/production")
Only in sets projects. Read sets-promotions.md, then: preview with npx featurevisor promote --from=<a> --to=<b> --includeFeatures="<key>", show the user the created/updated/conflicts summary, apply with --apply on their go-ahead, and lint + test the destination set. Use promotable: false to protect lane-specific definitions, feature rules, and keyed test assertions from being overwritten.
Debugging an evaluation
Use npx featurevisor evaluate --environment=<e> --feature=<k> --context='{…}' --verbose rather than reading the YAML and reasoning by hand. The evaluation flow (sticky → required → forced → rules → bucketing) is documented in features.md. If the surprise is in an application rather than the project, also check the app's actual context and datafile revision (sdk-javascript.md).
Querying ("what's enabled where?", "who uses this segment?")
See querying.md. It shows the right list/find-usage/evaluate invocations for the common questions a developer asks about an existing project, plus the Catalog for browsing.
Visual review with Catalog
npx featurevisor catalog serves a read-only UI of the whole project at http://127.0.0.1:3000 in watch mode — it rebuilds and reloads the browser whenever definition files change. That makes it the ideal companion to an authoring session:
- Start it once as a background process (it's local and read-only — safe to leave running).
- If you have a browser tool, open
http://127.0.0.1:3000 in it; otherwise give the user the URL.
- Author changes as usual — every edit shows up in the Catalog on save, so the user watches features, rules, variables, and test coverage evolve visually while they prompt you.
Offer this proactively when a session involves several authoring changes or when the user is less comfortable reading YAML — prompting plus a live Catalog is the best way to experience Featurevisor. Details in querying.md.
Upgrading a v2 project or application to v3
Read upgrading-to-v3.md. Do the project repo first, applications after: the datafile schema is unchanged, so v2 SDKs keep reading v3-generated datafiles and each app can upgrade on its own schedule. The one thing to fix before publishing v3 datafiles is multi-child not: in rule segments, whose meaning changed. Lead with npx featurevisor lint after upgrading the CLI: its errors are the worklist.
Recipes for higher-level use cases
When the request matches a named pattern — A/B test, multivariate, mutual exclusion, dependencies, remote config, entitlements/RBAC, kill switch, scheduled/time-window release, staged rollout ladder (employees → beta → everyone), app version gating, backend migration, stale-flag cleanup, testing in production, deprecation, trunk-based development, microfrontends, decoupling release from deploy, ownership — open recipes.md and adapt the matching section. It links back to the granular references for shape details.
Application integration (SDKs)
When the task is consuming features from application code:
- JavaScript / TypeScript / Node / browser / edge → read sdk-javascript.md in full. It covers install, context, all evaluation methods, datafile refresh and on-demand loading, events, sticky, server-side child instances (
spawn), diagnostics, and modules.
- React / React Native → sdk-react.md. Vue → sdk-vue.md.
- Type-safe bindings (generated
isEnabled/getVariation with compile-checked keys) → code-generation.md.
- Other languages — SDKs are cross-platform: Go, Python, Ruby, Java, Kotlin/Android, Swift, PHP, and more → sdk-other-languages.md. Every SDK consumes the same datafiles and is verified against a shared conformance contract, so a user bucketed into
treatment in a browser gets treatment on the backend and on mobile too — one project can serve an entire polyglot stack. That file also covers each SDK's CLI for running the project's own test specs through that language.
- OpenFeature — if the team standardizes on the vendor-neutral OpenFeature API, providers exist for Node.js, browsers, Go, Swift, Java, Ruby, Python, and PHP → openfeature.md. Optional: the native SDKs are unaffected and remain the simpler choice when Featurevisor is the only flag system.
- Framework guides (Next.js, Express, Fastify, Astro, Nuxt): https://featurevisor.com/docs/frameworks.
Key facts that prevent most integration mistakes: evaluations are local and synchronous (no network at evaluation time); the app must load a datafile (built and deployed from the project repo) and decide its own refresh strategy; feature keys, variable keys, and attribute names must match the project's definitions exactly — verify against the project (or its Catalog) rather than guessing.
What not to do
- Do not change a feature's
bucketBy or a rule's key "to clean things up" — that re-buckets users.
- Do not invent attribute, segment, or feature key names — in YAML or in application code. Verify they exist; create them explicitly if needed.
- Do not rename or delete attributes/segments before checking
find-usage.
- Do not add
expose: unless the user asks — it's a short-term migration tool.
- Do not run bare
build locally — incrementing REVISION and state files is CI's job. Local builds always get --no-state-files.
- Do not skip
npx featurevisor lint after edits.
- Do not author project definitions inside an application repo — they belong in the Featurevisor project repo.
1---2name: featurevisor3description: Author, query, and integrate Featurevisor — Git-based feature flags, A/B experiments, and remote config. Use whenever the user mentions Featurevisor, works in a project containing featurevisor.config.js, edits files under attributes/, segments/, features/, variables/, groups/, schemas/, targets/, sets/, or tests/, runs `featurevisor` CLI commands, or asks to add/roll out/ramp/target/A-B test/force-enable a feature flag, set up remote config or entitlements, define a shared/global configuration value that is not owned by any single feature, or asks where a feature/segment is used or why it evaluated that way. Also use when consuming Featurevisor from app code — @featurevisor/sdk, @featurevisor/react, @featurevisor/vue, the Go/Python/Ruby/Java/Kotlin/Swift/PHP SDKs, OpenFeature providers, datafiles, createFeaturevisor, isEnabled/getVariation/getVariable. Covers starting a project from scratch, features (flags, variations, variables), global variables defined under variables/, segments, attributes, schemas, grou4---56# Featurevisor78You are helping the user with [Featurevisor](https://featurevisor.com) — a Git-based feature management tool. A Featurevisor **project** is a repository of YAML (default) or JSON definitions that compile into static JSON **datafiles**, which applications evaluate locally through SDKs. There are two sides to every task:910- **Project side** — authoring/querying the definitions repo (features, segments, attributes, tests) and running the `featurevisor` CLI.11- **Application side** — consuming datafiles via SDKs (`@featurevisor/sdk`, React, Vue, or other languages).1213This skill covers both. The compact documentation index is at <https://featurevisor.com/llms.txt> and the complete feed at <https://featurevisor.com/llms-full.txt> — fetch on demand if a topic isn't covered in this skill's references.1415## Know your audience1617Featurevisor is used by engineers, product managers, marketers, and people who describe what they want without knowing the YAML. Calibrate:1819- **Not sure of the vocabulary?** Someone asking to "turn on the banner for 10% of Dutch users" wants a rollout rule — don't make them learn the words `segment`, `bucketBy`, or `percentage` first. Do the mapping for them, then show the result in their language: what changed, who is affected, what happens next.20- **Safe vs. risky changes.** Ramping a percentage up, adding a new rule, adding a feature, force-enabling for QA — routine; do them confidently. Renaming rule keys, changing `bucketBy`, resizing group slots, decreasing percentages — these silently re-bucket users; warn plainly ("some users would lose the feature mid-session") before proceeding.21- **Always close the loop.** After any change, say in one or two sentences what will happen when it ships (e.g. "once this merges and CI deploys the datafile, ~10% of users in NL will see the banner; the rest see nothing").22- For anyone who wants to *see* the project, offer the **Catalog** — a browsable read-only UI with live reload (see [Visual review with Catalog](#visual-review-with-catalog)).2324## Orient yourself first2526### No project yet? Interview, then scaffold2728**If there is no `featurevisor.config.js`** anywhere in the working tree, there is no Featurevisor project yet. If the user is in an **application repo** consuming Featurevisor, this is SDK work — see [Application integration](#application-integration-sdks); author definitions in the project repo, not here. If they want a new project, **ask a few setup questions before scaffolding** — these choices shape every file written afterwards and are annoying to retrofit:29301. **Environments?** Classic `staging` + `production` (recommended default), a custom list, or none (single-environment tools, internal apps — rules become direct lists instead of per-env maps).312. **Sets?** Default **no** — one tree is right for most projects. Offer sets only if they want independent trees: release lanes with promotion gates (dev → staging → production) or fully separate surfaces (storefront/admin). See [sets-promotions.md](references/sets-promotions.md) for the trade-off; sets add real structure overhead.323. **Consumers → tags and targets?** Who loads datafiles: one app (single `all` tag + one `all` target is fine) or several surfaces (a tag per surface — `web`, `ios`, `android` — and a target per datafile they'll load)?334. **File format?** YAML (default) or JSON.345. **What identifies a user?** `userId`, `deviceId`, or both (`{or: [userId, deviceId]}`) — this becomes `defaultBucketBy` and the first attributes.3536Then scaffold in an empty directory — a **separate repo** from application code (review flags like code, deploy datafiles independently — that separation is the point of the tool):3738```bash39npx @featurevisor/cli init # yml default; --example=json | no-environments | sets | test-environments (release lanes) | toml | namespace-slash40npm install41```4243Pick the `--example` closest to their answers, then adjust `featurevisor.config.js` to match exactly. [templates/example-project/](templates/example-project/) is an alternative lint-clean starting point.4445### Existing project? Detect the setup before touching anything4647**Always run these once at the start:**4849```bash50npx featurevisor config --json --pretty51npx featurevisor info52```5354Four config values change the **shape of everything you write** — get them wrong and files won't lint or, worse, will mean something else:5556- `sets` — if `true`, every path moves under `sets/<set>/…` and you must author in the right set and scope commands with `--set`. Read [sets-promotions.md](references/sets-promotions.md) before doing anything in such a project.57- `environments` — if present, `rules`, `force`, and `expose` are maps keyed by env; **if omitted, they are direct lists** with no env level at all.58- `parser` — if `"json"` author in JSON; otherwise YAML.59- `namespaceCharacter` — separator for directory-namespaced keys. **Default `.`** (`features/checkout/promo.yml` → `checkout.promo`); some projects use `/` (→ `checkout/promo`). Every key you write — in `segments:` of rules, `required:`, test specs, SDK calls — must use the project's separator. Confirm against reality, not assumption: `npx featurevisor list --segments --json` shows keys exactly as the project spells them.6061Also note: `tags` (must include any tag you put on a feature), `defaultBucketBy` (default `userId`), and directory-path overrides (`featuresDirectoryPath`, etc.).6263**Still on v2?** `npm ls @featurevisor/cli` tells you. v2-only shapes such as `scopes:` in the config, an `environments/` definition directory, `splitByEnvironment`, slash-separated namespaced keys, or `createInstance` in app code mean this skill's guidance won't match what lints. Point that out and offer the upgrade: [upgrading-to-v3.md](references/upgrading-to-v3.md).6465Then read one or two existing entities (a feature, a segment) to match local style — indentation, quoting, comment density, key ordering — before adding new ones.6667## When to load which reference6869This file is loaded eagerly. The files below are loaded only when relevant — read them in full **before** authoring or debugging in that area, don't rely on the summary in this file.7071| Task | Read |72| --------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |73| Create or edit a feature (flags, variations, variables, etc.) | [features.md](references/features.md) |74| Write or change segment conditions | [segments.md](references/segments.md) |75| Look up a condition operator | [operators.md](references/operators.md) |76| Define or change an attribute | [attributes.md](references/attributes.md) |77| Variables, JSON-Schema-ish types, reusable `schemas/` | [variables-schemas.md](references/variables-schemas.md) |78| Define or evaluate independent global variables under `variables/` | [global-variables.md](references/global-variables.md) |79| Variable overrides with deep merge (`mutations`) | [variables-schemas.md](references/variables-schemas.md#mutations) |80| Mutually-exclusive experiments via `groups/` | [groups.md](references/groups.md) |81| Bucketing, `bucketBy`, state files, sticky | [bucketing.md](references/bucketing.md) |82| Tags: feature and global variable metadata used by targets | [tags.md](references/tags.md) |83| Targets — generated datafile definitions | [targets.md](references/targets.md) |84| Namespaces — directory-based feature/segment key prefixes | [namespaces.md](references/namespaces.md) |85| `featurevisor.config.js`, environments, directory overrides | [configuration.md](references/configuration.md) |86| **Sets** (independent trees: release lanes, surfaces) and **promotions** between them | [sets-promotions.md](references/sets-promotions.md) |87| JSON / TOML / other format projects | [custom-parsers.md](references/custom-parsers.md) |88| Build datafiles, deploy to CDN, CI pipeline | [building-datafiles.md](references/building-datafiles.md) |89| Write a `.spec.yml` test, run `featurevisor test` | [testing.md](references/testing.md) |90| Any CLI invocation, flags, `list`/`find-usage`/`evaluate` | [cli.md](references/cli.md) |91| Answer "what's enabled where / who uses X"; browse via Catalog | [querying.md](references/querying.md) |92| **Use the SDK in an app** — JS/TS/Node/browser/edge, context, refresh, server-side | [sdk-javascript.md](references/sdk-javascript.md) |93| React or React Native integration (`useFlag` etc.) | [sdk-react.md](references/sdk-react.md) |94| Vue integration | [sdk-vue.md](references/sdk-vue.md) |95| Go, Python, Ruby, Java, Kotlin, Swift, PHP, and running your specs through them | [sdk-other-languages.md](references/sdk-other-languages.md) |96| **OpenFeature** providers (`@featurevisor/openfeature-provider-*`, other languages) | [openfeature.md](references/openfeature.md) |97| Code generation (typed TS bindings) | [code-generation.md](references/code-generation.md) |98| Analytics activation modules (GA4 / Segment / etc.) | [tracking.md](references/tracking.md) |99| **Upgrading v2 → v3** — project or application | [upgrading-to-v3.md](references/upgrading-to-v3.md) |100| Custom CLI commands (`plugins`), the datasource API, custom adapters | [plugins-datasource.md](references/plugins-datasource.md) |101| **Common patterns** — A/B, multivariate, entitlements, kill switches, scheduled releases, staged rollouts, version gating, migrations, testing-in-prod, deprecation/cleanup, microfrontends, ownership, trunk-based dev | [recipes.md](references/recipes.md) |102| Terminology refresher | [glossary.md](references/glossary.md) |103104Per-entity templates live in [templates/](templates/) — copy and adapt rather than writing from memory.105106A **complete end-to-end mini project** lives in [templates/example-project/](templates/example-project/). It passes `lint` and `test` as-is — use it as the source of truth for "show me how a realistic Featurevisor project hangs together" requests.107108## Core authoring rules109110These apply to every change. Internalize them; the references add depth, they do not override these.111112### 1. Bucketing and rule keys are append-only contracts113114- `bucketBy` is what keeps a user's experience consistent as percentages ramp. Pick once, never silently change it.115- Default for signed-in users: `userId`. For anonymous users: `deviceId` (or whatever the project calls it). **The attribute names may differ in this project — read the `attributes/` directory and ask the user which to use if it isn't obvious.** Do not invent attribute names.116- Combine attributes with a list (`bucketBy: [orgId, userId]`) or fall back via `bucketBy: {or: [userId, deviceId]}`.117- Each rule's `key` must be unique within its environment and **must not change once users are bucketed against it**. Renaming a rule key re-buckets users — the same user can lose or gain the feature. Add a new rule rather than renaming.118- Increasing `percentage` over time is safe **only** if the rule key stays stable.119120### 2. First matching rule wins121122Rules are evaluated top-to-bottom per environment. Put narrow targeting (e.g. country-specific rollouts at 100%) before the catch-all `segments: '*'` rule.123124### 3. Don't author rules that target unknown segments or attributes125126Before referencing `segments: foo` in a rule, confirm the segment exists (or create it). Same for attributes referenced inside segment conditions. Run `npx featurevisor lint` after edits — it catches dangling refs, percentage-sum errors in groups and variations, and schema mismatches.127128### 4. Variations weights sum to 100; group slot percentages sum to 100129130And a feature in a group cannot use a rollout `percentage` higher than its slot's percentage.131132### 5. Conditions must stay portable across SDKs133134Definitions are evaluated by SDKs in many languages, so lint enforces a portable subset — worth knowing before writing conditions:135136- **Dates** (`before`/`after`) need a full ISO 8601 timestamp **with timezone**, quoted: `"2026-11-27T00:00:00Z"`. Date-only or zone-less values fail lint.137- **Regex** (`matches`/`notMatches`) allows only `g`, `i`, `m`, `s` flags and rejects every `(?…)` construct — including non-capturing `(?:…)`, which is the usual surprise — plus backreferences and possessive quantifiers.138- Operators are checked against the attribute's declared type (numeric operators need `integer`/`double`, `includes` needs an `array`, etc.).139140Details and lint messages: [operators.md](references/operators.md#portable-conditions-cross-sdk-subset).141142### 6. After any edit, lint143144```bash145npx featurevisor lint146```147148If you wrote or changed a test spec, also run:149150```bash151npx featurevisor test --keyPattern=<theKey>152```153154## CLI: run freely155156All `featurevisor` CLI commands are local and safe to run without confirmation, with two caveats:157158- Bare `build` is a **CI command** — it increments `.featurevisor/REVISION` and updates state files, which only CI should commit. For local builds (yours *and* the user's), default to `--no-state-files`: same datafiles, same success/failure confirmation, no state side effects.159- `promote --apply` (sets projects) **writes definition files** — preview first and treat applying like any other edit ([sets-promotions.md](references/sets-promotions.md)).160161```bash162npx featurevisor build --no-state-files163```164165The most useful commands for an authoring agent (full reference in [cli.md](references/cli.md)):166167| Command | Purpose |168| --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |169| `npx featurevisor config --json --pretty` | Project configuration |170| `npx featurevisor info` | Counts of features / segments / attributes / tests |171| `npx featurevisor lint` | Validate definitions (run after every edit) |172| `npx featurevisor list --features --json [--filters…]` | Find features by tag, env, variable, archived, etc. |173| `npx featurevisor list --datafiles --json` | List generated datafile paths |174| `npx featurevisor list --segments --json` | List segments |175| `npx featurevisor list --attributes --json` | List attributes |176| `npx featurevisor list --variables --json` | List global variables |177| `npx featurevisor list --groups --json` | List exclusion groups |178| `npx featurevisor list --schemas --json` | List reusable schemas |179| `npx featurevisor list --targets --json` | List targets |180| `npx featurevisor find-usage --segment=<key>` | Where a segment is used |181| `npx featurevisor find-usage --attribute=<key>` | Where an attribute is used |182| `npx featurevisor find-usage --feature=<key>` | Feature usage details |183| `npx featurevisor find-usage --variable=<key>` | What a global variable depends on |184| `npx featurevisor find-usage --unusedSegments` | Dead segments |185| `npx featurevisor find-usage --unusedAttributes` | Dead attributes |186| `npx featurevisor find-duplicate-segments` | Segments with identical conditions |187| `npx featurevisor evaluate --environment=<e> --feature=<k> --context='{…}'` | Why a feature evaluates the way it does (debug) |188| `npx featurevisor evaluate --environment=<e> --variable=<k> --context='{…}'` | Why a global variable has its value |189| `npx featurevisor assess-distribution --environment=<e> --feature=<k> --context='{…}' --populateUuid=userId --n=1000` | Simulate rollout distribution |190| `npx featurevisor test [--keyPattern=…] [--assertionPattern=…]` | Run test specs |191| `npx featurevisor build --no-state-files` | Build datafiles without touching local revision/state |192| `npx featurevisor catalog` | Browsable read-only UI of the whole project |193194**Prefer the CLI over grepping** when answering questions like "what features use segment X?", "which features are enabled in production?", or "why does feature F evaluate to disabled for this context?". The CLI's `--json` output is parseable and authoritative.195196Use optional, repeatable `--target=<target>` selection with `build`, `test`, `evaluate`, `benchmark`, `assess-distribution`, `list --features`, and `info` when the question concerns deployed target datafiles. Runtime commands process each selected target independently. `build --json` and `build --print` accept only one target because they emit one datafile.197198## Changes ship through Git199200Featurevisor is GitOps: nothing you write takes effect until it travels the pipeline —201202**edit → PR review → merge → CI (lint, test, build) → datafile deployed to CDN → each app's next datafile refresh.**203204Practical consequences:205206- **Don't commit or push unless asked.** Editing files and running the CLI is your job; landing the change is the user's (or their CI's).207- Keep one logical change per branch/PR (a rollout bump, a new feature, a cleanup) — flags get reviewed like code, and small diffs get approved fast.208- Update or add the matching `.spec.yml` in the same change when behavior expectations shift.209- When the user asks **"when will this be live?"**, walk that pipeline: after merge, CI deploys the datafile, and apps pick it up on their next refresh (an app polling every 5 minutes lags up to 5 minutes). For emergency paths, see the kill-switch recipe in [recipes.md](references/recipes.md).210211## Common authoring flows212213### Starting a brand-new project2142151. Run the setup interview from [Orient yourself first](#no-project-yet-interview-then-scaffold) — environments, sets or not, tags/targets, format, bucketing identity.2162. Scaffold in an empty directory (a new repo, separate from app code) with the closest `init --example=…`, then `npm install`.2173. Adjust `featurevisor.config.js` until it matches the interview answers exactly (`environments`, `tags`, `sets`, `defaultBucketBy`, parser).2184. Replace the scaffolded example entities with the user's first real attribute → segment → feature, in that order (features reference segments; segments reference attributes), and matching targets.2195. `npx featurevisor lint && npx featurevisor test && npx featurevisor build --no-state-files` to prove the pipeline.2206. Offer the CI/CDN deployment setup from [building-datafiles.md](references/building-datafiles.md) when they're ready to ship — and `npx featurevisor catalog` so they can see what they built.221222### Adding a new feature flag2232241. Read the existing `features/` directory to match conventions (file naming, comment style).2252. Confirm the attribute used for `bucketBy` exists in `attributes/`; ask the user which to use if multiple plausible options exist (e.g. `userId` vs `deviceId`).2263. Create `features/<key>.yml` from [templates/feature.yml](templates/feature.yml).2274. If targeting specific segments, ensure each referenced segment exists in `segments/` — create it from [templates/segment.yml](templates/segment.yml) if not.2285. Run `npx featurevisor lint`.2296. Offer (don't force): "I can add a `tests/features/<key>.spec.yml` covering this — want me to?" If yes, use [templates/test-feature.spec.yml](templates/test-feature.spec.yml).230231### Adding variations (A/B test)232233Read [features.md](references/features.md) on variations, then use [templates/feature-with-variations.yml](templates/feature-with-variations.yml). Remind the user that weights sum to 100 and the `control`/`treatment` names are conventional only. If they ask how results get measured, that's [tracking.md](references/tracking.md).234235### Adding variables (remote config)236237Read [variables-schemas.md](references/variables-schemas.md) — covers all variable types, the inline JSON-Schema-ish form, reusable `schemas/`, variation-level variables, rule-level `variables:` and `variableOverrides:`, and the `mutations` feature for deep-merge overrides. Use [templates/feature-with-variables.yml](templates/feature-with-variables.yml) as the starting shape.238239### Adding a global variable (config no single feature owns)240241**Ask this first whenever someone wants a configurable value:** is the value owned by a feature, or does it stand on its own? A support address, a retry limit, a CDN base URL, a rate limit shared by four services. None of these is an attribute of any one flag, and hanging them off an unrelated feature is the mistake to head off. Those belong in `variables/<key>.yml` as **global variables**.242243Read [global-variables.md](references/global-variables.md) and start from [templates/variable.yml](templates/variable.yml). The differences from feature variables that catch people out:244245- **Never bucketed.** No `bucketBy`, no `percentage`, no variations. The first matching override wins for everyone it matches, so the value is deterministic for a given context. There is no way to roll one out to 10% of users; if that is what they want, they want a feature.246- **`requiredFeatures` instead of a parent.** Gate the value on one or more features (`{ feature, enabled?, variation? }`, `enabled` defaults to `true`). When unmet the SDK returns `disabledValue`, or `defaultValue` with `useDefaultWhenDisabled: true`, and reports `reason: required_features_unmet`.247- **Overrides may nest**, unlike the flat overrides on feature rules and variations. They follow the same environment rule as `rules` and `force`: a map keyed by environment when the project declares `environments`, a direct list when it does not. Give every override a `key` and keep those keys unique across the whole tree for one environment.248- **Read them with the two-argument SDK call**, `getVariable(variableKey, context)`, not the three-argument feature form. Go, Rust, and Elixir cannot dispatch on that, so they expose `GetGlobalVariable` / `get_global_variable` instead ([sdk-other-languages.md](references/sdk-other-languages.md#global-variables)).249250Before changing or deleting one, run `npx featurevisor find-usage --variable=<key>` for what it depends on; consuming applications are found by searching app code, not the project ([querying.md](references/querying.md)).251252### Complex targeting (and/or/not)253254Both segment `conditions` and feature rule `segments` support `and`, `or`, `not` with nesting. See [segments.md](references/segments.md) and [templates/segment-complex.yml](templates/segment-complex.yml).255256Important `not` rule: multiple direct children are treated as an implicit **AND** and then negated. So `not: [A, B]` means `not (A and B)`, not `not A and not B`. For "none of these match", wrap them in `or`: `not: [{ or: [A, B] }]`.257258### Mutual-exclusion experiments259260Read [groups.md](references/groups.md). Plan slot percentages **before** adding rules — once users are bucketed in a group, changing slot percentages re-buckets them. Use [templates/group.yml](templates/group.yml).261262### Force-enabling for QA / a specific user263264Use `force:` on the feature (per-environment), not `rules`. No `key`/`percentage` needed. See [features.md](references/features.md#force).265266### Promoting between sets ("move X to staging/production")267268Only in sets projects. Read [sets-promotions.md](references/sets-promotions.md), then: preview with `npx featurevisor promote --from=<a> --to=<b> --includeFeatures="<key>"`, show the user the created/updated/conflicts summary, apply with `--apply` on their go-ahead, and lint + test the destination set. Use `promotable: false` to protect lane-specific definitions, feature rules, and keyed test assertions from being overwritten.269270### Debugging an evaluation271272Use `npx featurevisor evaluate --environment=<e> --feature=<k> --context='{…}' --verbose` rather than reading the YAML and reasoning by hand. The evaluation flow (sticky → required → forced → rules → bucketing) is documented in [features.md](references/features.md#evaluation-flow). If the surprise is in an application rather than the project, also check the app's actual context and datafile revision ([sdk-javascript.md](references/sdk-javascript.md#why-did-it-evaluate-that-way)).273274### Querying ("what's enabled where?", "who uses this segment?")275276See [querying.md](references/querying.md). It shows the right `list`/`find-usage`/`evaluate` invocations for the common questions a developer asks about an existing project, plus the Catalog for browsing.277278### Visual review with Catalog279280`npx featurevisor catalog` serves a read-only UI of the whole project at `http://127.0.0.1:3000` **in watch mode** — it rebuilds and reloads the browser whenever definition files change. That makes it the ideal companion to an authoring session:2812821. Start it once as a background process (it's local and read-only — safe to leave running).2832. If you have a browser tool, open `http://127.0.0.1:3000` in it; otherwise give the user the URL.2843. Author changes as usual — every edit shows up in the Catalog on save, so the user watches features, rules, variables, and test coverage evolve visually while they prompt you.285286Offer this proactively when a session involves several authoring changes or when the user is less comfortable reading YAML — prompting plus a live Catalog is the best way to experience Featurevisor. Details in [querying.md](references/querying.md).287288### Upgrading a v2 project or application to v3289290Read [upgrading-to-v3.md](references/upgrading-to-v3.md). Do the **project repo first, applications after**: the datafile schema is unchanged, so v2 SDKs keep reading v3-generated datafiles and each app can upgrade on its own schedule. The one thing to fix before publishing v3 datafiles is multi-child `not:` in rule segments, whose meaning changed. Lead with `npx featurevisor lint` after upgrading the CLI: its errors are the worklist.291292### Recipes for higher-level use cases293294When the request matches a named pattern — A/B test, multivariate, mutual exclusion, dependencies, remote config, entitlements/RBAC, kill switch, scheduled/time-window release, staged rollout ladder (employees → beta → everyone), app version gating, backend migration, stale-flag cleanup, testing in production, deprecation, trunk-based development, microfrontends, decoupling release from deploy, ownership — open [recipes.md](references/recipes.md) and adapt the matching section. It links back to the granular references for shape details.295296## Application integration (SDKs)297298When the task is consuming features from application code:299300- **JavaScript / TypeScript / Node / browser / edge** → read [sdk-javascript.md](references/sdk-javascript.md) in full. It covers install, context, all evaluation methods, datafile refresh and on-demand loading, events, sticky, server-side child instances (`spawn`), diagnostics, and modules.301- **React / React Native** → [sdk-react.md](references/sdk-react.md). **Vue** → [sdk-vue.md](references/sdk-vue.md).302- **Type-safe bindings** (generated `isEnabled`/`getVariation` with compile-checked keys) → [code-generation.md](references/code-generation.md).303- **Other languages** — SDKs are **cross-platform**: Go, Python, Ruby, Java, Kotlin/Android, Swift, PHP, and more → [sdk-other-languages.md](references/sdk-other-languages.md). Every SDK consumes the same datafiles and is verified against a shared conformance contract, so a user bucketed into `treatment` in a browser gets `treatment` on the backend and on mobile too — one project can serve an entire polyglot stack. That file also covers each SDK's CLI for running the project's own test specs through that language.304- **OpenFeature** — if the team standardizes on the vendor-neutral [OpenFeature](https://openfeature.dev/) API, providers exist for Node.js, browsers, Go, Swift, Java, Ruby, Python, and PHP → [openfeature.md](references/openfeature.md). Optional: the native SDKs are unaffected and remain the simpler choice when Featurevisor is the only flag system.305- **Framework guides** (Next.js, Express, Fastify, Astro, Nuxt): <https://featurevisor.com/docs/frameworks>.306307Key facts that prevent most integration mistakes: evaluations are local and synchronous (no network at evaluation time); the app must load a **datafile** (built and deployed from the project repo) and decide its own refresh strategy; feature keys, variable keys, and attribute names must match the project's definitions exactly — verify against the project (or its Catalog) rather than guessing.308309## What not to do310311- Do not change a feature's `bucketBy` or a rule's `key` "to clean things up" — that re-buckets users.312- Do not invent attribute, segment, or feature key names — in YAML or in application code. Verify they exist; create them explicitly if needed.313- Do not rename or delete attributes/segments before checking `find-usage`.314- Do not add `expose:` unless the user asks — it's a short-term migration tool.315- Do not run bare `build` locally — incrementing REVISION and state files is CI's job. Local builds always get `--no-state-files`.316- Do not skip `npx featurevisor lint` after edits.317- Do not author project definitions inside an application repo — they belong in the Featurevisor project repo.