# Branching threads ("tributaries") design notes (trial)

## Data model (D1 migration, not applied)
- `threads` keeps one row per root conversation (root_tweet_id, topic, status).
- New `branches` table: id, thread_id, parent_branch_id (NULL means it forks from the root/Grok answer), fork_tweet_id, title ('Grok vs. @main: topic'), main_human, kind (informative|funny), quality_score, status (pending|approved|rejected), depth, tweet_count.
- `tweets` gets branch_id and parent_tweet_id (in_reply_to). Tweets shared between branches, such as the root and Grok's first answer, belong to the thread with branch_id NULL.
- Votes move to the branch level: `votes(branch_id, voter_hash, side)` with UNIQUE(branch_id, voter_hash). A thread's score is an aggregate (sum or most-voted branch), computed in a view and never stored as truth. Existing single-branch threads migrate as one branch each, so old votes carry over 1:1.
- The main human is computed per branch, not per thread.

## Fetch Worker spidering with cost caps
1. Read the root, then `conversation_id:X from:grok` (one or two pages). Grok's replies are the anchor of every possible argument, so this finds candidates cheaply.
2. Read `conversation_id:X to:grok -from:grok` for the human side, then optionally one batched `from:a OR from:b …` query for the top candidates' follow-ups.
3. Group by reply chain and fork at each human→Grok reply off the main Grok answer.
4. Budget per thread: max 8 reads, max 6 branches, max depth 12 tweets per branch. Also a global daily/monthly read counter in KV/D1, with a hard stop before the X cap (October is already over).
5. Pre-filter for free: drop branches with fewer than 2 human turns *addressed to Grok*, emoji/URL-only text, under 25 chars, or non-English/off-topic tags.
6. AI binding (Workers AI) runs one call per surviving branch: "Is this a back-and-forth disagreement with Grok? Score 0-10 on argument, informative and funny. Return JSON {keep, kind, title, reason}." Keep if score is 6 or higher; 4-5 goes to the queue flagged borderline.
7. Stop following a branch once two consecutive turns score as non-argument or the human stops addressing Grok.

## Admin queue
- Show a thread card with its branches as child rows, each with approve/reject/rename title/merge-into-sibling/reparent.
- The thread publishes when at least 1 branch is approved. Later-approved branches append, with no re-approval of the thread.
- Bulk "approve all ≥8" plus a per-branch AI reason shown inline.

## Risks
- Vote dilution: 5 branches split the same traffic, so each has small n. Mitigate with a thread-level "best tributary" rollup.
- Branch grouping is fuzzy (e.g. the paid-protester branch merges several sibling Q&As). Admin merge/split tools needed.
- Edgy/vulgar content (the funny branches here are crude). Need a per-branch NSFW flag.
- Extra reads: a branchy thread costs ~7 reads vs ~2 today. Caps are mandatory.
- Spidering surfaces private-ish people (non-public figures) and makes them the 'main human'. Consider a minimum follower count or anonymizing.
- Schema migration on the live D1 needs a backfill and a dual-read period.
