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¶
Migrations are schema-aware. Shared tables →
migrate_schemas --shared. Tenant tables are applied to every tenant schema. Useall_tenants_command <cmd>to run a command across all tenant schemas. Commands that readconnection.schema_namemust run inside the owning tenant’s context. (See multi-tenancy.)API base path is
/app/<module>/with an internalapi/prefix; DRF routers sit atapi/m2m/. It is not/api/<module>/. (See backend API.)MIDSUMMER_PROD=falselocally, always. The canonical run command isrender-ui-elements.sh && render-tenant-ui-elements.sh && entrypoint.sh. (See quickstart.)DB-backed
manage.py testdoes not run in the standard dev environment (remote Postgres lackstest_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 |
|---|---|
|
Django project: settings, root URLconf, ASGI/WSGI, middleware |
|
Cross-app: |
|
The Django apps (see Systems) |
|
Main event SPA (Angular 21) |
|
Tenant-admin SPA (Angular 19) |
|
Authoritative deep-dive docs (data-model, backend-api, frontend, architecture) |
|
Operational runbooks |
|
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 likeif check_permission(request, 'register') not in ('admin', 'superadmin'): return 403. UseMembership.isTenantSuperuser(not the non-existentisTenantAdmin). (See permissions.)Form system: dynamic forms are JSON schemas (
RegistrationForm.fields,VendorType.form,ShopItem.form) rendered by theregister-form-component-factory. Submissions live in*.dataJSON. Read answers withretrieve_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 withselect_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 aRegistrationvia admin tools does not touch Stripe. Use unrestricted keys; test mode only locally. (See payments.)
How to do common tasks¶
Add a REST endpoint¶
Add a
@api_viewfunction orViewSetin<app>/views.py.Gate with
check_permission(...); scope torequest.current_event.Register a URL in
<app>/urls.pyunder the existing/app/<module>/router (ViewSets onapi/m2m/, function views underapi/).Add a serializer in
<app>/serializers.pyif needed.
Add a model field / migration¶
Edit
<app>/models.py.python manage.py makemigrations <app>thenpython manage.py migrate_schemas.python manage.py makemigrations <app> --checkmust report no drift.
Add a form field type¶
Component under
register-form-components/primatives/<type>/.Descriptor in the right registry (
*-components.ts) —component,name,icon,canOnlyHaveOne,optionsschema. The factory picks it up automatically.Ensure
retrieve_from_datacan read it by itscomponentkey.
Add a management command¶
<app>/management/commands/<app>__<verb>.pywithCommand(BaseCommand).Write an accurate
help(do not copy the stale placeholder text).Make it schema-aware if it touches tenant data; document the context requirement.
Add/rebuild a frontend feature¶
Edit the component in
ui/src/app/apps/<module>/(ortenantui/).cd ui && npx ngc --noEmit -p tsconfig.app.json(type-check)../render-ui-elements.sh(rebuild into Django static).
Known gotchas¶
isTenantAdmindoes not exist onMembership— useisTenantSuperuser.Several register management commands have placeholder
helptext 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/loginroute);tenantui/has both a Django/login/and a guarded Angular/login.Windows is unsupported; Linux/macOS only.
Where to read more (in priority order)¶
Quickstart → First steps — build/run/gates.
Concepts → The custom form system — the signature subsystem.
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.