Files
crr-shifts/AGENTS.md
T

5.6 KiB

AGENTS.md

Mission

Maintain a deliberately small, self-hosted rota application for a non-profit bar/club. Prefer clarity and operational simplicity over enterprise HR features.

Domain invariants

  1. Assignments are the source of truth. Recurrence/rotation rules only create assignments that do not already exist.
  2. There are three seeded schedules:
    • night: fixed/manual, every day of the month.
    • saturday-lunch: rotating, Saturdays only.
    • sunday-lunch: rotating, Sundays only.
  3. Saturday and Sunday rotations are independent, each with its own configuration page (/admin/schedules/<id>); the night schedule is fixed and repeats every month with a single configuration. The /admin/schedules and /schedules pages are hubs that link to one page per schedule.
  4. Never silently rewrite an existing assignment when rotation membership/order changes.
  5. A swap/change request only applies if the requester still owns the assignment when it is accepted/approved.
  6. email and phone are optional. username/password are optional as a pair: a person either signs in or is rota-only. A username, when set, is unique.
  7. Email failure must never roll back or block a rota change. Log failures in notification_logs.
  8. Past assignments cannot be offered for change through the member UI.

Stack

  • Python 3.12+
  • FastAPI + server-rendered Jinja2
  • SQLAlchemy 2.x
  • SQLite by default
  • No frontend build step or JavaScript framework
  • Docker Compose for self-hosting
  • Docker Compose test profile for running tests independently of the host Python environment

Important files

  • app/models.py — database schema.
  • app/scheduling.py — recurrence and assignment generation. Treat changes here as high-risk.
  • app/web.py — HTTP routes and workflows.
  • app/notifications.py — SMTP delivery; should fail soft.
  • app/bootstrap.py — initial admin + three default schedules.
  • app/cli.py — database/demo/reminder commands.
  • docs/DOMAIN.md — expected business behavior.

Working rules for agents

  • Read docs/DOMAIN.md before changing scheduling behavior.
  • Add/update tests for every scheduling or swap-state change.
  • Keep existing generated/published assignments stable unless an admin explicitly edits them.
  • Do not add payroll, time clocks, attendance, leave management, or HR modules unless requested.
  • Keep phone as data-only until SMS/WhatsApp is explicitly implemented.
  • Do not commit .env, database files, credentials, or real member data.
  • Do not introduce a frontend package manager unless the UI requirements truly need it.
  • Schema changes go through Alembic. Edit app/models.py, then make revision m="short description", review the generated file in migrations/versions/, and never rely on create_all (the app runs alembic upgrade head on start). make check fails if the migrations and the models drift apart.

Validation before handoff

All checks run inside Docker, never on the host:

make check

That is python -m compileall -q app && pytest -ra in the checks service of docker-compose.dev.yml, which bind-mounts app/, tests/ and pyproject.toml so it always validates the working tree.

make dev (host uvicorn with auto-reload, using ./.venv) exists for interactive debugging. Running pytest, python -m compileall or python -m app.cli directly against the host interpreter is not a valid check.

The dev target of the Dockerfile installs requirements-dev.txt on top of the app image; the checks service supplies throwaway settings (SECRET_KEY, DATABASE_URL, INITIAL_ADMIN_PASSWORD), keeps data/ inside the container, and does not read .env, so it is safe on a fresh clone.

docker-compose.yml is the service stack only. Anything development-related (auto-reload, bind mounts, test runner) belongs in docker-compose.dev.yml.

For route/UI changes also start the app and verify /health, /login, the rota, each schedule's configuration page, and the swap workflow.

Security notes

  • Passwords use PBKDF2-SHA256 from Python stdlib.
  • State-changing forms use a session CSRF token.
  • Admin routes must call _require_admin.
  • Treat names, email addresses, phone numbers and schedules as private member data.
  • Change the default SECRET_KEY and initial admin password before deployment.

Branding and UI

  • The CRR logo is the user-supplied asset at app/static/logo-crr.png. Keep its aspect ratio and do not redraw, recolor, crop, or replace it unless explicitly requested.
  • CRR brand red is #ED1C24 and is exposed as --brand in app/static/app.css.
  • For controls with white text, use the darker accessible red --brand-deep (#C81018), not the exact brand red. The exact brand red is used for borders, focus states, highlights, and other identity accents.
  • Keep branding usable on mobile and preserve visible keyboard focus states.
  • Default application name is Contínuos CRR; deployments can still override it with APP_NAME.

Language

  • Everything a member reads is European Portuguese: templates, flash messages, e-mail subjects and bodies, the seeded schedule names, and the lang="pt-PT" document attribute.
  • Weekday and month names, and every rendered date, come from app/i18n.py. Never use calendar.month_name, date.strftime("%a"/"%b"/"%A"/"%B") or calendar.day_name for user-facing text; the container locale is not Portuguese.
  • Stored enum values (assignment.source, swap.status, notification_log.status/event_type) stay English in the database and are translated for display by the Jinja filters registered in app/main.py.
  • Code, comments, tests names and docs/ stay in English; only rendered strings are Portuguese.