# AGENTS.md

Family chore/reward manager. Flask + TinyDB backend (`backend/`), Vue 3 + TypeScript frontend (`frontend/`). Real-time updates over SSE.

## Commands

### Backend (run from `backend/`)
- Activate venv: `source .venv/bin/activate`
- Dev server: `python -m flask run --host=0.0.0.0 --port=5000` (entry: `main.py`)
- Required env vars: `SECRET_KEY`, `REFRESH_TOKEN_EXPIRY_DAYS`, `DIGEST_TOKEN_SECRET`, `VAPID_PUBLIC_KEY`, `VAPID_PRIVATE_KEY` — Flask raises `RuntimeError` on boot if any are missing
- Optional persistence switch: `USE_MONGODB` (`true` | `false`). Defaults to `true`; set `MONGO_URI` (and optionally `MONGO_DB_NAME`). Set to `false` to use TinyDB instead.
- Optional: `DB_ENV` / `DATA_ENV` (`prod` | `test` | `e2e`) — picks `data/` vs `test_data/` dir (see `config/paths.py`). For MongoDB these also select the default database name (`chore_db`, `chore_db_test`, `chore_db_e2e`) unless `MONGO_DB_NAME` is set.
- Tests: `pytest tests/` — `conftest.py` forces `DB_ENV=test`, `USE_MONGODB=true`, `MONGO_URI=mongomock`, and sets dummy secrets. Single test: `pytest tests/test_routine_api.py::test_name`
- Python imports assume `backend/` is on `sys.path` (set by `conftest.py` / `flask run` cwd). Run pytest from `backend/`.
- Create admin user: `python scripts/create_admin.py` (admin role cannot be set via signup)

### Frontend (run from `frontend/`)
- Dev: `npm run dev` (Vite, https://localhost:5173)
- Lint: `npm run lint`
- Type-check: `npm run type-check`
- Unit tests: `npm run test:unit` (Vitest). Single: `npx vitest run path/to/file.spec.ts`
- E2E: `npx playwright test` — config auto-starts both `npm run dev` and the Flask backend with `DB_ENV=e2e DATA_ENV=e2e USE_MONGODB=true MONGO_URI=mongomock`. Tests live in `e2e/`. `frontend/.env.test` contains the example MongoDB config.
- E2E buckets are Playwright projects (see `playwright.config.ts`) targeting directories under `e2e/mode_parent/`

## Architecture

### API routing — the `/api` prefix
- Frontend nginx (and Vite dev proxy) strips `/api` before forwarding. **Backend routes must NOT include `/api`.** Backend defines `@app.route('/user')`, frontend calls `/api/user`.
- `auth_api` is the only blueprint registered with a prefix: `url_prefix='/auth'` in `main.py:67`.
- API errors return `{ error, code }` (codes in `backend/api/error_codes.py`). Frontend extracts them via `parseErrorResponse(res)` in `src/common/api.ts`.

### Models — strict 1:1 parity
- Python `@dataclass`es in `backend/models/`. TypeScript interfaces in `frontend/src/common/models.ts`. Any model change requires updating both.
- Persistence is MongoDB by default (`USE_MONGODB=true`), or TinyDB when `USE_MONGODB=false`. Both are accessed through the `LockedTable` / `MongoLockedTable` wrappers in `backend/db/db.py`. Operate on model instances with `from_dict()` / `to_dict()` — never raw dicts.
- MongoDB client initialization is lazy (`backend/db/mongo_client.py`). `backend/gunicorn.conf.py` provides the `post_fork` hook required for multi-worker Gunicorn deployments; `backend/Dockerfile` loads it with `-c gunicorn.conf.py`.
- Migration script: `cd backend && python -m scripts/migrate_to_mongodb [--dry-run]`. It reads TinyDB JSON files and writes them to MongoDB idempotently, backing up the originals to `<db_dir>/backups/<timestamp>/`.

### SSE event bus — mandatory for every mutation
- Every backend mutation (add/edit/delete/trigger) **must** call `send_event_for_current_user` from `api/utils.py`. Event types in `backend/events/types/` are mirrored in `frontend/src/common/backendEvents.ts`.
- Frontend: register listeners in `onMounted`, clean up in `onUnmounted`. SSE endpoint is `/events`.

### Background schedulers (started in `main.py` at boot)
- `start_deletion_scheduler` — runs hourly, deletes accounts marked for deletion after threshold
- `start_digest_scheduler` — email digests
- `start_state_expiry_scheduler` — expires stale state
- `start_chore_expiry_notification_scheduler` — chore expiry notifications

## Frontend conventions
- SFC file order: `<template>` → `<script>` → `<style scoped>`. TypeScript only in `<script>`. All styles must be `scoped`.
- Colors/spacing: use only `:root` CSS variables from `colors.css`. No hardcoded hex/px for themed properties.
- Layout shells: `ParentLayout` for admin/management, `ChildLayout` for child dashboard/focus.
- Images: models carry `image_id`; frontend resolves to `image_url` for rendering.

## Testing gotchas
- E2E tests use pre-authenticated sessions via `storageState` in `playwright.config.ts` — do **not** navigate to `/auth/login`. Import `E2E_EMAIL` / `E2E_PASSWORD` from `e2e/e2e-constants.ts`.
- E2E buckets that mutate shared state (default tasks, delete-account, create-child) use isolated users. Preserve this pattern when adding new buckets.
- Backend tests: `conftest.py` sets `DB_ENV=test` + dummy secrets. Test DB lands in `test_data/db/`, never touches production `data/`.

## Feature specs
Specs live in `.github/specs/`. If a spec has a checklist, all items must be marked done before the feature is complete.
