build(docs): cloud referrals, standard fmt, and markdown lint rules - #489
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (18)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📜 Recent review details⏰ Context from checks skipped due to timeout. (2)
🧰 Additional context used📓 Path-based instructions (2)**/*.md📄 CodeRabbit inference engine (AGENTS.md)
Files:
docs/src/content/docs/**/*.{md,mdx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (4)📚 Learning: 2026-07-08T13:36:15.237ZApplied to files:
📚 Learning: 2026-06-10T15:01:09.027ZApplied to files:
📚 Learning: 2026-05-19T18:14:08.727ZApplied to files:
📚 Learning: 2026-05-19T18:26:33.503ZApplied to files:
🪛 LanguageToolCHANGELOG.md[uncategorized] ~14-~14: The official name of this software platform is spelled with a capital “H”. (GITHUB) [style] ~14-~14: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional. (EN_REPEATEDWORDS_NEED) AGENTS.md[style] ~326-~326: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional. (EN_REPEATEDWORDS_WHOLE) [style] ~341-~341: This sentence is over 40 words long. Consider splitting it up, as shorter sentences make the text easier to read. (TOO_LONG_SENTENCE) [uncategorized] ~342-~342: The official name of this software platform is spelled with a capital “H”. (GITHUB) [typographical] ~348-~348: Consider using an em dash in dialogues and enumerations. (DASH_RULE) 🔇 Additional comments (30)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThis PR adds Markdown and MDX linting and autofix workflows, integrates them with Claude and Makefile tooling, adds Cloud CTA and trademark features to the documentation site, improves development-server port selection, and updates documentation and examples. ChangesMarkdown and MDX authoring tooling
Documentation site features
Developer workflow
Estimated code review effort: 5 (Critical) | ~120 minutes Merge Risk: 🟠 High · up to This PR changes documentation generation, local development startup, and automatic Markdown/MDX rewriting. At the current head, unresolved issues can corrupt code examples, mis-handle preview ports, and render trademark notices inaccurately. Merge should be held until these correctness issues are fixed or explicitly accepted. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
📚 Docs preview is live → https://80655653-wavehouse-docs.wave-rf.workers.dev
|
Two classes of docs defect were arriving faster than review caught them,
both mechanical, both previously fixed by hand:
WH001 a prose paragraph broken across lines. Wrapped prose makes a
one-word edit land as a five-line diff. Autofixes by joining;
skips tables, code, lists, headings, JSX, and ::: delimiters.
WH002 an MDX code fence sitting directly against a JSX tag, which MDX
swallows into the JSX block so the code renders raw. The build
still succeeds, so nothing else caught it.
.mdx is now linted at all, which it previously was not — markdownlint
reading MDX as CommonMark turns out to be the feature that exposes
WH002's failure mode rather than a reason to skip the files.
WH002's autofix is a standalone pass (scripts/fix-mdx-fences.mjs) that
must run before markdownlint: while the blank line is missing CommonMark
sees no code block, so a YAML block's `#` comments read as ATX headings
and MD022/MD023/MD026/MD034 de-indent them out of the block and rewrite
bare URLs inside verbatim code. It shares its detector with the rule, so
there is still one implementation. A "WH002-only" markdownlint pass is
not expressible — cli2 always merges the nearest .markdownlint.json into
a --config run — which is why this is a script and not a config.
The Markdown track of `make fix` is now serial (fix-docs): markdownlint
and misspell both write .md/.mdx, so running them concurrently was a
lost-update race that predates this change.
A markdown-on-save PostToolUse hook applies the whole chain to files as
they are written, so an agent's own output is corrected in the same pass
instead of costing a lint failure and a manual cleanup. It only sees
Edit/Write/MultiEdit — a Bash heredoc bypasses it, so `make fix` remains
the backstop. Deliberately not wired into pre-commit: a commit hook that
rewrites and re-stages files changes what you reviewed.
WH001 is scoped to docs prose, off under .github/ and .claude/ via their
own .markdownlint.json — the line scripts/docs-prose.sh already draws.
Editors need no configuration: the markdownlint extension reads
customRules from .markdownlint-cli2.jsonc directly, and its
markdownlint.customRules setting is deprecated in favor of that file.
VS Code's markdown validator also stops reporting every :::note[Title]
aside as an undefined reference link, which it had been doing ~40 times
across the docs; the repo defines no reference-style links at all.
First pass of the rules over the tree reflows README.md,
CODE_OF_CONDUCT.md, the CHANGELOG header, and two paragraphs inside a
:::caution in the SDK docs, and tags one measurement block as ```text.
No wording changed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
Review findings on the rule added in fc01c57. All three were reproduced against the branch's own config, and all three were silent — markdownlint reported a clean file after destroying content, and the write-time hook applied them before anyone ran a command. - A GFM table written WITHOUT leading pipes was collapsed into one line. The delimiter row was guarded against being joined INTO, but nothing stopped a join from STARTING there and swallowing the body. Tables are now detected by looking ahead for the delimiter row, and the whole contiguous run is off limits. - A setext underline of one or two characters was joined into the heading text, demoting an <h1> to a paragraph. The pattern required three or more (`={3,}`), which is a thematic-break rule, not a setext one. - A multi-line MDX `import`/`export` body was joined. Only the opener matched, so the body classified as prose; with a `//` comment inside, joining swallows the rest of the statement and the MDX parse fails — a build break whose cause is invisible from the error. Also from review: - `make fix` was not a fixpoint. 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. `fix:md` now runs markdownlint twice. - WH002 shipped a fixInfo while four places said the fix was owned by scripts/fix-mdx-fences.mjs. A bare `--fix` therefore applied the blank line AND the generic fixes computed against the swallowed parse. It is now report-only, as documented. - biome.json excluded scripts/, so the new .mjs files were the only JavaScript in the repo outside the lint/format gate. Added. - fix-docs reaches fix-md through a sub-make, whose pnpm-install the parent cannot dedup against fix-ts's, so `make fix` could run two concurrent installs against one node_modules. Named as a prereq. - A list item is now joined as a unit. Joining only its continuation lines left a half-wrapped bullet the rule would never touch again. scripts/markdownlint-rules/rules.test.mjs adds 24 fixtures, run by the new `make test-md-rules` verify leaf. They drive the real CLI rather than calling the rules directly, because the defects live in how markdownlint combines one rule's line-delete with another rule's edit. Two of the three bugs above were four-line fixtures; nothing exercised those shapes. Docs corrected where they overclaimed: WH001's exclusions are narrower than scripts/docs-prose.sh (it applies to AGENTS.md and CHANGELOG.md, which that script skips), editor squiggles cover WH001 in .md only because the extension activates on Markdown and .mdx is not associated, markdown-on-save.sh was missing from the .claude/ reference page, and development.md sent readers to `go install golangci-lint@latest` when `make lint` downloads a pinned copy into .bin/ and never uses a global one. The prerequisites section now says the root pnpm workspace is where Biome and markdownlint-cli2 come from. Unrelated defects found by the same review and fixed here: two sentences left spliced mid-edit in sdk/queries.md, the unfinished warehouse -> wavehouse rename in sdk/index.mdx and the SDK readme, and a stray blank line inside an access-control.mdx YAML example. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
Blocking finding from pre-push review, plus the rest of that round.
markdownlint hands a rule a MASKED copy of any HTML comment's interior —
every non-whitespace character replaced by `.` — so that rules don't match
inside one. WH001 built its insertText from those same lines, so joining a
paragraph whose continuation touched a comment wrote the mask back to disk:
a note <!-- TODO: ask legal about the wording --> right here.
a note <!-- ..... ... ..... ..... ... ....... --> right here.
markdownlint then reports the file clean. Nothing in the tree is damaged
today — the at-risk comments all sit inside fences or on their own lines —
but the hook applies this unattended on every agent Markdown write, and a
reflow diff is precisely where it would hide. classify() now tracks HTML
comment state the way it tracks fences, and never joins a line touching one.
That also fixes the second symptom: prose immediately after a multi-line
comment's `-->` was being joined onto it, pulling the paragraph into the raw
HTML block.
Also from the same round:
- `fix-docs: pnpm-install` did not actually land in 4929f7f, despite that
commit's message. The edit was written and then clobbered by a later
write built from a stale buffer. It is in the file now, and grep-checked
rather than taken on faith.
- The ESM skip counted braces to find the end of a multi-line
import/export, and counted them inside comments and string literals too,
so one unbalanced brace ended the skip early and joined the remainder —
the same MDX build break 4929f7f fixed, reached a different way. It now
runs to the blank line that separates ESM from markdown, which is the
invariant the code already relied on and cannot miscount.
- The write-time hook ran markdownlint once where `fix:md` runs it twice,
for the reason `fix:md` does: WH001's insert carries pre-fix text, so
another rule's fix for a joined line is dropped on pass one. The hook
was leaving behind exactly the issue it exists to prevent.
- CHANGELOG had no [Unreleased] entry for this PR's headline change —
the Cloud CTAs, `cloudCta` frontmatter, UTM-tagged outbound links (and
the deliberate noopener-without-noreferrer posture), the per-page
trademark system, and the footer/hero rework. Added.
Docs corrected against the code: `make verify` runs `astro check`, not a
docs build, and the distinction matters because link validation only runs
under build-docs; the `make tools` list omitted misspell, shellcheck,
actionlint and the git-hooks install that CONTRIBUTING depends on;
architecture.md's EventMessage listing omitted the reserved Scope field;
sdk/admin.md lost the DLQ stream's actual failure mode (it connects and
receives no events, which reads nothing like "not functional") and had
drifted from its three sibling pages' shared boilerplate; README described
one auto-format hook where there are now two.
Rule fixtures are up to 27, covering the masking bug, the unbalanced-brace
ESM case, and prose following a multi-line comment.
The quick-start's `ghcr.io/wave-rf/wavehouse:latest` pull 404s (no tagged
release exists yet) — pre-existing, and a release decision rather than a
docs edit, so filed as #495 rather than folded in here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
Raised by the docs-reviewer gate as non-gating notes (CHANGELOG.md sits outside scripts/docs-prose.sh), but wrong as written: frontmatter pages render CloudCta variant "panel", not "band" (band is passed inline on the homepage only); rehype-trademarks appends the symbols while Trademarks.astro renders the notices, both off one registry; and the hero swap changed its second action, not its primary one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
There was a problem hiding this comment.
Actionable comments posted: 11
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 1b89cd52-b587-4fca-8d83-7a0bc87f3f60
📒 Files selected for processing (53)
.claude/.markdownlint.json.claude/hooks/markdown-on-save.sh.claude/settings.json.github/.markdownlint.json.markdownlint-cli2.jsonc.markdownlint.json.vscode/settings.jsonAGENTS.mdCHANGELOG.mdCODE_OF_CONDUCT.mdCONTRIBUTING.mdMakefileREADME.mdbiome.jsonclients/ts/README.mddocs/astro.config.mjsdocs/scripts/dev.mjsdocs/src/components/CloudCta.astrodocs/src/components/ExternalIcon.astrodocs/src/components/Footer.astrodocs/src/components/Header.astrodocs/src/components/Hero.astrodocs/src/components/LiveDemo.astrodocs/src/components/Trademarks.astrodocs/src/config/outbound.tsdocs/src/config/trademarks.tsdocs/src/content.config.tsdocs/src/content/docs/access-control.mdxdocs/src/content/docs/architecture.mddocs/src/content/docs/claude-code.mddocs/src/content/docs/configuration.mdxdocs/src/content/docs/deployment.mddocs/src/content/docs/development.mddocs/src/content/docs/durability.mddocs/src/content/docs/index.mdxdocs/src/content/docs/ingest-pipeline.mddocs/src/content/docs/pipes.mdxdocs/src/content/docs/reverse-proxy.mdxdocs/src/content/docs/sdk/admin.mddocs/src/content/docs/sdk/index.mdxdocs/src/content/docs/sdk/pipes.mddocs/src/content/docs/sdk/queries.mddocs/src/content/docs/sdk/reference.mddocs/src/content/docs/sdk/streaming.mddocs/src/content/docs/why-wavehouse.mddocs/src/plugins/rehype-trademarks.tsdocs/src/styles/global.csspackage.jsonscripts/fix-mdx-fences.mjsscripts/markdownlint-rules/lib/mdx-fences.mjsscripts/markdownlint-rules/mdx-fence-needs-blank-line.mjsscripts/markdownlint-rules/no-hard-wrapped-prose.mjsscripts/markdownlint-rules/rules.test.mjs
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: E2E tests
- GitHub Check: Coverage
- GitHub Check: Docs build
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
- Never hard-wrap prose. One paragraph is one line. No wrapping at 72/80 columns, no "semantic linefeeds" splitting a paragraph at sentence boundaries.
Files:
docs/src/content/docs/why-wavehouse.mdclients/ts/README.mddocs/src/content/docs/deployment.mddocs/src/content/docs/sdk/pipes.mdCODE_OF_CONDUCT.mddocs/src/content/docs/durability.mdREADME.mddocs/src/content/docs/pipes.mdxdocs/src/content/docs/sdk/admin.mdCONTRIBUTING.mddocs/src/content/docs/configuration.mdxdocs/src/content/docs/architecture.mddocs/src/content/docs/sdk/reference.mddocs/src/content/docs/reverse-proxy.mdxdocs/src/content/docs/sdk/streaming.mddocs/src/content/docs/claude-code.mddocs/src/content/docs/sdk/queries.mdCHANGELOG.mddocs/src/content/docs/access-control.mdxdocs/src/content/docs/ingest-pipeline.mddocs/src/content/docs/sdk/index.mdxdocs/src/content/docs/index.mdxdocs/src/content/docs/development.mdAGENTS.md
**/*.{go,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Every code change should update the corresponding docs in the same PR. A code change without its doc update is incomplete.
Files:
docs/src/content.config.tsdocs/src/plugins/rehype-trademarks.tsdocs/src/config/outbound.tsdocs/src/config/trademarks.ts
**/*.mdx
📄 CodeRabbit inference engine (AGENTS.md)
- In MDX, leave a blank line between a JSX tag and a code fence.
Files:
docs/src/content/docs/pipes.mdxdocs/src/content/docs/configuration.mdxdocs/src/content/docs/reverse-proxy.mdxdocs/src/content/docs/access-control.mdxdocs/src/content/docs/sdk/index.mdxdocs/src/content/docs/index.mdx
🧠 Learnings (12)
📓 Common learnings
Learnt from: taitelee
Repo: Wave-RF/WaveHouse PR: 479
File: docs/src/content/docs/sdk/admin.md:7-12
Timestamp: 2026-08-18T03:06:23.785Z
Learning: For WaveHouse pull request reviews, keep findings within the stated scope of the current PR. Defer broader documentation review feedback to a separately scoped documentation PR when the author requests it.
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 312
File: CHANGELOG.md:0-0
Timestamp: 2026-06-10T19:54:03.032Z
Learning: In the Wave-RF/WaveHouse repository, CHANGELOG.md entries under `[Unreleased]` use descriptive Keep-a-Changelog leads (e.g. "The structured-query column allowlist is now a hard cap…"), NOT the Conventional Commit PR title verbatim. Do not flag CHANGELOG entry leads for not matching the PR title — that is not a rule in this repo. There is no `.coderabbit.yaml`, and neither `AGENTS.md` nor `CONTRIBUTING.md` requires CHANGELOG leads to match PR titles.
Learnt from: CR
Repo: Wave-RF/WaveHouse
Timestamp: 2026-08-18T23:27:40.530Z
Learning: **Address and resolve every review finding** — substantive reply, fix it or track it in an issue, `@-mention` the bot, then resolve; never silently drop one ([§Review Response](`#review-response`)).
Learnt from: CR
Repo: Wave-RF/WaveHouse
Timestamp: 2026-08-18T23:27:40.530Z
Learning: **Never force-push or rebase a PR branch** — to absorb upstream main, `git merge origin/main` ([§Branch Maintenance](`#branch-maintenance`)).
📚 Learning: 2026-07-08T13:36:15.237Z
Learnt from: taitelee
Repo: Wave-RF/WaveHouse PR: 368
File: docs/src/components/SiteTitle.astro:9-22
Timestamp: 2026-07-08T13:36:15.237Z
Learning: For the WaveHouse Astro/Starlight docs site, theme switching is driven by a runtime `data-theme` attribute toggle (not `prefers-color-scheme` media queries). When implementing theme-specific branding assets (e.g., light/dark lockup/mark SVGs), prefer a CSS `display` swap keyed off selectors like `[data-theme="light"]` (and the corresponding dark selector) rather than `<picture media>`/media-query-based asset selection. This will intentionally cause all theme/breakpoint variants to be downloaded; keep this approach since it’s an accepted tradeoff to ensure pixel-identical branding.
Applied to files:
docs/src/components/Header.astrodocs/src/components/ExternalIcon.astrodocs/src/components/LiveDemo.astrodocs/src/components/Trademarks.astrodocs/src/components/Hero.astrodocs/src/components/CloudCta.astrodocs/src/components/Footer.astro
📚 Learning: 2026-06-10T15:01:09.027Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 312
File: docs/src/content/docs/development.md:0-0
Timestamp: 2026-06-10T15:01:09.027Z
Learning: In this repo’s Markdown review (all .md files), do not flag capitalization/style issues for literal paths starting with ".github/" (or any substring that is a path beginning with ".github/"). Treat ".github" as the correct lowercase dotfile directory name, even when it appears inside prose or code spans; automated checks such as LanguageTool’s "(GITHUB)" rule commonly produce false positives for this literal filesystem path.
Applied to files:
docs/src/content/docs/why-wavehouse.mdclients/ts/README.mddocs/src/content/docs/deployment.mddocs/src/content/docs/sdk/pipes.mdCODE_OF_CONDUCT.mddocs/src/content/docs/durability.mdREADME.mddocs/src/content/docs/sdk/admin.mdCONTRIBUTING.mddocs/src/content/docs/architecture.mddocs/src/content/docs/sdk/reference.mddocs/src/content/docs/sdk/streaming.mddocs/src/content/docs/claude-code.mddocs/src/content/docs/sdk/queries.mdCHANGELOG.mddocs/src/content/docs/ingest-pipeline.mddocs/src/content/docs/development.mdAGENTS.md
📚 Learning: 2026-08-11T15:22:23.813Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 448
File: clients/ts/src/types.ts:61-71
Timestamp: 2026-08-11T15:22:23.813Z
Learning: In the TypeScript SDK, `ClientConfig.baseURL` must be an absolute URL with a scheme and host. A relative `baseURL` causes `resolveURL` to throw a `TypeError` on the first request. REST requests reject because `resolveURL` runs outside `request()`'s retry `try` block. In `clients/ts/src/stream/sse.ts`, `SSETransport` catches this failure and reports `SSE_CONNECT_ERROR` through the optional `StreamSubscriber.error` callback, so a subscriber without that callback can observe no error.
Applied to files:
clients/ts/README.mddocs/src/content/docs/sdk/index.mdx
📚 Learning: 2026-08-12T21:45:41.503Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 456
File: clients/ts/src/pipes.ts:0-0
Timestamp: 2026-08-12T21:45:41.503Z
Learning: In the TypeScript SDK, `PipeRef.fetch` uses the exported `PipeRequestOptions` type rather than `Pick<RequestOptions, "signal">`. `PipeRequestOptions` declares `limit?: never` so both object literals and named `RequestOptions` values that include `limit` fail type checking instead of silently dropping the limit. A value declared as `RequestOptions` is intentionally not assignable to `PipeRequestOptions`, even if it has no runtime `limit`; consumers can use `PipeRequestOptions` for shared pipe, table, and query-builder fetch options, or use an inferred `{ signal }` object.
Applied to files:
docs/src/content/docs/sdk/pipes.mddocs/src/content/docs/sdk/queries.md
📚 Learning: 2026-05-19T18:14:08.727Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 147
File: .claude/settings.json:35-38
Timestamp: 2026-05-19T18:14:08.727Z
Learning: In Claude Code hook scripts (e.g., WaveHouse’s `.claude/hooks/`), don’t rely on `PostToolUse:Agent` for verdict parsing: it exposes subagent output as a structured JSON object at `.tool_response.content[].text`, so regex-based “VERDICT:” parsing is unreliable. Use the `SubagentStop` event instead: parse `VERDICT:` lines from the flat string at `.last_assistant_message`, and use `agent_type` to filter to the intended subagent (since `SubagentStop` has no `matcher` support—do any filtering in the script).
Applied to files:
.claude/hooks/markdown-on-save.sh
📚 Learning: 2026-05-19T18:26:33.503Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 147
File: .claude/hooks/review-marker.sh:0-0
Timestamp: 2026-05-19T18:26:33.503Z
Learning: In `.claude/hooks/review-marker.sh`, treat `jq` parse errors and “missing-jq” cases as intentional non-fatal marker-writer behavior: use `exit 0` (not `exit 2`). This hook is a marker writer, not an enforcement gate—the absence of `tmp/review-passed-<sha>` is the downstream signal consumed by `agent-bash-gate.sh` and `.githooks/pre-push`. Do not change these `exit` codes or stderr messaging (e.g., ending diagnostics with “— no marker written”), since `exit 2` would incorrectly conflate hook misbehavior with deliberate `iterate`/`block` verdicts.
Applied to files:
.claude/hooks/markdown-on-save.sh
📚 Learning: 2026-08-13T12:17:52.620Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 470
File: docs/src/content/docs/reverse-proxy.mdx:137-144
Timestamp: 2026-08-13T12:17:52.620Z
Learning: For Wave-RF/WaveHouse documentation, verify claims about implementation control flow against the authoritative implementation source (for example, internal/auth/auth.go) rather than relying solely on docs/** content. Documentation may lag behind or paraphrase behavior, so control-flow claims should be confirmed in source code.
Applied to files:
docs/src/content/docs/pipes.mdxdocs/src/content/docs/configuration.mdxdocs/src/content/docs/reverse-proxy.mdxdocs/src/content/docs/access-control.mdxdocs/src/content/docs/sdk/index.mdxdocs/src/content/docs/index.mdx
📚 Learning: 2026-05-19T14:42:16.296Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 142
File: docs/src/styles/global.css:13-20
Timestamp: 2026-05-19T14:42:16.296Z
Learning: In the Wave-RF/WaveHouse repo, CSS review should treat Tailwind v4 at-rules (e.g., `theme`, `layer`, and `import ... layer()`) as intentional and valid. They are processed by `tailwindcss/vite` configured in `docs/astro.config.mjs` (`vite.plugins`), so you should not flag these directives in `docs/src/styles/**/*.css` as “unknown at-rules” during review.
Applied to files:
docs/src/styles/global.css
📚 Learning: 2026-05-19T14:42:16.296Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 142
File: docs/src/styles/global.css:13-20
Timestamp: 2026-05-19T14:42:16.296Z
Learning: In Wave-RF/WaveHouse, Tailwind v4 directives in CSS (e.g., `theme`, `layer`, and `import ... layer(...)`) are intentionally supported by the docs build pipeline (via `tailwindcss/vite` configured in `docs/astro.config.mjs`). During code review, do NOT flag these as “unknown at-rules” in files under `docs/src/styles/`; they should be allowed because they validate during the build.
Applied to files:
docs/src/styles/global.css
📚 Learning: 2026-08-13T12:17:56.360Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 470
File: docs/src/content/docs/reverse-proxy.mdx:137-144
Timestamp: 2026-08-13T12:17:56.360Z
Learning: For Wave-RF/WaveHouse, derive learnings about implementation control flow from the implementation source, such as `internal/auth/auth.go`, rather than from documentation under `docs/**`. Documentation can lag behind or paraphrase behavior and must not be treated as authoritative evidence for control-flow claims.
Applied to files:
docs/src/content/docs/ingest-pipeline.md
📚 Learning: 2026-08-12T15:28:23.992Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 456
File: docs/src/content/docs/sdk/index.mdx:0-0
Timestamp: 2026-08-12T15:28:23.992Z
Learning: For `docs/src/content/docs/sdk/index.mdx`, the documented workaround for the undici idle-event-loop keep-alive stall is to upgrade to undici 8.10.0 or later. If a consumer is pinned to an affected version, `new Agent({ pipelining: 0 })` must be merged as `dispatcher` into the SDK-provided `RequestInit`; this disables keep-alive reuse. Configuring `keepAliveTimeout` does not mitigate this stall because the socket retirement timer is starved by the same idle event loop.
Applied to files:
docs/src/content/docs/sdk/index.mdx
🪛 ast-grep (0.45.1)
docs/src/config/trademarks.ts
[warning] 185-185: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(PATTERN_SOURCE, "g")
Note: [CWE-1333] Inefficient Regular Expression Complexity
(regexp-from-variable)
[warning] 185-185: Do not use variable for regular expressions
Context: new RegExp(PATTERN_SOURCE, "g")
Note: [CWE-1333] Inefficient Regular Expression Complexity. Security best practice.
(regexp-non-literal-typescript)
🪛 LanguageTool
docs/src/content/docs/sdk/pipes.md
[style] ~22-~22: Consider shortening this phrase to just ‘whether’, unless you mean ‘regardless of whether’.
Context: ...clared* as RequestOptions is rejected whether or not it actually carries a limit, since the ...
(WHETHER)
CODE_OF_CONDUCT.md
[style] ~22-~22: Try using a synonym here to strengthen your wording.
Context: ...ind * Trolling, insulting or derogatory comments, and personal or political attacks * Pu...
(COMMENT_REMARK)
docs/src/content/docs/sdk/streaming.md
[style] ~115-~115: Since ownership is already implied, this phrasing may be redundant.
Context: ...sues/449)) — so key on timestamp plus your own row identity if duplicates matter. Rep...
(PRP_OWN)
[style] ~122-~122: Since ownership is already implied, this phrasing may be redundant.
Context: ...ed more of on this path; see Supplying your own fetch. ...
(PRP_OWN)
[style] ~210-~210: Since ownership is already implied, this phrasing may be redundant.
Context: ...ers — treat initial() never firing as its own failure. Where auth rejects and the s...
(PRP_OWN)
[typographical] ~210-~210: The word ‘Where’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ...es auth or the URL, never the backfill. Re-run the fetch then; you never have t...
(WRB_QUESTION_MARK)
[style] ~210-~210: This sentence is over 40 words long. Consider splitting it up, as shorter sentences make the text easier to read.
Context: ...ver have to work out which row you hit. Leave it a moment first: events reach a stream from the message queue before the ingest worker lands them in ClickHouse, and its per-table batcher flushes on size or a deadline with the insert still to complete after that (see Ingest pipeline), so an immediate re-fetch can miss the newest rows. Why auth splits the way it does....
(TOO_LONG_SENTENCE)
docs/src/content/docs/sdk/queries.md
[style] ~84-~84: Consider using the typographical ellipsis character here instead.
Context: ....fetch()does. Mutually exclusive with.select(...) and with aggregations (.count(), ....
(ELLIPSIS)
CHANGELOG.md
[grammar] ~13-~13: Ensure spelling is correct
Context: ...ies are centralised in outbound.ts so every one carries the same UTM params (`utm_sourc...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[uncategorized] ~14-~14: The official name of this software platform is spelled with a capital “H”.
Context: ...rned off for CI docs and agent prompts (.github/, .claude/) while applying everywher...
(GITHUB)
[grammar] ~29-~29: Ensure spelling is correct
Context: ...owed, one MDX measurement block tagged, aside bodies unwrapped** (`docs/src/content/d...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
docs/src/content/docs/ingest-pipeline.md
[style] ~8-~8: Consider using “who” when you are referring to a person instead of an object.
Context: ...-dive on internal/ingest — the worker that turns the stream of ingest events into ...
(THAT_WHO)
[grammar] ~105-~105: Please add a punctuation mark at the end of paragraph.
Context: ... ## Why per table? The bug this design fixes A single shared batch across all table...
(PUNCTUATION_PARAGRAPH_END)
[style] ~132-~132: Since ownership is already implied, this phrasing may be redundant.
Context: ...l leftover after a size flush waits for its own size/timer.** When 500 rows flush and 1...
(PRP_OWN)
[style] ~213-~213: Consider an alternative for the overused word “exactly”.
Context: ...an fsync and therefore slow, which is exactly why acks run in the background (ackWg...
(EXACTLY_PRECISELY)
docs/src/content/docs/sdk/index.mdx
[style] ~61-~61: Consider using the typographical ellipsis character here instead.
Context: ...sm.sh/@wavehouse/sdk@0.1.0); jsDelivr (.../+esm) and unpkg (?module`) serve the ...
(ELLIPSIS)
[style] ~378-~378: Since ownership is already implied, this phrasing may be redundant.
Context: ...he SDK's value stands alone, and two of your own entries differing only in case collapse...
(PRP_OWN)
[style] ~398-~398: Since ownership is already implied, this phrasing may be redundant.
Context: ...row if it is set at all. See Supplying your own fetch for w...
(PRP_OWN)
[style] ~411-~411: Since ownership is already implied, this phrasing may be redundant.
Context: ...h client certificates, wrap requests in your own middleware (logging, tracing, circuit b...
(PRP_OWN)
[style] ~411-~411: Since ownership is already implied, this phrasing may be redundant.
Context: ...racing, circuit breaking), stub HTTP in your own tests without monkey-patching a global,...
(PRP_OWN)
[style] ~429-~429: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...used with .stream() or .liveQuery() needs a different set: .ok, .status, `.ty...
(EN_REPEATEDWORDS_NEED)
[style] ~462-~462: Since ownership is already implied, this phrasing may be redundant.
Context: ... you don't really control, and auditing your own code for fetch calls won't tell you: ...
(PRP_OWN)
[style] ~475-~475: Since ownership is already implied, this phrasing may be redundant.
Context: ... one underlying reason: undici declares its own request/response types, separate from t...
(PRP_OWN)
[style] ~475-~475: Consider using the typographical ellipsis character here instead.
Context: ...the two aren't structurally assignable. { ...init, dispatcher } as never covers the ...
(ELLIPSIS)
[style] ~475-~475: Consider shortening this phrase to just ‘whether’, unless you mean ‘regardless of whether’.
Context: ...ither spelling, so one snippet compiles whether or not your lib includes DOM); and the retur...
(WHETHER)
docs/src/content/docs/index.mdx
[grammar] ~112-~112: Please add a punctuation mark at the end of paragraph.
Context: ...like a database. Subscribe to it like a socket The TypeScript SDK wraps the w...
(PUNCTUATION_PARAGRAPH_END)
AGENTS.md
[style] ~326-~326: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...prose makes every later edit rewrap the whole block, so a one-word change lands as a ...
(EN_REPEATEDWORDS_WHOLE)
[style] ~341-~341: This sentence is over 40 words long. Consider splitting it up, as shorter sentences make the text easier to read.
Context: ...nd 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 pass, markdownlint --fix, and misspell on each .md/.mdx you write, 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...
(TOO_LONG_SENTENCE)
[uncategorized] ~342-~342: The official name of this software platform is spelled with a capital “H”.
Context: ...fter doing that. - WH001 is off under .github/ and .claude/ (CI docs and agent p...
(GITHUB)
🔇 Additional comments (48)
CODE_OF_CONDUCT.md (1)
5-47: LGTM!clients/ts/README.md (1)
60-60: LGTM!docs/src/content/docs/claude-code.md (1)
28-28: LGTM!Also applies to: 50-52, 212-212
docs/scripts/dev.mjs (3)
8-9: LGTM!Also applies to: 22-25
65-68: 🩺 Stability & AvailabilityVerify that Wrangler requires both loopback addresses.
This code rejects a port when either
127.0.0.1or::1is occupied. Wrangler 4.81.0 passes oneargs.ipvalue toserver.hostname, and its CLI documents--ipas a single listen address. The supplied evidence does not prove that Wrangler binds both addresses. Confirm the actual behavior. If Wrangler binds one address, probe only that address or pass--ipexplicitly. (github.com)Also applies to: 79-82
247-262: 🩺 Stability & AvailabilityVerify the remaining port race.
The probe closes before
spawn()starts Wrangler. Another process can claim the selected port during that interval. The later resolution reduces the window but does not remove it. Verify the child failure path. Retry port selection when Wrangler fails specifically because the address is already in use.docs/src/config/outbound.ts (1)
1-138: LGTM!docs/src/content.config.ts (1)
4-32: LGTM!docs/src/components/CloudCta.astro (1)
1-228: LGTM!docs/src/components/ExternalIcon.astro (1)
1-50: LGTM!docs/src/content/docs/pipes.mdx (1)
185-199: LGTM!docs/src/content/docs/sdk/index.mdx (5)
14-61: LGTM!Also applies to: 75-119
144-299: LGTM!
310-310: LGTM!Also applies to: 346-407
411-479: LGTM!
497-528: LGTM!docs/src/content/docs/sdk/pipes.md (1)
6-6: LGTM!Also applies to: 20-34
docs/src/content/docs/sdk/queries.md (1)
6-6: LGTM!Also applies to: 65-65, 84-84, 163-163, 225-225
docs/src/content/docs/sdk/reference.md (1)
6-6: LGTM!docs/src/config/trademarks.ts (1)
29-330: LGTM!docs/src/components/Hero.astro (1)
2-4: LGTM!Also applies to: 44-52, 116-123, 134-145, 176-176, 451-451
docs/src/components/LiveDemo.astro (1)
13-13: LGTM!Also applies to: 110-110, 794-794
docs/astro.config.mjs (1)
15-15: LGTM!Also applies to: 35-38
docs/src/plugins/rehype-trademarks.ts (1)
20-165: LGTM!docs/src/components/Header.astro (1)
228-228: LGTM!docs/src/content/docs/sdk/streaming.md (1)
6-10: LGTM!Also applies to: 43-43, 70-70, 79-81, 100-100, 112-122, 191-195, 208-214
docs/src/content/docs/sdk/admin.md (1)
6-6: LGTM!Also applies to: 71-71
docs/src/content/docs/access-control.mdx (1)
4-5: LGTM!Also applies to: 111-144, 156-177, 195-220, 261-293, 312-338, 364-389, 472-485, 497-567
docs/src/content/docs/configuration.mdx (1)
4-5: LGTM!Also applies to: 220-220, 298-301, 356-356
docs/src/content/docs/deployment.md (1)
4-5: LGTM!docs/src/content/docs/architecture.md (1)
4-5: LGTM!Also applies to: 249-249
docs/src/content/docs/durability.md (1)
4-5: LGTM!docs/src/content/docs/index.mdx (1)
18-36: LGTM!Also applies to: 57-57, 112-131, 140-158, 185-195, 223-223
docs/src/content/docs/ingest-pipeline.md (1)
4-10: LGTM!Also applies to: 54-57, 85-85, 99-113, 131-132, 144-144, 158-168, 189-203, 213-217, 228-232, 249-263
docs/src/content/docs/reverse-proxy.mdx (1)
4-5: LGTM!Also applies to: 44-60, 71-71, 82-85
docs/src/content/docs/why-wavehouse.md (1)
4-6: LGTM!docs/src/styles/global.css (1)
93-98: LGTM!Also applies to: 186-188, 662-691, 920-920, 1111-1125
README.md (1)
111-111: LGTM!Also applies to: 135-135
.claude/.markdownlint.json (1)
1-4: LGTM!.github/.markdownlint.json (1)
1-4: LGTM!.vscode/settings.json (1)
74-75: LGTM!Also applies to: 98-105
.claude/hooks/markdown-on-save.sh (1)
1-68: LGTM!.claude/settings.json (1)
52-55: LGTM!biome.json (1)
13-14: LGTM!Makefile (1)
266-270: LGTM!Also applies to: 410-417, 440-459, 473-480
AGENTS.md (1)
102-102: LGTM!Also applies to: 116-116, 324-343
CONTRIBUTING.md (1)
94-94: LGTM!docs/src/content/docs/development.md (1)
29-32: LGTM!Also applies to: 134-154, 403-438, 511-514
Ten of eleven findings; the eleventh needs a design decision and is called
out below.
WH002 missed a multiline JSX opening tag. `<TabItem` / `label="YAML">` is one
opening tag, but the detector only pattern-matched the single line above the
fence, so it reported the closing-side violation alone — and the fixer then
inserted one of the two blank lines the block needs and left it broken.
It now walks back to the line that starts the tag.
WH001 treated any leading `---` as frontmatter, even with no closing
delimiter, which marked every remaining line "skip" and silently disabled the
rule for the file. A lone `---` is a thematic break; frontmatter is only
frontmatter when it closes.
Both have fixtures (29 total).
dev-docs port handling, all reachable through DOCS_PORT:
- "" and "0" became port 0, binding an ephemeral port and announcing
http://localhost:0; non-numeric became NaN, so the scan ran zero times and
reported "NaN–NaN"; out-of-range threw ERR_SOCKET_BAD_PORT from inside the
probe. DOCS_PORT is now validated up front and rejected loudly.
- portFree() counted every error except EADDRINUSE/EACCES as "free", so
ENOTFOUND or a bad host would claim a port we cannot bind. Only
EADDRNOTAVAIL on ::1 — the IPv6-disabled case the comment describes —
still counts as free.
- The scan could walk past 65535 and throw instead of returning null. The
last candidate is clamped, and the failure message reports the clamped
range.
Trademarks.astro hid its own catch-all. `owner` is optional in the registry,
and trademarks.ts states that an ownerless mark is covered by the "all other
product names ... their respective owners" sentence in the footer — but that
sentence lives inside the block that was gated on `notices.length`. A page
naming only ownerless marks got its ® in prose with nothing explaining it.
Now gated on marks found, with the generated owner notices rendered only when
they exist. Latent today: all 29 registry entries currently have an owner.
Docs corrected against the code: .vscode/settings.json still claimed
WH001/WH002 squiggle in the editor — the correction made in AGENTS.md,
development.md and .markdownlint-cli2.jsonc, missed in that one file (the
extension activates on the `markdown` language ID, so WH001 covers .md alone
and WH002 never squiggles); the verify-parallel leaf inventory said 9 and is
now 13, naming test-md-rules, lint-sh, lint-gha and test-classify-paths;
"All test commands use gotestsum" is now "All Go test commands", since
test-md-rules runs node --test; and ingest-pipeline.md now names the reserved
`scope` field the same way architecture.md does, so the two pages describe one
contract.
NOT addressed, deliberately: Footer.astro derives trademark notices from
`entry.body` while rehype-trademarks skips headings and code, so a mark named
only in a heading or fence yields a footer notice with no matching symbol in
the prose (architecture.md and OpenTelemetry is the live example). That
contradicts the component's own "the footer can't promise a notice the prose
didn't mark" invariant, but fixing it properly means sharing the eligible-node
set between the plugin and the footer, which is a design decision rather than
a review fix. Over-inclusive is the safe direction meanwhile.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
The justification given for WH002 everywhere in this branch was wrong, and the docs-reviewer gate caught it. "MDX swallows the fence into the JSX block and the code renders raw" is not what the toolchain does: @astrojs/mdx builds its processor on @mdx-js/mdx 3, which parses a glued fence as an ordinary code block. Verified two ways before rewriting anything. Compiling a minimal glued and spaced <TabItem> sample against docs/node_modules' own @mdx-js/mdx@3.1.1 yields a `pre`/`code` node in both. Compiling this branch's real before/after of configuration.mdx — c93ae12^ and c93ae12 — yields 4 code blocks with the same four languages on both sides. The pages on main were never rendering raw, and a contributor who checked would have found the stated reason false and reasonably concluded the rule was pointless. The rule is not pointless; the hazard is one parser removed. markdownlint parses CommonMark, where `<TabItem …>` opens an HTML block that runs to the next blank line. A glued fence is therefore not a code block to any generic rule, so a YAML block's `#` comments read as ATX headings and MD022/MD023/ MD026/MD034 rewrite the code inside it — de-indenting it out of the block and autolinking bare URLs. That corruption is real and reproduced; the blank line is what keeps MDX and markdownlint agreeing about where the code is. Corrected in AGENTS.md, development.md, the WH002 rule header, the CHANGELOG entry, and the PR body. scripts/fix-mdx-fences.mjs, the shared detector and .markdownlint-cli2.jsonc already described the interop correctly and needed only a word. Also from review: portFree() allowed only EADDRNOTAVAIL as the "no usable IPv6" escape, which is what you get when ::1 is merely unconfigured. Where IPv6 is compiled out of the runtime (ipv6.disable=1, WSL1, images without AF_INET6) the socket call fails first with EAFNOSUPPORT or EPROTONOSUPPORT, so every candidate would fail the ::1 probe and `make dev-docs` would exit with "no free port in 4321-4340" on a machine where nothing holds one. That was a regression this branch introduced while tightening the check for CodeRabbit. All three codes now count, still only on ::1. CONTRIBUTING's new Docs-prose bullet moved to the end of the Code Style list, which is otherwise a contiguous Go run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
Two review findings, both reproduced first.
The markdown-on-save hook had no project boundary, so it reformatted any
.md/.mdx the tool wrote — anywhere. Confirmed by invoking it exactly as Claude
Code does against a file in /tmp: the paragraph came back joined. In practice
that reaches agent memory files under ~/.claude/projects/*/memory/, scratch
notes, and Markdown in unrelated checkouts, all of which a session writes
routinely — imposing this repo's prose conventions on files that never asked
for them. `rel` stays absolute exactly when the path is not under the project
root, so the guard is a two-line `case` right after it is computed. Sibling
worktrees were already safe: cli2 honors the `ignores` globs even for an
explicitly named file.
WH001 deleted a two-space hard break carried by a continuation line. The loop
guard stopped the run from continuing PAST such a line, but the line itself was
still absorbed through `.trim()`, which strips the trailing spaces that encode
the <br>:
alpha line / beta line␠␠ / gamma line -> alpha line beta line / gamma line
The two lines SHOULD join — they are one paragraph — but the break after them
must survive, which it now does. The existing fixture only covered a hard break
on the run's first line, i.e. the case that already worked, so it was giving
false confidence. Added one for the continuation shape (30 fixtures).
Also, from the same review: the WH002 rule header now names the trigger
condition. While the fenced content holds no blank line, CommonMark's HTML
block runs past the whole thing and the generic rules stay silent; the
rewriting only starts once an interior blank line ends that block. Without
that clause a minimal repro looks harmless, which is the same
wrong-conclusion trap that motivated e32c6d9.
The CHANGELOG now records the one substantive content deletion in the reflow
pass — reverse-proxy.mdx's `:::caution[Check the prefix actually survives to
the wire]`, which told readers the path-prefix fix was unreleased and shipped
on the `@dev` tag. That stops being true with the first tagged release, so the
removal is correct; it was just invisible inside a formatting commit.
Not fixed here: .claude/hooks/gofumpt-on-save.sh has the same missing boundary
check. It is outside this PR's scope and lower risk (stray .go files outside a
repo are rare where stray .md files are the norm), but it wants the same guard.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
The reflow entry described removing reverse-proxy.mdx's stale @dev-tag caution and still closed with "No wording changed" — both cannot be true of one entry, and the file was missing from its own manifest, so the only content change in that pass was the one thing a reader scanning it would not see. Split into the reflow (no wording changed) and the deletion, named in the headline, with a note that the durable half of the warning survives in the nginx and ingress-nginx notes on the same page. Also from review: - The WH001/WH002 entry omitted the three files that document the rules (AGENTS.md, CONTRIBUTING.md, development.md), which this file otherwise lists exhaustively. - The golangci-lint correction made earlier on this branch was recorded nowhere; it now has a Fixed entry, since sending contributors to install a version make ignores is worth a changelog line. - development.md's make verify row listed 12 of the 13 verify-parallel leaves, missing test-classify-paths — the same drift CodeRabbit caught in the Makefile's own comment. - reverse-proxy.mdx's Cloud CTA said "so none of this page applies". The 1 MiB / 16 MiB request-body caps on that page are WaveHouse-side and apply identically on Cloud, so a reader taking it literally skips the page and meets a '13 in production. Scoped to the proxy configuration, which is the part Cloud actually handles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
Splitting the reflow entry in 85a1bcd rewrote the head and left the original tail in place, so the entry stated the undici info-string fix, the unwrapped asides and "No wording changed" twice, verbatim. Removed the trailing copy; the entry now ends at the caution-removal note. While there, the reflow manifest gained the two other files that pass touched with fence/reflow-only edits: pipes.mdx (WH002 blank lines around the TabItem fences) and sdk/reference.md (WH001 unwrap of the intro). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/markdownlint-rules/no-hard-wrapped-prose.mjs (1)
117-120: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftPreserve multiline MDX ESM across internal blank lines.
classify()clearsesmbefore processing every blank line. For a valid export object, this classifies later properties and};as prose, and WH001 autofix rewritesbody: "b",\n};tobody: "b", };, corrupting the MDX source. The pinnedmicromark-extension-mdxjs-esm@3.0.0continues after a blank line when Acorn reports incomplete input. Do not treat every blank line as an ESM boundary. Add an end-to-end regression fixture, update the WH001 documentation, and test against the pinned MDX dependency.Source: MCP tools
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2bb3a43c-5fb2-4bf1-bca4-e14195585540
📒 Files selected for processing (16)
.claude/hooks/markdown-on-save.sh.markdownlint-cli2.jsonc.vscode/settings.jsonAGENTS.mdCHANGELOG.mdCONTRIBUTING.mdMakefiledocs/scripts/dev.mjsdocs/src/components/Trademarks.astrodocs/src/content/docs/development.mddocs/src/content/docs/ingest-pipeline.mddocs/src/content/docs/reverse-proxy.mdxscripts/markdownlint-rules/lib/mdx-fences.mjsscripts/markdownlint-rules/mdx-fence-needs-blank-line.mjsscripts/markdownlint-rules/no-hard-wrapped-prose.mjsscripts/markdownlint-rules/rules.test.mjs
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: Docs preview
- GitHub Check: E2E tests
- GitHub Check: Coverage
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{go,ts,tsx,js,jsx,md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
- Comment the why, not the what. Add a comment only when the reason isn't obvious from the code; a line that matches the surrounding pattern needs none.
Files:
docs/src/content/docs/reverse-proxy.mdxCONTRIBUTING.mdAGENTS.mdCHANGELOG.mddocs/src/content/docs/ingest-pipeline.mddocs/src/content/docs/development.md
**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
- Never hard-wrap prose. One paragraph is one line. No wrapping at 72/80 columns, no "semantic linefeeds" splitting a paragraph at sentence boundaries.
Files:
docs/src/content/docs/reverse-proxy.mdxCONTRIBUTING.mdAGENTS.mdCHANGELOG.mddocs/src/content/docs/ingest-pipeline.mddocs/src/content/docs/development.md
**/*.mdx
📄 CodeRabbit inference engine (AGENTS.md)
- In MDX, leave a blank line between a JSX tag and a code fence.
Files:
docs/src/content/docs/reverse-proxy.mdx
**/*
📄 CodeRabbit inference engine (AGENTS.md)
Every code change should update the corresponding docs in the same PR. A code change without its doc update is incomplete.
Files:
docs/src/content/docs/reverse-proxy.mdxCONTRIBUTING.mdAGENTS.mddocs/src/components/Trademarks.astroscripts/markdownlint-rules/lib/mdx-fences.mjsCHANGELOG.mddocs/scripts/dev.mjsdocs/src/content/docs/ingest-pipeline.mdscripts/markdownlint-rules/no-hard-wrapped-prose.mjsdocs/src/content/docs/development.mdscripts/markdownlint-rules/mdx-fence-needs-blank-line.mjsMakefilescripts/markdownlint-rules/rules.test.mjs
🧠 Learnings (5)
📚 Learning: 2026-08-13T12:17:52.620Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 470
File: docs/src/content/docs/reverse-proxy.mdx:137-144
Timestamp: 2026-08-13T12:17:52.620Z
Learning: For Wave-RF/WaveHouse documentation, verify claims about implementation control flow against the authoritative implementation source (for example, internal/auth/auth.go) rather than relying solely on docs/** content. Documentation may lag behind or paraphrase behavior, so control-flow claims should be confirmed in source code.
Applied to files:
docs/src/content/docs/reverse-proxy.mdx
📚 Learning: 2026-06-10T15:01:09.027Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 312
File: docs/src/content/docs/development.md:0-0
Timestamp: 2026-06-10T15:01:09.027Z
Learning: In this repo’s Markdown review (all .md files), do not flag capitalization/style issues for literal paths starting with ".github/" (or any substring that is a path beginning with ".github/"). Treat ".github" as the correct lowercase dotfile directory name, even when it appears inside prose or code spans; automated checks such as LanguageTool’s "(GITHUB)" rule commonly produce false positives for this literal filesystem path.
Applied to files:
CONTRIBUTING.mdAGENTS.mdCHANGELOG.mddocs/src/content/docs/ingest-pipeline.mddocs/src/content/docs/development.md
📚 Learning: 2026-07-08T13:36:15.237Z
Learnt from: taitelee
Repo: Wave-RF/WaveHouse PR: 368
File: docs/src/components/SiteTitle.astro:9-22
Timestamp: 2026-07-08T13:36:15.237Z
Learning: For the WaveHouse Astro/Starlight docs site, theme switching is driven by a runtime `data-theme` attribute toggle (not `prefers-color-scheme` media queries). When implementing theme-specific branding assets (e.g., light/dark lockup/mark SVGs), prefer a CSS `display` swap keyed off selectors like `[data-theme="light"]` (and the corresponding dark selector) rather than `<picture media>`/media-query-based asset selection. This will intentionally cause all theme/breakpoint variants to be downloaded; keep this approach since it’s an accepted tradeoff to ensure pixel-identical branding.
Applied to files:
docs/src/components/Trademarks.astro
📚 Learning: 2026-05-19T18:14:08.727Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 147
File: .claude/settings.json:35-38
Timestamp: 2026-05-19T18:14:08.727Z
Learning: In Claude Code hook scripts (e.g., WaveHouse’s `.claude/hooks/`), don’t rely on `PostToolUse:Agent` for verdict parsing: it exposes subagent output as a structured JSON object at `.tool_response.content[].text`, so regex-based “VERDICT:” parsing is unreliable. Use the `SubagentStop` event instead: parse `VERDICT:` lines from the flat string at `.last_assistant_message`, and use `agent_type` to filter to the intended subagent (since `SubagentStop` has no `matcher` support—do any filtering in the script).
Applied to files:
.claude/hooks/markdown-on-save.sh
📚 Learning: 2026-05-19T18:26:33.503Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 147
File: .claude/hooks/review-marker.sh:0-0
Timestamp: 2026-05-19T18:26:33.503Z
Learning: In `.claude/hooks/review-marker.sh`, treat `jq` parse errors and “missing-jq” cases as intentional non-fatal marker-writer behavior: use `exit 0` (not `exit 2`). This hook is a marker writer, not an enforcement gate—the absence of `tmp/review-passed-<sha>` is the downstream signal consumed by `agent-bash-gate.sh` and `.githooks/pre-push`. Do not change these `exit` codes or stderr messaging (e.g., ending diagnostics with “— no marker written”), since `exit 2` would incorrectly conflate hook misbehavior with deliberate `iterate`/`block` verdicts.
Applied to files:
.claude/hooks/markdown-on-save.sh
🪛 LanguageTool
CHANGELOG.md
[uncategorized] ~14-~14: The official name of this software platform is spelled with a capital “H”.
Context: ...rned off for CI docs and agent prompts (.github/, .claude/) while applying everywher...
(GITHUB)
[grammar] ~30-~30: Ensure spelling is correct
Context: ...owed, one MDX measurement block tagged, aside bodies unwrapped, one stale caution rem...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🔇 Additional comments (17)
docs/scripts/dev.mjs (1)
44-65: LGTM!Also applies to: 89-119, 287-287
docs/src/components/Trademarks.astro (1)
27-28: LGTM!Also applies to: 31-42, 45-60
docs/src/content/docs/ingest-pipeline.md (2)
20-20: LGTM!
144-144: 🩺 Stability & AvailabilityMake the ownership statement match the closure.
The documented goroutine evaluates
b.tableafter it starts. Therefore, it readsbin addition torows. Verify thatb.tableis immutable for the lifetime ofb. Otherwise, snapshotb.tablebefore launching the goroutine and pass the value into the closure.docs/src/content/docs/reverse-proxy.mdx (1)
5-5: LGTM!scripts/markdownlint-rules/lib/mdx-fences.mjs (1)
11-18: LGTM!scripts/markdownlint-rules/mdx-fence-needs-blank-line.mjs (1)
4-62: LGTM!scripts/markdownlint-rules/no-hard-wrapped-prose.mjs (1)
56-116: LGTM!Also applies to: 141-177, 184-226
scripts/markdownlint-rules/rules.test.mjs (1)
1-212: LGTM!.markdownlint-cli2.jsonc (1)
16-46: LGTM!.vscode/settings.json (1)
66-80: LGTM!Also applies to: 104-111
.claude/hooks/markdown-on-save.sh (1)
39-47: 🔒 Security & PrivacyCanonicalize
file_pathbefore checking the repository root.The check removes the literal
$PWD/prefix and rejects only values that remain absolute. An input such as$PWD/../outside.mdbecomes../outside.md, so this guard accepts a path whose resolved target is outside the repository. Symlinked paths require the same protection. Resolve the existing path withrealpathand compare it with the canonical repository root before running any fixer. Add traversal and symlink tests. If the earlier validation already performs this canonicalization, add tests that prove it.Makefile (1)
498-502: LGTM!Also applies to: 509-513, 656-657
CONTRIBUTING.md (1)
97-97: LGTM!AGENTS.md (1)
303-343: LGTM!docs/src/content/docs/development.md (1)
24-32: LGTM!Also applies to: 289-289, 403-438, 489-514
CHANGELOG.md (1)
11-15: LGTM!Also applies to: 27-30
CodeRabbit on #489. openingTagAbove() accepted any line starting with a tag and ending in `>`, which a complete inline element satisfies. Both shapes reproduced against the detector: <span>text</span> + fence -> reported as an opening tag <Foo> / prose ending > + fence -> backward scan crossed the prose The detector is shared with scripts/fix-mdx-fences.mjs, so a false positive does not merely over-report — it inserts a blank line into valid MDX, which is the one thing an autofixer must never do. The candidate is now validated as a whole rather than by its endpoints: the joined text must contain exactly one `>`, at the very end, and no `</` anywhere. That rejects complete elements and any scan that crossed prose, while still accepting a genuine tag split across lines. An attribute value holding a literal `>` is rejected too — a miss rather than a corruption, which is the direction this file is meant to err in. Two regression fixtures (32 total), and the tree still reports no WH002 violations. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
Review finding on a6b30e9, which fixed a false positive and introduced a worse false negative. The whole-candidate test rejected any opening tag carrying a > inside an attribute value or expression container — label="a>b", when={a > b}, onClick={() => f()}. That is not the harmless miss the previous commit message claimed. The rejection applies to the OPENING side only; the closing side still fires. So fix-mdx-fences.mjs inserts one of the two blank lines the block needs, WH002 then reports nothing, and the generic markdownlint pass rewrites the code inside the fence — autolinking a bare URL and de-indenting the YAML comments it reads as headings. A miss on both sides would be harmless; a miss on one side is precisely the corruption this rule exists to prevent. Strings and {...} containers are now masked before the "exactly one >, at the very end, and no </" test. Every rejection a6b30e9 added survives — complete inline elements, prose-crossing scans, nested elements, a comment after an open tag, multiline self-closing tags — and the four >-carrying shapes are accepted again. Verified end to end on the reviewer's repro through the real fix:md chain (fix-mdx-fences -> markdownlint --fix x2): both blank lines land and the fenced YAML comes out byte-identical. Two fixtures pin both sides firing for an attribute value and an expression container (3' total). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
Review finding on d97cd5e. The `{...}` mask ran once and replaced with `{}`, so it collapsed the innermost level only and left braces behind — meaning a second pass could not merge outward either. A `>` sitting between an outer and an inner brace therefore survived the mask, and the whole-candidate test rejected the tag: the same opening-side-only rejection d97cd5e set out to fix, reached through a different door. The reviewer's analysis of why it matters is the part worth recording. For a custom component name, CommonMark needs HTML block type 7 — a complete, valid open tag on one line — and a surviving top-level `>` is exactly what disqualifies it, so no HTML block forms, the fence stays a real code block, and MD031 supplies the blank line. Harmless. But type 6 needs only a known block-level tag name followed by whitespace and ignores the rest of the line, so `<div onClick={() => open({tab: 1})}>` DOES swallow the fence. Confirmed end to end: one violation reported, one blank line inserted, and the generic pass then rewrote `url: https://example.com/x` inside the fence to an autolink. `div`, `p`, `section`, `details`, `summary`, `figure`, `table`, `blockquote`, `ul` and `li` are all type-6 names, and `<div class="...">` already appears in index.mdx. Masking now loops innermost-first to a fixpoint with a brace-free replacement, so it converges. Verified across 14 shapes: every rejection added in a6b30e9 still rejects, every `>`-carrying tag accepts including deep nesting and a quote inside braces, and the type-6 repro comes out of the full fix:md chain with its fenced content byte-identical. Fixture added for that shape (35). Also fixes the `make tools` row in development.md, which named two of the five pinned binaries and omitted the git-hooks install — and disagreed with the prerequisites section this same branch added two paragraphs above. Known limitation, left alone on the reviewer's advice: in WH001, `jsxTag` is not reset at a blank line, so a paragraph opening with an inline element (`<kbd>Ctrl</kbd> opens the palette`) disables the rule for the rest of that file. It is miss-only, nothing in the tree reaches it, and both obvious fixes trade this nit for a corruption risk on genuine multi-line tags. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
Root fix for the class of defect this branch kept producing, rather than a sixth patch to the heuristic that kept producing it. markdownlint parses CommonMark; MDX does not parse as CommonMark. Where the two disagree, a generic autofix rewrites the inside of a code block. Confirmed by running `markdownlint --fix` over four shapes and diffing: <TabItem label="YAML"> + fence -> YAML rewritten (url autolinked) <Aside> / prose + fence -> YAML rewritten <TabItem / label="YAML"> + fence -> blank lines only, code untouched <span>x</span> text + fence -> blank line only, code untouched So `fix:md` now scopes the generic pass to **/*.md and never touches .mdx. `make lint` still CHECKS .mdx — reporting the disagreement is useful, acting on it is not. MDX gets exactly one fixer, scripts/fix-mdx-fences.mjs, which can only insert a blank line beside a JSX tag, so its worst failure is a render-neutral blank line rather than rewritten code. The on-save hook makes the same split. That third case is worth recording: a fence glued to a MULTILINE opening tag was never a swallowing hazard. `<TabItem` alone is not a complete tag and is not a block-level name, so no HTML block opens, the fence stays real code, and MD031 just adds blank lines. Three commits of escalating complexity — openingTagAbove, then attribute masking, then fixpoint masking — went into hardening a path that was never dangerous, because the original report was taken at face value instead of tested. The cost of the new arrangement, stated plainly in AGENTS.md, development.md and the CHANGELOG: an .mdx problem that `make lint` reports may need fixing by hand, WH001's hard-wrapped prose included. Since the docs site is heavily .mdx, that is most of WH001's value gone on the files that need it most. Filed #499 to do this properly — a formatter that understands MDX rather than a linter that guesses — with the constraints and the failed approaches written down. Also from review: - The hook's repo-scope guard was a literal prefix strip, so `<repo>/../x.md` stripped to a relative path and sailed past the bail. Both sides are now resolved with `pwd -P` and compared, closing the `..` and symlink escapes. - WH001 concatenated $$ display-math blocks — remark-math and rehype-katex are both wired into docs/astro.config.mjs, so that is a supported construct here. Tracked like a fence, since guarding only the delimiters still let the expressions between them join. Two fixtures (37 total). - Trademarks.astro claimed "the footer can't promise a notice the prose didn't mark". It can, and two shipping pages already do: /architecture and /deployment carry notices for marks named only in headings or code, which rehype-trademarks skips. The comment now says the footer is a superset and points at #497. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
9600e13 changed what the tooling does and left four descriptions promising the old behavior — including the two a reader actually skims and the one an agent acts on: - development.md's make fix row still advertised the MDX chain the same commit removed, contradicting that page's own prose two sections up. - claude-code.md described the hook as applying markdownlint --fix "WH001 unwrapping included" to .mdx, which is exactly what it no longer does. - AGENTS.md said the hook runs markdownlint --fix on each .md/.mdx, two bullets below the bullet saying .mdx is never generically fixed. That is the source of truth an agent reads, so the wrong half would have won. - CONTRIBUTING told a first-time contributor that make fix unwraps their prose, which for the .mdx-heavy docs site it does not. The retired ordering rationale also survived verbatim in five headers — the WH002 rule, the shared detector, the hook, .vscode/settings.json and AGENTS.md all still said the MDX pass must run BEFORE markdownlint. It doesn't run before anything now; the generic fixers simply never see .mdx. The no-fixInfo decision stands, but for that reason rather than ordering. And "MDX gets exactly one fixer" was false: make fix -> fix-prose runs misspell over DOCS_PROSE, which globs .mdx too. Verified by planting a typo in an .mdx and watching make fix-prose correct it. Now "exactly one STRUCTURAL fixer", with the spelling caveat stated, which also softens the hand-fixing warning — a spelling finding in .mdx is not a hand fix. Also adds $$ display math to the WH001 skip lists in AGENTS.md and development.md; 9600e13 taught classify() about it but both enumerations read as exhaustive, so a docs author using KaTeX had no way to know it was safe. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
Review finding on 9600e13. Scoping the fix command was not enough: the config still declared globs ["**/*.md", "**/*.mdx"], and --fix is orthogonal to globs, so a bare `markdownlint-cli2 --fix` from the repo root — the most natural manual invocation — still rewrote the inside of MDX code blocks. The only thing stopping it was a sentence in AGENTS.md saying not to, which is the class of guard the rest of this work replaced with structure. The .mdx glob now lives on `lint:md` instead. cli2 appends config globs to CLI globs, so linting still enumerates both extensions (57 files, WH002 still reported), fix:md collapses back to a plain `markdownlint-cli2 --fix` with no --no-globs incantation, and a bare --fix is safe by construction. The residual failure mode flips to a bare LINT under-reporting .mdx, which is the strictly better of the two. Pinned by two fixtures rather than by prose. Getting them right took two attempts worth recording: the first copied the repo config into a temp dir without rewriting its relative customRules paths, so cli2 failed to load the rules and the "file unchanged" assertion passed because the tool had errored out. The second used fenced content with no interior blank line — and without one, CommonMark's HTML block runs past the whole fence and the generic rules stay quiet, so it passed with the guard removed too. Both were verified against a deliberately regressed config: 39/39 with the guard, 38/39 with **/*.mdx put back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
… scope The scoping moved from the fix command into the config in 6f52f'8, and five places still narrated the old locus — .markdownlint-cli2.jsonc's own header worst of all, which claimed .mdx is "checked here" and that fix:md does the scoping, contradicting the globs note 25 lines below it in the one file a maintainer opens to answer exactly that question. A reader following any of those pointers lands on a bare `markdownlint-cli2 --fix` with no scope in sight and could reasonably conclude the guard was dropped. Also fixed: two headers still said "the only fixer" where the rest of the tree now says "the only STRUCTURAL fixer" (misspell writes .mdx), and development.md still described file selection as living in one config file when it is now split across two. The substantive one: the hook claimed its misspell pass used "the same scope `make lint-prose` uses". It did not. DOCS_PROSE was a local find over docs/src/content (21 files) while the hook used scripts/docs-prose.sh (27), so README, CONTRIBUTING, SECURITY, SUPPORT, CODE_OF_CONDUCT and the SDK readme were being rewritten by the on-save hook but never spell-checked by the gate. DOCS_PROSE now calls the canonical script: the comment becomes true, the gap closes, and a second definition of a list AGENTS.md names a single source of truth goes away. Verified clean over all 27 before switching. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
The header said "0.8125em is the 13px the hero used, expressed against that button's 16px label". The CSS 30 lines below uses 0.8667em and explains it as 13px against a 0.9375rem label, and Hero.astro:'38 confirms .wh-hero__action is 0.9375rem (15px) — so 13/15 = 0.8667 is right and the header was stale on both numbers. Worth fixing in a file whose whole purpose is being the one place the icon is defined. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
Both review gates landed on the same two stragglers from 26f237c. The block above DOCS_PROSE still opened "Markdown + MDX prose sources under the Starlight content dir ... so the find only runs when those targets run", directly above the sentence explaining that the find is gone and six governance docs are now in scope — two adjacent paragraphs stating opposite things. And lint-prose's header still described its scope as the Starlight content. Collapsed into one block that leads with the script as the source of truth, keeping the reason the list is files rather than a directory (misspell must never read a .ts content-config as text) — now enforced by the script's extension filter where find -name used to. Also documents an asymmetry the switch introduced and nobody had written down: the script lists TRACKED files, while the hook gates on `is-match`, a pure path test. A brand-new page gets fixed on write but is invisible to make fix-prose until it is staged, so the symptom is a commit failing on spelling right after a clean make fix. It self-heals via pre-commit; widening the script to `git ls-files -co` would also feed untracked drafts to the docs-reviewer, which is why it stays tracked-only. scripts/docs-prose.sh's own header now says it defines TWO consumers — the reviewer's reading list and the misspell gate set — so a later exclusion added for reviewer-scoping reasons doesn't silently drop a file from spell-checking. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
…rules
The fixture set claimed to cover "every construct classify() recognizes" — in
its own header and in the PR body — and three recognized shapes had none:
link-reference and footnote definitions, the _/asterisk thematic-break
spellings, and real closed YAML frontmatter. Added rather than softening the
claim, since these are exactly the class that already bit this rule twice.
The frontmatter one matters most and both gates asked for it independently: the
suite had the lone-`---` thematic-break case but nothing pinning that WH001
still fires AFTER real frontmatter. That failure is silent and total — if the
frontmatter state stopped clearing, every remaining line classifies as skip and
the rule quietly stops working across the whole docs site while lint stays
green. '' fixtures now.
Also documents three docs-site invariants that lived only in source comments,
each of which fails quietly if you hand-write around it:
- cloudCta frontmatter is how a page opts into the Cloud CTA (the homepage
band variant is the deliberate exception, since splash pages skip the
footer copy).
- Never hand-write a trademark symbol. markFirstMentions matches the bare
name and appends unconditionally, so "ClickHouse®" renders "ClickHouse®®".
Verified against trademarks.ts rather than assumed.
- Never hand-write utm_* or rel on a first-party link. noreferrer suppresses
the Referer header PostHog turns into $referring_domain, so writing it by
hand silently destroys the attribution the feature exists for.
Plus the comment reflow orphans this branch's insert-edits left in the Makefile,
the on-save hook, and the two rule files.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tz6sJGBiWn6uqUTA1BmSR
Three conflicts, all resolved keeping both sides' intent: - Makefile: both branches added a verify leaf -- #489's test-md-rules and this branch's test-release-channel. Kept both. - sdk/index.mdx: #489 relocated the CDN paragraph out of the <Tabs> block and rewrote it, dropping this branch's corrections with it. Took #489's structure and re-applied both: `latest` resolves to a 0.0.0-dev.* snapshot until the first STABLE release (a prerelease publishes to alpha/beta/rc/next and leaves it alone), and the two anchors. #489's prose linked /development#releasing-the-sdk, a section this branch renames -- neither PR's CI could catch that, since the anchor exists on main until this branch lands. Now #cutting-a-release and #the-dev-channel; no reference to the dead anchor survives repo-wide. - CHANGELOG: resolved with the same script as the previous merges (main's entry text into this branch's structure). 336 main entries + 27 branch entries, none lost, none invented, no duplicates. Preamble taken from main, which carries #489's reflow -- its new WH001/no-hard-wrapped-prose rule rejects the old hard-wrapped Keep a Changelog boilerplate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015bVwHtNakQgmnBhMcfW8pe
Four findings from the pre-push gates, all consequences of the merge. - Makefile: #489 rewrote the verify-parallel comment into an accurate 13-item inventory; adding test-release-channel made it 14 while the comment still said 13 and never named the new leaf. The conflict resolution updated the dependency line and missed the comment three lines above documenting the same list. - CHANGELOG: the restructure entry quoted "35 bands" and "333 top-level entries byte-identical". Both describe a base that has moved twice since. Rather than recount figures that drift on every merge, the entry now states the property qualitatively and says why no number is quoted -- a stale figure beside a "verified" claim is worse than none. This is the fourth stale self-describing count on this branch; the pattern is the number, not the arithmetic. - CHANGELOG: stamp moved to 2026-08-19. #489 merged today and this section covers it, so the 08-18 date was already wrong. - README: `docker pull ...:latest` 404s today and the explanation landed after the code block, so a reader copy-pastes the failure first. Annotated inline. - development.md: the `go install` paragraph describes what a TAGGED release reports, but sat as the last paragraph of "The dev channel", whose subject is what happens between releases. Moved to "What a release publishes", beside the archive and image bullets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015bVwHtNakQgmnBhMcfW8pe
Reconciles the Go SDK branch with main for the first time since 2026-08-11 (merge base e945ecc). All 14 conflicts were prose; no Go or TypeScript source conflicted. Ten of the conflicts share one cause: #489's WH001 rule unwrapped hard-wrapped prose across the docs tree on main, so the branch's edits sat on pre-reflow text. Each was resolved by taking main's reflowed paragraph and re-applying the branch's semantic edit into it. Resolutions of note: - AGENTS.md #14, README.md, docs/index.mdx, why-wavehouse.md: the branch's "zero third-party runtime dependencies in both SDKs" is no longer true — main added eventsource-parser to the TypeScript SDK. Kept the branch's two-SDK structure with main's dependency facts: one runtime dependency in TypeScript, none in Go. - CHANGELOG.md: main cut 0.1.0 on 2026-08-19, so the branch's Go SDK entry had landed inside a released section. Moved it to the top of main's new Unreleased/Added, and re-homed the branch-only TypeScript docs-corrections entry under Unreleased/Fixed. - Makefile: unioned the verify-parallel leaf list (16 leaves) and kept main's per-leaf inventory comment. - sdk/streaming.md: main's rewrite already covers both cautions the branch added — the projection-dedup caveat (#449) in step 3 and the like/not_like backfill-vs-live divergence (#451) in the operator section — so main's version supersedes it wholesale. - sdk/queries.md: kept main's /v1/ops/query routing and operator rows, grafted on the branch's select_all carve-out (unrestricted/admin roles do get SELECT *), the not_like wire token, and the aggregation allowlist. - sdk/index.mdx: dropped the branch's stale CDN paragraph — main's reflowed copy carries the post-#470 fetch wording and the correct /development#cutting-a-release anchor. The branch's dead #releasing-the-sdks link would have failed the docs build. - reverse-proxy.mdx: #428 is closed and the fix shipped in 0.1.0, so main's deletion of the prefix caution stands; kept a Go example on main's renamed /api/wavehouse prefix. - development.md: discarded the branch's release section entirely — main already documents the clients/go/vX.Y.Z tag scheme it was guessing at. Also clears the forward-references main left for this PR: the "(pending #434)" marker on make release-sdk-go, the paragraph saying it refuses to run until clients/go/ exists, and the missing Go line in the release example. Documents what a Go SDK release publishes (the module proxy serves the tag; no workflow fires, so no GitHub Release).
The six Go SDK pages were authored before #489 landed WH001 (no-hard-wrapped-prose), so they wrapped prose at ~76 columns while the rest of the docs tree had been reflowed. `make fix` output, whitespace only — verified no content changed in any of the four files.
Summary
The docs needed some tweaks to be more consistent, and largely this PR exists to let the docs refer to the managed cloud service we run at wavehouse.cloud including with CTA bits in docs and referral links.
Making the docs consistent turned out to need tooling, so this PR also lands it. Two classes of defect were arriving faster than review caught them, both mechanical and both previously fixed by hand:
WH001/ no-hard-wrapped-prose — a prose paragraph must be one line. Hard-wrapped prose makes a one-word edit land as a five-line diff. Autofixes by joining; tables, code, headings, setext underlines, blockquotes, JSX, and multi-line MDXimport/exportare left alone, and a list item is joined as a unit.WH002/ mdx-fence-needs-blank-line — an MDX code fence sitting directly against a JSX tag (<TabItem label="YAML">immediately followed by a fence). MDX itself renders that correctly; compiling both shapes against the same@mdx-js/mdxAstro uses gives identical output, and the pages onmainwere never broken. What the blank line protects is the interop: markdownlint parses CommonMark, where the tag opens an HTML block running to the next blank line, so the fence is not a code block to any generic rule — andmarkdownlint --fixthen reformats the code inside it (de-indenting YAML comments it reads as headings, autolinking bare URLs). The blank line is what keeps the two parsers agreeing about where the code is..mdxis now linted at all, which it previously wasn't. markdownlint reading MDX as CommonMark turns out to be the feature that exposesWH002's failure mode rather than a reason to skip the files.Two ordering constraints are load-bearing and documented where they bite:
.mdx..markdownlint-cli2.jsoncglobs.mdonly and the.mdxglob lives onlint:md, so linting covers both extensions while no fixing pass —fix:mdor a baremarkdownlint-cli2 --fix— can reach MDX. This is the root fix for a whole class of corruption: while a fence sits glued to a JSX tag, CommonMark sees no code block, so a YAML block's#comments read as ATX headings and MD022/MD023/MD026/MD034 de-indent them out of the block and autolink bare URLs inside verbatim code. MDX's only structural fixer isscripts/fix-mdx-fences.mjs, which can only insert a blank line. The cost — an.mdxfinding may need fixing by hand, WH001 wrapping included — is stated in AGENTS.md,development.mdand the CHANGELOG, and #499 tracks doing it properly.make fixis now serial. markdownlint and misspell both write.md/.mdx, so running them concurrently was a lost-update race that predates this PR..claude/hooks/markdown-on-save.sh(PostToolUse, sibling ofgofumpt-on-save.sh) applies the chain to files as they're written, so an agent's output is corrected in the same pass instead of costing a lint failure and a manual cleanup round-trip. Deliberately not wired intopre-commit: a commit hook that rewrites and re-stages files changes what you reviewed.Test plan
make cigreen locally atc685aea(Go total 91.1%, ts-total 79.11%, all gates passed)make test-md-rules— 44 fixtures inscripts/markdownlint-rules/rules.test.mjs, run as amake verifyleaf. They drive the realmarkdownlint-cli2rather than calling the rules directly, because the defects live in how markdownlint combines one rule's line-delete with another rule's editimport/exportbodies, HTML-comment text (markdownlint hands rules a masked buffer), multiline JSX opening tags, and two-space hard breaks on a continuation linemake fixverified to be a fixpoint (a second pass is a no-op)c93ae129starlight-links-validatorreports all internal links validCODE_OF_CONDUCT.mdconfirmed zero token changes vsmainunder--word-diff— pure reflow.README.mdis reflow plus one deliberate word (an auto-format hook→auto-format hooks, since this PR adds a second one), andCONTRIBUTING.mdgains a Code Style bulletRelated Issues
None.