Files
website/server/src/config/version.js
wtclaude 1667e636bd 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
2026-09-08 06:18:38 -05:00

43 lines
2.3 KiB
JavaScript

// ── Public API version / identity ──────────────────────────────────────────
//
// A tiny, dependency-free descriptor of this backend, surfaced on GET
// /public/status and GET /public/version. A client (notably the Android app)
// uses it to:
// • positively recognize a Runic Gateway backend on first-run (the `service`
// tag), instead of guessing from an incidental response shape; and
// • run a version-mismatch guard — compare `api` against the contract the
// client was built against and surface a clear "update required" state
// rather than mis-parsing a future, changed response.
//
// `api` is the coarse contract version (bumped only on a breaking re-shape, which
// would be a v2 mount); `server` is the informational package version.
//
// ── `capabilities` (Phase 14a) ──
//
// Opaque strings naming what CORE serves beyond the surface every backend has —
// the same idea as a module's `capabilities` on GET /public/modules, and
// deliberately the same word, so a client feature-detects one way rather than
// two. They are a different LIST because core is not a module: publishing core
// as a pseudo-module would leave a client unable to tell "this backend has
// events" from "a module called core happens to be installed", which is exactly
// the distinction the loader exists to make.
//
// The value is in what is ABSENT. A backend released before Events answers this
// object with no `capabilities` key at all, so a client can tell an older site
// from one that simply has nothing on its calendar — which it could not do by
// probing /public/events, where "not built" and "temporarily down" look alike.
//
// Static, because these are compiled-in features rather than installed ones:
// a core that has these routes always has them. An unknown string is to be
// treated as absent, exactly as MODULE_API.md §2.1 says of a module's.
const pkg = require('../../package.json')
module.exports = {
service: 'runic-gateway', // stable backend identifier for first-run detection
api: 'v1', // API contract version (matches the /api/v1 mount)
server: pkg.version || '0.0.0', // server package version (informational)
// What core serves beyond the baseline. See the note above.
capabilities: ['events'],
}