architecture-faculty-classroom
Architecture — Faculty Classrooms, Fees & Class Sessions
Status: Target architecture for the Faculty Classroom milestone
Inputs: PRD · shipped Elm Street day-gate stack
Companion: Engineering
1. Context
Agent Play already has:
- Overworld Pixi campus (
educationCampusLayer) with four buildings and day-gate proximity payment - Enclosed stages (house, amenity, arcade games) with Esc/exit patterns
- Dual-tender catalogs + session-store purchase CAS + scanner indexing
- Wallet inventory surface for
education_pass
This milestone adds faculty interiors, tuition enrollments, class sessions, and a file-backed curriculum pack.
┌─────────────────────────────────────────────────────────────┐
│ Overworld (Elm Street) │
│ faculty buildings · day gate · P/A/C prompts │
└───────────────┬─────────────────────────────────────────────┘
│ P / C
▼
┌─────────────────────────────────────────────────────────────┐
│ Faculty Interior Stage (classroom) │
│ curriculum sections · path boards · exit door │
│ class mode: lesson cards │
└───────────────┬─────────────────────────────────────────────┘
│ A (path) / A (lesson)
▼
┌─────────────────────────────────────────────────────────────┐
│ DOM panels │
│ path picker · tuition gate · lesson reader │
└─────────────────────────────────────────────────────────────┘
│ RPC
▼
┌─────────────────────────────────────────────────────────────┐
│ web-ui session store + SDK catalogs │
│ day pass · tuition enrollment · wallet · scanner │
└─────────────────────────────────────────────────────────────┘
▲
│ static import
┌─────────────────────────────────────────────────────────────┐
│ play-ui education content pack (repo folders) │
└─────────────────────────────────────────────────────────────┘
2. Domain model
2.1 Identities
| Concept | Id type | Example |
|---|---|---|
| Faculty | EducationFacultyId |
faculty-science |
| Learning path | EducationPathId |
sci-mathematics |
| Lesson | EducationLessonId |
sci-mathematics/03-linear-relations |
| Day pass | existing EducationAccessPass |
per faculty UTC day |
| Enrollment | EducationTuitionEnrollment |
per faculty+path, 365d |
2.2 Relationships
Faculty 1──* Path 1──* Lesson
Player 1──* DayPass(faculty, utcDay)
Player 1──* Enrollment(faculty, path, expiresAt)
Player 0──1 ActiveClassSession(faculty, path) // client stage state; server may mirror later
2.3 Access rules (authoritative)
| Action | Requires |
|---|---|
| Show post-gate prompts | Active day pass for faculty |
Enter interior (P) |
Active day pass |
Open path picker (A) |
Active day pass |
| Purchase tuition | Active day pass recommended for UX; server may allow tuition without day pass but client only offers A when gated |
Start class (C) |
Active day pass and unexpired enrollment for a path in that faculty |
| Open lesson | Active class session for that path (client) + day pass still nice-to-have same UTC day |
PRD client rule: tuition chooser only from gated proximity/interior.
Server rule: tuition purchase validates path id + funds; optionally requires day pass for anti-abuse (engineering recommendation: require day pass).
3. Runtime surfaces
3.1 play-ui (watch canvas)
| Module (proposed) | Responsibility |
|---|---|
education-campus-layer |
Faculty facades, plaques, lights, name strip (rename specs) |
education-campus-proximity |
Nearest faculty + prompt copy by access state |
education-access-* |
Day gate (existing) |
education-faculty-interior-stage |
Classroom graphics, sections, cards, exit |
education-path-panel |
Path picker UI |
education-tuition-panel |
Annual fee confirmation (dual tender) |
education-lesson-panel |
Lesson reader |
education/content/** |
Static curriculum pack |
main.ts |
Wire proximity keys P/A/C, stage enter, panels |
3.2 web-ui / SDK
| Layer | Responsibility |
|---|---|
education-access-catalog |
Day gate (existing) + faculty id rename |
education-tuition-catalog (new) |
Path tiers, APW$ sticker, dual tender resolve |
| Session store | getEducationTuition / purchaseEducationTuition (+ list enrollments) |
| RPC route | Mirror store ops |
| Scanner | education_tuition → purchaseEducationTuition |
| Wallet DTO | Show tuition rows |
3.3 Content pack (repo)
packages/play-ui/src/education/content/
README.md
catalog.ts # typed index: faculties → paths → lessons + fee tier
faculties/
art/
faculty.md
art-languages-communication/
outline.md
lessons/
01-....md
...
science/
medicine/
education/
Runtime loads via catalog.ts (import Markdown as raw strings through Vite ?raw or precompiled JSON generated in build — engineering picks one; architecture requires file-per-lesson source of truth).
4. Stage architecture
4.1 Stage id
Proposed: facultyClassroom (or educationFaculty) on StageController.
Lifecycle mirrors houseInterior / spaceYard:
enterFacultyClassroom({ facultyId, mode: "browse" | "class", pathId? })- Build stage handle with bounds, exit door, local player
back()on Esc / exit proximity
4.2 Modes
| Mode | Contents |
|---|---|
browse |
Curriculum section boards for 3 paths; plaques; no lesson cards (or dimmed) |
class |
Lesson cards for active pathId; section boards may remain as wayfinding |
Transition browse → class on successful C (or enter directly in class mode).
4.3 Spatial layout (logical)
[Board: Faculty title]
[Path A] [Path B] [Path C] ← browse sections
[Teacher desk]
card1 card2 card3 card4 ← class mode only
[Exit door]
World units local to stage (not overworld cells), same pattern as yard/house.
5. Input priority
When multiple systems compete:
- Modal open (tuition / path / lesson / day gate) → consume Esc first
- Agent partner proximity (assist/chat) on overworld
- Faculty post-gate P / A / C when nearest faculty & day pass
- Faculty day-gate payment when no pass
- House / parking / structure / arcade
Inside faculty stage:
- Lesson panel / path panel open
- Exit door
- Lesson-card A
- Path-section A (browse)
- Movement
C on overworld with enrollment: enter interior in class mode if not inside.
6. Money flows
6.1 Day gate (existing)
education_pass · 5 APU · UTC day · purchaseEducationAccess
6.2 Tuition (new)
Client A → path select → tuition panel
getEducationTuition({ facultyId, pathId })
→ quoteApw, apuCost, preferredTender, existing enrollment?
purchaseEducationTuition({ facultyId, pathId })
→ debit APU or APW$
→ Enrollment { purchasedAt, expiresAt: +365d, tier, apwCharged, apuCost }
→ PurchaseRecord amenityKind: education_tuition
→ scanner op: purchaseEducationTuition
Fee table (APW$ sticker):
| tier | quoteApw |
|---|---|
| foundation | 450 |
| intermediate | 675 |
| advanced | 900 |
APU side derived from live apwPerApu via shared dual-tender helper.
7. Client state caches
| Cache | Shape |
|---|---|
educationAccessPassCache |
Map<facultyId, DayPass> (existing, rename keys) |
educationTuitionCache |
Map<${facultyId}:${pathId}, Enrollment> |
activeClassPathByFaculty |
Map<facultyId, pathId> |
openedLessons |
optional Set for HUD (can be localStorage later) |
Invalidate / refresh from RPC after purchases and on enter proximity (throttled).
8. Data migration
8.1 Faculty id remap
| Legacy centerId | New facultyId |
|---|---|
foundations-hall |
faculty-art |
curriculum-tower |
faculty-science |
assessment-atelier |
faculty-medicine |
classroom-studio |
faculty-education |
Day passes stored under old ids: on read, map legacy → new; new writes use faculty ids only.
8.2 Content → DB (future)
Keep catalog.ts as adapter boundary. Future ContentRepository can swap file loaders for HTTP/CMS without changing stage/panels.
9. Security & abuse
- All charges server-authoritative
- Tuition amount taken from server catalog, never client-stated price
- CAS on wallet debit
- Scanner mirror failures must not silently skip wallet mutation (same as existing purchases)
- Lesson content is public static; no secrets in Markdown
10. Observability
- Verbose logs: enter faculty, tuition quote/purchase, class start, lesson open
- Analytics events (proposed):
education_faculty_enter,education_tuition_purchase,education_class_start,education_lesson_open
11. Testing architecture
| Layer | Examples |
|---|---|
| Pure catalog | Tier → 450/675/900; remap ids; enrollment active |
| Session store | Tuition purchase, insufficient funds, idempotent active enrollment |
| RPC | get/purchase tuition; invalid path 400 |
| Proximity pure fn | Prompt verbs by pass/enrollment/class state |
| Panel DOM | Path select → tuition; lesson open |
| Content pack | Catalog references existing files; every path ≥4 lessons |
12. Phased delivery (architectural)
- Rename & gate unlock — faculty ids/labels; post-gate P/A/C prompts (P/A/C may stub)
- Interior browse — classroom stage + exit
- Tuition — catalog, store, panels, wallet/scanner
- Class mode — cards + lesson reader wired to content pack
- Polish — copy, mobile layout, analytics
Dependencies: (1) before (2–4); (3) before meaningful (4); content pack can land in parallel from phase 1.