Repository navigation
Releases
v0.21.0 - Stay on top
Highlights
- Members see what is due across their sections in an Upcoming panel on My sections: open quizzes and due assignments they have not submitted, with staff seeing submission counts (roadmap R1).
- In-app notifications with a bell and unread count: new announcements, new quizzes and assignments, released scores and posted final grades, with mark-read and mark-all; announcement mail keeps flowing per post and each member can opt out of it (R2).
- A private calendar feed per member: section meetings and quiz and assignment deadlines as an iCal link for Google, Apple or Outlook Calendar, created once, rotatable and revocable (R3).
- Every date and time shows in the university's time zone and locale, with the zone named when it differs from the browser's, and quiz and assignment windows are entered in campus time (R4).
- Two new Playwright files cover notifications, Upcoming, the calendar feed lifecycle and the time-zone display.
What's Changed
Changes
- P21-L2: migration 0012, in-app notifications (producers, routes), announcement-mail opt-out by @kapildev333 in #196
- P21-L3: Upcoming, calendar feed with rotatable tokens, ICS writer, campus timezone and locale on /me by @kapildev333 in #197
- P21-L4: header bell with unread count, notifications page, announcement-mail opt-out by @kapildev333 in #199
- P21-L5: Upcoming panel, Calendar feed dialog, campus time zone and locale in the web app by @kapildev333 in #201
- P21-L6: e2e for notifications, Upcoming, calendar feed and campus time zone by @kapildev333 in #202
Full Changelog: v0.19.0...v0.21.0
v0.19.0 - Quiz and assignment follow-ups
Highlights
- Staff can fix an answer key after students have started: saving the questions regrades every submitted attempt, keeps points a reviewer set by hand, and records a regrade per attempt in the audit log (Spec 12).
- A quiz can limit each attempt to a number of minutes inside its window; the countdown shows the attempt's own deadline and the server submits the attempt when it passes.
- An assignment can accept late submissions until a second date with a flat percentage penalty; late work is marked, the raw grade stays on the submission and the penalised score goes to the gradebook, and students see both once released.
- The API server bounds how long a client may take to send a request (
UNICOURSE_READ_TIMEOUT, default 30 s) while uploads get their own longer deadline (UNICOURSE_UPLOAD_TIMEOUT, default 10 min); a timed-out read is logged as 499. - Three new end-to-end cases cover the time limit, the regrade and the late penalty.
What's Changed
Changes
- P19-L6: server read timeouts (UNICOURSE_READ_TIMEOUT, UNICOURSE_UPLOAD_TIMEOUT), 499 on a timed-out read by @kapildev333 in #176
- P19-L2: migration 0011, assessment time limit / late window / penalty fields, effective-deadline SQL, timed-quiz questions gate by @kapildev333 in #179
- P19-L5: late submissions with a penalty (late window, late flag, penalised gradebook score) by @kapildev333 in #180
- P19-L4: per-attempt time limit (effective deadline on save, submit and the sweeper; Attempt.deadlineAt) by @kapildev333 in #181
- P19-L3: regrade submitted attempts after an answer-key change (ids on questions, manual marks kept and capped) by @kapildev333 in #183
- P19-L7: web for Spec 12 - time limit, regrade editor mode, late submissions with penalty by @kapildev333 in #184
- P19-L8: e2e E-QUIZ-11 (time limit), E-QUIZ-12 (regrade), E-ASG-09 (late penalty) by @kapildev333 in #186
Full Changelog: v0.18.0...v0.19.0
v0.18.0 - Quizzes and assignments
Highlights
- Staff create quizzes on a section: multiple-choice and fill-in-the-blank questions with points, a description, a weight in the gradebook and an open-to-close window (Spec 11).
- Students take a quiz on one page with autosave and a countdown; at the close the server submits whatever was saved, grades it automatically and writes the score into the gradebook, so a student who stops mid-quiz still gets what they earned.
- Instructors and TAs review every attempt per question, adjust points, add feedback and release scores; until release students see only that they submitted, afterwards their score, feedback and the correct answers.
- Staff create assignments with a start date, a due date, a description and maximum points; students submit text and up to ten files, replace them until the due date, and staff grade with points and feedback that appear once released.
- Quizzes and assignments are gradebook columns, so running grades, finalization, GPA and My grades work unchanged; the demo university seeds an open quiz with attempts and a graded assignment.
- Two new Playwright files assert the whole flow end to end, including the automatic submission at the close and tenant isolation; 106 cases run on
npm run e2e.
What's Changed
Changes
- P18-L3: migration 0010, assessment kinds, window and dueOn, UpsertScore, MyAssessment.kind by @kapildev333 in #163
- P18-L5: internal/assignment - submissions, files, grading; content upload/download exports; prune-files by @kapildev333 in #165
- P18-L4: internal/quiz - questions, auto-grading, attempts, sweeper, review; quiz contract by @kapildev333 in #166
- P18-L6 (a+b): web quizzes - tabs, list, window dialog, question editor, take page, results; D144 gradebook edits by @kapildev333 in #168
- P18-L6c: web review-attempts page (table, in-place review, release) by @kapildev333 in #169
- P18-L7: web assignments - list, student submission page, grading page; typed My grades links by @kapildev333 in #170
- P18-L8a: demo seed - one open quiz with attempts and a review, one assignment with submissions and a grade on CS101 by @kapildev333 in #172
- P18-L8b: e2e quizzes.spec.ts and assignments.spec.ts (Spec 11 §1 criterion, 18 cases) by @kapildev333 in #173
Full Changelog: v0.17.0...v0.18.0
v0.17.0 - Backlog completion
Highlights
- Sign-in rate limits live in Postgres, so several API processes share one count per address and per email (Spec 10 B1); the e2e harness can raise the per-IP limit for repeated local runs.
- Mail delivery runs on configurable sender goroutines (
UNICOURSE_OUTBOX_SENDERS), the client address is read fromForwardedas well asX-Forwarded-For, IPv4-mapped prefixes match, and a malformedUNICOURSE_WEB_ORIGINrefuses startup (B2, B5, B10). - Department, term and course pickers search as you type (
qon the three lists), so universities beyond 200 rows stay usable (B3). create-universityruns as a confinedunicourse_platformrole that cannot migrate or reach an existing university (B4, D133, D135).- The upload limit comes from
UNICOURSE_MAX_UPLOAD_BYTES, the allowed types and limit are published atGET /api/v1/upload-policyand the web reads them; uploads show byte-level progress; routes are code-split (B7, B9, B13). seed-demo --fixture <path>loads any dataset described bydocs/seed/fixture.schema.json;make devbrings up Postgres, migrations, the demo and both dev servers; the screenshot script seeds throughseed-demo(B8).- Meetings in one section may not overlap; copy fixes for the section Remove dialog, course conflicts, department-admin controls and the audit filter; invitation 409 messages no longer reveal membership state (B6, B12); CI checks the two-origin dev sign-in flow (B11).
What's Changed
Changes
- P17-L9a: route code splitting, index chunk 546 kB to 293 kB by @kapildev333 in #147
- P17-L5: overlapping meetings rule (D131), generic invitation 409 (D132), F4 F5 F6 F10 by @kapildev333 in #149
- P17-L8: seed-demo --fixture, fixture schema, make dev, screens via seed-demo by @kapildev333 in #150
- P17-L10: CI dev-setup job checks the two-origin sign-in flow (B11) by @kapildev333 in #151
- P17-L3: outbox senders, Forwarded header and IPv4-mapped prefixes, WEB_ORIGIN validation, upload limit config by @kapildev333 in #152
- P17-L2: sign-in rate limits in Postgres (rate_buckets, migration 0008) by @kapildev333 in #153
- P17-L7: unicourse_platform role for create-university, migration 0009 (D133, D135) by @kapildev333 in #155
- P17-L6: q on departments and terms, picker search, overlap rule in the contract by @kapildev333 in #156
- P17-L4: upload limit from the environment, GET /upload-policy, web reads the policy (after #156) by @kapildev333 in #157
- P17-L2b: UNICOURSE_SIGNIN_IP_LIMIT; e2e raises the per-IP limit by @kapildev333 in #158
- P17-L9b: XHR upload progress with a native progress element, router pending component and intent preload (after #157) by @kapildev333 in #159
Full Changelog: v0.16.0...v0.17.0
v0.16.0 - End-to-end coverage
Highlights
- Every user-visible flow, for every role, now has a Playwright case that runs on
npm run e2eand in CI: 89 cases across 11 files, designed from a catalogue of 112 IDs that maps the specs, the README personas and the UI to tests (Spec 9, catalogue). - The demo dataset is guarded: every README persona signs in and sees exactly what the persona table promises (Spec 7).
- Sign-in, admin, courses and sections, grading, catalog and schedule, and section content each have their own spec file, so a regression names the role, the page and the expected copy.
- Accessibility is checked on every page and dialog per role in light and dark with axe (WCAG 2.x A and AA); no serious or critical violation today.
- Invited adjuncts and visiting TAs whose email domain belongs to no university can sign in again after accepting their invitation (Spec 1 §5, D128).
- The e2e harness takes a port (
UNICOURSE_E2E_PORT), seeds throughseed-demoor the HTTP API, andnpm run e2e -- <file>runs one file.
What's Changed
Changes
- P16-L2: e2e harness (port, shared helpers, seed-demo), demo.spec.ts persona smoke, CI e2e timeout 30 by @kapildev333 in #135
- P16-L5: courses.spec.ts, 12 cases (courses, sections, imports, staff, roster, Participants) by @kapildev333 in #139
- P16-L7: catalog.spec.ts, 8 cases (Catalog, My sections, schedule, phone agenda) by @kapildev333 in #136
- P16-L6: grades.spec.ts, 12 cases (assessments, scores, publish, finalize, scale, permissions) by @kapildev333 in #137
- P16-L4: admin.spec.ts, 11 cases incl. audit log filters and entries by @kapildev333 in #141
- P16-L8: content-edit.spec.ts, 9 cases (announcements, weeks, materials, uploads, access) by @kapildev333 in #138
- P16-L3: auth.spec.ts, 14 cases (E-AUTH-11 fixme pending P16-F1) by @kapildev333 in #140
- P16-F1: accepted outside-domain invitees can sign in again (D128); E-AUTH-11 passes by @kapildev333 in #142
- P16-L9: a11y.spec.ts axe sweep per role in light and dark (D125) by @kapildev333 in #144
Full Changelog: v0.14.1...v0.16.0
v0.14.1 - Sign-in from the dev setup
Highlights
Patch: signing in from the README development setup (API on 8080, web dev server on 5173) now lands in the app. The magic-link confirmation page allowed form submissions to its own origin only, and the browser applied that rule to the redirect into the web app, so users stayed on the confirmation page with a session already set. Single-origin deployments were unaffected.
What's Changed
Changes
- P15-L1: allow the web origin in the sign-in confirm page's form-action CSP (sign-in from the README dev setup) by @kapildev333 in #130
Full Changelog: v0.14.0...v0.14.1
v0.14.0 - Backlog sweep
Highlights
Phase 14, the backlog sweep: every defect and bounded improvement carried in PLAN.md since Phase 0 is fixed; what remains is gated on a trigger. Every decision to date is confirmed.
- Participants tab. Each section's staff editor and roster moved from Home to a Participants tab (Home keeps the title band, description and meeting times); every member sees staff with role badges, the roster stays with those allowed to see it.
- Account menu. The header shows your name; the account dialog sets your display name and, on phones, holds Sign out so the header stays one row.
- Gradebook and My grades. The gradebook sits under the section band and tabs; "(n missing)" is labelled against the Finalize count; Finalize-disabled reasons follow the design; a same-letter override keeps its recorded points; My grades shows how many assessments are not yet published; the sticky Student cell no longer takes the row hover.
- Admin. The Audit log gains a Target column and a true total ("Showing 50 of 100 changes"); admin tabs use the shared page header; catalog and course lists are in code order; one primary "New section" on an empty term; sign-in rate-limit copy says an hour.
- Operations. A client disconnect mid-request is logged as an abort, not a 500; expired sign-in mails are given up unsent;
seed-demo --resetremoves the old files directory;prune-filesclears day-old temporary uploads; the gradebook export honoursAcceptq-values. - Dependencies. React 19, Vite 8, Vitest 5, jsdom 30 and @types/node 26; local development needs Node 22.22.2 or newer. ESLint stays at 9 until its accessibility plugin supports 10.
- Process. GitHub Releases per phase (this is the second), generated notes without chore PRs, the capture script covers the section Home and Materials pages.
Specs: https://github.com/kapildev333/unicourse/tree/main/docs/specs. Plan and decisions: https://github.com/kapildev333/unicourse/blob/main/PLAN.md.
What's Changed
Changes
- P14-L1c: RemoveUniversity on seed --reset, .tmp pruning in prune-files, CSV writer error check by @kapildev333 in #122
- P14-L1a: client aborts logged as 499, expired sign-in mail given up, audit-log total by @kapildev333 in #123
- P14-L4: Participants tab, single New section on an empty term, e2e local year, Home and Materials captures by @kapildev333 in #124
- P14-L1b: catalog ordering, override keeps points, Accept q-values, real authz fixtures, unpublishedCount by @kapildev333 in #125
- P14-L2: TextLink, PageHeader levels, Account menu with display name, Audit log Target and total, rate-limit copy by @kapildev333 in #126
- P14-L3: gradebook under the section band, hover band fix, missing-count label, Finalize reasons, My grades unpublished count by @kapildev333 in #127
- P14-L5: React 19, Vite 8, Vitest 5, jsdom 30, @types/node 26; Node floor 22.22.2 by @kapildev333 in #128
Full Changelog: v0.12.0...v0.14.0
v0.12.0 - Baseline: Specs 1 to 8
Highlights
Baseline release: everything built through Phase 12 (Specs 1 to 8), as the product stands on 2026-10-07.
- A university in minutes.
unicourse-admin create-universitysets up a tenant with its own allowed email domains, timezone and first admin;unicourse-admin seed-demoloads the Northfield University demo (four departments, five courses, staff, rosters, grades, announcements and materials) for trying every role (Spec 1, Spec 7). - Sign in by magic link, Google or Microsoft, with per-university roles: admin, department admin, instructor, TA, student (Spec 1).
- Catalog and sections. Departments, terms, courses and sections with weekly meeting times and rooms; CSV imports for courses, sections and rosters; instructors and TAs assigned per section (Spec 1, Spec 2).
- My sections and schedule. Students and staff see their sections per term as cards or as a weekly timetable with a Today marker and clash detection (Spec 2).
- Grading. Weighted assessments, score entry and CSV import, running percent and letter, a per-university editable grade scale, finalisation with overrides and a reason, and GPA on My grades (Spec 4).
- Section content. Each section has a home page, announcements emailed to the roster, and weekly materials with file upload and safe download, for instructors and TAs alike (Spec 6).
- A professional light and dark UI, benchmarked screen by screen and improved across Phases 8 and 9, usable at phone width (Spec 3, Spec 5).
- For developers. A root README that goes from clone to a signed-in demo, an OpenAPI 3.1 contract checked in CI, a generated Postman collection with a local environment, and an admin CLI with migrations and file pruning (Spec 7, Spec 8).
Specs: docs/specs/. Plan and decisions: PLAN.md.
What's Changed
Changes
- P0-L1: API skeleton and httpx by @kapildev333 in #6
- P0-L3: web skeleton and API client by @kapildev333 in #7
- P0-L4: openapi.json contract, route registry and contract test by @kapildev333 in #9
- P0-L2: schema, RLS, pg, testdb, audit by @kapildev333 in #10
- P2-L1: csvimport engine by @kapildev333 in #13
- P1-L1a: auth primitives (sessions, magic links, mailer, limiter) by @kapildev333 in #14
- P1-L4: web sign-in and admin console by @kapildev333 in #16
- P1-L1b/c: sign-in, OIDC, auth handlers and wiring by @kapildev333 in #17
- P1-L2a/b: authz, tenant pipeline, departments and terms API by @kapildev333 in #18
- P2-L2a: courses and sections API by @kapildev333 in #19
- P2-L3: web courses, sections, staffing, roster import, my sections by @kapildev333 in #21
- P1-L2c: memberships, invitations and audit-log route by @kapildev333 in #22
- P1-L3: unicourse-admin CLI (migrate, create-university) by @kapildev333 in #23
- P2-L2b: section staff, enrollments and my-sections API by @kapildev333 in #27
- P1-L5: audit invitation acceptance at sign-in by @kapildev333 in #29
- P2-L2c: CSV imports for courses, sections and rosters by @kapildev333 in #30
- P3-L1: serve the web app from the API, end-to-end staffing flow, CI e2e job by @kapildev333 in #31
- P5-L1a: section meetings in the API, catalog filters by @kapildev333 in #38
- P5-L1b: catalog reads for members, pendingStaff masking, import meetings column by @kapildev333 in #39
- P5-L2: student schedule grid, catalog, meetings editor by @kapildev333 in #41
- P4-L1: mail outbox, sent after commit by an in-process sender by @kapildev333 in #43
- P5-L3: e2e for overlapping meetings, schedule clashes and catalog clash note by @kapildev333 in #44
- P4-L5a: cancel a pending staff assignment, exact-case JSON keys by @kapildev333 in #46
- P4-L3: trusted-proxy client address, OIDC start rate limit by @kapildev333 in #47
- P4-L5b: cancel a pending staff invitation from the staff editor, two catalog/dialog fixes by @kapildev333 in #48
- P4-L2: database role split - owner migrates, the API role cannot disable RLS by @kapildev333 in #50
- P6-L1: UI foundation - tokens for light and dark, DM Sans, Lucide, theme switch, Udemy top bar with search, shared components, literal-colour gate by @kapildev333 in #56
- P6-L2: entry pages on the Udemy foundation (sign-in, magic link sent, no access, root redirect) by @kapildev333 in #59
- P6-L3: My sections and Catalog as card grids, schedule on tokens by @kapildev333 in #60
- P6-L5: admin area on the Udemy foundation (tabs, tables, dialogs) by @kapildev333 in #62
- P7-L1a: grading schema, contract, grade scale, assessments, scores by @kapildev333 in #66
- P6-L4: courses area on the Udemy foundation (lists, section detail band, roster, dialogs, editors) by @kapildev333 in #63
- P7-L1b: gradebook, finalize with credits snapshot, GPA by @kapildev333 in #68
- P7-L1c: scores CSV import, gradebook CSV export by @kapildev333 in #70
- P7-L2: grading web - gradebook, My grades with GPA, grade scale editor by @kapildev333 in #71
- P7-L3: e2e for grading - assessments, scores, finalize, GPA by @kapildev333 in #72
- P8-L1: Playwright screen-capture project for the UI/UX benchmark by @kapildev333 in #75
- P8-L2: reference pack for the UI/UX benchmark (Dribbble shots described from images) by @kapildev333 in #76
- P8-L3: Spec 5 UI/UX benchmark and its Phase 9 plan by @kapildev333 in #78
- P9-L1a: shared components for Phase 9 (Table stack, Loading variants, Card footer, Dialog close, SegmentedControl, Disclosure, line-control) by @kapildev333 in #82
- P9-L1b: shell at phone width, current nav item, / shortcut, no-access page, capture width assertion by @kapildev333 in #85
- P9-L8: display names (invitation name, roster import fill, sign-in fill, PATCH /me) by @kapildev333 in #84
- P10-L1a: section content API foundation (migration, blob, description, announcements, mail, contract) by @kapildev333 in #87
- P9-L4: admin tables stack on phones, term status, audit labels and disclosure, titled banners by @kapildev333 in #89
- P9-L2: phone agenda, Today marker, merged meetings, Catalog filters and badges, My sections controls by @kapildev333 in #90
- P10-L1b: weeks, materials upload and download, blob cleanup after commit, prune-files by @kapildev333 in #91
- P9-L3: courses, sections and dialogs for phones; empty states; staff cells; Edit section by @kapildev333 in #93
- P9-L5: gradebook, finalize preview, assessment dialog, My grades and grade scale for phones and clarity by @kapildev333 in #94
- P10-L2: section tabs, Home description, Announcements and Materials pages by @kapildev333 in #97
- P10-L3: Spec 6 end-to-end criterion (content.spec.ts with generated fixtures) by @kapildev333 in #100
- P9-L1c: ButtonLink, ListCount, quiet row actions, Table clipping, sized inputs, gradebook Student column by @kapildev333 in #99
- P9-L6: one sign-in field, resend and last-used, one search, middle-dot separators, e2e selectors per §5.7 by @kapildev333 in #102
- P9-L9: Phase 9 closing audit (after scores, remaining findings, taste-skill pass) by @kapildev333 in #103
- P9-L10a: stacked phone rows, table style bugs, Admin heading, Audit log cards on phones, neutral Close and badges, copy by @kapildev333 in #107
- P9-L10b: schedule Today marker and time line, finalize preview and grade scale stacked on phones, My grades hint, capture seed names by @kapildev333 in #106
- P11-L1: unicourse-admin seed-demo, embedded demo fixture through the real handlers by @kapildev333 in #111
- P11-L2: root README, eight light and...