Commit Graph

7 Commits

Author SHA1 Message Date
Matt Van Horn 6acfc9017e feat: move stats footer into Python engine, harden synthesis voice contract
DO NOT MERGE until local validation passes on 3+ golden topics.

Problem: the magic footer ( All agents reported back!, emoji tree,
Top voices, Raw results saved) was composed by the synthesizer model
following a "Copy this EXACTLY" template buried 1150 lines into SKILL.md.
Under context pressure, Opus 4.7 dropped it. Three recent /last30days
runs (Opus 4.7, programming language for AI agents, Kanye West)
produced clean prose with no footer and used AI-slop section headers
(## The launch, ## Where it disappoints) instead of flowing paragraphs.

Fix:

1. render.py: new _render_emoji_footer() emits the deterministic footer
   as the final block of every compact output. Zero-count sources are
   omitted. Tree characters (├─ / └─) computed from populated-line
   count. The model no longer assembles the tree from text instructions.

2. render.py: new _site_name_for_url() and _format_web_line_sources()
   map URLs to clean publication names (Later, Buffer, CNN, etc.) so
   the 🌐 Web line is pre-assembled by Python.

3. last30days.py: compute_save_path_display() turns the save path into
   a ~/-relative string that the engine puts in the footer. Signature
   change: emit_output() and render_compact() both accept save_path.

4. SKILL.md synthesis contract rewritten:
   - Footer template DELETED. Replaced with instruction to include the
     engine footer block verbatim.
   - URL-to-site-name sub-block DELETED. Engine does this.
   - "Calculate actual totals" paragraph DELETED. Engine does this.
   - All em-dashes in the synthesis section replaced with ` - ` (single
     hyphen with spaces). Em-dashes are the most reliable AI-slop tell.
   - New rules: no ## markdown section headers in response body, no
     invented title line like "{Topic}: last 30 days", no bold section
     labels acting as headers. Bold-lead-in paragraph shape stays.
   - SELF-CHECK updated to verify footer presence, no em-dashes, no
     body-level headers.

Tests: 15 new tests covering footer emission, zero-source omission,
tree character placement, save-path threading, URL-to-name helper,
Web line formatting, Top voices combination, Polymarket line.

All 127 tests pass across render, rerank, cluster, briefing, CLI,
internals, fun-scoring.

Plan: docs/plans/2026-04-17-003-feat-deterministic-footer-plan.md

Local validation protocol (blocks merge):
- Run /last30days in a fresh Claude Code window on 5 golden topics
- Verify each output contains the footer block verbatim
- Verify zero ## body headers, zero em-dashes/en-dashes, zero invented
  title lines
- Report 5x8 pass/fail matrix; all 40 cells must be green before merge

🤖 Generated with Claude Opus 4.7 (1M context) via [Claude Code](https://claude.com/claude-code) + Compound Engineering v2.63.1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 09:15:55 -04:00
Matt Van Horn 371f62a403 Revert "feat: make default fun level actually surface comedy (#272)" (#273)
This reverts commit bad1d312ef.

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
2026-04-17 08:39:56 -04:00
Matt Van Horn bad1d312ef feat: make default fun level actually surface comedy (#272)
Most users never touch FUN_LEVEL. Default medium was shipping a stats
block but rarely a Best Takes block, and when it did it was below the
cluster fold where a synthesizing model had already stopped reading.
A 2,304-upvote Reddit comment ("WHAT?! I reached my monthly limit
just reading this post") on the 2026-04-17 Opus 4.7 run sat inside
cluster 11 and never made it into synthesis. Four coordinated changes:

1. render: promote Best Takes above the cluster list so the synthesizer
   sees comedy before it anchors on cluster 1.
2. render: lower medium threshold from 70 to 55 (heuristic maxes at 80),
   drop the two-gem floor to one-gem. Default now reliably emits the
   block on typical runs.
3. rerank: score individual top_comments by upvote ratio to their parent
   thread. A 2,304-upvote comment on a 300-upvote thread now outranks a
   400-upvote comment on a 3,400-upvote thread, which is the viral-wit
   signal. Handles both the LLM scoring path and the heuristic fallback.
4. render: merge scored comment gems into Best Takes alongside candidate
   gems, sorted together. Comment lines show body + parent title +
   r/subreddit or @handle + absolute upvotes.
5. SKILL: tell the synthesizer to quote at least two Best Takes entries
   verbatim, with an example of the new comment format.

Plan: docs/plans/2026-04-17-001-feat-default-fun-surfacing-plan.md

🤖 Generated with Claude Opus 4.7 (1M context) via [Claude Code](https://claude.com/claude-code) + Compound Engineering v2.56.1

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 08:30:39 -04:00
Matt Van Horn 082efe03e3 feat: surface YouTube + TikTok top comments alongside Reddit (#260)
* feat(normalize): pass YouTube top_comments through with Reddit-compatible shape

_normalize_youtube silently dropped top_comments after enrich_with_comments
populated them, so the downstream signals/render/entity layers never saw
YouTube comments. Map likes->score and text->excerpt so the existing
Reddit-compatible readers Just Work.

Shared _remap_comments helper will be reused for TikTok in a later commit.

* feat(tiktok): fetch top comments via ScrapeCreators when opted in

Mirrors the youtube_comments pattern: new env.is_tiktok_comments_available
gate (requires SCRAPECREATORS_API_KEY + tiktok_comments in INCLUDE_SOURCES),
tiktok.enrich_with_comments ranks posts and fetches via
GET /v1/tiktok/video/comments. Vote field is digg_count; text and user.nickname
come across verbatim. Pipeline calls the enricher right after TikTok search
when the gate is open.

Comment-fetch errors never crash the pipeline — the enricher returns an
empty list on 4xx/5xx.

* feat(normalize): pass TikTok top_comments through with digg_count->score mapping

Instagram uses the same shortform normalizer and has no comment fetcher
today, so the key is harmlessly absent there — no Instagram regression.

* feat(signals): add YouTube + TikTok top-comment score to engagement formula

Mirrors Reddit's 10% top-comment slot. Without top_comments present, the
formula reduces to views-dominant weighting; with a high-signal comment,
the item gets a meaningful bump (log1p(10k) ~ 9.2, weighted 0.10 = ~0.92
on the engagement score).

Updated the existing dominant-weight and missing-fields tests to the new
weights (0.45/0.32/0.13 for YT, 0.45/0.27/0.18 for TT). Views still dominate.

* feat(render): source-aware thresholds and vote labels for top comments

10 upvotes on Reddit signals community interest; 10 likes on a viral
TikTok is noise. Introduce per-source minimums (reddit 10, youtube 50,
tiktok 500) and native vote labels ('upvotes' for Reddit, 'likes' for
YT/TT). First-pass numbers — tune after live observation.

* docs: generalize top-comment quoting to YouTube + TikTok, add tiktok_comments opt-in

Synthesis instructions previously called out Reddit top comments only.
Now cover Reddit/YouTube/TikTok uniformly with source-appropriate vote
labels (upvotes vs likes), and explicitly frame YT transcript highlights
and comments as complementary signals. README and setup-wizard copy
document the new tiktok_comments INCLUDE_SOURCES token.

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
2026-04-15 08:26:06 -04:00
Pejman Pour-Moezzi 89e6ee29fd last30days: harden rerank and render prompts 2026-04-08 19:23:25 -07:00
Pejman Pour-Moezzi 77f67c1bd9 last30days: finish runtime/report rename 2026-04-08 13:03:41 -07:00
Matt Van Horn 0a9ff16dfc feat: v3.0.0 - intelligent search, GitHub person/project mode, ELI5, 13+ sources
v3 rewrites the search engine from the ground up:

- Intelligent pre-research: resolves X handles, GitHub repos, subreddits,
  TikTok hashtags, and YouTube channels before searching
- GitHub person-mode: PR velocity, top repos by stars, release notes
- GitHub project-mode: live star counts, README, releases, top issues
- ELI5 mode: plain language synthesis, no jargon
- 13+ sources: Reddit, X, YouTube, TikTok, Instagram, HN, Polymarket,
  GitHub, Threads, Pinterest, Perplexity, Bluesky, Web
- Free Reddit comments via public JSON (no API key needed)
- Fun judge v2: humor scoring baked into narrative
- Cookie consent before browser scanning
- 10,000 free ScrapeCreators calls
- 1,012 tests

Thank you to the community contributors whose issues and PRs shaped v3:
@uppinote20 (#143), @zerone0x (#134, #136), @thinkun (#116),
@thomasmktong (#124), @fanispoulinakisai-boop (#100), @pejmanjohn (#78),
@zl190 (#115), @hnshah (#84, #85, #86)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:52:23 -07:00