TOF-447: Add docs CI gates: frontmatter, links, redirects, code samples, OpenAPI - #180
TOF-447: Add docs CI gates: frontmatter, links, redirects, code samples, OpenAPI#180tylergoerzen-mxp with Copilot wants to merge 4 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.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
Confidence Score: 1/5The PR does not appear safe to merge because several required documentation gates still accept the defect classes they are intended to block. Redirect-source links and redirect chains remain valid, code samples are never compiled or smoke-tested, and OpenAPI examples are not checked against their schemas. Files Needing Attention: scripts/check_links.py, scripts/check_code_samples.py, scripts/check_redirects.py, scripts/check_openapi.py
|
| Filename | Overview |
|---|---|
| .github/workflows/docs-ci.yml | Adds SHA-pinned, read-only CI execution for all five validation scripts. |
| scripts/check_frontmatter.py | Now enforces non-empty descriptions and unique titles, resolving the prior frontmatter finding. |
| scripts/check_links.py | Still accepts exact redirect sources and arbitrary wildcard-source children as valid links. |
| scripts/check_code_samples.py | Enforces language labels but does not compile or smoke-test sample contents. |
| scripts/check_redirects.py | Detects cycles and duplicate sources but deliberately accepts non-cyclic redirect chains. |
| scripts/check_openapi.py | Adds structural and whole-spec validation, but examples remain unchecked against schemas. |
Reviews (4): Last reviewed commit: "Add OpenAPI gate, enforce description an..." | Re-trigger Greptile
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>
| source, or matched by a wildcard redirect source. The last case is a | ||
| chained redirect, which the CDN follows to a final landing page.""" |
There was a problem hiding this comment.
Redirect chains still pass validation
When a non-cyclic redirect points to another redirect source, resolves() accepts that destination while the traversal rejects only cycles, causing the redirect CI gate to pass a chain that TOF-447 requires it to reject.
Source Used: Linear — AEO QW10: Add docs CI gates (links, code samples, frontmatter, redirects, OpenAPI)
| if not errors and spec_validate is not None: | ||
| try: | ||
| spec_validate(load(path)) |
There was a problem hiding this comment.
OpenAPI examples remain unchecked
When a specification contains an example value that violates its associated schema, this code performs whole-spec validation without comparing examples to their schemas, causing CI to accept invalid API examples that can render incorrect request or response data.
Knowledge Base Used: API Reference (reference/ and openapi/)
Source Used: Linear — AEO QW10: Add docs CI gates (links, code samples, frontmatter, redirects, OpenAPI)
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, 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 still runs structurally without it.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 specsMerge order
Merge this last. The frontmatter gate is red until TOF-439 (#172) and TOF-445 (#178) land, because it now requires a description on every page and globally unique titles. Verified against the #172 tree: every description error clears, leaving only the duplicate titles that #178 resolves. That is the intended sequence — the plan lists this quick win tenth precisely because it locks in the other nine.
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.