Files
leetcode/AGENTS.md
T

7.0 KiB

Repository Guidelines

Project Overview

LeetCode interview-prep campaign (8 weeks, 2026-08-17 → Oct 11) run as a repo: solution files under work/, a Blume docs site under docs/, and GitHub Actions automation that reconciles issues, publishes docs, and drives a spaced-repetition system (SRS). Issues model the curriculum: ~24 topic issues own ~161 problem sub-issues (set:core|optional|deferred, diff:*); milestones are phases; a user-level GitHub Project ("Interview Prep", #2) mirrors review state.

Architecture & Data Flow

Two subsystems, one direction of truth:

flowchart LR
  work[work/ solutions] -->|bun run sync| docs[docs/solutions/*.mdx]
  work -->|close-solved.yml| PI[problem issues]
  PI -->|close-topics.yml| TI[topic issues]
  SRS[.github/srs/srs.json] -->|srs-scheduler| Today[📋 Today issue]
  Today -->|/done comments| Logger[srs-logger] --> SRS
  SRS -->|mirror, best-effort| Proj[GitHub Project fields]
  SRS -->|gate.yml| Gate[weekly gate issue + docs/gate-log.md]
  • Reconcile, don't react. close-solved.ts, close-topics.ts, and the SRS scripts recompute desired state from scratch each run: re-runs are no-ops, backfills need no special casing, closing is one-directional (nothing ever reopens).
  • .github/srs/srs.json is the single source of truth. Project fields (Target Date, SRS Stage, First Attempt) are a best-effort mirror via scripts/srs-project.ts: every GraphQL failure becomes a warning, never an error — an srs.json update must never be lost to a mirror failure. Topic rows' Target Date is never written (problem rows only).
  • Determinism = idempotency. Drill/gate sampling uses a seeded PRNG (rng() in srs.ts, FNV-1a → mulberry32, seed = date / ISO week), so running a workflow twice picks identical problems.
  • Chained workflows: GITHUB_TOKEN-driven issue closes fire no issues events, so close-topics.yml chains off workflow_run of Close Solved instead.

Key Directories

Path Purpose
work/<Difficulty>/<Category>/<num>.<slug>.{js,py} Solutions, e.g. work/Easy/Array/1.two-sum.py. Machine-parsed header comment (title / Difficulty: / URL / rule / statement) — preserve its exact shape
scripts/ Flat Bun TS: automation entries (shebang + top-level await) and libraries (no shebang, side-effect-free on import)
docs/ Blume site. docs/solutions/(<category>)/<num>-<slug>.mdx generated by sync; (algorithms)/, (data-structures)/ hand-written explainers
.github/srs/ Machine state: srs.json (bot-committed), schedule.json (day → topic issue, human-edited), badge.json (shields endpoint)
.github/workflows/ deploy, close-solved, close-topics, srs-scheduler, srs-logger, gate, srs-setup

Development Commands

bun run pick            # scaffold a solution via leetcode-cli (fuzzy picker)
bun run test            # run ONE solution against LeetCode's judge (not a test suite)
bun run sync            # work/ → docs/solutions/ pages
bun run dev|build       # Blume docs site
bun run close-solved -- --dry-run    # issue reconcilers (also DRY_RUN=1)
bun run close-topics -- --dry-run
SRS_DRY=1 SRS_TODAY=2026-08-30 bun scripts/srs-scheduler.ts   # preview a brief
bun scripts/srs-logger.ts --issue N --author prdlk --body "/done 704 pass"
bun scripts/srs-gate.ts create|close --issue N

Code Conventions & Common Patterns

  • Scripts are either entries or libraries. Entries (close-*.ts, srs-scheduler|logger|gate|setup.ts) use shebang + top-level await and run their pipeline linearly. Libraries (github.ts, report.ts, srs.ts, srs-project.ts) must stay side-effect-free on import — nothing touches network/credentials until a factory (github(), projectMirror()) is awaited.
  • Shared plumbing: github() gives repo, api() (throws METHOD path -> status body), list() (paginating async generator), closeIssue() (comment first, then close — a failed PATCH still leaves a trace). report.ts gives printTable/markdownTable/writeStepSummary.
  • Narrow API payloads with guards, not inline casts: if (!("number" in value) || typeof value.number !== "number") return; (see readProblemIssue in close-solved.ts). Named-const casts only with a reason.
  • Dry-run everything: reconcilers take --dry-run/DRY_RUN=1; SRS takes SRS_DRY=1 plus overrides SRS_TODAY, SRS_STATE, SRS_SCHEDULE, SRS_GATE_LOG, SRS_BADGE — point SRS_STATE at a scratch copy to exercise logic safely.
  • Dates: always ET calendar strings (YYYY-MM-DD), arithmetic anchored at noon UTC (atNoon in srs.ts) to dodge DST. Never new Date() math directly.
  • Style: section-divider comments (// ── name ───), file-top doc comments explaining the why and invariants, 2-space JSON with trailing newline, Map for dynamic keys / Record for static tables, no tiny one-expression wrapper functions.
  • Known intentional duplication: close-solved.ts re-implements header stripping instead of importing from sync.ts because sync.ts runs its pipeline on import. Don't "deduplicate" it.

Important Files

  • scripts/srs.ts — SRS domain: ladder (new → +2 → +5 → +10 → retired; fail resets to +2), fetchCatalog() (walks topic sub-issues, parses LC <n> · <name> · <diff> · <set> titles), campaign math (CAMPAIGN_START).
  • scripts/sync.ts — work→docs contract: only ## Solution onward is script-owned on existing pages; human prose is never touched; never hand-write solution pages or edit inside ## Solution.
  • .github/srs/srs.json, docs/gate-log.md, .github/srs/badge.jsonbot-committed (github-actions[bot], [skip ci]). Don't hand-edit; fix via scripts.
  • blume.config.ts — site base /leetcode; README.md campaign table doubles as topic-map input to sync.ts (readmeTopics(), TOPIC_ALIASES).
  • Workflows share concurrency group srs-state (never race on srs.json); PROJECT_PAT secret is required for user-Project GraphQL (default GITHUB_TOKEN cannot), used by srs-scheduler/logger/setup only.

Runtime/Tooling Preferences

  • Bun only: run scripts with bun scripts/<name>.ts, install with bun install --frozen-lockfile; bun.lock is the sole lockfile. Node 22 appears only in deploy.yml because Blume requires it.
  • No tsconfig, no ESLint, no Prettier — match surrounding style by hand. @types/bun is the only type dependency.
  • Automation workflows skip bun install (scripts use Bun builtins + fetch only) — keep new automation dependency-free.
  • Local GitHub auth falls back to gh auth token; scripts run fine outside Actions.

Testing & QA

  • No test framework, no *.test.* filesbun run test submits one solution to LeetCode's judge.
  • QA is dry-runs + scratch state: verify schedulers/reconcilers with --dry-run/SRS_DRY=1 and a copied SRS_STATE before live runs; idempotency check = run twice, cmp output.
  • Docs changes: bunx blume build --isolated must pass with no new warnings (then rm -rf .blume-verify); after sync, review only created pages (prose transforms are heuristic).