diff --git a/.claude/skills/campaign-issues/SKILL.md b/.claude/skills/campaign-issues/SKILL.md index ea3f222..f4b6a4b 100644 --- a/.claude/skills/campaign-issues/SKILL.md +++ b/.claude/skills/campaign-issues/SKILL.md @@ -114,7 +114,12 @@ One issue per curriculum topic; owns the problem sub-issues. ``` `Scheduled` should come from the calendar of record — `apps/api/data/schedule.json` -(day → topic issue) — not guessed. +(`date → { topics: [issue, …] }`; a doubled day lists two) — not guessed. + +Gate problems (`gate: [...]` in the same file) must exist in the catalog before their +Saturday, or the Worker refuses to create the review issue: create each as a +`set:optional` problem sub-issue of its natural topic. Being named in the plan keeps it +out of the drill pool, so it stays unseen until the gate. ## Reference tables diff --git a/AGENTS.md b/AGENTS.md index d4f08f0..c182d39 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,6 +33,7 @@ flowchart LR - **The picker owns the local review loop; the buckets are its state.** `work/` holds three buckets (`1` = first solve, `3`/`7` = blind re-solves, named after the ladder rungs) and `bun run pick` has three sections — Tab cycles them. Pools are reconciled from the filesystem plus one `git log` (`apps/cli/work.ts`): a first solve dates from the ET date of the commit that added its `work/1` file — the same push that closes the problem issue and starts the +3/+7 windows — and a window stops being owed the moment its bucket holds a file. Uncapped, unlike the review issue's `WINDOW_CAP`: that issue is one day's assignment, the picker is everything still owed. An empty scaffold under `work/1` owes nothing (it is unsolved), which is why `isImplemented()` lives in `apps/cli/source.ts` and is shared with `close-solved.ts`. leetcode-cli reads its output directory from `~/.leetcode/workspaces//config.json` and nowhere else — no flag, no env var — so `pick.ts` MERGES `workDir` into that file around the child (never replays saved bytes: the CLI owns the file too) and leaves it pointed at `work/1`, then treats the scaffold's existence as the acceptance test, since leetcode-cli exits 0 even when an expired session made it write nothing. The picker itself writes no state: pushing the re-solve is what moves the ladder, via `close-solved` → `/admin/solved` with that file's bucket. - **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`). `Target Date` is the *only* Project field the Worker mirrors (`apps/api/src/mirror.ts`): failures are warnings, never lost D1 writes; topic rows' `Target Date` is never written. SRS stage and first-attempt result live **only** in D1 — do not re-add them as Project fields. The Project's `Set`/`Difficulty` single-selects are a projection of the issue labels, reconciled by `bun run sync-project-fields` — a best-effort final step of `close-solved.yml`, never the Worker. +- **The plan names days; the samplers fill the gaps.** `apps/api/data/schedule.json` maps a date to a `ScheduledDay` (`apps/api/src/srs.ts`): `topics` (one or more topic issues — a doubled day lists two, and the digest renders each with its core list then the optional "if time" list), `problems` (a set day: fixed numbers, rendered like a topic), `gate` (Saturday's blind set, verbatim — `createReviewIssue` refuses to create the issue if any number is not a catalog row, so gate problems must exist as `set:optional` sub-issues of their topic first), or `sweep` (every problem whose last attempt failed, taps to re-log, no drills). Every number a `gate` or `problems` entry names is in `RESERVED`, and the drill/gate samplers never draw it — that is what keeps a planned gate problem unseen until its Saturday. Dates absent from the plan still get the ladder's reviews and drills; a Saturday without a `gate` entry falls back to sampling this week's optional pools. - **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`. - **Chained workflows:** `GITHUB_TOKEN`-driven issue closes fire no `issues` events, so `close-topics.yml` chains off `workflow_run` of Close Solved instead. The Worker's webhook uses its own `GH_PAT`, so its events flow normally. @@ -44,7 +45,7 @@ flowchart LR | `work/<1\|3\|7>///..{js,py}` | Solutions, e.g. `work/1/Easy/Array/1.two-sum.py`. The top directory is the spaced-repetition bucket: `1` = first solve, `3`/`7` = the blind re-solves of that problem 3 and 7 days later. Machine-parsed header comment (title / `Difficulty:` / URL / `─` rule / statement) — preserve its exact shape | | `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/` (append-only; `0002` is the +3/+7 ladder rebuild) | +| `apps/api/` | Cloudflare Worker workspace: `src/` modules, `data/schedule.json` (the plan: `date → { topics | problems | gate | sweep }`, human-edited, bundled at deploy), `migrations/` (append-only; `0002` is the +3/+7 ladder rebuild) | | `.github/workflows/` | deploy (docs → Pages, Worker → Cloudflare, on every main push), close-solved (issues + `/admin/solved` + Project `Set`/`Difficulty`, needs `SRS_ADMIN_KEY` and `PROJECT_PAT`), close-topics, sync-d1 (issue edits → `/admin/reconcile`), spaced-repetition (daily 06:00 ET cron → today's review issue; `GITHUB_TOKEN` only, no secrets) | ## Development Commands diff --git a/README.md b/README.md index 602a3f0..5d76821 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,7 @@ B ⭐295Find Median from Data Stream B ⭐502IPO - 15. Trie
Wk 4 · Wed Sep 9
builds on 11 + 15. Trie
Wk 6 · Mon Sep 21
builds on 11 A208Implement Trie A211Add & Search Words @@ -234,7 +234,7 @@ - 16. Graph DFS
Wk 4 · Thu Sep 10
builds on 11 + visited set + 16. Graph DFS
Wk 6 · Mon Sep 21
builds on 11 + visited set A200Number of Islands A695Max Area of Island @@ -244,7 +244,7 @@ B399Evaluate Division B261Graph Valid Tree - 17. Graph BFS
Wk 4 · Fri Sep 11
builds on 12 + visited set — shortest path unweighted + 17. Graph BFS
Wk 6 · Tue Sep 22
builds on 12 + visited set — shortest path unweighted A994Rotting Oranges A127Word Ladder @@ -252,14 +252,14 @@ B433Minimum Genetic Mutation B286Walls and Gates - 18. Topological Sort
Wk 5 · Mon Sep 14
builds on 16 + 18. Topological Sort
Wk 6 · Tue Sep 22
builds on 16 A207Course Schedule A210Course Schedule II B ⭐269Alien Dictionary B1462Course Schedule IV - 19. Union-Find
Wk 5 · Tue Sep 15
builds on 16's problem class + 19. Union-Find
Wk 6 · Wed Sep 23
builds on 16's problem class A684Redundant Connection A323Number of Connected Components @@ -282,7 +282,7 @@ - 20. Backtracking
Wk 5 · Wed Sep 16
builds on 16 — then unlock LC 212 from topic 15 + 20. Backtracking
Wk 6 · Thu Sep 24
builds on 16 — then unlock LC 212 from topic 15 A78Subsets A39Combination Sum @@ -293,7 +293,7 @@ B22Generate Parentheses B ⭐52N-Queens II - 21. 1-D Dynamic Programming
Wk 5 · Thu Sep 17
builds on 20 + 21. 1-D Dynamic Programming
Wk 6 · Fri Sep 25
builds on 20 A70Climbing Stairs A746Min Cost Climbing Stairs @@ -305,7 +305,7 @@ B91Decode Ways B647Palindromic Substrings - 22. Multi-D / Grid DP
Wk 5 · Fri Sep 18
builds on 21 + 22. Multi-D / Grid DP
Wk 7 · Mon Sep 28
builds on 21 A62Unique Paths A64Minimum Path Sum @@ -316,7 +316,7 @@ B221Maximal Square B ⭐97Interleaving String - 23. Greedy
Wk 6 · Mon Sep 21
builds on 21 — includes Kadane's + 23. Greedy
Wk 7 · Tue Sep 29
builds on 21 — includes Kadane's A53Maximum Subarray A55Jump Game @@ -326,7 +326,7 @@ B918Maximum Sum Circular Subarray B763Partition Labels - 24. Intervals
Wk 6 · Tue Sep 22
builds on 23 + 24. Intervals
Wk 7 · Wed Sep 30
builds on 23 A57Insert Interval A56Merge Intervals