TDS · 26T2 Review
TDS Term Review · May 2026 · Internal

What were we
measuring?

Across one term, 1,070 enrolled students left 57,033 saved attempts and 777,874 per-question scores in the exam portal. Every keystroke that mattered was timestamped. That record answers a question the marks themselves cannot: did the graded assignments measure capability, or something else?

They measured something else. Students who routed one shared URL scored 91.3% on graded assignments and 50.5% on the timed exam. Students who never touched it scored 64.2% and 59.1%. The assignments did not merely fail to detect the shortcut — they inverted, ranking the students who learned least at the top.

This is not a story about cheating. Under the course's stated policy — "Anything is allowed. Help from the Internet, ChatGPT, friends, pets, hacking, … anything" — not one student in this review broke a rule. The policy was not the failure. The failure was that the assessment offered no question a single shared service could not answer.

At-a-glance · 26T2 · May–Sep 2026
"Anything is allowed. Help from the Internet, ChatGPT, friends, pets, hacking, … anything. Just get the job done!"

— Instructions shown at the top of every graded assignment, 26T2

Chapter 00 · Overview

The term in numbers.

Before anything can be fixed, the shape of the term has to be visible. Nine graded assignments, two projects, one timed exam, and 5,305 refused saves. Participation held up better than the room suggested — 632 students still submitted GA8, 59% of those enrolled — while live-session attendance fell from roughly 150 to zero.

Those two facts belong together. Hundreds of students kept submitting assignments while nobody attended the teaching, which is the first clue about what the assignments were actually asking for. The second clue is in the clock: the median student's work on an assignment fell from 22 hours on GA0 to 20 minutes on GA8.

Effort per assignment, across the term
Median minutes between a student's first and last accepted save. GA0 took 22 hours; GA4 took 15. The GA5 spike is its two deadline extensions stretching the window open, not students working longer — 31% of GA5 saves were refused.
Query effort · median first-to-last save span, accepted attempts only
Who opened, who submitted
The access log records every student who opened an assignment page; the submission log records who saved. The gap between the lines is students who looked and left. Participation declines from 78% at GA1 to 59% at GA8 — real, but this is not a cohort that walked away.
Query participation · /stats access log joined to submissions
Chapter 01 · Cohorts

Five cohorts.
One broken ruler.

Every student who sat the Remote Online Exam can be placed on a single axis: how many graded questions they answered by submitting a URL that other students also submitted. That is an observable fact in the answer text, not an inference about intent.

The Remote Online Exam is the control. It ran in a fixed 45-minute window on questions nobody had seen, so a shared service cannot sit it for you. Comparing each cohort's assignment average against its exam average is therefore a direct test of whether those assignment marks meant anything — and the five cohorts answer it in five different ways.

Read the cards below as operational descriptions, not character judgments. Nobody in any of these bands broke a rule. The differences between them are differences in what the questions allowed, and each band needs a different fix.

The ruler stretches as you go down
One row per cohort, ordered by how much they used a shared endpoint. Each row shows the same students measured twice — pale dot = the timed exam, solid dot = the graded assignments — and the bar between them is the disagreement. Read top to bottom: the assignment dot slides right, the exam dot slides left, and the bar between them stretches from 5 points to 41.
Query inversion · 771 students with both an assignment average and an ROE score
Every student, one dot
The cohort averages hide the shape. Plotted individually, the students who used a shared endpoint pack against the right-hand edge — near-perfect assignment marks — and spread freely down the exam axis. The dotted diagonal is where a student's two scores would be equal: on it, the assignments predicted the exam. Points far below it are students the assignments over-rated, and the orange cloud lives there.
Query inversion_scatter · one point per student
Chapter 02 · Assessment

Twenty-two hours became twenty minutes.

GA0 is what this course looks like when nobody has the answers yet: 22 hours of work, 7% on the first attempt, 63% by the end. Students iterating, failing, improving. That is a functioning assignment, and it existed in week one — the course was capable of producing one.

By GA8 the median student finished in twenty minutes, scored 74.6% on their very first save, and a quarter of the cohort cleared 95% before iterating once. There was nothing left to iterate on. They arrived with the answers.

This did not start in May. January 2026 decayed too — but May decayed from a lower start and faster, hitting a fifteen-minute median by GA4, a level the previous term did not reach until GA5. Whatever was spreading, it spread faster the second time, because it did not have to be built again.

Set against the finalised marks, one component stands apart. The assignments settle between 66% and 92%, both projects land in the sixties and eighties — and the ROE alone sits at 50.3%. It is the only component where a shared endpoint could not answer for anyone.

The collapse, from four angles
Median score on the first save, median best score, the share clearing 95% on their first attempt, and the share who saved exactly once. All four say the same thing in different units.
Query effort
Two terms, same assignments
Median minutes of work per assignment, January 2026 against May 2026. Both terms decay. May starts lower and falls faster — the decay is structural, not a one-term accident.
Query effort_two_terms · comparable GAs only
Component timeline — the whole term
Each solid dot is one component's finalised average, positioned by when its window opened and sized by how many students submitted. Where offline marking moved the number, a hollow dot shows what the portal displayed at submission instead — Project 2 was 19.9% on the portal and 87.5% once evaluated. The bootcamp is drawn hollow — it carries no finalised mark.
Query component_scores + calendar · finalised marks from the marks record, portal scores from the submission log
Chapter 03 · One Address

One address,
five hundred students.

Many assignment questions asked students to deploy a working service and submit its URL. The grader would call the URL, check the response, and award marks. The answer to the question was, literally, an address.

The most-submitted address of the term was tds-roe-solver-api-t12026.onrender.com. The t12026 is not decoration — that service was built for the January 2026 Remote Online Exam. It was still running in May. It was still running in August. Over the term, 514 distinct students submitted it as their own deployed service, across 46 questions in 7 of the 9 graded assignments.

It was never the only one. And for GA8, somebody deployed one called tds-ga8-shared-…. Nobody was hiding this.

Addresses submitted as a student's own service
Counted by distinct students who submitted each host. Repositories and file hosts are excluded — these are answers, not references. The grey bars are loopback addresses no grader could ever reach: 236 students submitted 127.0.0.1:8000, and roughly 240 more submitted other localhost ports.
Query endpoints · threshold 20+ distinct students
Adoption, week by week
The week each student first submitted a shared endpoint. Adoption stepped rather than crept — one jump per assignment release — and it never reversed. No student who found the link went back to doing the work.
Query endpoint_adoption · week beginning, IST
What the grader did about it
The uncomfortable part is not that students used it. It is that it worked essentially every time. On GA8, 99.9% of shared-endpoint answers scored. Across the term, 20,791 of 118,224 assignment marks — 17.6% — were awarded on an answer whose entire content was a URL somebody else had deployed. The grader was validating the endpoint. It was never validating the student.
Query endpoint_diffusion · click any column heading to sort
Where the answer wasn't a URL, it was copied verbatim
GA1 had almost no endpoint questions, and almost no endpoint farming. It got copied a different way — byte for byte. Two mechanisms, one cause: the answer was something that could be passed along intact.
Query identical_answers · answers of 20+ characters shared by 25+ distinct students
Chapter 04 · Questions

Server-side questions made it worse.

The design intuition was reasonable: a question whose answer is a live service the grader calls should be harder to fake than a static answer. You have to actually build something. The data says the opposite, and it says it clearly.

The reason is structural. When the answer is a URL, one working service satisfies every student who holds the link. Server-side questions concentrate the entire cohort's work into a single artifact that costs nothing to copy. They are the most farmable question type in the set, not the least.

Share of questions server-side vs share farmed
GA8 was 100% server-side and 80% farmed. GA1 was 20% server-side and 0% farmed. The relationship runs the wrong way. A question counts as farmed when a shared endpoint was submitted for it and still scored.
Query question_resistance
GA6 was the exception
One shared-endpoint attempt across the whole assignment, scoring zero. The evaluation has to execute on the student's own machine, against state the student holds. There is no URL to pass around, because the deliverable is a local run.
10%
Of GA6 questions farmed, against GA8's 80%
Be honest about what that proves
GA6 was not immune by design; it was immune because nobody had automated it yet. But the mechanism is real and repeatable. Every assignment that resisted had the same property: the answer could not be a pointer.
46
Questions across 7 assignments that one address answered
And the grader couldn't tell
A validator that fetches a URL and checks the response cannot distinguish the student who built the service from the 513 who were handed the link. The check was never wrong — it was answering a different question than the one being graded.
17.6%
Of all assignment marks awarded on a shared URL
Chapter 05 · Marks

The score students saw
was not the score they got.

Project 1 awarded portal marks for an answer being present. The analytical judgment came later, offline. Which means that at the moment a student clicked save, the portal told them something that was not true.

Ninety-nine students in a hundred closed the Project 1 tab having been told they scored 12.5 out of 12.5 on Question 1. Then the offline evaluation ran, weeks later, and took much of it back. What followed was the largest single category of forum traffic in the term.

Be precise about what those students were arguing. They were not disputing a hard mark. They were disputing a mark the system had already promised them. That argument was unwinnable, and it did lasting damage: it taught the whole cohort that offline evaluation was arbitrary, which then poisoned the reception of Project 2's grader — which did not deserve it.

Project 1: students shown full marks at submission
Computed on each student's final accepted attempt. Any question with a high bar here was, at submission time, telling the student they had scored full marks on it.
Query p1_portal_vs_real
The gap the portal left open
Hollow is what the portal showed at submission; solid is the mark that stood. Both projects moved a long way after the student clicked submit — Project 1 by 20.4 points, Project 2 by 67.6. The difference is disclosure. Project 2 banked 2.5 of 12.5 per question and named the rest pending, so its gap was announced. Project 1's was not, and per question it moved in both directions — which is where the disputes came from, not from the average.
Query component_scores
Chapter 06 · The Judge

The judge is better
than its reputation.

The loudest grievance of the term was that offline LLM evaluation is a black box. That is two different complaints, and they deserve different answers.

For Project 1 and ROE the complaint is valid. No rubrics, judge prompts or per-question offline scores survive. A student who believes they were misgraded has nothing to appeal against — and the course team has nothing to defend with. That is not a grading disagreement; it is an absence of record.

For Project 2 it is not valid, and Project 2's grader is the template worth building on. It published the rubric, scored every submission across 126 named dimensions, quoted the student's own text as justification for each score, and gave each student a code to look up their breakdown. It also discriminates — but its two real faults are compression and length-bias, not opacity.

Does effort move the mark?
Students split into five quintiles by how much they wrote across all eight exercises. The judge is not random — the direction is right. But look at the vertical range: every quintile lands in the 80s, and the whole cohort averages 87.4 out of 100 with a standard deviation of 8.3.
Query p2_effort_vs_grade
A six-level rubric used as a two-level one
Across 5,643 question grades, 88% landed in the top two of six bands and only 91 fell below "mixed". A judge with one and a half usable bands cannot separate good work from adequate work.
Query p2_verdicts
The length channel
Characters written against the analytical mark, one point per student. The correlation is r = 0.57. Some of that is honest — thorough analysis genuinely runs longer. But a 0.57 correlation with raw character count is a channel, and a cohort that found a shared endpoint in week two will find a verbosity channel in a week.
Query p2_scatter · 710 students
What students actually failed at
All 29,623 rubric-dimension scores, on the rubric's own 0–4 scale, ordered weakest first. Note how little of the scale is used: every dimension lands between 3.11 and 3.81. The three strongest are usefulness and clarity, decision/action quality and conclusion fit. The four weakest are causal reasoning, investigative reasoning, calibration against alternatives and disconfirmation. Students produce a clear, useful, well-fitting answer and cannot show the reasoning that got them there. That is the gap an agent does not close — an agent will hand you a confident conclusion; it will not tell you which alternative it failed to rule out.
Query p2_dimensions · dimensions graded 400+ times
Chapter 07 · Instructions

Three hundred and forty-eight
spellings of one instruction.

Project 1 Question 2 required a Google Drive folder, shared with the graders, under a stated name. Both halves of that requirement failed at scale, and the failure was only discovered weeks later when a human went looking.

It is tempting to read that as carelessness, and the course team did — these were called silly mistakes, and students were told they should have read the instructions. Both sides were right, which is why the argument never resolved: the instruction was clear, and the students genuinely had no way to know they had failed it.

Every one of these failures was machine-checkable at submission time. The portal could have called the Drive API, confirmed access, checked the name, and refused the save with a reason. A rule that 600 people implement 348 different ways is not a discipline problem. It is a missing validator.

Did the graders get access?
68 students never successfully shared the folder — and were still inaccessible after a second follow-up request. They lost the question outright, having submitted work they believed was complete.
Source dumps/q-interview-requirements-audio-pending-access.csv, query drive_access
And what did they call the folder?
The five most common names, out of 348 distinct names used by 600 students. Even the most popular spelling was used by fewer than one student in twenty — against a single stated convention.
Query folder_names, folder_name_count
Chapter 08 · Calendar

The course kept
colliding with itself.

Every exam config carries an open date. The portal records the first save that actually succeeded. The gap between the two is how late the assignment really shipped — and GA1 shipped 28 days late.

The churn did not stop at release: the GA3 and GA5 question sets took ten commits each after their nominal open date, while students were working on them. The content repo tells the same story — 44 commits during the term, many of them pure date corrections, and Week 8 teaching content landed on 26 August, eight days after GA8 was released. The assessment shipped before the material it assessed.

Slippage has to be absorbed somewhere, and here it was absorbed by compressing the end of the term: GA6 open to 9 August, GA5 extended to 10 August, ROE sitting on 2 August. Three assessments and an exam inside a fortnight.

Days late, per component
Between the open date compiled into the exam config and the first save the portal actually accepted.
Query calendar
Share of saves the portal refused
GA5 and GA3 alone account for 3,116 refused saves — students who did the work and were told no, and who then could not tell whether the fault was theirs or the course's.
Query rejections
GA5, day by day
On 26 July — GA5's original deadline — the portal took 2,442 save attempts and refused 906 of them. Students had been raising it on the forum for five days ("GA5 Query: Guidance Request for Q9, Q10, and Q11 (Repeated Grader Errors)", 35 posts). The deadline was then extended twice, to 10 August.
Query ga5_daily
The full calendar
Config open date, first real save, deadline, commits to the question set after release, and refused saves. Click any column heading to sort.
Query calendar · commit counts from the exam repo's git history
Chapter 09 · Timing

When students work.
When students ask.

The timing data turns the review into an operations question. Roughly a third of every component's saves land between 22:00 and 06:00 IST, and close to half of Project 1's landed in the final twenty-four hours. These are working students fitting a degree around a job.

Any support model built around daytime availability is serving a different course than the one being taken. The forum follows the same rhythm, which means pinned answers, validator reliability and TA coverage matter most at predictable moments — and those moments are nights and weekends.

Final-save timing vs score
Each student's last accepted save, grouped by how far it landed from the deadline. Graded assignments and projects diverge sharply here: for GAs early finishers score higher, while projects show the opposite — because the project's portal score was participation marks, which reward submitting at all.
Query deadline_impact
Deadline pressure, by component
Where each component's final saves landed. GA5's flat profile is its twice-extended deadline, not calm working.
Query deadline_impact, timing_deadline
Submission rhythm by hour and day
Every accepted save in the term. The load climbs steadily through the day and peaks at 23:00, and Sunday alone carries 32.7% of it against Monday's 7.6%. Operationally predictable — and nowhere near office hours.
Query submission_rhythm · times IST
Forum rhythm by hour and day
Forum demand is less spiky than submissions but follows the same cycle. Students ask for help at exactly the hours when many are also submitting.
Query forum_rhythm · times IST
Submission pressure heatmap
The full day-by-hour matrix separates two patterns. The single brightest cell is Sunday 13:00 — ROE's 45-minute exam window, which alone drew 3,976 saves. Everything else is ordinary deadline pressure, and 48.9% of it lands at the weekend.
Query submission_heatmap
Forum demand heatmap
Forum posts rise in the same windows as submissions, so support coverage has to account for deadline bursts rather than a flat weekly rota.
Query forum_heatmap
Chapter 10 · Forum

A grievance desk wearing
a classroom's clothes.

281 threads were created in the course category during the term, carrying 1,816 posts. Labelled by what each was actually for, from its own text: 88.3% administrative friction — marks, bugs, deadlines, schedules, rules. 6.0% about learning the material.

One hundred and twenty-seven marks disputes. Forty-eight portal bugs. And four threads in the entire term asking for help with the actual task. It got worse as the term went on, from 55% marks-and-bugs threads in May to 72% in August.

Here is the counter-intuitive part. The obvious diagnosis is that support was slow, so students got angry. That is not what happened. Median time to a first reply was two to three hours, every single month, and only 17 of 281 threads went unanswered. By any normal measure this forum was well served. It was simply serving the wrong function — students were not asking how to do the work, they were contesting the score.

What the forum was for
All 281 threads grouped into three families. Labels come from an embedding nearest-anchor classifier; the fine-grained labels are noisy but the family split is robust, because low-confidence confusions fall almost entirely within the friction family.
Query forum_family · method in classify_forum.py
Threads by intent
The fine-grained view. Marks disputes outnumber requests for technical help by thirty-two to one.
Query forum_intent
Friction as a share of the forum
Marks-dispute and portal-bug threads as a percentage of all threads created that month. September is only four threads — ignore it.
Query forum_friction_trend
Response was never the problem
Median hours to a first reply, and threads that never received one. Fast, every month. The forum's problem was its workload, not its latency.
Query forum_monthly
The most-viewed threads of the term
Ranked by views, with the intent label attached. Read the titles in order — they are the term's story told by the people living it. Click any column heading to sort; thread titles link to Discourse.
Query forum_top_topics
Chapter 11 · Responders

Students talked.
The team resolved.

The May 2026 course team was seven people — the professor, two instructors and four teaching assistants — plus five Learning Assistants working against a separate exam-dev deployment. Two ex-TAs from the January 2026 term also kept answering. This chapter takes that roster and asks the forum what each of them did with it.

The headline is a genuine split. The team wrote only 26.4% of the term's posts, so on volume the forum is a peer network. But they wrote 158 of its 184 accepted answers — 86%. Students do the talking; the team does the resolving. Both of those are worth knowing, and the second one is usually missed.

The load inside the team is very unevenly spread: two teaching assistants account for 297 of the 479 team posts, and one of them holds 93 of the 158 accepted answers. The two roster accounts that never posted at all are both Learning Assistants, covered separately below.

Month-by-month coverage
The share of each month's threads whose first reply came from someone on the roster. The rest were answered by other students first — which is not a failure, but it does mean the first voice a student heard usually could not change their mark.
Query coverage
Team against everyone else
Every post in the term, split by whether its author is on the course-team roster. The peer network is doing most of the talking.
Query responders
The bench, in detail
The professor, both instructors, the four TAs and the two ex-TAs, ordered by posts. The Ex-TA cards were appointed in January 2026 and kept answering with no assigned load. Learning Assistants are counted separately below — they worked on a different deployment.
151
Posts from the two ex-TAs
Two of the four heaviest responders are ex-TAs, appointed in January 2026 and carrying no load this term. They answered anyway — 151 posts and 22 accepted answers, close to a third of everything the team contributed. That is a pipeline worth formalising rather than depending on informally.
2
LA posts on the forum
The five LAs worked against a separate exam-dev deployment rather than production, so their bug-finding leaves no trace in this dataset. On the forum, one of them posted twice and the other four never posted. Whatever the programme achieved, it achieved it invisibly — which is itself the problem: there is no record to review, credit or improve.
2
Posts from the two instructors
Both instructors posted once each across the whole term, in two different threads. The forum's 127 marks disputes are the one category a classmate cannot settle — and an instructor appeared in one of them. The teaching assistants absorbed the rest, without the standing to close an appeal.
Conclusion · Causes

Five causes.
One test.

Ordered by how much of the damage each accounts for, and paired with the change the data supports. Every one is actionable with infrastructure that already exists.

Underneath all five is a single design target, and it is falsifiable: every graded component should rank students in roughly the same order as a timed, unseen-question exam does. Where it doesn't, the component is measuring access, not capability. That can be checked in week one rather than in September.

And what did not cause it
Worth stating plainly, because these are the easy explanations and the data does not support any of them.
ROE deserves the last word: 787 students, 54.6% average, the only component of the term that produced a usable spread. It was the most complained-about component and the only one that measured anything.
Appendix · Method

How to read this data.

Everything here is descriptive. Where a relationship is causal the text says so and gives the mechanism; where it is only an association, it is labelled as one. Nothing in this review identifies a student as having broken a rule, because under the term's stated policy none of them did.

The whole document is reproducible. Raw data is pulled by collect.py, normalised by build.py into a DuckDB database, and queried by export_story.py — one named query per figure. This page computes no statistics of its own; every panel names the query behind it. A full rebuild from raw takes under a minute.

Measurement rules
    Known limitations
      Two terms, as collected
      The comparison term is January 2026 (26T1), pulled with the same code path.