Decouple tag identity from presentation (Tag.display_name) - #14
Merged
Conversation
Add nullable Tag.display_name (presentation only; name stays the immutable machine key for votes/Card.tags/federation). Serialize it through the schema, render displayName ?? name at all 6 frontend tag render sites via a shared useTagDisplayName hook, and backfill display names for the 6 no-match-reason tags plus Full Art/Borderless idempotently without clobbering manual edits. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016i9S7LQsCL3FGaih3ZTRBJ
…y-name # Conflicts: # docs/features/printing-tags.md
WilfordGrimley
marked this pull request as ready for review
July 14, 2026 14:17
This was referenced Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Decouples tag identity (
Tag.name, immutable) from tag presentation(
Tag.display_name, freely editable) before more tag data accumulates ontop of the printing/tag consensus system.
Tag.display_name— nullableCharField, additive migration0056_tag_display_name.nameremains the machine key used by votes,tag_vote_statuses,Card.tags, and federation; the field's help textspells out "NEVER rename a Tag after creation."
Tag.serialise()/SerialisedTagschema (Python +TS, quicktype-regenerated) gain
displayName. Frontend rendersdisplayName ?? nameat every tag render site — queue strips(
NoMatchReasonStrip,PrintingConfirmStrip,QueueTagQuestion),TagVotePickerchips, the tag filter UI, and the card detail modal —via a shared
useTagDisplayName()hook backed by the already-cacheduseGetTagsQuery(). All API submissions/filters still sendname.seed_no_match_reason_tagsnow also backfillsdisplay_namefor its six tags ("Custom art", "Altered frame","Upscaled", "AI art", "No collector line", "Non-English"), and
seed_default_tagsbackfills it for the legacy "Full Art"/"Borderless"tags. Both are idempotent and only fill when
display_nameiscurrently
None— a manually-edited display name is never clobbered onre-seed. No existing tag's
namewas renamed.cardpicker/tags.py'sTags.get_tags()/match_tag_fuzzy()/extract()match filenamebracket tokens against
Tag.nameandTag.aliasesonly — neverdisplay_name— andCard.tagspersiststag_object.name. So thisstage changes nothing about indexing today. A future change to
name's key style (not something this PR does) would still break twothings this PR doesn't touch: stale
.lower()filename-bracket matchesagainst the old name, and already-persisted
Card.tagssnapshot arrayswith no reconciliation migration path. Full writeup in
docs/features/printing-tags.md's new Stage 5 section.docs/federation-v1.md(tagnameisthe immutable interchange key;
display_namenever federates) and a newStage 5 section in
docs/features/printing-tags.mdcovering the model,serialization, hook, seeding, and the item-4 analysis in full.
displayName(backend + a newTestGetTagscase with a set display name), frontend fallback rendersraw
namewhendisplayNameis unset (newTagVotePicker.spec.tscase using a mock where only one tag has a display name), and seeding
idempotence (both
test_reason_tags.pyand newtest_default_tags.pyassert re-running the seeder never overwrites a manually-set
display_name).Test plan
pytest— 459 passed, 4 pre-existing unrelated failures(
test_integrations.py::TestMTGIntegration::test_valid_url[moxfield],[moxfield_without_www],test_sources.py::TestUpdateDatabase::test_comprehensive_snapshot,test_upsert— same baseline asmaster, not touched by this change).file, same as baseline),
eslint0 errors,prettier --checkclean,npm run buildsucceeds (static export, all 8 pages).cases.
NoMatchReasonStrip,PrintingConfirmStrip, andTagVotePickeragainst mocks with realdisplay names set — confirmed correct rendering (including the
known-and-intended "Full art" → "Full Art" chip-label change) with no
loading flicker or layout issues.
Holding as draft for review per task instructions — not merging.
🤖 Generated with Claude Code
https://claude.ai/code/session_016i9S7LQsCL3FGaih3ZTRBJ