Agent skill

Batch Problem Creation

by wislertt in wislertt/leetcode-py

Batch creation workflow for multiple LeetCode problems - loops through problem creation, solution implementation, and quality assurance for a specified count.

Apache-2.0Auto-check passedTesting & QA

Install Batch Problem Creation

skills CLI
$ npx skills add wislertt/leetcode-py --skill batch-problem-creation -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install wislertt/leetcode-py batch-problem-creation --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/wislertt/leetcode-py.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/batch-problem-creation .claude/skills/batch-problem-creation && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
batch-problem-creation
GitHub stars
142
Token cost
~10k tokens
SKILL.md length
4,481 words
Files
2
Skills in repo
5
Repo updated
First seen
Licence
Apache-2.0

At a glance

Batch creation workflow for multiple LeetCode problems - loops through problem creation, solution implementation, and quality assurance for a specified count.

  • Works in 3 steps: Problem Creation Loop → Batch Finalization (MANDATORY — after… → Batch Summary
  • Explicitly requests batch creation via /batch-problem-creation command
  • SKILL.md covers Assistant Workflow, Step 1: Problem Creation Loop, Step 2: Batch Finalization… and Step 3: Batch Summary, plus 3 more sections
  • Calls uv, curl and git; reaches leetcode.ca and raw.githubusercontent.com

What it does

Batch Problem Creation is an agent skill from wislertt/leetcode-py. Batch creation workflow for multiple LeetCode problems - loops through problem creation, solution implementation, and quality assurance for a specified count. Accepts optional count argument (e.g., /batch-problem-creation 10) or the keyword "unscrapable" to drain the not-yet-done queue in unscrapable.py via web-sourced data (/batch-problem-creation unscrapable). Defaults to 5 if no argument. Use ONLY when user explicitly requests batch creation via /batch-problem-creation command.

Its SKILL.md is about 10k tokens, which your agent loads only when the skill is triggered. The skill folder holds 1 other file (for example `RUN_HISTORY.md`).

It sits in Testing & QA, covering QA and bug reports. It works with Python. The repository describes itself as: Python LeetCode practice environment with automated problem generation, data structure visualizations, and comprehensive testing. Includes all Grind 75, partial Blind, Neetcode… The licence is Apache-2.0.

When your agent uses it

  • Explicitly requests batch creation via /batch-problem-creation command
  • Tasks that involve QA and bug reports

Example prompts

  • “unscrapable”
  • “/batch-problem-creation”

Requirements

  • Python 3

Workflow steps

3 steps, taken from the step headings in SKILL.md.

  1. Problem Creation Loop
  2. Batch Finalization (MANDATORY — after ALL problems)
  3. Batch Summary

What it can do on your machine

Read from SKILL.md and the folder at commit 9155db3. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • uv
    • curl
    • git
    • python3

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • leetcode.ca
    • raw.githubusercontent.com
    • api.github.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Batch Problem Creation loads about 10k tokens when it runs. Until then it costs about 127 tokens; SKILL.md has 4,481 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~127
When it runs · the whole SKILL.md, loaded when a task matches
~10k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from wislertt/leetcode-py at commit 9155db3, republished under its Apache-2.0 licence (© wislertt). 4,481 words, ~10,017 tokens.

Download SKILL.mdSave it as .claude/skills/batch-problem-creation/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.
name
batch-problem-creation
description
Batch creation workflow for multiple LeetCode problems - loops through problem creation, solution implementation, and quality assurance for a specified count. Accepts optional count argument (e.g., /batch-problem-creation 10) or the keyword "unscrapable" to drain the not-yet-done queue in unscrapable.py via web-sourced data (/batch-problem-creation unscrapable). Defaults to 5 if no argument. Use ONLY when user explicitly requests batch creation via /batch-problem-creation command.
argument-hint
[count]

Batch Problem Creation Command

Assistant Workflow

  1. Parse arguments: If $ARGUMENTS contains the keyword unscrapable, run the Creating Unscrapable Problems from the Web flow (bottom of this file) instead of the normal loop. Otherwise use $ARGUMENTS as count (valid integer); default 5 if absent
  2. Pre-assign problems (main context): find N problems in one pass, scrape each to a /tmp file WITHOUT reading its content (Step 1.1)
  3. Per-problem subagent loop: spawn one general-purpose subagent per problem; the agent does JSON creation, tag insert, p-gen, solution, QA. Main context only sees the agent's short report (Step 1.2-1.4)
  4. Finalize the whole batch (main context): pre-commit, tag sync check, consistency check (in order — see Batch Finalization)
  5. Summarize: batch results + skill improvement suggestions

Why subagents: a problem costs 30-60k context tokens when done inline (scrape dumps, JSON scripts, p-gen/test output). A subagent does the same work in its own context; the orchestrator pays only ~200-500 tokens per problem (spawn prompt + report). This keeps main context near-flat for any batch size.

Step 1: Problem Creation Loop

1.1: Pre-Assign All Problems (main context, ONE pass)

GOTCHA — never hand-copy the number into the scrape. Transcribing #214 as 257 scrapes the wrong problem silently and wastes a full cycle. Chain everything off script output.

next_problem.py --take N is a waterfall: it returns N distinct problems, one per line (NUMBER TAG NAME SOURCE), virtually consuming each pick so nothing repeats. Source priority: (1) unscrapable — the UNSCRAPABLE_QUEUE todo list in unscrapable.py, drained FIRST to keep the queue short; (2) list — registered problem lists via the best-list rule; (3) new — lowest LeetCode number absent from the database entirely (TAG is none, name unknown until scraped). Non-Python (SQL/shell) numbers are never picked. No-argument mode stays single-pick list-only.

Route each manifest line by SOURCE:

SOURCEHandling
listlcpy scrape → normal flow (template as written)
newlcpy scrape → normal flow, but _tags: { "list": [] } (NO tag — the problem belongs to no list) and gate 2.0 skips its tag insert
unscrapableNO lcpy scrape — premium/limited problems: fetch via the doocs/curl web flow (Unscrapable Problems Management, step 3), agent builds JSON from that data with the queue's tag (or _tags: { "list": [] } if tag is none). After creation, move the tuple from UNSCRAPABLE_QUEUE to UNSCRAPABLE_HANDLED in unscrapable.py
anything scraping as non-Python (SQL/shell statement)do NOT create — append (N, "kebab-name") to NON_PYTHON_PROBLEMS in unscrapable.py, pull a replacement (see the replacement-pull gotcha below; for unscrapable batches take the next unclaimed queue line, not --take)

Run the lookups + scrapes in ONE bash call. Scrape output goes to files — NEVER print it to main context (it is the single largest context cost; the agent reads the file instead). Print only number, tag, slug, title:

bash
uv run python .claude/.dev/next_problem.py --take {count} | while read -r line; do
  N=${line%% *}          # first token
  SOURCE=${line##* }     # LAST token — never `read -r N TAG NAME SOURCE`: zsh does NOT
  case "$SOURCE" in      # join the remainder into the last var, so a multi-word NAME
    unscrapable)         # ("(unknown until scraped)") silently mangles SOURCE and every
      echo "QUEUE $N none $NAME unscrapable_web_flow"   # no scrape; agent fetches via doocs/curl
      continue ;;
    new|list) ;;
    *) continue ;;       # malformed line — skip rather than scrape garbage
  esac
  uv run lcpy scrape -n "$N" > "/tmp/batch_scrape_${N}.json" 2>&1 < /dev/null
  rc=$?
  if [ $rc -ne 0 ] || head -c 1 "/tmp/batch_scrape_${N}.json" | grep -qv '{'; then
    echo "QUEUE $N none SCRAPE_FAILED $SOURCE"
  else
    META=$(python3 -c "import json; d=json.load(open('/tmp/batch_scrape_${N}.json')); print(d['slug'], '|', d['title'])")
    echo "QUEUE $N none $META $SOURCE"
  fi
done

GOTCHA — zsh read word-splitting. This machine's shell is zsh, and read -r N TAG NAME SOURCE does not join the line remainder into the last variable: NAME (unknown until scraped) leaves SOURCE="until scraped) new", so a [ "$SOURCE" = "new" ] check silently skips EVERY line and the loop no-ops with zero output (2026-09-02 batch 17: 3 no-op scrape loops before root cause). Always key off the FIRST token (${line%% *}) and LAST token (${line##* }) only. If loop-based parsing misbehaves again, fall back to a small python script that reads the raw lines and parses the scrape JSONs (batch 15 precedent — worked first try).

Notes:

  • A failed scrape (premium signature Error fetching problem: 'NoneType' object is not iterable, SQL Error: Problem number N not found) does NOT consume the number — handle per Error Handling below (premium → append to the unscrapable queue), then pull a replacement with another --take and scrape it
  • GOTCHA — replacement pulls must exclude every number already in the batch manifest, regardless of source. Pick consumption is per-process (state is rebuilt from files each invocation), so a replacement --take called mid-batch re-issues already-picked picks — and any failures you just queued (hit 2026-09-01 batch 9: --take 2 returned already-picked 1133-1152; batch 13, a pure list batch: --take 2 returned the just-queued 1966/2021 and --take 4 returned already-picked 1964/1968). One-pass recipe: --take WIDE in a single process (wide enough to cover queue entries + manifest), filter to the wanted SOURCE, drop manifest numbers, scrape the first survivors. For unscrapable batches an alternative is taking the next unclaimed UNSCRAPABLE_QUEUE lines directly
  • GOTCHA — the unscrapable source can hand you SQL problems. The non-Python screen (num not in non_python in next_problem.py) reads NON_PYTHON_PROBLEMS, so it only catches entries ALREADY filed there — a still-queued SQL tuple (1141/1142 in batch 9) flows straight into the manifest. Verify each queue entry's language BEFORE the manifest (doocs folder title / README grep for SQL); if SQL, move the tuple to NON_PYTHON_PROBLEMS and take the next unclaimed queue line. Known SQL already filed: none left queued — new SQL discoveries still surface here
  • The QUEUE ... lines are the batch manifest. Keep them; each line becomes one subagent spawn. Subagent prompts for unscrapable lines get the doocs-URL instructions instead of {SCRAPE_PATH} (see Unscrapable Problems Management step 3 for the URL pattern); prompts for new lines say tag is none
1.2: Spawn One Subagent Per Problem (sequential)

For each manifest line, spawn ONE agent with subagent_type: "general-purpose" (fresh empty context — NEVER fork, it inherits the orchestrator's whole conversation and defeats the purpose).

Parallel mode (default 8 concurrent unless the user specifies a number; experiment E4): agents do NOT write tags.json5 — the tag insert moved to finalization gate 2.0 (main-side, from the agent-reported dir names). That removes the only shared-file write, so agents own disjoint files and can run concurrently (p-gen writes only its own problem dir + scoped format/check on new dirs — verified in gen.py). bake p-gen tolerates a JSON absent from tags.json5 — verified 2026-09-01. Hold the flight level by reconciling to target, not one-for-one: on EVERY turn (each report, each spawn, each user message), count running agents (ListAgents) and spawn target - running replacements at once from the manifest. One-for-one refilling lags by 1-3 min per report and lets the flight sag to 3-4 (measured, E3 batch 13) — deficit spawning is what holds it at target. Brief overshoot is fine when several notifications land in one batch. Expect SHORT transient dips even with reconcile (notification latency + silently dropped notifications are real; 3/50 dropped in batch 13) — a sag of 1-2 for one turn is normal, a sag to half target that persists means dropped notifications: run the Step 1.3 fallback check. Ceiling note: ruff/ty/p-test are CPU-bound per agent, so past ~core-count concurrency each slows without wall-time gain — if agent durations inflate without FAILs or races, the cap is CPU, not the flow (E3 measured: durations did NOT inflate at 6 on the batch-13 machine).

GOTCHA — never derive a spawn number from memory. Every spawn prompt must be filled from a MANIFEST LINE read in that same turn (awk '{print $2}' /tmp/batch_manifest.txt to list unspawned numbers). Numbering refills from recall invents problems: 2026-09-02 batch 17 spawned 6 numbers (587-597) that were not in the manifest — 2 landed on SQL (dead spawns), 4 created unplanned problems, and a real manifest line (575) was skipped until the end-of-batch reconciliation caught it. Second consecutive batch with a manifest-discipline failure (batch 16 silently dropped 3 lines). Same rule as the scrape gotcha above, one level up: hand-transcription of numbers is where batches go wrong, whether into a scrape command or a spawn prompt. When refilling, take the LOWEST unspawned manifest number(s) not yet reported — and keep a spawned/set marker (e.g. touch /tmp/batch_spawned_{N}) if multiple reports land in one turn, so the same line is never double-spawned or skipped.

GOTCHA — the manifest has GAPS; never assume contiguous numbering. The new-source pool skips numbers already in the repo, so manifest numbers are non-contiguous (batch 19: ...858, 859, 864... — 853/860-863 absent). "The next number" or "previous + 1" is wrong by construction. Enforcement: touch a spawn marker ONLY for a number that the unspawned-scan (comm of manifest numbers vs spawned markers) listed in that same turn; run the scan FIRST, take its first line, touch, then spawn. Blind neighbor-touching cost batch 19 three stray off-manifest spawns (815/853/860 — all pre-existing problems; see also the pre-existing-problem line in the prompt template).

Subagent prompt template — fill {N}, {TAG}, {SCRAPE_PATH} (/tmp/batch_scrape_{N}.json) from the manifest line, verbatim otherwise:

text
Create LeetCode problem #{N} in this repo. Work autonomously; report back concisely.

FIRST: read these two files completely before any other action:
- .claude/skills/problem-creation/SKILL.md
- .claude/skills/test-quality-assurance/SKILL.md
Follow them exactly — every gotcha in them is there because it cost a real cycle.

Input data: the problem is already scraped to {SCRAPE_PATH}. Do NOT re-scrape.
Read that file (it has description, examples, constraints, topics, python_code signature, image URLs).
The JSON's _tags should be { "list": ["{TAG}"] } — but do NOT run insert_tag.py; the orchestrator
inserts all tags at finalization (other agents run concurrently and would race on tags.json5).

Do, in order:
1. Build the JSON template at src/leetcode_py/cli/resources/leetcode/json/problems/{problem_name}.json
   per problem-creation.md (images, _tags: { "list": ["{TAG}"] } — if {TAG} is `none` use
   "list": [] and skip all tagging, 12+ test cases, single-quote
   playground strings, None not null). Write it via a python script that machine-verifies every
   test-case expectation with a reference implementation and asserts BEFORE json.dump — never
   hand-transcribe expectations.
2. bake p-gen -p {problem_name}  (fix the JSON, never generated files, re-run until clean)
3. Implement ONE optimal solution in the single Solution class in solution.py (design problems:
   the custom class only), ruff/ty-clean up front per problem-creation.md Batch Flow Notes.
4. Run the QA chain from test-quality-assurance.md step 2 (backup -> p-gen -f -> restore
   solution -> p-test -> cleanup). Repo-wide `bake lint` is NOT yours — the orchestrator runs
   it at finalization; but scoped lint IS yours: `uv run ruff check leetcode/{problem_name}`,
   `uv run ruff format --check leetcode/{problem_name}`, `uv run ty check leetcode/{problem_name}`
   must all be clean before you report PASS. Tests MUST pass (solution is implemented); a
   failure means bad JSON expectations or a wrong solution — fix it, do not dismiss it.
   SPIN GUARD: if p-test exceeds ~1 min on your 12-20 tiny cases, kill it — that is an
   infinite loop in the solution/helper, not slowness (check `ps` for a ~100% CPU pytest);
   find the spinning case by running cases individually and fix the root cause. Never report
   PASS off a truncated or early-ended output file.
5. Write the actual directory name you created (one line) to /tmp/batch_name_{N}.txt, so the
   orchestrator can insert the tag and re-test by real dir names — do NOT assume the scrape
   slug (they can differ, e.g. shorter names chosen to satisfy E501).

IF THE PROBLEM ALREADY EXISTS in the repo (leetcode/{problem_name}/ or its JSON): do NOT
recreate or overwrite from scratch — verify the existing work against these requirements
instead (machine-verified expectations, QA chain, scoped lint), LEAVE `_tags` in the JSON
untouched (the orchestrator owns tags), and report status PASS with a note that the problem
pre-existed.

Boundaries — you own ONLY this problem:
- Touch only: your JSON file, leetcode/{problem_name}/, /tmp scratch files (including
  /tmp/batch_name_{N}.txt)
- Do NOT: edit bakefile.py, tags.json5, other problems' files, problem_lists/*.py,
  unscrapable.py; run `bake lint`, `bake check-consistency`, pre-commit, or any repo-wide
  command; git commit
- This is a subagent task: skip the brainstorming/superpowers skill ceremony; the two skill
  files above are your complete instructions.

Report back EXACTLY this and nothing else:
- problem_name: <name>
- status: PASS | FAIL
- tests: <count> passed
- if FAIL: one paragraph — what failed, the error, what you tried
- any deviation from the skills you had to make and why

MANDATORY before gate 2.0 — spawned-vs-done reconciliation. Never start finalization while any manifest line is unaccounted for. Every spawn must leave a marker (touch /tmp/batch_spawned_{N}), and the batch is fully reported only when this is empty:

bash
comm -23 <(sort -n <(ls /tmp/batch_spawned_* | sed 's/.*batch_spawned_//')) \
         <(sort -n <(ls /tmp/batch_name_*.txt | sed 's/.*batch_name_//;s/\.txt//'))

Non-empty output means a report never arrived (notification drops hit batches 13, 15, 16, and 18 — 2 of 50-111 lines each). Handle per the 1.3 dropped-notification fallback BEFORE proceeding: check /tmp/batch_name_{N}.txt + leetcode/<dir> existence, confirm with a scoped pytest, and respawn only if zero artifacts exist. Batch 18 stalled multiple turns waiting on two reports the user had to rule out manually — the skill had the check but not the when, which is why this is a gate now. Also prune stale markers for numbers you touched but that are NOT manifest lines (off-manifest touches poison this scan into reporting "all spawned" early).

1.3: After Each Agent Report (main context)
  • PASS: sed -i '' 's/ problem: str = ".*"/ problem: str = "{problem_name}"/' bakefile.py — keeps bake p-test default pointing at the newest problem.
  • MANDATORY every turn: run ListAgents and COUNT running agents, then spawn target - running from the manifest. NEVER infer the flight count from how many reports arrived this turn — notifications clump and lag (spawn latency 1-3 min), so "1 report = 1 slot free, spawn 1" under-spawns persistently and the flight decays (batch 19: sagged to 1 running by batch end; 150-problem batch stretched hours longer than needed). Count the roster, spawn the whole deficit at once.
  • The agent also wrote its real dir name to /tmp/batch_name_{N}.txt — authoritative for tag insert (gate 2.0) and Step 3 re-testing; do NOT derive names from the scrape slug (they can differ)
  • FAIL: log reason for the summary; decide retry once vs skip per Error Handling. Do not debug inline — a failed agent's context is gone; if retrying, spawn a fresh agent including the failure paragraph in the prompt
  • GOTCHA — completion notifications can be silently dropped (3 of 50 in batch 13: the agent left the roster with no notification ever arriving). If an agent seems long-running but you suspect it finished, do NOT respawn (it would collide on the same files) — check /tmp/batch_name_{N}.txt existence + leetcode/<dir> presence, then confirm with a scoped pytest run on that dir before counting it done or scheduling a respawn
  • Suppressed-output discipline (the whole point of this design): never print scrape JSON, full p-gen logs, or full test output to main context. | tail -3 or grep a count. If deeper inspection is needed, it belongs in the NEXT agent's prompt, not this conversation
1.4: What Moved Where (vs inline flow)
  • Agent-side (its context, not yours): scrape reading, JSON writing + verification, p-gen cycles, solution implementation, QA chain, all gotcha application
  • Orchestrator-side (main context): manifest, spawns, bakefile sed, tag insert (gate 2.0), finalization gates, summary
  • Unchanged skills: problem-creation.md and test-quality-assurance.md are read BY THE AGENT — do not summarize them into the prompt beyond the pointers above; the files are the source of truth

Step 2: Batch Finalization (MANDATORY — after ALL problems)

Six gates in order. Do NOT skip any.

2.0: Tag Insert (main-side — agents no longer insert their own)

Agents run concurrently and don't touch tags.json5; insert all batch tags here. Keep the name files — the Step 3 re-test still reads them (deleting them here cost a full wasted re-test cycle in one batch: all 30 problems printed MISSING_NAMEFILE and the mapping had to be rebuilt by hand). Cleanup happens at the END of Step 3:

bash
grep '^QUEUE' /tmp/batch_manifest.txt | while read -r _ N TAG _; do
  NAME=$(cat "/tmp/batch_name_${N}.txt")
  if [ "$TAG" != "none" ]; then
    uv run python .claude/.dev/insert_tag.py "$TAG" "$NAME"
  fi
done
  • Before inserting, verify each name file exists (ls /tmp/batch_name_*.txt | wc -l equals problem count) — a missing file means that agent never reported; handle per Error Handling before proceeding
  • GOTCHA — validate name-file CONTENT before insert, not just existence. An agent wrote its problem NUMBER (1180) instead of the dir name into its name file; insert_tag.py happily bisected the numeric string to a bogus position in tags.json5 and the downstream sed fix preserved that position, costing 2 extra cycles (name fix + position fix + pre-commit rerun). Guard: each name must match ^[a-z][a-z0-9_]*$ — on mismatch, look up the real dir in leetcode/ (or re-ask the agent) before running insert_tag.py. A second variant (453): the agent wrote a plausible but ABBREVIATED dir name (min_moves_...) while the real dir was minimum_moves_... — regex-clean but nonexistent. So also verify the dir and JSON actually exist (the re-test sweep would otherwise false-FAIL on the wrong name):
bash
grep '^QUEUE' /tmp/batch_manifest.txt | while read -r _ N TAG _; do
  NAME=$(cat "/tmp/batch_name_${N}.txt")
  echo "$NAME" | grep -qE '^[a-z][a-z0-9_]*$' || { echo "BAD NAME FILE for $N: '$NAME'"; exit 1; }
  [ -d "leetcode/$NAME" ] || echo "MISSING DIR for $N: $NAME"
  [ -f "src/leetcode_py/cli/resources/leetcode/json/problems/${NAME}.json" ] || echo "MISSING JSON for $N: $NAME"
done
  • Gate 2.2 (tag sync) must come out clean afterwards; if a name file's dir differs from the scrape slug, the name file wins
2.1: Pre-Commit (converts notebooks to .py)
bash
pre-commit run -a
  • nb-to-py converts every leetcode/**/playground.ipynb to playground.py and deletes the .ipynb; lint runs the full pipeline (sort_tags, check_tag_problems, gen_catalog, lint)
  • Fix failures and re-run until clean
  • Why first: bake check-consistency diffs playground.py, not playground.ipynb — notebooks MUST be converted before the consistency check
2.2: Tag Sync Check
bash
uv run python .claude/.dev/update_tags.py
  • Expect No changes found in any of the specified tags. — that IS the pass state (tags were synced per-problem during creation). Missing/Removed lines only appear on a dirty run
  • A clean run creates NO .claude/.dev/update_tags.json — the rm no-ops. Remove the temp file if it exists after a dirty run
  • Gate semantics: the gate counts only Missing lines. Pre-existing Removed lines do NOT block it — surface them to the user instead of silently resolving them
  • Warning: do not resolve Removed lines on curated lists (neetcode-150, neetcode-250, blind-75, etc.) without the user's call — the _tags.list membership check is circular for batch-created problems and following it once cemented bogus tuples into curated lists. Ground truth = source-list membership by problem number
  • If any Missing/Removed lines appear: read and follow @.claude/skills/update-tags.md
2.3: Consistency Check

Each run regenerates ALL problems (minutes on 1300+ trees) — plan to run it as few times as possible:

bash
bake check-consistency > /tmp/consistency.log 2>&1; grep -E 'PASSED|FAILED|Drift' /tmp/consistency.log
  • NEVER tail the live output — the Drift: box prints AFTER the summary line, so a tail window hides which file drifted and forces a full multi-minute re-run just to learn the name (batch 19: 3 full runs instead of 1). Redirect to a file and grep it.
  • Fix ALL drift files in ONE pass, then re-run once. Re-running after every single fix is the expensive failure mode.
  • Regenerates ALL problems from JSON into a temp dir, converts notebooks, lints, then diffs against the working tree (original leetcode/ is restored afterwards — solutions preserved)
  • Expect ✅ Consistency check PASSED: all files match JSON source of truth
  • On drift (Drift: leetcode/<problem>/<file>): read and follow @.claude/skills/consistency-fix.md
  • Loop until PASSED. bake p-gen -f reporting success does NOT mean consistency passes — only this command passing is real
2.4: Test Case Count Check
bash
bake check-test-cases
  • Must exit 0 (no problems at or below the default threshold — same command CI runs in test-reproducibility.yml, so local defaults and CI stay in lockstep). Success Criteria says 12+ per problem, but no other gate counts cases — p-test passes with any count, consistency only diffs generated-vs-JSON (a 10-case JSON is self-consistent), and the count otherwise surfaces only in CI. Batch 189's CI failure (campus_bikes, the_maze_iii at exactly 10) was caught nowhere locally. Runs in seconds — cheap fifth gate. On failure: add machine-verified cases to the JSON template AND mirror them into the generated test_solution.py by hand (regenerating clobbers solutions — see the p-gen -f warning below)
  • GOTCHA — a FAILED run can leave leetcode/ as regenerated stubs. Restoring original leetcode/... runs at the END of the pipeline; if the run aborts at its lint stage (ruff/ty error mid-batch), the restore never executes and the working tree keeps freshly generated TODO-stub solutions for EVERY problem. After any consistency failure, verify before continuing: grep -l "TODO: Implement" leetcode/*/solution.py — any hit means solutions were wiped (rewritable from session context or a /tmp copy; take the copy BEFORE the first consistency run, not after a failure). Only the PASSED run restores reliably
  • GOTCHA — flat cp can silently truncate the safety snapshot. cp leetcode/*/solution.py /tmp/dir/ copied exactly 1 file in one run (glob expansion mangled, likely by the rtk hook) — a silently-empty snapshot defeats its purpose. Use a per-problem loop and verify the count:
bash
mkdir -p /tmp/batch_solutions_$(date +%Y%m%d)
for d in leetcode/*/; do
  [ -f "$d/solution.py" ] && cp "$d/solution.py" "/tmp/batch_solutions_$(date +%Y%m%d)/$(basename "$d")_solution.py"
done
ls /tmp/batch_solutions_$(date +%Y%m%d) | wc -l   # must equal number of problems
  • Warning: bake p-gen -p {problem_name} -f overwrites solution.py with the TODO stub — backup and restore around ANY regen:
bash
cp leetcode/{problem_name}/solution.py /tmp/solution_backup.py
bake p-gen -p {problem_name} -f
cp /tmp/solution_backup.py leetcode/{problem_name}/solution.py
Show full SKILL.md (1,806 more words)Show less
2.5: Docs Stub-Poisoning Check
bash
grep -rl "TODO: Implement" docs/problems/ | wc -l   # must be 0
  • Why: check-consistency (gate 2.3) regenerates leetcode/ as stubs, lints (which regenerates docs/problems/ from those stubs), then restores leetcode/ — docs were the blind spot until 2026-09-03, when all 1404 pages shipped with # TODO: Implement + O(?) embedded despite clean git status. The bakefile now re-runs gen_problems.py after the restore, but any future clobber-window variant re-opens the hole; git status CANNOT detect this class of damage — the poisoned file looks legitimately generated
  • On a nonzero count: uv run python scripts/gen_problems.py regenerates from the real tree, then re-grep. If solutions themselves are stubbed (grep -rl "TODO: Implement" leetcode/*/solution.py), restore from the gate 2.4 safety snapshot FIRST, regen docs second
  • Runs in seconds — cheap sixth gate

Step 3: Batch Summary

Report: total created, success rate, failed problems with reasons, finalization results. Then re-test all batch problems with bake p-test -p {name} and report counts.

GOTCHA — derive re-test names from the agent manifest, not the scrape slug. The dir name the agent created can differ from the scrape's slug (shorter names chosen for E501: 1415 → k_th_lexicographical_..., 1524 → number_of_subarrays_..., 1662 → array_strings_are_equal). Deriving from the slug produced 3/100 false FAILs and a wasted re-verify cycle. Agents write real dir names to /tmp/batch_name_{N}.txt (prompt step 5); test from those files. On a false-FAIL, check the dir exists under a different name before treating it as a real failure.

After the re-test loop completes, remove the name files (they are still needed until here — gate 2.0 deliberately does not delete them):

bash
grep '^QUEUE' /tmp/batch_manifest.txt | while read -r _ N _; do rm -f "/tmp/batch_name_${N}.txt"; done

Finally, invoke the commit-message skill and show the user the ready-to-paste git commit command for the batch (do NOT execute it — the user stages and commits themselves).

3.1: Skill Improvement Suggestions (evidence-driven)

After the summary, review the run and suggest candidate updates to any skill used this session (batch-problem-creation, problem-creation, test-quality-assurance, update-tags, consistency-fix). Scope is the best overall skill, not just additions: new failure modes AND cuts — sections that wasted effort, duplicated another skill, or went stale. When in doubt, prefer deleting or merging over appending.

  • FIRST: read BOTH records:
    • auto-memory skill-suggestions-log (machine-local, not in git) — a suggestion already recorded there is BLOCKED from re-proposal; if it was applied, re-suggesting means the skill text failed (say which wording); if it was skipped, the user chose not to act (do not resurrect)
    • RUN_HISTORY.md in this skill folder (git-tracked) — the experiment registry + per-batch run history
  • Evaluate open experiments against this run's metrics: for each experiment in testing status in RUN_HISTORY.md, compare its success criteria against what this run actually recorded (wall time per problem, gate failures, boundary violations, context use). Move it to validated (then propose the skill change it implies) or rejected (record why, with numbers). An experiment needs at least one full batch of data before any verdict
  • Append this run's record to RUN_HISTORY.md's run history: date, batch size, mode (sequential/parallel, concurrency), agent durations or wall time, FAIL count, gate results, boundary violations. One line per batch — this is the baseline future experiments are measured against. Retention: NEVER more than the last 5 batches as full lines — when appending pushes the count past 5, distill and combine the oldest full lines into aggregate lines (keep only baseline-relevant facts; failure-mode detail lives in the skill-suggestions-log memory, not here). New process changes get registered in RUN_HISTORY.md too
  • Suggestion only. NEVER edit skill files — the user decides and updates skills themselves (unless the user explicitly instructs the edit this session; then record the applied change in the log)
  • Bar for additions: only what (a) will recur in future runs AND (b) existing skill content misses. Reject re-derivations (an existing gotcha catching the issue = skill working), refinements/widenings of working gotchas, niche one-offs (a greppable reference JSON suffices), run trivia. Test: name the exact step that failed AND the cycle it cost — no cost, no suggestion
  • Bar for cuts: anything the run showed to be redundant, misleading, or unused — including steps followed out of habit that added no value
  • New process changes get proposed as experiments, not direct suggestions: if a proposed change alters the run's shape (concurrency, flow order, gate structure), register it in RUN_HISTORY.md with a hypothesis, success criteria, and the metric to watch — the next run evaluates it. One-off gotcha fixes stay regular suggestions
  • If nothing new was learned, say so explicitly — do not invent suggestions

Error Handling

  • Continue the batch when a problem fails — log the reason, move to the next, note it in the summary
  • NEVER edit generated files (helpers.py, test_solution.py, README.md, ...) — fix the JSON template and regenerate. The ONLY exception is solution.py
  • Scrape failures: premium → unscrapable queue; SQL → NON_PYTHON_PROBLEMS; transient API → retry once. For new-source failures the name is unknown until classified — fetch each failed number's title and file accordingly: curl -s https://leetcode.ca/all/{N}.html | grep -o '<title>[^<]*'; SQL/shell titles (Combine Two Tables, Word Frequency, ...) go to NON_PYTHON_PROBLEMS, premium Python titles to UNSCRAPABLE_QUEUE (batch 16: 21 numbers classified in one pass this way)
  • Replacement pool exhausted: pick state does not advance until disk changes, so a replacement --take after failures re-issues only already-picked manifest lines. If the filtered pull yields nothing new, the new/list pool is EMPTY — do not keep widening the take (batch 18: 45-wide pull after 31 failures returned only re-issues; the wider sweep added zero candidates, exactly as batch 16's 230-wide-sweep note predicted in reverse). Instead: shrink the batch to the creatable count, tell the user the gap and where each lost number went (SQL → NON_PYTHON_PROBLEMS, premium → UNSCRAPABLE_QUEUE, premium queue entries can be created this batch via the doocs web flow), and proceed. Recurs every batch from now on: registered lists are complete and the fallback pool only shrinks.

Success Criteria

Each problem: all files generated, optimal solution implemented (single class), 12+ test cases, lint clean, bake p-test passes, QA chain run with solution preserved.

The batch: pre-commit run -a passes, tag sync clean, bake check-consistency PASSED, bake check-test-cases exits 0, zero TODO: Implement hits in docs/problems/, all problems still pass bake p-test after finalization.

Unscrapable Problems Management

Queue placement

unscrapable.py has two lists: UNSCRAPABLE_HANDLED (already created or confirmed not applicable) and UNSCRAPABLE_QUEUE (the todo queue, discovery order). Append new discoveries at the BOTTOM of UNSCRAPABLE_QUEUE (appending at the top re-orders the queue against discovery order). Format: (problem_number, "kebab-name"). next_problem.py skips them automatically. Non-Python problems (SQL, shell) go to NON_PYTHON_PROBLEMS in the same file — never the queue.

Premium clusters

Consecutive premium problems are common (e.g. 243-256). When two neighbors in a row fail, probe the whole range in a loop BEFORE adding exclusions one-by-one, then batch-add the failures:

bash
for n in 247 248 249 250 251 252 253 254 255; do
  r=$(uv run lcpy scrape -n $n 2>&1 | head -c 30)
  case "$r" in Error*) echo "$n FAIL";; *) echo "$n OK";; esac
done

Sanity-check with a known-scrapable number first (e.g. uv run lcpy scrape -n 205) — every number failing means a broken network session, not a premium cluster.

Creating Unscrapable Problems from the Web

When triggered via the unscrapable keyword, or any explicit request to work through unscrapable.py entries. Note: normal batches now drain the queue automatically (--take waterfall, source unscrapable — routing table in Step 1.1); this section covers the web-fetch details those agents need and the explicit keyword drain:

  1. Fetch ALL problem statements upfront, via curl — no Playwright needed. raw.githubusercontent.com serves plain static text; curl -s '<doocs-url>' returns the full markdown faster than a browser and keeps the shared Playwright MCP free for other agents (Playwright cannot run parallel sessions). Do NOT use the web_reader MCP tool — user rule. Playwright is FALLBACK ONLY, for sources needing a real browser (or if curl is blocked) — when needed, request it as the flow's first tool call so the permission prompt lands before any file work. With either method: fetch every statement back to back at the start, then build the batch offline — no web dependency mid-loop
  2. Find not-yet-done entries: UNSCRAPABLE_QUEUE is the todo queue; UNSCRAPABLE_HANDLED entries are done. Still verify against leetcode/ dirs, src/.../json/problems/, and tags.json5 — the split may be stale
  3. Determine tags from source lists: grep the problem number in .claude/.dev/problem_lists/*.py (Python lists, not .list files). The matching list gives the tag — usually neetcode for the NeetCode All queue. Never stamp roadmap tags the number does not belong to. Verify membership with an unambiguous per-file check (grep -c "($n," .claude/.dev/problem_lists/neetcode.py) — a multi-file grep -lE "\($n," .claude/.dev/problem_lists/*.py | sed ... one-liner truncated the filename column in one batch and 7 in-neetcode problems were misread as unscrapable-only (_tags: []), surfacing only as 7 Missing lines at the tag-sync gate
  4. Fetch problem data (see step 0 — fetch upfront via curl):
    • Primary: doocs/leetcode raw markdown mirror — carries the CURRENT official wording, examples, constraints, and topics (front matter tags:). URL pattern: https://raw.githubusercontent.com/doocs/leetcode/main/solution/0100-0199/0156.Binary%20Tree%20Upside%20Down/README_EN.md (century folder 0100-0199, zero-padded number, URL-encoded title). curl -s '<url>' | head -c 4000 — description + first solution. Title-guess 404? List the exact folder via GitHub API: curl -s https://api.github.com/repos/doocs/leetcode/contents/solution/0200-0299 | grep '"name"'
    • Fallback: https://leetcode.ca/all/{N}.html (older wording, usually no constraints) — curl first; Playwright only if it needs JS
    • Playwright GOTCHA (fallback only): browser_evaluate hangs on raw.githubusercontent pages (plain-text doc; eval stalls past 120s). Do NOT evaluate: navigate, then read the auto-saved snapshot at .playwright-mcp/page-*.yml — head -c the file
    • Watch for revised statements: doocs mirrors the current official text, which may differ from the classic version (163 Missing Ranges now returns list[list[int]] ranges and is Easy). Model the JSON on the fetched text, never on memory
    • Cross-check the queue slug against the doocs folder name: queue names are hand-entered and can be flat wrong (302 was queued as smallest-range-covering-elements-from-k-arrays; real 302 is Smallest Rectangle Enclosing Black Pixels — the K-Lists problem is 632, already in the repo). When the GitHub API listing or the fetched title disagrees with the queue tuple, the QUEUE is wrong: model the JSON on the real problem and correct the kebab-name when closing the loop in step 7
  5. Machine-verify every expectation before writing the JSON (see problem-creation.md gotcha). Premium problems ship only 2-3 official examples, so most test cases are hand-invented — run a reference implementation over all of them (uv run python with TreeNode[int].from_list for tree round-trips)
    • Design problems (solution_class_name != Solution, e.g. 244 WordDistance): model on design_hit_counter.json — ops-sequence test cases (['WordDistance', 'shortest', ...] + per-op inputs), run helper instantiates the class mid-sequence
    • Exponential-output problems (result list grows 2^n, e.g. 247): use the n_queens.json dual-method pattern — method 1 asserts full SORTED lists for small n, method 2 asserts result COUNT only for large n (extra assert_<name>_count in helpers_content)
  6. Insert into tags.json5 carefully: the neetcode: block begins with a metadata object ({ tag: "neetcode-250", },) before the string entries. Filter to quoted-string lines when bisecting; verify with git diff + bake lint (sort_tags), not a hand-rolled sort assert
  7. Continue with the normal loop (p-gen, solution, QA, finalization)
  8. Close the loop in unscrapable.py: once created and finalized, move the tuple from UNSCRAPABLE_QUEUE to UNSCRAPABLE_HANDLED. Only move what actually got created — non-Python problems go to NON_PYTHON_PROBLEMS, never the handled list. Entries found already-created during the step-1 staleness check also move at close time (verify they actually exist in leetcode/, the JSON dir, and tags before moving). If step 3 found the queue entry misnamed, fix the kebab-name in the moved tuple

© wislertt, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 1 other file in .claude/skills/batch-problem-creation of wislertt/leetcode-py.

  • SKILL.md
  • RUN_HISTORY.md

Open the folder on GitHubat commit 9155db3

Compare with similar skills

Batch Problem Creation next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Batch Problem Creation compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Batch Problem Creation this skillwislertt/leetcode-py142—~10kAutomated safety check: PassApache-2.0
Fake Model Provider Faultsdifferent-ai/openwork24k—~642Automated safety check: PassCustom licence
Agentacct Workflowmikehasa/agentacct765—~1.6kAutomated safety check: PassMIT
Pipensx Bug Report Triagei3sey/pipensx218—~1.5kAutomated safety check: NotesGPL-3.0
Sage Wiki Integratexoai/sage-wiki620—~861Automated safety check: PassMIT
Antigravity SDK End-to-End Testingomnigent-ai/omnigent11k—~2.7kAutomated safety check: PassApache-2.0

Similar skills

  • Fake Model Provider Faults

    different-ai/openwork

    Makes the desktop app's model provider fail on demand, with refused connections, resets, stalls and HTTP 4xx and 5xx errors, so error and retry states can be reproduced.

    24k GitHub stars~642 tokensUpdated today
    Testing & QAAuto-check passed
  • Agentacct Workflow

    mikehasa/agentacct

    A skill your agent uses when working in a repo with agentacct MCP configured, or when asked to track coding-agent work, smoke-test agentacct integrations, or report objective AI-agent task evidence.

    765 GitHub stars~1.6k tokensUpdated 4 days ago
    Testing & QAAuto-check passed
  • Decodes QR-code bug report photos and screenshots from the pipensx app into a log, then triages crashes and update, download or install failures.

    218 GitHub stars~1.5k tokensUpdated 5 days ago
    Testing & QAAuto-check: notes
  • Sage Wiki Integrate

    xoai/sage-wiki

    Pipeline skill that wires sage-wiki into an existing project — detects language, installs the client, runs a smoke test, and reports.

    620 GitHub stars~861 tokensUpdated yesterday
    Testing & QAAuto-check passed
  • Spins up a local Omnigent server and exercises the Antigravity (Gemini) SDK harness end to end: building agents, running real turns, smoke tests and bug-bashing.

    11k GitHub stars~2.7k tokensUpdated today
    Testing & QAAuto-check passed
  • Diagnose

    ipea/geobr

    Root-cause a failing or wrong geobr call with a disciplined check-the-environment-first loop instead of guessing.

    959 GitHub stars~1.5k tokensUpdated 10 days ago
    Testing & QAAuto-check passed

More from wislertt/leetcode-py

  • Leetcode Py

    wislertt/leetcode-py

    Generates Python LeetCode practice environments and manages a 307-problem catalog with the lcpy CLI.

    142 GitHub stars~1.4k tokensUpdated today
    Auto-check passed
  • Test Quality Assurance

    wislertt/leetcode-py

    Test quality assurance workflow for LeetCode problems - verifies reproducibility, fixes linting issues, and ensures test structure matches JSON templates.

    142 GitHub stars~2.9k tokensUpdated today
    Auto-check passed
  • Problem Creation

    wislertt/leetcode-py

    Guide for creating a single LeetCode problem scaffold - scrape, transform to JSON template, generate files, and verify.

    142 GitHub stars~9.9k tokensUpdated today
    Auto-check passed
  • Update Tags

    wislertt/leetcode-py

    Use ONLY when user explicitly requests "update tags" or "/update-tags", or after running .claude/.dev/updatetags.py and seeing "Missing" or "Removed" discrepancy lines.

    142 GitHub stars~2.5k tokensUpdated today
    Auto-check passed

Works with

Categories

Questions about Batch Problem Creation

What does Batch Problem Creation do?

Batch creation workflow for multiple LeetCode problems - loops through problem creation, solution implementation, and quality assurance for a specified count. Batch Problem Creation is an agent skill from wislertt/leetcode-py. Batch creation workflow for multiple LeetCode problems - loops through problem creation, solution implementation, and quality assurance for a specified count.

When should I use Batch Problem Creation?

Batch Problem Creation fits situations like: explicitly requests batch creation via /batch-problem-creation command; tasks that involve QA and bug reports.

How do I install Batch Problem Creation in Claude Code?

Run `npx skills add wislertt/leetcode-py --skill batch-problem-creation -a claude-code`. Or copy the skill folder (.claude/skills/batch-problem-creation in wislertt/leetcode-py) into .claude/skills/batch-problem-creation in your project. Claude Code loads it when a task matches its description.

How do I install Batch Problem Creation in Codex?

Run `npx skills add wislertt/leetcode-py --skill batch-problem-creation -a codex`. Or copy the skill folder (.claude/skills/batch-problem-creation in wislertt/leetcode-py) into .agents/skills/batch-problem-creation in your project. Codex loads it when a task matches its description.

Can I use Batch Problem Creation in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add wislertt/leetcode-py --skill batch-problem-creation -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/batch-problem-creation, .gemini/skills/batch-problem-creation, .github/skills/batch-problem-creation and .opencode/skills/batch-problem-creation in your project.

What does Batch Problem Creation need to run?

Going by SKILL.md and its folder, Batch Problem Creation needs the command-line tools its instructions call (uv, curl, git and python3). Our summary lists: Python 3.

Does Batch Problem Creation access the network?

SKILL.md names 3 domains. In commands or code: leetcode.ca, raw.githubusercontent.com and api.github.com; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.

Is Batch Problem Creation safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Batch Problem Creation use?

Batch Problem Creation is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Batch Problem Creation use?

About 10k tokens (SKILL.md is roughly 40k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Batch Problem Creation?

Skills that share tags, products or a category with Batch Problem Creation: Fake Model Provider Faults (different-ai/openwork, 24k stars), Agentacct Workflow (mikehasa/agentacct, 765 stars), Pipensx Bug Report Triage (i3sey/pipensx, 218 stars) and Sage Wiki Integrate (xoai/sage-wiki, 620 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Batch Problem Creation?

wislertt (a GitHub user) maintains it in wislertt/leetcode-py, which has 142 GitHub stars. The repository holds 5 skills in this directory. The repository was last updated on October 7, 2026.

Source: wislertt/leetcode-py on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.