Skip to content

Releases: micheltlutz/dev-challenge

2.0.0 — Executable specs, verification harness and agent instructions

Choose a tag to compare

@micheltlutz micheltlutz released this 12 Aug 17:50

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.59 no longer exists.

  • New fixture. Both demo users now share the password Challenge@123:

    User Statements Balance
    john.doe@email.com 5 15491.00
    jane.roe@email.com 1 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/me added. A client cannot call PUT /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 old readme_files/challenge_*.md are 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 crit header 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-jose dropped — 7 advisories, one with no fix available, and PyJWT already handled decoding.
  • passlib dropped. 1.7.4 is unmaintained and raises on bcrypt >= 4.1, which broke POST /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/.env was tracked and held the SECRET signing 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, exp and sub required.

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.json generated 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

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

Choose a tag to compare

@micheltlutz micheltlutz released this 30 Apr 14:57
bf3c49f

UI Screens 1.2.0

1.1.2

Choose a tag to compare

@micheltlutz micheltlutz released this 29 Apr 17:38
4575c46

Fix Tests =/

1.1.1

Choose a tag to compare

@micheltlutz micheltlutz released this 29 Apr 17:32
5db6562

Update requirements fast api to fastapi[all]>=0.109.1

1.1.0

Choose a tag to compare

@micheltlutz micheltlutz released this 02 Jan 19:54
027271e
  • New Challenges for back-end and front-end
  • Little fix to use ALGORITH from .env

1.0.0

Choose a tag to compare

@micheltlutz micheltlutz released this 28 Sep 22:25
3327af9

1.0.0 Version

  • auth_routes
  • balance_routes
  • contact_routes
  • home_routes
  • statement_routes
  • user_routes