Public: anyone can read this; the crew can change it.
Build scrims #2
Check · Review · Land · Live done — Landed and live in production.
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
Sign in to join this conversation.
new above
offline
The scrim board replaces the notes example:
contract.jsonnow declares announcement (/api/announcements, create/update/deleteadmin, read/listanon), match (/api/matches, createuser, update/deleteauthor, read/listanon) and signup (/api/signups, createuser, read/listhost, deleteauthor, updatenobody). One new role,host, held by a signup row'shost_id— the match's poster, copied onto the row when someone joins, since the poster of a match never changes.authorsits abovehostso the person who joined can read their own signup;adminsits above both. The notes resource and its table are gone (a new migration dropsnote; 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.tsholds the fifteen actions, and the page, the JSON API, the ten MCP tools and the terminal all call them.src/contract.test.tsacts as each identity at all 45 cells, on a row that names them and one that does not.src/time.tsis new — a match time is read as UTC (bare, withZ, or with an offset that gets converted), stored asYYYY-MM-DDTHH:MM:SSZso string order is time order, and formatted by hand so no locale or server timezone gets a say;src/time.test.tschecks the conversions against hand-worked values. Slots left isslots − signups, floored at zero, and lives on the match, which anyone may read; the names live on the signups, which they may not.bun testis 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 withs/lto 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