When PORT=0, the backend now writes its actual bound port to
data/test/.port. test-env.sh reads that file when TT_TEST_PORT=0
so all targets (test-up, test-reset, test-down) resolve the real URL.
test-up waits for .port to appear before the health-check loop.
Instead of applying a per-element heuristic (skip if value ≤ 50), identify
pixel-scale rooms at the row level with WHERE EXISTS, then convert all
elements unconditionally. Eliminates the risk of mixed-scale elements within
the same room.
- Makefile: add SHELL := /bin/bash so test-env.sh pipefail works in CI
- RoomCanvas: fix onElementClick firing on drag start (now fires on mouseup
for click-in-place only); fix Props type to accept null; guard grid pattern
against snapStep=0 (invalid SVG); remove unsafe null cast
- live/[slotId]: fix studentNamesBySeat $derived wrapping a function instead
of a value — reactivity was broken, map never updated
- s/[code]: block clicks on occupied seats before hitting the backend;
pass occupiedSeatIds to confirmed-view RoomCanvas; clear errorMsg on retry
- rooms/+page: replace alert() in deleteRoom with inline errorMsg state
- rooms/[roomId]: replace deprecated .substr with .slice
- courses.rs: assign_tutor uses fetch_optional → 404 on unknown tutor_id
instead of propagating RowNotFound as 500
- rooms.rs: delete_room returns 404 when room does not exist; replace
fract() != 0.0 float check with epsilon-based validation
- auth_routes.rs: refresh endpoint re-checks is_active so deactivated tutors
cannot obtain new access tokens; fix test INSERT to include is_active
- tutors.rs: wrap delete_tutor reference checks and DELETE in a transaction
- attendance.rs: replace #[allow(clippy::type_complexity)] with type alias
- migrations/003: document > 50 heuristic precondition
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- migration 003: apply pixel→grid transform per-element (CASE WHEN > 50)
instead of per-row, preventing double-conversion of mixed-scale rooms;
skip empty arrays via json_array_length guard to avoid NULL assignment
- attendance.rs: log layout JSON parse errors instead of silently
swallowing them with .ok()
- tutors.rs: check rows_affected() in set_tutor_active and return 404
for non-existent IDs; remap FK constraint errors on delete to 409
so concurrent inserts between conflict-check and DELETE don't surface
as 500
Frontend:
- live/[slotId]: expose polling failures to the tutor via error banner
instead of only console.error
- s/[code]: split checkin into two try/catch blocks so a successful
POST followed by a failed reload doesn't report failure to the student;
fix dead '409' string detection to match actual server error 'seat taken'
- rooms/[roomId]: remove duplicate onMount fetch; add .catch() to $effect
- tutors: expose loadTutors failures via error banner, not just console
- rooms: fix bare catch in createRoom (captures error, shows message);
add try/catch to onMount rooms load
CI:
- sync cargo audit --ignore RUSTSEC-2023-0071 with Makefile; the advisory
is in rsa which sqlx-mysql retains in the lock file even when the mysql
feature is disabled — aws_lc_rs correctly removes it from the active tree
The admin layout guard rendered only a "Redirecting to login..." placeholder
for the /admin/login child route, trapping every unauthenticated visitor.
Exempt the login route from the auth gate so the form renders correctly.
Also wire the new POST /api/auth/refresh endpoint (from the dual-token
migration) into both auth.init() and the api request() 401 handler, so
sessions survive the 15-minute access-token lifetime without a hard logout.
Adds a Playwright regression test asserting the login form is visible
in a clean (no-cookie) browser context.
- Security: Add Secure flag to checkin identity cookie, implement rate limiting on login, and harden Helm security context.
- Security: Add cargo-audit to CI and Release pipelines for dependency vulnerability scanning.
- Backend: Enable SQLite WAL mode and fix AppState initialization in tests.
- Frontend: Fully type the API client, fix importStudents FormData handling, and pin dependency versions.
- Frontend: Add auto-logout on 401 and resolve authentication initialization race conditions.
- CI/CD: Pin pnpm version in release workflow and include lint/audit quality gates.
- Dockerfile: Update binary name from attendance to tutortool to fix the release build pipeline failure.
- Backend: Expose test_mode in AppState to conditionally disable the secure flag on auth cookies during local E2E testing over HTTP.
- Backend: Enable tower-http trace feature and attach TraceLayer for improved request logging.
- Frontend: Refactor auth.svelte.ts to a plain reactive object to resolve initialization race conditions during tests.
- Frontend: Append cache-busting timestamp to /api/auth/me to prevent stale session states.
- Frontend: Update Playwright locator in superadmin.spec.ts for greater resilience.
- Makefile: Inject required environment variables (STATIC_DIR, JWT_SECRET) into the test-up target.
- Switched to secure httpOnly, SameSite=Strict cookies for JWT authentication.
- Refactored backend to use AppState for shared secrets and database pool caching.
- Modernized frontend with Svelte 5 runes ($state) and removed localStorage reliance.
- Gated destructive test endpoints behind debug_assertions and fixed unsafe test patterns.
- Enhanced CI pipeline with cargo clippy, cargo fmt, and pinned pnpm version.
- Updated documentation and implementation plans to match the hardened architecture.
- Add test-* and seed-demo Make targets to command reference
- Document TT_TEST_MODE, /health route, test_reset route module
- Expand admin subroutes list, add Testing and CI sections
- Fix SQLx contradiction (runtime queries, no DATABASE_URL needed)
- Rewrite GEMINI.md with full Claude parity
- Add root README.md with quickstart, stack, and doc links