Skip to content

feat(sdk): add Go client SDK with full API-tree parity - #434

Open
jfwoods wants to merge 43 commits into
mainfrom
go-sdk
Open

feat(sdk): add Go client SDK with full API-tree parity#434
jfwoods wants to merge 43 commits into
mainfrom
go-sdk

Conversation

@jfwoods

@jfwoods jfwoods commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Complete Go SDK at clients/go/ — a canonical, officially supported client with full API-tree parity against the TypeScript SDK. Zero third-party runtime dependencies (stdlib only).

What's included

SDK core (clients/go/)

  • Immutable QueryBuilder with generics (FetchTyped[Row], SQL[Row])
  • SSE StreamController with reconnect/backoff and client-side filtering
  • LiveQuery drain-then-switch backfill (stream-first, dedup, go live)
  • Reflect-based Insert handling any []T, not just []map[string]any
  • Full namespace coverage: ingest, query, streaming, live queries, pipes, DLQ, schema, policy, health

Codegen CLI (clients/go/cmd/wavehouse-codegen/)

  • Generates typed Go structs from /v1/schema endpoint
  • Maps ClickHouse types to JSON-wire-compatible Go types

Testing

  • 86 unit tests across 11 test files, run with -race (SDK coverage ~87%)
  • 45 cross-language wire-format conformance cases (shared JSON fixtures drive both Go and TS SDKs; both runners hard-fail on unhandled cases)
  • 9 E2E tests behind //go:build e2e tag (against live server)
  • make test-go-sdk, make test-go-sdk-e2e, make test-conformance-ts, make lint-go-sdk targets

Documentation

  • 6 Starlight pages: overview, queries, streaming, pipes, admin, reference
  • SDK README with quick-start and link to wavehouse.dev
  • Root README, architecture.md, AGENTS.md updated; TS SDK pages retitled for disambiguation

CI

  • test-go-sdk + test-conformance-ts wired into the CI unit job
  • lint-go-sdk + verify-go-sdk wired into verify-parallel

Commits

33 commits: the initial implementation (SDK, docs, CI wiring), then a review-response series addressing every Copilot/CodeRabbit inline thread plus successive local pre-push review rounds (each thread gets an inline reply; false positives are rebutted rather than patched).

Notes

jfwoods added 5 commits August 5, 2026 15:13
Complete Go SDK at clients/go/ with zero third-party runtime dependencies.
Covers ingest, query (structured + SQL), streaming (SSE with reconnect),
live queries, pipes, DLQ, schema, policy, and health endpoints.

- Immutable QueryBuilder with generics (FetchTyped[Row], SQL[Row])
- SSE StreamController with reconnect/backoff and client-side filtering
- LiveQuery drain-then-switch backfill (stream-first, dedup, go live)
- Reflect-based Insert handling any []T, not just []map[string]any
- wavehouse-codegen CLI for generating typed row structs from /v1/schema
- 42 unit tests + 44 cross-language wire-format conformance cases
- E2E test scaffolding (build tag e2e, 9 tests against live server)
- Cross-language conformance runner for TS SDK (tests/conformance/)
- Makefile targets: verify-go-sdk, test-go-sdk, test-go-sdk-e2e, lint-go-sdk
Six Starlight pages covering installation, queries, streaming, pipes,
admin operations, and API reference. Sidebar nav group added.
Cross-link from SDK index page. Architecture page updated with Go SDK.
Root README updated with Go SDK install.
- Add test-go-sdk to CI unit job
- AGENTS.md: Go SDK file structure + feature parity table
- lint-go-sdk already wired via verify-parallel in Makefile
- context.Context as first param in doRequest (revive)
- Checked all json Encode/Decode/Unmarshal returns (errcheck)
- Wrapped defer Body.Close with error discard (errcheck)
- if-else chain to switch in buildAST (gocritic)
- Tagged switch on r.Method in test (staticcheck)
- Renamed built-in shadow cap to capt (revive)
- Removed wasted msg assignment (wastedassign)
- WriteFile 0o644 to 0o600 (gosec)
- nolint:gosec for cancel called in Close (gosec)
Copilot AI lite review requested due to automatic review settings August 5, 2026 20:24
@github-actions github-actions Bot added documentation Improvements or additions to documentation github_actions Pull requests that update GitHub Actions code go Pull requests that update go code area/docs Documentation, site/, README area/infra CI, build, deploy, Docker, release labels Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added an official Go SDK with typed queries, inserts, SQL, streaming, live queries, retries, structured errors, and context cancellation.
    • Added schema-to-Go code generation through the wavehouse-codegen CLI.
    • Added support for schema administration, access policies, dead-letter queue statistics, and named-pipe management.
  • Documentation
    • Added comprehensive Go SDK guides, examples, API references, and installation instructions.
    • Updated platform documentation to cover both TypeScript and Go SDKs.
  • Tests
    • Expanded SDK, wire-format conformance, and end-to-end coverage across supported operations.

Walkthrough

The pull request adds an official Go SDK with query, ingestion, streaming, administration, code generation, conformance tests, documentation, and CI integration. It also adds independent Go SDK coverage reporting and TypeScript wire-format conformance execution.

Changes

Go SDK implementation

Layer / File(s) Summary
SDK contracts and HTTP transport
clients/go/types.go, clients/go/wavehouse.go, clients/go/http.go, clients/go/errors.go, clients/go/table.go, clients/go/query_builder.go
Adds public SDK types, client configuration, retries, structured errors, table operations, immutable query builders, typed pagination, ingestion, and SQL execution.
SSE streaming and live queries
clients/go/stream.go, clients/go/live_query.go
Adds SSE parsing, reconnects, filtering, projection, event channels, lifecycle controls, live-query buffering, timestamp deduplication, and cleanup.
Administrative APIs and named pipes
clients/go/schema.go, clients/go/policy.go, clients/go/dlq.go, clients/go/sys.go, clients/go/pipes.go
Adds schema, policy, DLQ, health, and named-pipe APIs with typed and untyped pipe execution.
Schema code generation
clients/go/cmd/wavehouse-codegen/*
Adds schema retrieval, ClickHouse-to-Go type mapping, deterministic struct generation, formatting, collision checks, and CLI error handling.
SDK unit and integration validation
clients/go/*_test.go
Adds tests for client setup, transport, errors, queries, ingestion, namespaces, streams, live queries, and code generation.
Shared wire-format conformance
clients/go/testdata/wire_cases.json, clients/go/conformance_test.go, tests/conformance/conformance_ts.mjs
Adds shared fixtures and Go and TypeScript runners that compare request methods, paths, headers, content types, and serialized bodies.
SDK E2E coverage and documentation
clients/go/e2e_test.go, clients/go/README.md, clients/go/example_test.go, docs/src/content/docs/sdk/go/*
Adds live-server E2E tests, usage examples, Go SDK documentation, and reference pages.
Coverage, CI, and repository guidance
Makefile, scripts/cov/main.go, .github/workflows/ci.yml, .testcoverage.yml, AGENTS.md, CONTRIBUTING.md, README.md, docs/src/**, .github/*, .claude/commands/cover.md, biome.json, CHANGELOG.md
Adds nested-module coverage and test targets, CI execution, repository guidance, SDK navigation, documentation updates, and labeling and dependency-monitoring changes.

Estimated code review effort: 5 (Critical) | ~120 minutes

Merge Risk: 🟠 High · up to 73edc

This PR adds a new Go SDK and related documentation, but the current head still has merge-blocking issues: REST requests may leak configured credentials across cross-host redirects, schema listing can fail on a supported response shape, and race-enabled tests can fail because shared header state is unsynchronized. Several credential-handling examples also need correction before publication, so the PR is not ready to merge until these are addressed.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 184 functions across 25 files. (37 skipped: 37 unsupported.) Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: adding a Go client SDK with API-tree parity.
Description check ✅ Passed The description directly explains the Go SDK, its features, tests, documentation, CI integration, and nested-module design.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch go-sdk
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch go-sdk

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

📚 Docs preview is livehttps://b00566b7-wavehouse-docs.wave-rf.workers.dev

  • Commit3ed1698: fix(sdk): compare stream filter timestamps as instants, not as text
  • Author@jfwoods
  • Committed — 2026-08-21 15:48 (UTC-04:00)
  • Deployed — 2026-08-21 15:54 EDT

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new, officially supported Go client SDK under clients/go/ as a nested Go module, aiming for wire-format/API parity with the existing TypeScript SDK. The PR also wires Go SDK lint/test into the repo Makefile + CI, and adds Go/TS conformance runners plus docs updates to publish Go SDK usage on the docs site.

Changes:

  • Introduces the Go SDK module (clients/go/) including query builder, ingest helpers, namespaces, streaming/live-query primitives, and a schema-based codegen CLI.
  • Adds cross-language wire-format conformance harnesses (Go test embedding shared fixtures + a Node runner for TS).
  • Wires Go SDK lint/vet/tests into make verify/make ci and GitHub Actions CI; updates docs/README/CHANGELOG/AGENTS to reflect the new canonical SDK.

Reviewed changes

Copilot reviewed 41 out of 41 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/conformance/conformance_ts.mjs Node-based TS conformance runner against shared wire fixture.
README.md Updates project summary to mention both TypeScript + Go SDKs.
Makefile Adds lint-go-sdk, verify-go-sdk, test-go-sdk, test-go-sdk-e2e; hooks into verify/ci.
docs/src/content/docs/sdk/index.mdx Adds Go SDK callouts + navigation card.
docs/src/content/docs/sdk/go/index.md New Go SDK landing page (install/quickstart/error model).
docs/src/content/docs/sdk/go/queries.md Go SDK query + ingest documentation (builder, pagination, raw SQL).
docs/src/content/docs/sdk/go/streaming.md Go SDK streaming + live query documentation.
docs/src/content/docs/sdk/go/pipes.md Go SDK pipes usage + admin CRUD docs.
docs/src/content/docs/sdk/go/admin.md Go SDK admin namespaces (schema/policy/dlq/sys) docs.
docs/src/content/docs/sdk/go/reference.md Go SDK reference (API tree, error codes, codegen CLI).
docs/src/content/docs/architecture.md Notes that both TS + Go SDKs exist.
docs/src/config/sidebar.ts Adds a separate Go SDK sidebar tree.
clients/go/go.mod Introduces nested Go module for the SDK.
clients/go/wavehouse.go Core client wiring (config/options/namespaces) + helpers.
clients/go/http.go HTTP transport: auth injection, retry/backoff, error parsing.
clients/go/errors.go SDK error type + helpers for retryability and HTTP error parsing.
clients/go/types.go Shared wire types (structured query AST, policy/pipes/schema, streaming, paging).
clients/go/query_builder.go Immutable query builder + typed/untyped fetch, pagination cursoring, stream wrapper.
clients/go/table.go Table ref: fetch/select/insert (JSON vs NDJSON batch) + schema + stream.
clients/go/sys.go /v1/health namespace.
clients/go/schema.go Admin schema namespace (list/refresh).
clients/go/policy.go Admin policy namespace (get/set/validate).
clients/go/pipes.go Pipe execution + pipes admin CRUD namespace.
clients/go/dlq.go DLQ stats namespace + placeholder stream entrypoint.
clients/go/live_query.go Live query orchestration (buffer, backfill, dedup, go-live).
clients/go/README.md Standalone Go SDK README for module users.
clients/go/cmd/wavehouse-codegen/main.go Codegen CLI to generate Go structs from /v1/schema.
clients/go/client_test.go Unit tests for client defaults, From, SQL, token helper.
clients/go/http_test.go Unit tests for transport behavior (auth, retry, abort, backoff).
clients/go/errors_test.go Unit tests for error parsing + retryable classification.
clients/go/query_builder_test.go Unit tests for builder immutability + AST emission + pagination.
clients/go/table_test.go Unit tests for insert behavior (single, batch, typed slice, ndjson, empty batch).
clients/go/namespaces_test.go Unit tests for Sys/Schema/Policy/DLQ/Pipes namespaces + PipeRef fetch.
clients/go/conformance_test.go Go conformance test runner embedding the shared wire cases fixture.
clients/go/example_test.go Go doc examples (non-asserting, requires running server).
clients/go/e2e_test.go Optional //go:build e2e live-server E2E suite.
CHANGELOG.md Announces the Go SDK addition under Unreleased.
AGENTS.md Updates “SDK Sync” and repo structure to include Go SDK as canonical.
.github/workflows/ci.yml Adds Go SDK tests to CI unit job invocation.

Comment thread clients/go/http.go
Comment thread clients/go/cmd/wavehouse-codegen/main.go
Comment thread clients/go/query_builder_test.go
@github-code-quality

github-code-quality Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: Go

Go

The overall line coverage in commit 3ed1698 in the go-sdk branch remains at 91%, unchanged from commit 26cb857 in the main branch.


Updated August 21, 2026 19:55 UTC

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 52


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b7726491-84ac-4412-9a11-65f929f6d91a

📥 Commits

Reviewing files that changed from the base of the PR and between c816e34 and b66413a.

📒 Files selected for processing (41)
  • .github/workflows/ci.yml
  • AGENTS.md
  • CHANGELOG.md
  • Makefile
  • README.md
  • clients/go/README.md
  • clients/go/client_test.go
  • clients/go/cmd/wavehouse-codegen/main.go
  • clients/go/conformance_test.go
  • clients/go/dlq.go
  • clients/go/e2e_test.go
  • clients/go/errors.go
  • clients/go/errors_test.go
  • clients/go/example_test.go
  • clients/go/go.mod
  • clients/go/http.go
  • clients/go/http_test.go
  • clients/go/live_query.go
  • clients/go/namespaces_test.go
  • clients/go/pipes.go
  • clients/go/policy.go
  • clients/go/query_builder.go
  • clients/go/query_builder_test.go
  • clients/go/schema.go
  • clients/go/stream.go
  • clients/go/sys.go
  • clients/go/table.go
  • clients/go/table_test.go
  • clients/go/testdata/wire_cases.json
  • clients/go/types.go
  • clients/go/wavehouse.go
  • docs/src/config/sidebar.ts
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/sdk/go/admin.md
  • docs/src/content/docs/sdk/go/index.md
  • docs/src/content/docs/sdk/go/pipes.md
  • docs/src/content/docs/sdk/go/queries.md
  • docs/src/content/docs/sdk/go/reference.md
  • docs/src/content/docs/sdk/go/streaming.md
  • docs/src/content/docs/sdk/index.mdx
  • tests/conformance/conformance_ts.mjs
📜 Review details
⏰ Context from checks skipped due to timeout. (8)
  • GitHub Check: Docs build
  • GitHub Check: Coverage
  • GitHub Check: E2E tests
  • GitHub Check: Integration tests
  • GitHub Check: Unit tests
  • GitHub Check: copilot-pull-request-reviewer
  • GitHub Check: Lint
  • GitHub Check: Analyze (go)
🧰 Additional context used
📓 Path-based instructions (9)
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Never force-push or rebase PR branches; merge origin/main instead.
Do not hand-write review or CI markers and do not bypass gates with --no-verify; use the prescribed tooling.

Files:

  • clients/go/go.mod
  • README.md
  • docs/src/content/docs/architecture.md
  • clients/go/sys.go
  • docs/src/content/docs/sdk/go/index.md
  • docs/src/config/sidebar.ts
  • docs/src/content/docs/sdk/go/admin.md
  • clients/go/testdata/wire_cases.json
  • clients/go/dlq.go
  • clients/go/schema.go
  • docs/src/content/docs/sdk/go/pipes.md
  • docs/src/content/docs/sdk/index.mdx
  • clients/go/e2e_test.go
  • clients/go/live_query.go
  • docs/src/content/docs/sdk/go/reference.md
  • clients/go/README.md
  • tests/conformance/conformance_ts.mjs
  • CHANGELOG.md
  • clients/go/wavehouse.go
  • clients/go/policy.go
  • clients/go/cmd/wavehouse-codegen/main.go
  • clients/go/pipes.go
  • clients/go/types.go
  • AGENTS.md
  • clients/go/namespaces_test.go
  • clients/go/conformance_test.go
  • clients/go/errors.go
  • clients/go/http_test.go
  • clients/go/query_builder_test.go
  • clients/go/table.go
  • clients/go/errors_test.go
  • docs/src/content/docs/sdk/go/queries.md
  • clients/go/example_test.go
  • clients/go/table_test.go
  • clients/go/client_test.go
  • clients/go/query_builder.go
  • clients/go/http.go
  • clients/go/stream.go
  • Makefile
  • docs/src/content/docs/sdk/go/streaming.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

Every code change must update corresponding documentation and add a notable-change entry to CHANGELOG.md under [Unreleased].

Files:

  • README.md
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/sdk/go/index.md
  • docs/src/content/docs/sdk/go/admin.md
  • docs/src/content/docs/sdk/go/pipes.md
  • docs/src/content/docs/sdk/go/reference.md
  • clients/go/README.md
  • CHANGELOG.md
  • AGENTS.md
  • docs/src/content/docs/sdk/go/queries.md
  • docs/src/content/docs/sdk/go/streaming.md
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go 1.26 with strict gofumpt formatting; return errors instead of panicking, wrapping them with fmt.Errorf("context: %w", err).
Use structured logging with log/slog, and pass dependencies explicitly rather than using global state.

Files:

  • clients/go/sys.go
  • clients/go/dlq.go
  • clients/go/schema.go
  • clients/go/e2e_test.go
  • clients/go/live_query.go
  • clients/go/wavehouse.go
  • clients/go/policy.go
  • clients/go/cmd/wavehouse-codegen/main.go
  • clients/go/pipes.go
  • clients/go/types.go
  • clients/go/namespaces_test.go
  • clients/go/conformance_test.go
  • clients/go/errors.go
  • clients/go/http_test.go
  • clients/go/query_builder_test.go
  • clients/go/table.go
  • clients/go/errors_test.go
  • clients/go/example_test.go
  • clients/go/table_test.go
  • clients/go/client_test.go
  • clients/go/query_builder.go
  • clients/go/http.go
  • clients/go/stream.go
clients/go/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

When backend public APIs change, update the Go SDK's corresponding typed client, query, streaming, policy, pipe, or payload types as applicable.

Files:

  • clients/go/sys.go
  • clients/go/dlq.go
  • clients/go/schema.go
  • clients/go/e2e_test.go
  • clients/go/live_query.go
  • clients/go/wavehouse.go
  • clients/go/policy.go
  • clients/go/cmd/wavehouse-codegen/main.go
  • clients/go/pipes.go
  • clients/go/types.go
  • clients/go/namespaces_test.go
  • clients/go/conformance_test.go
  • clients/go/errors.go
  • clients/go/http_test.go
  • clients/go/query_builder_test.go
  • clients/go/table.go
  • clients/go/errors_test.go
  • clients/go/example_test.go
  • clients/go/table_test.go
  • clients/go/client_test.go
  • clients/go/query_builder.go
  • clients/go/http.go
  • clients/go/stream.go
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

The TypeScript SDK must have zero third-party runtime dependencies and maintain API-tree parity with the Go SDK.

Files:

  • docs/src/config/sidebar.ts
docs/src/content/docs/**/*.mdx

📄 CodeRabbit inference engine (AGENTS.md)

Author Mermaid diagrams primarily top-down, avoid placing large diagrams side-by-side, and keep labels short and readable.

Files:

  • docs/src/content/docs/sdk/index.mdx
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*_test.go: Use table-driven tests with tests := []struct{...} and t.Run(tt.name, ...) for multiple scenarios.
Use shared helpers and mocks from internal/testutil/, including JWT, schema, policy, pipes, and HTTP response helpers, instead of ad-hoc implementations.

Files:

  • clients/go/e2e_test.go
  • clients/go/namespaces_test.go
  • clients/go/conformance_test.go
  • clients/go/http_test.go
  • clients/go/query_builder_test.go
  • clients/go/errors_test.go
  • clients/go/example_test.go
  • clients/go/table_test.go
  • clients/go/client_test.go
.github/workflows/**/*.yml

📄 CodeRabbit inference engine (AGENTS.md)

Pin third-party GitHub Actions to full commit SHAs with version comments; never use floating tags such as @main.

Files:

  • .github/workflows/ci.yml
Makefile

📄 CodeRabbit inference engine (AGENTS.md)

Use make ci as the full local validation pipeline before every push; do not use CI as the first feedback loop.

Files:

  • Makefile
🧠 Learnings (4)
📚 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:

  • README.md
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/sdk/go/index.md
  • docs/src/content/docs/sdk/go/admin.md
  • docs/src/content/docs/sdk/go/pipes.md
  • docs/src/content/docs/sdk/go/reference.md
  • clients/go/README.md
  • CHANGELOG.md
  • AGENTS.md
  • docs/src/content/docs/sdk/go/queries.md
  • docs/src/content/docs/sdk/go/streaming.md
📚 Learning: 2026-07-07T12:38:12.052Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 378
File: internal/auth/auth.go:119-132
Timestamp: 2026-07-07T12:38:12.052Z
Learning: In this repo, do not add or recommend logging/tracing client IP addresses using naive or untrusted sources (e.g., `r.RemoteAddr` or directly trusting/deriving `X-Forwarded-For`) anywhere in the Go codebase. `middleware.RealIP` was removed due to IP-spoofing risks, and proper trusted-proxy-aware client-IP handling is intentionally deferred to issue `#333`. During code review, if proposed changes would record client IPs (including in audit paths such as `internal/auth/auth.go`), reject/redirect until `#333` lands with correct trusted-proxy configuration and safeguards.

Applied to files:

  • clients/go/sys.go
  • clients/go/dlq.go
  • clients/go/schema.go
  • clients/go/e2e_test.go
  • clients/go/live_query.go
  • clients/go/wavehouse.go
  • clients/go/policy.go
  • clients/go/cmd/wavehouse-codegen/main.go
  • clients/go/pipes.go
  • clients/go/types.go
  • clients/go/namespaces_test.go
  • clients/go/conformance_test.go
  • clients/go/errors.go
  • clients/go/http_test.go
  • clients/go/query_builder_test.go
  • clients/go/table.go
  • clients/go/errors_test.go
  • clients/go/example_test.go
  • clients/go/table_test.go
  • clients/go/client_test.go
  • clients/go/query_builder.go
  • clients/go/http.go
  • clients/go/stream.go
📚 Learning: 2026-06-26T12:23:22.696Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 346
File: internal/stream/subscriber_test.go:9-28
Timestamp: 2026-06-26T12:23:22.696Z
Learning: In this Go repository, prefer table-driven tests (e.g., `[]struct{...}` with `t.Run(...)`) only for tests that cover multiple scenarios/inputs and can be cleanly enumerated. Do not artificially rewrite a clear single-scenario sequential behavioral-flow test into a table-driven form just to fit the pattern; if there’s only one meaningful scenario, keep the test as a straightforward linear flow (as in `TestSubscriber_SendDeliversThenDropsWhenFull`).

Applied to files:

  • clients/go/e2e_test.go
  • clients/go/namespaces_test.go
  • clients/go/conformance_test.go
  • clients/go/http_test.go
  • clients/go/query_builder_test.go
  • clients/go/errors_test.go
  • clients/go/example_test.go
  • clients/go/table_test.go
  • clients/go/client_test.go
📚 Learning: 2026-06-10T15:01:59.729Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 312
File: .github/workflows/ci.yml:232-237
Timestamp: 2026-06-10T15:01:59.729Z
Learning: In this repo’s CI workflow (ci.yml), treat `clickhouse/clickhouse-server:latest` in the workflow’s prefetch steps (`docker pull -q clickhouse/clickhouse-server:latest`) as an intentional canary: the `:latest` tag is meant to mirror the tag that testcontainers resolves at runtime. Do not flag it as a supply-chain concern during CI workflow reviews as long as it’s used specifically for this “latest mirrors testcontainers runtime” prefetch purpose. If the workflow pins a different tag/digest for a different reason, or uses `latest` outside of this prefetch/canary pattern, then it may warrant scrutiny.

Applied to files:

  • .github/workflows/ci.yml
🪛 LanguageTool
docs/src/content/docs/sdk/go/index.md

[style] ~11-~11: Since ownership is already implied, this phrasing may be redundant.
Context: ...ypeScript client (@wavehouse/sdk) has its own docs starting at SDK Overview —...

(PRP_OWN)


[style] ~228-~228: Since ownership is already implied, this phrasing may be redundant.
Context: ... returned *StreamController manages its own background goroutine and connection, ...

(PRP_OWN)


[style] ~237-~237: Consider using the typographical ellipsis character here instead.
Context: ...ed row slices, not just maps.** Passing []ClickRow{...} (any slice type, detected via reflect...

(ELLIPSIS)

docs/src/content/docs/sdk/go/reference.md

[style] ~216-~216: Since ownership is already implied, this phrasing may be redundant.
Context: ... unit tests colocated in clients/go/ (its own Go module — clients/go/go.mod — separ...

(PRP_OWN)


[style] ~221-~221: ‘exact same’ might be wordy. Consider a shorter alternative.
Context: ...lls and asserts the Go SDK produces the exact same HTTP method, path, content type, and bo...

(EN_WORDINESS_PREMIUM_EXACT_SAME)

docs/src/content/docs/sdk/go/queries.md

[style] ~211-~211: Consider using the typographical ellipsis character here instead.
Context: ...ent type ([]string, []int, []any, ...) | | wavehouse.OpLike | like | SQL ...

(ELLIPSIS)


[typographical] ~332-~332: The word ‘When’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ...east that many rows, HasMore is true. Cursor-based pagination's Next walks ...

(WRB_QUESTION_MARK)


[style] ~389-~389: Consider using the typographical ellipsis character here instead.
Context: ...put — use the structured query builder (wh.From(table)...). :::

(ELLIPSIS)

docs/src/content/docs/sdk/go/streaming.md

[style] ~86-~86: Consider using “who” when you are referring to a person instead of an object.
Context: ... buffered (256 events); a slow consumer that never drains it causes the SDK to **dro...

(THAT_WHO)


[typographical] ~160-~160: The word ‘Where’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ...ueryBuilderwith.Where()filters or.Select()columns calls.Stream()`, the...

(WRB_QUESTION_MARK)


[typographical] ~173-~173: The word ‘Where’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ...ilterOpset.Where()takes everywhere.like/not_like` match SQL LIKE sema...

(WRB_QUESTION_MARK)


[style] ~176-~176: Consider using the typographical ellipsis character here instead.
Context: ...and side ([]string, []int, []any, ...), not just []any. --- ## Live Queri...

(ELLIPSIS)


[style] ~231-~231: Since ownership is already implied, this phrasing may be redundant.
Context: ...query outside a live query. Decode into your own type inside the callback if you need on...

(PRP_OWN)

🔇 Additional comments (37)
clients/go/live_query.go (2)

95-126: LGTM!

Also applies to: 142-147


18-35: 🎯 Functional Correctness

No change needed for filters.

QueryBuilder.LiveQuery passes the query filters through QueryBuilder.Stream, which wraps the raw stream with newFilteredStreamController before newLiveQuery. Client-side filtering is already applied to live events.

clients/go/query_builder.go (1)

35-45: LGTM!

Also applies to: 239-273

clients/go/schema.go (1)

11-33: LGTM!

clients/go/dlq.go (1)

15-42: LGTM!

clients/go/stream.go (2)

169-169: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Sensitive Data Exposure (CWE-532): Insertion of Sensitive Information into Log File

Reachability: External

Reachability path
● Entry
  clients/go/e2e_test.go:313
  TestE2E_PipesCRUD: Create
│
▼
● Hop
  clients/go/errors.go:26
  Error
│
▼
● Sink
  clients/go/stream.go

Replace log.Printf with log/slog, and stop logging raw payloads.

Two problems:

  1. The coding guidelines require structured logging with log/slog and explicit dependency passing. Both call sites write to the global standard logger, which an SDK consumer cannot redirect or silence.
  2. Line 340 writes the whole malformed SSE frame into the log. That frame carries user record data, so a truncated or malformed frame leaks record contents into logs the consumer did not opt into. Log the length and the decode error instead of the payload.

Accept an optional *slog.Logger on ClientOptions, store it on httpContext, and default it to slog.Default().

🛡️ Proposed change
-		log.Printf("[wavehouse] stream event dropped: channel buffer full")
+		sc.logger.Warn("stream event dropped", "reason", "channel buffer full", "table", event.Table)
 	var msg sseMessage
 	if err := json.Unmarshal([]byte(data), &msg); err != nil {
-		log.Printf("[wavehouse] SSE received malformed message: %s", data)
+		sc.logger.Warn("malformed SSE message", "error", err, "bytes", len(data))
 		return
 	}

Then drop the log import and add log/slog.

Run the following script to find every global-logger call in the SDK:

#!/bin/bash
# Description: Locate global log package usage and any existing slog wiring in the Go SDK.
set -euo pipefail

fd -e go . clients/go --exec rg -n -C2 '\blog\.(Printf|Println|Print|Fatal|Fatalf)\b' {}
fd -e go . clients/go --exec rg -n -C3 'slog|Logger' {}

Also applies to: 337-342


363-393: 🩺 Stability & Availability

No change needed for the filtered controller callback scope.

StreamController.closeEventCh is not exported, the filtered path uses internal access to eventCh, and emitEvent uses a non-blocking send so it does not panic when sc.eventCh is closed.

			> Likely an incorrect or invalid review comment.
clients/go/go.mod (1)

1-3: 📐 Maintainability & Code Quality

No patch-version change needed for go 1.26.5.

The module explicitly supports Go 1.26.5 and no earlier Go 1.26 patch, so this matches the declared Go version.

			> Likely an incorrect or invalid review comment.
clients/go/types.go (1)

7-180: LGTM!

Also applies to: 193-245

clients/go/http_test.go (1)

13-199: LGTM!

clients/go/wavehouse.go (1)

44-48: LGTM!

Also applies to: 96-114, 132-142

clients/go/client_test.go (1)

11-100: LGTM!

clients/go/table.go (1)

20-76: LGTM!

clients/go/table_test.go (1)

12-209: LGTM!

clients/go/cmd/wavehouse-codegen/main.go (2)

300-338: LGTM!


42-46: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Sensitive Data Exposure (CWE-214)

Reachability: Internal

Accept the bearer token from an environment variable.

--auth requires the token on the command line. The token then appears in the process argument list and in shell history. Any local user can read /proc/<pid>/cmdline while the command runs. Add an environment-variable source such as WAVEHOUSE_AUTH and keep the flag as an override only.

🔐 Proposed change
 func parseArgs() cliArgs {
 	args := cliArgs{url: "http://localhost:8080", out: "./wavehouse_types.go", pkg: "main"}
+	if tok := os.Getenv("WAVEHOUSE_AUTH"); tok != "" {
+		args.auth = tok
+	}
 	for i := 1; i < len(os.Args); i++ {

Also document the environment variable in the --help text.

tests/conformance/conformance_ts.mjs (2)

73-146: LGTM!


258-261: 🎯 Functional Correctness

No change needed for health empty-response parsing.

The TS request<T> implementation treats successful responses without text bodies as undefined and does not call JSON.parse for the empty /v1/health body, so this cannot hide a correct request for that fixture.

			> Likely an incorrect or invalid review comment.
clients/go/testdata/wire_cases.json (1)

374-388: 🗄️ Data Integrity & Integration

No change needed.

Both SDKs send array insert as \n-joined NDJSON without a trailing newline, and insertNDJSON passes NDJSON sources through unchanged.

clients/go/e2e_test.go (1)

286-311: 🗄️ Data Integrity & Integration

No change needed.

The SDK Policy, SDK PolicyNamespace.Get/Set, and server policy.Policy share the same JSON-marshaled fields, so this round-trip does not drop server-side policy fields.

			> Likely an incorrect or invalid review comment.
.github/workflows/ci.yml (1)

207-208: LGTM!

AGENTS.md (1)

63-63: LGTM!

Also applies to: 334-349, 390-397

CHANGELOG.md (1)

14-14: LGTM!

Makefile (2)

431-435: LGTM!


735-739: LGTM!

Also applies to: 777-777

docs/src/content/docs/sdk/go/queries.md (2)

1-363: LGTM!

Also applies to: 369-382, 390-390


383-389: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Injection (CWE-89): Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

State the safe boundary for raw SQL.

SQL[Row] has no parameter binding. The phrase “Inline literals into the SQL” can be read as permission to concatenate untrusted values into /v1/admin/query. State that only trusted constants may be inlined. Require the structured query builder for user-supplied values.

Suggested wording
-Inline literals into the SQL, or — for safe binding from user-supplied input — use the structured query builder (`wh.From(table)...`).
+Inline only trusted constants. For user-supplied input, use the structured query builder (`wh.From(table)...`).
docs/src/content/docs/sdk/go/reference.md (1)

1-40: LGTM!

Also applies to: 45-136, 144-229, 234-234

docs/src/content/docs/sdk/go/streaming.md (1)

1-255: LGTM!

docs/src/content/docs/sdk/index.mdx (1)

17-18: LGTM!

Also applies to: 423-434

clients/go/README.md (1)

1-218: LGTM!

Also applies to: 221-221, 223-235

docs/src/config/sidebar.ts (1)

28-30: LGTM!

Also applies to: 41-51

docs/src/content/docs/architecture.md (1)

279-279: LGTM!

docs/src/content/docs/sdk/go/admin.md (1)

1-62: LGTM!

Also applies to: 66-118

docs/src/content/docs/sdk/go/index.md (1)

1-179: LGTM!

Also applies to: 196-249

docs/src/content/docs/sdk/go/pipes.md (1)

1-101: LGTM!

README.md (1)

63-63: 🎯 Functional Correctness

No change needed. Both SDKs provide a wavehouse-codegen CLI, and the summary sentence correctly scopes schema codegen to both clients.

Comment thread clients/go/cmd/wavehouse-codegen/main.go
Comment thread clients/go/cmd/wavehouse-codegen/main.go
Comment thread clients/go/cmd/wavehouse-codegen/main.go Outdated
Comment thread clients/go/cmd/wavehouse-codegen/main.go Outdated
Comment thread clients/go/cmd/wavehouse-codegen/main.go
Comment thread clients/go/wavehouse.go
Comment thread tests/conformance/conformance_ts.mjs Outdated
Comment thread tests/conformance/conformance_ts.mjs Outdated
Comment thread tests/conformance/conformance_ts.mjs
Comment thread tests/conformance/conformance_ts.mjs
@github-project-automation github-project-automation Bot moved this from Backlog to In review in WaveHouse Task Board Aug 5, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

Comment thread clients/go/example_test.go Outdated
Comment thread docs/src/content/docs/sdk/go/admin.md
Comment thread docs/src/content/docs/sdk/go/index.md Outdated
Comment thread docs/src/content/docs/sdk/go/index.md
Comment thread docs/src/content/docs/sdk/go/queries.md Outdated
Comment thread docs/src/content/docs/sdk/go/reference.md Outdated
Comment thread docs/src/content/docs/sdk/index.mdx Outdated
Comment thread Makefile
Comment thread Makefile Outdated
Comment thread Makefile Outdated
Source: extract helpers (errAborted, aggDefault, emptyInsertResult,
marshalNDJSON, dlq.stats, sortedKeys), collapse like/not_like, inline
trimTrailingSlashes, one-line IsRetryable, map-based numeric type lookup.

Tests: table-driven parseErrorResponse, loop namespace nil checks,
merge DLQ List+Table subcases, skipIfUnauthorized helper.

Docs: dedupe Quick Start + Error Handling in index.md (link to README
and reference.md), drop codegen type table from README (link to docs).
Copilot AI review requested due to automatic review settings August 5, 2026 21:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 41 changed files in this pull request and generated no new comments.

Suppressed comments (2)

clients/go/query_builder_test.go:28

  • captureQueryBody reads the request body with a single Read into a fixed 32KiB buffer, which can truncate JSON (and make tests flaky) if the encoded query ever grows beyond one read. It’s safer to decode the whole JSON body.
	wrapper := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		raw := make([]byte, 32*1024)
		n, _ := r.Body.Read(raw)
		body = raw[:n]
		handler.ServeHTTP(w, r)

clients/go/query_builder.go:288

  • Pagination cursor extraction coerces typed-row fields through map[string]any via json.Unmarshal, which turns numbers into float64 (and can lose integer precision). That value is then sent back as a filter in the next request, which can fail schema validation or page incorrectly for numeric cursor columns.
	// Extract the last row's value for the cursor column.
	lastRow := any(prevRows[len(prevRows)-1])
	m, ok := lastRow.(map[string]any)
	if !ok {
		// ponytail: marshal/unmarshal round-trip to get a map — optimize with reflect if perf matters.

jfwoods added 5 commits August 7, 2026 12:59
…indings

CI blockers:
- Bump google.golang.org/grpc 1.82.1, golang.org/x/text 0.39.0,
  klauspost/compress 1.18.7 (GO-2026-6061/-5970/-5841) — vulncheck green
- Drop forbidden trailing slash in sdk/go/index.md anchor link

SDK behavior (CodeRabbit/Copilot review):
- Retry: 429 now retryable, Retry-After honored for 429 and clamped to 30s,
  ±20% backoff jitter, dead 503 clause removed
- NewClient uses a fresh http.Client instead of mutable http.DefaultClient
- LiveQueryHandle: close state applied synchronously in Close(); dedup bound
  is the max backfilled timestamp compared as parsed time.Time
- Stream reconnect backoff resets after a connection reaches live
- Pagination replaces the cursor filter instead of stacking one per page
- PolicyFilter operators marshal with omitempty (absent, never null)
- Errors wrapped with operation context at every SDK boundary
- codegen: unknown args rejected, 30s HTTP timeout, wrapped errors,
  field/type collision detection, WAVEHOUSE_AUTH env var for the token,
  Int64/UInt64 map to string (ClickHouse quotes 64-bit ints in JSON output)

Tests:
- httptest servers closed via t.Cleanup; handler captures synchronized;
  captureQueryBody uses io.ReadAll; multi-scenario tables use t.Run subtests
- e2e: probe timeout, deterministic table pick returning its schema,
  polling instead of fixed sleeps, errors.As, unsupported-type skip
- conformance: SDK errors logged, arg guards, normalizePath compares decoded
  query values (Go + TS), TS harness counts unhandled endpoints as skipped,
  stubs aligned to real server shapes, deterministic exit, cacheTTL fixture

Docs/Makefile: policyDraft defined in admin example, (T, error) claim scoped
to request-response ops, operator-key path documented for admin SQL,
test-go-sdk-e2e documented, test-all includes test-go-sdk,
--allow-parallel-runners on SDK lint, HTTPS caution for bearer tokens.
… doc sync

Streaming correctness:
- Filtered stream no longer panics with send-on-closed-channel when Close()
  races in-flight inner deliveries: channel close and channel send now
  serialize under the controller mutex, and the wrapper unsubscribes from
  the inner stream before closing
- Events() channel is fed only once Events() has been called, so
  Subscribe-only consumers no longer overflow a channel nobody reads;
  the buffer-full drop is logged once, not per event
- Malformed-SSE log omits the payload (can carry tenant/PII fields)
- Unparseable Retry-After falls back to backoff(attempt), not the 30s max;
  parsing extracted to retryAfterDelay for testability

Tests (SDK coverage 33% → 80%+; streaming subsystem was 0%):
- stream_test.go: SSE lifecycle over httptest, filtered close-under-load
  regression test, Events()/Connected, full filter-engine tables
- live_query_test.go: backfill buffering, desc-order dedup bound, fetch
  error, no-callbacks-after-Close
- http_test.go: retryAfterDelay table + live 429 Retry-After flow
- Remaining unclosed httptest servers in table_test/http_test now use
  t.Cleanup

Build plumbing:
- test-go-sdk runs with -race; test/lint/fix aggregates now include the
  nested clients/go module; mangled test-go-sdk comment restored
- New test-conformance-ts target runs the TS conformance runner (was wired
  to nothing); CI unit job runs it

Docs:
- development.md synced: suites/targets tables, CI unit job, project
  structure with clients/, Go SDK in the dev-loop section
- reference.md: real codegen output (EventId not EventID; no bare int),
  initialism note, missing type-mapping rows (SimpleAggregateFunction,
  Time/Time64, Boolean, BFloat16), two-runner conformance wording, 401
  row corrected (missing token → 403) here and in sdk/reference.md
- Go SDK added alongside TS on the landing page, getting-started, and
  why-wavehouse comparison tables
…gen pointers, precision

SDK behavior:
- Non-retryable SSE connect errors (401/403/404) are now terminal: connect
  surfaces the parsed API error, run emits it and closes the stream instead
  of reconnecting forever; Connected() unblocks with "stream closed"
- codegen: defaulted columns generate pointer fields (*T + omitempty) — the
  Go spelling of the TS codegen's `field?: T` — so an explicit zero value
  is sent instead of silently dropped in favor of the server default
- Typed-row pagination cursor decodes with json.Number, keeping int64
  cursor values past 2^53 exact

Tests:
- Pagination: page.Next walked across three pages asserting exactly one
  replaced cursor filter with the right op/value, desc → lt, quiet end when
  the projection omits the order column, int64 precision regression test
- Terminal 403 stream test: error surfaced, StatusClosed, Connected fails
- e2e: buildMarkerRow returns the column it used (markerColumn could pick a
  defaulted column the row never wrote); http_test raw-body via io.ReadAll
- conformance_ts exits non-zero when nothing ran or any case was skipped

Docs/Makefile:
- Error-model claim scoped: HTTP-exchange errors are *wavehouse.Error;
  pre-request failures (auth provider, marshal) are plain wrapped errors —
  examples gain the else branch (go/index, go/reference, README)
- codegen README example uses WAVEHOUSE_AUTH; reference.md documents the
  pointer rule and sample output
- queries.md: real aggregation signatures/alias defaults, pagination
  example gains the OrderBy it needs
- development.md: coverage sentence scoped to instrumented suites, "four
  suites" count dropped, Releasing the SDKs covers the Go module
- access-control.mdx + pipes.mdx list the Go SDK method equivalents
- make ci runs test-conformance-ts (parity with the CI unit job)
…rray(UInt8), codegen tests

- Untyped-path cursor precision: acknowledged as a documented ceiling rather
  than claimed fixed — FetchUntyped rows are float64-decoded before
  pagination sees them (same 2^53 ceiling as the TS SDK's JS numbers), so
  the code comment now says exactly that, queries.md documents the caveat
  next to the pagination example, and a regression test pins the behavior;
  FetchTyped and codegen structs remain exact
- codegen: Array(UInt8) no longer generates []uint8 ([]byte, which
  encoding/json base64-encodes and the server rejects) — widened to
  []uint16; new main_test.go covers chTypeToGo (incl. this case),
  pascalCase's digit guard, findTopLevelComma, pointer-default output, and
  both collision failures (codegen package was 0% covered)
- Docs: streaming.md documents terminal non-retryable stream errors,
  SSE_ERROR row added to both SDK reference tables, make test/fmt/ci
  descriptions synced, internal/stream added to the project tree, SQL
  example no longer redeclares rows :=, CONTRIBUTING gains the SDK-sync
  bullet (+ configuration.mdx path, also in SUPPORT.md), AGENTS.md drops
  the nonexistent "wavehouse-go" name for the real module path, landing/
  why-wavehouse name the Go module importably, 404 page links the Go SDK
…IKE compile, doc precision

- codegen: Int64/UInt64 map to int64/uint64 and 128/256-bit ints to
  json.Number (with a conditional encoding/json import) — generated structs
  target /v1/query and /v1/pipes/*, where the server scans ClickHouse values
  into Go types and re-marshals them as UNQUOTED numbers; the round-1
  string mapping only held for /v1/admin/query, which forwards ClickHouse's
  own quoted JSON (use map[string]any with SQL[Row] there). Decode
  round-trip test pins the wire shape; docs type table and parity paragraph
  updated, pagination caveat now notes 64-bit codegen columns decode exactly
- Filtered streams compile LIKE patterns once at construction — the
  process-global likeRegexCache sync.Map (unbounded, keyed on caller input)
  is gone, and per-event matching is a plain regex call
- Docs: /sdk/go Quick Start is compilable (package main + func main, like
  the README); client concurrency-safety documented; Events() first-call
  feeding note; SELECT * expansion claim scoped to column-restricted roles
  (both SDK pages); Array(UInt8) exception in the type table; codegen
  go run uses @latest so it works outside the repo; stale TableRef
  "NOT safe for mutations" comment corrected (it holds no mutable state)
- AGENTS.md: configuration.mdx path, both SDK readmes in the prose list
- CHANGELOG: Go SDK entry expanded to house style (module path, nested-
  module caveat, new targets, conformance wiring)
…se-split

The LIKE caution added last round was right about like and wrong about
not_like. internal/query/builder.go's filterToSQL has cases for
eq/neq/gt/gte/lt/lte/like/in only, so not_like falls through to default and
/v1/query rejects it with 400 — a live query filtering on it fails its
Initial callback rather than quietly disagreeing with itself. Both streaming
pages now scope the case-sensitivity claim to like and describe not_like
separately, matching what the two queries pages and api.md already said.

Also corrects "the Makefile uses go run" to go tool (Makefile:144-150 defines
every pinned tool as `go tool <name>`; the same page already said so thirty
lines earlier), and gives the TS selectAll() description the same
restricted-vs-unrestricted split this branch added everywhere else — an
unrestricted role gets a bare SELECT *, not an expansion.
# Conflicts:
#	docs/src/content/docs/sdk/reference.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/src/content/docs/development.md (1)

496-498: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use make test-unit for the root unit-test row.

Line 341 describes tests in internal/*/_test.go, but Line 496 defines make test as test-unit plus test-go-sdk. The table therefore runs the separately listed Go SDK suite for the root unit-test entry.

Proposed documentation fix
- | Unit tests | `internal/*/_test.go` | No | `make test` |
+ | Unit tests | `internal/*/_test.go` | No | `make test-unit` |

As per coding guidelines, “Keep documentation synchronized with code changes.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 73533654-680d-4551-9ffa-18c2728ddbde

📥 Commits

Reviewing files that changed from the base of the PR and between 51d8b45 and 4ee4f1f.

📒 Files selected for processing (4)
  • docs/src/content/docs/development.md
  • docs/src/content/docs/sdk/go/streaming.md
  • docs/src/content/docs/sdk/queries.md
  • docs/src/content/docs/sdk/streaming.md
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (go)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Keep documentation synchronized with code changes, including API, configuration, architecture, deployment, development, and changelog updates as applicable.

Files:

  • docs/src/content/docs/sdk/queries.md
  • docs/src/content/docs/sdk/streaming.md
  • docs/src/content/docs/sdk/go/streaming.md
  • docs/src/content/docs/development.md
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Run make ci locally before every push, using the documented background execution method, and do not use CI as the first feedback loop.
Never force-push or rebase PR branches; merge origin/main instead.
Do not hand-write review markers or bypass hooks with --no-verify; run or formally skip every required pre-push reviewer and resolve all findings.

Files:

  • docs/src/content/docs/sdk/queries.md
  • docs/src/content/docs/sdk/streaming.md
  • docs/src/content/docs/sdk/go/streaming.md
  • docs/src/content/docs/development.md
🧠 Learnings (1)
📚 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/sdk/queries.md
  • docs/src/content/docs/sdk/streaming.md
  • docs/src/content/docs/sdk/go/streaming.md
  • docs/src/content/docs/development.md
🪛 LanguageTool
docs/src/content/docs/sdk/queries.md

[style] ~129-~129: Consider using the typographical ellipsis character here instead.
Context: ...et SELECT *). Mutually exclusive with .select(...) and with aggregations (.count(), `....

(ELLIPSIS)

🔇 Additional comments (6)
docs/src/content/docs/sdk/streaming.md (1)

2-2: LGTM!

Also applies to: 157-170

docs/src/content/docs/development.md (1)

191-199: LGTM!

Also applies to: 291-305, 318-333, 342-345, 359-360, 370-370, 382-382, 392-392, 432-440, 484-493, 499-505, 519-519, 552-556, 596-596

docs/src/content/docs/sdk/go/streaming.md (2)

210-283: LGTM!

Also applies to: 285-296, 298-305


1-18: 📐 Maintainability & Code Quality

No documentation changes required. The Go streaming documentation matches the implementation.

docs/src/content/docs/sdk/queries.md (2)

2-2: LGTM!

Also applies to: 89-89, 129-129


165-174: 🗄️ Data Integrity & Integration

Verify the aggregation allowlist against backend validation.

clients/go/query_builder_test.go confirms only the wire serialization of uniqExact. It does not prove that the backend accepts the documented case-insensitive allowlist or returns 400 unsupported aggregation function for unsupported names.

… to Go

Resolving the sdk/reference.md conflict by taking main's page wholesale was
too blunt. Main's SSE rows and their explanatory paragraph are correct and
stay — the branch's "the stream reconnects automatically" was wrong for the
TS SDK, which never re-dials a stream itself. But the same resolution also
reverted two unrelated branch edits on that page:

- The 401 row went back to "Missing or invalid JWT". internal/auth/auth.go
  returns no auth error for an empty token, so a missing token resolves to
  default_role and is denied 403; only a present-but-invalid or expired one
  yields 401. api.md and the Go reference page still said so, so the page
  contradicted both.
- The title lost its "TypeScript SDK" prefix, leaving the only unqualified
  "SDK ..." title in a two-SDK tree — it reads as the shared reference in
  breadcrumbs and search results when it is TS-only.

Main's new path-prefix guidance is TypeScript-only, which understates it now
that the Go SDK is co-canonical and preserves a prefix on both transports
(wavehouse.go trims the trailing slash; http.go and stream.go concatenate).
reverse-proxy.mdx now shows both clients and scopes the "upgrade your SDK"
caution to TypeScript, since the Go client never had #428. The Go README
gains the sentence its TypeScript counterpart got in the merge.
The merge documented path-prefix support as a promise on the Go SDK's Config
row and README, but nothing tested it — every test server in clients/go was
root-hosted. The TS side shipped url.test.ts and friends pinning exactly this
after #428. Adds a guard per transport, since the SSE URL is built in
stream.go independently of buildURL: a mux serving only the prefixed path, so
a dropped prefix 404s (REST) or never arrives (SSE). Both verified failing
against a deliberately broken buildURL and stream URL before being kept.

Qualifies the 401 row on both reference pages: "denied with 403" was too
absolute. A missing token is evaluated as default_role, which may well
succeed — the point is that it never yields 401. Also trims the changelog's
claim that this branch added SSE_ERROR/SSE_CONNECT_ERROR to the TS error
table; post-merge those rows come from #448 in the same Unreleased section.
…inters

Triage of the 24 open review threads surfaced one real defect. retryAfterDelay
computed time.Duration(secs) * time.Second before any range check, which wraps
negative past ~9.2e9 seconds — and because the wrap goes negative, the
min(delay, maxRetryAfter) clamp below selected it rather than capping. The
retry timer then fired immediately, so a server answering 429/503 with a large
Retry-After got hammered with zero-delay retries: the exact inverse of the
header's purpose, and a silent breach of the documented 30s cap. Verified:
"10000000000" yielded -2346317h47m53s, MaxInt64 yielded -1s. The HTTP-date
branch is unaffected — time.Sub saturates rather than wrapping.

emptyInsertResult aliased one &z across Total/Succeeded/Failed/Duplicates, so
a caller writing through any one of those exported *int fields mutated all
four. Separate vars now.

fetchNextTyped discarded its json.Marshal error, truncating a result set to
look like normal end-of-pagination. Propagated. The Decode error stays
deliberate — a Row marshaling to a non-object ends pagination the same way an
absent cursor column does — now commented as such, with the parity question
tracked in #452.

Test fixes for guards that could not fail:
- InsertByteSliceNotBatch asserted only the path and OK, both of which the
  batch path also satisfies; it now pins Content-Type and body, and was
  confirmed to fail with the []byte carve-out removed.
- backoff's CappedAt30s accepted 24-36s where the cap is applied after jitter
  and returns exactly 30s, so moving the cap before the jitter still passed.
- retryAfterDelay's HTTPDateFuture window subsumed the ~1s parse-failure
  fallback; tightened, and two overflow cases added.
- TestNewClient_HasNamespaces boxed typed pointers into map[string]any, where
  a nil typed pointer is never == nil — it could not fail. Compares concrete
  fields now.
- TestClient_From discarded Fetch's error, so an early return left its handler
  assertions unreached.

Docs: a pagination snippet that did not compile, three shell examples that
were bash syntax errors (unquoted <placeholder>), the cursor tie-breaker
caveat (#452), and the raw-SQL claim that a tokenless request is rejected —
untrue when default_role is the admin role, which AGENTS.md permits as
dev-only. Fixed on both SDKs' pages.
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 11, 2026
The guard added in 0a46251 had no test — the same gap that commit existed to
close. A Row that unmarshals cleanly but fails to marshal back (an exported
func field, absent from the response) previously produced an empty page,
indistinguishable from real exhaustion. Verified failing with the guard
swallowing the error instead of returning it.
The Go queries page documents two pagination footguns and names the TS SDK as
sharing both, but the TS page carried neither — so a data-loss caveat lived
only on the page TS readers don't open. Ports both: the strict single-column
cursor that drops rows tying the page boundary (#452, verified against
clients/ts/src/query-builder.ts), and the Number.MAX_SAFE_INTEGER ceiling on
integer cursors.

Also fills the not_like row's Backend column on the TS operator table — the
SDK does send a wire token (query-builder.ts maps not_like to "not_like"), and
a reader debugging the resulting 400 needs to know which token the server
rejected. The Go table already said so.

Two smaller corrections: reverse-proxy.mdx claimed the Go SDK "preserves the
prefix on every released version", asserting a property of a set that
development.md says is empty (no tagged Go releases yet); and the key-targets
table under-reported make lint / verify / fix, all three of which fan out to
markdown, prose, shell, workflow, and astro checks beyond what was listed.
The TypeScript codegen type table promised `number` for Decimal* columns, but
/v1/query returns them as quoted strings: transformRow in
internal/api/clickhouse_exec.go converts only UUID and time.Time, so a
shopspring decimal.Decimal reaches json.Marshal and marshals quoted. A TS user
with a Decimal price column gets a number-typed field holding "12.34" and
silently wrong arithmetic, with no type error. The Go page already documented
the real shape, which is what made the discrepancy visible. Footnoted both that
row and Array(UInt8) (base64 on the same path, #436); the codegen fix itself is
tracked in #453.

Also corrects the path-prefix sentence I got wrong in both directions: it now
says the prefix is preserved in every version `go get` can resolve, rather than
asserting a release history that doesn't exist yet.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CHANGELOG.md (1)

43-43: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the access-control documentation path.

Line 43 names docs/src/content/docs/access-control.md, but this PR uses docs/src/content/docs/access-control.mdx. Update the changelog reference.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6080abc9-b86a-422e-921a-0065022485f2

📥 Commits

Reviewing files that changed from the base of the PR and between 4ee4f1f and 6d29538.

📒 Files selected for processing (19)
  • CHANGELOG.md
  • clients/go/README.md
  • clients/go/client_test.go
  • clients/go/http.go
  • clients/go/http_test.go
  • clients/go/query_builder.go
  • clients/go/query_builder_test.go
  • clients/go/stream_test.go
  • clients/go/table.go
  • clients/go/table_test.go
  • docs/src/content/docs/api.md
  • docs/src/content/docs/development.md
  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/sdk/go/index.md
  • docs/src/content/docs/sdk/go/queries.md
  • docs/src/content/docs/sdk/go/reference.md
  • docs/src/content/docs/sdk/index.mdx
  • docs/src/content/docs/sdk/queries.md
  • docs/src/content/docs/sdk/reference.md
📜 Review details
⏰ Context from checks skipped due to timeout. (7)
  • GitHub Check: Coverage
  • GitHub Check: Docs build
  • GitHub Check: E2E tests
  • GitHub Check: Integration tests
  • GitHub Check: Unit tests
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (go)
🧰 Additional context used
📓 Path-based instructions (8)
docs/src/content/docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Keep documentation synchronized with code changes, including endpoint, configuration, architecture, ingest-format, deployment, and development-process changes.

Files:

  • docs/src/content/docs/api.md
  • docs/src/content/docs/sdk/index.mdx
  • docs/src/content/docs/sdk/go/reference.md
  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/sdk/go/queries.md
  • docs/src/content/docs/sdk/go/index.md
  • docs/src/content/docs/sdk/reference.md
  • docs/src/content/docs/sdk/queries.md
  • docs/src/content/docs/development.md
docs/src/content/docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

Author Mermaid diagrams vertically by default, avoid large side-by-side diagrams, and keep labels short enough to remain legible in the documentation column.

Files:

  • docs/src/content/docs/api.md
  • docs/src/content/docs/sdk/go/reference.md
  • docs/src/content/docs/sdk/go/queries.md
  • docs/src/content/docs/sdk/go/index.md
  • docs/src/content/docs/sdk/reference.md
  • docs/src/content/docs/sdk/queries.md
  • docs/src/content/docs/development.md
**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Comment the why rather than restating what the code does; keep comments concise and consistent with surrounding code.

Files:

  • docs/src/content/docs/api.md
  • CHANGELOG.md
  • docs/src/content/docs/sdk/index.mdx
  • docs/src/content/docs/sdk/go/reference.md
  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/sdk/go/queries.md
  • clients/go/README.md
  • docs/src/content/docs/sdk/go/index.md
  • docs/src/content/docs/sdk/reference.md
  • docs/src/content/docs/sdk/queries.md
  • docs/src/content/docs/development.md
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Before every push, validate locally with the documented make ci workflow; do not use CI as the first feedback loop.
Never force-push or rebase PR branches; merge origin/main instead. Do not hand-write review markers or bypass hooks with --no-verify.
Every review finding requires a substantive response, a fix or linked tracking issue, and thread resolution before merge.
Agent-created pull requests must be drafts and use a Conventional Commits title of at most 72 characters, with a lowercase-first subject and no trailing period.

Files:

  • docs/src/content/docs/api.md
  • CHANGELOG.md
  • docs/src/content/docs/sdk/index.mdx
  • docs/src/content/docs/sdk/go/reference.md
  • docs/src/content/docs/reverse-proxy.mdx
  • clients/go/http.go
  • docs/src/content/docs/sdk/go/queries.md
  • clients/go/client_test.go
  • clients/go/README.md
  • clients/go/table_test.go
  • docs/src/content/docs/sdk/go/index.md
  • clients/go/table.go
  • docs/src/content/docs/sdk/reference.md
  • clients/go/stream_test.go
  • docs/src/content/docs/sdk/queries.md
  • clients/go/http_test.go
  • clients/go/query_builder.go
  • clients/go/query_builder_test.go
  • docs/src/content/docs/development.md
CHANGELOG.md

📄 CodeRabbit inference engine (AGENTS.md)

Every notable code or user-visible change must update the [Unreleased] section of CHANGELOG.md in the same PR.

Files:

  • CHANGELOG.md
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Use Go 1.26, format code with gofumpt, return wrapped errors instead of panicking, use structured log/slog logging, and pass dependencies explicitly rather than using global state.
New Go code should target at least 80% project coverage overall and add tests for new functions; run make lint and make test before completion.

Files:

  • clients/go/http.go
  • clients/go/client_test.go
  • clients/go/table_test.go
  • clients/go/table.go
  • clients/go/stream_test.go
  • clients/go/http_test.go
  • clients/go/query_builder.go
  • clients/go/query_builder_test.go
clients/go/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Maintain the Go SDK as a canonical, zero-runtime-dependency client with API-tree parity, typed query builders, SSE/live-query support, and code generation.

Files:

  • clients/go/http.go
  • clients/go/client_test.go
  • clients/go/table_test.go
  • clients/go/table.go
  • clients/go/stream_test.go
  • clients/go/http_test.go
  • clients/go/query_builder.go
  • clients/go/query_builder_test.go
**/*_test.go

📄 CodeRabbit inference engine (AGENTS.md)

Use table-driven tests with t.Run, shared mocks and test helpers from internal/testutil, and provide corresponding tests for every new function.

Files:

  • clients/go/client_test.go
  • clients/go/table_test.go
  • clients/go/stream_test.go
  • clients/go/http_test.go
  • clients/go/query_builder_test.go
🧠 Learnings (6)
📚 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/api.md
  • CHANGELOG.md
  • docs/src/content/docs/sdk/go/reference.md
  • docs/src/content/docs/sdk/go/queries.md
  • clients/go/README.md
  • docs/src/content/docs/sdk/go/index.md
  • docs/src/content/docs/sdk/reference.md
  • docs/src/content/docs/sdk/queries.md
  • docs/src/content/docs/development.md
📚 Learning: 2026-08-11T21:55:32.845Z
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:32.845Z
Learning: Go SDK documentation should accurately distinguish the query APIs: `Client.From(table)` returns a `*TableRef`, whose `Fetch(ctx)` method fetches all columns as untyped rows. `QueryBuilder.FetchUntyped(ctx)` is available only on a `*QueryBuilder`, such as one returned by `TableRef.Select(...)` or `TableRef.SelectAll()`.

Applied to files:

  • docs/src/content/docs/sdk/go/reference.md
  • docs/src/content/docs/sdk/go/queries.md
  • docs/src/content/docs/sdk/go/index.md
📚 Learning: 2026-07-07T12:38:12.052Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 378
File: internal/auth/auth.go:119-132
Timestamp: 2026-07-07T12:38:12.052Z
Learning: In this repo, do not add or recommend logging/tracing client IP addresses using naive or untrusted sources (e.g., `r.RemoteAddr` or directly trusting/deriving `X-Forwarded-For`) anywhere in the Go codebase. `middleware.RealIP` was removed due to IP-spoofing risks, and proper trusted-proxy-aware client-IP handling is intentionally deferred to issue `#333`. During code review, if proposed changes would record client IPs (including in audit paths such as `internal/auth/auth.go`), reject/redirect until `#333` lands with correct trusted-proxy configuration and safeguards.

Applied to files:

  • clients/go/http.go
  • clients/go/client_test.go
  • clients/go/table_test.go
  • clients/go/table.go
  • clients/go/stream_test.go
  • clients/go/http_test.go
  • clients/go/query_builder.go
  • clients/go/query_builder_test.go
📚 Learning: 2026-08-11T21:55:53.726Z
Learnt from: jfwoods
Repo: Wave-RF/WaveHouse PR: 434
File: clients/go/stream_test.go:159-176
Timestamp: 2026-08-11T21:55:53.726Z
Learning: For Go files in this repository, do not report direct type assertions solely because the `forcetypeassert` rule is commented out in `.golangci.yml`. Only flag a type assertion when there is an independent correctness, safety, or maintainability issue.

Applied to files:

  • clients/go/http.go
  • clients/go/client_test.go
  • clients/go/table_test.go
  • clients/go/table.go
  • clients/go/stream_test.go
  • clients/go/http_test.go
  • clients/go/query_builder.go
  • clients/go/query_builder_test.go
📚 Learning: 2026-06-26T12:23:22.696Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 346
File: internal/stream/subscriber_test.go:9-28
Timestamp: 2026-06-26T12:23:22.696Z
Learning: In this Go repository, prefer table-driven tests (e.g., `[]struct{...}` with `t.Run(...)`) only for tests that cover multiple scenarios/inputs and can be cleanly enumerated. Do not artificially rewrite a clear single-scenario sequential behavioral-flow test into a table-driven form just to fit the pattern; if there’s only one meaningful scenario, keep the test as a straightforward linear flow (as in `TestSubscriber_SendDeliversThenDropsWhenFull`).

Applied to files:

  • clients/go/client_test.go
  • clients/go/table_test.go
  • clients/go/stream_test.go
  • clients/go/http_test.go
  • clients/go/query_builder_test.go
📚 Learning: 2026-08-11T21:55:39.391Z
Learnt from: jfwoods
Repo: Wave-RF/WaveHouse PR: 434
File: clients/go/cmd/wavehouse-codegen/main_test.go:55-59
Timestamp: 2026-08-11T21:55:39.391Z
Learning: In Go SDK tests, do not require named t.Run subtests for table-driven test loops when assertion errors already identify the failing input and expected and actual values. Avoid raising style-only findings to add t.Run in these cases.

Applied to files:

  • clients/go/client_test.go
  • clients/go/table_test.go
  • clients/go/stream_test.go
  • clients/go/http_test.go
  • clients/go/query_builder_test.go
🪛 LanguageTool
docs/src/content/docs/sdk/index.mdx

[style] ~349-~349: Consider using a more formal/concise alternative here.
Context: ...ix, for a WaveHouse reachable somewhere other than the root of an origin — behind a backen...

(OTHER_THAN)

docs/src/content/docs/reverse-proxy.mdx

[style] ~40-~40: Since ownership is already implied, this phrasing may be redundant.
Context: ... the proxy's job, and the server having its own opinion about it is a second place for ...

(PRP_OWN)

docs/src/content/docs/sdk/reference.md

[style] ~40-~40: A comma is missing here.
Context: ...RROR| Yes | Stream failed to connect (e.g. a non-absolutebaseURL) | | 0 | SSE_...

(EG_NO_COMMA)

🔇 Additional comments (18)
CHANGELOG.md (1)

39-42: LGTM!

Also applies to: 48-79

clients/go/README.md (1)

83-83: LGTM!

Also applies to: 198-203

clients/go/client_test.go (1)

40-56: LGTM!

Also applies to: 70-74

clients/go/http_test.go (1)

213-322: LGTM!

clients/go/table.go (1)

122-125: LGTM!

docs/src/content/docs/sdk/go/queries.md (1)

322-330: LGTM!

Also applies to: 362-367

docs/src/content/docs/sdk/index.mdx (1)

337-359: LGTM!

Also applies to: 382-395

docs/src/content/docs/sdk/queries.md (1)

9-11: LGTM!

Also applies to: 91-91, 131-131, 155-155, 167-175, 258-260

docs/src/content/docs/sdk/reference.md (1)

28-43: LGTM!

Also applies to: 139-143

clients/go/http.go (1)

176-182: 🎯 Functional Correctness

No 32-bit target is supported by the release matrix.

GoReleaser targets only amd64 and arm64, so this strconv.Atoi behavior does not affect a supported target.

			> Likely an incorrect or invalid review comment.
clients/go/query_builder.go (2)

287-300: LGTM!


7-7: 📐 Maintainability & Code Quality

Keep the Go SDK compatibility floor at Go 1.24.

The root module and CI use Go 1.26.5. clients/go/go.mod intentionally declares Go 1.24 as its compatibility floor, not its CI toolchain.

			> Likely an incorrect or invalid review comment.
clients/go/table_test.go (1)

129-162: LGTM!

docs/src/content/docs/api.md (1)

26-26: LGTM!

docs/src/content/docs/development.md (1)

342-345: LGTM!

Also applies to: 369-370, 382-382, 392-392, 486-489, 519-519, 554-556

docs/src/content/docs/sdk/go/reference.md (1)

57-57: LGTM!

Also applies to: 150-150, 266-266

docs/src/content/docs/reverse-proxy.mdx (1)

38-96: LGTM!

Also applies to: 147-147

docs/src/content/docs/sdk/go/index.md (1)

88-88: LGTM!

Comment thread clients/go/http_test.go
Comment on lines +464 to +481
func TestQueryBuilder_Pagination_UnmarshalableRowErrors(t *testing.T) {
type row struct {
ID string `json:"id"`
Cb func() `json:"cb"`
}
c, _ := pagingServer(t, [][]map[string]any{{{"id": "a"}, {"id": "b"}}})
page, err := FetchTyped[row](context.Background(),
c.From("clicks").Select("id").OrderBy("id", "asc").Limit(2))
if err != nil {
t.Fatal(err)
}
if page.Next == nil {
t.Fatal("want a Next cursor")
}
if _, err := page.Next(context.Background()); err == nil {
t.Fatal("want a marshal error, got a silently empty page")
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use table-driven subtests for the new Go tests.

Both new test functions use one linear case. The repository rule requires table-driven tests with t.Run.

  • clients/go/query_builder_test.go#L464-L481: wrap the marshal-error case in a table-driven t.Run test.
  • clients/go/stream_test.go#L436-L468: wrap the BaseURL-prefix case in a table-driven t.Run test.

As per coding guidelines: "**/*_test.go: Use table-driven tests with t.Run".

📍 Affects 2 files
  • clients/go/query_builder_test.go#L464-L481 (this comment)
  • clients/go/stream_test.go#L436-L468

Source: Coding guidelines

Comment thread docs/src/content/docs/sdk/go/queries.md Outdated
EricAndrechek added a commit that referenced this pull request Aug 19, 2026
)

## Summary

Release-readiness pass ahead of #149, plus #431 and #475. The
through-line: **every component now derives its version from the tag it
was built at**, so cutting a release is one tag and nothing else. That
isn't just convenience — the `main` ruleset forbids direct pushes, so a
version-bump commit would have needed its own reviewed PR before *every*
SDK release.

### Two bugs that would have fired on the first tag

- GoReleaser's `release.prerelease` defaults to a flat `false`, so a
`v0.1.0-alpha.1` tag would have published as a **stable** GitHub Release
and taken the "Latest release" badge. Now `auto`.
- `dockers_v2` tagged `:latest` unconditionally on any `v*` tag, so a
future `v1.3.0-rc.1` would have moved `ghcr.io/wave-rf/wavehouse:latest`
off the shipped `v1.2.0`. Each build now gets one immutable ref plus one
*channel* pointer (`:latest` stable, `:alpha`/`:beta`/`:rc`/`:next`
prerelease, `:dev` on main), resolved by `scripts/ci/release-channel.sh`
— shared with the npm dist-tag selection so `:rc` and `@rc` can't drift
apart.

### Release notes could come out empty

Two tag families share this repo, and both release paths asked GitHub to
generate notes without saying which tag to diff from. GitHub's default
is the previous *release*, so cutting the SDK tag on the same commit as
an already-published server release yields a **completely empty**
changelog. The server side is fixed at the root with `git.ignore_tags:
["clients/*"]`, so GoReleaser's own detection is family-aware and no
workflow has to pass an override; the SDK side passes
`--notes-start-tag`, resolved by walking git history from the tag's
parent rather than version-sorting (sorting picked a tag *ahead* of the
one being released). The SDK release step also no longer fails outright
when a release already exists — it previously ran a bare `gh release
create`, which reddens a run whose npm publish had already succeeded.

Release bodies now come from GitHub's own per-PR generator
(`changelog.use: github-native`) grouped by `.github/release.yml`,
rather than raw commit subjects.

### Tag naming: `clients/<lang>/vX.Y.Z`

`sdk-v*` becomes `clients/ts/v*`. Not cosmetic — Go resolves a
subdirectory module only against a tag carrying that subdirectory as a
prefix ([go.dev/ref/mod](https://go.dev/ref/mod)), so the incoming Go
SDK (#434, whose `clients/go/go.mod` declares
`github.com/Wave-RF/WaveHouse/clients/go`) *must* be
`clients/go/vX.Y.Z`. Every client follows that shape rather than leaving
Go as the exception. Done now because it was free exactly once: no
`sdk-v*` tag had ever been created. The `release tag protection` ruleset
was updated to `refs/tags/v*` + `refs/tags/clients/*/v*`.

### Fixes

- **#431** — `triage.yml` retired. GitHub Models was [retired
2026-07-30](https://github.blog/changelog/2026-07-30-github-models-is-now-retired/);
the endpoint returns `410` unconditionally and the workflow had failed
on **every** issue event since. The issue's suggested
`continue-on-error` + fallback was written while it still looked like
the announced brownout; with the service gone it would mean always
taking the fallback. Triage is manual now. `.github/board-config.env`
went with it (no other consumer), and `PROJECT_BOARD_TOKEN` is now
unused.
- **#475** — dev versions move from `0.0.0-dev.h<hash>` to
`0.0.1-dev.<utc-stamp>.h<hash>`. semver compares alphanumeric prerelease
identifiers *lexically*, so a content hash made the channel's order
arbitrary with respect to time — and since `npm i …@dev` records a
**range**, resolution walked backwards to a two-month-old build. A
numeric identifier compares numerically; the `0.0.1` base outranks the
permanently-published legacy versions and keeps the channel isolated
from any release range.
- **`-X main.BuildTime` has never worked** — the variable was
initialized to `time.Now()`, and `-X` silently no-ops on a non-constant
initializer, so `/version` reported process start time as build time.
- **`go install …@vX.Y.Z` reported `version=dev`** — added a
`debug.ReadBuildInfo()` fallback, gated so ldflags always win.

### Release-notes taxonomy

`changelog.use: github-native` hands the release body to GitHub's per-PR
generator, grouped by a new `.github/release.yml`. Getting that grouping
right took several passes and the reasoning is recorded in the file,
because the failure mode is silent — a wrong-looking changelog, not a
red build:

- **Dependabot is separated by author, not by label.** `github_actions`
is path-based (labeler maps `.github/workflows/**`), so it marks our own
CI PRs too — excluding the *label* filed all our CI work under
Dependencies, and dropping it instead empties that category, since
Dependabot's action bumps carry `github_actions` and not `dependencies`.
- **`documentation` needed the same exclude**, because the npm
Dependabot config points at the workspace root, so every npm bump
touches `docs/package.json`.
- **`🔧 CI & build` sits above `📚 Documentation`**, or it never matches:
AGENTS.md requires a doc update with every change, so 10 of the last 12
human `github_actions` PRs also carry `documentation`. It's a trade — a
few docs PRs that touch a workflow now file under CI — and both sides
are counted in the file.

The invariant, recorded inline: *any category keyed on a label a
Dependabot PR can carry needs the author exclude* — labeler's path
labels **and** the ecosystem labels Dependabot applies itself
(`javascript` is in neither `labeler.yml` nor our categories).

This routing can't be linted locally, but it can be dry-run against real
history from the branch — `configuration_file_path` resolves relative to
`target_commitish`:

```bash
gh api -X POST repos/Wave-RF/WaveHouse/releases/generate-notes \
  -f tag_name=v0.1.0 -f target_commitish=ci-tagger \
  -f configuration_file_path=.github/release.yml --jq .body
```

Run on this config: 23/23 merged Dependabot PRs in 📦 Dependencies, zero
leaking into 🔧 CI & build or 📚 Documentation.

### Tooling and housekeeping

- `make release-server` / `release-sdk-ts` / `release-sdk-go` →
`scripts/release.sh`, which preflights (on main, clean tree, synced with
origin, tag free both sides, `CI` green on that exact commit), prints
what will publish, and prompts. `DRY_RUN=1` stops after the plan.
- `release.yml` verifies its own attestations before finishing, so a
release publishing unverifiable provenance goes red.
- Lowercase artifact names (`wavehouse_linux_amd64.tar.gz`), `.zip` on
Windows, archives no longer bundle the 324 KB CHANGELOG.
- `CHANGELOG.md` merged from dozens of duplicate `###` bands under one
`## Unreleased` into 5 canonical sections and stamped `## [0.1.0]`.
Verified content-preserving — every entry byte-identical, checked by
diffing the sorted multiset of non-heading lines. (No count is quoted:
the base moves with every merge, and a stale figure beside a "verified"
claim is worse than none.)

## Test plan

- [x] `make ci` green
- [x] All four goreleaser tag modes rendered through a real registry
push (stable → `:v1.2.3`+`:latest`; prerelease →
`:v0.1.0-alpha.1`+`:alpha`; dev → `:dev-<sha>`+`:dev`; env-unset → safe
`:latest` fallback)
- [x] #475 reproduced and fixed against real `node-semver` using the ten
actually-published versions — old scheme resolves `^0.0.0-dev.h04a…` to
the June 8 build; new scheme resolves forward and is unsatisfiable by
`^0.1.0`
- [x] Full 8-target build matrix (4 GOOS × 2 GOARCH), archives +
checksums, Windows `.zip`
- [x] Version stamping verified across all four build paths: `go build`
in-VCS, `-ldflags`, no-VCS, and `make build`
- [x] `scripts/release.sh` exercised end-to-end in a scratch repo — real
tag creation and push, re-run refusal, abort at prompt, dirty tree,
wrong branch, bad version, missing `VERSION`
- [x] Container image smoke-tested (`wavehouse health` → clean exit 1,
labels/user/healthcheck correct)
- [x] Ten rounds of the pre-push gate (`pre-push-reviewer` +
`docs-reviewer`), both `ship_it` at zero findings
- [x] Release-notes routing dry-run against real history — all 23 merged
Dependabot PRs land in 📦 Dependencies, none leak into 🔧 CI & build (41)
or 📚 Documentation (54). `generate-notes` accepts
`configuration_file_path` resolved from `target_commitish`, so this
validates from the branch *before* merge
- [ ] Post-merge: first real tag exercises the release path end-to-end

## Notes for review

- **The `## [0.1.0] - 2026-08-19` stamp is a judgement call.** This
branch promotes `## Unreleased`, which its own new docs prescribe as a
separate pre-tag PR. The date is the *intended* cut date and the
`[0.1.0]` link 404s until the tag exists. If tagging slips, correct the
date and move anything merged in between up from `## Unreleased` — or
say the word and I'll split it out.
- **`PROJECT_BOARD_TOKEN` should be deleted.** Retiring `triage.yml`
left it with no consumers — an unused PAT with Projects scope on a
public repo. A branch can't remove a repo secret, so `gh secret delete
PROJECT_BOARD_TOKEN` plus revoking the PAT is a manual step. The docs
now say so rather than claiming it's already gone.
- **`:latest` and the bare CDN URL don't resolve yet.**
`ghcr.io/wave-rf/wavehouse:latest` 404s and npm's `latest` still points
at the June 4 bootstrap `0.0.0-dev.0f8826c`. Both self-heal the moment a
**stable** `v0.1.0` / `clients/ts/v0.1.0` ships — which is why the first
release should not be a prerelease, despite #149's title.
- **Deferred**: adding `/wavehouse` to `.gitignore` (the pre-push
reviewer's `go build ./cmd/...` drops a binary there that voids the
`make ci` marker), and a post-release smoke test that installs the
*published* SDK against the *published* image.

## Related Issues

Closes #431
Closes #475
Advances #149

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_015bVwHtNakQgmnBhMcfW8pe

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
jfwoods added 10 commits August 21, 2026 14:52
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).
Three catch-up changes, all client-side. The server is untouched.

Routes: main merged every admin-gated endpoint under /v1/ops (#479) with
no aliases, so thirteen call sites were 404ing against a current server —
schema list/refresh, DLQ stats, raw SQL, policy get/put/validate, and
pipes CRUD, plus the codegen CLI's schema fetch. Rewrote them along with
the tests, the shared wire_cases.json fixture, and the Go SDK docs. The
fixture is replayed by both conformance runners, so the stale paths broke
the TypeScript half too; `make test-conformance-ts` is back to 45/45.

ClientOptions.Headers: the TypeScript SDK gained options.headers in #456
and Go had no equivalent. Headers now apply to every request the client
makes, REST and SSE alike — which is also how an operator sends the
server's non-JWT X-Operator-Key. The SDK's own headers are set afterwards
and win a collision; net/http canonicalizes names, so matching is
case-insensitive; the map is copied at construction so later mutation
can't reach into requests.

SSE robustness, mirroring main's fetch-based rewrite (#470). The Go SDK
already authenticated by header, so that part was never stale, but three
gaps were:

- A credentialed stream followed redirects. net/http drops Authorization
  on a cross-host hop while forwarding custom headers verbatim, so a
  redirect either downgraded the stream to default_role in silence or
  handed configured secrets to wherever it pointed. Now refused with a
  terminal SSE_REDIRECT. Uncredentialed streams still follow.
- A 200 with any content type was treated as an event stream, so an auth
  gateway's login page left the stream sitting in StatusLive delivering
  nothing. Now a terminal SSE_BAD_CONTENT_TYPE.
- Every failure collapsed into one retryable SSE_ERROR, and malformed
  frames came back as a bare fmt.Errorf, so errors.As and IsRetryable
  didn't work on them. Replaced with the taxonomy the TypeScript SDK
  uses — SSE_AUTH_ERROR, SSE_NETWORK_ERROR, SSE_CONNECT_ERROR,
  SSE_REDIRECT, SSE_BAD_CONTENT_TYPE, SSE_PARSE_ERROR, SSE_READ_ERROR —
  each with its own retryable flag, all delivered as *Error.

Also documents what main changed underneath the Go SDK without changing
its code: DateTime values arrive canonicalized to RFC 3339 UTC (#402),
SSE applies policy row-filters per subscriber and fails closed (#381,
#457), /v1/stream is ungated so WaveHouse never 401s a stream, and
/v1/ops/dlq/stats is absent (404) when the DLQ is disabled rather than
returning empty stats.

Tests: terminal-failure table (bad content type, missing content type,
credentialed redirect, non-HTTP scheme), redirect-followed-when-
uncredentialed, typed retryable parse errors, and header precedence and
copying on both transports.
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.
biome.json's files.includes covered clients/ts, tests/e2e/sdk, docs, and
scripts, but not tests/conformance — so the 311-line conformance_ts.mjs
this branch adds was invisible to make lint-ts / fmt-ts / fix-ts.
Including it surfaced one formatting fix, applied here.
WH001's autofix deliberately never runs over .mdx, so this aside — added
on this branch before the rule landed — had to be joined by hand. Text
unchanged.
The gomod entry was `directory: /`, which covers the root module only —
Dependabot does not descend into nested modules, so clients/go/go.mod was
untracked. Converted to `directories:` (the form the github-actions entry
already uses for the same reason) so both share one schedule, group, and
commit prefix rather than forking the policy into a second block.

clients/go is stdlib-only today, so this catches the first dependency it
takes on rather than closing a gap that already exists — the same
argument the github-actions comment block makes about composite actions.
area/sdk globbed clients/ts/** and tests/e2e/sdk/** only, so every file in
the new Go SDK and the shared conformance runner went unlabeled. Adds
clients/go/** and tests/conformance/**.

Also broadens the dependencies label from root-anchored go.mod/go.sum to
**/go.mod and **/go.sum, so the nested clients/go module's manifests get
labeled the way clients/ts/package.json already does via **/package.json.
Left area/infra root-anchored — the TypeScript SDK's manifest doesn't
carry that label either.

Verified by replaying the config's globs over the branch's diff:
clients/go sources land on area/sdk + go, the codegen CLI on area/sdk,
and clients/go/go.mod on area/sdk + dependencies.

Note this takes effect from the next PR, not this one: housekeeping.yml
resolves labeler.yml from the default branch, not the PR head.
test-go-sdk ran a bare `go test -race ./...` — no profile, no threshold —
so the Go SDK would have shipped as the only component in the repo with no
coverage gate while its TypeScript peer has three.

It now collects covdata the same way the root-module Go suites do and
renders through scripts/cov, with a `go-sdk` suite gated at 75% against a
measured 82.7% (clients/go 88.6%, cmd/wavehouse-codegen 55.8%). That ratio
sits inside the band the existing floors already use — unit is 80 against
~91% actual — leaving headroom without being slack enough to rot.

Gated standalone rather than merged into the Go total: the total feeds
threshold.total and the README badge, and folding a shipped client library
into the server's project-wide number would move that badge for unrelated
reasons. The same separation the TypeScript SDK gets via ts-*. Being a
nested module, clients/go is invisible to the root -coverpkg anyway, so it
cannot leak into the total and needs no exclude.paths entry — verified:
zero clients/go rows in the merged profile, Go total unmoved at 81.2%.

One wrinkle worth recording: `go tool cover -html` resolves a profile's
package paths through the module in the working directory, so rendering
from the repo root fails to find the nested packages. renderHTML now runs
with cmd.Dir set to clients/go and absolute paths.

CI needed no functional change — the unit job already uploads the whole
tmp/coverage tree, so the fragment arrives and the coverage job gates it.
The client-side filter behind .Stream() compared every string operand
lexically. That was defensible before #402; it isn't now. The server
canonicalizes every top-level DateTime/DateTime64 value to RFC 3339 UTC
before publishing, so a payload reads 2026-06-21T04:00:00Z while a
caller's filter constant may name the same instant as
2026-06-21T06:00:00+02:00 — and lexically the payload sorts BELOW the
constant, so OpGte withheld a row that was chronologically equal, and
OpEq called two spellings of one instant different.

Both sides now parse as instants, mirroring what internal/policy's
row filter does for a DateTime column.

Deliberately narrow: only RFC 3339 with an explicit offset or Z counts.
A zone-less spelling names an instant only relative to the column's
declared timezone, which the server reads from the schema and a stream
subscriber does not have — reading it as UTC would move the instant, so
those fall through rather than being silently reinterpreted. A ','
fraction is ISO 8601 but not ClickHouse, and is refused for the same
reason the ingest grammar refuses it. The operand length is pre-gated at
64 bytes like the server's, so a megabyte 'timestamp' isn't scanned once
per filter per event.

Ordering an instant against a non-instant now fails closed instead of
falling back to text, which could admit rows the query path excludes —
the same direction the server errs in. The usual trigger is a zone-less
constant, which now yields no rows rather than wrong ones.

Also: a column missing from the payload no longer matches the literal
string "<nil>" through the fmt.Sprint equality fallback.

Not fixed here, and worth its own change: the TypeScript SDK's
matchesFilters compares the same way, and its compareOrdered comment
still asserts that lexicographic order 'is correct for ISO-8601
timestamps' — true only while both sides share an offset spelling, which
#402 stopped guaranteeing. Its unknown-operator branch also returns true
where Go and the server fail closed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (12)
clients/go/http.go (1)

96-105: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Apply the same credentialed-redirect refusal to REST requests.

doRequest uses hctx.httpClient with the default redirect policy. net/http strips Authorization on a cross-host hop, but it forwards custom headers verbatim. The ClientOptions.Headers documentation names "a gateway credential" as an intended use, so a redirect to another host sends that credential to the redirect target.

connect in clients/go/stream.go (lines 347-361) already refuses redirects when the request carries a credential, and its comment states the same net/http behavior. The REST transport lacks that protection, so the two transports enforce different credential-scope rules for the same configured headers.

Copy the client and set CheckRedirect when configured headers or an Authorization header are present, as the stream path does. If following redirects on REST is required, strip the configured headers on a cross-host hop instead.

🔒 Proposed fix
+	client := hctx.httpClient
+	if authHeader != "" || len(hctx.headers) > 0 {
+		// Same rule as the SSE path: never carry a credential across a
+		// redirect. net/http drops Authorization on a cross-host hop but
+		// forwards configured headers verbatim. Copy so a caller-supplied
+		// client keeps its own CheckRedirect elsewhere.
+		c := *client
+		c.CheckRedirect = func(*http.Request, []*http.Request) error {
+			return http.ErrUseLastResponse
+		}
+		client = &c
+	}
+
 	var lastErr error
 	maxAttempts := hctx.maxRetries + 1

Then use client.Do(req) in place of hctx.httpClient.Do(req) at line 107.

Note that TestConfiguredHeadersOnRESTRequests and TestConfiguredHeadersAreCopied never exercise a redirect, so neither test discriminates this case.

Source: Linters/SAST tools

clients/go/schema.go (1)

14-20: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Decode both supported schema response shapes.

SchemaNamespace.List decodes /v1/ops/schema directly into []TableSchema. The fetchSchemas implementation in clients/go/cmd/wavehouse-codegen/main.go supports both an array and a name-keyed object. A map response therefore fails before this method builds Schemas. Add dual-shape decoding and a test for the object response. clients/go/namespaces_test.go currently covers only the array form.

Proposed fix
-	var raw []TableSchema
+	var payload json.RawMessage
 	if err := doRequest(ctx, s.ctx, requestOptions{
 		method: "GET",
 		path:   "/v1/ops/schema",
-	}, &raw); err != nil {
+	}, &payload); err != nil {
 		return nil, fmt.Errorf("list schemas: %w", err)
 	}
+
+	var raw []TableSchema
+	if err := json.Unmarshal(payload, &raw); err == nil {
+		schemas := make(Schemas, len(raw))
+		for _, t := range raw {
+			schemas[t.Name] = t
+		}
+		return schemas, nil
+	}
+
+	var schemas Schemas
+	if err := json.Unmarshal(payload, &schemas); err != nil {
+		return nil, fmt.Errorf("decode schemas: %w", err)
+	}
+	return schemas, nil

Source: Linters/SAST tools

clients/go/cmd/wavehouse-codegen/main.go (3)

31-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tests for the remaining CLI helpers and failure paths. main_test.go covers chTypeToGo, pascalCase, findTopLevelComma, and generate, but not flagValue, parseArgs, fetchSchemas, sortedKeys, or os.Exit paths. Add these tests. Update table-driven tests to include name and call t.Run(tt.name, ...).

Source: Coding guidelines


258-281: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Check the first rune before validating the identifier.

result[0] reads the first UTF-8 byte. For "٢fa_events", the check misses the leading digit, and go/format rejects the generated identifier. Use the first rune and add a table-driven test for a non-ASCII digit.


352-390: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Enforce 0600 when overwriting the output file.

os.WriteFile preserves the permissions of an existing file. Apply os.Chmod(args.out, 0o600) after a successful write, or replace the file through a 0600 temporary file before reporting success.

docs/src/content/docs/sdk/index.mdx (2)

361-370: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Keep the Cloudflare Access secret out of browser examples.

This section also documents browser and CDN usage. A reader can copy this example into frontend code and bundle process.env.CF_ACCESS_CLIENT_SECRET. That exposes the service-token credential. Cloudflare documents this credential for automated systems, not browser bundles. (developers.cloudflare.com)

Mark this example as server-side only. Direct browser clients through a trusted BFF or proxy that injects the headers.

Source: MCP tools


441-441: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Require an origin allowlist before following credentialed redirects.

options.fetch receives the configured headers, and non-Authorization headers can cross an unexpected redirect target. Require an explicit trusted-origin check before overriding redirect, or remove this recommendation.

Source: MCP tools

docs/src/content/docs/reverse-proxy.mdx (1)

237-238: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Qualify the NDJSON batch-size claim.

This nginx setting caps the complete NDJSON request at 16 MiB. WaveHouse also applies a 16 MiB request cap and a 10 MiB per-line cap. State these limits so “unbounded batch” does not imply unlimited uploads.

Source: MCP tools

docs/src/content/docs/access-control.mdx (1)

92-92: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Keep privileged credentials out of process arguments.

  • In docs/src/content/docs/access-control.mdx#L92, $(cat operator.key) places the operator key in curl’s argv. Use a protected curl --config file or stdin.
  • In docs/src/content/docs/sdk/reference.md#L136, the TypeScript CLI currently accepts credentials only through --auth/-a. Add a non-argv input path and make it the primary documented method. Update the options table and CLI help.
docs/src/content/docs/index.mdx (1)

189-194: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use cloudCta frontmatter for this CTA.

This MDX page renders <CloudCta> directly. The repository rule requires Cloud CTA opt-in through cloudCta frontmatter, not through a component import. Move this CTA configuration into frontmatter and remove the component usage.

As per coding guidelines: docs/src/content/docs/**/*.mdx must opt a page into the Cloud CTA with cloudCta frontmatter, not by importing the component.

Source: Coding guidelines

docs/src/content/docs/deployment.md (1)

159-162: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Complete the conditional configuration comment.

Line 159 currently starts with WH_POLICY_FILE_PATH is set, which is not a complete condition. Change it to If WH_POLICY_FILE_PATH is set so the boot-failure requirement is unambiguous.

Proposed wording fix
-# WH_POLICY_FILE_PATH is set, the file MUST exist, parse, and pass policy
+# If WH_POLICY_FILE_PATH is set, the file MUST exist, parse, and pass policy
docs/src/content/docs/development.md (1)

480-481: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the duplicate SDK tree entry.

The project tree already documents clients/ with both ts/ and go/ at Lines 470-473. Lines 480-481 add a second clients/ section and omit the Go SDK, so the documented tree contradicts itself.

Proposed tree cleanup
-├── clients/                # Client SDKs
-│   └── ts/                 # TypeScript SDK (`@wavehouse/sdk`, pnpm workspace)

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 040aefe8-4d1f-4226-9dbb-01d1a99c59c6

📥 Commits

Reviewing files that changed from the base of the PR and between 6d29538 and 73edcbe.

📒 Files selected for processing (53)
  • .claude/commands/cover.md
  • .github/dependabot.yml
  • .github/labeler.yml
  • .github/workflows/ci.yml
  • .testcoverage.yml
  • AGENTS.md
  • CHANGELOG.md
  • CONTRIBUTING.md
  • Makefile
  • README.md
  • biome.json
  • clients/go/README.md
  • clients/go/client_test.go
  • clients/go/cmd/wavehouse-codegen/main.go
  • clients/go/conformance_test.go
  • clients/go/dlq.go
  • clients/go/http.go
  • clients/go/http_test.go
  • clients/go/namespaces_test.go
  • clients/go/pipes.go
  • clients/go/policy.go
  • clients/go/schema.go
  • clients/go/stream.go
  • clients/go/stream_test.go
  • clients/go/table.go
  • clients/go/testdata/wire_cases.json
  • clients/go/wavehouse.go
  • docs/src/content/docs/404.md
  • docs/src/content/docs/access-control.mdx
  • docs/src/content/docs/api.md
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/claude-code.md
  • docs/src/content/docs/deployment.md
  • docs/src/content/docs/development.md
  • docs/src/content/docs/getting-started.md
  • docs/src/content/docs/index.mdx
  • docs/src/content/docs/pipes.mdx
  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/sdk/admin.md
  • docs/src/content/docs/sdk/go/admin.md
  • docs/src/content/docs/sdk/go/index.md
  • docs/src/content/docs/sdk/go/pipes.md
  • docs/src/content/docs/sdk/go/queries.md
  • docs/src/content/docs/sdk/go/reference.md
  • docs/src/content/docs/sdk/go/streaming.md
  • docs/src/content/docs/sdk/index.mdx
  • docs/src/content/docs/sdk/pipes.md
  • docs/src/content/docs/sdk/queries.md
  • docs/src/content/docs/sdk/reference.md
  • docs/src/content/docs/sdk/streaming.md
  • docs/src/content/docs/why-wavehouse.md
  • scripts/cov/main.go
  • tests/conformance/conformance_ts.mjs
💤 Files with no reviewable changes (1)
  • CHANGELOG.md

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. (1)
  • GitHub Check: Coverage
🧰 Additional context used
📓 Path-based instructions (6)
**/*.md

📄 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/sdk/admin.md
  • docs/src/content/docs/404.md
  • docs/src/content/docs/getting-started.md
  • docs/src/content/docs/claude-code.md
  • docs/src/content/docs/sdk/streaming.md
  • CONTRIBUTING.md
  • docs/src/content/docs/sdk/go/admin.md
  • docs/src/content/docs/deployment.md
  • docs/src/content/docs/sdk/pipes.md
  • docs/src/content/docs/sdk/go/reference.md
  • docs/src/content/docs/api.md
  • docs/src/content/docs/why-wavehouse.md
  • clients/go/README.md
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/sdk/queries.md
  • README.md
  • docs/src/content/docs/sdk/go/queries.md
  • docs/src/content/docs/sdk/go/index.md
  • AGENTS.md
  • docs/src/content/docs/sdk/reference.md
  • docs/src/content/docs/development.md
  • docs/src/content/docs/sdk/go/streaming.md
  • docs/src/content/docs/sdk/go/pipes.md
docs/src/content/docs/**/*.{md,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/sdk/admin.md
  • docs/src/content/docs/404.md
  • docs/src/content/docs/index.mdx
  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/getting-started.md
  • docs/src/content/docs/claude-code.md
  • docs/src/content/docs/sdk/index.mdx
  • docs/src/content/docs/sdk/streaming.md
  • docs/src/content/docs/sdk/go/admin.md
  • docs/src/content/docs/deployment.md
  • docs/src/content/docs/sdk/pipes.md
  • docs/src/content/docs/sdk/go/reference.md
  • docs/src/content/docs/api.md
  • docs/src/content/docs/why-wavehouse.md
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/access-control.mdx
  • docs/src/content/docs/pipes.mdx
  • docs/src/content/docs/sdk/queries.md
  • docs/src/content/docs/sdk/go/queries.md
  • docs/src/content/docs/sdk/go/index.md
  • docs/src/content/docs/sdk/reference.md
  • docs/src/content/docs/development.md
  • docs/src/content/docs/sdk/go/streaming.md
  • docs/src/content/docs/sdk/go/pipes.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/index.mdx
  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/sdk/index.mdx
  • docs/src/content/docs/access-control.mdx
  • docs/src/content/docs/pipes.mdx
docs/src/content/docs/**/*.mdx

📄 CodeRabbit inference engine (AGENTS.md)

docs/src/content/docs/**/*.mdx: - Opt a page into the Cloud CTA with cloudCta frontmatter, not by importing the component.

  • Never hand-write ® or in prose.

Files:

  • docs/src/content/docs/index.mdx
  • docs/src/content/docs/reverse-proxy.mdx
  • docs/src/content/docs/sdk/index.mdx
  • docs/src/content/docs/access-control.mdx
  • docs/src/content/docs/pipes.mdx
**/*.{go,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

  • No global state: Dependencies are passed explicitly (constructor injection).

Files:

  • clients/go/schema.go
  • clients/go/dlq.go
  • clients/go/cmd/wavehouse-codegen/main.go
  • clients/go/namespaces_test.go
  • clients/go/policy.go
  • clients/go/pipes.go
  • clients/go/table.go
  • clients/go/http.go
  • clients/go/http_test.go
  • clients/go/wavehouse.go
  • clients/go/client_test.go
  • clients/go/conformance_test.go
  • scripts/cov/main.go
  • clients/go/stream_test.go
  • clients/go/stream.go
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: - Table-driven tests: Use tests := []struct{ name string; ... } with t.Run(tt.name, ...) for test cases.

  • Every new function should have corresponding test cases. Run make lint and make test before considering work complete.

Files:

  • clients/go/schema.go
  • clients/go/dlq.go
  • clients/go/cmd/wavehouse-codegen/main.go
  • clients/go/namespaces_test.go
  • clients/go/policy.go
  • clients/go/pipes.go
  • clients/go/table.go
  • clients/go/http.go
  • clients/go/http_test.go
  • clients/go/wavehouse.go
  • clients/go/client_test.go
  • clients/go/conformance_test.go
  • scripts/cov/main.go
  • clients/go/stream_test.go
  • clients/go/stream.go
🧠 Learnings (4)
📚 Learning: 2026-08-20T15:42:29.972Z
Learnt from: jfwoods
Repo: Wave-RF/WaveHouse PR: 505
File: docs/src/content/docs/404.md:14-19
Timestamp: 2026-08-20T15:42:29.972Z
Learning: For Markdown files under docs/src/content/docs, do not flag multi-line HTML comment interiors as violations of the one-paragraph-per-line rule. Markdownlint masks HTML comment contents, and joining those lines during autofix could write masked text back into the file; preserve the comment formatting instead.

Applied to files:

  • docs/src/content/docs/404.md
📚 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
  • docs/src/content/docs/sdk/index.mdx
  • docs/src/content/docs/access-control.mdx
  • docs/src/content/docs/pipes.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:

  • docs/src/content/docs/deployment.md
  • README.md
  • AGENTS.md
  • docs/src/content/docs/development.md
📚 Learning: 2026-06-26T12:23:22.696Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 346
File: internal/stream/subscriber_test.go:9-28
Timestamp: 2026-06-26T12:23:22.696Z
Learning: In this Go repository, prefer table-driven tests (e.g., `[]struct{...}` with `t.Run(...)`) only for tests that cover multiple scenarios/inputs and can be cleanly enumerated. Do not artificially rewrite a clear single-scenario sequential behavioral-flow test into a table-driven form just to fit the pattern; if there’s only one meaningful scenario, keep the test as a straightforward linear flow (as in `TestSubscriber_SendDeliversThenDropsWhenFull`).

Applied to files:

  • clients/go/http_test.go
  • clients/go/stream_test.go
🪛 ast-grep (0.45.1)
scripts/cov/main.go

[warning] 348-349: A log/format call (log.Print/Printf/Println, the Fatal/Panic variants, fmt.Sprintf, or a structured logger's Info/Warn/Error/Debug method) is given a message built by concatenating a string literal with a non-literal value such as request data. Unsanitized, attacker-controlled input written to logs enables log forging / CRLF injection: an attacker can inject newlines to spoof log entries or break log parsers. Do not concatenate raw input into the log message; pass it as a separate structured field/argument (e.g. 'log.Printf("user: %s", user)' or 'logger.Info("login", "user", user)') and strip or escape newline characters first.
Context: fmt.Printf(" %s%-13s%s %s %s(separate gate; not in merge above)%s\n",
cyan, s+":", reset, pct, yellow, reset)
Note: [CWE-117] Improper Output Neutralization for Logs.

(log-injection-request-data-concat-go)

🪛 Betterleaks (1.7.3)
docs/src/content/docs/development.md

[high] 212-214: Discovered a potential authorization token provided in a curl command header, which could compromise the curl accessed resource.

(curl-auth-header)

🪛 LanguageTool
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)

docs/src/content/docs/sdk/index.mdx

[style] ~347-~347: Consider using a more formal/concise alternative here.
Context: ...ix, for a WaveHouse reachable somewhere other than the root of an origin — behind a backen...

(OTHER_THAN)


[style] ~382-~382: 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] ~402-~402: 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] ~415-~415: 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] ~415-~415: 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] ~433-~433: 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] ~466-~466: 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] ~479-~479: 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] ~479-~479: Consider using the typographical ellipsis character here instead.
Context: ...the two aren't structurally assignable. { ...init, dispatcher } as never covers the ...

(ELLIPSIS)


[style] ~479-~479: 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/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)


[grammar] ~126-~126: Ensure spelling is correct
Context: ...nection's JWT claims. A stream on a row-policied table therefore delivers only the rows the policy admit...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[style] ~129-~129: ‘On top of that’ might be wordy. Consider a shorter alternative.
Context: ...ect. ### Client-Side Stream Filtering On top of that, when a QueryBuilder with .where() ...

(EN_WORDINESS_PREMIUM_ON_TOP_OF_THAT)


[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/deployment.md

[uncategorized] ~104-~104: The official name of this software platform is spelled with a capital “H”.
Context: ...tion — verify a downloaded archive with gh attestation verify <file> --repo Wave-RF/WaveHouse --signer-workflow Wave-RF/WaveHouse/.github/workflows/release.yml. (This covers th...

(GITHUB)


[typographical] ~376-~376: The word ‘When’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ...NATS stream under subjects dlq.{table}. This prevents infinite retry loops. Mon...

(WRB_QUESTION_MARK)

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)

docs/src/content/docs/sdk/go/reference.md

[style] ~25-~25: Since ownership is already implied, this phrasing may be redundant.
Context: ...he returned *StreamController manages its own context and goroutine, closed via `.Clo...

(PRP_OWN)


[grammar] ~31-~31: Use a hyphen to join words.
Context: ...hprovider, marshal failures) are plain wrapped errors; handle theerrors.As ==...

(QB_NEW_EN_HYPHEN)

docs/src/content/docs/api.md

[style] ~249-~249: Consider using the typographical ellipsis character here instead.
Context: ...n"}| Malformed request body | | 400 |{"error":"unknown column ... for table ..."}(also:missing requir...

(ELLIPSIS)


[style] ~249-~249: Consider using the typographical ellipsis character here instead.
Context: ...nown column ... for table ..."}(also:missing required column ..., type mismatch for column ..., null...

(ELLIPSIS)


[style] ~249-~249: Consider using the typographical ellipsis character here instead.
Context: ...(also:missing required column ..., type mismatch for column ..., null value for non-nullable column ....

(ELLIPSIS)


[style] ~249-~249: Consider using the typographical ellipsis character here instead.
Context: ...n ..., type mismatch for column ..., null value for non-nullable column ...`) | Schema validation failure (unknown ...

(ELLIPSIS)


[style] ~282-~282: Consider using a more formal/concise alternative here.
Context: ...edge cases] - Digit-strings of lengths other than 9–10 are ClickHouse's own forms — calen...

(OTHER_THAN)


[grammar] ~292-~292: Ensure spelling is correct
Context: ...nds (shorter runs it rejected outright, where best_effort reads "2026" as a year)...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[style] ~531-~531: Redundant conjunctions can lead to confusion; consider removing a conjunction here.
Context: ...meters can be supplied via query string and/or JSON body. Results are cached in the sh...

(AND_OR)

docs/src/content/docs/architecture.md

[style] ~75-~75: Since ownership is already implied, this phrasing may be redundant.
Context: ...e JWT/JWKS authentication middleware is its own package, [auth/](#auth--authenticatio...

(PRP_OWN)


[typographical] ~125-~125: Consider using an em dash in dialogues and enumerations.
Context: - worker.goStartIngestWorker lau...

(DASH_RULE)

docs/src/content/docs/access-control.mdx

[style] ~302-~302: Consider using “who” when you are referring to a person instead of an object.
Context: ...e rows are also invisible to the writer that produced them. (A Float column may in...

(THAT_WHO)


[typographical] ~414-~414: Consider using an em dash in dialogues and enumerations.
Context: - Numeric columns (Int*/UInt*/`Fl...

(DASH_RULE)


[typographical] ~461-~461: The word ‘When’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ...lidation**, or WaveHouse refuses to boot. That turns a typo, a missing mount, or ...

(WRB_QUESTION_MARK)


[grammar] ~461-~461: Ensure spelling is correct
Context: ...s to boot. That turns a typo, a missing mount, or a policy invalid under a newly-tigh...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[typographical] ~462-~462: The word ‘When’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ...s denied (logged loudly, admin included). Seed one via PUT /v1/ops/policy using...

(WRB_QUESTION_MARK)


[style] ~493-~493: Since ownership is already implied, this phrasing may be redundant.
Context: ...ous callers get nothing, viewer reads its own tenant's rows with sensitive columns ma...

(PRP_OWN)

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)

docs/src/content/docs/sdk/go/index.md

[style] ~9-~9: Since ownership is already implied, this phrasing may be redundant.
Context: ...ypeScript client (@wavehouse/sdk) has its own docs starting at SDK Overview —...

(PRP_OWN)


[locale-violation] ~115-~115: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...s copied at NewClient, so mutating it afterwards changes nothing. For the two remaining...

(AFTERWARDS_US)


[grammar] ~163-~163: Use a hyphen to join words.
Context: ...uthprovider, marshal errors) are plain wrapped errors; handle theerrors.As ==...

(QB_NEW_EN_HYPHEN)


[style] ~190-~190: Consider using the typographical ellipsis character here instead.
Context: ...Any slice batches.* Reflection allows []ClickRow{...} to use the same NDJSON batch path as ...

(ELLIPSIS)

AGENTS.md

[style] ~121-~121: Consider using the typographical ellipsis character here instead.
Context: ...ventions - Table-driven tests: Use tests := []struct{ name string; ... } with t.Run(tt.name, ...) for test ...

(ELLIPSIS)


[style] ~121-~121: Consider using the typographical ellipsis character here instead.
Context: ...s := []struct{ name string; ... }witht.Run(tt.name, ...)for test cases. - **Shared mocks in...

(ELLIPSIS)


[style] ~126-~126: Consider using the typographical ellipsis character here instead.
Context: ... without NATS. - Pipes helpers: Use pipes.NewMemoryStore(queries...) for in-memory pipes testing without N...

(ELLIPSIS)


[style] ~128-~128: Consider using the typographical ellipsis character here instead.
Context: ...module — invisible to the root module's -coverpkg=./..., so it is gated on its own `suites.go-...

(ELLIPSIS)


[uncategorized] ~128-~128: The official name of this software platform is spelled with a capital “H”.
Context: ... comments via GitHub Code Quality — see .github/workflows/README.md "Coverage publishi...

(GITHUB)


[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)


[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)

docs/src/content/docs/sdk/reference.md

[style] ~26-~26: Since ownership is already implied, this phrasing may be redundant.
Context: ... or .liveQuery(), described under If your own callback throws below. | Status | Cod...

(PRP_OWN)


[style] ~38-~38: A comma is missing here.
Context: ...OR| No | Stream could not be started (e.g. a non-absolutebaseURL) | | 0 | SSE_...

(EG_NO_COMMA)


[style] ~59-~59: Consider an alternative for the overused word “exactly”.
Context: ...roxy. That silent-downgrade behavior is exactly why auth is re-read on every connecti...

(EXACTLY_PRECISELY)


[style] ~63-~63: Since ownership is already implied, this phrasing may be redundant.
Context: ...cts rather than feeding it again. If your own callback throws. For anything deliver...

(PRP_OWN)


[style] ~63-~63: Since ownership is already implied, this phrasing may be redundant.
Context: ...r` callback, so a handler that swallows its own failures fails silently. **Wrap your h...

(PRP_OWN)

docs/src/content/docs/development.md

[style] ~327-~327: Consider using the typographical ellipsis character here instead.
Context: ... module, invisible to the root module's -coverpkg=./..., so its statements can never reach `tm...

(ELLIPSIS)


[style] ~380-~380: Since ownership is already implied, this phrasing may be redundant.
Context: .../`. The orchestrator always provisions its own stack — a fresh ClickHouse testcontaine...

(PRP_OWN)


[uncategorized] ~588-~588: The official name of this software platform is spelled with a capital “H”.
Context: ...ps:- **GitHub Actions** (root **and**/.github/actions/setup-env`) — outdated action v...

(GITHUB)


[uncategorized] ~588-~588: The official name of this software platform is spelled with a capital “H”.
Context: ...ons tracked against the SHA pins across .github/workflows/* and the setup-env compos...

(GITHUB)


[uncategorized] ~593-~593: The official name of this software platform is spelled with a capital “H”.
Context: ...o** directories. directory: / reaches .github/workflows/ but does not descend into `...

(GITHUB)


[uncategorized] ~593-~593: The official name of this software platform is spelled with a capital “H”.
Context: ...b/workflows/but does not descend into.github/actions/*/action.yml, so the setup-en...

(GITHUB)


[uncategorized] ~595-~595: The official name of this software platform is spelled with a capital “H”.
Context: ...de job down at once. See the comment in .github/dependabot.yml for the condition that ...

(GITHUB)


[typographical] ~609-~609: To join two clauses or introduce examples, consider using an em dash.
Context: ..., so open a PR renaming that heading to ## [X.Y.Z] - YYYY-MM-DD and adding the matching link...

(DASH_RULE)


[style] ~609-~609: ‘at the foot of’ might be wordy. Consider a shorter alternative.
Context: ... and adding the matching link reference at the foot of the file. Nothing in the release pipeli...

(EN_WORDINESS_PREMIUM_AT_THE_FOOT_OF)


[style] ~637-~637: Consider using the typographical ellipsis character here instead.
Context: ...f the ref name, so v* never matches a clients/... tag — which is what keeps a client rel...

(ELLIPSIS)


[typographical] ~641-~641: Consider using an em dash in dialogues and enumerations.
Context: - Server — a GitHub Release with ...

(DASH_RULE)


[uncategorized] ~641-~641: Do not mix variants of the same word (‘pre-release’ and ‘prerelease’) within a single text.
Context: ...v0.1.0-alpha.1) is marked as a GitHub pre-release, so it never takes the "Latest release"...

(EN_WORD_COHERENCY)


[uncategorized] ~642-~642: The official name of this software platform is spelled with a capital “H”.
Context: ... grouped into the categories defined in [.github/release.yml](https://github.com/Wave-R...

(GITHUB)


[uncategorized] ~642-~642: The official name of this software platform is spelled with a capital “H”.
Context: ...ease.yml). Grouping is by PR label: github_actions / documentation are applied ...

(GITHUB)


[style] ~642-~642: Since ownership is already implied, this phrasing may be redundant.
Context: ...github_actions, documentation` — mark our own PRs too; our CI work gets its own "CI &...

(PRP_OWN)


[uncategorized] ~642-~642: The official name of this software platform is spelled with a capital “H”.
Context: ...lf (dependencies, javascript, go, github_actions; javascript is in neither `l...

(GITHUB)


[style] ~644-~644: Since ownership is already implied, this phrasing may be redundant.
Context: .../beta/rc/next` (prerelease), plus its own GitHub Release. - Go SDK — nothing ...

(PRP_OWN)


[style] ~646-~646: Since ownership is already implied, this phrasing may be redundant.
Context: ...image in GHCR. The release job verifies its own attestations before finishing, so a rel...

(PRP_OWN)


[style] ~666-~666: To elevate your writing, consider using more formal language here.
Context: ...wo things the make targets do for you and the UI does not: none of the preflight ...

(AND_WHEREAS)

docs/src/content/docs/sdk/go/streaming.md

[typographical] ~161-~161: The word ‘Where’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: ...where (mapped to wire tokens eq/neq). OpLike/OpNotLike use SQL LIKE seman...

(WRB_QUESTION_MARK)

🔇 Additional comments (50)
clients/go/wavehouse.go (2)

44-53: LGTM!

Also applies to: 85-92, 107-107


147-147: 🗄️ Data Integrity & Integration

No route mismatch found. The server registers POST /v1/ops/query, and both SDKs use the same path.

clients/go/http.go (1)

32-39: LGTM!

Also applies to: 186-202, 204-210, 212-224

clients/go/table.go (1)

84-84: LGTM!

clients/go/stream.go (1)

10-10: LGTM!

Also applies to: 234-247, 249-272, 289-308, 320-327, 340-361, 363-390, 396-407, 458-467, 471-477, 493-498

clients/go/client_test.go (1)

79-80: LGTM!

clients/go/http_test.go (1)

116-116: LGTM!

Also applies to: 138-138, 202-202

clients/go/namespaces_test.go (1)

26-27: LGTM!

Also applies to: 135-135

clients/go/stream_test.go (1)

470-554: LGTM!

Also applies to: 556-581, 583-612, 614-654

clients/go/testdata/wire_cases.json (1)

457-457: LGTM!

Also applies to: 470-477, 503-510, 522-522, 541-541, 554-560, 575-575, 590-590

clients/go/conformance_test.go (1)

95-105: LGTM!

tests/conformance/conformance_ts.mjs (1)

25-34: LGTM!

Also applies to: 54-64, 310-311

clients/go/schema.go (1)

31-38: LGTM!

clients/go/policy.go (1)

9-23: LGTM!

Also applies to: 26-35, 38-48

clients/go/dlq.go (1)

10-18: LGTM!

Also applies to: 20-40, 42-45

clients/go/pipes.go (2)

10-23: LGTM!

Also applies to: 27-35, 39-47, 51-67, 69-92, 94-106


15-23: 📐 Maintainability & Code Quality

Run the required Go validation checks.

Confirm that every new Go function has corresponding test cases. Run make lint and make test before completion. Use table-driven tests with t.Run for test cases.

As per coding guidelines, every new Go function must have corresponding test cases, and make lint plus make test must run before completion.

Source: Coding guidelines

clients/go/cmd/wavehouse-codegen/main.go (5)

1-29: LGTM!


76-125: LGTM!


127-239: LGTM!


241-256: LGTM!


284-350: LGTM!

clients/go/README.md (1)

5-5: LGTM!

Also applies to: 83-83, 209-209, 221-221

docs/src/content/docs/sdk/index.mdx (1)

3-12: LGTM!

Also applies to: 14-342, 345-357, 375-439, 443-483, 485-532, 563-574

docs/src/content/docs/sdk/pipes.md (1)

2-2: LGTM!

Also applies to: 6-6, 8-43, 51-53, 76-84

docs/src/content/docs/sdk/queries.md (1)

6-6: LGTM!

Also applies to: 27-49, 58-65, 82-84, 122-124, 147-148, 150-165, 220-227, 247-249, 255-262

docs/src/content/docs/reverse-proxy.mdx (1)

4-5: LGTM!

Also applies to: 23-23, 44-85, 133-142, 178-178, 201-215, 272-272

docs/src/content/docs/sdk/admin.md (1)

2-2: LGTM!

Also applies to: 6-6, 23-25, 29-29, 61-72

docs/src/content/docs/sdk/go/admin.md (1)

6-23: LGTM!

Also applies to: 27-64, 68-82, 86-100

docs/src/content/docs/sdk/go/index.md (1)

6-9: LGTM!

Also applies to: 14-26, 61-61, 81-100, 103-138, 140-159, 163-190, 192-198

docs/src/content/docs/sdk/go/pipes.md (1)

6-10: LGTM!

Also applies to: 18-43, 51-53, 76-84

docs/src/content/docs/sdk/reference.md (1)

2-2: LGTM!

Also applies to: 6-6, 26-73, 92-93, 169-173, 187-187

docs/src/content/docs/sdk/streaming.md (2)

2-2: LGTM!

Also applies to: 6-6, 43-43, 68-81, 100-100, 102-146, 161-161, 163-226, 229-235


227-227: 🎯 Functional Correctness

No change needed. Both caution blocks already close with :::.

			> Likely an incorrect or invalid review comment.
docs/src/content/docs/why-wavehouse.md (1)

4-6: LGTM!

Also applies to: 160-160, 200-200

docs/src/content/docs/404.md (1)

16-19: LGTM!

Also applies to: 49-49

docs/src/content/docs/access-control.mdx (1)

4-5: LGTM!

Also applies to: 59-60, 62-63, 77-79, 104-105, 107-108, 122-123, 156-164, 178-179, 187-188, 191-194, 196-205, 220-221, 236-238, 242-243, 247-253, 257-260, 272-273, 294-303, 312-325, 364-375, 407-418, 439-466, 498-526, 528-567, 584-593

docs/src/content/docs/sdk/go/queries.md (1)

6-6: LGTM!

Also applies to: 8-18, 32-32, 34-65, 67-85, 104-118, 122-126, 132-168, 183-226, 228-301, 303-304, 307-325

docs/src/content/docs/sdk/go/reference.md (1)

6-6: LGTM!

Also applies to: 8-43, 53-68, 123-192, 194-209

docs/src/content/docs/sdk/go/streaming.md (1)

6-6: LGTM!

Also applies to: 8-14, 23-48, 50-133, 138-226, 229-235

README.md (1)

10-23: LGTM!

Also applies to: 35-92, 94-160, 164-164

docs/src/content/docs/api.md (1)

8-8: LGTM!

Also applies to: 26-34, 155-155, 161-183, 199-204, 225-230, 249-250, 269-305, 308-308, 370-370, 391-393, 406-406, 409-409, 457-457, 470-470, 498-498, 512-512, 531-531, 548-548, 593-600, 615-619, 642-642, 668-670, 694-704, 727-727, 767-767, 781-781, 821-821, 841-843

docs/src/content/docs/architecture.md (1)

4-5: LGTM!

Also applies to: 74-75, 84-84, 90-94, 118-126, 145-149, 158-158, 176-199, 211-249, 261-270

docs/src/content/docs/claude-code.md (1)

28-28: LGTM!

Also applies to: 50-52, 84-84, 212-212

docs/src/content/docs/deployment.md (1)

4-5: LGTM!

Also applies to: 86-100, 118-123, 134-136, 268-268, 281-281, 372-376

docs/src/content/docs/development.md (1)

21-22: LGTM!

Also applies to: 41-41, 123-130, 132-150, 154-154, 189-197, 212-212, 223-226, 289-291, 327-331, 341-343, 356-358, 368-369, 380-400, 402-402, 407-408, 435-446, 468-479, 510-510, 523-528, 535-549, 562-562, 588-595, 604-609, 611-613, 625-649, 653-663, 673-680, 682-706, 772-775

docs/src/content/docs/getting-started.md (1)

50-50: LGTM!

Also applies to: 76-79, 98-98, 108-109

docs/src/content/docs/index.mdx (1)

18-21: LGTM!

Also applies to: 57-57, 106-110, 114-160, 185-187, 219-223, 228-228

docs/src/content/docs/pipes.mdx (2)

124-151: LGTM!

Also applies to: 187-209, 225-231


122-122: 🔒 Security & Privacy

Remove this verification request.

PipesHandler.Execute checks allowed_roles before binding parameters or executing SQL. It does not evaluate table column or row policies. Existing execution tests cover role authorization.

			> Likely an incorrect or invalid review comment.

Comment thread AGENTS.md
- **Pipes helpers**: Use `pipes.NewMemoryStore(queries...)` for in-memory pipes testing without NATS.
- **Response assertions**: Use `testutil.AssertJSONResponse(t, rec, status, expected)` and `testutil.AssertJSONContains(t, rec, status, substring)`.
- **Coverage target**: 80% project-wide (CI enforces `threshold.total` in `.testcoverage.yml` against the merged unit + integration + e2e profile). Per-suite minima also enforced: unit 80%, integration 20%, e2e 60%, sdk 50%. Aim for 80%+ on new code. Coverage is published as a README badge (Go merged-total) and as PR comments via GitHub Code Quality — see `.github/workflows/README.md` "Coverage publishing"; the gate is unchanged.
- **Coverage target**: 80% project-wide (CI enforces `threshold.total` in `.testcoverage.yml` against the merged unit + integration + e2e profile). Per-suite minima also enforced: unit 80%, integration 20%, e2e 60%, go-sdk 75%, ts SDK 50%. The Go SDK (`clients/go`) is a nested module — invisible to the root module's `-coverpkg=./...`, so it is gated on its own `suites.go-sdk` floor and never merged into the project-wide total. Aim for 80%+ on new code. Coverage is published as a README badge (Go merged-total) and as PR comments via GitHub Code Quality — see `.github/workflows/README.md` "Coverage publishing"; the gate is unchanged.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

List each TypeScript coverage floor.

Line 128 states a 50% TypeScript SDK minimum. .testcoverage.yml sets ts-unit and ts-e2e to 40%, and ts-total to 50%. State all three thresholds so contributors do not treat the per-suite gates as informational.

🧰 Tools
🪛 LanguageTool

[style] ~128-~128: Consider using the typographical ellipsis character here instead.
Context: ...module — invisible to the root module's -coverpkg=./..., so it is gated on its own `suites.go-...

(ELLIPSIS)


[uncategorized] ~128-~128: The official name of this software platform is spelled with a capital “H”.
Context: ... comments via GitHub Code Quality — see .github/workflows/README.md "Coverage publishi...

(GITHUB)

Comment thread clients/go/http_test.go
Comment on lines +379 to +430
var got http.Header
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
got = r.Header.Clone()
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `[]`)
}))
defer srv.Close()

client := NewClient(Config{
BaseURL: srv.URL,
Auth: tc.auth,
HTTPClient: srv.Client(),
Options: &ClientOptions{Headers: tc.configured},
})
if _, err := client.Schema.List(context.Background()); err != nil {
t.Fatalf("schema list: %v", err)
}
if v := got.Values(tc.header); len(v) != 1 {
t.Fatalf("want exactly one %s header, got %v", tc.header, v)
}
if v := got.Get(tc.header); v != tc.want {
t.Fatalf("want %s: %q, got %q", tc.header, tc.want, v)
}
})
}
}

// TestConfiguredHeadersAreCopied: mutating the caller's map after NewClient
// must not change what later requests send.
func TestConfiguredHeadersAreCopied(t *testing.T) {
var got http.Header
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
got = r.Header.Clone()
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `[]`)
}))
defer srv.Close()

headers := map[string]string{"X-Tenant-Id": "acme"}
client := NewClient(Config{
BaseURL: srv.URL,
HTTPClient: srv.Client(),
Options: &ClientOptions{Headers: headers},
})
headers["X-Tenant-Id"] = "attacker"
delete(headers, "X-Tenant-Id")

if _, err := client.Schema.List(context.Background()); err != nil {
t.Fatalf("schema list: %v", err)
}
if v := got.Get("X-Tenant-Id"); v != "acme" {
t.Fatalf("want the value captured at construction, got %q", v)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Synchronize access to captured headers.

The handlers write got on server goroutines. The tests read got on test goroutines. The HTTP round trip does not create a race-detector synchronization edge for these variables. go test -race can fail for both tests.

Send the cloned header through a buffered channel, or guard both accesses with one mutex.

Proposed fix
-			var got http.Header
+			gotHeaders := make(chan http.Header, 1)
 			srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-				got = r.Header.Clone()
+				gotHeaders <- r.Header.Clone()
 				w.Header().Set("Content-Type", "application/json")
 				_, _ = io.WriteString(w, `[]`)
 			}))
@@
 			if _, err := client.Schema.List(context.Background()); err != nil {
 				t.Fatalf("schema list: %v", err)
 			}
+			got := <-gotHeaders

Apply the same pattern in TestConfiguredHeadersAreCopied.

📝 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.

Suggested change
var got http.Header
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
got = r.Header.Clone()
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `[]`)
}))
defer srv.Close()
client := NewClient(Config{
BaseURL: srv.URL,
Auth: tc.auth,
HTTPClient: srv.Client(),
Options: &ClientOptions{Headers: tc.configured},
})
if _, err := client.Schema.List(context.Background()); err != nil {
t.Fatalf("schema list: %v", err)
}
if v := got.Values(tc.header); len(v) != 1 {
t.Fatalf("want exactly one %s header, got %v", tc.header, v)
}
if v := got.Get(tc.header); v != tc.want {
t.Fatalf("want %s: %q, got %q", tc.header, tc.want, v)
}
})
}
}
// TestConfiguredHeadersAreCopied: mutating the caller's map after NewClient
// must not change what later requests send.
func TestConfiguredHeadersAreCopied(t *testing.T) {
var got http.Header
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
got = r.Header.Clone()
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `[]`)
}))
defer srv.Close()
headers := map[string]string{"X-Tenant-Id": "acme"}
client := NewClient(Config{
BaseURL: srv.URL,
HTTPClient: srv.Client(),
Options: &ClientOptions{Headers: headers},
})
headers["X-Tenant-Id"] = "attacker"
delete(headers, "X-Tenant-Id")
if _, err := client.Schema.List(context.Background()); err != nil {
t.Fatalf("schema list: %v", err)
}
if v := got.Get("X-Tenant-Id"); v != "acme" {
t.Fatalf("want the value captured at construction, got %q", v)
gotHeaders := make(chan http.Header, 1)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotHeaders <- r.Header.Clone()
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `[]`)
}))
defer srv.Close()
client := NewClient(Config{
BaseURL: srv.URL,
Auth: tc.auth,
HTTPClient: srv.Client(),
Options: &ClientOptions{Headers: tc.configured},
})
if _, err := client.Schema.List(context.Background()); err != nil {
t.Fatalf("schema list: %v", err)
}
got := <-gotHeaders
if v := got.Values(tc.header); len(v) != 1 {
t.Fatalf("want exactly one %s header, got %v", tc.header, v)
}
if v := got.Get(tc.header); v != tc.want {
t.Fatalf("want %s: %q, got %q", tc.header, tc.want, v)
}
})
}
}
// TestConfiguredHeadersAreCopied: mutating the caller's map after NewClient
// must not change what later requests send.
func TestConfiguredHeadersAreCopied(t *testing.T) {
gotHeaders := make(chan http.Header, 1)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotHeaders <- r.Header.Clone()
w.Header().Set("Content-Type", "application/json")
_, _ = io.WriteString(w, `[]`)
}))
defer srv.Close()
headers := map[string]string{"X-Tenant-Id": "acme"}
client := NewClient(Config{
BaseURL: srv.URL,
HTTPClient: srv.Client(),
Options: &ClientOptions{Headers: headers},
})
headers["X-Tenant-Id"] = "attacker"
delete(headers, "X-Tenant-Id")
if _, err := client.Schema.List(context.Background()); err != nil {
t.Fatalf("schema list: %v", err)
}
got := <-gotHeaders
if v := got.Get("X-Tenant-Id"); v != "acme" {
t.Fatalf("want the value captured at construction, got %q", v)
}

Comment thread docs/src/content/docs/sdk/go/reference.md
| 0 | `SSE_CONNECT_ERROR` | No | `BaseURL` is unparseable, or its scheme is not `http`/`https` — retrying cannot fix it |
| *3xx* | `SSE_REDIRECT` | No | The stream endpoint redirected while the request carried a credential, and the SDK refused to follow it |
| 200 | `SSE_BAD_CONTENT_TYPE` | No | A `200` that wasn't `text/event-stream` — something between you and WaveHouse answered (a captive portal, an auth gateway's login page) |
| 0 | `SSE_PARSE_ERROR` | Yes | A frame's JSON didn't decode; the frame is dropped and the stream continues |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(docs/src/content/docs/sdk/go/(reference|streaming)\.md$|.*\.(go|md|mdx)$)' | head -200

printf '%s\n' '--- relevant documentation ---'
sed -n '1,75p' docs/src/content/docs/sdk/go/reference.md
sed -n '105,155p' docs/src/content/docs/sdk/go/streaming.md

printf '%s\n' '--- parse/reconnect symbols ---'
rg -n -i 'SSE_PARSE_ERROR|SSE_READ_ERROR|SSE_AUTH_ERROR|SSE_CONNECT_ERROR|SSE_BAD_CONTENT_TYPE|reconnect|parse error|malformed|event stream' --glob '*.go' --glob '*.md' --glob '*.mdx' . | head -300

Repository: Wave-RF/WaveHouse

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- Go stream structure ---'
ast-grep outline clients/go/stream.go

printf '%s\n' '--- Go stream implementation ---'
sed -n '210,285p' clients/go/stream.go
sed -n '380,515p' clients/go/stream.go

printf '%s\n' '--- Go stream tests for malformed frames and reconnects ---'
sed -n '185,225p' clients/go/stream_test.go
sed -n '560,625p' clients/go/stream_test.go
sed -n '325,445p' clients/go/stream_test.go

printf '%s\n' '--- relevant Go error definitions and helpers ---'
sed -n '1,220p' clients/go/errors.go
rg -n 'IsRetryable|Retryable|SSE_PARSE_ERROR|parseSSE|bufio.Scanner|Split' clients/go --glob '*.go'

Repository: Wave-RF/WaveHouse

Length of output: 20747


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path

source = Path("clients/go/stream.go").read_text()

def body(name):
    marker = f"func (sc *StreamController) {name}("
    start = source.index(marker)
    brace = source.index("{", start)
    depth = 0
    for i in range(brace, len(source)):
        if source[i] == "{":
            depth += 1
        elif source[i] == "}":
            depth -= 1
            if depth == 0:
                return source[brace + 1:i]
    raise RuntimeError(f"unterminated {name}")

run = body("run")
handle = body("handleSSEData")

print("handleSSEData returns an error:", "error" in handle.split("{", 1)[0])
print("handleSSEData emits SSE_PARSE_ERROR:", 'Code:     "SSE_PARSE_ERROR"' in handle or 'Code:      "SSE_PARSE_ERROR"' in handle)
print("handleSSEData invokes reconnect control:", any(x in handle for x in ("connect(", "setStatus(StatusReconnecting)", "backoff(")))
print("run invokes handleSSEData:", "sc.handleSSEData(dataLine)" in run)
print("run reconnects after connect errors:", "if err != nil" in run and "sc.setStatus(StatusReconnecting)" in run)
print("run can observe a parse error as connect error:", "err := sc.handleSSEData" in run or "if err := sc.handleSSEData" in run)

for path in ("docs/src/content/docs/sdk/go/reference.md",
             "docs/src/content/docs/sdk/go/streaming.md"):
    lines = Path(path).read_text().splitlines()
    print(f"{path}:")
    for n, line in enumerate(lines, 1):
        if "SSE_PARSE_ERROR" in line or "Reconnect covers" in line:
            print(f"  {n}: {line}")
PY

Repository: Wave-RF/WaveHouse

Length of output: 1210


Document SSE_PARSE_ERROR as an in-place skip. Update the Go reference table to state that the error is reported, but the malformed frame is dropped without reconnecting. Remove SSE_PARSE_ERROR from the reconnect list in the Go streaming guide.

📍 Affects 2 files
  • docs/src/content/docs/sdk/go/reference.md#L49-L49 (this comment)
  • docs/src/content/docs/sdk/go/streaming.md#L134-L134

Comment thread scripts/cov/main.go
Comment on lines +73 to +84
var standaloneGoSuites = []string{"go-sdk"}

// suiteModuleDir maps a suite to the module directory its covdata was
// produced in, for suites that aren't the root module. `go tool cover
// -html` reads the source of every package named in the profile and
// resolves it through the module in the process's working directory, so a
// nested module's profile has to be rendered from inside that module —
// from the repo root the tool fails with "no required module provides
// package github.com/Wave-RF/WaveHouse/clients/go/...". `go tool covdata
// textfmt` has no such constraint (it only reads the covdata files), so
// only the HTML step needs the chdir.
var suiteModuleDir = map[string]string{"go-sdk": "clients/go"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Inject the coverage plan and command runner.

Lines 73 and 84 add mutable package-level state. Lines 254-267 and 997-1002 bind rendering to direct process execution. Pass an immutable coverage plan and a command runner dependency into the coverage functions. Add table-driven tests for root-module and nested-module rendering without invoking the real Go toolchain.

As per coding guidelines: “No global state: Dependencies are passed explicitly (constructor injection).” “Table-driven tests: Use tests := []struct{ name string; ... } with t.Run(tt.name, ...) for test cases.” “Every new function should have corresponding test cases.”

Also applies to: 254-268, 991-1002

Source: Coding guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Documentation, site/, README area/infra CI, build, deploy, Docker, release documentation Improvements or additions to documentation github_actions Pull requests that update GitHub Actions code go Pull requests that update go code

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

2 participants