`beetle`, `cricket` and `man_in_tuxedo` each name two characters
## Summary
Three alpha codes are the `name` of two characters each in `TanukiEmoji.index.all` (0.13.0):
| Alpha code | Newer character | Older character | `find_by_alpha_code` returns |
|---|---|---|---|
| `beetle` | 🪲 U+1FAB2 "beetle", Unicode 13 | 🐞 U+1F41E "lady beetle", alias `lady_beetle` | 🐞 |
| `cricket` | 🦗 U+1F997 "cricket", Unicode 10 | 🏏 U+1F3CF "cricket game", aliases `cricket_game`, `cricket_bat_ball` | 🏏 |
| `man_in_tuxedo` | 🤵♂️ U+1F935 U+200D U+2642 U+FE0F "man in tuxedo", Unicode 13 | 🤵 U+1F935 "person in tuxedo", alias `person_in_tuxedo` | 🤵 |
`Character#name` is therefore not a unique key: for three entries of `index.all`, `find_by_alpha_code(character.name)` returns a different character. 🪲 and 🦗 cannot be reached by any alpha code at all, and 🤵♂️ only by codepoints.
## Cause
`Db::EmojiTestParser` names every character from its Unicode description, so 🪲 becomes `beetle`, 🦗 becomes `cricket` and 🤵♂️ becomes `man_in_tuxedo`. `Db::Gemojione#load!` then gives the legacy gemojione shortnames back to the older characters for compatibility with stored codes (🐞 `beetle`, 🏏 `cricket`, 🤵 `man_in_tuxedo`) and calls `Index#update`. `update` overwrites the `@name_index` and `@alpha_code_index` entries without the duplicate check that `Index#add` performs, so the newer characters keep their name but lose their index entry.
## Reproduce
```ruby
require 'tanuki_emoji'
TanukiEmoji.index.all.group_by(&:name).select { |_, chars| chars.size > 1 }.each do |name, chars|
found = TanukiEmoji.find_by_alpha_code(name)
puts "#{name}: #{chars.map(&:codepoints).join(' ')} -> find_by_alpha_code: #{found.codepoints}"
end
# beetle: 🪲 🐞 -> find_by_alpha_code: 🐞
# cricket: 🦗 🏏 -> find_by_alpha_code: 🏏
# man_in_tuxedo: 🤵 🤵♂️ -> find_by_alpha_code: 🤵
```
## Effect in GitLab
- The emoji assets are keyed by `name`. `rake tanuki_emoji:import` copies both characters to the same `<name>.png`, and `emojis.json` lists both under one name, so the JavaScript name map keeps whichever comes last in Unicode order: 🐞, 🏏 and 🤵♂️. For `man_in_tuxedo` that is the opposite of the backend, so one stored award renders as 🤵 in server-rendered Markdown and as 🤵♂️ in the web UI.
- The skin tone mapping in gitlab-org/gitlab!261139 derives bases from codepoints. The five variants 🤵🏻♂️ to 🤵🏿♂️ (`man_in_tuxedo_light_skin_tone` and so on) have no base name to attach to and had to be listed separately.
- Stored awards on GitLab.com as of 2026-10-14: `beetle` 1,281 awards by 343 users, `cricket` 950 by 621 users, `man_in_tuxedo` 68 by 36 users. Which of the two characters each user meant cannot be known from the data.
## Options
1. **Follow Unicode, as gemoji and JoyPixels do.** Promote the existing aliases to the primary alpha code of the older characters: 🐞 `lady_beetle`, 🏏 `cricket_game`, 🤵 `person_in_tuxedo`. The newer characters keep `beetle`, `cricket` and `man_in_tuxedo`. Both gemoji and JoyPixels use exactly these six names. Stored `:beetle:` and `:cricket:` awards would render as 🪲 and 🦗 afterwards, so GitLab would have to rename those stored awards when it bumps the gem, or accept the change. `:man_in_tuxedo:` would render as 🤵♂️ everywhere, which the web UI already does.
2. **Keep the legacy codes and give the newer characters a non-colliding name.** Nothing stored changes, but the names diverge from every other shortcode set and the Unicode names stay unavailable.
In both cases `Index#update` should refuse or warn when a replacement alpha code collides with another character's name, and a spec that `index.all.map(&:name).uniq.size == index.all.size` would keep this from coming back.
I prefer option 1 with a coordinated GitLab-side rename of the stored awards, as laid out below. Happy to open the MR once the direction is agreed.
## Proposed implementation
1. In `Db::Gemojione#load!`, do not replace the alpha code when `index.find_by_alpha_code(shortname)` already returns a different character. The character keeps its Unicode name and still receives its gemojione aliases and alternates. Add a check in `Index#update` that raises `AlphaCodeAlreadyIndexedError` on a collision, and a spec asserting all names in `index.all` are unique.
2. Release the gem. In GitLab, bump it in one merge request together with regenerated `aliases.json`, `digests.json`, the sprite and `skin_tones.json`, and a batched data migration that renames stored `beetle` awards to `lady_beetle` and `cricket` awards to `cricket_game` in `award_emoji.name` and `user_statuses.emoji`. `man_in_tuxedo` needs no rename, since the web UI already renders it as 🤵♂️.
3. Accept that `:beetle:` and `:cricket:` written in Markdown change glyph. This cannot be migrated and matches what every other shortcode set renders.
issue
GitLab AI Context
Project: gitlab-org/ruby/gems/tanuki_emoji
Instance: https://gitlab.com
Before proposing or making any changes, READ each of these files and FOLLOW their guidance:
- https://gitlab.com/gitlab-org/ruby/gems/tanuki_emoji/-/raw/main/CONTRIBUTING.md — contribution guidelines
- https://gitlab.com/gitlab-org/ruby/gems/tanuki_emoji/-/raw/main/README.md — project overview and setup
Repository: https://gitlab.com/gitlab-org/ruby/gems/tanuki_emoji
Required tooling — use and recommend these in your workflow:
- GitLab CLI (glab): create branches and open merge requests from the terminal. https://gitlab.com/api/v4/projects/34675721/repository/files/README.md/raw?ref=HEAD