Development¶
See also CONTRIBUTING.md.
Setup and tests¶
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,docs]"
PYTHONPATH=. python3 -m unittest discover -s tests -v
The suite (about 300 tests, ~20 s) needs no GPU, network, Ollama or model files.
| Area | How it is tested without hardware |
|---|---|
| HTTP server | create_server(port=0) with a FakeEngine (tests/fakes.py): shapes, SSE framing, errors, auth/CORS/Host, concurrency, client disconnect |
OnnxGenAiEngine |
FakeOg, a stand-in onnxruntime_genai module: streaming, finish reasons, provider selection and fallback |
| Catalog / paths | Temp directories with stub genai_config.json |
pull |
A fake huggingface_hub.snapshot_download writing files, including the subfolder layout |
| CLI | prism.cli.main() with patched argv, capturing stdout |
| Connectors / MCP | Temp working directory and a live handshake against bin/prism mcp |
tests/test_prism_gpu_integration.py runs the real engine and server, and skips itself unless onnxruntime_genai, an
NVIDIA GPU and a model are present and the CUDA provider actually loads. To run it:
export PRISM_PYTHON=/path/to/venv/bin/python3 PRISM_MODEL_DIRS=/path/to/models
PYTHONPATH=. "$PRISM_PYTHON" -m unittest tests.test_prism_gpu_integration -v
tests/test_docs.py checks that every CLI subcommand and server route is mentioned in these docs.
Documentation site¶
mkdocs serve # http://127.0.0.1:8000, live reload
mkdocs build --strict # CI runs this; broken links and missing pages fail the build
Pages are published to GitHub Pages by .github/workflows/docs.yml on every push to main. One-time repository setting:
Settings → Pages → Build and deployment → Source: GitHub Actions.
Continuous integration¶
.github/workflows/ci.yml runs the unit tests on Python 3.10–3.12, byte-compiles the package, and builds a wheel that is
installed in a fresh virtualenv and smoke-tested with prism --help and prism doctor. On pull requests it also runs the
changelog check and ruff on the lines the pull request changed (see Lint).
Lint¶
ruff is configured in pyproject.toml (pip install -e ".[dev]"). Nothing is auto-formatted and old code is not a debt: the
gate reports only violations on lines you changed.
ruff check path/to/changed.py # everything in the file, for your information
python3 scripts/sdlc_check.py --only lint # what the gate and CI enforce: changed lines only
The check skips itself when ruff is not installed. It is not part of the opt-in pre-commit hook.
Working with AI coding agents¶
The repository carries one agent workflow that works across harnesses (Claude Code, Codex, Cursor, Copilot, Gemini CLI). Every non-trivial change goes through intent, spec, plan, test, code and review, and each stage leaves a committed file:
| File | Read by | Purpose |
|---|---|---|
AGENTS.md |
Codex, Cursor, Copilot, Gemini CLI, ... | Commands, the process and the project rules; the single source of truth |
CLAUDE.md |
Claude Code | Imports AGENTS.md, adds Claude-only notes |
intent.md, spec.md, plan.md, REVIEW.md |
Agents and humans | Intent, specification, plan (the state of the work) and review policy |
.agents/skills/sdlc* |
Skill-aware agents; Claude Code through the .claude/skills symlink |
One skill per stage group |
.agents/skills/python-conventions |
Same | How to write Python in this repository |
scripts/sdlc_check.py |
Agents, humans, CI | Deterministic gate (compile, tests, changelog, docs, lint) and --red |
.claude/settings.json, scripts/lint_hook.py |
Claude Code | Shared permissions and a hook that runs ruff on each edited Python file |
.editorconfig |
Editors | Encoding, line endings, indentation |
.githooks/pre-commit |
git (opt-in) | Runs the gate before each commit: git config core.hooksPath .githooks |
Details: Agent SDLC workflow.
Keeping the model aliases honest¶
PYTHONPATH=. python3 scripts/verify_aliases.py # needs network; exits 1 if any alias is broken
Releasing¶
Releases are published from GitHub Actions with PyPI trusted publishing (OIDC), so no API tokens are stored anywhere.
The workflow is .github/workflows/publish.yml (manual: Actions → Publish → Run workflow).
One-time setup
- On test.pypi.org (and later pypi.org),
add a pending publisher: project
prism-local, ownersenssei, repositoryprism-local, workflowpublish.yml, environmenttestpypi(respectivelypypi). - In the GitHub repository, create the environments
testpypiandpypi(Settings → Environments). Add yourself as a required reviewer onpypiso a release needs an explicit approval.
Each release
- Update
CHANGELOG.mdandprism/__init__.py(__version__), and merge tomainwith CI green. - Run Publish → target
testpypi. It builds the sdist and wheel, runstwine check --strict, uploads to TestPyPI, then installs the uploaded version into a clean virtualenv and smoke-testsprism --helpandprism doctor. - Tag the release (
git tag -s vX.Y.Z && git push origin vX.Y.Z) and run Publish on that tag with targetpypi. The workflow refuses to publish to PyPI unless it runs from the tagv<__version__>. - Create a GitHub release for the tag.
Versions are permanent
Neither index lets you re-upload a version, and PyPI never lets you reuse one. Use a pre-release version such as 0.1.0rc1
while rehearsing on TestPyPI if you expect to iterate.