Teaching Pai to read Canvas

How Pai turns Canvas into a study plan that fits around a work shift, tells me exactly what changed, and learns how long my work really takes.

Canvas has everything I need to know about school. The problem is that it waits for me to come and look. Due dates move, grades post, instructors send announcements, and none of it reaches me unless I open the app and go hunting. After a full shift, that's the last thing I want to do.

So the first real job I gave Pai was reading Canvas for me. It's still the part I use most.

Read-only, with my own token

Pai talks to the Canvas API with a personal access token, the same kind any student can make in their account settings. It only ever reads. It can't submit work, post in a discussion or send a message. If the token ever stopped working, the plan was to fall back to Canvas's calendar feed, which gives due dates but no points or grades.

Reads are cached for ten minutes, so checking /due a few times in a row doesn't hammer the API. The jobs that need fresh data, like the grade watch and the planner, skip the cache.

A plan that knows I have a job

Every Sunday night Pai pulls the next two weeks of work and builds a plan for each day. The important part is what happens before the model sees anything. Code takes my work hours, class times and the windows I like to study in, and works out the actual free blocks. The model only gets to place work into real gaps, so it can't schedule an essay in the middle of my shift.

Sonnet then splits the big assignments into chunks, puts heavily weighted work first and leaves some buffer before each deadline. It comes back as structured output, so Pai can check it before sending it to me. Each item links straight to the assignment.

If my week changes, I just tell it ("I'm off Friday") and it updates the schedule.

It learns how slow I actually am

My first plans were fiction. A discussion post would get 30 minutes and a lab would get two hours, and I'd be off by a lot in both directions.

Now when I start a /focus timer, Pai ties it to whichever block in the plan I'm working on. Once the assignment is turned in, it records how long the plan said and how long it really took. Work gets sorted into kinds with plain rules: essays, discussions, quizzes, labs, reading. After three or more of a kind, future plans get scaled by my real ratio, and Pai says so when the difference is big enough to matter.

"I'm behind"

Plans fall apart. I get home late, or dinner takes longer than it should. Every plan now comes with three buttons: 🐢 +30m, 🐢 +1h and ❌ Missed it. Saying "running an hour late" in chat does the same thing.

This one doesn't use a model at all. Code refits only the rest of tonight, from now plus the delay, around dinner and a bedtime cutoff. Anything due tonight goes first, and flexible work moves to the next free slot before its deadline. The reply is one line, plus a "⚠️ Won't fit" when something really doesn't.

Telling me what changed

Every two hours, Pai compares what Canvas says now with what it saw last time. This is a plain diff in code, with no model call. When something's different, I get one message:

  • 🆕 a new assignment
  • 📅 a due date that moved, appeared or disappeared
  • 🔢 points that changed
  • 🗑 an assignment that was removed or unpublished
  • a new grade, with the score

A couple of details took some getting wrong first. The very first run only records a baseline, so I don't get a flood of every grade from the start of term. A course only counts as having removed assignments if Canvas actually returned that course this time, so a failed request doesn't look like a teacher deleted everything. And anything more than a week past due is ignored, because nobody needs to hear about it.

Past-due work that's locked shows up as locked, not missing, and stays out of the plan. There's no point scheduling time for something I can't turn in.

Announcements and messages from instructors get forwarded too, and only the new ones.

Reading the whole assignment

Asking Pai about an assignment used to get the short description and nothing else. A lot of the real instructions live somewhere else: a linked Canvas page, an attached PDF, a rubric.

Now Pai follows up to five linked pages and files. PDFs go to the model as documents, Word files and text go in as text, and each rubric item comes with its rating levels. It can also open the attachments in a discussion thread, and send me any Canvas file straight into Telegram.

Group discussions were their own puzzle. In a group assignment, the posts live on each group's copy of the topic, so the course-level view looked empty. Pai now finds my group and reads that copy instead.

Small things that add up

  • /need comm B works out what I'd need on the rest of a class's posted work to finish with a B, using the real assignment-group weights. It's an estimate, since work that isn't posted yet doesn't count.
  • A weekly review on Sunday evening: what got done, what slipped, and how my pace compares to the plans.
  • A recap when my shift ends, built from a template: what's still due tonight, what changed on Canvas since the morning, and the rest of tonight's plan.

None of this is clever on its own. Most of it is code that diffs, sorts and fits time slots, with a model only where something needs judging. But together it means I don't open Canvas to find out what's going on anymore. Canvas tells Pai, and Pai tells me.