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]>
152 lines
9.5 KiB
Markdown
152 lines
9.5 KiB
Markdown
# 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).
|