Files
F-r-ldraledigplannerare/SENIOR_REPORT.md
T
2026-06-01 16:48:21 +02:00

118 lines
7.1 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.
# Senior Developer Report
**Project:** Föräldraledigplannerare
**Sessions:** 1 (scaffolding) + 2 (this session — stabilisation & handoff)
**Date:** 2026-06-01
**Status:** ✅ Junior handoff gate cleared
---
## Session 2 — What Was Done
### 1. Fixed FK constraint failures (§2a in handoff doc)
**Problem:** All `test_vacation_days_repository.py` tests and all `test_analytics_service.py` tests failed with `sqlite3.IntegrityError: FOREIGN KEY constraint failed`. The test helpers (`_make()`) generated random UUIDs for `workspace_id` and `personal_data_uuid` that had no corresponding rows in the DB, but the full schema enforces those as FK constraints.
**Fix:**
- Added a `SeededIds` dataclass and `seeded_ids` fixture to `tests/conftest.py`. The fixture pre-creates one `workspace` + one `personal_data` row and yields their IDs so any test that inserts rows with FK constraints can pass real parent IDs.
- Updated `test_vacation_days_repository.py`: `_make()` now accepts `workspace_id` / `personal_data_uuid` kwargs; all tests that call `create()` pass `seeded_ids`.
- Updated `test_analytics_service.py`: replaced `ws = uuid4()` / `pd_uuid = uuid4()` with `seeded_ids.workspace_id` / `seeded_ids.personal_data_uuid` in all four `TestAge4Projection` tests.
### 2. Created `BreakPeriodService`
**Problem:** `dependencies.py` referenced `BreakPeriodService` (causing three `F821 Undefined name` ruff errors), but `services/break_period_service.py` did not exist.
**Fix:** Created `app/services/break_period_service.py` with `create`, `get`, `list_for_workspace`, `update`, and `delete` methods, following the `VacationDaysService` pattern. Added the import to `dependencies.py`.
### 3. Fixed ruff lint & format
- `ruff check --fix .` auto-fixed 6 unused imports across `analytics_service.py`, `auth_service.py`, `fetch_data_service.py`, `tips_service.py`, and `routers/fetch_data.py`.
- `ruff format .` reformatted 37 files.
- Both `ruff check .` and `ruff format --check .` now pass clean.
### 4. Expanded analytics service test coverage
Added 6 new tests to push `analytics_service.py` from 44% → 100%:
| Test | Lines covered |
|---|---|
| `test_child_not_found_raises` (in `TestAge4Projection`) | L76 — `NotFoundError` path in `get_days_left_at_age_4` |
| `TestValidateDubbeldagar.test_child_not_found_raises` | L154–156 — `NotFoundError` path in `validate_dubbeldagar` |
| `TestValidateDubbeldagar.test_outside_window_raises` | L152–164 — outside-15-month window guard |
| `TestValidateDubbeldagar.test_quota_exceeded_raises` | L166–174 — max dubbeldagar quota guard (inserts 60 rows) |
| `TestGetOverview.test_overview_single_parent` | L123–136 — happy path of `get_overview` |
| `TestGetOverview.test_overview_no_parents_raises` | L124–125 — `NotFoundError` in `get_overview` |
### 5. Updated JUNIOR_ONBOARDING.md
- Updated the "Foundation status" block to reflect Session 2 completion.
- Corrected Task 1 (BreakPeriod): was "build everything"; corrected to "verify + write tests" since all backend layers now exist.
- Added ⚠️ note about `npm install` / `package-lock.json`.
---
## Final State After Session 2
| Check | Result |
|---|---|
| `uv run pytest` | ✅ 29 passed, 0 failed |
| `uv run ruff check .` | ✅ All checks passed |
| `uv run ruff format --check .` | ✅ 77 files already formatted |
| `analytics_service.py` coverage | ✅ 100% |
| `auth_service.py` coverage | ✅ 90% |
| `vacation_days_service.py` coverage | ✅ 92% |
| `vacation_days_repository` (SQLite) coverage | ✅ 100% |
| Junior handoff gate | ✅ Cleared (except `npm install` below) |
---
## What Remains
### One-off manual step (< 1 min, not junior-blocking)
| Task | Who | Notes |
|---|---|---|
| `cd frontend && npm install` → commit `package-lock.json` | **You (or anyone)** | Needed for `npm ci` in CI. Does not block local dev. Network-blocked inside Claude Code sessions. |
---
### Junior tasks (ordered by dependency)
These were unblocked by this session. Full descriptions in `JUNIOR_ONBOARDING.md`.
| # | Task | Files to create | Blocks |
|---|---|---|---|
| 1 | **BreakPeriod tests** | `tests/unit/test_break_period_repository.py` | Nothing (standalone) |
| 2 | **Child CRUD** | `services/child_service.py`, `models/child.py`, add routes to `routers/settings.py` | Task 3 |
| 3 | **PlannedLeaveDay CRUD + PlanningService** | `services/planning_service.py`, `models/planned_leave.py`, `routers/planned_days.py` | Requires Child (Task 2) |
| 4 | **PersonalData settings endpoint** | Thin service method + `PUT /api/settings/parent/{uuid}` | Nothing |
| 5 | **Frontend hooks** | `useParentalLeave.ts`, `usePlannedDays.ts`, `useAnalytics.ts`, `useOptimizationTips.ts` + matching `*.test.tsx` | Can run in parallel once backend is green |
| 6 | **Wire tips rules** | `services/tips_service.py` (9 stub rules) | Can be done incrementally, each rule is self-contained |
| 7 | **Date helpers** | `app/utils/dates.py` + `frontend/src/utils/dates.ts` | Needed by planning + calendar UI |
| 8 | **Swedish public-holiday helper** | Extend `app/utils/` | Needed by red-day tip and calendar UI |
| 9 | **Presentational frontend components** | `components/calendar/`, `components/analytics/`, `components/settings/`, etc. | Can start immediately |
---
### Senior-owned tasks (still open)
These were explicitly deferred and must not be picked up by the junior.
| Task | Location | Status | Notes |
|---|---|---|---|
| **FK scraper + BankID** | `scrapers/fk_scraper.py` | `NotImplementedError` stub | Legal/ToS gate must clear first (see `project_legal_gate.md`) |
| **Postgres repository impls** | `repositories_impl/postgres/` | Only `vacation_days` skeleton exists | Copy SQLite impls; replace `?` params with `$1, $2` (asyncpg style) |
| **`scraper_enabled` guard** in fetch router | `routers/fetch_data.py` | Guard not yet wired | Add `if not settings.scraper_enabled: raise 501` once scraper is real |
| **`TipsContext.workspace_planning_goal`** | `services/tips_service.py` | Hardcoded `"balanced"` | Inject `WorkspaceRepository` into `TipsService` or pass `planning_goal` into `get_tips()` |
| **`BreakPeriodUpdate` required fields** | `models/break_period.py` | All fields required (not Optional) | Either make fields Optional (partial update) or document full-replacement semantics |
---
## Sharp Edges to Pass On
Noted in `senior_skaffolding_1.md §4`; still unresolved:
- **`BreakPeriodUpdate` required fields** — The model uses required fields, so every PUT must supply all fields. Fine for now, but the junior's BreakPeriod tests will need to account for this.
- **`TipsContext.workspace_planning_goal` hardcoded** — `TipsService._build_context` always returns `"balanced"`. The 3 goal-specific rules (`skip_fp_first_12_months`, `wrap_weekend_with_vacation`, `wrap_red_days_with_vacation`) will never be filtered correctly until `WorkspaceRepository` is injected.
- **CI coverage gate** — The CI runs `--cov-fail-under=90` against all of `app/`. Total coverage is currently ~37% (all the router/service stubs are untouched). The gate will fail until the junior has written enough tests. Consider temporarily lowering the threshold in `ci.yml` or adding a `[tool.coverage.run] omit` list for stub/skeleton files.