fix(events): the public calendar, a stranded revert, and three dropped facts (Phase 16a) #198
Reference in New Issue
Block a user
No description provided.
Delete Branch "fix/events-p16a-walk"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Three defects the Phase 16 acceptance walk found in code already merged to
edge. The suite was green on either side of all three.1. The public calendar showed neither what is live nor what is recent
EVENTS.md§I:GET /public/eventsis "the calendar: upcoming, live and recent". Built, it was upcoming only.eventRuns.db.listInWindowfiltered onscheduled_for >= from AND < to, and the shipped page (client/src/routes/public/Events.jsx:28) callsapi.publicEvents()with no window at all, so it took the server default of now → +31d. A run that began five minutes ago and has three hours left hasscheduled_for < nowand was absent; so was one that ended an hour ago.The site contradicted itself:
{"live": true, "current": {...}}on/site/events/<slug>while/site/eventsservedentries: [].A run is an interval, not an instant.
listInWindownow matches a run whose occupied interval overlaps the window. That also closes the admin calendar's identical hole — a run that started last Sunday and is still going was missing from "this week" — which is why the fix is in the shared statement rather than in the public model.The public default now reaches
DEFAULT_RECENT_DAYS(7) back, so "recent" has somewhere to live. The 92-day span cap and the entry cap are untouched, so the anonymous-surface defence is unchanged. Projections are still forecast fromnow, never into the tail: a slot the runner has already passed did not happen, and a run that WAS created for it arrives as a real row anyway.2. A resource left
revertingby a crash was never reclaimedclaimRevert's own comment saidrevertingis not claimable "exactly as a step with a live claim is". The analogy was half-built: a step's claim carriesclaim_expires_atand is reclaimed when the lease lapses — §N4 names that reclaim as the reason the CAS survives a single instance — while a resource inrevertinghad no expiry and nothing released it.Observed: the process was killed at 07:45:37.487 mid-teardown, with a lease already flipped to
reverting. After restart the row sat atrevert_attempts=0, last_error=NULL, untouched, whileevent cleanup swept {"runs":1}ran every 15s for ever finding nothing it could claim.And the documented recourse was a silent no-op. §I says
POST …/cleanup"is the only thing that clears the counter". It answered 200 and changed nothing — it claims through the same function, and there was no counter to clear because the row was never counted as failed.It is not only a wrong record: the stranded lease blocked the next run of the same event, which failed with "Seasonal event status is already leased by another run (run 2)" until the shard's own deadline lapsed.
The stale test is
updated_at, not a new column — for arevertingrow that IS when the claim was taken, and it is not written again until the revert resolves.updated_atis re-stamped explicitly, and that is load-bearing rather than tidy: this connector sendsCLIENT_FOUND_ROWS, soaffectedRowscounts rows matched; without the write a second claimer would still match the row and two passes would revert the same resource.revert_attemptsis deliberately untouched — a stale claim is a process that died, not an attempt that failed.3. Three facts every event announcement computes and none could use
events/announce.jsbaseFor()putssummary,seriesNameandtimezoneon all sevenevent.*payloads. Four triggers declared none of them and a fifth declared one, soengagementEmit.validatePayloaddropped them: absent from the variable list an author picks from, unusable in a template, and loggingemit carried undeclared variablesat DEBUG on every event emit.They are now one shared
EVENT_AMBIENTdeclaration spread into all seven, with the per-trigger copies removed so the seven cannot drift.timezonein particular is the one an event mail wants.engagement-triggers.jsonregenerated.Verification
Against a real rig — ServUO 57.4 → sidecar on protocol 7 → this core with
module-uoinstalled from a release-shaped bundle → the app on an emulator:/site/eventsnow shows a live run as "Happening now" beside recent finished ones, in the browser and in the app (the app consumes the same route, so D1 was breaking its calendar too). It showed nothing at all before.cleanup_statuswentpending → complete.The
claimReverttests live ineventRunnerSql.test.jsrather than against a stub, because every part of the answer is the server's —NOW() - INTERVAL, whetherON UPDATEre-stamps, and above all whataffectedRowscounts. A stub counting changed rows would call the reclaim a failure; one counting matched rows would miss that the second claimer needs the re-stamp in order to lose.Pairs with
Module-uo#33,servuo-plugins#anddocs#(Phase 16a).🤖 Generated with Claude Code
https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4