2026-07-31 · ~10 min
The Console I Built to Stop Drowning
I went from four classes to seven in one term. The first thing I built to survive it was a QR attendance scanner. It taught me a shape, a data-free page that reads my teacher repos live and only writes back through one door, and that shape grew into the whole console: attendance on one tab, and every AI-proposed grade held grainy until I sign it on the other.
Four classes became seven
The term my teaching load jumped from four classes to seven, nothing about the job changed except the arithmetic, and the arithmetic is what nearly finished me. I teach between 150 and 300 students in a term, part-time, on top of a full-time job that does not care how many roll sheets I have. At four classes I was tired. At seven, the mechanical parts of teaching stopped being an annoyance and became the thing that could actually end it.
Attendance was the first to break. Not the hard part of teaching, not the part I signed up for, just a tax I paid every session: names called, a sheet passed, a spreadsheet reconciled later at a desk at night. Multiply that by seven rooms and it is no longer a tax, it is a second job stapled to the first. I was losing the hours I wanted to spend on actual students to the pure clerical grind of proving they were in the room.
So I did the thing I always do when a chore scales past me. I stopped doing it by hand and built the smallest possible tool that would.
So I built a scanner
The first version was almost embarrassingly small: a web page that turns my phone into a scanner. Each student carries a QR code in their own workspace. I open the scanner, pick the section, point the camera, and the room checks itself in. A green flash means a good scan, red means wrong class or an unreadable code, and a short sound tells me without looking. What used to be a roll call is now a walk down the aisle.
The part I care about is underneath. A bare student number painted into squares would be trivial to fake, and attendance you can forge is worse than no attendance at all. So each code carries three things: the section, the student number, and a signature. The signature is a short cryptographic stamp computed from a secret only my teacher repo holds. The scanner can read a name off the number, but it cannot mint a valid code, and neither can a student sitting at home. A code that does not verify does not get quietly dropped, it gets marked as flagged, so a bad scan is visible instead of silent.
Where the data lives matters as much as where it does not. The scanner page is a data-free shell. No class list is baked into it, nothing sits on a server, because there is no server. It writes each scanning session as a small batch, a plain CSV filed by date and time in my private teacher repo, and a workflow takes it from there: it verifies every signature, flags the forgeries, and rolls the batches into a summary. Then it publishes one file back to each student, an attendance record that holds only their own dates. A student can see that they were marked present on the days they were present. A student never sees a classmate's attendance, because that was never theirs to see.
A signed code the scanner can read but a student cannot forge, and a record that only ever shows you your own days.
The scanner taught me a shape
The scanner solved attendance, but the more useful thing it did was hand me a pattern, and once I saw the pattern I could not unsee how much of my job fit it.
The shape is this. The page itself holds nothing. It reads what it needs live, straight from my teacher repos through the GitHub API, into my own browser, using a token I paste into settings that never leaves this machine. It is allowed to talk to exactly one host on the whole internet and nothing else. And it does not scatter its writes: everything it changes goes back through a single, deliberate door, never a dozen quiet ones. Data-free by default, read live, write through one door. That was the scanner, and it turned out to be the answer to a much bigger problem I had been dreading.
Because attendance was never the thing that actually scared me at seven classes. Grading was.
The shape ate grading
I let an AI draft grades and feedback for hundreds of students a term. Written down plainly, that should frighten me, and for a while it did, because somewhere in that pile is a real person whose semester I can quietly get wrong at scale, faster than I ever could by hand. The rule that lets me sleep is the one I teach: the machine drafts, the teacher signs. The honest question was always what "signs" looks like when there are three hundred of them and one of me.
For a long time the answer was ugly. The grades and the feedback lived in gradebook files across a pile of teacher repos, and reviewing them meant opening tabs, reading raw Markdown, and editing in a text field that had no idea it was holding somebody's grade. So I pointed the scanner's shape at it, and the grading review became the console's other tab.
It reads my gradebooks live, the same way the scanner reads the room, and it shows me one screen of everything the machine has proposed and nothing has committed to. Here is the part I like most. The design system I build everything with, GRAIN, exists to make one idea real: you should be able to look at software and see whose hand did what. Text a machine wrote renders with a grain to it. Text a human has settled renders clean. On the review screen that stops being a metaphor. Every grade and every line of feedback the AI proposes shows up in grain type. It looks unfinished on purpose, because it is, a draft nobody has stood behind yet. The moment I approve it or edit it, the grain lifts and the text goes clean, because now a human wrote it. I cannot mistake a thing the machine guessed for a thing I decided, because the two do not look the same.
And the console does not grade. It never reaches into a repo and writes a score, which sounds like a limitation until you see what it buys. When I finish reviewing, it does not push my decisions anywhere. It gathers every call I made into a single prompt, a plain file that says give this student this score, send this feedback, leave that one alone because I flagged it, and it files that prompt in the repo. Later, in a Claude Code session I open there, I say run the pending decisions, and the AI does the actual typing: the gradebook, the feedback files, the push to Canvas. I watch it happen. I keep the last say. This is the single write door again, the same one the scanner uses. Instead of a dozen places where something can quietly write a grade, there is one way in, and it runs a prompt I have already read. The review is not a checkbox at the end. It is the only door, and I am standing in it.
A grade is grainy until I sign it, and the console cannot write one on its own.
The wall between the machine and the student
Scaling feedback to hundreds of students only works if the scaling never costs a student the honesty of it, so every piece of AI feedback is split in two before it goes anywhere. The student-facing half is prose only. No scores, no mention of a machine, and by a rule baked into the engine's code, never the corrected line or the exact fix, only the concept to revisit or a question to chase. It reads like my own margin notes because it is standing in for them. The instructor-only half is the part meant for my eyes: a proposed grade, and a quiet flag when something looks vibe-coded. There is even a defensive line of code whose entire job is to strip the instructor half out before it can leak into the student copy. Nothing the machine writes reaches a student on its own. It all waits on the review tab, grainy, until I sign it.
Where I lock it out
The console is where I let AI multiply me. It is not the whole story, because a multiplier is only ever worth what it multiplies, and ten times zero is still zero. So there are rooms I keep it out of, on purpose, and those limits are the design, not a gap in it.
- AI is banned from the highest-stakes assessment. When I need a true read on the fundamentals, I
take the tool away, from the students and from myself. I even used AI to build that exam, the one room the machine is not allowed into, which is almost too on-the-nose, and exactly why I trust it.
- It never grades or speaks to a student unsupervised. Every AI grade is proposed, every AI
paragraph is held for my review and edit. The whole system assumes the model is a drafter, not a decider.
- The vibe-coded flag is a nudge, not a verdict. It tells me where to look harder. It never
reaches a student, and it never stands as evidence on its own.
- Student data stays away from the model. The feedback engine skips personal data on purpose, and
the console is a data-free shell that keeps student records off any server and out of the page. The safest place for student data is nowhere, so that is where it goes.
You can open the thing
The last piece I am proud of is that none of this is a story you have to take on faith. The console ships a demo mode you can open in your own browser, and it is not a mock-up. It runs the real code, with one part swapped: the connection to GitHub is replaced with an in-memory stand-in full of synthetic students. Everything else is the actual page, the actual grain type, the actual review flow, just with nobody real inside it. The claims in this post are a URL, not a paragraph.
What I am honest about
None of this makes teaching unattended, and I would not want it to be. A few seams I say out loud:
- The token is real access. The scanner and the review both act with a token I generate. I scope
it tightly, give it a short life, and keep it in my browser, but it is a key, and I treat it like one.
- The two doors are not the same door, on purpose. Grades go through the reviewed prompt, because
a grade is a judgment. Attendance commits more directly, because a scan I made in the room is already my attestation. I decided that split deliberately, and I can defend it.
- The console cannot make me care. It removes the tab-juggling, the roll call, the raw-Markdown
editing. It does not remove the reading. If I rubber-stamp a screen full of grain, that is on me, not on the tool. It makes the right thing easy. It cannot make it happen.
I went from four classes to seven and built a scanner so I would not drown. What I actually built was a shape, and the shape turned out to be the whole answer: hold no data, read live, write through one door I am standing in. Attendance checks itself in. Every grade shows up grainy. I read it, I decide, I sign, and only then does a student ever see it.
The judgment is human. The typing, by design, is not.