The four closed recurrence shapes computed in the definition's own IANA zone,
a fourteen-day materialisation horizon with projections beyond it, series as a
managed thing, and the admin calendar that replaces the plugin this feature
exists to replace. An event now happens on its own.
No schema change: Phase 1 built every column this needed.
- events/recurrence.js is the ONE place an occurrence is computed, so the
runner's expansion and the calendar's forecast cannot disagree. No date
library added — Node ships the tzdata one would vendor, behind Intl.
- The runner's materialise leg is now two halves: expand, then sweep. The
window starts at `now - grace`, so an occurrence nobody could have seen is
never invented retroactively; the horizon is what makes the missed sweep
mean anything for a recurrence.
- Publishing is the schedule switch and archiving turns it off, and publishing
re-pins every occurrence that has not started.
- A projection is never drawn over an instant a run occupies, so a cancelled
occurrence does not reappear as a forecast.
54 new tests, incl. the DST fixture set the plan asked for and three new
statements proved against a real MariaDB. Suite 1768/1711/56 skipped/1 fail
(pre-existing CRLF). Walked end to end on the local review stack.
Docs: RunicGateway/docs#PENDING
Co-Authored-By: Claude <noreply@anthropic.com>
Three screens, an Events nav group and the six live run controls Phase 1 left
absent on purpose because nothing was in flight. An admin can now author,
publish, start and watch an event that announces things and cues a human; a
moderator can stop one that is going wrong.
Six controls, not eight. `advance` is absent because a phase today advances when
its steps go terminal — the per-step skip already does that — and Phase 5 is what
gives a phase an advance condition. Cancel takes `{ reason }`, not `{ cleanup }`,
until Phase 8's ledger exists. Every control is a compare-and-set on the status it
may act from, so a console rendered thirty seconds ago cannot act on a run that
has moved.
Fixes a defect in the Phase 2 runner: `advanceRun` drained up to
EVENT_STEPS_PER_TICK steps while only checking the run's status at the top of the
tick, so a pause pressed mid-batch did nothing for up to 24 more steps.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T6t8mrAWhZU5vnyYgZTMtL