feat(archunit-rules): NoBlanketCatch violationCount for ratchet assertions (#332 W1) #16

Merged
hibryda merged 2 commits from feat/archunit-noblanketcatch-count into main 2026-05-30 19:58:36 +02:00
Owner

Summary

Adds NoBlanketCatchArchTest.violationCount(JavaClasses) so consuming services can ratchet the blanket-catch debt instead of warning with no ceiling.

Surfaced by user-service #16 R1 (MAJOR): a pure warn test (log, no assertion) lets new catch (Exception) blocks accumulate silently forever — cosmetic from merge. The fix is a ratchet: pin the W1 baseline, assert violationCount(...) <= baseline → existing debt tolerated, new debt fails.

evaluateForWarning (report string) stays for logging; counting its rendered lines is fragile (the FailureReport toString carries header/format lines). violationCount returns getFailureReport().getDetails().size() — one entry per flagged catch clause, format-independent.

Changes

  • NoBlanketCatchArchTest.violationCount(JavaClasses) — new accessor (0 when compliant).
  • NoBlanketCatchSelfTest.violationCountMatchesFixtures — asserts 3 for the negative package (Exception+Throwable+RuntimeException fixtures), 0 for the positive package.
  • README — documents the ratchet pattern.

Verification

  • mvn -pl archunit-rules test → Tests run: 8, Failures: 0, Errors: 0; BUILD SUCCESS, zero warnings.

Blast radius

Additive public method on a test-helper class; no behaviour change to the rule itself. Score 2/10. Consumed next by the 7-service NoBlanketCatchWarnTest ratchets (W2 flip tracked in im2be-mono#4).

## Summary Adds `NoBlanketCatchArchTest.violationCount(JavaClasses)` so consuming services can **ratchet** the blanket-catch debt instead of warning with no ceiling. Surfaced by **user-service #16 R1 (MAJOR)**: a pure warn test (log, no assertion) lets new `catch (Exception)` blocks accumulate silently forever — cosmetic from merge. The fix is a ratchet: pin the W1 baseline, assert `violationCount(...) <= baseline` → existing debt tolerated, new debt fails. `evaluateForWarning` (report string) stays for logging; counting its rendered lines is fragile (the `FailureReport` toString carries header/format lines). `violationCount` returns `getFailureReport().getDetails().size()` — one entry per flagged catch clause, format-independent. ## Changes - `NoBlanketCatchArchTest.violationCount(JavaClasses)` — new accessor (0 when compliant). - `NoBlanketCatchSelfTest.violationCountMatchesFixtures` — asserts 3 for the negative package (Exception+Throwable+RuntimeException fixtures), 0 for the positive package. - README — documents the ratchet pattern. ## Verification - `mvn -pl archunit-rules test` → Tests run: 8, Failures: 0, Errors: 0; BUILD SUCCESS, zero warnings. ## Blast radius Additive public method on a test-helper class; no behaviour change to the rule itself. Score 2/10. Consumed next by the 7-service `NoBlanketCatchWarnTest` ratchets (W2 flip tracked in im2be-mono#4).
feat(archunit-rules): add NoBlanketCatch violationCount for ratchet assertions
All checks were successful
im2be-platform-libs CI / mvn install (pull_request) Successful in 1m20s
im2be-platform-libs CI / mvn verify (main only) (pull_request) Has been skipped
46c0edc779
The warn-mode adoption pattern needs a stable violation COUNT so consumers can
ratchet (pin the W1 baseline, assert count <= baseline → tolerate existing debt,
fail new debt) instead of warning with no ceiling. evaluateForWarning returns
the rendered report (good for logging, but line-counting it is fragile: the
FailureReport toString carries header/formatting lines).

violationCount(JavaClasses) returns result.getFailureReport().getDetails().size()
(one entry per flagged catch clause) — the canonical, format-independent count.
This is the accessor the per-service NoBlanketCatchWarnTest ratchet uses across
the #332 W1 sweep; surfaced by user-service #16 R1 (MAJOR: no violation ceiling).

Self-test: violationCount == 3 for the negative package (Exception + Throwable +
RuntimeException fixtures, one flagged catch each), 0 for the positive package.

Verification:
- mvn -pl archunit-rules test → Tests run: 8, Failures: 0, Errors: 0 (BUILD SUCCESS)
- NoBlanketCatchSelfTest 5 → 6 tests; zero warnings

Superseded by round 2.

Show previous round

hib-pr-reviewer review — PR #16 (affinity-intelligence-rework/im2be-platform-libs)

Round 1 — head 46c0edc77975, base main, trigger opened

TL;DR: CONDITIONAL_APPROVE — kept 1 minor unique-to-A finding (vacuous positive-branch self-test), Reviewer B filed 0 findings; no blocking issues.

Summary

Arbitration — PR #16 feat(archunit-rules): NoBlanketCatchArchTest.violationCount

Prior runs: None — first arbitration for this PR.

Reconciliation: A=1 finding, B=0 findings.

Unique-to-A finding (line 123, NoBlanketCatchSelfTest.java): Verified by reading the file. The positive branch of violationCountMatchesFixtures (lines 123–126) imports from the POSITIVE package and asserts isZero() with no fixture-containment guards. Contrast with passesForCompliantFixtures (lines 43–65), which guards with four positive.contain(...) assertions and an explicit comment // Guard against a vacuous pass. If importPackages(POSITIVE) returns an empty JavaClasses (classpath gap, accidental package rename, etc.), violationCount returns 0 and isZero() passes silently. The negative branch is self-protecting (count ≠ 3 fails). Finding kept.

B's summary mention of class-level Javadoc missing RATCHET mode was not filed as a formal finding — not surfaced here per reconciliation rules.

Result: 1 minor finding kept, 0 dropped.

Blast Radius

The diff adds one new public static method (violationCount) to an in-library helper class and a self-test; no existing behaviour is modified. The library is consumed by downstream services but the change is purely additive, so the blast radius is narrow and confined to test reliability of the new helper.

BLAST_SCORE: 2/10

CI status (head 46c0edc77975)

Overall: ✓ success

2 checks: 2 pending

Check State Link
im2be-platform-libs CI / mvn install (pull_request) pending details
im2be-platform-libs CI / mvn verify (main only) (pull_request) pending details

Findings (1)

[MINOR] Positive branch of violationCountMatchesFixtures can vacuously pass if POSITIVE package yields no classes

archunit-rules/src/test/java/com/aim2be/platform/archunit/NoBlanketCatchSelfTest.java:123

Lines 123–126 import the POSITIVE package and assert isZero() with no fixture-containment guards:

final JavaClasses positive = new ClassFileImporter().importPackages(POSITIVE);
assertThat(NoBlanketCatchArchTest.violationCount(positive))
        .as("compliant fixtures produce no violations")
        .isZero();

If importPackages(POSITIVE) returns an empty set — e.g. due to a classpath gap, compilation failure, or accidental package rename — violationCount returns 0 and isZero() passes without evaluating anything.

The existing passesForCompliantFixtures (lines 43–65) guards against exactly this with four positive.contain(...) assertions before evaluating, and carries an explicit comment // Guard against a vacuous pass. The new test's positive branch should follow the same pattern. At minimum add:

assertThat(positive.contain(SpecificCatchService.class))
        .as("specific-catch fixture must be present for zero-count assertion to be non-vacuous")
        .isTrue();

before the isZero() assertion. Adding all four guards (matching passesForCompliantFixtures) is preferable so both tests agree on which fixtures constitute the positive set. The negative branch (isEqualTo(3)) is already self-protecting: a missing fixture produces a count ≠ 3 and the test fails.

Verdict

CONDITIONAL_APPROVE


hib-pr-reviewer • round 1 • 1 finding (1m) • 2026-05-30T17:53:35.586Z → 2026-05-30T17:55:21.667Z • posted-as: pr-reviewer-bot • model: auto

<!-- hib-pr-reviewer collapsed --> > _Superseded by round 2._ <details> <summary>Show previous round</summary> ## hib-pr-reviewer review — PR #16 (affinity-intelligence-rework/im2be-platform-libs) **Round 1** — head `46c0edc77975`, base `main`, trigger `opened` **TL;DR:** CONDITIONAL_APPROVE — kept 1 minor unique-to-A finding (vacuous positive-branch self-test), Reviewer B filed 0 findings; no blocking issues. ### Summary ## Arbitration — PR #16 `feat(archunit-rules): NoBlanketCatchArchTest.violationCount` **Prior runs:** None — first arbitration for this PR. **Reconciliation:** A=1 finding, B=0 findings. **Unique-to-A finding (line 123, `NoBlanketCatchSelfTest.java`):** Verified by reading the file. The positive branch of `violationCountMatchesFixtures` (lines 123–126) imports from the `POSITIVE` package and asserts `isZero()` with **no fixture-containment guards**. Contrast with `passesForCompliantFixtures` (lines 43–65), which guards with four `positive.contain(...)` assertions and an explicit comment `// Guard against a vacuous pass`. If `importPackages(POSITIVE)` returns an empty `JavaClasses` (classpath gap, accidental package rename, etc.), `violationCount` returns `0` and `isZero()` passes silently. The negative branch is self-protecting (count ≠ 3 fails). Finding **kept**. **B's summary mention of class-level Javadoc missing RATCHET mode** was not filed as a formal finding — not surfaced here per reconciliation rules. **Result:** 1 minor finding kept, 0 dropped. ### Blast Radius The diff adds one new public static method (`violationCount`) to an in-library helper class and a self-test; no existing behaviour is modified. The library is consumed by downstream services but the change is purely additive, so the blast radius is narrow and confined to test reliability of the new helper. **BLAST_SCORE: 2/10** ### CI status (head `46c0edc77975`) **Overall: ✓ success** 2 checks: 2 pending | Check | State | Link | |---|---|---| | im2be-platform-libs CI / mvn install (pull_request) | ⏳ pending | [details](/affinity-intelligence-rework/im2be-platform-libs/actions/runs/96/jobs/0) | | im2be-platform-libs CI / mvn verify (main only) (pull_request) | ⏳ pending | [details](/affinity-intelligence-rework/im2be-platform-libs/actions/runs/96/jobs/1) | ### Findings (1) #### **[MINOR]** Positive branch of `violationCountMatchesFixtures` can vacuously pass if `POSITIVE` package yields no classes _archunit-rules/src/test/java/com/aim2be/platform/archunit/NoBlanketCatchSelfTest.java:123_ Lines 123–126 import the `POSITIVE` package and assert `isZero()` with no fixture-containment guards: ```java final JavaClasses positive = new ClassFileImporter().importPackages(POSITIVE); assertThat(NoBlanketCatchArchTest.violationCount(positive)) .as("compliant fixtures produce no violations") .isZero(); ``` If `importPackages(POSITIVE)` returns an empty set — e.g. due to a classpath gap, compilation failure, or accidental package rename — `violationCount` returns `0` and `isZero()` passes without evaluating anything. The existing `passesForCompliantFixtures` (lines 43–65) guards against exactly this with four `positive.contain(...)` assertions before evaluating, and carries an explicit comment `// Guard against a vacuous pass`. The new test's positive branch should follow the same pattern. At minimum add: ```java assertThat(positive.contain(SpecificCatchService.class)) .as("specific-catch fixture must be present for zero-count assertion to be non-vacuous") .isTrue(); ``` before the `isZero()` assertion. Adding all four guards (matching `passesForCompliantFixtures`) is preferable so both tests agree on which fixtures constitute the positive set. The negative branch (`isEqualTo(3)`) is already self-protecting: a missing fixture produces a count ≠ 3 and the test fails. ### Verdict **CONDITIONAL_APPROVE** --- <sub>hib-pr-reviewer • round 1 • 1 finding (1m) • 2026-05-30T17:53:35.586Z → 2026-05-30T17:55:21.667Z • posted-as: pr-reviewer-bot • model: auto</sub> </details>
test(archunit-rules): guard violationCount positive branch against vacuous pass (#16 R1)
All checks were successful
im2be-platform-libs CI / mvn install (pull_request) Successful in 1m45s
im2be-platform-libs CI / mvn verify (main only) (pull_request) Has been skipped
f0e907c491
R1 verdict CONDITIONAL_APPROVE; 1 minor finding (kept=1):

(1) MINOR NoBlanketCatchSelfTest:123 — violationCountMatchesFixtures' positive
    branch asserted isZero() with no containment guard; an empty POSITIVE import
    (classpath gap / rename) would make it vacuously pass. Added a
    positive.contain(SpecificCatchService.class) guard before the isZero()
    assertion (mirrors passesForCompliantFixtures' anti-vacuity guards). The
    negative branch (isEqualTo(3)) is already self-protecting — a missing fixture
    yields count != 3 and fails.

Verification:
- mvn -pl archunit-rules test → Tests run: 8, Failures: 0, Errors: 0 (BUILD SUCCESS)
- zero warnings

hib-pr-reviewer review — PR #16 (affinity-intelligence-rework/im2be-platform-libs)

Round 2 — head f0e907c491f6, base main, trigger synchronize

TL;DR: NO_NEW_FINDINGS — No new findings this round.

Summary

[quorum-converged] A=0 = B=0. Round 1 finding fully resolved. The new violationCountMatchesFixtures test adds an explicit positive.contain(SpecificCatchService.class) guard (lines 126–129) before the isZero() assertion, exactly the pattern the round-1 comment requested. SpecificCatchService was confirmed present at archunit-rules/src/test/java/com/aim2be/platform/archunit/fixtures/positive/SpecificCatchService.java. The negative branch's isEqualTo(3) assertion is self-guarding against vacuous pass (an empty import returns 0, which ≠ 3, so it cannot silently pass). violationCount's use of result.getFailureReport().getDetails().size() is the correct stable ArchUnit API for per-violation counts — it won't drift with header/formatting changes. README prose accurately describes the new method and ratchet pattern.

CI status (head f0e907c491f6)

Overall: pending

2 checks: 2 pending

Check State Link
im2be-platform-libs CI / mvn verify (main only) (pull_request) pending details
im2be-platform-libs CI / mvn install (pull_request) pending details

Findings

No new findings this round.

Quorum converged on empty findings (A + B both returned 0).

Verdict

NO_NEW_FINDINGS


hib-pr-reviewer • round 2 • 0 findings • 2026-05-30T17:56:34.615Z → 2026-05-30T17:58:04.983Z • posted-as: pr-reviewer-bot • model: auto • [bookkeeping fallback]

## hib-pr-reviewer review — PR #16 (affinity-intelligence-rework/im2be-platform-libs) **Round 2** — head `f0e907c491f6`, base `main`, trigger `synchronize` **TL;DR:** NO_NEW_FINDINGS — No new findings this round. ### Summary [quorum-converged] A=0 = B=0. Round 1 finding fully resolved. The new `violationCountMatchesFixtures` test adds an explicit `positive.contain(SpecificCatchService.class)` guard (lines 126–129) before the `isZero()` assertion, exactly the pattern the round-1 comment requested. `SpecificCatchService` was confirmed present at `archunit-rules/src/test/java/com/aim2be/platform/archunit/fixtures/positive/SpecificCatchService.java`. The negative branch's `isEqualTo(3)` assertion is self-guarding against vacuous pass (an empty import returns 0, which ≠ 3, so it cannot silently pass). `violationCount`'s use of `result.getFailureReport().getDetails().size()` is the correct stable ArchUnit API for per-violation counts — it won't drift with header/formatting changes. README prose accurately describes the new method and ratchet pattern. ### CI status (head `f0e907c491f6`) **Overall: ⏳ pending** 2 checks: 2 pending | Check | State | Link | |---|---|---| | im2be-platform-libs CI / mvn verify (main only) (pull_request) | ⏳ pending | [details](/affinity-intelligence-rework/im2be-platform-libs/actions/runs/97/jobs/1) | | im2be-platform-libs CI / mvn install (pull_request) | ⏳ pending | [details](/affinity-intelligence-rework/im2be-platform-libs/actions/runs/97/jobs/0) | ### Findings **No new findings this round.** _Quorum converged on empty findings (A + B both returned 0)._ ### Verdict **NO_NEW_FINDINGS** --- <sub>hib-pr-reviewer • round 2 • 0 findings • 2026-05-30T17:56:34.615Z → 2026-05-30T17:58:04.983Z • posted-as: pr-reviewer-bot • model: auto • [bookkeeping fallback]</sub>
hibryda deleted branch feat/archunit-noblanketcatch-count 2026-05-30 19:58:36 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
affinity-intelligence-rework/im2be-platform-libs!16
No description provided.