Skip to content

fix(ingest): break infinite-Nak loop on permanent delete errors - #122

Merged
EricAndrechek merged 10 commits into
mainfrom
issue-91
May 15, 2026
Merged

EricAndrechek merged 10 commits into
mainfrom
issue-91

Conversation

@EricAndrechek

@EricAndrechek EricAndrechek commented May 12, 2026 •

Copy link
Copy Markdown
Member

Summary

Phase 1 of issue #91 — addresses Phase 1 of #91. Does not close the issue: #91 stays open after this lands so Phase 2 (transient-vs-permanent error classification) has a visible tracker.

jsInput.Read's action: "delete" block called m.Nak() on every chConn.Exec failure, which JetStream interprets as "redeliver immediately." A delete whose error was deterministic (syntax error, unknown table, malformed identifier) looped forever — clogging the buffer consumer, burning CPU, and spamming logs with the same message.

This change treats every delete-Exec error as permanent:

  1. Logs the failure as ERROR with table, id, and underlying error.
  2. Publishes the original NATS envelope (m.Data()) to dlq.<table> so operators can inspect what failed. Reuses the existing bentoDLQDropped counter if the DLQ publish itself fails.
  3. Calls m.DoubleAck(ctx) so the message is removed from the main queue.
  4. continues to the next message (no more return … fmt.Errorf("execute delete: %w", err) propagating into Bento's reconnect path).

Phase 2 — distinguishing transient errors (timeouts, network) which should still Nak() for retry from permanent ones (syntax, unknown table) — is left for a follow-up PR. Issue #91 remains the tracker.

Files changed

  • internal/ingest/bento.go — new behavior in the delete-error branch + js jetstream.JetStream threaded into jsInput.
  • internal/ingest/bento_test.go — TestJsInput_Read_DeleteExecError rewritten to assert the new behavior; new TestJsInput_Read_DeleteExecErrorDLQPublishFails covers the DLQ-publish-also-fails path (must still DoubleAck).
  • docs/architecture.md — package summary + ingest data-flow updated.
  • AGENTS.md — ingest/ quick-reference mentions the permanent-error policy.
  • CHANGELOG.md — Fixed entry under [Unreleased].

Test plan

  • make ci passes locally (unit 73.4%, integration 17.2%, e2e 51.6%, sdk 53.2%, merged 81.1%)
  • internal/ingest coverage 85.6% — new tests cover the success path (msg routed to DLQ + DoubleAck) and the failure path (DLQ publish errors → still DoubleAck'd to break the loop)
  • CI green on the runner (first run hit an unrelated ClickHouse handshake flake on the E2E orchestrator boot path; re-running)
  • Claude review clean (0 MUST, 0 SHOULD, 1 MAY — addressed)
  • Gemini review clean
  • Admin approval

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Ingest pipeline now treats failed deletes as permanent: original messages are routed to the dead-letter queue and double-acked to prevent infinite retry loops.
  • Documentation

    • Architecture and changelog updated to clarify delete handling, DLQ routing, and Phase 1 retry semantics.
  • Tests

    • Added/updated tests to verify delete-failure handling, DLQ publish failure behavior, and ack semantics.

Review Change Stack

Phase 1 of issue #91 — every chConn.Exec failure on an `action: "delete"`
message used to trigger m.Nak(), which JetStream interprets as "redeliver
immediately." A deterministic error (syntax, unknown table, malformed
identifier) would loop forever — clogging the buffer consumer, burning
CPU, and spamming logs with the same message.

Now the worker treats every delete-Exec error as permanent: it publishes
the original NATS envelope to dlq.<table> (reusing the bentoDLQDropped
counter when the DLQ publish itself fails) and DoubleAcks so the message
leaves the main queue.

Phase 2 (transient vs permanent classification — timeouts and network
errors should still Nak) remains as follow-up on the issue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@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 addresses an issue where deterministic errors during delete operations in the Bento ingest pipeline caused infinite retry loops, leading to log spam and resource exhaustion. By reclassifying these errors as permanent and routing the affected messages to a dead-letter queue, the system now gracefully handles malformed delete requests while ensuring the main queue continues processing.

Highlights

  • Infinite Retry Loop Prevention: Modified the ingest worker to treat all delete-execution errors as permanent, preventing infinite Nak loops caused by deterministic errors like syntax issues or unknown tables.
  • DLQ Integration: Failed delete operations are now published to a dead-letter queue (dlq.) and explicitly DoubleAck'd to remove them from the main processing queue.
  • Resilience: Added logic to ensure that even if the DLQ publish fails, the message is still DoubleAck'd to maintain pipeline throughput and prevent blocking.
  • 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. ↩

@github-actions github-actions Bot added documentation Improvements or additions to documentation go Pull requests that update go code area/ingest Ingest pipeline (Bento, batching, DLQ) area/docs Documentation, site/, README labels May 12, 2026
@github-actions
github-actions Bot requested a review from taitelee May 12, 2026 22:30
Comment thread CHANGELOG.md Outdated
@claude

claude Bot commented May 12, 2026

Copy link
Copy Markdown

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

The fix is correct. The infinite-Nak root cause is real, the Phase 1 permanent-error policy is a sound stopgap, the new js wiring is clean, and the two new test cases (TestJsInput_Read_DeleteExecError and TestJsInput_Read_DeleteExecErrorDLQPublishFails) cover exactly the invariants that matter: no Nak on exec error, DoubleAck regardless of DLQ publish outcome. Documentation sync is done (CHANGELOG.md, docs/architecture.md). One minor observation: AGENTS.md §Architecture's quick-reference for ingest/ still says "plus delete handling" without mentioning the permanent-error policy — the doc-sync rule maps architecture changes to both docs/architecture.md and AGENTS.md; the former is updated, the latter is not. Given how brief the AGENTS.md blurb is it's a very small miss, but worth a one-line addition in a follow-up.

Ship it — the only thing to chase down is opening a Phase 2 tracking issue before #91 auto-closes on merge.

@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 addresses issue #91 by preventing infinite retry loops for permanent delete errors in the ingest worker. Specifically, in internal/ingest/bento.go, failed delete operations are now treated as permanent; the original message is routed to a table-specific DLQ subject and then DoubleAck'd to remove it from the main queue. The changes include updates to the jsInput struct to include a JetStream client, logic to handle DLQ publishing, and comprehensive test coverage for these failure scenarios. Documentation in docs/architecture.md and the CHANGELOG.md have been updated accordingly. I have no further feedback to provide.

Addresses Claude review feedback on PR #122:
- CHANGELOG.md no longer says "Closes #91" so the issue stays open after
  merge to track Phase 2 (transient-vs-permanent error classification).
- AGENTS.md §Architecture's `ingest/` quick-reference now mentions the
  permanent-error policy on delete failures (DLQ + DoubleAck, not Nak).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@EricAndrechek

Copy link
Copy Markdown
Member Author

Thanks for the review — all 1 MAY addressed in 26ba089:

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@claude

@claude

claude Bot commented May 12, 2026

Copy link
Copy Markdown

0 [MUST], 0 [SHOULD], 0 [MAY] — follow-up commit 26ba089 correctly addresses the one finding from the prior round.

AGENTS.md ingest/ blurb now explicitly states the permanent-error policy (failed deletes → dlq.<table> + DoubleAck). CHANGELOG no longer implies #91 auto-closes; it stays open as the Phase 2 tracker. closingIssuesReferences on the PR is empty, so GitHub won't close it on merge. Both changes match exactly what was requested.

No new findings in the follow-up commit.

Ship it — pending CI green and admin approval.

@EricAndrechek

Copy link
Copy Markdown
Member Author

Thanks Gemini — noting the clean review, no follow-up required on my end. The Phase 1 trade-off (all delete errors → permanent) is intentional per issue #91; Phase 2 (transient-vs-permanent classification so timeouts and network blips still Nak()) stays tracked on the same issue rather than auto-closing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@gemini-code-assist

@gemini-code-assist

Copy link
Copy Markdown

Thanks for the update, Eric. The changes in 26ba089 look solid and correctly address the documentation and tracking requirements discussed. The implementation of the permanent-error policy for delete operations, along with the robust handling of DLQ publish failures, effectively mitigates the infinite-retry loop issue. The added test coverage in internal/ingest/bento_test.go provides good assurance for these invariants. This looks ready for merge pending your final CI/CD checks.

@EricAndrechek EricAndrechek moved this from Backlog to In review in WaveHouse Task Board May 13, 2026
@coderabbitai

coderabbitai Bot commented May 13, 2026 •

Copy link
Copy Markdown

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

Walkthrough

The ingest worker treats ClickHouse DELETE Exec failures as permanent: it publishes the original envelope to dlq.<table> and DoubleAcks the message (no NAK), continues processing, and has matching tests and documentation updated.

Changes

Permanent Delete Failure Handling

Layer / File(s) Summary
JetStream integration & delete error handling
internal/ingest/bento.go
jsInput adds a js JetStream field. On ClickHouse DELETE Exec failure, the code publishes the original message to dlq.<table>, increments the DLQ-dropped metric if publish fails, DoubleAcks the message, ends the span, and continues instead of NAK-and-return-error. Stream setup injects the JetStream handle.
Test coverage for delete & DLQ publish failures
internal/ingest/bento_test.go
TestJsInput_Read_DeleteExecError now asserts the delete envelope is published to dlq.clicks, the delete message is not NAK'd and is DoubleAcked, and Read returns the next insert message without error. Added TestJsInput_Read_DeleteExecErrorDLQPublishFails to verify behavior when DLQ publish fails (still DoubleAck, no NAK, continue). JetStream mocks extended to capture PublishMsg headers and return publish errors.
Documentation and changelog updates
AGENTS.md, CHANGELOG.md, docs/src/content/docs/architecture.md
Documentation updated to describe inline delete execution, DLQ routing for failed deletes, DoubleAck behavior on success and failure, and Phase 1 permanent-classification handling. CHANGELOG entry documents the fix and references issue #91.

Sequence Diagram

sequenceDiagram
  participant IngestWorker
  participant ClickHouse
  participant JetStream
  IngestWorker->>ClickHouse: Execute DELETE
  ClickHouse-->>IngestWorker: Exec error
  IngestWorker->>JetStream: Publish original envelope to dlq.<table> (with Wave-DLQ-Type header + trace)
  IngestWorker->>IngestWorker: DoubleAck original message
  IngestWorker->>IngestWorker: Continue to next message
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #91: Implements the permanent delete error handling: publish failed DELETE envelopes to dlq.<table> and use DoubleAck instead of Nak.

Poem

🐰 I nibbled at a failing delete,
Sent the envelope where DLQs meet,
A double tap to end the spin,
No NAK loop to pull me in,
Bento hops forward, tidy and neat.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing an infinite NAK loop caused by permanent delete errors in the ingest pipeline by implementing permanent-failure routing to DLQ.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-91

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

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Around line 9-10: The "### Fixed" heading in CHANGELOG.md violates
markdownlint MD022; add one blank line immediately above the "### Fixed" heading
and one blank line immediately below it so the heading is separated from
surrounding text, then commit the change (no code changes required beyond
editing the CHANGELOG.md heading spacing).

In `@docs/src/content/docs/architecture.md`:
- Around line 159-161: The documentation is inconsistent: the flow currently
says "Ack messages" but the implementation uses DoubleAck for successful
inserts; update the wording by replacing the "Ack messages" phrase with
"DoubleAck" so the doc matches the implementation (search for the exact string
"Ack messages" and change it to "DoubleAck" in the same flow, keeping the
surrounding lines about routing to dlq.{table} and the Phase 1 note intact).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 17b4e3a1-9ebb-4495-b261-8e2107e8c3a9

📥 Commits

Reviewing files that changed from the base of the PR and between 7855982 and e95a514.

📒 Files selected for processing (5)
  • AGENTS.md
  • CHANGELOG.md
  • docs/src/content/docs/architecture.md
  • internal/ingest/bento.go
  • internal/ingest/bento_test.go

Comment thread CHANGELOG.md Outdated
Comment thread docs/src/content/docs/architecture.md Outdated
@github-project-automation github-project-automation Bot moved this from In review to Ready in WaveHouse Task Board May 13, 2026
Comment thread internal/ingest/bento.go Outdated
@claude

claude Bot commented May 13, 2026

Copy link
Copy Markdown

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

The fix is correct and the design is sound. The infinite-Nak root cause is real, the Phase 1 permanent-error policy is an appropriate stopgap, and the two new test cases cover exactly the invariants that matter: no Nak on exec error, DoubleAck unconditionally regardless of DLQ publish outcome. safeIdentifierRe validation upstream of the delete branch ensures the dlq. subject concatenation is injection-safe. Documentation sync (AGENTS.md, CHANGELOG.md, architecture.md) is complete and correct.

One [SHOULD]: dlq.<table> now carries two structurally different message shapes — insert failures publish the extracted data payload (via dlqOutput.WriteBatch), delete failures publish the full NATS envelope (via m.Data()). A DLQ consumer can't assume a uniform format. See the inline thread for the specific remediation options; documenting the contract is the ask, not restructuring the code.

Ship it — the [SHOULD] is documentation only and can be addressed with a one-line comment or a short addition to architecture.md before merge.

…k wording

Addresses bot-review feedback on PR #122:

- bento.go: comment above the delete-DLQ Publish noting the message-shape
  difference vs dlqOutput.WriteBatch — insert failures publish raw.Payload
  (inner data only), delete failures publish m.Data() (full envelope) so a
  consumer can re-issue the delete. Phase 2 (issue #91) may normalize.
- docs/src/content/docs/architecture.md: explicit DLQ-shape contract on the
  ingest/ blurb so future consumers find it; and fix the data-flow line
  that read "Ack messages" — the code uses DoubleAck.
- CHANGELOG.md: blank line above the new "### Fixed" heading (MD022).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread CHANGELOG.md Outdated

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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Line 11: Update the changelog entry to reference the current docs path by
replacing the stale `docs/architecture.md` string with
`docs/src/content/docs/architecture.md` in the CHANGELOG.md entry (the line
describing Ingest worker changes that currently includes
`docs/architecture.md`); ensure the updated path appears exactly in the same
list of affected files so the changelog points to the correct documentation
location.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e21d8a07-8870-4868-9039-6e3cf14e2ddc

📥 Commits

Reviewing files that changed from the base of the PR and between e95a514 and 34f742f.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • docs/src/content/docs/architecture.md
  • internal/ingest/bento.go

Comment thread CHANGELOG.md Outdated
@claude

claude Bot commented May 13, 2026

Copy link
Copy Markdown

0 [MUST], 0 [SHOULD], 0 [MAY] — no inline threads.

Head commit 34f742f correctly addresses all findings from prior rounds:

  • DLQ shape contract (my prior [SHOULD], commit e95a514): the two structurally different shapes on dlq.<table> (data-payload for insert failures, full envelope for delete failures) are now documented in both the bento.go inline comment and architecture.md. The Phase 2 normalization intent is noted.
  • MD022 (CodeRabbit): blank line added before ### Fixed in CHANGELOG.md.
  • DoubleAck wording (CodeRabbit): → On success: Ack messages correctly replaced with → On success: DoubleAck messages in the data-flow section.

The fix itself remains sound: the infinite-Nak root cause was real, the Phase 1 permanent-error policy is an appropriate stopgap, safeIdentifierRe validation upstream makes the dlq. subject concatenation injection-safe, and the two tests (TestJsInput_Read_DeleteExecError, TestJsInput_Read_DeleteExecErrorDLQPublishFails) cover the invariants that matter — no Nak on exec error, DoubleAck unconditionally regardless of DLQ publish outcome.

Ship it — pending CI green and admin approval.

…re.md

Stale path artifact from the merge — main moved docs/ to Astro layout
under docs/src/content/docs/ but my Phase-1 CHANGELOG entry still listed
the pre-merge path. Caught by CodeRabbit on the round-2 review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread tests/integration/ingest_test.go Outdated
Comment thread tests/integration/dlq_test.go Outdated
@claude

claude Bot commented May 14, 2026 •

Copy link
Copy Markdown

0 [MUST], 0 [SHOULD], 0 [MAY] — no inline threads.

Both findings from the prior round (commit 55da248) are correctly addressed in the latest commit 747a747:

  • [SHOULD] pollCount SQL parameterization — id is now bound via ? driver parameter (not string interpolation). The table identifier still uses fmt.Sprintf + backtick quoting, which matches exactly how bento.go handles identifiers — correct.
  • [MAY] Global NumAckPending assertion — replaced with postInfo.AckFloor.Stream >= pubAck.Sequence, which targets the specific message's stream sequence rather than a global consumer-quiescence claim. A Nak'd message would leave AckFloor.Stream stuck below pubAck.Sequence until AckWait expires, so this precisely tests the invariant the PR is fixing.

The core fix remains sound and has been extensively reviewed: safeIdentifierRe validation upstream makes the dlq.<table> subject concatenation injection-safe, the Wave-DLQ-Type header is a non-user-controlled BYOS discriminator, span.End() fires on all paths, and DoubleAck fires unconditionally even when DLQ publish itself fails — the loop-breaking invariant holds under every failure mode. Documentation sync (AGENTS.md, CHANGELOG.md, docs/src/content/docs/architecture.md) is complete and accurate.

Ship it — pending CI green and admin approval.

Two refinements from Claude's review of 55da248 on PR #122:

pollCount in ingest_test.go used fmt.Sprintf with single-quote id
interpolation and a raw (unquoted) table identifier. Current callers pass
controlled constants so there's no live injection risk, but the function
signature accepts arbitrary strings and would be copied verbatim by future
test authors. Switch to the same pattern bento.go:172 uses in production:
backtick-quoted table identifier + driver-bound ? parameter for the id.
Comment notes why the table can't be a bound parameter (CH identifiers
aren't bindable; backticks are the same defense the production path uses).

TestDelete_FailureRoutesToDLQWithHeader's no-redelivery assertion used
NumAckPending == 0, which is a global claim about the entire buffer
consumer rather than a claim about our specific message. If some unrelated
test left a Nak'd message whose AckWait hadn't elapsed, this would time out
with a misleading "must DoubleAck the failed delete" error. Switch to
capturing pubAck.Sequence from js.Publish and asserting
AckFloor.Stream >= pubAck.Sequence — that targets THIS message's stream
seq, immune to global consumer state. The pre/post Delivered snapshot is
no longer needed; removed.

Per Claude on PR #122 / commit 55da248.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@taitelee taitelee 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.

Good. I was looking for the DoubleAck call because that is also what I had done to solve part of this issue the first time I ran into it. Now we have SQL DELETE support for te ingest worker, using an in flight drain approach to make sure pending insert batches finish before a delete executes. To prevent infinite retry loops on malformed queries, failed deletes now follow a permanent-fail policy, so bypassing redelivery and routing directly to the NATS DLQ.

@EricAndrechek
EricAndrechek merged commit 79f9bfa into main May 15, 2026
11 checks passed
@EricAndrechek
EricAndrechek deleted the issue-91 branch May 15, 2026 17:44
@github-project-automation github-project-automation Bot moved this from In progress to Done in WaveHouse Task Board May 15, 2026
@EricAndrechek EricAndrechek mentioned this pull request May 15, 2026
7 of 8 tasks
EricAndrechek added a commit that referenced this pull request May 18, 2026
…ator

After merging main's boot-resilience and health work (#125, #122), e2e
coverage dipped to 49.9% (gate is 50%) because the Readiness handler and
the cmd/wavehouse/health.go probe binary were uncovered by the SDK
harness. Both are production code paths the operator-facing contract
(k8s readiness, Docker HEALTHCHECK) depends on — exercising them in the
e2e harness is principled, not a coverage hack.

Brings e2e from 49.9% → 50.9%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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/docs Documentation, site/, README area/ingest Ingest pipeline (Bento, batching, DLQ) 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.

2 participants