Skip to content

feat(cache): implement per-table tagging and raw SQL bypass - #119

Closed
taitelee wants to merge 43 commits into
mainfrom
cache_updates
Closed

feat(cache): implement per-table tagging and raw SQL bypass#119
taitelee wants to merge 43 commits into
mainfrom
cache_updates

Conversation

@taitelee

@taitelee taitelee commented May 12, 2026

Copy link
Copy Markdown
Member

Summary

This PR implements the Read-Your-Writes architectural consensus established in #73. It addresses the issue of stale data in the API query cache by introducing a granular, table-based invalidation signal triggered by the ingest pipeline.

  • Option C (Raw SQL Bypass): Stripped caching from the raw SQL path (/v1/query). Admin and ad-hoc queries now hit ClickHouse directly to ensure absolute consistency for manual debugging and E2E tests.
  • Option B (Table Tagging): - Updated the Cache interface to support tagging.
    • Implemented a tagsMap in LocalCache to track which cache keys are associated with specific database tables.
    • Added naive SQL parsing in pipes.go and table-name extraction in structured_query.go to tag cache entries during the Set operation.
  • Ingest Integration: Wired the TieredCache into the Bento ingest worker. Upon a successful batch flush to ClickHouse, Bento now triggers a targeted invalidation of all cache entries tagged with that table name.

Test plan

  • Unit Tests: Updated internal/cache tests to cover tag-based eviction and verified signature consistency across mocks.
  • Manual Verification: Verified that raw SQL queries return X-Cache: BYPASS headers.
  • E2E Integration: Verified that a structured query performed immediately after a Bento flush returns fresh data rather than a stale cached result.

Related Issues

Closes #73, #85, #93

Summary by CodeRabbit

  • New Features

    • Cache tagging for table-scoped entries; automatic per-table invalidation after successful DB writes
    • Query endpoint will explicitly indicate cache bypass via X-Cache: BYPASS
  • Breaking Changes

    • Cache write/invalidation API updated to support tags
    • /v1/query now bypasses cache by default
  • Bug Fixes

    • Fixed TOCTOU race in local cache invalidation
  • Documentation

    • Updated API and architecture docs for tagging, invalidation, and cache header behavior
  • Tests

    • Added tests for tag extraction and tag-based invalidation

Review Change Stack

@github-actions github-actions Bot added go Pull requests that update go code area/api HTTP handlers, routing, middleware area/ingest Ingest pipeline (Bento, batching, DLQ) area/cache Local / shared / tiered caching labels May 12, 2026
@github-actions
github-actions Bot requested a review from EricAndrechek May 12, 2026 18:26
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a robust mechanism to maintain data consistency between the API cache and the underlying ClickHouse database. By implementing table-based tagging, the system can now selectively invalidate stale cache entries when data is ingested. Additionally, it addresses consistency concerns for raw SQL queries by bypassing the cache entirely, ensuring that manual and automated debugging tasks always reflect the current state of the database.

Highlights

  • Table-Based Cache Invalidation: Implemented a tagging system in the cache layer, allowing for granular invalidation of cache entries associated with specific database tables.
  • Raw SQL Cache Bypass: Disabled caching for raw SQL queries (/v1/query) to ensure absolute data consistency for administrative and debugging operations.
  • Ingest Pipeline Integration: Integrated the cache invalidation logic into the Bento ingest worker, ensuring that cache entries are purged automatically after successful batch flushes to ClickHouse.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@taitelee taitelee removed their assignment May 12, 2026

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a tag-based cache invalidation system to ensure data consistency when tables are modified. It updates the Cache interface with InvalidateByTags, implements this in LocalCache using a tagsMap, and integrates invalidation logic into the PipesHandler, QueryHandler, and StructuredQueryHandler. Additionally, the ingest worker now triggers invalidation upon writing batches. Feedback focuses on a potential race condition in InvalidateByTags that could lead to orphaned cache entries and suggests refactoring duplicated table extraction logic into a shared, validated helper to align with repository security standards.

Comment thread internal/cache/local.go Outdated
Comment thread internal/api/query.go Outdated

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces tag-based cache invalidation to ensure consistency between ClickHouse and the API cache. Key updates include modifying the Cache interface to support tags, implementing a tag-to-key index in LocalCache, and integrating invalidation triggers within the ingestion pipeline and raw SQL query handler. Feedback highlights a high-severity race condition in the InvalidateByTags implementation that could result in stale cache entries and recommends refactoring duplicated table extraction logic into a shared helper that enforces strict identifier validation. Iterate.

Comment thread internal/cache/local.go Outdated
Comment thread internal/api/query.go Outdated
Comment thread internal/cache/cache.go
Comment thread internal/cache/local.go Outdated
Comment thread internal/cache/local.go Outdated
Comment thread internal/cache/cache.go
Comment thread internal/testutil/mocks.go Outdated
@claude

claude Bot commented May 12, 2026

Copy link
Copy Markdown

2 [MUST], 2 [SHOULD], 0 [MAY] — see inline threads for detail.

Verdict: Iterate.

The implementation direction is sound and the Gemini-flagged race from the prior commit was correctly addressed via LoadAndDelete. Two blockers remain:

  1. Residual TOCTOU in InvalidateByTags (local.go:71) — LoadAndDelete closes the "append-during-Range" window but opens the symmetric one: a concurrent Set that runs after the delete creates a fresh *sync.Map that the current pass never sees, leaving pre-ingest data cached until TTL. Switch tagsMap to a plain map guarded by a sync.RWMutex to make the delete and any racing Set mutually exclusive.
  2. Missing documentation sync (cache.go:17) — docs/api.md (X-Cache: BYPASS), docs/architecture.md (interface change), and CHANGELOG.md ([Unreleased] entry) are all required by AGENTS.md and absent from this diff.

The tagsMap memory leak ([SHOULD], local.go:63) and too-coarse test mocks ([SHOULD], mocks.go:147) should be addressed in this PR as well — the mocks as written can hide L1/L2 routing bugs in TieredCache.

@github-actions github-actions Bot added documentation Improvements or additions to documentation area/docs Documentation, site/, README labels May 12, 2026
Comment thread docs/architecture.md Outdated
Comment thread internal/api/query.go Outdated
Comment thread internal/api/pipes.go Outdated
Comment thread internal/api/structured_query.go Outdated
Comment thread internal/api/query_test.go
@claude

claude Bot commented May 15, 2026

Copy link
Copy Markdown

Review summary

1 [SHOULD], 3 [MAY], 0 [MUST] — see inline threads for detail.

The core implementation is solid: the double-indexed tag map (tagsMap / keyTags) with version-stamped eviction callbacks is the right approach to the TOCTOU race that a plain sync.Map couldn't handle. Wait() before the invalidation sweep correctly drains Ristretto's async admission buffer. The regex pipeline (strip literals → strip quoted identifiers → mutationRe) is correct and the \b anchors prevent false positives on column names like drop_rate. Integration tests exercise the full RYW path through the real HTTP layer. Doc sync is complete.

Threads:

  • query_test.go:228 — [SHOULD] misleading test name + missing mutationRe assertion for unquoted DML-prefixed column names
  • pipes.go:167, structured_query.go:161 — [MAY] setCancel() should be defer setCancel()
  • query_test.go:355 — [MAY] REPLACE INTO not covered in TestExtractMutationTargets

Ship it — address the [SHOULD] test gap; the [MAY] nits can go in a follow-up if preferred.

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 15, 2026
@EricAndrechek EricAndrechek moved this from Ready to In review in WaveHouse Task Board May 18, 2026
Comment thread internal/cache/local.go
l.ttls.Delete(key)
delete(l.keyVersion, key) // Clean up version tracker
l.removeKeyFromTagsLocked(key)
l.tagsMu.Unlock()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[SHOULD] Security: NONE / Correctness: LOW — Residual TOCTOU in the expiry eviction path.

The prior round's taitelee reply (3231258193) stated "once the tagsMu.Lock() is acquired, we verify again the TTL status. If a concurrent update happened, we gracefully abort the deletion and return the fresh value." But the code does not implement that re-check. The race scenario is:

  1. Get calls l.ttls.Load(key) (line 110) and reads the old expiry.
  2. Concurrent Set acquires tagsMu.Lock(), writes a new value + new TTL into l.ttls, updates keyVersion, releases lock.
  3. Get computes remaining <= 0 (true based on the old expiry), acquires tagsMu.Lock().
  4. Get deletes keyVersion[key], removes tags, and calls l.cache.Del(key) — all of which were just written by the concurrent Set.

Result: a live, freshly-written entry is silently evicted. The next caller gets a spurious MISS and re-queries ClickHouse — no data loss, but the invariant the author described is not actually enforced.

Fix: re-read and re-check the TTL inside the lock before deleting:

Suggested change
l.tagsMu.Unlock()
// Re-check TTL inside the lock: a concurrent Set may have
// refreshed the expiry between our outer check and here.
if freshExpVal, freshOk := l.ttls.Load(key); freshOk {
freshExp := freshExpVal.(time.Time)
if !freshExp.IsZero() && time.Until(freshExp) > 0 {
// Entry was refreshed; return the live value.
l.tagsMu.Unlock()
return val.data, time.Until(freshExp), nil
}
}
l.cache.Del(key)
l.ttls.Delete(key)
delete(l.keyVersion, key) // Clean up version tracker
l.removeKeyFromTagsLocked(key)
l.tagsMu.Unlock()
return nil, 0, nil

Comment thread internal/ingest/bento.go
consumer jetstream.Consumer
iter jetstream.MessagesContext
chConn driver.Conn
cache cache.Cache

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[SHOULD] Correctness: LOW — registerOnce (the package-level sync.Once at line 49) captures apiCache from the first StartIngestWorker call. Any subsequent call with a different apiCache silently discards its argument: the Bento plugin factories close over the original value.

The integration test documents this as "non-negotiable" (setup_test.go lines 10-13) and cmd/wavehouse/main.go only calls StartIngestWorker once, so there is no current regression. However the API is a silent trap — a future hot-reload handler, a second integration suite, or any caller that passes a fresh cache will observe stale invalidations with no error.

Minimum fix: add a godoc comment on StartIngestWorker stating the one-call-per-process constraint, consistent with the constraint the integration test already documents internally:

Suggested change
cache cache.Cache
cache cache.Cache // captured in the registerOnce factory; see StartIngestWorker godoc

And on StartIngestWorker:

// StartIngestWorker may only be called once per process: Bento's
// service.Register* functions are process-global singletons (guarded by
// registerOnce). The apiCache and chConn arguments are captured in the plugin
// factory closures on the first call; subsequent calls reuse those captured
// values regardless of the arguments passed.

@claude

claude Bot commented May 18, 2026

Copy link
Copy Markdown

Review — feat(cache): per-table tagging + raw SQL bypass (round 9)

0 [MUST] · 2 [SHOULD] · 0 [MAY] — see inline threads for detail.

What this round resolved

The two [MUST] items from round 8 are closed:

  • safeIdentifierRe duplication — fixed by exporting query.SafeIdentifierRe from internal/query/sqlutil.go and importing it in both bento.go and query.go. Single source of truth, compile-time link.
  • registerOnce integration test correctness — the integration test now has a clear TestMain-wires-once contract documented in setup_test.go lines 10-13; the shared tiered cache flows from setup() through to StartIngestWorker and buildServer correctly.

Also well done: context.WithoutCancel for mutation execution, defer setCancel() inside singleflight closures (runs when closure returns, not when the outer request handler returns), OnEvict cleanup hook, l.ttls.Store now inside tagsMu.Lock(), and full test coverage for REPLACE INTO + column-prefix false-positive invariant.

Remaining findings

Thread File Severity
[SHOULD] Expiry TOCTOU in Get: author's prior reply said "verify again the TTL status" after acquiring the lock, but the code at lines 114–120 has no re-check — a concurrent Set can write a fresh entry that the expiry eviction then silently deletes internal/cache/local.go:120 Correctness: LOW
[SHOULD] registerOnce one-call-per-process constraint is undocumented in the StartIngestWorker godoc — callers that pass a second apiCache get no error internal/ingest/bento.go:57 Correctness: LOW

Verdict: Ship it

No [MUST]-level findings remain. Both [SHOULD] items are low-probability correctness issues (spurious cache MISS and silent no-op argument) with no data-loss or security impact. The TOCTOU fix is a one-liner and the godoc addition is trivial — worth doing before merge, but not a blocker. Address both, resolve the threads, and this is ready.

Comment thread internal/cache/tiered.go
return &TieredCache{l1: l1, l2: l2}
func NewTiered(l1, l2 Cache) (*TieredCache, error) {
if l2 != nil {
return nil, errors.New("L2 cache requires tag-propagation support (not yet implemented); pass nil for standalone mode")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We don't have any L2/shared cache right now... it's just the same as the general cache interface, so technically the interface does support this we just don't have an implementation right now but we can/should still use the interface as if we do, no?

Comment thread internal/cache/tiered.go

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

See comments in file, but this needs revisions, should build it as if fully implemented/supported

Comment thread internal/cache/local.go

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would like to talk about this file and help understanding it in-person review later, lot's of questions/confusion on it and could use a walkthrough

Comment thread internal/api/pipes.go
Store *pipes.Store
CHConn driver.Conn
Cache *cache.TieredCache
Cache cache.Cache

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why? Why not leave as TieredCache? They do the same things for us under the hood, no?

Comment thread internal/api/pipes.go
setCtx, setCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer setCancel()
if err := h.Cache.Set(setCtx, cacheKey, data, h.DefaultTTL, tags); err != nil {
slog.WarnContext(r.Context(), "cache set failed for pipe execution",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this not be the same context as the setCtx is here for the cache set, why/why not?

Comment thread internal/api/query.go

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I really don't like all the regex in this file... it scares me... also worries me that is may go out of sync with regex we have in the internal query package. How much of this is needed here vs should/could be shared and may be useful more generally, and/or what of this do you think will be removed anyway when we work on #167?

type StructuredQueryHandler struct {
CHConn driver.Conn
Cache *cache.TieredCache
Cache cache.Cache

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same note on this file, why not TieredCache?

// Detach from r.Context() so a cancelled originating request doesn't
// abort the cache populate — singleflight followers may already have
// received the result, and the next request should be served warm.
setCtx, setCancel := context.WithTimeout(context.Background(), 5*time.Second)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The more I look at this/see it in several places, the more it concerns me – we don't know that 5sec will be enough and is a clear path to DoS risks and feels like a premature optimization that would be better tracked and implemented in one pass everywhere, as tracked by #120.

@EricAndrechek EricAndrechek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right direction, but I think there are a significant number of things that need changing before approval. Namely:

  • singleflight optimizations for dropped initial connections should be removed from this PR to keep it smaller, and tracked in #120 instead
  • TieredCache should still be used and templated as if it works with an L2 cache, no harm if there is no layered cache but the interface for it should be the same regardless
  • Not a huge fan of the regex, especially as part of the already bloated API package, let's consider cleaning it up/reorganizing where its put and if there are other/better ways we can do it, as this is effectively a custom partial lite SQL parser, and will become a thorn in our side with edge cases very quickly
  • I need some more help making sense of the actual cache interface's implementation (in local.go), perhaps some more of it could be abstracted out/shared in a way to let it work with minimal repeated code regardless of if using ristretto or redis, etc.

@EricAndrechek

Copy link
Copy Markdown
Member

Edit: Closing in favor of #164 first, it will make this issue significantly easier to address in a new PR.

@github-project-automation github-project-automation Bot moved this from In review to Done in WaveHouse Task Board May 19, 2026
EricAndrechek added a commit that referenced this pull request May 20, 2026
## Summary

Umbrella PR setting up shared Claude Code + AI agent infrastructure for
the WaveHouse team. Two work streams:

1. **AI rules drift cleanup** — corrected stale references that AI tools
(Claude Code, Gemini Code Assist, Copilot, CodeRabbit) were following
blindly.
2. **Claude Code native tooling** — committed `.claude/` configuration
and `.githooks/` so every teammate gets identical dev affordances out of
the box, with agent-specific gating layered on top.

The team just got Max 20x subscriptions across the board; this lands the
team-wide config so everyone is on the same agentic dev experience by
default.

## Scope

### 1. AI rules drift cleanup

- **17 doc-path references corrected** across AGENTS.md +
CONTRIBUTING.md to `docs/src/content/docs/*.md` (the actual Astro
Starlight location, not the old flat layout).
- **`.github/copilot-instructions.md` shrunk to a pointer** — was
drifting on Go 1.25 (vs current 1.26.3) and 60% coverage (vs current 80%
total / 70% unit per `.testcoverage.yml`).
- **`.gemini/styleguide.md`** — stale `#67` / 60% claim fixed (issue
closed, 70% restored); duplicated doc-sync bullet collapsed to defer to
AGENTS.md (already authoritative).
- **`.github/labeler.yml`** — dropped non-existent
`cmd/wavehouse-{api,worker}/**` entries; fixed
`tests/{compose.yaml,sdk/**}` → `tests/e2e/...`; added
`cmd/wavehouse/**` to `area/infra`.
- **`.github/prompts/pr-review.md`** — doc-sync list collapsed;
vestigial "tenant" wording dropped (no tenant model in WaveHouse);
hard-wrap reflowed (180 → 81 lines).
- **AGENTS.md** — `cmd/*/main.go` (plural) → `cmd/wavehouse/main.go`
(one binary); `tests/fixtures/` → `tests/e2e/fixtures/`; removed stale
"update `triage.yml` area enumeration" step (workflow now discovers
`area/*` labels dynamically); fixed internal-package count.
- **CONTRIBUTING.md** — vestigial "tenant isolation" wording removed.
- **TODO.md deleted** — audit summary below.

### 2. Claude Code native tooling

- **`.claude/`** — shared configuration: `settings.json` (deny rules +
worktree config + three hooks wired), `agents/pre-push-reviewer.md`,
`hooks/agent-bash-gate.sh` (PreToolUse Bash gate),
`hooks/review-marker.sh` (PostToolUse Agent marker writer),
`hooks/gofumpt-on-save.sh` (auto-format), `skills/pr-review-locally/`,
`skills/pr-sync-with-main/`, `commands/cover.md`.
- **`.githooks/`** — universal team hooks installed by `make tools`:
`pre-commit` runs `make verify`; `pre-push` requires
`tmp/ci-passed-<HEAD-sha>` marker (written by `make ci`).
- **`.config/wt.toml`** — worktrunk project hooks so parallel-agent
worktrees install `.githooks/` correctly.
- **AGENTS.md §"Agent PR Discipline"** — new section codifying the
agent-only ruleset:
- Drafts-only PR creation; human-only
ready/approve/request-changes/reviewer-add transitions.
- Bot reviewer re-triggers go through PR comments (`@coderabbitai
review`, `@gemini-code-assist`, `@claude` / `/review`).
- **Pre-push self-review mandatory** on PR branches: agent invokes
`pre-push-reviewer` subagent in fresh context. `ship_it` requires zero
findings at any severity — any `[MUST]` / `[SHOULD]` / `[MAY]` forces
iterate; the orchestrator loops review → fix → review until clean.
- **Honest-agent marker policy**: `--no-verify` regex-blocked + the
obvious marker-write idioms denied at the permission layer (`Bash(touch
tmp/ci-passed:*)`, `Write`/`Edit` on the canonical paths); everything
else is a documented rule, not regex-enforced. Bash can write a file by
a dozen paths and regex enforcement is a porous game of whack-a-mole.
- **`docs/src/content/docs/claude-code.md`** — contributor-facing page
documenting the four-layer model (universal git hooks → agent gate →
ergonomic hooks → skills/agents/commands), quick setup, and discipline
rules.
- **CHANGELOG.md** — `[Unreleased]` entry covering all of the above.

## Out-of-tree GitHub changes that pair with the AI-rules cleanup

Done via `gh` CLI as part of the same audit:

- **Closed #46** (Graceful Shutdown) — verified shipped in
`cmd/wavehouse/main.go:378-393` (SIGINT/SIGTERM → bounded shutCtx →
ingestStream.Stop → srv.Shutdown → promSrv.Shutdown).
- **Scope notes added to #44, #50, #94** with current-status / boundary
info (ldflags shipped vs `/version` remaining; DLQ shipped vs retry
remaining + scope boundary with #91; per-component logger source field
as a #94 complement).
- **Opened 4 new issues from orphan TODOs**: #143 (pprof), #144 (K8s
`/healthz` + per-dep health), #145 (RequireRoles fail-closed), #146
(split `internal/api/` into focused subpackages).

## TODO.md audit (one-time, for record)

| Bucket | Count | Disposition |
|--------|-------|-------------|
| Already shipped per closed issues (#11, #14, #16, #28, #40, #41, #42,
#45) + current code | ~12 | Deleted from TODO |
| Tracked as open issues (#32, #33, #34, #37, #39, #44, #48, #49, #50,
#51, #94) | ~12 | Kept as issues, scope notes added where useful |
| In-flight via open PRs (#83, #92, #119, #122, #125, #136, #137) | 4 |
Untouched |
| #46 Graceful Shutdown | 1 | Verified shipped, closed with comment |
| Orphan items | 4 | Split into #143-146 |
| Aspirational ("more tests", "update README") | 2 | Deleted — covered
by AGENTS.md doc-sync rules |

Projects #7 board + triage automation is now the single canonical
backlog.

## Test plan

- [x] `make ci` passes locally for each push (gated by
`.githooks/pre-push`)
- [x] CI green on the latest HEAD (8fbd7db)
- [x] PR-title-lint accepts the title (`chore: claude code native
improvements`)
- [x] All `docs/src/content/docs/*.md` paths in AGENTS.md resolve to
real files
- [x] Labeler workflow auto-labels correctly per the updated paths
- [x] `pre-push-reviewer` subagent loop reached `VERDICT: ship_it` with
zero findings under the strict rubric before the final push (validated
end-to-end across five iterations on this branch — each surfacing a real
doc-sync / off-by-one / quote-strip issue and forcing a fix before the
marker auto-wrote)
- [x] `agent-bash-gate.sh` quote-strip generalization sanity-tested
live: `echo "git push to deploy"` passes through; `git push --no-verify`
and `git commit --no-verify` still block (`bash -n` clean, JSON wiring
valid)
- [ ] Human review

## Related issues

- Closed during this work: #46
- Scope notes added: #44, #50, #94
- New follow-up issues created: #143, #144, #145, #146

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


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Claude Code integration: local pre-push reviewer with strict
ship/iterate/block verdicts, push gating via CI/review markers,
automatic review-marker creation, and a coverage-reporting command.

* **Documentation**
* Comprehensive Claude Code & agent docs, new skill guides for PR
review/sync, updated README/CONTRIBUTING/CHANGELOG/styleguide, and site
sidebar/page additions.

* **Chores**
* Added git and agent hooks, CI marker creation, worktrunk config,
labeler tweaks, and simplified Copilot instructions.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/Wave-RF/WaveHouse/pull/147?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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/cache Local / shared / tiered caching area/docs Documentation, site/, README area/ingest Ingest pipeline (Bento, batching, DLQ) area/query Structured query AST, SQL builder documentation Improvements or additions to documentation go Pull requests that update go code

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

perf(cache): query cache returns stale data after writes (read-your-writes)

2 participants