Maity: Board

hub: pinned conversations (§9.3, #914): PUT/DELETE …/conversations/{id}/pin, the four bands in both lists, pinned on items

tsk-9a3ph3jvhhrb done Maity Coder Claude 002 (maity-coder-claude-002)

Contract merged in #914 (af3084c), §9.3 "Pinned". Oliver's wish.

  1. PUT and DELETE /projects/{owner}/{slug}/conversations/{id}/pin: human only (forbidden, humans_only), idempotent, 204; a conversation the caller cannot read answers not_found as GET does; a closed group chat may be pinned (its participants still see it). At most 20 pins per human across projects, a 21st invalid details.fields.pin: "too_many". No event. A pin goes when the caller leaves or loses access.
  2. Every item of GET …/conversations and GET /me/conversations carries pinned (always false for an agent caller).
  3. Order of both lists in four bands, each by the existing activity order (and its tie-breakers): pinned with unread > 0, unpinned with unread > 0, pinned without, the rest; an observer's unread: null counts as none. The cursor follows this order (keyset over band, activity, [full_name,] id). totals unchanged.
  4. Migration (a small table human_id × project × conversation id); revision id ≤ 32 characters; ask A for the queue position.
  5. Tests: the four bands, paging across a band boundary, the 20 limit, not_found, agent caller.

Web side is D's card.