docs(events): Phase 4 as built - schedule, recurrence and the calendar
EVENTS.md: the materialise leg as two halves and why the horizon is what makes the missed sweep meaningful; the recurrence shapes as built, incl. the two DST rules and why nth has no fifth; publishing as the schedule switch and the re-pin; the calendar's run-vs-projection distinction and the reader's-zone/event's-zone split; the four new routes; P1-P4 ticked. EVENTS_PLAN.md: Phase 4 marked complete with the five org-lead decisions, the three the build settled, the test-harness defect the phase introduced and fixed, and the verified numbers. Website: RunicGateway/website#PENDING Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -233,7 +233,7 @@ the same rule.
|
||||
> the run enters it. The instant is now carried across the boundary. Found by writing the test, and
|
||||
> the test was re-run against the unfixed code to confirm it fails.
|
||||
>
|
||||
> **What "materialise" means here.** The spec validator accepts `kind: 'manual'` alone until Phase 4,
|
||||
> **What "materialise" meant in THIS phase.** The spec validator accepted `kind: 'manual'` alone,
|
||||
> so there is no recurrence to expand — this leg builds the half that is already real, the grace
|
||||
> window, and Phase 4 adds the expansion above it.
|
||||
>
|
||||
@@ -386,11 +386,56 @@ before building further.
|
||||
|
||||
### Phase 4 — Schedule, recurrence and the calendar (`website` + `docs`)
|
||||
|
||||
The closed recurrence shapes — `once`, `weekly`, `monthly` (nth weekday), `manual` — computed in the
|
||||
definition's **IANA timezone** and stored as UTC in `scheduled_for`. `event_series` becomes usable: a
|
||||
definition may belong to a series, and the series has an ordering.
|
||||
|
||||
An admin calendar view (month + list), filtered by state, scope and series.
|
||||
> **Complete.** `edge` in `website` and `docs`. The closed recurrence shapes — `once`, `weekly`,
|
||||
> `monthly` (nth weekday), `manual` — computed in the definition's **IANA timezone** and stored as UTC
|
||||
> in `scheduled_for`; `event_series` is usable and managed inline on the calendar; an admin calendar
|
||||
> (month + list) filtered by state, scope and series. **An event now happens on its own.** No schema
|
||||
> change: Phase 1 built every column this needed.
|
||||
>
|
||||
> **The decisions the org lead settled (2026-09-02), all as recommended:**
|
||||
>
|
||||
> - **A fourteen-day materialisation horizon, with projections beyond it.** Inside it an occurrence is
|
||||
> a real row an operator can see, cancel and reschedule one at a time; beyond it the calendar
|
||||
> forecasts from the same arithmetic, so a monthly event is still visible three weeks out. Drawn
|
||||
> differently on purpose — acting on a forecast as though it were a booking would be the UI's fault.
|
||||
> - **Automatic expansion is at the EMPTY scope.** A fan-out across named scopes needs a registry of
|
||||
> what a scope *is*, which no phase owns yet; inventing one here would be a contract the modules were
|
||||
> never asked about. The admin's own start route still takes any scope.
|
||||
> - **DST: skip-forward, take-first.** A local time the spring gap swallows moves forward to the first
|
||||
> one that exists (02:30 becomes 03:00, not 03:30); an hour that happens twice takes the first. Both
|
||||
> are recorded as `detail.dstAdjusted`. **Neither rule ever drops an occurrence.**
|
||||
> - **`nth` is 1..4 plus -1 for "last".** There is no fifth, so there is no absent-occurrence case to
|
||||
> define — every month has a first through fourth of every weekday. `-1` is not a synonym for `4`.
|
||||
> - **Publishing re-pins the occurrences that have not started.** Asked mid-build, once the horizon
|
||||
> made it real: on the day an editor fixes a typo there are already fourteen days of rows carrying
|
||||
> the old spec. A pin makes a run that **has run** reproducible; one that has not begun has nothing
|
||||
> to reproduce. The alternative was worse than doing nothing — cancelling a stale occurrence leaves
|
||||
> its slot held in `uq_evrun_occurrence`, so it would not come back on the new version, it would
|
||||
> vanish.
|
||||
>
|
||||
> **Three things the build settled:**
|
||||
>
|
||||
> - **`now - grace` is the window start, not `now`.** An occurrence nobody ever materialised is never
|
||||
> invented retroactively — three days down must not manufacture three days of `missed` history no
|
||||
> operator could have seen. It does not need to: rows exist a fortnight early, so a real outage finds
|
||||
> them already there. **The horizon is what makes the missed sweep mean anything for a recurrence.**
|
||||
> - **Publishing is the schedule switch; archiving turns it off.** `ready` already means "a version has
|
||||
> been published and the schedule is live", so a second enabled flag would be another answer to a
|
||||
> question `state` answers. Expansion reads the **published version's** spec, never the working copy.
|
||||
> - **Series writes are `admin, editor`.** Naming an arc is authoring; §N2's narrow gate is about
|
||||
> committing the deployment to a run.
|
||||
>
|
||||
> **A defect this phase introduced into the test harness, and fixed.** Putting the expansion leg in
|
||||
> front of `tick()` made `eventRunner.test.js` reach the dead-port pool on every tick — the file passed
|
||||
> and took minutes. Stubbing `findSchedulable` there returned it to 0.45s. Worth naming because a suite
|
||||
> that is merely *slow* reads as a suite that is fine.
|
||||
>
|
||||
> **Verified:** `npm test` — **1768 tests, 1711 pass, 56 skipped, 1 fail**, that one the pre-existing
|
||||
> `engagementManifest.test.js` CRLF failure (`edge` before this branch: 1714/1662/51/1). **54 new
|
||||
> tests**, of which `eventRecurrence.test.js` (17) is the DST fixture set this plan asked for, and 5 in
|
||||
> `eventRunnerSql.test.js` prove `findSchedulable`, `listInWindow` and `repinScheduled` **against a
|
||||
> real MariaDB** (32/32 with a database, skipped without). Client: 354 pass. Four routes added, none
|
||||
> moved; `check:modules` and `check:hosts` clean; the client builds.
|
||||
|
||||
**Ships:** recurring and scheduled events, and the calendar that replaces the thing this feature
|
||||
exists to replace.
|
||||
@@ -400,7 +445,9 @@ all.
|
||||
|
||||
**Trap:** the temptation is a cron string. There is no cron parser in the server dependency tree, the
|
||||
only precedent is in the bot (a different process), and a cron expression is the one field an operator
|
||||
cannot proofread. Closed shapes render as a form.
|
||||
cannot proofread. Closed shapes render as a form. *Held: `events/recurrence.js` inverts
|
||||
`Intl.DateTimeFormat` rather than adding a date library, since Node already ships the tzdata one would
|
||||
vendor.*
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user