How Saidtree works
Every rule behind a tree, a badge, a status and the Grove, with the numbers the code uses. Nothing here is our verdict on whether a claim is true.
Last updated Wednesday, September 23
How to read a tree
A tree is one story, as told by the public accounts we track. Its root is the earliest post we track about it. Every later post from one of those accounts is a branch. Headlines from publisher feeds sit next to the tree as press, never as branches.
- 1The root: the earliest post we track, drawn as a solid dot at the left.
- 2The trunk is time, from the root to the latest branch. It turns dotted when the tree stops growing.
- 3Each branch is one voice's post, in its segment's color: Official, Newsroom and Expert above the trunk, the others below.
- 4Two limbs: a fork, where the posts split into two framings.
- 5Ticks on the bottom edge: articles from the publisher feeds we read.
- 6A green ring: a branch added since your last visit.
A post joins a tree when its words are close enough to the tree's other posts, and clearly closer to that tree than to any other. We compare meaning with a text-embedding model, and fall back to shared words when that model is unavailable. Posts that match nothing start a sapling, which only editors see. A sapling becomes a public tree once it has a post from Meta and at least two independent sources. Saplings nobody plants expire after 48 hours.
A tree grows while new branches arrive. After 72 hours without one it is dormant, and after 14 more days it is archived. 180 days after its last branch it retires: the summary, post text and quotes are deleted, and the title, dates, voices and counts stay.
Root
The root is the post with the earliest time among the live branches: the time the platform gives, or for a post an editor added, the time shown on the post. An exact time beats an approximate one when they tie. Press articles are never the root.
The root line always says "first seen in the sources we track", because an earlier post may exist somewhere we can't see. When a post an editor added showed only a relative time, such as "3h", the root line says "about". When two voices posted within a minute of each other (or within an hour, when either time is approximate), we show both as the earliest posts.
Under the root we show milestones: the first official voice, the first newsroom, the first first-hand post and the first article, each as time after the root. When an article came before every post we track, we say so. If an earlier post is added at least 5 minutes before the root, or the root post is removed, the root moves, and the tree says so in its rings.
Voices
We count voices, not posts: an account that posts ten times counts once. Every voice sits in one segment, always shown in the same order: Official, Newsroom, Expert, Advocacy, Creator, Public and Unknown. Official includes organizations speaking for themselves.
A tree can show up to two missing-voice notes: "No official voice yet", "No newsroom yet" or "Only creators so far". They appear only after a tree is 2 hours old, and "No official voice yet" only on topics where an official voice is expected.
Members of the public are counted, never named: a list says "3 members of the public".
Forks
Some stories split: part of the voices tell it one way, part another. Once a tree has 6 branches with text from at least 4 voices, Claude groups the posts by their own wording into at most two framings. Each branch it assigns must come with a quote that appears word for word in that post, or the assignment is dropped.
We show a fork only when each framing has at least 2 voices and at least 20% of the assigned branches. The framing names are AI-written and labelled that way; each framing is backed by quotes from the posts. Framing names never appear in rings, titles or emails. An editor can rename or lock a framing, and a renamed framing loses its AI label.
Depth: shallow roots and deep roots
Depth compares how loud a story is with how well it is sourced. It looks at the tree's last 7 days.
- Loudness adds up the voices on the tree, weighted by reach: small accounts count 1, mid 2, large 3 and mass 4. It is compared with every tree that grew this week, once there are at least 10; before that, fixed cut-offs apply.
- Primary material is the share of labelled posts that are official statements, first-hand posts or documents.
- Reporting counts newsrooms on the tree and outlets in the publisher feeds, each organization once.
- Depth is half primary material (full marks at 50%) and half reporting (full marks at 4 organizations), from 0 to 1.
A tree gets Shallow roots when it is in the loudest quarter of the week, its depth is below 0.30 and it has at least 5 voices. It gets Deep roots when it is in the quieter half, its depth is at least 0.65 and it has at least 2 primary posts. Each badge needs a clear change to go away again, so it doesn't flicker. Other trees get a plain line instead: "Loud and well sourced, with little reporting yet", "Talked about and reported", or "Neither unusually loud nor unusually quiet".
Every depth line carries its caveat: this compares attention, not truth, and coverage outside the sources we track is not counted. A tree's "Why?" drawer shows every number against its threshold.
Depth is rated only when our coverage is good enough. Facebook Pages must have been connected for the tree's whole life (up to 48 hours), we must read at least 100 Facebook Pages automatically, at least 80% of our news feeds must have been fetched in the last 2 hours, and post labels must have passed their accuracy check. Otherwise the tree says "Depth not rated" and gives the reason. A tree also needs 3 labelled posts, 3 voices and 2 hours of age before it is compared.
Status
Every tree has exactly one status from a fixed list. A status describes what the voices we track have posted. It is never our ruling on whether a claim is true.
| Status | What it means | Set by |
|---|---|---|
| Unverified claim | What the voices we track have posted has not been backed by enough independent accounts yet. | A rule |
| Eyewitness post — not independently verified | Someone posted what they saw first-hand, and two or fewer independent accounts back it. | A rule |
| Corroborated (N independent accounts) | Corroborated means N separate organizations or accounts reported or stated it. It does not mean we checked it. | A rule |
| Confirmed by official account | An editor confirmed an official account's own statement as the evidence, and at least two independent sources carry the story. | An editor |
| Disputed | An editor recorded which branch disputes the claim, and why. | An editor |
| Recycled media | An editor linked an earlier upload of the same photo or video. That branch is never used as evidence. | An editor |
| Corrected | We published a correction, or an editor confirmed that a voice corrected its own post. | An editor |
Rules are checked in order: an editor's status first, then the emergency window, then Corroborated, then Eyewitness, and otherwise Unverified claim. Corroborated counts independent organizations or accounts with evidence, so reshares and aggregators don't count, and several accounts of one organization count once. "Corroborated" means that many separate organizations or accounts reported or stated it. It does not mean we checked it.
A tree resting on a single independent source can never be Corroborated or Confirmed by official account. If an editor's status loses its evidence, it is suspended until an editor looks again. Every status change is listed on the tree under "Changes to this tree".
In an emergency, for the first 60 minutes after the root, a tree shows only what official accounts said, no AI text, and the "Developing" chip.
Rings
Rings show what changed on a tree since you last looked. Each ring comes from a fixed sentence template, such as "Root moved", "An official voice joined", "Now reported by 5 outlets" or "Status: Unverified claim → Corroborated". Rings are never written by AI.
We know when you last looked from your account if you're signed in, or from this device's own storage if you're not. The first time you see a tree, rings count from when the day's Grove was published. New branches are folded into one line, such as "+4 branches from 3 voices", and never counted as rings on their own. When a post is removed, the ring says only that a post is no longer available, never which one.
A pruned ring means an editor confirmed a correction: a voice corrected, withdrew or edited its own post, or we corrected our summary. Code can propose a prune, but only an editor confirms one. A removed post is never called pruned.
Paid branches
For stories about government, elections, courts, health, the economy or the environment, or with official or advocacy voices, we check Meta's Ad Library for US political and issue ads from the tree's voices, or with wording an editor chose. Every result waits for an editor: an ad appears on a tree only after an editor confirms it is about that story.
Spend and impressions are the ranges Meta reports, never exact amounts, and different currencies are never converted. "Paid for by" is the advertiser's own disclaimer. Ad text is never sent to Claude, and it is deleted once an editor decides.
The Grove
The Grove is one short, finished edition a day, with at most 12 trees. Code suggests an order; editors choose, reorder and approve, and only trees with an editor-written title can be published.
A tree is a candidate when it is public, has at least 3 voices and grew in the last 36 hours. A tree from yesterday's edition returns only with at least 2 new rings, marked "Still growing". The suggested order is:
- 30% how loud the tree is this week,
- 30% its depth,
- 20% freshness (a root under 24 hours old counts fully, under 48 hours half),
- 20% momentum (new voices in the last 12 hours, full marks at 5).
When a tree's depth can't be rated, freshness and momentum count half each. Loudness and depth weigh the same, so the Grove is not a list of what is loudest. Filling it, we take at most 3 trees per topic, keep 2 places for Deep roots trees when there are any, and allow at most 2 Shallow roots trees, which always show their badge.
Code proposes the edition at 5:00 am ET. Editors publish it when it's ready; at 7:00 am ET, the approved trees are published in the suggested order. After that the edition never changes, except that a tree taken down is marked in place.
AI-written summaries
Summaries and framing names are written by Claude, by Anthropic, and are always labelled "AI-written". Titles are never written by AI: an editor writes them, or they quote a post word for word.
What Claude reads: the text of public posts from organizations and public voices on the tree, and quotes our editors typed from such posts, up to 40 posts per summary. What Claude never reads: posts by members of the public, editor notes, anything readers send us, Meta's embeds, radar results, Ad Library data, press headlines, and posts that name a private person or identify a minor.
A summary has 2 to 5 sentences of at most 40 words each. Every sentence must cite the posts it came from, and each citation must match the post's text exactly. Then every sentence is checked twice:
- By code: a sentence without a citation, with a citation that doesn't match, naming a member of the public, using the words we never use, or, in an emergency's first hour, stating casualty numbers, a perpetrator or a motive, is dropped.
- By a second Claude call that reads each sentence with only its cited text: a sentence that is only partly supported, or not supported, is dropped. A sentence that makes an allegation about a person becomes a direct quote from the post instead.
A summary is published only when at least 2 sentences survive and no more than 30% were dropped. Otherwise the tree shows attributed quotes instead. Each time a page loads, a sentence is shown only while every post it cites is still live and still says the same words.
Summaries are rewritten when at least 2 new posts arrive, at most every 30 minutes and 12 times a day, and at once when a cited post is edited or removed. Trees held for review, and trees in an emergency's first hour, show quotes only until an editor approves.
Claude, by Anthropic, writes every AI text on Saidtree.
| Task | Model | Effort | Prompt version |
|---|---|---|---|
| Summaries | claude-opus-5 | high | summarize.v1#c87ca4bb6007 |
| Checking summaries against their posts | claude-opus-5 | high | verify.v1#8eba446ba967 |
| Post labels | claude-opus-5 | low | classify.v1#5c7908e5386c |
| Framings | claude-opus-5 | medium | framing.v1#8e0cd4221f1f |
| Proposed voice types | claude-opus-5 | low | voice.v1#1aa9481266d9 |
Latest eval
The eval hasn't been run and committed yet. Until it passes, a prompt or model change can't ship.
The bar is 1%: a prompt or model change can't ship above it.
Post labels
Each post gets labels: how close its author is to the events (official statement, first-hand, document, reporting or commentary), whether it is original, a reshare or an aggregation, how it shows its sources, and what kind of work it is (news, analysis, opinion, sponsored, satire). An editor's label always wins, then a fixed rule, then Claude's. Claude must quote the words that justify each label, and a label whose quote isn't in the post is discarded.
The two-person gold set isn't finished yet, so no κ is published and classifier labels aren't shown on trees.
Classifier labels are shown on trees only when every label reaches κ 0.70.
Our sources
Where we are now
Facebook Pages: not connected yet. We've applied for Meta's approval. Until then, posts appear when an editor adds them.
0 Facebook Pages read automatically · 131 publisher feeds · 0 accounts whose posts editors add
- Facebook Pages
- Posts from the public Facebook Pages in our list, read automatically through Meta's Page Public Content Access once it is approved. We keep the text and the reaction, comment and share totals; never photos, videos or comments.
- Instagram, Threads and other Facebook posts
- Added by editors, from links they find or readers send. We keep the link, the time shown on the post, an editor's note and an optional quote the editor typed. The post itself loads through Meta's own embed, only when a reader asks.
- Threads radar
- Private searches readers run with their own Threads account. Results are shown only to them and never feed Saidtree.
- Publisher feeds
- Headlines from publishers' RSS feeds, as a baseline to compare against. Headlines only, never articles.
- Meta Ad Library
- US political and issue ads, shown on a tree only after an editor matches them to it. Spend and reach are the ranges Meta reports.
How each source is doing
| Source | State | Since | Last success | Limits |
|---|---|---|---|---|
| Facebook Pages | Off | Sep 23, 11:39 pm ET | Never | Busy Pages every 15 min, others every 60 min or 6 h; slow polls pause at 80% of Meta's rate limits |
| Instagram and Threads embeds | Off | Sep 23, 11:39 pm ET | Never | At most 900 embeds an hour per platform, only when a reader chooses Show post |
| Threads radar | Off | Sep 23, 11:39 pm ET | Never | At most 200 searches a day per reader, with the reader's own account |
| Publisher feeds | Off | Sep 23, 11:39 pm ET | Never | Each feed at most every 15 min; headlines only |
| Meta Ad Library | Off | Sep 23, 11:39 pm ET | Never | At most 100 requests an hour; US political and issue ads only |
A source can be off, sample data, testing, live, degraded (no success for three times its usual interval), paused, or access stopped (Meta withdrew permission). A source runs only when its credentials exist, an admin turned it on, and the site is running on real data.
No source changed state in the last 90 days.
How Meta approvals work
Reading public Facebook Pages automatically needs Meta's Page Public Content Access, granted after business verification and an app review. Instagram and Threads posts can only be shown through Meta's own embeds, so our editors add them one by one. Searching Threads needs Meta's approval for keyword search; when it is granted, readers search with their own Threads account and the results stay private to them. Saidtree is not affiliated with or endorsed by Meta.
What we can't see
- Private accounts and private posts.
- Facebook groups and personal profiles. Meta offers no way for apps like ours to read them.
- Reels posted by other Facebook Pages.
- Instagram personal accounts, and Instagram posts nobody has added.
- Threads posts nobody has added.
- Every post from busy Facebook Pages. Facebook's API returns about 600 posts per Page per year.
- Posts deleted before we saw them, and anything outside the sources we track.
- Paid promotion other than US political and issue ads.
- Community Notes. Meta doesn't make them available to apps like ours.
Privacy tiers
- Tier A: government bodies, companies, nonprofits, media outlets, elected officials and public figures posting in that role. They can be named in AI text and quoted, and they have a page on Saidtree.
- Tier B: semi-public voices, such as local officials, creators and commentators with a large audience, posting on public matters. They are named and quoted on the public matter only, and they have no page.
- Tier C: members of the public, eyewitnesses included. They are never named, their posts are never sent to AI, and they have no page. Their posts appear only through Meta's embed, when a reader asks. An editor may quote one only when it is the only first-hand account of something newsworthy, and never with the handle.
Deletion
We check whether posts still exist: on every poll for a Facebook post's first 48 hours, then every 6 hours for a week, daily for a month, and weekly after that until the tree retires. A post an editor added is checked when readers report that it no longer loads.
When a post is gone, or someone asks us to remove it, we delete its text, quotes and links, drop every summary sentence that cited it, recompute the tree, and rewrite the summary. If the tree is left with fewer than 2 independent sources, we take it down. Our target is under 1 hour from a confirmed removal to a clean page, and never more than 24 hours.
| Data | Kept for |
|---|---|
| Facebook Page post text and counts not attached to any public tree | 72 hours |
| Facebook Page post text and counts on branches of public trees | Until the tree retires (180 days after its last branch) or the post is removed |
| Curated typed quotes | 30 days after the tree goes dormant |
| Editor notes | Until the tree retires |
| Press headlines not matched to a tree / matched | 14 days / until the tree retires |
| Embeddings | Same as their item |
| Ad records: confirmed / rejected or unmatched candidates / creative text | Until the tree retires / 30 days / deleted on decision (a hash is kept) |
| Radar hit IDs and shortcodes | 7 days; immediately on disconnect or account deletion |
| Threads tokens | Until disconnect, deletion, a Meta callback, or 90 days of radar inactivity |
| AI run logs | 12 months, with cited text redacted when a post is removed |
| Tips and trace sends / their emails | 90 days after closing / 30 days |
| Error reports / their emails | 12 months after resolution / 30 days after resolution |
| Data-rights requests and takedown notices / their contact details | 3 years after closing / reduced to a salted hash 30 days after closing |
| Meta callback records | 12 months |
| Ingest run records and error text | 90 days |
| Tombstones | 12 months |
| Audit log | 3 years, append-only; holds ids, enums and hashes only, never post text or contact details |
| Suppression hashes | Indefinitely |
| Account data | Until you delete your account |
Every number we use
These are read from the same file the code reads, so this page can't fall out of date. They are starting values, tuned on sample data; every change is logged below.
| Name | Value | What it does | Where |
|---|---|---|---|
| JOIN / JOIN_MARGIN / PRESS_JOIN | 0.8 / 0.03 / 0.78 | Similarity a post or headline needs to join a tree, and how far ahead of the next tree it must be. | How to read a tree |
| JOIN_LEX / PRESS_JOIN_LEX | 0.3 / 0.28 | The same, when word overlap is used instead of embeddings. | How to read a tree |
| REGROW / MERGE | 0.85 / 0.86 | Similarity to wake a dormant tree, and to merge two saplings overnight. | How to read a tree |
| REGROW_LEX / MERGE_LEX | 0.35 / 0.36 | The same two, when word overlap is used instead of embeddings: the same distance above the joining threshold. | How to read a tree |
| ENTITY_BONUS / ENTITY_BONUS_MAX | 0.04 / 2 | Added to the similarity for each name a post shares with a tree, for at most this many names. | How to read a tree |
| SAPLING_TTL_H / DORMANT_H / ARCHIVE_D / RETIRE_D | 48 h / 72 h / 14 d / 180 d | How long a sapling waits to be planted, and when a quiet tree goes dormant, is archived and retires. | How to read a tree |
| NEEDS_BRANCHES_OUTLETS | 5 | Outlets a press-only cluster needs before editors are asked to look for posts. | Our sources |
| NEAR_TIE_EXACT_MIN / NEAR_TIE_APPROX_MIN | 1 min / 60 min | Two earliest posts this close together are shown as a near-tie. | Root |
| ROOT_MOVE_MIN | 5 min | How much earlier a new post must be to move the root. | Root |
| FORK_MIN_BRANCHES / FORK_MIN_VOICES | 6 / 4 | Text-bearing branches and voices a tree needs before we look for a fork. | Forks |
| FRAMING_MIN_VOICES / FRAMING_MIN_SHARE / MAX_FRAMINGS | 2 / 20% / 2 | Voices and share of branches each framing needs, and the most framings shown. | Forks |
| REACH_WEIGHT | small 1, mid 2, large 3, mass 4, unknown 1 | How much each voice adds to loudness, by reach. | Depth: shallow roots and deep roots |
| POP_MIN | 10 trees | Trees needed this week before loudness is compared by percentile. | Depth: shallow roots and deep roots |
| LOUD_PCT_ON / LOUD_PCT_OFF | 0.75 / 0.65 | Loudness percentile to enter and leave shallow roots. | Depth: shallow roots and deep roots |
| QUIET_PCT_ON / QUIET_PCT_OFF | 0.5 / 0.6 | Loudness percentile to enter and leave deep roots. | Depth: shallow roots and deep roots |
| LOUD_ABS / QUIET_ABS | L ≥ 12 / L ≤ 6 | Loud and quiet while fewer than POP_MIN trees exist. | Depth: shallow roots and deep roots |
| LOUD_ABS_OFF / QUIET_ABS_OFF | L < 10 / L > 8 | Where those badges are lost again. | Depth: shallow roots and deep roots |
| SHALLOW_D_ON / SHALLOW_D_OFF / SHALLOW_MIN_VOICES | < 0.3 / ≥ 0.4 / 5 | Depth to enter and leave shallow roots, and voices needed. | Depth: shallow roots and deep roots |
| DEEP_D_ON / DEEP_D_OFF / DEEP_MIN_PRIMARY | ≥ 0.65 / < 0.55 / 2 | Depth to enter and leave deep roots, and primary posts needed. | Depth: shallow roots and deep roots |
| DEPTH_MIN_LABELLED / DEPTH_MIN_VOICES / DEPTH_MIN_AGE_H | 3 / 3 / 2 h | Labelled posts, voices and age before depth is compared. | Depth: shallow roots and deep roots |
| GATE_WINDOW_H / GATE_MIN_PPCA_VOICES / GATE_FEED_HEALTH | 48 h / 100 / 80% | Coverage we need before any depth badge is shown. | Depth: shallow roots and deep roots |
| CORROBORATED_MIN | 3 | Independent accounts with evidence needed for Corroborated. | Status |
| CRISIS_WINDOW_MIN | 60 min | How long an emergency tree shows only official quotes and no AI text. | AI-written summaries |
| GROVE_MAX / GROVE_TOPIC_MAX / GROVE_DEEP_RESERVE / GROVE_SHALLOW_MAX | 12 / 3 / 2 / 2 | Grove size, trees per topic, slots kept for deep roots, and the most shallow-roots trees. | The Grove |
| SUMMARY_SENTENCES / SENTENCE_MAX_WORDS / SUMMARY_MIN_KEEP / SUMMARY_MAX_DROP | 2–5 / 40 / 2 / 30% | Summary length, sentence length, and when a summary is withheld. | AI-written summaries |
| SUMMARY_MAX_DOCS / REGEN_MIN_INTERVAL_MIN / REGEN_MAX_PER_DAY | 40 / 30 min / 12 | Posts a summary may read, and how often it may be rewritten. | AI-written summaries |
| QUOTE_MAX_CHARS / QUOTE_MAX_SENTENCES / NOTE_MAX_CHARS / TITLE_MAX_CHARS | 300 / 2 / 280 / 90 | The longest quote, editor's note and title we show. | AI-written summaries |
| PAID_SIM / PAID_SIM_LEX | 0.7 / 0.25 | Below this, a wording match to an ad is shown as weak. | Paid branches |
| OEMBED_BUDGET_H / OEMBED_READER_H / OEMBED_DESK_H | 900 / 800 / 100 | Embeds per hour per platform, for readers and for editors. | Our sources |
| TEND_MAX / RADAR_SEARCH_MAX / ANON_SEEN_MAX | 100 / 10 / 200 | Trees you can tend, saved radar searches, and trees remembered on a device. | Rings |
| DRAW_MAX_BRANCHES / TREELET_MAX_DOTS | 80 / 40 | Branches drawn in the tree view and dots in a treelet; all branches are always listed. | How to read a tree |
Changes
No threshold has changed since launch: every value above is its starting value.
No voice label has been changed after a dispute.