You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
QuotedTweetSnapshot was introduced in 6ef3f42 (April 2026) as the minimum-viable shape needed to render quoted tweets — handle, name, text, media, post date. PR #134 (merged 2026-05-02) refined preservation across syncs but didn't expand the captured fields. The shape hasn't been revisited despite ft's parser already extracting much richer data for the outer-tweet BookmarkRecord from the same response.
The quoted_status_result payload contains the same legacy.* and core.user_results.result.* paths that convertTweetToRecord parses for outer tweets at src/graphql-bookmarks.ts:255-376. Currently those paths are read for outer and silently dropped when constructing the quoted snapshot at lines 295-320. This issue proposes mirroring the extraction.
The asymmetry
ft extracts the following from the outer tweet's response. None are propagated to QuotedTweetSnapshot:
links: string[] (from legacy.entities.urls) + t.co→display_url expansion of text
✗
Full BookmarkAuthorSnapshot (incl. verification fields once #145 ships)
✗ — has flat authorHandle/authorName/authorProfileImageUrl only
All of these come from legacy.* and core.user_results.result.* paths that exist on quoted_status_result.result.* at the same nesting depth — verified May 2026 against live captures.
Concrete failing cases
Verified against live X API:
Article filter cannot detect quote-articles. Bookmark 2049874687069426008 is a quote-tweet whose quoted tweet's text is a single https://t.co/JlZbH7mCPt, resolving via 301 to x.com/i/article/2049760065427574784 (a real X Article). Downstream article-detection relies on links arrays; QuotedTweetSnapshot has none, so the filter is blind to article presence inside quotes.
Thread badges absent on quoted tweets.conversationId is the disambiguator for "is this part of a thread?" Outer tweets get it; quoted tweets don't. Quoted tweets that ARE in threads stay un-badged.
Auto-prepended @-mentions misattributed. Quoted text like "@user1 @user2 hello world" — without display_text_range, downstream consumers can't tell which leading @-handles were typed by the quoted author vs. auto-prepended by Twitter as reply targets. The same gap exists on outer BookmarkRecord currently — this issue subsumes the outer case as well, since the parsing is identical.
Suggested fix
Layer 1 — type + parser (≈25 lines, no schema work)
Extend QuotedTweetSnapshot at src/types.ts:43-53 to mirror BookmarkRecord:
Parser-side: mirror lines 343-376 of the outer-tweet construction inside the quoted-tweet construction at lines 307-330. Same code, applied to qtTweet/qtLegacy. BookmarkEngagementSnapshot (line 34-41 of types.ts) and BookmarkAuthorSnapshot (extended by #145) are reusable as-is.
Adding author alongside the flat fields keeps the change additive — no breaking changes for downstream consumers reading authorHandle/authorName/authorProfileImageUrl. Both populate from the same qtTweet.core.user_results.result.
Also add displayTextRange?: [number, number] to BookmarkRecord (not just QuotedTweetSnapshot) — same field, same parsing. Single line in the outer construction at line 343 area:
displayTextRange: legacy?.display_text_range,
This subsumes any separate proposal to add display_text_range for outer tweets only.
Layer 2 — SQLite columns (optional)
QuotedTweetSnapshot is already serialized as quoted_tweet_json TEXT (added in schema v4 with 6ef3f42). Layer 1 lands automatically without schema changes — the new fields just become additional keys in the existing JSON blob.
For BookmarkRecord.displayTextRange, optionally add a display_text_range TEXT column if per-bookmark queryability is desired. Not required for the fix.
QuotedTweetSnapshotwas introduced in6ef3f42(April 2026) as the minimum-viable shape needed to render quoted tweets — handle, name, text, media, post date. PR #134 (merged 2026-05-02) refined preservation across syncs but didn't expand the captured fields. The shape hasn't been revisited despite ft's parser already extracting much richer data for the outer-tweetBookmarkRecordfrom the same response.The
quoted_status_resultpayload contains the samelegacy.*andcore.user_results.result.*paths thatconvertTweetToRecordparses for outer tweets atsrc/graphql-bookmarks.ts:255-376. Currently those paths are read for outer and silently dropped when constructing the quoted snapshot at lines 295-320. This issue proposes mirroring the extraction.The asymmetry
ft extracts the following from the outer tweet's response. None are propagated to
QuotedTweetSnapshot:BookmarkRecord)QuotedTweetSnapshot?engagement: { likeCount, repostCount, replyCount, quoteCount, bookmarkCount, viewCount }conversationIdinReplyToStatusId,inReplyToUserIdquotedStatusId(chain detection)languagesourceApppossiblySensitivelinks: string[](fromlegacy.entities.urls) + t.co→display_url expansion oftextBookmarkAuthorSnapshot(incl. verification fields once #145 ships)authorHandle/authorName/authorProfileImageUrlonlyAll of these come from
legacy.*andcore.user_results.result.*paths that exist onquoted_status_result.result.*at the same nesting depth — verified May 2026 against live captures.Concrete failing cases
Verified against live X API:
Article filter cannot detect quote-articles. Bookmark
2049874687069426008is a quote-tweet whose quoted tweet's text is a singlehttps://t.co/JlZbH7mCPt, resolving via 301 tox.com/i/article/2049760065427574784(a real X Article). Downstream article-detection relies onlinksarrays;QuotedTweetSnapshothas none, so the filter is blind to article presence inside quotes.Thread badges absent on quoted tweets.
conversationIdis the disambiguator for "is this part of a thread?" Outer tweets get it; quoted tweets don't. Quoted tweets that ARE in threads stay un-badged.View counts dropped on quoted tweets. Once
view_countalways NULL —view_counts_everywhere_api_enabledflag missing fromGRAPHQL_FEATURES#144 lands andviews.countpopulates for outer, quoted tweets stay at zero —QuotedTweetSnapshothas noengagementfield to receive it.Auto-prepended @-mentions misattributed. Quoted text like
"@user1 @user2 hello world"— withoutdisplay_text_range, downstream consumers can't tell which leading @-handles were typed by the quoted author vs. auto-prepended by Twitter as reply targets. The same gap exists on outerBookmarkRecordcurrently — this issue subsumes the outer case as well, since the parsing is identical.Suggested fix
Layer 1 — type + parser (≈25 lines, no schema work)
Extend
QuotedTweetSnapshotatsrc/types.ts:43-53to mirrorBookmarkRecord:Parser-side: mirror lines 343-376 of the outer-tweet construction inside the quoted-tweet construction at lines 307-330. Same code, applied to
qtTweet/qtLegacy.BookmarkEngagementSnapshot(line 34-41 of types.ts) andBookmarkAuthorSnapshot(extended by #145) are reusable as-is.Adding
authoralongside the flat fields keeps the change additive — no breaking changes for downstream consumers readingauthorHandle/authorName/authorProfileImageUrl. Both populate from the sameqtTweet.core.user_results.result.Also add
displayTextRange?: [number, number]toBookmarkRecord(not justQuotedTweetSnapshot) — same field, same parsing. Single line in the outer construction at line 343 area:This subsumes any separate proposal to add
display_text_rangefor outer tweets only.Layer 2 — SQLite columns (optional)
QuotedTweetSnapshotis already serialized asquoted_tweet_json TEXT(added in schema v4 with6ef3f42). Layer 1 lands automatically without schema changes — the new fields just become additional keys in the existing JSON blob.For
BookmarkRecord.displayTextRange, optionally add adisplay_text_range TEXTcolumn if per-bookmark queryability is desired. Not required for the fix.Dependencies
This issue assumes #144 and #145 land:
view_countalways NULL —view_counts_everywhere_api_enabledflag missing fromGRAPHQL_FEATURES#144 addsview_counts_everywhere_api_enabledtoGRAPHQL_FEATURES. Without it, X stripsviewsfrom responses andengagement.viewCountstays NULL for both outer and quoted.isVerifiedboolean conflates Twitter Blue, Business, and Government accounts #145 extendsBookmarkAuthorSnapshotwith verification fields. Reusing that type for quoted tweets'authorautomatically inherits the disambiguation onceisVerifiedboolean conflates Twitter Blue, Business, and Government accounts #145 ships.This issue can be drafted/reviewed before either dependency lands but data quality depends on both being resolved.