From f697a986b8cf00558a29fb0b94487b3ac8dd40da Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Tue, 25 Aug 2026 15:45:20 -0400 Subject: [PATCH] docs(readme): clarify close-solved solved posting and live chart updates --- AGENTS.md | 12 +++++++++--- README.md | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b045c37..9c125cd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,8 +12,9 @@ Three owners, one direction of truth: flowchart LR work[work/ solutions] -->|bun run sync| docs[apps/docs/content/*.mdx] work -->|close-solved.yml| PI[problem issues] + work -->|close-solved.yml POST /admin/solved| D1[(D1 = SRS state)] PI -->|close-topics.yml| TI[topic issues] - GH[GitHub issues = catalog] -->|nightly reconcile| D1[(D1 = SRS state)] + GH[GitHub issues = catalog] -->|nightly reconcile| D1 D1 -->|8 AM ET cron| Mail[digest email + one-tap links] Mail -->|GET /log| D1 PI -->|/done webhook| D1 @@ -22,7 +23,9 @@ flowchart LR D1 --> Charts[SVG charts + /api/stats] ``` -- **Reconcile, don't react.** `close-solved.ts`, `close-topics.ts`, and the Worker's catalog sync recompute desired state from scratch each run: re-runs are no-ops, backfills need no special casing, closing is one-directional. +- **Reconcile, don't react.** `close-solved.ts`, `close-topics.ts`, and the Worker's catalog sync recompute desired state from scratch each run: re-runs are no-ops, backfills need no special casing, closing is one-directional. `close-solved` posts its *whole* implemented set to `/admin/solved`, not just the issues it closed this run. +- **Solving has three doors, one write path.** A digest one-tap, a `/done` comment, and a solution landing in `work/` all end in `logAttempt()`. Closing an issue is *not* one of them: the catalog reconcile never reads issue state, so a `work/` push that skipped the email would otherwise leave `stage='new'` — invisible to the charts, the digest's solved ticks, and the drill/gate pools. Hence `/admin/solved` (`source='commit'`, first solve only, no index needed for idempotency: it refuses any problem past stage `new`). `close-topics` needs no such call — D1 stores no topic completion, only problem rows. +- **The README charts are live, never committed.** They are Worker endpoints reading D1 per request (`Cache-Control: max-age=300`, honored by GitHub Camo), so a solved push moves them within ~5 minutes with no commit and no workflow of their own. - **D1 owns SRS state; GitHub issues own the catalog; `apps/api/data/schedule.json` owns the calendar.** The catalog reconcile never invents rows and never overwrites SRS columns (`stage`, `next_review`). Project fields (`Target Date`, `SRS Stage`, `First Attempt`) are a best-effort mirror (`apps/api/src/mirror.ts`): failures are warnings, never lost D1 writes; topic rows' `Target Date` is never written. - **Determinism = idempotency.** Drill/gate sampling uses a seeded PRNG (`rng()` in `apps/api/src/srs.ts`, FNV-1a → mulberry32, seed = date / ISO week); the digest is keyed by ET date in `email_log`; one-tap links are unique on (problem, date, kind). - **DST-proof crons:** each event has two UTC crons; code fires only on the computed ET hour (`etHour`), unit-checked in `apps/api/src/srs.test.ts`. @@ -36,7 +39,7 @@ flowchart LR | `apps/cli/` | Flat Bun TS workspace: automation entries (shebang + top-level await) and libraries (no shebang, side-effect-free on import). Root `bun run` scripts delegate here | | `apps/docs/` | Blume site (`blume.config.ts`, `content/`, `islands/`, `public/`). `content/()/-.mdx` generated by sync | | `apps/api/` | Cloudflare Worker workspace: `src/` modules, `data/schedule.json` (day → topic issue, human-edited, bundled at deploy), `migrations/`, `scripts/import-srs.ts` | -| `.github/workflows/` | deploy (docs → Pages, Worker → Cloudflare, on every main push), close-solved, close-topics, sync-d1 (issue edits → `/admin/reconcile`) | +| `.github/workflows/` | deploy (docs → Pages, Worker → Cloudflare, on every main push), close-solved (issues + `/admin/solved`, needs `SRS_ADMIN_KEY`), close-topics, sync-d1 (issue edits → `/admin/reconcile`) | ## Development Commands @@ -49,6 +52,9 @@ bun run sync # work/ → apps/docs/content/ pages bun run dev|build # Blume docs site (runs in apps/docs/) bun run close-solved -- --dry-run # issue reconcilers (also DRY_RUN=1) bun run close-topics -- --dry-run +# close-solved also pushes the solved set to the Worker; point it at a local +# one and nothing production is touched: +SRS_API=http://localhost:8787 SRS_ADMIN_KEY=$LINK_KEY bun run close-solved -- --dry-run bun run api:dev # wrangler dev on :8787 (local D1); api:test = DST guard tests bun run api:deploy # deploy the Worker by hand (CI also deploys on main pushes) curl -X POST -H "Authorization: Bearer $LINK_KEY" \ diff --git a/README.md b/README.md index be11a61..26f20a9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

8-week interview-prep fight camp. Each phase is a milestone; each topic an issue; each problem a sub-issue.
- Charts are live from the SRS Worker — the daily digest email drives the schedule.
+ Charts are live from the SRS Worker, never committed: pushing a solution or tapping the daily digest moves them within ~5 min.