Gateway consumer resolves raw channel senders to merged tenant
identities for DMs (e.g. Telegram "386246614" → tenant "viettx"). The
previous ActorIDFromContext unconditionally preferred SenderID, which
broke cross-channel ownership continuity — a user's skills / audit
records published from Telegram would carry the channel-specific
numeric ID instead of the stable tenant identity.
New resolution rule:
- group: / guild: scope → SenderID wins (UserID is the group namespace)
- DM / HTTP / cron → UserID wins (already merge-aware from consumer)
- SenderID fallback → only when UserID is empty
Unit coverage updated: 8 sub-tests covering group, guild, DM-merge,
DM-unmerged, HTTP, empty, sender-only, delimited-sender scenarios.
Introduce ActorIDFromContext(ctx) that returns SenderID if set, else
falls back to UserID. Separates ACTOR (acting principal) from SCOPE
(memory/namespace) semantics so permission checks, audit trails, and
ownership fields can migrate off the scope-coupled UserIDFromContext.
No callers yet; migration of existing sites is a follow-up.