Fix etcd plain→TLS container recreation leaking snapshot on unclean shutdown
Enabling distributed runners flips embedded etcd from plaintext to TLS (the mTLS setup in server.go), which forces the etcd component to recreate its containerd container on the next restart. That recreation wedges the server whenever the previous shutdown wasn't clean.
Embedded etcd runs as its own containerd task, independent of the miren process, so it survives a server restart or crash. On the next boot the component compares the requested TLS state against the saved state in etcd/etcd-state.json, sees false → true, and tears down and recreates the container. In components/base/base.go stopTask (around lines 251-254), if task.Kill(SIGTERM) returns "not found" (which happens when the prior server died without cleaning up, so the init process is already gone), it returns early and skips task.Delete. The task record stays registered, so container.Delete fails with "cannot delete running task," so WithSnapshotCleanup never runs, so miren-etcd-snapshot leaks, so creating the new TLS etcd fails with "snapshot already exists" and the server dies. There's no independent snapshot-cleanup fallback, so the node stays wedged until someone manually clears the snapshot, and the labs escape hatch doesn't cleanly rescue an already-wedged node.
Reproduced end to end in a dev standalone cluster:
etcd container config changed, recreating container │ previous_tls: false requested_tls: true
failed to send SIGTERM to etcd task │ error: "process already finished: not found"
failed to delete existing container during cleanup │ error: "cannot delete running task miren-etcd: failed precondition"
failed to start etcd component │ error: "snapshot \"miren-etcd-snapshot\": already exists"
A graceful shutdown of the previous server avoids this (Stop tears down task + container + snapshot cleanly). The danger is a fleet-wide default-on flip, where unclean shutdowns (crashes, OOM, host reboots, restarts mid-upgrade) are exactly what we have to survive. Garden actually did make this plain→TLS transition, but via a clean miren upgrade / etcd reboot, so the graceful Stop path tore everything down in order and it never hit the leak. The unclean-shutdown path is the one that wedges.
Fix: in stopTask, don't return when Kill fails; fall through and still task.Delete, ideally task.Delete(ctx, containerd.WithProcessKill) for force-kill-and-remove in one shot. Add a snapshot-remove fallback so a failed container delete can't strand the snapshot. Cover it with a regression test that leaves a registered etcd task from a plaintext boot and asserts the TLS recreate succeeds.