clone

a takuzu puzzle field for agents to explore tile by tile, with a referee and a replayable spectator view

daily: nightly moves to a scheduled machine; the app machine serves and keeps only the emergency fallback

dami committed 0cff066
push-day --tomorrow runs headless in the image (token from the
environment); the app's on-box prepare is off (MOSAIC_DAILY_PREPARE=0)
so it never contends with play; rollover prefers the pushed field, then
a prepared one, then the emergency field; /status reports whether
tomorrow's field is pushed.
commit
0cff06655bcb6c24a236b4c39bd439a521e9d120
parent
0ed7d0da406769e3683638cb4b0b0534fa817893
author
dami <gazey55@gmail.com>
5 files changed, +47 -4
Mfly.toml3+++
Msite/lib/daily.ts34+++++++++++++++++++++++++++---
Msite/routes/status.ts1+
Msite/scripts/push-day.ts12++++++++++-
Msite/tests/daily_test.ts1+
M fly.toml +3 -0
M site/lib/daily.ts +31 -3
@@ -45,6 +45,10 @@ export type DailyConfig = {
4545 fog: boolean;4646 /** run the engine on this box (fields can also be pushed) */4747 generate: boolean;48+ /** run the nightly prepare on this box; off when a scheduled machine49+ * pushes tomorrow's field (the app machine then only serves, and50+ * generates nothing but an emergency field) */51+ prepare: boolean;4852 /** the nightly prepare of tomorrow's field starts at this UTC hour and4953 * must finish by rollover (its deadline); jobs = chunk processes at once */5054 prepare_hour_utc: number;
@@ -72,6 +76,7 @@ export function configFromEnv(): DailyConfig {
7276 rollover_hour_utc: Number(Deno.env.get("MOSAIC_DAILY_ROLLOVER_UTC") ?? 0),7377 fog: (Deno.env.get("MOSAIC_DAILY_FOG") ?? "1") !== "0",7478 generate: Deno.env.get("MOSAIC_GENERATE") === "1",79+ prepare: (Deno.env.get("MOSAIC_DAILY_PREPARE") ?? "1") !== "0",7580 prepare_hour_utc: Number(Deno.env.get("MOSAIC_DAILY_PREPARE_UTC") ?? 20),7681 jobs: num("MOSAIC_DAILY_JOBS", 2),7782 band: num("MOSAIC_DAILY_BAND", 24),
@@ -289,6 +294,8 @@ export type DailyStatus = {
289294 since_s: number | null; // seconds spent generating so far290295 emergency: boolean;291296 latest_archived: { date: string; game_id: string } | null;297+ /** tomorrow's field: pushed to this box, or prepared in its cache */298+ tomorrow: { date: string; pushed: boolean; prepared: boolean };292299 };293300 294301 export type DailyState = {
@@ -386,6 +393,10 @@ export class Daily {
386393 date: string,387394 seed: string,388395 ): Promise<{ out: FieldOutput; source: DailyState["source"] } | null> {396+ // a pushed field wins: it was made by a machine that could afford397+ // the whole thing; the on-box prepare is the fallback for it398+ const pushed = await loadPushed(this.dir, date);399+ if (pushed) return { out: pushed, source: "pushed" };389400 const full = this.genParams(390401 date,391402 seed,
@@ -396,8 +407,6 @@ export class Daily {
396407 if (this.canGenerate && await this.isPrepared(full)) {397408 return { out: await this.generate(full), source: "prepared" };398409 }399- const pushed = await loadPushed(this.dir, date);400- if (pushed) return { out: pushed, source: "pushed" };401410 if (!this.canGenerate) return null;402411 const e = this.cfg.emergency;403412 this.log(
@@ -537,7 +546,13 @@ export class Daily {
537546 if (!this.current || !getGame(this.current.game_id)) {538547 await this.startDay(today);539548 }540- if (this.canGenerate) this.maybePrepare(today);549+ if (this.canGenerate && this.cfg.prepare) this.maybePrepare(today);550+ else if (this.lastPrepareLog !== "external") {551+ this.lastPrepareLog = "external";552+ this.log(553+ "nightly prepare is external: tomorrow's field is pushed by the nightly machine; this box generates only an emergency field",554+ );555+ }541556 }542557 543558 /** The nightly job: from the prepare hour, generate tomorrow's field
@@ -740,6 +755,14 @@ export class Daily {
740755 /** What `/` should show right now. */741756 status(): DailyStatus {742757 const cur = this.current;758+ const tomorrow = nextDate(this.today());759+ let pushed = false;760+ try {761+ Deno.statSync(pushedPath(this.dir, tomorrow));762+ pushed = true;763+ } catch {764+ // not yet765+ }743766 const live = cur && getGame(cur.game_id) ? cur.game_id : null;744767 const latest = this.index.list().at(-1) ?? null;745768 const today = this.today();
@@ -756,6 +779,11 @@ export class Daily {
756779 latest_archived: latest757780 ? { date: latest.date, game_id: latest.game_id }758781 : null,782+ tomorrow: {783+ date: tomorrow,784+ pushed,785+ prepared: this.prepared.has(tomorrow),786+ },759787 };760788 }761789
M site/routes/status.ts +1 -0
M site/scripts/push-day.ts +11 -1
M site/tests/daily_test.ts +1 -0