#RankCards
Alessia Bot – Release v21.15.0 🚀

Leveling is live on #Discord and #Twitch! Earn #XP, unlock #RoleRewards, and check stats with #RankCards and #Leaderboards. Customize all settings in #IntraOS.

#AlessiaBot #IntraMind
November 8, 2026 at 12:31 AM
Two mechanisms that look redundant, and why only one of them was
## The setup: one product, two rankings Kingpin shows the same listings on two surfaces. `/feed` is a swipeable media feed; `/board` is the ladder people actually land on and share. They had **two different ranking formulas** : | ranked by ---|--- `/feed` | `rankCards()` — bid baseline, shifted by crowd votes, eviction at −5 `/board` | `candidateListings()` — `effectiveCents` only: money + 5%/day decay So the crowd could dethrone you inside the swipe feed, and _only_ there. On the main page, money still bought a permanent position. The product's central claim — _the crowd decides who stays on top_ — was silently false on the most-visited surface. Nobody designed this. It's residue: the board is the pre-pivot leaderboard (Phases 0–8), the feed came with the pivot (Phases 9–15), and the old surface was never retired. **Architectural drift usually looks like this — not a bad decision, but an old decision nobody re-opened.** The fix was to rank both through the same function. What made it interesting was what surfaced while doing it. ## Decision 1: decay looked redundant. It wasn't. Once the board ranked on votes, a fair question came up: _why decay a bid automatically at all? Downvotes already push bad listings down — isn't decay doing the same job twice?_ It sounds right. It's wrong, and the reason is worth internalising. **They answer different questions:** * A downvote answers **"is this bad?"** It requires a human to actively dislike the thing. * Decay answers **"you paid in March — why are you still #1 in September?"** It requires nobody. The gap between those is _indifference_. A listing that's merely dull collects roughly **zero** net votes forever. Nobody downvotes a product for being old. So removing decay means: pay once, avoid annoying anyone, hold the top spot permanently — precisely the thing the product promises cannot happen. **Indifference is not a downvote, and a system relying on downvotes alone is relying on indifference to do a job it can't do.** There was a second, less obvious cost. Decay is the only reason the escalating **refuel** price exists — positions must erode for a re-bid to mean anything. Deleting decay would have silently retired a revenue model and left working, tested code permanently unreachable. That consequence was nowhere near the code being discussed. **But the instinct behind the question was still correct.** With ~2 listings and effectively no voters, decay was the _only_ force acting on the board, it moved one direction, and the only way to recover position was to pay again. That doesn't read as "the crowd decides." It reads as the platform billing people for standing still — and it punishes founders for the _platform's_ lack of traffic, which isn't their fault. So the fix wasn't to the mechanism, it was to its **conditions** : decay stays fully implemented behind `FLAG_DECAY`, defaulting **off** , flipped on deliberately once vote volume makes the crowd the dominant force. // lib/scoring.mjs — one gate, honoured by BOTH effectiveCents and bidBaseCents, // so no surface can disagree with another about what a listing is worth. export const decayEnabled = (flags = {}) => flags?.decay === true; **Why a manual flag and not an automatic traffic threshold:** a threshold that trips on its own would erode every paid position the instant it crossed, with no warning to any founder. A flag someone flips means the moment is chosen, announceable, and reversible. Same end state, no ambush. ### The generalisable lesson > Before deleting a mechanism that looks redundant, write down the exact question each mechanism > answers. If the questions differ, the redundancy is an illusion — and check what _else_ depends > on it, because load-bearing things are rarely labelled. A useful tell: the mechanisms overlapped in _effect_ (both push a listing down) but not in _trigger_ (dislike vs. time). Overlapping effects are what make two things look like one. ## Decision 2: founders don't vote A founder has a direct financial interest in every rival's rank. Once votes moved paid position on the main board, leaving founders able to vote would have turned the crowd signal into a proxy war between paying competitors — and made the cheapest listing on the platform a ticket to downvote the leader. Two design calls worth recording: **Scope: all voting, not just others' listings.** "Founders can't vote on other people's listings" immediately invites _"…so they can vote for themselves?"_ — and self-voting is the more blatant manipulation of the two. One rule with no edge cases beats a narrower rule that's harder to defend. **This rejection is loud, when every other rejection in that handler is silent.** The vote pipeline deliberately fails quietly, because a clear error tells a bot exactly which control it tripped. This case is the opposite: a _published policy_ , not a detection. Returning a fake success to a paying founder would be lying to a customer. // vote.mjs — 403 with a reason, not the usual silent { applied: false } if (identity.FounderId) return forbidden('Founders cannot vote. …'); > **Silence is a security tool, not a default.** Use it where disclosure helps an attacker tune. > Where the rule is public anyway, silence buys nothing and costs trust. ### The limit, stated honestly The link is per-device, not per-person, so it is a **deterrent and a statement of the rule, not an unconditional wall** — the same per-IP/per-ASN caps, confidence weighting, and velocity shadow-ban that resist any other bad actor are what actually backs it up. That distinction matters more than it sounds. A control described as airtight when it isn't produces worse decisions later, because someone downstream stops building the defence that actually does the work. ## A small thing that keeps recurring Wiring `FLAG_DECAY` meant adding it to the root stack **and** forwarding it to both nested stacks. The existing `FLAG_STREAK` was never forwarded — it only ever used each nested stack's default. Harmless while the default is what you want; invisible breakage the day someone tries to flip it. This is the third time today a parameter has been declared somewhere it couldn't actually be set from (see entry 001's `HostingerFrom`). **A config value you cannot demonstrate changing the behaviour of is not configuration — it's a constant with extra steps.** Worth testing the flip, not just the default, whenever a flag is introduced. _Originally published on the Kingpin build log._
dev.to
September 15, 2026 at 9:38 AM