118 lines
7.1 KiB
Markdown
118 lines
7.1 KiB
Markdown
# 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.
|