natsmsg: detach the dead-letter capture from the caller's cancellation (COR-1487)
Commit

DeadLetter bounded its publish with context.WithTimeout(ctx, dlqPublishTimeout), which caps the deadline AND inherits the parent's cancellation. It is the give-up capture a consumer runs before dropping a poison message, so the jobs most likely to reach it are exactly the ones whose ctx is already dead — a SIGTERM landing mid-handler, a cancelled upstream call. The publish context was born dead: PublishMsg failed with context.Canceled before touching the network and the 15s bound never applied. Seen in staging and prod.
The function's doc already promised the fixed behaviour ("bounded by dlqPublishTimeout regardless of ctx's deadline"); the reasoning behind it considered only whether ctx carried a deadline and missed that it can still be cancelled. So this restores a documented contract rather than changing one, and the doc now says cancellation explicitly — along with the cost, which is that against an unreachable broker the call blocks for the bound instead of failing at once, and a capture racing shutdown can outlast the join window.
ctx's values still propagate, so the handler's span carries into the capture.
Tests both bite: reverting the one-line fix fails the natsmsg unit test and the real-broker one. The shared fakeDLQPublisher now refuses a dead context the way a real client does — until now it ignored ctx entirely, so a cancellation test written against it would have passed against the broken code.
Deliberately unchanged, with signposts so the asymmetry is not "fixed" later: Publisher.Publish keeps inheriting cancellation (a producer wants to abandon and fall back to its outbox/nak/5xx path), and jsconsumer's settleCtx stays because it is what detaches the DoubleAck, which this does not cover.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com Claude-Session: https://claude.ai/code/session_018WgK5KJE13UKgoUPQwvM1o Entire-Checkpoint: a7a8d07ef047