The in-session progress bar reads session.cardsShown/cardsCorrect/cardsIncorrect,
but the session store's answer() never refreshed the session object — the backend
tracked the counters but the client kept the stale start values (all 0), so the bar
appeared frozen. answer() now mirrors the backend's increment locally; end() still
replaces with authoritative server totals. Adds an E2E regression test.
Two bugs surfaced by Excel import on a lesson with a lesson_path column:
1. resolveLesson created lessons without owner_id, so after the ownership
model (sub-project B) they never appeared in getLessonTree — import
reported success but nothing was visible.
2. lesson_path was resolved at the root; cards landed in new root lessons
instead of under the lesson the import was started from.
Now: auto-created lessons get ownerId + visibility 'private'; lesson_path is
resolved relative to the started lesson (each segment a sublesson). Also drop
the stale eager card_progress insert (progress is per-user and lazy since B).
Add e2e/ux.spec.ts covering the ⌘K search palette, lesson detail page,
stats sections, and the legacy /admin → /lessons redirect.
Also fixes two issues uncovered by running the full suite:
- Skip auth rate limiter in e2e by running the backend with NODE_ENV=test
(registration limit of 5 was tripping later tests).
- Render the card table for lesson owners even when the lesson has zero
cards, so the first card can be added from the detail page.
- /api/stats: add verifyCsrf middleware (defense-in-depth; no-op for GETs)
- VerifyEmailPage: useRef guard to prevent React StrictMode double-fire of
the single-use verify token in dev
- router.tsx: route-level code splitting via React.lazy + Suspense; initial
bundle drops from 397 KB to 224 KB with per-route chunks (0.3–14 KB each)
- e2e: wait for verify-email completion before login; bump Account-menu
timeout to handle Vite cold-chunk compile