Make app schedules timezone-durable via IANA identity - #314
Conversation
Co-authored-by: Möbius Agent <mobius-agent@users.noreply.github.com>
|
I found two correctness blockers while reviewing this PR and prepared a focused follow-up directly on its current head (
The follow-up uses a supervised wall-clock gate with explicit gap/fold policy and an atomic per-app/date claim, and installs the complete durable declaration before any live crontab change. It also removes the obsolete hourly rematerializer. Reviewed commit: https://github.com/hamzamerzic/mobius-1/commit/f31100d58f17f1e80e0346c7671c7ee67cfda72b Verification: 231 relevant tests passed (3 skipped), 46 focused regressions passed, Python compilation, shell syntax, and diff checks passed. The tradeoff is one lightweight gate process per timezone-owned app per minute; 1,439 ordinary non-due ticks exit before token minting or job authority. |
|
Superseded by #339, which has now merged. The replacement preserves durable IANA timezone identity while fixing the unsafe partial-application order, evaluating daylight-saving transitions from the zone rules rather than a captured offset, failing invalid declarations closed, and claiming at most one run per local civil date. Closing this original version rather than merging its known failure paths. |
Follow-up to #290, redesigned per its review: the offset-snapshot fields (
tz_name/tz_offset_minutes) are gone. The durable schedule identity is now an IANA timezone plus a zone-local daily cron, and the scheduler owns it end to end.Design
POST /api/apps/{id}/scheduleaccepts an optionaltimezone(IANA). The identity is declared asSCHEDULE_TZ/SCHEDULE_SOURCElines in the app'sinit-cron.sh(parsed, never executed — same contract asENTRY), written by the platform after the scaffold runs; the scaffold's own interface is unchanged.app/cron_tz.py), recomputed by the existing boot reconciliation and by an hourly in-process pass — so a DST transition in either zone explicitly reschedules the entry instead of drifting its wall time. The periodic pass only runs while a zone declaration exists.GET /api/apps/schedulesexposes the durable identity (timezone,zone_cron) plusserver_timezone, the server clock's own IANA name (TZ env,/etc/localtime, else UTC) — no abbreviations, no offsets.m h * * *) may carry a zone: shifting across an offset can cross a day boundary, which is well-defined daily and ambiguous for date-pinned expressions. Anything else is rejected with a 400.Tests cover both 2026 European DST boundaries in both directions of ownership (zone-owned schedule on a UTC server; UTC-owned schedule on a DST-observing server), day-boundary wrap (UTC+5:45), declaration parsing incl. malformed halves, endpoint validation, and reconciliation re-materializing a stale entry from its declaration.
The companion app-memory PR feeds this from the schedule settings UI and degrades to server-local against an older backend.
🤖 Generated with Claude Code