Skip to content

every outbound link says it came from here - #2754

Merged
Flotapponnier merged 1 commit into
mainfrom
cherry/utm-external-links
Sep 30, 2026
Merged

Flotapponnier merged 1 commit into
mainfrom
cherry/utm-external-links

Conversation

@Flotapponnier

Copy link
Copy Markdown
Collaborator

Cherry-pick of 07c3f8c3 from dev (#2753). This commit only — dev carries 14 others that are deliberately left there.

utm_source=openchainbench on outbound links, applied at render time through withUtm() and never stored, so the JSON the API serves, the citations, the structured data and anything a reader copies stay canonical.

40 links across 25 files: provider sites, docs, repos and X profiles, harness sources, venue and market pages, block explorer transactions, our own GitHub and X.

Left clean on purpose: non-http(s) (mailto:, the cursor:// and vscode: MCP deeplinks), our own site and subdomains, URLs that already carry a utm_source, the Zenodo DOI and the Creative Commons deed, the machine-readable surfaces (/api/*, llms.txt, sitemap, JSON-LD), and RPC endpoint URLs — those are printed so a reader can paste them into a config.

A missed link renders perfectly and reports nothing, so the test greps the source rather than sampling behaviour: it fails on any literal or template-literal external href that is not wrapped, and asserts the inverse for the two modules that render RPC endpoints.

Verified on this branch after the pick: tsc --noEmit clean, 390 tests pass, diff against origin/main is exactly the 27 files of the original commit.

Providers ask how much traffic we send them, and the honest answer was
"look in your referrer report". For anyone arriving through a browser
that strips the referrer, there was no answer at all. Outbound links now
carry utm_source=openchainbench.

Applied at render time, never stored. The registry and the specs keep
clean URLs, so the JSON the API serves, the citations, the structured
data and anything a reader copies stay canonical; only the href a human
clicks is tagged. 40 links across 25 files: provider sites, docs, repos
and X profiles, harness sources, venue and market pages, block explorer
transactions, our own GitHub and X.

Four kinds are deliberately left clean, and withUtm enforces each:

  - anything that is not http(s). mailto:, tel:, #anchors, relative
    paths, and the cursor:// and vscode: MCP deeplinks on /mcp.
  - our own site, subdomains included. Tagging an internal link starts a
    new campaign mid-visit and corrupts session attribution in PostHog.
  - a URL that already carries utm_source, so a hand-written campaign
    link keeps its own attribution.
  - identifiers that happen to be URLs: the Zenodo DOI and the Creative
    Commons deed are cited verbatim and appear in JSON-LD as well as in
    prose, where a tracking parameter is wrong in both places.

RPC endpoint URLs are the case worth stating twice. They are printed so a
reader can paste them into a config, so a utm_source in one would be
copied into production. They never reach the helper, and a test asserts
that the two modules that render them do not import it.

The machine-readable surfaces are excluded too: /api/*, llms.txt, the
sitemap and the JSON-LD builders hand URLs to agents and to other
people's pages, where a tracking parameter ends up inside a citation or
somebody else's HTML.

Missing one of these is invisible: the link works and simply reports
nothing. So the test greps the source rather than sampling behaviour. It
fails on any literal or template-literal external href that is not
wrapped, which is what caught the 28 the first pass had left, and it will
fail on the next one somebody adds.
@Flotapponnier
Flotapponnier merged commit a974417 into main Sep 30, 2026
2 checks passed
@Flotapponnier
Flotapponnier deleted the cherry/utm-external-links branch September 30, 2026 19:42
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