Lookout watches the pages you care about and speaks only when something changed. Runs on your machine: no API key, no network, no model, dependencies = [].
Project 3 of 5 · Agent Lab Vol 4
Page: https://dev48.infy.uk/agentlab/vol4-03-lookout.html
Repo: https://github.com/dev48v/lookout
The word doing the work is changed, and six reasonable definitions of it disagree on most of a corpus.
The corpus is invented, and the labels came first
There is no public archive of one person's watched pages with the meaningful changes labelled — and building one out of somebody else's browsing would be unavailable and a privacy problem. So 60 snapshots across 12 sources were written by hand from a fixed seed.
Each carries a declared label: would a person have wanted to be told? Made once, in the open, in pages.py, before any detector ran. That is what makes precision and recall counts rather than a judgement formed after seeing which detector won.
The obvious detector alerts on every single fetch
48 consecutive fetch pairs. Four are worth telling somebody about.
| detector | alerts | false | missed | precision | recall |
|---|---|---|---|---|---|
| hash the whole response | 48 | 44 | 0 | 0.0833 | 1.0000 |
| strip the tags, hash the text | 48 | 44 | 0 | 0.0833 | 1.0000 |
| hash the main region only | 12 | 8 | 0 | 0.3333 | 1.0000 |
| main region, numbers masked | 7 | 4 | 1 | 0.4286 | 0.7500 |
| the set of words | 3 | 0 | 1 | 1.0000 | 0.7500 |
| words + numbers in content | 8 | 4 | 0 | 0.5000 | 1.0000 |
Hashing the response fires 48 of 48 — a rotating CSRF token, a build stamp and an ad slot are all "changed". For every real change it interrupts you eleven times. A watcher you mute is a watcher you do not have.
Stripping the tags first changes nothing at all. Same 48, same precision. The noise was never in the markup.
The fix and the flaw are the same edit
boilerplate counter reordered one number the real change
raw-hash 24 4 4 1 3
main-normalised 0 0 4 0 3
word-set 0 0 0 0 3
word-set+numbers 0 4 0 1 3
The quietest detector is perfectly precise — three alerts, three real, zero false. It ignores a rotating token, a live view-counter and a re-ordered team list.
It also misses a price going from $49 to $59, because masking numbers is exactly what made it quiet. There is nothing in the HTML that distinguishes Viewed 4,182 times from $59 — both are a number in the main region that used to be a different number.
Put the numbers back: recall returns to 1.0000, the counter returns with it, precision falls to 0.5000.
No detector on this corpus gets both. Every one of the six is either noisy or blind. A change detector is a definition of "meaningful" applied by a program with no idea what the page is for — so the honest version of speaks only when something changed is speaks when this particular definition of changed says so.
What checks what
33 pytest, several of them controls. A differential verifier at 524 asserts lifts the page's engine out of the HTML and diffs it against the real Python package — all 60 generated documents by SHA-256, plus every score. Page self-check 18. Zero failures.
Two embedding hazards it caught, both worth knowing: the engine generates a fake page containing a literal </script>, which terminated the tag and blanked the page until the string was split — and the same corpus contains window.__nonce= as data, which made the verifier's "does not touch window" scan fail. That scan now blanks string literals as well as comments before looking. Check the code, not the text.
MIT. Python standard library only.
Top comments (0)