feat(dates): creation and update times for every booth, from the filesystem

The operator: "I think I want creation and update dates on the booths now too."

UPDATE was already there — `landed_at`, the newest mtime among CONTENT
excluding our own machinery, which the Desk already sorts "new since you looked"
by.

CREATION had no honest source. `.booth.json` carries a declared `created`, but
only for booths posted through the CLI since U5 — TWELVE OF THIRTY live booths
had none. Every alternative was a guess wearing a fact's clothes: oldest content
mtime is wrong the moment an agent copies files with timestamps preserved;
directory mtime is just "last thing added", which is landed_at renamed; and
stamping a first-seen marker on read is the same write-on-read shape that spent
an hour of today aging the booth it cached.

ext4 records a real birth time. CPython does not expose st_birthtime on Linux,
so booth/birthtime.py reads it through statx(2) — a fact the disk already holds
rather than one we invent. Verified against stat(1) on live booths, 6 of 6
exact, including every booth with no manifest. ONE rule for all thirty, which is
what invariant 6 asks of anything statable in a line.

None when the filesystem cannot say (tmpfs, NFS, an old kernel), and None
renders as nothing — the honest output when nobody knows. Never raises:
list_booths calls it once per booth on every index load, so a read that can
raise is a service-wide outage wearing a single-booth bug's clothes.

ALSO TWO REAL TEST-HARNESS DEFECTS, found chasing a flake and fixed on their
merits rather than because they were proven to be the cause:

- The keyboard-flag browser test fired ArrowRight and `f` back to back,
  assuming the first had finished — and focus() does a scrollIntoView, so under
  load `f` could arrive with no cursor and flag nothing. It now waits for the
  cursor to land.
- BOTH browser fixtures did bind -> getsockname -> CLOSE -> hand uvicorn the
  port NUMBER, leaving a window for the kernel to give that port to somebody
  else. This suite runs two browser files that each start a server per test, so
  the competitor is right there. The bound socket is now handed over directly.

⚠ THE FLAKE IS NOT PROVEN FIXED. Two different browser tests failed once each
across full-suite runs while passing 3/3 and 5/5 in isolation; since the fixes,
one failure in three runs. n=3 cannot distinguish that from the prior rate and
this commit does not claim it does.

770 green on a clean run.
This commit is contained in:
vh
2026-09-23 17:48:30 -07:00
parent cecd877f60
commit 091f4b5f2d
5 changed files with 179 additions and 5 deletions
+24 -3
View File
@@ -50,12 +50,25 @@ def live(tmp_path):
sock = socket.socket()
sock.bind(("127.0.0.1", 0))
port = sock.getsockname()[1]
sock.close()
# ⚠ THE SOCKET IS HANDED TO UVICORN STILL BOUND, never closed and
# re-opened by port number. The old form did bind -> getsockname -> CLOSE ->
# tell uvicorn the number, which leaves a window where the kernel can give
# that port to somebody else — and this suite runs TWO browser files that
# each start a server per test, so the other one is right there competing
# for it. Passing the live socket removes the window rather than narrowing
# it.
#
# Honest about the evidence: two different browser tests failed once each
# across full-suite runs while passing 3/3 and 5/5 on their own, which is
# the signature of contention. We cannot prove from two samples that this
# race was the cause. It is a real defect either way, and it is the only
# one visible in the harness.
app = create_app(tmp_path, ttl_hours=24, start_sweeper=False)
config = uvicorn.Config(app, host="127.0.0.1", port=port, log_level="error")
server = uvicorn.Server(config)
thread = threading.Thread(target=server.run, daemon=True)
thread = threading.Thread(target=lambda: server.run(sockets=[sock]), daemon=True)
thread.start()
deadline = time.time() + 10
while not server.started and time.time() < deadline:
@@ -608,7 +621,15 @@ def test_the_keyboard_flag_actually_submits(browser, live):
page.goto(f"{base}/b/g/", wait_until="networkidle")
page.evaluate("window.__noReload = 1")
page.keyboard.press("ArrowRight") # cursor onto the first tile
page.keyboard.press("ArrowRight")
# ⚠ WAIT FOR THE CURSOR TO LAND BEFORE PRESSING `f`. Firing both keys
# back to back assumed the first had finished, and `focus()` does a
# `scrollIntoView` — so under full-suite load `f` could arrive with no
# cursor set and flag nothing. It failed once in roughly five whole-suite
# runs while passing 3/3 on its own, which is the signature of a race
# rather than a defect, and a test that goes red one time in five trains
# people to ignore red.
page.wait_for_selector("figure.item.is-cursor", timeout=10000)
page.keyboard.press("f")
page.wait_for_selector("figure.item.is-flagged", timeout=10000)
flagged = page.locator("figure.item.is-flagged").count()