Rebuilding a Nostr reply tree without turning a quote into a parent

A note belongs to thread A, directly answers B and quotes Z. Linking every referenced event as a parent produces the wrong conversation tree.

Decode the marked references

For a kind-1 note with marked tags, use root for the thread and reply for the immediate parent. The short IDs in this example stand for event hashes. The output links the note under B and groups it with A. Z is cited content and contributes no parent edge.

const tags = [["e","A","","root"],["q","Z"],["e","B","","reply"]];
const eventAt = mark => tags.find(t => t[0] === "e" && t[3] === mark)?.[1];
console.log({ thread: eventAt("root"), parent: eventAt("reply") ?? eventAt("root") });

Handle the first reply and a missing parent

A direct reply to the root can carry just the root marker. In that case the root is also the parent. When B has not arrived, retain B’s ID as an unresolved parent rather than flattening the conversation into A. Older unmarked tags require a separate legacy interpretation; applying the marked-only sample to them would discard information.

github.com NIP-10