Skip to content

fix: decode HTML entities in scraped trending descriptions - #39

Draft
DamengRandom wants to merge 2 commits into
masterfrom
fix/scan-trending-html-entities
Draft

fix: decode HTML entities in scraped trending descriptions#39
DamengRandom wants to merge 2 commits into
masterfrom
fix/scan-trending-html-entities

Conversation

@DamengRandom

Copy link
Copy Markdown
Owner

Closes #38

parseTrendingHtml stripped tags out of the scraped <p> but left HTML entities encoded, so a repo description GitHub renders as Octadesk & Intercom was stored and prompted as Octadesk &amp; Intercom. Three of the 32 repos on today's trending page were affected.

Change

src/tools/trending-scrape.tool.ts — decode entities after the tags come out, so the tag-strip is not re-applied to a decoded <Suspense>. Named entities GitHub emits (&amp; &lt; &gt; &quot; &apos; &nbsp;) plus decimal and hex numeric references; anything unrecognised is left exactly as it was, so AT&T and &notanentity; survive intact. parseTrendingHtml is now exported so the parser can be tested directly, matching how fitRepos, toAiNewsItems and mergeSummaries are already exposed for their tests.

Test

src/tools/trending-scrape.test.ts — new, built on an article block trimmed from a real github.com/trending/typescript?since=daily response. Three of the five tests fail without the fix:

not ok 1 - decodes the escaped ampersand GitHub emits in repo descriptions
  expected: 'Open alternative to Kommo, Octadesk & Intercom'
  actual: 'Open alternative to Kommo, Octadesk &amp; Intercom'
not ok 2 - decodes the rest of the entities a description can carry
  expected: 'Type-safe <div> helpers'
  actual: 'Type-safe &lt;div&gt; helpers'
not ok 3 - a decoded angle bracket is text, not markup to strip a second time
  expected: 'Render <Suspense> on the server'
  actual: 'Render &lt;Suspense&gt; on the server'
ok 4 - leaves something that only looks like an entity untouched
ok 5 - still reads the name, language and both star counts
# tests 5
# pass 2
# fail 3

The other two pass either way on purpose — they guard against the fix over-reaching or disturbing the rest of the parse.

With the fix, the full suite, the type-check and the format check all pass:

# tests 84
# pass 84
# fail 0

Observed vs reasoned

  • Observed by execution: the corrupted descriptions, the raw page HTML they came from, the curator prompt line built from them, the three failing assertions above, and pnpm test / pnpm tsc / pnpm format:check after the fix.
  • Reasoned only: that the corrupted value reaches Postgres. There is no .env in this environment, so no database was written to — saveTrendingRepos passes repo.description through as a bind parameter with no transformation, which is where the claim comes from.

Generated by Claude Code

claude added 2 commits August 7, 2026 10:18
Keeps the replace callback's CRAP score under Fallow's threshold and
covers the out-of-range code point guard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013v6SNv4axPxsCPcmEFNk86
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trending scrape stores HTML entities verbatim — a repo description reads "Octadesk &amp; Intercom"

2 participants