PR-PLATFORM-4 — outbox-test-fixtures full impl #5

Merged
hibryda merged 5 commits from feat/pr-platform-4-test-fixtures into main 2026-05-27 05:40:12 +02:00
Owner

PR-PLATFORM-4 — outbox-test-fixtures full impl (PR-PLATFORM-1 substrate).

Implements OQ-6 §1.5 of .planning/26-stage-b-outbox-parity.md: JUnit Jupiter test fixtures that let downstream aim2be services assert against emitted OutboxRecord entries without standing up Postgres in unit tests.

What's in this PR

Three public classes wired by @AutoConfigureOutbox:

  • TestOutboxRecordCaptor — in-memory captor with ConcurrentLinkedQueue backing. assertEmittedExactly / assertNoEmissions raise AssertionError with diagnostic status-distribution messages ("Expected exactly N PENDING but found M; statuses observed: [...]").
  • InMemoryOutboxRecordRepository — JDK-dynamic-proxy OutboxRecordRepository backed by a ConcurrentHashMap keyed on the compound PK. Mirrors production JPQL UPDATE semantics: status-guarded PENDING → SENT in markSent (rowcount=0 when terminal), CASE-WHEN budget-exhaustion in markFailureAttempt (transition to FAILED when retries+1 >= maxRetries in the same operation). ConcurrentMap#computeIfPresent serialises concurrent transitions on the same row. Unsupported JpaRepository-inherited methods throw a descriptive UnsupportedOperationException.
  • OutboxTestAutoConfiguration — exposes both beans; in-memory repository marked @Primary to preempt the JPA-backed bean. @AutoConfigureBefore the production OutboxAutoConfiguration class name. Registered via META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.

Tests

  • TestOutboxRecordCaptorTest (10 tests) — assertion API, insertion ordering, snapshot immutability, clear semantics, 8-thread × 200-write concurrent-capture safety check.
  • InMemoryOutboxRecordRepositoryTest (20 tests) — save/findById, markSent guard + lastError clearing, markFailureAttempt retries + FAILED-on-budget-exhaustion + truncation, findByStatus FIFO ordering + pagination + unpaged, countByStatus, findOldestCreatedAtByStatus, unsupported-method UnsupportedOperationException with descriptive diagnostic.
  • AutoConfigureOutboxIT (3 tests) — @SpringBootTest with @AutoConfigureOutbox; verifies captor + in-memory repository are injectable and that repository.save(...) records to the captor. DataSourceAutoConfiguration + HibernateJpaAutoConfiguration + SqlInitializationAutoConfiguration excluded so the IT proves the no-JPA contract end-to-end.

Build

  • maven-failsafe-plugin added to outbox-test-fixtures/pom.xml so the *IT class executes during mvn verify (matches outbox-publisher pattern).
  • mvn -B -pl outbox-test-fixtures -am installBUILD SUCCESS, ZERO [WARNING] lines, ZERO [ERROR] lines (rule 62).
  • Test totals: 30 surefire + 3 failsafe = 33 tests, all pass.

Downstream usage contract

@SpringBootTest
@AutoConfigureOutbox
class CalendarEventEmissionTest {
    @Autowired TestOutboxRecordCaptor captor;
    @Autowired OutboxRecordRepository repository;

    @Test void emits_calendar_event_to_outbox() {
        service.createEvent(...);
        captor.assertEmittedExactly(1, OutboxRecord.Status.PENDING);
    }
}

Refs:

  • OQ-6 §1.5 in .planning/26-stage-b-outbox-parity.md
  • ADR-0014 D-2 — outbox + record_version parity, library-first delivery
  • Builds on PR-PLATFORM-1 (OutboxRecord, OutboxRecordRepository).
PR-PLATFORM-4 — outbox-test-fixtures full impl (PR-PLATFORM-1 substrate). Implements OQ-6 §1.5 of .planning/26-stage-b-outbox-parity.md: JUnit Jupiter test fixtures that let downstream aim2be services assert against emitted `OutboxRecord` entries without standing up Postgres in unit tests. ## What's in this PR Three public classes wired by `@AutoConfigureOutbox`: - **`TestOutboxRecordCaptor`** — in-memory captor with `ConcurrentLinkedQueue` backing. `assertEmittedExactly` / `assertNoEmissions` raise `AssertionError` with diagnostic status-distribution messages (`"Expected exactly N PENDING but found M; statuses observed: [...]"`). - **`InMemoryOutboxRecordRepository`** — JDK-dynamic-proxy `OutboxRecordRepository` backed by a `ConcurrentHashMap` keyed on the compound PK. Mirrors production JPQL UPDATE semantics: status-guarded `PENDING → SENT` in `markSent` (rowcount=0 when terminal), CASE-WHEN budget-exhaustion in `markFailureAttempt` (transition to FAILED when `retries+1 >= maxRetries` in the same operation). `ConcurrentMap#computeIfPresent` serialises concurrent transitions on the same row. Unsupported `JpaRepository`-inherited methods throw a descriptive `UnsupportedOperationException`. - **`OutboxTestAutoConfiguration`** — exposes both beans; in-memory repository marked `@Primary` to preempt the JPA-backed bean. `@AutoConfigureBefore` the production `OutboxAutoConfiguration` class name. Registered via `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`. ## Tests - **`TestOutboxRecordCaptorTest`** (10 tests) — assertion API, insertion ordering, snapshot immutability, clear semantics, 8-thread × 200-write concurrent-capture safety check. - **`InMemoryOutboxRecordRepositoryTest`** (20 tests) — `save`/`findById`, `markSent` guard + `lastError` clearing, `markFailureAttempt` retries + FAILED-on-budget-exhaustion + truncation, `findByStatus` FIFO ordering + pagination + unpaged, `countByStatus`, `findOldestCreatedAtByStatus`, unsupported-method `UnsupportedOperationException` with descriptive diagnostic. - **`AutoConfigureOutboxIT`** (3 tests) — `@SpringBootTest` with `@AutoConfigureOutbox`; verifies captor + in-memory repository are injectable and that `repository.save(...)` records to the captor. `DataSourceAutoConfiguration` + `HibernateJpaAutoConfiguration` + `SqlInitializationAutoConfiguration` excluded so the IT proves the no-JPA contract end-to-end. ## Build - `maven-failsafe-plugin` added to `outbox-test-fixtures/pom.xml` so the `*IT` class executes during `mvn verify` (matches outbox-publisher pattern). - `mvn -B -pl outbox-test-fixtures -am install` → **BUILD SUCCESS**, ZERO `[WARNING]` lines, ZERO `[ERROR]` lines (rule 62). - Test totals: 30 surefire + 3 failsafe = **33 tests**, all pass. ## Downstream usage contract ```java @SpringBootTest @AutoConfigureOutbox class CalendarEventEmissionTest { @Autowired TestOutboxRecordCaptor captor; @Autowired OutboxRecordRepository repository; @Test void emits_calendar_event_to_outbox() { service.createEvent(...); captor.assertEmittedExactly(1, OutboxRecord.Status.PENDING); } } ``` Refs: - OQ-6 §1.5 in `.planning/26-stage-b-outbox-parity.md` - ADR-0014 D-2 — outbox + record_version parity, library-first delivery - Builds on PR-PLATFORM-1 (`OutboxRecord`, `OutboxRecordRepository`).
feat(test-fixtures): PR-PLATFORM-4 — outbox-test-fixtures full impl
Some checks failed
im2be-platform-libs CI / mvn install (pull_request) Failing after 2s
im2be-platform-libs CI / mvn verify (main only) (pull_request) Has been skipped
2f3b49cdfb
Implements OQ-6 §1.5 of .planning/26-stage-b-outbox-parity.md: JUnit
Jupiter test fixtures that let downstream aim2be services assert against
emitted OutboxRecord entries without standing up Postgres in unit tests.

Three public classes wired by @AutoConfigureOutbox:

* TestOutboxRecordCaptor — in-memory captor with ConcurrentLinkedQueue
  backing; assertEmittedExactly / assertNoEmissions raise AssertionError
  with diagnostic status-distribution messages
  ("Expected exactly N PENDING but found M; statuses observed: [...]").
* InMemoryOutboxRecordRepository — JDK-dynamic-proxy implementation of
  OutboxRecordRepository backed by ConcurrentHashMap keyed on the
  compound PK. Mirrors production JPQL UPDATE semantics: status-guarded
  PENDING → SENT transition in markSent (rowcount=0 when terminal),
  CASE-WHEN budget-exhaustion in markFailureAttempt (transition to
  FAILED when retries+1 >= maxRetries in the same operation).
  ConcurrentMap#computeIfPresent serialises concurrent transitions on
  the same row.
* OutboxTestAutoConfiguration — exposes both beans, marks the
  in-memory repository @Primary so it preempts the JPA-backed bean
  when both are in context. @AutoConfigureBefore the production
  OutboxAutoConfiguration class name. Registered via
  META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.

Tests:

* TestOutboxRecordCaptorTest (10 tests) — assertion API, insertion
  order, immutability, clear semantics, 8-thread × 200-write
  concurrent-capture safety check.
* InMemoryOutboxRecordRepositoryTest (20 tests) — save/findById,
  markSent guard + lastError clearing, markFailureAttempt retries
  + FAILED-on-budget-exhaustion + truncation, findByStatus FIFO
  ordering + pagination, countByStatus, findOldestCreatedAtByStatus,
  unsupported-method UnsupportedOperationException with descriptive
  diagnostic.
* AutoConfigureOutboxIT (3 tests) — @SpringBootTest with
  @AutoConfigureOutbox; verifies captor + in-memory repository are
  injectable and that save(...) on the repo records to the captor.
  DataSource + JPA + SqlInit autoconfigs excluded so the IT proves
  the no-JPA contract.

Build:

* maven-failsafe-plugin added to outbox-test-fixtures/pom.xml so the
  *IT class executes during `mvn verify` (matches outbox-publisher
  pattern).
* `mvn -B -pl outbox-test-fixtures -am install` → BUILD SUCCESS,
  ZERO [WARNING] lines, ZERO [ERROR] lines (rule 62).
* Test totals: 30 surefire + 3 failsafe = 33 tests, all pass.

Superseded by round 2.

Show previous round

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

Round 1 — head 2f3b49cdfb30, base main, trigger opened

TL;DR: CONDITIONAL_APPROVE — kept 2 minor findings (1 agreed deleteAll Javadoc mismatch, 1 verified unique-to-A thread-safety overstatement); no blocking issues.

Summary

Arbitration Summary

First run for this PR. No prior Memora history. Checked reusable patterns — none found.

Agreed findings (A ∩ B): 1 — both A and B independently flagged the deleteAll() Javadoc/implementation mismatch at the same file with materially-equivalent bodies (A:line 268, B:line 266 — both refer to the same Javadoc+method block). Confirmed by reading the file: Javadoc at lines 266–268 says "Clears every captured row + the backing store" but line 271 only calls rows.clear(); captor is never touched. Kept as MINOR.

Unique-to-A findings: 1 — thread-safety Javadoc overstates SELECT … FOR UPDATE semantics. Verified: lines 60–64 do claim computeIfPresent mirrors SELECT … FOR UPDATE; markSent (lines 215–223) mutates a shared mutable OutboxRecord in-place inside the lambda; findById (line 137) returns rows.get(pk) — the same mutable object reference. A concurrent caller holding a reference from a prior findById call can observe mid-transition field state (e.g., status=SENT but sentAt=null). Technically valid. Kept as MINOR.

Reviewer B summary mention of a "major" auto-configuration bug: not present in B's structured findings[] array — only in the prose summary. Rule 4 applied; not carried forward as a finding.

Kept 2 findings (1 agreed, 1 verified unique-to-A). Memora persisted as run #1 (memory ID 307).

Blast Radius

All changes are confined to the new outbox-test-fixtures module, which is a test-scope library. The blast radius is limited but non-trivial because this fixture will be consumed by downstream service test suites — any behavioural contract misalignment (e.g. the deleteAll captor gap) propagates to every consumer that imports this library.

BLAST_SCORE: 3/10

CI status (head 2f3b49cdfb30)

Overall: ✗ failure

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 (2)

[MINOR] deleteAll() Javadoc claims captor is cleared but implementation only clears the backing map

outbox-test-fixtures/src/main/java/com/aim2be/platform/test/InMemoryOutboxRecordRepository.java:266

The Javadoc at lines 266–268 reads:

Clears every captured row + the backing store. Used between tests that share a context.

The method body (line 271) only calls rows.clear()captor.clear() is never invoked. A test author who calls repository.deleteAll() in @BeforeEach / @AfterEach to reset shared-context state will find the captor queue still populated with records from prior test methods, causing false assertion failures on assertNoEmissions() / assertEmittedExactly(0, …) even though the backing store is empty.

Fix A (preferred): add captor.clear() inside deleteAll() so one call performs a true full reset, matching what the Javadoc promises.

Fix B (documentation-only): reword the Javadoc to explicitly state that only the backing map is cleared and the captor must be reset separately via captor.clear().

[MINOR] Thread-safety Javadoc overstates field-level atomicity — computeIfPresent does not replicate SELECT … FOR UPDATE semantics

outbox-test-fixtures/src/main/java/com/aim2be/platform/test/InMemoryOutboxRecordRepository.java:60

Lines 60–64 claim:

status transitions (markSent / markFailureAttempt) use ConcurrentMap#compute to atomically read-modify-write a single row, mirroring the row-level SELECT … FOR UPDATE semantics of the JPA path.

computeIfPresent serialises concurrent map-slot writes, but the OutboxRecord stored in the map is a mutable object mutated in-place (row.setStatus(…), row.setSentAt(…) — lines 217–219). findById returns the same reference via rows.get(pk) (line 137). A concurrent reader that already holds that reference can observe partially-mutated state (e.g., status=SENT but sentAt=null mid-transition) because there is no volatile or synchronized barrier on the object fields themselves. SELECT … FOR UPDATE in Postgres prevents any concurrent read of the locked row until the transaction commits; this implementation does not provide that guarantee at the field level.

For single-threaded unit tests this is harmless. The risk surfaces if a future integration test fires markSent and findById concurrently on the same record.

Fix A (correctness): make OutboxRecord immutable (builder + copy-on-write inside the lambda; computeIfPresent stores and returns the new instance). findById then always yields a complete consistent snapshot.

Fix B (documentation-only): narrow the Javadoc to "atomically updates the map slot; field-level visibility across concurrent findById callers is not guaranteed — safe for single-threaded tests."

Verdict

CONDITIONAL_APPROVE


hib-pr-reviewer • round 1 • 2 findings (2m) • 2026-05-27T03:11:00.695Z → 2026-05-27T03:12:59.163Z • posted-as: pr-reviewer-bot

<!-- hib-pr-reviewer collapsed --> > _Superseded by round 2._ <details> <summary>Show previous round</summary> ## hib-pr-reviewer review — PR #5 (affinity-intelligence-rework/im2be-platform-libs) **Round 1** — head `2f3b49cdfb30`, base `main`, trigger `opened` **TL;DR:** CONDITIONAL_APPROVE — kept 2 minor findings (1 agreed deleteAll Javadoc mismatch, 1 verified unique-to-A thread-safety overstatement); no blocking issues. ### Summary ## Arbitration Summary First run for this PR. No prior Memora history. Checked reusable patterns — none found. **Agreed findings (A ∩ B):** 1 — both A and B independently flagged the `deleteAll()` Javadoc/implementation mismatch at the same file with materially-equivalent bodies (A:line 268, B:line 266 — both refer to the same Javadoc+method block). Confirmed by reading the file: Javadoc at lines 266–268 says *"Clears every captured row + the backing store"* but line 271 only calls `rows.clear()`; `captor` is never touched. Kept as MINOR. **Unique-to-A findings:** 1 — thread-safety Javadoc overstates `SELECT … FOR UPDATE` semantics. Verified: lines 60–64 do claim `computeIfPresent` mirrors `SELECT … FOR UPDATE`; `markSent` (lines 215–223) mutates a shared mutable `OutboxRecord` in-place inside the lambda; `findById` (line 137) returns `rows.get(pk)` — the same mutable object reference. A concurrent caller holding a reference from a prior `findById` call can observe mid-transition field state (e.g., `status=SENT` but `sentAt=null`). Technically valid. Kept as MINOR. **Reviewer B summary mention of a "major" auto-configuration bug:** not present in B's structured `findings[]` array — only in the prose summary. Rule 4 applied; not carried forward as a finding. Kept 2 findings (1 agreed, 1 verified unique-to-A). Memora persisted as run #1 (memory ID 307). ### Blast Radius All changes are confined to the new `outbox-test-fixtures` module, which is a test-scope library. The blast radius is limited but non-trivial because this fixture will be consumed by downstream service test suites — any behavioural contract misalignment (e.g. the `deleteAll` captor gap) propagates to every consumer that imports this library. **BLAST_SCORE: 3/10** ### CI status (head `2f3b49cdfb30`) **Overall: ✗ failure** 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/50/jobs/0) | | im2be-platform-libs CI / mvn verify (main only) (pull_request) | ⏳ pending | [details](/affinity-intelligence-rework/im2be-platform-libs/actions/runs/50/jobs/1) | ### Findings (2) #### **[MINOR]** `deleteAll()` Javadoc claims captor is cleared but implementation only clears the backing map _outbox-test-fixtures/src/main/java/com/aim2be/platform/test/InMemoryOutboxRecordRepository.java:266_ The Javadoc at lines 266–268 reads: > *Clears every captured row + the backing store. Used between tests that share a context.* The method body (line 271) only calls `rows.clear()` — `captor.clear()` is never invoked. A test author who calls `repository.deleteAll()` in `@BeforeEach` / `@AfterEach` to reset shared-context state will find the captor queue still populated with records from prior test methods, causing false assertion failures on `assertNoEmissions()` / `assertEmittedExactly(0, …)` even though the backing store is empty. **Fix A (preferred):** add `captor.clear()` inside `deleteAll()` so one call performs a true full reset, matching what the Javadoc promises. **Fix B (documentation-only):** reword the Javadoc to explicitly state that only the backing map is cleared and the captor must be reset separately via `captor.clear()`. #### **[MINOR]** Thread-safety Javadoc overstates field-level atomicity — `computeIfPresent` does not replicate `SELECT … FOR UPDATE` semantics _outbox-test-fixtures/src/main/java/com/aim2be/platform/test/InMemoryOutboxRecordRepository.java:60_ Lines 60–64 claim: > *status transitions (markSent / markFailureAttempt) use `ConcurrentMap#compute` to atomically read-modify-write a single row, mirroring the row-level `SELECT … FOR UPDATE` semantics of the JPA path.* `computeIfPresent` serialises concurrent *map-slot* writes, but the `OutboxRecord` stored in the map is a mutable object mutated in-place (`row.setStatus(…)`, `row.setSentAt(…)` — lines 217–219). `findById` returns the same reference via `rows.get(pk)` (line 137). A concurrent reader that already holds that reference can observe partially-mutated state (e.g., `status=SENT` but `sentAt=null` mid-transition) because there is no `volatile` or `synchronized` barrier on the object fields themselves. `SELECT … FOR UPDATE` in Postgres prevents any concurrent read of the locked row until the transaction commits; this implementation does not provide that guarantee at the field level. For single-threaded unit tests this is harmless. The risk surfaces if a future integration test fires `markSent` and `findById` concurrently on the same record. **Fix A (correctness):** make `OutboxRecord` immutable (builder + copy-on-write inside the lambda; `computeIfPresent` stores and returns the new instance). `findById` then always yields a complete consistent snapshot. **Fix B (documentation-only):** narrow the Javadoc to *"atomically updates the map slot; field-level visibility across concurrent `findById` callers is not guaranteed — safe for single-threaded tests."* ### Verdict **CONDITIONAL_APPROVE** --- <sub>hib-pr-reviewer • round 1 • 2 findings (2m) • 2026-05-27T03:11:00.695Z → 2026-05-27T03:12:59.163Z • posted-as: pr-reviewer-bot</sub> </details>
fix(test-fixtures): R2 — 2 R1 MINOR (deleteAll Javadoc + thread-safety overstatement)
Some checks failed
im2be-platform-libs CI / mvn install (pull_request) Failing after 2s
im2be-platform-libs CI / mvn verify (main only) (pull_request) Has been skipped
a6e3efcd95
R1 verdict findings (kept=2):

(1) [MINOR, agreed A+B] InMemoryOutboxRecordRepository.deleteAll() — Javadoc claimed "Clears every captured row + the backing store" but the implementation only calls `rows.clear()`. The TestOutboxRecordCaptor was never touched. Fix: corrected the Javadoc to accurately describe the scope ("Clears the in-memory backing store"); added explicit "Does NOT touch the TestOutboxRecordCaptor" note + cross-reference to TestOutboxRecordCaptor.clear() for tests that need both reset paths.

(2) [MINOR, unique-to-A verified] InMemoryOutboxRecordRepository class Javadoc thread-safety section — overstated the equivalence to JPA `SELECT ... FOR UPDATE`. ConcurrentMap#compute provides atomic read-modify-write semantics with respect to the map's presence check + apply, but the stored OutboxRecord is a mutable reference shared with any prior findById caller; a concurrent reader can observe mid-transition state (e.g. status=SENT but sentAt=null). JPA row-level locking would block the reader until commit. Fix: rewrote the thread-safety paragraph to document the actual contract — atomic-per-key-compute + warning that findById returns moment-in-time views, NOT frozen snapshots.

No code-flow changes; pure documentation accuracy fix. The in-memory repo's behavior is unchanged; the docs now match.

Verification:
- mvn -B -pl outbox-test-fixtures -am install → BUILD SUCCESS, zero [WARNING], zero [ERROR]

Ripple-effect inspection (rule 63):
- No callers depend on the documented-but-not-implemented "captor reset on deleteAll" behavior (verified via grep — no test invokes deleteAll() AND then asserts on captor state).
- The thread-safety Javadoc change is informational; no runtime contract changed.

Superseded by round 3.

Show previous round

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

Round 2 — head a6e3efcd9589, base main, trigger synchronize

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

Summary

[quorum-converged] A=0 = B=0. > ⚠️ Parse-fallback: the model's output was truncated mid-JSON. Findings could not be verified; the prose summary below is best-effort salvage.

Round 2 Review — PR-PLATFORM-4 outbox-test-fixtures

Prior-round findings status

  • R1-1 (deleteAll() Javadoc) FIXED. Lines 277–286 now correctly state the captor is NOT cleared and explicitly references the R1 finding.
  • R1-2 (Thread-safety Javadoc overstating row-locking) Addressed. The full locking-caveat paragraph was added at lines 66–74. However, while correcting R1-2 a new factual error was introduced: the class-level thread-safety block (line 63) still says ConcurrentMap#compute when both markSent (line 226) and markFailureAttempt (line 263) use computeIfPresent. See Finding 1.

New findings

  1. Minor — Residual Javadoc misnaming from R1 fix (line 63): compute vs computeIfPresent.
  2. MajormatchIfMissing = true at line 50 of OutboxTestAutoConfiguration causes the @Primary in-memory repo to activate in every @SpringBootTest that has this jar on the classpath, regardless of whether @AutoConfigureOutbox is present.
  3. MinorassertEmittedExactly (line 59 of TestOutboxRecordCaptor) reads record.getStatus() at assertion time, not at capture time. Because InMemoryOutboxRecordRepository mutates records in-place, tests asserting PENDING after a full publish cycle will fail spuriously.
  4. Minor (deferred)RepositoryInvocationHandler.invoke at lines 331–332 calls local.invoke(delegate, args) without unwrapping InvocationTargetException, so any RuntimeException thrown by a delegate method surfaces to callers as an opaque UndeclaredThrowableException.

CI status (head a6e3efcd9589)

Overall: ✗ failure

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

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-27T03:15:23.186Z → 2026-05-27T03:21:22.437Z • posted-as: pr-reviewer-bot • [bookkeeping fallback]

<!-- hib-pr-reviewer collapsed --> > _Superseded by round 3._ <details> <summary>Show previous round</summary> ## hib-pr-reviewer review — PR #5 (affinity-intelligence-rework/im2be-platform-libs) **Round 2** — head `a6e3efcd9589`, base `main`, trigger `synchronize` **TL;DR:** NO_NEW_FINDINGS — No new findings this round. ### Summary [quorum-converged] A=0 = B=0. > **⚠️ Parse-fallback:** the model's output was truncated mid-JSON. Findings could not be verified; the prose summary below is best-effort salvage. ## Round 2 Review — PR-PLATFORM-4 outbox-test-fixtures ### Prior-round findings status - **R1-1 (`deleteAll()` Javadoc)** — ✅ FIXED. Lines 277–286 now correctly state the captor is NOT cleared and explicitly references the R1 finding. - **R1-2 (Thread-safety Javadoc overstating row-locking)** — ✅ Addressed. The full locking-caveat paragraph was added at lines 66–74. However, while correcting R1-2 a new factual error was introduced: the class-level thread-safety block (line 63) still says `ConcurrentMap#compute` when both `markSent` (line 226) and `markFailureAttempt` (line 263) use `computeIfPresent`. See Finding 1. ### New findings 1. **Minor** — Residual Javadoc misnaming from R1 fix (line 63): `compute` vs `computeIfPresent`. 2. **Major** — `matchIfMissing = true` at line 50 of `OutboxTestAutoConfiguration` causes the `@Primary` in-memory repo to activate in every `@SpringBootTest` that has this jar on the classpath, regardless of whether `@AutoConfigureOutbox` is present. 3. **Minor** — `assertEmittedExactly` (line 59 of `TestOutboxRecordCaptor`) reads `record.getStatus()` at assertion time, not at capture time. Because `InMemoryOutboxRecordRepository` mutates records in-place, tests asserting `PENDING` after a full publish cycle will fail spuriously. 4. **Minor (deferred)** — `RepositoryInvocationHandler.invoke` at lines 331–332 calls `local.invoke(delegate, args)` without unwrapping `InvocationTargetException`, so any `RuntimeException` thrown by a delegate method surfaces to callers as an opaque `UndeclaredThrowableException`. ### CI status (head `a6e3efcd9589`) **Overall: ✗ failure** 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/51/jobs/0) | | im2be-platform-libs CI / mvn verify (main only) (pull_request) | ⏳ pending | [details](/affinity-intelligence-rework/im2be-platform-libs/actions/runs/51/jobs/1) | ### 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-27T03:15:23.186Z → 2026-05-27T03:21:22.437Z • posted-as: pr-reviewer-bot • [bookkeeping fallback]</sub> </details>
fix(test-fixtures): R3 — 4 R2 prose-only findings (Javadoc nit + matchIfMissing leak + capture-time status + InvocationTargetException unwrap)
Some checks failed
im2be-platform-libs CI / mvn install (pull_request) Failing after 3s
im2be-platform-libs CI / mvn verify (main only) (pull_request) Has been skipped
3d32f01ab2
R2 verdict had TL;DR=NO_NEW_FINDINGS but the prose Summary listed 4 new findings (parse-fallback path). Applied the same approach as PR-PLATFORM-1 R8 (PR #4 R7 prose-only blocker): address them anyway since the issues are real.

(1) [MINOR] InMemoryOutboxRecordRepository class Javadoc (line 63) said `ConcurrentMap#compute` but the implementation uses `computeIfPresent` at markSent (line 226) and markFailureAttempt (line 263). Fix: updated Javadoc to `computeIfPresent` (matching actual code).

(2) [MAJOR] OutboxTestAutoConfiguration had `@ConditionalOnProperty(matchIfMissing = true)` — the @Primary in-memory repo would activate in EVERY @SpringBootTest that had the jar on classpath, regardless of @AutoConfigureOutbox presence. Fleet-wide test contamination risk: consumer services pulling outbox-test-fixtures transitively would lose their real OutboxRecordRepository. Fix: removed matchIfMissing; the annotation now requires `im2be.outbox.test.captor-enabled=true` to be explicitly set (which @AutoConfigureOutbox supplies via @PropertyMapping). Consumers that just pull the jar transitively no longer accidentally enable the fixtures.

(3) [MINOR] TestOutboxRecordCaptor.assertEmittedExactly read `record.getStatus()` at assertion time, but the in-memory repo mutates records in-place during markSent/markFailureAttempt — a row captured AT save time as PENDING would assert as SENT after a full publish cycle, spuriously failing `assertEmittedExactly(N, PENDING)`. Fix: introduced internal `Entry { OutboxRecord record, Status statusAtCapture }` snapshot. `capture(record)` records the status at insertion time. `assertEmittedExactly` + `assertNoEmissions` + `describeStatuses` use the snapshot status. `listEmitted()` still returns the live OutboxRecord references (preserving the API for callers that want post-mutation introspection).

(4) [MINOR deferred] RepositoryInvocationHandler.invoke called `method.invoke(delegate, args)` without unwrapping InvocationTargetException — any RuntimeException thrown by a delegate method surfaced to callers as opaque UndeclaredThrowableException. Fix: wrapped both invoke paths (Object methods + local-method routing) in try/catch(InvocationTargetException), rethrowing the cause directly.

Verification:
- mvn -B -pl outbox-test-fixtures -am install → BUILD SUCCESS, 30 unit + 3 IT + outbox-publisher's 68 = 101 tests passed
- Zero [WARNING], zero [ERROR]

Ripple-effect inspection (rule 63):
- matchIfMissing=false: confirmed @AutoConfigureOutbox's @PropertyMapping("im2be.outbox.test") + captorEnabled=true (default) means the annotation alone still activates the autoconfig. The IT explicitly sets the property as belt-and-braces. No legitimate consumer path is broken.
- capture-time status snapshot: listEmitted() return type unchanged (List<OutboxRecord>); existing tests using `record.getStatus()` on returned refs continue to work (they're using the LIVE status, which is the documented contract for listEmitted). Only assertions now use the snapshot.
- InvocationTargetException unwrap: throws the underlying cause directly. Test/consumer code that catches specific exceptions from delegate methods now works correctly; nothing previously relied on UndeclaredThrowableException.

Superseded by round 4.

Show previous round

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

Round 3 — head 3d32f01ab22f, base main, trigger synchronize

TL;DR: CONDITIONAL_APPROVE — 1 agreed minor finding kept (captor-not-cleared regression guard missing in deleteAllClearsBackingStore test); all prior R1 findings confirmed resolved.

Summary

Arbitration — Round 3

Memora recall: No prior run history found for this PR (both queries returned empty). Memora persistence attempted but blocked by tag/permission constraints in this environment — run summary could not be persisted.

Agreed finding (Rule 1 — gold): Both A and B independently flagged the identical minor finding: deleteAllClearsBackingStore test at line 275 of InMemoryOutboxRecordRepositoryTest.java lacks an assertion that captor.listEmitted() still holds its 2 records after deleteAll(), leaving the R1 behavioral contract ("deleteAll MUST NOT touch the captor") without a regression guard.

Verification (Rule 2): Read lines 275–284 at HEAD. Confirmed: repository.save(newPending()) × 2, repository.deleteAll(), then only a findByStatusOrderByCreatedAtAsc / getTotalElements().isZero() assertion — no captor assertion present. captor field confirmed at line 25, initialized in @BeforeEach setUp() at line 30, fully in scope. Finding stands.

Disposition: 1 agreed finding kept. No unique-to-one findings to adjudicate. No blockers or majors — verdict CONDITIONAL_APPROVE.

Blast Radius

The diff is entirely contained within the outbox-test-fixtures module — a test-support library. The only production-scope change is OutboxTestAutoConfiguration (matchIfMissing fix). No cross-module contracts are altered and no migration files are touched.

BLAST_SCORE: 2/10

CI status (head 3d32f01ab22f)

Overall: ✗ failure

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] deleteAllClearsBackingStore missing captor-not-cleared assertion — R1 fix has no regression guard

outbox-test-fixtures/src/test/java/com/aim2be/platform/test/InMemoryOutboxRecordRepositoryTest.java:275

Both reviewers independently identified this gap. The test at line 275 saves 2 records, calls deleteAll(), then asserts only that the backing store is empty (lines 281–283). It never asserts that captor.listEmitted() still holds 2 entries, so a future change that accidentally adds captor.clear() inside deleteAll() would pass all tests.

The R1 fix explicitly documents in InMemoryOutboxRecordRepository that deleteAll() must NOT touch the captor. That behavioral guarantee needs a regression guard here.

Add one assertion immediately after line 283:

// captor must be untouched — R1 documented behavioral contract
assertThat(captor.listEmitted()).hasSize(2);

captor is already a field (line 25) initialized in @BeforeEach setUp() (line 30); no setup change is required.

Verdict

CONDITIONAL_APPROVE


hib-pr-reviewer • round 3 • 1 finding (1m) • 2026-05-27T03:30:44.255Z → 2026-05-27T03:31:56.976Z • posted-as: pr-reviewer-bot

<!-- hib-pr-reviewer collapsed --> > _Superseded by round 4._ <details> <summary>Show previous round</summary> ## hib-pr-reviewer review — PR #5 (affinity-intelligence-rework/im2be-platform-libs) **Round 3** — head `3d32f01ab22f`, base `main`, trigger `synchronize` **TL;DR:** CONDITIONAL_APPROVE — 1 agreed minor finding kept (captor-not-cleared regression guard missing in deleteAllClearsBackingStore test); all prior R1 findings confirmed resolved. ### Summary ## Arbitration — Round 3 **Memora recall:** No prior run history found for this PR (both queries returned empty). Memora persistence attempted but blocked by tag/permission constraints in this environment — run summary could not be persisted. **Agreed finding (Rule 1 — gold):** Both A and B independently flagged the identical minor finding: `deleteAllClearsBackingStore` test at line 275 of `InMemoryOutboxRecordRepositoryTest.java` lacks an assertion that `captor.listEmitted()` still holds its 2 records after `deleteAll()`, leaving the R1 behavioral contract ("deleteAll MUST NOT touch the captor") without a regression guard. **Verification (Rule 2):** Read lines 275–284 at HEAD. Confirmed: `repository.save(newPending())` × 2, `repository.deleteAll()`, then only a `findByStatusOrderByCreatedAtAsc` / `getTotalElements().isZero()` assertion — no captor assertion present. `captor` field confirmed at line 25, initialized in `@BeforeEach setUp()` at line 30, fully in scope. Finding stands. **Disposition:** 1 agreed finding kept. No unique-to-one findings to adjudicate. No blockers or majors — verdict CONDITIONAL_APPROVE. ### Blast Radius The diff is entirely contained within the `outbox-test-fixtures` module — a test-support library. The only production-scope change is `OutboxTestAutoConfiguration` (`matchIfMissing` fix). No cross-module contracts are altered and no migration files are touched. **BLAST_SCORE: 2/10** ### CI status (head `3d32f01ab22f`) **Overall: ✗ failure** 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/52/jobs/0) | | im2be-platform-libs CI / mvn verify (main only) (pull_request) | ⏳ pending | [details](/affinity-intelligence-rework/im2be-platform-libs/actions/runs/52/jobs/1) | ### Findings (1) #### **[MINOR]** `deleteAllClearsBackingStore` missing captor-not-cleared assertion — R1 fix has no regression guard _outbox-test-fixtures/src/test/java/com/aim2be/platform/test/InMemoryOutboxRecordRepositoryTest.java:275_ Both reviewers independently identified this gap. The test at line 275 saves 2 records, calls `deleteAll()`, then asserts only that the backing store is empty (lines 281–283). It never asserts that `captor.listEmitted()` still holds 2 entries, so a future change that accidentally adds `captor.clear()` inside `deleteAll()` would pass all tests. The R1 fix explicitly documents in `InMemoryOutboxRecordRepository` that `deleteAll()` must NOT touch the captor. That behavioral guarantee needs a regression guard here. Add one assertion immediately after line 283: ```java // captor must be untouched — R1 documented behavioral contract assertThat(captor.listEmitted()).hasSize(2); ``` `captor` is already a field (line 25) initialized in `@BeforeEach setUp()` (line 30); no setup change is required. ### Verdict **CONDITIONAL_APPROVE** --- <sub>hib-pr-reviewer • round 3 • 1 finding (1m) • 2026-05-27T03:30:44.255Z → 2026-05-27T03:31:56.976Z • posted-as: pr-reviewer-bot</sub> </details>
fix(test-fixtures): R4 — captor-not-cleared regression guard (1 R3 MINOR)
Some checks failed
im2be-platform-libs CI / mvn install (pull_request) Failing after 2s
im2be-platform-libs CI / mvn verify (main only) (pull_request) Has been skipped
124a820c8b
R3 verdict findings (kept=1):

(1) [MINOR, agreed A+B] InMemoryOutboxRecordRepositoryTest.deleteAllClearsBackingStore at line 275 — asserted only the backing-store-cleared half of the deleteAll() contract; the captor-not-cleared half (added in R2 Javadoc fix + R3 matchIfMissing fix) had no regression guard. A future revert of deleteAll() to "clear captor too" would silently pass.

Fix: renamed test to `deleteAllClearsBackingStoreButNotCaptor` (more accurately describes the contract); added `assertThat(captor.listEmitted()).hasSize(2)` AFTER repository.deleteAll() to pin both halves of the contract.

Verification:
- mvn -B -pl outbox-test-fixtures test -Dtest='InMemoryOutboxRecordRepositoryTest' → 20 tests passed
- BUILD SUCCESS

Ripple-effect inspection (rule 63):
- Test rename: only the test class itself references this method; no external invokers.
- Assertion uses AssertJ (already imported via spring-boot-starter-test transitive).

Superseded by round 5.

Show previous round

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

Round 4 — head 124a820c8baf, base main, trigger synchronize

TL;DR: CONDITIONAL_APPROVE — verified both unique-to-one findings (A line 61: missing repository.deleteAll() in shared context; B line 56: string-based proxy detection); kept both as minor; all 3 prior-round findings confirmed resolved.

Summary

Arbitration Round 4

Memora context: No prior run history found (both tags_any and tags_all queries returned empty); Memora memory_create calls failed with a server-side tag allowlist error ('memora/issues' not in allowed tag list) on every variant attempted — Memora persistence skipped.

Reconciliation: Both A and B report 0 agreed findings — each submitted exactly 1 unique finding against the same file (AutoConfigureOutboxIT.java) at different lines. Both were verified via Read against the actual file at HEAD.

Finding A (line 61) — verified ✓: Line 61 is captor.clear(). Confirmed: saveOnRepositoryRecordsToCaptor clears the captor but never calls repository.deleteAll(). After this test runs the shared Spring context repository retains the PENDING record saved at line 64. Current tests do not read repository state so nothing fails today, but any future test in this class querying countByStatus, findByStatusOrderByCreatedAtAsc, or findOldestCreatedAtByStatus will observe contaminated state. Kept.

Finding B (line 56) — verified ✓: Line 56 is assertThat(repository.getClass().getName()).contains("Proxy"). Confirmed: this is a string-based proxy detector. B's analysis is correct — Proxy.isProxyClass(repository.getClass()) is the precise JDK predicate for JDK dynamic proxies and would reject CGLIB/ByteBuddy proxies; the string match could pass on unrelated proxy implementations. Kept.

All 3 prior-round findings (R1 deleteAll() Javadoc, R1 thread-safety Javadoc, R3 captor-retention assertion) confirmed resolved by both reviewers independently. Not re-raised.

Outcome: 2 kept (both minor), 0 dropped. CONDITIONAL_APPROVE.

Blast Radius

Diff is contained entirely within the outbox-test-fixtures module — test-scope infrastructure only. The new InMemoryOutboxRecordRepository and OutboxTestAutoConfiguration are consumed by downstream service tests but do not touch any production code path or exported protocol contract.

BLAST_SCORE: 2/10

CI status (head 124a820c8baf)

Overall: ✗ failure

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 (2)

[MINOR] saveOnRepositoryRecordsToCaptor resets captor but not repository — shared context left with stale PENDING row

outbox-test-fixtures/src/test/java/com/aim2be/platform/test/AutoConfigureOutboxIT.java:61

Line 61 calls captor.clear() before saving, but there is no corresponding repository.deleteAll() call. After saveOnRepositoryRecordsToCaptor runs, the shared Spring-context repository still holds the OutboxRecord saved at line 64. The other two tests (captorBeanIsInjectable, repositoryBeanIsInMemory) never read repository state, so nothing fails today. However, any future test added to this class that calls countByStatus(PENDING), findByStatusOrderByCreatedAtAsc(...), or findOldestCreatedAtByStatus(...) will observe an extra unexpected row, producing wrong counts rather than a clear assertion failure.

Add a @BeforeEach that resets both the repository and the captor:

@BeforeEach
void resetOutboxState() {
    repository.deleteAll();
    captor.clear();
}

With this in place the individual captor.clear() at line 61 becomes redundant and can be removed.

[MINOR] contains("Proxy") is an unreliable proxy-type detector — use Proxy.isProxyClass() instead

outbox-test-fixtures/src/test/java/com/aim2be/platform/test/AutoConfigureOutboxIT.java:56

Line 56 asserts repository.getClass().getName().contains("Proxy") to verify the in-memory shim was wired. Two problems:

  1. Fragility on implementation change: if InMemoryOutboxRecordRepository.create() ever moves from Proxy.newProxyInstance() to a concrete class or CGLIB subclass, the assertion silently passes (or fails) for the wrong reason.
  2. Precision gap: a Spring-generated ByteBuddy or Objenesis proxy whose class name happens to contain "Proxy" would pass the check even when the JPA-backed bean was accidentally wired.

The JDK ships the exact predicate for this use case:

assertThat(java.lang.reflect.Proxy.isProxyClass(repository.getClass()))
    .as("outbox repository must be wired as JDK dynamic proxy (in-memory shim)")
    .isTrue();

Proxy.isProxyClass() returns true iff the class was generated by Proxy.newProxyInstance() / getProxyClass(), correctly identifying the shim and rejecting every other proxy mechanism.

Verdict

CONDITIONAL_APPROVE


hib-pr-reviewer • round 4 • 2 findings (2m) • 2026-05-27T03:36:42.298Z → 2026-05-27T03:38:11.483Z • posted-as: pr-reviewer-bot

<!-- hib-pr-reviewer collapsed --> > _Superseded by round 5._ <details> <summary>Show previous round</summary> ## hib-pr-reviewer review — PR #5 (affinity-intelligence-rework/im2be-platform-libs) **Round 4** — head `124a820c8baf`, base `main`, trigger `synchronize` **TL;DR:** CONDITIONAL_APPROVE — verified both unique-to-one findings (A line 61: missing repository.deleteAll() in shared context; B line 56: string-based proxy detection); kept both as minor; all 3 prior-round findings confirmed resolved. ### Summary ## Arbitration Round 4 **Memora context**: No prior run history found (both `tags_any` and `tags_all` queries returned empty); Memora `memory_create` calls failed with a server-side tag allowlist error (`'memora/issues' not in allowed tag list`) on every variant attempted — Memora persistence skipped. **Reconciliation**: Both A and B report 0 agreed findings — each submitted exactly 1 unique finding against the same file (`AutoConfigureOutboxIT.java`) at different lines. Both were verified via `Read` against the actual file at HEAD. **Finding A (line 61) — verified ✓**: Line 61 is `captor.clear()`. Confirmed: `saveOnRepositoryRecordsToCaptor` clears the captor but never calls `repository.deleteAll()`. After this test runs the shared Spring context repository retains the PENDING record saved at line 64. Current tests do not read repository state so nothing fails today, but any future test in this class querying `countByStatus`, `findByStatusOrderByCreatedAtAsc`, or `findOldestCreatedAtByStatus` will observe contaminated state. **Kept.** **Finding B (line 56) — verified ✓**: Line 56 is `assertThat(repository.getClass().getName()).contains("Proxy")`. Confirmed: this is a string-based proxy detector. B's analysis is correct — `Proxy.isProxyClass(repository.getClass())` is the precise JDK predicate for JDK dynamic proxies and would reject CGLIB/ByteBuddy proxies; the string match could pass on unrelated proxy implementations. **Kept.** **All 3 prior-round findings** (R1 `deleteAll()` Javadoc, R1 thread-safety Javadoc, R3 captor-retention assertion) confirmed resolved by both reviewers independently. Not re-raised. **Outcome**: 2 kept (both minor), 0 dropped. CONDITIONAL_APPROVE. ### Blast Radius Diff is contained entirely within the `outbox-test-fixtures` module — test-scope infrastructure only. The new `InMemoryOutboxRecordRepository` and `OutboxTestAutoConfiguration` are consumed by downstream service tests but do not touch any production code path or exported protocol contract. **BLAST_SCORE: 2/10** ### CI status (head `124a820c8baf`) **Overall: ✗ failure** 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/53/jobs/0) | | im2be-platform-libs CI / mvn verify (main only) (pull_request) | ⏳ pending | [details](/affinity-intelligence-rework/im2be-platform-libs/actions/runs/53/jobs/1) | ### Findings (2) #### **[MINOR]** saveOnRepositoryRecordsToCaptor resets captor but not repository — shared context left with stale PENDING row _outbox-test-fixtures/src/test/java/com/aim2be/platform/test/AutoConfigureOutboxIT.java:61_ Line 61 calls `captor.clear()` before saving, but there is no corresponding `repository.deleteAll()` call. After `saveOnRepositoryRecordsToCaptor` runs, the shared Spring-context repository still holds the `OutboxRecord` saved at line 64. The other two tests (`captorBeanIsInjectable`, `repositoryBeanIsInMemory`) never read repository state, so nothing fails today. However, any future test added to this class that calls `countByStatus(PENDING)`, `findByStatusOrderByCreatedAtAsc(...)`, or `findOldestCreatedAtByStatus(...)` will observe an extra unexpected row, producing wrong counts rather than a clear assertion failure. Add a `@BeforeEach` that resets both the repository and the captor: ```java @BeforeEach void resetOutboxState() { repository.deleteAll(); captor.clear(); } ``` With this in place the individual `captor.clear()` at line 61 becomes redundant and can be removed. #### **[MINOR]** `contains("Proxy")` is an unreliable proxy-type detector — use `Proxy.isProxyClass()` instead _outbox-test-fixtures/src/test/java/com/aim2be/platform/test/AutoConfigureOutboxIT.java:56_ Line 56 asserts `repository.getClass().getName().contains("Proxy")` to verify the in-memory shim was wired. Two problems: 1. **Fragility on implementation change**: if `InMemoryOutboxRecordRepository.create()` ever moves from `Proxy.newProxyInstance()` to a concrete class or CGLIB subclass, the assertion silently passes (or fails) for the wrong reason. 2. **Precision gap**: a Spring-generated ByteBuddy or Objenesis proxy whose class name happens to contain "Proxy" would pass the check even when the JPA-backed bean was accidentally wired. The JDK ships the exact predicate for this use case: ```java assertThat(java.lang.reflect.Proxy.isProxyClass(repository.getClass())) .as("outbox repository must be wired as JDK dynamic proxy (in-memory shim)") .isTrue(); ``` `Proxy.isProxyClass()` returns `true` iff the class was generated by `Proxy.newProxyInstance()` / `getProxyClass()`, correctly identifying the shim and rejecting every other proxy mechanism. ### Verdict **CONDITIONAL_APPROVE** --- <sub>hib-pr-reviewer • round 4 • 2 findings (2m) • 2026-05-27T03:36:42.298Z → 2026-05-27T03:38:11.483Z • posted-as: pr-reviewer-bot</sub> </details>
fix(test-fixtures): R5 — 2 R4 MINOR (proxy detection + IT context isolation)
Some checks failed
im2be-platform-libs CI / mvn install (pull_request) Failing after 2s
im2be-platform-libs CI / mvn verify (main only) (pull_request) Has been skipped
c7472b39dc
R4 verdict findings (kept=2):

(1) [MINOR, unique-to-A verified] AutoConfigureOutboxIT.saveOnRepositoryRecordsToCaptor at line 61 — clears the captor but doesn't call repository.deleteAll(). The @SpringBootTest context is shared across test methods, so any subsequent test in this class querying countByStatus/findByStatusOrderByCreatedAtAsc would observe the PENDING row from this test as residual state. No current test does so, but a future test addition would silently observe contamination. Fix: also call repository.deleteAll() at test entry; belt-and-braces isolation.

(2) [MINOR, unique-to-B verified] AutoConfigureOutboxIT.repositoryBeanIsInMemory at line 56 — used string-contains "Proxy" on the class name as the proxy detector. The JDK predicate `Proxy.isProxyClass(repository.getClass())` is precise: it rejects CGLIB / ByteBuddy proxies and accepts only JDK dynamic proxies, which is what InMemoryOutboxRecordRepository.create(...) actually produces. Fix: replaced string-contains with Proxy.isProxyClass().

Verification:
- mvn -B -pl outbox-test-fixtures verify → BUILD SUCCESS, 33 tests passed (30 unit + 3 IT), zero [WARNING], zero [ERROR]

Ripple-effect inspection (rule 63):
- The new Proxy.isProxyClass assertion stays green at HEAD (the repository IS a JDK dynamic proxy from InMemoryOutboxRecordRepository.create); the test now properly fails if the wiring switches to CGLIB without intentional refactor.
- repository.deleteAll() at test entry is harmless when the store is empty (deleteAll is documented as backing-store-only).

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

Round 5 — head c7472b39dc8a, base main, trigger synchronize

TL;DR: CONDITIONAL_APPROVE — 1 agreed minor Javadoc inaccuracy kept (ProxyFactory vs JDK Proxy); all 5 prior-round findings confirmed resolved.

Summary

Round 5 Arbitration Summary

Recalled Memora context: no prior memories found for this PR or submodule. Persistence attempted (tag allowlist errors blocked create; absorb blocked by permissions — best-effort).

Reconciliation: A=1 finding, B=1 finding — both independently flag the identical issue at OutboxTestAutoConfiguration.java:78: the @return Javadoc links to org.springframework.aop.framework.ProxyFactory while the actual implementation at line 85 calls InMemoryOutboxRecordRepository.create(captor) which uses java.lang.reflect.Proxy.newProxyInstance (JDK dynamic proxy). Direct verification confirmed: line 78 reads {@link org.springframework.aop.framework.ProxyFactory}-style; InMemoryOutboxRecordRepository.java:106 confirms Proxy.newProxyInstance(loader, interfaces, handler) — no Spring AOP involved.

Rule 1 (agreed findings are gold): kept 1 agreed finding.

All 5 prior-round findings confirmed resolved by both reviewers; no re-raise.

Blast Radius

Diff touches a single submodule (outbox-test-fixtures) containing test infrastructure only. No production code paths are affected. The only change in non-test scope is a Javadoc inaccuracy in OutboxTestAutoConfiguration.java.

BLAST_SCORE: 1/10

CI status (head c7472b39dc8a)

Overall: ✗ failure

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)

outbox-test-fixtures/src/main/java/com/aim2be/platform/test/OutboxTestAutoConfiguration.java:78

Line 78 reads:

 * @return a {@link org.springframework.aop.framework.ProxyFactory}-style
 *         dynamic proxy over the in-memory store

org.springframework.aop.framework.ProxyFactory is Spring AOP's advisor-chain proxy builder (backed by CGLIB/byte-buddy), which is a completely different mechanism from what is actually used. The implementation at line 85 delegates to InMemoryOutboxRecordRepository.create(captor), which calls java.lang.reflect.Proxy.newProxyInstance(loader, interfaces, handler) (confirmed at InMemoryOutboxRecordRepository.java:106) — a plain JDK dynamic proxy with a custom InvocationHandler. The AutoConfigureOutboxIT also verifies the JDK-proxy contract via Proxy.isProxyClass() at line 57. Clicking the {@link} reference will mislead maintainers into the Spring AOP stack.

Replace with:

 * @return a JDK dynamic proxy ({@link java.lang.reflect.Proxy#newProxyInstance}) implementing
 *         {@link com.aim2be.platform.outbox.OutboxRecordRepository} backed by the in-memory store

Verdict

CONDITIONAL_APPROVE


hib-pr-reviewer • round 5 • 1 finding (1m) • 2026-05-27T03:41:58.645Z → 2026-05-27T03:43:33.120Z • posted-as: pr-reviewer-bot

## hib-pr-reviewer review — PR #5 (affinity-intelligence-rework/im2be-platform-libs) **Round 5** — head `c7472b39dc8a`, base `main`, trigger `synchronize` **TL;DR:** CONDITIONAL_APPROVE — 1 agreed minor Javadoc inaccuracy kept (ProxyFactory vs JDK Proxy); all 5 prior-round findings confirmed resolved. ### Summary ## Round 5 Arbitration Summary Recalled Memora context: no prior memories found for this PR or submodule. Persistence attempted (tag allowlist errors blocked create; absorb blocked by permissions — best-effort). **Reconciliation:** A=1 finding, B=1 finding — both independently flag the identical issue at `OutboxTestAutoConfiguration.java:78`: the `@return` Javadoc links to `org.springframework.aop.framework.ProxyFactory` while the actual implementation at line 85 calls `InMemoryOutboxRecordRepository.create(captor)` which uses `java.lang.reflect.Proxy.newProxyInstance` (JDK dynamic proxy). Direct verification confirmed: line 78 reads `{@link org.springframework.aop.framework.ProxyFactory}-style`; `InMemoryOutboxRecordRepository.java:106` confirms `Proxy.newProxyInstance(loader, interfaces, handler)` — no Spring AOP involved. **Rule 1 (agreed findings are gold):** kept 1 agreed finding. **All 5 prior-round findings confirmed resolved** by both reviewers; no re-raise. ### Blast Radius Diff touches a single submodule (`outbox-test-fixtures`) containing test infrastructure only. No production code paths are affected. The only change in non-test scope is a Javadoc inaccuracy in `OutboxTestAutoConfiguration.java`. **BLAST_SCORE: 1/10** ### CI status (head `c7472b39dc8a`) **Overall: ✗ failure** 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/54/jobs/0) | | im2be-platform-libs CI / mvn verify (main only) (pull_request) | ⏳ pending | [details](/affinity-intelligence-rework/im2be-platform-libs/actions/runs/54/jobs/1) | ### Findings (1) #### **[MINOR]** `@return` Javadoc links to `ProxyFactory` but implementation uses JDK `Proxy.newProxyInstance` _outbox-test-fixtures/src/main/java/com/aim2be/platform/test/OutboxTestAutoConfiguration.java:78_ Line 78 reads: ```java * @return a {@link org.springframework.aop.framework.ProxyFactory}-style * dynamic proxy over the in-memory store ``` `org.springframework.aop.framework.ProxyFactory` is Spring AOP's advisor-chain proxy builder (backed by CGLIB/byte-buddy), which is a completely different mechanism from what is actually used. The implementation at line 85 delegates to `InMemoryOutboxRecordRepository.create(captor)`, which calls `java.lang.reflect.Proxy.newProxyInstance(loader, interfaces, handler)` (confirmed at `InMemoryOutboxRecordRepository.java:106`) — a plain JDK dynamic proxy with a custom `InvocationHandler`. The `AutoConfigureOutboxIT` also verifies the JDK-proxy contract via `Proxy.isProxyClass()` at line 57. Clicking the `{@link}` reference will mislead maintainers into the Spring AOP stack. Replace with: ```java * @return a JDK dynamic proxy ({@link java.lang.reflect.Proxy#newProxyInstance}) implementing * {@link com.aim2be.platform.outbox.OutboxRecordRepository} backed by the in-memory store ``` ### Verdict **CONDITIONAL_APPROVE** --- <sub>hib-pr-reviewer • round 5 • 1 finding (1m) • 2026-05-27T03:41:58.645Z → 2026-05-27T03:43:33.120Z • posted-as: pr-reviewer-bot</sub>
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!5
No description provided.