# Module 10 · request, error, database and review contract

## Observe these real routes

All paths below are on your explicit localhost port. No remote/provider URLs or uploads are accepted.

| Route | Input and actor | Actual result |
|---|---|---|
| GET /health/live | Public | Process alive/local_fixture_only; not readiness |
| GET /openapi.json | Public | Actual generated schemas and Bearer requirements |
| GET /docs | Public | Interactive docs; Swagger assets require internet |
| POST /evaluations | Configured author; strict metadata JSON | 201, fresh ID, Location, supplied values/two disclosed metadata checks |
| GET /evaluations/{id} | Any configured fixture actor | Stored creation snapshot or 404 |
| POST /evaluations/{id}/findings | Configured author; criterion/rating/evidence | 201 draft/revision 1/server actor ID |
| GET /evaluations/{id}/findings/{finding_id} | Any configured fixture actor | Current finding scoped to its actual parent, or 404 |
| POST /evaluations/{id}/findings/{finding_id}/transitions | Configured actor; action/expected_revision | Permitted atomic next state/revision/audit, or 403/409 |
| GET /evaluations/{id}/audit | Any configured fixture actor | Ordered controlled history, no raw evidence |

Supported metadata: canonical ASCII asset_id 1..64 letters/digits/underscore/hyphen; integer width/height 1..16384; integer frames 1..1,000,000; integer fps 24/25/30. No boolean stand-ins, strings-as-numbers or extra fields. Width/height are supplied metadata, not measured media. Resolution condition is width>=1920 AND height>=1080; duration is frames<=60*fps. Neither condition selects a winner.

Finding input: criterion motion or prompt_adherence, exact integer rating 1..5, evidence 1..1000 characters containing nonwhitespace text and no unsupported controls. Evidence is learner-authored fixture text, not machine-authenticated evidence. Unknown author/reviewer/state fields are refused rather than trusted.

## Trace four actual review requests

1. Create an evaluation with fixture-author-a. Retain its returned ID.
2. POST its findings route with `{"criterion":"motion","rating":3,"evidence":"Invented frame 12"}`. Retain finding ID.
3. POST that finding's transitions route with `{"action":"submit","expected_revision":1}` and fixture-author-a. Expect submitted/revision 2.
4. POST `{"action":"approve","expected_revision":2}` with fixture-reviewer-a. Expect approved/revision 3 and reviewer_A. Read the persisted finding and audit after restart.

Try approval while draft (409), another author's submit (403), self-approval after submission (403), a stale expected revision (409) and a forged body author/reviewer (422). A rejected finding also becomes terminal; rejection does not erase its original rating/evidence.

Creation snapshot `review_status_at_creation=unreviewed` remains a creation fact. It is not a current aggregate review state. The current finding and audit routes expose later workflow observations. Classroom approval does not prove a real person's identity, source rights, creative correctness or dataset eligibility.

## Error meanings

Every controlled error has `{"error":{"code":"…","message":"…","request_id":"…"}}`; request ID is server-generated and matches X-Request-ID, never copied from a supplied header. All responses use Cache-Control:no-store. 401 includes WWW-Authenticate:Bearer.

| Status | Stable code | Recovery |
|---|---|---|
| 400 | invalid_json, invalid_input, invalid_host, ambiguous_headers | Fix the request; no operation is reported complete |
| 401 | unauthorized | Use a configured PUBLIC fixture token, not a DVP login |
| 403 | forbidden, invalid_origin | Use an allowed actor/transition or same-origin local client |
| 404 | not_found, route_not_found | Check the actual returned ID/parent/path |
| 405 | method_not_allowed | Use the documented HTTP method |
| 409 | conflict | Read current state/revision; do not blindly retry stale writes |
| 413 | body_too_large | Reduce actual UTF-8 body below 32,769 bytes |
| 415 | unsupported_media_type, unsupported_encoding | Use plain application/json, no compression |
| 422 | invalid_request | Fix strict types, bounds or unknown fields |
| 500 | internal_error | Retain request ID and diagnose locally; never treat as client success |

Malformed UTF-8, duplicate JSON keys (including nested keys), NaN/Infinity and excessive nesting are refused before mutations. Body budget measures actual received chunks, not a declared Content-Length alone. The ASGI adapter is reviewed framework plumbing; these lessons do not yet teach an async pipeline. No rate limit/deadline/hostile-ingress guarantee.

## Actual persistence evidence

Record/event writes use parameterized SQL under BEGIN IMMEDIATE, COMMIT only after both writes, ROLLBACK on failure. Migration statements execute individually, not through an implicitly committing executescript. Fresh reads parse typed JSON; unknown/future databases refuse initialization. Existing NEW destinations are not replaced. Known file-path checks are not race-proof ownership.

Review commit uses the actual database revision, not only a prior HTTP read. Foreign parent lookups cannot return another evaluation's finding. A failed/stale transition adds no event. Injected audit failure must leave the previous finding/revision intact. Invalid or corrupted stored records fail visibly rather than become approved evidence.

## Learner boundary map

Copy this into your own project notes. Do not replace it with a green checkbox.

| Boundary | Authored test/input | Actual observation | What it does NOT prove | Assistance used |
|---|---|---|---|---|
| Strict metadata/model | | | | |
| API request/response/error | | | | |
| Real SQLite migration/restart | | | | |
| Atomic record + audit failure | | | | |
| Actor/state/revision separation | | | | |
| Actual localhost process/HTTP | | | | |

Keep evidence invented and local. Declare reviewed schema, ASGI/auth/docs/path/connection/migration assistance. Own your transformations, domain mapping, SQL/transactions and transition policy. No live provider, customer account, paid request or public deployment is required. Module 12's independent capstone/defense is still separate.
