Files
tech-log-backend/docs/superpowers/plans/2026-07-25-application-outbox-failure-reporting-harness-free.md
T

7.4 KiB

Application Outbox Failure Reporting Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Make application-core framework/logging-free while preserving one safe structured ERROR after each confirmed outbox FAILED/DEAD transition.

Architecture: The application owns a narrow typed reporting port and safe report value. Messaging renders the report through SLF4J, and bootstrap only injects it. Transition state remains authoritative; reporter failures are non-authoritative and contained.

Tech Stack: Java 21 records, JUnit Jupiter, AssertJ, Spring Boot 4 configuration, SLF4J 2 fluent logging, Logback capture tests, ArchUnit, Gradle Groovy DSL, dependency locking.


Task 1: Safe Application Report Contract

Files:

  • Create: src/application-core/src/test/java/dev/caskeleton/application/outbox/OutboxRelayFailureReportTest.java

  • Create: src/application-core/src/main/java/dev/caskeleton/application/outbox/OutboxRelayFailureReport.java

  • Create: src/application-core/src/main/java/dev/caskeleton/application/outbox/OutboxRelayFailureReportPort.java

  • Write factory, invariant, and reflection-whitelist tests for the exact eight record components.

  • Run ./gradlew :application-core:test --tests '*OutboxRelayFailureReportTest' --console=plain and record the expected missing-type RED.

  • Implement the immutable record, exact invariants, factories, and functional port.

  • Re-run the focused value test and record GREEN.

Task 2: Relay Reporting Behavior

Files:

  • Modify: src/application-core/src/test/java/dev/caskeleton/application/outbox/PublishPendingOutboxEventsUseCaseTest.java

  • Modify: src/application-core/src/main/java/dev/caskeleton/application/outbox/PublishPendingOutboxEventsUseCase.java

  • Modify direct test constructor sites under src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/integration/outbox/

  • Add recording/throwing reporters and tests for exact FAILED/DEAD reports, all no-report paths, transition failure propagation, and reporter-failure continuation.

  • Run the relay test and record constructor/behavior RED.

  • Inject the reporter after the publish port, remove SLF4J, report only after successful transition, and contain reporter RuntimeException.

  • Update test-only direct constructors with explicit lambdas and re-run relay tests GREEN.

Task 3: Structured Messaging Adapter and Publish-Adapter Deduplication

Files:

  • Create: src/adapter/outbound/messaging/src/test/java/dev/caskeleton/adapter/outbound/messaging/outbox/Slf4jOutboxRelayFailureReportAdapterTest.java

  • Create: src/adapter/outbound/messaging/src/main/java/dev/caskeleton/adapter/outbound/messaging/outbox/Slf4jOutboxRelayFailureReportAdapter.java

  • Modify: src/adapter/outbound/messaging/src/test/java/dev/caskeleton/adapter/outbound/messaging/outbox/OutboxMessagePublishAdapterTest.java

  • Modify: src/adapter/outbound/messaging/src/main/java/dev/caskeleton/adapter/outbound/messaging/outbox/OutboxMessagePublishAdapter.java

  • Write Logback capture tests for exact ERROR count, fixed fields, throwable, retry-only time, unsafe-data absence, internal logging failure containment, and the adapter contract that report(null) never throws.

  • Run ./gradlew :adapter:outbound:messaging:test --tests '*Slf4jOutboxRelayFailureReportAdapterTest' --console=plain and record missing-type RED.

  • Implement the SLF4J 2 fluent adapter and re-run GREEN.

  • Replace outbox publish WARN expectations with no-log and propagation expectations; run RED.

  • Remove FailOpenDependencyLogger from the outbox adapter and re-run its tests GREEN, leaving OutboundMessagePublisher unchanged.

Task 4: Unconditional Reporter Wiring

Files:

  • Modify: src/adapter/outbound/messaging/src/main/java/dev/caskeleton/adapter/outbound/messaging/MessagingConfig.java

  • Modify: src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxConfig.java

  • Modify: src/app-bootstrap/src/test/java/dev/caskeleton/adapter/outbound/OptionalAdapterBeanGatingTest.java

  • Modify: src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/outbox/OutboxConfigTest.java

  • Add disabled and active context assertions for exactly one structured reporter bean.

  • Run OptionalAdapterBeanGatingTest and record RED.

  • Add the unconditional messaging reporter bean, use disabled for blank broker, update outbox publish adapter construction, and inject the port through bootstrap.

  • Re-run the gating and outbox configuration tests GREEN.

Task 5: Application Dependency Purity

Files:

  • Modify: src/build.gradle

  • Modify: src/application-core/build.gradle

  • Mechanically regenerate only: src/application-core/gradle.lockfile

  • Add verifyApplicationCoreDependencyPurity, wire it into :application-core:check, and run it against the current starter declaration to record RED.

  • Give application-core only JUnit Jupiter and AssertJ test dependencies while retaining the shared Boot test dependencies for every other leaf.

  • Remove the application Spring Boot starter and re-run the purity task GREEN.

  • Run ./gradlew :application-core:resolveAndLockAll --write-locks --console=plain; confirm no other lockfile changes.

  • Run application lock verification, tests, and compile/test runtime dependency reports.

Task 6: Non-Vacuous Diagnostic Architecture Rule

Files:

  • Modify: src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/CleanArchitectureTest.java

  • Create: src/app-bootstrap/src/test/java/dev/caskeleton/application/architecture/violations/ApplicationDiagnosticFrameworkViolation.java

  • Modify: src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/ArchitectureViolationFixtureTest.java

  • Add the violation fixture inside the exact dev.caskeleton.application.. rule scope and its mutation assertion; run it before the rule to record RED.

  • Add APPLICATION_HAS_NO_DIAGNOSTIC_FRAMEWORK, scoped exactly to dev.caskeleton.application.., for SLF4J, JUL, Logback, Log4j, and Micrometer.

  • Run the mutation test and production CleanArchitectureTest GREEN.

Task 7: Documentation and Verification

Files:

  • Modify: src/application-core/CLAUDE.md

  • Modify: src/application-core/README.md

  • Modify: src/adapter/outbound/messaging/CLAUDE.md

  • Modify: src/adapter/outbound/messaging/README.md

  • Modify relevant wiring guidance in src/app-bootstrap/README.md

  • Document the framework-free application contract, typed report semantics, messaging ownership, duplicate-log rule, and bootstrap wiring-only role.

  • Run focused application, messaging, gating, architecture mutation, production architecture, and available outbox integration tests.

  • Run verifyCleanArchitectureDependencies, dependency evidence reports, and check.

  • Run required safety greps, git diff --check, and git status --short; report any skip or remaining risk.

  • Hand the exact LLM Wiki capture responsibility and evidence back to the top-level controller; do not write the vault from this dispatched scope.

No step authorizes staging, committing, amending, pushing, public-path changes, CI changes, module registry changes, or .harness changes.