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:
@@ -171,6 +171,7 @@ def set_blurred(booth: Path, rel: str, on: bool) -> set[str]:
|
||||
# can use it without pulling FastAPI in. Re-exported here because call sites and
|
||||
# tests already reference these names through app.
|
||||
from booth.thumbs import THUMB_DIR, ensure_thumb
|
||||
from booth.birthtime import birth_time
|
||||
from booth.asks import ( # noqa: E402
|
||||
ANSWER_SUFFIX,
|
||||
ASK_SUFFIX,
|
||||
@@ -706,6 +707,16 @@ def list_booths(data_dir: Path, ttl_seconds: float, now: float | None = None) ->
|
||||
# `landed_at` is content. "New since you looked" reads only the
|
||||
# second, so a flag or a view never makes a booth look new.
|
||||
"landed_at": _content_mtime(child),
|
||||
# WHEN THE BOOTH WAS MADE, from the filesystem's own record.
|
||||
# ONE RULE for all thirty: `.booth.json`'s declared `created`
|
||||
# only exists for booths posted through the CLI since U5, and
|
||||
# twelve 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, and
|
||||
# directory mtime just means "last thing added". ext4 records a
|
||||
# real birth time; this reads it. None when the filesystem
|
||||
# cannot say, and None renders as nothing.
|
||||
"created_at": birth_time(child),
|
||||
"viewed_at": _viewed_at(child),
|
||||
# The first four images in item order, as the originals shown
|
||||
# small. Blurred ones stay blurred, the cover's rule.
|
||||
@@ -1263,6 +1274,10 @@ def create_app(
|
||||
# a booth URL handed to the operator lands HERE, never on the
|
||||
# index, and job 5 is "operator, look at this".
|
||||
"manifest": read_manifest(booth),
|
||||
# Same two clocks the Desk row carries, because a booth URL
|
||||
# handed to the operator lands HERE and not on the index.
|
||||
"created_at": birth_time(booth),
|
||||
"landed_at": _content_mtime(booth),
|
||||
# The lifetime line, same three states as the index card: a
|
||||
# booth URL handed to the operator lands HERE, not on the index,
|
||||
# so "why is this not counting down" has to be answerable here.
|
||||
|
||||
Reference in New Issue
Block a user