37 Commits

Author SHA1 Message Date
Matt Isenhower
0e93b3d427 Fix rotating image caching and document direct R2 serving
Some checks failed
Checks / checks (push) Has been cancelled
2026-09-15 21:04:06 -07:00
Matt Isenhower
ac53b7e674 Show seconds in admin timestamps 2026-09-15 21:03:42 -07:00
Matt Isenhower
cf7d28028b Document production cutover and automatic deployments 2026-09-13 13:17:54 -07:00
Matt Isenhower
84cb8bc0cc Compact admin activity rows and identify staging 2026-09-13 12:56:40 -07:00
Matt Isenhower
ef24b6183a Retain a combined history of the latest 50 updater runs 2026-09-13 12:54:30 -07:00
Matt Isenhower
d92eaec75c Simplify admin controls and style scheduling switch 2026-09-13 12:49:59 -07:00
Matt Isenhower
5ef471c3e1 Document development updater autodeployment 2026-09-13 12:43:12 -07:00
Matt Isenhower
b37de03fe7 Separate development and production updater environments 2026-09-13 10:06:31 -07:00
Matt Isenhower
6555ff709b Add persistent automatic scheduling toggle to admin panel 2026-09-13 10:06:31 -07:00
Matt Isenhower
5deac8c8cb Use Browser binding for screenshots and local Wrangler testing 2026-09-07 18:06:15 -07:00
Matt Isenhower
a1e934d0bd Improve readability of test setup and assertions 2026-09-07 17:52:50 -07:00
Matt Isenhower
9971afe2e8 Improve spacing and visual grouping throughout runtime code 2026-09-07 17:52:36 -07:00
Matt Isenhower
52378c8b01 Document local and Cloudflare screenshot workflows 2026-09-07 17:44:14 -07:00
Matt Isenhower
bee6bda585 Support explicit Puppeteer and Cloudflare screenshot providers 2026-09-07 17:44:11 -07:00
Matt Isenhower
706b7ee0e8 Make run log capture an explicit class 2026-09-07 17:22:40 -07:00
Matt Isenhower
09caa34d7d Clarify Node runner names 2026-09-07 17:12:28 -07:00
Matt Isenhower
74cd311151 Serve admin panel at protected dev custom domain 2026-09-07 14:05:40 -07:00
Matt Isenhower
bfc35b9f99 Make admin panel source easier to read 2026-09-07 13:48:08 -07:00
Matt Isenhower
6c44b548b6 Simplify run helpers and clarify scheduler control flow 2026-09-07 13:48:06 -07:00
Matt Isenhower
41ac871fda Remove decorative introduction from the admin panel 2026-09-07 13:42:40 -07:00
Matt Isenhower
f9154a1dfb Show live and retained run logs in the admin panel 2026-09-07 13:42:25 -07:00
Matt Isenhower
ec797c4e13 Add an Access-protected admin panel and local interactive preview 2026-09-07 13:37:19 -07:00
Matt Isenhower
01d2b87335 Persist manual data and social runs for background execution 2026-09-07 13:37:19 -07:00
Matt Isenhower
8aa90337fd Keep environment variants and account configuration private 2026-09-07 13:22:14 -07:00
Matt Isenhower
a87855644d Convert social screenshots to JPEG without a second browser capture 2026-09-07 13:19:31 -07:00
Matt Isenhower
50156e96e1 Retry transient Browser Run screenshot failures with bounded backoff 2026-09-07 13:09:31 -07:00
Matt Isenhower
bc377f3cd6 Document social operation, shadow validation, and safe Worker cutover 2026-09-07 13:01:04 -07:00
Matt Isenhower
31e8f26db1 Check rendering site data against the published bucket before posting 2026-09-07 13:00:38 -07:00
Matt Isenhower
1b430d172a Simplify hourly scheduling and serialize all updater runs 2026-09-07 13:00:14 -07:00
Matt Isenhower
8a38942e3d Report social posting failures and retain successful post checkpoints 2026-09-07 12:57:27 -07:00
Matt Isenhower
235455bfea Remove X posting and rename the bot commands and modules to social 2026-09-07 12:56:53 -07:00
Matt Isenhower
9258447df7 Run the social posters in the Worker after the updaters
The Scheduler's hourly alarm now runs two jobs in order, updaters then
posters, matching the container; a failure in either schedules the
retry. POST /post runs the posters by hand and wake?job=posters through
the alarm. The posters read process.env, which Workers populate from the
bindings: SITE_URL and CLOUDFLARE_ACCOUNT_ID are vars, the Browser
Rendering token and the social credentials are secrets. Without social
credentials the posters only render and save the public images, which is
the shadow mode until cutover.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:06:48 -07:00
Matt Isenhower
abb11e972d Revert "Return a run summary from each updater"
This reverts commit 49ee02731c.
2026-09-06 18:04:42 -07:00
Matt Isenhower
49ee02731c Return a run summary from each updater
Updater.update() now returns what happened: per-phase timings (fetch,
localize, process, publish, images), which languages had to be fetched,
which locale documents changed, and how many images were downloaded.
Subclasses add their own details (stages seeded and new stages, festival
rankings fetched). updateAll() carries each summary in its results, so the
Worker's "Updaters finished" log entry contains the whole picture per
updater. FilesystemStorage.writeJson now skips unchanged content like
BucketStorage does, so "locale documents written" means the same locally.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 18:04:55 -07:00
Matt Isenhower
e5c8032d06 Add a list endpoint to the updater Worker
GET /list?prefix= returns the keys under a prefix in the public bucket.
Wrangler can fetch, put, and delete single objects but not list them,
and this is the only way to inspect the shadow bucket from a terminal.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 11:59:31 -07:00
Matt Isenhower
960076faae Embed the CDN-missing image so an empty bucket seeds itself
One skill image is no longer served by SplatNet's CDN. The updater's
image step falls back to a bundled copy: under Node, cdn.node.js reads
the PNG from the repo; in the Worker, cdn.worker.js imports it as bytes
through a wrangler "Data" module rule. A "#cdn-images" import condition
in package.json selects the right one, so the PNG in src/common/cdn stays
the only artifact and nothing is generated or copied by hand.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 11:42:49 -07:00
Matt Isenhower
188d9cb504 Run the updaters in a Cloudflare Worker
workers/updater runs src/app/updater, the same code npm run splatnet runs
locally, against the Worker's R2 bindings through BucketStorage. The
Scheduler Durable Object from the proof of concept owns when the run
happens: an alarm at :00:10 that re-arms itself, with retries after a
failed run; the only cron trigger is a minute-30 watchdog that re-arms a
lost alarm. Operator endpoints (/run, /wake, /arm, /status) are behind a
bearer token. Handlers and the Durable Object are wrapped with
@sentry/cloudflare so the updaters' @sentry/core reports are delivered
when SENTRY_DSN is set.

updateAll() now returns a per-updater result (name, ok, duration, error)
that both the local runner and the Worker can report on, and the SplatNet
client reads its user agent lazily since Workers populate process.env
from the bindings.

Deployed in shadow mode against the dev bucket: a full run of all eight
updaters on Cloudflare matched production's data semantically within the
same hour, and steady-state runs take about seven seconds.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 11:23:49 -07:00