Files
mechanic-pwa/docs/superpowers/STATE.md
T

11 KiB
Raw Blame History

Premium Mechanic PWA — Implementation State

Last updated: 2026-05-16 (end of session 2 — Phase B done)

What is done

Phase Task Status Artifact
0 Git infrastructure (Gitea + repos + feature branches) DONE repos exist, ssh keys configured
A A1: Recon TaxiDashboard backend structure DONE docs/backend-layout-notes.md
A A2: Caddy fragment for mechanic.pptaxi.ru DONE ops/Caddyfile.fragment
A A3: MinIO CORS on pp-inspections bucket DONE ops/set-minio-cors.sh, applied, preflight 204
A A4: SQLAlchemy models (MechanicInspection, MechanicInspectionPhoto, MechanicDamageMarker) DONE commit be0553e on feat/mechanic-mvp
B B1: Mechanic module skeleton + /healthz DONE commit d2113a0 — flat backend/app/api/mechanic.py with APIRouter(prefix="/v1/mechanic"), registered in app/main.py with prefix="/api" → final URL /api/v1/mechanic/healthz
B B2: SQLAlchemy models registered + tests DONE already shipped in A4 (be0553e) — 38 metadata tests in backend/tests/test_mechanic_models.py, no extra B2 commit
B B3: Pydantic schemas DONE commit 2739cc8 + fix 2fb978d — 14 schemas + 6 Literals in backend/app/api/mechanic_schemas.py, 15 behavioral tests in backend/tests/test_mechanic_schemas.py; fix added = None defaults to Optional[X] fields and tightened MarkerOut.damage_type/severity to DamageType/Severity Literals
B B4: Auth + DB session dependencies DONE commit f0d7e83backend/app/api/mechanic_deps.py re-exports get_db+get_current_user, adds require_mechanic guard (admin OR member of any active dept_type='svc' department); 9 tests in backend/tests/test_mechanic_deps.py use fake-DB stubs (no real session needed)

Branch state: feat/mechanic-mvp at f0d7e83. Stack: be0553e (A4) → d2113a0 (B1) → 2739cc8 (B3) → 2fb978d (B3-fix) → f0d7e83 (B4). All pushed to origin (Gitea).

Test count on branch: 62 mechanic tests pass (38 models + 15 schemas + 9 deps) in ~0.8s, no real DB needed.

Where things live

Repos (Gitea: http://100.64.0.9:3001/tremble7681)

Repo Local clone Working branch
mechanic-pwa c:\NewProject\PremiumDriverApp\ feat/mechanic-mvp
taxi-dashboard c:\NewProject\taxi-dashboard\ feat/mechanic-mvp

taxi-dashboard clone has two remotes: origin → Gitea, vds → SSH to /opt/sites/taxi-dashboard/ on 100.64.0.12 (for pulling back-and-forth with prod).

Reference paths (real, verified — see docs/backend-layout-notes.md)

  • Base, TimestampMixinapp.models.base
  • get_dbapp.db (file: app/db/__init__.py)
  • get_current_userapp.auth.deps
  • Userapp.models.user
  • Auth flow: OAuth2 password + Bearer JWT (not PIN as plan originally said)
  • DB init: Base.metadata.create_all at startup (not Alembic) + inline ALTER TABLE ADD COLUMN IF NOT EXISTS in app/main.py lifespan
  • Backend Docker container: taxi-backend-green / taxi-backend-blue (blue/green)
  • MinIO container: taxi-minio
  • Models registered in: app/models/__init__.py (imports + __all__)

Naming clash decision

Existing app/api/inspections.py + model CarInspection (table car_inspections) hold vendor-imported read-only data from NaughtySoft. Our new tables use the mechanic_ prefix to coexist:

  • mechanic_inspections (model MechanicInspection)
  • mechanic_inspection_photos (model MechanicInspectionPhoto)
  • mechanic_damage_markers (model MechanicDamageMarker)

Plan-vs-reality overrides applied in Phase B (Authoritative)

Three places where the original plan (docs/superpowers/plans/2026-05-16-premium-mechanic-phase1.md) was overridden during execution because Phase A1 recon showed the codebase doesn't match the plan's assumptions:

Plan said Actual implementation Why
backend/app/mechanic/router.py (subpackage) backend/app/api/mechanic.py (flat) TaxiDashboard uses flat app/api/*.py files; no subpackages
backend/app/mechanic/schemas.py (subpackage) backend/app/api/mechanic_schemas.py (flat sibling) Same convention
backend/app/mechanic/deps.py (subpackage) backend/app/api/mechanic_deps.py (flat sibling) Same convention
Router prefix /api/v1/mechanic Router prefix /v1/mechanic, with app.include_router(..., prefix="/api") in main.py Matches existing convention (inspections.py/v2/inspections + include with /api). Final URL is identical: /api/v1/mechanic/...
require_mechanic checks user.dept.lower() in (...) Queries UserDepartment ⨝ Department WHERE dept_type='svc' AND is_active, admin shortcuts before DB hit User has no dept attribute; departments are M2M via user_departments. DEPT_TYPES=['ops','svc','adm'], svc = service-type (мастерская)

When dispatching Phase CF implementers, copy this override map into their context so they don't follow the stale subpackage / dept paths from the plan.

Next task

Task C1 — GET /me endpoint

(From docs/superpowers/plans/2026-05-16-premium-mechanic-phase1.md Phase C.)

Working dir: c:\NewProject\taxi-dashboard (branch feat/mechanic-mvp, HEAD f0d7e83)

The endpoint itself is trivial:

@router.get("/me", response_model=MeOut)
async def me(user: User = Depends(require_mechanic)) -> MeOut:
    perms = ["mechanic:inspect"]
    if user.is_admin:
        perms.insert(0, "admin")
    return MeOut(
        id=user.id,
        name=user.full_name or user.username or f"user#{user.id}",
        email=user.email,
        permissions=perms,
    )

Add to backend/app/api/mechanic.py (the existing router from B1). Import require_mechanic from app.api.mechanic_deps and MeOut from app.api.mechanic_schemas.

⚠️ Open architecture decision before C1 (must resolve, not punt) — see "Phase C testing-infrastructure decision" section below. The plan's tests for C1 assume httpx.AsyncClient, pytest-asyncio, conftest.py with client + mechanic_user_token fixtures, and a real test DB. None of that exists. Don't let the C1 implementer freelance — decide first, then dispatch.

Phase C testing-infrastructure decision (UNRESOLVED — answer this before Task C1)

Current backend/tests/ contains zero fixtures: no conftest.py, no async client, no JWT minting, no test DB. Phases A and B got by with pure-unit and metadata tests. Phase C is the first phase that needs endpoint-level tests, which forces a choice:

Option Scope Pros Cons
A. Dependency-override mocks (recommended) ~30 lines: override get_current_user / require_mechanic / get_db on the FastAPI app, fake db.execute results in each test. Sync TestClient. Tiny, fast, no DB, matches the TDD London School preference in CLAUDE.md. Already proven in test_mechanic_deps.py. Won't catch real-DB query bugs; defers integration testing to Phase J e2e.
B. Real DB fixture infrastructure ~250400 lines: conftest.py with async SQLite (aiosqlite) or testcontainers Postgres, Base.metadata.create_all, seeded user+department+M2M, JWT mint helper, pytest-asyncio + httpx.AsyncClient. Catches real-query bugs, e2e-ish from day one. Big tangent, slows every PR, async fixture lifecycle is fiddly.
C. Hybrid Start with A; add a tiny "DB smoke" subset (Option B) only for endpoints with non-trivial query logic. Best of both, incrementally. Two paradigms to maintain.

Recommendation: A, with the understanding that Phase J adds end-to-end against a real running backend (the plan's existing Phase J e2e + smoke test tasks already cover the real-DB path).

If A is chosen, the controller's prompt for the C1 implementer should explicitly tell them: "use FastAPI TestClient (sync) + app.dependency_overrides[require_mechanic] to inject a fake user; don't add a conftest, don't add a real DB, don't introduce pytest-asyncio."

How to resume in a new session

In a new Claude Code chat in c:\NewProject\, paste this prompt:

Продолжаем проект Premium Mechanic PWA.

Прочитай PremiumDriverApp/docs/superpowers/STATE.md — там полное состояние и следующая задача.

Phase B полностью завершён (B1–B4, последний коммит f0d7e83). Следующая —
Task C1 (GET /me) из плана PremiumDriverApp/docs/superpowers/plans/2026-05-16-premium-mechanic-phase1.md,
НО сначала надо разрешить "Phase C testing-infrastructure decision" в STATE.md (Option A / B / C).

Используй superpowers:subagent-driven-development skill. Реши test-infra вопрос, потом
спавни первого implementer-subagent на C1 со специфической инструкцией про тестовый подход.

That prompt is the single entry point for the next session — Claude will read STATE.md, ground itself, resolve the test-infrastructure fork, and continue dispatching subagents from Task C1 onward.

Remaining roadmap (Phase 1 MVP)

  • Phase B (4 tasks) done — module skeleton, models, schemas, auth deps
  • Phase C (3 tasks): GET /me, GET /vehicles, GET /vehicles/{id}
  • Phase D (3 tasks): inspections CRUD with carry-over markers logic
  • Phase E (5 tasks): MinIO storage layer + presigned upload/confirm + 302 read + celery thumbnail
  • Phase F (2 tasks): markers create/update/delete
  • Phase G (5 tasks): Vite + React + Tailwind + shadcn + PWA scaffold
  • Phase H (3 tasks): Login + Home + VehicleCard pages
  • Phase I (6 tasks): CameraCapture + PhotoSlot + VehicleScheme + InspectionEditor + InspectionReview
  • Phase J (4 tasks): e2e test + deploy backend + deploy frontend + smoke test

≈ 31 tasks left until MVP live on mechanic.pptaxi.ru.

Known external dependencies

  • MinIO pp-inspections bucket — CORS already configured, presigned uploads will work
  • Caddy on 100.64.0.12 — fragment ready, will be wired during Phase J deploy
  • Docker blue/green deployment on VDS — backend re-deployment will need a touch of docker compose (Phase J)
  • Frontend deployment target: /opt/sites/mechanic-pwa/dist/ on VDS (Phase J)

Conventions reminder

  • All new code in feat/mechanic-mvp branches in both repos
  • All new tables use mechanic_ prefix
  • Don't touch existing app/api/inspections.py or app/models/inspection.py (vendor-imported)
  • All new mechanic source lives in flat files under backend/app/api/ with mechanic_* naming (mechanic.py, mechanic_schemas.py, mechanic_deps.py). No app/mechanic/ subpackage — see "Plan-vs-reality overrides" table above.
  • Router prefix is /v1/mechanic; /api is added by include_router(..., prefix="/api") in main.py. Final URLs always /api/v1/mechanic/*.
  • Phase C+ endpoint tests: pending the test-infrastructure decision (Option A / B / C above). Do NOT introduce pytest-asyncio, conftest.py, or a real DB without first resolving that decision.
  • require_mechanic allows admins + members of any active Department with dept_type='svc'. Use it as the default auth dep for all mechanic endpoints.