Skip to content

ci(integration): split the suite into parallel parts, select by tag - #756

Merged
EricAndrechek merged 19 commits into
mainfrom
ci/integration-split
Oct 7, 2026
Merged

EricAndrechek merged 19 commits into
mainfrom
ci/integration-split

Conversation

@EricAndrechek

Copy link
Copy Markdown
Member

Summary

The Integration job grew from about 3 to about 8 minutes with the distributed-backend tests and became CI's critical path (#701). This splits the suite into parts that CI runs as parallel jobs, runs the slow self-contained tests in parallel, and selects internal/mq's integration tests by build tag instead of by name (#692).

  • Parts, not shards. make test-integration runs test-integration-app (tests/integration) and test-integration-backends (internal/cache, internal/mq/natsspike, internal/mq's tagged tests) together. The integration job is a matrix over the Makefile's INTEGRATION_PARTS, which the changes job reads (scripts/ci/integration-parts.sh refuses anything but one INTEGRATION_PARTS := … line, and every part checks it against make's own list) and also turns into the fragment and producer lists the coverage job waits for. CI runs exactly the commands a local run does, a part added to the Makefile gets its own job with no workflow edit, and each part uploads coverage-integration-<part> for the coverage job to merge. Every Go job passes its own build-cache suffix (-integration-<part>, -unit, …), since two jobs on one key race to save it and a part would otherwise save a cache holding only its own test binaries.
  • Parallel tests. Ten slow tests in tests/integration that bring up their own servers or containers (shard claims, the NATS backend, the shared cache, boot resilience, the ClickHouse outage, the query errors against a stopped ClickHouse) call t.Parallel(), and the app part runs with -parallel 8 since they mostly wait. TestShardClaims_ScaleOneThreeTwo, the dedupe DynamoDB tests and the tests that share the wired app are untouched.
  • Nothing skipped silently. scripts/ci/tagged-tests.sh run prints the tests, fuzz targets and examples internal/mq's integration-tagged files declare, replacing the -run '^Test(ExternalNATS|NewNATS|NATSPermissions_Refuse|Leases)' pattern, and tagged-tests.sh check, which every part runs first, fails while a package with tagged tests is in no part. scripts/ci/tagged-tests.test.sh checks the selection against every entry go test -list prints on a throwaway module, and scripts/ci/integration-parts.test.sh checks the parts reading (both make verify leaves).
  • internal/mq/natsspike stays in the suite: it pins the nats-server behavior the external-NATS topology rests on, and it runs beside internal/cache, so it adds no wall-clock.

Test plan

Timings on a 4-vCPU amd64 Linux box (GitHub's runners have 4 vCPUs too), warm build cache:

  • make ci green locally.
  • Baseline on main: the two gotestsum calls took 339s + 58s = 397s (tests/integration 336.7s).
  • make test-integration COV_DEFER=1: 221s, all pass. tests/integration 143.9s with the same 276 tests (test set diffed against the baseline run); backends part 76s (cache 14s alongside natsspike 17s, then internal/mq 56s, the same 217 + 95 tests).
  • test-integration-app four more times: 143.9s to 154.9s. One run failed in TestShardClaims_ScaleOneThreeTwo at the one-writer check (test(integration): TestShardClaims_ScaleOneThreeTwo flakes when a table's rows arrive across a handover after the stop #737); it fails the same way on main (the baseline run hit it), and it runs in the sequential phase, before any parallel test starts.
  • Coverage: the integration profile covers 64.60% of statements against 64.59% for the baseline, and every block the baseline covered (9883 statements in both).
  • bug(mq): internal/mq integration tests are selected by name pattern, so a new prefix is silently skipped #692: with a throwaway //go:build integration TestMQNATSSomethingNew in internal/mq, the old pattern lists 60 tests and misses it; the new selection lists 61. Without it, both select the same 60.
  • Six more test-integration-app runs on a fresh 4-vCPU box: all pass (138–160s warm, 283s for the first, cold build). -count=10 of the four parallel shard-claim tests together: 40 of 40 pass.
  • tagged-tests.test.sh (it fails against the old name-only pattern), integration-parts.test.sh, make check-integration-parts, shellcheck and actionlint pass.
  • Pre-push review: code ship_it; docs review ship_it.
  • CI: Integration tests (app) and Integration tests (backends) replace Integration tests, the coverage job finds both fragments, and the run takes about 3.5–4 minutes (it was 8–9). The first run on each new cache suffix compiles cold.

Remaining findings: none.

Related Issues

Closes #701
Closes #692
Part of #740
Stacked on #755 (base fix/prepush-gate-holes)

🤖 Generated with Claude Code

EricAndrechek and others added 14 commits October 7, 2026 06:31
Ten tests in tests/integration bring up servers or containers of their
own (a ClickHouse, a NATS server, a Redis, processes on random ports) and
spend most of their time waiting on leases, handovers and restarts. Run
serially they made up most of the package's 337s on a 4-core machine.
Marked t.Parallel, they overlap each other and the multi-process roles
test, which already ran in parallel.

Each was checked for shared state first: per-test tables and databases on
the shared ClickHouse, no t.Setenv, no captured default logger, no fixed
ports. TestShardClaims_ScaleOneThreeTwo and the tests that share the
wired app stay sequential.

Refs #701

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pre-push review gate could attest to code no reviewer read.

review-marker.sh keyed the marker to whatever HEAD the session's launch
directory had when the reviewer stopped, so a commit made while a
reviewer ran inherited its ship_it, and a review of a sibling worktree
marked the launch directory's branch instead. Reviewers now end with
`REVIEWED: <sha>` naming the commit they pinned before reading anything.
The hook writes the marker into each worktree on that commit, and only
when that worktree's HEAD reflog shows it on the commit from the start
of the review (the subagent transcript's first timestamp) until now.
Same-commit entries such as `git reset` or `git stash` don't count as
moves. A report delivered through the subagent hand-back leaves only
closing text in last_assistant_message, so the hook reads the
hand-back's message from the transcript. Every decision goes to stderr
and to tmp/review-marker.log, which the push gate prints when it blocks.

agent-bash-gate.sh checked HEAD of the session's launch directory for
every push. It now follows the push to where it runs (the tool call's
cwd, a preceding cd/pushd, git -C), resolves each refspec source to the
commit it publishes, and looks for that commit's markers in every
worktree of the repository. It blocks when it can't tell: a computed
directory or refspec, GIT_DIR, --all/--mirror, a glob refspec, a wrapped
push, or a payload without cwd. A push into another repository with no
reviewer manifest is outside the policy. Pushes through `git -C` and
from a detached HEAD were not gated at all before.

skip-pre-push-review.sh writes to the worktree root when run from a
subdirectory. .claude/hooks/review-gate.test.sh drives both hooks with
synthetic events in a scratch repository with sibling worktrees; it is a
make verify leaf (test-review-gate). Because the pre-commit hook runs it
with GIT_DIR exported, it clears git's repository variables, names every
repository with -C, and refuses to write unless git sees the scratch
directory as outside any repository and creates each scratch repository
where asked. /prepush, the reviewer agents, AGENTS.md and the Claude Code
docs page describe the REVIEWED line and the rule not to move HEAD while
reviewers run.

Closes #454
Closes #565
Closes #659

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Integration job had grown to about 8 minutes and was CI's critical
path. make test-integration now runs two parts, test-integration-app
(tests/integration, with -parallel 8 since its parallel tests mostly
wait) and test-integration-backends (internal/cache, natsspike and
internal/mq's tagged tests), at once. CI runs one matrix job per part:
the changes job reads INTEGRATION_PARTS from the Makefile
(scripts/ci/integration-parts.sh) and hands the coverage job the
fragments and producer jobs to wait for, so a part added to the Makefile
gets its own job, and CI and a local run execute the same commands. Each
part keeps its own build-cache suffix, so no part saves a cache holding
only its own test binaries.

internal/mq's integration tests were chosen by a -run name pattern, so a
new prefix would have been skipped silently. scripts/ci/tagged-tests.sh
run selects the tests that only build with the integration tag, and
tagged-tests.sh check, run before every part, fails while a package with
tagged tests belongs to no part. tagged-tests.test.sh checks the
selection against go test -list on a throwaway module (a verify leaf).

natsspike stays in the suite: it pins the nats-server behavior the
external NATS topology rests on, and it runs beside internal/cache.

Closes #701
Closes #692

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cheap filter in agent-bash-gate.sh strips quotes one line at a
time, so a heredoc body, a multi-line quoted string or a comment that
mentions `git push` reached the tokenizer, which correctly found no
push, and the gate then refused the command as one it couldn't follow.
It now refuses only when the tokens hold a `git … push` the walk didn't
gate: behind an unknown wrapper, or inside a command substitution.

The gate runs before the command line does, so in `git commit … && git
push` it judged the commit before the new one. A push that follows a
HEAD-moving git command (commit, merge, rebase, reset, checkout, switch,
cherry-pick, revert, am, pull) on the same line is now blocked with
"push in a separate command". Pushes inside if/while/until/for … do are
followed instead of refused, and the matching refspec `:` (or `+:`) is
blocked like --all instead of being read as a delete.

review-marker.sh uses the hand-back message whenever the final message
lacks either the VERDICT or the REVIEWED line, retries the transcript
read once, and resolves an abbreviated REVIEWED sha that names exactly
one commit. A ship_it without a REVIEWED line, which is what a session
started before this change produces because reviewer definitions load
at session start, logs how to recover, and the gate's block message
says the same and now shows the latest decisions from every worktree's
log.

pre-push-reviewer pins its commit in step 1 with `git -C <path>
rev-parse HEAD`. The docs say the gate checks the commit each refspec
publishes (its tip, or HEAD with no refspec), and the remaining copies
that described a VERDICT-only, HEAD-keyed marker now describe REVIEWED
plus VERDICT and the reviewed commit. The test covers each change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-ups to the integration split.

- scripts/ci/integration-parts.sh read only the first INTEGRATION_PARTS
  line, so a later `+=` or a second assignment changed what make runs but
  not CI's matrix or the coverage job's wait list. It now refuses
  anything but a single `INTEGRATION_PARTS := ...` line, and its --check
  mode, run by check-integration-parts before every part, fails when its
  list differs from make's own. integration-parts.test.sh covers the
  shapes it must refuse (a verify leaf).
- scripts/ci/tagged-tests.sh missed tagged examples and tests whose
  signature wraps across lines. It now takes every top-level func named
  Test*, Fuzz* or Example*, and its test compares the selection with
  every entry `go test -list` prints, with an example and a wrapped
  signature among the fixtures.
- Docs: AGENTS.md's CI summary, the ci.yml comments that still called e2e
  the long pole or counted three fragments, development.md's account of
  the parts and of which tests run in parallel, where a package with both
  unit and tagged tests goes, a fresh cache suffix per job in the README's
  add-a-job recipe, estimates flagged in the timing table, and a
  transition note for purging the retired -integration cache entries.

Refs #701
Refs #692

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	Makefile
#	docs/src/content/docs/development.md
agent-bash-gate.sh looked for an unfollowed push only when it had
followed none, so a second push on the same line behind a wrapper or in
a command substitution went unchecked once the first was gated. The
walk now records each simple command it gates, and every other one is
checked. GIT_DIR or GIT_WORK_TREE exported, declared or assigned earlier
on the line now makes the push's repository unknown, the same as the
inline form.

The test requires jq up front, and development.md lists jq among the
prerequisites, since make verify now runs it. pr-review-locally names
the PR worktree's path in its reviewer prompts, because reviewers pin
the worktree their prompt names, else their current directory. The
reviewer agents say an unresolvable REVIEWED sha means no marker, and
the remaining "marker for HEAD" phrasings say "for the commit being
pushed".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The missing-suffix error, the input description and the header comment
still allowed reusing another flavor's suffix when the flags match. Two
jobs on one key race to save it, which is why the integration parts each
take their own. Also restores the blank line the merge dropped before
the test-review-gate rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four gaps in the push gate, each now covered by review-gate.test.sh:

- Every check fed the command to `grep -q` through a pipe under
  pipefail. Past a pipe buffer (64 KB), grep's early exit sent SIGPIPE
  to the writer and the pipeline read as no match, so a long
  `gh pr create` without --draft, or a push ahead of a long heredoc
  body, went unchecked. The checks now read here-strings.
- An exported GIT_DIR or GIT_WORK_TREE shared a variable with
  "directory unknown", so a later absolute cd, pushd or git -C cleared
  it. It is tracked on its own now; only unset or the end of a subshell
  clears it.
- A HEAD-moving git command counted only when run directly, so
  `timeout 60 git commit … && git push` or
  `out=$(git commit …) && git push` had the push judged on the commit
  before the move. A wrapped or substituted one counts too.
- A push in code handed to a shell (bash -c, sh -c, eval, a heredoc or
  here-string read by a shell) was never seen, and one in a process
  substitution was dropped as a redirect target. The first now blocks
  as a push the gate can't follow; the second is followed like any
  other push.

The Claude Code docs now say what a gating reviewer must end its report
with for its ship_it to write a marker, matching AGENTS.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three more gaps in the push gate, each covered by review-gate.test.sh:

- A substitution in a here-string or a redirect target was dropped with
  its word, so `grep -q x <<< "$(git -C ../wt-b push)"` or
  `echo hi > "$(git push …)"` went unchecked, and a commit in one didn't
  count as a HEAD move. The tokenizer now keeps the text of every $(…)
  and `…` wherever its word goes, and the push and HEAD-move checks
  scan all of it. A quoted substitution no longer slips past the cheap
  pre-filter either.
- Inside $(…), a here-string's text was glued to `<<<`, so a push in
  `$(sh <<< 'git push')` didn't read as one.
- Code handed to a shell was scanned without joining backslash-newline
  continuations, so a push split across lines in `bash -c` or a heredoc
  read by bash went through.

AGENTS.md, the Claude Code docs and the changelog now say the gate reads
only the command line, list what it blocks, and name the forms it
doesn't follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The last change scanned the text of every $(…) for a push, but with
its quotes already stripped, so `n=$(grep -c 'git push' AGENTS.md)`,
`git commit -m "$(echo 'explain the git push gate')"` and the like were
refused as pushes the gate can't follow. The tokenizer now marks quoted
text inside a $(…), and each substitution is checked with that text
replaced by a placeholder; only a substitution that hands it to a shell
(`$(bash -c '…')`, `$(sh <<<'…')`, `$(eval '…')`) is checked in full.

Two forms the wider pre-filter now tokenizes failed to parse: a heredoc
inside $(…) closed with `EOF)"` on one line, and an `$'…'` string
holding `\'`. Both parse now; an apostrophe in a comment inside $(…)
still doesn't, and the docs say such a line is refused.

The test for the here-string spacing inside $(…) now uses `<<<'…'`
with no space, so it fails without that fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two code paths from the last change had no test: stripping the quote
markers from a word's value, and reading an $'…' string inside $(…).
Each now has cases that fail when it is removed: a substitution's output
handed to eval or bash -c, and an $'…' string in a $(…), pushed or not.

The marker strip also runs in one pass with tr. Under bash 3.2,
${_w//…} took 47 s on a 200 KB substitution holding 200 quoted strings,
past the hook's time limit; it now takes about a second.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts:
- docs/src/content/docs/development.md: kept origin/main's reworded make vulncheck row and this branch's make verify row (adds review-gate hook tests).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Conflicts:
- docs/src/content/docs/development.md: make vulncheck row from the merged-in main (govulncheck -scan package); make verify row from this branch, which already carries the review-gate hook tests plus the integration-split fixtures.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation github_actions Pull requests that update GitHub Actions code go Pull requests that update go code area/docs Documentation, site/, README area/infra CI, build, deploy, Docker, release labels Oct 7, 2026
@EricAndrechek

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 38 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 113ea56f-7215-4e38-9632-14aa86c2f1d2
📥 Commits

Reviewing files that changed from the base of the PR and between e0c78c6 and 2cb0ae6.

📒 Files selected for processing (6)
  • .github/workflows/README.md
  • CHANGELOG.md
  • Makefile
  • docs/src/content/docs/development.md
  • scripts/ci/integration-parts.sh
  • scripts/ci/integration-parts.test.sh
📝 Summary

Summary by CodeRabbit

  • CI Improvements
    • Integration tests now run as parallel app and backend jobs, with coverage results combined across both.
    • Slow, independent integration tests can run concurrently, reducing CI wait times.
  • Documentation
    • Updated development and CI guidance to explain the integration-test split, test selection, and cache requirements.
  • Verification
    • Added checks to ensure integration parts are valid and all tagged tests are assigned to the suite.

Walkthrough

The integration suite is split into app and backends parts. Make targets, test-selection checks, and CI matrix jobs run these parts separately, with per-part coverage artifacts. Several integration tests now run in parallel.

Changes

Integration test partitioning and CI

Layer / File(s) Summary
Part definitions and test selection
Makefile, scripts/ci/*
The Makefile verification targets run checks for integration-part parsing and tagged-test discovery. The scripts validate the part list, identify tagged-only tests, and test these behaviors.
Local integration execution
Makefile, tests/integration/*
The Makefile runs the app and backends parts concurrently and collects coverage. The backends part selects integration-tagged tests in internal/mq. Ten integration tests now call t.Parallel().
CI matrix and coverage artifacts
.github/workflows/ci.yml, .github/actions/setup-env/action.yml, .github/workflows/README.md
CI creates a job for each integration part and uses part-specific coverage artifacts and Go cache suffixes. Coverage polling uses generated artifact and producer lists.
Integration and cache documentation
AGENTS.md, CHANGELOG.md, CONTRIBUTING.md, .github/workflows/README.md, docs/src/content/docs/development.md
The documentation describes the integration parts, tagged-test selection, per-part coverage, cache entries, and timing estimates.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Other · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant ChangesJob
  participant IntegrationPartsScript
  participant IntegrationMatrix
  participant CoverageJob
  ChangesJob->>IntegrationPartsScript: Read and validate Makefile parts
  IntegrationPartsScript->>ChangesJob: Return part, artifact, and producer lists
  ChangesJob->>IntegrationMatrix: Supply part matrix
  IntegrationMatrix->>CoverageJob: Upload per-part coverage artifacts
  ChangesJob->>CoverageJob: Supply generated artifact and producer lists
Loading

Suggested reviewers: taitelee

Merge Risk: 🔵 Low · up to e0c78

The integration suite is now split into app and backend parts that run in parallel and are wired consistently into CI and coverage. The current configuration runs every part. However, the safety check would not detect a future edit that drops a part from the list, which would silently skip those tests. Tightening that check is a small follow-up.

🚥 Pre-merge checks | ✅ 4 | ❓ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive #692 is addressed: scripts/ci/tagged-tests.sh selects tagged test declarations, checks that tagged-test packages belong to a suite part, and has a behavioral test for selection. #701's Makefile part… Provide a CI run for this head that confirms the integration parts pass, coverage fragments merge and pass their gates, and the overall CI runtime meets the issue's target.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: splitting integration tests into parallel parts and selecting tests by build tag.
Description check ✅ Passed The description explains the integration-suite split, tagged-test selection, validation, and test results, all of which relate directly to the changeset.
Out of Scope Changes check ✅ Passed The CI matrix, cache suffixes, parallel integration tests, tagged-test safeguards, coverage handling, and related documentation support the #701 runtime objective or the #692 test-selection objective.…
Docstring Coverage ✅ Passed Docstring coverage is 95.24% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 11 files. (8 skipped: 8…
Full details: Linked Issues check

Explanation

#692 is addressed: scripts/ci/tagged-tests.sh selects tagged test declarations, checks that tagged-test packages belong to a suite part, and has a behavioral test for selection. #701's Makefile parts and CI matrix run app and backend tests concurrently. The PR reports unchanged test coverage and shorter local runtime. However, the PR description says CI validation of runtime and coverage artifacts remains unchecked, so the stated CI runtime target is not established.

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
✨ Simplify code
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

EricAndrechek and others added 3 commits October 7, 2026 13:27
Two review findings in the push gate and the marker hook, each with a
test that fails without the fix:

- The gate took local main as the base when it existed, so commits made
  on local main by mistake, then branched from (`git switch -c feat`),
  counted as already on the base and were pushed with no review. The
  base is now origin/main, and local main only when there's no
  remote-tracking ref; a stale origin/main only gates more.
- The marker hook fell back to the hand-back report whenever the final
  message lacked either parseable line, so a final `VERDICT: iterate`
  without a REVIEWED line was replaced by an earlier hand-back's
  ship_it. It now falls back only when the final message has no verdict
  or says ship_it without naming the commit.

The new-reviewer recipe in AGENTS.md and the Claude Code docs now says to
pin with `git -C <worktree> rev-parse HEAD`, as the shipped reviewers do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The header of review-marker.sh still said the final message is the
report only when it carries both parseable lines. Since the last change
it is the report whenever it has a verdict, unless that verdict is
ship_it with no REVIEWED line, and a final iterate or block stands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from fix/prepush-gate-holes to main October 7, 2026 18:47
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

📚 Docs preview is live → https://aaccb56a-wavehouse-docs.wave-rf.workers.dev

  • Commit — 2cb0ae6: fix(ci): check integration parts against their make targets
  • Author — @EricAndrechek, Claude Sonnet 5.5
  • Committed — 2026-10-07 15:14 (UTC-04:00)
  • Deployed — 2026-10-07 15:27 EDT

@EricAndrechek

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-code-quality

github-code-quality Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: Go

Go

The overall line coverage in commit 2cb0ae6 in the ci/integration-split branch remains at 93%, unchanged from commit cd4192e in the main branch.

Show a line coverage summary of the most impacted files.
File main cd4192e ci/integration-split 2cb0ae6 +/-
internal/mq/nats_topology.go 91% 92% +1%

Updated October 07, 2026 19:27 UTC

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 0868ece3-c232-4f75-8a11-b2e523c0263c
📥 Commits

Reviewing files that changed from the base of the PR and between cd4192e and e0c78c6.

📒 Files selected for processing (19)
  • .github/actions/setup-env/action.yml
  • .github/workflows/README.md
  • .github/workflows/ci.yml
  • AGENTS.md
  • CHANGELOG.md
  • CONTRIBUTING.md
  • Makefile
  • docs/src/content/docs/development.md
  • scripts/ci/integration-parts.sh
  • scripts/ci/integration-parts.test.sh
  • scripts/ci/tagged-tests.sh
  • scripts/ci/tagged-tests.test.sh
  • tests/integration/boot_resilience_test.go
  • tests/integration/ingest_outage_test.go
  • tests/integration/mq_nats_test.go
  • tests/integration/query_errors_test.go
  • tests/integration/shard_claims_test.go
  • tests/integration/shard_order_test.go
  • tests/integration/shared_cache_test.go

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Integration tests (app)
  • GitHub Check: Coverage
🧰 Additional context used
📚 Code guidelines (5)
.github/workflows/README.md — auto-discovered
docs/src/content/docs/development.md — auto-discovered
CONTRIBUTING.md — auto-discovered
AGENTS.md — auto-discovered
.github/copilot-instructions.md — auto-discovered
📓 Path-based instructions (8)
Source excerpt: **A dedicated `coverage` job applies the consolidated gate, polling — not `needs`-ing — the suites.**

📄 CodeRabbit inference engine (.github/workflows/README.md)

Files:

  • .github/workflows/ci.yml
Source excerpt: **Integration test needing Docker** → add a subtest under `tests/integration/` (e.g.

📄 CodeRabbit inference engine (docs/src/content/docs/development.md)

Files:

  • tests/integration/query_errors_test.go
  • tests/integration/mq_nats_test.go
  • tests/integration/shard_order_test.go
  • tests/integration/boot_resilience_test.go
  • tests/integration/ingest_outage_test.go
  • tests/integration/shard_claims_test.go
  • tests/integration/shared_cache_test.go
Source excerpt: Use [Conventional Commits](https://www.conventionalcommits.org/):

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Files:

  • CONTRIBUTING.md
Source excerpt: Create `*_test.go` files in the same package as the code under test.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • tests/integration/query_errors_test.go
  • tests/integration/mq_nats_test.go
  • tests/integration/shard_order_test.go
  • tests/integration/boot_resilience_test.go
  • tests/integration/ingest_outage_test.go
  • tests/integration/shard_claims_test.go
  • tests/integration/shared_cache_test.go
See [AGENTS.md](../AGENTS.md) for project conventions, architecture notes, and AI agent instructions.

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • AGENTS.md
Source excerpt: **Strict Go formatting**: Use `gofumpt` (a stricter superset of `gofmt`, enforced by CI).

📄 CodeRabbit inference engine (docs/src/content/docs/development.md)

Files:

  • tests/integration/query_errors_test.go
  • tests/integration/mq_nats_test.go
  • tests/integration/shard_order_test.go
  • tests/integration/boot_resilience_test.go
  • tests/integration/ingest_outage_test.go
  • tests/integration/shard_claims_test.go
  • tests/integration/shared_cache_test.go
Source excerpt: **Docs prose**: never hard-wrap Markdown — one paragraph is one line.

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Files:

  • CONTRIBUTING.md
  • CHANGELOG.md
  • AGENTS.md
  • docs/src/content/docs/development.md
Source excerpt: Run `make fix` to apply them.

📄 CodeRabbit inference engine (docs/src/content/docs/development.md)

Files:

  • CONTRIBUTING.md
  • CHANGELOG.md
  • AGENTS.md
  • docs/src/content/docs/development.md
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Wave-RF/WaveHouse

Timestamp: 2026-10-07T19:05:35.260Z
Learning: Source excerpt:
# AGENTS.md — AI Agent Instructions for WaveHouse

## Operating Rules

3. **Every code change updates its docs + `CHANGELOG.md` in the same PR** — a code change without its doc update is incomplete ([§Documentation Sync](#documentation-sync)).
🪛 checkmake (0.3.2)
Makefile

[warning] 856-856: Target body for "test-integration-app" exceeds allowed length of 5 lines (6).

(maxbodylength)


[warning] 865-865: Target body for "test-integration-backends" exceeds allowed length of 5 lines (11).

(maxbodylength)

🪛 LanguageTool
CHANGELOG.md

[uncategorized] ~75-~75: The official name of this software platform is spelled with a capital “H”.
Context: ...ed tests run in parallel** (Makefile, .github/workflows/ci.yml, `scripts/ci/{integra...

(GITHUB)


[uncategorized] ~75-~75: The official name of this software platform is spelled with a capital “H”.
Context: ...ims,shard_order,shared_cache}_test.go, .github/workflows/README.md, docs/src/content...

(GITHUB)


[style] ~75-~75: This sentence is over 40 words long. Consider splitting it up, as shorter sentences make the text easier to read.
Context: ...e same 276 tests and the same coverage. internal/mq's integration tests were picked by a -run name pattern, so one with a new prefix would have been skipped without a word; scripts/ci/tagged-tests.sh now picks the tests that only build with the integration tag, and every part fails while a package with tagged tests belongs to none, or while the parts CI reads from the Makefile differ from make's own list. internal/mq/natsspike stays in the su...

(TOO_LONG_SENTENCE)

AGENTS.md

[uncategorized] ~152-~152: The official name of this software platform is spelled with a capital “H”.
Context: ...me gates CI will run — the CI workflow (.github/workflows/ci.yml) is a job DAG over th...

(GITHUB)


[uncategorized] ~476-~476: The official name of this software platform is spelled with a capital “H”.
Context: ... check); caches are owned end-to-end by .github/actions/setup-env (nested `actions/cac...

(GITHUB)

docs/src/content/docs/development.md

[style] ~351-~351: Since ownership is already implied, this phrasing may be redundant.
Context: ...ternal/cache`'s integration tests start their own containers instead — Redis, Valkey, Dra...

(PRP_OWN)


[style] ~351-~351: This sentence is over 40 words long. Consider splitting it up, as shorter sentences make the text easier to read.
Context: ...uite has a 15-second limit per package. For the same reason the external NATS broker's tests (internal/mq/external*_test.go, including its run of the mqtest conformance suite) and the NATS KV lease tests (internal/mq/lease_test.go, including their run of the coordtest conformance suite) carry the integration tag inside internal/mq, and the backends part runs the tests those tagged files declare (scripts/ci/tagged-tests.sh), whatever their names, so the package's untagged tests stay in the unit suite alone. In tests/integration, most of the slo...

(TOO_LONG_SENTENCE)


[style] ~351-~351: Since ownership is already implied, this phrasing may be redundant.
Context: ..., most of the slow tests that bring up their own servers call t.Parallel()` (never one ...

(PRP_OWN)


[uncategorized] ~715-~715: The official name of this software platform is spelled with a capital “H”.
Context: ...lumes | - CI — the aggregator job of .github/workflows/ci.yml. The workflow is a jo...

(GITHUB)


[uncategorized] ~715-~715: The official name of this software platform is spelled with a capital “H”.
Context: ...che policy, how to add a job — lives in [.github/workflows/README.md](https://github.co...

(GITHUB)

.github/workflows/README.md

[typographical] ~3-~3: The word ‘How’ starts a question. Add a question mark (“?”) at the end of the sentence.
Context: How ci.yml is shaped and why. This is the canonical reference — the w...

(WRB_QUESTION_MARK)


[uncategorized] ~53-~53: The official name of this software platform is spelled with a capital “H”.
Context: ...re owned end-to-end by setup-env** ([.github/actions/setup-env](../actions/setup-env...

(GITHUB)


[style] ~55-~55: Since ownership is already implied, this phrasing may be redundant.
Context: ...r, so a part added to the Makefile gets its own job and fragment with no edit to `ci.ym...

(PRP_OWN)


[style] ~55-~55: This sentence is over 40 words long. Consider splitting it up, as shorter sentences make the text easier to read.
Context: ... ci.yml, and can't be left out of CI. A part is a set of packages: scripts/ci/tagged-tests.sh check, which every part runs first, fails while a package with integration-tagged tests is in none, and internal/mq, whose untagged tests are the unit suite's, runs the tests its tagged files declare (tagged-tests.sh run), not a -run name pattern that a new prefix would silently miss (#692). Each part's job runs exactly the com...

(TOO_LONG_SENTENCE)


[style] ~80-~80: Since ownership is already implied, this phrasing may be redundant.
Context: ...es' objects. go.mod is in the key for its own reason — the compiler's build ID keys e...

(PRP_OWN)


[style] ~121-~121: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ....mod/go.sum` or lockfile bump mints a whole new set while the previous one is still...

(EN_REPEATEDWORDS_WHOLE)

🪛 zizmor (1.30.1)
.github/workflows/ci.yml

[warning] 260-260: use GitHub's dedicated self-repository syntax (self-repository): use '$/...' instead of './...'

(self-repository)

🔇 Additional comments (4)
AGENTS.md (1)

152-152: LGTM!

Also applies to: 453-453

CONTRIBUTING.md (1)

42-42: LGTM!

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

351-351: LGTM!

Also applies to: 358-358, 531-531, 541-541


715-715: 🗄️ Data Integrity & Integration

The integration matrix and coverage artifact wiring match the claims in the cited files. No actionable discrepancy remains.

Comment thread Makefile Outdated
check-integration-parts compared the parts list with itself and handed the
tagged-package check a fixed set of packages, so dropping a part from
INTEGRATION_PARTS left both passing while its test-integration-<part>
target ran in no CI job and no local run.

integration-parts.sh now requires the list to match the Makefile's
test-integration-<part> targets one for one, and the packages the tagged
check treats as covered come from the parts listed
(INTEGRATION_PKGS_<part>), so a dropped part also uncovers its packages.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@EricAndrechek

Copy link
Copy Markdown
Member Author

@coderabbitai approve

The one finding from your review is fixed in 2cb0ae6 (see the thread).


— Posted by Claude Code on behalf of @EricAndrechek

@EricAndrechek
EricAndrechek marked this pull request as ready for review October 7, 2026 19:22
@EricAndrechek
EricAndrechek requested review from a team and taitelee October 7, 2026 19:22
@coderabbitai

coderabbitai Bot commented Oct 7, 2026

Copy link
Copy Markdown

@EricAndrechek Thanks for fixing the finding in 2cb0ae62. I’ll submit approval for #756.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

✅ Action performed

Changes approved. Review threads were not changed.

@EricAndrechek

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@EricAndrechek
EricAndrechek merged commit afd7669 into main Oct 7, 2026
@EricAndrechek
EricAndrechek deleted the ci/integration-split branch October 7, 2026 19:59
EricAndrechek added a commit that referenced this pull request Oct 8, 2026
…757)

## Summary

- The Makefile exports `GOTOOLCHAIN` derived from `go.mod`'s `go`
directive. `auto` means the newer of the local Go and `go.mod`'s, so a
newer local Go (or a future runner image) built on a different toolchain
than CI and made `make lint-go` panic. `setup-env` exports the same pin
to `$GITHUB_ENV` for CI steps that run Go outside `make`, and its
comments that called `auto` exact are corrected. The pin is strict, so
`go install` in `make tools` also runs on it.
- One script, `scripts/ci/go-toolchain.sh`, derives the pin for both the
Makefile and setup-env. It prefers go.mod's `toolchain` line and fails
with one message unless the result is a full x.y.z version, since `go
mod edit -go=1.27` writes a `go` line Go rejects as a toolchain name. A
fixture test (`make test-go-toolchain`) is wired into `make verify`.
- `development.md` notes the new coupling: raising the `go` directive to
a new minor needs a golangci-lint built with that minor or newer (patch
bumps do not), bump it with `go get go@1.N.P`, and `make
GOTOOLCHAIN=local` overrides the pin.
- `pnpm-install` no longer uses `--reporter=silent`, which prints
nothing even on failure. It captures pnpm's default output and replays
it only on failure, with `--config.confirm-modules-purge=false` so
pnpm's purge prompt cannot hang the captured output on a terminal. The
capture-and-replay fix suggested in #578 does not work with `silent`,
since there is nothing to capture.

## Test plan

- [x] `make ci` green locally.
- [x] With `GOTOOLCHAIN` unset and local Go 1.27.1, `make lint-go`
passes (it panicked before) and a make recipe running `go version`
prints `go1.26.6`.
- [x] With `minimumReleaseAge` raised temporarily in
`pnpm-workspace.yaml`, `make pnpm-install` now prints
`ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION` and exits non-zero; the old
command printed nothing. Reverted, not committed.
- [x] A successful `make pnpm-install` prints 0 bytes.
- [x] `make test-go-toolchain`, `make lint-gha`, `make lint-md`.
- [x] Pre-push review: code `ship_it`; docs review `ship_it`.
- [ ] First CI run: the "Pin and verify Go toolchain" step prints
`go1.26.6 linux/amd64`, and Lint, Unit and Integration stay green.

Remaining findings: none.

## Related Issues

Closes #580
Closes #578
Part of #740
Stacked on #756 (base `ci/integration-split`)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
EricAndrechek added a commit that referenced this pull request Oct 9, 2026
)

Repeated pushes to a PR reused no Go build objects. Measured from CI
logs (2026-10-06 to 10-08), that only matters on a PR that changes
`go.mod`/`go.sum`, so only that case is cached, and only for the two
long-pole jobs.

- **Measured, typical PR.** The integration suite's `app` part compiles
in 5.8–8.5 s from `main`'s entry, against 2.3–3.2 s from a same-tree
one. e2e's 4–7 s cover build is hidden behind its SDK build. A PR-scoped
cache would buy about 4–5 s per push, which is inside the `app` part's
run-to-run spread, so these PRs still save nothing.
- **Measured, dependency-changing PR (#730, #742, #753).** Every push
recompiles the dependency delta from `main`'s generation: 35–50 s in the
integration suite and a 37–49 s cover build in e2e, against about 3 s
and 4 s from the PR's own entry. That is about 30–45 s per push on the
critical path. Of 129 PR runs since 2026-09-25, 34 came from 9 such PRs,
and 25 of those were second or later pushes.
- **Classifier.** `scripts/classify-paths.sh` also emits `go-deps`: any
`go.mod`/`go.sum` in the change set, in any module, which are the files
the cache keys hash. `classify-changes.sh` passes it through only for a
cleanly classified pull request and sets it false everywhere it fails
closed. The `changes` job exposes it as an output.
- **setup-env.** A new `go-cache-pr-save` input, which the `app` part
and e2e set only when `go-deps` is true. So a PR that changes no
dependency never saves, even while `main` holds no entry for its key:
just after a bump lands on `main`, after a failed `main` run, or for a
newly opted-in job. With the input set, the job looks up the exact key
first, from the PR's scope or `main`'s, so a PR whose dependency change
`main` already has hits `main`'s entry and saves nothing. Only on a miss
does it restore the newest entry of its flavor (the PR's own, then
`main`'s) and save the exact key into the PR's scope. The key is per
dependency state, so a PR saves once per dependency change. Merge-queue
refs and branches stay restore-only, and nothing is added to `ci.yml`
beyond the two inputs.
- **Pruning.** `pr-cache-cleanup.yml` gains a `workflow_run` job that
runs after every PR's CI run. It runs the new
`scripts/ci/prune-pr-build-cache.sh` from the default branch with
`actions: write` and deletes every PR-scoped `gobuild-v3` entry that
`main` also holds or that a newer entry of the same flavor supersedes.
It deduplicates the paged listing before grouping, since a row returned
twice would otherwise delete the PR's newest entry, and a fixture test
(`make test-prune-pr-build-cache`, a new `make verify` leaf) pins the
selection and the failure handling. No PR code holds the write token.
The job needs no PR number, so it covers fork PRs too. A PR holds at
most about 0.48 GiB of build cache (double that for the minutes before a
prune).
- **Budget.** The README sizes this for four such PRs open at once, the
most open at once in the last two weeks. The worst case at rest is about
11.7 GiB, about 1.7 GiB over the cap, and eviction by last access takes
it from `main`'s superseded generation, which nothing reads (inferred).
At two such PRs everything fits. `main`'s live entries stay safe up to
six: 3.9 + N × (0.5 CodeQL + 0.48 build) ≤ 10 GiB, assuming the
superseded generation is the size of the live one. The six build flavors
together are 1.02 GiB on `main`; the issue's "about 1.3 GiB per suffix"
was the total for all six of a PR's flavors.

## Test plan

This branch's diff matches #783's (same 13 files, +450/−67), which
CodeRabbit approved; the only line differences are the verify-leaf list
and the `make verify` table row, which differ because the base did.
Re-run here: the pre-commit `make verify` (which runs both fixture
suites below) and a full `make ci`. Items marked "measured on #783" were
not repeated; the CI items are observable once this PR targets `main`.

- [x] `make lint-gha` (actionlint, which also checks the new input and
the `go-deps` output) and `make lint-sh` clean; `make verify` green in
the pre-commit hook, including the new leaf.
- [x] `make test-classify-paths`: new `go-deps` cases (root and nested
modules, look-alike names, mixed with docs, empty, large input); a
mutant that matches only the root `go.mod`/`go.sum` fails it (mutant
measured on #783). `classify-changes.sh` against a stub `gh`:
`go-deps=true` only for a pull request whose file list holds a `go.sum`;
false on push, merge group, dispatch and a failed listing.
- [ ] (measured on #783) The setup-env step conditions, evaluated for
push to `main`, `pull_request` with and without the input, `merge_group`
and `workflow_dispatch`: exactly one restore path runs, and only a PR
miss with the input set is save-capable.
- [ ] (measured on #783) `prune-pr-build-cache.sh --dry-run` against the
live cache list selects 6 entries (1058 MiB): copies of `main`'s keys
under #731, #754 and #756. It keeps #753's own entries (one per flavor,
not on `main`) and never touches `main` or merge-queue scopes.
- [x] `make test-prune-pr-build-cache`: deletes copies of `main`'s key
and version and keeps a same key with another version; deletes
superseded entries and the older of a same-second pair; keeps
`-integration-` apart from `-integration-app-`; ignores other families
and `main`/merge-queue scopes; keeps a PR's only entry listed twice.
Through a stub `gh`: a 404 reads as already gone, a 403 fails after the
rest are attempted, and a failed listing fails. Mutants caught (measured
on #783): no early dedup (the pre-fix script), key-only match, merged
flavors, no prefix filter, `main` scope included, listing failure
ignored, 404 treated as an error, 403 swallowed.
- [ ] This PR's own run, which changes no dependency: `Detect changes`
logs `go-deps=false`, and every Go job, the `app` part and e2e included,
uses the plain restore step, with no `Cache saved` for `gobuild-v3` in
any post step.
- [ ] After merge, the first `PR cache cleanup` run triggered by a PR's
CI (`Prune PR build caches`): passes and deletes the entries the dry run
listed, if they are still there.
- [ ] The first PR that changes `go.mod`/`go.sum` (the next Dependabot
go-deps PR): `Detect changes` logs `go-deps=true`; the first push
restores `main`'s generation through restore-keys and saves
`gobuild-v3-Linux-go-integration-app-<its hash>` and `-e2e-cov-<its
hash>` under `refs/pull/<n>/merge`; the second push exact-hits them, its
integration compile is about 3 s and e2e reaches its containers about 20
s after `make` starts; the prune leaves one entry per flavor; the close
cleanup deletes them. A fork PR's save is unverified: if GitHub refuses
it, the job only logs a warning.
- [x] `make ci` passes on this branch, after merging `main` (#793 and
#788).

## Related Issues

Closes #781. Refs #535 (two generations on `main` still leave thin
headroom). Part of #740.

Supersedes #783. Now based on `main`: its parent, the `-trimpath` PR
#793, has merged. It does not itself use `-trimpath` or `make
ci-remote`.

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

https://claude.ai/code/session_01Wjv2nu841pxTMdUWSAPmDH

---------

Co-authored-by: Claude Opus 5.5 <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/infra CI, build, deploy, Docker, release documentation Improvements or additions to documentation github_actions Pull requests that update GitHub Actions code go Pull requests that update go code

Projects

Archived in project

1 participant