Shared Contract — One Truth
Files that are frozen together
kxinspect_backend_python/docs/contract-v1.md ← prose contract, G-01
kxinspect_backend_python/docs/openapi-v1.json ← wire snapshot
kxinspect_backend_python/docs/contracts/examples/*.json (15) ← golden envelopes
kxinspect_backend_python/scripts/export_fixtures.py → bundle
kxinspect_backend_python/app/domain/charge_state.py + deadline.py → vectors
kxinspect_frontend_flutter/assets/fixtures/{app_state,manifest,vectors,media}
kxinspect_backend_python/app/schemas/* ← StrictModel definitions
Changing any of those without a new G-01 decision breaks cross-repo codegen and causes contract tests to fail on the other side.
Why app/domain being pure matters
app/domain/charge_state.py imports only enum/dataclasses; deadline.py imports only datetime. No FastAPI, Pydantic, filesystem or clock. That single rule lets:
charge_state_vectors.json(30 rows) anddeadline_vectors.jsonbe generated bypython scripts/export_fixtures.py --output /tmp/bundlewithout importing the web layer,- the same JSON be replayed by
tests/unit/test_domain.pyandtest/unit/charge_state_vectors_test.dart— two runtimes, one oracle.
Any future lifecycle field must land there first.
Digest highlights
| Concept | Frozen detail |
|---|---|
| Identifiers | CHG-, BKG-, INS-, RPT-, NTF-, TCK- seed; task id client UUID; attachment att_<32 hex> opaque |
| Money | amountMinor int + currency ISO 4217; floats rejected; totals per-currency |
| Text | grapheme 10–2000 via UAX #29; server graphemes.py = Dart package:characters |
| Enums | 11 frozen tables (inspectionType, chargeStatus, …) — unknown ⇒ protocol error |
| Envelope | {data,meta} or {error,meta}; stateEpoch only on reset, storeRevision per commit |
| Idempotency | (method, routeTemplate, resourceId, key) scope; RFC 8785 digest SHA-256(canonical JSON); only 2xx cached |
| Deadline | deadlineAt = raisedAt + graceDays UTC, inclusive now >= deadlineAt, reconciliation under lock |
| Seed | referenceNow 2026-08-01T12:00:00Z; Europe/London; 30-day default grace |
OpenAPI snapshot discipline
docs/openapi-v1.json is checked in. uv run python scripts/export_openapi.py --check docs/openapi-v1.json diffs the live FastAPI generation byte-for-byte. On mismatch you do not overwrite — you file a contract change, regenerate vectors/fixtures/examples, and update Dart api_models + domain_mapper lockstep.
Fixture bundle commitment
After any seed edit, the steward must run the export, verify via verify_fixture_manifest.py, and cp the bundle into kxinspect_frontend_flutter/assets/fixtures/ and commit. Fixture-mode review must stay network-free; the frontend FixtureHttpClientAdapter must render the same states as remote.
See Fixture Bundle for the file map and Decisions for frozen interpretations (Open vs History overlap, bottom nav dedup, etc.).