From 285a19fd9475f7b0fcbbf81cc0bbbebc8202f41d Mon Sep 17 00:00:00 2001 From: enlorik <99548776+enlorik@users.noreply.github.com> Date: Fri, 24 Jul 2026 19:57:46 +0200 Subject: [PATCH 1/3] docs: add design notes to README --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index d160584..ee52cf5 100644 --- a/README.md +++ b/README.md @@ -112,3 +112,13 @@ The app is configured for [Railway](https://railway.app) via `railway.json`: - **Build**: `npm run build` (Nixpacks builder) - **Start**: `node server.js` - **Restart policy**: on failure, up to 10 retries + +## Design notes + +**Why a proxy server for a frontend app** — Codeforces doesn't send CORS headers, so the browser can't call the API directly. The Express proxy handles that, and also scrapes problem statement HTML, which is impossible cross-origin from the browser. A side benefit: one chokepoint for rate limiting, so a buggy client can't hammer Codeforces and get the shared hosting IP temporarily banned. + +**Why the daily pick is deterministic** — The same UTC date produces the same problem for every user. Refreshing doesn't reroll (no slot-machine effect), the pick is trivially cacheable, and two people can discuss "today's graph problem" the way people discuss Wordle answers. It also makes the selection logic easy to test: fix the date and inputs, snapshot the expected output. + +**Why the rating band is asymmetric [−100, +300]** — Practice should stretch, not repeat. A small floor below your current rating keeps a warm-up option available; the larger ceiling pushes toward difficulty. The asymmetry reflects the deliberate-practice idea that slightly-too-hard is more valuable for growth than slightly-too-easy. + +**Why `Promise.allSettled` instead of `Promise.all`** — The daily page needs three things: the problemset, your user info, and your solved submissions. If one fetch fails (handle not set, Codeforces API hiccup), `allSettled` delivers whatever succeeded and lets the page render with sensible defaults. `Promise.all` would reject everything on a single partial failure and leave the page blank. From ec9afe9a895ace5015651d7bc2387203b461d350 Mon Sep 17 00:00:00 2001 From: enlorik <99548776+enlorik@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:14:52 +0200 Subject: [PATCH 2/3] docs: clarify daily pick is deterministic per-user, not globally identical per Codex review --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ee52cf5..a42dd1a 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ The app is configured for [Railway](https://railway.app) via `railway.json`: **Why a proxy server for a frontend app** — Codeforces doesn't send CORS headers, so the browser can't call the API directly. The Express proxy handles that, and also scrapes problem statement HTML, which is impossible cross-origin from the browser. A side benefit: one chokepoint for rate limiting, so a buggy client can't hammer Codeforces and get the shared hosting IP temporarily banned. -**Why the daily pick is deterministic** — The same UTC date produces the same problem for every user. Refreshing doesn't reroll (no slot-machine effect), the pick is trivially cacheable, and two people can discuss "today's graph problem" the way people discuss Wordle answers. It also makes the selection logic easy to test: fix the date and inputs, snapshot the expected output. +**Why the daily pick is deterministic** — For the same date, rating, and solved-problem history, the pick is always identical. Refreshing doesn't reroll (no slot-machine effect), and the result is easy to test: fix the date and inputs, snapshot the expected output. Two users with the same rating and solved set get the same problem; users with different ratings or solved sets get different candidates and may get different picks — the pick is stable per user across the day, not globally uniform. **Why the rating band is asymmetric [−100, +300]** — Practice should stretch, not repeat. A small floor below your current rating keeps a warm-up option available; the larger ceiling pushes toward difficulty. The asymmetry reflects the deliberate-practice idea that slightly-too-hard is more valuable for growth than slightly-too-easy. From 81827fbcdc503c28da5ddfef7b368d2432cae32f Mon Sep 17 00:00:00 2001 From: enlorik <99548776+enlorik@users.noreply.github.com> Date: Fri, 24 Jul 2026 20:31:33 +0200 Subject: [PATCH 3/3] docs: qualify proxy rate-limiting scope; Timeline/Bounty bypass it per Codex review --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a42dd1a..83b241b 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ The app is configured for [Railway](https://railway.app) via `railway.json`: ## Design notes -**Why a proxy server for a frontend app** — Codeforces doesn't send CORS headers, so the browser can't call the API directly. The Express proxy handles that, and also scrapes problem statement HTML, which is impossible cross-origin from the browser. A side benefit: one chokepoint for rate limiting, so a buggy client can't hammer Codeforces and get the shared hosting IP temporarily banned. +**Why a proxy server for a frontend app** — Codeforces doesn't send CORS headers, so the browser can't call the API directly. The Express proxy handles that for proxied endpoints, and also scrapes problem statement HTML, which is impossible cross-origin from the browser. Note that the Timeline and Bounty pages fetch contest data directly from the browser (not via the proxy), so the rate limiter only covers the proxied daily-problems and statement endpoints — not all Codeforces traffic. **Why the daily pick is deterministic** — For the same date, rating, and solved-problem history, the pick is always identical. Refreshing doesn't reroll (no slot-machine effect), and the result is easy to test: fix the date and inputs, snapshot the expected output. Two users with the same rating and solved set get the same problem; users with different ratings or solved sets get different candidates and may get different picks — the pick is stable per user across the day, not globally uniform.