{"id":1602,"title":"Project: Westside Email Agent","slug":"project-westside-email-agent","html_content":"<h2 id=\"westside-email-agent\">Westside Email Agent</h2>\n<h3 id=\"vision\">Vision</h3>\n<p>A single agent + generic blast system that sends every branded Westside email type. New email types are added as <strong>data</strong> — audience query + EmailType + layout — not as new agents or new endpoint code. One agent, many email types, zero PRs to send a new email when its three pieces already exist.</p>\n<h3 id=\"user-stories\">User Stories</h3>\n<table>\n<tr>\n<th>Key</th>\n<th>Story Note</th>\n<th>Role</th>\n<th>Success Metric</th>\n</tr>\n<tr>\n<td><code>agent-handles-emails</code></td>\n<td><a href=\"/notes/story-westside-email-agent-agent-handles-emails\">story-westside-email-agent-agent-handles-emails</a></td>\n<td>Admin (Lucas; Marcus via relay)</td>\n<td>Lucas can send any email type with one CLI session, zero code changes, zero unapproved sends</td>\n</tr>\n</table>\n<p>Additional stories will be added as new email types or workflow patterns surface. The single-story start is intentional — the existing infrastructure already covers the universal case.</p>\n<h3 id=\"architecture\">Architecture</h3>\n<p>This project shares its backend architecture with <code>westside-basketball</code>. Diagrams already exist:</p>\n<ul>\n<li><a href=\"/notes/arch-email\">arch-email</a> — components (gmail-sdk, services/email.py, services/email_queries.py, services/outbox.py, brand.py, MJML templates, EmailLog), layouts (notification, action, announcement), MJML→compile→send flow, design decisions</li>\n<li><a href=\"/notes/sop-email-send\">sop-email-send</a> — 8-step universal workflow + contract enhanced gate + 11 rules (the canonical nuclear gate)</li>\n</ul>\n<p>The dedicated triplet (<code>arch-domain-westside-email-agent</code>, <code>arch-dataflow-westside-email-agent</code>, <code>arch-deployment-westside-email-agent</code>) is intentionally <strong>not built</strong> — the existing arch-email already describes the system at the right level. Triplet can be backfilled if/when the agent's runtime layer diverges meaningfully from basketball-api's email layer.</p>\n<h4 id=\"key-decisions\">Key Decisions</h4>\n<ul>\n<li><strong>Audience as data, not code path</strong> — <code>QUERY_REGISTRY</code> in basketball-api already lets new audiences register without endpoint changes. Future: lift the registry from Python dict to DB table for non-engineer authoring.</li>\n<li><strong>Universal nuclear gate</strong> — sop-email-send's gate applies to ALL email types. No per-email-type gate logic.</li>\n<li><strong>One agent, not per-type</strong> — westside-email-agent handles every email type. Separate agents (e.g. tournament-email-agent) were considered and rejected as duplicate scaffolding.</li>\n<li><strong>Agent persona is local-only</strong> — <code>~/westside-email-agent/CLAUDE.md</code> is not in a Forgejo repo, parallel to other working-dir agents under <code>~/westside-agency/</code>.</li>\n</ul>\n<h3 id=\"board\">Board</h3>\n<p><a href=\"/notes/board-westside-email-agent\">board-westside-email-agent</a> — primary kanban for this project. Permanent, not time-boxed. Columns: backlog → todo → next_up → in_progress → qa → needs_approval → validation → done. Items reference Forgejo issues in <code>basketball-api</code> (since that's where email infrastructure code lives).</p>\n<h3 id=\"status\">Status</h3>\n<table>\n<tr>\n<th>Component</th>\n<th>Status</th>\n<th>Notes</th>\n</tr>\n<tr>\n<td><code>POST /admin/email/blast</code></td>\n<td>Built</td>\n<td>basketball-api/routes/admin.py:1233 — generic, validates query + email_type, supports test_email</td>\n</tr>\n<tr>\n<td><code>QUERY_REGISTRY</code></td>\n<td>Built (3 queries)</td>\n<td>unsigned_contracts, incomplete_profiles, tournament_committed</td>\n</tr>\n<tr>\n<td><code>query_monthly_fee_due</code></td>\n<td>Missing</td>\n<td>Tracked: <a href=\"https://forgejo.tail5b443a.ts.net/forgejo_admin/basketball-api/issues/512\">basketball-api#512</a></td>\n</tr>\n<tr>\n<td>Layouts (action, notification, announcement, jersey-reminder)</td>\n<td>Built</td>\n<td>basketball-api/templates/email/compiled/</td>\n</tr>\n<tr>\n<td>EmailType enum</td>\n<td>Built</td>\n<td>basketball-api models — values per email type</td>\n</tr>\n<tr>\n<td>EmailLog</td>\n<td>Built</td>\n<td>Automatic per send</td>\n</tr>\n<tr>\n<td>Agent (CLAUDE.md, auth shortcut, safety rules)</td>\n<td>Built</td>\n<td>~/westside-email-agent/CLAUDE.md (3.6KB)</td>\n</tr>\n<tr>\n<td>arch-email + sop-email-send</td>\n<td>Built</td>\n<td>Canonical, active</td>\n</tr>\n</table>\n<p><strong>Today's blockers:</strong> contract reminder send is unblocked (existing infra). Monthly fee send waits on basketball-api#512.</p>\n<h3 id=\"milestones\">Milestones</h3>\n<ul>\n<li>TBD — first milestone will land when the contract send + monthly send are both completed and EmailLog confirms zero-delta delivery.</li>\n</ul>\n<h3 id=\"repos\">Repos</h3>\n<table>\n<tr>\n<th>Repo</th>\n<th>Platform</th>\n<th>Role</th>\n<th>Status</th>\n</tr>\n<tr>\n<td><code>~/westside-email-agent/</code></td>\n<td>local-only</td>\n<td>Agent CLAUDE.md (no Forgejo repo, by design)</td>\n<td>Active</td>\n</tr>\n<tr>\n<td><code>forgejo_admin/basketball-api</code></td>\n<td>Forgejo</td>\n<td>Backend — blast endpoint, query registry, EmailLog, EmailType enum</td>\n<td>Active</td>\n</tr>\n<tr>\n<td><code>forgejo_admin/westside-emails</code></td>\n<td>Forgejo</td>\n<td>MJML template source (compiled HTML mounted into basketball-api)</td>\n<td>Active</td>\n</tr>\n</table>\n<h3 id=\"related\">Related</h3>\n<ul>\n<li><a href=\"/notes/project-westside-basketball\">project-westside-basketball</a> — backend repo where email infrastructure lives</li>\n<li><a href=\"/notes/project-westside-agency\">project-westside-agency</a> — orchestrator project (this agent registered as a slot)</li>\n<li><a href=\"/notes/feedback-email-blast-nuclear-gate\">feedback_email_blast_nuclear_gate</a> — gate provenance</li>\n<li><a href=\"/notes/feedback-payment-email-e2e-validation\">feedback_payment_email_e2e_validation</a> — e2e validation requirement</li>\n</ul>","is_public":true,"note_type":"project-page","status":"active","parent_slug":null,"position":null,"created_by_sub":null,"created_by_name":null,"updated_by_sub":null,"updated_by_name":null,"project":{"id":47,"name":"Westside Email Agent","slug":"westside-email-agent","platform":"forgejo","repo_url":null,"is_public":true,"page_note":null,"created_at":"2026-04-25T02:21:40.966330","updated_at":"2026-04-25T02:21:40.966330"},"tags":[{"id":8,"name":"active"},{"id":18,"name":"project-page"}],"created_at":"2026-04-25T02:26:49.409470","updated_at":"2026-04-25T02:26:49.409470"}