One conflict, in the category catalogue: this branch added a virt category for
the sandboxed container runtimes, the other did not.
Keeping our side alone would have closed the conflict and broken the build. The
same merge introduces a test requiring every category to declare a hanzi label,
unique among its siblings, and virt predates that rule and carries none. It now
declares 沙箱, checked against its ten siblings for a collision.
Adds a `hanzi` field to every node in meta/categories.yml plus a lint that
keeps it there. The labels are compact two-character fragments that compose
into a display path (sys-ctl-cln reads as 系统·控制·清理), so a category tree
that is otherwise only navigable by dash-joined role prefixes gains a short
human-facing form.
The lint asserts presence on every node and uniqueness among siblings, not
across the whole tree: the labels are path fragments and repeat across
branches on purpose (web-svc and svc both carry 服务), so sibling scope is
what actually guarantees a unique display path.
test_paths.py gained `hanzi` in its attribute skip list; without it the new
key reads as a sub-category and the test demands roles/web-hanzi,
roles/svc-hanzi and friends.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
categories.yml moves from roles/ to the repository-root meta/, joining packages.yml. utils/roles/categories.py owns its location plus the tree-walking helpers that were stranded in entity/name.py, so no caller spells the path or duplicates the lookup.
meta/distros.yml becomes the SPOT for every distribution: os_family, Galaxy platform name, dev-environment base image, and the pkgmgr/pkgmgr-virgin/environment reference templates. It replaces seven scattered copies (DISTRO_FAMILY, VALID_DISTROS, _DISTROS, _CANONICAL_PLATFORMS, two shell arrays, a Makefile literal). INFINITO_DISTROS is no longer a static default but generated by utils/env/handlers/infinito/distros.py; the shell consumers read it through the env layer.
Drops dead CLI surface found along the way: --categories-file and --roles-dir on the provision CLI plus three unused parameters of generate_dynamic_inventory, and the positional roles_file of the invokable categories command.
tests/lint/repository/test_distros_spot.py pins the copies that cannot call the SPOT (workflow literals, docs table) and forbids composing a pkgmgr reference outside it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>