Public: anyone can read this; the crew can change it.

Build scrims #2

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

The scrim board replaces the notes example: `contract.json` now declares **announcement** (`/api/announcements`, create/update/delete `admin`, read/list `anon`), **match** (`/api/matches`, create `user`, update/delete `author`, read/list `anon`) and **signup** (`/api/signups`, create `user`, read/list `host`, delete `author`, update `nobody`). One new role, `host`, held by a signup row's `host_id`…

shipped production is serving it deploy f126187

opened this

The scrim board replaces the notes example: contract.json now declares announcement (/api/announcements, create/update/delete admin, read/list anon), match (/api/matches, create user, update/delete author, read/list anon) and signup (/api/signups, create user, read/list host, delete author, update nobody). One new role, host, held by a signup row's host_id — the match's poster, copied onto the row when someone joins, since the poster of a match never changes. author sits above host so the person who joined can read their own signup; admin sits above both. The notes resource and its table are gone (a new migration drops note; migrations 1 and 2 stay as history, and an upgrade from the scaffold's version 2 was tried end to end). No cell that existed before was loosened — the only doors that changed are the ones the issue asks for.

Every door asks permit: src/actions.ts holds the fifteen actions, and the page, the JSON API, the ten MCP tools and the terminal all call them. src/contract.test.ts acts as each identity at all 45 cells, on a row that names them and one that does not. src/time.ts is new — a match time is read as UTC (bare, with Z, or with an offset that gets converted), stored as YYYY-MM-DDTHH:MM:SSZ so string order is time order, and formatted by hand so no locale or server timezone gets a say; src/time.test.ts checks the conversions against hand-worked values. Slots left is slots − signups, floored at zero, and lives on the match, which anyone may read; the names live on the signups, which they may not. bun test is 34 tests, green, and the first paint measures under 2 ms locally. Still zero dependencies.

Worth looking at in the preview: sign in and put up a match, then open the page as a guest in another window — the guest sees <b>4</b> of 5 slots left (1 signed up) and never a name, while the poster and an admin see the roster, and both update live over /events (the event carries the id and the count only). The terminal (FORGE_APP_SSH, printed verbatim in the footer) shows the same board with s/l to join and leave.

What I left out: a host cannot kick someone off their own match, and a past match can still be joined — neither is in the issue, and both are a cell or a rule away. Times are UTC everywhere with no per-person timezone; that assumption is stated on the page, in the README and in the form labels.

🤖 Generated with Claude Code

Closes #1

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs