You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
"AI art needs to be a votable thing for users the same way external-ip does."
Related, earlier: "being able to vote for UB/UW for custom cards is important."
Verified against origin/master at 17345a48050cd6734f312d033b6ef1ed4f1af014 (2026-08-05).
Current state (code-grounded)
V1 — The attribute-chip taxonomy
Authoritatively defined at MPCAutofill/cardpicker/attribute_tags.py:32-44, ATTRIBUTE_CHIP_TAG_NAMES — exactly 11 tags: Full Art, Borderless, Showcase, Extended, Etched, Black Border, White Border, Silver Border, Old Border, Modern Border, Future Frame.
Mirrored frontend-side in frontend/src/features/attributeChips/attributeChips.ts:96-127 (STANDALONE_CHIPS [5] + BORDER_COLOR_GROUP [3] + FRAME_STYLE_GROUP [3] = 11, via ALL_ATTRIBUTE_CHIPS). Backend _tag_confidence() (question_feed.py:148-153) filters Tag.objects.filter(name__in=ATTRIBUTE_CHIP_TAG_NAMES) — this is the curated, proactively-surfaced roster.
V2 — The human vote surface (three surfaces, one generic mechanism underneath)
Generic TagVotePicker.tsx grid — rendered via AttributeVotingPanel.tsx on the card-detail modal / printing-tag queue when a printing hasn't resolved. Backed by post_tag_consensus (views.py:1560-1574), which queries Tag.objects.order_by("name") — every seeded Tag, no taxonomy filter — and post_submit_tag_vote (views.py:1577-1619), which accepts Tag.objects.get(name=req.tagName) for any existing tag name.
Question-feed "tag" tier — QueueTagQuestion.tsx, served by _tier_2_contested/_tier_4_fresh (question_feed.py:470-519, 536+) via get_tag_review_queue_pairs() (tag_consensus.py:358-389). Candidate set is Card.tag_vote_statuses for any tag with CONTESTED/UNRESOLVED status — populated by resolve_and_persist_tag_votes() (tag_consensus.py:81-134) for "every tag that has at least one vote cast against card" — also not filtered to the 11-tag roster.
Conclusion: the 11-tag chip ring is a curated subset; the underlying CardTagVote/consensus/question-feed machinery is generic to any seeded Tag row.
V3 — Is "AI-Generated" already human-votable?
Mechanically, yes — it already works end-to-end, at low coverage. "AI-Generated" is a real, pre-existing Tag (cardpicker.default_tags.DEFAULT_TAGS), TagModerationClass.STANDARD — briefly marked SENSITIVE in PR #263, reverted by owner decision 2026-07-21 (local_detect_ai_art.py:22-42: "ordinary human votes is fine for AI I think... they will go contested if there is not an immediate human consensus that is the system working as intended"). It is not in ATTRIBUTE_CHIP_TAG_NAMES (no ring/rail chip), but is reachable via surfaces 2 and 3 above.
Production row counts (read-only query, 2026-08-05):
CardTagVote rows with anonymous_id='ai-art-detector-v1': 1,183, all source=OCR, from a singlerun_id (20260721T114816-0fa65084 — confirmed via .distinct() on both CardScanLog and CardTagVote; the detector has never run a second time).
"AI-Generated" tag total CardTagVote rows: 1,185 (1,183 machine + 2 human USER-sourced — essentially untouched by humans despite being live-votable today).
tag_vote_statuses breakdown for "AI-Generated" across 1,185 cards: 100% unresolved, 0 contested, 0 resolved — a lone OCR vote can never clear the human-backed gate (vote_consensus.is_human_backed_source, line 55-56: only sources outside {DEDUCTION, OCR, FEDERATED, IMPLICIT} count), so all 1,185 of these cards are, right now, live-eligible in get_tag_review_queue_pairs() — a human confirmation prompt already exists for them; almost nobody has answered it.
Detector coverage (verified 2026-08-05, contradicts nothing in issue #367 — if anything it's worse today): CardScanLog skip breakdown for ai-art-detector-v1: no-evidence=196,856, no-marker-hit=19,401, plus 1,183 votes = 217,440 cards ever touched, out of 230,501 total Card rows (13,061 never touched at all). Of those, only no-marker-hit + votes = 20,584 cards (8.9% of the catalog) have ever actually had their evidence checked against the marker list. The other ~91% either had no ImageEvidence yet at the single 2026-07-21 run (no-evidence, technically rescan-eligible but never rescanned) or postdate that run entirely. #367's "ran once, ~90% uncovered" finding is current, not stale.
V4 — Is external-IP/UB-UW defined and human-votable?
A "external-ip" Tag already exists (Tag.objects.filter(name='external-ip').exists() → True, 7 production CardTagVote rows — necessarily all human, since the only machine writer, import_external_ip_tags, was retired 2026-07-29 having never run in production, 0 rows — models.py:1597-1617, scryfall_bulk_data.py:74). But its wiring is narrow: it is one of seven fixed reason-chips in MPCAutofill/cardpicker/reason_tags.py:56-98 (EXTERNAL_IP_TAG_NAME = "external-ip"), surfaced only by NoMatchReasonStrip.tsx — a single-tap, one-directional "why no match?" reason shown only immediately after a human has already voted "No match" on printing identification. reason_tags.py:90-93 states explicitly: "There is no 'external-ip'-negation counterpart tag here either" — no persistent Yes/No toggle exists for it the way the 11-chip roster or TagVotePicker work for other tags.
#505's claim ("no UB/UW-related data is stored or indexed anywhere... set_type/security_stamp [are not] declared" on PrintingMetadataRow/CanonicalPrintingMetadata) is verified current: grep -c 'set_type\|security_stamp' MPCAutofill/cardpicker/models.py → 0 matches.
#635 (open, owner-ratified 2026-07-29) defines "external-ip" as an artwork-origin test ("does this art come from outside Magic's own IP"), explicitly not the Wizards "Universes Beyond" product line — and rules the tag applies at illustration grain, not card/printing grain (1,467 oracle cards split card-grain vs. 0-of-50,757 illustration counterexamples). Still open: whether 21 homage basic lands (Godzilla/Mothra/Dracula art) count. #437 (open) is the companion machine deductive-tagging pass researching set_type/security_stamp as the canonical-side UB/UW primitive — separate from human votability, cross-referenced not duplicated here.
V5 — Taxonomy addition or mechanism build?
Taxonomy addition, not a mechanism build. The generic CardTagVote → resolve_and_persist_tag_votes → get_tag_review_queue_pairs → question-feed-tag-tier pipeline already handles both tags mechanically today, proven by live production data: 1,185 UNRESOLVED "AI-Generated" rows and 7 "external-ip" CardTagVote rows already exist and already flow through the same consensus gate as any other tag. No new model, endpoint, or consensus mechanism is required. What's actually missing:
Neither tag is in the curated, proactively-surfaced roster — ATTRIBUTE_CHIP_TAG_NAMES (backend) / ALL_ATTRIBUTE_CHIPS (frontend) — so neither gets a ring/rail chip or a prioritized question-feed slot; a voter only reaches "AI-Generated" via the passive, unprompted TagVotePicker grid, and only reaches "external-ip" via the one-shot no-match-reason strip.
For "external-ip" specifically: today's wiring has no persistent toggle / no NOT_APPLICABLE counter-vote path (a deliberate prior decision, reason_tags.py:90-93) — making it "votable... for custom cards" the way AI-art already partially is would mean either adding it to the direct-access roster (reopening that no-negation decision) or building the What's That: contextual report button — flag an image for moderators against relevant tags, with optional per-user hide #714 context-aware report affordance on top of the existing reason-chip.
Why AI-art voting is load-bearing, not merely parity
#278 (open) confirms: AI_ART_RESCANNABLE_SKIP_REASONS = {no-evidence, incomplete-evidence} (local_detect_ai_art.py:139) — no-marker-hit is deliberately excluded. Once the detector scans a card's evidence and finds no marker, that negative conclusion is permanent; no automated path revisits it (#278's own body: "no-marker-hit negatives are permanently gated", one concrete flip already found: card 214416). Combined with #367's ~91%-never-checked figure verified above, for the overwhelming majority of the catalog there is either no machine verdict at all, or a stale one the machine cannot itself correct. A human vote is the only correction path for a detector false negative — this makes AI-art human voting a repair mechanism for a channel that structurally cannot repair itself, not a nice-to-have UI parity item.
The two-mode distinction (differs between the two attributes)
AI-art: a machine channel exists but has thin, currently-frozen coverage (8.9% actually checked, single historical run). Where the detector has run and hit (1,185 cards) — a human vote is a confirmation with a prior, and the mechanism to serve that confirmation already exists live in the question feed today, just unprompted/low-visibility. Where it hasn't run (the other ~91%) — the human answers cold, and today has no entry point at all short of unprompted use of the generic TagVotePicker grid.
External-IP on custom art: no machine ground truth is possible — custom art has no Scryfall record, so there is no set_type/security_stamp to read (confirmed: those fields don't exist anywhere in models.py). The human vote is the primary and sole signal, always cold — but the current wiring (the no-match-reason strip) only reaches a card after an unrelated prior vote, is single-tap, and has no revisit path.
This maps onto the difficulty-lane model recorded in #716 (easy = confirm an uncontested machine vote; medium = break a tie between competing votes; hard = answer cold), derived purely from data state, no telemetry needed. #619 is the sibling case for frame-style/bleed-edge chips (145,419 evidence-derivable votes sitting uncast) — same "channel exists, coverage is the gap" shape as AI-art, cross-referenced not duplicated.
Two open decisions (for the owner — not answered here)
Vote or report? Should AI-art determination be a consensus vote, a moderator report (the affordance proposed but not yet built in What's That: contextual report button — flag an image for moderators against relevant tags, with optional per-user hide #714, part of epic [Epic] What's That Card: full-page repass (layout, question content, feed intelligence) #704), or both? The closest existing precedent is "Marked as proxy" tag + marker-absence moderation flags (catalog compliance scan) #291 ("Marked as proxy" tag + marker-absence moderation flags): a machine detector casts a positive tag, human-confirmable via ordinary consensus (same generic gate, no special moderator requirement on the vote itself) — but the negative/absence case routes to a separate, batched moderator-review flag surface, not the vote system. That precedent already suggests an answer in the direction of "both, split by direction" — vote for confirmation, a separate flag surface for escalation — but the owner should rule on it explicitly. What's That: contextual report button — flag an image for moderators against relevant tags, with optional per-user hide #714 itself is scoped only as a record (owner's own words, quoted there): "a card that is custom art could be universes within or beyond (art:external-ip)... there should be a report button... that lets a user flag for mods among relevant tags" — i.e. the owner has already asked for exactly this kind of context-aware report affordance, evaluated against the existing ReportCardPanel.tsx (fixed NSFW/Low-quality/Wrong-info/Broken-image/Other reasons, anonymous-id-scoped, 10/day rate limit) and cardpicker/moderation.py moderator queue, both of which already exist and were assessed as the natural extension point (not greenfield).
Threshold. The standard consensus gate is PRINTING_TAG_MIN_SHARE = 0.6 (60%), PRINTING_TAG_MIN_VOTES = 2 (MPCAutofill/settings.py:65-66), plus the hard human-backed gate (vote_consensus.is_human_backed_source, line 55-56 — no volume of DEDUCTION/OCR/FEDERATED/IMPLICIT votes can resolve a tag alone). This applies uniformly today to "AI-Generated" (moderation_class=standard, confirmed live — no AI-art-specific higher bar or mandatory moderator co-sign exists in code, per the owner's own 2026-07-21 reversal of its earlier SENSITIVE marking). A false "this is AI-generated" resolution is a public accusation about a named human artist's work — the reputational cost of a wrong resolution is asymmetric, unlike e.g. border-colour, where a wrong call is merely a wrong call. Should this tag carry a higher threshold, a mandatory moderator review before the resolution becomes visible/actionable, or the standard gate as-is? Stated neutrally; the owner decides.
Owner, verbatim: "I think I want them to surface context dependant. external-ip, ai art can show when we want to ask about custom art."
This settles part of decision 1 above (vote or report?) in one direction: neither tag joins the fixed 11-tag ATTRIBUTE_CHIP_TAG_NAMES roster surfaced unconditionally on every card. Instead both surface conditionally, when the feed/rail is asking about custom art specifically — a third surfacing mode alongside the two already documented in V2 above (the curated chip ring, and the generic always-available TagVotePicker/question-feed-tag-tier). Not yet specified by the owner: the exact trigger condition ("when we want to ask about custom art" — plausibly a custom-art tag already present on the card, or a card with no resolved printing at all) or which of the two existing generic mechanisms this context-dependent mode attaches to. Both remain open.
Owner, also verbatim: "They can be sensitive, but no consumer built for sensitive yet."
V1 finding (verified against current code) — refutes this claim as stated. A SENSITIVE-class consumer already exists and is live, not absent. tag_consensus.resolve_tag passes require_privileged=tag.moderation_class == TagModerationClass.SENSITIVE into vote_consensus.resolve_weighted_consensus (tag_consensus.py:73). When true, a crowd consensus that would otherwise resolve is instead parked as PENDING_PRIVILEGED (vote_consensus.py:533-534) unless a moderator/admin vote backs the winning side — persisted as TagVoteStatus.PENDING_APPROVAL (tag_consensus.py:140-141), with card.tags deliberately left untouched so a pending tag has zero search consequences until a moderator co-signs. A moderator-facing approval queue is already wired too: get_pending_approval_queue_pairs (views.py:1992-2003), and SENSITIVE tags additionally block implicit votes while pending (views.py:1711). Tested end-to-end (tests/test_moderation_gate.py, tests/test_sensitive_tags.py).
What's actually true and load-bearing for this issue's open threshold decision (item 2 above): neither "AI-Generated" nor "external-ip" is currently marked SENSITIVE. "AI-Generated" is explicitly STANDARD (sensitive_tags.py:46-60, FORMERLY_SENSITIVE_TAG_NAMES, reverted from #263's brief SENSITIVE marking by the 2026-07-21 owner decision already cited in V3 above), and "external-ip" was never listed in SENSITIVE_TAGS at all. So the gap is narrower than the owner's framing suggests: the SENSITIVE consensus gate itself is built and working; what does NOT exist is any UI/report-affordance CONSUMER of a SENSITIVE tag (the surface #714 describes), and no decision has marked either of these two tags SENSITIVE in the first place. If this issue's threshold decision lands on "raise the bar for AI-art", the consensus-side change is one line (add the tag name to sensitive_tags.SENSITIVE_TAGS); the report-button UI consumer described in #714 is the piece that would need to be built from scratch.
Threshold decision: resolved - standard consensus gate, no raised bar (2026-08-06)
Answers this issue's open decision 2 (threshold). Both AI-Generated and external-ip stay STANDARD class (see #714 for the full moderation-class ruling and code verification - not duplicated here); the ruling's reasoning: ordinary human votes are sufficient for these two attributes, and absent immediate human consensus the tag going CONTESTED is the system working as intended, not a failure to be pre-empted with a privileged co-sign.
Use the standard consensus gate for both - verified: PRINTING_TAG_MIN_SHARE = 0.6 (MPCAutofill/MPCAutofill/settings.py:66), PRINTING_TAG_MIN_VOTES = 2 (:65), plus the unconditional has_human_backed requirement (vote_consensus.py:532, winner["has_human_backed"]) - the same gate every other STANDARD tag resolves through, applied via tag_consensus.resolve_tag (tag_consensus.py:35-78) with require_privileged=False for a STANDARD tag's moderation_class (:73). No raised bar, no mandatory moderator co-sign, for either tag.
Consequence - context-dependent surfacing is now principled, not arbitrary. Per the 2026-08-05 owner decision above, both tags surface conditionally ("when we want to ask about custom art") as ordinary votable chips through the existing generic TagVotePicker/question-feed-tag-tier mechanism (V2 above) - the same STANDARD consensus path as any other tag. This pairs with #714's SENSITIVE-set ruling: SENSITIVE narrows to exactly NSFW and appropriate-bleed, which surface instead through #714's always-visible report affordance with privileged co-sign. STANDARD attributes (this issue) surface context-dependently as ordinary votable chips; SENSITIVE attributes (#714) surface through the report affordance and carry privileged co-sign - two surfaces for two moderation classes, not an arbitrary split.
Owner requirement
Related, earlier: "being able to vote for UB/UW for custom cards is important."
Verified against
origin/masterat17345a48050cd6734f312d033b6ef1ed4f1af014(2026-08-05).Current state (code-grounded)
V1 — The attribute-chip taxonomy
Authoritatively defined at
MPCAutofill/cardpicker/attribute_tags.py:32-44,ATTRIBUTE_CHIP_TAG_NAMES— exactly 11 tags:Full Art, Borderless, Showcase, Extended, Etched, Black Border, White Border, Silver Border, Old Border, Modern Border, Future Frame.Mirrored frontend-side in
frontend/src/features/attributeChips/attributeChips.ts:96-127(STANDALONE_CHIPS[5] +BORDER_COLOR_GROUP[3] +FRAME_STYLE_GROUP[3] = 11, viaALL_ATTRIBUTE_CHIPS). Backend_tag_confidence()(question_feed.py:148-153) filtersTag.objects.filter(name__in=ATTRIBUTE_CHIP_TAG_NAMES)— this is the curated, proactively-surfaced roster.V2 — The human vote surface (three surfaces, one generic mechanism underneath)
attributeChipRender.tsx(Yes/No buttons, redesigned by fix(question-feed): direct-access Yes/No chips, narrowing after no-match, art-crop gap documented #687 2026-08-04, see its header comment lines 12-21), rendered byAttributesSection.tsx(display rail) andQuestionFeed.tsx's ring. Restricted to the 11-tag roster above.TagVotePicker.tsxgrid — rendered viaAttributeVotingPanel.tsxon the card-detail modal / printing-tag queue when a printing hasn't resolved. Backed bypost_tag_consensus(views.py:1560-1574), which queriesTag.objects.order_by("name")— every seeded Tag, no taxonomy filter — andpost_submit_tag_vote(views.py:1577-1619), which acceptsTag.objects.get(name=req.tagName)for any existing tag name.QueueTagQuestion.tsx, served by_tier_2_contested/_tier_4_fresh(question_feed.py:470-519,536+) viaget_tag_review_queue_pairs()(tag_consensus.py:358-389). Candidate set isCard.tag_vote_statusesfor any tag withCONTESTED/UNRESOLVEDstatus — populated byresolve_and_persist_tag_votes()(tag_consensus.py:81-134) for "every tag that has at least one vote cast againstcard" — also not filtered to the 11-tag roster.Conclusion: the 11-tag chip ring is a curated subset; the underlying
CardTagVote/consensus/question-feed machinery is generic to any seededTagrow.V3 — Is "AI-Generated" already human-votable?
Mechanically, yes — it already works end-to-end, at low coverage. "AI-Generated" is a real, pre-existing
Tag(cardpicker.default_tags.DEFAULT_TAGS),TagModerationClass.STANDARD— briefly markedSENSITIVEin PR #263, reverted by owner decision 2026-07-21 (local_detect_ai_art.py:22-42: "ordinary human votes is fine for AI I think... they will go contested if there is not an immediate human consensus that is the system working as intended"). It is not inATTRIBUTE_CHIP_TAG_NAMES(no ring/rail chip), but is reachable via surfaces 2 and 3 above.Production row counts (read-only query, 2026-08-05):
CardTagVoterows withanonymous_id='ai-art-detector-v1': 1,183, allsource=OCR, from a singlerun_id(20260721T114816-0fa65084— confirmed via.distinct()on bothCardScanLogandCardTagVote; the detector has never run a second time).CardTagVoterows: 1,185 (1,183 machine + 2 humanUSER-sourced — essentially untouched by humans despite being live-votable today).tag_vote_statusesbreakdown for "AI-Generated" across 1,185 cards: 100%unresolved, 0contested, 0resolved— a loneOCRvote can never clear the human-backed gate (vote_consensus.is_human_backed_source, line 55-56: only sources outside{DEDUCTION, OCR, FEDERATED, IMPLICIT}count), so all 1,185 of these cards are, right now, live-eligible inget_tag_review_queue_pairs()— a human confirmation prompt already exists for them; almost nobody has answered it.Detector coverage (verified 2026-08-05, contradicts nothing in issue #367 — if anything it's worse today):
CardScanLogskip breakdown forai-art-detector-v1:no-evidence=196,856,no-marker-hit=19,401, plus 1,183 votes = 217,440 cards ever touched, out of 230,501 totalCardrows (13,061 never touched at all). Of those, onlyno-marker-hit+ votes = 20,584 cards (8.9% of the catalog) have ever actually had their evidence checked against the marker list. The other ~91% either had noImageEvidenceyet at the single 2026-07-21 run (no-evidence, technically rescan-eligible but never rescanned) or postdate that run entirely. #367's "ran once, ~90% uncovered" finding is current, not stale.V4 — Is external-IP/UB-UW defined and human-votable?
A "external-ip"
Tagalready exists (Tag.objects.filter(name='external-ip').exists()→True, 7 productionCardTagVoterows — necessarily all human, since the only machine writer,import_external_ip_tags, was retired 2026-07-29 having never run in production, 0 rows —models.py:1597-1617,scryfall_bulk_data.py:74). But its wiring is narrow: it is one of seven fixed reason-chips inMPCAutofill/cardpicker/reason_tags.py:56-98(EXTERNAL_IP_TAG_NAME = "external-ip"), surfaced only byNoMatchReasonStrip.tsx— a single-tap, one-directional "why no match?" reason shown only immediately after a human has already voted "No match" on printing identification.reason_tags.py:90-93states explicitly: "There is no 'external-ip'-negation counterpart tag here either" — no persistent Yes/No toggle exists for it the way the 11-chip roster orTagVotePickerwork for other tags.#505's claim ("no UB/UW-related data is stored or indexed anywhere...
set_type/security_stamp[are not] declared" onPrintingMetadataRow/CanonicalPrintingMetadata) is verified current:grep -c 'set_type\|security_stamp' MPCAutofill/cardpicker/models.py→ 0 matches.#635 (open, owner-ratified 2026-07-29) defines "external-ip" as an artwork-origin test ("does this art come from outside Magic's own IP"), explicitly not the Wizards "Universes Beyond" product line — and rules the tag applies at illustration grain, not card/printing grain (1,467 oracle cards split card-grain vs. 0-of-50,757 illustration counterexamples). Still open: whether 21 homage basic lands (Godzilla/Mothra/Dracula art) count. #437 (open) is the companion machine deductive-tagging pass researching
set_type/security_stampas the canonical-side UB/UW primitive — separate from human votability, cross-referenced not duplicated here.V5 — Taxonomy addition or mechanism build?
Taxonomy addition, not a mechanism build. The generic
CardTagVote→resolve_and_persist_tag_votes→get_tag_review_queue_pairs→ question-feed-tag-tier pipeline already handles both tags mechanically today, proven by live production data: 1,185 UNRESOLVED "AI-Generated" rows and 7 "external-ip"CardTagVoterows already exist and already flow through the same consensus gate as any other tag. No new model, endpoint, or consensus mechanism is required. What's actually missing:ATTRIBUTE_CHIP_TAG_NAMES(backend) /ALL_ATTRIBUTE_CHIPS(frontend) — so neither gets a ring/rail chip or a prioritized question-feed slot; a voter only reaches "AI-Generated" via the passive, unpromptedTagVotePickergrid, and only reaches "external-ip" via the one-shot no-match-reason strip.NOT_APPLICABLEcounter-vote path (a deliberate prior decision,reason_tags.py:90-93) — making it "votable... for custom cards" the way AI-art already partially is would mean either adding it to the direct-access roster (reopening that no-negation decision) or building the What's That: contextual report button — flag an image for moderators against relevant tags, with optional per-user hide #714 context-aware report affordance on top of the existing reason-chip.Why AI-art voting is load-bearing, not merely parity
#278 (open) confirms:
AI_ART_RESCANNABLE_SKIP_REASONS = {no-evidence, incomplete-evidence}(local_detect_ai_art.py:139) —no-marker-hitis deliberately excluded. Once the detector scans a card's evidence and finds no marker, that negative conclusion is permanent; no automated path revisits it (#278's own body: "no-marker-hit negatives are permanently gated", one concrete flip already found: card 214416). Combined with #367's ~91%-never-checked figure verified above, for the overwhelming majority of the catalog there is either no machine verdict at all, or a stale one the machine cannot itself correct. A human vote is the only correction path for a detector false negative — this makes AI-art human voting a repair mechanism for a channel that structurally cannot repair itself, not a nice-to-have UI parity item.The two-mode distinction (differs between the two attributes)
TagVotePickergrid.set_type/security_stampto read (confirmed: those fields don't exist anywhere inmodels.py). The human vote is the primary and sole signal, always cold — but the current wiring (the no-match-reason strip) only reaches a card after an unrelated prior vote, is single-tap, and has no revisit path.This maps onto the difficulty-lane model recorded in #716 (easy = confirm an uncontested machine vote; medium = break a tie between competing votes; hard = answer cold), derived purely from data state, no telemetry needed. #619 is the sibling case for frame-style/bleed-edge chips (145,419 evidence-derivable votes sitting uncast) — same "channel exists, coverage is the gap" shape as AI-art, cross-referenced not duplicated.
Two open decisions (for the owner — not answered here)
Vote or report? Should AI-art determination be a consensus vote, a moderator report (the affordance proposed but not yet built in What's That: contextual report button — flag an image for moderators against relevant tags, with optional per-user hide #714, part of epic [Epic] What's That Card: full-page repass (layout, question content, feed intelligence) #704), or both? The closest existing precedent is "Marked as proxy" tag + marker-absence moderation flags (catalog compliance scan) #291 ("Marked as proxy" tag + marker-absence moderation flags): a machine detector casts a positive tag, human-confirmable via ordinary consensus (same generic gate, no special moderator requirement on the vote itself) — but the negative/absence case routes to a separate, batched moderator-review flag surface, not the vote system. That precedent already suggests an answer in the direction of "both, split by direction" — vote for confirmation, a separate flag surface for escalation — but the owner should rule on it explicitly. What's That: contextual report button — flag an image for moderators against relevant tags, with optional per-user hide #714 itself is scoped only as a record (owner's own words, quoted there): "a card that is custom art could be universes within or beyond (art:external-ip)... there should be a report button... that lets a user flag for mods among relevant tags" — i.e. the owner has already asked for exactly this kind of context-aware report affordance, evaluated against the existing
ReportCardPanel.tsx(fixed NSFW/Low-quality/Wrong-info/Broken-image/Other reasons, anonymous-id-scoped, 10/day rate limit) andcardpicker/moderation.pymoderator queue, both of which already exist and were assessed as the natural extension point (not greenfield).Threshold. The standard consensus gate is
PRINTING_TAG_MIN_SHARE = 0.6(60%),PRINTING_TAG_MIN_VOTES = 2(MPCAutofill/settings.py:65-66), plus the hard human-backed gate (vote_consensus.is_human_backed_source, line 55-56 — no volume ofDEDUCTION/OCR/FEDERATED/IMPLICITvotes can resolve a tag alone). This applies uniformly today to "AI-Generated" (moderation_class=standard, confirmed live — no AI-art-specific higher bar or mandatory moderator co-sign exists in code, per the owner's own 2026-07-21 reversal of its earlierSENSITIVEmarking). A false "this is AI-generated" resolution is a public accusation about a named human artist's work — the reputational cost of a wrong resolution is asymmetric, unlike e.g. border-colour, where a wrong call is merely a wrong call. Should this tag carry a higher threshold, a mandatory moderator review before the resolution becomes visible/actionable, or the standard gate as-is? Stated neutrally; the owner decides.Cross-references (not modified by this issue)
#367, #278, #291, #437, #505, #635, #714, #716, #619
Owner decision (2026-08-05): context-dependent surfacing
Owner, verbatim: "I think I want them to surface context dependant. external-ip, ai art can show when we want to ask about custom art."
This settles part of decision 1 above (vote or report?) in one direction: neither tag joins the fixed 11-tag
ATTRIBUTE_CHIP_TAG_NAMESroster surfaced unconditionally on every card. Instead both surface conditionally, when the feed/rail is asking about custom art specifically — a third surfacing mode alongside the two already documented in V2 above (the curated chip ring, and the generic always-availableTagVotePicker/question-feed-tag-tier). Not yet specified by the owner: the exact trigger condition ("when we want to ask about custom art" — plausibly acustom-arttag already present on the card, or a card with no resolved printing at all) or which of the two existing generic mechanisms this context-dependent mode attaches to. Both remain open.Owner, also verbatim: "They can be sensitive, but no consumer built for sensitive yet."
V1 finding (verified against current code) — refutes this claim as stated. A SENSITIVE-class consumer already exists and is live, not absent.
tag_consensus.resolve_tagpassesrequire_privileged=tag.moderation_class == TagModerationClass.SENSITIVEintovote_consensus.resolve_weighted_consensus(tag_consensus.py:73). When true, a crowd consensus that would otherwise resolve is instead parked asPENDING_PRIVILEGED(vote_consensus.py:533-534) unless a moderator/admin vote backs the winning side — persisted asTagVoteStatus.PENDING_APPROVAL(tag_consensus.py:140-141), withcard.tagsdeliberately left untouched so a pending tag has zero search consequences until a moderator co-signs. A moderator-facing approval queue is already wired too:get_pending_approval_queue_pairs(views.py:1992-2003), and SENSITIVE tags additionally block implicit votes while pending (views.py:1711). Tested end-to-end (tests/test_moderation_gate.py,tests/test_sensitive_tags.py).What's actually true and load-bearing for this issue's open threshold decision (item 2 above): neither "AI-Generated" nor "external-ip" is currently marked SENSITIVE. "AI-Generated" is explicitly
STANDARD(sensitive_tags.py:46-60,FORMERLY_SENSITIVE_TAG_NAMES, reverted from #263's brief SENSITIVE marking by the 2026-07-21 owner decision already cited in V3 above), and "external-ip" was never listed inSENSITIVE_TAGSat all. So the gap is narrower than the owner's framing suggests: the SENSITIVE consensus gate itself is built and working; what does NOT exist is any UI/report-affordance CONSUMER of a SENSITIVE tag (the surface #714 describes), and no decision has marked either of these two tags SENSITIVE in the first place. If this issue's threshold decision lands on "raise the bar for AI-art", the consensus-side change is one line (add the tag name tosensitive_tags.SENSITIVE_TAGS); the report-button UI consumer described in #714 is the piece that would need to be built from scratch.Threshold decision: resolved - standard consensus gate, no raised bar (2026-08-06)
Answers this issue's open decision 2 (threshold). Both AI-Generated and external-ip stay STANDARD class (see #714 for the full moderation-class ruling and code verification - not duplicated here); the ruling's reasoning: ordinary human votes are sufficient for these two attributes, and absent immediate human consensus the tag going CONTESTED is the system working as intended, not a failure to be pre-empted with a privileged co-sign.
Use the standard consensus gate for both - verified:
PRINTING_TAG_MIN_SHARE = 0.6(MPCAutofill/MPCAutofill/settings.py:66),PRINTING_TAG_MIN_VOTES = 2(:65), plus the unconditionalhas_human_backedrequirement (vote_consensus.py:532,winner["has_human_backed"]) - the same gate every other STANDARD tag resolves through, applied viatag_consensus.resolve_tag(tag_consensus.py:35-78) withrequire_privileged=Falsefor a STANDARD tag'smoderation_class(:73). No raised bar, no mandatory moderator co-sign, for either tag.Consequence - context-dependent surfacing is now principled, not arbitrary. Per the 2026-08-05 owner decision above, both tags surface conditionally ("when we want to ask about custom art") as ordinary votable chips through the existing generic
TagVotePicker/question-feed-tag-tier mechanism (V2 above) - the same STANDARD consensus path as any other tag. This pairs with #714's SENSITIVE-set ruling: SENSITIVE narrows to exactly NSFW and appropriate-bleed, which surface instead through #714's always-visible report affordance with privileged co-sign. STANDARD attributes (this issue) surface context-dependently as ordinary votable chips; SENSITIVE attributes (#714) surface through the report affordance and carry privileged co-sign - two surfaces for two moderation classes, not an arbitrary split.