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

145 lines
7.4 KiB
Markdown

# 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.