Skip to content

Implement FMSG-005 reactions and the terminal flag - #43

Merged
markmnl merged 2 commits into
mainfrom
reactions
Sep 2, 2026
Merged

Implement FMSG-005 reactions and the terminal flag#43
markmnl merged 2 commits into
mainfrom
reactions

Conversation

@markmnl

@markmnl markmnl commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Summary

Implements FMSG-005 Reactions and exposes the fmsg Specification v0.6.0 terminal flag. Requires fmsgd's msg.is_terminal column from markmnl/fmsgd#43.

Message objects

  • terminal: the fmsg terminal flag, settable on POST /fmsg and PUT /fmsg/:id.
  • reaction: the emoji a message carries when it is a reaction ("" for a clearing reaction), null otherwise, so clients can hide reaction messages from lists.
  • reactions: effective reactions on the message. Each participant's latest reaction by time, ties broken by message hash, grouped by emoji in order of first reaction.

Recognition is by FMSG-005 shape: a reply with no_reply and terminal set, not important, no add-to, type text/plain;charset=UTF-8, no attachments, body a single emoji or empty. No dedicated media type.

POST /fmsg/:id/react sets or clears the caller's reaction by creating and sending a reaction message to every other participant in one transaction. Idempotent when the reaction is unchanged. Applies the same reply-domain check as send. Local recipients get their delivery resolved as send does.

Terminal enforcement at the API: a reply to a terminal message (create/update) and add-to on one return 409 with a clear message rather than surfacing the database trigger error.

WebSocket and push: an arriving reaction is routed as a reaction event carrying the refreshed subject message, never as new_msg, and produces no Web Push.

internal/emoji: IsRGI (exact RGI_Emoji membership), IsPossible (UTS #51 possible_emoji grammar with emoji presentation required) and IsSingle. Tables generated from Unicode 17.0 by go run ./internal/emoji/gen, which downloads the latest data by default.

Thread routes are unaffected: they walk ancestors, and reactions are leaves. terminal is added to thread message objects.

Verification

  • go build, go vet, go test ./... pass, with new tests for shape recognition, data reading, effective-reaction reduction (change, clear, case-insensitive reactor, hash tie-break), recipient derivation, and hub routing of reactions to the subject.
  • The new SQL (reaction insert, loadReactionRows, reactionSubject, list selects) was executed against fmsgd's v0.6.0 dd.sql plus this repo's dd.sql in a scratch PostgreSQL 16, and the schema trigger refused a reply to the reaction.

Docs

  • README: route table, POST /fmsg and PUT fields and errors, message object fields, new react section, add-to 409, WebSocket event table.
  • FMSG-003 v0.2.0 in the spec PR documents the same contract.

Follow-ups

  • fmsg-docker: integration test for react, and sync fmsgd's dd.sql copy once fmsgd#43 merges.
  • fmsg-cli / hermes-fmsg: expose the react route.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MW4FXDGacoWXeKPU5cQTHS

Requires fmsgd's msg.is_terminal column (markmnl/fmsgd#43).

- Message objects gain `terminal` (settable on create/update), `reaction`
  (the emoji a message carries when it is a reaction, "" for a clearing
  one, null otherwise) and `reactions` (effective reactions on the
  message: each participant's latest by time, ties by message hash,
  grouped by emoji).
- A reaction is recognised by its FMSG-005 shape: a reply with no_reply
  and terminal set, not important, no add-to, type
  text/plain;charset=UTF-8, no attachments, body a single emoji or empty.
- POST /fmsg/:id/react sets or clears the caller's reaction by sending a
  reaction message to every other participant in one step; idempotent
  when the reaction is unchanged; same reply-domain check as send.
- Replies to a terminal message and add-to on one are refused with 409
  before the database trigger would.
- WebSocket: an arriving reaction is pushed as a `reaction` event
  carrying the refreshed subject, never as new_msg; no Web Push.
- New internal/emoji package: RGI_Emoji set and emoji property tables
  generated from Unicode 17.0 data (go run ./internal/emoji/gen), with
  IsRGI, IsPossible (UTS #51 possible_emoji, emoji presentation
  required) and IsSingle.
- README documents the route, fields and event.

Verified: go test ./... passes; the new SQL was executed against fmsgd's
v0.6.0 schema in a scratch PostgreSQL, including the trigger refusing a
reply to a reaction.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MW4FXDGacoWXeKPU5cQTHS
@markmnl
markmnl merged commit e2748d0 into main Sep 2, 2026
1 check passed
@markmnl
markmnl deleted the reactions branch September 2, 2026 07:45
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.

1 participant