Skip to main content

KxInspections — Contesting Charges

One contract, two runtimes, zero required backend for review. This site is the single navigable reference for the whole assignment: a FastAPI mock backend in kxinspect_backend_python/ and a Flutter frontend in kxinspect_frontend_flutter/, both frozen against Contract v1 (schemaVersion 1, contractVersion 1, seedVersion 1).

  • Interview goal — A student inspects a property, receives damage/cleaning charges, and can Accept or Contest each charge (with reason + attachments) before a 30-day deadline auto-accepts it. Maintenance Hub, Inspection Detail, Charge Detail and Contest Charge are the four feature states plus Hub tabs.
  • Fixture-first — The Flutter app ships a byte-stable fixture bundle copied from the backend seed. Reviewers complete the full journey on Chrome + Android with no server, and the app can be flipped to remote with a single config flag.
  • Backend role — A deliberately non-production FastAPI service over a single-writer JSON store that implements every lifecycle rule, envelope, idempotency, attachment, SSE and deadline reconciliation exactly as docs/contract-v1.md specifies.

How this documentation is organized

SectionWhat you find
Getting Started5-minute boot for both stacks, project map, workflow
BackendArchitecture, domain, store engine, API, config, verification
FrontendArchitecture, bootstrap/DI, routing, BLoCs, design system, data & sync
Shared ContractContract v1 digest, fixture bundle, glossary, ADRs
Guides & OpsCI/CD artefacts, troubleshooting, evidence boundaries
graph TD
Contract[Contract v1\ncontract-v1.md + openapi-v1.json + vectors] --> BE[Backend\nFastAPI + JsonStore]
Contract --> FE[Frontend\nFlutter + Drift + fixtures]
BE -->|seed export| Fixtures[assets/fixtures\napp_state.json + manifest.json]
Fixtures --> FE
BE <-->|HTTP + SSE + idempotency| FE
FE -->|fixture mode| Local[(Drift local_store)]
FE -->|remote mode| Remote[(FastAPI + runtime/state.json)]

Two repositories, one workspace

The assignment ships as a workspace containing two independent Git repos. That matters for history, CI and submission.

kienetic_assignment/
├── kxinspect_backend_python/ # Python 3.13, uv, FastAPI 0.141, Pydantic v2
│ ├── app/core, domain, db, services, schemas, api
│ ├── app/seed/*.json # canonical seed
│ ├── app/static/photos # deterministic media
│ ├── docs/contract-v1.md # frozen
│ ├── docs/openapi-v1.json # frozen
│ ├── runtime/state.json # demo state (gitignored at root, committed in sample)
│ └── tests/{unit,api,contract}
├── kxinspect_frontend_flutter/ # Flutter 3.41 / Dart 3.11, bloc + go_router + drift
│ ├── lib/app, core, design_system, features, router, l10n
│ ├── assets/fixtures/{app_state.json, manifest.json, media, vectors}
│ ├── test/{unit,widget,golden, integration_test}
│ ├── tool/{test_all.sh, android_runtime_proof.sh, aggregate_evidence.dart}
│ └── docs/{adr, evidence, handoffs}
└── documentation/ # this Docusaurus site
└── docs/{backend, frontend, shared, guides}

:::tip Reviewer fast-path

  1. cd kxinspect_frontend_flutter && flutter run -d chrome --dart-define=DATA_SOURCE=fixture — complete a Contest and an Accept, reload, see persistence.
  2. Flip one flag: --dart-define=DATA_SOURCE=remote --dart-define=API_BASE_URL=http://127.0.0.1:8000 after starting the backend with uv run uvicorn app.main:create_app --factory --host 127.0.0.1 --port 8000.
  3. Run uv run pytest -q and ./tool/test_all.sh — they are the canonical gates. :::

Contract-first philosophy

Everything derives from one frozen artefact: contract-v1.md + openapi-v1.json + golden examples under docs/contracts/examples/ + the exported fixture bundle. Changing any of those is a G-01 decision that must update both repos in lockstep. The Python app/domain imports no FastAPI/Pydantic/filesystem code so the same lifecycle table can be exported as charge_state_vectors.json and replayed from Dart.

Key properties preserved by both stacks:

  • 30-day deadline deadlineAt = raisedAt + gracePeriodDays (UTC, inclusive now >= deadlineAt).
  • Explicit lifecycle: 5 statuses, 6 events, total function — every (status,event) pair is either a transition or charge.invalid_transition.
  • Determinism: fixture export, manifest hashes, OpenAPI snapshot and contract tests are byte-stable.

Status & non-goals

AreaState
Contract v1frozen at G-01
Backend reads, commands, idempotency, attachments, SSE, dev routesimplemented, tested
Frontend fixture mode, hub/inspection/charge/contest/statement, offline outbox, theme, notificationsimplemented
ruff, mypy --strict, flutter analyze, dart formatpassing locally
CI workflows / hosted evidenceowned by a later work package; local gates are documented

Non-goals (by design): production auth/payment, multi-process DB, hosted push notifications, real payment settlement. The backend is loopback-only, single-worker, and loudly documents its limits in contract-v1.md §15.

Where to go next

This site is built with Docusaurus 3 (classic, TypeScript). npm run build produces a static build/ you can host anywhere. No backend is required to browse it.