Skip to content

Measured checks ad-heavy and text-warped; navigation paths in interaction analytics - #99

Open
Karanvir1729 wants to merge 7 commits into
mainfrom
strange-sites
Open

Karanvir1729 wants to merge 7 commits into
mainfrom
strange-sites

Conversation

@Karanvir1729

@Karanvir1729 Karanvir1729 commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

The owner asked for GenClass to "detect strange sites, like too many ads, warped text, and exact specifics of how a user navigates the site". This adds two measured visual checks and navigation paths to interaction analytics, within AGENTS.md rules 1, 2, 3 and 8.

Two measured visual checks (docs/runtime/VISUAL.md 3.25, 3.26; 3.24 is main's request-stalled)

Both are kind "check" (labelled as measured, never a model finding), report only (no runtime fix, nothing written to the page in any mode), run in the lazy chunk's idle slices on the full tier, and never feed situations, facts, the event log, the gate or the model.

ad-heavy. Ad-like regions: elements whose id or class contains the word ad, ads, advert…, adslot, adunit, adsbygoogle, sponsored (split into words, so header, badge, add-to-cart, adaptive don't match), the AdSense / Ad Manager / Prebid attributes, and cross-site iframes at a fixed ad size within 2 px. Only the outermost region counts. Thresholds (Coalition for Better Ads standards):

reason threshold severity
interstitial one fixed ad region covers ≥ 50% of the screen and is on top high
sticky one fixed ad region covers ≥ 30% of the screen medium
density ad regions cover ≥ 30% of the page medium
first-screen ad regions cover ≥ 50% of the first screen medium

It reports the share of the first screen, the share of the page, the fixed share and the count. A reason must still hold 2 s later, after network quiet.

text-warped. Text a reader cannot read as text. It is measured from Range.getClientRects() line boxes, rects, the computed transform chain and code points, never from the wording:

reason threshold
overlap two line boxes overlap by ≥ 50% of the smaller height (and 8 px horizontally), within an element (not under an ellipsis or line clamp, whose truncated line Chromium gives two nested boxes) or between two elements with nothing painted between them
clipped an overflow box cuts the first line: ≥ 4 px past its edge, or only 20–80% of its height shown; not with an ellipsis, a line clamp, a mask, a transform or a scroller
tofu lone surrogates or unassigned code points (U+FFFD stays with text-encoding)
mirrored / skewed transform determinant < 0 (not behind backface-visibility: hidden); skew > 20° or stretch ≥ 2x; pure rotation is never reported
spacing letter-spacing ≥ 0.5 em with two or more words, or ≤ -0.2 em; word-spacing ≤ -0.2 em
tiny drawn below 8 px with a line box under 10 px, for opaque text with at least 3 letters that is not pointer-events: none

False reports and recall (docs/runtime/RESULTS.md section 22)

Run on the development Mac in Chromium 153. The faults and fixtures were written by the author of the checks, so recall here is a mechanism check, not an independent figure.

false reports recall
Chromium fixtures (test/browser/visual, 49 tests) 0: clean-strange.html desktop and phone, and the 44 existing fixtures' exact sets are unchanged 12 / 12 (2 ad-heavy, 10 text-warped across the 7 reasons)
bench, demos (28 clean page views, every kind) 0 text-warped 95 / 95, ad-heavy 14 / 14
bench, templates (16 clean page views, every kind) 0 text-warped 15 / 15, ad-heavy 8 / 8
bench, compat apps and public sites not run: visual-measure.yml loads about 40 public sites, which is the owner's call –

The only finding outside the planted faults on the fault loads is a real layout-shift: the harness inserts the ad slots after load, so the content does move.

Cost (demos, same machine, main's engine against this branch): first pass p50 15.1 → 21.4 ms at 1x and 70.5 → 78.8 ms at 4x. The longest slice is unchanged (≤ 13.9 ms at 4x). One of 7 throttled loads ends its first pass as partial.

First load: the code is byte-identical to main. first-load-size.mjs shows +3 B / +0 B, and that is only the hashed names of the lazy chunks. The lazy visual chunk grows by +6.7 KB gzip and the analytics chunk by +0.57 KB.

Navigation paths in interaction analytics (docs/runtime/ANALYTICS.md section 11)

This is still off unless the app sets analytics: true with its token. Every telemetry opt-out and GPC still applies. Element names are the existing keys (data-genclass-name, test id, id, link target, classes, short path), never text.

What the interactions events now also send (this widens rule 8, at the owner's request):

  • nav on a view's first event: load, reload, back_forward, push, back, forward or pop. The type comes from Navigation Timing and the direction from the Navigation API's entry index.
  • from and fromMs: the previous view's route in the same page load and the ms between the two.
  • first and firstMs: the element key of the first click, or of the first keyboard focus on a form field, and the time to it. Sent once per view.
  • exit: true and path on the pagehide event: the page load's routes in order, at most 10, then "(more)".

Never sent: the referrer, history entry keys, or anything that links two page loads. A full page navigation therefore starts a new path.

The collector adds the ix_entry, ix_nav, ix_nav_ms, ix_arrive, ix_first, ix_first_n, ix_first_ms, ix_exit and ix_path counters, within the same per-batch budget and with no migration. The dashboard's Visitors view gains "How visitors move through this app": paths, page-to-page steps with average time, entries, exits, back/forward use and the first interaction per page. It is hidden for runtimes that don't send these fields.

TELEMETRY.md, PRIVACY.md (dated 2026-10-25), both READMEs, the console notice, the InitOptions.analytics JSDoc, API.md, the CHANGELOG, the dashboard footer and the telemetry-worker README now say exactly what is added. The site's privacy page lives in genclass-site and is not touched here.

Checks run (after merging main at 651aa1c: PRs 95 and 98)

  • npm run typecheck -w @genclass/runtime and npm run build -w @genclass/runtime: clean
  • runtime vitest, CI step 1: 1193 passed, 14 skipped (the model-parity tests); review-perf: 4 passed; no review test edited
  • git diff --stat packages/runtime/src/situation packages/runtime/src/util.ts: empty (situation-v2.3 unchanged)
  • npx playwright test --config test/browser/visual/playwright.config.ts in Chromium: 49 passed. In one earlier full run the "10,000 elements with hover" test saw one 52 ms long task with the checks' longest slice at 9.7 ms; it passed 4/4 on its own and in the two full runs after
  • telemetry-worker vitest: 299 passed
  • node test/first-load-size.mjs against main's dist (651aa1c): +3 B / +0 B, within the 0.6 KB limit

Not run: the compat and public-site bench (above), Firefox and WebKit.

🤖 Generated with Claude Code

Karanvir1729 and others added 7 commits October 25, 2026 01:32
…on analytics

ad-heavy (VISUAL.md 3.24): the share of the first screen and of the page that ad
slots cover (named by id, class or ad attributes, or cross-site frames at fixed
ad sizes), against the Coalition for Better Ads thresholds, plus fixed and
interstitial ads. text-warped (3.25): overlapping line boxes, lines cut by their
overflow box, lone surrogates and unassigned code points, mirrored or skewed
text, letter spacing that splits words, text drawn below 8 px. Both are kind
"check", report only, in the lazy chunk's idle slices, never model input.

Interaction analytics (ANALYTICS.md 11): how each view was reached, the previous
route and the time between, the first interaction per view, and at pagehide the
exit and the ordered path; collector counters and a dashboard section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nly findings; RESULTS section 21

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	packages/runtime/CHANGELOG.md
# Conflicts:
#	docs/runtime/API.md
#	docs/runtime/RESULTS.md
#	docs/runtime/VISUAL.md
#	packages/runtime/CHANGELOG.md
#	packages/runtime/README.md
#	packages/runtime/src/types.ts
#	packages/runtime/src/visual/engine.ts
#	packages/runtime/test/visual-checks.test.ts
…romium gives the clamped line two nested boxes)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	PRIVACY.md
#	docs/runtime/API.md
#	packages/runtime/CHANGELOG.md
#	packages/runtime/TELEMETRY.md
#	packages/runtime/src/analytics/index.ts
#	packages/runtime/src/types.ts
#	packages/runtime/test/analytics.test.ts
#	telemetry-worker/README.md
#	telemetry-worker/src/dashboard.ts
#	telemetry-worker/src/pages.ts
#	telemetry-worker/src/sample.ts
#	telemetry-worker/test/pages.test.ts
# Conflicts:
#	packages/runtime/README.md
#	packages/runtime/test/analytics.test.ts

This branch has not been deployed

No deployments
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.

1 participant