Skip to content

chore(pages): replace the gh-pages docs mirror with a branded link stub - #62

Merged
uipreliga merged 7 commits into
mainfrom
chore/pages-stub-redirect
Jul 28, 2026
Merged

chore(pages): replace the gh-pages docs mirror with a branded link stub#62
uipreliga merged 7 commits into
mainfrom
chore/pages-stub-redirect

Conversation

@uipreliga

@uipreliga uipreliga commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Why

uipath.github.io/coder_eval/ was serving a second live copy of docs/, duplicating coder-eval.com/docs. Verified before this change:

  • Both copies self-canonicalized — the mirror declared <link rel="canonical" href="https://uipath.github.io/coder_eval/"> rather than deferring to coder-eval.com. Google elects one canonical per duplicate cluster, and nothing told it which to pick.
  • uipath.github.io/robots.txt is 404, so nothing blocked crawling, and the mirror shipped its own sitemap.xml actively advertising all 14 URLs for indexing.
  • The mirror had drifted: it still served /BYOD/ and /CODEX_AGENT_GUIDE/, pages renamed upstream to DATASETS.md and agents/CODEX.md. So the risk wasn't only duplication — the copy competing with us was out of date.

There's no penalty for duplicate content, but the mechanical costs are real: split link equity on a domain whose measured problem is a shortage of inbound links, and Search Console reporting "Duplicate, Google chose different canonical" for pages we're verified for — optimizing without feedback.

What

Replaces the MkDocs deploy with a single branded stub page.

  • .github/pages-stub/index.html — Coder Eval wordmark, a one-sentence description of what the product is, a link to the docs, and a brand-orange bar linking to this repo with its live star/fork counts.
  • .github/workflows/docs.yml — publishes that stub instead of running mkdocs gh-deploy, force-replacing gh-pages so none of the old output (sitemap included) survives. 404.html is a copy of the stub, so Pages serves it for every old deep URL without needing a file per route.

Decisions worth reviewing

noindex, follow rather than a canonical to /docs. The stub's content differs from the target; Google discourages combining noindex with rel=canonical and ignores canonicals across substantially different pages. This takes the duplicate cluster to zero indexed pages rather than one.

The mkdocs deploy is deleted, not commented out. It had already been commented out once, and that is exactly how the duplicate got published — one uncomment away. mkdocs.yml itself is untouched and still required: CE028 generates the index surfaces from its nav:, the website derives its sidebar from it, and mkdocs build still verifies anchors locally. Only the publishing of a second site is gone.

One logo copy, not two. The wordmark is inlined from the website's src/assets/logo-light.svg. The light and dark variants differ in only four navy fills (three icon bars and the "Coder" text group — "Eval" and the arc are always brand orange), so those became currentColor and one 3.5 KB copy serves both colour schemes. The substitution was scripted with assertions that it recoloured exactly four fills and left #FA4616 intact — no path data was hand-copied. The page also moved onto the real brand palette (#FA4616, #10233A, #F5F7FA) from my earlier approximations.

Counts are never hardcoded. A committed star count is wrong within days, so the markup ships spinners and the script fills them from api.github.com. Anything that isn't a live number stays a spinner — offline, non-2xx, missing field, or scripting disabled. Resolution is per cell, so a partial payload fills what it has. Accepted tradeoff: with JS off the spinner never resolves, which is the price of never publishing a stale number on a page nobody should land on. Unauthenticated API is 60 req/hour/IP.

An earlier revision carried a snapshot value plus restore-on-failure, a 4s AbortController deadline and a race guard; dropping the hardcoded values made all of it dead weight, so the script is now 10 lines.

The page says what Coder Eval is. It previously named the product and said the docs had moved, so anyone arriving from a stale link had to click through to learn what it was. It now leads with a one-sentence description, worded to match coder-eval.com and the package metadata so the product is described identically everywhere. There is a matching <title> and meta description — but note those are for link previews, not ranking: the page is noindex, so search engines will not surface it. A comment above them says so, so they aren't later mistaken for SEO copy doing work it isn't.

No text is repeated. The wordmark already renders "Coder Eval", so the <h1> beneath it was the same two words twice; the heading is now visually hidden, keeping document structure and serving as the accessible name (the logo is aria-hidden, which also stops a screen reader announcing the name twice). Linking coder-eval.com/docs inside the notice then made the "Documentation" list row a second copy of the same URL two lines apart, so that row is gone. Each destination appears exactly once: docs in the notice, the project site in the list, the repo in the bar.

Verification

  • Workflow YAML parses; the assemble step dry-run produces index.html, 404.html, .nojekyll, llms.txt.
  • All three outbound links return 200.
  • Renders in both colour schemes (confirmed for light by deleting the dark @media rule at runtime, so the wordmark's currentColor was actually exercised).
  • Counts: live load fills the real values; network reject, a request that never settles, and a payload missing forks_count each leave the spinner in place. Markup contains no numeric literals for the counts.
  • make lint passes (166).

After merge

This needs one manual workflow_dispatch run of the Pages stub workflow from the Actions tab — merging alone does not replace the live mirror.

Not in scope

  • The sharp HIGH Dependabot alert on main (transitive in evalboard, fixed in 0.35.0) is pre-existing and unrelated.
  • /llms.txt on the website was pointing three links at the uipath.github.io mirror; that is fixed separately in the website repo, along with adding /llms-full.txt.

uipreliga and others added 4 commits July 28, 2026 12:58
The MkDocs build published to gh-pages was a second live copy of docs/,
duplicating coder-eval.com/docs. Both copies self-canonicalized, which
splits search signals and lets the wrong host rank for our own docs — and
the mirror had drifted, still serving BYOD/ and CODEX_AGENT_GUIDE/ after
those pages were renamed upstream, with a sitemap.xml advertising all 14
stale URLs for indexing.

Replace it with a single static page linking to coder-eval.com/docs, the
project site, and the repo. Pages serves 404.html for unknown paths, so the
same stub catches every old deep URL without a file per route. It carries
`noindex, follow` rather than a canonical pointing at /docs: the stub's
content differs from the target, and Google discourages combining noindex
with rel=canonical and ignores canonicals across substantially different
pages. Net effect: the duplicate cluster drops to zero indexed pages.

The mkdocs gh-deploy step is removed, not commented out — left one
uncomment away, a future run would silently restore the duplicate. mkdocs
itself stays: mkdocs.yml remains the docs IA source of truth (CE028
generates the index surfaces from its nav:, the website derives its sidebar
from it) and `mkdocs build` still verifies anchors locally. Only the
publishing of a second site is gone.

gh-pages is force-replaced with a fresh single-commit history so none of the
old output — sitemap included — survives.

Verified: workflow YAML parses, the assemble step dry-run produces
index.html/404.html/.nojekyll/llms.txt, all three links return 200, the page
renders in light and dark, and `make lint` passes (166).

Co-Authored-By: Claude <noreply@anthropic.com>
Dresses up the redirect stub so it reads as ours rather than as a bare
fallback: the Coder Eval wordmark above the heading, and a brand-orange bar
linking to the repo with its star and fork counts.

The wordmark is inlined from the website's src/assets/logo-light.svg rather
than linked. The light and dark variants differ only in four navy fills (the
three icon bars and the "Coder" text group — "Eval" and the arc are always
brand orange), so those four become `currentColor` and one 3.5 KB copy
serves both colour schemes. Substitution was scripted, not hand-typed, and
asserts it recoloured exactly four fills and left the orange intact. Keeps
the page free of external stylesheets, fonts, and images.

Palette switched from approximations to the real brand values taken from
those assets: #FA4616 orange, #10233A navy, #F5F7FA off-white.

Star/fork counts ship as a committed snapshot so the bar is never blank,
then refresh from api.github.com on load. Unauthenticated is 60 req/hour/IP,
ample for a page nobody should land on, and any failure silently leaves the
snapshot. GitHub's own octicon paths are inlined for the mark, star, and
fork glyphs.

Verified in-browser: renders in both colour schemes (dark @media rule
deleted at runtime to confirm the light path), and the refresh was proven to
work by poisoning the counts to 999 and watching the fetch restore 107/2.

Co-Authored-By: Claude <noreply@anthropic.com>
Requested as a loading state for the star/fork counts. Built inverted from
the obvious reading, because a spinner in the markup would be worst for the
no-JS case it was meant to serve: with scripting off nothing would ever
resolve it, so it would claim "fetching…" forever and read as broken. So the
snapshot stays in the HTML and the spinner is installed BY the script — it
therefore only appears where JS is also able to finish the job. No-JS
visitors and crawlers get 107/2, which is stale at worst, never a lie.

Every exit resolves the spinner: live values on success; the snapshot back on
network error, non-2xx, a payload missing either count, or the 4s deadline.
The count cell reserves 3 digits of width so the swaps don't shift the bar.

The deadline restores the UI itself rather than only calling abort(). Relying
on the abort was a real hole — a fetch that ignores the signal would spin
forever, exactly the failure being avoided — and a stubbed hanging request
reproduced it before this was fixed. A `settled` flag makes the race
single-shot so a response landing after the deadline cannot overwrite the
restored snapshot.

Verified in-browser against a stubbed network, running this exact script:
spinner renders mid-flight; hang restores after 4s; reject, HTTP 500, partial
payload, and a 6s-late response all settle on 107/2 with no cell left
spinning (the late 99999/88888 payload was correctly discarded). Markup
contains zero spinner elements, so the no-JS path is the static snapshot.

Co-Authored-By: Claude <noreply@anthropic.com>
Simplifies the repo bar's star/fork counts on two instructions: no committed
values, and a failed fetch should keep spinning rather than fall back.

The spinner is now the initial state in the markup and the only fallback, so
the previous scaffolding — a snapshot Map, restore-on-failure, the 4s
AbortController deadline, and the `settled` race guard — all had nothing left
to do and is gone. Net 70 lines deleted for 20 added; the script is 10 lines.

Anything that isn't a live number stays a spinner: offline, non-2xx, a payload
missing a field, or scripting disabled. Resolution is per cell, so a partial
payload fills what it has and leaves the rest spinning, rather than needing an
all-or-nothing rule.

Tradeoff, accepted deliberately: with JavaScript off the spinner never
resolves. That is the cost of never publishing a count that is wrong within
days, on a stub page nobody should be landing on.

Verified in-browser: live load fills 107/2; network reject, a request that
never settles, and a payload missing forks_count all leave the spinner in
place; markup contains no numeric literals for the counts.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

Claude finished @uipreliga's task in 1m 24s —— View job


Code Review In Progress

  • Read .github/code_review.md for review standards
  • Read CLAUDE.md for project conventions
  • Analyze git diff origin/main...HEAD
  • Read full files for context
  • Review .github/pages-stub/index.html
  • Review .github/workflows/docs.yml changes
  • Check cross-file consistency
  • Perform design-level scrutiny
  • Submit structured review feedback

uipreliga and others added 2 commits July 28, 2026 13:23
The page named the product and said the docs had moved, but never described
what it is. Anyone arriving from a stale link had to click through to find out.

Leads with the product now: `Coder Eval` as the h1, then a one-sentence
description (open-source framework, evaluates and benchmarks AI coding agents
and Claude Code skills, sandboxed real-agent runs against declarative YAML
tasks, scores the files and commands produced), with the moved notice demoted
to a secondary line. Wording mirrors coder-eval.com and the package metadata
so the product is described identically everywhere.

Adds a matching <title> and meta description. Note these do NOT help ranking —
the page is `noindex` — they are for link previews and shared URLs; the comment
above them says so, so nobody later mistakes them for SEO copy that is doing
work it isn't.

The wordmark is now aria-hidden. It renders the same text as the new h1, so a
screen reader was announcing "Coder Eval" twice; the h1 is the accessible name
and the logo is decorative.

Co-Authored-By: Claude <noreply@anthropic.com>
…wice

Three copy fixes.

The notice now links coder-eval.com/docs instead of naming it as plain text —
it is the sentence a visitor actually reads to find where the docs went, so it
should be clickable.

"Coder Eval" was rendered twice, by the wordmark and again by the h1 directly
beneath it. The h1 is now visually hidden: it stays for document structure and
is the accessible name for the page (the logo is aria-hidden), while the eye
sees the wordmark once and then the description. The lead paragraph takes the
visual weight the heading was carrying.

Linking the docs in the notice then made the "Documentation" list row a second
copy of the same URL two lines apart, so that row is gone. Each destination now
appears exactly once: docs in the notice, the project site in the list, the
repo in the bar.

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

@bai-uipath bai-uipath left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. Deleting the mirror is right, and 404.html-as-stub is the correct answer to 15 dead routes. All follow-up below.

  • Pull the wordmark out into a committed .svg. 5.1 KB of inline SVG is 35% of the page, 3.1 KB of it font outlines for two words the hidden <h1> already renders. Fix: reference it root-absolute (/coder_eval/logo.svg), since 404.html serves at arbitrary depths, and use a <picture> for light/dark because an external <img> can't inherit currentColor; website/src/assets/ already has both variants.

  • The multi-repo split is the real exposure, not the stub. docs/ is the source of truth here but publishing now lives entirely in coder_eval_uipath/website/ via scripts/sync-docs.mjs. After this merges nothing in this repo publishes docs anywhere, and mkdocs-material[imaging]>=9.5,<10 is pinned nowhere while CLAUDE.md:208 still says to verify anchors with mkdocs build.

  • coder-eval.com is served from an unmerged branch. coder_eval_uipath 62 has been open since Jul 22 at 73 commits and the live site tracks its head. The stub hardcodes /llms-full.txt and copies logo-light.svg, both of which exist only there. Not blocking (site is live, all links 200). Fix: land internal 62, and note the logo's source commit so it reads as a copy.

  • Merging doesn't take effect. workflow_dispatch-only leaves the stale mirror live until someone remembers the Actions tab. Fix: push: on main with paths: ['.github/pages-stub/**', '.github/workflows/docs.yml'].

  • Minor: #FA4616 as text fails WCAG AA (3.53:1 on white, same for white-on-orange in the bar; rest of the palette is 5.5:1+); with JS off or rate-limited both counts spin forever, so consider dropping them; noindex is a no-op for the 15 deep paths, which now 404 on status.

@uipreliga
uipreliga merged commit b819607 into main Jul 28, 2026
13 checks passed
@uipreliga
uipreliga deleted the chore/pages-stub-redirect branch July 28, 2026 23:37
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