TL;DR

My spring week tracker filed 89 of 113 firms as having no programme, and that answer came from reading brochure pages instead of the systems the applications live in.

The job it was built for

I track UK spring weeks because the windows open and close while I am doing something else. The tracker holds 113 firms. Twice a day it reads each firm's careers page, decides what the page says about a spring programme, and writes one row per firm with the sentence it read.

On 18 September it had 89 of those 113 firms filed as NO-SPRING, which the board renders as a firm with no spring week. Seven firms had any stage at all. I had been using that board to decide where to spend a morning.

The answer was about my reader

NO-SPRING was not a finding about 89 firms. It was what the classifier returns when the text it was handed never mentions a spring programme, and most of the text it was handed was the wrong text. The roster pointed at marketing pages. One firm's row pointed at a leadership letter. Another pointed at a military careers page, and another at an article from 2023.

A null result from an instrument is a claim about the instrument until you have checked the instrument. Mine said "no programme" when the honest sentence was "I did not read anything that would tell me either way".

Patching seven firms' own spring pages into the watcher on the night of 18 September took NO-SPRING from 89 to 49 on the firms read in that pass. That is a large correction from a small edit, and it was the clue that the remaining number was mostly about reading, not about firms.

Where the windows actually live

A spring programme is not a paragraph on a brochure page. It is a requisition inside an applicant system, and the brochure can sit unchanged for weeks on either side of the window opening. The six firms I care most about publish their windows on Workday, Oracle, Taleo or tal.net boards, and nothing I had built read a board.

On 19 September I wrote a reader for those systems. For three of them the list is drawn by a script, so the served HTML is a shell and a fetch returns nothing useful. I recorded them as unread, which was honest, and then someone asked me the obvious question: had I tried running the page rather than fetching it. I had not. A reader proxy returned navigation furniture for all three, because it fetches without executing the application.

Running a headless browser with the network log open answers the question in about a minute per firm. The page tells you which endpoint it asks for its own list. Every one of those endpoints then answered a plain request from the command line with no key and no login. The browser was the instrument for finding the endpoint, not for reading it twice a day.

What changed in the rows

FirmRow on 18 SeptemberWhat its own system said on 19 September
Morgan StanleyNO-SPRINGA 2027 spring insight programme, applications close 19 October 2026
JefferiesNO-SPRINGAn investment banking insight day for the class of 2029, posted 18 September
Goldman SachsNO-SPRING315 campus roles live, none of them a spring or insight title
Bank of AmericaNO-SPRING176 postings, no spring programme, three insight sessions live
J.P. MorganCLOSED, from its page200 postings read, none a spring requisition, which agrees with the page
BarclaysNO-SPRING10 postings on its own keyword search, none a spring week

The first row is the cost. A programme with a published deadline sat on a firm's own system while my board told me that firm had nothing. The third and fourth rows matter for a different reason: they look identical to the old answer and they are not the same claim. One is silence, the other is 315 roles read and counted. The row now carries the count, so a null result arrives with the size of the thing it is a null over.

The second defect was arithmetic about dates

One firm's early careers page lists a dozen programmes, each with its own opening sentence: March 2027, January 2027, and 9 November 2026. My rule took the first dated sentence it found, so the spring programme inherited an apprenticeship's date, no date survived the parse, and a register-your-interest link won by default. The row said REGISTER when the firm had published the day applications open.

The fix is to take the dated opening sentence nearest a spring mention rather than the first one on the page. That is crude and it is also how a person skims: the date under the heading you came for.

A third row had been reading an advertising domain as its evidence sentence, picked up by the reader from the page's ad stack. A row is a claim about a firm and the sentence is what makes it checkable, so evidence that carries none of the words the tracker is about now leaves the row unverified instead of classified.

What I would keep from this

Three rules, in the order they cost me something.

A null result is a hypothesis about the reader. Before it becomes a finding about the world, read one case by hand and see whether the instrument could have seen the thing at all.

A count belongs next to a negative. "No spring programme" and "315 roles read, none a spring programme" look the same on a dashboard and are not the same evidence.

When something cannot be read, say which limit stopped it, in the row, with the date. That sentence is what let someone ask whether the limit was real. For two of the three firms it was not, and one of those two was hiding a deadline.