docs(website): the public surface, and the flag that decides what reaches it (Phase 14a)

EVENTS.md gains what the phase settled -- a new §I section on the public
surface, the four API surface rows filled in, `listed` in §D, and the §J
rows for player profiles and mobile. EVENTS_PLAN.md records the 14a/14b
split and 14a as built. BACKEND_DESIGN.md points at the two tiers the
Phase 14a reads live on. MODULE_API.md records core's own capability list
beside a module's -- the same word, a separate list, and why.

Two things this phase corrected in the document rather than in code.

"Venue" was never a field. §I's screens table and the API surface table had
both described one since the first revision; there has never been a column,
a spec key, an input on Phase 13's form, or a string anywhere in either
repo. Rather than add a field on the way past to a public page, both
descriptions dropped it.

And the six public triggers' missing url variable, which this document has
carried as a promise since Phase 10, is now kept: `eventUrl`, carrying
`?run=`, arriving with the page it points at.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
This commit is contained in:
2026-09-08 06:18:55 -05:00
parent c17893a215
commit 9aee5920af
4 changed files with 172 additions and 27 deletions

View File

@@ -1165,6 +1165,7 @@ push; the ceiling on `run.failed` proved to exclude a moderator.
> the template editor previewed a link that was dead in every mail it sent. Phase 14 adds the
> variable alongside the page it points at, which is a version bump. `event.run.failed` is the
> exception because `/admin/events/runs/:runId` exists today.
> **Done in 14a**, as `eventUrl`, carrying `?run=` — and the six went to version 2.
> - **`startsAtLabel` is a presentational fragment computed at the emitter**, which is
> `ENGAGEMENT.md` §4.6.1 convention 1 rather than a shortcut. `startsAt` is a `datetime` and the
> seam normalises it to an ISO string — right as data, unreadable in a sentence — and a template has
@@ -1699,19 +1700,51 @@ at any point.
---
### Phase 14 — Public, player and mobile (`website` + `android-app` + `docs`)
### Phase 14 — Public, player and mobile — SPLIT into 14a and 14b
Split by the org lead on 2026-09-07, on the repo line: the app cannot be walked against a page that
does not exist yet, so 14b's emulator walk is only meaningful once 14a has merged. The same argument
12a/12b was split on.
#### Phase 14a — the public and player surface (`website` + `docs`) — **built**
`GET /public/events`, `/public/events/:slug`, `/public/events/series/:slug`,
`/player/events/history`, and an `events` capability string.
`/player/events/history`, and an `events` capability string. `website#196`, `docs#224`.
**One prerequisite fix, in this phase and not after it.** The Android app hardcodes
`api/v1/public/shard/stream` — a *module* path — and reads `/public/modules` nowhere, so it cannot
render a site whose module it has never heard of. That is a small fix and it blocks the app seeing
events at all.
Four org-lead decisions, all taken up front: split the phase; add a **`listed`** flag rather than
letting `state` mean both schedulable and announced; put the capability string in the **`version`
block** rather than publishing core as a pseudo-module; and **drop "venue"** from this document
rather than adding the field §I had described since the first revision and nothing had ever built.
**Ships:** the public calendar, event pages with their storyline and results, participation history,
and the app.
**Verify:** the client and app suites; an emulator walk.
`listed` is the one that changes the shape of the work. 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 P13'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 six public `event.` triggers gained **`eventUrl`** here, which is the version bump this plan
promised (1 → 2). It carries `?run=`, because the page lives at the definition's slug and every one
of those triggers is about one occurrence. `notify.event-started` gained the button to go with it,
at `seedVersion` 2.
**Ships:** the public calendar, event pages with their storyline and results, and participation
history.
**Verify:** both suites; a browser walk of the calendar, an event page, an arc and the history.
#### Phase 14b — the app (`android-app` + `docs`)
The app's events screens, and **one prerequisite fix that is in this phase and not after it.** The
Android app hardcodes `api/v1/public/shard/stream` — a *module* path — reads `/public/modules`
nowhere, and `ShardFeaturesRepository` maps a failed `/public/shard/features` lookup to "unknown",
which `canSee` treats as visible. On a site with no `uo` module installed that route 404s, so every
shard entry in the drawer renders and every one of them 404s. Failing open is right *within* a module
that exists and wrong for one that is not installed; the fix gates the group on a **capability
string** — `shard`, which module-uo declares — rather than on a module id, and gates the new events
entry on core's `events`.
`docs/android/PLAN.md` gains the milestone before the code is written.
**Ships:** the calendar, event pages and participation history in the app.
**Verify:** the app suite; an emulator walk.
---