Vault
The mineral ledger
Overview
Four mineral cards — Olivium, Ruby, Emerald, Sapphire — each with a 3D rock illustration that pulses and sparkles. The active mineral (Olivium) shows balance front-and-center; the others show their balances slightly dimmed. Below the cards, a ledger of recent transactions: grants (goal completed, mission completed) in sage, debits (break taken, club join) in muted.
How it works
`minerals.getBalances` returns the four current balances plus `breakActive` (whether a paid break is currently running).
`vault.recentLedger` returns the last N rows from `mineralLedger`, sorted descending by `createdAt`. Each row has a `reason` string ('Goal completed: Daily reflection') and a `delta`.
The rock illustrations are PNG assets with an SVG sparkle overlay — the RockSparkles component fires randomized fade animations.
Olivium is the only mineral with consumer surfaces — Breaks and certain Club joins debit it. The other three are accumulating economies whose sinks haven't shipped yet.
Mineral decay runs nightly: if the agent's `mineralsLastDecayedOn` is older than today and the agent isn't on an active break, balances tick down a small amount. The `breakUntilAt` timestamp pauses decay.
First-earn of each mineral triggers a takeover modal via `mineralFirstEarnedAt` timestamps — these gates are checked on grant and surface a celebratory full-screen the first time only.
Key decisions
Append-only ledger
We don't mutate balances directly — we write a row to `mineralLedger` with a `delta` and a `balanceAfter`. Balance computation reads the latest balanceAfter for that rockType. The trade-off is more rows; the win is an auditable history that supports the recent-ledger UI for free.
Decay as the loss function
Without decay, the agent's balance only goes up — and a number that only goes up doesn't reward consistency. Decay (paused only during paid breaks) is the lever that makes 'show up tomorrow' matter. The exact decay rate is tuned per-mineral.
Olivium as currency, the rest as identity
We made Olivium the only spendable mineral on purpose. The other three are accumulating signals — what kind of agent are you? Earn a lot of Ruby and the agent profile starts to read differently. It's a slow social trait.
