Batched actor-by-id resolver.
Joins actor ⨝ account so callers see (username, display_name) for
each actor row. The byline / owner-column / grantor surfaces stamp an
actor id, so resolving "who is this actor?" lands the human label in
one round trip.
Accounts may host multiple actors (multi-actor shipped in v0.55.0).
The inner join still resolves one row per actor — actor.account_id
is NOT NULL so every actor has exactly one account.
Info-leak posture (see auth/actor_lookup_action_specs.ts §audit):
- Row shape omits
account_id — the join is control-plane,
not wire-visible. - Hard-deleted actors (or account-cascade-orphaned rows) drop out
silently — indistinguishable from never-existed (no tombstone
oracle).
- No
created_at / updated_at projected (timing-oracle avoidance). - Response order is unspecified —
WHERE id = ANY(...) returns
index-scan order in practice but callers must not depend on it.
Caller is responsible for capping ids.length — the SQL itself does
not enforce a bound; the action-spec layer surfaces invalid_params
via ACTOR_LOOKUP_IDS_MAX.