codepraxis
Author and validate CodePraxis challenge packs from your own repository.
pip install codepraxis
codepraxis --example # see a real challenge, no account needed
codepraxis new my-challenge # scaffold one that already validates
codepraxis lint my-challenge # static checks, no execution
codepraxis validate --local my-challenge # run it, fast and advisory
codepraxis --login
codepraxis --publish my-challenge # validates in the runner, then publishes
What this is
A challenge pack is a directory — starter code, a test module, instructions. This CLI lets you keep packs in your own git repository, iterate on them with your own editor, and check them before they reach candidates.
Two tiers, one result shape:
| Command | Runs | Speed | Authoritative? |
|---|---|---|---|
codepraxis validate --local |
Pure-Python harness on your machine | seconds | No — advisory |
codepraxis validate --remote |
The real runner image, on CodePraxis | ~1 min | Yes — gates publish |
--local reproduces how the runner loads, orders and scores a pack, so it
catches most authoring mistakes in the inner loop. It is not the container:
it does not run setup.sh and does not have the image's package set. Anything
it cannot check is reported as a note rather than silently passing.
Publishing always requires a remote run.
Packs that call a model
By default there is no model endpoint locally, so cases that need one are reported unverifiable rather than failed — a pack is not broken just because your laptop has no LLM proxy. Point it at a real endpoint and they run for real:
export OPENAI_API_KEY=... # or --llm-api-key
export OPENAI_BASE_URL=... # or --llm-base-url
codepraxis validate --local my-challenge
Once a key is configured the leniency stops: a model failure is then a real failure, because it can be judged.
Try it first
codepraxis --example
Starts a throwaway container with the featured challenge already cloned into it
and prints a URL. That is exactly what a candidate sees. No login, no account,
nothing recorded as an attempt. Add --open to launch it in your browser.
Starting a new pack
codepraxis new my-challenge
codepraxis validate --local my-challenge # → PASSED
The generated pack is complete and already passing — solution passes, starter
fails. Edit ._course_data/feature.md, source/, ._tests/test_1.py and
../solution/ to make it yours.
Two kinds of checking
| Command | Runs | Cost |
|---|---|---|
codepraxis lint |
Static rules over the files | milliseconds, no execution |
codepraxis validate |
The tests, against both fixtures | seconds (local) or ~1 min (remote) |
lint never imports pack code, so it is safe on a pack you did not write and
fast enough for every save. It catches things that otherwise only surface inside
a container — a two-argument testCases.__init__, zero-padded case names, a
solution/ directory nested inside the pack, LaTeX in the Instructions brief.
validate runs it automatically first and stops if it finds an error, because
executing a pack whose class cannot be constructed only produces a confusing
traceback.
The two fixtures
Every pack is validated twice:
- solution —
source/overlaid withsolution/. Must pass everything. - starter —
source/alone. Must fail.
The starter run is the one authors forget. A pack whose starter already passes has tests that do not discriminate, and every candidate will score full marks.
askgit (local)
solution 18/18 passed
starter 0/18 passed
PASSED 8.9s
Three verdicts: PASSED, FAILED, and INCONCLUSIVE (this tier lacked
the infrastructure to judge it — not a failure, and it does not fail the
command). --json always emits a single document with a packs array, however
many packs ran.
Pack layout
my-challenge/
├── metadata.json # {"name": "..."} — becomes the workspace directory
├── backend.conf # {"BACKEND": "AI", "LANGUAGE": "PYTHON"}
├── setup.sh # optional; installs dependencies (remote only)
├── source/ # what the candidate starts from
├── ._tests/test_1.py # a `testCases` class
└── ._course_data/
├── course_toc.json # selects the active test module
└── feature.md # the Instructions tab
solution/ # sibling, never uploaded — the reference solution
Authoring with Claude Code
codepraxis --install claude-plugin
Writes a local plugin into .codepraxis/claude-plugin/, then tells you the two
commands to enable it. You get:
/codepraxis:new— scaffold a pack from a description/codepraxis:validate— validate and fix what fails, in a looppack-authoringskill — loads automatically when Claude touches a pack, so it already knows thetestCasescontract and the two-fixture rule
Re-run with --force to overwrite an existing install.
Authentication
codepraxis --login
Prompts for an API key (hidden input), verifies it against the platform, and
stores it at ~/.config/codepraxis/config.json with 0600 permissions. It
prints which company the key publishes as — worth reading, because that is what
every publish is scoped to.
In CI, skip the prompt:
export CODEPRAXIS_TOKEN=...
export CODEPRAXIS_API_URL=... # optional; defaults to the production API
Publishing
codepraxis --publish my-challenge # draft, with confirmation
codepraxis --publish my-challenge --live # straight to candidates
codepraxis --publish my-challenge --yes # non-interactive, for CI
Publishing is deliberately strict, because a published challenge can be assigned to candidates immediately:
- Remote validation runs first. Local results never qualify. Reuse an
earlier passing run with
--validation-run-idif you have one. - A reference solution is required. It is what proves the challenge is solvable.
- It publishes as a draft unless you pass
--live. - The company comes from your API key. The CLI never sends a company id — ownership is derived server-side, so a compromised or mistyped client can't publish into someone else's catalog.
You'll be shown the company and asked to confirm before anything is created.
Development
python3.11 -m pip install -e '.[dev]'
pytest
ruff check src tests scripts
The package has no runtime dependencies. The harness must run on an author's
machine with nothing but a Python interpreter, so keep it that way — the remote
tier uses urllib from the standard library for the same reason.
Conformance tests
The harness mirrors the production runner's behaviour (setupCodeBase.py,
koro/test_loader.py, koro/test_runner.py). Mirrors drift, so
tests/conformance/ replays the harness across a corpus of real packs and
asserts the expected verdicts.
That corpus is private and lives outside this repository:
PRAXIS_CONFORMANCE_PACKS=/path/to/question-bank pytest tests/conformance
Without the variable the conformance tests skip.
Do not vendor packs into this repository. This package is published publicly. No challenge content, no reference solutions, no fixtures derived from real questions. Scaffold templates must be written from scratch.
Release files for codepraxis 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| codepraxis-0.3.1.tar.gz | 56.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| codepraxis-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 126.1 kB
Release files / codepraxis-0.3.1.tar.gz
| Download URL | codepraxis-0.3.1.tar.gz |
|---|---|
| Size | 56.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d9483be8d28e2c5286757ca6e662772005136fe4054f766ff7cd5bea83fd2094
|
|
BLAKE2b-256 checksum How to use checksums |
13e3850183517a8a1a3f10db7d1d4838e05bcda56684d6b3d60eb9d331230e96
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.
Transparency logRelease files / codepraxis-0.3.1-py3-none-any.whl
| Download URL | codepraxis-0.3.1-py3-none-any.whl |
|---|---|
| Size | 69.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ace5f8a03fb24a4212c508c1596bcbddbd8688d8ff21d4bc5e7fabb8671d8eb3
|
|
BLAKE2b-256 checksum How to use checksums |
c7df6cfc2e41c11b8bc50d38f5977d37d6070b231b9e24d355b0b93ca47a2e12
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.
Transparency log