-
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
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.
Two items from the original Phase 6 proposal were cut from scope before implementation, since neither could be built and verified in the implementation environment:
- 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.
Both remain candidates for a future phase if there's demand.
Phase 9 (Accessibility & Reach Expansion) and Phase 10 (Improved Searching & Querying) are now complete — see the "✅ Completed Phases" section above.
Closes the loop on "data leaves the device" use cases beyond JSON backup. Scoped against the current export surface: ExportPage.tsx's CSV path today is a single generic rubric.criteria-shaped dump (handleCsvExport, via Papa.unparse); assignment due dates already exist (EssayAssignment.dueDate/Test.dueDate in src/types/index.ts) but nothing reads them outside the app; there is no public-facing HTTP surface today (AdminPage.tsx's IntegrationsTab only holds API keys the app calls outward, not an endpoint others call in).
- New CSV column-mapping presets alongside the existing generic export on the Export page — same
Papa.unparsepipeline, different column set/order/header names per target system. Start with 1-2 concrete Dutch/EU systems (e.g. Magister, SOMtoday) rather than a generic mapper, since "generic" is what the existing CSV export already is. - A preset is just a
{ id, label, columns: { csvHeader, valueFn }[] }list — no new settings/admin UI beyond a dropdown next to the current CSV button.
- One new utility (
src/utils/icsExport.ts) that takes the existingEssayAssignment[]/Test[](already carrydueDate) and emits a minimal.ics(VCALENDAR/VEVENT, no recurrence/timezone-DB dependency — stdlib string building is enough). Download button next to each assignment list and one "export all upcoming due dates" button on the Export page. - No calendar API integration, no new dependency — keeps the offline-first model intact.
- Biggest item of the three — the only one introducing a new attack surface. Needs: a Supabase Edge Function reading already-aggregated stats (reuse
gradeCalc.ts/statisticsAggregator-style logic, not raw tables), an opt-in toggle inAdminPage.tsx→IntegrationsTab(off by default, per-school), and a scoped API key/token distinct from the existing Standards/Cambridge keys (those are outbound-only; this is inbound). - Response must be pre-anonymized server-side (no student names/IDs) — do this in the SQL/function layer, not by trusting the caller to discard fields.
- Recommend shipping 11.1/11.2 first and treating 11.3 as a separate, smaller PR once there's a concrete consuming dashboard to validate the shape against — building a public API against a hypothetical consumer risks guessing the wrong response shape.
- Include a template download and import similar to the rubric export
- Make sure that the template includes the themes of all the possible Tip Tap modifications
- Include a template download and import similar to the rubric export
- Naming should be changed to just Period Report
- Make sure that any HTML tags are removed
- Add in any relevant graphs
- Include a template download and import similar to the rubric export
- Make sure that any HTML tags are removed
- Add in any relevant graphs
- Extend coverage on all fronts to at least 70% coverage on all categories and pages
- Up the coverage gating to 65% after achieving the above
- 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)
| 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: 29 June 2026 — Phase 9.5 (UX audit + navigation/visual refresh, 23 audit findings resolved) and Phase 10 (global search, active-class selector, Statistics Custom Views, Export page restructure) both complete. Phase 11 (Data Portability & School-System Interop) scoped and ready to start.