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>
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>
* 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>