Commit Graph

3 Commits

Author SHA1 Message Date
jacobh2 4fa93c4985 Write a front door for the repository
There was no README at the root, and the backend's own still pointed at an
onboarding guide that had been deleted. The new one says what the app is, how
to run it, and where the code lives, and states the test gates as they
actually are rather than as an older note claimed.

It records the two things that waste a newcomer's afternoon: the dev server
answers on localhost but not on the loopback address, because Vite binds IPv6
first, and the two design documents are largely superseded and say so inline.
2026-09-18 00:56:11 +02:00
jacobh2 249e277696 Fix flexible-wish double-booking, extend calendar cache to 2055, add journey tests
Second real bug found by driving the running app while the suites were green.

Flexible wishes were funded twice across propose -> apply -> re-propose. Because
planned_leave_day carries no wish_id, the engine could not tell that applied days
were that wish's own prior occurrence; it treated them as occupied space, routed
the candidate search around them, and funded the year again on shifted dates while
reporting weeks_funded 1.0. Applying twice turned "one week each spring" into 18
paid days instead of 9. Fixed wishes were unaffected and stay unaffected.

Fix (allocation_service.py), per the approved approach of counting already-applied
days inside the bounding range rather than adding a wish_id column:
- _Ledger._persisted_lookup snapshots existing days BEFORE this run folds in its
  own freshly-computed windows, so a fixed wish's new placement is never mistaken
  for a flexible wish's history
- _reuse_already_funded_window finds this wish's persisted dates within its bound
  and reuses the window containing them, via the ordinary fund_window path
- full funding therefore yields zero new days and partial funding tops up exactly
  the remainder, with no duplication and no false shortfall
- with no persisted dates, or if the reused window is no longer collision-free, it
  falls through to the original search, so an unsatisfied year is never frozen

Calendar cache extended 2026-2035 -> 2026-2055 (39KB -> 116KB) and the allocator
switched to strict, so an uncached year raises UncachedYearError -> ValidationError
-> HTTP 422 instead of silently treating Julafton, Nyarsafton and Midsommarafton as
working days. Also fixed normalize_day dropping the second name when two holidays
share a date (Nationaldagen was being lost). 2049 and 2055 legitimately have 17 red
days, not 18: Pingstdagen coincides with Nationaldagen.

New backend/tests/integration/test_user_journey.py drives the real app over HTTP
across seven journeys: the double-booking guard, the fixed-wish control, apply
idempotency, fixed-never-displaced-by-flexible, flexible wishes tracking Easter
per year, age-4 arithmetic self-consistency, and a recursive check that no Swedish
text reaches the wire now that everything is message_key + data.

New backend/scripts/seed_demo.py seeds a realistic workspace through the real
service layer for manual testing: demo@example.com / DemoPass123!

Backend 414 -> 435 tests. Frontend unchanged at 705.

Still not verified in a browser.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RXMhc9nT6sNz1SL4j8dznL
2026-09-14 07:38:07 +02:00
jacobh2 53d157535a Initial commit 2026-06-01 16:48:21 +02:00