A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

Select the types of activity you want to include in your feed.

Record decision 0019: editor mentions + notifications

+39
+39
docs/decisions/0019-editor-mentions.md
··· 1 + # 0019 — Editor @mentions and Bluesky mention notifications 2 + 3 + - **Status:** Accepted 4 + - **Date:** 2026-06-12 5 + - **Scope:** the `skypress/mention` rich-text format, the companion `app.bsky.feed.post` 6 + (Decision 0005 / 0013 publish flow), `collectMentions`/`buildBskyPost` 7 + (`src/lib/publish/records.ts`), the reader render + sanitizer, and `PublishPanel` 8 + 9 + ## Context 10 + 11 + Writers want to mention atproto users in an article and have those users notified. 12 + Bluesky only generates a mention notification when it indexes an `app.bsky.feed.post` 13 + whose `facets` contain `app.bsky.richtext.facet#mention`; the AppView indexes per 14 + collection and never scans custom records (`site.standard.document`) for facets. 15 + 16 + ## Decision 17 + 18 + - **Notifications ride the companion post.** Publish appends a `cc @a @b` line (plus the 19 + writer's lede) to the `app.bsky.feed.post` body and attaches a `#mention` facet per 20 + handle. A facet on the document record would be inert. 21 + - **Storage is inline.** Mentions are a registered `skypress/mention` rich-text format 22 + serializing to `<a class="skypress-mention" href="https://bsky.app/profile/{handle}" 23 + data-did="{did}">@{handle}</a>`. This is the single source of truth; `collectMentions` 24 + derives the cc list and the document's flat `mentions` list from it at publish. 25 + - **Link target is Bluesky.** Correct for every mentionable user; no SkyPress-profile 404s. 26 + - **Interop is the inline anchors + a flat `mentions` list** on 27 + `blog.skypress.content.gutenberg`, not a byte-offset facet (no flat-text host fits our 28 + block content model). The reader renders the mention from the inline anchor; the 29 + sanitizer keeps `class` + `href` and strips `data-did`. 30 + - **Body only.** Lede-field mentions, edit-time notifications, and SkyPress-internal 31 + mention links are out of scope. 32 + 33 + ## Consequences 34 + 35 + - The post body can approach Bluesky's 300-grapheme limit; PublishPanel shows a live count 36 + and blocks publish over the limit, with a backstop guard in `buildBskyPost`. 37 + - Adding the format means a `render.ts` fidelity assertion and a sanitizer lock test. 38 + - DIDs are resolved once, at pick time, and baked into the block — publish never 39 + re-resolves.