From ea504f578923342fef6c354a64b7839738f7e9b3 Mon Sep 17 00:00:00 2001 From: Evan Phyillaier Date: Sat, 8 Aug 2026 12:55:56 -0400 Subject: [PATCH] Capture the post-v1.9 idea backlog Fifteen ideas from the owner, written down before they evaporate. Captured, not designed - deliberately a backlog rather than a plan, because on this project jumping straight to a plan is how ideas pick up accidental requirements. Each entry records the idea, what it touches, and anything already known that constrains it. Three findings came out of checking rather than assuming: - "The leaderboard should show 0s after a singularity" is a BUG with a located cause: leaderboardService.js filters every board with `.value > 0`, so a player reset to 0 legacyCores vanishes instead of showing 0. The filter is not gratuitous (it keeps never-played accounts off every board), so the fix has to tell "reset" apart from "never started" - lifetimeFlopsAllTime survives prestige and can do exactly that. - Multi-provider account linking needs no migration. `identities` is keyed on (provider, provider_id) with a user_id FK and an index on it; two rows pointing at one users.id is already the intended shape, which is why v1.7 split identities out of users. It is a flow problem, not a schema one. - Timed hazards and Grid downtime already have a precedent in scheduleAnomaly: choose the moment ahead, store it, let lazy evaluation notice it passed. Rolling an event when a request happens to arrive cannot reconcile against offline progress, and offline progress is a headline feature. Two ideas are flagged as needing a different shape than proposed, both for safety rather than taste: event-authored custom CSS would hand event_coordinator - deliberately the lowest role - a data-exfiltration primitive via attribute selectors and url(), so a named effect registry gets the same snow and falling money without shipping a stylesheet; and joining accounts on matching email is the account-takeover vector already documented in authentication-methods.md, so linking should be an authenticated act proven by a live session, never by a string match. Co-Authored-By: Claude Opus 5 --- .../2026-08-08-post-v1.9-idea-backlog.md | 335 ++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-08-post-v1.9-idea-backlog.md 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.