DEV Community

Cover image for Why I'm betting sentence uniqueness beats page count for programmatic AdSense approval
MORINAGA
MORINAGA

Posted on

Why I'm betting sentence uniqueness beats page count for programmatic AdSense approval

The bet, stated plainly: fixing 251 rows of repeated boilerplate in my AI model directory will have a larger positive effect on the next AdSense review than publishing 50 additional unique pages would. Timeline: end of July 2026. If the next rejection still cites low-value content on model pages, the bet is wrong in the most direct way possible.

That's a testable claim. Most advice about programmatic AdSense approval focuses on adding content — E-E-A-T pages, more unique entries, editorial overlays. I've done all of that. Four rejections and several months of iteration later, I found something I hadn't looked at: sentence-level repetition inside what I thought was individually generated content.

How 467 identical sentences appeared in generated content

The AI tools directory uses a three-tier content quality ladder. HuggingFace's model metadata schema includes license tags, architecture strings, and pipeline task fields that each entry exposes via the Hub REST API: entries start as thin fallback stubs, get upgraded to metadata-enriched descriptions via scripts/polish.py, and eventually reach full LLM-generated prose via Claude Haiku. The LLM tier is rate-limited at 100 upgrades per daily run.

That rate limit creates a structural problem. The 1,449 model entries weren't all upgraded at once — they were processed in batches across dozens of daily runs, each asking Claude Haiku to generate pros, cons, a summary, and use cases. The prompt was consistent across runs, because consistency reduces per-entry generation cost.

Consistent prompt + similar input = similar output. When I ran a deduplication audit across the pros and cons fields, the numbers were worse than I'd expected:

  • 467 instances of the same sentence in the pros field across different model entries
  • 378 instances of a different repeated sentence in the cons field
  • 251 total rows where at least one field carried shared boilerplate

The two worst offenders were sentences that could plausibly apply to any HuggingFace model that uses the transformers library — which is most of them. The generation prompt didn't prevent this because it didn't know about other entries; each call was independent.

An AdSense reviewer scanning 20 random model pages and finding the same sentence on 15 of them sees a content mill, not a curated directory. The noindex gate kept the weakest entries out of Google's index, but the sentence-repetition problem existed in the indexed tier too.

The fix: deterministic rotation from existing metadata, no API

Calling Claude Haiku to regenerate all 251 rows would cost roughly $3 in API fees and re-introduce the same structural risk: run the same prompt over similar inputs, get similar outputs again. The deterministic hash pool approach I'd used for the initial fallback upgrades pointed toward a better path.

The new scripts/humanize-aiappdex.mjs script rebuilds affected rows from the metadata already present in each model's HuggingFace record — license type, parameter count, quantization formats, supported languages, architecture family, pipeline task, download rank. No network requests. No API calls. The only input is the existing apps/ai-tools/src/data/models.json file.

The uniqueness mechanism is a per-slug seed derived from an FNV-1a hash of the model's ID. When the script picks between equivalent phrasings — "supports 14 languages" versus "multilingual, covering 14 language pairs" — it uses (hash(slug) + facet_salt) % variants.length. Two models with identical metadata get different phrasing because their slugs hash differently.

function hash(str) {
  let h = 2166136261;
  for (let i = 0; i < str.length; i++) {
    h ^= str.charCodeAt(i);
    h = Math.imul(h, 16777619);
  }
  return h >>> 0;
}

function variant(arr, seed, salt) {
  return arr[(seed + salt) % arr.length];
}
Enter fullscreen mode Exit fullscreen mode

The script only touches rows carrying known-shared boilerplate. Rows where the existing prose was genuinely unique — either hand-written or generated with enough specificity that no pattern matched — are left unchanged.

After running: lint:humanization --strict went from 11 warnings to 0. The 251 rows now carry the metadata-derived tag, which excludes them from the LLM regen queue so a future Haiku run won't overwrite the fix.

The three approaches compared

Approach API cost Reproducible Prose quality Uniqueness risk
Keep existing boilerplate $0 Medium High (repeated across 251 rows)
Re-run Claude Haiku ~$3 No (stochastic) High Medium (same prompt → clustering)
Deterministic metadata rotation $0 Yes Medium Low (per-slug seed prevents clustering)

The tradeoff is prose quality. Metadata-derived sentences like "Licensed under Apache 2.0, making it suitable for commercial deployment without restrictions" read like database annotations, not editorial. LLM-generated prose — when the prompt is specific enough — sounds more natural. The pairwise model compare pages use LLM prose specifically because the comparative framing requires reasoning that metadata can't supply.

For pros and cons on a model listing page, though, the uniqueness constraint outweighs the prose-quality constraint. A sentence that's factually accurate, not repeated anywhere else in the directory, and grounded in real metadata is more valuable than a prettier sentence shared with 466 other pages.

The counterargument I take seriously

The strongest objection: AdSense reviewers don't read 251 model pages and compare sentences. They sample a handful of pages, check whether the page answers a search query, and look at structural signals — ad placement, navigation, content-to-template ratio. Sentence-level repetition in body text is below the threshold they're examining.

If that's correct, the fix won't help. The accidental low-value signals that triggered rejections three and four were things a human skimming for five seconds would notice: the methodology page saying AI summaries shouldn't be trusted, the llms.txt framing the site as an "experiment," generic author attribution. Those are visible at a glance. Identical sentences two paragraphs into a page body are not.

There's also a coverage argument. My E-E-A-T transparency pages were in place before the last rejection and didn't prevent it. Adding infrastructure that passes a lint check I wrote myself doesn't mean AdSense reviewers will weight it the same way.

I can't dismiss this objection. It might be exactly right. Sentence repetition might be completely invisible to human reviewers and irrelevant to Google's automated quality signals. The honest framing is that this intervention targets something I measured, not something I can verify was the causal factor in previous rejections.

Why I still think it's the right move

Two reasons that don't depend on the AdSense bet being correct.

First, the content quality gate enforces article-level quality before publishing to Dev.to. Extending that logic to directory entry content is consistent, not just hopeful. If I wouldn't publish a 600-word article with 12 repeated sentences, I shouldn't be fine with 251 model pages sharing prose.

Second, the metadata-derived text is factually superior to the LLM-generated boilerplate it replaces. The sentence "Loads via the HuggingFace transformers pipeline and supports standard inference APIs" was the repeated pro for 467 different models. It was technically true for all of them. It was also completely useless for distinguishing between them. Replacing it with a sentence that references the specific model's license, architecture, and language support gives a user something actionable, regardless of what AdSense thinks about it.

The AI directories vs Google AI Overviews bet was about structural positioning: can a curated directory offer something AI-generated summaries can't? This bet has the same structure. If the answer to "does this page deserve to exist?" is "yes, because it says something distinct about this specific model," then sentence-level uniqueness isn't just an AdSense concern — it's the editorial standard the directory should meet anyway.

What would change my mind

Three signals would tell me this bet was wrong.

Another rejection with the same language. If the next AdSense review still cites "low-value" or "insufficient content" after the humanization pass, and the rejection appears on model pages specifically, then sentence uniqueness wasn't the variable. I'd need to examine what the reviewer actually sampled before assuming I know what to fix next.

Engagement data with no difference. The humanization changes content on 251 pages. If time-on-page and scroll depth are flat across those pages relative to the unchanged 1,198, the metadata-derived text isn't providing value to users either. That won't reverse the fix — different phrasing per page is still better than identical phrasing — but it would tell me the prose quality tradeoff is real and meaningful.

Lint: humanization strict finds new patterns. The deduplication audit found 467 and 378 as the worst offenders. If a follow-up audit at the 500-row threshold finds a different sentence repeated 200 times in entries I thought were already unique, it means the audit approach is chasing the problem rather than preventing it. The fix to that is structural — changing how generation prompts work — not another deduplication pass.

The 30-day checkpoint

I'll resubmit the AI tools directory to AdSense by mid-July 2026. The result will either support or contradict the bet. I'll write the follow-up the same way I wrote the accidental low-value signals post: specific language from the rejection if there is one, what I inferred from it, what I did next.

If it passes on the first resubmission after the humanization fix, I can't claim causation — there have been other changes to the sites in the last few weeks. I can only report the sequence. That's the honest version of this kind of bet: you can measure the outcome, you can't always measure the cause.


FAQ

Why not use Claude Haiku to write the humanized content instead of metadata rotation?

Two reasons. The deterministic approach produces the same output every time from the same input, which means CI reruns don't alter content and the lint:humanization --strict check is stable. Stochastic LLM output passes the same lint check differently on each run. The second reason is cost predictability: 251 rows at Claude Haiku input/output rates is a few dollars, but if the audit keeps finding new patterns and requiring reruns, that accumulates. The zero-API-cost path runs in every CI environment without token budget concerns.

What's "lint:humanization --strict" checking exactly?

It scans all prose fields — summary, pros, cons, use_cases — across every entry in models.json and flags any string that appears identically in more than five rows. The --strict flag tightens that threshold to three. The count of 11 warnings before the fix corresponded to 11 distinct sentences repeated beyond the threshold; the fix eliminated all 11.

Does metadata-derived text work for models without rich HuggingFace tags?

For the 251 rows targeted, every model had at minimum a pipeline tag, a license, and a download count. The script degrades gracefully: if no license tag is present, it omits the license pro rather than generating a generic statement. If no language tags are present, the multilingual pro is skipped. The minimum output is a sentence anchored on architecture family and task — still unique per model because it references the model's actual architecture string.

If sentence repetition wasn't the cause of previous AdSense rejections, what was?

The four-rejection retrospective identified self-undermining language in the methodology page, a disclaimer advising users not to trust AI-generated summaries, and generic author attribution. Those were all fixed before the rejection that preceded this humanization work. The current hypothesis is that model page content quality is the next variable, but I don't have confirmation of that from a rejection letter — AdSense doesn't explain its reasoning in detail.

Can this approach generalize beyond AI model directories?

The deterministic rotation mechanism works for any directory with structured per-entry metadata that varies by entry. A restaurant directory with cuisine type, neighborhood, price tier, and outdoor seating could generate unique boilerplate for each combination using the same technique. The constraint is having enough metadata dimensions to rotate across — if every entry has the same tags, the per-slug seed produces unique ordering but the underlying facts are still the same.


Related: What I learned about accidental low-value signals after four AdSense rejections | Upgrading fallback AI model entries to curated quality with a deterministic hash pool

Part of an ongoing 6-month experiment running three AI-curated directory sites. The technical claims here are real; this article was AI-assisted.

Top comments (0)