Entitlements v1 + launch-pacing aura tuning#46
Merged
Conversation
Drops the hourly regen from 50 -> 10 and the first-visit guest credit from 50 -> 5. Prepares the aura curve for the Show HN / Reddit / Bookface launches where the previous ratios would have let a viral spike blow through a town's reservoir uncontrollably. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces a first-pass entitlement system so a viral launch can't blow
past the caps we can afford. Every enforceable cap lives directly on
the entity it applies to; the append-only EntitlementGrant table is
history only, populated later when the purchase/milestone/level-up flow
ships.
Schema
- User.tier ("free"), User.maxTowns (3), User.maxTotalCustomPlots (20)
- Town.level (1), Town.xp (0), Town.maxNpcs (10), Town.maxBuildings (10),
Town.maxCustomPlots (5), Town.maxActiveGuests (10)
- EntitlementGrant model with (userId, townId?, target, delta, source,
reason?, ref?) — schema only, no writers yet
Enforcement
- pickTown counts existing towns vs User.maxTowns, throws
town-limit-reached with the limit attached
- /api/towns/me surfaces that as 403 with a human-readable detail
- POST /api/town checks buildings/npcs/customPlots against per-town
caps and (customPlots + otherTowns' totals) against User's
maxTotalCustomPlots via jsonb_array_length; returns 403 with issues
keyed on { path, message, requested, limit }
- realtime-token polls Centrifugo presence_stats on the positions
channel; refuses non-owner tokens with 429 when numUsers >=
maxActiveGuests + 1 (owner slot reserved). Fails open if Centrifugo
is unreachable
CLI
- shared/scaffold.ts postJson now prefers server `detail` for the
thrown error message so `town new` shows the town-limit hint verbatim
- deploy.ts already renders `path`/`message` on issues + top-level
detail — no CLI change needed there
Not built yet: grant flow (purchases, milestones, level-ups), UI to
show current caps or capacity, backfill of EntitlementGrant for
existing rows (defaults are the truth for existing entities).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
EntitlementGranttable records history for future purchase/milestone/level-up grants (schema only for now, no writers).pickTown(user.maxTowns),POST /api/town(per-town buildings/npcs/customPlots + account-widemaxTotalCustomPlotsviajsonb_array_length),realtime-token(town.maxActiveGuestsvia Centrifugopresence_stats).postJsonprefers serverdetailsotown newsurfaces the human-readable limit message;town deploy's existing issue renderer picks up the new{ path, message }shape without change.What's not in this PR
EntitlementGrantfor existing rows — defaults are the truth for pre-existing entities.Test plan
pnpm db:migrateon a fresh DB — verify the three ALTER TABLEs and the newEntitlementGranttable land.NOT NULL DEFAULTclauses.town newpastmaxTowns→ 403 withYou've reached your limit of 3 towns….town deploywith too many NPCs/buildings/customPlots → 403 with per-issuepath/messagelines rendered by the CLI.town deploywhere combined custom-plot count across all owned towns exceedsmaxTotalCustomPlots→ 403 with the aggregate message.maxActiveGuestsset low, a fresh visitor at capacity gets 429town-at-capacity; owner is exempt.CENTRIFUGO_API_BASE), the realtime token still mints (fails open).🤖 Generated with Claude Code