From 49d9f3e0eaeeab234c3ec903be43b88919d98eff Mon Sep 17 00:00:00 2001 From: Sam Scholten Date: Sun, 30 Aug 2026 20:40:39 +1000 Subject: rss: render score head as HTML paragraph Descriptions render as sanitized HTML in readers, where bare newlines collapse and the feed/score line ran into the article text. Wrap the headline in

so it breaks cleanly and reads as metadata, escaped on the wire by encoding/xml as description-carried HTML should be. --- cmds/rss.go | 23 +++++++++++++++-------- cmds/rss_test.go | 6 +++--- 2 files changed, 18 insertions(+), 11 deletions(-) (limited to 'cmds') diff --git a/cmds/rss.go b/cmds/rss.go index 8679f6f..30be578 100644 --- a/cmds/rss.go +++ b/cmds/rss.go @@ -49,8 +49,9 @@ type rssDocument struct { // RSS 2.0 document. Each item title gets the source feed as a rune-capped // suffix (readers show the channel title as the source of every item, so // this is the only in-list provenance), and the description body opens -// with feed + model score + the batch-wide median score. Item content was -// already cleaned when the run row was written. +// with feed + model score + the batch-wide median score as an emphasized +// HTML paragraph (bare newlines collapse where descriptions render as +// HTML). Item content was already cleaned when the run row was written. func RenderRSS(runs []core.RunRow) ([]byte, error) { doc := rssDocument{ Version: "2.0", @@ -95,9 +96,14 @@ func truncateRunes(s string, max int) string { return string([]rune(s)[:max]) + "…" } -// rssDescription builds the item body: first a headline line with the -// source feed, model score, and batch-wide median (omitted for run rows -// written before the median was logged), then the cleaned content (if any). +// rssDescription builds the item body: first a headline with the source +// feed, model score, and batch-wide median (omitted for run rows written +// before the median was logged), then the cleaned content (if any). The +// headline is wrapped in an emphasized HTML paragraph: descriptions render +// as sanitized HTML in most readers, where bare newlines collapse and the +// metadata line would run into the article text. encoding/xml escapes the +// tags on the wire, which is exactly how description-carried HTML is meant +// to be transported. func rssDescription(k core.KeptItem, median *float64) string { head := fmt.Sprintf("score %.2f", k.Score) if median != nil { @@ -106,8 +112,9 @@ func rssDescription(k core.KeptItem, median *float64) string { if k.FeedTitle != "" { head = k.FeedTitle + " · " + head } - if k.Content == "" { - return head + body := "

" + head + "

" + if k.Content != "" { + body += "\n" + k.Content } - return head + "\n\n" + k.Content + return body } diff --git a/cmds/rss_test.go b/cmds/rss_test.go index ca216ca..79d6d37 100644 --- a/cmds/rss_test.go +++ b/cmds/rss_test.go @@ -51,21 +51,21 @@ func TestRenderRSSGolden(t *testing.T) { https://example.com/c mf:103 Fri, 01 Mar 2024 11:00:00 +0000 - Feed A · score 0.80 + <p><em>Feed A · score 0.80</em></p> Alpha & Omega — Feed A https://example.com/a?x=1&y=2 mf:101 Fri, 01 Mar 2024 05:00:00 +0000 - Feed A · score 0.90 · batch median 0.55 x < y + <p><em>Feed A · score 0.90 · batch median 0.55</em></p> x < y Beta — A Feed With An Extremely Long And Descri… https://example.com/b mf:102 Fri, 01 Mar 2024 05:30:00 +0000 - A Feed With An Extremely Long And Descriptive Title Here · score 0.20 · batch median 0.55 second body + <p><em>A Feed With An Extremely Long And Descriptive Title Here · score 0.20 · batch median 0.55</em></p> second body ` -- cgit v1.2.3