Skip to content

Roadmap

Wouter Meetsma edited this page Jul 2, 2026 · 48 revisions

Development Roadmap

🎯 Overview

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.


✅ Completed Phases

Phase 0: MVP & Core Features (Launched)

  • ✅ 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 via color-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

Phase 3: EFL Classroom Enhancements (PR #134#136)

  • ✅ 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.ts parses {{cloze}} and [[hot-text]] syntax; scoring in testCalc.ts
  • ✅ Standalone /essays workspace (parallel to /tests) — prompt editor, rubric connector, per-student share links, submission-code import, reusable assignment templates (PR #143)

Phase 4: Admin & School Operations (PR #144#146)

  • ✅ 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

Phase 5: UI/UX Polish (PR #147#153)

  • ✅ 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_members tables, /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.pastClassMemberships recorded 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 (StudentRubric with isPeerReview: true); src/utils/coGradingModerationQueue.ts flags disputes above a configurable point threshold; /moderation lists disputes with a per-criterion delta and keep/accept resolution
  • ✅ 8.2 Department rubric/comment-bank libraries — sharedWithSchool flag 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 (GradingTask type, 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/dnd computes 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 from pastClassMemberships, 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.

Phase 9: Accessibility & Reach Expansion (9.1–9.4, complete)

  • ✅ 9.1 RTL readiness — CSS logical-properties audit (src/index.css): physical left/right/margin-left/padding-left/border-right/text-align: left rules converted to inset-inline-*/margin-inline-*/padding-inline-*/border-inline-*/text-align: start, a no-op in the 5 existing LTR locales. New src/utils/rtlLanguages.ts + an AppContext effect sets dir="rtl" on <html> when an RTL language is active — inert today (no RTL locale shipped yet) but ready for one. The mobile sidebar drawer's transform: 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' (not autoUpdate) 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 explicitly NetworkOnly in the Workbox runtime-caching config — the service worker must never make a failed sync request look like it succeeded. Works under the existing relative base: './' 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 dedicated maskable purpose 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.04em app-wide via the same CSS-custom-property pattern as the existing theme bundles; no new component, just two new :root vars in src/index.css with their var(--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) and src/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, DOCX PageBreak, PDF page-break-after). No new data model was needed: the essay/rubric/analysis combined export (exportEssayWithRubric) joins EssaySubmissionStudentRubricDocumentAnalysisResult at 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's EssayEditor emits (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 existing printHtml/browser-print pattern). Batch "separate files" mode loops single-file downloads rather than zipping — no JSZip added.

Phase 9.5: UX Audit, Navigation Redesign & Visual Refresh (PR #202#226, #237)

  • ✅ Two-tier navigation — Sidebar.tsx rebuilt 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.tsx hardcoded --accent/--font as 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, via coGradingModerationQueue.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 localStorage while connected to Supabase; persistence now gated through isOffline()/the pending-sync queue per the rule in CLAUDE.md (PR #175)

Phase 10: Improved Searching & Querying (PR #227#231)

  • ✅ 10.1 Global search — spotlight modal (Ctrl/Cmd+K or 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 to settings.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 via 1 + pct/100*9). Reuses the existing Papa.unparse/handleCsvExport pipeline — 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 — Test has 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.

Phase 12: Improving the application (exports and group work) — scoped slices complete

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 handles color/highlight/font-family/font-size/line-height/text-align marks, real GFM tables (was: flattened to pipe-separated text), and GFM task-list checkbox syntax — previously silently dropped per the walker's own ponytail: scope-limiting comment. DOCX tables render as real Table/TableRow/TableCell nodes (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 (exportPeriodReport vs exportReportCard), not the same feature under two names; (2) comment/feedback text run through the existing &lt;...&gt;-stripping pattern (already used in docxExport.ts/essayUtils.ts) before being written into periodReportExport.ts's TextRuns; (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 DOCX ImageRun, replacing the old colored-cell trend table — falls back to the original table when canvas.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.groupId field; createGroupStudentRubrics(rubricId, studentIds) in AppContext.tsx creates one StudentRubric per student sharing a fresh groupId; the existing SAVE_STUDENT_RUBRIC reducer case fans out entries/overallComment/globalModifier to every sibling sharing that groupId on 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-student GradeStudent screen — no new grading UI was built. A banner on GradeStudent shows 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).

🔭 Future Roadmap: Phase 13 and beyond

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.


Phase 13: Extending testing

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/statements 52→65, functions 42→60, branches 43→58 (vite.config.ts) — branches/functions land at 58/60 rather than a flat 70% across every category, which the gating increase already reflects

13.2: Extend E2E testing

  • Extend testing suite to cover all routes in the teacher and student views
  • Extend the Supabase testing suite to cover all the supabase methods

13.3: Fix lighthouse issues

  • Run a lighthouse audit
  • Fix any issues that make the application less usable or responsive

13.4: Unexpected 'Any' fixes

  • Fix all the unexpected 'any' warnings
  • Triage all the issues, and order them from simple to complex fixes

13.5: Husky Pre-Commit Hooks

  • Add pre-commit hooks that help the commits work more cleanly. Think prettier --write and eslint --fix

13.6: Research further CI/CD additions

  • 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)

14.1: Expand the statistics views ✅

  • ✅ "My Progress" section on the student portal — a CriterionRadarChart of 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 and calcEntryPoints — no new aggregator file.

14.2: Better planned view for students ✅ (merged with 14.5)

14.5: Student to-do list for tests and essays ✅

  • ✅ 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 (migration 044_test_assignments.sql), mirroring essay_assignments' RLS pattern (get_my_test_assignment_ids()), plus a read-only policy on student_tests so the portal can show submission status, and a scoped read policy on tests so 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: TestAssignmentModal shared a single teacherKey across an entire class batch, so a "DB mode" link pointed every student at the same row. Now mints one teacherKey/DB row per student, matching essay_assignments' 1:1-per-teacherKey constraint.
  • Test still has no due-date field on the Test type itself (only per-assignment expiresAt, same as essays) — unchanged from the "Deferred from Phase 11" note below.

14.3: Messaging between student and teacher

  • 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

14.4: Anki-like flashcards for vocabulary

  • Allow for students to use teacher made flashcard decks for vocabulary
  • .csv, .xls(x), and .doc(x) imports

🗂️ Deferred Items

Scoped out of an earlier phase's implementation, not dropped. All remain candidates for a future phase if there's demand.

Deferred from Phase 6

  • 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.

Deferred from Phase 11

  • 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 in AdminPage.tsxIntegrationsTab (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 exportTest has no due-date field in the current data model (only EssayAssignment.expiresAt does), so 11.2 shipped essay-deadline export only. Adding a dueDate/expiresAt field to Test and wiring it into icsExport.ts's event list is a small follow-up if test deadlines turn out to matter in practice — not added speculatively.

Deferred from Phase 12

  • 12.1 Essay export styling template (download/import) — not started. The rubric's ExportTemplate flow (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.

Deferred from Phase 14

  • 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.tsx opens 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_own both require auth.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 on TestAssignmentModal currently 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 scoped tests RLS 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.tsx solves the identical problem via EssayAdapter.signInAnonymously() (supabase.auth.signInAnonymously()) plus two edge functionsget-essay-assignment (fetches assignment content server-side via service role, since migration 029 removed anonymous-session direct table access entirely to prevent prompt enumeration) and submit-essay (validates expiry/word-count/roster-email, rate-limits, and inserts via service role — the client-side essay_submissions_student_insert RLS 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:
      1. New edge function get-test-assignment (mirrors get-essay-assignment): POST, requires JWT, looks up test_assignments by id, checks expiry, joins tests for full content, returns it. No direct tests/test_assignments REST access for anonymous sessions.
      2. New edge function submit-test (mirrors submit-essay): POST, requires JWT, validates assignment + expiry, rate-limits, inserts into student_tests via service role. Needs a duplicate-submission guard — student_tests currently has no real test_id/student_id columns to put a UNIQUE constraint on (they're jsonb-only); add them as real columns (migration, backfilled from existing data) mirroring essay_submissions_assignment_student_uniq.
      3. New TestAdapter.ts (mirrors EssayAdapter.ts): signInAnonymously(), fetchAssignmentContent(), submitTest() — thin wrappers calling the two edge functions above.
      4. StudentTestPage.tsx: call signInAnonymously() before anything DB-related; replace the direct .from('tests').select(...) effect and the direct .from('student_tests').insert(...) in handleSubmit with the new adapter calls. Remove the now-redundant disconnected-client table access.
      5. Auto-scoring: decide whether rawTotalPoints gets computed in the edge function (duplicating testCalc.ts logic 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.
    • 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.

📌 Known Issues & Technical Debt

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

🎓 Development Notes

Testing Standards

  • Unit tests: Vitest (coverage thresholds: 50% lines/statements, 37% functions/branches — see npm run coverage)
  • E2E tests: Playwright, including a dedicated mobile-chrome and supabase project
  • 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.

Clone this wiki locally