Repository navigation
Releases: micheltlutz/dev-challenge
Release list
2.0.0 — Executable specs, verification harness and agent instructions
The lab release. dev-challenge moves from a set of markdown briefs to something you can actually work in: the specification is machine-checked, your solution is verifiable by a command, and coding agents have the context to help without handing you the answer.
⚠️ Breaking changes
-
Balances and statement lists are now scoped to the authenticated user. Previously every user of the shared instance saw and polluted the same global ledger, which made challenge 5 semantically empty. The documented global balance of
13512.59no longer exists. -
New fixture. Both demo users now share the password
Challenge@123:User Statements Balance john.doe@email.com5 15491.00 jane.roe@email.com1 4200.00 The old seeded user had an unrecoverable bcrypt hash, so nobody could actually log in to the ledger every brief quoted.
-
GET /users/meadded. A client cannot callPUT /users/{user_id}without knowing its own id, and the token carries the e-mail rather than the id — the profile challenge was literally unimplementable by a front-end. -
Challenges moved to
specs/challenges/<id>/. The oldreadme_files/challenge_*.mdare redirect stubs, so shared links keep working.
🔒 Security
Closes every open Dependabot alert.
- PyJWT 2.8.0 → >=2.13.0, clearing five advisories: CVE-2026-48526 (a public-key JWK accepted as an HMAC secret, enabling forged HS256 tokens), CVE-2026-32597 (unknown
critheader extensions), CVE-2026-48525 (detached-JWS DoS) and CVE-2026-48522/48524 (PyJWKClient SSRF and DoS). Two were genuinely reachable, since the API decodes attacker-supplied tokens. python-josedropped — 7 advisories, one with no fix available, and PyJWT already handled decoding.passlibdropped. 1.7.4 is unmaintained and raises onbcrypt >= 4.1, which brokePOST /users/outright on any fresh install.fastapi[all]replaced with the packages actually imported. The extra pulled in ujson, orjson, jinja2 and more that nothing here uses.- The committed JWT signing key is gone.
app/.envwas tracked and held theSECRETsigning tokens on the public instance. Rotate it on your deployment if you run one. - Version floors (
>=) instead of~=. A compatible-release pin blocks every patch outside the pinned minor — exactly how the project came to ship five open advisories. - Authorization fixed on
PUT /users/{user_id}(403 for someone else's profile, no password echoed), single-algorithm allow-list on decode,expandsubrequired.
pip-audit is clean in a fresh venv, in the API image and in the harness image.
📋 Executable specifications
specs/ is now the source of truth — not the code, not the prose.
The briefs had drifted: they documented POST /create_contact with a field named nome while the API served POST /contact/ with name. The first request anyone made returned a 422 for reasons the documentation could not explain.
- 20 challenges (17 existing + a new fullstack track where neither side of the contract is given to you), with validated frontmatter: track, difficulty, estimated hours, prerequisites, endpoints, mockups, test IDs.
- 132 acceptance criteria with stable IDs in Given/When/Then form.
specs/openapi.jsongenerated from the code and diffed in CI.- Design tokens on disk (W3C DTCG + generated CSS), a style guide and a screen inventory — the design previously existed only behind a Figma link.
✅ Verification harness
docker run --rm --network host ghcr.io/micheltlutz/dev-challenge-harness \
--base-url http://localhost:3000 --challenge backend-05-balance PASS BE05-AC1 Unauthenticated request is rejected
FAIL BE05-AC3 Seeded balance is exact
└─ john.doe@email.com should have a balance of 15491.00, got 13512.59.
SKIP BE05-AC5 Deposit increases the balance
└─ Cannot create statements (POST /statement/ returned 404).
2/7 criteria passed
Black-box, language-agnostic, reporting per criterion rather than per stack trace. Playwright for the web track, driven by the testids each spec declares. Maestro flows for mobile, marked best-effort — native UI cannot be verified the way an API can, and the specs say so rather than implying coverage that does not exist.
🤖 Agent instructions
AGENTS.md as the single source, mirrored to CLAUDE.md, Copilot and Cursor. Three skills, two subagents, and /challenge, /verify, /next.
The default posture is mentor, not solver — it plans and reviews with you instead of writing the challenge for you — with an explicit, documented escape hatch when you do want the code.
🔁 CI that keeps it honest
spec-drift.yml fails the build when the contract, the tokens, the specs, the seed or the committed fixture disagree with the code. Each mechanism was verified by deliberately breaking it. harness-selftest.yml proves the harness and the reference API agree. audit.yml runs pip-audit on every push and weekly.
Getting started
- Challengers: readme_files/challenged.md
- All challenges: specs/README.md
- Contributors: CONTRIBUTING.md
Design by Aline Moraes. Still a non-profit teaching lab — see SECURITY.md before deploying anything from it.
Full changelog: 1.2.0...2.0.0
1.2.0
1.1.2
1.1.1
Update requirements fast api to fastapi[all]>=0.109.1
1.1.0
- New Challenges for back-end and front-end
- Little fix to use ALGORITH from .env
1.0.0
1.0.0 Version
- auth_routes
- balance_routes
- contact_routes
- home_routes
- statement_routes
- user_routes