Public: anyone can read this; the crew can change it.
The roster: a public list of who counts toward a quorum, kept by an admin #4
commented
Parked by the crew: the check failed and the decider saw no point in another try (pull #16 closed). Planner: split this smaller, or rewrite what done looks like; a person can also say what to try.
commented
Parked, and now split. The check log says the preview exited before answering with the stack at the serve() call in src/server.ts, so this was the migration failing at boot on a database copied from production, not a failing test. Replaced by #27 (the member table and the three actions, with a test that upgrades a seeded database) and #28 (the page, the API, MCP and the terminal). #29 makes that class of failure loud and catches it in the check. Closing this one; nothing in it is lost.
Sign in to join this conversation.
A quorum needs a denominator, so the group is an explicit list of people that an admin keeps and anyone can read.
What done looks like
src/db.tscreatesmember(id,person_idTEXT NOT NULL UNIQUE,nameTEXT NOT NULL,added_byTEXT NOT NULL, created_at).src/actions.tsgainslistMembers(db, who),addMember(db, who, {personId, name})andremoveMember(db, who, {personId}), plus an exportedisMember(db, who)that later work leans on. Reading is open to everyone; writing isadminonly (forbiddenfor a user,signin_requiredfor anon).personIdandnameare 1–64 characters,personIdmatches[A-Za-z0-9_.-]+, and adding someone already on the roster is not an error and adds no second row. The roster is capped at 256 with a named failure past it.removeMemberfor someone who is not there isnot_found.addMemberemitsmember.added,removeMemberemitsmember.removed./grouppage lists the roster (name, since when) with an add form and a Remove button visible to an admin only, and tells a signed-in person whether they are a member. On an empty roster an admin is offered "Add me", which calls the same action with their own identity — no special case inside the action.src/pages/does not exist yet, this change creates it (layout.ts,home.ts,group.ts) and leaves/rendering what it renders today; if it exists,group.tsgoes beside the others with a link inlayout.GET /api/members,POST /api/members,DELETE /api/members/:personId; MCP toollist_members; the terminal shows the roster undergand says whether the account at the keyboard is on it.person_idwith a slash is refused; removing an absent person isnot_found;GET /api/membersworks for a guest;DELETEfrom a user is 403.README.mdsays what the roster is and that it is public, andbun testis green.Files I expect to change:
src/db.ts,src/actions.ts,src/actions.test.ts,src/routes.ts,src/pages/group.ts(new),src/pages/layout.ts,src/mcp.ts,src/tty.ts,src/server.test.ts,README.md.Risks: a member is the forge's
person_id, never the display name — a rename must not drop someone from the group; store the name for display and match on the id./groupis the only place an admin's power shows: keep the admin check in the action so the API and MCP get it too, and let the page decide only what to draw. This issue and "Propose, read and withdraw" both touch the page layer; whichever lands second rebases onto the other'ssrc/pages/.Fit: Hearth — a small panel that says plainly who is in and who is out. A person assigns; I do not.