A validated thread id. Construction from user- or externally-supplied
input goes through ThreadId::new, which rejects anything that is not a
safe single shell token (see validate_thread_id). That invariant is what
lets recommended-command breadcrumbs interpolate a thread id bare — there
is no whitespace or shell metacharacter to quote, by construction.
Rejection from ThreadId::new / validate_thread_id. Its Display is
a clear, actionable CLI message naming the offending input and suggesting a
valid rename.
The single rule for thread-id validity. A valid id is non-empty, made up
only of the safe slug set (ASCII alphanumerics plus _ - . / @ : + =), has
no .. segment, and does not begin with /. This is deliberately the same
safe set the shell quoting rule treats as needing no quoting, so a valid
thread id is always a single shell token: feature/x, v1.2, my-thread,
and team@scope are accepted; spaces, quotes, ;, |, $, &, *,
backticks, and newlines are rejected. Thread ids flow into worktree paths,
so .. and a leading / are rejected to keep them in-tree. A leading -
is also rejected: it is in the safe set (for my-thread) but a breadcrumb
like heddle land --thread -foo parses -foo as a flag, not the value.