Fix failures found during the frontend integration sweep:
- Hydrate image attachment views in chat edit/reaction/pin/unpin
responses, pinned-message lists, and durable pin replay. These mutations
could commit successfully and then fail serialization or disconnect
observers.
- Expose room password presence in client/admin projections, using batch
lookups for room lists.
- Preserve nullable category IDs in room-creation review queries so
uncategorized submissions can be read and listed; refresh SQLx metadata.
Validation: Rust formatting, workspace Clippy with warnings denied, five
Docker-backed chat regression tests, eight secondary-read repository
integration tests, eight CLI human-output tests, protobuf freshness, and
a dynamic-Web backend build passed. Remote CI results are tracked on
this PR.
Frontend integration: https://github.com/synctv-org/synctv-app/pull/70
"query":"\n SELECT rcr.id AS \"id!: RoomId\",\n rcr.requested_by AS \"requested_by!: UserId\",\n COALESCE(u.username, '') AS \"requested_by_username!\",\n rcr.name AS \"name!\",\n rcr.description AS \"description!\",\n rc.id AS \"category_id: RoomCategoryId\",\n rc.key AS \"category_key?\",\n rc.name AS \"category_name?\",\n rc.description AS \"category_description?\",\n rc.sort_order AS \"category_sort_order?\",\n rc.is_enabled AS \"category_is_enabled?\",\n rc.created_at AS \"category_created_at?\",\n rc.updated_at AS \"category_updated_at?\",\n rcr.status AS \"status!: ReviewStatus\",\n rcr.requested_at AS \"requested_at!\",\n rcr.reviewed_at AS \"reviewed_at?\",\n rcr.reviewed_by AS \"reviewed_by?: UserId\",\n rcr.rejection_reason AS \"rejection_reason?\"\n FROM room_creation_requests rcr\n LEFT JOIN users u ON u.id = rcr.requested_by\n LEFT JOIN room_categories rc ON rc.id = rcr.category_id\n WHERE rcr.id = $1\n ",
"query":"\n SELECT rcr.id AS \"id!: RoomId\",\n rcr.requested_by AS \"requested_by!: UserId\",\n COALESCE(u.username, '') AS \"requested_by_username!\",\n rcr.name AS \"name!\",\n rcr.description AS \"description!\",\n rc.id AS \"category_id?: RoomCategoryId\",\n rc.key AS \"category_key?\",\n rc.name AS \"category_name?\",\n rc.description AS \"category_description?\",\n rc.sort_order AS \"category_sort_order?\",\n rc.is_enabled AS \"category_is_enabled?\",\n rc.created_at AS \"category_created_at?\",\n rc.updated_at AS \"category_updated_at?\",\n rcr.status AS \"status!: ReviewStatus\",\n rcr.requested_at AS \"requested_at!\",\n rcr.reviewed_at AS \"reviewed_at?\",\n rcr.reviewed_by AS \"reviewed_by?: UserId\",\n rcr.rejection_reason AS \"rejection_reason?\"\n FROM room_creation_requests rcr\n LEFT JOIN users u ON u.id = rcr.requested_by\n LEFT JOIN room_categories rc ON rc.id = rcr.category_id\n WHERE rcr.id = $1\n ",
"query":"\n SELECT rcr.id AS \"id!: RoomId\",\n rcr.requested_by AS \"requested_by!: UserId\",\n COALESCE(u.username, '') AS \"requested_by_username!\",\n rcr.name AS \"name!\",\n rcr.description AS \"description!\",\n rc.id AS \"category_id: RoomCategoryId\",\n rc.key AS \"category_key?\",\n rc.name AS \"category_name?\",\n rc.description AS \"category_description?\",\n rc.sort_order AS \"category_sort_order?\",\n rc.is_enabled AS \"category_is_enabled?\",\n rc.created_at AS \"category_created_at?\",\n rc.updated_at AS \"category_updated_at?\",\n rcr.status AS \"status!: ReviewStatus\",\n rcr.requested_at AS \"requested_at!\",\n rcr.reviewed_at AS \"reviewed_at?\",\n rcr.reviewed_by AS \"reviewed_by?: UserId\",\n rcr.rejection_reason AS \"rejection_reason?\"\n FROM room_creation_requests rcr\n LEFT JOIN users u ON u.id = rcr.requested_by\n LEFT JOIN room_categories rc ON rc.id = rcr.category_id\n WHERE rcr.status = $1\n AND ($2::bigint IS NULL OR rcr.requested_by = $2)\n AND ($3 = '' OR rcr.name ILIKE $3 ESCAPE '\\' OR rcr.description ILIKE $3 ESCAPE '\\')\n ORDER BY rcr.requested_at DESC, rcr.id DESC\n LIMIT $4 OFFSET $5\n ",
"query":"\n SELECT rcr.id AS \"id!: RoomId\",\n rcr.requested_by AS \"requested_by!: UserId\",\n COALESCE(u.username, '') AS \"requested_by_username!\",\n rcr.name AS \"name!\",\n rcr.description AS \"description!\",\n rc.id AS \"category_id?: RoomCategoryId\",\n rc.key AS \"category_key?\",\n rc.name AS \"category_name?\",\n rc.description AS \"category_description?\",\n rc.sort_order AS \"category_sort_order?\",\n rc.is_enabled AS \"category_is_enabled?\",\n rc.created_at AS \"category_created_at?\",\n rc.updated_at AS \"category_updated_at?\",\n rcr.status AS \"status!: ReviewStatus\",\n rcr.requested_at AS \"requested_at!\",\n rcr.reviewed_at AS \"reviewed_at?\",\n rcr.reviewed_by AS \"reviewed_by?: UserId\",\n rcr.rejection_reason AS \"rejection_reason?\"\n FROM room_creation_requests rcr\n LEFT JOIN users u ON u.id = rcr.requested_by\n LEFT JOIN room_categories rc ON rc.id = rcr.category_id\n WHERE rcr.status = $1\n AND ($2::bigint IS NULL OR rcr.requested_by = $2)\n AND ($3 = '' OR rcr.name ILIKE $3 ESCAPE '\\' OR rcr.description ILIKE $3 ESCAPE '\\')\n ORDER BY rcr.requested_at DESC, rcr.id DESC\n LIMIT $4 OFFSET $5\n ",