Found by the live walk, signed in as an admin: /account/events redirected to
the dashboard. `GET /player/events/history` is behind requireAuth alone and
self-scoped on req.user.id -- staff are a superset of players -- but the WEB
has two logged-in shells, and RequirePlayer sends anyone who is not a
`player` out of /account. A single mount there is a screen the reviewing
admin can never open.
Engagement Phase 7 hit this exact wall with the inbox and answered it with
two routes, one pair of components and one mapping. `eventHistoryPath` joins
`inboxPath` and `notificationSettingsPath` in notificationPaths.js rather
than starting a second file with the same comment at the top of it. The
staff path is /admin/events/mine, in the Events section of the sidebar, and
it is the one row in that group with no `roles`.
Also: the eventAnnounce fixture carried no slug, state or `listed`, so
`eventUrl` answered undefined in every test in that file and the new code
was exercised by none of them. The fixture now looks like a definition row,
and three tests cover the link, the unlisted case and the draft case.
The run.failed assertion that came with them was reading the wrong layer:
`baseFor` assembles eventUrl for every trigger and the SEAM drops the keys a
trigger does not declare, so the declaration test is what proves it. Removed,
with a note saying where the rule actually lives.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
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