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:
EDITOR=vim rails credentials:editThe 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:
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 Length | Formula | Cost |
|---|---|---|
| 5 days | 100 + (5 × 5) | 125 XP |
| 20 days | 100 + (5 × 20) | 200 XP |
| 50 days | 100 + (5 × 50) | 350 XP |
| 80 days | min(100 + (5 × 80), 500) | 500 XP (capped) |
Tuning Guide
Too cheap (users trivially repair streaks):
- Increase
cost_baseorcost_factor - Example:
cost_base: 150, cost_factor: 10results in 5-day = 200 XP, 20-day = 350 XP
Too expensive (low repair acceptance):
- Decrease
cost_baseorcost_factor - Example:
cost_base: 50, cost_factor: 3results in 5-day = 65 XP, 20-day = 110 XP
Scaling not steep enough:
- Increase
cost_factorto make longer streaks more expensive to repair - Example:
cost_factor: 10makes each day worth 10 XP instead of 5
Scaling too steep:
- Decrease
cost_factoror increasemax_costto prevent very long streaks from being uncapped - Example:
cost_factor: 2, max_cost: 750flattens the curve and raises the ceiling
Monitoring Cost Distribution
To monitor what streak lengths are being repaired and what cost users are paying:
- Sentry ReleaseEvents: Filter by
gamification.streak_repairedevent - PostHog: Create a custom event property
streak_repair_costto track cost distribution by cohort - 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
Edit credentials:
bashEDITOR=vim rails credentials:editAdjust the values:
yamlstreak_repair: cost_base: 150 # Changed cost_factor: 8 # Changed max_cost: 600 # ChangedRestart the API:
bashpkill -f "rails server" # Stop rails server # Restart (loads new credentials)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:
# rails_api/app/services/gamification/streak_repair_offer_resolver.rb
INSURANCE_MONTHLY_ALLOWANCE = 1One 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.gitignorefor a reason. - Rotate
master.keyafter 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 whenRAILS_ENV=staging) - Production:
config/credentials/production.yml.enc(loaded whenRAILS_ENV=production)
- Staging:
- Local development: Uses
config/credentials.yml.enc(same for all developers; see.gitignoreformaster.key)
Further Reading
- Rails Credentials Guide
docs/operations/deployment.md— environment variable setup for staging/productiondocs/product/completed/streak-compassion-prd.md— Streak Repair feature context and success metricsdocs/operations/rollouts/v1.11-streak-compassion.md— Phase 51 rollout playbookdocs/operations/rollouts/v4.67-streak-insurance.md— Streak Insurance ramp plan and kill-switch runbook