Skip to content

Game Balance & Configuration Parameters

This document covers how to tune game-balance parameters in Objectuve (XP costs, progression formulas, gamification settings).

Last updated: 2026-09-20 Version: v4.67+


Overview

Game-balance parameters are configured via Rails encrypted credentials (config/credentials.yml.enc), which are environment-specific and never committed to git. Edit via:

bash
EDITOR=vim rails credentials:edit

The decryption key is stored in config/master.key (never committed; loaded from environment variable RAILS_MASTER_KEY in production).


Streak Repair XP Cost

Feature: Phase 51 — Streak Repair Window + Compassionate Break Messaging

XP Cost Formula

When a user repairs a broken streak, the XP cost is calculated as:

cost = min(BASE + (FACTOR × streak_length), MAX)

Configure via credentials:

yaml
streak_repair:
  cost_base: 100      # Base cost (XP)
  cost_factor: 5      # Cost per day of streak (XP/day)
  max_cost: 500       # Maximum cost cap (XP)

Defaults (if not specified): Base 100, Factor 5, Max 500.

Examples

Streak LengthFormulaCost
5 days100 + (5 × 5)125 XP
20 days100 + (5 × 20)200 XP
50 days100 + (5 × 50)350 XP
80 daysmin(100 + (5 × 80), 500)500 XP (capped)

Tuning Guide

Too cheap (users trivially repair streaks):

  • Increase cost_base or cost_factor
  • Example: cost_base: 150, cost_factor: 10 results in 5-day = 200 XP, 20-day = 350 XP

Too expensive (low repair acceptance):

  • Decrease cost_base or cost_factor
  • Example: cost_base: 50, cost_factor: 3 results in 5-day = 65 XP, 20-day = 110 XP

Scaling not steep enough:

  • Increase cost_factor to make longer streaks more expensive to repair
  • Example: cost_factor: 10 makes each day worth 10 XP instead of 5

Scaling too steep:

  • Decrease cost_factor or increase max_cost to prevent very long streaks from being uncapped
  • Example: cost_factor: 2, max_cost: 750 flattens the curve and raises the ceiling

Monitoring Cost Distribution

To monitor what streak lengths are being repaired and what cost users are paying:

  1. Sentry ReleaseEvents: Filter by gamification.streak_repaired event
  2. PostHog: Create a custom event property streak_repair_cost to track cost distribution by cohort
  3. Analytics query:
    sql
    SELECT AVG(xp_cost), MAX(xp_cost), MIN(xp_cost)
    FROM domain_events
    WHERE event_type = 'gamification.streak_repaired'
    AND created_at > NOW() - INTERVAL 7 days;

Applying Changes

  1. Edit credentials:

    bash
    EDITOR=vim rails credentials:edit
  2. Adjust the values:

    yaml
    streak_repair:
      cost_base: 150      # Changed
      cost_factor: 8      # Changed
      max_cost: 600       # Changed
  3. Restart the API:

    bash
    pkill -f "rails server"  # Stop
    rails server             # Restart (loads new credentials)
  4. Verify: Check logs for new values or test a repair mutation to confirm the new cost is calculated.

No database migration needed — credentials are read at runtime, not stored in the database.


Streak Insurance Monthly Allowance

Feature: v4.67 — Streak Insurance for Supporters

Streak Insurance (Gamification::ClaimStreakInsurance) is a Supporter-only recovery lane that costs no XP — instead it's rationed by a monthly allowance. Unlike Streak Repair's XP cost above, the allowance is not a Rails credential; it's a hardcoded constant:

ruby
# rails_api/app/services/gamification/streak_repair_offer_resolver.rb
INSURANCE_MONTHLY_ALLOWANCE = 1

One claim per calendar month, tracked via a unique index on (user_id, period_key) on the streak_insurance_claims table (period_key is 'YYYY-MM', computed in the claiming user's own timezone). This applies uniformly across every Supporter tier (monthly, yearly, lifetime) — a calendar month is the only period all three tiers share, since lifetime has no billing period at all.

Tuning: changing the allowance is a code change (edit the constant), not a credentials edit — there's no rails credentials:edit equivalent for this knob today. A future pass could move it to credentials if per-cohort tuning becomes useful, the same way streak_repair's cost formula already is.

A claim never touches goal.streak_repaired_count (the XP lane's own once-per-goal-lifetime guard) — the allowance rations how often a Supporter can claim, not whether claiming costs the free XP-repair lane anything.

Monitoring: filter Sentry ReleaseEvents / domain_events by gamification.streak_repaired with via: 'insurance' in the payload to distinguish insurance claims from XP repairs (both lanes publish the same event type — see DDD § Domain Events).


Other Game-Balance Parameters

(This section will grow as new features ship and become tunable. Examples: XP rewards for goal completion, badge rarity weights, challenge multipliers, etc.)


Security Notes

  • Never commit config/master.key — it's in .gitignore for a reason.
  • Rotate master.key after any Git history re-write — if the key is exposed, regenerate: rails credentials:edit --force
  • Staging & Production: Both environments use separate encrypted credentials files:
    • Staging: config/credentials/staging.yml.enc (loaded when RAILS_ENV=staging)
    • Production: config/credentials/production.yml.enc (loaded when RAILS_ENV=production)
  • Local development: Uses config/credentials.yml.enc (same for all developers; see .gitignore for master.key)

Further Reading

  • Rails Credentials Guide
  • docs/operations/deployment.md — environment variable setup for staging/production
  • docs/product/completed/streak-compassion-prd.md — Streak Repair feature context and success metrics
  • docs/operations/rollouts/v1.11-streak-compassion.md — Phase 51 rollout playbook
  • docs/operations/rollouts/v4.67-streak-insurance.md — Streak Insurance ramp plan and kill-switch runbook

Loading…