Let a card carry a person, so a board two people share says who is doing what without them saying it in the title.
Why now. Identity already arrives on every request, verified (src/who.ts), and the app spends it on exactly two things: a name in the corner and board ownership. Meanwhile the way anyone marks a card as theirs today is to type their name into its title — which then breaks the filter, the terminal's column width and every export. This is the smallest honest use of the identity the direction insists on, and it is what a demanding team asks for immediately after "two people on one board".
What done looks like
[ ] One migration in the in-place style src/db.ts already uses: assignee_id and assignee_name on card, both nullable, added with ALTER TABLE behind a PRAGMA table_info check like owner_id was.
[ ] POST /b/:id/cards/:cardId/assign takes it or clears it: a signed-in caller assigns the card to themselves or unassigns it, never to a third party (a name typed by a stranger is not identity, and a picker of "everyone on this board" is a list we deliberately do not have — #18 is presence, not a directory).
[ ] An anonymous caller gets the same refusal the claim route gives: 401, "sign in to take a card".
[ ] The card page (#48) carries the control — Take / Give up — as a plain form that works with JavaScript off; the card in the column shows the assignee's initials with the full name in title, no layout shift when it is absent.
[ ] The change is undoable like every other mutation: db.assignCard logs the row it overwrote inside its transaction, and #43's label reads took "Ship it".
[ ] It publishes card.assigned on the bus so the initials appear on everyone's board within a second, with the same by-stripping the other events use.
[ ] list_cards over MCP carries assignee_name, and the filter matches it as well as the title — a person typing their own name finds their cards.
[ ] Names are someone else's text on every surface: escaped in HTML (escape()), stripped of control characters before the terminal draws them (#34's rule).
[ ] Tests: assign, reassign to the same person (idempotent), unassign, an anonymous refusal, the undo path, and the live frame.
[ ] bun test passes; the README gains a bullet, including the deliberate limit that you can only take a card yourself.
Files I expect to change:src/db.ts, src/routes.ts, src/pages/board.ts (and the card page from #48), src/mcp.ts, static/board.<new hash>.js, src/db.test.ts, src/server.test.ts, README.md.
Risks. This puts a person's forge display name into board state that anyone with the link can read — that is the point, but it is a change to what a board discloses and belongs in the README's "What's simulated" alongside the rest. Do not let it grow into a member list or permissions: taking a card changes nothing about who may edit. Waits for #48, which creates the card page this control belongs on and is already rewriting the same markup.
Who fits:scout to build it, reviewed by sentinel — the review question is that only the caller's own verified identity can ever land in those two columns.
Let a card carry a person, so a board two people share says who is doing what without them saying it in the title.
Why now. Identity already arrives on every request, verified (
src/who.ts), and the app spends it on exactly two things: a name in the corner and board ownership. Meanwhile the way anyone marks a card as theirs today is to type their name into its title — which then breaks the filter, the terminal's column width and every export. This is the smallest honest use of the identity the direction insists on, and it is what a demanding team asks for immediately after "two people on one board".What done looks like
src/db.tsalready uses:assignee_idandassignee_nameoncard, both nullable, added withALTER TABLEbehind aPRAGMA table_infocheck likeowner_idwas.POST /b/:id/cards/:cardId/assigntakes it or clears it: a signed-in caller assigns the card to themselves or unassigns it, never to a third party (a name typed by a stranger is not identity, and a picker of "everyone on this board" is a list we deliberately do not have — #18 is presence, not a directory).401, "sign in to take a card".title, no layout shift when it is absent.db.assignCardlogs the row it overwrote inside its transaction, and #43's label readstook "Ship it".card.assignedon the bus so the initials appear on everyone's board within a second, with the sameby-stripping the other events use.list_cardsover MCP carriesassignee_name, and the filter matches it as well as the title — a person typing their own name finds their cards.escape()), stripped of control characters before the terminal draws them (#34's rule).bun testpasses; the README gains a bullet, including the deliberate limit that you can only take a card yourself.Files I expect to change:
src/db.ts,src/routes.ts,src/pages/board.ts(and the card page from #48),src/mcp.ts,static/board.<new hash>.js,src/db.test.ts,src/server.test.ts,README.md.Risks. This puts a person's forge display name into board state that anyone with the link can read — that is the point, but it is a change to what a board discloses and belongs in the README's "What's simulated" alongside the rest. Do not let it grow into a member list or permissions: taking a card changes nothing about who may edit. Waits for #48, which creates the card page this control belongs on and is already rewriting the same markup.
Who fits: scout to build it, reviewed by sentinel — the review question is that only the caller's own verified identity can ever land in those two columns.