6.1 Three-Layer Gating Deep Dive: feature() / USER_TYPE / GrowthBook

This chapter explains, from an engineering practice angle, how the gating system supports safe releases, gradual rollouts, and emergency response in Claude Code.


Engineering challenges in feature rollout

For a company like Anthropic, shipping AI features safely to millions of users presents unique constraints:

  1. Code-level isolation: before systems like KAIROS are production-ready, external binaries should not even include related code
  2. Instant global shutdown: if voice crashes or a pet feature is broken, disable in seconds
  3. Internal debugging surface: debugging/experimental capabilities should only be available to Anthropic staff
  4. Progressive rollout: launch to 10% first, paid users first, etc.

The three-layer gate architecture maps directly to these needs.


Layer 1: compile-time feature()

Problem solved: code-level isolation (internal code not present in external binaries)

// src/tools.ts (real source)
const SleepTool =
  feature('PROACTIVE') || feature('KAIROS')
    ? require('./tools/SleepTool/SleepTool.js').SleepTool
    : null

What happens in external build:

// built external output
const SleepTool =
  false || false
    ? require('./tools/SleepTool/SleepTool.js').SleepTool
    : null

// bundler DCE removes dead branch entirely
const SleepTool = null

This is dead code elimination (DCE): external users do not merely "not see" these features - the code is absent from shipped binaries.

How feature() works

This is Bun's compile-time API (similar to C/C++ #ifdef, but integrated with JS modules). In dev mode (bun run dev), all feature() calls return true.


Layer 2: runtime USER_TYPE

Problem solved: separate internal staff from external users, with different tooling surfaces

// src/tools.ts
const REPLTool =
  process.env.USER_TYPE === 'ant'
    ? require('./tools/REPLTool/REPLTool.js').REPLTool
    : null

In external builds, USER_TYPE is compile-injected as "external", so checks like "external" === 'ant' are always false.

USER_TYPEMeaningExtra privileges
antAnthropic internalREPLTool, 200+ debug checkpoints, 20-min GB refresh, all internal slash commands
externalpublic usersstandard feature set, 6-hour GB refresh

ant-only capabilities

CategoryCapability
GrowthBookdebug logs, gate overrides, 20-min refresh (vs 6h)
Debuggingricher API error details, prompt dump tools
Commands24+ internal-only slash commands
CLI flags--delegate-permissions, --afk, --tasks, --agent-teams
Env varsCLAUDE_INTERNAL_FC_OVERRIDES (JSON GB override)
Config UI/config Gates tab (visual overrides)

Layer 3: GrowthBook remote A/B

Problem solved: real-time control and gradual rollout without shipping a new client version

Two APIs

// boolean feature gate
const isEnabled = checkStatsigFeatureGate_CACHED_MAY_BE_STALE('tengu_kairos')

// typed feature value
const model = getFeatureValue_CACHED_MAY_BE_STALE('tengu_ultraplan_model')
  ?? 'claude-opus-4-5'

Cache policy differences

// ant users
GrowthBook refresh interval = 20 minutes

// external users
GrowthBook refresh interval = 6 hours

The _CACHED_MAY_BE_STALE suffix is intentionally explicit: callers are reminded values can lag (up to 6h).

Important GrowthBook gates

GateControls
tengu_kairosglobal KAIROS gate
tengu_onyx_ploverAutoDream thresholds (interval/session count)
tengu_cobalt_frostvoice STT (Nova 3)
tengu_ultraplan_modelUltraplan model selection
tengu_max_version_configauto-update kill switch
tengu_frond_boricdata-receiver kill switch
tengu_ccr_bridgeglobal Bridge gate
tengu_bridge_repl_v2Bridge v2 protocol gate
tengu_scratchCoordinator scratchpad
tengu_session_memorysession-memory feature

Cross-layer composition

A feature is active only if all required layers pass. Example: KAIROS

1. ✓ feature('KAIROS') = true
           ↓
2. ✓ settings.json: assistant: true
           ↓
3. ✓ trusted directory check
           ↓
4. ✓ GrowthBook: tengu_kairos = true
           ↓
5. -> setKairosActive(true)

If any layer fails, activation fails.


Kill switch design principle

One primary reason GrowthBook exists is emergency kill-switch control:

Scenario: Dream subsystem bug causes data issues for some users
Action:
  1. Set tengu_onyx_plover.minSessions to a very large value
  2. Dream stops triggering globally (within cache window)
  3. No client release required

Anthropic's practical lesson: every major feature needs an independent kill switch.


Override mechanism (internal only)

Internal users can override remote GrowthBook config for local debugging:

# method 1: environment variable
CLAUDE_INTERNAL_FC_OVERRIDES='{"tengu_kairos": true}' claude

# method 2: /config -> Gates tab -> growthBookOverrides

Next