--- name: flypython-challenges version: 0.1.0 updated: 2026-09-14 description: Run FlyPython challenge courses with a coding agent — authorize yourself with a one-time link, fetch the course files, work the task contract, check with a single `python verify.py`, and submit earned claim codes in one batch. Use when the user mentions FlyPython, wants to practice Python through challenges, or asks to record/claim challenge checkpoints. --- # FlyPython challenge workflow FlyPython is a challenge platform: the human has an account, you (the agent) hold its API token after they approve a one-time authorization link, and `verify.py` decides when a challenge is done. Points and badges come only from codes `verify.py` actually printed. The foundation idea: the human learns to drive **their own** agent — any agent — through a challenge; this skill works the same way in every one of them. **Prerequisite (say this plainly when asked):** these courses need a coding agent that can execute commands *and* reach the network — the Skill itself is a URL, course files come from an API, and claims are HTTP POSTs. A chat-only web AI cannot do them. If your environment blocks the network, use the fallbacks in "Installing" below. ## Version check This Skill is version `0.1.0` (updated 2026-09-14). `GET https://flypython.com/api/challenges` returns `skill_version`. If it is newer than the version at the top of this file, tell the human their local copy is stale and reinstall it with the commands below. ## Installing this skill (relay to the human when asked) The skill lives at `https://flypython.com/skills/flypython/SKILL.md`. Per tool — install location, **how to allow network access**, and the fallback when networking is unavailable (the human runs the given command in their own terminal and pastes the result back to you): | Tool | Install | Network | No-network fallback | | --- | --- | --- | --- | | **Claude Code** | `mkdir -p .claude/skills/flypython && curl -s https://flypython.com/skills/flypython/SKILL.md -o .claude/skills/flypython/SKILL.md` (personal: under `~/.claude/skills/`) | approve the fetch/curl permission prompts; sandboxed runs may block the network | the human runs each curl themselves and pastes outputs | | **Codex app / CLI** | `curl -s https://flypython.com/skills/flypython/SKILL.md >> AGENTS.md` in the project | approve commands and web access in the thread; the Codex CLI's default sandbox blocks network — enable it or approve per run | the human runs commands in their own terminal | | **Cursor** | `mkdir -p .cursor/rules && curl -s https://flypython.com/skills/flypython/SKILL.md -o .cursor/rules/flypython.mdc` | Agent mode fetches URLs with approval — approve it | same curl fallback | | **DeepSeek Harness** | `curl -s https://flypython.com/skills/flypython/SKILL.md >> AGENTS.md` in the project root | approve the agent's network tool use | same curl fallback | | **Kimi Code** | `curl -s https://flypython.com/skills/flypython/SKILL.md >> AGENTS.md` in the project root | approve network commands | same curl fallback | | **ZCode** | `curl -s https://flypython.com/skills/flypython/SKILL.md >> AGENTS.md` in the workspace root | approve network tool use | same curl fallback | | **Any other agent** | paste this SKILL.md's URL (or body) as the first message | whatever the tool calls "web access / fetch" — turn it on | paste command outputs to the agent manually | ## 0. Setup — one authorization link, no passwords Never ask the human for their password and never register on their behalf. Ask for their agent-facing name, then start an authorization request: ``` curl -s -X POST https://flypython.com/api/agent/authorize \ -H "Content-Type: application/json" \ -d '{"agent_name": ""}' ``` You get `{ user_code, verify_url, device_code, expires_in }` (10 minutes). Show the human the **verify_url and the user_code together**, and say: "Open this link, log in, check that the code shown there matches this one, and click Allow. Then tell me when you're done." When they say done, exchange **once** — do not poll, you are a turn-based agent: ``` curl -s -X POST https://flypython.com/api/agent/token \ -H "Content-Type: application/json" \ -d '{"device_code": ""}' ``` - `authorization_pending` → the human has not approved yet (or the code mismatched). Ask them again; exchange again only after they confirm. - `access_token` → export it as `FLYPYTHON_TOKEN` for this session. Ask before writing it anywhere persistent. Immediately fetch the claim secret for run receipts (the token's `claim-secret` scope allows exactly this): ``` curl -s -H "Authorization: Bearer $FLYPYTHON_TOKEN" \ https://flypython.com/api/account/claim-secret # → {"claim_secret": "fcs_…"} ``` Export it as `FLYPYTHON_CLAIM_SECRET`. Receipts are optional — without the variable, `verify.py` behaves exactly as before. Manual fallback (rare): the human creates a token at https://flypython.com/account/agent and pastes it to you. OAuth-only users must use this path or the link above — passwords are never collected. The token is the human's identity for claims — it can submit codes, read the claim secret, and (if they subscribe) fetch Pro files; nothing else. Progress and badges live at /dashboard; tokens can be revoked at /account/agent. ## 1. Pick a challenge — ask, then drive Ask one question: goal or level, e.g. "brand-new to Python, or already writing scripts? want a guided path or one specific challenge?" - Catalog: `curl -s https://flypython.com/api/challenges` — every challenge with title, points, and a `pro` flag. Free ones are `pro: false`. - By level: complete beginner → the **foundation** path (https://flypython.com/tracks/foundation, free, walks them through driving you); comfortable scripting → single courses like `da-eda`; dev tooling → the `dt-*` courses (Pro). - By goal: data work → `da-*`; verification → `verifying-ai-generated-code`; agent rules → `agent-rules-single-source`. Paths bundle courses in order with their own badge — https://flypython.com/tracks. - Confirm the pick in one line ("foundation, starting l01 — go?"), then solve it before suggesting the next. ## 2. Fetch the course files — you fetch, the human does not download - Brief: `curl -s https://flypython.com/api/challenges/` — task-contract path, checkpoint ids, verify command, the submission contract, and a `files` field pointing at the manifest below. - Files manifest (free courses): ``` curl -s https://flypython.com/api/challenges//files ``` It lists every file with its repository-relative `path`, `sha256`, `size`, and `encoding` (`utf-8` text or `base64` binary), plus a prebuilt `zip` URL (`?format=zip`). Write each file at its `path` — that layout is what `verify.py` expects (the shared `tools/claim_receipt.py` included). Verify sha256 after writing; base64-decode the binary ones. - Pro courses: the same manifest endpoint inlines every file as `content_b64` — it needs the bearer token (your `pro:files` scope only works while the human's subscription is active; otherwise you get 403 with a /pricing link to show them). Transition fallbacks (an old Skill copy's habits — avoid unless the endpoint is down): the whole-repository `repository.zip` from the brief, or `git clone https://github.com/flypythoncom/python.git`. Both ship 500+ files for a 25-file course. ## 3. Read the contract, then solve - Open `TASK.md` (path is in the brief) before touching any file — it is the contract: task, boundaries, definition of done. `COURSE.md` is an optional guided mode; use it only if the human asks for teaching. - Rules: smallest change that satisfies the contract; no new dependencies beyond the course's `requirements.txt`; never edit `tests/`, `verify.py`, or `solution/` — those are the judge. ## The human stays in the loop (hard gates — a spec, not a guarantee) The point is that *the human* learns; you are the tool. The site cannot enforce this — these gates are this Skill's rules, and you follow them: | Gate | What you must do | | --- | --- | | Course start | restate the task contract in one sentence; ask whether to begin | | Self-reported checkpoints (l01, l02, l05, …) | ask the human the lesson's Checkpoint questions; only submit after they answer | | Bounded change (test-gated checkpoints) | show a diff summary; continue to the next failing group only after the human confirms | | Before submitting | list the checkpoints you are about to claim and their evidence tier; submit after the human confirms | | Track attestation modules | submit `attest: true` only after the human explicitly says they did it | Never claim a gate was "human-confirmed" to the server — there is no such field to trust. Run receipts prove the suite ran; these gates keep the human learning. Neither is a certification. ## 4. Prove it with one command From the course folder: ``` python verify.py ``` That is the whole check: it runs the suites against the learner's implementation, prints per-checkpoint status with the earned claim codes (English first, Chinese after), and exits non-zero while a gated checkpoint is still open. `[open]` means keep working in `starter/`; `[passed]` means the gate is green. A code only counts when this command (or `python verify.py progress`) printed it. With `FLYPYTHON_CLAIM_SECRET` exported the same command prepares signed local-run receipts — for machine-readable output use `python verify.py progress --json` (adds a `"receipts"` array) or `--receipt-out receipts.json`. Maintenance commands exist (`starter --expect-failure`, `solution`) but are not the learner's workflow — never present the reference `solution` run as a completion standard. ## 5. Submit in one batch The brief's `checkpoints[].type` decides how each one is claimed: | `type` | what it means | how you submit | | --- | --- | --- | | `gated` | a verify.py suite decides; the code appears once it passes | the printed code | | `self-reported` | verify.py prints the code unconditionally | the printed code, **only if the human did the work** | | `attest` | no code exists (track modules) | `attest: true`, after the human confirms | List the earned checkpoints for the human, get their confirmation, then submit the whole run at once: ``` curl -s -X POST https://flypython.com/api/claims \ -H "Authorization: Bearer $FLYPYTHON_TOKEN" \ -H "Content-Type: application/json" \ -d '{"claims": [ {"course": "", "checkpoint": "l01", "code": ""}, {"course": "", "checkpoint": "l03", "code": "", "receipt": { …the checkpoint's own receipt object from the receipts array… }} ]}' ``` Each item is judged independently (`results[]` in the response: per-item `ok/recorded/receipt`), so one bad code never blocks the rest — fix and re-send only the failed ones; repeats return `recorded: false` and change nothing. Attach each checkpoint's own receipt when you produced them (§4). The response carries totals: `points`, `badges`. Single-claim posts (`{"course": …, "checkpoint": …, "code": …}`) still work as a fallback. Use JSON with the bearer token (cross-site form posts are rejected by CSRF). A `429 rate_limited` means too many *rejected* submissions recently: stop, re-read the checkpoint type, and re-run `python verify.py` for a fresh code rather than retrying the same body. Track modules with no verifier (e.g. `path-foundation`: `m0-orientation`, `route-challenge`) submit `attest: true` — only after the human says they actually did it: ```json {"claims": [{"course": "path-foundation", "checkpoint": "route-challenge", "attest": true}]} ``` Report the response to the human and point them at /dashboard/progress to see progress and badges. ## Honesty rules (non-negotiable) - Never fabricate, guess, or brute-force a claim code. Report only codes `python verify.py` printed in a run you executed. - Never weaken or bypass a test to make a gate pass. - Claims are self-reported evidence for the human's own progress — submit only checkpoints they earned, and say plainly when something did not pass. ## Environment notes - Python 3.11+. Courses needing pandas/matplotlib declare them in `requirements.txt`; `uv` is the preferred runner for those (`uv run python verify.py`). - Each course folder is self-contained once fetched through §2: `TASK.md`, `starter/`, `solution/`, `tests/`, `verify.py`, scenario data, and the shared `tools/claim_receipt.py` at its relative path. ## Changelog - **0.1.0 (2026-09-14)** — entitlement wording now matches the launched model: `pro:files` works while the human's subscription is active, and a subscription unlocks every Pro course (no per-path pick). No flow changes. - **0.0.8 (2026-09-13)** — authorization links replace password collection (§0); course files arrive via the per-course manifest instead of the whole-repository zip (§2); one default command `python verify.py` replaces the four-usage workflow (§4); batch submission (§5); explicit human-in-the-loop gates; per-tool install + network table; version check. Old flows (password signup, `repository.zip`) keep working during a transition window — expect `skill_update` hints in API responses until the human reinstalls. - **0.0.7** — optional run receipts (`FLYPYTHON_CLAIM_SECRET`, `progress --json` / `--receipt-out`), attachable per claim. - **0.0.6 and earlier** — session-cookie signup, whole-repo zip fetch, `verify.py starter --expect-failure` / `solution` / `progress` as the documented workflow, single-claim submission.