TOF-447: Add docs CI gates: frontmatter, links, redirects, code samples, OpenAPI - #180
tylergoerzen-mxp with Copilot wants to merge 10 commits into
Conversation
Co-authored-by: tylergoerzen-mxp <259741734+tylergoerzen-mxp@users.noreply.github.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
|
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>
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>
Rec #11 asks for a CI uniqueness test on rendered titles, which only passes at zero duplicates. Three pairs survived the first pass: - Data Pipeline Integrations: the old-pipelines copy is now marked (Legacy), keeping "Integrations" as its sidebar label - Lookup Tables: the reference page is the API, so it becomes Lookup Tables API - Mixpanel Headless: the guide gets the benefit-style title, leaving the bare product name to the docs page Verified zero duplicate titles across the tree, so the frontmatter gate in #180 can enforce uniqueness once this and #172 land. 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>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… 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>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Ready for review. Why the job was failingThe checker wasn't buggy. The frontmatter gate correctly found real problems already on What changed
What each gate enforces
None of the gates are warn-only. Updating the baselineAfter a cleanup PR merges, prune the fixed entries: python scripts/check_frontmatter.py --update-baseline
python scripts/check_redirects.py --update-baselineDon't use Out of scopeGreptile flagged that the Slack Bot and Warehouse Sync Monitoring redirects in |
Adds five Python validation scripts and a GitHub Actions workflow that block merges when docs quality invariants break.
Relates to: https://linear.app/mixpanel/issue/TOF-447/aeo-qw10-add-docs-ci-gates
Why this matters for AEO
Search engines and assistants redistribute code samples verbatim. A broken snippet does not just fail one reader — it propagates at scale into answers, repos, and other people's docs. The Next.js page in TOF-438 had shipped a snippet importing a function that was never defined; that is the class of defect this exists to stop.
The rest of the plan is a one-time cleanup that decays without enforcement. Every gate here corresponds to work in another quick win:
Descriptions matter beyond hygiene: Mintlify generates
llms.txtfrom that field, and/llms.txtwas the single most-fetched page by AI agents in the trailing 30 days at 7,833 requests. A page without a description renders bare in the index agents actually read.The gates
check_frontmatter.py— every page needs a non-emptytitleanddescription, and no two pages may share a rendered titlecheck_links.py— internal links must resolve to a real page or a redirect source; code fences and inline code are excluded so a page documenting an example link does not failcheck_redirects.py— no duplicate sources, no loops, no chains (a destination that is itself redirected), and every destination resolvescheck_code_samples.py— every fenced block declares a languagecheck_openapi.py(new) — all 14 specs parse, carryopenapi/info/paths, and every local$refresolves. Usesopenapi-spec-validatorfor full schema validation when installed, and then checks every example (media-type, parameter, and schema-level) against the schema it illustrates. Still runs structurally without the validator.Fixes from review
check_redirects.pyhad an 88% false-positive rate. Its docstring says chained redirects are allowed, but it only matched exact sources and ignored the 461 wildcard sources indocs.json. It reported 25 errors onmainof which 3 were real. A gate that noisy gets disabled in a week.It also could not see loops. Every node in a cycle is itself a source, so the chained-redirect rule silently swallowed
/self → /selfand/a → /b → /a. Both now fail.check_links.pywould have failed any future page showing an example link inside a code fence. Zero occurrences today, which is exactly when it is cheap to fix.check_code_samples.pymis-parsed nested fences — a```pythonblock inside a````mdxblock closed the outer one early.Actions are now pinned to commit SHAs, matching
stale.yml. Four near-identical jobs collapsed into one with ordered steps, plus a concurrency group.Verification
Each gate was executed, not just reviewed:
$refcheck_openapi.pyvalidates all 14 real specsExisting violations: baselined, not blocking
The first CI run failed because the frontmatter gate correctly found real problems already on
main: 362 pages without adescriptionand 14 groups of duplicate titles. The checker wasn't wrong, but a gate that fails every unrelated PR on day one would get disabled.So each check can list known violations in
scripts/docs-ci-baseline.json. Only new violations fail. A new page without a description, a new duplicate title, or a new redirect chain is blocked; old ones aren't.mainfixed here)$refs, schema, examples)Baseline entries that stop occurring print a note but never fail. So the cleanup PRs can land in any order without touching the baseline. Verified against each tree: #172 clears all 362 description entries, #178 clears all 45 duplicate-title entries, #174 clears all 229 chains. Prune afterwards with:
Merge order no longer matters. This PR can merge before or after #172, #174, and #178.
Not implemented
Rec #20 Part B asks that priority code examples be compiled or smoke-tested, not just checked for a language tag. That needs fixtures or runnable example projects per language and is a larger piece of work. The Next.js page was instead verified by hand for TOF-438: both routers scaffolded with
create-next-app, every snippet transcribed verbatim,npm run buildandnpm run devpassing in both.