Agent guide

Context for AI coding agents working on Midsummer

This page is a distilled orientation designed for an AI coding agent to consume before working in this codebase. Read it first; it encodes the non-obvious conventions that will save you from common mistakes. Everything here is expanded in the rest of this site — this is the cheat sheet.

If you maintain a project-level memory bank or AGENTS.md, treat the facts below as authoritative context.


What Midsummer is (one paragraph)

Midsummer V2 is a multi-tenant monolith: Django 5.x (Python 3.12) backend serving REST + WebSocket APIs, with two Angular SPAs (ui/ on Angular 21, tenantui/ on Angular 19), all over one PostgreSQL database with per-tenant schema isolation (django-tenants). It manages conventions: registration, vendors/dealers, scheduling, staff, and an e-commerce shop. Payments are all Stripe.

The four rules that will bite you

  1. Migrations are schema-aware. Shared tables → migrate_schemas --shared. Tenant tables are applied to every tenant schema. Use all_tenants_command <cmd> to run a command across all tenant schemas. Commands that read connection.schema_name must run inside the owning tenant’s context. (See multi-tenancy.)

  2. API base path is /app/<module>/ with an internal api/ prefix; DRF routers sit at api/m2m/. It is not /api/<module>/. (See backend API.)

  3. MIDSUMMER_PROD=false locally, always. The canonical run command is render-ui-elements.sh && render-tenant-ui-elements.sh && entrypoint.sh. (See quickstart.)

  4. DB-backed manage.py test does not run in the standard dev environment (remote Postgres lacks test_midsummerpool). Write pure-helper unit tests for new business logic, and rely on the always-on gates below.

Always-on verification gates

Run these before declaring work done:

python manage.py check                                   # 0 errors (~19 warnings normal)
python manage.py makemigrations <app> --check            # no drift
cd ui && npx ngc --noEmit -p tsconfig.app.json           # frontend type-check (exit 0)
cd ui && npx ng build                                    # full build (exit 0)

Frontend-only changes: also run the tenantui/ equivalent if you touched it.

Where code lives

Path

What

midsummer/

Django project: settings, root URLconf, ASGI/WSGI, middleware

midsummer/common/

Cross-app: ui.py (menus), permissions.py, triggers/, SPA shell

accounts/ event/ register/ vendors/ staff/ schedule/ shop/ tenant/ kioskos/

The Django apps (see Systems)

ui/

Main event SPA (Angular 21)

tenantui/

Tenant-admin SPA (Angular 19)

documentation/

Authoritative deep-dive docs (data-model, backend-api, frontend, architecture)

guides/

Operational runbooks

.plans/

Implementation plan docs (BSER workflow)

Per-app Django conventions: models.py, views.py, urls.py, serializers.py, admin.py, tests.py, migrations/, plus optional services/, consumers.py, routing.py, tasks.py.

The patterns you must follow

  • Permission gating: every mutating/privileged view starts with check_permission(request, '<module>') returning a level; gate like if check_permission(request, 'register') not in ('admin', 'superadmin'): return 403. Use Membership.isTenantSuperuser (not the non-existent isTenantAdmin). (See permissions.)

  • Form system: dynamic forms are JSON schemas (RegistrationForm.fields, VendorType.form, ShopItem.form) rendered by the register-form-component-factory. Submissions live in *.data JSON. Read answers with retrieve_from_data('<component-key>', 'value.<path>'), which is hardened against non-dict entries (returns -1). (See custom form system.)

  • Audit + snapshot: mutating historical data (merch, tier, registrant edit) → run inside transaction.atomic(), re-fetch with select_for_update(), save(update_fields=[...]), and append an audit-log row via a silent try/except helper (logging must never break a successful op). (See audit logging.)

  • Stripe: products/prices auto-managed in model save(); confirmation is webhook-driven; mutating a Registration via admin tools does not touch Stripe. Use unrestricted keys; test mode only locally. (See payments.)

How to do common tasks

Add a REST endpoint

  1. Add a @api_view function or ViewSet in <app>/views.py.

  2. Gate with check_permission(...); scope to request.current_event.

  3. Register a URL in <app>/urls.py under the existing /app/<module>/ router (ViewSets on api/m2m/, function views under api/).

  4. Add a serializer in <app>/serializers.py if needed.

Add a model field / migration

  1. Edit <app>/models.py.

  2. python manage.py makemigrations <app> then python manage.py migrate_schemas.

  3. python manage.py makemigrations <app> --check must report no drift.

Add a form field type

  1. Component under register-form-components/primatives/<type>/.

  2. Descriptor in the right registry (*-components.ts) — component, name, icon, canOnlyHaveOne, options schema. The factory picks it up automatically.

  3. Ensure retrieve_from_data can read it by its component key.

Add a management command

  1. <app>/management/commands/<app>__<verb>.py with Command(BaseCommand).

  2. Write an accurate help (do not copy the stale placeholder text).

  3. Make it schema-aware if it touches tenant data; document the context requirement.

Add/rebuild a frontend feature

  1. Edit the component in ui/src/app/apps/<module>/ (or tenantui/).

  2. cd ui && npx ngc --noEmit -p tsconfig.app.json (type-check).

  3. ./render-ui-elements.sh (rebuild into Django static).

Known gotchas

  • isTenantAdmin does not exist on Membership — use isTenantSuperuser.

  • Several register management commands have placeholder help text and hit live Stripe; some (manual_export, merch_summary, syncv1) are event- specific one-offs with hardcoded UUIDs. (See management commands.)

  • Two Angular versions: ui/ is PrimeNG 21, tenantui/ is PrimeNG 19 — don’t copy components between them blindly.

  • ui/ login page is Django-served (no Angular /login route); tenantui/ has both a Django /login/ and a guarded Angular /login.

  • Windows is unsupported; Linux/macOS only.

Where to read more (in priority order)

  1. Quickstart → First steps — build/run/gates.

  2. Concepts → The custom form system — the signature subsystem.

  3. Concepts → Multi-tenancy and Request resolution.

  4. Reference → Management commands.

  5. The exhaustive documentation/ files (data-model, backend-api, frontend, architecture) when you need field/endpoint-level detail.

Project workflow (BSER)

This repo uses the Brief → Scope → Execute → Review+Sync workflow with /scope, /implement, /review, /sync commands and a @syncer agent that keeps documentation/ (and thus this site’s deep-dive links) current. See BSER-QUICKREF.md and AGENTS.md. Plans live in .plans/; never expand scope beyond the active plan — add discoveries to the plan’s “Future (Out of Scope)” section.