···11+# 0019 — Editor @mentions and Bluesky mention notifications
22+33+- **Status:** Accepted
44+- **Date:** 2026-06-12
55+- **Scope:** the `skypress/mention` rich-text format, the companion `app.bsky.feed.post`
66+ (Decision 0005 / 0013 publish flow), `collectMentions`/`buildBskyPost`
77+ (`src/lib/publish/records.ts`), the reader render + sanitizer, and `PublishPanel`
88+99+## Context
1010+1111+Writers want to mention atproto users in an article and have those users notified.
1212+Bluesky only generates a mention notification when it indexes an `app.bsky.feed.post`
1313+whose `facets` contain `app.bsky.richtext.facet#mention`; the AppView indexes per
1414+collection and never scans custom records (`site.standard.document`) for facets.
1515+1616+## Decision
1717+1818+- **Notifications ride the companion post.** Publish appends a `cc @a @b` line (plus the
1919+ writer's lede) to the `app.bsky.feed.post` body and attaches a `#mention` facet per
2020+ handle. A facet on the document record would be inert.
2121+- **Storage is inline.** Mentions are a registered `skypress/mention` rich-text format
2222+ serializing to `<a class="skypress-mention" href="https://bsky.app/profile/{handle}"
2323+ data-did="{did}">@{handle}</a>`. This is the single source of truth; `collectMentions`
2424+ derives the cc list and the document's flat `mentions` list from it at publish.
2525+- **Link target is Bluesky.** Correct for every mentionable user; no SkyPress-profile 404s.
2626+- **Interop is the inline anchors + a flat `mentions` list** on
2727+ `blog.skypress.content.gutenberg`, not a byte-offset facet (no flat-text host fits our
2828+ block content model). The reader renders the mention from the inline anchor; the
2929+ sanitizer keeps `class` + `href` and strips `data-did`.
3030+- **Body only.** Lede-field mentions, edit-time notifications, and SkyPress-internal
3131+ mention links are out of scope.
3232+3333+## Consequences
3434+3535+- The post body can approach Bluesky's 300-grapheme limit; PublishPanel shows a live count
3636+ and blocks publish over the limit, with a backstop guard in `buildBskyPost`.
3737+- Adding the format means a `render.ts` fidelity assertion and a sanitizer lock test.
3838+- DIDs are resolved once, at pick time, and baked into the block — publish never
3939+ re-resolves.