Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
54ad439
trademark stuff added
EricAndrechek Aug 17, 2026
dfe3ada
Merge branch 'main' of github.com:Wave-RF/WaveHouse into docs-cloud-r…
EricAndrechek Aug 17, 2026
c93ae12
no premature new line splits, tabitem fencing and codeblocks fixed
EricAndrechek Aug 18, 2026
4c2eb80
Merge branch 'main' of github.com:Wave-RF/WaveHouse into docs-cloud-r…
EricAndrechek Aug 18, 2026
fc01c57
build(lint): autofix hard-wrapped prose and MDX fences at write time
EricAndrechek Aug 18, 2026
4929f7f
fix(lint): stop WH001 corrupting tables, setext headings, and MDX ESM
EricAndrechek Aug 18, 2026
b276661
Merge remote-tracking branch 'origin/main' into docs-cloud-refer
EricAndrechek Aug 18, 2026
9cb4bb0
fix(lint): stop WH001 writing markdownlint's masked buffer back to disk
EricAndrechek Aug 18, 2026
ca1545a
docs(changelog): correct three claims in the cloud-CTA entry
EricAndrechek Aug 18, 2026
8fc1c3f
fix(docs): address CodeRabbit review on #489
EricAndrechek Aug 19, 2026
e32c6d9
fix(docs): correct WH002's rationale — MDX renders a glued fence fine
EricAndrechek Aug 19, 2026
e513251
fix(lint): scope the on-save hook to the repo, keep WH001's hard breaks
EricAndrechek Aug 19, 2026
85a1bcd
docs(changelog): separate the reflow from the one content deletion
EricAndrechek Aug 19, 2026
3c8a625
docs(changelog): drop the sentence pair duplicated by the last split
EricAndrechek Aug 19, 2026
a6b30e9
fix(lint): reject non-opening tags in WH002 multiline detection
EricAndrechek Aug 19, 2026
d97cd5e
fix(lint): mask attribute values before WH002 validates an opening tag
EricAndrechek Aug 19, 2026
ac62a5f
fix(lint): collapse nested braces to a fixpoint in the WH002 tag test
EricAndrechek Aug 19, 2026
9600e13
fix(lint): never run the generic markdownlint fixers over MDX
EricAndrechek Aug 19, 2026
b601b2b
docs: reconcile every description with MDX no longer being auto-fixed
EricAndrechek Aug 19, 2026
6f52f48
fix(lint): glob MDX on lint:md so a bare --fix cannot reach it
EricAndrechek Aug 19, 2026
26f237c
docs: point every description at the config glob, and align the prose…
EricAndrechek Aug 19, 2026
5ed4fae
docs(components): correct the stale sizing note in ExternalIcon
EricAndrechek Aug 19, 2026
c450678
docs(makefile): reconcile the DOCS_PROSE comments with the widened scope
EricAndrechek Aug 19, 2026
c685aea
docs: fixture the last three classify() branches, document authoring …
EricAndrechek Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .claude/.markdownlint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../.markdownlint.json",
"WH001": false
}
89 changes: 89 additions & 0 deletions .claude/hooks/markdown-on-save.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env bash
# PostToolUse hook: auto-fix Markdown/MDX after edits — the same fixers `make
# fix` runs, narrowed to the one file just written.
#
# Why a hook (vs leaving it to `make fix` or CI): these are deterministic,
# mechanical corrections — unwrapping hard-wrapped prose (WH001), inserting the
# blank line an MDX fence needs beside a JSX tag (WH002), common typos and
# US spelling. Catching them at write time means an agent's own output is
# already correct, instead of costing a lint failure and a second pass to fix
# by hand. Sibling of gofumpt-on-save.sh, which does the same for Go.
#
# Deliberately NOT wired into .githooks/pre-commit: a commit hook that rewrites
# and re-stages files silently changes what you reviewed and fights `git add -p`.
# The commit hook stays a check; this fixes early enough that it rarely fires.
#
# The two branches below are mutually exclusive by extension: markdownlint's
# generic fixers never see .mdx. See scripts/fix-mdx-fences.mjs for why.
#
# Safety: best-effort throughout. A missing tool, an unparseable file, or a
# lint error that has no fix leaves the file alone and never blocks the edit.

set -uo pipefail

input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty' 2>/dev/null)

case "$file_path" in
*.md | *.mdx) ;;
*) exit 0 ;;
esac
[ -f "$file_path" ] || exit 0

cd "${CLAUDE_PROJECT_DIR:-.}" 2>/dev/null || exit 0

# Resolve both sides to physical paths before comparing. A literal prefix strip
# is not a containment check: `<repo>/../notes.md` strips to `../notes.md`,
# which is not absolute and would sail past a `case */*` bail. Symlinks have the
# same problem in reverse. This repo's Markdown conventions have no business
# rewriting agent memory files under ~/.claude/, scratch notes in /tmp, or
# Markdown in an unrelated checkout — all of which a session routinely writes.
root=$(pwd -P) || exit 0
dir=$(cd "$(dirname "$file_path")" 2>/dev/null && pwd -P) || exit 0
case "$dir" in
"$root" | "$root"/*) ;;
*) exit 0 ;;
esac

# markdownlint-cli2 resolves globs (and per-directory config) from the repo
# root, so hand it a repo-relative path.
if [ "$dir" = "$root" ]; then
rel=$(basename "$file_path")
else
rel="${dir#"$root"/}/$(basename "$file_path")"
fi

# .mdx gets exactly one STRUCTURAL fixer, and it is ours (misspell below still
# corrects spelling there). The generic markdownlint rules are deliberately
# never run against MDX — markdownlint parses CommonMark, MDX
# does not, and where the two disagree a generic autofix rewrites the inside of
# a code block. fix-mdx-fences only ever inserts a blank line beside a JSX tag,
# so its worst failure is a render-neutral blank line. `make lint` still CHECKS
# .mdx; it just never acts on the disagreement. Mirrors `fix:md`.
if [ "${rel##*.}" = "mdx" ]; then
node scripts/fix-mdx-fences.mjs "$rel" >/dev/null 2>&1 || true
elif [ -x node_modules/.bin/markdownlint-cli2 ]; then
# Plain Markdown: markdownlint's parse IS authoritative, so the full fixer
# chain is safe. `--no-globs` keeps it to this one file rather than the whole
# repo; a nonzero exit only means something unfixable remains (e.g. a fence
# with no language), which CI reports.
#
# Twice, mirroring `fix:md`: WH001's insert carries the pre-fix text of the
# lines it joins, so another rule's fix for a joined line is dropped on the
# first pass. One pass would leave behind exactly the issue this hook exists
# to prevent, in the common case where WH001 fires.
node_modules/.bin/markdownlint-cli2 --no-globs --fix ":$rel" >/dev/null 2>&1 || true
node_modules/.bin/markdownlint-cli2 --no-globs --fix ":$rel" >/dev/null 2>&1 || true
fi

# Phase 3: spelling, over docs prose only — the same scope `make lint-prose`
# uses, resolved by the canonical script rather than a second copy of the list.
if scripts/docs-prose.sh is-match "$rel" 2>/dev/null; then
for misspell in .bin/*/misspell-*; do
[ -x "$misspell" ] || continue
"$misspell" -locale US -source text -w "$rel" >/dev/null 2>&1 || true
break
done
fi

exit 0
4 changes: 4 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/gofumpt-on-save.sh"
},
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/markdown-on-save.sh"
}
]
}
Expand Down
4 changes: 4 additions & 0 deletions .github/.markdownlint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../.markdownlint.json",
"WH001": false
}
35 changes: 33 additions & 2 deletions .markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,28 @@
// `astro build` (CI, via `make ci` → build-docs), the single source of truth
// for broken links. See docs/astro.config.mjs.
//
// NOTE: .mdx is intentionally NOT linted — markdownlint parses CommonMark, not
// MDX's JSX/import syntax (docs/src/content/docs/index.mdx).
// .mdx IS linted, with a caveat worth knowing: markdownlint parses CommonMark,
// not MDX, so it reads JSX as HTML blocks. That turns out to be a feature —
// when a fence is glued to a <TabItem> (see WH002) the fence stops being a
// code block and the rules light up — which is exactly the hazard we want
// caught, since it is what makes a generic --fix rewrite the code.
// It also means generic `--fix` must never run over .mdx AT ALL — the rules
// would "fix" code that only looks like prose. .mdx IS linted, but through the
// `**/*.mdx` glob on `lint:md` in package.json, not this file's `globs`, which
// are .md only so that no generic pass — `fix:md` or a bare `--fix` — can
// reach MDX (see the globs note below). The only fixer that touches .mdx is
// scripts/fix-mdx-fences.mjs, which just inserts blank lines beside JSX tags.
//
// customRules live here and nowhere else — the VS Code extension reads this
// file too, so no editor-side setting is needed (markdownlint.customRules is
// deprecated in favor of this file). Note the editor only activates on
// Markdown, so .mdx gets no squiggles either way. CI reports both; `make fix`
// repairs WH001 in .md and only WH002 in .mdx:
// WH001 prose paragraphs must not be hard-wrapped (autofix: joins them)
// WH002 MDX fence adjacent to a JSX tag needs a blank line (fixed in phase 1)
// Both are enabled in .markdownlint.json. WH001 is turned off for CI docs and
// agent prompts by .github/.markdownlint.json and .claude/.markdownlint.json,
// and applies everywhere else — a narrower exclusion than scripts/docs-prose.sh.
//
// markdownlint-cli2 globs with dot:true, so `**/*.md` descends into hidden
// dirs — including .worktrees/, where this repo nests git worktrees. Honor
Expand All @@ -23,7 +43,18 @@
// (`--fix`) never rewrites another branch's files. The explicit ignores below
// stay as a fallback for the case where .gitignore is absent.
"gitignore": true,
// .md ONLY, deliberately. The .mdx glob lives on `lint:md` in package.json
// instead, because --fix is orthogonal to globs: leaving **/*.mdx here means
// a bare `markdownlint-cli2 --fix` from the repo root rewrites the inside of
// MDX code blocks, and "don't run that by hand" is a prohibition, not a
// guard. With the glob on the lint script, a bare --fix is safe by
// construction and the worst a bare lint can do is under-report .mdx —
// the strictly better failure of the two.
"globs": ["**/*.md"],
"customRules": [
"./scripts/markdownlint-rules/no-hard-wrapped-prose.mjs",
"./scripts/markdownlint-rules/mdx-fence-needs-blank-line.mjs"
],
"ignores": [
"**/node_modules/**",
"**/dist/**",
Expand Down
4 changes: 3 additions & 1 deletion .markdownlint.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
"MD024": { "siblings_only": true },
"MD033": false,
"MD041": false,
"MD060": false
"MD060": false,
"WH001": true,
"WH002": true
}
28 changes: 25 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,23 @@
"editor.tabSize": 2,
// Apply markdownlint's auto-fixes on save — mirrors `make fix` / `pnpm run
// fix:md`. The DavidAnson.vscode-markdownlint extension and the CLI both read
// the same .markdownlint.json (rules) + .markdownlint-cli2.jsonc (globs), so
// the editor and CI stay in lockstep.
// the same .markdownlint.json (rules) + .markdownlint-cli2.jsonc (globs AND
// customRules — so no setting is needed here; markdownlint.customRules is
// deprecated in favor of exactly that file), so the editor and CI stay in
// lockstep for Markdown.
//
// Not for MDX: the extension activates on the `markdown` language ID only,
// and .mdx is not associated with it, so WH001 squiggles in .md alone and
// WH002 — which only applies to .mdx — never squiggles at all. `make fix`
// and .claude/hooks/markdown-on-save.sh are the MDX path. Associating .mdx
// with markdown would be the wrong fix: it would turn on the fix-on-save
// below for MDX, running exactly the generic fixers the config globs keep
// away from .mdx (see the note below).
//
// WH002's autofix is owned by scripts/fix-mdx-fences.mjs, not by fix-on-save:
// the generic markdownlint fixers are never run over .mdx, because where
// their CommonMark parse disagrees with MDX they rewrite the inside of a
// code block. `make fix` (or the markdown-on-save hook) repairs it instead.
"editor.codeActionsOnSave": {
"source.fixAll.markdownlint": "explicit"
}
Expand All @@ -88,7 +103,14 @@
"markdown.validate.fileLinks.enabled": "ignore",
"markdown.validate.fileLinks.markdownFragmentLinks": "ignore",
"markdown.validate.fragmentLinks.enabled": "warning",
"markdown.validate.referenceLinks.enabled": "warning",
// Off, not "warning": Starlight asides are `:::note[Title]`, and the validator
// reads that trailing `[Title]` as a shortcut reference link with no
// definition — ~40 false positives across the docs, one per aside, plus a few
// more where it parses MDX JSX props the same way. This repo defines no
// reference-style links at all, so the check has nothing true to say here.
// (markdownlint's equivalent, MD052, stays quiet for its own reason: its
// shortcut_syntax option is off by default. Editor and CLI agree.)
"markdown.validate.referenceLinks.enabled": "ignore",
"markdown.validate.unusedLinkDefinitions.enabled": "warning",
"markdown.validate.duplicateLinkDefinitions.enabled": "warning",

Expand Down
32 changes: 30 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ make ci # Full pre-push pipeline — run it the documented way (
make build # Compile → bin/wavehouse
make dev # ClickHouse + hot-reload server on :8080 (Docker)
make deps-up # Start ClickHouse alone — for `make dev`; NOT needed by `make ci`
make dev-docs # Prod-faithful docs dev loop: rebuild-on-save + wrangler dev on :4321
make dev-docs # Prod-faithful docs dev loop: rebuild-on-save + wrangler dev on :4321 (next free port if busy)
make build-docs # Production build → docs/dist/
make preview-docs # Wrangler preview of the production build (auto-builds if dist/ missing)
make branding-docs # Regenerate logo/favicon/OG assets from docs/src/assets/branding/mark.svg
Expand All @@ -113,7 +113,7 @@ Tooling notes (the non-obvious bits `make help` won't tell you):
- `golangci-lint` is pinned in the Makefile (v2.11.4), auto-installed to `.bin/` on first `make lint` — kept out of `go.mod` (its deps conflict with the main module).
- `pnpm` (≥ 11.21) + `Node 22 LTS` (`.nvmrc`, matches CI) must be on PATH; `make tools` runs one root `pnpm install --frozen-lockfile` across the three workspaces (SDK `clients/ts/`, E2E `tests/e2e/sdk/`, docs `docs/`).
- **GNU Make 4+** required (uses `--output-sync=target`); macOS BSD Make 3.81 won't parse it. Full setup: `docs/src/content/docs/development.md` § Prerequisites.
- **Lint split**: Biome owns JS/TS/JSON, markdownlint owns Markdown *style*, misspell owns spelling (all under `make lint`/`make fix`); accuracy/clarity/doc-sync is the `docs-reviewer` gate (§Docs review).
- **Lint split**: Biome owns JS/TS/JSON, markdownlint owns Markdown *and MDX* style — including two repo-local rules, WH001 (no hard-wrapped prose) and WH002 (MDX fence beside a JSX tag) in `scripts/markdownlint-rules/` — misspell owns spelling (all under `make lint`/`make fix`); accuracy/clarity/doc-sync is the `docs-reviewer` gate (§Docs review). See §Markdown authoring rules.
- **Worktrunk** (`wt`, `.config/wt.toml`): `wt switch --create` seeds `.bin/` + `node_modules/` from main, then runs `make tools`.

## Testing Conventions
Expand Down Expand Up @@ -321,6 +321,34 @@ Source-of-truth pairs that must agree:

Before finishing a task, grep for the identifiers you touched (field names, env var names, endpoint paths) across docs to catch staleness.

### Markdown authoring rules

- **Never hard-wrap prose. One paragraph is one line.** No wrapping at 72/80 columns, no "semantic linefeeds" splitting a paragraph at sentence boundaries. Wrapped prose makes every later edit rewrap the whole block, so a one-word change lands as a five-line diff. Enforced by WH001 (`scripts/markdownlint-rules/no-hard-wrapped-prose.mjs`), which autofixes. Tables (with or without leading pipes), code, headings, setext underlines, blockquotes, JSX, `$$` display math, multi-line MDX `import`/`export`, and `:::` aside delimiters are left alone; a list item is joined as a unit, marker line included; an aside's *body* is joined but its delimiters are not.
- **In MDX, leave a blank line between a JSX tag and a code fence.** MDX itself renders the glued form correctly — verified by compiling both shapes with the same `@mdx-js/mdx` Astro uses. The blank line is what keeps *markdownlint* agreeing with it: markdownlint parses CommonMark, where `<TabItem …>` opens an HTML block that runs to the next blank line, so a glued fence is not a code block to any generic rule and `markdownlint --fix` will reformat the code inside it:

````mdx
<TabItem label="YAML">

```yaml
data_dir: ./data
```

</TabItem>
````

Enforced by WH002. **`.mdx` is never auto-fixed by the generic markdownlint rules** — `make fix` scopes that pass to `**/*.md`, because where markdownlint's CommonMark parse and MDX disagree a generic autofix rewrites the inside of a code block. MDX gets exactly one *structural* fixer, `scripts/fix-mdx-fences.mjs`, which only ever inserts a blank line beside a JSX tag (misspell still corrects spelling there — its curated list needs no parse). So `make lint` reports MDX problems but `make fix` will not silently repair them — including WH001 wrapping, which you must unwrap by hand in `.mdx`. You can't reach MDX with a bare `markdownlint-cli2 --fix` either — the config globs `.md` only, and the `.mdx` glob lives on `lint:md` — so that hazard is closed by construction rather than by this instruction.
- **Editors see WH001 in `.md` only.** The markdownlint extension reads `.markdownlint-cli2.jsonc`, `customRules` included, so no `.vscode` setting is needed (`markdownlint.customRules` is deprecated in favor of that file). But it activates on the `markdown` language ID, and `.mdx` is not associated with it — so WH002 never squiggles in the editor, and WH001 squiggles only in `.md`. Don't "fix" that with a `files.associations` entry: it would enable `source.fixAll.markdownlint` on `.mdx`, running exactly the generic fixers that must never see MDX. `make fix` and the agent hook are the MDX path.
- **These fix themselves as you write.** `.claude/hooks/markdown-on-save.sh` (PostToolUse, sibling of `gofumpt-on-save.sh`) runs the MDX fence pass on `.mdx`, markdownlint `--fix` on `.md`, and misspell on both, so an agent's output is corrected in the same pass rather than costing a lint failure and a manual cleanup. It only sees `Edit`/`Write`/`MultiEdit` — a file written through a Bash heredoc bypasses it, so run `make fix` after doing that.
- **WH001 is off under `.github/` and `.claude/`** (CI docs and agent prompts) via their own `.markdownlint.json`. It applies everywhere else, `AGENTS.md` and `CHANGELOG.md` included — so this is a narrower exclusion than `scripts/docs-prose.sh`, which also skips those two.

### Authoring docs-site pages

Three invariants the docs site enforces in code, each of which fails quietly rather than loudly if you hand-write around it:

- **Opt a page into the Cloud CTA with `cloudCta` frontmatter**, not by importing the component. `cloudCta: true` takes the default copy; `cloudCta: { title?, body? }` overrides it, which is the point — the CTA lands hardest when it names the work *that* page just described. Schema in `docs/src/content.config.ts`; the footer renders it. (The homepage is the exception: it passes `<CloudCta variant="band">` inline, because the wide band variant is splash-only and `template: splash` pages don't render the footer's copy.)
- **Never hand-write `®` or `™` in prose.** `rehype-trademarks` appends the symbol to each mark's first mention automatically, and `markFirstMentions` (`docs/src/config/trademarks.ts`) matches the bare name with no check for a symbol already there — so "ClickHouse®" renders as "ClickHouse®®". Add the mark to the registry in `trademarks.ts` and let the plugin place it; the footer notice is generated from the same registry.
- **Never hand-write `utm_*` params or `rel` on a link to `wavehouse.cloud` or `wave-rf.com`.** Use `cloudLink()` / `relFor()` from `docs/src/config/outbound.ts`. First-party links deliberately carry `rel="noopener"` *without* `noreferrer`, because `noreferrer` suppresses the `Referer` header PostHog turns into `$referring_domain` — writing the `rel` by hand is the easy way to silently destroy the attribution the whole feature exists for. Third-party links keep both.

### Authoring Mermaid diagrams

Diagrams render inside the Starlight content column (~46–58rem wide) as build-time SVG via `astro-themed-mermaid`, themed by `docs/src/config/mermaid-theme.mjs`. **Author them vertically so they fit the page at a legible size** — the single most common diagram mistake here is a wide left-to-right flowchart that gets scaled down to fit the column until its labels are unreadable.
Expand Down
Loading