r/eczema · 2026-02-27 → 2026-03-27 · 7,634 raw items · 4,570 mentions

Treatment Signal Mining for Chronic Conditions

Glean Health collates Reddit treatment mentions with PubMed literature coverage to help patients with chronic conditions arrive at their appointments better-prepared. We mine condition-specific subreddits, score each poster's credibility from their full cross-Reddit history, cluster the treatments they mention, and surface how much published research exists for each. Not a recommender — a doctor-visit prep tool. The patient picks what to ask about; we save them the hours of Reddit and PubMed searching.

The Hypothesis

Patients with chronic conditions share what's worked for them on Reddit — both standard-of-care and long-tail options. Finding it requires hours of clicking through threads and cross-checking PubMed. The hypothesis: collating those mentions across credibility-scored patients, alongside PubMed presence per treatment, gives a 60-second version of that research so a patient arrives at their appointment with real questions instead of vague impressions.

The Approach

Extract blind. The LLM mines Reddit for treatment mentions without seeing PubMed first — patient signal isn't contaminated by literature priming. PubMed is then added as a parallel column (paper count + search link), so the patient sees community endorsement and literature coverage side-by-side. We collate, we don't combine.

Authority Matters

Not all posters carry the same weight. We pull each author's full cross-Reddit history — every subreddit they post in, not just the ones in our corpus — and score credibility from their behavior: posting longevity, subreddit diversity, commercial-link ratio, engagement quality. A U-curve catches single-purpose accounts; a commerce signal catches affiliate-driven promoters. Real patients with broad Reddit footprints get higher weight than focused shills.

Data Source

Arctic Shift — full Reddit archive, 2005-2026, every public subreddit, 2.5B items. We pull per-subreddit dumps from condition-specific communities across 25 chronic conditions, plus per-author cross-Reddit histories for credibility scoring. Reddit API only fills gaps in the live tail.

7,634

Raw Items Pulled

r/eczema · 2026-02-27 → 2026-03-27. 729 submissions + 6,905 comments.

2586

After Filtering

Hard filter: posts need len 200–8000 and score ≥ 1; comments need len ≥ 75, score ≥ 1, and a treatment-verb keyword. No deleted or bot content.

1924

Contain Treatment Info

1924/2490 items (77%) had extractable treatment information, including those framed as rants or questions.

702

Treatment Clusters

4,570 total mentions clustered into 702 distinct treatments.

Pipeline Architecture

Every box in the diagram is clickable. Numbered boxes are pipeline stages; "Arctic Shift" and "Reddit API" describe the data sources; "Condition Config" describes the per-condition manifest that parameterizes stages 8 through 9.5.

Retired Stage 3 — condition-context filter → why it's gone
flowchart TD subgraph D["DATA SOURCES"] D1["Arctic Shift (Phase 1)
Per-sub bulk dumps + per-author API
2005 through 2026-02"] D2["Reddit API (Phase 2)
Gap backfill + ongoing ingestion"] D1 -.-> D2 end subgraph CFG["CONDITION CONFIG — conditions/<name>.json"] C1["sources[] — subreddits, dirs, date ranges
pubmed_terms[] — for stage 9
proximity_map_csv — for stage 6 U-curve
--condition flag drives stages 8-10"] end subgraph FUNNEL["THE FUNNEL — cheap filters before LLM"] S1["1. Ingest
Per-sub JSONL (--sub)
deterministic I/O"] S2["2. Hard Filter
len 200-8000, score ≥1
rules, keeps ~77%"] S45["4+5. LLM Classify + Extract
Haiku 4.5 via Anthropic API
async, concurrency-capped, prompt-cached
contains_treatment_info, stance,
commercial_intent, symptoms[],
treatments[] w/ symptoms_targeted[]
~$3-5 per condition / 1.6k items"] S7["7. Treatment Clustering
(1) RxNorm canonicalization via _drug_key
collapses brand variants pre-embedding
(2) fastembed BAAI/bge-small-en-v1.5
agglomerative complete-link cosine ≥ 0.75
Atomic + combined_with links"] S1 --> S2 --> S45 --> S7 end subgraph TRACK_C["PARALLEL TRACK: USER AUTHORITY (deterministic)"] U1["Distinct Authors
from filtered corpus"] U2["Per-Author Pull
Arctic Shift API
cross-sub history"] U3["Six Feature Families
longevity / karma / commerce
U-curve fit / dispersion / sufficiency"] U4["user_credibility
composite (0.3 to 1.3)"] U1 --> U2 --> U3 --> U4 end subgraph AGG["AGGREGATION & EVIDENCE"] S8["8. Cluster Aggregation
Σ(post_q × user_c)"] S9["9. PubMed Enrichment
NCBI eUtils, [Title/Abstract] tag
Evidence coverage tag per cluster"] S95["9.5 Pro/Con/Neutral Classify
Haiku 4.5 on top-N abstracts
EFETCH bulk fetch + LLM label
pro / con / neutral counts per cluster"] S10["10. Surface Ranking
Novelty = weight × (1-evidence)"] S8 --> S9 --> S95 --> S10 end D1 --> S1 C1 -.drives.-> S1 C1 -.drives.-> S8 C1 -.drives.-> S9 S2 -.author list.-> U1 S7 --> S8 U4 --> S8 classDef dataSource fill:#eff6ff,stroke:#4f46e5,stroke-width:2px,color:#1e293b classDef config fill:#fffbeb,stroke:#d97706,stroke-width:2px,color:#1e293b classDef funnel fill:#fef2f2,stroke:#dc2626,stroke-width:2px,color:#1e293b classDef parallelTrack fill:#f5f3ff,stroke:#7c3aed,stroke-width:2px,color:#1e293b classDef aggregation fill:#ecfdf5,stroke:#059669,stroke-width:2px,color:#1e293b classDef highlight fill:#f5f3ff,stroke:#7c3aed,stroke-width:3px,color:#1e293b classDef finalNode fill:#ecfdf5,stroke:#059669,stroke-width:3px,color:#1e293b classDef configNode fill:#fffbeb,stroke:#d97706,stroke-width:2px,color:#1e293b class D dataSource class CFG config class FUNNEL funnel class TRACK_C parallelTrack class AGG aggregation class S45 highlight class S10 finalNode class C1 configNode
Click a stage in the diagram to see its details

Stage 1: Ingest

Pull raw posts and comments from Arctic Shift API for target subreddits.

Input

Arctic Shift API — per-subreddit paginated search, date range, posts + comments

Output

JSONL files per (subreddit, kind, date-range) in data/raw/
729
Posts
6,905
Comments
7,634
Total

Stage 2: Hard Filter

Drop noise: deleted content, bots, too short/long, low score. Comments also require treatment-verb keyword match.

Input

7,634 raw items

Output

2586 filtered items (33% kept)
Posts: len in [200, 8000] AND score ≥ 1 AND not deleted/bot
Comments: len ≥ 75 AND score ≥ 1 AND contains treatment verb AND not deleted/bot
606
Posts kept (83%)
1980
Comments kept (28%)
5,048
Dropped

Stage 4+5: LLM Classification + Extraction

Haiku 4.5 reads each item and produces structured JSON: treatment info presence, stance, commercial intent, and extracted treatment details.

Input

2586 items × (subreddit context + title + body)

Output

1924 items with treatment info (77%), 4,570 treatment mentions
Per item: contains_treatment_info, stance (7-class), about_condition, commercial_intent (3-level), skip_reason, symptoms[] (name + duration)
Per treatment (12 fields): intervention, category, form, dose, frequency, timing, duration_tried, outcome, outcome_detail, symptoms_targeted, combined_with, caveats
2490
Items processed
1924
Contain treatments
4,570
Treatment mentions

Stage 6: User Authority (parallel track)

Score each author's credibility from their posting behavior. Runs in parallel with the LLM extraction track; only joins back at stage 8.

Input

Distinct author list from filtered corpus + Arctic Shift per-author cross-Reddit history

Output

user_credibility composite in [0.3, 1.3] per author
Phase A (within-corpus, always runs): karma + commerce + sufficiency
Phase B (when cross-Reddit history is cached): adds longevity + condition-fit U-curve + dispersion. Commerce becomes a blend of within-corpus LLM signal and cross-Reddit regex.

Current rollout (this condition): 1,392 of 1,397 authors scored on Phase B (99%). The remaining 5 are on Phase A — in most cases because their cached cross-Reddit history is too thin to compute Phase B features (very new accounts, very few items). For the other 26 conditions, the Arctic Shift author-history puller has cached ~86% of authors and is paused (see data/authors/_TRUNCATION_NOTE.md); each condition's Phase B coverage will lift once we re-run build_pipeline.py for it.

Stage 7: Treatment Clustering

Group treatment mentions by semantic similarity, so "L-glutamine", "l glutamine powder", and "Lglutamine" all collapse into one cluster — and so do harder cases like "low-FODMAP" and "FODMAP elimination" that pure string matching would miss.

Input

4,570 treatment mentions with free-text intervention names

Output

702 treatment clusters with canonical names
Embed each intervention name with fastembed BAAI/bge-small-en-v1.5, L2-normalized.
Pairwise cosine distance → scipy agglomerative complete-linkage (NN-chain).
Cut the dendrogram at cosine similarity ≥ 0.75 → merge into same cluster.
Canonical name = most frequent spelling within cluster.

Stage 8: Per-Cluster Aggregation

Combine post-level quality and user-level credibility into a single cluster weight.

Input

Treatment clusters + author credibility scores

Output

cluster_weight, cluster_breadth, positive_ratio per cluster
cluster_weight = Σ(author_credibility) per mention
cluster_breadth = distinct_authors × distinct_subs
positive_ratio = (helped_a_lot + helped_some) / (helped_a_lot + helped_some + worsened + no_effect + mixed)
unclear outcomes are excluded from the denominator; if no rated outcomes, defaults to 0.5

Stage 9: PubMed Enrichment

Query PubMed for each treatment cluster to tag evidence coverage level.

Input

702 cluster canonical names

Output

pubmed_count + evidence_level (none/weak/moderate/strong) + top article links
Query structure: "<treatment>" AND (<condition.pubmed_terms> joined with OR)
For this condition: "<treatment>" AND ("eczema" OR "atopic dermatitis" OR "atopic eczema")
none: 0 results | weak: 1-5 | moderate: 6-20 | strong: 21+
441
None
86
Weak
50
Moderate
125
Strong

Stage 9.5: PubMed Pro/Con/Neutral Classification

For each cluster's top-N PubMed articles (from stage 9), fetch the abstract via NCBI EFETCH and ask Haiku 4.5 to classify the paper as pro (supports the treatment for this condition), con (refutes or warns), or neutral (mention/background/review without stance, or empty abstract). Conservative prompt — defaults to neutral when unsure. Honest sample-of-top-N framing: this classifies the top 5 most-relevance-sorted papers per cluster, not all pubmed_count papers.

Input

261 clusters × top-N pubmed_articles[].pmid from stage 9

Output

Per cluster: pubmed_pro_count / pubmed_con_count / pubmed_neutral_count / pubmed_classified_count. Per article: classification + fetched abstract
EFETCH bulk fetch (200 PMIDs/chunk, XML parsed) → AsyncAnthropic classify (concurrency=10, prompt-cached, max-cost guard)
Patient surface chip: {pro} supportive · {con} against · {neutral} background with tooltip "of the top N most-relevant PubMed papers"
375
Pro
81
Con
615
Neutral
261
Clusters classified

Stage 10: Surface Ranking (dev-only QC)

Compute novelty_score as an internal QC metric — credibility-weighted Reddit signal scaled down by literature coverage. The patient-facing Browse tab does not use this; it sorts by net-positive credibility-weighted endorsement instead. Novelty is kept for the Results Table sort and the novelty histogram, useful when scanning for high-signal treatments the literature hasn't touched.

Input

Cluster weights + evidence levels

Output

Ranked treatment list → Results Table (dev tab only)
novelty_score = cluster_weight × (1 - evidence_score)

evidence_score: none=0.00 | weak=0.35 | moderate=0.70 | strong=0.95

Data Source: Arctic Shift (Phase 1)

Bulk Reddit archive that powers every ingest. Per-subreddit dumps + per-author cross-Reddit history search. Public mirror of Reddit going back to its first commit.

Coverage

2005-06 through 2026-02 monthly releases (~1-2 month lag). 2.5B items (1.3B comments + 1.3B submissions). 261.8 GB compressed across every public subreddit.

How we use it

Per-subreddit search (stage 1 ingest) + per-author history API (stage 6 user authority). Polite at ~2 req/sec; circuit-breaker at 5 failures → 30 min cool-off (~1.2k authors/IP threshold).
Endpoints: /api/posts/search?subreddit=X & /api/comments/search?subreddit=X (ingest)
/api/posts/search?author=Y & /api/comments/search?author=Y (user authority)
limit:"auto" returns 100-1000 per page. Schema-tagged on ingest: source=arctic_shift, retrieved_at=<timestamp>.

Data Source: Reddit API (Phase 2, future)

Not yet integrated. Planned for the live tail: Arctic Shift releases monthly and lags ~1-2 months behind real-time, so a production deployment needs Reddit API to close that gap.

Why Phase 2

Phase 1 (Arctic Shift only) proves the concept on stable historical data. The 1-2 month lag is acceptable for a research MVP but unacceptable for ongoing patient use.

What it adds

Backfill: ingest items from (latest Arctic Shift dump) → now. Ongoing: scheduled pulls for new posts in the 26 condition-specific subreddits.
Schema rule (already enforced in stage 1): every record carries source=arctic_shift|reddit_api and retrieved_at=<timestamp>, so the two sources can be mixed in the pipeline without ambiguity.

Condition Config — conditions/<name>.json

Per-condition manifest that parameterizes the pipeline. The --condition flag on stages 8 through 9.5 reads this file to drive what gets ingested, how PubMed is queried, and how the U-curve is fit. Onboarding a new condition is a config file + sample ingest — no code change.

Schema

sources[] — {subreddit, extracted_dir, filtered_dir, date_range} per source. pubmed_terms[] — condition terms ORed and wrapped in [Title/Abstract]. proximity_map_csv — pointer to the subreddit → cluster table for U-curve scoring.

For eczema

Sources: 1 · PubMed terms: 3 · Proximity map: conditions/eczema_proximity_map.csv
Operator handoff: --sub drives ingest/filter/extract; --condition drives build/enrich/classify/UI. The subreddit field inside sources[] is what to pass to --sub. Ingest is NOT condition-aware yet (manual handoff works; parameterization is a possible cleanup, not a blocker).

Stage 3: Retired (no longer in pipeline)

Stage 3 was specified in early-prototype design as a condition-context keyword filter sitting between hard-filter (2) and LLM extract (4+5). It was never implemented and was formally removed from pipeline.mmd on 2026-05-25.

Why it was unnecessary

Each condition's sources[] list pre-vets subreddit relevance (e.g., r/migraine items are already on-topic by construction). A separate keyword-matching pass would just re-filter what subreddit selection already filtered for free.

Why the number stays "3"

Stages 4+5, 6, 7, 8, 9, 9.5, 10 are referenced by number across code, comments, docs, and the doc-coupling guard's stage-alignment check. Re-numbering would touch dozens of references; leaving the gap is the cheaper signal.
See PLAN.md Decisions Log #29 and the architecture overview header for the same explanation in the project plan.

Data Flow Waterfall

Eczema — r/eczema — 2026-02-27 → 2026-03-27 — every number below is from this run

1. Arctic Shift Pull
Source: Arctic Shift — full Reddit archive (2005–2026, ~2.5B items). We query their public API per-subreddit.
Method: Paginated search via /api/posts/search and /api/comments/search, sorted ascending by timestamp. Sliding after parameter for pagination. Rate limit: 0.5s between requests, honors X-RateLimit-Remaining header. Retry with exponential backoff on 429/5xx.
Tagging: Every item gets _source: "arctic_shift" and _retrieved_at: <unix_ts> appended — so we can distinguish data sources when Reddit API is added later.
Raw fields per item: id, author, subreddit, created_utc, score, title (posts), selftext (posts), body (comments), link_id (comments), parent_id (comments).
7,634
raw items
↓ 729 submissions + 6,905 comments
2. Hard Filter
Purpose: Drop items that can't possibly contain extractable treatment information before spending LLM tokens on them.

Post filter (submissions):
Length [200–8000 chars] (title + body combined). Below 200: too short for treatment context (e.g., "IBS sucks" at 9 chars). Above 8000: mega-posts, usually life stories. Threshold calibrated from spike: original 2000-char cap was dropping real treatment-protocol posts.
Score ≥ 1. "Score" = Reddit karma = upvotes minus downvotes. Score 0 or negative means the community buried it (spam, off-topic, or auto-removed). Score 1 = at minimum the author's own upvote (default). Original threshold of ≥ 5 dropped 54% of corpus including real treatment claims. Relaxed to ≥ 1 after spike audit.
Not deleted/removed. Body is literally "[deleted]" or "[removed]" — no text to extract.
Not a bot. Blocklist: AutoModerator, RemindMeBot, sneakpeekbot, WikiSummarizerBot, LocationBot, RepostSleuthBot, FatFingerHelperBot.
→ 606/729 kept (83%)

Comment filter:
• Same author/deletion/bot rules as posts.
Length ≥ 75 chars. Shorter = "same," "thanks," "lol" — no treatment info possible.
Score ≥ 1. Same logic as posts.
Must contain at least one treatment-verb keyword. This is the key lever for comments — drops 78% of noise. A comment saying "same here :(" has no verb → dropped. A comment saying "tried L-glutamine 5g daily" → kept.
Treatment verbs: tried, took, taking, started, helped, helps, worked, works, cured, fixed, relief, reduced, improved, failed, stopped, recommend, suggest, noticed, eliminated, switched, prescribed, diagnosed
→ 1980/6,905 kept (28%)
2586
filtered
↓ dropped 5,048 items (66%)
6. User AuthorityPARALLEL TRACK
Branches off from Step 2 (filtered corpus); rejoins at Step 8 (Aggregation). Runs independently of LLM extraction — no dependency on what Stage 4+5 finds. The patient signal and the “who's saying it” signal stay separate until Stage 8 multiplies them.

Step A — Collect authors: distinct authors from the filtered corpus (subreddit-bounded). For this condition: 1,397 distinct authors scored.
Step B — Pull cross-Reddit history: for each author, arctic_client.author_submissions() + author_comments()no subreddit filter. We want the author's entire Reddit footprint to gauge whether they're a real human (broad activity across many subs) or a single-purpose account (concentrated in one or two health subs — the shill / health-anxiety signature).
Step C — Score: composite per author in [0.3, 1.3]:
Phase A (always, within-corpus): karma + commerce + sufficiency — 5 authors on Phase A here.
Phase B (when cross-Reddit history is cached): adds longevity + condition-fit U-curve + dispersion — 1,392 authors on Phase B here.

Why cross-Reddit footprint matters: a poster looks like a normal patient on r/migraine. The credibility check looks at the other 50 subs they post in — their cooking, their gaming, their work life. Real people have broad footprints; shills don't.

Rollout state (this condition): 1,392 of 1,397 authors on Phase B (99%); the remaining 5 fall back to Phase A because their cached history is too thin for Phase B features. The other 26 conditions are at lower Phase B coverage pending the U-curve re-aggregation pass — the Arctic Shift author-history puller cached ~86% of remaining authors before being paused (see data/authors/_TRUNCATION_NOTE.md).
1,397
scored authors
cluster_weight = Σ(user_credibility) per mention — this is where the parallel track rejoins (Step 8 input)
4+5. LLM Extraction
Model: Claude Haiku 4.5 (claude-haiku-4-5-20251001) via the Anthropic Python SDK (AsyncAnthropic) — async with a concurrency semaphore and ephemeral prompt caching on the system prompt. Hard per-run cost cap via --max-cost. Realized cost ~$0.0023/item.
Unit of analysis: One post or comment + subreddit context. Author identity is NOT passed to the LLM (user-authority scoring is a separate, independent track).

What the LLM classifies per item:
contains_treatment_info (bool): Does this post contain extractable treatment information? Measures content presence, not post-intent tone. A rant with an embedded "I tried X" still gets true.
stance (7-class): embodied (poster tried it) | secondhand (someone they know) | hearsay | question | rant | meta | none
about_condition (bool): Is the post on-topic for the target condition? Off-topic posts in a condition sub get false.
commercial_intent (3-level): Is the poster promoting something they created/profit from?
none = no self-promotion | weak = free tool/blog/channel | strong = paid product, referral codes, affiliate links
Mentioning someone else's ad/brand = none. Casual brand names = none. (Validated 7/7 on real + synthetic tests.)
skip_reason (text): When contains_treatment_info is false, brief explanation why. Used for calibration.
symptoms[]: array of {name, duration} extracted regardless of whether treatments are present.

What the LLM extracts per treatment (12 fields):
• intervention (free text, canonicalized downstream), category (9-class), form, dose, frequency, timing
• duration_tried, outcome (6-class: helped_a_lot | helped_some | no_effect | worsened | mixed | unclear)
• outcome_detail (free text), symptoms_targeted (subset of post-level symptoms this treatment is aimed at), combined_with (links to co-treatments), caveats

Compound protocols: "Low-FODMAP + L-glutamine + walking" becomes three atomic treatment records linked via combined_with. Clustering needs atomic units; protocol links preserved.

Key finding from spike: 64% of items contain treatment info. 68% of posts labeled "question" and 56% of "rants" had embedded treatment claims. Filtering by post-intent tone would destroy the pipeline.
1924
with treatments
if contains_treatment_info: extract treatments[] with schema fields | else: skip_reason = "why not"
↓ 566 items (22%) had no extractable treatment content
Treatment Mentions
1924 items yielded 4,570 individual treatment mentions (avg 2.4 treatments per item).
Each mention is one atomic intervention. Compound protocols are decomposed: "low-FODMAP + probiotics" = 2 mentions, each with combined_with pointing to the other.
Intervention names are free text at this point — "L-glutamine," "l glutamine powder," "glutamine supplement" are all separate strings. Canonicalization happens in the next step.
4,570
mentions
7. Clustering
Purpose: Group variant spellings and semantically-equivalent phrasings of the same treatment into one cluster.
Method: Embed each intervention name with fastembed BAAI/bge-small-en-v1.5 (a sentence-embedding model), L2-normalize, then compute pairwise cosine distance and run scipy agglomerative complete-linkage clustering (NN-chain implementation, O(N²)). Cut the dendrogram at cosine similarity ≥ 0.75.
Why embeddings, not string matching: "low-fodmap diet" and "FODMAP elimination" are the same intervention but share few characters — string matching would miss it. Embeddings catch the semantic equivalence. Brand/generic pairs ("imodium" vs "loperamide") are handled separately via the RxNorm dictionary in extract/rxnorm.py, applied at stage 9 (PubMed query expansion).
Canonical name: Most frequent spelling within each cluster becomes the display name.
702
clusters
8. Aggregation
Purpose: Score each treatment cluster by community consensus strength.

Formulas:
cluster_weight = Σ(author_credibility) for each mention in the cluster. Higher = more credible voices reporting this treatment.
Output range [0.3, 1.3]. Phase A (always runs): karma signal (1 - pct_at_floor) + commerce signal (LLM-flagged commercial_intent) + data sufficiency (min(n_posts/3, 1)). Phase B (when cross-Reddit history is cached): adds longevity (log-bounded by account age), condition-fit U-curve over proximity-map clusters, and dispersion (cross-sub entropy penalty); commerce becomes a blend of within-corpus LLM signal and cross-Reddit regex. Rollout state (this condition): 1,392 of 1,397 authors on Phase B (99%); the remaining 5 are on Phase A. The other 26 conditions are at lower Phase B coverage pending the U-curve re-aggregation pass — the Arctic Shift author-history puller cached ~86% of remaining authors before being paused (see data/authors/_TRUNCATION_NOTE.md). Per-author breakdown lives in pipeline_output.author_credibility_index.
cluster_breadth = distinct_authors × distinct_subreddits. Higher = signal isn't one person repeating themselves.
positive_ratio = (helped_a_lot + helped_some) / (helped_a_lot + helped_some + worsened + no_effect + mixed). The "did it work?" rate. unclear outcomes are excluded; defaults to 0.5 if no rated outcomes.
Σ
weighted scores
9. PubMed Enrichment
Purpose: Tag each treatment with how much published medical research exists. PubMed annotates; it never subtracts. Even well-studied treatments stay in the output.
Query structure: "<treatment_name>" AND (<condition.pubmed_terms> joined with OR) — terms come from each condition's config file.
For this condition (eczema): "<treatment_name>" AND ("eczema" OR "atopic dermatitis" OR "atopic eczema")
API: NCBI E-utilities (free, no key required). ~3 requests/sec. Returns result count + top 5 PMIDs. Article titles fetched via eSummary endpoint.

Evidence level mapping:
0 results → none (evidence_score = 0.00)
1–5 results → weak (evidence_score = 0.35)
6–20 results → moderate (evidence_score = 0.70)
21+ results → strong (evidence_score = 0.95)

This run: 441 none | 86 weak | 50 moderate | 125 strong
441
novel (no PubMed)
novelty_score = cluster_weight × (1 - evidence_score)
10. Surface Ranking (dev-only QC)
Purpose: Compute novelty_score as an internal QC metric. The patient-facing Browse tab does not use this — it sorts by net-positive credibility-weighted endorsement so well-studied and under-studied treatments are surfaced together. Novelty is retained for the dev-facing Results Table sort and the novelty histogram, where it's useful for scanning the long tail.
Formula: novelty_score = cluster_weight × (1 - evidence_score)

How it works:
• cluster_weight=10, evidence=none (0.00): novelty = 10 × 1.0 = 10.0 ← high signal, zero research
• cluster_weight=10, evidence=strong (0.95): novelty = 10 × 0.05 = 0.5 ← well-studied, suppressed in this internal metric
• cluster_weight=2, evidence=none: novelty = 2 × 1.0 = 2.0 ← novel but weak signal

Why we keep it: for dev QC. The Results Table novelty sort is a fast way to scan for high-credibility patient signal that the literature hasn't touched — candidates worth investigating further, even though we never present them to patients as such.
702
ranked treatments
0
Treatment Clusters
0
Total Mentions
0
Posts Analyzed
0
Novel (no PubMed)
Helped
Mixed
Worsened/No effect
Unclear
# Treatment Category Mentions Authors Outcomes PubMed Evidence Novelty

Results Charts

Evidence Distribution

Treatment Categories

Outcome Distribution (all mentions)

Mentions vs PubMed Coverage

Top 25 Treatments by Mention Count

Novelty Score Distribution

Project Structure

How the codebase is organized. Data files (raw, filtered, extracted) are generated by the pipeline and gitignored.

glean-health/
CLAUDE.md — Project rules, coupled-artifact protocol, stack description
PLAN.md — Living plan: architecture, 34 decisions, spike findings, open questions, next actions
pipeline.html — Coupled Mermaid diagram (standalone, open in browser to view pipeline)
schema.html — Data schema & lineage: every table/file, schema, and join, stage-by-stage
web/ (Vercel app) — Patient surface: Next.js app on Vercel reading Supabase Postgres (condition + treatment search)
dev.html — Dev hub card grid linking out to schema, pipeline, corpus, and dev_status
condition_map.md — 95 subreddits mapped to 9 clusters for user-authority U-curve scoring
condition_map.csv — Machine-readable version of condition map (subreddit, cluster, notes)
.gitignore — Excludes data/, deploy/, .wrangler/ from git
ingest/ STAGE 1-2
Arctic Shift API client and data ingestion pipeline
arctic_client.py — ArcticShiftClient class: paginated search, rate limiting, retry, per-author queries
ingest_sub.py — CLI: pull one subreddit's posts + comments for a date range
ingest_all.py — CLI: batch pull subreddits across all 26 condition configs, then auto-filter
filter_posts.py — Hard filter for submissions: len [200-8000], score ≥ 1, not deleted/bot
filter_comments.py — Hard filter for comments: len ≥ 75, score ≥ 1, treatment-verb keyword gate
README.md — Usage docs and scale notes
extract/ STAGES 4-10
LLM extraction, clustering, PubMed enrichment, and dashboard generation
run_extraction.py — Stage 4+5: Haiku 4.5 extraction via Anthropic SDK (async, prompt-cached, hard cost cap via --max-cost). Production prompt in prompt.md
build_pipeline.py — Stages 6-8: fuzzy clustering, author credibility, per-cluster aggregation
pubmed_enrich.py — Stage 9: query PubMed per cluster, tag evidence level, compute novelty score
build_ui.py — Stage 10: generate this dashboard (self-contained HTML with embedded data)
summarize.py — Quick CLI summary of extraction results (treatment counts, categories)
archive/spike-2026-04-06/ CALIBRATION
Throwaway scripts from the calibration spike that informed pipeline design decisions
pull_ibs_sample.py — One-off Arctic Shift pull for r/ibs Jan-Feb 2026
filter.py — Spike-era hard filter (led to threshold calibration)
sample_for_labeling.py — Random sample 100 items for hand-labeling
build_labeler.py — Browser-based labeling UI generator
run_haiku_batch.ps1 — First Haiku extraction test (50 items)
run_haiku_on_labeled.ps1 — Haiku vs hand-label agreement test (30 items)
tabulate.py — Compute spike metrics: density, keyword P/R, Haiku quality
test_commercial_prompt.py — Commercial intent prompt fix validation (7/7 pass)
data/ GENERATED — GITIGNORED
All data is regenerated by the pipeline. Not checked into git.
raw/<sub>/ — JSONL from Arctic Shift API (posts + comments per subreddit per date range)
filtered/<sub>/ — Post-filter JSONL (hard rules applied, noise removed)
extracted/<sub>/ — One JSON per item: Haiku's structured extraction output
pipeline/ — pipeline_output.json (clustered + scored) and results.html (this dashboard)
_spike_archive/ — Archived spike-era data files (label sheets, old extractions)
archive/ RETIRED
Previous version of the project (pre-rewrite)
v1-retired-2026-04-02/ — Original PDF pipeline design, research notes

Treatments people with Eczema have tried

Below are the things real patients with Eczema have talked about, surfaced from 2,490 Reddit posts. For each one you can see how many people endorsed it, how it worked for them, and how much published research exists. Bring this list to your next appointment as ideas to discuss with your doctor — both well-studied options and ones the literature hasn't caught up to yet are mixed in by how strongly the community endorses them.

Crowd-sourced from Reddit. Not medical advice. Talk to your doctor before trying anything. ← Back to all conditions

Type to filter the list below

What patients are talking about

Ranked by how strongly the community endorses each — well-studied and under-studied treatments mixed together. Click any card for posts, doses, and PubMed links.

Browse all 0 treatments (advanced) Full sortable list with filters — for power users.