Repository navigation
feat(relay): index reactions under their target author for #p reads - #9285
bostonaholic wants to merge 7 commits into
Conversation
Slice 1 of relay-derived reaction recipients: matcher knows derived recipients. StoredEvent gains derived_recipients, hex pubkeys the relay computed from stored data. A #p filter now matches an event when any filter value is a literal p tag or a derived recipient. The field is never sent to clients; the wire event stays the signed event. #h channel fallback is unchanged. Co-authored-by: mboston Implementer <932d3b6ebea6cc83e091551ae46d1c7e7f8f7d0fa8e9cef5e6facc8544af413b@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matthew Boston <mboston@squareup.com>
Slice 2 of relay-derived reaction recipients: write and read path.
Ingest resolves a reaction target's effective author (actor tag for
relay-signed rows, else the signer) and buzz-db indexes the reaction in
event_mentions under that author, in the same transaction as the event,
with ON CONFLICT DO NOTHING so a reaction that already tags the author
keeps one row. The rows are written for channel-less targets too.
The #p read path returns the matched m.pubkey_hex, and a kind:7 row
carries it in derived_recipients, so the REQ and COUNT re-check accepts
a reaction without a p tag. Other kinds still match #p by literal tags
only: their mention rows can name a pubkey no tag matches, such as a
workflow owner's attribution indexed before that stopped.
Adds buzz_relay_reaction_recipient_derived_total{outcome=inserted|
duplicate|fallback_pubkey}; fallback_pubkey is a relay-signed target
with no resolvable author, indexed under the relay key.
Co-authored-by: mboston Implementer <932d3b6ebea6cc83e091551ae46d1c7e7f8f7d0fa8e9cef5e6facc8544af413b@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Matthew Boston <mboston@squareup.com>
Slice 4 of relay-derived reaction recipients: backfill script. scripts/maintenance/backfill_reaction_target_mentions.sql indexes every stored kind:7 reaction under its target's effective author in event_mentions, for reactions stored before ingest wrote that row. It follows docs/database.md Rule 3: a top-level DO block commits 5,000 reactions per batch, each batch takes the shared community deletion lock for its communities and writes only to active, undeleted ones, inserts use ON CONFLICT DO NOTHING, and a final validation query must print 0. The author resolution matches effective_message_author: actor, then p, then the signer for relay-signed targets. Run it after every serving relay writes the row at ingest. Local run against 12,005 seeded reactions in an active community and 5 in a quiescing one: first run inserted 12,003 rows in the active community (plus 6 for older local test reactions), second run inserted 0, validation printed 0 both times, and the quiescing community kept 0 mention rows. Co-authored-by: mboston Implementer <932d3b6ebea6cc83e091551ae46d1c7e7f8f7d0fa8e9cef5e6facc8544af413b@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matthew Boston <mboston@squareup.com>
Slice 5 of relay-derived reaction recipients (block/buzz half): docs. The kind:7 row in NOSTR.md now says the relay indexes the target's author as a #p recipient of the reaction, and that clients SHOULD still tag p per NIP-25. Co-authored-by: mboston Implementer <932d3b6ebea6cc83e091551ae46d1c7e7f8f7d0fa8e9cef5e6facc8544af413b@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matthew Boston <mboston@squareup.com>
|
@codex review |
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9dcc9c90e7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
A multi-value `#p` filter is not pushed into SQL, so reactions came back without derived recipients and the `#p` post-filter dropped untagged reactions in REQ history and the COUNT fallback. The store now loads the matching mention rows for returned reactions when the filter has several `#p` values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matthew Boston <mboston@squareup.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a3bfb0ea43
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The backfill walked `reactions` rows with a `reaction_event_id`, so a reaction stored while `set_reaction_event_id` was best-effort never got its target-author mention, and the validation query skipped it too. Walk stored kind:7 events per community instead, resolving the target from the last 64-hex `e` tag as ingest does. Validation now counts every stored kind:7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Matthew Boston <mboston@squareup.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b25d76715
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
A global {kinds:[7], "#p":[author]} subscription now receives an
untagged reaction to a channel-less event live, matching what a
historical REQ returns.
Co-authored-by: mboston Implementer <932d3b6ebea6cc83e091551ae46d1c7e7f8f7d0fa8e9cef5e6facc8544af413b@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Matthew Boston <mboston@squareup.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Why
buzz-app's Activity → Reactions tab (block/buzz-app#2638) finds reactions to your messages with
{kinds:[7], "#p":[you]}. A reaction shows up there only if the client that sent it tagged the message author (p, per NIP-25). Today buzz-app and its MCP tool do this. Mobile does it since #9141 (merged); the CLI change is #9121. Agents and third-party clients may never do it. With this PR the relay works out the recipient from the reacted-to message, so the tab works for reactions from every client.What
#precipient. That author is the message's effective author: theactor(or leadingp) for relay-signed messages, otherwise the signer. The relay looks up the target message during ingest; it never reads the author from the reaction.#preads and COUNT return those reactions to the message author, whether the filter has one#pvalue or several. Nothing is added to the signed event, so clients receive exactly what was sent.pindex rows for other kinds stay hidden, as they are today (fix: don't notify workflow owners through their attribution tag #8694).scripts/maintenance/backfill_reaction_target_mentions.sqlrepairs reactions stored before this change, underdocs/database.mdRule 3:reactions.reaction_event_idlink are coveredbuzz_relay_reaction_recipient_derived_total{outcome}with outcomesinserted,duplicateandfallback_pubkey.NOSTR.mdkind 7 now notes the derived recipient. Clients should still tagp, per NIP-25.Design: relay-derived reaction recipients (slices 1, 2, 4 and the block/buzz docs). The buzz-app side is block/buzz-app#3115. The Activity reactions tab this serves is block/buzz-app#2638.
Testing
#pmatches a derived recipient with noptag. It does not match when there is none or it is a different pubkey. Literalptags still match, and the#hfallback is unchanged.actorresolves to the actor.pproduces exactly one index row.#pREQ and COUNT return the same reactions as a single-value read.#precipient load was skipped.actor/p/ neither) and soft-deleted targets.etag chose the target, a soft-deleted target was skipped, and a batch size of 1 paged through reactions sharing one timestamp without gaps. Rerun inserted 0; validation returned 0.relay_pubkey_hexexits before writing anything.cargo fmt --checkandcargo clippy --workspace --all-targets -D warningspass.cargo testpasses for buzz-core, for buzz-db (unit tests plus the Postgres tests for event, reaction and feed), and for the buzz-relayhandlersandsubscriptionmodules.cluster_global_*relay Postgres tests. They need a database role withCREATEROLE, which the localbuzzrole lacks; Docker isn't available here.Known limits
#p+#hsubscription on another node does not receive untagged reactions: the Redis fan-out rebuilds the event without the derived recipient. buzz-app subscribes by#honly, so Activity is not affected.Rollout (
docs/database.mdRule 3 order)scripts/maintenance/backfill_reaction_target_mentions.sql, passingrelay_pubkey_hex. Its validation query (stored reactions with a live target but no index row for the target's author) must return0. Re-run the script if it doesn't: it is idempotent.ptag.Rollback: revert the relay. The extra index rows are inert, because the previous binary's re-check drops them.
🤖 Generated with Claude Code