diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 00000000..1bb75e78
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1 @@
+Refer to `/AGENTS.md` for all coding rules, conventions, and restrictions.
\ No newline at end of file
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 00000000..04613027
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,117 @@
+# AGENTS.md
+
+## Role & Scope
+
+- You are an automated coding assistant contributing to https://github.com/schmave/demschooltools.
+- This document defines the rules you must follow when manipulating this codebase. Treat every instruction below as mandatory guidance for how to design, code, test, and safeguard changes.
+
+## Technology Directives
+
+| Area | Rule | Implementation hints |
+|----------|---------------------------------------------------------------------------------------------------------|----------------------|
+| Backend | You must implement all backend work with Django under `django/`. New apps live beside `custodia` and `dst`, are wired into `django/demschooltools/settings.py`, and exposed through `django/demschooltools/urls.py`. | Use `uv run manage.py startapp`, register the app, add URLs/migrations/tests in the same subtree. |
+| Frontend | You must implement all frontend work with React under `react/` using the existing Vite build (see `react/vite.config.js`). Update the manifest entries and Django templates (`django/dst/templates/*.html`) to load new bundles via `django-vite`. | Run `npm run watch` during development and `vite build` only via CI/deploy scripts. |
+| Legacy | The historical Play/Scala + JVM stack under `app/`, `conf/`, `project/`, `target/`, `authLibrary/`, `modelsLibrary/`, `lib/`, `node_modules/`, and related webpack config in the repo root (`/package.json`) is frozen. Do **not** modify or expand it without explicit human approval. |
+
+## Current Django Architecture
+
+- **Project layout (`django/demschooltools/`)**: central settings (`settings.py`), URLs (`urls.py`), middleware (`middleware.py`), custom auth backend/middleware (`auth.py`), templates (`templates/`), and storage logic coordinate the Django runtime. Settings load secrets from `.env` via `dotenv`, target PostgreSQL (`school_crm`), enable `django_vite`, and configure DRF to use ORJSON renderers/parsers plus Rollbar exception handling.
+- **Apps**:
+ - `django/dst/` contains the modern school-management surface: models for organizations, people, roles, manual chapters, attendance, etc. (`models.py`), multiple views (`attendance_views.py`, `manual_views.py`), reusable HTTP helpers (`utils.py` with `DstHttpRequest` and `render_main_template`), PDF generators (`pdf_utils.py` using Playwright), organization-level configuration (`org_config.py`), templates (`templates/`), and management commands (`management/commands/*.py`) used for seeding, reporting, and manual copying.
+ - `django/custodia/` bridges legacy attendance tooling: DRF-style API views (`views.py`) expose `/custodia-api/*`, while server-rendered templates plus React bundles (see `django/dst/templates/sign_in_sheet.html`) back the UI. Models live in `custodia/models.py`, with migrations tracking schema history.
+- **Views & templates**:
+ - Django class-based views rely on `LoginRequiredMixin` and `request.org` (set by `PlaySessionMiddleware`) to scope queries to the current school. Manual-management views under `dst/manual_views.py` re-use `render_main_template` to inject menus and `org_config`.
+ - DRF `APIView`s in `custodia/views.py` enforce permissions via `RequireAdmin` and raise built-in exceptions so the Rollbar handler captures issues.
+ - Templates live beside each app (`django/dst/templates/`, `django/custodia/templates/`) and expect React entry points to hydrate a `
` while `window.initialData` prepopulates JSON payloads.
+- **Utilities & services**: Keep cross-cutting helpers centralized (`dst/utils.py`, `dst/org_config.py`, `dst/pdf_utils.py`, `demschooltools/form_renderer.py`). Extend those modules rather than scattering ad-hoc helpers.
+- **Serializers**: The current code manually shapes dicts in the views; when adding new APIs prefer creating explicit serializers (e.g., `django/dst/serializers.py`) so data contracts are testable and ORJSON-friendly.
+- **Migrations/tests**: App-specific migrations live under `django/dst/migrations/` and `django/custodia/migrations/`. Even though `django/dst/tests.py` is minimal, all new backend logic must be backed by Django `TestCase`s in each app’s `tests.py` or a dedicated `tests/` package.
+
+## Current React Architecture
+
+- **Bundles & routing**:
+ - The modern bundle is rooted at `react/index.jsx` and `react/App.jsx`. It uses `createBrowserRouter` to map Django routes (e.g., `/attendance/signInSheet`) to React pages. Templates such as `django/dst/templates/sign_in_sheet.html` mount this bundle and pass bootstrapped data through `window.initialData`.
+ - `react/custodia/js/*` houses the legacy HashRouter-based React app loaded inside `/custodia/*` pages; extend it only when you are explicitly working on Custodia screens.
+ - The Vite config (`react/vite.config.js`) defines three named entry points (`custodia`, `custodia_css`, `reactapp`) that emit hashed assets under `django/static-vite/`. Whenever you add a new bundle, add it to `rollupOptions.input`, regenerate the manifest, and update the corresponding Django template.
+- **Components & state**:
+ - Shared UI components wrap MUI primitives (`react/components/*.jsx`) and are re-exported through `react/components/index.js`. Use these wrappers instead of raw `@mui/material` imports so styling stays consistent.
+ - Containers in `react/containers/` (e.g., `DeleteDialog`) encapsulate modal flows, while `react/contexts/` exposes React Contexts such as `SnackbarContext` used throughout `react/pages/SignInSheetPage/SignInSheetPage.jsx`.
+ - Pages (currently `SignInSheetPage`) live under `react/pages/*`, own their state via hooks (`useState`, `useMemo`, `useCallback`, `useEffect`), and depend on utilities from `react/utils/` (safe parsing, option normalization, formatting) plus `@react-pdf/renderer` to generate PDFs.
+- **Styling & theming**: Global SCSS imports live in `react/index.scss`. Theme definitions (`react/theme/theme.js`) create responsive light/dark palettes with MUI. When introducing new surfaces, wrap them with `ThemeProvider` and `PageWrapper` so typography and padding stay uniform.
+- **Testing**: No automated frontend tests currently exist; you must introduce them alongside new features (prefer Vitest + React Testing Library) and wire the runner into `package.json` before relying on it. Until then, document manual verification steps.
+- **Lint/format**: ESLint is configured via `react/eslint.config.js` (React + Hooks + import rules) and Prettier (with `@trivago/prettier-plugin-sort-imports`) enforces formatting. Run `npm run lint` from `react/` and fix all warnings before submission.
+
+## Database Rules
+
+- **Canonical schema**: The authoritative ORM models are `django/dst/models.py` (core DST tables mapped onto existing `public.*` tables via `db_table` overrides) and `django/custodia/models.py` for swipe/attendance supplements. These models intentionally mirror legacy Play-era schemas; do not “clean up” column names or relationships.
+- **Migrations workflow**:
+ - Generate changes with `uv run manage.py makemigrations
` and review the generated files under `django//migrations/`.
+ - Apply them locally with `uv run manage.py migrate`. The helper scripts `django/test_initial_migrate.sh` and `django/run_django.sh` show the expected bootstrap flow (provision `school_crm`, apply migrations, run `setup_initial_data` via `uv`).
+ - When comparing against production data, dump schemas with `pg_dump -O --schema-only school_crm > ` as demonstrated in `django/test_initial_migrate.sh`.
+- **High-risk tables**: Many models map to shared legacy tables (`public"."users`, `public"."user_role`, `public"."linked_account`, `tag`, etc.). Custodia tables rely on uniqueness constraints (e.g., `custodia/migrations/0003_swipe_person_swipe_day_empty_out_unique.py`).
+- **Schema DO / DO NOT**:
+
+| DO | DO NOT |
+|----|--------|
+| Use Django ORM field definitions and migrations to evolve the schema. | Do **not** touch SQL files such as `fix_case_numbers.sql` or run ad-hoc DDL unless a human explicitly instructs you. |
+| Coordinate cross-app migrations when tables span both `custodia` and `dst`. | Do **not** rename or drop columns/constraints on legacy tables (`public.users`, `user_role`, etc.); they are still consumed by the Play/Scala stack. |
+| Respect partial indexes and unique constraints already defined in migrations (e.g., swipe uniqueness, overrides). | Do **not** perform schema migrations that drop or truncate data without written human approval. |
+| Seed or adjust reference data through management commands (`django/dst/management/commands/*.py`) when possible. | Do **not** bypass Django migrations with raw SQL or external tools. |
+
+## Auth, Permissions & API Expectations
+
+- Authentication flows are defined in `django/demschooltools/auth.py`. `PlaySessionMiddleware` inspects the `PLAY_SESSION` JWT cookie (or falls back to allowed IP addresses via `AllowedIp`) to populate `request.user` and `request.org`. You must preserve this flow—never implement alternative login states.
+- Every view must trust but verify `request.org`: filter ORM queries by `organization=request.org` to prevent cross-school data leaks. Example: `custodia/views.get_person` enforces this before mutating person data.
+- Authorization hinges on `dst.models.UserRole` and `User.hasRole`. Use explicit permission checks (`LoginRequiredMixin`, `UserRole` assertions, or DRF `BasePermission` subclasses like `RequireAdmin` in `custodia/views.py`) before touching attendance/manual data.
+- API endpoints live in Django apps:
+ - HTML views render through `render_main_template` (see `dst/utils.py`) with the React mountpoint and `window.initialData` payloads.
+ - JSON endpoints use DRF `APIView`s or `ViewSet`s, returning `Response` objects compatible with ORJSON. Register them under `/custodia-api/*` or other namespaces via `django/demschooltools/urls.py`.
+- Example pattern for a new endpoint:
+ 1. Add a view to the relevant app (e.g., `django/dst/views.py`) that subclasses `APIView`, reads `request.org`, enforces `UserRole` granularity, and serializes data with a serializer or helper.
+ 2. Wire the URL in `django/demschooltools/urls.py`.
+ 3. Provide tests plus a front-end consumer (typically via `window.initialData` or a fetch call scoped to `/custodia-api/...`).
+- Never expose raw Django models directly—shape payloads similar to `custodia/views.student_to_dict`.
+
+## Behavior Contract for AI Agents
+
+- ❌ Do **not** modify or extend legacy Play/Scala/Java code under `app/`, `project/`, `conf/`, `target/`, `authLibrary/`, `modelsLibrary/`, or legacy webpack configs without human direction.
+- ❌ Do **not** introduce new Python dependencies, npm packages, or system-level tools without approval, except when a React/Vite change makes a new npm dependency unavoidable. In that case, update `react/package.json` and document the requirement.
+- ❌ Do **not** rename database columns, change `db_table` mappings, or drop constraints without human review.
+- ❌ Do **not** generate, commit, or log secrets (`APPLICATION_SECRET`, Rollbar tokens, etc.).
+- ❌ Do **not** run destructive migrations or data backfills that can drop or rewrite production data without explicit sign-off.
+- ✔️ Do follow the closest existing pattern in the area you are modifying (views mimic `dst/manual_views.py`, React screens follow `react/pages/SignInSheetPage` structure, etc.).
+- ✔️ Do scope every backend query by `request.org` and enforce `UserRole`s before exposing data.
+- ✔️ Do ask for clarification (or fail fast) when requirements are ambiguous or would violate any rule above.
+
+## Testing & Linting Enforcement
+
+- **Backend**:
+ - Run `uv run manage.py test` from `django/` to execute Django’s test runner. Tests should live in each app (`django/dst/tests.py`, etc.) and cover new models, views, serializers, and management commands.
+ - Run `uv run ruff check django` (per `django/pyproject.toml`) before submission. Fix import order/lint errors rather than silencing them. For HTML templates, format with `django/format-djhtml.sh` when touched.
+- **Frontend**:
+ - Run `npm run lint` inside `react/`. The ESLint config enforces React, Hooks, and import rules; no warnings may remain.
+ - Format JS/JSX/SCSS via Prettier (`npx prettier --check .` / `--write`) leveraging the configured import-sort plugin.
+ - When you add a frontend test suite, document and run it (e.g., `npm run test`) before committing.
+- **Build checks**:
+ - Use `npm run watch` (Vite dev server on port 8082 per `DJANGO_VITE`) during development; reserve `npm run build` for release workflows so hashed assets match `django-vite`.
+ - For backend processes, use the Procfile in `django/Procfile` (`uv run manage.py migrate && uv run manage.py runserver`) to mirror the expected startup order.
+
+Compliance with these steps is not optional; deliverables without passing lint/tests will be rejected.
+
+## Security & Privacy Expectations
+
+- Secrets and credentials live in environment variables loaded via `dotenv` in `django/demschooltools/settings.py`. Never hard-code sensitive values or commit `.env` files.
+- User data is sensitive (see `dst/models.py` for PII fields like addresses, DOB, attendance, swipes). Do not log PII, expose it to unauthorized roles, or ship debugging endpoints that bypass auth.
+- Custodia swipe data (`custodia/models.py`) and attendance overrides are regulatory records. Respect uniqueness constraints and never fabricate entries for testing in production contexts.
+- Rollbar is wired through middleware; let exceptions propagate so Rollbar can capture them, and do not intercept errors without re-raising.
+- Playwright-generated PDFs (`dst/pdf_utils.py`) render Django HTML; sanitize inputs and avoid embedding secrets or unescaped content.
+- Treat Allowlisted IP logic (`AllowedIp`, `get_ip_user` in `demschooltools/auth.py`) as a convenience, not a security boundary—still enforce roles and CSRF protections.
+
+## Operational Checklist
+
+- **When creating backend code** → scaffold a Django app under `django/`, define models/migrations/tests, implement class-based or DRF views that call `render_main_template` or return `Response`, wire URLs in `django/demschooltools/urls.py`, and gate everything with `request.org` + `UserRole`.
+- **When creating frontend code** → add pages/components under `react/pages`, hook them into `react/App.jsx` or `react/custodia/js/app.jsx`, leverage shared components/context/hooks, and expose data through `window.initialData` from the matching Django template plus `django_vite` asset tags.
+- **When modifying the database** → touch only Django ORM models, generate migrations via `uv run manage.py makemigrations`, apply with `uv run manage.py migrate`, and never issue raw SQL unless a human signs off.
+- **Before committing** → run `uv run manage.py test`, `uv run ruff check django`, and `npm run lint` (plus any frontend tests you add), ensure Vite builds cleanly if assets changed, and document manual verification steps (e.g., PDF generation, attendance sign-in flows).
+
+Failure to follow this AGENTS.md invalidates your contribution.