Ms. Okafor · Riverbend Elementary · Grade 3

Delivery plan

Development roadmap

Six sprints, roughly twenty-two weeks. The order is deliberate: identity and the student record come first because every later surface is a view onto them, and district intelligence comes last because aggregate analytics are only trustworthy once the documentation loop is real. Every sprint ships something a school can actually use.

A real educator can sign in, land in their district and school, and see a shell whose navigation already matches their role. Nothing is mocked below the UI layer.

Features

  • Email/password and Google sign-in with district email domain hinting
  • Onboarding: accept invitation, confirm school assignment, set display name
  • Role-aware navigation and scope switcher (district → school → caseload)
  • Command palette (⌘K) and Copilot launcher (⌘J) wired to real routes
  • Notification center shell with unread counts
  • Dark mode, reduced motion, and WCAG AA focus states as platform defaults

Pages

  • /authSign in / sign upnew
  • /onboardingFirst-run setupnew
  • /Dashboard (role router)upgrade
  • /settingsProfile & preferencesupgrade
  • /settings/usersUsers & rolesnew
  • /settings/schoolsSchools & screening windowsnew

Components

  • AppShell, SidebarNav, ScopeSwitcher, RoleBadge
  • CommandPalette, CopilotLauncher, NotificationCenter
  • ds/primitives: Text, IconChip, StudentAvatar, Meter, EmptyState, Skeletons
  • PermissionGate — hides only what the policy would reject

Database

  • districts, schools, profiles
  • user_roles (separate table — never a column on profiles)
  • permissions, role_permissions, staff_school_assignments
  • Security-definer helpers: has_role, is_staff, current_district, can_access_school
  • Indexes on (user_id), (district_id), (school_id) for every scope lookup

Backend

  • createServerFn: getMyProfile, getMyScopes, listSchools, inviteStaff, assignRole
  • Bearer-token middleware registered in src/start.ts
  • Audit write helper invoked on every role grant and scope change

Authentication & access

  • Managed cloud auth with leaked-password (HIBP) protection enabled
  • No anonymous sign-up; staff arrive by district invitation only
  • Protected routes under _authenticated/; public marketing and auth routes stay SSR
  • Session-scoped RLS — the app never trusts a role sent from the client

AI

  • Copilot shell with context awareness (current route, current student)
  • Intent router + guardrail: refuse anything outside the user's RLS scope

Testing

  • RLS policy suite: for each role × each table, assert allow and deny
  • Cross-tenant leak test — user in District A cannot read District B by ID
  • Playwright: sign in → land on correct role dashboard → sign out clears cache
  • Axe accessibility pass on shell, nav, and auth screens

Dependencies

  • None — this is the base layer

Definition of done

  • Every table created in this sprint has GRANTs, RLS enabled, and at least one deny test
  • A teacher, coordinator, admin, district admin, and parent account each land on a correct, differently-scoped shell
  • Zero security-linter warnings
  • Keyboard-only traversal of the shell works; contrast verified at AA

Risk & mitigation

Role sprawl: ten roles is a lot. Mitigated by the specialist archetype — four specialist roles share one permission set with a domain filter.

Always-on standards

These apply to every sprint. They are merge requirements, not aspirations.

Design

  • No legacy-SIS patterns: no dense grey grids, no modal chains, no 40-field forms
  • Every screen states the next best action above the fold
  • AI output is always labeled, sourced, and editable — never silently authoritative
  • Typing is a fallback; chips, defaults, and drafts are the primary path

Security

  • RLS is the only authorization boundary; the UI merely mirrors it
  • Service-role access only for verified webhooks and admin jobs
  • Every student-data access is audited and immutable
  • Zero security-linter warnings is a merge requirement

Performance

  • Cursor pagination everywhere; no unbounded selects
  • Route-level code splitting; interaction to next paint under 200ms
  • Time-series reads always hit a (student_id, date DESC) index

Quality

  • Every table ships with allow and deny policy tests
  • Every workflow ships with a Playwright happy path and one failure path
  • Axe clean at WCAG 2.1 AA on every new surface
  • Practitioner review before any AI feature is enabled by default