Skip to content

Phases 4–5.1: Ads live, Discord community, ParseForge bot (LootList+ parity) + cleanup - #17

Open
alexandermayes wants to merge 288 commits into
mainfrom
growth/phase-2-review-fixes
Open

alexandermayes wants to merge 288 commits into
mainfrom
growth/phase-2-review-fixes

Conversation

@alexandermayes

@alexandermayes alexandermayes commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner

Summary

This branch carries Phase 4 (Ads Live) plus the still-unmerged Phase 2.1 and Phase 3 work that
has accumulated on growth/phase-2-review-fixes since PR #16. Phase 4's own commits (04-01
through 04-07 Task 1) are the focus of this PR:

  • 04-01 — Captured the RPGLogs API Terms of Service and API docs with provenance; wrote the
    pre-ad Core Web Vitals baseline, D-10 rollback trigger, D-08 rollback runbook, and the D-11/D-14
    observation and escalation clocks into docs/OPS-01-SHIP-GATE.md Part 6.
  • 04-02 — Recorded real WCL rankings/rate-limit/guild fixtures (R0-2 tracer).
  • 04-03 — Shipped the phase tracer: one reserved, consent-gated AdSense slot end-to-end,
    report-only CSP entries for Google ad hosts, /ads.txt, and the ad-slot whitelist gate in
    scripts/protected-elements.mjs.
  • 04-04 — Rankings blob types, a pure parse-lens engine, and a Redis-backed WCL rate-budget
    gate — tested, unwired, imported by nothing yet (R0-3, by design).
  • 04-05 — Expanded to the full four-slot D-02 ad placement map across /tbc-audit and
    /analyze/[reportCode], rewrote /privacy's advertising and Warcraft Logs data-source
    disclosure, and wired the AdSense account (unit ids, env vars, Auto ads confirmed off).
  • 04-06 — Four-round preview-deployment defect-fix loop: found and fixed a reserved-box sizing
    bug and an occluding unfilled-ad-frame visual mismatch, both confirmed by the developer's
    first-hand review of the fourth preview.
  • 04-07 (Task 1 of 3, this dispatch) — Recorded the RPGLogs correspondence state honestly (the
    approval request drafted in 04-01 was never sent, so there is no reply) and the developer's
    explicit, twice-given, dated operator-override decision to proceed with the production ad
    deploy without that approval on record, after being told the real consequence (RPGLogs could
    revoke the WCL API client entirely). See docs/OPS-01-SHIP-GATE.md Part 6
    ### Deploy decision (Phase 4, 2026-09-22) for the full record.

Earlier commits on this branch (Phase 2.1's consent-gate hotfix, Phase 3's share loop) are
already deployed to production in prior sessions but have not yet been merged to main via a
PR — this PR brings main up to date with everything currently live plus Phase 4's work so far.

Test plan

  • npx tsc --noEmit clean
  • npm test — full vitest suite passing (285/285 as of 04-06)
  • npm run lint — no new findings (pre-existing debt in components/ui/meteors.tsx and
    lib/analysis-engine.ts left as-is)
  • npm run protected-elements — all rows passing
  • Production deploy + post-deploy route-contract evidence (04-07 Task 2, in progress)
  • Post-deploy OPS-01 live-traffic evidence (04-07 Task 3, not yet dispatched — needs 60+ min
    of live traffic to measure)

🤖 Generated with Claude Code

Phase 5.1 — ParseForge Discord bot (LootList+ parity) + cleanup

Phase 5.1 is executed 10/10 (plan 05.1-11 gap closure added, not yet run). Highlights on this branch since the Phase 5 commits:

  • 05.1-10 (D-08): removed app/api/discord/interactions/* and lib/discord-interactions*; prod deploy 4 (dpl_GacDSHqUmFCPs1DrA2Q6qrMt8vfn) verified — POST /api/discord/interactions → 404, sitemap/home/guide/demo report → 200; DISCORD_BOT_TOKEN and DISCORD_PUBLIC_KEY removed from Vercel Production. Sitemap pipeline files unchanged since 297cb98.
  • Quick 261013-vma (CR-01): bot /analyze and /raid now score the last kill (highest fight id with kill) for #fight=last or bare links, falling back to the last pull only when the report has no kills, and say which fight was picked. New bot/src/commands/pick-fight.ts; parsers and site untouched. Needs a Railway redeploy to go live.
  • Nyquist: +19 root / +16 bot tests (sanitiser, closure prefixes, OPS-01 log lines, bot lane on the three routes, parser parity, retired route). 05.1-VALIDATION.md validated.
  • Reports: 05.1-VERIFICATION.md (human_needed, 6/10 verified, 0 failed), 05.1-REVIEW.md (1 critical → fixed, 19 warnings → planned in 05.1-11), 05.1-SECURITY.md (blocked on T-05.1-13 credential rotation and T-05.1-41 /help guardrails — the latter planned in 05.1-11).

Gates at HEAD: root tsc clean, vitest 490/490; bot tsc clean, vitest 166/166; lint unchanged (1 pre-existing warning).

Still human before 05.1 can close: rotate the GitHub PAT + Upstash REST token (never rotated after the 05.1-03 exposure); confirm #ops is not visible to @everyone; #welcome picker tap-twice; #log-review /analyze / /raid / /help run; 05.1-08's three checks; #announcements purge (Phase 05 UAT test 20, deadline passed).

alexandermayes and others added 30 commits September 15, 2026 15:44
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Seven plans for Phase 3 (Share Loop), tracer-first across five waves:
awards engine + OG branch tracer, pool expansion, player permalink
receipts, analyze-page share surfaces, the protected-elements gate,
preview review, and the approval-gated production close-out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…teMetadata

- Add lib/awards-engine.ts: pure computeAwards() over RaidOverviewResult with
  a 5-rule seed pool (first-to-die, top-dps, top-hps, flaskless, best-prepared)
  and lib/awards-engine.test.ts (13 tests)
- Add AwardWinner/AwardTone/AwardRow/AwardsResult types to lib/wcl-types.ts
- Extend app/og/route.tsx with a view=awards branch (AwardsCard) that POSTs
  /api/raid-overview + GETs /api/report, falling back to the existing
  ReportCard on any failure or fewer than MIN_AWARDS_FOR_CARD fired awards
- Forward view into the OG URL from generateMetadata, canonical stays param-free

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- buildReportShareUrl/buildPlayerShareUrl/buildAwardsShareUrl build normalized,
  tab-free permalinks with the correct ref value (D-09/D-16)
- buildAwardsOgPath builds the relative /og preview path with no ref
- parseShareRef allowlists ref to exactly share|parse|awards (RESEARCH Pitfall 5)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Append graveyard-shift, gcd-tourist, fire-dancer, naked-slots,
  skipped-breakfast, dull-blade, iron-man, watering-the-garden,
  kept-them-breathing and punching-up to AWARD_POOL at priorities 6-15,
  each reading only RaidOverviewResult fields (D-03)
- Add a median() helper shared by fire-dancer's and punching-up's
  threshold checks
- Extend lib/awards-engine.test.ts with a fires/does-not-fire case per
  new rule plus a fifteen-rule fixture proving every rule fires and the
  card caps at the six lowest priorities
- Adjust the pre-existing ordering/cap fixture so the new iron-man and
  punching-up rules cannot bleed into a test scoped to the original
  5-rule tracer pool
- Add an AWARD_POOL pool-wide invariants describe block: unique ids,
  contiguous priorities 1-15, non-empty title/icon on every rule, tone
  restricted to praise|jab
- No-unconditional-firing test loops AWARD_POOL against a minimal
  all-zero fixture with an explicit allowlist (top-dps, best-prepared)
  so a future rule that fires on an empty fight fails the suite
- Every fired row across a representative set of fixtures in the suite
  carries a non-empty stat and 1..MAX_WINNER_NAMES winners, capped at
  MAX_AWARDS_SHOWN
- Stable-sort and non-mutation (structuredClone snapshot) assertions on
  computeAwards
Records the shipped fifteen-row award pool verbatim for the developer's
D-04 tone review at the 03-06 preview gate.
…eipts to the player OG card

- Player branch now Promise.all's /api/analyze + /api/report to resolve the fight outcome, degrading to a null outcome on any meta-fetch failure
- PlayerCard renders a Kill/Wipe pill, fight length (mm:ss), a vs-top-N comparison label, and a healer (effective HPS + overheal) or DPS (Active Time) proof line, reading only data.healer and the metricPercentiles activeTime entry — no arithmetic of its own

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…scorecard

- ComparisonSummary now takes reportCode/fightId/sourceId and builds one normalized playerShareUrl via buildPlayerShareUrl — no share surface in this component reads window.location.href anymore
- Share my parse (default variant, data-protected="share-player") copies the ref=parse permalink and fires share_action{kind:player_link}; only renders when fightId and sourceId are known
- Copy for Discord (outline variant, data-protected="share-discord") stays secondary, keeps discord_copied, and now also dual-emits share_action{kind:discord_text}
- AnalysisView forwards reportCode/fightId/sourceId down to ComparisonSummary, no new fetch

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Widen RaidOverviewProps with reportCode, fight and openAwards
- Add AwardsPanel (Copy awards link button, award rows, inline /og image)
  computed via computeAwards over the in-memory RaidOverviewResult
- Panel renders above Raid Buff Coverage so its button sits above the fold
  on mobile (D-13); scrolls into view once when openAwards is true

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- handleShareLink now builds from buildReportShareUrl(origin, ...) instead
  of reading the live address bar; dual-emits share_action{kind:report_link}
  alongside the legacy share_link_copied
- Header Share button carries data-protected="share-header"
- Removed the bottom glass "Found this useful?" share bar entirely
- RaidOverview call site now passes reportCode and the selected fight entry

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Initial tab defaults to Player when source is present and tab is absent
  (explicit tab still wins); awards-view visitors keep the Raid default and
  the panel scrolls itself into view (D-12)
- One mount effect, guarded by a ref so it runs exactly once, resolves ref
  via parseShareRef, captures share_landing with the allowlisted value only,
  then strips ref from the address bar via updateUrlParam (D-16)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…face

- docs/PROTECTED-ELEMENTS.md lists all 6 shipped data-protected attributes (share-header, share-player, share-discord, awards-panel, awards-preview, share-awards), the /og route and URL contracts, the enforcement mechanism, and the change procedure
- Every attribute value verified present in its owner file before being recorded
- Names Phase 4 (ad-placement whitelist) and Phase 7 (per-route SEO gate) as the consuming phases
…he checklist

- Plain ESM, no new dependency; mirrors seo-invariants.mjs/token-audit.mjs header shape and exit-code convention (0/1 gate, --report always 0 on check failures, 2 on fatal)
- Parses docs/PROTECTED-ELEMENTS.md's DOM-attributes table and confirms each data-protected value is present in its owner file
- Fetches the live awards/player/report /og cards and the analyze canonical against a resolved --base (default production)
- Missing/empty checklist, a nonexistent owner file, or an unreachable base are all fatal (exit 2) rather than a silent pass
- npm run protected-elements script entry added alongside seo-invariants and token-audit
- Verified: 10/10 checks pass against production; a synthetic bad row makes the gate exit 1; an unreachable base makes it exit 2; eslint clean
…S-01 + roadmap

- OPS-01 Part 1 item 4 gains a share-rate HogQL sub-block (D-14): distinct sessions with share_action over distinct sessions with analysis_complete, vs the ~2.8% baseline; notes share_link_copied/discord_copied keep dual-emitting this phase
- OPS-01 Part 1 gains item 8, Protected elements: npm run protected-elements is a gate item, a fatal exit is never a pass, and it's named as a hard input to Phase 4 and Phase 7; also records the two carried-forward open items (dpl_CDCu1FVfPZcd8dHr4RpLcrHWJcJ5 unsigned item-7 row, GSC crawled-not-indexed investigation) as open, not resolved
- Items 1-7 untouched — purely additive edit (0 lines removed)
- ROADMAP.md Phase 4 Depends-on/Notes and Phase 7 Notes now name docs/PROTECTED-ELEMENTS.md explicitly
- Records the protected-elements checklist, the machine-check script, and the OPS-01/ROADMAP wiring completed by this plan
alexandermayes and others added 28 commits October 1, 2026 14:45
…ntent

- scripts/build-bot-data.mjs: second generator extracts bot/src/generated/
  help-seed.ts from app/guides/*/page.tsx (5 guides), app/tbc-audit/page.tsx's
  FAQ (6 items) and docs/DISCORD-SEED.md's four named copy blocks — JSX/HTML
  stripped to plain prose (headings, list items, paragraph reflow), entities
  decoded, no className=/tags/braces left, 200-8000 chars, fails loudly on a
  thin extraction; --check now covers both generated files
- bot/src/help-kb.ts: seedHelpArticles upserts every seed as a published
  seed-{slug} article (sha256 content-hash dedupe), archives a seed no
  longer in the list, never touches pending drafts
- bot/src/index.ts: calls seedHelpArticles on ClientReady when the store is
  configured, in its own try/catch
- 7 new tests in bot/src/help-kb.test.ts (generated-seed shape + idempotent
  seed/update/archive); root lib/bot-data.test.ts still passes; bot
  tsc/build/lint and root tsc/test clean
- bot/src/help-kb.ts: queueDraftArticle (pending_review, sanitised origin
  question, no asker id), setReviewMessage/articleIdForReviewMessage,
  publishArticle (slugify + pending->published), archiveArticle
- bot/src/help.ts: a novel answer's draft is now queued after the member's
  own reply is sent — posts the review embed to OPS_CHANNEL_ID, reacts
  ✅ then 🗑️, records the review message, logs help_draft_queued; every
  step soft-fails
- bot/src/help-review.ts (new): handleHelpReviewReaction ports LootList+'s
  help-review.js onto the Redis KB — Developer-role-gated, OPS_CHANNEL_ID
  -scoped, only acts on a still-pending_review mapped article; publish/
  archive edits the embed footer to the Zev-signed line
- bot/src/index.ts: second messageReactionAdd listener, its own try/catch
- 8 new tests in bot/src/help-kb.test.ts (draft queue, review publish/
  archive, non-Developer/bot/wrong-emoji/wrong-channel refusals, listing);
  bot suite 130/130, root 518/518, no site file touched
… gate

- scripts/discord-bot.mjs: feedback-forum --open and archive-channel
  subcommands (dry-run by default, --execute to write), both refusing on
  the wrong channel type; no subcommand deletes a channel. preflight gains
  feedback_archive and feedback_forum's public: yes|no.
- docs/DISCORD-SEED.md: #feedback-archive topic copy.
- docs/DISCORD.md: #feedback-archive and #ops channel rows, ### Limits
  (D-16) with the chosen cooldown/cap numbers and override vars, full
  metric-name list through plan 05.1-07, and the help-KB/spend-guard Redis
  keys under ### Data the bot keeps.
- docs/OPS-01-SHIP-GATE.md Part 8: What-shipped rows for plans 05.1-04 to
  05.1-07, and #### Local gate — deploy 2 with this run's exit codes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Records Task 2's checkpoint:decision outcome in Part 8 deploy-2 — the
developer's verbatim "go live" choice at 2026-10-02T17:45:05Z (archive,
not delete, the old #feedback channel), the forum-permission side
finding (the private feedback forum was already open to @everyone
before this deploy, per the developer's own prior manual change), and
a doc-drift note on the Developer role's display name. Deployment id,
commit, and variable names are left for Task 3.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Deploy 2 (developer-run, deployment 5bd7e083-f993-46cb-b034-4a10958ebf78,
commit a1f48ad) confirmed bot_ready/help_seeded/backfill_run. Executed the
two authorized channel changes: feedback-forum --open (D-11, forum now
public) and archive-channel --id 1552413587094376620 --name
feedback-archive (read-only, never deleted). Recorded Part 8 deploy
record/evidence, the forum's opening date and role-name drift in
docs/DISCORD.md, and the guidelines-applied note in docs/DISCORD-SEED.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- scripts/discord-bot.mjs: set-interactions-endpoint accepts --clear (prints
  requested: none; --execute sends an empty string, falls back to null on a
  400, reports which form Discord accepted) alongside the existing --url form
- docs/OPS-01-SHIP-GATE.md Part 8: #### Cutover plan — deploy 3 records the
  D-05/D-08 relay steps a-e, who runs each, the one-command rollback, and the
  pre-cutover gate outputs (tsc/lint/518 root + 130 bot tests/commands
  list/preflight/sitemap, all clean) — verify prints cutover-prep-ok

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Developer chose "cut over" (claude-opus-5 for /help) at 2026-10-02T21:29:40Z
after the relay, rollback, cost trade-off, and site-diff context was
presented. Phase 5 UAT test 19 recorded as superseded by this plan's Task 3
step-c tap test rather than run independently.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
BOT_INTERNAL_API_KEY added to Vercel Production (sensitive, piped from a
mode-600 scratch file, confirmed by name only). The production deploy
command was denied by the harness's auto-mode classifier before it ran; no
new deployment exists. Stopped per the plan's own guidance rather than
working around the block — probes, scratch-file cleanup, clipboard copy,
and Task 3 step b are all withheld until the developer runs the deploy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…cleared

Production deploy (dpl_6FwgsubwLVrb96ZdfbTDSPSESavF) was run by the developer
after the harness blocked Claude's attempt; records the deployment id/url/time,
all four public-page probes (200), both bot-lane probes (valid key -> 400
"Missing or invalid caller id."; no key -> different 400, confirming the
caller-id gate), the pre-existing build-log observation, the clipboard/header-
file handling, and the kept-key-file deviation (header file deleted, key file
kept pending the Railway paste at step d).

Step b: `set-interactions-endpoint --clear --execute` cleared the Interactions
Endpoint URL (current: none, confirmed via dry run and preflight). Every
interaction now routes to the gateway bot; the HTTP route and its rollback
command are untouched. Step c (the real tap test) is next, awaiting the
developer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Task 3 steps c-e of the D-05 relay.

- Step c: the developer's real tap test passed on the gateway path
  ("Tapped, toggled fine", "Good to move on").
- Re-key: step a's key never reached Railway (clipboard and kept key file
  both gone), so a fresh key was generated; the developer swapped it into
  Vercel Production and redeployed (dpl_DoWDyuJTU1ntLGJVBqamQ8F992XH,
  Claude's own secret-store write was blocked). All probes re-passed.
- Step d: three Railway variable redeploys (a colon-suffixed variable
  name, then a missing COMMANDS_ENABLED) ending at a1a7e859 with
  commands_registered count=3; `commands list` shows guild: 3. Key parity
  between Railway and Vercel verified (`keys match`); key file deleted and
  clipboard cleared.
- docs/DISCORD.md `## Role picker` rewritten for the gateway bot, the
  rollback route kept until 05.1-10, and the registered commands.
- Task gate printed cutover-ok (run by the developer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SUMMARY for plan 05.1-09 (choice, model, both Vercel deployments, the
three Railway redeploys, probes, endpoint before/after, tap outcome,
commands list, key parity, deviations); ROADMAP 9/10; STATE advanced.
Human-check (/analyze, /raid, /help in #log-review) recorded PENDING.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rate-limit bucket

- Delete app/api/discord/interactions/route.ts(+test) and lib/discord-interactions.ts(+test); the gateway bot owns the picker since 05.1-09 (D-08)
- Drop the interaction-only RATE_LIMITS["discord-roles"] bucket; isRateLimitedKey and checkCallerRateLimit stay
- Reword scripts/discord-bot.mjs comments to point at bot/src/roles.ts and note the endpoint URL is cleared
- lib/discord-roles.json untouched (bot's generated copy and the ops script read it)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Part 8 deploy 4 record: choice "retire route", the developer's words
  and the UTC time (2026-10-13T00:09:57Z)
- Notes that the decision ran nothing; Task 3 still gates the deploy and
  the env removals on the developer's words

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Production deploy denied by the auto-mode classifier before it ran
- vercel ls confirms no new deployment; both Discord secrets untouched

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Deploy dpl_GacDSHqUmFCPs1DrA2Q6qrMt8vfn live and aliased to parseforge.gg
- Interactions route 404; sitemap, home, guide and demo report 200
- env ls names for production and preview; no env rm has run

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…se 5.1 sign-off

- Part 8 deploy 4: step b words and commands, env ls before and after,
  preflight (endpoint none) and commands list (guild 3)
- Sign-off: criteria 1-5, measurement per metric with no-data re-check,
  PostHog item 7 not applicable, SEO checks, open items incl. #ops flag
- DISCORD.md Role picker: route and Vercel Discord secrets removed

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Status human_needed — 6/10 truths verified, 3 behavior-unverified in
production, 1 uncertain (OPS-01 measurement is log-only). Flags #ops
visibility (D-04), the never-closed PAT/Upstash rotation, and the
pending human checks from 05.1-08/09/10.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
29 findings (1 critical, 19 warning, 9 info) across the 50-file phase
diff. CR-01: /analyze and /raid silently fall back to the first pull
(fights[0]) for fight=last or bare links, so wipes can be scored
against kill parses.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…, when the link names no fight

- add pickDefaultFight/describeFightPick (bot/src/commands/pick-fight.ts): last kill by highest fight id, last pull only when there are no kills
- /raid uses the helper for a bare link and #fight=last, and discloses the pick in one line on success and error replies
- log fight_pick (explicit | last_kill | last_pull) on command_run
- tests for the helper and the /raid bare, fight=last, no-kill, explicit and error paths (CR-01)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ull, when the link names no fight

- /analyze uses the shared pickDefaultFight/describeFightPick on both the player-analysis and raid-overview paths
- pick note precedes the existing no-source note and any API error, so the member always sees which fight was tried
- tests for fight=last, bare code, no-kill, player-not-in-fight, explicit-fight and no_fights paths
- document the fight_pick command_run log field in docs/DISCORD.md (CR-01)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…last kill for fight=last or bare links

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Six uncovered behaviors get direct tests: the public sanitiser (D-10),
closure thread prefixes (D-11), one anonymous log line per OPS-01
metric, the bot lane on the three site routes (D-16), bot/site URL
parser parity (D-02) and the retired interactions route (D-08).
Root 471→490, bot 150→166. VALIDATION.md: status validated,
nyquist_compliant true, wave_0_complete true; WR-01…19 left to 05.1-11.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
64 threats: 41 closed, 14 partial, 6 open, 3 accept-pending. Blocking:
T-05.1-13 (PAT + Upstash token exposed in 05.1-03, never rotated) and
T-05.1-41 (/help never-guess and gameplay-redirect rules live only in
the prompt — WR-05/06). New: lib/rate-limit.ts logs the Upstash error
message including rl:bot-member:discord:<id>.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rity threats

Seven tasks (6 code + Railway redeploy checkpoint) closing WR-01…WR-19,
T-05.1-41/-10/-26/-06/-18/-33/-37/-02 and R-01…R-04. ROADMAP wave 10,
05.1 now 10/11. Credential rotation and #ops visibility stay human.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@alexandermayes alexandermayes changed the title Phase 4: Ads Live — AdSense placement, consent-gated loader, privacy disclosure, RPGLogs deploy decision Phases 4–5.1: Ads live, Discord community, ParseForge bot (LootList+ parity) + cleanup Oct 16, 2026

This branch was successfully deployed

1 active deployment
Preview — 655055b3 Deployed Oct 14, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant