atagen

macltok

public
Star0
Watch0

Commit 6f47f7cbe75a

tarball

atagen <boss@atagen.co>2026-10-26 02:31 UTC

2 files changed+49-1
timeline: ask for a reply's details by the replying event's id

Timeline::fetch_details_for_event looks up the item that holds the reply, but the watcher passed the target's id, so the sdk found a non-reply and returned Ok. Every reply whose target wasn't loaded stayed Unavailable, which reads as Pending, and never resolved.
Modified file: src/normalise/timeline.rs+1 additions−1 deletions
@@ -107,7 +107,7 @@ impl Normaliser<'_> {
107107 timestamp_ms: embedded.timestamp.get().into(),
108108 },
109109 TimelineDetails::Unavailable => {
110- self.missing_replies.push(reply.event_id.clone());
110+ self.missing_replies.extend(event.event_id().map(ToOwned::to_owned));
111111 ReplyDetails::Pending
112112 }
113113 TimelineDetails::Pending => ReplyDetails::Pending,
Modified file: tests/history_repair.rs+48 additions
@@ -614,3 +614,51 @@ async fn closing_during_a_recent_load_settles_idle_not_failed() {
614614 );
615615 shut_down(&room.handle, &mut room.events, room.running).await;
616616 }
617+
618+#[tokio::test]
619+async fn a_reply_whose_target_is_not_loaded_fetches_it() {
620+ let room_id = room_id!("!replies:localhost");
621+ let target_id = event_id!("$target:localhost");
622+ let reply_id = event_id!("$reply:localhost");
623+ let mut room = start_room(room_id).await;
624+ room.server
625+ .mock_room_messages()
626+ .ok(RoomMessagesResponseTemplate::default())
627+ .mount()
628+ .await;
629+ room.server
630+ .mock_room_event()
631+ .match_event_id()
632+ .ok(EventFactory::new()
633+ .room(room_id)
634+ .text_msg("target")
635+ .event_id(target_id)
636+ .sender(&ALICE)
637+ .into_event())
638+ .mount()
639+ .await;
640+ room.server
641+ .sync_room(
642+ &room.sync_client,
643+ JoinedRoomBuilder::new(room_id).add_timeline_event(
644+ EventFactory::new()
645+ .text_msg("reply")
646+ .reply_to(target_id)
647+ .event_id(reply_id)
648+ .sender(&ALICE),
649+ ),
650+ )
651+ .await;
652+ open_room(&mut room, room_id).await;
653+ timeline_until(&mut room, |items| {
654+ items.iter().any(|item| {
655+ matches!(item, TimelineItem::Event(event)
656+ if event.event_id.as_deref() == Some(reply_id)
657+ && event.reply_to.as_ref().is_some_and(|reply| {
658+ matches!(reply.details, macltok::ReplyDetails::Ready { .. })
659+ }))
660+ })
661+ })
662+ .await;
663+ shut_down(&room.handle, &mut room.events, room.running).await;
664+}