Files
rootnode-academy/docs/PHASE7_PRODUCT_ARCHITECTURE.md
T
ced4568andClaude Sonnet 5 df5e39f767 docs(phase7): document the business/product architecture foundation
New: ROOTNODE_ACADEMY_BUSINESS_AND_PRODUCT_ARCHITECTURE.md (canonical business/platform
bridge — course families, delivery modes, BUILT NOW/PHASE 7 BUILT/FUTURE), OFFERING_AND_
REGISTRATION_MODEL.md (lifecycle diagrams, capacity mechanics), STUDENT_GUARDIAN_DATA_
MODEL.md (data minimization, RBAC, legal-review items), SCHOOL_AND_COMMUNITY_DELIVERY_
ARCHITECTURE.md (organization/location mapping, explicitly not multi-tenancy),
PHASE7_PRODUCT_ARCHITECTURE.md (phase summary).

Updated: WEBSITE_ENROLLMENT_INTEGRATION.md (documented future offering/registration flow,
current live contract unchanged), platform/README.md (test list).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 22:44:13 -05:00

9.5 KiB

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.

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).