A hackathon project, honestly described
What is built
An afternoon has run end to end in a house: devised, approved in a browser, printed, filled in, scanned, continued from what came back, and closed on its own hour. That happened once, on 24 August 2026, and four defects surfaced that day — none of which had been found by a test. They were found by somebody standing next to the printer.
This is a hackathon project and it is not a finished product. It is also not a demo: the parts below run against real services, with real credentials, on hardware in a room. Both of those things are true at once, and the point of this page is to say which is which.
Running
- The cloud tier, the parent panel and its API, the model router with real credentials, and the content-safety gate.
- The hub services that serve the displays, pull an hourly picture, print, scan and answer a button press inside the same HTTP response.
- The afternoon format and its six checks, a second model that rereads every afternoon, the parent approval, the page drawn whole and read against its blank, and the clock that guarantees an ending.
- The picture archive, and usage accounting with a per-household cap.
- Identity: Entra External ID for parents, a separate workforce app registration with an app role for administrators, per-household device keys for the house.
Enforced, not merely intended
Each of these is a test rather than a convention, and each was mutation-checked: the violation was injected and the test was watched to fail. If you change the design so one no longer holds, please change the product rather than the test.
| Agents cannot approve their own output | Proposal has no status field |
|---|---|
| Generated output cannot skip Content Safety | the payload type is minted only by the gate |
| Approved content cannot be swapped afterwards | the approval seal covers the safety seal |
| Only one component talks to a model | import-level check across every package |
| No model runs in the house | import-level check, router included |
| A reading cannot be pickled, copied or cached | the type closes every implicit route out of memory |
| Nothing works by making it hard to stop | a vocabulary check across storage, panel, browser and prompts |
Every push to main runs ruff and the suite on a clean checkout, and leaves a record: 962 tests, about 1 m 30 s. It is deliberately not a gate on the deploy — a check that can block a deploy on a Saturday is a check somebody disables.
Not built, and not enforced
- The handheld camera. The object is being assembled by hand;
vision/is an empty Python package and nothing in the repository takes a photograph. Every guarantee stated about the camera is a design decision that no test would notice being dropped. A device on a bench and an empty package are not the same distance from working, and the second is the longer one. - The button panel. Also an object on a bench. Today there is one button per display, and it is enough to close the paper loop and not enough to answer a question.
- That a reading never lands in a store. The type refuses to be pickled, copied or cached, but the reading travels from the house to the panel and back as a request body, and nothing stops either end writing that body down. One named household deliberately does keep it, for a fortnight, while this is being built.
- The seventh property. That every moment has an answer that can be wrong, and that the last moment produces something worth keeping, is specified and not checked. A plan that fails it is saved.
- A second house. Never provisioned. The installer's
--installhas never been run.
What the failures taught
The defects worth reading are in the repository with their measurements attached, because a system like this is mostly the record of things that turned out to be false. Three that changed the design:
- A per-cell threshold computed with Otsu reported 45 % ink on visibly empty boxes, and the synthetic test passed — because a uniform white patch makes Otsu degenerate. Only real paper showed it.
- A length cap on one field was being hit in 6 attempts out of 11, and each overrun cost a second full model call. The cap protected nothing physical: it was shaping the output instead of bounding it.
- A validation rule compared a whole sentence as a substring, so an afternoon that named the same object twice in different words was refused. The rule was reading a phrasing rather than a fact, and it was the single most frequent refusal.
The system in photographs, page by page ·what is not built, ranked, each with a cost ·the lines this project will not cross
This site sets no cookies, collects no statistics, and loads nothing from anybody else's server.