Skip to content

TOF-447: Add docs CI gates: frontmatter, links, redirects, code samples, OpenAPI - #180

Draft
tylergoerzen-mxp with Copilot wants to merge 4 commits into
mainfrom
copilot/tof-447-add-docs-ci-gates
Draft

TOF-447: Add docs CI gates: frontmatter, links, redirects, code samples, OpenAPI#180
tylergoerzen-mxp with Copilot wants to merge 4 commits into
mainfrom
copilot/tof-447-add-docs-ci-gates

Conversation

Copilot AI commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

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:

Gate Locks in
Frontmatter: title, description, uniqueness TOF-439 descriptions, TOF-445 titles
Internal links resolve to canonical routes TOF-441
Redirects: no chains, loops, or duplicate sources TOF-441
Code blocks declare a language TOF-438
OpenAPI specs parse and resolve TOF-447

Descriptions matter beyond hygiene: Mintlify generates llms.txt from that field, and /llms.txt was 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-empty title and description, and no two pages may share a rendered title
  • check_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 fail
  • check_redirects.py — no duplicate sources, no loops, and every destination resolves
  • check_code_samples.py — every fenced block declares a language
  • check_openapi.py (new) — all 14 specs parse, carry openapi/info/paths, and every local $ref resolves. Uses openapi-spec-validator for full schema validation when installed, and still runs structurally without it.

Fixes from review

check_redirects.py had an 88% false-positive rate. Its docstring says chained redirects are allowed, but it only matched exact sources and ignored the 461 wildcard sources in docs.json. It reported 25 errors on main of 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 → /self and /a → /b → /a. Both now fail.

check_links.py would 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.py mis-parsed nested fences — a ```python block inside a ````mdx block 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:

  • All five pass on this branch's content
  • Each fails on a deliberate bad fixture: empty title, bare fence, dead link, redirect loop, spec with a dangling $ref
  • check_openapi.py validates all 14 real specs

Merge 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 build and npm run dev passing in both.

@linear-code

linear-code Bot commented Aug 18, 2026

Copy link
Copy Markdown

TOF-447

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 (UTC)
mixpanel-docs 🟢 Ready View Preview Aug 18, 2026, 7:09 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Copilot AI changed the title [WIP] Add CI gates for docs validation including links and code samples Add docs CI gates: frontmatter, code samples, internal links, redirects Aug 18, 2026
Copilot AI requested a review from tylergoerzen-mxp August 18, 2026 19:07
@greptile-apps

greptile-apps Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 1/5

The 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

Important Files Changed

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

Comment thread scripts/check_links.py
Comment thread scripts/check_code_samples.py
Comment thread scripts/check_frontmatter.py Outdated
Comment thread scripts/check_redirects.py
Comment thread .github/workflows/docs-ci.yml Outdated
Comment thread .github/workflows/docs-ci.yml Outdated
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>
tylergoerzen-mxp added a commit that referenced this pull request Aug 20, 2026
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>
Comment on lines +89 to +90
source, or matched by a wildcard redirect source. The last case is a
chained redirect, which the CDN follows to a final landing page."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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)

Comment thread scripts/check_openapi.py
Comment on lines +123 to +125
if not errors and spec_validate is not None:
try:
spec_validate(load(path))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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)

@tylergoerzen-mxp tylergoerzen-mxp changed the title Add docs CI gates: frontmatter, code samples, internal links, redirects TOF-447: Add docs CI gates: frontmatter, links, redirects, code samples, OpenAPI Aug 20, 2026
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.

2 participants