clickhouse/docs-pr-review
> Review a docs PR from someone else. Triage issues by severity, draft comments with rationale, and focus on what matters. Use when reviewing a PR, not when checking your own work (use docs-pre-ship-review for that).
npx skills add https://github.com/ClickHouse/clickhouse-docs --skill docs-pr-review
Review PR #[number] using the docs-pr-review skill.
Pass 1 runs first (diff-scoped, 13 checks). Pass 2 runs second (full file,
pre-existing quality issues).
Review someone else's docs PR. Your job is to triage and communicate.
This skill is read-only — do not edit files, stage changes, or commit.
Output a list of comments, suggestions, and questions. The user decides
what to act on.
Scope note: Changes to files under i18n/ (jp, ko, ru, zh) are not
worth flagging — translations sync automatically from the English source.
This is different from pre-ship review (checking your own work). Here you're
balancing thoroughness with respect for the author's time.
No shortcuts. No laziness. Catch every problem, every time.
A different session reviewing the same PR must produce the same findings,
the same line numbers, and the same severity tiers. This is the quality
bar — reviews that miss issues or report wrong line numbers are failures,
not approximations. Every check runs completely, every finding is verified
before it is reported, and every line number is mechanically computed. No
step is skipped because the PR is large, because the content looks fine at
a glance, or because a similar check already passed.
This review has two distinct passes with different scopes.
gh pr diff to see only what the contributor changed.All review comments must be scoped to lines in the diff.
Line numbers — compute them mechanically, never estimate:
Every line number in the review output must be exact and deterministic.
A different session reviewing the same PR must produce identical line
numbers. Never use ~ prefixes, never round, never eyeball.
gh pr diff N | grep -nE '^\+\+\+ b/|^@@ ' to get thediff-output line number of every file boundary and @@ hunk header.
gh pr diff N | grep -n '^+#.*{#' (or similar targeted greps)to find the diff-output line number of the content you need to cite.
@@ header shows the target file line where the hunk starts(e.g., @@ -0,0 +1,42 @@ → file line 1; @@ -85,7 +89,10 @@ →
file line 89).
file_line = diff_output_line - @@_header_linefile_line = hunk_start + (diff_output_line - @@_line - 1)(only counting + and context lines, not - lines).
Content between hunks (gaps): The @@ start line tells you where
the *hunk* begins, not where unchanged content between hunks lives.
For content in a gap:
prev_hunk_start + new_count - 1.@@ header.lines to locate the target. The gap contains exactly
next_hunk_start - prev_hunk_end - 1 unchanged lines.
@@ start —compute its exact position by counting.
Verification step: Before including any line number in the review
output, confirm it was computed (not estimated) and trace back to the
@@ header or Read tool output that produced it. If you cannot trace
a number back to a mechanical computation, recompute it before
proceeding.
For Pass 2 (local file reads), the Read tool prints N→ content —
cite N verbatim.
Never estimate line numbers by reading the diff and guessing.
Always compute from @@ headers. Never write awk scripts or custom
tooling either.
PR branch — it fails on forks and risks disrupting local git state.
not Pass 1.
.md files. CI already runsVale on the PR's diff, so don't report Vale issues on diff lines —
focus on issues outside the diff that CI won't catch. Run vale
directly on the local file path. Cross-reference Vale line numbers
against the diff's @@ hunk headers — issues on lines outside the
diff are pre-existing and go in Pass 2. If the PR renames or moves a
file, skip Vale — the old path still has the content locally, but
line numbers won't match the post-merge state.
grammar errors, missing punctuation, typos, and voice violations
(third person in second-person docs). Cross-reference against the
diff the same way — only flag lines outside the diff.
items that fail. This is the complete list — do not invent additional
review categories.
what you'd let go.
| # | Check | Tier if failed |
|----|-------|----------------|
| 1 | Frontmatter complete — slug, title, sidebar_label, description, keywords present | Block |
| 2 | MDX/build safety — imports exist, no broken JSX, images referenced exist | Block |
| 3 | Technical accuracy — commands, SQL, column/table names, config keys are correct | Block |
| 4 | No secrets — no hardcoded credentials, internal URLs, or API keys | Block |
| 5 | Product terminology — correct product names (ClickStack vs HyperDX, etc.) | Block |
| 6 | Grammar, spelling, typos — misspellings, wrong verb forms, missing words, wrong articles | Block |
| 7 | Sentence casing — headings must use sentence case, not Title Case | Block |
| 8 | Content preservation on rewrites — substantial deletions or rewrites must preserve original information, or the new content must cover it or render it moot | Block |
| 9 | Vale errors in diff — CI covers these; only flag if CI is not running on this PR | Block/Suggest |
| 10 | Voice/style in diff — mechanical pre-screen for AI density, then watchlist scan; "you" not "users," active voice | Suggest / Block on AI cluster |
| 11 | First-mention links — ClickHouse features/concepts link to their docs on first use | Suggest |
| 12 | Component opportunities — numbered steps → VerticalStepper, variants → Tabs, etc. | Suggest |
| 13 | Structure and flow — sections in logical order, prerequisites before procedures, page organization matches sibling pages | Suggest |
| 14 | Screenshot utility — screenshots used as shortcuts ("refer to the screenshot") without sufficient prose context | Suggest |
Block-tier checks (#1–#8) must be exhaustive — every instance found,
not sampled. Some checks have deterministic patterns that grep can catch;
others require careful reading. Run the two modes separately, in order:
grep-based checks first (fast, mechanical), then a reading pass (slower,
requires comprehension). Do not try to do both in a single pass through
the diff — that's how things get missed.
Mode 1: Pattern-based checks (run first, via grep)
These checks have deterministic patterns. Grep the diff to get a complete
list, then verify each result.
*Sentence casing (#7):*
Run gh pr diff N | grep -n '^+#.*{#' to extract every added heading
with its diff-output line number. Check each heading: only the first
word and proper nouns may be capitalized. Compute the file line for every
violation using the @@-header method above.
Common proper nouns in this codebase: ClickHouse, ClickStack, HyperDX,
Helm, Kubernetes, MongoDB, OpenTelemetry Collector, OTEL, AWS, GKE, EKS,
AKS, API, SQL, TLS, DNS, JSON, YAML, ConfigMap, Secret (K8s resource types).
*Frontmatter (#1):*
For every changed or new .md file, verify the frontmatter block in the
diff contains all required fields: slug, title, sidebar_label,
description, keywords. Integration guides also need pagination_prev,
pagination_next, and doc_type.
Mode 2: Comprehension checks (reading pass)
These checks require understanding the content — grep can't catch them.
Read through the diff content carefully, file by file.
*Technical accuracy (#3):*
A reviewer's job is not to re-run every command — that's the author's
responsibility (covered by docs-pre-ship-review). Your job is to catch
what looks wrong and flag what you can't verify. Use this hierarchy:
Verify yourself (fast, do these):
find docs/ -name '*.md' | xargs grep -l 'slug:' | head -20 to spot-check that a referenced page exists.grep -r 'MergeTree' docs/ --include='*.md' -l) to confirm it's real and consistently spelled.doc_type is one of guide, reference, changelog, landing-page — anything else is wrong.clickhouse-client --help output is documented in docs/interfaces/cli.md).Ask the author (you cannot verify these without running the system):
When asking the author, be specific: "Can you confirm this command runs without error against a fresh install?" is more useful than "Please verify this is correct."
Flag as Block if:
Flag as Suggest (ask author) if:
Do not flag claims you have no specific reason to doubt. Skepticism without evidence is noise.
*Grammar, spelling, typos (#6):*
Read the prose in added lines. Watch for wrong articles, missing words,
subject-verb disagreement, and misspellings that pass spellcheck (e.g.,
"form" for "from").
*Content preservation on rewrites (#8):*
Only run this check when the diff is a substantial rewrite or large
deletion — skip it for small edits. Trigger when any of these are true:
deleted.
For each substantial deletion, list the distinct claims, warnings,
examples, and links being removed, then map each one to:
surrounding content (different wording is fine).
a version that the new content makes irrelevant.
in the change justifies dropping it.
Litmus test: "If a reader needed information X, could they still find
it after this change?" If no, flag.
Flag as Block if:
being addressed elsewhere.
new prose doesn't.
equivalent in the new content.
Do not flag content that is genuinely redundant with what remains,
clearly outdated, or replaced by a tighter treatment of the same point.
*No secrets (#4), product terminology (#5), MDX/build safety (#2):*
These surface during reading. Flag hardcoded credentials, wrong product
names, and missing imports as you encounter them.
*Voice/style and AI-isms (#10):*
Check the doc_type in frontmatter before running this check. The
watchlist and thresholds differ by type.
Run this check in three stages: mechanical pre-screen, watchlist scan,
escalation decision. Sophisticated AI output (e.g., recent Claude)
avoids the obvious phrase-based tells in the watchlist below — the
structural tells (punctuation density, intensifier frequency, header
voice) are what catches it. Run the pre-screen first; it's
deterministic and surfaces patterns the eyeball pass misses.
Stage 1: Mechanical pre-screen.
# Em-dash count in additions
gh pr diff N | grep '^+' | grep -v '^+++' | grep -o '—' | wc -l
# Em-dash locations (skim to separate bullet "term — def" from prose)
gh pr diff N | grep -n '^+.*—'
# Semicolon usage in prose additions (excludes table rows)
gh pr diff N | grep -nE '^\+[^|+].*;'
# "actually" as intensifier
gh pr diff N | grep -niE '^\+.*\bactually\b'
# Added section headings (review for folksy/blog-post voice)
gh pr diff N | grep -nE '^\+#{2,} '
Thresholds for a new or substantially rewritten page:
term — defdefinitions) is a signal. Watch especially for multiple em-dashes
in a single sentence ("X — Y — Z") or em-dashes in consecutive
paragraphs.
rarely need semicolons; human writers default to periods.
workload is actually made of") is a tell. "Actually" carries almost
no information; AI uses it to add conversational texture.
rather than reference labels ("Out of the way of the database,"
"Same engine our customers use," "Raw events, not aggregates").
Reference-quality headings are short and literal ("Overhead,"
"Architecture," "Events").
Record counts and note which thresholds tripped. Don't flag yet —
feed into Stage 3.
Stage 2: Watchlist scan.
Read added prose and flag instances from this watchlist.
*Phrase-based (often grep-friendly):*
crucially, interestingly, surprisingly, fascinatingly, that said,
that being said, it's worth noting that, it bears mentioning
the truth is, here's what most people get wrong, in today's
fast-paced, in an era of, at the end of the day, when all is said
and done
game-changing, robust, comprehensive, cutting-edge, unlock, leverage,
empower, powerful, elegant, intuitive
paradigm shift, north star, guiding principle
beyond the scope, without further ado, without getting too deep
into, as we'll see, as we've seen, as mentioned earlier
hope this guide
*Pattern tells (require reading):*
events, not aggregates").
Because...").
Debezium configuration.").
strings that read like a person speaking, scattered through prose
("p99 is creeping up", "what is costing me the most?", "Show me only
what the orders service is doing"). One or two is fine for flavor;
a cluster is a strong signal.
at the end of a section that restate its point in catchy form
("Everything you need to diagnose a slow pattern is in one place,
on one screen."). Human writers usually let the section end with
its content.
order you'd actually use them", "becomes two dropdowns").
familiar shape").
paragraph).
High-severity-only subset (for reference and landing-page types):
tapestry, mosaic, fabric, paradigm shift, seamless, groundbreaking,
revolutionary, game-changing, landscape, ecosystem, north star,
unlock, leverage, empower, in today's fast-paced, in an era of, thank
you for reading. Plus em-dash overuse (5+ prose em-dashes). Reference
pages are structured by nature (parallel lists, topic sentences are
expected); only the cliches above are flagged.
Stage 3: Escalation decision.
Tally hits across both stages. Categories: punctuation (em-dash,
semicolon), sentence patterns (contrast, question-answer,
re-explanation, tricolon, fragment summaries, meta-commentary),
transitions, hedging/filler, buzzwords, metaphor, voice (folksy
headers, anthropomorphism, "actually" intensifier), formatting (bold
abuse).
| doc_type | Hits | Action |
|---|---|---|
| guide | 0 | Move on, no comment. |
| guide | 1–2 in one category | Single batched comment with rewrites. Suggest. |
| guide | 3+ in one category | Batched comment with rewrites. Suggest. Reference load required (see below). |
| guide | 3+ categories (cluster) | Block. Reference load required (see below). Flag whole page as AI-generated; ask for a revision pass before merge. Don't comment line-by-line — the page needs a voice rewrite, not patches. |
| reference / landing-page | < 3 hits | Move on. |
| reference / landing-page | 3+ hits | Batched comment. Suggest. Reference load required (see below). |
| reference / landing-page | 5+ hits or 3+ categories | Block. Reference load required (see below). |
If doc_type is missing from frontmatter, default to guide thresholds.
Reference load (gating step — required when any row above says so).
When a threshold triggers a reference load, STOP before drafting any
AI-tell findings. In order:
references/ai-tells.md in full (use the Read tool, not a grepor partial read).
(e.g., em-dash overuse → §1.1, "X not Y" contrast → §2.1).
and use the catalogue's remediation language, not your own.
A review that flags an AI cluster without §-citations is incomplete —
the catalogue exists so rewrites are reproducible across sessions, and
improvised remediation defeats that purpose. If you find yourself
drafting AI-tell rewrites without the catalogue loaded, stop and load
it before continuing.
A whole-page AI cluster is qualitatively different from scattered
style nits. A reader can tell the difference between docs written by
a person and docs written by an LLM with light edits; a page that
trips the cluster threshold reads as the latter and shouldn't ship as
the former.
Always check general voice issues regardless of doc type: third person
("the user," "users") in second-person docs, passive voice where
active is clearer.
*Screenshot utility (#14):*
Scan for crutch phrases like "refer to the screenshot", "see screenshot",
"as shown in the screenshot below", or "see the image above." These
indicate the author is relying on a visual instead of explaining the step
in prose.
Apply this litmus test: **could the reader succeed with the task based
only on the prose description, without looking at the screenshot?**
this fully — is the screenshot adding anything the text doesn't?"
explain what to look for. Flag: "This step relies on the screenshot to
explain itself. Add a sentence describing what the reader should see or
do, so the screenshot supplements rather than replaces the instruction."
Screenshots earn their place by showing the result of a step
(confirmation), orienting the reader in a complex UI, or surfacing a
non-obvious option. They don't earn their place when they substitute for
writing a clear instruction.
*Structure and flow (#13):*
Read the full file (not just the diff) to evaluate page organization.
Check that sections follow a logical progression — context before
procedures, prerequisites before steps, conceptual overview before
reference detail. Read 2–3 sibling files in the same directory to see
whether the page matches the section ordering conventions around it.
Flag cases where a reader would need to scroll back to understand
something, where procedural steps are buried after reference tables, or
where the page structure diverges from its neighbors without reason.
Don't flag structure that is clear and works even if you'd organize it
differently — this check targets disorienting layouts, not style
preferences.
This pass uses the Vale output and manual read-through you collected in
Pass 1 step 3. Both are required — Vale catches mechanical issues
(spelling, contractions, wordy phrases) while the manual read catches
grammar errors, voice violations, and formatting problems that Vale misses.
Always scan the full files touched by the PR for pre-existing quality
issues — regardless of how small the diff is. Even a one-line change is
an opportunity to catch issues in the surrounding page. These are tracked
separately, never raised as PR comments.
Flag only these categories:
AI-ism clusters (3+ patterns from the watchlist in check #9)
blank lines that affect rendering
Do NOT flag:
Observations (no ticket, reported separately):
Nothing in Pass 2 blocks the PR. It's all informational for the human
reviewer to decide what to act on.
Edge case — renamed/moved files: If the PR renames or moves a file,
pre-existing issues will reference a path that doesn't exist yet (before
merge) or no longer exists (after merge). In this case, flag the issues
in the review output but do not draft a Linear ticket — note that
the file is being moved and the issues can be addressed after merge.
Output: Present findings in two groups:
Draft a Linear ticket preview using the Historical Docs Quality Fixes - PRs project,
low priority, with file path and bullet-form issues. Do not
create the ticket without the user confirming the preview. Format:
Title: Quality fixes: <page name>
Description:
Pre-existing quality issues found during review of PR #XXXX.
`path/to/file.md`
* Line X: description of issue
* Lines X, Y: description of repeated issue (N occurrences)
* Line X: description of issue
the reviewer should be aware of. Flag them so the human can decide
whether to raise with the contributor or investigate further.
than 10, keep all Blocks, then rank Suggests by reader impact and cut
the rest. Pass 2 quality issues count toward the cap; observations do not.
Pass 2 flag list above. If an issue doesn't fit a listed category, it's
Ignore.
These prevent the PR from shipping. The author must address them.
won't run, wrong column or table names
missing image file
where ClickStack is correct) in a way that will confuse readers
exist, or not linking to essential context the reader needs
missing words, incorrect articles ("an" vs "a")
not Title Case ("Tuning Performance"). This is a hard site-wide requirement.
drops a warning, example, link, or distinct claim that isn't preserved
elsewhere or rendered moot by the change.
threshold (3+ AI-tell categories with hits, see Stage 3). The page
reads as LLM-generated and needs a voice rewrite before merge, not
line-by-line patches.
These are real issues but shouldn't block the PR. The author can address
them in this PR or a follow-up.
of "you," em dash overuse
should link to their docs
variant content that should be Tabs, supporting detail that should be
collapsible
procedures, page organization that diverges from sibling pages without
reason, or layouts where the reader must scroll back to understand context
that don't affect comprehension
Don't leave comments on these. They create noise and slow down the review.
technically accurate, it is Ignore** — even if you'd write it differently.
This is the single most important triage rule.
match the latest standard
actually clearer
timeout values) — don't flag these as "might go stale"
and links to a page that defines it, don't suggest inlining the
definition. The link is doing its job.
slightly long sentences, or phrasing you'd restructure — if the meaning
is clear, it's Ignore. A suggestion should be clearly worth the
author's time, not a marginal polish.
Every comment should help the author understand *what* to change and *why*.
is more useful than "Per our style guide, column names should be verified."
column is actually TimestampTime" tells the author what's at stake.
the answer, include it. If you don't, say what you'd check.
them all instead of 5 separate comments.
"could flow better" is not.
inconsistent punctuation on list items, formality mismatches.
this be ClickStack?" is better than "Change HyperDX to ClickStack."
without a suggested rewrite isn't actionable.
is clean, say so. Reviews that only flag problems are demoralizing.
for issues they didn't introduce. Pre-existing problems go in Pass 2.
doesn't cover topic X, that's a separate issue, not a PR comment.
that's a conversation, not a review comment.
flag it once with "this pattern appears throughout" instead of commenting
on every instance.
(check the schema docs), frontmatter fields (check required fields list)
whether a feature works as described (they may have tested it), whether
omitted content is intentional
When drafting review comments for the user, format each as:
**Line X** (block/suggest): <comment text>
Group by severity — blocks first, then suggestions. End with a one-line
summary of your recommendation (approve, approve with suggestions, or
request changes).
When drafting Slack messages, PR descriptions, or Linear comments:
Take clickhouse/docs-pr-review from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.