validate_icon stripped the whole value before validating, so
`emoji:🐕 `, `emoji:🐕\n` and `emoji:🐕\x1c` were silently accepted as
`emoji:🐕`. Validate the raw namespace, name and emoji payload; a
whitespace-only value still clears the icon. Padded keys such as
`lucide:dog ` are now rejected naming `icon` (NQ-241-3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add core/icons.py as the one place icon keys are validated and
normalized. A definition icon is now stored as lucide:<name>,
fa:<name> or emoji:<payload> (at most 8 code points, no whitespace,
control characters or markup). Create and edit validate strictly and
store the canonical form; a legacy bare name is stored as
lucide:<name>, and None still clears the icon.
Reads normalize leniently so existing bare-name rows need no data
migration: the admin quest-definition response and the panel snapshot's
per-instance icon both return the namespaced form.
Task 707994b7.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>