Workflow: Family Onboarding
Technical reference for the shared family onboarding aggregate. This module is currently projection-first: it centralizes readiness and next-step decisions used by parent and coordinator surfaces, even though it does not yet own many write commands.
Current scope
Family onboarding is active as a shared aggregate + projection layer. It currently answers questions such as whether the family has an address, whether any child exists, and which child IDs are ready for posting. It is not yet a heavy mutation workflow like the session or posting modules.
Module Contract
| Concern | Implementation | What it does |
|---|---|---|
| Workflow module | workers_nivaa/src/lib/workflows/family-onboarding/ | Owns the parent + child readiness aggregate and normalized family projection. |
| Aggregate | types.ts + repository.ts | Loads the parent account plus each child and linked student-profile intake readiness. |
| Projection | guards.ts + projections.ts | Calculates has_family_address, has_any_child, ready_child_ids, and next_step. |
| Command surface | commands.ts | Currently exposes observation only; mutation routes still live elsewhere. |
| Effects | effects.ts | Packages the aggregate into the shared workflow result contract for API consumers. |
Consumers
| Surface | How it uses the workflow | What changes because of it |
|---|---|---|
GET /profile/parent/me | Returns family_onboarding projection alongside parent particulars. | Parent workspace can show the next family onboarding step without duplicating readiness logic. |
GET /children/overview | Annotates each child with ready_for_posting based on the aggregate. | Parent and admin child lists can distinguish intake-complete vs not-ready children consistently. |
POST /postings | Uses requireFamilyOnboardingAggregate(), assertChildOwnedByParent(), and assertChildReadyForPosting(). | Parents cannot create a posting for a child that is missing intake readiness. |
| Admin parent detail | Reads the same family projection. | Coordinator sees family readiness using the same rules as the parent-facing UI. |
Detailed Lifecycle
| Step | Trigger | Workflow evaluation | Result | Impacted next action |
|---|---|---|---|---|
| 1. Parent account exists | Parent signs up or is imported | Aggregate checks users.address_data and basic parent identity fields. | Projection can still report has_family_address = false. | Parent is steered toward family profile completion. |
| 2. Child profile exists | Parent or coordinator creates a child row | Aggregate now has at least one child but still checks student-profile intake linkage. | Projection may show has_any_child = true but no ready_child_ids yet. | Parent can continue child intake. |
| 3. Student profile intake captured | Child intake/readiness flow creates or updates student_profiles requirements. | Aggregate marks that child hasIntake = true and readyForPosting = true when the required fields are present. | Child appears in ready_child_ids. | Parent can create a posting for that child. |
| 4. Posting request attempted | Parent submits /postings | Posting workflow calls family-onboarding guards before any posting write occurs. | If the child is not ready, the route fails before inserting a posting row. | Parent must finish intake before retrying. |
| 5. Parent and coordinator dashboards render | Any read of profile/overview/admin parent detail | Consumers receive the same normalized next-step model from one place. | No state drift across parent/admin views. | Subsequent onboarding guidance stays consistent across the product. |
Tables And Fields Touched
users: parentdisplay_name,phone,address_data,created_at.child_profiles:id,display_name, level/school/curriculum,deactivated_at.student_profiles: intake requirement fields used as the readiness signal for posting eligibility.