feat(events): the public calendar, event pages and participation history (Phase 14a)
The anonymous surface an event was always for: GET /public/events, /public/events/:slug and /public/events/series/:slug, plus GET /player/events/history, and the four screens over them. Four org-lead decisions taken up front: split Phase 14 into 14a (website) and 14b (the app); add a `listed` flag rather than letting `state` mean both schedulable and announced; put the `events` capability string in the version block rather than publishing core as a pseudo-module; and drop "venue" from the spec rather than adding a field nothing had ever built. `listed` is announcement, not permission. Publishing is what makes a definition runnable, so without a separate flag a surprise event would have to be advertised in order to be allowed to happen. It is a column, a switch in Phase 13's editor, and three SQL predicates -- never a filter applied after a read, which works exactly as well until the first caller that forgets. The public shapes are a projection, and the projection is the security boundary: nothing is spread, so a column added to event_runs next year does not ride out through it. The spec, health, cleanup, claims, errors and member_key are all absent by construction. The six public event triggers gained `eventUrl` (version 1 -> 2), carrying ?run= because the page lives at the definition's slug while every trigger is about one occurrence. notify.event-started gained the button, at seedVersion 2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
"subjectKey": "runId",
|
||||
"audience": "subscribers",
|
||||
"ceiling": "authenticated",
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"variables": [
|
||||
{
|
||||
"name": "runId",
|
||||
@@ -54,6 +54,13 @@
|
||||
"required": true,
|
||||
"example": 4,
|
||||
"description": "How many phases the pinned version has in total."
|
||||
},
|
||||
{
|
||||
"name": "eventUrl",
|
||||
"type": "url",
|
||||
"required": false,
|
||||
"example": "/site/events/the-yew-invasion?run=3692",
|
||||
"description": "The public page for this occurrence."
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -66,7 +73,7 @@
|
||||
"subjectKey": "runId",
|
||||
"audience": "subscribers",
|
||||
"ceiling": "authenticated",
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"variables": [
|
||||
{
|
||||
"name": "runId",
|
||||
@@ -88,6 +95,13 @@
|
||||
"required": false,
|
||||
"example": "The shard is down for an emergency patch.",
|
||||
"description": "What the staff member gave as the reason, when they gave one."
|
||||
},
|
||||
{
|
||||
"name": "eventUrl",
|
||||
"type": "url",
|
||||
"required": false,
|
||||
"example": "/site/events/the-yew-invasion?run=3692",
|
||||
"description": "The public page for this occurrence."
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -100,7 +114,7 @@
|
||||
"subjectKey": "runId",
|
||||
"audience": "subscribers",
|
||||
"ceiling": "authenticated",
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"variables": [
|
||||
{
|
||||
"name": "runId",
|
||||
@@ -136,6 +150,13 @@
|
||||
"required": true,
|
||||
"example": 95,
|
||||
"description": "How long the run took, start to end, in whole minutes."
|
||||
},
|
||||
{
|
||||
"name": "eventUrl",
|
||||
"type": "url",
|
||||
"required": false,
|
||||
"example": "/site/events/the-yew-invasion?run=3692",
|
||||
"description": "The public page for this occurrence."
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -148,7 +169,7 @@
|
||||
"subjectKey": "runId",
|
||||
"audience": "subscribers",
|
||||
"ceiling": "authenticated",
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"variables": [
|
||||
{
|
||||
"name": "runId",
|
||||
@@ -163,6 +184,13 @@
|
||||
"required": true,
|
||||
"example": "The Yew Invasion",
|
||||
"description": "The event title."
|
||||
},
|
||||
{
|
||||
"name": "eventUrl",
|
||||
"type": "url",
|
||||
"required": false,
|
||||
"example": "/site/events/the-yew-invasion?run=3692",
|
||||
"description": "The public page for this occurrence."
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -223,7 +251,7 @@
|
||||
"subjectKey": "runId",
|
||||
"audience": "subscribers",
|
||||
"ceiling": "authenticated",
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"variables": [
|
||||
{
|
||||
"name": "runId",
|
||||
@@ -273,6 +301,13 @@
|
||||
"required": false,
|
||||
"example": "Saturday 12 September at 8:00 pm (America/New_York)",
|
||||
"description": "The start time written out in the shard-local zone, for a mail to read."
|
||||
},
|
||||
{
|
||||
"name": "eventUrl",
|
||||
"type": "url",
|
||||
"required": false,
|
||||
"example": "/site/events/the-yew-invasion?run=3692",
|
||||
"description": "The public page for this occurrence."
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -285,7 +320,7 @@
|
||||
"subjectKey": "runId",
|
||||
"audience": "subscribers",
|
||||
"ceiling": "authenticated",
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"variables": [
|
||||
{
|
||||
"name": "runId",
|
||||
@@ -335,6 +370,13 @@
|
||||
"required": false,
|
||||
"example": "Saturday 12 September at 8:00 pm (America/New_York)",
|
||||
"description": "The start time written out in the shard-local zone, for a mail to read."
|
||||
},
|
||||
{
|
||||
"name": "eventUrl",
|
||||
"type": "url",
|
||||
"required": false,
|
||||
"example": "/site/events/the-yew-invasion?run=3692",
|
||||
"description": "The public page for this occurrence."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user