Documents / Issues / issue-cc61d038cf8f
Renaming a tag onto an existing key is rejected, so two tags cannot be merged
Vocabularies drift and need consolidating; the registry can only grow.
Class: missing · Severity: material
Flow: arch-ccfcceeb35eb · Step: consolidating two tags
Question: None · Frequency: unknown
Finding¶
Renaming a tag onto an existing key is rejected, so two tags cannot be merged.
What happens today¶
tag_service.py:69-70 raises TagAlreadyExistsError. The only path is tag rm --force on one (losing the classification) then re-tagging by hand.
Impact¶
Vocabularies drift and need consolidating; the registry can only grow. Coverage checklist item 'merge/deduplicate two records' is unmet.
Proposed default¶
Allow rename-onto-existing as an explicit merge (tag rename old new --merge), rewriting documents and deduplicating keys.
Resolution¶
FIXED 2026-07-29, as proposed. tag rename old new --merge folds old into an existing new: every document carrying old gets new, old leaves the registry, and the ids rewritten are returned so a bulk edit says what it touched. Without the flag the refusal stands, and its message now names --merge — a merge discards one of the two descriptions, which is not what someone fixing a typo means. THE CASE THAT MAKES IT MORE THAN A RELAXED CHECK: a document carrying both tags. The existing rewrite maps old->new positionally, so that document would have ended up with ('authn', 'authn'). Deduped with dict.fromkeys, which preserves order. Verified by reverting to the naive rewrite: the dedup test fails. new's description is kept, not old's — new is the tag being kept, so its wording is the one people chose for it. The merge inherits the staleness rule established by issue-9ed4905e0db8: rewritten documents keep their updated. A bulk classification edit across a whole vocabulary is the largest version of exactly the edit that rule exists for, and it is pinned by its own test. --merge onto a key that does not exist behaves as a plain rename rather than becoming a second code path; also pinned.
Actors affected¶
- repository maintainer
Evidence¶
src/docir/modules/tags/application/services/tag_service.py:69-70
Migrated from the discovery gap register (GAP-028); the register itself now lives in this store.