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

158 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `f0d7e83``backend/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`, `TimestampMixin``app.models.base`
- `get_db``app.db` (file: `app/db/__init__.py`)
- `get_current_user``app.auth.deps`
- `User``app.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:
```python
@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.