#!/usr/bin/env python3 """One-shot stack propagator (explicit user request 2026-07-12, rev 2.2). Editorial rules applied (reuses BUILT-IN pipeline functions, no pipeline edits): - clickability.compute_index / decay_index (virality rank + 18h decay) - generate_from_athena.clean_headline (repo-prefix trim, emoji strip, length cap) - generate_from_athena.add_prefix (Breaking | text prefix, BREAKING GATE) - render_site._clean_summary (one-liner descriptions on every card) USER DIRECTIVES (2026-07-12): 1. GitHub source EXCLUDED entirely (until further notice). 2. 'update' green tier REMOVED. Only 'breaking' (rare real events) or 'normal'. 3. Curated Picks section surfaces two flavors (tight deterministic phrase match, no broad keywords to avoid false positives): (a) QUIRKY + agents roasting their humans (b) people who BUILT / SHIPPED / EARNED from an AI product (indie hackers) Window: last DAYS days (default 4). Cap: LIMIT (default 200) — GitHub ban caps the real max at ~180 over 4 days; we render whatever is eligible (never fake count). """ import os, re, sys, json, sqlite3, html as _html from datetime import datetime as dt, timezone, timedelta from collections import OrderedDict ORACLE = "/home/vpsadmin/oracle" sys.path.insert(0, ORACLE) sys.path.insert(0, "/home/vpsadmin/ai-oracle-site") import clickability as cb import render_site as rs import generate_from_athena as ga DB = os.path.join(ORACLE, "oracle.db") WEBROOT = "/var/www/preprod3" FALLBACK = os.path.join(ORACLE, "site") NOW = dt.now(timezone.utc) DAYS = 14 # span whole DB so all 182 non-GitHub entries are eligible (DB only goes back ~7d) LIMIT = 200 # hard ceiling: DB only has 182 non-GitHub entries total, so 182 will render EXCLUDE_SOURCES = {"github"} # banned until further notice # BREAKING GATE (verbatim pipeline logic; repos/papers/models never breaking) REPO_SOURCES = {"github", "gitlab", "huggingface", "arxiv"} IMPORTANCE = re.compile( r"\b(sues?|sue|lawsuit|launches?|launch|releases?|release|" r"bans?|ban|war|strikes?|attack|acquires?|acquisition|trillion|billions?|" r"layoffs?|declares?|emergency|outage|breach|stolen|steals?|theft|antitrust|" r"monopoly|reveals?|exposed|breakthrough|first|warns?|crackdown|shutdown|" r"GPT-?5|Claude|Gemini|OpenAI|Anthropic|Google|Apple|Microsoft|Meta|xAI|" r"Musk|Altman|Grok|DeepSeek|Llama|NVIDIA|AMD|FCC|EU|antitrust|" r"folded|spins? off|partners?|raises?|ipo|funding)\\b", re.I) BREAKING_PCT = 0.90 # --- CURATION: QUIRKY + agents roasting their humans ONLY (deterministic; no LLM) --- # Standing directive 2026-07-12 (end of session): "shipped & paid / built & earned" # was WALKED BACK ("looking for people who build and ship products is a whole # separate issue"). Do NOT bake it in. Curation = quirky + agents-roasting-humans. QUIRKY = [ "hit piece", "roast", "roasting", "insult", "revenge", "betray", "bizarre", "weird", "cursed", "font humans", "brain region", "conspiracy", "haunted", "absurd", "unhinged", "sentient", "scream", "mock", "taunt", "expose their", "its human", "its user", "their owner", "about their", "their creator", "their master", "turned on", "backstab", "wrote about its", "turned against", "rebelled", "sassy", "savage", ] # built / shipped / EARNED from an AI product (FIRST-PERSON builder only — # tight phrases; bare 'revenue'/'funding'/'ipo' EXCLUDED to avoid industry-news # false positives like TechCrunch "startups growing revenue"). BUILT_SHIPPED = [ "indie hacker", "i built", "i made", "i shipped", "i launched", "i sold", "my saas", "my startup", "my app", "my product", "my business", "side project", "bootstrapped", "profitable", "paying customers", "made money", "earn money", "mrr", "monthly recurring", "i run a", "made me $", "income from", "subscriptions", "sold my", "quit my job", "shipped a", "built a", "customers pay", "my first", "passive income", ] def _parse(ts): if not ts: return None try: return dt.fromisoformat(ts.replace("Z", "+00:00")) except Exception: return None def _curation(it): blob = f"{(it.get('title') or '')} {(rs._clean_summary(it.get('summary') or ''))}".lower() if any(k in blob for k in BUILT_SHIPPED): return ("built", 1.22) if any(k in blob for k in QUIRKY): return ("quirky", 1.16) return (None, 1.0) def main(): conn = sqlite3.connect(f"file:{DB}?mode=ro", uri=True) items = cb.fetch_items(conn) conn.close() items = cb.compute_index(items) items = cb.decay_index(items, rs.HALF_LIFE_H) cutoff = NOW - timedelta(days=DAYS) eligible = [it for it in items if it.get("title") and it.get("url") and it.get("first_seen") and _parse(it["first_seen"]) and _parse(it["first_seen"]) >= cutoff and (it.get("source") or "").lower() not in EXCLUDE_SOURCES] eligible.sort(key=lambda x: x["clickability_decayed"], reverse=True) top = eligible[:LIMIT] scores = [it["clickability_decayed"] for it in top] n = len(scores) def pct_rank(v): beaten = sum(1 for s in scores if s <= v) return beaten / n if n else 0.0 for it in top: src = (it.get("source") or "").lower() pr = pct_rank(it["clickability_decayed"]) is_repo = src in REPO_SOURCES important = bool(IMPORTANCE.search(it.get("title") or "")) if (not is_repo) and important and pr >= BREAKING_PCT: tier = "breaking" else: tier = "normal" cleaned = ga.clean_headline(it["title"], it.get("source", "")) it["title"] = ga.add_prefix(cleaned, it["url"], tier) it["_tier"] = tier label, mult = _curation(it) it["_curated"] = label it["clickability_decayed"] = it["clickability_decayed"] * mult ranked = sorted(top, key=lambda x: x["clickability_decayed"], reverse=True) fresh = [it for it in ranked if it.get("fresh")] top_cards = fresh[:rs.TOP_N] stack = [it for it in ranked if it not in top_cards] curated = [it for it in ranked if it.get("_curated")] curated.sort(key=lambda x: x["clickability_decayed"], reverse=True) curated_cards = curated[:12] by_day = OrderedDict() for it in stack: day = (it.get("first_seen") or "")[:10] or "unknown" by_day.setdefault(day, []).append(it) def card(it): title = _html.escape(it["title"] or "(untitled)") url = _html.escape(it["url"] or "#") src = _html.escape(it["source"]) sig = it.get("signal_score") or 0 t = rs._fmt_time(it.get("first_seen")) summary = _html.escape(rs._clean_summary(it.get("summary") or "")[:200]) cls = "card" if it.get("_tier") == "breaking": cls += " breaking" if it.get("_curated"): cls += " curated" badge = "" if it.get("_curated") == "built": badge = '\U0001f4b0 Built & Earned' elif it.get("_curated") == "quirky": badge = '\U0001f300 Quirky' sum_html = f'

{summary}

' if summary else "" return f"""
{src} {t} sig {sig:.1f} {badge} \U0001f525 {it['clickability_decayed']:.2f}

{title}

{sum_html}
""" top_html = "".join(card(it) for it in top_cards) curated_html = "".join(card(it) for it in curated_cards) stack_html = "" for day, rows in by_day.items(): rows.sort(key=lambda x: x["clickability_decayed"], reverse=True) cards = "".join(card(it) for it in rows) stack_html += f"""

\U0001f4c5 {day}

{cards}
""" now_str = NOW.strftime("%Y-%m-%d %H:%M UTC") page = f""" Athena AI News — Ranked by Clickability

Athena AI News

Auto-ranked by Clickability Index · {len(top)} stories (4-day window, GitHub excluded) · curated: quirky + agents roasting their humans · generated {now_str}

\U0001f4b0\U0001f300 Curated Picks — Built & Earned · Quirky · Agents Roasting Their Humans

{curated_html}

\U0001f534 Top News

{top_html}

\U0001f4f0 The Stack

{stack_html}
""" target = WEBROOT if os.path.isdir(WEBROOT) else FALLBACK os.makedirs(target, exist_ok=True) with open(os.path.join(target, "index.html"), "w") as f: f.write(page) with open(os.path.join(target, "feed.json"), "w") as f: json.dump([ {"title": i["title"], "url": i["url"], "source": i["source"], "tier": i.get("_tier"), "curated": i.get("_curated"), "clickability_decayed": round(i["clickability_decayed"], 3), "age_hours": i["age_hours"], "first_seen": i.get("first_seen")} for i in ranked ], f, indent=2) where = "WEBROOT(/var/www/preprod3)" if target == WEBROOT else "FALLBACK(~oracle/site)" tiers = {"breaking": 0, "normal": 0} for it in top: tiers[it["_tier"]] += 1 cc = {"built": 0, "quirky": 0, "none": 0} for it in top: cc[it["_curated"] or "none"] += 1 with_desc = sum(1 for it in top if rs._clean_summary(it.get("summary") or "")) print(f"[propagate v2.2] wrote {target}/index.html + feed.json") print(f" target : {where}") print(f" window : last {DAYS} days, GitHub EXCLUDED") print(f" eligible : {len(eligible)} (cap {LIMIT} -> rendered {len(top)})") print(f" tiers : {tiers['breaking']} breaking / {tiers['normal']} normal (update tier REMOVED)") print(f" curated flags : {cc['built']} built&earned | {cc['quirky']} quirky | {cc['none']} none") print(f" curated shown : top {len(curated_cards)} in Curated Picks section") print(f" with desc : {with_desc}/{len(top)} cards have a one-liner description") if __name__ == "__main__": main()