# The cache that aged the thing it cached _2026-09-23 · booth_ **Thumbnails: 77.5 MB → 0.78 MB on the biggest gallery, ~100 MB → 1.12 MB on the Desk.** The operator found the defect in about a minute of using the live redesign. Two lessons, and the second nearly shipped. ## ⚠ LESSON 1 — we parked it on a count and the cost was in the bytes ROADMAP parked progressive loading on *"the largest gallery is 66 images; at that size a lazy grid is almost certainly fine"*, and the parking-lot row said *"270 `` may be fine."* **Both count IMAGES. Neither weighs BYTES.** 66 is a fine count sitting on 77.5 MB of 1024×1024 PNGs rendered at 250px — roughly 16× the pixels that reach the screen. The rule "measure the real booth before optimising it" was followed and still produced the wrong answer, because **we measured the dimension that was easy to measure rather than the one that determines the experience.** Before parking anything on a measurement again, ask what the user would actually feel. ## ⚠ LESSON 2 — a cache INSIDE the thing it describes can age it, and excluding the cache is not enough `.thumbs/` lives inside the booth on purpose, so it is swept with the booth and can never outlive what it describes. But `_newest_mtime` — which feeds `is_expired`, which feeds `rmtree` — walks the booth, so **the server writing a cache on a mere view counted as the operator touching the booth.** **The first fix passed its own test and was still wrong.** Excluding every path under `THUMB_DIR` does not help, because **creating the directory touches the BOOTH DIRECTORY's own mtime**, and `_newest_mtime` SEEDS from exactly that. The cache's contents were excluded; its *existence* was the leak. The fix restores the booth's stamp across the `mkdir`. That cannot hide real activity: any file an agent adds is counted by its own mtime in the same walk, so the directory stamp is only a seed. **The blast radius if it had reached the Desk.** The Desk pulls a preview thumbnail per booth, so ONE INDEX LOAD would have pushed EVERY booth's expiry out — the TTL would never fire again and nothing would ever sweep. Caught by design-dev from the outside, hours before the strip landed; verified after the fix on the live set: **29 booths, two Desk loads, 22 caches generated, 0 clocks moved.** `.viewed` counting as activity is different and deliberate — that is a record of a *person* looking, which U4 says is activity. **A server-written derived cache is machinery, like the `.lock` sidecars already excluded.** Any future cache inherits this: exclude it from the clock AND preserve the parent's stamp. ## A third, smaller one: the dot-namespace was only skin deep `booth_items` and `zip_booth` both tested `p.name.startswith(".")` — the FILE's name — so `.thumbs/a.png` (name `a.png`) would have rendered as a gallery item and shipped inside every zip download. CLAUDE.md invariant 2 promises a dotfile costs nothing in item counts, galleries or zips; **that was true only at the top level.** Both now skip every dot-prefixed path COMPONENT.