31 KiB
Full Integration Refactor Plan
Purpose
This document is the active cross-application backlog for integrating frontend/, backend/, and the PostgreSQL database.
It must track only unfinished integration work and hard implementation rules. Completed migration history belongs in the feature-specific docs under backend/docs/, frontend/docs/, and supporting audit docs.
When an item is completed, remove it from this plan and update the owning feature documentation instead of adding a completed checklist entry here.
Current Application Shape
frontend/is the only product frontend.backend/is the source of truth for auth, tenant ownership, roles, permissions, users, staff profiles, campuses, persisted workflows, content catalogs, email, files, and database access.ref-frontend/is a temporary generated reference. It is not runtime code and must be deleted after all needed endpoint-contract reference value is exhausted.
Non-Negotiable Rules
- No frontend persisted workflow may use mock, sample, seed, or fallback records.
- No frontend runtime code may import backend seed files.
- Frontend runtime constants may contain only UI config, route/module metadata, query keys, timing values, style tokens, static UI labels, and intentionally static product copy.
- Backend seeds are for database seeding only.
- Frontend seed data is allowed only in tests or
frontend/src/test-seeds/. - Backend owns tenant scoping and permissions. Frontend route hiding is UX only.
- All frontend backend calls go through typed modules in
frontend/src/shared/api/. - New frontend workflows must follow
View -> Business Logic -> API/Data Access -> Backend. - View components must not import
frontend/src/shared/api/. - Business logic must not import view components.
- API modules must not import business or view code.
- All imports use the
@alias. - No
any, unsafe casts, disabled TypeScript rules, disabled ESLint rules, compatibility bypasses, silent failures, or legacy re-export surfaces. - Frontend auth must use backend-owned HttpOnly cookies only. Access and refresh tokens must never be stored in frontend browser storage.
- Secrets live only in backend
.envor deployment environment variables. Frontend env values must be public browser-safe values only. - New backend modules must include migration/model/service/route, tenant and role enforcement, docs, and focused verification.
- Every changed workflow must update the relevant docs before it is considered complete.
Current Verification Baseline
Frontend current baseline:
npm run typecheckpasses.npm run lintpasses.npm run testpasses with 51 files and 198 tests.npm run buildpasses.npm run test:e2epasses with 4 backend-free Playwright smoke tests.npm run test:e2e:contentexists for backend-seeded content catalog integration tests. It requires backend migrations, backend seeders, and a running backend server.
Backend current baseline:
- Runtime code is 100% TypeScript + native ESM; the JS->TS / CJS->ESM migration is complete.
npm run typecheck(tsc --noEmit) passes.npm run lint(eslint .) passes with no broad ignores.npm test(node --testviatsx) passes: 2 files, 15 tests (error-handler + import-boundaries).npm run verify(typecheck + lint + test) is the combined gate and is green.npm run build(tsc+tsc-alias -f+ email-template asset copy) produces a runnabledist/.- Migrations/seeders run via Umzug (
npm run db:migrate,npm run db:seed); a run against the configured local database is still pending (Workstream 1).
Active Workstreams
1. Backend Migration And Runtime Verification
Status: open.
Problem:
Backend product modules exist, but the configured local database migration/seed run has not been verified as a passing gate in this plan.
Required work:
- Run
npm run db:migrateagainst the configured local database. - Run
npm run db:seed. - Start backend with the documented env file.
- Verify public content catalog routes, auth routes, and product module routes respond.
- Record exact failing migration/seed/runtime errors if any.
Acceptance criteria:
npm run db:migratepasses.npm run db:seedpasses.- Backend starts without generated default secrets.
GET /api/public/content-catalog/:contentTypeworks for the required seeded content catalog types.- Any remaining backend runtime blocker is captured as a specific follow-up with file/error references.
2. Tenant Boundary Audit And Tests
Status: open and high priority.
Problem:
Generated backend code has partial tenant scoping. Multi-tenant correctness cannot rely on frontend filtering.
Required work:
- Audit all tenant-owned generated and product routes for organization scoping.
Resolve inconsistent— done: unified onorganizationsIdversusorganizationIdusageorganizationIdfor all models (renamed theusers.organizationsIdcolumn via migration20260609000000-rename-users-organizationsid-to-organizationid, updated db/api scoping, the auth DTO, and the frontendCurrentUsertype). Verify the tenant-scopingwherenow correctly targets each model'sorganizationId.- Ensure create/update/delete paths cannot accept another tenant's organization or campus.
- Ensure list/count/autocomplete endpoints are tenant-scoped.
- Add backend tests proving cross-tenant records are not visible or mutable.
Acceptance criteria:
- Tenant isolation tests cover users, campuses, staff, students, attendance, messages, documents, and product module tables.
- A non-global user cannot read or mutate another tenant's data.
- Campus-scoped users cannot mutate another campus unless their backend permission explicitly allows it.
3. Role Model Decision
Status: open.
Problem:
Backend currently maps generated roles to product roles. It is not yet a final product decision whether this mapping is the supported model or product roles should become first-class persisted backend roles/permissions.
Required decision:
- Option A: keep generated-role to product-role mapping as the supported model.
- Option B: create first-class persisted product roles:
Teacher,Para,Office Manager,Director,Superintendent.
Required work after decision:
- Document the chosen role model.
- Align
/api/auth/mepermissions and product role payload with the chosen model. - Add backend role/permission tests for staff, director, and superintendent paths.
- Keep frontend navigation driven by backend role/permissions.
Acceptance criteria:
- Role model is documented in backend docs.
- Backend tests prove role access for product modules.
- No frontend role grants capability that backend does not enforce.
4. Product Onboarding Contract
Status: blocked by customer decision.
Problem:
Generated auth signup/profile endpoints exist, but they do not define the product workflow for company creation, campus creation, user creation, staff profile creation, role assignment, campus assignment, or profile updates.
No-go rules:
- Do not implement frontend registration.
- Do not implement profile creation/editing UI.
- Do not treat generated signup/profile endpoints as the product onboarding contract.
- Do not add temporary compatibility paths.
Required customer decisions:
- Who creates a company.
- Who creates campuses.
- Who invites or creates users.
- How staff profiles are created and linked to users.
- How roles and campuses are assigned.
- Which profile fields users may update themselves.
- Which profile fields require director/superintendent/admin permissions.
Required work after decision:
- Add backend onboarding contract and docs.
- Add tenant-scoped backend workflows.
- Add frontend typed API modules.
- Add frontend business hooks.
- Add UI only after backend contract exists.
- Add authenticated Playwright workflows using backend-seeded fixtures.
Acceptance criteria:
- Onboarding workflow is customer-approved and documented.
- Backend owns all creation, assignment, and permission checks.
- Frontend only exposes flows backed by typed backend contracts.
5. Refresh Token Maintenance
Status: open.
Problem:
Cookie auth and refresh rotation exist, but scheduled cleanup for expired/revoked refresh-token rows remains unresolved.
Required work:
- Define refresh-token retention period.
- Add cleanup job or operational command.
- Ensure cleanup is observable.
- Document operational usage.
Acceptance criteria:
- Expired and revoked refresh-token rows are cleaned after the approved retention window.
- Cleanup failures are visible and not silent.
- Auth behavior remains unchanged for valid sessions.
6. API Documentation Hardening
Status: open.
Problem:
Markdown docs exist for migrated modules, but Swagger/OpenAPI coverage for product-specific and cookie-session endpoints is incomplete.
Required work:
- Document
/api/auth/signin/local. - Document
/api/auth/refresh. - Document
/api/auth/signout. - Document
/api/auth/me. - Document product module endpoints that are not covered by generated Swagger output.
- Document response and error shapes per endpoint.
Acceptance criteria:
- API docs match actual route payloads and response shapes.
- Cookie auth behavior is explicit.
- Frontend API contract tests remain aligned with docs.
7. Policy And Safety Acknowledgment Persistence
Status: open pending product contract.
Problem:
Policy content is document-backed, but policy/protocol acknowledgments are not yet persisted.
Required decisions:
- Which policies/protocols require acknowledgment.
- Which roles must acknowledge.
- Whether acknowledgments are per document version.
- Who can report acknowledgment status.
Required work after decision:
- Add acknowledgment backend model/migration/service/route.
- Enforce tenant and role scope.
- Add frontend typed API and business workflow.
- Add report views only if required.
Acceptance criteria:
- Acknowledgments survive reload.
- Acknowledgment status is tenant-scoped.
- Unauthorized roles cannot view individual acknowledgment records.
8. Attendance Source Contracts
Status: partially open.
Current state:
- Campus attendance daily aggregate summaries are implemented for the current UI.
- Staff attendance snapshot/reporting is read-only.
- Student/class attendance source-of-truth workflow is not defined.
Open decisions:
- Whether campus attendance aggregates are manually entered, imported, or derived from student attendance records.
- Which external or internal source owns staff attendance writes/imports.
- Whether student-level attendance UI is required.
Required work after decision:
- Add write/import endpoints only after source contract exists.
- Keep derived summaries server-side if summaries are derived.
- Add backend tests for source-of-truth calculations.
- Add frontend workflows only after backend contracts exist.
Acceptance criteria:
- Attendance source of truth is documented.
- UI values can be traced to backend records or server-side derivation.
- No frontend-only attendance source remains in persisted workflows.
9. Generated Audio Provider Contract
Status: open pending provider decision.
Problem:
Classroom timer uses built-in Web Audio sounds. AI-generated audio UI is intentionally not exposed because no backend audio provider contract exists.
No-go rules:
- Do not add generated audio UI.
- Do not call external audio providers from frontend runtime.
- Do not add frontend API keys or provider secrets.
Required work after decision:
- Define backend provider contract.
- Keep provider secrets in backend env.
- Add backend service with native provider errors where required.
- Add typed frontend API and explicit loading/error states.
Acceptance criteria:
- Generated audio is backend-mediated.
- No provider secret reaches the browser.
- Provider failures remain visible.
10. File Upload And Download Permissions
Status: open.
Problem:
Backend file and document routes exist. Product file workflows need explicit permission verification before new upload/download UI is added.
Required work:
- Audit upload permissions.
- Audit download permissions.
- Verify tenant and document ownership checks.
- Add typed frontend upload client only for approved workflows.
Acceptance criteria:
- Unauthorized users cannot access another tenant's files.
- Upload/download behavior is documented and tested before new UI is added.
11. Public Backend Route Audit
Status: open.
Problem:
Public routes must be intentionally public. This includes public content catalog routes and any generated/template public integrations.
Required work:
- List all routes without auth middleware.
- Mark each as public-by-design or requiring auth.
- Add auth where required.
- Document intentionally public routes.
Acceptance criteria:
- Every unauthenticated backend route is documented.
- No tenant-owned data is exposed through accidental public routes.
12. Backend-Seeded Authenticated E2E
Status: blocked by onboarding/profile fixtures.
Problem:
Current Playwright coverage includes backend-free smoke tests and backend-seeded content catalog tests. Authenticated persisted workflows need backend-seeded users, roles, campuses, staff profiles, and known credentials.
Required prerequisites:
- Product onboarding/profile contract.
- Backend-seeded auth fixtures.
- Tenant-scoped campus/staff fixtures.
- Stable test credentials stored only in ignored local env or dedicated test seed config.
Required workflows after prerequisites:
- Login to dashboard.
- Director creates or edits a FRAME entry and sees it after reload.
- Staff completes QBS quiz and director/superintendent sees compliance.
- Office enters campus attendance and superintendent sees aggregate.
- Director submits walkthrough and sees summary update.
- Staff marks a sign learned and progress persists after reload.
Acceptance criteria:
- Authenticated e2e tests use backend seeds, not frontend mock data.
- Tests do not require production secrets.
- Tests are documented and repeatable.
13. Accessibility Test Coverage
Status: open.
Required work:
- Add axe/Playwright accessibility checks for login.
- Add axe/Playwright checks for dashboard.
- Add checks for sidebar navigation.
- Add checks for modal dialogs.
- Add checks for forms with validation.
- Add checks for tables/reports.
Acceptance criteria:
- Accessibility tests run in a documented command.
- Critical violations block completion of the refactor.
14. ref-frontend/ Removal
Status: open.
Required work:
- Confirm no active docs require
ref-frontend/for endpoint contract reference. - Confirm no scripts import or run
ref-frontend/. - Delete
ref-frontend/. - Update root docs and any setup docs.
Acceptance criteria:
- Only
frontend/andbackend/remain as active application code. - No docs describe
ref-frontend/as needed for normal development.
15. OAuth Provider Strategy Modernization
Status: open. Deferred until after the backend TypeScript/ESM migration (now complete), to keep auth-flow changes separate from the language/module migration.
Problem:
Social login uses passport-google-oauth2 (0.2.0, last published 2022) and passport-microsoft (2.1.0). The Google strategy is low-maintenance and should be modernized, ideally consolidating both providers on a single maintained OAuth/OIDC library.
Required work:
- Choose target: minimal swap to
passport-google-oauth20, or consolidate both providers onopenid-client. - Replace the Google (and optionally Microsoft) passport strategy in
backend/src/auth/auth.ts. - Keep the existing cookie-based session and JWT issuance unchanged.
- Verify callback URLs, scopes, and the social-signup user flow end to end.
- Update auth docs (
backend/docs/auth-profile.md,cookie-auth.md).
Acceptance criteria:
- Google and Microsoft sign-in work end to end with the chosen library.
- No deprecated/unmaintained OAuth strategy remains in dependencies.
- Cookie/JWT auth behavior is unchanged for existing sessions.
- This change is isolated from the language/module migration (separate PR/task).
16. Permission-Based Frontend Authorization
Status: open.
Problem:
The backend already authorizes every request by permission, not by role. backend/src/middlewares/check-permissions.ts exposes checkCrudPermissions(entity), which derives a permission name ${METHOD}_${ENTITY} (e.g. READ_CAMPUSES, CREATE_USERS, DELETE_ROLES via METHOD_MAP) and grants access when any of the following holds, in order: (1) self-access — currentUser.id === req.params.id/req.body.id; (2) the user has a direct custom_permissions entry with that name; (3) the user's app_role (or the Public role for unauthenticated/no-role requests) has that permission via role.getPermissions(). An admin can therefore create roles and attach permissions (Roles ↔ Permissions many-to-many through RolesDBApi.setPermissions), plus assign per-user custom_permissions.
The frontend, however, does not gate on permissions. Module/page access is currently decided by productRole (teacher | para | office | director | superintendent) in frontend/src/business/app-shell/selectors.ts (canAccessModule(modules, moduleId, userRole)), and per the architecture contract frontend route hiding is UX-only. The user DTO already carries a permissions: string[] array (built by getPermissionNames in services/auth.ts), but the frontend ignores it. This makes the frontend authorization model inconsistent with the backend: a role with customized permissions is correctly enforced by the backend but not reflected in what the UI shows or allows.
Goal: make the frontend gate UI affordances (routes/menu items/buttons/request triggers) by permission — using the same ${METHOD}_${ENTITY} permission names the backend checks — while keeping the backend as the sole source of truth (frontend gating stays UX-only; it must never be the only enforcement).
Required work:
- Expose permissions reliably in the auth contract. Confirm
GET /api/auth/meand the sign-in response include the resolvedpermissions: string[](effective permissions = role permissions ∪custom_permissions). Addcustom_permissionsto the resolution if not already merged. Document the exact field inbackend/docs/auth-profile.md. - Define a shared permission vocabulary. Add a typed catalog of permission names on the frontend (
frontend/src/shared/auth/permissions.ts) mirroring backend naming${METHOD}_${ENTITY}(READ/CREATE/UPDATE/DELETE × entity). Keep it inshared/constants-style UI config; do not import backend code. - Add a permission selector/hook. Implement
hasPermission(user, permissionName)andhasAnyPermission/hasAllPermissionsinfrontend/src/business/auth/(pure functions overCurrentUser.permissions), plus ausePermissions()hook for components. Include the self-access nuance only where relevant (the backend allows self-access regardless of permission). - Gate routes by permission. Replace/augment role-based
canAccessModulewith permission-based checks. Each route/module declares the permission(s) it requires; the router redirects/hides when the user lacks them. KeepproductRoleonly where a true role concept is still needed (e.g. dashboards), not for resource access. - Gate UI affordances. Hide or disable create/edit/delete buttons and other action triggers based on the matching permission (e.g. hide "Add campus" without
CREATE_CAMPUSES). Avoid firing requests the backend will reject with 403. - Handle 403 consistently. Ensure the API layer surfaces backend
forbiddenresponses to a single handler (toast + no crash), so permission drift between UI and backend degrades gracefully. - Admin UI for roles/permissions. Verify the roles management screens let an admin create a role, attach/detach permissions, and assign
custom_permissionsto a user — backed by the existingroles/permissions/usersendpoints. - Tests. Unit-test
hasPermission/selectors with permission fixtures; add route-guard tests proving a user withoutREAD_Xcannot open module X; add a backend-seeded e2e proving UI affordances match backend enforcement for at least one CRUD entity. - Docs. Update
frontend/docs/frontend-architecture.mdandbackend/docs/auth-profile.mdto describe the permission-based frontend model and the${METHOD}_${ENTITY}contract.
Acceptance criteria:
- Frontend route/menu/affordance visibility is driven by the user's effective permissions, using the same names the backend enforces.
- The backend remains the sole authority: removing a frontend check never grants real access (backend still returns 403).
- An admin can create a role, assign permissions, and the change is reflected in both backend enforcement and frontend UI for affected users.
productRole-based gating is removed for resource access (kept only for genuine role-specific UI), with no remaining role↔permission inconsistency.- Frontend
typecheck,lint,testpass; a seeded e2e proves UI/permission alignment for at least one entity.
17. API Surface Coverage And Dead-Endpoint Decision
Status: open (analysis complete; decision pending).
Problem:
The backend exposes the full Flatlogic-generated CRUD surface for all 39 models plus template auth/file/search routes, but the product frontend only consumes a small set of custom feature endpoints. The unused surface is dead code and attack surface; it must be either pruned or intentionally wired.
Method (how this was established):
- Enumerated every backend route from
backend/src/routes/*plus its mount prefix inbackend/src/index.ts. - Enumerated every frontend HTTP call. All frontend HTTP goes through
frontend/src/shared/api/*overhttpClient(fetch); there are no strayfetch/axios/XMLHttpRequestcalls elsewhere, so the frontend call list is exhaustive.
Findings:
- Frontend is fully wired: every
shared/apimethod targets a real, mounted backend endpoint. There are 0 orphan/broken frontend calls. - Backend is only partially consumed: of ~278 endpoints, the frontend uses ~35 (~13%); ~243 (~87%) have no frontend consumer.
- The mismatch is one-directional: the frontend calls nothing extra; the backend carries a large unused layer.
Consumed endpoints (the only wired surface — 35):
auth(4 of 16):POST /api/auth/signin/local,GET /api/auth/me,POST /api/auth/refresh,POST /api/auth/signout.campuses(1):GET /api/public/campuses(the authenticated/api/campusesCRUD is NOT used).content-catalog(6):GET /api/public/content-catalog/:contentType,GET /api/content-catalog,GET /api/content-catalog/:contentType,POST /api/content-catalog,PUT /api/content-catalog/:contentType,DELETE /api/content-catalog/:contentType.campus_attendance(4):GET /configs,PUT /configs/:campusKey,GET /summaries,PUT /summaries/:campusKey/:date.communications(4):GET /parent-messages,POST /parent-messages,GET /events,POST /events.frame_entries(3):GET /,POST /,PUT /:id.personality_quiz_results(3):GET /me,PUT /me,GET /distribution.safety_quiz_results(2):GET /,POST /.staff_attendance(2):GET /records,GET /summary.user_progress(3):GET /,POST /,DELETE /by-item.walkthrough_checkins(3):GET /,POST /,DELETE /:id.
Unused backend endpoints (no frontend consumer):
- Generic CRUD template — 25 route groups × 9 endpoints = 225, none called:
academic_years,assessments,assessment_results,attendance_records,attendance_sessions,campuses(the/api/campusesCRUD),classes,class_enrollments,class_subjects,fee_plans,grades,guardians,invoices,message_recipients,messages,organizations,payments,permissions,roles,staff,students,subjects,timetable_periods,timetables,users. Each exposes the identical shape:POST /,POST /bulk-import,PUT /:id,DELETE /:id,POST /deleteByIds,GET /,GET /count,GET /autocomplete,GET /:id. authextras — 12, not called:POST /api/auth/signup,PUT /api/auth/profile,PUT /api/auth/password-reset,PUT /api/auth/password-update,POST /api/auth/send-password-reset-email,POST /api/auth/send-email-address-verification-email,PUT /api/auth/verify-email,GET /api/auth/email-configured,GET /api/auth/signin/google(+/callback),GET /api/auth/signin/microsoft(+/callback).file— 2, not called:GET /api/file/download,POST /api/file/upload/:table/:field(the frontend never uploads;DocumentMutationDtohas nofile).search— 1, not called:GET /api/search.
Decision (owner, recorded): the generic CRUD layer is WIRE — kept, not
pruned. These ~24 groups are not dead code; they will be used and integrated
with the frontend later (likely modeled on ref-frontend). No generic CRUD group
is to be removed. The auth/file/search extras (items 2–4 below) remain
individually decision-gated.
Required work:
- Generic CRUD groups: WIRE (decided above) — build the frontend that uses
them (e.g. real
students/staff/guardians/invoicesmanagement) and bring each group up to the target backend architecture. Do not prune them. authextras: keepsignup/password-reset/verify-email only if onboarding/recovery is in scope (see Workstream 4); otherwise prune. OAuth callbacks tie to Workstream 15.file: keep only if document/avatar upload is on the roadmap (ties to Workstream 10); otherwise prune.search: prune unless a search UI is planned.- After any prune, re-run backend
typecheck/lint, and regeneratedatabase-schema.mdonly if models change.
Cross-references: Workstream 10 (file upload), 11 (public route audit), 15 (OAuth), 16 (permission-based authorization).
Acceptance criteria:
- Every backend endpoint is classified as used, prune, or wire (planned), with no “unclassified” remainder.
- Pruned routes are removed cleanly (route + service + db/api + permission wiring) with
typecheck/lintgreen and no dangling imports. - The frontend remains fully wired (0 orphan calls) after changes.
- This document reflects the final surface.
Performance hardening applied (owner chose wire, not prune, for the generic CRUD layer — it will back near-term management UIs modeled on ref-frontend):
findByin all 24 generic-CRUDdb/api/*.tsnow loads its associations via a singlePromise.allinstead of sequential awaited getters (detail-endpoint latency drops from sum to max of the association queries).users.findBywas done earlier.- List pagination now has shared defaults via
@/shared/constants/pagination(resolvePagination, default page size 10 to match theref-frontendgrids, capped at 100). Applied to every generic-CRUDfindAlland to the feature lists (user_progress,safety_quiz_results,walkthrough_checkins,frame_entries,content_catalog,communicationsparent-messages/events,campus_attendanceconfigs), which were reverted fromfindAllback tofindAndCountAllsocountis the true total for the pager.staff_attendance /recordsandcampus_attendance /summarieskeep their pre-existing per-endpoint limits. - Fixed a latent CRUD tenant-scoping bug: routes and
db/apicreate/update readcurrentUser.organization?.id(singular), butfindByonly ever populatesorganizations(plural) + theorganizationIdscalar, so that read was alwaysundefined(non-global creates silently set no organization). All 22db/api+ 24 route reads now usecurrentUser.organizationId; the dead 3-field fallback term was dropped from the 4 feature services; and the non-existentorganization?: { id }field was removed from theCurrentUsertype so the mistake cannot recur. - The redundant existence-check
findByin the 22 CRUDupdateservices was removed; they now rely onDbApi.updatereturningnull(avoids loading every association just to validate existence).removealready had no pre-check. - The per-request
UsersDBApi.findBy(passport JWT strategy →req.currentUser, read by every guarded route) was collapsed fromfindOne+ 4 parallel association getters + agetPermissions()into a single eager-loaded query (findOne+includeofapp_role+permissions,staff_user,custom_permissions,organizations). Its returnedapp_rolenow carriespermissions, andmiddlewares/check-permissions.tswas reordered to read that eager-loadedpermissionsarray before falling back togetPermissions()— removing the extra per-request permissions query. Same returned shape/fields as before (AuthenticatedUser); ~6–7 queries per request → 1. The cachedPublicrole still works (its record carriespermissions). AuthService.currentUserProfile(theGET /me, signin and refresh responses) now uses a dedicatedUsersDBApi.findProfileById— a single eager-loaded query (findByPk+ scopedinclude/attributes) returning only the columns and relations the profile DTO reads — instead of the heavy genericfindBy(1findOne+ parallel association getters +getPermissions) plus a separategetCampus. Required idiomaticNonAttributeassociation declarations on theUsers/Roles/Staffmodels so the include is type-safe without casts. The per-request passportfindBythat populatesreq.currentUseris unchanged (it is the auth gate read by every guard);/mestill performs that auth fetch plus this one lean profile query.
Strict Implementation Sequence
Use this order unless the user explicitly reprioritizes:
- Backend migration and seed verification.
- Tenant boundary audit and tests.
- Role model decision and enforcement tests.
- Public route audit.
- API documentation hardening.
- Product onboarding after customer decision.
- Authenticated backend-seeded e2e after onboarding/profile fixtures exist.
- Remaining optional product contracts: acknowledgments, attendance imports, generated audio, file upload/download UI.
- Accessibility test coverage.
- Remove
ref-frontend/. - OAuth provider strategy modernization.
- Permission-based frontend authorization (align frontend gating with backend permission checks).
- API surface coverage decision: classify every endpoint and prune or wire the unused backend layer (Workstream 17).
Definition Of Done
The integration refactor is complete only when all of the following are true:
- Backend migrations and seeders pass on the configured local database.
- Backend lint passes without broad ignores.
- Backend tenant isolation tests prove cross-tenant data is not visible or mutable.
- Backend role/permission tests cover product staff, director, and superintendent paths.
- Every unauthenticated backend route is documented as public-by-design.
- Product onboarding contract is customer-approved or explicitly excluded from release scope.
- Every visible frontend workflow is backed by a typed backend API contract.
- No frontend runtime workflow depends on mock, sample, seed, or fallback records for persisted behavior.
- Frontend
typecheck,lint,test,build,test:e2e, and documented seeded e2e suites pass in their required environments. - Backend auth/API docs match actual route behavior.
- Required accessibility checks pass.
ref-frontend/is deleted or a dated, explicit exception explains why it is still needed.