Skip to content

feat(activitypub,mastodon-api): accept EmojiReact and undo reactions - #581

Merged
davidwkeith merged 2 commits into
mainfrom
claude/trusting-hawking-3eawag
Nov 1, 2026
Merged

davidwkeith merged 2 commits into
mainfrom
claude/trusting-hawking-3eawag

Conversation

@davidwkeith

@davidwkeith davidwkeith commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

Inbound emoji reactions, the second of the SiliconBeest interop extensions (after inbound Move in #579).

  • EmojiReact (Pleroma/Akkoma) used to fall through to default and was dropped. It is now stored and forwarded exactly like a Like, counts toward favourites_count, and appears as a favourite notification. Mastodon's API has no reaction notification type, so the emoji itself isn't shown, the same downgrade Misskey makes when it sends its own reactions to Mastodon peers as Like.
  • Undo(Like) / Undo(EmojiReact): this was a gap even for plain Like. #onUndo only handled Follow, so un-favouriting on a remote server never removed the favourite here. An Undo naming a Like or EmojiReact (embedded with an id, or by bare IRI) now deletes that stored row, but only when the row's actor is the Undo's actor (already bound to the HTTP signer, and matched whether the stored actor is an IRI or an embedded { id }), so no one can retract someone else's reaction. A bare-IRI Undo still never touches followers, and Undo(Announce) is unchanged.
  • No tombstone is kept: redelivery of the same Like id within the 7-day seen dedup window is dropped, but a reaction that arrives after its own Undo, or after that window, is stored again. The spec documents this.

Not in this PR: sending EmojiReact. The Mastodon client API has no way to request one.

Packages affected

@dwk/activitypub, @dwk/mastodon-api

Checklist

  • Read the relevant spec(s) under spec/packages/ and updated them if
    behaviour changed: spec/packages/activitypub.md (inbound list and a reactions bullet), spec/packages/mastodon-api.md (Notification mapping)
  • Added/updated colocated tests (src/*.test.ts). In activitypub mastodon-api.test.ts: an EmojiReact counts as a favourite and is notified; Undo of an embedded or bare-id Like and EmojiReact drops the count back to 0; a reaction stored with an embedded actor object can be undone; a forged Undo from another actor leaves it. In mastodon-api entities.test.ts: EmojiReact maps to a favourite notification.
  • Ran the local CI gate: pnpm lint && pnpm format:check && pnpm typecheck && pnpm build && pnpm test (3322 passed)
  • Added a changeset (pnpm changeset) if this touches a publishable
    package (minor for activitypub, patch for mastodon-api)
  • Updated catalog.json / conformance/status.json if this adds a new
    mountable worker or changes conformance status: not applicable, no new worker and no change to conformance status

🤖 Generated with Claude Code

https://claude.ai/code/session_015LRGjmHE99WESN5HBERxSj

Store Pleroma/Akkoma EmojiReact like a Like, so it counts toward
favourites_count and renders as a favourite notification. Handle Undo of
a Like or EmojiReact (embedded or by bare id) by deleting the stored
reaction, only when the Undo's actor authored it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015LRGjmHE99WESN5HBERxSj

Copy link
Copy Markdown
Owner Author

Automated review against CONTRIBUTING.md and the diff.

Conforms: the title and commit use Conventional Commits with the right scope. The body keeps the Summary, Packages affected and Checklist headings. The changeset covers both packages. The specs, README and package CLAUDE.md are updated. The tests cover the forged-Undo case. CI was still running when I checked, so I haven't confirmed the local gate.

Suggestions (none blocking):

  1. object.ts #undoReaction filters on json_extract(json, '$.type') and json_extract(json, '$.actor'). The inbox table already has a type column, which #storeInbox fills in. Using it avoids a JSON parse per row. For actor, json_extract is fine if there's no column, but it only matches when actor is a string. An inbound Like with an embedded actor object would never be retractable. Either confirm that actor is normalized before storage, or use the same normalization on both sides.
  2. After an Undo, the row is gone, but INSERT OR IGNORE on the activity id means a replayed or late-delivered original Like will be stored again. A tombstone, or a check on Undo ordering, would stop a stale re-delivery from resurrecting a favourite. Please at least mention this in the spec if it's intended.
  3. The #onUndo branch undone === undefined || REACTION_TYPES.includes(undone) also routes an embedded object without a type into #undoReaction. That's harmless because only reaction rows are deleted. A short comment saying so would help, since the change just above it rejects non-typed Follows deliberately.
  4. The mastodon-api changeset is minor, but the package change is a one-line type mapping. That's acceptable, but patch may be more accurate for it.
  5. npx prettier --check flags object.ts and object.test.ts on this branch. I ran a possibly different prettier version than CI, so please confirm pnpm format:check is clean.

Generated by Claude Code

Match the reaction's actor whether stored as an IRI or an embedded
{ id } object, filter on the inbox `type` column, explain why untyped
Undo objects may try the reaction path, document that no tombstone is
kept, and split the mastodon-api changeset as a patch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_015LRGjmHE99WESN5HBERxSj
@davidwkeith
davidwkeith merged commit 12925db into main Nov 1, 2026
8 checks passed
@davidwkeith
davidwkeith deleted the claude/trusting-hawking-3eawag branch November 1, 2026 06:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants