diff --git a/docs/superpowers/specs/2026-08-08-post-v1.9-idea-backlog.md b/docs/superpowers/specs/2026-08-08-post-v1.9-idea-backlog.md new file mode 100644 index 0000000..ef1b41c --- /dev/null +++ b/docs/superpowers/specs/2026-08-08-post-v1.9-idea-backlog.md @@ -0,0 +1,335 @@ +# Post-v1.9 idea backlog + +**Status: CAPTURED, NOT DESIGNED.** Owner's ideas from 2026-08-08, written down +so they are not lost. Nothing here is approved, scoped or scheduled. + +This is deliberately *not* a plan. The process that has worked on this project +is brainstorming → spec → plan → subagent-driven implementation → whole-branch +final review, and skipping to a plan is how ideas acquire accidental +requirements. Each item below is the raw idea, what it touches, and anything +already known that constrains it. + +Several of these are large enough to be their own release. A plausible grouping +is at the bottom. + +--- + +## Two constraints that cut across most of this + +**1. The economy is server-authoritative and lazily evaluated.** Nothing ticks +in real time — state is computed from the last-seen timestamp forward when a +request arrives. So **anything time-based must be *schedulable*, not rolled +live**: pick the moment ahead, store it, and let evaluation notice it has +passed. The existing precedent is `scheduleAnomaly` in `shared/reducer.js`: + +```js +server.nextAnomalyAt = now + minDelayMs + rng() * (maxDelayMs - minDelayMs); +server.anomalyExpiresAt = server.nextAnomalyAt + windowMs; +``` + +Randomness is injected (`rng = Math.random`) rather than called directly, which +is what makes it testable. Every "bad events happen at a certain time" and +"Grid nodes go down" idea below should extend this pattern rather than invent a +new one. An event rolled at the moment a request happens to arrive cannot be +reconciled against offline progress, and offline progress is a headline feature +of this game. + +**2. `users.id` is `provider:providerId` and is referenced by three foreign +keys.** It never changes. Anything touching identity has to work without +rewriting it. + +--- + +## A. The leaderboard hides reset players — this one is a bug + +> "The leaderboard should show 0s even after users reset their cores after a +> singularity." + +**Diagnosed.** `server/leaderboardService.js` builds every board with: + +```js +.filter((r) => r.value > 0) +``` + +So a player whose `legacyCores` went to 0 in a Singularity does not show as +`0` — they **vanish from the board entirely**. Same for any board whose value +resets. + +The filter is not gratuitous: without it every registered account that has +never played would sit on every board at 0. So the fix is not "delete the +filter", it is "distinguish a player who reset from one who never started". +`allTimeFlops` reads `meta.stats.lifetimeFlopsAllTime`, which survives every +prestige, so it can discriminate — include a player when they have any +all-time activity, even when *this* board's value is 0. + +Worth deciding explicitly: should a reset player rank *below* everyone with a +positive score (0 sorts last naturally), or be pinned with a "just prestiged" +marker? The second is more interesting — a Singularity is an achievement, and +currently it looks like disappearing. + +Small, self-contained, and fixable well before the bigger items. + +--- + +## B. Risk and mitigation — the largest new system here + +> "Bad events should happen at a certain time (like drive failures, technician +> failure, malware). You then have to buy antivirus or something else in the +> store to get rid of the bad effects. Add the ability to buy cybersecurity +> insurance to offset ransomware automatically (but it gets used up) — backup +> ISP for ISP outages — backup hard drives for hard drive failures." + +This is a genuine second axis for the game: today everything is monotonic +growth, and this introduces loss to defend against. + +**Shape it borrows from:** `scheduleAnomaly` already schedules a timed +opportunity with an expiry window. A hazard is the same machinery with the sign +flipped — schedule it, let it fire, let it expire. + +**The interesting design question** is the mitigation economy, because there are +two distinct kinds and they should feel different: + +- **Reactive** ("buy antivirus to get rid of the bad effect") — a cure. The + player is already hurting and pays to stop it. +- **Prepaid and consumable** ("insurance ... but it gets used up", backup ISP, + backup drives) — a hedge bought before anything happens, which silently + absorbs one hit and then needs restocking. + +The prepaid kind is the better mechanic: it rewards planning, creates a +recurring sink, and the moment it silently saves you is satisfying *only if the +game tells you it did*. A consumed insurance policy must produce a visible +"ransomware absorbed" notification, or the player never learns the hedge was +worth buying. + +**Constraints:** +- Hazards must respect offline time. If a player is away 12 hours and a drive + fails at hour 3, evaluation has to apply it from hour 3 — not on next login — + or the offline-gain calculation lies. +- There has to be a floor. A player returning to a wrecked rack with no + currency to repair it is a dead save. Consider capping hazard severity by + progression, or guaranteeing repairs are always affordable from what the + hazard itself didn't destroy. +- Cold Storage is offline-gated by design; decide whether hazards can strike it + or whether it is a safe harbour (a good reason to keep tapes there). + +--- + +## C. Make the Grid dynamic + +> "things like home volunteers and university clusters aren't on all the time, +> there could be downtime or other maintenance" + +Thematically strong — it is exactly how volunteer compute behaves in reality, +and it gives the Grid a personality distinct from the Racks. + +Same scheduling constraint as B. Natural extensions: university clusters idle +on a term schedule, home volunteers drop off in the evening, and a maintenance +window the player can *see coming* so it is planning rather than punishment. +The difference between "the Grid is unreliable" and "the Grid is unreliable in +a way I can route around" is whether the downtime is visible in advance. + +Interacts with B: if hazards and Grid downtime both reduce output, they need a +shared notion of "capacity currently offline" so the UI can explain a slowdown +with one coherent story instead of two competing ones. + +--- + +## D. Overclock rework + +> "The overclock should be a multiplier against the Racks, but then should turn +> off a rack at random when you overheat" + +Converts Overclock from additive to a real risk/reward dial. Note v1.6 already +made venting percentage-based and added an auto-dismissing overheat popup, so +the heat UX this builds on is recent and deliberate — worth re-reading that +work before changing what overheating means. + +A rack switching off at random needs the same determinism treatment: which rack +must be derivable, not rolled fresh on each evaluation, or two clients +reconciling the same overheat could disagree about which rack died. + +--- + +## E. Buy-to-milestone buttons + +> "Add a button on the Racks, Grid, and Overclock that buys to the next +> milestone" + +Straightforward quality-of-life, and the kind of thing that quietly removes a +lot of clicking. + +**Known trap:** `UpgradesPanel.jsx` and `SingularityPanel.jsx` read upgrade +maximums from the static definition's `maxLevel` rather than the live +`config.upgrades.maxLevels[id]`, so admin balance edits do not reach them. +`ColdStoragePanel.jsx` does it correctly. A "buy to next milestone" button that +inherits the static-def bug would compute its target from numbers the server +does not agree with, and the server is authoritative — so it would visibly +overshoot or stall. Fix the config read as part of this, not after. + +--- + +## F. A third prestige + +> "What about a third prestige that is a HARD reset with Cold Storage and sets +> back to absolute zero with something special. Maybe something with a portal" + +**Note the deliberate inversion.** Cold Storage's Tapes and its 7-upgrade tree +were specified to *survive* Migrate and Singularity — that permanence is what +makes the offline lane feel worth investing in. A third prestige that wipes it +is not an extension of the existing ladder, it is a reversal of a design +promise, and it needs to buy something proportionate. + +That is not an objection — a prestige that finally takes the thing nothing else +could take is a strong hook, and "absolute zero" earns a genuinely different +reward tier. But it should be designed as such rather than slipped in as a +third rung. + +The portal idea is worth developing: it suggests the reward is *access* to +something rather than another multiplier, which would be a welcome change of +shape at that depth. + +--- + +## G. Unique items you can place in your racks + +> "Brainstorm a way to have unique items as rewards that you can add to your +> racks. This should be an event and special achievement award" + +The most open-ended item here, and the one that most changes what the game is — +it introduces collection and identity alongside optimisation. + +Things to settle early: are items cosmetic, functional, or both? Do they +survive prestige (if they are event rewards, almost certainly yes — an +unrepeatable event reward that a prestige destroys is a trap)? Are they +tradeable (no, presumably — that is an economy)? And how do they render, since +the rack view is the game's main visual surface. + +Ties naturally to F's "something special" and to the event system, which +already has per-user participation tracking and a rung ladder to hang rewards +on. + +--- + +## H. Event theming with custom CSS + +> "Custom CSS import for the events to allow leaves and snow falling for the +> winter ones. Money falling for the Black Friday event" + +Great instinct — seasonal events currently change numbers and copy but not +atmosphere. + +**Do not implement this as arbitrary operator-supplied CSS.** Events are +authored by `event_coordinator`, which is deliberately the *lowest* of the +three roles (owner > admin > event_coordinator). Arbitrary CSS injected into an +authenticated page is not cosmetic — attribute selectors combined with +`url()` can exfiltrate page contents to a remote host, and it would hand the +least-trusted role a data-exfiltration primitive against every player, +including the owner. + +The safe version gives the same result: a **named effect registry** in the +client (`snow`, `leaves`, `money`, `embers`, …), with events selecting an +effect id plus a few bounded parameters (density, colour from a fixed palette, +speed). Coordinators get the creative control they actually want; nobody gets +to ship a stylesheet. If freeform styling is genuinely needed later, restrict +it to owner-authored themes, not event config. + +--- + +## I. Shard sinks, including themes + +> "Shard store to buy boosts? We need some other way to spend Shards. Also add +> a way to buy different themes" + +A real gap — Singularity Shards currently have one destination, so past a point +they accumulate meaninglessly. + +Themes are a particularly good sink: purely cosmetic, permanent, no balance +risk, and they pair with H's effect registry (one visual system serving both +event atmosphere and player-bought themes). Boosts need balance care since +shards are unbounded over time — prefer permanent small unlocks or repeatable +consumables over stacking multipliers. + +--- + +## J. Badge progress bars + +> "For badges, add a progress bar for each badge to show the user where they +> are in the process" + +Achievements exist and unlock silently against thresholds. Showing distance to +the next one turns a list of things you happen to have into a set of goals. + +Requires each achievement to expose its progress as (current, target) rather +than just a boolean — a change to how achievements are defined, not just +rendered. Some may not have a meaningful scalar (one-off events); those should +degrade to locked/unlocked rather than a fake bar. + +--- + +## K. Minigame high scores + +> "Track high scores in Games" + +Small and self-contained. Minigame sessions are already server-mediated +(`/api/minigame/start` and `/finish`), so the score already reaches the server +and there is somewhere honest to record a best. Pairs naturally with the +leaderboard work in A. + +--- + +## L. Link multiple auth providers to one account + +> "When adding SuperTokens, we should add the ability to link multiple auth +> types and join based on email. Multiple auth types (ie. GitHub and Discord) +> should be allowed to be linked to a single user" + +**The schema already supports this.** `identities` is keyed on +`(provider, provider_id)` with a `user_id` foreign key and an index on it — +two identity rows pointing at one `users.id` is exactly the intended shape, and +v1.7 split identities out of `users` precisely so this would be possible. +`users.id` never has to change. So this is a *flow* problem, not a migration. + +**But "join based on email" is the dangerous half, and it is already documented +as such** in `docs/authentication-methods.md`: automatically linking accounts +that share an email address is an account-takeover vector. If someone registers +a GitHub account using an email that matches an existing Discord player, they +inherit that player's save, shards and admin roles. The provider asserting the +email does not make it safe — email ownership is not verified consistently +across providers, and RackStack currently uses email for nothing at all. + +**The safe design:** linking is an authenticated, deliberate act. A logged-in +player goes to settings, presses "link Discord", completes that OAuth flow, and +a second `identities` row is written pointing at *their* existing `user_id`. +Identity is proven by the live session, never by a string match. Email matching +can at most *suggest* "this looks like your account, sign in with GitHub to +link it" — never perform the join. + +Also relevant if email enters the picture: adding any email-bearing recipe +makes the previously-accepted nodemailer advisory live again. + +--- + +## A plausible grouping + +Not a commitment — just how these seem to cluster: + +- **Quick wins, mostly independent:** A (leaderboard zeros — arguably a bug fix + that should not wait), E (buy-to-milestone, with the config-read fix), K + (minigame high scores), J (badge progress). +- **One release: risk & reliability.** B and C and D share scheduling + machinery, a "capacity offline" concept and a notification surface. Doing + them together is much cheaper than separately, and they are incoherent apart. +- **One release: identity.** L on its own, small and security-sensitive enough + to deserve its own review. +- **One release: expression.** H (effect registry) + I (shard store, themes), + since both want the same visual system. +- **Needs real design work first:** F (third prestige) and G (unique items). + These change what the game is, and G in particular should be brainstormed + properly before anyone estimates it. + +## Standing obligation + +Any of these that ships a feature tour must also append its steps to +`client/src/game/data/tours/onboarding.js` — completing the onboarding tour +marks every registered tour complete, which is only correct while onboarding +remains a superset. No test catches a violation.