Skip to content

ci: cancel a PR's still-running CI when the PR closes - #107

Merged
astubbs merged 3 commits into
masterfrom
ci/cancel-runs-when-pr-closes
Aug 3, 2026
Merged

ci: cancel a PR's still-running CI when the PR closes#107
astubbs merged 3 commits into
masterfrom
ci/cancel-runs-when-pr-closes

Conversation

@astubbs

@astubbs astubbs commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Description

Merging or closing a PR does not stop its in-flight CI. GitHub leaves those runs to finish, and on hosted runners that's mostly harmless — but this repo's heavy suites run on a small self-hosted pool, where an abandoned run holds an executor a live PR needs.

Observed on 2026-08-03: five branches' runs queued against five online runners (highcpu-1 offline), and three of those branches had already merged.

The existing per-suite concurrency in pr-highcpu-fast-feedback.yml can't help here. Its group is highcpu-${suite}-${ref}, so it only supersedes the same suite on the same branch — and nothing about a merge looks like a new push.

What it does

On pull_request: closed, cancels that PR's queued / in_progress / waiting / requested / pending runs for its head branch.

Applies to closed-unmerged too — an abandoned PR's run is just as wasteful as a merged one's.

Two guards, both deliberate:

  • Refuses to act when the head ref is the default branch. A merge queues master's own CI under that name, and cancelling the merge commit's build would be actively harmful.
  • Skips runs whose head repository differs from the PR's. Branch names are not unique across forks, so feature/x on a fork must not cancel feature/x here.

A 409 from a run that finished between listing and cancelling is logged rather than failed — the goal (that run no longer holds a runner) is already met.

Needs actions: write to cancel runs.

Notes

  • Validated with actionlint.
  • Doesn't address the other half of what was observed: highcpu-1 and mac-laptop are offline, so the pool is running at reduced capacity. That's an infrastructure matter, not a workflow one.

Checklist

  • Changelog entry added (CHANGELOG.adoc) - Build & CI, citing this PR; README.adoc regenerated
  • Docs updated - N/A - the workflow's own header comment carries the rationale, including why the existing concurrency cannot cover this case
  • Tests added/updated - N/A - CI workflow; validated with actionlint. It cannot be exercised before merge (it triggers on pull_request: closed), so this PR's own merge is its first real run
  • Title & body reflect the final content of this PR
  • Self-hosted runner / security implications considered - directly motivated by self-hosted capacity. Runs on ubuntu-latest, not the self-hosted box. Adds actions: write, scoped by the two guards above so it can only cancel runs belonging to the closing PR's own head repo and never the default branch

🤖 Generated with Claude Code

Merging or closing a PR does not stop its in-flight runs - GitHub leaves
them to finish. Mostly harmless on hosted runners, but this repo's heavy
suites run on a small self-hosted pool, where an abandoned run holds an
executor a live PR needs.

Observed 2026-08-03: five branches' runs queued against five online
runners (highcpu-1 offline), and three of those branches had already
merged. The per-suite concurrency in pr-highcpu-fast-feedback.yml cannot
help - it only supersedes the same suite on the same branch, and nothing
about a merge looks like a new push.

Covers closed-unmerged too, since an abandoned PR's run is just as
wasteful. Two guards worth keeping: it refuses to act when the head ref
is the default branch (a merge queues master's own CI under that name,
and cancelling the merge commit's build would be actively harmful), and
it skips runs whose head repository differs from the PR's, because
branch names are not unique across forks. A 409 from a run that finished
between listing and cancelling is logged, not failed - the goal is
already met.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

OpenSSF Scorecard

PackageVersionScoreDetails
actions/actions/github-script 7.*.* 🟢 6.7
Details
CheckScoreReason
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Code-Review🟢 10all changesets reviewed
Packaging⚠️ -1packaging workflow not detected
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions🟢 9detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 1dependency not pinned by hash detected -- score normalized to 1
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
SAST🟢 10SAST tool is run on all commits
Branch-Protection🟢 6branch protection is not maximal on development and all release branches

Scanned Files

  • .github/workflows/cancel-closed-pr-runs.yml

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
@claude

claude Bot commented Aug 3, 2026

Copy link
Copy Markdown

Claude finished @astubbs's task in 3m 48s —— View job


Code Review: ci: cancel a PR's still-running CI when the PR closes

  • Gather context (PR diff, workflow file, repo conventions)
  • Review .github/workflows/cancel-closed-pr-runs.yml for correctness and security
  • Review CHANGELOG.adoc / README.adoc changes
  • Post findings

Overall this is a well-scoped, well-commented workflow — the self-run exclusion, default-branch guard, and 409-as-success handling are all correct. One finding undermines the "skip a different head repository" guard the PR specifically calls out as a safety property, so it's worth a look before merge.


1. Cross-repo guard is bypassed when the PR's source fork has been deleted (Medium)

.github/workflows/cancel-closed-pr-runs.yml:36,62-68

const headRepo = pr.head.repo ? pr.head.repo.full_name : null;
...
const runRepo = run.head_repository ? run.head_repository.full_name : null;
if (headRepo && runRepo && runRepo !== headRepo) {
  core.info(`Skipping run ${run.id} (${run.name}) - head repo ${runRepo} is not ${headRepo}.`);
  continue;
}

GitHub sets pull_request.head.repo to null when the PR's source repository (fork) has been deleted — a common occurrence, since many contributors delete their fork/branch right after merge. In that case headRepo is null, and if (headRepo && runRepo && runRepo !== headRepo) short-circuits to false for every run, regardless of runRepo — the exact guard the PR description calls out ("Branch names are not unique across forks, so feature/x on a fork must not cancel feature/x here") is silently disabled. The job would then cancel any run matching headRef across all repos, including an unrelated, currently in-flight PR from a different contributor that happens to share the same branch name (e.g. fix, feature, main).

Suggested fix — treat "can't verify the owning repo" as "don't touch it", not "no filter":

if (runRepo && (!headRepo || runRepo !== headRepo)) continue;

2. actions: write is a no-op for fork-originated PRs (Informational, low impact here)

GitHub issues a read-only GITHUB_TOKEN for pull_request-triggered runs originating from a forked repository, regardless of the permissions: block — so closing a fork PR here will hit a 403 on cancelWorkflowRun, silently swallowed by the existing catch-and-log. In practice this doesn't undercut the PR's stated goal: pr-highcpu-fast-feedback.yml already restricts the self-hosted heavy jobs (the actual scarce resource) to same-repo PRs only, so the runner-starvation scenario this PR targets is unaffected. Just worth knowing this workflow can't clean up a fork PR's hosted-runner runs either, in case that assumption changes.


Other checks:

  • CHANGELOG.adoc / README.adoc: entries match, correctly cite ci: cancel a PR's still-running CI when the PR closes #107, consistent with the PR checklist convention added in ci(pr-checklist): require new changelog entries to cite their PR #104.
  • Workflow YAML structure and github-script usage read as syntactically sound (author notes actionlint was run; not independently re-run here since no network/CLI tooling access in this session for that).
  • waiting/pending/requested/queued/in_progress are all valid values for the status filter on listWorkflowRunsForRepo.
  • Self-run exclusion (run.id === context.runId), default-branch refusal, and the 409-swallow are all correct as written.

@claude

claude Bot commented Aug 3, 2026

Copy link
Copy Markdown

Claude finished @astubbs's task in 2m 4s —— View job


Review: PR #107 — cancel a PR's still-running CI when the PR closes

  • Gather context (diff, changed files, AGENTS.md)
  • Review new workflow file cancel-closed-pr-runs.yml
  • Review CHANGELOG/README changes
  • Cross-check claims (concurrency group scoping, referenced docs)
  • Post final review

Summary

Solid, well-documented addition. The two stated guards (refuse on default branch, skip mismatched head repos) are both implemented, the 409-as-non-failure handling is correct, and the self-skip (run.id === context.runId) prevents the job from cancelling itself. I checked the claim that pr-highcpu-fast-feedback.yml's concurrency group is highcpu-${suite}-${ref} — confirmed at .github/workflows/pr-highcpu-fast-feedback.yml:60, so the stated gap (same-suite/same-branch only, nothing about a merge looks like a new push) is accurate. docs/SELF_HOSTED_RUNNER.md, referenced in the header comment, exists. CHANGELOG/README entries match and correctly cite #107.

Finding: fork-repo guard is bypassed when pr.head.repo is null

.github/workflows/cancel-closed-pr-runs.yml:64-68:

const runRepo = run.head_repository ? run.head_repository.full_name : null;
if (headRepo && runRepo && runRepo !== headRepo) {
  core.info(`Skipping run ${run.id} (${run.name}) - head repo ${runRepo} is not ${headRepo}.`);
  continue;
}

headRepo (from pr.head.repo) is null when the head repository has been deleted (a common occurrence for fork PRs whose fork was deleted after merging/closing). In that case headRepo && runRepo && ... short-circuits to false for every run, so the mismatch check is skipped entirely rather than defaulting to "skip when unverifiable." This inverts the intended safety direction: the PR description states it "skips runs whose head repository differs from the PR's" as a deliberate guard against branch-name collisions across forks, but with a deleted fork, any repo's identically-named branch run becomes cancellable. Rare in practice, but it's the exact scenario the guard exists for, and the failure mode is silent (no error, just a wrongly-cancelled run belonging to someone else).

Suggested fix: when headRepo is falsy, skip the run rather than proceeding — e.g. if (!headRepo || !runRepo || runRepo !== headRepo) continue; (adjusting the log message accordingly).

Fix this →

Minor notes (non-blocking)

  • Fork PRs likely can't actually cancel. For pull_request (not pull_request_target) events triggered by a fork PR, GITHUB_TOKEN is read-only by default regardless of the permissions: actions: write requested here — so cancellation calls for fork-originated branches will typically 403. That's caught by the generic catch and just logged, so it fails safe, but it means the fork case this workflow explicitly guards for is also the case where it's least likely to have permission to act. Worth a one-line comment if this is a known/accepted limitation.
  • Generic catch swallows all errors, not just 409. The catch block treats any cancellation failure (including the 403 above, or an unexpected API error) the same as the expected "already finished" 409 — it logs and moves on without ever failing the job. Intentional per the design goal, but it means a systemic permission problem (e.g., token misconfigured) would silently no-op every run forever with no visible failure signal.
  • Cannot run actionlint in this sandbox (not installed) to independently verify the author's validation claim — YAML structure and github-script usage read correctly on manual inspection.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

✅ Duplicate Code Report

Two engines run in parallel for cross-validation. Each has its own thresholds tuned to its baseline - the real safety net is the per-engine "max increase vs base" check.

✅ PMD CPD

PR Base Change
Clones 33 33 ➖ 0
Duplicated lines 1001 1001 ➖ 0
Duplication 3.03% 3.03% ➖ 0
Rule Limit Status
Max duplication 5% ✅ Pass (3.03%)
Max increase vs base +0.1% ✅ Pass (+0.00%)

No new clones introduced by this PR.

✅ jscpd (language-agnostic)

PR Base Change
Clones 82 82 ➖ 0
Duplicated lines 1175 1175 ➖ 0
Duplication 3.59% 3.59% ➖ 0
Rule Limit Status
Max duplication 5% ✅ Pass (3.59%)
Max increase vs base +0.1% ✅ Pass (+0.00%)

No new clones introduced by this PR.

Powered by astubbs/duplicate-code-cross-check

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

📌 Duplicate code detection tool report

The tool analyzed your source code and found the following degree of similarity between the files:

✅ No new or increased file similarities introduced by this PR.

Full similarity report
parallel-consumer-core/src/main/java/io/confluent/csid/utils/Java8StreamUtils.java

📄 parallel-consumer-core/src/main/java/io/confluent/csid/utils/Java8StreamUtils.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/csid/utils/JavaUtils.java 35.09
parallel-consumer-core/src/test/java/io/confluent/csid/utils/CollectionUtils.java 33.0
parallel-consumer-core/src/main/java/io/confluent/csid/utils/JavaUtils.java

📄 parallel-consumer-core/src/main/java/io/confluent/csid/utils/JavaUtils.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/csid/utils/CollectionUtils.java 39.27
parallel-consumer-core/src/main/java/io/confluent/csid/utils/Java8StreamUtils.java 35.09
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ExceptionInUserFunctionException.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ExceptionInUserFunctionException.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerException.java 54.23 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java 40.68
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/EncodingNotSupportedException.java 37.32
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PCRetriableException.java 37.16
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV1EncodingNotSupported.java 35.6
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV2EncodingNotSupported.java 35.6
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/BitSetEncodingNotSupportedException.java 35.48
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/OffsetDecodingError.java 34.62
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/NoEncodingPossibleException.java 30.19
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelEoSStreamProcessor.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelEoSStreamProcessor.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelStreamProcessor.java 60.34 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessor.java 54.2 ⚠️
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelEoSStreamProcessor.java 40.07
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelStreamProcessor.java 36.58
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PollContextInternal.java 32.17
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelStreamProcessor.java 31.13
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelStreamProcessor.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelStreamProcessor.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelEoSStreamProcessor.java 60.34 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessor.java 50.02 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelStreamProcessor.java 36.27
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelEoSStreamProcessor.java 32.02
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PollContextInternal.java 31.12
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PCRetriableException.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PCRetriableException.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ExceptionInUserFunctionException.java 37.16
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java 33.13
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerException.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerException.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ExceptionInUserFunctionException.java 54.23 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java 53.71 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/EncodingNotSupportedException.java 45.01
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/BitSetEncodingNotSupportedException.java 35.15
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/OffsetDecodingError.java 34.12
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV1EncodingNotSupported.java 31.28
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV2EncodingNotSupported.java 31.28
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalRuntimeException.java 30.58
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/NoEncodingPossibleException.java 30.11
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerOptions.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerOptions.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/ProducerManager.java 32.79
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/PartitionState.java 31.0
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessor.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessor.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelEoSStreamProcessor.java 54.2 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelStreamProcessor.java 50.02 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelStreamProcessor.java 45.17
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PollContextInternal.java 33.48
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/AbstractParallelEoSStreamProcessor.java 32.79
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/TestParallelEoSStreamProcessor.java 30.69
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelStreamProcessor.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelStreamProcessor.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessor.java 45.17
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelEoSStreamProcessor.java 36.58
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelStreamProcessor.java 36.27
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelStreamProcessor.java 32.29
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PollContext.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PollContext.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/RecordContextInternal.java 35.45
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PollContextInternal.java 32.68
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PollContextInternal.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PollContextInternal.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessor.java 33.48
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PollContext.java 32.68
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/RecordContextInternal.java 32.59
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelEoSStreamProcessor.java 32.17
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelStreamProcessor.java 31.12
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/RecordContextInternal.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/RecordContextInternal.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PollContext.java 35.45
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PollContextInternal.java 32.59
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/AbstractParallelEoSStreamProcessor.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/AbstractParallelEoSStreamProcessor.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/BrokerPollSystem.java 32.86
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessor.java 32.79
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/BrokerPollSystem.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/BrokerPollSystem.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/AbstractParallelEoSStreamProcessor.java 32.86
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/ExternalEngine.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/ExternalEngine.java

File Similarity (%)
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/VertxParallelEoSStreamProcessor.java 39.54
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/EncodingNotSupportedException.java 60.5 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerException.java 53.71 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/OffsetDecodingError.java 51.23 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/NoEncodingPossibleException.java 48.61
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ExceptionInUserFunctionException.java 40.68
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalRuntimeException.java 39.01
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/BitSetEncodingNotSupportedException.java 37.41
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV1EncodingNotSupported.java 34.18
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV2EncodingNotSupported.java 34.18
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/PCRetriableException.java 33.13
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalRuntimeException.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalRuntimeException.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java 39.01
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/EncodingNotSupportedException.java 31.09
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerException.java 30.58
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/PCModule.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/PCModule.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/PCModuleTestEnv.java 32.81
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/ProducerManager.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/ProducerManager.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerOptions.java 32.79
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/BitSetEncodingNotSupportedException.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/BitSetEncodingNotSupportedException.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/EncodingNotSupportedException.java 51.8 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV1EncodingNotSupported.java 37.82
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV2EncodingNotSupported.java 37.82
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java 37.41
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ExceptionInUserFunctionException.java 35.48
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerException.java 35.15
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/OffsetDecodingError.java 33.35
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/EncodingNotSupportedException.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/EncodingNotSupportedException.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java 60.5 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/BitSetEncodingNotSupportedException.java 51.8 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/OffsetDecodingError.java 48.82
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV1EncodingNotSupported.java 47.5
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV2EncodingNotSupported.java 47.5
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/NoEncodingPossibleException.java 46.75
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerException.java 45.01
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ExceptionInUserFunctionException.java 37.32
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalRuntimeException.java 31.09
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/NoEncodingPossibleException.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/NoEncodingPossibleException.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java 48.61
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/EncodingNotSupportedException.java 46.75
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/OffsetDecodingError.java 45.48
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV1EncodingNotSupported.java 38.69
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV2EncodingNotSupported.java 38.69
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ExceptionInUserFunctionException.java 30.19
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerException.java 30.11
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/OffsetDecodingError.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/OffsetDecodingError.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java 51.23 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/EncodingNotSupportedException.java 48.82
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/NoEncodingPossibleException.java 45.48
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ExceptionInUserFunctionException.java 34.62
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerException.java 34.12
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/BitSetEncodingNotSupportedException.java 33.35
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV1EncodingNotSupported.java 32.19
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV2EncodingNotSupported.java 32.19
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV1EncodingNotSupported.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV1EncodingNotSupported.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV2EncodingNotSupported.java 63.91 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/EncodingNotSupportedException.java 47.5
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/NoEncodingPossibleException.java 38.69
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/BitSetEncodingNotSupportedException.java 37.82
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ExceptionInUserFunctionException.java 35.6
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java 34.18
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/OffsetDecodingError.java 32.19
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerException.java 31.28
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV2EncodingNotSupported.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV2EncodingNotSupported.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/RunLengthV1EncodingNotSupported.java 63.91 ⚠️
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/EncodingNotSupportedException.java 47.5
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/NoEncodingPossibleException.java 38.69
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/BitSetEncodingNotSupportedException.java 37.82
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ExceptionInUserFunctionException.java 35.6
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/InternalException.java 34.18
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/offsets/OffsetDecodingError.java 32.19
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerException.java 31.28
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/PartitionState.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/PartitionState.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelConsumerOptions.java 31.0
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/PartitionStateManager.java 30.74
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/PartitionStateManager.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/PartitionStateManager.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/WorkManager.java 41.88
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/PartitionState.java 30.74
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/ProcessingShard.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/ProcessingShard.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/ShardManager.java 38.33
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/ShardManager.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/ShardManager.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/ProcessingShard.java 38.33
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/WorkManager.java

📄 parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/WorkManager.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/state/PartitionStateManager.java 41.88
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/AmbientProbeExtension.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/AmbientProbeExtension.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/AmbientProbeExtensionTest.java 30.31
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/KafkaSanityTests.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/KafkaSanityTests.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/csid/utils/LoopingResumingIteratorTest.java 33.96
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/MultiInstanceHighVolumeTest.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/MultiInstanceHighVolumeTest.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/VeryLargeMessageVolumeTest.java 55.36 ⚠️
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/TransactionAndCommitModeTest.java 46.87
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/MultiInstanceRebalanceTest.java 38.64
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/MultiInstanceRebalanceTest.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/MultiInstanceRebalanceTest.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/VeryLargeMessageVolumeTest.java 44.16
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/TransactionAndCommitModeTest.java 41.04
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/MultiInstanceHighVolumeTest.java 38.64
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/RebalanceEoSDeadlockTest.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/RebalanceEoSDeadlockTest.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/RebalanceTest.java 36.78
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/RebalanceTest.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/RebalanceTest.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/RebalanceEoSDeadlockTest.java 36.78
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/TransactionAndCommitModeTest.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/TransactionAndCommitModeTest.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/VeryLargeMessageVolumeTest.java 60.71 ⚠️
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/MultiInstanceHighVolumeTest.java 46.87
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/MultiInstanceRebalanceTest.java 41.04
parallel-consumer-vertx/src/test-integration/java/io/confluent/parallelconsumer/vertx/integrationTests/VertxConcurrencyIT.java 30.12
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/VeryLargeMessageVolumeTest.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/VeryLargeMessageVolumeTest.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/TransactionAndCommitModeTest.java 60.71 ⚠️
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/MultiInstanceHighVolumeTest.java 55.36 ⚠️
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/MultiInstanceRebalanceTest.java 44.16
parallel-consumer-vertx/src/test-integration/java/io/confluent/parallelconsumer/vertx/integrationTests/VertxConcurrencyIT.java 39.38
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/AbstractRevokeUnderWorkScenario.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/AbstractRevokeUnderWorkScenario.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosChurnStormIT.java 48.62
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosRevokeUnderWorkIT.java 35.35
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosChurnStormIT.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosChurnStormIT.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/AbstractRevokeUnderWorkScenario.java 48.62
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosScenarioBase.java 37.72
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosRevokeUnderWorkIT.java 32.55
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosRevokeUnderWorkCooperativeIT.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosRevokeUnderWorkCooperativeIT.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosRevokeUnderWorkIT.java 50.82 ⚠️
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosRevokeUnderWorkIT.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosRevokeUnderWorkIT.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosRevokeUnderWorkCooperativeIT.java 50.82 ⚠️
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/AbstractRevokeUnderWorkScenario.java 35.35
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosChurnStormIT.java 32.55
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ProgressProbe.java 30.22
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosScenarioBase.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosScenarioBase.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosChurnStormIT.java 37.72
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ProgressProbe.java

📄 parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ProgressProbe.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/chaostests/ChaosRevokeUnderWorkIT.java 30.22
parallel-consumer-core/src/test/java/io/confluent/csid/utils/CollectionUtils.java

📄 parallel-consumer-core/src/test/java/io/confluent/csid/utils/CollectionUtils.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/csid/utils/JavaUtils.java 39.27
parallel-consumer-core/src/main/java/io/confluent/csid/utils/Java8StreamUtils.java 33.0
parallel-consumer-core/src/test/java/io/confluent/csid/utils/LoopingResumingIteratorTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/csid/utils/LoopingResumingIteratorTest.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/KafkaSanityTests.java 33.96
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/AbstractParallelEoSStreamProcessorTestBase.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/AbstractParallelEoSStreamProcessorTestBase.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessorTest.java 30.24
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/AmbientProbeExtensionTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/AmbientProbeExtensionTest.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/AmbientProbeExtension.java 30.31
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/BatchTestBase.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/BatchTestBase.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/CoreBatchTest.java 30.36
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/CheckQuarantineOwnersScriptTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/CheckQuarantineOwnersScriptTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/QuarantineLaneReportScriptTest.java 45.58
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/QuarantineRegistryScriptTest.java 43.89
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/CoreBatchTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/CoreBatchTest.java

File Similarity (%)
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorBatchTest.java 52.02 ⚠️
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyBatchTest.java 50.71 ⚠️
parallel-consumer-vertx/src/test/java/io/confluent/parallelconsumer/vertx/VertxBatchTest.java 44.8
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/BatchTestBase.java 30.36
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerCommitTimeoutTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerCommitTimeoutTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerEarlyCloseTest.java 70.44 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerTest.java 56.78 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerSaslAuthenticationTest.java 49.77
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerEarlyCloseTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerEarlyCloseTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerCommitTimeoutTest.java 70.44 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerTest.java 55.75 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerSaslAuthenticationTest.java 52.98 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerSaslAuthenticationTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerSaslAuthenticationTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerEarlyCloseTest.java 52.98 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerCommitTimeoutTest.java 49.77
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerTest.java 47.42
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerCommitTimeoutTest.java 56.78 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerEarlyCloseTest.java 55.75 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/MockConsumerSaslAuthenticationTest.java 47.42
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/ParallelEoSSStreamProcessorRebalancedTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/ParallelEoSSStreamProcessorRebalancedTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessorTest.java 34.7
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessorTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessorTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/ParallelEoSSStreamProcessorRebalancedTest.java 34.7
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/AbstractParallelEoSStreamProcessorTestBase.java 30.24
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/QuarantineLaneReportScriptTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/QuarantineLaneReportScriptTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/CheckQuarantineOwnersScriptTest.java 45.58
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/QuarantineRegistryScriptTest.java 33.46
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/QuarantineRegistryScriptTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/QuarantineRegistryScriptTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/CheckQuarantineOwnersScriptTest.java 43.89
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/QuarantineLaneReportScriptTest.java 33.46
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/TestConventionsArchTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/TestConventionsArchTest.java

File Similarity (%)
parallel-consumer-vertx/src/test/java/io/confluent/parallelconsumer/vertx/TestConventionsArchTest.java 90.38 ⚠️
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/TestConventionsArchTest.java 89.71 ⚠️
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/TestConventionsArchTest.java 89.71 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/ExceptionConstructorsTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/ExceptionConstructorsTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/InternalRuntimeExceptionTest.java 30.12
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/InternalRuntimeExceptionTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/InternalRuntimeExceptionTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/ExceptionConstructorsTest.java 30.12
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/PCModuleTestEnv.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/PCModuleTestEnv.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/PCModule.java 32.81
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/TestParallelEoSStreamProcessor.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/internal/TestParallelEoSStreamProcessor.java

File Similarity (%)
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelEoSStreamProcessor.java 30.69
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/offsets/OffsetEncodingBackPressureTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/offsets/OffsetEncodingBackPressureTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/offsets/OffsetEncodingBackPressureUnitTest.java 40.07
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/offsets/OffsetEncodingBackPressureUnitTest.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/offsets/OffsetEncodingBackPressureUnitTest.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/offsets/OffsetEncodingBackPressureTest.java 40.07
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/truth/CommitHistorySubject.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/truth/CommitHistorySubject.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/truth/LongPollingMockConsumerSubject.java 36.58
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/truth/LongPollingMockConsumerSubject.java

📄 parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/truth/LongPollingMockConsumerSubject.java

File Similarity (%)
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/truth/CommitHistorySubject.java 36.58
parallel-consumer-mutiny/src/main/java/io/confluent/parallelconsumer/mutiny/MutinyProcessor.java

📄 parallel-consumer-mutiny/src/main/java/io/confluent/parallelconsumer/mutiny/MutinyProcessor.java

File Similarity (%)
parallel-consumer-reactor/src/main/java/io/confluent/parallelconsumer/reactor/ReactorProcessor.java 52.5 ⚠️
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyBatchTest.java

📄 parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyBatchTest.java

File Similarity (%)
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorBatchTest.java 79.06 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/CoreBatchTest.java 50.71 ⚠️
parallel-consumer-vertx/src/test/java/io/confluent/parallelconsumer/vertx/VertxBatchTest.java 49.27
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyPCTest.java

📄 parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyPCTest.java

File Similarity (%)
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorPCTest.java 69.61 ⚠️
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyTest.java

📄 parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyTest.java

File Similarity (%)
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorTest.java 34.11
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyUnitTestBase.java

📄 parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyUnitTestBase.java

File Similarity (%)
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorUnitTestBase.java 32.9
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/TestConventionsArchTest.java

📄 parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/TestConventionsArchTest.java

File Similarity (%)
parallel-consumer-vertx/src/test/java/io/confluent/parallelconsumer/vertx/TestConventionsArchTest.java 91.22 ⚠️
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/TestConventionsArchTest.java 90.55 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/TestConventionsArchTest.java 89.71 ⚠️
parallel-consumer-reactor/src/main/java/io/confluent/parallelconsumer/reactor/ReactorProcessor.java

📄 parallel-consumer-reactor/src/main/java/io/confluent/parallelconsumer/reactor/ReactorProcessor.java

File Similarity (%)
parallel-consumer-mutiny/src/main/java/io/confluent/parallelconsumer/mutiny/MutinyProcessor.java 52.5 ⚠️
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorBatchTest.java

📄 parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorBatchTest.java

File Similarity (%)
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyBatchTest.java 79.06 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/CoreBatchTest.java 52.02 ⚠️
parallel-consumer-vertx/src/test/java/io/confluent/parallelconsumer/vertx/VertxBatchTest.java 50.53 ⚠️
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorPCTest.java

📄 parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorPCTest.java

File Similarity (%)
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyPCTest.java 69.61 ⚠️
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorTest.java

📄 parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorTest.java

File Similarity (%)
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyTest.java 34.11
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorUnitTestBase.java

📄 parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorUnitTestBase.java

File Similarity (%)
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyUnitTestBase.java 32.9
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/TestConventionsArchTest.java

📄 parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/TestConventionsArchTest.java

File Similarity (%)
parallel-consumer-vertx/src/test/java/io/confluent/parallelconsumer/vertx/TestConventionsArchTest.java 91.22 ⚠️
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/TestConventionsArchTest.java 90.55 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/TestConventionsArchTest.java 89.71 ⚠️
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelEoSStreamProcessor.java

📄 parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelEoSStreamProcessor.java

File Similarity (%)
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/VertxParallelEoSStreamProcessor.java 41.51
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelEoSStreamProcessor.java 40.07
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelStreamProcessor.java 39.72
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/VertxParallelStreamProcessor.java 35.33
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelStreamProcessor.java 32.02
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelStreamProcessor.java

📄 parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelStreamProcessor.java

File Similarity (%)
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelEoSStreamProcessor.java 39.72
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/VertxParallelStreamProcessor.java 39.16
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/ParallelStreamProcessor.java 32.29
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/JStreamParallelEoSStreamProcessor.java 31.13
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/VertxParallelEoSStreamProcessor.java

📄 parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/VertxParallelEoSStreamProcessor.java

File Similarity (%)
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/VertxParallelStreamProcessor.java 41.75
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelEoSStreamProcessor.java 41.51
parallel-consumer-core/src/main/java/io/confluent/parallelconsumer/internal/ExternalEngine.java 39.54
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/VertxParallelStreamProcessor.java

📄 parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/VertxParallelStreamProcessor.java

File Similarity (%)
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/VertxParallelEoSStreamProcessor.java 41.75
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelStreamProcessor.java 39.16
parallel-consumer-vertx/src/main/java/io/confluent/parallelconsumer/vertx/JStreamVertxParallelEoSStreamProcessor.java 35.33
parallel-consumer-vertx/src/test-integration/java/io/confluent/parallelconsumer/vertx/integrationTests/VertxConcurrencyIT.java

📄 parallel-consumer-vertx/src/test-integration/java/io/confluent/parallelconsumer/vertx/integrationTests/VertxConcurrencyIT.java

File Similarity (%)
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/VeryLargeMessageVolumeTest.java 39.38
parallel-consumer-core/src/test-integration/java/io/confluent/parallelconsumer/integrationTests/TransactionAndCommitModeTest.java 30.12
parallel-consumer-vertx/src/test/java/io/confluent/parallelconsumer/vertx/TestConventionsArchTest.java

📄 parallel-consumer-vertx/src/test/java/io/confluent/parallelconsumer/vertx/TestConventionsArchTest.java

File Similarity (%)
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/TestConventionsArchTest.java 91.22 ⚠️
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/TestConventionsArchTest.java 91.22 ⚠️
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/TestConventionsArchTest.java 90.38 ⚠️
parallel-consumer-vertx/src/test/java/io/confluent/parallelconsumer/vertx/VertxBatchTest.java

📄 parallel-consumer-vertx/src/test/java/io/confluent/parallelconsumer/vertx/VertxBatchTest.java

File Similarity (%)
parallel-consumer-reactor/src/test/java/io/confluent/parallelconsumer/reactor/ReactorBatchTest.java 50.53 ⚠️
parallel-consumer-mutiny/src/test/java/io/confluent/parallelconsumer/mutiny/MutinyBatchTest.java 49.27
parallel-consumer-core/src/test/java/io/confluent/parallelconsumer/CoreBatchTest.java 44.8

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

✅ SpotBugs Report

No bugs found (new bugs only — baseline from base branch excluded).

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

🧪🔒 Quarantine Lane Report

Quarantined test Outcome Owner Meaning
ChaosChurnStormIT.churnStormMeetsSlosAndBalancesLedger 🔴 failing (expected) #80 quarantine holding
PartitionStateCommittedOffsetIT.committedOffsetRemoved 🟡🎲 passed (flapper) #80 proves nothing - passes most runs by nature

🔴 expected while the owner PR is open · 🟡🎲 flapper, pass proves nothing · 🚨 a deterministic quarantined test passing means its fix landed: delete its @Quarantined annotation + docs/QUARANTINED_TESTS.md entry (a merge-blocking review thread has been opened). Lane: non-gating; rules: see the Quarantine Audit check.

Review finding on #107, and a real bug rather than a nit.

The fork-repo guard read:
    if (headRepo && runRepo && runRepo !== headRepo) continue;

GitHub sets pull_request.head.repo to null once the source fork is
deleted, which is routine - contributors often delete a fork straight
after merge. headRepo is then null, the condition short-circuits to
false for EVERY run, and the guard silently disables itself: the job
would cancel any run matching the branch name in any repository,
including a live PR from another contributor on a branch called 'fix' or
'main'.

Precisely the scenario the guard exists to prevent, and it failed open.

Now: if (runRepo && (!headRepo || runRepo !== headRepo)) continue;
so an unverifiable owner means leave it alone. The log line says which
case it hit. The reasoning is recorded next to the check, since 'why is
this not the simpler condition' is exactly what invites reverting it.

Also documents the reviewer's second, informational finding: fork PRs
get a read-only GITHUB_TOKEN whatever the permissions block says, so
this workflow cannot cancel a fork PR's runs at all. It does not undercut
the goal - the self-hosted heavy jobs are already same-repo only - but
it is worth knowing if that ever changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn
@claude

claude Bot commented Aug 3, 2026

Copy link
Copy Markdown

Claude finished @astubbs's task in 1m 46s —— View job


Code Review: ci: cancel a PR's still-running CI when the PR closes

  • Gather context (PR diff, workflow file, prior review comments)
  • Review .github/workflows/cancel-closed-pr-runs.yml in depth
  • Check whether prior findings (fork-repo guard bypass) were addressed
  • Review CHANGELOG.adoc / README.adoc changes
  • Post final review

Summary

The finding from the earlier automated reviews on this PR — that the fork-repo guard silently disabled itself when pr.head.repo is null (deleted fork) — has already been fixed in commit 8e9ec483 ("ci: do not let 'cannot verify the repo' mean 'cancel everything'"). Current state at .github/workflows/cancel-closed-pr-runs.yml:78:

if (runRepo && (!headRepo || runRepo !== headRepo)) {

This correctly treats an unverifiable owner as "skip", not "no filter" — matches the fix both prior reviews suggested, and the accompanying comment (lines 68-76) explains why the simpler-looking headRepo && runRepo && ... form is wrong, which should deter a future "simplification" from reintroducing the bug.

Verified claims

  • pr-highcpu-fast-feedback.yml:60 — concurrency group is highcpu-${{ matrix.suite-name }}-${{ github.head_ref || github.ref }}, confirming the stated gap (same-suite/same-branch only; a merge doesn't look like a new push).
  • docs/SELF_HOSTED_RUNNER.md, referenced in the header comment, exists.
  • CHANGELOG.adoc:33 / README.adoc:1604 entries match, both cite #107, consistent with the #104 checklist convention.
  • Self-skip (run.id === context.runId), default-branch refusal, and 409-as-non-failure handling are all correct as written.
  • queued/in_progress/waiting/requested/pending are all valid status values for listWorkflowRunsForRepo.

One residual edge case (informational, not blocking)

.github/workflows/cancel-closed-pr-runs.yml:78: the guard skips whenever runRepo is truthy and unverifiable-or-mismatched, but if both headRepo and run.head_repository are null (e.g. the run's own source fork was also deleted), runRepo && (...) short-circuits false and the run is not skipped — it proceeds to cancellation without any owner check. This is the same class of gap as the bug just fixed, just one level narrower: it requires two different deleted forks producing runs under the same branch name against this repo simultaneously. Given how narrow the window is (and that the PR already documents one accepted residual limitation — fork PRs 403 on cancel anyway), I'd call this worth a one-line comment at most, not a blocker.

Other notes

  • Could not run actionlint in this sandbox to independently verify the author's validation claim (binary not available / shell restricted here); YAML structure and github-script usage read correctly on manual inspection.
  • No test coverage possible before merge since it triggers on pull_request: closed — acknowledged in the PR checklist, reasonable given this is a CI-only workflow change.

Overall: ready to merge. The one prior blocking-adjacent issue is resolved with a well-reasoned fix; the remaining edge case is narrow enough to leave as-is or address in a follow-up.

@astubbs

astubbs commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

Fixed — and this was a real bug, not a nit. Thank you for tracing the null case rather than reading the condition as written.

The guard failed open, in exactly its own scenario

if (headRepo && runRepo && runRepo !== headRepo) continue;   // before

You're right that pull_request.head.repo is null once the source fork is deleted — routine, since contributors often delete a fork straight after merge. headRepo is then null, the condition short-circuits to false for every run, and the guard silently disables itself: the job would cancel any run matching the branch name in any repository, including a live PR from another contributor on a branch called fix or main.

So the one case the guard exists for is the one case it stopped working. Fixed with your suggested form:

if (runRepo && (!headRepo || runRepo !== headRepo)) continue;   // after

"Cannot verify the owner" now means leave it alone rather than no filter. The log line distinguishes the two cases, and I've recorded the reasoning beside the check — "why isn't this the simpler condition?" is exactly the question that invites reverting it.

Second finding recorded rather than dismissed

You're right that actions: write is a no-op for fork-originated PRs — GitHub issues a read-only GITHUB_TOKEN regardless of the permissions: block, so closing a fork PR gets a 403 that the existing catch swallows. Documented in the workflow header, along with why it doesn't undercut the goal: pr-highcpu-fast-feedback.yml already restricts the self-hosted heavy jobs — the actually scarce resource — to same-repo PRs. Worth knowing if that assumption ever changes.

On the duplicate reviews

You reviewed this PR twice, 23 seconds apart, and both passes found this same finding. That's fixed separately in #109: claude-code-review.yml had no concurrency group, so the run triggered by opening the PR was never superseded by the run triggered by the next push.

Worth noting the sequence is self-inflicted rather than rare — #104's changelog gate requires the entry to cite the PR number, which doesn't exist until the PR is open, so opening is routinely followed within seconds by a push.

🤖 Generated with Claude Code

@astubbs
astubbs merged commit cd672ae into master Aug 3, 2026
23 of 29 checks passed
@astubbs
astubbs deleted the ci/cancel-runs-when-pr-closes branch August 3, 2026 03:48
astubbs added a commit that referenced this pull request Aug 3, 2026
* ci(review): one review per PR at a time, newest wins

PR #107 got two bot reviews 23 seconds apart. claude-code-review.yml had
no concurrency group at all - unlike maven.yml, pr-checklist.yml,
copyright.yml and the highcpu lane - so the run started by opening the
PR was never superseded by the run started by the next push. Both
completed, both posted.

The triggering sequence is not unusual, it is what this repo's own rules
produce: #104's changelog gate requires the entry to cite the PR number,
and that number does not exist until the PR is opened. So opening a PR
is routinely followed within seconds by a push to add the reference -
which is exactly the double-trigger.

Keyed on the PR number rather than the ref, so it also covers reopened
PRs and cannot collide with anything else keyed on a branch name.

Deliberately a separate PR rather than folded into #107: touching
claude-code-review.yml makes a PR unreviewable by the bot
(claude-code-action refuses to run when the workflow differs from the
default branch's copy), and #107's fork-repo and default-branch guards
are worth having reviewed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn

* docs(changelog): cite this PR (#109)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn

* ci(changelog-gate): cite the issue, not this PR

Requiring the entry to cite its own PR was a nuisance by construction:
the number does not exist until the PR is opened, so every PR needed a
second push purely to add the reference. That push is also what
double-triggered the review workflow, which is the other half of this
PR.

An issue number is known before the work starts, and 'which reported
problem does this address' is what a changelog reader actually wants -
the PR is an implementation detail they can reach from the issue.

Two deliberate constraints:

- The citation must be an explicit /issues/ link. A bare #NN cannot be
  told apart from a PR reference without an API call, since GitHub
  numbers issues and pull requests from one sequence - and 'cite the
  issue' is the entire point. Fork and upstream issues both count.

- Only user-visible sections require one: Breaking, Improvements, Fixes,
  Examples. Build & CI is exempt, and that is not laziness - of the 12
  Build & CI entries predating any of this, 7 cite nothing at all and
  the rest cite a PR. This project's tooling work is self-directed and
  has no issue behind it, so requiring one would mean inventing issues
  or writing changelog-ref: N/A on every CI PR - the same paperwork this
  change removes, pointing the other way.

Existing entries without an issue are left alone. Only newly ADDED
entries are checked, so nothing needs backfilling.

The opt-out is unchanged for the genuine no-issue case. AGENTS.md's
reference convention is updated to match, since it documented the old
rule. 34 unit tests, including that a pull link and a bare #NN both
correctly fail to count as issue citations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn

* ci(changelog-gate): read the changelog, and drop the clever half

Review on #109 found the section detection was inert, and proved it
against this PR's own diff: git has no funcname pattern for asciidoc, so
a hunk header for CHANGELOG.adoc reads '@@ ... @@ endif::[]' rather than
the heading, and entries are one long line each so three lines of
context never reach one either. Section resolved to null for
essentially every real entry - and since an unknown section counted as
exempt, the gate passed everything silently. It was decoration.

Fixed by reading the CHANGELOG itself rather than inferring from the
patch. The job already checks the repo out, so the file is right there:
find the added line, walk back to the nearest '=== ' heading. Verified
end to end against the real changelog - a real Build & CI entry resolves
to Build & CI and passes, a real 'fix:' entry with no issue link is
flagged.

Also took the chance to cut this down, per feedback that it had got too
complex for what it is. Gone: Dice-coefficient similarity, edit scoring,
content-based pairing of removed against added bullets, EDIT_THRESHOLD
and its pinned mispairing limitation. 176 -> 96 lines of logic, 269 ->
147 of tests.

What that costs, stated in the code rather than discovered later:
editing an old uncited entry now asks for a citation it never had.
Telling edits from additions needs the fuzzy matching that was just
removed - which was the largest and subtlest part of the file and still
mispaired same-template entries. The changelog-ref: N/A opt-out covers
the rare case. That is a better trade than machinery nobody can follow,
for a check that exists to remind us, not to withstand an adversary.

The test suite now models a realistic patch - no heading anywhere in the
visible context - which is exactly the case the old tests all avoided by
putting '=== Fixes' in the synthetic hunk header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QqHpNSXC39ANv9kG1ZvUzn

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant