ROwO Academic is a program-progress and course-planning companion for students. It uses ROwO accounts for sign-in and University of Waterloo academic-calendar data to help students:
- track progress through one or more programs;
- validate completed and planned courses against program requirements; and
- plan future terms while seeing which requirements each course can satisfy.
The production Worker is deployed at https://academic.rowo.link.
Guests can browse the active calendar's plan and course information without an
account. Guest endpoints are read-only and query only ACADEMIC_DB; selecting a
program, recording courses, evaluating a personal plan, and every write to DB
remain behind ROwO sign-in.
Signed-in students use the same Plans and Courses browser from the Browse
tab. They can track multiple plans against one shared course record. The
Progress tab evaluates every tracked plan independently, while the planner
ranks courses that advance several plans ahead of single-plan suggestions and
highlights that overlap. A course detail page evaluates its parsed requirements
against courses and programs already saved to the student's account, clearly
marks requirements as satisfied, not yet satisfied, or needing review, and can
add that course to the student's record without leaving the detail page.
Course and plan checks evaluate the serialized requirement AST from
ACADEMIC_DB; the browser preserves its nested groups and links resolved course
and program references to their catalog detail views.
Courses whose codes start with COOP or PD remain visible as non-academic
requirements, but contribute zero academic units to completed, planned, and
projected totals.
- vinext, Vite, React, and TypeScript run as a Cloudflare Worker.
DBis the app-owned D1 binding. It stores user programs, course history, future-term plans, catalog snapshots, and opaque ROwO Academic sessions.ACADEMIC_DBis the query-only academic-calendar binding. It currently points touwaterloo-academic-calendar-2026..openai/hosting.jsonkeeps the Sites-managed logical app database binding;wrangler.jsoncsupplies direct Cloudflare staging and production bindings.
The two databases are deliberately separate. Application code and migrations
must never write to or migrate ACADEMIC_DB.
Prerequisites:
- Node.js
>=22.13.0 - npm
- a Cloudflare login that can read the configured academic-calendar D1
npm ci
cp .env.example .env.local
npx wrangler login
npx wrangler d1 migrations apply DB --local --config wrangler.jsonc
npm run devWrangler/Miniflare persists the app-owned DB locally under the ignored
.wrangler/ directory. The checked-in development configuration marks only
ACADEMIC_DB as remote: true, so catalog searches use the real query-only
calendar while local student data remains local. The application contains no
write path for the academic binding.
npx tsc --noEmit
npm run cf:types:check
npm run lint
npm testnpm test performs the production vinext build before running the repository
tests. GitHub Actions runs this same verification and intentionally does not
deploy.
The sign-in flow adapts ROwO's first-party /sso fragment handoff:
- ROwO returns its existing session JWT in the callback URL fragment.
- The callback removes the fragment from browser history immediately and posts the token once to the Academic Worker.
- The Worker validates it through the
ROWO_AUTHservice binding to the productionrowo-authWorker (withhttps://api.rowo.link/api/user/meas the local-development fallback), discards it, and creates an opaque, HttpOnly Academic session whose token is stored only as a hash inDB.
This flow uses ROWO_WEB_ORIGIN and ROWO_API_ORIGIN. It does not require an
OAuth client ID, client secret, or session-signing secret. Before sign-in can
work, the parent ROwO site must explicitly allow
https://academic.rowo.link and the exact localhost development origin in its
SsoPage.tsx allowlist, then be redeployed. A ready-to-apply change is included
at docs/rowo-auth-academic-sso.patch.
Browser code does not call the ROwO API directly, so this integration does not
require a CORS change.
The /api/catalog/programs* and /api/catalog/courses* routes expose bounded,
read-only calendar searches and summaries to guests. When a valid local session
is present, a course-detail response also includes an account-specific
eligibility evaluation and is returned with private, no-store caching. User
dashboards, saved programs, course records, and mutations continue to require a
valid local Academic session.
wrangler.jsonc defines local, staging, and production configurations. D1
bindings and runtime variables are repeated intentionally because Wrangler does
not inherit them into named environments. The committed staging and production
IDs point to the separately provisioned rowo-academic-users-staging and
rowo-academic-users-production databases.
ACADEMIC_CALENDAR_LABEL, ACADEMIC_CATALOG_ID, and the ACADEMIC_DB
database name/ID form the changeable calendar configuration. For a new academic
year, update all four values together in a reviewed PR, validate against
staging, and then promote to production. No application-data migration should
target the calendar database. Saved student rows keep catalog/PID/version
snapshots; a mismatch is surfaced for reconfirmation instead of silently
re-auditing an old plan under new rules.
After the one-time bootstrap, Cloudflare Workers Builds is the intended ongoing
CD path. GitHub Actions remains CI-only. Protecting main and requiring the CI
check will ensure tests pass before Cloudflare sees a production commit. D1
migrations run against the app-owned database by its explicit database name
before the Worker deploy step. The prebuild lifecycle runs ESLint before
npm run build produces deployable output. The first production Worker version
and its app-owned D1 schema were bootstrapped with Wrangler.
See docs/cloudflare-setup.md for D1 provisioning, least-privilege tokens, GitHub integration, Workers Builds commands, custom domain setup, the parent-site SSO prerequisite, and the annual calendar rollover checklist.