Adds the Swarm-mode counterpart to ComposeExecutor: updates a Swarm
service's image directly through the Docker SDK (ServiceInspectWithRaw +
ServiceUpdate with QueryRegistry=true so floating tags resolve to a fresh
digest), gated by the same Refused guard used in Compose mode. Satisfies
the existing Executor interface unchanged, so Queue needs no changes.
Verified the Docker SDK v28.5.2+incompatible ServiceList signature via go doc
before implementing: the options type is swarm.ServiceListOptions, not
types.ServiceListOptions as the brief assumed. Everything else (Service.ID,
Service.Spec via embedded Annotations for Name/Labels, TaskTemplate.ContainerSpec.Image)
matched the brief exactly.
Reuses discovery.ImagesMatch and discovery.HasOptIn rather than duplicating
matching/opt-in logic. Opt-in label is read from the service spec's own
labels since Swarm services have no local compose file to anchor a
STACKS_ROOT path check against (unlike Compose mode).