@@ -70,7 +70,9 @@ fn mention_tags(mentions: &[&str]) -> Result<Vec<Tag>, String> {
7070 check_pubkey ( hex) ?;
7171 let lower = hex. to_ascii_lowercase ( ) ;
7272 if seen. insert ( lower. clone ( ) ) {
73+ // Parallel p (fan-out) + mention (explicit intent) for #2296 contract.
7374 tags. push ( tag ( vec ! [ "p" , & lower] ) ?) ;
75+ tags. push ( tag ( vec ! [ "mention" , & lower] ) ?) ;
7476 }
7577 }
7678 Ok ( tags)
@@ -404,9 +406,9 @@ pub fn build_forum_comment(
404406///
405407/// `mentions` carries the pubkeys of mentions that are *newly added* by this
406408/// edit (the caller diffs the edited body against the original). Only those get
407- /// a `p` tag so the newly-mentioned party is notified/woken, while a typo-fix
408- /// edit that leaves the mention set unchanged emits no `p` tags and never
409- /// re-wakes anyone. This mirrors the send path's `mention_tags` (dedup +
409+ /// parallel `p` + `mention` tags so the newly-mentioned party is notified/woken,
410+ /// while a typo-fix edit that leaves the mention set unchanged emits neither
411+ /// and never re-wakes anyone. Mirrors the send path's `mention_tags` (dedup +
410412/// lowercase); the receiver overlays these onto the original event's audience.
411413pub fn build_message_edit (
412414 channel_id : Uuid ,
@@ -931,11 +933,11 @@ mod tests {
931933 assert_eq ! ( event. pubkey. to_hex( ) , TARGET_HEX ) ;
932934 }
933935
934- // ── build_message_edit `p`-tag emission (lane 8ace8eed) ──────────────
936+ // ── build_message_edit p+mention emission (lane 8ace8eed) ──────────────
935937 //
936938 // The composer diffs the edited body's mentions against the original and
937939 // hands `build_message_edit` only the *newly added* pubkeys. These tests
938- // pin the builder's contract given that contract : emit a `p` per added
940+ // pins the builder's contract: emit parallel `p` + `mention ` per added
939941 // mention (deduped, lowercased), and none when the added set is empty
940942 // (typo-fix edit) — so an unchanged mention set re-wakes nobody.
941943
@@ -962,20 +964,21 @@ mod tests {
962964 let tags = edit_tags ( & [ ALICE_HEX ] ) ;
963965 assert_eq ! ( tags[ 0 ] [ 0 ] , "h" ) ;
964966 assert_eq ! ( tags[ 1 ] [ 0 ] , "e" ) ;
965- // The `p` tag rides right after the `e` tag (insertion order).
967+ // `p` + parallel `mention` ride right after the `e` tag (insertion order).
966968 assert_eq ! ( tags[ 2 ] , vec![ "p" . to_string( ) , ALICE_HEX . to_string( ) ] ) ;
969+ assert_eq ! ( tags[ 3 ] , vec![ "mention" . to_string( ) , ALICE_HEX . to_string( ) ] ) ;
967970 }
968971
969972 #[ test]
970973 fn edit_with_no_added_mentions_emits_no_p_tag ( ) {
971974 // Typo-fix edit: mention set unchanged, so the composer passes `&[]`.
972- // The edit event must carry no `p` tag and re-wake nobody.
975+ // The edit event must carry no `p`/`mention` tags and re-wake nobody.
973976 let tags = edit_tags ( & [ ] ) ;
974977 assert ! (
975- !tags
976- . iter ( )
977- . any ( |t| t . first ( ) . map ( String :: as_str ) == Some ( "p" ) ) ,
978- "unchanged-mention edit must not emit any `p` tag , got {tags:?}"
978+ !tags. iter ( ) . any ( |t| {
979+ matches! ( t . first ( ) . map ( String :: as_str ) , Some ( "p" | "mention" ) )
980+ } ) ,
981+ "unchanged-mention edit must not emit p/mention tags , got {tags:?}"
979982 ) ;
980983 }
981984
@@ -995,5 +998,18 @@ mod tests {
995998 ) ;
996999 assert_eq ! ( p_tags[ 0 ] , & vec![ "p" . to_string( ) , ALICE_HEX . to_string( ) ] ) ;
9971000 assert_eq ! ( p_tags[ 1 ] , & vec![ "p" . to_string( ) , BOB_HEX . to_string( ) ] ) ;
1001+ let mention_tags: Vec < & Vec < String > > = tags
1002+ . iter ( )
1003+ . filter ( |t| t. first ( ) . map ( String :: as_str) == Some ( "mention" ) )
1004+ . collect ( ) ;
1005+ assert_eq ! ( mention_tags. len( ) , 2 ) ;
1006+ assert_eq ! (
1007+ mention_tags[ 0 ] ,
1008+ & vec![ "mention" . to_string( ) , ALICE_HEX . to_string( ) ]
1009+ ) ;
1010+ assert_eq ! (
1011+ mention_tags[ 1 ] ,
1012+ & vec![ "mention" . to_string( ) , BOB_HEX . to_string( ) ]
1013+ ) ;
9981014 }
9991015}
0 commit comments