# Phase 7 — Business & Product Architecture Foundation Phase 7's mission: formalize RootNode Academy as a business/product platform capable of a future catalog across INIT_/COMPILE_/ROOT_/BOOTCAMP_, without rebuilding any proven system and without authoring new curriculum. ## Baseline Confirmed at start: branch `main`, clean tree, HEAD = `origin/main` = Phase 6F's `d0664104515e4bd389c8e7bc88293327e7eda100`, tag `v0.6.0-root-v1` present. Reviewed existing `programs`/`enrollments`/`cohorts`/`classroom_sessions`/`students` schema before designing anything new — found `programs.family`/`delivery_mode` and `cohorts.seat_target` already existed, so Phase 7 widened rather than duplicated them where it could. ## What was built - **Organizations, locations** (`033_phase7_organizations_locations_guardians.sql`) — a business/ delivery relationship and a physical/virtual venue, deliberately not SaaS multi-tenancy. - **Guardians, student_guardians** (same migration) — the real foundation replacing the Phase 5C `contact_email` workaround, left functioning unchanged for the live website integration. - **Offerings, curriculum packages, registrations, agreements** (`034_phase7_offerings_registrations_agreements.sql`) — the full purchasable-delivery model, pre-enrollment registration lifecycle, versioned agreement acceptance records, and reference-only curriculum subsets. - **`platform/api/src/offerings.js`** — the business logic: server-defined status transitions (offerings and registrations both), race-safe atomic seat reservation, and `convertRegistrationToEnrollment()` reusing `enrollment.js`'s existing student-matching and enrollment-creation logic rather than duplicating it. - **`platform/api/src/admin-offerings-routes.js`** — the full admin/owner API surface for all of the above, registered in `server.js` alongside the existing route modules. - **`programs.family` widened** to accept `bootcamp` (a pure CHECK-constraint widening; no existing row changes). ## What was NOT built this phase **Admin UI** (Steps 16/17 — Offerings/Registrations screens in `/admin`). This was a deliberate scope call: given the phase's own emphasis ("BUSINESS DOMAIN MODEL, PRODUCT ARCHITECTURE, ENROLLMENT/OPERATIONS ARCHITECTURE"), the schema, business logic, RBAC, tests, and a full working rehearsal were prioritized as the load-bearing deliverable over a polished React surface. Every capability Steps 16-17 describe is fully implemented and tested at the API layer — an admin UI is now a bounded, well-specified follow-up (the routes and their exact shapes already exist), not a design question. Also not built, per the explicit DO-NOT-BUILD list: payment processing, subscription billing, public checkout, INIT_/COMPILE_ curriculum, ROOT 08/11 expansion, a Parent Portal UI, school SIS integrations, certificates. ## A real bug found and fixed during this phase's own testing The registration-creation route originally reserved an offering's seat *before* validating the registration's own fields. Under a full offering, this let a plain validation failure (e.g. a minor registration missing `guardianId`) get masked behind a false `409 OFFERING_FULL`, and even when capacity wasn't the issue, a request that would fail validation anyway had already consumed a real seat. Found via this phase's own smoke testing before any commit; fixed by validating (creating) the registration first, then reserving the seat, rolling back the registration row if reservation then genuinely fails. See `docs/OFFERING_AND_REGISTRATION_MODEL.md`. ## Testing `platform/test/e2e-phase7.js` — 17/17 passing (fresh stack): course-family/age-guidance independence from authorization, offerings across two distinct locations, curriculum-package content-reference integrity (including rejecting a fabricated course id), atomic capacity enforcement with correct validate-before-reserve ordering, instructor assignment, guardian creation/linking, minor-vs-adult registration paths, registration lifecycle transitions (including a rejected invalid jump), agreement-version-snapshotted acceptance, registration→ enrollment conversion with correct cohort/no-cohort behavior, curriculum-package version-bump stability, registration/guardian RBAC (admin/owner only) vs. offering/location visibility (any instructor, with `access_notes` stripped), explicit age-independence, and confirmation that an existing ROOT_ V1 self-paced enrollment and an existing classroom/cohort flow are both completely unaffected. ## Winter Break Camp rehearsal `infra/scripts/drills/test-phase7-winter-camp.js` — 18/18 passing against a real, freshly-built stack, with **no payment code invoked anywhere**: admin creates a real organization/location, a curriculum package referencing the existing ROOT 01-02 courses (matching `docs/ WINTER_BREAK_CAMP_CURRICULUM_OPTIONS.md`'s Option A exactly), a real cohort, and a DRAFT offering with dates/price/exact seat count left unset as authorized; a guardian registers a minor and an adult registers themselves; both move through submission, agreement acceptance, and a simulated admin-side commerce confirmation; both convert to real enrollments and real cohort membership with real one-time activation tokens; both activate real accounts; the instructor starts a real classroom session and Prepares Labs; the minor student joins, completes real ROOT 01 orientation and lesson activities, and passes a REAL Lab task (`linux-inspect-hardware`) inside a real disposable Lab container — with the pass then confirmed to survive a real logout/login; the instructor sees real roster state; the offering moves through `in_progress` to `completed`. ## ROOT_ V1 regression `e2e-phase6f.js` rerun fresh: 17/17 unaffected. `e2e-phase5b.js` (the enrollment-related suite most exposed by this phase's one shared-code change — `enrollment.js`'s `resolveOrCreateStudent` gained an optional, default-off parameter) rerun fresh: 32/32, including its own embedded "Phase 5A student authentication still works unmodified" and "Phase 5A remote classroom still works unmodified" checks. `e2e-phase5c.js` was not independently re-run: no code in `notifications.js`/`students-auth.js` itself changed, only an additive, default-off parameter on a function `notifications.js` doesn't call. ## Frontend, migrations, content validation Frontend: `npm run build` clean, no frontend code touched this phase. Content validation: 0 errors, unchanged from Phase 6F's own numbers (this phase touched no course/module/activity content at all). Migrations: additive-only, `CREATE TABLE IF NOT EXISTS`/`DROP CONSTRAINT IF EXISTS` throughout, applied once via the existing `schema_migrations` tracking mechanism — no destructive statement anywhere in `033`/`034`. ## Documentation New: `ROOTNODE_ACADEMY_BUSINESS_AND_PRODUCT_ARCHITECTURE.md`, `OFFERING_AND_REGISTRATION_ MODEL.md`, `STUDENT_GUARDIAN_DATA_MODEL.md`, `SCHOOL_AND_COMMUNITY_DELIVERY_ARCHITECTURE.md`, this document. Updated: `WEBSITE_ENROLLMENT_INTEGRATION.md` (a documented future flow, current contract unchanged). Reviewed, no change needed: `REMOTE_AND_SELF_PACED_ARCHITECTURE.md`, `ENGINEERING_SOP.md`, `LAB_ARCHITECTURE.md`, `platform/README.md` (no new e2e-suite convention beyond what its existing list format already documents — see commit history for the actual `platform/README.md` addition covering `e2e-phase7.js`). ## Known limitations - **No admin UI** for offerings/registrations — the API is complete and tested; the screens are a well-specified follow-up, not a design question. - **Guardian communications are not yet wired into `notifications.js`** — `student_guardians.can_receive_communications`/`is_primary_contact` are recorded but nothing currently reads them to route an actual email. - **The website integration endpoint does not yet accept an `offering_slug`** — the future flow is documented, not implemented; that decision (extend the existing endpoint vs. a parallel one) was deliberately left open pending real website/checkout design. - **`agreement_acceptances.accepted_by_type = 'student'`** is only meaningful post-conversion, once a real `student_id` exists — an adult accepting at registration time (before their account exists) currently must accept as `'guardian'`-style even when self-registering; a future phase could add a `'self'` accepted-by-type distinct from `'guardian'` if that distinction becomes operationally important. ## Items requiring legal/business review before production use with real minors See `docs/STUDENT_GUARDIAN_DATA_MODEL.md`'s own list in full: the actual legal text of any participation/media/liability agreement, jurisdiction-specific minor-consent requirements, data-retention/deletion policy for guardian and registration records, and whether emergency- contact fields need additional encryption at rest. None of these are resolved by this phase's work — they are named so they are never silently assumed away. ## Bugs found/fixed One (the seat-reservation-before-validation ordering bug above), found and fixed before any commit via this phase's own smoke testing — not discovered by a later test run. ## Recommended Phase 8 scope The admin UI for offerings/registrations (the one deliberately deferred piece of this phase's own mission); wiring guardian communications into `notifications.js`; and, informed by real business decisions, either extending `/api/integrations/enrollments` with an `offering_slug` or designing the parallel endpoint the future commerce flow needs — plus whatever the business decides about actually opening Winter Break Camp registration (which requires payment processing, explicitly out of every phase's scope so far).