-
Notifications
You must be signed in to change notification settings - Fork 1
Roadmap
RubricMaker is an offline-first rubric creation and grading tool for educators. This roadmap lists completed phases first (newest at the top of that section is the most recently shipped), then planned work.
- β Rubric builder with multiple scoring modes (Total Points, Weighted, Single-Point)
- β Interactive grading interface with score modifiers
- β CEFR framework integration (Can-Do statements)
- β Student self-assessment & peer review
- β Essay assignments with rich text editor
- β Analytics dashboard with class statistics
- β Multi-format export (PDF, DOCX, CSV)
- β Student portal with shareable links
- β Offline-first architecture with localStorage
- β Basic theme customization
Phase 1: Cloud Sync & Collaboration (PR #128)
- β
Sync conflict resolution β
src/utils/syncMerge.ts, remote-as-baseline merge with LWW on rubrics, wired into all three hydration sites (startup, reconnect, OTP sign-in) - β
Cambridge English framework β CEFR β exam mapping (
src/data/cambridgeExams.ts), opt-in "B2 Β· FCE" badges, optional Cambridge Dictionary API vocabulary lookups - β
Theme bundles β 5 one-click presets (
src/data/themes.ts), tonal accent scale viacolor-mix(), 5 decorative export fonts for PDF/DOCX
Phase 2: Infrastructure Stabilization (PR #131 + wiki updates)
- β
2.1 Multi-device e2e β second-browser-context Supabase fixture, propagation/LWW-race/network-partition specs (
e2e/specs/18-multi-device-sync.spec.ts) - β
2.2 LWW extended to every editable collection (rubrics, grades, peer reviews, classes, students, grade scales, comment snippets/bank, self-assessments, speaking sessions, analysis results); add/delete-only collections (
attachments,exportTemplates,favoriteStandards) intentionally left on pending-queue protection only - β 2.3 Wiki documentation pass β Cambridge + theming guides added to Features.md, conflict-resolution strategy documented in Supabase-Sync.md
- β 3.1 Speaking session recordings β IndexedDB blob store, local-only audio, DB-gated video, optional cloud sync with cascade delete
- β
3.2 Vocabulary profiling dashboard (
/vocabulary) β per-student/class CEFR distribution charts, open CEFR word lists, CSV export - β
3.3 Peer review analytics (
/peer-analytics/:rubricId) β consistency scoring, leniency bias, inter-rater spread, comment heatmap - β
3.4 Full testing environment (
/tests,/test/:code) β MC/short-answer/open questions, auto-scoring, class-average adjustment, live proctoring monitor
Phase 3.5: Hot Text Questions & Standalone Essays Workspace (PR #140)
- β
Hot-text question type β
src/utils/clozeParse.tsparses{{cloze}}and[[hot-text]]syntax; scoring intestCalc.ts - β
Standalone
/essaysworkspace (parallel to/tests) β prompt editor, rubric connector, per-student share links, submission-code import, reusable assignment templates (PR #143)
- β 4.1 Expanded statistics β multi-class Compare view, trend overlay, rule-based insights, track/year filters
- β
4.2 Activity dashboard (
/activity-dashboard) β rubric/test/essay Γ class grid, assign-from-view, submission count badges - β
4.3 Admin hardening β
teacher/admin/observer roles + RLS, pg_cron data retention, student soft-delete archive (restore/anonymize), audit log with CSV export - β 4.4 SIS roster sync (partial) β CSV upsert + optional roster-removal sync; native Magister API integration still reserved for community contribution
- β
4.5 Self-hosting ops β
docs/SELF_HOSTING_OPS.md, HestiaCP/Virtualmin guides
- β 5.1 Mobile grading β touch stepper alongside sliders, single-column ComparativeGrading reflow <768px, iPad CSS, mobile e2e
- β
5.2 WCAG 2.1 AAA β
src/utils/contrastCheck.ts+ enforcement test, accessible modals with focus trap/restoration, keyboard drag-and-drop, i18n'd skip link - β
5.3 Help & documentation β page tours (Joyride) on 12 pages total, contextual tooltips (
src/components/ui/HelpPopover.tsx) for score modifiers/LWW sync/proctoring flags - β a11y test coverage β RubricBuilder/GradeStudent/ComparativeGrading added to the jest-axe page suite (PR #152, closing the Phase 5.2 follow-up)
- β Route-shaped skeleton loading for lazy routes and async actions (PR #153)
Phase 6: Strategic Features (PR #157)
Scoped down from the original proposal before implementation β see "Deferred from Phase 6" below for what was cut and why.
- β
6.1 Student Learning Paths β rule-based (no AI/LLM) rubric recommendations from CEFR-skill cohort gaps, consecutive-low-score intervention flagging,
/students/:id/learning-path(src/utils/learningPathAggregator.ts) - β
6.2 Rubric Marketplace (school-scoped) β publish/browse/clone/upvote within a school via the existing
schools/school_memberstables,/marketplace(supabase/migrations/040_rubric_marketplace.sql,src/pages/MarketplacePage.tsx) - β 6.3 Integration Ecosystem (CSV import only) β Clever CSV and OneRoster CSV roster-import auto-detection added to the existing CSV import modal; LTI 1.3 LMS integration deferred (see below)
- β
6.4 Test Summary Export β per-question and per-skill/standard strong/weak breakdown, PDF/DOCX export (single + batch) from the test results page, feature parity with the rubric summary export (
src/utils/testSummaryAggregator.ts) - β
6.5 Report Card Improvement β consolidated PDF/DOCX report card per student combining rubric grades, standards coverage, learning goals, CEFR overview, and test summary, with section toggles and single/batch export from the Export page (
src/utils/reportCardAggregator.ts)
Verification baseline (carried through every phase above): typecheck/lint clean, full unit suite green (1755+ tests), i18n parity across en/nl/fr/de/es, e2e suite green on chromium/firefox/webkit/mobile-chrome (pre-existing/environmental flakes tracked separately, not regressions).
Phase 7: Longitudinal & Historical Insight (PR #159)
- β
7.1 Multi-year student history β
Student.pastClassMembershipsrecorded on every class change (manual move via Edit Student, and CSV sync-mode re-import transfers), with a confirmation summary before sync-mode CSV mutates anything; trail rendered on the student profile - β
7.2 Rubric version diffing β "Compare" button per saved version opens a diff against the current rubric (criteria added/removed/changed, title/weight, level points/labels) (
src/utils/rubricVersionDiff.ts,RubricVersionDiffModal.tsx); version history/snapshot/restore already existed - β
7.3 Standards coverage gap analysis β class-level aggregator cross-references every linked standard against what's been graded, split into Assessed/Not yet assessed, surfaced on the Activity Dashboard (
src/utils/standardsCoverageAggregator.ts,ClassCoverageGapPanel.tsx)
Phase 8: Collaboration & Department Workflow (PR #160)
- β
8.1 Co-grading & moderation β second-marker grade reuses the peer-review data model (
StudentRubricwithisPeerReview: true);src/utils/coGradingModerationQueue.tsflags disputes above a configurable point threshold;/moderationlists disputes with a per-criterion delta and keep/accept resolution - β
8.2 Department rubric/comment-bank libraries β
sharedWithSchoolflag on rubrics and comment-bank items, read-only to every teacher in the same school (041_school_sharing.sql, mirrors the Phase 6 marketplace's school_members join pattern) - β
8.3 Grading task assignment β batch-assign a class's ungraded submissions for a rubric to a colleague from the Activity Dashboard (
GradingTasktype,042_grading_tasks.sql); completion is derived, not a stored flag - β
8.4 Manual reordering β drag-and-drop reordering (
src/utils/displayOrder.ts,@hello-pangea/dnd) on RubricList, TestListPage, EssayListPage, the Activity Dashboard, and the class list on the Students page, per teacher. List containers use flexbox-wrap rather than CSS grid, since@hello-pangea/dndcomputes drag displacement from sibling bounding rects and doesn't support CSS grid track layout β grid caused incorrect cross-row drag animation and made it impossible to drop a card between two others in a different row. - β
8.5 Cohort-based filtering β year/track cohort filter on the Rubrics, Tests, and Essays lists (
src/utils/cohortAggregator.ts,src/components/CohortFilter.tsx); a student counts as in-cohort via their current class or any past class frompastClassMemberships, so an item stays visible to a cohort across a class transfer. Manual reordering is disabled while a cohort filter is active. Not added to the Activity Dashboard, since its existing class-level year/track filter already serves the same need for that per-class matrix view.
- β
9.1 RTL readiness β CSS logical-properties audit (
src/index.css): physicalleft/right/margin-left/padding-left/border-right/text-align: leftrules converted toinset-inline-*/margin-inline-*/padding-inline-*/border-inline-*/text-align: start, a no-op in the 5 existing LTR locales. Newsrc/utils/rtlLanguages.ts+ anAppContexteffect setsdir="rtl"on<html>when an RTL language is active β inert today (no RTL locale shipped yet) but ready for one. The mobile sidebar drawer'stransform: translateX()slide animation has no logical-property equivalent and was deliberately left sliding from the left edge only β tracked below as a known gap, not fixed in this pass. - β
9.2 Installable offline PWA β
vite-plugin-pwa(Workbox) generates the manifest + service worker at build time;registerType: 'prompt'(notautoUpdate) so a new build never silently swaps JS under a mid-session sync β the user gets a native confirm prompt (src/pwa.ts) before reloading. Supabase requests (/rest/,/auth/,/realtime/,/storage/,/functions/+ version segment, matched by path so it covers both hosted and self-hosted Supabase) are explicitlyNetworkOnlyin the Workbox runtime-caching config β the service worker must never make a failed sync request look like it succeeded. Works under the existing relativebase: './'sub-path hosting config. Manifest icons:rubric-icon.svg(sizes: any) plus 192Γ192/512Γ512 PNG fallbacks (pwa-192.png/pwa-512.png, rasterized from the same SVG via a one-off Playwright screenshot script β no new dependency) for broader Chrome/Android installability. A dedicatedmaskablepurpose variant is still a follow-up, see Known Issues. - β
9.3 Dyslexia-friendly reading mode β new Settings toggle (
dyslexiaFriendlyMode) sets--line-height: 1.8/--letter-spacing: 0.04emapp-wide via the same CSS-custom-property pattern as the existing theme bundles; no new component, just two new:rootvars insrc/index.csswith theirvar(--x, default)fallback as the single source of truth for the off state. - β
9.4 Expanded essay exports β new "Essay Export" section on the Export page (
src/pages/ExportPage.tsx) andsrc/utils/essayExport.ts. Exports a TipTap essay submission (EssaySubmission.contentHtml) to Markdown, DOCX, or PDF, either as a single essay, separate files for several selected students, or combined into one document (Markdown---join, DOCXPageBreak, PDFpage-break-after). No new data model was needed: the essay/rubric/analysis combined export (exportEssayWithRubric) joinsEssaySubmissionβStudentRubricβDocumentAnalysisResultat export time by(rubricId, studentId), rather than adding a stored attachment FK β avoiding the schema change the original proposal assumed was required. The HTMLβMarkdown/DOCX conversion is a small custom DOM walker scoped to exactly the tags TipTap'sEssayEditoremits (no new dependency); tables/task-lists degrade to plain text in Markdown/DOCX (PDF keeps full fidelity since it prints the HTML directly via the existingprintHtml/browser-print pattern). Batch "separate files" mode loops single-file downloads rather than zipping β no JSZip added.
- β
Two-tier navigation β
Sidebar.tsxrebuilt as an icon rail + sub-panel, grouped into 6 domains; fixed the rail mis-highlighting "Overview" on dynamic grading/session routes - β
"Warm Scholar" re-skin β new color tokens and fonts (Bricolage Grotesque/Hanken Grotesk), CEFR badge palette retuned to keep 4.5:1 contrast; fixed a bug where
AppContext.tsxhardcoded--accent/--fontas JS inline-style overrides that silently beat whatever the CSS theme said - β
Per-student CEFR profile depth β per-cell evidence (rubric/date/score) in
cefrStudentAggregator.ts, surfaced as a "Skill Evidence" section on the CEFR detail page - β
Live-session nudge β teacher can nudge a student mid essay/test live-monitor session (
useLiveSessionTelemetry.ts) - β Marketplace CEFR tags β chip picker on the publish form, new migration
- β
Moderation Reconcile β third resolution action in
ModerationQueuePage.tsx(per-criterion average, viacoGradingModerationQueue.ts) - β Dev seed-data tool β "Load sample data" button (Settings β Administration, dev-only) for realistic demo data during design review
- β
Full desktop/tablet UX audit (
docs/UX_AUDIT.md, tracking issue #201) β 23 findings filed, all resolved: grade-pill clutter on Students, CEFR Overview sticky columns, rubric editor/Comparative Grading/Grade Student tablet clipping, Statistics heatmap-vs-table/histogram redundancy, rubrics card-grid wrap, test/essay action-button stacking, moderation baseline-zero bug, CEFRβLearning PathβVocabulary cross-links, notification panel clipping, Activity Dashboard date grouping/legend, Marketplace filter/sort, Student Portal nav + teacher-preview banner, dev-only direct-access share-code links, spotlight-search open animation - β Co-grading reviewer identity β colleague picker replaces free-text reviewer name entry (PR #173)
- β DocsPage i18n migration β remaining hardcoded body content moved to translation keys (PR #174)
- β
Offline-first storage fix β stopped writing a full duplicate copy to
localStoragewhile connected to Supabase; persistence now gated throughisOffline()/the pending-sync queue per the rule inCLAUDE.md(PR #175)
- β
10.1 Global search β spotlight modal (
Ctrl/Cmd+Kor the Topbar search icon) over rubrics, tests, students, classes, and essay assignments;type:/class:filter tokens plus diacritic-insensitive substring matching (searchAll()) - β
10.2 Global active-class selector β Topbar
<select>two-way bound tosettings.activeClassId; Statistics' own class filter initializes from and syncs back to it live - β
10.3 Statistics Custom Views β gallery of 3 recommended charts, independently recolorable/toggleable, exportable as PNG (
statsChartPresets.ts); fixed a bug where the Criterion Heat Map ignored the page's class filter and exclude-not-handed-in toggle - β 10.4 Export page student selector β split out of the Rubric Export card into its own collapsible section, so it no longer gets in the way of Essay/Period/Report-card export
Phase 11: Data Portability & School-System Interop (11.1β11.2, complete) (PR #238)
- β
11.1 Gradebook export presets β
src/utils/gradebookExportPresets.ts, a "Gradebook format" dropdown next to the Export page's CSV button switches between the existing generic export and ready-made column presets for Magister and SOMtoday (student name/number + grade converted to the Dutch 1-10 scale via1 + pct/100*9). Reuses the existingPapa.unparse/handleCsvExportpipeline β generic export behavior unchanged. - β
11.2 Calendar/due-date export β
src/utils/icsExport.ts, a minimal VCALENDAR/VEVENT builder (no new dependency, no recurrence/timezone-DB). "Export due dates (.ics)" button on the Essay Export section downloads every essay assignment's deadline (EssayAssignment.expiresAt) as one file. Scoped to essay assignments only βTesthas no due-date field in the current data model, so test deadlines were dropped from this item rather than adding a new field speculatively; see below.
Scoped against the codebase first (see prior revision of this page for the full per-item analysis), then implemented the well-bounded slices from each item; the open-ended/large halves were deferred rather than rushed β see "Deferred from Phase 12" below.
- β
12.1 (partial) TipTap mark coverage β
src/utils/essayExport.ts's HTMLβDOCX/Markdown walker now handlescolor/highlight/font-family/font-size/line-height/text-alignmarks, real GFM tables (was: flattened to pipe-separated text), and GFM task-list checkbox syntax β previously silently dropped per the walker's ownponytail:scope-limiting comment. DOCX tables render as realTable/TableRow/TableCellnodes (header-row shading, borders) instead of flattened paragraphs. Essay template download/import deferred β see below. - β
12.2 (partial) Period Report β three independently-shipped slices: (1) the ambiguous combined locale heading
"Period Report / Report Card"renamed to just"Period Report"across all 5 locales β "Report Card" stays a separate, more detailed export with its own name, since they're genuinely two different functions (exportPeriodReportvsexportReportCard), not the same feature under two names; (2) comment/feedback text run through the existing<...>-stripping pattern (already used indocxExport.ts/essayUtils.ts) before being written intoperiodReportExport.ts'sTextRuns; (3) a real grade-trend bar chart, rasterized via a plain<canvas>(no Recharts/headless-render infra pulled into this non-React export utility) and embedded as a DOCXImageRun, replacing the old colored-cell trend table β falls back to the original table whencanvas.getContext('2d')is unavailable (e.g. under jsdom in tests). Essay-style template download/import for period reports deferred β see below. - β
12.3 phase 1: group-graded rubric (duplicate-scores) β new
StudentRubric.groupIdfield;createGroupStudentRubrics(rubricId, studentIds)inAppContext.tsxcreates oneStudentRubricper student sharing a freshgroupId; the existingSAVE_STUDENT_RUBRICreducer case fans outentries/overallComment/globalModifierto every sibling sharing thatgroupIdon every save (not just at creation β the group stays in sync on later edits too). New "Group grade" entry point on the Rubrics list opens a student-picker modal, then drops the teacher into the normal single-studentGradeStudentscreen β no new grading UI was built. A banner onGradeStudentshows which other students will receive the same scores. Per-criterion individual/collaborative scope split (phase 2) deferred β see below. - Verification: typecheck/lint clean (0 errors, same 152 pre-existing warnings), 1846/1846 unit tests (8 new, incl. a reducer test asserting the group fan-out and a chart-embedding test stubbing
HTMLCanvasElement), i18n parity 0 missing/extra across en/nl/fr/de/es, coverage 58.67%/50.42%/48.17%/59.96% (stmts/branches/fns/lines, above the 50/37 thresholds). Docs/i18n updated per CLAUDE.md:DocsPage.tsx(Essays tab styling-fidelity note, Analytics tab report-card chart note, new Grading tab "Group grading" section),README.md(Export options + new Group grading bullet),LandingPage.tsx(extended the existing Smart Grading card β no new card, per the established "extension not new feature" convention).
Each phase below groups proposals under one theme so they can be scoped and shipped independently. None of these involve AI/LLM content generation β that remains explicitly out of scope for this project.
Phase 9 (Accessibility & Reach Expansion), Phase 10 (Improved Searching & Querying), Phase 11 (Data Portability & School-System Interop), and Phase 12 (Improving the application β scoped slices) are now complete β see the "β Completed Phases" section above. The larger, open-ended halves of Phase 12 were deferred rather than rushed β see "Deferred from Phase 12" below.
13.1: Extend Coverage β (PR #245)
- β 22 new/extended test files covering previously zero- or low-coverage pages, components, and utilities (QuestionEditor, GlobalSearch, ActivityDashboardPage, MarketplacePage, StudentPortalPage, StudentProfilePage, StudentsPage, clozeParse, seededShuffle, and more)
- β Statement coverage raised from ~58% β 70% (8065/11520 statements); 2101 tests passing
- β
CI gating raised:
lines/statements52β65,functions42β60,branches43β58 (vite.config.ts) β branches/functions land at 58/60 rather than a flat 70% across every category, which the gating increase already reflects
- Extend testing suite to cover all routes in the teacher and student views
- Extend the Supabase testing suite to cover all the supabase methods
- Run a lighthouse audit
- Fix any issues that make the application less usable or responsive
- Fix all the unexpected 'any' warnings
- Triage all the issues, and order them from simple to complex fixes
- Add pre-commit hooks that help the commits work more cleanly. Think
prettier --writeandeslint --fix
- Audit the CI/CD pipeline
- See if any of the ordering should be changed
- Add extra reporting for critical CI/CD tests
- Research any additions that could fill CI/CD gaps (free tools are a requirement)
Phase 14: Expanding the student portal (14.1, 14.2, 14.5 complete via PR #248, pending merge)
- β
"My Progress" section on the student portal β a
CriterionRadarChartof the student's own per-criterion scores, either combined across every graded rubric (criteria with matching titles averaged together, e.g. shared skill categories across rubric templates) or filtered to a single rubric via a picker. Shown once at least 3 distinct graded criteria exist. Reuses the existing radar component andcalcEntryPointsβ no new aggregator file.
- β Shipped as one combined "My Work" section rather than two separate features, since a planned/overdue list and a to-do-with-status list are the same underlying data sliced two ways. Merges essay and test assignments into a single list grouped into Overdue / Planned / Completed, each item showing status (not started / in progress / submitted) and due date where set.
- β
Tests previously had no persisted assignment record at all β a teacher-generated share link was the only way a student ever learned a test existed, so there was nothing to list. Added
test_assignments(migration044_test_assignments.sql), mirroringessay_assignments' RLS pattern (get_my_test_assignment_ids()), plus a read-only policy onstudent_testsso the portal can show submission status, and a scoped read policy ontestsso the portal can embed full test content into a self-contained "Open" link (bypassing the broken share-code fetch path β see "Deferred from Phase 14" below). - β
Fixed a real bug found along the way:
TestAssignmentModalshared a singleteacherKeyacross an entire class batch, so a "DB mode" link pointed every student at the same row. Now mints oneteacherKey/DB row per student, matchingessay_assignments' 1:1-per-teacherKey constraint. -
Teststill has no due-date field on theTesttype itself (only per-assignmentexpiresAt, same as essays) β unchanged from the "Deferred from Phase 11" note below.
- Allow a student to ask for additional feedback and explanation on a test, rubric, or essay
- Send this to the teacher's notifications, allowing extra feedback or explanation to be given in a reply or the assessment
- Allow for students to use teacher made flashcard decks for vocabulary
- .csv, .xls(x), and .doc(x) imports
Scoped out of an earlier phase's implementation, not dropped. All remain candidates for a future phase if there's demand.
- LTI 1.3 LMS integration (Canvas, Blackboard, Moodle) β needs a live LMS sandbox to verify OAuth/launch flows; CSV-based roster import (Clever, OneRoster) shipped instead as a same-outcome, locally-testable substitute for the common "get students into the app" need.
-
Public/cross-tenant rubric marketplace β shipped as school-scoped only (reuses
schools/school_members); a public marketplace would need a moderation queue, abuse controls, and report/flag tooling that don't exist yet.
-
11.3 Read-only public API for institutional reporting β not started. The biggest item of the original three and the only one introducing a new attack surface. Needs: a Supabase Edge Function reading already-aggregated stats (reuse
gradeCalc.ts-style logic, not raw tables), an opt-in toggle inAdminPage.tsxβIntegrationsTab(off by default, per-school), and a scoped inbound API key/token distinct from the existing outbound Standards/Cambridge keys. Response must be pre-anonymized server-side (no student names/IDs) in the SQL/function layer, not left to the caller to discard fields. Recommend scoping this against a concrete consuming dashboard once one exists, rather than guessing the response shape β a small follow-up PR, not a large one. -
Test due-date export β
Testhas no due-date field in the current data model (onlyEssayAssignment.expiresAtdoes), so 11.2 shipped essay-deadline export only. Adding adueDate/expiresAtfield toTestand wiring it intoicsExport.ts's event list is a small follow-up if test deadlines turn out to matter in practice β not added speculatively.
-
12.1 Essay export styling template (download/import) β not started. The rubric's
ExportTemplateflow (table-header extraction from an uploaded blank DOCX) doesn't transfer to essays, which aren't tabular; needs its own template shape (paragraph/heading styles) and parsing logic designed from scratch. The TipTap mark-coverage half of 12.1 shipped; this half needs its own design pass before implementation. - 12.2 Period Report styling template (download/import) β same as above, not started; deferred for the same reason (no tabular pattern to reuse, needs a fresh design).
-
12.2 Unifying "Period Report" and "Report Card" into one export β only the naming was de-duplicated (one heading instead of two ambiguous ones); the two underlying functions (
exportPeriodReport/exportReportCard) still have different input shapes and weren't merged. A real merge is a product decision, not just a refactor β revisit only if having two adjacent export panels turns out to actually confuse teachers in practice. -
12.3 Phase 2: per-criterion group grading scope β not started. Phase 1 (duplicate-scores group, all criteria shared) shipped; splitting which criteria are collaborative vs. individual per rubric, with per-student override storage and offline-sync fan-out for partial edits, is real new data-model and UI work β recommend scoping a UI flow first, per the original Phase 12 scoping note's recommendation, before touching
RubricCriterion/gradeCalc.ts.
-
DB-mode test-taking is broken today for the bare share-code link (pre-existing, found while building 14.5, not caused by it).
StudentTestPage.tsxopens a disconnected Supabase client (persistSession: false) and never authenticates, so both its content-fetch (.from('tests').select(...)) and its submission insert (.from('student_tests').insert(...)) silently fail RLS (tests_own/student_tests_ownboth requireauth.uid() = owner_id, and there's no session at all). The app still "works" because it always falls back to the legacy submission-code exchange β but the "Enable direct submission to database" toggle onTestAssignmentModalcurrently does nothing useful for the actual test-taking page. The 14.5 portal to-do list sidesteps this for its own "Open" button by embedding full test content directly (a new scopedtestsRLS read, portal-authenticated only β already shipped), but the standalone share-link flow (the primary distribution mechanism, no portal account needed) is unfixed.-
Proven precedent to mirror exactly:
StudentEssayPage.tsxsolves the identical problem viaEssayAdapter.signInAnonymously()(supabase.auth.signInAnonymously()) plus two edge functions βget-essay-assignment(fetches assignment content server-side via service role, since migration 029 removed anonymous-session direct table access entirely to prevent prompt enumeration) andsubmit-essay(validates expiry/word-count/roster-email, rate-limits, and inserts via service role β the client-sideessay_submissions_student_insertRLS policy is unused dead code, kept defensively). Every actual submission goes through the edge function, never a direct client insert. -
Concrete plan for a fresh session:
- New edge function
get-test-assignment(mirrorsget-essay-assignment): POST, requires JWT, looks uptest_assignmentsby id, checks expiry, joinstestsfor full content, returns it. No directtests/test_assignmentsREST access for anonymous sessions. - New edge function
submit-test(mirrorssubmit-essay): POST, requires JWT, validates assignment + expiry, rate-limits, inserts intostudent_testsvia service role. Needs a duplicate-submission guard βstudent_testscurrently has no realtest_id/student_idcolumns to put a UNIQUE constraint on (they're jsonb-only); add them as real columns (migration, backfilled from existingdata) mirroringessay_submissions_assignment_student_uniq. - New
TestAdapter.ts(mirrorsEssayAdapter.ts):signInAnonymously(),fetchAssignmentContent(),submitTest()β thin wrappers calling the two edge functions above. -
StudentTestPage.tsx: callsignInAnonymously()before anything DB-related; replace the direct.from('tests').select(...)effect and the direct.from('student_tests').insert(...)inhandleSubmitwith the new adapter calls. Remove the now-redundant disconnected-client table access. - Auto-scoring: decide whether
rawTotalPointsgets computed in the edge function (duplicatingtestCalc.tslogic in Deno) or left for the teacher-side view to compute on read from stored raw answers β check how the existing legacy-code import path already handles this before choosing, so both paths stay consistent.
- New edge function
- Not started. Flagged as its own PR rather than folded into 14.5: it's a fix to the general-purpose test-taking mechanism (used with or without a portal account), not portal-specific, and touches anonymous auth + RLS + two new edge functions + a schema change β enough surface area to want a clean review on its own.
-
Proven precedent to mirror exactly:
| Issue | Impact | Owner | Priority |
|---|---|---|---|
| Native Magister SIS API integration (bulk import + live sync) not built β CSV path covers production use today | School IT can't fully automate roster sync | Reserved for community contribution | Low |
Multi-device e2e specs (18-multi-device-sync.spec.ts) require a local Supabase stack to verify outside CI; no Docker daemon in some dev sandboxes |
Slower local iteration on sync code | β | Low |
Mobile sidebar drawer (.sidebar overlay, transform: translateX()) always slides in from the left edge, even under dir="rtl" β translateX has no CSS logical-property equivalent |
Cosmetic-only RTL gap on mobile nav; inert until an RTL locale ships | β | Low |
PWA manifest icons (SVG + 192/512 PNG) have no dedicated maskable purpose variant |
Android adaptive-icon masking may crop/pad the icon awkwardly on some launchers | β | Low |
-
Unit tests: Vitest (coverage thresholds: 50% lines/statements, 37% functions/branches β see
npm run coverage) -
E2E tests: Playwright, including a dedicated
mobile-chromeandsupabaseproject - i18n: Parity tests across EN, NL, FR, DE, ES locales β new locales must pass the same parity check and be checked against existing UI layout for overflow
-
Accessibility: axe-core in CI (WCAG 2.1 AA minimum; AAA contrast enforced via
contrastCheck.ts) - Performance: Offline-first validation; sync latency benchmarks for > 100 rubrics
Last updated: 2 July 2026 β Phase 14 (14.1 My Progress radar, 14.2+14.5 combined My Work to-do list) implemented via PR #248, pending merge; 14.3/14.4 remain open. DB-mode test-taking auth/edge-function fix scoped out as its own follow-up β see "Deferred from Phase 14". Phase 13.1 (extend test coverage to 70%, raise CI gating to 65/65/60/58) confirmed merged via PR #245. 13.2β13.6 remain open. Phase 11 (11.1/11.2) confirmed merged via PR #238. Phase 12 implemented in scoped slices: 12.1 TipTap mark coverage in essay exports, 12.2 Period Report naming/HTML-stripping/grade-trend chart, 12.3 phase-1 group grading (duplicate-scores). Template download/import (12.1/12.2) and per-criterion group scope (12.3 phase 2) deferred. All deferred items (Phase 6, 11, 12, 14) consolidated under one "Deferred Items" heading after Phase 13.