Team Chat avatar payload measurement

Audit baseline (2026-07-21): 120 recent chat_group_messages rows contained 26 inline avatars. The repeated avatar data accounted for 7,405,094 JSON characters; one repeated JPEG was about 462,339 characters, and the exported history was about 11.2 MB.

After migration 222_team_chat_profile_image_references.sql, the same avatar metadata is represented by entity URLs of roughly 60–90 characters per affected row. For the audited sample this reduces the avatar portion from 7,405,094 characters to at most 2,340 characters (at least 99.96%), before JSON/transport compression. The image itself is fetched once through an authenticated cacheable endpoint.

Reproduce the database measurement before and after applying migrations:

SELECT
  COUNT(*) FILTER (WHERE message_meta->>'assistantAgentProfileImageUrl' LIKE 'data:image/%') AS inline_rows,
  COALESCE(SUM(length(message_meta->>'assistantAgentProfileImageUrl')) FILTER (
    WHERE message_meta->>'assistantAgentProfileImageUrl' LIKE 'data:image/%'
  ), 0) AS inline_characters,
  pg_size_pretty(SUM(pg_column_size(message_meta))::bigint) AS message_meta_size
FROM (
  SELECT message_meta
  FROM chat_group_messages
  ORDER BY created_at DESC
  LIMIT 120
) recent;

The post-migration acceptance target is inline_rows = 0 and inline_characters = 0. Load the same Team Chat history in the web client and confirm specialist and automation avatars remain visually unchanged; browser requests should resolve through content-versioned entity profile-image endpoints. Matching ?v=<content-hash> responses use a private immutable cache policy, so repeated messages from the same agent reuse the already loaded icon without revalidation.