fix(materialize): facet Lemmy spoilers and escape-hidden bare URLs master
Two gaps in the markdown→richtext converter surfaced on real !comicstrips@lemmy.world posts, both storing content that renders wrong in every Coves client. Lemmy spells spoilers with the markdown-it-container syntax, which is not CommonMark: ::: spoiler Bonus Panel  ::: goldmark left those lines as paragraph text, so the fence markers leaked into the canonical plaintext AND the hidden content rendered revealed — "Old [Mr Lovenstein]" stored three literal ":::" blocks wrapping a 1.5KB transcript. social.coves.richtext.facet already defines #spoiler with an optional `reason` matching the container title, so this is a missing parser, not a lexicon change. Separately, Linkify runs over the SOURCE bytes during inline parsing while backslash escapes only resolve at render time (unescapeMarkdown). PieFed escapes punctuation on the way out — `https\://piefed.social/…` — so "Jucika - Courteous" stored text that reads as a URL and carried no link facet at all. Scanning the finished plaintext catches those at the point where offsets are already final. Changes: - internal/materialize/spoiler.go: goldmark block parser for `::: spoiler` containers, as a child-bearing block so inner markdown still parses. Closes on an all-colon line at least as long as the opener, or at end of input; nesting falls out of goldmark's innermost-first close order. - richtext.go: register the parser; add spoilerFeature (reason truncated to the lexicon's 32 graphemes / 128 bytes rather than dropped, since a clipped label still names what is hidden) and renderSpoiler, which keeps the title out of the plaintext. - richtext.go: bareURLSpans post-pass over rendered plaintext, skipping ranges already covered by link/code/codeBlock so an author's chosen destination wins and literal code stays inert. trimURLTail applies GFM's extended-autolink tail rules. Overlap resolution is a single forward cursor over merged intervals — interleaved code spans and URLs would otherwise go quadratic on ~100KB of federated input. - 14 tests: the PieFed escaped-colon case, tail trimming, code-must-not- linkify, titleless/unclosed containers, adversarial nesting bounds, and an end-to-end materializer test putting a #spoiler with reason through real lexicon validation. Existing records are deliberately NOT backfilled: deterministic rkeys mean these take effect for newly bridged posts and for existing posts whose upstream edit triggers re-materialization. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>