fix(tag-prefix): drop artist/meta/rating from KNOWN_KINDS — artist tags retired in FC-2d-vii-c (provenance is its own axis), meta/rating retired by operator 2026-05-26. User-typeable prefixes now just character/fandom/series. Frontend placeholder + icon map + client-side mirror updated; new test confirms retired prefixes parse as literal text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,15 +10,18 @@ KNOWN_KINDS keeps its colon as literal text.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
# Kinds the user can type as a prefix at the input boundary.
|
# Kinds the user can type as a prefix at the input boundary.
|
||||||
# `general` is intentionally excluded — it's the default for un-prefixed
|
# Exclusions:
|
||||||
# input. `archive` and `post` are system-managed and never user-typed.
|
# - `general` is the default for un-prefixed input (never typed as prefix)
|
||||||
|
# - `archive`, `post` are system-managed
|
||||||
|
# - `artist` was retired in FC-2d-vii-c — artists are first-class
|
||||||
|
# entities (Artist row + ImageRecord.artist_id), browsed via the
|
||||||
|
# provenance axis rather than as tags. See project_provenance_separation.
|
||||||
|
# - `meta`, `rating` retired as user-typeable per operator 2026-05-26 —
|
||||||
|
# content classification only needs character/fandom/series.
|
||||||
KNOWN_KINDS: frozenset[str] = frozenset({
|
KNOWN_KINDS: frozenset[str] = frozenset({
|
||||||
"artist",
|
|
||||||
"character",
|
"character",
|
||||||
"fandom",
|
"fandom",
|
||||||
"series",
|
"series",
|
||||||
"meta",
|
|
||||||
"rating",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="fc-tag-autocomplete">
|
<div class="fc-tag-autocomplete">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="query"
|
v-model="query"
|
||||||
placeholder="Add tag (or kind:name — artist/character/fandom/series/meta/rating)"
|
placeholder="Add tag (or kind:name — character/fandom/series)"
|
||||||
density="compact" hide-details
|
density="compact" hide-details
|
||||||
@keydown.down.prevent="moveHighlight(1)"
|
@keydown.down.prevent="moveHighlight(1)"
|
||||||
@keydown.up.prevent="moveHighlight(-1)"
|
@keydown.up.prevent="moveHighlight(-1)"
|
||||||
@@ -71,12 +71,11 @@ const fandomDialog = ref(false)
|
|||||||
let pendingNewName = null
|
let pendingNewName = null
|
||||||
|
|
||||||
const KNOWN_KINDS = new Set([
|
const KNOWN_KINDS = new Set([
|
||||||
'artist', 'character', 'fandom', 'series', 'meta', 'rating',
|
'character', 'fandom', 'series',
|
||||||
])
|
])
|
||||||
const KIND_ICONS = {
|
const KIND_ICONS = {
|
||||||
general: 'mdi-tag', artist: 'mdi-palette', character: 'mdi-account-circle',
|
general: 'mdi-tag', character: 'mdi-account-circle',
|
||||||
fandom: 'mdi-book-open-page-variant', series: 'mdi-bookshelf',
|
fandom: 'mdi-book-open-page-variant', series: 'mdi-bookshelf',
|
||||||
meta: 'mdi-cog-outline', rating: 'mdi-shield-check-outline',
|
|
||||||
}
|
}
|
||||||
function iconFor (k) { return KIND_ICONS[k] || 'mdi-tag' }
|
function iconFor (k) { return KIND_ICONS[k] || 'mdi-tag' }
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ from backend.app.utils.tag_prefix import KNOWN_KINDS, parse_kind_prefix
|
|||||||
|
|
||||||
|
|
||||||
def test_recognized_kinds_match_user_input_set():
|
def test_recognized_kinds_match_user_input_set():
|
||||||
# System-managed kinds (archive, post) and the default (general)
|
# Excluded: archive + post (system-managed), general (default for
|
||||||
# are intentionally excluded — the user can't type those at the
|
# un-prefixed input), artist (retired in FC-2d-vii-c — first-class
|
||||||
# input boundary.
|
# entities now), meta + rating (retired as user-typeable per
|
||||||
|
# operator 2026-05-26).
|
||||||
assert KNOWN_KINDS == frozenset({
|
assert KNOWN_KINDS == frozenset({
|
||||||
"artist", "character", "fandom", "series", "meta", "rating",
|
"character", "fandom", "series",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -30,8 +31,17 @@ def test_unknown_prefix_kept_as_literal():
|
|||||||
assert parse_kind_prefix("http://example.com") == (None, "http://example.com")
|
assert parse_kind_prefix("http://example.com") == (None, "http://example.com")
|
||||||
|
|
||||||
|
|
||||||
|
def test_retired_prefixes_kept_as_literal():
|
||||||
|
# `artist:`, `meta:`, `rating:` are no longer recognized — they
|
||||||
|
# parse as literal text so the operator's input is preserved (and
|
||||||
|
# serves as a nudge to use the appropriate dedicated UI instead).
|
||||||
|
assert parse_kind_prefix("artist:Eric") == (None, "artist:Eric")
|
||||||
|
assert parse_kind_prefix("meta:wide") == (None, "meta:wide")
|
||||||
|
assert parse_kind_prefix("rating:safe") == (None, "rating:safe")
|
||||||
|
|
||||||
|
|
||||||
def test_whitespace_stripped():
|
def test_whitespace_stripped():
|
||||||
assert parse_kind_prefix("artist: Eric ") == ("artist", "Eric")
|
assert parse_kind_prefix("series: Bleach ") == ("series", "Bleach")
|
||||||
assert parse_kind_prefix(" sunset ") == (None, "sunset")
|
assert parse_kind_prefix(" sunset ") == (None, "sunset")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user