|
1 | | -# CLAUDE.md — orientation for AI agents (and humans) working on pytest-gpu-proof |
| 1 | +# Claude orientation |
2 | 2 |
|
3 | | -pytest-gpu-proof is a **pytest plugin that emits a signed JSON receipt of a GPU |
4 | | -test run** (git SHA + source fingerprint + per-test outcomes + GPU info), plus a |
5 | | -**CPU-only verifier** (`gpu-proof verify`) that checks the signature against the |
6 | | -signer's public `github.com/<user>.keys`. The point: run GPU tests on your own |
7 | | -hardware, let CI prove who attested them — no cloud-GPU fees, no secrets in CI. |
| 3 | +Use [AGENTS.md](AGENTS.md) as the canonical agent guide. In particular: |
8 | 4 |
|
9 | | -**Trust model — never oversell it:** a receipt is a **signed attestation by a |
10 | | -keyholder, NOT cryptographic proof of GPU execution**. `gpu_info` is |
11 | | -self-reported; `--require-gpu` is modest hardening against accidents, not |
12 | | -against a dishonest signer. `docs/security_model.md` is the honest statement of |
13 | | -what is and isn't proven — keep every README/docs claim consistent with it. |
| 5 | +- a receipt is signer attestation, not proof of GPU execution; |
| 6 | +- receipt, fingerprint, signature, Git, merge, and policy code fail closed; |
| 7 | +- new recordings use schema 3 and bind signer metadata inside the signature; |
| 8 | +- the default manifest covers the full tracked repository; |
| 9 | +- tests are hermetic and maintain 100% line and branch coverage; |
| 10 | +- generated `site/`, `dist/`, coverage output, real keys, and local receipts are |
| 11 | + not source changes. |
14 | 12 |
|
15 | | -## Source layout (`src/pytest_gpu_proof/`) |
16 | | - |
17 | | -| Module | Role | |
18 | | -|---|---| |
19 | | -| `plugin.py` | pytest hooks: options, `gpu_proof` marker, `gpu_proof_check` fixture, outcome+skip capture, receipt emission at session end | |
20 | | -| `receipt.py` | payload build (repo/fingerprint/tests/env), canonical JSON, sign+write. Signer resolution: flag/config → `gh` CLI login (keyholder) → origin-remote owner (warned — orgs have no SSH keys) | |
21 | | -| `verify.py` | the 7 verification checks (signature, fingerprint, commit SHA, outcomes+skip policy, gpu_info, freshness, dirty policy). Expected-skips baseline = EXACT set match | |
22 | | -| `cli.py` | `gpu-proof verify` argument surface | |
23 | | -| `config.py` | `GpuProofConfig`; CLI flags override `[tool.gpu_proof]` in pyproject.toml | |
24 | | -| `fingerprint.py` | SHA-256 digest over configured paths | |
25 | | -| `gitutils.py` | git/gh shell-outs, all failure-tolerant (return `None`) | |
26 | | -| `signers/` | `base.py` protocol + `ed25519.py` SSH-key signing / GitHub-keys verification; backend `none` emits unsigned receipts | |
27 | | -| `compare.py` | receipt diffing | |
28 | | - |
29 | | -Signing covers the canonical (compact, sorted-key) JSON **without** the |
30 | | -`signature` field; the sig block carries `signer`, key fingerprint, algorithm |
31 | | -(derived from actual key type — don't hardcode ed25519). |
32 | | - |
33 | | -## Behavioral invariants (test-enforced — don't regress) |
34 | | - |
35 | | -- **Skips prove nothing.** Verifier rejects receipts with skips unless |
36 | | - `--allow-skipped` (any skips) or `--expected-skips` (EXACT baseline: a new |
37 | | - skip fails, a stale baseline entry fails). The two are mutually exclusive. |
38 | | -- **Unsigned receipts** verify only with `--allow-unsigned`, loudly. |
39 | | -- **`--max-age-days 0`** means "today only", not "disabled". |
40 | | -- The signer recorded at signing time must be the **keyholder**, never |
41 | | - silently the repo owner. |
42 | | - |
43 | | -## Dev workflow |
44 | | - |
45 | | -```bash |
46 | | -.venv/bin/python -m pytest tests/ -q # full suite, ~2s, no GPU needed |
47 | | -.venv/bin/mkdocs build --strict # docs must stay warning-clean |
48 | | -``` |
49 | | - |
50 | | -- Tests are **hermetic**: `tests/conftest.py` has an autouse fixture nulling |
51 | | - `get_gh_cli_login` (a dev box with authenticated `gh` would otherwise hit the |
52 | | - network). Signer tests re-patch explicitly. Keep new shell-outs mockable and |
53 | | - wrapped in try/except like `gitutils._git`. |
54 | | -- `tests/conftest.py` uses `pytest_plugins = ["pytester"]`; keypairs are |
55 | | - generated in-memory (no real SSH keys touched). |
56 | | -- requires-python ≥ 3.11 (`datetime.UTC`). |
57 | | -- CI (`.github/workflows/`): tests on 3.11/3.12 + mkdocs gh-pages deploy. |
58 | | - |
59 | | -## Conventions & state |
60 | | - |
61 | | -- Short single-line commit messages; no Co-Authored-By footer. |
62 | | -- Flow: feature branch → PR → CI green → merge to `main`. Consumers install |
63 | | - from git (`pip install -e` on a submodule) or PyPI. **Releases**: OIDC |
64 | | - trusted publishing via `.github/workflows/publish.yml` (TestPyPI on manual |
65 | | - dispatch, PyPI on GitHub release) — process in `RELEASING.md`; keep |
66 | | - `CHANGELOG.md` current and bump `pyproject.toml` version in the same PR. |
67 | | -- Reference integration: **GLASS** (github.com/A2R-Lab/GLASS) — |
68 | | - `test/run_gpu_proof.sh`, `test/expected_skips.txt`, |
69 | | - `.github/workflows/verify-gpu-proof.yml`. If you change plugin/verifier |
70 | | - flags, check GLASS's usage still works and note it in the PR. |
71 | | -- Consumer-side gotcha worth remembering: a repo that submodules this project |
72 | | - under its pytest rootdir must `collect_ignore = ["pytest-gpu-proof"]` in its |
73 | | - conftest, or this repo's `tests/conftest.py` will shadow theirs. |
| 13 | +Before implementation work, read `docs/architecture.md`, |
| 14 | +`docs/security_model.md`, `docs/policy.md`, and `CONTRIBUTING.md`. |
0 commit comments