Skip to content

TOF-441: Flatten redirect chains and fix internal links to redirected URLs - #174

Merged
hywel-mixpanel merged 45 commits into
mainfrom
copilot/tof-441-flatten-redirects
Sep 30, 2026
Merged

hywel-mixpanel merged 45 commits into
mainfrom
copilot/tof-441-flatten-redirects

Conversation

Copilot AI commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

docs.json carried 873 redirect rules, and hundreds of them pointed at another redirect source rather than a live page. Content files also linked directly to redirect sources instead of canonical URLs.

Relates to: https://linear.app/mixpanel/issue/TOF-441/aeo-qw4-flatten-redirect-chains-and-fix-internal-links-to-redirect

Why this matters for AEO

Redirect chains are invisible to a human clicking a link and expensive for everything else.

  • Google's own guidance is to redirect to the final destination and to link to canonical pages directly. Every extra hop is latency and an opportunity for a crawler to stop early.
  • AI crawlers are now the majority of docs traffic — agents account for 51.8% of intentional documentation reads (GitBook, 61.2M pageviews, May 2026). They fetch far more aggressively than humans and are less forgiving of multi-hop routing.
  • Internal links pointing at redirect sources dilute the canonical URL. When our own docs link to the old path, that is the URL that gets discovered and cited, even though it is not the page we want cited.

This is the least glamorous item in the plan and the most mechanically verifiable: the acceptance criterion is a number, and the number is now zero.

Result

Check Before After
Redirect chains 216 0
Redirect loops 0 0
Duplicate sources 0 0
Internal links pointing at a redirect source 123 0
Total rules 873 873

Rule count is deliberately unchanged. Old routes may hold external backlinks, so this flattens destinations rather than deleting anything.

Changes

  • docs.json — every chained destination resolved through the graph to its final landing page, in one pass rather than per-hop
  • 58 content files — 126 internal links repointed from redirect sources to canonical URLs

Corrections made during review

24 chains survived the first pass because the flattening matched on the full destination string including its #fragment, so anything with an anchor never matched its source. A second pass strips the fragment before lookup.

15 links kept an anchor that no longer exists on the new destination. These were already stale on main — the old page was deleted and the fragment rode through the redirect — but the rewrite would have hard-coded them. Each replacement was verified against the target page's actual headings. Two were landing on the wrong page entirely: /docs/reports#company-profiles and #activation-metrics both belong to group-analytics.

9 anchors were dropped rather than guessed where the heading is genuinely gone. A link to the top of the right page beats a link to a dead anchor.

17 redirect destinations pointed at onboarding-playbook subpages that do not exist. Only guides/strategic-playbooks/onboarding-playbook.mdx is real. The PR had already repointed the content links there; the map itself was still dangling.

Note on sequencing

PR #180 adds a CI gate that fails the build on new chains, loops, and duplicate sources. That gate can only be enforced once this merges. Merge this first.

@linear-code

linear-code Bot commented Aug 18, 2026

Copy link
Copy Markdown

TOF-441

… URLs

Co-authored-by: tylergoerzen-mxp <259741734+tylergoerzen-mxp@users.noreply.github.com>
@mintlify

mintlify Bot commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
mixpanel-docs 🟢 Ready View Preview Sep 30, 2026, 11:11 PM

Co-authored-by: tylergoerzen-mxp <259741734+tylergoerzen-mxp@users.noreply.github.com>
Copilot AI changed the title [WIP] Flatten redirect chains and fix internal links to redirect sources TOF-441: Flatten redirect chains and fix internal links to redirected URLs Aug 18, 2026
Copilot AI requested a review from tylergoerzen-mxp August 18, 2026 18:57
@greptile-apps

greptile-apps Bot commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[Low risk] Updates internal documentation links to match reorganized page structure.

The PR is not ready to merge until the Agent Intelligence setup example reliably produces spans with the required user identifier.

Reviews (28) · Last reviewed commit: "Merge origin/main into copilot/tof-441-f..."

Comment thread docs.json
The URL rewrites preserved #fragments that no longer match a heading on
the new destination, so 15 links landed at the top of the right page or,
in two cases, on the wrong page entirely. Each replacement was checked
against the target page's actual headings.

Notable: two changelog links pointed at /docs/reports for company
profiles and activation metrics, which live on group-analytics; and
/docs/reports#custom-buckets was a self-link to an anchor that no longer
exists, now pointing at /docs/features/custom-buckets.

Also repoint 17 redirect destinations aimed at onboarding-playbook
subpages that do not exist. Only guides/strategic-playbooks/
onboarding-playbook.mdx is real, and the PR already sent the content
links there, so the map now agrees with them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rec #22's acceptance is zero chains, loops, or duplicate sources. The
earlier pass fixed the 24 caused by fragment-matching; 40 remained where
a destination matched a wildcard source such as /changelogs/* or an
exact source that had itself been repointed.

Resolved each destination through the redirect graph to its final
landing page. Where the inherited #anchor no longer exists on that page,
dropped the anchor rather than hard-coding a dead one (9 cases).

Rule count unchanged at 873 — flattened only, nothing deleted, so
external backlinks on old routes still resolve.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Fix 13 residual chains the first pass missed (trailing slash on
  /docs/ and ?sdk= query strings) and 3 dangling destinations that
  existed on main
- Repoint ~50 links from main's AI-guides reorg to their canonical
  /guides/mcp, /guides/mixpanel-agent, /guides/use-mixpanel-ai paths
- Fix stale anchors: onboarding-playbook/plan/tracking-strategy#...,
  mixpanel-agent/use-cases#rca-agent and #experiments-agent,
  lookup-tables double-hyphen typo, pricing group-keys anchor

Verified with PR #180's check_redirects.py and check_links.py (both pass).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tylergoerzen-mxp

Copy link
Copy Markdown
Contributor

@mbocianski conflicts are resolved: merged main and pushed two commits so you can review them separately.

8298f05: merge only. Took main's side in all 4 content conflicts, because each points at a more specific live page than the generic onboarding-playbook fallback (product-analytics-strategy/get-started, developer-environments, guides-by-topic/govern-data). docs.json kept main's new AI-guide redirects.

bc49444: re-flatten after the merge. Main's AI-guides reorg (#182) added redirects and new links, so the PR's zeros stopped holding:

  • 13 residual chains the first pass missed (/docs/ trailing slash, ?sdk=httpapi query) plus 3 dangling destinations already on main
  • ~50 links repointed to /guides/mcp, /guides/mixpanel-agent, /guides/use-mixpanel-ai
  • stale anchors fixed against real headings (onboarding-playbook/plan/tracking-strategy#…, use-cases#rca-agent / #experiments-agent, lookup-tables -- typo, pricing group-keys)

PR #180's check_redirects.py and check_links.py both pass on the merged tree (882 rules; main added 9).

Left alone on purpose: /changelogs/<slug> links that hit the /changelogs/* catch-all (fixing them means guessing anchors), and a #group-keys-tracked-as-event-properties anchor that's broken on 14 SDK pages on main that this PR doesn't touch.

tylergoerzen-mxp and others added 2 commits September 25, 2026 12:48
- Drop stray "?" before tab anchors on install-mixpanel links
- Make the "Mixpanel format" links relative (one pointed at mixpanel.com/docs)
- Fix "Mixpanel's uses" typo in single-sign-on

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tylergoerzen-mxp

Copy link
Copy Markdown
Contributor

@mbocianski this is current with main again and ready for another look. Because main moved and I pushed new commits, it needs a fresh approval on the latest push (cd07c295) before it can merge.

What changed since your review

  • e215fafe: merged main (3 commits: GDPR/CCPA export docs, end-user-data-management edit, audit-logs publish). No conflicts.
  • cd07c295: small cleanups on top of your edits. Your edits are otherwise untouched.
    • Dropped the stray ? in install-mixpanel?#javascript, ?#python, ?#react-native, ?#ios-swift
    • amazon-s3.mdx pointed "Mixpanel format" at mixpanel.com/docs/... (wrong host); google-cloud-storage.mdx used an absolute docs.mixpanel.com URL. Both are now /docs/data-structure/concepts.
    • "Mixpanel's uses a SCIM spec subset" → "Mixpanel uses a SCIM spec subset"

I checked your tab anchors (#javascript, #ios-objective-c, #unity, etc.) against the rendered page. They all match Mintlify's tab ids.

Re-verified on the merged tree (script checks exact and wildcard sources, ignoring #fragment, ?query, and trailing slash)

Check main This PR
Redirect chains 251 0
Redirect loops 0 0
Duplicate sources 0 0
Dangling redirect destinations 2 0
Content links to a redirect source 177 7
Content links to a missing page 0 0
New broken anchors vs. main n/a 0
  • All 882 redirect sources are unchanged and in the same order. Only destinations moved.
  • The 7 remaining links are /changelogs/<slug> links caught by the /changelogs/* catch-all, left alone as noted earlier.
  • The Warehouse Sync Monitoring redirect from Remove Warehouse Sync Monitoring docs page and add redirect #251 lands directly on data-volume-monitoring, so no new chain.

One question for you: sdks/ios.mdx is the deprecated Objective-C SDK page, but its quickstart link now goes to #ios-swift. Was that intentional, to push people to Swift? If not, #ios-objective-c is the matching tab.

tylergoerzen-mxp added a commit that referenced this pull request Sep 25, 2026
… redirect chains; validate OpenAPI examples

The frontmatter gate failed on main content (362 pages missing a
description, 14 duplicate-title groups), so it would have blocked every
unrelated PR. Known violations now live in scripts/docs-ci-baseline.json
and only new ones fail. Entries that get fixed are reported, never fatal,
so TOF-439 (#172) and TOF-441 (#174) can land without touching it.

- check_redirects: reject chains (destination is itself redirected);
  229 existing chains baselined for #174. Loops reported once per cycle.
- check_openapi: validate media-type, parameter and schema-level examples
  against their schemas; fix the one invalid GDPR example.
- check_code_samples: tag two bare fences that landed on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tylergoerzen-mxp and others added 2 commits September 25, 2026 13:16
…en-redirects

# Conflicts:
#	docs/tracking-methods/warehouse-connectors.mdx
#	troubleshooting/faqs.mdx
The FAQ split (#177) moved three links this PR had already fixed into new
per-topic pages, where they pointed at redirect sources again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tylergoerzen-mxp

Copy link
Copy Markdown
Contributor

@mbocianski I merged main again after #173, #175, and #177 landed. This needs a fresh approval on 34486ca2.

  • d845fdb9 is the merge. warehouse-connectors.mdx keeps this PR's links and your wording, using main's straight quotes. faqs.mdx takes main's new stub.
  • 34486ca2: the FAQ split moved 3 links this PR had already fixed into the new per-topic pages, where they pointed at redirect sources again. They're repointed now (building-reports, identity-management, managing-projects).
Check main This PR
Redirect chains 251 0
Redirect loops 0 0
Duplicate sources 0 0
Dangling redirect destinations 2 0
Content links to a redirect source 177 7 (all /changelogs/<slug> catch-all, unchanged)
Content links to a missing page 1 1 (intentional /docs/quickstart.md example on the new Docs for AI Agents page)
New broken anchors vs. main n/a 0

The new merges added no redirect chains. The redirect count is still 882.

Comment thread docs/tracking-methods/sdks/ios.mdx Outdated
@mbocianski

Copy link
Copy Markdown
Contributor

@tylergoerzen-mxp just need an approval with write access. Looks good. Changed that one link to objective C as you mentoned.

Resolve conflict in properties.mdx by keeping the PR side, which includes
main's group-key anchor fix (#261) plus the redirect-source repoint.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@hywel-mixpanel
hywel-mixpanel merged commit 4a8d7c7 into main Sep 30, 2026
6 checks passed
@hywel-mixpanel
hywel-mixpanel deleted the copilot/tof-441-flatten-redirects branch September 30, 2026 23:22
hywel-mixpanel added a commit that referenced this pull request Oct 1, 2026
Resolve docs.json conflict by taking main's redirect destination from #174,
which adds a valid #group-profiles anchor.

Accept a trailing .md on internal links in check_links.py: Mintlify serves
every page as Markdown at <path>.md, which docs-for-ai-agents.mdx links to.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
hywel-mixpanel added a commit that referenced this pull request Oct 1, 2026
…es, OpenAPI (#180)

* Initial plan

* Add docs CI gates: frontmatter, code samples, links, redirects

Co-authored-by: tylergoerzen-mxp <259741734+tylergoerzen-mxp@users.noreply.github.com>

* Fix false positives and false negatives in the docs CI gates

check_redirects.py
- Honour wildcard redirect sources when resolving a destination. The
  docstring says chained redirects are allowed, but only exact sources
  were matched, so the 461 wildcard sources were ignored. That produced
  25 errors on main of which only 3 were real: an 88% false-positive
  rate that would have fired again on the next redirect anyone added.
- Detect loops. Every node in a cycle is also a source, so the
  chained-redirect rule silently swallowed /self -> /self and /a -> /b -> /a.

check_links.py
- Blank out fenced and inline code before extracting links, so a page
  documenting an example <a href="/docs/..."> does not fail CI. Line
  numbers are preserved.

check_code_samples.py
- Track fence length so a ```python block nested in a ````mdx block does
  not close the outer block early. Drop the unused FENCE_OPEN_RE and
  report repo-relative paths instead of absolute ones.

check_frontmatter.py
- An empty title no longer passes.

Verified: all four pass on this branch, and each rejects a deliberate
bad fixture (empty title, bare fence, dead link, redirect loop).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Add OpenAPI gate, enforce description and unique titles, pin actions

Completes rec #20 Part B's gate list.

- New check_openapi.py: parses all 14 specs, requires openapi/info/paths,
  and resolves every local $ref. Uses openapi-spec-validator for full
  schema validation when installed, and still runs structurally without
  it. Passes on all 14 specs today; rejects a spec with a dangling $ref.
- check_frontmatter.py now requires a non-empty description and fails on
  duplicate rendered titles, which rec #11 asked for.
- Pin actions/checkout and actions/setup-python to commit SHAs, matching
  stale.yml. Collapse four near-identical jobs into one with ordered
  steps, and add a concurrency group.

MERGE ORDER: the frontmatter gate is red until #172 (description
backfill) and #178 (title dedupe) land. Verified against the #172 tree:
all description errors clear, leaving only the duplicate titles that
#178 resolves. Merge this last, as the plan intends.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Baseline existing violations so the gates only block new ones; reject redirect chains; validate OpenAPI examples

The frontmatter gate failed on main content (362 pages missing a
description, 14 duplicate-title groups), so it would have blocked every
unrelated PR. Known violations now live in scripts/docs-ci-baseline.json
and only new ones fail. Entries that get fixed are reported, never fatal,
so TOF-439 (#172) and TOF-441 (#174) can land without touching it.

- check_redirects: reject chains (destination is itself redirected);
  229 existing chains baselined for #174. Loops reported once per cycle.
- check_openapi: validate media-type, parameter and schema-level examples
  against their schemas; fix the one invalid GDPR example.
- check_code_samples: tag two bare fences that landed on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Stop tracking Python bytecode

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Note the baseline in the workflow

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fail wildcard redirect destinations that match no page

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fix docs CI violations on pages added since the gates were written

Add missing descriptions to agent-intelligence and wingify, and tag three
bare code fences in audit-log-streaming as text.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tylergoerzen-mxp <259741734+tylergoerzen-mxp@users.noreply.github.com>
Co-authored-by: Tyler Goerzen <tyler.goerzen@mixpanel.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: no value <270450525+hywel-mixpanel@users.noreply.github.com>
Co-authored-by: hywel-mixpanel <hywel.wong@mixpanel.com>

This branch was successfully deployed

1 active deployment
staging — e54bcdf6 Deployed Sep 30, 2026 by mintlify[bot]
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.

5 participants