Skip to content

feat(dedupe): per-table id_field/require_id overrides#377

Open
taitelee wants to merge 6 commits into
mainfrom
dedupe-per-table
Open

feat(dedupe): per-table id_field/require_id overrides#377
taitelee wants to merge 6 commits into
mainfrom
dedupe-per-table

Conversation

@taitelee

@taitelee taitelee commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

Dedupe's id_field and require_id were single, deployment-wide settings. This PR makes them per-table and fixes two dedupe keying bugs found along the way:

Related Issues

Closes #222
Closes #370

@taitelee
taitelee requested review from a team and EricAndrechek July 6, 2026 19:11
@github-actions github-actions Bot added documentation Improvements or additions to documentation go Pull requests that update go code area/observability Metrics, logs, traces, health, profiling area/api HTTP handlers, routing, middleware area/dedupe Deduplication (Pebble, ScyllaDB) area/docs Documentation, site/, README area/infra CI, build, deploy, Docker, release labels Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2850647c-b91b-4b5b-8f68-8db04c089615

📥 Commits

Reviewing files that changed from the base of the PR and between 70a9b91 and eda2f31.

📒 Files selected for processing (10)
  • AGENTS.md
  • CHANGELOG.md
  • cmd/wavehouse/main.go
  • config.yaml
  • docs/src/content/docs/api.md
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/configuration.mdx
  • internal/api/ingest_test.go
  • internal/config/config.go
  • tests/e2e/fixtures/config.yaml
📜 Recent review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Docs preview
  • GitHub Check: Coverage
  • GitHub Check: E2E tests
  • GitHub Check: Lint
⚠️ CI failures not shown inline (2)

GitHub Actions: PR housekeeping / PR housekeeping: feat(dedupe): per-table id_field/require_id overrides

Conclusion: failure

View job details

##[group]Run # Single source of truth for the rule: scripts/lint-pr-title.sh — the
 �[36;1m# Single source of truth for the rule: scripts/lint-pr-title.sh — the�[0m
 �[36;1m# SAME validator the local agent gate runs (.claude/hooks/agent-bash-gate.sh),�[0m
 �[36;1m# so CI and local can't drift. The checkout above is ref: main, so this is�[0m
 �[36;1m# always the default-branch script. Dependabot's grouped-update titles�[0m
 �[36;1m# routinely exceed the 72-char subject cap and the format isn't�[0m
 �[36;1m# configurable, so Dependabot PRs are exempt from the length check�[0m
 �[36;1m# (the format check still applies).�[0m
 �[36;1mif [[ "$PR_AUTHOR" == "dependabot[bot]" || "$PR_AUTHOR" == "app/dependabot" ]]; then�[0m
 �[36;1m  export PR_TITLE_SKIP_LENGTH=1�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif reason=$(bash scripts/lint-pr-title.sh "$PR_TITLE" 2>&1); then�[0m
 �[36;1m  echo "passed=true" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  echo "PR title OK: $PR_TITLE"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "passed=false" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  printf '%s\n' "$reason"�[0m
 �[36;1m  echo "::error::$(printf '%s' "$reason" | head -1)"�[0m

GitHub Actions: PR housekeeping / 0_PR housekeeping.txt: feat(dedupe): per-table id_field/require_id overrides

Conclusion: failure

View job details

##[group]Run # Single source of truth for the rule: scripts/lint-pr-title.sh — the
 �[36;1m# Single source of truth for the rule: scripts/lint-pr-title.sh — the�[0m
 �[36;1m# SAME validator the local agent gate runs (.claude/hooks/agent-bash-gate.sh),�[0m
 �[36;1m# so CI and local can't drift. The checkout above is ref: main, so this is�[0m
 �[36;1m# always the default-branch script. Dependabot's grouped-update titles�[0m
 �[36;1m# routinely exceed the 72-char subject cap and the format isn't�[0m
 �[36;1m# configurable, so Dependabot PRs are exempt from the length check�[0m
 �[36;1m# (the format check still applies).�[0m
 �[36;1mif [[ "$PR_AUTHOR" == "dependabot[bot]" || "$PR_AUTHOR" == "app/dependabot" ]]; then�[0m
 �[36;1m  export PR_TITLE_SKIP_LENGTH=1�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif reason=$(bash scripts/lint-pr-title.sh "$PR_TITLE" 2>&1); then�[0m
 �[36;1m  echo "passed=true" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  echo "PR title OK: $PR_TITLE"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "passed=false" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  printf '%s\n' "$reason"�[0m
 �[36;1m  echo "::error::$(printf '%s' "$reason" | head -1)"�[0m
🧰 Additional context used
📓 Path-based instructions (5)
docs/src/content/docs/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep the docs site accurate and vertically readable: update docs when behavior changes, prefer top-down Mermaid diagrams, avoid wide side-by-side diagrams, and keep labels short enough to remain legible in the Starlight column.

Files:

  • docs/src/content/docs/configuration.mdx
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/api.md
cmd/wavehouse/**

📄 CodeRabbit inference engine (AGENTS.md)

In cmd/wavehouse, preserve non-fatal boot behavior: schema-discovery failures must still bind the server, expose /livez and /readyz as 503 with diagnostics, and retry refresh in the background.

Files:

  • cmd/wavehouse/main.go
tests/**

📄 CodeRabbit inference engine (AGENTS.md)

Write tests in a table-driven style, use shared mocks/helpers from internal/testutil/, add corresponding test cases for every new function, and prefer the repo’s assertion helpers for HTTP responses.

Files:

  • tests/e2e/fixtures/config.yaml
internal/config/**

📄 CodeRabbit inference engine (AGENTS.md)

In internal/config, keep config struct tags and loader behavior aligned with the documented YAML/env schema; new or changed config fields must use the expected yaml/env/env-default tags.

Files:

  • internal/config/config.go
internal/api/**

📄 CodeRabbit inference engine (AGENTS.md)

In the structured-query handler under internal/api, keep POST /v1/query?table={table} schema-validated, permission-enforced, timestamp-bucketed, and capped by DefaultMaxRows (10,000); route/handler changes must stay aligned with the documented endpoint behavior and error responses.

Files:

  • internal/api/ingest_test.go
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-07-07T18:50:01.460Z
Learning: Validate locally before every push by running `make ci` the documented way; do not use CI as the first feedback loop.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-07-07T18:50:01.460Z
Learning: On PR-branch pushes, run the required pre-push reviewer workflow so every reviewer needed by the diff ends with a marker for HEAD; never hardcode the reviewer set.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-07-07T18:50:01.460Z
Learning: Every code change must include the corresponding docs update and `CHANGELOG.md` entry in the same PR.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-07-07T18:50:01.460Z
Learning: Address and resolve every review finding; reply substantively, fix or track it, mention the bot when required, and never silently drop a comment.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-07-07T18:50:01.460Z
Learning: Create agent PRs as drafts only, and ensure the PR title passes Conventional Commits and the 72-character limit before creation.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-07-07T18:50:01.460Z
Learning: Never force-push or rebase a PR branch; to sync with upstream main, merge `origin/main` instead.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-07-07T18:50:01.460Z
Learning: Never hand-write review/CI markers or bypass gates with `--no-verify`; use the documented tooling instead.
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-07-07T18:50:01.460Z
Learning: Keep `CLAUDE.md` as a short pointer to `AGENTS.md` and do not duplicate the instructions there.
📚 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:

  • cmd/wavehouse/main.go
  • internal/config/config.go
  • internal/api/ingest_test.go
📚 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:

  • AGENTS.md
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/api.md
  • CHANGELOG.md
📚 Learning: 2026-05-20T01:02:00.784Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 164
File: internal/api/router_test.go:289-350
Timestamp: 2026-05-20T01:02:00.784Z
Learning: In WaveHouse’s internal API tests (files matching internal/api/**/*_test.go), follow the existing separation-of-concerns convention for testing the RequireRole middleware: inject `ContextKeyRole` directly into the request `context.Context` instead of using `testutil.MakeJWT`/JWT-driven flows. Do not refactor role-gate tests to use JWT tokens—JWT parsing and token handling are covered separately in `middleware_test.go` (the dedicated JWT parsing tests), and mixing those concerns would expand the failure surface and reduce isolation.

Applied to files:

  • internal/api/ingest_test.go
📚 Learning: 2026-05-23T01:23:59.268Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 174
File: internal/api/ingest_test.go:111-111
Timestamp: 2026-05-23T01:23:59.268Z
Learning: In WaveHouse Go tests in internal/api/**/*_test.go, use internal/testutil.AssertJSONErrorResponse(t, w) for HTTP error-path JSON assertions. Do not use (or reintroduce) package-local assertJSONErrorResponse helpers. AssertJSONErrorResponse verifies the response Content-Type is application/json, includes the X-Content-Type-Options: nosniff header, and that the JSON body contains an "error" field.

Applied to files:

  • internal/api/ingest_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:

  • internal/api/ingest_test.go
🪛 Checkov (3.3.2)
tests/e2e/fixtures/config.yaml

[low] 27-28: Base64 High Entropy String

(CKV_SECRET_6)

🔇 Additional comments (12)
internal/api/ingest_test.go (1)

581-598: LGTM!

The added testutil.AssertJSONErrorResponse(t, w) at line 596 resolves the previously flagged gap versus the sibling null-id test.

internal/config/config.go (2)

138-139: LGTM!


161-172: Unrelated to PR scope.

The Auth.OperatorKey field/comment block is unrelated to the stated per-table dedupe PR objective and isn't mentioned in the AI summary. Confirm this is intentionally included (e.g., merged from a prior PR into this branch) rather than accidental scope creep.

config.yaml (1)

96-101: Unrelated to PR scope.

This operator_key line is unrelated to the per-table dedupe changes described in the PR objectives/AI summary.

AGENTS.md (1)

57-57: LGTM!

cmd/wavehouse/main.go (2)

337-348: LGTM!


81-89: Unrelated to PR scope.

The OperatorKey trimming/logging and auth.Middleware wiring are unrelated to the per-table dedupe feature described in this PR's objectives/AI summary.

Also applies to: 385-389

CHANGELOG.md (1)

14-15: LGTM!

Also applies to: 33-37

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

32-40: LGTM!

Also applies to: 238-238

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

72-78: LGTM!

Also applies to: 96-97, 111-112, 170-172, 203-203

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

165-167: LGTM!

Also applies to: 277-277, 346-346

tests/e2e/fixtures/config.yaml (1)

20-22: LGTM!

Also applies to: 24-29


📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added per-table deduplication settings so specific tables can use different ID fields or require an ID independently of the global defaults.
    • Added an option to disable deduplication for a table by setting its ID field to empty.
  • Bug Fixes

    • Fixed handling of explicit null values for deduplication IDs so they’re treated like missing IDs.
    • Prevented deduplication collisions between different tables by separating dedupe state per table.
  • Documentation

    • Updated config, API, and architecture docs with the new deduplication behavior and examples.

Walkthrough

Adds per-table dedupe overrides, table-namespaced dedupe keys, explicit JSON null handling for dedupe ids, and matching config, test, and documentation updates.

Changes

Per-table dedupe overrides and table namespacing

Layer / File(s) Summary
Config schema and guidance
internal/config/config.go, config.yaml, AGENTS.md, CHANGELOG.md, docs/src/content/docs/configuration.mdx, docs/src/content/docs/api.md, docs/src/content/docs/architecture.md
Adds dedupe.tables config types and YAML guidance, updates operational notes, and documents per-table overrides, null-id handling, and dedupe behavior changes.
Deduplicator contract and namespaced storage
internal/dedupe/dedupe.go, internal/dedupe/embedded.go, internal/dedupe/embedded_test.go, internal/testutil/mocks.go, internal/observability/metrics_test.go
Changes CheckAndMark to accept table, namespaces dedupe keys by table, and updates tests and mocks to match the new keying behavior.
Ingest handler resolution and wiring
internal/api/ingest.go, cmd/wavehouse/main.go
Adds per-table override resolution in the ingest path, treats explicit JSON null ids as missing, and wires config overrides into the handler.
Ingest behavior tests
internal/api/ingest_test.go
Adds coverage for explicit-null ids and per-table overrides for RequireID, IDField, and dedupe opt-out.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant IngestHandler
  participant Deduplicator
  participant Pebble

  Client->>IngestHandler: POST /v1/ingest?table={table}
  IngestHandler->>IngestHandler: resolve dedupeFieldsFor(table)
  alt id field empty
    IngestHandler-->>Client: publish without dedupe
  else id missing or explicit null
    alt requireID true
      IngestHandler-->>Client: 400 reject
    else
      IngestHandler-->>Client: publish un-deduped
    end
  else id present
    IngestHandler->>Deduplicator: CheckAndMark(table, eventID)
    Deduplicator->>Pebble: lookup table + "\x00" + eventID
    Pebble-->>Deduplicator: found / not found
    Deduplicator-->>IngestHandler: isDuplicate
    IngestHandler-->>Client: publish or mark duplicate
  end
Loading

Possibly related issues

Possibly related PRs

  • Wave-RF/WaveHouse#364: Also changes ingest dedupe enforcement around missing ids and RequireID, with this PR extending that flow to per-table overrides and JSON null handling.

Suggested labels: go, area/api, area/infra
Suggested reviewers: EricAndrechek

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: per-table dedupe overrides for id_field and require_id.
Description check ✅ Passed The description matches the changeset and accurately describes the per-table overrides, key namespacing, and null-id fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dedupe-per-table
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch dedupe-per-table

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.

@taitelee taitelee moved this from Backlog to In review in WaveHouse Task Board Jul 6, 2026
@github-code-quality

github-code-quality Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: Go

Go

The overall coverage in the branch remains at 90%, unchanged from the branch.

Show a code coverage summary of the most impacted files.
File 774faec eda2f31 +/-
cmd/wavehouse/main.go 70% 70% 0%
internal/api/ingest.go 97% 97% 0%
internal/dedupe/embedded.go 84% 85% +1%

Updated July 07, 2026 18:52 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

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

  • Commiteda2f31: Merge origin/main into dedupe-per-table
  • Author@taitelee
  • Committed — 2026-07-07 14:43 (UTC-04:00)
  • Deployed — 2026-07-07 14:52 EDT

@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: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5f8723d4-7881-4381-981c-e130bfb559fb

📥 Commits

Reviewing files that changed from the base of the PR and between adee449 and 70a9b91.

📒 Files selected for processing (14)
  • CHANGELOG.md
  • cmd/wavehouse/main.go
  • config.yaml
  • docs/src/content/docs/api.md
  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/configuration.mdx
  • internal/api/ingest.go
  • internal/api/ingest_test.go
  • internal/config/config.go
  • internal/dedupe/dedupe.go
  • internal/dedupe/embedded.go
  • internal/dedupe/embedded_test.go
  • internal/observability/metrics_test.go
  • internal/testutil/mocks.go
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: Integration tests
  • GitHub Check: E2E tests
  • GitHub Check: Docs build
  • GitHub Check: Coverage
  • GitHub Check: Lint
⚠️ CI failures not shown inline (2)

GitHub Actions: PR housekeeping / PR housekeeping: feat(dedupe): per-table id_field/require_id overrides

Conclusion: failure

View job details

##[group]Run # Single source of truth for the rule: scripts/lint-pr-title.sh — the
 �[36;1m# Single source of truth for the rule: scripts/lint-pr-title.sh — the�[0m
 �[36;1m# SAME validator the local agent gate runs (.claude/hooks/agent-bash-gate.sh),�[0m
 �[36;1m# so CI and local can't drift. The checkout above is ref: main, so this is�[0m
 �[36;1m# always the default-branch script. Dependabot's grouped-update titles�[0m
 �[36;1m# routinely exceed the 72-char subject cap and the format isn't�[0m
 �[36;1m# configurable, so Dependabot PRs are exempt from the length check�[0m
 �[36;1m# (the format check still applies).�[0m
 �[36;1mif [[ "$PR_AUTHOR" == "dependabot[bot]" || "$PR_AUTHOR" == "app/dependabot" ]]; then�[0m
 �[36;1m  export PR_TITLE_SKIP_LENGTH=1�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif reason=$(bash scripts/lint-pr-title.sh "$PR_TITLE" 2>&1); then�[0m
 �[36;1m  echo "passed=true" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  echo "PR title OK: $PR_TITLE"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "passed=false" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  printf '%s\n' "$reason"�[0m
 �[36;1m  echo "::error::$(printf '%s' "$reason" | head -1)"�[0m

GitHub Actions: PR housekeeping / 0_PR housekeeping.txt: feat(dedupe): per-table id_field/require_id overrides

Conclusion: failure

View job details

##[group]Run # Single source of truth for the rule: scripts/lint-pr-title.sh — the
 �[36;1m# Single source of truth for the rule: scripts/lint-pr-title.sh — the�[0m
 �[36;1m# SAME validator the local agent gate runs (.claude/hooks/agent-bash-gate.sh),�[0m
 �[36;1m# so CI and local can't drift. The checkout above is ref: main, so this is�[0m
 �[36;1m# always the default-branch script. Dependabot's grouped-update titles�[0m
 �[36;1m# routinely exceed the 72-char subject cap and the format isn't�[0m
 �[36;1m# configurable, so Dependabot PRs are exempt from the length check�[0m
 �[36;1m# (the format check still applies).�[0m
 �[36;1mif [[ "$PR_AUTHOR" == "dependabot[bot]" || "$PR_AUTHOR" == "app/dependabot" ]]; then�[0m
 �[36;1m  export PR_TITLE_SKIP_LENGTH=1�[0m
 �[36;1mfi�[0m
 �[36;1m�[0m
 �[36;1mif reason=$(bash scripts/lint-pr-title.sh "$PR_TITLE" 2>&1); then�[0m
 �[36;1m  echo "passed=true" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  echo "PR title OK: $PR_TITLE"�[0m
 �[36;1melse�[0m
 �[36;1m  echo "passed=false" >> "$GITHUB_OUTPUT"�[0m
 �[36;1m  printf '%s\n' "$reason"�[0m
 �[36;1m  echo "::error::$(printf '%s' "$reason" | head -1)"�[0m
🧰 Additional context used
📓 Path-based instructions (5)
internal/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Core packages should be interface-first: behaviors such as cache, dedupe, publisher, and subscriber should be defined behind interfaces to keep implementations swappable.

Files:

  • internal/dedupe/embedded.go
  • internal/observability/metrics_test.go
  • internal/dedupe/embedded_test.go
  • internal/dedupe/dedupe.go
  • internal/testutil/mocks.go
  • internal/api/ingest_test.go
  • internal/config/config.go
  • internal/api/ingest.go
cmd/wavehouse/**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

Wire new dependencies in cmd/wavehouse/main.go rather than relying on global state or hidden initialization.

Files:

  • cmd/wavehouse/main.go
docs/src/content/docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

Update the relevant docs pages when APIs, configuration, architecture, ingest format, deployment, or development workflows change; keep examples and prose accurate.

Files:

  • docs/src/content/docs/architecture.md
  • docs/src/content/docs/api.md
docs/src/content/docs/**/*.mdx

📄 CodeRabbit inference engine (AGENTS.md)

Author Mermaid diagrams vertically by default, keep labels short, and avoid placing two large diagrams side by side.

Files:

  • docs/src/content/docs/configuration.mdx
internal/config/config.go

📄 CodeRabbit inference engine (AGENTS.md)

Add configuration fields with matching yaml, env, and env-default struct tags, and keep the config struct in sync with documented settings.

Files:

  • internal/config/config.go
🧠 Learnings (5)
📚 Learning: 2026-05-25T11:25:08.794Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 180
File: internal/observability/instruments.go:40-117
Timestamp: 2026-05-25T11:25:08.794Z
Learning: In WaveHouse (Go), it is idiomatic to declare OpenTelemetry (OTel) metric instruments as package-level `var` values created via the OTel Go SDK global proxy pattern (e.g., `var h metric.Float64Histogram = Meter().Float64Histogram(...)`, `var c metric.Int64Counter = Meter().Int64Counter(...)`). When reviewing, do NOT flag these as “global state” violations under the AGENTS.md constructor-injection rule; that rule is intended for swappable application-level dependencies (Cache/Publisher/Subscriber/Deduplicator), not OTel proxy instrument declarations. Do not recommend refactoring these instruments behind an `Instruments` struct for dependency injection.

Applied to files:

  • internal/observability/metrics_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:

  • internal/observability/metrics_test.go
  • internal/dedupe/embedded_test.go
  • internal/api/ingest_test.go
📚 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/architecture.md
  • docs/src/content/docs/api.md
  • CHANGELOG.md
📚 Learning: 2026-05-20T01:02:00.784Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 164
File: internal/api/router_test.go:289-350
Timestamp: 2026-05-20T01:02:00.784Z
Learning: In WaveHouse’s internal API tests (files matching internal/api/**/*_test.go), follow the existing separation-of-concerns convention for testing the RequireRole middleware: inject `ContextKeyRole` directly into the request `context.Context` instead of using `testutil.MakeJWT`/JWT-driven flows. Do not refactor role-gate tests to use JWT tokens—JWT parsing and token handling are covered separately in `middleware_test.go` (the dedicated JWT parsing tests), and mixing those concerns would expand the failure surface and reduce isolation.

Applied to files:

  • internal/api/ingest_test.go
📚 Learning: 2026-05-23T01:23:59.268Z
Learnt from: EricAndrechek
Repo: Wave-RF/WaveHouse PR: 174
File: internal/api/ingest_test.go:111-111
Timestamp: 2026-05-23T01:23:59.268Z
Learning: In WaveHouse Go tests in internal/api/**/*_test.go, use internal/testutil.AssertJSONErrorResponse(t, w) for HTTP error-path JSON assertions. Do not use (or reintroduce) package-local assertJSONErrorResponse helpers. AssertJSONErrorResponse verifies the response Content-Type is application/json, includes the X-Content-Type-Options: nosniff header, and that the JSON body contains an "error" field.

Applied to files:

  • internal/api/ingest_test.go
🔇 Additional comments (15)
CHANGELOG.md (1)

14-15: LGTM!

Also applies to: 33-37

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

77-77: LGTM!

Also applies to: 171-172

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

132-149: LGTM!

internal/config/config.go (1)

135-148: LGTM!

config.yaml (1)

76-85: LGTM!

internal/dedupe/dedupe.go (1)

6-16: LGTM!

internal/dedupe/embedded.go (1)

28-51: LGTM!

internal/dedupe/embedded_test.go (1)

21-57: LGTM! Sequential test structure here correctly follows the prior preference for not forcing single-scenario tests into a table-driven form.

internal/testutil/mocks.go (1)

100-112: LGTM!

internal/observability/metrics_test.go (1)

23-27: LGTM!

internal/api/ingest.go (1)

39-46: LGTM!

Also applies to: 59-79, 427-463

cmd/wavehouse/main.go (1)

334-340: LGTM!

internal/api/ingest_test.go (3)

533-551: LGTM!


553-576: LGTM!


599-650: LGTM!

Comment thread docs/src/content/docs/api.md Outdated
Comment thread internal/api/ingest_test.go
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 6, 2026
@taitelee taitelee moved this from In review to Ready in WaveHouse Task Board Jul 7, 2026
@EricAndrechek

Copy link
Copy Markdown
Member

So just to be sure I'm understanding this correctly:

  • you can now NOT set everything via ENV variables – tables dedupe settings become possible ONLY via the config file now?
  • is there a way to default to disabled and turn on only specific tables, or only the inverse via setting a table's id_field to "" to disable dedupe for it?

@taitelee

taitelee commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

To answer Eric's questions:

  • Yes. The tables dedupe settings become possible only via the config file.
  • Both. Verifying this exposed a bug. The flat dedupe.* settings are the defaults, and a tables entry overrides them. An empty id_field means "don't dedupe". Turning one table off (id_field: "" under tables:) already worked. The opposite — off by defauopt-in was an undocumented env var trick.

GOing to fix now. Leaving id_field out still defaults to event_id. An explicit "" (in the file, or WH_DEDUPE_ID_FIELD=) now sticks and turns dedupe off by default:

We realized that having dedupe settings requiring a config file could get messy for the user, and solely using env variables for dedupe settings can be a complex task, so we are deferring this until we have solved issue #48.

@taitelee taitelee moved this from Ready to Backlog in WaveHouse Task Board Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api HTTP handlers, routing, middleware area/dedupe Deduplication (Pebble, ScyllaDB) area/docs Documentation, site/, README area/infra CI, build, deploy, Docker, release area/observability Metrics, logs, traces, health, profiling documentation Improvements or additions to documentation go Pull requests that update go code

Projects

Status: Backlog

2 participants