docs: condense documentation to ~65% of original length by word count - #462
docs: condense documentation to ~65% of original length by word count#462jfwoods wants to merge 1 commit into
Conversation
The docs had accumulated redundancy, hedging, and verbose phrasing. This tightens prose across the site and README without changing what any page says: 9,153 words removed, 52 KB smaller. Every code fence is byte-identical to before, and every heading, table, list, link, and YAML frontmatter block is preserved. Identifier retention was checked mechanically per file -- inline code spans, link targets, and bare URLs present in the original are all present in the condensed version. Per-file prose ratios land between 62% and 77% for substantive pages. Four short stubs (404, sdk/pipes, sdk/reference, sdk/admin) compress less because they carry little redundancy to remove.
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe pull request revises documentation across the product overview, API and architecture contracts, deployment and runtime operations, development workflows, ingest and durability, SDK usage, and user guides. No exported or public entity declarations change. ChangesDocumentation refresh
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 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://7e4614f6-wavehouse-docs.wave-rf.workers.dev |
There was a problem hiding this comment.
Actionable comments posted: 21
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 77a20929-7bbc-4266-b12f-94bcc463bacf
📒 Files selected for processing (16)
README.mddocs/src/content/docs/404.mddocs/src/content/docs/api.mddocs/src/content/docs/architecture.mddocs/src/content/docs/claude-code.mddocs/src/content/docs/deployment.mddocs/src/content/docs/development.mddocs/src/content/docs/durability.mddocs/src/content/docs/getting-started.mddocs/src/content/docs/ingest-pipeline.mddocs/src/content/docs/sdk/admin.mddocs/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.md
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
- GitHub Check: Docs build
- GitHub Check: Lint
🧰 Additional context used
📓 Path-based instructions (3)
docs/src/content/docs/**/*.{md,mdx}
📄 CodeRabbit inference engine (AGENTS.md)
Documentation prose under the Starlight docs site must stay accurate against code, include runnable examples where relevant, and reflect code↔docs sync for changed behavior.
Files:
docs/src/content/docs/404.mddocs/src/content/docs/sdk/pipes.mddocs/src/content/docs/sdk/admin.mddocs/src/content/docs/sdk/streaming.mddocs/src/content/docs/durability.mddocs/src/content/docs/claude-code.mddocs/src/content/docs/why-wavehouse.mddocs/src/content/docs/sdk/reference.mddocs/src/content/docs/getting-started.mddocs/src/content/docs/sdk/queries.mddocs/src/content/docs/architecture.mddocs/src/content/docs/deployment.mddocs/src/content/docs/development.mddocs/src/content/docs/ingest-pipeline.mddocs/src/content/docs/api.md
README.md
📄 CodeRabbit inference engine (AGENTS.md)
Keep the repository README in sync with user-facing API, SDK, or governance changes when the README is part of the documented source-of-truth set.
Files:
README.md
docs/src/content/docs/development.md
📄 CodeRabbit inference engine (AGENTS.md)
Update the development docs when changing build or test process details.
Files:
docs/src/content/docs/development.md
🧠 Learnings (63)
📓 Common learnings
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to docs/src/content/docs/**/*.{md,mdx} : Documentation prose under the Starlight docs site must stay accurate against code, include runnable examples where relevant, and reflect code↔docs sync for changed behavior.
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to README.md : Keep the repository README in sync with user-facing API, SDK, or governance changes when the README is part of the documented source-of-truth set.
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to clients/ts/README.md : Keep the TypeScript SDK README in sync with SDK-facing changes and public client behavior.
📚 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/404.mddocs/src/content/docs/sdk/pipes.mddocs/src/content/docs/sdk/admin.mddocs/src/content/docs/sdk/streaming.mddocs/src/content/docs/durability.mddocs/src/content/docs/claude-code.mddocs/src/content/docs/why-wavehouse.mdREADME.mddocs/src/content/docs/sdk/reference.mddocs/src/content/docs/getting-started.mddocs/src/content/docs/sdk/queries.mddocs/src/content/docs/architecture.mddocs/src/content/docs/deployment.mddocs/src/content/docs/development.mddocs/src/content/docs/ingest-pipeline.mddocs/src/content/docs/api.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to clients/ts/README.md : Keep the TypeScript SDK README in sync with SDK-facing changes and public client behavior.
Applied to files:
docs/src/content/docs/sdk/pipes.mddocs/src/content/docs/sdk/admin.mddocs/src/content/docs/sdk/streaming.mdREADME.mddocs/src/content/docs/sdk/reference.mddocs/src/content/docs/getting-started.mddocs/src/content/docs/sdk/queries.mddocs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to internal/pipes/**/*.{go} : Named query pipes must remain fail-closed: per-pipe `allowed_roles` is the only execute-path gate, with admin-only behavior when no allowlist is present.
Applied to files:
docs/src/content/docs/sdk/pipes.mddocs/src/content/docs/architecture.mddocs/src/content/docs/api.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to README.md : Keep the repository README in sync with user-facing API, SDK, or governance changes when the README is part of the documented source-of-truth set.
Applied to files:
docs/src/content/docs/sdk/admin.mdREADME.mddocs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to clients/ts/**/*.{ts,tsx} : The TypeScript SDK in `clients/ts/` is the canonical client; when backend API changes affect users, update the SDK surface, auth handling, query builder, streaming helpers, pipes/policy helpers, or regenerated types as needed.
Applied to files:
docs/src/content/docs/sdk/streaming.mddocs/src/content/docs/sdk/reference.mddocs/src/content/docs/sdk/queries.mddocs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to internal/stream/**/*.{go} : Streaming/SSE code must preserve the hub’s per-role projection model, subscriber queues, bucket fan-out, heartbeating, and metrics semantics.
Applied to files:
docs/src/content/docs/sdk/streaming.mddocs/src/content/docs/sdk/reference.mddocs/src/content/docs/architecture.mddocs/src/content/docs/ingest-pipeline.mddocs/src/content/docs/api.md
📚 Learning: 2026-08-12T15:28:20.891Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 456
File: docs/src/content/docs/sdk/index.mdx:0-0
Timestamp: 2026-08-12T15:28:20.891Z
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/streaming.mddocs/src/content/docs/sdk/reference.md
📚 Learning: 2026-08-11T21:56:06.521Z
Learnt from: jfwoods
Repo: Wave-RF/WaveHouse PR: 434
File: docs/src/content/docs/sdk/go/queries.md:356-362
Timestamp: 2026-08-11T21:56:06.521Z
Learning: In Wave-RF/WaveHouse Go SDK cursor pagination, `fetchNextTyped` uses only the first `QueryBuilder.OrderBy` column and a strict `gt` or `lt` filter. Duplicate values at a page boundary can skip rows. The Go SDK documentation must require a unique ordering column until the shared Go and TypeScript composite-cursor or tie-breaker implementation tracked in GitHub issue `#452` is available.
Applied to files:
docs/src/content/docs/sdk/streaming.mddocs/src/content/docs/sdk/queries.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to docs/src/content/docs/configuration.md : Document every new or changed configuration option in the configuration docs.
Applied to files:
docs/src/content/docs/durability.mddocs/src/content/docs/deployment.mddocs/src/content/docs/api.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Every code change must update its documentation and `CHANGELOG.md` in the same PR.
Applied to files:
docs/src/content/docs/claude-code.mddocs/src/content/docs/development.md
📚 Learning: 2026-05-19T18:26:36.393Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 147
File: .claude/hooks/review-marker.sh:0-0
Timestamp: 2026-05-19T18:26:36.393Z
Learning: In `.claude/hooks/review-marker.sh` (WaveHouse), jq parse errors and missing-jq cases use `exit 0` (not `exit 2`) intentionally. The hook is a marker *writer*, not a gate: the absence of `tmp/review-passed-<sha>` is itself the downstream enforcement signal (consumed by `agent-bash-gate.sh` and `.githooks/pre-push`). Using `exit 2` would conflate hook misbehavior with a deliberate `iterate`/`block` verdict. Stderr diagnostics end with "— no marker written" so the orchestrator's next-turn system reminder makes the outcome explicit without requiring knowledge of the hook contract.
Applied to files:
docs/src/content/docs/claude-code.md
📚 Learning: 2026-05-19T18:14:13.458Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 147
File: .claude/settings.json:35-38
Timestamp: 2026-05-19T18:14:13.458Z
Learning: In Claude Code hook scripts, `PostToolUse:Agent` exposes the subagent's final output as `.tool_response.content[].text` — a structured JSON object, not a flat string — which makes regex-based verdict parsing unreliable. `SubagentStop` instead exposes `.last_assistant_message` as a flat string and `agent_type` for filtering, but has no `matcher` support (filtering must be done in-process in the script). For WaveHouse's `review-marker.sh` hook in `.claude/hooks/`, `SubagentStop` is the correct event to use for parsing `VERDICT:` lines from the `pre-push-reviewer` subagent.
Applied to files:
docs/src/content/docs/claude-code.md
📚 Learning: 2026-05-13T15:14:46.366Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-05-13T15:14:46.366Z
Learning: AGENTS.md is the single source of truth for project conventions and AI agent instructions
Applied to files:
docs/src/content/docs/claude-code.mdREADME.md
📚 Learning: 2026-06-10T19:54:03.032Z
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.
Applied to files:
docs/src/content/docs/claude-code.mddocs/src/content/docs/why-wavehouse.mdREADME.mddocs/src/content/docs/architecture.mddocs/src/content/docs/deployment.mddocs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Create agent PRs as drafts only, and ensure the PR title follows Conventional Commits, is at most 72 characters, has a lowercase-first subject, and no trailing period.
Applied to files:
docs/src/content/docs/claude-code.mddocs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: For PR-branch pushes, run `/prepush` so every required pre-push reviewer reaches `ship_it`, using fresh context and recorded skips only when a reviewer truly has nothing to do.
Applied to files:
docs/src/content/docs/claude-code.md
📚 Learning: 2026-05-20T03:17:53.774Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-05-20T03:17:53.774Z
Learning: Refer to AGENTS.md for project conventions, architecture notes, and AI agent instructions - it is the single source of truth
Applied to files:
docs/src/content/docs/claude-code.mdREADME.md
📚 Learning: 2026-05-13T15:14:46.366Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-05-13T15:14:46.366Z
Learning: Refer to AGENTS.md for project conventions, architecture notes, and AI agent instructions
Applied to files:
docs/src/content/docs/claude-code.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Validate locally before every push by running `make ci` the documented way; do not use CI as the first feedback loop.
Applied to files:
docs/src/content/docs/claude-code.mddocs/src/content/docs/development.md
📚 Learning: 2026-05-20T03:17:53.774Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-05-20T03:17:53.774Z
Learning: Maintain .github/copilot-instructions.md as a pointer to AGENTS.md for GitHub Copilot integration
Applied to files:
docs/src/content/docs/claude-code.md
📚 Learning: 2026-08-11T15:22:47.380Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 448
File: docs/src/content/docs/sdk/index.mdx:330-334
Timestamp: 2026-08-11T15:22:47.380Z
Learning: In WaveHouse Go server authentication, `internal/auth/auth.go` `bearerToken` returns from the `Authorization` header path before modifying `r.URL`. It removes the `token` query parameter only when authentication uses the query parameter without an `Authorization` header. Documentation must state that this protects WaveHouse's own logs only; reverse proxies, CDNs, load balancers, and other upstream intermediaries require query-string redaction.
Applied to files:
docs/src/content/docs/why-wavehouse.mddocs/src/content/docs/architecture.mddocs/src/content/docs/api.md
📚 Learning: 2026-05-13T21:06:12.242Z
Learnt from: jfwoods
Repo: Wave-RF/WaveHouse PR: 135
File: deployments/signoz/clickhouse/users.xml:68-68
Timestamp: 2026-05-13T21:06:12.242Z
Learning: In the WaveHouse repository, `deployments/signoz/clickhouse/users.xml` intentionally configures the ClickHouse `default` user with an empty password and global network access (`::0`). This is acceptable because the `deployments/signoz/` stack is a local-dev-only SigNoz observability stack; ClickHouse ports are not published to the host in `deployments/signoz/compose.yaml`. Do not flag this as a security issue in future reviews.
Applied to files:
docs/src/content/docs/why-wavehouse.mdREADME.mddocs/src/content/docs/getting-started.mddocs/src/content/docs/deployment.mddocs/src/content/docs/development.md
📚 Learning: 2026-07-07T12:38:15.328Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 378
File: internal/auth/auth.go:119-132
Timestamp: 2026-07-07T12:38:15.328Z
Learning: Repo: Wave-RF/WaveHouse. WaveHouse deliberately does not log or trace any client IP address anywhere in the codebase. `middleware.RealIP` was removed in PR `#332` due to IP-spoofing GHSAs, and trusted-proxy-aware client-IP extraction for logs/traces is tracked as a future cross-cutting effort in issue `#333`. Do not suggest adding `r.RemoteAddr` or naive `X-Forwarded-For`-derived IPs to logs (e.g., audit logs in internal/auth/auth.go for the operator-key path) until `#333` lands with proper trusted-proxy handling.
Applied to files:
README.mddocs/src/content/docs/architecture.mddocs/src/content/docs/deployment.md
📚 Learning: 2026-06-10T15:02:09.425Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 312
File: .github/workflows/ci.yml:232-237
Timestamp: 2026-06-10T15:02:09.425Z
Learning: In the Wave-RF/WaveHouse repository, `clickhouse/clickhouse-server:latest` is used deliberately in `tests/integration/setup_test.go`, `scripts/orchestrator/main.go`, and the CI workflow prefetch steps (`docker pull -q clickhouse/clickhouse-server:latest`). The `:latest` tag in the prefetch steps intentionally mirrors the tag testcontainers resolves at runtime — this is a deliberate canary approach. Pinning to a concrete version/digest is a separate decision tracked as a follow-up issue and should not be flagged as a supply-chain concern in CI workflow reviews for this repo.
Applied to files:
README.mddocs/src/content/docs/getting-started.mddocs/src/content/docs/architecture.mddocs/src/content/docs/deployment.mddocs/src/content/docs/development.md
📚 Learning: 2026-08-12T05:35:22.804Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 455
File: clients/ts/README.md:11-11
Timestamp: 2026-08-12T05:35:22.804Z
Learning: For Wave-RF/WaveHouse, CI currently tests Node 22 only through `.nvmrc` and `.github/actions/setup-env` using `node-version-file`. Local `make ci` verification on Node 26 is not CI coverage and must not be documented as continuously tested runtime support.
Applied to files:
README.mddocs/src/content/docs/deployment.mddocs/src/content/docs/development.md
📚 Learning: 2026-08-11T21:55:44.607Z
Learnt from: jfwoods
Repo: Wave-RF/WaveHouse PR: 434
File: clients/go/go.mod:3-6
Timestamp: 2026-08-11T21:55:44.607Z
Learning: In the WaveHouse repository, `clients/go/go.mod` declares `go 1.24` as the deliberate minimum supported Go version for the published Go SDK. This SDK compatibility floor is independent of the server build toolchain declared by the root `go.mod` and referenced in `AGENTS.md`; do not require the Go SDK module to use the server toolchain version.
Applied to files:
README.mddocs/src/content/docs/deployment.mddocs/src/content/docs/development.md
📚 Learning: 2026-05-19T14:43:09.653Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 142
File: docs/BRANDING.md:0-0
Timestamp: 2026-05-19T14:43:09.653Z
Learning: In the Wave-RF/WaveHouse repository, the files `docs/BRANDING.md`, `docs/scripts/render-brand.mjs`, `docs/public/brand/` (PNG download pack), and `docs/src/assets/branding/concepts/round-{1..10}/` were intentionally excluded from PR `#142` per commit 26794bb. These design-history and branding-tutorial assets live in the off-repo archive at ~/Code/work/Wave-RF/WaveHouse-Logos/ instead. Do not raise review comments on these paths for this or future PRs unless they reappear in the diff.
Applied to files:
README.mddocs/src/content/docs/development.md
📚 Learning: 2026-08-12T05:38:52.277Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 455
File: tests/e2e/sdk/helpers.ts:214-221
Timestamp: 2026-08-12T05:38:52.277Z
Learning: In `tests/e2e/sdk/helpers.ts`, `chQuery` must reclassify a caught error as a request timeout or caller abort only when the error is an abort error. On Node 22, `AbortSignal.timeout()` produces an error named `TimeoutError`, `AbortController.abort()` and caller cancellation through `AbortSignal.any()` produce `AbortError`, and `JSON.parse()` failures produce `SyntaxError`.
Applied to files:
docs/src/content/docs/sdk/reference.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:
docs/src/content/docs/sdk/reference.md
📚 Learning: 2026-08-11T21:56:03.206Z
Learnt from: jfwoods
Repo: Wave-RF/WaveHouse PR: 434
File: clients/go/query_builder.go:278-291
Timestamp: 2026-08-11T21:56:03.206Z
Learning: In `clients/go/query_builder.go`, `fetchNextTyped` intentionally treats a failed JSON decode of a non-object typed `Row` as normal end-of-pagination. This behavior matches the existing “cursor column was not in the projection” path and TypeScript SDK parity. The broader behavior change is tracked in GitHub issue `#452`.
Applied to files:
docs/src/content/docs/sdk/queries.md
📚 Learning: 2026-08-11T21:55:36.083Z
Learnt from: jfwoods
Repo: Wave-RF/WaveHouse PR: 434
File: docs/src/content/docs/sdk/go/index.md:180-180
Timestamp: 2026-08-11T21:55:36.083Z
Learning: In the Go SDK, `Client.From(table)` returns `*TableRef`. Use `TableRef.Fetch(ctx)` to fetch all columns as untyped rows. `QueryBuilder.FetchUntyped(ctx)` is only available after creating a `*QueryBuilder`, for example with `TableRef.Select(...)` or `TableRef.SelectAll()`.
Applied to files:
docs/src/content/docs/sdk/queries.md
📚 Learning: 2026-06-26T15:07:28.749Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 0
File: :0-0
Timestamp: 2026-06-26T15:07:28.749Z
Learning: In the Go SSE implementation in `internal/api/stream.go`, keepalive frames from `internal/stream.Heartbeater` are only written from the post-replay select loop. The replay/gap-fill step is synchronous before entering that loop, so registering the `internal/stream.Subscriber` before replay does not materially improve idle-time coverage during replay; it can at most buffer one heartbeat in the subscriber's capacity-1 queue. Covering a genuinely long replay would require interleaving replay with the select loop and is tied to the broader delivery-path rework tracked by Issue `#294`.
Applied to files:
docs/src/content/docs/architecture.mddocs/src/content/docs/ingest-pipeline.md
📚 Learning: 2026-05-25T11:25:11.992Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 180
File: internal/observability/instruments.go:40-117
Timestamp: 2026-05-25T11:25:11.992Z
Learning: In the WaveHouse project (Go), package-level `var` declarations of OTel metric instruments (e.g., `metric.Float64Histogram`, `metric.Int64Counter`) created via `Meter().Float64Histogram(...)` / `Meter().Int64Counter(...)` are idiomatic and intentional — they follow the OTel Go SDK global proxy pattern and are NOT considered "global state" violations under the AGENTS.md constructor-injection rule. That rule targets swappable application-level interface dependencies (Cache, Publisher, Subscriber, Deduplicator), not OTel proxy instruments. Do not suggest wrapping these into an `Instruments` struct for injection.
Applied to files:
docs/src/content/docs/architecture.mddocs/src/content/docs/deployment.md
📚 Learning: 2026-08-11T21:55:42.427Z
Learnt from: jfwoods
Repo: Wave-RF/WaveHouse PR: 434
File: clients/go/cmd/wavehouse-codegen/main.go:96-97
Timestamp: 2026-08-11T21:55:42.427Z
Learning: In `clients/go/cmd/wavehouse-codegen/main.go`, `fetchSchemas` is a self-contained CLI helper with one caller, `main`. The `AGENTS.md` convention to pass dependencies explicitly applies to package constructors, not to this type of CLI helper. Do not request HTTP-client injection unless HTTP-level test coverage or additional callers make that refactor necessary.
Applied to files:
docs/src/content/docs/architecture.mddocs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to internal/observability/**/*.{go} : Observability code must preserve the repo’s logging and telemetry invariants, including stdout fan-out, OTLP behavior, lazy dialing, and the private Prometheus registry.
Applied to files:
docs/src/content/docs/architecture.mddocs/src/content/docs/deployment.mddocs/src/content/docs/development.md
📚 Learning: 2026-07-08T12:46:29.364Z
Learnt from: taitelee
Repo: Wave-RF/WaveHouse PR: 381
File: internal/stream/hub.go:142-178
Timestamp: 2026-07-08T12:46:29.364Z
Learning: In `internal/stream/hub.go`, the per-subscriber `policy.Evaluate(...)` call in `Broadcast` (on the row-filtered path, when `perms.HasRowFilter()` is true) is intentionally not memoized per distinct claim set. Rationale from maintainer taitelee: the claims-independent fast path (`!HasRowFilter()`) already ensures high-fanout public streams without a row-filter pay no per-subscriber cost; for topics that do carry a row-filter, visibility is inherently per-connection (different JWT claims → different rows) so the per-subscriber evaluation can't be hoisted without losing correctness, and memoization by claim set would rarely hit since subscribers in a row-filtered bucket typically have distinct tenant claims (plus `map[string]any` claims aren't cheaply hashable). This tradeoff is intentional; don't flag it as a perf issue unless profiling on a real filtered-high-fanout topic shows it matters.
Applied to files:
docs/src/content/docs/architecture.md
📚 Learning: 2026-05-13T14:35:40.574Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 116
File: internal/observability/provider.go:0-0
Timestamp: 2026-05-13T14:35:40.574Z
Learning: In `internal/observability/provider.go` (Go), `runtime.Start` from `go.opentelemetry.io/contrib/instrumentation/runtime` is wrapped in a package-level `runtimeStartOnce sync.Once`. The key design decision: `runtime.Start` errors must NOT route through `handleErr` (which rolls back OTel globals) — they should go through `slog.Warn` so the rest of the pipeline stays initialized with degraded host metrics. With non-fatal error handling in place, `sync.Once` is a clean goroutine-leak guard rather than a behavior-changing one. Production `main.go` calls `InitProvider` exactly once; the Once guard caps the leak in test re-init paths. Resolved in commit 6de31ee.
Applied to files:
docs/src/content/docs/architecture.md
📚 Learning: 2026-05-13T14:12:20.026Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 116
File: internal/observability/provider.go:155-158
Timestamp: 2026-05-13T14:12:20.026Z
Learning: In `internal/observability/provider.go` (Go), `runtime.Start` from `go.opentelemetry.io/contrib/instrumentation/runtime` spawns a goroutine with no shutdown/stop API. A `sync.Once` guard was deliberately NOT added around `runtime.Start` because it would mask the issue: a second `InitProvider` call would silently omit runtime metrics for the new MeterProvider, which is a worse failure mode than the goroutine leak. Production `main.go` calls `InitProvider` exactly once per process (leak surface bounded to tests). The integration test `TestOTel_UnreachableEndpoint_DoesNotBlockStartupOrEmits` documents and intentionally accepts this leak. Will revisit when upstream OTel adds a `Stop()` to the runtime package.
Applied to files:
docs/src/content/docs/architecture.mddocs/src/content/docs/ingest-pipeline.md
📚 Learning: 2026-05-25T11:25:14.412Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 180
File: internal/observability/instruments.go:22-38
Timestamp: 2026-05-25T11:25:14.412Z
Learning: In WaveHouse's `internal/observability/instruments.go`, the `mustFloat64Histogram` and `mustInt64Counter` helpers intentionally panic at package init time if OTel instrument registration fails. This follows the `regexp.MustCompile`/`template.Must` Go idiom for build-time-constant invariants. The "return errors, don't panic" coding guideline applies to runtime/request-response paths only, not to init-time instrument registration. Do not flag this pattern as a violation.
Applied to files:
docs/src/content/docs/architecture.md
📚 Learning: 2026-05-25T11:24:24.022Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 180
File: internal/cache/local.go:0-0
Timestamp: 2026-05-25T11:24:24.022Z
Learning: In the WaveHouse codebase (`internal/cache/local.go` and related packages), package-level `var` declarations of immutable `metric.MeasurementOption` / OTel attribute sets (e.g., `cacheL1Attrs = metric.WithAttributes(attribute.String("tier", "L1"))`) are intentional and acceptable. These are pre-allocated constants analogous to `regexp.MustCompile(...)`, not mutable global state. The AGENTS.md "no global state / constructor injection" rule applies to application dependencies (Cache, Publisher, Deduplicator), not to stateless OTel metric attribute options. Do not flag these as violations of the constructor-injection guideline.
Applied to files:
docs/src/content/docs/architecture.md
📚 Learning: 2026-07-24T18:23:07.472Z
Learnt from: taitelee
Repo: Wave-RF/WaveHouse PR: 418
File: internal/observability/metrics_test.go:108-240
Timestamp: 2026-07-24T18:23:07.472Z
Learning: In `internal/observability/metrics_test.go`, tests in package `observability` cannot import shared `internal/testutil/` mocks because `internal/testutil/` imports `mq`, which imports `observability` and would create an import cycle. Keep minimal local test stubs (such as `stubDeduplicator`, `stubCHConn`, and `stubPartsRows`) in this package unless the dependency structure changes.
Applied to files:
docs/src/content/docs/architecture.md
📚 Learning: 2026-06-29T14:21:45.067Z
Learnt from: taitelee
Repo: Wave-RF/WaveHouse PR: 343
File: internal/api/pipe_deps.go:0-0
Timestamp: 2026-06-29T14:21:45.067Z
Learning: In `internal/api/pipes.go`, direct table-function reads and direct cross-database table reads are intentionally omitted from the pipe dependency set and continue using the normal query-derived TTL; only resolved-but-unmaintainable dependencies (such as unknown or unfoldable view-derived names) trigger the unresolved-dependency TTL cap.
Applied to files:
docs/src/content/docs/architecture.mddocs/src/content/docs/api.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to internal/query/**/*.{go} : Structured-query code must enforce schema validation, permission checks, timestamp bucketing, and fail-closed column authorization inside `query.Build`.
Applied to files:
docs/src/content/docs/architecture.mddocs/src/content/docs/api.md
📚 Learning: 2026-08-11T12:41:08.161Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 446
File: .github/actions/setup-env/action.yml:112-127
Timestamp: 2026-08-11T12:41:08.161Z
Learning: In WaveHouse CI, the shared `gomod-v1` cache in `.github/actions/setup-env/action.yml` can be saved by multiple Go jobs on an exact-key miss. Every workflow path that can write this cache must fully populate `~/go/pkg/mod` through the Makefile `go-mod-download` prerequisite before the post-job cache save. The `cov` target must retain this prerequisite.
Applied to files:
docs/src/content/docs/deployment.mddocs/src/content/docs/development.md
📚 Learning: 2026-05-13T20:41:09.256Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 125
File: internal/api/health_test.go:100-163
Timestamp: 2026-05-13T20:41:09.256Z
Learning: In `internal/api/health_test.go` (WaveHouse), every handler test explicitly asserts `Content-Type: application/json` and `X-Content-Type-Options: nosniff` headers, including on 503 responses. This is deliberate regression coverage: the comment in `TestHealth_Readiness_PingFails` explains that without the 503-path header test, a future refactor moving header setup into the success branch would silently drop headers on error responses. New boot-degraded tests should follow the same pattern.
Applied to files:
docs/src/content/docs/deployment.mddocs/src/content/docs/development.mddocs/src/content/docs/api.md
📚 Learning: 2026-05-14T19:05:28.606Z
Learnt from: jfwoods
Repo: Wave-RF/WaveHouse PR: 137
File: deployments/signoz/dashboards/wavehouse-runtime-internals.json:91-93
Timestamp: 2026-05-14T19:05:28.606Z
Learning: In `deployments/signoz/dashboards/wavehouse-overview.json`, the three widgets covering "Spans / metrics / logs received by the collector" (OTLP intake panels) intentionally have empty `filter.expression` fields — they are collector-wide by design and should NOT be scoped to `service.name = 'wavehouse'`. Only the service-level metric widgets in that dashboard and all widgets in `wavehouse-runtime-internals.json` should be scoped to `service.name = 'wavehouse'`.
Applied to files:
docs/src/content/docs/deployment.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to docs/src/content/docs/development.md : Update the development docs when changing build or test process details.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to Makefile : Keep the Makefile in sync with documented build, test, and CI targets, because it is the source of truth for those commands.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to internal/auth/**/*.{go} : JWT auth middleware must always run, verify with either HMAC or JWKS (not both), pin accepted `alg` to the active verifier, and keep authN/authZ decoupled except for the sanctioned operator key.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-06-26T12:23:26.034Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 346
File: internal/stream/subscriber_test.go:9-28
Timestamp: 2026-06-26T12:23:26.034Z
Learning: In this Go repository, the `**/*_test.go` table-driven test guideline is intended for genuinely multi-scenario tests. Single sequential behavioral-flow tests, such as `internal/stream/subscriber_test.go`'s `TestSubscriber_SendDeliversThenDropsWhenFull`, do not need to be rewritten into `[]struct{...}` + `t.Run(...)` when that would be artificial and less clear.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-05-23T01:24:02.141Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 174
File: internal/api/ingest_test.go:111-111
Timestamp: 2026-05-23T01:24:02.141Z
Learning: In WaveHouse tests under internal/api/**/*_test.go, use `testutil.AssertJSONErrorResponse(t, w)` (from `internal/testutil`) for HTTP error-path assertions — NOT a package-local `assertJSONErrorResponse` helper. The package-local helper was removed in PR `#174` and its functionality was promoted to `internal/testutil.AssertJSONErrorResponse`. This helper asserts `Content-Type: application/json`, `X-Content-Type-Options: nosniff` headers, and the presence of an `"error"` field in the JSON body.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to **/*_test.go : Use the repo’s JWT, schema, policy, pipes, and JSON response test helpers (`testutil.MakeJWT`, `testutil.MakeExpiredJWT`, `NewTestSchemaRegistry`, `policy.NewMemoryStore`, `pipes.NewMemoryStore`, `AssertJSONResponse`, `AssertJSONContains`) where applicable.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-05-20T01:02:03.228Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 164
File: internal/api/router_test.go:289-350
Timestamp: 2026-05-20T01:02:03.228Z
Learning: In the WaveHouse project (`internal/api/**/*_test.go`), the convention for testing `RequireRole` middleware is to inject `ContextKeyRole` directly into the request context rather than using `testutil.MakeJWT`. JWT token parsing is covered separately in `middleware_test.go` (17 dedicated tests). Do not suggest switching role-gate tests to JWT-driven tests — the separation of concerns is intentional to keep failure surfaces isolated.
Applied to files:
docs/src/content/docs/development.mddocs/src/content/docs/api.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to **/*_test.go : Write tests in table-driven form with `t.Run(tt.name, ...)` for multiple cases.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to **/*_test.go : Every new function should have corresponding test cases, and new code should aim for 80%+ coverage.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-05-13T20:40:56.906Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 125
File: internal/discovery/discovery_test.go:404-513
Timestamp: 2026-05-13T20:40:56.906Z
Learning: In `internal/discovery/discovery_test.go`, the five `TestRetryRefresh_*` tests (SucceedsOnFirstAttempt, RetriesUntilSuccess, ReturnsOnContextCancel, BackoffIsBounded, NilOnAttemptIsSafe) are intentionally written as individual named tests rather than a table-driven suite. Their setup pipelines and assertion shapes are fundamentally heterogeneous: ReturnsOnContextCancel requires goroutine + channel + select-with-timeout orchestration, BackoffIsBounded uses wall-clock elapsed bounds, and NilOnAttemptIsSafe is a nil-callback panic-safety check. Forcing them into a table would produce mostly-null rows with nested `if` branches, which is worse readability. The table-driven pattern is correctly applied to `TestClampBackoff` in the same file (pure function, uniform I/O shape). Do not suggest converting these RetryRefresh tests to a table-driven suite.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-05-19T14:41:38.228Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 142
File: docs/scripts/screenshot.mjs:31-31
Timestamp: 2026-05-19T14:41:38.228Z
Learning: `docs/scripts/screenshot.mjs` in the Wave-RF/WaveHouse repo is an intentionally manual dev-iteration tool (not wired into CI). Response-status validation and retry logic are deliberately deferred until the script is promoted to a CI visual-regression workflow. Do not flag the absence of `response.ok()` checks as an issue in this file.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to **/*.{go} : Go code must use Go 1.26 conventions: gofumpt formatting, structured logging with `log/slog`, Chi v5 routing, explicit error returns, no global state, and package naming that is lowercase and single-word or abbreviated.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-08-11T12:41:05.990Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 446
File: .github/workflows/README.md:0-0
Timestamp: 2026-08-11T12:41:05.990Z
Learning: In the GitHub Actions cache design, `.github/actions/setup-env/action.yml` owns the shared `gomod-v1` module cache and per-suffix `gobuild-v3` build-object caches for `ci.yml` Go jobs. `.github/workflows/publish-dev.yml` saves only the `gobuild-v3-...-release-...` build cache and uses `actions/cache/restore` to read `gomod-v1` without saving it. `.github/workflows/release.yml` does not save these caches.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Address and resolve every review finding; reply substantively, fix it or track it in an issue, mention the bot when needed, and never silently drop a comment.
Applied to files:
docs/src/content/docs/development.md
📚 Learning: 2026-07-07T16:19:29.374Z
Learnt from: CR
Repo: Wave-RF/WaveHouse PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-07T16:19:29.374Z
Learning: Applies to internal/policy/**/*.{go} : Policy code must preserve fail-closed access control: `IsAdmin` is the single admin check, empty roles match nothing, `Validate` rejects empty role keys, and policy deletion denies everyone except the operator-key break-glass path.
Applied to files:
docs/src/content/docs/api.md
📚 Learning: 2026-05-20T20:30:22.556Z
Learnt from: taitelee
Repo: Wave-RF/WaveHouse PR: 172
File: internal/api/pipes_test.go:106-118
Timestamp: 2026-05-20T20:30:22.556Z
Learning: In WaveHouse's pipes authorization fix (PR `#172`), fixing the AllowedRoles fail-open bug requires two changes: (1) remove the outer `if role != ""` guard in PipesHandler.Execute so empty roles are evaluated against the allowlist, AND (2) add an `ar != ""` guard inside the allowlist scan (i.e., `ar != "" && ar == role`) so a malformed allowlist containing empty strings (e.g., `[""]`) cannot match an empty role via `"" == ""`. Doing only (1) is insufficient and would make `[""]` fail-open.
Applied to files:
docs/src/content/docs/api.md
🪛 LanguageTool
docs/src/content/docs/durability.md
[style] ~32-~32: Consider using the typographical ellipsis character here instead.
Context: ...equest timeouts, manifesting at boot as create stream: ... context deadline exceeded. - If the wo...
(ELLIPSIS)
docs/src/content/docs/claude-code.md
[grammar] ~40-~40: Ensure spelling is correct
Context: ...ushes require the make verify marker (tmp/verify-passed-tree-<TREE-sha>). CI also skips Go/SDK suites for docs. *...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[grammar] ~43-~43: Ensure spelling is correct
Context: ...see AGENTS.md §"Agent PR Discipline"). Tree-keyed markers allow commit-then-push wi...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[uncategorized] ~82-~82: The official name of this software platform is spelled with a capital “H”.
Context: ...ers' PRs after wt switch pr:<N>. Runs .github/prompts/pr-review.md in fresh context,...
(GITHUB)
[uncategorized] ~83-~83: The official name of this software platform is spelled with a capital “H”.
Context: ...y, completeness, and code↔docs sync via .github/prompts/docs-review.md over the `scrip...
(GITHUB)
[grammar] ~83-~83: Ensure spelling is correct
Context: ... ship_it|iterate|block$\rightarrow$tmp/docs-reviewer-passed-; path/all` is advisory. Never comments on PRs or edits docs. Co...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[grammar] ~83-~83: Ensure spelling is correct
Context: ...y. Never comments on PRs or edits docs. Complements misspell, markdownlint, and starlight-l...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[typographical] ~139-~139: In American English, use a period after an abbreviation.
Context: ...e hooks only. ## GitHub access: gh CLI vs MCP **Use gh CLI as the canonical Gi...
(MISSING_PERIOD_AFTER_ABBREVIATION)
docs/src/content/docs/sdk/reference.md
[style] ~38-~38: A comma is missing here.
Context: ...RROR| Yes | Stream failed to connect (e.g. non-absolutebaseURL) | | 0 | SSE_ER...
(EG_NO_COMMA)
docs/src/content/docs/getting-started.md
[style] ~31-~31: Since ownership is already implied, this phrasing may be redundant.
Context: ...production, tune it: your own roles, real tables, scoped columns, and...
(PRP_OWN)
[style] ~35-~35: Since ownership is already implied, this phrasing may be redundant.
Context: ...lickHouse table WaveHouse uses Bring Your Own Schema; it discovers tables via `syst...
(PRP_OWN)
docs/src/content/docs/sdk/queries.md
[style] ~91-~91: Consider using the typographical ellipsis character here instead.
Context: ...cit .fetch(). Mutually exclusive with .select(...) and aggregations (.count(), `.sum()...
(ELLIPSIS)
[style] ~129-~129: Consider using the typographical ellipsis character here instead.
Context: ...d server-side). Mutually exclusive with .select(...) and aggregations (.count(), `.sum()...
(ELLIPSIS)
[style] ~250-~250: 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: ...?) Execute raw SQL./v1/admin/query` needs a JWT resolving to the policy admin rol...
(EN_REPEATEDWORDS_NEED)
docs/src/content/docs/deployment.md
[style] ~217-~217: Consider shortening this phrase to just ‘whether’, unless you mean ‘regardless of whether’.
Context: ...roless nonroot user; the number works whether or not your host has a matching name in `/etc/...
(WHETHER)
[style] ~224-~224: 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: ...If you see "permission denied," use the suggested chown command from the logs. **Pipes...
(EN_REPEATEDWORDS_SUGGEST)
[grammar] ~329-~329: Ensure spelling is correct
Context: ...shold` to your worst-case CH boot time; 5min (30
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[style] ~337-~337: Since ownership is already implied, this phrasing may be redundant.
Context: ...ickHouse Schema WaveHouse uses Bring Your Own Schema. Create tables in ClickHouse w...
(PRP_OWN)
docs/src/content/docs/development.md
[style] ~316-~316: Consider using the typographical ellipsis character here instead.
Context: ...testdox format. Extra flags: Pass ARGS="..." for additional go test flags (e.g.,...
(ELLIPSIS)
[style] ~317-~317: Consider using the typographical ellipsis character here instead.
Context: ...un, -count). **Timing**: gotestsum's DONE ... in X.XXXs` reports pure test execution ...
(ELLIPSIS)
[style] ~330-~330: A comma is missing here.
Context: ...Utilities**: internal/testutil/ (Go), e.g. testutil.NopLogger() to silence embed...
(EG_NO_COMMA)
[style] ~356-~356: Since ownership is already implied, this phrasing may be redundant.
Context: ...e2e/data/. The orchestrator provisions its own stack, so it won't collide with make d...
(PRP_OWN)
[grammar] ~406-~406: Please add a punctuation mark at the end of paragraph.
Context: ...tparallel*: Missing t.Parallel() in subtests Formatting (gofumpt and **goimport...
(PUNCTUATION_PARAGRAPH_END)
[style] ~512-~512: Consider using the typographical ellipsis character here instead.
Context: ...+ Docker volumes | Test targets accept ARGS="..." for go test flags. Build targets ac...
(ELLIPSIS)
[style] ~512-~512: Consider using the typographical ellipsis character here instead.
Context: ...r go test flags. Build targets accept TAGS="..." for Go build tags. V=1 enables verb...
(ELLIPSIS)
[uncategorized] ~535-~535: The official name of this software platform is spelled with a capital “H”.
Context: ...request. ### Dependabot Configured in .github/dependabot.yml, Dependabot opens weekl...
(GITHUB)
[uncategorized] ~547-~547: The official name of this software platform is spelled with a capital “H”.
Context: ... in clients/ts/) publishes to npm via .github/workflows/publish-npm.yml using OIDC t...
(GITHUB)
[uncategorized] ~552-~552: Do not mix variants of the same word (‘pre-release’ and ‘prerelease’) within a single text.
Context: ... on the suffix and are marked as GitHub pre-releases. The tag must match `clients/ts/pac...
(EN_WORD_COHERENCY)
[uncategorized] ~573-~573: The official name of this software platform is spelled with a capital “H”.
Context: ...e commit subject. The PR title job in .github/workflows/ci.yml enforces this; valida...
(GITHUB)
[uncategorized] ~585-~585: The official name of this software platform is spelled with a capital “H”.
Context: ...leset requires the CI aggregator job (.github/workflows/ci.yml) to pass before mergi...
(GITHUB)
[uncategorized] ~596-~596: The official name of this software platform is spelled with a capital “H”.
Context: ...all-clock data on the Summary page. See [.github/workflows/README.md](https://github.co...
(GITHUB)
[style] ~606-~606: Since ownership is already implied, this phrasing may be redundant.
Context: ...erge only. Admins may bypass these for their own PRs but cannot push directly to main....
(PRP_OWN)
[uncategorized] ~646-~646: The official name of this software platform is spelled with a capital “H”.
Context: ...ng PRs The PR housekeeping workflow (.github/workflows/housekeeping.yml) runs `acti...
(GITHUB)
[uncategorized] ~646-~646: The official name of this software platform is spelled with a capital “H”.
Context: ...eping.yml) runs actions/labelerwith.github/labeler.ymlto applyarea/*, depend...
(GITHUB)
[uncategorized] ~646-~646: The official name of this software platform is spelled with a capital “H”.
Context: ...ymlto applyarea/*, dependencies, github_actions, go, and documentation` lab...
(GITHUB)
[uncategorized] ~653-~653: The official name of this software platform is spelled with a capital “H”.
Context: ... 2. Add the path .github/labeler.yml.
(GITHUB)
docs/src/content/docs/ingest-pipeline.md
[grammar] ~107-~107: Please add a punctuation mark at the end of paragraph.
Context: ... ## Why per table? The bug this design fixes Shared batches couple tables: high-vol...
(PUNCTUATION_PARAGRAPH_END)
[style] ~134-~134: Since ownership is already implied, this phrasing may be redundant.
Context: ...maxAckPending. - Leftovers wait for their own size/timer. If 500 rows flush and 100...
(PRP_OWN)
docs/src/content/docs/api.md
[typographical] ~185-~185: The word ‘WHERE’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ... be proven to match only authorized rows. ::: Request: ```json { "url": "...
(WRB_QUESTION_MARK)
[style] ~235-~235: Consider using the typographical ellipsis character here instead.
Context: ...alid json"}| Malformed body | | 400 |{"error":"..."}` | Schema failure (unknown column, mi...
(ELLIPSIS)
[style] ~239-~239: Consider using the typographical ellipsis character here instead.
Context: ...insert permission on table. | | 404 | {"error":"unknown table: ..."} | Table not found in schema. | | 413...
(ELLIPSIS)
[grammar] ~270-~270: Ensure spelling is correct
Context: ...parses bare numbers only as integers. - Instants outside the column type's range pass th...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[style] ~296-~296: Consider using the typographical ellipsis character here instead.
Context: ...ecord never blocks the batch. The SDK's insert([...]) helper uses NDJSON automatically. -...
(ELLIPSIS)
[style] ~350-~350: Consider using the typographical ellipsis character here instead.
Context: ... body"}| No records in body | | 400 |{"error":"invalid json: ..."}` | Structural JSON error, truncated a...
(ELLIPSIS)
[style] ~436-~436: Consider using the typographical ellipsis character here instead.
Context: ...rbatim ClickHouse error text. | | 502 | {"error":"clickhouse request failed: ..."} | Transport-level failure reaching C...
(ELLIPSIS)
[style] ~437-~437: Consider using the typographical ellipsis character here instead.
Context: ...l failure reaching ClickHouse | | 502 | {"error":"clickhouse response exceeded N bytes; ..."} | Response body exceeded the 64 MiB ...
(ELLIPSIS)
[style] ~504-~504: Consider using the typographical ellipsis character here instead.
Context: ...use | | ------ | ---- | ----- | | 400 | {"error":"..."} | Schema validation error (unknown c...
(ELLIPSIS)
[style] ~513-~513: Redundant conjunctions can lead to confusion; consider removing a conjunction here.
Context: ...with parameter binding via query string and/or JSON body. Results use shared L1 (Ristr...
(AND_OR)
🔇 Additional comments (16)
docs/src/content/docs/development.md (1)
12-31: LGTM!Also applies to: 43-54, 81-96, 121-121, 132-193, 203-203, 223-223, 264-276, 281-312, 319-385, 386-389, 392-408, 454-485, 491-512, 514-552, 564-653
docs/src/content/docs/claude-code.md (1)
8-140: LGTM!Also applies to: 146-241
docs/src/content/docs/api.md (1)
12-19: LGTM!Also applies to: 27-49, 55-80, 87-89, 108-151, 164-177, 179-185, 199-224, 234-243, 255-299, 321-321, 343-358, 377-390, 410-410, 432-437, 449-454, 481-513, 528-561, 571-580, 593-598, 616-618, 636-648, 664-680, 694-706, 740-744, 768-776, 793-798, 814-824
docs/src/content/docs/architecture.md (1)
12-12: LGTM!Also applies to: 46-46, 70-82, 94-98, 102-118, 123-157, 239-239
docs/src/content/docs/deployment.md (1)
12-12: LGTM!Also applies to: 41-41, 53-53, 69-69, 79-87, 100-100, 109-111, 168-200, 209-237, 249-296, 311-315, 329-429, 433-438
docs/src/content/docs/durability.md (1)
8-15: LGTM!Also applies to: 24-57, 66-70, 72-78, 80-87, 89-93
docs/src/content/docs/ingest-pipeline.md (1)
18-26: LGTM!Also applies to: 56-59, 87-87, 101-115, 131-150, 160-170, 191-210, 216-219, 230-234, 251-265
README.md (1)
55-63: LGTM!Also applies to: 77-77, 90-90, 99-99, 111-121, 131-145
docs/src/content/docs/404.md (1)
41-41: LGTM!Also applies to: 48-48
docs/src/content/docs/getting-started.md (1)
8-35: LGTM!Also applies to: 50-54, 63-67, 78-80, 84-84, 96-113
docs/src/content/docs/why-wavehouse.md (1)
8-16: LGTM!Also applies to: 39-60, 106-110, 142-153, 155-156, 159-175, 198-198, 231-231, 247-249, 252-260
docs/src/content/docs/sdk/admin.md (1)
6-6: LGTM!Also applies to: 21-23, 71-71, 89-89
docs/src/content/docs/sdk/pipes.md (1)
24-24: LGTM!docs/src/content/docs/sdk/queries.md (1)
17-27: LGTM!Also applies to: 36-36, 50-56, 72-72, 105-107, 117-128, 135-137, 153-153, 168-168, 190-194, 205-213, 227-235, 248-257
docs/src/content/docs/sdk/reference.md (1)
6-10: LGTM!Also applies to: 26-26, 36-41, 95-95, 104-104, 146-156
docs/src/content/docs/sdk/streaming.md (1)
6-14: LGTM!Also applies to: 56-56, 83-97, 108-114, 151-154
| For SSE connections, tokens can be passed as a query parameter: | ||
|
|
||
| ```text | ||
| GET /v1/stream?token=<jwt> | ||
| ``` | ||
|
|
||
| The `Authorization` header takes precedence when both are provided: the `?token=` query parameter is only a fallback for clients that can't set headers (browser `EventSource`), so a token in the more log-leakable URL never overrides an explicit header credential. A `?token=` is stripped from the URL after extraction whichever credential wins, so it stays out of WaveHouse's own logs — but it has already crossed the wire in the request URI, so redact query strings at any proxy, CDN, or load balancer in front. | ||
| The `Authorization` header takes precedence; `?token=` is a fallback for clients like browser `EventSource`. To prevent log leaks, WaveHouse strips `?token=` from the URL after extraction, though you should still redact query strings at your proxy or CDN. |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Limit the token-stripping claim to WaveHouse logs.
?token is removed only after the request reaches WaveHouse. A proxy, CDN, or load balancer can log the original URL before that. Replace “To prevent log leaks” with wording that limits the protection to WaveHouse’s own logs, and retain the upstream query-redaction requirement.
Based on learnings: query-token stripping protects WaveHouse’s own logs only; reverse proxies, CDNs, load balancers, and other upstream intermediaries still require query-string redaction.
Source: Learnings
| | NDJSON (one object per line) | `application/x-ndjson` | per-record summary — see [Batch Ingest](#batch-ingest) | | ||
|
|
||
| The inbound request body is capped at 16 MiB; a body over the cap is rejected with `413` (matching [`POST /v1/admin/query`](#post-v1adminquery--query-clickhouse)). For uploads larger than that, use the streaming NDJSON form below rather than one big body, and set your own outer limit at the [reverse proxy](/reverse-proxy#request-body-size-limits). | ||
| Request bodies are capped at 16 MiB; overflows return `413` (see [`POST /v1/admin/query`](#post-v1adminquery--query-clickhouse)). For larger uploads, use streaming NDJSON and configure the [reverse proxy](/reverse-proxy#request-body-size-limits). |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Point the 16 MiB request-limit reference to the request-body documentation.
The link to POST /v1/admin/query does not document the ingest request cap in the supplied section. Its documented size limit is a 64 MiB buffered response cap. Link to the ingest error table or the request-body-limit section instead.
| :::note[Admin only] | ||
| The route is mounted under `/v1/admin/*`, behind the `RequireAdmin` gate: only a caller whose JWT role equals the policy `admin_role` (`"admin"` by default) may use it. A request with no/invalid token resolves to the `default_role` (not the admin role unless `default_role` is deliberately set to it — a loudly-warned dev-only setting) and is rejected. Raw SQL has no per-statement scope check (a full SQL parser would be needed to authorize predicates), so the role gate is the entire authorization story, shared with the rest of `/v1/admin/*` (policy CRUD, pipes CRUD). The normal surfaces for non-admin callers are `POST /v1/ingest?table={table}` for writes, `POST /v1/query?table={table}` for structured reads, and `GET/POST /v1/pipes/{name}` for pre-defined queries — none of which expose raw SQL. | ||
| Mounted under `/v1/admin/*` behind the `RequireAdmin` gate: only callers with a JWT role matching the policy `admin_role` (`"admin"` by default) may use it. Requests with no/invalid tokens resolve to `default_role` and are rejected. Raw SQL has no per-statement scope check; the role gate is the sole authorization mechanism, shared with `/v1/admin/*` (policy/pipes CRUD). Non-admins should use `POST /v1/ingest?table={table}` for writes, `POST /v1/query?table={table}` for structured reads, or `GET/POST /v1/pipes/{name}` for pre-defined queries. | ||
| ::: | ||
|
|
||
| `/v1/admin/query` is the only sanctioned surface for non-insert mutations (the ingest pipeline is insert-only). Granting raw-SQL access to a non-admin role via the policy engine is no longer supported: authenticate with the admin role (`admin_role`). | ||
| `/v1/admin/query` is the only sanctioned surface for non-insert mutations. Granting raw-SQL access to non-admin roles via the policy engine is unsupported; authenticate with the `admin_role`. |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Correct the raw-SQL authentication exception.
This paragraph says requests with no or invalid tokens are rejected after resolving to default_role. Earlier documentation states that default_role: admin_role grants unauthenticated admin access in local or development configurations. Qualify this paragraph or explicitly exclude that configuration.
| The SSE fan-out is factored out of `api/` so the delivery hot path ([#294](https://github.com/Wave-RF/WaveHouse/issues/294)) resides with its shared keepalive primitives. One abstraction per file. | ||
|
|
||
| - **hub.go** — `Hub`, the event fan-out. Subscribers register under `(topic, role)`; `Broadcast` decodes each event once, applies each subscribed role's column policy once, builds one SSE frame per role, and fans it to every member of that role's `Bucket` — collapsing the prior per-subscriber `unmarshal → evaluate → filter → marshal` into one pass per distinct `(role, table)` output shape (the [#294](https://github.com/Wave-RF/WaveHouse/issues/294) lever; the measured ceiling was ~2 270 deliveries/s from re-projecting per subscriber). The `(topic, role)` key is sufficient because column visibility derives only from the role+table policy entry, never from JWT claims (claims feed only the row-level `WHERE`/`CHECK`, which the stream path does not apply). `ReplayFrame` shares the same projection for the handler's per-connection gap-fill. | ||
| - **subscriber.go** — `Subscriber`, the per-connection handle. It owns a single ready-to-write outbound queue of `Frame`s (each tagged with its `kind`, so the handler labels the write where it happens): producers — the keepalive wheel and the event `Hub` — fan frames in with `Send` (non-blocking; a full queue drops and the `Hub` counts it), and the handler drains `Frames()` to the client verbatim. The queue is sized for buffering live events (cap 64, up from the keepalive-only cap 1; #152 will make it a knob), and an `Evicted()` channel is the seam the slow-consumer follow-up closes to disconnect a wedged consumer. | ||
| - **bucket.go** — `Bucket`, the reusable fan-out primitive: a concurrency-safe set of subscribers. `Push` delivers a shared `Frame` to each fire-and-forget (the keepalive wheel's ring); `Snapshot` exposes the members so the event `Hub` can fan out while inspecting each `Send` result (to count drops). The `Hub` holds one `Bucket` per `(topic, role)` so a projected frame is built once and sent to every member instead of re-projected per subscriber. | ||
| - **heartbeat.go** — The keepalive wheel (`Heartbeater`). A single process-wide ticker fans a minimal `:` comment across the ring of `Bucket`s, waking ~1/N of live streams per tick so the writes don't synchronize. The effective per-connection keepalive period is `stream.keepalive_interval` (the wheel ticks every `keepalive_interval ÷ keepalive_buckets`, so one rotation spans the interval); the owning handler goroutine does the actual write, so the shared ticker never touches a `ResponseWriter` directly. | ||
| - **metrics.go** — `Metrics`, the SSE instrument set: `wavehouse_sse_active_streams` (open streams), `wavehouse_sse_stream_duration_seconds` (lifetime), `wavehouse_sse_frames_sent_total` / `wavehouse_sse_bytes_sent_total` (labeled by `kind`: `keepalive`, `event`, `replay`), and `wavehouse_sse_dropped_frames_total` (frames dropped to a full subscriber queue — the slow-consumer signal that was silent before #294). Nil-safe, so the handler holds one unconditionally and tests skip wiring it; one shared instance records both the handler's write sites and the `Hub`'s drop counts. Separate from `observability.RegisterSystemMetrics`, which covers only the NATS/Pebble system gauges. Streams are observed through these metrics rather than per-event traces (the router excludes `/v1/stream` from the HTTP tracer). | ||
| - **hub.go** — `Hub`, the event fan-out. Subscribers register under `(topic, role)`. `Broadcast` decodes each event once, applies the role's column policy once, builds one SSE frame per role, and fans it to every member of that role's `Bucket`. This collapses the prior per-subscriber `unmarshal → evaluate → filter → marshal` into one pass per distinct `(role, table)` output shape (the [#294](https://github.com/Wave-RF/WaveHouse/issues/294) lever; previous ceiling was ~2 270 deliveries/s). The `(topic, role)` key suffices because column visibility derives from the role+table policy, not JWT claims (claims only feed row-level `WHERE`/`CHECK`, which the stream path ignores). `ReplayFrame` uses this projection for per-connection gap-fill. | ||
| - **subscriber.go** — `Subscriber`, the per-connection handle. It owns an outbound queue of `Frame`s tagged by `kind`. Producers (keepalive wheel and event `Hub`) use `Send` (non-blocking; full queues drop frames and the `Hub` counts them), while the handler drains `Frames()` to the client. The queue is sized for live events (cap 64, up from keepalive cap 1; #152 will make this a knob). An `Evicted()` channel allows slow-consumer follow-up to disconnect wedged clients. | ||
| - **bucket.go** — `Bucket`, a concurrency-safe set of subscribers. `Push` delivers a shared `Frame` to each (used by the keepalive wheel); `Snapshot` exposes members so the `Hub` can fan out and count drops via `Send` results. The `Hub` holds one `Bucket` per `(topic, role)` to avoid re-projecting frames per subscriber. | ||
| - **heartbeat.go** — The keepalive wheel (`Heartbeater`). A process-wide ticker fans a `:` comment across the `Bucket` ring, waking ~1/N of streams per tick to prevent synchronized writes. The effective period is `stream.keepalive_interval` (wheel ticks every `keepalive_interval ÷ keepalive_buckets`). The handler goroutine performs the write; the ticker never touches a `ResponseWriter`. | ||
| - **metrics.go** — `Metrics`, the SSE instrument set: `wavehouse_sse_active_streams`, `wavehouse_sse_stream_duration_seconds`, `wavehouse_sse_frames_sent_total` / `wavehouse_sse_bytes_sent_total` (labeled by `kind`: `keepalive`, `event`, `replay`), and `wavehouse_sse_dropped_frames_total` (slow-consumer signal added in #294). Nil-safe, one shared instance records handler writes and `Hub` drops. Separate from `observability.RegisterSystemMetrics` (NATS/Pebble gauges). Streams use these metrics instead of per-event traces; the router excludes `/v1/stream` from the HTTP tracer. |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Do not document row-filtered streams as role-only projection.
The once-per-role frame claim applies only when the policy has no row filter. For row-filtered streams, the hub evaluates policy.Evaluate(...) per subscriber, so JWT claims affect stream visibility. Remove “which the stream path ignores” and qualify the shared-frame statement.
Based on learnings: internal/stream/hub.go evaluates policy.Evaluate(...) per subscriber when perms.HasRowFilter() is true; only the claims-independent fast path avoids that work.
Source: Learnings
| **Use `gh` CLI as the canonical GitHub access path**, not a GitHub MCP server, because: | ||
|
|
||
| - `gh` is already a hard dev requirement | ||
| - `gh` is a hard dev requirement | ||
| - Works identically in Claude Code, terminal, and shell scripts | ||
| - No extra auth / approval / npx cold-start | ||
| - No extra auth, approval, or npx cold-start |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Align the gh requirement with the development prerequisites.
This page calls gh a hard development requirement, but development.md Lines 12-22 do not list it. Add gh and its authentication step to the prerequisite table, or scope this wording to GitHub and Claude Code workflows.
| A popular dashboard recomputes the same expensive aggregation for every viewer. ClickHouse has a built-in query cache, but it is **per-server** and there is no client-facing singleflight: 50 dashboards hitting refresh at once means up to 50 identical queries land on ClickHouse. | ||
|
|
||
| WaveHouse coalesces identical queries with an in-process Ristretto cache and Go's `singleflight`, so only the first of N concurrent identical queries actually hits ClickHouse — the rest receive the same result without making the round trip. | ||
| ClickHouse's query cache is per-server with no client-facing singleflight: 50 simultaneous refreshes are 50 identical queries. WaveHouse coalesces them with an in-process Ristretto cache and Go `singleflight` — one query reaches ClickHouse. |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win
Qualify in-process cache coalescing as per-instance behavior. Ristretto and singleflight do not provide one cluster-wide ClickHouse query when multiple WaveHouse instances serve the requests.
docs/src/content/docs/why-wavehouse.md#L83-L83: Change “one query reaches ClickHouse” to “one query per WaveHouse instance reaches ClickHouse.”docs/src/content/docs/getting-started.md#L76-L76: Change “hit ClickHouse once” to “hit ClickHouse once per instance.”
📍 Affects 2 files
docs/src/content/docs/why-wavehouse.md#L83-L83(this comment)docs/src/content/docs/getting-started.md#L76-L76
| The DIY path works for big teams; the ops cost is Kafka bills and 3 a.m. batch-consumer stalls. | ||
|
|
||
| **The scenario.** A seed-stage team building user-facing analytics picks "Kafka + ClickHouse + custom ingest". Six months in: two engineers are spending ~30% of their time on data-plane reliability (batching edge cases, DLQ replay tooling, Kafka upgrades, monitoring dashboards for all of it). That's roughly one full-time engineer of drag on a 3-person backend team. A drop-in gateway removes that line item. | ||
| **Scenario:** a seed-stage team picks "Kafka + ClickHouse + custom ingest". Six months on, two engineers spend ~30% of their time on data-plane reliability (batching edge cases, DLQ replay, Kafka upgrades) — one full-time engineer of drag on a 3-person backend team. A drop-in gateway removes it. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Fix the FTE arithmetic.
Two engineers spending approximately 30% each represent approximately 0.6 FTE, not one full-time engineer.
Proposed wording
- Six months on, two engineers spend ~30% of their time on data-plane reliability (batching edge cases, DLQ replay, Kafka upgrades) — one full-time engineer of drag on a 3-person backend team.
+ Six months on, two engineers spend ~30% of their time on data-plane reliability (batching edge cases, DLQ replay, Kafka upgrades) — ~0.6 FTE of drag on a 3-person backend team.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| **Scenario:** a seed-stage team picks "Kafka + ClickHouse + custom ingest". Six months on, two engineers spend ~30% of their time on data-plane reliability (batching edge cases, DLQ replay, Kafka upgrades) — one full-time engineer of drag on a 3-person backend team. A drop-in gateway removes it. | |
| **Scenario:** a seed-stage team picks "Kafka + ClickHouse + custom ingest". Six months on, two engineers spend ~30% of their time on data-plane reliability (batching edge cases, DLQ replay, Kafka upgrades) — ~0.6 FTE of drag on a 3-person backend team. A drop-in gateway removes it. |
| | Vendor lock-in | Rewriting queries | None — WaveHouse is Apache 2.0, ClickHouse is yours | | ||
|
|
||
| Tinybird wins on "zero ops to start." WaveHouse wins on "own your data plane and pay AWS, not a second vendor" — which gets more compelling at scale, on sensitive data, or for anyone who needs on-prem. | ||
| Tinybird wins on "zero ops to start". WaveHouse wins on owning the data plane and paying AWS instead of a second vendor — for scale, sensitive data, or on-prem. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Do not assume AWS for every self-hosted deployment.
The sentence also includes on-premises deployments, but says teams pay AWS. Use “their infrastructure provider” instead.
| - **Internal BI / data team workloads** — if the clients are BI tools and batch ETL, point them straight at ClickHouse. | ||
| - **Pure bulk ETL** — redundant when writes already arrive as 100k-row blocks from Airflow or dbt. | ||
| - **ClickHouse-as-a-datalake** — WaveHouse targets the hot path, not cold analytics over S3/Iceberg. | ||
| - **Kafka-shaped organizations** — deep in Kafka Connect custom sinks, migration may cost too much; run Kafka → WaveHouse → ClickHouse for the real-time layer. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -i --glob '*.go' --glob 'go.mod' \
'kafka|redpanda|sarama|franz-go|segmentio/kafka-go|kafka-go' .Repository: Wave-RF/WaveHouse
Length of output: 155
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- repository files relevant to integrations ---'
git ls-files | rg -i '(^|/)(readme|docs|connector|ingest|kafka|redpanda|clickhouse)|\.(go|rs|ts|tsx|js|yaml|yml|json|md|mdx|toml)$' | head -300
printf '%s\n' '--- all tracked Kafka-related references ---'
rg -n -i --hidden --glob '!.git/**' \
'kafka|redpanda|sarama|franz-go|segmentio/kafka-go|kafka-go|consumer|connector' .
printf '%s\n' '--- target documentation context ---'
sed -n '220,270p' docs/src/content/docs/why-wavehouse.md
printf '%s\n' '--- ingestion and integration references ---'
rg -n -i --hidden --glob '!.git/**' \
'v1/ingest|http ingest|clickhouse|integration|source|sink|webhook|stream' \
README* docs src cmd internal 2>/dev/null || trueRepository: Wave-RF/WaveHouse
Length of output: 50374
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- exact Kafka and client references ---'
rg -n -i --hidden --glob '!.git/**' \
'\bkafka\b|redpanda|sarama|franz-go|segmentio/kafka-go|kafka-go' . || true
printf '%s\n' '--- declared Go dependencies ---'
rg -n -i 'nats|clickhouse|kafka|redpanda|sarama|franz-go' go.mod go.sum 2>/dev/null || true
printf '%s\n' '--- documented input and integration surfaces ---'
rg -n -i --glob '*.md' --glob '*.mdx' \
'POST /v1/ingest|/v1/ingest|NATS|webhook|Kafka|Redpanda|connector|integration' \
README.md docs/src/content/docs 2>/dev/null | head -250
printf '%s\n' '--- target line and nearby section ---'
cat -n docs/src/content/docs/why-wavehouse.md | sed -n '238,258p'Repository: Wave-RF/WaveHouse
Length of output: 39713
Replace the Kafka topology with a supported integration.
WaveHouse accepts writes through POST /v1/ingest and has no Kafka consumer or connector. Document an external Kafka-to-HTTP bridge if this topology is required.
| ## 🛠️ Quick Start | ||
|
|
||
| Pick whichever fits — each ends with WaveHouse listening on `http://localhost:8080`. | ||
| All methods result in WaveHouse listening on `http://localhost:8080`. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Do not claim that every quick-start method starts WaveHouse.
Method B only pulls the image. Method C only installs the binary. Neither method starts a server or establishes http://localhost:8080. Add the missing run and ClickHouse-networking commands, or limit this sentence to the Docker Compose method.
|
This AI approach to condense and re-write docs was worse than what we had after thorough human review – while verbose and clearly AI written, the original docs are at least correct – these suggested changes drop important specifiers without understanding their importance or make docs factually incorrect in places, and do not meaningfully improve docs quality especially given the SNR of filtering out the inaccuracies, thus I have stopped using them for docs cleanups and will dedicate a human pass and docs fixes one doc at a time in the future instead. Closing. |
Summary
The docs have become... excessively verbose. This PR makes the documentation site and the README much less wordy, without changing the actual meaning of any page.
Per-file word count reductions land between 62% and 77% of the original word count for substantive pages. Four short pages (
404,sdk/pipes,sdk/reference,sdk/admin) compress less. You can only optimize something so much right?Largest reductions:
api.md(7,699 → 5,015 words),development.md(5,754 → 3,692),claude-code.md(3,003 → 2,207).Test plan
markdownlintpassesastro checkpasses — content collections still build, frontmatter intactRelated Issues
Closes #