Compare commits

..

1 Commits

Author SHA1 Message Date
claude-checkpoint-tooling 3a7b2ab306 Add PC1 checkpoint record + playtest instructions
Validation results from the 2026-08-20 checkpoint run (fresh clone,
commit 927f9bd: migrations, 68/68 pytest, all 4 Godot smoke tests, both
platform exports) plus manual playtest instructions for backend on
cloudbison / client on pinkbison. First of docs/checkpoints/PC<N>.md,
one per Package Checkpoint going forward.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 01:17:18 +00:00
2 changed files with 131 additions and 63 deletions
+84
View File
@@ -0,0 +1,84 @@
# Package Checkpoint 1 (PC1)
Validated 2026-08-20 against `main` commit `927f9bd`, from a genuinely fresh
clone (not a reused agent workspace).
## Automated validation
Run with `scripts/run_checkpoint_tests.py` — see
`docs/testing/test_inventory.md` for exactly what each suite covers.
| Check | Result |
|---|---|
| Alembic migrations, fresh DB | PASS |
| Backend pytest | 68/68 PASS |
| Godot smoke tests (4 files) | ALL PASS |
| Linux export (`agp-game.x86_64`) | Builds, verified as a real ELF64 binary |
| Windows export (`agp-game.exe`) | Builds, verified as a real PE32+ binary |
## Problems found during this checkpoint (fixed, tracked as tasks)
- Roadmap showed the Godot Engine Scaffold feature amber despite 6/6 tasks
done — a historical data gap (a review-feedback follow-up task's
supersession link was never recorded, predating when that field started
being tracked). Backfilled in the PM database; confirmed green.
- `docs/testing/test_inventory.md` was stale (said 38 backend tests,
actually 68; said no integration tests existed, 4 real ones had landed).
Fixed via task `398bf1a22a2d` / PR #40.
## What's actually testable right now
Login/register/browse-worlds only — there's no joinable gameplay yet. That's
the real scope of PC1; don't expect more when playtesting.
- Register a new account (18+ only, rejected otherwise)
- Log out, log back in with the same credentials
- Play → Browse Worlds — loads a (currently empty) world list with no errors
## Manual playtest: backend on cloudbison, client on pinkbison
Cloudbison's LAN IP: `192.168.58.138`. Pinkbison's: `192.168.58.124`.
### 1. On cloudbison — start a real (persistent, not throwaway) backend
```bash
cd ~
git clone http://localhost:3000/daddy/agp-game.git agp-game-playtest
cd agp-game-playtest/backend
docker compose up -d postgres
docker compose exec -T postgres pg_isready -U agp_user -d agp_db # wait for this to succeed
export DATABASE_URL="postgresql+asyncpg://agp_user:agp_password@localhost:5432/agp_db"
export JWT_ALGORITHM="RS256"
/home/daddy/agp/venv/bin/python3 -m alembic upgrade head
nohup /home/daddy/agp/venv/bin/python3 -m uvicorn app.main:app --host 0.0.0.0 --port 8000 > /tmp/agp-backend.log 2>&1 &
disown
```
`--host 0.0.0.0` matters — the default `127.0.0.1` only listens on loopback
and pinkbison won't be able to reach it. Verify: `curl
http://localhost:8000/api/health` should return `{"status":"ok",...}`.
### 2. On pinkbison — get the game and point it at cloudbison
Copy `export/windows/agp-game.exe` (or `export/linux/agp-game.x86_64`,
whichever matches pinkbison's OS) from cloudbison, or clone the repo there
and run `scripts/build.sh` locally. Launch with the backend URL as an env
var so you don't have to rebuild to change it:
- Windows (PowerShell): `$env:AGP_API_BASE_URL="http://192.168.58.138:8000"; .\agp-game.exe`
- Linux: `AGP_API_BASE_URL=http://192.168.58.138:8000 ./agp-game.x86_64`
### 3. Tear down when done
```bash
# on cloudbison
pkill -f "uvicorn app.main:app"
cd ~/agp-game-playtest/backend && docker compose down -v
```
## Status
`config/checkpoint_focus_feature_ids` in the AGP framework (`~/agp`) still
restricts agent claiming to the 5 PC1-scope features. Agents will not move
on to post-PC1 work until that's widened.
+47 -63
View File
@@ -7,88 +7,72 @@ to know what "run the tests" actually checked.
Run everything below in one shot with `scripts/run_checkpoint_tests.py`. Run everything below in one shot with `scripts/run_checkpoint_tests.py`.
Last verified against `main`: 2026-08-20 (commit `927f9bd`) — full PC1 Last verified against `main`: 2026-08-19 (commit `1c75367`).
checkpoint run, fresh clone, migrations + pytest + all 4 Godot smoke tests +
both platform exports, all clean.
## Backend — `backend/tests/` (pytest) ## Backend — `backend/tests/` (pytest)
Run: `cd backend && python -m pytest -v` (needs `DATABASE_URL` pointed at a Run: `cd backend && python -m pytest -v` (needs `DATABASE_URL` pointed at a
reachable Postgres — see the runner script). reachable Postgres — see the runner script; most of these tests don't
actually touch the DB, but the app's import chain requires a valid URL to
construct).
**Current total: 68 tests, all passing on a clean `main` checkout.** **Current total: 38 tests, all passing on a clean `main` checkout.**
| File | Tests | What it covers | | File | Tests | What it covers |
|---|---|---| |---|---|---|
| `test_auth_age_gate.py` | 17 | `calculate_age()` date-math correctness (exact boundary, leap years, birthday-not-yet-this-year) + `RegisterRequest` date-of-birth validation (parametrized valid/invalid date rejection). Backs the 18+ registration gate (PR #23). | | `test_auth_age_gate.py` | 10 | `calculate_age()` date-math correctness (exact boundary, leap years, birthday-not-yet-this-year) + `RegisterRequest` date-of-birth validation (parametrized valid/invalid date rejection). Backs the 18+ registration gate (PR #23). |
| `test_gltf_metadata.py` | 21 | glTF/GLB binary + JSON parsing: magic-byte detection, chunk-padding handling, mesh/animation/texture-reference metadata extraction, malformed-input rejection. Backs the asset-import pipeline (PR #27). | | `test_gltf_metadata.py` | 21 | glTF/GLB binary + JSON parsing: magic-byte detection, chunk-padding handling, mesh/animation/texture-reference metadata extraction, malformed-input rejection. Backs the asset-import pipeline (PR #27). |
| `test_auth_endpoints.py` | 11 | **Integration tests** for `POST /api/auth/register` and `POST /api/auth/login` against the real FastAPI app + real Postgres. Covers: successful register (201), duplicate username (409), duplicate email (409), underage rejection (400), invalid date format (422), exact-18 registration, successful login with correct credentials (200), wrong password (401), nonexistent user (401), form-encoded login (OAuth2), and JWT validity of returned tokens. |
| `test_refresh_endpoint.py` | 8 | **Integration tests** for `POST /api/auth/refresh`. Covers: successful refresh (200, new access token, same refresh token returned), refresh produces different access tokens on repeated calls, invalid refresh token (401), empty body (422), missing field (422), refresh token from login also works, returned access token is valid JWT decodable with public key, `access_token_expires_minutes` in response. |
| `test_refresh_token_jti.py` | 6 | Unit tests for refresh token uniqueness (jti claim) and model column length. Covers: two calls with same sub produce different tokens (jti uniqueness), jti is valid UUID, rapid repeated calls all produce unique jtis, jti survives decode, `RefreshToken.token` column is `String(512)`, column type is `String`. |
| `test_auth_expired_token.py` | 5 | **Integration tests** for expired-token rejection across auth endpoints. Covers: expired access JWT rejected by `GET /api/auth/me` (401), garbage access token rejected by `/me` (401), missing bearer token returns 401, expired refresh token (DB `expires_at` in past) rejected by `/refresh` (401), expired refresh JWT (past `exp` claim) rejected before DB lookup (401). |
**What these are, honestly:** a mix of pure unit tests (`test_auth_age_gate.py`, **What these are, honestly:** pure unit tests of standalone functions
`test_gltf_metadata.py`, `test_refresh_token_jti.py`) and real integration tests (`calculate_age`, the glTF parser, Pydantic validators). None of them spin
(`test_auth_endpoints.py`, `test_refresh_endpoint.py`, `test_auth_expired_token.py`) up the FastAPI app or hit a real endpoint — they run in ~1s and don't
that hit the FastAPI app against a real Postgres database via `TestClient`. actually need Postgres reachable at all (the import chain just needs a
The integration tests exercise the full stack: Pydantic validation → DB → syntactically valid `DATABASE_URL`, nothing connects during collection).
JWT → response. The suite is also re-runnable without manual cleanup —
`backend/tests/conftest.py` has a session-scoped autouse fixture that
truncates all app tables once before the suite runs.
**Known coverage gaps** (real gaps, not placeholders — nothing enforces filling **Known coverage gaps** (real gaps, not placeholders — nothing enforces filling
these yet): these yet):
- No endpoint tests for `GET /api/worlds`, `GET /api/assets/*`, or any - No integration/endpoint tests at all — `/register`, `/login`, `/refresh`,
world/asset CRUD routes — only auth endpoints have integration coverage `/worlds`, `/assets/*` have zero tests that actually call them through
(`GET /api/worlds` is covered indirectly via the Godot smoke tests below). the FastAPI app (e.g. via `httpx.AsyncClient` + `ASGITransport`) against a
- No tests for the moderation schema/migration beyond "the migration runs." real (or test) database. Everything currently shipped works because it
- No tests for the `GET /api/auth/me` endpoint's response body (only its was manually verified at merge time (checkpoint review), not because a
auth-header validation is covered via expired-token tests). test asserts it.
- No tests for session management (create/validate/revoke sessions). - No tests for the RS256 auth upgrade (key generation, refresh-token
issuance/persistence) — PR #30, pending review as of this writing,
manually verified (correct key usage, all 38 existing tests still pass)
but not covered by an automated test of its own yet.
- No tests for the moderation schema/migration (in progress as of this
writing) beyond "the migration runs."
## Godot client — `scripts/smoke_test_*.gd` ## Godot client — `scripts/smoke_test_*.gd`
**4 smoke tests merged into `main`.** Each needs a live backend — **Nothing merged into `main` yet as of this writing** — `sandbox_probe/test_script.gd`
`AGP_API_BASE_URL` env var, falling back to the is the only Godot script on `main` right now, and it's **not a real
`application/agp/api_base_url` project setting (default test** (a leftover manual probe, returns a hardcoded string, asserts
`http://127.0.0.1:8000`) if unset. nothing). Don't count it toward coverage; delete or repurpose it if anyone
actually needs a sandbox probe again.
| File | What it covers | `scripts/smoke_test_api.gd` (PC1, PR #29, pending review as of this writing)
|---|---| will be the first real one once merged — `ApiClient` autoload's
| `smoke_test_api.gd` | Exercises `ApiClient.health_check()` against a live backend. Verifies the real HTTP request path (`HTTPRequest` node lifecycle, JSON parse, result-dictionary contract). Exits 1 on failure. | `health_check()` against a **live backend** (set `AGP_API_BASE_URL` to point
| `smoke_test_auth.gd` | Tests the full auth flow via `ApiClient` autoload: register unique user (201), duplicate username (409), under-18 registration (400), correct login (200), wrong password (401), nonexistent user (401). | at one), verified by hand end-to-end before opening the PR: exercises the
| `smoke_test_e2e.gd` | End-to-end flow: health check → register → login → worlds list. Verifies the full request chain with a fresh user, including token storage and `get_worlds()` with auth header. | real HTTP request path (`HTTPRequest` node lifecycle, JSON parse, the
| `smoke_test_world_list.gd` | Instantiates `world_list.tscn` as a real scene, adds to tree (catches `@onready` node-path bugs), waits for `_refresh_worlds()` to complete, confirms the screen reaches a real end state (world cards, empty-list message, or error label — not stuck loading). | result-dictionary contract), not just that the script compiles. Exits 1 on
failure, so it's CI/script-friendly.
Run (once, to build the class cache): `godot --headless --path . --import`, Run (once merged): `godot --headless --path . --import` once (builds the
then for each smoke test: `godot --headless --path . --script class cache — skip this and every `class_name`-declared class falsely
res://scripts/smoke_test_<name>.gd` — **no `--quit`**, it kills these before errors as undeclared), then for each `scripts/smoke_test_*.gd`:
their awaited HTTP calls finish. `godot --headless --path . --script res://scripts/<name>.gd`
**Known gaps:** **Known gap once it lands:** only `health_check()` is exercised end-to-end.
- No smoke tests for the main menu scene (`main_menu.tscn`). `login`, `register`, `get_worlds`, `get_me` are implemented (see
- No smoke tests for chunk loading/streaming (`chunk.gd`, `chunk_scene.gd`). `scripts/api_client.gd`) but have no smoke coverage yet — each would need a
- No smoke tests for dynamic/static object nodes. real user/world fixture in the target backend to test against meaningfully.
- All smoke tests require a live backend; no offline/unit smoke tests exist.
- `smoke_test_world_list.gd` is the only one that exercises a real `.tscn`
scene instantiation — others only test the `ApiClient` autoload.
- `get_me()` (`GET /auth/me`) is implemented in `scripts/api_client.gd` but
has no smoke coverage yet.
## Client export
`scripts/build.sh` produces real Linux (`agp-game.x86_64`) and Windows
(`agp-game.exe`) binaries via `--export-release`. Verified 2026-08-20: both
exports complete clean from a fresh checkout and produce valid binaries
(confirmed via `file` — real ELF64/PE32+, not empty or truncated). Neither
export is exercised by an automated GUI test (headless Godot can't drive a
window) — actually launching and clicking through the exported binary is a
manual step, see `docs/checkpoints/PC1.md` for playtest instructions.
## What "full coverage" should eventually mean here ## What "full coverage" should eventually mean here
Not defined precisely yet — the honest target as of 2026-08-20 is Not defined precisely yet — the honest target as of 2026-08-19 is just
"integration tests for every endpoint + smoke tests for every major scene." "more than pure-function unit tests." As real integration/endpoint tests get
As real integration/endpoint tests get added, update the tables above added, update the tables above rather than letting this doc drift stale;
rather than letting this doc drift stale; a wrong inventory is worse than a wrong inventory is worse than no inventory.
no inventory.