Fifteen elements, thirty questions

A filter list blocks what someone has already seen and written a rule for. This one reads the page. Watch Jev walk every element of a news site, score how ad-like each one is, and hand your code a number it can threshold — then run the same page through a filter list and through an LLM and see what each one costs you.

Run all three to fill the scorecard below.
🔒dailyledger.example/technology
no blocker
The Daily Ledger
BreakingSenate passes the chip subsidy bill, 68–31, sending it to the president’s desk.
adservly.net · 728×90
GOLD IS SOARING 📈
Open a bullion account in 4 minutes. 0% commission this week only.
Claim offer
Semiconductors

Chip makers race to re-tool fabs before the subsidy window closes

Three of the largest foundries have moved capital spending forward by a quarter, betting that the credit will not be renewed.

Media

Inside the advertising industry’s scramble for a new identity graph

With third-party cookies gone, ad buyers are paying for sponsored placements they can measure. Publishers say the money is following attention, not audience.

Earn 4.8% on savings with MetaBank

Sponsored by MetaBank · Member FDIC · Rate subject to change

Seven signs your home network is already exposed

Most routers ship with defaults that anyone can look up. Here is what our security team checks first — and the one setting that matters most.

Our picks: the five best laptops for students

  • Best overall — Lenovo Yoga 7i
  • Best battery — MacBook Air M4
We may earn a commission when you buy through these links.
Markets

Memory prices slip for the third straight month

Oversupply in DRAM is starting to show up in handset margins, analysts said.

© 2026 The Daily LedgerAdvertise with usCareersPrivacyEthics policy
×

Join 200,000 readers

The morning briefing, free, every weekday at 7am.

you@example.comSign up
We value your privacyWe and 847 partners store and access information on your device.
Accept allManage

Note on the animation: Jev’s answers are revealed one element at a time so you can read them. In a real integration all thirty questions come back in the same round trip — the walk you see is your own loop applying the answers, not the model thinking.

The same eight elements, three engines

These are the elements that decide whether a blocker is any good. The easy ones — a masthead, three news stories, a footer — every engine gets right. Columns fill in as you run each scan above.

✓ did what the policy asked · ~ defensible but not what the policy asked · ✗ wrong

What Jev is actually asked, per element

Your code walks the DOM and builds one small state object per candidate element: what it says, what it links to, how big it is, where it sits. No pixels — Jev reads text and structured data, and the browser already has both.

Then two questions about each element. Not “what should I do with this?” — that is your decision, and it depends on settings Jev knows nothing about. Just how ad-like is it, and what kind of thing is it.

// one element of fifteen
state = {
  url: "dailyledger.example/technology",
  element: {
    tag: "article", classes: "story",
    text: "Paid partnership · Nordvault — Seven signs your
           home network is already exposed — Most routers ship
           with defaults that anyone can look up…",
    links: ["nordvault.example/?utm_source=ledger&utm_campaign=q3"],
    size: "412x132", slot: "3rd item in the main story river"
  },
  page: "technology news index, 6 stories, 1 sidebar"
}

questions = {
  isAd: noul("This element is a paid placement whose purpose is to
             promote a product or service, rather than editorial
             content the reader came for."),

  kind: choice("What kind of element is this?", {
    display_ad:        "A bought banner or unit from an ad network.",
    sponsored_content: "Editorial-looking content paid for by a brand.",
    affiliate_block:   "Editorial picks that earn a commission on clicks.",
    newsletter_modal:  "The site asking for an email address.",
    cookie_banner:     "A consent or privacy prompt.",
    house_promo:       "The site promoting its own subscription.",
    content:           "What the reader came for, or site furniture."
  })
}

// 96 ms later, for all fifteen elements at once
answers["07"] = { isAd: { noul: 0.93 },
                  kind: { choice: "sponsored_content", confidence: 0.88 } }

Nothing in that answer decides anything. The deciding is eleven lines of your own code, and it is the part your users actually argue about — so it belongs where you can change it without touching a model:

function policy(a, settings) {
  // interruptions go, whatever the ad score says
  if (a.kind.choice === "cookie_banner") return "hide";
  if (a.kind.choice === "newsletter_modal") return "hide";

  // confident ad: hide it, unless the reader asked to see
  // sponsored posts marked rather than removed
  if (a.isAd.noul >= 0.85)
    return (a.kind.choice === "sponsored_content" && settings.labelSponsored)
      ? "label" : "hide";

  // genuinely in between: dim and label, never disappear
  if (a.isAd.noul >= 0.45) return "dim";

  return "keep";
}

Why a number beats a verdict

  • The affiliate box scored 0.58. That is not a mistake, it is the truth: it is half editorial, half commerce. A model that answers “yes” or “no” hides that from you.
  • Two readers want two different blockers. One hides sponsored posts, one wants them labelled. Same answers, one setting, no re-prompting.
  • Thresholds are testable. Move 0.85 to 0.9, re-run your saved pages, count what changed. You cannot regression-test a paragraph.
  • The category carries the rest. A cookie banner is not advertising at all — it scored 0.11 — and it still goes, because the second question said what it was.

What the walk really looks like

  • One request, thirty questions. Independent questions over the same page run in parallel. Fifteen elements did not cost fifteen round trips.
  • Above the fold first. 96 ms still lands after first paint, so a real integration classifies the visible elements first and streams the rest as you scroll.
  • Cache by template, not by page. Every article on this site has the same shape; you are paying to classify a layout, and only once.
  • Same page, same answers. Re-running the scan gives the same distribution, so your screenshots and your tests stay stable.

Rules, prose, and judgment

A filter list is a list of things someone has already seen. An LLM understands the page but takes seconds and costs cents. Jev is the narrow middle: it only answers the question you defined, and it answers it fast enough to run on every element of every page.

Filter listLLMJev
Matches onSelectors and URLs a volunteer wrote down after seeing the ad.Meaning, read from the page text.Meaning, read from the page text.
Time per pageUnder a millisecond.2–5 seconds, after the page has already rendered.About 96 ms for the whole batch.
Cost per pageFree.~$0.012 at frontier prices. A thousand pages a day is $12.~$0.0001. A thousand pages a day is a cent.
An ad nobody has seen beforeInvisible until someone files a rule.Caught.Caught.
Renamed or rotating classesDefeated by a build step.Irrelevant — it reads the words.Irrelevant — it reads the words.
Native “paid partnership” postsSame markup as real articles. No rule can separate them.Caught.Caught, and labelled as such.
Borderline thingsNo concept of borderline.Gives you its opinion, not a dial.Gives you a calibrated number to threshold.
Output your code getsA boolean per rule.Free text or JSON you parse, often CSS selectors that break.A probability and a category per element.
Same page twiceIdentical.May differ on the awkward ones.Same distribution.
Runs on deviceYes.No — the page goes to a server.No — the page goes to a server.
Good atBlocking known ad servers at the network layer, for free, before a byte is fetched.The odd hard page, offline analysis, writing the rules and thresholds in the first place.Judging every element of every page, continuously, at a price that lets you.

A filter list knows what an ad looked like yesterday. A judgment model can tell you what this element is doing on the page today — and hand your code a number instead of an opinion.

The honest part

Keep your filter list

This is not a replacement. Rules block known ad servers at the network layer, for free, before a single byte is fetched. Classifying an element happens after the ad has already loaded — you saved the reader’s attention, not their bandwidth or their beacon. The sensible design is rules first, judgment for everything that has no rule yet.

The page leaves the device

Any model-based blocker sends page text to a server, which is an uncomfortable trade in exactly this product. On-device or self-hosted is the honest answer, and it changes the latency and cost numbers above. Worth saying out loud rather than burying.

Typed is not true

0.98 is not proof. Jev cannot return an option you did not define, so your parser never breaks — but it can still be wrong about an element, and over-blocking has a real cost. The filter list in this demo deleted a breaking news banner because of one bad rule; a bad threshold does the same thing at scale.

All the numbers are made up

Every probability, latency and price in the panels is invented for the demo. The figures behind them are TypeSafe’s published launch numbers — 70–500 ms per request, $42 per billion input tokens, output free, calibrated confidence on every answer. Jev has been in early access since 15 September 2026.

Nobody ships this yet

No mainstream blocker classifies elements with a hosted model today, and this page is a teaching demo rather than a proposal. What is real is the shape of the decision: a loop over elements, one narrow question each, a number back, and a policy in code.