website-review
The Double-Knuth review for a site — a two-pass correctness + cross-file consistency audit run at the END of a build (pre-launch) and after ADDING or substantially editing a page. Pass 1 = bugs/correc
Files1
View on GitHubSKILL.mdskills/website-review/SKILL.md
GitHubWebsite review — Double-Knuth (correctness + consistency)
A two-pass audit of a site's code/config/content, modelled on the
portfolio-code-reviewer Double-Knuth. It sits on top of website-qa: the
tests are the floor (must be green first); this is the human-grade review that
catches what a test suite can't — cross-file drift and half-done work.
When to run
- End of a build, before launch (pipeline step 8).
- After adding or substantially editing a page — a new route touches
PAGES, nav, the sitemap, internal links and canonical, so consistency can silently break. - It is read-only: produce a ranked list, then fix BUGs before launch. Don't fix-and-forget — surface first (Rule 12: never hide a skipped or broken thing).
- For just "is the offer clear to a stranger" on a site that's already built —
faster and narrower than this full audit — use
website-positioning-check.
Review depth — the owner chooses, every time
A website is lower-stakes than application code: a subtle bug in a landing page's copy costs a bad reading experience, not a broken payment or a data leak. This skill (Double-Knuth, below) is free, runs entirely in the current session, and is the default, sufficient review for the large majority of website changes — new pages, copy edits, layout tweaks, small SEO adjustments, content updates. An external AI reviewer on top of it is an optional extra, never a requirement, and never more than one at a time.
Default — no setup, no cost, use this unless there's a specific reason not to:
- This skill, both passes — Pass 1 delegates to
/code-review(correctness bugs), Pass 2 is the cross-file/consistency sweep below. /code-reviewalone — just Pass 1, for a small, self-contained diff where cross-file consistency clearly isn't at risk (a one-page copy fix, a single component tweak). Faster than the full two-pass audit when that's all the change touches.
Optional escalation — one external reviewer, only when it's genuinely worth it:
A handful of website changes carry enough risk that a second, independent
model is worth the extra step: a site-wide SEO repositioning (the
seo-reposition skill's plan/diff gates), a payment or checkout flow, a
form collecting sensitive personal data, or a major structural
redesign where the author reviewing their own architecture risks sharing
its blind spots. Even for these, use one external reviewer, not the
independent-review skill's own default of two — that default is calibrated
for production application code (Rails migrations, auth changes), not
website content/structure work:
# from this repo's skills root, or the equivalent path in a site that has
# independent-review vendored in:
skills/independent-review/scripts/independent_review.sh <file> --plan --first-success # or --diff
A --diff run first checks the diff against git, from the repository it came from: a diff
git diff made from the merge base with the default branch passes as it is; a diff of only some
commits names its start with --base <commit>, and one not made by git diff there passes
--base none (independent-review's script header, BASE CHECK).
--first-success stops at the first reviewer that answers (Codex CLI,
falling back to ollama-cloud) instead of running both — independent-review
honors this override for plans, it doesn't force the pair. Setup: if
independent-review isn't already working in this repo, its own SKILL.md
has a guided onboarding wizard for getting Codex or ollama-cloud running
(free, no payment) — walk through that first rather than assuming it's ready.
In a scaffolded site the skill isn't shipped at all: check that the
independent-review folder actually exists before offering this escalation
as ready-to-run. If it doesn't, either vendor it first (copy
skills/independent-review/ from the Croftweaver suite — the same kit
this skill was copied from — into the site's .claude/skills/) or frame the
offer honestly as "available after a one-time setup step".
Explain the choice, then ask — don't decide silently. Before reaching for anything beyond this skill's own two passes, say plainly what the options are and what's actually at stake: "I can review this with the built-in check — free, runs now — or also send it to an outside AI for a second opinion, which costs nothing either but takes a few extra minutes. For [this kind of change] I'd lean toward [recommendation] because [reason] — want that, or would you rather [the other option]?" The owner decides; this skill's job is to make an informed recommendation, never to pick unasked.
Pass 1 — correctness / bugs
- Run
/code-reviewon the branch/diff (correctness bugs + reuse/simplification)./code-reviewis Claude Code-only — under another host, review the diff by hand against this pass's checklist, or vendor the genericdouble-knuthskill from the Croftweaver suite. npm run buildis clean — no errors or warnings; TS strict passes.npm testgreen (a11y/seo/navigation/anchors/orphans/images/tone/positioning/placeholders/email/links/llms-coverage/middleware) — nothing skipped or loosened. (A site scaffolded before a spec existed: copy it in from the starter rather than reviewing without it.placeholders.spec.tsgoes in with itsUNFILLED_UNTIL_LAUNCHas shipped, on a launched site too: the first run names each entry to delete, and its check for lower-case slots, which runs on listed pages only, finds the one older starters carried on the privacy page.)astro previewthe new/edited pages — no console errors; interactions work.- Nothing half-done: no TODO/placeholder/lorem and no leftover
[BRACKET]slots in shipped pages ORpublic/assets.placeholders.spec.tsenforces this on the rendered pages and the manifest; read for what it cannot see: pages outsidePAGES(the 404 page, noindex pages), otherpublic/files (llms.txtand its own slots included), PDFs, text inside images, the starter's example values, and an entry still sitting in itsUNFILLED_UNTIL_LAUNCHat launch (it only warns there). - Freshness date matches the edit: if the page's visible content changed and it carries a
last-modified signal (a
dateModified/"Updated" byline, an Article/BlogPosting JSON-LD date), that date is bumped to today. It silently drifts otherwise — stale in the visible byline, the<time datetime>attribute, and the schema'sdateModified(an SEO/AI-answer-engine freshness signal) all at once, and no test catches it because nothing can know a date is "wrong" without knowing the edit happened. - Nothing silently unshipped (two-stage sites):
git log origin/production..origin/mainempty, or the gap is a conscious decision — and the lastnpm run shipended "✓ LIVE — verified". Any deploy you run or announce follows the site'sPUBLISHING.md§ "For AI assistants — deploy-time guardrails" (preview-vs-live announcements + the cached-404 rule).
Pass 2 — completeness + cross-file/skill consistency
- Routes: every
src/pages/**route is intests/_helpers.tsPAGES(and vice-versa; seo.spec.ts's sitemap drift alarm enforces it); nav + every internal link points to a real route; every route is reachable via at least one inbound internal link (no orphan pages — the privacy page included). - URLs: the no-trailing-slash convention holds everywhere (
/about, not/about/);canonical == og:url == SITE.url + path. - Single source of truth: address, email, phone, nav come only from
src/config.ts— no hardcoded copies drifting inside pages. - Recurring section patterns carry consistent headings: if a component pattern (e.g. a
grid grid-2/grid-3card block) has its own<h2>directly above it everywhere else it appears on the site, a new or edited instance should too. A local heading step (h2 → h3) will not trip axe's heading-order rule, so this only surfaces by checking this page's heading structure against how the same pattern is used on other pages, not by trusting the a11y suite. - SEO (
website-seo-geo): title ≤60, description 120–160, og/twitter mirror, exactly one<h1>, JSON-LD valid, sitemap covers every page,llms.txtcurrent. - Images (
website-design-system): WebP/AVIF, explicitwidth/height,alt; the LCP image eager (fetchpriority="high"), below-fold lazy; no oversized files. - Email: every address rendered via
<EmailLink>;email.spec.tsgreen. - Headers/redirects:
_headersCSP present; legacy URLs 301 in_redirects; the*.pages.devmiddleware in place (previews noindexed); on a launched site, the project's ownpages.devaddress (as Cloudflare shows it; not always the project's name) answers 301 to the live domain — if it answers 200, see the causes listed innew-website/references/CLOUDFLARE_FIRST_DEPLOY.md, "After go-live", step 3 (variable not under Production or rejected, no redeploy since, or a middleware that predates the redirect). - Secrets: no
.env/token committed;.gitignorecovers them. - Docs match code: README test list + "how to add a page / run tests / deploy" current;
CONTENT_GUIDE.md+BRAND.mdfilled (+STORY.md, consistent withPOSITIONING.md, if the site opted intowebsite-story); decision-interview answers recorded; the handoff skill set present in.claude/skills/. - Language/i18n (non-English or multi-locale sites): every page's CONTENT language
matches its
<html lang>(the tone gate's density check covers German; eyeball other languages);og:localepresent and matching on every page; chrome (skip link, footer) in the site's language; German-market sites carry the filled Impressum and the German privacy page at the right URL (/datenschutzsingle-locale,/de/privacymultilingual); multi-locale sites: hreflang sets complete + reciprocal (tests/i18n.spec.tsgreen, or the twin-page seo.spec test for the light path), every/localeroute in PAGES, the language switcher on every multi-locale page, llms.txt entries in each page's language.
Output / done means
A ranked BUG / RISK / NIT list, each with file:line and a concrete fix (same shape as
the portfolio Double-Knuth). Fix BUGs before launch; RISK/NIT by judgment. Re-run website-qa
after fixes. Done = both passes clean, or the remaining items explicitly listed — never hidden.