perRollMultiplier (default 0.05) was being applied as a direct multiplier
on the base roll cost, making everything cost 1-3 scraps instead of the
correct amount. It should only be used as an escalation factor per
previous roll: displayRollCost = baseRollCost * (1 + perRollMultiplier * rollCount)
- Restore handler now decrements shop_items.count for purchase/luck_win
orders (reverses the increment from delete), with GREATEST(..., 0) guard
- Delete handler now selects notes, isFulfilled, updatedAt so they're
included in the archived deleted_payload JSONB
- Restore INSERT now includes notes, is_fulfilled, updated_at columns
- Archive order + related rows (refinery, rolls, penalties) into single
deleted_payload JSONB column matching migration 0014 schema
- Add runtime CREATE TABLE fallback for missing admin_deleted_orders
- Full restore handler restores order with original ID (OVERRIDING SYSTEM
VALUE) plus all related rows; remove duplicate simple restore handler
- Delete transaction refunds stock count for purchase/luck_win orders
- Add detailed error logging for diagnostics
- Add batch compute-roll-costs endpoint and per-roll multiplier support
- change DOLLARS_PER_HOUR from 5 to 4 across backend and frontend
- merge soft-delete/restore/permanent-delete into single DELETE endpoint
- refund scraps, restore inventory, reverse penalties, and delete order row in one step
- add console.log for admin order reverts with full context
- simplify admin orders UI to single revert button with confirmation modal
- New nullable roll_cost_override column on shop_items (migration 0011)
- calculateRollCost uses override if set, otherwise auto-calculates
- Admin UI: new 'roll cost override' input with auto placeholder + reset button
- EV simulation and item cards respect the override
- Frontend shop page + ShopItemModal use override for display and affordability
- Backend try-luck endpoint uses override from locked row
- calculateShopItemPricing now simulates all upgrade levels and bumps
baseUpgradeCost until expectedTotalCost >= price at every level
- Synced to shop-pricing.ts (computeItemPricing) and frontend calculatePricing
- All price points from $5-$800 verified safe (house edge 0.2-18%)
- Show detailed required/available scraps in insufficient funds errors
- ShopItemModal: check data.error/data.success instead of response.ok
(backend returns 200 for all responses including errors, so response.ok
was always true and errors were silently swallowed, closing the modal)
- Shop page + ShopItemModal: use baseProbability for roll cost display
(matching backend calculateRollCost which uses fixed base probability)
- Auth: anchor session cookie regex to prevent matching wrong cookies
- Auth: keep dev-mode reduced OAuth scopes
The regex /session=([^;]+)/ was matching _theseus_session (from
HackClub auth) instead of the actual session cookie, since it matched
the first occurrence of 'session=' anywhere in the cookie string.
Changed to /(?:^|;\s*)session=([^;]+)/ which only matches 'session'
at the start of the string or after a semicolon separator.
Also returns a 200 HTML page with meta-refresh instead of a 302, to
ensure Set-Cookie is reliably stored by the browser before redirecting
to the frontend.
In dev, only requests: openid, profile, email, name, slack_id,
verification_status. Skips birthdate, address, and basic_info
which require additional verification.
- Changed refinery spent query from refinery_orders to refinery_spending_history
(refinery_orders rows are deleted on item win, inflating displayed balance)
- Removed extra status != 'permanently_rejected' filter from earned subqueries
to match getUserScrapsBalance which only checks scraps_paid_at IS NOT NULL
- Removed unused shopOrdersTable import