You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
url: pages: a quote that runs through a link or bold text fails; cache readable text instead of markup #102
Since #92, URLSource caches a page's body markup without scripts, styles, comments or attributes. That fixed the size and leakage problem. But a quote can still only match text that sits inside one text node: any sentence that runs through inline markup (a link, bold, emphasis) fails, even when it is copied exactly from the page.
Reproduce (0.3.0)
R="url:https://patch.com/michigan/flint/flint-water-pollution-control-division-press-release-10-25-2021"
linkml-reference-validator validate text "This press release was produced by""$R"# Valid: True
linkml-reference-validator validate text "This press release was produced by the City of Flint.""$R"# Valid: False
The cache holds This press release was produced by <a>the City of Flint</a>. …
The same failures, met while curating Mechs made with mechmaker:
Wikipedia's lead sentences bold the article's subject (The <b>Flint water crisis</b> was a public health crisis…), so the first sentence of almost any article cannot be quoted. DaTMech works around this with a JSON source that fetches Wikipedia's plain-text extract.
ZFIN's terms page puts the license name in a link (licensed under a <a>Creative Commons Attribution 4.0 International License</a>). IngestMech had to quote it in two parts joined with ....
Store a url: page that is HTML as readable text, not markup. HTMLExtractor already flattens with breaks at block-level tags and without welding inline neighbours together, so it may serve as is:
inline tags (a, b, em, span, …) dropped, and their text kept in place;
block-level tags (p, li, h1–h6, div, br, …) become line breaks;
URL_SOURCE_CACHE_VERSION bumped, so existing entries are re-fetched.
The other way round would keep the markup in the cache but match quotes against the extracted text. That keeps the cache as it is, but leaves it unreadable.
Every quote that matches today lies within one text node, so it should still match the flattened text (whitespace is already normalized). Quotes through inline markup would start to pass.
Companion to monarch-initiative/mechmaker#82, where IngestMech's 1.1 MB ZFIN page (cached by 0.2.1) is now 19 KB with 0.3.0.
Since #92, URLSource caches sanitized body markup (sanitize_html in etl/extract/html.py). A quote that spans inline markup (<a>, <b>, <em>) fails against that markup. The proposal is to cache readable text instead, and bump URL_SOURCE_CACHE_VERSION.
I read the current code on main. I haven't run the repro URL.
Related issues
Create generic URL checking #12 (closed): the original generic URL source. It asked for "markdown-converted contents", so flattened text was the original intent.
It probably can't, as written. In HTMLExtractor._extract_scope (etl/extract/html.py, around lines 240–290):
<p>-only branch. If the page has any <p>, only paragraph text is returned. Headings, <li> items outside a <p>, table cells and <pre> are dropped. The issue asks for list items and table rows to be kept, so a page with a few <p> tags would silently lose text that matches today.
No table-row handling. The issue wants one line per row with separated cells. The extractor only inserts a "\n" after each block-level tag, so <td> cells would be on separate lines, not joined on one row.
<pre> whitespace is not distinguished, though sanitize_html handles it.
Probably the cleanest option is a new whole-document mode or function, e.g. html_to_text(content). It would run after the existing sanitize step, or fold into it. It would keep the "block tags get a break, inline tags are unwrapped" logic and skip the <p>-only and <article> shortcuts. It would also add the row-per-line rule, and it should be idempotent like sanitize_html. Getting this right matters because the invariant "every quote that matches today still matches" holds only if no text node is dropped.
Alternative: match against extracted text
The issue mentions matching quotes against extracted text while keeping markup in the cache. I'd stay with the primary proposal. Matching against derived text means the validator and the cache file can disagree about what was matched, which is a bad property for an audit trail. A cache that reads like the page is also the simpler design.
Suggested tests, TDD per CLAUDE.md
Doctests for the new function covering <a>, <b>, <br>, <li>, a table row, entities, and <pre>.
A regression test with the Flint sentence: "This press release was produced by <a>the City of Flint</a>."
A property check on fixtures: any quote that matches the old sanitized HTML text-node-wise also matches the flattened text.
A test that a page with both <p> and <li> or <td> text keeps all of it.
A test that old cache entries (url_source_version = 1) are re-fetched after the bump.
A test that title still comes from <meta>.
Open questions
Should table cells be separated by |, a tab, or a space? This affects quotes copied from a rendered table, since normalization collapses whitespace but not pipes.
Do cache files from earlier versions need a migration note? Bumping the version re-fetches them, which needs network access and can fail on pages that have since changed.
I can't tell from the repo whether mechmaker#82 or the DaTMech JSON-source workaround have changed, so I haven't relied on them.
If you want a PR for this:
@dragon-ai-agent please implement #102: add a whole-document HTML-to-text function (not reusing HTMLExtractor's <p>-only/<article> shortcuts), use it in URLSource.fetch after title extraction, bump URL_SOURCE_CACHE_VERSION to 2, and add doctests and tests per the list in this comment.
Activity
github-actions commentedon Oct 10, 2026
Summary
Since #92,
URLSourcecaches sanitized body markup (sanitize_htmlinetl/extract/html.py). A quote that spans inline markup (<a>,<b>,<em>) fails against that markup. The proposal is to cache readable text instead, and bumpURL_SOURCE_CACHE_VERSION.I read the current code on
main. I haven't run the repro URL.Related issues
sanitize_html. This issue is its follow-up. It was deliberately "markup kept, not flattened", so this reverses that decision. The reason to do it is that the cache is read by people and diffs, and matched by quotes.URLSource.fetchreadscitation_titlefrom<meta>before sanitizing. Flattening must stay after_extract_title, as it is now.full_text_html: a cache entry can claim full text while holding only the abstract #65: same theme. The cached body has to be real article text, because a quote validates against anything in it.Check on "HTMLExtractor may serve as is"
It probably can't, as written. In
HTMLExtractor._extract_scope(etl/extract/html.py, around lines 240–290):<p>-only branch. If the page has any<p>, only paragraph text is returned. Headings,<li>items outside a<p>, table cells and<pre>are dropped. The issue asks for list items and table rows to be kept, so a page with a few<p>tags would silently lose text that matches today.extract()narrows to<article>or<main>. For a genericurl:page, text that matches today in a sidebar or footer would stop matching. This is arguably a feature (Landing-page fallback is stampedfull_text_html: a cache entry can claim full text while holding only the abstract #65), but it is a behaviour change that isn't mentioned."\n"after each block-level tag, so<td>cells would be on separate lines, not joined on one row.<pre>whitespace is not distinguished, thoughsanitize_htmlhandles it.Probably the cleanest option is a new whole-document mode or function, e.g.
html_to_text(content). It would run after the existing sanitize step, or fold into it. It would keep the "block tags get a break, inline tags are unwrapped" logic and skip the<p>-only and<article>shortcuts. It would also add the row-per-line rule, and it should be idempotent likesanitize_html. Getting this right matters because the invariant "every quote that matches today still matches" holds only if no text node is dropped.Alternative: match against extracted text
The issue mentions matching quotes against extracted text while keeping markup in the cache. I'd stay with the primary proposal. Matching against derived text means the validator and the cache file can disagree about what was matched, which is a bad property for an audit trail. A cache that reads like the page is also the simpler design.
Suggested tests, TDD per CLAUDE.md
<a>,<b>,<br>,<li>, a table row, entities, and<pre>."This press release was produced by <a>the City of Flint</a>."<p>and<li>or<td>text keeps all of it.url_source_version= 1) are re-fetched after the bump.titlestill comes from<meta>.Open questions
|, a tab, or a space? This affects quotes copied from a rendered table, since normalization collapses whitespace but not pipes.I can't tell from the repo whether
mechmaker#82or the DaTMech JSON-source workaround have changed, so I haven't relied on them.If you want a PR for this:
@dragon-ai-agent please implement #102: add a whole-document HTML-to-text function (not reusing HTMLExtractor's <p>-only/<article> shortcuts), use it in URLSource.fetch after title extraction, bump URL_SOURCE_CACHE_VERSION to 2, and add doctests and tests per the list in this comment.