Repository navigation
A third-party audit of 8 October, worked end to end. All four of its findings were real. Investigating the first found a second defect underneath it that the audit had not seen, and the two compounded — one was manufacturing the condition the other mistook for the end of a walk.
If you use
--mode events, this release changes what you get and what the sidecar calls it. A walk used to stop at the first event page that produced no markets and still reportstatus: completewith exit 0. Expect row counts to rise sharply, expectcompleteto start meaning what it says, and note thatno_new_productsis no longer a stop reason this project emits.
The walk stopped early and called itself complete
Reproduced end to end against the audited commit: one empty event page at position 4 of 20 left 17 event pages never fetched, with status: complete, exit 0, and pages_completed: 4 of 21 in the same sidecar.
The rule came from a sibling repo whose pages are consecutive days, where stopping early genuinely saves fetches. Here pages 2..N are the event URLs page 1 named, capped at --pages - 1 — a finite list, nothing to exhaust, nothing saved. The walk now visits every planned URL and records the empty ones as pages_without_rows.
Every page the site serves was classified as its own 404
Next.js inlines its 404 template into the payload of every page. The not-found check scanned the first 200,000 bytes for that template's wording. Counted on 2026-10-09 across the twenty event pages /predictions named, as the site serves them:
| carry the wording | 20 of 20 |
| …inside the scanned window | 17 of 20, always at ~134,000 bytes |
| …rendered into the body markup | 0 of 5 checked, bogus pages included |
| misclassified while holding markets | 12 of 20, holding 5 to 53 each |
empty carries parse: False, so those rows were discarded — which produced exactly the zero-row page that ended the walk above.
A marker on every good page is not a marker, so the text markers are gone rather than narrowed, and the content check moved to the top: a page whose payload names markets is content, whatever templates it also ships. Two measurements shaped the fix — a slug that does not exist answers HTTP 200 (4 of 4), so the docstring promising "a real HTTP 404" was wrong; and digest, which looked like a clean replacement, is on 2 of 10 good pages and was not adopted.
The browser engines escaped this by byte position rather than by design: the template sits at 511,547 in a browser's DOM and 134,033 in the served bytes of the same URL.
Also in this release
- Atomic writes.
open(path, "w")truncates before a byte is written, so an interrupted run left a shorter file where a complete one was — measured here, a 204,292-byteout.jsoncame back 28 bytes and invalid. All three writers go through one_atomicnow. The mode is the part eight sibling repos get wrong, measured by calling each one: a new file gets whatopen()would have given it, and a target somebody tightened keeps its own. - The canary runs all three engines daily, compared on ids and schema rather than on prices, since the runs are minutes apart and a prediction market reprices continuously. Verified before shipping: the same 5 markets, 40 columns and 22 stable fields, with
scraped_atthe only field that differed. - The Docker base is pinned by digest, with the tag kept beside it.
events_on_page_1was counting the events whose rows were not upgraded by a deeper read — 3 reported where page 1 had named 11, printed besidetotal_events: 23475.- A check that failed a fresh clone's first command, found by cloning the published repo and typing what the README says.
Declined, with reasons
- Lock files. They would stop the daily canary testing new releases of the libraries, which is what catches a dependency change the morning it happens. The image is a build artefact and should be reproducible; the canary is a detector and should be exposed.
- A run identifier across the JSON, CSV and sidecar. A failed run writes no sidecar at all,
saverefuses to overwrite good output with an empty result, and each file is now replaced by an atomic rename. An id would add a second thing to keep consistent. - A queue, scheduler, storage and webhook layer. A product decision rather than a defect; this is a scraper with a file output contract, and the README says so.
Every change in this release was verified by planting the fault it prevents and requiring the suite to go red on a named check — 22 controls in all. Five of them behaved unexpectedly on the first attempt, and every one of those exposed a hole in the checking rather than in the code — a control that crashed instead of naming a check, three that left the suite green because the guard they targeted could not fail as written, and a crash detector that called every control a crash because a passing run of this suite prints a traceback on purpose.