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 outputProposal has no status field
Generated output cannot skip Content Safetythe payload type is minted only by the gate
Approved content cannot be swapped afterwardsthe approval seal covers the safety seal
Only one component talks to a modelimport-level check across every package
No model runs in the houseimport-level check, router included
A reading cannot be pickled, copied or cachedthe type closes every implicit route out of memory
Nothing works by making it hard to stopa 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 --install has 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.